Skip to content

Commit

Permalink
Include page groups in page TOC
Browse files Browse the repository at this point in the history
Resolves #2616
  • Loading branch information
Gerrit0 committed Jun 26, 2024
1 parent 9f0fb04 commit 0304c26
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 91 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unreleased

### Features

- "On This Page" navigation now includes the page groups in collapsible sections, #2616.

### Bug Fixes

- `mailto:` links are no longer incorrectly recognized as relative paths, #2613.
Expand Down
38 changes: 31 additions & 7 deletions src/lib/output/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ export class RendererEvent {
}
}

export interface PageHeading {
link: string;
text: string;
level?: number;
kind?: ReflectionKind;
classes?: string;
}

/**
* An event emitted by the {@link Renderer} class before and after the
* markup of a page is rendered.
Expand Down Expand Up @@ -108,13 +116,29 @@ export class PageEvent<out Model = unknown> extends Event {
* Links to content within this page that should be rendered in the page navigation.
* This is built when rendering the document content.
*/
pageHeadings: Array<{
link: string;
text: string;
level?: number;
kind?: ReflectionKind;
classes?: string;
}> = [];
pageHeadings: PageHeading[] = [];

/**
* Sections of the page, generally set by `@group`s
*/
pageSections = [
{
title: "",
headings: this.pageHeadings,
},
];

/**
* Start a new section of the page. Sections are collapsible within
* the "On This Page" sidebar.
*/
startNewSection(title: string) {
this.pageHeadings = [];
this.pageSections.push({
title,
headings: this.pageHeadings,
});
}

/**
* Triggered before a document will be rendered.
Expand Down
6 changes: 3 additions & 3 deletions src/lib/output/themes/default/DefaultThemeRenderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { memberSignatureTitle } from "./partials/member.signature.title";
import { memberSignatures } from "./partials/member.signatures";
import { memberSources } from "./partials/member.sources";
import { members } from "./partials/members";
import { membersGroup } from "./partials/members.group";
import {
sidebar,
pageSidebar,
Expand Down Expand Up @@ -63,7 +62,7 @@ export class DefaultThemeRenderContext {
i18n: TranslationProxy;

constructor(
private theme: DefaultTheme,
readonly theme: DefaultTheme,
public page: PageEvent<Reflection>,
options: Options,
) {
Expand Down Expand Up @@ -144,7 +143,8 @@ export class DefaultThemeRenderContext {
memberSignatures = bind(memberSignatures, this);
memberSources = bind(memberSources, this);
members = bind(members, this);
membersGroup = bind(membersGroup, this);
/** @deprecated Since 0.26.3 members does group/category flattening internally */
membersGroup?: Function;
sidebar = bind(sidebar, this);
pageSidebar = bind(pageSidebar, this);
sidebarLinks = bind(sidebarLinks, this);
Expand Down
39 changes: 0 additions & 39 deletions src/lib/output/themes/default/partials/members.group.tsx

This file was deleted.

86 changes: 56 additions & 30 deletions src/lib/output/themes/default/partials/members.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext";
import { JSX } from "../../../../utils";
import { type ContainerReflection, DeclarationReflection } from "../../../../models";
import { classNames } from "../../lib";
import { filterMap, JSX } from "../../../../utils";
import type { ContainerReflection } from "../../../../models";

export function members(context: DefaultThemeRenderContext, props: ContainerReflection) {
if (props.categories && props.categories.length) {
return (
<>
{props.categories.map(
(item) =>
!item.allChildrenHaveOwnDocument() && (
<details
class={classNames(
{ "tsd-panel-group": true, "tsd-member-group": true, "tsd-accordion": true },
props instanceof DeclarationReflection ? context.getReflectionClasses(props) : "",
)}
>
<summary class="tsd-accordion-summary" data-key={"section-" + item.title}>
<h2>
{context.icons.chevronDown()} {item.title}
</h2>
</summary>
<section>
{item.children.map((item) => !item.hasOwnDocument && context.member(item))}
</section>
</details>
),
)}
</>
);
function getMemberSections(parent: ContainerReflection) {
if (parent.categories?.length) {
return filterMap(parent.categories, (cat) => {
if (!cat.allChildrenHaveOwnDocument()) {
return {
title: cat.title,
children: cat.children.filter((child) => !child.hasOwnDocument),
};
}
});
}

return <>{props.groups?.map((item) => !item.allChildrenHaveOwnDocument() && context.membersGroup(item))}</>;
if (parent.groups?.length) {
return parent.groups.flatMap((group) => {
if (group.categories?.length) {
return filterMap(group.categories, (cat) => {
if (!cat.allChildrenHaveOwnDocument()) {
return {
title: `${group.title} - ${cat.title}`,
children: cat.children.filter((child) => !child.hasOwnDocument),
};
}
});
}

return {
title: group.title,
children: group.children.filter((child) => !child.hasOwnDocument),
};
});
}

return [];
}

export function members(context: DefaultThemeRenderContext, props: ContainerReflection) {
const sections = getMemberSections(props).filter((sect) => sect.children.length);

return (
<>
{sections.map(({ title, children }) => {
context.page.startNewSection(title);

return (
<details class="tsd-panel-group tsd-member-group tsd-accordion" open>
<summary class="tsd-accordion-summary" data-key={"section-" + title}>
<h2>
{context.icons.chevronDown()} {title}
</h2>
</summary>
<section>{children.map((item) => context.member(item))}</section>
</details>
);
})}
</>
);
}
41 changes: 33 additions & 8 deletions src/lib/output/themes/default/partials/navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Reflection, ReflectionFlag, ReflectionKind } from "../../../../models";
import { JSX } from "../../../../utils";
import type { PageEvent } from "../../../events";
import type { PageEvent, PageHeading } from "../../../events";
import { classNames, getDisplayName, wbr } from "../../lib";
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext";

Expand Down Expand Up @@ -145,7 +145,7 @@ export function pageSidebar(context: DefaultThemeRenderContext, props: PageEvent
);
}

export function pageNavigation(context: DefaultThemeRenderContext, props: PageEvent<Reflection>) {
function buildSectionNavigation(context: DefaultThemeRenderContext, headings: PageHeading[]) {
const levels: JSX.Element[][] = [[]];

function finalizeLevel(finishedHandlingHeadings: boolean) {
Expand All @@ -165,8 +165,12 @@ export function pageNavigation(context: DefaultThemeRenderContext, props: PageEv
levels[levels.length - 1].push(built);
}

for (const heading of props.pageHeadings) {
const inferredLevel = heading.level ? heading.level + 1 : 1;
for (const heading of headings) {
const inferredLevel = heading.level
? heading.level + 2 // regular heading
: heading.kind
? 2 // reflection
: 1; // group/category
while (inferredLevel < levels.length) {
finalizeLevel(false);
}
Expand All @@ -187,12 +191,33 @@ export function pageNavigation(context: DefaultThemeRenderContext, props: PageEv
finalizeLevel(true);
}

if (!levels[0].length) {
levels.unshift([]);
finalizeLevel(true);
return levels[0];
}

export function pageNavigation(context: DefaultThemeRenderContext, props: PageEvent<Reflection>) {
if (!props.pageSections.some((sect) => sect.headings.length)) {
return <></>;
}

levels.unshift([]);
finalizeLevel(true);
const sections: JSX.Children = [];

for (const section of props.pageSections) {
if (section.title) {
sections.push(
<details open class="tsd-accordion tsd-page-navigation-section">
<summary class="tsd-accordion-summary" data-key={`tsd-otp-${section.title}`}>
{context.icons.chevronDown()}
{section.title}
</summary>
<div>{buildSectionNavigation(context, section.headings)}</div>
</details>,
);
} else {
sections.push(buildSectionNavigation(context, section.headings));
}
}

return (
<details open={true} class="tsd-accordion tsd-page-navigation">
Expand All @@ -202,7 +227,7 @@ export function pageNavigation(context: DefaultThemeRenderContext, props: PageEv
{context.i18n.theme_on_this_page()}
</h3>
</summary>
<div class="tsd-accordion-details">{levels[0]}</div>
<div class="tsd-accordion-details">{sections}</div>
</details>
);
}
6 changes: 3 additions & 3 deletions src/lib/utils/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ export function getEnumKeys(Enum: {}): string[] {
return Object.keys(E).filter((k) => E[E[k]] === k);
}

export type EnumKeys<E extends {}> = keyof {
[K in keyof E as number extends E[K] ? K : never]: 1;
};
export type EnumKeys<E extends {}> = {
[K in keyof E]: number extends E[K] ? K : never;
}[keyof E] & {};
2 changes: 1 addition & 1 deletion src/lib/utils/options/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export interface TypeDocOptionMap {
externalPattern: string[];
excludeExternals: boolean;
excludeNotDocumented: boolean;
excludeNotDocumentedKinds: Array<keyof typeof ReflectionKind>;
excludeNotDocumentedKinds: ReflectionKind.KindString[];
excludeInternal: boolean;
excludePrivate: boolean;
excludeProtected: boolean;
Expand Down
10 changes: 10 additions & 0 deletions static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,15 @@ input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark {
margin-left: -1.5rem;
}

.tsd-page-navigation-section {
margin-left: 10px;
}
.tsd-page-navigation-section > summary {
padding: 0.25rem;
}
.tsd-page-navigation-section > div {
margin-left: 20px;
}
.tsd-page-navigation ul {
padding-left: 1.75rem;
}
Expand Down Expand Up @@ -841,6 +850,7 @@ a.tsd-index-link {
}
.tsd-accordion .tsd-accordion-summary > svg {
margin-left: 0.25rem;
vertical-align: text-top;
}
.tsd-index-content > :not(:first-child) {
margin-top: 0.75rem;
Expand Down

0 comments on commit 0304c26

Please sign in to comment.