mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 23:57:22 +02:00
feat(theme-classic): allow passing additional attributes to tab headings (#6082)
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
This commit is contained in:
parent
74aa87242f
commit
fa3926e2a1
5 changed files with 127 additions and 11 deletions
|
@ -516,6 +516,7 @@ declare module '@theme/TabItem' {
|
||||||
readonly label?: string;
|
readonly label?: string;
|
||||||
readonly hidden?: boolean;
|
readonly hidden?: boolean;
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
|
readonly attributes?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TabItem: (props: Props) => JSX.Element;
|
const TabItem: (props: Props) => JSX.Element;
|
||||||
|
@ -531,7 +532,11 @@ declare module '@theme/Tabs' {
|
||||||
readonly block?: boolean;
|
readonly block?: boolean;
|
||||||
readonly children: readonly ReactElement<TabItemProps>[];
|
readonly children: readonly ReactElement<TabItemProps>[];
|
||||||
readonly defaultValue?: string | null;
|
readonly defaultValue?: string | null;
|
||||||
readonly values?: readonly {value: string; label?: string}[];
|
readonly values?: readonly {
|
||||||
|
value: string;
|
||||||
|
label?: string;
|
||||||
|
attributes?: Record<string, unknown>;
|
||||||
|
}[];
|
||||||
readonly groupId?: string;
|
readonly groupId?: string;
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,13 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const values =
|
const values =
|
||||||
valuesProp ?? children.map(({props: {value, label}}) => ({value, label}));
|
valuesProp ??
|
||||||
|
// We only pick keys that we recognize. MDX would inject some keys by default
|
||||||
|
children.map(({props: {value, label, attributes}}) => ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
attributes,
|
||||||
|
}));
|
||||||
const dup = duplicates(values, (a, b) => a.value === b.value);
|
const dup = duplicates(values, (a, b) => a.value === b.value);
|
||||||
if (dup.length > 0) {
|
if (dup.length > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -144,19 +150,25 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
},
|
},
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{values.map(({value, label}) => (
|
{values.map(({value, label, attributes}) => (
|
||||||
<li
|
<li
|
||||||
role="tab"
|
role="tab"
|
||||||
tabIndex={selectedValue === value ? 0 : -1}
|
tabIndex={selectedValue === value ? 0 : -1}
|
||||||
aria-selected={selectedValue === value}
|
aria-selected={selectedValue === value}
|
||||||
className={clsx('tabs__item', styles.tabItem, {
|
|
||||||
'tabs__item--active': selectedValue === value,
|
|
||||||
})}
|
|
||||||
key={value}
|
key={value}
|
||||||
ref={(tabControl) => tabRefs.push(tabControl)}
|
ref={(tabControl) => tabRefs.push(tabControl)}
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
onFocus={handleTabChange}
|
onFocus={handleTabChange}
|
||||||
onClick={handleTabChange}>
|
onClick={handleTabChange}
|
||||||
|
{...attributes}
|
||||||
|
className={clsx(
|
||||||
|
'tabs__item',
|
||||||
|
styles.tabItem,
|
||||||
|
attributes?.className as string,
|
||||||
|
{
|
||||||
|
'tabs__item--active': selectedValue === value,
|
||||||
|
},
|
||||||
|
)}>
|
||||||
{label ?? value}
|
{label ?? value}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.red[aria-selected="true"] {
|
||||||
|
border-bottom-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.orange[aria-selected="true"] {
|
||||||
|
border-bottom-color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow {
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
.yellow[aria-selected="true"] {
|
||||||
|
border-bottom-color: yellow;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ slug: /markdown-features/tabs
|
||||||
import BrowserWindow from '@site/src/components/BrowserWindow';
|
import BrowserWindow from '@site/src/components/BrowserWindow';
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
import TabItem from '@theme/TabItem';
|
import TabItem from '@theme/TabItem';
|
||||||
|
import styles from './markdown-features-tabs-styles.module.css';
|
||||||
```
|
```
|
||||||
|
|
||||||
Docusaurus provides `<Tabs>` components that you can use thanks to [MDX](./markdown-features-react.mdx):
|
Docusaurus provides `<Tabs>` components that you can use thanks to [MDX](./markdown-features-react.mdx):
|
||||||
|
@ -229,15 +230,12 @@ Tab choices with different `groupId`s will not interfere with each other:
|
||||||
You might want to customize the appearance of certain set of tabs. To do that you can pass the string in `className` prop and the specified CSS class will be added to the `Tabs` component:
|
You might want to customize the appearance of certain set of tabs. To do that you can pass the string in `className` prop and the specified CSS class will be added to the `Tabs` component:
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import Tabs from '@theme/Tabs';
|
|
||||||
import TabItem from '@theme/TabItem';
|
|
||||||
|
|
||||||
// highlight-next-line
|
// highlight-next-line
|
||||||
<Tabs className="unique-tabs">
|
<Tabs className="unique-tabs">
|
||||||
<TabItem value="Apple">This is an apple 🍎</TabItem>
|
<TabItem value="Apple">This is an apple 🍎</TabItem>
|
||||||
<TabItem value="Orange">This is an orange 🍊</TabItem>
|
<TabItem value="Orange">This is an orange 🍊</TabItem>
|
||||||
<TabItem value="Banana">This is a banana 🍌</TabItem>
|
<TabItem value="Banana">This is a banana 🍌</TabItem>
|
||||||
</Tabs>;
|
</Tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
```mdx-code-block
|
```mdx-code-block
|
||||||
|
@ -249,3 +247,76 @@ import TabItem from '@theme/TabItem';
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</BrowserWindow>
|
</BrowserWindow>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Customizing tab headings
|
||||||
|
|
||||||
|
You can also customize each tab heading independently by using the `attributes` field. The extra props can be passed to the headings either through the `values` prop in `Tabs`, or props of each `TabItem`—in the same way as you declare `label`.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
```jsx title="some-doc.mdx"
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
|
||||||
|
This is an apple 🍎
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
|
||||||
|
This is an orange 🍊
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
|
||||||
|
This is a banana 🍌
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
```
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
```css title="styles.module.css"
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.red[aria-selected='true'] {
|
||||||
|
border-bottom-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.orange[aria-selected='true'] {
|
||||||
|
border-bottom-color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow {
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
.yellow[aria-selected='true'] {
|
||||||
|
border-bottom-color: yellow;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mdx-code-block
|
||||||
|
<BrowserWindow>
|
||||||
|
<Tabs>
|
||||||
|
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
|
||||||
|
This is an apple 🍎
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
|
||||||
|
This is an orange 🍊
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
|
||||||
|
This is a banana 🍌
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</BrowserWindow>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
`className` would be merged with other default class names. You may also use a custom `data-value` field (`{'data-value': 'apple'}`) paired with CSS attribute selectors:
|
||||||
|
|
||||||
|
```css title="styles.module.css"
|
||||||
|
li[role='tab'][data-value='apple'] {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
border-top-left-radius: var(--ifm-global-radius);
|
border-top-left-radius: var(--ifm-global-radius);
|
||||||
border-top-right-radius: var(--ifm-global-radius);
|
border-top-right-radius: var(--ifm-global-radius);
|
||||||
box-shadow: var(--ifm-global-shadow-lw);
|
box-shadow: var(--ifm-global-shadow-lw);
|
||||||
|
margin-bottom: var(--ifm-leading);
|
||||||
}
|
}
|
||||||
|
|
||||||
.browserWindowHeader {
|
.browserWindowHeader {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue