lexicaljs

Keyboard navigation across text and decorator nodes


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.


Solution

  • 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