It is a wrapper around html5 input with a custom validation. Having encountered an uncountable number of limitations of input type number, I have decided to switch to type text. Unfortuntaly this app must run in mobile environment and customers require that when a user focuses on the input a digital keyboard shows up. I tried to implement dynamic type switching with onFocus and onBlur events which works just fine on iOS devices. This code however breaks on Android devices: it seems that type does not change and the regex is not applied to the input.
I am creating a react input component
class InputNumber extends Component {
constructor(props) {
super(props);
this.re = props.regex ? props.regex : /^(?:[1-9]\d*(?:\.\d{0,2})?)?$/;
}
componentWillReceiveProps(nextProps) {
// check new props and reassign the stake if needed
if (this.input && this.currentVal !== nextProps.newValue) {
if (nextProps.newValue === "0") {
this.input.value = "";
} else {
this.input.value = nextProps.newValue;
}
}
}
makeTypeText = e => {
e.target.type = "text";
};
makeTypeNumber = e => {
e.target.type = "number";
};
setValue = e => {
const re = this.re; // pattern that we will validate our input against
const currentVal = e.target.value.split(""); // in order to validate we need to know what would be the next value
const newChar = e.key; // we transform the current value into an array and
let nextVal = [...currentVal]; // create a new array with previous values
const start = e.target.selectionStart; // and then place the input char into the current cursor position
const end = e.target.selectionEnd;
if (e.key === "Backspace") {
// if backspace is typed
if (start === end) {
nextVal.splice(start - 1, 1); // if there is no selection just delete the digit before the cursor
} else {
nextVal.splice(start, end - start); // if the user has selected several digits just replace them with empty string
}
} else if (e.key === "Delete") {
// if delete is typed
if (start === end) {
nextVal.splice(start, 1); // delete a digit before after the cursor if no selection
} else {
nextVal.splice(start, end - start); // delete selection
}
} else {
nextVal.splice(start, end - start, e.key); // insert digit before the cursor or replace selection with a digit
}
const valueAfterInput = nextVal.join("");
// check the value that would result from input against the regular expression
if (!re.test(valueAfterInput)) {
e.preventDefault(); // if the next value is invalid we prevent the user action
} else {
this.currentVal =
valueAfterInput.slice(-1) === "."
? valueAfterInput.substr(0, valueAfterInput.length - 1)
: valueAfterInput; // assign the current value to a class variable so we can check it against incoming props
this.props.action(valueAfterInput); // dispatch an action
}
};
render() {
return (
<div className="form-field__input">
<input
type="number"
onKeyDown={this.setValue}
onFocus={this.makeTypeText}
onBlur={this.makeTypeNumber}
ref={input => (this.input = input)}
className={cn("form-input", "form-input_focus", {
"form-input_orange": this.props.hideCurrency
})}
placeholder={t("Stake...")}
/>
<span
className={cn(
"form-field__action",
"form-field__action_showed",
"form-field__action_text",
{hidden: this.props.hideCurrency}
)}
>
₽
</span>
</div>
);
}
}
InputNumber.propTypes = {
newValue: PropTypes.string,
hideCurrency: PropTypes.bool.isRequired,
action: PropTypes.func.isRequired,
regex: PropTypes.object
};
export default InputNumber;
Any thoughts on how to remedy this situation?
P.S.: stop marking as duplicate before understanding the issue.