diff --git a/packages/features/shell/navigation/NavigationItem.tsx b/packages/features/shell/navigation/NavigationItem.tsx index 535a0f060f..a66e1e790a 100644 --- a/packages/features/shell/navigation/NavigationItem.tsx +++ b/packages/features/shell/navigation/NavigationItem.tsx @@ -1,16 +1,38 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; -import React, { Fragment } from "react"; +import React, { Fragment, useState, useEffect } from "react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { SkeletonText } from "@calcom/ui/components/skeleton"; import classNames from "@calcom/ui/classNames"; import { Icon } from "@calcom/ui/components/icon"; import type { IconName } from "@calcom/ui/components/icon"; +import { SkeletonText } from "@calcom/ui/components/skeleton"; import { Tooltip } from "@calcom/ui/components/tooltip"; import { useShouldDisplayNavigationItem } from "./useShouldDisplayNavigationItem"; +const usePersistedExpansionState = (itemName: string) => { + const [isExpanded, setIsExpanded] = useState(false); + + useEffect(() => { + try { + const stored = sessionStorage.getItem(`nav-expansion-${itemName}`); + if (stored !== null) { + setIsExpanded(JSON.parse(stored)); + } + } catch (_error) {} + }, [itemName]); + + const setPersistedExpansion = (expanded: boolean) => { + setIsExpanded(expanded); + try { + sessionStorage.setItem(`nav-expansion-${itemName}`, JSON.stringify(expanded)); + } catch (_error) {} + }; + + return [isExpanded, setPersistedExpansion] as const; +}; + export type NavigationItemType = { name: string; href: string; @@ -50,55 +72,106 @@ export const NavigationItem: React.FC<{ const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; const current = isCurrent({ isChild: !!isChild, item, pathname }); const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); + const [isExpanded, setIsExpanded] = usePersistedExpansionState(item.name); if (!shouldDisplayNavigationItem) return null; + const hasChildren = item.child && item.child.length > 0; + const hasActiveChild = + hasChildren && item.child?.some((child) => isCurrent({ isChild: true, item: child, pathname })); + const shouldShowChildren = isExpanded || hasActiveChild || isCurrent({ pathname, isChild, item }); + const shouldShowChevron = hasChildren && !hasActiveChild; + const isParentNavigationItem = hasChildren && !isChild; + return ( - - - {item.icon && ( - + {isParentNavigationItem ? ( + + + + ) : ( + + + {item.icon && ( + + )} {item.child && - isCurrent({ pathname, isChild, item }) && + shouldShowChildren && item.child.map((item, index) => )} ); @@ -144,20 +217,58 @@ export const MobileNavigationMoreItem: React.FC<{ const { item } = props; const { t, isLocaleReady } = useLocale(); const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); + const [isExpanded, setIsExpanded] = usePersistedExpansionState(item.name); if (!shouldDisplayNavigationItem) return null; + const hasChildren = item.child && item.child.length > 0; + return (
  • - - - {item.icon && ( - - - + + ) : ( + + + {item.icon && ( + + + + )}
  • ); };