UNPKG

maz-ui

Version:

A standalone components library for Vue.Js 3 & Nuxt.Js 3

164 lines (163 loc) 7.72 kB
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 };