diff --git a/src/components/navigation/BottomNavBar.tsx b/src/components/navigation/BottomNavBar.tsx index 6a00a9a..180c01a 100644 --- a/src/components/navigation/BottomNavBar.tsx +++ b/src/components/navigation/BottomNavBar.tsx @@ -11,7 +11,6 @@ import { toastLoginRequired } from "../Toast"; type BottomNavBarProps = { onPagerClick?: () => void; - compact?: boolean; }; type NavItemProps = { @@ -42,7 +41,7 @@ function NavItem({ href, isActive, onClick, onButtonClick, Icon }: NavItemProps) ); } -function BottomNavBar({ onPagerClick, compact = false }: BottomNavBarProps) { +function BottomNavBar({ onPagerClick }: BottomNavBarProps) { const params = useParams(); const name = params.name as string; const pathname = usePathname(); @@ -75,13 +74,7 @@ function BottomNavBar({ onPagerClick, compact = false }: BottomNavBarProps) { }; return ( -
+
{onPagerClick ? : } diff --git a/src/components/wrapper/ClientMenuRouteWrapper.tsx b/src/components/wrapper/ClientMenuRouteWrapper.tsx index f2232de..f219a98 100644 --- a/src/components/wrapper/ClientMenuRouteWrapper.tsx +++ b/src/components/wrapper/ClientMenuRouteWrapper.tsx @@ -1,80 +1,112 @@ -'use client'; +"use client"; -import React, { useEffect, useRef, useState } from 'react' -import TopBar from '../topbar/TopBar' -import SideMenu from '../menu/SideMenu' -import BottomNavBar from '../navigation/BottomNavBar' -import useToggle from '@/hooks/helpers/useToggle'; -import { usePathname, useRouter } from 'next/navigation'; -import usePreference from '@/hooks/helpers/usePreference'; -import PagerModal from '../pager/PagerModal'; -import clsx from 'clsx'; -import { glassSurface } from '@/lib/styles/glassSurface'; +import usePreference from "@/hooks/helpers/usePreference"; +import useToggle from "@/hooks/helpers/useToggle"; +import { glassSurface } from "@/lib/styles/glassSurface"; +import clsx from "clsx"; +import { usePathname, useRouter } from "next/navigation"; +import React, { useEffect, useRef } from "react"; +import SideMenu from "../menu/SideMenu"; +import BottomNavBar from "../navigation/BottomNavBar"; +import PagerModal from "../pager/PagerModal"; +import TopBar from "../topbar/TopBar"; -type Props = {} & React.AllHTMLAttributes +type Props = {} & React.AllHTMLAttributes; function ClientMenuRouteWrapper({ children }: Props) { const { state: profileDrop, toggle: _toggleProfileDrop } = useToggle(false); const { state: menuState, toggle: _toggleMenuState, set: setMenuState } = useToggle(false); const { state: searchModal, toggle: _toggleSearchModal } = useToggle(false); - const { state: nightMode, toggle: _toggleNightMode } = usePreference('night-mode', false); + const { state: nightMode, toggle: _toggleNightMode } = usePreference("night-mode", false); const { state: pagerOpen, toggle: togglePager, set: setPagerOpen } = useToggle(false); const router = useRouter(); const pathname = usePathname(); const toggleProfileDrop = () => { _toggleProfileDrop(); - } + }; const toggleNightMode = () => { _toggleNightMode(); - } + }; const toggleMenuState = () => { _toggleMenuState(); - } + }; const toggleSearchModal = () => { _toggleSearchModal(); - } + }; const handleClosePager = () => { setPagerOpen(false); }; const location = usePathname(); - const hideBottomNav = pathname.endsWith('/cart'); + const hideBottomNav = pathname.endsWith("/cart"); const mainRef = useRef(null); - const [navCompact, setNavCompact] = useState(false); + + // Refs for the scroll-driven animation — kept outside React state so scroll + // events never trigger re-renders. The RAF+lerp loop writes directly to the DOM. + const navContainerRef = useRef(null); + const targetProgressRef = useRef(0); + const currentProgressRef = useRef(0); + const rafIdRef = useRef(0); useEffect(() => { if (menuState) { - setMenuState(false) + setMenuState(false); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]) + }, [location]); useEffect(() => { - const scrollContainer = mainRef.current; - if (!scrollContainer || hideBottomNav) { - setNavCompact(false); + const scrollEl = mainRef.current; + const navEl = navContainerRef.current; + + if (!navEl) return; + + if (!scrollEl || hideBottomNav) { + cancelAnimationFrame(rafIdRef.current); + navEl.style.transform = ""; + targetProgressRef.current = 0; + currentProgressRef.current = 0; return; } - const handleScroll = () => { - setNavCompact(scrollContainer.scrollTop > 24); + const SCROLL_RANGE = 150; // px of scroll to reach full compact state + const LERP_FACTOR = 0.1; // per-frame smoothing (lower = smoother, higher = snappier) + const MIN_SCALE = 0.82; // scale floor (18% smaller at full scroll) + + const tick = () => { + const diff = targetProgressRef.current - currentProgressRef.current; + if (Math.abs(diff) < 0.0005) { + currentProgressRef.current = targetProgressRef.current; + } else { + currentProgressRef.current += diff * LERP_FACTOR; + rafIdRef.current = requestAnimationFrame(tick); + } + const scale = 1 - (1 - MIN_SCALE) * currentProgressRef.current; + navEl.style.transform = `scale(${scale.toFixed(4)})`; }; - scrollContainer.addEventListener('scroll', handleScroll, { passive: true }); - handleScroll(); + const onScroll = () => { + targetProgressRef.current = Math.min(1, Math.max(0, scrollEl.scrollTop / SCROLL_RANGE)); + cancelAnimationFrame(rafIdRef.current); + rafIdRef.current = requestAnimationFrame(tick); + }; - return () => scrollContainer.removeEventListener('scroll', handleScroll); + scrollEl.addEventListener("scroll", onScroll, { passive: true }); + + return () => { + scrollEl.removeEventListener("scroll", onScroll); + cancelAnimationFrame(rafIdRef.current); + }; }, [hideBottomNav, location]); return (
- -
+
- {!hideBottomNav && ( -
- +
+
)} + - - - -
+
{children}
- - ) + ); } -export default ClientMenuRouteWrapper \ No newline at end of file +export default ClientMenuRouteWrapper;