@progress/kendo-angular-inputs
Version:
Kendo UI for Angular Inputs Package - Everything you need to build professional form functionality (Checkbox, ColorGradient, ColorPalette, ColorPicker, FlatColorPicker, FormField, MaskedTextBox, NumericTextBox, RadioButton, RangeSlider, Slider, Switch, Te
931 lines (930 loc) • 34.9 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Injector, Input, NgZone, Output, QueryList, Renderer2, ViewChildren, forwardRef } from "@angular/core";
import { SharedInputEventsDirective } from "../shared/shared-events.directive";
import { NgFor, NgIf } from "@angular/common";
import { KendoInput, Keys, hasObservers, isPresent } from "@progress/kendo-angular-common";
import { TextBoxComponent } from "../textbox/textbox.component";
import { NG_VALUE_ACCESSOR, NgControl } from "@angular/forms";
import { SIZE_MAP, areSame, replaceMessagePlaceholder } from "../common/utils";
import { L10N_PREFIX, LocalizationService } from "@progress/kendo-angular-l10n";
import { OTPInputSeparatorComponent } from "./otpinput-separator.component";
import { take } from "rxjs/operators";
import { LocalizedOTPInputMessagesDirective } from "./localization/localized-textbox-messages.directive";
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-l10n";
const DEFAULT_SIZE = 'medium';
const DEFAULT_ROUNDED = 'medium';
const DEFAULT_FILL_MODE = 'solid';
const DEFAULT_OTPINPUT_LENGTH = 4;
export class OTPInputComponent {
hostElement;
cdr;
injector;
renderer;
localizationService;
zone;
/**
* Configures the total number of input fields.
*
* @default 4
*/
set length(value) {
if (value < 1 || this._length === value) {
return;
}
this._length = value;
this.inputsArray = new Array(this._length);
}
get length() {
return this._length;
}
/**
* Configures the input type.
*
* * The possible values are:
* * `text` (default)
* * `number`
* * `password`
*
* @default 'text'
*/
type = 'text';
/**
* Configures whether the input fields are separate or adjacent to each other.
*
* @default true
*/
spacing = true;
/**
* Specifies the separator between groups of input fields.
*
* > The configuration can only be applied when `groupLength` is set.
*/
separator;
/**
* Configures whether the component is enabled or disabled.
*
* @default false
*/
disabled = false;
/**
* Configures whether the component is readonly.
*
* @default false
*/
readonly = false;
/**
* Configures the placeholder of the input fields.
*/
placeholder;
/**
* Configures the length of the groups. If `groupLength` is a number, all groups will have the same length. If it's an array, each group can have a different length.
*/
get groupLength() {
return this._groupLength;
}
set groupLength(length) {
const isNumber = typeof length === 'number';
if (this._groupLength === length ||
isPresent(length) &&
((isNumber && (length < 1 || length > this.length)) ||
(!isNumber && !this.isValidGroupArray(length)))) {
return;
}
if (!isPresent(length)) {
this.clearGroups();
}
else if (isNumber) {
this.populateGroupArray(length);
}
else {
this.groupLengthArray = length;
if (!this.spacing) {
this.adjacentGroups = this.groupLengthArray;
}
}
this._groupLength = length;
this.populateSeparatorPositions();
}
/**
* Configures the value of the component. Unfilled input fields are represented with space.
*/
get value() {
return this._value;
}
set value(input) {
const isInvalidInput = this.type === 'number' && isPresent(input) && !this.containsDigitsOrSpaces(input);
if (this._value === input || isInvalidInput) {
return;
}
if (!isPresent(input)) {
this.clearInputValues();
this._value = null;
}
else {
this._value = input.slice(0, this.length);
if (!this.inputFieldValueChanged) {
this.fillInputs(input, 0, true);
}
}
if (this.inputAttributes) {
this.setInputAttributes();
}
else {
this.setDefaultAttributes();
}
}
/**
* The `size` property specifies the padding of the input fields.
*
* The possible values are:
* * `small`
* * `medium` (default)
* * `large`
* * `none`
*/
set size(size) {
const newSize = size || DEFAULT_SIZE;
const elem = this.hostElement.nativeElement;
this.renderer.removeClass(elem, `k-otp-${SIZE_MAP[this._size]}`);
this.renderer.addClass(elem, `k-otp-${SIZE_MAP[newSize]}`);
this._size = newSize;
}
get size() {
return this._size;
}
/**
* The `rounded` property specifies the border radius of the OTP Input.
*
* The possible values are:
* * `small`
* * `medium` (default)
* * `large`
* * `full`
* * `none`
*/
set rounded(rounded) {
this._rounded = rounded || DEFAULT_ROUNDED;
}
get rounded() {
return this._rounded;
}
/**
* The `fillMode` property specifies the background and border styles of the OTP Input.
*
* The possible values are:
* * `flat`
* * `solid` (default)
* * `outline`
* * `none`
*/
set fillMode(fillMode) {
const newFillMode = fillMode || DEFAULT_FILL_MODE;
this.setGroupFillMode(newFillMode, this._fillMode);
this._fillMode = newFillMode;
}
get fillMode() {
return this._fillMode;
}
/**
* Sets the HTML attributes of the inner focusable input element. Attributes which are essential for certain component functionalities cannot be changed.
*/
set inputAttributes(attributes) {
this._inputAttributes = attributes;
this.parsedAttributes = this.inputAttributes ?
{ ...this.defaultAttributes, ...this.inputAttributes } :
this.inputAttributes;
this.setInputAttributes();
}
get inputAttributes() {
return this._inputAttributes;
}
/**
* Fires each time the value is changed by the user—
* When the value of the component is programmatically changed to `ngModel` or `formControl`
* through its API or form binding, the `valueChange` event is not triggered because it
* might cause a mix-up with the built-in `valueChange` mechanisms of the `ngModel` or `formControl` bindings.
*/
valueChange = new EventEmitter();
/**
* Fires each time the user focuses the OTP Input.
*/
onFocus = new EventEmitter();
/**
* Fires each time the user blurs the OTP Input.
*/
onBlur = new EventEmitter();
wrapperClass = true;
get invalidClass() {
return this.isControlInvalid;
}
direction;
role = 'group';
/**
* @hidden
*/
inputFields;
/**
* @hidden
*/
set inputGroups(elements) {
this._inputGroups = elements;
this.setGroupFillMode(this.fillMode);
}
get inputGroups() {
return this._inputGroups;
}
/**
* @hidden
*/
groupLengthArray;
/**
* @hidden
*/
inputsArray;
/**
* @hidden
*/
inputsValues = [].constructor(DEFAULT_OTPINPUT_LENGTH);
/**
* @hidden
*/
adjacentGroups;
_length = DEFAULT_OTPINPUT_LENGTH;
_groupLength;
_inputGroups;
separatorPositions = new Set();
_value = null;
_size = DEFAULT_SIZE;
_rounded = DEFAULT_ROUNDED;
_fillMode = DEFAULT_FILL_MODE;
_isFocused = false;
focusChangedProgrammatically = false;
inputFieldValueChanged = false;
focusedInput;
_inputAttributes;
parsedAttributes = {};
get defaultAttributes() {
return {
autocomplete: 'off'
};
}
subscriptions;
ngChange = (_) => { };
ngTouched = () => { };
constructor(hostElement, cdr, injector, renderer, localizationService, zone) {
this.hostElement = hostElement;
this.cdr = cdr;
this.injector = injector;
this.renderer = renderer;
this.localizationService = localizationService;
this.zone = zone;
this.direction = localizationService.rtl ? 'rtl' : 'ltr';
}
ngOnInit() {
this.inputsArray = Array.from({ length: this._length });
this.subscriptions = this.localizationService.changes.subscribe(({ rtl }) => {
this.direction = rtl ? 'rtl' : 'ltr';
});
this.zone.runOutsideAngular(() => {
this.subscriptions.add(this.renderer.listen(this.hostElement.nativeElement, 'paste', this.handlePaste.bind(this)));
this.subscriptions.add(this.renderer.listen(this.hostElement.nativeElement, 'keydown', this.handleKeydown.bind(this)));
});
}
ngAfterViewInit() {
this.subscriptions.add(this.inputFields.changes.subscribe(this.handleInputChanges.bind(this)));
this.handleInputChanges();
this.renderer.addClass(this.hostElement.nativeElement, `k-otp-${SIZE_MAP[this._size]}`);
this.setGroupFillMode(this.fillMode);
this.zone.onStable.pipe(take(1)).subscribe(() => {
this.fillInputs(this.value);
});
}
ngOnChanges(changes) {
if (changes.length) {
if (typeof this.groupLength === 'number') {
this.populateGroupArray(this.groupLength);
}
this.populateSeparatorPositions();
}
if (changes.spacing) {
if (this.spacing === true) {
this.adjacentGroups = null;
}
else {
this.adjacentGroups = this.groupLengthArray ?? [this.length];
}
}
if (changes.type && this.type === 'number') {
if (isPresent(this.value) && !this.containsDigitsOrSpaces(this.value)) {
this.value = null;
this.zone.runOutsideAngular(() => setTimeout(() => this.zone.run(() => {
this.ngChange(null);
this.cdr.markForCheck();
})));
}
}
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
/**
* @hidden
*/
get formControl() {
const ngControl = this.injector.get(NgControl, null);
return ngControl?.control || null;
}
/**
* @hidden
*/
writeValue(value) {
this.value = value;
}
/**
* @hidden
*/
registerOnChange(fn) {
this.ngChange = fn;
}
/**
* @hidden
*/
registerOnTouched(fn) {
this.ngTouched = fn;
}
/**
* @hidden
*/
setDisabledState(isDisabled) {
this.cdr.markForCheck();
this.disabled = isDisabled;
}
/**
* @hidden
*/
get isControlInvalid() {
return this.formControl?.touched && this.formControl.invalid;
}
/**
* @hidden
*/
get isFocused() {
return this._isFocused;
}
/**
* @hidden
*/
set isFocused(value) {
if (this._isFocused !== value && this.hostElement) {
this._isFocused = value;
}
}
/**
* @hidden
*/
get hasGroups() {
if (!this.spacing && isPresent(this.groupLength)) {
return true;
}
}
/**
* @hidden
*/
showGroupSeparator(index) {
return this.groupLengthArray && index < this.groupLengthArray.length - 1;
}
/**
* @hidden
*/
showSeparator(index) {
return this.groupLength ? this.separatorPositions.has(index) : false;
}
/**
* @hidden
*/
handleValueChange(index, groupIndex) {
this.inputFieldValueChanged = true;
if (groupIndex) {
index = this.getIndexByGroup(groupIndex, index);
}
let newValue = '';
this.inputFields.forEach((input) => newValue = newValue.concat(input.value?.toString() || ' '));
if (!areSame(this.value, newValue)) {
this.zone.run(() => {
this.value = newValue;
this.ngChange(newValue);
this.valueChange.emit(newValue);
this.cdr.markForCheck();
});
}
this.inputFieldValueChanged = false;
if (isPresent(index) && isPresent(this.inputFields?.get(index).value)) {
this.focusNext();
}
}
/**
* @hidden
*/
handleInputFocus(index, groupIndex) {
if (this.focusChangedProgrammatically) {
return;
}
if (groupIndex) {
index = this.getIndexByGroup(groupIndex, index);
}
this.focusedInput = index;
}
/**
* @hidden
*/
handleInput(event, index, groupIndex) {
if (this.type === 'number' && !this.isValidNumber(event?.data)) {
const inputIndex = groupIndex ? this.getIndexByGroup(groupIndex, index) : index;
const textbox = this.inputFields.get(inputIndex);
if (this.value && this.isValidNumber(this.value[inputIndex])) {
textbox.value = this.value[inputIndex];
}
else {
textbox.value = null;
}
this.showInvalidInput(inputIndex);
return;
}
this.handleValueChange(index, groupIndex);
}
/**
* @hidden
*/
fillInputs(text, start = 0, replaceLast = false) {
if (!isPresent(text)) {
return;
}
let charCounter = 0;
this.inputFields?.forEach((otpInput, i) => {
if (i < start) {
return;
}
if (charCounter < text.length) {
if (text[charCounter] === ' ') {
otpInput.value = null;
}
else {
otpInput.value = text[charCounter];
}
charCounter++;
}
else if (replaceLast) {
otpInput.value = null;
}
});
}
/**
* Focuses the OTP Input.
*/
focus(index) {
if (!this.inputFields || index < 0 || index >= this.length) {
return;
}
this.focusChangedProgrammatically = true;
this.isFocused = true;
this.inputFields.get(index || 0).focus();
this.focusedInput = index || 0;
this.focusChangedProgrammatically = false;
}
/**
* Blurs the OTP Input.
*/
blur() {
this.focusChangedProgrammatically = true;
const isFocusedElement = this.hostElement.nativeElement.querySelector(':focus');
if (isFocusedElement) {
isFocusedElement.blur();
}
this.isFocused = false;
this.focusChangedProgrammatically = false;
}
/**
* @hidden
*/
handleFocus() {
this.zone.run(() => {
if (!this.focusChangedProgrammatically && hasObservers(this.onFocus)) {
this.onFocus.emit();
}
this.isFocused = true;
});
}
/**
* @hidden
*/
handleBlur() {
this.zone.run(() => {
if (!this.focusChangedProgrammatically) {
this.ngTouched();
this.onBlur.emit();
}
this.isFocused = false;
});
}
getIndexByGroup(groupIndex, itemIndex) {
return this.groupLengthArray.slice(0, groupIndex).reduce((sum, current) => sum + current, 0) + itemIndex;
}
focusNext() {
if (!this.inputFields || this.focusedInput === this.length - 1) {
return;
}
this.focusChangedProgrammatically = true;
this.isFocused = true;
this.inputFields.get(this.focusedInput).blur();
this.inputFields.get(this.focusedInput + 1).focus();
this.focusedInput++;
this.focusChangedProgrammatically = false;
}
focusPrevious() {
if (!this.inputFields || this.focusedInput === 0) {
return;
}
this.focusChangedProgrammatically = true;
this.isFocused = true;
this.inputFields.get(this.focusedInput).blur();
this.inputFields.get(this.focusedInput - 1).focus();
this.focusedInput--;
this.focusChangedProgrammatically = false;
}
handlePaste(event) {
event.preventDefault();
const text = event.clipboardData.getData('text').trim();
if (text === '') {
return;
}
if (this.type === 'number' && !this.isValidNumber(text)) {
this.showInvalidInput(this.focusedInput);
return;
}
this.inputFieldValueChanged = true;
this.fillInputs(text, this.focusedInput);
this.handleValueChange();
this.inputFieldValueChanged = false;
const focusedInput = this.focusedInput + text.length < this.inputFields?.length ?
this.focusedInput + text.length :
this.inputFields.length - 1;
this.inputFields.get(this.focusedInput).blur();
this.focusedInput = focusedInput;
this.inputFields.get(this.focusedInput).focus();
}
handleKeydown(event) {
if (this.readonly) {
const isCopyCommand = (event.ctrlKey || event.metaKey) && event.keyCode === Keys.KeyC;
if (!(event.keyCode === Keys.Tab || isCopyCommand)) {
event.preventDefault();
return;
}
}
switch (event.keyCode) {
case Keys.ArrowRight:
event.preventDefault();
this.direction === 'ltr' ? this.focusNext() : this.focusPrevious();
break;
case Keys.ArrowLeft:
event.preventDefault();
this.direction === 'ltr' ? this.focusPrevious() : this.focusNext();
break;
case Keys.Backspace:
event.preventDefault();
this.inputFields.get(this.focusedInput).value = null;
this.handleValueChange();
this.focusPrevious();
break;
case Keys.Delete:
event.preventDefault();
this.inputFields.get(this.focusedInput).value = null;
this.handleValueChange();
break;
default:
break;
}
}
isValidGroupArray(groups) {
if (!isPresent(groups)) {
return;
}
const sum = groups.reduce((sum, current) => sum + current, 0);
return sum === this.length;
}
populateGroupArray(length) {
const groupsCount = Math.floor(this.length / length);
const remainder = this.length % length;
const result = Array(groupsCount).fill(length);
if (remainder > 0) {
result.push(remainder);
}
this.groupLengthArray = [...result];
// groups with spacing shouldn't be wrapped in `k-input-group`
if (!this.spacing) {
this.adjacentGroups = [...this.groupLengthArray];
}
}
populateSeparatorPositions() {
let itemIndex = 0;
this.separatorPositions.clear();
if (!isPresent(this.groupLengthArray)) {
return;
}
for (let i = 0; i < this.groupLengthArray.length - 1; i++) {
itemIndex += this.groupLengthArray[i];
this.separatorPositions.add(itemIndex - 1);
}
}
clearGroups() {
this.groupLengthArray = null;
if (!this.spacing) {
this.adjacentGroups = [this.length];
}
else {
this.adjacentGroups = null;
}
this.separatorPositions.clear();
}
clearInputValues() {
this.inputFields?.forEach((input) => input.value = null);
}
handleInputChanges() {
this.zone.onStable.pipe(take(1)).subscribe(() => {
this.fillInputs(this.value?.trim());
if (this.inputAttributes) {
this.setInputAttributes();
}
else {
this.setDefaultAttributes();
}
this.cdr.detectChanges();
});
}
setGroupFillMode(fillMode, previousFillMode) {
this.inputGroups?.forEach(element => {
if (previousFillMode !== 'none') {
this.renderer.removeClass(element.nativeElement, `k-input-group-${previousFillMode}`);
}
if (fillMode !== 'none') {
this.renderer.addClass(element.nativeElement, `k-input-group-${fillMode}`);
}
});
}
setInputAttributes() {
this.inputFields?.forEach((input, index) => {
if (!this.parsedAttributes || !this.parsedAttributes?.['aria-label']) {
input.inputAttributes = { ...this.parsedAttributes, 'aria-label': this.ariaLabel(index) };
}
else {
input.inputAttributes = this.parsedAttributes;
}
});
}
setDefaultAttributes() {
this.inputFields?.forEach((input, index) => {
input.inputAttributes = {
autocomplete: 'off',
'aria-label': this.ariaLabel(index)
};
});
}
ariaLabel(index) {
const localizationMsg = this.localizationService.get('ariaLabel') || '';
return replaceMessagePlaceholder(replaceMessagePlaceholder(replaceMessagePlaceholder(localizationMsg, 'currentInput', (index + 1).toString()), 'totalInputs', this.length.toString()), 'value', this.value);
}
isValidNumber(value) {
if (!isPresent(value)) {
return;
}
const trimmedValue = value.trim();
return trimmedValue !== '' &&
trimmedValue !== 'Infinity' &&
trimmedValue !== '-Infinity' &&
!isNaN(Number(trimmedValue));
}
showInvalidInput(index) {
const textbox = this.inputFields.get(index);
const textboxElement = this.inputFields.get(index).hostElement.nativeElement;
const inputElement = textbox.input.nativeElement;
this.renderer.addClass(textboxElement, 'k-invalid');
if (textbox.value && this.isValidNumber(textbox.value)) {
this.zone.onStable.pipe(take(1)).subscribe(() => inputElement.select());
}
this.zone.runOutsideAngular(() => {
setTimeout(() => {
if (!this.isControlInvalid && textboxElement) {
this.renderer.removeClass(textboxElement, 'k-invalid');
}
}, 300);
});
}
containsDigitsOrSpaces(value) {
// @ts-expect-error TS does not allow comparing string with number
const isDigitOrSpace = (char) => (char == +char) || char === ' ';
for (let i = 0; i < value.length; i++) {
if (!isDigitOrSpace(value[i])) {
return false;
}
}
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: OTPInputComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i0.Injector }, { token: i0.Renderer2 }, { token: i1.LocalizationService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: OTPInputComponent, isStandalone: true, selector: "kendo-otpinput", inputs: { length: "length", type: "type", spacing: "spacing", separator: "separator", disabled: "disabled", readonly: "readonly", placeholder: "placeholder", groupLength: "groupLength", value: "value", size: "size", rounded: "rounded", fillMode: "fillMode", inputAttributes: "inputAttributes" }, outputs: { valueChange: "valueChange", onFocus: "focus", onBlur: "blur" }, host: { properties: { "class.k-otp": "this.wrapperClass", "class.k-invalid": "this.invalidClass", "attr.dir": "this.direction", "attr.role": "this.role" } }, providers: [
LocalizationService,
{ provide: L10N_PREFIX, useValue: 'kendo.otpinput' },
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OTPInputComponent),
multi: true
},
{ provide: KendoInput, useExisting: forwardRef(() => OTPInputComponent) }
], viewQueries: [{ propertyName: "inputFields", predicate: TextBoxComponent, descendants: true }, { propertyName: "inputGroups", predicate: ["inputGroup"], descendants: true }], exportAs: ["kendoOTPInput"], usesOnChanges: true, ngImport: i0, template: `
<ng-container kendoOTPInputLocalizedMessages
i18n-ariaLabel="kendo.otpinput.ariaLabel|The value of the aria-label attribute of the input fields."
ariaLabel="{{ 'Input {currentInput} of {totalInputs}, current value {value}' }}"
></ng-container>
<ng-container
kendoInputSharedEvents
[hostElement]="hostElement"
[(isFocused)]="isFocused"
(handleBlur)="handleBlur()"
(onFocus)="handleFocus()"
>
<ng-container *ngIf="spacing; else groups">
<ng-container *ngFor="let input of inputsArray; let i = index">
<kendo-textbox
class="k-otp-input"
[class.k-invalid]="isControlInvalid"
[selectOnFocus]="true"
[maxlength]="1"
[type]="type !== 'number' ? type : null"
[placeholder]="placeholder"
[size]="size"
[rounded]="rounded"
[fillMode]="fillMode"
[disabled]="disabled"
[readonly]="readonly"
(focus)="handleInputFocus(i)"
(input)="handleInput($event, i)"
></kendo-textbox>
<kendo-otpinput-separator *ngIf="showSeparator(i)" [separator]="separator"></kendo-otpinput-separator>
</ng-container>
</ng-container>
<ng-template #groups>
<ng-container *ngFor="let group of adjacentGroups; let i = index">
<div #inputGroup class="k-input-group">
<kendo-textbox
*ngFor="let input of [].constructor(group); let j = index"
class="k-otp-input"
[class.k-invalid]="isControlInvalid"
[selectOnFocus]="true"
[maxlength]="1"
[type]="type !== 'number' ? type : null"
[placeholder]="placeholder"
[size]="size"
[rounded]="rounded"
[fillMode]="fillMode"
[disabled]="disabled"
[readonly]="readonly"
(focus)="handleInputFocus(j, i)"
(input)="handleInput($event, j, i)"
></kendo-textbox>
</div>
<kendo-otpinput-separator *ngIf="showGroupSeparator(i)" [separator]="separator"></kendo-otpinput-separator>
</ng-container>
</ng-template>
<ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: SharedInputEventsDirective, selector: "[kendoInputSharedEvents]", inputs: ["hostElement", "clearButtonClicked", "isFocused"], outputs: ["isFocusedChange", "onFocus", "handleBlur"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: OTPInputSeparatorComponent, selector: "kendo-otpinput-separator", inputs: ["separator"], exportAs: ["kendoOTPInputSeparator"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: LocalizedOTPInputMessagesDirective, selector: "[kendoOTPInputLocalizedMessages]" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: OTPInputComponent, decorators: [{
type: Component,
args: [{
exportAs: 'kendoOTPInput',
providers: [
LocalizationService,
{ provide: L10N_PREFIX, useValue: 'kendo.otpinput' },
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OTPInputComponent),
multi: true
},
{ provide: KendoInput, useExisting: forwardRef(() => OTPInputComponent) }
],
selector: 'kendo-otpinput',
template: `
<ng-container kendoOTPInputLocalizedMessages
i18n-ariaLabel="kendo.otpinput.ariaLabel|The value of the aria-label attribute of the input fields."
ariaLabel="{{ 'Input {currentInput} of {totalInputs}, current value {value}' }}"
></ng-container>
<ng-container
kendoInputSharedEvents
[hostElement]="hostElement"
[(isFocused)]="isFocused"
(handleBlur)="handleBlur()"
(onFocus)="handleFocus()"
>
<ng-container *ngIf="spacing; else groups">
<ng-container *ngFor="let input of inputsArray; let i = index">
<kendo-textbox
class="k-otp-input"
[class.k-invalid]="isControlInvalid"
[selectOnFocus]="true"
[maxlength]="1"
[type]="type !== 'number' ? type : null"
[placeholder]="placeholder"
[size]="size"
[rounded]="rounded"
[fillMode]="fillMode"
[disabled]="disabled"
[readonly]="readonly"
(focus)="handleInputFocus(i)"
(input)="handleInput($event, i)"
></kendo-textbox>
<kendo-otpinput-separator *ngIf="showSeparator(i)" [separator]="separator"></kendo-otpinput-separator>
</ng-container>
</ng-container>
<ng-template #groups>
<ng-container *ngFor="let group of adjacentGroups; let i = index">
<div #inputGroup class="k-input-group">
<kendo-textbox
*ngFor="let input of [].constructor(group); let j = index"
class="k-otp-input"
[class.k-invalid]="isControlInvalid"
[selectOnFocus]="true"
[maxlength]="1"
[type]="type !== 'number' ? type : null"
[placeholder]="placeholder"
[size]="size"
[rounded]="rounded"
[fillMode]="fillMode"
[disabled]="disabled"
[readonly]="readonly"
(focus)="handleInputFocus(j, i)"
(input)="handleInput($event, j, i)"
></kendo-textbox>
</div>
<kendo-otpinput-separator *ngIf="showGroupSeparator(i)" [separator]="separator"></kendo-otpinput-separator>
</ng-container>
</ng-template>
<ng-container>
`,
standalone: true,
imports: [SharedInputEventsDirective, TextBoxComponent, OTPInputSeparatorComponent, NgFor, NgIf, LocalizedOTPInputMessagesDirective]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i0.Injector }, { type: i0.Renderer2 }, { type: i1.LocalizationService }, { type: i0.NgZone }]; }, propDecorators: { length: [{
type: Input
}], type: [{
type: Input
}], spacing: [{
type: Input
}], separator: [{
type: Input
}], disabled: [{
type: Input
}], readonly: [{
type: Input
}], placeholder: [{
type: Input
}], groupLength: [{
type: Input
}], value: [{
type: Input
}], size: [{
type: Input
}], rounded: [{
type: Input
}], fillMode: [{
type: Input
}], inputAttributes: [{
type: Input
}], valueChange: [{
type: Output
}], onFocus: [{
type: Output,
args: ['focus']
}], onBlur: [{
type: Output,
args: ['blur']
}], wrapperClass: [{
type: HostBinding,
args: ['class.k-otp']
}], invalidClass: [{
type: HostBinding,
args: ['class.k-invalid']
}], direction: [{
type: HostBinding,
args: ['attr.dir']
}], role: [{
type: HostBinding,
args: ['attr.role']
}], inputFields: [{
type: ViewChildren,
args: [TextBoxComponent]
}], inputGroups: [{
type: ViewChildren,
args: ['inputGroup']
}] } });