import { Box } from '@luke-ui/react/box'; import { Icon } from '@luke-ui/react/icon'; import { Stack } from '@luke-ui/react/stack'; import { Text } from '@luke-ui/react/text'; import { vars } from '@luke-ui/react/theme'; import { cx } from '@luke-ui/react/utils'; import { useActiveAnchor } from 'fumadocs-core/toc'; import type { TOCItemType } from 'fumadocs-core/toc'; import type { CSSProperties, ReactNode } from 'react'; import { useEffect, useRef } from 'react'; import { docsNavPaneProps } from '../docs-nav.js'; import * as styles from './docs-article.css.js'; const TOC_LABEL = 'On this page'; const tocListBorder = { borderInlineStart: `1px solid ${vars.color.border.decorative}`, } as const satisfies CSSProperties; const tocLinkPaddingInlineStart = { top: 'sp12', nested: 'sp24', deep: 'sp40', } as const; /** Sticky table of contents shown in its own column at wide viewports. */ export function DocsTocColumn({ toc }: { toc: Array }) { return ( {TOC_LABEL} ); } /** Disclosure that holds the table of contents below the wide breakpoint. */ export function DocsTocBar({ toc }: { toc: Array }) { const detailsRef = useRef(null); function close() { if (detailsRef.current) detailsRef.current.open = false; } // Escape closes the panel from anywhere inside it and returns focus to the summary. A press // outside the disclosure closes it too, because the open panel overlays the article. useEffect(() => { const details = detailsRef.current; if (!details) return; function handleKeyDown(event: KeyboardEvent) { if (event.key !== 'Escape' || !details?.open) return; details.open = false; details.querySelector('summary')?.focus(); } function handlePointerDown(event: PointerEvent) { if (event.target instanceof Node && !details?.contains(event.target)) close(); } details.addEventListener('keydown', handleKeyDown); document.addEventListener('pointerdown', handlePointerDown); return () => { details.removeEventListener('keydown', handleKeyDown); document.removeEventListener('pointerdown', handlePointerDown); }; }, []); return (
{TOC_LABEL}
); } function TocLinks({ onNavigate, toc }: { onNavigate?: () => void; toc: Array }) { const activeId = useActiveAnchor(); return ( {toc.map((item) => ( {item.title} ))} ); } function TocLink({ children, href, isCurrent, onNavigate, paddingInlineStart, }: { children: ReactNode; href: string; isCurrent: boolean; onNavigate?: () => void; paddingInlineStart: (typeof tocLinkPaddingInlineStart)[keyof typeof tocLinkPaddingInlineStart]; }) { return ( ( {domProps.children} )} > {children} ); } /** Heading depth 2 sits flush. Each deeper level indents one step, capped at two. */ function tocDepth(depth: number): keyof typeof tocLinkPaddingInlineStart { if (depth <= 2) return 'top'; return depth === 3 ? 'nested' : 'deep'; }