diff --git a/src/bundle/Resources/config/bazinga_js_translation.yaml b/src/bundle/Resources/config/bazinga_js_translation.yaml index ad62c5d39e..830fa2dbab 100644 --- a/src/bundle/Resources/config/bazinga_js_translation.yaml +++ b/src/bundle/Resources/config/bazinga_js_translation.yaml @@ -12,5 +12,6 @@ active_domains: - 'ibexa_user_invitation' - 'ibexa_content_type' - 'ibexa_dropdown' + - 'ibexa_collapse' - 'ibexa_popup_menu' - 'messages' diff --git a/src/bundle/Resources/public/js/scripts/core/collapse.js b/src/bundle/Resources/public/js/scripts/core/collapse.js index 77da449ee4..d7fce01003 100644 --- a/src/bundle/Resources/public/js/scripts/core/collapse.js +++ b/src/bundle/Resources/public/js/scripts/core/collapse.js @@ -1,4 +1,16 @@ -(function (global, doc) { +(function (global, doc, bootstrap, Translator) { + let toggleAllTimeout; + const TOGGLE_TIMEOUT = 200; + const toggleAllBtns = [...doc.querySelectorAll(`[data-multi-collapse-btn-id]`)]; + const toggleMultiCollapseBtn = (btn, changeToCollapseAll) => { + const displayedText = changeToCollapseAll + ? Translator.trans(/*@Desc("Collapse all sections")*/ 'collapse.collapse_all', {}, 'ibexa_collapse') + : Translator.trans(/*@Desc("Expand all sections")*/ 'collapse.expand_all', {}, 'ibexa_collapse'); + + btn.innerText = displayedText; + btn.classList.toggle('ibexa-multi-collapse__btn--expand-all-label', !changeToCollapseAll); + }; + doc.querySelectorAll('.ibexa-collapse').forEach((collapseNode) => { const toggleButton = collapseNode.querySelector('.ibexa-collapse__toggle-btn'); const isCollapsed = toggleButton.classList.contains('collapsed'); @@ -6,16 +18,82 @@ collapseNode.classList.toggle('ibexa-collapse--collapsed', isCollapsed); collapseNode.dataset.collapsed = isCollapsed; + const multiCollapseNode = collapseNode.closest(`[data-multi-collapse-body]`); + collapseNode.addEventListener('hide.bs.collapse', (event) => { event.stopPropagation(); + collapseNode.classList.add('ibexa-collapse--collapsed'); collapseNode.dataset.collapsed = true; }); collapseNode.addEventListener('show.bs.collapse', (event) => { event.stopPropagation(); + collapseNode.classList.remove('ibexa-collapse--collapsed'); collapseNode.dataset.collapsed = false; }); + + if (!multiCollapseNode || !toggleAllBtns.length) { + return; + } + + const currentToggleAllBtn = doc.querySelector(`[data-multi-collapse-btn-id="${multiCollapseNode.dataset.multiCollapseBody}"]`); + const attachClickToggleHandler = (section) => { + section.addEventListener('click', () => { + const currentCollapsibleBtns = [...multiCollapseNode.querySelectorAll('[data-bs-toggle]')]; + + global.clearTimeout(toggleAllTimeout); + + toggleAllTimeout = global.setTimeout(() => { + const collapsedCount = currentCollapsibleBtns.filter((btn) => btn.classList.contains('collapsed')).length; + const shouldBeToggled = collapsedCount === currentCollapsibleBtns.length || collapsedCount === 0; + + if (shouldBeToggled) { + toggleMultiCollapseBtn(currentToggleAllBtn, collapsedCount === 0); + } + }, TOGGLE_TIMEOUT); + }); + }; + + collapseNode.querySelectorAll('[data-bs-toggle]').forEach(attachClickToggleHandler); }); -})(window, window.document); + + const handleCollapseAction = (multiCollapseNode, isExpandAction) => { + multiCollapseNode.querySelectorAll('.ibexa-collapse').forEach((collapseNode) => { + const isElementCollapsed = collapseNode.classList.contains('ibexa-collapse--collapsed'); + const shouldBeToggled = isExpandAction === isElementCollapsed; + + if (shouldBeToggled) { + const element = bootstrap.Collapse.getOrCreateInstance(collapseNode.querySelector('.ibexa-multi-collapse__single-item')); + + if (isExpandAction) { + element.show(); + } else { + element.hide(); + } + } + }); + }; + const attachAllElementsToggler = (btn) => { + btn.addEventListener('click', () => { + const collapseId = btn.dataset.multiCollapseBtnId; + + if (!collapseId) { + return; + } + + const multiCollapseBodyNode = doc.querySelector(`[data-multi-collapse-body="${collapseId}"]`); + + global.clearTimeout(toggleAllTimeout); + + toggleAllTimeout = global.setTimeout(() => { + const isExpandingAction = btn.classList.contains('ibexa-multi-collapse__btn--expand-all-label'); + + handleCollapseAction(multiCollapseBodyNode, isExpandingAction); + toggleMultiCollapseBtn(btn, isExpandingAction); + }, TOGGLE_TIMEOUT); + }); + }; + toggleAllBtns.forEach(attachAllElementsToggler); +})(window, window.document, window.bootstrap, window.Translator); diff --git a/src/bundle/Resources/public/scss/_anchor-navigation.scss b/src/bundle/Resources/public/scss/_anchor-navigation.scss index 2d06541482..a2325f0e6d 100644 --- a/src/bundle/Resources/public/scss/_anchor-navigation.scss +++ b/src/bundle/Resources/public/scss/_anchor-navigation.scss @@ -18,6 +18,10 @@ &:last-child { border-bottom: none; } + + &--no-border { + border-bottom: none; + } } &__section-group { diff --git a/src/bundle/Resources/public/scss/_multi-collapse.scss b/src/bundle/Resources/public/scss/_multi-collapse.scss new file mode 100644 index 0000000000..bf49d0c4b1 --- /dev/null +++ b/src/bundle/Resources/public/scss/_multi-collapse.scss @@ -0,0 +1,32 @@ +.ibexa-multi-collapse { + &__header { + display: flex; + align-items: center; + justify-content: space-between; + } + + &__content { + width: 100%; + margin: calculateRem(16px) 0; + } + + &__input { + margin-top: calculateRem(16px); + } + + &__group { + .ibexa-collapse { + &__header { + display: flex; + border-bottom: calculateRem(1px) solid $ibexa-color-light; + } + + &__toggle-btn:not(.ibexa-collapse__toggle-btn--status) { + font-size: $ibexa-text-font-size-large; + font-weight: bold; + margin-right: auto; + padding-left: 0; + } + } + } +} diff --git a/src/bundle/Resources/public/scss/ibexa.scss b/src/bundle/Resources/public/scss/ibexa.scss index 7a5e84f112..bb74981f5f 100644 --- a/src/bundle/Resources/public/scss/ibexa.scss +++ b/src/bundle/Resources/public/scss/ibexa.scss @@ -102,6 +102,7 @@ @import 'autosave'; @import 'side-menu'; @import 'collapse'; +@import 'multi-collapse'; @import 'tag-view-select'; @import 'grid-view'; @import 'grid-view-item'; diff --git a/src/bundle/Resources/translations/ibexa_collapse.en.xliff b/src/bundle/Resources/translations/ibexa_collapse.en.xliff new file mode 100644 index 0000000000..96aa765a96 --- /dev/null +++ b/src/bundle/Resources/translations/ibexa_collapse.en.xliff @@ -0,0 +1,21 @@ + + + +
+ + The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. +
+ + + Collapse all sections + Collapse all sections + key: collapse.collapse_all + + + Expand all sections + Expand all sections + key: collapse.expand_all + + +
+
diff --git a/src/bundle/Resources/views/themes/admin/ui/component/collapse_all_btn/collapse_all_btn.html.twig b/src/bundle/Resources/views/themes/admin/ui/component/collapse_all_btn/collapse_all_btn.html.twig new file mode 100644 index 0000000000..9e31262283 --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/ui/component/collapse_all_btn/collapse_all_btn.html.twig @@ -0,0 +1,7 @@ +{% trans_default_domain 'ibexa_collapse' %} + +
+ +