テーブルセルの背景色を正規化し、貼り付け・ロード時の外部カラーを除去
Lexxy のテーブルで、外部コンテンツ(Excel など)から持ち込まれたセルの背景色や文字色が、貼り付け後だけでなく保存・再編集時にも残っていた問題を、セル変換で背景色を常に空文字列にリセットすることで統一的に解決しました。
背景
Lexical はセルの background-color を TableCellNode の state として保持し、インポート時にインラインスタイルから取得します。貼り付け時に実行される PastedContentFormatter はセル色を除去しますが、setValue でロードされる保存済みコンテンツには適用されず、結果として外部色がエディタ → 保存 → 再表示 → 再編集のサイクルを通過します。さらに、既存の TableCellNode 変換は null のときだけ背景をクリア する回避策(Lexical のハードコードされたヘッダー背景色問題 #8089)であり、実際には外部色が残る抜け穴となっていました。
技術的な変更
src/extensions/tables_extension.js において、すべての TableCellNode を対象に背景色を正規化するノード変換が追加されました。コメントも更新され、セルの背景は「外部コンテンツからのみ来る」前提で 常に空文字列にリセット する旨が明記されています。
@@ -55,9 +55,13 @@ export class TablesExtension extends LexxyExtension {
}
}),
- // Bug fix: Prevent hardcoded background color (Lexical #8089)
+ // A table in Lexxy is a Lexxy table: cell shading can't be set in the
+ // editor, so any cell background only ever comes from foreign content
+ // (pasted spreadsheets, loaded documents). Normalize every cell to no
+ // background so it adopts the current theme. This also clears
+ // Lexical's hardcoded default header background (Lexical #8089).
editor.registerNodeTransform(TableCellNode, (node) => {
- if (node.getBackgroundColor() === null) {
+ if (node.getBackgroundColor() !== "") {
node.setBackgroundColor("")
}
}),
変更前は node.getBackgroundColor() === null のときだけ setBackgroundColor("") が実行されていましたが、!== "" という条件に改めることで、背景色が空文字列であっても(実際には外部スタイルが設定されているケース)必ずクリアされます。これにより、Lexical のデフォルトヘッダー背景色と、貼り付け・ロードされたセルの外部シェーディングの両方が一括で除去されます。
テストスイートも拡充され、test/browser/tests/paste/table_cell_styles.test.js に以下のシナリオが追加されました。
// Excel がセル内部の span/font にハードコードした文字色も除去するテスト
test("strips color from elements nested inside pasted table cells", async ({
editor,
}) => {
const tableHtml = `
<table>
<tr>
<td style="color: windowtext;"><span style="color: black;">First</span></td>
<td><font color="#000000"><span style="color: #000000;">Second</span></font></td>
</tr>
</table>
`;
await editor.paste("First\tSecond", { html: tableHtml });
// すべてのセルと子要素の color スタイルが空になることを期待
});
// Excel 風のシェーディングセルが貼り付け時に背景と文字色が除去されるテスト
test("strips shading and text color from an Excel-style shaded cell", async ({
editor,
}) => {
const tableHtml = `
<table>
<tr>
<td style="background-color: #ffff00; color: black;"><span style="color: black;">Shaded</span></td>
</tr>
</table>
`;
await editor.paste("Shaded", { html: tableHtml });
});
これらのテストは、保存済みコンテンツのロード時、貼り付け時にセル内にネストした要素、そして Excel 風のシェーディングセル すべてで色が除去されることを検証しています。
設計判断
セル背景の正規化は新しいサニタイズロジックを別途導入するのではなく、既存の TableCellNode ノード変換 に統合された点が重要です。これにより、貼り付け・ロード・再編集の三つのパスに対して同一ロジックが適用され、コードベースの重複を防ぎつつメンテナンスコストを抑えられます。
背景色をクリアするだけでテーマカラーはそのまま継承されるため、Lexxy の テーマ一貫性 が保たれます。文字色やその他のインラインスタイルは パレットハイライト が内部ノードで管理しているため影響を受けず、ユーザーが意図したテキスト装飾は残ります。
セルシェーディング自体がエディタ側で設定できない方針(「セルの背景は外部コンテンツの影響下にある」)と整合させるため、背景のみを空文字列にリセット するという最小限の変更にとどめた設計です。これにより、将来的に背景以外の属性を拡張する際にも影響範囲が限定的です。
まとめ
TableCellNode の変換ロジックを改修し、すべてのセル背景を空文字列に正規化したことで、貼り付け・保存・再読み込みのすべてのフローで外部カラーが残らないようになりました。結果として、Lexxy のテーブルはテーマに忠実に表示され、不要なセルシェーディングや内部文字色が除去されることで、コンテンツの一貫性と編集体験が向上します。