WebFOCUSのHTML5拡張グラフを開発する(カード型デザイン)

2025/02/21に公開

WebFOCUSのHTML5拡張グラフでカード型デザイン

はじめに

WebFOCUSのHTML5拡張グラフ機能を使って、カード型デザインのレイアウトを作成し拡張機能の理解を深めたいと思います。

拡張機能変更のポイント

com.shimokado.card-simple.jsで行う処理は、前作の<table>タグで表として出力したものよりシンプルになっています。

<div class="data-card">
 <div class="card-label">${data[n].labels}</div>
 <div class="card-value">${data[n].value}</div>
</div>

外部CSS

今回のポイントは外部CSSを利用している所です。

config.resources.cssの配列にcss/style.cssを読み込んでいます。

com.shimokado.card-simple.jsの一部
	var config = {
		id: 'com.shimokado.card-simple',	// エクステンションID
		resources: {
			script: [], // 読み込むリソースのリスト。.jsまたは.cssファイルを指定可能
			css: ['css/style.css'] // 読み込むリソースのリスト。.jsまたは.cssファイルを指定可能
		},

※ 拡張機能フォルダは以下の構成になっています。

  • com.shimokado.card-simple
    • css
      • style.css
    • icons
      • card-simple.png
    • com.shimokado.card-simple.js
    • properties.json

レスポンシブルなカードを実現しているCSS

style.css

/* カードグリッドコンテナ */
.card-grid-container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1.5rem;
    padding: 1.5rem;
    width: 100%;
    background: #fff5f5;
}

/* カード */
.data-card {
    background: linear-gradient(135deg, #fff, #ffe4e4);
    border-radius: 12px;
    box-shadow: 0 4px 15px rgba(220, 20, 60, 0.1);
    padding: 2rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    min-height: 150px;
    border: 1px solid rgba(220, 20, 60, 0.1);
}

/* カードホバー効果 */
.data-card:hover {
    transform: translateY(-8px) scale(1.02);
    box-shadow: 0 8px 25px rgba(220, 20, 60, 0.2);
    background: linear-gradient(135deg, #fff, #ffd5d5);
}

/* ラベル */
.card-label {
    font-size: 1rem;
    color: #dc143c;
    margin-bottom: 1.2rem;
    text-align: center;
    width: 100%;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* 値 */
.card-value {
    font-size: 2.5rem;
    font-weight: bold;
    background: linear-gradient(45deg, #dc143c, #ff4d6d);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-align: center;
    text-shadow: 2px 2px 4px rgba(220, 20, 60, 0.1);
    transition: all 0.3s ease;
}

/* 値のホバー効果 */
.data-card:hover .card-value {
    transform: scale(1.1);
}

実装イメージ

グラフ選択

valueの降順にカードを並べています。

グラフ選択

拡張モジュール全文

com.shimokado.card-simple.js
/* Copyright (C) 2025. Shimokado Masataka. All rights reserved. */

(function() {
	/**
	 * チャートの初期化処理
	 * @param {function} successCallback - 初期化成功時に呼び出すコールバック関数
	 * @param {object} initConfig - 初期化設定
	 */
	function initCallback(successCallback, initConfig) {
		successCallback(true);
	}

	/**
	 * データがない場合の事前レンダリングコールバック
	 * @param {object} preRenderConfig - 事前レンダリング設定
	 */
	function noDataPreRenderCallback(preRenderConfig) {
	}

	/**
	 * データがない場合のレンダリングコールバック
	 * @param {object} renderConfig - レンダリング設定
	 */
	function noDataRenderCallback(renderConfig) {
	}

	/**
	 * プリレンダリングコールバック
	 * @param {object} preRenderConfig - 事前レンダリング設定
	 */
	function preRenderCallback(preRenderConfig) {
	}
	
	/**
	 * レンダリングコールバック
	 * @param {object} renderConfig - レンダリング設定
	 * @param {object} renderConfig.moonbeamInstance - Moonbeamインスタンス
	 * @param {object} renderConfig.properties - プロパティ
	 * @param {object} renderConfig.container - コンテナ
	 * @param {object} renderConfig.data - データ
	 * @param {object} renderConfig.dataBuckets - データバケット
	 * @param {object} renderConfig.dataBuckets.labels - ラベルデータバケット
	 * @param {object} renderConfig.dataBuckets.value - 値データバケット
	 * @param {function} renderConfig.renderComplete - レンダリング完了時に呼び出すコールバック関数
	 */
	function renderCallback(renderConfig) {
		var chart = renderConfig.moonbeamInstance;
		var props = renderConfig.properties;
		var container = renderConfig.container;
		var data = renderConfig.data;
		var dataBuckets = renderConfig.dataBuckets.buckets;

		 // データを値の降順でソート
		data.sort(function(a, b) {
			return b.value - a.value;
		});

		// カードのスタイル設定
		var fontSize = props.tableStyle ? props.tableStyle.fontSize : "12px";
		var color = props.tableStyle ? props.tableStyle.color : "#000000";

		// カードコンテナの作成
		var cardContainer = document.createElement('div');
		cardContainer.className = 'card-grid-container';
		container.appendChild(cardContainer);

		// データを降順でソート
		data.sort(function(a, b) {
			return b.value - a.value;
		});

		// カードの生成
		data.forEach(function(row) {
			var card = document.createElement('div');
			card.className = 'data-card';
			
			var label = document.createElement('div');
			label.className = 'card-label';
			label.textContent = row.labels;
			
			var value = document.createElement('div');
			value.className = 'card-value';
			value.textContent = chart.formatNumber(row.value, dataBuckets.value.numberFormat || '###');
			
			card.appendChild(label);
			card.appendChild(value);
			cardContainer.appendChild(card);
		});

		renderConfig.renderComplete();
	}

	var config = {
		id: 'com.shimokado.card-simple',	// エクステンションID
		containerType: 'html',	// コンテナタイプ(html/svg)
		initCallback: initCallback,	// 拡張機能の初期化直前に呼び出される関数への参照。必要に応じてMonbeamインスタンスを設定するために使用
		preRenderCallback: preRenderCallback,  // 拡張機能のレンダリング直前に呼び出される関数への参照。preRenderConfigオブジェクトが渡されます
		renderCallback: renderCallback,  // 実際のチャートを描画する関数への参照。renderConfigオブジェクトが渡されます
		noDataPreRenderCallback: noDataPreRenderCallback, // データがない場合のレンダリング直前に呼び出される関数への参照
		noDataRenderCallback: noDataRenderCallback, // データがない場合のチャート描画関数への参照
		resources: {
			script: [], // 読み込むリソースのリスト。.jsまたは.cssファイルを指定可能
			css: ['css/style.css'] // 読み込むリソースのリスト。.jsまたは.cssファイルを指定可能
		},
		modules: {
			dataSelection: {
				supported: true,  // データ選択を有効にする場合はtrueに設定
				needSVGEventPanel: true, // HTMLコンテナを使用するか、SVGコンテナを変更する場合はtrueに設定
				svgNode: function(arg){}  // HTMLコンテナを使用するか、SVGコンテナを変更する場合、ルートSVGノードへの参照を返す
			},
			eventHandler: {
				supported: true // イベントハンドラを有効にする場合はtrueに設定
			},
			tooltip: {
				supported: true,  // HTMLツールチップを有効にする場合はtrueに設定
				// デフォルトのツールチップコンテンツが渡されない場合に呼び出されるコールバック
				// 指定されたターゲット、ID、データに対して適切なデフォルトツールチップを定義
				// 戻り値は文字列(HTMLを含む)、HTMLノード、またはMoonbeamツールチップAPIオブジェクト
				autoContent: function(target, s, g, d) {
					return d.labels + ': ' + d.value; // 単純な文字列を返す
				}
			}
		}
	};
	// エクステンションをtdgchartエクステンションマネージャに登録
	tdgchart.extensionManager.register(config);
}());

まとめ

外部CSSを読み込むことで、鮮やかなデザインも実現できますね。

CSS配列には、相対パス以外にもhttp://xxx.xxx/xxx.cssの記述も可能です。

次回、外部JSやD3も使ってダッシュボードのような拡張グラフを作ってみようと思います。

Discussion