How to connect arrow key movement to/from inputfield inside decoration

Hey guys, I wonder if anyone else here has done this: In our realtime collaborative editor, we want almost all the text input to be shared immediately with collaborators. But we have a few exceptions, among which are keywords. We would like new keywords to appear as atomic commits - so only either the entire keyword is added to the document and shared with collaborators or it is not added at all.

To achieve this, I created a keywordInput plugin with a widget decoration which contains an input field. The widget is set to always stay at the end of the keywords block node and whenever enter, “;” or “,” is pressed, a transaction is made to commit a new keyword to the document.

This all works quite well. However - making arrow caret movement between the input field and the prosemirror instance work seemlessly seems rather complex – of course I can intercept transactions, see if they set the selection and whether the movement was crossing the input field and if this is the case select the input field instead, but I cannot relaly see if the crossing of the input field is due to the pressing of an arrow key or whether the user just coincidentally clicked right on the other side. Also, combined with checking for ArrowLeft and ArrowRight from the input field, it ends up being quite a bit of code.

So here I was wondering: Has anyone else tried this? And have you found a good way of doing this sort of operation?

Edit: I found that adding a handleKeyDown prop instead of trying to filter transactions for key movement is more helpful, as it allows me to only address cases in which there has been an actual key press event. What I still need to find out is a way to determine where the selection will be if the keypress takes place. In our above case, if the caret is at the beginning of the next element (body), it should be 3 higher than the position number of the input decoration. But in other cases it’s less clear. Is there a way to find out the following in the same event/ trigger:

  • where the caret will be after the selection change
    • what kind of event lead is the cause of the seleciton change

?

It is totally Ok for the selection change to actually take place, so it would be good to get this information either before or after it has taken place.

You could try to make the keyword inputs contentEditable elements, which will give you (some degree of) selection motion support for free, and using the stopEvent option to avoid events inside the element from propagating to the editor.

Thanks! I used input instead of contenteditable to avoid richtext (through paste or keyboard shortcuts). But now I see that Firefox has a major issue with input inside of a contenteditable element [1]. For contenteditable we have “plaintext-only”, but AFAIK, it’s not yet available on all browsers. Hmm…

[1] https://github.com/yabwe/medium-editor/issues/1197

That seems like another manifestation of #678 that medium-editor is handling poorly, not a fundamental issue with contentEditable + input elements.

I tried making a minimal example just with a contenteditable element and inside of it a noneditable island with a <input type="text"> in it and the arrow keys wouldn’t work in Firefox. Same for textarea. cE with “plaintext-only” doesn’t work on Firefox at all.

So I think I have settled for manual handling of arrow keys just on gecko for now as well as handling of movement into and out of the input-element as the simplest alternatives. But what a pain! It makes me remember why I decided to stop maintaining our own editing engine and switch to ProseMirror.

Another option would be to use another ProseMirror instance. At first it may seem like a bit overkill to invoke an entirely new Prosemirror instance for a simple text only input field. The advantage would be that I wouldn’t have to worry about whether it runs on all browsers, whether carets can move also on Safari, etc. .

I have now tried to put a second ProseMirror instance inside a widget decoration. Unfortunately that gives me an error from the main prosemirror instance when the caret moves into the ProseMirror instance inside the widget decoration. “stopEvent” doesn’t seem to make any difference.

Cannot read property 'select' of undefined

Should this be working?

Do you have a stack trace for that error? It sounds like it might be coming from something you’re doing with menus, not from the core library, which doesn’t use any properties named select.

Sorry, I was about to change the above text. It is working now, but I had to hook up all four caret movements (in/out at start/end), enable stopEvent for most events, and Firefox doesn’t show the caret when the field is empty. Anyway, this is better than all the previous attempts, so I guess we’ll stick with this for now.