Files
onboard/resources/js/Components/Tour/composables/useFloating.js

125 lines
2.3 KiB
JavaScript

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
};
}