diff --git a/view/adminhtml/templates/menu-magefan.phtml b/view/adminhtml/templates/menu-magefan.phtml
index 9c824cd..184444b 100644
--- a/view/adminhtml/templates/menu-magefan.phtml
+++ b/view/adminhtml/templates/menu-magefan.phtml
@@ -12,32 +12,222 @@
* @var $mfSecureRenderer \Magefan\Community\Api\SecureHtmlRendererInterface
*/
?>
+
+
+
ul[role=menu]');
- if (submenu.classList.contains('_show') && submenuDiv && submenuList) {
- submenuList.style.maxHeight = (document.documentElement.clientHeight - 120) + 'px';
+ document.addEventListener('DOMContentLoaded', function () {
+ const menu = document.querySelector('#menu-magefan-community-elements');
+ if (!menu) return;
+
+ // Get background color for submenu
+ const bgSubmenu = document.querySelector('.admin__menu .level-0 > .submenu');
+ let bgColor = '#4a4542'
+ if (bgSubmenu) {
+ bgColor = window.getComputedStyle(bgSubmenu).backgroundColor;
+ }
+
+
+ // 1. Processing ALL .level-1.parent add title + button close
+ const level1Parents = menu.querySelectorAll('.level-1.parent');
+
+ level1Parents.forEach(parent => {
+ // 1.1. Get group title and submenu
+ const groupTitleSpan = parent.querySelector('.submenu-group-title span');
+ const submenu = parent.querySelector('.submenu');
+
+ // 1.2. Set background color for submenu
+ submenu.style.backgroundColor = bgColor;
+ if (!groupTitleSpan || !submenu) return;
+
+ // 1.2.1. Get title text
+ const titleText = groupTitleSpan.textContent.trim();
+
+ // 1.3. Check if title already exists
+ if (submenu.querySelector('.submenu-item-title')) return;
+
+ // 2. Create Title
+ const itemTitle = document.createElement('strong');
+ itemTitle.className = 'submenu-item-title';
+ itemTitle.textContent = titleText;
+
+ // 3. Create Close button
+ const closeBtn = document.createElement('a');
+ closeBtn.href = '#';
+ closeBtn.className = 'action-close-submenu';
+
+ // 4. Add elements to end submenu
+ submenu.prepend(closeBtn);
+ submenu.prepend(itemTitle);
+
+ // 5. Add event listeners
+ closeBtn.addEventListener('click', function () {
+ parent.classList.remove('active');
+ });
+
+ // 6. Close active submenu on click outside submenu
+ menu.querySelector('.action-close').addEventListener('click', function () {
+ parent.classList.remove('active');
+ });
+
+ // 7. Open submenu on click parent
+ parent.addEventListener('click', function () {
+ var isOpen = document.querySelector('#menu-magefan-community-elements .level-1.parent.active .submenu._show');
+ if (isOpen) {
+ isOpen.classList.remove('_show');
+ }
+ this.classList.add('active');
+ const submenuParentList = this.querySelector('.submenu');
+ if (submenuParentList) {
+ submenuParentList.classList.toggle('_show');
+ }
+ });
+ });
+
+
+ // Removing columns
+ const submenuContainer = menu.querySelector('.level-0 > .submenu > ul[role=\"menu\"]');
+ if (submenuContainer) {
+
+ // Finds all elements with classes inside .parent.level-1 .level-0 > .submenu
+ const allLevel1Parents = Array.from(menu.querySelectorAll('.level-0 > .submenu .parent.level-1'));
+
+ // Clean (delete .column and everything inside)
+ submenuContainer.innerHTML = '';
+
+ // Add all .parent.level-1 directly to submenu > ul
+ allLevel1Parents.forEach(parent => {
+ submenuContainer.appendChild(parent);
+ });
+
+ // Sorting without columns
+ const level1Items = Array.from(submenuContainer.querySelectorAll('.parent.level-1'));
+ level1Items.sort((a, b) => {
+ const textA = (a.querySelector('.submenu-group-title span')?.textContent || '').trim();
+ const textB = (b.querySelector('.submenu-group-title span')?.textContent || '').trim();
+ return textA.localeCompare(textB);
+ });
+ level1Items.forEach(item => submenuContainer.appendChild(item));
+
+
+ // SEARCH AND FILTER
+ if (level1Items.length > 16) {
+ // Create a search Container
+ const searchContainer = document.createElement('div');
+ searchContainer.className = 'mf-menu-search-container';
+
+ // Create Input
+ const searchInput = document.createElement('input');
+ searchInput.type = 'text';
+ searchInput.placeholder = 'Search in the menu...';
+ searchInput.className = 'mf-menu-search-input';
+
+ // Search button close
+ const searchClose = document.createElement('div');
+ searchClose.className = 'action-search-close';
+ // searchClose.addEventListener('click', function () {
+ // searchInput.value = '';
+ // });
+
+ // Insert Input in Container
+ searchContainer.appendChild(searchInput);
+
+ // Insert button close
+ searchContainer.appendChild(searchClose);
+
+ // Insert search Container to top submenu Container
+ submenuContainer.parentElement.insertBefore(searchContainer, submenuContainer);
+
+ // Search filtering
+ searchInput.addEventListener('input', function() {
+ const searchText = this.value.toLowerCase().trim();
+
+ // Show/hide close button
+ if (this.value.length > 0) {
+ searchClose.classList.add('_show');
+ } else {
+ searchClose.classList.remove('_show');
+ }
+
+ level1Items.forEach(item => {
+ const titleSpan = item.querySelector('.submenu-group-title span');
+ const titleText = titleSpan ? titleSpan.textContent.toLowerCase() : '';
+
+ // Filtering
+ if (searchText === '' || titleText.includes(searchText)) {
+ item.style.display = ''; // visible
+ } else {
+ item.style.display = 'none'; // hidden
+ }
+
+ // Highlighting found text
+ if (searchText !== '' && titleText.includes(searchText)) {
+ titleSpan.innerHTML = titleSpan.textContent.replace(
+ new RegExp(searchText, 'gi'),
+ match => `\${match}`
+ );
+ } else {
+ titleSpan.textContent = titleSpan.textContent; // clear the backlight
+ }
+ });
+ });
+
+ // Click handler for close button
+ searchClose.addEventListener('click', function() {
+ // Clear input value
+ searchInput.value = '';
+
+ // Hide close button
+ this.classList.remove('_show');
+
+ // Show all items
+ level1Items.forEach(item => {
+ item.style.display = '';
+
+ // Clear highlighting
+ const titleSpan = item.querySelector('.submenu-group-title span');
+ if (titleSpan) {
+ titleSpan.textContent = titleSpan.textContent;
+ }
+ });
+
+ // Set focus back to input
+ searchInput.focus();
+ });
+ }
+ }
+
+
+ // Observer for close active submenu
+ let observer = new MutationObserver(function (mutationsList) {
+ if (!menu.classList.contains('_show')) {
+ let activeSubmenu = menu.querySelector('.level-1.parent.active .submenu._show');
+ if (activeSubmenu) {
+ activeSubmenu.classList.toggle('_show');
+ }
+ }
+
+ const submenuDiv = menu.querySelector('.submenu');
+ const submenuList = menu.querySelector('.submenu > ul[role=menu]');
+ if (menu.classList.contains('_show') && submenuDiv && submenuList) {
+ const search = menu.querySelector('.mf-menu-search-container')
+ let height = 0;
+ if (search) {
+ height = search.offsetHeight;
+ }
+ submenuList.style.maxHeight = (document.documentElement.clientHeight - 147 - height) + 'px';
submenuDiv.style.position = 'fixed';
submenuDiv.style.left = '88px';
document.body.style.overflowY = 'hidden';
} else {
document.body.style.overflowY = 'auto';
}
- }
+ });
+ observer.observe(menu, { attributes: true, attributeFilter: ['class'] });
- let menuElement = document.querySelector('#menu-magefan-community-elements');
- if (menuElement) {
- var observer = new MutationObserver(updateMenuHeight);
- observer.observe(menuElement, { attributes: true });
- }
});
"
-?>
+ ?>
= /* @noEscape */ $mfSecureRenderer->renderTag('script', [], $script, false) ?>
diff --git a/view/adminhtml/web/css/source/_module.less b/view/adminhtml/web/css/source/_module.less
index f2a8738..efab975 100644
--- a/view/adminhtml/web/css/source/_module.less
+++ b/view/adminhtml/web/css/source/_module.less
@@ -1,33 +1,189 @@
+
.admin__menu {
-
+
// Magefan Community Menu Lvl-0
#menu-magefan-community-elements {
- &._show {
- > .submenu {
- width: calc(100vw - 8.800000000000001rem);
- > ul[role=menu] {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(28rem, 1fr));
- overflow-x: hidden;
- overflow-y: auto;
- > .column {
- display: contents;
+ overflow-x: visible;
+ > .submenu {
+ width: auto;
+ bottom: 0;
+ height: 100vh;
+ max-height: 100vh;
+ min-height: 770px;
+ min-width: 320px;
+ z-index: 698;
+
+ // Submenu title
+ .submenu-title {}
+
+ // Button Close
+ a.action-close-submenu {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 2.4rem 2.8rem;
+ z-index: 999;
+ &:before {
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Admin Icons';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ speak: none;
+ content: '\e62f';
+ display: block;
+ color: #ffffff;
+ font-size: 1.7rem;
+ width: 20px;
+ height: 20px;
+ background-size: 26px;
+ margin: 0 auto .3rem auto;
+ transition: color 0.1s linear;
+ }
+ &:hover {
+ background-color: transparent;
+ }
+ }
+
+ // Search
+ .mf-menu-search-container {
+ margin: 0 30px 30px;
+ position: relative;
+ }
+ .action-search-close {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ right: 14px;
+ z-index: 10;
+ width: 15px;
+ height: 15px;
+ cursor: pointer;
+ opacity: 0;
+ visibility: hidden;
+ transition: all 0.2s ease;
+ }
+ .action-search-close._show {
+ opacity: 1;
+ visibility: visible;
+ }
+ .action-search-close:before {
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Admin Icons';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 15px;
+ speak: none;
+ content: '\e62f';
+ display: block;
+ width: 15px;
+ height: 15px;
+ color: #000000;
+ font-size: 15px;
+ }
+ // Input Search
+ .mf-menu-search-input {
+ width: 100%;
+ padding: 8px 30px 8px 40px;
+ color: #000;
+ border: 1px solid #292826;
+ border-radius: 4px;
+ font-size: 14px;
+ box-sizing: border-box;
+ background: #ffffff url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIiBmaWxsPSJub25lIj4NCiAgICA8cGF0aCBkPSJNMTcuNSAxNy41TDE0LjU4MzQgMTQuNTgzM00xNi42NjY3IDkuNTgzMzNDMTYuNjY2NyAxMy40OTU0IDEzLjQ5NTQgMTYuNjY2NyA5LjU4MzMzIDE2LjY2NjdDNS42NzEzMiAxNi42NjY3IDIuNSAxMy40OTU0IDIuNSA5LjU4MzMzQzIuNSA1LjY3MTMyIDUuNjcxMzIgMi41IDkuNTgzMzMgMi41QzEzLjQ5NTQgMi41IDE2LjY2NjcgNS42NzEzMiAxNi42NjY3IDkuNTgzMzNaIiBzdHJva2U9IiM0MTM2MmYiIHN0cm9rZS13aWR0aD0iMS42NjY2NyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48L3BhdGg+DQo8L3N2Zz4=") no-repeat 2% 7px;
+ }
+
+ // Submenu content
+ > ul[role='menu'] {
+ display: flex;
+ flex-direction: column;
+ //overflow-x: hidden;
+ overflow-y: auto;
+ min-height: 320px;
+ max-height: ~'calc(100vh - 90px)';
+ position: relative;
+ margin-right: 10px;
+ scrollbar-color: rgb(110, 106, 106) transparent;
+ scrollbar-width: thin;
+ .level-1 {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 0;
+ cursor: pointer;
+ .submenu-group-title {
+ font-weight: normal;
+ margin: 0;
+ pointer-events: auto;
+ &:hover {
+ background: var(--mfav-menu-bc, #403934);
+ }
+ }
+ // Submenu 2
+ .submenu {
+ display: none;
+ visibility: hidden;
+ //opacity: 0;
+ position: fixed;
+ top: 0;
+ left: 100%;
+ bottom: 0;
+ z-index: 698;
+ min-width: 310px;
+ transform: translateX(-100%);
+ transition-duration: .3s;
+ transition-property: transform, visibility;
+ transition-timing-function: ease-in-out;
+ color: #ffffff;
+ background: #4a4542;
+ box-shadow: 0 0 3px #000000;
+ padding: 2rem 0 0;
+ cursor: default;
+ strong.submenu-item-title {
+ display: block;
+ font-size: 2.2rem;
+ font-weight: 600;
+ margin-bottom: 4.2rem;
+ margin-left: 3rem;
+ margin-right: 5.8rem;
+ }
+ a.action-close {
+ padding: 2.4rem 2.8rem;
+ }
> ul[role=menu] {
- display: contents;
- > li {
- display: flex;
- flex-direction: column;
- min-width: 24.8rem;
- border-bottom: 1px dotted rgba(255, 255, 255, 0.2);
- margin-bottom: 2rem;
- padding-bottom: 2.5rem;
+ display: flex;
+ flex-direction: column;
+ margin: 0 1.5rem;
+ }
+ }
+ &:hover {
+ //background-color: #0b182a;
+ }
+ &.active {
+ .submenu {
+ &._show {
+ display: block;
+ visibility: visible;
+ //opacity: 1;
+ transform: translateX(0);
+ transition-duration: .3s;
+ transition-property: transform, visibility;
+ transition-timing-function: ease-in-out;
+ z-index: 698;
}
}
}
}
+
+ .column {
+ display: contents;
+ }
+ ul[role='menu'] {
+ display: contents;
+ }
}
}
+ // Logo menu
> a {
&:before {
content: '';