component PinInput {
  outputs {
    input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
    change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
    complete(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
    paste(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
    clear(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
    error(payload: { error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)
  }

  props {
    size: string = "default"
    color: string = "primary"
    label: string = "Verification code"
    name: string = "pin"
    value: string = ""
    length: number = 4
    pattern: string = "[0-9]"
    type: string = "text"
    inputMode: string = "numeric"
    placeholder: string = "○"
    autocomplete: string = "one-time-code"
    masked: boolean = false
    disabled: boolean = false
    readonly: boolean = false
    required: boolean = false
    autoFocus: boolean = false
    autoSubmit: boolean = false
    allowPaste: boolean = true
    clearable: boolean = true
    clearLabel: string = "Clear code"
    separator: string = ""
    groupSize: number = 0
    helpText: string = ""
    invalid: boolean = false
    validationMessage: string = ""
    class: string = ""
  }

  functions {
    shared function inputIndexes() {
      return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].slice(
        0,
        Math.max(1, Math.min(12, Number(length)))
      )
    }
  }

  view {
    <fieldset
      {...attrs}
      class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--pin-input {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
      data-wrn-pin-input
      data-length="{length}"
      data-pattern="{pattern}"
      data-allow-paste="{allowPaste ? 'true' : 'false'}"
      data-auto-submit="{autoSubmit ? 'true' : 'false'}"
      data-value="{value}"
      disabled="{disabled}"
      aria-describedby="{validationMessage ? `${name}-validation` : helpText ? `${name}-help` : ''}"
    >
      <div class="wire-next__pin-heading">
        <legend>{label}</legend>
        <button
          type="button"
          class="wire-next__pin-clear"
          data-pin-clear
          aria-label="{clearLabel}"
          title="{clearLabel}"
          style="{clearable && value ? '' : 'display: none'}"
        >
          <span class="icon-[lucide--rotate-ccw]" aria-hidden="true"></span>
          <span>{clearLabel}</span>
        </button>
      </div>

      <div class="wire-next__pin-cells" role="group" aria-label="{label}">
        {#each inputIndexes() as index}
          <input
            data-pin-cell
            data-pin-index="{index}"
            id="{name}-{index}"
            type="{masked ? 'password' : type}"
            inputmode="{inputMode}"
            maxlength="1"
            value="{value[index] || ''}"
            placeholder="{placeholder}"
            autocomplete="{index === 0 ? autocomplete : 'off'}"
            aria-label="{label} digit {index + 1} of {length}"
            aria-required="{required ? 'true' : 'false'}"
            readonly="{readonly}"
            disabled="{disabled}"
            autofocus="{autoFocus && index === 0}"
          />
          {#if separator && groupSize !== 0 && (index + 1) % groupSize === 0 && index + 1 !== length}
            <span class="wire-next__pin-separator" aria-hidden="true">{separator}</span>
          {/if}
        {/each}
      </div>

      <input
        type="hidden"
        name="{name}"
        value="{value}"
        required="{required}"
        data-pin-value
      />

      {#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
      <small
        id="{name}-validation"
        class="wire-next__validation"
        data-error="{name}"
      >{validationMessage}</small>
    </fieldset>
  }

  style {
    .wire-next--pin-input.wire-next--invalid .wire-next__pin-cells input,
    .wire-next--pin-input:has(input.wire-invalid) .wire-next__pin-cells input {
      border-color: var(--wire-color-danger);
    }

    .wire-next--pin-input {
      display: grid;
      width: 100%;
      min-width: 0;
      gap: 0.65rem;
      padding: 0;
      border: 0;
      color: var(--wire-color-text);
    }

    .wire-next__pin-heading {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 1rem;
    }

    .wire-next__pin-heading legend {
      padding: 0;
      font-size: 0.82em;
      font-weight: 700;
    }

    .wire-next__pin-clear {
      display: inline-flex;
      align-items: center;
      gap: 0.35rem;
      padding: 0;
      border: 0;
      color: var(--wire-component-color);
      background: transparent;
      font: inherit;
      font-size: 0.7em;
      font-weight: 750;
      cursor: pointer;
    }

    .wire-next__pin-clear > span:first-child {
      width: 0.9rem;
      height: 0.9rem;
    }

    .wire-next__pin-cells {
      display: flex;
      max-width: 100%;
      align-items: center;
      gap: clamp(0.35rem, 1.5vw, 0.65rem);
      overflow-x: auto;
      padding: 0.2rem;
      scrollbar-width: thin;
    }

    .wire-next--pin-input .wire-next__pin-cells input {
      flex: 0 0 clamp(2.4rem, 8vw, 3.25rem);
      width: clamp(2.4rem, 8vw, 3.25rem);
      height: clamp(2.75rem, 9vw, 3.5rem);
      min-width: clamp(2.4rem, 8vw, 3.25rem);
      padding: 0;
      border: 1px solid var(--wire-color-border);
      border-radius: var(--wire-radius-sm);
      color: var(--wire-color-text);
      background: var(--wire-color-bg);
      font: inherit;
      font-size: clamp(1rem, 3vw, 1.25rem);
      font-weight: 750;
      text-align: center;
      caret-color: var(--wire-component-color);
      transition:
        border-color var(--wire-motion-base) var(--wire-ease-standard),
        box-shadow var(--wire-motion-base) var(--wire-ease-standard),
        transform var(--wire-motion-fast) var(--wire-ease-standard);
    }

    .wire-next--pin-input .wire-next__pin-cells input::placeholder {
      color: var(--wire-color-muted);
      opacity: 0.45;
    }

    .wire-next--pin-input .wire-next__pin-cells input:focus-visible {
      border-color: var(--wire-component-color);
      outline: 0;
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-component-color) 18%, transparent);
      transform: translateY(-1px);
    }

    .wire-next--pin-input .wire-next__pin-cells input:disabled,
    .wire-next--pin-input .wire-next__pin-cells input:read-only {
      cursor: not-allowed;
      opacity: 0.55;
    }

    .wire-next__pin-separator {
      color: var(--wire-color-muted);
      font-size: 1.1rem;
      font-weight: 800;
    }

    .wire-next--pin-input[data-complete="true"] .wire-next__pin-cells input {
      border-color: color-mix(in srgb, var(--wire-component-color) 72%, var(--wire-color-border));
      background: color-mix(in srgb, var(--wire-component-color) 7%, var(--wire-color-bg));
    }

    @media (max-width: 480px) {
    .wire-next__pin-cells {
        gap: 0.3rem;
      }
    .wire-next--pin-input .wire-next__pin-cells input {
        flex-basis: min(2.7rem, 12vw);
        width: min(2.7rem, 12vw);
        min-width: min(2.7rem, 12vw);
        height: min(3rem, 14vw);
      }
    }

    /* Shared component foundation. */

        .wire-component {
          --wire-component-color: var(--wire-color-primary);
          --wire-component-scale: 1;
          margin: 0;
          color: var(--wire-color-text);
          font-family: var(--wrn-font-sans, inherit);
        }

        .wire-component--color-primary {
          --wire-component-color: var(--wire-color-primary);
        }

        .wire-component--color-secondary {
          --wire-component-color: var(--wire-color-secondary);
        }

        .wire-component--color-success {
          --wire-component-color: var(--wire-color-success);
        }

        .wire-component--color-warning {
          --wire-component-color: var(--wire-color-warning);
        }

        .wire-component--color-danger {
          --wire-component-color: var(--wire-color-danger);
        }

        .wire-component--color-info {
          --wire-component-color: var(--wire-color-info);
        }

        .wire-component--size-xs {
          --wire-component-scale: 0.78;
        }

        .wire-component--size-sm {
          --wire-component-scale: 0.88;
        }

        .wire-component--size-default,
        .wire-component--size-md {
          --wire-component-scale: 1;
        }

        .wire-component--size-lg {
          --wire-component-scale: 1.14;
        }

        .wire-component--size-xl {
          --wire-component-scale: 1.28;
        }

        .wire-component:not(.wire-btn) {
          font-size: calc(1em * var(--wire-component-scale));
        }

        .wire-component :is(a, button, input, select, textarea):focus-visible {
          outline-color: var(--wire-component-color);
        }

        .wire-component p {
          margin: 0;
          color: var(--wire-color-muted);
          line-height: 1.6;
        }
  }
}
