diff --git a/src/bundle/Resources/public/js/scripts/core/multilevel.popup.menu.js b/src/bundle/Resources/public/js/scripts/core/multilevel.popup.menu.js index 9776a69f16..44ee62f767 100644 --- a/src/bundle/Resources/public/js/scripts/core/multilevel.popup.menu.js +++ b/src/bundle/Resources/public/js/scripts/core/multilevel.popup.menu.js @@ -71,6 +71,7 @@ const isTopBranch = !triggerElement.classList.contains('ibexa-popup-menu__item'); const branchItems = this.getBranchItems(branchElement); const offset = isTopBranch ? [0, 3] : [-8, 2]; + const branchSearchInput = branchElement.querySelector('.ibexa-multilevel-popup-menu__search-input'); const popperInstance = Popper.createPopper(referenceElement ?? triggerElement, branchElement, { placement, @@ -91,6 +92,8 @@ ], }); + branchSearchInput.addEventListener('keyup', this.filterBranchItems, false); + branchSearchInput.addEventListener('input', this.filterBranchItems, false); branchElement.popperInstance = popperInstance; if (isTopBranch) { @@ -151,6 +154,8 @@ } updateBranchOpenState(branchElement) { + const searchInput = branchElement.querySelector('.ibexa-multilevel-popup-menu__search-input'); + const isSearchInputFilled = !!searchInput?.value; const isSubbranchOpened = (otherBranchElement) => { return ( otherBranchElement && @@ -159,7 +164,7 @@ }; const isBranchOrAnySubbranchHovered = [...this.hoveredItemsBranches, ...this.hoveredBranches].some(isSubbranchOpened); - if (isBranchOrAnySubbranchHovered) { + if (isBranchOrAnySubbranchHovered || isSearchInputFilled) { this.openBranch(branchElement); } else { this.closeWithSubbranches(branchElement); @@ -213,13 +218,21 @@ } generateMenu(menuTree) { - const { triggerElement, groups, placement, fallbackPlacements, processAfterCreated: processBranchAfterCreated } = menuTree; + const { + triggerElement, + groups, + placement, + fallbackPlacements, + processAfterCreated: processBranchAfterCreated, + hasSearch, + } = menuTree; const branchElement = this.generateBranch( { triggerElement, placement, fallbackPlacements, + hasSearch, }, processBranchAfterCreated, ); @@ -250,7 +263,7 @@ } generateBranch(data, processAfterCreated = () => {}) { - const { triggerElement, placement, fallbackPlacements } = data; + const { triggerElement, placement, fallbackPlacements, hasSearch = false } = data; const { branchTemplate } = this.container.dataset; const container = doc.createElement('div'); @@ -259,6 +272,9 @@ container.insertAdjacentHTML('beforeend', renderedItem); const newBranchElement = container.querySelector('.ibexa-multilevel-popup-menu__branch'); + const searchInputWrapper = newBranchElement.querySelector('.ibexa-multilevel-popup-menu__search'); + + searchInputWrapper.classList.toggle('ibexa-multilevel-popup-menu__search--hidden', !hasSearch); processAfterCreated(newBranchElement, data); @@ -304,7 +320,9 @@ processAfterCreated(newGroupElement, data); - branchElement.appendChild(newGroupElement); + const newGroupContainer = branchElement.querySelector('.ibexa-multilevel-popup-menu__groups'); + + newGroupContainer.appendChild(newGroupElement); return newGroupElement; } @@ -365,15 +383,46 @@ return; } + const { target } = event; const isPopupMenuExpanded = !topBranch.classList.contains('ibexa-popup-menu--hidden'); - const isClickInsideTrigger = this.triggerElement.contains(event.target); + const isClickInsideTrigger = this.triggerElement.contains(target); + const isTargetBranch = target.classList.contains('ibexa-multilevel-popup-menu__branch'); + const targetBranch = target.closest('.ibexa-multilevel-popup-menu__branch'); + const isClickInsideMenu = isTargetBranch || !!targetBranch; - if (!isPopupMenuExpanded || isClickInsideTrigger) { + if (!isPopupMenuExpanded || isClickInsideTrigger || isClickInsideMenu) { return; } + const branchsSearchInput = doc.querySelectorAll('.ibexa-multilevel-popup-menu__search-input'); + + branchsSearchInput.forEach((searchInput) => { + if (searchInput.value !== '') { + const searchInputBranch = searchInput.closest('.ibexa-multilevel-popup-menu__branch'); + + searchInput.value = ''; + searchInputBranch.dispatchEvent(new Event('mouseleave')); + searchInput.dispatchEvent(new Event('input')); + } + }); + this.closeWithSubbranches(topBranch); } + + filterBranchItems(event) { + const searchInput = event.currentTarget; + const branch = searchInput.closest('.ibexa-multilevel-popup-menu__branch'); + const branchItems = branch.querySelectorAll('.ibexa-popup-menu__group > .ibexa-popup-menu__item'); + const phraseLowerCase = searchInput.value.toLowerCase(); + + branchItems.forEach((item) => { + const { label } = item.dataset; + const labelLowerCase = label.toLowerCase(); + const hideItem = !labelLowerCase.includes(phraseLowerCase); + + item.classList.toggle('ibexa-popup-menu__item--hidden', hideItem); + }); + } } ibexa.addConfig('core.MultilevelPopupMenu', MultilevelPopupMenu); diff --git a/src/bundle/Resources/public/js/scripts/embedded.item.actions.js b/src/bundle/Resources/public/js/scripts/embedded.item.actions.js index d3f3e12c8a..8fbb9a1195 100644 --- a/src/bundle/Resources/public/js/scripts/embedded.item.actions.js +++ b/src/bundle/Resources/public/js/scripts/embedded.item.actions.js @@ -1,6 +1,7 @@ (function (global, doc, ibexa, Routing, Translator, Popper) { + const MIN_ITEMS_NUMBER_TO_SHOW_SEARCH = 10; const MENU_PROPS = { - placement: 'bottom-end', + placement: 'bottom-start', fallbackPlacements: ['bottom-start', 'top-end', 'top-start'], }; const token = document.querySelector('meta[name="CSRF-Token"]').content; @@ -117,6 +118,7 @@ return { label: Translator.trans(/*@Desc("Edit")*/ 'embedded_items.action.edit', {}, 'ibexa_content'), branch: { + hasSearch: languages.length >= MIN_ITEMS_NUMBER_TO_SHOW_SEARCH, groups: [ { id: 'edit-group', diff --git a/src/bundle/Resources/public/scss/_multilevel-popup-menu.scss b/src/bundle/Resources/public/scss/_multilevel-popup-menu.scss new file mode 100644 index 0000000000..e141a5dda6 --- /dev/null +++ b/src/bundle/Resources/public/scss/_multilevel-popup-menu.scss @@ -0,0 +1,19 @@ +.ibexa-multilevel-popup-menu { + &__search { + margin-bottom: calculateRem(4px); + padding: 0 calculateRem(8px); + + &--hidden { + display: none; + } + } + + &__search-input { + border-radius: $ibexa-border-radius; + } + + &__groups { + max-height: calculateRem(390px); + overflow-y: auto; + } +} diff --git a/src/bundle/Resources/public/scss/ibexa.scss b/src/bundle/Resources/public/scss/ibexa.scss index e5ae89c97e..d0d6278233 100644 --- a/src/bundle/Resources/public/scss/ibexa.scss +++ b/src/bundle/Resources/public/scss/ibexa.scss @@ -90,6 +90,7 @@ @import 'links'; @import 'footer'; @import 'popup-menu'; +@import 'multilevel-popup-menu'; @import 'header-user-menu'; @import 'main-header'; @import 'main-container'; diff --git a/src/bundle/Resources/views/themes/admin/ui/component/multilevel_popup_menu/multilevel_popup_menu_branch.html.twig b/src/bundle/Resources/views/themes/admin/ui/component/multilevel_popup_menu/multilevel_popup_menu_branch.html.twig index f9bbd8c76f..7dfaa63291 100644 --- a/src/bundle/Resources/views/themes/admin/ui/component/multilevel_popup_menu/multilevel_popup_menu_branch.html.twig +++ b/src/bundle/Resources/views/themes/admin/ui/component/multilevel_popup_menu/multilevel_popup_menu_branch.html.twig @@ -8,17 +8,29 @@ }) -%} {%- block branch -%} + {% trans_default_domain 'ibexa_dropdown' %} +