@yamada-ui/pin-input 1.1.3-next-20241117114930 → 1.2.0-dev-20241118202244

Sign up to get free protection for your applications and to get access to all the features.
@@ -94,6 +94,18 @@ var PinInput = forwardRef(
94
94
  },
95
95
  [descendants, moveFocus, manageFocus]
96
96
  );
97
+ const focusInputField = useCallback(
98
+ (direction, index) => {
99
+ const input = direction === "next" ? descendants.nextValue(index, void 0, false) : descendants.prevValue(index, void 0, false);
100
+ if (!input) return;
101
+ const valueLength = input.node.value.length;
102
+ requestAnimationFrame(() => {
103
+ input.node.focus();
104
+ input.node.setSelectionRange(0, valueLength);
105
+ });
106
+ },
107
+ [descendants]
108
+ );
97
109
  const setValue = useCallback(
98
110
  (value2, index, isFocus = true) => {
99
111
  var _a;
@@ -158,19 +170,34 @@ var PinInput = forwardRef(
158
170
  ]
159
171
  );
160
172
  const onKeyDown = useCallback(
161
- (index) => ({ key, target }) => {
162
- if (key !== "Backspace" || !manageFocus) return;
163
- if (target.value === "") {
164
- const prevInput = descendants.prevValue(index, void 0, false);
165
- if (!prevInput) return;
166
- setValue("", index - 1, false);
167
- prevInput.node.focus();
168
- setMoveFocus(true);
169
- } else {
170
- setMoveFocus(false);
171
- }
173
+ (index) => (ev) => {
174
+ if (!manageFocus) return;
175
+ const actions = {
176
+ ArrowLeft: () => {
177
+ ev.preventDefault();
178
+ focusInputField("prev", index);
179
+ },
180
+ ArrowRight: () => {
181
+ ev.preventDefault();
182
+ focusInputField("next", index);
183
+ },
184
+ Backspace: () => {
185
+ if (ev.target.value === "") {
186
+ const prevInput = descendants.prevValue(index, void 0, false);
187
+ if (!prevInput) return;
188
+ setValue("", index - 1, false);
189
+ prevInput.node.focus();
190
+ setMoveFocus(true);
191
+ } else {
192
+ setMoveFocus(false);
193
+ }
194
+ }
195
+ };
196
+ const action = actions[ev.key];
197
+ if (!action) return;
198
+ action();
172
199
  },
173
- [descendants, manageFocus, setValue]
200
+ [descendants, focusInputField, manageFocus, setValue]
174
201
  );
175
202
  const onFocus = useCallback(
176
203
  (index) => () => setFocusedIndex(index),
@@ -263,4 +290,4 @@ export {
263
290
  PinInput,
264
291
  PinInputField
265
292
  };
266
- //# sourceMappingURL=chunk-MYBEULDW.mjs.map
293
+ //# sourceMappingURL=chunk-TU5BZGLV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const focusInputField = useCallback(\n (direction: \"next\" | \"prev\", index: number) => {\n const input =\n direction === \"next\"\n ? descendants.nextValue(index, undefined, false)\n : descendants.prevValue(index, undefined, false)\n\n if (!input) return\n\n const valueLength = input.node.value.length\n\n requestAnimationFrame(() => {\n input.node.focus()\n input.node.setSelectionRange(0, valueLength)\n })\n },\n [descendants],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) => (ev: KeyboardEvent<HTMLInputElement>) => {\n if (!manageFocus) return\n\n const actions: { [key: string]: Function | undefined } = {\n ArrowLeft: () => {\n ev.preventDefault()\n focusInputField(\"prev\", index)\n },\n ArrowRight: () => {\n ev.preventDefault()\n focusInputField(\"next\", index)\n },\n Backspace: () => {\n if ((ev.target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n }\n\n const action = actions[ev.key]\n\n if (!action) return\n\n action()\n },\n [descendants, focusInputField, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}
package/dist/index.js CHANGED
@@ -104,6 +104,18 @@ var PinInput = (0, import_core.forwardRef)(
104
104
  },
105
105
  [descendants, moveFocus, manageFocus]
106
106
  );
107
+ const focusInputField = (0, import_react.useCallback)(
108
+ (direction, index) => {
109
+ const input = direction === "next" ? descendants.nextValue(index, void 0, false) : descendants.prevValue(index, void 0, false);
110
+ if (!input) return;
111
+ const valueLength = input.node.value.length;
112
+ requestAnimationFrame(() => {
113
+ input.node.focus();
114
+ input.node.setSelectionRange(0, valueLength);
115
+ });
116
+ },
117
+ [descendants]
118
+ );
107
119
  const setValue = (0, import_react.useCallback)(
108
120
  (value2, index, isFocus = true) => {
109
121
  var _a;
@@ -168,19 +180,34 @@ var PinInput = (0, import_core.forwardRef)(
168
180
  ]
169
181
  );
170
182
  const onKeyDown = (0, import_react.useCallback)(
171
- (index) => ({ key, target }) => {
172
- if (key !== "Backspace" || !manageFocus) return;
173
- if (target.value === "") {
174
- const prevInput = descendants.prevValue(index, void 0, false);
175
- if (!prevInput) return;
176
- setValue("", index - 1, false);
177
- prevInput.node.focus();
178
- setMoveFocus(true);
179
- } else {
180
- setMoveFocus(false);
181
- }
183
+ (index) => (ev) => {
184
+ if (!manageFocus) return;
185
+ const actions = {
186
+ ArrowLeft: () => {
187
+ ev.preventDefault();
188
+ focusInputField("prev", index);
189
+ },
190
+ ArrowRight: () => {
191
+ ev.preventDefault();
192
+ focusInputField("next", index);
193
+ },
194
+ Backspace: () => {
195
+ if (ev.target.value === "") {
196
+ const prevInput = descendants.prevValue(index, void 0, false);
197
+ if (!prevInput) return;
198
+ setValue("", index - 1, false);
199
+ prevInput.node.focus();
200
+ setMoveFocus(true);
201
+ } else {
202
+ setMoveFocus(false);
203
+ }
204
+ }
205
+ };
206
+ const action = actions[ev.key];
207
+ if (!action) return;
208
+ action();
182
209
  },
183
- [descendants, manageFocus, setValue]
210
+ [descendants, focusInputField, manageFocus, setValue]
184
211
  );
185
212
  const onFocus = (0, import_react.useCallback)(
186
213
  (index) => () => setFocusedIndex(index),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/pin-input.tsx"],"sourcesContent":["export { PinInput, PinInputField } from \"./pin-input\"\nexport type { PinInputFieldProps, PinInputProps } from \"./pin-input\"\n","import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) =>\n ({ key, target }: KeyboardEvent<HTMLInputElement>) => {\n if (key !== \"Backspace\" || !manageFocus) return\n\n if ((target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n [descendants, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/pin-input.tsx"],"sourcesContent":["export { PinInput, PinInputField } from \"./pin-input\"\nexport type { PinInputFieldProps, PinInputProps } from \"./pin-input\"\n","import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const focusInputField = useCallback(\n (direction: \"next\" | \"prev\", index: number) => {\n const input =\n direction === \"next\"\n ? descendants.nextValue(index, undefined, false)\n : descendants.prevValue(index, undefined, false)\n\n if (!input) return\n\n const valueLength = input.node.value.length\n\n requestAnimationFrame(() => {\n input.node.focus()\n input.node.setSelectionRange(0, valueLength)\n })\n },\n [descendants],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) => (ev: KeyboardEvent<HTMLInputElement>) => {\n if (!manageFocus) return\n\n const actions: { [key: string]: Function | undefined } = {\n ArrowLeft: () => {\n ev.preventDefault()\n focusInputField(\"prev\", index)\n },\n ArrowRight: () => {\n ev.preventDefault()\n focusInputField(\"next\", index)\n },\n Backspace: () => {\n if ((ev.target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n }\n\n const action = actions[ev.key]\n\n if (!action) return\n\n action()\n },\n [descendants, focusInputField, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PinInput,
4
4
  PinInputField
5
- } from "./chunk-MYBEULDW.mjs";
5
+ } from "./chunk-TU5BZGLV.mjs";
6
6
  export {
7
7
  PinInput,
8
8
  PinInputField
package/dist/pin-input.js CHANGED
@@ -102,6 +102,18 @@ var PinInput = (0, import_core.forwardRef)(
102
102
  },
103
103
  [descendants, moveFocus, manageFocus]
104
104
  );
105
+ const focusInputField = (0, import_react.useCallback)(
106
+ (direction, index) => {
107
+ const input = direction === "next" ? descendants.nextValue(index, void 0, false) : descendants.prevValue(index, void 0, false);
108
+ if (!input) return;
109
+ const valueLength = input.node.value.length;
110
+ requestAnimationFrame(() => {
111
+ input.node.focus();
112
+ input.node.setSelectionRange(0, valueLength);
113
+ });
114
+ },
115
+ [descendants]
116
+ );
105
117
  const setValue = (0, import_react.useCallback)(
106
118
  (value2, index, isFocus = true) => {
107
119
  var _a;
@@ -166,19 +178,34 @@ var PinInput = (0, import_core.forwardRef)(
166
178
  ]
167
179
  );
168
180
  const onKeyDown = (0, import_react.useCallback)(
169
- (index) => ({ key, target }) => {
170
- if (key !== "Backspace" || !manageFocus) return;
171
- if (target.value === "") {
172
- const prevInput = descendants.prevValue(index, void 0, false);
173
- if (!prevInput) return;
174
- setValue("", index - 1, false);
175
- prevInput.node.focus();
176
- setMoveFocus(true);
177
- } else {
178
- setMoveFocus(false);
179
- }
181
+ (index) => (ev) => {
182
+ if (!manageFocus) return;
183
+ const actions = {
184
+ ArrowLeft: () => {
185
+ ev.preventDefault();
186
+ focusInputField("prev", index);
187
+ },
188
+ ArrowRight: () => {
189
+ ev.preventDefault();
190
+ focusInputField("next", index);
191
+ },
192
+ Backspace: () => {
193
+ if (ev.target.value === "") {
194
+ const prevInput = descendants.prevValue(index, void 0, false);
195
+ if (!prevInput) return;
196
+ setValue("", index - 1, false);
197
+ prevInput.node.focus();
198
+ setMoveFocus(true);
199
+ } else {
200
+ setMoveFocus(false);
201
+ }
202
+ }
203
+ };
204
+ const action = actions[ev.key];
205
+ if (!action) return;
206
+ action();
180
207
  },
181
- [descendants, manageFocus, setValue]
208
+ [descendants, focusInputField, manageFocus, setValue]
182
209
  );
183
210
  const onFocus = (0, import_react.useCallback)(
184
211
  (index) => () => setFocusedIndex(index),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) =>\n ({ key, target }: KeyboardEvent<HTMLInputElement>) => {\n if (key !== \"Backspace\" || !manageFocus) return\n\n if ((target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n [descendants, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}
1
+ {"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const focusInputField = useCallback(\n (direction: \"next\" | \"prev\", index: number) => {\n const input =\n direction === \"next\"\n ? descendants.nextValue(index, undefined, false)\n : descendants.prevValue(index, undefined, false)\n\n if (!input) return\n\n const valueLength = input.node.value.length\n\n requestAnimationFrame(() => {\n input.node.focus()\n input.node.setSelectionRange(0, valueLength)\n })\n },\n [descendants],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) => (ev: KeyboardEvent<HTMLInputElement>) => {\n if (!manageFocus) return\n\n const actions: { [key: string]: Function | undefined } = {\n ArrowLeft: () => {\n ev.preventDefault()\n focusInputField(\"prev\", index)\n },\n ArrowRight: () => {\n ev.preventDefault()\n focusInputField(\"next\", index)\n },\n Backspace: () => {\n if ((ev.target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n }\n\n const action = actions[ev.key]\n\n if (!action) return\n\n action()\n },\n [descendants, focusInputField, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PinInput,
4
4
  PinInputField
5
- } from "./chunk-MYBEULDW.mjs";
5
+ } from "./chunk-TU5BZGLV.mjs";
6
6
  export {
7
7
  PinInput,
8
8
  PinInputField
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yamada-ui/pin-input",
3
- "version": "1.1.3-next-20241117114930",
3
+ "version": "1.2.0-dev-20241118202244",
4
4
  "description": "Yamada UI pin input component",
5
5
  "keywords": [
6
6
  "yamada",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport { createDescendant } from \"@yamada-ui/use-descendant\"\nimport {\n createContext,\n cx,\n filterUndefined,\n getValidChildren,\n handlerAll,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useEffect, useId, useState } from \"react\"\n\nconst toArray = (value?: string) => value?.split(\"\")\n\nconst validate = (value: string, type: PinInputProps[\"type\"]) => {\n const NUMERIC_REGEX = /^[0-9]+$/\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i\n\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX\n\n return regex.test(value)\n}\n\ninterface PinInputContext {\n styles: { [key: string]: CSSUIObject | undefined }\n getInputProps: (\n props: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps,\n ) => PinInputFieldProps\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n name: \"PinInputContext\",\n errorMessage: `PinInputContext returned is 'undefined'. Seems you forgot to wrap the components in \"<PinInput />\"`,\n})\n\nconst { DescendantsContextProvider, useDescendant, useDescendants } =\n createDescendant<HTMLInputElement>()\n\ninterface PinInputOptions {\n /**\n * The top-level id string that will be applied to the input fields.\n * The index of the input will be appended to this top-level id.\n */\n id?: string\n /**\n * The type of values the pin-input should allow.\n *\n * @default 'number'\n */\n type?: \"alphanumeric\" | \"number\"\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\n /**\n * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * If `true`, focus will move automatically to the next input once filled.\n *\n * @default true\n */\n manageFocus?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\n /**\n * If `true`, the pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * The placeholder for the pin input.\n *\n * @default '○'\n */\n placeholder?: string\n /**\n * The value of the pin input.\n */\n value?: string\n /**\n * Function called on input change.\n */\n onChange?: (value: string) => void\n /**\n * Function called when all inputs have valid values.\n */\n onComplete?: (value: string) => void\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"mask\" | \"onChange\">,\n ThemeProps<\"PinInput\">,\n FormControlOptions,\n PinInputOptions {}\n\n/**\n * `PinInput` is a component used to capture pin codes or OTP (One-Time Password) inputs.\n *\n * @see Docs https://yamada-ui.com/components/forms/pin-input\n */\nexport const PinInput = forwardRef<PinInputProps, \"div\">(\n ({ errorBorderColor, focusBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n errorBorderColor,\n focusBorderColor,\n ...props,\n })\n let {\n id,\n type = \"number\",\n className,\n autoFocus,\n children,\n defaultValue,\n items = 4,\n manageFocus = true,\n mask,\n otp = false,\n placeholder = \"○\",\n value,\n onChange: onChangeProp,\n onComplete,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": _ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n containerProps,\n ] = splitObject(rest, formControlProperties)\n const uuid = useId()\n\n id ??= uuid\n\n const descendants = useDescendants()\n\n const [moveFocus, setMoveFocus] = useState<boolean>(true)\n const [focusedIndex, setFocusedIndex] = useState<number>(-1)\n\n useEffect(() => {\n if (!autoFocus) return\n\n const firstValue = descendants.firstValue()\n\n if (!firstValue) return\n\n requestAnimationFrame(() => firstValue.node.focus())\n }, [autoFocus, descendants])\n\n const [values, setValues] = useControllableState<string[]>({\n defaultValue: toArray(defaultValue) || [],\n value: toArray(value),\n onChange: (values) => onChangeProp?.(values.join(\"\")),\n })\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus || !manageFocus) return\n\n const next = descendants.nextValue(index, undefined, false)\n\n if (!next) return\n\n requestAnimationFrame(() => next.node.focus())\n },\n [descendants, moveFocus, manageFocus],\n )\n\n const setValue = useCallback(\n (value: string, index: number, isFocus = true) => {\n let nextValues = [...values]\n\n nextValues[index] = value\n\n setValues(nextValues)\n\n nextValues = nextValues.filter(Boolean)\n\n const isComplete =\n value !== \"\" && nextValues.length === descendants.count()\n\n if (isComplete) {\n onComplete?.(nextValues.join(\"\"))\n descendants.value(index)?.node.blur()\n } else if (isFocus) {\n focusNext(index)\n }\n },\n [values, setValues, descendants, onComplete, focusNext],\n )\n\n const getNextValue = useCallback(\n (value: string | undefined, eventValue: string) => {\n let nextValue = eventValue\n\n if (!value?.length) return nextValue\n\n if (value.startsWith(eventValue.charAt(0))) {\n nextValue = eventValue.charAt(1)\n } else if (value.startsWith(eventValue.charAt(1))) {\n nextValue = eventValue.charAt(0)\n }\n\n return nextValue\n },\n [],\n )\n\n const onChange = useCallback(\n (index: number) =>\n ({ target }: ChangeEvent<HTMLInputElement>) => {\n const eventValue = target.value\n const currentValue = values[index]\n const nextValue = getNextValue(currentValue, eventValue)\n\n if (nextValue === \"\") {\n setValue(\"\", index)\n\n return\n }\n\n if (eventValue.length > 2) {\n if (!validate(eventValue, type)) return\n\n const nextValue = eventValue\n .split(\"\")\n .filter((_, index) => index < descendants.count())\n\n setValues(nextValue)\n\n if (nextValue.length === descendants.count()) {\n onComplete?.(nextValue.join(\"\"))\n descendants.value(index)?.node.blur()\n }\n } else {\n if (validate(nextValue, type)) setValue(nextValue, index)\n\n setMoveFocus(true)\n }\n },\n [\n descendants,\n getNextValue,\n onComplete,\n setValue,\n setValues,\n type,\n values,\n ],\n )\n\n const onKeyDown = useCallback(\n (index: number) =>\n ({ key, target }: KeyboardEvent<HTMLInputElement>) => {\n if (key !== \"Backspace\" || !manageFocus) return\n\n if ((target as HTMLInputElement).value === \"\") {\n const prevInput = descendants.prevValue(index, undefined, false)\n\n if (!prevInput) return\n\n setValue(\"\", index - 1, false)\n prevInput.node.focus()\n setMoveFocus(true)\n } else {\n setMoveFocus(false)\n }\n },\n [descendants, manageFocus, setValue],\n )\n\n const onFocus = useCallback(\n (index: number) => () => setFocusedIndex(index),\n [],\n )\n\n const onBlur = useCallback(() => setFocusedIndex(-1), [])\n\n const getInputProps = useCallback(\n ({\n index,\n ...props\n }: {\n index: number\n ref?: Ref<HTMLInputElement>\n } & PinInputFieldProps): PinInputFieldProps => ({\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n disabled,\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n readOnly,\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n value: values[index] || \"\",\n onBlur: handlerAll(props.onBlur, onBlur),\n onChange: handlerAll(props.onChange, onChange(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n }),\n [\n type,\n mask,\n formControlProps,\n id,\n values,\n onChange,\n onKeyDown,\n onFocus,\n onBlur,\n otp,\n focusedIndex,\n disabled,\n readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n display: \"flex\",\n ...styles.container,\n }\n\n let cloneChildren = getValidChildren(children)\n\n if (!cloneChildren.length)\n for (let i = 0; i < items; i++) {\n cloneChildren.push(<PinInputField key={i} />)\n }\n\n return (\n <DescendantsContextProvider value={descendants}>\n <PinInputProvider value={{ styles, getInputProps }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n role=\"group\"\n __css={css}\n {...formControlProps}\n {...containerProps}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nPinInput.displayName = \"PinInput\"\nPinInput.__ui__ = \"PinInput\"\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { styles, getInputProps } = usePinInputContext()\n const { index, register } = useDescendant()\n\n rest = useFormControlProps(rest)\n\n const css: CSSUIObject = { ...styles.field }\n\n return (\n <ui.input\n className={cx(\"ui-pin-input__field\", className)}\n {...getInputProps({ ...rest, ref: mergeRefs(register, ref), index })}\n __css={css}\n />\n )\n },\n)\n\nPinInputField.displayName = \"PinInputField\"\nPinInputField.__ui__ = \"PinInputField\"\n"],"mappings":"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","names":["values","value","nextValue","index","props"]}