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

138 lines
2.4 KiB
JavaScript

import {
ref,
computed,
watch,
onMounted,
onBeforeUnmount,
unref
} from "vue";
export function useHighlight(target, options = {}) {
const rect = ref(null);
const padding = options.padding ?? 8;
let resizeObserver = 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 update() {
const element = resolveTarget();
if (!element) {
rect.value = null;
return;
}
const box = element.getBoundingClientRect();
rect.value = {
top: box.top - padding,
left: box.left - padding,
width: box.width + padding * 2,
height: box.height + padding * 2,
bottom:
box.bottom + padding,
right:
box.right + padding
};
}
function observe() {
const element = resolveTarget();
if (!element)
return;
resizeObserver?.disconnect();
resizeObserver = new ResizeObserver(() => {
update();
});
resizeObserver.observe(element);
}
const style = computed(() => {
if (!rect.value)
return {};
return {
top:
`${rect.value.top}px`,
left:
`${rect.value.left}px`,
width:
`${rect.value.width}px`,
height:
`${rect.value.height}px`
};
});
watch(
() => unref(target),
() => {
update();
observe();
},
{
immediate: true
}
);
onMounted(() => {
window.addEventListener(
"resize",
update
);
window.addEventListener(
"scroll",
update,
true
);
update();
observe();
});
onBeforeUnmount(() => {
resizeObserver?.disconnect();
window.removeEventListener(
"resize",
update
);
window.removeEventListener(
"scroll",
update,
true
);
});
return {
rect,
style,
update
};
}