@docusaurus/theme-common 2.0.0-beta.15d451942 → 2.0.0-beta.16
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/lib/components/Collapsible/index.d.ts +36 -0
- package/lib/components/Collapsible/index.d.ts.map +1 -0
- package/lib/components/Collapsible/index.js +144 -0
- package/lib/components/Collapsible/index.js.map +1 -0
- package/lib/components/Details/index.d.ts +12 -0
- package/lib/components/Details/index.d.ts.map +1 -0
- package/lib/components/Details/index.js +66 -0
- package/lib/components/Details/index.js.map +1 -0
- package/lib/components/Details/styles.module.css +58 -0
- package/lib/{utils/useChangeRoute.d.ts → hooks/styles.css} +4 -1
- package/lib/hooks/useHideableNavbar.d.ts +13 -0
- package/lib/hooks/useHideableNavbar.d.ts.map +1 -0
- package/lib/hooks/useHideableNavbar.js +59 -0
- package/lib/hooks/useHideableNavbar.js.map +1 -0
- package/lib/hooks/useKeyboardNavigation.d.ts +10 -0
- package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/lib/hooks/useKeyboardNavigation.js +31 -0
- package/lib/hooks/useKeyboardNavigation.js.map +1 -0
- package/{src/utils/docsUtils.ts → lib/hooks/useLockBodyScroll.d.ts} +2 -5
- package/lib/hooks/useLockBodyScroll.d.ts.map +1 -0
- package/lib/hooks/useLockBodyScroll.js +16 -0
- package/lib/hooks/useLockBodyScroll.js.map +1 -0
- package/lib/hooks/usePrismTheme.d.ts +9 -0
- package/lib/hooks/usePrismTheme.d.ts.map +1 -0
- package/lib/hooks/usePrismTheme.js +18 -0
- package/lib/hooks/usePrismTheme.js.map +1 -0
- package/lib/hooks/useSearchPage.d.ts +14 -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/useWindowSize.d.ts +15 -0
- package/lib/hooks/useWindowSize.d.ts.map +1 -0
- package/lib/hooks/useWindowSize.js +56 -0
- package/lib/hooks/useWindowSize.js.map +1 -0
- package/lib/index.d.ts +35 -4
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +29 -3
- package/lib/index.js.map +1 -0
- package/lib/utils/ThemeClassNames.d.ts +40 -12
- package/lib/utils/ThemeClassNames.d.ts.map +1 -0
- package/lib/utils/ThemeClassNames.js +41 -3
- package/lib/utils/ThemeClassNames.js.map +1 -0
- package/lib/utils/announcementBarUtils.d.ts +18 -0
- package/lib/utils/announcementBarUtils.d.ts.map +1 -0
- package/lib/utils/announcementBarUtils.js +71 -0
- package/lib/utils/announcementBarUtils.js.map +1 -0
- package/lib/utils/codeBlockUtils.d.ts +10 -0
- package/lib/utils/codeBlockUtils.d.ts.map +1 -0
- package/lib/utils/codeBlockUtils.js +125 -2
- package/lib/utils/codeBlockUtils.js.map +1 -0
- package/lib/utils/colorModeUtils.d.ts +18 -0
- package/lib/utils/colorModeUtils.d.ts.map +1 -0
- package/lib/utils/colorModeUtils.js +106 -0
- package/lib/utils/colorModeUtils.js.map +1 -0
- package/lib/utils/docSidebarItemsExpandedState.d.ts +17 -0
- package/lib/utils/docSidebarItemsExpandedState.d.ts.map +1 -0
- package/lib/utils/docSidebarItemsExpandedState.js +22 -0
- package/lib/utils/docSidebarItemsExpandedState.js.map +1 -0
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts +3 -2
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts.map +1 -0
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js +8 -11
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js.map +1 -0
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts +2 -1
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts.map +1 -0
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js +2 -3
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js.map +1 -0
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts +12 -3
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts.map +1 -0
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js +3 -3
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js.map +1 -0
- package/lib/utils/docsUtils.d.ts +26 -0
- package/lib/utils/docsUtils.d.ts.map +1 -0
- package/lib/utils/docsUtils.js +136 -1
- package/lib/utils/docsUtils.js.map +1 -0
- package/lib/utils/generalUtils.d.ts +7 -0
- package/lib/utils/generalUtils.d.ts.map +1 -0
- package/lib/utils/generalUtils.js +3 -2
- package/lib/utils/generalUtils.js.map +1 -0
- package/lib/utils/historyUtils.d.ts +23 -0
- package/lib/utils/historyUtils.d.ts.map +1 -0
- package/lib/utils/historyUtils.js +41 -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/mobileSecondaryMenu.d.ts +21 -0
- package/lib/utils/mobileSecondaryMenu.d.ts.map +1 -0
- package/lib/utils/mobileSecondaryMenu.js +50 -0
- package/lib/utils/mobileSecondaryMenu.js.map +1 -0
- package/lib/utils/pathUtils.d.ts +1 -0
- package/lib/utils/pathUtils.d.ts.map +1 -0
- package/lib/utils/pathUtils.js +6 -2
- package/lib/utils/pathUtils.js.map +1 -0
- package/lib/utils/reactUtils.d.ts +28 -0
- package/lib/utils/reactUtils.d.ts.map +1 -0
- package/lib/utils/reactUtils.js +36 -0
- package/lib/utils/reactUtils.js.map +1 -0
- package/lib/utils/regexpUtils.d.ts +11 -0
- package/lib/utils/regexpUtils.d.ts.map +1 -0
- package/lib/utils/regexpUtils.js +17 -0
- package/lib/utils/regexpUtils.js.map +1 -0
- package/lib/utils/routesUtils.d.ts +11 -0
- package/lib/utils/routesUtils.d.ts.map +1 -0
- package/lib/utils/routesUtils.js +33 -0
- package/lib/utils/routesUtils.js.map +1 -0
- package/lib/utils/scrollUtils.d.ts +53 -0
- package/lib/utils/scrollUtils.d.ts.map +1 -0
- package/lib/utils/scrollUtils.js +136 -0
- package/lib/utils/scrollUtils.js.map +1 -0
- package/lib/utils/searchUtils.d.ts +1 -0
- package/lib/utils/searchUtils.d.ts.map +1 -0
- package/lib/utils/searchUtils.js +1 -0
- package/lib/utils/searchUtils.js.map +1 -0
- package/lib/utils/storageUtils.d.ts +5 -0
- package/lib/utils/storageUtils.d.ts.map +1 -0
- package/lib/utils/storageUtils.js +41 -16
- package/lib/utils/storageUtils.js.map +1 -0
- package/lib/utils/tabGroupChoiceUtils.d.ts +19 -0
- package/lib/utils/tabGroupChoiceUtils.d.ts.map +1 -0
- package/lib/utils/tabGroupChoiceUtils.js +54 -0
- package/lib/utils/tabGroupChoiceUtils.js.map +1 -0
- package/lib/utils/tagsUtils.d.ts +19 -0
- package/lib/utils/tagsUtils.d.ts.map +1 -0
- package/lib/utils/tagsUtils.js +34 -0
- package/lib/utils/tagsUtils.js.map +1 -0
- package/lib/utils/tocUtils.d.ts +20 -0
- package/lib/utils/tocUtils.d.ts.map +1 -0
- package/lib/utils/tocUtils.js +73 -0
- package/lib/utils/tocUtils.js.map +1 -0
- package/lib/utils/useAlternatePageUtils.d.ts +1 -0
- package/lib/utils/useAlternatePageUtils.d.ts.map +1 -0
- package/lib/utils/useAlternatePageUtils.js +3 -1
- package/lib/utils/useAlternatePageUtils.js.map +1 -0
- package/lib/utils/useContextualSearchFilters.d.ts +12 -0
- package/lib/utils/useContextualSearchFilters.d.ts.map +1 -0
- package/lib/utils/useContextualSearchFilters.js +37 -0
- package/lib/utils/useContextualSearchFilters.js.map +1 -0
- package/lib/utils/useLocalPathname.d.ts +8 -0
- package/lib/utils/useLocalPathname.d.ts.map +1 -0
- package/lib/utils/useLocalPathname.js +17 -0
- package/lib/utils/useLocalPathname.js.map +1 -0
- package/lib/utils/useLocationChange.d.ts +15 -0
- package/lib/utils/useLocationChange.d.ts.map +1 -0
- package/lib/utils/useLocationChange.js +27 -0
- package/lib/utils/useLocationChange.js.map +1 -0
- package/lib/utils/usePluralForm.d.ts +1 -0
- package/lib/utils/usePluralForm.d.ts.map +1 -0
- package/lib/utils/usePluralForm.js +29 -25
- package/lib/utils/usePluralForm.js.map +1 -0
- package/lib/utils/usePrevious.d.ts +8 -0
- package/lib/utils/usePrevious.d.ts.map +1 -0
- package/lib/utils/usePrevious.js +16 -0
- package/lib/utils/usePrevious.js.map +1 -0
- package/lib/utils/useTOCHighlight.d.ts +14 -0
- package/lib/utils/useTOCHighlight.d.ts.map +1 -0
- package/lib/utils/useTOCHighlight.js +128 -0
- package/lib/utils/useTOCHighlight.js.map +1 -0
- package/lib/utils/useThemeConfig.d.ts +36 -11
- package/lib/utils/useThemeConfig.d.ts.map +1 -0
- package/lib/utils/useThemeConfig.js +1 -0
- package/lib/utils/useThemeConfig.js.map +1 -0
- package/package.json +19 -13
- package/src/components/Collapsible/index.tsx +247 -0
- package/src/components/Details/index.tsx +102 -0
- package/src/components/Details/styles.module.css +58 -0
- package/src/hooks/styles.css +10 -0
- package/src/hooks/useHideableNavbar.ts +77 -0
- package/src/hooks/useKeyboardNavigation.ts +37 -0
- package/src/hooks/useLockBodyScroll.ts +18 -0
- package/src/hooks/usePrismTheme.ts +20 -0
- package/src/hooks/useSearchPage.ts +66 -0
- package/src/hooks/useWindowSize.ts +70 -0
- package/src/index.ts +102 -4
- package/src/types.d.ts +0 -2
- package/src/utils/ThemeClassNames.ts +46 -4
- package/src/utils/announcementBarUtils.tsx +121 -0
- package/src/utils/codeBlockUtils.ts +153 -2
- package/src/utils/colorModeUtils.tsx +156 -0
- package/src/utils/docSidebarItemsExpandedState.tsx +41 -0
- package/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +16 -14
- package/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts +3 -4
- package/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +18 -14
- package/src/utils/docsUtils.tsx +230 -0
- package/src/utils/generalUtils.ts +3 -2
- package/src/utils/historyUtils.ts +51 -0
- package/src/utils/jsUtils.ts +36 -0
- package/src/utils/mobileSecondaryMenu.tsx +115 -0
- package/src/utils/pathUtils.ts +6 -4
- package/src/utils/reactUtils.tsx +43 -0
- package/src/utils/regexpUtils.ts +23 -0
- package/src/utils/routesUtils.ts +39 -0
- package/src/utils/scrollUtils.tsx +237 -0
- package/src/utils/storageUtils.ts +40 -15
- package/src/utils/tabGroupChoiceUtils.tsx +90 -0
- package/src/utils/tagsUtils.ts +48 -0
- package/src/utils/tocUtils.ts +108 -0
- package/src/utils/useAlternatePageUtils.ts +13 -4
- package/src/utils/useContextualSearchFilters.ts +53 -0
- package/src/utils/useLocalPathname.ts +20 -0
- package/src/utils/useLocationChange.ts +39 -0
- package/src/utils/usePluralForm.ts +35 -28
- package/src/utils/usePrevious.ts +19 -0
- package/src/utils/useTOCHighlight.ts +183 -0
- package/src/utils/useThemeConfig.ts +38 -11
- package/lib/.tsbuildinfo +0 -4159
- package/lib/utils/useChangeRoute.js +0 -18
- package/src/utils/__tests__/codeBlockUtils.test.ts +0 -54
- package/src/utils/__tests__/pathUtils.test.ts +0 -32
- package/src/utils/useChangeRoute.ts +0 -21
- package/tsconfig.json +0 -10
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
useEffect,
|
|
11
|
+
useCallback,
|
|
12
|
+
useMemo,
|
|
13
|
+
type ReactNode,
|
|
14
|
+
useContext,
|
|
15
|
+
createContext,
|
|
16
|
+
} from 'react';
|
|
17
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
18
|
+
import {createStorageSlot} from './storageUtils';
|
|
19
|
+
import {useThemeConfig} from './useThemeConfig';
|
|
20
|
+
|
|
21
|
+
export const AnnouncementBarDismissStorageKey =
|
|
22
|
+
'docusaurus.announcement.dismiss';
|
|
23
|
+
const AnnouncementBarIdStorageKey = 'docusaurus.announcement.id';
|
|
24
|
+
|
|
25
|
+
const AnnouncementBarDismissStorage = createStorageSlot(
|
|
26
|
+
AnnouncementBarDismissStorageKey,
|
|
27
|
+
);
|
|
28
|
+
const IdStorage = createStorageSlot(AnnouncementBarIdStorageKey);
|
|
29
|
+
|
|
30
|
+
const isDismissedInStorage = () =>
|
|
31
|
+
AnnouncementBarDismissStorage.get() === 'true';
|
|
32
|
+
const setDismissedInStorage = (bool: boolean) =>
|
|
33
|
+
AnnouncementBarDismissStorage.set(String(bool));
|
|
34
|
+
|
|
35
|
+
type AnnouncementBarAPI = {
|
|
36
|
+
readonly isActive: boolean;
|
|
37
|
+
readonly close: () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
|
41
|
+
const {announcementBar} = useThemeConfig();
|
|
42
|
+
const isBrowser = useIsBrowser();
|
|
43
|
+
|
|
44
|
+
const [isClosed, setClosed] = useState(() =>
|
|
45
|
+
isBrowser
|
|
46
|
+
? // On client navigation: init with local storage value
|
|
47
|
+
isDismissedInStorage()
|
|
48
|
+
: // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed)
|
|
49
|
+
false,
|
|
50
|
+
);
|
|
51
|
+
// Update state after hydration
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setClosed(isDismissedInStorage());
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const handleClose = useCallback(() => {
|
|
57
|
+
setDismissedInStorage(true);
|
|
58
|
+
setClosed(true);
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!announcementBar) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const {id} = announcementBar;
|
|
66
|
+
|
|
67
|
+
let viewedId = IdStorage.get();
|
|
68
|
+
|
|
69
|
+
// retrocompatibility due to spelling mistake of default id
|
|
70
|
+
// see https://github.com/facebook/docusaurus/issues/3338
|
|
71
|
+
// cSpell:ignore annoucement
|
|
72
|
+
if (viewedId === 'annoucement-bar') {
|
|
73
|
+
viewedId = 'announcement-bar';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const isNewAnnouncement = id !== viewedId;
|
|
77
|
+
|
|
78
|
+
IdStorage.set(id);
|
|
79
|
+
|
|
80
|
+
if (isNewAnnouncement) {
|
|
81
|
+
setDismissedInStorage(false);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isNewAnnouncement || !isDismissedInStorage()) {
|
|
85
|
+
setClosed(false);
|
|
86
|
+
}
|
|
87
|
+
}, [announcementBar]);
|
|
88
|
+
|
|
89
|
+
return useMemo(
|
|
90
|
+
() => ({
|
|
91
|
+
isActive: !!announcementBar && !isClosed,
|
|
92
|
+
close: handleClose,
|
|
93
|
+
}),
|
|
94
|
+
[announcementBar, isClosed, handleClose],
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
|
|
99
|
+
|
|
100
|
+
export function AnnouncementBarProvider({
|
|
101
|
+
children,
|
|
102
|
+
}: {
|
|
103
|
+
children: ReactNode;
|
|
104
|
+
}): JSX.Element {
|
|
105
|
+
const value = useAnnouncementBarContextValue();
|
|
106
|
+
return (
|
|
107
|
+
<AnnouncementBarContext.Provider value={value}>
|
|
108
|
+
{children}
|
|
109
|
+
</AnnouncementBarContext.Provider>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const useAnnouncementBar = (): AnnouncementBarAPI => {
|
|
114
|
+
const api = useContext(AnnouncementBarContext);
|
|
115
|
+
if (!api) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
'useAnnouncementBar(): AnnouncementBar not found in React context: make sure to use the AnnouncementBarProvider on top of the tree',
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return api;
|
|
121
|
+
};
|
|
@@ -5,8 +5,159 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import rangeParser from 'parse-numeric-range';
|
|
9
|
+
|
|
10
|
+
const codeBlockTitleRegex = /title=(?<quote>["'])(?<title>.*?)\1/;
|
|
11
|
+
const highlightLinesRangeRegex = /{(?<range>[\d,-]+)}/;
|
|
12
|
+
|
|
13
|
+
const commentTypes = ['js', 'jsBlock', 'jsx', 'python', 'html'] as const;
|
|
14
|
+
type CommentType = typeof commentTypes[number];
|
|
15
|
+
|
|
16
|
+
type CommentPattern = {
|
|
17
|
+
start: string;
|
|
18
|
+
end: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Supported types of highlight comments
|
|
22
|
+
const commentPatterns: Record<CommentType, CommentPattern> = {
|
|
23
|
+
js: {
|
|
24
|
+
start: '\\/\\/',
|
|
25
|
+
end: '',
|
|
26
|
+
},
|
|
27
|
+
jsBlock: {
|
|
28
|
+
start: '\\/\\*',
|
|
29
|
+
end: '\\*\\/',
|
|
30
|
+
},
|
|
31
|
+
jsx: {
|
|
32
|
+
start: '\\{\\s*\\/\\*',
|
|
33
|
+
end: '\\*\\/\\s*\\}',
|
|
34
|
+
},
|
|
35
|
+
python: {
|
|
36
|
+
start: '#',
|
|
37
|
+
end: '',
|
|
38
|
+
},
|
|
39
|
+
html: {
|
|
40
|
+
start: '<!--',
|
|
41
|
+
end: '-->',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const magicCommentDirectives = [
|
|
46
|
+
'highlight-next-line',
|
|
47
|
+
'highlight-start',
|
|
48
|
+
'highlight-end',
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const getMagicCommentDirectiveRegex = (
|
|
52
|
+
languages: readonly CommentType[] = commentTypes,
|
|
53
|
+
) => {
|
|
54
|
+
// to be more reliable, the opening and closing comment must match
|
|
55
|
+
const commentPattern = languages
|
|
56
|
+
.map((lang) => {
|
|
57
|
+
const {start, end} = commentPatterns[lang];
|
|
58
|
+
return `(?:${start}\\s*(${magicCommentDirectives.join('|')})\\s*${end})`;
|
|
59
|
+
})
|
|
60
|
+
.join('|');
|
|
61
|
+
// white space is allowed, but otherwise it should be on it's own line
|
|
62
|
+
return new RegExp(`^\\s*(?:${commentPattern})\\s*$`);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// select comment styles based on language
|
|
66
|
+
const magicCommentDirectiveRegex = (lang: string) => {
|
|
67
|
+
switch (lang) {
|
|
68
|
+
case 'js':
|
|
69
|
+
case 'javascript':
|
|
70
|
+
case 'ts':
|
|
71
|
+
case 'typescript':
|
|
72
|
+
return getMagicCommentDirectiveRegex(['js', 'jsBlock']);
|
|
73
|
+
|
|
74
|
+
case 'jsx':
|
|
75
|
+
case 'tsx':
|
|
76
|
+
return getMagicCommentDirectiveRegex(['js', 'jsBlock', 'jsx']);
|
|
77
|
+
|
|
78
|
+
case 'html':
|
|
79
|
+
return getMagicCommentDirectiveRegex(['js', 'jsBlock', 'html']);
|
|
80
|
+
|
|
81
|
+
case 'python':
|
|
82
|
+
case 'py':
|
|
83
|
+
return getMagicCommentDirectiveRegex(['python']);
|
|
84
|
+
|
|
85
|
+
default:
|
|
86
|
+
// all comment types
|
|
87
|
+
return getMagicCommentDirectiveRegex();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
9
90
|
|
|
10
91
|
export function parseCodeBlockTitle(metastring?: string): string {
|
|
11
|
-
return metastring?.match(codeBlockTitleRegex)?.
|
|
92
|
+
return metastring?.match(codeBlockTitleRegex)?.groups!.title ?? '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function parseLanguage(className: string): string | undefined {
|
|
96
|
+
const languageClassName = className
|
|
97
|
+
.split(' ')
|
|
98
|
+
.find((str) => str.startsWith('language-'));
|
|
99
|
+
return languageClassName?.replace(/language-/, '');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param metastring The highlight range declared here starts at 1
|
|
104
|
+
* @returns Note: all line numbers start at 0, not 1
|
|
105
|
+
*/
|
|
106
|
+
export function parseLines(
|
|
107
|
+
content: string,
|
|
108
|
+
metastring?: string,
|
|
109
|
+
language?: string,
|
|
110
|
+
): {
|
|
111
|
+
highlightLines: number[];
|
|
112
|
+
code: string;
|
|
113
|
+
} {
|
|
114
|
+
let code = content.replace(/\n$/, '');
|
|
115
|
+
// Highlighted lines specified in props: don't parse the content
|
|
116
|
+
if (metastring && highlightLinesRangeRegex.test(metastring)) {
|
|
117
|
+
const highlightLinesRange = metastring.match(highlightLinesRangeRegex)!
|
|
118
|
+
.groups!.range;
|
|
119
|
+
const highlightLines = rangeParser(highlightLinesRange)
|
|
120
|
+
.filter((n) => n > 0)
|
|
121
|
+
.map((n) => n - 1);
|
|
122
|
+
return {highlightLines, code};
|
|
123
|
+
}
|
|
124
|
+
if (language === undefined) {
|
|
125
|
+
return {highlightLines: [], code};
|
|
126
|
+
}
|
|
127
|
+
const directiveRegex = magicCommentDirectiveRegex(language);
|
|
128
|
+
// go through line by line
|
|
129
|
+
const lines = code.split('\n');
|
|
130
|
+
let highlightBlockStart: number;
|
|
131
|
+
let highlightRange = '';
|
|
132
|
+
// loop through lines
|
|
133
|
+
for (let lineNumber = 0; lineNumber < lines.length; ) {
|
|
134
|
+
const line = lines[lineNumber];
|
|
135
|
+
const match = line.match(directiveRegex);
|
|
136
|
+
if (match !== null) {
|
|
137
|
+
const directive = match.slice(1).find((item) => item !== undefined);
|
|
138
|
+
switch (directive) {
|
|
139
|
+
case 'highlight-next-line':
|
|
140
|
+
highlightRange += `${lineNumber},`;
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case 'highlight-start':
|
|
144
|
+
highlightBlockStart = lineNumber;
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case 'highlight-end':
|
|
148
|
+
highlightRange += `${highlightBlockStart!}-${lineNumber - 1},`;
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
default:
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
lines.splice(lineNumber, 1);
|
|
155
|
+
} else {
|
|
156
|
+
// lines without directives are unchanged
|
|
157
|
+
lineNumber += 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const highlightLines = rangeParser(highlightRange);
|
|
161
|
+
code = lines.join('\n');
|
|
162
|
+
return {highlightLines, code};
|
|
12
163
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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 type {ReactNode} from 'react';
|
|
9
|
+
import React, {
|
|
10
|
+
useState,
|
|
11
|
+
useCallback,
|
|
12
|
+
useEffect,
|
|
13
|
+
useContext,
|
|
14
|
+
useMemo,
|
|
15
|
+
} from 'react';
|
|
16
|
+
|
|
17
|
+
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
18
|
+
import {createStorageSlot} from './storageUtils';
|
|
19
|
+
import {useThemeConfig} from './useThemeConfig';
|
|
20
|
+
|
|
21
|
+
type ColorModeContextValue = {
|
|
22
|
+
readonly isDarkTheme: boolean;
|
|
23
|
+
readonly setLightTheme: () => void;
|
|
24
|
+
readonly setDarkTheme: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const ThemeStorageKey = 'theme';
|
|
28
|
+
const ThemeStorage = createStorageSlot(ThemeStorageKey);
|
|
29
|
+
|
|
30
|
+
const themes = {
|
|
31
|
+
light: 'light',
|
|
32
|
+
dark: 'dark',
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
type Themes = typeof themes[keyof typeof themes];
|
|
36
|
+
|
|
37
|
+
// Ensure to always return a valid theme even if input is invalid
|
|
38
|
+
const coerceToTheme = (theme?: string | null): Themes =>
|
|
39
|
+
theme === themes.dark ? themes.dark : themes.light;
|
|
40
|
+
|
|
41
|
+
const getInitialTheme = (defaultMode: Themes | undefined): Themes => {
|
|
42
|
+
if (!ExecutionEnvironment.canUseDOM) {
|
|
43
|
+
return coerceToTheme(defaultMode);
|
|
44
|
+
}
|
|
45
|
+
return coerceToTheme(document.documentElement.getAttribute('data-theme'));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const storeTheme = (newTheme: Themes) => {
|
|
49
|
+
ThemeStorage.set(coerceToTheme(newTheme));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function useColorModeContextValue(): ColorModeContextValue {
|
|
53
|
+
const {
|
|
54
|
+
colorMode: {defaultMode, disableSwitch, respectPrefersColorScheme},
|
|
55
|
+
} = useThemeConfig();
|
|
56
|
+
const [theme, setTheme] = useState(getInitialTheme(defaultMode));
|
|
57
|
+
|
|
58
|
+
const setLightTheme = useCallback(() => {
|
|
59
|
+
setTheme(themes.light);
|
|
60
|
+
storeTheme(themes.light);
|
|
61
|
+
}, []);
|
|
62
|
+
const setDarkTheme = useCallback(() => {
|
|
63
|
+
setTheme(themes.dark);
|
|
64
|
+
storeTheme(themes.dark);
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
document.documentElement.setAttribute('data-theme', coerceToTheme(theme));
|
|
69
|
+
}, [theme]);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (disableSwitch) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const onChange = (e: StorageEvent) => {
|
|
76
|
+
if (e.key !== ThemeStorageKey) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const storedTheme = ThemeStorage.get();
|
|
81
|
+
if (storedTheme !== null) {
|
|
82
|
+
setTheme(coerceToTheme(storedTheme));
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(err);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
window.addEventListener('storage', onChange);
|
|
89
|
+
return () => {
|
|
90
|
+
window.removeEventListener('storage', onChange);
|
|
91
|
+
};
|
|
92
|
+
}, [disableSwitch, setTheme]);
|
|
93
|
+
|
|
94
|
+
// PCS is coerced to light mode when printing, which causes the color mode to
|
|
95
|
+
// be reset to dark when exiting print mode, disregarding user settings. When
|
|
96
|
+
// the listener fires only because of a print/screen switch, we don't change
|
|
97
|
+
// color mode. See https://github.com/facebook/docusaurus/pull/6490
|
|
98
|
+
const previousMediaIsPrint = React.useRef(false);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (disableSwitch && !respectPrefersColorScheme) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
105
|
+
const onChange = ({matches}: MediaQueryListEvent) => {
|
|
106
|
+
if (window.matchMedia('print').matches || previousMediaIsPrint.current) {
|
|
107
|
+
previousMediaIsPrint.current = window.matchMedia('print').matches;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
setTheme(matches ? themes.dark : themes.light);
|
|
111
|
+
};
|
|
112
|
+
mql.addListener(onChange);
|
|
113
|
+
return () => {
|
|
114
|
+
mql.removeListener(onChange);
|
|
115
|
+
};
|
|
116
|
+
}, [disableSwitch, respectPrefersColorScheme]);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
isDarkTheme: theme === themes.dark,
|
|
120
|
+
setLightTheme,
|
|
121
|
+
setDarkTheme,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const ColorModeContext = React.createContext<ColorModeContextValue | undefined>(
|
|
126
|
+
undefined,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
export function ColorModeProvider({
|
|
130
|
+
children,
|
|
131
|
+
}: {
|
|
132
|
+
children: ReactNode;
|
|
133
|
+
}): JSX.Element {
|
|
134
|
+
const {isDarkTheme, setLightTheme, setDarkTheme} = useColorModeContextValue();
|
|
135
|
+
const contextValue = useMemo(
|
|
136
|
+
() => ({isDarkTheme, setLightTheme, setDarkTheme}),
|
|
137
|
+
[isDarkTheme, setLightTheme, setDarkTheme],
|
|
138
|
+
);
|
|
139
|
+
return (
|
|
140
|
+
<ColorModeContext.Provider value={contextValue}>
|
|
141
|
+
{children}
|
|
142
|
+
</ColorModeContext.Provider>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function useColorMode(): ColorModeContextValue {
|
|
147
|
+
const context = useContext<ColorModeContextValue | undefined>(
|
|
148
|
+
ColorModeContext,
|
|
149
|
+
);
|
|
150
|
+
if (context == null) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
'"useColorMode()" is used outside of "Layout" component. Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.',
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return context;
|
|
156
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
|
|
10
|
+
const EmptyContext: unique symbol = Symbol('EmptyContext');
|
|
11
|
+
const Context = React.createContext<
|
|
12
|
+
DocSidebarItemsExpandedState | typeof EmptyContext
|
|
13
|
+
>(EmptyContext);
|
|
14
|
+
type DocSidebarItemsExpandedState = {
|
|
15
|
+
expandedItem: number | null;
|
|
16
|
+
setExpandedItem: (a: number | null) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function DocSidebarItemsExpandedStateProvider({
|
|
20
|
+
children,
|
|
21
|
+
}: {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}): JSX.Element {
|
|
24
|
+
const [expandedItem, setExpandedItem] = useState<number | null>(null);
|
|
25
|
+
const contextValue = useMemo(
|
|
26
|
+
() => ({expandedItem, setExpandedItem}),
|
|
27
|
+
[expandedItem],
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useDocSidebarItemsExpandedState(): DocSidebarItemsExpandedState {
|
|
34
|
+
const contextValue = useContext(Context);
|
|
35
|
+
if (contextValue === EmptyContext) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'This hook requires usage of <DocSidebarItemsExpandedStateProvider>',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return contextValue;
|
|
41
|
+
}
|
|
@@ -4,18 +4,22 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
+
|
|
7
8
|
import React, {
|
|
8
9
|
createContext,
|
|
9
|
-
ReactNode,
|
|
10
|
+
type ReactNode,
|
|
10
11
|
useContext,
|
|
11
12
|
useEffect,
|
|
12
13
|
useMemo,
|
|
13
14
|
useState,
|
|
14
15
|
} from 'react';
|
|
15
|
-
import {useThemeConfig, DocsVersionPersistence} from '../useThemeConfig';
|
|
16
|
+
import {useThemeConfig, type DocsVersionPersistence} from '../useThemeConfig';
|
|
16
17
|
import {isDocsPluginEnabled} from '../docsUtils';
|
|
17
18
|
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
useAllDocsData,
|
|
21
|
+
type GlobalPluginData,
|
|
22
|
+
} from '@docusaurus/plugin-content-docs/client';
|
|
19
23
|
|
|
20
24
|
import DocsPreferredVersionStorage from './DocsPreferredVersionStorage';
|
|
21
25
|
|
|
@@ -34,7 +38,7 @@ type DocsPreferredVersionState = Record<
|
|
|
34
38
|
DocsPreferredVersionPluginState
|
|
35
39
|
>;
|
|
36
40
|
|
|
37
|
-
// Initial state is always null as we can't read
|
|
41
|
+
// Initial state is always null as we can't read local storage from node SSR
|
|
38
42
|
function getInitialState(pluginIds: string[]): DocsPreferredVersionState {
|
|
39
43
|
const initialState: DocsPreferredVersionState = {};
|
|
40
44
|
pluginIds.forEach((pluginId) => {
|
|
@@ -54,7 +58,7 @@ function readStorageState({
|
|
|
54
58
|
}: {
|
|
55
59
|
pluginIds: string[];
|
|
56
60
|
versionPersistence: DocsVersionPersistence;
|
|
57
|
-
allDocsData:
|
|
61
|
+
allDocsData: Record<string, GlobalPluginData>;
|
|
58
62
|
}): DocsPreferredVersionState {
|
|
59
63
|
// The storage value we read might be stale,
|
|
60
64
|
// and belong to a version that does not exist in the site anymore
|
|
@@ -68,14 +72,13 @@ function readStorageState({
|
|
|
68
72
|
);
|
|
69
73
|
const pluginData = allDocsData[pluginId];
|
|
70
74
|
const versionExists = pluginData.versions.some(
|
|
71
|
-
(version
|
|
75
|
+
(version) => version.name === preferredVersionNameUnsafe,
|
|
72
76
|
);
|
|
73
77
|
if (versionExists) {
|
|
74
78
|
return {preferredVersionName: preferredVersionNameUnsafe};
|
|
75
|
-
} else {
|
|
76
|
-
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
|
77
|
-
return {preferredVersionName: null};
|
|
78
79
|
}
|
|
80
|
+
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
|
81
|
+
return {preferredVersionName: null};
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
const initialState: DocsPreferredVersionState = {};
|
|
@@ -120,7 +123,7 @@ function useContextValue() {
|
|
|
120
123
|
return {
|
|
121
124
|
savePreferredVersion,
|
|
122
125
|
};
|
|
123
|
-
}, [
|
|
126
|
+
}, [versionPersistence]);
|
|
124
127
|
|
|
125
128
|
return [state, api] as const;
|
|
126
129
|
}
|
|
@@ -132,7 +135,7 @@ const Context = createContext<DocsPreferredVersionContextValue | null>(null);
|
|
|
132
135
|
export function DocsPreferredVersionContextProvider({
|
|
133
136
|
children,
|
|
134
137
|
}: {
|
|
135
|
-
children:
|
|
138
|
+
children: JSX.Element;
|
|
136
139
|
}): JSX.Element {
|
|
137
140
|
if (isDocsPluginEnabled) {
|
|
138
141
|
return (
|
|
@@ -140,9 +143,8 @@ export function DocsPreferredVersionContextProvider({
|
|
|
140
143
|
{children}
|
|
141
144
|
</DocsPreferredVersionContextProviderUnsafe>
|
|
142
145
|
);
|
|
143
|
-
} else {
|
|
144
|
-
return <>{children}</>;
|
|
145
146
|
}
|
|
147
|
+
return children;
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
function DocsPreferredVersionContextProviderUnsafe({
|
|
@@ -158,7 +160,7 @@ export function useDocsPreferredVersionContext(): DocsPreferredVersionContextVal
|
|
|
158
160
|
const value = useContext(Context);
|
|
159
161
|
if (!value) {
|
|
160
162
|
throw new Error(
|
|
161
|
-
|
|
163
|
+
'Can\'t find docs preferred context, maybe you forgot to use the "DocsPreferredVersionContextProvider"?',
|
|
162
164
|
);
|
|
163
165
|
}
|
|
164
166
|
return value;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {createStorageSlot} from '../storageUtils';
|
|
9
|
-
import {DocsVersionPersistence} from '../useThemeConfig';
|
|
9
|
+
import type {DocsVersionPersistence} from '../useThemeConfig';
|
|
10
10
|
|
|
11
11
|
const storageKey = (pluginId: string) => `docs-preferred-version-${pluginId}`;
|
|
12
12
|
|
|
@@ -22,9 +22,8 @@ const DocsPreferredVersionStorage = {
|
|
|
22
22
|
read: (
|
|
23
23
|
pluginId: string,
|
|
24
24
|
persistence: DocsVersionPersistence,
|
|
25
|
-
): string | null =>
|
|
26
|
-
|
|
27
|
-
},
|
|
25
|
+
): string | null =>
|
|
26
|
+
createStorageSlot(storageKey(pluginId), {persistence}).get(),
|
|
28
27
|
|
|
29
28
|
clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
|
|
30
29
|
createStorageSlot(storageKey(pluginId), {persistence}).del();
|
|
@@ -4,40 +4,47 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
+
|
|
7
8
|
import {useCallback} from 'react';
|
|
8
9
|
import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider';
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useAllDocsData,
|
|
12
|
+
useDocsData,
|
|
13
|
+
type GlobalVersion,
|
|
14
|
+
} from '@docusaurus/plugin-content-docs/client';
|
|
10
15
|
|
|
11
16
|
import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants';
|
|
12
17
|
|
|
13
|
-
// TODO improve typing
|
|
14
|
-
|
|
15
18
|
// Note, the preferredVersion attribute will always be null before mount
|
|
16
19
|
export function useDocsPreferredVersion(
|
|
17
20
|
pluginId: string | undefined = DEFAULT_PLUGIN_ID,
|
|
18
|
-
) {
|
|
21
|
+
): {
|
|
22
|
+
preferredVersion: GlobalVersion | null | undefined;
|
|
23
|
+
savePreferredVersionName: (versionName: string) => void;
|
|
24
|
+
} {
|
|
19
25
|
const docsData = useDocsData(pluginId);
|
|
20
26
|
const [state, api] = useDocsPreferredVersionContext();
|
|
21
27
|
|
|
22
28
|
const {preferredVersionName} = state[pluginId];
|
|
23
29
|
|
|
24
30
|
const preferredVersion = preferredVersionName
|
|
25
|
-
? docsData.versions.find(
|
|
26
|
-
(version: any) => version.name === preferredVersionName,
|
|
27
|
-
)
|
|
31
|
+
? docsData.versions.find((version) => version.name === preferredVersionName)
|
|
28
32
|
: null;
|
|
29
33
|
|
|
30
34
|
const savePreferredVersionName = useCallback(
|
|
31
35
|
(versionName: string) => {
|
|
32
36
|
api.savePreferredVersion(pluginId, versionName);
|
|
33
37
|
},
|
|
34
|
-
[api],
|
|
38
|
+
[api, pluginId],
|
|
35
39
|
);
|
|
36
40
|
|
|
37
41
|
return {preferredVersion, savePreferredVersionName} as const;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
export function useDocsPreferredVersionByPluginId(): Record<
|
|
44
|
+
export function useDocsPreferredVersionByPluginId(): Record<
|
|
45
|
+
string,
|
|
46
|
+
GlobalVersion | null | undefined
|
|
47
|
+
> {
|
|
41
48
|
const allDocsData = useAllDocsData();
|
|
42
49
|
const [state] = useDocsPreferredVersionContext();
|
|
43
50
|
|
|
@@ -47,17 +54,14 @@ export function useDocsPreferredVersionByPluginId(): Record<string, any> {
|
|
|
47
54
|
|
|
48
55
|
return preferredVersionName
|
|
49
56
|
? docsData.versions.find(
|
|
50
|
-
(version
|
|
57
|
+
(version) => version.name === preferredVersionName,
|
|
51
58
|
)
|
|
52
59
|
: null;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
const pluginIds = Object.keys(allDocsData);
|
|
56
63
|
|
|
57
|
-
const result: Record<
|
|
58
|
-
string,
|
|
59
|
-
any // TODO find a way to type this properly!
|
|
60
|
-
> = {};
|
|
64
|
+
const result: Record<string, GlobalVersion | null | undefined> = {};
|
|
61
65
|
pluginIds.forEach((pluginId) => {
|
|
62
66
|
result[pluginId] = getPluginIdPreferredVersion(pluginId);
|
|
63
67
|
});
|