From f8e5f1a5a2afa1f18567e6d965359b6a1f082367 Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Fri, 27 Oct 2023 20:58:07 +0200 Subject: [PATCH] Fix incorrect tooltip position after window resize This commit fixes an issue where the tooltip position becomes inaccurate after resizing the window. The solution uses `autoUpdate` functionality of `floating-ui` to update the position automatically on resize events. This function depends on browser APIs: `IntersectionObserver` and `ResizeObserver`. The official documentation recommends polyfilling those to support old browsers. Polyfilling `ResizeObserver` is already part of the codebase, used by `SizeObserver.vue`. This commit refactors polyfill logic to be reusable across different components, and reuses it on `TooltipWrapper.vue`. Polyfilling `IntersectionObserver` is ignored due to this API being older and more widely supported. --- .../Shared/Hooks/UseResizeObserverPolyfill.ts | 27 +++++++++++++++++++ .../components/Shared/SizeObserver.vue | 21 ++++++--------- .../components/Shared/TooltipWrapper.vue | 6 ++++- 3 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 src/presentation/components/Shared/Hooks/UseResizeObserverPolyfill.ts diff --git a/src/presentation/components/Shared/Hooks/UseResizeObserverPolyfill.ts b/src/presentation/components/Shared/Hooks/UseResizeObserverPolyfill.ts new file mode 100644 index 00000000..738dadc4 --- /dev/null +++ b/src/presentation/components/Shared/Hooks/UseResizeObserverPolyfill.ts @@ -0,0 +1,27 @@ +import { onMounted } from 'vue'; +import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy'; + +// AsyncLazy ensures single load of the ResizeObserver polyfill, +// even when multiple calls are made simultaneously. +const polyfillLoader = new AsyncLazy(async () => { + if ('ResizeObserver' in window) { + return window.ResizeObserver; + } + const module = await import('@juggle/resize-observer'); + globalThis.window.ResizeObserver = module.ResizeObserver; + return module.ResizeObserver; +}); + +async function polyfillResizeObserver(): Promise { + return polyfillLoader.getValue(); +} + +export function useResizeObserverPolyfill() { + const resizeObserverReady = new Promise((resolve) => { + onMounted(async () => { + await polyfillResizeObserver(); + resolve(); + }); + }); + return { resizeObserverReady }; +} diff --git a/src/presentation/components/Shared/SizeObserver.vue b/src/presentation/components/Shared/SizeObserver.vue index 7670a342..171ff1d4 100644 --- a/src/presentation/components/Shared/SizeObserver.vue +++ b/src/presentation/components/Shared/SizeObserver.vue @@ -8,6 +8,7 @@ import { defineComponent, ref, onMounted, onBeforeUnmount, } from 'vue'; +import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill'; export default defineComponent({ emits: { @@ -18,18 +19,22 @@ export default defineComponent({ /* eslint-enable @typescript-eslint/no-unused-vars */ }, setup(_, { emit }) { + const { resizeObserverReady } = useResizeObserverPolyfill(); + const containerElement = ref(); let width = 0; let height = 0; let observer: ResizeObserver; - onMounted(async () => { + onMounted(() => { width = containerElement.value.offsetWidth; height = containerElement.value.offsetHeight; - observer = await initializeResizeObserver(updateSize); - observer.observe(containerElement.value); + resizeObserverReady.then(() => { + observer = new ResizeObserver(updateSize); + observer.observe(containerElement.value); + }); fireChangeEvents(); }); @@ -38,16 +43,6 @@ export default defineComponent({ observer?.disconnect(); }); - async function initializeResizeObserver( - callback: ResizeObserverCallback, - ): Promise { - if ('ResizeObserver' in window) { - return new window.ResizeObserver(callback); - } - const module = await import('@juggle/resize-observer'); - return new module.ResizeObserver(callback); - } - function updateSize() { let sizeChanged = false; if (isWidthChanged()) { diff --git a/src/presentation/components/Shared/TooltipWrapper.vue b/src/presentation/components/Shared/TooltipWrapper.vue index 65319231..1449264b 100644 --- a/src/presentation/components/Shared/TooltipWrapper.vue +++ b/src/presentation/components/Shared/TooltipWrapper.vue @@ -24,9 +24,10 @@