@ojiepermana/angular 21.3.4 → 22.0.1
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 +10 -6
- package/component/accordion/README.md +195 -0
- package/component/alert/README.md +182 -0
- package/component/alert-dialog/README.md +239 -0
- package/component/aspect-ratio/README.md +112 -0
- package/component/avatar/README.md +176 -0
- package/component/badge/README.md +133 -0
- package/component/breadcrumb/README.md +216 -0
- package/component/button/README.md +139 -0
- package/component/button-group/README.md +208 -0
- package/component/calendar/README.md +132 -0
- package/component/card/README.md +220 -0
- package/component/carousel/README.md +276 -0
- package/component/checkbox/README.md +149 -0
- package/component/collapsible/README.md +195 -0
- package/component/combobox/README.md +198 -0
- package/component/command/README.md +275 -0
- package/component/composer/README.md +235 -0
- package/component/composer/package.json +4 -0
- package/component/context-menu/README.md +267 -0
- package/component/date-picker/README.md +177 -0
- package/component/dialog/README.md +237 -0
- package/component/drawer/README.md +145 -0
- package/component/dropdown-menu/README.md +311 -0
- package/component/editor/README.md +136 -0
- package/component/editor/package.json +4 -0
- package/component/empty/README.md +183 -0
- package/component/empty/package.json +4 -0
- package/component/form/README.md +210 -0
- package/component/hover-card/README.md +146 -0
- package/component/hover-card/package.json +4 -0
- package/component/input/README.md +159 -0
- package/component/input-group/README.md +239 -0
- package/component/input-otp/README.md +278 -0
- package/component/input-otp/package.json +4 -0
- package/component/item/README.md +247 -0
- package/component/kanban/README.md +81 -0
- package/component/kanban/package.json +4 -0
- package/component/kbd/README.md +139 -0
- package/component/kbd/package.json +4 -0
- package/component/label/README.md +136 -0
- package/component/menubar/README.md +269 -0
- package/component/menubar/package.json +4 -0
- package/component/native-select/README.md +176 -0
- package/component/native-select/package.json +4 -0
- package/component/navigation-menu/README.md +160 -0
- package/component/navigation-menu/package.json +4 -0
- package/component/pagination/README.md +144 -0
- package/component/pillbox/README.md +67 -0
- package/component/pillbox/package.json +4 -0
- package/component/popover/README.md +43 -0
- package/component/progress/README.md +160 -0
- package/component/radio/README.md +209 -0
- package/component/resizable/README.md +168 -0
- package/component/resizable/package.json +4 -0
- package/component/scroll-area/README.md +143 -0
- package/component/select/README.md +174 -0
- package/component/separator/README.md +170 -0
- package/component/sheet/README.md +183 -0
- package/component/skeleton/README.md +158 -0
- package/component/slider/README.md +207 -0
- package/component/spinner/README.md +160 -0
- package/component/spinner/package.json +4 -0
- package/component/switch/README.md +166 -0
- package/component/table/README.md +291 -0
- package/component/tabs/README.md +219 -0
- package/component/textarea/README.md +154 -0
- package/component/timeline/README.md +94 -0
- package/component/timeline/package.json +4 -0
- package/component/toast/README.md +321 -0
- package/component/toggle/README.md +131 -0
- package/component/toggle/package.json +4 -0
- package/component/toggle-group/README.md +206 -0
- package/component/toggle-group/package.json +4 -0
- package/component/tooltip/README.md +211 -0
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +45 -30
- package/fesm2022/ojiepermana-angular-component-accordion.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +95 -61
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-alert.mjs +30 -21
- package/fesm2022/ojiepermana-angular-component-alert.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +50 -34
- package/fesm2022/ojiepermana-angular-component-avatar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-badge.mjs +9 -6
- package/fesm2022/ojiepermana-angular-component-badge.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +49 -35
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +25 -17
- package/fesm2022/ojiepermana-angular-component-button-group.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-button.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-button.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +23 -13
- package/fesm2022/ojiepermana-angular-component-calendar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-card.mjs +51 -36
- package/fesm2022/ojiepermana-angular-component-card.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +66 -42
- package/fesm2022/ojiepermana-angular-component-carousel.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-chart.mjs +494 -283
- package/fesm2022/ojiepermana-angular-component-chart.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +23 -13
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +28 -20
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +27 -18
- package/fesm2022/ojiepermana-angular-component-combobox.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-command.mjs +77 -52
- package/fesm2022/ojiepermana-angular-component-command.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-composer.mjs +352 -0
- package/fesm2022/ojiepermana-angular-component-composer.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs +9 -6
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +34 -19
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +55 -38
- package/fesm2022/ojiepermana-angular-component-dialog.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-drawer.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +108 -74
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-editor.mjs +717 -0
- package/fesm2022/ojiepermana-angular-component-editor.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-empty.mjs +145 -0
- package/fesm2022/ojiepermana-angular-component-empty.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-form.mjs +200 -42
- package/fesm2022/ojiepermana-angular-component-form.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +297 -0
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +48 -33
- package/fesm2022/ojiepermana-angular-component-input-group.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +514 -0
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input.mjs +7 -5
- package/fesm2022/ojiepermana-angular-component-input.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-item.mjs +76 -53
- package/fesm2022/ojiepermana-angular-component-item.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-kanban.mjs +314 -0
- package/fesm2022/ojiepermana-angular-component-kanban.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-kbd.mjs +55 -0
- package/fesm2022/ojiepermana-angular-component-kbd.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-label.mjs +9 -6
- package/fesm2022/ojiepermana-angular-component-label.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +308 -0
- package/fesm2022/ojiepermana-angular-component-menubar.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-native-select.mjs +67 -0
- package/fesm2022/ojiepermana-angular-component-native-select.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +413 -0
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +65 -31
- package/fesm2022/ojiepermana-angular-component-pagination.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +812 -0
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-popover.mjs +18 -12
- package/fesm2022/ojiepermana-angular-component-popover.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-progress.mjs +17 -10
- package/fesm2022/ojiepermana-angular-component-progress.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-radio.mjs +47 -17
- package/fesm2022/ojiepermana-angular-component-radio.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +481 -0
- package/fesm2022/ojiepermana-angular-component-resizable.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +15 -9
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-select.mjs +71 -26
- package/fesm2022/ojiepermana-angular-component-select.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-separator.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-separator.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +91 -42
- package/fesm2022/ojiepermana-angular-component-sheet.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +7 -5
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-slider.mjs +401 -7
- package/fesm2022/ojiepermana-angular-component-slider.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-spinner.mjs +60 -0
- package/fesm2022/ojiepermana-angular-component-spinner.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-switch.mjs +47 -15
- package/fesm2022/ojiepermana-angular-component-switch.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-table.mjs +56 -40
- package/fesm2022/ojiepermana-angular-component-table.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +58 -38
- package/fesm2022/ojiepermana-angular-component-tabs.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +8 -6
- package/fesm2022/ojiepermana-angular-component-textarea.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-timeline.mjs +237 -0
- package/fesm2022/ojiepermana-angular-component-timeline.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-toast.mjs +28 -4
- package/fesm2022/ojiepermana-angular-component-toast.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +289 -0
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-toggle.mjs +82 -0
- package/fesm2022/ojiepermana-angular-component-toggle.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs +304 -6
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-utils.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-component.mjs +45 -24
- package/fesm2022/ojiepermana-angular-layout-component.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-provider.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-services.mjs +7 -5
- package/fesm2022/ojiepermana-angular-layout-services.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-shell.mjs +3 -3
- package/fesm2022/ojiepermana-angular-layout-shell.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-token-directive.mjs +9 -6
- package/fesm2022/ojiepermana-angular-layout-token-directive.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-token.mjs.map +1 -1
- package/fesm2022/{ojiepermana-angular-layout-empty.mjs → ojiepermana-angular-layout-type-empty.mjs} +4 -4
- package/fesm2022/ojiepermana-angular-layout-type-empty.mjs.map +1 -0
- package/fesm2022/{ojiepermana-angular-layout-horizontal.mjs → ojiepermana-angular-layout-type-horizontal.mjs} +26 -17
- package/fesm2022/ojiepermana-angular-layout-type-horizontal.mjs.map +1 -0
- package/fesm2022/{ojiepermana-angular-layout-vertical.mjs → ojiepermana-angular-layout-type-vertical.mjs} +28 -18
- package/fesm2022/ojiepermana-angular-layout-type-vertical.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-layout.mjs +74 -50
- package/fesm2022/ojiepermana-angular-layout.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-demo-data.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs +11 -7
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-item.mjs +27 -16
- package/fesm2022/ojiepermana-angular-navigation-item.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +29 -20
- package/fesm2022/ojiepermana-angular-navigation-service.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-sidebar.mjs +71 -43
- package/fesm2022/ojiepermana-angular-navigation-sidebar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-topbar.mjs +261 -24
- package/fesm2022/ojiepermana-angular-navigation-topbar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-provider.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-services.mjs +19 -11
- package/fesm2022/ojiepermana-angular-theme-services.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-token.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme.mjs +19 -11
- package/fesm2022/ojiepermana-angular-theme.mjs.map +1 -1
- package/generator/api/bin/src/emit/client.js +4 -2
- package/generator/api/bin/src/writer/index.js +2 -2
- package/generator/guide/bin/schematics/build/index.js +3 -2
- package/generator/guide/bin/src/engine/component.js +2 -2
- package/generator/guide/bin/src/engine/index.js +3 -3
- package/generator/guide/bin/src/engine/render.js +10 -5
- package/layout/type/empty/package.json +4 -0
- package/layout/type/horizontal/package.json +4 -0
- package/layout/type/vertical/package.json +4 -0
- package/navigation/topbar/README.md +196 -0
- package/package.json +89 -25
- package/theme/README.md +110 -3
- package/theme/styles/integrations/material/autocomplete.css +178 -0
- package/theme/styles/integrations/material/button.css +468 -0
- package/theme/styles/integrations/material/dialog.css +152 -0
- package/theme/styles/integrations/material/select.css +175 -0
- package/theme/styles/integrations/material/slide-toggle.css +234 -0
- package/theme/styles/integrations/material/slider.css +194 -0
- package/theme/styles/integrations/material/tabs.css +229 -0
- package/theme/styles/integrations/material.css +70 -60
- package/types/ojiepermana-angular-component-combobox.d.ts +1 -2
- package/types/ojiepermana-angular-component-composer.d.ts +90 -0
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +2 -0
- package/types/ojiepermana-angular-component-editor.d.ts +123 -0
- package/types/ojiepermana-angular-component-empty.d.ts +50 -0
- package/types/ojiepermana-angular-component-form.d.ts +52 -3
- package/types/ojiepermana-angular-component-hover-card.d.ts +74 -0
- package/types/ojiepermana-angular-component-input-otp.d.ts +136 -0
- package/types/ojiepermana-angular-component-kanban.d.ts +70 -0
- package/types/ojiepermana-angular-component-kbd.d.ts +16 -0
- package/types/ojiepermana-angular-component-menubar.d.ts +67 -0
- package/types/ojiepermana-angular-component-native-select.d.ts +26 -0
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +96 -0
- package/types/ojiepermana-angular-component-pagination.d.ts +10 -4
- package/types/ojiepermana-angular-component-pillbox.d.ts +157 -0
- package/types/ojiepermana-angular-component-radio.d.ts +7 -1
- package/types/ojiepermana-angular-component-resizable.d.ts +99 -0
- package/types/ojiepermana-angular-component-select.d.ts +17 -5
- package/types/ojiepermana-angular-component-sheet.d.ts +3 -1
- package/types/ojiepermana-angular-component-slider.d.ts +59 -1
- package/types/ojiepermana-angular-component-spinner.d.ts +13 -0
- package/types/ojiepermana-angular-component-switch.d.ts +13 -3
- package/types/ojiepermana-angular-component-timeline.d.ts +63 -0
- package/types/ojiepermana-angular-component-toast.d.ts +12 -3
- package/types/ojiepermana-angular-component-toggle-group.d.ts +89 -0
- package/types/ojiepermana-angular-component-toggle.d.ts +25 -0
- package/types/ojiepermana-angular-component-tooltip.d.ts +72 -5
- package/types/{ojiepermana-angular-layout-horizontal.d.ts → ojiepermana-angular-layout-type-horizontal.d.ts} +3 -3
- package/types/{ojiepermana-angular-layout-vertical.d.ts → ojiepermana-angular-layout-type-vertical.d.ts} +3 -3
- package/types/ojiepermana-angular-layout.d.ts +5 -5
- package/types/ojiepermana-angular-navigation-item.d.ts +1 -1
- package/types/ojiepermana-angular-navigation-service.d.ts +7 -7
- package/types/ojiepermana-angular-navigation-sidebar.d.ts +8 -8
- package/types/ojiepermana-angular-navigation-topbar.d.ts +24 -4
- package/types/ojiepermana-angular-navigation-types.d.ts +14 -8
- package/fesm2022/ojiepermana-angular-layout-empty.mjs.map +0 -1
- package/fesm2022/ojiepermana-angular-layout-horizontal.mjs.map +0 -1
- package/fesm2022/ojiepermana-angular-layout-vertical.mjs.map +0 -1
- package/layout/empty/package.json +0 -4
- package/layout/horizontal/package.json +0 -4
- package/layout/vertical/package.json +0 -4
- /package/types/{ojiepermana-angular-layout-empty.d.ts → ojiepermana-angular-layout-type-empty.d.ts} +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Input Group
|
|
2
|
+
|
|
3
|
+
Grouped input primitives for prefixes, suffixes, stacked helper rows, and inline actions.
|
|
4
|
+
|
|
5
|
+
Use Input Group when the field and its addon content should read as a single control boundary instead of separate neighboring elements.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
FormControlDirective,
|
|
12
|
+
FormDescriptionComponent,
|
|
13
|
+
FormFieldComponent,
|
|
14
|
+
FormLabelComponent,
|
|
15
|
+
} from '@ojiepermana/angular/component/form';
|
|
16
|
+
import {
|
|
17
|
+
InputGroupAddonComponent,
|
|
18
|
+
InputGroupButtonComponent,
|
|
19
|
+
InputGroupComponent,
|
|
20
|
+
InputGroupInputComponent,
|
|
21
|
+
InputGroupTextComponent,
|
|
22
|
+
InputGroupTextareaComponent,
|
|
23
|
+
} from '@ojiepermana/angular/component/input-group';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Keep the input or textarea first in the DOM, then append one or more addons after it.
|
|
29
|
+
Use the `align` input on `ui-input-group-addon` to place the addon at the inline or block edge.
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<ui-form-field>
|
|
33
|
+
<ui-form-label>Search</ui-form-label>
|
|
34
|
+
<ui-input-group>
|
|
35
|
+
<input ui-input-group-input uiFormControl placeholder="Search documentation..." />
|
|
36
|
+
<ui-input-group-addon>
|
|
37
|
+
<span aria-hidden="true">⌕</span>
|
|
38
|
+
</ui-input-group-addon>
|
|
39
|
+
<ui-input-group-addon align="inline-end">
|
|
40
|
+
<ui-input-group-text>12 results</ui-input-group-text>
|
|
41
|
+
</ui-input-group-addon>
|
|
42
|
+
</ui-input-group>
|
|
43
|
+
<ui-form-description
|
|
44
|
+
>Keep addons after the control in the DOM and use align for visual placement.</ui-form-description
|
|
45
|
+
>
|
|
46
|
+
</ui-form-field>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Composition
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
ui-input-group
|
|
53
|
+
├── input[ui-input-group-input] or textarea[ui-input-group-textarea]
|
|
54
|
+
├── ui-input-group-addon
|
|
55
|
+
│ ├── ui-input-group-text
|
|
56
|
+
│ └── button[ui-input-group-button]
|
|
57
|
+
└── ui-input-group-addon
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Common Patterns
|
|
61
|
+
|
|
62
|
+
### Inline search
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<ui-input-group class="max-w-md">
|
|
66
|
+
<input ui-input-group-input placeholder="Search..." />
|
|
67
|
+
<ui-input-group-addon>
|
|
68
|
+
<span aria-hidden="true">⌕</span>
|
|
69
|
+
</ui-input-group-addon>
|
|
70
|
+
<ui-input-group-addon align="inline-end">
|
|
71
|
+
<ui-input-group-text>12 results</ui-input-group-text>
|
|
72
|
+
</ui-input-group-addon>
|
|
73
|
+
</ui-input-group>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Text addons
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<ui-input-group>
|
|
80
|
+
<input ui-input-group-input placeholder="0.00" />
|
|
81
|
+
<ui-input-group-addon>
|
|
82
|
+
<ui-input-group-text>$</ui-input-group-text>
|
|
83
|
+
</ui-input-group-addon>
|
|
84
|
+
<ui-input-group-addon align="inline-end">
|
|
85
|
+
<ui-input-group-text>USD</ui-input-group-text>
|
|
86
|
+
</ui-input-group-addon>
|
|
87
|
+
</ui-input-group>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Textarea footer
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<ui-input-group>
|
|
94
|
+
<textarea ui-input-group-textarea placeholder="Write a comment..."></textarea>
|
|
95
|
+
<ui-input-group-addon align="block-end">
|
|
96
|
+
<ui-input-group-text>0/280</ui-input-group-text>
|
|
97
|
+
<button ui-input-group-button type="button" size="sm" variant="default" class="ml-auto">Post</button>
|
|
98
|
+
</ui-input-group-addon>
|
|
99
|
+
</ui-input-group>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Custom control
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<ui-input-group>
|
|
106
|
+
<textarea
|
|
107
|
+
data-slot="input-group-control"
|
|
108
|
+
class="min-h-20 w-full resize-none bg-transparent px-3 py-2.5 text-sm outline-none placeholder:text-muted-foreground"
|
|
109
|
+
placeholder="Use any custom control that matches the group contract..."></textarea>
|
|
110
|
+
<ui-input-group-addon align="block-end">
|
|
111
|
+
<button ui-input-group-button type="button" variant="default" size="sm" class="ml-auto">Submit</button>
|
|
112
|
+
</ui-input-group-addon>
|
|
113
|
+
</ui-input-group>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Keyboard shortcut badge
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<ui-input-group class="max-w-sm">
|
|
120
|
+
<input ui-input-group-input placeholder="Search..." />
|
|
121
|
+
<ui-input-group-addon>
|
|
122
|
+
<span aria-hidden="true">⌕</span>
|
|
123
|
+
</ui-input-group-addon>
|
|
124
|
+
<ui-input-group-addon align="inline-end">
|
|
125
|
+
<span
|
|
126
|
+
aria-hidden="true"
|
|
127
|
+
class="inline-flex h-6 items-center rounded-md border border-border bg-background px-2 font-mono text-[11px] font-medium text-foreground shadow-sm">
|
|
128
|
+
⌘K
|
|
129
|
+
</span>
|
|
130
|
+
</ui-input-group-addon>
|
|
131
|
+
</ui-input-group>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Dropdown composition
|
|
135
|
+
|
|
136
|
+
Use the local menu primitives when the grouped field needs scoped actions or search destinations.
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<ui-input-group>
|
|
140
|
+
<input ui-input-group-input placeholder="Enter file name" />
|
|
141
|
+
<ui-input-group-addon align="inline-end">
|
|
142
|
+
<button
|
|
143
|
+
ui-input-group-button
|
|
144
|
+
type="button"
|
|
145
|
+
size="icon-xs"
|
|
146
|
+
aria-label="Open file actions"
|
|
147
|
+
[uiMenuTrigger]="fileActionsMenu">
|
|
148
|
+
⋯
|
|
149
|
+
</button>
|
|
150
|
+
</ui-input-group-addon>
|
|
151
|
+
</ui-input-group>
|
|
152
|
+
|
|
153
|
+
<ng-template uiMenuContent #fileActionsMenu="uiMenuContent">
|
|
154
|
+
<ui-menu-surface>
|
|
155
|
+
<ui-menu-label>File actions</ui-menu-label>
|
|
156
|
+
<button ui-menu-item type="button">Settings</button>
|
|
157
|
+
<button ui-menu-item type="button">Copy path</button>
|
|
158
|
+
<ui-menu-separator />
|
|
159
|
+
<button ui-menu-item type="button">Open location</button>
|
|
160
|
+
</ui-menu-surface>
|
|
161
|
+
</ng-template>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Spinner status row
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<ui-input-group>
|
|
168
|
+
<input ui-input-group-input placeholder="Saving changes..." />
|
|
169
|
+
<ui-input-group-addon align="inline-end">
|
|
170
|
+
<ui-input-group-text>Saving...</ui-input-group-text>
|
|
171
|
+
<span
|
|
172
|
+
aria-hidden="true"
|
|
173
|
+
class="inline-flex size-4 animate-spin rounded-full border-2 border-muted border-t-foreground"></span>
|
|
174
|
+
</ui-input-group-addon>
|
|
175
|
+
</ui-input-group>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API Reference
|
|
179
|
+
|
|
180
|
+
### `InputGroupComponent`
|
|
181
|
+
|
|
182
|
+
| Input | Type | Default |
|
|
183
|
+
| ------- | -------- | ------------ |
|
|
184
|
+
| `class` | `string` | empty string |
|
|
185
|
+
|
|
186
|
+
### `InputGroupAddonComponent`
|
|
187
|
+
|
|
188
|
+
| Input | Type | Default |
|
|
189
|
+
| ------- | ------------------------------------------------------------- | ---------------- |
|
|
190
|
+
| `align` | `'inline-start', 'inline-end', 'block-start', or 'block-end'` | `'inline-start'` |
|
|
191
|
+
| `class` | `string` | empty string |
|
|
192
|
+
|
|
193
|
+
### `InputGroupButtonComponent`
|
|
194
|
+
|
|
195
|
+
| Input | Type | Default |
|
|
196
|
+
| --------- | ------------------------------------- | ------------ |
|
|
197
|
+
| `variant` | `ButtonVariant` | `'ghost'` |
|
|
198
|
+
| `size` | `'xs', 'icon-xs', 'sm', or 'icon-sm'` | `'xs'` |
|
|
199
|
+
| `class` | `string` | empty string |
|
|
200
|
+
|
|
201
|
+
### `InputGroupInputComponent` and `InputGroupTextareaComponent`
|
|
202
|
+
|
|
203
|
+
| Input | Type | Default |
|
|
204
|
+
| ------- | -------- | ------------ |
|
|
205
|
+
| `class` | `string` | empty string |
|
|
206
|
+
|
|
207
|
+
All other native input and textarea attributes pass through unchanged.
|
|
208
|
+
|
|
209
|
+
## Styling And Theming
|
|
210
|
+
|
|
211
|
+
- `ui-input-group` owns the shared border, focus ring, radius, and background surface.
|
|
212
|
+
- `ui-input-group-addon` uses logical separators so start and end alignments still read correctly in RTL layouts.
|
|
213
|
+
- Override `--radius` on the group root when the grouped field should be sharper or more pill-shaped.
|
|
214
|
+
- Add spacing or typography utilities through `class` on any primitive without breaking the core layout contract.
|
|
215
|
+
|
|
216
|
+
## Accessibility
|
|
217
|
+
|
|
218
|
+
- Keep the input or textarea before addons in the DOM so tab order and assistive reading order stay predictable.
|
|
219
|
+
- Provide a visible label with `ui-form-label`, `ui-label`, or another accessible name source.
|
|
220
|
+
- Give icon-only `ui-input-group-button` elements an `aria-label`.
|
|
221
|
+
- Use `uiFormControl` inside `ui-form-field` when labels, descriptions, and invalid messaging should auto-wire to the grouped control.
|
|
222
|
+
|
|
223
|
+
## Keyboard Interactions
|
|
224
|
+
|
|
225
|
+
- Text input and textarea editing remain native.
|
|
226
|
+
- Buttons inside addons participate in the normal tab sequence after the text control.
|
|
227
|
+
- Changing `align` only affects visual placement; it does not change keyboard order.
|
|
228
|
+
|
|
229
|
+
## Angular Notes
|
|
230
|
+
|
|
231
|
+
- `ui-input-group-input` and `ui-input-group-textarea` are attribute selectors on native controls, so native input and textarea behavior remains intact.
|
|
232
|
+
- For custom controls, set `data-slot="input-group-control"` and provide your own spacing utilities so the wrapper can still coordinate focus and addon layout.
|
|
233
|
+
- Upstream shadcn `Field` examples map to the local `ui-form-field`, `ui-form-label`, and `ui-form-description` primitives rather than a separate `field` runtime component.
|
|
234
|
+
|
|
235
|
+
## Source Parity
|
|
236
|
+
|
|
237
|
+
This Angular implementation covers the core shadcn Input Group primitives, all four addon alignments, textarea support, custom control guidance, and RTL composition.
|
|
238
|
+
|
|
239
|
+
Keyboard shortcut badges, dropdown triggers, and spinner/status rows are composed from the same primitives plus the local menu surface and inline status markup instead of requiring input-group-specific wrappers.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Input OTP
|
|
2
|
+
|
|
3
|
+
Accessible one-time password input with grouped slots, copy-paste distribution, roving focus, signal-friendly control, and reactive-form support.
|
|
4
|
+
|
|
5
|
+
Use Input OTP for verification codes, step-up authentication, banking PIN entry, and short recovery codes where each character should be visually separated without losing keyboard or paste ergonomics.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
InputOtpComponent,
|
|
12
|
+
InputOtpGroupComponent,
|
|
13
|
+
InputOtpSeparatorComponent,
|
|
14
|
+
InputOtpSlotComponent,
|
|
15
|
+
REGEXP_ONLY_DIGITS,
|
|
16
|
+
REGEXP_ONLY_DIGITS_AND_CHARS,
|
|
17
|
+
} from '@ojiepermana/angular/component/input-otp';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
When the code input belongs to a richer field layout, pair it with the existing form primitives:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
FormDescriptionComponent,
|
|
25
|
+
FormFieldComponent,
|
|
26
|
+
FormLabelComponent,
|
|
27
|
+
FormMessageComponent,
|
|
28
|
+
} from '@ojiepermana/angular/component/form';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Composition
|
|
32
|
+
|
|
33
|
+
The Angular structure follows the shadcn Input OTP information architecture while translating it to standalone selectors.
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
ui-input-otp
|
|
37
|
+
├── ui-input-otp-group
|
|
38
|
+
│ ├── ui-input-otp-slot
|
|
39
|
+
│ ├── ui-input-otp-slot
|
|
40
|
+
│ └── ui-input-otp-slot
|
|
41
|
+
├── ui-input-otp-separator
|
|
42
|
+
└── ui-input-otp-group
|
|
43
|
+
├── ui-input-otp-slot
|
|
44
|
+
├── ui-input-otp-slot
|
|
45
|
+
└── ui-input-otp-slot
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Basic Usage
|
|
49
|
+
|
|
50
|
+
Use the root component for value flow and the projected parts for layout.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
protected readonly code = signal('');
|
|
54
|
+
protected readonly digitsPattern = REGEXP_ONLY_DIGITS;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<ui-form-field>
|
|
59
|
+
<ui-form-label>Verification code</ui-form-label>
|
|
60
|
+
<ui-input-otp [maxLength]="6" [pattern]="digitsPattern" [value]="code()" (valueChange)="code.set($event)">
|
|
61
|
+
<ui-input-otp-group>
|
|
62
|
+
<ui-input-otp-slot [index]="0" />
|
|
63
|
+
<ui-input-otp-slot [index]="1" />
|
|
64
|
+
<ui-input-otp-slot [index]="2" />
|
|
65
|
+
<ui-input-otp-slot [index]="3" />
|
|
66
|
+
<ui-input-otp-slot [index]="4" />
|
|
67
|
+
<ui-input-otp-slot [index]="5" />
|
|
68
|
+
</ui-input-otp-group>
|
|
69
|
+
</ui-input-otp>
|
|
70
|
+
<ui-form-description>Paste the code from your email or authenticator app.</ui-form-description>
|
|
71
|
+
</ui-form-field>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Common Patterns
|
|
75
|
+
|
|
76
|
+
### Separator groups
|
|
77
|
+
|
|
78
|
+
Split longer codes into smaller clusters when the verification flow benefits from visual chunking.
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<ui-input-otp [maxLength]="6" [pattern]="digitsPattern" [value]="code()" (valueChange)="code.set($event)">
|
|
82
|
+
<ui-input-otp-group>
|
|
83
|
+
<ui-input-otp-slot [index]="0" />
|
|
84
|
+
<ui-input-otp-slot [index]="1" />
|
|
85
|
+
</ui-input-otp-group>
|
|
86
|
+
<ui-input-otp-separator />
|
|
87
|
+
<ui-input-otp-group>
|
|
88
|
+
<ui-input-otp-slot [index]="2" />
|
|
89
|
+
<ui-input-otp-slot [index]="3" />
|
|
90
|
+
</ui-input-otp-group>
|
|
91
|
+
<ui-input-otp-separator />
|
|
92
|
+
<ui-input-otp-group>
|
|
93
|
+
<ui-input-otp-slot [index]="4" />
|
|
94
|
+
<ui-input-otp-slot [index]="5" />
|
|
95
|
+
</ui-input-otp-group>
|
|
96
|
+
</ui-input-otp>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Reactive forms
|
|
100
|
+
|
|
101
|
+
The root component implements `ControlValueAccessor` and also integrates directly with `ui-form-field`, so labels, descriptions, and invalid-state wiring work without `uiFormControl`.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
readonly verificationForm = new FormGroup({
|
|
105
|
+
code: new FormControl('', {
|
|
106
|
+
nonNullable: true,
|
|
107
|
+
validators: [Validators.required, Validators.minLength(6)],
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<ui-form-field>
|
|
114
|
+
<ui-form-label>Verification code</ui-form-label>
|
|
115
|
+
<ui-input-otp formControlName="code" [maxLength]="6" [pattern]="digitsPattern">
|
|
116
|
+
<ui-input-otp-group
|
|
117
|
+
class="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
|
118
|
+
<ui-input-otp-slot [index]="0" />
|
|
119
|
+
<ui-input-otp-slot [index]="1" />
|
|
120
|
+
<ui-input-otp-slot [index]="2" />
|
|
121
|
+
</ui-input-otp-group>
|
|
122
|
+
<ui-input-otp-separator class="mx-2" />
|
|
123
|
+
<ui-input-otp-group
|
|
124
|
+
class="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
|
125
|
+
<ui-input-otp-slot [index]="3" />
|
|
126
|
+
<ui-input-otp-slot [index]="4" />
|
|
127
|
+
<ui-input-otp-slot [index]="5" />
|
|
128
|
+
</ui-input-otp-group>
|
|
129
|
+
</ui-input-otp>
|
|
130
|
+
<ui-form-description>Enter the 6-digit code sent to m@example.com.</ui-form-description>
|
|
131
|
+
<ui-form-message />
|
|
132
|
+
</ui-form-field>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Disabled and invalid states
|
|
136
|
+
|
|
137
|
+
Use the native `disabled` input when the code should remain visible but unavailable, and forward `aria-invalid="true"` when an expired or rejected code needs the destructive treatment.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<ui-input-otp [maxLength]="6" [pattern]="digitsPattern" value="123456" disabled>
|
|
141
|
+
<ui-input-otp-group>
|
|
142
|
+
<ui-input-otp-slot [index]="0" />
|
|
143
|
+
<ui-input-otp-slot [index]="1" />
|
|
144
|
+
<ui-input-otp-slot [index]="2" />
|
|
145
|
+
</ui-input-otp-group>
|
|
146
|
+
<ui-input-otp-separator />
|
|
147
|
+
<ui-input-otp-group>
|
|
148
|
+
<ui-input-otp-slot [index]="3" />
|
|
149
|
+
<ui-input-otp-slot [index]="4" />
|
|
150
|
+
<ui-input-otp-slot [index]="5" />
|
|
151
|
+
</ui-input-otp-group>
|
|
152
|
+
</ui-input-otp>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<ui-input-otp
|
|
157
|
+
aria-invalid="true"
|
|
158
|
+
[maxLength]="6"
|
|
159
|
+
[pattern]="digitsPattern"
|
|
160
|
+
[value]="invalidCode()"
|
|
161
|
+
(valueChange)="invalidCode.set($event)">
|
|
162
|
+
<ui-input-otp-group>
|
|
163
|
+
<ui-input-otp-slot [index]="0" />
|
|
164
|
+
<ui-input-otp-slot [index]="1" />
|
|
165
|
+
<ui-input-otp-slot [index]="2" />
|
|
166
|
+
</ui-input-otp-group>
|
|
167
|
+
<ui-input-otp-separator />
|
|
168
|
+
<ui-input-otp-group>
|
|
169
|
+
<ui-input-otp-slot [index]="3" />
|
|
170
|
+
<ui-input-otp-slot [index]="4" />
|
|
171
|
+
<ui-input-otp-slot [index]="5" />
|
|
172
|
+
</ui-input-otp-group>
|
|
173
|
+
</ui-input-otp>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Four digits and alphanumeric codes
|
|
177
|
+
|
|
178
|
+
Use `REGEXP_ONLY_DIGITS` for numeric OTPs and `REGEXP_ONLY_DIGITS_AND_CHARS` for mixed recovery codes.
|
|
179
|
+
|
|
180
|
+
```html
|
|
181
|
+
<ui-input-otp [maxLength]="4" [pattern]="digitsPattern" [inputmode]="'numeric'">
|
|
182
|
+
<ui-input-otp-group>
|
|
183
|
+
<ui-input-otp-slot [index]="0" />
|
|
184
|
+
<ui-input-otp-slot [index]="1" />
|
|
185
|
+
<ui-input-otp-slot [index]="2" />
|
|
186
|
+
<ui-input-otp-slot [index]="3" />
|
|
187
|
+
</ui-input-otp-group>
|
|
188
|
+
</ui-input-otp>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<ui-input-otp [maxLength]="6" [pattern]="digitsAndCharsPattern" [inputmode]="'text'">
|
|
193
|
+
<ui-input-otp-group>
|
|
194
|
+
<ui-input-otp-slot [index]="0" />
|
|
195
|
+
<ui-input-otp-slot [index]="1" />
|
|
196
|
+
<ui-input-otp-slot [index]="2" />
|
|
197
|
+
</ui-input-otp-group>
|
|
198
|
+
<ui-input-otp-separator />
|
|
199
|
+
<ui-input-otp-group>
|
|
200
|
+
<ui-input-otp-slot [index]="3" />
|
|
201
|
+
<ui-input-otp-slot [index]="4" />
|
|
202
|
+
<ui-input-otp-slot [index]="5" />
|
|
203
|
+
</ui-input-otp-group>
|
|
204
|
+
</ui-input-otp>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## API Reference
|
|
208
|
+
|
|
209
|
+
### `InputOtpComponent`
|
|
210
|
+
|
|
211
|
+
| Input | Type | Default |
|
|
212
|
+
| ------------------ | -------------------------- | ----------------- |
|
|
213
|
+
| `maxLength` | `number` | `6` |
|
|
214
|
+
| `value` | `string` | `undefined` |
|
|
215
|
+
| `pattern` | `RegExp \| string \| null` | `null` |
|
|
216
|
+
| `required` | `boolean` | `false` |
|
|
217
|
+
| `disabled` | `boolean` | `false` |
|
|
218
|
+
| `inputmode` | `string \| null` | `'numeric'` |
|
|
219
|
+
| `autocomplete` | `string \| null` | `'one-time-code'` |
|
|
220
|
+
| `aria-label` | `string \| null` | `null` |
|
|
221
|
+
| `aria-labelledby` | `string \| null` | `null` |
|
|
222
|
+
| `aria-describedby` | `string \| null` | `null` |
|
|
223
|
+
| `class` | `string` | `''` |
|
|
224
|
+
|
|
225
|
+
### Output
|
|
226
|
+
|
|
227
|
+
| Output | Type |
|
|
228
|
+
| ------------- | -------- |
|
|
229
|
+
| `valueChange` | `string` |
|
|
230
|
+
|
|
231
|
+
### Parts
|
|
232
|
+
|
|
233
|
+
| Part | Selector | Notes |
|
|
234
|
+
| ---------------------------- | ------------------------ | ------------------------------------------------------------- |
|
|
235
|
+
| `InputOtpGroupComponent` | `ui-input-otp-group` | Groups adjacent slots into one bordered cluster. |
|
|
236
|
+
| `InputOtpSlotComponent` | `ui-input-otp-slot` | Requires an `index` and renders the focusable character slot. |
|
|
237
|
+
| `InputOtpSeparatorComponent` | `ui-input-otp-separator` | Inserts an optional visual separator between groups. |
|
|
238
|
+
|
|
239
|
+
Convenience exports: `REGEXP_ONLY_DIGITS` and `REGEXP_ONLY_DIGITS_AND_CHARS`.
|
|
240
|
+
|
|
241
|
+
## Styling and Theming
|
|
242
|
+
|
|
243
|
+
The component exposes shadcn-style `data-slot` hooks on the root, group, separator, and slot hosts. Use `*:data-[slot=input-otp-slot]` utilities on `ui-input-otp-group` when a layout needs taller or wider cells, for example:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<ui-input-otp-group
|
|
247
|
+
class="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
|
248
|
+
...
|
|
249
|
+
</ui-input-otp-group>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The default styles use the shared border, ring, destructive, foreground, and muted-foreground tokens from the library theme.
|
|
253
|
+
|
|
254
|
+
## Accessibility
|
|
255
|
+
|
|
256
|
+
- Each slot is a real input with an accessible name that includes its position in the sequence.
|
|
257
|
+
- `ui-form-field` labels and descriptions wire to the component automatically when the OTP lives inside the field wrapper.
|
|
258
|
+
- `aria-invalid="true"` on the root forwards the destructive treatment across the slot set.
|
|
259
|
+
- Keep the surrounding label and helper text descriptive. Users should understand where the code came from and how many characters are expected.
|
|
260
|
+
|
|
261
|
+
## Keyboard Interactions
|
|
262
|
+
|
|
263
|
+
- Typing a character replaces the current slot and advances focus.
|
|
264
|
+
- Paste distributes accepted characters from the current slot onward.
|
|
265
|
+
- Arrow keys move across slots and flip direction under RTL layouts.
|
|
266
|
+
- `Backspace` removes the current or previous slot and shifts the remaining value left.
|
|
267
|
+
- `Delete` removes the current slot and shifts the remaining value left.
|
|
268
|
+
|
|
269
|
+
## Angular Notes
|
|
270
|
+
|
|
271
|
+
- Use `[value]` plus `(valueChange)` for signal-backed local state.
|
|
272
|
+
- Use `formControlName`, `[formControl]`, or `[(ngModel)]` for form-driven flows.
|
|
273
|
+
- There is no separate `defaultValue` input; seed the initial code with the bound signal or form control value.
|
|
274
|
+
- Unlike native inputs, this component does not need `uiFormControl`; it integrates with the form-field context directly.
|
|
275
|
+
|
|
276
|
+
## Source Parity
|
|
277
|
+
|
|
278
|
+
This Angular slice keeps the shadcn composition, grouped separators, pattern filtering, paste handling, and verification-form examples. The deliberate Angular deviation is the runtime implementation: each slot is a visible input coordinated by the root CVA instead of the upstream single hidden-input package. Browser one-time-code autofill works best through the first slot with the default `autocomplete="one-time-code"` behavior.
|