Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/www/src/components/demo/demo.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
'use client';

import {
FontBoldIcon,
FontItalicIcon,
InfoCircledIcon,
Pencil2Icon,
PlusIcon,
TextAlignCenterIcon,
TextAlignLeftIcon,
TextAlignRightIcon,
TransformIcon,
UnderlineIcon,
UploadIcon
} from '@radix-ui/react-icons';
import * as Apsara from '@raystack/apsara';
Expand Down Expand Up @@ -37,6 +43,12 @@ export default function Demo(props: DemoProps) {
Pencil2Icon,
InfoCircledIcon,
UploadIcon,
FontBoldIcon,
FontItalicIcon,
UnderlineIcon,
TextAlignLeftIcon,
TextAlignCenterIcon,
TextAlignRightIcon,
dayjs
}
} = props;
Expand Down
1 change: 1 addition & 0 deletions apps/www/src/components/playground/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ export * from './tabs-examples';
export * from './text-area-examples';
export * from './text-examples';
export * from './toast-examples';
export * from './toggle-examples';
export * from './tooltip-examples';
95 changes: 95 additions & 0 deletions apps/www/src/components/playground/toggle-examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'use client';

import {
FontBoldIcon,
FontItalicIcon,
TextAlignCenterIcon,
TextAlignLeftIcon,
TextAlignRightIcon,
UnderlineIcon
} from '@radix-ui/react-icons';
import { Flex, Text, Toggle } from '@raystack/apsara';
import PlaygroundLayout from './playground-layout';

export function ToggleExamples() {
return (
<PlaygroundLayout title='Toggle'>
<Flex direction='column' gap='large'>
<Text size={2} weight={500}>
Standalone
</Text>
<Flex gap='medium' align='center'>
<Toggle aria-label='Bold'>
<FontBoldIcon />
</Toggle>
<Toggle aria-label='Bold' defaultPressed>
<FontBoldIcon />
</Toggle>
<Toggle aria-label='Bold' disabled>
<FontBoldIcon />
</Toggle>
</Flex>

<Text size={2} weight={500}>
Sizes
</Text>
<Flex gap='medium' align='center'>
<Toggle size={1} aria-label='Bold'>
<FontBoldIcon />
</Toggle>
<Toggle size={2} aria-label='Bold'>
<FontBoldIcon />
</Toggle>
<Toggle size={3} aria-label='Bold'>
<FontBoldIcon />
</Toggle>
<Toggle size={4} aria-label='Bold'>
<FontBoldIcon />
</Toggle>
</Flex>

<Text size={2} weight={500}>
Toggle.Group (single selection)
</Text>
<Toggle.Group defaultValue={['center']} aria-label='Text alignment'>
<Toggle value='left' aria-label='Align left'>
<TextAlignLeftIcon />
</Toggle>
<Toggle value='center' aria-label='Align center'>
<TextAlignCenterIcon />
</Toggle>
<Toggle value='right' aria-label='Align right'>
<TextAlignRightIcon />
</Toggle>
</Toggle.Group>

<Text size={2} weight={500}>
Toggle.Group (multiple selection)
</Text>
<Toggle.Group multiple aria-label='Text formatting'>
<Toggle value='bold' aria-label='Bold'>
<FontBoldIcon />
</Toggle>
<Toggle value='italic' aria-label='Italic'>
<FontItalicIcon />
</Toggle>
<Toggle value='underline' aria-label='Underline'>
<UnderlineIcon />
</Toggle>
</Toggle.Group>

<Text size={2} weight={500}>
Disabled Group
</Text>
<Toggle.Group disabled aria-label='Disabled alignment'>
<Toggle value='left' aria-label='Align left'>
<TextAlignLeftIcon />
</Toggle>
<Toggle value='center' aria-label='Align center'>
<TextAlignCenterIcon />
</Toggle>
</Toggle.Group>
</Flex>
</PlaygroundLayout>
);
}
123 changes: 123 additions & 0 deletions apps/www/src/content/docs/components/toggle/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use client';

import { getPropsString } from '@/lib/utils';

const getCode = (props: any) => {
return `<Toggle aria-label="Bold"${getPropsString(props)}>
<FontBoldIcon />
</Toggle>`;
};

export const playground = {
type: 'playground',
controls: {
defaultPressed: {
type: 'checkbox',
defaultValue: false
},
disabled: {
type: 'checkbox',
defaultValue: false
},
size: {
type: 'select',
options: ['1', '2', '3', '4'],
defaultValue: '3'
}
},
getCode
};

export const preview = {
type: 'code',
code: `<Toggle aria-label="Bold" defaultPressed>
<b>B</b>
</Toggle>`
};

export const groupDemo = {
type: 'code',
code: `<Toggle.Group defaultValue={["center"]} aria-label="Text alignment">
<Toggle value="left" aria-label="Align left">
<TextAlignLeftIcon />
</Toggle>
<Toggle value="center" aria-label="Align center">
<TextAlignCenterIcon />
</Toggle>
<Toggle value="right" aria-label="Align right">
<TextAlignRightIcon />
</Toggle>
</Toggle.Group>`
};

export const multipleDemo = {
type: 'code',
code: `<Toggle.Group multiple aria-label="Text formatting">
<Toggle value="bold" aria-label="Bold">
<FontBoldIcon />
</Toggle>
<Toggle value="italic" aria-label="Italic">
<FontItalicIcon />
</Toggle>
<Toggle value="underline" aria-label="Underline">
<UnderlineIcon />
</Toggle>
</Toggle.Group>`
};

export const controlledDemo = {
type: 'code',
code: `function ControlledToggle() {
const [pressed, setPressed] = React.useState(false);
return (
<Toggle
aria-label="Star"
pressed={pressed}
onPressedChange={setPressed}
>
{pressed ? "★" : "☆"}
</Toggle>
);
}`
};

export const sizeDemo = {
type: 'code',
code: `<Flex gap="large" align="center">
<Toggle size={1} aria-label="Bold">
<FontBoldIcon />
</Toggle>
<Toggle size={2} aria-label="Bold">
<FontBoldIcon />
</Toggle>
<Toggle size={3} aria-label="Bold">
<FontBoldIcon />
</Toggle>
<Toggle size={4} aria-label="Bold">
<FontBoldIcon />
</Toggle>
</Flex>`
};

export const disabledDemo = {
type: 'code',
tabs: [
{
name: 'Single',
code: `<Toggle aria-label="Bold" disabled>
<b>B</b>
</Toggle>`
},
{
name: 'Group',
code: `<Toggle.Group disabled aria-label="Text alignment">
<Toggle value="left" aria-label="Align left">
<TextAlignLeftIcon />
</Toggle>
<Toggle value="center" aria-label="Align center">
<TextAlignCenterIcon />
</Toggle>
</Toggle.Group>`
}
]
};
83 changes: 83 additions & 0 deletions apps/www/src/content/docs/components/toggle/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Toggle
description: A two-state button that can be toggled on or off, used standalone or within a group.
source: packages/raystack/components/toggle
tag: new
---

import { playground, preview, groupDemo, multipleDemo, sizeDemo, controlledDemo, disabledDemo } from "./demo.ts";

<Demo data={playground} />

## Anatomy

Import and use the component:

```tsx
import { Toggle } from "@raystack/apsara";

<Toggle aria-label="Bold">B</Toggle>
```

Use `Toggle.Group` to manage a set of toggles with shared state:

```tsx
<Toggle.Group defaultValue={["left"]}>
<Toggle value="left" aria-label="Align left">L</Toggle>
<Toggle value="center" aria-label="Align center">C</Toggle>
<Toggle value="right" aria-label="Align right">R</Toggle>
</Toggle.Group>
```

## API Reference

### Root

A two-state button that can be toggled on or off.

<auto-type-table path="./props.ts" name="ToggleProps" />

### Group

Groups multiple toggle buttons and manages their shared pressed state.

<auto-type-table path="./props.ts" name="ToggleGroupProps" />

## Examples

### Group

Use `Toggle.Group` for exclusive or multi-select toggle sets. By default, only one toggle can be pressed at a time.

<Demo data={groupDemo} />

### Multiple Selection

Set `multiple` on the group to allow more than one toggle to be pressed simultaneously.

<Demo data={multipleDemo} />

### Size

The toggle supports four sizes matching the icon button sizes.

<Demo data={sizeDemo} />

### Controlled

Control the pressed state programmatically.

<Demo data={controlledDemo} />

### Disabled

Disable individual toggles or the entire group.

<Demo data={disabledDemo} />

## Accessibility

- Uses native `<button>` element with `aria-pressed` for toggle state
- Supports keyboard activation with Space and Enter keys
- `Toggle.Group` renders with `role="group"` and supports arrow key navigation
- Use `aria-label` on each toggle to provide an accessible name when using icon-only content
Loading
Loading