@@ -24,6 +24,10 @@ import {
2424 ChevronDown ,
2525} from 'lucide-react' ;
2626
27+ const NAV_REVEAL_TOP_OFFSET = 24 ;
28+ const NAV_HIDE_SCROLL_DISTANCE = 72 ;
29+ const NAV_SHOW_SCROLL_DISTANCE = 36 ;
30+
2731const NavDropdown = memo ( ( { menuItem, activeDropdown, handleMouseEnter } ) => {
2832 const isActive = activeDropdown === menuItem . key ;
2933 const hasMega = Boolean ( menuItem . mega ) ;
@@ -148,11 +152,15 @@ const Nav = () => {
148152 const [ isUserMenuOpen , setUserMenuOpen ] = useState ( false ) ;
149153 const [ activeDropdown , setActiveDropdown ] = useState ( null ) ;
150154 const [ openMobileSections , setOpenMobileSections ] = useState ( ( ) => new Set ( ) ) ;
155+ const scrollRafRef = useRef ( null ) ;
151156 const lastScrollYRef = useRef ( 0 ) ;
157+ const lastToggleYRef = useRef ( 0 ) ;
158+ const desktopSubNavVisibleRef = useRef ( true ) ;
159+ const scrollUiStateRef = useRef ( { scrolled : false } ) ;
152160 const dispatch = useDispatch ( ) ;
153161 const menuItems = useMemo (
154162 ( ) => NAV_ORDER . map ( ( key ) => ( { key, ...NAVIGATION [ key ] } ) ) ,
155- [ NAV_ORDER , NAVIGATION ]
163+ [ ]
156164 ) ;
157165 const activeMenu = useMemo (
158166 ( ) => ( activeDropdown ? NAVIGATION [ activeDropdown ] : null ) ,
@@ -161,7 +169,7 @@ const Nav = () => {
161169 const activeDropdownData = activeMenu ?. mega || null ;
162170 const searchSuggestions = useMemo (
163171 ( ) => SEARCH_SUGGESTIONS ,
164- [ SEARCH_SUGGESTIONS ]
172+ [ ]
165173 ) ;
166174 const {
167175 query : searchQuery ,
@@ -177,27 +185,84 @@ const Nav = () => {
177185 } ) ;
178186
179187 useEffect ( ( ) => {
180- const handleScroll = ( ) => {
188+ const updateFromScroll = ( ) => {
181189 const currentScrollY = window . scrollY ;
190+ const nextScrolled = currentScrollY > 10 ;
191+
192+ if ( scrollUiStateRef . current . scrolled !== nextScrolled ) {
193+ scrollUiStateRef . current . scrolled = nextScrolled ;
194+ setScrolled ( nextScrolled ) ;
195+ }
196+
197+ const isDesktop = window . innerWidth >= 768 ;
198+ if ( ! isDesktop ) {
199+ lastScrollYRef . current = currentScrollY ;
200+ if ( ! desktopSubNavVisibleRef . current ) {
201+ desktopSubNavVisibleRef . current = true ;
202+ setShowDesktopSubNav ( true ) ;
203+ }
204+ return ;
205+ }
206+
182207 const delta = currentScrollY - lastScrollYRef . current ;
208+ lastScrollYRef . current = currentScrollY ;
209+ if ( Math . abs ( delta ) < 2 ) return ;
183210
184- setScrolled ( currentScrollY > 10 ) ;
211+ if ( currentScrollY <= NAV_REVEAL_TOP_OFFSET ) {
212+ lastToggleYRef . current = currentScrollY ;
213+ if ( ! desktopSubNavVisibleRef . current ) {
214+ desktopSubNavVisibleRef . current = true ;
215+ setShowDesktopSubNav ( true ) ;
216+ }
217+ return ;
218+ }
185219
186- if ( currentScrollY < 24 ) {
187- setShowDesktopSubNav ( true ) ;
188- } else if ( delta > 6 ) {
220+ if (
221+ delta > 0 &&
222+ desktopSubNavVisibleRef . current &&
223+ currentScrollY - lastToggleYRef . current >= NAV_HIDE_SCROLL_DISTANCE
224+ ) {
225+ desktopSubNavVisibleRef . current = false ;
226+ lastToggleYRef . current = currentScrollY ;
189227 setShowDesktopSubNav ( false ) ;
190228 setActiveDropdown ( null ) ;
191- } else if ( delta < - 6 ) {
229+ return ;
230+ }
231+
232+ if (
233+ delta < 0 &&
234+ ! desktopSubNavVisibleRef . current &&
235+ lastToggleYRef . current - currentScrollY >= NAV_SHOW_SCROLL_DISTANCE
236+ ) {
237+ desktopSubNavVisibleRef . current = true ;
238+ lastToggleYRef . current = currentScrollY ;
192239 setShowDesktopSubNav ( true ) ;
193240 }
241+ } ;
194242
195- lastScrollYRef . current = currentScrollY ;
243+ const handleScroll = ( ) => {
244+ if ( scrollRafRef . current !== null ) return ;
245+ scrollRafRef . current = window . requestAnimationFrame ( ( ) => {
246+ scrollRafRef . current = null ;
247+ updateFromScroll ( ) ;
248+ } ) ;
196249 } ;
197250
198- lastScrollYRef . current = window . scrollY ;
251+ const initialScrollY = window . scrollY ;
252+ lastScrollYRef . current = initialScrollY ;
253+ lastToggleYRef . current = initialScrollY ;
254+ scrollUiStateRef . current . scrolled = initialScrollY > 10 ;
255+ desktopSubNavVisibleRef . current = true ;
256+ setScrolled ( scrollUiStateRef . current . scrolled ) ;
257+ setShowDesktopSubNav ( true ) ;
258+
199259 window . addEventListener ( 'scroll' , handleScroll , { passive : true } ) ;
200- return ( ) => window . removeEventListener ( 'scroll' , handleScroll ) ;
260+ return ( ) => {
261+ window . removeEventListener ( 'scroll' , handleScroll ) ;
262+ if ( scrollRafRef . current !== null ) {
263+ window . cancelAnimationFrame ( scrollRafRef . current ) ;
264+ }
265+ } ;
201266 } , [ ] ) ;
202267
203268 useEffect ( ( ) => {
@@ -236,7 +301,7 @@ const Nav = () => {
236301 setActiveDropdown ( null ) ;
237302 }
238303 } ,
239- [ activeDropdown , NAVIGATION ]
304+ [ activeDropdown ]
240305 ) ;
241306
242307 const handleMouseLeave = useCallback ( ( ) => {
@@ -278,8 +343,9 @@ const Nav = () => {
278343 < >
279344 { /* Main Navigation */ }
280345 < nav
281- className = { `sticky top-0 z-50 transition-all duration-300 ease-out relative
282- ${ scrolled ? 'bg-stone-950/95 backdrop-blur-xl shadow-md shadow-black/20 border-b border-white/10' : 'bg-stone-950' } ` }
346+ style = { { position : 'sticky' } }
347+ className = { `relative top-0 z-[200] nav-shell backdrop-blur-xl transition-[background-color,border-color,box-shadow] duration-300 ease-out
348+ ${ scrolled ? 'bg-stone-950/95 shadow-md shadow-black/20 border-b border-white/10' : 'bg-stone-950/90 border-b border-transparent' } ` }
283349 >
284350 < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
285351 { /* Mobile Header */ }
@@ -319,7 +385,7 @@ const Nav = () => {
319385 < User className = "w-5 h-5" strokeWidth = { 1.5 } />
320386 </ button >
321387 { isUserMenuOpen && (
322- < div className = "absolute top-full right-0 mt-2 w-52 bg-stone-950 rounded-lg shadow-xl border border-gray-800 p-2 animate-in slide-in-from-top-2 z-2 " >
388+ < div className = "absolute top-full right-0 mt-2 w-52 bg-stone-950 rounded-lg shadow-xl border border-gray-800 p-2 animate-in slide-in-from-top-2 z-[220] nav-transition-layer " >
323389 < div className = "px-3 py-2 border-b border-gray-700" >
324390 < div className = "text-sm font-medium text-white" > { user . name || 'User' } </ div >
325391 < div className = "text-xs text-gray-400" >
@@ -467,7 +533,7 @@ const Nav = () => {
467533 < User className = "w-5 h-5" strokeWidth = { 1.5 } />
468534 </ button >
469535 { isUserMenuOpen && (
470- < div className = "absolute top-full right-0 mt-2 w-52 bg-stone-950 rounded-lg shadow-xl border border-gray-800 p-2 animate-in slide-in-from-top-2 z-2 " >
536+ < div className = "absolute top-full right-0 mt-2 w-52 bg-stone-950 rounded-lg shadow-xl border border-gray-800 p-2 animate-in slide-in-from-top-2 z-[220] nav-transition-layer " >
471537 < div className = "px-3 py-2 border-b border-gray-700" >
472538 < div className = "text-sm font-medium text-white" > { user . name || 'User' } </ div >
473539 < div className = "text-xs text-gray-400" >
@@ -565,32 +631,34 @@ const Nav = () => {
565631
566632 { /* Desktop Navigation Menu */ }
567633 < div
568- className = { `hidden md:flex items-center justify-center overflow-hidden transition-all duration-300 ease-out ${
634+ className = { `hidden md:flex items-center justify-center overflow-hidden transform-gpu nav- transition-layer transition-[height,opacity,transform,border-color] duration-300 ease-out ${
569635 showDesktopSubNav
570- ? 'max-h-20 py-3 opacity-100 border-t border-white/10'
571- : 'max- h-0 py -0 opacity-0 border-t border-transparent pointer-events-none'
636+ ? 'h-[58px] opacity-100 translate-y-0 border-t border-white/10'
637+ : 'h-0 opacity -0 -translate-y-2 border-t border-transparent pointer-events-none'
572638 } `}
573639 >
574- < div
575- className = "flex items-center rounded-full border border-white/10 bg-white/[0.03] px-2 divide-x divide-white/15 dropdown-container"
576- onMouseLeave = { handleMouseLeave }
577- >
578- { menuItems . map ( ( menuItem ) => (
579- < div key = { menuItem . key } className = "px-3.5" >
580- < NavDropdown
581- menuItem = { menuItem }
582- activeDropdown = { activeDropdown }
583- handleMouseEnter = { handleMouseEnter }
584- />
585- </ div >
586- ) ) }
640+ < div className = "h-[58px] flex items-center justify-center" >
641+ < div
642+ className = "flex items-center rounded-full border border-white/10 bg-white/[0.03] px-2 divide-x divide-white/15 dropdown-container"
643+ onMouseLeave = { handleMouseLeave }
644+ >
645+ { menuItems . map ( ( menuItem ) => (
646+ < div key = { menuItem . key } className = "px-3.5" >
647+ < NavDropdown
648+ menuItem = { menuItem }
649+ activeDropdown = { activeDropdown }
650+ handleMouseEnter = { handleMouseEnter }
651+ />
652+ </ div >
653+ ) ) }
654+ </ div >
587655 </ div >
588656 </ div >
589657
590658 { /* Desktop Mega Menu */ }
591659 { showDesktopSubNav && activeDropdownData && (
592660 < div
593- className = "absolute left-0 right-0 top-full mt-2 bg-stone-950/95 backdrop-blur-xl shadow-2xl animate-in slide-in-from-top-4 duration-300 z-50 dropdown-container"
661+ className = "absolute left-0 right-0 top-full mt-2 bg-stone-950/95 backdrop-blur-xl shadow-2xl animate-in slide-in-from-top-4 duration-300 z-[210] dropdown-container transform-gpu nav-transition-layer "
594662 onMouseEnter = { ( ) => clearTimeout ( dropdownTimeoutRef . current ) }
595663 onMouseLeave = { handleMouseLeave }
596664 >
0 commit comments