/** * VITEC — collapse/expand toggle for container content elements in the * TYPO3 v14 Page module. * * Adds a chevron button into the `.btn-toolbar` of every VITEC container * (vitec_container, vitec_cols_*, vitec_cards_carousel — anything that * renders a nested b13 grid). Clicking it hides/shows the container body * (the nested child columns). State is persisted per element uid in * localStorage, so it survives reloads. * * Pure DOM enhancement — no core or b13 template overrides. */ class VitecContainerCollapse { constructor() { this.storagePrefix = 'vitec-ce-collapsed-'; this.ready(); } ready() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.boot()); } else { this.boot(); } } boot() { this.processAll(); // Page module can re-render columns via AJAX; watch for new elements. const root = document.querySelector('.t3js-page-columns-wrapper, .module, body'); if (root) { const observer = new MutationObserver(() => { window.clearTimeout(this._debounce); this._debounce = window.setTimeout(() => this.processAll(), 150); }); observer.observe(root, { childList: true, subtree: true }); } } processAll() { document.querySelectorAll('.t3-page-ce[data-uid]').forEach((ce) => this.enhance(ce)); } isCollapsed(uid) { try { return window.localStorage.getItem(this.storagePrefix + uid) === '1'; } catch (e) { return false; } } setCollapsed(uid, collapsed) { try { if (collapsed) { window.localStorage.setItem(this.storagePrefix + uid, '1'); } else { window.localStorage.removeItem(this.storagePrefix + uid); } } catch (e) { /* storage unavailable — ignore */ } } enhance(ce) { if (ce.dataset.vitecCollapseReady === '1') { return; } // The direct body of THIS element (not a nested child's body). const element = ce.querySelector(':scope > .t3-page-ce-element'); if (!element) { return; } const body = element.querySelector(':scope > .t3-page-ce-body'); if (!body) { return; } // A container is recognised by a nested b13 grid inside its body. const hasNestedGrid = body.querySelector('.t3-grid-container') !== null; if (!hasNestedGrid) { return; } // Best-effort VITEC filter via the header icon identifier // (b13 sets the icon identifier to the CType, e.g. "vitec_container"). const iconEl = ce.querySelector( '.t3-page-ce-header-left typo3-backend-icon[identifier], .t3-page-ce-header-left [data-identifier]' ); if (iconEl) { const identifier = iconEl.getAttribute('identifier') || iconEl.getAttribute('data-identifier') || ''; if (identifier && identifier.indexOf('vitec') === -1) { return; // a non-VITEC container — leave it alone } } const toolbar = element.querySelector( ':scope > .t3-page-ce-header .btn-toolbar .btn-group' ); if (!toolbar) { return; } ce.dataset.vitecCollapseReady = '1'; const uid = ce.dataset.uid; const button = document.createElement('button'); button.type = 'button'; button.className = 'btn btn-sm btn-default btn-borderless vitec-collapse-toggle'; button.title = 'Collapse / expand container'; button.setAttribute('aria-label', 'Collapse or expand container'); button.innerHTML = ''; toolbar.insertBefore(button, toolbar.firstChild); if (this.isCollapsed(uid)) { ce.classList.add('vitec-ce-collapsed'); } button.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); const collapsed = ce.classList.toggle('vitec-ce-collapsed'); this.setCollapsed(uid, collapsed); }); } } export default new VitecContainerCollapse();