@docusaurus/theme-common 2.4.1 → 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 +4 -3
- 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 +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -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/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 +40 -16
- 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 +1 -1
- package/lib/utils/skipToContentUtils.d.ts.map +1 -1
- package/lib/utils/skipToContentUtils.js +1 -0
- 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 +3 -3
- 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 +8 -1
- package/src/internal.ts +7 -3
- package/src/utils/ThemeClassNames.ts +6 -2
- 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 +57 -19
- package/src/utils/reactUtils.tsx +1 -15
- package/src/utils/scrollUtils.tsx +2 -2
- package/src/utils/skipToContentUtils.tsx +1 -0
- package/src/utils/storageUtils.ts +1 -1
- package/src/utils/tabsUtils.tsx +20 -18
- package/src/utils/unlistedUtils.tsx +39 -0
|
@@ -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
|
|
@@ -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;
|
|
@@ -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
|
|
@@ -93,6 +93,7 @@ export function SkipToContentLink(props: SkipToContentLinkProps): JSX.Element {
|
|
|
93
93
|
ref={containerRef}
|
|
94
94
|
role="region"
|
|
95
95
|
aria-label={DefaultSkipToContentLabel}>
|
|
96
|
+
{/* eslint-disable-next-line @docusaurus/no-html-links */}
|
|
96
97
|
<a
|
|
97
98
|
{...props}
|
|
98
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
|
+
}
|