UNPKG

@helpscout/hsds-react

Version:

React component library for Help Scout's Design System

359 lines (288 loc) 13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _getValidProps = _interopRequireDefault(require("@helpscout/react-utils/dist/getValidProps")); var _classnames = _interopRequireDefault(require("classnames")); var _VisuallyHidden = _interopRequireDefault(require("../VisuallyHidden")); var _clipboard = require("../../utilities/clipboard"); var _VerificationCode = require("./VerificationCode.utils"); var _VerificationCode2 = require("./VerificationCode.css"); var _Tooltip = _interopRequireDefault(require("../Tooltip")); var _jsxRuntime = require("react/jsx-runtime"); var VerificationCode = /*#__PURE__*/function (_React$Component) { (0, _inheritsLoose2.default)(VerificationCode, _React$Component); function VerificationCode() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this; _this.setVerificationCodeFieldNode = function (node) { _this.verificationCodeFieldRef = node; }; _this.setClipboardPlaceholderNode = function (node) { _this.clipboardPlaceholderRef = node; }; _this.handleChange = function (value) { var onChange = _this.props.onChange; onChange(value); }; _this.handlePaste = function (e) { var _this$props = _this.props, numberOfChars = _this$props.numberOfChars, autoSubmitPaste = _this$props.autoSubmitPaste, onEnter = _this$props.onEnter; var clipboardData = e.clipboardData || window.clipboardData; var pastedData = clipboardData.getData('Text'); e.stopPropagation(); e.preventDefault(); if (pastedData.length > 0) { pastedData.slice(0, numberOfChars).split('').forEach(function (char, index, arr) { if (_this.digitInputNodes.length > 0) { _this.digitInputNodes[index].value = char; _this.digitMaskNodes[index].innerText = char; if (index === arr.length - 1) { _this.digitInputNodes[index].focus(); } } }); var value = (0, _VerificationCode.getCurrentCodeValue)(_this.digitInputNodes); _this.handleChange(value); if (autoSubmitPaste && value.length === numberOfChars) { onEnter(value); } } }; _this.handleKeyDown = function (e) { var key = e.key, metaKey = e.metaKey, ctrlKey = e.ctrlKey; if ((metaKey || ctrlKey) && key === 'a') { (0, _VerificationCode.selectAll)(_this.digitInputNodes, _this.digitMaskNodes); e.preventDefault(); } else if ((metaKey || ctrlKey) && key === 'c') { var selectionText = _this.digitMaskNodes.map(function (el) { return el.innerText; }).join(''); _this.clipboardPlaceholderRef.value = selectionText; _this.clipboardPlaceholderRef.select(); (0, _clipboard.copyToClipboard)(); // if we trigger selectAll to close of the copy action, it interfere with the value copied over setTimeout(function () { return (0, _VerificationCode.selectAll)(_this.digitInputNodes, _this.digitMaskNodes); }, 50); } else if (key === 'Enter') { var onEnter = _this.props.onEnter; var code = _this.digitInputNodes.map(function (input) { return input.value; }).join('').trim(); onEnter(code); } }; _this.handleMouseDown = function (e) { var activeElement = document.activeElement; var target = e.target; if (!target.classList.contains('DigitInput') && !activeElement.classList.contains('DigitInput')) { // First we check if the click a) Wasn't executed directly on the input and b) one of the inputs is already focused // If that is the case, we focus the first input // We use a timeout to take the execution of this action out of the event loop so that the browser doesn't focus the body setTimeout(function () { _this.digitInputNodes[0].focus(); }, 0); } else if (!target.classList.contains('DigitInput')) { // If the click happened anywhere inside but not an input, the browser tries to focus the body (or a parent), we bring the focus // back to the cached activeElement using a timeout as above setTimeout(function () { activeElement.focus(); }, 0); } }; _this.handleInputKeyUp = function (index, e) { var key = e.key; if (key !== 'Meta') { var value = e.target.value; var _this$props2 = _this.props, numberOfChars = _this$props2.numberOfChars, autoSubmitKeyUp = _this$props2.autoSubmitKeyUp, onEnter = _this$props2.onEnter; var digitMask = _this.digitMaskNodes[index]; if (key === 'Backspace') { var selectionText = (0, _VerificationCode.getCleanSelectedText)(); if (selectionText.length > 1) { (0, _VerificationCode.clearAll)(_this.digitInputNodes, _this.digitMaskNodes); (0, _VerificationCode.showInputDigits)(_this.digitInputNodes, _this.digitMaskNodes); _this.digitInputNodes[0].focus(); _this.handleChange(''); } else if (value === '' && !digitMask.innerText) { // Tested ¯\_(ツ)_/¯ var prevIndex = index === 0 ? 0 : index - 1; var previousDigit = _this.digitInputNodes[prevIndex]; digitMask.innerText = value; previousDigit && previousDigit.select(); _this.handleChange((0, _VerificationCode.getCurrentCodeValue)(_this.digitInputNodes)); } else { digitMask.innerText = value; _this.handleChange((0, _VerificationCode.getCurrentCodeValue)(_this.digitInputNodes)); } } else if (key === 'ArrowLeft') { var _prevIndex = index === 0 ? 0 : index - 1; var _previousDigit = _this.digitInputNodes[_prevIndex]; _previousDigit && _previousDigit.select(); } else if (key === 'ArrowRight') { var nextIndex = index === numberOfChars - 1 ? numberOfChars : index + 1; var nextDigit = _this.digitInputNodes[nextIndex]; nextDigit && nextDigit.select(); } else if (key.length === 1) { // all chars have a length of 1, special keys have more var _nextIndex = index === numberOfChars - 1 ? numberOfChars : index + 1; var _nextDigit = _this.digitInputNodes[_nextIndex]; digitMask.innerText = value; _nextDigit && _nextDigit.select(); var currentCodeValue = (0, _VerificationCode.getCurrentCodeValue)(_this.digitInputNodes); _this.handleChange(currentCodeValue); if (autoSubmitKeyUp && currentCodeValue.length === numberOfChars) { onEnter(currentCodeValue); } } } }; _this.handleInputClick = function (e) { (0, _VerificationCode.showInputDigits)(_this.digitInputNodes, _this.digitMaskNodes); e.target.select(); }; return _this; } var _proto = VerificationCode.prototype; _proto.componentDidMount = function componentDidMount() { var _this2 = this; this.digitInputNodes = Array.from(this.verificationCodeFieldRef.querySelectorAll('.DigitInput')); this.digitMaskNodes = Array.from(this.verificationCodeFieldRef.querySelectorAll('.DigitMask')); var _this$props3 = this.props, code = _this$props3.code, autoFocus = _this$props3.autoFocus, numberOfChars = _this$props3.numberOfChars; var startIndex = 0; if (code) { code.slice(0, numberOfChars).split('').forEach(function (char, index, arr) { if (_this2.digitInputNodes.length > 0) { _this2.digitInputNodes[index].value = char; _this2.digitMaskNodes[index].innerText = char; if (char) startIndex = index; } }); } if (autoFocus && this.digitInputNodes[startIndex]) { this.digitInputNodes[startIndex].focus(); } }; _proto.componentDidUpdate = function componentDidUpdate(prevProps) { var _this3 = this; var _this$props4 = this.props, code = _this$props4.code, numberOfChars = _this$props4.numberOfChars; if (prevProps.code !== code) { if (code) { (0, _VerificationCode.clearAll)(this.digitInputNodes, this.digitMaskNodes); code.slice(0, numberOfChars).split('').forEach(function (char, index, arr) { if (_this3.digitInputNodes.length > 0) { _this3.digitInputNodes[index].value = char; _this3.digitMaskNodes[index].innerText = char; } }); } } }; _proto.getClassName = function getClassName() { var _this$props5 = this.props, className = _this$props5.className, isValid = _this$props5.isValid; return (0, _classnames.default)('c-VerificationCode', !isValid && 'not-valid', className); }; _proto.render = function render() { var _this4 = this; var _this$props6 = this.props, numberOfChars = _this$props6.numberOfChars, isValid = _this$props6.isValid, rest = (0, _objectWithoutPropertiesLoose2.default)(_this$props6, ["numberOfChars", "isValid"]); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_VerificationCode2.VerificationCodeFieldUI, (0, _extends2.default)({}, (0, _getValidProps.default)(rest), { className: this.getClassName(), ref: this.setVerificationCodeFieldNode, onPaste: this.handlePaste, onKeyDown: this.handleKeyDown, onMouseDown: this.handleMouseDown, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_VerificationCode2.ClipboardPlaceholderUI, { readOnly: true, tabIndex: "-1", ref: this.setClipboardPlaceholderNode }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_VisuallyHidden.default, { id: "digitInput", children: "Code Digit" }), Array(numberOfChars).fill(0).map(function (_, index) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_VerificationCode2.DigitInputWrapperUI, { className: "DigitInputWrapper", children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_VerificationCode2.DigitMaskUI, { className: "DigitMask " + _VerificationCode.CLASSNAMES.hidden }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_VerificationCode2.DigitInputUI, { className: "DigitInput", "aria-labelledby": "digitInput", maxLength: "1", onClick: _this4.handleInputClick, onKeyUp: function onKeyUp(e) { _this4.handleInputKeyUp(index, e); } })] }, "DigitInput-" + index); }), !isValid ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_VerificationCode2.ValidIconUI, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { animationDelay: 0, animationDuration: 0, appendTo: document.body, display: "block", placement: "top-end", title: "Invalid verification code", children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_VerificationCode2.IconUI, { name: "alert", size: 24 }) }) }) : null] })); }; return VerificationCode; }(_react.default.Component); function noop() {} VerificationCode.defaultProps = { autoFocus: false, autoSubmitPaste: false, autoSubmitKeyUp: false, code: '', 'data-cy': 'VerificationCode', isValid: true, numberOfChars: 6, onEnter: noop, onChange: noop }; VerificationCode.propTypes = { autoFocus: _propTypes.default.bool, autoSubmitPaste: _propTypes.default.bool, autoSubmitKeyUp: _propTypes.default.bool, /** If you need to assign a value externally, use this prop. */ code: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), /** Gives the field invalid stylings */ isValid: _propTypes.default.bool, /** If you need more or less number of characters */ numberOfChars: _propTypes.default.number, /** To get the current value on enter press */ onEnter: _propTypes.default.func, /** To get the current value on change */ onChange: _propTypes.default.func, /** Data attr for Cypress tests. */ 'data-cy': _propTypes.default.string }; var _default = VerificationCode; exports.default = _default;