From 3467184b8f0843fc4b5d28260276929a507303e8 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 6 Feb 2026 04:27:37 -0800 Subject: [PATCH] Consolidate WebsiteNavSection into WebsiteNav and fix scrollbar flash. Move sidebar rendering logic into WebsiteNav with isCollapsed prop so SideNav uses WebsiteNav directly instead of duplicating code. Add overflow hidden to prevent horizontal scrollbar during expand transition. Co-Authored-By: Claude Opus 4.6 --- src/app/(main)/SideNav.tsx | 119 ++---------------- .../websites/[websiteId]/WebsiteNav.tsx | 86 ++++++++++++- 2 files changed, 91 insertions(+), 114 deletions(-) diff --git a/src/app/(main)/SideNav.tsx b/src/app/(main)/SideNav.tsx index 2a00ca424..852c9c88e 100644 --- a/src/app/(main)/SideNav.tsx +++ b/src/app/(main)/SideNav.tsx @@ -1,5 +1,4 @@ import { - Box, Button, type ButtonProps, Column, @@ -13,24 +12,17 @@ import { } from '@umami/react-zen'; import Link from 'next/link'; import type { Key } from 'react'; +import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav'; import { IconLabel } from '@/components/common/IconLabel'; -import { useGlobalState, useMessages, useNavigation, useWebsiteNavItems } from '@/components/hooks'; -import { - ArrowLeft, - Globe, - Grid2x2, - LayoutDashboard, - LinkIcon, - PanelLeft, -} from '@/components/icons'; +import { useGlobalState, useMessages, useNavigation } from '@/components/hooks'; +import { Globe, Grid2x2, LayoutDashboard, LinkIcon, PanelLeft } from '@/components/icons'; import { LanguageButton } from '@/components/input/LanguageButton'; import { NavButton } from '@/components/input/NavButton'; -import { WebsiteSelect } from '@/components/input/WebsiteSelect'; import { Logo } from '@/components/svg'; export function SideNav(props: any) { const { formatMessage, labels } = useMessages(); - const { pathname, renderUrl, websiteId, teamId, router } = useNavigation(); + const { pathname, renderUrl, websiteId, router } = useNavigation(); const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed'); const hasNav = !!(websiteId || pathname.startsWith('/admin') || pathname.includes('/settings')); @@ -66,10 +58,6 @@ export function SideNav(props: any) { router.push(id === 'user' ? '/websites' : `/teams/${id}/websites`); }; - const handleWebsiteChange = (value: string) => { - router.push(renderUrl(`/websites/${value}`)); - }; - return ( - + {websiteId ? ( - + ) : ( {links.map(({ id, path, label, icon }) => { @@ -143,95 +127,6 @@ export function SideNav(props: any) { ); } -function WebsiteNavSection({ - websiteId, - teamId, - isCollapsed, - onWebsiteChange, -}: { - websiteId: string; - teamId: string; - isCollapsed: boolean; - onWebsiteChange: (value: string) => void; -}) { - const { formatMessage, labels } = useMessages(); - const { renderUrl } = useNavigation(); - const { items, selectedKey } = useWebsiteNavItems(websiteId); - - const renderValue = (value: any) => { - return ( - - {value?.selectedItem?.name} - - ); - }; - - return ( - - - - - - } - label={isCollapsed ? '' : formatMessage(labels.back)} - padding - /> - - - {formatMessage(labels.back)} - - - {!isCollapsed && ( - - - - )} - {items.map(({ label: sectionLabel, items: sectionItems }, index) => ( - - {!isCollapsed && ( - - {sectionLabel} - - )} - {sectionItems.map(({ id, path, label, icon }) => { - const isSelected = selectedKey === id; - return ( - - - - - - - - {label} - - - ); - })} - - ))} - - ); -} - const PanelButton = (props: ButtonProps) => { const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed'); return ( diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx index a301e015c..d43a1c141 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx @@ -1,15 +1,30 @@ -import { Column, Text } from '@umami/react-zen'; +import { + Box, + Column, + Focusable, + Label, + Row, + Text, + Tooltip, + TooltipTrigger, +} from '@umami/react-zen'; +import Link from 'next/link'; +import { IconLabel } from '@/components/common/IconLabel'; import { NavMenu } from '@/components/common/NavMenu'; -import { useNavigation, useWebsiteNavItems } from '@/components/hooks'; +import { useMessages, useNavigation, useWebsiteNavItems } from '@/components/hooks'; +import { ArrowLeft } from '@/components/icons'; import { WebsiteSelect } from '@/components/input/WebsiteSelect'; export function WebsiteNav({ websiteId, + isCollapsed, onItemClick, }: { websiteId: string; + isCollapsed?: boolean; onItemClick?: () => void; }) { + const { formatMessage, labels } = useMessages(); const { teamId, router, renderUrl } = useNavigation(); const { items, selectedKey } = useWebsiteNavItems(websiteId); @@ -25,6 +40,73 @@ export function WebsiteNav({ ); }; + if (isCollapsed !== undefined) { + return ( + + + + + + } + label={isCollapsed ? '' : formatMessage(labels.back)} + padding + /> + + + {formatMessage(labels.back)} + + + {!isCollapsed && ( + + + + )} + {items.map(({ label: sectionLabel, items: sectionItems }, index) => ( + + {!isCollapsed && ( + + {sectionLabel} + + )} + {sectionItems.map(({ id, path, label, icon }) => { + const isSelected = selectedKey === id; + return ( + + + + + + + + {label} + + + ); + })} + + ))} + + ); + } + return (