import Link from "next/link"; import { usePathname } from "next/navigation"; import posthog from "posthog-js"; import React, { Fragment, useState, useEffect } from "react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { sessionStorage } from "@calcom/lib/webstorage"; import classNames from "@calcom/ui/classNames"; import { Badge } from "@calcom/ui/components/badge"; 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(() => { const stored = sessionStorage.getItem(`nav-expansion-${itemName}`); if (stored !== null) { setIsExpanded(JSON.parse(stored)); } }, [itemName]); const setPersistedExpansion = (expanded: boolean) => { setIsExpanded(expanded); sessionStorage.setItem(`nav-expansion-${itemName}`, JSON.stringify(expanded)); }; return [isExpanded, setPersistedExpansion] as const; }; const trackNavigationClick = (itemName: string, parentItemName?: string) => { posthog.capture("navigation_item_clicked", { item_name: itemName, parent_name: parentItemName, }); }; export type NavigationItemType = { name: string; href: string; isLoading?: boolean; onClick?: React.MouseEventHandler; target?: HTMLAnchorElement["target"]; badge?: React.ReactNode; icon?: IconName; child?: NavigationItemType[]; pro?: true; onlyMobile?: boolean; onlyDesktop?: boolean; moreOnMobile?: boolean; isCurrent?: ({ item, isChild, pathname, }: { item: Pick; isChild?: boolean; pathname: string | null; }) => boolean; }; const defaultIsCurrent: NavigationItemType["isCurrent"] = ({ isChild, item, pathname }) => { return isChild ? item.href === pathname : item.href ? (pathname?.startsWith(item.href) ?? false) : false; }; export const NavigationItem: React.FC<{ index?: number; item: NavigationItemType; isChild?: boolean; }> = (props) => { const { item, isChild } = props; const { t, isLocaleReady } = useLocale(); const pathname = usePathname(); 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); const isTablet = useMediaQuery("(max-width: 1024px)"); const [isTooltipOpen, setIsTooltipOpen] = useState(false); 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 ( {isParentNavigationItem ? ( {t(item.name)}
{item.child?.map((childItem) => { const childIsCurrent = typeof childItem.isCurrent === "function" ? childItem.isCurrent({ isChild: true, item: childItem, pathname, }) : defaultIsCurrent({ isChild: true, item: childItem, pathname, }); return ( { setIsTooltipOpen(false); trackNavigationClick(childItem.name, item.name); }} className={classNames( "group relative block rounded-md px-3 py-1 text-sm font-medium", childIsCurrent ? "bg-emphasis text-white" : "hover:bg-emphasis text-mute hover:text-emphasis" )}> {t(childItem.name)} ); })}
) : ( t(item.name) ) } className="lg:hidden">
) : ( trackNavigationClick(item.name)} href={item.href} aria-label={t(item.name)} target={item.target} className={classNames( "todesktop:py-[7px] text-default group flex items-center rounded-md px-2 py-1.5 text-sm font-medium transition", item.child ? `aria-[aria-current='page']:bg-transparent!` : `[&[aria-current='page']]:bg-emphasis`, isChild ? `[&[aria-current='page']]:text-emphasis [&[aria-current='page']]:bg-emphasis hidden h-8 pl-16 lg:flex lg:pl-11 ${ props.index === 0 ? "mt-0" : "mt-1 hover:mt-1 [&[aria-current='page']]:mt-1" }` : "[&[aria-current='page']]:text-emphasis mt-0.5 text-sm md:justify-center lg:justify-start", isLocaleReady ? "hover:bg-subtle todesktop:[&[aria-current='page']]:bg-emphasis todesktop:hover:bg-transparent hover:text-emphasis" : "" )} aria-current={current ? "page" : undefined}> {item.icon && ( )} {hasChildren && (
{item.child?.map((item, index) => ( ))}
)}
); }; export const MobileNavigationItem: React.FC<{ item: NavigationItemType; isChild?: boolean; }> = (props) => { const { item, isChild } = props; const pathname = usePathname(); const { t, isLocaleReady } = useLocale(); const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; const current = isCurrent({ isChild: !!isChild, item, pathname }); const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); if (!shouldDisplayNavigationItem) return null; return ( {item.badge &&
{item.badge}
} {item.icon && (