From ea94d4bf0caa8173ed5d95682aee17bb2a6e58d2 Mon Sep 17 00:00:00 2001 From: Brandon Pugh Date: Fri, 9 Oct 2015 22:51:15 -0500 Subject: [PATCH 01/29] Allow form submission by hitting enter Fixes #4 --- src/index.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.jsx b/src/index.jsx index 5132bac..ab030ea 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -119,7 +119,8 @@ var MaskedInput = React.createClass({ // console.log('onKeyPress', JSON.stringify(getSelection(this.getDOMNode())), e.key, e.target.value) // Ignore modified key presses - if (e.metaKey || e.altKey || e.ctrlKey) { return } + // Ignore enter key to allow form submission + if (e.metaKey || e.altKey || e.ctrlKey || e.key == 'Enter') { return } e.preventDefault() this._updateMaskSelection() From 649cf142888cb2c15706c3b38787ba1d04f15e0b Mon Sep 17 00:00:00 2001 From: Jonny Buchanan Date: Fri, 22 Jan 2016 18:27:02 +0000 Subject: [PATCH 02/29] Add project status badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dec5b9c..e0d1898 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A [React](http://facebook.github.io/react/) component for `` masking, built on top of [inputmask-core](https://github.com/insin/inputmask-core). +![This project has never been used by its author, other than while making it.](https://img.shields.io/badge/author--usage-never-red.png "This project has never been used by its author, other than while making it") + ## [Live Demo](http://insin.github.io/react-maskedinput/) ## Install From bcc75d9ff15e2306c6669809c87f8e7634acdd29 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Thu, 11 Feb 2016 10:25:41 -0500 Subject: [PATCH 03/29] Allow for updating MaskedInput value --- src/index.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.jsx b/src/index.jsx index 35ef9b5..7cceab2 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -43,6 +43,9 @@ var MaskedInput = React.createClass({ }, componentWillReceiveProps(nextProps) { + if (this.props.value !== nextProps.value) { + this.mask.setValue(nextProps.value) + } if (this.props.mask !== nextProps.mask) { this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()}) } From 5d62fed95b9f2d29aefe037805bf64fdefe617d5 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Thu, 11 Feb 2016 10:26:07 -0500 Subject: [PATCH 04/29] Demo: update for external state setting --- demo/index.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/demo/index.html b/demo/index.html index 0a9d2e8..e1c166d 100644 --- a/demo/index.html +++ b/demo/index.html @@ -92,7 +92,12 @@

<MaskedInputA React component which creates a masked <input/>

- + +
+

You can even externally update the card state like a standard input element:

+
+ +

Placeholders are automatically generated but can be overridden with your own:

From 53b566cf7ff1d9e567d8172ec7efa493e6e10e6c Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Thu, 11 Feb 2016 10:38:04 -0500 Subject: [PATCH 05/29] v3.1.0: version bump, build, changelog --- CHANGES.md | 4 + dist/react-maskedinput.js | 430 +++++++++++++++++----------------- dist/react-maskedinput.min.js | 4 +- package.json | 2 +- 4 files changed, 225 insertions(+), 215 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0b067f7..80d52b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 3.1.0 / 2015-10-23 + +* Added support for `value` behaving as a controlled component. + ## 3.0.0 / 2015-10-23 **Breaking change:** Now uses a `mask` prop to define the input mask instead of `pattern`, to avoid preventing use of the the HTML5 `pattern` attribute in conjunction with the input mask. diff --git a/dist/react-maskedinput.js b/dist/react-maskedinput.js index 980b89d..a7ad6bf 100644 --- a/dist/react-maskedinput.js +++ b/dist/react-maskedinput.js @@ -1,5 +1,5 @@ /*! - * react-maskedinput 3.0.0 - https://github.com/insin/react-maskedinput + * react-maskedinput 3.1.0 (dev build at Thu, 11 Feb 2016 15:36:55 GMT) - https://github.com/insin/react-maskedinput * MIT Licensed */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MaskedInput = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oi;i++)t[n[i]]=e[n[i]];return t}function s(t){return i({},t)}function r(t){var e=s(p);if(t)for(var n=Object.keys(t),i=0,r=n.length;r>i;i++){var a=n[i];null==t[a]?delete e[a]:e[a]=t[a]}return e}function a(t,e,n){return this instanceof a?(this.placeholderChar=n||d,this.formatCharacters=e||p,this.source=t,this.pattern=[],this.length=0,this.firstEditableIndex=null,this.lastEditableIndex=null,this._editableIndices={},void this._parse()):new a(t,e,n)}function o(t){if(!(this instanceof o))return new o(t);if(t=i({formatCharacters:null,pattern:null,placeholderChar:d,selection:{start:0,end:0},value:""},t),null==t.pattern)throw new Error("InputMask: you must provide a pattern.");if(1!==t.placeholderChar.length)throw new Error("InputMask: placeholderChar should be a single character.");this.placeholderChar=t.placeholderChar,this.formatCharacters=r(t.formatCharacters),this.setPattern(t.pattern,{value:t.value,selection:t.selection})}var l="\\",h=/^\d$/,c=/^[A-Za-z]$/,u=/^[\dA-Za-z]$/,d="_",p={"*":{validate:function(t){return u.test(t)}},1:{validate:function(t){return h.test(t)}},a:{validate:function(t){return c.test(t)}},A:{validate:function(t){return c.test(t)},transform:function(t){return t.toUpperCase()}},"#":{validate:function(t){return u.test(t)},transform:function(t){return t.toUpperCase()}}};a.prototype._parse=function(){for(var t=this.source.split(""),e=0,n=[],i=0,s=t.length;s>i;i++){var r=t[i];if(r===l){if(i===s-1)throw new Error("InputMask: pattern ends with a raw "+l);r=t[++i]}else r in this.formatCharacters&&(null===this.firstEditableIndex&&(this.firstEditableIndex=e),this.lastEditableIndex=e,this._editableIndices[e]=!0);n.push(r),e++}if(null===this.firstEditableIndex)throw new Error('InputMask: pattern "'+this.source+'" does not contain any editable characters.');this.pattern=n,this.length=n.length},a.prototype.formatValue=function(t){for(var e=new Array(this.length),n=0,i=0,s=this.length;s>i;i++)this.isEditableIndex(i)?(e[i]=t.length>n&&this.isValidAtIndex(t[n],i)?this.transform(t[n],i):this.placeholderChar,n++):(e[i]=this.pattern[i],t.length>n&&t[n]===this.pattern[i]&&n++);return e},a.prototype.isEditableIndex=function(t){return!!this._editableIndices[t]},a.prototype.isValidAtIndex=function(t,e){return this.formatCharacters[this.pattern[e]].validate(t)},a.prototype.transform=function(t,e){var n=this.formatCharacters[this.pattern[e]];return"function"==typeof n.transform?n.transform(t):t},o.prototype.input=function(t){if(this.selection.start===this.selection.end&&this.selection.start===this.pattern.length)return!1;var e=s(this.selection),n=this.getValue(),i=this.selection.start;if(ii;)this.pattern.isEditableIndex(r)&&(this.value[r]=this.placeholderChar),r--;for(this.selection.start=this.selection.end=i+1;this.pattern.length>this.selection.start&&!this.pattern.isEditableIndex(this.selection.start);)this.selection.start++,this.selection.end++;return null!=this._historyIndex&&(console.log("splice(",this._historyIndex,this._history.length-this._historyIndex,")"),this._history.splice(this._historyIndex,this._history.length-this._historyIndex),this._historyIndex=null),("input"!==this._lastOp||e.start!==e.end||null!==this._lastSelection&&e.start!==this._lastSelection.start)&&this._history.push({value:n,selection:e,lastOp:this._lastOp}),this._lastOp="input",this._lastSelection=s(this.selection),!0},o.prototype.backspace=function(){if(0===this.selection.start&&0===this.selection.end)return!1;var t=s(this.selection),e=this.getValue();if(this.selection.start===this.selection.end)this.pattern.isEditableIndex(this.selection.start-1)&&(this.value[this.selection.start-1]=this.placeholderChar),this.selection.start--,this.selection.end--;else{for(var n=this.selection.end-1;n>=this.selection.start;)this.pattern.isEditableIndex(n)&&(this.value[n]=this.placeholderChar),n--;this.selection.end=this.selection.start}return null!=this._historyIndex&&this._history.splice(this._historyIndex,this._history.length-this._historyIndex),("backspace"!==this._lastOp||t.start!==t.end||null!==this._lastSelection&&t.start!==this._lastSelection.start)&&this._history.push({value:e,selection:t,lastOp:this._lastOp}),this._lastOp="backspace",this._lastSelection=s(this.selection),!0},o.prototype.paste=function(t){var e={value:this.value.slice(),selection:s(this.selection),_lastOp:this._lastOp,_history:this._history.slice(),_historyIndex:this._historyIndex,_lastSelection:s(this._lastSelection)};if(this.selection.startn;n++)if(t.charAt(n)!==this.pattern.pattern[n])return!1;t=t.substring(this.pattern.firstEditableIndex-this.selection.start),this.selection.start=this.pattern.firstEditableIndex}for(n=0,r=t.length;r>n&&this.selection.start<=this.pattern.lastEditableIndex;n++){var a=this.input(t.charAt(n));if(!a){if(this.selection.start>0){var o=this.selection.start-1;if(!this.pattern.isEditableIndex(o)&&t.charAt(n)===this.pattern.pattern[o])continue}return i(this,e),!1}}return!0},o.prototype.undo=function(){if(0===this._history.length||0===this._historyIndex)return!1;var t;if(null==this._historyIndex){this._historyIndex=this._history.length-1,t=this._history[this._historyIndex];var e=this.getValue();(t.value!==e||t.selection.start!==this.selection.start||t.selection.end!==this.selection.end)&&this._history.push({value:e,selection:s(this.selection),lastOp:this._lastOp,startUndo:!0})}else t=this._history[--this._historyIndex];return this.value=t.value.split(""),this.selection=t.selection,this._lastOp=t.lastOp,!0},o.prototype.redo=function(){if(0===this._history.length||null==this._historyIndex)return!1;var t=this._history[++this._historyIndex];return this._historyIndex===this._history.length-1&&(this._historyIndex=null,t.startUndo&&this._history.pop()),this.value=t.value.split(""),this.selection=t.selection,this._lastOp=t.lastOp,!0},o.prototype.setPattern=function(t,e){e=i({selection:{start:0,end:0},value:""},e),this.pattern=new a(t,this.formatCharacters,this.placeholderChar),this.setValue(e.value),this.emptyValue=this.pattern.formatValue([]).join(""),this.selection=e.selection,this._resetHistory()},o.prototype.setSelection=function(t){if(this.selection=s(t),this.selection.start===this.selection.end){if(this.selection.startthis.pattern.lastEditableIndex+1)return this.selection.start=this.selection.end=this.pattern.lastEditableIndex+1,!0}return!1},o.prototype.setValue=function(t){null==t&&(t=""),this.value=this.pattern.formatValue(t.split(""))},o.prototype.getValue=function(){return this.value.join("")},o.prototype.getRawValue=function(){for(var t=[],e=0;ee.end?(n=e.end,i=e.start):(n=e.start,i=e.end),s.moveToElementText(t),s.moveStart("character",n),s.setEndPoint("EndToStart",s),s.moveEnd("character",i-n),s.select()}function o(t,e){if(window.getSelection){var n=window.getSelection(),i=t[c()].length,s=Math.min(e.start,i),r="undefined"==typeof e.end?s:Math.min(e.end,i);if(!n.extend&&s>r){var a=r;r=s,s=a}var o=h(t,s),l=h(t,r);if(o&&l){var u=document.createRange();u.setStart(o.node,o.offset),n.removeAllRanges(),s>r?(n.addRange(u),n.extend(l.node,l.offset)):(u.setEnd(l.node,l.offset),n.addRange(u))}}}var l=t("fbjs/lib/ExecutionEnvironment"),h=t("./getNodeForCharacterOffset"),c=t("./getTextContentAccessor"),u=l.canUseDOM&&"selection"in document&&!("getSelection"in window),d={getOffsets:u?s:r,setOffsets:u?a:o};e.exports=d},{"./getNodeForCharacterOffset":5,"./getTextContentAccessor":6,"fbjs/lib/ExecutionEnvironment":7}],4:[function(t,e,n){"use strict";function i(t){return r(document.documentElement,t)}var s=t("./ReactDOMSelection"),r=t("fbjs/lib/containsNode"),a=t("fbjs/lib/focusNode"),o=t("fbjs/lib/getActiveElement"),l={hasSelectionCapabilities:function(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&("input"===e&&"text"===t.type||"textarea"===e||"true"===t.contentEditable)},getSelectionInformation:function(){var t=o();return{focusedElem:t,selectionRange:l.hasSelectionCapabilities(t)?l.getSelection(t):null}},restoreSelection:function(t){var e=o(),n=t.focusedElem,s=t.selectionRange;e!==n&&i(n)&&(l.hasSelectionCapabilities(n)&&l.setSelection(n,s),a(n))},getSelection:function(t){var e;if("selectionStart"in t)e={start:t.selectionStart,end:t.selectionEnd};else if(document.selection&&t.nodeName&&"input"===t.nodeName.toLowerCase()){var n=document.selection.createRange();n.parentElement()===t&&(e={start:-n.moveStart("character",-t.value.length),end:-n.moveEnd("character",-t.value.length)})}else e=s.getOffsets(t);return e||{start:0,end:0}},setSelection:function(t,e){var n=e.start,i=e.end;if("undefined"==typeof i&&(i=n),"selectionStart"in t)t.selectionStart=n,t.selectionEnd=Math.min(i,t.value.length);else if(document.selection&&t.nodeName&&"input"===t.nodeName.toLowerCase()){var r=t.createTextRange();r.collapse(!0),r.moveStart("character",n),r.moveEnd("character",i-n),r.select()}else s.setOffsets(t,e)}};e.exports=l},{"./ReactDOMSelection":3,"fbjs/lib/containsNode":8,"fbjs/lib/focusNode":9,"fbjs/lib/getActiveElement":10}],5:[function(t,e,n){"use strict";function i(t){for(;t&&t.firstChild;)t=t.firstChild;return t}function s(t){for(;t;){if(t.nextSibling)return t.nextSibling;t=t.parentNode}}function r(t,e){for(var n=i(t),r=0,a=0;n;){if(3===n.nodeType){if(a=r+n.textContent.length,e>=r&&a>=e)return{node:n,offset:e-r};r=a}n=i(s(n))}}e.exports=r},{}],6:[function(t,e,n){"use strict";function i(){return!r&&s.canUseDOM&&(r="textContent"in document.documentElement?"textContent":"innerText"),r}var s=t("fbjs/lib/ExecutionEnvironment"),r=null;e.exports=i},{"fbjs/lib/ExecutionEnvironment":7}],7:[function(t,e,n){"use strict";var i=!("undefined"==typeof window||!window.document||!window.document.createElement),s={canUseDOM:i,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:i&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:i&&!!window.screen,isInWorker:!i};e.exports=s},{}],8:[function(t,e,n){"use strict";function i(t,e){var n=!0;t:for(;n;){var i=t,r=e;if(n=!1,i&&r){if(i===r)return!0;if(s(i))return!1;if(s(r)){t=i,e=r.parentNode,n=!0;continue t}return i.contains?i.contains(r):i.compareDocumentPosition?!!(16&i.compareDocumentPosition(r)):!1}return!1}}var s=t("./isTextNode");e.exports=i},{"./isTextNode":12}],9:[function(t,e,n){"use strict";function i(t){try{t.focus()}catch(e){}}e.exports=i},{}],10:[function(t,e,n){"use strict";function i(){if("undefined"==typeof document)return null;try{return document.activeElement||document.body}catch(t){return document.body}}e.exports=i},{}],11:[function(t,e,n){"use strict";function i(t){return!(!t||!("function"==typeof Node?t instanceof Node:"object"==typeof t&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName))}e.exports=i},{}],12:[function(t,e,n){"use strict";function i(t){return s(t)&&3==t.nodeType}var s=t("./isNode");e.exports=i},{"./isNode":11}]},{},[1])(1)}); \ No newline at end of file +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.MaskedInput=t()}}(function(){return function t(e,n,i){function s(a,o){if(!n[a]){if(!e[a]){var l="function"==typeof require&&require;if(!o&&l)return l(a,!0);if(r)return r(a,!0);var h=new Error("Cannot find module '"+a+"'");throw h.code="MODULE_NOT_FOUND",h}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return s(n?n:t)},c,c.exports,t,e,n,i)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;ai;i++)t[n[i]]=e[n[i]];return t}function s(t){return i({},t)}function r(t){var e=s(p);if(t)for(var n=Object.keys(t),i=0,r=n.length;r>i;i++){var a=n[i];null==t[a]?delete e[a]:e[a]=t[a]}return e}function a(t,e,n){return this instanceof a?(this.placeholderChar=n||d,this.formatCharacters=e||p,this.source=t,this.pattern=[],this.length=0,this.firstEditableIndex=null,this.lastEditableIndex=null,this._editableIndices={},void this._parse()):new a(t,e,n)}function o(t){if(!(this instanceof o))return new o(t);if(t=i({formatCharacters:null,pattern:null,placeholderChar:d,selection:{start:0,end:0},value:""},t),null==t.pattern)throw new Error("InputMask: you must provide a pattern.");if(1!==t.placeholderChar.length)throw new Error("InputMask: placeholderChar should be a single character.");this.placeholderChar=t.placeholderChar,this.formatCharacters=r(t.formatCharacters),this.setPattern(t.pattern,{value:t.value,selection:t.selection})}var l="\\",h=/^\d$/,c=/^[A-Za-z]$/,u=/^[\dA-Za-z]$/,d="_",p={"*":{validate:function(t){return u.test(t)}},1:{validate:function(t){return h.test(t)}},a:{validate:function(t){return c.test(t)}},A:{validate:function(t){return c.test(t)},transform:function(t){return t.toUpperCase()}},"#":{validate:function(t){return u.test(t)},transform:function(t){return t.toUpperCase()}}};a.prototype._parse=function(){for(var t=this.source.split(""),e=0,n=[],i=0,s=t.length;s>i;i++){var r=t[i];if(r===l){if(i===s-1)throw new Error("InputMask: pattern ends with a raw "+l);r=t[++i]}else r in this.formatCharacters&&(null===this.firstEditableIndex&&(this.firstEditableIndex=e),this.lastEditableIndex=e,this._editableIndices[e]=!0);n.push(r),e++}if(null===this.firstEditableIndex)throw new Error('InputMask: pattern "'+this.source+'" does not contain any editable characters.');this.pattern=n,this.length=n.length},a.prototype.formatValue=function(t){for(var e=new Array(this.length),n=0,i=0,s=this.length;s>i;i++)this.isEditableIndex(i)?(e[i]=t.length>n&&this.isValidAtIndex(t[n],i)?this.transform(t[n],i):this.placeholderChar,n++):(e[i]=this.pattern[i],t.length>n&&t[n]===this.pattern[i]&&n++);return e},a.prototype.isEditableIndex=function(t){return!!this._editableIndices[t]},a.prototype.isValidAtIndex=function(t,e){return this.formatCharacters[this.pattern[e]].validate(t)},a.prototype.transform=function(t,e){var n=this.formatCharacters[this.pattern[e]];return"function"==typeof n.transform?n.transform(t):t},o.prototype.input=function(t){if(this.selection.start===this.selection.end&&this.selection.start===this.pattern.length)return!1;var e=s(this.selection),n=this.getValue(),i=this.selection.start;if(ii;)this.pattern.isEditableIndex(r)&&(this.value[r]=this.placeholderChar),r--;for(this.selection.start=this.selection.end=i+1;this.pattern.length>this.selection.start&&!this.pattern.isEditableIndex(this.selection.start);)this.selection.start++,this.selection.end++;return null!=this._historyIndex&&(console.log("splice(",this._historyIndex,this._history.length-this._historyIndex,")"),this._history.splice(this._historyIndex,this._history.length-this._historyIndex),this._historyIndex=null),("input"!==this._lastOp||e.start!==e.end||null!==this._lastSelection&&e.start!==this._lastSelection.start)&&this._history.push({value:n,selection:e,lastOp:this._lastOp}),this._lastOp="input",this._lastSelection=s(this.selection),!0},o.prototype.backspace=function(){if(0===this.selection.start&&0===this.selection.end)return!1;var t=s(this.selection),e=this.getValue();if(this.selection.start===this.selection.end)this.pattern.isEditableIndex(this.selection.start-1)&&(this.value[this.selection.start-1]=this.placeholderChar),this.selection.start--,this.selection.end--;else{for(var n=this.selection.end-1;n>=this.selection.start;)this.pattern.isEditableIndex(n)&&(this.value[n]=this.placeholderChar),n--;this.selection.end=this.selection.start}return null!=this._historyIndex&&this._history.splice(this._historyIndex,this._history.length-this._historyIndex),("backspace"!==this._lastOp||t.start!==t.end||null!==this._lastSelection&&t.start!==this._lastSelection.start)&&this._history.push({value:e,selection:t,lastOp:this._lastOp}),this._lastOp="backspace",this._lastSelection=s(this.selection),!0},o.prototype.paste=function(t){var e={value:this.value.slice(),selection:s(this.selection),_lastOp:this._lastOp,_history:this._history.slice(),_historyIndex:this._historyIndex,_lastSelection:s(this._lastSelection)};if(this.selection.startn;n++)if(t.charAt(n)!==this.pattern.pattern[n])return!1;t=t.substring(this.pattern.firstEditableIndex-this.selection.start),this.selection.start=this.pattern.firstEditableIndex}for(n=0,r=t.length;r>n&&this.selection.start<=this.pattern.lastEditableIndex;n++){var a=this.input(t.charAt(n));if(!a){if(this.selection.start>0){var o=this.selection.start-1;if(!this.pattern.isEditableIndex(o)&&t.charAt(n)===this.pattern.pattern[o])continue}return i(this,e),!1}}return!0},o.prototype.undo=function(){if(0===this._history.length||0===this._historyIndex)return!1;var t;if(null==this._historyIndex){this._historyIndex=this._history.length-1,t=this._history[this._historyIndex];var e=this.getValue();(t.value!==e||t.selection.start!==this.selection.start||t.selection.end!==this.selection.end)&&this._history.push({value:e,selection:s(this.selection),lastOp:this._lastOp,startUndo:!0})}else t=this._history[--this._historyIndex];return this.value=t.value.split(""),this.selection=t.selection,this._lastOp=t.lastOp,!0},o.prototype.redo=function(){if(0===this._history.length||null==this._historyIndex)return!1;var t=this._history[++this._historyIndex];return this._historyIndex===this._history.length-1&&(this._historyIndex=null,t.startUndo&&this._history.pop()),this.value=t.value.split(""),this.selection=t.selection,this._lastOp=t.lastOp,!0},o.prototype.setPattern=function(t,e){e=i({selection:{start:0,end:0},value:""},e),this.pattern=new a(t,this.formatCharacters,this.placeholderChar),this.setValue(e.value),this.emptyValue=this.pattern.formatValue([]).join(""),this.selection=e.selection,this._resetHistory()},o.prototype.setSelection=function(t){if(this.selection=s(t),this.selection.start===this.selection.end){if(this.selection.startthis.pattern.lastEditableIndex+1)return this.selection.start=this.selection.end=this.pattern.lastEditableIndex+1,!0}return!1},o.prototype.setValue=function(t){null==t&&(t=""),this.value=this.pattern.formatValue(t.split(""))},o.prototype.getValue=function(){return this.value.join("")},o.prototype.getRawValue=function(){for(var t=[],e=0;ee.end?(n=e.end,i=e.start):(n=e.start,i=e.end),s.moveToElementText(t),s.moveStart("character",n),s.setEndPoint("EndToStart",s),s.moveEnd("character",i-n),s.select()}function o(t,e){if(window.getSelection){var n=window.getSelection(),i=t[c()].length,s=Math.min(e.start,i),r="undefined"==typeof e.end?s:Math.min(e.end,i);if(!n.extend&&s>r){var a=r;r=s,s=a}var o=h(t,s),l=h(t,r);if(o&&l){var u=document.createRange();u.setStart(o.node,o.offset),n.removeAllRanges(),s>r?(n.addRange(u),n.extend(l.node,l.offset)):(u.setEnd(l.node,l.offset),n.addRange(u))}}}var l=t("fbjs/lib/ExecutionEnvironment"),h=t("./getNodeForCharacterOffset"),c=t("./getTextContentAccessor"),u=l.canUseDOM&&"selection"in document&&!("getSelection"in window),d={getOffsets:u?s:r,setOffsets:u?a:o};e.exports=d},{"./getNodeForCharacterOffset":11,"./getTextContentAccessor":12,"fbjs/lib/ExecutionEnvironment":2}],10:[function(t,e,n){"use strict";function i(t){return r(document.documentElement,t)}var s=t("./ReactDOMSelection"),r=t("fbjs/lib/containsNode"),a=t("fbjs/lib/focusNode"),o=t("fbjs/lib/getActiveElement"),l={hasSelectionCapabilities:function(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&("input"===e&&"text"===t.type||"textarea"===e||"true"===t.contentEditable)},getSelectionInformation:function(){var t=o();return{focusedElem:t,selectionRange:l.hasSelectionCapabilities(t)?l.getSelection(t):null}},restoreSelection:function(t){var e=o(),n=t.focusedElem,s=t.selectionRange;e!==n&&i(n)&&(l.hasSelectionCapabilities(n)&&l.setSelection(n,s),a(n))},getSelection:function(t){var e;if("selectionStart"in t)e={start:t.selectionStart,end:t.selectionEnd};else if(document.selection&&t.nodeName&&"input"===t.nodeName.toLowerCase()){var n=document.selection.createRange();n.parentElement()===t&&(e={start:-n.moveStart("character",-t.value.length),end:-n.moveEnd("character",-t.value.length)})}else e=s.getOffsets(t);return e||{start:0,end:0}},setSelection:function(t,e){var n=e.start,i=e.end;if("undefined"==typeof i&&(i=n),"selectionStart"in t)t.selectionStart=n,t.selectionEnd=Math.min(i,t.value.length);else if(document.selection&&t.nodeName&&"input"===t.nodeName.toLowerCase()){var r=t.createTextRange();r.collapse(!0),r.moveStart("character",n),r.moveEnd("character",i-n),r.select()}else s.setOffsets(t,e)}};e.exports=l},{"./ReactDOMSelection":9,"fbjs/lib/containsNode":3,"fbjs/lib/focusNode":4,"fbjs/lib/getActiveElement":5}],11:[function(t,e,n){"use strict";function i(t){for(;t&&t.firstChild;)t=t.firstChild;return t}function s(t){for(;t;){if(t.nextSibling)return t.nextSibling;t=t.parentNode}}function r(t,e){for(var n=i(t),r=0,a=0;n;){if(3===n.nodeType){if(a=r+n.textContent.length,e>=r&&a>=e)return{node:n,offset:e-r};r=a}n=i(s(n))}}e.exports=r},{}],12:[function(t,e,n){"use strict";function i(){return!r&&s.canUseDOM&&(r="textContent"in document.documentElement?"textContent":"innerText"),r}var s=t("fbjs/lib/ExecutionEnvironment"),r=null;e.exports=i},{"fbjs/lib/ExecutionEnvironment":2}]},{},[1])(1)}); \ No newline at end of file diff --git a/package.json b/package.json index c3b4699..0483f02 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-maskedinput", "description": "Masked React component", - "version": "3.0.0", + "version": "3.1.0", "main": "./lib/index.js", "standalone": "MaskedInput", "homepage": "https://github.com/insin/react-maskedinput", From e111d7185bd0d2f8537ba92bb27e52468e349890 Mon Sep 17 00:00:00 2001 From: Brandon Pugh Date: Sat, 13 Feb 2016 20:17:31 -0600 Subject: [PATCH 06/29] Migrate build process to nwb - Removes .npmignore in favor of whitelisting with "files" in package.json - Removes dist folder and instead builds to umd folder that is only published to npm and hosted on npmcdn instead of checking into git - Remove 'use strict' statement since babel inserts it automactically --- .gitignore | 9 +- .npmignore | 7 - demo/index.html | 2 +- dist/react-maskedinput.js | 1337 --------------------------------- dist/react-maskedinput.min.js | 5 - nwb.config.js | 21 + package.json | 42 +- src/{index.jsx => index.js} | 2 - 8 files changed, 45 insertions(+), 1380 deletions(-) delete mode 100644 .npmignore delete mode 100644 dist/react-maskedinput.js delete mode 100644 dist/react-maskedinput.min.js create mode 100644 nwb.config.js rename src/{index.jsx => index.js} (99%) diff --git a/.gitignore b/.gitignore index 46f1072..4994ab3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ -lib/ -node_modules/ \ No newline at end of file +/coverage +/demo/dist +/es6 +/lib +/node_modules +/umd +npm-debug.log diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 564d394..0000000 --- a/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -demo -src -test -.gitignore -.jshintrc -.travis.yml -gulpfile.js diff --git a/demo/index.html b/demo/index.html index e1c166d..e8b0e1f 100644 --- a/demo/index.html +++ b/demo/index.html @@ -5,7 +5,7 @@ - + - - -
- - +render(, document.getElementById('demo')) diff --git a/demo/src/style.css b/demo/src/style.css new file mode 100644 index 0000000..0c54f0c --- /dev/null +++ b/demo/src/style.css @@ -0,0 +1,41 @@ +body { + box-sizing: border-box; + width: 550px; + margin: 1em auto; + padding: 0 1em; + font-family: sans-serif; +} +code { + font-size: 1.3em; +} +h1 { + font-size: 3em; + text-align: center; + margin-top: 0; +} +p.lead { + font-weight: bold; + text-align: center; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #222; +} +.form-field { + margin-bottom: .5em; +} +label { + display: inline-block; + width: 7em; + text-align: right; + margin-right: .75em; +} +input { + border: none; + font-size: 1.5em; +} +footer { + text-align: center; +} From 898a14a1ca4d25d69e16948d0e7d6d18c555902a Mon Sep 17 00:00:00 2001 From: Michael Henderson Date: Fri, 8 Apr 2016 12:49:41 -0400 Subject: [PATCH 14/29] Updating React and ReactDOM dependencies to v15; allowing minor and patch updates in peerDependencies --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 14a214a..072f8e0 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,13 @@ "inputmask-core": "^2.1.1" }, "peerDependencies": { - "react": "0.14.x" + "react": "0.14.x || 15.x.x" }, "devDependencies": { "eslint-config-jonnybuchanan": "2.0.3", "nwb": "0.8.x", - "react": "0.14.x", - "react-dom": "0.14.x" + "react": "15.x.x", + "react-dom": "15.x.x" }, "repository": { "type": "git", From 36eb9481c111512a3002dd302811459e6161cba6 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Mon, 11 Apr 2016 10:47:04 -0400 Subject: [PATCH 15/29] v3.1.1 changelist --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7b69d30..3df2db5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 3.1.2 / 2016-04-11 + +* Support for React 15.x.x + ## 3.1.1 / 2016-03-09 * Convert tooling to use [nwb](https://github.com/insin/nwb/) [[bpugh]][[bpugh]] From cd97d3e81c0f1c89020835c31b56f92f1cc0e0f1 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Mon, 11 Apr 2016 10:47:10 -0400 Subject: [PATCH 16/29] 3.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 072f8e0..d416487 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-maskedinput", "description": "Masked React component", - "version": "3.1.1", + "version": "3.1.2", "main": "./lib/index.js", "jsnext:main": "es6/index.js", "standalone": "MaskedInput", From 47f45f611d5821cde830859c706e50042e6cc4d0 Mon Sep 17 00:00:00 2001 From: Brandon Pugh Date: Fri, 29 Apr 2016 21:57:00 -0500 Subject: [PATCH 17/29] Update nwb dependency to 0.9.x Updates a number of transitive dependency versions as a result of all the left-pad hubbub --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d416487..789696f 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "eslint-config-jonnybuchanan": "2.0.3", - "nwb": "0.8.x", + "nwb": "0.9.x", "react": "15.x.x", "react-dom": "15.x.x" }, From d9ac7a0c20a8c8aa8ed749082968d477bccb0281 Mon Sep 17 00:00:00 2001 From: Arseny Sysolyatin Date: Mon, 2 May 2016 18:31:51 +0400 Subject: [PATCH 18/29] Call this.props.onChange only if it's defined (#39) Call this.props.onChange only if it's defined --- src/index.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index e1b70dd..747c39e 100644 --- a/src/index.js +++ b/src/index.js @@ -88,7 +88,9 @@ var MaskedInput = React.createClass({ if (this.mask.undo()) { e.target.value = this._getDisplayValue() this._updateInputSelection() - this.props.onChange(e) + if (this.props.onChange) { + this.props.onChange(e) + } } return } @@ -97,7 +99,9 @@ var MaskedInput = React.createClass({ if (this.mask.redo()) { e.target.value = this._getDisplayValue() this._updateInputSelection() - this.props.onChange(e) + if (this.props.onChange) { + this.props.onChange(e) + } } return } @@ -111,7 +115,9 @@ var MaskedInput = React.createClass({ if (value) { this._updateInputSelection() } - this.props.onChange(e) + if (this.props.onChange) { + this.props.onChange(e) + } } } }, @@ -128,7 +134,9 @@ var MaskedInput = React.createClass({ if (this.mask.input(e.key)) { e.target.value = this.mask.getValue() this._updateInputSelection() - this.props.onChange(e) + if (this.props.onChange) { + this.props.onChange(e) + } } }, @@ -142,7 +150,9 @@ var MaskedInput = React.createClass({ e.target.value = this.mask.getValue() // Timeout needed for IE setTimeout(this._updateInputSelection, 0) - this.props.onChange(e) + if (this.props.onChange) { + this.props.onChange(e) + } } }, From 1c698bc0265a1f0e1b5188e0701a817b97c07e58 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Mon, 2 May 2016 10:38:23 -0400 Subject: [PATCH 19/29] v3.1.3 --- CHANGES.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3df2db5..c9c1b42 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## 3.1.3 / 2016-05-02 + +* Don’t call `onChange` function if undefined. +* Update nwb to 0.9.x + ## 3.1.2 / 2016-04-11 * Support for React 15.x.x diff --git a/package.json b/package.json index 789696f..a9ecc3f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-maskedinput", "description": "Masked React component", - "version": "3.1.2", + "version": "3.1.3", "main": "./lib/index.js", "jsnext:main": "es6/index.js", "standalone": "MaskedInput", From 61e395bf4388373e14a79412d2597652b6b6dd7d Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Mon, 2 May 2016 13:15:43 -0400 Subject: [PATCH 20/29] Init tests (#51) * basic tests * travis --- .travis.yml | 5 +++++ tests/index-test.js | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .travis.yml create mode 100644 tests/index-test.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5c036ab --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "4" + - "5" +script: npm test diff --git a/tests/index-test.js b/tests/index-test.js new file mode 100644 index 0000000..e48f6e9 --- /dev/null +++ b/tests/index-test.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +import React from 'react' +import ReactDOM from 'react-dom' +import expect from 'expect' +import MaskedInput from 'src' + +const setup = () => { + const element = document.createElement('div') + document.body.appendChild(element) + return element; +}; + +const cleanup = (element) => { + ReactDOM.unmountComponentAtNode(element) + document.body.removeChild(element) +} + +describe('MaskedInput', () => { + it('should render (smokescreen test)', () => { + expect.spyOn(console, 'error') + expect().toExist() + expect(console.error.calls[0].arguments[0]).toBe( + 'Warning: Failed propType: Required prop `mask` was not specified in ' + + '`MaskedInput`.' + ) + }) + + it('should handle a masking workflow', () => { + const el = setup() + let ref = null + ReactDOM.render( + { + if (r) ref = r + }} + mask="11/11" + />, + el + ) + const input = ReactDOM.findDOMNode(ref) + + // initial state + expect(input.value).toBe('') + expect(input.placeholder).toBe('__/__') + expect(input.size).toBe(5) + + cleanup(el) + }) +}) + From d78f89b6e01c3ecb515a3ab491b78b7f23b122dd Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Fri, 20 May 2016 18:48:03 +0300 Subject: [PATCH 21/29] Proxy focus and blur methos (#52) --- src/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/index.js b/src/index.js index 747c39e..71be730 100644 --- a/src/index.js +++ b/src/index.js @@ -160,6 +160,14 @@ var MaskedInput = React.createClass({ var value = this.mask.getValue() return value === this.mask.emptyValue ? '' : value }, + + focus() { + this.input.focus(); + }, + + blur() { + this.input.blur(); + }, render() { var {mask, formatCharacters, size, placeholder, ...props} = this.props From aee2ccf6eb950f5a62c94f2afdcc140343d78c18 Mon Sep 17 00:00:00 2001 From: martinphee Date: Tue, 24 May 2016 08:34:55 -0500 Subject: [PATCH 22/29] Add support for updating the `mask`. Fixes #8 When resetting the mask the cursor would jump to the end of the line. This PR fixes that behavior. --- demo/src/index.js | 16 +++++++++++++++- src/index.js | 19 +++++++++++++++++++ tests/index-test.js | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/demo/src/index.js b/demo/src/index.js index e2a64e6..024e4b8 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -23,7 +23,8 @@ const App = React.createClass({ leading: '', custom: '', changing: '', - pattern: '1111 1111' + pattern: '1111 1111', + cardPattern: '1111 1111 1111 1111' } }, @@ -37,6 +38,14 @@ const App = React.createClass({ this.setState({pattern: e.target.value}) }, + _onCardChange(e) { + if(/^3[47]/.test(e.target.value)) { + this.setState({cardPattern: "1111 111111 11111"}) + } else { + this.setState({cardPattern: '1111 1111 1111 1111'}) + } + }, + render() { return

@@ -86,6 +95,11 @@ const App = React.createClass({ {PATTERNS.map(pattern => )}

+

Dynamically changing the pattern as the user types:

+
+ + +

Custom format character (W=[a-zA-Z0-9_], transformed to uppercase) and placeholder character (en space):

diff --git a/src/index.js b/src/index.js index 747c39e..5750d70 100644 --- a/src/index.js +++ b/src/index.js @@ -49,6 +49,25 @@ var MaskedInput = React.createClass({ } }, + componentWillUpdate(nextProps, nextState) { + if (nextProps.mask !== this.props.mask) { + this._updatePattern(nextProps) + } + }, + + componentDidUpdate(prevProps) { + if (prevProps.mask !== this.props.mask && this.mask.selection.start) { + this._updateInputSelection() + } + }, + + _updatePattern: function(props) { + this.mask.setPattern(props.mask, { + value: this.mask.getRawValue(), + selection: getSelection(this.input) + }); + }, + _updateMaskSelection() { this.mask.selection = getSelection(this.input) }, diff --git a/tests/index-test.js b/tests/index-test.js index e48f6e9..889a838 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -46,5 +46,46 @@ describe('MaskedInput', () => { cleanup(el) }) + + it('should handle updating mask masking', () => { + const el = setup() + let ref = null + let defaultMask = '1111 1111 1111 1111' + let amexMask = '1111 111111 11111' + let mask = defaultMask + + function render() { + ReactDOM.render( + { + if (r) ref = r + }} + mask={mask} + />, + el + ) + } + + render(); + let input = ReactDOM.findDOMNode(ref) + + // initial state + expect(input.value).toBe('') + expect(input.placeholder).toBe('____ ____ ____ ____') + expect(input.size).toBe(19) + expect(input.selectionStart).toBe(0) + + mask = amexMask + render(); + input = ReactDOM.findDOMNode(ref) + + // initial state + expect(input.value).toBe('') + expect(input.placeholder).toBe('____ ______ _____') + expect(input.size).toBe(17) + expect(input.selectionStart).toBe(0) + + cleanup(el) + }) }) From ed4808660b67d75794978bb85daee2f87941b810 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 May 2016 14:55:35 -0400 Subject: [PATCH 23/29] v3.2.0 --- CHANGES.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c9c1b42..fa2ef21 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,12 @@ +## 3.2.0 / 2016-05-24 + +* Allow dynamic pattern updating [[martyphee][martyphee]] + ## 3.1.3 / 2016-05-02 * Don’t call `onChange` function if undefined. * Update nwb to 0.9.x - +s ## 3.1.2 / 2016-04-11 * Support for React 15.x.x @@ -60,3 +64,4 @@ Initial release features: [jquense]: https://github.com/jquense [muffinresearch]: https://github.com/muffinresearch +[martyphee]: https://github.com/martyphee diff --git a/package.json b/package.json index a9ecc3f..9935e8f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-maskedinput", "description": "Masked React component", - "version": "3.1.3", + "version": "3.2.0", "main": "./lib/index.js", "jsnext:main": "es6/index.js", "standalone": "MaskedInput", From 7ff53d2090e97b32980f2e4cab7900171f77c5f5 Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Wed, 21 Oct 2015 16:24:56 -0500 Subject: [PATCH 24/29] fixed so masked input works with chrome auto-fill and on paste --- src/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/index.js b/src/index.js index 20568a2..ba2929f 100644 --- a/src/index.js +++ b/src/index.js @@ -88,6 +88,7 @@ var MaskedInput = React.createClass({ this.mask.selection.end = this.mask.selection.start + sizeDiff this.mask.backspace() } + this.mask.setValue(e.target.value); var value = this._getDisplayValue() e.target.value = value if (value) { @@ -173,6 +174,14 @@ var MaskedInput = React.createClass({ this.props.onChange(e) } } + else { + this.mask.setValue(e.clipboardData.getData('Text')); + var value = this._getDisplayValue() + e.target.value = value + if (value) { + this._updateInputSelection() + } + } }, _getDisplayValue() { From d2519b828e3f5e6cc0e57caf644dd52c1137e1a2 Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Tue, 27 Oct 2015 10:36:47 -0500 Subject: [PATCH 25/29] adding lib for tyler --- lib/index.js | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 lib/index.js diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..954274a --- /dev/null +++ b/lib/index.js @@ -0,0 +1,178 @@ +'use strict'; + +var React = require('react') +var $__0= require('react/lib/ReactInputSelection'),getSelection=$__0.getSelection,setSelection=$__0.setSelection + +var InputMask = require('inputmask-core') + +var KEYCODE_Z = 90 +var KEYCODE_Y = 89 + +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) +} + +var MaskedInput = React.createClass({displayName: "MaskedInput", + propTypes: { + mask: React.PropTypes.string.isRequired, + + formatCharacters: React.PropTypes.object, + placeholderChar: React.PropTypes.string + }, + + getDefaultProps:function() { + return { + value: '' + } + }, + + componentWillMount:function() { + var options = { + pattern: this.props.mask, + value: this.props.value, + formatCharacters: this.props.formatCharacters + } + if (this.props.placeholderChar) { + options.placeholderChar = this.props.placeholderChar + } + this.mask = new InputMask(options) + }, + + componentWillReceiveProps:function(nextProps) { + if (this.props.mask !== nextProps.mask) { + this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()}) + } + }, + + _updateMaskSelection:function() { + this.mask.selection = getSelection(this.input) + }, + + _updateInputSelection:function() { + setSelection(this.input, this.mask.selection) + }, + + _onChange:function(e) { + // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value) + + var maskValue = this.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() + this.mask.selection.end = this.mask.selection.start + sizeDiff + this.mask.backspace() + } + this.mask.setValue(e.target.value); + var value = this._getDisplayValue() + e.target.value = value + if (value) { + this._updateInputSelection() + } + } + if (this.props.onChange) { + this.props.onChange(e) + } + }, + + _onKeyDown:function(e) { + // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value) + + if (isUndo(e)) { + e.preventDefault() + if (this.mask.undo()) { + e.target.value = this._getDisplayValue() + this._updateInputSelection() + this.props.onChange(e) + } + return + } + else if (isRedo(e)) { + e.preventDefault() + if (this.mask.redo()) { + e.target.value = this._getDisplayValue() + this._updateInputSelection() + this.props.onChange(e) + } + return + } + + if (e.key == 'Backspace') { + e.preventDefault() + this._updateMaskSelection() + if (this.mask.backspace()) { + var value = this._getDisplayValue() + e.target.value = value + if (value) { + this._updateInputSelection() + } + this.props.onChange(e) + } + } + }, + + _onKeyPress:function(e) { + // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value) + + // Ignore modified key presses + if (e.metaKey || e.altKey || e.ctrlKey) { return } + + e.preventDefault() + this._updateMaskSelection() + if (this.mask.input(e.key)) { + e.target.value = this.mask.getValue() + this._updateInputSelection() + this.props.onChange(e) + } + }, + + _onPaste:function(e) { + // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value) + + e.preventDefault() + this._updateMaskSelection() + // getData value needed for IE also works in FF & Chrome + if (this.mask.paste(e.clipboardData.getData('Text'))) { + e.target.value = this.mask.getValue() + // Timeout needed for IE + setTimeout(this._updateInputSelection, 0) + this.props.onChange(e) + } + else { + this.mask.setValue(e.clipboardData.getData('Text')); + var value = this._getDisplayValue() + e.target.value = value + if (value) { + this._updateInputSelection() + } + } + }, + + _getDisplayValue:function() { + var value = this.mask.getValue() + return value === this.mask.emptyValue ? '' : value + }, + + render:function() { + var $__0= this.props,mask=$__0.mask,formatCharacters=$__0.formatCharacters,size=$__0.size,placeholder=$__0.placeholder,props=(function(source, exclusion) {var rest = {};var hasOwn = Object.prototype.hasOwnProperty;if (source == null) {throw new TypeError();}for (var key in source) {if (hasOwn.call(source, key) && !hasOwn.call(exclusion, key)) {rest[key] = source[key];}}return rest;})($__0,{mask:1,formatCharacters:1,size:1,placeholder:1}) + var patternLength = this.mask.pattern.length + return React.createElement("input", React.__spread({}, props, + {ref: function(r) {return this.input = r;}.bind(this), + maxLength: patternLength, + onChange: this._onChange, + onKeyDown: this._onKeyDown, + onKeyPress: this._onKeyPress, + onPaste: this._onPaste, + placeholder: placeholder || this.mask.emptyValue, + size: size || patternLength, + value: this._getDisplayValue()}) + ) + } +}) + +module.exports = MaskedInput From 3270574174f02e7ea74c277236c073bbc84b66b4 Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Fri, 13 Nov 2015 15:58:33 -0600 Subject: [PATCH 26/29] fixing mask issue --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index ba2929f..5382bfb 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,7 @@ var MaskedInput = React.createClass({ if (this.props.mask !== nextProps.mask) { this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()}) } + this.mask.setValue(nextProps.value); }, componentWillUpdate(nextProps, nextState) { From 855e02cf7c0964c59b252d309b059a5da8921024 Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Wed, 1 Jun 2016 11:44:59 -0500 Subject: [PATCH 27/29] updating unit tests --- tests/index-test.js | 77 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/index-test.js b/tests/index-test.js index 889a838..ee66a16 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -87,5 +87,80 @@ describe('MaskedInput', () => { cleanup(el) }) -}) + describe('testing full value change', () => { + const el = setup() + let ref = null + let mask = '(111) 111-1111' + let value = '' + + function render() { + ReactDOM.render( + { + if (r) ref = r + }} + mask={mask} + value={value} + />, + el + ) + } + + // keeping the same element to simulate a user changing between different autofill options and selecting different ones. + render(); + let input = ReactDOM.findDOMNode(ref) + + // initial state + it('should have the expected initial state', () => { + expect(input.value).toBe('') + expect(input.placeholder).toBe('(___) ___-____') + expect(input.size).toBe(14) + expect(input.selectionStart).toBe(0) + }) + + it('should handle updating value with formatting', () => { + value = '(432) 543-9876' + + render(); + input = ReactDOM.findDOMNode(ref) + + // new state + expect(input.value).toBe('(432) 543-9876') + expect(input.size).toBe(14) + expect(input.selectionStart).toBe(14) + }) + + it('should handle updating value without formatting', () => { + + value = '3454521234' + + render(); + input = ReactDOM.findDOMNode(ref) + + // new state + expect(input.value).toBe('(345) 452-1234') + expect(input.size).toBe(14) + expect(input.selectionStart).toBe(14) + + }) + + it('should handle updating value with slightly different formatting', () => { + + // please note: if 789-123-4321, the input will fail because it is taking the - as an input + value = '789 123-4321' + + render(); + input = ReactDOM.findDOMNode(ref) + + // new state + expect(input.value).toBe('(789) 123-4321') + expect(input.size).toBe(14) + expect(input.selectionStart).toBe(14) + + }) + + cleanup(el) + }) + +}) From 98a58e5a2c834dfac8d8fa9663f2711aa766ad5f Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Wed, 1 Jun 2016 11:58:14 -0500 Subject: [PATCH 28/29] following linting standards --- src/index.js | 6 +++--- tests/index-test.js | 13 ++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index 5382bfb..7a654c3 100644 --- a/src/index.js +++ b/src/index.js @@ -176,7 +176,7 @@ var MaskedInput = React.createClass({ } } else { - this.mask.setValue(e.clipboardData.getData('Text')); + this.mask.setValue(e.clipboardData.getData('Text')) var value = this._getDisplayValue() e.target.value = value if (value) { @@ -189,11 +189,11 @@ var MaskedInput = React.createClass({ var value = this.mask.getValue() return value === this.mask.emptyValue ? '' : value }, - + focus() { this.input.focus(); }, - + blur() { this.input.blur(); }, diff --git a/tests/index-test.js b/tests/index-test.js index ee66a16..aa684d4 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -108,7 +108,7 @@ describe('MaskedInput', () => { } // keeping the same element to simulate a user changing between different autofill options and selecting different ones. - render(); + render() let input = ReactDOM.findDOMNode(ref) // initial state @@ -122,7 +122,7 @@ describe('MaskedInput', () => { it('should handle updating value with formatting', () => { value = '(432) 543-9876' - render(); + render() input = ReactDOM.findDOMNode(ref) // new state @@ -132,35 +132,30 @@ describe('MaskedInput', () => { }) it('should handle updating value without formatting', () => { - value = '3454521234' - render(); + render() input = ReactDOM.findDOMNode(ref) // new state expect(input.value).toBe('(345) 452-1234') expect(input.size).toBe(14) expect(input.selectionStart).toBe(14) - }) it('should handle updating value with slightly different formatting', () => { - // please note: if 789-123-4321, the input will fail because it is taking the - as an input value = '789 123-4321' - render(); + render() input = ReactDOM.findDOMNode(ref) // new state expect(input.value).toBe('(789) 123-4321') expect(input.size).toBe(14) expect(input.selectionStart).toBe(14) - }) cleanup(el) }) - }) From 1bc4ed782e2e284bd3ae0a831cb65b160deb9f09 Mon Sep 17 00:00:00 2001 From: Matt Wanner Date: Wed, 1 Jun 2016 12:12:52 -0500 Subject: [PATCH 29/29] removing additions that have already been fixed. --- src/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.js b/src/index.js index 7a654c3..346bc87 100644 --- a/src/index.js +++ b/src/index.js @@ -47,7 +47,6 @@ var MaskedInput = React.createClass({ if (this.props.mask !== nextProps.mask) { this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()}) } - this.mask.setValue(nextProps.value); }, componentWillUpdate(nextProps, nextState) { @@ -89,7 +88,6 @@ var MaskedInput = React.createClass({ this.mask.selection.end = this.mask.selection.start + sizeDiff this.mask.backspace() } - this.mask.setValue(e.target.value); var value = this._getDisplayValue() e.target.value = value if (value) {