docusaurus/packages/docusaurus-theme-classic/src/theme/TOCItems/index.tsx
Erick Zhao c86dfbda61
feat(v2): allow specifying TOC max depth (themeConfig + frontMatter) (#5578)
* feat: add all TOC levels to MDX loader

* feat: add theme-level config for heading depth

* test: add remark MDX loader test

* fix: limit maxDepth validation to H2 - H6

* refactor: set default `maxDepth` using `joi`

* refactor: `maxDepth` -> `maxHeadingLevel

* refactor: invert underlying TOC depth API

* refactor: make TOC algorithm level-aware

* feat: add support for per-doc TOC heading levels

* feat: support document-level heading levels for blog

* fix: correct validation for toc level frontmatter

* fix: ensure TOC doesn't generate redundant DOM

* perf: simpler TOC heading search alg

* docs: document heading level props for `TOCInline`

* Update website/docs/guides/markdown-features/markdown-features-inline-toc.mdx

Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>

* docs: fix docs (again)

* create dedicated  test file for heading searching logic: exhaustive tests will be simpler to write

* toc search: add real-world test

* fix test

* add dogfooding tests for toc min/max

* add test for min/max toc frontmatter

* reverse min/max order

* add theme minHeadingLevel + tests

* simpler TOC rendering logic

* simplify TOC implementation (temp, WIP)

* reverse unnatural order for minHeadingLevel/maxHeadingLevel

* add TOC dogfooding tests to all content plugins

* expose toc min/max heading level frontmatter to all 3 content plugins

* refactor blogLayout: accept toc ReactElement directly

* move toc utils to theme-common

* add tests for filterTOC

* create new generic TOCItems component

* useless css file copied

* fix toc highlighting className conflicts

* update doc

* fix types

Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
Co-authored-by: slorber <lorber.sebastien@gmail.com>
2021-09-29 11:19:11 +02:00

96 lines
2.6 KiB
TypeScript

/**
* 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 React, {useMemo} from 'react';
import type {TOCItemsProps} from '@theme/TOCItems';
import {TOCItem} from '@docusaurus/types';
import {
TOCHighlightConfig,
useThemeConfig,
useTOCFilter,
useTOCHighlight,
} from '@docusaurus/theme-common';
// Recursive component rendering the toc tree
/* eslint-disable jsx-a11y/control-has-associated-label */
function TOCItemList({
toc,
className = 'table-of-contents table-of-contents__left-border',
linkClassName = 'table-of-contents__link',
isChild,
}: {
readonly toc: readonly TOCItem[];
readonly className: string;
readonly linkClassName: string;
readonly isChild?: boolean;
}): JSX.Element | null {
if (!toc.length) {
return null;
}
return (
<ul className={isChild ? '' : className}>
{toc.map((heading) => (
<li key={heading.id}>
<a
href={`#${heading.id}`}
className={linkClassName}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: heading.value}}
/>
<TOCItemList
isChild
toc={heading.children}
className={className}
linkClassName={linkClassName}
/>
</li>
))}
</ul>
);
}
export default function TOCItems({
toc,
className = 'table-of-contents table-of-contents__left-border',
linkClassName = 'table-of-contents__link',
linkActiveClassName = undefined,
minHeadingLevel: minHeadingLevelOption,
maxHeadingLevel: maxHeadingLevelOption,
...props
}: TOCItemsProps): JSX.Element | null {
const themeConfig = useThemeConfig();
const minHeadingLevel =
minHeadingLevelOption ?? themeConfig.tableOfContents.minHeadingLevel;
const maxHeadingLevel =
maxHeadingLevelOption ?? themeConfig.tableOfContents.maxHeadingLevel;
const tocFiltered = useTOCFilter({toc, minHeadingLevel, maxHeadingLevel});
const tocHighlightConfig: TOCHighlightConfig | undefined = useMemo(() => {
if (linkClassName && linkActiveClassName) {
return {
linkClassName,
linkActiveClassName,
minHeadingLevel,
maxHeadingLevel,
};
}
return undefined;
}, [linkClassName, linkActiveClassName, minHeadingLevel, maxHeadingLevel]);
useTOCHighlight(tocHighlightConfig);
return (
<TOCItemList
toc={tocFiltered}
className={className}
linkClassName={linkClassName}
{...props}
/>
);
}