[WEB-5556] chore: tab navigation and sidebar improvements (#8218)

This commit is contained in:
Anmol Singh Bhatia 2025-12-02 18:05:01 +05:30 committed by GitHub
parent ec478a8979
commit e650b19933
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 40 additions and 16 deletions

View file

@ -115,7 +115,7 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = 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,

View file

@ -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<HTMLDivElement>;
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<HTMLDivElement>(null);
// Refs for measuring items
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
const resizeObserverRef = useRef<ResizeObserver | null>(null);
// State for responsive behavior
const [containerWidth, setContainerWidth] = useState<number>(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,
};
};

View file

@ -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";