@coreui/react-pro
Version:
UI Components Library for React.js
177 lines (174 loc) • 10.8 kB
JavaScript
import { __rest } from '../../node_modules/tslib/tslib.es6.js';
import React, { forwardRef, useRef, useMemo, Children, useState, useEffect, useCallback, isValidElement, cloneElement } from 'react';
import PropTypes from 'prop-types';
import classNames from '../../_virtual/index.js';
import { CFormControlWrapper } from '../form/CFormControlWrapper.js';
import { isValidInput, extractValidChars } from './utils.js';
import getNextActiveElement from '../../utils/getNextActiveElement.js';
import isRTL from '../../utils/isRTL.js';
const COneTimePassword = forwardRef((_a, ref) => {
var { ariaLabel = (index, total) => `Digit ${index + 1} of ${total}`, autoSubmit = false, children, className, defaultValue, disabled = false, feedback, feedbackInvalid, feedbackValid, floatingClassName, floatingLabel, id, invalid, label, linear = true, masked = false, name, onChange, onComplete, placeholder, readOnly = false, required = false, size, text, tooltipFeedback, type = 'number', valid, value } = _a, rest = __rest(_a, ["ariaLabel", "autoSubmit", "children", "className", "defaultValue", "disabled", "feedback", "feedbackInvalid", "feedbackValid", "floatingClassName", "floatingLabel", "id", "invalid", "label", "linear", "masked", "name", "onChange", "onComplete", "placeholder", "readOnly", "required", "size", "text", "tooltipFeedback", "type", "valid", "value"]);
const inputRefs = useRef([]);
const hiddenInputRef = useRef(null);
// Count valid OTP input children
const inputCount = useMemo(() => {
return Children.count(children);
}, [children]);
const [inputValues, setInputValues] = useState(() => {
var _a;
const initialValue = String((_a = value !== null && value !== void 0 ? value : defaultValue) !== null && _a !== void 0 ? _a : '');
return Array.from({ length: inputCount }, (_, i) => initialValue[i] || '');
});
// Update input values when value prop changes (controlled mode)
useEffect(() => {
if (value !== undefined) {
const valueString = String(value);
setInputValues(Array.from({ length: inputCount }, (_, i) => valueString[i] || ''));
}
}, [value, inputCount]);
// Update hidden input and trigger events
const updateValue = useCallback((newValues) => {
var _a;
const newValue = newValues.join('');
if (hiddenInputRef.current) {
hiddenInputRef.current.value = newValue;
}
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
if (newValue.length === inputCount) {
onComplete === null || onComplete === void 0 ? void 0 : onComplete(newValue);
if (autoSubmit) {
const form = (_a = hiddenInputRef.current) === null || _a === void 0 ? void 0 : _a.closest('form');
if (form && typeof form.requestSubmit === 'function') {
form.requestSubmit();
}
}
}
}, [autoSubmit, onChange, onComplete, inputCount]);
const handleInputChange = useCallback((index, event) => {
const inputValue = event.target.value;
if (inputValue.length === 1 && !isValidInput(inputValue, type)) {
return;
}
const newValues = [...inputValues];
newValues[index] = inputValue.length === 1 ? inputValue : '';
setInputValues(newValues);
updateValue(newValues);
if (inputValue.length === 1) {
const nextInput = getNextActiveElement(inputRefs.current, event.target, true, false);
nextInput === null || nextInput === void 0 ? void 0 : nextInput.focus();
}
}, [inputValues, type, updateValue]);
const handleInputFocus = useCallback((event) => {
const { target } = event;
if (target.value) {
setTimeout(() => {
target.select();
}, 0);
return;
}
if (linear) {
const firstEmptyInput = inputRefs.current.find((input) => !(input === null || input === void 0 ? void 0 : input.value));
if (firstEmptyInput && firstEmptyInput !== target) {
firstEmptyInput.focus();
}
}
}, [linear]);
const handleKeyDown = useCallback((event) => {
const { key, target } = event;
if (key === 'Backspace' && target.value === '') {
const newValues = [...inputValues];
const prevInput = getNextActiveElement(inputRefs.current, target, false, false);
if (prevInput) {
const prevIndex = inputRefs.current.indexOf(prevInput);
if (prevIndex !== -1) {
newValues[prevIndex] = '';
setInputValues(newValues);
updateValue(newValues);
prevInput.focus();
}
}
return;
}
if (key === 'ArrowRight') {
if (linear && target.value === '') {
return;
}
const shouldMoveNext = !isRTL(target);
const nextInput = getNextActiveElement(inputRefs.current, target, shouldMoveNext, false);
nextInput === null || nextInput === void 0 ? void 0 : nextInput.focus();
return;
}
if (key === 'ArrowLeft') {
const shouldMoveNext = isRTL(target);
const prevInput = getNextActiveElement(inputRefs.current, target, shouldMoveNext, false);
prevInput === null || prevInput === void 0 ? void 0 : prevInput.focus();
}
}, [inputValues, linear, updateValue]);
const handlePaste = useCallback((index, event) => {
var _a, _b;
event.preventDefault();
const pastedData = event.clipboardData.getData('text');
const validChars = extractValidChars(pastedData, type);
if (!validChars)
return;
const newValues = [...inputValues];
const startIndex = index;
for (let i = 0; i < validChars.length && startIndex + i < inputCount; i++) {
newValues[startIndex + i] = validChars[i];
}
setInputValues(newValues);
updateValue(newValues);
// Focus the next empty input or the last filled input
const nextEmptyIndex = startIndex + validChars.length;
if (nextEmptyIndex < inputCount) {
(_a = inputRefs.current[nextEmptyIndex]) === null || _a === void 0 ? void 0 : _a.focus();
}
else {
(_b = inputRefs.current.at(-1)) === null || _b === void 0 ? void 0 : _b.focus();
}
}, [inputValues, type, updateValue, inputCount]);
let inputIndex = 0;
return (React.createElement(CFormControlWrapper, { describedby: rest['aria-describedby'], feedback: feedback, feedbackInvalid: feedbackInvalid, feedbackValid: feedbackValid, floatingClassName: floatingClassName, floatingLabel: floatingLabel, id: id, invalid: invalid, label: label, text: text, tooltipFeedback: tooltipFeedback, valid: valid },
React.createElement("div", Object.assign({ className: classNames('form-otp', {
[`form-otp-${size}`]: size,
}, className), role: "group", ref: ref }, rest),
Children.map(children, (child, index) => {
var _a;
if (!isValidElement(child) ||
((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) !== 'COneTimePasswordInput') {
return child;
}
const inputChild = child;
const currentInputIndex = inputIndex++;
return cloneElement(inputChild, Object.assign(Object.assign({}, inputChild.props), { type: masked ? 'password' : 'text', className: classNames({
'is-invalid': invalid,
'is-valid': valid,
}, inputChild.props.className), id: inputChild.props.id || (id ? `${id}-${currentInputIndex}` : undefined), name: inputChild.props.name || (name ? `${name}-${currentInputIndex}` : undefined), placeholder: inputChild.props.placeholder ||
(placeholder && placeholder.length > 1
? placeholder[currentInputIndex]
: placeholder), value: inputValues[currentInputIndex] || '', tabIndex: currentInputIndex === 0 ? 0 : inputValues[currentInputIndex - 1] ? 0 : -1, disabled: disabled || inputChild.props.disabled, readOnly: readOnly || inputChild.props.readOnly, required: required || inputChild.props.required, 'aria-label': inputChild.props['aria-label'] || ariaLabel(currentInputIndex, inputCount), inputMode: type === 'number' ? 'numeric' : 'text', pattern: type === 'number' ? '[0-9]*' : '.*', onInput: (event) => {
var _a, _b;
handleInputChange(currentInputIndex, event);
(_b = (_a = inputChild.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, event);
}, onFocus: (event) => {
var _a, _b;
handleInputFocus(event);
(_b = (_a = inputChild.props).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, event);
}, onKeyDown: (event) => {
var _a, _b;
handleKeyDown(event);
(_b = (_a = inputChild.props).onKeyDown) === null || _b === void 0 ? void 0 : _b.call(_a, event);
}, onPaste: (event) => {
var _a, _b;
handlePaste(index, event);
(_b = (_a = inputChild.props).onPaste) === null || _b === void 0 ? void 0 : _b.call(_a, event);
}, ref: (el) => {
inputRefs.current[currentInputIndex] = el;
} }));
}),
React.createElement("input", { type: "hidden", id: id, name: name, value: inputValues.join(''), disabled: disabled, ref: hiddenInputRef }))));
});
COneTimePassword.propTypes = Object.assign({ ariaLabel: PropTypes.func, autoSubmit: PropTypes.bool, children: PropTypes.node, className: PropTypes.string, defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), disabled: PropTypes.bool, id: PropTypes.string, linear: PropTypes.bool, masked: PropTypes.bool, name: PropTypes.string, onChange: PropTypes.func, onComplete: PropTypes.func, placeholder: PropTypes.string, readOnly: PropTypes.bool, required: PropTypes.bool, size: PropTypes.oneOf(['sm', 'lg']), type: PropTypes.oneOf(['number', 'text']), value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }, CFormControlWrapper.propTypes);
COneTimePassword.displayName = 'COneTimePassword';
export { COneTimePassword };
//# sourceMappingURL=COneTimePassword.js.map