@yamada-ui/pin-input 1.0.42-dev-20240927170756 → 1.0.42-dev-20240927172557
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-P7VD2MDV.mjs → chunk-JKVJNYB5.mjs} +3 -2
- package/dist/chunk-JKVJNYB5.mjs.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/pin-input.js +2 -1
- package/dist/pin-input.js.map +1 -1
- package/dist/pin-input.mjs +1 -1
- package/package.json +6 -6
- package/dist/chunk-P7VD2MDV.mjs.map +0 -1
@@ -65,7 +65,8 @@ var PinInput = forwardRef(
|
|
65
65
|
"aria-readonly": _ariaReadonly,
|
66
66
|
...formControlProps
|
67
67
|
} = pickObject(rest, formControlProperties);
|
68
|
-
|
68
|
+
const uuid = useId();
|
69
|
+
id != null ? id : id = uuid;
|
69
70
|
const descendants = useDescendants();
|
70
71
|
const [moveFocus, setMoveFocus] = useState(true);
|
71
72
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
@@ -250,4 +251,4 @@ export {
|
|
250
251
|
PinInput,
|
251
252
|
PinInputField
|
252
253
|
};
|
253
|
-
//# sourceMappingURL=chunk-
|
254
|
+
//# sourceMappingURL=chunk-JKVJNYB5.mjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"mappings":"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","names":["values","value","nextValue","index","props"]}
|
package/dist/index.js
CHANGED
@@ -75,7 +75,8 @@ var PinInput = (0, import_core.forwardRef)(
|
|
75
75
|
"aria-readonly": _ariaReadonly,
|
76
76
|
...formControlProps
|
77
77
|
} = (0, import_utils.pickObject)(rest, import_form_control.formControlProperties);
|
78
|
-
|
78
|
+
const uuid = (0, import_react.useId)();
|
79
|
+
id != null ? id : id = uuid;
|
79
80
|
const descendants = useDescendants();
|
80
81
|
const [moveFocus, setMoveFocus] = (0, import_react.useState)(true);
|
81
82
|
const [focusedIndex, setFocusedIndex] = (0, import_react.useState)(-1);
|
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 { PinInputProps, PinInputFieldProps } from \"./pin-input\"\n","import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(rest, formControlProperties)\n\n id ??= useId()\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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"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 { PinInputProps, PinInputFieldProps } from \"./pin-input\"\n","import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"mappings":"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","names":["values","value","nextValue","index","props"]}
|
package/dist/index.mjs
CHANGED
package/dist/pin-input.js
CHANGED
@@ -73,7 +73,8 @@ var PinInput = (0, import_core.forwardRef)(
|
|
73
73
|
"aria-readonly": _ariaReadonly,
|
74
74
|
...formControlProps
|
75
75
|
} = (0, import_utils.pickObject)(rest, import_form_control.formControlProperties);
|
76
|
-
|
76
|
+
const uuid = (0, import_react.useId)();
|
77
|
+
id != null ? id : id = uuid;
|
77
78
|
const descendants = useDescendants();
|
78
79
|
const [moveFocus, setMoveFocus] = (0, import_react.useState)(true);
|
79
80
|
const [focusedIndex, setFocusedIndex] = (0, import_react.useState)(-1);
|
package/dist/pin-input.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(rest, formControlProperties)\n\n id ??= useId()\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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"mappings":"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","names":["values","value","nextValue","index","props"]}
|
1
|
+
{"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"mappings":"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","names":["values","value","nextValue","index","props"]}
|
package/dist/pin-input.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@yamada-ui/pin-input",
|
3
|
-
"version": "1.0.42-dev-
|
3
|
+
"version": "1.0.42-dev-20240927172557",
|
4
4
|
"description": "Yamada UI pin input component",
|
5
5
|
"keywords": [
|
6
6
|
"yamada",
|
@@ -36,11 +36,11 @@
|
|
36
36
|
"url": "https://github.com/yamada-ui/yamada-ui/issues"
|
37
37
|
},
|
38
38
|
"dependencies": {
|
39
|
-
"@yamada-ui/core": "1.15.1-dev-
|
40
|
-
"@yamada-ui/form-control": "2.1.3-dev-
|
41
|
-
"@yamada-ui/use-controllable-state": "1.0.21-dev-
|
42
|
-
"@yamada-ui/use-descendant": "1.0.22-dev-
|
43
|
-
"@yamada-ui/utils": "1.5.2-dev-
|
39
|
+
"@yamada-ui/core": "1.15.1-dev-20240927172557",
|
40
|
+
"@yamada-ui/form-control": "2.1.3-dev-20240927172557",
|
41
|
+
"@yamada-ui/use-controllable-state": "1.0.21-dev-20240927172557",
|
42
|
+
"@yamada-ui/use-descendant": "1.0.22-dev-20240927172557",
|
43
|
+
"@yamada-ui/utils": "1.5.2-dev-20240927172557"
|
44
44
|
},
|
45
45
|
"devDependencies": {
|
46
46
|
"clean-package": "2.2.0",
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../src/pin-input.tsx"],"sourcesContent":["import type {\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n ColorModeToken,\n CSS,\n} from \"@yamada-ui/core\"\nimport {\n ui,\n forwardRef,\n useComponentMultiStyle,\n omitThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\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 handlerAll,\n mergeRefs,\n pickObject,\n filterUndefined,\n getValidChildren,\n} from \"@yamada-ui/utils\"\nimport type { ChangeEvent, KeyboardEvent, Ref } from \"react\"\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 getInputProps: (\n props: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n },\n ) => PinInputFieldProps\n styles: { [key: string]: CSSUIObject }\n}\n\nconst [PinInputProvider, usePinInputContext] = createContext<PinInputContext>({\n strict: false,\n name: \"PinInputContext\",\n})\n\nconst { DescendantsContextProvider, useDescendants, useDescendant } =\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 * 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 * The initial value of the pin input.\n */\n defaultValue?: string\n /**\n * If `true`, the pin input receives focus on mount.\n *\n * @default false\n */\n autoFocus?: boolean\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 pin input component signals to its fields that they should.\n */\n otp?: boolean\n /**\n * If `true`, the input's value will be masked just like `type=password`.\n */\n mask?: boolean\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 * The number of inputs to display.\n *\n * @default 4\n */\n items?: number\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n}\n\nexport interface PinInputProps\n extends Omit<HTMLUIProps, \"defaultValue\" | \"onChange\" | \"mask\">,\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 ({ focusBorderColor, errorBorderColor, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"PinInput\", {\n focusBorderColor,\n errorBorderColor,\n ...props,\n })\n let {\n id,\n className,\n type = \"number\",\n placeholder = \"○\",\n value,\n defaultValue,\n autoFocus,\n manageFocus = true,\n otp = false,\n mask,\n onChange: onChangeProp,\n onComplete,\n items = 4,\n children,\n ...rest\n } = useFormControlProps(omitThemeProps(mergedProps))\n const {\n readOnly,\n \"aria-readonly\": _ariaReadonly,\n ...formControlProps\n } = pickObject(rest, formControlProperties)\n\n id ??= useId()\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 value: toArray(value),\n defaultValue: toArray(defaultValue) || [],\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: boolean = 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 !== \"\" &&\n nextValues.length === descendants.count() &&\n nextValues.every((value) => value != null && value !== \"\")\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[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1)\n } else if (value[0] === 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 }: PinInputFieldProps & {\n index: number\n ref?: Ref<HTMLInputElement>\n }): PinInputFieldProps => ({\n inputMode: type === \"number\" ? \"numeric\" : \"text\",\n type: mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n value: values[index] || \"\",\n onChange: handlerAll(props.onChange, onChange(index)),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown(index)),\n onFocus: handlerAll(props.onFocus, onFocus(index)),\n onBlur: handlerAll(props.onBlur, onBlur),\n autoComplete: otp ? \"one-time-code\" : \"off\",\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\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 readOnly,\n placeholder,\n ],\n )\n\n const css: CSSUIObject = {\n display: \"flex\",\n alignItems: \"center\",\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={{ getInputProps, styles }}>\n <ui.div\n ref={ref}\n className={cx(\"ui-pin-input\", className)}\n __css={css}\n {...rest}\n >\n {cloneChildren}\n </ui.div>\n </PinInputProvider>\n </DescendantsContextProvider>\n )\n },\n)\n\nexport interface PinInputFieldProps\n extends HTMLUIProps<\"input\">,\n FormControlOptions {}\n\nexport const PinInputField = forwardRef<PinInputFieldProps, \"input\">(\n ({ className, ...rest }, ref) => {\n const { getInputProps, styles } = 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"],"mappings":"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","names":["values","value","nextValue","index","props"]}
|