UNPKG

@studiocms/ui

Version:

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

109 lines (108 loc) 4.33 kB
const switchTab = (tabContainer, newActiveHeader, originatedFromSync = false) => { const tabHeaders = Array.from( tabContainer.querySelectorAll(":scope > .sui-tabs-list > .sui-tab-header") ); const tabPanels = Array.from( tabContainer.querySelectorAll(":scope > .sui-tabs-content > sui-tab-item") ); if (!newActiveHeader || !tabHeaders.includes(newActiveHeader)) return; for (const header of tabHeaders) { header.classList.remove("active"); header.tabIndex = -1; header.setAttribute("aria-selected", "false"); } for (const panel of tabPanels) { panel.classList.remove("active"); } newActiveHeader.classList.add("active"); newActiveHeader.tabIndex = 0; newActiveHeader.setAttribute("aria-selected", "true"); const activePanel = tabPanels.find((p) => p.id === newActiveHeader.getAttribute("aria-controls")); if (activePanel) { activePanel.classList.add("active"); } const syncKey = tabContainer.dataset.syncKey; if (syncKey && !originatedFromSync) { const storage = tabContainer.dataset.storageStrategy ?? "session"; const storageLayer = storage === "session" ? sessionStorage : localStorage; const tabIndex = tabHeaders.indexOf(newActiveHeader); storageLayer.setItem(syncKey, tabIndex.toString()); document.dispatchEvent( new CustomEvent(`sui-tab-switch:${syncKey}`, { detail: { tabIndex, uniqueId: tabContainer.dataset.uniqueId } }) ); } }; const setupTabContainer = (tabContainer) => { const tabHeaders = Array.from( tabContainer.querySelectorAll(":scope > .sui-tabs-list > .sui-tab-header") ); const tabPanels = Array.from( tabContainer.querySelectorAll(":scope > .sui-tabs-content > sui-tab-item") ); const tabList = tabContainer.querySelector(":scope > .sui-tabs-list"); if (!tabHeaders.length || !tabPanels.length || !tabList) return; tabHeaders.forEach((header, index) => { const panel = tabPanels[index]; if (!panel) return; const panelId = panel.dataset.tabId ?? `sui-panel-${crypto.randomUUID()}`; const headerId = header.id || `sui-header-${crypto.randomUUID()}`; header.id = headerId; panel.id = panelId; panel.setAttribute("role", "tabpanel"); panel.setAttribute("aria-labelledby", headerId); header.setAttribute("aria-controls", panelId); }); tabList.addEventListener("click", (e) => { const header = e.target.closest(".sui-tab-header"); if (header && tabHeaders.includes(header)) { switchTab(tabContainer, header); } }); tabList.addEventListener("keydown", (e) => { if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return; const currentHeader = e.target.closest(".sui-tab-header"); if (!currentHeader || !tabHeaders.includes(currentHeader)) return; e.preventDefault(); const currentIndex = tabHeaders.indexOf(currentHeader); const newIndex = e.key === "ArrowLeft" ? (currentIndex - 1 + tabHeaders.length) % tabHeaders.length : (currentIndex + 1) % tabHeaders.length; const nextTab = tabHeaders[newIndex]; nextTab.focus(); switchTab(tabContainer, nextTab); }); const syncKey = tabContainer.dataset.syncKey; const initiallyActiveTab = tabHeaders.find((h) => h.classList.contains("active")) ?? tabHeaders[0]; if (syncKey) { const storage = tabContainer.dataset.storageStrategy ?? "session"; const storageLayer = storage === "session" ? sessionStorage : localStorage; const activeTabIndex = storageLayer.getItem(syncKey); if (activeTabIndex) { const activeTab = tabHeaders[Number.parseInt(activeTabIndex, 10)]; if (activeTab) { switchTab(tabContainer, activeTab); } } else { switchTab(tabContainer, initiallyActiveTab); } document.addEventListener(`sui-tab-switch:${syncKey}`, (e) => { const { tabIndex, uniqueId } = e.detail; if (uniqueId === tabContainer.dataset.uniqueId) return; const newTab = tabHeaders[tabIndex]; if (newTab) { switchTab(tabContainer, newTab, true); } }); } else { switchTab(tabContainer, initiallyActiveTab); } }; const loadTabs = () => { for (const tabContainer of document.querySelectorAll(".sui-tabs-container")) { setupTabContainer(tabContainer); } }; document.addEventListener("astro:page-load", loadTabs);