@flexilla/alpine-offcanvas
Version:
AlpineJS plugin for adding offcanvas functionality to your AlpineJS components
392 lines (387 loc) • 15 kB
JavaScript
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;