From 62382d9828a9c159b7a23d1469b7bee062d67bc5 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Fri, 17 Jul 2026 17:32:42 +0800 Subject: [PATCH] fix: tighten composable sidebar primitives --- frontend/src/components/ui/sidebar.tsx | 24 +++++++++++------ frontend/src/hooks/use-sidebar-viewport.ts | 3 ++- frontend/tests/sidebar-contract.test.ts | 31 ++++++++++++++++++++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/ui/sidebar.tsx b/frontend/src/components/ui/sidebar.tsx index 0e0aa519..b276f8ec 100644 --- a/frontend/src/components/ui/sidebar.tsx +++ b/frontend/src/components/ui/sidebar.tsx @@ -81,6 +81,7 @@ export function SidebarProvider({ const { viewport, ready } = useSidebarViewport(); const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen); const [openMobile, setOpenMobileState] = useState(false); + const openMobileRef = useRef(false); const userChangedDesktopState = useRef(false); const insetTriggerRef = useRef(null); const sidebarTriggerRef = useRef(null); @@ -96,6 +97,8 @@ export function SidebarProvider({ }, [isControlled, onOpenChange]); const setOpenMobile = useCallback((nextOpen: boolean) => { + if (openMobileRef.current === nextOpen) return; + openMobileRef.current = nextOpen; setOpenMobileState(nextOpen); onMobileOpenChange?.(nextOpen); }, [onMobileOpenChange]); @@ -107,10 +110,10 @@ export function SidebarProvider({ }, [isControlled, ready, viewport]); useEffect(() => { - if (viewport === "mobile") return; + if (viewport === "mobile" || !openMobile) return; const closeMobileDrawer = window.requestAnimationFrame(() => setOpenMobile(false)); return () => window.cancelAnimationFrame(closeMobileDrawer); - }, [setOpenMobile, viewport]); + }, [openMobile, setOpenMobile, viewport]); useEffect(() => { const handleKeydown = (event: KeyboardEvent) => { @@ -168,9 +171,12 @@ export function SidebarProvider({ ); } -export function Sidebar({ className, ...props }: ComponentProps<"aside">) { - const { open, openMobile } = useSidebar(); - return