Table機能をLexical Extensionとして再パッケージング
LexxyのTable機能が、Lexical Extension APIを使用した形式にリファクタリングされました。#668では、テーブル関連のコードをTablesLexicalExtensionとして分離し、エディタのコア実装をよりモジュール化しています。
背景
これまでLexxyのTable機能は、エディタのコアファイル(src/elements/editor.jsやsrc/editor/command_dispatcher.js)に直接実装されていました。この構造では、Table機能を無効化したい場合や、他の機能との依存関係を管理する際に柔軟性が欠けていました。
技術的な変更
Extension化による分離
新しく作成されたTablesLexicalExtensionは、Lexical Extension APIのdefineExtensionを使用して定義されています:
export const TablesLexicalExtension = defineExtension({
name: "lexxy/tables",
nodes: [
WrappedTableNode,
{
replace: TableNode,
with: () => new WrappedTableNode()
},
TableCellNode,
TableRowNode
],
register(editor) {
registerTablePlugin(editor)
registerTableSelectionObserver(editor, true)
setScrollableTablesActive(editor, true)
// ...
}
})
この設計により、以下の要素が一箇所に集約されました:
- テーブル関連のNode定義(
TableNode、TableCellNodeなど) - Lexicalのテーブルプラグインの登録処理
- テーブル固有のバグ修正トランスフォーム
エディタコアからの削除
src/elements/editor.jsからは、テーブル関連のNode登録とプラグイン処理が削除され、代わりにExtensionの登録のみが行われるようになりました:
const richTextExtensions = [
this.highlighter.lexicalExtension,
TrixContentExtension,
TablesLexicalExtension // 追加
]
これにより、エディタの初期化コードが23行削減され、コアロジックがシンプルになっています。
コマンドディスパッチャーの整理
CommandDispatcherからは、テーブル操作関連のコマンド実装が完全に削除されました:
削除されたコマンド:
- insertTable
- insertTableRowBefore / insertTableRowAfter
- insertTableColumnBefore / insertTableColumnAfter
- deleteTableRow / deleteTableColumn / deleteTable
これらのコマンド自体は引き続き動作しますが、その実装はLexical本体の@lexical/tableパッケージに委譲される形になりました。
バグ修正の移管
以前はsrc/helpers/table_helper.jsで管理されていた2つのバグ修正用トランスフォームが、Extensionのregisterメソッド内に移動されています:
// Bug fix: Prevent hardcoded background color (Lexical #8089)
editor.registerNodeTransform(TableCellNode, (node) => {
if (node.getBackgroundColor() === null) {
node.setBackgroundColor("")
}
})
// Bug fix: Fix column header states (Lexical #8090)
editor.registerNodeTransform(TableCellNode, (node) => {
const headerState = node.getHeaderStyles()
if (headerState !== TableCellHeaderStates.ROW) return
// ...
})
これにより、バグ修正の適用範囲が明確になり、Table機能を無効化した場合には不要なトランスフォームも自動的に除外されます。
設計判断
このリファクタリングの最大の利点は、機能のオプショナル化の容易さです。Extension化により、将来的に以下のような柔軟な構成が可能になります:
// Table機能なしのエディタ構成も簡単に実現可能
const richTextExtensions = [
this.highlighter.lexicalExtension,
TrixContentExtension
// TablesLexicalExtension を含めなければTable機能は無効化される
]
また、WrappedTableNodeの実装が独立したファイル(src/nodes/wrapped_table_node.js)として残されている点も注目に値します。これはLexxyのカスタムNode実装であり、標準のTableNodeを<figure class="lexxy-content__table-wrapper">でラップする独自の振る舞いを提供しています。Extension内ではreplaceオプションを使って標準Nodeを置き換える形で統合されています。