mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 16:17:25 +02:00
refactor(docs,theme): split DocItem comp, useDoc hook (#7644)
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
This commit is contained in:
parent
2316900174
commit
fd87afd249
17 changed files with 357 additions and 144 deletions
|
@ -8,7 +8,8 @@
|
||||||
/// <reference types="@docusaurus/module-type-aliases" />
|
/// <reference types="@docusaurus/module-type-aliases" />
|
||||||
|
|
||||||
declare module '@docusaurus/plugin-content-docs' {
|
declare module '@docusaurus/plugin-content-docs' {
|
||||||
import type {MDXOptions} from '@docusaurus/mdx-loader';
|
import type {MDXOptions, LoadedMDXContent} from '@docusaurus/mdx-loader';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ContentPaths,
|
ContentPaths,
|
||||||
FrontMatterTag,
|
FrontMatterTag,
|
||||||
|
@ -491,6 +492,12 @@ declare module '@docusaurus/plugin-content-docs' {
|
||||||
[docId: string]: PropVersionDoc;
|
[docId: string]: PropVersionDoc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PropDocContent = LoadedMDXContent<
|
||||||
|
DocFrontMatter,
|
||||||
|
DocMetadata,
|
||||||
|
Assets
|
||||||
|
>;
|
||||||
|
|
||||||
export type PropVersionMetadata = Pick<
|
export type PropVersionMetadata = Pick<
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
'label' | 'banner' | 'badge' | 'className' | 'isLast'
|
'label' | 'banner' | 'badge' | 'className' | 'isLast'
|
||||||
|
@ -549,13 +556,7 @@ declare module '@docusaurus/plugin-content-docs' {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocItem' {
|
declare module '@theme/DocItem' {
|
||||||
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
|
import type {PropDocContent} from '@docusaurus/plugin-content-docs';
|
||||||
import type {
|
|
||||||
PropVersionMetadata,
|
|
||||||
Assets,
|
|
||||||
DocMetadata,
|
|
||||||
DocFrontMatter,
|
|
||||||
} from '@docusaurus/plugin-content-docs';
|
|
||||||
|
|
||||||
export type DocumentRoute = {
|
export type DocumentRoute = {
|
||||||
readonly component: () => JSX.Element;
|
readonly component: () => JSX.Element;
|
||||||
|
@ -566,8 +567,7 @@ declare module '@theme/DocItem' {
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
readonly route: DocumentRoute;
|
readonly route: DocumentRoute;
|
||||||
readonly versionMetadata: PropVersionMetadata;
|
readonly content: PropDocContent;
|
||||||
readonly content: LoadedMDXContent<DocFrontMatter, DocMetadata, Assets>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocItem(props: Props): JSX.Element;
|
export default function DocItem(props: Props): JSX.Element;
|
||||||
|
|
|
@ -261,10 +261,40 @@ declare module '@theme/DocCardList' {
|
||||||
export default function DocCardList(props: Props): JSX.Element;
|
export default function DocCardList(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocItemFooter' {
|
declare module '@theme/DocItem/Layout' {
|
||||||
import type {Props} from '@theme/DocItem';
|
export interface Props {
|
||||||
|
readonly children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
export default function DocItemFooter(props: Props): JSX.Element;
|
export default function DocItemLayout(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/Metadata' {
|
||||||
|
export default function DocItemMetadata(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/Content' {
|
||||||
|
export interface Props {
|
||||||
|
readonly children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocItemContent(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/TOC/Mobile' {
|
||||||
|
export default function DocItemTOCMobile(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/TOC/Desktop' {
|
||||||
|
export default function DocItemTOCDesktop(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/Paginator' {
|
||||||
|
export default function DocItemPaginator(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocItem/Footer' {
|
||||||
|
export default function DocItemFooter(): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage/Layout' {
|
declare module '@theme/DocPage/Layout' {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import {ThemeClassNames, useDoc} from '@docusaurus/theme-common';
|
||||||
|
import Heading from '@theme/Heading';
|
||||||
|
import MDXContent from '@theme/MDXContent';
|
||||||
|
import type {Props} from '@theme/DocItem/Content';
|
||||||
|
|
||||||
|
/**
|
||||||
|
Title can be declared inside md content or declared through
|
||||||
|
front matter and added manually. To make both cases consistent,
|
||||||
|
the added title is added under the same div.markdown block
|
||||||
|
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
|
||||||
|
|
||||||
|
We render a "synthetic title" if:
|
||||||
|
- user doesn't ask to hide it with front matter
|
||||||
|
- the markdown content does not already contain a top-level h1 heading
|
||||||
|
*/
|
||||||
|
function useSyntheticTitle(): string | null {
|
||||||
|
const {metadata, frontMatter, contentTitle} = useDoc();
|
||||||
|
const shouldRender =
|
||||||
|
!frontMatter.hide_title && typeof contentTitle === 'undefined';
|
||||||
|
if (!shouldRender) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return metadata.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocItemContent({children}: Props): JSX.Element {
|
||||||
|
const syntheticTitle = useSyntheticTitle();
|
||||||
|
return (
|
||||||
|
<div className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
|
||||||
|
{syntheticTitle && (
|
||||||
|
<header>
|
||||||
|
<Heading as="h1">{syntheticTitle}</Heading>
|
||||||
|
</header>
|
||||||
|
)}
|
||||||
|
<MDXContent>{children}</MDXContent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,13 +7,16 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {ThemeClassNames} from '@docusaurus/theme-common';
|
import {
|
||||||
|
ThemeClassNames,
|
||||||
|
useDoc,
|
||||||
|
type DocContextValue,
|
||||||
|
} from '@docusaurus/theme-common';
|
||||||
import LastUpdated from '@theme/LastUpdated';
|
import LastUpdated from '@theme/LastUpdated';
|
||||||
import EditThisPage from '@theme/EditThisPage';
|
import EditThisPage from '@theme/EditThisPage';
|
||||||
import TagsListInline, {
|
import TagsListInline, {
|
||||||
type Props as TagsListInlineProps,
|
type Props as TagsListInlineProps,
|
||||||
} from '@theme/TagsListInline';
|
} from '@theme/TagsListInline';
|
||||||
import type {Props} from '@theme/DocItem';
|
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
@ -32,7 +35,7 @@ function TagsRow(props: TagsListInlineProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type EditMetaRowProps = Pick<
|
type EditMetaRowProps = Pick<
|
||||||
Props['content']['metadata'],
|
DocContextValue['metadata'],
|
||||||
'editUrl' | 'lastUpdatedAt' | 'lastUpdatedBy' | 'formattedLastUpdatedAt'
|
'editUrl' | 'lastUpdatedAt' | 'lastUpdatedBy' | 'formattedLastUpdatedAt'
|
||||||
>;
|
>;
|
||||||
function EditMetaRow({
|
function EditMetaRow({
|
||||||
|
@ -58,9 +61,8 @@ function EditMetaRow({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocItemFooter(props: Props): JSX.Element | null {
|
export default function DocItemFooter(): JSX.Element | null {
|
||||||
const {content: DocContent} = props;
|
const {metadata} = useDoc();
|
||||||
const {metadata} = DocContent;
|
|
||||||
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
|
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
|
||||||
metadata;
|
metadata;
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import {useWindowSize, useDoc} from '@docusaurus/theme-common';
|
||||||
|
import DocItemPaginator from '@theme/DocItem/Paginator';
|
||||||
|
import DocVersionBanner from '@theme/DocVersionBanner';
|
||||||
|
import DocVersionBadge from '@theme/DocVersionBadge';
|
||||||
|
import DocItemFooter from '@theme/DocItem/Footer';
|
||||||
|
import DocItemTOCMobile from '@theme/DocItem/TOC/Mobile';
|
||||||
|
import DocItemTOCDesktop from '@theme/DocItem/TOC/Desktop';
|
||||||
|
import DocItemContent from '@theme/DocItem/Content';
|
||||||
|
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
|
||||||
|
import type {Props} from '@theme/DocItem/Layout';
|
||||||
|
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide if the toc should be rendered, on mobile or desktop viewports
|
||||||
|
*/
|
||||||
|
function useDocTOC() {
|
||||||
|
const {frontMatter, toc} = useDoc();
|
||||||
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
|
const hidden = frontMatter.hide_table_of_contents;
|
||||||
|
const canRender = !hidden && toc.length > 0;
|
||||||
|
|
||||||
|
const mobile = canRender ? <DocItemTOCMobile /> : undefined;
|
||||||
|
|
||||||
|
const desktop =
|
||||||
|
canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? (
|
||||||
|
<DocItemTOCDesktop />
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hidden,
|
||||||
|
mobile,
|
||||||
|
desktop,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocItemLayout({children}: Props): JSX.Element {
|
||||||
|
const docTOC = useDocTOC();
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<div className={clsx('col', !docTOC.hidden && styles.docItemCol)}>
|
||||||
|
<DocVersionBanner />
|
||||||
|
<div className={styles.docItemContainer}>
|
||||||
|
<article>
|
||||||
|
<DocBreadcrumbs />
|
||||||
|
<DocVersionBadge />
|
||||||
|
{docTOC.mobile}
|
||||||
|
<DocItemContent>{children}</DocItemContent>
|
||||||
|
<DocItemFooter />
|
||||||
|
</article>
|
||||||
|
<DocItemPaginator />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{docTOC.desktop && <div className="col col--3">{docTOC.desktop}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.docItemContainer header + *,
|
||||||
|
.docItemContainer article > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 997px) {
|
||||||
|
.docItemCol {
|
||||||
|
max-width: 75% !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import {PageMetadata, useDoc} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
export default function DocItemMetadata(): JSX.Element {
|
||||||
|
const {metadata, frontMatter, assets} = useDoc();
|
||||||
|
return (
|
||||||
|
<PageMetadata
|
||||||
|
title={metadata.title}
|
||||||
|
description={metadata.description}
|
||||||
|
keywords={frontMatter.keywords}
|
||||||
|
image={assets.image ?? frontMatter.image}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import {useDoc} from '@docusaurus/theme-common';
|
||||||
|
import DocPaginator from '@theme/DocPaginator';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This extra component is needed, because <DocPaginator> should remain generic.
|
||||||
|
* DocPaginator is used in non-docs contexts too: generated-index pages...
|
||||||
|
*/
|
||||||
|
export default function DocItemPaginator(): JSX.Element {
|
||||||
|
const {metadata} = useDoc();
|
||||||
|
return <DocPaginator previous={metadata.previous} next={metadata.next} />;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import {ThemeClassNames, useDoc} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
import TOC from '@theme/TOC';
|
||||||
|
|
||||||
|
export default function DocItemTOCDesktop(): JSX.Element {
|
||||||
|
const {toc, frontMatter} = useDoc();
|
||||||
|
return (
|
||||||
|
<TOC
|
||||||
|
toc={toc}
|
||||||
|
minHeadingLevel={frontMatter.toc_min_heading_level}
|
||||||
|
maxHeadingLevel={frontMatter.toc_max_heading_level}
|
||||||
|
className={ThemeClassNames.docs.docTocDesktop}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import {ThemeClassNames, useDoc} from '@docusaurus/theme-common';
|
||||||
|
import TOCCollapsible from '@theme/TOCCollapsible';
|
||||||
|
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
export default function DocItemTOCMobile(): JSX.Element {
|
||||||
|
const {toc, frontMatter} = useDoc();
|
||||||
|
return (
|
||||||
|
<TOCCollapsible
|
||||||
|
toc={toc}
|
||||||
|
minHeadingLevel={frontMatter.toc_min_heading_level}
|
||||||
|
maxHeadingLevel={frontMatter.toc_max_heading_level}
|
||||||
|
className={clsx(ThemeClassNames.docs.docTocMobile, styles.tocMobile)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -5,16 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.docItemContainer header + *,
|
|
||||||
.docItemContainer article > *:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 997px) {
|
@media (min-width: 997px) {
|
||||||
.docItemCol {
|
|
||||||
max-width: 75% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prevent hydration FOUC, as the mobile TOC needs to be server-rendered */
|
/* Prevent hydration FOUC, as the mobile TOC needs to be server-rendered */
|
||||||
.tocMobile {
|
.tocMobile {
|
||||||
display: none;
|
display: none;
|
|
@ -6,124 +6,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import {HtmlClassNameProvider, DocProvider} from '@docusaurus/theme-common';
|
||||||
import {
|
import DocItemMetadata from '@theme/DocItem/Metadata';
|
||||||
PageMetadata,
|
import DocItemLayout from '@theme/DocItem/Layout';
|
||||||
HtmlClassNameProvider,
|
|
||||||
ThemeClassNames,
|
|
||||||
useWindowSize,
|
|
||||||
} from '@docusaurus/theme-common';
|
|
||||||
import DocPaginator from '@theme/DocPaginator';
|
|
||||||
import DocVersionBanner from '@theme/DocVersionBanner';
|
|
||||||
import DocVersionBadge from '@theme/DocVersionBadge';
|
|
||||||
import DocItemFooter from '@theme/DocItemFooter';
|
|
||||||
import TOC from '@theme/TOC';
|
|
||||||
import TOCCollapsible from '@theme/TOCCollapsible';
|
|
||||||
import Heading from '@theme/Heading';
|
|
||||||
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
|
|
||||||
import MDXContent from '@theme/MDXContent';
|
|
||||||
import type {Props} from '@theme/DocItem';
|
import type {Props} from '@theme/DocItem';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
|
||||||
|
|
||||||
function DocItemMetadata(props: Props): JSX.Element {
|
|
||||||
const {content: DocContent} = props;
|
|
||||||
const {metadata, frontMatter, assets} = DocContent;
|
|
||||||
const {keywords} = frontMatter;
|
|
||||||
const {description, title} = metadata;
|
|
||||||
const image = assets.image ?? frontMatter.image;
|
|
||||||
|
|
||||||
return <PageMetadata {...{title, description, keywords, image}} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DocItemContent(props: Props): JSX.Element {
|
|
||||||
const {content: DocContent} = props;
|
|
||||||
const {metadata, frontMatter} = DocContent;
|
|
||||||
const {
|
|
||||||
hide_title: hideTitle,
|
|
||||||
hide_table_of_contents: hideTableOfContents,
|
|
||||||
toc_min_heading_level: tocMinHeadingLevel,
|
|
||||||
toc_max_heading_level: tocMaxHeadingLevel,
|
|
||||||
} = frontMatter;
|
|
||||||
const {title} = metadata;
|
|
||||||
|
|
||||||
// We only add a title if:
|
|
||||||
// - user doesn't ask to hide it with front matter
|
|
||||||
// - the markdown content does not already contain a top-level h1 heading
|
|
||||||
const shouldAddTitle =
|
|
||||||
!hideTitle && typeof DocContent.contentTitle === 'undefined';
|
|
||||||
|
|
||||||
const windowSize = useWindowSize();
|
|
||||||
|
|
||||||
const canRenderTOC = !hideTableOfContents && DocContent.toc.length > 0;
|
|
||||||
|
|
||||||
const renderTocDesktop =
|
|
||||||
canRenderTOC && (windowSize === 'desktop' || windowSize === 'ssr');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="row">
|
|
||||||
<div className={clsx('col', !hideTableOfContents && styles.docItemCol)}>
|
|
||||||
<DocVersionBanner />
|
|
||||||
<div className={styles.docItemContainer}>
|
|
||||||
<article>
|
|
||||||
<DocBreadcrumbs />
|
|
||||||
<DocVersionBadge />
|
|
||||||
|
|
||||||
{canRenderTOC && (
|
|
||||||
<TOCCollapsible
|
|
||||||
toc={DocContent.toc}
|
|
||||||
minHeadingLevel={tocMinHeadingLevel}
|
|
||||||
maxHeadingLevel={tocMaxHeadingLevel}
|
|
||||||
className={clsx(
|
|
||||||
ThemeClassNames.docs.docTocMobile,
|
|
||||||
styles.tocMobile,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
|
|
||||||
{/*
|
|
||||||
Title can be declared inside md content or declared through
|
|
||||||
front matter and added manually. To make both cases consistent,
|
|
||||||
the added title is added under the same div.markdown block
|
|
||||||
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
|
|
||||||
*/}
|
|
||||||
{shouldAddTitle && (
|
|
||||||
<header>
|
|
||||||
<Heading as="h1">{title}</Heading>
|
|
||||||
</header>
|
|
||||||
)}
|
|
||||||
<MDXContent>
|
|
||||||
<DocContent />
|
|
||||||
</MDXContent>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DocItemFooter {...props} />
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<DocPaginator previous={metadata.previous} next={metadata.next} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{renderTocDesktop && (
|
|
||||||
<div className="col col--3">
|
|
||||||
<TOC
|
|
||||||
toc={DocContent.toc}
|
|
||||||
minHeadingLevel={tocMinHeadingLevel}
|
|
||||||
maxHeadingLevel={tocMaxHeadingLevel}
|
|
||||||
className={ThemeClassNames.docs.docTocDesktop}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DocItem(props: Props): JSX.Element {
|
export default function DocItem(props: Props): JSX.Element {
|
||||||
const docHtmlClassName = `docs-doc-id-${props.content.metadata.unversionedId}`;
|
const docHtmlClassName = `docs-doc-id-${props.content.metadata.unversionedId}`;
|
||||||
|
const MDXComponent = props.content;
|
||||||
return (
|
return (
|
||||||
|
<DocProvider content={props.content}>
|
||||||
<HtmlClassNameProvider className={docHtmlClassName}>
|
<HtmlClassNameProvider className={docHtmlClassName}>
|
||||||
<DocItemMetadata {...props} />
|
<DocItemMetadata />
|
||||||
<DocItemContent {...props} />
|
<DocItemLayout>
|
||||||
|
<MDXComponent />
|
||||||
|
</DocItemLayout>
|
||||||
</HtmlClassNameProvider>
|
</HtmlClassNameProvider>
|
||||||
|
</DocProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import type {Props} from '@theme/DocPaginator';
|
||||||
|
|
||||||
export default function DocPaginator(props: Props): JSX.Element {
|
export default function DocPaginator(props: Props): JSX.Element {
|
||||||
const {previous, next} = props;
|
const {previous, next} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className="pagination-nav docusaurus-mt-lg"
|
className="pagination-nav docusaurus-mt-lg"
|
||||||
|
|
72
packages/docusaurus-theme-common/src/contexts/doc.tsx
Normal file
72
packages/docusaurus-theme-common/src/contexts/doc.tsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* 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, type ReactNode, useContext} from 'react';
|
||||||
|
import {ReactContextError} from '../utils/reactUtils';
|
||||||
|
import type {PropDocContent} from '@docusaurus/plugin-content-docs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The React context value returned by the `useDoc()` hook.
|
||||||
|
* It contains useful data related to the currently browsed doc.
|
||||||
|
*/
|
||||||
|
export type DocContextValue = Pick<
|
||||||
|
PropDocContent,
|
||||||
|
'metadata' | 'frontMatter' | 'toc' | 'assets' | 'contentTitle'
|
||||||
|
>;
|
||||||
|
|
||||||
|
const Context = React.createContext<DocContextValue | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: we don't use `PropDoc` as context value on purpose. Metadata is
|
||||||
|
* currently stored inside the MDX component, but we may want to change that in
|
||||||
|
* the future. This layer is a good opportunity to decouple storage from
|
||||||
|
* consuming API, potentially allowing us to provide metadata as both props and
|
||||||
|
* route context without duplicating the chunks in the future.
|
||||||
|
*/
|
||||||
|
function useContextValue(content: PropDocContent): DocContextValue {
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
metadata: content.metadata,
|
||||||
|
frontMatter: content.frontMatter,
|
||||||
|
assets: content.assets,
|
||||||
|
contentTitle: content.contentTitle,
|
||||||
|
toc: content.toc,
|
||||||
|
}),
|
||||||
|
[content],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a very thin layer around the `content` received from the MDX loader.
|
||||||
|
* It provides the component to be rendered and other metadata about the doc to
|
||||||
|
* the children.
|
||||||
|
*/
|
||||||
|
export function DocProvider({
|
||||||
|
children,
|
||||||
|
content,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
content: PropDocContent;
|
||||||
|
}): JSX.Element {
|
||||||
|
const contextValue = useContextValue(content);
|
||||||
|
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data of the currently browsed doc. Gives access to the doc's MDX
|
||||||
|
* Component, front matter, metadata, TOC, etc. When swizzling a low-level
|
||||||
|
* component (e.g. the "Edit this page" link) and you need some extra metadata,
|
||||||
|
* you don't have to drill the props all the way through the component tree:
|
||||||
|
* simply use this hook instead.
|
||||||
|
*/
|
||||||
|
export function useDoc(): DocContextValue {
|
||||||
|
const doc = useContext(Context);
|
||||||
|
if (doc === null) {
|
||||||
|
throw new ReactContextError('DocProvider');
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ export {
|
||||||
} from './contexts/docSidebarItemsExpandedState';
|
} from './contexts/docSidebarItemsExpandedState';
|
||||||
export {DocsVersionProvider, useDocsVersion} from './contexts/docsVersion';
|
export {DocsVersionProvider, useDocsVersion} from './contexts/docsVersion';
|
||||||
export {DocsSidebarProvider, useDocsSidebar} from './contexts/docsSidebar';
|
export {DocsSidebarProvider, useDocsSidebar} from './contexts/docsSidebar';
|
||||||
|
export {DocProvider, useDoc} from './contexts/doc';
|
||||||
|
export type {DocContextValue} from './contexts/doc';
|
||||||
|
|
||||||
export {createStorageSlot, listStorageKeys} from './utils/storageUtils';
|
export {createStorageSlot, listStorageKeys} from './utils/storageUtils';
|
||||||
|
|
||||||
|
|
|
@ -324,7 +324,7 @@ export function useDocRouteMetadata({
|
||||||
? versionMetadata.docsSidebars[sidebarName]
|
? versionMetadata.docsSidebars[sidebarName]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const docElement = renderRoutes(docRoutes, {versionMetadata});
|
const docElement = renderRoutes(docRoutes);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
docElement,
|
docElement,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue