@docusaurus/theme-common 2.0.0-beta.2 → 2.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Details.d.ts +14 -0
- package/lib/components/Collapsible/index.d.ts +64 -0
- package/lib/components/Collapsible/index.d.ts.map +1 -0
- package/lib/components/Collapsible/index.js +152 -0
- package/lib/components/Collapsible/index.js.map +1 -0
- package/lib/components/Details/index.d.ts +17 -0
- package/lib/components/Details/index.d.ts.map +1 -0
- package/lib/components/Details/index.js +71 -0
- package/lib/components/Details/index.js.map +1 -0
- package/lib/components/Details/styles.module.css +62 -0
- package/lib/contexts/announcementBar.d.ts +22 -0
- package/lib/contexts/announcementBar.d.ts.map +1 -0
- package/lib/{utils/announcementBarUtils.js → contexts/announcementBar.js} +30 -31
- package/lib/contexts/announcementBar.js.map +1 -0
- package/lib/contexts/colorMode.d.ts +27 -0
- package/lib/contexts/colorMode.d.ts.map +1 -0
- package/lib/contexts/colorMode.js +132 -0
- package/lib/contexts/colorMode.js.map +1 -0
- package/lib/contexts/docSidebarItemsExpandedState.d.ts +31 -0
- package/lib/contexts/docSidebarItemsExpandedState.d.ts.map +1 -0
- package/lib/contexts/docSidebarItemsExpandedState.js +28 -0
- package/lib/contexts/docSidebarItemsExpandedState.js.map +1 -0
- package/lib/contexts/docsPreferredVersion.d.ts +31 -0
- package/lib/contexts/docsPreferredVersion.d.ts.map +1 -0
- package/lib/contexts/docsPreferredVersion.js +128 -0
- package/lib/contexts/docsPreferredVersion.js.map +1 -0
- package/lib/contexts/docsSidebar.d.ts +26 -0
- package/lib/contexts/docsSidebar.d.ts.map +1 -0
- package/lib/contexts/docsSidebar.js +30 -0
- package/lib/contexts/docsSidebar.js.map +1 -0
- package/lib/contexts/docsVersion.d.ts +20 -0
- package/lib/contexts/docsVersion.d.ts.map +1 -0
- package/lib/contexts/docsVersion.js +26 -0
- package/lib/contexts/docsVersion.js.map +1 -0
- package/lib/contexts/navbarMobileSidebar.d.ts +31 -0
- package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -0
- package/lib/contexts/navbarMobileSidebar.js +56 -0
- package/lib/contexts/navbarMobileSidebar.js.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/content.d.ts +37 -0
- package/lib/contexts/navbarSecondaryMenu/content.d.ts.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/content.js +56 -0
- package/lib/contexts/navbarSecondaryMenu/content.js.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/display.d.ts +24 -0
- package/lib/contexts/navbarSecondaryMenu/display.d.ts.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/display.js +62 -0
- package/lib/contexts/navbarSecondaryMenu/display.js.map +1 -0
- package/lib/contexts/tabGroupChoice.d.ts +21 -0
- package/lib/contexts/tabGroupChoice.d.ts.map +1 -0
- package/lib/contexts/tabGroupChoice.js +49 -0
- package/lib/contexts/tabGroupChoice.js.map +1 -0
- package/lib/{utils/usePrevious.d.ts → hooks/styles.css} +4 -1
- package/lib/hooks/useBackToTopButton.d.ts +27 -0
- package/lib/hooks/useBackToTopButton.d.ts.map +1 -0
- package/lib/hooks/useBackToTopButton.js +50 -0
- package/lib/hooks/useBackToTopButton.js.map +1 -0
- package/lib/hooks/useCodeWordWrap.d.ts +14 -0
- package/lib/hooks/useCodeWordWrap.d.ts.map +1 -0
- package/lib/hooks/useCodeWordWrap.js +41 -0
- package/lib/hooks/useCodeWordWrap.js.map +1 -0
- package/lib/hooks/useHideableNavbar.d.ts +17 -0
- package/lib/hooks/useHideableNavbar.d.ts.map +1 -0
- package/lib/hooks/useHideableNavbar.js +60 -0
- package/lib/hooks/useHideableNavbar.js.map +1 -0
- package/lib/hooks/useKeyboardNavigation.d.ts +20 -0
- package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/lib/hooks/useKeyboardNavigation.js +39 -0
- package/lib/hooks/useKeyboardNavigation.js.map +1 -0
- package/lib/hooks/useLockBodyScroll.d.ts +12 -0
- package/lib/hooks/useLockBodyScroll.d.ts.map +1 -0
- package/lib/hooks/useLockBodyScroll.js +20 -0
- package/lib/hooks/useLockBodyScroll.js.map +1 -0
- package/lib/hooks/usePrismTheme.d.ts +13 -0
- package/lib/hooks/usePrismTheme.d.ts.map +1 -0
- package/lib/hooks/usePrismTheme.js +21 -0
- package/lib/hooks/usePrismTheme.js.map +1 -0
- package/lib/hooks/useSearchPage.d.ts +25 -0
- package/lib/hooks/useSearchPage.d.ts.map +1 -0
- package/lib/hooks/useSearchPage.js +43 -0
- package/lib/hooks/useSearchPage.js.map +1 -0
- package/lib/hooks/useSkipToContent.d.ts +25 -0
- package/lib/hooks/useSkipToContent.d.ts.map +1 -0
- package/lib/hooks/useSkipToContent.js +35 -0
- package/lib/hooks/useSkipToContent.js.map +1 -0
- package/lib/hooks/useTOCHighlight.d.ts +25 -0
- package/lib/hooks/useTOCHighlight.d.ts.map +1 -0
- package/lib/hooks/useTOCHighlight.js +130 -0
- package/lib/hooks/useTOCHighlight.js.map +1 -0
- package/lib/hooks/useWindowSize.d.ts +28 -0
- package/lib/hooks/useWindowSize.d.ts.map +1 -0
- package/lib/hooks/useWindowSize.js +59 -0
- package/lib/hooks/useWindowSize.js.map +1 -0
- package/lib/index.d.ts +38 -10
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +38 -9
- package/lib/index.js.map +1 -0
- package/lib/utils/ThemeClassNames.d.ts +47 -12
- package/lib/utils/ThemeClassNames.d.ts.map +1 -0
- package/lib/utils/ThemeClassNames.js +45 -4
- package/lib/utils/ThemeClassNames.js.map +1 -0
- package/lib/utils/codeBlockUtils.d.ts +63 -0
- package/lib/utils/codeBlockUtils.d.ts.map +1 -0
- package/lib/utils/codeBlockUtils.js +157 -3
- package/lib/utils/codeBlockUtils.js.map +1 -0
- package/lib/utils/docsUtils.d.ts +91 -0
- package/lib/utils/docsUtils.d.ts.map +1 -0
- package/lib/utils/docsUtils.js +217 -1
- package/lib/utils/docsUtils.js.map +1 -0
- package/lib/utils/footerUtils.d.ts +13 -0
- package/lib/utils/footerUtils.d.ts.map +1 -0
- package/lib/utils/footerUtils.js +14 -0
- package/lib/utils/footerUtils.js.map +1 -0
- package/lib/utils/generalUtils.d.ts +11 -1
- package/lib/utils/generalUtils.d.ts.map +1 -0
- package/lib/utils/generalUtils.js +9 -5
- package/lib/utils/generalUtils.js.map +1 -0
- package/lib/utils/historyUtils.d.ts +17 -0
- package/lib/utils/historyUtils.d.ts.map +1 -0
- package/lib/utils/historyUtils.js +38 -0
- package/lib/utils/historyUtils.js.map +1 -0
- package/lib/utils/jsUtils.d.ts +23 -0
- package/lib/utils/jsUtils.d.ts.map +1 -0
- package/lib/utils/jsUtils.js +29 -0
- package/lib/utils/jsUtils.js.map +1 -0
- package/lib/utils/metadataUtils.d.ts +38 -0
- package/lib/utils/metadataUtils.d.ts.map +1 -0
- package/lib/utils/metadataUtils.js +61 -0
- package/lib/utils/metadataUtils.js.map +1 -0
- package/lib/utils/navbarUtils.d.ts +21 -0
- package/lib/utils/navbarUtils.d.ts.map +1 -0
- package/lib/utils/navbarUtils.js +32 -0
- package/lib/utils/navbarUtils.js.map +1 -0
- package/lib/utils/reactUtils.d.ts +42 -0
- package/lib/utils/reactUtils.d.ts.map +1 -0
- package/lib/utils/reactUtils.js +64 -0
- package/lib/utils/reactUtils.js.map +1 -0
- package/lib/utils/regexpUtils.d.ts +12 -0
- package/lib/utils/regexpUtils.d.ts.map +1 -0
- package/lib/utils/regexpUtils.js +18 -0
- package/lib/utils/regexpUtils.js.map +1 -0
- package/lib/utils/routesUtils.d.ts +26 -0
- package/lib/utils/routesUtils.d.ts.map +1 -0
- package/lib/utils/routesUtils.js +54 -0
- package/lib/utils/routesUtils.js.map +1 -0
- package/lib/utils/scrollUtils.d.ts +83 -0
- package/lib/utils/scrollUtils.d.ts.map +1 -0
- package/lib/utils/scrollUtils.js +200 -0
- package/lib/utils/scrollUtils.js.map +1 -0
- package/lib/utils/searchUtils.d.ts +13 -0
- package/lib/utils/searchUtils.d.ts.map +1 -0
- package/lib/utils/searchUtils.js +37 -0
- package/lib/utils/searchUtils.js.map +1 -0
- package/lib/utils/storageUtils.d.ts +15 -7
- package/lib/utils/storageUtils.d.ts.map +1 -0
- package/lib/utils/storageUtils.js +55 -22
- package/lib/utils/storageUtils.js.map +1 -0
- package/lib/utils/tagsUtils.d.ts +18 -0
- package/lib/utils/tagsUtils.d.ts.map +1 -0
- package/lib/utils/tagsUtils.js +36 -0
- package/lib/utils/tagsUtils.js.map +1 -0
- package/lib/utils/tocUtils.d.ts +36 -0
- package/lib/utils/tocUtils.d.ts.map +1 -0
- package/lib/utils/tocUtils.js +84 -0
- package/lib/utils/tocUtils.js.map +1 -0
- package/lib/utils/useAlternatePageUtils.d.ts +21 -1
- package/lib/utils/useAlternatePageUtils.d.ts.map +1 -0
- package/lib/utils/useAlternatePageUtils.js +9 -4
- package/lib/utils/useAlternatePageUtils.js.map +1 -0
- package/lib/utils/useLocalPathname.d.ts +13 -0
- package/lib/utils/useLocalPathname.d.ts.map +1 -0
- package/lib/utils/useLocalPathname.js +19 -0
- package/lib/utils/useLocalPathname.js.map +1 -0
- package/lib/utils/useLocationChange.d.ts +9 -6
- package/lib/utils/useLocationChange.d.ts.map +1 -0
- package/lib/utils/useLocationChange.js +17 -11
- package/lib/utils/useLocationChange.js.map +1 -0
- package/lib/utils/usePluralForm.d.ts +12 -0
- package/lib/utils/usePluralForm.d.ts.map +1 -0
- package/lib/utils/usePluralForm.js +36 -37
- package/lib/utils/usePluralForm.js.map +1 -0
- package/lib/utils/useThemeConfig.d.ts +55 -24
- package/lib/utils/useThemeConfig.d.ts.map +1 -0
- package/lib/utils/useThemeConfig.js +4 -0
- package/lib/utils/useThemeConfig.js.map +1 -0
- package/package.json +26 -13
- package/src/components/Collapsible/index.tsx +265 -0
- package/src/components/Details/index.tsx +109 -0
- package/src/components/Details/styles.module.css +62 -0
- package/src/{utils/announcementBarUtils.tsx → contexts/announcementBar.tsx} +43 -39
- package/src/contexts/colorMode.tsx +199 -0
- package/src/contexts/docSidebarItemsExpandedState.tsx +55 -0
- package/src/contexts/docsPreferredVersion.tsx +253 -0
- package/src/contexts/docsSidebar.tsx +50 -0
- package/src/contexts/docsVersion.tsx +36 -0
- package/src/contexts/navbarMobileSidebar.tsx +99 -0
- package/src/contexts/navbarSecondaryMenu/content.tsx +110 -0
- package/src/contexts/navbarSecondaryMenu/display.tsx +102 -0
- package/src/contexts/tabGroupChoice.tsx +85 -0
- package/{lib/utils/pathUtils.d.ts → src/hooks/styles.css} +4 -1
- package/src/hooks/useBackToTopButton.ts +73 -0
- package/src/hooks/useCodeWordWrap.ts +56 -0
- package/src/hooks/useHideableNavbar.ts +75 -0
- package/src/hooks/useKeyboardNavigation.ts +45 -0
- package/src/hooks/useLockBodyScroll.ts +21 -0
- package/src/hooks/usePrismTheme.ts +24 -0
- package/src/hooks/useSearchPage.ts +79 -0
- package/src/hooks/useSkipToContent.ts +58 -0
- package/src/hooks/useTOCHighlight.ts +192 -0
- package/src/hooks/useWindowSize.ts +72 -0
- package/src/index.ts +130 -19
- package/src/types.d.ts +0 -2
- package/src/utils/ThemeClassNames.ts +51 -5
- package/src/utils/codeBlockUtils.ts +239 -2
- package/src/utils/docsUtils.tsx +334 -0
- package/src/utils/footerUtils.ts +18 -0
- package/src/utils/generalUtils.ts +9 -5
- package/src/utils/historyUtils.ts +45 -0
- package/src/utils/jsUtils.ts +36 -0
- package/src/utils/metadataUtils.tsx +115 -0
- package/src/utils/navbarUtils.tsx +45 -0
- package/src/utils/reactUtils.tsx +76 -0
- package/src/utils/regexpUtils.ts +24 -0
- package/src/utils/routesUtils.ts +75 -0
- package/src/utils/scrollUtils.tsx +308 -0
- package/src/utils/searchUtils.ts +51 -0
- package/src/utils/storageUtils.ts +56 -23
- package/src/utils/tagsUtils.ts +50 -0
- package/src/utils/tocUtils.ts +119 -0
- package/src/utils/useAlternatePageUtils.ts +19 -6
- package/src/utils/useLocalPathname.ts +22 -0
- package/src/utils/useLocationChange.ts +24 -20
- package/src/utils/usePluralForm.ts +46 -38
- package/src/utils/useThemeConfig.ts +54 -26
- package/lib/.tsbuildinfo +0 -1
- package/lib/utils/announcementBarInlineJavaScript.d.ts +0 -0
- package/lib/utils/announcementBarInlineJavaScript.js +0 -1
- package/lib/utils/announcementBarUtils.d.ts +0 -17
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts +0 -21
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js +0 -94
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts +0 -13
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js +0 -20
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts +0 -5
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js +0 -41
- package/lib/utils/pathUtils.js +0 -13
- package/lib/utils/usePrevious.js +0 -14
- package/src/utils/__tests__/codeBlockUtils.test.ts +0 -54
- package/src/utils/__tests__/pathUtils.test.ts +0 -32
- package/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +0 -165
- package/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts +0 -34
- package/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +0 -66
- package/src/utils/docsUtils.ts +0 -11
- package/src/utils/pathUtils.ts +0 -17
- package/src/utils/usePrevious.ts +0 -18
- package/tsconfig.json +0 -10
|
@@ -10,13 +10,13 @@ import React, {
|
|
|
10
10
|
useEffect,
|
|
11
11
|
useCallback,
|
|
12
12
|
useMemo,
|
|
13
|
-
ReactNode,
|
|
14
13
|
useContext,
|
|
15
|
-
|
|
14
|
+
type ReactNode,
|
|
16
15
|
} from 'react';
|
|
17
|
-
import
|
|
18
|
-
import {createStorageSlot} from '
|
|
19
|
-
import {
|
|
16
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
17
|
+
import {createStorageSlot} from '../utils/storageUtils';
|
|
18
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
19
|
+
import {useThemeConfig} from '../utils/useThemeConfig';
|
|
20
20
|
|
|
21
21
|
export const AnnouncementBarDismissStorageKey =
|
|
22
22
|
'docusaurus.announcement.dismiss';
|
|
@@ -32,22 +32,28 @@ const isDismissedInStorage = () =>
|
|
|
32
32
|
const setDismissedInStorage = (bool: boolean) =>
|
|
33
33
|
AnnouncementBarDismissStorage.set(String(bool));
|
|
34
34
|
|
|
35
|
-
type
|
|
36
|
-
|
|
35
|
+
type ContextValue = {
|
|
36
|
+
/** Whether the announcement bar should be displayed. */
|
|
37
|
+
readonly isActive: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Callback fired when the user closes the announcement. Will be saved.
|
|
40
|
+
*/
|
|
37
41
|
readonly close: () => void;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
|
-
const
|
|
44
|
+
const Context = React.createContext<ContextValue | null>(null);
|
|
45
|
+
|
|
46
|
+
function useContextValue(): ContextValue {
|
|
41
47
|
const {announcementBar} = useThemeConfig();
|
|
42
|
-
const
|
|
48
|
+
const isBrowser = useIsBrowser();
|
|
43
49
|
|
|
44
|
-
const [isClosed, setClosed] = useState(() =>
|
|
45
|
-
|
|
46
|
-
? // On client navigation: init with
|
|
50
|
+
const [isClosed, setClosed] = useState(() =>
|
|
51
|
+
isBrowser
|
|
52
|
+
? // On client navigation: init with local storage value
|
|
47
53
|
isDismissedInStorage()
|
|
48
54
|
: // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed)
|
|
49
|
-
false
|
|
50
|
-
|
|
55
|
+
false,
|
|
56
|
+
);
|
|
51
57
|
// Update state after hydration
|
|
52
58
|
useEffect(() => {
|
|
53
59
|
setClosed(isDismissedInStorage());
|
|
@@ -66,8 +72,9 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
|
|
66
72
|
|
|
67
73
|
let viewedId = IdStorage.get();
|
|
68
74
|
|
|
69
|
-
//
|
|
75
|
+
// Retrocompatibility due to spelling mistake of default id
|
|
70
76
|
// see https://github.com/facebook/docusaurus/issues/3338
|
|
77
|
+
// cSpell:ignore annoucement
|
|
71
78
|
if (viewedId === 'annoucement-bar') {
|
|
72
79
|
viewedId = 'announcement-bar';
|
|
73
80
|
}
|
|
@@ -83,33 +90,30 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
|
|
83
90
|
if (isNewAnnouncement || !isDismissedInStorage()) {
|
|
84
91
|
setClosed(false);
|
|
85
92
|
}
|
|
86
|
-
}, []);
|
|
93
|
+
}, [announcementBar]);
|
|
87
94
|
|
|
88
|
-
return useMemo(
|
|
89
|
-
|
|
90
|
-
isClosed,
|
|
95
|
+
return useMemo(
|
|
96
|
+
() => ({
|
|
97
|
+
isActive: !!announcementBar && !isClosed,
|
|
91
98
|
close: handleClose,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
|
|
97
|
-
|
|
98
|
-
export const AnnouncementBarProvider = ({children}: {children: ReactNode}) => {
|
|
99
|
-
const value = useAnnouncementBarContextValue();
|
|
100
|
-
return (
|
|
101
|
-
<AnnouncementBarContext.Provider value={value}>
|
|
102
|
-
{children}
|
|
103
|
-
</AnnouncementBarContext.Provider>
|
|
99
|
+
}),
|
|
100
|
+
[announcementBar, isClosed, handleClose],
|
|
104
101
|
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export
|
|
108
|
-
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function AnnouncementBarProvider({
|
|
105
|
+
children,
|
|
106
|
+
}: {
|
|
107
|
+
children: ReactNode;
|
|
108
|
+
}): JSX.Element {
|
|
109
|
+
const value = useContextValue();
|
|
110
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function useAnnouncementBar(): ContextValue {
|
|
114
|
+
const api = useContext(Context);
|
|
109
115
|
if (!api) {
|
|
110
|
-
throw new
|
|
111
|
-
'useAnnouncementBar(): AnnouncementBar not found in React context: make sure to use the AnnouncementBarProvider on top of the tree',
|
|
112
|
-
);
|
|
116
|
+
throw new ReactContextError('AnnouncementBarProvider');
|
|
113
117
|
}
|
|
114
118
|
return api;
|
|
115
|
-
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useState,
|
|
10
|
+
useCallback,
|
|
11
|
+
useEffect,
|
|
12
|
+
useContext,
|
|
13
|
+
useMemo,
|
|
14
|
+
useRef,
|
|
15
|
+
type ReactNode,
|
|
16
|
+
} from 'react';
|
|
17
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
18
|
+
|
|
19
|
+
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
20
|
+
import {createStorageSlot} from '../utils/storageUtils';
|
|
21
|
+
import {useThemeConfig} from '../utils/useThemeConfig';
|
|
22
|
+
|
|
23
|
+
type ContextValue = {
|
|
24
|
+
/** Current color mode. */
|
|
25
|
+
readonly colorMode: ColorMode;
|
|
26
|
+
/** Set new color mode. */
|
|
27
|
+
readonly setColorMode: (colorMode: ColorMode) => void;
|
|
28
|
+
|
|
29
|
+
// TODO legacy APIs kept for retro-compatibility: deprecate them
|
|
30
|
+
readonly isDarkTheme: boolean;
|
|
31
|
+
readonly setLightTheme: () => void;
|
|
32
|
+
readonly setDarkTheme: () => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const Context = React.createContext<ContextValue | undefined>(undefined);
|
|
36
|
+
|
|
37
|
+
const ColorModeStorageKey = 'theme';
|
|
38
|
+
const ColorModeStorage = createStorageSlot(ColorModeStorageKey);
|
|
39
|
+
|
|
40
|
+
const ColorModes = {
|
|
41
|
+
light: 'light',
|
|
42
|
+
dark: 'dark',
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
export type ColorMode = typeof ColorModes[keyof typeof ColorModes];
|
|
46
|
+
|
|
47
|
+
// Ensure to always return a valid colorMode even if input is invalid
|
|
48
|
+
const coerceToColorMode = (colorMode?: string | null): ColorMode =>
|
|
49
|
+
colorMode === ColorModes.dark ? ColorModes.dark : ColorModes.light;
|
|
50
|
+
|
|
51
|
+
const getInitialColorMode = (defaultMode: ColorMode | undefined): ColorMode =>
|
|
52
|
+
ExecutionEnvironment.canUseDOM
|
|
53
|
+
? coerceToColorMode(document.documentElement.getAttribute('data-theme'))
|
|
54
|
+
: coerceToColorMode(defaultMode);
|
|
55
|
+
|
|
56
|
+
const storeColorMode = (newColorMode: ColorMode) => {
|
|
57
|
+
ColorModeStorage.set(coerceToColorMode(newColorMode));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function useContextValue(): ContextValue {
|
|
61
|
+
const {
|
|
62
|
+
colorMode: {defaultMode, disableSwitch, respectPrefersColorScheme},
|
|
63
|
+
} = useThemeConfig();
|
|
64
|
+
const [colorMode, setColorModeState] = useState(
|
|
65
|
+
getInitialColorMode(defaultMode),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// A site is deployed without disableSwitch
|
|
70
|
+
// => User visits the site and has a persisted value
|
|
71
|
+
// => Site later enabled disableSwitch
|
|
72
|
+
// => Clear the previously stored value to apply the site's setting
|
|
73
|
+
if (disableSwitch) {
|
|
74
|
+
ColorModeStorage.del();
|
|
75
|
+
}
|
|
76
|
+
}, [disableSwitch]);
|
|
77
|
+
|
|
78
|
+
const setColorMode = useCallback(
|
|
79
|
+
(newColorMode: ColorMode | null, options: {persist?: boolean} = {}) => {
|
|
80
|
+
const {persist = true} = options;
|
|
81
|
+
if (newColorMode) {
|
|
82
|
+
setColorModeState(newColorMode);
|
|
83
|
+
if (persist) {
|
|
84
|
+
storeColorMode(newColorMode);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
if (respectPrefersColorScheme) {
|
|
88
|
+
setColorModeState(
|
|
89
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
90
|
+
? ColorModes.dark
|
|
91
|
+
: ColorModes.light,
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
setColorModeState(defaultMode);
|
|
95
|
+
}
|
|
96
|
+
ColorModeStorage.del();
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[respectPrefersColorScheme, defaultMode],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
document.documentElement.setAttribute(
|
|
104
|
+
'data-theme',
|
|
105
|
+
coerceToColorMode(colorMode),
|
|
106
|
+
);
|
|
107
|
+
}, [colorMode]);
|
|
108
|
+
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (disableSwitch) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const onChange = (e: StorageEvent) => {
|
|
114
|
+
if (e.key !== ColorModeStorageKey) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const storedColorMode = ColorModeStorage.get();
|
|
118
|
+
if (storedColorMode !== null) {
|
|
119
|
+
setColorMode(coerceToColorMode(storedColorMode));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
window.addEventListener('storage', onChange);
|
|
123
|
+
return () => window.removeEventListener('storage', onChange);
|
|
124
|
+
}, [disableSwitch, setColorMode]);
|
|
125
|
+
|
|
126
|
+
// PCS is coerced to light mode when printing, which causes the color mode to
|
|
127
|
+
// be reset to dark when exiting print mode, disregarding user settings. When
|
|
128
|
+
// the listener fires only because of a print/screen switch, we don't change
|
|
129
|
+
// color mode. See https://github.com/facebook/docusaurus/pull/6490
|
|
130
|
+
const previousMediaIsPrint = useRef(false);
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (disableSwitch && !respectPrefersColorScheme) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
137
|
+
const onChange = () => {
|
|
138
|
+
if (window.matchMedia('print').matches || previousMediaIsPrint.current) {
|
|
139
|
+
previousMediaIsPrint.current = window.matchMedia('print').matches;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
setColorMode(null);
|
|
143
|
+
};
|
|
144
|
+
mql.addListener(onChange);
|
|
145
|
+
return () => mql.removeListener(onChange);
|
|
146
|
+
}, [setColorMode, disableSwitch, respectPrefersColorScheme]);
|
|
147
|
+
|
|
148
|
+
return useMemo(
|
|
149
|
+
() => ({
|
|
150
|
+
colorMode,
|
|
151
|
+
setColorMode,
|
|
152
|
+
get isDarkTheme() {
|
|
153
|
+
if (process.env.NODE_ENV === 'development') {
|
|
154
|
+
console.error(
|
|
155
|
+
'`useColorMode().isDarkTheme` is deprecated. Please use `useColorMode().colorMode === "dark"` instead.',
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return colorMode === ColorModes.dark;
|
|
159
|
+
},
|
|
160
|
+
setLightTheme() {
|
|
161
|
+
if (process.env.NODE_ENV === 'development') {
|
|
162
|
+
console.error(
|
|
163
|
+
'`useColorMode().setLightTheme` is deprecated. Please use `useColorMode().setColorMode("light")` instead.',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
setColorMode(ColorModes.light);
|
|
167
|
+
},
|
|
168
|
+
setDarkTheme() {
|
|
169
|
+
if (process.env.NODE_ENV === 'development') {
|
|
170
|
+
console.error(
|
|
171
|
+
'`useColorMode().setDarkTheme` is deprecated. Please use `useColorMode().setColorMode("dark")` instead.',
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
setColorMode(ColorModes.dark);
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
[colorMode, setColorMode],
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function ColorModeProvider({
|
|
182
|
+
children,
|
|
183
|
+
}: {
|
|
184
|
+
children: ReactNode;
|
|
185
|
+
}): JSX.Element {
|
|
186
|
+
const value = useContextValue();
|
|
187
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function useColorMode(): ContextValue {
|
|
191
|
+
const context = useContext(Context);
|
|
192
|
+
if (context == null) {
|
|
193
|
+
throw new ReactContextError(
|
|
194
|
+
'ColorModeProvider',
|
|
195
|
+
'Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.',
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return context;
|
|
199
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {type ReactNode, useMemo, useState, useContext} from 'react';
|
|
9
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
10
|
+
|
|
11
|
+
type ContextValue = {
|
|
12
|
+
/**
|
|
13
|
+
* The item that the user last opened, `null` when there's none open. On
|
|
14
|
+
* initial render, it will always be `null`, which doesn't necessarily mean
|
|
15
|
+
* there's no category open (can have 0, 1, or many being initially open).
|
|
16
|
+
*/
|
|
17
|
+
expandedItem: number | null;
|
|
18
|
+
/**
|
|
19
|
+
* Set the currently expanded item, when the user opens one. Set the value to
|
|
20
|
+
* `null` when the user closes an open category.
|
|
21
|
+
*/
|
|
22
|
+
setExpandedItem: (a: number | null) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const EmptyContext: unique symbol = Symbol('EmptyContext');
|
|
26
|
+
const Context = React.createContext<ContextValue | typeof EmptyContext>(
|
|
27
|
+
EmptyContext,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Should be used to wrap one sidebar category level. This provider syncs the
|
|
32
|
+
* expanded states of all sibling categories, and categories can choose to
|
|
33
|
+
* collapse itself if another one is expanded.
|
|
34
|
+
*/
|
|
35
|
+
export function DocSidebarItemsExpandedStateProvider({
|
|
36
|
+
children,
|
|
37
|
+
}: {
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
}): JSX.Element {
|
|
40
|
+
const [expandedItem, setExpandedItem] = useState<number | null>(null);
|
|
41
|
+
const contextValue = useMemo(
|
|
42
|
+
() => ({expandedItem, setExpandedItem}),
|
|
43
|
+
[expandedItem],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function useDocSidebarItemsExpandedState(): ContextValue {
|
|
50
|
+
const value = useContext(Context);
|
|
51
|
+
if (value === EmptyContext) {
|
|
52
|
+
throw new ReactContextError('DocSidebarItemsExpandedStateProvider');
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useContext,
|
|
10
|
+
useEffect,
|
|
11
|
+
useMemo,
|
|
12
|
+
useState,
|
|
13
|
+
useCallback,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import {
|
|
17
|
+
useThemeConfig,
|
|
18
|
+
type DocsVersionPersistence,
|
|
19
|
+
} from '../utils/useThemeConfig';
|
|
20
|
+
import {isDocsPluginEnabled} from '../utils/docsUtils';
|
|
21
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
22
|
+
import {createStorageSlot} from '../utils/storageUtils';
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
useAllDocsData,
|
|
26
|
+
useDocsData,
|
|
27
|
+
type GlobalPluginData,
|
|
28
|
+
type GlobalVersion,
|
|
29
|
+
} from '@docusaurus/plugin-content-docs/client';
|
|
30
|
+
|
|
31
|
+
import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants';
|
|
32
|
+
|
|
33
|
+
const storageKey = (pluginId: string) => `docs-preferred-version-${pluginId}`;
|
|
34
|
+
|
|
35
|
+
const DocsPreferredVersionStorage = {
|
|
36
|
+
save: (
|
|
37
|
+
pluginId: string,
|
|
38
|
+
persistence: DocsVersionPersistence,
|
|
39
|
+
versionName: string,
|
|
40
|
+
): void => {
|
|
41
|
+
createStorageSlot(storageKey(pluginId), {persistence}).set(versionName);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
read: (
|
|
45
|
+
pluginId: string,
|
|
46
|
+
persistence: DocsVersionPersistence,
|
|
47
|
+
): string | null =>
|
|
48
|
+
createStorageSlot(storageKey(pluginId), {persistence}).get(),
|
|
49
|
+
|
|
50
|
+
clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
|
|
51
|
+
createStorageSlot(storageKey(pluginId), {persistence}).del();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type DocsPreferredVersionName = string | null;
|
|
56
|
+
|
|
57
|
+
/** State for a single docs plugin instance */
|
|
58
|
+
type DocsPreferredVersionPluginState = {
|
|
59
|
+
preferredVersionName: DocsPreferredVersionName;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* We need to store the state in storage globally, with one preferred version
|
|
64
|
+
* per docs plugin instance.
|
|
65
|
+
*/
|
|
66
|
+
type DocsPreferredVersionState = {
|
|
67
|
+
[pluginId: string]: DocsPreferredVersionPluginState;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Initial state is always null as we can't read local storage from node SSR
|
|
72
|
+
*/
|
|
73
|
+
const getInitialState = (pluginIds: string[]): DocsPreferredVersionState =>
|
|
74
|
+
Object.fromEntries(pluginIds.map((id) => [id, {preferredVersionName: null}]));
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Read storage for all docs plugins, assigning each doc plugin a preferred
|
|
78
|
+
* version (if found)
|
|
79
|
+
*/
|
|
80
|
+
function readStorageState({
|
|
81
|
+
pluginIds,
|
|
82
|
+
versionPersistence,
|
|
83
|
+
allDocsData,
|
|
84
|
+
}: {
|
|
85
|
+
pluginIds: string[];
|
|
86
|
+
versionPersistence: DocsVersionPersistence;
|
|
87
|
+
allDocsData: {[pluginId: string]: GlobalPluginData};
|
|
88
|
+
}): DocsPreferredVersionState {
|
|
89
|
+
/**
|
|
90
|
+
* The storage value we read might be stale, and belong to a version that does
|
|
91
|
+
* not exist in the site anymore. In such case, we remove the storage value to
|
|
92
|
+
* avoid downstream errors.
|
|
93
|
+
*/
|
|
94
|
+
function restorePluginState(
|
|
95
|
+
pluginId: string,
|
|
96
|
+
): DocsPreferredVersionPluginState {
|
|
97
|
+
const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read(
|
|
98
|
+
pluginId,
|
|
99
|
+
versionPersistence,
|
|
100
|
+
);
|
|
101
|
+
const pluginData = allDocsData[pluginId]!;
|
|
102
|
+
const versionExists = pluginData.versions.some(
|
|
103
|
+
(version) => version.name === preferredVersionNameUnsafe,
|
|
104
|
+
);
|
|
105
|
+
if (versionExists) {
|
|
106
|
+
return {preferredVersionName: preferredVersionNameUnsafe};
|
|
107
|
+
}
|
|
108
|
+
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
|
109
|
+
return {preferredVersionName: null};
|
|
110
|
+
}
|
|
111
|
+
return Object.fromEntries(
|
|
112
|
+
pluginIds.map((id) => [id, restorePluginState(id)]),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function useVersionPersistence(): DocsVersionPersistence {
|
|
117
|
+
return useThemeConfig().docs.versionPersistence;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type ContextValue = [
|
|
121
|
+
state: DocsPreferredVersionState,
|
|
122
|
+
api: {
|
|
123
|
+
savePreferredVersion: (pluginId: string, versionName: string) => void;
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const Context = React.createContext<ContextValue | null>(null);
|
|
128
|
+
|
|
129
|
+
function useContextValue(): ContextValue {
|
|
130
|
+
const allDocsData = useAllDocsData();
|
|
131
|
+
const versionPersistence = useVersionPersistence();
|
|
132
|
+
const pluginIds = useMemo(() => Object.keys(allDocsData), [allDocsData]);
|
|
133
|
+
|
|
134
|
+
// Initial state is empty, as we can't read browser storage in node/SSR
|
|
135
|
+
const [state, setState] = useState(() => getInitialState(pluginIds));
|
|
136
|
+
|
|
137
|
+
// On mount, we set the state read from browser storage
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
setState(readStorageState({allDocsData, versionPersistence, pluginIds}));
|
|
140
|
+
}, [allDocsData, versionPersistence, pluginIds]);
|
|
141
|
+
|
|
142
|
+
// The API that we expose to consumer hooks (memo for constant object)
|
|
143
|
+
const api = useMemo(() => {
|
|
144
|
+
function savePreferredVersion(pluginId: string, versionName: string) {
|
|
145
|
+
DocsPreferredVersionStorage.save(
|
|
146
|
+
pluginId,
|
|
147
|
+
versionPersistence,
|
|
148
|
+
versionName,
|
|
149
|
+
);
|
|
150
|
+
setState((s) => ({
|
|
151
|
+
...s,
|
|
152
|
+
[pluginId]: {preferredVersionName: versionName},
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
savePreferredVersion,
|
|
158
|
+
};
|
|
159
|
+
}, [versionPersistence]);
|
|
160
|
+
|
|
161
|
+
return [state, api];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function DocsPreferredVersionContextProviderUnsafe({
|
|
165
|
+
children,
|
|
166
|
+
}: {
|
|
167
|
+
children: ReactNode;
|
|
168
|
+
}): JSX.Element {
|
|
169
|
+
const value = useContextValue();
|
|
170
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* This is a maybe-layer. If the docs plugin is not enabled, this provider is a
|
|
175
|
+
* simple pass-through.
|
|
176
|
+
*/
|
|
177
|
+
export function DocsPreferredVersionContextProvider({
|
|
178
|
+
children,
|
|
179
|
+
}: {
|
|
180
|
+
children: JSX.Element;
|
|
181
|
+
}): JSX.Element {
|
|
182
|
+
if (isDocsPluginEnabled) {
|
|
183
|
+
return (
|
|
184
|
+
<DocsPreferredVersionContextProviderUnsafe>
|
|
185
|
+
{children}
|
|
186
|
+
</DocsPreferredVersionContextProviderUnsafe>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return children;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function useDocsPreferredVersionContext(): ContextValue {
|
|
193
|
+
const value = useContext(Context);
|
|
194
|
+
if (!value) {
|
|
195
|
+
throw new ReactContextError('DocsPreferredVersionContextProvider');
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Returns a read-write interface to a plugin's preferred version. The
|
|
202
|
+
* "preferred version" is defined as the last version that the user visited.
|
|
203
|
+
* For example, if a user is using v3, even when v4 is later published, the user
|
|
204
|
+
* would still be browsing v3 docs when she opens the website next time. Note,
|
|
205
|
+
* the `preferredVersion` attribute will always be `null` before mount.
|
|
206
|
+
*/
|
|
207
|
+
export function useDocsPreferredVersion(
|
|
208
|
+
pluginId: string | undefined = DEFAULT_PLUGIN_ID,
|
|
209
|
+
): {
|
|
210
|
+
preferredVersion: GlobalVersion | null;
|
|
211
|
+
savePreferredVersionName: (versionName: string) => void;
|
|
212
|
+
} {
|
|
213
|
+
const docsData = useDocsData(pluginId);
|
|
214
|
+
const [state, api] = useDocsPreferredVersionContext();
|
|
215
|
+
|
|
216
|
+
const {preferredVersionName} = state[pluginId]!;
|
|
217
|
+
|
|
218
|
+
const preferredVersion =
|
|
219
|
+
docsData.versions.find(
|
|
220
|
+
(version) => version.name === preferredVersionName,
|
|
221
|
+
) ?? null;
|
|
222
|
+
|
|
223
|
+
const savePreferredVersionName = useCallback(
|
|
224
|
+
(versionName: string) => {
|
|
225
|
+
api.savePreferredVersion(pluginId, versionName);
|
|
226
|
+
},
|
|
227
|
+
[api, pluginId],
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
return {preferredVersion, savePreferredVersionName};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function useDocsPreferredVersionByPluginId(): {
|
|
234
|
+
[pluginId: string]: GlobalVersion | null;
|
|
235
|
+
} {
|
|
236
|
+
const allDocsData = useAllDocsData();
|
|
237
|
+
const [state] = useDocsPreferredVersionContext();
|
|
238
|
+
|
|
239
|
+
function getPluginIdPreferredVersion(pluginId: string) {
|
|
240
|
+
const docsData = allDocsData[pluginId]!;
|
|
241
|
+
const {preferredVersionName} = state[pluginId]!;
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
docsData.versions.find(
|
|
245
|
+
(version) => version.name === preferredVersionName,
|
|
246
|
+
) ?? null
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const pluginIds = Object.keys(allDocsData);
|
|
250
|
+
return Object.fromEntries(
|
|
251
|
+
pluginIds.map((id) => [id, getPluginIdPreferredVersion(id)]),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {useMemo, useContext, type ReactNode} from 'react';
|
|
9
|
+
import type {PropSidebar} from '@docusaurus/plugin-content-docs';
|
|
10
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
11
|
+
|
|
12
|
+
// Using a Symbol because null is a valid context value (a doc with no sidebar)
|
|
13
|
+
// Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx
|
|
14
|
+
const EmptyContext: unique symbol = Symbol('EmptyContext');
|
|
15
|
+
|
|
16
|
+
type ContextValue = {name: string; items: PropSidebar};
|
|
17
|
+
|
|
18
|
+
const Context = React.createContext<ContextValue | null | typeof EmptyContext>(
|
|
19
|
+
EmptyContext,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Provide the current sidebar to your children.
|
|
24
|
+
*/
|
|
25
|
+
export function DocsSidebarProvider({
|
|
26
|
+
children,
|
|
27
|
+
name,
|
|
28
|
+
items,
|
|
29
|
+
}: {
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
name: string | undefined;
|
|
32
|
+
items: PropSidebar | undefined;
|
|
33
|
+
}): JSX.Element {
|
|
34
|
+
const stableValue: ContextValue | null = useMemo(
|
|
35
|
+
() => (name && items ? {name, items} : null),
|
|
36
|
+
[name, items],
|
|
37
|
+
);
|
|
38
|
+
return <Context.Provider value={stableValue}>{children}</Context.Provider>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets the sidebar that's currently displayed, or `null` if there isn't one
|
|
43
|
+
*/
|
|
44
|
+
export function useDocsSidebar(): ContextValue | null {
|
|
45
|
+
const value = useContext(Context);
|
|
46
|
+
if (value === EmptyContext) {
|
|
47
|
+
throw new ReactContextError('DocsSidebarProvider');
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|