@vue-material/core
Version:
Yet another 'Material Design Components' library for Vue3.
83 lines (82 loc) • 2.58 kB
JavaScript
import { ref, watch, onMounted, onUnmounted } from "vue";
import { getParent, $ } from "../utils/dom/selector.js";
const focusableSelector = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
function focusFirstFocusable(element) {
var _a;
(_a = $(focusableSelector, element)) == null ? void 0 : _a.focus();
}
function focusLastLockable() {
const lastFocusLock = document.querySelectorAll("[focus-lock]");
if (lastFocusLock.length > 0) {
focusFirstFocusable([...lastFocusLock].at(-1));
}
}
function focusDummy() {
const focusDummy2 = document.createElement("div");
focusDummy2.setAttribute("tabindex", "0");
focusDummy2.setAttribute(
"style",
"position: absolute; opacity: 0; pointer-events: none;"
);
return focusDummy2;
}
function useFocusLock(elem, state = true) {
const dummy = focusDummy();
const enabled = ref(state);
let ignore = false;
function onClick(event) {
if (!enabled.value) return;
const root = elem.value;
const target = event.target;
if (root) {
ignore = root.contains(target) || root === target;
ignore || focusFirstFocusable(root);
}
}
function onBlur(event) {
if (!enabled.value) return;
const root = event.currentTarget;
const target = event.relatedTarget;
if (ignore) {
ignore = false;
return;
}
if (root.contains(target)) return;
if (getParent(target, "[focus-lock]")) return;
event.preventDefault();
focusFirstFocusable(root);
}
function mountEvent(element) {
element.setAttribute("focus-lock", "");
element.after(dummy);
document.addEventListener("pointerdown", onClick);
document.addEventListener("focusin", focusOther);
element.addEventListener("focusout", onBlur);
focusFirstFocusable(element);
}
function unmountEvent(element) {
element.removeAttribute("focus-lock");
document.removeEventListener("pointerdown", onClick);
document.removeEventListener("focusin", focusOther);
element.removeEventListener("focusout", onBlur);
}
function focusOther(event) {
if (!enabled.value) return;
if (getParent(event.relatedTarget, "[focus-lock]")) return;
focusLastLockable();
}
watch(elem, (newElement, oldElement) => {
oldElement && unmountEvent(oldElement);
newElement && mountEvent(newElement);
});
onMounted(() => elem.value && mountEvent(elem.value));
onUnmounted(() => {
dummy.remove();
elem.value && unmountEvent(elem.value);
focusLastLockable();
});
return enabled;
}
export {
useFocusLock
};