refactor(theme-classic): extract doc-related navbar items' logic to theme-common (#7067)

This commit is contained in:
Joshua Chen 2022-03-30 14:50:04 +08:00 committed by GitHub
parent fd24bd180d
commit 13e7de853e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 135 deletions

View file

@ -198,8 +198,11 @@ function useDocsPreferredVersionContext(): ContextValue {
}
/**
* Returns a read-write interface to a plugin's preferred version.
* Note, the `preferredVersion` attribute will always be `null` before mount.
* Returns a read-write interface to a plugin's preferred version. The
* "preferred version" is defined as the last version that the user visited.
* For example, if a user is using v3, even when v4 is later published, the user
* would still be browsing v3 docs when she opens the website next time. Note,
* the `preferredVersion` attribute will always be `null` before mount.
*/
export function useDocsPreferredVersion(
pluginId: string | undefined = DEFAULT_PLUGIN_ID,

View file

@ -50,6 +50,9 @@ export {
useCurrentSidebarCategory,
isActiveSidebarItem,
useSidebarBreadcrumbs,
useDocsVersionCandidates,
useLayoutDoc,
useLayoutDocsSidebar,
} from './utils/docsUtils';
export {useTitleFormatter} from './utils/generalUtils';

View file

@ -5,9 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/
import {useMemo} from 'react';
import {
useAllDocsData,
useActivePlugin,
useActiveDocContext,
useLatestVersion,
type GlobalVersion,
type GlobalSidebar,
type GlobalDoc,
} from '@docusaurus/plugin-content-docs/client';
import type {
PropSidebar,
@ -16,8 +22,10 @@ import type {
PropVersionDoc,
PropSidebarBreadcrumbsItem,
} from '@docusaurus/plugin-content-docs';
import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion';
import {useDocsVersion} from '../contexts/docsVersion';
import {useDocsSidebar} from '../contexts/docsSidebar';
import {uniq} from './jsUtils';
import {isSamePath} from './routesUtils';
import {useLocation} from '@docusaurus/router';
@ -178,3 +186,91 @@ export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null {
return breadcrumbs.reverse();
}
/**
* "Version candidates" are mostly useful for the layout components, which must
* be able to work on all pages. For example, if a user has `{ type: "doc",
* docId: "intro" }` as a navbar item, which version does that refer to? We
* believe that it could refer to at most three version candidates:
*
* 1. The **active version**, the one that the user is currently browsing. See
* {@link useActiveDocContext}.
* 2. The **preferred version**, the one that the user last visited. See
* {@link useDocsPreferredVersion}.
* 3. The **latest version**, the "default". See {@link useLatestVersion}.
*
* @param docsPluginId The plugin ID to get versions from.
* @returns An array of 1~3 versions with priorities defined above, guaranteed
* to be unique and non-sparse. Will be memoized, hence stable for deps array.
*/
export function useDocsVersionCandidates(
docsPluginId?: string,
): [GlobalVersion, ...GlobalVersion[]] {
const {activeVersion} = useActiveDocContext(docsPluginId);
const {preferredVersion} = useDocsPreferredVersion(docsPluginId);
const latestVersion = useLatestVersion(docsPluginId);
return useMemo(
() =>
uniq(
[activeVersion, preferredVersion, latestVersion].filter(Boolean),
) as [GlobalVersion, ...GlobalVersion[]],
[activeVersion, preferredVersion, latestVersion],
);
}
/**
* The layout components, like navbar items, must be able to work on all pages,
* even on non-doc ones. This hook would always return a sidebar to be linked
* to. See also {@link useDocsVersionCandidates} for how this selection is done.
*
* @throws This hook throws if a sidebar with said ID is not found.
*/
export function useLayoutDocsSidebar(
sidebarId: string,
docsPluginId?: string,
): GlobalSidebar {
const versions = useDocsVersionCandidates(docsPluginId);
return useMemo(() => {
const allSidebars = versions.flatMap((version) =>
version.sidebars ? Object.entries(version.sidebars) : [],
);
const sidebarEntry = allSidebars.find(
(sidebar) => sidebar[0] === sidebarId,
);
if (!sidebarEntry) {
throw new Error(
`Can't find any sidebar with id "${sidebarId}" in version${
versions.length > 1 ? 's' : ''
} ${versions.map((version) => version.name).join(', ')}".
Available sidebar ids are:
- ${Object.keys(allSidebars).join('\n- ')}`,
);
}
return sidebarEntry[1];
}, [sidebarId, versions]);
}
/**
* The layout components, like navbar items, must be able to work on all pages,
* even on non-doc ones. This hook would always return a doc to be linked
* to. See also {@link useDocsVersionCandidates} for how this selection is done.
*
* @throws This hook throws if a doc with said ID is not found.
*/
export function useLayoutDoc(docId: string, docsPluginId?: string): GlobalDoc {
const versions = useDocsVersionCandidates(docsPluginId);
return useMemo(() => {
const allDocs = versions.flatMap((version) => version.docs);
const doc = allDocs.find((versionDoc) => versionDoc.id === docId);
if (!doc) {
throw new Error(
`DocNavbarItem: couldn't find any doc with id "${docId}" in version${
versions.length > 1 ? 's' : ''
} ${versions.map((version) => version.name).join(', ')}".
Available doc ids are:
- ${uniq(allDocs.map((versionDoc) => versionDoc.id)).join('\n- ')}`,
);
}
return doc;
}, [docId, versions]);
}

View file

@ -35,6 +35,8 @@ export function useContextualSearchFilters(): {locale: string; tags: string[]} {
const activePluginAndVersion = useActivePluginAndVersion();
const docsPreferredVersionByPluginId = useDocsPreferredVersionByPluginId();
// This can't use more specialized hooks because we are mapping over all
// plugin instances.
function getDocPluginTags(pluginId: string) {
const activeVersion =
activePluginAndVersion?.activePlugin?.pluginId === pluginId