前回のセルタイプの記事では、操作するUIをセルの「中」に置きました。今回はグリッド自身に色を付けさせます ― 在庫がしきい値を下回れば赤、売上が目標を超えれば緑、キャンセル注文は自動でフラグ。しかもどの色も、値が変わった瞬間に更新されます。スタイルのループも、再描画の配線も不要。ルールを一度宣言するだけです。
条件付き書式とは
条件付き書式は、セルをその値に応じてスタイリングする仕組みです。「在庫が20未満なら赤」というルールを定義しておくと、グリッドは編集のたびにそれを再評価します。静的なレポートと、値に反応するダッシュボードの違いはここにあります。
ReoGrid Web は、Excelユーザーにはおなじみの3種類のルールをサポートします。
| 種類 | 判定基準 | 例 |
|---|---|---|
cellIs | 固定のしきい値と値を比較 | 在庫 < 20 を強調 |
expression | セルごとに数式を評価 | =E2>D2(売上が目標超)の行を強調 |
containsText | テキストの部分一致 | "Cancelled" 行を色分け |
条件付き書式は Pro 機能です(料金)。以下のコードは
@reogrid/proを前提とします。
いちばんシンプルなルール
addConditionalFormat で範囲にルールを付けます。引数は Excel 形式の A1 範囲とルールオブジェクトです。
import { createReogrid } from '@reogrid/pro';
const { worksheet } = createReogrid('#grid');
// 90点以上を緑に
worksheet.range('C2:C100').addConditionalFormat({
type: 'cellIs',
operator: 'greaterThanOrEqual',
value1: 90,
style: { backgroundColor: '#bbf7d0', color: '#14532d', bold: true },
});
パターンはこれで全部です。あとはルールの種類を選び、ルールを重ねていくだけです。
実例:在庫ダッシュボードを作る
ひと目で「どこに注意すべきか」が分かる商品テーブルを作ってみましょう。在庫不足は赤、潤沢なら緑、目標を超えた(あるいは届かなかった)売上、そしてステータスの色分け。これがフルコードです。
import { createReogrid } from '@reogrid/pro';
const { worksheet: ws } = createReogrid('#grid');
ws.suspendRender(); // 初期構築をまとめて描画
// ── 列幅 ──
[140, 110, 80, 80, 80, 100, 100].forEach((w, i) => { ws.column(i).width = w; });
// ── データ ──
const HEADERS = ['商品', 'カテゴリ', '在庫', '目標', '売上', '売上高', 'ステータス'];
const ROWS = [
['ワイヤレスマウス', '電子機器', 150, 200, 210, 6300, 'Delivered'],
['USB-Cハブ', '電子機器', 25, 180, 165, 8250, 'Delivered'],
['昇降デスク', '家具', 45, 100, 82, 24600, 'Pending'],
['エルゴノミクスチェア', '家具', 8, 60, 45, 13500, 'Pending'],
['メカニカルキーボード', '電子機器', 200, 300, 320, 25600, 'Delivered'],
['HD Webカメラ', '電子機器', 65, 150, 95, 4750, 'Cancelled'],
['ノートPCスタンド', 'アクセサリ', 5, 90, 70, 2100, 'Pending'],
['ノイズキャンセルイヤホン', '電子機器', 15, 250, 180, 14400, 'Pending'],
];
// ヘッダー行
HEADERS.forEach((h, c) => {
ws.cell(0, c).setValue(h).setStyle({
bold: true, backgroundColor: '#1e3a5f', color: '#e2e8f0', textAlign: 'center',
});
});
ws.row(0).height = 30;
// データ行(数値は右寄せ)
ROWS.forEach((row, r) => {
row.forEach((val, c) => {
ws.cell(r + 1, c).setValue(val).setStyle({ textAlign: c >= 2 && c <= 5 ? 'right' : 'left' });
});
});
const last = ROWS.length + 1; // 1始まりの最終行番号
// ── ルール1: 在庫 ― 信号機(cellIs + priority + stopIfTrue) ──
ws.range(`C2:C${last}`).addConditionalFormat({
type: 'cellIs', operator: 'lessThan', value1: 20,
priority: 1, stopIfTrue: true,
style: { backgroundColor: '#fecaca', color: '#7f1d1d', bold: true }, // 危険
});
ws.range(`C2:C${last}`).addConditionalFormat({
type: 'cellIs', operator: 'lessThan', value1: 50,
priority: 2, stopIfTrue: true,
style: { backgroundColor: '#fef3c7', color: '#92400e' }, // 少なめ
});
ws.range(`C2:C${last}`).addConditionalFormat({
type: 'cellIs', operator: 'greaterThanOrEqual', value1: 150,
priority: 3,
style: { backgroundColor: '#bbf7d0', color: '#14532d', bold: true }, // 潤沢
});
// ── ルール2: 売上 vs 目標 ― 数式ベース(expression) ──
ws.range(`E2:E${last}`).addConditionalFormat({
type: 'expression', formula: '=E2>D2', // 目標達成
priority: 1, stopIfTrue: true,
style: { backgroundColor: '#dcfce7', color: '#166534', bold: true },
});
ws.range(`E2:E${last}`).addConditionalFormat({
type: 'expression', formula: '=E2<D2*0.7', // 目標の70%未満
priority: 2,
style: { backgroundColor: '#fee2e2', color: '#991b1b' },
});
// ── ルール3: ステータス ― テキスト一致(containsText) ──
ws.range(`G2:G${last}`).addConditionalFormat({
type: 'containsText', operator: 'contains', text: 'Delivered',
style: { backgroundColor: '#dcfce7', color: '#166534' },
});
ws.range(`G2:G${last}`).addConditionalFormat({
type: 'containsText', operator: 'contains', text: 'Cancelled',
style: { backgroundColor: '#fee2e2', color: '#991b1b', bold: true },
});
// ── ルール4: 高売上高 ― 太字+青文字(塗りなし) ──
ws.range(`F2:F${last}`).addConditionalFormat({
type: 'cellIs', operator: 'greaterThanOrEqual', value1: 10000,
style: { bold: true, color: '#1d4ed8' },
});
ws.range(`A1:G${last}`).border({ style: 'solid', color: '#cbd5e1' });
ws.resumeRender();
4ブロックの宣言から4色のダッシュボードができました。いくつかポイントを挙げます。
cellIs ― しきい値との比較
主力です。各セルを1つ、または2つの固定値と比較します。
// しきい値1つ
ws.range('C2:C100').addConditionalFormat({
type: 'cellIs', operator: 'lessThan', value1: 50,
style: { backgroundColor: '#fef3c7' },
});
// しきい値2つ(範囲)
ws.range('C2:C100').addConditionalFormat({
type: 'cellIs', operator: 'between', value1: 50, value2: 70,
style: { backgroundColor: '#fef3c7' },
});
演算子は Excel と一対一で対応します ― equal・notEqual・greaterThan・lessThan・greaterThanOrEqual・lessThanOrEqual・between・notBetween。2値の演算子(between / notBetween)は value1 と value2 の両方を、それ以外は value1 だけを使います。
expression ― 比較だけでは足りないとき
cellIs ルールは、色を付けるセル自身しか見られません。2つの列を比較する ― 売上と目標を突き合わせる ― には数式が必要です。範囲の先頭行向けに書けば、ReoGrid が相対参照を各行に合わせて自動でずらします。
// 売上(E)が目標(D)を超える行で E列を緑に
ws.range('E2:E100').addConditionalFormat({
type: 'expression',
formula: '=E2>D2', // E3>D3, E4>D4 … と範囲を下に評価
style: { backgroundColor: '#dcfce7', color: '#166534', bold: true },
});
先頭の = は省略可能で、'E2>D2' でも同じ動作です。数式エンジンが評価できるものは何でも書けるので、関数を織り込むこともできます ― =AND(E2>D2, G2="Delivered")。
containsText ― テキストの色分け
ステータス列には部分一致を。一致はデフォルトで大文字小文字を区別しません(Excel互換)。
ws.range('G2:G100').addConditionalFormat({
type: 'containsText', operator: 'contains', text: 'Cancelled',
style: { backgroundColor: '#fee2e2', color: '#991b1b', bold: true },
});
テキスト演算子は contains・notContains・beginsWith・endsWith。大文字小文字を区別したいときは caseSensitive: true を渡します。
priority と stopIfTrue ― 信号機のコツ
複数のルールが同じセルに当たるとき、priority が評価順を決め(数字が小さいほど先)、stopIfTrue はルールが一致した時点で以降の評価を止めます。この2つで「最初に一致したものが勝つ」を実現できます ― まさに信号機に必要な挙動で、危険なほど少ない値が「ただ少なめ」の黄色まで拾ってしまうのを防げます。
// 在庫 < 20 → 赤、そして停止(下の黄色ルールは適用しない)
ws.range('C2:C100').addConditionalFormat({
type: 'cellIs', operator: 'lessThan', value1: 20,
priority: 1, stopIfTrue: true,
style: { backgroundColor: '#fecaca' },
});
// 在庫 < 50 → 黄
ws.range('C2:C100').addConditionalFormat({
type: 'cellIs', operator: 'lessThan', value1: 50,
priority: 2, stopIfTrue: true,
style: { backgroundColor: '#fef3c7' },
});
// 在庫 >= 150 → 緑
ws.range('C2:C100').addConditionalFormat({
type: 'cellIs', operator: 'greaterThanOrEqual', value1: 150,
priority: 3,
style: { backgroundColor: '#bbf7d0' },
});
stopIfTrue がなければ、後から一致したルールが上に重なります ― 塗りと青太字のテキストルールを両方かけたいときは便利ですが、そうでないときは厄介です。
自分で再評価する
ここが肝です。ルールはライブです。セルを変えると、グリッドはそれを含むすべてのルールを再実行します ― 手入力でも、数式経由でも、プログラムからでも。
ws.cell('C5').value = 12; // 20を下回る → セルがひとりでに赤くなる
これこそ、ReoGrid のシートをスナップショットではなくダッシュボードにするものです。値をデータソースに繋げば、追加コードゼロで色が数値に追従します。条件付き書式デモで試してみてください。在庫や売上の列を編集するか「Randomize」を押すと、グリッド全体が色を塗り替えます。
おまけ:色付きの罫線
style ペイロードに加えて、ルールは border ペイロードを持てます ― 個別の辺を塗るので、セル全体の見た目を崩さずに外れ値を色付きルールで目立たせるのに便利です。
ws.range('B2:B100').addConditionalFormat({
type: 'cellIs', operator: 'greaterThan', value1: 1000,
style: {}, // style は必須 ― 罫線のみのルールでは {} を渡す
border: { right: { style: 'solid', color: '#dc2626', width: 2 } },
});
省いた辺は手動の罫線がそのまま残ります。style と border は併用でき、一致時には両方が適用されます。
ルールの管理
const id = ws.range('C2:C100').addConditionalFormat({ /* … */ }); // id を返す
ws.removeConditionalFormat(id); // 1つ削除
ws.range('C2:C100').clearConditionalFormats(); // 範囲をクリア
ws.clearConditionalFormats(); // シート全体をクリア
ws.getConditionalFormats(); // 一覧: [{ id, range, rule }, …]
Lite と Pro の境界
| 操作 | Lite(無料) | Pro |
|---|---|---|
| xlsx 読み込み・結合・罫線・表示形式 | ✅ | ✅ |
算術式(=D4*1.25) | ✅ | ✅ |
条件付き書式(addConditionalFormat) | — | ✅ |
組み込み関数(SUM・COUNTIF 等) | — | ✅ |
xlsx 書き出し(saveAsXlsx) | — | ✅ |
グリッドの表示・編集・xlsx読み込みは無料の Lite で完結します。自分で再評価するルールベースのスタイリングが Pro の領域です。
xlsx ラウンドトリップ
ルールは Excel との往復でも保持されます。書き出し時には標準の <conditionalFormatting> 要素として、スタイルは <dxfs> テーブルに書かれます。辺ごとの border ペイロードは <dxf><border> でラウンドトリップします。Excel でファイルを開けば、同じルールがホーム → 条件付き書式 → ルールの管理にそのまま並び、再インポートすればグリッドに戻ってきます。
まとめ
条件付き書式は、テーブルを「自分で読めるもの」に変えます。3種類のルール ― しきい値の cellIs、列をまたぐロジックの expression、ステータスの containsText ― に、信号機のための priority / stopIfTrue を加えれば、値が変わってもグリッドが全部の色を同期し続けます。
条件付き書式デモで動きを確かめるか、条件付き書式のドキュメントでAPI全体を読んでください。セルにスタイルを付けるのではなく、操作するUIをセルの中に描く話はセルタイプの記事へ。