UNPKG

@flexilla/alpine-offcanvas

Version:

AlpineJS plugin for adding offcanvas functionality to your AlpineJS components

392 lines (387 loc) 15 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // builds/module.js var module_exports = {}; __export(module_exports, { default: () => module_default }); module.exports = __toCommonJS(module_exports); // ../../node_modules/@flexilla/offcanvas/dist/offcanvas.js var y = Object.defineProperty; var A = (e, t, n) => t in e ? y(e, t, { enumerable: true, configurable: true, writable: true, value: n }) : e[t] = n; var o = (e, t, n) => A(e, typeof t != "symbol" ? t + "" : t, n); var u = (e, t = document.body) => t.querySelector(e); var v = (e, t = document.body) => Array.from(t.querySelectorAll(e)); var k = ({ newElement: e, existingElement: t }) => { if (!(e instanceof HTMLElement) || !(t instanceof HTMLElement)) throw new Error("Both parameters must be valid HTML elements."); const n = t.parentElement; if (n) n.insertBefore(e, t); else throw new Error("Existing element must have a parent element."); }; var O = ({ element: e, callback: t, type: n, keysCheck: s }) => { const a = getComputedStyle(e), i = a.transition; if (i !== "none" && i !== "" && !s.includes(i)) { const l = "transitionend", f = () => { e.removeEventListener(l, f), t(); }; e.addEventListener(l, f, { once: true }); } else t(); }; var B = ({ element: e, callback: t }) => { O({ element: e, callback: t, type: "transition", keysCheck: ["all 0s ease 0s", "all"] }); }; var h = (e, t, n) => { const s = new CustomEvent(t, { detail: n }); e.dispatchEvent(s); }; function I(e) { const t = () => { document.querySelector( "[data-fx-component]:not([data-component-initialized])" ) ? requestAnimationFrame(t) : e(); }; t(); } var E = "data-fx-teleport-root"; var m = "data-fx-teleported"; var L = { append: () => { }, remove: () => { }, restore: () => { } }; function S(e, t, n = "move") { var s; if (!(e instanceof HTMLElement)) throw new Error("Source element must be an HTMLElement"); if (!(t instanceof HTMLElement)) throw new Error("Target element must be an HTMLElement"); if (!["move", "detachable"].includes(n)) throw new Error(`Invalid teleport mode: ${n}. Must be "move" or "detachable".`); if ((s = e.parentElement) != null && s.closest(`[${E}]`)) return L; e.setAttribute(E, ""); let a = document.createComment("teleporter-placeholder"); const i = e.parentNode; return i && i.insertBefore(a, e), n === "move" ? (e.parentNode && (t.appendChild(e), e.setAttribute(m, "")), { append() { e.parentNode !== t && (t.appendChild(e), e.setAttribute(m, "")); }, remove() { a != null && a.parentNode && e.parentNode && a.parentNode.insertBefore(e, a); }, restore() { a != null && a.parentNode && e.parentNode !== i && a.parentNode.insertBefore(e, a); } }) : (e.parentNode && (t.appendChild(e), e.setAttribute(m, "")), { append() { t.contains(e) || t.appendChild(e); }, remove() { e.parentNode && e.remove(); }, restore() { a != null && a.parentNode && !e.parentNode && a.parentNode.insertBefore(e, a); } }); } var T = (e) => { var t; return (t = e.parentElement) == null ? void 0 : t.removeChild(e); }; var g = (e) => { e.setAttribute("data-state", "invisible"), B({ element: e, callback() { T(e); } }); }; var x = (e, t) => { const n = e; if (n === "" || !n) return; const s = document.createElement("div"); if (s.setAttribute("aria-hidden", "true"), s.setAttribute("data-state", "visible"), s.setAttribute("data-fx-offcanvas-overlay", ""), s.setAttribute("data-offcanvas-el", t), n === "") return; const a = n.split(" "); return n !== "" && s.classList.add(...a), s; }; var C = (e, t, n) => { e.setAttribute("aria-hidden", n === "open" ? "false" : "true"), e.setAttribute("data-state", n), t || $(n); }; var $ = (e) => { document.body.style.overflow = e === "open" ? "hidden" : "", document.body.style.overflowY = e === "open" ? "hidden" : "auto"; }; var N = (e, t) => { if (e === t) return; e.setAttribute("aria-hidden", "true"), e.setAttribute("data-state", "close"); const n = u(`[data-fx-offcanvas-overlay][data-offcanvas-el=${e.getAttribute("id")}]`, e.parentElement); n instanceof HTMLElement && g(n); }; var H = (e) => { const t = v("[data-fx-offcanvas][data-state=open]"); if (!(t.length <= 0)) for (const n of t) N(n, e); }; var d = class { static initGlobalRegistry() { window.$flexillaInstances || (window.$flexillaInstances = {}); } static register(t, n, s) { return this.initGlobalRegistry(), window.$flexillaInstances[t] || (window.$flexillaInstances[t] = []), this.getInstance(t, n) || (window.$flexillaInstances[t].push({ element: n, instance: s }), s); } static getInstance(t, n) { var s, a; return this.initGlobalRegistry(), (a = (s = window.$flexillaInstances[t]) == null ? void 0 : s.find( (i) => i.element === n )) == null ? void 0 : a.instance; } static removeInstance(t, n) { this.initGlobalRegistry(), window.$flexillaInstances[t] && (window.$flexillaInstances[t] = window.$flexillaInstances[t].filter( (s) => s.element !== n )); } static setup(t) { t.setAttribute("data-fx-component", "fx"); } static initialized(t) { t.setAttribute("data-component-initialized", "initialized"); } }; var c = class c2 { /** * Creates an instance of Offcanvas. * @param offcanvas - The offcanvas element selector or HTMLElement * @param options - Configuration options for the offcanvas * @throws {Error} When the provided element is not a valid HTMLElement * * @example * ```ts * const offcanvas = new Offcanvas('#sidebar', { * allowBodyScroll: true, // Allow scrolling when offcanvas is open * staticBackdrop: false, // Close when clicking outside * backdrop: 'dark', // Backdrop appearance * onShow: () => console.log('Offcanvas shown'), * onHide: () => console.log('Offcanvas hidden') * }); * ``` */ constructor(t, n = {}) { o(this, "offCanvasElement"); o(this, "offCanvasTriggers"); o(this, "offCanvasCloseBtns"); o(this, "offCanvasId", ""); o(this, "dispatchEventToDocument"); o(this, "allowBodyScroll"); o(this, "staticBackdrop"); o(this, "backdrop"); o(this, "options"); o(this, "teleporter"); o(this, "moveElOnInit", () => { I(() => this.teleporter.append()); }); o(this, "closeWhenClickOutSide", (t2) => { const n2 = this.offCanvasElement.getAttribute("data-state") === "open", s2 = !this.offCanvasElement.contains(t2.target) && ![...this.offCanvasTriggers].includes(t2.target); n2 && s2 && this.closeOffCanvas(); }); o(this, "closeOffCanvas", () => { var i2, l2, f2, r2, p; let t2 = false; if (h(this.offCanvasElement, "offcanvas-before-hide", { offcanvasId: this.offCanvasElement.id, setExitAction: (w) => { t2 = w; } }), ((f2 = (l2 = (i2 = this.options).beforeHide) == null ? void 0 : l2.call(i2)) == null ? void 0 : f2.cancelAction) || t2) return; const s2 = this.offCanvasElement.getAttribute("id"), a2 = u(`[data-fx-offcanvas-overlay][data-offcanvas-el=${s2}]`); a2 instanceof HTMLElement && g(a2), this.offCanvasElement.blur(), C( this.offCanvasElement, this.allowBodyScroll, "close" ), document.removeEventListener("keydown", this.closeWithEsc), !this.allowBodyScroll && !a2 && document.removeEventListener("click", this.closeWhenClickOutSide), (p = (r2 = this.options).onHide) == null || p.call(r2), h(this.offCanvasElement, "offcanvas-close", { offcanvasId: this.offCanvasElement.id }); }); o(this, "closeWithEsc", (t2) => { t2.key === "Escape" && (t2.preventDefault(), this.closeOffCanvas()); }); o(this, "closeFromCloseBtn", (t2) => { t2.target.blur(), this.closeOffCanvas(); }); o(this, "changeState", () => { this.offCanvasElement.getAttribute("data-state") === "open" ? this.closeOffCanvas() : this.openOffCanvas(); }); o(this, "open", () => { this.openOffCanvas(); }); o(this, "close", () => { this.closeOffCanvas(); }); o(this, "setOptions", ({ allowBodyscroll: t2 }) => { t2 !== void 0 && (this.allowBodyScroll = t2); }); const s = typeof t == "string" ? u(t) : t; if (!(s instanceof HTMLElement)) throw new Error("Invalid Offcanvas, the provided Element is not a valid HTMLElement"); this.offCanvasElement = s; const a = d.getInstance("offcanvas", s); if (a) return a; d.setup(this.offCanvasElement), this.options = n; const { staticBackdrop: i, allowBodyScroll: l, backdrop: f } = this.options; this.setupAttributes(), this.staticBackdrop = i || s.hasAttribute("data-static-backdrop") && s.dataset.staticBackdrop !== "false" || false, this.allowBodyScroll = l || s.hasAttribute("data-allow-body-scroll") && s.dataset.allowBodyScroll !== "false" || false; const r = this.offCanvasElement.getAttribute("id"); if (!r || r === "") throw new Error("\u274C id is required but missing on element:"); this.offCanvasId = r, this.offCanvasTriggers = this.findOffCanvasElements("[data-offcanvas-trigger]", false, r), this.offCanvasCloseBtns = this.findOffCanvasElements("[data-offcanvas-close]", true, r, this.offCanvasElement), this.backdrop = f || this.offCanvasElement.dataset.offcanvasBackdrop || "", this.dispatchEventToDocument = this.dispatchEventToDocument = this.options.dispatchEventToDocument || true, this.teleporter = S(this.offCanvasElement, document.body, "move"), this.setupOffcanvas(), this.moveElOnInit(), d.register("offcanvas", this.offCanvasElement, this), d.initialized(this.offCanvasElement); } findOffCanvasElements(t, n, s, a) { return n ? v(`${t}`, a) : v(`${t}[data-target=${s}]`); } setupAttributes() { this.offCanvasElement.hasAttribute("data-fx-offcanvas") || this.offCanvasElement.setAttribute("data-fx-offcanvas", ""); } openOffCanvas() { var s, a, i, l; (a = (s = this.options).beforeShow) == null || a.call(s), H(this.offCanvasElement), C( this.offCanvasElement, this.allowBodyScroll, "open" ); const t = this.offCanvasElement.getAttribute("id"), n = x( this.backdrop, t ); n instanceof HTMLElement && (k({ newElement: n, existingElement: this.offCanvasElement }), this.staticBackdrop || n.addEventListener("click", this.closeOffCanvas)), document.addEventListener("keydown", this.closeWithEsc), (l = (i = this.options).onShow) == null || l.call(i), h(this.offCanvasElement, "offcanvas-open", { offcanvasId: this.offCanvasElement.id }); } initCloseBtns() { for (const t of this.offCanvasCloseBtns) t.addEventListener("click", this.closeFromCloseBtn); } initTriggers() { for (const t of this.offCanvasTriggers) t.addEventListener("click", this.changeState); } setupOffcanvas() { this.initTriggers(), this.initCloseBtns(), this.dispatchEventToDocument && document.addEventListener(`sheet:${this.offCanvasId}:open`, this.open), this.dispatchEventToDocument && document.addEventListener(`sheet:${this.offCanvasId}:close`, this.close); } /** * Cleans up the offcanvas instance by removing event listeners and references. * Call this method when the offcanvas component is no longer needed to prevent memory leaks. * * @example * ```ts * const offcanvas = new Offcanvas('#sidebar'); * // ... use offcanvas ... * offcanvas.cleanup(); * ``` */ cleanup() { for (const t of this.offCanvasTriggers) t.removeEventListener("click", this.changeState); for (const t of this.offCanvasCloseBtns) t.removeEventListener("click", this.closeOffCanvas); document.removeEventListener("keydown", this.closeWithEsc), this.allowBodyScroll || document.removeEventListener("click", this.closeWhenClickOutSide), this.dispatchEventToDocument && document.removeEventListener(`sheet:${this.offCanvasId}:open`, this.open), this.dispatchEventToDocument && document.removeEventListener(`sheet:${this.offCanvasId}:close`, this.close), d.removeInstance("offcanvas", this.offCanvasElement); } }; o(c, "autoInit", (t = "[data-fx-offcanvas]") => { const n = v(t); for (const s of n) new c(s); }), /** * This is an alternative to using the constructor directly. * @param offcanvas - The offcanvas element selector or HTMLElement * @param options - Configuration options for the offcanvas * @returns A new Offcanvas instance * * @example * ```ts * const offcanvas = Offcanvas.init('#sidebar', { * allowBodyScroll: true, * staticBackdrop: false * }); * ``` */ o(c, "init", (t, n = {}) => new c(t, n)); var b = c; // src/index.js function Offcanvas(Alpine) { Alpine.directive("offcanvas", (el, {}, { cleanup }) => { const offcanvasId = el.getAttribute("id"); if (!offcanvasId) { console.error("\u274C id is required but missing on element:", el); return; } const offcanvas_ = new b(el); if (!Alpine.store("sheets")) { Alpine.store("sheets", {}); } Alpine.store("sheets")[offcanvasId] = offcanvas_; const openHandler = () => offcanvas_.open(); const closeHandler = () => offcanvas_.close(); document.addEventListener(`sheet:${offcanvasId}:open`, openHandler); document.addEventListener(`sheet:${offcanvasId}:close`, closeHandler); cleanup(() => { document.removeEventListener(`sheet:${offcanvasId}:open`, openHandler); document.removeEventListener( `sheet:${offcanvasId}:close`, closeHandler ); offcanvas_.cleanup(); delete Alpine.store("sheets")[offcanvasId]; }); }); Alpine.magic("offcanvas", (el) => (id) => { if (!Alpine.store("sheets")) { console.warn("\u26A0\uFE0F Alpine store for Offcanvas is not initialized."); return null; } if (!Alpine.store("sheets")[id]) { console.warn(`\u26A0\uFE0F No offcanvas instance found for ID: ${id}`); return null; } return Alpine.store("sheets")[id]; }); } var src_default = Offcanvas; // builds/module.js var module_default = src_default;