react-number-format
Advanced tools
Comparing version 4.9.3 to 5.0.0-beta.1
/** | ||
* react-number-format - 4.9.3 | ||
* react-number-format - 5.0.0-beta.1 | ||
* Author : Sudhanshu Yadav | ||
@@ -10,85 +10,118 @@ * Copyright (c) 2016, 2022 to Sudhanshu Yadav, released under the MIT license. | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var React = _interopDefault(require('react')); | ||
var React = require('react'); | ||
var React__default = _interopDefault(React); | ||
// | ||
/****************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
// basic noop function | ||
function noop() {} | ||
function returnTrue() { | ||
return true; | ||
} | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function charIsNumber(char ) { | ||
return !!(char || '').match(/\d/); | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
{ t[p] = s[p]; } } | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
{ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
{ t[p[i]] = s[p[i]]; } | ||
} } | ||
return t; | ||
} | ||
function isNil(val ) { | ||
return val === null || val === undefined; | ||
} | ||
var SourceType; | ||
(function (SourceType) { | ||
SourceType["event"] = "event"; | ||
SourceType["props"] = "prop"; | ||
})(SourceType || (SourceType = {})); | ||
function escapeRegExp(str ) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
// basic noop function | ||
function noop() { } | ||
function charIsNumber(char) { | ||
return !!(char || '').match(/\d/); | ||
} | ||
function getThousandsGroupRegex(thousandsGroupStyle ) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
} | ||
function isNil(val) { | ||
return val === null || val === undefined; | ||
} | ||
function isNanValue(val) { | ||
return typeof val === 'number' && isNaN(val); | ||
} | ||
function escapeRegExp(str) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
} | ||
function getThousandsGroupRegex(thousandsGroupStyle) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
} | ||
} | ||
function applyThousandSeparator(str, thousandSeparator, thousandsGroupStyle) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return (str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator)); | ||
} | ||
function usePersistentCallback(cb) { | ||
var callbackRef = React.useRef(cb); | ||
// keep the callback ref upto date | ||
callbackRef.current = cb; | ||
/** | ||
* initialize a persistent callback which never changes | ||
* through out the component lifecycle | ||
*/ | ||
var persistentCbRef = React.useRef(function () { | ||
var args = [], len = arguments.length; | ||
while ( len-- ) args[ len ] = arguments[ len ]; | ||
function applyThousandSeparator( | ||
str , | ||
thousandSeparator , | ||
thousandsGroupStyle | ||
) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return ( | ||
str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator) | ||
); | ||
return callbackRef.current.apply(callbackRef, args); | ||
}); | ||
return persistentCbRef.current; | ||
} | ||
//spilt a float number into different parts beforeDecimal, afterDecimal, and negation | ||
function splitDecimal(numStr , allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
function splitDecimal(numStr, allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
var hasNagation = numStr[0] === '-'; | ||
var addNegation = hasNagation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNagation: hasNagation, | ||
addNegation: addNegation, | ||
}; | ||
var hasNegation = numStr[0] === '-'; | ||
var addNegation = hasNegation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNegation: hasNegation, | ||
addNegation: addNegation, | ||
}; | ||
} | ||
function fixLeadingZero(numStr ) { | ||
if (!numStr) { return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) { numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
function fixLeadingZero(numStr) { | ||
if (!numStr) | ||
{ return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) | ||
{ numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
} | ||
/** | ||
@@ -98,59 +131,51 @@ * limit decimal numbers to given scale | ||
*/ | ||
function limitToScale(numStr , scale , fixedDecimalScale ) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
} | ||
return str; | ||
function limitToScale(numStr, scale, fixedDecimalScale) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
} | ||
return str; | ||
} | ||
function repeat(str, count) { | ||
return Array(count + 1).join(str); | ||
return Array(count + 1).join(str); | ||
} | ||
function toNumericString(num) { | ||
num += ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = num[0] === '-' ? '-' : ''; | ||
if (sign) { num = num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) { return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
} | ||
return sign + coefficient; | ||
var _num = num + ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = _num[0] === '-' ? '-' : ''; | ||
if (sign) | ||
{ _num = _num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = _num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) | ||
{ return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} | ||
else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} | ||
else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
} | ||
return sign + coefficient; | ||
} | ||
/** | ||
@@ -160,1232 +185,1000 @@ * This method is required to round prop value to given scale. | ||
*/ | ||
function roundToPrecision(numStr , scale , fixedDecimalScale ) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) { return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNagation = ref.hasNagation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = | ||
afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ( | ||
(Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length) | ||
); | ||
} | ||
return current + roundedStr; | ||
function roundToPrecision(numStr, scale, fixedDecimalScale) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) | ||
{ return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNegation = ref.hasNegation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ((Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length)); | ||
} | ||
return current + roundedStr; | ||
}, roundedDecimalParts[0]); | ||
var decimalPart = limitToScale( | ||
roundedDecimalParts[1] || '', | ||
Math.min(scale, afterDecimal.length), | ||
fixedDecimalScale | ||
); | ||
var negation = hasNagation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
var decimalPart = limitToScale(roundedDecimalParts[1] || '', scale, fixedDecimalScale); | ||
var negation = hasNegation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
} | ||
/** set the caret positon in an input field **/ | ||
function setCaretPosition(el , caretPos ) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
if (el.createTextRange) { | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
function setCaretPosition(el, caretPos) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
/* @ts-ignore */ | ||
if (el.createTextRange) { | ||
/* @ts-ignore */ | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
function findChangeRange(prevValue, newValue) { | ||
var i = 0, j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) | ||
{ i++; } | ||
//check what has been changed from last | ||
while (prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i) { | ||
j++; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
} | ||
return { | ||
from: { start: i, end: prevLength - j }, | ||
to: { start: i, end: newLength - j }, | ||
}; | ||
} | ||
/** | ||
Given previous value and newValue it returns the index | ||
start - end to which values have changed. | ||
This function makes assumption about only consecutive | ||
characters are changed which is correct assumption for caret input. | ||
*/ | ||
function findChangedIndex(prevValue , newValue ) { | ||
var i = 0, | ||
j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) { i++; } | ||
//check what has been changed from last | ||
while ( | ||
prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i | ||
) { | ||
j++; | ||
} | ||
return { start: i, end: prevLength - j }; | ||
} | ||
/* | ||
Returns a number whose value is limited to the given range | ||
*/ | ||
function clamp(num , min , max ) { | ||
return Math.min(Math.max(num, min), max); | ||
function clamp(num, min, max) { | ||
return Math.min(Math.max(num, min), max); | ||
} | ||
function getCurrentCaretPosition(el ) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
function geInputCaretPosition(el) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
} | ||
function addInputMode(format ) { | ||
return ( | ||
format || | ||
(typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))) | ||
); | ||
function addInputMode() { | ||
return (typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))); | ||
} | ||
// | ||
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } | ||
var defaultProps = { | ||
displayType: 'input', | ||
decimalSeparator: '.', | ||
thousandsGroupStyle: 'thousand', | ||
fixedDecimalScale: false, | ||
prefix: '', | ||
suffix: '', | ||
allowNegative: true, | ||
allowEmptyFormatting: false, | ||
allowLeadingZeros: false, | ||
isNumericString: false, | ||
type: 'text', | ||
onValueChange: noop, | ||
onChange: noop, | ||
onKeyDown: noop, | ||
onMouseUp: noop, | ||
onFocus: noop, | ||
onBlur: noop, | ||
isAllowed: returnTrue, | ||
}; | ||
var NumberFormat = /*@__PURE__*/(function (superclass) { | ||
function NumberFormat(props ) { | ||
superclass.call(this, props); | ||
var defaultValue = props.defaultValue; | ||
//validate props | ||
this.validateProps(); | ||
var formattedValue = this.formatValueProp(defaultValue); | ||
this.state = { | ||
value: formattedValue, | ||
numAsString: this.removeFormatting(formattedValue), | ||
mounted: false, | ||
}; | ||
this.selectionBeforeInput = { | ||
selectionStart: 0, | ||
selectionEnd: 0, | ||
}; | ||
this.onChange = this.onChange.bind(this); | ||
this.onKeyDown = this.onKeyDown.bind(this); | ||
this.onMouseUp = this.onMouseUp.bind(this); | ||
this.onFocus = this.onFocus.bind(this); | ||
this.onBlur = this.onBlur.bind(this); | ||
} | ||
if ( superclass ) NumberFormat.__proto__ = superclass; | ||
NumberFormat.prototype = Object.create( superclass && superclass.prototype ); | ||
NumberFormat.prototype.constructor = NumberFormat; | ||
NumberFormat.prototype.componentDidMount = function componentDidMount () { | ||
// set mounted state | ||
// eslint-disable-next-line react/no-did-mount-set-state | ||
this.setState({ | ||
mounted: true, | ||
}); | ||
}; | ||
NumberFormat.prototype.componentDidUpdate = function componentDidUpdate (prevProps ) { | ||
this.updateValueIfRequired(prevProps); | ||
}; | ||
NumberFormat.prototype.componentWillUnmount = function componentWillUnmount () { | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
}; | ||
NumberFormat.prototype.updateValueIfRequired = function updateValueIfRequired (prevProps ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var focusedElm = ref.focusedElm; | ||
var stateValue = state.value; | ||
var lastNumStr = state.numAsString; if ( lastNumStr === void 0 ) lastNumStr = ''; | ||
// If only state changed no need to do any thing | ||
if (prevProps !== props) { | ||
//validate props | ||
this.validateProps(); | ||
var lastValueWithNewFormat = this.formatNumString(lastNumStr); | ||
var formattedValue = isNil(props.value) ? lastValueWithNewFormat : this.formatValueProp(); | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var floatValue = parseFloat(numAsString); | ||
var lastFloatValue = parseFloat(lastNumStr); | ||
if ( | ||
//while typing set state only when float value changes | ||
((!isNaN(floatValue) || !isNaN(lastFloatValue)) && floatValue !== lastFloatValue) || | ||
//can also set state when float value is same and the format props changes | ||
lastValueWithNewFormat !== stateValue || | ||
//set state always when not in focus and formatted value is changed | ||
(focusedElm === null && formattedValue !== stateValue) | ||
) { | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: focusedElm, | ||
source: 'prop', | ||
event: null, | ||
}); | ||
} | ||
} | ||
}; | ||
/** Misc methods **/ | ||
NumberFormat.prototype.getFloatString = function getFloatString (num) { | ||
if ( num === void 0 ) num = ''; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var numRegex = this.getNumberRegex(true); | ||
//remove negation for regex check | ||
var hasNegation = num[0] === '-'; | ||
if (hasNegation) { num = num.replace('-', ''); } | ||
//if decimal scale is zero remove decimal and number after decimalSeparator | ||
if (decimalSeparator && decimalScale === 0) { | ||
num = num.split(decimalSeparator)[0]; | ||
} | ||
num = (num.match(numRegex) || []).join('').replace(decimalSeparator, '.'); | ||
//remove extra decimals | ||
var firstDecimalIndex = num.indexOf('.'); | ||
if (firstDecimalIndex !== -1) { | ||
num = (num.substring(0, firstDecimalIndex)) + "." + (num | ||
.substring(firstDecimalIndex + 1, num.length) | ||
.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), '')); | ||
} | ||
//add negation back | ||
if (hasNegation) { num = '-' + num; } | ||
return num; | ||
}; | ||
//returned regex assumes decimalSeparator is as per prop | ||
NumberFormat.prototype.getNumberRegex = function getNumberRegex (g , ignoreDecimalSeparator ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var customNumerals = ref.customNumerals; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
return new RegExp( | ||
'[0-9' + | ||
(customNumerals ? customNumerals.join('') : '') + | ||
']' + | ||
(decimalSeparator && decimalScale !== 0 && !ignoreDecimalSeparator && !format | ||
? '|' + escapeRegExp(decimalSeparator) | ||
: ''), | ||
g ? 'g' : undefined | ||
); | ||
}; | ||
NumberFormat.prototype.getSeparators = function getSeparators () { | ||
var ref = this.props; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var ref$1 = this.props; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
function getDefaultChangeMeta(value) { | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
from: { | ||
start: 0, | ||
end: 0, | ||
}, | ||
to: { | ||
start: 0, | ||
end: value.length, | ||
}, | ||
lastValue: '', | ||
}; | ||
}; | ||
} | ||
function getMaskAtIndex(mask, index) { | ||
if ( mask === void 0 ) mask = ' '; | ||
NumberFormat.prototype.getMaskAtIndex = function getMaskAtIndex (index ) { | ||
var ref = this.props; | ||
var mask = ref.mask; if ( mask === void 0 ) mask = ' '; | ||
if (typeof mask === 'string') { | ||
return mask; | ||
return mask; | ||
} | ||
return mask[index] || ' '; | ||
}; | ||
NumberFormat.prototype.getValueObject = function getValueObject (formattedValue , numAsString ) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
NumberFormat.prototype.validateProps = function validateProps () { | ||
var ref = this.props; | ||
var mask = ref.mask; | ||
//validate decimalSeparator and thousandSeparator | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
function getCaretPosition(formattedValue, curValue, curCaretPos) { | ||
var curValLn = curValue.length; | ||
var formattedValueLn = formattedValue.length; | ||
// create index map | ||
var addedIndexMap = {}; | ||
var indexMap = new Array(curValLn); | ||
for (var i = 0; i < curValLn; i++) { | ||
indexMap[i] = -1; | ||
for (var j = 0, jLn = formattedValueLn; j < jLn; j++) { | ||
if (curValue[i] === formattedValue[j] && addedIndexMap[j] !== true) { | ||
indexMap[i] = j; | ||
addedIndexMap[j] = true; | ||
break; | ||
} | ||
} | ||
} | ||
//validate mask | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("\n Mask " + mask + " should not contain numeric character;\n ")); | ||
} | ||
/** | ||
* For current caret position find closest characters (left and right side) | ||
* which are properly mapped to formatted value. | ||
* The idea is that the new caret position will exist always in the boundary of | ||
* that mapped index | ||
*/ | ||
var pos = curCaretPos; | ||
while (pos < curValLn && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) { | ||
pos++; | ||
} | ||
}; | ||
/** Misc methods end **/ | ||
/** caret specific methods **/ | ||
NumberFormat.prototype.setPatchedCaretPosition = function setPatchedCaretPosition (el , caretPos , currentValue ) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
this.caretPositionTimeout = setTimeout(function () { | ||
if (el.value === currentValue) { setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
NumberFormat.prototype.correctCaretPosition = function correctCaretPosition (value , caretPos , direction ) { | ||
var ref = this.props; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
//if value is empty return 0 | ||
if (value === '') { return 0; } | ||
//caret position should be between 0 and value length | ||
caretPos = clamp(caretPos, 0, value.length); | ||
//in case of format as number limit between prefix and suffix | ||
if (!format) { | ||
var hasNegation = value[0] === '-'; | ||
return clamp(caretPos, prefix.length + (hasNegation ? 1 : 0), value.length - suffix.length); | ||
// if the caret position is on last keep the endIndex as last for formatted value | ||
var endIndex = pos === curValLn || indexMap[pos] === -1 ? formattedValueLn : indexMap[pos]; | ||
pos = curCaretPos - 1; | ||
while (pos > 0 && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) | ||
{ pos--; } | ||
var startIndex = pos === -1 || indexMap[pos] === -1 ? 0 : indexMap[pos] + 1; | ||
/** | ||
* case where a char is added on suffix and removed from middle, example 2sq345 becoming $2,345 sq | ||
* there is still a mapping but the order of start index and end index is changed | ||
*/ | ||
if (startIndex > endIndex) | ||
{ return endIndex; } | ||
/** | ||
* given the current caret position if it closer to startIndex | ||
* keep the new caret position on start index or keep it closer to endIndex | ||
*/ | ||
return curCaretPos - startIndex < endIndex - curCaretPos ? startIndex : endIndex; | ||
} | ||
function caretUnknownFormatBoundary(formattedValue) { | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
boundaryAry[i] = Boolean(charIsNumber(formattedValue[i]) || charIsNumber(formattedValue[i - 1])); | ||
} | ||
return boundaryAry; | ||
} | ||
function useInternalValues(value, defaultValue, isNumericString, format, removeFormatting, onValueChange) { | ||
if ( onValueChange === void 0 ) onValueChange = noop; | ||
//in case if custom format method don't do anything | ||
if (typeof format === 'function') { return caretPos; } | ||
var propValues = React.useRef(); | ||
var getValues = usePersistentCallback(function (value) { | ||
var formattedValue, numAsString; | ||
if (isNil(value) || isNanValue(value)) { | ||
numAsString = ''; | ||
formattedValue = ''; | ||
} | ||
else if (typeof value === 'number' || isNumericString) { | ||
numAsString = typeof value === 'number' ? toNumericString(value) : value; | ||
formattedValue = format(numAsString); | ||
} | ||
else { | ||
numAsString = removeFormatting(value, undefined); | ||
formattedValue = value; | ||
} | ||
return { formattedValue: formattedValue, numAsString: numAsString }; | ||
}); | ||
var ref = React.useState(function () { | ||
return getValues(defaultValue); | ||
}); | ||
var values = ref[0]; | ||
var setValues = ref[1]; | ||
var _onValueChange = function (values, sourceInfo) { | ||
setValues({ | ||
formattedValue: values.formattedValue, | ||
numAsString: values.value, | ||
}); | ||
onValueChange(values, sourceInfo); | ||
}; | ||
React.useMemo(function () { | ||
//if element is moved to uncontrolled mode, don't reset the value | ||
if (!isNil(value)) { | ||
propValues.current = getValues(value); | ||
setValues(propValues.current); | ||
} | ||
else { | ||
propValues.current = undefined; | ||
} | ||
}, [value, getValues]); | ||
return [values, _onValueChange]; | ||
} | ||
/* in case format is string find the closest # position from the caret position */ | ||
//in case the caretPos have input value on it don't do anything | ||
if (format[caretPos] === '#' && charIsNumber(value[caretPos])) { | ||
return caretPos; | ||
function defaultRemoveFormatting(value) { | ||
return value.replace(/[^0-9]/g, ''); | ||
} | ||
function defaultFormat(value) { | ||
return value; | ||
} | ||
function NumberFormatBase(props) { | ||
var type = props.type; if ( type === void 0 ) type = 'text'; | ||
var displayType = props.displayType; if ( displayType === void 0 ) displayType = 'input'; | ||
var customInput = props.customInput; | ||
var renderText = props.renderText; | ||
var getInputRef = props.getInputRef; | ||
var format = props.format; if ( format === void 0 ) format = defaultFormat; | ||
var removeFormatting = props.removeFormatting; if ( removeFormatting === void 0 ) removeFormatting = defaultRemoveFormatting; | ||
var defaultValue = props.defaultValue; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var isAllowed = props.isAllowed; | ||
var onChange = props.onChange; if ( onChange === void 0 ) onChange = noop; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onMouseUp = props.onMouseUp; if ( onMouseUp === void 0 ) onMouseUp = noop; | ||
var onFocus = props.onFocus; if ( onFocus === void 0 ) onFocus = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var propValue = props.value; | ||
var getCaretBoundary = props.getCaretBoundary; if ( getCaretBoundary === void 0 ) getCaretBoundary = caretUnknownFormatBoundary; | ||
var otherProps = __rest(props, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "removeFormatting", "defaultValue", "isNumericString", "onValueChange", "isAllowed", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value", "getCaretBoundary"]); | ||
var ref = useInternalValues(propValue, defaultValue, isNumericString, format, removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var formattedValue = ref_0.formattedValue; | ||
var numAsString = ref_0.numAsString; | ||
var onFormattedValueChange = ref[1]; | ||
var lastUpdatedValue = React.useRef(); | ||
var _onValueChange = function (values, source) { | ||
lastUpdatedValue.current = values.formattedValue; | ||
onFormattedValueChange(values, source); | ||
}; | ||
// check if there is any change in the value due to props change | ||
React.useEffect(function () { | ||
var newFormattedValue = format(numAsString); | ||
// if the formatted value is not synced to parent, or if the formatted value is different | ||
if (lastUpdatedValue.current === undefined || newFormattedValue !== lastUpdatedValue.current) { | ||
var input = focusedElm.current; | ||
updateValue({ | ||
formattedValue: newFormattedValue, | ||
numAsString: numAsString, | ||
input: input, | ||
setCaretPosition: true, | ||
source: SourceType.props, | ||
event: null, | ||
}); | ||
} | ||
}); | ||
var ref$1 = React.useState(false); | ||
var mounted = ref$1[0]; | ||
var setMounted = ref$1[1]; | ||
var focusedElm = React.useRef(null); | ||
var timeout = React.useRef({ | ||
setCaretTimeout: null, | ||
focusTimeout: null, | ||
}); | ||
React.useEffect(function () { | ||
setMounted(true); | ||
return function () { | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
clearTimeout(timeout.current.focusTimeout); | ||
}; | ||
}, []); | ||
var _format = format; | ||
var getValueObject = function (formattedValue, numAsString) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
var setPatchedCaretPosition = function (el, caretPos, currentValue) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
timeout.current.setCaretTimeout = setTimeout(function () { | ||
if (el.value === currentValue) | ||
{ setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
var correctCaretPosition = function (value, caretPos, direction) { | ||
var valLn = value.length; | ||
// clamp caret position to [0, value.length] | ||
caretPos = clamp(caretPos, 0, valLn); | ||
var boundary = getCaretBoundary(value); | ||
if (direction === 'left') { | ||
while (caretPos >= 0 && !boundary[caretPos]) | ||
{ caretPos--; } | ||
// if we don't find any suitable caret position on left, set it on first allowed position | ||
if (caretPos === -1) | ||
{ caretPos = boundary.indexOf(true); } | ||
} | ||
else { | ||
while (caretPos <= valLn && !boundary[caretPos]) | ||
{ caretPos++; } | ||
// if we don't find any suitable caret position on right, set it on last allowed position | ||
if (caretPos > valLn) | ||
{ caretPos = boundary.lastIndexOf(true); } | ||
} | ||
// if we still don't find caret position, set it at the end of value | ||
if (caretPos === -1) | ||
{ caretPos = valLn; } | ||
return caretPos; | ||
}; | ||
var getNewCaretPosition = function (inputValue, formattedValue, caretPos) { | ||
var updatedCaretPos = getCaretPosition(formattedValue, inputValue, caretPos); | ||
//correct caret position if its outside of editable area | ||
updatedCaretPos = correctCaretPosition(formattedValue, updatedCaretPos); | ||
return updatedCaretPos; | ||
}; | ||
var updateValue = function (params) { | ||
var newFormattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = geInputCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = newFormattedValue; | ||
//get the caret position | ||
caretPos = getNewCaretPosition(inputValue, newFormattedValue, currentCaretPosition); | ||
} | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
*/ | ||
input.value = newFormattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
setPatchedCaretPosition(input, caretPos, newFormattedValue); | ||
} | ||
} | ||
if (newFormattedValue !== formattedValue) { | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
_onValueChange(getValueObject(newFormattedValue, numAsString), { event: event, source: source }); | ||
} | ||
}; | ||
var formatInputValue = function (inputValue, event, source) { | ||
var changeRange = findChangeRange(formattedValue, inputValue); | ||
var changeMeta = Object.assign(Object.assign({}, changeRange), { lastValue: formattedValue }); | ||
var _numAsString = removeFormatting(inputValue, changeMeta); | ||
var _formattedValue = _format(_numAsString); | ||
if (isAllowed && !isAllowed(getValueObject(_formattedValue, _numAsString))) { | ||
return false; | ||
} | ||
updateValue({ | ||
formattedValue: _formattedValue, | ||
numAsString: _numAsString, | ||
inputValue: inputValue, | ||
event: event, | ||
source: source, | ||
setCaretPosition: true, | ||
input: event.target, | ||
}); | ||
}; | ||
var _onChange = function (e) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var changed = formatInputValue(inputValue, e, SourceType.event); | ||
if (changed) | ||
{ onChange(e); } | ||
}; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = Math.max(selectionStart - 1, 0); | ||
} | ||
else if (key === 'ArrowRight') { | ||
expectedCaretPosition = Math.min(selectionStart + 1, value.length); | ||
} | ||
else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
// also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, direction); | ||
} | ||
else if (key === 'Delete' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of delete go to closest caret boundary on the right side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'right'); | ||
} | ||
else if (key === 'Backspace' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of backspace go to closest caret boundary on the left side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'left'); | ||
} | ||
if (newCaretPosition !== expectedCaretPosition) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
/* @ts-ignore */ | ||
if (e.isUnitTestRun) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
var _onMouseUp = function (e) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
} | ||
onMouseUp(e); | ||
}; | ||
var _onFocus = function (e) { | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
var el = e.target; | ||
focusedElm.current = el; | ||
timeout.current.focusTimeout = setTimeout(function () { | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if (caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length)) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
onFocus(e); | ||
}, 0); | ||
}; | ||
var _onBlur = function (e) { | ||
focusedElm.current = null; | ||
clearTimeout(timeout.current.focusTimeout); | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
onBlur(e); | ||
}; | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode() ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: formattedValue, | ||
onChange: _onChange, | ||
onKeyDown: _onKeyDown, | ||
onMouseUp: _onMouseUp, | ||
onFocus: _onFocus, | ||
onBlur: _onBlur, | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? (React__default.createElement(React__default.Fragment, null, renderText(formattedValue, otherProps) || null)) : (React__default.createElement("span", Object.assign({}, otherProps, { ref: getInputRef }), formattedValue)); | ||
} | ||
//if caretPos is just after input value don't do anything | ||
if (format[caretPos - 1] === '#' && charIsNumber(value[caretPos - 1])) { | ||
return caretPos; | ||
else if (customInput) { | ||
var CustomInput = customInput; | ||
/* @ts-ignore */ | ||
return React__default.createElement(CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
return React__default.createElement("input", Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
//find the nearest caret position | ||
var firstHashPosition = format.indexOf('#'); | ||
var lastHashPosition = format.lastIndexOf('#'); | ||
//limit the cursor between the first # position and the last # position | ||
caretPos = clamp(caretPos, firstHashPosition, lastHashPosition + 1); | ||
var nextPos = format.substring(caretPos, format.length).indexOf('#'); | ||
var caretLeftBound = caretPos; | ||
var caretRightBound = caretPos + (nextPos === -1 ? 0 : nextPos); | ||
//get the position where the last number is present | ||
while ( | ||
caretLeftBound > firstHashPosition && | ||
(format[caretLeftBound] !== '#' || !charIsNumber(value[caretLeftBound])) | ||
) { | ||
caretLeftBound -= 1; | ||
function format(numStr, props) { | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; if ( thousandsGroupStyle === void 0 ) thousandsGroupStyle = 'thousand'; | ||
// don't apply formatting on empty string or '-' | ||
if (numStr === '' || numStr === '-') { | ||
return numStr; | ||
} | ||
var goToLeft = | ||
!charIsNumber(value[caretRightBound]) || | ||
(direction === 'left' && caretPos !== firstHashPosition) || | ||
caretPos - caretLeftBound < caretRightBound - caretPos; | ||
if (goToLeft) { | ||
//check if number should be taken after the bound or after it | ||
//if number preceding a valid number keep it after | ||
return charIsNumber(value[caretLeftBound]) ? caretLeftBound + 1 : caretLeftBound; | ||
} | ||
return caretRightBound; | ||
}; | ||
NumberFormat.prototype.getCaretPosition = function getCaretPosition (inputValue , formattedValue , caretPos ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var stateValue = this.state.value; | ||
var numRegex = this.getNumberRegex(true); | ||
var inputNumber = (inputValue.match(numRegex) || []).join(''); | ||
var formattedNumber = (formattedValue.match(numRegex) || []).join(''); | ||
var j, i; | ||
j = 0; | ||
for (i = 0; i < caretPos; i++) { | ||
var currentInputChar = inputValue[i] || ''; | ||
var currentFormatChar = formattedValue[j] || ''; | ||
//no need to increase new cursor position if formatted value does not have those characters | ||
//case inputValue = 1a23 and formattedValue = 123 | ||
if (!currentInputChar.match(numRegex) && currentInputChar !== currentFormatChar) { | ||
continue; | ||
} | ||
//When we are striping out leading zeros maintain the new cursor position | ||
//Case inputValue = 00023 and formattedValue = 23; | ||
if ( | ||
currentInputChar === '0' && | ||
currentFormatChar.match(numRegex) && | ||
currentFormatChar !== '0' && | ||
inputNumber.length !== formattedNumber.length | ||
) { | ||
continue; | ||
} | ||
//we are not using currentFormatChar because j can change here | ||
while (currentInputChar !== formattedValue[j] && j < formattedValue.length) { | ||
j++; | ||
} | ||
j++; | ||
} | ||
if (typeof format === 'string' && !stateValue) { | ||
//set it to the maximum value so it goes after the last number | ||
j = formattedValue.length; | ||
} | ||
//correct caret position if its outside of editable area | ||
j = this.correctCaretPosition(formattedValue, j); | ||
return j; | ||
}; | ||
/** caret specific methods ends **/ | ||
/** methods to remove formattting **/ | ||
NumberFormat.prototype.removePrefixAndSuffix = function removePrefixAndSuffix (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
//remove prefix and suffix | ||
if (!format && val) { | ||
var isNegative = val[0] === '-'; | ||
//remove negation sign | ||
if (isNegative) { val = val.substring(1, val.length); } | ||
//remove prefix | ||
val = prefix && val.indexOf(prefix) === 0 ? val.substring(prefix.length, val.length) : val; | ||
//remove suffix | ||
var suffixLastIndex = val.lastIndexOf(suffix); | ||
val = | ||
suffix && suffixLastIndex !== -1 && suffixLastIndex === val.length - suffix.length | ||
? val.substring(0, suffixLastIndex) | ||
: val; | ||
//add negation sign back | ||
if (isNegative) { val = '-' + val; } | ||
} | ||
return val; | ||
}; | ||
NumberFormat.prototype.removePatternFormatting = function removePatternFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var formatArray = format.split('#').filter(function (str) { return str !== ''; }); | ||
var start = 0; | ||
var numStr = ''; | ||
for (var i = 0, ln = formatArray.length; i <= ln; i++) { | ||
var part = formatArray[i] || ''; | ||
//if i is the last fragment take the index of end of the value | ||
//For case like +1 (911) 911 91 91 having pattern +1 (###) ### ## ## | ||
var index = i === ln ? val.length : val.indexOf(part, start); | ||
/* in any case if we don't find the pattern part in the value assume the val as numeric string | ||
This will be also in case if user has started typing, in any other case it will not be -1 | ||
unless wrong prop value is provided */ | ||
if (index === -1) { | ||
numStr = val; | ||
break; | ||
} else { | ||
numStr += val.substring(start, index); | ||
start = index + part.length; | ||
} | ||
} | ||
return (numStr.match(this.getNumberRegex(true)) || []).join(''); | ||
}; | ||
NumberFormat.prototype.removeFormatting = function removeFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var removeFormatting = ref.removeFormatting; | ||
if (!val) { return val; } | ||
if (!format) { | ||
val = this.removePrefixAndSuffix(val); | ||
val = this.getFloatString(val); | ||
} else if (typeof format === 'string') { | ||
val = this.removePatternFormatting(val); | ||
} else if (typeof removeFormatting === 'function') { | ||
//condition need to be handled if format method is provide, | ||
val = removeFormatting(val); | ||
} else { | ||
val = (val.match(this.getNumberRegex(true)) || []).join(''); | ||
} | ||
return val; | ||
}; | ||
/** methods to remove formattting end **/ | ||
/*** format specific methods start ***/ | ||
/** | ||
* Format when # based string is provided | ||
* @param {string} numStr Numeric String | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatWithPattern = function formatWithPattern (numStr ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || this.getMaskAtIndex(hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
}; | ||
/** | ||
* @param {string} numStr Numeric string/floatString] It always have decimalSeparator as . | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatAsNumber = function formatAsNumber (numStr ) { | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var allowNegative = ref.allowNegative; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var ref$1 = this.getSeparators(); | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var hasDecimalSeparator = numStr.indexOf('.') !== -1 || (decimalScale && fixedDecimalScale); | ||
var ref$2 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$2.beforeDecimal; | ||
var afterDecimal = ref$2.afterDecimal; | ||
var addNegation = ref$2.addNegation; // eslint-disable-line prefer-const | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
/** | ||
* Keep the decimal separator | ||
* when decimalScale is not defined or non zero and the numStr has decimal in it | ||
* Or if decimalScale is > 0 and fixeDecimalScale is true (even if numStr has no decimal) | ||
*/ | ||
var hasDecimalSeparator = (decimalScale !== 0 && numStr.indexOf('.') !== -1) || (decimalScale && fixedDecimalScale); | ||
var ref$1 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//apply decimal precision if its defined | ||
if (decimalScale !== undefined) { | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
} | ||
if (thousandSeparator) { | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
} | ||
//add prefix and suffix | ||
if (prefix) { beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) { afterDecimal = afterDecimal + suffix; } | ||
//add prefix and suffix when there is a number present | ||
if (prefix) | ||
{ beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) | ||
{ afterDecimal = afterDecimal + suffix; } | ||
//restore negation sign | ||
if (addNegation) { beforeDecimal = '-' + beforeDecimal; } | ||
if (addNegation) | ||
{ beforeDecimal = '-' + beforeDecimal; } | ||
numStr = beforeDecimal + ((hasDecimalSeparator && decimalSeparator) || '') + afterDecimal; | ||
return numStr; | ||
}; | ||
NumberFormat.prototype.formatNumString = function formatNumString (numStr) { | ||
if ( numStr === void 0 ) numStr = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var customNumerals = ref.customNumerals; | ||
var formattedValue = numStr; | ||
if (customNumerals && customNumerals.length === 10) { | ||
var customNumeralRegex = new RegExp('[' + customNumerals.join('') + ']', 'g'); | ||
formattedValue = numStr.replace(customNumeralRegex, function (digit) { return customNumerals.indexOf(digit).toString(); } | ||
); | ||
} | ||
function getSeparators(props) { | ||
var decimalSeparator = props.decimalSeparator; if ( decimalSeparator === void 0 ) decimalSeparator = '.'; | ||
var thousandSeparator = props.thousandSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (numStr === '' && !allowEmptyFormatting) { | ||
formattedValue = ''; | ||
} else if (numStr === '-' && !format) { | ||
formattedValue = '-'; | ||
} else if (typeof format === 'string') { | ||
formattedValue = this.formatWithPattern(formattedValue); | ||
} else if (typeof format === 'function') { | ||
formattedValue = format(formattedValue); | ||
} else { | ||
formattedValue = this.formatAsNumber(formattedValue); | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
}; | ||
} | ||
function handleNegation(value, allowNegative) { | ||
if ( value === void 0 ) value = ''; | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatValueProp = function formatValueProp (defaultValue ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var ref$1 = this.props; | ||
var value = ref$1.value; | ||
var isNumericString = ref$1.isNumericString; | ||
// if value is undefined or null, use defaultValue instead | ||
value = isNil(value) ? defaultValue : value; | ||
var isNonNumericFalsy = !value && value !== 0; | ||
if (isNonNumericFalsy && allowEmptyFormatting) { | ||
value = ''; | ||
} | ||
// if value is not defined return empty string | ||
if (isNonNumericFalsy && !allowEmptyFormatting) { return ''; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
isNumericString = true; | ||
} | ||
//change infinity value to empty string | ||
if (value === 'Infinity' && isNumericString) { | ||
value = ''; | ||
} | ||
//round the number based on decimalScale | ||
//format only if non formatted value is provided | ||
if (isNumericString && !format && typeof decimalScale === 'number') { | ||
value = roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
var formattedValue = isNumericString ? this.formatNumString(value) : this.formatInput(value); | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatNegation = function formatNegation (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var allowNegative = ref.allowNegative; | ||
var negationRegex = new RegExp('(-)'); | ||
var doubleNegationRegex = new RegExp('(-)(.)*(-)'); | ||
// Check number has '-' value | ||
var hasNegation = negationRegex.test(value); | ||
// Check number has 2 or more '-' values | ||
var removeNegation = doubleNegationRegex.test(value); | ||
//remove negation | ||
value = value.replace(/-/g, ''); | ||
if (hasNegation && !removeNegation && allowNegative) { | ||
value = '-' + value; | ||
value = '-' + value; | ||
} | ||
return value; | ||
}; | ||
} | ||
function getNumberRegex(decimalSeparator, decimalScale, global) { | ||
return new RegExp(("(^-)|[0-9]|" + (escapeRegExp(decimalSeparator))), global ? 'g' : undefined); | ||
} | ||
function removeFormatting(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
NumberFormat.prototype.formatInput = function formatInput (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
//format negation only if we are formatting as number | ||
if (!format) { | ||
value = this.removePrefixAndSuffix(value); | ||
value = this.formatNegation(value); | ||
} | ||
//remove formatting from number | ||
value = this.removeFormatting(value); | ||
return this.formatNumString(value); | ||
}; | ||
/*** format specific methods end ***/ | ||
NumberFormat.prototype.isCharacterAFormat = function isCharacterAFormat (caretPos , value ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
//check within format pattern | ||
if (typeof format === 'string' && format[caretPos] !== '#') { return true; } | ||
//check in number format | ||
if ( | ||
!format && | ||
(caretPos < prefix.length || | ||
caretPos >= value.length - suffix.length || | ||
(decimalScale && fixedDecimalScale && value[caretPos] === decimalSeparator)) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* This will check if any formatting got removed by the delete or backspace and reset the value | ||
* It will also work as fallback if android chome keyDown handler does not work | ||
**/ | ||
NumberFormat.prototype.correctInputValue = function correctInputValue (caretPos , lastValue , value ) { | ||
var this$1 = this; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowNegative = ref.allowNegative; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var lastNumStr = this.state.numAsString || ''; | ||
var ref$2 = this.selectionBeforeInput; | ||
var selectionStart = ref$2.selectionStart; | ||
var selectionEnd = ref$2.selectionEnd; | ||
var ref$3 = findChangedIndex(lastValue, value); | ||
var start = ref$3.start; | ||
var end = ref$3.end; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var decimalScale = props.decimalScale; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var start = to.start; | ||
var end = to.end; | ||
var ref = getSeparators(props); | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var isBeforeDecimalSeparator = value[end] === decimalSeparator; | ||
/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */ | ||
if ( | ||
!format && | ||
start === end && | ||
allowedDecimalSeparators.indexOf(value[selectionStart]) !== -1 | ||
) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
return ( | ||
value.substr(0, selectionStart) + separator + value.substr(selectionStart + 1, value.length) | ||
); | ||
if (end - start === 1 && allowedDecimalSeparators.indexOf(value[start]) !== -1) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
value = value.substring(0, start) + separator + value.substring(start + 1, value.length); | ||
} | ||
var leftBound = !!format ? 0 : prefix.length; | ||
var rightBound = lastValue.length - (!!format ? 0 : suffix.length); | ||
if ( | ||
// don't do anything if something got added | ||
value.length > lastValue.length || | ||
// or if the new value is an empty string | ||
!value.length || | ||
// or if nothing has changed, in which case start will be same as end | ||
start === end || | ||
// or in case if whole input is selected and new value is typed | ||
(selectionStart === 0 && selectionEnd === lastValue.length) || | ||
// or in case if the whole content is replaced by browser, example (autocomplete) | ||
(start === 0 && end === lastValue.length) || | ||
// or if charcters between prefix and suffix is selected. | ||
// For numeric inputs we apply the format so, prefix and suffix can be ignored | ||
(selectionStart === leftBound && selectionEnd === rightBound) | ||
) { | ||
return value; | ||
var hasNegation = false; | ||
/** | ||
* if prefix starts with - the number hast to have two - at the start | ||
* if suffix starts with - and the value length is same as suffix length, then the - sign is from the suffix | ||
* In other cases, if the value starts with - then it is a negation | ||
*/ | ||
if (prefix.startsWith('-')) | ||
{ hasNegation = value.startsWith('--'); } | ||
else if (suffix.startsWith('-') && value.length === suffix.length) | ||
{ hasNegation = false; } | ||
else if (value[0] === '-') | ||
{ hasNegation = true; } | ||
// remove negation from start to simplify prefix logic as negation comes before prefix | ||
if (hasNegation) { | ||
value = value.substring(1); | ||
// account for the removal of the negation for start and end index | ||
start -= 1; | ||
end -= 1; | ||
} | ||
// check whether the deleted portion has a character that is part of a format | ||
var deletedValues = lastValue.substr(start, end - start); | ||
var formatGotDeleted = !![].concat( deletedValues ).find(function (deletedVal, idx) { return this$1.isCharacterAFormat(idx + start, lastValue); } | ||
); | ||
// if it has, only remove characters that are not part of the format | ||
if (formatGotDeleted) { | ||
var deletedValuePortion = lastValue.substr(start); | ||
var recordIndexOfFormatCharacters = {}; | ||
var resolvedPortion = []; | ||
[].concat( deletedValuePortion ).forEach(function (currentPortion, idx) { | ||
if (this$1.isCharacterAFormat(idx + start, lastValue)) { | ||
recordIndexOfFormatCharacters[idx] = currentPortion; | ||
} else if (idx > deletedValues.length - 1) { | ||
resolvedPortion.push(currentPortion); | ||
} | ||
}); | ||
Object.keys(recordIndexOfFormatCharacters).forEach(function (idx) { | ||
if (resolvedPortion.length > idx) { | ||
resolvedPortion.splice(idx, 0, recordIndexOfFormatCharacters[idx]); | ||
} else { | ||
resolvedPortion.push(recordIndexOfFormatCharacters[idx]); | ||
} | ||
}); | ||
value = lastValue.substr(0, start) + resolvedPortion.join(''); | ||
} | ||
//for numbers check if beforeDecimal got deleted and there is nothing after decimal, | ||
/** | ||
* remove prefix | ||
* Remove whole prefix part if its present on the value | ||
* If the prefix is partially deleted (in which case change start index will be less the prefix length) | ||
* Remove only partial part of prefix. | ||
*/ | ||
var startIndex = 0; | ||
if (value.startsWith(prefix)) | ||
{ startIndex += prefix.length; } | ||
else if (start < prefix.length) | ||
{ startIndex = start; } | ||
value = value.substring(startIndex); | ||
// account for deleted prefix for end index | ||
end -= startIndex; | ||
/** | ||
* Remove suffix | ||
* Remove whole suffix part if its present on the value | ||
* If the suffix is partially deleted (in which case change end index will be greater than the suffixStartIndex) | ||
* remove the partial part of suffix | ||
*/ | ||
var endIndex = value.length; | ||
var suffixStartIndex = value.length - suffix.length; | ||
if (value.endsWith(suffix)) | ||
{ endIndex = suffixStartIndex; } | ||
else if (end > value.length - suffix.length) | ||
{ endIndex = end; } | ||
value = value.substring(0, endIndex); | ||
// add the negation back and handle for double negation | ||
value = handleNegation(hasNegation ? ("-" + value) : value, allowNegative); | ||
// remove non numeric characters | ||
value = (value.match(getNumberRegex(decimalSeparator, decimalScale, true)) || []).join(''); | ||
// replace the decimalSeparator with ., and only keep the first separator, ignore following ones | ||
var firstIndex = value.indexOf(decimalSeparator); | ||
value = value.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), function (match, index) { | ||
return index === firstIndex ? '.' : ''; | ||
}); | ||
//check if beforeDecimal got deleted and there is nothing after decimal, | ||
//clear all numbers in such case while keeping the - sign | ||
if (!format) { | ||
var numericString = this.removeFormatting(value); | ||
var ref$4 = splitDecimal( | ||
numericString, | ||
allowNegative | ||
); | ||
var beforeDecimal = ref$4.beforeDecimal; | ||
var afterDecimal = ref$4.afterDecimal; | ||
var addNegation = ref$4.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted | ||
var isBeforeDecimalPoint = caretPos < value.indexOf(decimalSeparator) + 1; | ||
if ( | ||
numericString.length < lastNumStr.length && | ||
isBeforeDecimalPoint && | ||
var ref$1 = splitDecimal(value, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted before decimal (cursor is before decimal) | ||
if (to.end - to.start < from.end - from.start && | ||
beforeDecimal === '' && | ||
!parseFloat(afterDecimal) | ||
) { | ||
return addNegation ? '-' : ''; | ||
} | ||
isBeforeDecimalSeparator && | ||
!parseFloat(afterDecimal)) { | ||
value = addNegation ? '-' : ''; | ||
} | ||
return value; | ||
}; | ||
/** Update value and caret position */ | ||
NumberFormat.prototype.updateValue = function updateValue (params | ||
) { | ||
var formattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
var ref = this.props; | ||
var onValueChange = ref.onValueChange; | ||
var ref$1 = this.state; | ||
var lastValue = ref$1.value; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = getCurrentCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = formattedValue; | ||
//get the caret position | ||
caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition); | ||
} | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
*/ | ||
input.value = formattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
this.setPatchedCaretPosition(input, caretPos, formattedValue); | ||
} | ||
} | ||
function getCaretBoundary(formattedValue, props) { | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hasNegation = formattedValue[0] === '-'; | ||
// fill for prefix and negation | ||
boundaryAry.fill(false, 0, prefix.length + (hasNegation ? 1 : 0)); | ||
// fill for suffix | ||
var valLn = formattedValue.length; | ||
boundaryAry.fill(false, valLn - suffix.length + 1, valLn + 1); | ||
return boundaryAry; | ||
} | ||
function validateProps(props) { | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (thousandSeparator === decimalSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
//calculate numeric string if not passed | ||
if (numAsString === undefined) { | ||
numAsString = this.removeFormatting(formattedValue); | ||
} | ||
function useNumericFormat(props) { | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
// validate props | ||
validateProps(props); | ||
var _format = function (numStr) { return format(numStr, props); }; | ||
var _removeFormatting = function (inputValue, changeMeta) { return removeFormatting(inputValue, changeMeta, props); }; | ||
var _isNumericString = isNumericString; | ||
if (!isNil(value)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof value === 'number'; | ||
} | ||
//update state if value is changed | ||
if (formattedValue !== lastValue) { | ||
this.setState({ value: formattedValue, numAsString: numAsString }); | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
onValueChange(this.getValueObject(formattedValue, numAsString), { event: event, source: source }); | ||
else if (!isNil(defaultValue)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof defaultValue === 'number'; | ||
} | ||
}; | ||
var roundIncomingValueToPrecision = function (value) { | ||
if (isNil(value) || isNanValue(value)) | ||
{ return value; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
} | ||
/** | ||
* only round numeric or float string values coming through props, | ||
* we don't need to do it for onChange events, as we want to prevent typing there | ||
*/ | ||
if (_isNumericString && typeof decimalScale === 'number') { | ||
return roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
return value; | ||
}; | ||
var ref = useInternalValues(roundIncomingValueToPrecision(value), roundIncomingValueToPrecision(defaultValue), _isNumericString, _format, _removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var numAsString = ref_0.numAsString; | ||
var formattedValue = ref_0.formattedValue; | ||
var _onValueChange = ref[1]; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if user hits backspace, while the cursor is before prefix, and the input has negation, remove the negation | ||
if (key === 'Backspace' && value[0] === '-' && selectionStart === prefix.length + 1) { | ||
// bring the cursor to after negation | ||
setCaretPosition(el, 1); | ||
} | ||
// don't allow user to delete decimal separator when decimalScale and fixedDecimalScale is set | ||
var ref = getSeparators(props); | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (key === 'Backspace' && | ||
value[selectionStart - 1] === decimalSeparator && | ||
decimalScale && | ||
fixedDecimalScale) { | ||
setCaretPosition(el, selectionStart - 1); | ||
e.preventDefault(); | ||
} | ||
// move cursor when delete or backspace is pressed before/after thousand separator | ||
if (key === 'Backspace' && value[selectionStart - 1] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart - 1); | ||
} | ||
if (key === 'Delete' && value[selectionStart] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart + 1); | ||
} | ||
onKeyDown(e); | ||
}; | ||
var _onBlur = function (e) { | ||
var _value = numAsString; | ||
// if there no no numeric value, clear the input | ||
if (!_value.match(/\d/g)) { | ||
_value = ''; | ||
} | ||
// clear leading 0s | ||
if (!allowLeadingZeros) { | ||
_value = fixLeadingZero(_value); | ||
} | ||
// apply fixedDecimalScale on blur event | ||
if (fixedDecimalScale && decimalScale) { | ||
_value = roundToPrecision(_value, decimalScale, fixedDecimalScale); | ||
} | ||
if (_value !== numAsString) { | ||
var formattedValue = format(_value, props); | ||
_onValueChange({ | ||
formattedValue: formattedValue, | ||
value: _value, | ||
floatValue: parseFloat(_value), | ||
}, { | ||
event: e, | ||
source: SourceType.event, | ||
}); | ||
} | ||
onBlur(e); | ||
}; | ||
return { | ||
value: formattedValue, | ||
isNumericString: false, | ||
onValueChange: _onValueChange, | ||
format: _format, | ||
removeFormatting: _removeFormatting, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
onBlur: _onBlur, | ||
}; | ||
} | ||
function NumericFormat(props) { | ||
var decimalSeparator = props.decimalSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; | ||
var suffix = props.suffix; | ||
var allowNegative = props.allowNegative; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; | ||
var onBlur = props.onBlur; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var restProps = __rest(props, ["decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "suffix", "allowNegative", "allowLeadingZeros", "onKeyDown", "onBlur", "thousandSeparator", "decimalScale", "fixedDecimalScale", "prefix", "defaultValue", "value", "isNumericString", "onValueChange"]); | ||
var numericFormatProps = useNumericFormat(props); | ||
return React__default.createElement(NumberFormatBase, Object.assign({}, restProps, numericFormatProps)); | ||
} | ||
NumberFormat.prototype.onChange = function onChange (e ) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var ref = this; | ||
var state = ref.state; | ||
var props = ref.props; | ||
var isAllowed = props.isAllowed; | ||
var lastValue = state.value || ''; | ||
var currentCaretPosition = getCurrentCaretPosition(el); | ||
inputValue = this.correctInputValue(currentCaretPosition, lastValue, inputValue); | ||
var formattedValue = this.formatInput(inputValue) || ''; | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var valueObj = this.getValueObject(formattedValue, numAsString); | ||
var isChangeAllowed = isAllowed(valueObj); | ||
if (!isChangeAllowed) { | ||
formattedValue = lastValue; | ||
function format$1(numStr, props) { | ||
var format = props.format; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var mask = props.mask; | ||
if (numStr === '' && !allowEmptyFormatting) | ||
{ return ''; } | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || getMaskAtIndex(mask, hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
} | ||
function removeFormatting$1(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
inputValue: inputValue, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
if (isChangeAllowed) { | ||
props.onChange(e); | ||
} | ||
}; | ||
NumberFormat.prototype.onBlur = function onBlur (e ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var format = props.format; | ||
var onBlur = props.onBlur; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var numAsString = state.numAsString; | ||
var lastValue = state.value; | ||
this.focusedElm = null; | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
if (!format) { | ||
// if the numAsString is not a valid number reset it to empty | ||
if (isNaN(parseFloat(numAsString))) { | ||
numAsString = ''; | ||
} | ||
if (!allowLeadingZeros) { | ||
numAsString = fixLeadingZero(numAsString); | ||
} | ||
var formattedValue = this.formatNumString(numAsString); | ||
//change the state | ||
if (formattedValue !== lastValue) { | ||
// the event needs to be persisted because its properties can be accessed in an asynchronous way | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: e.target, | ||
setCaretPosition: false, | ||
event: e, | ||
source: 'event', | ||
}); | ||
onBlur(e); | ||
return; | ||
} | ||
} | ||
onBlur(e); | ||
}; | ||
NumberFormat.prototype.onKeyDown = function onKeyDown (e ) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
var onKeyDown = ref.onKeyDown; | ||
var ignoreDecimalSeparator = decimalScale !== undefined && fixedDecimalScale; | ||
var numRegex = this.getNumberRegex(false, ignoreDecimalSeparator); | ||
var negativeRegex = new RegExp('-'); | ||
var isPatternFormat = typeof format === 'string'; | ||
this.selectionBeforeInput = { | ||
selectionStart: selectionStart, | ||
selectionEnd: selectionEnd, | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var lastValue = changeMeta.lastValue; if ( lastValue === void 0 ) lastValue = ''; | ||
var isNumericSlot = function (caretPos) { return format[caretPos] === patternChar; }; | ||
var removeFormatChar = function (string, startIndex) { | ||
var str = ''; | ||
for (var i = 0; i < string.length; i++) { | ||
if (isNumericSlot(startIndex + i)) { | ||
str += string[i]; | ||
} | ||
} | ||
return str; | ||
}; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = selectionStart - 1; | ||
} else if (key === 'ArrowRight') { | ||
expectedCaretPosition = selectionStart + 1; | ||
} else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
var extractNumbers = function (str) { return str.replace(/[^0-9]/g, ''); }; | ||
// if format doesn't have any number, remove all the non numeric characters | ||
if (!format.match(/\d/)) { | ||
return extractNumbers(value); | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
//also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
var leftBound = isPatternFormat ? format.indexOf('#') : prefix.length; | ||
var rightBound = isPatternFormat ? format.lastIndexOf('#') + 1 : value.length - suffix.length; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = this.correctCaretPosition(value, expectedCaretPosition, direction); | ||
} else if ( | ||
key === 'Delete' && | ||
!numRegex.test(value[expectedCaretPosition]) && | ||
!negativeRegex.test(value[expectedCaretPosition]) | ||
) { | ||
while (!numRegex.test(value[newCaretPosition]) && newCaretPosition < rightBound) { | ||
newCaretPosition++; | ||
} | ||
} else if (key === 'Backspace' && !numRegex.test(value[expectedCaretPosition])) { | ||
/* NOTE: This is special case when backspace is pressed on a | ||
negative value while the cursor position is after prefix. We can't handle it on onChange because | ||
we will not have any information of keyPress | ||
*/ | ||
if (selectionStart <= leftBound + 1 && value[0] === '-' && typeof format === 'undefined') { | ||
var newValue = value.substring(1); | ||
this.updateValue({ | ||
formattedValue: newValue, | ||
caretPos: newCaretPosition, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
} else if (!negativeRegex.test(value[expectedCaretPosition])) { | ||
while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) { | ||
newCaretPosition--; | ||
/** | ||
* if user paste the whole formatted text in an empty input, check if matches to the pattern | ||
* and remove the format characters, if there is a mismatch on the pattern, do plane number extract | ||
*/ | ||
if (lastValue === '' && value.length === format.length) { | ||
var str = ''; | ||
for (var i = 0; i < value.length; i++) { | ||
if (isNumericSlot(i)) { | ||
str += value[i]; | ||
} | ||
else if (value[i] !== format[i]) { | ||
// if there is a mismatch on the pattern, do plane number extract | ||
return extractNumbers(value); | ||
} | ||
} | ||
newCaretPosition = this.correctCaretPosition(value, newCaretPosition, 'left'); | ||
} | ||
return str; | ||
} | ||
if ( | ||
newCaretPosition !== expectedCaretPosition || | ||
expectedCaretPosition < leftBound || | ||
expectedCaretPosition > rightBound | ||
) { | ||
e.preventDefault(); | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
if (e.isUnitTestRun) { | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
NumberFormat.prototype.onMouseUp = function onMouseUp (e ) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
* For partial change, | ||
* where ever there is a change on the input, we can break the number in three parts | ||
* 1st: left part which is unchanged | ||
* 2nd: middle part which is changed | ||
* 3rd: right part which is unchanged | ||
* | ||
* The first and third section will be same as last value, only the middle part will change | ||
* We can consider on the change part all the new characters are non format characters. | ||
* And on the first and last section it can have partial format characters. | ||
* | ||
* We pick first and last section from the lastValue (as that has 1-1 mapping with format) | ||
* and middle one from the update value. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = this.correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
this.setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
} | ||
this.props.onMouseUp(e); | ||
}; | ||
NumberFormat.prototype.onFocus = function onFocus (e ) { | ||
var this$1 = this; | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
this.focusedElm = e.target; | ||
this.focusTimeout = setTimeout(function () { | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = this$1.correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if ( | ||
caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length) | ||
) { | ||
this$1.setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
this$1.props.onFocus(e); | ||
}, 0); | ||
}; | ||
NumberFormat.prototype.render = function render () { | ||
var ref = this.props; | ||
var type = ref.type; | ||
var displayType = ref.displayType; | ||
var customInput = ref.customInput; | ||
var renderText = ref.renderText; | ||
var getInputRef = ref.getInputRef; | ||
var format = ref.format; | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var removeFormatting = ref.removeFormatting; | ||
var mask = ref.mask; | ||
var defaultValue = ref.defaultValue; | ||
var isNumericString = ref.isNumericString; | ||
var allowNegative = ref.allowNegative; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var allowLeadingZeros = ref.allowLeadingZeros; | ||
var onValueChange = ref.onValueChange; | ||
var isAllowed = ref.isAllowed; | ||
var customNumerals = ref.customNumerals; | ||
var onChange = ref.onChange; | ||
var onKeyDown = ref.onKeyDown; | ||
var onMouseUp = ref.onMouseUp; | ||
var onFocus = ref.onFocus; | ||
var onBlur = ref.onBlur; | ||
var propValue = ref.value; | ||
var rest = objectWithoutProperties( ref, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "thousandSeparator", "decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "decimalScale", "fixedDecimalScale", "prefix", "suffix", "removeFormatting", "mask", "defaultValue", "isNumericString", "allowNegative", "allowEmptyFormatting", "allowLeadingZeros", "onValueChange", "isAllowed", "customNumerals", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value"] ); | ||
var otherProps = rest; | ||
var ref$1 = this.state; | ||
var value = ref$1.value; | ||
var mounted = ref$1.mounted; | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode(format) ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: value, | ||
onChange: this.onChange, | ||
onKeyDown: this.onKeyDown, | ||
onMouseUp: this.onMouseUp, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
var firstSection = lastValue.substring(0, from.start); | ||
var middleSection = value.substring(to.start, to.end); | ||
var lastSection = lastValue.substring(from.end); | ||
return ("" + (removeFormatChar(firstSection, 0)) + (extractNumbers(middleSection)) + (removeFormatChar(lastSection, from.end))); | ||
} | ||
function getCaretBoundary$1(formattedValue, props) { | ||
var format = props.format; | ||
var mask = props.mask; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hashCount = 0; | ||
var maskAndFormatMap = format.split('').map(function (char) { | ||
if (char === patternChar) { | ||
hashCount++; | ||
return getMaskAtIndex(mask, hashCount - 1); | ||
} | ||
return undefined; | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? ( | ||
renderText(value, otherProps) || null | ||
) : ( | ||
React.createElement( 'span', Object.assign({}, otherProps, { ref: getInputRef }), | ||
value | ||
) | ||
); | ||
} else if (customInput) { | ||
var CustomInput = customInput; | ||
return React.createElement( CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
var isPosAllowed = function (pos) { | ||
// the position is allowed if the position is not masked and valid number area | ||
return format[pos] === patternChar && formattedValue[pos] !== maskAndFormatMap[pos]; | ||
}; | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
// Note: on pattern based format its denoted by patternCharacter | ||
boundaryAry[i] = isPosAllowed(i) || isPosAllowed(i - 1); | ||
} | ||
// the first patternChar position is always allowed | ||
boundaryAry[format.indexOf(patternChar)] = true; | ||
return boundaryAry; | ||
} | ||
function validateProps$1(props) { | ||
var mask = props.mask; | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("Mask " + mask + " should not contain numeric character;")); | ||
} | ||
} | ||
} | ||
function usePatternFormat(props) { | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; if ( inputMode === void 0 ) inputMode = 'numeric'; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
// validate props | ||
validateProps$1(props); | ||
var _onKeyDown = function (e) { | ||
var key = e.key; | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if backspace is pressed after the format characters, bring it to numeric section | ||
// if delete is pressed before the format characters, bring it to numeric section | ||
if (key === 'Backspace' || key === 'Delete') { | ||
// bring the cursor to closest numeric section | ||
var index = selectionStart; | ||
if (key === 'Backspace') { | ||
while (index > 0 && formatProp[index - 1] !== patternChar) { | ||
index--; | ||
} | ||
} | ||
else { | ||
var formatLn = formatProp.length; | ||
while (index < formatLn && formatProp[index] !== patternChar) { | ||
index++; | ||
} | ||
} | ||
if (index !== selectionStart) { | ||
setCaretPosition(el, index); | ||
} | ||
} | ||
onKeyDown(e); | ||
}; | ||
return { | ||
inputMode: inputMode, | ||
format: function (numStr) { return format$1(numStr, props); }, | ||
removeFormatting: function (inputValue, changeMeta) { return removeFormatting$1(inputValue, changeMeta, props); }, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary$1(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
}; | ||
} | ||
function PatternFormat(props) { | ||
var mask = props.mask; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; | ||
var onKeyDown = props.onKeyDown; | ||
var patternChar = props.patternChar; | ||
var restProps = __rest(props, ["mask", "allowEmptyFormatting", "format", "inputMode", "onKeyDown", "patternChar"]); | ||
var patternFormatProps = usePatternFormat(props); | ||
return React__default.createElement(NumberFormatBase, Object.assign({}, restProps, patternFormatProps)); | ||
} | ||
return React.createElement( 'input', Object.assign({}, inputProps, { ref: getInputRef })); | ||
}; | ||
return NumberFormat; | ||
}(React.Component)); | ||
NumberFormat.defaultProps = defaultProps; | ||
module.exports = NumberFormat; | ||
exports.NumberFormatBase = NumberFormatBase; | ||
exports.NumericFormat = NumericFormat; | ||
exports.PatternFormat = PatternFormat; | ||
exports.getNumericCaretBoundary = getCaretBoundary; | ||
exports.getPatternCaretBoundary = getCaretBoundary$1; | ||
exports.numericFormatter = format; | ||
exports.patterFormatter = format$1; | ||
exports.removeNumericFormat = removeFormatting; | ||
exports.removePatternFormat = removeFormatting$1; | ||
exports.useNumericFormat = useNumericFormat; | ||
exports.usePatternFormat = usePatternFormat; |
/** | ||
* react-number-format - 4.9.3 | ||
* react-number-format - 5.0.0-beta.1 | ||
* Author : Sudhanshu Yadav | ||
@@ -8,83 +8,113 @@ * Copyright (c) 2016, 2022 to Sudhanshu Yadav, released under the MIT license. | ||
import React from 'react'; | ||
import React, { useRef, useState, useMemo, useEffect } from 'react'; | ||
// | ||
/****************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
// basic noop function | ||
function noop() {} | ||
function returnTrue() { | ||
return true; | ||
} | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function charIsNumber(char ) { | ||
return !!(char || '').match(/\d/); | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
{ t[p] = s[p]; } } | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
{ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
{ t[p[i]] = s[p[i]]; } | ||
} } | ||
return t; | ||
} | ||
function isNil(val ) { | ||
return val === null || val === undefined; | ||
} | ||
var SourceType; | ||
(function (SourceType) { | ||
SourceType["event"] = "event"; | ||
SourceType["props"] = "prop"; | ||
})(SourceType || (SourceType = {})); | ||
function escapeRegExp(str ) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
// basic noop function | ||
function noop() { } | ||
function charIsNumber(char) { | ||
return !!(char || '').match(/\d/); | ||
} | ||
function getThousandsGroupRegex(thousandsGroupStyle ) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
} | ||
function isNil(val) { | ||
return val === null || val === undefined; | ||
} | ||
function isNanValue(val) { | ||
return typeof val === 'number' && isNaN(val); | ||
} | ||
function escapeRegExp(str) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
} | ||
function getThousandsGroupRegex(thousandsGroupStyle) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
} | ||
} | ||
function applyThousandSeparator(str, thousandSeparator, thousandsGroupStyle) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return (str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator)); | ||
} | ||
function usePersistentCallback(cb) { | ||
var callbackRef = useRef(cb); | ||
// keep the callback ref upto date | ||
callbackRef.current = cb; | ||
/** | ||
* initialize a persistent callback which never changes | ||
* through out the component lifecycle | ||
*/ | ||
var persistentCbRef = useRef(function () { | ||
var args = [], len = arguments.length; | ||
while ( len-- ) args[ len ] = arguments[ len ]; | ||
function applyThousandSeparator( | ||
str , | ||
thousandSeparator , | ||
thousandsGroupStyle | ||
) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return ( | ||
str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator) | ||
); | ||
return callbackRef.current.apply(callbackRef, args); | ||
}); | ||
return persistentCbRef.current; | ||
} | ||
//spilt a float number into different parts beforeDecimal, afterDecimal, and negation | ||
function splitDecimal(numStr , allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
function splitDecimal(numStr, allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
var hasNagation = numStr[0] === '-'; | ||
var addNegation = hasNagation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNagation: hasNagation, | ||
addNegation: addNegation, | ||
}; | ||
var hasNegation = numStr[0] === '-'; | ||
var addNegation = hasNegation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNegation: hasNegation, | ||
addNegation: addNegation, | ||
}; | ||
} | ||
function fixLeadingZero(numStr ) { | ||
if (!numStr) { return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) { numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
function fixLeadingZero(numStr) { | ||
if (!numStr) | ||
{ return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) | ||
{ numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
} | ||
/** | ||
@@ -94,59 +124,51 @@ * limit decimal numbers to given scale | ||
*/ | ||
function limitToScale(numStr , scale , fixedDecimalScale ) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
} | ||
return str; | ||
function limitToScale(numStr, scale, fixedDecimalScale) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
} | ||
return str; | ||
} | ||
function repeat(str, count) { | ||
return Array(count + 1).join(str); | ||
return Array(count + 1).join(str); | ||
} | ||
function toNumericString(num) { | ||
num += ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = num[0] === '-' ? '-' : ''; | ||
if (sign) { num = num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) { return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
} | ||
return sign + coefficient; | ||
var _num = num + ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = _num[0] === '-' ? '-' : ''; | ||
if (sign) | ||
{ _num = _num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = _num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) | ||
{ return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} | ||
else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} | ||
else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
} | ||
return sign + coefficient; | ||
} | ||
/** | ||
@@ -156,1232 +178,990 @@ * This method is required to round prop value to given scale. | ||
*/ | ||
function roundToPrecision(numStr , scale , fixedDecimalScale ) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) { return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNagation = ref.hasNagation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = | ||
afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ( | ||
(Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length) | ||
); | ||
} | ||
return current + roundedStr; | ||
function roundToPrecision(numStr, scale, fixedDecimalScale) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) | ||
{ return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNegation = ref.hasNegation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ((Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length)); | ||
} | ||
return current + roundedStr; | ||
}, roundedDecimalParts[0]); | ||
var decimalPart = limitToScale( | ||
roundedDecimalParts[1] || '', | ||
Math.min(scale, afterDecimal.length), | ||
fixedDecimalScale | ||
); | ||
var negation = hasNagation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
var decimalPart = limitToScale(roundedDecimalParts[1] || '', scale, fixedDecimalScale); | ||
var negation = hasNegation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
} | ||
/** set the caret positon in an input field **/ | ||
function setCaretPosition(el , caretPos ) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
if (el.createTextRange) { | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
function setCaretPosition(el, caretPos) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
/* @ts-ignore */ | ||
if (el.createTextRange) { | ||
/* @ts-ignore */ | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
function findChangeRange(prevValue, newValue) { | ||
var i = 0, j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) | ||
{ i++; } | ||
//check what has been changed from last | ||
while (prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i) { | ||
j++; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
} | ||
return { | ||
from: { start: i, end: prevLength - j }, | ||
to: { start: i, end: newLength - j }, | ||
}; | ||
} | ||
/** | ||
Given previous value and newValue it returns the index | ||
start - end to which values have changed. | ||
This function makes assumption about only consecutive | ||
characters are changed which is correct assumption for caret input. | ||
*/ | ||
function findChangedIndex(prevValue , newValue ) { | ||
var i = 0, | ||
j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) { i++; } | ||
//check what has been changed from last | ||
while ( | ||
prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i | ||
) { | ||
j++; | ||
} | ||
return { start: i, end: prevLength - j }; | ||
} | ||
/* | ||
Returns a number whose value is limited to the given range | ||
*/ | ||
function clamp(num , min , max ) { | ||
return Math.min(Math.max(num, min), max); | ||
function clamp(num, min, max) { | ||
return Math.min(Math.max(num, min), max); | ||
} | ||
function getCurrentCaretPosition(el ) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
function geInputCaretPosition(el) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
} | ||
function addInputMode(format ) { | ||
return ( | ||
format || | ||
(typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))) | ||
); | ||
function addInputMode() { | ||
return (typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))); | ||
} | ||
// | ||
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } | ||
var defaultProps = { | ||
displayType: 'input', | ||
decimalSeparator: '.', | ||
thousandsGroupStyle: 'thousand', | ||
fixedDecimalScale: false, | ||
prefix: '', | ||
suffix: '', | ||
allowNegative: true, | ||
allowEmptyFormatting: false, | ||
allowLeadingZeros: false, | ||
isNumericString: false, | ||
type: 'text', | ||
onValueChange: noop, | ||
onChange: noop, | ||
onKeyDown: noop, | ||
onMouseUp: noop, | ||
onFocus: noop, | ||
onBlur: noop, | ||
isAllowed: returnTrue, | ||
}; | ||
var NumberFormat = /*@__PURE__*/(function (superclass) { | ||
function NumberFormat(props ) { | ||
superclass.call(this, props); | ||
var defaultValue = props.defaultValue; | ||
//validate props | ||
this.validateProps(); | ||
var formattedValue = this.formatValueProp(defaultValue); | ||
this.state = { | ||
value: formattedValue, | ||
numAsString: this.removeFormatting(formattedValue), | ||
mounted: false, | ||
}; | ||
this.selectionBeforeInput = { | ||
selectionStart: 0, | ||
selectionEnd: 0, | ||
}; | ||
this.onChange = this.onChange.bind(this); | ||
this.onKeyDown = this.onKeyDown.bind(this); | ||
this.onMouseUp = this.onMouseUp.bind(this); | ||
this.onFocus = this.onFocus.bind(this); | ||
this.onBlur = this.onBlur.bind(this); | ||
} | ||
if ( superclass ) NumberFormat.__proto__ = superclass; | ||
NumberFormat.prototype = Object.create( superclass && superclass.prototype ); | ||
NumberFormat.prototype.constructor = NumberFormat; | ||
NumberFormat.prototype.componentDidMount = function componentDidMount () { | ||
// set mounted state | ||
// eslint-disable-next-line react/no-did-mount-set-state | ||
this.setState({ | ||
mounted: true, | ||
}); | ||
}; | ||
NumberFormat.prototype.componentDidUpdate = function componentDidUpdate (prevProps ) { | ||
this.updateValueIfRequired(prevProps); | ||
}; | ||
NumberFormat.prototype.componentWillUnmount = function componentWillUnmount () { | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
}; | ||
NumberFormat.prototype.updateValueIfRequired = function updateValueIfRequired (prevProps ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var focusedElm = ref.focusedElm; | ||
var stateValue = state.value; | ||
var lastNumStr = state.numAsString; if ( lastNumStr === void 0 ) lastNumStr = ''; | ||
// If only state changed no need to do any thing | ||
if (prevProps !== props) { | ||
//validate props | ||
this.validateProps(); | ||
var lastValueWithNewFormat = this.formatNumString(lastNumStr); | ||
var formattedValue = isNil(props.value) ? lastValueWithNewFormat : this.formatValueProp(); | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var floatValue = parseFloat(numAsString); | ||
var lastFloatValue = parseFloat(lastNumStr); | ||
if ( | ||
//while typing set state only when float value changes | ||
((!isNaN(floatValue) || !isNaN(lastFloatValue)) && floatValue !== lastFloatValue) || | ||
//can also set state when float value is same and the format props changes | ||
lastValueWithNewFormat !== stateValue || | ||
//set state always when not in focus and formatted value is changed | ||
(focusedElm === null && formattedValue !== stateValue) | ||
) { | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: focusedElm, | ||
source: 'prop', | ||
event: null, | ||
}); | ||
} | ||
} | ||
}; | ||
/** Misc methods **/ | ||
NumberFormat.prototype.getFloatString = function getFloatString (num) { | ||
if ( num === void 0 ) num = ''; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var numRegex = this.getNumberRegex(true); | ||
//remove negation for regex check | ||
var hasNegation = num[0] === '-'; | ||
if (hasNegation) { num = num.replace('-', ''); } | ||
//if decimal scale is zero remove decimal and number after decimalSeparator | ||
if (decimalSeparator && decimalScale === 0) { | ||
num = num.split(decimalSeparator)[0]; | ||
} | ||
num = (num.match(numRegex) || []).join('').replace(decimalSeparator, '.'); | ||
//remove extra decimals | ||
var firstDecimalIndex = num.indexOf('.'); | ||
if (firstDecimalIndex !== -1) { | ||
num = (num.substring(0, firstDecimalIndex)) + "." + (num | ||
.substring(firstDecimalIndex + 1, num.length) | ||
.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), '')); | ||
} | ||
//add negation back | ||
if (hasNegation) { num = '-' + num; } | ||
return num; | ||
}; | ||
//returned regex assumes decimalSeparator is as per prop | ||
NumberFormat.prototype.getNumberRegex = function getNumberRegex (g , ignoreDecimalSeparator ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var customNumerals = ref.customNumerals; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
return new RegExp( | ||
'[0-9' + | ||
(customNumerals ? customNumerals.join('') : '') + | ||
']' + | ||
(decimalSeparator && decimalScale !== 0 && !ignoreDecimalSeparator && !format | ||
? '|' + escapeRegExp(decimalSeparator) | ||
: ''), | ||
g ? 'g' : undefined | ||
); | ||
}; | ||
NumberFormat.prototype.getSeparators = function getSeparators () { | ||
var ref = this.props; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var ref$1 = this.props; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
function getDefaultChangeMeta(value) { | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
from: { | ||
start: 0, | ||
end: 0, | ||
}, | ||
to: { | ||
start: 0, | ||
end: value.length, | ||
}, | ||
lastValue: '', | ||
}; | ||
}; | ||
} | ||
function getMaskAtIndex(mask, index) { | ||
if ( mask === void 0 ) mask = ' '; | ||
NumberFormat.prototype.getMaskAtIndex = function getMaskAtIndex (index ) { | ||
var ref = this.props; | ||
var mask = ref.mask; if ( mask === void 0 ) mask = ' '; | ||
if (typeof mask === 'string') { | ||
return mask; | ||
return mask; | ||
} | ||
return mask[index] || ' '; | ||
}; | ||
NumberFormat.prototype.getValueObject = function getValueObject (formattedValue , numAsString ) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
NumberFormat.prototype.validateProps = function validateProps () { | ||
var ref = this.props; | ||
var mask = ref.mask; | ||
//validate decimalSeparator and thousandSeparator | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
function getCaretPosition(formattedValue, curValue, curCaretPos) { | ||
var curValLn = curValue.length; | ||
var formattedValueLn = formattedValue.length; | ||
// create index map | ||
var addedIndexMap = {}; | ||
var indexMap = new Array(curValLn); | ||
for (var i = 0; i < curValLn; i++) { | ||
indexMap[i] = -1; | ||
for (var j = 0, jLn = formattedValueLn; j < jLn; j++) { | ||
if (curValue[i] === formattedValue[j] && addedIndexMap[j] !== true) { | ||
indexMap[i] = j; | ||
addedIndexMap[j] = true; | ||
break; | ||
} | ||
} | ||
} | ||
//validate mask | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("\n Mask " + mask + " should not contain numeric character;\n ")); | ||
} | ||
/** | ||
* For current caret position find closest characters (left and right side) | ||
* which are properly mapped to formatted value. | ||
* The idea is that the new caret position will exist always in the boundary of | ||
* that mapped index | ||
*/ | ||
var pos = curCaretPos; | ||
while (pos < curValLn && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) { | ||
pos++; | ||
} | ||
}; | ||
/** Misc methods end **/ | ||
/** caret specific methods **/ | ||
NumberFormat.prototype.setPatchedCaretPosition = function setPatchedCaretPosition (el , caretPos , currentValue ) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
this.caretPositionTimeout = setTimeout(function () { | ||
if (el.value === currentValue) { setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
NumberFormat.prototype.correctCaretPosition = function correctCaretPosition (value , caretPos , direction ) { | ||
var ref = this.props; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
//if value is empty return 0 | ||
if (value === '') { return 0; } | ||
//caret position should be between 0 and value length | ||
caretPos = clamp(caretPos, 0, value.length); | ||
//in case of format as number limit between prefix and suffix | ||
if (!format) { | ||
var hasNegation = value[0] === '-'; | ||
return clamp(caretPos, prefix.length + (hasNegation ? 1 : 0), value.length - suffix.length); | ||
// if the caret position is on last keep the endIndex as last for formatted value | ||
var endIndex = pos === curValLn || indexMap[pos] === -1 ? formattedValueLn : indexMap[pos]; | ||
pos = curCaretPos - 1; | ||
while (pos > 0 && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) | ||
{ pos--; } | ||
var startIndex = pos === -1 || indexMap[pos] === -1 ? 0 : indexMap[pos] + 1; | ||
/** | ||
* case where a char is added on suffix and removed from middle, example 2sq345 becoming $2,345 sq | ||
* there is still a mapping but the order of start index and end index is changed | ||
*/ | ||
if (startIndex > endIndex) | ||
{ return endIndex; } | ||
/** | ||
* given the current caret position if it closer to startIndex | ||
* keep the new caret position on start index or keep it closer to endIndex | ||
*/ | ||
return curCaretPos - startIndex < endIndex - curCaretPos ? startIndex : endIndex; | ||
} | ||
function caretUnknownFormatBoundary(formattedValue) { | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
boundaryAry[i] = Boolean(charIsNumber(formattedValue[i]) || charIsNumber(formattedValue[i - 1])); | ||
} | ||
return boundaryAry; | ||
} | ||
function useInternalValues(value, defaultValue, isNumericString, format, removeFormatting, onValueChange) { | ||
if ( onValueChange === void 0 ) onValueChange = noop; | ||
//in case if custom format method don't do anything | ||
if (typeof format === 'function') { return caretPos; } | ||
var propValues = useRef(); | ||
var getValues = usePersistentCallback(function (value) { | ||
var formattedValue, numAsString; | ||
if (isNil(value) || isNanValue(value)) { | ||
numAsString = ''; | ||
formattedValue = ''; | ||
} | ||
else if (typeof value === 'number' || isNumericString) { | ||
numAsString = typeof value === 'number' ? toNumericString(value) : value; | ||
formattedValue = format(numAsString); | ||
} | ||
else { | ||
numAsString = removeFormatting(value, undefined); | ||
formattedValue = value; | ||
} | ||
return { formattedValue: formattedValue, numAsString: numAsString }; | ||
}); | ||
var ref = useState(function () { | ||
return getValues(defaultValue); | ||
}); | ||
var values = ref[0]; | ||
var setValues = ref[1]; | ||
var _onValueChange = function (values, sourceInfo) { | ||
setValues({ | ||
formattedValue: values.formattedValue, | ||
numAsString: values.value, | ||
}); | ||
onValueChange(values, sourceInfo); | ||
}; | ||
useMemo(function () { | ||
//if element is moved to uncontrolled mode, don't reset the value | ||
if (!isNil(value)) { | ||
propValues.current = getValues(value); | ||
setValues(propValues.current); | ||
} | ||
else { | ||
propValues.current = undefined; | ||
} | ||
}, [value, getValues]); | ||
return [values, _onValueChange]; | ||
} | ||
/* in case format is string find the closest # position from the caret position */ | ||
//in case the caretPos have input value on it don't do anything | ||
if (format[caretPos] === '#' && charIsNumber(value[caretPos])) { | ||
return caretPos; | ||
function defaultRemoveFormatting(value) { | ||
return value.replace(/[^0-9]/g, ''); | ||
} | ||
function defaultFormat(value) { | ||
return value; | ||
} | ||
function NumberFormatBase(props) { | ||
var type = props.type; if ( type === void 0 ) type = 'text'; | ||
var displayType = props.displayType; if ( displayType === void 0 ) displayType = 'input'; | ||
var customInput = props.customInput; | ||
var renderText = props.renderText; | ||
var getInputRef = props.getInputRef; | ||
var format = props.format; if ( format === void 0 ) format = defaultFormat; | ||
var removeFormatting = props.removeFormatting; if ( removeFormatting === void 0 ) removeFormatting = defaultRemoveFormatting; | ||
var defaultValue = props.defaultValue; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var isAllowed = props.isAllowed; | ||
var onChange = props.onChange; if ( onChange === void 0 ) onChange = noop; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onMouseUp = props.onMouseUp; if ( onMouseUp === void 0 ) onMouseUp = noop; | ||
var onFocus = props.onFocus; if ( onFocus === void 0 ) onFocus = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var propValue = props.value; | ||
var getCaretBoundary = props.getCaretBoundary; if ( getCaretBoundary === void 0 ) getCaretBoundary = caretUnknownFormatBoundary; | ||
var otherProps = __rest(props, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "removeFormatting", "defaultValue", "isNumericString", "onValueChange", "isAllowed", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value", "getCaretBoundary"]); | ||
var ref = useInternalValues(propValue, defaultValue, isNumericString, format, removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var formattedValue = ref_0.formattedValue; | ||
var numAsString = ref_0.numAsString; | ||
var onFormattedValueChange = ref[1]; | ||
var lastUpdatedValue = useRef(); | ||
var _onValueChange = function (values, source) { | ||
lastUpdatedValue.current = values.formattedValue; | ||
onFormattedValueChange(values, source); | ||
}; | ||
// check if there is any change in the value due to props change | ||
useEffect(function () { | ||
var newFormattedValue = format(numAsString); | ||
// if the formatted value is not synced to parent, or if the formatted value is different | ||
if (lastUpdatedValue.current === undefined || newFormattedValue !== lastUpdatedValue.current) { | ||
var input = focusedElm.current; | ||
updateValue({ | ||
formattedValue: newFormattedValue, | ||
numAsString: numAsString, | ||
input: input, | ||
setCaretPosition: true, | ||
source: SourceType.props, | ||
event: null, | ||
}); | ||
} | ||
}); | ||
var ref$1 = useState(false); | ||
var mounted = ref$1[0]; | ||
var setMounted = ref$1[1]; | ||
var focusedElm = useRef(null); | ||
var timeout = useRef({ | ||
setCaretTimeout: null, | ||
focusTimeout: null, | ||
}); | ||
useEffect(function () { | ||
setMounted(true); | ||
return function () { | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
clearTimeout(timeout.current.focusTimeout); | ||
}; | ||
}, []); | ||
var _format = format; | ||
var getValueObject = function (formattedValue, numAsString) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
var setPatchedCaretPosition = function (el, caretPos, currentValue) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
timeout.current.setCaretTimeout = setTimeout(function () { | ||
if (el.value === currentValue) | ||
{ setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
var correctCaretPosition = function (value, caretPos, direction) { | ||
var valLn = value.length; | ||
// clamp caret position to [0, value.length] | ||
caretPos = clamp(caretPos, 0, valLn); | ||
var boundary = getCaretBoundary(value); | ||
if (direction === 'left') { | ||
while (caretPos >= 0 && !boundary[caretPos]) | ||
{ caretPos--; } | ||
// if we don't find any suitable caret position on left, set it on first allowed position | ||
if (caretPos === -1) | ||
{ caretPos = boundary.indexOf(true); } | ||
} | ||
else { | ||
while (caretPos <= valLn && !boundary[caretPos]) | ||
{ caretPos++; } | ||
// if we don't find any suitable caret position on right, set it on last allowed position | ||
if (caretPos > valLn) | ||
{ caretPos = boundary.lastIndexOf(true); } | ||
} | ||
// if we still don't find caret position, set it at the end of value | ||
if (caretPos === -1) | ||
{ caretPos = valLn; } | ||
return caretPos; | ||
}; | ||
var getNewCaretPosition = function (inputValue, formattedValue, caretPos) { | ||
var updatedCaretPos = getCaretPosition(formattedValue, inputValue, caretPos); | ||
//correct caret position if its outside of editable area | ||
updatedCaretPos = correctCaretPosition(formattedValue, updatedCaretPos); | ||
return updatedCaretPos; | ||
}; | ||
var updateValue = function (params) { | ||
var newFormattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = geInputCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = newFormattedValue; | ||
//get the caret position | ||
caretPos = getNewCaretPosition(inputValue, newFormattedValue, currentCaretPosition); | ||
} | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
*/ | ||
input.value = newFormattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
setPatchedCaretPosition(input, caretPos, newFormattedValue); | ||
} | ||
} | ||
if (newFormattedValue !== formattedValue) { | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
_onValueChange(getValueObject(newFormattedValue, numAsString), { event: event, source: source }); | ||
} | ||
}; | ||
var formatInputValue = function (inputValue, event, source) { | ||
var changeRange = findChangeRange(formattedValue, inputValue); | ||
var changeMeta = Object.assign(Object.assign({}, changeRange), { lastValue: formattedValue }); | ||
var _numAsString = removeFormatting(inputValue, changeMeta); | ||
var _formattedValue = _format(_numAsString); | ||
if (isAllowed && !isAllowed(getValueObject(_formattedValue, _numAsString))) { | ||
return false; | ||
} | ||
updateValue({ | ||
formattedValue: _formattedValue, | ||
numAsString: _numAsString, | ||
inputValue: inputValue, | ||
event: event, | ||
source: source, | ||
setCaretPosition: true, | ||
input: event.target, | ||
}); | ||
}; | ||
var _onChange = function (e) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var changed = formatInputValue(inputValue, e, SourceType.event); | ||
if (changed) | ||
{ onChange(e); } | ||
}; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = Math.max(selectionStart - 1, 0); | ||
} | ||
else if (key === 'ArrowRight') { | ||
expectedCaretPosition = Math.min(selectionStart + 1, value.length); | ||
} | ||
else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
// also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, direction); | ||
} | ||
else if (key === 'Delete' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of delete go to closest caret boundary on the right side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'right'); | ||
} | ||
else if (key === 'Backspace' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of backspace go to closest caret boundary on the left side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'left'); | ||
} | ||
if (newCaretPosition !== expectedCaretPosition) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
/* @ts-ignore */ | ||
if (e.isUnitTestRun) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
var _onMouseUp = function (e) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
} | ||
onMouseUp(e); | ||
}; | ||
var _onFocus = function (e) { | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
var el = e.target; | ||
focusedElm.current = el; | ||
timeout.current.focusTimeout = setTimeout(function () { | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if (caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length)) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
onFocus(e); | ||
}, 0); | ||
}; | ||
var _onBlur = function (e) { | ||
focusedElm.current = null; | ||
clearTimeout(timeout.current.focusTimeout); | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
onBlur(e); | ||
}; | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode() ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: formattedValue, | ||
onChange: _onChange, | ||
onKeyDown: _onKeyDown, | ||
onMouseUp: _onMouseUp, | ||
onFocus: _onFocus, | ||
onBlur: _onBlur, | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? (React.createElement(React.Fragment, null, renderText(formattedValue, otherProps) || null)) : (React.createElement("span", Object.assign({}, otherProps, { ref: getInputRef }), formattedValue)); | ||
} | ||
//if caretPos is just after input value don't do anything | ||
if (format[caretPos - 1] === '#' && charIsNumber(value[caretPos - 1])) { | ||
return caretPos; | ||
else if (customInput) { | ||
var CustomInput = customInput; | ||
/* @ts-ignore */ | ||
return React.createElement(CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
return React.createElement("input", Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
//find the nearest caret position | ||
var firstHashPosition = format.indexOf('#'); | ||
var lastHashPosition = format.lastIndexOf('#'); | ||
//limit the cursor between the first # position and the last # position | ||
caretPos = clamp(caretPos, firstHashPosition, lastHashPosition + 1); | ||
var nextPos = format.substring(caretPos, format.length).indexOf('#'); | ||
var caretLeftBound = caretPos; | ||
var caretRightBound = caretPos + (nextPos === -1 ? 0 : nextPos); | ||
//get the position where the last number is present | ||
while ( | ||
caretLeftBound > firstHashPosition && | ||
(format[caretLeftBound] !== '#' || !charIsNumber(value[caretLeftBound])) | ||
) { | ||
caretLeftBound -= 1; | ||
function format(numStr, props) { | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; if ( thousandsGroupStyle === void 0 ) thousandsGroupStyle = 'thousand'; | ||
// don't apply formatting on empty string or '-' | ||
if (numStr === '' || numStr === '-') { | ||
return numStr; | ||
} | ||
var goToLeft = | ||
!charIsNumber(value[caretRightBound]) || | ||
(direction === 'left' && caretPos !== firstHashPosition) || | ||
caretPos - caretLeftBound < caretRightBound - caretPos; | ||
if (goToLeft) { | ||
//check if number should be taken after the bound or after it | ||
//if number preceding a valid number keep it after | ||
return charIsNumber(value[caretLeftBound]) ? caretLeftBound + 1 : caretLeftBound; | ||
} | ||
return caretRightBound; | ||
}; | ||
NumberFormat.prototype.getCaretPosition = function getCaretPosition (inputValue , formattedValue , caretPos ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var stateValue = this.state.value; | ||
var numRegex = this.getNumberRegex(true); | ||
var inputNumber = (inputValue.match(numRegex) || []).join(''); | ||
var formattedNumber = (formattedValue.match(numRegex) || []).join(''); | ||
var j, i; | ||
j = 0; | ||
for (i = 0; i < caretPos; i++) { | ||
var currentInputChar = inputValue[i] || ''; | ||
var currentFormatChar = formattedValue[j] || ''; | ||
//no need to increase new cursor position if formatted value does not have those characters | ||
//case inputValue = 1a23 and formattedValue = 123 | ||
if (!currentInputChar.match(numRegex) && currentInputChar !== currentFormatChar) { | ||
continue; | ||
} | ||
//When we are striping out leading zeros maintain the new cursor position | ||
//Case inputValue = 00023 and formattedValue = 23; | ||
if ( | ||
currentInputChar === '0' && | ||
currentFormatChar.match(numRegex) && | ||
currentFormatChar !== '0' && | ||
inputNumber.length !== formattedNumber.length | ||
) { | ||
continue; | ||
} | ||
//we are not using currentFormatChar because j can change here | ||
while (currentInputChar !== formattedValue[j] && j < formattedValue.length) { | ||
j++; | ||
} | ||
j++; | ||
} | ||
if (typeof format === 'string' && !stateValue) { | ||
//set it to the maximum value so it goes after the last number | ||
j = formattedValue.length; | ||
} | ||
//correct caret position if its outside of editable area | ||
j = this.correctCaretPosition(formattedValue, j); | ||
return j; | ||
}; | ||
/** caret specific methods ends **/ | ||
/** methods to remove formattting **/ | ||
NumberFormat.prototype.removePrefixAndSuffix = function removePrefixAndSuffix (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
//remove prefix and suffix | ||
if (!format && val) { | ||
var isNegative = val[0] === '-'; | ||
//remove negation sign | ||
if (isNegative) { val = val.substring(1, val.length); } | ||
//remove prefix | ||
val = prefix && val.indexOf(prefix) === 0 ? val.substring(prefix.length, val.length) : val; | ||
//remove suffix | ||
var suffixLastIndex = val.lastIndexOf(suffix); | ||
val = | ||
suffix && suffixLastIndex !== -1 && suffixLastIndex === val.length - suffix.length | ||
? val.substring(0, suffixLastIndex) | ||
: val; | ||
//add negation sign back | ||
if (isNegative) { val = '-' + val; } | ||
} | ||
return val; | ||
}; | ||
NumberFormat.prototype.removePatternFormatting = function removePatternFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var formatArray = format.split('#').filter(function (str) { return str !== ''; }); | ||
var start = 0; | ||
var numStr = ''; | ||
for (var i = 0, ln = formatArray.length; i <= ln; i++) { | ||
var part = formatArray[i] || ''; | ||
//if i is the last fragment take the index of end of the value | ||
//For case like +1 (911) 911 91 91 having pattern +1 (###) ### ## ## | ||
var index = i === ln ? val.length : val.indexOf(part, start); | ||
/* in any case if we don't find the pattern part in the value assume the val as numeric string | ||
This will be also in case if user has started typing, in any other case it will not be -1 | ||
unless wrong prop value is provided */ | ||
if (index === -1) { | ||
numStr = val; | ||
break; | ||
} else { | ||
numStr += val.substring(start, index); | ||
start = index + part.length; | ||
} | ||
} | ||
return (numStr.match(this.getNumberRegex(true)) || []).join(''); | ||
}; | ||
NumberFormat.prototype.removeFormatting = function removeFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var removeFormatting = ref.removeFormatting; | ||
if (!val) { return val; } | ||
if (!format) { | ||
val = this.removePrefixAndSuffix(val); | ||
val = this.getFloatString(val); | ||
} else if (typeof format === 'string') { | ||
val = this.removePatternFormatting(val); | ||
} else if (typeof removeFormatting === 'function') { | ||
//condition need to be handled if format method is provide, | ||
val = removeFormatting(val); | ||
} else { | ||
val = (val.match(this.getNumberRegex(true)) || []).join(''); | ||
} | ||
return val; | ||
}; | ||
/** methods to remove formattting end **/ | ||
/*** format specific methods start ***/ | ||
/** | ||
* Format when # based string is provided | ||
* @param {string} numStr Numeric String | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatWithPattern = function formatWithPattern (numStr ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || this.getMaskAtIndex(hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
}; | ||
/** | ||
* @param {string} numStr Numeric string/floatString] It always have decimalSeparator as . | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatAsNumber = function formatAsNumber (numStr ) { | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var allowNegative = ref.allowNegative; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var ref$1 = this.getSeparators(); | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var hasDecimalSeparator = numStr.indexOf('.') !== -1 || (decimalScale && fixedDecimalScale); | ||
var ref$2 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$2.beforeDecimal; | ||
var afterDecimal = ref$2.afterDecimal; | ||
var addNegation = ref$2.addNegation; // eslint-disable-line prefer-const | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
/** | ||
* Keep the decimal separator | ||
* when decimalScale is not defined or non zero and the numStr has decimal in it | ||
* Or if decimalScale is > 0 and fixeDecimalScale is true (even if numStr has no decimal) | ||
*/ | ||
var hasDecimalSeparator = (decimalScale !== 0 && numStr.indexOf('.') !== -1) || (decimalScale && fixedDecimalScale); | ||
var ref$1 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//apply decimal precision if its defined | ||
if (decimalScale !== undefined) { | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
} | ||
if (thousandSeparator) { | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
} | ||
//add prefix and suffix | ||
if (prefix) { beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) { afterDecimal = afterDecimal + suffix; } | ||
//add prefix and suffix when there is a number present | ||
if (prefix) | ||
{ beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) | ||
{ afterDecimal = afterDecimal + suffix; } | ||
//restore negation sign | ||
if (addNegation) { beforeDecimal = '-' + beforeDecimal; } | ||
if (addNegation) | ||
{ beforeDecimal = '-' + beforeDecimal; } | ||
numStr = beforeDecimal + ((hasDecimalSeparator && decimalSeparator) || '') + afterDecimal; | ||
return numStr; | ||
}; | ||
NumberFormat.prototype.formatNumString = function formatNumString (numStr) { | ||
if ( numStr === void 0 ) numStr = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var customNumerals = ref.customNumerals; | ||
var formattedValue = numStr; | ||
if (customNumerals && customNumerals.length === 10) { | ||
var customNumeralRegex = new RegExp('[' + customNumerals.join('') + ']', 'g'); | ||
formattedValue = numStr.replace(customNumeralRegex, function (digit) { return customNumerals.indexOf(digit).toString(); } | ||
); | ||
} | ||
function getSeparators(props) { | ||
var decimalSeparator = props.decimalSeparator; if ( decimalSeparator === void 0 ) decimalSeparator = '.'; | ||
var thousandSeparator = props.thousandSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (numStr === '' && !allowEmptyFormatting) { | ||
formattedValue = ''; | ||
} else if (numStr === '-' && !format) { | ||
formattedValue = '-'; | ||
} else if (typeof format === 'string') { | ||
formattedValue = this.formatWithPattern(formattedValue); | ||
} else if (typeof format === 'function') { | ||
formattedValue = format(formattedValue); | ||
} else { | ||
formattedValue = this.formatAsNumber(formattedValue); | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
}; | ||
} | ||
function handleNegation(value, allowNegative) { | ||
if ( value === void 0 ) value = ''; | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatValueProp = function formatValueProp (defaultValue ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var ref$1 = this.props; | ||
var value = ref$1.value; | ||
var isNumericString = ref$1.isNumericString; | ||
// if value is undefined or null, use defaultValue instead | ||
value = isNil(value) ? defaultValue : value; | ||
var isNonNumericFalsy = !value && value !== 0; | ||
if (isNonNumericFalsy && allowEmptyFormatting) { | ||
value = ''; | ||
} | ||
// if value is not defined return empty string | ||
if (isNonNumericFalsy && !allowEmptyFormatting) { return ''; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
isNumericString = true; | ||
} | ||
//change infinity value to empty string | ||
if (value === 'Infinity' && isNumericString) { | ||
value = ''; | ||
} | ||
//round the number based on decimalScale | ||
//format only if non formatted value is provided | ||
if (isNumericString && !format && typeof decimalScale === 'number') { | ||
value = roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
var formattedValue = isNumericString ? this.formatNumString(value) : this.formatInput(value); | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatNegation = function formatNegation (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var allowNegative = ref.allowNegative; | ||
var negationRegex = new RegExp('(-)'); | ||
var doubleNegationRegex = new RegExp('(-)(.)*(-)'); | ||
// Check number has '-' value | ||
var hasNegation = negationRegex.test(value); | ||
// Check number has 2 or more '-' values | ||
var removeNegation = doubleNegationRegex.test(value); | ||
//remove negation | ||
value = value.replace(/-/g, ''); | ||
if (hasNegation && !removeNegation && allowNegative) { | ||
value = '-' + value; | ||
value = '-' + value; | ||
} | ||
return value; | ||
}; | ||
} | ||
function getNumberRegex(decimalSeparator, decimalScale, global) { | ||
return new RegExp(("(^-)|[0-9]|" + (escapeRegExp(decimalSeparator))), global ? 'g' : undefined); | ||
} | ||
function removeFormatting(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
NumberFormat.prototype.formatInput = function formatInput (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
//format negation only if we are formatting as number | ||
if (!format) { | ||
value = this.removePrefixAndSuffix(value); | ||
value = this.formatNegation(value); | ||
} | ||
//remove formatting from number | ||
value = this.removeFormatting(value); | ||
return this.formatNumString(value); | ||
}; | ||
/*** format specific methods end ***/ | ||
NumberFormat.prototype.isCharacterAFormat = function isCharacterAFormat (caretPos , value ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
//check within format pattern | ||
if (typeof format === 'string' && format[caretPos] !== '#') { return true; } | ||
//check in number format | ||
if ( | ||
!format && | ||
(caretPos < prefix.length || | ||
caretPos >= value.length - suffix.length || | ||
(decimalScale && fixedDecimalScale && value[caretPos] === decimalSeparator)) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* This will check if any formatting got removed by the delete or backspace and reset the value | ||
* It will also work as fallback if android chome keyDown handler does not work | ||
**/ | ||
NumberFormat.prototype.correctInputValue = function correctInputValue (caretPos , lastValue , value ) { | ||
var this$1 = this; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowNegative = ref.allowNegative; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var lastNumStr = this.state.numAsString || ''; | ||
var ref$2 = this.selectionBeforeInput; | ||
var selectionStart = ref$2.selectionStart; | ||
var selectionEnd = ref$2.selectionEnd; | ||
var ref$3 = findChangedIndex(lastValue, value); | ||
var start = ref$3.start; | ||
var end = ref$3.end; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var decimalScale = props.decimalScale; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var start = to.start; | ||
var end = to.end; | ||
var ref = getSeparators(props); | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var isBeforeDecimalSeparator = value[end] === decimalSeparator; | ||
/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */ | ||
if ( | ||
!format && | ||
start === end && | ||
allowedDecimalSeparators.indexOf(value[selectionStart]) !== -1 | ||
) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
return ( | ||
value.substr(0, selectionStart) + separator + value.substr(selectionStart + 1, value.length) | ||
); | ||
if (end - start === 1 && allowedDecimalSeparators.indexOf(value[start]) !== -1) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
value = value.substring(0, start) + separator + value.substring(start + 1, value.length); | ||
} | ||
var leftBound = !!format ? 0 : prefix.length; | ||
var rightBound = lastValue.length - (!!format ? 0 : suffix.length); | ||
if ( | ||
// don't do anything if something got added | ||
value.length > lastValue.length || | ||
// or if the new value is an empty string | ||
!value.length || | ||
// or if nothing has changed, in which case start will be same as end | ||
start === end || | ||
// or in case if whole input is selected and new value is typed | ||
(selectionStart === 0 && selectionEnd === lastValue.length) || | ||
// or in case if the whole content is replaced by browser, example (autocomplete) | ||
(start === 0 && end === lastValue.length) || | ||
// or if charcters between prefix and suffix is selected. | ||
// For numeric inputs we apply the format so, prefix and suffix can be ignored | ||
(selectionStart === leftBound && selectionEnd === rightBound) | ||
) { | ||
return value; | ||
var hasNegation = false; | ||
/** | ||
* if prefix starts with - the number hast to have two - at the start | ||
* if suffix starts with - and the value length is same as suffix length, then the - sign is from the suffix | ||
* In other cases, if the value starts with - then it is a negation | ||
*/ | ||
if (prefix.startsWith('-')) | ||
{ hasNegation = value.startsWith('--'); } | ||
else if (suffix.startsWith('-') && value.length === suffix.length) | ||
{ hasNegation = false; } | ||
else if (value[0] === '-') | ||
{ hasNegation = true; } | ||
// remove negation from start to simplify prefix logic as negation comes before prefix | ||
if (hasNegation) { | ||
value = value.substring(1); | ||
// account for the removal of the negation for start and end index | ||
start -= 1; | ||
end -= 1; | ||
} | ||
// check whether the deleted portion has a character that is part of a format | ||
var deletedValues = lastValue.substr(start, end - start); | ||
var formatGotDeleted = !![].concat( deletedValues ).find(function (deletedVal, idx) { return this$1.isCharacterAFormat(idx + start, lastValue); } | ||
); | ||
// if it has, only remove characters that are not part of the format | ||
if (formatGotDeleted) { | ||
var deletedValuePortion = lastValue.substr(start); | ||
var recordIndexOfFormatCharacters = {}; | ||
var resolvedPortion = []; | ||
[].concat( deletedValuePortion ).forEach(function (currentPortion, idx) { | ||
if (this$1.isCharacterAFormat(idx + start, lastValue)) { | ||
recordIndexOfFormatCharacters[idx] = currentPortion; | ||
} else if (idx > deletedValues.length - 1) { | ||
resolvedPortion.push(currentPortion); | ||
} | ||
}); | ||
Object.keys(recordIndexOfFormatCharacters).forEach(function (idx) { | ||
if (resolvedPortion.length > idx) { | ||
resolvedPortion.splice(idx, 0, recordIndexOfFormatCharacters[idx]); | ||
} else { | ||
resolvedPortion.push(recordIndexOfFormatCharacters[idx]); | ||
} | ||
}); | ||
value = lastValue.substr(0, start) + resolvedPortion.join(''); | ||
} | ||
//for numbers check if beforeDecimal got deleted and there is nothing after decimal, | ||
/** | ||
* remove prefix | ||
* Remove whole prefix part if its present on the value | ||
* If the prefix is partially deleted (in which case change start index will be less the prefix length) | ||
* Remove only partial part of prefix. | ||
*/ | ||
var startIndex = 0; | ||
if (value.startsWith(prefix)) | ||
{ startIndex += prefix.length; } | ||
else if (start < prefix.length) | ||
{ startIndex = start; } | ||
value = value.substring(startIndex); | ||
// account for deleted prefix for end index | ||
end -= startIndex; | ||
/** | ||
* Remove suffix | ||
* Remove whole suffix part if its present on the value | ||
* If the suffix is partially deleted (in which case change end index will be greater than the suffixStartIndex) | ||
* remove the partial part of suffix | ||
*/ | ||
var endIndex = value.length; | ||
var suffixStartIndex = value.length - suffix.length; | ||
if (value.endsWith(suffix)) | ||
{ endIndex = suffixStartIndex; } | ||
else if (end > value.length - suffix.length) | ||
{ endIndex = end; } | ||
value = value.substring(0, endIndex); | ||
// add the negation back and handle for double negation | ||
value = handleNegation(hasNegation ? ("-" + value) : value, allowNegative); | ||
// remove non numeric characters | ||
value = (value.match(getNumberRegex(decimalSeparator, decimalScale, true)) || []).join(''); | ||
// replace the decimalSeparator with ., and only keep the first separator, ignore following ones | ||
var firstIndex = value.indexOf(decimalSeparator); | ||
value = value.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), function (match, index) { | ||
return index === firstIndex ? '.' : ''; | ||
}); | ||
//check if beforeDecimal got deleted and there is nothing after decimal, | ||
//clear all numbers in such case while keeping the - sign | ||
if (!format) { | ||
var numericString = this.removeFormatting(value); | ||
var ref$4 = splitDecimal( | ||
numericString, | ||
allowNegative | ||
); | ||
var beforeDecimal = ref$4.beforeDecimal; | ||
var afterDecimal = ref$4.afterDecimal; | ||
var addNegation = ref$4.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted | ||
var isBeforeDecimalPoint = caretPos < value.indexOf(decimalSeparator) + 1; | ||
if ( | ||
numericString.length < lastNumStr.length && | ||
isBeforeDecimalPoint && | ||
var ref$1 = splitDecimal(value, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted before decimal (cursor is before decimal) | ||
if (to.end - to.start < from.end - from.start && | ||
beforeDecimal === '' && | ||
!parseFloat(afterDecimal) | ||
) { | ||
return addNegation ? '-' : ''; | ||
} | ||
isBeforeDecimalSeparator && | ||
!parseFloat(afterDecimal)) { | ||
value = addNegation ? '-' : ''; | ||
} | ||
return value; | ||
}; | ||
/** Update value and caret position */ | ||
NumberFormat.prototype.updateValue = function updateValue (params | ||
) { | ||
var formattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
var ref = this.props; | ||
var onValueChange = ref.onValueChange; | ||
var ref$1 = this.state; | ||
var lastValue = ref$1.value; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = getCurrentCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = formattedValue; | ||
//get the caret position | ||
caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition); | ||
} | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
*/ | ||
input.value = formattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
this.setPatchedCaretPosition(input, caretPos, formattedValue); | ||
} | ||
} | ||
function getCaretBoundary(formattedValue, props) { | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hasNegation = formattedValue[0] === '-'; | ||
// fill for prefix and negation | ||
boundaryAry.fill(false, 0, prefix.length + (hasNegation ? 1 : 0)); | ||
// fill for suffix | ||
var valLn = formattedValue.length; | ||
boundaryAry.fill(false, valLn - suffix.length + 1, valLn + 1); | ||
return boundaryAry; | ||
} | ||
function validateProps(props) { | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (thousandSeparator === decimalSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
//calculate numeric string if not passed | ||
if (numAsString === undefined) { | ||
numAsString = this.removeFormatting(formattedValue); | ||
} | ||
function useNumericFormat(props) { | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
// validate props | ||
validateProps(props); | ||
var _format = function (numStr) { return format(numStr, props); }; | ||
var _removeFormatting = function (inputValue, changeMeta) { return removeFormatting(inputValue, changeMeta, props); }; | ||
var _isNumericString = isNumericString; | ||
if (!isNil(value)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof value === 'number'; | ||
} | ||
//update state if value is changed | ||
if (formattedValue !== lastValue) { | ||
this.setState({ value: formattedValue, numAsString: numAsString }); | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
onValueChange(this.getValueObject(formattedValue, numAsString), { event: event, source: source }); | ||
else if (!isNil(defaultValue)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof defaultValue === 'number'; | ||
} | ||
}; | ||
var roundIncomingValueToPrecision = function (value) { | ||
if (isNil(value) || isNanValue(value)) | ||
{ return value; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
} | ||
/** | ||
* only round numeric or float string values coming through props, | ||
* we don't need to do it for onChange events, as we want to prevent typing there | ||
*/ | ||
if (_isNumericString && typeof decimalScale === 'number') { | ||
return roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
return value; | ||
}; | ||
var ref = useInternalValues(roundIncomingValueToPrecision(value), roundIncomingValueToPrecision(defaultValue), _isNumericString, _format, _removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var numAsString = ref_0.numAsString; | ||
var formattedValue = ref_0.formattedValue; | ||
var _onValueChange = ref[1]; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if user hits backspace, while the cursor is before prefix, and the input has negation, remove the negation | ||
if (key === 'Backspace' && value[0] === '-' && selectionStart === prefix.length + 1) { | ||
// bring the cursor to after negation | ||
setCaretPosition(el, 1); | ||
} | ||
// don't allow user to delete decimal separator when decimalScale and fixedDecimalScale is set | ||
var ref = getSeparators(props); | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (key === 'Backspace' && | ||
value[selectionStart - 1] === decimalSeparator && | ||
decimalScale && | ||
fixedDecimalScale) { | ||
setCaretPosition(el, selectionStart - 1); | ||
e.preventDefault(); | ||
} | ||
// move cursor when delete or backspace is pressed before/after thousand separator | ||
if (key === 'Backspace' && value[selectionStart - 1] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart - 1); | ||
} | ||
if (key === 'Delete' && value[selectionStart] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart + 1); | ||
} | ||
onKeyDown(e); | ||
}; | ||
var _onBlur = function (e) { | ||
var _value = numAsString; | ||
// if there no no numeric value, clear the input | ||
if (!_value.match(/\d/g)) { | ||
_value = ''; | ||
} | ||
// clear leading 0s | ||
if (!allowLeadingZeros) { | ||
_value = fixLeadingZero(_value); | ||
} | ||
// apply fixedDecimalScale on blur event | ||
if (fixedDecimalScale && decimalScale) { | ||
_value = roundToPrecision(_value, decimalScale, fixedDecimalScale); | ||
} | ||
if (_value !== numAsString) { | ||
var formattedValue = format(_value, props); | ||
_onValueChange({ | ||
formattedValue: formattedValue, | ||
value: _value, | ||
floatValue: parseFloat(_value), | ||
}, { | ||
event: e, | ||
source: SourceType.event, | ||
}); | ||
} | ||
onBlur(e); | ||
}; | ||
return { | ||
value: formattedValue, | ||
isNumericString: false, | ||
onValueChange: _onValueChange, | ||
format: _format, | ||
removeFormatting: _removeFormatting, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
onBlur: _onBlur, | ||
}; | ||
} | ||
function NumericFormat(props) { | ||
var decimalSeparator = props.decimalSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; | ||
var suffix = props.suffix; | ||
var allowNegative = props.allowNegative; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; | ||
var onBlur = props.onBlur; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var restProps = __rest(props, ["decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "suffix", "allowNegative", "allowLeadingZeros", "onKeyDown", "onBlur", "thousandSeparator", "decimalScale", "fixedDecimalScale", "prefix", "defaultValue", "value", "isNumericString", "onValueChange"]); | ||
var numericFormatProps = useNumericFormat(props); | ||
return React.createElement(NumberFormatBase, Object.assign({}, restProps, numericFormatProps)); | ||
} | ||
NumberFormat.prototype.onChange = function onChange (e ) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var ref = this; | ||
var state = ref.state; | ||
var props = ref.props; | ||
var isAllowed = props.isAllowed; | ||
var lastValue = state.value || ''; | ||
var currentCaretPosition = getCurrentCaretPosition(el); | ||
inputValue = this.correctInputValue(currentCaretPosition, lastValue, inputValue); | ||
var formattedValue = this.formatInput(inputValue) || ''; | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var valueObj = this.getValueObject(formattedValue, numAsString); | ||
var isChangeAllowed = isAllowed(valueObj); | ||
if (!isChangeAllowed) { | ||
formattedValue = lastValue; | ||
function format$1(numStr, props) { | ||
var format = props.format; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var mask = props.mask; | ||
if (numStr === '' && !allowEmptyFormatting) | ||
{ return ''; } | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || getMaskAtIndex(mask, hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
} | ||
function removeFormatting$1(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
inputValue: inputValue, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
if (isChangeAllowed) { | ||
props.onChange(e); | ||
} | ||
}; | ||
NumberFormat.prototype.onBlur = function onBlur (e ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var format = props.format; | ||
var onBlur = props.onBlur; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var numAsString = state.numAsString; | ||
var lastValue = state.value; | ||
this.focusedElm = null; | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
if (!format) { | ||
// if the numAsString is not a valid number reset it to empty | ||
if (isNaN(parseFloat(numAsString))) { | ||
numAsString = ''; | ||
} | ||
if (!allowLeadingZeros) { | ||
numAsString = fixLeadingZero(numAsString); | ||
} | ||
var formattedValue = this.formatNumString(numAsString); | ||
//change the state | ||
if (formattedValue !== lastValue) { | ||
// the event needs to be persisted because its properties can be accessed in an asynchronous way | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: e.target, | ||
setCaretPosition: false, | ||
event: e, | ||
source: 'event', | ||
}); | ||
onBlur(e); | ||
return; | ||
} | ||
} | ||
onBlur(e); | ||
}; | ||
NumberFormat.prototype.onKeyDown = function onKeyDown (e ) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
var onKeyDown = ref.onKeyDown; | ||
var ignoreDecimalSeparator = decimalScale !== undefined && fixedDecimalScale; | ||
var numRegex = this.getNumberRegex(false, ignoreDecimalSeparator); | ||
var negativeRegex = new RegExp('-'); | ||
var isPatternFormat = typeof format === 'string'; | ||
this.selectionBeforeInput = { | ||
selectionStart: selectionStart, | ||
selectionEnd: selectionEnd, | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var lastValue = changeMeta.lastValue; if ( lastValue === void 0 ) lastValue = ''; | ||
var isNumericSlot = function (caretPos) { return format[caretPos] === patternChar; }; | ||
var removeFormatChar = function (string, startIndex) { | ||
var str = ''; | ||
for (var i = 0; i < string.length; i++) { | ||
if (isNumericSlot(startIndex + i)) { | ||
str += string[i]; | ||
} | ||
} | ||
return str; | ||
}; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = selectionStart - 1; | ||
} else if (key === 'ArrowRight') { | ||
expectedCaretPosition = selectionStart + 1; | ||
} else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
var extractNumbers = function (str) { return str.replace(/[^0-9]/g, ''); }; | ||
// if format doesn't have any number, remove all the non numeric characters | ||
if (!format.match(/\d/)) { | ||
return extractNumbers(value); | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
//also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
var leftBound = isPatternFormat ? format.indexOf('#') : prefix.length; | ||
var rightBound = isPatternFormat ? format.lastIndexOf('#') + 1 : value.length - suffix.length; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = this.correctCaretPosition(value, expectedCaretPosition, direction); | ||
} else if ( | ||
key === 'Delete' && | ||
!numRegex.test(value[expectedCaretPosition]) && | ||
!negativeRegex.test(value[expectedCaretPosition]) | ||
) { | ||
while (!numRegex.test(value[newCaretPosition]) && newCaretPosition < rightBound) { | ||
newCaretPosition++; | ||
} | ||
} else if (key === 'Backspace' && !numRegex.test(value[expectedCaretPosition])) { | ||
/* NOTE: This is special case when backspace is pressed on a | ||
negative value while the cursor position is after prefix. We can't handle it on onChange because | ||
we will not have any information of keyPress | ||
*/ | ||
if (selectionStart <= leftBound + 1 && value[0] === '-' && typeof format === 'undefined') { | ||
var newValue = value.substring(1); | ||
this.updateValue({ | ||
formattedValue: newValue, | ||
caretPos: newCaretPosition, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
} else if (!negativeRegex.test(value[expectedCaretPosition])) { | ||
while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) { | ||
newCaretPosition--; | ||
/** | ||
* if user paste the whole formatted text in an empty input, check if matches to the pattern | ||
* and remove the format characters, if there is a mismatch on the pattern, do plane number extract | ||
*/ | ||
if (lastValue === '' && value.length === format.length) { | ||
var str = ''; | ||
for (var i = 0; i < value.length; i++) { | ||
if (isNumericSlot(i)) { | ||
str += value[i]; | ||
} | ||
else if (value[i] !== format[i]) { | ||
// if there is a mismatch on the pattern, do plane number extract | ||
return extractNumbers(value); | ||
} | ||
} | ||
newCaretPosition = this.correctCaretPosition(value, newCaretPosition, 'left'); | ||
} | ||
return str; | ||
} | ||
if ( | ||
newCaretPosition !== expectedCaretPosition || | ||
expectedCaretPosition < leftBound || | ||
expectedCaretPosition > rightBound | ||
) { | ||
e.preventDefault(); | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
if (e.isUnitTestRun) { | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
NumberFormat.prototype.onMouseUp = function onMouseUp (e ) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
* For partial change, | ||
* where ever there is a change on the input, we can break the number in three parts | ||
* 1st: left part which is unchanged | ||
* 2nd: middle part which is changed | ||
* 3rd: right part which is unchanged | ||
* | ||
* The first and third section will be same as last value, only the middle part will change | ||
* We can consider on the change part all the new characters are non format characters. | ||
* And on the first and last section it can have partial format characters. | ||
* | ||
* We pick first and last section from the lastValue (as that has 1-1 mapping with format) | ||
* and middle one from the update value. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = this.correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
this.setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
} | ||
this.props.onMouseUp(e); | ||
}; | ||
NumberFormat.prototype.onFocus = function onFocus (e ) { | ||
var this$1 = this; | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
this.focusedElm = e.target; | ||
this.focusTimeout = setTimeout(function () { | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = this$1.correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if ( | ||
caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length) | ||
) { | ||
this$1.setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
this$1.props.onFocus(e); | ||
}, 0); | ||
}; | ||
NumberFormat.prototype.render = function render () { | ||
var ref = this.props; | ||
var type = ref.type; | ||
var displayType = ref.displayType; | ||
var customInput = ref.customInput; | ||
var renderText = ref.renderText; | ||
var getInputRef = ref.getInputRef; | ||
var format = ref.format; | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var removeFormatting = ref.removeFormatting; | ||
var mask = ref.mask; | ||
var defaultValue = ref.defaultValue; | ||
var isNumericString = ref.isNumericString; | ||
var allowNegative = ref.allowNegative; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var allowLeadingZeros = ref.allowLeadingZeros; | ||
var onValueChange = ref.onValueChange; | ||
var isAllowed = ref.isAllowed; | ||
var customNumerals = ref.customNumerals; | ||
var onChange = ref.onChange; | ||
var onKeyDown = ref.onKeyDown; | ||
var onMouseUp = ref.onMouseUp; | ||
var onFocus = ref.onFocus; | ||
var onBlur = ref.onBlur; | ||
var propValue = ref.value; | ||
var rest = objectWithoutProperties( ref, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "thousandSeparator", "decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "decimalScale", "fixedDecimalScale", "prefix", "suffix", "removeFormatting", "mask", "defaultValue", "isNumericString", "allowNegative", "allowEmptyFormatting", "allowLeadingZeros", "onValueChange", "isAllowed", "customNumerals", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value"] ); | ||
var otherProps = rest; | ||
var ref$1 = this.state; | ||
var value = ref$1.value; | ||
var mounted = ref$1.mounted; | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode(format) ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: value, | ||
onChange: this.onChange, | ||
onKeyDown: this.onKeyDown, | ||
onMouseUp: this.onMouseUp, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
var firstSection = lastValue.substring(0, from.start); | ||
var middleSection = value.substring(to.start, to.end); | ||
var lastSection = lastValue.substring(from.end); | ||
return ("" + (removeFormatChar(firstSection, 0)) + (extractNumbers(middleSection)) + (removeFormatChar(lastSection, from.end))); | ||
} | ||
function getCaretBoundary$1(formattedValue, props) { | ||
var format = props.format; | ||
var mask = props.mask; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hashCount = 0; | ||
var maskAndFormatMap = format.split('').map(function (char) { | ||
if (char === patternChar) { | ||
hashCount++; | ||
return getMaskAtIndex(mask, hashCount - 1); | ||
} | ||
return undefined; | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? ( | ||
renderText(value, otherProps) || null | ||
) : ( | ||
React.createElement( 'span', Object.assign({}, otherProps, { ref: getInputRef }), | ||
value | ||
) | ||
); | ||
} else if (customInput) { | ||
var CustomInput = customInput; | ||
return React.createElement( CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
var isPosAllowed = function (pos) { | ||
// the position is allowed if the position is not masked and valid number area | ||
return format[pos] === patternChar && formattedValue[pos] !== maskAndFormatMap[pos]; | ||
}; | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
// Note: on pattern based format its denoted by patternCharacter | ||
boundaryAry[i] = isPosAllowed(i) || isPosAllowed(i - 1); | ||
} | ||
// the first patternChar position is always allowed | ||
boundaryAry[format.indexOf(patternChar)] = true; | ||
return boundaryAry; | ||
} | ||
function validateProps$1(props) { | ||
var mask = props.mask; | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("Mask " + mask + " should not contain numeric character;")); | ||
} | ||
} | ||
} | ||
function usePatternFormat(props) { | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; if ( inputMode === void 0 ) inputMode = 'numeric'; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
// validate props | ||
validateProps$1(props); | ||
var _onKeyDown = function (e) { | ||
var key = e.key; | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if backspace is pressed after the format characters, bring it to numeric section | ||
// if delete is pressed before the format characters, bring it to numeric section | ||
if (key === 'Backspace' || key === 'Delete') { | ||
// bring the cursor to closest numeric section | ||
var index = selectionStart; | ||
if (key === 'Backspace') { | ||
while (index > 0 && formatProp[index - 1] !== patternChar) { | ||
index--; | ||
} | ||
} | ||
else { | ||
var formatLn = formatProp.length; | ||
while (index < formatLn && formatProp[index] !== patternChar) { | ||
index++; | ||
} | ||
} | ||
if (index !== selectionStart) { | ||
setCaretPosition(el, index); | ||
} | ||
} | ||
onKeyDown(e); | ||
}; | ||
return { | ||
inputMode: inputMode, | ||
format: function (numStr) { return format$1(numStr, props); }, | ||
removeFormatting: function (inputValue, changeMeta) { return removeFormatting$1(inputValue, changeMeta, props); }, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary$1(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
}; | ||
} | ||
function PatternFormat(props) { | ||
var mask = props.mask; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; | ||
var onKeyDown = props.onKeyDown; | ||
var patternChar = props.patternChar; | ||
var restProps = __rest(props, ["mask", "allowEmptyFormatting", "format", "inputMode", "onKeyDown", "patternChar"]); | ||
var patternFormatProps = usePatternFormat(props); | ||
return React.createElement(NumberFormatBase, Object.assign({}, restProps, patternFormatProps)); | ||
} | ||
return React.createElement( 'input', Object.assign({}, inputProps, { ref: getInputRef })); | ||
}; | ||
return NumberFormat; | ||
}(React.Component)); | ||
NumberFormat.defaultProps = defaultProps; | ||
export default NumberFormat; | ||
export { NumberFormatBase, NumericFormat, PatternFormat, getCaretBoundary as getNumericCaretBoundary, getCaretBoundary$1 as getPatternCaretBoundary, format as numericFormatter, format$1 as patterFormatter, removeFormatting as removeNumericFormat, removeFormatting$1 as removePatternFormat, useNumericFormat, usePatternFormat }; |
/** | ||
* react-number-format - 4.9.3 | ||
* react-number-format - 5.0.0-beta.1 | ||
* Author : Sudhanshu Yadav | ||
@@ -9,1385 +9,1177 @@ * Copyright (c) 2016, 2022 to Sudhanshu Yadav, released under the MIT license. | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) : | ||
typeof define === 'function' && define.amd ? define(['react'], factory) : | ||
(global = global || self, global.NumberFormat = factory(global.React)); | ||
}(this, function (React) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : | ||
(global = global || self, factory(global.NumberFormat = {}, global.React)); | ||
}(this, (function (exports, React) { 'use strict'; | ||
React = React && React.hasOwnProperty('default') ? React['default'] : React; | ||
var React__default = 'default' in React ? React['default'] : React; | ||
// | ||
/****************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
// basic noop function | ||
function noop() {} | ||
function returnTrue() { | ||
return true; | ||
} | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function charIsNumber(char ) { | ||
return !!(char || '').match(/\d/); | ||
} | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
{ t[p] = s[p]; } } | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
{ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
{ t[p[i]] = s[p[i]]; } | ||
} } | ||
return t; | ||
} | ||
function isNil(val ) { | ||
return val === null || val === undefined; | ||
} | ||
var SourceType; | ||
(function (SourceType) { | ||
SourceType["event"] = "event"; | ||
SourceType["props"] = "prop"; | ||
})(SourceType || (SourceType = {})); | ||
function escapeRegExp(str ) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
} | ||
// basic noop function | ||
function noop() { } | ||
function charIsNumber(char) { | ||
return !!(char || '').match(/\d/); | ||
} | ||
function isNil(val) { | ||
return val === null || val === undefined; | ||
} | ||
function isNanValue(val) { | ||
return typeof val === 'number' && isNaN(val); | ||
} | ||
function escapeRegExp(str) { | ||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); | ||
} | ||
function getThousandsGroupRegex(thousandsGroupStyle) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
} | ||
} | ||
function applyThousandSeparator(str, thousandSeparator, thousandsGroupStyle) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return (str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator)); | ||
} | ||
function usePersistentCallback(cb) { | ||
var callbackRef = React.useRef(cb); | ||
// keep the callback ref upto date | ||
callbackRef.current = cb; | ||
/** | ||
* initialize a persistent callback which never changes | ||
* through out the component lifecycle | ||
*/ | ||
var persistentCbRef = React.useRef(function () { | ||
var args = [], len = arguments.length; | ||
while ( len-- ) args[ len ] = arguments[ len ]; | ||
function getThousandsGroupRegex(thousandsGroupStyle ) { | ||
switch (thousandsGroupStyle) { | ||
case 'lakh': | ||
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g; | ||
case 'wan': | ||
return /(\d)(?=(\d{4})+(?!\d))/g; | ||
case 'thousand': | ||
default: | ||
return /(\d)(?=(\d{3})+(?!\d))/g; | ||
return callbackRef.current.apply(callbackRef, args); | ||
}); | ||
return persistentCbRef.current; | ||
} | ||
} | ||
//spilt a float number into different parts beforeDecimal, afterDecimal, and negation | ||
function splitDecimal(numStr, allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
function applyThousandSeparator( | ||
str , | ||
thousandSeparator , | ||
thousandsGroupStyle | ||
) { | ||
var thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle); | ||
var index = str.search(/[1-9]/); | ||
index = index === -1 ? str.length : index; | ||
return ( | ||
str.substring(0, index) + | ||
str.substring(index, str.length).replace(thousandsGroupRegex, '$1' + thousandSeparator) | ||
); | ||
} | ||
//spilt a float number into different parts beforeDecimal, afterDecimal, and negation | ||
function splitDecimal(numStr , allowNegative) { | ||
if ( allowNegative === void 0 ) allowNegative = true; | ||
var hasNagation = numStr[0] === '-'; | ||
var addNegation = hasNagation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNagation: hasNagation, | ||
addNegation: addNegation, | ||
}; | ||
} | ||
function fixLeadingZero(numStr ) { | ||
if (!numStr) { return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) { numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
} | ||
/** | ||
* limit decimal numbers to given scale | ||
* Not used .fixedTo because that will break with big numbers | ||
*/ | ||
function limitToScale(numStr , scale , fixedDecimalScale ) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
var hasNegation = numStr[0] === '-'; | ||
var addNegation = hasNegation && allowNegative; | ||
numStr = numStr.replace('-', ''); | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0]; | ||
var afterDecimal = parts[1] || ''; | ||
return { | ||
beforeDecimal: beforeDecimal, | ||
afterDecimal: afterDecimal, | ||
hasNegation: hasNegation, | ||
addNegation: addNegation, | ||
}; | ||
} | ||
return str; | ||
} | ||
function repeat(str, count) { | ||
return Array(count + 1).join(str); | ||
} | ||
function toNumericString(num) { | ||
num += ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = num[0] === '-' ? '-' : ''; | ||
if (sign) { num = num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) { return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
function fixLeadingZero(numStr) { | ||
if (!numStr) | ||
{ return numStr; } | ||
var isNegative = numStr[0] === '-'; | ||
if (isNegative) | ||
{ numStr = numStr.substring(1, numStr.length); } | ||
var parts = numStr.split('.'); | ||
var beforeDecimal = parts[0].replace(/^0+/, '') || '0'; | ||
var afterDecimal = parts[1] || ''; | ||
return ("" + (isNegative ? '-' : '') + beforeDecimal + (afterDecimal ? ("." + afterDecimal) : '')); | ||
} | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
* limit decimal numbers to given scale | ||
* Not used .fixedTo because that will break with big numbers | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
function limitToScale(numStr, scale, fixedDecimalScale) { | ||
var str = ''; | ||
var filler = fixedDecimalScale ? '0' : ''; | ||
for (var i = 0; i <= scale - 1; i++) { | ||
str += numStr[i] || filler; | ||
} | ||
return str; | ||
} | ||
return sign + coefficient; | ||
} | ||
/** | ||
* This method is required to round prop value to given scale. | ||
* Not used .round or .fixedTo because that will break with big numbers | ||
*/ | ||
function roundToPrecision(numStr , scale , fixedDecimalScale ) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) { return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNagation = ref.hasNagation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = | ||
afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ( | ||
(Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length) | ||
); | ||
function repeat(str, count) { | ||
return Array(count + 1).join(str); | ||
} | ||
function toNumericString(num) { | ||
var _num = num + ''; // typecast number to string | ||
// store the sign and remove it from the number. | ||
var sign = _num[0] === '-' ? '-' : ''; | ||
if (sign) | ||
{ _num = _num.substring(1); } | ||
// split the number into cofficient and exponent | ||
var ref = _num.split(/[eE]/g); | ||
var coefficient = ref[0]; | ||
var exponent = ref[1]; | ||
// covert exponent to number; | ||
exponent = Number(exponent); | ||
// if there is no exponent part or its 0, return the coffiecient with sign | ||
if (!exponent) | ||
{ return sign + coefficient; } | ||
coefficient = coefficient.replace('.', ''); | ||
/** | ||
* for scientific notation the current decimal index will be after first number (index 0) | ||
* So effective decimal index will always be 1 + exponent value | ||
*/ | ||
var decimalIndex = 1 + exponent; | ||
var coffiecientLn = coefficient.length; | ||
if (decimalIndex < 0) { | ||
// if decimal index is less then 0 add preceding 0s | ||
// add 1 as join will have | ||
coefficient = '0.' + repeat('0', Math.abs(decimalIndex)) + coefficient; | ||
} | ||
return current + roundedStr; | ||
}, roundedDecimalParts[0]); | ||
var decimalPart = limitToScale( | ||
roundedDecimalParts[1] || '', | ||
Math.min(scale, afterDecimal.length), | ||
fixedDecimalScale | ||
); | ||
var negation = hasNagation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
} | ||
/** set the caret positon in an input field **/ | ||
function setCaretPosition(el , caretPos ) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
if (el.createTextRange) { | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
else if (decimalIndex >= coffiecientLn) { | ||
// if decimal index is less then 0 add leading 0s | ||
coefficient = coefficient + repeat('0', decimalIndex - coffiecientLn); | ||
} | ||
else { | ||
// else add decimal point at proper index | ||
coefficient = | ||
(coefficient.substring(0, decimalIndex) || '0') + '.' + coefficient.substring(decimalIndex); | ||
} | ||
return sign + coefficient; | ||
} | ||
} | ||
/** | ||
Given previous value and newValue it returns the index | ||
start - end to which values have changed. | ||
This function makes assumption about only consecutive | ||
characters are changed which is correct assumption for caret input. | ||
*/ | ||
function findChangedIndex(prevValue , newValue ) { | ||
var i = 0, | ||
j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) { i++; } | ||
//check what has been changed from last | ||
while ( | ||
prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i | ||
) { | ||
j++; | ||
/** | ||
* This method is required to round prop value to given scale. | ||
* Not used .round or .fixedTo because that will break with big numbers | ||
*/ | ||
function roundToPrecision(numStr, scale, fixedDecimalScale) { | ||
//if number is empty don't do anything return empty string | ||
if (['', '-'].indexOf(numStr) !== -1) | ||
{ return numStr; } | ||
var shoudHaveDecimalSeparator = numStr.indexOf('.') !== -1 && scale; | ||
var ref = splitDecimal(numStr); | ||
var beforeDecimal = ref.beforeDecimal; | ||
var afterDecimal = ref.afterDecimal; | ||
var hasNegation = ref.hasNegation; | ||
var floatValue = parseFloat(("0." + (afterDecimal || '0'))); | ||
var floatValueStr = afterDecimal.length <= scale ? ("0." + afterDecimal) : floatValue.toFixed(scale); | ||
var roundedDecimalParts = floatValueStr.split('.'); | ||
var intPart = beforeDecimal | ||
.split('') | ||
.reverse() | ||
.reduce(function (roundedStr, current, idx) { | ||
if (roundedStr.length > idx) { | ||
return ((Number(roundedStr[0]) + Number(current)).toString() + | ||
roundedStr.substring(1, roundedStr.length)); | ||
} | ||
return current + roundedStr; | ||
}, roundedDecimalParts[0]); | ||
var decimalPart = limitToScale(roundedDecimalParts[1] || '', scale, fixedDecimalScale); | ||
var negation = hasNegation ? '-' : ''; | ||
var decimalSeparator = shoudHaveDecimalSeparator ? '.' : ''; | ||
return ("" + negation + intPart + decimalSeparator + decimalPart); | ||
} | ||
return { start: i, end: prevLength - j }; | ||
} | ||
/* | ||
Returns a number whose value is limited to the given range | ||
*/ | ||
function clamp(num , min , max ) { | ||
return Math.min(Math.max(num, min), max); | ||
} | ||
function getCurrentCaretPosition(el ) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
} | ||
function addInputMode(format ) { | ||
return ( | ||
format || | ||
(typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))) | ||
); | ||
} | ||
// | ||
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } | ||
var defaultProps = { | ||
displayType: 'input', | ||
decimalSeparator: '.', | ||
thousandsGroupStyle: 'thousand', | ||
fixedDecimalScale: false, | ||
prefix: '', | ||
suffix: '', | ||
allowNegative: true, | ||
allowEmptyFormatting: false, | ||
allowLeadingZeros: false, | ||
isNumericString: false, | ||
type: 'text', | ||
onValueChange: noop, | ||
onChange: noop, | ||
onKeyDown: noop, | ||
onMouseUp: noop, | ||
onFocus: noop, | ||
onBlur: noop, | ||
isAllowed: returnTrue, | ||
}; | ||
var NumberFormat = /*@__PURE__*/(function (superclass) { | ||
function NumberFormat(props ) { | ||
superclass.call(this, props); | ||
var defaultValue = props.defaultValue; | ||
//validate props | ||
this.validateProps(); | ||
var formattedValue = this.formatValueProp(defaultValue); | ||
this.state = { | ||
value: formattedValue, | ||
numAsString: this.removeFormatting(formattedValue), | ||
mounted: false, | ||
}; | ||
this.selectionBeforeInput = { | ||
selectionStart: 0, | ||
selectionEnd: 0, | ||
}; | ||
this.onChange = this.onChange.bind(this); | ||
this.onKeyDown = this.onKeyDown.bind(this); | ||
this.onMouseUp = this.onMouseUp.bind(this); | ||
this.onFocus = this.onFocus.bind(this); | ||
this.onBlur = this.onBlur.bind(this); | ||
/** set the caret positon in an input field **/ | ||
function setCaretPosition(el, caretPos) { | ||
el.value = el.value; | ||
// ^ this is used to not only get 'focus', but | ||
// to make sure we don't have it everything -selected- | ||
// (it causes an issue in chrome, and having it doesn't hurt any other browser) | ||
if (el !== null) { | ||
/* @ts-ignore */ | ||
if (el.createTextRange) { | ||
/* @ts-ignore */ | ||
var range = el.createTextRange(); | ||
range.move('character', caretPos); | ||
range.select(); | ||
return true; | ||
} | ||
// (el.selectionStart === 0 added for Firefox bug) | ||
if (el.selectionStart || el.selectionStart === 0) { | ||
el.focus(); | ||
el.setSelectionRange(caretPos, caretPos); | ||
return true; | ||
} | ||
// fail city, fortunately this never happens (as far as I've tested) :) | ||
el.focus(); | ||
return false; | ||
} | ||
} | ||
if ( superclass ) NumberFormat.__proto__ = superclass; | ||
NumberFormat.prototype = Object.create( superclass && superclass.prototype ); | ||
NumberFormat.prototype.constructor = NumberFormat; | ||
NumberFormat.prototype.componentDidMount = function componentDidMount () { | ||
// set mounted state | ||
// eslint-disable-next-line react/no-did-mount-set-state | ||
this.setState({ | ||
mounted: true, | ||
}); | ||
}; | ||
NumberFormat.prototype.componentDidUpdate = function componentDidUpdate (prevProps ) { | ||
this.updateValueIfRequired(prevProps); | ||
}; | ||
NumberFormat.prototype.componentWillUnmount = function componentWillUnmount () { | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
}; | ||
NumberFormat.prototype.updateValueIfRequired = function updateValueIfRequired (prevProps ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var focusedElm = ref.focusedElm; | ||
var stateValue = state.value; | ||
var lastNumStr = state.numAsString; if ( lastNumStr === void 0 ) lastNumStr = ''; | ||
// If only state changed no need to do any thing | ||
if (prevProps !== props) { | ||
//validate props | ||
this.validateProps(); | ||
var lastValueWithNewFormat = this.formatNumString(lastNumStr); | ||
var formattedValue = isNil(props.value) ? lastValueWithNewFormat : this.formatValueProp(); | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var floatValue = parseFloat(numAsString); | ||
var lastFloatValue = parseFloat(lastNumStr); | ||
if ( | ||
//while typing set state only when float value changes | ||
((!isNaN(floatValue) || !isNaN(lastFloatValue)) && floatValue !== lastFloatValue) || | ||
//can also set state when float value is same and the format props changes | ||
lastValueWithNewFormat !== stateValue || | ||
//set state always when not in focus and formatted value is changed | ||
(focusedElm === null && formattedValue !== stateValue) | ||
) { | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: focusedElm, | ||
source: 'prop', | ||
event: null, | ||
}); | ||
function findChangeRange(prevValue, newValue) { | ||
var i = 0, j = 0; | ||
var prevLength = prevValue.length; | ||
var newLength = newValue.length; | ||
while (prevValue[i] === newValue[i] && i < prevLength) | ||
{ i++; } | ||
//check what has been changed from last | ||
while (prevValue[prevLength - 1 - j] === newValue[newLength - 1 - j] && | ||
newLength - j > i && | ||
prevLength - j > i) { | ||
j++; | ||
} | ||
} | ||
}; | ||
return { | ||
from: { start: i, end: prevLength - j }, | ||
to: { start: i, end: newLength - j }, | ||
}; | ||
} | ||
/* | ||
Returns a number whose value is limited to the given range | ||
*/ | ||
function clamp(num, min, max) { | ||
return Math.min(Math.max(num, min), max); | ||
} | ||
function geInputCaretPosition(el) { | ||
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/ | ||
return Math.max(el.selectionStart, el.selectionEnd); | ||
} | ||
function addInputMode() { | ||
return (typeof navigator !== 'undefined' && | ||
!(navigator.platform && /iPhone|iPod/.test(navigator.platform))); | ||
} | ||
function getDefaultChangeMeta(value) { | ||
return { | ||
from: { | ||
start: 0, | ||
end: 0, | ||
}, | ||
to: { | ||
start: 0, | ||
end: value.length, | ||
}, | ||
lastValue: '', | ||
}; | ||
} | ||
function getMaskAtIndex(mask, index) { | ||
if ( mask === void 0 ) mask = ' '; | ||
/** Misc methods **/ | ||
NumberFormat.prototype.getFloatString = function getFloatString (num) { | ||
if ( num === void 0 ) num = ''; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var numRegex = this.getNumberRegex(true); | ||
//remove negation for regex check | ||
var hasNegation = num[0] === '-'; | ||
if (hasNegation) { num = num.replace('-', ''); } | ||
//if decimal scale is zero remove decimal and number after decimalSeparator | ||
if (decimalSeparator && decimalScale === 0) { | ||
num = num.split(decimalSeparator)[0]; | ||
} | ||
num = (num.match(numRegex) || []).join('').replace(decimalSeparator, '.'); | ||
//remove extra decimals | ||
var firstDecimalIndex = num.indexOf('.'); | ||
if (firstDecimalIndex !== -1) { | ||
num = (num.substring(0, firstDecimalIndex)) + "." + (num | ||
.substring(firstDecimalIndex + 1, num.length) | ||
.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), '')); | ||
} | ||
//add negation back | ||
if (hasNegation) { num = '-' + num; } | ||
return num; | ||
}; | ||
//returned regex assumes decimalSeparator is as per prop | ||
NumberFormat.prototype.getNumberRegex = function getNumberRegex (g , ignoreDecimalSeparator ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var customNumerals = ref.customNumerals; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
return new RegExp( | ||
'[0-9' + | ||
(customNumerals ? customNumerals.join('') : '') + | ||
']' + | ||
(decimalSeparator && decimalScale !== 0 && !ignoreDecimalSeparator && !format | ||
? '|' + escapeRegExp(decimalSeparator) | ||
: ''), | ||
g ? 'g' : undefined | ||
); | ||
}; | ||
NumberFormat.prototype.getSeparators = function getSeparators () { | ||
var ref = this.props; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var ref$1 = this.props; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
}; | ||
}; | ||
NumberFormat.prototype.getMaskAtIndex = function getMaskAtIndex (index ) { | ||
var ref = this.props; | ||
var mask = ref.mask; if ( mask === void 0 ) mask = ' '; | ||
if (typeof mask === 'string') { | ||
return mask; | ||
} | ||
return mask[index] || ' '; | ||
}; | ||
NumberFormat.prototype.getValueObject = function getValueObject (formattedValue , numAsString ) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
NumberFormat.prototype.validateProps = function validateProps () { | ||
var ref = this.props; | ||
var mask = ref.mask; | ||
//validate decimalSeparator and thousandSeparator | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
if (decimalSeparator === thousandSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
//validate mask | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("\n Mask " + mask + " should not contain numeric character;\n ")); | ||
if (typeof mask === 'string') { | ||
return mask; | ||
} | ||
} | ||
}; | ||
/** Misc methods end **/ | ||
/** caret specific methods **/ | ||
NumberFormat.prototype.setPatchedCaretPosition = function setPatchedCaretPosition (el , caretPos , currentValue ) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
this.caretPositionTimeout = setTimeout(function () { | ||
if (el.value === currentValue) { setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
NumberFormat.prototype.correctCaretPosition = function correctCaretPosition (value , caretPos , direction ) { | ||
var ref = this.props; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
//if value is empty return 0 | ||
if (value === '') { return 0; } | ||
//caret position should be between 0 and value length | ||
caretPos = clamp(caretPos, 0, value.length); | ||
//in case of format as number limit between prefix and suffix | ||
if (!format) { | ||
var hasNegation = value[0] === '-'; | ||
return clamp(caretPos, prefix.length + (hasNegation ? 1 : 0), value.length - suffix.length); | ||
} | ||
//in case if custom format method don't do anything | ||
if (typeof format === 'function') { return caretPos; } | ||
/* in case format is string find the closest # position from the caret position */ | ||
//in case the caretPos have input value on it don't do anything | ||
if (format[caretPos] === '#' && charIsNumber(value[caretPos])) { | ||
return caretPos; | ||
} | ||
//if caretPos is just after input value don't do anything | ||
if (format[caretPos - 1] === '#' && charIsNumber(value[caretPos - 1])) { | ||
return caretPos; | ||
} | ||
//find the nearest caret position | ||
var firstHashPosition = format.indexOf('#'); | ||
var lastHashPosition = format.lastIndexOf('#'); | ||
//limit the cursor between the first # position and the last # position | ||
caretPos = clamp(caretPos, firstHashPosition, lastHashPosition + 1); | ||
var nextPos = format.substring(caretPos, format.length).indexOf('#'); | ||
var caretLeftBound = caretPos; | ||
var caretRightBound = caretPos + (nextPos === -1 ? 0 : nextPos); | ||
//get the position where the last number is present | ||
while ( | ||
caretLeftBound > firstHashPosition && | ||
(format[caretLeftBound] !== '#' || !charIsNumber(value[caretLeftBound])) | ||
) { | ||
caretLeftBound -= 1; | ||
} | ||
var goToLeft = | ||
!charIsNumber(value[caretRightBound]) || | ||
(direction === 'left' && caretPos !== firstHashPosition) || | ||
caretPos - caretLeftBound < caretRightBound - caretPos; | ||
if (goToLeft) { | ||
//check if number should be taken after the bound or after it | ||
//if number preceding a valid number keep it after | ||
return charIsNumber(value[caretLeftBound]) ? caretLeftBound + 1 : caretLeftBound; | ||
} | ||
return caretRightBound; | ||
}; | ||
NumberFormat.prototype.getCaretPosition = function getCaretPosition (inputValue , formattedValue , caretPos ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var stateValue = this.state.value; | ||
var numRegex = this.getNumberRegex(true); | ||
var inputNumber = (inputValue.match(numRegex) || []).join(''); | ||
var formattedNumber = (formattedValue.match(numRegex) || []).join(''); | ||
var j, i; | ||
j = 0; | ||
for (i = 0; i < caretPos; i++) { | ||
var currentInputChar = inputValue[i] || ''; | ||
var currentFormatChar = formattedValue[j] || ''; | ||
//no need to increase new cursor position if formatted value does not have those characters | ||
//case inputValue = 1a23 and formattedValue = 123 | ||
if (!currentInputChar.match(numRegex) && currentInputChar !== currentFormatChar) { | ||
continue; | ||
return mask[index] || ' '; | ||
} | ||
function getCaretPosition(formattedValue, curValue, curCaretPos) { | ||
var curValLn = curValue.length; | ||
var formattedValueLn = formattedValue.length; | ||
// create index map | ||
var addedIndexMap = {}; | ||
var indexMap = new Array(curValLn); | ||
for (var i = 0; i < curValLn; i++) { | ||
indexMap[i] = -1; | ||
for (var j = 0, jLn = formattedValueLn; j < jLn; j++) { | ||
if (curValue[i] === formattedValue[j] && addedIndexMap[j] !== true) { | ||
indexMap[i] = j; | ||
addedIndexMap[j] = true; | ||
break; | ||
} | ||
} | ||
} | ||
//When we are striping out leading zeros maintain the new cursor position | ||
//Case inputValue = 00023 and formattedValue = 23; | ||
if ( | ||
currentInputChar === '0' && | ||
currentFormatChar.match(numRegex) && | ||
currentFormatChar !== '0' && | ||
inputNumber.length !== formattedNumber.length | ||
) { | ||
continue; | ||
/** | ||
* For current caret position find closest characters (left and right side) | ||
* which are properly mapped to formatted value. | ||
* The idea is that the new caret position will exist always in the boundary of | ||
* that mapped index | ||
*/ | ||
var pos = curCaretPos; | ||
while (pos < curValLn && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) { | ||
pos++; | ||
} | ||
//we are not using currentFormatChar because j can change here | ||
while (currentInputChar !== formattedValue[j] && j < formattedValue.length) { | ||
j++; | ||
// if the caret position is on last keep the endIndex as last for formatted value | ||
var endIndex = pos === curValLn || indexMap[pos] === -1 ? formattedValueLn : indexMap[pos]; | ||
pos = curCaretPos - 1; | ||
while (pos > 0 && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) | ||
{ pos--; } | ||
var startIndex = pos === -1 || indexMap[pos] === -1 ? 0 : indexMap[pos] + 1; | ||
/** | ||
* case where a char is added on suffix and removed from middle, example 2sq345 becoming $2,345 sq | ||
* there is still a mapping but the order of start index and end index is changed | ||
*/ | ||
if (startIndex > endIndex) | ||
{ return endIndex; } | ||
/** | ||
* given the current caret position if it closer to startIndex | ||
* keep the new caret position on start index or keep it closer to endIndex | ||
*/ | ||
return curCaretPos - startIndex < endIndex - curCaretPos ? startIndex : endIndex; | ||
} | ||
function caretUnknownFormatBoundary(formattedValue) { | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
boundaryAry[i] = Boolean(charIsNumber(formattedValue[i]) || charIsNumber(formattedValue[i - 1])); | ||
} | ||
j++; | ||
} | ||
return boundaryAry; | ||
} | ||
function useInternalValues(value, defaultValue, isNumericString, format, removeFormatting, onValueChange) { | ||
if ( onValueChange === void 0 ) onValueChange = noop; | ||
if (typeof format === 'string' && !stateValue) { | ||
//set it to the maximum value so it goes after the last number | ||
j = formattedValue.length; | ||
} | ||
var propValues = React.useRef(); | ||
var getValues = usePersistentCallback(function (value) { | ||
var formattedValue, numAsString; | ||
if (isNil(value) || isNanValue(value)) { | ||
numAsString = ''; | ||
formattedValue = ''; | ||
} | ||
else if (typeof value === 'number' || isNumericString) { | ||
numAsString = typeof value === 'number' ? toNumericString(value) : value; | ||
formattedValue = format(numAsString); | ||
} | ||
else { | ||
numAsString = removeFormatting(value, undefined); | ||
formattedValue = value; | ||
} | ||
return { formattedValue: formattedValue, numAsString: numAsString }; | ||
}); | ||
var ref = React.useState(function () { | ||
return getValues(defaultValue); | ||
}); | ||
var values = ref[0]; | ||
var setValues = ref[1]; | ||
var _onValueChange = function (values, sourceInfo) { | ||
setValues({ | ||
formattedValue: values.formattedValue, | ||
numAsString: values.value, | ||
}); | ||
onValueChange(values, sourceInfo); | ||
}; | ||
React.useMemo(function () { | ||
//if element is moved to uncontrolled mode, don't reset the value | ||
if (!isNil(value)) { | ||
propValues.current = getValues(value); | ||
setValues(propValues.current); | ||
} | ||
else { | ||
propValues.current = undefined; | ||
} | ||
}, [value, getValues]); | ||
return [values, _onValueChange]; | ||
} | ||
//correct caret position if its outside of editable area | ||
j = this.correctCaretPosition(formattedValue, j); | ||
return j; | ||
}; | ||
/** caret specific methods ends **/ | ||
/** methods to remove formattting **/ | ||
NumberFormat.prototype.removePrefixAndSuffix = function removePrefixAndSuffix (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
//remove prefix and suffix | ||
if (!format && val) { | ||
var isNegative = val[0] === '-'; | ||
//remove negation sign | ||
if (isNegative) { val = val.substring(1, val.length); } | ||
//remove prefix | ||
val = prefix && val.indexOf(prefix) === 0 ? val.substring(prefix.length, val.length) : val; | ||
//remove suffix | ||
var suffixLastIndex = val.lastIndexOf(suffix); | ||
val = | ||
suffix && suffixLastIndex !== -1 && suffixLastIndex === val.length - suffix.length | ||
? val.substring(0, suffixLastIndex) | ||
: val; | ||
//add negation sign back | ||
if (isNegative) { val = '-' + val; } | ||
} | ||
return val; | ||
}; | ||
NumberFormat.prototype.removePatternFormatting = function removePatternFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var formatArray = format.split('#').filter(function (str) { return str !== ''; }); | ||
var start = 0; | ||
var numStr = ''; | ||
for (var i = 0, ln = formatArray.length; i <= ln; i++) { | ||
var part = formatArray[i] || ''; | ||
//if i is the last fragment take the index of end of the value | ||
//For case like +1 (911) 911 91 91 having pattern +1 (###) ### ## ## | ||
var index = i === ln ? val.length : val.indexOf(part, start); | ||
/* in any case if we don't find the pattern part in the value assume the val as numeric string | ||
This will be also in case if user has started typing, in any other case it will not be -1 | ||
unless wrong prop value is provided */ | ||
if (index === -1) { | ||
numStr = val; | ||
break; | ||
} else { | ||
numStr += val.substring(start, index); | ||
start = index + part.length; | ||
} | ||
} | ||
return (numStr.match(this.getNumberRegex(true)) || []).join(''); | ||
}; | ||
NumberFormat.prototype.removeFormatting = function removeFormatting (val ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var removeFormatting = ref.removeFormatting; | ||
if (!val) { return val; } | ||
if (!format) { | ||
val = this.removePrefixAndSuffix(val); | ||
val = this.getFloatString(val); | ||
} else if (typeof format === 'string') { | ||
val = this.removePatternFormatting(val); | ||
} else if (typeof removeFormatting === 'function') { | ||
//condition need to be handled if format method is provide, | ||
val = removeFormatting(val); | ||
} else { | ||
val = (val.match(this.getNumberRegex(true)) || []).join(''); | ||
} | ||
return val; | ||
}; | ||
/** methods to remove formattting end **/ | ||
/*** format specific methods start ***/ | ||
/** | ||
* Format when # based string is provided | ||
* @param {string} numStr Numeric String | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatWithPattern = function formatWithPattern (numStr ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || this.getMaskAtIndex(hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
}; | ||
/** | ||
* @param {string} numStr Numeric string/floatString] It always have decimalSeparator as . | ||
* @return {string} formatted Value | ||
*/ | ||
NumberFormat.prototype.formatAsNumber = function formatAsNumber (numStr ) { | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var allowNegative = ref.allowNegative; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var ref$1 = this.getSeparators(); | ||
var thousandSeparator = ref$1.thousandSeparator; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var hasDecimalSeparator = numStr.indexOf('.') !== -1 || (decimalScale && fixedDecimalScale); | ||
var ref$2 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$2.beforeDecimal; | ||
var afterDecimal = ref$2.afterDecimal; | ||
var addNegation = ref$2.addNegation; // eslint-disable-line prefer-const | ||
//apply decimal precision if its defined | ||
if (decimalScale !== undefined) { | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
} | ||
if (thousandSeparator) { | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
} | ||
//add prefix and suffix | ||
if (prefix) { beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) { afterDecimal = afterDecimal + suffix; } | ||
//restore negation sign | ||
if (addNegation) { beforeDecimal = '-' + beforeDecimal; } | ||
numStr = beforeDecimal + ((hasDecimalSeparator && decimalSeparator) || '') + afterDecimal; | ||
return numStr; | ||
}; | ||
NumberFormat.prototype.formatNumString = function formatNumString (numStr) { | ||
if ( numStr === void 0 ) numStr = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var customNumerals = ref.customNumerals; | ||
var formattedValue = numStr; | ||
if (customNumerals && customNumerals.length === 10) { | ||
var customNumeralRegex = new RegExp('[' + customNumerals.join('') + ']', 'g'); | ||
formattedValue = numStr.replace(customNumeralRegex, function (digit) { return customNumerals.indexOf(digit).toString(); } | ||
); | ||
} | ||
if (numStr === '' && !allowEmptyFormatting) { | ||
formattedValue = ''; | ||
} else if (numStr === '-' && !format) { | ||
formattedValue = '-'; | ||
} else if (typeof format === 'string') { | ||
formattedValue = this.formatWithPattern(formattedValue); | ||
} else if (typeof format === 'function') { | ||
formattedValue = format(formattedValue); | ||
} else { | ||
formattedValue = this.formatAsNumber(formattedValue); | ||
} | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatValueProp = function formatValueProp (defaultValue ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var ref$1 = this.props; | ||
var value = ref$1.value; | ||
var isNumericString = ref$1.isNumericString; | ||
// if value is undefined or null, use defaultValue instead | ||
value = isNil(value) ? defaultValue : value; | ||
var isNonNumericFalsy = !value && value !== 0; | ||
if (isNonNumericFalsy && allowEmptyFormatting) { | ||
value = ''; | ||
} | ||
// if value is not defined return empty string | ||
if (isNonNumericFalsy && !allowEmptyFormatting) { return ''; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
isNumericString = true; | ||
} | ||
//change infinity value to empty string | ||
if (value === 'Infinity' && isNumericString) { | ||
value = ''; | ||
} | ||
//round the number based on decimalScale | ||
//format only if non formatted value is provided | ||
if (isNumericString && !format && typeof decimalScale === 'number') { | ||
value = roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
var formattedValue = isNumericString ? this.formatNumString(value) : this.formatInput(value); | ||
return formattedValue; | ||
}; | ||
NumberFormat.prototype.formatNegation = function formatNegation (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var allowNegative = ref.allowNegative; | ||
var negationRegex = new RegExp('(-)'); | ||
var doubleNegationRegex = new RegExp('(-)(.)*(-)'); | ||
// Check number has '-' value | ||
var hasNegation = negationRegex.test(value); | ||
// Check number has 2 or more '-' values | ||
var removeNegation = doubleNegationRegex.test(value); | ||
//remove negation | ||
value = value.replace(/-/g, ''); | ||
if (hasNegation && !removeNegation && allowNegative) { | ||
value = '-' + value; | ||
} | ||
return value; | ||
}; | ||
NumberFormat.prototype.formatInput = function formatInput (value) { | ||
if ( value === void 0 ) value = ''; | ||
var ref = this.props; | ||
var format = ref.format; | ||
//format negation only if we are formatting as number | ||
if (!format) { | ||
value = this.removePrefixAndSuffix(value); | ||
value = this.formatNegation(value); | ||
} | ||
//remove formatting from number | ||
value = this.removeFormatting(value); | ||
return this.formatNumString(value); | ||
}; | ||
/*** format specific methods end ***/ | ||
NumberFormat.prototype.isCharacterAFormat = function isCharacterAFormat (caretPos , value ) { | ||
var ref = this.props; | ||
var format = ref.format; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
//check within format pattern | ||
if (typeof format === 'string' && format[caretPos] !== '#') { return true; } | ||
//check in number format | ||
if ( | ||
!format && | ||
(caretPos < prefix.length || | ||
caretPos >= value.length - suffix.length || | ||
(decimalScale && fixedDecimalScale && value[caretPos] === decimalSeparator)) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* This will check if any formatting got removed by the delete or backspace and reset the value | ||
* It will also work as fallback if android chome keyDown handler does not work | ||
**/ | ||
NumberFormat.prototype.correctInputValue = function correctInputValue (caretPos , lastValue , value ) { | ||
var this$1 = this; | ||
var ref = this.props; | ||
var format = ref.format; | ||
var allowNegative = ref.allowNegative; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var decimalScale = ref.decimalScale; | ||
var ref$1 = this.getSeparators(); | ||
var allowedDecimalSeparators = ref$1.allowedDecimalSeparators; | ||
var decimalSeparator = ref$1.decimalSeparator; | ||
var lastNumStr = this.state.numAsString || ''; | ||
var ref$2 = this.selectionBeforeInput; | ||
var selectionStart = ref$2.selectionStart; | ||
var selectionEnd = ref$2.selectionEnd; | ||
var ref$3 = findChangedIndex(lastValue, value); | ||
var start = ref$3.start; | ||
var end = ref$3.end; | ||
/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */ | ||
if ( | ||
!format && | ||
start === end && | ||
allowedDecimalSeparators.indexOf(value[selectionStart]) !== -1 | ||
) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
return ( | ||
value.substr(0, selectionStart) + separator + value.substr(selectionStart + 1, value.length) | ||
); | ||
} | ||
var leftBound = !!format ? 0 : prefix.length; | ||
var rightBound = lastValue.length - (!!format ? 0 : suffix.length); | ||
if ( | ||
// don't do anything if something got added | ||
value.length > lastValue.length || | ||
// or if the new value is an empty string | ||
!value.length || | ||
// or if nothing has changed, in which case start will be same as end | ||
start === end || | ||
// or in case if whole input is selected and new value is typed | ||
(selectionStart === 0 && selectionEnd === lastValue.length) || | ||
// or in case if the whole content is replaced by browser, example (autocomplete) | ||
(start === 0 && end === lastValue.length) || | ||
// or if charcters between prefix and suffix is selected. | ||
// For numeric inputs we apply the format so, prefix and suffix can be ignored | ||
(selectionStart === leftBound && selectionEnd === rightBound) | ||
) { | ||
function defaultRemoveFormatting(value) { | ||
return value.replace(/[^0-9]/g, ''); | ||
} | ||
function defaultFormat(value) { | ||
return value; | ||
} | ||
// check whether the deleted portion has a character that is part of a format | ||
var deletedValues = lastValue.substr(start, end - start); | ||
var formatGotDeleted = !![].concat( deletedValues ).find(function (deletedVal, idx) { return this$1.isCharacterAFormat(idx + start, lastValue); } | ||
); | ||
// if it has, only remove characters that are not part of the format | ||
if (formatGotDeleted) { | ||
var deletedValuePortion = lastValue.substr(start); | ||
var recordIndexOfFormatCharacters = {}; | ||
var resolvedPortion = []; | ||
[].concat( deletedValuePortion ).forEach(function (currentPortion, idx) { | ||
if (this$1.isCharacterAFormat(idx + start, lastValue)) { | ||
recordIndexOfFormatCharacters[idx] = currentPortion; | ||
} else if (idx > deletedValues.length - 1) { | ||
resolvedPortion.push(currentPortion); | ||
} | ||
} | ||
function NumberFormatBase(props) { | ||
var type = props.type; if ( type === void 0 ) type = 'text'; | ||
var displayType = props.displayType; if ( displayType === void 0 ) displayType = 'input'; | ||
var customInput = props.customInput; | ||
var renderText = props.renderText; | ||
var getInputRef = props.getInputRef; | ||
var format = props.format; if ( format === void 0 ) format = defaultFormat; | ||
var removeFormatting = props.removeFormatting; if ( removeFormatting === void 0 ) removeFormatting = defaultRemoveFormatting; | ||
var defaultValue = props.defaultValue; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var isAllowed = props.isAllowed; | ||
var onChange = props.onChange; if ( onChange === void 0 ) onChange = noop; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onMouseUp = props.onMouseUp; if ( onMouseUp === void 0 ) onMouseUp = noop; | ||
var onFocus = props.onFocus; if ( onFocus === void 0 ) onFocus = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var propValue = props.value; | ||
var getCaretBoundary = props.getCaretBoundary; if ( getCaretBoundary === void 0 ) getCaretBoundary = caretUnknownFormatBoundary; | ||
var otherProps = __rest(props, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "removeFormatting", "defaultValue", "isNumericString", "onValueChange", "isAllowed", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value", "getCaretBoundary"]); | ||
var ref = useInternalValues(propValue, defaultValue, isNumericString, format, removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var formattedValue = ref_0.formattedValue; | ||
var numAsString = ref_0.numAsString; | ||
var onFormattedValueChange = ref[1]; | ||
var lastUpdatedValue = React.useRef(); | ||
var _onValueChange = function (values, source) { | ||
lastUpdatedValue.current = values.formattedValue; | ||
onFormattedValueChange(values, source); | ||
}; | ||
// check if there is any change in the value due to props change | ||
React.useEffect(function () { | ||
var newFormattedValue = format(numAsString); | ||
// if the formatted value is not synced to parent, or if the formatted value is different | ||
if (lastUpdatedValue.current === undefined || newFormattedValue !== lastUpdatedValue.current) { | ||
var input = focusedElm.current; | ||
updateValue({ | ||
formattedValue: newFormattedValue, | ||
numAsString: numAsString, | ||
input: input, | ||
setCaretPosition: true, | ||
source: SourceType.props, | ||
event: null, | ||
}); | ||
} | ||
}); | ||
Object.keys(recordIndexOfFormatCharacters).forEach(function (idx) { | ||
if (resolvedPortion.length > idx) { | ||
resolvedPortion.splice(idx, 0, recordIndexOfFormatCharacters[idx]); | ||
} else { | ||
resolvedPortion.push(recordIndexOfFormatCharacters[idx]); | ||
} | ||
var ref$1 = React.useState(false); | ||
var mounted = ref$1[0]; | ||
var setMounted = ref$1[1]; | ||
var focusedElm = React.useRef(null); | ||
var timeout = React.useRef({ | ||
setCaretTimeout: null, | ||
focusTimeout: null, | ||
}); | ||
value = lastValue.substr(0, start) + resolvedPortion.join(''); | ||
} | ||
//for numbers check if beforeDecimal got deleted and there is nothing after decimal, | ||
//clear all numbers in such case while keeping the - sign | ||
if (!format) { | ||
var numericString = this.removeFormatting(value); | ||
var ref$4 = splitDecimal( | ||
numericString, | ||
allowNegative | ||
); | ||
var beforeDecimal = ref$4.beforeDecimal; | ||
var afterDecimal = ref$4.afterDecimal; | ||
var addNegation = ref$4.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted | ||
var isBeforeDecimalPoint = caretPos < value.indexOf(decimalSeparator) + 1; | ||
if ( | ||
numericString.length < lastNumStr.length && | ||
isBeforeDecimalPoint && | ||
beforeDecimal === '' && | ||
!parseFloat(afterDecimal) | ||
) { | ||
return addNegation ? '-' : ''; | ||
React.useEffect(function () { | ||
setMounted(true); | ||
return function () { | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
clearTimeout(timeout.current.focusTimeout); | ||
}; | ||
}, []); | ||
var _format = format; | ||
var getValueObject = function (formattedValue, numAsString) { | ||
var floatValue = parseFloat(numAsString); | ||
return { | ||
formattedValue: formattedValue, | ||
value: numAsString, | ||
floatValue: isNaN(floatValue) ? undefined : floatValue, | ||
}; | ||
}; | ||
var setPatchedCaretPosition = function (el, caretPos, currentValue) { | ||
/* 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 */ | ||
setCaretPosition(el, caretPos); | ||
timeout.current.setCaretTimeout = setTimeout(function () { | ||
if (el.value === currentValue) | ||
{ setCaretPosition(el, caretPos); } | ||
}, 0); | ||
}; | ||
/* This keeps the caret within typing area so people can't type in between prefix or suffix */ | ||
var correctCaretPosition = function (value, caretPos, direction) { | ||
var valLn = value.length; | ||
// clamp caret position to [0, value.length] | ||
caretPos = clamp(caretPos, 0, valLn); | ||
var boundary = getCaretBoundary(value); | ||
if (direction === 'left') { | ||
while (caretPos >= 0 && !boundary[caretPos]) | ||
{ caretPos--; } | ||
// if we don't find any suitable caret position on left, set it on first allowed position | ||
if (caretPos === -1) | ||
{ caretPos = boundary.indexOf(true); } | ||
} | ||
else { | ||
while (caretPos <= valLn && !boundary[caretPos]) | ||
{ caretPos++; } | ||
// if we don't find any suitable caret position on right, set it on last allowed position | ||
if (caretPos > valLn) | ||
{ caretPos = boundary.lastIndexOf(true); } | ||
} | ||
// if we still don't find caret position, set it at the end of value | ||
if (caretPos === -1) | ||
{ caretPos = valLn; } | ||
return caretPos; | ||
}; | ||
var getNewCaretPosition = function (inputValue, formattedValue, caretPos) { | ||
var updatedCaretPos = getCaretPosition(formattedValue, inputValue, caretPos); | ||
//correct caret position if its outside of editable area | ||
updatedCaretPos = correctCaretPosition(formattedValue, updatedCaretPos); | ||
return updatedCaretPos; | ||
}; | ||
var updateValue = function (params) { | ||
var newFormattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = geInputCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = newFormattedValue; | ||
//get the caret position | ||
caretPos = getNewCaretPosition(inputValue, newFormattedValue, currentCaretPosition); | ||
} | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
*/ | ||
input.value = newFormattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
setPatchedCaretPosition(input, caretPos, newFormattedValue); | ||
} | ||
} | ||
if (newFormattedValue !== formattedValue) { | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
_onValueChange(getValueObject(newFormattedValue, numAsString), { event: event, source: source }); | ||
} | ||
}; | ||
var formatInputValue = function (inputValue, event, source) { | ||
var changeRange = findChangeRange(formattedValue, inputValue); | ||
var changeMeta = Object.assign(Object.assign({}, changeRange), { lastValue: formattedValue }); | ||
var _numAsString = removeFormatting(inputValue, changeMeta); | ||
var _formattedValue = _format(_numAsString); | ||
if (isAllowed && !isAllowed(getValueObject(_formattedValue, _numAsString))) { | ||
return false; | ||
} | ||
updateValue({ | ||
formattedValue: _formattedValue, | ||
numAsString: _numAsString, | ||
inputValue: inputValue, | ||
event: event, | ||
source: source, | ||
setCaretPosition: true, | ||
input: event.target, | ||
}); | ||
}; | ||
var _onChange = function (e) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var changed = formatInputValue(inputValue, e, SourceType.event); | ||
if (changed) | ||
{ onChange(e); } | ||
}; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = Math.max(selectionStart - 1, 0); | ||
} | ||
else if (key === 'ArrowRight') { | ||
expectedCaretPosition = Math.min(selectionStart + 1, value.length); | ||
} | ||
else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
// also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, direction); | ||
} | ||
else if (key === 'Delete' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of delete go to closest caret boundary on the right side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'right'); | ||
} | ||
else if (key === 'Backspace' && !charIsNumber(value[expectedCaretPosition])) { | ||
// in case of backspace go to closest caret boundary on the left side | ||
newCaretPosition = correctCaretPosition(value, expectedCaretPosition, 'left'); | ||
} | ||
if (newCaretPosition !== expectedCaretPosition) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
/* @ts-ignore */ | ||
if (e.isUnitTestRun) { | ||
setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
var _onMouseUp = function (e) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
} | ||
onMouseUp(e); | ||
}; | ||
var _onFocus = function (e) { | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
var el = e.target; | ||
focusedElm.current = el; | ||
timeout.current.focusTimeout = setTimeout(function () { | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if (caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length)) { | ||
setPatchedCaretPosition(el, caretPosition, value); | ||
} | ||
onFocus(e); | ||
}, 0); | ||
}; | ||
var _onBlur = function (e) { | ||
focusedElm.current = null; | ||
clearTimeout(timeout.current.focusTimeout); | ||
clearTimeout(timeout.current.setCaretTimeout); | ||
onBlur(e); | ||
}; | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode() ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: formattedValue, | ||
onChange: _onChange, | ||
onKeyDown: _onKeyDown, | ||
onMouseUp: _onMouseUp, | ||
onFocus: _onFocus, | ||
onBlur: _onBlur, | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? (React__default.createElement(React__default.Fragment, null, renderText(formattedValue, otherProps) || null)) : (React__default.createElement("span", Object.assign({}, otherProps, { ref: getInputRef }), formattedValue)); | ||
} | ||
} | ||
else if (customInput) { | ||
var CustomInput = customInput; | ||
/* @ts-ignore */ | ||
return React__default.createElement(CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
return React__default.createElement("input", Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
return value; | ||
}; | ||
/** Update value and caret position */ | ||
NumberFormat.prototype.updateValue = function updateValue (params | ||
) { | ||
var formattedValue = params.formattedValue; | ||
var input = params.input; | ||
var setCaretPosition = params.setCaretPosition; if ( setCaretPosition === void 0 ) setCaretPosition = true; | ||
var source = params.source; | ||
var event = params.event; | ||
var numAsString = params.numAsString; | ||
var caretPos = params.caretPos; | ||
var ref = this.props; | ||
var onValueChange = ref.onValueChange; | ||
var ref$1 = this.state; | ||
var lastValue = ref$1.value; | ||
if (input) { | ||
//calculate caret position if not defined | ||
if (caretPos === undefined && setCaretPosition) { | ||
var inputValue = params.inputValue || input.value; | ||
var currentCaretPosition = getCurrentCaretPosition(input); | ||
/** | ||
* set the value imperatively, this is required for IE fix | ||
* This is also required as if new caret position is beyond the previous value. | ||
* Caret position will not be set correctly | ||
*/ | ||
input.value = formattedValue; | ||
//get the caret position | ||
caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition); | ||
function format(numStr, props) { | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; if ( thousandsGroupStyle === void 0 ) thousandsGroupStyle = 'thousand'; | ||
// don't apply formatting on empty string or '-' | ||
if (numStr === '' || numStr === '-') { | ||
return numStr; | ||
} | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
/** | ||
* set the value imperatively, as we set the caret position as well imperatively. | ||
* This is to keep value and caret position in sync | ||
* Keep the decimal separator | ||
* when decimalScale is not defined or non zero and the numStr has decimal in it | ||
* Or if decimalScale is > 0 and fixeDecimalScale is true (even if numStr has no decimal) | ||
*/ | ||
input.value = formattedValue; | ||
//set caret position, and value imperatively when element is provided | ||
if (setCaretPosition) { | ||
//set caret position | ||
this.setPatchedCaretPosition(input, caretPos, formattedValue); | ||
var hasDecimalSeparator = (decimalScale !== 0 && numStr.indexOf('.') !== -1) || (decimalScale && fixedDecimalScale); | ||
var ref$1 = splitDecimal(numStr, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//apply decimal precision if its defined | ||
if (decimalScale !== undefined) { | ||
afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale); | ||
} | ||
} | ||
//calculate numeric string if not passed | ||
if (numAsString === undefined) { | ||
numAsString = this.removeFormatting(formattedValue); | ||
} | ||
//update state if value is changed | ||
if (formattedValue !== lastValue) { | ||
this.setState({ value: formattedValue, numAsString: numAsString }); | ||
// trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 | ||
onValueChange(this.getValueObject(formattedValue, numAsString), { event: event, source: source }); | ||
} | ||
}; | ||
NumberFormat.prototype.onChange = function onChange (e ) { | ||
var el = e.target; | ||
var inputValue = el.value; | ||
var ref = this; | ||
var state = ref.state; | ||
var props = ref.props; | ||
var isAllowed = props.isAllowed; | ||
var lastValue = state.value || ''; | ||
var currentCaretPosition = getCurrentCaretPosition(el); | ||
inputValue = this.correctInputValue(currentCaretPosition, lastValue, inputValue); | ||
var formattedValue = this.formatInput(inputValue) || ''; | ||
var numAsString = this.removeFormatting(formattedValue); | ||
var valueObj = this.getValueObject(formattedValue, numAsString); | ||
var isChangeAllowed = isAllowed(valueObj); | ||
if (!isChangeAllowed) { | ||
formattedValue = lastValue; | ||
} | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
inputValue: inputValue, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
if (isChangeAllowed) { | ||
props.onChange(e); | ||
} | ||
}; | ||
NumberFormat.prototype.onBlur = function onBlur (e ) { | ||
var ref = this; | ||
var props = ref.props; | ||
var state = ref.state; | ||
var format = props.format; | ||
var onBlur = props.onBlur; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var numAsString = state.numAsString; | ||
var lastValue = state.value; | ||
this.focusedElm = null; | ||
clearTimeout(this.focusTimeout); | ||
clearTimeout(this.caretPositionTimeout); | ||
if (!format) { | ||
// if the numAsString is not a valid number reset it to empty | ||
if (isNaN(parseFloat(numAsString))) { | ||
numAsString = ''; | ||
if (thousandSeparator) { | ||
beforeDecimal = applyThousandSeparator(beforeDecimal, thousandSeparator, thousandsGroupStyle); | ||
} | ||
if (!allowLeadingZeros) { | ||
numAsString = fixLeadingZero(numAsString); | ||
//add prefix and suffix when there is a number present | ||
if (prefix) | ||
{ beforeDecimal = prefix + beforeDecimal; } | ||
if (suffix) | ||
{ afterDecimal = afterDecimal + suffix; } | ||
//restore negation sign | ||
if (addNegation) | ||
{ beforeDecimal = '-' + beforeDecimal; } | ||
numStr = beforeDecimal + ((hasDecimalSeparator && decimalSeparator) || '') + afterDecimal; | ||
return numStr; | ||
} | ||
function getSeparators(props) { | ||
var decimalSeparator = props.decimalSeparator; if ( decimalSeparator === void 0 ) decimalSeparator = '.'; | ||
var thousandSeparator = props.thousandSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
if (thousandSeparator === true) { | ||
thousandSeparator = ','; | ||
} | ||
if (!allowedDecimalSeparators) { | ||
allowedDecimalSeparators = [decimalSeparator, '.']; | ||
} | ||
return { | ||
decimalSeparator: decimalSeparator, | ||
thousandSeparator: thousandSeparator, | ||
allowedDecimalSeparators: allowedDecimalSeparators, | ||
}; | ||
} | ||
function handleNegation(value, allowNegative) { | ||
if ( value === void 0 ) value = ''; | ||
var formattedValue = this.formatNumString(numAsString); | ||
//change the state | ||
if (formattedValue !== lastValue) { | ||
// the event needs to be persisted because its properties can be accessed in an asynchronous way | ||
this.updateValue({ | ||
formattedValue: formattedValue, | ||
numAsString: numAsString, | ||
input: e.target, | ||
setCaretPosition: false, | ||
event: e, | ||
source: 'event', | ||
}); | ||
onBlur(e); | ||
return; | ||
var negationRegex = new RegExp('(-)'); | ||
var doubleNegationRegex = new RegExp('(-)(.)*(-)'); | ||
// Check number has '-' value | ||
var hasNegation = negationRegex.test(value); | ||
// Check number has 2 or more '-' values | ||
var removeNegation = doubleNegationRegex.test(value); | ||
//remove negation | ||
value = value.replace(/-/g, ''); | ||
if (hasNegation && !removeNegation && allowNegative) { | ||
value = '-' + value; | ||
} | ||
} | ||
onBlur(e); | ||
}; | ||
return value; | ||
} | ||
function getNumberRegex(decimalSeparator, decimalScale, global) { | ||
return new RegExp(("(^-)|[0-9]|" + (escapeRegExp(decimalSeparator))), global ? 'g' : undefined); | ||
} | ||
function removeFormatting(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
NumberFormat.prototype.onKeyDown = function onKeyDown (e ) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var expectedCaretPosition; | ||
var ref = this.props; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var format = ref.format; | ||
var onKeyDown = ref.onKeyDown; | ||
var ignoreDecimalSeparator = decimalScale !== undefined && fixedDecimalScale; | ||
var numRegex = this.getNumberRegex(false, ignoreDecimalSeparator); | ||
var negativeRegex = new RegExp('-'); | ||
var isPatternFormat = typeof format === 'string'; | ||
this.selectionBeforeInput = { | ||
selectionStart: selectionStart, | ||
selectionEnd: selectionEnd, | ||
}; | ||
//Handle backspace and delete against non numerical/decimal characters or arrow keys | ||
if (key === 'ArrowLeft' || key === 'Backspace') { | ||
expectedCaretPosition = selectionStart - 1; | ||
} else if (key === 'ArrowRight') { | ||
expectedCaretPosition = selectionStart + 1; | ||
} else if (key === 'Delete') { | ||
expectedCaretPosition = selectionStart; | ||
} | ||
//if expectedCaretPosition is not set it means we don't want to Handle keyDown | ||
//also if multiple characters are selected don't handle | ||
if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
var newCaretPosition = expectedCaretPosition; | ||
var leftBound = isPatternFormat ? format.indexOf('#') : prefix.length; | ||
var rightBound = isPatternFormat ? format.lastIndexOf('#') + 1 : value.length - suffix.length; | ||
if (key === 'ArrowLeft' || key === 'ArrowRight') { | ||
var direction = key === 'ArrowLeft' ? 'left' : 'right'; | ||
newCaretPosition = this.correctCaretPosition(value, expectedCaretPosition, direction); | ||
} else if ( | ||
key === 'Delete' && | ||
!numRegex.test(value[expectedCaretPosition]) && | ||
!negativeRegex.test(value[expectedCaretPosition]) | ||
) { | ||
while (!numRegex.test(value[newCaretPosition]) && newCaretPosition < rightBound) { | ||
newCaretPosition++; | ||
var allowNegative = props.allowNegative; if ( allowNegative === void 0 ) allowNegative = true; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var decimalScale = props.decimalScale; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var start = to.start; | ||
var end = to.end; | ||
var ref = getSeparators(props); | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var isBeforeDecimalSeparator = value[end] === decimalSeparator; | ||
/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */ | ||
if (end - start === 1 && allowedDecimalSeparators.indexOf(value[start]) !== -1) { | ||
var separator = decimalScale === 0 ? '' : decimalSeparator; | ||
value = value.substring(0, start) + separator + value.substring(start + 1, value.length); | ||
} | ||
} else if (key === 'Backspace' && !numRegex.test(value[expectedCaretPosition])) { | ||
/* NOTE: This is special case when backspace is pressed on a | ||
negative value while the cursor position is after prefix. We can't handle it on onChange because | ||
we will not have any information of keyPress | ||
*/ | ||
if (selectionStart <= leftBound + 1 && value[0] === '-' && typeof format === 'undefined') { | ||
var newValue = value.substring(1); | ||
this.updateValue({ | ||
formattedValue: newValue, | ||
caretPos: newCaretPosition, | ||
input: el, | ||
event: e, | ||
source: 'event', | ||
}); | ||
} else if (!negativeRegex.test(value[expectedCaretPosition])) { | ||
while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) { | ||
newCaretPosition--; | ||
} | ||
newCaretPosition = this.correctCaretPosition(value, newCaretPosition, 'left'); | ||
var hasNegation = false; | ||
/** | ||
* if prefix starts with - the number hast to have two - at the start | ||
* if suffix starts with - and the value length is same as suffix length, then the - sign is from the suffix | ||
* In other cases, if the value starts with - then it is a negation | ||
*/ | ||
if (prefix.startsWith('-')) | ||
{ hasNegation = value.startsWith('--'); } | ||
else if (suffix.startsWith('-') && value.length === suffix.length) | ||
{ hasNegation = false; } | ||
else if (value[0] === '-') | ||
{ hasNegation = true; } | ||
// remove negation from start to simplify prefix logic as negation comes before prefix | ||
if (hasNegation) { | ||
value = value.substring(1); | ||
// account for the removal of the negation for start and end index | ||
start -= 1; | ||
end -= 1; | ||
} | ||
} | ||
/** | ||
* remove prefix | ||
* Remove whole prefix part if its present on the value | ||
* If the prefix is partially deleted (in which case change start index will be less the prefix length) | ||
* Remove only partial part of prefix. | ||
*/ | ||
var startIndex = 0; | ||
if (value.startsWith(prefix)) | ||
{ startIndex += prefix.length; } | ||
else if (start < prefix.length) | ||
{ startIndex = start; } | ||
value = value.substring(startIndex); | ||
// account for deleted prefix for end index | ||
end -= startIndex; | ||
/** | ||
* Remove suffix | ||
* Remove whole suffix part if its present on the value | ||
* If the suffix is partially deleted (in which case change end index will be greater than the suffixStartIndex) | ||
* remove the partial part of suffix | ||
*/ | ||
var endIndex = value.length; | ||
var suffixStartIndex = value.length - suffix.length; | ||
if (value.endsWith(suffix)) | ||
{ endIndex = suffixStartIndex; } | ||
else if (end > value.length - suffix.length) | ||
{ endIndex = end; } | ||
value = value.substring(0, endIndex); | ||
// add the negation back and handle for double negation | ||
value = handleNegation(hasNegation ? ("-" + value) : value, allowNegative); | ||
// remove non numeric characters | ||
value = (value.match(getNumberRegex(decimalSeparator, decimalScale, true)) || []).join(''); | ||
// replace the decimalSeparator with ., and only keep the first separator, ignore following ones | ||
var firstIndex = value.indexOf(decimalSeparator); | ||
value = value.replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), function (match, index) { | ||
return index === firstIndex ? '.' : ''; | ||
}); | ||
//check if beforeDecimal got deleted and there is nothing after decimal, | ||
//clear all numbers in such case while keeping the - sign | ||
var ref$1 = splitDecimal(value, allowNegative); | ||
var beforeDecimal = ref$1.beforeDecimal; | ||
var afterDecimal = ref$1.afterDecimal; | ||
var addNegation = ref$1.addNegation; // eslint-disable-line prefer-const | ||
//clear only if something got deleted before decimal (cursor is before decimal) | ||
if (to.end - to.start < from.end - from.start && | ||
beforeDecimal === '' && | ||
isBeforeDecimalSeparator && | ||
!parseFloat(afterDecimal)) { | ||
value = addNegation ? '-' : ''; | ||
} | ||
return value; | ||
} | ||
function getCaretBoundary(formattedValue, props) { | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var suffix = props.suffix; if ( suffix === void 0 ) suffix = ''; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hasNegation = formattedValue[0] === '-'; | ||
// fill for prefix and negation | ||
boundaryAry.fill(false, 0, prefix.length + (hasNegation ? 1 : 0)); | ||
// fill for suffix | ||
var valLn = formattedValue.length; | ||
boundaryAry.fill(false, valLn - suffix.length + 1, valLn + 1); | ||
return boundaryAry; | ||
} | ||
function validateProps(props) { | ||
var ref = getSeparators(props); | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (thousandSeparator === decimalSeparator) { | ||
throw new Error(("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: " + thousandSeparator + " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: " + decimalSeparator + " (default value for decimalSeparator is .)\n ")); | ||
} | ||
} | ||
function useNumericFormat(props) { | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var onBlur = props.onBlur; if ( onBlur === void 0 ) onBlur = noop; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
// validate props | ||
validateProps(props); | ||
var _format = function (numStr) { return format(numStr, props); }; | ||
var _removeFormatting = function (inputValue, changeMeta) { return removeFormatting(inputValue, changeMeta, props); }; | ||
var _isNumericString = isNumericString; | ||
if (!isNil(value)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof value === 'number'; | ||
} | ||
else if (!isNil(defaultValue)) { | ||
_isNumericString = isNumericString !== null && isNumericString !== void 0 ? isNumericString : typeof defaultValue === 'number'; | ||
} | ||
var roundIncomingValueToPrecision = function (value) { | ||
if (isNil(value) || isNanValue(value)) | ||
{ return value; } | ||
if (typeof value === 'number') { | ||
value = toNumericString(value); | ||
} | ||
/** | ||
* only round numeric or float string values coming through props, | ||
* we don't need to do it for onChange events, as we want to prevent typing there | ||
*/ | ||
if (_isNumericString && typeof decimalScale === 'number') { | ||
return roundToPrecision(value, decimalScale, fixedDecimalScale); | ||
} | ||
return value; | ||
}; | ||
var ref = useInternalValues(roundIncomingValueToPrecision(value), roundIncomingValueToPrecision(defaultValue), _isNumericString, _format, _removeFormatting, onValueChange); | ||
var ref_0 = ref[0]; | ||
var numAsString = ref_0.numAsString; | ||
var formattedValue = ref_0.formattedValue; | ||
var _onValueChange = ref[1]; | ||
var _onKeyDown = function (e) { | ||
var el = e.target; | ||
var key = e.key; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if user hits backspace, while the cursor is before prefix, and the input has negation, remove the negation | ||
if (key === 'Backspace' && value[0] === '-' && selectionStart === prefix.length + 1) { | ||
// bring the cursor to after negation | ||
setCaretPosition(el, 1); | ||
} | ||
// don't allow user to delete decimal separator when decimalScale and fixedDecimalScale is set | ||
var ref = getSeparators(props); | ||
var decimalSeparator = ref.decimalSeparator; | ||
if (key === 'Backspace' && | ||
value[selectionStart - 1] === decimalSeparator && | ||
decimalScale && | ||
fixedDecimalScale) { | ||
setCaretPosition(el, selectionStart - 1); | ||
e.preventDefault(); | ||
} | ||
// move cursor when delete or backspace is pressed before/after thousand separator | ||
if (key === 'Backspace' && value[selectionStart - 1] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart - 1); | ||
} | ||
if (key === 'Delete' && value[selectionStart] === thousandSeparator) { | ||
setCaretPosition(el, selectionStart + 1); | ||
} | ||
onKeyDown(e); | ||
}; | ||
var _onBlur = function (e) { | ||
var _value = numAsString; | ||
// if there no no numeric value, clear the input | ||
if (!_value.match(/\d/g)) { | ||
_value = ''; | ||
} | ||
// clear leading 0s | ||
if (!allowLeadingZeros) { | ||
_value = fixLeadingZero(_value); | ||
} | ||
// apply fixedDecimalScale on blur event | ||
if (fixedDecimalScale && decimalScale) { | ||
_value = roundToPrecision(_value, decimalScale, fixedDecimalScale); | ||
} | ||
if (_value !== numAsString) { | ||
var formattedValue = format(_value, props); | ||
_onValueChange({ | ||
formattedValue: formattedValue, | ||
value: _value, | ||
floatValue: parseFloat(_value), | ||
}, { | ||
event: e, | ||
source: SourceType.event, | ||
}); | ||
} | ||
onBlur(e); | ||
}; | ||
return { | ||
value: formattedValue, | ||
isNumericString: false, | ||
onValueChange: _onValueChange, | ||
format: _format, | ||
removeFormatting: _removeFormatting, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
onBlur: _onBlur, | ||
}; | ||
} | ||
function NumericFormat(props) { | ||
var decimalSeparator = props.decimalSeparator; | ||
var allowedDecimalSeparators = props.allowedDecimalSeparators; | ||
var thousandsGroupStyle = props.thousandsGroupStyle; | ||
var suffix = props.suffix; | ||
var allowNegative = props.allowNegative; | ||
var allowLeadingZeros = props.allowLeadingZeros; | ||
var onKeyDown = props.onKeyDown; | ||
var onBlur = props.onBlur; | ||
var thousandSeparator = props.thousandSeparator; | ||
var decimalScale = props.decimalScale; | ||
var fixedDecimalScale = props.fixedDecimalScale; | ||
var prefix = props.prefix; if ( prefix === void 0 ) prefix = ''; | ||
var defaultValue = props.defaultValue; | ||
var value = props.value; | ||
var isNumericString = props.isNumericString; | ||
var onValueChange = props.onValueChange; | ||
var restProps = __rest(props, ["decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "suffix", "allowNegative", "allowLeadingZeros", "onKeyDown", "onBlur", "thousandSeparator", "decimalScale", "fixedDecimalScale", "prefix", "defaultValue", "value", "isNumericString", "onValueChange"]); | ||
var numericFormatProps = useNumericFormat(props); | ||
return React__default.createElement(NumberFormatBase, Object.assign({}, restProps, numericFormatProps)); | ||
} | ||
if ( | ||
newCaretPosition !== expectedCaretPosition || | ||
expectedCaretPosition < leftBound || | ||
expectedCaretPosition > rightBound | ||
) { | ||
e.preventDefault(); | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
/* NOTE: this is just required for unit test as we need to get the newCaretPosition, | ||
Remove this when you find different solution */ | ||
if (e.isUnitTestRun) { | ||
this.setPatchedCaretPosition(el, newCaretPosition, value); | ||
} | ||
onKeyDown(e); | ||
}; | ||
/** required to handle the caret position when click anywhere within the input **/ | ||
NumberFormat.prototype.onMouseUp = function onMouseUp (e ) { | ||
var el = e.target; | ||
/** | ||
* NOTE: we have to give default value for value as in case when custom input is provided | ||
* value can come as undefined when nothing is provided on value prop. | ||
*/ | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
if (selectionStart === selectionEnd) { | ||
var caretPosition = this.correctCaretPosition(value, selectionStart); | ||
if (caretPosition !== selectionStart) { | ||
this.setPatchedCaretPosition(el, caretPosition, value); | ||
function format$1(numStr, props) { | ||
var format = props.format; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var mask = props.mask; | ||
if (numStr === '' && !allowEmptyFormatting) | ||
{ return ''; } | ||
var hashCount = 0; | ||
var formattedNumberAry = format.split(''); | ||
for (var i = 0, ln = format.length; i < ln; i++) { | ||
if (format[i] === '#') { | ||
formattedNumberAry[i] = numStr[hashCount] || getMaskAtIndex(mask, hashCount); | ||
hashCount += 1; | ||
} | ||
} | ||
} | ||
return formattedNumberAry.join(''); | ||
} | ||
function removeFormatting$1(value, changeMeta, props) { | ||
if ( changeMeta === void 0 ) changeMeta = getDefaultChangeMeta(value); | ||
this.props.onMouseUp(e); | ||
}; | ||
NumberFormat.prototype.onFocus = function onFocus (e ) { | ||
var this$1 = this; | ||
// Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328 | ||
// (onFocus event target selectionStart is always 0 before setTimeout) | ||
e.persist(); | ||
this.focusedElm = e.target; | ||
this.focusTimeout = setTimeout(function () { | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var value = el.value; if ( value === void 0 ) value = ''; | ||
var caretPosition = this$1.correctCaretPosition(value, selectionStart); | ||
//setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field) | ||
if ( | ||
caretPosition !== selectionStart && | ||
!(selectionStart === 0 && selectionEnd === value.length) | ||
) { | ||
this$1.setPatchedCaretPosition(el, caretPosition, value); | ||
var format = props.format; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var from = changeMeta.from; | ||
var to = changeMeta.to; | ||
var lastValue = changeMeta.lastValue; if ( lastValue === void 0 ) lastValue = ''; | ||
var isNumericSlot = function (caretPos) { return format[caretPos] === patternChar; }; | ||
var removeFormatChar = function (string, startIndex) { | ||
var str = ''; | ||
for (var i = 0; i < string.length; i++) { | ||
if (isNumericSlot(startIndex + i)) { | ||
str += string[i]; | ||
} | ||
} | ||
return str; | ||
}; | ||
var extractNumbers = function (str) { return str.replace(/[^0-9]/g, ''); }; | ||
// if format doesn't have any number, remove all the non numeric characters | ||
if (!format.match(/\d/)) { | ||
return extractNumbers(value); | ||
} | ||
/** | ||
* if user paste the whole formatted text in an empty input, check if matches to the pattern | ||
* and remove the format characters, if there is a mismatch on the pattern, do plane number extract | ||
*/ | ||
if (lastValue === '' && value.length === format.length) { | ||
var str = ''; | ||
for (var i = 0; i < value.length; i++) { | ||
if (isNumericSlot(i)) { | ||
str += value[i]; | ||
} | ||
else if (value[i] !== format[i]) { | ||
// if there is a mismatch on the pattern, do plane number extract | ||
return extractNumbers(value); | ||
} | ||
} | ||
return str; | ||
} | ||
/** | ||
* For partial change, | ||
* where ever there is a change on the input, we can break the number in three parts | ||
* 1st: left part which is unchanged | ||
* 2nd: middle part which is changed | ||
* 3rd: right part which is unchanged | ||
* | ||
* The first and third section will be same as last value, only the middle part will change | ||
* We can consider on the change part all the new characters are non format characters. | ||
* And on the first and last section it can have partial format characters. | ||
* | ||
* We pick first and last section from the lastValue (as that has 1-1 mapping with format) | ||
* and middle one from the update value. | ||
*/ | ||
var firstSection = lastValue.substring(0, from.start); | ||
var middleSection = value.substring(to.start, to.end); | ||
var lastSection = lastValue.substring(from.end); | ||
return ("" + (removeFormatChar(firstSection, 0)) + (extractNumbers(middleSection)) + (removeFormatChar(lastSection, from.end))); | ||
} | ||
function getCaretBoundary$1(formattedValue, props) { | ||
var format = props.format; | ||
var mask = props.mask; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
var boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(function () { return true; }); | ||
var hashCount = 0; | ||
var maskAndFormatMap = format.split('').map(function (char) { | ||
if (char === patternChar) { | ||
hashCount++; | ||
return getMaskAtIndex(mask, hashCount - 1); | ||
} | ||
return undefined; | ||
}); | ||
var isPosAllowed = function (pos) { | ||
// the position is allowed if the position is not masked and valid number area | ||
return format[pos] === patternChar && formattedValue[pos] !== maskAndFormatMap[pos]; | ||
}; | ||
for (var i = 0, ln = boundaryAry.length; i < ln; i++) { | ||
// consider caret to be in boundary if it is before or after numeric value | ||
// Note: on pattern based format its denoted by patternCharacter | ||
boundaryAry[i] = isPosAllowed(i) || isPosAllowed(i - 1); | ||
} | ||
// the first patternChar position is always allowed | ||
boundaryAry[format.indexOf(patternChar)] = true; | ||
return boundaryAry; | ||
} | ||
function validateProps$1(props) { | ||
var mask = props.mask; | ||
if (mask) { | ||
var maskAsStr = mask === 'string' ? mask : mask.toString(); | ||
if (maskAsStr.match(/\d/g)) { | ||
throw new Error(("Mask " + mask + " should not contain numeric character;")); | ||
} | ||
} | ||
} | ||
function usePatternFormat(props) { | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; if ( inputMode === void 0 ) inputMode = 'numeric'; | ||
var onKeyDown = props.onKeyDown; if ( onKeyDown === void 0 ) onKeyDown = noop; | ||
var patternChar = props.patternChar; if ( patternChar === void 0 ) patternChar = '#'; | ||
// validate props | ||
validateProps$1(props); | ||
var _onKeyDown = function (e) { | ||
var key = e.key; | ||
var el = e.target; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
// if multiple characters are selected and user hits backspace, no need to handle anything manually | ||
if (selectionStart !== selectionEnd) { | ||
onKeyDown(e); | ||
return; | ||
} | ||
// if backspace is pressed after the format characters, bring it to numeric section | ||
// if delete is pressed before the format characters, bring it to numeric section | ||
if (key === 'Backspace' || key === 'Delete') { | ||
// bring the cursor to closest numeric section | ||
var index = selectionStart; | ||
if (key === 'Backspace') { | ||
while (index > 0 && formatProp[index - 1] !== patternChar) { | ||
index--; | ||
} | ||
} | ||
else { | ||
var formatLn = formatProp.length; | ||
while (index < formatLn && formatProp[index] !== patternChar) { | ||
index++; | ||
} | ||
} | ||
if (index !== selectionStart) { | ||
setCaretPosition(el, index); | ||
} | ||
} | ||
onKeyDown(e); | ||
}; | ||
return { | ||
inputMode: inputMode, | ||
format: function (numStr) { return format$1(numStr, props); }, | ||
removeFormatting: function (inputValue, changeMeta) { return removeFormatting$1(inputValue, changeMeta, props); }, | ||
getCaretBoundary: function (formattedValue) { return getCaretBoundary$1(formattedValue, props); }, | ||
onKeyDown: _onKeyDown, | ||
}; | ||
} | ||
function PatternFormat(props) { | ||
var mask = props.mask; | ||
var allowEmptyFormatting = props.allowEmptyFormatting; | ||
var formatProp = props.format; | ||
var inputMode = props.inputMode; | ||
var onKeyDown = props.onKeyDown; | ||
var patternChar = props.patternChar; | ||
var restProps = __rest(props, ["mask", "allowEmptyFormatting", "format", "inputMode", "onKeyDown", "patternChar"]); | ||
var patternFormatProps = usePatternFormat(props); | ||
return React__default.createElement(NumberFormatBase, Object.assign({}, restProps, patternFormatProps)); | ||
} | ||
this$1.props.onFocus(e); | ||
}, 0); | ||
}; | ||
exports.NumberFormatBase = NumberFormatBase; | ||
exports.NumericFormat = NumericFormat; | ||
exports.PatternFormat = PatternFormat; | ||
exports.getNumericCaretBoundary = getCaretBoundary; | ||
exports.getPatternCaretBoundary = getCaretBoundary$1; | ||
exports.numericFormatter = format; | ||
exports.patterFormatter = format$1; | ||
exports.removeNumericFormat = removeFormatting; | ||
exports.removePatternFormat = removeFormatting$1; | ||
exports.useNumericFormat = useNumericFormat; | ||
exports.usePatternFormat = usePatternFormat; | ||
NumberFormat.prototype.render = function render () { | ||
var ref = this.props; | ||
var type = ref.type; | ||
var displayType = ref.displayType; | ||
var customInput = ref.customInput; | ||
var renderText = ref.renderText; | ||
var getInputRef = ref.getInputRef; | ||
var format = ref.format; | ||
var thousandSeparator = ref.thousandSeparator; | ||
var decimalSeparator = ref.decimalSeparator; | ||
var allowedDecimalSeparators = ref.allowedDecimalSeparators; | ||
var thousandsGroupStyle = ref.thousandsGroupStyle; | ||
var decimalScale = ref.decimalScale; | ||
var fixedDecimalScale = ref.fixedDecimalScale; | ||
var prefix = ref.prefix; | ||
var suffix = ref.suffix; | ||
var removeFormatting = ref.removeFormatting; | ||
var mask = ref.mask; | ||
var defaultValue = ref.defaultValue; | ||
var isNumericString = ref.isNumericString; | ||
var allowNegative = ref.allowNegative; | ||
var allowEmptyFormatting = ref.allowEmptyFormatting; | ||
var allowLeadingZeros = ref.allowLeadingZeros; | ||
var onValueChange = ref.onValueChange; | ||
var isAllowed = ref.isAllowed; | ||
var customNumerals = ref.customNumerals; | ||
var onChange = ref.onChange; | ||
var onKeyDown = ref.onKeyDown; | ||
var onMouseUp = ref.onMouseUp; | ||
var onFocus = ref.onFocus; | ||
var onBlur = ref.onBlur; | ||
var propValue = ref.value; | ||
var rest = objectWithoutProperties( ref, ["type", "displayType", "customInput", "renderText", "getInputRef", "format", "thousandSeparator", "decimalSeparator", "allowedDecimalSeparators", "thousandsGroupStyle", "decimalScale", "fixedDecimalScale", "prefix", "suffix", "removeFormatting", "mask", "defaultValue", "isNumericString", "allowNegative", "allowEmptyFormatting", "allowLeadingZeros", "onValueChange", "isAllowed", "customNumerals", "onChange", "onKeyDown", "onMouseUp", "onFocus", "onBlur", "value"] ); | ||
var otherProps = rest; | ||
var ref$1 = this.state; | ||
var value = ref$1.value; | ||
var mounted = ref$1.mounted; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
// add input mode on element based on format prop and device once the component is mounted | ||
var inputMode = mounted && addInputMode(format) ? 'numeric' : undefined; | ||
var inputProps = Object.assign({ inputMode: inputMode }, otherProps, { | ||
type: type, | ||
value: value, | ||
onChange: this.onChange, | ||
onKeyDown: this.onKeyDown, | ||
onMouseUp: this.onMouseUp, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
}); | ||
if (displayType === 'text') { | ||
return renderText ? ( | ||
renderText(value, otherProps) || null | ||
) : ( | ||
React.createElement( 'span', Object.assign({}, otherProps, { ref: getInputRef }), | ||
value | ||
) | ||
); | ||
} else if (customInput) { | ||
var CustomInput = customInput; | ||
return React.createElement( CustomInput, Object.assign({}, inputProps, { ref: getInputRef })); | ||
} | ||
return React.createElement( 'input', Object.assign({}, inputProps, { ref: getInputRef })); | ||
}; | ||
return NumberFormat; | ||
}(React.Component)); | ||
NumberFormat.defaultProps = defaultProps; | ||
return NumberFormat; | ||
})); | ||
}))); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):(t=t||self).NumberFormat=e(t.React)}(this,function(h){"use strict";function t(){}function f(t){return!!(t||"").match(/\d/)}function c(t){return null==t}function s(t){return t.replace(/[-[\]/{}()*+?.\\^$|]/g,"\\$&")}function j(t,e){void 0===e&&(e=!0);var r="-"===t[0],o=r&&e,n=(t=t.replace("-","")).split(".");return{beforeDecimal:n[0],afterDecimal:n[1]||"",hasNagation:r,addNegation:o}}function d(t,e,r){for(var o="",n=r?"0":"",a=0;a<=e-1;a++)o+=t[a]||n;return o}function p(t,e){return Array(e+1).join(t)}function n(t,e){if(t.value=t.value,null!==t){if(t.createTextRange){var r=t.createTextRange();return r.move("character",e),r.select(),!0}return t.selectionStart||0===t.selectionStart?(t.focus(),t.setSelectionRange(e,e),!0):(t.focus(),!1)}}function m(t,e,r){return Math.min(Math.max(t,e),r)}function g(t){return Math.max(t.selectionStart,t.selectionEnd)}var e={displayType:"input",decimalSeparator:".",thousandsGroupStyle:"thousand",fixedDecimalScale:!1,prefix:"",suffix:"",allowNegative:!0,allowEmptyFormatting:!1,allowLeadingZeros:!1,isNumericString:!1,type:"text",onValueChange:t,onChange:t,onKeyDown:t,onMouseUp:t,onFocus:t,onBlur:t,isAllowed:function(){return!0}},r=function(o){function t(t){o.call(this,t);var e=t.defaultValue;this.validateProps();var r=this.formatValueProp(e);this.state={value:r,numAsString:this.removeFormatting(r),mounted:!1},this.selectionBeforeInput={selectionStart:0,selectionEnd:0},this.onChange=this.onChange.bind(this),this.onKeyDown=this.onKeyDown.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onFocus=this.onFocus.bind(this),this.onBlur=this.onBlur.bind(this)}return o&&(t.__proto__=o),((t.prototype=Object.create(o&&o.prototype)).constructor=t).prototype.componentDidMount=function(){this.setState({mounted:!0})},t.prototype.componentDidUpdate=function(t){this.updateValueIfRequired(t)},t.prototype.componentWillUnmount=function(){clearTimeout(this.focusTimeout),clearTimeout(this.caretPositionTimeout)},t.prototype.updateValueIfRequired=function(t){var e=this.props,r=this.state,o=this.focusedElm,n=r.value,a=r.numAsString;if(void 0===a&&(a=""),t!==e){this.validateProps();var i=this.formatNumString(a),s=c(e.value)?i:this.formatValueProp(),u=this.removeFormatting(s),l=parseFloat(u),p=parseFloat(a);(isNaN(l)&&isNaN(p)||l===p)&&i===n&&(null!==o||s===n)||this.updateValue({formattedValue:s,numAsString:u,input:o,source:"prop",event:null})}},t.prototype.getFloatString=function(t){void 0===t&&(t="");var e=this.props.decimalScale,r=this.getSeparators().decimalSeparator,o=this.getNumberRegex(!0),n="-"===t[0];n&&(t=t.replace("-","")),r&&0===e&&(t=t.split(r)[0]);var a=(t=(t.match(o)||[]).join("").replace(r,".")).indexOf(".");return-1!==a&&(t=t.substring(0,a)+"."+t.substring(a+1,t.length).replace(new RegExp(s(r),"g"),"")),n&&(t="-"+t),t},t.prototype.getNumberRegex=function(t,e){var r=this.props,o=r.format,n=r.decimalScale,a=r.customNumerals,i=this.getSeparators().decimalSeparator;return new RegExp("[0-9"+(a?a.join(""):"")+"]"+(!i||0===n||e||o?"":"|"+s(i)),t?"g":void 0)},t.prototype.getSeparators=function(){var t=this.props.decimalSeparator,e=this.props,r=e.thousandSeparator,o=e.allowedDecimalSeparators;return!0===r&&(r=","),{decimalSeparator:t,thousandSeparator:r,allowedDecimalSeparators:o=o||[t,"."]}},t.prototype.getMaskAtIndex=function(t){var e=this.props.mask;return void 0===e&&(e=" "),"string"==typeof e?e:e[t]||" "},t.prototype.getValueObject=function(t,e){var r=parseFloat(e);return{formattedValue:t,value:e,floatValue:isNaN(r)?void 0:r}},t.prototype.validateProps=function(){var t=this.props.mask,e=this.getSeparators(),r=e.decimalSeparator,o=e.thousandSeparator;if(r===o)throw new Error("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: "+o+' (thousandSeparator = {true} is same as thousandSeparator = ",")\n decimalSeparator: '+r+" (default value for decimalSeparator is .)\n ");if(t&&("string"===t?t:t.toString()).match(/\d/g))throw new Error("\n Mask "+t+" should not contain numeric character;\n ")},t.prototype.setPatchedCaretPosition=function(t,e,r){n(t,e),this.caretPositionTimeout=setTimeout(function(){t.value===r&&n(t,e)},0)},t.prototype.correctCaretPosition=function(t,e,r){var o=this.props,n=o.prefix,a=o.suffix,i=o.format;if(""===t)return 0;if(e=m(e,0,t.length),!i){var s="-"===t[0];return m(e,n.length+(s?1:0),t.length-a.length)}if("function"==typeof i)return e;if("#"===i[e]&&f(t[e]))return e;if("#"===i[e-1]&&f(t[e-1]))return e;var u=i.indexOf("#");e=m(e,u,i.lastIndexOf("#")+1);for(var l=i.substring(e,i.length).indexOf("#"),p=e,c=e+(-1===l?0:l);u<p&&("#"!==i[p]||!f(t[p]));)p-=1;return!f(t[c])||"left"===r&&e!==u||e-p<c-e?f(t[p])?p+1:p:c},t.prototype.getCaretPosition=function(t,e,r){var o,n,a=this.props.format,i=this.state.value,s=this.getNumberRegex(!0),u=(t.match(s)||[]).join(""),l=(e.match(s)||[]).join("");for(n=o=0;n<r;n++){var p=t[n]||"",c=e[o]||"";if((p.match(s)||p===c)&&("0"!==p||!c.match(s)||"0"===c||u.length===l.length)){for(;p!==e[o]&&o<e.length;)o++;o++}}return"string"!=typeof a||i||(o=e.length),o=this.correctCaretPosition(e,o)},t.prototype.removePrefixAndSuffix=function(t){var e=this.props,r=e.format,o=e.prefix,n=e.suffix;if(!r&&t){var a="-"===t[0];a&&(t=t.substring(1,t.length));var i=(t=o&&0===t.indexOf(o)?t.substring(o.length,t.length):t).lastIndexOf(n);t=n&&-1!==i&&i===t.length-n.length?t.substring(0,i):t,a&&(t="-"+t)}return t},t.prototype.removePatternFormatting=function(t){for(var e=this.props.format.split("#").filter(function(t){return""!==t}),r=0,o="",n=0,a=e.length;n<=a;n++){var i=e[n]||"",s=n===a?t.length:t.indexOf(i,r);if(-1===s){o=t;break}o+=t.substring(r,s),r=s+i.length}return(o.match(this.getNumberRegex(!0))||[]).join("")},t.prototype.removeFormatting=function(t){var e=this.props,r=e.format,o=e.removeFormatting;return t?t=r?"string"==typeof r?this.removePatternFormatting(t):"function"==typeof o?o(t):(t.match(this.getNumberRegex(!0))||[]).join(""):(t=this.removePrefixAndSuffix(t),this.getFloatString(t)):t},t.prototype.formatWithPattern=function(t){for(var e=this.props.format,r=0,o=e.split(""),n=0,a=e.length;n<a;n++)"#"===e[n]&&(o[n]=t[r]||this.getMaskAtIndex(r),r+=1);return o.join("")},t.prototype.formatAsNumber=function(t){var e=this.props,r=e.decimalScale,o=e.fixedDecimalScale,n=e.prefix,a=e.suffix,i=e.allowNegative,s=e.thousandsGroupStyle,u=this.getSeparators(),l=u.thousandSeparator,p=u.decimalSeparator,c=-1!==t.indexOf(".")||r&&o,f=j(t,i),h=f.beforeDecimal,m=f.afterDecimal,g=f.addNegation;return void 0!==r&&(m=d(m,r,o)),l&&(h=function(t,e,r){var o=function(t){switch(t){case"lakh":return/(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g;case"wan":return/(\d)(?=(\d{4})+(?!\d))/g;case"thousand":default:return/(\d)(?=(\d{3})+(?!\d))/g}}(r),n=t.search(/[1-9]/);return n=-1===n?t.length:n,t.substring(0,n)+t.substring(n,t.length).replace(o,"$1"+e)}(h,l,s)),n&&(h=n+h),a&&(m+=a),g&&(h="-"+h),t=h+(c&&p||"")+m},t.prototype.formatNumString=function(t){void 0===t&&(t="");var e=this.props,r=e.format,o=e.allowEmptyFormatting,n=e.customNumerals,a=t;if(n&&10===n.length){var i=new RegExp("["+n.join("")+"]","g");a=t.replace(i,function(t){return n.indexOf(t).toString()})}return a=""!==t||o?"-"!==t||r?"string"==typeof r?this.formatWithPattern(a):"function"==typeof r?r(a):this.formatAsNumber(a):"-":""},t.prototype.formatValueProp=function(t){var e=this.props,r=e.format,o=e.decimalScale,n=e.fixedDecimalScale,a=e.allowEmptyFormatting,i=this.props,s=i.value,u=i.isNumericString,l=!(s=c(s)?t:s)&&0!==s;return l&&a&&(s=""),l&&!a?"":("number"==typeof s&&(s=function(t){var e="-"===(t+="")[0]?"-":"";e&&(t=t.substring(1));var r=t.split(/[eE]/g),o=r[0],n=r[1];if(!(n=Number(n)))return e+o;var a=1+n,i=(o=o.replace(".","")).length;return a<0?o="0."+p("0",Math.abs(a))+o:i<=a?o+=p("0",a-i):o=(o.substring(0,a)||"0")+"."+o.substring(a),e+o}(s),u=!0),"Infinity"===s&&u&&(s=""),u&&!r&&"number"==typeof o&&(s=function(t,e,r){if(-1!==["","-"].indexOf(t))return t;var o=-1!==t.indexOf(".")&&e,n=j(t),a=n.beforeDecimal,i=n.afterDecimal,s=n.hasNagation,u=parseFloat("0."+(i||"0")),l=(i.length<=e?"0."+i:u.toFixed(e)).split(".");return(s?"-":"")+a.split("").reverse().reduce(function(t,e,r){return t.length>r?(Number(t[0])+Number(e)).toString()+t.substring(1,t.length):e+t},l[0])+(o?".":"")+d(l[1]||"",Math.min(e,i.length),r)}(s,o,n)),u?this.formatNumString(s):this.formatInput(s))},t.prototype.formatNegation=function(t){void 0===t&&(t="");var e=this.props.allowNegative,r=new RegExp("(-)"),o=new RegExp("(-)(.)*(-)"),n=r.test(t),a=o.test(t);return t=t.replace(/-/g,""),n&&!a&&e&&(t="-"+t),t},t.prototype.formatInput=function(t){return void 0===t&&(t=""),this.props.format||(t=this.removePrefixAndSuffix(t),t=this.formatNegation(t)),t=this.removeFormatting(t),this.formatNumString(t)},t.prototype.isCharacterAFormat=function(t,e){var r=this.props,o=r.format,n=r.prefix,a=r.suffix,i=r.decimalScale,s=r.fixedDecimalScale,u=this.getSeparators().decimalSeparator;return"string"==typeof o&&"#"!==o[t]||!(o||!(t<n.length||t>=e.length-a.length||i&&s&&e[t]===u))},t.prototype.correctInputValue=function(t,r,e){var o=this,n=this.props,a=n.format,i=n.allowNegative,s=n.prefix,u=n.suffix,l=n.decimalScale,p=this.getSeparators(),c=p.allowedDecimalSeparators,f=p.decimalSeparator,h=this.state.numAsString||"",m=this.selectionBeforeInput,g=m.selectionStart,d=m.selectionEnd,v=function(t,e){for(var r=0,o=0,n=t.length,a=e.length;t[r]===e[r]&&r<n;)r++;for(;t[n-1-o]===e[a-1-o]&&r<a-o&&r<n-o;)o++;return{start:r,end:n-o}}(r,e),S=v.start,y=v.end;if(!a&&S===y&&-1!==c.indexOf(e[g])){var x=0===l?"":f;return e.substr(0,g)+x+e.substr(g+1,e.length)}var b=a?0:s.length,w=r.length-(a?0:u.length);if(e.length>r.length||!e.length||S===y||0===g&&d===r.length||0===S&&y===r.length||g===b&&d===w)return e;var N=r.substr(S,y-S);if(!![].concat(N).find(function(t,e){return o.isCharacterAFormat(e+S,r)})){var P=r.substr(S),F={},D=[];[].concat(P).forEach(function(t,e){o.isCharacterAFormat(e+S,r)?F[e]=t:e>N.length-1&&D.push(t)}),Object.keys(F).forEach(function(t){D.length>t?D.splice(t,0,F[t]):D.push(F[t])}),e=r.substr(0,S)+D.join("")}if(!a){var C=this.removeFormatting(e),V=j(C,i),A=V.beforeDecimal,E=V.afterDecimal,O=V.addNegation,R=t<e.indexOf(f)+1;if(C.length<h.length&&R&&""===A&&!parseFloat(E))return O?"-":""}return e},t.prototype.updateValue=function(t){var e=t.formattedValue,r=t.input,o=t.setCaretPosition;void 0===o&&(o=!0);var n=t.source,a=t.event,i=t.numAsString,s=t.caretPos,u=this.props.onValueChange,l=this.state.value;if(r){if(void 0===s&&o){var p=t.inputValue||r.value,c=g(r);r.value=e,s=this.getCaretPosition(p,e,c)}r.value=e,o&&this.setPatchedCaretPosition(r,s,e)}void 0===i&&(i=this.removeFormatting(e)),e!==l&&(this.setState({value:e,numAsString:i}),u(this.getValueObject(e,i),{event:a,source:n}))},t.prototype.onChange=function(t){var e=t.target,r=e.value,o=this.state,n=this.props,a=n.isAllowed,i=o.value||"",s=g(e);r=this.correctInputValue(s,i,r);var u=this.formatInput(r)||"",l=this.removeFormatting(u),p=a(this.getValueObject(u,l));p||(u=i),this.updateValue({formattedValue:u,numAsString:l,inputValue:r,input:e,event:t,source:"event"}),p&&n.onChange(t)},t.prototype.onBlur=function(t){var e=this.props,r=this.state,o=e.format,n=e.onBlur,a=e.allowLeadingZeros,i=r.numAsString,s=r.value;if(this.focusedElm=null,clearTimeout(this.focusTimeout),clearTimeout(this.caretPositionTimeout),!o){isNaN(parseFloat(i))&&(i=""),a||(i=function(t){if(!t)return t;var e="-"===t[0];e&&(t=t.substring(1,t.length));var r=t.split("."),o=r[0].replace(/^0+/,"")||"0",n=r[1]||"";return(e?"-":"")+o+(n?"."+n:"")}(i));var u=this.formatNumString(i);if(u!==s)return this.updateValue({formattedValue:u,numAsString:i,input:t.target,setCaretPosition:!1,event:t,source:"event"}),void n(t)}n(t)},t.prototype.onKeyDown=function(t){var e,r=t.target,o=t.key,n=r.selectionStart,a=r.selectionEnd,i=r.value;void 0===i&&(i="");var s=this.props,u=s.decimalScale,l=s.fixedDecimalScale,p=s.prefix,c=s.suffix,f=s.format,h=s.onKeyDown,m=void 0!==u&&l,g=this.getNumberRegex(!1,m),d=new RegExp("-"),v="string"==typeof f;if(this.selectionBeforeInput={selectionStart:n,selectionEnd:a},"ArrowLeft"===o||"Backspace"===o?e=n-1:"ArrowRight"===o?e=n+1:"Delete"===o&&(e=n),void 0!==e&&n===a){var S=e,y=v?f.indexOf("#"):p.length,x=v?f.lastIndexOf("#")+1:i.length-c.length;if("ArrowLeft"===o||"ArrowRight"===o){var b="ArrowLeft"===o?"left":"right";S=this.correctCaretPosition(i,e,b)}else if("Delete"!==o||g.test(i[e])||d.test(i[e])){if("Backspace"===o&&!g.test(i[e]))if(n<=y+1&&"-"===i[0]&&void 0===f){var w=i.substring(1);this.updateValue({formattedValue:w,caretPos:S,input:r,event:t,source:"event"})}else if(!d.test(i[e])){for(;!g.test(i[S-1])&&y<S;)S--;S=this.correctCaretPosition(i,S,"left")}}else for(;!g.test(i[S])&&S<x;)S++;(S!==e||e<y||x<e)&&(t.preventDefault(),this.setPatchedCaretPosition(r,S,i)),t.isUnitTestRun&&this.setPatchedCaretPosition(r,S,i),h(t)}else h(t)},t.prototype.onMouseUp=function(t){var e=t.target,r=e.selectionStart,o=e.selectionEnd,n=e.value;if(void 0===n&&(n=""),r===o){var a=this.correctCaretPosition(n,r);a!==r&&this.setPatchedCaretPosition(e,a,n)}this.props.onMouseUp(t)},t.prototype.onFocus=function(a){var i=this;a.persist(),this.focusedElm=a.target,this.focusTimeout=setTimeout(function(){var t=a.target,e=t.selectionStart,r=t.selectionEnd,o=t.value;void 0===o&&(o="");var n=i.correctCaretPosition(o,e);n===e||0===e&&r===o.length||i.setPatchedCaretPosition(t,n,o),i.props.onFocus(a)},0)},t.prototype.render=function(){var t=this.props,e=t.type,r=t.displayType,o=t.customInput,n=t.renderText,a=t.getInputRef,i=t.format,s=(t.thousandSeparator,t.decimalSeparator,t.allowedDecimalSeparators,t.thousandsGroupStyle,t.decimalScale,t.fixedDecimalScale,t.prefix,t.suffix,t.removeFormatting,t.mask,t.defaultValue,t.isNumericString,t.allowNegative,t.allowEmptyFormatting,t.allowLeadingZeros,t.onValueChange,t.isAllowed,t.customNumerals,t.onChange,t.onKeyDown,t.onMouseUp,t.onFocus,t.onBlur,t.value,function(t,e){var r={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&-1===e.indexOf(o)&&(r[o]=t[o]);return r}(t,["type","displayType","customInput","renderText","getInputRef","format","thousandSeparator","decimalSeparator","allowedDecimalSeparators","thousandsGroupStyle","decimalScale","fixedDecimalScale","prefix","suffix","removeFormatting","mask","defaultValue","isNumericString","allowNegative","allowEmptyFormatting","allowLeadingZeros","onValueChange","isAllowed","customNumerals","onChange","onKeyDown","onMouseUp","onFocus","onBlur","value"])),u=this.state,l=u.value,p=u.mounted&&function(t){return t||"undefined"!=typeof navigator&&!(navigator.platform&&/iPhone|iPod/.test(navigator.platform))}(i)?"numeric":void 0,c=Object.assign({inputMode:p},s,{type:e,value:l,onChange:this.onChange,onKeyDown:this.onKeyDown,onMouseUp:this.onMouseUp,onFocus:this.onFocus,onBlur:this.onBlur});if("text"===r)return n?n(l,s)||null:h.createElement("span",Object.assign({},s,{ref:a}),l);if(o){var f=o;return h.createElement(f,Object.assign({},c,{ref:a}))}return h.createElement("input",Object.assign({},c,{ref:a}))},t}((h=h&&h.hasOwnProperty("default")?h.default:h).Component);return r.defaultProps=e,r}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e=e||self).NumberFormat={},e.React)}(this,function(e,R){"use strict";var k,t,j="default"in R?R.default:R;function M(e,t){var r={};for(a in e)Object.prototype.hasOwnProperty.call(e,a)&&t.indexOf(a)<0&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,a=Object.getOwnPropertySymbols(e);n<a.length;n++)t.indexOf(a[n])<0&&Object.prototype.propertyIsEnumerable.call(e,a[n])&&(r[a[n]]=e[a[n]]);return r}function P(){}function K(e){return!!(e||"").match(/\d/)}function b(e){return null==e}function w(e){return"number"==typeof e&&isNaN(e)}function m(e){return e.replace(/[-[\]/{}()*+?.\\^$|]/g,"\\$&")}function d(e,t,r){var r=function(e){switch(e){case"lakh":return/(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g;case"wan":return/(\d)(?=(\d{4})+(?!\d))/g;default:return/(\d)(?=(\d{3})+(?!\d))/g}}(r),n=-1===(n=e.search(/[1-9]/))?e.length:n;return e.substring(0,n)+e.substring(n,e.length).replace(r,"$1"+t)}function g(e,t){void 0===t&&(t=!0);var r="-"===e[0],t=r&&t,e=(e=e.replace("-","")).split(".");return{beforeDecimal:e[0],afterDecimal:e[1]||"",hasNegation:r,addNegation:t}}function v(e,t,r){for(var n="",a=r?"0":"",o=0;o<=t-1;o++)n+=e[o]||a;return n}function a(e,t){return Array(t+1).join(e)}function x(e){var e=e+"",t="-"===e[0]?"-":"",e=(e=t?e.substring(1):e).split(/[eE]/g),r=e[0],e=e[1];if(!(e=Number(e)))return t+r;var e=1+e,n=(r=r.replace(".","")).length;return e<0?r="0."+a("0",Math.abs(e))+r:n<=e?r+=a("0",e-n):r=(r.substring(0,e)||"0")+"."+r.substring(e),t+r}function D(e,t,r){if(-1!==["","-"].indexOf(e))return e;var n=-1!==e.indexOf(".")&&t,e=g(e),a=e.beforeDecimal,o=e.afterDecimal,e=e.hasNegation,u=parseFloat("0."+(o||"0")),o=(o.length<=t?"0."+o:u.toFixed(t)).split(".");return(e?"-":"")+a.split("").reverse().reduce(function(e,t,r){return e.length>r?(Number(e[0])+Number(t)).toString()+e.substring(1,e.length):t+e},o[0])+(n?".":"")+v(o[1]||"",t,r)}function I(e,t){var r;if(e.value=e.value,null!==e)return e.createTextRange?((r=e.createTextRange()).move("character",t),void r.select()):e.selectionStart||0===e.selectionStart?(e.focus(),void e.setSelectionRange(t,t)):void e.focus()}function p(e){return{from:{start:0,end:0},to:{start:0,end:e.length},lastValue:""}}function s(e,t){return"string"==typeof(e=void 0===e?" ":e)?e:e[t]||" "}function L(e){for(var t=Array.from({length:e.length+1}).map(function(){return!0}),r=0,n=t.length;r<n;r++)t[r]=Boolean(K(e[r])||K(e[r-1]));return t}function W(e,t,r,n,a,o){void 0===o&&(o=P);var u,i=R.useRef(),l=(c=function(e){var t,e=b(e)||w(e)?t="":"number"==typeof e||r?(t="number"==typeof e?x(e):e,n(t)):(t=a(e,void 0),e);return{formattedValue:e,numAsString:t}},(u=R.useRef(c)).current=c,R.useRef(function(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return u.current.apply(u,e)}).current),c=R.useState(function(){return l(t)}),f=c[0],s=c[1];return R.useMemo(function(){b(e)?i.current=void 0:(i.current=l(e),s(i.current))},[e,l]),[f,function(e,t){s({formattedValue:e.formattedValue,numAsString:e.value}),o(e,t)}]}function U(e){return e.replace(/[^0-9]/g,"")}function G(e){return e}function r(e){function c(e,t,r){I(e,t),O.current.setCaretTimeout=setTimeout(function(){e.value===r&&I(e,t)},0)}function f(e,t,r){return e=function(e,t,r){for(var n=t.length,a=e.length,o={},u=new Array(n),i=0;i<n;i++){u[i]=-1;for(var l=0,c=a;l<c;l++)if(t[i]===e[l]&&!0!==o[l]){o[u[i]=l]=!0;break}}for(var f=r;f<n&&(-1===u[f]||!K(t[f]));)f++;for(var s=f===n||-1===u[f]?a:u[f],f=r-1;0<f&&(-1===u[f]||!K(t[f]));)f--;var d=-1===f||-1===u[f]?0:u[f]+1;return!(s<d)&&r-d<s-r?d:s}(t,e,r),T(t,e)}function r(e,t,r){var n=function(e,t){for(var r=0,n=0,a=e.length,o=t.length;e[r]===t[r]&&r<a;)r++;for(;e[a-1-n]===t[o-1-n]&&r<o-n&&r<a-n;)n++;return{from:{start:r,end:a-n},to:{start:r,end:o-n}}}(x,e),n=Object.assign(Object.assign({},n),{lastValue:x}),n=l(e,n),a=E(n);if(g&&!g(B(a,n)))return!1;A({formattedValue:a,numAsString:n,inputValue:e,event:t,source:r,setCaretPosition:!0,input:t.target})}var t=e.type,n=(void 0===t&&(t="text"),e.displayType),a=(void 0===n&&(n="input"),e.customInput),o=e.renderText,u=e.getInputRef,i=e.format,l=(void 0===i&&(i=G),e.removeFormatting),s=(void 0===l&&(l=U),e.defaultValue),d=e.isNumericString,m=e.onValueChange,g=e.isAllowed,v=e.onChange,p=(void 0===v&&(v=P),e.onKeyDown),h=(void 0===p&&(p=P),e.onMouseUp),S=(void 0===h&&(h=P),e.onFocus),y=(void 0===S&&(S=P),e.onBlur),b=(void 0===y&&(y=P),e.value),w=e.getCaretBoundary,e=(void 0===w&&(w=L),M(e,["type","displayType","customInput","renderText","getInputRef","format","removeFormatting","defaultValue","isNumericString","onValueChange","isAllowed","onChange","onKeyDown","onMouseUp","onFocus","onBlur","value","getCaretBoundary"])),b=W(b,s,d,i,l,m),s=b[0],x=s.formattedValue,D=s.numAsString,V=b[1],N=R.useRef(),d=(R.useEffect(function(){var e,t=i(D);void 0!==N.current&&t===N.current||(e=F.current,A({formattedValue:t,numAsString:D,input:e,setCaretPosition:!0,source:k.props,event:null}))}),R.useState(!1)),m=d[0],C=d[1],F=R.useRef(null),O=R.useRef({setCaretTimeout:null,focusTimeout:null}),E=(R.useEffect(function(){return C(!0),function(){clearTimeout(O.current.setCaretTimeout),clearTimeout(O.current.focusTimeout)}},[]),i),B=function(e,t){var r=parseFloat(t);return{formattedValue:e,value:t,floatValue:isNaN(r)?void 0:r}},T=function(e,t,r){var n,a,o,u=e.length,i=(n=t,a=0,o=u,t=Math.min(Math.max(n,a),o),w(e));if("left"===r){for(;0<=t&&!i[t];)t--;-1===t&&(t=i.indexOf(!0))}else{for(;t<=u&&!i[t];)t++;u<t&&(t=i.lastIndexOf(!0))}return t=-1===t?u:t},A=function(e){var t,r=e.formattedValue,n=e.input,a=e.setCaretPosition,o=(void 0===a&&(a=!0),e.source),u=e.event,i=e.numAsString,l=e.caretPos;n&&(void 0===l&&a&&(e=e.inputValue||n.value,t=Math.max(n.selectionStart,n.selectionEnd),n.value=r,l=f(e,r,t)),n.value=r,a&&c(n,l,r)),r!==x&&(e=B(r,i),t={event:u,source:o},N.current=e.formattedValue,V(e,t))},s=m&&!("undefined"==typeof navigator||navigator.platform&&/iPhone|iPod/.test(navigator.platform))?"numeric":void 0,b=Object.assign({inputMode:s},e,{type:t,value:x,onChange:function(e){var t=e.target.value;r(t,e,k.event)&&v(e)},onKeyDown:function(e){var t,r=e.target,n=e.key,a=r.selectionStart,o=r.selectionEnd,u=r.value;void 0===u&&(u=""),"ArrowLeft"===n||"Backspace"===n?t=Math.max(a-1,0):"ArrowRight"===n?t=Math.min(a+1,u.length):"Delete"===n&&(t=a),void 0!==t&&a===o&&(a=t,"ArrowLeft"===n||"ArrowRight"===n?a=T(u,t,"ArrowLeft"===n?"left":"right"):"Delete"!==n||K(u[t])?"Backspace"!==n||K(u[t])||(a=T(u,t,"left")):a=T(u,t,"right"),a!==t&&c(r,a,u),e.isUnitTestRun&&c(r,a,u)),p(e)},onMouseUp:function(e){var t=e.target,r=t.selectionStart,n=t.selectionEnd,a=t.value;void 0===a&&(a=""),r===n&&(n=T(a,r))!==r&&c(t,n,a),h(e)},onFocus:function(a){a.persist();var o=a.target;F.current=o,O.current.focusTimeout=setTimeout(function(){var e=o.selectionStart,t=o.selectionEnd,r=o.value,n=T(r=void 0===r?"":r,e);n===e||0===e&&t===r.length||c(o,n,r),S(a)},0)},onBlur:function(e){F.current=null,clearTimeout(O.current.focusTimeout),clearTimeout(O.current.setCaretTimeout),y(e)}});return"text"===n?o?j.createElement(j.Fragment,null,o(x,e)||null):j.createElement("span",Object.assign({},e,{ref:u}),x):a?j.createElement(a,Object.assign({},b,{ref:u})):j.createElement("input",Object.assign({},b,{ref:u}))}function V(e,t){var r=t.decimalScale,n=t.fixedDecimalScale,a=t.prefix,o=(void 0===a&&(a=""),t.suffix),u=(void 0===o&&(o=""),t.allowNegative),i=(void 0===u&&(u=!0),t.thousandsGroupStyle);if(void 0===i&&(i="thousand"),""===e||"-"===e)return e;var t=N(t),l=t.thousandSeparator,t=t.decimalSeparator,c=0!==r&&-1!==e.indexOf(".")||r&&n,u=g(e,u),f=u.beforeDecimal,s=u.afterDecimal,u=u.addNegation;return void 0!==r&&(s=v(s,r,n)),l&&(f=d(f,l,i)),a&&(f=a+f),o&&(s+=o),e=(f=u?"-"+f:f)+(c&&t||"")+s}function N(e){var t=e.decimalSeparator,r=e.thousandSeparator,e=e.allowedDecimalSeparators;return{decimalSeparator:t=void 0===t?".":t,thousandSeparator:r=!0===r?",":r,allowedDecimalSeparators:e=e||[t,"."]}}function C(e,t,r){void 0===t&&(t=p(e));var n=r.allowNegative,a=(void 0===n&&(n=!0),r.prefix),o=(void 0===a&&(a=""),r.suffix),u=(void 0===o&&(o=""),r.decimalScale),i=t.from,t=t.to,l=t.start,c=t.end,r=N(r),f=r.allowedDecimalSeparators,r=r.decimalSeparator,s=e[c]===r,u=(c-l==1&&-1!==f.indexOf(e[l])&&(f=0===u?"":r,e=e.substring(0,l)+f+e.substring(l+1,e.length)),!1),f=(a.startsWith("-")?u=e.startsWith("--"):o.startsWith("-")&&e.length===o.length?u=!1:"-"===e[0]&&(u=!0),u&&(e=e.substring(1),--l,--c),0),a=(e.startsWith(a)?f+=a.length:l<a.length&&(f=l),c-=f,(e=e.substring(f)).length),l=e.length-o.length,d=(e.endsWith(o)?a=l:c>e.length-o.length&&(a=c),e=e.substring(0,a),f=n,void 0===(l=u?"-"+e:e)&&(l=""),o=new RegExp("(-)"),c=new RegExp("(-)(.)*(-)"),o=o.test(l),c=c.test(l),l=l.replace(/-/g,""),(e=((e=l=o&&!c&&f?"-"+l:l).match((a=!0,new RegExp("(^-)|[0-9]|"+m(r),a?"g":void 0)))||[]).join("")).indexOf(r)),u=g(e=e.replace(new RegExp(m(r),"g"),function(e,t){return t===d?".":""}),n),o=u.beforeDecimal,c=u.afterDecimal,f=u.addNegation;return e=t.end-t.start<i.end-i.start&&""===o&&s&&!parseFloat(c)?f?"-":"":e}function F(e,t){var r=t.prefix,t=(void 0===r&&(r=""),t.suffix),n=(void 0===t&&(t=""),Array.from({length:e.length+1}).map(function(){return!0})),a="-"===e[0],r=(n.fill(!1,0,r.length+(a?1:0)),e.length);return n.fill(!1,r-t.length+1,r+1),n}function n(u){var n=u.allowLeadingZeros,i=u.onKeyDown,a=(void 0===i&&(i=P),u.onBlur),l=(void 0===a&&(a=P),u.thousandSeparator),c=u.decimalScale,f=u.fixedDecimalScale,s=u.prefix,e=(void 0===s&&(s=""),u.defaultValue),t=u.value,r=u.isNumericString,o=u.onValueChange,d=(m=N(m=u)).thousandSeparator,m=m.decimalSeparator;if(d===m)throw new Error("\n Decimal separator can't be same as thousand separator.\n thousandSeparator: "+d+' (thousandSeparator = {true} is same as thousandSeparator = ",")\n decimalSeparator: '+m+" (default value for decimalSeparator is .)\n ");function g(e){return V(e,u)}function v(e,t){return C(e,t,u)}function p(e){return b(e)||w(e)?e:("number"==typeof e&&(e=x(e)),h&&"number"==typeof c?D(e,c,f):e)}var h=r,d=(b(t)?b(e)||(h=null!=r?r:"number"==typeof e):h=null!=r?r:"number"==typeof t,W(p(t),p(e),h,g,v,o)),m=d[0],S=m.numAsString,r=m.formattedValue,y=d[1];return{value:r,isNumericString:!1,onValueChange:y,format:g,removeFormatting:v,getCaretBoundary:function(e){return F(e,u)},onKeyDown:function(e){var t=e.target,r=e.key,n=t.selectionStart,a=t.selectionEnd,o=t.value;void 0===o&&(o=""),n===a&&("Backspace"===r&&"-"===o[0]&&n===s.length+1&&I(t,1),a=N(u).decimalSeparator,"Backspace"===r&&o[n-1]===a&&c&&f&&(I(t,n-1),e.preventDefault()),"Backspace"===r&&o[n-1]===l&&I(t,n-1),"Delete"===r&&o[n]===l&&I(t,n+1)),i(e)},onBlur:function(e){var t,r=S;r.match(/\d/g)||(r=""),n||(r=function(e){if(!e)return e;var t="-"===e[0],r=(e=(e=t?e.substring(1,e.length):e).split("."))[0].replace(/^0+/,"")||"0",e=e[1]||"";return(t?"-":"")+r+(e?"."+e:"")}(r)),(r=f&&c?D(r,c,f):r)!==S&&(t=V(r,u),y({formattedValue:t,value:r,floatValue:parseFloat(r)},{event:e,source:k.event})),a(e)}}}function o(e,t){var r=t.format,n=t.allowEmptyFormatting,a=t.mask;if(""===e&&!n)return"";for(var o=0,u=r.split(""),i=0,l=r.length;i<l;i++)"#"===r[i]&&(u[i]=e[o]||s(a,o),o+=1);return u.join("")}function c(e,t,r){void 0===t&&(t=p(e));function a(e){return u[e]===i}function n(e,t){for(var r="",n=0;n<e.length;n++)a(t+n)&&(r+=e[n]);return r}function o(e){return e.replace(/[^0-9]/g,"")}var u=r.format,i=r.patternChar,r=(void 0===i&&(i="#"),t.from),l=t.to,t=t.lastValue;void 0===t&&(t="");if(!u.match(/\d/))return o(e);if(""===t&&e.length===u.length){for(var c="",f=0;f<e.length;f++)if(a(f))c+=e[f];else if(e[f]!==u[f])return o(e);return c}var s=t.substring(0,r.start),l=e.substring(l.start,l.end),t=t.substring(r.end);return""+n(s,0)+o(l)+n(t,r.end)}function f(t,e){for(var r=e.format,n=e.mask,a=e.patternChar,o=(void 0===a&&(a="#"),Array.from({length:t.length+1}).map(function(){return!0})),u=0,i=r.split("").map(function(e){if(e===a)return s(n,++u-1)}),l=function(e){return r[e]===a&&t[e]!==i[e]},c=0,f=o.length;c<f;c++)o[c]=l(c)||l(c-1);return o[r.indexOf(a)]=!0,o}function u(r){var u=r.format,e=r.inputMode,i=(void 0===e&&(e="numeric"),r.onKeyDown),l=(void 0===i&&(i=P),r.patternChar),t=(void 0===l&&(l="#"),r);if((t=t.mask)&&("string"===t?t:t.toString()).match(/\d/g))throw new Error("Mask "+t+" should not contain numeric character;");return{inputMode:e,format:function(e){return o(e,r)},removeFormatting:function(e,t){return c(e,t,r)},getCaretBoundary:function(e){return f(e,r)},onKeyDown:function(e){var t=e.key,r=e.target,n=r.selectionStart;if(n!==r.selectionEnd)i(e);else{if("Backspace"===t||"Delete"===t){var a=n;if("Backspace"===t)for(;0<a&&u[a-1]!==l;)a--;else for(var o=u.length;a<o&&u[a]!==l;)a++;a!==n&&I(r,a)}i(e)}}}}(t=k=k||{}).event="event",t.props="prop",e.NumberFormatBase=r,e.NumericFormat=function(e){e.decimalSeparator,e.allowedDecimalSeparators,e.thousandsGroupStyle,e.suffix,e.allowNegative,e.allowLeadingZeros,e.onKeyDown,e.onBlur,e.thousandSeparator,e.decimalScale,e.fixedDecimalScale,e.prefix,e.defaultValue,e.value,e.isNumericString,e.onValueChange;var t=M(e,["decimalSeparator","allowedDecimalSeparators","thousandsGroupStyle","suffix","allowNegative","allowLeadingZeros","onKeyDown","onBlur","thousandSeparator","decimalScale","fixedDecimalScale","prefix","defaultValue","value","isNumericString","onValueChange"]),e=n(e);return j.createElement(r,Object.assign({},t,e))},e.PatternFormat=function(e){e.mask,e.allowEmptyFormatting,e.format,e.inputMode,e.onKeyDown,e.patternChar;var t=M(e,["mask","allowEmptyFormatting","format","inputMode","onKeyDown","patternChar"]),e=u(e);return j.createElement(r,Object.assign({},t,e))},e.getNumericCaretBoundary=F,e.getPatternCaretBoundary=f,e.numericFormatter=V,e.patterFormatter=o,e.removeNumericFormat=C,e.removePatternFormat=c,e.useNumericFormat=n,e.usePatternFormat=u,Object.defineProperty(e,"__esModule",{value:!0})}); |
{ | ||
"name": "react-number-format", | ||
"description": "React component to format number in an input or as a text.", | ||
"version": "4.9.3", | ||
"version": "5.0.0-beta.1", | ||
"main": "dist/react-number-format.cjs.js", | ||
"module": "dist/react-number-format.es.js", | ||
"types": "types/index.d.ts", | ||
"author": "Sudhanshu Yadav", | ||
"license": "MIT", | ||
"types": "typings/number_format.d.ts", | ||
"repository": { | ||
@@ -29,9 +29,9 @@ "type": "git", | ||
"start": "webpack-dev-server --hot", | ||
"format": "prettier --write ./src ./typings ./test ./example", | ||
"bundle": "cross-env yarn compile && yarn bundle-dist && yarn test-build && yarn test-ts && yarn format", | ||
"bundle-dist": "cross-env NODE_ENV=production rollup -c rollup.config.js", | ||
"compile": "cross-env NODE_ENV=production babel src --out-dir lib", | ||
"format": "prettier --write ./src ./test ./example", | ||
"build": "cross-env yarn build-dist && yarn test-build && yarn test-ts && yarn format", | ||
"build-dist": "cross-env NODE_ENV=production rollup -c rollup.config.js", | ||
"build-types": "tsc -p tsconfig.build.json", | ||
"test": "cross-env NODE_ENV=test karma start && yarn test-ts", | ||
"test-build": "cross-env NODE_ENV=production TEST_BROWSER=ChromeHeadless karma start", | ||
"test-ts": "yarn tsc -p typings", | ||
"test-ts": "yarn tsc --noEmit", | ||
"lint": "cross-env eslint src/**" | ||
@@ -49,5 +49,7 @@ }, | ||
"@rollup/plugin-buble": "^0.21.3", | ||
"@rollup/plugin-typescript": "^8.3.0", | ||
"@testing-library/react": "^12.1.2", | ||
"@testing-library/user-event": "^13.5.0", | ||
"@types/react": "^18.0.6", | ||
"@typescript-eslint/parser": "^5.15.0", | ||
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.7", | ||
@@ -67,3 +69,2 @@ "babel-eslint": "^10.0.3", | ||
"eslint-plugin-react": "^7.15.1", | ||
"flow-bin": "^0.118.0", | ||
"jasmine": "^2.4.1", | ||
@@ -79,8 +80,8 @@ "jasmine-core": "^2.4.1", | ||
"karma-spec-reporter": "^0.0.32", | ||
"karma-webpack": "^4.0.2", | ||
"karma-webpack": "^5.0.0", | ||
"material-ui": "^0.20.2", | ||
"pascal-case": "3.1.2", | ||
"prettier": "^2.2.1", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0", | ||
"react-router": "5", | ||
"react-transform-hmr": "^1.0.4", | ||
@@ -91,3 +92,2 @@ "rollup": "^1.22.0", | ||
"rollup-plugin-filesize": "^6.2.0", | ||
"rollup-plugin-flow": "^1.1.1", | ||
"rollup-plugin-license": "^0.12.1", | ||
@@ -97,6 +97,7 @@ "rollup-plugin-node-resolve": "^5.2.0", | ||
"rollup-plugin-uglify": "^6.0.3", | ||
"ts-loader": "^9.2.6", | ||
"typescript": "^4.6.3", | ||
"webpack": "^4.41.0", | ||
"webpack-cli": "^3.3.9", | ||
"webpack-dev-server": "^3.8.2" | ||
"webpack": "^5.69.1", | ||
"webpack-cli": "^4.9.2", | ||
"webpack-dev-server": "^4.7.4" | ||
}, | ||
@@ -103,0 +104,0 @@ "peerDependencies": { |
@@ -431,3 +431,3 @@ [![Actions Status](https://github.com/s-yadav/react-number-format/workflows/CI/badge.svg)](https://github.com/s-yadav/react-number-format/actions) | ||
- `yarn test` to test changes | ||
- `yarn bundle` to bundle files | ||
- `yarn build` to bundle files | ||
@@ -434,0 +434,0 @@ #### Testing |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
28
0
231683
58
4282
3
1