torch-glare 2.5.6 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -395
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -88
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -168
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -255
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -597
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -300
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1217
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/scroller.ts +0 -26
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -738
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1521
- package/docs/components/data-views/migration.md +0 -81
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -284
- package/docs/components/form-renderer.md +0 -396
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -483
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -60
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1523
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -726
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,882 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Toggle
|
|
3
|
-
version: 2.4.5
|
|
4
|
-
status: stable
|
|
5
|
-
category: components/forms
|
|
6
|
-
tags: [form, toggle, button, radix-ui, accessible, variants]
|
|
7
|
-
last-reviewed: 2024-11-05
|
|
8
|
-
bundle-size: 2.4kb
|
|
9
|
-
dependencies:
|
|
10
|
-
- "@radix-ui/react-toggle": "^1.0.0"
|
|
11
|
-
- "class-variance-authority": "^0.7.0"
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# Toggle
|
|
15
|
-
|
|
16
|
-
> A versatile toggle button component built on Radix UI primitives with multiple style variants and sizes. Perfect for toolbar actions, view toggles, formatting options, and any binary state that requires a button-style interaction.
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
21
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npx torch-glare@latest init
|
|
25
|
-
npx torch-glare@latest add Toggle
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`add` also copies any components, hooks, and utilities that `Toggle` depends on.
|
|
29
|
-
|
|
30
|
-
## Import
|
|
31
|
-
|
|
32
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
import { Toggle } from "@/components/Toggle";
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Examples
|
|
39
|
-
|
|
40
|
-
### Basic Usage
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { Toggle } from '@/components/Toggle'
|
|
44
|
-
|
|
45
|
-
function Example() {
|
|
46
|
-
const [pressed, setPressed] = useState(false)
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<Toggle
|
|
50
|
-
pressed={pressed}
|
|
51
|
-
onPressedChange={setPressed}
|
|
52
|
-
>
|
|
53
|
-
<BoldIcon className="w-4 h-4" />
|
|
54
|
-
</Toggle>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Text Formatting Toolbar
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
function FormattingToolbar() {
|
|
63
|
-
const [formatting, setFormatting] = useState({
|
|
64
|
-
bold: false,
|
|
65
|
-
italic: false,
|
|
66
|
-
underline: false,
|
|
67
|
-
strike: false
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const toggleFormat = (format: keyof typeof formatting) => {
|
|
71
|
-
setFormatting(prev => ({ ...prev, [format]: !prev[format] }))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<div className="flex gap-1 p-2 border rounded">
|
|
76
|
-
<Toggle
|
|
77
|
-
pressed={formatting.bold}
|
|
78
|
-
onPressedChange={() => toggleFormat('bold')}
|
|
79
|
-
aria-label="Bold"
|
|
80
|
-
>
|
|
81
|
-
<i className="ri-bold" />
|
|
82
|
-
</Toggle>
|
|
83
|
-
|
|
84
|
-
<Toggle
|
|
85
|
-
pressed={formatting.italic}
|
|
86
|
-
onPressedChange={() => toggleFormat('italic')}
|
|
87
|
-
aria-label="Italic"
|
|
88
|
-
>
|
|
89
|
-
<i className="ri-italic" />
|
|
90
|
-
</Toggle>
|
|
91
|
-
|
|
92
|
-
<Toggle
|
|
93
|
-
pressed={formatting.underline}
|
|
94
|
-
onPressedChange={() => toggleFormat('underline')}
|
|
95
|
-
aria-label="Underline"
|
|
96
|
-
>
|
|
97
|
-
<i className="ri-underline" />
|
|
98
|
-
</Toggle>
|
|
99
|
-
|
|
100
|
-
<Toggle
|
|
101
|
-
pressed={formatting.strike}
|
|
102
|
-
onPressedChange={() => toggleFormat('strike')}
|
|
103
|
-
aria-label="Strikethrough"
|
|
104
|
-
>
|
|
105
|
-
<i className="ri-strikethrough" />
|
|
106
|
-
</Toggle>
|
|
107
|
-
</div>
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### View Toggle
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
function ViewToggle() {
|
|
116
|
-
const [view, setView] = useState<'grid' | 'list'>('grid')
|
|
117
|
-
|
|
118
|
-
return (
|
|
119
|
-
<div className="flex gap-1">
|
|
120
|
-
<Toggle
|
|
121
|
-
pressed={view === 'grid'}
|
|
122
|
-
onPressedChange={() => setView('grid')}
|
|
123
|
-
aria-label="Grid view"
|
|
124
|
-
>
|
|
125
|
-
<GridIcon className="w-4 h-4" />
|
|
126
|
-
</Toggle>
|
|
127
|
-
|
|
128
|
-
<Toggle
|
|
129
|
-
pressed={view === 'list'}
|
|
130
|
-
onPressedChange={() => setView('list')}
|
|
131
|
-
aria-label="List view"
|
|
132
|
-
>
|
|
133
|
-
<ListIcon className="w-4 h-4" />
|
|
134
|
-
</Toggle>
|
|
135
|
-
</div>
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Different Variants
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
function VariantExamples() {
|
|
144
|
-
const [selected, setSelected] = useState('')
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<div className="space-y-4">
|
|
148
|
-
<div className="flex gap-2">
|
|
149
|
-
<Toggle variant="PrimeStyle">Prime</Toggle>
|
|
150
|
-
<Toggle variant="BluSecStyle">Blue</Toggle>
|
|
151
|
-
<Toggle variant="YelSecStyle">Yellow</Toggle>
|
|
152
|
-
<Toggle variant="RedSecStyle">Red</Toggle>
|
|
153
|
-
</div>
|
|
154
|
-
|
|
155
|
-
<div className="flex gap-2">
|
|
156
|
-
<Toggle variant="BorderStyle">Border</Toggle>
|
|
157
|
-
<Toggle variant="PrimeContStyle">Prime Cont</Toggle>
|
|
158
|
-
<Toggle variant="BluContStyle">Blue Cont</Toggle>
|
|
159
|
-
<Toggle variant="RedContStyle">Red Cont</Toggle>
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Different Sizes
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
function SizeVariants() {
|
|
170
|
-
return (
|
|
171
|
-
<div className="flex items-center gap-3">
|
|
172
|
-
<Toggle size="S">
|
|
173
|
-
<i className="ri-heart-line" />
|
|
174
|
-
</Toggle>
|
|
175
|
-
|
|
176
|
-
<Toggle size="M">
|
|
177
|
-
<i className="ri-heart-line" />
|
|
178
|
-
</Toggle>
|
|
179
|
-
|
|
180
|
-
<Toggle size="L">
|
|
181
|
-
<i className="ri-heart-line" />
|
|
182
|
-
</Toggle>
|
|
183
|
-
|
|
184
|
-
<Toggle size="XL">
|
|
185
|
-
<i className="ri-heart-line" />
|
|
186
|
-
</Toggle>
|
|
187
|
-
</div>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Alignment Toolbar
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
function AlignmentToolbar() {
|
|
196
|
-
const [alignment, setAlignment] = useState('left')
|
|
197
|
-
|
|
198
|
-
const options = [
|
|
199
|
-
{ value: 'left', icon: 'ri-align-left' },
|
|
200
|
-
{ value: 'center', icon: 'ri-align-center' },
|
|
201
|
-
{ value: 'right', icon: 'ri-align-right' },
|
|
202
|
-
{ value: 'justify', icon: 'ri-align-justify' }
|
|
203
|
-
]
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<div className="flex gap-1 p-2 border rounded">
|
|
207
|
-
{options.map(option => (
|
|
208
|
-
<Toggle
|
|
209
|
-
key={option.value}
|
|
210
|
-
pressed={alignment === option.value}
|
|
211
|
-
onPressedChange={() => setAlignment(option.value)}
|
|
212
|
-
aria-label={`Align ${option.value}`}
|
|
213
|
-
>
|
|
214
|
-
<i className={option.icon} />
|
|
215
|
-
</Toggle>
|
|
216
|
-
))}
|
|
217
|
-
</div>
|
|
218
|
-
)
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Media Controls
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
function MediaControls() {
|
|
226
|
-
const [playing, setPlaying] = useState(false)
|
|
227
|
-
const [muted, setMuted] = useState(false)
|
|
228
|
-
const [loop, setLoop] = useState(false)
|
|
229
|
-
|
|
230
|
-
return (
|
|
231
|
-
<div className="flex items-center gap-2">
|
|
232
|
-
<Toggle
|
|
233
|
-
size="L"
|
|
234
|
-
variant="PrimeStyle"
|
|
235
|
-
pressed={playing}
|
|
236
|
-
onPressedChange={setPlaying}
|
|
237
|
-
aria-label={playing ? 'Pause' : 'Play'}
|
|
238
|
-
>
|
|
239
|
-
<i className={playing ? 'ri-pause-fill' : 'ri-play-fill'} />
|
|
240
|
-
</Toggle>
|
|
241
|
-
|
|
242
|
-
<Toggle
|
|
243
|
-
size="M"
|
|
244
|
-
variant="BorderStyle"
|
|
245
|
-
pressed={muted}
|
|
246
|
-
onPressedChange={setMuted}
|
|
247
|
-
aria-label={muted ? 'Unmute' : 'Mute'}
|
|
248
|
-
>
|
|
249
|
-
<i className={muted ? 'ri-volume-mute-fill' : 'ri-volume-up-fill'} />
|
|
250
|
-
</Toggle>
|
|
251
|
-
|
|
252
|
-
<Toggle
|
|
253
|
-
size="M"
|
|
254
|
-
variant="BorderStyle"
|
|
255
|
-
pressed={loop}
|
|
256
|
-
onPressedChange={setLoop}
|
|
257
|
-
aria-label="Loop"
|
|
258
|
-
>
|
|
259
|
-
<i className="ri-loop-left-line" />
|
|
260
|
-
</Toggle>
|
|
261
|
-
</div>
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Filter Options
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
function FilterToggles() {
|
|
270
|
-
const [filters, setFilters] = useState({
|
|
271
|
-
inStock: false,
|
|
272
|
-
onSale: false,
|
|
273
|
-
newArrivals: false,
|
|
274
|
-
featured: false
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const toggleFilter = (filter: keyof typeof filters) => {
|
|
278
|
-
setFilters(prev => ({ ...prev, [filter]: !prev[filter] }))
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return (
|
|
282
|
-
<div className="space-y-2">
|
|
283
|
-
<h4 className="font-semibold">Filters</h4>
|
|
284
|
-
<div className="flex flex-wrap gap-2">
|
|
285
|
-
<Toggle
|
|
286
|
-
variant="BluSecStyle"
|
|
287
|
-
pressed={filters.inStock}
|
|
288
|
-
onPressedChange={() => toggleFilter('inStock')}
|
|
289
|
-
>
|
|
290
|
-
In Stock
|
|
291
|
-
</Toggle>
|
|
292
|
-
|
|
293
|
-
<Toggle
|
|
294
|
-
variant="YelSecStyle"
|
|
295
|
-
pressed={filters.onSale}
|
|
296
|
-
onPressedChange={() => toggleFilter('onSale')}
|
|
297
|
-
>
|
|
298
|
-
On Sale
|
|
299
|
-
</Toggle>
|
|
300
|
-
|
|
301
|
-
<Toggle
|
|
302
|
-
variant="PrimeStyle"
|
|
303
|
-
pressed={filters.newArrivals}
|
|
304
|
-
onPressedChange={() => toggleFilter('newArrivals')}
|
|
305
|
-
>
|
|
306
|
-
New Arrivals
|
|
307
|
-
</Toggle>
|
|
308
|
-
|
|
309
|
-
<Toggle
|
|
310
|
-
variant="RedSecStyle"
|
|
311
|
-
pressed={filters.featured}
|
|
312
|
-
onPressedChange={() => toggleFilter('featured')}
|
|
313
|
-
>
|
|
314
|
-
Featured
|
|
315
|
-
</Toggle>
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Disabled State
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
function DisabledToggles() {
|
|
326
|
-
return (
|
|
327
|
-
<div className="flex gap-2">
|
|
328
|
-
<Toggle disabled>
|
|
329
|
-
Disabled Off
|
|
330
|
-
</Toggle>
|
|
331
|
-
|
|
332
|
-
<Toggle disabled pressed>
|
|
333
|
-
Disabled On
|
|
334
|
-
</Toggle>
|
|
335
|
-
</div>
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
## API Reference
|
|
341
|
-
|
|
342
|
-
### Toggle Props
|
|
343
|
-
|
|
344
|
-
| Prop | Type | Default | Description |
|
|
345
|
-
|------|------|---------|-------------|
|
|
346
|
-
| `pressed` | `boolean` | - | Controlled pressed state |
|
|
347
|
-
| `defaultPressed` | `boolean` | `false` | Uncontrolled default state |
|
|
348
|
-
| `onPressedChange` | `(pressed: boolean) => void` | - | Called when state changes |
|
|
349
|
-
| `variant` | `VariantType` | `'PrimeStyle'` | Visual style variant |
|
|
350
|
-
| `size` | `'S' \| 'M' \| 'L' \| 'XL'` | `'M'` | Size variant |
|
|
351
|
-
| `disabled` | `boolean` | `false` | Disables the toggle |
|
|
352
|
-
| `asChild` | `boolean` | `false` | Render as child element |
|
|
353
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
354
|
-
| `children` | `ReactNode` | - | Toggle content |
|
|
355
|
-
|
|
356
|
-
### Variant Types
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
type VariantType =
|
|
360
|
-
| 'PrimeStyle'
|
|
361
|
-
| 'BluSecStyle'
|
|
362
|
-
| 'YelSecStyle'
|
|
363
|
-
| 'RedSecStyle'
|
|
364
|
-
| 'BorderStyle'
|
|
365
|
-
| 'PrimeContStyle'
|
|
366
|
-
| 'BluContStyle'
|
|
367
|
-
| 'RedContStyle'
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### TypeScript
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
import { ToggleProps } from '@radix-ui/react-toggle'
|
|
374
|
-
import { VariantProps } from 'class-variance-authority'
|
|
375
|
-
|
|
376
|
-
interface CustomToggleProps extends ToggleProps, VariantProps<typeof toggleVariants> {
|
|
377
|
-
variant?: VariantType
|
|
378
|
-
size?: 'S' | 'M' | 'L' | 'XL'
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
export const Toggle: React.ForwardRefExoticComponent<CustomToggleProps>
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
## Known Limitations & Frontend Patterns
|
|
385
|
-
|
|
386
|
-
> ⚠️ **`Toggle` is icon-only.** Its dimensions are hardcoded squares — `S=22×22`, `M=28×28`, `L=34×34`, `XL=40×40` — so any text label wider than ~3 characters will visually clip. The "Filter Options", "Theme Switcher with text", and similar text-label examples below are misleading: they only render correctly in environments that override the dimensions.
|
|
387
|
-
|
|
388
|
-
### Use `Button` with variant swap for selectable text chips
|
|
389
|
-
|
|
390
|
-
For format selectors (DOCX, MD, RTF), tag pickers, filter pills, or any case where the chip carries text wider than ~3 characters, **do not use `Toggle`**. Use `Button` with a `variant` swap to express the selected state:
|
|
391
|
-
|
|
392
|
-
```tsx
|
|
393
|
-
const FORMATS = ["DOCX", "MD", "RTF", "ODT", "EPUB", "TXT"];
|
|
394
|
-
|
|
395
|
-
export function FormatPicker({ selected, onSelect }: Props) {
|
|
396
|
-
return (
|
|
397
|
-
<div className="flex flex-wrap gap-2">
|
|
398
|
-
{FORMATS.map((format) => {
|
|
399
|
-
const isSelected = selected === format;
|
|
400
|
-
return (
|
|
401
|
-
<Button
|
|
402
|
-
key={format}
|
|
403
|
-
type="button"
|
|
404
|
-
variant={isSelected ? "PrimeStyle" : "BorderStyle"}
|
|
405
|
-
size="S"
|
|
406
|
-
onClick={() => onSelect(format)}
|
|
407
|
-
className="uppercase"
|
|
408
|
-
>
|
|
409
|
-
{format}
|
|
410
|
-
</Button>
|
|
411
|
-
);
|
|
412
|
-
})}
|
|
413
|
-
</div>
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
This gives you:
|
|
419
|
-
|
|
420
|
-
- Auto-sizing horizontal padding via the button's `px-*` defaults
|
|
421
|
-
- Theme-correct surface and text tokens (button variants are pre-themed)
|
|
422
|
-
- Clear pressed/unpressed visual via `PrimeStyle` (filled) vs `BorderStyle` (outlined)
|
|
423
|
-
- All the focus/disabled/loading states from `Button` for free
|
|
424
|
-
|
|
425
|
-
### When `Toggle` IS the right component
|
|
426
|
-
|
|
427
|
-
Reserve `Toggle` for its actual designed use case — **icon-only toolbar actions** with a binary on/off state:
|
|
428
|
-
|
|
429
|
-
- Bold / Italic / Underline in a rich-text toolbar
|
|
430
|
-
- Play / Pause
|
|
431
|
-
- Grid / List view
|
|
432
|
-
- Mute / Unmute
|
|
433
|
-
- Eye / Eye-slash (show/hide password)
|
|
434
|
-
|
|
435
|
-
```tsx
|
|
436
|
-
<Toggle pressed={isBold} onPressedChange={setBold} aria-label="Bold">
|
|
437
|
-
<i className="ri-bold" />
|
|
438
|
-
</Toggle>
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
The icon must fit within the square dimension — `[&_i]:text-[14px]` for size M, etc.
|
|
442
|
-
|
|
443
|
-
## Common Patterns
|
|
444
|
-
|
|
445
|
-
### Toolbar Group
|
|
446
|
-
|
|
447
|
-
```typescript
|
|
448
|
-
function EditorToolbar() {
|
|
449
|
-
const [styles, setStyles] = useState({
|
|
450
|
-
bold: false,
|
|
451
|
-
italic: false,
|
|
452
|
-
underline: false
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
const [alignment, setAlignment] = useState('left')
|
|
456
|
-
const [lists, setLists] = useState<string | null>(null)
|
|
457
|
-
|
|
458
|
-
return (
|
|
459
|
-
<div className="flex items-center gap-3 p-2 border-b">
|
|
460
|
-
{/* Text Styles */}
|
|
461
|
-
<div className="flex gap-1">
|
|
462
|
-
{Object.entries(styles).map(([style, active]) => (
|
|
463
|
-
<Toggle
|
|
464
|
-
key={style}
|
|
465
|
-
size="S"
|
|
466
|
-
pressed={active}
|
|
467
|
-
onPressedChange={(pressed) =>
|
|
468
|
-
setStyles(prev => ({ ...prev, [style]: pressed }))
|
|
469
|
-
}
|
|
470
|
-
>
|
|
471
|
-
<i className={`ri-${style}`} />
|
|
472
|
-
</Toggle>
|
|
473
|
-
))}
|
|
474
|
-
</div>
|
|
475
|
-
|
|
476
|
-
<div className="w-px h-6 bg-gray-300" />
|
|
477
|
-
|
|
478
|
-
{/* Alignment */}
|
|
479
|
-
<div className="flex gap-1">
|
|
480
|
-
{['left', 'center', 'right', 'justify'].map(align => (
|
|
481
|
-
<Toggle
|
|
482
|
-
key={align}
|
|
483
|
-
size="S"
|
|
484
|
-
pressed={alignment === align}
|
|
485
|
-
onPressedChange={() => setAlignment(align)}
|
|
486
|
-
>
|
|
487
|
-
<i className={`ri-align-${align}`} />
|
|
488
|
-
</Toggle>
|
|
489
|
-
))}
|
|
490
|
-
</div>
|
|
491
|
-
|
|
492
|
-
<div className="w-px h-6 bg-gray-300" />
|
|
493
|
-
|
|
494
|
-
{/* Lists */}
|
|
495
|
-
<div className="flex gap-1">
|
|
496
|
-
<Toggle
|
|
497
|
-
size="S"
|
|
498
|
-
pressed={lists === 'bullet'}
|
|
499
|
-
onPressedChange={() => setLists(lists === 'bullet' ? null : 'bullet')}
|
|
500
|
-
>
|
|
501
|
-
<i className="ri-list-unordered" />
|
|
502
|
-
</Toggle>
|
|
503
|
-
<Toggle
|
|
504
|
-
size="S"
|
|
505
|
-
pressed={lists === 'number'}
|
|
506
|
-
onPressedChange={() => setLists(lists === 'number' ? null : 'number')}
|
|
507
|
-
>
|
|
508
|
-
<i className="ri-list-ordered" />
|
|
509
|
-
</Toggle>
|
|
510
|
-
</div>
|
|
511
|
-
</div>
|
|
512
|
-
)
|
|
513
|
-
}
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
### Favorite Toggle
|
|
517
|
-
|
|
518
|
-
```typescript
|
|
519
|
-
function FavoriteButton({ itemId }: { itemId: string }) {
|
|
520
|
-
const [isFavorite, setIsFavorite] = useState(false)
|
|
521
|
-
const [isLoading, setIsLoading] = useState(false)
|
|
522
|
-
|
|
523
|
-
const toggleFavorite = async () => {
|
|
524
|
-
setIsLoading(true)
|
|
525
|
-
try {
|
|
526
|
-
// API call to toggle favorite
|
|
527
|
-
await toggleFavoriteAPI(itemId)
|
|
528
|
-
setIsFavorite(!isFavorite)
|
|
529
|
-
} finally {
|
|
530
|
-
setIsLoading(false)
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
return (
|
|
535
|
-
<Toggle
|
|
536
|
-
variant={isFavorite ? 'RedSecStyle' : 'BorderStyle'}
|
|
537
|
-
pressed={isFavorite}
|
|
538
|
-
onPressedChange={toggleFavorite}
|
|
539
|
-
disabled={isLoading}
|
|
540
|
-
aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
|
|
541
|
-
>
|
|
542
|
-
<i className={isFavorite ? 'ri-heart-fill' : 'ri-heart-line'} />
|
|
543
|
-
</Toggle>
|
|
544
|
-
)
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Theme Switcher
|
|
549
|
-
|
|
550
|
-
```typescript
|
|
551
|
-
function ThemeSwitcher() {
|
|
552
|
-
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
|
553
|
-
|
|
554
|
-
return (
|
|
555
|
-
<Toggle
|
|
556
|
-
size="L"
|
|
557
|
-
variant="BorderStyle"
|
|
558
|
-
pressed={theme === 'dark'}
|
|
559
|
-
onPressedChange={(pressed) => setTheme(pressed ? 'dark' : 'light')}
|
|
560
|
-
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
|
|
561
|
-
>
|
|
562
|
-
<i className={theme === 'dark' ? 'ri-moon-fill' : 'ri-sun-line'} />
|
|
563
|
-
</Toggle>
|
|
564
|
-
)
|
|
565
|
-
}
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
### Settings Panel
|
|
569
|
-
|
|
570
|
-
```typescript
|
|
571
|
-
function SettingsToggles() {
|
|
572
|
-
const [settings, setSettings] = useState({
|
|
573
|
-
notifications: true,
|
|
574
|
-
sounds: false,
|
|
575
|
-
vibration: true,
|
|
576
|
-
autoUpdate: false
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
const icons = {
|
|
580
|
-
notifications: 'ri-notification-line',
|
|
581
|
-
sounds: 'ri-volume-up-line',
|
|
582
|
-
vibration: 'ri-smartphone-line',
|
|
583
|
-
autoUpdate: 'ri-download-line'
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
return (
|
|
587
|
-
<div className="grid grid-cols-2 gap-4">
|
|
588
|
-
{Object.entries(settings).map(([key, value]) => (
|
|
589
|
-
<div key={key} className="flex items-center justify-between">
|
|
590
|
-
<label className="text-sm capitalize">{key.replace(/([A-Z])/g, ' $1')}</label>
|
|
591
|
-
<Toggle
|
|
592
|
-
size="S"
|
|
593
|
-
variant="BluSecStyle"
|
|
594
|
-
pressed={value}
|
|
595
|
-
onPressedChange={(pressed) =>
|
|
596
|
-
setSettings(prev => ({ ...prev, [key]: pressed }))
|
|
597
|
-
}
|
|
598
|
-
>
|
|
599
|
-
<i className={icons[key as keyof typeof icons]} />
|
|
600
|
-
</Toggle>
|
|
601
|
-
</div>
|
|
602
|
-
))}
|
|
603
|
-
</div>
|
|
604
|
-
)
|
|
605
|
-
}
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
## Testing
|
|
609
|
-
|
|
610
|
-
### Unit Test Example
|
|
611
|
-
|
|
612
|
-
```typescript
|
|
613
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
614
|
-
import { Toggle } from '@/components/Toggle'
|
|
615
|
-
|
|
616
|
-
describe('Toggle', () => {
|
|
617
|
-
it('toggles pressed state on click', () => {
|
|
618
|
-
const handleChange = jest.fn()
|
|
619
|
-
render(
|
|
620
|
-
<Toggle onPressedChange={handleChange}>
|
|
621
|
-
Toggle
|
|
622
|
-
</Toggle>
|
|
623
|
-
)
|
|
624
|
-
|
|
625
|
-
const toggle = screen.getByRole('button')
|
|
626
|
-
fireEvent.click(toggle)
|
|
627
|
-
|
|
628
|
-
expect(handleChange).toHaveBeenCalledWith(true)
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
it('applies variant styles', () => {
|
|
632
|
-
const { container } = render(
|
|
633
|
-
<Toggle variant="BluSecStyle">Blue</Toggle>
|
|
634
|
-
)
|
|
635
|
-
|
|
636
|
-
const toggle = container.querySelector('button')
|
|
637
|
-
expect(toggle).toHaveClass('hover:bg-background-presentation-state-information-primary')
|
|
638
|
-
})
|
|
639
|
-
|
|
640
|
-
it('respects size prop', () => {
|
|
641
|
-
const { container } = render(
|
|
642
|
-
<Toggle size="XL">Large</Toggle>
|
|
643
|
-
)
|
|
644
|
-
|
|
645
|
-
const toggle = container.querySelector('button')
|
|
646
|
-
expect(toggle).toHaveClass('h-[40px]', 'w-[40px]')
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
it('shows pressed state', () => {
|
|
650
|
-
render(<Toggle pressed>Pressed</Toggle>)
|
|
651
|
-
|
|
652
|
-
const toggle = screen.getByRole('button')
|
|
653
|
-
expect(toggle).toHaveAttribute('data-state', 'on')
|
|
654
|
-
})
|
|
655
|
-
|
|
656
|
-
it('handles disabled state', () => {
|
|
657
|
-
const handleChange = jest.fn()
|
|
658
|
-
render(
|
|
659
|
-
<Toggle disabled onPressedChange={handleChange}>
|
|
660
|
-
Disabled
|
|
661
|
-
</Toggle>
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
const toggle = screen.getByRole('button')
|
|
665
|
-
fireEvent.click(toggle)
|
|
666
|
-
|
|
667
|
-
expect(handleChange).not.toHaveBeenCalled()
|
|
668
|
-
expect(toggle).toHaveClass('cursor-not-allowed')
|
|
669
|
-
})
|
|
670
|
-
})
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
### Accessibility Test
|
|
674
|
-
|
|
675
|
-
```typescript
|
|
676
|
-
import { axe } from '@axe-core/react'
|
|
677
|
-
|
|
678
|
-
test('Toggle meets WCAG standards', async () => {
|
|
679
|
-
const { container } = render(
|
|
680
|
-
<Toggle aria-label="Toggle feature">
|
|
681
|
-
<i className="ri-star-line" />
|
|
682
|
-
</Toggle>
|
|
683
|
-
)
|
|
684
|
-
|
|
685
|
-
const results = await axe(container)
|
|
686
|
-
expect(results).toHaveNoViolations()
|
|
687
|
-
})
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
## Accessibility
|
|
691
|
-
|
|
692
|
-
### Keyboard Support
|
|
693
|
-
|
|
694
|
-
- **Space**: Toggle pressed state when focused
|
|
695
|
-
- **Enter**: Toggle pressed state
|
|
696
|
-
- **Tab**: Move focus to/from toggle
|
|
697
|
-
|
|
698
|
-
### ARIA Attributes
|
|
699
|
-
|
|
700
|
-
Radix UI automatically provides:
|
|
701
|
-
|
|
702
|
-
```html
|
|
703
|
-
<!-- Unpressed -->
|
|
704
|
-
<button
|
|
705
|
-
role="button"
|
|
706
|
-
aria-pressed="false"
|
|
707
|
-
data-state="off"
|
|
708
|
-
/>
|
|
709
|
-
|
|
710
|
-
<!-- Pressed -->
|
|
711
|
-
<button
|
|
712
|
-
role="button"
|
|
713
|
-
aria-pressed="true"
|
|
714
|
-
data-state="on"
|
|
715
|
-
/>
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
### Screen Reader Support
|
|
719
|
-
|
|
720
|
-
- Announces toggle button role
|
|
721
|
-
- Communicates pressed state
|
|
722
|
-
- Reads aria-label or content
|
|
723
|
-
- Announces state changes
|
|
724
|
-
|
|
725
|
-
### Best Practices
|
|
726
|
-
|
|
727
|
-
```typescript
|
|
728
|
-
// Always provide aria-label for icon-only toggles
|
|
729
|
-
<Toggle aria-label="Bold text">
|
|
730
|
-
<BoldIcon />
|
|
731
|
-
</Toggle>
|
|
732
|
-
|
|
733
|
-
// Or include screen-reader text
|
|
734
|
-
<Toggle>
|
|
735
|
-
<BoldIcon />
|
|
736
|
-
<span className="sr-only">Bold</span>
|
|
737
|
-
</Toggle>
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
## Styling
|
|
741
|
-
|
|
742
|
-
### Variant Styles
|
|
743
|
-
|
|
744
|
-
Each variant provides different visual styles:
|
|
745
|
-
|
|
746
|
-
- **PrimeStyle**: Default primary style
|
|
747
|
-
- **BluSecStyle**: Blue secondary style
|
|
748
|
-
- **YelSecStyle**: Yellow secondary style
|
|
749
|
-
- **RedSecStyle**: Red secondary style
|
|
750
|
-
- **BorderStyle**: Border-focused style
|
|
751
|
-
- **PrimeContStyle**: Primary container style
|
|
752
|
-
- **BluContStyle**: Blue container style
|
|
753
|
-
- **RedContStyle**: Red container style
|
|
754
|
-
|
|
755
|
-
### Size Classes
|
|
756
|
-
|
|
757
|
-
| Size | Dimensions | Typography | Icon Size |
|
|
758
|
-
|------|------------|------------|-----------|
|
|
759
|
-
| S | 22×22px | Small | 12px |
|
|
760
|
-
| M | 28×28px | Large | 18px |
|
|
761
|
-
| L | 34×34px | Large | 20px |
|
|
762
|
-
| XL | 40×40px | Headers | 22px |
|
|
763
|
-
|
|
764
|
-
### Custom Styles
|
|
765
|
-
|
|
766
|
-
```typescript
|
|
767
|
-
<Toggle
|
|
768
|
-
className="custom-toggle rounded-full"
|
|
769
|
-
size="L"
|
|
770
|
-
variant="PrimeStyle"
|
|
771
|
-
/>
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
## Performance
|
|
775
|
-
|
|
776
|
-
| Metric | Value |
|
|
777
|
-
|--------|-------|
|
|
778
|
-
| Bundle size (gzip) | 2.4kb |
|
|
779
|
-
| First render | <5ms |
|
|
780
|
-
| Re-render | <2ms |
|
|
781
|
-
| Interaction | <1ms |
|
|
782
|
-
| Tree-shakeable | ✅ |
|
|
783
|
-
|
|
784
|
-
### Optimization Tips
|
|
785
|
-
|
|
786
|
-
1. Use uncontrolled mode with `defaultPressed` when possible
|
|
787
|
-
2. Memoize toggle groups with React.memo
|
|
788
|
-
3. Use single state object for multiple toggles
|
|
789
|
-
4. Avoid recreating event handlers
|
|
790
|
-
|
|
791
|
-
## Migration
|
|
792
|
-
|
|
793
|
-
### From Button
|
|
794
|
-
|
|
795
|
-
```diff
|
|
796
|
-
// From button with active state
|
|
797
|
-
- <button
|
|
798
|
-
- className={isActive ? 'bg-blue-500' : 'bg-gray-200'}
|
|
799
|
-
- onClick={() => setIsActive(!isActive)}
|
|
800
|
-
- >
|
|
801
|
-
- Icon
|
|
802
|
-
- </button>
|
|
803
|
-
|
|
804
|
-
// To Toggle
|
|
805
|
-
+ <Toggle
|
|
806
|
-
+ pressed={isActive}
|
|
807
|
-
+ onPressedChange={setIsActive}
|
|
808
|
-
+ >
|
|
809
|
-
+ Icon
|
|
810
|
-
+ </Toggle>
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### From Custom Toggle
|
|
814
|
-
|
|
815
|
-
```diff
|
|
816
|
-
// From custom implementation
|
|
817
|
-
- <div
|
|
818
|
-
- className={`toggle ${toggled ? 'active' : ''}`}
|
|
819
|
-
- onClick={() => setToggled(!toggled)}
|
|
820
|
-
- />
|
|
821
|
-
|
|
822
|
-
// To Toggle component
|
|
823
|
-
+ <Toggle
|
|
824
|
-
+ pressed={toggled}
|
|
825
|
-
+ onPressedChange={setToggled}
|
|
826
|
-
+ />
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
## Troubleshooting
|
|
830
|
-
|
|
831
|
-
### Toggle not changing state
|
|
832
|
-
|
|
833
|
-
**Solution:** Use controlled or uncontrolled mode properly
|
|
834
|
-
|
|
835
|
-
```typescript
|
|
836
|
-
// Controlled
|
|
837
|
-
<Toggle pressed={value} onPressedChange={setValue} />
|
|
838
|
-
|
|
839
|
-
// Uncontrolled
|
|
840
|
-
<Toggle defaultPressed />
|
|
841
|
-
```
|
|
842
|
-
|
|
843
|
-
### Icons not sizing correctly
|
|
844
|
-
|
|
845
|
-
**Solution:** Use the icon class utilities
|
|
846
|
-
|
|
847
|
-
```typescript
|
|
848
|
-
<Toggle size="L">
|
|
849
|
-
<i className="ri-heart-line" />
|
|
850
|
-
{/* Icon size handled by component */}
|
|
851
|
-
</Toggle>
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
## Related Components
|
|
855
|
-
|
|
856
|
-
- [Switch](/docs/components/switch.md) - For on/off states
|
|
857
|
-
- [Button](/docs/components/button.md) - For actions
|
|
858
|
-
- [Checkbox](/docs/components/checkbox.md) - For selections
|
|
859
|
-
- [Radio](/docs/components/radio.md) - For single choice
|
|
860
|
-
|
|
861
|
-
## Browser Support
|
|
862
|
-
|
|
863
|
-
- Chrome 90+ ✅
|
|
864
|
-
- Firefox 88+ ✅
|
|
865
|
-
- Safari 14+ ✅
|
|
866
|
-
- Edge 90+ ✅
|
|
867
|
-
- Mobile browsers ✅
|
|
868
|
-
|
|
869
|
-
## Changelog
|
|
870
|
-
|
|
871
|
-
### v1.1.15
|
|
872
|
-
- Migrated to Radix UI Toggle primitive
|
|
873
|
-
- Added 8 style variants
|
|
874
|
-
- Added 4 size variants
|
|
875
|
-
- Improved accessibility
|
|
876
|
-
|
|
877
|
-
### v1.1.14
|
|
878
|
-
- Enhanced visual styles
|
|
879
|
-
- Fixed focus management
|
|
880
|
-
|
|
881
|
-
### v1.1.0
|
|
882
|
-
- Initial stable release
|