{"mappings":"AAAA,cAAc,oBAA4E;AAC1F,cAAc,sBAAiC;AAI/C,SAAS,kBAAkB;;;;;;;;;;;;;;AAoB3B,cA2CM,cAAc,WAAW;;;;CAI7B,AACA;;;;CAKA,AACA;CAEA,AACA;CAEA,AACA;CAEA;CAYA,IAAI,kBAAkB;CAItB,UAAU,UAAU,eAAe;CAiCnC;CAKA,UAAU,aAAa,GAAG,aAAa,kBAAkB;;;;;;;;;;;;;;;;;CA4BzD,UAAU,UAAU;CA2CpB;CAKA,QAAQ;CAKR;CAMA;;AAUF,eAAe;AACf,SAAS","names":[],"sources":["../../../../src/web-components/split/component.ts"],"sourcesContent":["import { type HandlerEvent, attr, tokenList, godown, styles, loop, StyleController } from \"@godown/element\";\nimport { type TemplateResult, css, html } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n\nimport { cssGlobalVars, scopePrefix } from \"../../internal/global-style.js\";\nimport { SuperInput } from \"../../internal/super-input.js\";\nimport { RingBuilder, ringTypeAttribute } from \"../../internal/ring.js\";\nimport { omit } from \"sharekit\";\n\nconst protoName = \"split\";\nconst cssScope = scopePrefix(protoName);\n\n/**\n * {@linkcode Split} renders multiple input boxes.\n *\n * Input: will move the focus box backward until the complete input from start to end.\n *\n * Delete: will move the focus box forward until the first and no inputs for each.\n *\n * @fires input - Fires when the input value changes.\n * @fires change - Fires when the input value changes.\n * @fires focus - Fires when the input is focused.\n * @fires blur - Fires when the input is blurred.\n * @category input\n */\n@godown(protoName)\n@styles(css`\n  :host {\n    display: block;\n    border-radius: 0.1em;\n    width: fit-content;\n    ${cssScope}--size: 2em;\n    ${cssScope}--gap: .25em;\n  }\n\n  [part=\"root\"] {\n    gap: var(${cssScope}--gap);\n    position: relative;\n    vertical-align: top;\n    display: flex;\n    justify-content: space-between;\n    border-radius: inherit;\n  }\n\n  [part=\"input-box\"] {\n    width: var(${cssScope}--size);\n    height: var(${cssScope}--size);\n    vertical-align: top;\n    border-radius: inherit;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n\n  [part=\"input\"] {\n    width: 100%;\n    height: 100%;\n    opacity: 0;\n    background: none;\n    position: absolute;\n    z-index: -1;\n  }\n\n  .focus,\n  [part=\"input-box\"]:active {\n    ${cssGlobalVars.ringColor}: var(${cssGlobalVars.active});\n  }\n`)\nclass Split extends SuperInput {\n  /**\n   * The number of input boxes.\n   */\n  @property({ type: Number })\n  len = 6;\n\n  /**\n   * Focus index.\n   */\n  @property({ type: Number })\n  index = -1;\n\n  @state()\n  current = -1;\n\n  @state()\n  currentValue: (string | void)[] = [];\n\n  constructor() {\n    super();\n    new StyleController(\n      this,\n      () =>\n        new RingBuilder({\n          selector: \"[part=input-box]\",\n          type: this.ringType,\n        }).css,\n    );\n  }\n\n  get observedRecord(): Record<string, any> {\n    return omit(super.observedRecord, ringTypeAttribute);\n  }\n\n  protected render(): TemplateResult<1> {\n    return html`\n      <div\n        part=\"root\"\n        ${attr(this.observedRecord)}\n      >\n        ${loop(\n          this.len,\n          (index: number) => html`\n            <span\n              part=\"input-box\"\n              ring-type=\"${this.ringType}\"\n              class=\"${tokenList({ focus: this.current === index })}\"\n              @click=\"${this.disabled ? null : () => this.focusAt(index)}\"\n            >\n              ${this.currentValue[index]}\n            </span>\n          `,\n        )}\n        <input\n          part=\"input\"\n          @blur=${this.blur}\n          @input=\"${this._handleInput}\"\n          @change=\"${this._handleChange}\"\n          .value=\"${\n            /* Ensure that input always has a value of length this.len */\n            this.value.padStart(this.len, \" \")\n          }\"\n        />\n      </div>\n    `;\n  }\n\n  connectedCallback(): void {\n    super.connectedCallback();\n    this.reset();\n  }\n\n  protected _handleInput(e: HandlerEvent<HTMLInputElement, InputEvent>): void {\n    e.stopPropagation();\n    if (this.compositing) {\n      return;\n    }\n\n    this.fillInput(e.data);\n    this.value = this.currentValue.join(\"\");\n\n    this.dispatchCustomEvent(\"input\", this.value, { bubbles: true });\n  }\n\n  /**\n   * Fill input with data.\n   *\n   * If data is null\n   *  - If current value is null, move to before.\n   *  - If current value is not null, delete it.\n   *\n   * If data is not null\n   *  - If current value is null, input data.\n   *  - If current value is not null, input data and move to after.\n   *\n   * If data is multiple characters,\n   *   Fill input with data[0] and call fillInput with data.slice(1).\n   *\n   * @param data Input event data.\n   */\n  protected fillInput(data: string | null): void {\n    if (data === null) {\n      // delete\n\n      if (this.currentValue[this.current] !== null) {\n        // delete exist value\n\n        this.currentValue[this.current] = null;\n      } else {\n        // go to before\n\n        this.currentValue[this.current - 1] = null;\n        const lastNotNull = this.currentValue.findLastIndex((a) => a !== null);\n        this.current = this.current - 1 < 0 ? (lastNotNull < 0 ? 0 : lastNotNull) : this.current - 1;\n      }\n      return;\n    }\n\n    const multiple = data.length > 1;\n\n    // input\n    this.currentValue[this.current] = data[0];\n    if (this.current + 1 >= this.len) {\n      // index overflow\n\n      this.current = this.currentValue.indexOf(null);\n      if (this.current === -1) {\n        this.blur();\n      }\n    } else {\n      // go to after\n\n      this.current += 1;\n    }\n\n    if (multiple) {\n      const after = data.slice(1);\n      if (after) {\n        this.fillInput(after);\n      }\n    }\n  }\n\n  focus(): void {\n    this.focusAt(this.current);\n    super.focus();\n  }\n\n  focusAt(i: number): void {\n    this.current = i;\n    this._input.focus();\n  }\n\n  blur(): void {\n    this._input.blur();\n    this.current = -1;\n    super.blur();\n  }\n\n  reset(): void {\n    this.current = -1;\n    this.value = this.default;\n    this.currentValue = this.value.split(\"\").concat(Array(this.len - this.value.length).fill(null));\n    if (this.index > -1) {\n      this.current = this.index;\n    }\n  }\n}\n\nexport default Split;\nexport { Split };\n"],"version":3,"file":"component.d.ts"}