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,770 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: LabelField
|
|
3
|
-
description: Combined Label and InputField component for streamlined form field creation with all label features
|
|
4
|
-
group: Labels & Text
|
|
5
|
-
keywords: [label-field, form, input, label, required, field]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# LabelField
|
|
9
|
-
|
|
10
|
-
> A convenient combination of Label and InputField components that provides all label features (primary, secondary, required) along with complete input functionality in a single component. Perfect for quickly building consistent form fields.
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
15
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npx torch-glare@latest init
|
|
19
|
-
npx torch-glare@latest add LabelField
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `LabelField` depends on.
|
|
23
|
-
|
|
24
|
-
## Import
|
|
25
|
-
|
|
26
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
import { LabelField } from "@/components/LabelField";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Usage
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { LabelField } from "@/components/LabelField";
|
|
38
|
-
import { useState } from 'react'
|
|
39
|
-
|
|
40
|
-
function Example() {
|
|
41
|
-
const [value, setValue] = useState('')
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<LabelField
|
|
45
|
-
label="Email Address"
|
|
46
|
-
type="email"
|
|
47
|
-
value={value}
|
|
48
|
-
onChange={(e) => setValue(e.target.value)}
|
|
49
|
-
/>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### With Required Indicator
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { LabelField } from "@/components/LabelField";
|
|
58
|
-
|
|
59
|
-
function RequiredField() {
|
|
60
|
-
return (
|
|
61
|
-
<LabelField
|
|
62
|
-
label="Full Name"
|
|
63
|
-
requiredLabel="*Required"
|
|
64
|
-
required
|
|
65
|
-
/>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### With Secondary Label
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { LabelField } from "@/components/LabelField";
|
|
74
|
-
|
|
75
|
-
function WithSecondary() {
|
|
76
|
-
return (
|
|
77
|
-
<LabelField
|
|
78
|
-
label="Password"
|
|
79
|
-
secondaryLabel="(At least 8 characters)"
|
|
80
|
-
type="password"
|
|
81
|
-
/>
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### With Error Message
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
import { LabelField } from "@/components/LabelField";
|
|
90
|
-
import { useState } from 'react'
|
|
91
|
-
|
|
92
|
-
function WithValidation() {
|
|
93
|
-
const [email, setEmail] = useState('')
|
|
94
|
-
const [error, setError] = useState('')
|
|
95
|
-
|
|
96
|
-
const handleBlur = () => {
|
|
97
|
-
if (!email.includes('@')) {
|
|
98
|
-
setError('Please enter a valid email')
|
|
99
|
-
} else {
|
|
100
|
-
setError('')
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<LabelField
|
|
106
|
-
label="Email"
|
|
107
|
-
type="email"
|
|
108
|
-
value={email}
|
|
109
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
110
|
-
onBlur={handleBlur}
|
|
111
|
-
errorMessage={error}
|
|
112
|
-
/>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### With Icon
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { LabelField } from "@/components/LabelField";
|
|
121
|
-
|
|
122
|
-
function WithIcon() {
|
|
123
|
-
return (
|
|
124
|
-
<LabelField
|
|
125
|
-
label="Search"
|
|
126
|
-
icon={<i className="ri-search-line" />}
|
|
127
|
-
placeholder="Type to search..."
|
|
128
|
-
/>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### With Action Button
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { LabelField } from "@/components/LabelField";
|
|
137
|
-
import { ActionButton } from "@/components/ActionButton";
|
|
138
|
-
import { useState } from 'react'
|
|
139
|
-
|
|
140
|
-
function WithActionButton() {
|
|
141
|
-
const [password, setPassword] = useState('')
|
|
142
|
-
const [showPassword, setShowPassword] = useState(false)
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<LabelField
|
|
146
|
-
label="Password"
|
|
147
|
-
type={showPassword ? 'text' : 'password'}
|
|
148
|
-
value={password}
|
|
149
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
150
|
-
childrenSide={
|
|
151
|
-
<ActionButton onClick={() => setShowPassword(!showPassword)}>
|
|
152
|
-
<i className={showPassword ? 'ri-eye-off-line' : 'ri-eye-line'} />
|
|
153
|
-
</ActionButton>
|
|
154
|
-
}
|
|
155
|
-
/>
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Horizontal Layout
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import { LabelField } from "@/components/LabelField";
|
|
164
|
-
|
|
165
|
-
function HorizontalLayout() {
|
|
166
|
-
return (
|
|
167
|
-
<LabelField
|
|
168
|
-
label="Notifications"
|
|
169
|
-
labelDirections="horizontal"
|
|
170
|
-
childrenDirections="horizontal"
|
|
171
|
-
type="checkbox"
|
|
172
|
-
/>
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Different Sizes
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { LabelField } from "@/components/LabelField";
|
|
181
|
-
|
|
182
|
-
function Sizes() {
|
|
183
|
-
return (
|
|
184
|
-
<div className="space-y-4">
|
|
185
|
-
<LabelField
|
|
186
|
-
label="Small Input"
|
|
187
|
-
size="S"
|
|
188
|
-
placeholder="Small size"
|
|
189
|
-
/>
|
|
190
|
-
|
|
191
|
-
<LabelField
|
|
192
|
-
label="Medium Input"
|
|
193
|
-
size="M"
|
|
194
|
-
placeholder="Medium size"
|
|
195
|
-
/>
|
|
196
|
-
</div>
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Registration Form
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
import { LabelField } from "@/components/LabelField";
|
|
205
|
-
import { Button } from "@/components/Button";
|
|
206
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
207
|
-
import { useState } from 'react'
|
|
208
|
-
|
|
209
|
-
function RegistrationForm() {
|
|
210
|
-
const [formData, setFormData] = useState({
|
|
211
|
-
name: '',
|
|
212
|
-
email: '',
|
|
213
|
-
password: '',
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
217
|
-
|
|
218
|
-
const validate = () => {
|
|
219
|
-
const newErrors: Record<string, string> = {}
|
|
220
|
-
|
|
221
|
-
if (!formData.name) {
|
|
222
|
-
newErrors.name = 'Name is required'
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (!formData.email) {
|
|
226
|
-
newErrors.email = 'Email is required'
|
|
227
|
-
} else if (!formData.email.includes('@')) {
|
|
228
|
-
newErrors.email = 'Invalid email format'
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!formData.password) {
|
|
232
|
-
newErrors.password = 'Password is required'
|
|
233
|
-
} else if (formData.password.length < 8) {
|
|
234
|
-
newErrors.password = 'Password must be at least 8 characters'
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
setErrors(newErrors)
|
|
238
|
-
return Object.keys(newErrors).length === 0
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const handleSubmit = (e: React.FormEvent) => {
|
|
242
|
-
e.preventDefault()
|
|
243
|
-
if (validate()) {
|
|
244
|
-
console.log('Form submitted:', formData)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return (
|
|
249
|
-
<form onSubmit={handleSubmit} className="space-y-4">
|
|
250
|
-
<LabelField
|
|
251
|
-
label="Full Name"
|
|
252
|
-
requiredLabel="*"
|
|
253
|
-
value={formData.name}
|
|
254
|
-
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
255
|
-
errorMessage={errors.name}
|
|
256
|
-
required
|
|
257
|
-
/>
|
|
258
|
-
|
|
259
|
-
<LabelField
|
|
260
|
-
label="Email"
|
|
261
|
-
secondaryLabel="(We'll never share your email)"
|
|
262
|
-
requiredLabel="*"
|
|
263
|
-
type="email"
|
|
264
|
-
value={formData.email}
|
|
265
|
-
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
266
|
-
errorMessage={errors.email}
|
|
267
|
-
required
|
|
268
|
-
/>
|
|
269
|
-
|
|
270
|
-
<div className="space-y-2">
|
|
271
|
-
<LabelField
|
|
272
|
-
label="Password"
|
|
273
|
-
secondaryLabel="(At least 8 characters)"
|
|
274
|
-
requiredLabel="*"
|
|
275
|
-
labelDirections="vertical"
|
|
276
|
-
type="password"
|
|
277
|
-
value={formData.password}
|
|
278
|
-
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
|
279
|
-
errorMessage={errors.password}
|
|
280
|
-
required
|
|
281
|
-
/>
|
|
282
|
-
<PasswordLevel value={formData.password} />
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
<Button type="submit">Create Account</Button>
|
|
286
|
-
</form>
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### With Popover Dropdown
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
import { LabelField } from "@/components/LabelField";
|
|
295
|
-
import { useState } from 'react'
|
|
296
|
-
|
|
297
|
-
function WithPopover() {
|
|
298
|
-
const [value, setValue] = useState('')
|
|
299
|
-
const suggestions = ['Option 1', 'Option 2', 'Option 3']
|
|
300
|
-
|
|
301
|
-
return (
|
|
302
|
-
<LabelField
|
|
303
|
-
label="Select Option"
|
|
304
|
-
value={value}
|
|
305
|
-
onChange={(e) => setValue(e.target.value)}
|
|
306
|
-
popoverChildren={
|
|
307
|
-
<div className="p-2 space-y-1">
|
|
308
|
-
{suggestions.map((suggestion) => (
|
|
309
|
-
<div
|
|
310
|
-
key={suggestion}
|
|
311
|
-
className="p-2 hover:bg-gray-100 cursor-pointer rounded"
|
|
312
|
-
onClick={() => setValue(suggestion)}
|
|
313
|
-
>
|
|
314
|
-
{suggestion}
|
|
315
|
-
</div>
|
|
316
|
-
))}
|
|
317
|
-
</div>
|
|
318
|
-
}
|
|
319
|
-
/>
|
|
320
|
-
)
|
|
321
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### Profile Settings Form
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
import { LabelField } from "@/components/LabelField";
|
|
328
|
-
import { Button } from "@/components/Button";
|
|
329
|
-
import { useState } from 'react'
|
|
330
|
-
|
|
331
|
-
function ProfileSettings() {
|
|
332
|
-
const [profile, setProfile] = useState({
|
|
333
|
-
username: 'johndoe',
|
|
334
|
-
email: 'john@example.com',
|
|
335
|
-
bio: '',
|
|
336
|
-
website: '',
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
return (
|
|
340
|
-
<form className="space-y-4">
|
|
341
|
-
<LabelField
|
|
342
|
-
label="Username"
|
|
343
|
-
requiredLabel="*"
|
|
344
|
-
value={profile.username}
|
|
345
|
-
onChange={(e) => setProfile({ ...profile, username: e.target.value })}
|
|
346
|
-
icon={<i className="ri-user-line" />}
|
|
347
|
-
/>
|
|
348
|
-
|
|
349
|
-
<LabelField
|
|
350
|
-
label="Email"
|
|
351
|
-
requiredLabel="*"
|
|
352
|
-
type="email"
|
|
353
|
-
value={profile.email}
|
|
354
|
-
onChange={(e) => setProfile({ ...profile, email: e.target.value })}
|
|
355
|
-
icon={<i className="ri-mail-line" />}
|
|
356
|
-
/>
|
|
357
|
-
|
|
358
|
-
<LabelField
|
|
359
|
-
label="Bio"
|
|
360
|
-
secondaryLabel="(Optional)"
|
|
361
|
-
value={profile.bio}
|
|
362
|
-
onChange={(e) => setProfile({ ...profile, bio: e.target.value })}
|
|
363
|
-
placeholder="Tell us about yourself"
|
|
364
|
-
/>
|
|
365
|
-
|
|
366
|
-
<LabelField
|
|
367
|
-
label="Website"
|
|
368
|
-
secondaryLabel="(Optional)"
|
|
369
|
-
type="url"
|
|
370
|
-
value={profile.website}
|
|
371
|
-
onChange={(e) => setProfile({ ...profile, website: e.target.value })}
|
|
372
|
-
placeholder="https://example.com"
|
|
373
|
-
icon={<i className="ri-global-line" />}
|
|
374
|
-
/>
|
|
375
|
-
|
|
376
|
-
<Button type="submit">Save Changes</Button>
|
|
377
|
-
</form>
|
|
378
|
-
)
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## API Reference
|
|
383
|
-
|
|
384
|
-
### LabelField Props
|
|
385
|
-
|
|
386
|
-
Combines all Label and InputField props (excluding conflicting ones).
|
|
387
|
-
|
|
388
|
-
| Prop | Type | Default | Description |
|
|
389
|
-
|------|------|---------|-------------|
|
|
390
|
-
| `label` | `ReactNode` | - | Primary label text |
|
|
391
|
-
| `requiredLabel` | `ReactNode` | - | Required indicator text |
|
|
392
|
-
| `secondaryLabel` | `ReactNode` | - | Secondary/helper text |
|
|
393
|
-
| `size` | `'S' \| 'M'` | `'M'` | Component size |
|
|
394
|
-
| `variant` | `'SystemStyle' \| 'PresentationStyle'` | `'PresentationStyle'` | Style variant |
|
|
395
|
-
| `theme` | `'light' \| 'dark' \| 'default'` | - | Theme variant |
|
|
396
|
-
| `icon` | `ReactNode` | - | Left-side icon |
|
|
397
|
-
| `childrenSide` | `ReactNode` | - | Right-side content (action button) |
|
|
398
|
-
| `popoverChildren` | `ReactNode` | - | Dropdown content |
|
|
399
|
-
| `errorMessage` | `string` | - | Error message (shows tooltip) |
|
|
400
|
-
| `onTable` | `boolean` | `false` | Special styling for table context |
|
|
401
|
-
| `labelDirections` | `'vertical' \| 'horizontal'` | `'horizontal'` | Label texts direction |
|
|
402
|
-
| `childrenDirections` | `'vertical' \| 'horizontal'` | `'vertical'` | Label and input direction |
|
|
403
|
-
| `toolTipSide` | `ToolTipSide` | - | Tooltip position |
|
|
404
|
-
| ...InputHTMLAttributes | - | - | All input attributes |
|
|
405
|
-
|
|
406
|
-
## TypeScript
|
|
407
|
-
|
|
408
|
-
### Full Type Definitions
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
import { InputHTMLAttributes, ReactNode } from 'react'
|
|
412
|
-
|
|
413
|
-
interface LabelFieldProps
|
|
414
|
-
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'variant'> {
|
|
415
|
-
size?: 'S' | 'M'
|
|
416
|
-
variant?: 'SystemStyle' | 'PresentationStyle'
|
|
417
|
-
icon?: ReactNode
|
|
418
|
-
childrenSide?: ReactNode
|
|
419
|
-
popoverChildren?: ReactNode
|
|
420
|
-
errorMessage?: string
|
|
421
|
-
onTable?: boolean
|
|
422
|
-
label?: ReactNode
|
|
423
|
-
requiredLabel?: ReactNode
|
|
424
|
-
secondaryLabel?: ReactNode
|
|
425
|
-
labelDirections?: 'vertical' | 'horizontal'
|
|
426
|
-
childrenDirections?: 'vertical' | 'horizontal'
|
|
427
|
-
toolTipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
428
|
-
theme?: 'light' | 'dark' | 'default'
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
export const LabelField: React.ForwardRefExoticComponent<
|
|
432
|
-
LabelFieldProps & React.RefAttributes<HTMLInputElement>
|
|
433
|
-
>
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
### Usage with Types
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
import { LabelField } from "@/components/LabelField";
|
|
440
|
-
import { useRef, useState } from 'react'
|
|
441
|
-
|
|
442
|
-
function TypedExample() {
|
|
443
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
444
|
-
const [value, setValue] = useState<string>('')
|
|
445
|
-
|
|
446
|
-
const focusInput = () => {
|
|
447
|
-
inputRef.current?.focus()
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return (
|
|
451
|
-
<LabelField
|
|
452
|
-
ref={inputRef}
|
|
453
|
-
label="Typed Input"
|
|
454
|
-
value={value}
|
|
455
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
456
|
-
setValue(e.target.value)
|
|
457
|
-
}}
|
|
458
|
-
/>
|
|
459
|
-
)
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
## Common Patterns
|
|
464
|
-
|
|
465
|
-
### Form Field Wrapper Hook
|
|
466
|
-
|
|
467
|
-
```typescript
|
|
468
|
-
import { LabelField } from "@/components/LabelField";
|
|
469
|
-
import { useState, useCallback } from 'react'
|
|
470
|
-
|
|
471
|
-
function useFormField(initialValue: string = '') {
|
|
472
|
-
const [value, setValue] = useState(initialValue)
|
|
473
|
-
const [error, setError] = useState<string>()
|
|
474
|
-
const [touched, setTouched] = useState(false)
|
|
475
|
-
|
|
476
|
-
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
477
|
-
setValue(e.target.value)
|
|
478
|
-
setError(undefined)
|
|
479
|
-
}, [])
|
|
480
|
-
|
|
481
|
-
const handleBlur = useCallback(() => {
|
|
482
|
-
setTouched(true)
|
|
483
|
-
}, [])
|
|
484
|
-
|
|
485
|
-
const validate = useCallback((validator: (value: string) => string | undefined) => {
|
|
486
|
-
const errorMessage = validator(value)
|
|
487
|
-
setError(errorMessage)
|
|
488
|
-
return !errorMessage
|
|
489
|
-
}, [value])
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
value,
|
|
493
|
-
setValue,
|
|
494
|
-
error,
|
|
495
|
-
touched,
|
|
496
|
-
handleChange,
|
|
497
|
-
handleBlur,
|
|
498
|
-
validate,
|
|
499
|
-
fieldProps: {
|
|
500
|
-
value,
|
|
501
|
-
onChange: handleChange,
|
|
502
|
-
onBlur: handleBlur,
|
|
503
|
-
errorMessage: touched ? error : undefined,
|
|
504
|
-
},
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Usage
|
|
509
|
-
function FormWithHook() {
|
|
510
|
-
const emailField = useFormField('')
|
|
511
|
-
|
|
512
|
-
const handleSubmit = (e: React.FormEvent) => {
|
|
513
|
-
e.preventDefault()
|
|
514
|
-
|
|
515
|
-
const isValid = emailField.validate((value) =>
|
|
516
|
-
!value.includes('@') ? 'Invalid email' : undefined
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
if (isValid) {
|
|
520
|
-
console.log('Submit:', emailField.value)
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
return (
|
|
525
|
-
<form onSubmit={handleSubmit}>
|
|
526
|
-
<LabelField
|
|
527
|
-
label="Email"
|
|
528
|
-
type="email"
|
|
529
|
-
{...emailField.fieldProps}
|
|
530
|
-
/>
|
|
531
|
-
</form>
|
|
532
|
-
)
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
### Reusable Form Fields
|
|
537
|
-
|
|
538
|
-
```typescript
|
|
539
|
-
import { LabelField } from "@/components/LabelField";
|
|
540
|
-
import { ComponentProps } from 'react'
|
|
541
|
-
|
|
542
|
-
type FieldConfig = Omit<ComponentProps<typeof LabelField>, 'value' | 'onChange'> & {
|
|
543
|
-
name: string
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function FormBuilder({
|
|
547
|
-
fields,
|
|
548
|
-
values,
|
|
549
|
-
onChange,
|
|
550
|
-
}: {
|
|
551
|
-
fields: FieldConfig[]
|
|
552
|
-
values: Record<string, string>
|
|
553
|
-
onChange: (name: string, value: string) => void
|
|
554
|
-
}) {
|
|
555
|
-
return (
|
|
556
|
-
<div className="space-y-4">
|
|
557
|
-
{fields.map((field) => (
|
|
558
|
-
<LabelField
|
|
559
|
-
key={field.name}
|
|
560
|
-
{...field}
|
|
561
|
-
value={values[field.name] || ''}
|
|
562
|
-
onChange={(e) => onChange(field.name, e.target.value)}
|
|
563
|
-
/>
|
|
564
|
-
))}
|
|
565
|
-
</div>
|
|
566
|
-
)
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Usage
|
|
570
|
-
function DynamicForm() {
|
|
571
|
-
const [values, setValues] = useState<Record<string, string>>({})
|
|
572
|
-
|
|
573
|
-
const fields: FieldConfig[] = [
|
|
574
|
-
{ name: 'firstName', label: 'First Name', requiredLabel: '*' },
|
|
575
|
-
{ name: 'lastName', label: 'Last Name', requiredLabel: '*' },
|
|
576
|
-
{ name: 'email', label: 'Email', type: 'email', requiredLabel: '*' },
|
|
577
|
-
]
|
|
578
|
-
|
|
579
|
-
const handleChange = (name: string, value: string) => {
|
|
580
|
-
setValues({ ...values, [name]: value })
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return <FormBuilder fields={fields} values={values} onChange={handleChange} />
|
|
584
|
-
}
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
## Testing
|
|
588
|
-
|
|
589
|
-
### Unit Test Examples
|
|
590
|
-
|
|
591
|
-
```typescript
|
|
592
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
593
|
-
import { LabelField } from "@/components/LabelField";
|
|
594
|
-
import userEvent from '@testing-library/user-event'
|
|
595
|
-
|
|
596
|
-
describe('LabelField', () => {
|
|
597
|
-
it('renders label and input', () => {
|
|
598
|
-
render(<LabelField label="Test Label" />)
|
|
599
|
-
|
|
600
|
-
expect(screen.getByText('Test Label')).toBeInTheDocument()
|
|
601
|
-
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
it('renders all label types', () => {
|
|
605
|
-
render(
|
|
606
|
-
<LabelField
|
|
607
|
-
label="Main"
|
|
608
|
-
secondaryLabel="Secondary"
|
|
609
|
-
requiredLabel="*"
|
|
610
|
-
/>
|
|
611
|
-
)
|
|
612
|
-
|
|
613
|
-
expect(screen.getByText('Main')).toBeInTheDocument()
|
|
614
|
-
expect(screen.getByText('Secondary')).toBeInTheDocument()
|
|
615
|
-
expect(screen.getByText('*')).toBeInTheDocument()
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
it('handles input changes', async () => {
|
|
619
|
-
const handleChange = jest.fn()
|
|
620
|
-
|
|
621
|
-
render(
|
|
622
|
-
<LabelField
|
|
623
|
-
label="Email"
|
|
624
|
-
onChange={handleChange}
|
|
625
|
-
/>
|
|
626
|
-
)
|
|
627
|
-
|
|
628
|
-
const input = screen.getByRole('textbox')
|
|
629
|
-
await userEvent.type(input, 'test@example.com')
|
|
630
|
-
|
|
631
|
-
expect(handleChange).toHaveBeenCalled()
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
it('shows error message', () => {
|
|
635
|
-
render(
|
|
636
|
-
<LabelField
|
|
637
|
-
label="Email"
|
|
638
|
-
errorMessage="Invalid email"
|
|
639
|
-
/>
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
// Error tooltip should be present
|
|
643
|
-
expect(screen.getByRole('textbox')).toHaveAttribute('aria-invalid', 'true')
|
|
644
|
-
})
|
|
645
|
-
|
|
646
|
-
it('renders with icon', () => {
|
|
647
|
-
render(
|
|
648
|
-
<LabelField
|
|
649
|
-
label="Search"
|
|
650
|
-
icon={<span data-testid="icon">🔍</span>}
|
|
651
|
-
/>
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
expect(screen.getByTestId('icon')).toBeInTheDocument()
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
it('forwards ref correctly', () => {
|
|
658
|
-
const ref = React.createRef<HTMLInputElement>()
|
|
659
|
-
|
|
660
|
-
render(<LabelField label="Test" ref={ref} />)
|
|
661
|
-
|
|
662
|
-
expect(ref.current).toBeInstanceOf(HTMLInputElement)
|
|
663
|
-
})
|
|
664
|
-
})
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
## Accessibility
|
|
668
|
-
|
|
669
|
-
- **Semantic HTML**: Proper label-input relationship
|
|
670
|
-
- **ARIA Attributes**: Error states use aria-invalid
|
|
671
|
-
- **Keyboard Navigation**: Full keyboard support
|
|
672
|
-
- **Screen Readers**: Labels and errors announced correctly
|
|
673
|
-
- **Required Indication**: Clear visual and semantic markers
|
|
674
|
-
- **Error Tooltips**: Accessible error messaging
|
|
675
|
-
|
|
676
|
-
### Accessibility Best Practices
|
|
677
|
-
|
|
678
|
-
```typescript
|
|
679
|
-
// Provide clear labels
|
|
680
|
-
<LabelField
|
|
681
|
-
label="Email Address"
|
|
682
|
-
placeholder="name@example.com"
|
|
683
|
-
/>
|
|
684
|
-
|
|
685
|
-
// Mark required fields
|
|
686
|
-
<LabelField
|
|
687
|
-
label="Password"
|
|
688
|
-
requiredLabel="*Required"
|
|
689
|
-
required
|
|
690
|
-
aria-required="true"
|
|
691
|
-
/>
|
|
692
|
-
|
|
693
|
-
// Provide helpful secondary labels
|
|
694
|
-
<LabelField
|
|
695
|
-
label="Phone"
|
|
696
|
-
secondaryLabel="Format: (555) 555-5555"
|
|
697
|
-
/>
|
|
698
|
-
|
|
699
|
-
// Use proper error messaging
|
|
700
|
-
<LabelField
|
|
701
|
-
label="Email"
|
|
702
|
-
errorMessage="Please enter a valid email address"
|
|
703
|
-
aria-describedby="email-error"
|
|
704
|
-
/>
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
## Performance
|
|
708
|
-
|
|
709
|
-
| Metric | Value |
|
|
710
|
-
|--------|-------|
|
|
711
|
-
| Bundle size (minified) | ~6kb |
|
|
712
|
-
| Bundle size (gzipped) | ~2kb |
|
|
713
|
-
| Dependencies | Label + InputField |
|
|
714
|
-
| Re-renders | Optimized with React.memo |
|
|
715
|
-
| Tree-shakeable | ✅ |
|
|
716
|
-
|
|
717
|
-
### Performance Tips
|
|
718
|
-
|
|
719
|
-
1. **Memoize handlers**: Prevent unnecessary re-renders
|
|
720
|
-
```typescript
|
|
721
|
-
const handleChange = useCallback((e) => setValue(e.target.value), [])
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
2. **Controlled vs Uncontrolled**: Use uncontrolled for simple forms
|
|
725
|
-
```typescript
|
|
726
|
-
<LabelField label="Name" defaultValue="John" />
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
3. **Debounce validation**: Avoid validating on every keystroke
|
|
730
|
-
```typescript
|
|
731
|
-
const debouncedValidate = useMemo(
|
|
732
|
-
() => debounce(validate, 300),
|
|
733
|
-
[validate]
|
|
734
|
-
)
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
## Styling
|
|
738
|
-
|
|
739
|
-
### Custom Styles
|
|
740
|
-
|
|
741
|
-
```typescript
|
|
742
|
-
// Custom container
|
|
743
|
-
<LabelField
|
|
744
|
-
label="Styled Field"
|
|
745
|
-
className="bg-gray-50 p-4 rounded-lg"
|
|
746
|
-
/>
|
|
747
|
-
|
|
748
|
-
// Custom label styling
|
|
749
|
-
<LabelField
|
|
750
|
-
label="Custom Label"
|
|
751
|
-
labelsClassName="text-blue-600"
|
|
752
|
-
/>
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
## Best Practices
|
|
756
|
-
|
|
757
|
-
1. **Use for complete form fields**: Combines label and input conveniently
|
|
758
|
-
2. **Provide clear labels**: Essential for usability
|
|
759
|
-
3. **Mark required fields**: Use requiredLabel prop
|
|
760
|
-
4. **Add helper text**: Use secondaryLabel for context
|
|
761
|
-
5. **Validate on blur**: Better UX than on every keystroke
|
|
762
|
-
6. **Show errors clearly**: Use errorMessage prop
|
|
763
|
-
7. **Group related fields**: Use consistent sizing and spacing
|
|
764
|
-
|
|
765
|
-
## Related Components
|
|
766
|
-
|
|
767
|
-
- [Label](./label.md) - Standalone label component
|
|
768
|
-
- [InputField](./input-field.md) - Standalone input component
|
|
769
|
-
- [InnerLabelField](./inner-label-field.md) - Floating label inside input
|
|
770
|
-
- [FieldHint](./field-hint.md) - Inline field hints
|