UNPKG

react-rxinput

Version:

react-rxinput React extends input element to validate against a regular expression as you type input (incremental regex matcher)

672 lines (577 loc) 21.8 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } //component.js import React, { Component } from 'react'; import { Popover, OverlayTrigger } from 'react-bootstrap'; import RX, { convertMask, contract, RXInputMask, isMeta } from "incr-regex-package"; // const RX = require("incr-regex-package"); // const {convertMask,contract,RXInputMask,isMeta} = RX; /** * Copyright (c) 2016, Nurul Choudhury * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ // // Modified from https://github.com/insin/react-maskedinput // This was originally written by insin - on GIT hub // The code worked fine for fixed formatted input mask, but is not so useful for // varible mask based on regular expression (RegExp) // That capability regires this implementation of Regexp, and provides incremental processing of regular expression // Amost the entire original code has been replaces but the original interfaces remain // //const convertMask = convertMask; var rxPlaceHolder = new RegExp(convertMask('[?*]')); var KEYCODE_Z = 90; var KEYCODE_Y = 89; function except(object, list) { var result = {}; var keys = list; for (var key in object) { if (keys.indexOf(key) === -1) { result[key] = object[key]; } } return result; } function isUndo(e) { return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z); } function isRedo(e) { return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y); } function getSelection(el) { var start = void 0, end = void 0, rangeEl = void 0, clone = void 0; if (el.selectionStart !== undefined) { start = el.selectionStart; end = el.selectionEnd; } else { try { el.focus(); rangeEl = el.createTextRange(); clone = rangeEl.duplicate(); rangeEl.moveToBookmark(document.selection.createRange().getBookmark()); clone.setEndPoint('EndToStart', rangeEl); start = clone.text.length; end = start + rangeEl.text.length; } catch (e) {/* not focused or not visible */} } return { start: start, end: end }; } function setSelection(el, selection) { var rangeEl = void 0; try { if (el.selectionStart !== undefined) { el.focus(); el.setSelectionRange(selection.start, selection.end); } else { el.focus(); rangeEl = el.createTextRange(); rangeEl.collapse(true); rangeEl.moveStart('character', selection.start); rangeEl.moveEnd('character', selection.end - selection.start); rangeEl.select(); } } catch (e) {/* not focused or not visible */} } function supportArrowNavigation(mask) { return contract.isFunc(mask.arrowAction); } function asStr(anObj) { return JSON.stringify(anObj); } function eqSel(sel1, sel2) { if (sel1 === sel2) return true; if (sel1 === undefined || sel2 === undefined) return false; return sel1.start === sel2.start && sel1.end === sel2.end; } function selAt(sel, x) { if (!sel && x === 0) return true; return sel.start === x && (sel.end === undefined || sel.end === x); } var mapImg = { "DONE": [React.createElement('span', { className: 'glyphicon glyphicon-ok form-control-feedback' }), ""], "MORE": [React.createElement('span', { className: 'glyphicon glyphicon-arrow-right form-control-feedback' }), " has-warning"], "OK": [React.createElement('span', { className: 'glyphicon glyphicon-option-horizontal form-control-feedback' }), ""] }; //const LOG = (a, msg='') => { console.log(msg+": "+ a); return a; } var LOG = function LOG(x) { return x; }; var RxStatus = function (_Component) { _inherits(RxStatus, _Component); function RxStatus() { _classCallCheck(this, RxStatus); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } RxStatus.prototype.render = function render() { function printElem(_ref) { var code = _ref[0], typ = _ref[1]; if (typ === undefined) return code; return React.createElement( 'b', null, code ); } var t = this.props.mask.pattern.getInputTracker() || []; return React.createElement( 'div', null, t.map(printElem) ); }; return RxStatus; }(Component); function minV(minVal) { return function (val) { return Math.max(minVal, val); }; } export function hashStr(str) { var hashVal = 5381, i = str.length; while (i) { hashVal = hashVal * 33 ^ str.charCodeAt(--i); }return (hashVal >>> 0) + 12; } export var RxInputBase = function (_Component2) { _inherits(RxInputBase, _Component2); function RxInputBase(props) { _classCallCheck(this, RxInputBase); var _this2 = _possibleConstructorReturn(this, _Component2.call(this, props)); _this2.state = _this2.getInitialState(); _this2._onChange = _this2._onChange.bind(_this2); _this2._onKeyDown = _this2._onKeyDown.bind(_this2); _this2._onKeyPress = _this2._onKeyPress.bind(_this2); _this2._onPaste = _this2._onPaste.bind(_this2); _this2._onFocus = _this2._onFocus.bind(_this2); _this2._onBlur = _this2._onBlur.bind(_this2); _this2.input = null; return _this2; } // propTypes: { // mask: React.PropTypes.object.isRequired, // name: React.PropTypes.string.isRequired, // popover: React.PropTypes.string, // selection: React.PropTypes.object, // value: React.PropTypes.string, // }, // get input() { // return this.myRef.current; // } RxInputBase.prototype.getInitialState = function getInitialState() { var options = { pattern: this.props.mask || /.*/, value: this.props.value || '' }; return { focus: false, value: this.props.value || '', selection: this.props.selection, mask: new RXInputMask(options) }; }; RxInputBase.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { if (this.props.mask.toString() !== nextProps.mask.toString()) { //this.state.mask.setPattern(nextProps.mask, {value: this.state.mask.getRawValue()}); this.state.mask.setPattern(nextProps.mask, { value: nextProps.value, selection: this.state.mask.selection }); this.setState({ selection: this.state.selection, value: nextProps.value }); } else if (this.props.value !== nextProps.value) { this.state.mask.setValue(nextProps.value); } }; // static getDerivedStateFromProps(nextProps, state) { // //if (this.props.mask.toString() !== nextProps.mask.toString()) { // let value = nextProps.value||''; // console.log('getDerivedStateFromProps',{oldValue: state.value, value, oldmask:state.mask.pattern.toString(),mask: nextProps.mask.toString() }); // if (state.mask.pattern.toString() !== nextProps.mask.toString()) { // //this.state.mask.setPattern(nextProps.mask, {value: this.state.mask.getRawValue()}); // state.mask.setPattern(nextProps.mask, {value, selection: state.mask.selection}); // //this.setState({ selection: this.state.selection, value: nextProps.value}); // return {...state, value: nextProps.value}; // } // else if (state.value !== value) { // state.mask.setValue(nextProps.value); // console.log("value change"); // return {...state }; // } // return null; // } RxInputBase.prototype._updateMaskSelection = function _updateMaskSelection() { this.state.mask.selection = getSelection(this.input); }; RxInputBase.prototype._updateInputSelection = function _updateInputSelection() { if (!eqSel(getSelection(this.input), this.state.mask.selection)) setSelection(this.input, this.state.mask.selection); }; RxInputBase.prototype._onFocus = function _onFocus(e) { if (this.props.onFocus) this.props.onFocus(e); }; RxInputBase.prototype._onBlur = function _onBlur(e) { this.fireChange(e); }; RxInputBase.prototype.fireChange = function fireChange(e) { if (this.props.onChange) { var opt = { value: this.state.mask._getValue(), target: e.target, name: this.props.name, mask: this.state.mask }; //this.props.onChange(opt); this.props.onChange({ target: opt }); } }; RxInputBase.prototype._onChange = function _onChange(e) { // console.log('onChange', asStr(getSelection(this.input)), e.target.value) var mask = this.state.mask; var maskValue = mask.getValue(); if (e.target.value !== maskValue) { // Cut or delete operations will have shortened the value if (e.target.value.length < maskValue.length) { var sizeDiff = maskValue.length - e.target.value.length; this._updateMaskSelection(); mask.selection.end = mask.selection.start + sizeDiff; mask.backspace(); //console.log("Fix maskValue", maskValue, "diff:", sizeDiff, "target value: ", e.target.value); } var value = this._getDisplayValue(); e.target.value = value; if (value) { this._updateInputSelection(); } } this.setState({ selection: this.mask.selection }); this.fireChange(e); // console.log("on change", e) }; RxInputBase.prototype._onKeyDown = function _onKeyDown(e) { var _this3 = this; var mask = this.state.mask; var isKey = function isKey(keyV) { return function (e) { return e.key === keyV; }; }; var _C = function _C(test, action) { if (!test(e)) return false; e.preventDefault(); _this3._updateMaskSelection(); if (action()) { var oldVal = e.target.value; var value = _this3._getDisplayValue(); e.target.value = value; //console.log(action+":getDisplayValue", value); if (value) { _this3._updateInputSelection(); } if (_this3.props.onChange && oldVal != value) { //let opt = {target: {value: mask._getValue()}}; //this.props.onChange(opt); _this3.fireChange(e); //console.log("on change", e) } // console.log("on change1", e) } _this3.setState({ selection: mask.selection }); return true; }; //console.log('onKeyDown',mask, asStr(getSelection(this.input))+"/"+asStr(mask.selection), e.key, e.keyCode, e.target.value) if (_C(isUndo, function () { return mask.undo(); }) || _C(isRedo, function () { return mask.redo(); }) || _C(isKey("Backspace"), function () { return mask.backspace(); }) || _C(isKey("Delete"), function () { return mask.del(); })) return; if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey || e.key === 'Enter' || e.key === 'Tab') { return; } if (e.key === 'ArrowLeft' || e.key == 'ArrowRight') { // Check if mask supports arrow support var sel = getSelection(this.input); //mask.selection = sel; if (sel.start === sel.end && mask.left !== undefined) { e.preventDefault(); if (e.key === 'ArrowLeft') mask.left(sel);else mask.right(sel); this._updateInputSelection(); //this.refs.debug.props.forceUpdate(); } //console.log("Arrow Action support:", supportArrowNavigation(mask), " value:",this._getDisplayValue(), " selection: ", asStr(getSelection(this.input)), asStr(mask.selection)); } }; RxInputBase.prototype._onKeyPress = function _onKeyPress(e) { var mask = this.state.mask; //console.log('onKeyPress', asStr(getSelection(this.input)),asStr(mask.selection), e.key, e.target.value) // Ignore modified key presses // Ignore enter key to allow form submission if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return; } var selX = getSelection(this.input); var oldMaskX = mask.getSelection(); e.preventDefault(); this._updateMaskSelection(); if (insert(e.key)) { var oldVal = e.target.value; var value = mask.getValue(); e.target.value = value; //console.log("keyPress:getDisplayValue", this._getDisplayValue(), " selection: ", asStr(selX)+"/"+asStr(mask.selection)+"<"+asStr(oldMaskX)); this._updateInputSelection(); this.setState({ selection: mask.selection }); if (this.props.onChange && oldVal != value) { var opt = { target: { value: mask._getValue() } }; this.props.onChange(opt); } //console.log("on change", e) } function insert(ch) { if (mask.input(ch)) return true; if (ch !== ch.toUpperCase()) return mask.input(ch.toUpperCase());else if (ch != ch.toLowerCase()) return mask.input(ch.toLowerCase()); return false; } }; RxInputBase.prototype._onPaste = function _onPaste(e) { var mask = this.state.mask; //console.log('onPaste', asStr(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value) e.preventDefault(); this._updateMaskSelection(); // getData value needed for IE also works in FF & Chrome //console.log("paste: ", e.clipboardData.getData('Text')); if (mask.paste(e.clipboardData.getData('Text'))) { e.target.value = mask.getValue(); //console.log("undo:getDisplayValue", this._getDisplayValue()); // Timeout needed for IE setTimeout(this._updateInputSelection, 0); //this.props.onChange(e); this.setState({ selection: mask.selection }); } }; RxInputBase.prototype._getMaskList = function _getMaskList(flag) { var list = this.state.mask.minCharsList(!!flag); if (list && list.length < 20) return list; return this.state.mask.minCharsList(); }; RxInputBase.prototype._getDisplayValue = function _getDisplayValue() { var value = this.state.mask.getValue(); return value === this.state.mask.emptyValue ? '' : value; }; RxInputBase.prototype.selected = function selected(str, e) { //console.log("Selected: "+str); if (!str.split('').find(function (c) { return isMeta(c) ? c : undefined; })) { var mask = this.state.mask; mask.setValue(str); this.setState({ mask: mask }); //console.log("Selected(done): "+str); } }; RxInputBase.prototype.getMaxWidth = function getMaxWidth(valueList, maxWidth) { var dflt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 200; if (maxWidth) return Math.max(maxWidth, dflt); if (!valueList || !valueList.length) return dflt; var len = function len(s) { return s.replace(/\u0332/g, "").length; }; var lenList = valueList.map(len).map(minV(20)); return 12 * Math.max.apply(null, lenList); }; RxInputBase.prototype.getMapImg = function getMapImg() { return mapImg; }; RxInputBase.prototype.getRxPlaceHolder = function getRxPlaceHolder() { return rxPlaceHolder; }; RxInputBase.prototype.getInput = function getInput(input) { return input; }; RxInputBase.prototype.inputClassName = function inputClassName() { return "form-control"; }; RxInputBase.prototype.getPopoverData = function getPopoverData(valueList, headers, maxWidth, placeholder) { var _this4 = this; var MAXWIDTH = this.getMaxWidth(valueList, maxWidth, 300); if (!valueList || valueList.length <= 1) { if (!placeholder) return undefined; //{valueList: [""], headers, MAXWIDTH, hasSmallHeader: false} ; else valueList = [placeholder]; } var val = this._getDisplayValue() || ''; var ph = placeholder || this.state.mask.emptyValue; var popList = [val, ph].concat(valueList); var hasSmallHeader = popList.find(function (v) { return v.match(_this4.getRxPlaceHolder()); }); return { valueList: valueList, headers: headers, MAXWIDTH: MAXWIDTH, hasSmallHeader: hasSmallHeader }; }; RxInputBase.prototype.render = function render() { var _this5 = this; var _props = this.props, mask = _props.mask, size = _props.size, placeholder = _props.placeholder, popover = _props.popover, selection = _props.selection, showAll = _props.showAll, props = _objectWithoutProperties(_props, ['mask', 'size', 'placeholder', 'popover', 'selection', 'showAll']); var patternLength = this.state.mask.pattern.length; var setRef = function setRef(aDomElem) { _this5.input = aDomElem; }; var input = React.createElement('input', _extends({ style: { padding: "3px 0px 3px 0px" }, className: this.inputClassName() }, props, { ref: setRef, maxLength: patternLength, onChange: this._onChange, onKeyDown: this._onKeyDown, onKeyPress: this._onKeyPress, onPaste: this._onPaste, onFocus: this._onFocus, onBlur: this._onBlur, placeholder: placeholder || this.state.mask.emptyValue, size: size || patternLength, value: this._getDisplayValue() })); return this.getInput(input, placeholder); }; return RxInputBase; }(Component); //const LOG = (first, ...params) => {console.log(first, ...params); return first; } function strCmp1(a, b) { var nameA = a.toUpperCase(); // ignore upper and lowercase var nameB = b.toUpperCase(); // ignore upper and lowercase if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } // names must be equal return 0; } export var RxInput = function (_RxInputBase) { _inherits(RxInput, _RxInputBase); function RxInput() { _classCallCheck(this, RxInput); return _possibleConstructorReturn(this, _RxInputBase.apply(this, arguments)); } RxInput.prototype._createPopover = function _createPopover(props) { var _this7 = this; if (!props) return React.createElement('span', null); var MAXWIDTH = props.MAXWIDTH, hasSmallHeader = props.hasSmallHeader, valueList = props.valueList, headers = props.headers; var smallHeader = hasSmallHeader ? smallHeader = React.createElement( 'pre', { className: 'text-muted small-text' }, convertMask('? - optional, * - zero or more') ) : ""; var SPANSTYLE = { width: MAXWIDTH - 50, maxWidth: MAXWIDTH - 50 }; var TS = void 0, PADDING = void 0; console.log("valueList", valueList); if (valueList.length > 20) { TS = { height: "400px", display: "block", overflow: "auto" }; PADDING = React.createElement( 'div', null, ' \xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0' ); } var me = this; return React.createElement( Popover, { id: this.props.name + "myPopover", className: 'col-xs-10 col-md-10', style: { width: MAXWIDTH, maxWidth: MAXWIDTH, fontSize: "10px", marginTop: "10px", marginBottom: "10px" } }, smallHeader, React.createElement( 'table', { key: this.props.name + "myPopover1", className: 'table-responsive table-striped table-hover table-condensed col-xs-10 col-md-10', style: SPANSTYLE }, React.createElement( 'thead', null, React.createElement( 'tr', null, headers.map(function (e) { return React.createElement( 'th', { key: _this7.props.name + e }, e ); }) ) ), React.createElement( 'tbody', { style: TS }, valueList.sort(strCmp1).map(function (l) { return React.createElement( 'tr', { onClick: function onClick(e) { return me.selected(l, e); }, key: _this7.props.name + "L" + hashStr(l) }, React.createElement( 'td', { onClick: function onClick(e) { return me.selected(l, e); } }, l ) ); }) ) ), PADDING ); }; RxInput.prototype.getInput = function getInput(input, placeholder) { var OK = void 0; var warningStyle = { marginBotton: "0px", fontSize: "70%", color: 'red', fontStyle: 'italic' }; var mapImg = this.getMapImg(); //let status = <RxStatus mask={this.state.mask}; var status = ""; var popOverData = this.getPopoverData(this._getMaskList(this.props.showAll !== 'no'), ['Possible Values'], undefined, placeholder); var myPopover = this.props.popover ? this._createPopover(popOverData) : React.createElement('span', null); var ok = this.state.mask.isDone(); if (ok) OK = mapImg[this.state.mask.isDone()]; //<span className="input-group-addon">.00</span>; // return React.createElement( 'div', { style: { marginBotton: "0px", paddingLeft: "100px" } }, React.createElement( 'div', { style: warningStyle }, ok, ' \xA0' ), status, React.createElement( 'div', { className: "form-group has-feedback" + OK[1] }, React.createElement( OverlayTrigger, { trigger: 'focus', style: { marginBotton: "0px" }, placement: 'bottom', overlay: myPopover }, input ), OK[0] ) ); }; return RxInput; }(RxInputBase);