forty-cdk 0.2.0 → 0.3.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/accordion/README.md +122 -0
- package/aspect-ratio/README.md +76 -0
- package/avatar/README.md +100 -0
- package/breadcrumbs/README.md +49 -0
- package/breakpoints/README.md +81 -0
- package/button/README.md +49 -0
- package/calendar/README.md +458 -0
- package/carousel/README.md +358 -0
- package/checkbox/README.md +146 -0
- package/combobox/README.md +535 -0
- package/context-menu/README.md +139 -0
- package/date-field/README.md +184 -0
- package/date-picker/README.md +338 -0
- package/dialog/README.md +388 -0
- package/disclosure/README.md +114 -0
- package/drag-drop/README.md +359 -0
- package/drawer/README.md +560 -0
- package/dropdown-menu/README.md +176 -0
- package/fesm2022/forty-cdk-accordion.mjs +348 -0
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
- package/fesm2022/forty-cdk-avatar.mjs +308 -0
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
- package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
- package/fesm2022/forty-cdk-button.mjs +134 -0
- package/fesm2022/forty-cdk-button.mjs.map +1 -0
- package/fesm2022/forty-cdk-calendar.mjs +2034 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
- package/fesm2022/forty-cdk-carousel.mjs +968 -0
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
- package/fesm2022/forty-cdk-checkbox.mjs +226 -0
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-combobox.mjs +2596 -0
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
- package/fesm2022/forty-cdk-context-menu.mjs +413 -0
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-core.mjs +9022 -0
- package/fesm2022/forty-cdk-core.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-field.mjs +744 -0
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-dialog.mjs +707 -0
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
- package/fesm2022/forty-cdk-disclosure.mjs +190 -0
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
- package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/forty-cdk-drawer.mjs +1641 -0
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-field.mjs +425 -0
- package/fesm2022/forty-cdk-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-fieldset.mjs +164 -0
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
- package/fesm2022/forty-cdk-file-upload.mjs +221 -0
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
- package/fesm2022/forty-cdk-hover-card.mjs +496 -0
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
- package/fesm2022/forty-cdk-input.mjs +274 -0
- package/fesm2022/forty-cdk-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +1279 -0
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-menu.mjs +1439 -0
- package/fesm2022/forty-cdk-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-menubar.mjs +787 -0
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
- package/fesm2022/forty-cdk-meter.mjs +211 -0
- package/fesm2022/forty-cdk-meter.mjs.map +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-number-input.mjs +559 -0
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-otp-input.mjs +527 -0
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-pagination.mjs +323 -0
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
- package/fesm2022/forty-cdk-popover.mjs +698 -0
- package/fesm2022/forty-cdk-popover.mjs.map +1 -0
- package/fesm2022/forty-cdk-progress.mjs +226 -0
- package/fesm2022/forty-cdk-progress.mjs.map +1 -0
- package/fesm2022/forty-cdk-radio-group.mjs +378 -0
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
- package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
- package/fesm2022/forty-cdk-search.mjs +205 -0
- package/fesm2022/forty-cdk-search.mjs.map +1 -0
- package/fesm2022/forty-cdk-select.mjs +1661 -0
- package/fesm2022/forty-cdk-select.mjs.map +1 -0
- package/fesm2022/forty-cdk-separator.mjs +82 -0
- package/fesm2022/forty-cdk-separator.mjs.map +1 -0
- package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
- package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
- package/fesm2022/forty-cdk-slider.mjs +803 -0
- package/fesm2022/forty-cdk-slider.mjs.map +1 -0
- package/fesm2022/forty-cdk-stepper.mjs +886 -0
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
- package/fesm2022/forty-cdk-switch.mjs +137 -0
- package/fesm2022/forty-cdk-switch.mjs.map +1 -0
- package/fesm2022/forty-cdk-table.mjs +1518 -0
- package/fesm2022/forty-cdk-table.mjs.map +1 -0
- package/fesm2022/forty-cdk-tabs.mjs +400 -0
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-field.mjs +593 -0
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-toast.mjs +1153 -0
- package/fesm2022/forty-cdk-toast.mjs.map +1 -0
- package/fesm2022/forty-cdk-toggle.mjs +516 -0
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
- package/fesm2022/forty-cdk-toolbar.mjs +374 -0
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
- package/fesm2022/forty-cdk-tooltip.mjs +672 -0
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
- package/fesm2022/forty-cdk-tree.mjs +2007 -0
- package/fesm2022/forty-cdk-tree.mjs.map +1 -0
- package/fesm2022/forty-cdk-virtualization.mjs +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk.mjs +0 -43310
- package/fesm2022/forty-cdk.mjs.map +1 -1
- package/field/README.md +97 -0
- package/fieldset/README.md +86 -0
- package/file-upload/README.md +73 -0
- package/hover-card/README.md +171 -0
- package/input/README.md +156 -0
- package/listbox/README.md +424 -0
- package/menu/README.md +181 -0
- package/menubar/README.md +140 -0
- package/meter/README.md +128 -0
- package/navigation-menu/README.md +253 -0
- package/number-input/README.md +171 -0
- package/otp-input/README.md +198 -0
- package/package.json +213 -1
- package/pagination/README.md +61 -0
- package/pane-resizer/README.md +136 -0
- package/popover/README.md +262 -0
- package/progress/README.md +115 -0
- package/radio-group/README.md +129 -0
- package/scroll-area/README.md +184 -0
- package/search/README.md +42 -0
- package/select/README.md +488 -0
- package/separator/README.md +84 -0
- package/signal-forms/README.md +72 -0
- package/slider/README.md +152 -0
- package/stepper/README.md +292 -0
- package/switch/README.md +116 -0
- package/table/README.md +769 -0
- package/tabs/README.md +130 -0
- package/time-field/README.md +157 -0
- package/time-picker/README.md +172 -0
- package/toast/README.md +398 -0
- package/toggle/README.md +224 -0
- package/toolbar/README.md +109 -0
- package/tooltip/README.md +274 -0
- package/tree/README.md +708 -0
- package/types/forty-cdk-accordion.d.ts +242 -0
- package/types/forty-cdk-aspect-ratio.d.ts +59 -0
- package/types/forty-cdk-avatar.d.ts +133 -0
- package/types/forty-cdk-breadcrumbs.d.ts +92 -0
- package/types/forty-cdk-breakpoints.d.ts +141 -0
- package/types/forty-cdk-button.d.ts +80 -0
- package/types/forty-cdk-calendar.d.ts +914 -0
- package/types/forty-cdk-carousel.d.ts +530 -0
- package/types/forty-cdk-checkbox.d.ts +141 -0
- package/types/forty-cdk-combobox.d.ts +1259 -0
- package/types/forty-cdk-context-menu.d.ts +313 -0
- package/types/forty-cdk-core.d.ts +5774 -0
- package/types/forty-cdk-date-field.d.ts +307 -0
- package/types/forty-cdk-date-picker.d.ts +622 -0
- package/types/forty-cdk-dialog.d.ts +546 -0
- package/types/forty-cdk-disclosure.d.ts +127 -0
- package/types/forty-cdk-drag-drop.d.ts +456 -0
- package/types/forty-cdk-drawer.d.ts +871 -0
- package/types/forty-cdk-dropdown-menu.d.ts +242 -0
- package/types/forty-cdk-field.d.ts +236 -0
- package/types/forty-cdk-fieldset.d.ts +119 -0
- package/types/forty-cdk-file-upload.d.ts +124 -0
- package/types/forty-cdk-hover-card.d.ts +320 -0
- package/types/forty-cdk-input.d.ts +169 -0
- package/types/forty-cdk-internationalized-date.d.ts +1 -1
- package/types/forty-cdk-listbox.d.ts +513 -0
- package/types/forty-cdk-menu.d.ts +629 -0
- package/types/forty-cdk-menubar.d.ts +451 -0
- package/types/forty-cdk-meter.d.ts +122 -0
- package/types/forty-cdk-navigation-menu.d.ts +514 -0
- package/types/forty-cdk-number-input.d.ts +319 -0
- package/types/forty-cdk-otp-input.d.ts +248 -0
- package/types/forty-cdk-pagination.d.ts +214 -0
- package/types/forty-cdk-pane-resizer.d.ts +145 -0
- package/types/forty-cdk-popover.d.ts +509 -0
- package/types/forty-cdk-progress.d.ts +143 -0
- package/types/forty-cdk-radio-group.d.ts +222 -0
- package/types/forty-cdk-scroll-area.d.ts +258 -0
- package/types/forty-cdk-search.d.ts +142 -0
- package/types/forty-cdk-select.d.ts +899 -0
- package/types/forty-cdk-separator.d.ts +59 -0
- package/types/forty-cdk-signal-forms.d.ts +58 -0
- package/types/forty-cdk-slider.d.ts +379 -0
- package/types/forty-cdk-stepper.d.ts +650 -0
- package/types/forty-cdk-switch.d.ts +87 -0
- package/types/forty-cdk-table.d.ts +723 -0
- package/types/forty-cdk-tabs.d.ts +235 -0
- package/types/forty-cdk-time-field.d.ts +307 -0
- package/types/forty-cdk-time-picker.d.ts +578 -0
- package/types/forty-cdk-toast.d.ts +598 -0
- package/types/forty-cdk-toggle.d.ts +310 -0
- package/types/forty-cdk-toolbar.d.ts +217 -0
- package/types/forty-cdk-tooltip.d.ts +436 -0
- package/types/forty-cdk-tree.d.ts +688 -0
- package/types/forty-cdk.d.ts +1 -19743
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { InjectionToken, inject, ElementRef, DOCUMENT, DestroyRef, PLATFORM_ID, signal, model, input, booleanAttribute, output, computed, effect, afterNextRender, Directive } from '@angular/core';
|
|
4
|
+
import { FormUiControlBase, FOR_FIELD_CONTEXT, mirrorUnfocusedValue, createDefaults } from 'forty-cdk/core';
|
|
5
|
+
|
|
6
|
+
/** Injection token for the `[forOtpInput]` coordination surface the slots read. */
|
|
7
|
+
const FOR_OTP_INPUT_CONTEXT = new InjectionToken('FOR_OTP_INPUT_CONTEXT');
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the surrounding `[forOtpInput]` coordination context, or throw a
|
|
10
|
+
* descriptive error. A `[forOtpInputSlot]` is only meaningful inside a
|
|
11
|
+
* `[forOtpInput]`.
|
|
12
|
+
*/
|
|
13
|
+
function injectOtpInputContext(piece) {
|
|
14
|
+
const ctx = inject(FOR_OTP_INPUT_CONTEXT, { optional: true });
|
|
15
|
+
if (!ctx) {
|
|
16
|
+
throw new Error(`[forty-cdk/otp-input] ${piece} must be used inside a [forOtpInput].`);
|
|
17
|
+
}
|
|
18
|
+
return ctx;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Matches a value made up only of digits. Bind to `[pattern]` for a digit-only
|
|
23
|
+
* code; equivalent to the default `type="numeric"`.
|
|
24
|
+
*/
|
|
25
|
+
const OTP_REGEXP_ONLY_DIGITS = /^[0-9]+$/;
|
|
26
|
+
/** Matches a value made up only of latin letters (`type="alphabetic"`). */
|
|
27
|
+
const OTP_REGEXP_ONLY_CHARS = /^[a-zA-Z]+$/;
|
|
28
|
+
/** Matches a value made up only of digits and latin letters (`type="alphanumeric"`). */
|
|
29
|
+
const OTP_REGEXP_ONLY_DIGITS_AND_CHARS = /^[a-zA-Z0-9]+$/;
|
|
30
|
+
const SINGLE_CHAR_BY_TYPE = {
|
|
31
|
+
numeric: /[0-9]/,
|
|
32
|
+
alphanumeric: /[a-zA-Z0-9]/,
|
|
33
|
+
alphabetic: /[a-zA-Z]/,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The single-character matcher for a built-in `type`. Each returned RegExp is
|
|
37
|
+
* non-global, so repeated `.test()` calls are stateless.
|
|
38
|
+
*/
|
|
39
|
+
function allowedCharForType(type) {
|
|
40
|
+
return SINGLE_CHAR_BY_TYPE[type];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The `inputmode` a built-in `type` maps to: `numeric` keeps the digits-only
|
|
44
|
+
* keypad, the letter classes fall back to the full text keyboard.
|
|
45
|
+
*/
|
|
46
|
+
function inputModeForType(type) {
|
|
47
|
+
return type === 'numeric' ? 'numeric' : 'text';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const MASK_CHAR = '•';
|
|
51
|
+
function setAttr(el, name, value) {
|
|
52
|
+
if (value === null) {
|
|
53
|
+
el.removeAttribute(name);
|
|
54
|
+
}
|
|
55
|
+
else if (el.getAttribute(name) !== value) {
|
|
56
|
+
el.setAttribute(name, value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Headless OTP / PIN input following the **single-input** model: one real
|
|
61
|
+
* `<input maxlength=N>` carries the whole code as a `string`, and the
|
|
62
|
+
* `[forOtpInputSlot]` pieces are a pure
|
|
63
|
+
* styling surface painted over it. There is **no** WAI-ARIA APG pattern for
|
|
64
|
+
* OTP; this gives the cleanest screen-reader experience (one ordinary text
|
|
65
|
+
* field, not "edit text, 1 of 6" announced N times), native mobile SMS autofill
|
|
66
|
+
* via `autocomplete="one-time-code"`, and native paste / caret / selection.
|
|
67
|
+
*
|
|
68
|
+
* Apply `[forOtpInput]` on a wrapper element — it becomes a `role="group"` and
|
|
69
|
+
* the directive injects the single visually-hidden-but-interactive `<input>`
|
|
70
|
+
* inside it (the consumer styles that input to overlay the slots). It
|
|
71
|
+
* implements Angular's `FormValueControl<string>` from `@angular/forms/signals`,
|
|
72
|
+
* so it auto-wires with `[formField]` and auto-associates inside a `[forField]`
|
|
73
|
+
* (label / description / error) with no extra markup.
|
|
74
|
+
*
|
|
75
|
+
* Because the focusable, submittable control is the injected `<input>` and not
|
|
76
|
+
* the `role="group"` host, `ForOtpInput` redirects `FormUiControlBase`'s
|
|
77
|
+
* host-targeted helpers onto that input: it overrides `fieldLabelledElement()`
|
|
78
|
+
* (so the field association lands on the input) and `fieldStateReflectionTarget()`
|
|
79
|
+
* (so the form-state `data-*` reflect there), and reflects the OTP-specific input
|
|
80
|
+
* attributes (`maxLength` / `inputmode` / `autocomplete` / `pattern` / `name`)
|
|
81
|
+
* and ARIA on the input itself.
|
|
82
|
+
*
|
|
83
|
+
* The host gets `data-complete` (while every slot is filled) and `data-disabled`
|
|
84
|
+
* for CSS hooks; the real input carries `data-disabled` / `data-readonly` plus
|
|
85
|
+
* `data-touched` / `data-dirty` / `data-pending` / `data-invalid`.
|
|
86
|
+
*
|
|
87
|
+
* > **`allowedPattern`, not `pattern`.** The custom allowed-character RegExp is
|
|
88
|
+
* > named `allowedPattern` because `FormUiControl.pattern` is reserved by Signal
|
|
89
|
+
* > Forms for an array of validation patterns the `[formField]` directive binds
|
|
90
|
+
* > in — reusing the name would both break the `implements` contract and let the
|
|
91
|
+
* > field overwrite the character filter.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```html
|
|
95
|
+
* <div forOtpInput [(value)]="code" [length]="6" type="numeric" ariaLabel="Verification code" #otp="forOtpInput">
|
|
96
|
+
* @for (i of otp.slots(); track i) {
|
|
97
|
+
* <div forOtpInputSlot [index]="i" #s="forOtpInputSlot">
|
|
98
|
+
* {{ s.char() }}
|
|
99
|
+
* @if (s.hasFakeCaret()) { <span class="caret"></span> }
|
|
100
|
+
* </div>
|
|
101
|
+
* }
|
|
102
|
+
* </div>
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
class ForOtpInput extends FormUiControlBase {
|
|
106
|
+
#host = inject(ElementRef);
|
|
107
|
+
#document = inject(DOCUMENT);
|
|
108
|
+
#destroyRef = inject(DestroyRef);
|
|
109
|
+
#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
110
|
+
#field = inject(FOR_FIELD_CONTEXT, { optional: true });
|
|
111
|
+
/** The injected real `<input>`, once created in the browser. */
|
|
112
|
+
#inputEl = signal(null, /* @ts-ignore */
|
|
113
|
+
...(ngDevMode ? [{ debugName: "#inputEl" }] : /* istanbul ignore next */ []));
|
|
114
|
+
#focused = signal(false, /* @ts-ignore */
|
|
115
|
+
...(ngDevMode ? [{ debugName: "#focused" }] : /* istanbul ignore next */ []));
|
|
116
|
+
#selectionStart = signal(0, /* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "#selectionStart" }] : /* istanbul ignore next */ []));
|
|
118
|
+
#selectionEnd = signal(0, /* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [{ debugName: "#selectionEnd" }] : /* istanbul ignore next */ []));
|
|
120
|
+
#composing = false;
|
|
121
|
+
/**
|
|
122
|
+
* The current code. Required by `FormValueControl<string>`. Two-way bindable;
|
|
123
|
+
* its rendered length is clamped to `length()`.
|
|
124
|
+
*/
|
|
125
|
+
value = model('', /* @ts-ignore */
|
|
126
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
127
|
+
/** Number of characters / slots. */
|
|
128
|
+
length = input.required(/* @ts-ignore */
|
|
129
|
+
...(ngDevMode ? [{ debugName: "length" }] : /* istanbul ignore next */ []));
|
|
130
|
+
/** Allowed character class. Ignored when `allowedPattern` is set. Defaults to `'numeric'`. */
|
|
131
|
+
type = input('numeric', /* @ts-ignore */
|
|
132
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
133
|
+
/**
|
|
134
|
+
* Custom allowed-character RegExp, tested per typed/pasted character;
|
|
135
|
+
* overrides `type`. Named `allowedPattern` (not `pattern`) to avoid the
|
|
136
|
+
* reserved `FormUiControl.pattern` member — see the class JSDoc.
|
|
137
|
+
*/
|
|
138
|
+
allowedPattern = input(null, /* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "allowedPattern" }] : /* istanbul ignore next */ []));
|
|
140
|
+
/** Obscure entered characters in the slots (PIN entry); `value()` stays raw. */
|
|
141
|
+
mask = input(false, { ...(ngDevMode ? { debugName: "mask" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
142
|
+
/** Toggle `autocomplete="one-time-code"` for mobile SMS autofill. */
|
|
143
|
+
oneTimeCode = input(true, { ...(ngDevMode ? { debugName: "oneTimeCode" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
144
|
+
/** Rewrite pasted text before it fills the slots (e.g. strip separators). */
|
|
145
|
+
pasteTransformer = input(null, /* @ts-ignore */
|
|
146
|
+
...(ngDevMode ? [{ debugName: "pasteTransformer" }] : /* istanbul ignore next */ []));
|
|
147
|
+
/**
|
|
148
|
+
* Accessible name for the group, also reflected as `aria-label` on the
|
|
149
|
+
* injected real `<input>` whenever no field-provided `aria-labelledby`
|
|
150
|
+
* applies (standalone usage, or a `[forField]` without a label). Emits
|
|
151
|
+
* `aria-label` only when truthy; a field label always wins on the input.
|
|
152
|
+
*/
|
|
153
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
154
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
155
|
+
/** Fires when every slot is filled (by typing or paste). */
|
|
156
|
+
valueComplete = output();
|
|
157
|
+
/** Fires when an entered / pasted character is rejected by `type` / `allowedPattern`. */
|
|
158
|
+
valueInvalid = output();
|
|
159
|
+
/** The value clamped to `length()` — the source of truth for the slots. */
|
|
160
|
+
#clampedValue = computed(() => this.value().slice(0, this.length()), /* @ts-ignore */
|
|
161
|
+
...(ngDevMode ? [{ debugName: "#clampedValue" }] : /* istanbul ignore next */ []));
|
|
162
|
+
/** `true` when every slot is filled. */
|
|
163
|
+
complete = computed(() => this.#clampedValue().length === this.length(), /* @ts-ignore */
|
|
164
|
+
...(ngDevMode ? [{ debugName: "complete" }] : /* istanbul ignore next */ []));
|
|
165
|
+
/** The slot indices, for the consumer's `@for`. */
|
|
166
|
+
slots = computed(() => Array.from({ length: this.length() }, (_, i) => i), /* @ts-ignore */
|
|
167
|
+
...(ngDevMode ? [{ debugName: "slots" }] : /* istanbul ignore next */ []));
|
|
168
|
+
#inputMode = computed(() => inputModeForType(this.type()), /* @ts-ignore */
|
|
169
|
+
...(ngDevMode ? [{ debugName: "#inputMode" }] : /* istanbul ignore next */ []));
|
|
170
|
+
/** Predicate deciding whether a single character is allowed. */
|
|
171
|
+
#allowed = computed(() => {
|
|
172
|
+
const pattern = this.allowedPattern();
|
|
173
|
+
if (pattern) {
|
|
174
|
+
return (ch) => {
|
|
175
|
+
pattern.lastIndex = 0;
|
|
176
|
+
return pattern.test(ch);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const re = allowedCharForType(this.type());
|
|
180
|
+
return (ch) => re.test(ch);
|
|
181
|
+
}, /* @ts-ignore */
|
|
182
|
+
...(ngDevMode ? [{ debugName: "#allowed" }] : /* istanbul ignore next */ []));
|
|
183
|
+
constructor() {
|
|
184
|
+
super();
|
|
185
|
+
// Reflect the OTP-specific input attributes and ARIA onto the injected real
|
|
186
|
+
// input — not the role="group" host. The four form-state data-* booleans and
|
|
187
|
+
// the field association are reflected onto the same input by the base, via
|
|
188
|
+
// the fieldStateReflectionTarget() / fieldLabelledElement() overrides below.
|
|
189
|
+
// Runs once the input exists.
|
|
190
|
+
effect(() => {
|
|
191
|
+
const el = this.#inputEl();
|
|
192
|
+
if (!el) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
el.maxLength = this.length();
|
|
196
|
+
setAttr(el, 'inputmode', this.#inputMode());
|
|
197
|
+
setAttr(el, 'autocomplete', this.oneTimeCode() ? 'one-time-code' : 'off');
|
|
198
|
+
// Legacy iOS numeric-keypad hint; modern browsers honour inputmode.
|
|
199
|
+
setAttr(el, 'pattern', this.#inputMode() === 'numeric' ? '[0-9]*' : null);
|
|
200
|
+
setAttr(el, 'name', this.name() || null);
|
|
201
|
+
el.toggleAttribute('disabled', this.effectiveDisabled());
|
|
202
|
+
el.toggleAttribute('readonly', this.readonly());
|
|
203
|
+
setAttr(el, 'aria-label', this.#field?.labelledBy() ? null : this.ariaLabel() || null);
|
|
204
|
+
setAttr(el, 'aria-disabled', this.effectiveDisabled() ? 'true' : null);
|
|
205
|
+
setAttr(el, 'aria-readonly', this.readonly() ? 'true' : null);
|
|
206
|
+
setAttr(el, 'aria-required', this.required() ? 'true' : null);
|
|
207
|
+
setAttr(el, 'aria-invalid', this.invalid() ? 'true' : null);
|
|
208
|
+
setAttr(el, 'aria-busy', this.pending() ? 'true' : null);
|
|
209
|
+
el.toggleAttribute('data-disabled', this.effectiveDisabled());
|
|
210
|
+
el.toggleAttribute('data-readonly', this.readonly());
|
|
211
|
+
});
|
|
212
|
+
// Mirror external value writes (consumer `[(value)]` / `[formField]`) back
|
|
213
|
+
// to the input while it isn't focused. Live typing flows in through the
|
|
214
|
+
// `input` listener.
|
|
215
|
+
mirrorUnfocusedValue(this.#inputEl, this.#clampedValue);
|
|
216
|
+
// Create the single real input after hydration (browser only), so the
|
|
217
|
+
// server-rendered markup stays the group + slots and there is no hydration
|
|
218
|
+
// mismatch from a node the server never emitted.
|
|
219
|
+
afterNextRender(() => {
|
|
220
|
+
const el = this.#document.createElement('input');
|
|
221
|
+
el.type = 'text';
|
|
222
|
+
el.autocapitalize = 'none';
|
|
223
|
+
el.setAttribute('autocorrect', 'off');
|
|
224
|
+
el.spellcheck = false;
|
|
225
|
+
el.value = this.#clampedValue();
|
|
226
|
+
this.#host.nativeElement.appendChild(el);
|
|
227
|
+
el.addEventListener('input', () => this.#onInput());
|
|
228
|
+
el.addEventListener('compositionstart', () => this.#onCompositionStart());
|
|
229
|
+
el.addEventListener('compositionend', () => this.#onCompositionEnd());
|
|
230
|
+
el.addEventListener('paste', (event) => this.#onPaste(event));
|
|
231
|
+
el.addEventListener('focus', () => {
|
|
232
|
+
this.#focused.set(true);
|
|
233
|
+
this.#syncSelection();
|
|
234
|
+
});
|
|
235
|
+
el.addEventListener('blur', () => {
|
|
236
|
+
this.#focused.set(false);
|
|
237
|
+
this.markTouched();
|
|
238
|
+
});
|
|
239
|
+
el.addEventListener('click', () => this.#syncSelection());
|
|
240
|
+
el.addEventListener('keyup', () => this.#syncSelection());
|
|
241
|
+
el.addEventListener('select', () => this.#syncSelection());
|
|
242
|
+
this.#inputEl.set(el);
|
|
243
|
+
this.#destroyRef.onDestroy(() => el.remove());
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The injected real `<input>` is the focusable, submittable control, so the
|
|
248
|
+
* surrounding `[forField]` associates with it rather than the `role="group"`
|
|
249
|
+
* host. Returns `null` until the input is created (after hydration), at which
|
|
250
|
+
* point the field-wiring effect re-targets it. See the Select precedent.
|
|
251
|
+
*/
|
|
252
|
+
fieldLabelledElement() {
|
|
253
|
+
return this.#inputEl();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* The four form-state `data-*` booleans reflect onto the injected real
|
|
257
|
+
* `<input>`, alongside the OTP-specific `data-disabled` / `data-readonly`,
|
|
258
|
+
* rather than the `role="group"` host. `null` until the input exists.
|
|
259
|
+
*/
|
|
260
|
+
fieldStateReflectionTarget() {
|
|
261
|
+
return this.#inputEl();
|
|
262
|
+
}
|
|
263
|
+
/** Move focus to the real input. Implements `FormUiControl.focus`. */
|
|
264
|
+
focus(options) {
|
|
265
|
+
this.#inputEl()?.focus(options);
|
|
266
|
+
}
|
|
267
|
+
/** The character in slot `index` (masked when `mask`), or `null` when empty. */
|
|
268
|
+
charAt(index) {
|
|
269
|
+
const v = this.#clampedValue();
|
|
270
|
+
if (index < 0 || index >= v.length) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return this.mask() ? MASK_CHAR : v[index];
|
|
274
|
+
}
|
|
275
|
+
/** Whether slot `index` is the active caret position (or inside the selection). */
|
|
276
|
+
isActive(index) {
|
|
277
|
+
if (!this.#focused()) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
const len = this.length();
|
|
281
|
+
if (index < 0 || index >= len) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const start = this.#selectionStart();
|
|
285
|
+
const end = this.#selectionEnd();
|
|
286
|
+
if (start === end) {
|
|
287
|
+
return index === Math.min(start, len - 1);
|
|
288
|
+
}
|
|
289
|
+
return index >= start && index < end;
|
|
290
|
+
}
|
|
291
|
+
/** Whether slot `index` should render a fake caret. */
|
|
292
|
+
hasFakeCaret(index) {
|
|
293
|
+
return (this.#focused() &&
|
|
294
|
+
this.#selectionStart() === this.#selectionEnd() &&
|
|
295
|
+
this.isActive(index) &&
|
|
296
|
+
this.charAt(index) === null);
|
|
297
|
+
}
|
|
298
|
+
#onCompositionStart() {
|
|
299
|
+
this.#composing = true;
|
|
300
|
+
}
|
|
301
|
+
#onCompositionEnd() {
|
|
302
|
+
this.#composing = false;
|
|
303
|
+
this.#normalize();
|
|
304
|
+
}
|
|
305
|
+
#onInput() {
|
|
306
|
+
if (this.#composing) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
this.#normalize();
|
|
310
|
+
}
|
|
311
|
+
#normalize() {
|
|
312
|
+
const el = this.#inputEl();
|
|
313
|
+
if (!el) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (this.effectiveDisabled() || this.readonly()) {
|
|
317
|
+
el.value = this.#clampedValue();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const raw = el.value;
|
|
321
|
+
const { filtered, rejected } = this.#filter(raw);
|
|
322
|
+
const clamped = filtered.slice(0, this.length());
|
|
323
|
+
if (el.value !== clamped) {
|
|
324
|
+
el.value = clamped;
|
|
325
|
+
el.setSelectionRange(clamped.length, clamped.length);
|
|
326
|
+
}
|
|
327
|
+
this.#commit(clamped);
|
|
328
|
+
this.#syncSelection();
|
|
329
|
+
if (rejected) {
|
|
330
|
+
this.valueInvalid.emit({ value: raw });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
#onPaste(event) {
|
|
334
|
+
const el = this.#inputEl();
|
|
335
|
+
if (!el || this.effectiveDisabled() || this.readonly()) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
event.preventDefault();
|
|
339
|
+
const text = event.clipboardData?.getData('text') ?? '';
|
|
340
|
+
const transformer = this.pasteTransformer();
|
|
341
|
+
const transformed = transformer ? transformer(text) : text;
|
|
342
|
+
const { filtered, rejected } = this.#filter(transformed);
|
|
343
|
+
const clamped = filtered.slice(0, this.length());
|
|
344
|
+
el.value = clamped;
|
|
345
|
+
el.setSelectionRange(clamped.length, clamped.length);
|
|
346
|
+
this.#commit(clamped);
|
|
347
|
+
this.#syncSelection();
|
|
348
|
+
if (rejected) {
|
|
349
|
+
this.valueInvalid.emit({ value: text });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
#commit(next) {
|
|
353
|
+
const wasComplete = this.complete();
|
|
354
|
+
const prev = this.value();
|
|
355
|
+
this.value.set(next);
|
|
356
|
+
if (next.length === this.length() && (!wasComplete || next !== prev)) {
|
|
357
|
+
this.valueComplete.emit(next);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
#filter(raw) {
|
|
361
|
+
const test = this.#allowed();
|
|
362
|
+
let filtered = '';
|
|
363
|
+
let rejected = false;
|
|
364
|
+
for (const ch of raw) {
|
|
365
|
+
if (test(ch)) {
|
|
366
|
+
filtered += ch;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
rejected = true;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return { filtered, rejected };
|
|
373
|
+
}
|
|
374
|
+
#syncSelection() {
|
|
375
|
+
const el = this.#inputEl();
|
|
376
|
+
if (!el) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const len = this.#clampedValue().length;
|
|
380
|
+
const start = Math.min(el.selectionStart ?? 0, len);
|
|
381
|
+
const end = Math.min(el.selectionEnd ?? start, len);
|
|
382
|
+
this.#selectionStart.set(start);
|
|
383
|
+
this.#selectionEnd.set(end);
|
|
384
|
+
}
|
|
385
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForOtpInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
386
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForOtpInput, isStandalone: true, selector: "[forOtpInput]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: true, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, allowedPattern: { classPropertyName: "allowedPattern", publicName: "allowedPattern", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, oneTimeCode: { classPropertyName: "oneTimeCode", publicName: "oneTimeCode", isSignal: true, isRequired: false, transformFunction: null }, pasteTransformer: { classPropertyName: "pasteTransformer", publicName: "pasteTransformer", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", valueComplete: "valueComplete", valueInvalid: "valueInvalid" }, host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "ariaLabel() || null", "attr.data-complete": "complete() ? \"\" : null", "attr.data-disabled": "effectiveDisabled() ? \"\" : null" } }, providers: [{ provide: FOR_OTP_INPUT_CONTEXT, useExisting: ForOtpInput }], exportAs: ["forOtpInput"], usesInheritance: true, ngImport: i0 });
|
|
387
|
+
}
|
|
388
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForOtpInput, decorators: [{
|
|
389
|
+
type: Directive,
|
|
390
|
+
args: [{
|
|
391
|
+
selector: '[forOtpInput]',
|
|
392
|
+
exportAs: 'forOtpInput',
|
|
393
|
+
host: {
|
|
394
|
+
role: 'group',
|
|
395
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
396
|
+
'[attr.data-complete]': 'complete() ? "" : null',
|
|
397
|
+
'[attr.data-disabled]': 'effectiveDisabled() ? "" : null',
|
|
398
|
+
},
|
|
399
|
+
providers: [{ provide: FOR_OTP_INPUT_CONTEXT, useExisting: ForOtpInput }],
|
|
400
|
+
}]
|
|
401
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: true }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], allowedPattern: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedPattern", required: false }] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], oneTimeCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "oneTimeCode", required: false }] }], pasteTransformer: [{ type: i0.Input, args: [{ isSignal: true, alias: "pasteTransformer", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], valueComplete: [{ type: i0.Output, args: ["valueComplete"] }], valueInvalid: [{ type: i0.Output, args: ["valueInvalid"] }] } });
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* One slot of a `[forOtpInput]` — a pure **styling surface** painted over the
|
|
405
|
+
* single real input. Apply one per index
|
|
406
|
+
* inside the `[forOtpInput]` wrapper, passing the 0-based `index`. It renders
|
|
407
|
+
* nothing structural: it exposes the slot's character and active state, and
|
|
408
|
+
* reflects boolean `data-active` / `data-empty` for CSS.
|
|
409
|
+
*
|
|
410
|
+
* The slot has no click handler by design — the real input overlays the slots
|
|
411
|
+
* (the consumer styles it to fill the group), so pointer events land on the
|
|
412
|
+
* input and native caret positioning drives which slot is active.
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```html
|
|
416
|
+
* <div forOtpInputSlot [index]="i" #s="forOtpInputSlot">
|
|
417
|
+
* {{ s.char() }}
|
|
418
|
+
* @if (s.hasFakeCaret()) { <span class="caret"></span> }
|
|
419
|
+
* </div>
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
class ForOtpInputSlot {
|
|
423
|
+
ctx = injectOtpInputContext('ForOtpInputSlot');
|
|
424
|
+
/** This slot's position (0-based). */
|
|
425
|
+
index = input.required(/* @ts-ignore */
|
|
426
|
+
...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
|
|
427
|
+
/** The character in this slot, or `null` when empty (masked when `mask` is on). */
|
|
428
|
+
char = computed(() => this.ctx.charAt(this.index()), /* @ts-ignore */
|
|
429
|
+
...(ngDevMode ? [{ debugName: "char" }] : /* istanbul ignore next */ []));
|
|
430
|
+
/** Whether this slot is the active caret position. */
|
|
431
|
+
active = computed(() => this.ctx.isActive(this.index()), /* @ts-ignore */
|
|
432
|
+
...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
433
|
+
/** Whether to render a fake caret in this slot. */
|
|
434
|
+
hasFakeCaret = computed(() => this.ctx.hasFakeCaret(this.index()), /* @ts-ignore */
|
|
435
|
+
...(ngDevMode ? [{ debugName: "hasFakeCaret" }] : /* istanbul ignore next */ []));
|
|
436
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForOtpInputSlot, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
437
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForOtpInputSlot, isStandalone: true, selector: "[forOtpInputSlot]", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-active": "active() ? \"\" : null", "attr.data-empty": "char() === null ? \"\" : null" } }, exportAs: ["forOtpInputSlot"], ngImport: i0 });
|
|
438
|
+
}
|
|
439
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForOtpInputSlot, decorators: [{
|
|
440
|
+
type: Directive,
|
|
441
|
+
args: [{
|
|
442
|
+
selector: '[forOtpInputSlot]',
|
|
443
|
+
exportAs: 'forOtpInputSlot',
|
|
444
|
+
host: {
|
|
445
|
+
'[attr.data-active]': 'active() ? "" : null',
|
|
446
|
+
'[attr.data-empty]': 'char() === null ? "" : null',
|
|
447
|
+
},
|
|
448
|
+
}]
|
|
449
|
+
}], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }] } });
|
|
450
|
+
|
|
451
|
+
const FALLBACK = {};
|
|
452
|
+
const { token, provideDefaults } = createDefaults('FOR_OTP_INPUT_DEFAULTS', FALLBACK);
|
|
453
|
+
/** Token holding the resolved OTP-input defaults for the current scope. */
|
|
454
|
+
const FOR_OTP_INPUT_DEFAULTS = token;
|
|
455
|
+
/**
|
|
456
|
+
* Configures forty-cdk OTP-input defaults for this injector scope. Partial
|
|
457
|
+
* overrides inherit unspecified keys from the parent scope (or library defaults
|
|
458
|
+
* at the root).
|
|
459
|
+
*/
|
|
460
|
+
function provideForOtpInputDefaults(defaults = {}) {
|
|
461
|
+
return provideDefaults(defaults);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Exact public names of every `ForOtpInput` input, its models included. Spread it into the
|
|
466
|
+
* `inputs` array of a `hostDirectives` entry so a wrapper component re-exposes the
|
|
467
|
+
* primitive's full surface — the Signal Forms members `[formField]` binds among them —
|
|
468
|
+
* without hand-maintaining the list. Always spread into an inline object literal as shown
|
|
469
|
+
* below: the literal is what keeps the entry statically analyzable for consumers compiling
|
|
470
|
+
* against the published package. An anti-drift spec fails when this list no longer matches
|
|
471
|
+
* the directive's actual API. See `docs/wrapping-form-primitives.md` for both supported
|
|
472
|
+
* wrapping patterns.
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```ts
|
|
476
|
+
* @Component({
|
|
477
|
+
* selector: 'div[myOtpInput]',
|
|
478
|
+
* template: '',
|
|
479
|
+
* hostDirectives: [
|
|
480
|
+
* {
|
|
481
|
+
* directive: ForOtpInput,
|
|
482
|
+
* inputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS],
|
|
483
|
+
* outputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS],
|
|
484
|
+
* },
|
|
485
|
+
* ],
|
|
486
|
+
* })
|
|
487
|
+
* export class MyOtpInput {}
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
const FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS = [
|
|
491
|
+
'value',
|
|
492
|
+
'allowedPattern',
|
|
493
|
+
'ariaLabel',
|
|
494
|
+
'dirty',
|
|
495
|
+
'disabled',
|
|
496
|
+
'errors',
|
|
497
|
+
'invalid',
|
|
498
|
+
'length',
|
|
499
|
+
'mask',
|
|
500
|
+
'name',
|
|
501
|
+
'oneTimeCode',
|
|
502
|
+
'pasteTransformer',
|
|
503
|
+
'pending',
|
|
504
|
+
'readonly',
|
|
505
|
+
'required',
|
|
506
|
+
'type',
|
|
507
|
+
'touched',
|
|
508
|
+
];
|
|
509
|
+
/**
|
|
510
|
+
* Exact public names of every `ForOtpInput` output, the Signal Forms `touch` output
|
|
511
|
+
* included. Spread it into the `outputs` array of the same `hostDirectives` entry as
|
|
512
|
+
* {@link FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS}.
|
|
513
|
+
*/
|
|
514
|
+
const FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS = [
|
|
515
|
+
'valueChange',
|
|
516
|
+
'touchedChange',
|
|
517
|
+
'touch',
|
|
518
|
+
'valueComplete',
|
|
519
|
+
'valueInvalid',
|
|
520
|
+
];
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Generated bundle index. Do not edit.
|
|
524
|
+
*/
|
|
525
|
+
|
|
526
|
+
export { FOR_OTP_INPUT_CONTEXT, FOR_OTP_INPUT_DEFAULTS, FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS, FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS, ForOtpInput, ForOtpInputSlot, OTP_REGEXP_ONLY_CHARS, OTP_REGEXP_ONLY_DIGITS, OTP_REGEXP_ONLY_DIGITS_AND_CHARS, provideForOtpInputDefaults };
|
|
527
|
+
//# sourceMappingURL=forty-cdk-otp-input.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forty-cdk-otp-input.mjs","sources":["../../../projects/forty-cdk/otp-input/src/otp-input-context.ts","../../../projects/forty-cdk/otp-input/src/otp-patterns.ts","../../../projects/forty-cdk/otp-input/src/otp-input.ts","../../../projects/forty-cdk/otp-input/src/otp-input-slot.ts","../../../projects/forty-cdk/otp-input/src/otp-input-defaults.ts","../../../projects/forty-cdk/otp-input/src/otp-input-host-directive.ts","../../../projects/forty-cdk/otp-input/src/forty-cdk-otp-input.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\n/**\n * The coordination surface a `[forOtpInput]` exposes to its `[forOtpInputSlot]`\n * children. Each slot reads its own state through these methods, passing its\n * `index`. The methods read the directive's signals internally, so calling them\n * inside a slot's `computed` tracks the underlying state reactively.\n */\nexport interface ForOtpInputContext {\n /** The configured number of slots (the `length` input). */\n readonly length: Signal<number>;\n /**\n * The character displayed in slot `index` — masked when `mask` is on — or\n * `null` when the slot is empty.\n */\n charAt(index: number): string | null;\n /**\n * Whether slot `index` is the active caret position (or inside the current\n * selection range). At most one slot is active for a collapsed caret.\n */\n isActive(index: number): boolean;\n /**\n * Whether slot `index` should render a fake caret: it is the active, empty\n * slot while the input is focused with a collapsed caret.\n */\n hasFakeCaret(index: number): boolean;\n}\n\n/** Injection token for the `[forOtpInput]` coordination surface the slots read. */\nexport const FOR_OTP_INPUT_CONTEXT = new InjectionToken<ForOtpInputContext>(\n 'FOR_OTP_INPUT_CONTEXT',\n);\n\n/**\n * Resolve the surrounding `[forOtpInput]` coordination context, or throw a\n * descriptive error. A `[forOtpInputSlot]` is only meaningful inside a\n * `[forOtpInput]`.\n */\nexport function injectOtpInputContext(piece: string): ForOtpInputContext {\n const ctx = inject(FOR_OTP_INPUT_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/otp-input] ${piece} must be used inside a [forOtpInput].`);\n }\n return ctx;\n}\n","/**\n * Allowed-character class for an OTP / PIN input. Drives both the per-character\n * filter (rejected characters are dropped and fire `valueInvalid`) and the\n * derived `inputmode`. Override entirely with a custom `pattern` RegExp.\n */\nexport type OtpInputType = 'numeric' | 'alphanumeric' | 'alphabetic';\n\n/**\n * Matches a value made up only of digits. Bind to `[pattern]` for a digit-only\n * code; equivalent to the default `type=\"numeric\"`.\n */\nexport const OTP_REGEXP_ONLY_DIGITS = /^[0-9]+$/;\n\n/** Matches a value made up only of latin letters (`type=\"alphabetic\"`). */\nexport const OTP_REGEXP_ONLY_CHARS = /^[a-zA-Z]+$/;\n\n/** Matches a value made up only of digits and latin letters (`type=\"alphanumeric\"`). */\nexport const OTP_REGEXP_ONLY_DIGITS_AND_CHARS = /^[a-zA-Z0-9]+$/;\n\nconst SINGLE_CHAR_BY_TYPE: Record<OtpInputType, RegExp> = {\n numeric: /[0-9]/,\n alphanumeric: /[a-zA-Z0-9]/,\n alphabetic: /[a-zA-Z]/,\n};\n\n/**\n * The single-character matcher for a built-in `type`. Each returned RegExp is\n * non-global, so repeated `.test()` calls are stateless.\n */\nexport function allowedCharForType(type: OtpInputType): RegExp {\n return SINGLE_CHAR_BY_TYPE[type];\n}\n\n/**\n * The `inputmode` a built-in `type` maps to: `numeric` keeps the digits-only\n * keypad, the letter classes fall back to the full text keyboard.\n */\nexport function inputModeForType(type: OtpInputType): 'numeric' | 'text' {\n return type === 'numeric' ? 'numeric' : 'text';\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n DOCUMENT,\n effect,\n ElementRef,\n inject,\n input,\n model,\n output,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { FOR_FIELD_CONTEXT, FormUiControlBase, mirrorUnfocusedValue } from 'forty-cdk/core';\nimport { FOR_OTP_INPUT_CONTEXT, type ForOtpInputContext } from './otp-input-context';\nimport { allowedCharForType, inputModeForType, type OtpInputType } from './otp-patterns';\n\nconst MASK_CHAR = '•';\n\nfunction setAttr(el: HTMLElement, name: string, value: string | null): void {\n if (value === null) {\n el.removeAttribute(name);\n } else if (el.getAttribute(name) !== value) {\n el.setAttribute(name, value);\n }\n}\n\n/**\n * Headless OTP / PIN input following the **single-input** model: one real\n * `<input maxlength=N>` carries the whole code as a `string`, and the\n * `[forOtpInputSlot]` pieces are a pure\n * styling surface painted over it. There is **no** WAI-ARIA APG pattern for\n * OTP; this gives the cleanest screen-reader experience (one ordinary text\n * field, not \"edit text, 1 of 6\" announced N times), native mobile SMS autofill\n * via `autocomplete=\"one-time-code\"`, and native paste / caret / selection.\n *\n * Apply `[forOtpInput]` on a wrapper element — it becomes a `role=\"group\"` and\n * the directive injects the single visually-hidden-but-interactive `<input>`\n * inside it (the consumer styles that input to overlay the slots). It\n * implements Angular's `FormValueControl<string>` from `@angular/forms/signals`,\n * so it auto-wires with `[formField]` and auto-associates inside a `[forField]`\n * (label / description / error) with no extra markup.\n *\n * Because the focusable, submittable control is the injected `<input>` and not\n * the `role=\"group\"` host, `ForOtpInput` redirects `FormUiControlBase`'s\n * host-targeted helpers onto that input: it overrides `fieldLabelledElement()`\n * (so the field association lands on the input) and `fieldStateReflectionTarget()`\n * (so the form-state `data-*` reflect there), and reflects the OTP-specific input\n * attributes (`maxLength` / `inputmode` / `autocomplete` / `pattern` / `name`)\n * and ARIA on the input itself.\n *\n * The host gets `data-complete` (while every slot is filled) and `data-disabled`\n * for CSS hooks; the real input carries `data-disabled` / `data-readonly` plus\n * `data-touched` / `data-dirty` / `data-pending` / `data-invalid`.\n *\n * > **`allowedPattern`, not `pattern`.** The custom allowed-character RegExp is\n * > named `allowedPattern` because `FormUiControl.pattern` is reserved by Signal\n * > Forms for an array of validation patterns the `[formField]` directive binds\n * > in — reusing the name would both break the `implements` contract and let the\n * > field overwrite the character filter.\n *\n * @example\n * ```html\n * <div forOtpInput [(value)]=\"code\" [length]=\"6\" type=\"numeric\" ariaLabel=\"Verification code\" #otp=\"forOtpInput\">\n * @for (i of otp.slots(); track i) {\n * <div forOtpInputSlot [index]=\"i\" #s=\"forOtpInputSlot\">\n * {{ s.char() }}\n * @if (s.hasFakeCaret()) { <span class=\"caret\"></span> }\n * </div>\n * }\n * </div>\n * ```\n */\n@Directive({\n selector: '[forOtpInput]',\n exportAs: 'forOtpInput',\n host: {\n role: 'group',\n '[attr.aria-label]': 'ariaLabel() || null',\n '[attr.data-complete]': 'complete() ? \"\" : null',\n '[attr.data-disabled]': 'effectiveDisabled() ? \"\" : null',\n },\n providers: [{ provide: FOR_OTP_INPUT_CONTEXT, useExisting: ForOtpInput }],\n})\nexport class ForOtpInput\n extends FormUiControlBase\n implements FormValueControl<string>, ForOtpInputContext\n{\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #document = inject(DOCUMENT);\n readonly #destroyRef = inject(DestroyRef);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n readonly #field = inject(FOR_FIELD_CONTEXT, { optional: true });\n\n /** The injected real `<input>`, once created in the browser. */\n readonly #inputEl = signal<HTMLInputElement | null>(null);\n readonly #focused = signal(false);\n readonly #selectionStart = signal(0);\n readonly #selectionEnd = signal(0);\n\n #composing = false;\n\n /**\n * The current code. Required by `FormValueControl<string>`. Two-way bindable;\n * its rendered length is clamped to `length()`.\n */\n readonly value = model<string>('');\n\n /** Number of characters / slots. */\n readonly length = input.required<number>();\n\n /** Allowed character class. Ignored when `allowedPattern` is set. Defaults to `'numeric'`. */\n readonly type = input<OtpInputType>('numeric');\n\n /**\n * Custom allowed-character RegExp, tested per typed/pasted character;\n * overrides `type`. Named `allowedPattern` (not `pattern`) to avoid the\n * reserved `FormUiControl.pattern` member — see the class JSDoc.\n */\n readonly allowedPattern = input<RegExp | null>(null);\n\n /** Obscure entered characters in the slots (PIN entry); `value()` stays raw. */\n readonly mask = input(false, { transform: booleanAttribute });\n\n /** Toggle `autocomplete=\"one-time-code\"` for mobile SMS autofill. */\n readonly oneTimeCode = input(true, { transform: booleanAttribute });\n\n /** Rewrite pasted text before it fills the slots (e.g. strip separators). */\n readonly pasteTransformer = input<((pasted: string) => string) | null>(null);\n\n /**\n * Accessible name for the group, also reflected as `aria-label` on the\n * injected real `<input>` whenever no field-provided `aria-labelledby`\n * applies (standalone usage, or a `[forField]` without a label). Emits\n * `aria-label` only when truthy; a field label always wins on the input.\n */\n readonly ariaLabel = input<string | null>(null);\n\n /** Fires when every slot is filled (by typing or paste). */\n readonly valueComplete = output<string>();\n\n /** Fires when an entered / pasted character is rejected by `type` / `allowedPattern`. */\n readonly valueInvalid = output<{ value: string }>();\n\n /** The value clamped to `length()` — the source of truth for the slots. */\n readonly #clampedValue = computed(() => this.value().slice(0, this.length()));\n\n /** `true` when every slot is filled. */\n readonly complete = computed(() => this.#clampedValue().length === this.length());\n\n /** The slot indices, for the consumer's `@for`. */\n readonly slots = computed<readonly number[]>(() =>\n Array.from({ length: this.length() }, (_, i) => i),\n );\n\n readonly #inputMode = computed(() => inputModeForType(this.type()));\n\n /** Predicate deciding whether a single character is allowed. */\n readonly #allowed = computed<(ch: string) => boolean>(() => {\n const pattern = this.allowedPattern();\n if (pattern) {\n return (ch: string) => {\n pattern.lastIndex = 0;\n return pattern.test(ch);\n };\n }\n const re = allowedCharForType(this.type());\n return (ch: string) => re.test(ch);\n });\n\n constructor() {\n super();\n\n // Reflect the OTP-specific input attributes and ARIA onto the injected real\n // input — not the role=\"group\" host. The four form-state data-* booleans and\n // the field association are reflected onto the same input by the base, via\n // the fieldStateReflectionTarget() / fieldLabelledElement() overrides below.\n // Runs once the input exists.\n effect(() => {\n const el = this.#inputEl();\n if (!el) {\n return;\n }\n el.maxLength = this.length();\n setAttr(el, 'inputmode', this.#inputMode());\n setAttr(el, 'autocomplete', this.oneTimeCode() ? 'one-time-code' : 'off');\n // Legacy iOS numeric-keypad hint; modern browsers honour inputmode.\n setAttr(el, 'pattern', this.#inputMode() === 'numeric' ? '[0-9]*' : null);\n setAttr(el, 'name', this.name() || null);\n el.toggleAttribute('disabled', this.effectiveDisabled());\n el.toggleAttribute('readonly', this.readonly());\n setAttr(el, 'aria-label', this.#field?.labelledBy() ? null : this.ariaLabel() || null);\n setAttr(el, 'aria-disabled', this.effectiveDisabled() ? 'true' : null);\n setAttr(el, 'aria-readonly', this.readonly() ? 'true' : null);\n setAttr(el, 'aria-required', this.required() ? 'true' : null);\n setAttr(el, 'aria-invalid', this.invalid() ? 'true' : null);\n setAttr(el, 'aria-busy', this.pending() ? 'true' : null);\n el.toggleAttribute('data-disabled', this.effectiveDisabled());\n el.toggleAttribute('data-readonly', this.readonly());\n });\n\n // Mirror external value writes (consumer `[(value)]` / `[formField]`) back\n // to the input while it isn't focused. Live typing flows in through the\n // `input` listener.\n mirrorUnfocusedValue(this.#inputEl, this.#clampedValue);\n\n // Create the single real input after hydration (browser only), so the\n // server-rendered markup stays the group + slots and there is no hydration\n // mismatch from a node the server never emitted.\n afterNextRender(() => {\n const el = this.#document.createElement('input');\n el.type = 'text';\n el.autocapitalize = 'none';\n el.setAttribute('autocorrect', 'off');\n el.spellcheck = false;\n el.value = this.#clampedValue();\n this.#host.nativeElement.appendChild(el);\n\n el.addEventListener('input', () => this.#onInput());\n el.addEventListener('compositionstart', () => this.#onCompositionStart());\n el.addEventListener('compositionend', () => this.#onCompositionEnd());\n el.addEventListener('paste', (event) => this.#onPaste(event));\n el.addEventListener('focus', () => {\n this.#focused.set(true);\n this.#syncSelection();\n });\n el.addEventListener('blur', () => {\n this.#focused.set(false);\n this.markTouched();\n });\n el.addEventListener('click', () => this.#syncSelection());\n el.addEventListener('keyup', () => this.#syncSelection());\n el.addEventListener('select', () => this.#syncSelection());\n\n this.#inputEl.set(el);\n\n this.#destroyRef.onDestroy(() => el.remove());\n });\n }\n\n /**\n * The injected real `<input>` is the focusable, submittable control, so the\n * surrounding `[forField]` associates with it rather than the `role=\"group\"`\n * host. Returns `null` until the input is created (after hydration), at which\n * point the field-wiring effect re-targets it. See the Select precedent.\n */\n protected override fieldLabelledElement(): HTMLElement | null {\n return this.#inputEl();\n }\n\n /**\n * The four form-state `data-*` booleans reflect onto the injected real\n * `<input>`, alongside the OTP-specific `data-disabled` / `data-readonly`,\n * rather than the `role=\"group\"` host. `null` until the input exists.\n */\n protected override fieldStateReflectionTarget(): HTMLElement | null {\n return this.#inputEl();\n }\n\n /** Move focus to the real input. Implements `FormUiControl.focus`. */\n focus(options?: FocusOptions): void {\n this.#inputEl()?.focus(options);\n }\n\n /** The character in slot `index` (masked when `mask`), or `null` when empty. */\n charAt(index: number): string | null {\n const v = this.#clampedValue();\n if (index < 0 || index >= v.length) {\n return null;\n }\n return this.mask() ? MASK_CHAR : v[index]!;\n }\n\n /** Whether slot `index` is the active caret position (or inside the selection). */\n isActive(index: number): boolean {\n if (!this.#focused()) {\n return false;\n }\n const len = this.length();\n if (index < 0 || index >= len) {\n return false;\n }\n const start = this.#selectionStart();\n const end = this.#selectionEnd();\n if (start === end) {\n return index === Math.min(start, len - 1);\n }\n return index >= start && index < end;\n }\n\n /** Whether slot `index` should render a fake caret. */\n hasFakeCaret(index: number): boolean {\n return (\n this.#focused() &&\n this.#selectionStart() === this.#selectionEnd() &&\n this.isActive(index) &&\n this.charAt(index) === null\n );\n }\n\n #onCompositionStart(): void {\n this.#composing = true;\n }\n\n #onCompositionEnd(): void {\n this.#composing = false;\n this.#normalize();\n }\n\n #onInput(): void {\n if (this.#composing) {\n return;\n }\n this.#normalize();\n }\n\n #normalize(): void {\n const el = this.#inputEl();\n if (!el) {\n return;\n }\n if (this.effectiveDisabled() || this.readonly()) {\n el.value = this.#clampedValue();\n return;\n }\n const raw = el.value;\n const { filtered, rejected } = this.#filter(raw);\n const clamped = filtered.slice(0, this.length());\n if (el.value !== clamped) {\n el.value = clamped;\n el.setSelectionRange(clamped.length, clamped.length);\n }\n this.#commit(clamped);\n this.#syncSelection();\n if (rejected) {\n this.valueInvalid.emit({ value: raw });\n }\n }\n\n #onPaste(event: ClipboardEvent): void {\n const el = this.#inputEl();\n if (!el || this.effectiveDisabled() || this.readonly()) {\n return;\n }\n event.preventDefault();\n const text = event.clipboardData?.getData('text') ?? '';\n const transformer = this.pasteTransformer();\n const transformed = transformer ? transformer(text) : text;\n const { filtered, rejected } = this.#filter(transformed);\n const clamped = filtered.slice(0, this.length());\n el.value = clamped;\n el.setSelectionRange(clamped.length, clamped.length);\n this.#commit(clamped);\n this.#syncSelection();\n if (rejected) {\n this.valueInvalid.emit({ value: text });\n }\n }\n\n #commit(next: string): void {\n const wasComplete = this.complete();\n const prev = this.value();\n this.value.set(next);\n if (next.length === this.length() && (!wasComplete || next !== prev)) {\n this.valueComplete.emit(next);\n }\n }\n\n #filter(raw: string): { filtered: string; rejected: boolean } {\n const test = this.#allowed();\n let filtered = '';\n let rejected = false;\n for (const ch of raw) {\n if (test(ch)) {\n filtered += ch;\n } else {\n rejected = true;\n }\n }\n return { filtered, rejected };\n }\n\n #syncSelection(): void {\n const el = this.#inputEl();\n if (!el) {\n return;\n }\n const len = this.#clampedValue().length;\n const start = Math.min(el.selectionStart ?? 0, len);\n const end = Math.min(el.selectionEnd ?? start, len);\n this.#selectionStart.set(start);\n this.#selectionEnd.set(end);\n }\n}\n","import { computed, Directive, input } from '@angular/core';\n\nimport { injectOtpInputContext } from './otp-input-context';\n\n/**\n * One slot of a `[forOtpInput]` — a pure **styling surface** painted over the\n * single real input. Apply one per index\n * inside the `[forOtpInput]` wrapper, passing the 0-based `index`. It renders\n * nothing structural: it exposes the slot's character and active state, and\n * reflects boolean `data-active` / `data-empty` for CSS.\n *\n * The slot has no click handler by design — the real input overlays the slots\n * (the consumer styles it to fill the group), so pointer events land on the\n * input and native caret positioning drives which slot is active.\n *\n * @example\n * ```html\n * <div forOtpInputSlot [index]=\"i\" #s=\"forOtpInputSlot\">\n * {{ s.char() }}\n * @if (s.hasFakeCaret()) { <span class=\"caret\"></span> }\n * </div>\n * ```\n */\n@Directive({\n selector: '[forOtpInputSlot]',\n exportAs: 'forOtpInputSlot',\n host: {\n '[attr.data-active]': 'active() ? \"\" : null',\n '[attr.data-empty]': 'char() === null ? \"\" : null',\n },\n})\nexport class ForOtpInputSlot {\n protected readonly ctx = injectOtpInputContext('ForOtpInputSlot');\n\n /** This slot's position (0-based). */\n readonly index = input.required<number>();\n\n /** The character in this slot, or `null` when empty (masked when `mask` is on). */\n readonly char = computed<string | null>(() => this.ctx.charAt(this.index()));\n\n /** Whether this slot is the active caret position. */\n readonly active = computed<boolean>(() => this.ctx.isActive(this.index()));\n\n /** Whether to render a fake caret in this slot. */\n readonly hasFakeCaret = computed<boolean>(() => this.ctx.hasFakeCaret(this.index()));\n}\n","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant `[forOtpInput]` controls in the surrounding\n * injector scope. Configure with `provideForOtpInputDefaults`. The shape is a\n * stub today — present so future per-scope tuning can land without churning the\n * public surface.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ForOtpInputDefaults {}\n\nconst FALLBACK: ForOtpInputDefaults = {};\n\nconst { token, provideDefaults } = createDefaults<ForOtpInputDefaults>(\n 'FOR_OTP_INPUT_DEFAULTS',\n FALLBACK,\n);\n\n/** Token holding the resolved OTP-input defaults for the current scope. */\nexport const FOR_OTP_INPUT_DEFAULTS = token;\n\n/**\n * Configures forty-cdk OTP-input defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library defaults\n * at the root).\n */\nexport function provideForOtpInputDefaults(\n defaults: Partial<ForOtpInputDefaults> = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","/**\n * Exact public names of every `ForOtpInput` input, its models included. Spread it into the\n * `inputs` array of a `hostDirectives` entry so a wrapper component re-exposes the\n * primitive's full surface — the Signal Forms members `[formField]` binds among them —\n * without hand-maintaining the list. Always spread into an inline object literal as shown\n * below: the literal is what keeps the entry statically analyzable for consumers compiling\n * against the published package. An anti-drift spec fails when this list no longer matches\n * the directive's actual API. See `docs/wrapping-form-primitives.md` for both supported\n * wrapping patterns.\n *\n * @example\n * ```ts\n * @Component({\n * selector: 'div[myOtpInput]',\n * template: '',\n * hostDirectives: [\n * {\n * directive: ForOtpInput,\n * inputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS],\n * outputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS],\n * },\n * ],\n * })\n * export class MyOtpInput {}\n * ```\n */\nexport const FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS = [\n 'value',\n 'allowedPattern',\n 'ariaLabel',\n 'dirty',\n 'disabled',\n 'errors',\n 'invalid',\n 'length',\n 'mask',\n 'name',\n 'oneTimeCode',\n 'pasteTransformer',\n 'pending',\n 'readonly',\n 'required',\n 'type',\n 'touched',\n] as const;\n\n/**\n * Exact public names of every `ForOtpInput` output, the Signal Forms `touch` output\n * included. Spread it into the `outputs` array of the same `hostDirectives` entry as\n * {@link FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS}.\n */\nexport const FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS = [\n 'valueChange',\n 'touchedChange',\n 'touch',\n 'valueComplete',\n 'valueInvalid',\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|