hodgef / react-simple-keyboard

React Virtual Keyboard - Customizable, responsive and lightweight
https://virtual-keyboard.js.org/react
MIT License
568 stars 61 forks source link

Backspace issue with initial value, manual mask logic, and rendering condition triggered by html input's onFocus #2776

Closed pablobrodt closed 5 months ago

pablobrodt commented 5 months ago

So, i know this is a very specific issue, you can say just by the title.

In preparation for this cenario we should have:

So lets describe when this issue happens: You have your html input on screen and the keyboard is not rendered yet, because we only want the keyboard to be shown when the input gets focus.

You have a initial value to fill your html input and the Keyboard internal input.

This value is not masked at all, we only apply a mask before we pass the value to the html input's value attribute.

So, when the html input triggers its onFocus callback, we set the boolean state that defines if the Keyboard is shown as true, then our Keyboard gets rendered and we set its input value with our intended initial value using the onInit prop and doing a keyboard.setInput(initialValue).

After that, we want to edit the value, and try to erase it with backspace, but nothing happens.

A few observations:

I couldn't find any relationship between the html input's onFocus and the Keyboard directly to make sense that the backspace dont work.

Here is a codesandbox with a repro example: https://codesandbox.io/p/sandbox/react-simple-keyboard-backspace-focus-issue-7fp289

I'm using react-simple-keyboard 3.7.80

hodgef commented 5 months ago

Hello @pablobrodt,

When you click on the input, its caret position is relayed to simple-keyboard. This allows simple-keyboard to delete characters at the right place, but in your case there's a mismatch between the string length in the input element and in the internal simple-keyboard state.

image

So as a result simple-keyboard tries to delete at a position that does not exist.

To fix this, you can use the option disableCaretPositioning.

https://codesandbox.io/p/sandbox/react-simple-keyboard-backspace-focus-issue-forked-vhz9jx?file=%2Fsrc%2Fcomponents%2Fmy-keyboard%2Fmy-keyboard.component.tsx%3A46%2C7

Regards, Francisco Hodge

pablobrodt commented 5 months ago

Hi @hodgef, the formatted string is only used for presentation reasons, when manipulating the input's value i'm using the raw value, so i couldn't understand why the keyboard is using the formatted value for the caret positioning.

Can you explain why?

hodgef commented 5 months ago

I linked to this part of the code in my previous comment: https://github.com/hodgef/simple-keyboard/blob/b45ea000279225ad2b82fddc2cc681b1443442ec/src/lib/components/Keyboard.ts#L1250

When you click on an input element, the caret/cursor position (element.selectionStart https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/selectionStart) of this input is communicated to simple keyboard. If your masked input has its cursor at 20 and the internal (non-masked) state of simple-keyboard has a length of 16, then for sure backspace won't work when it tries to remove a character at position 20 (since position 20 does not exist in the internal state).

This is the best I can explain it. Please feel free to check out the console in my modified sandbox to verify. Thanks!

pablobrodt commented 5 months ago

Yeah thats exactly what i wanted to understand, thank you very much.