UNPKG

react-pin-code-input

Version:

React component for entering and validating a PIN code.

191 lines (183 loc) 7.94 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); var PropTypes = require('prop-types'); var jsxRuntime = require('react/jsx-runtime'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); 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)); } react.memo(ReactPinCodeInputComponent); // If you are using this file is assumed you have installed prop-types // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access ReactPinCodeInputComponent.propTypes = { type: PropTypes__default['default'].oneOf(['text', 'number', 'password', 'tel']), autoFocus: PropTypes__default['default'].bool, disabled: PropTypes__default['default'].bool, invalid: PropTypes__default['default'].bool, onInputChange: PropTypes__default['default'].func.isRequired, value: PropTypes__default['default'].arrayOf(PropTypes__default['default'].string.isRequired).isRequired, className: PropTypes__default['default'].string, pattern: PropTypes__default['default'].instanceOf(RegExp), required: PropTypes__default['default'].bool, disableInlineStyles: PropTypes__default['default'].bool, style: PropTypes__default['default'].object, inputStyle: PropTypes__default['default'].object, inputInvalidStyle: PropTypes__default['default'].object, }; const ReactPinCodeInput = react.memo(ReactPinCodeInputComponent); exports.ReactPinCodeInput = ReactPinCodeInput;