Files
privacy.sexy/src/presentation/components/Shared/SizeObserver.vue
undergroundwires cb42f11b97 Fix code highlighting and optimize category select
This commit introduces a batched debounce mechanism for managing user
selection state changes. It effectively reduces unnecessary processing
during rapid script checking, preventing multiple triggers for code
compilation and UI rendering.

Key improvements include:

- Enhanced performance, especially noticeable when selecting large
  categories. This update resolves minor UI freezes experienced when
  selecting categories with numerous scripts.
- Correction of a bug where the code area only highlighted the last
  selected script when multiple scripts were chosen.

Other changes include:

- Timing functions:
  - Create a `Timing` folder for `throttle` and the new
    `batchedDebounce` functions.
  - Move these functions to the application layer from the presentation
    layer, reflecting their application-wide use.
  - Refactor existing code for improved clarity, naming consistency, and
    adherence to new naming conventions.
  - Add missing unit tests.
- `UserSelection`:
  - State modifications in `UserSelection` now utilize a singular object
    inspired by the CQRS pattern, enabling batch updates and flexible
    change configurations, thereby simplifying change management.
- Remove the `I` prefix from related interfaces to align with new coding
  standards.
- Refactor related code for better testability in isolation with
  dependency injection.
- Repository:
  - Move repository abstractions to the application layer.
  - Improve repository abstraction to combine `ReadonlyRepository` and
    `MutableRepository` interfaces.
- E2E testing:
  - Introduce E2E tests to validate the correct batch selection
    behavior.
  - Add a specialized data attribute in `TheCodeArea.vue` for improved
    testability.
  - Reorganize shared Cypress functions for a more idiomatic Cypress
    approach.
  - Improve test documentation with related information.
- `SelectedScript`:
  - Create an abstraction for simplified testability.
  - Introduce `SelectedScriptStub` in tests as a substitute for the
    actual object.
2023-11-18 22:23:27 +01:00

103 lines
2.5 KiB
Vue

<template>
<div ref="containerElement" class="container">
<slot />
</div>
</template>
<script lang="ts">
import {
defineComponent, shallowRef, onMounted, onBeforeUnmount, watch,
} from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
import { throttle } from '@/application/Common/Timing/Throttle';
export default defineComponent({
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
sizeChanged: () => true,
widthChanged: (width: number) => true,
heightChanged: (height: number) => true,
/* eslint-enable @typescript-eslint/no-unused-vars */
},
setup(_, { emit }) {
const { resizeObserverReady } = useResizeObserverPolyfill();
const containerElement = shallowRef<HTMLElement>();
let width = 0;
let height = 0;
let observer: ResizeObserver | undefined;
onMounted(() => {
watch(() => containerElement.value, async (element) => {
if (!element) {
disposeObserver();
return;
}
resizeObserverReady.then(() => {
disposeObserver();
observer = new ResizeObserver(throttle(updateSize, 200));
observer.observe(element);
});
updateSize(); // Do not throttle, immediately inform new width
}, { immediate: true });
});
onBeforeUnmount(() => {
disposeObserver();
});
function updateSize() {
const changes = [
updateWidth(),
updateHeight(),
];
if (changes.some((c) => c.isChanged)) {
emit('sizeChanged');
}
}
function updateWidth(): {
readonly isChanged: boolean;
} {
const newWidth = containerElement.value?.offsetWidth ?? 0;
if (newWidth === width) {
return { isChanged: false };
}
width = newWidth;
emit('widthChanged', newWidth);
return { isChanged: true };
}
function updateHeight(): {
readonly isChanged: boolean;
} {
const newHeight = containerElement.value?.offsetHeight ?? 0;
if (newHeight === height) {
return { isChanged: false };
}
height = newHeight;
emit('heightChanged', newHeight);
return { isChanged: true };
}
function disposeObserver() {
observer?.disconnect();
observer = undefined;
}
return {
containerElement,
};
},
});
</script>
<style scoped lang="scss">
.container {
width: 100%;
height: 100%;
display: inline-block; // if inline then it has no height or weight
}
</style>