import { ref, watch, onBeforeUnmount, unref, nextTick } from "vue"; import { computePosition, autoUpdate, offset, flip, shift } from "@floating-ui/dom"; export function useFloating(target, floating, options = {}) { const x = ref(0); const y = ref(0); const strategy = ref("fixed"); const placement = ref( options.placement ?? "bottom" ); let cleanup = null; function resolveTarget() { const value = unref(target); if (!value) return null; if (typeof value === "function") return value(); if (typeof value === "string") return document.querySelector(value); return value; } function resolveFloating() { return unref(floating); } async function update() { const reference = resolveTarget(); const popup = resolveFloating(); if (!reference || !popup) return; const result = await computePosition( reference, popup, { strategy: strategy.value, placement: placement.value, middleware: [ offset( options.offset ?? 12 ), flip(), shift({ padding: 8 }) ] } ) x.value = result.x; y.value = result.y; } function start() { cleanup?.(); const reference = resolveTarget(); const popup = resolveFloating(); if (!reference || !popup) return; cleanup = autoUpdate( reference, popup, update ); update(); } watch( [ () => resolveTarget(), () => resolveFloating() ], () => { start() }, { immediate:true } ) watch( floating, async value => { if (!value) return await nextTick() update() } ) onBeforeUnmount(() => { cleanup?.(); }); return { x, y, strategy, update }; }