@yamada-ui/pin-input 1.1.2-dev-20241110150602 → 1.1.2-dev-20241111191457
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-SZYUAKLR.mjs → chunk-MYBEULDW.mjs} +3 -3
- package/dist/{chunk-SZYUAKLR.mjs.map → chunk-MYBEULDW.mjs.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/pin-input.js +2 -2
- package/dist/pin-input.js.map +1 -1
- package/dist/pin-input.mjs +1 -1
- package/package.json +3 -3
@@ -183,14 +183,14 @@ var PinInput = forwardRef(
|
|
183
183
|
...props2
|
184
184
|
}) => ({
|
185
185
|
type: mask ? "password" : type === "number" ? "tel" : "text",
|
186
|
+
disabled,
|
186
187
|
inputMode: type === "number" ? "numeric" : "text",
|
188
|
+
readOnly,
|
187
189
|
...formControlProps,
|
188
190
|
...filterUndefined(props2),
|
189
191
|
id: `${id}-${index}`,
|
190
192
|
autoComplete: otp ? "one-time-code" : "off",
|
191
|
-
disabled,
|
192
193
|
placeholder: focusedIndex === index && !readOnly && !props2.readOnly ? "" : placeholder,
|
193
|
-
readOnly,
|
194
194
|
value: values[index] || "",
|
195
195
|
onBlur: handlerAll(props2.onBlur, onBlur),
|
196
196
|
onChange: handlerAll(props2.onChange, onChange(index)),
|
@@ -263,4 +263,4 @@ export {
|
|
263
263
|
PinInput,
|
264
264
|
PinInputField
|
265
265
|
};
|
266
|
-
//# sourceMappingURL=chunk-
|
266
|
+
//# sourceMappingURL=chunk-MYBEULDW.mjs.map
|
@@ -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 inputMode: type === \"number\" ? \"numeric\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n disabled,\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n readOnly,\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 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"]}
|
package/dist/index.js
CHANGED
@@ -193,14 +193,14 @@ var PinInput = (0, import_core.forwardRef)(
|
|
193
193
|
...props2
|
194
194
|
}) => ({
|
195
195
|
type: mask ? "password" : type === "number" ? "tel" : "text",
|
196
|
+
disabled,
|
196
197
|
inputMode: type === "number" ? "numeric" : "text",
|
198
|
+
readOnly,
|
197
199
|
...formControlProps,
|
198
200
|
...(0, import_utils.filterUndefined)(props2),
|
199
201
|
id: `${id}-${index}`,
|
200
202
|
autoComplete: otp ? "one-time-code" : "off",
|
201
|
-
disabled,
|
202
203
|
placeholder: focusedIndex === index && !readOnly && !props2.readOnly ? "" : placeholder,
|
203
|
-
readOnly,
|
204
204
|
value: values[index] || "",
|
205
205
|
onBlur: (0, import_utils.handlerAll)(props2.onBlur, onBlur),
|
206
206
|
onChange: (0, import_utils.handlerAll)(props2.onChange, onChange(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 inputMode: type === \"number\" ? \"numeric\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n disabled,\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n readOnly,\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 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"]}
|
package/dist/index.mjs
CHANGED
package/dist/pin-input.js
CHANGED
@@ -191,14 +191,14 @@ var PinInput = (0, import_core.forwardRef)(
|
|
191
191
|
...props2
|
192
192
|
}) => ({
|
193
193
|
type: mask ? "password" : type === "number" ? "tel" : "text",
|
194
|
+
disabled,
|
194
195
|
inputMode: type === "number" ? "numeric" : "text",
|
196
|
+
readOnly,
|
195
197
|
...formControlProps,
|
196
198
|
...(0, import_utils.filterUndefined)(props2),
|
197
199
|
id: `${id}-${index}`,
|
198
200
|
autoComplete: otp ? "one-time-code" : "off",
|
199
|
-
disabled,
|
200
201
|
placeholder: focusedIndex === index && !readOnly && !props2.readOnly ? "" : placeholder,
|
201
|
-
readOnly,
|
202
202
|
value: values[index] || "",
|
203
203
|
onBlur: (0, import_utils.handlerAll)(props2.onBlur, onBlur),
|
204
204
|
onChange: (0, import_utils.handlerAll)(props2.onChange, onChange(index)),
|
package/dist/pin-input.js.map
CHANGED
@@ -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 inputMode: type === \"number\" ? \"numeric\" : \"text\",\n ...formControlProps,\n ...filterUndefined(props),\n id: `${id}-${index}`,\n autoComplete: otp ? \"one-time-code\" : \"off\",\n disabled,\n placeholder:\n focusedIndex === index && !readOnly && !props.readOnly\n ? \"\"\n : placeholder,\n readOnly,\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 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"]}
|
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.1.2-dev-
|
3
|
+
"version": "1.1.2-dev-20241111191457",
|
4
4
|
"description": "Yamada UI pin input component",
|
5
5
|
"keywords": [
|
6
6
|
"yamada",
|
@@ -36,8 +36,8 @@
|
|
36
36
|
"url": "https://github.com/yamada-ui/yamada-ui/issues"
|
37
37
|
},
|
38
38
|
"dependencies": {
|
39
|
-
"@yamada-ui/core": "1.15.5-dev-
|
40
|
-
"@yamada-ui/form-control": "2.1.7-dev-
|
39
|
+
"@yamada-ui/core": "1.15.5-dev-20241111191457",
|
40
|
+
"@yamada-ui/form-control": "2.1.7-dev-20241111191457",
|
41
41
|
"@yamada-ui/use-controllable-state": "1.0.23",
|
42
42
|
"@yamada-ui/use-descendant": "1.0.25",
|
43
43
|
"@yamada-ui/utils": "1.5.4"
|