react-number-format
Advanced tools
Comparing version 1.2.1 to 2.0.0-alpha
/*! | ||
* react-number-format - 1.2.1 | ||
* react-number-format - 2.0.0-alpha | ||
* Author : Sudhanshu Yadav | ||
@@ -89,3 +89,14 @@ * Copyright (c) 2016,2017 to Sudhanshu Yadav - ignitersworld.com , released under the MIT license. | ||
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; } //const React = require('react'); | ||
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; } /** | ||
* 1. Validate thousand separators and decimals throw error | ||
* 2. Thousand separator just have value true or any other string | ||
* 3. Decimal separator should be defined only as string | ||
* 4. Decimal precision should be only defined as number | ||
* 5. If user don't want floating numbers set decimalPrecision to 0 | ||
* 6. User can pass value as floating point numbers or string, if user passes string decimal separator in string should match to provided decimalSeparator | ||
* 7. Add formattedValue, numeric value, value with string in event object and not as parameters so that getting values should look consistent | ||
* 8. dont use parseFloat that will not able to parse 2^23 | ||
* 9. Always have decimal precision | ||
* 10. isAllowed props to validate input and block if returns false | ||
*/ | ||
@@ -97,6 +108,19 @@ | ||
function removeLeadingZero(numStr) { | ||
//remove leading zeros | ||
return numStr.replace(/^0+/, '') || '0'; | ||
} | ||
function limitToPrecision(numStr, precision) { | ||
var str = ''; | ||
for (var i = 0; i <= precision - 1; i++) { | ||
str += numStr[i] || '0'; | ||
} | ||
return str; | ||
} | ||
var propTypes = { | ||
thousandSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]), | ||
decimalSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]), | ||
decimalPrecision: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.bool]), | ||
thousandSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.oneOf([true])]), | ||
decimalSeparator: _propTypes2.default.string, | ||
decimalPrecision: _propTypes2.default.number, | ||
displayType: _propTypes2.default.oneOf(['input', 'text']), | ||
@@ -111,3 +135,4 @@ prefix: _propTypes2.default.string, | ||
onKeyDown: _propTypes2.default.func, | ||
onChange: _propTypes2.default.func | ||
onChange: _propTypes2.default.func, | ||
isAllowed: _propTypes2.default.func | ||
}; | ||
@@ -118,4 +143,6 @@ | ||
decimalSeparator: '.', | ||
decimalPrecision: false, | ||
allowNegative: true | ||
allowNegative: true, | ||
isAllowed: function isAllowed() { | ||
return true; | ||
} | ||
}; | ||
@@ -131,4 +158,5 @@ | ||
var value = _this.optimizeValueProp(props); | ||
_this.state = { | ||
value: _this.formatInput(props.value).formattedValue | ||
value: _this.formatInput(value).formattedValue | ||
}; | ||
@@ -141,16 +169,70 @@ _this.onChange = _this.onChange.bind(_this); | ||
_createClass(NumberFormat, [{ | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(newProps) { | ||
if (newProps.value !== this.props.value) { | ||
this.setState({ | ||
value: this.formatInput(newProps.value).formattedValue | ||
}); | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate(prevProps, prevState) { | ||
this.updateValueIfRequired(prevProps, prevState); | ||
} | ||
}, { | ||
key: 'updateValueIfRequired', | ||
value: function updateValueIfRequired(prevProps) { | ||
var props = this.props, | ||
state = this.state; | ||
if (prevProps !== props) { | ||
var stateValue = state.value; | ||
var value = this.optimizeValueProp(props); | ||
if (value === undefined) value = stateValue; | ||
var _formatInput = this.formatInput(value), | ||
formattedValue = _formatInput.formattedValue; | ||
if (formattedValue !== stateValue) { | ||
this.setState({ | ||
value: this.formatInput(value).formattedValue | ||
}); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'getFloatValue', | ||
value: function getFloatValue(num) { | ||
var decimalSeparator = this.props.decimalSeparator; | ||
return parseFloat(num.replace(decimalSeparator, '.')) || 0; | ||
} | ||
}, { | ||
key: 'optimizeValueProp', | ||
value: function optimizeValueProp(props) { | ||
var _getSeparators = this.getSeparators(props), | ||
decimalSeparator = _getSeparators.decimalSeparator; | ||
var value = props.value, | ||
decimalPrecision = props.decimalPrecision, | ||
format = props.format; | ||
if (format || value === undefined) return value; | ||
var isNumber = typeof value === 'number'; | ||
if (isNumber) value = value.toString(); | ||
//correct decimal separator | ||
if (decimalSeparator && isNumber) { | ||
value = value.replace('.', decimalSeparator); | ||
} | ||
//if decimalPrecision is 0 remove decimalNumbers | ||
if (decimalPrecision === 0) return value.split(decimalSeparator)[0]; | ||
return value; | ||
} | ||
}, { | ||
key: 'getSeparators', | ||
value: function getSeparators() { | ||
var _props = this.props, | ||
thousandSeparator = _props.thousandSeparator, | ||
decimalSeparator = _props.decimalSeparator; | ||
value: function getSeparators(props) { | ||
var _ref = props || this.props, | ||
thousandSeparator = _ref.thousandSeparator, | ||
decimalSeparator = _ref.decimalSeparator, | ||
decimalPrecision = _ref.decimalPrecision; | ||
@@ -161,14 +243,6 @@ if (thousandSeparator === true) { | ||
if (decimalSeparator && thousandSeparator && typeof decimalSeparator !== 'string') { | ||
decimalSeparator = thousandSeparator === '.' ? ',' : '.'; | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error('\n Decimal separator can\'t be same as thousand separator.\n\n thousandSeparator: ' + thousandSeparator + ' (thousandSeparator = {true} is same as thousandSeparator = ",")\n decimalSeparator: ' + decimalSeparator + ' (default value for decimalSeparator is .)\n '); | ||
} | ||
if (thousandSeparator === '.') { | ||
decimalSeparator = ','; | ||
} | ||
if (decimalSeparator === true) { | ||
decimalSeparator = '.'; | ||
} | ||
return { | ||
@@ -181,9 +255,11 @@ decimalSeparator: decimalSeparator, | ||
key: 'getNumberRegex', | ||
value: function getNumberRegex(g, ignoreDecimalSeperator) { | ||
var format = this.props.format; | ||
value: function getNumberRegex(g, ignoreDecimalSeparator) { | ||
var _props = this.props, | ||
format = _props.format, | ||
decimalPrecision = _props.decimalPrecision; | ||
var _getSeparators = this.getSeparators(), | ||
decimalSeparator = _getSeparators.decimalSeparator; | ||
var _getSeparators2 = this.getSeparators(), | ||
decimalSeparator = _getSeparators2.decimalSeparator; | ||
return new RegExp('\\d' + (decimalSeparator && !ignoreDecimalSeperator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
return new RegExp('\\d' + (decimalSeparator && decimalPrecision !== 0 && !ignoreDecimalSeparator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
} | ||
@@ -217,2 +293,17 @@ }, { | ||
}, { | ||
key: 'setPatchedCaretPosition', | ||
value: function setPatchedCaretPosition(el, caretPos) { | ||
var _this2 = this; | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
this.setCaretPosition(el, caretPos); | ||
setTimeout(function () { | ||
return _this2.setCaretPosition(el, caretPos); | ||
}, 0); | ||
} | ||
}, { | ||
key: 'formatWithPattern', | ||
@@ -254,5 +345,5 @@ value: function formatWithPattern(str) { | ||
var _getSeparators2 = this.getSeparators(), | ||
thousandSeparator = _getSeparators2.thousandSeparator, | ||
decimalSeparator = _getSeparators2.decimalSeparator; | ||
var _getSeparators3 = this.getSeparators(), | ||
thousandSeparator = _getSeparators3.thousandSeparator, | ||
decimalSeparator = _getSeparators3.decimalSeparator; | ||
@@ -298,27 +389,18 @@ var maskPattern = format && typeof format == 'string' && !!mask; | ||
} else { | ||
var beforeDecimal = formattedValue, | ||
afterDecimal = ''; | ||
var hasDecimals = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision !== false; | ||
if (decimalSeparator && hasDecimals) { | ||
var parts = void 0; | ||
if (decimalPrecision !== false) { | ||
var precision = decimalPrecision === true ? 2 : decimalPrecision; | ||
if (decimalSeparator !== '.') { | ||
// Replace custom decimalSeparator with '.' for parseFloat function | ||
parts = parseFloat(formattedValue.replace(decimalSeparator, '.')).toFixed(precision); | ||
// Put custom decimalSeparator back | ||
parts = parts.replace('.', decimalSeparator); | ||
} else { | ||
parts = parseFloat(formattedValue).toFixed(precision); | ||
} | ||
parts = parts.split(decimalSeparator); | ||
} else { | ||
parts = formattedValue.split(decimalSeparator); | ||
} | ||
beforeDecimal = parts[0]; | ||
afterDecimal = parts[1]; | ||
} | ||
var hasDecimalSeparator = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision; | ||
var parts = formattedValue.split(decimalSeparator); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
//remove leading zeros from number before decimal | ||
beforeDecimal = removeLeadingZero(beforeDecimal); | ||
//apply decimal precision if its defined | ||
if (decimalPrecision !== undefined) afterDecimal = limitToPrecision(afterDecimal, decimalPrecision); | ||
if (thousandSeparator) { | ||
beforeDecimal = beforeDecimal.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousandSeparator); | ||
} | ||
//add prefix and suffix | ||
@@ -330,3 +412,3 @@ if (prefix) beforeDecimal = prefix + beforeDecimal; | ||
formattedValue = beforeDecimal + (hasDecimals && decimalSeparator || '') + afterDecimal; | ||
formattedValue = beforeDecimal + (hasDecimalSeparator && decimalSeparator || '') + afterDecimal; | ||
} | ||
@@ -347,4 +429,6 @@ | ||
j = 0; | ||
for (i = 0; i < cursorPos; i++) { | ||
if (!inputValue[i].match(numRegex) && inputValue[i] !== formattedValue[j]) continue; | ||
if (inputValue[i] === '0' && formattedValue[j].match(numRegex) && formattedValue[j] !== '0') continue; | ||
while (inputValue[i] !== formattedValue[j] && j < formattedValue.length) { | ||
@@ -360,3 +444,3 @@ j++; | ||
value: function onChangeHandler(e, callback) { | ||
var _this2 = this; | ||
var _this3 = this; | ||
@@ -366,22 +450,26 @@ e.persist(); | ||
var inputValue = el.value; | ||
var isAllowed = this.props.isAllowed; | ||
var _formatInput = this.formatInput(inputValue), | ||
formattedValue = _formatInput.formattedValue, | ||
value = _formatInput.value; | ||
var lastValue = this.state.value; | ||
var cursorPos = el.selectionStart; | ||
var _formatInput2 = this.formatInput(inputValue), | ||
formattedValue = _formatInput2.formattedValue, | ||
value = _formatInput2.value; | ||
var cursorPos = this.getCursorPosition(inputValue, formattedValue, el.selectionStart); | ||
//set caret position befor setState | ||
//this.setPatchedCaretPosition(el, cursorPos); | ||
if (!isAllowed(formattedValue, value, this.getFloatValue(value))) { | ||
formattedValue = lastValue; | ||
} | ||
//change the state | ||
this.setState({ value: formattedValue }, function () { | ||
cursorPos = _this2.getCursorPosition(inputValue, formattedValue, cursorPos); | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
_this2.setCaretPosition(el, cursorPos); | ||
setTimeout(function () { | ||
return _this2.setCaretPosition(el, cursorPos); | ||
}, 0); | ||
if (callback) callback(e, value); | ||
//reset again after setState so if formattedValue is other then | ||
_this3.setPatchedCaretPosition(el, cursorPos); | ||
if (callback && formattedValue !== lastValue) callback(e, value); | ||
}); | ||
@@ -404,9 +492,15 @@ | ||
var decimalPrecision = this.props.decimalPrecision; | ||
var key = e.key; | ||
var key = e.key, | ||
which = e.which, | ||
keyCode = e.keyCode; | ||
var numRegex = this.getNumberRegex(false, decimalPrecision !== false); | ||
var numRegex = this.getNumberRegex(false, decimalPrecision !== undefined); | ||
console.log(numRegex.toString(), key, which, keyCode); | ||
console.log(e); | ||
var negativeRegex = new RegExp('-'); | ||
//Handle backspace and delete against non numerical/decimal characters | ||
if (selectionEnd - selectionStart === 0) { | ||
console.log('coming here'); | ||
if (key === 'Delete' && !numRegex.test(value[selectionStart]) && !negativeRegex.test(value[selectionStart])) { | ||
console.log('delete'); | ||
e.preventDefault(); | ||
@@ -416,4 +510,5 @@ var nextCursorPosition = selectionStart; | ||
nextCursorPosition++; | ||
}this.setCaretPosition(el, nextCursorPosition); | ||
}this.setPatchedCaretPosition(el, nextCursorPosition); | ||
} else if (key === 'Backspace' && !numRegex.test(value[selectionStart - 1]) && !negativeRegex.test(value[selectionStart - 1])) { | ||
console.log('Backspace'); | ||
e.preventDefault(); | ||
@@ -423,3 +518,3 @@ var prevCursorPosition = selectionStart; | ||
prevCursorPosition--; | ||
}this.setCaretPosition(el, prevCursorPosition); | ||
}this.setPatchedCaretPosition(el, prevCursorPosition); | ||
} | ||
@@ -426,0 +521,0 @@ } |
/*! | ||
* react-number-format - 1.2.1 | ||
* react-number-format - 2.0.0-alpha | ||
* Author : Sudhanshu Yadav | ||
* Copyright (c) 2016,2017 to Sudhanshu Yadav - ignitersworld.com , released under the MIT license. | ||
*/ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.NumberFormat=t(require("react")):e.NumberFormat=t(e.React)}(this,function(e){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){e.exports=r(1)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},f=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),l=r(2),p=n(l),c=r(7),d=n(c),h={thousandSeparator:p.default.oneOfType([p.default.string,p.default.bool]),decimalSeparator:p.default.oneOfType([p.default.string,p.default.bool]),decimalPrecision:p.default.oneOfType([p.default.number,p.default.bool]),displayType:p.default.oneOf(["input","text"]),prefix:p.default.string,suffix:p.default.string,format:p.default.oneOfType([p.default.string,p.default.func]),mask:p.default.string,value:p.default.oneOfType([p.default.number,p.default.string]),customInput:p.default.func,allowNegative:p.default.bool,onKeyDown:p.default.func,onChange:p.default.func},v={displayType:"input",decimalSeparator:".",decimalPrecision:!1,allowNegative:!0},m=function(e){function t(e){o(this,t);var r=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return r.state={value:r.formatInput(e.value).formattedValue},r.onChange=r.onChange.bind(r),r.onKeyDown=r.onKeyDown.bind(r),r}return i(t,e),f(t,[{key:"componentWillReceiveProps",value:function(e){e.value!==this.props.value&&this.setState({value:this.formatInput(e.value).formattedValue})}},{key:"getSeparators",value:function(){var e=this.props,t=e.thousandSeparator,r=e.decimalSeparator;return t===!0&&(t=","),r&&t&&"string"!=typeof r&&(r="."===t?",":"."),"."===t&&(r=","),r===!0&&(r="."),{decimalSeparator:r,thousandSeparator:t}}},{key:"getNumberRegex",value:function(e,t){var r=this.props.format,n=this.getSeparators(),o=n.decimalSeparator;return new RegExp("\\d"+(!o||t||r?"":"|"+u(o)),e?"g":void 0)}},{key:"setCaretPosition",value:function(e,t){if(e.value=e.value,null!==e){if(e.createTextRange){var r=e.createTextRange();return r.move("character",t),r.select(),!0}return e.selectionStart||0===e.selectionStart?(e.focus(),e.setSelectionRange(t,t),!0):(e.focus(),!1)}}},{key:"formatWithPattern",value:function(e){var t=this.props,r=t.format,n=t.mask;if(!r)return e;for(var o=r.split("#").length-1,a=0,i=r,u=0,s=e.length;u<s;u++)u<o&&(a=i.indexOf("#"),i=i.replace("#",e[u]));var f=i.lastIndexOf("#");return n?i.replace(/#/g,n):i.substring(0,a+1)+(f!==-1?i.substring(f+1,i.length):"")}},{key:"formatInput",value:function(e){var t=this.props,r=t.prefix,n=t.suffix,o=(t.mask,t.format),a=t.allowNegative,i=t.decimalPrecision,u=this.getSeparators(),s=u.thousandSeparator,f=u.decimalSeparator,l=this.getNumberRegex(!0),p=void 0,c=void 0;"number"==typeof e&&(e+="");var d=new RegExp("(-)"),h=new RegExp("(-)(.)*(-)");a&&!o&&(p=d.test(e),c=h.test(e));var v=e&&e.match(l);if(!v&&c)return{value:"",formattedValue:""};if(!v&&p)return{value:"",formattedValue:"-"};if(!v)return{value:"",formattedValue:""};var m=e.match(l).join(""),y=m;if(o)"string"==typeof o?y=this.formatWithPattern(y):"function"==typeof o&&(y=o(y));else{var g=y,b="",x=y.indexOf(f)!==-1||i!==!1;if(f&&x){var O=void 0;if(i!==!1){var w=i===!0?2:i;"."!==f?(O=parseFloat(y.replace(f,".")).toFixed(w),O=O.replace(".",f)):O=parseFloat(y).toFixed(w),O=O.split(f)}else O=y.split(f);g=O[0],b=O[1]}s&&(g=g.replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1"+s)),r&&(g=r+g),n&&(b+=n),p&&!c&&(g="-"+g),y=g+(x&&f||"")+b}return{value:(p&&!c?"-":"")+y.match(l).join(""),formattedValue:y}}},{key:"getCursorPosition",value:function(e,t,r){var n=this.getNumberRegex(),o=void 0,a=void 0;for(o=0,a=0;a<r;a++)if(e[a].match(n)||e[a]===t[o]){for(;e[a]!==t[o]&&o<t.length;)o++;o++}return o}},{key:"onChangeHandler",value:function(e,t){var r=this;e.persist();var n=e.target,o=n.value,a=this.formatInput(o),i=a.formattedValue,u=a.value,s=n.selectionStart;return this.setState({value:i},function(){s=r.getCursorPosition(o,i,s),r.setCaretPosition(n,s),setTimeout(function(){return r.setCaretPosition(n,s)},0),t&&t(e,u)}),u}},{key:"onChange",value:function(e){this.onChangeHandler(e,this.props.onChange)}},{key:"onKeyDown",value:function(e){var t=e.target,r=t.selectionStart,n=t.selectionEnd,o=t.value,a=this.props.decimalPrecision,i=e.key,u=this.getNumberRegex(!1,a!==!1),s=new RegExp("-");if(n-r===0)if("Delete"!==i||u.test(o[r])||s.test(o[r])){if("Backspace"===i&&!u.test(o[r-1])&&!s.test(o[r-1])){e.preventDefault();for(var f=r;!u.test(o[f-1])&&f>0;)f--;this.setCaretPosition(t,f)}}else{e.preventDefault();for(var l=r;!u.test(o[l])&&l<o.length;)l++;this.setCaretPosition(t,l)}this.props.onKeyDown&&this.props.onKeyDown(e)}},{key:"render",value:function(){var e=s({},this.props);Object.keys(h).forEach(function(t){delete e[t]});var t=s({},e,{type:"text",value:this.state.value,onChange:this.onChange,onKeyDown:this.onKeyDown});if("text"===this.props.displayType)return d.default.createElement("span",e,this.state.value);if(this.props.customInput){var r=this.props.customInput;return d.default.createElement(r,t)}return d.default.createElement("input",t)}}]),t}(d.default.Component);m.propTypes=h,m.defaultProps=v,e.exports=m},function(e,t,r){e.exports=r(3)()},function(e,t,r){"use strict";var n=r(4),o=r(5),a=r(6);e.exports=function(){function e(e,t,r,n,i,u){u!==a&&o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var r={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t};return r.checkPropTypes=n,r.PropTypes=r,r}},function(e,t){"use strict";function r(e){return function(){return e}}var n=function(){};n.thatReturns=r,n.thatReturnsFalse=r(!1),n.thatReturnsTrue=r(!0),n.thatReturnsNull=r(null),n.thatReturnsThis=function(){return this},n.thatReturnsArgument=function(e){return e},e.exports=n},function(e,t,r){"use strict";function n(e,t,r,n,a,i,u,s){if(o(t),!e){var f;if(void 0===t)f=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[r,n,a,i,u,s],p=0;f=new Error(t.replace(/%s/g,function(){return l[p++]})),f.name="Invariant Violation"}throw f.framesToPop=1,f}}var o=function(e){};e.exports=n},function(e,t){"use strict";var r="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=r},function(t,r){t.exports=e}])}); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.NumberFormat=t(require("react")):e.NumberFormat=t(e.React)}(this,function(e){return function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){e.exports=r(1)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}function s(e){return e.replace(/^0+/,"")||"0"}function l(e,t){for(var r="",n=0;n<=t-1;n++)r+=e[n]||"0";return r}var f=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},c=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),p=r(2),d=n(p),h=r(7),v=n(h),m={thousandSeparator:d.default.oneOfType([d.default.string,d.default.oneOf([!0])]),decimalSeparator:d.default.string,decimalPrecision:d.default.number,displayType:d.default.oneOf(["input","text"]),prefix:d.default.string,suffix:d.default.string,format:d.default.oneOfType([d.default.string,d.default.func]),mask:d.default.string,value:d.default.oneOfType([d.default.number,d.default.string]),customInput:d.default.func,allowNegative:d.default.bool,onKeyDown:d.default.func,onChange:d.default.func,isAllowed:d.default.func},g={displayType:"input",decimalSeparator:".",allowNegative:!0,isAllowed:function(){return!0}},y=function(e){function t(e){a(this,t);var r=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e)),n=r.optimizeValueProp(e);return r.state={value:r.formatInput(n).formattedValue},r.onChange=r.onChange.bind(r),r.onKeyDown=r.onKeyDown.bind(r),r}return i(t,e),c(t,[{key:"componentDidUpdate",value:function(e,t){this.updateValueIfRequired(e,t)}},{key:"updateValueIfRequired",value:function(e){var t=this.props,r=this.state;if(e!==t){var n=r.value,a=this.optimizeValueProp(t);void 0===a&&(a=n);var o=this.formatInput(a),i=o.formattedValue;i!==n&&this.setState({value:this.formatInput(a).formattedValue})}}},{key:"getFloatValue",value:function(e){var t=this.props.decimalSeparator;return parseFloat(e.replace(t,"."))||0}},{key:"optimizeValueProp",value:function(e){var t=this.getSeparators(e),r=t.decimalSeparator,n=e.value,a=e.decimalPrecision,o=e.format;if(o||void 0===n)return n;var i="number"==typeof n;return i&&(n=n.toString()),r&&i&&(n=n.replace(".",r)),0===a?n.split(r)[0]:n}},{key:"getSeparators",value:function(e){var t=e||this.props,r=t.thousandSeparator,n=t.decimalSeparator;t.decimalPrecision;if(r===!0&&(r=","),n===r)throw new Error("\n Decimal separator can't be same as thousand separator.\n\n thousandSeparator: "+r+' (thousandSeparator = {true} is same as thousandSeparator = ",")\n decimalSeparator: '+n+" (default value for decimalSeparator is .)\n ");return{decimalSeparator:n,thousandSeparator:r}}},{key:"getNumberRegex",value:function(e,t){var r=this.props,n=r.format,a=r.decimalPrecision,o=this.getSeparators(),i=o.decimalSeparator;return new RegExp("\\d"+(!i||0===a||t||n?"":"|"+u(i)),e?"g":void 0)}},{key:"setCaretPosition",value:function(e,t){if(e.value=e.value,null!==e){if(e.createTextRange){var r=e.createTextRange();return r.move("character",t),r.select(),!0}return e.selectionStart||0===e.selectionStart?(e.focus(),e.setSelectionRange(t,t),!0):(e.focus(),!1)}}},{key:"setPatchedCaretPosition",value:function(e,t){var r=this;this.setCaretPosition(e,t),setTimeout(function(){return r.setCaretPosition(e,t)},0)}},{key:"formatWithPattern",value:function(e){var t=this.props,r=t.format,n=t.mask;if(!r)return e;for(var a=r.split("#").length-1,o=0,i=r,u=0,s=e.length;u<s;u++)u<a&&(o=i.indexOf("#"),i=i.replace("#",e[u]));var l=i.lastIndexOf("#");return n?i.replace(/#/g,n):i.substring(0,o+1)+(l!==-1?i.substring(l+1,i.length):"")}},{key:"formatInput",value:function(e){var t=this.props,r=t.prefix,n=t.suffix,a=(t.mask,t.format),o=t.allowNegative,i=t.decimalPrecision,u=this.getSeparators(),f=u.thousandSeparator,c=u.decimalSeparator,p=this.getNumberRegex(!0),d=void 0,h=void 0;"number"==typeof e&&(e+="");var v=new RegExp("(-)"),m=new RegExp("(-)(.)*(-)");o&&!a&&(d=v.test(e),h=m.test(e));var g=e&&e.match(p);if(!g&&h)return{value:"",formattedValue:""};if(!g&&d)return{value:"",formattedValue:"-"};if(!g)return{value:"",formattedValue:""};var y=e.match(p).join(""),b=y;if(a)"string"==typeof a?b=this.formatWithPattern(b):"function"==typeof a&&(b=a(b));else{var x=b.indexOf(c)!==-1||i,P=b.split(c),S=P[0],w=P[1]||"";S=s(S),void 0!==i&&(w=l(w,i)),f&&(S=S.replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1"+f)),r&&(S=r+S),n&&(w+=n),d&&!h&&(S="-"+S),b=S+(x&&c||"")+w}return{value:(d&&!h?"-":"")+b.match(p).join(""),formattedValue:b}}},{key:"getCursorPosition",value:function(e,t,r){var n=this.getNumberRegex(),a=void 0,o=void 0;for(a=0,o=0;o<r;o++)if((e[o].match(n)||e[o]===t[a])&&("0"!==e[o]||!t[a].match(n)||"0"===t[a])){for(;e[o]!==t[a]&&a<t.length;)a++;a++}return a}},{key:"onChangeHandler",value:function(e,t){var r=this;e.persist();var n=e.target,a=n.value,o=this.props.isAllowed,i=this.state.value,u=this.formatInput(a),s=u.formattedValue,l=u.value,f=this.getCursorPosition(a,s,n.selectionStart);return o(s,l,this.getFloatValue(l))||(s=i),this.setState({value:s},function(){r.setPatchedCaretPosition(n,f),t&&s!==i&&t(e,l)}),l}},{key:"onChange",value:function(e){this.onChangeHandler(e,this.props.onChange)}},{key:"onKeyDown",value:function(e){var t=e.target,r=t.selectionStart,n=t.selectionEnd,a=t.value,o=this.props.decimalPrecision,i=e.key,u=e.which,s=e.keyCode,l=this.getNumberRegex(!1,void 0!==o);console.log(l.toString(),i,u,s),console.log(e);var f=new RegExp("-");if(n-r===0)if(console.log("coming here"),"Delete"!==i||l.test(a[r])||f.test(a[r])){if("Backspace"===i&&!l.test(a[r-1])&&!f.test(a[r-1])){console.log("Backspace"),e.preventDefault();for(var c=r;!l.test(a[c-1])&&c>0;)c--;this.setPatchedCaretPosition(t,c)}}else{console.log("delete"),e.preventDefault();for(var p=r;!l.test(a[p])&&p<a.length;)p++;this.setPatchedCaretPosition(t,p)}this.props.onKeyDown&&this.props.onKeyDown(e)}},{key:"render",value:function(){var e=f({},this.props);Object.keys(m).forEach(function(t){delete e[t]});var t=f({},e,{type:"text",value:this.state.value,onChange:this.onChange,onKeyDown:this.onKeyDown});if("text"===this.props.displayType)return v.default.createElement("span",e,this.state.value);if(this.props.customInput){var r=this.props.customInput;return v.default.createElement(r,t)}return v.default.createElement("input",t)}}]),t}(v.default.Component);y.propTypes=m,y.defaultProps=g,e.exports=y},function(e,t,r){e.exports=r(3)()},function(e,t,r){"use strict";var n=r(4),a=r(5),o=r(6);e.exports=function(){function e(e,t,r,n,i,u){u!==o&&a(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var r={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t};return r.checkPropTypes=n,r.PropTypes=r,r}},function(e,t){"use strict";function r(e){return function(){return e}}var n=function(){};n.thatReturns=r,n.thatReturnsFalse=r(!1),n.thatReturnsTrue=r(!0),n.thatReturnsNull=r(null),n.thatReturnsThis=function(){return this},n.thatReturnsArgument=function(e){return e},e.exports=n},function(e,t,r){"use strict";function n(e,t,r,n,o,i,u,s){if(a(t),!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var f=[r,n,o,i,u,s],c=0;l=new Error(t.replace(/%s/g,function(){return f[c++]})),l.name="Invariant Violation"}throw l.framesToPop=1,l}}var a=function(e){};e.exports=n},function(e,t){"use strict";var r="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=r},function(t,r){t.exports=e}])}); |
@@ -53,3 +53,3 @@ import React from 'react'; | ||
</h3> | ||
<NumberFormat thousandSeparator={true} decimalPrecision={true} prefix={'$'}/> | ||
<NumberFormat thousandSeparator={true} decimalPrecision={3} prefix={'$'}/> | ||
</div> | ||
@@ -56,0 +56,0 @@ |
@@ -21,3 +21,14 @@ 'use strict'; | ||
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; } //const React = require('react'); | ||
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; } /** | ||
* 1. Validate thousand separators and decimals throw error | ||
* 2. Thousand separator just have value true or any other string | ||
* 3. Decimal separator should be defined only as string | ||
* 4. Decimal precision should be only defined as number | ||
* 5. If user don't want floating numbers set decimalPrecision to 0 | ||
* 6. User can pass value as floating point numbers or string, if user passes string decimal separator in string should match to provided decimalSeparator | ||
* 7. Add formattedValue, numeric value, value with string in event object and not as parameters so that getting values should look consistent | ||
* 8. dont use parseFloat that will not able to parse 2^23 | ||
* 9. Always have decimal precision | ||
* 10. isAllowed props to validate input and block if returns false | ||
*/ | ||
@@ -29,6 +40,19 @@ | ||
function removeLeadingZero(numStr) { | ||
//remove leading zeros | ||
return numStr.replace(/^0+/, '') || '0'; | ||
} | ||
function limitToPrecision(numStr, precision) { | ||
var str = ''; | ||
for (var i = 0; i <= precision - 1; i++) { | ||
str += numStr[i] || '0'; | ||
} | ||
return str; | ||
} | ||
var propTypes = { | ||
thousandSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]), | ||
decimalSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]), | ||
decimalPrecision: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.bool]), | ||
thousandSeparator: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.oneOf([true])]), | ||
decimalSeparator: _propTypes2.default.string, | ||
decimalPrecision: _propTypes2.default.number, | ||
displayType: _propTypes2.default.oneOf(['input', 'text']), | ||
@@ -43,3 +67,4 @@ prefix: _propTypes2.default.string, | ||
onKeyDown: _propTypes2.default.func, | ||
onChange: _propTypes2.default.func | ||
onChange: _propTypes2.default.func, | ||
isAllowed: _propTypes2.default.func | ||
}; | ||
@@ -50,4 +75,6 @@ | ||
decimalSeparator: '.', | ||
decimalPrecision: false, | ||
allowNegative: true | ||
allowNegative: true, | ||
isAllowed: function isAllowed() { | ||
return true; | ||
} | ||
}; | ||
@@ -63,4 +90,5 @@ | ||
var value = _this.optimizeValueProp(props); | ||
_this.state = { | ||
value: _this.formatInput(props.value).formattedValue | ||
value: _this.formatInput(value).formattedValue | ||
}; | ||
@@ -73,16 +101,70 @@ _this.onChange = _this.onChange.bind(_this); | ||
_createClass(NumberFormat, [{ | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(newProps) { | ||
if (newProps.value !== this.props.value) { | ||
this.setState({ | ||
value: this.formatInput(newProps.value).formattedValue | ||
}); | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate(prevProps, prevState) { | ||
this.updateValueIfRequired(prevProps, prevState); | ||
} | ||
}, { | ||
key: 'updateValueIfRequired', | ||
value: function updateValueIfRequired(prevProps) { | ||
var props = this.props, | ||
state = this.state; | ||
if (prevProps !== props) { | ||
var stateValue = state.value; | ||
var value = this.optimizeValueProp(props); | ||
if (value === undefined) value = stateValue; | ||
var _formatInput = this.formatInput(value), | ||
formattedValue = _formatInput.formattedValue; | ||
if (formattedValue !== stateValue) { | ||
this.setState({ | ||
value: this.formatInput(value).formattedValue | ||
}); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'getFloatValue', | ||
value: function getFloatValue(num) { | ||
var decimalSeparator = this.props.decimalSeparator; | ||
return parseFloat(num.replace(decimalSeparator, '.')) || 0; | ||
} | ||
}, { | ||
key: 'optimizeValueProp', | ||
value: function optimizeValueProp(props) { | ||
var _getSeparators = this.getSeparators(props), | ||
decimalSeparator = _getSeparators.decimalSeparator; | ||
var value = props.value, | ||
decimalPrecision = props.decimalPrecision, | ||
format = props.format; | ||
if (format || value === undefined) return value; | ||
var isNumber = typeof value === 'number'; | ||
if (isNumber) value = value.toString(); | ||
//correct decimal separator | ||
if (decimalSeparator && isNumber) { | ||
value = value.replace('.', decimalSeparator); | ||
} | ||
//if decimalPrecision is 0 remove decimalNumbers | ||
if (decimalPrecision === 0) return value.split(decimalSeparator)[0]; | ||
return value; | ||
} | ||
}, { | ||
key: 'getSeparators', | ||
value: function getSeparators() { | ||
var _props = this.props, | ||
thousandSeparator = _props.thousandSeparator, | ||
decimalSeparator = _props.decimalSeparator; | ||
value: function getSeparators(props) { | ||
var _ref = props || this.props, | ||
thousandSeparator = _ref.thousandSeparator, | ||
decimalSeparator = _ref.decimalSeparator, | ||
decimalPrecision = _ref.decimalPrecision; | ||
@@ -93,14 +175,6 @@ if (thousandSeparator === true) { | ||
if (decimalSeparator && thousandSeparator && typeof decimalSeparator !== 'string') { | ||
decimalSeparator = thousandSeparator === '.' ? ',' : '.'; | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error('\n Decimal separator can\'t be same as thousand separator.\n\n thousandSeparator: ' + thousandSeparator + ' (thousandSeparator = {true} is same as thousandSeparator = ",")\n decimalSeparator: ' + decimalSeparator + ' (default value for decimalSeparator is .)\n '); | ||
} | ||
if (thousandSeparator === '.') { | ||
decimalSeparator = ','; | ||
} | ||
if (decimalSeparator === true) { | ||
decimalSeparator = '.'; | ||
} | ||
return { | ||
@@ -113,9 +187,11 @@ decimalSeparator: decimalSeparator, | ||
key: 'getNumberRegex', | ||
value: function getNumberRegex(g, ignoreDecimalSeperator) { | ||
var format = this.props.format; | ||
value: function getNumberRegex(g, ignoreDecimalSeparator) { | ||
var _props = this.props, | ||
format = _props.format, | ||
decimalPrecision = _props.decimalPrecision; | ||
var _getSeparators = this.getSeparators(), | ||
decimalSeparator = _getSeparators.decimalSeparator; | ||
var _getSeparators2 = this.getSeparators(), | ||
decimalSeparator = _getSeparators2.decimalSeparator; | ||
return new RegExp('\\d' + (decimalSeparator && !ignoreDecimalSeperator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
return new RegExp('\\d' + (decimalSeparator && decimalPrecision !== 0 && !ignoreDecimalSeparator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
} | ||
@@ -149,2 +225,17 @@ }, { | ||
}, { | ||
key: 'setPatchedCaretPosition', | ||
value: function setPatchedCaretPosition(el, caretPos) { | ||
var _this2 = this; | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
this.setCaretPosition(el, caretPos); | ||
setTimeout(function () { | ||
return _this2.setCaretPosition(el, caretPos); | ||
}, 0); | ||
} | ||
}, { | ||
key: 'formatWithPattern', | ||
@@ -186,5 +277,5 @@ value: function formatWithPattern(str) { | ||
var _getSeparators2 = this.getSeparators(), | ||
thousandSeparator = _getSeparators2.thousandSeparator, | ||
decimalSeparator = _getSeparators2.decimalSeparator; | ||
var _getSeparators3 = this.getSeparators(), | ||
thousandSeparator = _getSeparators3.thousandSeparator, | ||
decimalSeparator = _getSeparators3.decimalSeparator; | ||
@@ -230,27 +321,18 @@ var maskPattern = format && typeof format == 'string' && !!mask; | ||
} else { | ||
var beforeDecimal = formattedValue, | ||
afterDecimal = ''; | ||
var hasDecimals = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision !== false; | ||
if (decimalSeparator && hasDecimals) { | ||
var parts = void 0; | ||
if (decimalPrecision !== false) { | ||
var precision = decimalPrecision === true ? 2 : decimalPrecision; | ||
if (decimalSeparator !== '.') { | ||
// Replace custom decimalSeparator with '.' for parseFloat function | ||
parts = parseFloat(formattedValue.replace(decimalSeparator, '.')).toFixed(precision); | ||
// Put custom decimalSeparator back | ||
parts = parts.replace('.', decimalSeparator); | ||
} else { | ||
parts = parseFloat(formattedValue).toFixed(precision); | ||
} | ||
parts = parts.split(decimalSeparator); | ||
} else { | ||
parts = formattedValue.split(decimalSeparator); | ||
} | ||
beforeDecimal = parts[0]; | ||
afterDecimal = parts[1]; | ||
} | ||
var hasDecimalSeparator = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision; | ||
var parts = formattedValue.split(decimalSeparator); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
//remove leading zeros from number before decimal | ||
beforeDecimal = removeLeadingZero(beforeDecimal); | ||
//apply decimal precision if its defined | ||
if (decimalPrecision !== undefined) afterDecimal = limitToPrecision(afterDecimal, decimalPrecision); | ||
if (thousandSeparator) { | ||
beforeDecimal = beforeDecimal.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousandSeparator); | ||
} | ||
//add prefix and suffix | ||
@@ -262,3 +344,3 @@ if (prefix) beforeDecimal = prefix + beforeDecimal; | ||
formattedValue = beforeDecimal + (hasDecimals && decimalSeparator || '') + afterDecimal; | ||
formattedValue = beforeDecimal + (hasDecimalSeparator && decimalSeparator || '') + afterDecimal; | ||
} | ||
@@ -279,4 +361,6 @@ | ||
j = 0; | ||
for (i = 0; i < cursorPos; i++) { | ||
if (!inputValue[i].match(numRegex) && inputValue[i] !== formattedValue[j]) continue; | ||
if (inputValue[i] === '0' && formattedValue[j].match(numRegex) && formattedValue[j] !== '0') continue; | ||
while (inputValue[i] !== formattedValue[j] && j < formattedValue.length) { | ||
@@ -292,3 +376,3 @@ j++; | ||
value: function onChangeHandler(e, callback) { | ||
var _this2 = this; | ||
var _this3 = this; | ||
@@ -298,22 +382,26 @@ e.persist(); | ||
var inputValue = el.value; | ||
var isAllowed = this.props.isAllowed; | ||
var _formatInput = this.formatInput(inputValue), | ||
formattedValue = _formatInput.formattedValue, | ||
value = _formatInput.value; | ||
var lastValue = this.state.value; | ||
var cursorPos = el.selectionStart; | ||
var _formatInput2 = this.formatInput(inputValue), | ||
formattedValue = _formatInput2.formattedValue, | ||
value = _formatInput2.value; | ||
var cursorPos = this.getCursorPosition(inputValue, formattedValue, el.selectionStart); | ||
//set caret position befor setState | ||
//this.setPatchedCaretPosition(el, cursorPos); | ||
if (!isAllowed(formattedValue, value, this.getFloatValue(value))) { | ||
formattedValue = lastValue; | ||
} | ||
//change the state | ||
this.setState({ value: formattedValue }, function () { | ||
cursorPos = _this2.getCursorPosition(inputValue, formattedValue, cursorPos); | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
_this2.setCaretPosition(el, cursorPos); | ||
setTimeout(function () { | ||
return _this2.setCaretPosition(el, cursorPos); | ||
}, 0); | ||
if (callback) callback(e, value); | ||
//reset again after setState so if formattedValue is other then | ||
_this3.setPatchedCaretPosition(el, cursorPos); | ||
if (callback && formattedValue !== lastValue) callback(e, value); | ||
}); | ||
@@ -336,9 +424,15 @@ | ||
var decimalPrecision = this.props.decimalPrecision; | ||
var key = e.key; | ||
var key = e.key, | ||
which = e.which, | ||
keyCode = e.keyCode; | ||
var numRegex = this.getNumberRegex(false, decimalPrecision !== false); | ||
var numRegex = this.getNumberRegex(false, decimalPrecision !== undefined); | ||
console.log(numRegex.toString(), key, which, keyCode); | ||
console.log(e); | ||
var negativeRegex = new RegExp('-'); | ||
//Handle backspace and delete against non numerical/decimal characters | ||
if (selectionEnd - selectionStart === 0) { | ||
console.log('coming here'); | ||
if (key === 'Delete' && !numRegex.test(value[selectionStart]) && !negativeRegex.test(value[selectionStart])) { | ||
console.log('delete'); | ||
e.preventDefault(); | ||
@@ -348,4 +442,5 @@ var nextCursorPosition = selectionStart; | ||
nextCursorPosition++; | ||
}this.setCaretPosition(el, nextCursorPosition); | ||
}this.setPatchedCaretPosition(el, nextCursorPosition); | ||
} else if (key === 'Backspace' && !numRegex.test(value[selectionStart - 1]) && !negativeRegex.test(value[selectionStart - 1])) { | ||
console.log('Backspace'); | ||
e.preventDefault(); | ||
@@ -355,3 +450,3 @@ var prevCursorPosition = selectionStart; | ||
prevCursorPosition--; | ||
}this.setCaretPosition(el, prevCursorPosition); | ||
}this.setPatchedCaretPosition(el, prevCursorPosition); | ||
} | ||
@@ -358,0 +453,0 @@ } |
{ | ||
"name": "react-number-format", | ||
"description": "React component to format number in an input or as a text.", | ||
"version": "1.2.1", | ||
"version": "2.0.0-alpha", | ||
"main": "lib/number_format.js", | ||
@@ -6,0 +6,0 @@ "author": "Sudhanshu Yadav", |
@@ -1,2 +0,13 @@ | ||
//const React = require('react'); | ||
/** | ||
* 1. Validate thousand separators and decimals throw error | ||
* 2. Thousand separator just have value true or any other string | ||
* 3. Decimal separator should be defined only as string | ||
* 4. Decimal precision should be only defined as number | ||
* 5. If user don't want floating numbers set decimalPrecision to 0 | ||
* 6. User can pass value as floating point numbers or string, if user passes string decimal separator in string should match to provided decimalSeparator | ||
* 7. Add formattedValue, numeric value, value with string in event object and not as parameters so that getting values should look consistent | ||
* 8. dont use parseFloat that will not able to parse 2^23 | ||
* 9. Always have decimal precision | ||
* 10. isAllowed props to validate input and block if returns false | ||
*/ | ||
import PropTypes from 'prop-types'; | ||
@@ -9,6 +20,19 @@ import React from 'react'; | ||
function removeLeadingZero(numStr) { | ||
//remove leading zeros | ||
return numStr.replace(/^0+/,'') || '0'; | ||
} | ||
function limitToPrecision(numStr, precision) { | ||
let str = '' | ||
for (let i=0; i<=precision - 1; i++) { | ||
str += numStr[i] || '0' | ||
} | ||
return str; | ||
} | ||
const propTypes = { | ||
thousandSeparator: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), | ||
decimalSeparator: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), | ||
decimalPrecision: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), | ||
thousandSeparator: PropTypes.oneOfType([PropTypes.string, PropTypes.oneOf([true])]), | ||
decimalSeparator: PropTypes.string, | ||
decimalPrecision: PropTypes.number, | ||
displayType: PropTypes.oneOf(['input', 'text']), | ||
@@ -29,3 +53,4 @@ prefix: PropTypes.string, | ||
onKeyDown: PropTypes.func, | ||
onChange: PropTypes.func | ||
onChange: PropTypes.func, | ||
isAllowed: PropTypes.func | ||
}; | ||
@@ -36,4 +61,4 @@ | ||
decimalSeparator: '.', | ||
decimalPrecision: false, | ||
allowNegative: true | ||
allowNegative: true, | ||
isAllowed: function() {return true;} | ||
}; | ||
@@ -44,4 +69,5 @@ | ||
super(props); | ||
const value = this.optimizeValueProp(props); | ||
this.state = { | ||
value: this.formatInput(props.value).formattedValue | ||
value: this.formatInput(value).formattedValue | ||
} | ||
@@ -52,26 +78,63 @@ this.onChange = this.onChange.bind(this); | ||
componentWillReceiveProps(newProps) { | ||
if(newProps.value !== this.props.value) { | ||
this.setState({ | ||
value : this.formatInput(newProps.value).formattedValue | ||
}); | ||
} | ||
componentDidUpdate(prevProps, prevState) { | ||
this.updateValueIfRequired(prevProps, prevState); | ||
} | ||
getSeparators() { | ||
let {thousandSeparator, decimalSeparator} = this.props; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ',' | ||
updateValueIfRequired(prevProps) { | ||
const {props, state} = this; | ||
if(prevProps !== props) { | ||
const stateValue = state.value; | ||
let value = this.optimizeValueProp(props); | ||
if (value === undefined) value = stateValue; | ||
const {formattedValue} = this.formatInput(value); | ||
if (formattedValue !== stateValue) { | ||
this.setState({ | ||
value : this.formatInput(value).formattedValue | ||
}) | ||
} | ||
} | ||
} | ||
if (decimalSeparator && thousandSeparator && typeof decimalSeparator !== 'string') { | ||
decimalSeparator = thousandSeparator === '.' ? ',' : '.'; | ||
getFloatValue(num) { | ||
const {decimalSeparator} = this.props; | ||
return parseFloat(num.replace(decimalSeparator, '.')) || 0; | ||
} | ||
optimizeValueProp(props) { | ||
const {decimalSeparator} = this.getSeparators(props); | ||
let {value, decimalPrecision, format} = props; | ||
if (format || value === undefined) return value; | ||
const isNumber = typeof value === 'number'; | ||
if (isNumber) value = value.toString(); | ||
//correct decimal separator | ||
if (decimalSeparator && isNumber) { | ||
value = value.replace('.', decimalSeparator); | ||
} | ||
if (thousandSeparator === '.') { | ||
decimalSeparator = ','; | ||
//if decimalPrecision is 0 remove decimalNumbers | ||
if (decimalPrecision === 0) return value.split(decimalSeparator)[0] | ||
return value; | ||
} | ||
getSeparators(props) { | ||
let {thousandSeparator, decimalSeparator, decimalPrecision} = props || this.props; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ',' | ||
} | ||
if (decimalSeparator === true) { | ||
decimalSeparator = '.' | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error(` | ||
Decimal separator can\'t be same as thousand separator.\n | ||
thousandSeparator: ${thousandSeparator} (thousandSeparator = {true} is same as thousandSeparator = ",") | ||
decimalSeparator: ${decimalSeparator} (default value for decimalSeparator is .) | ||
`); | ||
} | ||
@@ -85,6 +148,6 @@ | ||
getNumberRegex(g, ignoreDecimalSeperator) { | ||
const {format} = this.props; | ||
getNumberRegex(g, ignoreDecimalSeparator) { | ||
const {format, decimalPrecision} = this.props; | ||
const {decimalSeparator} = this.getSeparators(); | ||
return new RegExp('\\d' + (decimalSeparator && !ignoreDecimalSeperator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
return new RegExp('\\d' + (decimalSeparator && decimalPrecision !== 0 && !ignoreDecimalSeparator && !format ? '|' + escapeRegExp(decimalSeparator) : ''), g ? 'g' : undefined); | ||
} | ||
@@ -117,2 +180,12 @@ | ||
setPatchedCaretPosition(el, caretPos) { | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
this.setCaretPosition(el, caretPos); | ||
setTimeout(() => this.setCaretPosition(el, caretPos), 0); | ||
} | ||
formatWithPattern(str) { | ||
@@ -161,9 +234,9 @@ const {format,mask} = this.props; | ||
const valMatch = val && val.match(numRegex); | ||
if (!valMatch && removeNegative) { | ||
return {value :'', formattedValue: ''} | ||
return {value :'', formattedValue: ''} | ||
} else if (!valMatch && hasNegative) { | ||
return {value :'', formattedValue: '-'} | ||
return {value :'', formattedValue: '-'} | ||
} else if (!valMatch) { | ||
return {value :'', formattedValue: (maskPattern ? '' : '')} | ||
return {value :'', formattedValue: (maskPattern ? '' : '')} | ||
} | ||
@@ -184,26 +257,18 @@ | ||
else{ | ||
let beforeDecimal = formattedValue, afterDecimal = ''; | ||
const hasDecimals = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision !== false; | ||
if(decimalSeparator && hasDecimals) { | ||
let parts; | ||
if (decimalPrecision !== false) { | ||
const precision = decimalPrecision === true ? 2 : decimalPrecision; | ||
if (decimalSeparator !== '.') { | ||
// Replace custom decimalSeparator with '.' for parseFloat function | ||
parts = parseFloat(formattedValue.replace(decimalSeparator, '.')).toFixed(precision) | ||
// Put custom decimalSeparator back | ||
parts = parts.replace('.', decimalSeparator); | ||
} else { | ||
parts = parseFloat(formattedValue).toFixed(precision) | ||
} | ||
parts = parts.split(decimalSeparator); | ||
} else { | ||
parts = formattedValue.split(decimalSeparator); | ||
} | ||
beforeDecimal = parts[0]; | ||
afterDecimal = parts[1]; | ||
} | ||
const hasDecimalSeparator = formattedValue.indexOf(decimalSeparator) !== -1 || decimalPrecision; | ||
const parts = formattedValue.split(decimalSeparator); | ||
let beforeDecimal = parts[0]; | ||
let afterDecimal = parts[1] || ''; | ||
//remove leading zeros from number before decimal | ||
beforeDecimal = removeLeadingZero(beforeDecimal); | ||
//apply decimal precision if its defined | ||
if (decimalPrecision !== undefined) afterDecimal = limitToPrecision(afterDecimal, decimalPrecision); | ||
if(thousandSeparator) { | ||
beforeDecimal = beforeDecimal.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousandSeparator); | ||
} | ||
//add prefix and suffix | ||
@@ -215,3 +280,3 @@ if(prefix) beforeDecimal = prefix + beforeDecimal; | ||
formattedValue = beforeDecimal + (hasDecimals && decimalSeparator || '') + afterDecimal; | ||
formattedValue = beforeDecimal + (hasDecimalSeparator && decimalSeparator || '') + afterDecimal; | ||
} | ||
@@ -230,4 +295,6 @@ | ||
j=0; | ||
for(i=0; i<cursorPos; i++){ | ||
if(!inputValue[i].match(numRegex) && inputValue[i] !== formattedValue[j]) continue; | ||
if (inputValue[i] === '0' && formattedValue[j].match(numRegex) && formattedValue[j] !== '0') continue; | ||
while(inputValue[i] !== formattedValue[j] && j<formattedValue.length) j++; | ||
@@ -244,16 +311,21 @@ j++; | ||
const inputValue = el.value; | ||
const {formattedValue,value} = this.formatInput(inputValue); | ||
let cursorPos = el.selectionStart; | ||
const {isAllowed} = this.props; | ||
const lastValue = this.state.value; | ||
let {formattedValue, value} = this.formatInput(inputValue); | ||
const cursorPos = this.getCursorPosition(inputValue, formattedValue, el.selectionStart); | ||
//set caret position befor setState | ||
//this.setPatchedCaretPosition(el, cursorPos); | ||
if (!isAllowed(formattedValue, value, this.getFloatValue(value))) { | ||
formattedValue = lastValue; | ||
} | ||
//change the state | ||
this.setState({value : formattedValue},()=>{ | ||
cursorPos = this.getCursorPosition(inputValue, formattedValue, cursorPos ); | ||
/* | ||
setting caret position within timeout of 0ms is required for mobile chrome, | ||
otherwise browser resets the caret position after we set it | ||
We are also setting it without timeout so that in normal browser we don't see the flickering | ||
*/ | ||
this.setCaretPosition(el, cursorPos); | ||
setTimeout(() => this.setCaretPosition(el, cursorPos), 0); | ||
if(callback) callback(e,value); | ||
//reset again after setState so if formattedValue is other then | ||
this.setPatchedCaretPosition(el, cursorPos); | ||
if(callback && formattedValue !== lastValue) callback(e, value); | ||
}); | ||
@@ -271,17 +343,22 @@ | ||
const {decimalPrecision} = this.props; | ||
const {key} = e; | ||
const numRegex = this.getNumberRegex(false, decimalPrecision !== false); | ||
const {key, which, keyCode} = e; | ||
const numRegex = this.getNumberRegex(false, decimalPrecision !== undefined); | ||
console.log(numRegex.toString(), key, which, keyCode); | ||
console.log(e); | ||
const negativeRegex = new RegExp('-'); | ||
//Handle backspace and delete against non numerical/decimal characters | ||
if(selectionEnd - selectionStart === 0) { | ||
console.log('coming here'); | ||
if (key === 'Delete' && !numRegex.test(value[selectionStart]) && !negativeRegex.test(value[selectionStart])) { | ||
console.log('delete'); | ||
e.preventDefault(); | ||
let nextCursorPosition = selectionStart; | ||
while (!numRegex.test(value[nextCursorPosition]) && nextCursorPosition < value.length) nextCursorPosition++; | ||
this.setCaretPosition(el, nextCursorPosition); | ||
this.setPatchedCaretPosition(el, nextCursorPosition); | ||
} else if (key === 'Backspace' && !numRegex.test(value[selectionStart - 1]) && !negativeRegex.test(value[selectionStart-1])) { | ||
console.log('Backspace'); | ||
e.preventDefault(); | ||
let prevCursorPosition = selectionStart; | ||
while (!numRegex.test(value[prevCursorPosition - 1]) && prevCursorPosition > 0) prevCursorPosition--; | ||
this.setCaretPosition(el, prevCursorPosition); | ||
this.setPatchedCaretPosition(el, prevCursorPosition); | ||
} | ||
@@ -330,2 +407,2 @@ } | ||
module.exports = NumberFormat; | ||
module.exports = NumberFormat; |
@@ -76,3 +76,3 @@ import React from 'react'; | ||
const wrapper = shallow(<NumberFormat thousandSeparator={true} prefix={'$'} />); | ||
wrapper.find('input').simulate('change', getCustomEvent('-2456981.89')); | ||
@@ -84,3 +84,3 @@ | ||
expect(wrapper.state().value).toEqual('-'); | ||
}); | ||
@@ -90,3 +90,3 @@ | ||
const wrapper = shallow(<NumberFormat format="#### #### #### ####" />); | ||
wrapper.find('input').simulate('change', getCustomEvent('-2456981')); | ||
@@ -99,3 +99,3 @@ | ||
const wrapper = shallow(<NumberFormat thousandSeparator={true} prefix={'$'} />); | ||
wrapper.find('input').simulate('change', getCustomEvent('--2456981.89')); | ||
@@ -107,3 +107,3 @@ | ||
expect(wrapper.state().value).toEqual(''); | ||
}); | ||
@@ -113,3 +113,3 @@ | ||
const wrapper = shallow(<NumberFormat thousandSeparator={true} prefix={'$'}/>); | ||
wrapper.find('input').simulate('change', getCustomEvent('24569-81.89')); | ||
@@ -150,2 +150,12 @@ | ||
it('should throw error when decimal separator and thousand separator are same', () => { | ||
expect(() => { | ||
shallow(<NumberFormat thousandSeparator={'.'} prefix={'$'} />); | ||
}).toThrow() | ||
expect(() => { | ||
shallow(<NumberFormat thousandSeparator={','} decimalSeparator={','} prefix={'$'} />); | ||
}).toThrow() | ||
}); | ||
it('should support decimal precision with custom decimal separator', () => { | ||
@@ -159,2 +169,41 @@ const wrapper = shallow(<NumberFormat thousandSeparator={'.'} decimalSeparator={','} decimalPrecision={2} />); | ||
it('should allow floating/integer numbers as values and do proper formatting', () => { | ||
const wrapper = shallow(<NumberFormat value={12345.67} />, { lifecycleExperimental: true }); | ||
expect(wrapper.state().value).toEqual('12345.67'); | ||
wrapper.setProps({thousandSeparator: true}); | ||
expect(wrapper.state().value).toEqual('12,345.67'); | ||
wrapper.setProps({thousandSeparator: '.', decimalSeparator: ','}); | ||
expect(wrapper.state().value).toEqual('12.345,67'); | ||
wrapper.setProps({thousandSeparator: '.', decimalSeparator: ',', decimalPrecision: 0}); | ||
expect(wrapper.state().value).toEqual('12.345'); | ||
}); | ||
it('should update formatted value if any of the props changes', () => { | ||
const wrapper = shallow(<NumberFormat value={12345.67} />, { lifecycleExperimental: true }); | ||
expect(wrapper.state().value).toEqual('12345.67'); | ||
wrapper.setProps({thousandSeparator: true}); | ||
expect(wrapper.state().value).toEqual('12,345.67'); | ||
wrapper.setProps({thousandSeparator: '.', decimalSeparator: ','}); | ||
expect(wrapper.state().value).toEqual('12.345,67'); | ||
wrapper.setProps({thousandSeparator: '.', decimalSeparator: ',', decimalPrecision: 0}); | ||
expect(wrapper.state().value).toEqual('12.345'); | ||
}); | ||
it('should allow bigger number than 2^53 and do proper formatting', () => { | ||
const wrapper = shallow(<NumberFormat thousandSeparator="." decimalSeparator="," />); | ||
const input = wrapper.find('input'); | ||
input.simulate('change', getCustomEvent('981273724234817383478127')); | ||
expect(wrapper.state().value).toEqual('981.273.724.234.817.383.478.127'); | ||
input.simulate('change', getCustomEvent('981273724234817383478,127')); | ||
expect(wrapper.state().value).toEqual('981.273.724.234.817.383.478,127'); | ||
}) | ||
it('should have proper intermediate formatting', () => { | ||
@@ -252,3 +301,17 @@ const wrapper = shallow(<NumberFormat format="#### #### #### ####" />); | ||
it('sould not allow decimal numbers if decimal precision is set to 0', () => { | ||
const wrapper = shallow(<NumberFormat thousandSeparator={true} decimalPrecision={0}/>, {lifecycleExperimental: true}); | ||
const input = wrapper.find('input'); | ||
//case 1 - decimal precision set to 0 | ||
input.simulate('change', getCustomEvent('4111.')); | ||
expect(wrapper.state().value).toEqual('4,111'); | ||
//case 2 - It should remove decimal values if passed value props as decimal values | ||
wrapper.setProps({value: 1234.78}); | ||
expect(wrapper.state().value).toEqual('1,234'); | ||
}) | ||
it('should not round by default', () => { | ||
@@ -287,3 +350,3 @@ const wrapper = shallow(<NumberFormat/>); | ||
wrapper.setProps({format: '#### #### #### ####', mask: '_', thousandSeparator: false, decimalSeparator: false}); | ||
wrapper.setProps({format: '#### #### #### ####', mask: '_'}); | ||
input.simulate('change', getCustomEvent('41111 1')); | ||
@@ -330,3 +393,3 @@ expect(input.get(0).value).toEqual('4111 11__ ____ ____'); | ||
it('should round to 2 decimals if passed true', () => { | ||
const wrapper = shallow(<NumberFormat value="4111" displayType={'text'} decimalPrecision={true} />); | ||
const wrapper = shallow(<NumberFormat value="4111" displayType={'text'} decimalPrecision={2} />); | ||
expect(wrapper.find('span').text()).toEqual('4111.00'); | ||
@@ -333,0 +396,0 @@ }); |
@@ -7,3 +7,3 @@ module.exports = { | ||
}, | ||
devtool: "eval", | ||
devtool: "cheap-module-eval-source-map", | ||
debug: true, | ||
@@ -10,0 +10,0 @@ output: { |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
128320
2234
3