import { useRouter } from 'next/compat/router'; import React, { FC, useMemo } from 'react'; import { ApiVersionSelect } from './ApiVersionSelect'; import { GroupList } from './GroupList'; import { PageLink } from './PageLink'; import { SectionList } from './SectionList'; import { NavigationNode, NavigationRenderProps, NavigationType } from './types'; import { LayoutScroll, usePersistScroll } from '~/ui/components/Layout'; export type NavigationProps = { /** The tree of navigation nodes to render in the sidebar */ routes: NavigationNode[]; }; export function Navigation({ routes }: NavigationProps) { const router = useRouter(); const activeRoutes = useMemo(() => findActiveRoute(routes, router?.pathname), [router?.pathname]); const persistScroll = usePersistScroll('navigation'); return ( ); } const renderers: Record>> = { section: SectionList, group: GroupList, page: PageLink, }; function navigationRenderer( route: NavigationNode, activeRoutes: Record ) { if (route.hidden) return null; const Component = renderers[route.type]; const routeKey = `${route.type}-${route.name}`; const isActive = activeRoutes[route.type] === route; const hasChildren = route.type !== 'page' && route.children.length; return ( {hasChildren && route.children.map(nested => navigationRenderer(nested, activeRoutes))} ); } /** * Find the active routes by pathname, and do it once. * This will iterate the tree and find the nodes which are "active". * - Page -> if the pathname matches the page's href property * - Group -> if the group contains an active page * - Section -> if the section contains an active group or page */ export function findActiveRoute(routes: NavigationNode[], pathname?: string) { const activeRoutes: Record = { page: null, group: null, section: null, }; if (!pathname) { return activeRoutes; } for (const route of routes) { // Try to exit early on hidden routes if (route.hidden) continue; switch (route.type) { case 'page': if (route.href === pathname) { activeRoutes.page = route; break; } break; case 'group': { const nestedActiveRoutes = findActiveRoute(route.children, pathname); if (nestedActiveRoutes?.page) { activeRoutes.page = nestedActiveRoutes.page; activeRoutes.group = route; break; } } break; case 'section': { const nestedActiveRoutes = findActiveRoute(route.children, pathname); if (nestedActiveRoutes?.group || nestedActiveRoutes?.page) { activeRoutes.page = nestedActiveRoutes.page; activeRoutes.group = nestedActiveRoutes.group; activeRoutes.section = route; break; } } break; } } return activeRoutes; }