UNPKG

stylescape

Version:

Stylescape is a visual identity framework developed by Scape Agency.

173 lines 6.63 kB
export class CollapsibleSectionManager { constructor(selectorOrElement, options = {}) { this.trigger = null; this.content = null; this.isExpanded = false; this.handleClick = (event) => { if (this.content?.contains(event.target) && event.target !== this.trigger) { return; } this.toggle(); }; this.handleKeydown = (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); this.toggle(); } }; this.element = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement; this.options = { expanded: options.expanded ?? false, animationDuration: options.animationDuration ?? 300, collapsedClass: options.collapsedClass ?? "collapsible--collapsed", expandedClass: options.expandedClass ?? "collapsible--expanded", persist: options.persist ?? false, storageKey: options.storageKey ?? this.element?.id ?? "collapsible-state", onExpand: options.onExpand ?? (() => { }), onCollapse: options.onCollapse ?? (() => { }), triggerSelector: options.triggerSelector ?? "[data-ss-collapsible-trigger]", contentSelector: options.contentSelector ?? "[data-ss-collapsible-content]", }; if (!this.element) { console.warn("[Stylescape] CollapsibleSectionManager element not found"); return; } this.init(); } get expanded() { return this.isExpanded; } set expanded(value) { if (value) { this.expand(); } else { this.collapse(); } } toggle() { if (this.isExpanded) { this.collapse(); } else { this.expand(); } } expand() { if (!this.element || !this.content || this.isExpanded) return; this.isExpanded = true; this.element.classList.remove(this.options.collapsedClass); this.element.classList.add(this.options.expandedClass); this.trigger?.setAttribute("aria-expanded", "true"); this.content.hidden = false; const height = this.content.scrollHeight; this.content.style.height = "0px"; this.content.style.overflow = "hidden"; requestAnimationFrame(() => { if (!this.content) return; this.content.style.transition = `height ${this.options.animationDuration}ms ease`; this.content.style.height = `${height}px`; setTimeout(() => { if (!this.content) return; this.content.style.height = ""; this.content.style.overflow = ""; this.content.style.transition = ""; }, this.options.animationDuration); }); if (this.options.persist) { localStorage.setItem(this.options.storageKey, "true"); } this.options.onExpand(this.element); } collapse() { if (!this.element || !this.content || !this.isExpanded) return; this.isExpanded = false; this.element.classList.add(this.options.collapsedClass); this.element.classList.remove(this.options.expandedClass); this.trigger?.setAttribute("aria-expanded", "false"); const height = this.content.scrollHeight; this.content.style.height = `${height}px`; this.content.style.overflow = "hidden"; requestAnimationFrame(() => { if (!this.content) return; this.content.style.transition = `height ${this.options.animationDuration}ms ease`; this.content.style.height = "0px"; setTimeout(() => { if (!this.content) return; this.content.hidden = true; this.content.style.height = ""; this.content.style.overflow = ""; this.content.style.transition = ""; }, this.options.animationDuration); }); if (this.options.persist) { localStorage.setItem(this.options.storageKey, "false"); } this.options.onCollapse(this.element); } destroy() { this.trigger?.removeEventListener("click", this.handleClick); this.trigger?.removeEventListener("keydown", this.handleKeydown); this.element = null; this.trigger = null; this.content = null; } static initCollapsibles() { const managers = []; document .querySelectorAll('[data-ss="collapsible"]') .forEach((el) => { const expanded = el.dataset.ssCollapsibleExpanded === "true"; const persist = el.dataset.ssCollapsiblePersist === "true"; managers.push(new CollapsibleSectionManager(el, { expanded, persist, })); }); return managers; } init() { if (!this.element) return; this.trigger = this.element.querySelector(this.options.triggerSelector) || this.element.querySelector(".collapsible-trigger") || this.element; this.content = this.element.querySelector(this.options.contentSelector) || this.element.querySelector(".collapsible-content"); if (!this.content) { this.content = this.element; } const contentId = this.content.id || `collapsible-content-${Date.now()}`; this.content.id = contentId; this.trigger.setAttribute("aria-controls", contentId); if (!this.trigger.hasAttribute("tabindex") && this.trigger.tagName !== "BUTTON") { this.trigger.setAttribute("tabindex", "0"); } let initialExpanded = this.options.expanded; if (this.options.persist) { const stored = localStorage.getItem(this.options.storageKey); if (stored !== null) { initialExpanded = stored === "true"; } } this.isExpanded = !initialExpanded; this.toggle(); this.trigger.addEventListener("click", this.handleClick); this.trigger.addEventListener("keydown", this.handleKeydown); } } export default CollapsibleSectionManager; //# sourceMappingURL=CollapsibleSectionManager.js.map