Merge pull request #32232 from Believe-SA/blv/button-toggle
feat(bui): Introduce ToggleButton/ToggleButtonGroup
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': minor
|
||||
---
|
||||
|
||||
Introduce new `ToggleButton` & `ToggleButtonGroup` components in Backstage UI
|
||||
@@ -0,0 +1,126 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
toggleButtonGroupPropDefs,
|
||||
toggleButtonGroupUsageSnippet,
|
||||
toggleButtonGroupSingleSnippet,
|
||||
toggleButtonGroupMultipleSnippet,
|
||||
toggleButtonGroupVerticalSnippet,
|
||||
toggleButtonGroupDisabledSnippet,
|
||||
toggleButtonGroupDisallowEmptySnippet,
|
||||
toggleButtonGroupIconsSnippet,
|
||||
toggleButtonGroupIconsOnlySnippet,
|
||||
toggleButtonGroupSurfacesSnippet,
|
||||
} from './toggle-button-group.props';
|
||||
import { ToggleButtonGroupSnippet } from '@/snippets/stories-snippets';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { ToggleButtonGroupDefinition } from '../utils/definitions';
|
||||
|
||||
<PageTitle
|
||||
title="ToggleButtonGroup"
|
||||
description="A grouping container for related [ToggleButtons](./toggle-button) supporting single or multiple selection."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<ToggleButtonGroupSnippet story="SingleSelection" />}
|
||||
code={toggleButtonGroupSingleSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={toggleButtonGroupUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={toggleButtonGroupPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Surfaces
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="Surfaces" />}
|
||||
code={toggleButtonGroupSurfacesSnippet}
|
||||
/>
|
||||
|
||||
### Single Selection
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="SingleSelection" />}
|
||||
code={toggleButtonGroupSingleSnippet}
|
||||
/>
|
||||
|
||||
### Multiple Selection
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="MultipleSelection" />}
|
||||
code={toggleButtonGroupMultipleSnippet}
|
||||
/>
|
||||
|
||||
### Icons and Text
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="WithIcons" />}
|
||||
code={toggleButtonGroupIconsSnippet}
|
||||
/>
|
||||
|
||||
### Icons Only
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="IconsOnly" />}
|
||||
code={toggleButtonGroupIconsOnlySnippet}
|
||||
/>
|
||||
|
||||
### Disallow Empty Selection
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="DisallowEmptySelection" />}
|
||||
code={toggleButtonGroupDisallowEmptySnippet}
|
||||
/>
|
||||
|
||||
### Vertical Orientation
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="Orientation" />}
|
||||
code={toggleButtonGroupVerticalSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonGroupSnippet story="DisabledGroup" />}
|
||||
code={toggleButtonGroupDisabledSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={ToggleButtonGroupDefinition} />
|
||||
|
||||
<ChangelogComponent component="toggle-button-group" />
|
||||
@@ -0,0 +1,142 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const toggleButtonGroupPropDefs: Record<string, PropDef> = {
|
||||
selectionMode: {
|
||||
type: 'enum',
|
||||
values: ['single', 'multiple'],
|
||||
default: 'single',
|
||||
},
|
||||
orientation: {
|
||||
type: 'enum',
|
||||
values: ['horizontal', 'vertical'],
|
||||
default: 'horizontal',
|
||||
responsive: true,
|
||||
},
|
||||
selectedKeys: { type: 'enum', values: ['Iterable<Key>'] },
|
||||
defaultSelectedKeys: { type: 'enum', values: ['Iterable<Key>'] },
|
||||
onSelectionChange: { type: 'enum', values: ['(keys) => void'] },
|
||||
isDisabled: { type: 'boolean', default: 'false' },
|
||||
disallowEmptySelection: { type: 'boolean', default: 'false' },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const toggleButtonGroupUsageSnippet = `import { ToggleButtonGroup, ToggleButton } from '@backstage/ui';
|
||||
|
||||
<ToggleButtonGroup selectionMode="single">
|
||||
<ToggleButton id="dogs">Dogs</ToggleButton>
|
||||
<ToggleButton id="cats">Cats</ToggleButton>
|
||||
<ToggleButton id="birds">Birds</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupSingleSnippet = `<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['dogs']}>
|
||||
<ToggleButton id="dogs">Dogs</ToggleButton>
|
||||
<ToggleButton id="cat">Cats</ToggleButton>
|
||||
<ToggleButton id="bird">Birds</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupMultipleSnippet = `<ToggleButtonGroup selectionMode="multiple" defaultSelectedKeys={['frontend']}>
|
||||
<ToggleButton id="frontend">Frontend</ToggleButton>
|
||||
<ToggleButton id="backend">Backend</ToggleButton>
|
||||
<ToggleButton id="platform">Platform</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupVerticalSnippet = `<ToggleButtonGroup selectionMode="single" orientation="vertical">
|
||||
<ToggleButton id="morning">Morning</ToggleButton>
|
||||
<ToggleButton id="afternoon">Afternoon</ToggleButton>
|
||||
<ToggleButton id="evening">Evening</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupDisabledSnippet = `<ToggleButtonGroup selectionMode="single" isDisabled>
|
||||
<ToggleButton id="cat">Cat</ToggleButton>
|
||||
<ToggleButton id="dog">Dog</ToggleButton>
|
||||
<ToggleButton id="bird">Bird</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupDisallowEmptySnippet = `<ToggleButtonGroup selectionMode="single" disallowEmptySelection defaultSelectedKeys={['one']}>
|
||||
<ToggleButton id="one">One</ToggleButton>
|
||||
<ToggleButton id="two">Two</ToggleButton>
|
||||
<ToggleButton id="three">Three</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupIconsSnippet = `import { RiCloudLine, RiStarFill, RiStarLine, RiArrowRightSLine } from '@remixicon/react';
|
||||
|
||||
<ToggleButtonGroup selectionMode="multiple" defaultSelectedKeys={['cloud']}>
|
||||
<ToggleButton id="cloud" aria-label="Cloud" iconStart={<RiCloudLine />} />
|
||||
<ToggleButton
|
||||
id="starred"
|
||||
aria-label="Starred"
|
||||
iconStart={<RiStarFill />}
|
||||
/>
|
||||
<ToggleButton id="star" iconStart={<RiStarLine />}>
|
||||
Star
|
||||
</ToggleButton>
|
||||
<ToggleButton id="next" iconEnd={<RiArrowRightSLine />}>
|
||||
Next
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupIconsOnlySnippet = `import { RiCloudLine, RiStarLine, RiArrowRightSLine } from '@remixicon/react';
|
||||
|
||||
<ToggleButtonGroup selectionMode="multiple" defaultSelectedKeys={['cloud']}>
|
||||
<ToggleButton id="cloud" iconStart={<RiCloudLine />} />
|
||||
<ToggleButton id="star" iconStart={<RiStarLine />} />
|
||||
<ToggleButton id="next" iconEnd={<RiArrowRightSLine />} />
|
||||
</ToggleButtonGroup>`;
|
||||
|
||||
export const toggleButtonGroupSurfacesSnippet = `<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default</Text>
|
||||
<Flex align="center" p="4" gap="4">
|
||||
<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['option1']}>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4" gap="4">
|
||||
<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['option1']}>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4" gap="4">
|
||||
<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['option1']}>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4" gap="4">
|
||||
<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['option1']}>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4" gap="4">
|
||||
<ToggleButtonGroup selectionMode="single" defaultSelectedKeys={['option1']}>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
@@ -0,0 +1,106 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { ToggleButtonSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
toggleButtonPropDefs,
|
||||
toggleButtonUsageSnippet,
|
||||
toggleButtonSurfacesSnippet,
|
||||
toggleButtonSizesSnippet,
|
||||
toggleButtonIconsSnippet,
|
||||
toggleButtonDisabledSnippet,
|
||||
toggleButtonControlledSnippet,
|
||||
toggleButtonFunctionChildrenSnippet,
|
||||
} from './toggle-button.props';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { ToggleButtonDefinition } from '../utils/definitions';
|
||||
|
||||
<PageTitle
|
||||
title="ToggleButton"
|
||||
description="A button that toggles between selected and unselected states. Can be used as a single toggle or as part of a [ToggleButtonGroup](./toggle-button-group)."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<ToggleButtonSnippet story="Default" />}
|
||||
code={toggleButtonUsageSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={toggleButtonUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={toggleButtonPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Surfaces
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="Surfaces" />}
|
||||
code={toggleButtonSurfacesSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="Sizes" />}
|
||||
code={toggleButtonSizesSnippet}
|
||||
/>
|
||||
|
||||
### With Icons
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="WithIcons" />}
|
||||
code={toggleButtonIconsSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="Disabled" />}
|
||||
code={toggleButtonDisabledSnippet}
|
||||
/>
|
||||
|
||||
### Controlled
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="Controlled" />}
|
||||
code={toggleButtonControlledSnippet}
|
||||
/>
|
||||
|
||||
### Dynamic Content with Function Children
|
||||
|
||||
The `children` prop can be a function that receives render props, allowing you to dynamically customize the button content based on component state (such as `isSelected`, `isDisabled`, `isHovered`, etc.).
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ToggleButtonSnippet story="DynamicContent" />}
|
||||
code={toggleButtonFunctionChildrenSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={ToggleButtonDefinition} />
|
||||
|
||||
<ChangelogComponent component="toggle-button" />
|
||||
@@ -0,0 +1,195 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const toggleButtonPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['small', 'medium'],
|
||||
default: 'small',
|
||||
responsive: true,
|
||||
},
|
||||
onSurface: {
|
||||
type: 'enum',
|
||||
values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
|
||||
description: 'Surface level this toggle is placed on',
|
||||
responsive: true,
|
||||
},
|
||||
iconStart: { type: 'enum', values: ['ReactNode'] },
|
||||
iconEnd: { type: 'enum', values: ['ReactNode'] },
|
||||
isSelected: { type: 'boolean' },
|
||||
defaultSelected: { type: 'boolean' },
|
||||
onChange: { type: 'enum', values: ['(isSelected: boolean) => void'] },
|
||||
isDisabled: { type: 'boolean', default: 'false' },
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode', '(values: ToggleButtonRenderProps) => ReactNode'],
|
||||
description:
|
||||
'The children of the component. A function may be provided to alter the children based on component state (such as `isSelected`, `isDisabled`, `isHovered`, etc.).',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const toggleButtonUsageSnippet = `import { ToggleButton } from '@backstage/ui';
|
||||
|
||||
<ToggleButton>Toggle</ToggleButton>`;
|
||||
|
||||
export const toggleButtonSurfacesSnippet = `<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default</Text>
|
||||
<Flex align="center" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
|
||||
export const toggleButtonSizesSnippet = `<Flex align="center">
|
||||
<ToggleButton size="small">Small</ToggleButton>
|
||||
<ToggleButton size="medium">Medium</ToggleButton>
|
||||
</Flex>`;
|
||||
|
||||
export const toggleButtonIconsSnippet = `import { RiStarLine, RiStarFill, RiCheckLine } from '@remixicon/react';
|
||||
|
||||
<Flex align="center">
|
||||
<ToggleButton iconStart={<RiStarLine />}>Favorite</ToggleButton>
|
||||
<ToggleButton iconStart={<RiStarFill />} defaultSelected>Starred</ToggleButton>
|
||||
<ToggleButton iconEnd={<RiCheckLine />}>Confirm</ToggleButton>
|
||||
</Flex>`;
|
||||
|
||||
export const toggleButtonDisabledSnippet = `<Flex align="center">
|
||||
<ToggleButton isDisabled>Disabled</ToggleButton>
|
||||
<ToggleButton defaultSelected isDisabled>Selected</ToggleButton>
|
||||
</Flex>`;
|
||||
|
||||
export const toggleButtonControlledSnippet = `import { useState } from 'react';
|
||||
import { RiStarFill, RiStarLine } from '@remixicon/react';
|
||||
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
<ToggleButton
|
||||
isSelected={selected}
|
||||
onChange={setSelected}
|
||||
iconStart={selected ? <RiStarFill /> : <RiStarLine />}
|
||||
>
|
||||
{selected ? 'Starred' : 'Not starred'}
|
||||
</ToggleButton>`;
|
||||
|
||||
export const toggleButtonFunctionChildrenSnippet = `import { RiStarFill, RiStarLine } from '@remixicon/react';
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 1: Selection State</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 2: Multiple States</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected, isHovered }) => {
|
||||
const states = [];
|
||||
if (isSelected) states.push('on');
|
||||
else states.push('off');
|
||||
if (isHovered) states.push('hovered');
|
||||
return \`Email (\${states.join(', ')})\`;
|
||||
}}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected, isHovered }) => {
|
||||
const states = [];
|
||||
if (isSelected) states.push('on');
|
||||
else states.push('off');
|
||||
if (isHovered) states.push('hovered');
|
||||
return \`Push (\${states.join(', ')})\`;
|
||||
}}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 3: Conditional Icons</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (
|
||||
<>
|
||||
{isSelected ? <RiStarFill /> : <RiStarLine />}
|
||||
<span>{isSelected ? 'Starred' : 'Star'}</span>
|
||||
</>
|
||||
)}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 4: Status Indicators</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected }) => (
|
||||
<Flex align="center" gap="2">
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isSelected
|
||||
? 'var(--bui-fg-success)'
|
||||
: 'var(--bui-fg-secondary)',
|
||||
}}
|
||||
/>
|
||||
<span>Active</span>
|
||||
</Flex>
|
||||
)}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (
|
||||
<Flex align="center" gap="2">
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isSelected
|
||||
? 'var(--bui-fg-danger)'
|
||||
: 'var(--bui-fg-secondary)',
|
||||
}}
|
||||
/>
|
||||
<span>Inactive</span>
|
||||
</Flex>
|
||||
)}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
@@ -30,6 +30,8 @@ import * as TableStories from '../../../packages/ui/src/components/Table/stories
|
||||
import * as TagGroupStories from '../../../packages/ui/src/components/TagGroup/TagGroup.stories';
|
||||
import * as PasswordFieldStories from '../../../packages/ui/src/components/PasswordField/PasswordField.stories';
|
||||
import * as VisuallyHiddenStories from '../../../packages/ui/src/components/VisuallyHidden/VisuallyHidden.stories';
|
||||
import * as ToggleButtonStories from '../../../packages/ui/src/components/ToggleButton/ToggleButton.stories';
|
||||
import * as ToggleButtonGroupStories from '../../../packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories';
|
||||
|
||||
// Helper function to create snippet components
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -75,6 +77,10 @@ export const HeaderSnippet = createSnippetComponent(HeaderStories);
|
||||
export const HeaderPageSnippet = createSnippetComponent(HeaderPageStories);
|
||||
export const TableSnippet = createSnippetComponent(TableStories);
|
||||
export const TagGroupSnippet = createSnippetComponent(TagGroupStories);
|
||||
export const ToggleButtonSnippet = createSnippetComponent(ToggleButtonStories);
|
||||
export const ToggleButtonGroupSnippet = createSnippetComponent(
|
||||
ToggleButtonGroupStories,
|
||||
);
|
||||
export const VisuallyHiddenSnippet = createSnippetComponent(
|
||||
VisuallyHiddenStories,
|
||||
);
|
||||
|
||||
@@ -116,6 +116,14 @@ export const components: Page[] = [
|
||||
title: 'TextField',
|
||||
slug: 'text-field',
|
||||
},
|
||||
{
|
||||
title: 'ToggleButton',
|
||||
slug: 'toggle-button',
|
||||
},
|
||||
{
|
||||
title: 'ToggleButtonGroup',
|
||||
slug: 'toggle-button-group',
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
slug: 'tooltip',
|
||||
|
||||
@@ -51,6 +51,8 @@ import type { TagGroupProps as TagGroupProps_2 } from 'react-aria-components';
|
||||
import type { TagListProps } from 'react-aria-components';
|
||||
import type { TagProps as TagProps_2 } from 'react-aria-components';
|
||||
import type { TextFieldProps as TextFieldProps_2 } from 'react-aria-components';
|
||||
import type { ToggleButtonGroupProps as ToggleButtonGroupProps_2 } from 'react-aria-components';
|
||||
import type { ToggleButtonProps as ToggleButtonProps_2 } from 'react-aria-components';
|
||||
import { TooltipProps as TooltipProps_2 } from 'react-aria-components';
|
||||
import { TooltipTriggerComponentProps } from 'react-aria-components';
|
||||
|
||||
@@ -1814,6 +1816,55 @@ export type TextVariants =
|
||||
// @public (undocumented)
|
||||
export type TextWeights = 'regular' | 'bold';
|
||||
|
||||
// @public (undocumented)
|
||||
export const ToggleButton: ForwardRefExoticComponent<
|
||||
ToggleButtonProps & RefAttributes<HTMLButtonElement>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const ToggleButtonDefinition: {
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-ToggleButton';
|
||||
readonly content: 'bui-ToggleButtonContent';
|
||||
};
|
||||
readonly dataAttributes: {
|
||||
readonly size: readonly ['small', 'medium'];
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const ToggleButtonGroup: ForwardRefExoticComponent<
|
||||
ToggleButtonGroupProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const ToggleButtonGroupDefinition: {
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-ToggleButtonGroup';
|
||||
};
|
||||
readonly dataAttributes: {
|
||||
readonly orientation: readonly ['horizontal', 'vertical'];
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ToggleButtonGroupProps
|
||||
extends Omit<ToggleButtonGroupProps_2, 'orientation'> {
|
||||
// (undocumented)
|
||||
orientation?: NonNullable<ToggleButtonGroupProps_2['orientation']>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ToggleButtonProps extends ToggleButtonProps_2 {
|
||||
// (undocumented)
|
||||
iconEnd?: ReactElement;
|
||||
// (undocumented)
|
||||
iconStart?: ReactElement;
|
||||
onSurface?: Responsive<Surface>;
|
||||
// (undocumented)
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Tooltip: ForwardRefExoticComponent<
|
||||
TooltipProps & RefAttributes<HTMLDivElement>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@layer tokens, base, components, utilities;
|
||||
|
||||
@layer components {
|
||||
.bui-ToggleButton {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--bui-space-1_5);
|
||||
border: none;
|
||||
border-radius: var(--bui-radius-2);
|
||||
background: var(--bui-bg-surface-1);
|
||||
color: var(--bui-fg-primary);
|
||||
font-family: var(--bui-font-regular);
|
||||
font-weight: var(--bui-font-weight-bold);
|
||||
padding: 0 var(--bui-space-2);
|
||||
cursor: pointer;
|
||||
transition: background-color 150ms ease, box-shadow 150ms ease,
|
||||
color 150ms ease, transform 100ms ease;
|
||||
|
||||
&[data-selected],
|
||||
&[data-pressed] {
|
||||
background: var(--bui-bg-solid);
|
||||
color: var(--bui-fg-solid);
|
||||
}
|
||||
|
||||
&:not([data-selected])[data-hovered] {
|
||||
background: var(--bui-bg-surface-2);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
background: var(--bui-bg-neutral-on-surface-0-disabled);
|
||||
color: var(--bui-fg-disabled);
|
||||
}
|
||||
|
||||
&[data-disabled][data-hovered] {
|
||||
background: var(--bui-bg-neutral-on-surface-0-disabled);
|
||||
}
|
||||
|
||||
&[data-disabled][data-selected] {
|
||||
background: var(--bui-bg-solid-disabled);
|
||||
color: var(--bui-fg-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-focus-visible] {
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 0 2px var(--bui-ring);
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-pressed] {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-disabled][data-pressed] {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-size='small'] {
|
||||
height: 2rem;
|
||||
font-size: var(--bui-font-size-3);
|
||||
padding: 0 var(--bui-space-2);
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.bui-ToggleButton[data-size='medium'] {
|
||||
height: 2.5rem;
|
||||
font-size: var(--bui-font-size-4);
|
||||
padding: 0 var(--bui-space-3);
|
||||
|
||||
svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.bui-ToggleButtonContent {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--bui-space-1_5);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import preview from '../../../../../.storybook/preview';
|
||||
import { ToggleButton } from './ToggleButton';
|
||||
import { Flex } from '../Flex';
|
||||
import { Text } from '../Text';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
RiCheckLine,
|
||||
RiStarFill,
|
||||
RiStarLine,
|
||||
RiCloudLine,
|
||||
RiArrowRightSLine,
|
||||
} from '@remixicon/react';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/ToggleButton',
|
||||
component: ToggleButton,
|
||||
argTypes: {
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['small', 'medium'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Default = meta.story({
|
||||
args: {
|
||||
children: 'Toggle',
|
||||
},
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
args: {
|
||||
children: 'Toggle',
|
||||
},
|
||||
parameters: {
|
||||
argTypes: {
|
||||
size: {
|
||||
control: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default</Text>
|
||||
<Flex align="center" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Sizes = meta.story({
|
||||
args: {
|
||||
children: 'Toggle',
|
||||
},
|
||||
parameters: {
|
||||
argTypes: {
|
||||
size: {
|
||||
control: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<Flex align="center">
|
||||
<ToggleButton size="small">Small</ToggleButton>
|
||||
<ToggleButton size="medium">Medium</ToggleButton>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const WithIcons = meta.story({
|
||||
args: {
|
||||
children: 'Favorite',
|
||||
},
|
||||
render: args => (
|
||||
<Flex align="center">
|
||||
<ToggleButton {...args} iconStart={<RiStarLine />} />
|
||||
<ToggleButton {...args} iconStart={<RiStarFill />} defaultSelected />
|
||||
<ToggleButton {...args} iconEnd={<RiCheckLine />} />
|
||||
<ToggleButton iconEnd={<RiCheckLine />} />
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const IconsOnly = meta.story({
|
||||
render: () => (
|
||||
<Flex align="center">
|
||||
<ToggleButton aria-label="Cloud" iconStart={<RiCloudLine />} />
|
||||
<ToggleButton
|
||||
aria-label="Starred"
|
||||
defaultSelected
|
||||
iconStart={<RiStarFill />}
|
||||
/>
|
||||
<ToggleButton aria-label="Next" iconStart={<RiArrowRightSLine />} />
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const IconsAndText = meta.story({
|
||||
render: () => (
|
||||
<Flex align="center">
|
||||
<ToggleButton iconStart={<RiCloudLine />}>Cloud</ToggleButton>
|
||||
<ToggleButton iconStart={<RiStarLine />}>Star</ToggleButton>
|
||||
<ToggleButton iconEnd={<RiArrowRightSLine />}>Next</ToggleButton>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Disabled = meta.story({
|
||||
render: () => (
|
||||
<Flex align="center">
|
||||
<ToggleButton isDisabled>Disabled</ToggleButton>
|
||||
<ToggleButton defaultSelected isDisabled>
|
||||
Selected
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Controlled = meta.story({
|
||||
render: () => {
|
||||
const [selected, setSelected] = useState(false);
|
||||
return (
|
||||
<Flex direction="column" gap="3">
|
||||
<ToggleButton
|
||||
aria-label="Star"
|
||||
isSelected={selected}
|
||||
onChange={setSelected}
|
||||
iconStart={selected ? <RiStarFill /> : <RiStarLine />}
|
||||
>
|
||||
{selected ? 'Starred' : 'Not starred'}
|
||||
</ToggleButton>
|
||||
<Text>State: {selected ? 'selected' : 'unselected'}</Text>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const FunctionChildren = meta.story({
|
||||
render: () => {
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
return (
|
||||
<Flex direction="column" gap="3">
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton isDisabled={disabled}>
|
||||
{({ isDisabled, isSelected }) =>
|
||||
isDisabled
|
||||
? `Disabled ${isSelected ? '(Selected)' : '(Unselected)'}`
|
||||
: `Enabled ${isSelected ? '(Selected)' : '(Unselected)'}`
|
||||
}
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
aria-label="Toggle disabled state"
|
||||
onChange={() => setDisabled(!disabled)}
|
||||
>
|
||||
{disabled ? 'Enable' : 'Disable'}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
<Text>
|
||||
Toggle the button to change the disabled state and see text update
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const DynamicContent = meta.story({
|
||||
render: () => {
|
||||
return (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 1: Selection State</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 2: Multiple States</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected, isHovered }) => {
|
||||
const states = [];
|
||||
if (isSelected) states.push('on');
|
||||
else states.push('off');
|
||||
if (isHovered) states.push('hovered');
|
||||
return `Email (${states.join(', ')})`;
|
||||
}}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected, isHovered }) => {
|
||||
const states = [];
|
||||
if (isSelected) states.push('on');
|
||||
else states.push('off');
|
||||
if (isHovered) states.push('hovered');
|
||||
return `Push (${states.join(', ')})`;
|
||||
}}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 3: Conditional Icons</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (
|
||||
<>
|
||||
{isSelected ? <RiStarFill /> : <RiStarLine />}
|
||||
<span>{isSelected ? 'Starred' : 'Star'}</span>
|
||||
</>
|
||||
)}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
<Text weight="bold">Example 4: Status Indicators</Text>
|
||||
<Flex align="center" gap="2">
|
||||
<ToggleButton defaultSelected>
|
||||
{({ isSelected }) => (
|
||||
<Flex align="center" gap="2">
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isSelected
|
||||
? 'var(--bui-fg-success)'
|
||||
: 'var(--bui-fg-secondary)',
|
||||
}}
|
||||
/>
|
||||
<span>Active</span>
|
||||
</Flex>
|
||||
)}
|
||||
</ToggleButton>
|
||||
<ToggleButton>
|
||||
{({ isSelected }) => (
|
||||
<Flex align="center" gap="2">
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isSelected
|
||||
? 'var(--bui-fg-danger)'
|
||||
: 'var(--bui-fg-secondary)',
|
||||
}}
|
||||
/>
|
||||
<span>Inactive</span>
|
||||
</Flex>
|
||||
)}
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { forwardRef, Ref } from 'react';
|
||||
import { ToggleButton as AriaToggleButton } from 'react-aria-components';
|
||||
import type { ToggleButtonProps } from './types';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { ToggleButtonDefinition } from './definition';
|
||||
import styles from './ToggleButton.module.css';
|
||||
import { useSurface } from '../../hooks/useSurface';
|
||||
|
||||
/** @public */
|
||||
export const ToggleButton = forwardRef(
|
||||
(props: ToggleButtonProps, ref: Ref<HTMLButtonElement>) => {
|
||||
const { classNames, dataAttributes, cleanedProps } = useStyles(
|
||||
ToggleButtonDefinition,
|
||||
{
|
||||
size: 'small',
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
const { children, className, iconStart, iconEnd, onSurface, ...rest } =
|
||||
cleanedProps;
|
||||
|
||||
const { surface } = useSurface({ onSurface });
|
||||
|
||||
return (
|
||||
<AriaToggleButton
|
||||
className={clsx(classNames.root, styles[classNames.root], className)}
|
||||
ref={ref}
|
||||
{...dataAttributes}
|
||||
{...(typeof surface === 'string' ? { 'data-on-surface': surface } : {})}
|
||||
{...rest}
|
||||
>
|
||||
{renderProps => {
|
||||
// If children is a function, call it with render props; otherwise use children as-is
|
||||
const renderedChildren =
|
||||
typeof children === 'function' ? children(renderProps) : children;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={clsx(classNames.content, styles[classNames.content])}
|
||||
data-slot="content"
|
||||
>
|
||||
{iconStart}
|
||||
{renderedChildren}
|
||||
{iconEnd}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
</AriaToggleButton>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ToggleButton.displayName = 'ToggleButton';
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ComponentDefinition } from '../../types';
|
||||
|
||||
/**
|
||||
* Component definition for ToggleButton
|
||||
* @public
|
||||
*/
|
||||
export const ToggleButtonDefinition = {
|
||||
classNames: {
|
||||
root: 'bui-ToggleButton',
|
||||
content: 'bui-ToggleButtonContent',
|
||||
},
|
||||
dataAttributes: {
|
||||
size: ['small', 'medium'] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './ToggleButton';
|
||||
export * from './types';
|
||||
export { ToggleButtonDefinition } from './definition';
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Breakpoint } from '../..';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { ToggleButtonProps as AriaToggleButtonProps } from 'react-aria-components';
|
||||
import type { Responsive, Surface } from '../../types';
|
||||
|
||||
/**
|
||||
* Properties for {@link ToggleButton}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ToggleButtonProps extends AriaToggleButtonProps {
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
iconStart?: ReactElement;
|
||||
iconEnd?: ReactElement;
|
||||
/** Surface the toggle button is placed on. Defaults to context surface if available */
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@layer tokens, base, components, utilities;
|
||||
|
||||
@layer components {
|
||||
.bui-ToggleButtonGroup {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
border-radius: var(--bui-radius-2);
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 1px var(--bui-border);
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical'] {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup > :global(.bui-ToggleButton) {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* Horizontal radius rules (default orientation) */
|
||||
.bui-ToggleButtonGroup:not([data-orientation='vertical'])
|
||||
> :global(.bui-ToggleButton) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup:not([data-orientation='vertical'])
|
||||
> :global(.bui-ToggleButton):first-child {
|
||||
border-radius: var(--bui-radius-2) 0 0 var(--bui-radius-2);
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup:not([data-orientation='vertical'])
|
||||
> :global(.bui-ToggleButton):last-child {
|
||||
border-radius: 0 var(--bui-radius-2) var(--bui-radius-2) 0;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup:not([data-orientation='vertical'])
|
||||
> :global(.bui-ToggleButton):only-child {
|
||||
border-radius: var(--bui-radius-2);
|
||||
}
|
||||
|
||||
/* Horizontal dividers */
|
||||
.bui-ToggleButtonGroup:not([data-orientation='vertical'])
|
||||
:global(.bui-ToggleButton)
|
||||
+ :global(.bui-ToggleButton) {
|
||||
border-left: 1px solid var(--bui-border);
|
||||
}
|
||||
|
||||
/* Vertical dividers */
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
:global(.bui-ToggleButton) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
:global(.bui-ToggleButton)
|
||||
+ :global(.bui-ToggleButton) {
|
||||
border-top: 1px solid var(--bui-border);
|
||||
}
|
||||
|
||||
/* Vertical radius rules */
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
> :global(.bui-ToggleButton) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
> :global(.bui-ToggleButton):first-child {
|
||||
border-radius: var(--bui-radius-2) var(--bui-radius-2) 0 0;
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
> :global(.bui-ToggleButton):last-child {
|
||||
border-radius: 0 0 var(--bui-radius-2) var(--bui-radius-2);
|
||||
}
|
||||
|
||||
.bui-ToggleButtonGroup[data-orientation='vertical']
|
||||
> :global(.bui-ToggleButton):only-child {
|
||||
border-radius: var(--bui-radius-2);
|
||||
}
|
||||
|
||||
/* Focus ring on group surface */
|
||||
.bui-ToggleButtonGroup:focus-visible {
|
||||
outline: 2px solid var(--bui-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import preview from '../../../../../.storybook/preview';
|
||||
import { ToggleButtonGroup } from './ToggleButtonGroup';
|
||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
||||
import { Flex } from '../Flex';
|
||||
import { Text } from '../Text';
|
||||
import { useState } from 'react';
|
||||
import type { Selection } from 'react-aria-components';
|
||||
import {
|
||||
RiCloudLine,
|
||||
RiStarLine,
|
||||
RiStarFill,
|
||||
RiArrowRightSLine,
|
||||
} from '@remixicon/react';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/ToggleButtonGroup',
|
||||
component: ToggleButtonGroup,
|
||||
argTypes: {
|
||||
selectionMode: {
|
||||
control: 'select',
|
||||
options: ['single', 'multiple'],
|
||||
},
|
||||
orientation: {
|
||||
control: 'select',
|
||||
options: ['horizontal', 'vertical'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const SingleSelection = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
defaultSelectedKeys: ['dogs'],
|
||||
},
|
||||
render: args => (
|
||||
<ToggleButtonGroup {...args}>
|
||||
<ToggleButton id="dogs">Dogs</ToggleButton>
|
||||
<ToggleButton id="cats">Cats</ToggleButton>
|
||||
<ToggleButton id="birds">Birds</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const MultipleSelection = meta.story({
|
||||
args: {
|
||||
selectionMode: 'multiple',
|
||||
defaultSelectedKeys: ['frontend'],
|
||||
},
|
||||
render: args => (
|
||||
<ToggleButtonGroup {...args}>
|
||||
<ToggleButton id="frontend">Frontend</ToggleButton>
|
||||
<ToggleButton id="backend">Backend</ToggleButton>
|
||||
<ToggleButton id="platform">Platform</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
defaultSelectedKeys: ['option1'],
|
||||
},
|
||||
parameters: {
|
||||
argTypes: {
|
||||
selectionMode: {
|
||||
control: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default</Text>
|
||||
<Flex align="center" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
>
|
||||
<ToggleButton id="option1">Option 1</ToggleButton>
|
||||
<ToggleButton id="option2">Option 2</ToggleButton>
|
||||
<ToggleButton id="option3">Option 3</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const DisabledGroup = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
isDisabled: true,
|
||||
},
|
||||
render: args => (
|
||||
<ToggleButtonGroup {...args}>
|
||||
<ToggleButton id="cat">Cat</ToggleButton>
|
||||
<ToggleButton id="dog">Dog</ToggleButton>
|
||||
<ToggleButton id="bird">Bird</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const DisallowEmptySelection = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
disallowEmptySelection: true,
|
||||
defaultSelectedKeys: ['one'],
|
||||
},
|
||||
render: args => (
|
||||
<ToggleButtonGroup {...args}>
|
||||
<ToggleButton id="one">One</ToggleButton>
|
||||
<ToggleButton id="two">Two</ToggleButton>
|
||||
<ToggleButton id="three">Three</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const MixedDisabled = meta.story({
|
||||
render: () => (
|
||||
<ToggleButtonGroup selectionMode="single">
|
||||
<ToggleButton id="one">One</ToggleButton>
|
||||
<ToggleButton id="two" isDisabled>
|
||||
Two
|
||||
</ToggleButton>
|
||||
<ToggleButton id="three">Three</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Orientation = meta.story({
|
||||
args: {
|
||||
orientation: 'vertical',
|
||||
},
|
||||
render: args => (
|
||||
<ToggleButtonGroup {...args} selectionMode="single">
|
||||
<ToggleButton id="morning">Morning</ToggleButton>
|
||||
<ToggleButton id="afternoon">Afternoon</ToggleButton>
|
||||
<ToggleButton id="evening">Evening</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const ControlledGroup = meta.story({
|
||||
render: () => {
|
||||
const [selectedKeys, setSelectedKeys] = useState<Selection>(
|
||||
new Set(['beta']),
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="3">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
>
|
||||
<ToggleButton id="alpha">Alpha</ToggleButton>
|
||||
<ToggleButton id="beta">Beta</ToggleButton>
|
||||
<ToggleButton id="gamma">Gamma</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
<Text>Selected: {[...selectedKeys].join(', ') || 'none'}</Text>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const WithIcons = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
},
|
||||
|
||||
render: () => (
|
||||
<ToggleButtonGroup selectionMode="multiple" defaultSelectedKeys={['cloud']}>
|
||||
<ToggleButton id="cloud" aria-label="Cloud" iconStart={<RiCloudLine />} />
|
||||
<ToggleButton
|
||||
id="starred"
|
||||
aria-label="Starred"
|
||||
iconStart={<RiStarFill />}
|
||||
/>
|
||||
<ToggleButton id="star" iconStart={<RiStarLine />}>
|
||||
Star
|
||||
</ToggleButton>
|
||||
<ToggleButton id="next" iconEnd={<RiArrowRightSLine />}>
|
||||
Next
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const IconsOnly = meta.story({
|
||||
render: () => (
|
||||
<ToggleButtonGroup selectionMode="multiple" defaultSelectedKeys={['cloud']}>
|
||||
<ToggleButton id="cloud" iconStart={<RiCloudLine />} />
|
||||
<ToggleButton id="star" iconStart={<RiStarLine />} />
|
||||
<ToggleButton id="next" iconEnd={<RiArrowRightSLine />} />
|
||||
</ToggleButtonGroup>
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { forwardRef, Ref } from 'react';
|
||||
import { ToggleButtonGroup as AriaToggleButtonGroup } from 'react-aria-components';
|
||||
import type { ToggleButtonGroupProps } from './types';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { ToggleButtonGroupDefinition } from './definition';
|
||||
import styles from './ToggleButtonGroup.module.css';
|
||||
|
||||
/** @public */
|
||||
export const ToggleButtonGroup = forwardRef(
|
||||
(props: ToggleButtonGroupProps, ref: Ref<HTMLDivElement>) => {
|
||||
const { classNames, dataAttributes, cleanedProps } = useStyles(
|
||||
ToggleButtonGroupDefinition,
|
||||
{
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
const { className, children, ...rest } = cleanedProps;
|
||||
|
||||
return (
|
||||
<AriaToggleButtonGroup
|
||||
className={clsx(classNames.root, styles[classNames.root], className)}
|
||||
ref={ref}
|
||||
{...dataAttributes}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</AriaToggleButtonGroup>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ToggleButtonGroup.displayName = 'ToggleButtonGroup';
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ComponentDefinition } from '../../types';
|
||||
|
||||
/**
|
||||
* Component definition for ToggleButtonGroup
|
||||
* @public
|
||||
*/
|
||||
export const ToggleButtonGroupDefinition = {
|
||||
classNames: {
|
||||
root: 'bui-ToggleButtonGroup',
|
||||
},
|
||||
dataAttributes: {
|
||||
orientation: ['horizontal', 'vertical'] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './ToggleButtonGroup';
|
||||
export * from './types';
|
||||
export { ToggleButtonGroupDefinition } from './definition';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ToggleButtonGroupProps as AriaToggleButtonGroupProps } from 'react-aria-components';
|
||||
|
||||
/** @public */
|
||||
export interface ToggleButtonGroupProps
|
||||
extends Omit<AriaToggleButtonGroupProps, 'orientation'> {
|
||||
orientation?: NonNullable<AriaToggleButtonGroupProps['orientation']>;
|
||||
}
|
||||
@@ -47,6 +47,8 @@ export { SearchFieldDefinition } from './components/SearchField/definition';
|
||||
export { SelectDefinition } from './components/Select/definition';
|
||||
export { SkeletonDefinition } from './components/Skeleton/definition';
|
||||
export { SwitchDefinition } from './components/Switch/definition';
|
||||
export { ToggleButtonDefinition } from './components/ToggleButton/definition';
|
||||
export { ToggleButtonGroupDefinition } from './components/ToggleButtonGroup/definition';
|
||||
export { TableDefinition } from './components/Table/definition';
|
||||
export { TablePaginationDefinition } from './components/TablePagination/definition';
|
||||
export { TabsDefinition } from './components/Tabs/definition';
|
||||
|
||||
@@ -52,6 +52,8 @@ export * from './components/Link';
|
||||
export * from './components/Select';
|
||||
export * from './components/Skeleton';
|
||||
export * from './components/Switch';
|
||||
export * from './components/ToggleButton';
|
||||
export * from './components/ToggleButtonGroup';
|
||||
export * from './components/VisuallyHidden';
|
||||
|
||||
// Types
|
||||
|
||||
Reference in New Issue
Block a user