[basecamp/lexxy] テーブルをfigure要素でラップしてoverflow-xに対応

basecamp/lexxy

背景

HTMLのテーブル要素は、コンテンツが親要素の幅を超える場合、max-width: 100% を指定しても親コンテナからはみ出してしまうという特性があります。これを解決するには、テーブルを overflow-x: auto を持つラッパー要素で囲む必要がありました。

Lexxyエディタでは、エディタ内では setScrollableTablesActive(this.editor, true) でテーブルをラップできましたが、出力されるHTMLには反映されていませんでした。当初はレンダリング側で処理することを想定していましたが、Lexxyの出力をそのまま使えるようにするため、テーブル出力時に自動的にラッパーを含めることになりました。

実装の詳細

WrappedTableNodeの追加

Lexicalの TableNode を継承した WrappedTableNode クラスを新規作成しました。このノードは exportDOM メソッドをオーバーライドし、テーブル要素を <figure class="lexxy-content__table-wrapper"> で囲んだHTMLを出力します。

import { TableNode } from "@lexical/table"
import { createElement } from "../helpers/html_helper"

export class WrappedTableNode extends TableNode {
  static clone(node) {
    return new WrappedTableNode(node.__key)
  }

  exportDOM(editor) {
    const superExport = super.exportDOM(editor)

    return {
      ...superExport,
      after: (tableElement) => {
        if (superExport.after) {
          tableElement = superExport.after(tableElement)
        }

        const clonedTable = tableElement.cloneNode(true)
        const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML)

        return wrappedTable
      }
    }
  }
}

ポイントは after フックを使って、親クラスの処理後にテーブルをクローンし、figure要素でラップして返すことです。superExport.after が存在する場合は先に実行してから、ラッピング処理を行います。

エディタ設定での置き換え

エディタの初期化時に、標準の TableNodeWrappedTableNode に置き換える設定を追加しました。

import { WrappedTableNode } from "../nodes/wrapped_table_node"

// ノード登録部分
nodes: [
  // ...
  {
    replace: TableNode,
    with: () => { return new WrappedTableNode() }
  },
  TableCellNode,
  TableRowNode,
]

この設定により、既存の TableNodeWrappedTableNode に自動的に置き換えられます。

CSSの調整

ラッパー要素のスタイルには margin: 0 を追加し、デフォルトのfigure要素のマージンをリセットしました。

.lexxy-content__table-wrapper {
  margin: 0;
  margin-block: 1ch;
  overflow-x: auto;
}

テストの追加

出力HTMLに正しくfigure要素が含まれることを確認するテストを追加しました。

test "table is wrapped in figure.table-wrapper" do
  find_editor.toggle_command("insertTable")

  html = find_editor.value
  assert_match(/<figure class="lexxy-content__table-wrapper">/, html, "Exported HTML should have figure. table-wrapper")

  assert_match(/<figure class="lexxy-content__table-wrapper">.*?<table>.*?<\/table>.*?<\/figure>/m, html, "Table with content should be nested inside the figure wrapper")
end

また、既存のテストヘルパーで引数の順序(cols, rows)を修正し、テストの一貫性を保ちました。

影響範囲

この変更により、Lexxyエディタから出力される全てのテーブルが自動的に <figure class="lexxy-content__table-wrapper"> で囲まれるようになります。レンダリング側で個別にラッパーを追加する必要がなくなり、Lexxyの出力をそのまま使用できるようになりました。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

ガイドライン準拠 ✓ PASS

記事構成とDiffDaily Styleへの準拠状況

記事構成の3要素(Title, Context, Technical Detail)が明確に記載されています。コードブロック前後の空行やファイル名付きシンタックスハイライト、GitHubリンク記法などのカスタムMarkdown構文も正しく使用されており、ガイドラインを完全に遵守しています。

  • 記事構成(Title、Context、Technical Detail)
  • DiffDaily Styleガイド準拠
  • カスタムMarkdown活用
  • 対象読者への適合性
技術的整合性 ✓ PASS

技術的な正確性と表現の適切性

記事で引用されているコードはPRのDiff内容と一致しており、技術的な説明も正確です。`WrappedTableNode`の`exportDOM`メソッドのオーバーライドや、エディタ設定でのノード置き換えなど、変更の核心部分が論理的に解説されています。

  • 技術用語の正確性
  • コード例の正確性
  • 説明の技術的正確性
PR内容との整合性 ✓ PASS

元のPR情報との一致度

PRのDescriptionに記載された変更の背景(エディタ内と出力HTMLの乖離)や目的(出力をそのまま使えるようにする)が記事に忠実に反映されています。事実に基づかない推測やハルシネーションは見られず、PRの内容と完全に整合性が取れています。

  • タイトル・説明の一致
  • Diff内容の正確な反映
  • 推測の排除