{"version":3,"file":"index.mjs","names":["Fragment","InputEvent","KeyboardEvent","useEffect","useMemo","useRef","Input","clsx","TotpInputProps","value","length","onValue","type","autoFocus","autoComplete","name","space","getIsValidValue","test","TotpInput","t0","$","_c","t1","t2","undefined","t3","Array","from","list","splitValues","split","values","map","_","i","value_0","refArray","focus","i_0","el","current","t4","slice","handleMultipleValues","multipleValues","i_1","result","filter","pastedValue","value_1","newValues","splice","Math","min","join","focusIndex","trim","t5","t6","__0","i_2","value_2","isValidValue","ref","_temp","event_0","event","clipboardData","getData","preventDefault","event_1","bb73","key","targetIndex","currentTarget","selectionStart","selectionEnd","newValues_0","event_2","target","HTMLInputElement","event_3","newValue","isNewValueValid","removedValidValue","newValues_1","t7","select","FieldName","useField","useControl","Field","FieldProps","TotpInput","Props","Omit","name","TotpField","id","forcedId","rest","field","control","defaultValue","register","errors","key","value","change"],"sources":["../../../../src/components/fields/totp/TotpInput.tsx","../../../../src/components/fields/totp/TotpField.tsx"],"sourcesContent":["import { Fragment, InputEvent, KeyboardEvent, useEffect, useMemo, useRef } from \"react\";\nimport { Input } from \"../text/Input\";\nimport clsx from \"clsx\";\n\ninterface TotpInputProps {\n  value: string;\n  length: number;\n  onValue: (value: string) => void;\n  type?: \"number\" | \"alphabet\";\n  autoFocus?: boolean;\n  autoComplete?: \"one-time-code\";\n  name?: string;\n}\n\nconst space = \" \";\n\nconst getIsValidValue = (value: string, type: TotpInputProps[\"type\"]) => {\n  if (type === \"number\") {\n    return /[0-9]/.test(value);\n  }\n  return /[0-9A-Za-z]/.test(value);\n};\n\nexport function TotpInput({\n  value = \"\",\n  length,\n  onValue,\n  type = \"alphabet\",\n  autoFocus,\n  name,\n}: TotpInputProps) {\n  const list = useMemo(() => Array.from({ length }), [length]);\n  const splitValues = value.split(\"\");\n  const values = list.map((_, i) => {\n    // TODO different from [i]?.[0] vs [i]\n    const value = splitValues[i] || space;\n    if (!getIsValidValue(value, type)) {\n      return space;\n    }\n    return value;\n  });\n\n  const refArray = useRef<(HTMLInputElement | null)[]>([]);\n\n  const focus = (i: number) => {\n    const el = refArray.current?.[i];\n    el?.focus?.();\n  };\n\n  useEffect(() => {\n    if (refArray.current.length !== list.length) {\n      refArray.current = refArray.current.slice(0, list.length);\n    }\n  }, [list.length]);\n\n  const handleMultipleValues = (multipleValues: string, i: number) => {\n    const result = multipleValues\n      .slice(0, list.length - i)\n      .split(\"\")\n      .filter((pastedValue) => {\n        const value = pastedValue[0];\n        return getIsValidValue(value, type);\n      });\n    if (!result.length) {\n      return;\n    }\n    const newValues = [...values];\n    newValues.splice(i, result.length, ...result);\n    focus(Math.min(i + result.length, length - 1));\n    onValue(newValues.join(\"\"));\n  };\n\n  const focusIndex = Math.min(value.trim().length, length - 1);\n\n  return (\n    <div className=\"flex flex-nowrap gap-2\">\n      {list.map((_, i) => {\n        // trim only for replacing ' ' with ''\n        const value = values[i].trim();\n        const isValidValue = getIsValidValue(value, type);\n        return (\n          <Fragment key={i}>\n            <Input\n              name={name ? `${name}-chars[${i}]` : undefined}\n              size=\"custom\"\n              autoFocus={i === focusIndex ? autoFocus : undefined}\n              type={type === \"number\" ? \"tel\" : \"text\"}\n              inputMode={type === \"number\" ? \"numeric\" : undefined}\n              className={clsx(\"2sm:w-16 h-(--h-input) w-12 shrink-0 p-0 [--h-input:72px]\")}\n              inputClassName=\"text-center text-body-3xl\"\n              autoCapitalize=\"none\"\n              autoCorrect=\"off\"\n              spellCheck=\"false\"\n              ref={(ref) => {\n                refArray.current[i] = ref;\n              }}\n              value={value}\n              onFocus={(event) => {\n                event.currentTarget.select();\n              }}\n              onPaste={(event) => {\n                handleMultipleValues(event.clipboardData?.getData(\"text/plain\") || \"\", i);\n                event.preventDefault();\n              }}\n              onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {\n                switch (event.key) {\n                  case \"ArrowLeft\":\n                    event.preventDefault();\n                    focus(i - 1);\n                    break;\n                  case \"ArrowRight\":\n                    event.preventDefault();\n                    focus(i + 1);\n                    break;\n                  case \"Backspace\": {\n                    // onChange doesn't trigger on empty values, or when backspacing the left-most selection\n                    const targetIndex = i - 1;\n                    if (\n                      targetIndex >= 0 &&\n                      (event.currentTarget.selectionStart !== 0 ||\n                        event.currentTarget.selectionEnd !== 0)\n                    ) {\n                      return;\n                    }\n                    event.preventDefault();\n                    const newValues = [...values];\n                    newValues[targetIndex] = space;\n                    focus(targetIndex);\n                    onValue(newValues.join(\"\"));\n                    break;\n                  }\n                }\n              }}\n              onInput={(event: InputEvent<HTMLInputElement>) => {\n                // onChange won't trigger if the values are the same\n                if ((event.target as HTMLInputElement).value === value) {\n                  focus(i + 1);\n                }\n              }}\n              onChange={(event) => {\n                if (event.target.value.length > 1) {\n                  handleMultipleValues(event.target.value, i);\n                  return;\n                }\n                const newValue = event.target.value.length === 1 ? event.target.value[0] : space;\n                const isNewValueValid = getIsValidValue(newValue, type);\n                if (!isNewValueValid && newValue !== space) {\n                  return;\n                }\n                const removedValidValue = isValidValue && newValue === space;\n                if (removedValidValue || isNewValueValid) {\n                  const newValues = [...values];\n                  newValues[i] = newValue;\n                  if (isNewValueValid) {\n                    focus(i + 1);\n                  }\n                  onValue(newValues.join(\"\"));\n                }\n              }}\n            />\n          </Fragment>\n        );\n      })}\n    </div>\n  );\n}\n","import type { FieldName } from \"@conform-to/react\";\nimport { useField, useControl } from \"@conform-to/react/future\";\nimport { Field, type FieldProps } from \"../field/Field\";\nimport { TotpInput } from \"./TotpInput\";\n\ninterface Props extends Omit<FieldProps, \"errors\" | \"children\" | \"group\"> {\n  name: FieldName<string | null | undefined>;\n}\nexport function TotpField({ name, id: forcedId, ...rest }: Props) {\n  const field = useField(name);\n  const id = forcedId ?? field.id;\n  const control = useControl({\n    defaultValue: field.defaultValue,\n  });\n\n  return (\n    <>\n      <input type=\"text\" name={field.name} ref={control.register} hidden />\n      <Field id={id} group errors={field.errors} data-testid={field.name} {...rest}>\n        <TotpInput\n          type=\"number\"\n          length={6}\n          key={field.key}\n          value={control.value ?? \"\"}\n          onValue={control.change}\n        />\n      </Field>\n    </>\n  );\n}\n"],"mappings":"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"}