ReoGrid ReoGrid Web

JavaScriptスプレッドシートで作る「信号機ダッシュボード」 ― 条件付き書式を実例で

· unvell team
JavaScriptスプレッドシートで作る「信号機ダッシュボード」 ― 条件付き書式を実例で

前回のセルタイプの記事では、操作する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 と一対一で対応します ― equalnotEqualgreaterThanlessThangreaterThanOrEquallessThanOrEqualbetweennotBetween。2値の演算子(between / notBetween)は value1value2 の両方を、それ以外は 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 },
});

テキスト演算子は containsnotContainsbeginsWithendsWith。大文字小文字を区別したいときは 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 } },
});

省いた辺は手動の罫線がそのまま残ります。styleborder は併用でき、一致時には両方が適用されます。


ルールの管理

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
組み込み関数(SUMCOUNTIF 等)
xlsx 書き出し(saveAsXlsx

グリッドの表示・編集・xlsx読み込みは無料の Lite で完結します。自分で再評価するルールベースのスタイリングが Pro の領域です。


xlsx ラウンドトリップ

ルールは Excel との往復でも保持されます。書き出し時には標準の <conditionalFormatting> 要素として、スタイルは <dxfs> テーブルに書かれます。辺ごとの border ペイロードは <dxf><border> でラウンドトリップします。Excel でファイルを開けば、同じルールがホーム → 条件付き書式 → ルールの管理にそのまま並び、再インポートすればグリッドに戻ってきます。


まとめ

条件付き書式は、テーブルを「自分で読めるもの」に変えます。3種類のルール ― しきい値の cellIs、列をまたぐロジックの expression、ステータスの containsText ― に、信号機のための priority / stopIfTrue を加えれば、値が変わってもグリッドが全部の色を同期し続けます。

条件付き書式デモで動きを確かめるか、条件付き書式のドキュメントでAPI全体を読んでください。セルにスタイルを付けるのではなく、操作するUIをセルのに描く話はセルタイプの記事へ。

ReoGrid Web を試してみる

React/Vue 向けの Canvas ベース Excel 互換スプレッドシートコンポーネント。 Lite は無料 — npm install 一発で始められます。

関連記事

ニュースレター

開発の最新情報をお届けします

新しいリリース・機能追加・お知らせをいち早く受け取るには、
メーリングリストにご登録ください。