From 9afc2662a52ac40a355d2b5d503fd417e4d94e8a Mon Sep 17 00:00:00 2001 From: Sudhanshu Date: Sun, 25 Sep 2022 16:32:08 +0530 Subject: [PATCH] Fix #673 --- src/number_format_base.tsx | 26 ++------------------ src/pattern_format.tsx | 22 +++++++++++++---- src/utils.tsx | 32 ++++++++++++++++++++++++- test/library/keypress_and_caret.spec.js | 22 +++++++++++++++++ 4 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/number_format_base.tsx b/src/number_format_base.tsx index 51701510..64dc0820 100644 --- a/src/number_format_base.tsx +++ b/src/number_format_base.tsx @@ -12,11 +12,11 @@ import { geInputCaretPosition, setCaretPosition, getCaretPosition, - clamp, charIsNumber, useInternalValues, noop, caretUnknownFormatBoundary, + getCaretPosInBoundary, } from './utils'; function defaultRemoveFormatting(value: string) { @@ -136,29 +136,7 @@ export default function NumberFormatBase( /* This keeps the caret within typing area so people can't type in between prefix or suffix */ const correctCaretPosition = (value: string, caretPos: number, direction?: string) => { - const valLn = value.length; - - // clamp caret position to [0, value.length] - caretPos = clamp(caretPos, 0, valLn); - - const boundary = getCaretBoundary(value); - - if (direction === 'left') { - while (caretPos >= 0 && !boundary[caretPos]) caretPos--; - - // if we don't find any suitable caret position on left, set it on first allowed position - if (caretPos === -1) caretPos = boundary.indexOf(true); - } else { - while (caretPos <= valLn && !boundary[caretPos]) caretPos++; - - // if we don't find any suitable caret position on right, set it on last allowed position - if (caretPos > valLn) caretPos = boundary.lastIndexOf(true); - } - - // if we still don't find caret position, set it at the end of value - if (caretPos === -1) caretPos = valLn; - - return caretPos; + return getCaretPosInBoundary(value, caretPos, getCaretBoundary(value), direction); }; const getNewCaretPosition = (inputValue: string, formattedValue: string, caretPos: number) => { diff --git a/src/pattern_format.tsx b/src/pattern_format.tsx index b89cdb76..b5bdbd93 100644 --- a/src/pattern_format.tsx +++ b/src/pattern_format.tsx @@ -1,6 +1,12 @@ import React from 'react'; import { PatternFormatProps, InputAttributes, ChangeMeta, InternalNumberFormatBase } from './types'; -import { getDefaultChangeMeta, getMaskAtIndex, noop, setCaretPosition } from './utils'; +import { + getCaretPosInBoundary, + getDefaultChangeMeta, + getMaskAtIndex, + noop, + setCaretPosition, +} from './utils'; import NumberFormatBase from './number_format_base'; export function format( @@ -147,10 +153,14 @@ export function usePatternFormat(props: PatternForma // validate props validateProps(props); + const _getCaretBoundary = (formattedValue: string) => { + return getCaretBoundary(formattedValue, props); + }; + const _onKeyDown = (e: React.KeyboardEvent) => { const { key } = e; const el = e.target as HTMLInputElement; - const { selectionStart, selectionEnd } = el; + const { selectionStart, selectionEnd, value } = el; // if multiple characters are selected and user hits backspace, no need to handle anything manually if (selectionStart !== selectionEnd || !selectionStart) { @@ -164,18 +174,22 @@ export function usePatternFormat(props: PatternForma // bring the cursor to closest numeric section let index = selectionStart; + let direction: string; + if (key === 'Backspace') { while (index > 0 && formatProp[index - 1] !== patternChar) { index--; } + direction = 'left'; } else { const formatLn = formatProp.length; while (index < formatLn && formatProp[index] !== patternChar) { index++; } + direction = 'right'; } - if (index !== selectionStart) { + index = getCaretPosInBoundary(value, index, _getCaretBoundary(value), direction); setCaretPosition(el, index); } } @@ -188,7 +202,7 @@ export function usePatternFormat(props: PatternForma format: (numStr: string) => format(numStr, props), removeFormatting: (inputValue: string, changeMeta: ChangeMeta) => removeFormatting(inputValue, changeMeta, props), - getCaretBoundary: (formattedValue: string) => getCaretBoundary(formattedValue, props), + getCaretBoundary: _getCaretBoundary, onKeyDown: _onKeyDown, }; } diff --git a/src/utils.tsx b/src/utils.tsx index 44da9765..167af90b 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -336,7 +336,7 @@ export function getCaretPosition(formattedValue: string, curValue: string, curCa const endIndex = pos === curValLn || indexMap[pos] === -1 ? formattedValueLn : indexMap[pos]; pos = curCaretPos - 1; - while (pos > 0 && (indexMap[pos] === -1 || !charIsNumber(curValue[pos]))) pos--; + while (pos > 0 && indexMap[pos] === -1) pos--; const startIndex = pos === -1 || indexMap[pos] === -1 ? 0 : indexMap[pos] + 1; /** @@ -352,6 +352,36 @@ export function getCaretPosition(formattedValue: string, curValue: string, curCa return curCaretPos - startIndex < endIndex - curCaretPos ? startIndex : endIndex; } +/* This keeps the caret within typing area so people can't type in between prefix or suffix or format characters */ +export function getCaretPosInBoundary( + value: string, + caretPos: number, + boundary: boolean[], + direction?: string, +) { + const valLn = value.length; + + // clamp caret position to [0, value.length] + caretPos = clamp(caretPos, 0, valLn); + + if (direction === 'left') { + while (caretPos >= 0 && !boundary[caretPos]) caretPos--; + + // if we don't find any suitable caret position on left, set it on first allowed position + if (caretPos === -1) caretPos = boundary.indexOf(true); + } else { + while (caretPos <= valLn && !boundary[caretPos]) caretPos++; + + // if we don't find any suitable caret position on right, set it on last allowed position + if (caretPos > valLn) caretPos = boundary.lastIndexOf(true); + } + + // if we still don't find caret position, set it at the end of value + if (caretPos === -1) caretPos = valLn; + + return caretPos; +} + export function caretUnknownFormatBoundary(formattedValue: string) { const boundaryAry = Array.from({ length: formattedValue.length + 1 }).map(() => true); diff --git a/test/library/keypress_and_caret.spec.js b/test/library/keypress_and_caret.spec.js index de63030c..1aa4f6cf 100644 --- a/test/library/keypress_and_caret.spec.js +++ b/test/library/keypress_and_caret.spec.js @@ -155,6 +155,28 @@ describe('Test keypress and caret position changes', () => { expect(getInputValue(wrapper)).toEqual('123 999 845'); expect(caretPos).toEqual(7); }); + + it('after typing decimal cursor position should go after the . when suffix is provided. #673', () => { + let caretPos; + const setSelectionRange = (pos) => { + caretPos = pos; + }; + + const wrapper = mount( + , + ); + simulateKeyInput(wrapper.find('input'), '.', 3, 3, setSelectionRange); + + expect(caretPos).toEqual(4); + }); }); describe('Test delete/backspace with format pattern', async () => {