Skip to content

Commit

Permalink
- Maintain caret position if suffix/prefix is updated while typing #249.
Browse files Browse the repository at this point in the history
- Refactor code to have one place to update state and caret position.

- Add spec for maintaining caret pos when . is used instead of decimal separator
  • Loading branch information
s-yadav committed Nov 11, 2018
1 parent 735082a commit b424a45
Show file tree
Hide file tree
Showing 11 changed files with 681 additions and 556 deletions.
135 changes: 81 additions & 54 deletions dist/react-number-format.es.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* react-number-format - 4.0.3
* react-number-format - 4.0.4
* Author : Sudhanshu Yadav
* Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license.
* https://github.com/s-yadav/react-number-format
Expand Down Expand Up @@ -496,6 +496,10 @@ function findChangedIndex(prevValue, newValue) {
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);
}

var propTypes$1 = {
thousandSeparator: propTypes.oneOfType([propTypes.string, propTypes.oneOf([true])]),
Expand Down Expand Up @@ -590,8 +594,7 @@ function (_React$Component) {
value: function updateValueIfRequired(prevProps) {
var props = this.props,
state = this.state,
inFocus = this.inFocus;
var onValueChange = props.onValueChange;
focusedElm = this.focusedElm;
var stateValue = state.value,
_state$numAsString = state.numAsString,
lastNumStr = _state$numAsString === void 0 ? '' : _state$numAsString;
Expand All @@ -608,12 +611,12 @@ function (_React$Component) {
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
inFocus === false && formattedValue !== stateValue) {
this.setState({
value: formattedValue,
numAsString: numAsString
focusedElm === null && formattedValue !== stateValue) {
this.updateValue({
formattedValue: formattedValue,
numAsString: numAsString,
input: focusedElm
});
onValueChange(this.getValueObject(formattedValue, numAsString));
}
}
}
Expand Down Expand Up @@ -1169,6 +1172,54 @@ function (_React$Component) {

return value;
}
/** Update value and caret position */

}, {
key: "updateValue",
value: function updateValue(params) {
var _this2 = this;

var onUpdate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop;
var formattedValue = params.formattedValue,
input = params.input;
var numAsString = params.numAsString,
caretPos = params.caretPos;
var onValueChange = this.props.onValueChange;
var lastValue = this.state.value; //set caret position, and value imperatively when element is provided

if (input) {
//calculate caret position if not defined
if (!caretPos) {
var inputValue = params.inputValue || input.value;
var currentCaretPosition = getCurrentCaretPosition(input); //get the caret position

caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition);
} //set the value imperatively, this is required for IE fix


input.value = formattedValue; //set caret position

this.setPatchedCaretPosition(input, caretPos, formattedValue);
} //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
}, function () {
onValueChange(_this2.getValueObject(formattedValue, numAsString));
onUpdate();
});
} else {
onUpdate();
}
}
}, {
key: "onChange",
value: function onChange(e) {
Expand All @@ -1179,49 +1230,35 @@ function (_React$Component) {
props = this.props;
var isAllowed = props.isAllowed;
var lastValue = state.value || '';
/*Max of selectionStart and selectionEnd is taken for the patch of pixel and other mobile device caret bug*/

var currentCaretPosition = Math.max(el.selectionStart, el.selectionEnd);
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);

if (!isAllowed(valueObj)) {
formattedValue = lastValue;
} //set the value imperatively, this is required for IE fix


el.value = formattedValue; //get the caret position

var caretPos = this.getCaretPosition(inputValue, formattedValue, currentCaretPosition); //set caret position

this.setPatchedCaretPosition(el, caretPos, formattedValue); //change the state
}

if (formattedValue !== lastValue) {
this.setState({
value: formattedValue,
numAsString: numAsString
}, function () {
props.onValueChange(valueObj);
props.onChange(e);
});
} else {
this.updateValue({
formattedValue: formattedValue,
numAsString: numAsString,
inputValue: inputValue,
input: el
}, function () {
props.onChange(e);
}
});
}
}, {
key: "onBlur",
value: function onBlur(e) {
var _this2 = this;

var props = this.props,
state = this.state;
var format = props.format,
onBlur = props.onBlur;
var numAsString = state.numAsString;
var lastValue = state.value;
this.inFocus = false;
this.focusedElm = null;

if (!format) {
numAsString = fixLeadingZero(numAsString);
Expand All @@ -1230,13 +1267,10 @@ function (_React$Component) {
if (formattedValue !== lastValue) {
// the event needs to be persisted because its properties can be accessed in an asynchronous way
e.persist();
this.setState({
value: formattedValue,
this.updateValue({
formattedValue: formattedValue,
numAsString: numAsString
}, function () {
var valueObj = _this2.getValueObject(formattedValue, numAsString);

props.onValueChange(valueObj);
onBlur(e);
});
return;
Expand All @@ -1248,8 +1282,6 @@ function (_React$Component) {
}, {
key: "onKeyDown",
value: function onKeyDown(e) {
var _this3 = this;

var el = e.target;
var key = e.key;
var selectionStart = el.selectionStart,
Expand Down Expand Up @@ -1307,18 +1339,13 @@ function (_React$Component) {
we will not have any information of keyPress
*/
if (selectionStart <= leftBound + 1 && value[0] === '-' && typeof format === 'undefined') {
var newValue = value.substring(1);
var numAsString = this.removeFormatting(newValue);
var valueObj = this.getValueObject(newValue, numAsString); //persist event before performing async task
var newValue = value.substring(1); //persist event before performing async task

e.persist();
this.setState({
value: newValue,
numAsString: numAsString
}, function () {
_this3.setPatchedCaretPosition(el, newCaretPosition, newValue);

onValueChange(valueObj);
this.updateValue({
formattedValue: newValue,
caretPos: newCaretPosition,
input: el
});
} else if (!negativeRegex.test(value[expectedCaretPosition])) {
while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) {
Expand Down Expand Up @@ -1372,27 +1399,27 @@ function (_React$Component) {
}, {
key: "onFocus",
value: function onFocus(e) {
var _this4 = this;
var _this3 = 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.inFocus = true;
this.focusedElm = e.target;
setTimeout(function () {
var el = e.target;
var selectionStart = el.selectionStart,
selectionEnd = el.selectionEnd,
_el$value3 = el.value,
value = _el$value3 === void 0 ? '' : _el$value3;

var caretPosition = _this4.correctCaretPosition(value, selectionStart); //setPatchedCaretPosition only when everything is not selected on focus (while tabbing into the field)
var caretPosition = _this3.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)) {
_this4.setPatchedCaretPosition(el, caretPosition, value);
_this3.setPatchedCaretPosition(el, caretPosition, value);
}

_this4.props.onFocus(e);
_this3.props.onFocus(e);
}, 0);
}
}, {
Expand Down
Loading

0 comments on commit b424a45

Please sign in to comment.