Skip to content

Commit 9afc266

Browse files
committed
Fix #673
1 parent f1d6696 commit 9afc266

File tree

4 files changed

+73
-29
lines changed

4 files changed

+73
-29
lines changed

src/number_format_base.tsx

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import {
1212
geInputCaretPosition,
1313
setCaretPosition,
1414
getCaretPosition,
15-
clamp,
1615
charIsNumber,
1716
useInternalValues,
1817
noop,
1918
caretUnknownFormatBoundary,
19+
getCaretPosInBoundary,
2020
} from './utils';
2121

2222
function defaultRemoveFormatting(value: string) {
@@ -136,29 +136,7 @@ export default function NumberFormatBase<BaseType = InputAttributes>(
136136

137137
/* This keeps the caret within typing area so people can't type in between prefix or suffix */
138138
const correctCaretPosition = (value: string, caretPos: number, direction?: string) => {
139-
const valLn = value.length;
140-
141-
// clamp caret position to [0, value.length]
142-
caretPos = clamp(caretPos, 0, valLn);
143-
144-
const boundary = getCaretBoundary(value);
145-
146-
if (direction === 'left') {
147-
while (caretPos >= 0 && !boundary[caretPos]) caretPos--;
148-
149-
// if we don't find any suitable caret position on left, set it on first allowed position
150-
if (caretPos === -1) caretPos = boundary.indexOf(true);
151-
} else {
152-
while (caretPos <= valLn && !boundary[caretPos]) caretPos++;
153-
154-
// if we don't find any suitable caret position on right, set it on last allowed position
155-
if (caretPos > valLn) caretPos = boundary.lastIndexOf(true);
156-
}
157-
158-
// if we still don't find caret position, set it at the end of value
159-
if (caretPos === -1) caretPos = valLn;
160-
161-
return caretPos;
139+
return getCaretPosInBoundary(value, caretPos, getCaretBoundary(value), direction);
162140
};
163141

164142
const getNewCaretPosition = (inputValue: string, formattedValue: string, caretPos: number) => {

src/pattern_format.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import React from 'react';
22
import { PatternFormatProps, InputAttributes, ChangeMeta, InternalNumberFormatBase } from './types';
3-
import { getDefaultChangeMeta, getMaskAtIndex, noop, setCaretPosition } from './utils';
3+
import {
4+
getCaretPosInBoundary,
5+
getDefaultChangeMeta,
6+
getMaskAtIndex,
7+
noop,
8+
setCaretPosition,
9+
} from './utils';
410
import NumberFormatBase from './number_format_base';
511

612
export function format<BaseType = InputAttributes>(
@@ -147,10 +153,14 @@ export function usePatternFormat<BaseType = InputAttributes>(props: PatternForma
147153
// validate props
148154
validateProps(props);
149155

156+
const _getCaretBoundary = (formattedValue: string) => {
157+
return getCaretBoundary(formattedValue, props);
158+
};
159+
150160
const _onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
151161
const { key } = e;
152162
const el = e.target as HTMLInputElement;
153-
const { selectionStart, selectionEnd } = el;
163+
const { selectionStart, selectionEnd, value } = el;
154164

155165
// if multiple characters are selected and user hits backspace, no need to handle anything manually
156166
if (selectionStart !== selectionEnd || !selectionStart) {
@@ -164,18 +174,22 @@ export function usePatternFormat<BaseType = InputAttributes>(props: PatternForma
164174
// bring the cursor to closest numeric section
165175
let index = selectionStart;
166176

177+
let direction: string;
178+
167179
if (key === 'Backspace') {
168180
while (index > 0 && formatProp[index - 1] !== patternChar) {
169181
index--;
170182
}
183+
direction = 'left';
171184
} else {
172185
const formatLn = formatProp.length;
173186
while (index < formatLn && formatProp[index] !== patternChar) {
174187
index++;
175188
}
189+
direction = 'right';
176190
}
177-
178191
if (index !== selectionStart) {
192+
index = getCaretPosInBoundary(value, index, _getCaretBoundary(value), direction);
179193
setCaretPosition(el, index);
180194
}
181195
}
@@ -188,7 +202,7 @@ export function usePatternFormat<BaseType = InputAttributes>(props: PatternForma
188202
format: (numStr: string) => format(numStr, props),
189203
removeFormatting: (inputValue: string, changeMeta: ChangeMeta) =>
190204
removeFormatting(inputValue, changeMeta, props),
191-
getCaretBoundary: (formattedValue: string) => getCaretBoundary(formattedValue, props),
205+
getCaretBoundary: _getCaretBoundary,
192206
onKeyDown: _onKeyDown,
193207
};
194208
}

src/utils.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export function getCaretPosition(formattedValue: string, curValue: string, curCa
336336
const endIndex = pos === curValLn || indexMap[pos] === -1 ? formattedValueLn : indexMap[pos];
337337

338338
pos = curCaretPos - 1;
339-
while (pos > 0 && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) pos--;
339+
while (pos > 0 && indexMap[pos] === -1) pos--;
340340
const startIndex = pos === -1 || indexMap[pos] === -1 ? 0 : indexMap[pos] + 1;
341341

342342
/**
@@ -352,6 +352,36 @@ export function getCaretPosition(formattedValue: string, curValue: string, curCa
352352
return curCaretPos - startIndex < endIndex - curCaretPos ? startIndex : endIndex;
353353
}
354354

355+
/* This keeps the caret within typing area so people can't type in between prefix or suffix or format characters */
356+
export function getCaretPosInBoundary(
357+
value: string,
358+
caretPos: number,
359+
boundary: boolean[],
360+
direction?: string,
361+
) {
362+
const valLn = value.length;
363+
364+
// clamp caret position to [0, value.length]
365+
caretPos = clamp(caretPos, 0, valLn);
366+
367+
if (direction === 'left') {
368+
while (caretPos >= 0 && !boundary[caretPos]) caretPos--;
369+
370+
// if we don't find any suitable caret position on left, set it on first allowed position
371+
if (caretPos === -1) caretPos = boundary.indexOf(true);
372+
} else {
373+
while (caretPos <= valLn && !boundary[caretPos]) caretPos++;
374+
375+
// if we don't find any suitable caret position on right, set it on last allowed position
376+
if (caretPos > valLn) caretPos = boundary.lastIndexOf(true);
377+
}
378+
379+
// if we still don't find caret position, set it at the end of value
380+
if (caretPos === -1) caretPos = valLn;
381+
382+
return caretPos;
383+
}
384+
355385
export function caretUnknownFormatBoundary(formattedValue: string) {
356386
const boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(() => true);
357387

test/library/keypress_and_caret.spec.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ describe('Test keypress and caret position changes', () => {
155155
expect(getInputValue(wrapper)).toEqual('123 999 845');
156156
expect(caretPos).toEqual(7);
157157
});
158+
159+
it('after typing decimal cursor position should go after the . when suffix is provided. #673', () => {
160+
let caretPos;
161+
const setSelectionRange = (pos) => {
162+
caretPos = pos;
163+
};
164+
165+
const wrapper = mount(
166+
<NumericFormat
167+
type="text"
168+
allowNegative={false}
169+
valueIsNumericString={true}
170+
decimalScale={8}
171+
placeholder="Enter Amount"
172+
defaultValue="123"
173+
suffix=" USD"
174+
/>,
175+
);
176+
simulateKeyInput(wrapper.find('input'), '.', 3, 3, setSelectionRange);
177+
178+
expect(caretPos).toEqual(4);
179+
});
158180
});
159181

160182
describe('Test delete/backspace with format pattern', async () => {

0 commit comments

Comments
 (0)