@yamada-ui/react
Version:
React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion
186 lines (182 loc) • 6.68 kB
JavaScript
"use client";
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
const require_dom = require('../../utils/dom.cjs');
const require_utils_index = require('../../utils/index.cjs');
const require_hooks_use_controllable_state_index = require('../../hooks/use-controllable-state/index.cjs');
const require_hooks_use_descendants_index = require('../../hooks/use-descendants/index.cjs');
const require_use_field_props = require('../field/use-field-props.cjs');
let react = require("react");
react = require_rolldown_runtime.__toESM(react);
//#region src/components/pin-input/use-pin-input.ts
const { DescendantsContext: PinInputDescendantsContext, useDescendant: usePinInputDescendant, useDescendants: usePinInputDescendants } = require_hooks_use_descendants_index.createDescendants();
const toArray = (value) => value?.split("");
const validate = (value, type) => {
return (type === "alphanumeric" ? /^[a-zA-Z0-9]+$/i : /^[0-9]+$/).test(value);
};
const usePinInput = (props = {}) => {
const uuid = (0, react.useId)();
const { props: { id = uuid, type = "number", autoFocus, defaultValue, disabled, manageFocus = true, mask, otp = false, placeholder = "◯", readOnly, value, onChange: onChangeProp, onComplete,...rest }, ariaProps, dataProps, eventProps } = require_use_field_props.useFieldProps(props);
const descendants = usePinInputDescendants();
const [moveFocus, setMoveFocus] = (0, react.useState)(true);
const [focusedIndex, setFocusedIndex] = (0, react.useState)(-1);
const [values, setValues] = require_hooks_use_controllable_state_index.useControllableState({
defaultValue: toArray(defaultValue) || [],
value: toArray(value),
onChange: (values$1) => onChangeProp?.(values$1.join(""))
});
const focusNext = (0, react.useCallback)((index) => {
if (!moveFocus || !manageFocus) return;
const next = descendants.nextValue(index, false);
if (!next) return;
requestAnimationFrame(() => next.node.focus());
}, [
descendants,
moveFocus,
manageFocus
]);
const focusInputField = (0, react.useCallback)((direction, index) => {
const input = direction === "next" ? descendants.nextValue(index, false) : descendants.prevValue(index, false);
if (!input) return;
const valueLength = input.node.value.length;
requestAnimationFrame(() => {
input.node.focus();
input.node.setSelectionRange(0, valueLength);
});
}, [descendants]);
const setValue = (0, react.useCallback)((value$1, index, focus = true) => {
let nextValues = [...values];
nextValues[index] = value$1;
setValues(nextValues);
nextValues = nextValues.filter(Boolean);
if (value$1 !== "" && nextValues.length === descendants.count()) {
onComplete?.(nextValues.join(""));
descendants.value(index)?.node.blur();
} else if (focus) focusNext(index);
}, [
values,
setValues,
descendants,
onComplete,
focusNext
]);
const getNextValue = (0, react.useCallback)((value$1, eventValue) => {
let nextValue = eventValue;
if (!value$1?.length) return nextValue;
if (value$1.startsWith(eventValue.charAt(0))) nextValue = eventValue.charAt(1);
else if (value$1.startsWith(eventValue.charAt(1))) nextValue = eventValue.charAt(0);
return nextValue;
}, []);
const onChange = (0, react.useCallback)((index) => ({ target }) => {
const eventValue = target.value;
const currentValue = values[index];
const nextValue = getNextValue(currentValue, eventValue);
if (nextValue === "") {
setValue("", index);
return;
}
if (eventValue.length > 2) {
if (!validate(eventValue, type)) return;
const nextValue$1 = eventValue.split("").filter((_, index$1) => index$1 < descendants.count());
setValues(nextValue$1);
if (nextValue$1.length === descendants.count()) {
onComplete?.(nextValue$1.join(""));
descendants.value(index)?.node.blur();
}
} else {
if (validate(nextValue, type)) setValue(nextValue, index);
setMoveFocus(true);
}
}, [
descendants,
getNextValue,
onComplete,
setValue,
setValues,
type,
values
]);
const onKeyDown = (0, react.useCallback)((index) => (ev) => {
if (!manageFocus) return;
require_dom.runKeyAction(ev, {
ArrowLeft: (ev$1) => {
ev$1.preventDefault();
focusInputField("prev", index);
},
ArrowRight: (ev$1) => {
ev$1.preventDefault();
focusInputField("next", index);
},
Backspace: (ev$1) => {
if (ev$1.target.value === "") {
const prevInput = descendants.prevValue(index, false);
if (!prevInput) return;
setValue("", index - 1, false);
prevInput.node.focus();
setMoveFocus(true);
} else setMoveFocus(false);
}
}, { preventDefault: false });
}, [
descendants,
focusInputField,
manageFocus,
setValue
]);
const onFocus = (0, react.useCallback)((index) => () => setFocusedIndex(index), []);
const onBlur = (0, react.useCallback)(() => setFocusedIndex(-1), []);
(0, react.useEffect)(() => {
if (!autoFocus) return;
const firstValue = descendants.firstValue();
if (!firstValue) return;
requestAnimationFrame(() => firstValue.node.focus());
}, [autoFocus, descendants]);
const getRootProps = (0, react.useCallback)((props$1) => ({
role: "group",
...rest,
...props$1
}), [rest]);
return {
descendants,
getInputProps: (0, react.useCallback)(({ index,...props$1 }) => ({
...ariaProps,
...dataProps,
type: mask ? "password" : type === "number" ? "tel" : "text",
autoComplete: otp ? "one-time-code" : "off",
disabled,
inputMode: type === "number" ? "numeric" : "text",
placeholder: focusedIndex === index && !readOnly && !props$1.readOnly ? "" : placeholder,
readOnly,
value: values[index] || "",
...(0, require_utils_index.utils_exports.filterUndefined)(props$1),
id: `${id}${index ? `-${index}` : ""}`,
onBlur: (0, require_utils_index.utils_exports.handlerAll)(eventProps.onBlur, props$1.onBlur, onBlur),
onChange: (0, require_utils_index.utils_exports.handlerAll)(props$1.onChange, onChange(index)),
onFocus: (0, require_utils_index.utils_exports.handlerAll)(eventProps.onFocus, props$1.onFocus, onFocus(index)),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(props$1.onKeyDown, onKeyDown(index))
}), [
ariaProps,
dataProps,
eventProps,
mask,
type,
disabled,
readOnly,
id,
otp,
focusedIndex,
placeholder,
values,
onBlur,
onChange,
onFocus,
onKeyDown
]),
getRootProps
};
};
//#endregion
exports.PinInputDescendantsContext = PinInputDescendantsContext;
exports.usePinInput = usePinInput;
exports.usePinInputDescendant = usePinInputDescendant;
exports.usePinInputDescendants = usePinInputDescendants;
//# sourceMappingURL=use-pin-input.cjs.map