UNPKG

stylescape

Version:

Stylescape is a visual identity framework developed by Scape Agency.

208 lines 7.9 kB
export class AccordionManager { constructor(selectorOrElement, options = {}) { this.items = []; this.handleClick = (event) => { const header = event.currentTarget; const index = this.items.findIndex((item) => item.header === header); if (index !== -1) { this.toggle(index); } }; this.handleKeydown = (event) => { const header = event.currentTarget; const index = this.items.findIndex((item) => item.header === header); switch (event.key) { case "Enter": case " ": event.preventDefault(); this.toggle(index); break; case "ArrowDown": event.preventDefault(); this.focusHeader((index + 1) % this.items.length); break; case "ArrowUp": event.preventDefault(); this.focusHeader((index - 1 + this.items.length) % this.items.length); break; case "Home": event.preventDefault(); this.focusHeader(0); break; case "End": event.preventDefault(); this.focusHeader(this.items.length - 1); break; } }; this.container = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement; this.options = { allowMultiple: options.allowMultiple ?? false, defaultOpen: options.defaultOpen ?? -1, animationDuration: options.animationDuration ?? 300, activeHeaderClass: options.activeHeaderClass ?? "accordion-header--active", activeContentClass: options.activeContentClass ?? "accordion-content--active", onOpen: options.onOpen ?? (() => { }), onClose: options.onClose ?? (() => { }), contentSelector: options.contentSelector ?? "[data-ss-accordion-content]", }; if (!this.container) { console.warn("[Stylescape] AccordionManager container not found"); return; } this.init(); } open(index) { if (index < 0 || index >= this.items.length) return; if (!this.options.allowMultiple) { this.items.forEach((item, i) => { if (i !== index && item.isOpen) { this.closeItem(i); } }); } this.openItem(index); } close(index) { if (index < 0 || index >= this.items.length) return; this.closeItem(index); } toggle(index) { if (index < 0 || index >= this.items.length) return; if (this.items[index].isOpen) { this.close(index); } else { this.open(index); } } openAll() { this.items.forEach((_, i) => this.openItem(i)); } closeAll() { this.items.forEach((_, i) => this.closeItem(i)); } isOpen(index) { return this.items[index]?.isOpen ?? false; } destroy() { this.items.forEach((item) => { item.header.removeEventListener("click", this.handleClick); item.header.removeEventListener("keydown", this.handleKeydown); }); this.items = []; } static initAccordions() { const managers = []; const accordions = document.querySelectorAll('[data-ss="accordion"]'); accordions.forEach((el) => { const multiple = el.dataset.ssAccordionMultiple === "true"; const defaultOpen = el.dataset.ssAccordionDefaultOpen; managers.push(new AccordionManager(el, { allowMultiple: multiple, defaultOpen: defaultOpen ? parseInt(defaultOpen, 10) : -1, })); }); return managers; } init() { if (!this.container) return; const headers = this.container.querySelectorAll("[data-ss-accordion-header], .accordion-header"); headers.forEach((header, index) => { const content = this.findContent(header); if (!content) return; const id = `accordion-content-${index}-${Date.now()}`; content.id = content.id || id; header.setAttribute("aria-expanded", "false"); header.setAttribute("aria-controls", content.id); content.setAttribute("role", "region"); content.setAttribute("aria-labelledby", header.id || `accordion-header-${index}`); if (!header.hasAttribute("tabindex")) { header.setAttribute("tabindex", "0"); } content.hidden = true; content.style.overflow = "hidden"; this.items.push({ header, content, isOpen: false, }); header.addEventListener("click", this.handleClick); header.addEventListener("keydown", this.handleKeydown); }); this.openDefaults(); } findContent(header) { const nextSibling = header.nextElementSibling; if (nextSibling?.matches(this.options.contentSelector)) { return nextSibling; } const parent = header.parentElement; return (parent?.querySelector(this.options.contentSelector) ?? null); } openDefaults() { const defaults = this.options.defaultOpen; if (Array.isArray(defaults)) { defaults.forEach((i) => this.openItem(i)); } else if (defaults >= 0) { this.openItem(defaults); } } openItem(index) { const item = this.items[index]; if (!item || item.isOpen) return; item.header.setAttribute("aria-expanded", "true"); item.header.classList.add(this.options.activeHeaderClass); item.content.hidden = false; item.content.classList.add(this.options.activeContentClass); const height = item.content.scrollHeight; item.content.style.height = "0px"; requestAnimationFrame(() => { item.content.style.transition = `height ${this.options.animationDuration}ms ease`; item.content.style.height = `${height}px`; setTimeout(() => { item.content.style.height = ""; item.content.style.transition = ""; }, this.options.animationDuration); }); item.isOpen = true; this.options.onOpen(item.header, item.content, index); } closeItem(index) { const item = this.items[index]; if (!item || !item.isOpen) return; item.header.setAttribute("aria-expanded", "false"); item.header.classList.remove(this.options.activeHeaderClass); item.content.classList.remove(this.options.activeContentClass); const height = item.content.scrollHeight; item.content.style.height = `${height}px`; requestAnimationFrame(() => { item.content.style.transition = `height ${this.options.animationDuration}ms ease`; item.content.style.height = "0px"; setTimeout(() => { item.content.hidden = true; item.content.style.height = ""; item.content.style.transition = ""; }, this.options.animationDuration); }); item.isOpen = false; this.options.onClose(item.header, item.content, index); } focusHeader(index) { this.items[index]?.header.focus(); } } export default AccordionManager; //# sourceMappingURL=AccordionManager.js.map