UNPKG

react-number-format

Version:

React component to format number in an input or as a text.

205 lines (167 loc) 6.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.noop = noop; exports.returnTrue = returnTrue; exports.charIsNumber = charIsNumber; exports.escapeRegExp = escapeRegExp; exports.getThousandsGroupRegex = getThousandsGroupRegex; exports.applyThousandSeparator = applyThousandSeparator; exports.splitDecimal = splitDecimal; exports.fixLeadingZero = fixLeadingZero; exports.limitToScale = limitToScale; exports.roundToPrecision = roundToPrecision; exports.omit = omit; exports.setCaretPosition = setCaretPosition; exports.findChangedIndex = findChangedIndex; exports.clamp = clamp; exports.getCurrentCaretPosition = getCurrentCaretPosition; // basic noop function function noop() {} function returnTrue() { return true; } function charIsNumber(char) { return !!(char || '').match(/\d/); } 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); } //spilt a float number into different parts beforeDecimal, afterDecimal, and negation function splitDecimal(numStr) { var allowNegative = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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 "".concat(isNegative ? '-' : '').concat(beforeDecimal).concat(afterDecimal ? ".".concat(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; } return str; } /** * 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 _splitDecimal = splitDecimal(numStr), beforeDecimal = _splitDecimal.beforeDecimal, afterDecimal = _splitDecimal.afterDecimal, hasNagation = _splitDecimal.hasNagation; var roundedDecimalParts = parseFloat("0.".concat(afterDecimal || '0')).toFixed(scale).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 "".concat(negation).concat(intPart).concat(decimalSeparator).concat(decimalPart); } function omit(obj, keyMaps) { var filteredObj = {}; Object.keys(obj).forEach(function (key) { if (!keyMaps[key]) filteredObj[key] = obj[key]; }); return filteredObj; } /** 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; } } /** 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 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); }