diff --git a/packages/ui/app/package.json b/packages/ui/app/package.json index fddb994ed1..38041b1ea9 100644 --- a/packages/ui/app/package.json +++ b/packages/ui/app/package.json @@ -61,6 +61,7 @@ "@next/third-parties": "^14.2.4", "@radix-ui/colors": "^3.0.0", "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-select": "^2.0.0", diff --git a/packages/ui/app/src/atoms/layout.ts b/packages/ui/app/src/atoms/layout.ts index 8dbffaab42..d8aaeaa3cc 100644 --- a/packages/ui/app/src/atoms/layout.ts +++ b/packages/ui/app/src/atoms/layout.ts @@ -14,21 +14,28 @@ export const DOCS_LAYOUT_ATOM = selectAtom( ); DOCS_LAYOUT_ATOM.debugLabel = "DOCS_LAYOUT_ATOM"; +function getHeaderHeightPx(layout: DocsV1Read.DocsLayoutConfig | undefined): number { + if (layout?.headerHeight?.type === "px") { + return layout.headerHeight.value; + } else if (layout?.headerHeight?.type === "rem") { + return layout.headerHeight.value * 16; + } else { + return 64; + } +} + export const HEADER_HEIGHT_ATOM = atom((get) => { const layout = get(DOCS_LAYOUT_ATOM); const isMobileSidebarEnabled = get(MOBILE_SIDEBAR_ENABLED_ATOM); - const headerHeight = - layout?.headerHeight == null - ? 64 - : layout.headerHeight.type === "px" - ? layout.headerHeight.value - : layout.headerHeight.type === "rem" - ? layout.headerHeight.value * 16 - : 64; + const headerHeight = getHeaderHeightPx(layout); return isMobileSidebarEnabled || layout?.disableHeader !== true ? headerHeight : 0; }); HEADER_HEIGHT_ATOM.debugLabel = "HEADER_HEIGHT_ATOM"; +export const MOBILE_HEADER_HEIGHT_ATOM = atom((get) => { + return getHeaderHeightPx(get(DOCS_LAYOUT_ATOM)); +}); + const SETTABLE_HEADER_TABS_HEIGHT_ATOM = atom(44); SETTABLE_HEADER_TABS_HEIGHT_ATOM.debugLabel = "SETTABLE_HEADER_TABS_HEIGHT_ATOM"; @@ -87,6 +94,10 @@ export const HEADER_OFFSET_ATOM = atom((get) => { }); HEADER_OFFSET_ATOM.debugLabel = "HEADER_OFFSET_ATOM"; +export const MOBILE_HEADER_OFFSET_ATOM = atom((get) => { + return get(MOBILE_HEADER_HEIGHT_ATOM) + get(ANNOUNCEMENT_HEIGHT_ATOM); +}); + export const BELOW_HEADER_HEIGHT_ATOM = atom((get) => { const headerHeight = get(HEADER_HEIGHT_ATOM); const windowHeight = get(VIEWPORT_HEIGHT_ATOM); diff --git a/packages/ui/app/src/atoms/sidebar.ts b/packages/ui/app/src/atoms/sidebar.ts index 8306d81203..e101454539 100644 --- a/packages/ui/app/src/atoms/sidebar.ts +++ b/packages/ui/app/src/atoms/sidebar.ts @@ -1,11 +1,18 @@ import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; -import { SetStateAction, atom, useAtomValue, useSetAtom } from "jotai"; -import { useAtomCallback } from "jotai/utils"; +import { atom, useAtomValue, useSetAtom } from "jotai"; +import { RESET, atomWithDefault, useAtomCallback } from "jotai/utils"; import { useCallback } from "react"; import { useCallbackOne, useMemoOne } from "use-memo-one"; +import { FEATURE_FLAGS_ATOM } from "./flags"; import { useAtomEffect } from "./hooks"; import { DOCS_LAYOUT_ATOM } from "./layout"; -import { CURRENT_NODE_ATOM, CURRENT_NODE_ID_ATOM, RESOLVED_PATH_ATOM, SIDEBAR_ROOT_NODE_ATOM } from "./navigation"; +import { + CURRENT_NODE_ATOM, + CURRENT_NODE_ID_ATOM, + NAVIGATION_NODES_ATOM, + RESOLVED_PATH_ATOM, + SIDEBAR_ROOT_NODE_ATOM, +} from "./navigation"; import { THEME_ATOM } from "./theme"; import { IS_MOBILE_SCREEN_ATOM, MOBILE_SIDEBAR_ENABLED_ATOM } from "./viewport"; @@ -42,24 +49,22 @@ export const SIDEBAR_PARENT_TO_CHILDREN_MAP_ATOM = atom((get) => { }); SIDEBAR_PARENT_TO_CHILDREN_MAP_ATOM.debugLabel = "SIDEBAR_PARENT_TO_CHILDREN_MAP_ATOM"; -const INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM = atom<{ +const INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM = atomWithDefault<{ sidebarRootId: FernNavigation.NodeId; - expandedNodes: FernNavigation.NodeId[]; -}>({ - sidebarRootId: FernNavigation.NodeId(""), - expandedNodes: [], -}); - -const INITIAL_EXPANDED_SIDEBAR_NODES_ATOM = atom((get) => { + expandedNodes: Set; + implicitExpandedNodes: Set; +}>((get) => { + const sidebar = get(SIDEBAR_ROOT_NODE_ATOM); const expandedNodes = new Set(); const childToParentsMap = get(SIDEBAR_CHILD_TO_PARENTS_MAP_ATOM); - const currentNode = get(CURRENT_NODE_ATOM); + const currentNode = get(CURRENT_NODE_ID_ATOM); if (currentNode != null) { - expandedNodes.add(currentNode.id); - childToParentsMap.get(currentNode.id)?.forEach((parent) => { + expandedNodes.add(currentNode); + childToParentsMap.get(currentNode)?.forEach((parent) => { expandedNodes.add(parent); }); } + // TODO: compute default expanded nodes // the following was commented out because FDR stores `collapsed: false` by default. Another solution is needed. // const sidebar = get(SIDEBAR_ROOT_NODE_ATOM); @@ -71,64 +76,61 @@ const INITIAL_EXPANDED_SIDEBAR_NODES_ATOM = atom((get) => { // } // }); // } - return [...expandedNodes]; + + return { + sidebarRootId: sidebar?.id ?? FernNavigation.NodeId(""), + expandedNodes: new Set(), + implicitExpandedNodes: expandedNodes, + }; }); -INITIAL_EXPANDED_SIDEBAR_NODES_ATOM.debugLabel = "INITIAL_EXPANDED_SIDEBAR_NODES_ATOM"; -export const EXPANDED_SIDEBAR_NODES_ATOM = atom( - (get) => { - const sidebar = get(SIDEBAR_ROOT_NODE_ATOM); - const { expandedNodes: internalExpandedNodes, sidebarRootId } = get(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM); +export function useInitSidebarExpandedNodes(): void { + useAtomEffect( + useCallbackOne((get, set) => { + const currentNodeId = get(CURRENT_NODE_ID_ATOM); - // when the sidebar changes, reset the expanded nodes to the initial state - if (sidebarRootId !== sidebar?.id) { - return new Set(get(INITIAL_EXPANDED_SIDEBAR_NODES_ATOM)); - } + if (currentNodeId == null) { + return; + } - const childToParentsMap = get(SIDEBAR_CHILD_TO_PARENTS_MAP_ATOM); - const expandedNodes = new Set(); - internalExpandedNodes.forEach((nodeId) => { - expandedNodes.add(nodeId); - childToParentsMap.get(nodeId)?.forEach((parent) => { - expandedNodes.add(parent); - }); - }); - const current = get(CURRENT_NODE_ATOM); - if (current != null) { - expandedNodes.add(current.id); - childToParentsMap.get(current.id)?.forEach((parent) => { - expandedNodes.add(parent); - }); - } - return expandedNodes; - }, - (get, set, update: SetStateAction) => { - const sidebar = get(SIDEBAR_ROOT_NODE_ATOM); - const internal = get(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM); - - // when the sidebar changes, reset the expanded nodes to the initial state - if (internal.sidebarRootId !== sidebar?.id) { - const expandedNodes = get(INITIAL_EXPANDED_SIDEBAR_NODES_ATOM); - set(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM, { - sidebarRootId: sidebar?.id ?? FernNavigation.NodeId(""), - expandedNodes: typeof update === "function" ? update(expandedNodes) : update, + const sidebarNodeId = get.peek(SIDEBAR_ROOT_NODE_ATOM)?.id; + + // resets the sidebar expanded state when switching between tabs or versions + if (sidebarNodeId !== get.peek(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM).sidebarRootId) { + set(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM, RESET); + return; + } + + set(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM, (prev) => { + const childToParentsMap = get.peek(SIDEBAR_CHILD_TO_PARENTS_MAP_ATOM); + + // always clear the implicitly expanded nodes as a side effect of changing the current node + const implicitExpandedNodes = new Set(); + implicitExpandedNodes.add(currentNodeId); + childToParentsMap.get(currentNodeId)?.forEach((parent) => { + implicitExpandedNodes.add(parent); + }); + return { + sidebarRootId: prev.sidebarRootId, + expandedNodes: prev.expandedNodes, + implicitExpandedNodes, + }; }); - } else { - // only update the expanded nodes that are still in the sidebar - const nodes = get(SIDEBAR_CHILD_TO_PARENTS_MAP_ATOM); - set(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM, (prev) => ({ - sidebarRootId: prev.sidebarRootId, - expandedNodes: (typeof update === "function" ? update(prev.expandedNodes) : update).filter((nodeId) => - nodes.has(nodeId), - ), - })); - } - }, -); -EXPANDED_SIDEBAR_NODES_ATOM.debugLabel = "EXPANDED_SIDEBAR_NODES_ATOM"; + }, []), + ); +} export const useIsExpandedSidebarNode = (nodeId: FernNavigation.NodeId): boolean => { - return useAtomValue(useMemoOne(() => atom((get) => get(EXPANDED_SIDEBAR_NODES_ATOM).has(nodeId)), [nodeId])); + return useAtomValue( + useMemoOne( + () => + atom((get) => { + const { expandedNodes, implicitExpandedNodes } = get(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM); + return expandedNodes.has(nodeId) || implicitExpandedNodes.has(nodeId); + }), + [nodeId], + ), + ); }; export const useIsSelectedSidebarNode = (nodeId: FernNavigation.NodeId): boolean => { @@ -160,12 +162,65 @@ export const useToggleExpandedSidebarNode = (nodeId: FernNavigation.NodeId): (() useCallbackOne( (get, set) => { const parentToChildrenMap = get(SIDEBAR_PARENT_TO_CHILDREN_MAP_ATOM); - set(EXPANDED_SIDEBAR_NODES_ATOM, (prev) => { - if (prev.includes(nodeId)) { + const childToParentsMap = get(SIDEBAR_CHILD_TO_PARENTS_MAP_ATOM); + + set(INTERNAL_EXPANDED_SIDEBAR_NODES_ATOM, (prev) => { + const expandedNodes = new Set(prev.expandedNodes); + const implicitExpandedNodes = new Set(prev.implicitExpandedNodes); + const collector = get(NAVIGATION_NODES_ATOM); + + if (prev.expandedNodes.has(nodeId) || prev.implicitExpandedNodes.has(nodeId)) { + const node = collector.get(nodeId); + + if (node != null && node.id !== get(CURRENT_NODE_ID_ATOM) && FernNavigation.hasMarkdown(node)) { + return prev; + } + // remove this node and all children from the expanded set - return prev.filter((id) => id !== nodeId && !parentToChildrenMap.get(nodeId)?.includes(id)); + // return prev.filter((id) => id !== nodeId && !parentToChildrenMap.get(nodeId)?.includes(id)); + expandedNodes.delete(nodeId); + implicitExpandedNodes.delete(nodeId); + parentToChildrenMap.get(nodeId)?.forEach((child) => { + expandedNodes.delete(child); + implicitExpandedNodes.delete(child); + }); + return { + sidebarRootId: prev.sidebarRootId, + expandedNodes, + implicitExpandedNodes, + }; } else { - return [...prev, nodeId]; + const parents = childToParentsMap.get(nodeId) ?? []; + const { isApiScrollingDisabled } = get(FEATURE_FLAGS_ATOM); + + // if long scrolling is enabled, implicitly "expand" its parent nodes + if (!isApiScrollingDisabled) { + const isLongScrollingApiReference = [...parents, nodeId] + .map((id) => collector.get(id)) + .some((node) => node?.type === "apiReference" && !node.paginated); + if (isLongScrollingApiReference) { + implicitExpandedNodes.add(nodeId); + parents.forEach((parent) => { + implicitExpandedNodes.add(parent); + }); + return { + sidebarRootId: prev.sidebarRootId, + expandedNodes, + implicitExpandedNodes, + }; + } + } + + expandedNodes.add(nodeId); + childToParentsMap.get(nodeId)?.forEach((child) => { + expandedNodes.add(child); + }); + + return { + sidebarRootId: prev.sidebarRootId, + expandedNodes, + implicitExpandedNodes, + }; } }); }, diff --git a/packages/ui/app/src/components/HttpMethodTag.tsx b/packages/ui/app/src/components/HttpMethodTag.tsx index 893415c4c7..91f27c2b1e 100644 --- a/packages/ui/app/src/components/HttpMethodTag.tsx +++ b/packages/ui/app/src/components/HttpMethodTag.tsx @@ -20,13 +20,7 @@ const METHOD_COLOR_SCHEMES: Record = ({ - method, - active, - size = "lg", - className, - ...rest -}) => { +export const HttpMethodTag = memo(({ method, active, size = "lg", className, ...rest }) => { return ( = ({ {method === APIV1Read.HttpMethod.Delete ? "DEL" : method} ); -}; +}); +HttpMethodTag.displayName = "HttpMethodTag"; export function withStream(text: ReactNode, size: "sm" | "lg" = "sm"): ReactNode { return ( {text} - + ); } @@ -60,10 +55,8 @@ export function withWss(text: ReactNode, size: "sm" | "lg" = "sm"): ReactNode { {text} - + ); } - -export const HttpMethodTag = memo(UnmemoizedHttpMethodTag); diff --git a/packages/ui/app/src/components/TableOfContents.tsx b/packages/ui/app/src/components/TableOfContents.tsx index 2ade28e32a..bd2a8d4442 100644 --- a/packages/ui/app/src/components/TableOfContents.tsx +++ b/packages/ui/app/src/components/TableOfContents.tsx @@ -37,7 +37,7 @@ function TableOfContentsItem({ {text.length > 0 && ( > = ( const isMobileScreen = useAtomValue(IS_MOBILE_SCREEN_ATOM); if (!searchConfig.isAvailable || !isMobileScreen) { - return <>{children}; + return children; } if (searchConfig.inkeep?.replaceSearch !== true && algoliaSearchClient != null) { @@ -105,5 +105,5 @@ export const SearchSidebar: React.FC> = ( ); } - return <>{children}; + return children; }; diff --git a/packages/ui/app/src/sidebar/CollapsibleSidebarGroup.tsx b/packages/ui/app/src/sidebar/CollapsibleSidebarGroup.tsx new file mode 100644 index 0000000000..9309340865 --- /dev/null +++ b/packages/ui/app/src/sidebar/CollapsibleSidebarGroup.tsx @@ -0,0 +1,30 @@ +import type { FernNavigation } from "@fern-api/fdr-sdk"; +import * as Collapsible from "@radix-ui/react-collapsible"; +import type { ReactElement } from "react"; + +interface FernSidebarGroupProps { + nodes: T[]; + open: boolean; + renderNode: (node: T) => ReactElement; + children: ReactElement; +} + +export function CollapsibleSidebarGroup({ + nodes, + open, + renderNode, + children, +}: FernSidebarGroupProps): ReactElement | null { + return ( + + {children} + +
    + {nodes.map((node) => ( +
  • {renderNode(node)}
  • + ))} +
+
+
+ ); +} diff --git a/packages/ui/app/src/sidebar/DismissableSidebar.tsx b/packages/ui/app/src/sidebar/DismissableSidebar.tsx index 1f4165b341..c5165f3690 100644 --- a/packages/ui/app/src/sidebar/DismissableSidebar.tsx +++ b/packages/ui/app/src/sidebar/DismissableSidebar.tsx @@ -20,7 +20,7 @@ export function DismissableSidebar({ className }: { className?: string }): React const handleDismissSidebar = useDismissSidebar(); const showSidebar = useAtomValue(DISMISSABLE_SIDEBAR_OPEN_ATOM); const sidebarRef = useRef(null); - const duration = useAtomValue(IS_MOBILE_SCREEN_ATOM) ? 0 : 0.15; + const duration = useAtomValue(IS_MOBILE_SCREEN_ATOM) ? 0 : 0.18; useAtomEffect( useCallbackOne((_get, set) => { diff --git a/packages/ui/app/src/sidebar/Sidebar.scss b/packages/ui/app/src/sidebar/Sidebar.scss deleted file mode 100644 index cbe4d5c5a6..0000000000 --- a/packages/ui/app/src/sidebar/Sidebar.scss +++ /dev/null @@ -1,13 +0,0 @@ -@layer components { - .fern-sidebar-tabs { - @apply mt-5 mb-6 flex list-none flex-col; - } - - .fern-sidebar-searchbar-container { - @apply mb-3 hidden last:mb-0 lg:block; - } - - .fern-sidebar-header { - @apply mx-3 hidden h-header-height-real border-b border-transparent lg:flex lg:items-center lg:justify-between; - } -} diff --git a/packages/ui/app/src/sidebar/SidebarContainer.tsx b/packages/ui/app/src/sidebar/SidebarContainer.tsx index def8e8285a..07bdbfff09 100644 --- a/packages/ui/app/src/sidebar/SidebarContainer.tsx +++ b/packages/ui/app/src/sidebar/SidebarContainer.tsx @@ -8,6 +8,7 @@ import { MOBILE_SIDEBAR_ENABLED_ATOM, SIDEBAR_SCROLL_CONTAINER_ATOM, TABS_ATOM, + useInitSidebarExpandedNodes, useIsMobileSidebarOpen, useSidebarNodes, } from "../atoms"; @@ -31,13 +32,14 @@ const UnmemoizedSidebarContainer = forwardRef ((props, toggleExpand, expanded = false, rightElement, - children, tooltipContent, target, rel, @@ -148,47 +147,38 @@ const SidebarLinkInternal = forwardRef((props, ); return ( - <> -
- {withTooltip( - renderLink( - <> - {range(0, depth).map((i) => ( -
+ {withTooltip( + renderLink( + <> + {range(0, depth).map((i) => ( +
+ ))} + + {icon != null && ( + + {typeof icon === "string" ? ( + + ) : ( + icon )} - /> - ))} - - {icon != null && ( - - {typeof icon === "string" ? ( - - ) : ( - icon - )} - - )} - {createElement(as, { className: "fern-sidebar-link-text" }, title)} - {rightElement} - - {expandButton} - , - ), - )} -
- {children} - + + )} + {createElement(as, { className: "fern-sidebar-link-text" }, title)} + {rightElement} + + {expandButton} + , + ), + )} +
); }); @@ -198,7 +188,7 @@ export const SidebarLink = memo(SidebarLinkInternal); export const SidebarSlugLink = forwardRef>( (props, parentRef) => { - const { slug, onClick, ...innerProps } = props; + const { slug, onClick, toggleExpand, ...innerProps } = props; const ref = useRef(null); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion useImperativeHandle(parentRef, () => ref.current!); @@ -235,6 +225,7 @@ export const SidebarSlugLink = forwardRef diff --git a/packages/ui/app/src/sidebar/index.scss b/packages/ui/app/src/sidebar/index.scss new file mode 100644 index 0000000000..b1435c6a6d --- /dev/null +++ b/packages/ui/app/src/sidebar/index.scss @@ -0,0 +1,160 @@ +@keyframes slide-down { + 0% { + height: 0; + opacity: 0; + } + + 100% { + opacity: 1; + height: var(--radix-collapsible-content-height); + } +} + +@keyframes slide-up { + 0% { + opacity: 1; + height: var(--radix-collapsible-content-height); + } + + 100% { + opacity: 0; + height: 0; + } +} + +@layer components { + .fern-sidebar-heading { + @apply flex min-h-[32px] lg:min-h-[36px] mb-1; + @apply px-4 lg:px-3 inline-flex items-center; + + .fa-icon { + @apply mr-3 text-faded shrink-0; + } + + .fern-sidebar-heading-content { + @apply m-0 text-base leading-6 lg:text-sm lg:leading-5 font-semibold; + } + } + + .fern-sidebar-group { + @apply list-none overflow-hidden; + @apply max-lg:-mx-4; + + .fern-sidebar-group { + @apply max-lg:mx-0; + } + } + + .fern-sidebar-group[data-state="open"] { + animation: slide-down 0.18s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + } + + .fern-sidebar-group[data-state="closed"] { + animation: slide-up 0.18s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + } + + .fern-sidebar-item { + @apply scroll-my-32; + } + + .fern-sidebar-link-container { + @apply items-stretch relative flex min-h-[36px]; + @apply data-[state=active]:text-accented-a11; + + &:not(.top-level) { + @apply data-[state=inactive]:text-grayscale-a11; + } + } + + .fern-sidebar-link-indent { + @apply relative flex-none w-4 lg:w-3 shrink-0 border-l border-default; + } + + .fern-sidebar-icon { + @apply mr-3 inline-flex items-center my-0.5; + } + + .fern-sidebar-link-container[data-state="active"] { + .fern-sidebar-link-indent::after { + @apply border-l border-accented-a7 absolute inset-y-1 -left-px; + + content: ""; + } + + .fa-icon { + @apply bg-accented-a10; + } + + .fern-sidebar-icon { + @apply text-accented-a10; + } + } + + .fern-sidebar-link-container[data-state="inactive"] { + .fa-icon { + @apply bg-faded; + } + + .fern-sidebar-icon { + @apply text-faded; + } + } + + .fern-sidebar-link { + @apply text-left relative inline-flex flex-1 content-between items-stretch lg:px-3 max-lg:px-4; + @apply max-lg:rounded-none lg:rounded-lg; + @apply overflow-hidden; + } + + .fern-sidebar-link-container[data-state="active"] .fern-sidebar-link { + @apply bg-accented-a5; + } + + .fern-sidebar-link-container[data-state="inactive"] .fern-sidebar-link { + @apply hover:bg-grayscale-a3; + } + + .fern-sidebar-link-content { + @apply inline-flex flex-1 items-center py-2.5 sm:py-2; + + &:has(.fern-sidebar-icon > .fern-tag) { + @apply items-start; + } + } + + .fern-sidebar-link-text { + @apply flex-1 text-base leading-6 lg:text-sm lg:leading-5; + + word-break: break-word; + } + + .fern-sidebar-link, + .fern-sidebar-link-expand { + @apply transition-colors hover:transition-none; + } + + .fern-sidebar-link-expand { + @apply relative flex w-10 lg:w-6 justify-center items-center rounded-none ease-out; + @apply lg:rounded-lg; + + &:last-child { + @apply flex -mr-3 ml-1; + } + } + + .fern-sidebar-link-expand[data-state="active"] { + @apply after:content-[''] after:absolute after:inset-1 after:rounded-xl lg:after:rounded-md after:bg-tag-primary t-accent after:pointer-events-none; + } + + .fern-sidebar-tabs { + @apply mt-5 mb-6 flex list-none flex-col; + } + + .fern-sidebar-searchbar-container { + @apply mb-3 hidden last:mb-0 lg:block; + } + + .fern-sidebar-header { + @apply mx-3 hidden h-header-height-real border-b border-transparent lg:flex lg:items-center lg:justify-between; + } +} diff --git a/packages/ui/app/src/sidebar/nodes/SidebarApiPackageNode.tsx b/packages/ui/app/src/sidebar/nodes/SidebarApiPackageNode.tsx index 4d2e3fae3c..b0ff8e79e4 100644 --- a/packages/ui/app/src/sidebar/nodes/SidebarApiPackageNode.tsx +++ b/packages/ui/app/src/sidebar/nodes/SidebarApiPackageNode.tsx @@ -1,5 +1,5 @@ import type * as FernNavigation from "@fern-api/fdr-sdk/navigation"; -import clsx from "clsx"; +import { useCallback } from "react"; import { useIsApiReferenceShallowLink, useIsChildSelected, @@ -7,6 +7,7 @@ import { useIsSelectedSidebarNode, useToggleExpandedSidebarNode, } from "../../atoms"; +import { CollapsibleSidebarGroup } from "../CollapsibleSidebarGroup"; import { SidebarSlugLink } from "../SidebarLink"; import { SidebarApiPackageChild } from "./SidebarApiPackageChild"; @@ -27,6 +28,13 @@ export function SidebarApiPackageNode({ const expanded = useIsExpandedSidebarNode(node.id); const shallow = useIsApiReferenceShallowLink(node); + const renderNode = useCallback( + (node: FernNavigation.ApiPackageChild) => ( + + ), + [depth, shallow], + ); + if (node.children.length === 0) { if (node.overviewPageId == null) { return null; @@ -57,7 +65,7 @@ export function SidebarApiPackageNode({ if (node.type === "apiReference" && node.hideTitle) { return ( -
    +
      {node.children.map((child) => (
    • @@ -70,31 +78,25 @@ export function SidebarApiPackageNode({ const showIndicator = childSelected && !expanded; return ( - +