mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-17 19:16:58 +02:00
feat(docs,theme-classic): docs breadcrumbs (#6517)
Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
67918e35e2
commit
3629b5ab39
16 changed files with 341 additions and 1 deletions
|
@ -242,6 +242,7 @@ exports[`simple website content 5`] = `
|
||||||
Object {
|
Object {
|
||||||
"pluginName": Object {
|
"pluginName": Object {
|
||||||
"pluginId": Object {
|
"pluginId": Object {
|
||||||
|
"breadcrumbs": true,
|
||||||
"path": "/docs",
|
"path": "/docs",
|
||||||
"versions": Array [
|
"versions": Array [
|
||||||
Object {
|
Object {
|
||||||
|
@ -955,6 +956,7 @@ exports[`simple website content: global data 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"pluginName": Object {
|
"pluginName": Object {
|
||||||
"pluginId": Object {
|
"pluginId": Object {
|
||||||
|
"breadcrumbs": true,
|
||||||
"path": "/docs",
|
"path": "/docs",
|
||||||
"versions": Array [
|
"versions": Array [
|
||||||
Object {
|
Object {
|
||||||
|
@ -2411,6 +2413,7 @@ exports[`versioned website (community) content: global data 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"pluginName": Object {
|
"pluginName": Object {
|
||||||
"pluginId": Object {
|
"pluginId": Object {
|
||||||
|
"breadcrumbs": true,
|
||||||
"path": "/community",
|
"path": "/community",
|
||||||
"versions": Array [
|
"versions": Array [
|
||||||
Object {
|
Object {
|
||||||
|
@ -3450,6 +3453,7 @@ exports[`versioned website content: global data 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"pluginName": Object {
|
"pluginName": Object {
|
||||||
"pluginId": Object {
|
"pluginId": Object {
|
||||||
|
"breadcrumbs": true,
|
||||||
"path": "/docs",
|
"path": "/docs",
|
||||||
"versions": Array [
|
"versions": Array [
|
||||||
Object {
|
Object {
|
||||||
|
|
|
@ -56,6 +56,7 @@ describe('normalizeDocsPluginOptions', () => {
|
||||||
rehypePlugins: [markdownPluginsFunctionStub],
|
rehypePlugins: [markdownPluginsFunctionStub],
|
||||||
beforeDefaultRehypePlugins: [],
|
beforeDefaultRehypePlugins: [],
|
||||||
beforeDefaultRemarkPlugins: [],
|
beforeDefaultRemarkPlugins: [],
|
||||||
|
breadcrumbs: true,
|
||||||
showLastUpdateTime: true,
|
showLastUpdateTime: true,
|
||||||
showLastUpdateAuthor: true,
|
showLastUpdateAuthor: true,
|
||||||
admonitions: {},
|
admonitions: {},
|
||||||
|
|
|
@ -217,6 +217,7 @@ export default async function pluginContentDocs(
|
||||||
docLayoutComponent,
|
docLayoutComponent,
|
||||||
docItemComponent,
|
docItemComponent,
|
||||||
docCategoryGeneratedIndexComponent,
|
docCategoryGeneratedIndexComponent,
|
||||||
|
breadcrumbs,
|
||||||
} = options;
|
} = options;
|
||||||
const {addRoute, createData, setGlobalData} = actions;
|
const {addRoute, createData, setGlobalData} = actions;
|
||||||
|
|
||||||
|
@ -295,6 +296,7 @@ export default async function pluginContentDocs(
|
||||||
setGlobalData<GlobalPluginData>({
|
setGlobalData<GlobalPluginData>({
|
||||||
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
||||||
versions: loadedVersions.map(toGlobalDataVersion),
|
versions: loadedVersions.map(toGlobalDataVersion),
|
||||||
|
breadcrumbs,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
|
||||||
editLocalizedFiles: false,
|
editLocalizedFiles: false,
|
||||||
sidebarCollapsible: true,
|
sidebarCollapsible: true,
|
||||||
sidebarCollapsed: true,
|
sidebarCollapsed: true,
|
||||||
|
breadcrumbs: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VersionOptionsSchema = Joi.object({
|
const VersionOptionsSchema = Joi.object({
|
||||||
|
@ -139,6 +140,7 @@ export const OptionsSchema = Joi.object({
|
||||||
disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning),
|
disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning),
|
||||||
lastVersion: Joi.string().optional(),
|
lastVersion: Joi.string().optional(),
|
||||||
versions: VersionsOptionsSchema,
|
versions: VersionsOptionsSchema,
|
||||||
|
breadcrumbs: Joi.bool().default(DEFAULT_OPTIONS.breadcrumbs),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function validateOptions({
|
export function validateOptions({
|
||||||
|
|
|
@ -38,6 +38,7 @@ declare module '@docusaurus/plugin-content-docs' {
|
||||||
showLastUpdateTime?: boolean;
|
showLastUpdateTime?: boolean;
|
||||||
showLastUpdateAuthor?: boolean;
|
showLastUpdateAuthor?: boolean;
|
||||||
numberPrefixParser: NumberPrefixParser;
|
numberPrefixParser: NumberPrefixParser;
|
||||||
|
breadcrumbs: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PathOptions = {
|
export type PathOptions = {
|
||||||
|
@ -126,6 +127,8 @@ declare module '@docusaurus/plugin-content-docs' {
|
||||||
export type PropSidebarItemCategory =
|
export type PropSidebarItemCategory =
|
||||||
import('./sidebars/types').PropSidebarItemCategory;
|
import('./sidebars/types').PropSidebarItemCategory;
|
||||||
export type PropSidebarItem = import('./sidebars/types').PropSidebarItem;
|
export type PropSidebarItem = import('./sidebars/types').PropSidebarItem;
|
||||||
|
export type PropSidebarBreadcrumbsItem =
|
||||||
|
import('./sidebars/types').PropSidebarBreadcrumbsItem;
|
||||||
export type PropSidebar = import('./sidebars/types').PropSidebar;
|
export type PropSidebar = import('./sidebars/types').PropSidebar;
|
||||||
export type PropSidebars = import('./sidebars/types').PropSidebars;
|
export type PropSidebars = import('./sidebars/types').PropSidebars;
|
||||||
|
|
||||||
|
@ -237,6 +240,10 @@ declare module '@theme/DocTagDocListPage' {
|
||||||
export default function DocTagDocListPage(props: Props): JSX.Element;
|
export default function DocTagDocListPage(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocBreadcrumbs' {
|
||||||
|
export default function DocBreadcrumbs(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage' {
|
declare module '@theme/DocPage' {
|
||||||
import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
|
import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
|
||||||
import type {DocumentRoute} from '@theme/DocItem';
|
import type {DocumentRoute} from '@theme/DocItem';
|
||||||
|
@ -294,6 +301,7 @@ declare module '@docusaurus/plugin-content-docs/client' {
|
||||||
export type GlobalPluginData = {
|
export type GlobalPluginData = {
|
||||||
path: string;
|
path: string;
|
||||||
versions: GlobalVersion[];
|
versions: GlobalVersion[];
|
||||||
|
breadcrumbs: boolean;
|
||||||
};
|
};
|
||||||
export type DocVersionSuggestions = {
|
export type DocVersionSuggestions = {
|
||||||
// suggest the latest version
|
// suggest the latest version
|
||||||
|
|
|
@ -195,6 +195,10 @@ export type PropSidebars = {
|
||||||
[sidebarId: string]: PropSidebar;
|
[sidebarId: string]: PropSidebar;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PropSidebarBreadcrumbsItem =
|
||||||
|
| PropSidebarItemLink
|
||||||
|
| PropSidebarItemCategory;
|
||||||
|
|
||||||
export type PropVersionDoc = {
|
export type PropVersionDoc = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/**
|
||||||
|
* 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, {type ReactNode} from 'react';
|
||||||
|
import {ThemeClassNames, useSidebarBreadcrumbs} from '@docusaurus/theme-common';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
|
|
||||||
|
// TODO move to design system folder
|
||||||
|
function BreadcrumbsItemLink({
|
||||||
|
children,
|
||||||
|
href,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
href?: string;
|
||||||
|
}): JSX.Element {
|
||||||
|
const className = clsx('breadcrumbs__link', styles.breadcrumbsItemLink);
|
||||||
|
return href ? (
|
||||||
|
<Link className={className} href={href}>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span className={className}>{children}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move to design system folder
|
||||||
|
function BreadcrumbsItem({
|
||||||
|
children,
|
||||||
|
active,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
active?: boolean;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className={clsx('breadcrumbs__item', {
|
||||||
|
'breadcrumbs__item--active': active,
|
||||||
|
})}>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function HomeBreadcrumbItem() {
|
||||||
|
const homeHref = useBaseUrl('/');
|
||||||
|
return (
|
||||||
|
<BreadcrumbsItem>
|
||||||
|
<BreadcrumbsItemLink href={homeHref}>🏠</BreadcrumbsItemLink>
|
||||||
|
</BreadcrumbsItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocBreadcrumbs(): JSX.Element | null {
|
||||||
|
const breadcrumbs = useSidebarBreadcrumbs();
|
||||||
|
|
||||||
|
if (!breadcrumbs) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
className={clsx(
|
||||||
|
ThemeClassNames.docs.docBreadcrumbs,
|
||||||
|
styles.breadcrumbsContainer,
|
||||||
|
)}
|
||||||
|
aria-label="breadcrumbs">
|
||||||
|
<ul className="breadcrumbs">
|
||||||
|
<HomeBreadcrumbItem />
|
||||||
|
{breadcrumbs.map((item, idx) => (
|
||||||
|
<BreadcrumbsItem key={idx} active={idx === breadcrumbs.length - 1}>
|
||||||
|
<BreadcrumbsItemLink href={item.href}>
|
||||||
|
{item.label}
|
||||||
|
</BreadcrumbsItemLink>
|
||||||
|
</BreadcrumbsItem>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.breadcrumbsContainer {
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbsItemLink {
|
||||||
|
--ifm-breadcrumb-size-multiplier: 0.7 !important;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
background: var(--ifm-color-gray-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] .breadcrumbsItemLink {
|
||||||
|
background-color: var(--ifm-color-gray-900);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 997px) {
|
||||||
|
.breadcrumbsItemLink {
|
||||||
|
--ifm-breadcrumb-size-multiplier: 0.8;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import DocPaginator from '@theme/DocPaginator';
|
||||||
import Seo from '@theme/Seo';
|
import Seo from '@theme/Seo';
|
||||||
import DocVersionBanner from '@theme/DocVersionBanner';
|
import DocVersionBanner from '@theme/DocVersionBanner';
|
||||||
import DocVersionBadge from '@theme/DocVersionBadge';
|
import DocVersionBadge from '@theme/DocVersionBadge';
|
||||||
|
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
|
||||||
import Heading from '@theme/Heading';
|
import Heading from '@theme/Heading';
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ export default function DocCategoryGeneratedIndexPage({
|
||||||
/>
|
/>
|
||||||
<div className={styles.generatedIndexPage}>
|
<div className={styles.generatedIndexPage}>
|
||||||
<DocVersionBanner />
|
<DocVersionBanner />
|
||||||
|
<DocBreadcrumbs />
|
||||||
<DocVersionBadge />
|
<DocVersionBadge />
|
||||||
<header>
|
<header>
|
||||||
<Heading as="h1" className={styles.title}>
|
<Heading as="h1" className={styles.title}>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import TOCCollapsible from '@theme/TOCCollapsible';
|
||||||
import Heading from '@theme/Heading';
|
import Heading from '@theme/Heading';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
import {ThemeClassNames, useWindowSize} from '@docusaurus/theme-common';
|
import {ThemeClassNames, useWindowSize} from '@docusaurus/theme-common';
|
||||||
|
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
|
||||||
|
|
||||||
export default function DocItem(props: Props): JSX.Element {
|
export default function DocItem(props: Props): JSX.Element {
|
||||||
const {content: DocContent} = props;
|
const {content: DocContent} = props;
|
||||||
|
@ -58,6 +59,7 @@ export default function DocItem(props: Props): JSX.Element {
|
||||||
<DocVersionBanner />
|
<DocVersionBanner />
|
||||||
<div className={styles.docItemContainer}>
|
<div className={styles.docItemContainer}>
|
||||||
<article>
|
<article>
|
||||||
|
<DocBreadcrumbs />
|
||||||
<DocVersionBadge />
|
<DocVersionBadge />
|
||||||
|
|
||||||
{canRenderTOC && (
|
{canRenderTOC && (
|
||||||
|
|
|
@ -49,6 +49,7 @@ export {
|
||||||
findFirstCategoryLink,
|
findFirstCategoryLink,
|
||||||
useCurrentSidebarCategory,
|
useCurrentSidebarCategory,
|
||||||
isActiveSidebarItem,
|
isActiveSidebarItem,
|
||||||
|
useSidebarBreadcrumbs,
|
||||||
} from './utils/docsUtils';
|
} from './utils/docsUtils';
|
||||||
|
|
||||||
export {isSamePath} from './utils/pathUtils';
|
export {isSamePath} from './utils/pathUtils';
|
||||||
|
|
|
@ -43,6 +43,7 @@ export const ThemeClassNames = {
|
||||||
docs: {
|
docs: {
|
||||||
docVersionBanner: 'theme-doc-version-banner',
|
docVersionBanner: 'theme-doc-version-banner',
|
||||||
docVersionBadge: 'theme-doc-version-badge',
|
docVersionBadge: 'theme-doc-version-badge',
|
||||||
|
docBreadcrumbs: 'theme-doc-breadcrumbs',
|
||||||
docMarkdown: 'theme-doc-markdown',
|
docMarkdown: 'theme-doc-markdown',
|
||||||
docTocMobile: 'theme-doc-toc-mobile',
|
docTocMobile: 'theme-doc-toc-mobile',
|
||||||
docTocDesktop: 'theme-doc-toc-desktop',
|
docTocDesktop: 'theme-doc-toc-desktop',
|
||||||
|
|
|
@ -16,11 +16,13 @@ import {
|
||||||
useDocsSidebar,
|
useDocsSidebar,
|
||||||
DocsSidebarProvider,
|
DocsSidebarProvider,
|
||||||
findSidebarCategory,
|
findSidebarCategory,
|
||||||
|
getBreadcrumbs,
|
||||||
} from '../docsUtils';
|
} from '../docsUtils';
|
||||||
import type {
|
import type {
|
||||||
PropSidebar,
|
PropSidebar,
|
||||||
PropSidebarItem,
|
PropSidebarItem,
|
||||||
PropSidebarItemCategory,
|
PropSidebarItemCategory,
|
||||||
|
PropSidebarItemLink,
|
||||||
PropVersionMetadata,
|
PropVersionMetadata,
|
||||||
} from '@docusaurus/plugin-content-docs';
|
} from '@docusaurus/plugin-content-docs';
|
||||||
|
|
||||||
|
@ -39,6 +41,15 @@ function testCategory(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testLink(data?: Partial<PropSidebarItemLink>): PropSidebarItemLink {
|
||||||
|
return {
|
||||||
|
type: 'link',
|
||||||
|
href: '/testLinkHref',
|
||||||
|
label: 'Link label',
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function testVersion(data?: Partial<PropVersionMetadata>): PropVersionMetadata {
|
function testVersion(data?: Partial<PropVersionMetadata>): PropVersionMetadata {
|
||||||
return {
|
return {
|
||||||
version: 'versionName',
|
version: 'versionName',
|
||||||
|
@ -330,4 +341,123 @@ describe('docsUtils', () => {
|
||||||
expect(isActiveSidebarItem(item, '/sub-sub-link-path/')).toEqual(true);
|
expect(isActiveSidebarItem(item, '/sub-sub-link-path/')).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getBreadcrumbs', () => {
|
||||||
|
test('should return empty for empty sidebar', () => {
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar: [],
|
||||||
|
pathname: '/doesNotExist',
|
||||||
|
}),
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return empty for sidebar but unknown pathname', () => {
|
||||||
|
const sidebar = [testCategory(), testLink()];
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname: '/doesNotExist',
|
||||||
|
}),
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return first level category', () => {
|
||||||
|
const pathname = '/somePathName';
|
||||||
|
const sidebar = [testCategory({href: pathname}), testLink()];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname,
|
||||||
|
}),
|
||||||
|
).toEqual([sidebar[0]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return first level link', () => {
|
||||||
|
const pathname = '/somePathName';
|
||||||
|
const sidebar = [testCategory(), testLink({href: pathname})];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname,
|
||||||
|
}),
|
||||||
|
).toEqual([sidebar[1]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return nested category', () => {
|
||||||
|
const pathname = '/somePathName';
|
||||||
|
|
||||||
|
const categoryLevel3 = testCategory({
|
||||||
|
href: pathname,
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLevel2 = testCategory({
|
||||||
|
items: [
|
||||||
|
testCategory(),
|
||||||
|
categoryLevel3,
|
||||||
|
testLink({href: pathname}),
|
||||||
|
testLink(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLevel1 = testCategory({
|
||||||
|
items: [testLink(), categoryLevel2],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sidebar = [
|
||||||
|
testLink(),
|
||||||
|
testCategory(),
|
||||||
|
categoryLevel1,
|
||||||
|
testLink(),
|
||||||
|
testCategory(),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname,
|
||||||
|
}),
|
||||||
|
).toEqual([categoryLevel1, categoryLevel2, categoryLevel3]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return nested link', () => {
|
||||||
|
const pathname = '/somePathName';
|
||||||
|
|
||||||
|
const link = testLink({href: pathname});
|
||||||
|
|
||||||
|
const categoryLevel3 = testCategory({
|
||||||
|
items: [testLink(), link, testLink()],
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLevel2 = testCategory({
|
||||||
|
items: [
|
||||||
|
testCategory(),
|
||||||
|
categoryLevel3,
|
||||||
|
testLink({href: pathname}),
|
||||||
|
testLink(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLevel1 = testCategory({
|
||||||
|
items: [testLink(), categoryLevel2],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sidebar = [
|
||||||
|
testLink(),
|
||||||
|
testCategory(),
|
||||||
|
categoryLevel1,
|
||||||
|
testLink(),
|
||||||
|
testCategory(),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname,
|
||||||
|
}),
|
||||||
|
).toEqual([categoryLevel1, categoryLevel2, categoryLevel3, link]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,13 +6,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createContext, type ReactNode, useContext} from 'react';
|
import React, {createContext, type ReactNode, useContext} from 'react';
|
||||||
import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
import {
|
||||||
|
useActivePlugin,
|
||||||
|
useAllDocsData,
|
||||||
|
} from '@docusaurus/plugin-content-docs/client';
|
||||||
import type {
|
import type {
|
||||||
PropSidebar,
|
PropSidebar,
|
||||||
PropSidebarItem,
|
PropSidebarItem,
|
||||||
PropSidebarItemCategory,
|
PropSidebarItemCategory,
|
||||||
PropVersionDoc,
|
PropVersionDoc,
|
||||||
PropVersionMetadata,
|
PropVersionMetadata,
|
||||||
|
PropSidebarBreadcrumbsItem,
|
||||||
} from '@docusaurus/plugin-content-docs';
|
} from '@docusaurus/plugin-content-docs';
|
||||||
import {isSamePath} from './pathUtils';
|
import {isSamePath} from './pathUtils';
|
||||||
import {useLocation} from '@docusaurus/router';
|
import {useLocation} from '@docusaurus/router';
|
||||||
|
@ -181,3 +185,46 @@ export function isActiveSidebarItem(
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getBreadcrumbs({
|
||||||
|
sidebar,
|
||||||
|
pathname,
|
||||||
|
}: {
|
||||||
|
sidebar: PropSidebar;
|
||||||
|
pathname: string;
|
||||||
|
}): PropSidebarBreadcrumbsItem[] {
|
||||||
|
const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
|
||||||
|
|
||||||
|
function extract(items: PropSidebar) {
|
||||||
|
for (const item of items) {
|
||||||
|
if (
|
||||||
|
item.type === 'category' &&
|
||||||
|
(isSamePath(item.href, pathname) || extract(item.items))
|
||||||
|
) {
|
||||||
|
breadcrumbs.push(item);
|
||||||
|
return true;
|
||||||
|
} else if (item.type === 'link' && isSamePath(item.href, pathname)) {
|
||||||
|
breadcrumbs.push(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
extract(sidebar);
|
||||||
|
|
||||||
|
return breadcrumbs.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null {
|
||||||
|
const sidebar = useDocsSidebar();
|
||||||
|
const {pathname} = useLocation();
|
||||||
|
const breadcrumbsOption = useActivePlugin()?.pluginData.breadcrumbs;
|
||||||
|
|
||||||
|
if (breadcrumbsOption === false || !sidebar) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBreadcrumbs({sidebar, pathname});
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ Accepted fields:
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `path` | `string` | `'docs'` | Path to data on filesystem relative to site dir. |
|
| `path` | `string` | `'docs'` | Path to data on filesystem relative to site dir. |
|
||||||
|
| `breadcrumbs` | `boolean` | `true` | To enable or disable the breadcrumbs on docs pages. |
|
||||||
| `editUrl` | <code>string \| EditUrlFunction</code> | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativeDocPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. |
|
| `editUrl` | <code>string \| EditUrlFunction</code> | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativeDocPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. |
|
||||||
| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. |
|
| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. |
|
||||||
| `editCurrentVersion` | `boolean` | `false` | The edit URL will always target the current version doc instead of older versions. Ignored when `editUrl` is a function. |
|
| `editCurrentVersion` | `boolean` | `false` | The edit URL will always target the current version doc instead of older versions. Ignored when `editUrl` is a function. |
|
||||||
|
@ -127,6 +128,7 @@ Most Docusaurus users configure this plugin through the preset options.
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
path: 'docs',
|
path: 'docs',
|
||||||
|
breadcrumbs: true,
|
||||||
// Simple use-case: string editUrl
|
// Simple use-case: string editUrl
|
||||||
// editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/',
|
// editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/',
|
||||||
// Advanced use-case: functional editUrl
|
// Advanced use-case: functional editUrl
|
||||||
|
|
|
@ -160,6 +160,28 @@ To pass in custom props to a swizzled sidebar item, add the optional `customProp
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Sidebar Breadcrumbs {#sidebar-breadcrumbs}
|
||||||
|
|
||||||
|
By default, breadcrumbs are rendered at the top, using the "sidebar path" of the current page.
|
||||||
|
|
||||||
|
This behavior can be disabled with plugin options:
|
||||||
|
|
||||||
|
```js title="docusaurus.config.js"
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@docusaurus/preset-classic',
|
||||||
|
{
|
||||||
|
docs: {
|
||||||
|
// highlight-next-line
|
||||||
|
breadcrumbs: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Complex sidebars example {#complex-sidebars-example}
|
## Complex sidebars example {#complex-sidebars-example}
|
||||||
|
|
||||||
A real-world example from the Docusaurus site:
|
A real-world example from the Docusaurus site:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue