Closed10

lexicalでenterで改行するときの挙動を調整する

tawachantawachan

lexicalで開発を試している

https://lexical.dev/

https://zenn.dev/tawachan/scraps/e37cb119f536a2

そこで改行のときのデフォルト挙動が気になった

どこの文化かわからないけど、自分にとっては文字がないリストで改行をしたらリストでなくなるという挙動がしっくりくるのだが、lexicalのデフォルトの挙動ではそうならなかった

tawachantawachan

Enterを押したときの挙動を制御するプラグインを作る方向で考える

tawachantawachan

registerCommandでキーボードのイベントをハンドルできるっぽい

https://lexical.dev/docs/concepts/commands#editorregistercommand


  useEffect(() => {
    editor.registerCommand(
      KEY_ENTER_COMMAND,
      (payload) => {
        if (!payload) return true;
        const event: KeyboardEvent = payload;
        event.preventDefault();
        // 何かをする
      },
      COMMAND_PRIORITY_EDITOR
    );
  }, [editor]);

event.preventDefault()ができるのありがたい

確かに、改行も何も動かなくなる

tawachantawachan

対象のノードを取得

lexical全然情報がないけど、各所の限られた情報を見ながらAPIの使い方を邪推する

const selection = $getSelection();
if (!selection) return false;
if (!$isRangeSelection(selection)) return false;

const anchorNode = selection.anchor.getNode();
tawachantawachan

挙動をすべて自分でハンドルしようかと思ったけど、気になるところ以外はevent.preventDefault()しないでおまかせする方針にした

とりあえずリストにしか興味がないので、それ以外は早期リターン

if (!$isListItemNode(anchorNode)) return false;
tawachantawachan

選択中のリストアイテムに文字がないときにはリストではなくただのパラグラフを追加する仕様にした

if (anchorNode.getTextContentSize() === 0) {
  event.preventDefault();
  editor.update(() => {
    selection.insertNodes([$createParagraphNode()]);
  });
}

APIの使い方が適切なのかはわからないけどとりあえず意図通りに動くので良し

tawachantawachan

やっぱり、リストの途中ではリストを終わらせてほしくないのでリストの中で最後のアイテムであるという条件も足す

if (anchorNode.getTextContentSize() === 0 && anchorNode.isLastChild()) {
  event.preventDefault();
  editor.update(() => {
    selection.insertNodes([$createParagraphNode()]);
  });
}
tawachantawachan

リファクタリングするなどして最終的にプラグインはこんな感じになった

import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import {
  $getSelection,
  $isRangeSelection,
  COMMAND_PRIORITY_EDITOR,
  KEY_ENTER_COMMAND,
  $createParagraphNode,
  RangeSelection,
} from "lexical";
import { FC, useEffect } from "react";
import { $isListItemNode } from "@lexical/list";

// 条件をチェックする関数を定義
const shouldPreventDefaultEnter = (selection: RangeSelection) => {
  const anchorNode = selection.anchor.getNode();
  if (!$isListItemNode(anchorNode)) return false;
  // リストアイテムノードで、テキストが0以上で最後の子ではない場合にtrueを返す
  return anchorNode.getTextContentSize() === 0 && anchorNode.isLastChild();
};

export const EnterPlugin: FC = () => {
  const [editor] = useLexicalComposerContext();

  useEffect(() => {
    editor.registerCommand(
      KEY_ENTER_COMMAND,
      (payload) => {
        if (!payload) return true;
        const event: KeyboardEvent = payload;

        const selection = $getSelection();
        if (!selection) return false;
        if (!$isRangeSelection(selection)) return false;

        if (shouldPreventDefaultEnter(selection)) {
          event.preventDefault();
          editor.update(() => {
            selection.insertNodes([$createParagraphNode()]);
          });
        }
        return true;
      },
      COMMAND_PRIORITY_EDITOR
    );
  }, [editor]);

  return null;
};
tawachantawachan

lexical何もわからないという気持ちだったけど、少しは自分の思ったとおりにいじれたので少しずつカスタマイズの仕方がつかめてきたかもしれない

このスクラップは2ヶ月前にクローズされました