// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { TreeView } from '@jupyter/react-components'; import * as React from 'react'; import { TableOfContentsItem } from './tocitem'; import { TableOfContents } from './tokens'; /** * Interface describing component properties. */ export interface ITableOfContentsTreeProps { /** * Currently active heading. */ activeHeading: TableOfContents.IHeading | null; /** * Type of document supported by the model. */ documentType: string; /** * List of headings to render. */ headings: TableOfContents.IHeading[]; /** * Set active heading. */ setActiveHeading: (heading: TableOfContents.IHeading) => void; /** * Collapse heading callback. */ onCollapseChange: (heading: TableOfContents.IHeading) => void; } /** * React component for a table of contents tree. */ export class TableOfContentsTree extends React.PureComponent { /** * Renders a table of contents tree. */ render(): JSX.Element { const { documentType } = this.props; return ( {this.buildTree()} ); } /** * Convert the flat headings list to a nested tree list */ protected buildTree(): JSX.Element[] { if (this.props.headings.length === 0) { return []; } const buildOneTree = (currentIndex: number): [JSX.Element, number] => { const items = this.props.headings; const children = new Array(); const current = items[currentIndex]; let nextCandidateIndex = currentIndex + 1; while (nextCandidateIndex < items.length) { const candidateItem = items[nextCandidateIndex]; if (candidateItem.level <= current.level) { break; } const [child, nextIndex] = buildOneTree(nextCandidateIndex); children.push(child); nextCandidateIndex = nextIndex; } const currentTree = ( {children.length ? children : null} ); return [currentTree, nextCandidateIndex]; }; const trees = new Array(); let currentIndex = 0; while (currentIndex < this.props.headings.length) { const [tree, nextIndex] = buildOneTree(currentIndex); trees.push(tree); currentIndex = nextIndex; } return trees; } }