/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { useMemo } from 'react'; function treeifyTOC(flatTOC) { const headings = flatTOC.map((heading) => ({ ...heading, parentIndex: -1, children: [], })); // Keep track of which previous index would be the current heading's direct // parent. Each entry is the last index of the `headings` array at heading // level . We will modify these indices as we iterate through all headings. // e.g. if an ### H3 was last seen at index 2, then prevIndexForLevel[3] === 2 // indices 0 and 1 will remain unused. const prevIndexForLevel = Array(7).fill(-1); headings.forEach((curr, currIndex) => { // Take the last seen index for each ancestor level. the highest index will // be the direct ancestor of the current heading. const ancestorLevelIndexes = prevIndexForLevel.slice(2, curr.level); curr.parentIndex = Math.max(...ancestorLevelIndexes); // Mark that curr.level was last seen at the current index. prevIndexForLevel[curr.level] = currIndex; }); const rootNodes = []; // For a given parentIndex, add each Node into that parent's `children` array headings.forEach((heading) => { const { parentIndex, ...rest } = heading; if (parentIndex >= 0) { headings[parentIndex].children.push(rest); } else { rootNodes.push(rest); } }); return rootNodes; } /** * Takes a flat TOC list (from the MDX loader) and treeifies it into what the * TOC components expect. Memoized for performance. */ export function useTreeifiedTOC(toc) { return useMemo(() => treeifyTOC(toc), [toc]); } function filterTOC({ toc, minHeadingLevel, maxHeadingLevel, }) { function isValid(item) { return item.level >= minHeadingLevel && item.level <= maxHeadingLevel; } return toc.flatMap((item) => { const filteredChildren = filterTOC({ toc: item.children, minHeadingLevel, maxHeadingLevel, }); if (isValid(item)) { return [ { ...item, children: filteredChildren, }, ]; } return filteredChildren; }); } /** * Takes a flat TOC list (from the MDX loader) and treeifies it into what the * TOC components expect, applying the `minHeadingLevel` and `maxHeadingLevel`. * Memoized for performance. * * **Important**: this is not the same as `useTreeifiedTOC(toc.filter(...))`, * because we have to filter the TOC after it has been treeified. This is mostly * to ensure that weird TOC structures preserve their semantics. For example, an * h3-h2-h4 sequence should not be treeified as an "h3 > h4" hierarchy with * min=3, max=4, but should rather be "[h3, h4]" (since the h2 heading has split * the two headings and they are not parent-children) */ export function useFilteredAndTreeifiedTOC({ toc, minHeadingLevel, maxHeadingLevel, }) { return useMemo(() => filterTOC({ toc: treeifyTOC(toc), minHeadingLevel, maxHeadingLevel }), [toc, minHeadingLevel, maxHeadingLevel]); } //# sourceMappingURL=tocUtils.js.map