UNPKG

@studiocms/ui

Version:

The UI library for StudioCMS. Includes the layouts & components we use to build StudioCMS.

99 lines (98 loc) 3.64 kB
const findOwningAccordion = (accordionItem) => { let current = accordionItem.parentElement; while (current) { if (current.classList.contains("sui-accordion")) { return current; } current = current.parentElement; } return null; }; const getDirectAccordionItems = (accordion) => { const allItems = accordion.querySelectorAll(".sui-accordion-item"); const directItems = []; for (let i = 0; i < allItems.length; i++) { if (findOwningAccordion(allItems[i]) === accordion) { directItems.push(allItems[i]); } } return directItems; }; const updateAccordionARIA = (item, isOpen) => { const summary = item.querySelector(".sui-accordion-summary"); const details = item.querySelector(".sui-accordion-details"); if (summary) summary.setAttribute("aria-expanded", isOpen.toString()); if (details) details.setAttribute("aria-hidden", (!isOpen).toString()); }; const toggleAccordionItem = (accordionItem, accordion) => { const isAlreadyOpen = accordionItem.dataset.open === "true"; const newState = !isAlreadyOpen; if (accordion.dataset.multiple === "true") { accordionItem.dataset.open = newState.toString(); } else { const directItems = getDirectAccordionItems(accordion); for (let i = 0; i < directItems.length; i++) { const item = directItems[i]; const shouldBeOpen = item === accordionItem; item.dataset.open = shouldBeOpen.toString(); updateAccordionARIA(item, shouldBeOpen); } return; } updateAccordionARIA(accordionItem, newState); }; const focusAccordionItem = (accordion, direction, currentItem) => { const items = getDirectAccordionItems(accordion); const currentIndex = items.indexOf(currentItem); if (currentIndex === -1) return; const targetIndex = direction === "next" ? (currentIndex + 1) % items.length : (currentIndex - 1 + items.length) % items.length; const targetSummary = items[targetIndex]?.querySelector(".sui-accordion-summary"); targetSummary?.focus(); }; const initializeAccordionStates = () => { const allAccordionItems = document.querySelectorAll(".sui-accordion-item"); for (let i = 0; i < allAccordionItems.length; i++) { const item = allAccordionItems[i]; const isOpen = item.dataset.open === "true"; updateAccordionARIA(item, isOpen); } }; let listenersAdded = false; const handleAccordionInteraction = (event, isKeyboard = false) => { const target = event.target; const accordionSummary = target.closest(".sui-accordion-summary"); if (!accordionSummary) return; const accordionItem = accordionSummary.closest(".sui-accordion-item"); const accordion = accordionItem && findOwningAccordion(accordionItem); if (!accordionItem || !accordion) return; if (isKeyboard) { const keyEvent = event; switch (keyEvent.key) { case "Enter": case " ": event.preventDefault(); toggleAccordionItem(accordionItem, accordion); break; case "ArrowDown": event.preventDefault(); focusAccordionItem(accordion, "next", accordionItem); break; case "ArrowUp": event.preventDefault(); focusAccordionItem(accordion, "prev", accordionItem); break; } } else { event.preventDefault(); toggleAccordionItem(accordionItem, accordion); } }; const loadAccordions = () => { initializeAccordionStates(); if (!listenersAdded) { document.addEventListener("click", handleAccordionInteraction); document.addEventListener("keydown", (e) => handleAccordionInteraction(e, true)); listenersAdded = true; } }; document.addEventListener("astro:page-load", loadAccordions);