From a236c1cf26625b473de8aca434a8fb1721c17b40 Mon Sep 17 00:00:00 2001 From: Manya Date: Fri, 19 Jun 2026 20:44:02 +0800 Subject: [PATCH] Replace mobile navbar scroll with hamburger menu Co-authored-by: Cursor --- packages/vue-components/src/Dropdown.vue | 24 +- packages/vue-components/src/Navbar.vue | 333 ++++++++++++++---- .../__snapshots__/Navbar.spec.js.snap | 120 +++++-- 3 files changed, 373 insertions(+), 104 deletions(-) diff --git a/packages/vue-components/src/Dropdown.vue b/packages/vue-components/src/Dropdown.vue index 5cee2ce716..e19b16ad57 100644 --- a/packages/vue-components/src/Dropdown.vue +++ b/packages/vue-components/src/Dropdown.vue @@ -247,11 +247,27 @@ export default { } .navbar-default .dropdown-menu { - position: absolute; + position: static; + float: none; max-width: 100%; - max-height: 75vh; - overflow-y: auto; - overscroll-behavior: contain; + max-height: none; + overflow-y: visible; + overscroll-behavior: auto; + border: 0; + border-radius: 0.375rem; + box-shadow: none; + margin: 0.125rem 0 0.25rem; + padding: 0.25rem 0 0.25rem 0.75rem; + background: rgb(var(--bs-emphasis-color-rgb) / 5%); + } + + .navbar-dark .navbar-default .dropdown-menu { + background: rgb(255 255 255 / 5%); + } + + .navbar-default .dropdown-menu > li > a, + .navbar-default .dropdown-menu > .dropdown-submenu > a { + padding: 0.625rem 0.5rem; } .navbar-default .dropdown-menu-end { diff --git a/packages/vue-components/src/Navbar.vue b/packages/vue-components/src/Navbar.vue index 02f6e693d3..eef6ae4a0a 100644 --- a/packages/vue-components/src/Navbar.vue +++ b/packages/vue-components/src/Navbar.vue @@ -8,17 +8,34 @@ - + @@ -78,6 +95,7 @@ export default { id: 'bs-example-navbar-collapse-1', styles: {}, isLowerNavbarShowing: false, + isMobileNavOpen: false, }; }, computed: { @@ -240,6 +258,24 @@ export default { this.isLowerNavbarShowing = false; } }, + toggleMobileNav() { + this.isMobileNavOpen = !this.isMobileNavOpen; + }, + closeMobileNav() { + this.isMobileNavOpen = false; + }, + handleWindowResize() { + this.toggleLowerNavbar(); + if (window.innerWidth >= 768) { + this.closeMobileNav(); + } + }, + bindMobileNavCloseOnNavigate() { + this.$refs.navbarDefault.querySelectorAll('a.nav-link:not(.dropdown-toggle), a.dropdown-item') + .forEach((link) => { + link.addEventListener('click', this.closeMobileNav); + }); + }, }, created() { this._navbar = true; @@ -264,55 +300,70 @@ export default { // highlight current nav link this.highlightLink(window.location.href); - // scroll default navbar horizontally to current link if it is beyond the current scroll - const currentNavlink = $(this.$refs.navbarDefault).find('.current')[0]; - if (currentNavlink && window.innerWidth < 768 - && currentNavlink.offsetLeft + currentNavlink.offsetWidth > window.innerWidth) { - this.$refs.navbarDefault.scrollLeft = currentNavlink.offsetLeft + currentNavlink.offsetWidth - - window.innerWidth; - } - this.toggleLowerNavbar(); - $(window).on('resize', this.toggleLowerNavbar); - - // scroll default navbar horizontally when mousewheel is scrolled - $(this.$refs.navbarDefault).on('wheel', (e) => { - const isDropdown = (nodes) => { - for (let i = 0; i < nodes.length; i += 1) { - if (nodes[i].classList && nodes[i].classList.contains('dropdown-menu')) { - return true; - } - } - return false; - }; - - // prevent horizontal scrolling if the scroll is on dropdown menu - if (window.innerWidth < 768 && !isDropdown(e.path)) { - e.preventDefault(); - this.$refs.navbarDefault.scrollLeft += e.deltaY; - } - }); + this.bindMobileNavCloseOnNavigate(); + $(window).on('resize', this.handleWindowResize); }, beforeUnmount() { $('.dropdown', this.$el).off('click').offBlur(); - $(window).off('resize', this.toggleLowerNavbar); - $(this.$refs.navbarDefault).off('wheel'); + $(window).off('resize', this.handleWindowResize); + if (this.$refs.navbarDefault) { + this.$refs.navbarDefault.querySelectorAll('a.nav-link:not(.dropdown-toggle), a.dropdown-item') + .forEach((link) => { + link.removeEventListener('click', this.closeMobileNav); + }); + } }, };