{"version":3,"file":"w2p-ui-kit-components-otp-input.mjs","sources":["../../../projects/kit/components/otp-input/otp-input.ts","../../../projects/kit/components/otp-input/otp-input.html","../../../projects/kit/components/otp-input/w2p-ui-kit-components-otp-input.ts"],"sourcesContent":["import {\n  afterNextRender,\n  Component,\n  effect,\n  ElementRef,\n  inject,\n  input,\n  output,\n  signal,\n  viewChildren,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { toSignal } from '@angular/core/rxjs-interop';\n\n@Component({\n  selector: 'w2p-otp-input',\n  imports: [],\n  templateUrl: './otp-input.html',\n  styleUrls: ['./otp-input.scss'],\n})\nexport class W2pOtpInput {\n  // сколько ячеек (длина кода)\n  public readonly length = input<number>(4);\n  public readonly disabledInput = input<boolean>(false);\n  public readonly invalidInput = input<boolean>(false);\n  public readonly autofocus = input<boolean>(false);\n\n  // эмитим, когда код полностью введён\n  public readonly complete = output<string>();\n\n  public readonly cells = viewChildren<ElementRef<HTMLInputElement>>('cellRef');\n\n  // массив введённых символов по ячейкам\n  public readonly digits = signal<string[]>([]);\n  public readonly disabled = signal<boolean>(false);\n  public readonly invalid = signal<boolean>(false);\n\n  private readonly ngControl = inject(NgControl, { optional: true, self: true });\n\n  private readonly controlStatus = this.ngControl?.statusChanges\n    ? toSignal(this.ngControl.statusChanges, { initialValue: this.ngControl.status })\n    : signal(null);\n\n  private onChange: (value: string) => void = () => {};\n  private onTouched: () => void = () => {};\n\n  constructor() {\n    if (this.ngControl) {\n      this.ngControl.valueAccessor = this;\n    }\n\n    // пересоздаём массив ячеек при изменении length\n    effect(() => {\n      const len = this.length();\n      const current = this.digits();\n      if (current.length !== len) {\n        const next = Array.from({ length: len }, (_, i) => current[i] ?? '');\n        this.digits.set(next);\n      }\n    });\n\n    effect(() => {\n      this.disabled.set(this.disabledInput());\n    });\n\n    effect(() => {\n      this.controlStatus();\n      const manualInvalid = this.invalidInput();\n      const controlInvalid = !!(\n        this.ngControl?.invalid &&\n        (this.ngControl?.touched || this.ngControl?.dirty)\n      );\n      this.invalid.set(manualInvalid || controlInvalid);\n    });\n\n    afterNextRender(() => {\n      if (this.autofocus()) {\n        this.cells()[0]?.nativeElement.focus();\n      }\n    });\n  }\n\n  registerOnChange(fn: any): void {\n    this.onChange = fn;\n  }\n\n  registerOnTouched(fn: any): void {\n    this.onTouched = fn;\n  }\n\n  setDisabledState(isDisabled: boolean): void {\n    this.disabled.set(isDisabled);\n  }\n\n  public setInvalid(isInvalid: boolean): void {\n    this.invalid.set(isInvalid);\n  }\n\n  writeValue(obj: string): void {\n    const value = obj ?? '';\n    const len = this.length();\n    this.digits.set(Array.from({ length: len }, (_, i) => value[i] ?? ''));\n  }\n\n  onInput(event: Event, index: number): void {\n    const input = event.target as HTMLInputElement;\n    // берём только последний введённый символ, только цифра\n    const raw = input.value.replace(/\\D/g, '');\n    const char = raw.slice(-1);\n\n    const next = [...this.digits()];\n    next[index] = char;\n    this.digits.set(next);\n    input.value = char;\n\n    if (char && index < this.length() - 1) {\n      this.focusCell(index + 1);\n    }\n\n    this.emitValue();\n  }\n\n  onKeydown(event: KeyboardEvent, index: number): void {\n    const input = event.target as HTMLInputElement;\n\n    if (event.key === 'Backspace') {\n      if (!input.value && index > 0) {\n        event.preventDefault();\n        const next = [...this.digits()];\n        next[index - 1] = '';\n        this.digits.set(next);\n        this.focusCell(index - 1);\n        this.emitValue();\n      }\n      return;\n    }\n\n    if (event.key === 'ArrowLeft' && index > 0) {\n      event.preventDefault();\n      this.focusCell(index - 1);\n      return;\n    }\n\n    if (event.key === 'ArrowRight' && index < this.length() - 1) {\n      event.preventDefault();\n      this.focusCell(index + 1);\n      return;\n    }\n  }\n\n  onPaste(event: ClipboardEvent, index: number): void {\n    event.preventDefault();\n    const pasted = event.clipboardData?.getData('text').replace(/\\D/g, '') ?? '';\n    if (!pasted) return;\n\n    const next = [...this.digits()];\n    let cursor = index;\n    for (const char of pasted) {\n      if (cursor >= this.length()) break;\n      next[cursor] = char;\n      cursor++;\n    }\n    this.digits.set(next);\n    this.emitValue();\n\n    const focusIndex = Math.min(cursor, this.length() - 1);\n    this.focusCell(focusIndex);\n  }\n\n  public reset(): void {\n    const len = this.length();\n    this.digits.set(Array.from({ length: len }, () => ''));\n    this.invalid.set(false);\n    this.onChange('');\n    this.focusCell(0);\n  }\n\n  onBlur(): void {\n    this.onTouched();\n  }\n\n  private focusCell(index: number): void {\n    // ячейки перерисуются после сигнала, откладываем фокус на следующий тик\n    setTimeout(() => this.cells()[index]?.nativeElement.focus(), 0);\n  }\n\n  private emitValue(): void {\n    const digitsArr = this.digits();\n    const value = digitsArr.join('');\n    this.onChange(value);\n\n    const isComplete = digitsArr.length === this.length() && digitsArr.every((d) => d !== '');\n\n    if (isComplete) {\n      this.complete.emit(value);\n    }\n  }\n}\n","<div class=\"otp-wrapper\" [class.invalid]=\"invalid()\">\n  @for (digit of digits(); track $index) {\n    <input\n      #cellRef\n      class=\"otp-cell\"\n      type=\"text\"\n      inputmode=\"numeric\"\n      maxlength=\"1\"\n      autocomplete=\"one-time-code\"\n      [value]=\"digit\"\n      [disabled]=\"disabled()\"\n      [class.filled]=\"!!digit\"\n      [class.invalid]=\"invalid()\"\n      (input)=\"onInput($event, $index)\"\n      (keydown)=\"onKeydown($event, $index)\"\n      (paste)=\"onPaste($event, $index)\"\n      (blur)=\"onBlur()\"\n    />\n  }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}