Repository navigation
Description
Lexical version: 0.53.0, and the code is the same on main at 5326e06.
Steps To Reproduce
- Make an editor with
registerRichTextand an inlineDecoratorNodewhosecreateDOMreturns a span holding text, withdecorate()returning null. The node is in the code below. - Put a paragraph in it: text, the decorator, text.
- In Firefox, click in the text before the decorator.
- Hold Shift and click on the decorator's text.
- Type a letter.
Link to code example:
No public repo, so here's the node. The rest is createEditor({nodes: [ChipNode]}), setRootElement on a contenteditable div and registerRichText(editor).
class ChipNode extends DecoratorNode {
static getType() { return "chip"; }
static clone(node) { return new ChipNode(node.__text, node.__key); }
static importJSON(json) { return new ChipNode(json.text); }
constructor(text, key) { super(key); this.__text = text; }
exportJSON() { return { ...super.exportJSON(), text: this.__text }; }
createDOM() { const span = document.createElement("span"); span.textContent = this.__text; return span; }
updateDOM() { return false; }
isInline() { return true; }
getTextContent() { return this.__text; }
decorate() { return null; }
}The current behavior
| DOM selection after the Shift+click | Lexical's selection | The letter | |
|---|---|---|---|
| Firefox 157 | none | null | lost |
| Chromium 156 | "Before "@3 .. <span>@0 |
range, to the paragraph's offset 1 | replaces the selected text, Befk[x + 1] … |
| WebKit 27.2 | "Before "@2 .. "Before "@7 |
range, to the end of "Before " |
replaces the selected text, Bek[x + 1] … |
| Control, Firefox: Shift+click on the text after the decorator | "Before "@2 .. " after and more words."@6 |
range across the decorator | replaces it, Bek and more words. |
In Firefox the events run pointerdown, mousedown, selectionchange with the focus in the decorator's text, selectionchange with no selection, pointerup. So the user's selection is gone before the button is released.
From reading the code, not stepped through: a press inside a decorator leaves inputState.mouseDrag null, because isDOMCapturingSelection is true there (LexicalEvents.ts L2680-L2687, LexicalUtils.ts L3519-L3533). So $adjustMouseDrag, which would keep the anchor and put the focus beside the decorator, doesn't run (L365-L368), and $internalResolveSelectionPoints gives null for the focus (LexicalSelection.ts L3444-L3475).
The expected behavior
The anchor stays, and the focus goes beside the decorator, as in Chromium and WebKit and as $moveMouseDragFocus does for a press beside one.
Impact of fix
It affects any editor with an inline decorator, such as a mention or an inline equation, in Firefox. A Shift+click meant to grow a selection over one loses the selection instead, and the next typed character goes nowhere.
Activity
[lexical] Refactor: Scope decorator mouseDrag tracking to Shift-click…
[lexical] Refactor: Support plaintext-only in isEmbeddedInput and ref…