Skip to content

Commit

Permalink
🗺 Add option for maxdepth on an outline
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanc1 committed Nov 22, 2023
1 parent 9f44076 commit a03c57e
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 23 deletions.
6 changes: 6 additions & 0 deletions .changeset/neat-spiders-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@myst-theme/site': patch
'@myst-theme/book': patch
---

Add `outline_maxdepth` to document outline
52 changes: 31 additions & 21 deletions packages/site/src/components/DocumentOutline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const HIGHLIGHT_CLASS = 'highlight';
const onClient = typeof document !== 'undefined';

type Heading = {
element: HTMLHeadingElement;
id: string;
title: string;
titleHTML: string;
Expand Down Expand Up @@ -46,15 +47,15 @@ const Headings = ({ headings, activeId, highlight, selector }: Props) => (
>
<a
className={classNames('block p-1', {
'text-slate-900 dark:text-slate-50': heading.level < 3 && heading.id !== activeId,
'text-slate-500 dark:text-slate-300': heading.level >= 3 && heading.id !== activeId,
'text-slate-900 dark:text-slate-50': heading.level < 2 && heading.id !== activeId,
'text-slate-500 dark:text-slate-300': heading.level >= 2 && heading.id !== activeId,
'text-blue-600 dark:text-white font-bold': heading.id === activeId,
'pr-2': heading.id !== activeId, // Allows for bold to change length
'pl-2': heading.level === 1 || heading.level === 2,
'pl-4': heading.level === 3,
'pl-8 text-xs': heading.level === 4,
'pl-10 text-xs font-light': heading.level === 5,
'pl-12 text-xs font-extralight': heading.level === 6,
'pl-2': heading.level === 1,
'pl-4': heading.level === 2,
'pl-8 text-xs': heading.level === 3,
'pl-10 text-xs font-light': heading.level === 4,
'pl-12 text-xs font-extralight': heading.level === 5,
})}
href={`#${heading.id}`}
onClick={(e) => {
Expand Down Expand Up @@ -104,7 +105,7 @@ function getHeaders(selector: string): HTMLHeadingElement[] {
return headers as HTMLHeadingElement[];
}

function useHeaders(selector: string) {
function useHeaders(selector: string, maxdepth: number) {
if (!onClient) return { activeId: '', headings: [] };
const onScreen = useRef<Set<HTMLHeadingElement>>(new Set());
const [activeId, setActiveId] = useState<string>();
Expand Down Expand Up @@ -145,28 +146,35 @@ function useHeaders(selector: string) {
Array.from(elements).map((e) => observer.current?.observe(e));
}, [observer]);

elements.forEach((e) => {
if (headingsSet.current.has(e)) return;
observer.current?.observe(e);
headingsSet.current.add(e);
});

let minLevel = 10;
const headings: Heading[] = elements
.map((heading) => {
.map((element) => {
return {
level: Number(heading.tagName.slice(1)),
id: heading.id,
text: heading.querySelector('.heading-text'),
element,
level: Number(element.tagName.slice(1)),
id: element.id,
text: element.querySelector('.heading-text'),
};
})
.filter((h) => !!h.text)
.map(({ level, text, id }) => {
.map(({ element, level, text, id }) => {
const { innerText: title, innerHTML: titleHTML } = cloneHeadingElement(
text as HTMLSpanElement,
);
return { title, titleHTML, id, level };
minLevel = Math.min(minLevel, level);
return { element, title, titleHTML, id, level };
})
.filter((heading) => {
heading.level = heading.level - minLevel + 1;
return heading.level < maxdepth + 1;
});

headings.forEach(({ element: e }) => {
if (headingsSet.current.has(e)) return;
observer.current?.observe(e);
headingsSet.current.add(e);
});

return { activeId, highlight, headings };
}

Expand Down Expand Up @@ -224,15 +232,17 @@ export const DocumentOutline = ({
className,
selector = SELECTOR,
children,
maxdepth = 4,
}: {
outlineRef?: React.RefObject<HTMLElement>;
top?: number;
height?: number;
className?: string;
selector?: string;
children?: React.ReactNode;
maxdepth?: number;
}) => {
const { activeId, headings, highlight } = useHeaders(selector);
const { activeId, headings, highlight } = useHeaders(selector, maxdepth);
if (headings.length <= 1 || !onClient) {
return <nav suppressHydrationWarning>{children}</nav>;
}
Expand Down
13 changes: 11 additions & 2 deletions themes/book/app/routes/$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ interface BookThemeTemplateOptions {
hide_toc?: boolean;
hide_outline?: boolean;
hide_footer_links?: boolean;
outline_maxdepth?: number;
}

export default function Page() {
Expand All @@ -99,13 +100,21 @@ export default function Page() {
const pageDesign: BookThemeTemplateOptions = (article.frontmatter as any)?.options ?? {};
const siteDesign: BookThemeTemplateOptions =
(useSiteManifest() as SiteManifest & BookThemeTemplateOptions)?.options ?? {};
const { hide_toc, hide_outline, hide_footer_links } = { ...siteDesign, ...pageDesign };
const { hide_toc, hide_outline, hide_footer_links, outline_maxdepth } = {
...siteDesign,
...pageDesign,
};
return (
<ArticlePageAndNavigation hide_toc={hide_toc} projectSlug={article.project}>
<main ref={container} className="article-grid subgrid-gap col-screen">
{!hide_outline && (
<div className="sticky z-10 hidden h-0 col-margin-right-inset lg:block" style={{ top }}>
<DocumentOutline top={16} className="relative" outlineRef={outline} />
<DocumentOutline
top={16}
className="relative"
outlineRef={outline}
maxdepth={outline_maxdepth}
/>
</div>
)}
<ArticlePage article={article} hide_all_footer_links={hide_footer_links} />
Expand Down
6 changes: 6 additions & 0 deletions themes/book/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ options:
- type: boolean
id: hide_outline
description: Hide the document outline on all pages
- type: number
id: outline_maxdepth
description: The maximum depth to show on the document outline, for example, `2` would show only two depths of headings (e.g. `<H1>` and `<H2>`).
min: 1
max: 6
integer: true
- type: string
id: twitter
description: Twitter handle related to the site
Expand Down

0 comments on commit a03c57e

Please sign in to comment.