stylescape
Version:
Stylescape is a visual identity framework developed by Scape Agency.
173 lines • 6.63 kB
JavaScript
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