@vuu-ui/vuu-chart 4.0.0-alpha.20 → 4.0.0-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +324 -0
  2. package/package.json +14 -17
  3. package/src/Chart.css.js +22 -0
  4. package/src/Chart.js +4 -4
  5. package/src/ChartSeries.js +20 -3
  6. package/src/chart-data/ChartDataStore.js +131 -0
  7. package/src/chart-data/projection-utils.js +119 -0
  8. package/src/charts/CandlestickChart.js +31 -0
  9. package/src/charts/CartesianChart.js +28 -0
  10. package/src/charts/DepthChart.js +28 -0
  11. package/src/charts/HeatmapChart.js +31 -0
  12. package/src/charts/HierarchyChart.js +41 -0
  13. package/src/charts/candlestick-model.js +373 -0
  14. package/src/charts/cartesian-model.js +234 -0
  15. package/src/charts/depth-model.js +159 -0
  16. package/src/charts/heatmap-model.js +243 -0
  17. package/src/charts/hierarchy-model.js +224 -0
  18. package/src/echarts.js +26 -0
  19. package/src/engine/ChartModel.js +2 -0
  20. package/src/engine/VuuChartContainer.js +196 -0
  21. package/src/engine/useStructuralMemo.js +18 -0
  22. package/src/index.js +8 -0
  23. package/src/options/common.js +181 -0
  24. package/src/options/mergeOptions.js +37 -0
  25. package/src/theme/chartTheme.js +74 -0
  26. package/src/useChartOptions.js +25 -2
  27. package/src/vuu-chart-types.js +0 -0
  28. package/types/Chart.d.ts +0 -28
  29. package/types/ChartSeries.d.ts +0 -32
  30. package/types/DataRowMovingWindow.d.ts +0 -23
  31. package/types/chart-types.d.ts +0 -42
  32. package/types/chartTooltip.d.ts +0 -12
  33. package/types/index.d.ts +0 -5
  34. package/types/src/Chart.d.ts +0 -28
  35. package/types/src/ChartSeries.d.ts +0 -32
  36. package/types/src/DataRowMovingWindow.d.ts +0 -23
  37. package/types/src/chart-types.d.ts +0 -42
  38. package/types/src/chartTooltip.d.ts +0 -12
  39. package/types/src/index.d.ts +0 -5
  40. package/types/src/useChartContextMenu.d.ts +0 -16
  41. package/types/src/useChartOptions.d.ts +0 -28
  42. package/types/src/useChartSelection.d.ts +0 -14
  43. package/types/useChartContextMenu.d.ts +0 -16
  44. package/types/useChartOptions.d.ts +0 -28
  45. package/types/useChartSelection.d.ts +0 -14
@@ -0,0 +1,234 @@
1
+ import { Aggregator, colorForRow, filterRows, getColumnValue, getNumber, sortRowsByNumber, toNumber } from "../chart-data/projection-utils.js";
2
+ import { GRID_OUTER_BOUNDS, axisStyle, dataZoomOption, hasSlider, legendOption, markLineOption, textStyle, tooltipOption } from "../options/common.js";
3
+ import { resolveColor } from "../theme/chartTheme.js";
4
+ const seriesIds = (series)=>{
5
+ const ids = new Set();
6
+ return series.map(({ columnName, id = columnName }, i)=>{
7
+ const uniqueId = ids.has(id) ? `${id}-${i}` : id;
8
+ ids.add(uniqueId);
9
+ return uniqueId;
10
+ });
11
+ };
12
+ const createCartesianModel = ({ aggregation, crosshair = true, dataSource, dataZoom, legend, orientation = "vertical", series, showTooltip = true, xAxis, yAxes = [
13
+ {}
14
+ ] })=>{
15
+ const xType = xAxis.type ?? "category";
16
+ const isCategory = "category" === xType;
17
+ const horizontal = "horizontal" === orientation;
18
+ const hasBars = series.some((s)=>"bar" === s.type);
19
+ const ids = seriesIds(series);
20
+ const legendPosition = legend ?? (series.length > 1 ? "top" : "none");
21
+ const dataSources = [
22
+ dataSource
23
+ ].concat(series.flatMap((s)=>s.dataSource ? [
24
+ s.dataSource
25
+ ] : []));
26
+ const buildBaseOption = (theme)=>{
27
+ const { palette } = theme;
28
+ const categoryAxis = {
29
+ id: "x",
30
+ type: xType,
31
+ name: xAxis.name,
32
+ boundaryGap: isCategory ? hasBars : void 0,
33
+ scale: "value" === xType ? true : void 0,
34
+ ...axisStyle(theme, false),
35
+ ...isCategory ? {
36
+ data: []
37
+ } : void 0
38
+ };
39
+ const valueAxes = yAxes.map((y, i)=>({
40
+ id: `y${i}`,
41
+ type: y.type ?? "value",
42
+ name: y.name,
43
+ position: y.position ?? (horizontal ? void 0 : 0 === i ? "left" : "right"),
44
+ scale: y.scale ?? !hasBars,
45
+ min: y.min,
46
+ max: y.max,
47
+ ...axisStyle(theme, 0 === i)
48
+ }));
49
+ const slider = hasSlider(dataZoom);
50
+ return {
51
+ animation: false,
52
+ backgroundColor: theme.backgroundColor,
53
+ color: palette,
54
+ textStyle: textStyle(theme),
55
+ grid: {
56
+ id: "main",
57
+ left: 8,
58
+ right: 8,
59
+ outerBounds: GRID_OUTER_BOUNDS,
60
+ top: "top" === legendPosition ? 32 : 16,
61
+ bottom: (slider ? 32 : 8) + ("bottom" === legendPosition ? 24 : 0)
62
+ },
63
+ legend: legendOption(theme, legendPosition),
64
+ tooltip: tooltipOption(theme, {
65
+ crosshair,
66
+ show: showTooltip
67
+ }),
68
+ xAxis: horizontal ? valueAxes : [
69
+ categoryAxis
70
+ ],
71
+ yAxis: horizontal ? [
72
+ categoryAxis
73
+ ] : valueAxes,
74
+ dataZoom: dataZoomOption(dataZoom, theme, [
75
+ 0
76
+ ], horizontal ? "y" : "x"),
77
+ series: series.map((s, i)=>{
78
+ const color = resolveColor(s.color, theme) ?? palette[i % palette.length];
79
+ const isLine = "line" === s.type || "area" === s.type;
80
+ const axisIndex = s.yAxisIndex ?? 0;
81
+ return {
82
+ id: ids[i],
83
+ name: s.name ?? s.columnName,
84
+ type: isLine ? "line" : s.type,
85
+ color,
86
+ ...horizontal ? {
87
+ xAxisIndex: axisIndex
88
+ } : {
89
+ yAxisIndex: axisIndex
90
+ },
91
+ encode: horizontal ? {
92
+ x: 1,
93
+ y: 0
94
+ } : {
95
+ x: 0,
96
+ y: 1
97
+ },
98
+ stack: s.stack,
99
+ ...isLine ? {
100
+ step: s.step ?? false,
101
+ smooth: s.smooth ?? false,
102
+ showSymbol: s.showSymbol ?? false,
103
+ symbolSize: s.symbolSize ?? 4,
104
+ connectNulls: true,
105
+ lineStyle: {
106
+ width: 1.5
107
+ },
108
+ areaStyle: "area" === s.type ? {
109
+ opacity: s.areaOpacity ?? 0.25
110
+ } : void 0
111
+ } : void 0,
112
+ ..."scatter" === s.type ? {
113
+ symbolSize: s.symbolSize ?? 8
114
+ } : void 0,
115
+ ..."bar" === s.type ? {
116
+ barMaxWidth: 40
117
+ } : void 0,
118
+ data: []
119
+ };
120
+ })
121
+ };
122
+ };
123
+ const buildDataOption = (data, theme)=>{
124
+ const { columnMap: mainColumnMap, rows: mainRows } = data.get(dataSource);
125
+ const categories = [];
126
+ const categoryIndex = new Map();
127
+ const addCategory = (value)=>{
128
+ const key = String(value);
129
+ let index = categoryIndex.get(key);
130
+ if (void 0 === index) {
131
+ index = categories.length;
132
+ categories.push(key);
133
+ categoryIndex.set(key, index);
134
+ }
135
+ return index;
136
+ };
137
+ if (isCategory) for (const row of mainRows)addCategory(getColumnValue(row, mainColumnMap, xAxis.columnName));
138
+ const rowLookup = {};
139
+ const seriesData = series.map((s, i)=>{
140
+ const ds = s.dataSource ?? dataSource;
141
+ const { columnMap, rows: allRows } = data.get(ds);
142
+ const xColumn = s.xColumnName ?? xAxis.columnName;
143
+ let rows = filterRows(allRows, columnMap, s.where);
144
+ if (!isCategory) rows = sortRowsByNumber(rows, columnMap, xColumn);
145
+ const items = [];
146
+ const values = [];
147
+ const itemRows = [];
148
+ const pushItem = (x, y, row)=>{
149
+ const color = s.colorBy ? colorForRow(row, columnMap, s.colorBy, theme) : void 0;
150
+ items.push(color ? {
151
+ value: [
152
+ x,
153
+ y
154
+ ],
155
+ itemStyle: {
156
+ color
157
+ }
158
+ } : [
159
+ x,
160
+ y
161
+ ]);
162
+ values.push(y);
163
+ itemRows.push(row);
164
+ };
165
+ if (isCategory && aggregation) {
166
+ const aggregators = new Map();
167
+ for (const row of rows){
168
+ const x = addCategory(getColumnValue(row, columnMap, xColumn));
169
+ let entry = aggregators.get(x);
170
+ if (void 0 === entry) {
171
+ entry = [
172
+ new Aggregator(aggregation),
173
+ row
174
+ ];
175
+ aggregators.set(x, entry);
176
+ }
177
+ entry[0].add(getNumber(row, columnMap, s.columnName));
178
+ }
179
+ const entries = Array.from(aggregators.entries()).sort(([x1], [x2])=>x1 - x2);
180
+ for (const [x, [aggregator, row]] of entries)pushItem(x, aggregator.value, row);
181
+ } else for (const row of rows){
182
+ const xValue = getColumnValue(row, columnMap, xColumn);
183
+ const x = isCategory ? addCategory(xValue) : toNumber(xValue);
184
+ if (null !== x) pushItem(x, getNumber(row, columnMap, s.columnName), row);
185
+ }
186
+ rowLookup[ids[i]] = {
187
+ dataSource: ds,
188
+ rows: itemRows
189
+ };
190
+ const markLine = markLineOption(s.markLines, theme, values, {
191
+ axis: horizontal ? "xAxis" : "yAxis",
192
+ defaultColor: resolveColor(s.color, theme) ?? theme.palette[i % theme.palette.length],
193
+ weights: s.markLines?.reduce((w, def)=>{
194
+ if (void 0 === w && "object" == typeof def.value) {
195
+ const weightColumn = def.value.weightedAverage;
196
+ return itemRows.map((row)=>getNumber(row, columnMap, weightColumn));
197
+ }
198
+ return w;
199
+ }, void 0)
200
+ });
201
+ return {
202
+ id: ids[i],
203
+ data: items,
204
+ ...markLine ? {
205
+ markLine
206
+ } : void 0
207
+ };
208
+ });
209
+ const categoryAxisData = isCategory ? [
210
+ {
211
+ id: "x",
212
+ data: categories
213
+ }
214
+ ] : void 0;
215
+ const option = {
216
+ series: seriesData,
217
+ ...categoryAxisData ? horizontal ? {
218
+ yAxis: categoryAxisData
219
+ } : {
220
+ xAxis: categoryAxisData
221
+ } : void 0
222
+ };
223
+ return {
224
+ option,
225
+ rowLookup
226
+ };
227
+ };
228
+ return {
229
+ buildBaseOption,
230
+ buildDataOption,
231
+ dataSources
232
+ };
233
+ };
234
+ export { createCartesianModel, seriesIds };
@@ -0,0 +1,159 @@
1
+ import { getColumnValue, getNumber } from "../chart-data/projection-utils.js";
2
+ import { GRID_OUTER_BOUNDS, axisStyle, legendOption, textStyle, tooltipOption } from "../options/common.js";
3
+ const cumulativeDepth = (levels, side)=>{
4
+ const sorted = levels.slice().sort((l1, l2)=>"bid" === side ? l2.price - l1.price : l1.price - l2.price);
5
+ let total = 0;
6
+ return sorted.map((level)=>{
7
+ total = level.cum ?? total + level.size;
8
+ return {
9
+ price: level.price,
10
+ cumSize: total,
11
+ row: level.row
12
+ };
13
+ });
14
+ };
15
+ const createDepthModel = ({ askValue = "SELL", bidValue = "BUY", cumulativeSizeColumnName, dataSource, legend = "top", priceColumnName = "price", showMidPrice = true, showTooltip = true, sideColumnName = "side", sizeColumnName = "size" })=>{
16
+ const buildBaseOption = (theme)=>{
17
+ const sideSeries = (id, color)=>({
18
+ id,
19
+ name: "bids" === id ? "Bids" : "Asks",
20
+ type: "line",
21
+ step: "bids" === id ? "start" : "end",
22
+ color,
23
+ showSymbol: false,
24
+ lineStyle: {
25
+ width: 1.5
26
+ },
27
+ areaStyle: {
28
+ opacity: 0.25
29
+ },
30
+ encode: {
31
+ x: 0,
32
+ y: 1
33
+ },
34
+ data: []
35
+ });
36
+ return {
37
+ animation: false,
38
+ backgroundColor: theme.backgroundColor,
39
+ textStyle: textStyle(theme),
40
+ grid: {
41
+ id: "main",
42
+ left: 8,
43
+ right: 8,
44
+ outerBounds: GRID_OUTER_BOUNDS,
45
+ top: "top" === legend ? 32 : 16,
46
+ bottom: "bottom" === legend ? 32 : 8
47
+ },
48
+ legend: legendOption(theme, legend),
49
+ tooltip: tooltipOption(theme, {
50
+ show: showTooltip
51
+ }),
52
+ xAxis: [
53
+ {
54
+ id: "x",
55
+ type: "value",
56
+ scale: true,
57
+ ...axisStyle(theme, false)
58
+ }
59
+ ],
60
+ yAxis: [
61
+ {
62
+ id: "y0",
63
+ type: "value",
64
+ position: "right",
65
+ ...axisStyle(theme)
66
+ }
67
+ ],
68
+ series: [
69
+ sideSeries("bids", theme.upColor),
70
+ sideSeries("asks", theme.downColor)
71
+ ]
72
+ };
73
+ };
74
+ const buildDataOption = (data, theme)=>{
75
+ const { columnMap, rows } = data.get(dataSource);
76
+ const bids = [];
77
+ const asks = [];
78
+ for (const row of rows){
79
+ const price = getNumber(row, columnMap, priceColumnName);
80
+ if (null === price) continue;
81
+ const side = String(getColumnValue(row, columnMap, sideColumnName));
82
+ const level = {
83
+ price,
84
+ size: getNumber(row, columnMap, sizeColumnName) ?? 0,
85
+ cum: cumulativeSizeColumnName ? getNumber(row, columnMap, cumulativeSizeColumnName) ?? void 0 : void 0,
86
+ row
87
+ };
88
+ if (side === bidValue) bids.push(level);
89
+ else if (side === askValue) asks.push(level);
90
+ }
91
+ const bidPoints = cumulativeDepth(bids, "bid").reverse();
92
+ const askPoints = cumulativeDepth(asks, "ask");
93
+ const bestBid = bidPoints.at(-1)?.price;
94
+ const bestAsk = askPoints[0]?.price;
95
+ const midPrice = void 0 !== bestBid && void 0 !== bestAsk ? (bestBid + bestAsk) / 2 : void 0;
96
+ const rowLookup = {
97
+ bids: {
98
+ dataSource,
99
+ rows: bidPoints.map((p)=>p.row)
100
+ },
101
+ asks: {
102
+ dataSource,
103
+ rows: askPoints.map((p)=>p.row)
104
+ }
105
+ };
106
+ const option = {
107
+ series: [
108
+ {
109
+ id: "bids",
110
+ data: bidPoints.map((p)=>[
111
+ p.price,
112
+ p.cumSize
113
+ ]),
114
+ markLine: {
115
+ silent: true,
116
+ symbol: [
117
+ "none",
118
+ "none"
119
+ ],
120
+ animation: false,
121
+ data: showMidPrice && void 0 !== midPrice ? [
122
+ {
123
+ xAxis: midPrice,
124
+ lineStyle: {
125
+ color: theme.crosshairColor,
126
+ type: "dashed",
127
+ width: 1
128
+ },
129
+ label: {
130
+ color: theme.secondaryForegroundColor,
131
+ formatter: `Mid ${+midPrice.toFixed(6)}`
132
+ }
133
+ }
134
+ ] : []
135
+ }
136
+ },
137
+ {
138
+ id: "asks",
139
+ data: askPoints.map((p)=>[
140
+ p.price,
141
+ p.cumSize
142
+ ])
143
+ }
144
+ ]
145
+ };
146
+ return {
147
+ option,
148
+ rowLookup
149
+ };
150
+ };
151
+ return {
152
+ buildBaseOption,
153
+ buildDataOption,
154
+ dataSources: [
155
+ dataSource
156
+ ]
157
+ };
158
+ };
159
+ export { createDepthModel, cumulativeDepth };
@@ -0,0 +1,243 @@
1
+ import { Aggregator, filterRows, getColumnValue, getNumber, toNumber } from "../chart-data/projection-utils.js";
2
+ import { axisStyle, dataZoomOption, formatTimeLabel, hasSlider, textStyle, tooltipOption } from "../options/common.js";
3
+ const decimalPlaces = (n)=>{
4
+ const [, fraction = ""] = String(n).split(".");
5
+ return fraction.length;
6
+ };
7
+ const bucketFloor = (value, bucketSize)=>{
8
+ const bucket = Math.floor(value / bucketSize + 1e-9) * bucketSize;
9
+ return Number(bucket.toFixed(decimalPlaces(bucketSize)));
10
+ };
11
+ const bucketValue = (value, axis)=>{
12
+ const isNumeric = "time" === axis.type || "value" === axis.type;
13
+ if (isNumeric || void 0 !== axis.bucketSize) {
14
+ const num = toNumber(value);
15
+ if (null === num) return null;
16
+ return axis.bucketSize ? bucketFloor(num, axis.bucketSize) : num;
17
+ }
18
+ return null == value ? null : String(value);
19
+ };
20
+ const sortKeys = (keys, axis)=>"time" === axis.type || "value" === axis.type || void 0 !== axis.bucketSize ? keys.slice().sort((k1, k2)=>Number(k1) - Number(k2)) : keys;
21
+ const createHeatmapModel = ({ aggregation = "sum", colorScale = "sequential", dataSource, dataZoom, formatTime = formatTimeLabel, max, min, showTooltip = true, showVisualMap = true, valueColumnName, where, xAxis, yAxis })=>{
22
+ const labelFormatter = (axis)=>"time" === axis.type ? (value)=>formatTime(Number(value)) : void 0;
23
+ const diverging = "diverging" === colorScale;
24
+ const visualMapRange = (hasValues, minValue, maxValue)=>{
25
+ if (diverging) {
26
+ const extent = hasValues ? Math.max(Math.abs(minValue), Math.abs(maxValue), 1) : 1;
27
+ return {
28
+ min: min ?? -extent,
29
+ max: max ?? extent
30
+ };
31
+ }
32
+ return {
33
+ min: min ?? (hasValues ? Math.min(0, minValue) : 0),
34
+ max: max ?? (hasValues ? Math.max(maxValue, minValue + 1) : 1)
35
+ };
36
+ };
37
+ let xLabels = [];
38
+ let yLabels = [];
39
+ const formatKey = (key, axis)=>void 0 !== key && "time" === axis.type ? formatTime(Number(key)) : key ?? "";
40
+ const buildBaseOption = (theme)=>{
41
+ const slider = hasSlider(dataZoom);
42
+ return {
43
+ animation: false,
44
+ backgroundColor: theme.backgroundColor,
45
+ textStyle: textStyle(theme),
46
+ grid: {
47
+ id: "main",
48
+ left: 8,
49
+ right: showVisualMap ? 56 : 8,
50
+ top: 16,
51
+ bottom: slider ? 36 : 8
52
+ },
53
+ tooltip: {
54
+ ...tooltipOption(theme, {
55
+ show: showTooltip,
56
+ trigger: "item"
57
+ }),
58
+ formatter: ({ value })=>{
59
+ const [x, y, v] = value;
60
+ return `${xAxis.name ?? xAxis.columnName}: ${formatKey(xLabels[x], xAxis)}<br/>${yAxis.name ?? yAxis.columnName}: ${formatKey(yLabels[y], yAxis)}<br/>${valueColumnName}: <b>${v ?? "-"}</b>`;
61
+ }
62
+ },
63
+ xAxis: [
64
+ {
65
+ id: "x",
66
+ type: "category",
67
+ name: xAxis.name,
68
+ data: [],
69
+ splitArea: {
70
+ show: false
71
+ },
72
+ ...axisStyle(theme, false),
73
+ axisLabel: {
74
+ ...axisStyle(theme).axisLabel,
75
+ formatter: labelFormatter(xAxis)
76
+ }
77
+ }
78
+ ],
79
+ yAxis: [
80
+ {
81
+ id: "y0",
82
+ type: "category",
83
+ name: yAxis.name,
84
+ data: [],
85
+ ...axisStyle(theme, false),
86
+ axisLabel: {
87
+ ...axisStyle(theme).axisLabel,
88
+ formatter: labelFormatter(yAxis)
89
+ }
90
+ }
91
+ ],
92
+ visualMap: [
93
+ {
94
+ id: "vm",
95
+ type: "continuous",
96
+ show: showVisualMap,
97
+ calculable: true,
98
+ orient: "vertical",
99
+ right: 0,
100
+ top: "center",
101
+ itemHeight: 120,
102
+ itemWidth: 12,
103
+ min: min ?? 0,
104
+ max: max ?? 1,
105
+ inRange: {
106
+ color: diverging ? [
107
+ theme.downColor,
108
+ theme.splitLineColor,
109
+ theme.upColor
110
+ ] : theme.heatmapColors
111
+ },
112
+ textStyle: {
113
+ color: theme.secondaryForegroundColor
114
+ }
115
+ }
116
+ ],
117
+ dataZoom: dataZoomOption(dataZoom, theme, [
118
+ 0
119
+ ]),
120
+ series: [
121
+ {
122
+ id: "heatmap",
123
+ name: valueColumnName,
124
+ type: "heatmap",
125
+ encode: {
126
+ x: 0,
127
+ y: 1,
128
+ value: 2
129
+ },
130
+ progressive: 0,
131
+ emphasis: {
132
+ itemStyle: {
133
+ borderColor: theme.foregroundColor,
134
+ borderWidth: 1
135
+ }
136
+ },
137
+ data: []
138
+ }
139
+ ]
140
+ };
141
+ };
142
+ const buildDataOption = (data)=>{
143
+ const { columnMap, rows: allRows } = data.get(dataSource);
144
+ const rows = filterRows(allRows, columnMap, where);
145
+ const cells = new Map();
146
+ const xKeys = new Set();
147
+ const yKeys = new Set();
148
+ for (const row of rows){
149
+ const x = bucketValue(getColumnValue(row, columnMap, xAxis.columnName), xAxis);
150
+ const y = bucketValue(getColumnValue(row, columnMap, yAxis.columnName), yAxis);
151
+ if (null === x || null === y) continue;
152
+ xKeys.add(x);
153
+ yKeys.add(y);
154
+ const key = `${x}|${y}`;
155
+ let cell = cells.get(key);
156
+ if (void 0 === cell) {
157
+ cell = {
158
+ x,
159
+ y,
160
+ agg: new Aggregator(aggregation),
161
+ rows: []
162
+ };
163
+ cells.set(key, cell);
164
+ }
165
+ cell.agg.add(getNumber(row, columnMap, valueColumnName));
166
+ cell.rows.push(row);
167
+ }
168
+ const xCategories = sortKeys(Array.from(xKeys), xAxis);
169
+ const yCategories = sortKeys(Array.from(yKeys), yAxis);
170
+ const xIndex = new Map(xCategories.map((k, i)=>[
171
+ k,
172
+ i
173
+ ]));
174
+ const yIndex = new Map(yCategories.map((k, i)=>[
175
+ k,
176
+ i
177
+ ]));
178
+ let minValue = 1 / 0;
179
+ let maxValue = -1 / 0;
180
+ const items = [];
181
+ const itemRows = [];
182
+ for (const cell of cells.values()){
183
+ const value = cell.agg.value;
184
+ if (null !== value) {
185
+ minValue = Math.min(minValue, value);
186
+ maxValue = Math.max(maxValue, value);
187
+ }
188
+ items.push([
189
+ xIndex.get(cell.x) ?? 0,
190
+ yIndex.get(cell.y) ?? 0,
191
+ value
192
+ ]);
193
+ itemRows.push(cell.rows[0]);
194
+ }
195
+ const hasValues = Number.isFinite(minValue);
196
+ xLabels = xCategories.map(String);
197
+ yLabels = yCategories.map(String);
198
+ const rowLookup = {
199
+ heatmap: {
200
+ dataSource,
201
+ rows: itemRows
202
+ }
203
+ };
204
+ const option = {
205
+ xAxis: [
206
+ {
207
+ id: "x",
208
+ data: xLabels
209
+ }
210
+ ],
211
+ yAxis: [
212
+ {
213
+ id: "y0",
214
+ data: yLabels
215
+ }
216
+ ],
217
+ visualMap: [
218
+ {
219
+ id: "vm",
220
+ ...visualMapRange(hasValues, minValue, maxValue)
221
+ }
222
+ ],
223
+ series: [
224
+ {
225
+ id: "heatmap",
226
+ data: items
227
+ }
228
+ ]
229
+ };
230
+ return {
231
+ option,
232
+ rowLookup
233
+ };
234
+ };
235
+ return {
236
+ buildBaseOption,
237
+ buildDataOption,
238
+ dataSources: [
239
+ dataSource
240
+ ]
241
+ };
242
+ };
243
+ export { createHeatmapModel };