3

I'm building a template editor with lexical that uses custom decorator nodes to represent template fields (placeholders) following this example.

sample image

When changing the selection using the arrow keys, the selection gets stuck at the decorators. For example, when the cursor is just before a decorator (as in the image above) and I press Arrow Right, the RangeSelection changes into a NodeSelection for the decorator node. From that point, pressing arrow keys does not change the selection anymore.

Is is possible to configure decorator nodes so that they are skipped, i.e. the selection changes from the location before to the location after the decorator?

I'm using lexical@0.5.0.

ralfstx
  • 3,893
  • 2
  • 25
  • 41
  • 1
    I've got the same issue and haven't found a solution yet. But I think it's worth mentioning my another observation: the "Backspace" behaviour. When you press a "Backspace" after decorator node it removes it on Desktop/iOS but not on Android. On Android it pretty much closes the keyboard. I guess it could be somehow connected to the issue with arrows. – oleksandr.kazimirchuk Oct 16 '22 at 17:21

2 Answers2

2

here is the fix I used:

inside of your custom decorator node, use the isIsolated method and return true.

it's my understanding that when you select nodes, modify is invoked.

here is the block of code that you will be interested in:

    if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
      // Make it possible to move selection from range selection to
      // node selection on the node.
      if (collapse) {
        const nodeSelection = $createNodeSelection();
        nodeSelection.add(possibleNode.__key);
        $setSelection(nodeSelection);
        return;
      }

      const sibling = isBackward ? possibleNode.getPreviousSibling() : possibleNode.getNextSibling();

      if (!$isTextNode(sibling)) {
        const parent = possibleNode.getParentOrThrow();
        let offset;
        let elementKey;

        if ($isElementNode(sibling)) {
          elementKey = sibling.__key;
          offset = isBackward ? sibling.getChildrenSize() : 0;
        } else {
          offset = possibleNode.getIndexWithinParent();
          elementKey = parent.__key;

          if (!isBackward) {
            offset++;
          }
        }

        focus.set(elementKey, offset, 'element');

        if (collapse) {
          anchor.set(elementKey, offset, 'element');
        }

        return;
      } else {
        const siblingKey = sibling.__key;
        const offset = isBackward ? sibling.getTextContent().length : 0;
        focus.set(siblingKey, offset, 'text');

        if (collapse) {
          anchor.set(siblingKey, offset, 'text');
        }

        return;
      }
    }

by setting your decorator as isolated, you'd be able to select that node's siblings

LawN
  • 36
  • 1
0

I had a very similiar issue for anyone coming across this question. The only difference is, that I wanted the node to be selectable and not jump over the node. The issue of my selecton getting 'stuck' was still the same.

The funny part was that when using my exact same custom node in the playground example, I was able to get the correct keyboard behaviour. isIsolated was not giving me the behaviour I was hoping for either because it would just skip over my node.

After doing some digging, I found out that the issue was the PlainTextPlugin. I switched to the RichTextPlugin and got the correct keyboard navigation for decorator nodes.

The critical difference is in the KEY_ARROW_ handler. The RichTextPlugin handles node selections:

          if ($isNodeSelection(selection)) {
            // If selection is on a node, let's try and move selection
            // back to being a range selection.
            const nodes = selection.getNodes();
            if (nodes.length > 0) {
              event.preventDefault();
              nodes[0].selectPrevious();
              return true;
            }
          }
Yidaotus
  • 105
  • 1
  • 10