Skip to content

Commit

Permalink
Fix incorrect tooltip position after window resize
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
undergroundwires committed Oct 27, 2023
1 parent f4a74f0 commit f8e5f1a
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -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<typeof ResizeObserver> {
return polyfillLoader.getValue();
}

export function useResizeObserverPolyfill() {
const resizeObserverReady = new Promise<void>((resolve) => {
onMounted(async () => {
await polyfillResizeObserver();
resolve();
});
});
return { resizeObserverReady };
}
21 changes: 8 additions & 13 deletions src/presentation/components/Shared/SizeObserver.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {
defineComponent, ref, onMounted, onBeforeUnmount,
} from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
export default defineComponent({
emits: {
Expand All @@ -18,18 +19,22 @@ export default defineComponent({
/* eslint-enable @typescript-eslint/no-unused-vars */
},
setup(_, { emit }) {
const { resizeObserverReady } = useResizeObserverPolyfill();
const containerElement = ref<HTMLElement>();
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();
});
Expand All @@ -38,16 +43,6 @@ export default defineComponent({
observer?.disconnect();
});
async function initializeResizeObserver(
callback: ResizeObserverCallback,
): Promise<ResizeObserver> {
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()) {
Expand Down
6 changes: 5 additions & 1 deletion src/presentation/components/Shared/TooltipWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@

<script lang="ts">
import {
useFloating, arrow, shift, flip, Placement, offset, Side, Coords,
useFloating, arrow, shift, flip, Placement, offset, Side, Coords, autoUpdate,
} from '@floating-ui/vue';
import { defineComponent, ref, computed } from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
import type { CSSProperties } from 'vue/types/jsx'; // In Vue 3.0 import from 'vue'
const GAP_BETWEEN_TOOLTIP_AND_TRIGGER_IN_PX = 2;
Expand All @@ -40,6 +41,8 @@ export default defineComponent({
const arrowElement = ref<HTMLElement | undefined>();
const placement = ref<Placement>('top');
useResizeObserverPolyfill();
const { floatingStyles, middlewareData } = useFloating(
triggeringElement,
tooltipDisplayElement,
Expand All @@ -56,6 +59,7 @@ export default defineComponent({
flip(),
arrow({ element: arrowElement }),
],
whileElementsMounted: autoUpdate,
},
);
const arrowStyles = computed<CSSProperties>(() => {
Expand Down

0 comments on commit f8e5f1a

Please sign in to comment.