- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks - Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists - Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services - Add Microsoft OIDC SSO, API token management, and user lifecycle features - Add swagger-ui vendor integration and OpenAPI spec - Add production Docker setup and bin/ scripts - Update composer dependencies, config, templates, and frontend assets throughout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
export function initToolbarToggles(root = document) {
|
|
const toggles = root.querySelectorAll('[data-toolbar-toggle]');
|
|
toggles.forEach((toggle) => {
|
|
if (toggle.dataset.toolbarBound === '1') {
|
|
return;
|
|
}
|
|
toggle.dataset.toolbarBound = '1';
|
|
if (!toggle.hasAttribute('type')) {
|
|
toggle.setAttribute('type', 'button');
|
|
}
|
|
|
|
const targetSelector = toggle.dataset.toolbarTarget || toggle.getAttribute('aria-controls');
|
|
if (!targetSelector) {
|
|
return;
|
|
}
|
|
|
|
const targets = Array.from(root.querySelectorAll(targetSelector)).filter(Boolean);
|
|
if (!targets.length) {
|
|
return;
|
|
}
|
|
|
|
const labelTarget = toggle.querySelector('[data-toolbar-label]');
|
|
const showLabel = toggle.dataset.toolbarTextShow;
|
|
const hideLabel = toggle.dataset.toolbarTextHide;
|
|
const storageKey =
|
|
toggle.dataset.toolbarStorageKey ||
|
|
(targets.length === 1 && targets[0].id
|
|
? `toolbar:${targets[0].id}`
|
|
: `toolbar:${targetSelector}`);
|
|
|
|
if (!toggle.getAttribute('aria-controls') && targets.length === 1 && targets[0].id) {
|
|
toggle.setAttribute('aria-controls', targets[0].id);
|
|
}
|
|
|
|
targets.forEach((target) => {
|
|
if (target.dataset.toolbarInitialized === '1') {
|
|
return;
|
|
}
|
|
const initial = target.dataset.toolbarInitial;
|
|
if (initial === 'hidden') {
|
|
target.hidden = true;
|
|
} else if (initial === 'shown') {
|
|
target.hidden = false;
|
|
}
|
|
target.dataset.toolbarInitialized = '1';
|
|
});
|
|
|
|
if (storageKey) {
|
|
try {
|
|
const stored = localStorage.getItem(storageKey);
|
|
if (stored === 'hidden' || stored === 'shown') {
|
|
const shouldShow = stored === 'shown';
|
|
targets.forEach((target) => {
|
|
target.hidden = !shouldShow;
|
|
});
|
|
}
|
|
} catch (error) {
|
|
// localStorage not available
|
|
}
|
|
}
|
|
|
|
const getExpanded = () => targets.every((target) => !target.hidden);
|
|
const setExpanded = (expanded) => {
|
|
targets.forEach((target) => {
|
|
target.hidden = !expanded;
|
|
});
|
|
toggle.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
if (labelTarget && (showLabel || hideLabel)) {
|
|
labelTarget.textContent = expanded ? (hideLabel || labelTarget.textContent) : (showLabel || labelTarget.textContent);
|
|
}
|
|
if (storageKey) {
|
|
try {
|
|
localStorage.setItem(storageKey, expanded ? 'shown' : 'hidden');
|
|
} catch (error) {
|
|
// localStorage not available
|
|
}
|
|
}
|
|
};
|
|
|
|
setExpanded(getExpanded());
|
|
|
|
toggle.addEventListener('click', () => {
|
|
setExpanded(!getExpanded());
|
|
});
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => initToolbarToggles());
|
|
} else {
|
|
initToolbarToggles();
|
|
}
|