mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-15 18:17:35 +02:00
feat(v2): persist docs preferred version (#3543)
* persist docs preferred version * add proper implementation for useDocsPreferredVersion * add proper implementation for useDocsPreferredVersion * useDocsPreferredVersion => make localstorage read only after mount * why @docusaurus/constants can't work? * fix weird TS issue when not duplicating constants * add basic @docusaurus/constants doc * attempt to fix docs-only mode where we should not call useDocs hooks * attempt to fix docs-only mode where we should not call useDocs hooks * fix children * encapsulate hacky isDocsPluginEnabled in docsUtils * use same priority order for all navbar items: activeVersion ?? preferredVersion ?? latestVersion
This commit is contained in:
parent
d1510770f4
commit
4130f1a195
15 changed files with 344 additions and 12 deletions
|
@ -21,10 +21,10 @@ import {
|
||||||
GetActivePluginOptions,
|
GetActivePluginOptions,
|
||||||
} from '../../client/docsClientUtils';
|
} from '../../client/docsClientUtils';
|
||||||
|
|
||||||
const useAllDocsData = (): Record<string, GlobalPluginData> =>
|
export const useAllDocsData = (): Record<string, GlobalPluginData> =>
|
||||||
useAllPluginInstancesData('docusaurus-plugin-content-docs');
|
useAllPluginInstancesData('docusaurus-plugin-content-docs');
|
||||||
|
|
||||||
const useDocsData = (pluginId: string | undefined) =>
|
export const useDocsData = (pluginId: string | undefined) =>
|
||||||
usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData;
|
usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData;
|
||||||
|
|
||||||
export const useActivePlugin = (options: GetActivePluginOptions = {}) => {
|
export const useActivePlugin = (options: GetActivePluginOptions = {}) => {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
useActiveVersion,
|
useActiveVersion,
|
||||||
useDocVersionSuggestions,
|
useDocVersionSuggestions,
|
||||||
} from '@theme/hooks/useDocs';
|
} from '@theme/hooks/useDocs';
|
||||||
|
import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion';
|
||||||
|
|
||||||
const getVersionMainDoc = (version) =>
|
const getVersionMainDoc = (version) =>
|
||||||
version.docs.find((doc) => doc.id === version.mainDocId);
|
version.docs.find((doc) => doc.id === version.mainDocId);
|
||||||
|
@ -22,6 +23,9 @@ function DocVersionSuggestions(): JSX.Element {
|
||||||
siteConfig: {title: siteTitle},
|
siteConfig: {title: siteTitle},
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
const {pluginId} = useActivePlugin({failfast: true});
|
const {pluginId} = useActivePlugin({failfast: true});
|
||||||
|
|
||||||
|
const {savePreferredVersionName} = useDocsPreferredVersion(pluginId);
|
||||||
|
|
||||||
const activeVersion = useActiveVersion(pluginId);
|
const activeVersion = useActiveVersion(pluginId);
|
||||||
const {
|
const {
|
||||||
latestDocSuggestion,
|
latestDocSuggestion,
|
||||||
|
@ -35,7 +39,7 @@ function DocVersionSuggestions(): JSX.Element {
|
||||||
|
|
||||||
// try to link to same doc in latest version (not always possible)
|
// try to link to same doc in latest version (not always possible)
|
||||||
// fallback to main doc of latest version
|
// fallback to main doc of latest version
|
||||||
const suggestedDoc =
|
const latestVersionSuggestedDoc =
|
||||||
latestDocSuggestion ?? getVersionMainDoc(latestVersionSuggestion);
|
latestDocSuggestion ?? getVersionMainDoc(latestVersionSuggestion);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -58,7 +62,13 @@ function DocVersionSuggestions(): JSX.Element {
|
||||||
<div className="margin-top--md">
|
<div className="margin-top--md">
|
||||||
For up-to-date documentation, see the{' '}
|
For up-to-date documentation, see the{' '}
|
||||||
<strong>
|
<strong>
|
||||||
<Link to={suggestedDoc.path}>latest version</Link>
|
<Link
|
||||||
|
to={latestVersionSuggestedDoc.path}
|
||||||
|
onClick={() =>
|
||||||
|
savePreferredVersionName(latestVersionSuggestion.name)
|
||||||
|
}>
|
||||||
|
latest version
|
||||||
|
</Link>
|
||||||
</strong>{' '}
|
</strong>{' '}
|
||||||
({latestVersionSuggestion.label}).
|
({latestVersionSuggestion.label}).
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,11 +19,16 @@ import Footer from '@theme/Footer';
|
||||||
import type {Props} from '@theme/Layout';
|
import type {Props} from '@theme/Layout';
|
||||||
|
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
import DocsPreferredVersionContextProvider from '../../utils/docsPreferredVersion/DocsPreferredVersionProvider';
|
||||||
|
|
||||||
function Providers({children}) {
|
function Providers({children}) {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<UserPreferencesProvider>{children}</UserPreferencesProvider>
|
<UserPreferencesProvider>
|
||||||
|
<DocsPreferredVersionContextProvider>
|
||||||
|
{children}
|
||||||
|
</DocsPreferredVersionContextProvider>
|
||||||
|
</UserPreferencesProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import DefaultNavbarItem from './DefaultNavbarItem';
|
||||||
import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs';
|
import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type {Props} from '@theme/NavbarItem/DocNavbarItem';
|
import type {Props} from '@theme/NavbarItem/DocNavbarItem';
|
||||||
|
import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion';
|
||||||
|
|
||||||
export default function DocNavbarItem({
|
export default function DocNavbarItem({
|
||||||
docId,
|
docId,
|
||||||
|
@ -18,10 +19,11 @@ export default function DocNavbarItem({
|
||||||
docsPluginId,
|
docsPluginId,
|
||||||
...props
|
...props
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const latestVersion = useLatestVersion(docsPluginId);
|
|
||||||
const {activeVersion, activeDoc} = useActiveDocContext(docsPluginId);
|
const {activeVersion, activeDoc} = useActiveDocContext(docsPluginId);
|
||||||
|
const {preferredVersion} = useDocsPreferredVersion(docsPluginId);
|
||||||
|
const latestVersion = useLatestVersion(docsPluginId);
|
||||||
|
|
||||||
const version = activeVersion ?? latestVersion;
|
const version = activeVersion ?? preferredVersion ?? latestVersion;
|
||||||
|
|
||||||
const doc = version.docs.find((versionDoc) => versionDoc.id === docId);
|
const doc = version.docs.find((versionDoc) => versionDoc.id === docId);
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
useActiveDocContext,
|
useActiveDocContext,
|
||||||
} from '@theme/hooks/useDocs';
|
} from '@theme/hooks/useDocs';
|
||||||
import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
|
import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
|
||||||
|
import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion';
|
||||||
|
|
||||||
const getVersionMainDoc = (version) =>
|
const getVersionMainDoc = (version) =>
|
||||||
version.docs.find((doc) => doc.id === version.mainDocId);
|
version.docs.find((doc) => doc.id === version.mainDocId);
|
||||||
|
@ -29,6 +30,10 @@ export default function DocsVersionDropdownNavbarItem({
|
||||||
const versions = useVersions(docsPluginId);
|
const versions = useVersions(docsPluginId);
|
||||||
const latestVersion = useLatestVersion(docsPluginId);
|
const latestVersion = useLatestVersion(docsPluginId);
|
||||||
|
|
||||||
|
const {preferredVersion, savePreferredVersionName} = useDocsPreferredVersion(
|
||||||
|
docsPluginId,
|
||||||
|
);
|
||||||
|
|
||||||
function getItems() {
|
function getItems() {
|
||||||
const versionLinks = versions.map((version) => {
|
const versionLinks = versions.map((version) => {
|
||||||
// We try to link to the same doc, in another version
|
// We try to link to the same doc, in another version
|
||||||
|
@ -41,6 +46,9 @@ export default function DocsVersionDropdownNavbarItem({
|
||||||
label: version.label,
|
label: version.label,
|
||||||
to: versionDoc.path,
|
to: versionDoc.path,
|
||||||
isActive: () => version === activeDocContext?.activeVersion,
|
isActive: () => version === activeDocContext?.activeVersion,
|
||||||
|
onClick: () => {
|
||||||
|
savePreferredVersionName(version.name);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,7 +68,8 @@ export default function DocsVersionDropdownNavbarItem({
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropdownVersion = activeDocContext.activeVersion ?? latestVersion;
|
const dropdownVersion =
|
||||||
|
activeDocContext.activeVersion ?? preferredVersion ?? latestVersion;
|
||||||
|
|
||||||
// Mobile is handled a bit differently
|
// Mobile is handled a bit differently
|
||||||
const dropdownLabel = mobile ? 'Versions' : dropdownVersion.label;
|
const dropdownLabel = mobile ? 'Versions' : dropdownVersion.label;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
||||||
import DefaultNavbarItem from './DefaultNavbarItem';
|
import DefaultNavbarItem from './DefaultNavbarItem';
|
||||||
import {useActiveVersion, useLatestVersion} from '@theme/hooks/useDocs';
|
import {useActiveVersion, useLatestVersion} from '@theme/hooks/useDocs';
|
||||||
import type {Props} from '@theme/NavbarItem/DocsVersionNavbarItem';
|
import type {Props} from '@theme/NavbarItem/DocsVersionNavbarItem';
|
||||||
|
import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion';
|
||||||
|
|
||||||
const getVersionMainDoc = (version) =>
|
const getVersionMainDoc = (version) =>
|
||||||
version.docs.find((doc) => doc.id === version.mainDocId);
|
version.docs.find((doc) => doc.id === version.mainDocId);
|
||||||
|
@ -20,8 +21,9 @@ export default function DocsVersionNavbarItem({
|
||||||
...props
|
...props
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const activeVersion = useActiveVersion(docsPluginId);
|
const activeVersion = useActiveVersion(docsPluginId);
|
||||||
|
const {preferredVersion} = useDocsPreferredVersion(docsPluginId);
|
||||||
const latestVersion = useLatestVersion(docsPluginId);
|
const latestVersion = useLatestVersion(docsPluginId);
|
||||||
const version = activeVersion ?? latestVersion;
|
const version = activeVersion ?? preferredVersion ?? latestVersion;
|
||||||
const label = staticLabel ?? version.label;
|
const label = staticLabel ?? version.label;
|
||||||
const path = staticTo ?? getVersionMainDoc(version).path;
|
const path = staticTo ?? getVersionMainDoc(version).path;
|
||||||
return <DefaultNavbarItem {...props} label={label} to={path} />;
|
return <DefaultNavbarItem {...props} label={label} to={path} />;
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* 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, {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import useThemeConfig, {DocsVersionPersistence} from '../useThemeConfig';
|
||||||
|
import {isDocsPluginEnabled} from '../docsUtils';
|
||||||
|
|
||||||
|
import {useAllDocsData} from '@theme/hooks/useDocs';
|
||||||
|
|
||||||
|
import DocsPreferredVersionStorage from './DocsPreferredVersionStorage';
|
||||||
|
|
||||||
|
type DocsPreferredVersionName = string | null;
|
||||||
|
|
||||||
|
// State for a single docs plugin instance
|
||||||
|
type DocsPreferredVersionPluginState = {
|
||||||
|
preferredVersionName: DocsPreferredVersionName;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need to store in state/storage globally
|
||||||
|
// one preferred version per docs plugin instance
|
||||||
|
// pluginId => pluginState
|
||||||
|
type DocsPreferredVersionState = Record<
|
||||||
|
string,
|
||||||
|
DocsPreferredVersionPluginState
|
||||||
|
>;
|
||||||
|
|
||||||
|
// Initial state is always null as we can't read localstorage from node SSR
|
||||||
|
function getInitialState(pluginIds: string[]): DocsPreferredVersionState {
|
||||||
|
const initialState: DocsPreferredVersionState = {};
|
||||||
|
pluginIds.forEach((pluginId) => {
|
||||||
|
initialState[pluginId] = {
|
||||||
|
preferredVersionName: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read storage for all docs plugins
|
||||||
|
// Assign to each doc plugin a preferred version (if found)
|
||||||
|
function readStorageState({
|
||||||
|
pluginIds,
|
||||||
|
versionPersistence,
|
||||||
|
allDocsData,
|
||||||
|
}: {
|
||||||
|
pluginIds: string[];
|
||||||
|
versionPersistence: DocsVersionPersistence;
|
||||||
|
allDocsData: any; // TODO find a way to type it :(
|
||||||
|
}): DocsPreferredVersionState {
|
||||||
|
// The storage value we read might be stale,
|
||||||
|
// and belong to a version that does not exist in the site anymore
|
||||||
|
// In such case, we remove the storage value to avoid downstream errors
|
||||||
|
function restorePluginState(
|
||||||
|
pluginId: string,
|
||||||
|
): DocsPreferredVersionPluginState {
|
||||||
|
const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read(
|
||||||
|
pluginId,
|
||||||
|
versionPersistence,
|
||||||
|
);
|
||||||
|
const pluginData = allDocsData[pluginId];
|
||||||
|
const versionExists = pluginData.versions.some(
|
||||||
|
(version) => version.name === preferredVersionNameUnsafe,
|
||||||
|
);
|
||||||
|
if (versionExists) {
|
||||||
|
return {preferredVersionName: preferredVersionNameUnsafe};
|
||||||
|
} else {
|
||||||
|
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
||||||
|
return {preferredVersionName: null};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: DocsPreferredVersionState = {};
|
||||||
|
pluginIds.forEach((pluginId) => {
|
||||||
|
initialState[pluginId] = restorePluginState(pluginId);
|
||||||
|
});
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useVersionPersistence(): DocsVersionPersistence {
|
||||||
|
return useThemeConfig().docs.versionPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value that will be accessible through context: [state,api]
|
||||||
|
function useContextValue() {
|
||||||
|
const allDocsData = useAllDocsData();
|
||||||
|
const versionPersistence = useVersionPersistence();
|
||||||
|
const pluginIds = useMemo(() => Object.keys(allDocsData), [allDocsData]);
|
||||||
|
|
||||||
|
// Initial state is empty, as we can't read browser storage in node/SSR
|
||||||
|
const [state, setState] = useState(() => getInitialState(pluginIds));
|
||||||
|
|
||||||
|
// On mount, we set the state read from browser storage
|
||||||
|
useEffect(() => {
|
||||||
|
setState(readStorageState({allDocsData, versionPersistence, pluginIds}));
|
||||||
|
}, [allDocsData, versionPersistence, pluginIds]);
|
||||||
|
|
||||||
|
// The API that we expose to consumer hooks (memo for constant object)
|
||||||
|
const api = useMemo(() => {
|
||||||
|
function savePreferredVersion(pluginId: string, versionName: string) {
|
||||||
|
DocsPreferredVersionStorage.save(
|
||||||
|
pluginId,
|
||||||
|
versionPersistence,
|
||||||
|
versionName,
|
||||||
|
);
|
||||||
|
setState((s) => ({
|
||||||
|
...s,
|
||||||
|
[pluginId]: {preferredVersionName: versionName},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
savePreferredVersion,
|
||||||
|
};
|
||||||
|
}, [setState]);
|
||||||
|
|
||||||
|
return [state, api] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocsPreferredVersionContextValue = ReturnType<typeof useContextValue>;
|
||||||
|
|
||||||
|
const Context = createContext<DocsPreferredVersionContextValue | null>(null);
|
||||||
|
|
||||||
|
export default function DocsPreferredVersionContextProvider({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
if (isDocsPluginEnabled) {
|
||||||
|
return (
|
||||||
|
<DocsPreferredVersionContextProviderUnsafe>
|
||||||
|
{children}
|
||||||
|
</DocsPreferredVersionContextProviderUnsafe>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocsPreferredVersionContextProviderUnsafe({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
const contextValue = useContextValue();
|
||||||
|
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDocsPreferredVersionContext(): DocsPreferredVersionContextValue {
|
||||||
|
const value = useContext(Context);
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(
|
||||||
|
"Can't find docs preferred context, maybe you forgot to use the DocsPreferredVersionContextProvider ?",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* 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 {DocsVersionPersistence} from '../useThemeConfig';
|
||||||
|
|
||||||
|
const storageKey = (pluginId: string) => `docs-preferred-version-${pluginId}`;
|
||||||
|
|
||||||
|
const DocsPreferredVersionStorage = {
|
||||||
|
save: (
|
||||||
|
pluginId: string,
|
||||||
|
persistence: DocsVersionPersistence,
|
||||||
|
versionName: string,
|
||||||
|
): void => {
|
||||||
|
if (persistence === 'none') {
|
||||||
|
// noop
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(storageKey(pluginId), versionName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
read: (
|
||||||
|
pluginId: string,
|
||||||
|
persistence: DocsVersionPersistence,
|
||||||
|
): string | null => {
|
||||||
|
if (persistence === 'none') {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return window.localStorage.getItem(storageKey(pluginId));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
|
||||||
|
if (persistence === 'none') {
|
||||||
|
// noop
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem(storageKey(pluginId));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocsPreferredVersionStorage;
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* 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 {useCallback} from 'react';
|
||||||
|
import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider';
|
||||||
|
import {useDocsData} from '@theme/hooks/useDocs';
|
||||||
|
|
||||||
|
import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants';
|
||||||
|
|
||||||
|
// Note, the preferredVersion attribute will always be null before mount
|
||||||
|
export default function useDocsPreferredVersion(
|
||||||
|
pluginId: string | undefined = DEFAULT_PLUGIN_ID,
|
||||||
|
) {
|
||||||
|
const docsData = useDocsData(pluginId);
|
||||||
|
const [state, api] = useDocsPreferredVersionContext();
|
||||||
|
|
||||||
|
const {preferredVersionName} = state[pluginId];
|
||||||
|
|
||||||
|
const preferredVersion = preferredVersionName
|
||||||
|
? docsData.versions.find((version) => version.name === preferredVersionName)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const savePreferredVersionName = useCallback(
|
||||||
|
(versionName: string) => {
|
||||||
|
api.savePreferredVersion(pluginId, versionName);
|
||||||
|
},
|
||||||
|
[api],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {preferredVersion, savePreferredVersionName} as const;
|
||||||
|
}
|
11
packages/docusaurus-theme-classic/src/utils/docsUtils.ts
Normal file
11
packages/docusaurus-theme-classic/src/utils/docsUtils.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* 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 {useAllDocsData} from '@theme/hooks/useDocs';
|
||||||
|
|
||||||
|
// TODO not ideal, see also "useDocs"
|
||||||
|
export const isDocsPluginEnabled: boolean = !!useAllDocsData;
|
|
@ -6,7 +6,13 @@
|
||||||
*/
|
*/
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
|
||||||
type ThemeConfig = {
|
export type DocsVersionPersistence = 'localStorage' | 'none';
|
||||||
|
|
||||||
|
export type ThemeConfig = {
|
||||||
|
docs: {
|
||||||
|
versionPersistence: DocsVersionPersistence;
|
||||||
|
};
|
||||||
|
|
||||||
// TODO we should complete this theme config type over time
|
// TODO we should complete this theme config type over time
|
||||||
// and share it across all themes
|
// and share it across all themes
|
||||||
// and use it in the Joi validation schema?
|
// and use it in the Joi validation schema?
|
||||||
|
|
|
@ -8,6 +8,15 @@
|
||||||
const Joi = require('@hapi/joi');
|
const Joi = require('@hapi/joi');
|
||||||
const {URISchema} = require('@docusaurus/utils-validation');
|
const {URISchema} = require('@docusaurus/utils-validation');
|
||||||
|
|
||||||
|
const DEFAULT_DOCS_CONFIG = {
|
||||||
|
versionPersistence: 'localStorage',
|
||||||
|
};
|
||||||
|
const DocsSchema = Joi.object({
|
||||||
|
versionPersistence: Joi.string()
|
||||||
|
.equal('localStorage', 'none')
|
||||||
|
.default(DEFAULT_DOCS_CONFIG.versionPersistence),
|
||||||
|
}).default(DEFAULT_DOCS_CONFIG);
|
||||||
|
|
||||||
const DEFAULT_COLOR_MODE_CONFIG = {
|
const DEFAULT_COLOR_MODE_CONFIG = {
|
||||||
defaultMode: 'light',
|
defaultMode: 'light',
|
||||||
disableSwitch: false,
|
disableSwitch: false,
|
||||||
|
@ -22,6 +31,7 @@ const DEFAULT_COLOR_MODE_CONFIG = {
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
||||||
|
docs: DEFAULT_DOCS_CONFIG,
|
||||||
metadatas: [],
|
metadatas: [],
|
||||||
prism: {
|
prism: {
|
||||||
additionalLanguages: [],
|
additionalLanguages: [],
|
||||||
|
@ -206,6 +216,7 @@ const ThemeConfigSchema = Joi.object({
|
||||||
customCss: CustomCssSchema,
|
customCss: CustomCssSchema,
|
||||||
colorMode: ColorModeSchema,
|
colorMode: ColorModeSchema,
|
||||||
image: Joi.string(),
|
image: Joi.string(),
|
||||||
|
docs: DocsSchema,
|
||||||
metadatas: Joi.array()
|
metadatas: Joi.array()
|
||||||
.items(HtmlMetadataSchema)
|
.items(HtmlMetadataSchema)
|
||||||
.default(DEFAULT_CONFIG.metadatas),
|
.default(DEFAULT_CONFIG.metadatas),
|
||||||
|
|
20
packages/docusaurus/src/client/exports/constants.ts
Normal file
20
packages/docusaurus/src/client/exports/constants.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
export {
|
||||||
|
// constants were only available on node
|
||||||
|
// this makes some useful constants available to frontend/themes too
|
||||||
|
// import {DEFAULT_PLUGIN_ID} '@docusaurus/constants'
|
||||||
|
DEFAULT_PLUGIN_ID,
|
||||||
|
} from '../../constants';
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Not duplicating the constants seems to produce
|
||||||
|
// weird TS compilation side-effects
|
||||||
|
export const DEFAULT_PLUGIN_ID = 'default';
|
|
@ -320,3 +320,15 @@ function MyPage() {
|
||||||
| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. |
|
| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. |
|
||||||
| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. |
|
| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. |
|
||||||
| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. |
|
| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. |
|
||||||
|
|
||||||
|
### `constants`
|
||||||
|
|
||||||
|
A module exposing useful constants to client-side theme code.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants';
|
||||||
|
```
|
||||||
|
|
||||||
|
| Named export | Value |
|
||||||
|
| ------------------- | --------- |
|
||||||
|
| `DEFAULT_PLUGIN_ID` | `default` |
|
||||||
|
|
|
@ -17689,7 +17689,7 @@ react-dev-utils@^9.1.0:
|
||||||
strip-ansi "5.2.0"
|
strip-ansi "5.2.0"
|
||||||
text-table "0.2.0"
|
text-table "0.2.0"
|
||||||
|
|
||||||
react-dom@^16.8.4:
|
react-dom@^16.10.2, react-dom@^16.8.4:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
|
||||||
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
|
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
|
||||||
|
@ -17869,7 +17869,7 @@ react-waypoint@^9.0.2:
|
||||||
prop-types "^15.0.0"
|
prop-types "^15.0.0"
|
||||||
react-is "^16.6.3"
|
react-is "^16.6.3"
|
||||||
|
|
||||||
react@^16.8.4:
|
react@^16.10.2, react@^16.8.4:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
|
||||||
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
|
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue