react-component / input-number

React Input Number
https://input-number.vercel.app/
MIT License
313 stars 173 forks source link

Cursor position at end of string when formatter appends character #651

Closed pierluigigiancola closed 2 months ago

pierluigigiancola commented 2 months ago

Hi,

I have an issue with formatting EUR currency where the value always displays at least two decimal places, adding ",00" if the input doesn't have any decimals.

The problem occurs when pressing the Tab key and gaining focus on the component. When the entire value is selected and a key is pressed (e.g., 5), it overwrites the value. The formatter is called, and the value becomes "5,00", with the cursor positioned at the end of the string.

You can reproduce this issue on your example page using the second example in the formatter section: https://input-number.vercel.app/example#formatter.

In your example the % is not a problem but with currency the cursor at the end it's a problem because the next keystroke goes to the decimal and get removed (e.g. 5,00 with the cursor at the end if you press 3 the result is 5,003 and after formatting become 5,00).

the bug is in useCursor hook restoreCursor function.

function restoreCursor() {
    if (input && selectionRef.current && focused) {
      try {
        const { value } = input;
        const { beforeTxt, afterTxt, start } = selectionRef.current;

        let startPos = value.length;

        if (value.endsWith(afterTxt)) {
          startPos = value.length - selectionRef.current.afterTxt.length;
        } else if (value.startsWith(beforeTxt)) {
          startPos = beforeTxt.length;
        } else {
          const beforeLastChar = beforeTxt[start - 1];
          const newIndex = value.indexOf(beforeLastChar, start - 1);
          if (newIndex !== -1) {
            startPos = newIndex + 1;
          }
        }

        input.setSelectionRange(startPos, startPos);
      } catch (e) {
        warning(
          false,
          `Something warning of cursor restore. Please fire issue about this: ${e.message}`,
        );
      }
    }
  }

value.endsWith(afterTxt) return true when checking empty string value = "5,00" and afterTxt = "" so startPos = 4.

by inverting value.endsWith(afterTxt) with value.startsWith(beforeTxt) the cursor if position as intended.