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,738 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: BadgeField
|
|
3
|
-
description: Interactive input field with tag selection, search filtering, and keyboard navigation for multi-select scenarios.
|
|
4
|
-
component: true
|
|
5
|
-
group: Data Display
|
|
6
|
-
keywords: [badge, field, tags, multi-select, input, chips, tokens, autocomplete]
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# BadgeField
|
|
10
|
-
|
|
11
|
-
An advanced tag selection input component that combines badges with search functionality. Features keyboard navigation, filtering, and multi-select capabilities with a popover dropdown.
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
16
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npx torch-glare@latest init
|
|
20
|
-
npx torch-glare@latest add BadgeField
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
`add` also copies any components, hooks, and utilities that `BadgeField` depends on.
|
|
24
|
-
|
|
25
|
-
## Imports
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
import { BadgeField } from '@/components/BadgeField'
|
|
29
|
-
import type { Tag } from '@/hooks/useTagSelection'
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Basic Usage
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
import { BadgeField } from '@/components/BadgeField'
|
|
36
|
-
import { useState } from 'react'
|
|
37
|
-
|
|
38
|
-
export function BasicBadgeField() {
|
|
39
|
-
const [tags] = useState([
|
|
40
|
-
{ id: '1', name: 'React', isSelected: false, variant: 'blue' },
|
|
41
|
-
{ id: '2', name: 'TypeScript', isSelected: false, variant: 'purple' },
|
|
42
|
-
{ id: '3', name: 'Next.js', isSelected: false, variant: 'slate' },
|
|
43
|
-
])
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<BadgeField
|
|
47
|
-
tags={tags}
|
|
48
|
-
placeholder="Select technologies..."
|
|
49
|
-
size="M"
|
|
50
|
-
/>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Examples
|
|
56
|
-
|
|
57
|
-
### Technology Stack Selector
|
|
58
|
-
|
|
59
|
-
Select multiple technologies with search.
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
export function TechnologySelector() {
|
|
63
|
-
const [selectedTags, setSelectedTags] = useState<Tag[]>([])
|
|
64
|
-
|
|
65
|
-
const allTags: Tag[] = [
|
|
66
|
-
{ id: '1', name: 'React', isSelected: false, variant: 'blue' },
|
|
67
|
-
{ id: '2', name: 'TypeScript', isSelected: false, variant: 'purple' },
|
|
68
|
-
{ id: '3', name: 'Next.js', isSelected: false, variant: 'slate' },
|
|
69
|
-
{ id: '4', name: 'Tailwind CSS', isSelected: false, variant: 'green' },
|
|
70
|
-
{ id: '5', name: 'Node.js', isSelected: false, variant: 'green' },
|
|
71
|
-
{ id: '6', name: 'PostgreSQL', isSelected: false, variant: 'blue' },
|
|
72
|
-
{ id: '7', name: 'MongoDB', isSelected: false, variant: 'green' },
|
|
73
|
-
{ id: '8', name: 'GraphQL', isSelected: false, variant: 'purple' },
|
|
74
|
-
{ id: '9', name: 'REST API', isSelected: false, variant: 'gray' },
|
|
75
|
-
{ id: '10', name: 'Docker', isSelected: false, variant: 'blue' },
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div className="max-w-2xl">
|
|
80
|
-
<label className="block mb-2 font-medium">
|
|
81
|
-
Select Your Tech Stack
|
|
82
|
-
</label>
|
|
83
|
-
|
|
84
|
-
<BadgeField
|
|
85
|
-
tags={allTags}
|
|
86
|
-
placeholder="Search technologies..."
|
|
87
|
-
size="M"
|
|
88
|
-
onValueChange={setSelectedTags}
|
|
89
|
-
/>
|
|
90
|
-
|
|
91
|
-
<div className="mt-4">
|
|
92
|
-
<p className="text-sm text-content-presentation-global-secondary">
|
|
93
|
-
Selected: {selectedTags.length} technologies
|
|
94
|
-
</p>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Category Filter
|
|
102
|
-
|
|
103
|
-
Filter content by multiple categories.
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
export function CategoryFilter() {
|
|
107
|
-
const categories: Tag[] = [
|
|
108
|
-
{ id: 'design', name: 'Design', isSelected: false, variant: 'purple' },
|
|
109
|
-
{ id: 'development', name: 'Development', isSelected: false, variant: 'blue' },
|
|
110
|
-
{ id: 'marketing', name: 'Marketing', isSelected: false, variant: 'yellow' },
|
|
111
|
-
{ id: 'sales', name: 'Sales', isSelected: false, variant: 'green' },
|
|
112
|
-
{ id: 'support', name: 'Support', isSelected: false, variant: 'orange' },
|
|
113
|
-
{ id: 'hr', name: 'Human Resources', isSelected: false, variant: 'rose' },
|
|
114
|
-
{ id: 'finance', name: 'Finance', isSelected: false, variant: 'slate' },
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
const [selectedCategories, setSelectedCategories] = useState<Tag[]>([])
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<div className="space-y-4">
|
|
121
|
-
<div>
|
|
122
|
-
<h3 className="font-semibold mb-2">Filter by Category</h3>
|
|
123
|
-
<BadgeField
|
|
124
|
-
tags={categories}
|
|
125
|
-
placeholder="Select categories to filter..."
|
|
126
|
-
size="M"
|
|
127
|
-
onValueChange={setSelectedCategories}
|
|
128
|
-
/>
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
<div className="p-4 border rounded-lg">
|
|
132
|
-
<h4 className="font-medium mb-2">Filtered Results</h4>
|
|
133
|
-
{selectedCategories.length === 0 ? (
|
|
134
|
-
<p className="text-sm text-content-presentation-global-secondary">
|
|
135
|
-
Select categories to filter results
|
|
136
|
-
</p>
|
|
137
|
-
) : (
|
|
138
|
-
<p className="text-sm">
|
|
139
|
-
Showing results for: {selectedCategories.map(c => c.name).join(', ')}
|
|
140
|
-
</p>
|
|
141
|
-
)}
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
)
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Skills Selector
|
|
149
|
-
|
|
150
|
-
Multi-select skills for job applications or profiles.
|
|
151
|
-
|
|
152
|
-
```tsx
|
|
153
|
-
export function SkillsSelector() {
|
|
154
|
-
const skills: Tag[] = [
|
|
155
|
-
{ id: 'js', name: 'JavaScript', isSelected: false, variant: 'yellow' },
|
|
156
|
-
{ id: 'ts', name: 'TypeScript', isSelected: false, variant: 'blue' },
|
|
157
|
-
{ id: 'react', name: 'React', isSelected: false, variant: 'blue' },
|
|
158
|
-
{ id: 'vue', name: 'Vue.js', isSelected: false, variant: 'green' },
|
|
159
|
-
{ id: 'angular', name: 'Angular', isSelected: false, variant: 'red' },
|
|
160
|
-
{ id: 'node', name: 'Node.js', isSelected: false, variant: 'green' },
|
|
161
|
-
{ id: 'python', name: 'Python', isSelected: false, variant: 'blue' },
|
|
162
|
-
{ id: 'java', name: 'Java', isSelected: false, variant: 'orange' },
|
|
163
|
-
{ id: 'go', name: 'Go', isSelected: false, variant: 'green' },
|
|
164
|
-
{ id: 'rust', name: 'Rust', isSelected: false, variant: 'orange' },
|
|
165
|
-
{ id: 'sql', name: 'SQL', isSelected: false, variant: 'slate' },
|
|
166
|
-
{ id: 'nosql', name: 'NoSQL', isSelected: false, variant: 'green' },
|
|
167
|
-
{ id: 'aws', name: 'AWS', isSelected: false, variant: 'yellow' },
|
|
168
|
-
{ id: 'azure', name: 'Azure', isSelected: false, variant: 'blue' },
|
|
169
|
-
{ id: 'gcp', name: 'Google Cloud', isSelected: false, variant: 'blue' },
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
const [selectedSkills, setSelectedSkills] = useState<Tag[]>([])
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<form className="max-w-2xl space-y-4">
|
|
176
|
-
<div>
|
|
177
|
-
<label className="block mb-2 font-medium">
|
|
178
|
-
Your Skills
|
|
179
|
-
<span className="text-content-presentation-global-secondary text-sm ml-2">
|
|
180
|
-
(Select all that apply)
|
|
181
|
-
</span>
|
|
182
|
-
</label>
|
|
183
|
-
|
|
184
|
-
<BadgeField
|
|
185
|
-
tags={skills}
|
|
186
|
-
placeholder="Type to search skills..."
|
|
187
|
-
size="M"
|
|
188
|
-
onValueChange={setSelectedSkills}
|
|
189
|
-
/>
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
<div className="p-4 bg-background-presentation-global-secondary rounded-lg">
|
|
193
|
-
<h4 className="font-medium mb-2">Selected Skills ({selectedSkills.length})</h4>
|
|
194
|
-
{selectedSkills.length > 0 ? (
|
|
195
|
-
<div className="flex flex-wrap gap-2">
|
|
196
|
-
{selectedSkills.map(skill => (
|
|
197
|
-
<span key={skill.id} className="text-sm">
|
|
198
|
-
{skill.name}
|
|
199
|
-
</span>
|
|
200
|
-
))}
|
|
201
|
-
</div>
|
|
202
|
-
) : (
|
|
203
|
-
<p className="text-sm text-content-presentation-global-secondary">
|
|
204
|
-
No skills selected yet
|
|
205
|
-
</p>
|
|
206
|
-
)}
|
|
207
|
-
</div>
|
|
208
|
-
</form>
|
|
209
|
-
)
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Project Tags
|
|
214
|
-
|
|
215
|
-
Tag management for projects with custom variants.
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
|
-
export function ProjectTags() {
|
|
219
|
-
const projectTags: Tag[] = [
|
|
220
|
-
{ id: '1', name: 'High Priority', isSelected: false, variant: 'red' },
|
|
221
|
-
{ id: '2', name: 'In Progress', isSelected: false, variant: 'blue' },
|
|
222
|
-
{ id: '3', name: 'Completed', isSelected: false, variant: 'green' },
|
|
223
|
-
{ id: '4', name: 'On Hold', isSelected: false, variant: 'yellow' },
|
|
224
|
-
{ id: '5', name: 'Needs Review', isSelected: false, variant: 'purple' },
|
|
225
|
-
{ id: '6', name: 'Client Approval', isSelected: false, variant: 'purple' },
|
|
226
|
-
{ id: '7', name: 'Internal', isSelected: false, variant: 'gray' },
|
|
227
|
-
{ id: '8', name: 'External', isSelected: false, variant: 'slate' },
|
|
228
|
-
{ id: '9', name: 'Urgent', isSelected: false, variant: 'orange' },
|
|
229
|
-
{ id: '10', name: 'Can Wait', isSelected: false, variant: 'green' },
|
|
230
|
-
]
|
|
231
|
-
|
|
232
|
-
const [projectData, setProjectData] = useState({
|
|
233
|
-
name: '',
|
|
234
|
-
description: '',
|
|
235
|
-
tags: [] as Tag[]
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
return (
|
|
239
|
-
<form className="max-w-2xl space-y-4">
|
|
240
|
-
<div>
|
|
241
|
-
<label className="block mb-2">Project Name</label>
|
|
242
|
-
<InputField
|
|
243
|
-
value={projectData.name}
|
|
244
|
-
onChange={(e) => setProjectData({ ...projectData, name: e.target.value })}
|
|
245
|
-
placeholder="Enter project name"
|
|
246
|
-
/>
|
|
247
|
-
</div>
|
|
248
|
-
|
|
249
|
-
<div>
|
|
250
|
-
<label className="block mb-2">Description</label>
|
|
251
|
-
<TextAreaInput
|
|
252
|
-
value={projectData.description}
|
|
253
|
-
onChange={(e) => setProjectData({ ...projectData, description: e.target.value })}
|
|
254
|
-
placeholder="Enter project description"
|
|
255
|
-
rows={3}
|
|
256
|
-
/>
|
|
257
|
-
</div>
|
|
258
|
-
|
|
259
|
-
<div>
|
|
260
|
-
<label className="block mb-2">Tags</label>
|
|
261
|
-
<BadgeField
|
|
262
|
-
tags={projectTags}
|
|
263
|
-
placeholder="Add project tags..."
|
|
264
|
-
size="M"
|
|
265
|
-
onValueChange={(tags) => setProjectData({ ...projectData, tags })}
|
|
266
|
-
/>
|
|
267
|
-
</div>
|
|
268
|
-
|
|
269
|
-
<Button variant="PrimeStyle">Create Project</Button>
|
|
270
|
-
</form>
|
|
271
|
-
)
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Different Sizes
|
|
276
|
-
|
|
277
|
-
BadgeField in various sizes.
|
|
278
|
-
|
|
279
|
-
```tsx
|
|
280
|
-
export function BadgeFieldSizes() {
|
|
281
|
-
const tags: Tag[] = [
|
|
282
|
-
{ id: '1', name: 'Small', isSelected: false, variant: 'blue' },
|
|
283
|
-
{ id: '2', name: 'Medium', isSelected: false, variant: 'green' },
|
|
284
|
-
{ id: '3', name: 'Large', isSelected: false, variant: 'purple' },
|
|
285
|
-
]
|
|
286
|
-
|
|
287
|
-
return (
|
|
288
|
-
<div className="space-y-6">
|
|
289
|
-
<div>
|
|
290
|
-
<label className="block mb-2 text-sm">Extra Small (XS)</label>
|
|
291
|
-
<BadgeField tags={tags} size="XS" placeholder="XS size..." />
|
|
292
|
-
</div>
|
|
293
|
-
|
|
294
|
-
<div>
|
|
295
|
-
<label className="block mb-2">Small (S)</label>
|
|
296
|
-
<BadgeField tags={tags} size="S" placeholder="S size..." />
|
|
297
|
-
</div>
|
|
298
|
-
|
|
299
|
-
<div>
|
|
300
|
-
<label className="block mb-2">Medium (M)</label>
|
|
301
|
-
<BadgeField tags={tags} size="M" placeholder="M size..." />
|
|
302
|
-
</div>
|
|
303
|
-
</div>
|
|
304
|
-
)
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### With Icons and Actions
|
|
309
|
-
|
|
310
|
-
Badge field with custom icons and action buttons.
|
|
311
|
-
|
|
312
|
-
```tsx
|
|
313
|
-
export function BadgeFieldWithIcon() {
|
|
314
|
-
const tags: Tag[] = [
|
|
315
|
-
{ id: '1', name: 'JavaScript', isSelected: false, variant: 'yellow' },
|
|
316
|
-
{ id: '2', name: 'Python', isSelected: false, variant: 'blue' },
|
|
317
|
-
{ id: '3', name: 'Ruby', isSelected: false, variant: 'red' },
|
|
318
|
-
]
|
|
319
|
-
|
|
320
|
-
return (
|
|
321
|
-
<BadgeField
|
|
322
|
-
tags={tags}
|
|
323
|
-
size="M"
|
|
324
|
-
placeholder="Select programming languages..."
|
|
325
|
-
icon={<i className="ri-code-line"></i>}
|
|
326
|
-
actionButton={
|
|
327
|
-
<ActionButton size="S" title="Clear all">
|
|
328
|
-
<i className="ri-close-line"></i>
|
|
329
|
-
</ActionButton>
|
|
330
|
-
}
|
|
331
|
-
/>
|
|
332
|
-
)
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### With Error State
|
|
337
|
-
|
|
338
|
-
Display validation errors. `errorMessage` turns on the field's negative border.
|
|
339
|
-
|
|
340
|
-
```tsx
|
|
341
|
-
export function BadgeFieldWithError() {
|
|
342
|
-
const [tags, setTags] = useState<Tag[]>([])
|
|
343
|
-
const [error, setError] = useState('')
|
|
344
|
-
|
|
345
|
-
const availableTags: Tag[] = [
|
|
346
|
-
{ id: '1', name: 'Option 1', isSelected: false, variant: 'blue' },
|
|
347
|
-
{ id: '2', name: 'Option 2', isSelected: false, variant: 'green' },
|
|
348
|
-
{ id: '3', name: 'Option 3', isSelected: false, variant: 'purple' },
|
|
349
|
-
]
|
|
350
|
-
|
|
351
|
-
const handleChange = (newTags: Tag[]) => {
|
|
352
|
-
setTags(newTags)
|
|
353
|
-
if (newTags.length === 0) {
|
|
354
|
-
setError('Please select at least one option')
|
|
355
|
-
} else if (newTags.length > 3) {
|
|
356
|
-
setError('Maximum 3 options allowed')
|
|
357
|
-
} else {
|
|
358
|
-
setError('')
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return (
|
|
363
|
-
<div>
|
|
364
|
-
<label className="block mb-2">
|
|
365
|
-
Required Selection
|
|
366
|
-
<span className="text-red-500 ml-1">*</span>
|
|
367
|
-
</label>
|
|
368
|
-
|
|
369
|
-
<BadgeField
|
|
370
|
-
tags={availableTags}
|
|
371
|
-
size="M"
|
|
372
|
-
placeholder="Select options (1-3)..."
|
|
373
|
-
onValueChange={handleChange}
|
|
374
|
-
errorMessage={error}
|
|
375
|
-
toolTipSide="right"
|
|
376
|
-
/>
|
|
377
|
-
</div>
|
|
378
|
-
)
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### Email Recipients
|
|
383
|
-
|
|
384
|
-
Tag-style email recipient selector.
|
|
385
|
-
|
|
386
|
-
```tsx
|
|
387
|
-
export function EmailRecipients() {
|
|
388
|
-
const contacts: Tag[] = [
|
|
389
|
-
{ id: '1', name: 'john@example.com', isSelected: false, variant: 'blue' },
|
|
390
|
-
{ id: '2', name: 'jane@example.com', isSelected: false, variant: 'green' },
|
|
391
|
-
{ id: '3', name: 'team@example.com', isSelected: false, variant: 'purple' },
|
|
392
|
-
{ id: '4', name: 'support@example.com', isSelected: false, variant: 'slate' },
|
|
393
|
-
]
|
|
394
|
-
|
|
395
|
-
const [recipients, setRecipients] = useState<Tag[]>([])
|
|
396
|
-
|
|
397
|
-
return (
|
|
398
|
-
<form className="max-w-2xl space-y-4">
|
|
399
|
-
<div>
|
|
400
|
-
<label className="block mb-2">To:</label>
|
|
401
|
-
<BadgeField
|
|
402
|
-
tags={contacts}
|
|
403
|
-
placeholder="Add recipients..."
|
|
404
|
-
size="M"
|
|
405
|
-
onValueChange={setRecipients}
|
|
406
|
-
icon={<i className="ri-user-add-line"></i>}
|
|
407
|
-
/>
|
|
408
|
-
</div>
|
|
409
|
-
|
|
410
|
-
<div>
|
|
411
|
-
<label className="block mb-2">Subject:</label>
|
|
412
|
-
<InputField placeholder="Email subject" />
|
|
413
|
-
</div>
|
|
414
|
-
|
|
415
|
-
<div>
|
|
416
|
-
<label className="block mb-2">Message:</label>
|
|
417
|
-
<TextAreaInput placeholder="Type your message..." rows={6} />
|
|
418
|
-
</div>
|
|
419
|
-
|
|
420
|
-
<Button variant="PrimeStyle" disabled={recipients.length === 0}>
|
|
421
|
-
<i className="ri-send-plane-line mr-2"></i>
|
|
422
|
-
Send to {recipients.length} recipient{recipients.length !== 1 ? 's' : ''}
|
|
423
|
-
</Button>
|
|
424
|
-
</form>
|
|
425
|
-
)
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### RTL Support
|
|
430
|
-
|
|
431
|
-
Pass `dir="rtl"` (and a translated `addLabel`) to mirror the field for right-to-left languages.
|
|
432
|
-
|
|
433
|
-
```tsx
|
|
434
|
-
export function RtlBadgeField() {
|
|
435
|
-
const tags: Tag[] = [
|
|
436
|
-
{ id: '1', name: 'إلكترونيات', isSelected: false, variant: 'blue' },
|
|
437
|
-
{ id: '2', name: 'كتب', isSelected: false, variant: 'green' },
|
|
438
|
-
{ id: '3', name: 'ملابس', isSelected: false, variant: 'purple' },
|
|
439
|
-
]
|
|
440
|
-
|
|
441
|
-
const [selected, setSelected] = useState<Tag[]>([])
|
|
442
|
-
|
|
443
|
-
return (
|
|
444
|
-
<div dir="rtl">
|
|
445
|
-
<BadgeField
|
|
446
|
-
dir="rtl"
|
|
447
|
-
tags={tags}
|
|
448
|
-
onValueChange={setSelected}
|
|
449
|
-
addLabel="إضافة"
|
|
450
|
-
placeholder="اختر وسماً"
|
|
451
|
-
/>
|
|
452
|
-
</div>
|
|
453
|
-
)
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
## API Reference
|
|
458
|
-
|
|
459
|
-
### BadgeField Props
|
|
460
|
-
|
|
461
|
-
Extends all Input element props (except size and variant).
|
|
462
|
-
|
|
463
|
-
| Prop | Type | Default | Description |
|
|
464
|
-
|------|------|---------|-------------|
|
|
465
|
-
| tags | `Tag[]` | **Required** | Available tags to select from |
|
|
466
|
-
| onValueChange | `(tags: Tag[]) => void` | - | Callback when selection changes |
|
|
467
|
-
| size | `'XS' \| 'S' \| 'M'` | `'M'` | Field size |
|
|
468
|
-
| variant | `'PresentationStyle'` | `'PresentationStyle'` | Visual variant |
|
|
469
|
-
| icon | `ReactNode` | - | Leading icon |
|
|
470
|
-
| errorMessage | `string` | - | Marks the field invalid — any non-undefined value turns on the negative border |
|
|
471
|
-
| onTable | `boolean` | `false` | Table-specific styling |
|
|
472
|
-
| toolTipSide | `ToolTipSide` | - | **Deprecated, ignored.** The error tooltip was removed; an invalid field is shown by its border alone |
|
|
473
|
-
| label | `string` | - | Field label |
|
|
474
|
-
| required | `boolean` | `false` | Required indicator |
|
|
475
|
-
| theme | `Themes` | - | Theme override |
|
|
476
|
-
| actionButton | `ReactNode` | - | Trailing action button |
|
|
477
|
-
| addLabel | `string` | `'add'` | Label for the add action shown in the field |
|
|
478
|
-
| dir | `string` | `'ltr'` | Reading direction (`'rtl'` for right-to-left) |
|
|
479
|
-
| placeholder | `string` | - | Input placeholder text |
|
|
480
|
-
| creatable | `boolean` | `false` | Let the user type a value that is not in `tags` and commit it as a badge |
|
|
481
|
-
| createLabel | `(value: string) => string` | ``value => `Create "${value}"` `` | Label for the create row; receives the typed text |
|
|
482
|
-
|
|
483
|
-
### Creatable tags
|
|
484
|
-
|
|
485
|
-
With `creatable`, the field stops being a picker over a fixed list: whatever the user types can
|
|
486
|
-
become a badge. Enter or comma commits it; Backspace on an empty box removes the last badge. Pass
|
|
487
|
-
`tags={[]}` for a pure free-text list — emails, aliases, arbitrary labels — which otherwise has to be
|
|
488
|
-
modelled as a one-column table.
|
|
489
|
-
|
|
490
|
-
```tsx
|
|
491
|
-
<BadgeField
|
|
492
|
-
creatable
|
|
493
|
-
tags={recipients}
|
|
494
|
-
onValueChange={setRecipients}
|
|
495
|
-
placeholder="Add an email…"
|
|
496
|
-
createLabel={(value) => `Invite ${value}`}
|
|
497
|
-
/>
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
The create row is suppressed when the typed text already matches a selected or listed tag
|
|
501
|
-
(case-insensitive), so you cannot produce duplicates. With `creatable` set, the empty-list message
|
|
502
|
-
becomes "Type a value and press Enter" rather than "All tags selected".
|
|
503
|
-
|
|
504
|
-
> `FormBuilder.MultiSelect` / `.Tags` forward `creatable`, but **not** `createLabel` — inside a form
|
|
505
|
-
> the create row keeps the default label.
|
|
506
|
-
|
|
507
|
-
### Tag Type
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
interface Tag {
|
|
511
|
-
id: string
|
|
512
|
-
name: string
|
|
513
|
-
isSelected: boolean
|
|
514
|
-
variant?: string
|
|
515
|
-
value?: string
|
|
516
|
-
[key: string]: any
|
|
517
|
-
}
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
## Keyboard Navigation
|
|
521
|
-
|
|
522
|
-
BadgeField includes comprehensive keyboard support:
|
|
523
|
-
|
|
524
|
-
| Key | Action |
|
|
525
|
-
|-----|--------|
|
|
526
|
-
| `ArrowDown` | Move focus to next tag in dropdown |
|
|
527
|
-
| `ArrowUp` | Move focus to previous tag in dropdown |
|
|
528
|
-
| `ArrowLeft` | Move focus to previous selected tag |
|
|
529
|
-
| `ArrowRight` | Move focus to next selected tag |
|
|
530
|
-
| `Enter` | Select the focused tag from the dropdown |
|
|
531
|
-
| `Delete` / `Backspace` | Remove the focused selected tag |
|
|
532
|
-
| `Escape` | Close dropdown |
|
|
533
|
-
| `Tab` | Close dropdown and move to next field |
|
|
534
|
-
|
|
535
|
-
## Styling
|
|
536
|
-
|
|
537
|
-
### Default Styles
|
|
538
|
-
|
|
539
|
-
- **Container**: Flex wrap with gap
|
|
540
|
-
- **Input**: Grows to fill available space
|
|
541
|
-
- **Badges**: Displayed inline with remove capability
|
|
542
|
-
- **Dropdown**: Popover with filtered tags
|
|
543
|
-
- **Focus**: Ring indicator on focused badges
|
|
544
|
-
|
|
545
|
-
### Customization
|
|
546
|
-
|
|
547
|
-
```tsx
|
|
548
|
-
// Custom width
|
|
549
|
-
<BadgeField className="w-full max-w-md" />
|
|
550
|
-
|
|
551
|
-
// Custom dropdown styling
|
|
552
|
-
<PopoverContent className="max-h-64 overflow-y-auto" />
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
## TypeScript Types
|
|
556
|
-
|
|
557
|
-
```typescript
|
|
558
|
-
interface BadgeFieldProps
|
|
559
|
-
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'variant'> {
|
|
560
|
-
size?: 'XS' | 'S' | 'M'
|
|
561
|
-
variant?: 'PresentationStyle'
|
|
562
|
-
icon?: ReactNode
|
|
563
|
-
errorMessage?: string
|
|
564
|
-
onTable?: boolean
|
|
565
|
-
toolTipSide?: ToolTipSide
|
|
566
|
-
label?: string
|
|
567
|
-
required?: boolean
|
|
568
|
-
theme?: Themes
|
|
569
|
-
actionButton?: ReactNode
|
|
570
|
-
tags: Tag[]
|
|
571
|
-
onValueChange?: (tags: Tag[]) => void
|
|
572
|
-
addLabel?: string
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
interface Tag {
|
|
576
|
-
id: string
|
|
577
|
-
name: string
|
|
578
|
-
isSelected: boolean
|
|
579
|
-
variant?: string
|
|
580
|
-
value?: string
|
|
581
|
-
[key: string]: any
|
|
582
|
-
}
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
## Common Patterns
|
|
586
|
-
|
|
587
|
-
### Controlled Component
|
|
588
|
-
|
|
589
|
-
```tsx
|
|
590
|
-
function ControlledBadgeField() {
|
|
591
|
-
const [selectedTags, setSelectedTags] = useState<Tag[]>([])
|
|
592
|
-
|
|
593
|
-
return (
|
|
594
|
-
<BadgeField
|
|
595
|
-
tags={allTags}
|
|
596
|
-
onValueChange={setSelectedTags}
|
|
597
|
-
placeholder="Select tags..."
|
|
598
|
-
/>
|
|
599
|
-
)
|
|
600
|
-
}
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
### Form Integration
|
|
604
|
-
|
|
605
|
-
```tsx
|
|
606
|
-
function FormWithBadgeField() {
|
|
607
|
-
const { register, setValue, watch } = useForm()
|
|
608
|
-
const selectedTags = watch('tags', [])
|
|
609
|
-
|
|
610
|
-
return (
|
|
611
|
-
<form>
|
|
612
|
-
<BadgeField
|
|
613
|
-
tags={availableTags}
|
|
614
|
-
onValueChange={(tags) => setValue('tags', tags)}
|
|
615
|
-
{...register('tags', { required: true })}
|
|
616
|
-
/>
|
|
617
|
-
</form>
|
|
618
|
-
)
|
|
619
|
-
}
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
## Testing
|
|
623
|
-
|
|
624
|
-
```typescript
|
|
625
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
626
|
-
import { BadgeField } from '@/components/BadgeField'
|
|
627
|
-
|
|
628
|
-
describe('BadgeField', () => {
|
|
629
|
-
const mockTags = [
|
|
630
|
-
{ id: '1', name: 'Tag 1', isSelected: false, variant: 'blue' },
|
|
631
|
-
{ id: '2', name: 'Tag 2', isSelected: false, variant: 'green' },
|
|
632
|
-
]
|
|
633
|
-
|
|
634
|
-
it('renders placeholder', () => {
|
|
635
|
-
render(
|
|
636
|
-
<BadgeField tags={mockTags} placeholder="Select tags" />
|
|
637
|
-
)
|
|
638
|
-
|
|
639
|
-
expect(screen.getByPlaceholderText('Select tags')).toBeInTheDocument()
|
|
640
|
-
})
|
|
641
|
-
|
|
642
|
-
it('shows dropdown on focus', () => {
|
|
643
|
-
render(<BadgeField tags={mockTags} />)
|
|
644
|
-
|
|
645
|
-
const input = screen.getByRole('textbox')
|
|
646
|
-
fireEvent.focus(input)
|
|
647
|
-
|
|
648
|
-
expect(screen.getByText('Tag 1')).toBeInTheDocument()
|
|
649
|
-
expect(screen.getByText('Tag 2')).toBeInTheDocument()
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
it('filters tags on search', () => {
|
|
653
|
-
render(<BadgeField tags={mockTags} />)
|
|
654
|
-
|
|
655
|
-
const input = screen.getByRole('textbox')
|
|
656
|
-
fireEvent.change(input, { target: { value: 'Tag 1' } })
|
|
657
|
-
|
|
658
|
-
expect(screen.getByText('Tag 1')).toBeInTheDocument()
|
|
659
|
-
expect(screen.queryByText('Tag 2')).not.toBeInTheDocument()
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
it('calls onValueChange when tag selected', () => {
|
|
663
|
-
const handleChange = jest.fn()
|
|
664
|
-
render(
|
|
665
|
-
<BadgeField tags={mockTags} onValueChange={handleChange} />
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
const input = screen.getByRole('textbox')
|
|
669
|
-
fireEvent.focus(input)
|
|
670
|
-
fireEvent.click(screen.getByText('Tag 1'))
|
|
671
|
-
|
|
672
|
-
// Selecting a tag emits it with isSelected: true
|
|
673
|
-
expect(handleChange).toHaveBeenCalledWith([
|
|
674
|
-
expect.objectContaining({ id: '1', isSelected: true }),
|
|
675
|
-
])
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
it('removes tag on unselect', () => {
|
|
679
|
-
const handleChange = jest.fn()
|
|
680
|
-
render(
|
|
681
|
-
<BadgeField tags={mockTags} onValueChange={handleChange} />
|
|
682
|
-
)
|
|
683
|
-
|
|
684
|
-
// Select tag
|
|
685
|
-
fireEvent.focus(screen.getByRole('textbox'))
|
|
686
|
-
fireEvent.click(screen.getByText('Tag 1'))
|
|
687
|
-
|
|
688
|
-
// Remove tag
|
|
689
|
-
const removeButton = screen.getByRole('button', { name: 'Remove badge' })
|
|
690
|
-
fireEvent.click(removeButton)
|
|
691
|
-
|
|
692
|
-
expect(handleChange).toHaveBeenLastCalledWith([])
|
|
693
|
-
})
|
|
694
|
-
})
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
## Accessibility
|
|
698
|
-
|
|
699
|
-
- **Keyboard Navigation**: Full keyboard support
|
|
700
|
-
- **ARIA Labels**: Proper labels for screen readers
|
|
701
|
-
- **Focus Management**: Clear focus indicators
|
|
702
|
-
- **Screen Reader**: Announces selected/removed tags
|
|
703
|
-
- **Error Messages**: Invalid fields are marked with the negative border token
|
|
704
|
-
- **Tab Order**: Logical tab navigation
|
|
705
|
-
|
|
706
|
-
## Performance
|
|
707
|
-
|
|
708
|
-
- **Filtered Search**: Efficient tag filtering
|
|
709
|
-
- **Click Outside**: Optimized outside click detection
|
|
710
|
-
- **Memoization**: Selected tags memoized
|
|
711
|
-
- **Virtual Scrolling**: Consider for 100+ tags
|
|
712
|
-
- **Bundle Size**: ~4 KB gzipped (with dependencies)
|
|
713
|
-
|
|
714
|
-
## Migration Guide
|
|
715
|
-
|
|
716
|
-
### From Multi-Select
|
|
717
|
-
|
|
718
|
-
```tsx
|
|
719
|
-
// Before: Standard multi-select
|
|
720
|
-
<select multiple>
|
|
721
|
-
<option value="1">Tag 1</option>
|
|
722
|
-
<option value="2">Tag 2</option>
|
|
723
|
-
</select>
|
|
724
|
-
|
|
725
|
-
// After: BadgeField
|
|
726
|
-
<BadgeField tags={tags} onValueChange={handleChange} />
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
## Best Practices
|
|
730
|
-
|
|
731
|
-
1. **Tag Limits**: Consider max selected tags for UX
|
|
732
|
-
2. **Search Placeholder**: Provide helpful search hints
|
|
733
|
-
3. **Error Handling**: Show clear validation errors
|
|
734
|
-
4. **Loading States**: Handle async tag loading
|
|
735
|
-
5. **Empty States**: Show message when no tags available
|
|
736
|
-
6. **Mobile UX**: Ensure touch-friendly targets
|
|
737
|
-
7. **Performance**: Virtualize for large tag lists (100+)
|
|
738
|
-
8. **Accessibility**: Always provide clear labels
|