ProseMirror / prosemirror

The ProseMirror WYSIWYM editor
http://prosemirror.net/
MIT License
7.53k stars 334 forks source link

Dragging to create selection is direction-dependently buggy with Decoration.widget in some circumstances #1445

Open Nantris opened 5 months ago

Nantris commented 5 months ago

We hide sections of the editor using decorations and it works great when dragging from top to bottom, but for bottom to top the behavior is different and the selection is interrupted.

Rather than being caused by the hidden content itself, this appears in some way to be related to Decoration.widget. If we remove the widgets but keep everything else the same, the selection can be expanded in either direction as expected. I'm not sure why they might interfere.

The issue does not affect Shift+Click to expand selections, only pure drag-based selections.

https://github.com/ProseMirror/prosemirror/assets/6835891/eeaea307-e316-4ed4-b03d-ab06b9bfc88d

The video is from Chrome on Linux. It behaves fundamentally the same in Firefox, although it does select some text on the upper line unlike Chrome.


I'm sure this is entirely insufficient to really reveal the root cause, but I found commenting this line out prevents the issue:

https://github.com/ProseMirror/prosemirror-view/blob/39fb7c2e71287d6ac2013f5a8c878873a074244e/src/selection.ts#L89

marijnh commented 5 months ago

ProseMirror relies on the native browser drag-selection behavior. Some browsers glitch out when the DOM is changed in certain ways during selection. Delaying your decoration until the mouse button is released may be a way to work around this.

Nantris commented 5 months ago

Thank you so much for your reply @marijnh.

When you say changing the DOM, do you mean the way we change the color of our widget?

Unfortunately even patching our decorations to disable updating them once they exist doesn't seem to make a difference:

apply: (tr, oldSet, oldState, state) => {
  const decorations = [];
  const { from, to, empty } = state.selection;

  // Prevent decoration updates
  if (decorations.length > 0) return decorations;

  // ... rest of decoration code
}

Is there any possibility this could be due to an internal bug in ProseMirror rather than browser-specific behaviors? It's just curious to me that it works in one direction but not the other and that it affects both Chrome and Firefox.

Here's a demo of Firefox, which has that slight difference but presents overall the same:

https://github.com/ProseMirror/prosemirror/assets/6835891/431f6838-7f08-4c3f-8a1a-b69c1be9809e

Nantris commented 4 months ago

I determined that setting the following styles prevents the bug (but also, expectedly, prevents view.posAtCoords from functioning.)

While this isn't definitive proof, I think it's a decent indicator this may not be a browser bug.

.ProseMirror { pointer-events: none; } 
.ProseMirror * { pointer-events: all; }