diff --git a/apps/web/core/components/navigation/tab-navigation-root.tsx b/apps/web/core/components/navigation/tab-navigation-root.tsx index 103c4dba8b8..00229be7634 100644 --- a/apps/web/core/components/navigation/tab-navigation-root.tsx +++ b/apps/web/core/components/navigation/tab-navigation-root.tsx @@ -115,7 +115,7 @@ export const TabNavigationRoot: FC = observer((props) = const hiddenNavigationItems = allNavigationItems.filter((item) => tabPreferences.hiddenTabs.includes(item.key)); // Responsive tab layout hook - const { visibleItems, overflowItems, hasOverflow, containerRef, itemRefs } = useResponsiveTabLayout({ + const { visibleItems, overflowItems, hasOverflow, itemRefs, containerRef } = useResponsiveTabLayout({ visibleNavigationItems, hiddenNavigationItems, isActive, diff --git a/apps/web/core/components/navigation/use-responsive-tab-layout.ts b/apps/web/core/components/navigation/use-responsive-tab-layout.ts index 4e91fea85d8..c76724a067e 100644 --- a/apps/web/core/components/navigation/use-responsive-tab-layout.ts +++ b/apps/web/core/components/navigation/use-responsive-tab-layout.ts @@ -1,12 +1,12 @@ -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { TNavigationItem } from "./tab-navigation-root"; export type TResponsiveTabLayout = { visibleItems: TNavigationItem[]; overflowItems: TNavigationItem[]; hasOverflow: boolean; - containerRef: React.RefObject; itemRefs: React.MutableRefObject<(HTMLDivElement | null)[]>; + containerRef: (node: HTMLDivElement | null) => void; }; type UseResponsiveTabLayoutProps = { @@ -30,9 +30,9 @@ export const useResponsiveTabLayout = ({ hiddenNavigationItems, isActive, }: UseResponsiveTabLayoutProps): TResponsiveTabLayout => { - // Refs for measuring space and items - const containerRef = useRef(null); + // Refs for measuring items const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + const resizeObserverRef = useRef(null); // State for responsive behavior const [containerWidth, setContainerWidth] = useState(0); @@ -42,24 +42,44 @@ export const useResponsiveTabLayout = ({ const gap = 4; // gap-1 = 4px const overflowButtonWidth = 40; - const container = containerRef?.current; + // Callback ref that sets up ResizeObserver when element is attached + const containerRef = useCallback((node: HTMLDivElement | null) => { + // Clean up previous observer if it exists + if (resizeObserverRef.current) { + resizeObserverRef.current.disconnect(); + resizeObserverRef.current = null; + } - // ResizeObserver to measure container width - useEffect(() => { - if (!container) return; + // If node is null (unmounting), just clean up + if (!node) { + setContainerWidth(0); + return; + } + // Set initial width immediately + setContainerWidth(node.offsetWidth); + + // Create and set up new ResizeObserver const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { setContainerWidth(entry.contentRect.width); } }); - resizeObserver.observe(container); + resizeObserverRef.current = resizeObserver; + resizeObserver.observe(node); + }, []); // Empty deps - callback function remains stable - return () => { - resizeObserver.disconnect(); - }; - }, [container]); + // Cleanup effect to disconnect observer on component unmount + useEffect( + () => () => { + if (resizeObserverRef.current) { + resizeObserverRef.current.disconnect(); + resizeObserverRef.current = null; + } + }, + [] + ); // Calculate how many items can fit useEffect(() => { @@ -137,7 +157,7 @@ export const useResponsiveTabLayout = ({ visibleItems, overflowItems, hasOverflow, - containerRef, itemRefs, + containerRef, }; }; diff --git a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx index bdf8eb6492e..38e1122cf4b 100644 --- a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx +++ b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx @@ -69,8 +69,8 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem const { isMobile } = usePlatformOS(); const { allowPermissions } = useUserPermissions(); const { getIsProjectListOpen, toggleProjectListOpen } = useCommandPalette(); - const { toggleAnySidebarDropdown } = useAppTheme(); const { preferences: projectPreferences } = useProjectNavigationPreferences(); + const { isExtendedProjectSidebarOpened, toggleExtendedProjectSidebar, toggleAnySidebarDropdown } = useAppTheme(); // states const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false); @@ -259,6 +259,10 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem } else { router.push(defaultTabUrl); } + // close the extended sidebar if it is open + if (isExtendedProjectSidebarOpened) { + toggleExtendedProjectSidebar(false); + } }; const isAccordionMode = projectPreferences.navigationMode === "accordion";