UNPKG

react-masked-field

Version:
326 lines (319 loc) 12.3 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var tslib = require('tslib'); var React = _interopDefault(require('react')); var PropTypes = _interopDefault(require('prop-types')); function getSelection(node) { return { start: node.selectionStart || 0, end: node.selectionEnd || 0 }; } function setSelection(node, start, end) { node.setSelectionRange(start, end); } /** * Copyright (c) 2015 ZenPayroll * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var DEFAULT_TRANSLATIONS = { 9: /\d/, a: /[A-Za-z]/, '*': /[A-Za-z0-9]/ }; var BLANK_CHAR = '_'; var AlwaysMaskedField = /** @class */ (function (_super) { tslib.__extends(AlwaysMaskedField, _super); function AlwaysMaskedField(props) { var _this = _super.call(this, props) || this; _this.buffer = []; _this.firstNonMaskIdx = -1; _this.cursorPos = -1; _this.input = null; _this.handleFocus = function (e) { setTimeout(function () { return _this.setSelection(_this.cursorPos); }, 0); var onFocus = _this.props.onFocus; if (onFocus) { onFocus(e); } _this.setState({ value: _this.bufferString() }); }; _this.handleBlur = function (e) { if (_this.isBufferEmpty()) { _this.setValue(''); } var onBlur = _this.props.onBlur; if (onBlur) { onBlur(e); } }; _this.handleKeyDown = function (e) { if (e.key === 'Backspace' || e.key === 'Delete') { var _a = _this.getSelection(), start = _a.start, end = _a.end; if (start === end) { start = e.key === 'Delete' ? _this.nextNonMaskIdx(start - 1) : _this.prevNonMaskIdx(start); end = _this.nextNonMaskIdx(start); } var newVal = void 0; var pattern = _this.getPattern(start); if (pattern && pattern.test(_this.buffer[end])) { var value = _this.state.value; newVal = _this.maskedValue(value.substring(end), start); } else { _this.resetBuffer(start, end); newVal = _this.bufferString(); } _this.setValue(newVal); _this.cursorPos = Math.max(start, _this.firstNonMaskIdx); e.preventDefault(); } var onKeyDown = _this.props.onKeyDown; if (onKeyDown) { onKeyDown(e); } }; _this.handleChange = function (e) { var value = _this.maskedValue(e.target.value); _this.setValue(value); _this.callOnComplete(value); }; _this.buffer = _this.initialBuffer(); _this.cursorPos = _this.firstNonMaskIdx; var propsValue = _this.getPropsValue(); _this.state = { // TODO: Any way we can do this in one pass? value: propsValue ? _this.maskedValue(propsValue) : '' }; return _this; } AlwaysMaskedField.prototype.componentDidMount = function () { var propsValue = this.getPropsValue(); var value = this.state.value; if (typeof propsValue === 'string' && value !== propsValue) { this.callOnChange(value); } }; AlwaysMaskedField.prototype.componentDidUpdate = function (_a) { var prevValue = _a.value; if (this.cursorPos !== -1) { this.setSelection(this.cursorPos); } var value = this.props.value; if (value && value !== prevValue && value !== this.bufferString()) { var maskedValue = this.maskedValue(value); this.setValue(maskedValue); this.callOnComplete(maskedValue); } }; AlwaysMaskedField.prototype.getSelection = function () { if (this.input) { return getSelection(this.input); } var cursorPos = (this.getPropsValue() || '').length; return { start: cursorPos, end: cursorPos }; }; AlwaysMaskedField.prototype.getPropsValue = function () { var _a = this.props, valueLink = _a.valueLink, value = _a.value; if (valueLink) { return valueLink.value; } return value; }; AlwaysMaskedField.prototype.getPattern = function (idx) { var _a = this.props, mask = _a.mask, translations = _a.translations; var maskChar = mask[idx]; var pattern = translations ? translations[maskChar] : null; return pattern || DEFAULT_TRANSLATIONS[maskChar]; }; AlwaysMaskedField.prototype.setSelection = function (start, end) { if (end === void 0) { end = start; } if (this.input && this.input === document.activeElement) { setSelection(this.input, start, end); } }; AlwaysMaskedField.prototype.setValue = function (newVal) { var value = this.state.value; if (newVal !== value) { this.callOnChange(newVal); } this.setState({ value: newVal }); }; AlwaysMaskedField.prototype.resetBuffer = function (start, end) { for (var i = start; i < end; i += 1) { if (this.getPattern(i)) { this.buffer[i] = BLANK_CHAR; } } }; AlwaysMaskedField.prototype.initialBuffer = function () { var buffer = []; var mask = this.props.mask; for (var idx = 0; idx < mask.length; idx += 1) { if (this.getPattern(idx)) { if (this.firstNonMaskIdx === -1) { this.firstNonMaskIdx = idx; } buffer.push('_'); } else { buffer.push(mask[idx]); } } return buffer; }; AlwaysMaskedField.prototype.bufferString = function () { return this.buffer.join(''); }; AlwaysMaskedField.prototype.isBufferEmpty = function () { var _this = this; return this.buffer.every(function (char, idx) { return !_this.getPattern(idx) || char === BLANK_CHAR; }); }; AlwaysMaskedField.prototype.isBufferFull = function () { var _this = this; return this.buffer.every(function (char, idx) { return !_this.getPattern(idx) || char !== BLANK_CHAR; }); }; AlwaysMaskedField.prototype.nextNonMaskIdx = function (idx) { var next = idx + 1; var mask = this.props.mask; for (; next < mask.length; next += 1) { if (this.getPattern(next)) { break; } } return next; }; AlwaysMaskedField.prototype.prevNonMaskIdx = function (idx) { var prev = idx - 1; for (; prev >= 0; prev -= 1) { if (this.getPattern(prev)) { break; } } return prev; }; AlwaysMaskedField.prototype.callOnChange = function (value) { var _a = this.props, id = _a.id, name = _a.name, valueLink = _a.valueLink, onChange = _a.onChange; if (valueLink) { valueLink.requestChange(value); } else if (onChange) { onChange({ target: { id: id, name: name, value: value } }); } }; AlwaysMaskedField.prototype.callOnComplete = function (value) { var onComplete = this.props.onComplete; if (onComplete && this.isBufferFull()) { onComplete(value); } }; AlwaysMaskedField.prototype.maskedValue = function (value, start) { if (start === void 0) { start = 0; } this.cursorPos = this.getSelection().start; var originalCursorPos = this.cursorPos; var mask = this.props.mask; for (var bufferIdx = start, valueIdx = 0; bufferIdx < mask.length; bufferIdx += 1) { var pattern = this.getPattern(bufferIdx); if (pattern) { var lastPatternIdx = bufferIdx; this.buffer[bufferIdx] = BLANK_CHAR; while (valueIdx < value.length && bufferIdx < mask.length) { var c = value[valueIdx]; valueIdx += 1; if (c === this.buffer[bufferIdx]) { bufferIdx += 1; } else if (pattern.test(c)) { while (this.buffer[bufferIdx] !== '_' && bufferIdx < this.buffer.length) { bufferIdx += 1; } if (this.buffer[bufferIdx] !== undefined) { this.buffer[bufferIdx] = c; } break; } else if (this.cursorPos > lastPatternIdx) { this.cursorPos -= 1; } } if (valueIdx >= value.length) { this.resetBuffer(lastPatternIdx + 1, mask.length); break; } } else if (this.buffer[bufferIdx] === value[valueIdx]) { if (valueIdx === originalCursorPos) { this.cursorPos += 1; } valueIdx += 1; } else if (valueIdx <= originalCursorPos) { this.cursorPos += 1; } } return this.bufferString(); }; AlwaysMaskedField.prototype.render = function () { var _this = this; var _a = this.props, mask = _a.mask, translations = _a.translations, onComplete = _a.onComplete, valueLink = _a.valueLink, placeholder = _a.placeholder, inputRef = _a.inputRef, props = tslib.__rest(_a, ["mask", "translations", "onComplete", "valueLink", "placeholder", "inputRef"]); var value = this.state.value; return (React.createElement("input", tslib.__assign({ ref: function (c) { _this.input = c; if (inputRef) { inputRef(c); } } }, props, { onChange: this.handleChange, onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur, value: value, placeholder: placeholder || this.initialBuffer().join(''), type: "text" }))); }; AlwaysMaskedField.propTypes = { mask: PropTypes.string, translations: PropTypes.objectOf(PropTypes.instanceOf(RegExp)), value: PropTypes.string, placeholder: PropTypes.string, onChange: PropTypes.func, onKeyDown: PropTypes.func, onComplete: PropTypes.func, onFocus: PropTypes.func, onBlur: PropTypes.func, valueLink: PropTypes.shape({ value: PropTypes.string.isRequired, requestChange: PropTypes.func.isRequired }) }; AlwaysMaskedField.defaultProps = { mask: undefined, translations: undefined, value: undefined, placeholder: undefined, onChange: undefined, onKeyDown: undefined, onComplete: undefined, onFocus: undefined, onBlur: undefined, valueLink: undefined }; return AlwaysMaskedField; }(React.Component)); /** * Copyright (c) 2015 ZenPayroll * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var MaskedField = function (_a) { var mask = _a.mask, props = tslib.__rest(_a, ["mask"]); if (mask) { return React.createElement(AlwaysMaskedField, tslib.__assign({ mask: mask }, props)); } var translations = props.translations, onComplete = props.onComplete, valueLink = props.valueLink, inputRef = props.inputRef, inputProps = tslib.__rest(props, ["translations", "onComplete", "valueLink", "inputRef"]); return React.createElement("input", tslib.__assign({}, inputProps, { type: "text" })); }; MaskedField.propTypes = { mask: PropTypes.string }; MaskedField.defaultProps = { mask: undefined }; module.exports = MaskedField; //# sourceMappingURL=index.js.map