Skip to content

Commit

Permalink
Fix(InputItem): Adjust the caret position of the formatted value (#2854)
Browse files Browse the repository at this point in the history
* Fix(InputItem): Adjust the caret position of the formatted value (#2840 #1553 #1208 #1162 #1165 #810)

* refactor: remove onChange in the event loop, refactor adjustCaretPosition

* Fix(InputItem): caret position problem when using android phone keyboard
  • Loading branch information
zack24q authored and ziluo committed Dec 28, 2018
1 parent b42725f commit 6db1c19
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 25 deletions.
37 changes: 32 additions & 5 deletions components/input-item/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
// import React from 'react';
// import InputItem from '../index';
import React from 'react';
import { mount } from 'enzyme';
import InputItem from '../index';

describe('InputItem', () => {
// No need to render Snapshot again, because of `./demo.test.js`
it('trigger event correctly', () => {
// todos: write test!
expect(true).toBe(true);

it('format bankCard correctly', () => {
const bankCard = mount((
<InputItem
type="bankCard"
>银行卡</InputItem>
));
bankCard.find('input').simulate('change', { target: { value: '1a23 4-5.6 7w890' } });
expect(bankCard.state('value')).toBe('1234 5678 90');
});

it('format phone correctly', () => {
const phone = mount((
<InputItem
type="phone"
>手机号码</InputItem>
));
phone.find('input').simulate('change', { target: { value: '1a23 4-5.6 7w890a123123' } });
expect(phone.state('value')).toBe('123 4567 8901');
});

it('format number correctly', () => {
const number = mount((
<InputItem
type="number"
>数字</InputItem>
));
number.find('input').simulate('change', { target: { value: '1a23 4-5.6 7w890' } });
expect(number.state('value')).toBe('1234567890');
});
});
62 changes: 42 additions & 20 deletions components/input-item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,46 +91,46 @@ class InputItem extends React.Component<InputItemProps, any> {
}

onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
const el = e.target;
const { value: rawVal, selectionEnd: prePos } = el;
const { value: preCtrlVal } = this.state;
const { type } = this.props;

let newValue = value;
let ctrlValue = rawVal;
switch (type) {
case 'bankCard':
newValue = value.replace(/\D/g, '').replace(/(....)(?=.)/g, '$1 ');
ctrlValue = rawVal.replace(/\D/g, '').replace(/(....)(?=.)/g, '$1 ');
break;
case 'phone':
newValue = value.replace(/\D/g, '').substring(0, 11);
const valueLen = newValue.length;
ctrlValue = rawVal.replace(/\D/g, '').substring(0, 11);
const valueLen = ctrlValue.length;
if (valueLen > 3 && valueLen < 8) {
newValue = `${newValue.substr(0, 3)} ${newValue.substr(3)}`;
ctrlValue = `${ctrlValue.substr(0, 3)} ${ctrlValue.substr(3)}`;
} else if (valueLen >= 8) {
newValue = `${newValue.substr(0, 3)} ${newValue.substr(3, 4)} ${newValue.substr(
ctrlValue = `${ctrlValue.substr(0, 3)} ${ctrlValue.substr(3, 4)} ${ctrlValue.substr(
7,
)}`;
}
break;
case 'number':
newValue = value.replace(/\D/g, '');
ctrlValue = rawVal.replace(/\D/g, '');
break;
case 'text':
case 'password':
default:
break;
}
this.handleOnChange(newValue, newValue !== value);
}

handleOnChange = (value: string, isMutated: boolean = false) => {
const { onChange } = this.props;

if (!('value' in this.props)) {
this.setState({ value });
} else {
this.setState({ value: this.props.value });
}
if (onChange) {
isMutated ? setTimeout(() => onChange(value)) : onChange(value);
this.setState({ value: ctrlValue });
switch (type) {
case 'bankCard':
case 'phone':
case 'number':
// controlled input type needs to adjust the position of the caret
setTimeout(() => el.selectionStart = el.selectionEnd = this.calcPos(prePos || 0, preCtrlVal, rawVal, ctrlValue, [' '], /\D/g));
break;
default:
break;
}
}

Expand Down Expand Up @@ -187,6 +187,28 @@ class InputItem extends React.Component<InputItemProps, any> {
}
}

// calculate the position of the caret
calcPos = (prePos: number, preCtrlVal: string, rawVal: string, ctrlVal: string, placeholderChars: Array<string>, maskReg: RegExp) => {
const editLength = rawVal.length - preCtrlVal.length;
const isAddition = editLength > 0;
let pos = prePos;
if (isAddition) {
const additionStr = rawVal.substr(pos - editLength, editLength);
let ctrlCharCount = additionStr.replace(maskReg, '').length;
pos -= (editLength - ctrlCharCount);
let placeholderCharCount = 0;
while (ctrlCharCount > 0) {
if (placeholderChars.indexOf(ctrlVal.charAt(pos - ctrlCharCount + placeholderCharCount)) === -1) {
ctrlCharCount--;
} else {
placeholderCharCount++;
}
}
pos += placeholderCharCount;
}
return pos
}

render() {
const props = { ...this.props };
delete props.updatePlaceholder;
Expand Down

0 comments on commit 6db1c19

Please sign in to comment.