beta/src/components/Layout/Toc.tsx (70 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. */ import cx from 'classnames'; import * as React from 'react'; import {useTocHighlight} from './useTocHighlight'; export function Toc({ headings, }: { headings: Array<{url: string; text: React.ReactNode; depth: number}>; }) { const {currentIndex} = useTocHighlight(); // TODO: We currently have a mismatch between the headings in the document // and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges). // Select the max TOC item we have here for now, but remove this after the fix. const selectedIndex = Math.min(currentIndex, headings.length - 1); return ( <nav role="navigation" className="pt-6 fixed top-10 right-0" style={{ // This keeps the layout fixed width instead of adjusting for content. width: 'inherit', maxWidth: 'inherit', }}> <h2 className="mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full"> On this page </h2> <div className="toc h-full overflow-y-auto pl-4"> <ul className="space-y-2 pb-16"> {headings && headings.length > 0 && headings.map((h, i) => { if (h.url == null) { // TODO: only log in DEV console.error('Heading does not have URL'); } return ( <li key={`heading-${h.url}-${i}`} className={cx( 'text-sm px-2 rounded-l-lg', selectedIndex === i ? 'bg-highlight dark:bg-highlight-dark' : null, { 'pl-4': h?.depth === 3, hidden: h.depth && h.depth > 3, } )}> <a className={cx( selectedIndex === i ? 'text-link dark:text-link-dark font-bold' : 'text-secondary dark:text-secondary-dark', 'block hover:text-link dark:hover:text-link-dark leading-normal py-2' )} href={h.url}> {h.text} </a> </li> ); })} </ul> </div> <style jsx global>{` .toc { /** Screen - nav - toc offset */ max-height: calc(100vh - 7.5rem); } .toc-link > code { overflow-wrap: break-word; white-space: pre-wrap; font-size: 90%; } `}</style> </nav> ); }