@flexilla/collapse
Version:
A versatile and interactive collapse component for creating collapsible sections in web applications, conserving space and improving user experience.
209 lines (208 loc) • 7.99 kB
JavaScript
var u = Object.defineProperty;
var m = (t, e, s) => e in t ? u(t, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : t[e] = s;
var n = (t, e, s) => m(t, typeof e != "symbol" ? e + "" : e, s);
const c = (t, e = document.body) => e.querySelector(t), x = (t, e = document.body) => Array.from(e.querySelectorAll(t)), w = ({
element: t,
callback: e,
type: s,
keysCheck: i
}) => {
const l = getComputedStyle(t), a = l.transition;
if (a !== "none" && a !== "" && !i.includes(a)) {
const g = "transitionend", p = () => {
t.removeEventListener(g, p), e();
};
t.addEventListener(g, p, { once: !0 });
} else
e();
}, E = ({
element: t,
callback: e
}) => {
w({
element: t,
callback: e,
type: "transition",
keysCheck: ["all 0s ease 0s", "all"]
});
}, r = (t, e, s) => {
const i = new CustomEvent(e, { detail: s });
t.dispatchEvent(i);
}, d = (t, e) => {
t.setAttribute("aria-hidden", e === "open" ? "false" : "true"), t.setAttribute("data-state", e);
}, b = (t, e = "close", s = "0px") => {
t.style.height = e === "open" ? "auto" : s, d(t, e);
}, y = (t) => {
if (t.getAttribute("data-state") === "open") return;
d(t, "open");
const e = t.scrollHeight;
t.style.height = `${e}px`, E({
element: t,
callback: () => {
t.getAttribute("data-state") === "open" && (t.style.height = "auto");
}
});
}, I = (t, e = "0px") => {
t.getAttribute("data-state") !== "close" && (t.style.height = `${t.scrollHeight}px`, t.offsetHeight, t.style.height = e, d(t, "close"));
};
class h {
static initGlobalRegistry() {
window.$flexillaInstances || (window.$flexillaInstances = {});
}
static register(e, s, i) {
return this.initGlobalRegistry(), window.$flexillaInstances[e] || (window.$flexillaInstances[e] = []), this.getInstance(e, s) || (window.$flexillaInstances[e].push({ element: s, instance: i }), i);
}
static getInstance(e, s) {
var i, l;
return this.initGlobalRegistry(), (l = (i = window.$flexillaInstances[e]) == null ? void 0 : i.find(
(a) => a.element === s
)) == null ? void 0 : l.instance;
}
static removeInstance(e, s) {
this.initGlobalRegistry(), window.$flexillaInstances[e] && (window.$flexillaInstances[e] = window.$flexillaInstances[e].filter(
(i) => i.element !== s
));
}
static setup(e) {
e.setAttribute("data-fx-component", "fx");
}
static initialized(e) {
e.setAttribute("data-component-initialized", "initialized");
}
}
const o = class o {
/**
* Creates a new Collapse instance.
* @param selector - The CSS selector string or HTMLElement to be collapsed/expanded
* @param options - Configuration options for the collapse behavior
* @param triggerSelector - Optional CSS selector for the trigger element. If not provided,
* it will look for an element with data-collapse-trigger attribute
* @throws {Error} When the provided element is not a valid HTMLElement
*/
constructor(e, s = {}, i) {
n(this, "element");
n(this, "defaultState");
n(this, "collapseId");
n(this, "collapseTrigger");
n(this, "options");
n(this, "closeHeight");
/**
* Expands the collapse element to show its content.
* Triggers 'beforeshow' and 'aftershow' events, and calls the onToggle callback if provided.
*
* @example
* ```ts
* const collapse = new Collapse('#myCollapse');
* collapse.show();
* ```
*/
n(this, "show", () => {
var e, s;
r(this.element, "before-expand", {
isExpanded: !1
}), this.collapseTrigger && (this.collapseTrigger.ariaExpanded = "true"), y(this.element), (s = (e = this.options).onToggle) == null || s.call(e, { isExpanded: !0 }), r(this.element, "expanded", {
isExpanded: !1
});
});
/**
* Collapses the element to hide its content.
* Triggers 'beforehide' and 'afterhide' events, and calls the onToggle callback if provided.
*
* @example
* ```ts
* const collapse = new Collapse('#myCollapse');
* collapse.hide();
* ```
*/
n(this, "hide", () => {
var e, s;
this.collapseTrigger && (this.collapseTrigger.ariaExpanded = "false"), I(this.element, `${this.closeHeight}px`), (s = (e = this.options).onToggle) == null || s.call(e, { isExpanded: !1 }), r(this.element, "collapsed", { isExpanded: !1 });
});
/**
* Toggles the collapse element between expanded and collapsed states.
* Triggers 'beforetoggle' and 'aftertoggle' events, and calls the onToggle callback if provided.
*
* @example
* ```ts
* const collapse = new Collapse('#myCollapse');
* collapse.toggle();
* ```
*/
n(this, "toggle", () => {
var s, i;
const e = this.element.dataset.state === "open";
e ? this.hide() : this.show(), (i = (s = this.options).onToggle) == null || i.call(s, { isExpanded: !e });
});
n(this, "setCloseHeight", (e) => {
this.closeHeight = e;
});
let l;
if (l = typeof e == "string" ? c(`${e}`) : e, typeof e == "string" && !l)
throw new Error(`No element found matching selector: ${e}`);
if (!(l instanceof HTMLElement))
throw new Error("Provided element must be a valid HTMLElement or selector");
this.element = l;
const a = h.getInstance("collapse", this.element);
if (a)
return a;
this.collapseId = this.element.getAttribute("id"), this.collapseTrigger = c(`${i}`) || c(`[data-collapse-trigger][data-target*='${this.collapseId}']`), this.options = s, this.defaultState = this.element.dataset.state ? this.element.dataset.state === "open" ? "open" : "close" : this.options.defaultState || "close", this.closeHeight = this.element.dataset.closeHeight ? parseInt(this.element.dataset.closeHeight || "0") : this.options.closeHeight || 0, this.initCollapse(), h.register("collapse", this.element, this);
}
initCollapse() {
this.collapseTrigger instanceof HTMLElement && (this.collapseTrigger.addEventListener("click", this.toggle), this.collapseTrigger.ariaExpanded = this.defaultState === "open" ? "true" : "false"), b(this.element, this.defaultState, `${this.closeHeight}px`);
}
/**
* Cleans up the Collapse instance by removing event listeners.
* This method should be called when the collapse component is no longer needed
* to prevent memory leaks.
*
* @example
* ```ts
* const collapse = new Collapse('#myCollapse');
* // When done with the collapse component
* collapse.cleanup();
* ```
*/
cleanup() {
this.collapseTrigger instanceof HTMLElement && this.collapseTrigger.removeEventListener("click", this.toggle), h.removeInstance("collapse", this.element);
}
};
/**
* Initializes a new Collapse instance with the specified configuration.
*
* @param selector - The CSS selector string or HTMLElement to be collapsed/expanded
* @param options - Configuration options for the collapse behavior
* @param triggerSelector - Optional CSS selector for the trigger element
* @returns A new Collapse instance
*
* @example
* ```ts
* const collapse = Collapse.init('#myCollapse', {
* defaultState: 'open',
* onToggle: ({ isExpanded }) => console.log(isExpanded)
* });
* ```
*/
n(o, "init", (e, s = {}, i) => new o(e, s, i)), /**
* Automatically initializes all collapse components in the document that match the provided selector.
* This is useful for setting up multiple collapse elements at once without manual initialization.
*
* @param selector - CSS selector to identify collapse elements. Defaults to '[data-fx-collapse]'
*
* @example
* ```ts
* // Initialize all elements with data-fx-collapse attribute
* Collapse.autoInit();
*
* // Initialize elements with custom selector
* Collapse.autoInit('.custom-collapse');
* ```
*/
n(o, "autoInit", (e = "[data-fx-collapse]") => {
const s = x(e);
for (const i of s)
new o(i);
});
let f = o;
export {
f as Collapse
};