diff --git a/public/main-menu.js b/public/main-menu.js index c91e58e05..8135cc581 100644 --- a/public/main-menu.js +++ b/public/main-menu.js @@ -1 +1 @@ -(()=>{"use strict";function n(t){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},n(t)}function t(t){var e=function(t){if("object"!=n(t)||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var o=e.call(t,"string");if("object"!=n(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==n(e)?e:e+""}function e(n,e){for(var o=0;o0}},{key:"hasProducts",value:function(n){var t;return(null==n||null===(t=n.products)||void 0===t?void 0:t.length)>0}},{key:"getDesktopClasses",value:function(n,t){return"!hidden lg:!block ".concat(t?"root-level lg:!inline-block":"relative"," ").concat(n.products?" mega-menu":"","\n ").concat(this.hasChildren(n)?" has-children":"")}},{key:"getMobileMenu",value:function(n,t){var e=this,o=n.image?'').concat(n.title,''):"";return'\n
  • \n ").concat(this.hasChildren(n)?'\n \n ').concat(o,"\n ").concat(n.title,'\n \n \n "):'\n \n ").concat(o,"\n ").concat(n.title||"","\n "),"\n
  • ")}},{key:"getDesktopMenu",value:function(n,t){var e=this;return'\n
  • \n \n ").concat(n.title,"\n \n ").concat(this.hasChildren(n)?'\n "):"","\n
  • ")}},{key:"getMenus",value:function(){var n=this;return this.menus.map((function(t){return"\n ".concat(n.getMobileMenu(t,n.displayAllText),"\n ").concat(n.getDesktopMenu(t,!0),"\n ")})).join("\n")}},{key:"render",value:function(){this.innerHTML='\n \n ')}}])&&e(a.prototype,i),Object.defineProperty(a,"prototype",{writable:!1}),a;var a,i}(a(HTMLElement));customElements.define("custom-main-menu",i)})(); \ No newline at end of file +(()=>{"use strict";function n(n,e){(null==e||e>n.length)&&(e=n.length);for(var t=0,r=Array(e);t0}},{key:"hasProducts",value:function(n){var e;return(null==n||null===(e=n.products)||void 0===e?void 0:e.length)>0}},{key:"getDesktopClasses",value:function(n,e){return"!hidden lg:!block ".concat(e?"root-level lg:!inline-block":"relative"," ").concat(n.products?" mega-menu":"","\n ").concat(this.hasChildren(n)?" has-children":"")}},{key:"getMobileMenu",value:function(n,e){var t=this,r=n.image?'').concat(n.title,''):"";return'\n
  • \n ").concat(this.hasChildren(n)?'\n \n ').concat(r,"\n ").concat(n.title,'\n \n
      \n
    • \n ').concat(e,"\n
    • \n ").concat(n.children.map((function(n){return t.getMobileMenu(n,e)})).join(""),"\n
    \n "):'\n \n ").concat(r,"\n ").concat(n.title||"","\n "),"\n
  • ")}},{key:"getDesktopMenu",value:function(n,e){var t=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return'\n
  • \n \n ").concat(n.title,"\n \n ").concat(this.hasChildren(n)?'\n "):"","\n
  • ")}},{key:"getMenus",value:function(){var n=this;return this.menus.map((function(e){return"\n ".concat(n.getMobileMenu(e,n.displayAllText),"\n ").concat(n.getDesktopMenu(e,!0),"\n ")})).join("\n")}},{key:"createMoreDropdown",value:function(){var n=this;return 0===this.overflowMenus.length?"":'\n
  • \n \n ').concat(this.moreText,'\n \n \n
  • ")}},{key:"initializeResponsiveMenu",value:function(){var n=this;if(!(window.innerWidth<1024)&&this.querySelector(".main-menu")){this.checkMenuOverflow();var e=this.debounce((function(){n.checkMenuOverflow()}),250);window.addEventListener("resize",e)}}},{key:"checkMenuOverflow",value:function(){var e=this,t=this.querySelector(".main-menu");if(t){var r=t.closest(".container");if(r){this.visibleMenus=function(e){return function(e){if(Array.isArray(e))return n(e)}(e)||function(n){if("undefined"!=typeof Symbol&&null!=n[Symbol.iterator]||null!=n["@@iterator"])return Array.from(n)}(e)||function(e,t){if(e){if("string"==typeof e)return n(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}(this.menus),this.overflowMenus=[];var o=t.querySelector("#more-menu-dropdown");o&&o.remove();var i=t.querySelectorAll("[data-menu-item]");i.forEach((function(n){n.style.display=""}));var c=r.offsetWidth,a=r.querySelector(".flex").children,l=0;Array.from(a).forEach((function(n){n.contains(t)||(l+=n.offsetWidth)}));var u=c-l-100,s=0,f=0;i.forEach((function(n,t){var r=n.offsetWidth;s+r<=u&&t0&&t.insertAdjacentHTML("beforeend",this.createMoreDropdown())}}}},{key:"debounce",value:function(n,e){var t;return function(){for(var r=arguments.length,o=new Array(r),i=0;i\n \n \n ')}}],u&&r(a.prototype,u),Object.defineProperty(a,"prototype",{writable:!1}),a;var a,u}(a(HTMLElement));customElements.define("custom-main-menu",u)})(); \ No newline at end of file diff --git a/src/assets/js/app.js b/src/assets/js/app.js index 5e8e4ad11..05e1a8d47 100644 --- a/src/assets/js/app.js +++ b/src/assets/js/app.js @@ -22,7 +22,15 @@ class App extends AppHelpers { this.initiateModals(); this.initiateCollapse(); this.initAttachWishlistListeners(); - this.changeMenuDirection() + + // Ensure #more-menu-dropdown exists before running changeMenuDirection + const menuDirInterval = setInterval(() => { + if (document.querySelector('#more-menu-dropdown')) { + this.changeMenuDirection(); + clearInterval(menuDirInterval); + } + }, 100); + initTootTip(); this.loadModalImgOnclick(); @@ -38,18 +46,22 @@ class App extends AppHelpers { return this; } - // fix Menu Direction at the third level >> The menu at the third level was popping off the page - changeMenuDirection(){ - app.all('.root-level.has-children',item=>{ - if(item.classList.contains('change-menu-dir')) return; - app.on('mouseover',item,()=>{ - let submenu = item.querySelector('.sub-menu .sub-menu'); - if(submenu){ - let rect = submenu.getBoundingClientRect(); - (rect.left < 10 || rect.right > window.innerWidth - 10) && app.addClass(item,'change-menu-dir') - } - }) - }) + changeMenuDirection() { + setTimeout(() => { + app.all('.root-level.has-children', item => { + if (item.classList.contains('change-menu-dir')) return; + app.on('mouseover', item, () => { + let allSubMenus = item.querySelectorAll('.sub-menu'); + allSubMenus.forEach((submenu, idx) => { + if (idx === 0) return; + let rect = submenu.getBoundingClientRect(); + if (rect.left < 10 || rect.right > window.innerWidth - 10) { + app.addClass(item, 'change-menu-dir'); + } + }); + }); + }); + }, 1000); } loadModalImgOnclick(){ diff --git a/src/assets/js/partials/main-menu.js b/src/assets/js/partials/main-menu.js index 9164056f1..8f2f864b6 100644 --- a/src/assets/js/partials/main-menu.js +++ b/src/assets/js/partials/main-menu.js @@ -5,11 +5,16 @@ class NavigationMenu extends HTMLElement { .then(() => { this.menus = []; this.displayAllText = salla.lang.get('blocks.home.display_all'); + this.moreText = salla.lang.get('common.titles.more') || 'المزيد'; + this.visibleMenus = []; + this.overflowMenus = []; return salla.api.component.getMenus() .then(({ data }) => { this.menus = data; return this.render() + }).then(() => { + this.initializeResponsiveMenu(); }).catch((error) => salla.logger.error('salla-menu::Error fetching menus', error)); }); } @@ -78,11 +83,12 @@ class NavigationMenu extends HTMLElement { * Get the desktop menu * @param {Object} menu * @param {Boolean} isRootMenu + * @param {String} additionalClasses * @returns {String} */ - getDesktopMenu(menu, isRootMenu) { + getDesktopMenu(menu, isRootMenu, additionalClasses = '') { return ` -
  • +
  • ${menu.title} @@ -111,6 +117,134 @@ class NavigationMenu extends HTMLElement { `).join('\n'); } + /** + * Create More dropdown menu + * @returns {String} + */ + createMoreDropdown() { + if (this.overflowMenus.length === 0) return ''; + + return ` +
  • + + ${this.moreText} + + +
  • `; + } + + /** + * Initialize responsive menu functionality + */ + initializeResponsiveMenu() { + if (window.innerWidth < 1024) return; // Only for desktop + + const mainMenu = this.querySelector('.main-menu'); + if (!mainMenu) return; + + // Check if more menu is enabled in theme settings + const isMoreMenuEnabled = theme.settings.get('enable_more_menu'); + if (isMoreMenuEnabled) { + this.checkMenuOverflow(); + + // Re-check on window resize + const resizeHandler = this.debounce(() => { + this.checkMenuOverflow(); + }, 250); + + window.addEventListener('resize', resizeHandler); + } + } + + /** + * Check if menu items overflow and move them to More dropdown + */ + checkMenuOverflow() { + const mainMenu = this.querySelector('.main-menu'); + if (!mainMenu) return; + + const container = mainMenu.closest('.container'); + if (!container) return; + + // Reset menus + this.visibleMenus = [...this.menus]; + this.overflowMenus = []; + + // Remove existing more dropdown + const existingMore = mainMenu.querySelector('#more-menu-dropdown'); + if (existingMore) { + existingMore.remove(); + } + + // Show all menu items first + const menuItems = mainMenu.querySelectorAll('.root-level[data-menu-item]'); + menuItems.forEach(item => { + item.style.display = ''; + }); + + // Calculate available width + const containerWidth = container.offsetWidth; + const otherElements = container.querySelector('.flex').children; + let usedWidth = 0; + + // Calculate width used by logo and other elements + Array.from(otherElements).forEach(element => { + if (!element.contains(mainMenu)) { + usedWidth += element.offsetWidth; + } + }); + + const availableWidth = containerWidth - usedWidth - 300; // 300px buffer for More dropdown + let currentWidth = 0; + let visibleCount = 0; + + // Check each menu item + menuItems.forEach((item, index) => { + const itemWidth = item.offsetWidth; + + if (currentWidth + itemWidth <= availableWidth && index < this.menus.length) { + currentWidth += itemWidth; + visibleCount++; + } else { + // Hide overflow items + item.style.setProperty('display', 'none', 'important'); + if (index < this.menus.length) { + this.overflowMenus.push(this.menus[index]); + } + } + }); + + // Update visible menus + this.visibleMenus = this.menus.slice(0, visibleCount); + + // Add More dropdown if needed + if (this.overflowMenus.length > 0) { + mainMenu.insertAdjacentHTML('beforeend', this.createMoreDropdown()); + } + } + + /** + * Debounce function to limit resize event calls + * @param {Function} func + * @param {Number} wait + * @returns {Function} + */ + debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + /** * Render the header menu */ diff --git a/src/assets/styles/04-components/menus.scss b/src/assets/styles/04-components/menus.scss index 1393b01ec..ae4fc905a 100644 --- a/src/assets/styles/04-components/menus.scss +++ b/src/assets/styles/04-components/menus.scss @@ -6,7 +6,7 @@ @media only screen and (min-width: 1024px) { .main-menu { - @apply hidden lg:flex flex-wrap items-center mx-6 pt-8 pb-0; + @apply hidden lg:flex items-center mx-6 pt-8 pb-0 flex-nowrap; .fixed-pinned & { padding-top: 0; @@ -22,7 +22,7 @@ @apply inline-block; > a{ - @apply font-bold pt-0 pb-8; + @apply font-bold pt-0 pb-8 whitespace-nowrap; } } } @@ -99,6 +99,15 @@ transform: translateY(0); } } + + // More dropdown specific styles + #more-menu-dropdown { + @apply flex-shrink-0; + + > a { + @apply whitespace-nowrap; + } + } } .main-menu .sub-menu ul > li:not(:first-child) > .sub-menu{ diff --git a/twilight.json b/twilight.json index a21feef68..b546aefcd 100644 --- a/twilight.json +++ b/twilight.json @@ -117,6 +117,17 @@ "value": true, "selected": true }, + { + "type": "boolean", + "icon": "sicon-toggle-off", + "label": "تفعيل قائمة المزيد في القائمة الرئيسية", + "description": "عند تفعيلها، ستظهر الروابط الإضافية في قائمة منسدلة تسمى 'المزيد' عند عدم وجود مساحة كافية", + "id": "enable_more_menu", + "format": "switch", + "required": false, + "value": true, + "selected": true + }, { "type": "static", "id": "static-line3",