currency-fomatter 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,686 @@
1
+ import { __assign, __extends } from "tslib";
2
+ //@flow
3
+ import PropTypes from "prop-types";
4
+ import React, { Component } from "react";
5
+ import { noop, returnTrue, charIsNumber, escapeRegExp, fixLeadingZero, splitString, limitToScale, roundToPrecision, omit, setCaretPosition, thousandGroupSpacing, } from "./utils";
6
+ var propTypes = {
7
+ thousandSeparator: PropTypes.oneOfType([PropTypes.string, PropTypes.oneOf([true])]),
8
+ thousandSpacing: PropTypes.oneOf(["2", "2s", "3", "4"]),
9
+ decimalSeparator: PropTypes.string,
10
+ decimalScale: PropTypes.number,
11
+ fixedDecimalScale: PropTypes.bool,
12
+ displayType: PropTypes.oneOf(["input", "text"]),
13
+ prefix: PropTypes.string,
14
+ suffix: PropTypes.string,
15
+ format: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
16
+ removeFormatting: PropTypes.func,
17
+ mask: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
18
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
19
+ isNumericString: PropTypes.bool,
20
+ customInput: PropTypes.func,
21
+ allowNegative: PropTypes.bool,
22
+ onValueChange: PropTypes.func,
23
+ onKeyDown: PropTypes.func,
24
+ onMouseUp: PropTypes.func,
25
+ onChange: PropTypes.func,
26
+ onFocus: PropTypes.func,
27
+ onBlur: PropTypes.func,
28
+ type: PropTypes.oneOf(["text", "tel"]),
29
+ isAllowed: PropTypes.func,
30
+ renderText: PropTypes.func,
31
+ };
32
+ var defaultProps = {
33
+ displayType: "input",
34
+ decimalSeparator: ".",
35
+ thousandSpacing: "3",
36
+ fixedDecimalScale: false,
37
+ prefix: "",
38
+ suffix: "",
39
+ allowNegative: true,
40
+ isNumericString: false,
41
+ type: "text",
42
+ onValueChange: noop,
43
+ onChange: noop,
44
+ onKeyDown: noop,
45
+ onMouseUp: noop,
46
+ onFocus: noop,
47
+ onBlur: noop,
48
+ isAllowed: returnTrue,
49
+ };
50
+ var CurrencyFormat = /** @class */ (function (_super) {
51
+ __extends(CurrencyFormat, _super);
52
+ function CurrencyFormat(props) {
53
+ var _this = _super.call(this, props) || this;
54
+ //validate props
55
+ _this.validateProps();
56
+ var formattedValue = _this.formatValueProp();
57
+ _this.state = {
58
+ value: formattedValue,
59
+ numAsString: _this.removeFormatting(formattedValue),
60
+ };
61
+ _this.onChange = _this.onChange.bind(_this);
62
+ _this.onKeyDown = _this.onKeyDown.bind(_this);
63
+ _this.onMouseUp = _this.onMouseUp.bind(_this);
64
+ _this.onFocus = _this.onFocus.bind(_this);
65
+ _this.onBlur = _this.onBlur.bind(_this);
66
+ return _this;
67
+ }
68
+ CurrencyFormat.prototype.componentDidUpdate = function (prevProps) {
69
+ this.updateValueIfRequired(prevProps);
70
+ };
71
+ CurrencyFormat.prototype.updateValueIfRequired = function (prevProps) {
72
+ var _a = this, props = _a.props, state = _a.state;
73
+ if (prevProps !== props) {
74
+ //validate props
75
+ this.validateProps();
76
+ var stateValue = state.value;
77
+ var lastNumStr = state.numAsString || "";
78
+ var formattedValue = props.value === undefined ? this.formatNumString(lastNumStr) : this.formatValueProp();
79
+ if (formattedValue !== stateValue) {
80
+ this.setState({
81
+ value: formattedValue,
82
+ numAsString: this.removeFormatting(formattedValue),
83
+ });
84
+ }
85
+ }
86
+ };
87
+ /** Misc methods **/
88
+ CurrencyFormat.prototype.getFloatString = function (num) {
89
+ if (num === void 0) { num = ""; }
90
+ var decimalSeparator = this.getSeparators().decimalSeparator;
91
+ var numRegex = this.getNumberRegex(true);
92
+ //remove negation for regex check
93
+ var hasNegation = num[0] === "-";
94
+ if (hasNegation)
95
+ num = num.replace("-", "");
96
+ num = (num.match(numRegex) || []).join("").replace(decimalSeparator, ".");
97
+ //remove extra decimals
98
+ var firstDecimalIndex = num.indexOf(".");
99
+ if (firstDecimalIndex !== -1) {
100
+ num = "".concat(num.substring(0, firstDecimalIndex), ".").concat(num
101
+ .substring(firstDecimalIndex + 1, num.length)
102
+ .replace(new RegExp(escapeRegExp(decimalSeparator), "g"), ""));
103
+ }
104
+ //add negation back
105
+ if (hasNegation)
106
+ num = "-" + num;
107
+ return num;
108
+ };
109
+ //returned regex assumes decimalSeparator is as per prop
110
+ CurrencyFormat.prototype.getNumberRegex = function (g, ignoreDecimalSeparator) {
111
+ var _a = this.props, format = _a.format, decimalScale = _a.decimalScale;
112
+ var decimalSeparator = this.getSeparators().decimalSeparator;
113
+ return new RegExp("\\d" +
114
+ (decimalSeparator && decimalScale !== 0 && !ignoreDecimalSeparator && !format
115
+ ? "|" + escapeRegExp(decimalSeparator)
116
+ : ""), g ? "g" : undefined);
117
+ };
118
+ CurrencyFormat.prototype.getSeparators = function () {
119
+ var _a = this.props, decimalSeparator = _a.decimalSeparator, thousandSpacing = _a.thousandSpacing;
120
+ var thousandSeparator = this.props.thousandSeparator;
121
+ if (thousandSeparator === true) {
122
+ thousandSeparator = ",";
123
+ }
124
+ return {
125
+ decimalSeparator: decimalSeparator,
126
+ thousandSeparator: thousandSeparator,
127
+ thousandSpacing: thousandSpacing,
128
+ };
129
+ };
130
+ CurrencyFormat.prototype.getMaskAtIndex = function (index) {
131
+ var _a = this.props.mask, mask = _a === void 0 ? " " : _a;
132
+ if (typeof mask === "string") {
133
+ return mask;
134
+ }
135
+ return mask[index] || " ";
136
+ };
137
+ CurrencyFormat.prototype.validateProps = function () {
138
+ var mask = this.props.mask;
139
+ //validate decimalSeparator and thousandSeparator
140
+ var _a = this.getSeparators(), decimalSeparator = _a.decimalSeparator, thousandSeparator = _a.thousandSeparator;
141
+ if (decimalSeparator === thousandSeparator) {
142
+ throw new Error("\n Decimal separator can't be same as thousand separator.\n\n thousandSeparator: ".concat(thousandSeparator, " (thousandSeparator = {true} is same as thousandSeparator = \",\")\n decimalSeparator: ").concat(decimalSeparator, " (default value for decimalSeparator is .)\n "));
143
+ }
144
+ //validate mask
145
+ if (mask) {
146
+ var maskAsStr = mask === "string" ? mask : mask.toString();
147
+ if (maskAsStr.match(/\d/g)) {
148
+ throw new Error("\n Mask ".concat(mask, " should not contain numeric character;\n "));
149
+ }
150
+ }
151
+ };
152
+ CurrencyFormat.prototype.splitDecimal = function (numStr) {
153
+ var allowNegative = this.props.allowNegative;
154
+ var hasNagation = numStr[0] === "-";
155
+ var addNegation = hasNagation && allowNegative;
156
+ numStr = numStr.replace("-", "");
157
+ var parts = numStr.split(".");
158
+ var beforeDecimal = parts[0];
159
+ var afterDecimal = parts[1] || "";
160
+ return {
161
+ beforeDecimal: beforeDecimal,
162
+ afterDecimal: afterDecimal,
163
+ hasNagation: hasNagation,
164
+ addNegation: addNegation,
165
+ };
166
+ };
167
+ /** Misc methods end **/
168
+ /** caret specific methods **/
169
+ CurrencyFormat.prototype.setPatchedCaretPosition = function (el, caretPos, currentValue) {
170
+ /* setting caret position within timeout of 0ms is required for mobile chrome,
171
+ otherwise browser resets the caret position after we set it
172
+ We are also setting it without timeout so that in normal browser we don't see the flickering */
173
+ setCaretPosition(el, caretPos);
174
+ setTimeout(function () {
175
+ if (el.value === currentValue)
176
+ setCaretPosition(el, caretPos);
177
+ }, 0);
178
+ };
179
+ /* This keeps the caret within typing area so people can't type in between prefix or suffix */
180
+ CurrencyFormat.prototype.correctCaretPosition = function (value, caretPos, direction) {
181
+ var _a = this.props, prefix = _a.prefix, suffix = _a.suffix, format = _a.format;
182
+ //in case of format as number limit between prefix and suffix
183
+ if (!format) {
184
+ var hasNegation = value[0] === "-";
185
+ return Math.min(Math.max(caretPos, prefix.length + (hasNegation ? 1 : 0)), value.length - suffix.length);
186
+ }
187
+ //in case if custom format method don't do anything
188
+ if (typeof format === "function")
189
+ return caretPos;
190
+ /* in case format is string find the closest # position from the caret position */
191
+ //in case the caretPos have input value on it don't do anything
192
+ if (format[caretPos] === "#" && charIsNumber(value[caretPos]))
193
+ return caretPos;
194
+ //if caretPos is just after input value don't do anything
195
+ if (format[caretPos - 1] === "#" && charIsNumber(value[caretPos - 1]))
196
+ return caretPos;
197
+ //find the nearest caret position
198
+ var firstHashPosition = format.indexOf("#");
199
+ var lastHashPosition = format.lastIndexOf("#");
200
+ //limit the cursor between the first # position and the last # position
201
+ caretPos = Math.min(Math.max(caretPos, firstHashPosition), lastHashPosition + 1);
202
+ var nextPos = format.substring(caretPos, format.length).indexOf("#");
203
+ var caretLeftBound = caretPos;
204
+ var caretRightBoud = caretPos + (nextPos === -1 ? 0 : nextPos);
205
+ //get the position where the last number is present
206
+ while (caretLeftBound > firstHashPosition &&
207
+ (format[caretLeftBound] !== "#" || !charIsNumber(value[caretLeftBound]))) {
208
+ caretLeftBound -= 1;
209
+ }
210
+ var goToLeft = !charIsNumber(value[caretRightBoud]) ||
211
+ (direction === "left" && caretPos !== firstHashPosition) ||
212
+ caretPos - caretLeftBound < caretRightBoud - caretPos;
213
+ return goToLeft ? caretLeftBound + 1 : caretRightBoud;
214
+ };
215
+ CurrencyFormat.prototype.getCaretPosition = function (inputValue, formattedValue, caretPos) {
216
+ var format = this.props.format;
217
+ var stateValue = this.state.value;
218
+ var numRegex = this.getNumberRegex(true);
219
+ var inputNumber = (inputValue.match(numRegex) || []).join("");
220
+ var formattedNumber = (formattedValue.match(numRegex) || []).join("");
221
+ var j, i;
222
+ j = 0;
223
+ for (i = 0; i < caretPos; i++) {
224
+ var currentInputChar = inputValue[i] || "";
225
+ var currentFormatChar = formattedValue[j] || "";
226
+ //no need to increase new cursor position if formatted value does not have those characters
227
+ //case inputValue = 1a23 and formattedValue = 123
228
+ if (!currentInputChar.match(numRegex) && currentInputChar !== currentFormatChar)
229
+ continue;
230
+ //When we are striping out leading zeros maintain the new cursor position
231
+ //Case inputValue = 00023 and formattedValue = 23;
232
+ if (currentInputChar === "0" &&
233
+ currentFormatChar.match(numRegex) &&
234
+ currentFormatChar !== "0" &&
235
+ inputNumber.length !== formattedNumber.length)
236
+ continue;
237
+ //we are not using currentFormatChar because j can change here
238
+ while (currentInputChar !== formattedValue[j] && j < formattedValue.length)
239
+ j++;
240
+ j++;
241
+ }
242
+ if (typeof format === "string" && !stateValue) {
243
+ //set it to the maximum value so it goes after the last number
244
+ j = formattedValue.length;
245
+ }
246
+ //correct caret position if its outside of editable area
247
+ j = this.correctCaretPosition(formattedValue, j);
248
+ return j;
249
+ };
250
+ /** caret specific methods ends **/
251
+ /** methods to remove formattting **/
252
+ CurrencyFormat.prototype.removePrefixAndSuffix = function (val) {
253
+ var _a = this.props, format = _a.format, prefix = _a.prefix, suffix = _a.suffix;
254
+ //remove prefix and suffix
255
+ if (!format && val) {
256
+ var isNegative = val[0] === "-";
257
+ //remove negation sign
258
+ if (isNegative)
259
+ val = val.substring(1, val.length);
260
+ //remove prefix
261
+ val = prefix && val.indexOf(prefix) === 0 ? val.substring(prefix.length, val.length) : val;
262
+ //remove suffix
263
+ var suffixLastIndex = val.lastIndexOf(suffix);
264
+ val =
265
+ suffix && suffixLastIndex !== -1 && suffixLastIndex === val.length - suffix.length
266
+ ? val.substring(0, suffixLastIndex)
267
+ : val;
268
+ //add negation sign back
269
+ if (isNegative)
270
+ val = "-" + val;
271
+ }
272
+ return val;
273
+ };
274
+ CurrencyFormat.prototype.removePatternFormatting = function (val) {
275
+ var format = this.props.format;
276
+ var formatArray = format.split("#").filter(function (str) { return str !== ""; });
277
+ var start = 0;
278
+ var numStr = "";
279
+ for (var i = 0, ln = formatArray.length; i <= ln; i++) {
280
+ var part = formatArray[i] || "";
281
+ //if i is the last fragment take the index of end of the value
282
+ //For case like +1 (911) 911 91 91 having pattern +1 (###) ### ## ##
283
+ var index = i === ln ? val.length : val.indexOf(part, start);
284
+ /* in any case if we don't find the pattern part in the value assume the val as numeric string
285
+ This will be also in case if user has started typing, in any other case it will not be -1
286
+ unless wrong prop value is provided */
287
+ if (index === -1) {
288
+ numStr = val;
289
+ break;
290
+ }
291
+ else {
292
+ numStr += val.substring(start, index);
293
+ start = index + part.length;
294
+ }
295
+ }
296
+ return (numStr.match(/\d/g) || []).join("");
297
+ };
298
+ CurrencyFormat.prototype.removeFormatting = function (val) {
299
+ var _a = this.props, format = _a.format, removeFormatting = _a.removeFormatting;
300
+ if (!val)
301
+ return val;
302
+ if (!format) {
303
+ val = this.removePrefixAndSuffix(val);
304
+ val = this.getFloatString(val);
305
+ }
306
+ else if (typeof format === "string") {
307
+ val = this.removePatternFormatting(val);
308
+ }
309
+ else if (typeof removeFormatting === "function") {
310
+ //condition need to be handled if format method is provide,
311
+ val = removeFormatting(val);
312
+ }
313
+ else {
314
+ val = (val.match(/\d/g) || []).join("");
315
+ }
316
+ return val;
317
+ };
318
+ /** methods to remove formattting end **/
319
+ /*** format specific methods start ***/
320
+ /**
321
+ * Format when # based string is provided
322
+ * @param {string} numStr Numeric String
323
+ * @return {string} formatted Value
324
+ */
325
+ CurrencyFormat.prototype.formatWithPattern = function (numStr) {
326
+ var format = this.props.format;
327
+ var hashCount = 0;
328
+ var formattedNumberAry = format.split("");
329
+ for (var i = 0, ln = format.length; i < ln; i++) {
330
+ if (format[i] === "#") {
331
+ formattedNumberAry[i] = numStr[hashCount] || this.getMaskAtIndex(hashCount);
332
+ hashCount += 1;
333
+ }
334
+ }
335
+ return formattedNumberAry.join("");
336
+ };
337
+ /**
338
+ * Format the given string according to thousand separator and thousand spacing
339
+ * @param {*} beforeDecimal
340
+ * @param {*} thousandSeparator
341
+ * @param {*} thousandSpacing
342
+ */
343
+ CurrencyFormat.prototype.formatThousand = function (beforeDecimal, thousandSeparator, thousandSpacing) {
344
+ var digitalGroup;
345
+ switch (thousandSpacing) {
346
+ case thousandGroupSpacing.two:
347
+ digitalGroup = /(\d)(?=(\d{2})+(?!\d))/g;
348
+ break;
349
+ case thousandGroupSpacing.twoScaled:
350
+ digitalGroup = /(\d)(?=(((\d{2})+)(\d{1})(?!\d)))/g;
351
+ break;
352
+ case thousandGroupSpacing.four:
353
+ digitalGroup = /(\d)(?=(\d{4})+(?!\d))/g;
354
+ break;
355
+ default:
356
+ digitalGroup = /(\d)(?=(\d{3})+(?!\d))/g;
357
+ }
358
+ return beforeDecimal.replace(digitalGroup, "$1" + thousandSeparator);
359
+ };
360
+ /**
361
+ * @param {string} numStr Numeric string/floatString] It always have decimalSeparator as .
362
+ * @return {string} formatted Value
363
+ */
364
+ CurrencyFormat.prototype.formatAsNumber = function (numStr) {
365
+ var _a = this.props, decimalScale = _a.decimalScale, fixedDecimalScale = _a.fixedDecimalScale, prefix = _a.prefix, suffix = _a.suffix;
366
+ var _b = this.getSeparators(), thousandSeparator = _b.thousandSeparator, decimalSeparator = _b.decimalSeparator, thousandSpacing = _b.thousandSpacing;
367
+ var hasDecimalSeparator = numStr.indexOf(".") !== -1 || (decimalScale && fixedDecimalScale);
368
+ var _c = this.splitDecimal(numStr), beforeDecimal = _c.beforeDecimal, afterDecimal = _c.afterDecimal, addNegation = _c.addNegation; // eslint-disable-line prefer-const
369
+ //apply decimal precision if its defined
370
+ if (decimalScale !== undefined)
371
+ afterDecimal = limitToScale(afterDecimal, decimalScale, fixedDecimalScale);
372
+ if (thousandSeparator) {
373
+ beforeDecimal = this.formatThousand(beforeDecimal, thousandSeparator, thousandSpacing);
374
+ }
375
+ //add prefix and suffix
376
+ if (prefix)
377
+ beforeDecimal = prefix + beforeDecimal;
378
+ if (suffix)
379
+ afterDecimal = afterDecimal + suffix;
380
+ //restore negation sign
381
+ if (addNegation)
382
+ beforeDecimal = "-" + beforeDecimal;
383
+ numStr = beforeDecimal + ((hasDecimalSeparator && decimalSeparator) || "") + afterDecimal;
384
+ return numStr;
385
+ };
386
+ CurrencyFormat.prototype.formatNumString = function (value) {
387
+ if (value === void 0) { value = ""; }
388
+ var format = this.props.format;
389
+ var formattedValue = value;
390
+ if (value === "") {
391
+ formattedValue = "";
392
+ }
393
+ else if (value === "-" && !format) {
394
+ formattedValue = "-";
395
+ value = "";
396
+ }
397
+ else if (typeof format === "string") {
398
+ formattedValue = this.formatWithPattern(formattedValue);
399
+ }
400
+ else if (typeof format === "function") {
401
+ formattedValue = format(formattedValue);
402
+ }
403
+ else {
404
+ formattedValue = this.formatAsNumber(formattedValue);
405
+ }
406
+ return formattedValue;
407
+ };
408
+ CurrencyFormat.prototype.formatValueProp = function () {
409
+ var _a = this.props, format = _a.format, decimalScale = _a.decimalScale, fixedDecimalScale = _a.fixedDecimalScale;
410
+ var _b = this.props, value = _b.value, isNumericString = _b.isNumericString;
411
+ // if value is not defined return empty string
412
+ if (value === undefined)
413
+ return "";
414
+ if (typeof value === "number") {
415
+ value = value.toString();
416
+ isNumericString = true;
417
+ }
418
+ //round the number based on decimalScale
419
+ //format only if non formatted value is provided
420
+ if (isNumericString && !format && typeof decimalScale === "number") {
421
+ value = roundToPrecision(value, decimalScale, fixedDecimalScale);
422
+ }
423
+ var formattedValue = isNumericString ? this.formatNumString(value) : this.formatInput(value);
424
+ return formattedValue;
425
+ };
426
+ CurrencyFormat.prototype.formatNegation = function (value) {
427
+ if (value === void 0) { value = ""; }
428
+ var allowNegative = this.props.allowNegative;
429
+ var negationRegex = new RegExp("(-)");
430
+ var doubleNegationRegex = new RegExp("(-)(.)*(-)");
431
+ // Check number has '-' value
432
+ var hasNegation = negationRegex.test(value);
433
+ // Check number has 2 or more '-' values
434
+ var removeNegation = doubleNegationRegex.test(value);
435
+ //remove negation
436
+ value = value.replace(/-/g, "");
437
+ if (hasNegation && !removeNegation && allowNegative) {
438
+ value = "-" + value;
439
+ }
440
+ return value;
441
+ };
442
+ CurrencyFormat.prototype.formatInput = function (value) {
443
+ if (value === void 0) { value = ""; }
444
+ var format = this.props.format;
445
+ //format negation only if we are formatting as number
446
+ if (!format) {
447
+ value = this.formatNegation(value);
448
+ }
449
+ //remove formatting from number
450
+ value = this.removeFormatting(value);
451
+ return this.formatNumString(value);
452
+ };
453
+ /*** format specific methods end ***/
454
+ CurrencyFormat.prototype.isCharacterAFormat = function (caretPos, value) {
455
+ var _a = this.props, format = _a.format, prefix = _a.prefix, suffix = _a.suffix, decimalScale = _a.decimalScale, fixedDecimalScale = _a.fixedDecimalScale;
456
+ var decimalSeparator = this.getSeparators().decimalSeparator;
457
+ //check within format pattern
458
+ if (typeof format === "string" && format[caretPos] !== "#")
459
+ return true;
460
+ //check in number format
461
+ if (!format &&
462
+ (caretPos < prefix.length ||
463
+ caretPos >= value.length - suffix.length ||
464
+ (decimalScale && fixedDecimalScale && value[caretPos] === decimalSeparator))) {
465
+ return true;
466
+ }
467
+ return false;
468
+ };
469
+ CurrencyFormat.prototype.checkIfFormatGotDeleted = function (start, end, value) {
470
+ for (var i = start; i < end; i++) {
471
+ if (this.isCharacterAFormat(i, value))
472
+ return true;
473
+ }
474
+ return false;
475
+ };
476
+ /**
477
+ * This will check if any formatting got removed by the delete or backspace and reset the value
478
+ * It will also work as fallback if android chome keyDown handler does not work
479
+ **/
480
+ CurrencyFormat.prototype.correctInputValue = function (caretPos, lastValue, value) {
481
+ var format = this.props.format;
482
+ var lastNumStr = this.state.numAsString || "";
483
+ //don't do anyhting if something got added, or if value is empty string (when whole input is cleared)
484
+ if (value.length >= lastValue.length || !value.length) {
485
+ return value;
486
+ }
487
+ var start = caretPos;
488
+ var lastValueParts = splitString(lastValue, caretPos);
489
+ var newValueParts = splitString(value, caretPos);
490
+ var deletedIndex = lastValueParts[1].lastIndexOf(newValueParts[1]);
491
+ var diff = deletedIndex !== -1 ? lastValueParts[1].substring(0, deletedIndex) : "";
492
+ var end = start + diff.length;
493
+ //if format got deleted reset the value to last value
494
+ if (this.checkIfFormatGotDeleted(start, end, lastValue)) {
495
+ value = lastValue;
496
+ }
497
+ //for numbers check if beforeDecimal got deleted and there is nothing after decimal,
498
+ //clear all numbers in such case while keeping the - sign
499
+ if (!format) {
500
+ var numericString = this.removeFormatting(value);
501
+ var _a = this.splitDecimal(numericString), beforeDecimal = _a.beforeDecimal, afterDecimal = _a.afterDecimal, addNegation = _a.addNegation; // eslint-disable-line prefer-const
502
+ //clear only if something got deleted
503
+ if (numericString.length < lastNumStr.length && beforeDecimal === "" && !parseFloat(afterDecimal)) {
504
+ return addNegation ? "-" : "";
505
+ }
506
+ }
507
+ return value;
508
+ };
509
+ CurrencyFormat.prototype.onChange = function (e) {
510
+ e.persist();
511
+ var el = e.target;
512
+ var inputValue = el.value;
513
+ var _a = this, state = _a.state, props = _a.props;
514
+ var isAllowed = props.isAllowed;
515
+ var lastValue = state.value || "";
516
+ /*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/
517
+ var currentCaretPosition = Math.max(el.selectionStart, el.selectionEnd);
518
+ inputValue = this.correctInputValue(currentCaretPosition, lastValue, inputValue);
519
+ var formattedValue = this.formatInput(inputValue) || "";
520
+ var numAsString = this.removeFormatting(formattedValue);
521
+ var valueObj = {
522
+ formattedValue: formattedValue,
523
+ value: numAsString,
524
+ floatValue: parseFloat(numAsString),
525
+ };
526
+ if (!isAllowed(valueObj)) {
527
+ formattedValue = lastValue;
528
+ }
529
+ //set the value imperatively, this is required for IE fix
530
+ el.value = formattedValue;
531
+ //get the caret position
532
+ var caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition);
533
+ //set caret position
534
+ this.setPatchedCaretPosition(el, caretPos, formattedValue);
535
+ //change the state
536
+ if (formattedValue !== lastValue) {
537
+ this.setState({ value: formattedValue, numAsString: numAsString }, function () {
538
+ props.onValueChange(valueObj);
539
+ props.onChange(e);
540
+ });
541
+ }
542
+ else {
543
+ props.onChange(e);
544
+ }
545
+ };
546
+ CurrencyFormat.prototype.onBlur = function (e) {
547
+ var _a = this, props = _a.props, state = _a.state;
548
+ var format = props.format, onBlur = props.onBlur;
549
+ var numAsString = state.numAsString;
550
+ var lastValue = state.value;
551
+ if (!format) {
552
+ numAsString = fixLeadingZero(numAsString);
553
+ var formattedValue = this.formatNumString(numAsString);
554
+ var valueObj_1 = {
555
+ formattedValue: formattedValue,
556
+ value: numAsString,
557
+ floatValue: parseFloat(numAsString),
558
+ };
559
+ //change the state
560
+ if (formattedValue !== lastValue) {
561
+ // the event needs to be persisted because its properties can be accessed in an asynchronous way
562
+ e.persist();
563
+ this.setState({ value: formattedValue, numAsString: numAsString }, function () {
564
+ props.onValueChange(valueObj_1);
565
+ onBlur(e);
566
+ });
567
+ return;
568
+ }
569
+ }
570
+ onBlur(e);
571
+ };
572
+ CurrencyFormat.prototype.onKeyDown = function (e) {
573
+ var el = e.target;
574
+ var key = e.key;
575
+ var selectionEnd = el.selectionEnd, value = el.value;
576
+ var selectionStart = el.selectionStart;
577
+ var expectedCaretPosition;
578
+ var _a = this.props, decimalScale = _a.decimalScale, fixedDecimalScale = _a.fixedDecimalScale, prefix = _a.prefix, suffix = _a.suffix, format = _a.format, onKeyDown = _a.onKeyDown;
579
+ var ignoreDecimalSeparator = decimalScale !== undefined && fixedDecimalScale;
580
+ var numRegex = this.getNumberRegex(false, ignoreDecimalSeparator);
581
+ var negativeRegex = new RegExp("-");
582
+ var isPatternFormat = typeof format === "string";
583
+ //Handle backspace and delete against non numerical/decimal characters or arrow keys
584
+ if (key === "ArrowLeft" || key === "Backspace") {
585
+ expectedCaretPosition = selectionStart - 1;
586
+ }
587
+ else if (key === "ArrowRight") {
588
+ expectedCaretPosition = selectionStart + 1;
589
+ }
590
+ else if (key === "Delete") {
591
+ expectedCaretPosition = selectionStart;
592
+ }
593
+ //if expectedCaretPosition is not set it means we don't want to Handle keyDown
594
+ //also if multiple characters are selected don't handle
595
+ if (expectedCaretPosition === undefined || selectionStart !== selectionEnd) {
596
+ onKeyDown(e);
597
+ return;
598
+ }
599
+ var newCaretPosition = expectedCaretPosition;
600
+ var leftBound = isPatternFormat ? format.indexOf("#") : prefix.length;
601
+ var rightBound = isPatternFormat ? format.lastIndexOf("#") + 1 : value.length - suffix.length;
602
+ if (key === "ArrowLeft" || key === "ArrowRight") {
603
+ var direction = key === "ArrowLeft" ? "left" : "right";
604
+ newCaretPosition = this.correctCaretPosition(value, expectedCaretPosition, direction);
605
+ }
606
+ else if (key === "Delete" &&
607
+ !numRegex.test(value[expectedCaretPosition]) &&
608
+ !negativeRegex.test(value[expectedCaretPosition])) {
609
+ while (!numRegex.test(value[newCaretPosition]) && newCaretPosition < rightBound)
610
+ newCaretPosition++;
611
+ }
612
+ else if (key === "Backspace" &&
613
+ !numRegex.test(value[expectedCaretPosition]) &&
614
+ !negativeRegex.test(value[expectedCaretPosition])) {
615
+ while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) {
616
+ newCaretPosition--;
617
+ }
618
+ newCaretPosition = this.correctCaretPosition(value, newCaretPosition, "left");
619
+ }
620
+ if (newCaretPosition !== expectedCaretPosition ||
621
+ expectedCaretPosition < leftBound ||
622
+ expectedCaretPosition > rightBound) {
623
+ e.preventDefault();
624
+ this.setPatchedCaretPosition(el, newCaretPosition, value);
625
+ }
626
+ /* NOTE: this is just required for unit test as we need to get the newCaretPosition,
627
+ Remove this when you find different solution */
628
+ if (e.isUnitTestRun) {
629
+ this.setPatchedCaretPosition(el, newCaretPosition, value);
630
+ }
631
+ this.props.onKeyDown(e);
632
+ };
633
+ /** required to handle the caret position when click anywhere within the input **/
634
+ CurrencyFormat.prototype.onMouseUp = function (e) {
635
+ var el = e.target;
636
+ var selectionStart = el.selectionStart, selectionEnd = el.selectionEnd, value = el.value;
637
+ if (selectionStart === selectionEnd) {
638
+ var caretPostion = this.correctCaretPosition(value, selectionStart);
639
+ if (caretPostion !== selectionStart) {
640
+ this.setPatchedCaretPosition(el, caretPostion, value);
641
+ }
642
+ }
643
+ this.props.onMouseUp(e);
644
+ };
645
+ CurrencyFormat.prototype.onFocus = function (e) {
646
+ var _this = this;
647
+ // Workaround Chrome and Safari bug https://bugs.chromium.org/p/chromium/issues/detail?id=779328
648
+ // (onFocus event target selectionStart is always 0 before setTimeout)
649
+ e.persist();
650
+ setTimeout(function () {
651
+ var el = e.target;
652
+ var selectionStart = el.selectionStart, value = el.value;
653
+ var caretPosition = _this.correctCaretPosition(value, selectionStart);
654
+ if (caretPosition !== selectionStart) {
655
+ _this.setPatchedCaretPosition(el, caretPosition, value);
656
+ }
657
+ _this.props.onFocus(e);
658
+ });
659
+ };
660
+ CurrencyFormat.prototype.render = function () {
661
+ var _a = this.props, type = _a.type, displayType = _a.displayType, customInput = _a.customInput, renderText = _a.renderText;
662
+ var value = this.state.value;
663
+ var otherProps = omit(this.props, propTypes);
664
+ var inputProps = Object.assign({}, otherProps, {
665
+ type: type,
666
+ value: value,
667
+ onChange: this.onChange,
668
+ onKeyDown: this.onKeyDown,
669
+ onMouseUp: this.onMouseUp,
670
+ onFocus: this.onFocus,
671
+ onBlur: this.onBlur,
672
+ });
673
+ if (displayType === "text") {
674
+ return renderText ? renderText(value) || null : React.createElement("span", __assign({}, otherProps), value);
675
+ }
676
+ else if (customInput) {
677
+ var CustomInput = customInput;
678
+ return React.createElement(CustomInput, __assign({}, inputProps));
679
+ }
680
+ return React.createElement("input", __assign({}, inputProps));
681
+ };
682
+ return CurrencyFormat;
683
+ }(Component));
684
+ CurrencyFormat.defaultProps = defaultProps;
685
+ module.exports = CurrencyFormat;
686
+ //# sourceMappingURL=index.js.map