@vezham/input-otp
Version:
InputOTP is a component that allows users to enter otp input. It can be used to get user otp in forms.
392 lines (384 loc) • 14.7 kB
JavaScript
"use client";
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
InputOtp: () => input_otp_default,
REGEXP_ONLY_CHARS: () => import_input_otp3.REGEXP_ONLY_CHARS,
REGEXP_ONLY_DIGITS: () => import_input_otp3.REGEXP_ONLY_DIGITS,
REGEXP_ONLY_DIGITS_AND_CHARS: () => import_input_otp3.REGEXP_ONLY_DIGITS_AND_CHARS,
useInputOtp: () => useInputOtp
});
module.exports = __toCommonJS(index_exports);
// src/input-otp.tsx
var import_system2 = require("@vezham/system");
var import_react3 = require("react");
var import_input_otp = require("input-otp");
// src/use-input-otp.ts
var import_system = require("@vezham/system");
var import_theme = require("@vezham/theme");
var import_react_utils = require("@vezham/react-utils");
var import_shared_utils = require("@vezham/shared-utils");
var import_react = require("react");
var import_utils = require("@react-aria/utils");
var import_utils2 = require("@react-stately/utils");
var import_form = require("@react-stately/form");
var import_form2 = require("@react-aria/form");
var import_focus = require("@react-aria/focus");
var import_form3 = require("@vezham/form");
function useInputOtp(originalProps) {
var _a, _b, _c, _d;
const globalContext = (0, import_system.useProviderContext)();
const { validationBehavior: formValidationBehavior } = (0, import_form3.useSlottedContext)(import_form3.FormContext) || {};
const [props, variantProps] = (0, import_system.mapPropsVariants)(originalProps, import_theme.inputOtp.variantKeys);
const {
ref,
baseRef,
as,
className,
classNames,
length = 4,
autoFocus,
"aria-label": ariaLabel = "One-time password input",
onValueChange = () => {
},
allowedKeys = "^[0-9]*$",
validationBehavior = (_a = formValidationBehavior != null ? formValidationBehavior : globalContext == null ? void 0 : globalContext.validationBehavior) != null ? _a : "native",
type,
name,
maxLength,
minLength,
textAlign = "center",
onComplete = () => {
},
pushPasswordManagerStrategy,
pasteTransformer,
containerClassName,
noScriptCSSFallback,
onChange,
inputMode,
...otherProps
} = props;
const Component = as || "div";
const inputRef = (0, import_react_utils.useDOMRef)(ref);
const baseDomRef = (0, import_react_utils.useDOMRef)(baseRef);
const { isFocusVisible, isFocused, focusProps } = (0, import_focus.useFocusRing)({
autoFocus,
isTextInput: true
});
const handleValueChange = (0, import_react.useCallback)(
(value2) => {
onValueChange(value2 != null ? value2 : "");
},
[onValueChange]
);
const [value, setValue] = (0, import_utils2.useControlledState)(
props.value,
(_b = props.defaultValue) != null ? _b : "",
handleValueChange
);
const disableAnimation = (_d = (_c = originalProps.disableAnimation) != null ? _c : globalContext == null ? void 0 : globalContext.disableAnimation) != null ? _d : false;
const isDisabled = originalProps.isDisabled;
const baseStyles = (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.base, className);
const validationState = (0, import_form.useFormValidationState)({
...props,
validationBehavior,
value
});
(0, import_utils.useFormReset)(inputRef, value, setValue);
(0, import_form2.useFormValidation)({ ...props, validationBehavior }, validationState, inputRef);
const {
isInvalid: isAriaInvalid,
validationErrors,
validationDetails
} = validationState.displayValidation;
const isReadOnly = originalProps.isReadOnly;
const isRequired = originalProps.isRequired;
const isInvalid = originalProps.isInvalid || isAriaInvalid;
const errorMessage = typeof props.errorMessage === "function" ? props.errorMessage({ isInvalid, validationErrors, validationDetails }) : props.errorMessage || (validationErrors == null ? void 0 : validationErrors.join(" "));
const description = props.description;
const hasHelper = !!description || !!errorMessage;
const slots = (0, import_react.useMemo)(
() => (0, import_theme.inputOtp)({
...variantProps,
disableAnimation,
isInvalid,
isReadOnly
}),
[(0, import_shared_utils.objectToDeps)(variantProps), disableAnimation, isInvalid, isReadOnly]
);
const getBaseProps = (0, import_react.useCallback)(
(props2 = {}) => {
return {
ref: baseDomRef,
className: slots.base({
class: baseStyles
}),
"data-slot": "base",
"data-disabled": (0, import_shared_utils.dataAttr)(isDisabled),
"data-invalid": (0, import_shared_utils.dataAttr)(isInvalid),
"data-required": (0, import_shared_utils.dataAttr)(originalProps == null ? void 0 : originalProps.isRequired),
"data-readonly": (0, import_shared_utils.dataAttr)(originalProps == null ? void 0 : originalProps.isReadOnly),
"data-filled": (0, import_shared_utils.dataAttr)(value.length === length),
"aria-label": ariaLabel,
"aria-required": (0, import_shared_utils.dataAttr)(originalProps.isRequired),
"aria-readonly": (0, import_shared_utils.dataAttr)(originalProps == null ? void 0 : originalProps.isReadOnly),
role: "base",
...(0, import_utils.mergeProps)(
(0, import_react_utils.filterDOMProps)(otherProps, {
enabled: true
}),
(0, import_react_utils.filterDOMProps)(props2)
),
onChange: (e) => {
var _a2;
const val = (_a2 = e.target) == null ? void 0 : _a2.value;
const regex = new RegExp(allowedKeys);
if (regex.test(val)) {
onChange == null ? void 0 : onChange(e);
}
}
};
},
[baseDomRef, slots, baseStyles, isDisabled, isInvalid, isRequired, isReadOnly, value, length]
);
const getInputOtpProps = (0, import_react.useCallback)(
(props2 = {}) => {
var _a2;
const otpProps = {
...focusProps,
required: isRequired,
disabled: isDisabled,
readOnly: isReadOnly,
pattern: allowedKeys,
maxLength: maxLength != null ? maxLength : length,
minLength: minLength != null ? minLength : length,
textAlign,
ref: inputRef,
name,
value,
autoFocus,
onChange: setValue,
onBlur: (0, import_utils.chain)(focusProps.onBlur, props2 == null ? void 0 : props2.onBlur),
onComplete,
pushPasswordManagerStrategy,
pasteTransformer,
noScriptCSSFallback,
inputMode: inputMode != null ? inputMode : (0, import_shared_utils.isPatternNumeric)(allowedKeys) ? "numeric" : "text",
containerClassName: (_a2 = slots.wrapper) == null ? void 0 : _a2.call(slots, { class: (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.wrapper, containerClassName) }),
...props2
};
return otpProps;
},
[
inputMode,
isRequired,
isDisabled,
isReadOnly,
allowedKeys,
inputRef,
name,
value,
length,
setValue,
props.onBlur,
onComplete,
autoFocus
]
);
const getSegmentWrapperProps = (0, import_react.useCallback)(
(props2 = {}) => {
return {
className: slots.segmentWrapper({
class: (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.segmentWrapper, props2 == null ? void 0 : props2.className)
}),
"data-slot": "segment-wrapper",
"data-disabled": (0, import_shared_utils.dataAttr)(isDisabled),
"aria-label": ariaLabel,
...props2
};
},
[classNames == null ? void 0 : classNames.segmentWrapper, isDisabled]
);
const getHelperWrapperProps = (0, import_react.useCallback)(
(props2 = {}) => {
return {
className: slots.helperWrapper({
class: (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.helperWrapper, props2 == null ? void 0 : props2.className)
}),
"data-slot": "helper-wrapper",
...props2
};
},
[classNames == null ? void 0 : classNames.helperWrapper]
);
const getErrorMessageProps = (0, import_react.useCallback)(
(props2 = {}) => {
return {
className: slots.errorMessage({
class: (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.errorMessage, props2 == null ? void 0 : props2.className)
}),
"data-slot": "error-message",
...props2
};
},
[classNames == null ? void 0 : classNames.errorMessage]
);
const getDescriptionProps = (0, import_react.useCallback)(
(props2 = {}) => {
return {
className: slots.description({
class: (0, import_shared_utils.clsx)(classNames == null ? void 0 : classNames.description, props2 == null ? void 0 : props2.className)
}),
"data-slot": "description",
...props2
};
},
[classNames == null ? void 0 : classNames.description]
);
return {
Component,
inputRef,
length,
value,
type,
slots,
hasHelper,
classNames,
isInvalid,
description,
errorMessage,
isFocusVisible,
isFocused,
getBaseProps,
getInputOtpProps,
getSegmentWrapperProps,
getHelperWrapperProps,
getErrorMessageProps,
getDescriptionProps
};
}
// src/input-otp-context.ts
var import_react_utils2 = require("@vezham/react-utils");
var [InputOtpProvider, useInputOtpContext] = (0, import_react_utils2.createContext)({
name: "InputOtpContext",
errorMessage: "useInputOtpContext: `context` is undefined. Seems like you forgot to wrap all input-otp components within `<InputOtp />`"
});
// src/input-otp-segment.tsx
var import_react2 = require("react");
var import_shared_utils2 = require("@vezham/shared-utils");
var import_theme2 = require("@vezham/theme");
var import_jsx_runtime = require("react/jsx-runtime");
var InputOtpSegment = ({
...props
}) => {
var _a;
const { classNames, slots, type } = useInputOtpContext();
const passwordCharStyles = (0, import_shared_utils2.clsx)(classNames == null ? void 0 : classNames.passwordChar);
const caretStyles = (0, import_shared_utils2.clsx)(classNames == null ? void 0 : classNames.caret);
const segmentStyles = (0, import_shared_utils2.clsx)(classNames == null ? void 0 : classNames.segment);
const displayValue = (0, import_react2.useMemo)(() => {
var _a2, _b;
if (props.isActive && !props.char) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_theme2.cn)((_a2 = slots.caret) == null ? void 0 : _a2.call(slots, { class: caretStyles })) });
}
if (props.char) {
return type === "password" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_theme2.cn)((_b = slots.passwordChar) == null ? void 0 : _b.call(slots, { class: passwordCharStyles })) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: props.char });
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: props.placeholderChar });
}, [props.char, props.isActive, props.placeholderChar, type]);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
className: (0, import_theme2.cn)((_a = slots.segment) == null ? void 0 : _a.call(slots, { class: segmentStyles })),
"data-active": (0, import_shared_utils2.dataAttr)(props.isActive),
"data-focus": (0, import_shared_utils2.dataAttr)(props.isFocused && props.isActive),
"data-focus-visible": (0, import_shared_utils2.dataAttr)(props.isFocusVisible && props.isActive),
"data-has-value": (0, import_shared_utils2.dataAttr)(!!props.char),
"data-slot": "segment2",
role: "presentation",
children: displayValue
}
);
};
// src/input-otp.tsx
var import_jsx_runtime2 = require("react/jsx-runtime");
var InputOtp = (0, import_system2.forwardRef)((props, ref) => {
const context = useInputOtp({ ...props, ref });
const {
Component,
hasHelper,
isInvalid,
errorMessage,
description,
isFocusVisible,
isFocused,
getBaseProps,
getInputOtpProps,
getSegmentWrapperProps,
getHelperWrapperProps,
getErrorMessageProps,
getDescriptionProps
} = context;
const helperSection = (0, import_react3.useMemo)(() => {
if (!hasHelper) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...getHelperWrapperProps(), children: isInvalid && errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...getErrorMessageProps(), children: errorMessage }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...getDescriptionProps(), children: description }) });
}, [
hasHelper,
isInvalid,
errorMessage,
description,
getHelperWrapperProps,
getErrorMessageProps,
getDescriptionProps
]);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InputOtpProvider, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Component, { ...getBaseProps(), children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
import_input_otp.OTPInput,
{
...getInputOtpProps(),
render: ({ slots }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...getSegmentWrapperProps(), children: slots.map((slot, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
InputOtpSegment,
{
...slot,
isFocusVisible,
isFocused
},
idx
)) })
}
),
helperSection
] }) });
});
InputOtp.displayName = "HeroUI.InputOtp";
var input_otp_default = InputOtp;
// src/index.ts
var import_input_otp3 = require("input-otp");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
InputOtp,
REGEXP_ONLY_CHARS,
REGEXP_ONLY_DIGITS,
REGEXP_ONLY_DIGITS_AND_CHARS,
useInputOtp
});