Files
breadcrumb-the-shire/web/js/components/app-details-state.js
fs 25370a1a55 add composer-unused, comprehensive docs, and project restructure
- 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>
2026-02-22 15:27:35 +01:00

81 lines
2.0 KiB
JavaScript

const readStored = (storageKey) => {
try {
const raw = window.localStorage.getItem(storageKey);
if (!raw) {
return [];
}
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
return [];
}
};
const writeStored = (storageKey, openKeys) => {
try {
window.localStorage.setItem(storageKey, JSON.stringify(openKeys));
} catch (error) {
// ignore storage errors
}
};
const initDetailsGroup = (group) => {
if (!group || group.dataset.detailsStorageBound === '1') {
return;
}
const storageKey = (group.dataset.detailsStorage || '').trim();
if (!storageKey) {
return;
}
const managedDetails = Array.from(group.querySelectorAll('details[data-details-key]'));
const alwaysOpenDetails = Array.from(group.querySelectorAll('details[data-details-always-open]'));
if (!managedDetails.length && !alwaysOpenDetails.length) {
return;
}
const storedOpenKeys = readStored(storageKey);
if (storedOpenKeys.length > 0) {
managedDetails.forEach((details) => {
const key = (details.dataset.detailsKey || '').trim();
if (key !== '') {
details.open = storedOpenKeys.includes(key);
}
});
}
alwaysOpenDetails.forEach((details) => {
details.open = true;
details.addEventListener('toggle', () => {
if (!details.open) {
details.open = true;
}
});
});
managedDetails.forEach((details) => {
details.addEventListener('toggle', () => {
const openKeys = managedDetails
.filter((item) => item.open)
.map((item) => (item.dataset.detailsKey || '').trim())
.filter(Boolean);
writeStored(storageKey, openKeys);
});
});
group.dataset.detailsStorageBound = '1';
};
const initDetailsState = () => {
const groups = Array.from(document.querySelectorAll('[data-details-storage]'));
groups.forEach(initDetailsGroup);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDetailsState);
} else {
initDetailsState();
}