From 958f63ecd234253b99266cde0d9070aab527de8d Mon Sep 17 00:00:00 2001 From: Subhash Halder Date: Wed, 5 Jul 2023 11:35:57 +0530 Subject: [PATCH] Improved parsing to work for minimal configuration possible. --- demos/xychart.html | 6 +- .../xychart/chartBuilder/Interfaces.ts | 12 +- .../mermaid/src/diagrams/xychart/xychartDb.ts | 107 ++++++++++++++---- 3 files changed, 96 insertions(+), 29 deletions(-) diff --git a/demos/xychart.html b/demos/xychart.html index 5803de4b8..ea24e4872 100644 --- a/demos/xychart.html +++ b/demos/xychart.html @@ -39,11 +39,9 @@

XY Charts demos

-    xychart-beta horizontal
-    title Basic xychart
-    x-axis "this is x axis" [category1, "category 2", category3, category4]
-    y-axis yaxisText 10 --> 150
+    xychart-beta
     line [23, 46, 75, 43]
+    bar "sample bat" [52, 96, 35, 10]
     

diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/Interfaces.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/Interfaces.ts index efee023ba..54b7bc004 100644 --- a/packages/mermaid/src/diagrams/xychart/chartBuilder/Interfaces.ts +++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/Interfaces.ts @@ -4,7 +4,7 @@ export interface ChartComponent { getDrawableElements(): DrawableElem[]; } -export type SimplePlotDataType = [string | number, number][]; +export type SimplePlotDataType = [string, number][]; export interface LinePlotData { type: 'line'; @@ -26,11 +26,13 @@ export function isBarPlot(data: PlotData): data is BarPlotData { } export interface BandAxisDataType { + type: 'band'; title: string; categories: string[]; } export interface LinearAxisDataType { + type: 'linear'; title: string; min: number; max: number; @@ -38,8 +40,12 @@ export interface LinearAxisDataType { export type AxisDataType = LinearAxisDataType | BandAxisDataType; -export function isBandAxisData(data: any): data is BandAxisDataType { - return data.categories && Array.isArray(data.categories); +export function isBandAxisData(data: AxisDataType): data is BandAxisDataType { + return data.type === 'band'; +} + +export function isLinearAxisData(data: AxisDataType): data is LinearAxisDataType { + return data.type === 'linear'; } export interface XYChartData { diff --git a/packages/mermaid/src/diagrams/xychart/xychartDb.ts b/packages/mermaid/src/diagrams/xychart/xychartDb.ts index eb43d6315..a8d6971c8 100644 --- a/packages/mermaid/src/diagrams/xychart/xychartDb.ts +++ b/packages/mermaid/src/diagrams/xychart/xychartDb.ts @@ -12,7 +12,13 @@ import { clear as commonClear, } from '../../commonDb.js'; import { XYChartBuilder } from './chartBuilder/index.js'; -import { DrawableElem, XYChartData, isBandAxisData } from './chartBuilder/Interfaces.js'; +import { + DrawableElem, + SimplePlotDataType, + XYChartData, + isBandAxisData, + isLinearAxisData, +} from './chartBuilder/Interfaces.js'; import { XYChartConfig } from '../../config.type.js'; const config = configApi.getConfig(); @@ -64,12 +70,14 @@ function getChartDefaultConfig(): XYChartConfig { function getChartDefalutData(): XYChartData { return { yAxis: { - title: 'yAxis1', - min: 0, - max: 100, + type: 'linear', + title: '', + min: Infinity, + max: -Infinity, }, xAxis: { - title: 'xAxis', + type: 'band', + title: '', categories: [], }, title: '', @@ -79,6 +87,8 @@ function getChartDefalutData(): XYChartData { let xyChartConfig: XYChartConfig = getChartDefaultConfig(); let xyChartData: XYChartData = getChartDefalutData(); +let hasSetXAxis = false; +let hasSetYAxis = false; function textSanitizer(text: string) { return sanitizeText(text.trim(), config); @@ -100,41 +110,92 @@ function setXAxisTitle(title: string) { xyChartData.xAxis.title = textSanitizer(title); } function setXAxisRangeData(min: number, max: number) { - xyChartData.xAxis = { title: xyChartData.xAxis.title, min, max }; + xyChartData.xAxis = { type: 'linear', title: xyChartData.xAxis.title, min, max }; + hasSetXAxis = true; } function setXAxisBand(categories: string[]) { xyChartData.xAxis = { + type: 'band', title: xyChartData.xAxis.title, categories: categories.map((c) => textSanitizer(c)), }; + hasSetXAxis = true; } function setYAxisTitle(title: string) { xyChartData.yAxis.title = textSanitizer(title); } function setYAxisRangeData(min: number, max: number) { - xyChartData.yAxis = { title: xyChartData.yAxis.title, min, max }; + xyChartData.yAxis = { type: 'linear', title: xyChartData.yAxis.title, min, max }; + hasSetYAxis = true; +} + +// this function does not set `hasSetYAxis` as there can be multiple data so we should calculate the range accordingly +function setYAxisRangeFromPlotData(data: number[]) { + const minValue = Math.min(...data); + const maxValue = Math.max(...data); + const prevMinValue = isLinearAxisData(xyChartData.yAxis) ? xyChartData.yAxis.min : Infinity; + const prevMaxValue = isLinearAxisData(xyChartData.yAxis) ? xyChartData.yAxis.max : -Infinity; + xyChartData.yAxis = { + type: 'linear', + title: xyChartData.yAxis.title, + min: Math.min(prevMinValue, minValue), + max: Math.max(prevMaxValue, maxValue), + }; +} + +function transformDataWithOutCategory(data: number[]): SimplePlotDataType { + let retData: SimplePlotDataType = []; + if (data.length === 0) { + return retData; + } + if (!hasSetXAxis) { + const prevMinValue = isLinearAxisData(xyChartData.xAxis) ? xyChartData.xAxis.min : Infinity; + const prevMaxValue = isLinearAxisData(xyChartData.xAxis) ? xyChartData.xAxis.max : -Infinity; + setXAxisRangeData(Math.min(prevMinValue, 1), Math.max(prevMaxValue, data.length)); + } + if (!hasSetYAxis) { + setYAxisRangeFromPlotData(data); + } + + if (isBandAxisData(xyChartData.xAxis)) { + retData = xyChartData.xAxis.categories.map((c, i) => [c, data[i]]); + } + + if (isLinearAxisData(xyChartData.xAxis)) { + const min = xyChartData.xAxis.min; + const max = xyChartData.xAxis.max; + const step = (max - min + 1) / data.length; + const categories: string[] = []; + for (let i = min; i <= max; i += step) { + categories.push(`${i}`); + } + retData = categories.map((c, i) => [c, data[i]]); + } + + return retData; } function setLineData(title: string, data: number[]) { - if (isBandAxisData(xyChartData.xAxis)) { - xyChartData.plots.push({ - type: 'line', - strokeFill: '#00ff00', - strokeWidth: 2, - data: xyChartData.xAxis.categories.map((c, i) => [c, data[i]]), - }); - } + const plotData = transformDataWithOutCategory(data); + xyChartData.plots.push({ + type: 'line', + strokeFill: '#00ff00', + strokeWidth: 2, + data: plotData, + }); } function setBarData(title: string, data: number[]) { - if (isBandAxisData(xyChartData.xAxis)) { - xyChartData.plots.push({ - type: 'bar', - fill: '#0000bb', - data: xyChartData.xAxis.categories.map((c, i) => [c, data[i]]), - }); - } + const plotData = transformDataWithOutCategory(data); + xyChartData.plots.push({ + type: 'bar', + fill: '#0000bb', + data: plotData, + }); } function getDrawableElem(): DrawableElem[] { + if (xyChartData.plots.length === 0) { + throw Error('No Plot to render, please provide a plot with some data'); + } xyChartData.title = getDiagramTitle(); return XYChartBuilder.build(xyChartConfig, xyChartData); } @@ -151,6 +212,8 @@ const clear = function () { commonClear(); xyChartConfig = getChartDefaultConfig(); xyChartData = getChartDefalutData(); + hasSetXAxis = false; + hasSetYAxis = false; }; export default {