react-pin-code-input
Version:
React component for entering and validating a PIN code.
167 lines (162 loc) • 6.71 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
const defaultStyle = {
textAlign: 'center',
};
const defaultInputStyle = {
background: 'transparent',
border: 'none',
borderBottom: '1px solid #ccc',
borderRadius: 0,
boxShadow: 'none',
color: '#3e4958',
fontFamily: 'unset',
fontSize: '2rem',
height: '29px',
marginRight: '8px',
outline: 'none',
padding: '0 0 4px 0',
textAlign: 'center',
width: '36px',
userSelect: 'none',
};
const defaultInvalidInputStyle = {
...defaultInputStyle,
borderBottom: '1px solid #fcc',
};
const keyCode = {
BACKSPACE: 'Backspace',
ENTER: 'Enter',
LEFT_ARROW_COMPATIBILITY: 'Left',
LEFT_ARROW: 'ArrowLeft',
UP_ARROW_COMPATIBILITY: 'Up',
UP_ARROW: 'ArrowUp',
RIGHT_ARROW_COMPATIBILITY: 'Right',
RIGHT_ARROW: 'ArrowRight',
DOWN_ARROW_COMPATIBILITY: 'Down',
DOWN_ARROW: 'ArrowDown',
};
const patterns = {
number: /^$|^([0-9])$/,
tel: /^$|^([0-9])$/,
text: /^$|^(.)$/,
password: /^$|^(.)$/,
};
const defaultProps = {
type: 'number',
autoFocus: true,
disabled: false,
invalid: false,
disableInlineStyles: false,
className: 'react-pin-code-input',
id: 'react-pin-code-input',
};
const getFocusPosition = (array) => {
const position = array.findIndex((state) => state === '');
return position > -1 ? position : array.length;
};
function ReactPinCodeInputComponent(props) {
const { id, type, value: stateValues, onInputChange, className, autoFocus, pattern, disabled, required, invalid, disableInlineStyles, style, inputStyle, inputInvalidStyle, ...otherProps } = { ...defaultProps, ...props };
const focusIndex = autoFocus ? getFocusPosition(stateValues) : -1;
const refs = Array.from({ length: stateValues.length }).map(() => react.createRef());
const checkPattern = pattern || patterns[type];
react.useEffect(() => {
stateValues.forEach((value, index) => {
var _a;
const ref = (_a = refs[index]) === null || _a === void 0 ? void 0 : _a.current;
if (ref) {
ref.value = value;
}
});
}, [refs, stateValues]);
const onChange = (value, index) => {
const changedValues = [...stateValues];
changedValues[index] = value;
onInputChange(changedValues);
};
const onChangeAll = (values) => {
for (const [key, value] of values.entries()) {
if (checkPattern && !checkPattern.test(value)) {
values[key] = '';
}
}
onInputChange(values);
};
const onPaste = (e, index) => {
var _a, _b;
e.preventDefault();
const values = [...stateValues];
const clipboardValues = e.clipboardData.getData('Text').split('');
for (const [key, value] of clipboardValues.entries()) {
const ref = (_a = refs[index + key]) === null || _a === void 0 ? void 0 : _a.current;
if (!ref) {
break;
}
values[index + key] = value;
const next = (_b = refs[index + key + 1]) === null || _b === void 0 ? void 0 : _b.current;
if (next) {
next.focus();
continue;
}
ref.focus();
}
onChangeAll(values);
};
const onKeyDown = (e, index) => {
var _a, _b, _c;
const current = refs[index].current;
const next = (_a = refs[index + 1]) === null || _a === void 0 ? void 0 : _a.current;
const prev = (_b = refs[index - 1]) === null || _b === void 0 ? void 0 : _b.current;
const isValidKey = checkPattern && checkPattern.test(e.key);
if (!(e.ctrlKey || e.metaKey)) {
e.preventDefault();
}
switch (e.key) {
case keyCode.BACKSPACE:
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
onChangeAll(Array(stateValues.length).fill(''));
(_c = refs[0].current) === null || _c === void 0 ? void 0 : _c.focus();
}
else if ((current === null || current === void 0 ? void 0 : current.value) !== '') {
onChange('', index);
}
else if ((current === null || current === void 0 ? void 0 : current.value) === '' && index > 0) {
onChange('', index - 1);
prev === null || prev === void 0 ? void 0 : prev.focus();
}
break;
case keyCode.LEFT_ARROW:
case keyCode.LEFT_ARROW_COMPATIBILITY:
case keyCode.UP_ARROW:
case keyCode.UP_ARROW_COMPATIBILITY:
prev === null || prev === void 0 ? void 0 : prev.focus();
break;
case keyCode.RIGHT_ARROW:
case keyCode.RIGHT_ARROW_COMPATIBILITY:
case keyCode.DOWN_ARROW:
case keyCode.DOWN_ARROW_COMPATIBILITY:
next === null || next === void 0 ? void 0 : next.focus();
break;
default:
if ((current === null || current === void 0 ? void 0 : current.value) === e.key) {
next === null || next === void 0 ? void 0 : next.focus();
}
else if (current && isValidKey && !e.ctrlKey && !e.metaKey) {
onChange(e.key, index);
next === null || next === void 0 ? void 0 : next.focus();
}
}
};
return (jsxRuntime.jsx("div", Object.assign({ id: id, className: invalid ? `${className} invalid` : className, style: disableInlineStyles ? {} : { ...defaultStyle, ...style } }, otherProps, { children: refs.map((ref, index) => (jsxRuntime.jsx("input", { id: `${id}-${index}`, "data-id": `${index}`, type: type, autoFocus: index === focusIndex, value: stateValues[index], ref: ref, onChange: (e) => onChange(e.currentTarget.value, index), onKeyDown: (e) => onKeyDown(e, index), onPaste: (e) => onPaste(e, index), disabled: disabled, required: required, autoComplete: "off", style: disableInlineStyles
? {}
: invalid
? { ...defaultInvalidInputStyle, ...inputInvalidStyle }
: { ...defaultInputStyle, ...inputStyle } }, `${id}-${index}`))) }), void 0));
}
const ReactPinCodeInput = react.memo(ReactPinCodeInputComponent);
exports.ReactPinCodeInput = ReactPinCodeInput;
exports.ReactPinCodeInputComponent = ReactPinCodeInputComponent;
exports.defaultProps = defaultProps;