192 lines
5.9 KiB
JavaScript
192 lines
5.9 KiB
JavaScript
export function initAsidePanels(options = {}) {
|
|
const {
|
|
barSelector = '.aside-icon-bar',
|
|
panelSelector = '.app-sidebar-panel',
|
|
titleSelector = '.app-sidebar-title',
|
|
toolsSelector = '[data-aside-tools]',
|
|
revealSidebarOnActivate = true,
|
|
persistSidebarStateOnActivate = true
|
|
} = options;
|
|
|
|
const bar = document.querySelector(barSelector);
|
|
if (!bar) return;
|
|
|
|
const tabs = Array.from(bar.querySelectorAll('[data-aside-target]'));
|
|
if (!tabs.length) return;
|
|
|
|
const panels = Array.from(document.querySelectorAll(panelSelector));
|
|
const titleEl = document.querySelector(titleSelector);
|
|
const toolsHost = document.querySelector(toolsSelector);
|
|
const sidebarController = window.AppSidebar || null;
|
|
const defaultTitle = titleEl?.dataset.asideTitleDefault || titleEl?.textContent || '';
|
|
const storageKey = bar.dataset.asideStorage || '';
|
|
|
|
const getPanelKey = (panel) => panel?.dataset?.asidePanel || '';
|
|
const getTabKey = (tab) => tab?.dataset?.asideTarget || '';
|
|
const getPanelTools = (panel) => panel?.querySelector('[data-aside-panel-tools]') || null;
|
|
const getTabHref = (tab) => tab?.dataset?.asideHref || '';
|
|
|
|
const initPanelDetails = (panel) => {
|
|
if (!panel || panel.dataset.detailsBound === '1') return;
|
|
const storageKey = panel.dataset.asideDetailsStorage || '';
|
|
if (!storageKey) return;
|
|
const detailsEls = Array.from(panel.querySelectorAll('details[data-details-key]'));
|
|
if (!detailsEls.length) return;
|
|
|
|
const readStored = () => {
|
|
try {
|
|
const raw = window.localStorage.getItem(storageKey);
|
|
return raw ? JSON.parse(raw) : [];
|
|
} catch (error) {
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const writeStored = (value) => {
|
|
try {
|
|
const list = Array.isArray(value) ? value : [];
|
|
window.localStorage.setItem(storageKey, JSON.stringify(list));
|
|
} catch (error) {
|
|
// ignore storage errors
|
|
}
|
|
};
|
|
|
|
const applyStored = () => {
|
|
const stored = readStored();
|
|
if (!stored.length) return;
|
|
detailsEls.forEach((details) => {
|
|
details.open = stored.includes(details.dataset.detailsKey || '');
|
|
});
|
|
};
|
|
|
|
detailsEls.forEach((details) => {
|
|
details.addEventListener('toggle', () => {
|
|
const openKeys = detailsEls
|
|
.filter((item) => item.open)
|
|
.map((item) => item.dataset.detailsKey || '')
|
|
.filter(Boolean);
|
|
writeStored(openKeys);
|
|
});
|
|
});
|
|
|
|
applyStored();
|
|
panel.dataset.detailsBound = '1';
|
|
};
|
|
|
|
const readStored = () => {
|
|
if (!storageKey) return null;
|
|
try {
|
|
return window.localStorage.getItem(storageKey);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const writeStored = (key) => {
|
|
if (!storageKey) return;
|
|
try {
|
|
window.localStorage.setItem(storageKey, key);
|
|
} catch (error) {
|
|
// ignore storage errors
|
|
}
|
|
};
|
|
|
|
const syncTools = (panel) => {
|
|
if (!toolsHost) return;
|
|
toolsHost.innerHTML = '';
|
|
const tools = getPanelTools(panel);
|
|
if (!tools) return;
|
|
toolsHost.innerHTML = tools.innerHTML;
|
|
};
|
|
|
|
const openSidebar = (persist) => {
|
|
if (!revealSidebarOnActivate || !sidebarController) return;
|
|
if (!sidebarController.isCollapsed()) return;
|
|
sidebarController.open({ persist });
|
|
};
|
|
|
|
const setActive = (key, { fromUser = false } = {}) => {
|
|
if (!key) return;
|
|
|
|
let activePanel = null;
|
|
panels.forEach((panel) => {
|
|
const isActive = getPanelKey(panel) === key;
|
|
if (isActive) activePanel = panel;
|
|
panel.hidden = !isActive;
|
|
panel.setAttribute('aria-hidden', isActive ? 'false' : 'true');
|
|
});
|
|
|
|
tabs.forEach((tab) => {
|
|
const isActive = getTabKey(tab) === key;
|
|
tab.classList.toggle('active', isActive);
|
|
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
tab.setAttribute('tabindex', isActive ? '0' : '-1');
|
|
});
|
|
|
|
if (titleEl) {
|
|
const nextTitle = activePanel?.dataset?.asideTitle || defaultTitle;
|
|
if (nextTitle) {
|
|
titleEl.textContent = nextTitle;
|
|
}
|
|
}
|
|
if (activePanel) {
|
|
syncTools(activePanel);
|
|
initPanelDetails(activePanel);
|
|
}
|
|
writeStored(key);
|
|
if (fromUser) {
|
|
openSidebar(persistSidebarStateOnActivate);
|
|
}
|
|
};
|
|
|
|
const getInitialKey = () => {
|
|
const stored = readStored();
|
|
if (stored && panels.some((panel) => getPanelKey(panel) === stored)) {
|
|
return stored;
|
|
}
|
|
const activeTab = tabs.find((tab) => tab.classList.contains('active'));
|
|
if (activeTab) return getTabKey(activeTab);
|
|
const visiblePanel = panels.find((panel) => !panel.hidden);
|
|
if (visiblePanel) return getPanelKey(visiblePanel);
|
|
return getTabKey(tabs[0]);
|
|
};
|
|
|
|
setActive(getInitialKey(), { fromUser: false });
|
|
|
|
tabs.forEach((tab) => {
|
|
tab.addEventListener('click', (event) => {
|
|
event.preventDefault();
|
|
const key = getTabKey(tab);
|
|
const isActive = tab.classList.contains('active');
|
|
const href = getTabHref(tab);
|
|
if (href) {
|
|
const base = document.baseURI || window.location.origin;
|
|
const targetUrl = new URL(href, base);
|
|
const currentUrl = new URL(window.location.href);
|
|
const isSame = targetUrl.pathname === currentUrl.pathname && targetUrl.search === currentUrl.search;
|
|
if (!isSame) {
|
|
setActive(key, { fromUser: true });
|
|
window.location.href = targetUrl.toString();
|
|
return;
|
|
}
|
|
}
|
|
if (sidebarController && sidebarController.isCollapsed()) {
|
|
sidebarController.open({ persist: persistSidebarStateOnActivate });
|
|
setActive(key, { fromUser: true });
|
|
return;
|
|
}
|
|
if (isActive && sidebarController) {
|
|
sidebarController.close({ persist: true });
|
|
return;
|
|
}
|
|
setActive(key, { fromUser: true });
|
|
});
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => initAsidePanels());
|
|
} else {
|
|
initAsidePanels();
|
|
}
|