Skip to main content
Glama
TableOfContents.tsx4.25 kB
'use client'; import { Link } from '@intlayer/design-system'; import { cn } from '@utils/cn'; import { useIntlayer } from 'next-intlayer'; import type { FC } from 'react'; import { useTitlesTree } from '../DocPage/useTitlesTree'; type TableOfContentsProps = { /** Array of heading levels to display (e.g., [2, 3, 4] for h2, h3, h4) */ levels: number[]; /** Maximum depth of nested headings to show */ maxDepth?: number; /** Custom class name for styling */ className?: string; /** Content element ID to observe for headings */ contentId?: string; }; type HeadingChildren = Map<HTMLElement, HTMLElement[]>; type NavTitlesChildrenProps = { headings: HTMLElement[]; childrenMap: HeadingChildren; depth: number; maxDepth: number; }; const NavTitlesChildren: FC<NavTitlesChildrenProps> = ({ headings, childrenMap, depth, maxDepth, }) => { const { linkLabel } = useIntlayer('nav-titles'); if (depth >= maxDepth) return null; return ( <ul className={cn( 'my-2 flex flex-1 flex-col gap-2', depth === 1 && 'ml-6', depth === 2 && 'ml-8', depth === 3 && 'ml-12', depth === 4 && 'ml-16', depth === 5 && 'ml-20', depth === 6 && 'ml-24' )} > {headings.map((heading) => { const { id } = heading; const subChildren = childrenMap.get(heading); const hasChildren = subChildren && subChildren.length > 0; if ((heading.innerText ?? '').length === 0) return <></>; return ( <li key={id} className={cn('list-none')}> <Link href={`#${id}`} label={`${linkLabel.value}: ${heading.innerText}`} color="text" variant="hoverable" className={cn( 'flex flex-1 items-center justify-between text-wrap p-2 pr-4 text-base transition-colors', depth === 1 && 'text-neutral/75 text-sm', depth === 2 && 'text-neutral/60 text-sm', depth === 3 && 'text-neutral/50 text-sm', depth === 4 && 'text-neutral/40 text-xs', depth === 5 && 'text-neutral/30 text-xs', depth === 6 && 'text-neutral/20 text-xs' )} > {heading.innerText} </Link> {hasChildren && ( <NavTitlesChildren headings={subChildren} childrenMap={childrenMap} depth={depth + 1} maxDepth={maxDepth} /> )} </li> ); })} </ul> ); }; export const TableOfContents: FC<TableOfContentsProps> = ({ levels, maxDepth = 2, className, contentId = 'content', }) => { const { linkLabel } = useIntlayer('nav-titles'); const { topLevelHeadings, headingMap, isLoading } = useTitlesTree({ levels, contentId, }); if (isLoading || topLevelHeadings.length === 0) { return null; } return ( <nav className={className}> <ul className="flex flex-1 flex-col gap-3 pt-8 text-sm"> {topLevelHeadings.map((heading) => { const { id } = heading; const children = headingMap.get(heading); const hasChildren = children && children.length > 0; if ((heading.innerText ?? '').length === 0) return <></>; return ( <li key={id} className="list-none"> <Link href={`#${id}`} label={`${linkLabel.value}: ${heading.innerText}`} color="text" isPageSection variant="hoverable" className="flex w-full items-center justify-between text-wrap p-2 pr-4 text-base text-neutral transition-colors" > {heading.innerText} </Link> {hasChildren && ( <div className="border-neutral/20 border-l"> <NavTitlesChildren headings={children} childrenMap={headingMap} depth={1} maxDepth={maxDepth} /> </div> )} </li> ); })} </ul> </nav> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server