@docusaurus/theme-common 2.4.0 → 3.0.0-alpha.0
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 +4 -4
- package/lib/components/Collapsible/index.d.ts.map +1 -1
- package/lib/components/Collapsible/index.js +9 -8
- package/lib/components/Collapsible/index.js.map +1 -1
- package/lib/components/Details/index.d.ts +1 -1
- package/lib/components/Details/index.d.ts.map +1 -1
- package/lib/components/Details/styles.module.css +5 -1
- package/lib/components/ThemedComponent/index.d.ts +32 -0
- package/lib/components/ThemedComponent/index.d.ts.map +1 -0
- package/lib/components/ThemedComponent/index.js +48 -0
- package/lib/components/ThemedComponent/index.js.map +1 -0
- package/lib/components/ThemedComponent/styles.module.css +18 -0
- package/lib/contexts/announcementBar.d.ts +1 -1
- package/lib/contexts/announcementBar.d.ts.map +1 -1
- package/lib/contexts/blogPost.d.ts +1 -1
- package/lib/contexts/blogPost.d.ts.map +1 -1
- package/lib/contexts/blogPost.js +3 -3
- package/lib/contexts/colorMode.d.ts +2 -2
- package/lib/contexts/colorMode.d.ts.map +1 -1
- package/lib/contexts/doc.d.ts +1 -1
- package/lib/contexts/doc.d.ts.map +1 -1
- package/lib/contexts/docSidebarItemsExpandedState.d.ts +1 -1
- package/lib/contexts/docSidebarItemsExpandedState.d.ts.map +1 -1
- package/lib/contexts/docsSidebar.d.ts +1 -1
- package/lib/contexts/docsSidebar.d.ts.map +1 -1
- package/lib/contexts/navbarMobileSidebar.d.ts +1 -1
- package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -1
- package/lib/contexts/navbarSecondaryMenu/content.d.ts +2 -2
- package/lib/contexts/navbarSecondaryMenu/content.d.ts.map +1 -1
- package/lib/hooks/useMutationObserver.d.ts +1 -1
- package/lib/hooks/useMutationObserver.d.ts.map +1 -1
- package/lib/hooks/useTOCHighlight.d.ts +1 -1
- package/lib/hooks/useTOCHighlight.d.ts.map +1 -1
- package/lib/hooks/useWindowSize.d.ts +1 -1
- package/lib/hooks/useWindowSize.d.ts.map +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -1
- package/lib/index.js.map +1 -1
- package/lib/internal.d.ts +3 -2
- package/lib/internal.d.ts.map +1 -1
- package/lib/internal.js +3 -2
- package/lib/internal.js.map +1 -1
- package/lib/utils/ThemeClassNames.d.ts +2 -1
- package/lib/utils/ThemeClassNames.d.ts.map +1 -1
- package/lib/utils/ThemeClassNames.js +4 -0
- package/lib/utils/ThemeClassNames.js.map +1 -1
- package/lib/utils/accessibilityUtils.d.ts +8 -0
- package/lib/utils/accessibilityUtils.d.ts.map +1 -0
- package/lib/utils/accessibilityUtils.js +10 -0
- package/lib/utils/accessibilityUtils.js.map +1 -0
- package/lib/utils/admonitionUtils.d.ts.map +1 -1
- package/lib/utils/admonitionUtils.js +5 -6
- package/lib/utils/admonitionUtils.js.map +1 -1
- package/lib/utils/blogUtils.d.ts +13 -0
- package/lib/utils/blogUtils.d.ts.map +1 -0
- package/lib/utils/blogUtils.js +24 -0
- package/lib/utils/blogUtils.js.map +1 -0
- package/lib/utils/codeBlockUtils.d.ts +1 -1
- package/lib/utils/codeBlockUtils.d.ts.map +1 -1
- package/lib/utils/codeBlockUtils.js +15 -2
- package/lib/utils/codeBlockUtils.js.map +1 -1
- package/lib/utils/docsUtils.d.ts +11 -5
- package/lib/utils/docsUtils.d.ts.map +1 -1
- package/lib/utils/docsUtils.js +41 -17
- package/lib/utils/docsUtils.js.map +1 -1
- package/lib/utils/historyUtils.d.ts +1 -1
- package/lib/utils/historyUtils.d.ts.map +1 -1
- package/lib/utils/metadataUtils.d.ts +1 -1
- package/lib/utils/metadataUtils.d.ts.map +1 -1
- package/lib/utils/reactUtils.d.ts +2 -11
- package/lib/utils/reactUtils.d.ts.map +1 -1
- package/lib/utils/reactUtils.js +2 -13
- package/lib/utils/reactUtils.js.map +1 -1
- package/lib/utils/scrollUtils.d.ts +3 -3
- package/lib/utils/scrollUtils.d.ts.map +1 -1
- package/lib/utils/scrollUtils.js +3 -2
- package/lib/utils/scrollUtils.js.map +1 -1
- package/lib/utils/skipToContentUtils.d.ts +5 -2
- package/lib/utils/skipToContentUtils.d.ts.map +1 -1
- package/lib/utils/skipToContentUtils.js +5 -1
- package/lib/utils/skipToContentUtils.js.map +1 -1
- package/lib/utils/storageUtils.d.ts +2 -2
- package/lib/utils/storageUtils.d.ts.map +1 -1
- package/lib/utils/tabsUtils.d.ts +3 -2
- package/lib/utils/tabsUtils.d.ts.map +1 -1
- package/lib/utils/tabsUtils.js +10 -7
- package/lib/utils/tabsUtils.js.map +1 -1
- package/lib/utils/tagsUtils.d.ts +1 -1
- package/lib/utils/tagsUtils.d.ts.map +1 -1
- package/lib/utils/tocUtils.d.ts +1 -1
- package/lib/utils/tocUtils.d.ts.map +1 -1
- package/lib/utils/unlistedUtils.d.ts +11 -0
- package/lib/utils/unlistedUtils.d.ts.map +1 -0
- package/lib/utils/unlistedUtils.js +26 -0
- package/lib/utils/unlistedUtils.js.map +1 -0
- package/lib/utils/useThemeConfig.d.ts +17 -17
- package/lib/utils/useThemeConfig.d.ts.map +1 -1
- package/package.json +15 -15
- package/src/components/Collapsible/index.tsx +8 -9
- package/src/components/Details/styles.module.css +5 -1
- package/src/components/ThemedComponent/index.tsx +77 -0
- package/src/components/ThemedComponent/styles.module.css +18 -0
- package/src/contexts/blogPost.tsx +3 -3
- package/src/contexts/colorMode.tsx +1 -1
- package/src/index.ts +10 -1
- package/src/internal.ts +7 -3
- package/src/utils/ThemeClassNames.ts +6 -2
- package/src/utils/accessibilityUtils.ts +10 -0
- package/src/utils/admonitionUtils.tsx +9 -8
- package/src/utils/blogUtils.ts +32 -0
- package/src/utils/codeBlockUtils.ts +21 -2
- package/src/utils/docsUtils.tsx +58 -20
- package/src/utils/reactUtils.tsx +1 -15
- package/src/utils/scrollUtils.tsx +2 -2
- package/src/utils/skipToContentUtils.tsx +5 -1
- package/src/utils/storageUtils.ts +1 -1
- package/src/utils/tabsUtils.tsx +20 -18
- package/src/utils/unlistedUtils.tsx +39 -0
|
@@ -0,0 +1,77 @@
|
|
|
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 from 'react';
|
|
9
|
+
import clsx from 'clsx';
|
|
10
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
11
|
+
import {useColorMode} from '../../contexts/colorMode';
|
|
12
|
+
|
|
13
|
+
import styles from './styles.module.css';
|
|
14
|
+
|
|
15
|
+
const AllThemes = ['light', 'dark'] as const;
|
|
16
|
+
|
|
17
|
+
type Theme = (typeof AllThemes)[number];
|
|
18
|
+
|
|
19
|
+
type RenderFn = ({
|
|
20
|
+
theme,
|
|
21
|
+
className,
|
|
22
|
+
}: {
|
|
23
|
+
theme: Theme;
|
|
24
|
+
className: string;
|
|
25
|
+
}) => React.ReactNode;
|
|
26
|
+
|
|
27
|
+
type Props = {
|
|
28
|
+
children: RenderFn;
|
|
29
|
+
className?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generic component to render anything themed in light/dark
|
|
34
|
+
* Note: it's preferable to use CSS for theming because this component
|
|
35
|
+
* will need to render all the variants during SSR to avoid a theme flash.
|
|
36
|
+
*
|
|
37
|
+
* Use this only when CSS customizations are not convenient or impossible.
|
|
38
|
+
* For example, rendering themed images or SVGs...
|
|
39
|
+
*
|
|
40
|
+
* @param className applied to all the variants
|
|
41
|
+
* @param children function to render a theme variant
|
|
42
|
+
* @constructor
|
|
43
|
+
*/
|
|
44
|
+
export default function ThemedComponent({
|
|
45
|
+
className,
|
|
46
|
+
children,
|
|
47
|
+
}: Props): JSX.Element {
|
|
48
|
+
const isBrowser = useIsBrowser();
|
|
49
|
+
const {colorMode} = useColorMode();
|
|
50
|
+
|
|
51
|
+
function getThemesToRender(): Theme[] {
|
|
52
|
+
if (isBrowser) {
|
|
53
|
+
return colorMode === 'dark' ? ['dark'] : ['light'];
|
|
54
|
+
}
|
|
55
|
+
// We need to render both components on the server / hydration to avoid:
|
|
56
|
+
// - a flash of wrong theme before hydration
|
|
57
|
+
// - React hydration mismatches
|
|
58
|
+
// See https://github.com/facebook/docusaurus/pull/3730
|
|
59
|
+
return ['light', 'dark'];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
{getThemesToRender().map((theme) => {
|
|
65
|
+
const themedElement = children({
|
|
66
|
+
theme,
|
|
67
|
+
className: clsx(
|
|
68
|
+
className,
|
|
69
|
+
styles.themedComponent,
|
|
70
|
+
styles[`themedComponent--${theme}`],
|
|
71
|
+
),
|
|
72
|
+
});
|
|
73
|
+
return <React.Fragment key={theme}>{themedElement}</React.Fragment>;
|
|
74
|
+
})}
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
.themedComponent {
|
|
9
|
+
display: none;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
[data-theme='light'] .themedComponent--light {
|
|
13
|
+
display: initial;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-theme='dark'] .themedComponent--dark {
|
|
17
|
+
display: initial;
|
|
18
|
+
}
|
|
@@ -24,9 +24,9 @@ export type BlogPostContextValue = Pick<
|
|
|
24
24
|
const Context = React.createContext<BlogPostContextValue | null>(null);
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Note: we don't use `PropBlogPostContent` as context value on purpose.
|
|
28
|
-
* currently stored inside the MDX component, but we may want to
|
|
29
|
-
* the future.
|
|
27
|
+
* Note: we don't use `PropBlogPostContent` as context value on purpose.
|
|
28
|
+
* Metadata is currently stored inside the MDX component, but we may want to
|
|
29
|
+
* change that in the future.
|
|
30
30
|
*/
|
|
31
31
|
function useContextValue({
|
|
32
32
|
content,
|
|
@@ -41,7 +41,7 @@ const ColorModes = {
|
|
|
41
41
|
dark: 'dark',
|
|
42
42
|
} as const;
|
|
43
43
|
|
|
44
|
-
export type ColorMode = typeof ColorModes[keyof typeof ColorModes];
|
|
44
|
+
export type ColorMode = (typeof ColorModes)[keyof typeof ColorModes];
|
|
45
45
|
|
|
46
46
|
// Ensure to always return a valid colorMode even if input is invalid
|
|
47
47
|
const coerceToColorMode = (colorMode?: string | null): ColorMode =>
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,8 @@ export {
|
|
|
24
24
|
type ColorModeConfig,
|
|
25
25
|
} from './utils/useThemeConfig';
|
|
26
26
|
|
|
27
|
+
export {default as ThemedComponent} from './components/ThemedComponent';
|
|
28
|
+
|
|
27
29
|
export {
|
|
28
30
|
createStorageSlot,
|
|
29
31
|
useStorageSlot,
|
|
@@ -43,8 +45,9 @@ export {useCollapsible, Collapsible} from './components/Collapsible';
|
|
|
43
45
|
|
|
44
46
|
export {ThemeClassNames} from './utils/ThemeClassNames';
|
|
45
47
|
|
|
48
|
+
export {prefersReducedMotion} from './utils/accessibilityUtils';
|
|
49
|
+
|
|
46
50
|
export {
|
|
47
|
-
useIsomorphicLayoutEffect,
|
|
48
51
|
useEvent,
|
|
49
52
|
usePrevious,
|
|
50
53
|
composeProviders,
|
|
@@ -95,6 +98,12 @@ export {
|
|
|
95
98
|
SkipToContentLink,
|
|
96
99
|
} from './utils/skipToContentUtils';
|
|
97
100
|
|
|
101
|
+
export {
|
|
102
|
+
UnlistedBannerTitle,
|
|
103
|
+
UnlistedBannerMessage,
|
|
104
|
+
UnlistedMetadata,
|
|
105
|
+
} from './utils/unlistedUtils';
|
|
106
|
+
|
|
98
107
|
export {
|
|
99
108
|
ErrorBoundaryTryAgainButton,
|
|
100
109
|
ErrorBoundaryError,
|
package/src/internal.ts
CHANGED
|
@@ -42,7 +42,7 @@ export {
|
|
|
42
42
|
useAnnouncementBar,
|
|
43
43
|
} from './contexts/announcementBar';
|
|
44
44
|
|
|
45
|
-
export {useTabs} from './utils/tabsUtils';
|
|
45
|
+
export {useTabs, sanitizeTabsChildren} from './utils/tabsUtils';
|
|
46
46
|
export type {TabValue, TabsProps, TabItemProps} from './utils/tabsUtils';
|
|
47
47
|
|
|
48
48
|
export {useNavbarMobileSidebar} from './contexts/navbarMobileSidebar';
|
|
@@ -65,13 +65,15 @@ export {
|
|
|
65
65
|
isDocsPluginEnabled,
|
|
66
66
|
useDocById,
|
|
67
67
|
findSidebarCategory,
|
|
68
|
-
|
|
68
|
+
findFirstSidebarItemLink,
|
|
69
69
|
isActiveSidebarItem,
|
|
70
|
+
isVisibleSidebarItem,
|
|
71
|
+
useVisibleSidebarItems,
|
|
70
72
|
useSidebarBreadcrumbs,
|
|
71
73
|
useDocsVersionCandidates,
|
|
72
74
|
useLayoutDoc,
|
|
73
75
|
useLayoutDocsSidebar,
|
|
74
|
-
|
|
76
|
+
useDocRootMetadata,
|
|
75
77
|
} from './utils/docsUtils';
|
|
76
78
|
|
|
77
79
|
export {useTitleFormatter} from './utils/generalUtils';
|
|
@@ -111,6 +113,8 @@ export {
|
|
|
111
113
|
type TOCHighlightConfig,
|
|
112
114
|
} from './hooks/useTOCHighlight';
|
|
113
115
|
|
|
116
|
+
export {useVisibleBlogSidebarItems} from './utils/blogUtils';
|
|
117
|
+
|
|
114
118
|
export {useHideableNavbar} from './hooks/useHideableNavbar';
|
|
115
119
|
export {
|
|
116
120
|
useKeyboardNavigation,
|
|
@@ -27,6 +27,9 @@ export const ThemeClassNames = {
|
|
|
27
27
|
},
|
|
28
28
|
wrapper: {
|
|
29
29
|
main: 'main-wrapper',
|
|
30
|
+
// TODO these wrapper class names are now quite useless
|
|
31
|
+
// TODO do breaking change later in 3.0
|
|
32
|
+
// we already add plugin name/id class on <html>: that's enough
|
|
30
33
|
blogPages: 'blog-wrapper',
|
|
31
34
|
docsPages: 'docs-wrapper',
|
|
32
35
|
mdxPages: 'mdx-wrapper',
|
|
@@ -37,8 +40,9 @@ export const ThemeClassNames = {
|
|
|
37
40
|
backToTopButton: 'theme-back-to-top-button',
|
|
38
41
|
codeBlock: 'theme-code-block',
|
|
39
42
|
admonition: 'theme-admonition',
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
unlistedBanner: 'theme-unlisted-banner',
|
|
44
|
+
|
|
45
|
+
admonitionType: (type: string) => `theme-admonition-${type}`,
|
|
42
46
|
},
|
|
43
47
|
layout: {
|
|
44
48
|
// TODO add other stable classNames here
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export function prefersReducedMotion(): boolean {
|
|
9
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
10
|
+
}
|
|
@@ -14,16 +14,17 @@ function extractMDXAdmonitionTitle(children: ReactNode): {
|
|
|
14
14
|
rest: ReactNode;
|
|
15
15
|
} {
|
|
16
16
|
const items = React.Children.toArray(children);
|
|
17
|
-
const
|
|
18
|
-
(item) =>
|
|
19
|
-
React.isValidElement(item) &&
|
|
20
|
-
(item.props as {mdxType: string} | null)?.mdxType ===
|
|
21
|
-
'mdxAdmonitionTitle',
|
|
17
|
+
const mdxAdmonitionTitleWrapper = items.find(
|
|
18
|
+
(item) => React.isValidElement(item) && item.type === 'mdxAdmonitionTitle',
|
|
22
19
|
) as JSX.Element | undefined;
|
|
23
|
-
|
|
20
|
+
|
|
21
|
+
const rest = items.filter((item) => item !== mdxAdmonitionTitleWrapper);
|
|
22
|
+
|
|
23
|
+
const mdxAdmonitionTitle = mdxAdmonitionTitleWrapper?.props.children;
|
|
24
|
+
|
|
24
25
|
return {
|
|
25
|
-
mdxAdmonitionTitle
|
|
26
|
-
rest,
|
|
26
|
+
mdxAdmonitionTitle,
|
|
27
|
+
rest: rest.length > 0 ? <>{rest}</> : null,
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -0,0 +1,32 @@
|
|
|
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 {useMemo} from 'react';
|
|
9
|
+
import {useLocation} from '@docusaurus/router';
|
|
10
|
+
import {isSamePath} from './routesUtils';
|
|
11
|
+
import type {BlogSidebarItem} from '@docusaurus/plugin-content-blog';
|
|
12
|
+
|
|
13
|
+
function isVisible(item: BlogSidebarItem, pathname: string): boolean {
|
|
14
|
+
if (item.unlisted && !isSamePath(item.permalink, pathname)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return the visible blog sidebar items to display.
|
|
22
|
+
* Unlisted items are filtered.
|
|
23
|
+
*/
|
|
24
|
+
export function useVisibleBlogSidebarItems(
|
|
25
|
+
items: BlogSidebarItem[],
|
|
26
|
+
): BlogSidebarItem[] {
|
|
27
|
+
const {pathname} = useLocation();
|
|
28
|
+
return useMemo(
|
|
29
|
+
() => items.filter((item) => isVisible(item, pathname)),
|
|
30
|
+
[items, pathname],
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -19,6 +19,9 @@ const commentPatterns = {
|
|
|
19
19
|
jsx: {start: '\\{\\s*\\/\\*', end: '\\*\\/\\s*\\}'},
|
|
20
20
|
bash: {start: '#', end: ''},
|
|
21
21
|
html: {start: '<!--', end: '-->'},
|
|
22
|
+
lua: {start: '--', end: ''},
|
|
23
|
+
wasm: {start: '\\;\\;', end: ''},
|
|
24
|
+
tex: {start: '%', end: ''},
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
type CommentType = keyof typeof commentPatterns;
|
|
@@ -83,10 +86,26 @@ function getAllMagicCommentDirectiveStyles(
|
|
|
83
86
|
// Text uses HTML, front matter uses bash
|
|
84
87
|
return getCommentPattern(['html', 'jsx', 'bash'], magicCommentDirectives);
|
|
85
88
|
|
|
89
|
+
case 'tex':
|
|
90
|
+
case 'latex':
|
|
91
|
+
case 'matlab':
|
|
92
|
+
return getCommentPattern(['tex'], magicCommentDirectives);
|
|
93
|
+
|
|
94
|
+
case 'lua':
|
|
95
|
+
case 'haskell':
|
|
96
|
+
case 'sql':
|
|
97
|
+
return getCommentPattern(['lua'], magicCommentDirectives);
|
|
98
|
+
|
|
99
|
+
case 'wasm':
|
|
100
|
+
return getCommentPattern(['wasm'], magicCommentDirectives);
|
|
101
|
+
|
|
86
102
|
default:
|
|
87
|
-
// All comment types
|
|
103
|
+
// All comment types except lua, wasm and matlab
|
|
88
104
|
return getCommentPattern(
|
|
89
|
-
Object.keys(commentPatterns)
|
|
105
|
+
Object.keys(commentPatterns).filter(
|
|
106
|
+
(pattern) =>
|
|
107
|
+
!['lua', 'wasm', 'tex', 'latex', 'matlab'].includes(pattern),
|
|
108
|
+
) as CommentType[],
|
|
90
109
|
magicCommentDirectives,
|
|
91
110
|
);
|
|
92
111
|
}
|
package/src/utils/docsUtils.tsx
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
type GlobalSidebar,
|
|
18
18
|
type GlobalDoc,
|
|
19
19
|
} from '@docusaurus/plugin-content-docs/client';
|
|
20
|
-
import type {Props as
|
|
20
|
+
import type {Props as DocRootProps} from '@theme/DocRoot';
|
|
21
21
|
import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion';
|
|
22
22
|
import {useDocsVersion} from '../contexts/docsVersion';
|
|
23
23
|
import {useDocsSidebar} from '../contexts/docsSidebar';
|
|
@@ -79,27 +79,38 @@ export function findSidebarCategory(
|
|
|
79
79
|
* Best effort to assign a link to a sidebar category. If the category doesn't
|
|
80
80
|
* have a link itself, we link to the first sub item with a link.
|
|
81
81
|
*/
|
|
82
|
-
export function
|
|
82
|
+
export function findFirstSidebarItemCategoryLink(
|
|
83
83
|
item: PropSidebarItemCategory,
|
|
84
84
|
): string | undefined {
|
|
85
|
-
if (item.href) {
|
|
85
|
+
if (item.href && !item.linkUnlisted) {
|
|
86
86
|
return item.href;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
for (const subItem of item.items) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const categoryLink = findFirstCategoryLink(subItem);
|
|
94
|
-
if (categoryLink) {
|
|
95
|
-
return categoryLink;
|
|
96
|
-
}
|
|
90
|
+
const link = findFirstSidebarItemLink(subItem);
|
|
91
|
+
if (link) {
|
|
92
|
+
return link;
|
|
97
93
|
}
|
|
98
|
-
// Could be "html" items
|
|
99
94
|
}
|
|
100
95
|
return undefined;
|
|
101
96
|
}
|
|
102
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Best effort to assign a link to a sidebar item.
|
|
100
|
+
*/
|
|
101
|
+
export function findFirstSidebarItemLink(
|
|
102
|
+
item: PropSidebarItem,
|
|
103
|
+
): string | undefined {
|
|
104
|
+
if (item.type === 'link' && !item.unlisted) {
|
|
105
|
+
return item.href;
|
|
106
|
+
}
|
|
107
|
+
if (item.type === 'category') {
|
|
108
|
+
return findFirstSidebarItemCategoryLink(item);
|
|
109
|
+
}
|
|
110
|
+
// Other items types, like "html"
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
103
114
|
/**
|
|
104
115
|
* Gets the category associated with the current location. Should only be used
|
|
105
116
|
* on category index pages.
|
|
@@ -152,6 +163,34 @@ export function isActiveSidebarItem(
|
|
|
152
163
|
return false;
|
|
153
164
|
}
|
|
154
165
|
|
|
166
|
+
export function isVisibleSidebarItem(
|
|
167
|
+
item: PropSidebarItem,
|
|
168
|
+
activePath: string,
|
|
169
|
+
): boolean {
|
|
170
|
+
switch (item.type) {
|
|
171
|
+
case 'category':
|
|
172
|
+
return (
|
|
173
|
+
isActiveSidebarItem(item, activePath) ||
|
|
174
|
+
item.items.some((subItem) => isVisibleSidebarItem(subItem, activePath))
|
|
175
|
+
);
|
|
176
|
+
case 'link':
|
|
177
|
+
// An unlisted item remains visible if it is active
|
|
178
|
+
return !item.unlisted || isActiveSidebarItem(item, activePath);
|
|
179
|
+
default:
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function useVisibleSidebarItems(
|
|
185
|
+
items: readonly PropSidebarItem[],
|
|
186
|
+
activePath: string,
|
|
187
|
+
): PropSidebarItem[] {
|
|
188
|
+
return useMemo(
|
|
189
|
+
() => items.filter((item) => isVisibleSidebarItem(item, activePath)),
|
|
190
|
+
[items, activePath],
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
155
194
|
function getSidebarBreadcrumbs(param: {
|
|
156
195
|
sidebarItems: PropSidebar;
|
|
157
196
|
pathname: string;
|
|
@@ -272,7 +311,7 @@ export function useLayoutDocsSidebar(
|
|
|
272
311
|
versions.length > 1 ? 's' : ''
|
|
273
312
|
} ${versions.map((version) => version.name).join(', ')}".
|
|
274
313
|
Available sidebar ids are:
|
|
275
|
-
- ${
|
|
314
|
+
- ${allSidebars.map((entry) => entry[0]).join('\n- ')}`,
|
|
276
315
|
);
|
|
277
316
|
}
|
|
278
317
|
return sidebarEntry[1];
|
|
@@ -321,14 +360,11 @@ Available doc ids are:
|
|
|
321
360
|
* version metadata, and the subroutes creating individual doc pages. This hook
|
|
322
361
|
* will match the current location against all known sub-routes.
|
|
323
362
|
*
|
|
324
|
-
* @param props The props received by `@theme/
|
|
363
|
+
* @param props The props received by `@theme/DocRoot`
|
|
325
364
|
* @returns The data of the relevant document at the current location, or `null`
|
|
326
365
|
* if no document associated with the current location can be found.
|
|
327
366
|
*/
|
|
328
|
-
export function
|
|
329
|
-
route,
|
|
330
|
-
versionMetadata,
|
|
331
|
-
}: DocPageProps): null | {
|
|
367
|
+
export function useDocRootMetadata({route}: DocRootProps): null | {
|
|
332
368
|
/** The element that should be rendered at the current location. */
|
|
333
369
|
docElement: JSX.Element;
|
|
334
370
|
/**
|
|
@@ -340,6 +376,7 @@ export function useDocRouteMetadata({
|
|
|
340
376
|
sidebarItems: PropSidebar | undefined;
|
|
341
377
|
} {
|
|
342
378
|
const location = useLocation();
|
|
379
|
+
const versionMetadata = useDocsVersion();
|
|
343
380
|
const docRoutes = route.routes!;
|
|
344
381
|
const currentDocRoute = docRoutes.find((docRoute) =>
|
|
345
382
|
matchPath(location.pathname, docRoute),
|
|
@@ -365,15 +402,16 @@ export function useDocRouteMetadata({
|
|
|
365
402
|
}
|
|
366
403
|
|
|
367
404
|
/**
|
|
368
|
-
* Filter
|
|
405
|
+
* Filter items we don't want to display on the doc card list view
|
|
369
406
|
* @param items
|
|
370
407
|
*/
|
|
371
408
|
export function filterDocCardListItems(
|
|
372
409
|
items: PropSidebarItem[],
|
|
373
410
|
): PropSidebarItem[] {
|
|
374
411
|
return items.filter((item) => {
|
|
375
|
-
|
|
376
|
-
|
|
412
|
+
const canHaveLink = item.type === 'category' || item.type === 'link';
|
|
413
|
+
if (canHaveLink) {
|
|
414
|
+
return !!findFirstSidebarItemLink(item);
|
|
377
415
|
}
|
|
378
416
|
return true;
|
|
379
417
|
});
|
package/src/utils/reactUtils.tsx
CHANGED
|
@@ -7,26 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import React, {
|
|
9
9
|
useCallback,
|
|
10
|
-
useEffect,
|
|
11
|
-
useLayoutEffect,
|
|
12
10
|
useMemo,
|
|
13
11
|
useRef,
|
|
14
12
|
type ComponentType,
|
|
15
13
|
type ReactNode,
|
|
16
14
|
} from 'react';
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* This hook is like `useLayoutEffect`, but without the SSR warning.
|
|
21
|
-
* It seems hacky but it's used in many React libs (Redux, Formik...).
|
|
22
|
-
* Also mentioned here: https://github.com/facebook/react/issues/16956
|
|
23
|
-
*
|
|
24
|
-
* It is useful when you need to update a ref as soon as possible after a React
|
|
25
|
-
* render (before `useEffect`).
|
|
26
|
-
*/
|
|
27
|
-
export const useIsomorphicLayoutEffect = ExecutionEnvironment.canUseDOM
|
|
28
|
-
? useLayoutEffect
|
|
29
|
-
: useEffect;
|
|
15
|
+
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
|
|
30
16
|
|
|
31
17
|
/**
|
|
32
18
|
* Temporary userland implementation until an official hook is implemented
|
|
@@ -9,13 +9,13 @@ import React, {
|
|
|
9
9
|
useCallback,
|
|
10
10
|
useContext,
|
|
11
11
|
useEffect,
|
|
12
|
-
useLayoutEffect,
|
|
13
12
|
useMemo,
|
|
14
13
|
useRef,
|
|
15
14
|
type ReactNode,
|
|
16
15
|
} from 'react';
|
|
17
16
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
18
17
|
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
18
|
+
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
|
|
19
19
|
import {useEvent, ReactContextError} from './reactUtils';
|
|
20
20
|
|
|
21
21
|
type ScrollController = {
|
|
@@ -221,7 +221,7 @@ export function useScrollPositionBlocker(): {
|
|
|
221
221
|
[scrollController, scrollPositionSaver],
|
|
222
222
|
);
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
useIsomorphicLayoutEffect(() => {
|
|
225
225
|
// Queuing permits to restore scroll position after all useLayoutEffect
|
|
226
226
|
// have run, and yet preserve the sync nature of the scroll restoration
|
|
227
227
|
// See https://github.com/facebook/docusaurus/issues/8625
|
|
@@ -14,8 +14,11 @@ import {useLocationChange} from './useLocationChange';
|
|
|
14
14
|
* The id of the element that should become focused on a page
|
|
15
15
|
* that does not have a <main> html tag.
|
|
16
16
|
* Focusing the Docusaurus Layout children is a reasonable fallback.
|
|
17
|
+
*
|
|
18
|
+
* __ prefix allows search crawlers (Algolia/DocSearch) to ignore anchors
|
|
19
|
+
* https://github.com/facebook/docusaurus/issues/8883#issuecomment-1516328368
|
|
17
20
|
*/
|
|
18
|
-
export const SkipToContentFallbackId = '
|
|
21
|
+
export const SkipToContentFallbackId = '__docusaurus_skipToContent_fallback';
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* Returns the skip to content element to focus when the link is clicked.
|
|
@@ -90,6 +93,7 @@ export function SkipToContentLink(props: SkipToContentLinkProps): JSX.Element {
|
|
|
90
93
|
ref={containerRef}
|
|
91
94
|
role="region"
|
|
92
95
|
aria-label={DefaultSkipToContentLabel}>
|
|
96
|
+
{/* eslint-disable-next-line @docusaurus/no-html-links */}
|
|
93
97
|
<a
|
|
94
98
|
{...props}
|
|
95
99
|
// Note this is a fallback href in case JS is disabled
|
|
@@ -11,7 +11,7 @@ import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
|
|
11
11
|
|
|
12
12
|
const StorageTypes = ['localStorage', 'sessionStorage', 'none'] as const;
|
|
13
13
|
|
|
14
|
-
export type StorageType = typeof StorageTypes[number];
|
|
14
|
+
export type StorageType = (typeof StorageTypes)[number];
|
|
15
15
|
|
|
16
16
|
const DefaultStorageType: StorageType = 'localStorage';
|
|
17
17
|
|
package/src/utils/tabsUtils.tsx
CHANGED
|
@@ -12,9 +12,9 @@ import React, {
|
|
|
12
12
|
useMemo,
|
|
13
13
|
type ReactNode,
|
|
14
14
|
type ReactElement,
|
|
15
|
-
useLayoutEffect,
|
|
16
15
|
} from 'react';
|
|
17
16
|
import {useHistory} from '@docusaurus/router';
|
|
17
|
+
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
|
|
18
18
|
import {useQueryStringValue} from '@docusaurus/theme-common/internal';
|
|
19
19
|
import {duplicates, useStorageSlot} from '../index';
|
|
20
20
|
|
|
@@ -61,25 +61,27 @@ function isTabItem(
|
|
|
61
61
|
return !!props && typeof props === 'object' && 'value' in props;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function
|
|
65
|
-
return (React.Children.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
export function sanitizeTabsChildren(children: TabsProps['children']) {
|
|
65
|
+
return (React.Children.toArray(children)
|
|
66
|
+
.filter((child) => child !== '\n')
|
|
67
|
+
.map((child) => {
|
|
68
|
+
if (!child || (isValidElement(child) && isTabItem(child))) {
|
|
69
|
+
return child;
|
|
70
|
+
}
|
|
71
|
+
// child.type.name will give non-sensical values in prod because of
|
|
72
|
+
// minification, but we assume it won't throw in prod.
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Docusaurus error: Bad <Tabs> child <${
|
|
75
|
+
// @ts-expect-error: guarding against unexpected cases
|
|
76
|
+
typeof child.type === 'string' ? child.type : child.type.name
|
|
77
|
+
}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`,
|
|
78
|
+
);
|
|
79
|
+
})
|
|
80
|
+
?.filter(Boolean) ?? []) as ReactElement<TabItemProps>[];
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
function extractChildrenTabValues(children: TabsProps['children']): TabValue[] {
|
|
82
|
-
return
|
|
84
|
+
return sanitizeTabsChildren(children).map(
|
|
83
85
|
({props: {value, label, attributes, default: isDefault}}) => ({
|
|
84
86
|
value,
|
|
85
87
|
label,
|
|
@@ -250,7 +252,7 @@ export function useTabs(props: TabsProps): {
|
|
|
250
252
|
})();
|
|
251
253
|
// Sync in a layout/sync effect is important, for useScrollPositionBlocker
|
|
252
254
|
// See https://github.com/facebook/docusaurus/issues/8625
|
|
253
|
-
|
|
255
|
+
useIsomorphicLayoutEffect(() => {
|
|
254
256
|
if (valueToSync) {
|
|
255
257
|
setSelectedValue(valueToSync);
|
|
256
258
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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 from 'react';
|
|
9
|
+
import Translate from '@docusaurus/Translate';
|
|
10
|
+
import Head from '@docusaurus/Head';
|
|
11
|
+
|
|
12
|
+
export function UnlistedBannerTitle(): JSX.Element {
|
|
13
|
+
return (
|
|
14
|
+
<Translate
|
|
15
|
+
id="theme.unlistedContent.title"
|
|
16
|
+
description="The unlisted content banner title">
|
|
17
|
+
Unlisted page
|
|
18
|
+
</Translate>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function UnlistedBannerMessage(): JSX.Element {
|
|
23
|
+
return (
|
|
24
|
+
<Translate
|
|
25
|
+
id="theme.unlistedContent.message"
|
|
26
|
+
description="The unlisted content banner message">
|
|
27
|
+
This page is unlisted. Search engines will not index it, and only users
|
|
28
|
+
having a direct link can access it.
|
|
29
|
+
</Translate>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function UnlistedMetadata(): JSX.Element {
|
|
34
|
+
return (
|
|
35
|
+
<Head>
|
|
36
|
+
<meta name="robots" content="noindex, nofollow" />
|
|
37
|
+
</Head>
|
|
38
|
+
);
|
|
39
|
+
}
|