@docusaurus/theme-classic 2.2.0 → 2.3.1
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/getSwizzleConfig.js +35 -1
- package/lib/theme/DocBreadcrumbs/Items/Home/index.d.ts +8 -0
- package/lib/theme/DocBreadcrumbs/Items/Home/index.js +29 -0
- package/lib/theme/DocBreadcrumbs/Items/Home/styles.module.css +14 -0
- package/lib/theme/DocBreadcrumbs/index.js +1 -19
- package/lib/theme/DocBreadcrumbs/styles.module.css +0 -8
- package/lib/theme/DocPage/Layout/Sidebar/ExpandButton/styles.module.css +3 -2
- package/lib/theme/DocPage/Layout/Sidebar/index.js +13 -8
- package/lib/theme/DocPage/Layout/Sidebar/styles.module.css +7 -0
- package/lib/theme/DocPage/Layout/styles.module.css +1 -0
- package/lib/theme/DocSidebar/Desktop/CollapseButton/styles.module.css +1 -0
- package/lib/theme/DocSidebar/Desktop/Content/index.js +6 -0
- package/lib/theme/DocSidebar/Desktop/styles.module.css +0 -6
- package/lib/theme/Heading/index.js +17 -8
- package/lib/theme/Layout/Provider/index.js +0 -2
- package/lib/theme/Layout/styles.module.css +2 -0
- package/lib/theme/Navbar/Layout/index.js +6 -0
- package/lib/theme/Tabs/index.js +65 -130
- package/package.json +14 -14
- package/src/getSwizzleConfig.ts +39 -1
- package/src/theme/DocBreadcrumbs/Items/Home/index.tsx +33 -0
- package/src/theme/DocBreadcrumbs/Items/Home/styles.module.css +14 -0
- package/src/theme/DocBreadcrumbs/index.tsx +1 -20
- package/src/theme/DocBreadcrumbs/styles.module.css +0 -8
- package/src/theme/DocPage/Layout/Sidebar/ExpandButton/styles.module.css +3 -2
- package/src/theme/DocPage/Layout/Sidebar/index.tsx +13 -8
- package/src/theme/DocPage/Layout/Sidebar/styles.module.css +7 -0
- package/src/theme/DocPage/Layout/styles.module.css +1 -0
- package/src/theme/DocSidebar/Desktop/CollapseButton/styles.module.css +1 -0
- package/src/theme/DocSidebar/Desktop/Content/index.tsx +6 -0
- package/src/theme/DocSidebar/Desktop/styles.module.css +0 -6
- package/src/theme/Heading/index.tsx +18 -8
- package/src/theme/Layout/Provider/index.tsx +0 -2
- package/src/theme/Layout/styles.module.css +2 -0
- package/src/theme/Navbar/Layout/index.tsx +6 -0
- package/src/theme/Tabs/index.tsx +81 -142
- package/src/theme-classic.d.ts +8 -25
package/src/getSwizzleConfig.ts
CHANGED
|
@@ -20,6 +20,14 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
|
|
20
20
|
description:
|
|
21
21
|
'The component used to render multi-line code blocks, generally used in Markdown files.',
|
|
22
22
|
},
|
|
23
|
+
'CodeBlock/Content': {
|
|
24
|
+
actions: {
|
|
25
|
+
eject: 'unsafe',
|
|
26
|
+
wrap: 'forbidden',
|
|
27
|
+
},
|
|
28
|
+
description:
|
|
29
|
+
'The folder containing components responsible for rendering different types of CodeBlock content.',
|
|
30
|
+
},
|
|
23
31
|
ColorModeToggle: {
|
|
24
32
|
actions: {
|
|
25
33
|
eject: 'safe',
|
|
@@ -28,6 +36,14 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
|
|
28
36
|
description:
|
|
29
37
|
'The color mode toggle to switch between light and dark mode.',
|
|
30
38
|
},
|
|
39
|
+
'DocBreadcrumbs/Items': {
|
|
40
|
+
actions: {
|
|
41
|
+
eject: 'unsafe',
|
|
42
|
+
wrap: 'forbidden', // Can't wrap a folder
|
|
43
|
+
},
|
|
44
|
+
description:
|
|
45
|
+
'The components responsible for rendering the breadcrumb items',
|
|
46
|
+
},
|
|
31
47
|
DocCardList: {
|
|
32
48
|
actions: {
|
|
33
49
|
eject: 'safe',
|
|
@@ -36,6 +52,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
|
|
36
52
|
description:
|
|
37
53
|
'The component responsible for rendering a list of sidebar items cards.\nNotable used on the category generated-index pages.',
|
|
38
54
|
},
|
|
55
|
+
'DocItem/TOC': {
|
|
56
|
+
actions: {
|
|
57
|
+
// Forbidden because it's a parent folder, makes the CLI crash atm
|
|
58
|
+
// TODO the CLI should rather support --eject
|
|
59
|
+
// Subfolders can be swizzled
|
|
60
|
+
eject: 'forbidden',
|
|
61
|
+
wrap: 'forbidden',
|
|
62
|
+
},
|
|
63
|
+
description:
|
|
64
|
+
'The DocItem TOC is not directly swizzle-able, but you can swizzle its sub-components.',
|
|
65
|
+
},
|
|
39
66
|
DocSidebar: {
|
|
40
67
|
actions: {
|
|
41
68
|
eject: 'unsafe', // Too much technical code in sidebar, not very safe atm
|
|
@@ -101,6 +128,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
|
|
101
128
|
},
|
|
102
129
|
description: 'The footer logo',
|
|
103
130
|
},
|
|
131
|
+
Icon: {
|
|
132
|
+
actions: {
|
|
133
|
+
// Forbidden because it's a parent folder, makes the CLI crash atm
|
|
134
|
+
// TODO the CLI should rather support --eject
|
|
135
|
+
// Subfolders can be swizzled
|
|
136
|
+
eject: 'forbidden',
|
|
137
|
+
wrap: 'forbidden',
|
|
138
|
+
},
|
|
139
|
+
description:
|
|
140
|
+
'The Icon folder is not directly swizzle-able, but you can swizzle its sub-components.',
|
|
141
|
+
},
|
|
104
142
|
'Icon/Arrow': {
|
|
105
143
|
actions: {
|
|
106
144
|
eject: 'safe',
|
|
@@ -220,7 +258,7 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
|
|
220
258
|
wrap: 'forbidden',
|
|
221
259
|
},
|
|
222
260
|
description:
|
|
223
|
-
'The Navbar item components mapping. Can be ejected to add custom navbar item types
|
|
261
|
+
'The Navbar item components mapping. Can be ejected to add custom navbar item types.\nSee https://github.com/facebook/docusaurus/issues/7227.',
|
|
224
262
|
},
|
|
225
263
|
NotFound: {
|
|
226
264
|
actions: {
|
|
@@ -0,0 +1,33 @@
|
|
|
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 Link from '@docusaurus/Link';
|
|
10
|
+
import useBaseUrl from '@docusaurus/useBaseUrl';
|
|
11
|
+
import {translate} from '@docusaurus/Translate';
|
|
12
|
+
import IconHome from '@theme/Icon/Home';
|
|
13
|
+
|
|
14
|
+
import styles from './styles.module.css';
|
|
15
|
+
|
|
16
|
+
export default function HomeBreadcrumbItem(): JSX.Element {
|
|
17
|
+
const homeHref = useBaseUrl('/');
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<li className="breadcrumbs__item">
|
|
21
|
+
<Link
|
|
22
|
+
aria-label={translate({
|
|
23
|
+
id: 'theme.docs.breadcrumbs.home',
|
|
24
|
+
message: 'Home page',
|
|
25
|
+
description: 'The ARIA label for the home page in the breadcrumbs',
|
|
26
|
+
})}
|
|
27
|
+
className="breadcrumbs__link"
|
|
28
|
+
href={homeHref}>
|
|
29
|
+
<IconHome className={styles.breadcrumbHomeIcon} />
|
|
30
|
+
</Link>
|
|
31
|
+
</li>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
.breadcrumbHomeIcon {
|
|
9
|
+
position: relative;
|
|
10
|
+
top: 1px;
|
|
11
|
+
vertical-align: top;
|
|
12
|
+
height: 1.1rem;
|
|
13
|
+
width: 1.1rem;
|
|
14
|
+
}
|
|
@@ -13,9 +13,8 @@ import {
|
|
|
13
13
|
useHomePageRoute,
|
|
14
14
|
} from '@docusaurus/theme-common/internal';
|
|
15
15
|
import Link from '@docusaurus/Link';
|
|
16
|
-
import useBaseUrl from '@docusaurus/useBaseUrl';
|
|
17
16
|
import {translate} from '@docusaurus/Translate';
|
|
18
|
-
import
|
|
17
|
+
import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
|
|
19
18
|
|
|
20
19
|
import styles from './styles.module.css';
|
|
21
20
|
|
|
@@ -79,24 +78,6 @@ function BreadcrumbsItem({
|
|
|
79
78
|
);
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
function HomeBreadcrumbItem() {
|
|
83
|
-
const homeHref = useBaseUrl('/');
|
|
84
|
-
return (
|
|
85
|
-
<li className="breadcrumbs__item">
|
|
86
|
-
<Link
|
|
87
|
-
aria-label={translate({
|
|
88
|
-
id: 'theme.docs.breadcrumbs.home',
|
|
89
|
-
message: 'Home page',
|
|
90
|
-
description: 'The ARIA label for the home page in the breadcrumbs',
|
|
91
|
-
})}
|
|
92
|
-
className={clsx('breadcrumbs__link', styles.breadcrumbsItemLink)}
|
|
93
|
-
href={homeHref}>
|
|
94
|
-
<IconHome className={styles.breadcrumbHomeIcon} />
|
|
95
|
-
</Link>
|
|
96
|
-
</li>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
81
|
export default function DocBreadcrumbs(): JSX.Element | null {
|
|
101
82
|
const breadcrumbs = useSidebarBreadcrumbs();
|
|
102
83
|
const homePageRoute = useHomePageRoute();
|
|
@@ -60,15 +60,20 @@ export default function DocPageLayoutSidebar({
|
|
|
60
60
|
}
|
|
61
61
|
}}>
|
|
62
62
|
<ResetOnSidebarChange>
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
<div
|
|
64
|
+
className={clsx(
|
|
65
|
+
styles.sidebarViewport,
|
|
66
|
+
hiddenSidebar && styles.sidebarViewportHidden,
|
|
67
|
+
)}>
|
|
68
|
+
<DocSidebar
|
|
69
|
+
sidebar={sidebar}
|
|
70
|
+
path={pathname}
|
|
71
|
+
onCollapse={toggleSidebar}
|
|
72
|
+
isHidden={hiddenSidebar}
|
|
73
|
+
/>
|
|
74
|
+
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
|
|
75
|
+
</div>
|
|
69
76
|
</ResetOnSidebarChange>
|
|
70
|
-
|
|
71
|
-
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
|
|
72
77
|
</aside>
|
|
73
78
|
);
|
|
74
79
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
useAnnouncementBar,
|
|
13
13
|
useScrollPosition,
|
|
14
14
|
} from '@docusaurus/theme-common/internal';
|
|
15
|
+
import {translate} from '@docusaurus/Translate';
|
|
15
16
|
import DocSidebarItems from '@theme/DocSidebarItems';
|
|
16
17
|
import type {Props} from '@theme/DocSidebar/Desktop/Content';
|
|
17
18
|
|
|
@@ -41,6 +42,11 @@ export default function DocSidebarDesktopContent({
|
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
44
|
<nav
|
|
45
|
+
aria-label={translate({
|
|
46
|
+
id: 'theme.docs.sidebar.navAriaLabel',
|
|
47
|
+
message: 'Docs sidebar',
|
|
48
|
+
description: 'The ARIA label for the sidebar navigation',
|
|
49
|
+
})}
|
|
44
50
|
className={clsx(
|
|
45
51
|
'menu thin-scrollbar',
|
|
46
52
|
styles.menu,
|
|
@@ -9,13 +9,9 @@
|
|
|
9
9
|
.sidebar {
|
|
10
10
|
display: flex;
|
|
11
11
|
flex-direction: column;
|
|
12
|
-
max-height: 100vh;
|
|
13
12
|
height: 100%;
|
|
14
|
-
position: sticky;
|
|
15
|
-
top: 0;
|
|
16
13
|
padding-top: var(--ifm-navbar-height);
|
|
17
14
|
width: var(--doc-sidebar-width);
|
|
18
|
-
transition: opacity 50ms ease;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
17
|
.sidebarWithHideableNavbar {
|
|
@@ -24,8 +20,6 @@
|
|
|
24
20
|
|
|
25
21
|
.sidebarHidden {
|
|
26
22
|
opacity: 0;
|
|
27
|
-
height: 0;
|
|
28
|
-
overflow: hidden;
|
|
29
23
|
visibility: hidden;
|
|
30
24
|
}
|
|
31
25
|
|
|
@@ -9,6 +9,7 @@ import React from 'react';
|
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import {translate} from '@docusaurus/Translate';
|
|
11
11
|
import {useThemeConfig} from '@docusaurus/theme-common';
|
|
12
|
+
import Link from '@docusaurus/Link';
|
|
12
13
|
import type {Props} from '@theme/Heading';
|
|
13
14
|
|
|
14
15
|
import styles from './styles.module.css';
|
|
@@ -22,6 +23,17 @@ export default function Heading({as: As, id, ...props}: Props): JSX.Element {
|
|
|
22
23
|
return <As {...props} id={undefined} />;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
const anchorTitle = translate(
|
|
27
|
+
{
|
|
28
|
+
id: 'theme.common.headingLinkTitle',
|
|
29
|
+
message: 'Direct link to {heading}',
|
|
30
|
+
description: 'Title for link to heading',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
heading: typeof props.children === 'string' ? props.children : id,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
25
37
|
return (
|
|
26
38
|
<As
|
|
27
39
|
{...props}
|
|
@@ -30,19 +42,17 @@ export default function Heading({as: As, id, ...props}: Props): JSX.Element {
|
|
|
30
42
|
hideOnScroll
|
|
31
43
|
? styles.anchorWithHideOnScrollNavbar
|
|
32
44
|
: styles.anchorWithStickyNavbar,
|
|
45
|
+
props.className,
|
|
33
46
|
)}
|
|
34
47
|
id={id}>
|
|
35
48
|
{props.children}
|
|
36
|
-
<
|
|
49
|
+
<Link
|
|
37
50
|
className="hash-link"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
message: 'Direct link to heading',
|
|
42
|
-
description: 'Title for link to heading',
|
|
43
|
-
})}>
|
|
51
|
+
to={`#${id}`}
|
|
52
|
+
aria-label={anchorTitle}
|
|
53
|
+
title={anchorTitle}>
|
|
44
54
|
​
|
|
45
|
-
</
|
|
55
|
+
</Link>
|
|
46
56
|
</As>
|
|
47
57
|
);
|
|
48
58
|
}
|
|
@@ -9,7 +9,6 @@ import React from 'react';
|
|
|
9
9
|
import {composeProviders} from '@docusaurus/theme-common';
|
|
10
10
|
import {
|
|
11
11
|
ColorModeProvider,
|
|
12
|
-
TabGroupChoiceProvider,
|
|
13
12
|
AnnouncementBarProvider,
|
|
14
13
|
DocsPreferredVersionContextProvider,
|
|
15
14
|
ScrollControllerProvider,
|
|
@@ -21,7 +20,6 @@ import type {Props} from '@theme/Layout/Provider';
|
|
|
21
20
|
const Provider = composeProviders([
|
|
22
21
|
ColorModeProvider,
|
|
23
22
|
AnnouncementBarProvider,
|
|
24
|
-
TabGroupChoiceProvider,
|
|
25
23
|
ScrollControllerProvider,
|
|
26
24
|
DocsPreferredVersionContextProvider,
|
|
27
25
|
PluginHtmlClassNameProvider,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
useHideableNavbar,
|
|
13
13
|
useNavbarMobileSidebar,
|
|
14
14
|
} from '@docusaurus/theme-common/internal';
|
|
15
|
+
import {translate} from '@docusaurus/Translate';
|
|
15
16
|
import NavbarMobileSidebar from '@theme/Navbar/MobileSidebar';
|
|
16
17
|
import type {Props} from '@theme/Navbar/Layout';
|
|
17
18
|
|
|
@@ -36,6 +37,11 @@ export default function NavbarLayout({children}: Props): JSX.Element {
|
|
|
36
37
|
return (
|
|
37
38
|
<nav
|
|
38
39
|
ref={navbarRef}
|
|
40
|
+
aria-label={translate({
|
|
41
|
+
id: 'theme.NavBar.navAriaLabel',
|
|
42
|
+
message: 'Main',
|
|
43
|
+
description: 'The ARIA label for the main navigation',
|
|
44
|
+
})}
|
|
39
45
|
className={clsx(
|
|
40
46
|
'navbar',
|
|
41
47
|
'navbar--fixed-top',
|
package/src/theme/Tabs/index.tsx
CHANGED
|
@@ -5,104 +5,27 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, {
|
|
9
|
-
useState,
|
|
10
|
-
cloneElement,
|
|
11
|
-
isValidElement,
|
|
12
|
-
type ReactElement,
|
|
13
|
-
} from 'react';
|
|
8
|
+
import React, {cloneElement} from 'react';
|
|
14
9
|
import clsx from 'clsx';
|
|
15
|
-
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
16
|
-
import {duplicates} from '@docusaurus/theme-common';
|
|
17
10
|
import {
|
|
18
11
|
useScrollPositionBlocker,
|
|
19
|
-
|
|
12
|
+
useTabs,
|
|
20
13
|
} from '@docusaurus/theme-common/internal';
|
|
14
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
21
15
|
import type {Props} from '@theme/Tabs';
|
|
22
|
-
import type {Props as TabItemProps} from '@theme/TabItem';
|
|
23
|
-
|
|
24
16
|
import styles from './styles.module.css';
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function TabsComponent(props: Props): JSX.Element {
|
|
35
|
-
const {
|
|
36
|
-
lazy,
|
|
37
|
-
block,
|
|
38
|
-
defaultValue: defaultValueProp,
|
|
39
|
-
values: valuesProp,
|
|
40
|
-
groupId,
|
|
41
|
-
className,
|
|
42
|
-
} = props;
|
|
43
|
-
const children = React.Children.map(props.children, (child) => {
|
|
44
|
-
if (isValidElement(child) && isTabItem(child)) {
|
|
45
|
-
return child;
|
|
46
|
-
}
|
|
47
|
-
// child.type.name will give non-sensical values in prod because of
|
|
48
|
-
// minification, but we assume it won't throw in prod.
|
|
49
|
-
throw new Error(
|
|
50
|
-
`Docusaurus error: Bad <Tabs> child <${
|
|
51
|
-
// @ts-expect-error: guarding against unexpected cases
|
|
52
|
-
typeof child.type === 'string' ? child.type : child.type.name
|
|
53
|
-
}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`,
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
const values =
|
|
57
|
-
valuesProp ??
|
|
58
|
-
// Only pick keys that we recognize. MDX would inject some keys by default
|
|
59
|
-
children.map(({props: {value, label, attributes}}) => ({
|
|
60
|
-
value,
|
|
61
|
-
label,
|
|
62
|
-
attributes,
|
|
63
|
-
}));
|
|
64
|
-
const dup = duplicates(values, (a, b) => a.value === b.value);
|
|
65
|
-
if (dup.length > 0) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`Docusaurus error: Duplicate values "${dup
|
|
68
|
-
.map((a) => a.value)
|
|
69
|
-
.join(', ')}" found in <Tabs>. Every value needs to be unique.`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
// When defaultValueProp is null, don't show a default tab
|
|
73
|
-
const defaultValue =
|
|
74
|
-
defaultValueProp === null
|
|
75
|
-
? defaultValueProp
|
|
76
|
-
: defaultValueProp ??
|
|
77
|
-
children.find((child) => child.props.default)?.props.value ??
|
|
78
|
-
children[0]!.props.value;
|
|
79
|
-
if (defaultValue !== null && !values.some((a) => a.value === defaultValue)) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${values
|
|
82
|
-
.map((a) => a.value)
|
|
83
|
-
.join(
|
|
84
|
-
', ',
|
|
85
|
-
)}. If you intend to show no default tab, use defaultValue={null} instead.`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
|
|
90
|
-
const [selectedValue, setSelectedValue] = useState(defaultValue);
|
|
18
|
+
function TabList({
|
|
19
|
+
className,
|
|
20
|
+
block,
|
|
21
|
+
selectedValue,
|
|
22
|
+
selectValue,
|
|
23
|
+
tabValues,
|
|
24
|
+
}: Props & ReturnType<typeof useTabs>) {
|
|
91
25
|
const tabRefs: (HTMLLIElement | null)[] = [];
|
|
92
26
|
const {blockElementScrollPositionUntilNextRender} =
|
|
93
27
|
useScrollPositionBlocker();
|
|
94
28
|
|
|
95
|
-
if (groupId != null) {
|
|
96
|
-
const relevantTabGroupChoice = tabGroupChoices[groupId];
|
|
97
|
-
if (
|
|
98
|
-
relevantTabGroupChoice != null &&
|
|
99
|
-
relevantTabGroupChoice !== selectedValue &&
|
|
100
|
-
values.some((value) => value.value === relevantTabGroupChoice)
|
|
101
|
-
) {
|
|
102
|
-
setSelectedValue(relevantTabGroupChoice);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
29
|
const handleTabChange = (
|
|
107
30
|
event:
|
|
108
31
|
| React.FocusEvent<HTMLLIElement>
|
|
@@ -111,15 +34,11 @@ function TabsComponent(props: Props): JSX.Element {
|
|
|
111
34
|
) => {
|
|
112
35
|
const newTab = event.currentTarget;
|
|
113
36
|
const newTabIndex = tabRefs.indexOf(newTab);
|
|
114
|
-
const newTabValue =
|
|
37
|
+
const newTabValue = tabValues[newTabIndex]!.value;
|
|
115
38
|
|
|
116
39
|
if (newTabValue !== selectedValue) {
|
|
117
40
|
blockElementScrollPositionUntilNextRender(newTab);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (groupId != null) {
|
|
121
|
-
setTabGroupChoices(groupId, String(newTabValue));
|
|
122
|
-
}
|
|
41
|
+
selectValue(newTabValue);
|
|
123
42
|
}
|
|
124
43
|
};
|
|
125
44
|
|
|
@@ -149,61 +68,81 @@ function TabsComponent(props: Props): JSX.Element {
|
|
|
149
68
|
};
|
|
150
69
|
|
|
151
70
|
return (
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
71
|
+
<ul
|
|
72
|
+
role="tablist"
|
|
73
|
+
aria-orientation="horizontal"
|
|
74
|
+
className={clsx(
|
|
75
|
+
'tabs',
|
|
76
|
+
{
|
|
77
|
+
'tabs--block': block,
|
|
78
|
+
},
|
|
79
|
+
className,
|
|
80
|
+
)}>
|
|
81
|
+
{tabValues.map(({value, label, attributes}) => (
|
|
82
|
+
<li
|
|
83
|
+
// TODO extract TabListItem
|
|
84
|
+
role="tab"
|
|
85
|
+
tabIndex={selectedValue === value ? 0 : -1}
|
|
86
|
+
aria-selected={selectedValue === value}
|
|
87
|
+
key={value}
|
|
88
|
+
ref={(tabControl) => tabRefs.push(tabControl)}
|
|
89
|
+
onKeyDown={handleKeydown}
|
|
90
|
+
onClick={handleTabChange}
|
|
91
|
+
{...attributes}
|
|
92
|
+
className={clsx(
|
|
93
|
+
'tabs__item',
|
|
94
|
+
styles.tabItem,
|
|
95
|
+
attributes?.className as string,
|
|
96
|
+
{
|
|
97
|
+
'tabs__item--active': selectedValue === value,
|
|
98
|
+
},
|
|
99
|
+
)}>
|
|
100
|
+
{label ?? value}
|
|
101
|
+
</li>
|
|
102
|
+
))}
|
|
103
|
+
</ul>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
185
106
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
107
|
+
function TabContent({
|
|
108
|
+
lazy,
|
|
109
|
+
children,
|
|
110
|
+
selectedValue,
|
|
111
|
+
}: Props & ReturnType<typeof useTabs>) {
|
|
112
|
+
// eslint-disable-next-line no-param-reassign
|
|
113
|
+
children = Array.isArray(children) ? children : [children];
|
|
114
|
+
if (lazy) {
|
|
115
|
+
const selectedTabItem = children.find(
|
|
116
|
+
(tabItem) => tabItem.props.value === selectedValue,
|
|
117
|
+
);
|
|
118
|
+
if (!selectedTabItem) {
|
|
119
|
+
// fail-safe or fail-fast? not sure what's best here
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return cloneElement(selectedTabItem, {className: 'margin-top--md'});
|
|
123
|
+
}
|
|
124
|
+
return (
|
|
125
|
+
<div className="margin-top--md">
|
|
126
|
+
{children.map((tabItem, i) =>
|
|
127
|
+
cloneElement(tabItem, {
|
|
128
|
+
key: i,
|
|
129
|
+
hidden: tabItem.props.value !== selectedValue,
|
|
130
|
+
}),
|
|
202
131
|
)}
|
|
203
132
|
</div>
|
|
204
133
|
);
|
|
205
134
|
}
|
|
206
135
|
|
|
136
|
+
function TabsComponent(props: Props): JSX.Element {
|
|
137
|
+
const tabs = useTabs(props);
|
|
138
|
+
return (
|
|
139
|
+
<div className={clsx('tabs-container', styles.tabList)}>
|
|
140
|
+
<TabList {...props} {...tabs} />
|
|
141
|
+
<TabContent {...props} {...tabs} />
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
207
146
|
export default function Tabs(props: Props): JSX.Element {
|
|
208
147
|
const isBrowser = useIsBrowser();
|
|
209
148
|
return (
|