torch-glare 2.5.5 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -338
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -317
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -383
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -84
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -63
- package/apps/lib/components/DataViews/states.tsx +0 -38
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -420
- package/apps/lib/components/DropdownMenu.tsx +0 -540
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -73
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -163
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -91
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -47
- package/apps/lib/components/FormBuilder/types.ts +0 -311
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -128
- package/apps/lib/components/FormRenderer/detail.tsx +0 -207
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -297
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -15
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -339
- package/apps/lib/components/FormRenderer/types.ts +0 -78
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -147
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -253
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -610
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -58
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -214
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1201
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -712
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1495
- package/docs/components/data-views/migration.md +0 -79
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -276
- package/docs/components/form-renderer.md +0 -326
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -477
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -54
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1500
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -704
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,819 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: InnerLabelField
|
|
3
|
-
description: Floating label input field with animated label that moves on focus and when value exists
|
|
4
|
-
group: Labels & Text
|
|
5
|
-
keywords: [inner-label, floating-label, input, animated, material-design]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# InnerLabelField
|
|
9
|
-
|
|
10
|
-
> An input field with an animated floating label that appears inside the input and animates to a smaller size when focused or when the field has a value. Inspired by Material Design's floating label pattern.
|
|
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 InnerLabelField
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `InnerLabelField` 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 { InnerLabelField } from "@/components/InnerLabelField";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Usage
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
38
|
-
import { useState } from 'react'
|
|
39
|
-
|
|
40
|
-
function Example() {
|
|
41
|
-
const [value, setValue] = useState('')
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<InnerLabelField
|
|
45
|
-
label="Email Address"
|
|
46
|
-
value={value}
|
|
47
|
-
onChange={(e) => setValue(e.target.value)}
|
|
48
|
-
/>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### With Required Indicator
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
57
|
-
|
|
58
|
-
function RequiredField() {
|
|
59
|
-
return (
|
|
60
|
-
<InnerLabelField
|
|
61
|
-
label="Full Name"
|
|
62
|
-
required
|
|
63
|
-
/>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Different Input Types
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
72
|
-
import { useState } from 'react'
|
|
73
|
-
|
|
74
|
-
function InputTypes() {
|
|
75
|
-
return (
|
|
76
|
-
<div className="space-y-4">
|
|
77
|
-
<InnerLabelField
|
|
78
|
-
label="Email"
|
|
79
|
-
type="email"
|
|
80
|
-
/>
|
|
81
|
-
|
|
82
|
-
<InnerLabelField
|
|
83
|
-
label="Password"
|
|
84
|
-
type="password"
|
|
85
|
-
/>
|
|
86
|
-
|
|
87
|
-
<InnerLabelField
|
|
88
|
-
label="Phone"
|
|
89
|
-
type="tel"
|
|
90
|
-
/>
|
|
91
|
-
|
|
92
|
-
<InnerLabelField
|
|
93
|
-
label="Website"
|
|
94
|
-
type="url"
|
|
95
|
-
/>
|
|
96
|
-
</div>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### With Error Message
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
105
|
-
import { useState } from 'react'
|
|
106
|
-
|
|
107
|
-
function WithValidation() {
|
|
108
|
-
const [email, setEmail] = useState('')
|
|
109
|
-
const [error, setError] = useState('')
|
|
110
|
-
|
|
111
|
-
const handleBlur = () => {
|
|
112
|
-
if (email && !email.includes('@')) {
|
|
113
|
-
setError('Please enter a valid email')
|
|
114
|
-
} else {
|
|
115
|
-
setError('')
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<InnerLabelField
|
|
121
|
-
label="Email"
|
|
122
|
-
type="email"
|
|
123
|
-
value={email}
|
|
124
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
125
|
-
onBlur={handleBlur}
|
|
126
|
-
errorMessage={error}
|
|
127
|
-
/>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### With Action Button
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
136
|
-
import { ActionButton } from "@/components/ActionButton";
|
|
137
|
-
import { useState } from 'react'
|
|
138
|
-
|
|
139
|
-
function WithActionButton() {
|
|
140
|
-
const [password, setPassword] = useState('')
|
|
141
|
-
const [showPassword, setShowPassword] = useState(false)
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<InnerLabelField
|
|
145
|
-
label="Password"
|
|
146
|
-
type={showPassword ? 'text' : 'password'}
|
|
147
|
-
value={password}
|
|
148
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
149
|
-
childrenSide={
|
|
150
|
-
<ActionButton onClick={() => setShowPassword(!showPassword)}>
|
|
151
|
-
<i className={showPassword ? 'ri-eye-off-line' : 'ri-eye-line'} />
|
|
152
|
-
</ActionButton>
|
|
153
|
-
}
|
|
154
|
-
/>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Login Form
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
163
|
-
import { Button } from "@/components/Button";
|
|
164
|
-
import { FieldHint } from "@/components/FieldHint";
|
|
165
|
-
import { useState } from 'react'
|
|
166
|
-
|
|
167
|
-
function LoginForm() {
|
|
168
|
-
const [credentials, setCredentials] = useState({
|
|
169
|
-
email: '',
|
|
170
|
-
password: '',
|
|
171
|
-
})
|
|
172
|
-
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
173
|
-
|
|
174
|
-
const handleSubmit = (e: React.FormEvent) => {
|
|
175
|
-
e.preventDefault()
|
|
176
|
-
|
|
177
|
-
const newErrors: Record<string, string> = {}
|
|
178
|
-
|
|
179
|
-
if (!credentials.email) {
|
|
180
|
-
newErrors.email = 'Email is required'
|
|
181
|
-
} else if (!credentials.email.includes('@')) {
|
|
182
|
-
newErrors.email = 'Invalid email format'
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!credentials.password) {
|
|
186
|
-
newErrors.password = 'Password is required'
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
setErrors(newErrors)
|
|
190
|
-
|
|
191
|
-
if (Object.keys(newErrors).length === 0) {
|
|
192
|
-
console.log('Login:', credentials)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<form onSubmit={handleSubmit} className="space-y-4">
|
|
198
|
-
<div className="space-y-2">
|
|
199
|
-
<InnerLabelField
|
|
200
|
-
label="Email"
|
|
201
|
-
type="email"
|
|
202
|
-
value={credentials.email}
|
|
203
|
-
onChange={(e) => setCredentials({ ...credentials, email: e.target.value })}
|
|
204
|
-
errorMessage={errors.email}
|
|
205
|
-
required
|
|
206
|
-
/>
|
|
207
|
-
{errors.email && (
|
|
208
|
-
<FieldHint state="error" label={errors.email} />
|
|
209
|
-
)}
|
|
210
|
-
</div>
|
|
211
|
-
|
|
212
|
-
<div className="space-y-2">
|
|
213
|
-
<InnerLabelField
|
|
214
|
-
label="Password"
|
|
215
|
-
type="password"
|
|
216
|
-
value={credentials.password}
|
|
217
|
-
onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
|
|
218
|
-
errorMessage={errors.password}
|
|
219
|
-
required
|
|
220
|
-
/>
|
|
221
|
-
{errors.password && (
|
|
222
|
-
<FieldHint state="error" label={errors.password} />
|
|
223
|
-
)}
|
|
224
|
-
</div>
|
|
225
|
-
|
|
226
|
-
<Button type="submit" className="w-full">
|
|
227
|
-
Sign In
|
|
228
|
-
</Button>
|
|
229
|
-
</form>
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Contact Form
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
238
|
-
import { Button } from "@/components/Button";
|
|
239
|
-
import { useState } from 'react'
|
|
240
|
-
|
|
241
|
-
function ContactForm() {
|
|
242
|
-
const [formData, setFormData] = useState({
|
|
243
|
-
name: '',
|
|
244
|
-
email: '',
|
|
245
|
-
subject: '',
|
|
246
|
-
phone: '',
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
<form className="space-y-4">
|
|
251
|
-
<InnerLabelField
|
|
252
|
-
label="Your Name"
|
|
253
|
-
value={formData.name}
|
|
254
|
-
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
255
|
-
required
|
|
256
|
-
/>
|
|
257
|
-
|
|
258
|
-
<InnerLabelField
|
|
259
|
-
label="Email Address"
|
|
260
|
-
type="email"
|
|
261
|
-
value={formData.email}
|
|
262
|
-
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
263
|
-
required
|
|
264
|
-
/>
|
|
265
|
-
|
|
266
|
-
<InnerLabelField
|
|
267
|
-
label="Subject"
|
|
268
|
-
value={formData.subject}
|
|
269
|
-
onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
|
|
270
|
-
required
|
|
271
|
-
/>
|
|
272
|
-
|
|
273
|
-
<InnerLabelField
|
|
274
|
-
label="Phone Number"
|
|
275
|
-
type="tel"
|
|
276
|
-
value={formData.phone}
|
|
277
|
-
onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
|
278
|
-
/>
|
|
279
|
-
|
|
280
|
-
<Button type="submit" className="w-full">
|
|
281
|
-
Send Message
|
|
282
|
-
</Button>
|
|
283
|
-
</form>
|
|
284
|
-
)
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Different Sizes
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
292
|
-
|
|
293
|
-
function Sizes() {
|
|
294
|
-
return (
|
|
295
|
-
<div className="space-y-4">
|
|
296
|
-
<InnerLabelField
|
|
297
|
-
label="Small Input"
|
|
298
|
-
size="S"
|
|
299
|
-
/>
|
|
300
|
-
|
|
301
|
-
<InnerLabelField
|
|
302
|
-
label="Medium Input"
|
|
303
|
-
size="M"
|
|
304
|
-
/>
|
|
305
|
-
</div>
|
|
306
|
-
)
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Style Variants
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
314
|
-
|
|
315
|
-
function Variants() {
|
|
316
|
-
return (
|
|
317
|
-
<div className="space-y-4">
|
|
318
|
-
<InnerLabelField
|
|
319
|
-
label="Presentation Style"
|
|
320
|
-
variant="PresentationStyle"
|
|
321
|
-
/>
|
|
322
|
-
|
|
323
|
-
<div className="bg-gray-900 p-4 rounded">
|
|
324
|
-
<InnerLabelField
|
|
325
|
-
label="System Style"
|
|
326
|
-
variant="SystemStyle"
|
|
327
|
-
theme="dark"
|
|
328
|
-
/>
|
|
329
|
-
</div>
|
|
330
|
-
</div>
|
|
331
|
-
)
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
### With Popover Autocomplete
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
339
|
-
import { useState } from 'react'
|
|
340
|
-
|
|
341
|
-
function AutocompleteField() {
|
|
342
|
-
const [value, setValue] = useState('')
|
|
343
|
-
const [showSuggestions, setShowSuggestions] = useState(false)
|
|
344
|
-
|
|
345
|
-
const suggestions = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']
|
|
346
|
-
.filter(item => item.toLowerCase().includes(value.toLowerCase()))
|
|
347
|
-
|
|
348
|
-
return (
|
|
349
|
-
<InnerLabelField
|
|
350
|
-
label="Search Fruits"
|
|
351
|
-
value={value}
|
|
352
|
-
onChange={(e) => {
|
|
353
|
-
setValue(e.target.value)
|
|
354
|
-
setShowSuggestions(true)
|
|
355
|
-
}}
|
|
356
|
-
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
|
|
357
|
-
popoverChildren={
|
|
358
|
-
showSuggestions && suggestions.length > 0 && (
|
|
359
|
-
<div className="p-2 space-y-1">
|
|
360
|
-
{suggestions.map((suggestion) => (
|
|
361
|
-
<div
|
|
362
|
-
key={suggestion}
|
|
363
|
-
className="p-2 hover:bg-gray-100 cursor-pointer rounded"
|
|
364
|
-
onMouseDown={() => {
|
|
365
|
-
setValue(suggestion)
|
|
366
|
-
setShowSuggestions(false)
|
|
367
|
-
}}
|
|
368
|
-
>
|
|
369
|
-
{suggestion}
|
|
370
|
-
</div>
|
|
371
|
-
))}
|
|
372
|
-
</div>
|
|
373
|
-
)
|
|
374
|
-
}
|
|
375
|
-
/>
|
|
376
|
-
)
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Disabled State
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
384
|
-
|
|
385
|
-
function DisabledField() {
|
|
386
|
-
return (
|
|
387
|
-
<div className="space-y-4">
|
|
388
|
-
<InnerLabelField
|
|
389
|
-
label="Disabled Empty"
|
|
390
|
-
disabled
|
|
391
|
-
/>
|
|
392
|
-
|
|
393
|
-
<InnerLabelField
|
|
394
|
-
label="Disabled with Value"
|
|
395
|
-
value="Cannot edit this"
|
|
396
|
-
disabled
|
|
397
|
-
/>
|
|
398
|
-
</div>
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
## API Reference
|
|
404
|
-
|
|
405
|
-
### InnerLabelField Props
|
|
406
|
-
|
|
407
|
-
Extends all InputField props with added label animation.
|
|
408
|
-
|
|
409
|
-
| Prop | Type | Default | Description |
|
|
410
|
-
|------|------|---------|-------------|
|
|
411
|
-
| `label` | `string` | - | Label text (animates on focus) |
|
|
412
|
-
| `required` | `boolean` | `false` | Shows required asterisk |
|
|
413
|
-
| `size` | `'S' \| 'M'` | `'M'` | Component size |
|
|
414
|
-
| `variant` | `'SystemStyle' \| 'PresentationStyle'` | `'PresentationStyle'` | Style variant |
|
|
415
|
-
| `theme` | `'light' \| 'dark' \| 'default'` | - | Theme variant |
|
|
416
|
-
| `childrenSide` | `ReactNode` | - | Right-side content (action button) |
|
|
417
|
-
| `popoverChildren` | `ReactNode` | - | Dropdown content |
|
|
418
|
-
| `errorMessage` | `string` | - | Error message (shows tooltip) |
|
|
419
|
-
| `onTable` | `boolean` | `false` | Special styling for table context |
|
|
420
|
-
| `toolTipSide` | `ToolTipSide` | - | Tooltip position |
|
|
421
|
-
| ...InputHTMLAttributes | - | - | All input attributes |
|
|
422
|
-
|
|
423
|
-
### Label Animation States
|
|
424
|
-
|
|
425
|
-
| State | Label Position | Label Size | Trigger |
|
|
426
|
-
|-------|---------------|------------|---------|
|
|
427
|
-
| Empty & Unfocused | Inside input | Normal (body-small) | Default |
|
|
428
|
-
| Focused (Empty) | Inside input | Normal (body-small) | User focus |
|
|
429
|
-
| Has Value | Inside input | Small (labels-small) | Value exists |
|
|
430
|
-
| Disabled (Empty) | Inside input | Normal (body-small) | Disabled prop |
|
|
431
|
-
|
|
432
|
-
## TypeScript
|
|
433
|
-
|
|
434
|
-
### Full Type Definitions
|
|
435
|
-
|
|
436
|
-
```typescript
|
|
437
|
-
import { InputHTMLAttributes, ReactNode } from 'react'
|
|
438
|
-
|
|
439
|
-
interface InnerLabelFieldProps
|
|
440
|
-
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'variant'> {
|
|
441
|
-
size?: 'S' | 'M'
|
|
442
|
-
variant?: 'SystemStyle' | 'PresentationStyle'
|
|
443
|
-
childrenSide?: ReactNode
|
|
444
|
-
popoverChildren?: ReactNode
|
|
445
|
-
errorMessage?: string
|
|
446
|
-
onTable?: boolean
|
|
447
|
-
label?: string
|
|
448
|
-
required?: boolean
|
|
449
|
-
toolTipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
450
|
-
theme?: 'light' | 'dark' | 'default'
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
export const InnerLabelField: React.ForwardRefExoticComponent<
|
|
454
|
-
InnerLabelFieldProps & React.RefAttributes<HTMLInputElement>
|
|
455
|
-
>
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Usage with Types
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
462
|
-
import { useRef, useState } from 'react'
|
|
463
|
-
|
|
464
|
-
function TypedExample() {
|
|
465
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
466
|
-
const [value, setValue] = useState<string>('')
|
|
467
|
-
|
|
468
|
-
const focusInput = () => {
|
|
469
|
-
inputRef.current?.focus()
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return (
|
|
473
|
-
<InnerLabelField
|
|
474
|
-
ref={inputRef}
|
|
475
|
-
label="Typed Input"
|
|
476
|
-
value={value}
|
|
477
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
478
|
-
setValue(e.target.value)
|
|
479
|
-
}}
|
|
480
|
-
required
|
|
481
|
-
/>
|
|
482
|
-
)
|
|
483
|
-
}
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
## Common Patterns
|
|
487
|
-
|
|
488
|
-
### Controlled Form with Validation
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
492
|
-
import { Button } from "@/components/Button";
|
|
493
|
-
import { useState } from 'react'
|
|
494
|
-
|
|
495
|
-
function ValidatedForm() {
|
|
496
|
-
const [formData, setFormData] = useState({
|
|
497
|
-
email: '',
|
|
498
|
-
password: '',
|
|
499
|
-
confirmPassword: '',
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
503
|
-
const [touched, setTouched] = useState<Record<string, boolean>>({})
|
|
504
|
-
|
|
505
|
-
const validate = (field: string, value: string) => {
|
|
506
|
-
let error = ''
|
|
507
|
-
|
|
508
|
-
switch (field) {
|
|
509
|
-
case 'email':
|
|
510
|
-
if (!value) error = 'Email is required'
|
|
511
|
-
else if (!value.includes('@')) error = 'Invalid email'
|
|
512
|
-
break
|
|
513
|
-
case 'password':
|
|
514
|
-
if (!value) error = 'Password is required'
|
|
515
|
-
else if (value.length < 8) error = 'Minimum 8 characters'
|
|
516
|
-
break
|
|
517
|
-
case 'confirmPassword':
|
|
518
|
-
if (value !== formData.password) error = 'Passwords must match'
|
|
519
|
-
break
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return error
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const handleBlur = (field: string) => {
|
|
526
|
-
setTouched({ ...touched, [field]: true })
|
|
527
|
-
const error = validate(field, formData[field as keyof typeof formData])
|
|
528
|
-
setErrors({ ...errors, [field]: error })
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const handleChange = (field: string, value: string) => {
|
|
532
|
-
setFormData({ ...formData, [field]: value })
|
|
533
|
-
if (touched[field]) {
|
|
534
|
-
const error = validate(field, value)
|
|
535
|
-
setErrors({ ...errors, [field]: error })
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
return (
|
|
540
|
-
<form className="space-y-4">
|
|
541
|
-
<InnerLabelField
|
|
542
|
-
label="Email"
|
|
543
|
-
type="email"
|
|
544
|
-
value={formData.email}
|
|
545
|
-
onChange={(e) => handleChange('email', e.target.value)}
|
|
546
|
-
onBlur={() => handleBlur('email')}
|
|
547
|
-
errorMessage={touched.email ? errors.email : undefined}
|
|
548
|
-
required
|
|
549
|
-
/>
|
|
550
|
-
|
|
551
|
-
<InnerLabelField
|
|
552
|
-
label="Password"
|
|
553
|
-
type="password"
|
|
554
|
-
value={formData.password}
|
|
555
|
-
onChange={(e) => handleChange('password', e.target.value)}
|
|
556
|
-
onBlur={() => handleBlur('password')}
|
|
557
|
-
errorMessage={touched.password ? errors.password : undefined}
|
|
558
|
-
required
|
|
559
|
-
/>
|
|
560
|
-
|
|
561
|
-
<InnerLabelField
|
|
562
|
-
label="Confirm Password"
|
|
563
|
-
type="password"
|
|
564
|
-
value={formData.confirmPassword}
|
|
565
|
-
onChange={(e) => handleChange('confirmPassword', e.target.value)}
|
|
566
|
-
onBlur={() => handleBlur('confirmPassword')}
|
|
567
|
-
errorMessage={touched.confirmPassword ? errors.confirmPassword : undefined}
|
|
568
|
-
required
|
|
569
|
-
/>
|
|
570
|
-
|
|
571
|
-
<Button type="submit">Register</Button>
|
|
572
|
-
</form>
|
|
573
|
-
)
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### Real-time Search
|
|
578
|
-
|
|
579
|
-
```typescript
|
|
580
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
581
|
-
import { ActionButton } from "@/components/ActionButton";
|
|
582
|
-
import { useState, useEffect } from 'react'
|
|
583
|
-
|
|
584
|
-
function SearchField() {
|
|
585
|
-
const [query, setQuery] = useState('')
|
|
586
|
-
const [results, setResults] = useState<string[]>([])
|
|
587
|
-
const [loading, setLoading] = useState(false)
|
|
588
|
-
|
|
589
|
-
useEffect(() => {
|
|
590
|
-
if (!query) {
|
|
591
|
-
setResults([])
|
|
592
|
-
return
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
setLoading(true)
|
|
596
|
-
const timer = setTimeout(() => {
|
|
597
|
-
// Simulate API call
|
|
598
|
-
setResults([`Result for "${query}" 1`, `Result for "${query}" 2`])
|
|
599
|
-
setLoading(false)
|
|
600
|
-
}, 500)
|
|
601
|
-
|
|
602
|
-
return () => clearTimeout(timer)
|
|
603
|
-
}, [query])
|
|
604
|
-
|
|
605
|
-
return (
|
|
606
|
-
<div>
|
|
607
|
-
<InnerLabelField
|
|
608
|
-
label="Search"
|
|
609
|
-
value={query}
|
|
610
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
611
|
-
childrenSide={
|
|
612
|
-
query && (
|
|
613
|
-
<ActionButton onClick={() => setQuery('')}>
|
|
614
|
-
<i className="ri-close-line" />
|
|
615
|
-
</ActionButton>
|
|
616
|
-
)
|
|
617
|
-
}
|
|
618
|
-
/>
|
|
619
|
-
|
|
620
|
-
{loading && <p className="mt-2 text-sm">Searching...</p>}
|
|
621
|
-
|
|
622
|
-
{results.length > 0 && (
|
|
623
|
-
<div className="mt-2 space-y-1">
|
|
624
|
-
{results.map((result, index) => (
|
|
625
|
-
<div key={index} className="p-2 bg-gray-50 rounded">
|
|
626
|
-
{result}
|
|
627
|
-
</div>
|
|
628
|
-
))}
|
|
629
|
-
</div>
|
|
630
|
-
)}
|
|
631
|
-
</div>
|
|
632
|
-
)
|
|
633
|
-
}
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
## Testing
|
|
637
|
-
|
|
638
|
-
### Unit Test Examples
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
642
|
-
import { InnerLabelField } from "@/components/InnerLabelField";
|
|
643
|
-
import userEvent from '@testing-library/user-event'
|
|
644
|
-
|
|
645
|
-
describe('InnerLabelField', () => {
|
|
646
|
-
it('renders label inside input', () => {
|
|
647
|
-
render(<InnerLabelField label="Email" />)
|
|
648
|
-
|
|
649
|
-
expect(screen.getByText('Email')).toBeInTheDocument()
|
|
650
|
-
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
|
651
|
-
})
|
|
652
|
-
|
|
653
|
-
it('shows required asterisk', () => {
|
|
654
|
-
render(<InnerLabelField label="Name" required />)
|
|
655
|
-
|
|
656
|
-
expect(screen.getByText('*')).toBeInTheDocument()
|
|
657
|
-
})
|
|
658
|
-
|
|
659
|
-
it('animates label on focus', async () => {
|
|
660
|
-
const { container } = render(<InnerLabelField label="Email" />)
|
|
661
|
-
|
|
662
|
-
const input = screen.getByRole('textbox')
|
|
663
|
-
const label = screen.getByText('Email')
|
|
664
|
-
|
|
665
|
-
// Initial state
|
|
666
|
-
expect(label).toHaveClass('typography-body-small-regular')
|
|
667
|
-
|
|
668
|
-
// Focus input
|
|
669
|
-
await userEvent.click(input)
|
|
670
|
-
|
|
671
|
-
// Label should remain same size when empty and focused
|
|
672
|
-
expect(label).toHaveClass('typography-body-small-regular')
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
it('changes label size when value exists', async () => {
|
|
676
|
-
render(<InnerLabelField label="Email" />)
|
|
677
|
-
|
|
678
|
-
const input = screen.getByRole('textbox')
|
|
679
|
-
await userEvent.type(input, 'test@example.com')
|
|
680
|
-
|
|
681
|
-
// Label should shrink when value exists
|
|
682
|
-
const label = screen.getByText('Email')
|
|
683
|
-
expect(label).toHaveClass('typography-labels-small-regular')
|
|
684
|
-
})
|
|
685
|
-
|
|
686
|
-
it('handles onChange', async () => {
|
|
687
|
-
const handleChange = jest.fn()
|
|
688
|
-
|
|
689
|
-
render(
|
|
690
|
-
<InnerLabelField
|
|
691
|
-
label="Email"
|
|
692
|
-
onChange={handleChange}
|
|
693
|
-
/>
|
|
694
|
-
)
|
|
695
|
-
|
|
696
|
-
const input = screen.getByRole('textbox')
|
|
697
|
-
await userEvent.type(input, 'test')
|
|
698
|
-
|
|
699
|
-
expect(handleChange).toHaveBeenCalled()
|
|
700
|
-
})
|
|
701
|
-
|
|
702
|
-
it('shows error message', () => {
|
|
703
|
-
render(
|
|
704
|
-
<InnerLabelField
|
|
705
|
-
label="Email"
|
|
706
|
-
errorMessage="Invalid email"
|
|
707
|
-
/>
|
|
708
|
-
)
|
|
709
|
-
|
|
710
|
-
expect(screen.getByRole('textbox')).toHaveAttribute('aria-invalid', 'true')
|
|
711
|
-
})
|
|
712
|
-
|
|
713
|
-
it('handles disabled state', () => {
|
|
714
|
-
render(<InnerLabelField label="Disabled" disabled />)
|
|
715
|
-
|
|
716
|
-
const input = screen.getByRole('textbox')
|
|
717
|
-
expect(input).toBeDisabled()
|
|
718
|
-
})
|
|
719
|
-
})
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
## Accessibility
|
|
723
|
-
|
|
724
|
-
- **Label Association**: Label properly linked to input
|
|
725
|
-
- **Keyboard Navigation**: Full keyboard support
|
|
726
|
-
- **Screen Readers**: Label announced on focus
|
|
727
|
-
- **Required Indication**: Visual asterisk and aria-required
|
|
728
|
-
- **Error States**: aria-invalid when error present
|
|
729
|
-
- **Focus Indicators**: Clear focus states
|
|
730
|
-
|
|
731
|
-
### Accessibility Best Practices
|
|
732
|
-
|
|
733
|
-
```typescript
|
|
734
|
-
// Always provide labels
|
|
735
|
-
<InnerLabelField label="Email" /> // ✅
|
|
736
|
-
|
|
737
|
-
// Mark required fields
|
|
738
|
-
<InnerLabelField
|
|
739
|
-
label="Password"
|
|
740
|
-
required
|
|
741
|
-
aria-required="true"
|
|
742
|
-
/>
|
|
743
|
-
|
|
744
|
-
// Provide error context
|
|
745
|
-
<InnerLabelField
|
|
746
|
-
label="Email"
|
|
747
|
-
errorMessage="Invalid email"
|
|
748
|
-
aria-invalid="true"
|
|
749
|
-
/>
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
## Performance
|
|
753
|
-
|
|
754
|
-
| Metric | Value |
|
|
755
|
-
|--------|-------|
|
|
756
|
-
| Bundle size (minified) | ~5kb |
|
|
757
|
-
| Bundle size (gzipped) | ~2kb |
|
|
758
|
-
| Dependencies | InputField |
|
|
759
|
-
| Animation performance | 60fps (CSS transitions) |
|
|
760
|
-
| Tree-shakeable | ✅ |
|
|
761
|
-
|
|
762
|
-
### Performance Tips
|
|
763
|
-
|
|
764
|
-
1. **Use controlled sparingly**: Uncontrolled for simple forms
|
|
765
|
-
2. **Memoize handlers**: Prevent re-renders
|
|
766
|
-
3. **Debounce validation**: Avoid excessive checks
|
|
767
|
-
|
|
768
|
-
## Styling
|
|
769
|
-
|
|
770
|
-
### Animation Details
|
|
771
|
-
|
|
772
|
-
The label animation uses CSS transitions:
|
|
773
|
-
- Duration: 300ms
|
|
774
|
-
- Timing: ease-in-out
|
|
775
|
-
- Properties: font-size, color
|
|
776
|
-
|
|
777
|
-
### Custom Styles
|
|
778
|
-
|
|
779
|
-
```typescript
|
|
780
|
-
// Custom input styling
|
|
781
|
-
<InnerLabelField
|
|
782
|
-
label="Custom"
|
|
783
|
-
className="border-2 border-blue-500"
|
|
784
|
-
/>
|
|
785
|
-
|
|
786
|
-
// Override animation
|
|
787
|
-
<InnerLabelField
|
|
788
|
-
label="Fast Animation"
|
|
789
|
-
className="[&_p]:transition-[font-size]_[&_p]:duration-150"
|
|
790
|
-
/>
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
## Best Practices
|
|
794
|
-
|
|
795
|
-
1. **Use for modern forms**: Great for clean, minimal designs
|
|
796
|
-
2. **Always provide label**: Required for accessibility
|
|
797
|
-
3. **Mark required fields**: Use required prop
|
|
798
|
-
4. **Validate on blur**: Better UX than real-time
|
|
799
|
-
5. **Keep labels short**: Long labels may truncate
|
|
800
|
-
6. **Test on mobile**: Ensure touch targets are adequate
|
|
801
|
-
7. **Provide error feedback**: Use errorMessage prop
|
|
802
|
-
|
|
803
|
-
## Comparison with LabelField
|
|
804
|
-
|
|
805
|
-
| Feature | InnerLabelField | LabelField |
|
|
806
|
-
|---------|----------------|------------|
|
|
807
|
-
| Label position | Inside input | Above input |
|
|
808
|
-
| Animation | Animated | Static |
|
|
809
|
-
| Space efficiency | More compact | More spacious |
|
|
810
|
-
| Best for | Modern, minimal | Traditional forms |
|
|
811
|
-
| Secondary label | ❌ | ✅ |
|
|
812
|
-
| Label types | Single | Primary + Secondary + Required |
|
|
813
|
-
|
|
814
|
-
## Related Components
|
|
815
|
-
|
|
816
|
-
- [LabelField](./label-field.md) - Traditional label above input
|
|
817
|
-
- [Label](./label.md) - Standalone label component
|
|
818
|
-
- [InputField](./input-field.md) - Base input component
|
|
819
|
-
- [FieldHint](./field-hint.md) - Inline field hints
|