maz-ui
Version:
A standalone components library for Vue.Js 3 & Nuxt.Js 3
164 lines (163 loc) • 7.72 kB
JavaScript
import { defineComponent, ref, onMounted, watch, nextTick, createBlock, openBlock, Teleport, createVNode, Transition, withCtx, createElementBlock, createCommentVNode, mergeProps, createElementVNode, withModifiers, normalizeClass, renderSlot } from "vue";
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper.B--vMWp3.js";
import '../assets/MazBackdrop.09bziTPM.css';const _hoisted_1 = ["aria-labelledby", "aria-describedby"], _hoisted_2 = {
role: "dialog",
class: "m-backdrop-container",
"aria-modal": "true"
}, _hoisted_3 = { class: "m-backdrop-wrapper" }, MODAL_OPENED_CLASS = "--backdrop-present", _sfc_main = /* @__PURE__ */ defineComponent({
inheritAttrs: !1,
__name: "MazBackdrop",
props: {
modelValue: { type: Boolean, default: !1 },
teleportSelector: { default: "body" },
beforeClose: { type: Function },
persistent: { type: Boolean, default: !1 },
closeOnEscape: { type: Boolean, default: !0 },
transitionName: { default: "backdrop-anim" },
backdropClass: {},
backdropContentClass: {},
contentPadding: { type: Boolean, default: !1 },
justify: { default: "none" },
align: { default: "none" },
variant: {},
ariaLabelledby: {},
ariaDescribedby: {}
},
emits: ["open", "close", "update:model-value", "before-close"],
setup(__props, { expose: __expose, emit: __emit }) {
const emits = __emit;
function getScrollbarWidth() {
return window.innerWidth - document.documentElement.clientWidth;
}
function addClassToDocument() {
const scrollbarWidth = getScrollbarWidth();
scrollbarWidth > 0 && (document.documentElement.style.setProperty("--scrollbar-width", `${scrollbarWidth}px`), document.documentElement.classList.add("--has-scrollbar")), document.documentElement.classList.add(MODAL_OPENED_CLASS);
}
function removeClassFromDocument() {
document.querySelector(".m-backdrop.--present") || (document.documentElement.classList.remove(MODAL_OPENED_CLASS), document.documentElement.classList.remove("--has-scrollbar"), document.documentElement.style.removeProperty("--scrollbar-width"));
}
const present = ref(__props.modelValue);
function close() {
__props.persistent || toggleModal(!1);
}
async function toggleModal(value) {
const newValue = value ?? !present.value;
newValue || (emits("before-close"), await __props.beforeClose?.()), present.value = newValue;
}
function onBackdropAnimationEnter() {
emits("open");
}
function onBackdropAnimationLeave() {
emits("update:model-value", !1), emits("close"), removeClassAndEventToDocument();
}
function onKeyPress(event) {
__props.closeOnEscape && event.key === "Escape" && close();
}
function addClassAndEventToDocument() {
addClassToDocument(), document.addEventListener("keyup", onKeyPress, !1), document.addEventListener("keydown", trapFocus, !1);
}
function removeClassAndEventToDocument() {
try {
document.removeEventListener("keyup", onKeyPress), document.removeEventListener("keydown", trapFocus);
} catch (error) {
console.warn("Error removing event listeners:", error);
}
removeClassFromDocument();
}
onMounted(() => {
__props.modelValue ? addClassAndEventToDocument() : removeClassAndEventToDocument();
});
let initialFocusableElement = null;
function getAllFocusableElements(selector) {
const modal = document.querySelector(selector);
return (modal && Array.from(modal.querySelectorAll('a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], input[type="email"], input[type="password"], input[type="url"], input[type="tel"], input[type="number"], input[type="search"], input[type="date"], input[type="time"], select, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]')))?.filter((el) => {
const style = globalThis.getComputedStyle(el), isDisabled = "disabled" in el ? el.disabled : !1;
return style.display !== "none" && style.visibility !== "hidden" && !isDisabled && el.tabIndex !== -1;
}) || [];
}
function findFirstFocusableElement(selector) {
const focusableElements = getAllFocusableElements(selector);
return focusableElements.length > 0 ? focusableElements[0] : null;
}
function trapFocus(event) {
if (event.key !== "Tab")
return;
const focusableElements = getAllFocusableElements(".m-backdrop-content");
if (focusableElements.length === 0)
return;
const firstFocusable = focusableElements[0], lastFocusable = focusableElements[focusableElements.length - 1];
event.shiftKey ? document.activeElement === firstFocusable && (event.preventDefault(), lastFocusable.focus()) : document.activeElement === lastFocusable && (event.preventDefault(), firstFocusable.focus());
}
return watch(
() => __props.modelValue,
async (value) => {
present.value = value, value ? (addClassAndEventToDocument(), initialFocusableElement = document.activeElement, await nextTick(), findFirstFocusableElement(".m-backdrop-content")?.focus()) : (removeClassAndEventToDocument(), await nextTick(), initialFocusableElement?.focus());
}
), __expose({
/**
* Animation leave event
* @description This is used to handle animation leave events
*/
onBackdropAnimationLeave,
/**
* Close the backdrop
* @description This is used to close the backdrop
*/
close,
/**
* The present state of the backdrop
* @description This is used to check if the backdrop is present (open)
*/
present,
/**
* Toggle the backdrop
* @description This is used to toggle the backdrop
* @param {boolean} value - The value to toggle the backdrop (optional)
*/
toggleModal,
/**
* Key press event
* @description This is used to handle key press events
*/
onKeyPress
}), (_ctx, _cache) => (openBlock(), createBlock(Teleport, {
to: _ctx.teleportSelector,
disabled: !present.value
}, [
createVNode(Transition, {
appear: "",
name: _ctx.transitionName,
onAfterEnter: onBackdropAnimationEnter,
onAfterLeave: onBackdropAnimationLeave
}, {
default: withCtx(() => [
present.value ? (openBlock(), createElementBlock("div", mergeProps({
key: 0,
class: "m-backdrop --present m-reset-css"
}, _ctx.$attrs, {
class: [_ctx.backdropClass, _ctx.variant && `--variant-${_ctx.variant}`, { "--persistent": _ctx.persistent }],
"aria-labelledby": _ctx.ariaLabelledby,
"aria-describedby": _ctx.ariaDescribedby
}), [
createElementVNode("div", _hoisted_2, [
createElementVNode("div", _hoisted_3, [
createElementVNode("div", {
class: normalizeClass(["m-backdrop-content", [_ctx.backdropContentClass, `--justify-${_ctx.justify}`, `--align-${_ctx.align}`, { "--padding": _ctx.contentPadding }]]),
role: "button",
tabindex: "-1",
onPointerdown: withModifiers(close, ["self"])
}, [
renderSlot(_ctx.$slots, "default", { close }, void 0, !0)
], 34)
])
])
], 16, _hoisted_1)) : createCommentVNode("", !0)
]),
_: 3
}, 8, ["name"])
], 8, ["to", "disabled"]));
}
}), MazBackdrop = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d23340ed"]]);
export {
MazBackdrop as default
};