@xui/input-otp 2.0.0-alpha.20 → 2.0.0-alpha.22
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.
|
@@ -13,12 +13,17 @@ import { xui } from '@xui/core';
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
class XuiInputOtp {
|
|
16
|
+
/** Extra classes, merged into the component's own rather than replacing them. */
|
|
16
17
|
class = input('', /* @ts-ignore */
|
|
17
18
|
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
19
|
+
/** How many characters the code has, and so how many slots are drawn. */
|
|
18
20
|
length = input(6, { ...(ngDevMode ? { debugName: "length" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
21
|
+
/** The code entered so far — a single string, not one value per slot. Two-way bindable with `[(value)]`. */
|
|
19
22
|
value = model('', /* @ts-ignore */
|
|
20
23
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
24
|
+
/** Block entry and dim the slots. */
|
|
21
25
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
26
|
+
/** `numeric` restricts entry to digits and asks for the numeric keypad; `text` accepts anything. */
|
|
22
27
|
inputType = input('numeric', /* @ts-ignore */
|
|
23
28
|
...(ngDevMode ? [{ debugName: "inputType" }] : /* istanbul ignore next */ []));
|
|
24
29
|
/** Render each filled slot as a dot instead of the character. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xui-input-otp.mjs","sources":["../../../../../../libs/ui/input-otp/xui/src/lib/input-otp.ts","../../../../../../libs/ui/input-otp/xui/src/index.ts","../../../../../../libs/ui/input-otp/xui/src/xui-input-otp.ts"],"sourcesContent":["import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n model,\n numberAttribute,\n output,\n signal,\n viewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { xui } from '@xui/core';\nimport type { ClassValue } from 'clsx';\n\n/**\n * A segmented one-time-code / PIN field. A single hidden input drives the whole\n * row — so native caret, selection, paste and autofill (`one-time-code`) all\n * work — while the visible slots render each character. `value` is two-way\n * bindable; `completed` fires once every slot is filled.\n *\n * ```html\n * <xui-input-otp [length]=\"6\" [(value)]=\"code\" (completed)=\"verify($event)\" />\n * ```\n */\n@Component({\n selector: 'xui-input-otp',\n template: `\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div [class]=\"rowClass()\" (click)=\"focus()\">\n @for (i of slots(); track i) {\n <div [class]=\"slotClass(i)\">\n @if (charAt(i)) {\n <span>{{ mask() ? '•' : charAt(i) }}</span>\n } @else if (showCaret(i)) {\n <span class=\"bg-foreground h-4 w-px animate-pulse\"></span>\n }\n </div>\n }\n </div>\n\n <input\n #field\n class=\"absolute inset-0 h-full w-full cursor-default opacity-0 outline-none\"\n [value]=\"value()\"\n [attr.inputmode]=\"inputType() === 'numeric' ? 'numeric' : 'text'\"\n [attr.maxlength]=\"length()\"\n [disabled]=\"disabled()\"\n autocomplete=\"one-time-code\"\n aria-label=\"One-time code\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n />\n `,\n host: {\n '[class]': 'computedClass()'\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class XuiInputOtp {\n readonly class = input<ClassValue>('');\n readonly length = input<number, NumberInput>(6, { transform: numberAttribute });\n readonly value = model<string>('');\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n readonly inputType = input<'numeric' | 'text'>('numeric');\n /** Render each filled slot as a dot instead of the character. */\n readonly mask = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n /** Fired once every slot is filled. */\n readonly completed = output<string>();\n\n private readonly field = viewChild.required<ElementRef<HTMLInputElement>>('field');\n protected readonly focused = signal(false);\n\n protected readonly slots = computed(() => Array.from({ length: this.length() }, (_, i) => i));\n\n protected charAt(index: number): string {\n return this.value()[index] ?? '';\n }\n\n /** The caret sits in the first empty slot while focused. */\n protected showCaret(index: number): boolean {\n return this.focused() && index === this.value().length && index < this.length();\n }\n\n protected focus(): void {\n if (!this.disabled()) {\n this.field().nativeElement.focus();\n }\n }\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n const cleaned = (this.inputType() === 'numeric' ? raw.replace(/\\D/g, '') : raw).slice(0, this.length());\n this.value.set(cleaned);\n // Keep the native input in sync when sanitising dropped characters.\n (event.target as HTMLInputElement).value = cleaned;\n if (cleaned.length === this.length()) {\n this.completed.emit(cleaned);\n }\n }\n\n protected readonly computedClass = computed(() => xui('relative inline-flex', this.class()));\n protected readonly rowClass = computed(() => xui('flex items-center gap-2'));\n\n protected slotClass(index: number): string {\n const active = this.focused() && index === this.value().length;\n return xui(\n 'flex size-(--control-height-lg) items-center justify-center rounded-md border text-lg font-medium tabular-nums transition-colors',\n 'border-border bg-surface text-foreground',\n active && 'border-primary ring-primary/30 ring-2',\n this.disabled() && 'opacity-50'\n );\n }\n}\n","import { XuiInputOtp } from './lib/input-otp';\n\nexport * from './lib/input-otp';\n\nexport const XuiInputOtpImports = [XuiInputOtp] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAkBA;;;;;;;;;AASG;MAqCU,WAAW,CAAA
|
|
1
|
+
{"version":3,"file":"xui-input-otp.mjs","sources":["../../../../../../libs/ui/input-otp/xui/src/lib/input-otp.ts","../../../../../../libs/ui/input-otp/xui/src/index.ts","../../../../../../libs/ui/input-otp/xui/src/xui-input-otp.ts"],"sourcesContent":["import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n model,\n numberAttribute,\n output,\n signal,\n viewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { xui } from '@xui/core';\nimport type { ClassValue } from 'clsx';\n\n/**\n * A segmented one-time-code / PIN field. A single hidden input drives the whole\n * row — so native caret, selection, paste and autofill (`one-time-code`) all\n * work — while the visible slots render each character. `value` is two-way\n * bindable; `completed` fires once every slot is filled.\n *\n * ```html\n * <xui-input-otp [length]=\"6\" [(value)]=\"code\" (completed)=\"verify($event)\" />\n * ```\n */\n@Component({\n selector: 'xui-input-otp',\n template: `\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div [class]=\"rowClass()\" (click)=\"focus()\">\n @for (i of slots(); track i) {\n <div [class]=\"slotClass(i)\">\n @if (charAt(i)) {\n <span>{{ mask() ? '•' : charAt(i) }}</span>\n } @else if (showCaret(i)) {\n <span class=\"bg-foreground h-4 w-px animate-pulse\"></span>\n }\n </div>\n }\n </div>\n\n <input\n #field\n class=\"absolute inset-0 h-full w-full cursor-default opacity-0 outline-none\"\n [value]=\"value()\"\n [attr.inputmode]=\"inputType() === 'numeric' ? 'numeric' : 'text'\"\n [attr.maxlength]=\"length()\"\n [disabled]=\"disabled()\"\n autocomplete=\"one-time-code\"\n aria-label=\"One-time code\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n />\n `,\n host: {\n '[class]': 'computedClass()'\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class XuiInputOtp {\n /** Extra classes, merged into the component's own rather than replacing them. */\n readonly class = input<ClassValue>('');\n /** How many characters the code has, and so how many slots are drawn. */\n readonly length = input<number, NumberInput>(6, { transform: numberAttribute });\n /** The code entered so far — a single string, not one value per slot. Two-way bindable with `[(value)]`. */\n readonly value = model<string>('');\n /** Block entry and dim the slots. */\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n /** `numeric` restricts entry to digits and asks for the numeric keypad; `text` accepts anything. */\n readonly inputType = input<'numeric' | 'text'>('numeric');\n /** Render each filled slot as a dot instead of the character. */\n readonly mask = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n /** Fired once every slot is filled. */\n readonly completed = output<string>();\n\n private readonly field = viewChild.required<ElementRef<HTMLInputElement>>('field');\n protected readonly focused = signal(false);\n\n protected readonly slots = computed(() => Array.from({ length: this.length() }, (_, i) => i));\n\n protected charAt(index: number): string {\n return this.value()[index] ?? '';\n }\n\n /** The caret sits in the first empty slot while focused. */\n protected showCaret(index: number): boolean {\n return this.focused() && index === this.value().length && index < this.length();\n }\n\n protected focus(): void {\n if (!this.disabled()) {\n this.field().nativeElement.focus();\n }\n }\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n const cleaned = (this.inputType() === 'numeric' ? raw.replace(/\\D/g, '') : raw).slice(0, this.length());\n this.value.set(cleaned);\n // Keep the native input in sync when sanitising dropped characters.\n (event.target as HTMLInputElement).value = cleaned;\n if (cleaned.length === this.length()) {\n this.completed.emit(cleaned);\n }\n }\n\n protected readonly computedClass = computed(() => xui('relative inline-flex', this.class()));\n protected readonly rowClass = computed(() => xui('flex items-center gap-2'));\n\n protected slotClass(index: number): string {\n const active = this.focused() && index === this.value().length;\n return xui(\n 'flex size-(--control-height-lg) items-center justify-center rounded-md border text-lg font-medium tabular-nums transition-colors',\n 'border-border bg-surface text-foreground',\n active && 'border-primary ring-primary/30 ring-2',\n this.disabled() && 'opacity-50'\n );\n }\n}\n","import { XuiInputOtp } from './lib/input-otp';\n\nexport * from './lib/input-otp';\n\nexport const XuiInputOtpImports = [XuiInputOtp] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xui/input-otp",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.22",
|
|
4
4
|
"description": "Modern Angular 22 UI Library based on TailwindCSS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@angular/cdk": "22",
|
|
40
40
|
"@angular/core": "22",
|
|
41
|
-
"@xui/core": "2.0.0-alpha.
|
|
41
|
+
"@xui/core": "2.0.0-alpha.22",
|
|
42
42
|
"clsx": "^2.1.1"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
package/types/xui-input-otp.d.ts
CHANGED
|
@@ -13,10 +13,15 @@ import { ClassValue } from 'clsx';
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
declare class XuiInputOtp {
|
|
16
|
+
/** Extra classes, merged into the component's own rather than replacing them. */
|
|
16
17
|
readonly class: _angular_core.InputSignal<ClassValue>;
|
|
18
|
+
/** How many characters the code has, and so how many slots are drawn. */
|
|
17
19
|
readonly length: _angular_core.InputSignalWithTransform<number, NumberInput>;
|
|
20
|
+
/** The code entered so far — a single string, not one value per slot. Two-way bindable with `[(value)]`. */
|
|
18
21
|
readonly value: _angular_core.ModelSignal<string>;
|
|
22
|
+
/** Block entry and dim the slots. */
|
|
19
23
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
|
|
24
|
+
/** `numeric` restricts entry to digits and asks for the numeric keypad; `text` accepts anything. */
|
|
20
25
|
readonly inputType: _angular_core.InputSignal<"numeric" | "text">;
|
|
21
26
|
/** Render each filled slot as a dot instead of the character. */
|
|
22
27
|
readonly mask: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
|