torch-glare 2.5.5 → 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 -338
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -317
- 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 -383
- 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 -84
- 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 -63
- package/apps/lib/components/DataViews/states.tsx +0 -38
- 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 -420
- package/apps/lib/components/DropdownMenu.tsx +0 -540
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -73
- 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 -163
- 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 -91
- 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 -47
- package/apps/lib/components/FormBuilder/types.ts +0 -311
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -128
- package/apps/lib/components/FormRenderer/detail.tsx +0 -207
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -297
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -15
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -339
- package/apps/lib/components/FormRenderer/types.ts +0 -78
- 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 -147
- 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 -253
- 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 -610
- 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 -58
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- 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 -214
- 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 -1201
- 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/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 -712
- 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 -1495
- package/docs/components/data-views/migration.md +0 -79
- 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 -276
- package/docs/components/form-renderer.md +0 -326
- 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 -477
- 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 -54
- 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 -1500
- 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 -704
- 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,741 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: SimpleSelect
|
|
3
|
-
version: 2.4.5
|
|
4
|
-
status: stable
|
|
5
|
-
category: components/forms
|
|
6
|
-
tags: [form, select, dropdown, custom, system-style, accessible]
|
|
7
|
-
last-reviewed: 2024-11-05
|
|
8
|
-
bundle-size: 3.4kb
|
|
9
|
-
dependencies:
|
|
10
|
-
- "class-variance-authority": "^0.7.0"
|
|
11
|
-
- "@/hooks/useClickOutside": "internal"
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# SimpleSelect
|
|
15
|
-
|
|
16
|
-
> A lightweight, custom-styled select component with system theme styling. Features a compact dropdown with automatic scroll-to-selected behavior and click-outside handling. Ideal for system UI and compact form layouts.
|
|
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 SimpleSelect
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`add` also copies any components, hooks, and utilities that `SimpleSelect` 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 { SimpleSelect } from "@/components/SimpleSelect";
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Examples
|
|
39
|
-
|
|
40
|
-
### Basic Usage
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { SimpleSelectValue, SimpleSelectItem } from '@/components/SimpleSelect'
|
|
44
|
-
|
|
45
|
-
function Example() {
|
|
46
|
-
const [value, setValue] = useState('option1')
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<SimpleSelectValue
|
|
50
|
-
value={value}
|
|
51
|
-
onChange={setValue}
|
|
52
|
-
placeholder="Select option..."
|
|
53
|
-
>
|
|
54
|
-
<SimpleSelectItem onClick={() => setValue('option1')}>
|
|
55
|
-
Option 1
|
|
56
|
-
</SimpleSelectItem>
|
|
57
|
-
<SimpleSelectItem onClick={() => setValue('option2')}>
|
|
58
|
-
Option 2
|
|
59
|
-
</SimpleSelectItem>
|
|
60
|
-
<SimpleSelectItem onClick={() => setValue('option3')}>
|
|
61
|
-
Option 3
|
|
62
|
-
</SimpleSelectItem>
|
|
63
|
-
</SimpleSelectValue>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### With Selected State
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
function SelectWithHighlight() {
|
|
72
|
-
const [selected, setSelected] = useState('medium')
|
|
73
|
-
|
|
74
|
-
const options = [
|
|
75
|
-
{ value: 'small', label: 'Small' },
|
|
76
|
-
{ value: 'medium', label: 'Medium' },
|
|
77
|
-
{ value: 'large', label: 'Large' },
|
|
78
|
-
{ value: 'xlarge', label: 'Extra Large' }
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<SimpleSelectValue
|
|
83
|
-
value={selected}
|
|
84
|
-
onChange={setSelected}
|
|
85
|
-
>
|
|
86
|
-
{options.map(option => (
|
|
87
|
-
<SimpleSelectItem
|
|
88
|
-
key={option.value}
|
|
89
|
-
selected={selected === option.value}
|
|
90
|
-
onClick={() => setSelected(option.value)}
|
|
91
|
-
>
|
|
92
|
-
{option.label}
|
|
93
|
-
</SimpleSelectItem>
|
|
94
|
-
))}
|
|
95
|
-
</SimpleSelectValue>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Country Selector
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
function CountrySelect() {
|
|
104
|
-
const [country, setCountry] = useState('us')
|
|
105
|
-
|
|
106
|
-
const countries = [
|
|
107
|
-
{ code: 'us', name: 'United States', flag: '🇺🇸' },
|
|
108
|
-
{ code: 'gb', name: 'United Kingdom', flag: '🇬🇧' },
|
|
109
|
-
{ code: 'ca', name: 'Canada', flag: '🇨🇦' },
|
|
110
|
-
{ code: 'au', name: 'Australia', flag: '🇦🇺' },
|
|
111
|
-
{ code: 'de', name: 'Germany', flag: '🇩🇪' },
|
|
112
|
-
{ code: 'fr', name: 'France', flag: '🇫🇷' },
|
|
113
|
-
{ code: 'jp', name: 'Japan', flag: '🇯🇵' }
|
|
114
|
-
]
|
|
115
|
-
|
|
116
|
-
const selectedCountry = countries.find(c => c.code === country)
|
|
117
|
-
|
|
118
|
-
return (
|
|
119
|
-
<SimpleSelectValue
|
|
120
|
-
value={`${selectedCountry?.flag} ${selectedCountry?.name}`}
|
|
121
|
-
onChange={() => {}}
|
|
122
|
-
readOnly
|
|
123
|
-
>
|
|
124
|
-
{countries.map(c => (
|
|
125
|
-
<SimpleSelectItem
|
|
126
|
-
key={c.code}
|
|
127
|
-
selected={country === c.code}
|
|
128
|
-
onClick={() => setCountry(c.code)}
|
|
129
|
-
>
|
|
130
|
-
<span className="flex items-center gap-2">
|
|
131
|
-
<span>{c.flag}</span>
|
|
132
|
-
<span>{c.name}</span>
|
|
133
|
-
</span>
|
|
134
|
-
</SimpleSelectItem>
|
|
135
|
-
))}
|
|
136
|
-
</SimpleSelectValue>
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Time Zone Selector
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
function TimeZoneSelect() {
|
|
145
|
-
const [timezone, setTimezone] = useState('UTC')
|
|
146
|
-
|
|
147
|
-
const timezones = [
|
|
148
|
-
{ value: 'UTC', label: 'UTC (±00:00)' },
|
|
149
|
-
{ value: 'EST', label: 'Eastern Time (−05:00)' },
|
|
150
|
-
{ value: 'CST', label: 'Central Time (−06:00)' },
|
|
151
|
-
{ value: 'MST', label: 'Mountain Time (−07:00)' },
|
|
152
|
-
{ value: 'PST', label: 'Pacific Time (−08:00)' },
|
|
153
|
-
{ value: 'CET', label: 'Central European (+ 01:00)' },
|
|
154
|
-
{ value: 'JST', label: 'Japan Time (+09:00)' }
|
|
155
|
-
]
|
|
156
|
-
|
|
157
|
-
return (
|
|
158
|
-
<SimpleSelectValue
|
|
159
|
-
value={timezone}
|
|
160
|
-
onChange={setTimezone}
|
|
161
|
-
className="w-48"
|
|
162
|
-
>
|
|
163
|
-
{timezones.map(tz => (
|
|
164
|
-
<SimpleSelectItem
|
|
165
|
-
key={tz.value}
|
|
166
|
-
selected={timezone === tz.value}
|
|
167
|
-
onClick={() => setTimezone(tz.value)}
|
|
168
|
-
>
|
|
169
|
-
{tz.label}
|
|
170
|
-
</SimpleSelectItem>
|
|
171
|
-
))}
|
|
172
|
-
</SimpleSelectValue>
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### With Icons
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
function IconSelect() {
|
|
181
|
-
const [priority, setPriority] = useState('medium')
|
|
182
|
-
|
|
183
|
-
const priorities = [
|
|
184
|
-
{ value: 'low', label: 'Low', icon: '🟢', color: 'text-green-500' },
|
|
185
|
-
{ value: 'medium', label: 'Medium', icon: '🟡', color: 'text-yellow-500' },
|
|
186
|
-
{ value: 'high', label: 'High', icon: '🟠', color: 'text-orange-500' },
|
|
187
|
-
{ value: 'critical', label: 'Critical', icon: '🔴', color: 'text-red-500' }
|
|
188
|
-
]
|
|
189
|
-
|
|
190
|
-
const selected = priorities.find(p => p.value === priority)
|
|
191
|
-
|
|
192
|
-
return (
|
|
193
|
-
<SimpleSelectValue
|
|
194
|
-
value={priority}
|
|
195
|
-
onChange={setPriority}
|
|
196
|
-
inputClassName={selected?.color}
|
|
197
|
-
>
|
|
198
|
-
{priorities.map(p => (
|
|
199
|
-
<SimpleSelectItem
|
|
200
|
-
key={p.value}
|
|
201
|
-
selected={priority === p.value}
|
|
202
|
-
onClick={() => setPriority(p.value)}
|
|
203
|
-
className={p.color}
|
|
204
|
-
>
|
|
205
|
-
<span className="flex items-center gap-2">
|
|
206
|
-
<span>{p.icon}</span>
|
|
207
|
-
<span>{p.label}</span>
|
|
208
|
-
</span>
|
|
209
|
-
</SimpleSelectItem>
|
|
210
|
-
))}
|
|
211
|
-
</SimpleSelectValue>
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Default Open State
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
function DefaultOpenSelect() {
|
|
220
|
-
const [value, setValue] = useState('')
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<SimpleSelectValue
|
|
224
|
-
value={value}
|
|
225
|
-
onChange={setValue}
|
|
226
|
-
placeholder="Choose..."
|
|
227
|
-
defaultOpen={true}
|
|
228
|
-
>
|
|
229
|
-
<SimpleSelectItem onClick={() => setValue('opt1')}>
|
|
230
|
-
Option 1
|
|
231
|
-
</SimpleSelectItem>
|
|
232
|
-
<SimpleSelectItem onClick={() => setValue('opt2')}>
|
|
233
|
-
Option 2
|
|
234
|
-
</SimpleSelectItem>
|
|
235
|
-
</SimpleSelectValue>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## API Reference
|
|
241
|
-
|
|
242
|
-
### SimpleSelectValue Props
|
|
243
|
-
|
|
244
|
-
| Prop | Type | Default | Description |
|
|
245
|
-
|------|------|---------|-------------|
|
|
246
|
-
| `value` | `string` | - | Display value |
|
|
247
|
-
| `onChange` | `(value: string) => void` | - | Input change handler |
|
|
248
|
-
| `defaultOpen` | `boolean` | `false` | Initial open state |
|
|
249
|
-
| `className` | `string` | - | Container CSS classes |
|
|
250
|
-
| `inputClassName` | `string` | - | Input element CSS classes |
|
|
251
|
-
| `placeholder` | `string` | - | Placeholder text |
|
|
252
|
-
| `readOnly` | `boolean` | `false` | Makes input read-only |
|
|
253
|
-
| `disabled` | `boolean` | `false` | Disables the select |
|
|
254
|
-
| `children` | `ReactNode` | - | Dropdown items |
|
|
255
|
-
|
|
256
|
-
Plus standard HTML input attributes.
|
|
257
|
-
|
|
258
|
-
### SimpleSelectItem Props
|
|
259
|
-
|
|
260
|
-
| Prop | Type | Default | Description |
|
|
261
|
-
|------|------|---------|-------------|
|
|
262
|
-
| `selected` | `boolean` | `false` | Highlights as selected |
|
|
263
|
-
| `onClick` | `() => void` | - | Click handler |
|
|
264
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
265
|
-
| `children` | `ReactNode` | - | Item content |
|
|
266
|
-
|
|
267
|
-
### SimpleSelectDropDown Props
|
|
268
|
-
|
|
269
|
-
| Prop | Type | Default | Description |
|
|
270
|
-
|------|------|---------|-------------|
|
|
271
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
272
|
-
| `children` | `ReactNode` | - | Dropdown content |
|
|
273
|
-
| `onClick` | `() => void` | - | Click handler |
|
|
274
|
-
|
|
275
|
-
### TypeScript
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
interface SimpleSelectValueProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
|
279
|
-
inputClassName?: string
|
|
280
|
-
onChange?: (value: string) => void
|
|
281
|
-
defaultOpen?: boolean
|
|
282
|
-
className?: string
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
interface SimpleSelectItemProps extends HTMLAttributes<HTMLLIElement> {
|
|
286
|
-
selected?: boolean
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
interface SimpleSelectDropDownProps {
|
|
290
|
-
className?: string
|
|
291
|
-
children: ReactNode
|
|
292
|
-
onClick?: () => void
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export const SimpleSelectValue: React.FC<SimpleSelectValueProps>
|
|
296
|
-
export const SimpleSelectItem: React.FC<SimpleSelectItemProps>
|
|
297
|
-
export const SimpleSelectDropDown: React.FC<SimpleSelectDropDownProps>
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
## Common Patterns
|
|
301
|
-
|
|
302
|
-
### Language Selector
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
function LanguageSelector() {
|
|
306
|
-
const [lang, setLang] = useState('en')
|
|
307
|
-
|
|
308
|
-
const languages = [
|
|
309
|
-
{ code: 'en', name: 'English', native: 'English' },
|
|
310
|
-
{ code: 'es', name: 'Spanish', native: 'Español' },
|
|
311
|
-
{ code: 'fr', name: 'French', native: 'Français' },
|
|
312
|
-
{ code: 'de', name: 'German', native: 'Deutsch' },
|
|
313
|
-
{ code: 'it', name: 'Italian', native: 'Italiano' },
|
|
314
|
-
{ code: 'pt', name: 'Portuguese', native: 'Português' },
|
|
315
|
-
{ code: 'ru', name: 'Russian', native: 'Русский' },
|
|
316
|
-
{ code: 'zh', name: 'Chinese', native: '中文' },
|
|
317
|
-
{ code: 'ja', name: 'Japanese', native: '日本語' },
|
|
318
|
-
{ code: 'ko', name: 'Korean', native: '한국어' }
|
|
319
|
-
]
|
|
320
|
-
|
|
321
|
-
const selected = languages.find(l => l.code === lang)
|
|
322
|
-
|
|
323
|
-
return (
|
|
324
|
-
<SimpleSelectValue
|
|
325
|
-
value={selected?.name || ''}
|
|
326
|
-
onChange={() => {}}
|
|
327
|
-
readOnly
|
|
328
|
-
className="w-40"
|
|
329
|
-
>
|
|
330
|
-
<div className="max-h-64">
|
|
331
|
-
{languages.map(language => (
|
|
332
|
-
<SimpleSelectItem
|
|
333
|
-
key={language.code}
|
|
334
|
-
selected={lang === language.code}
|
|
335
|
-
onClick={() => setLang(language.code)}
|
|
336
|
-
>
|
|
337
|
-
<div className="flex justify-between w-full">
|
|
338
|
-
<span>{language.native}</span>
|
|
339
|
-
<span className="text-xs opacity-60">{language.name}</span>
|
|
340
|
-
</div>
|
|
341
|
-
</SimpleSelectItem>
|
|
342
|
-
))}
|
|
343
|
-
</div>
|
|
344
|
-
</SimpleSelectValue>
|
|
345
|
-
)
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### Status Selector
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
function StatusSelector() {
|
|
353
|
-
const [status, setStatus] = useState('active')
|
|
354
|
-
|
|
355
|
-
const statuses = [
|
|
356
|
-
{ value: 'active', label: 'Active', dot: '🟢' },
|
|
357
|
-
{ value: 'pending', label: 'Pending', dot: '🟡' },
|
|
358
|
-
{ value: 'inactive', label: 'Inactive', dot: '⚪' },
|
|
359
|
-
{ value: 'blocked', label: 'Blocked', dot: '🔴' }
|
|
360
|
-
]
|
|
361
|
-
|
|
362
|
-
const current = statuses.find(s => s.value === status)
|
|
363
|
-
|
|
364
|
-
return (
|
|
365
|
-
<SimpleSelectValue
|
|
366
|
-
value={`${current?.dot} ${current?.label}`}
|
|
367
|
-
onChange={() => {}}
|
|
368
|
-
readOnly
|
|
369
|
-
className="w-36"
|
|
370
|
-
>
|
|
371
|
-
{statuses.map(s => (
|
|
372
|
-
<SimpleSelectItem
|
|
373
|
-
key={s.value}
|
|
374
|
-
selected={status === s.value}
|
|
375
|
-
onClick={() => setStatus(s.value)}
|
|
376
|
-
>
|
|
377
|
-
<span className="flex items-center gap-2">
|
|
378
|
-
<span>{s.dot}</span>
|
|
379
|
-
<span>{s.label}</span>
|
|
380
|
-
</span>
|
|
381
|
-
</SimpleSelectItem>
|
|
382
|
-
))}
|
|
383
|
-
</SimpleSelectValue>
|
|
384
|
-
)
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Font Size Selector
|
|
389
|
-
|
|
390
|
-
```typescript
|
|
391
|
-
function FontSizeSelector() {
|
|
392
|
-
const [fontSize, setFontSize] = useState('14')
|
|
393
|
-
|
|
394
|
-
const sizes = ['10', '11', '12', '13', '14', '15', '16', '18', '20', '24']
|
|
395
|
-
|
|
396
|
-
return (
|
|
397
|
-
<div className="flex items-center gap-2">
|
|
398
|
-
<label>Font Size:</label>
|
|
399
|
-
<SimpleSelectValue
|
|
400
|
-
value={`${fontSize}px`}
|
|
401
|
-
onChange={(val) => setFontSize(val.replace('px', ''))}
|
|
402
|
-
className="w-20"
|
|
403
|
-
>
|
|
404
|
-
{sizes.map(size => (
|
|
405
|
-
<SimpleSelectItem
|
|
406
|
-
key={size}
|
|
407
|
-
selected={fontSize === size}
|
|
408
|
-
onClick={() => setFontSize(size)}
|
|
409
|
-
>
|
|
410
|
-
{size}px
|
|
411
|
-
</SimpleSelectItem>
|
|
412
|
-
))}
|
|
413
|
-
</SimpleSelectValue>
|
|
414
|
-
</div>
|
|
415
|
-
)
|
|
416
|
-
}
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Dynamic Options
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
function DynamicSelect() {
|
|
423
|
-
const [selected, setSelected] = useState('')
|
|
424
|
-
const [options, setOptions] = useState(['Option 1', 'Option 2'])
|
|
425
|
-
const [newOption, setNewOption] = useState('')
|
|
426
|
-
|
|
427
|
-
const addOption = () => {
|
|
428
|
-
if (newOption && !options.includes(newOption)) {
|
|
429
|
-
setOptions([...options, newOption])
|
|
430
|
-
setNewOption('')
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return (
|
|
435
|
-
<div className="space-y-2">
|
|
436
|
-
<SimpleSelectValue
|
|
437
|
-
value={selected}
|
|
438
|
-
onChange={setSelected}
|
|
439
|
-
placeholder="Select or add..."
|
|
440
|
-
>
|
|
441
|
-
{options.map(opt => (
|
|
442
|
-
<SimpleSelectItem
|
|
443
|
-
key={opt}
|
|
444
|
-
selected={selected === opt}
|
|
445
|
-
onClick={() => setSelected(opt)}
|
|
446
|
-
>
|
|
447
|
-
{opt}
|
|
448
|
-
</SimpleSelectItem>
|
|
449
|
-
))}
|
|
450
|
-
<SimpleSelectItem onClick={() => {}}>
|
|
451
|
-
<input
|
|
452
|
-
value={newOption}
|
|
453
|
-
onChange={(e) => setNewOption(e.target.value)}
|
|
454
|
-
onKeyDown={(e) => {
|
|
455
|
-
if (e.key === 'Enter') {
|
|
456
|
-
e.stopPropagation()
|
|
457
|
-
addOption()
|
|
458
|
-
}
|
|
459
|
-
}}
|
|
460
|
-
placeholder="Add new..."
|
|
461
|
-
className="bg-transparent outline-none"
|
|
462
|
-
onClick={(e) => e.stopPropagation()}
|
|
463
|
-
/>
|
|
464
|
-
</SimpleSelectItem>
|
|
465
|
-
</SimpleSelectValue>
|
|
466
|
-
</div>
|
|
467
|
-
)
|
|
468
|
-
}
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
## Testing
|
|
472
|
-
|
|
473
|
-
### Unit Test Example
|
|
474
|
-
|
|
475
|
-
```typescript
|
|
476
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
477
|
-
import { SimpleSelectValue, SimpleSelectItem } from '@/components/SimpleSelect'
|
|
478
|
-
|
|
479
|
-
describe('SimpleSelect', () => {
|
|
480
|
-
it('opens dropdown on click', () => {
|
|
481
|
-
render(
|
|
482
|
-
<SimpleSelectValue value="" onChange={() => {}}>
|
|
483
|
-
<SimpleSelectItem>Option 1</SimpleSelectItem>
|
|
484
|
-
</SimpleSelectValue>
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
const button = screen.getByRole('button')
|
|
488
|
-
fireEvent.click(button)
|
|
489
|
-
|
|
490
|
-
expect(screen.getByText('Option 1')).toBeVisible()
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
it('selects item and closes dropdown', () => {
|
|
494
|
-
const handleChange = jest.fn()
|
|
495
|
-
render(
|
|
496
|
-
<SimpleSelectValue value="" onChange={handleChange}>
|
|
497
|
-
<SimpleSelectItem onClick={() => handleChange('opt1')}>
|
|
498
|
-
Option 1
|
|
499
|
-
</SimpleSelectItem>
|
|
500
|
-
</SimpleSelectValue>
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
const button = screen.getByRole('button')
|
|
504
|
-
fireEvent.click(button)
|
|
505
|
-
|
|
506
|
-
const item = screen.getByText('Option 1')
|
|
507
|
-
fireEvent.click(item)
|
|
508
|
-
|
|
509
|
-
expect(handleChange).toHaveBeenCalledWith('opt1')
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
it('scrolls to selected item', () => {
|
|
513
|
-
const scrollIntoViewMock = jest.fn()
|
|
514
|
-
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock
|
|
515
|
-
|
|
516
|
-
render(
|
|
517
|
-
<SimpleSelectValue value="selected" defaultOpen>
|
|
518
|
-
<SimpleSelectItem>Option 1</SimpleSelectItem>
|
|
519
|
-
<SimpleSelectItem selected>Selected</SimpleSelectItem>
|
|
520
|
-
<SimpleSelectItem>Option 3</SimpleSelectItem>
|
|
521
|
-
</SimpleSelectValue>
|
|
522
|
-
)
|
|
523
|
-
|
|
524
|
-
expect(scrollIntoViewMock).toHaveBeenCalled()
|
|
525
|
-
})
|
|
526
|
-
})
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### Accessibility Test
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
import { axe } from '@axe-core/react'
|
|
533
|
-
|
|
534
|
-
test('SimpleSelect meets WCAG standards', async () => {
|
|
535
|
-
const { container } = render(
|
|
536
|
-
<div>
|
|
537
|
-
<label htmlFor="select">Choose option</label>
|
|
538
|
-
<SimpleSelectValue
|
|
539
|
-
value=""
|
|
540
|
-
onChange={() => {}}
|
|
541
|
-
id="select"
|
|
542
|
-
>
|
|
543
|
-
<SimpleSelectItem>Option 1</SimpleSelectItem>
|
|
544
|
-
<SimpleSelectItem>Option 2</SimpleSelectItem>
|
|
545
|
-
</SimpleSelectValue>
|
|
546
|
-
</div>
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
const results = await axe(container)
|
|
550
|
-
expect(results).toHaveNoViolations()
|
|
551
|
-
})
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
## Known Limitations & Frontend Patterns
|
|
555
|
-
|
|
556
|
-
### Empty-string option values crash at runtime
|
|
557
|
-
|
|
558
|
-
`SimpleSelect` is built on Radix UI's `Select` primitive, which **forbids `<Select.Item value="" />`**. Radix reserves the empty string for "clear selection / show placeholder" semantics. Passing an option with `value: ""` throws:
|
|
559
|
-
|
|
560
|
-
```
|
|
561
|
-
A <Select.Item /> must have a value prop that is not an empty string.
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
This is a common footgun for filter bars where the natural "All / Any / None" option looks like `{ value: "", label: "All Statuses" }`.
|
|
565
|
-
|
|
566
|
-
**Workaround — use a sentinel value and translate in both directions:**
|
|
567
|
-
|
|
568
|
-
```tsx
|
|
569
|
-
<SimpleSelect
|
|
570
|
-
value={statusFilter || "ALL"}
|
|
571
|
-
onValueChange={(val) => setStatusFilter(val === "ALL" ? "" : val)}
|
|
572
|
-
options={[
|
|
573
|
-
{ value: "ALL", label: "All Statuses" },
|
|
574
|
-
{ value: "DRAFT", label: "Draft" },
|
|
575
|
-
{ value: "POSTED", label: "Posted" },
|
|
576
|
-
]}
|
|
577
|
-
className="w-48 h-10"
|
|
578
|
-
placeholder="All Statuses"
|
|
579
|
-
/>
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
Pick a sentinel that won't collide with real values (`"ALL"`, `"__NONE__"`, etc.). Map back to the empty string (or `null`/`undefined`, depending on your state shape) inside `onValueChange` so consumers downstream still see the cleared state.
|
|
583
|
-
|
|
584
|
-
### Width / height — same as `Select`
|
|
585
|
-
|
|
586
|
-
`SimpleSelect`'s trigger does not include `w-full` by default. For form rows and filter bars, always pass `className="w-full h-10"` (or `className="w-48 h-10"` for narrower filter selects).
|
|
587
|
-
|
|
588
|
-
## Accessibility
|
|
589
|
-
|
|
590
|
-
### Keyboard Support
|
|
591
|
-
|
|
592
|
-
- **Tab**: Focus the select
|
|
593
|
-
- **Space/Enter**: Open dropdown
|
|
594
|
-
- **Escape**: Close dropdown
|
|
595
|
-
- **Arrow Keys**: Navigate options (with additional implementation)
|
|
596
|
-
|
|
597
|
-
### ARIA Attributes
|
|
598
|
-
|
|
599
|
-
```typescript
|
|
600
|
-
// Enhanced accessibility
|
|
601
|
-
<SimpleSelectValue
|
|
602
|
-
aria-expanded={isOpen}
|
|
603
|
-
aria-haspopup="listbox"
|
|
604
|
-
aria-label="Select option"
|
|
605
|
-
/>
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
### Focus Management
|
|
609
|
-
|
|
610
|
-
- Auto-focus on selected item when opening
|
|
611
|
-
- Click outside to close
|
|
612
|
-
- Visual focus indicators
|
|
613
|
-
|
|
614
|
-
## Styling
|
|
615
|
-
|
|
616
|
-
### System Theme
|
|
617
|
-
|
|
618
|
-
The component uses system-style theming by default:
|
|
619
|
-
|
|
620
|
-
```css
|
|
621
|
-
/* Dark theme with purple accents */
|
|
622
|
-
.simple-select {
|
|
623
|
-
background: rgba(0, 0, 0, 0.2);
|
|
624
|
-
border-color: #2c2d2e;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
.simple-select:hover {
|
|
628
|
-
border-color: #9748ff;
|
|
629
|
-
background: rgba(151, 72, 255, 0.1);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
.simple-select[data-active="true"] {
|
|
633
|
-
border-color: #9748ff;
|
|
634
|
-
background: rgba(151, 72, 255, 0.1);
|
|
635
|
-
}
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### Custom Styles
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
<SimpleSelectValue
|
|
642
|
-
className="custom-container"
|
|
643
|
-
inputClassName="custom-input"
|
|
644
|
-
>
|
|
645
|
-
<SimpleSelectItem className="custom-item">
|
|
646
|
-
Option
|
|
647
|
-
</SimpleSelectItem>
|
|
648
|
-
</SimpleSelectValue>
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
## Performance
|
|
652
|
-
|
|
653
|
-
| Metric | Value |
|
|
654
|
-
|--------|-------|
|
|
655
|
-
| Bundle size (gzip) | 3.4kb |
|
|
656
|
-
| First render | <10ms |
|
|
657
|
-
| Dropdown open | <5ms |
|
|
658
|
-
| Scroll to selected | <2ms |
|
|
659
|
-
| Tree-shakeable | ✅ |
|
|
660
|
-
|
|
661
|
-
### Optimization Tips
|
|
662
|
-
|
|
663
|
-
1. Virtualize long option lists (>100 items)
|
|
664
|
-
2. Debounce search/filter operations
|
|
665
|
-
3. Memoize option components
|
|
666
|
-
4. Use `React.memo` for static options
|
|
667
|
-
|
|
668
|
-
## Migration
|
|
669
|
-
|
|
670
|
-
### From Native Select
|
|
671
|
-
|
|
672
|
-
```diff
|
|
673
|
-
// From HTML select
|
|
674
|
-
- <select value={value} onChange={(e) => setValue(e.target.value)}>
|
|
675
|
-
- <option value="opt1">Option 1</option>
|
|
676
|
-
- <option value="opt2">Option 2</option>
|
|
677
|
-
- </select>
|
|
678
|
-
|
|
679
|
-
// To SimpleSelect
|
|
680
|
-
+ <SimpleSelectValue value={value} onChange={setValue}>
|
|
681
|
-
+ <SimpleSelectItem onClick={() => setValue('opt1')}>
|
|
682
|
-
+ Option 1
|
|
683
|
-
+ </SimpleSelectItem>
|
|
684
|
-
+ <SimpleSelectItem onClick={() => setValue('opt2')}>
|
|
685
|
-
+ Option 2
|
|
686
|
-
+ </SimpleSelectItem>
|
|
687
|
-
+ </SimpleSelectValue>
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
## Troubleshooting
|
|
691
|
-
|
|
692
|
-
### Dropdown not closing
|
|
693
|
-
|
|
694
|
-
**Solution:** Ensure click handlers stop propagation properly
|
|
695
|
-
|
|
696
|
-
```typescript
|
|
697
|
-
<SimpleSelectItem onClick={(e) => {
|
|
698
|
-
e.stopPropagation()
|
|
699
|
-
setValue(option)
|
|
700
|
-
}}>
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
### Scroll not working
|
|
704
|
-
|
|
705
|
-
**Solution:** Check container has max-height
|
|
706
|
-
|
|
707
|
-
```typescript
|
|
708
|
-
<SimpleSelectValue>
|
|
709
|
-
<div className="max-h-64 overflow-auto">
|
|
710
|
-
{/* items */}
|
|
711
|
-
</div>
|
|
712
|
-
</SimpleSelectValue>
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
## Related Components
|
|
716
|
-
|
|
717
|
-
- [Select](/docs/components/select.md) - Full-featured select with Radix UI
|
|
718
|
-
- [InputField](/docs/components/input-field.md) - Input with dropdown support
|
|
719
|
-
- [Popover](/docs/components/popover.md) - Generic popover component
|
|
720
|
-
|
|
721
|
-
## Browser Support
|
|
722
|
-
|
|
723
|
-
- Chrome 90+ ✅
|
|
724
|
-
- Firefox 88+ ✅
|
|
725
|
-
- Safari 14+ ✅
|
|
726
|
-
- Edge 90+ ✅
|
|
727
|
-
- Mobile browsers ✅
|
|
728
|
-
|
|
729
|
-
## Changelog
|
|
730
|
-
|
|
731
|
-
### v1.1.15
|
|
732
|
-
- Added scroll-to-selected feature
|
|
733
|
-
- Improved click outside handling
|
|
734
|
-
- Enhanced TypeScript types
|
|
735
|
-
|
|
736
|
-
### v1.1.14
|
|
737
|
-
- Initial SimpleSelect implementation
|
|
738
|
-
- System theme styling
|
|
739
|
-
|
|
740
|
-
### v1.1.0
|
|
741
|
-
- Component planning phase
|