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,801 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: RadioCard
|
|
3
|
-
version: 2.4.5
|
|
4
|
-
status: stable
|
|
5
|
-
category: components/forms
|
|
6
|
-
tags: [form, radio, card, selection, compound, accessible]
|
|
7
|
-
last-reviewed: 2024-11-05
|
|
8
|
-
bundle-size: 2.8kb
|
|
9
|
-
dependencies:
|
|
10
|
-
- "@/components/Radio": "internal"
|
|
11
|
-
- "@/components/Card": "internal"
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# RadioCard
|
|
15
|
-
|
|
16
|
-
> A card-style radio button component that combines Radio with Card for rich, visual selection options. Perfect for plan selection, feature choices, and any scenario requiring detailed radio options with descriptions and custom content.
|
|
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 RadioCard
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`add` also copies any components, hooks, and utilities that `RadioCard` 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 { RadioCard } from "@/components/RadioCard";
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Examples
|
|
39
|
-
|
|
40
|
-
### Basic Usage
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { RadioCard } from '@/components/RadioCard'
|
|
44
|
-
import { RadioGroup } from '@/components/Radio'
|
|
45
|
-
|
|
46
|
-
function Example() {
|
|
47
|
-
const [selected, setSelected] = useState('option1')
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<RadioGroup value={selected} onValueChange={setSelected}>
|
|
51
|
-
<div className="grid gap-4">
|
|
52
|
-
<RadioCard
|
|
53
|
-
value="option1"
|
|
54
|
-
id="card1"
|
|
55
|
-
headerLabel="Option 1"
|
|
56
|
-
description="Description for option 1"
|
|
57
|
-
/>
|
|
58
|
-
<RadioCard
|
|
59
|
-
value="option2"
|
|
60
|
-
id="card2"
|
|
61
|
-
headerLabel="Option 2"
|
|
62
|
-
description="Description for option 2"
|
|
63
|
-
/>
|
|
64
|
-
</div>
|
|
65
|
-
</RadioGroup>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Plan Selection
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
function PricingPlans() {
|
|
74
|
-
const [plan, setPlan] = useState('pro')
|
|
75
|
-
|
|
76
|
-
const plans = [
|
|
77
|
-
{
|
|
78
|
-
value: 'basic',
|
|
79
|
-
name: 'Basic',
|
|
80
|
-
price: '$9/month',
|
|
81
|
-
description: 'Perfect for individuals',
|
|
82
|
-
features: ['10 GB Storage', '1 User', 'Email Support']
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
value: 'pro',
|
|
86
|
-
name: 'Pro',
|
|
87
|
-
price: '$29/month',
|
|
88
|
-
description: 'Great for small teams',
|
|
89
|
-
features: ['100 GB Storage', '5 Users', 'Priority Support', 'API Access']
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
value: 'enterprise',
|
|
93
|
-
name: 'Enterprise',
|
|
94
|
-
price: '$99/month',
|
|
95
|
-
description: 'For large organizations',
|
|
96
|
-
features: ['Unlimited Storage', 'Unlimited Users', '24/7 Support', 'Custom Integration']
|
|
97
|
-
}
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<RadioGroup value={plan} onValueChange={setPlan}>
|
|
102
|
-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
103
|
-
{plans.map((p) => (
|
|
104
|
-
<RadioCard
|
|
105
|
-
key={p.value}
|
|
106
|
-
value={p.value}
|
|
107
|
-
id={`plan-${p.value}`}
|
|
108
|
-
headerLabel={
|
|
109
|
-
<div>
|
|
110
|
-
<h3 className="font-semibold">{p.name}</h3>
|
|
111
|
-
<p className="text-2xl font-bold mt-2">{p.price}</p>
|
|
112
|
-
</div>
|
|
113
|
-
}
|
|
114
|
-
description={p.description}
|
|
115
|
-
>
|
|
116
|
-
<ul className="mt-4 space-y-2">
|
|
117
|
-
{p.features.map((feature, i) => (
|
|
118
|
-
<li key={i} className="flex items-center gap-2">
|
|
119
|
-
<span className="text-green-500">✓</span>
|
|
120
|
-
<span className="text-sm">{feature}</span>
|
|
121
|
-
</li>
|
|
122
|
-
))}
|
|
123
|
-
</ul>
|
|
124
|
-
</RadioCard>
|
|
125
|
-
))}
|
|
126
|
-
</div>
|
|
127
|
-
</RadioGroup>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Shipping Methods
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
function ShippingOptions() {
|
|
136
|
-
const [shipping, setShipping] = useState('standard')
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<RadioGroup value={shipping} onValueChange={setShipping}>
|
|
140
|
-
<div className="space-y-3">
|
|
141
|
-
<RadioCard
|
|
142
|
-
value="standard"
|
|
143
|
-
id="ship-standard"
|
|
144
|
-
headerLabel={
|
|
145
|
-
<div className="flex justify-between items-center">
|
|
146
|
-
<span>Standard Shipping</span>
|
|
147
|
-
<span className="text-green-600">Free</span>
|
|
148
|
-
</div>
|
|
149
|
-
}
|
|
150
|
-
description="Delivered in 5-7 business days"
|
|
151
|
-
/>
|
|
152
|
-
|
|
153
|
-
<RadioCard
|
|
154
|
-
value="express"
|
|
155
|
-
id="ship-express"
|
|
156
|
-
headerLabel={
|
|
157
|
-
<div className="flex justify-between items-center">
|
|
158
|
-
<span>Express Shipping</span>
|
|
159
|
-
<span className="font-semibold">$12.99</span>
|
|
160
|
-
</div>
|
|
161
|
-
}
|
|
162
|
-
description="Delivered in 2-3 business days"
|
|
163
|
-
/>
|
|
164
|
-
|
|
165
|
-
<RadioCard
|
|
166
|
-
value="overnight"
|
|
167
|
-
id="ship-overnight"
|
|
168
|
-
headerLabel={
|
|
169
|
-
<div className="flex justify-between items-center">
|
|
170
|
-
<span>Overnight Shipping</span>
|
|
171
|
-
<span className="font-semibold">$29.99</span>
|
|
172
|
-
</div>
|
|
173
|
-
}
|
|
174
|
-
description="Delivered next business day by 10 AM"
|
|
175
|
-
/>
|
|
176
|
-
</div>
|
|
177
|
-
</RadioGroup>
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Payment Methods
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
function PaymentMethodCards() {
|
|
186
|
-
const [method, setMethod] = useState('card')
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<RadioGroup value={method} onValueChange={setMethod}>
|
|
190
|
-
<div className="grid grid-cols-2 gap-4">
|
|
191
|
-
<RadioCard
|
|
192
|
-
value="card"
|
|
193
|
-
id="pay-card"
|
|
194
|
-
headerLabel={
|
|
195
|
-
<div className="flex items-center gap-3">
|
|
196
|
-
<CreditCardIcon className="w-6 h-6" />
|
|
197
|
-
<span>Credit/Debit Card</span>
|
|
198
|
-
</div>
|
|
199
|
-
}
|
|
200
|
-
description="Pay with Visa, Mastercard, or Amex"
|
|
201
|
-
>
|
|
202
|
-
<div className="flex gap-2 mt-3">
|
|
203
|
-
<img src="/visa.svg" alt="Visa" className="h-8" />
|
|
204
|
-
<img src="/mastercard.svg" alt="Mastercard" className="h-8" />
|
|
205
|
-
<img src="/amex.svg" alt="Amex" className="h-8" />
|
|
206
|
-
</div>
|
|
207
|
-
</RadioCard>
|
|
208
|
-
|
|
209
|
-
<RadioCard
|
|
210
|
-
value="paypal"
|
|
211
|
-
id="pay-paypal"
|
|
212
|
-
headerLabel={
|
|
213
|
-
<div className="flex items-center gap-3">
|
|
214
|
-
<PayPalIcon className="w-6 h-6" />
|
|
215
|
-
<span>PayPal</span>
|
|
216
|
-
</div>
|
|
217
|
-
}
|
|
218
|
-
description="Fast and secure payment"
|
|
219
|
-
/>
|
|
220
|
-
|
|
221
|
-
<RadioCard
|
|
222
|
-
value="crypto"
|
|
223
|
-
id="pay-crypto"
|
|
224
|
-
headerLabel={
|
|
225
|
-
<div className="flex items-center gap-3">
|
|
226
|
-
<BitcoinIcon className="w-6 h-6" />
|
|
227
|
-
<span>Cryptocurrency</span>
|
|
228
|
-
</div>
|
|
229
|
-
}
|
|
230
|
-
description="Pay with Bitcoin or Ethereum"
|
|
231
|
-
/>
|
|
232
|
-
|
|
233
|
-
<RadioCard
|
|
234
|
-
value="bank"
|
|
235
|
-
id="pay-bank"
|
|
236
|
-
headerLabel={
|
|
237
|
-
<div className="flex items-center gap-3">
|
|
238
|
-
<BankIcon className="w-6 h-6" />
|
|
239
|
-
<span>Bank Transfer</span>
|
|
240
|
-
</div>
|
|
241
|
-
}
|
|
242
|
-
description="Direct bank transfer"
|
|
243
|
-
/>
|
|
244
|
-
</div>
|
|
245
|
-
</RadioGroup>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Theme Selection
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
function ThemeSelector() {
|
|
254
|
-
const [theme, setTheme] = useState('light')
|
|
255
|
-
|
|
256
|
-
return (
|
|
257
|
-
<RadioGroup value={theme} onValueChange={setTheme}>
|
|
258
|
-
<div className="grid grid-cols-3 gap-4">
|
|
259
|
-
<RadioCard
|
|
260
|
-
value="light"
|
|
261
|
-
id="theme-light"
|
|
262
|
-
theme="light"
|
|
263
|
-
headerLabel={
|
|
264
|
-
<div className="text-center">
|
|
265
|
-
<SunIcon className="w-8 h-8 mx-auto mb-2" />
|
|
266
|
-
<span>Light</span>
|
|
267
|
-
</div>
|
|
268
|
-
}
|
|
269
|
-
description="Bright and clean"
|
|
270
|
-
/>
|
|
271
|
-
|
|
272
|
-
<RadioCard
|
|
273
|
-
value="dark"
|
|
274
|
-
id="theme-dark"
|
|
275
|
-
theme="dark"
|
|
276
|
-
headerLabel={
|
|
277
|
-
<div className="text-center">
|
|
278
|
-
<MoonIcon className="w-8 h-8 mx-auto mb-2" />
|
|
279
|
-
<span>Dark</span>
|
|
280
|
-
</div>
|
|
281
|
-
}
|
|
282
|
-
description="Easy on the eyes"
|
|
283
|
-
/>
|
|
284
|
-
|
|
285
|
-
<RadioCard
|
|
286
|
-
value="auto"
|
|
287
|
-
id="theme-auto"
|
|
288
|
-
theme="default"
|
|
289
|
-
headerLabel={
|
|
290
|
-
<div className="text-center">
|
|
291
|
-
<AutoIcon className="w-8 h-8 mx-auto mb-2" />
|
|
292
|
-
<span>Auto</span>
|
|
293
|
-
</div>
|
|
294
|
-
}
|
|
295
|
-
description="Follow system"
|
|
296
|
-
/>
|
|
297
|
-
</div>
|
|
298
|
-
</RadioGroup>
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Disabled State
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
function DisabledOptions() {
|
|
307
|
-
const [selected, setSelected] = useState('available')
|
|
308
|
-
|
|
309
|
-
return (
|
|
310
|
-
<RadioGroup value={selected} onValueChange={setSelected}>
|
|
311
|
-
<div className="space-y-3">
|
|
312
|
-
<RadioCard
|
|
313
|
-
value="available"
|
|
314
|
-
id="opt-available"
|
|
315
|
-
headerLabel="Available Option"
|
|
316
|
-
description="This option can be selected"
|
|
317
|
-
/>
|
|
318
|
-
|
|
319
|
-
<RadioCard
|
|
320
|
-
value="unavailable"
|
|
321
|
-
id="opt-unavailable"
|
|
322
|
-
headerLabel="Unavailable Option"
|
|
323
|
-
description="This option is currently not available"
|
|
324
|
-
disabled
|
|
325
|
-
/>
|
|
326
|
-
</div>
|
|
327
|
-
</RadioGroup>
|
|
328
|
-
)
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## API Reference
|
|
333
|
-
|
|
334
|
-
### RadioCard Props
|
|
335
|
-
|
|
336
|
-
| Prop | Type | Default | Description |
|
|
337
|
-
|------|------|---------|-------------|
|
|
338
|
-
| `value` | `string` | - | Radio button value (required) |
|
|
339
|
-
| `id` | `string` | - | Unique identifier (required) |
|
|
340
|
-
| `headerLabel` | `ReactNode` | - | Content for card header |
|
|
341
|
-
| `description` | `ReactNode` | - | Description content |
|
|
342
|
-
| `children` | `ReactNode` | - | Additional card content |
|
|
343
|
-
| `disabled` | `boolean` | `false` | Disables the radio card |
|
|
344
|
-
| `theme` | `'dark' \| 'light' \| 'default'` | - | Theme variant |
|
|
345
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
346
|
-
|
|
347
|
-
Note: Must be used within a `RadioGroup` component.
|
|
348
|
-
|
|
349
|
-
### TypeScript
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
import { ComponentProps, ReactNode } from 'react'
|
|
353
|
-
import { Radio } from '@/components/Radio'
|
|
354
|
-
|
|
355
|
-
interface RadioCardProps extends Omit<ComponentProps<typeof Radio>, 'size' | 'value'> {
|
|
356
|
-
headerLabel?: ReactNode
|
|
357
|
-
id: string
|
|
358
|
-
description?: ReactNode
|
|
359
|
-
disabled?: boolean
|
|
360
|
-
children?: ReactNode
|
|
361
|
-
theme?: 'dark' | 'light' | 'default'
|
|
362
|
-
value: string
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export const RadioCard: React.ForwardRefExoticComponent<RadioCardProps>
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Common Patterns
|
|
369
|
-
|
|
370
|
-
### Subscription Tiers
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
function SubscriptionTiers() {
|
|
374
|
-
const [tier, setTier] = useState('pro')
|
|
375
|
-
|
|
376
|
-
const tiers = [
|
|
377
|
-
{
|
|
378
|
-
value: 'free',
|
|
379
|
-
name: 'Free',
|
|
380
|
-
price: '$0',
|
|
381
|
-
badge: null,
|
|
382
|
-
features: {
|
|
383
|
-
included: ['Basic features', '1 Project', 'Community support'],
|
|
384
|
-
excluded: ['Advanced analytics', 'Priority support', 'Custom domains']
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
{
|
|
388
|
-
value: 'pro',
|
|
389
|
-
name: 'Pro',
|
|
390
|
-
price: '$19',
|
|
391
|
-
badge: 'Most Popular',
|
|
392
|
-
features: {
|
|
393
|
-
included: ['All Free features', '10 Projects', 'Advanced analytics', 'Priority support'],
|
|
394
|
-
excluded: ['Custom domains', 'White label']
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
value: 'business',
|
|
399
|
-
name: 'Business',
|
|
400
|
-
price: '$49',
|
|
401
|
-
badge: 'Best Value',
|
|
402
|
-
features: {
|
|
403
|
-
included: ['All Pro features', 'Unlimited Projects', 'Custom domains', 'White label', 'API Access'],
|
|
404
|
-
excluded: []
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
]
|
|
408
|
-
|
|
409
|
-
return (
|
|
410
|
-
<RadioGroup value={tier} onValueChange={setTier}>
|
|
411
|
-
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
412
|
-
{tiers.map((t) => (
|
|
413
|
-
<RadioCard
|
|
414
|
-
key={t.value}
|
|
415
|
-
value={t.value}
|
|
416
|
-
id={`tier-${t.value}`}
|
|
417
|
-
className="relative"
|
|
418
|
-
headerLabel={
|
|
419
|
-
<div>
|
|
420
|
-
{t.badge && (
|
|
421
|
-
<span className="absolute -top-3 right-4 bg-blue-500 text-white px-3 py-1 rounded-full text-xs">
|
|
422
|
-
{t.badge}
|
|
423
|
-
</span>
|
|
424
|
-
)}
|
|
425
|
-
<h3 className="text-xl font-bold">{t.name}</h3>
|
|
426
|
-
<div className="mt-4">
|
|
427
|
-
<span className="text-3xl font-bold">{t.price}</span>
|
|
428
|
-
<span className="text-gray-500">/month</span>
|
|
429
|
-
</div>
|
|
430
|
-
</div>
|
|
431
|
-
}
|
|
432
|
-
>
|
|
433
|
-
<div className="mt-6 space-y-4">
|
|
434
|
-
<div>
|
|
435
|
-
{t.features.included.map((feature, i) => (
|
|
436
|
-
<div key={i} className="flex items-center gap-2 py-1">
|
|
437
|
-
<CheckIcon className="w-5 h-5 text-green-500" />
|
|
438
|
-
<span className="text-sm">{feature}</span>
|
|
439
|
-
</div>
|
|
440
|
-
))}
|
|
441
|
-
</div>
|
|
442
|
-
{t.features.excluded.length > 0 && (
|
|
443
|
-
<div>
|
|
444
|
-
{t.features.excluded.map((feature, i) => (
|
|
445
|
-
<div key={i} className="flex items-center gap-2 py-1 opacity-50">
|
|
446
|
-
<XIcon className="w-5 h-5 text-gray-400" />
|
|
447
|
-
<span className="text-sm line-through">{feature}</span>
|
|
448
|
-
</div>
|
|
449
|
-
))}
|
|
450
|
-
</div>
|
|
451
|
-
)}
|
|
452
|
-
</div>
|
|
453
|
-
</RadioCard>
|
|
454
|
-
))}
|
|
455
|
-
</div>
|
|
456
|
-
</RadioGroup>
|
|
457
|
-
)
|
|
458
|
-
}
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
### Deployment Options
|
|
462
|
-
|
|
463
|
-
```typescript
|
|
464
|
-
function DeploymentOptions() {
|
|
465
|
-
const [deployment, setDeployment] = useState('cloud')
|
|
466
|
-
|
|
467
|
-
return (
|
|
468
|
-
<RadioGroup value={deployment} onValueChange={setDeployment}>
|
|
469
|
-
<div className="space-y-4">
|
|
470
|
-
<RadioCard
|
|
471
|
-
value="cloud"
|
|
472
|
-
id="deploy-cloud"
|
|
473
|
-
headerLabel={
|
|
474
|
-
<div className="flex items-center gap-3">
|
|
475
|
-
<CloudIcon className="w-6 h-6 text-blue-500" />
|
|
476
|
-
<div>
|
|
477
|
-
<h3 className="font-semibold">Cloud Hosted</h3>
|
|
478
|
-
<p className="text-xs text-gray-500">Managed by us</p>
|
|
479
|
-
</div>
|
|
480
|
-
</div>
|
|
481
|
-
}
|
|
482
|
-
description="Zero maintenance, automatic updates"
|
|
483
|
-
>
|
|
484
|
-
<div className="mt-3 p-3 bg-blue-50 rounded">
|
|
485
|
-
<p className="text-sm">✓ 99.9% uptime SLA</p>
|
|
486
|
-
<p className="text-sm">✓ Automatic backups</p>
|
|
487
|
-
<p className="text-sm">✓ 24/7 monitoring</p>
|
|
488
|
-
</div>
|
|
489
|
-
</RadioCard>
|
|
490
|
-
|
|
491
|
-
<RadioCard
|
|
492
|
-
value="onprem"
|
|
493
|
-
id="deploy-onprem"
|
|
494
|
-
headerLabel={
|
|
495
|
-
<div className="flex items-center gap-3">
|
|
496
|
-
<ServerIcon className="w-6 h-6 text-gray-500" />
|
|
497
|
-
<div>
|
|
498
|
-
<h3 className="font-semibold">On-Premise</h3>
|
|
499
|
-
<p className="text-xs text-gray-500">Self-hosted</p>
|
|
500
|
-
</div>
|
|
501
|
-
</div>
|
|
502
|
-
}
|
|
503
|
-
description="Full control over your data"
|
|
504
|
-
>
|
|
505
|
-
<div className="mt-3 p-3 bg-gray-50 rounded">
|
|
506
|
-
<p className="text-sm">✓ Complete data sovereignty</p>
|
|
507
|
-
<p className="text-sm">✓ Custom configurations</p>
|
|
508
|
-
<p className="text-sm">✓ Air-gapped deployment</p>
|
|
509
|
-
</div>
|
|
510
|
-
</RadioCard>
|
|
511
|
-
|
|
512
|
-
<RadioCard
|
|
513
|
-
value="hybrid"
|
|
514
|
-
id="deploy-hybrid"
|
|
515
|
-
headerLabel={
|
|
516
|
-
<div className="flex items-center gap-3">
|
|
517
|
-
<HybridIcon className="w-6 h-6 text-purple-500" />
|
|
518
|
-
<div>
|
|
519
|
-
<h3 className="font-semibold">Hybrid</h3>
|
|
520
|
-
<p className="text-xs text-gray-500">Best of both</p>
|
|
521
|
-
</div>
|
|
522
|
-
</div>
|
|
523
|
-
}
|
|
524
|
-
description="Flexible deployment model"
|
|
525
|
-
>
|
|
526
|
-
<div className="mt-3 p-3 bg-purple-50 rounded">
|
|
527
|
-
<p className="text-sm">✓ Sensitive data on-premise</p>
|
|
528
|
-
<p className="text-sm">✓ Cloud scalability</p>
|
|
529
|
-
<p className="text-sm">✓ Unified management</p>
|
|
530
|
-
</div>
|
|
531
|
-
</RadioCard>
|
|
532
|
-
</div>
|
|
533
|
-
</RadioGroup>
|
|
534
|
-
)
|
|
535
|
-
}
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
## Testing
|
|
539
|
-
|
|
540
|
-
### Unit Test Example
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
544
|
-
import { RadioCard } from '@/components/RadioCard'
|
|
545
|
-
import { RadioGroup } from '@/components/Radio'
|
|
546
|
-
|
|
547
|
-
describe('RadioCard', () => {
|
|
548
|
-
it('renders card with header and description', () => {
|
|
549
|
-
render(
|
|
550
|
-
<RadioGroup>
|
|
551
|
-
<RadioCard
|
|
552
|
-
value="test"
|
|
553
|
-
id="test-card"
|
|
554
|
-
headerLabel="Test Header"
|
|
555
|
-
description="Test Description"
|
|
556
|
-
/>
|
|
557
|
-
</RadioGroup>
|
|
558
|
-
)
|
|
559
|
-
|
|
560
|
-
expect(screen.getByText('Test Header')).toBeInTheDocument()
|
|
561
|
-
expect(screen.getByText('Test Description')).toBeInTheDocument()
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
it('handles selection in group', () => {
|
|
565
|
-
const handleChange = jest.fn()
|
|
566
|
-
render(
|
|
567
|
-
<RadioGroup onValueChange={handleChange}>
|
|
568
|
-
<RadioCard value="card1" id="card1" headerLabel="Card 1" />
|
|
569
|
-
<RadioCard value="card2" id="card2" headerLabel="Card 2" />
|
|
570
|
-
</RadioGroup>
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
const card2 = screen.getByText('Card 2').closest('label')
|
|
574
|
-
fireEvent.click(card2!)
|
|
575
|
-
|
|
576
|
-
expect(handleChange).toHaveBeenCalledWith('card2')
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
it('renders children content', () => {
|
|
580
|
-
render(
|
|
581
|
-
<RadioGroup>
|
|
582
|
-
<RadioCard value="test" id="test">
|
|
583
|
-
<div>Custom Content</div>
|
|
584
|
-
</RadioCard>
|
|
585
|
-
</RadioGroup>
|
|
586
|
-
)
|
|
587
|
-
|
|
588
|
-
expect(screen.getByText('Custom Content')).toBeInTheDocument()
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
it('applies disabled state', () => {
|
|
592
|
-
render(
|
|
593
|
-
<RadioGroup>
|
|
594
|
-
<RadioCard
|
|
595
|
-
value="test"
|
|
596
|
-
id="test"
|
|
597
|
-
headerLabel="Disabled"
|
|
598
|
-
disabled
|
|
599
|
-
/>
|
|
600
|
-
</RadioGroup>
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
const radio = screen.getByRole('radio')
|
|
604
|
-
expect(radio).toBeDisabled()
|
|
605
|
-
})
|
|
606
|
-
})
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
### Accessibility Test
|
|
610
|
-
|
|
611
|
-
```typescript
|
|
612
|
-
import { axe } from '@axe-core/react'
|
|
613
|
-
|
|
614
|
-
test('RadioCard meets WCAG standards', async () => {
|
|
615
|
-
const { container } = render(
|
|
616
|
-
<fieldset>
|
|
617
|
-
<legend>Select a plan</legend>
|
|
618
|
-
<RadioGroup defaultValue="basic">
|
|
619
|
-
<RadioCard
|
|
620
|
-
value="basic"
|
|
621
|
-
id="plan-basic"
|
|
622
|
-
headerLabel="Basic Plan"
|
|
623
|
-
description="Perfect for starters"
|
|
624
|
-
/>
|
|
625
|
-
<RadioCard
|
|
626
|
-
value="pro"
|
|
627
|
-
id="plan-pro"
|
|
628
|
-
headerLabel="Pro Plan"
|
|
629
|
-
description="For professionals"
|
|
630
|
-
/>
|
|
631
|
-
</RadioGroup>
|
|
632
|
-
</fieldset>
|
|
633
|
-
)
|
|
634
|
-
|
|
635
|
-
const results = await axe(container)
|
|
636
|
-
expect(results).toHaveNoViolations()
|
|
637
|
-
})
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
## Accessibility
|
|
641
|
-
|
|
642
|
-
### Features
|
|
643
|
-
|
|
644
|
-
- Radio button positioned in top-right corner for easy access
|
|
645
|
-
- Entire card is clickable via label association
|
|
646
|
-
- Proper focus states inherited from Radio component
|
|
647
|
-
- Selected state indicated by border color change
|
|
648
|
-
- Disabled state clearly communicated visually
|
|
649
|
-
|
|
650
|
-
### Keyboard Support
|
|
651
|
-
|
|
652
|
-
- **Tab**: Move focus into radio group
|
|
653
|
-
- **Arrow Keys**: Navigate between cards
|
|
654
|
-
- **Space**: Select focused card
|
|
655
|
-
|
|
656
|
-
### Screen Reader Support
|
|
657
|
-
|
|
658
|
-
- Card content is associated with radio button
|
|
659
|
-
- Selected state announced
|
|
660
|
-
- Descriptions and labels read in logical order
|
|
661
|
-
|
|
662
|
-
### ARIA Implementation
|
|
663
|
-
|
|
664
|
-
```html
|
|
665
|
-
<!-- Rendered structure -->
|
|
666
|
-
<label for="radio-id" class="card">
|
|
667
|
-
<div class="radio-container">
|
|
668
|
-
<button role="radio" aria-checked="false" id="radio-id" />
|
|
669
|
-
</div>
|
|
670
|
-
<header>Header Content</header>
|
|
671
|
-
<div>Description</div>
|
|
672
|
-
<div>Additional Content</div>
|
|
673
|
-
</label>
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
## Styling
|
|
677
|
-
|
|
678
|
-
### Custom Styles
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
<RadioCard
|
|
682
|
-
className="hover:shadow-lg transition-shadow"
|
|
683
|
-
headerLabel="Custom Styled"
|
|
684
|
-
/>
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
### State Styling
|
|
688
|
-
|
|
689
|
-
- Default: Normal card appearance
|
|
690
|
-
- Hover: Subtle border color change
|
|
691
|
-
- Selected: Blue border indicating selection
|
|
692
|
-
- Disabled: Grayed out appearance with cursor-not-allowed
|
|
693
|
-
|
|
694
|
-
### Theme Variables
|
|
695
|
-
|
|
696
|
-
```css
|
|
697
|
-
[data-theme="custom"] {
|
|
698
|
-
--radio-card-border: #e5e7eb;
|
|
699
|
-
--radio-card-border-selected: #3b82f6;
|
|
700
|
-
--radio-card-bg-disabled: #f9fafb;
|
|
701
|
-
}
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
## Performance
|
|
705
|
-
|
|
706
|
-
| Metric | Value |
|
|
707
|
-
|--------|-------|
|
|
708
|
-
| Bundle size (gzip) | 2.8kb |
|
|
709
|
-
| First render | <8ms |
|
|
710
|
-
| Re-render | <4ms |
|
|
711
|
-
| Tree-shakeable | ✅ |
|
|
712
|
-
|
|
713
|
-
### Optimization Tips
|
|
714
|
-
|
|
715
|
-
1. Memoize complex card content with React.memo
|
|
716
|
-
2. Use virtualization for long lists of cards
|
|
717
|
-
3. Lazy load heavy content (images, icons)
|
|
718
|
-
4. Keep RadioGroup structure flat
|
|
719
|
-
|
|
720
|
-
## Migration
|
|
721
|
-
|
|
722
|
-
### From Radio to RadioCard
|
|
723
|
-
|
|
724
|
-
```diff
|
|
725
|
-
// From basic Radio
|
|
726
|
-
- <RadioGroup>
|
|
727
|
-
- <div>
|
|
728
|
-
- <Radio value="opt" id="opt" />
|
|
729
|
-
- <label htmlFor="opt">
|
|
730
|
-
- <h3>Title</h3>
|
|
731
|
-
- <p>Description</p>
|
|
732
|
-
- </label>
|
|
733
|
-
- </div>
|
|
734
|
-
- </RadioGroup>
|
|
735
|
-
|
|
736
|
-
// To RadioCard
|
|
737
|
-
+ <RadioGroup>
|
|
738
|
-
+ <RadioCard
|
|
739
|
-
+ value="opt"
|
|
740
|
-
+ id="opt"
|
|
741
|
-
+ headerLabel="Title"
|
|
742
|
-
+ description="Description"
|
|
743
|
-
+ />
|
|
744
|
-
+ </RadioGroup>
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
## Troubleshooting
|
|
748
|
-
|
|
749
|
-
### Card not selectable
|
|
750
|
-
|
|
751
|
-
**Solution:** Ensure RadioCard is within RadioGroup
|
|
752
|
-
|
|
753
|
-
```typescript
|
|
754
|
-
// ❌ Wrong
|
|
755
|
-
<RadioCard value="opt" id="opt" />
|
|
756
|
-
|
|
757
|
-
// ✅ Correct
|
|
758
|
-
<RadioGroup>
|
|
759
|
-
<RadioCard value="opt" id="opt" />
|
|
760
|
-
</RadioGroup>
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
### Content overflow
|
|
764
|
-
|
|
765
|
-
**Solution:** Use appropriate layout classes
|
|
766
|
-
|
|
767
|
-
```typescript
|
|
768
|
-
<RadioCard
|
|
769
|
-
className="overflow-hidden"
|
|
770
|
-
headerLabel={<div className="truncate">Long text...</div>}
|
|
771
|
-
/>
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
## Related Components
|
|
775
|
-
|
|
776
|
-
- [Radio](/docs/components/radio.md) - Base radio component
|
|
777
|
-
- [LabeledRadio](/docs/components/labeled-radio.md) - Radio with label
|
|
778
|
-
- [Card](/docs/components/card.md) - Base card component
|
|
779
|
-
- [RadioGroup](/docs/components/radio.md#radiogroup) - Container for radios
|
|
780
|
-
|
|
781
|
-
## Browser Support
|
|
782
|
-
|
|
783
|
-
- Chrome 90+ ✅
|
|
784
|
-
- Firefox 88+ ✅
|
|
785
|
-
- Safari 14+ ✅
|
|
786
|
-
- Edge 90+ ✅
|
|
787
|
-
- Mobile browsers ✅
|
|
788
|
-
|
|
789
|
-
## Changelog
|
|
790
|
-
|
|
791
|
-
### v1.1.15
|
|
792
|
-
- Combined Radio and Card components
|
|
793
|
-
- Added theme support
|
|
794
|
-
- Improved accessibility
|
|
795
|
-
|
|
796
|
-
### v1.1.14
|
|
797
|
-
- Initial RadioCard implementation
|
|
798
|
-
- Basic selection support
|
|
799
|
-
|
|
800
|
-
### v1.1.0
|
|
801
|
-
- Component planning phase
|