3社のグリッドを調べて「セル結合」の落とし所を決めた話
TssGrid は「ビルド不要・依存ゼロ」を売りにした軽量グリッドです。だからこそ、機能を1つ足すたびに 「これは肥大化に見合うか?」を毎回考えます。今回はずっと避けてきた セル結合 に手を出すにあたって、 メジャーな3つのライブラリ ― ag-grid / Handsontable / jspreadsheet ― が どう実装しているかを調べ、TssGrid としての「落とし所」を決めた記録です。
なぜ今まで避けていたか
セル結合は「見た目だけ」の機能に見えて、実は座標系の前提を壊すのがやっかいです。 1セル=1行1列という素直なモデルに、「このセルは3列ぶん」「この行とこの行はつながっている」という 例外が差し込まれる。選択・編集・コピー・行挿入・Undo ― ほぼ全機能が結合を意識し始めます。 ここを雑に作ると、軽量どころか一番の複雑性発生源になります。
3社はどう割り切っているか
実装方針を、業務フォーム視点でざっくり並べるとこうでした。
| 観点 | ag-grid | Handsontable | jspreadsheet |
|---|---|---|---|
| 結合の持ち方 | 列スパン中心(行はピン留め思想) | mergeCells 配列(矩形) | colspan/rowspan 属性 |
| ソートとの関係 | 仮想化前提で強い | 結合とソートは相性注意 | 基本シンプル |
| 狙う規模 | 大規模・仮想スクロール | Excel ライク全部入り | 軽量・スプレッドシート |
| 重さ | 大 | 中〜大 | 小 |
TssGrid が狙うのは「日本の業務フォーム」。1万行を仮想スクロールで捌く世界ではなく、 帳票の見出しをきれいに結合したい世界です。だから ag-grid 的な重武装は要らない。 jspreadsheet の軽さに、Handsontable の「矩形 mergeCells」の素直さを足すのが筋だと判断しました。
何を捨てたか
- 結合中のソート ― 完全にロック。結合した帳票を並べ替える要求は実務でほぼ無く、 整合性コストに見合わない。
- 縦結合がある状態の行移動 ― 禁止。行順を崩すと縦結合が分断され、 それを自動で割る(auto-split)処理は重い。横結合だけなら行と一緒に動くので許可。
「できない」を最初に決めると、残りの実装は驚くほど素直になります。
何を残したか
逆に、業務で本当に要るものは妥協しませんでした。矩形結合(colspan×rowspan)、 Undo 対応(結合と、結合時に消えた従属セルの値の両方を復元)、 行/列の挿入削除での座標追従。API はこれだけです。
grid.setMerge(0, 1, 3); // (0,1) から3列を横結合 grid.setMerge(1, 0, 1, 2); // (1,0) から2行を縦結合 grid.setMerge(2, 2, 2, 2); // 2×2 の矩形結合 grid.mergeSelection(); // 選択範囲をまとめて結合 grid.removeMerge(0, 1); // 解除(Undo 可)
実装の勘所:対称化で行数を抑える
横結合(colspan)と縦結合(rowspan)を別々に書くと2倍に膨らむ。そこで 「アンカー(左上)+ 覆われた従属セル」という1つのモデルに統一し、 列方向・行方向を対称に扱う関数だけ用意しました。既存の選択・描画スキップの仕組みを使い回したので、 フル矩形結合でもコアの増分は +71 行に収まっています。
動かしてみる
下のグリッドで実際にセル結合を試せます。セルを範囲選択して「選択を結合」(または右クリック → セルを結合)。結合セルを選んで「結合を解除」。横・縦・矩形どれもOK、Undo(Ctrl+Z)も効きます。