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,756 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: InputOTP
|
|
3
|
-
version: 2.4.5
|
|
4
|
-
status: stable
|
|
5
|
-
category: components/forms
|
|
6
|
-
tags: [form, otp, input, verification, authentication, accessible]
|
|
7
|
-
last-reviewed: 2024-11-05
|
|
8
|
-
bundle-size: 2.6kb
|
|
9
|
-
dependencies:
|
|
10
|
-
- "input-otp": "^1.0.0"
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# InputOTP
|
|
14
|
-
|
|
15
|
-
> A fully accessible OTP (One-Time Password) input component built on the input-otp library. Features individual digit slots, automatic focus management, and support for various OTP formats. Perfect for verification codes, 2FA, and PIN inputs.
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
20
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npx torch-glare@latest init
|
|
24
|
-
npx torch-glare@latest add InputOTP
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
`add` also copies any components, hooks, and utilities that `InputOTP` depends on.
|
|
28
|
-
|
|
29
|
-
## Import
|
|
30
|
-
|
|
31
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
import { InputOTP } from "@/components/InputOTP";
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Quick Examples
|
|
38
|
-
|
|
39
|
-
### Basic 6-Digit OTP
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/InputOTP'
|
|
43
|
-
|
|
44
|
-
function Example() {
|
|
45
|
-
const [otp, setOtp] = useState('')
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<InputOTP maxLength={6} value={otp} onChange={setOtp}>
|
|
49
|
-
<InputOTPGroup>
|
|
50
|
-
<InputOTPSlot index={0} />
|
|
51
|
-
<InputOTPSlot index={1} />
|
|
52
|
-
<InputOTPSlot index={2} />
|
|
53
|
-
<InputOTPSlot index={3} />
|
|
54
|
-
<InputOTPSlot index={4} />
|
|
55
|
-
<InputOTPSlot index={5} />
|
|
56
|
-
</InputOTPGroup>
|
|
57
|
-
</InputOTP>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### With Separator
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
function OTPWithSeparator() {
|
|
66
|
-
const [code, setCode] = useState('')
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<InputOTP maxLength={6} value={code} onChange={setCode}>
|
|
70
|
-
<InputOTPGroup>
|
|
71
|
-
<InputOTPSlot index={0} />
|
|
72
|
-
<InputOTPSlot index={1} />
|
|
73
|
-
<InputOTPSlot index={2} />
|
|
74
|
-
</InputOTPGroup>
|
|
75
|
-
<InputOTPSeparator />
|
|
76
|
-
<InputOTPGroup>
|
|
77
|
-
<InputOTPSlot index={3} />
|
|
78
|
-
<InputOTPSlot index={4} />
|
|
79
|
-
<InputOTPSlot index={5} />
|
|
80
|
-
</InputOTPGroup>
|
|
81
|
-
</InputOTP>
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 4-Digit PIN
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
function PINInput() {
|
|
90
|
-
const [pin, setPIN] = useState('')
|
|
91
|
-
const [error, setError] = useState('')
|
|
92
|
-
|
|
93
|
-
const handleComplete = (value: string) => {
|
|
94
|
-
if (value.length === 4) {
|
|
95
|
-
// Validate PIN
|
|
96
|
-
if (value === '1234') {
|
|
97
|
-
console.log('PIN correct!')
|
|
98
|
-
} else {
|
|
99
|
-
setError('Incorrect PIN')
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
handleComplete(pin)
|
|
106
|
-
}, [pin])
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div className="space-y-2">
|
|
110
|
-
<label>Enter 4-digit PIN</label>
|
|
111
|
-
<InputOTP
|
|
112
|
-
maxLength={4}
|
|
113
|
-
value={pin}
|
|
114
|
-
onChange={(value) => {
|
|
115
|
-
setPIN(value)
|
|
116
|
-
setError('')
|
|
117
|
-
}}
|
|
118
|
-
pattern={REGEXP_ONLY_DIGITS}
|
|
119
|
-
>
|
|
120
|
-
<InputOTPGroup>
|
|
121
|
-
<InputOTPSlot index={0} />
|
|
122
|
-
<InputOTPSlot index={1} />
|
|
123
|
-
<InputOTPSlot index={2} />
|
|
124
|
-
<InputOTPSlot index={3} />
|
|
125
|
-
</InputOTPGroup>
|
|
126
|
-
</InputOTP>
|
|
127
|
-
{error && <p className="text-red-500 text-sm">{error}</p>}
|
|
128
|
-
</div>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### SMS Verification
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
function SMSVerification() {
|
|
137
|
-
const [code, setCode] = useState('')
|
|
138
|
-
const [isVerifying, setIsVerifying] = useState(false)
|
|
139
|
-
const [isVerified, setIsVerified] = useState(false)
|
|
140
|
-
|
|
141
|
-
const handleVerify = async () => {
|
|
142
|
-
if (code.length === 6) {
|
|
143
|
-
setIsVerifying(true)
|
|
144
|
-
try {
|
|
145
|
-
await verifyCode(code)
|
|
146
|
-
setIsVerified(true)
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error('Verification failed')
|
|
149
|
-
} finally {
|
|
150
|
-
setIsVerifying(false)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<div className="space-y-4">
|
|
157
|
-
<p>Enter the 6-digit code sent to your phone</p>
|
|
158
|
-
|
|
159
|
-
<InputOTP
|
|
160
|
-
maxLength={6}
|
|
161
|
-
value={code}
|
|
162
|
-
onChange={setCode}
|
|
163
|
-
onComplete={handleVerify}
|
|
164
|
-
disabled={isVerifying || isVerified}
|
|
165
|
-
>
|
|
166
|
-
<InputOTPGroup>
|
|
167
|
-
{[0, 1, 2, 3, 4, 5].map(index => (
|
|
168
|
-
<InputOTPSlot key={index} index={index} />
|
|
169
|
-
))}
|
|
170
|
-
</InputOTPGroup>
|
|
171
|
-
</InputOTP>
|
|
172
|
-
|
|
173
|
-
{isVerifying && <p>Verifying...</p>}
|
|
174
|
-
{isVerified && <p className="text-green-500">✓ Verified successfully!</p>}
|
|
175
|
-
|
|
176
|
-
<button
|
|
177
|
-
onClick={handleVerify}
|
|
178
|
-
disabled={code.length !== 6 || isVerifying || isVerified}
|
|
179
|
-
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
|
|
180
|
-
>
|
|
181
|
-
Verify Code
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Two-Factor Authentication
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
function TwoFactorAuth() {
|
|
192
|
-
const [authCode, setAuthCode] = useState('')
|
|
193
|
-
const [showBackupOption, setShowBackupOption] = useState(false)
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<div className="space-y-4">
|
|
197
|
-
<h3 className="text-lg font-semibold">Two-Factor Authentication</h3>
|
|
198
|
-
|
|
199
|
-
<p className="text-sm text-gray-600">
|
|
200
|
-
Enter the 6-digit code from your authenticator app
|
|
201
|
-
</p>
|
|
202
|
-
|
|
203
|
-
<InputOTP
|
|
204
|
-
maxLength={6}
|
|
205
|
-
value={authCode}
|
|
206
|
-
onChange={setAuthCode}
|
|
207
|
-
pattern={REGEXP_ONLY_DIGITS}
|
|
208
|
-
>
|
|
209
|
-
<InputOTPGroup>
|
|
210
|
-
<InputOTPSlot index={0} />
|
|
211
|
-
<InputOTPSlot index={1} />
|
|
212
|
-
<InputOTPSlot index={2} />
|
|
213
|
-
</InputOTPGroup>
|
|
214
|
-
<InputOTPSeparator />
|
|
215
|
-
<InputOTPGroup>
|
|
216
|
-
<InputOTPSlot index={3} />
|
|
217
|
-
<InputOTPSlot index={4} />
|
|
218
|
-
<InputOTPSlot index={5} />
|
|
219
|
-
</InputOTPGroup>
|
|
220
|
-
</InputOTP>
|
|
221
|
-
|
|
222
|
-
<div className="flex justify-between items-center">
|
|
223
|
-
<button
|
|
224
|
-
onClick={() => setShowBackupOption(!showBackupOption)}
|
|
225
|
-
className="text-sm text-blue-500 hover:underline"
|
|
226
|
-
>
|
|
227
|
-
Use backup code instead
|
|
228
|
-
</button>
|
|
229
|
-
|
|
230
|
-
<button
|
|
231
|
-
disabled={authCode.length !== 6}
|
|
232
|
-
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
|
|
233
|
-
>
|
|
234
|
-
Verify
|
|
235
|
-
</button>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
{showBackupOption && (
|
|
239
|
-
<input
|
|
240
|
-
type="text"
|
|
241
|
-
placeholder="Enter backup code"
|
|
242
|
-
className="w-full p-2 border rounded"
|
|
243
|
-
/>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Custom Pattern (Alphanumeric)
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
function AlphanumericCode() {
|
|
254
|
-
const [code, setCode] = useState('')
|
|
255
|
-
|
|
256
|
-
return (
|
|
257
|
-
<div>
|
|
258
|
-
<label>Enter verification code (letters and numbers)</label>
|
|
259
|
-
<InputOTP
|
|
260
|
-
maxLength={6}
|
|
261
|
-
value={code}
|
|
262
|
-
onChange={setCode}
|
|
263
|
-
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
|
264
|
-
className="mt-2"
|
|
265
|
-
>
|
|
266
|
-
<InputOTPGroup>
|
|
267
|
-
{[0, 1, 2, 3, 4, 5].map(index => (
|
|
268
|
-
<InputOTPSlot
|
|
269
|
-
key={index}
|
|
270
|
-
index={index}
|
|
271
|
-
className="uppercase"
|
|
272
|
-
/>
|
|
273
|
-
))}
|
|
274
|
-
</InputOTPGroup>
|
|
275
|
-
</InputOTP>
|
|
276
|
-
<p className="text-xs text-gray-500 mt-1">
|
|
277
|
-
Code: {code.toUpperCase()}
|
|
278
|
-
</p>
|
|
279
|
-
</div>
|
|
280
|
-
)
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Resend Code Timer
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
function OTPWithResend() {
|
|
288
|
-
const [otp, setOtp] = useState('')
|
|
289
|
-
const [timer, setTimer] = useState(30)
|
|
290
|
-
const [canResend, setCanResend] = useState(false)
|
|
291
|
-
|
|
292
|
-
useEffect(() => {
|
|
293
|
-
if (timer > 0) {
|
|
294
|
-
const interval = setInterval(() => {
|
|
295
|
-
setTimer(t => t - 1)
|
|
296
|
-
}, 1000)
|
|
297
|
-
return () => clearInterval(interval)
|
|
298
|
-
} else {
|
|
299
|
-
setCanResend(true)
|
|
300
|
-
}
|
|
301
|
-
}, [timer])
|
|
302
|
-
|
|
303
|
-
const handleResend = () => {
|
|
304
|
-
setOtp('')
|
|
305
|
-
setTimer(30)
|
|
306
|
-
setCanResend(false)
|
|
307
|
-
// Trigger resend API call
|
|
308
|
-
console.log('Resending code...')
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return (
|
|
312
|
-
<div className="space-y-4">
|
|
313
|
-
<InputOTP maxLength={6} value={otp} onChange={setOtp}>
|
|
314
|
-
<InputOTPGroup>
|
|
315
|
-
{[0, 1, 2, 3, 4, 5].map(index => (
|
|
316
|
-
<InputOTPSlot key={index} index={index} />
|
|
317
|
-
))}
|
|
318
|
-
</InputOTPGroup>
|
|
319
|
-
</InputOTP>
|
|
320
|
-
|
|
321
|
-
<div className="text-center">
|
|
322
|
-
{canResend ? (
|
|
323
|
-
<button
|
|
324
|
-
onClick={handleResend}
|
|
325
|
-
className="text-blue-500 hover:underline"
|
|
326
|
-
>
|
|
327
|
-
Resend code
|
|
328
|
-
</button>
|
|
329
|
-
) : (
|
|
330
|
-
<p className="text-gray-500">
|
|
331
|
-
Resend code in {timer}s
|
|
332
|
-
</p>
|
|
333
|
-
)}
|
|
334
|
-
</div>
|
|
335
|
-
</div>
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
## API Reference
|
|
341
|
-
|
|
342
|
-
### InputOTP Props
|
|
343
|
-
|
|
344
|
-
| Prop | Type | Default | Description |
|
|
345
|
-
|------|------|---------|-------------|
|
|
346
|
-
| `maxLength` | `number` | - | Maximum number of characters |
|
|
347
|
-
| `value` | `string` | - | Controlled value |
|
|
348
|
-
| `onChange` | `(value: string) => void` | - | Change handler |
|
|
349
|
-
| `onComplete` | `(value: string) => void` | - | Called when all slots filled |
|
|
350
|
-
| `pattern` | `RegExp` | - | Validation pattern |
|
|
351
|
-
| `disabled` | `boolean` | `false` | Disables input |
|
|
352
|
-
| `autoFocus` | `boolean` | `false` | Focus first slot on mount |
|
|
353
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
354
|
-
| `containerClassName` | `string` | - | Container CSS classes |
|
|
355
|
-
|
|
356
|
-
### InputOTPGroup Props
|
|
357
|
-
|
|
358
|
-
| Prop | Type | Default | Description |
|
|
359
|
-
|------|------|---------|-------------|
|
|
360
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
361
|
-
| `children` | `ReactNode` | - | OTP slots |
|
|
362
|
-
|
|
363
|
-
### InputOTPSlot Props
|
|
364
|
-
|
|
365
|
-
| Prop | Type | Default | Description |
|
|
366
|
-
|------|------|---------|-------------|
|
|
367
|
-
| `index` | `number` | - | Slot index (required) |
|
|
368
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
369
|
-
|
|
370
|
-
### InputOTPSeparator Props
|
|
371
|
-
|
|
372
|
-
| Prop | Type | Default | Description |
|
|
373
|
-
|------|------|---------|-------------|
|
|
374
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
375
|
-
|
|
376
|
-
### Common Patterns
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
// Digits only
|
|
380
|
-
import { REGEXP_ONLY_DIGITS } from 'input-otp'
|
|
381
|
-
|
|
382
|
-
// Digits and letters
|
|
383
|
-
import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp'
|
|
384
|
-
|
|
385
|
-
// Custom pattern
|
|
386
|
-
const REGEXP_CUSTOM = /^[A-Z0-9]$/
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### TypeScript
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
interface InputOTPProps {
|
|
393
|
-
maxLength: number
|
|
394
|
-
value: string
|
|
395
|
-
onChange: (value: string) => void
|
|
396
|
-
onComplete?: (value: string) => void
|
|
397
|
-
pattern?: RegExp
|
|
398
|
-
disabled?: boolean
|
|
399
|
-
autoFocus?: boolean
|
|
400
|
-
className?: string
|
|
401
|
-
containerClassName?: string
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
interface InputOTPSlotProps {
|
|
405
|
-
index: number
|
|
406
|
-
className?: string
|
|
407
|
-
}
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
## Common Patterns
|
|
411
|
-
|
|
412
|
-
### Email Verification
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
function EmailVerification() {
|
|
416
|
-
const [step, setStep] = useState<'email' | 'verify'>('email')
|
|
417
|
-
const [email, setEmail] = useState('')
|
|
418
|
-
const [code, setCode] = useState('')
|
|
419
|
-
|
|
420
|
-
const sendCode = async () => {
|
|
421
|
-
await sendVerificationEmail(email)
|
|
422
|
-
setStep('verify')
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const verifyEmail = async () => {
|
|
426
|
-
const isValid = await verifyEmailCode(email, code)
|
|
427
|
-
if (isValid) {
|
|
428
|
-
// Email verified
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (step === 'email') {
|
|
433
|
-
return (
|
|
434
|
-
<div>
|
|
435
|
-
<input
|
|
436
|
-
type="email"
|
|
437
|
-
value={email}
|
|
438
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
439
|
-
placeholder="Enter email"
|
|
440
|
-
/>
|
|
441
|
-
<button onClick={sendCode}>Send Code</button>
|
|
442
|
-
</div>
|
|
443
|
-
)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return (
|
|
447
|
-
<div>
|
|
448
|
-
<p>Enter code sent to {email}</p>
|
|
449
|
-
<InputOTP maxLength={6} value={code} onChange={setCode}>
|
|
450
|
-
<InputOTPGroup>
|
|
451
|
-
{[0, 1, 2, 3, 4, 5].map(i => (
|
|
452
|
-
<InputOTPSlot key={i} index={i} />
|
|
453
|
-
))}
|
|
454
|
-
</InputOTPGroup>
|
|
455
|
-
</InputOTP>
|
|
456
|
-
<button onClick={verifyEmail}>Verify</button>
|
|
457
|
-
</div>
|
|
458
|
-
)
|
|
459
|
-
}
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
### Multi-Step Verification
|
|
463
|
-
|
|
464
|
-
```typescript
|
|
465
|
-
function MultiStepVerification() {
|
|
466
|
-
const [phone, setPhone] = useState('')
|
|
467
|
-
const [email, setEmail] = useState('')
|
|
468
|
-
const [phoneCode, setPhoneCode] = useState('')
|
|
469
|
-
const [emailCode, setEmailCode] = useState('')
|
|
470
|
-
const [step, setStep] = useState(1)
|
|
471
|
-
|
|
472
|
-
return (
|
|
473
|
-
<div className="space-y-6">
|
|
474
|
-
{/* Step 1: Phone Verification */}
|
|
475
|
-
{step === 1 && (
|
|
476
|
-
<div>
|
|
477
|
-
<h3>Step 1: Verify Phone</h3>
|
|
478
|
-
<InputOTP maxLength={6} value={phoneCode} onChange={setPhoneCode}>
|
|
479
|
-
<InputOTPGroup>
|
|
480
|
-
{[0, 1, 2, 3, 4, 5].map(i => (
|
|
481
|
-
<InputOTPSlot key={i} index={i} />
|
|
482
|
-
))}
|
|
483
|
-
</InputOTPGroup>
|
|
484
|
-
</InputOTP>
|
|
485
|
-
<button onClick={() => setStep(2)}>Next</button>
|
|
486
|
-
</div>
|
|
487
|
-
)}
|
|
488
|
-
|
|
489
|
-
{/* Step 2: Email Verification */}
|
|
490
|
-
{step === 2 && (
|
|
491
|
-
<div>
|
|
492
|
-
<h3>Step 2: Verify Email</h3>
|
|
493
|
-
<InputOTP maxLength={6} value={emailCode} onChange={setEmailCode}>
|
|
494
|
-
<InputOTPGroup>
|
|
495
|
-
{[0, 1, 2, 3, 4, 5].map(i => (
|
|
496
|
-
<InputOTPSlot key={i} index={i} />
|
|
497
|
-
))}
|
|
498
|
-
</InputOTPGroup>
|
|
499
|
-
</InputOTP>
|
|
500
|
-
<button onClick={() => console.log('Verified!')}>Complete</button>
|
|
501
|
-
</div>
|
|
502
|
-
)}
|
|
503
|
-
</div>
|
|
504
|
-
)
|
|
505
|
-
}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
## Testing
|
|
509
|
-
|
|
510
|
-
### Unit Test Example
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
514
|
-
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/InputOTP'
|
|
515
|
-
|
|
516
|
-
describe('InputOTP', () => {
|
|
517
|
-
it('handles input correctly', () => {
|
|
518
|
-
const handleChange = jest.fn()
|
|
519
|
-
render(
|
|
520
|
-
<InputOTP maxLength={4} onChange={handleChange}>
|
|
521
|
-
<InputOTPGroup>
|
|
522
|
-
<InputOTPSlot index={0} />
|
|
523
|
-
<InputOTPSlot index={1} />
|
|
524
|
-
<InputOTPSlot index={2} />
|
|
525
|
-
<InputOTPSlot index={3} />
|
|
526
|
-
</InputOTPGroup>
|
|
527
|
-
</InputOTP>
|
|
528
|
-
)
|
|
529
|
-
|
|
530
|
-
const input = screen.getByRole('textbox', { hidden: true })
|
|
531
|
-
fireEvent.change(input, { target: { value: '1234' } })
|
|
532
|
-
|
|
533
|
-
expect(handleChange).toHaveBeenCalledWith('1234')
|
|
534
|
-
})
|
|
535
|
-
|
|
536
|
-
it('calls onComplete when all slots filled', () => {
|
|
537
|
-
const handleComplete = jest.fn()
|
|
538
|
-
render(
|
|
539
|
-
<InputOTP
|
|
540
|
-
maxLength={4}
|
|
541
|
-
onComplete={handleComplete}
|
|
542
|
-
>
|
|
543
|
-
<InputOTPGroup>
|
|
544
|
-
{[0, 1, 2, 3].map(i => (
|
|
545
|
-
<InputOTPSlot key={i} index={i} />
|
|
546
|
-
))}
|
|
547
|
-
</InputOTPGroup>
|
|
548
|
-
</InputOTP>
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
const input = screen.getByRole('textbox', { hidden: true })
|
|
552
|
-
fireEvent.change(input, { target: { value: '1234' } })
|
|
553
|
-
|
|
554
|
-
expect(handleComplete).toHaveBeenCalledWith('1234')
|
|
555
|
-
})
|
|
556
|
-
})
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### Accessibility Test
|
|
560
|
-
|
|
561
|
-
```typescript
|
|
562
|
-
import { axe } from '@axe-core/react'
|
|
563
|
-
|
|
564
|
-
test('InputOTP meets WCAG standards', async () => {
|
|
565
|
-
const { container } = render(
|
|
566
|
-
<div>
|
|
567
|
-
<label htmlFor="otp">Enter verification code</label>
|
|
568
|
-
<InputOTP maxLength={6} id="otp">
|
|
569
|
-
<InputOTPGroup>
|
|
570
|
-
{[0, 1, 2, 3, 4, 5].map(i => (
|
|
571
|
-
<InputOTPSlot key={i} index={i} />
|
|
572
|
-
))}
|
|
573
|
-
</InputOTPGroup>
|
|
574
|
-
</InputOTP>
|
|
575
|
-
</div>
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
const results = await axe(container)
|
|
579
|
-
expect(results).toHaveNoViolations()
|
|
580
|
-
})
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
## Accessibility
|
|
584
|
-
|
|
585
|
-
### Keyboard Support
|
|
586
|
-
|
|
587
|
-
- **Arrow Keys**: Navigate between slots
|
|
588
|
-
- **Backspace**: Delete and move to previous slot
|
|
589
|
-
- **Delete**: Clear current slot
|
|
590
|
-
- **Tab**: Move focus out of component
|
|
591
|
-
- **Numbers/Letters**: Input based on pattern
|
|
592
|
-
|
|
593
|
-
### ARIA Support
|
|
594
|
-
|
|
595
|
-
- Hidden input field manages actual value
|
|
596
|
-
- Individual slots are visual only
|
|
597
|
-
- Screen readers announce the full value
|
|
598
|
-
- Proper label association
|
|
599
|
-
|
|
600
|
-
### Best Practices
|
|
601
|
-
|
|
602
|
-
```typescript
|
|
603
|
-
// Always provide a label
|
|
604
|
-
<label htmlFor="verification-code">
|
|
605
|
-
Enter the 6-digit code
|
|
606
|
-
</label>
|
|
607
|
-
<InputOTP id="verification-code" maxLength={6}>
|
|
608
|
-
{/* slots */}
|
|
609
|
-
</InputOTP>
|
|
610
|
-
|
|
611
|
-
// Provide clear instructions
|
|
612
|
-
<div role="status" aria-live="polite">
|
|
613
|
-
{code.length === 6 ? 'Code complete' : `${6 - code.length} digits remaining`}
|
|
614
|
-
</div>
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
## Styling
|
|
618
|
-
|
|
619
|
-
### Slot States
|
|
620
|
-
|
|
621
|
-
```css
|
|
622
|
-
/* Default slot */
|
|
623
|
-
.slot {
|
|
624
|
-
background: var(--bg-primary);
|
|
625
|
-
border: 1px solid var(--border-primary);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/* Active/focused slot */
|
|
629
|
-
.slot[data-active="true"] {
|
|
630
|
-
border-color: var(--focus-color);
|
|
631
|
-
box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.3);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/* Slot with value */
|
|
635
|
-
.slot:not(:empty) {
|
|
636
|
-
font-weight: 500;
|
|
637
|
-
}
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
### Custom Slot Styles
|
|
641
|
-
|
|
642
|
-
```typescript
|
|
643
|
-
<InputOTPSlot
|
|
644
|
-
index={0}
|
|
645
|
-
className="w-12 h-12 text-lg font-bold"
|
|
646
|
-
/>
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
### Theme Variants
|
|
650
|
-
|
|
651
|
-
```typescript
|
|
652
|
-
// Custom themed slots
|
|
653
|
-
<InputOTPSlot
|
|
654
|
-
index={0}
|
|
655
|
-
className="border-blue-500 focus:border-blue-600"
|
|
656
|
-
/>
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
## Performance
|
|
660
|
-
|
|
661
|
-
| Metric | Value |
|
|
662
|
-
|--------|-------|
|
|
663
|
-
| Bundle size (gzip) | 2.6kb |
|
|
664
|
-
| First render | <6ms |
|
|
665
|
-
| Input handling | <1ms |
|
|
666
|
-
| Focus transition | <2ms |
|
|
667
|
-
| Tree-shakeable | ✅ |
|
|
668
|
-
|
|
669
|
-
### Optimization Tips
|
|
670
|
-
|
|
671
|
-
1. Use React.memo for static surrounding elements
|
|
672
|
-
2. Avoid re-rendering parent on each input change
|
|
673
|
-
3. Debounce validation calls
|
|
674
|
-
4. Use onComplete for API calls instead of onChange
|
|
675
|
-
|
|
676
|
-
## Migration
|
|
677
|
-
|
|
678
|
-
### From Custom OTP Input
|
|
679
|
-
|
|
680
|
-
```diff
|
|
681
|
-
// From custom implementation
|
|
682
|
-
- <div className="flex gap-2">
|
|
683
|
-
- {[0,1,2,3].map(i => (
|
|
684
|
-
- <input
|
|
685
|
-
- key={i}
|
|
686
|
-
- maxLength={1}
|
|
687
|
-
- className="w-10 h-10"
|
|
688
|
-
- />
|
|
689
|
-
- ))}
|
|
690
|
-
- </div>
|
|
691
|
-
|
|
692
|
-
// To InputOTP
|
|
693
|
-
+ <InputOTP maxLength={4}>
|
|
694
|
-
+ <InputOTPGroup>
|
|
695
|
-
+ {[0,1,2,3].map(i => (
|
|
696
|
-
+ <InputOTPSlot key={i} index={i} />
|
|
697
|
-
+ ))}
|
|
698
|
-
+ </InputOTPGroup>
|
|
699
|
-
+ </InputOTP>
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
## Troubleshooting
|
|
703
|
-
|
|
704
|
-
### Paste not working
|
|
705
|
-
|
|
706
|
-
**Solution:** Ensure pattern allows pasted characters
|
|
707
|
-
|
|
708
|
-
```typescript
|
|
709
|
-
// Allow paste of 6 digits
|
|
710
|
-
<InputOTP
|
|
711
|
-
maxLength={6}
|
|
712
|
-
pattern={REGEXP_ONLY_DIGITS}
|
|
713
|
-
onPaste={(e) => {
|
|
714
|
-
const pasted = e.clipboardData.getData('text')
|
|
715
|
-
if (/^\d{6}$/.test(pasted)) {
|
|
716
|
-
onChange(pasted)
|
|
717
|
-
}
|
|
718
|
-
}}
|
|
719
|
-
/>
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
### Focus not moving between slots
|
|
723
|
-
|
|
724
|
-
**Solution:** This is handled automatically by input-otp
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
// Focus management is automatic
|
|
728
|
-
<InputOTP autoFocus> {/* Focus first slot */}
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
## Related Components
|
|
732
|
-
|
|
733
|
-
- [Input](/docs/components/input.md) - Basic input
|
|
734
|
-
- [InputField](/docs/components/input-field.md) - Enhanced input
|
|
735
|
-
- [Form](/docs/components/form.md) - Form wrapper
|
|
736
|
-
|
|
737
|
-
## Browser Support
|
|
738
|
-
|
|
739
|
-
- Chrome 90+ ✅
|
|
740
|
-
- Firefox 88+ ✅
|
|
741
|
-
- Safari 14+ ✅
|
|
742
|
-
- Edge 90+ ✅
|
|
743
|
-
- Mobile browsers ✅
|
|
744
|
-
|
|
745
|
-
## Changelog
|
|
746
|
-
|
|
747
|
-
### v1.1.15
|
|
748
|
-
- Integrated input-otp library
|
|
749
|
-
- Added slot components
|
|
750
|
-
- Enhanced accessibility
|
|
751
|
-
|
|
752
|
-
### v1.1.14
|
|
753
|
-
- Initial OTP input planning
|
|
754
|
-
|
|
755
|
-
### v1.1.0
|
|
756
|
-
- Component requirements defined
|