@meonode/canvas 2.0.1 → 2.0.3
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.
- package/dist/cjs/canvas/canvas.helper.js +0 -230
- package/dist/cjs/canvas/canvas.helper.js.map +1 -1
- package/dist/cjs/canvas/chart.canvas.js +70 -144
- package/dist/cjs/canvas/chart.canvas.js.map +1 -1
- package/dist/cjs/canvas/grid.canvas.js.map +1 -1
- package/dist/cjs/canvas/image.canvas.js +2 -2
- package/dist/cjs/canvas/image.canvas.js.map +1 -1
- package/dist/cjs/canvas/layout.canvas.js +23 -16
- package/dist/cjs/canvas/layout.canvas.js.map +1 -1
- package/dist/cjs/canvas/root.canvas.js +23 -117
- package/dist/cjs/canvas/root.canvas.js.map +1 -1
- package/dist/cjs/canvas/text.canvas.js +2 -2
- package/dist/cjs/canvas/text.canvas.js.map +1 -1
- package/dist/cjs/constant/common.const.js.map +1 -1
- package/dist/cjs/{canvas → src/canvas}/canvas.helper.d.ts +1 -20
- package/dist/cjs/src/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/{esm → cjs/src}/canvas/canvas.type.d.ts +1 -12
- package/dist/cjs/src/canvas/canvas.type.d.ts.map +1 -0
- package/dist/cjs/{canvas → src/canvas}/chart.canvas.d.ts +1 -1
- package/dist/cjs/src/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/cjs/src/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/{esm → cjs/src}/canvas/image.canvas.d.ts +1 -1
- package/dist/cjs/src/canvas/image.canvas.d.ts.map +1 -0
- package/dist/{esm → cjs/src}/canvas/layout.canvas.d.ts +2 -2
- package/dist/cjs/src/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/{esm → cjs/src}/canvas/root.canvas.d.ts +3 -2
- package/dist/cjs/src/canvas/root.canvas.d.ts.map +1 -0
- package/dist/cjs/{canvas → src/canvas}/text.canvas.d.ts +1 -1
- package/dist/cjs/src/canvas/text.canvas.d.ts.map +1 -0
- package/dist/cjs/src/constant/common.const.d.ts +18 -0
- package/dist/cjs/src/constant/common.const.d.ts.map +1 -0
- package/dist/cjs/src/index.d.ts.map +1 -0
- package/dist/cjs/src/util/disk.cache.d.ts.map +1 -0
- package/dist/cjs/src/worker/comlink.pool.d.ts +30 -0
- package/dist/cjs/src/worker/comlink.pool.d.ts.map +1 -0
- package/dist/cjs/src/worker/comlink.setup.d.ts +4 -0
- package/dist/cjs/src/worker/comlink.setup.d.ts.map +1 -0
- package/dist/cjs/src/worker/render.worker.d.ts.map +1 -0
- package/dist/cjs/src/worker/worker.types.d.ts +13 -0
- package/dist/cjs/src/worker/worker.types.d.ts.map +1 -0
- package/dist/cjs/util/disk.cache.js.map +1 -1
- package/dist/cjs/worker/comlink.pool.js +164 -0
- package/dist/cjs/worker/comlink.pool.js.map +1 -0
- package/dist/cjs/worker/comlink.setup.js +53 -0
- package/dist/cjs/worker/comlink.setup.js.map +1 -0
- package/dist/cjs/worker/render.worker.js +58 -61
- package/dist/cjs/worker/render.worker.js.map +1 -1
- package/dist/esm/canvas/canvas.helper.js +1 -230
- package/dist/esm/canvas/chart.canvas.js +71 -145
- package/dist/esm/canvas/image.canvas.js +2 -2
- package/dist/esm/canvas/layout.canvas.js +23 -16
- package/dist/esm/canvas/root.canvas.js +23 -116
- package/dist/esm/canvas/text.canvas.js +2 -2
- package/dist/esm/{canvas → src/canvas}/canvas.helper.d.ts +1 -20
- package/dist/esm/src/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/{cjs → esm/src}/canvas/canvas.type.d.ts +1 -12
- package/dist/esm/src/canvas/canvas.type.d.ts.map +1 -0
- package/dist/esm/{canvas → src/canvas}/chart.canvas.d.ts +1 -1
- package/dist/esm/src/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/esm/src/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/{cjs → esm/src}/canvas/image.canvas.d.ts +1 -1
- package/dist/esm/src/canvas/image.canvas.d.ts.map +1 -0
- package/dist/{cjs → esm/src}/canvas/layout.canvas.d.ts +2 -2
- package/dist/esm/src/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/{cjs → esm/src}/canvas/root.canvas.d.ts +3 -2
- package/dist/esm/src/canvas/root.canvas.d.ts.map +1 -0
- package/dist/esm/{canvas → src/canvas}/text.canvas.d.ts +1 -1
- package/dist/esm/src/canvas/text.canvas.d.ts.map +1 -0
- package/dist/esm/src/constant/common.const.d.ts +18 -0
- package/dist/esm/src/constant/common.const.d.ts.map +1 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/util/disk.cache.d.ts.map +1 -0
- package/dist/esm/src/worker/comlink.pool.d.ts +30 -0
- package/dist/esm/src/worker/comlink.pool.d.ts.map +1 -0
- package/dist/esm/src/worker/comlink.setup.d.ts +4 -0
- package/dist/esm/src/worker/comlink.setup.d.ts.map +1 -0
- package/dist/esm/src/worker/render.worker.d.ts.map +1 -0
- package/dist/esm/src/worker/worker.types.d.ts +13 -0
- package/dist/esm/src/worker/worker.types.d.ts.map +1 -0
- package/dist/esm/worker/comlink.pool.js +139 -0
- package/dist/esm/worker/comlink.setup.js +30 -0
- package/dist/esm/worker/render.worker.js +38 -60
- package/package.json +9 -8
- package/dist/cjs/canvas/canvas.helper.d.ts.map +0 -1
- package/dist/cjs/canvas/canvas.type.d.ts.map +0 -1
- package/dist/cjs/canvas/chart.canvas.d.ts.map +0 -1
- package/dist/cjs/canvas/grid.canvas.d.ts.map +0 -1
- package/dist/cjs/canvas/image.canvas.d.ts.map +0 -1
- package/dist/cjs/canvas/layout.canvas.d.ts.map +0 -1
- package/dist/cjs/canvas/root.canvas.d.ts.map +0 -1
- package/dist/cjs/canvas/text.canvas.d.ts.map +0 -1
- package/dist/cjs/constant/common.const.d.ts +0 -37
- package/dist/cjs/constant/common.const.d.ts.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/util/disk.cache.d.ts.map +0 -1
- package/dist/cjs/worker/render.worker.d.ts.map +0 -1
- package/dist/cjs/worker/worker.types.d.ts +0 -76
- package/dist/cjs/worker/worker.types.d.ts.map +0 -1
- package/dist/esm/canvas/canvas.helper.d.ts.map +0 -1
- package/dist/esm/canvas/canvas.type.d.ts.map +0 -1
- package/dist/esm/canvas/chart.canvas.d.ts.map +0 -1
- package/dist/esm/canvas/grid.canvas.d.ts.map +0 -1
- package/dist/esm/canvas/image.canvas.d.ts.map +0 -1
- package/dist/esm/canvas/layout.canvas.d.ts.map +0 -1
- package/dist/esm/canvas/root.canvas.d.ts.map +0 -1
- package/dist/esm/canvas/text.canvas.d.ts.map +0 -1
- package/dist/esm/constant/common.const.d.ts +0 -37
- package/dist/esm/constant/common.const.d.ts.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/util/disk.cache.d.ts.map +0 -1
- package/dist/esm/worker/render.worker.d.ts.map +0 -1
- package/dist/esm/worker/worker.types.d.ts +0 -76
- package/dist/esm/worker/worker.types.d.ts.map +0 -1
- /package/dist/cjs/{canvas → src/canvas}/grid.canvas.d.ts +0 -0
- /package/dist/cjs/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/cjs/{util → src/util}/disk.cache.d.ts +0 -0
- /package/dist/cjs/{worker → src/worker}/render.worker.d.ts +0 -0
- /package/dist/esm/{canvas → src/canvas}/grid.canvas.d.ts +0 -0
- /package/dist/esm/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/esm/{util → src/util}/disk.cache.d.ts +0 -0
- /package/dist/esm/{worker → src/worker}/render.worker.d.ts +0 -0
|
@@ -1,29 +1,7 @@
|
|
|
1
|
-
import { BoxNode, RowNode
|
|
1
|
+
import { BoxNode, RowNode } from './layout.canvas.js';
|
|
2
2
|
import { Style } from '../constant/common.const.js';
|
|
3
3
|
import { TextNode } from './text.canvas.js';
|
|
4
|
-
import { ImageNode } from './image.canvas.js';
|
|
5
4
|
|
|
6
|
-
/**
|
|
7
|
-
* Local buildTree for pre-computed render function results.
|
|
8
|
-
* Handles only node types that render functions would return (Box, Column, Row, Text, Image).
|
|
9
|
-
* Avoids circular dependency with root.canvas.ts.
|
|
10
|
-
*/
|
|
11
|
-
function buildDescriptorTree(descriptor) {
|
|
12
|
-
switch (descriptor.__type) {
|
|
13
|
-
case 'Box':
|
|
14
|
-
return new BoxNode({ ...descriptor.props, children: descriptor.children?.map(buildDescriptorTree) });
|
|
15
|
-
case 'Column':
|
|
16
|
-
return new ColumnNode({ ...descriptor.props, children: descriptor.children?.map(buildDescriptorTree) });
|
|
17
|
-
case 'Row':
|
|
18
|
-
return new RowNode({ ...descriptor.props, children: descriptor.children?.map(buildDescriptorTree) });
|
|
19
|
-
case 'Image':
|
|
20
|
-
return new ImageNode(descriptor.props);
|
|
21
|
-
case 'Text':
|
|
22
|
-
return new TextNode(descriptor.text, descriptor.props);
|
|
23
|
-
default:
|
|
24
|
-
return new BoxNode({});
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
5
|
class ChartNode extends BoxNode {
|
|
28
6
|
chartData;
|
|
29
7
|
chartType;
|
|
@@ -68,9 +46,9 @@ class ChartNode extends BoxNode {
|
|
|
68
46
|
}
|
|
69
47
|
}
|
|
70
48
|
}
|
|
71
|
-
_renderContent(ctx, x, y, width, height) {
|
|
49
|
+
async _renderContent(ctx, x, y, width, height) {
|
|
72
50
|
// First render background/borders from parent
|
|
73
|
-
super._renderContent(ctx, x, y, width, height);
|
|
51
|
+
await super._renderContent(ctx, x, y, width, height);
|
|
74
52
|
// Then render chart-specific content
|
|
75
53
|
const paddingLeft = this.node.getComputedPadding(Style.Edge.Left);
|
|
76
54
|
const paddingRight = this.node.getComputedPadding(Style.Edge.Right);
|
|
@@ -82,16 +60,16 @@ class ChartNode extends BoxNode {
|
|
|
82
60
|
const contentHeight = height - paddingTop - paddingBottom;
|
|
83
61
|
switch (this.chartType) {
|
|
84
62
|
case 'bar':
|
|
85
|
-
this.renderBarChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
63
|
+
await this.renderBarChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
86
64
|
break;
|
|
87
65
|
case 'line':
|
|
88
|
-
this.renderLineChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
66
|
+
await this.renderLineChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
89
67
|
break;
|
|
90
68
|
case 'pie':
|
|
91
|
-
this.renderPieChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
69
|
+
await this.renderPieChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
92
70
|
break;
|
|
93
71
|
case 'doughnut':
|
|
94
|
-
this.renderDoughnutChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
72
|
+
await this.renderDoughnutChart(ctx, contentX, contentY, contentWidth, contentHeight);
|
|
95
73
|
break;
|
|
96
74
|
}
|
|
97
75
|
}
|
|
@@ -211,7 +189,7 @@ class ChartNode extends BoxNode {
|
|
|
211
189
|
chartY: chartAreaY,
|
|
212
190
|
};
|
|
213
191
|
}
|
|
214
|
-
renderBarChart(ctx, x, y, width, height) {
|
|
192
|
+
async renderBarChart(ctx, x, y, width, height) {
|
|
215
193
|
if (this.chartType !== 'bar')
|
|
216
194
|
return;
|
|
217
195
|
const chartData = this.chartData;
|
|
@@ -226,8 +204,7 @@ class ChartNode extends BoxNode {
|
|
|
226
204
|
if (chartOptions?.showYAxis) {
|
|
227
205
|
const fontSize = chartOptions.yAxisFontSize || 12;
|
|
228
206
|
ctx.font = `${fontSize}px ${this.props.fontFamily || 'sans-serif'}`;
|
|
229
|
-
const maxLabel = chartOptions.
|
|
230
|
-
(chartOptions.yAxisLabelFormatter ? chartOptions.yAxisLabelFormatter(maxValue) : this.getSmartYAxisFormatter(maxValue)(maxValue));
|
|
207
|
+
const maxLabel = chartOptions.yAxisLabelFormatter ? await chartOptions.yAxisLabelFormatter(maxValue) : this.getSmartYAxisFormatter(maxValue)(maxValue);
|
|
231
208
|
const yAxisWidth = ctx.measureText(maxLabel).width + 10;
|
|
232
209
|
chartX += yAxisWidth;
|
|
233
210
|
chartWidth -= yAxisWidth;
|
|
@@ -261,8 +238,7 @@ class ChartNode extends BoxNode {
|
|
|
261
238
|
ctx.stroke();
|
|
262
239
|
if (chartOptions?.showYAxis) {
|
|
263
240
|
const value = maxValue - (maxValue / 5) * i;
|
|
264
|
-
const label = chartOptions.
|
|
265
|
-
(chartOptions.yAxisLabelFormatter ? chartOptions.yAxisLabelFormatter(value) : this.getSmartYAxisFormatter(maxValue)(value));
|
|
241
|
+
const label = chartOptions.yAxisLabelFormatter ? await chartOptions.yAxisLabelFormatter(value) : this.getSmartYAxisFormatter(maxValue)(value);
|
|
266
242
|
TextNode.renderSimpleText(ctx, label, chartX - 5, gridY, {
|
|
267
243
|
color: chartOptions.yAxisColor || chartOptions.axisColor || '#000',
|
|
268
244
|
fontSize: chartOptions.yAxisFontSize || 12,
|
|
@@ -275,9 +251,11 @@ class ChartNode extends BoxNode {
|
|
|
275
251
|
ctx.setLineDash([]);
|
|
276
252
|
}
|
|
277
253
|
// Render bars
|
|
278
|
-
labels.
|
|
254
|
+
for (let index = 0; index < labels.length; index++) {
|
|
255
|
+
const label = labels[index];
|
|
279
256
|
const groupX = chartX + index * groupWidth + barSpacing / 2;
|
|
280
|
-
datasets.
|
|
257
|
+
for (let datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
|
258
|
+
const dataset = datasets[datasetIndex];
|
|
281
259
|
const barHeight = (dataset.data[index] / maxValue) * finalChartHeight;
|
|
282
260
|
const barX = groupX + datasetIndex * barWidth;
|
|
283
261
|
const barY = chartY + finalChartHeight - barHeight;
|
|
@@ -288,21 +266,13 @@ class ChartNode extends BoxNode {
|
|
|
288
266
|
const value = dataset.data[index];
|
|
289
267
|
const valueX = barX + barWidth / 2;
|
|
290
268
|
const valueY = barY - 5; // 5px padding above bar
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const valueNode = buildDescriptorTree(preComputedValueDesc);
|
|
294
|
-
valueNode.processInitialChildren();
|
|
295
|
-
valueNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
296
|
-
const layout = valueNode.node.getComputedLayout();
|
|
297
|
-
valueNode.render(ctx, valueX - layout.width / 2, valueY - layout.height);
|
|
298
|
-
}
|
|
299
|
-
else if (chartOptions.renderValueItem) {
|
|
300
|
-
const valueNode = chartOptions.renderValueItem({ item: value, index, datasetIndex });
|
|
269
|
+
if (chartOptions.renderValueItem) {
|
|
270
|
+
const valueNode = await chartOptions.renderValueItem({ item: value, index, datasetIndex });
|
|
301
271
|
if (valueNode) {
|
|
302
272
|
valueNode.processInitialChildren();
|
|
303
273
|
valueNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
304
274
|
const layout = valueNode.node.getComputedLayout();
|
|
305
|
-
valueNode.render(ctx, valueX - layout.width / 2, valueY - layout.height);
|
|
275
|
+
await valueNode.render(ctx, valueX - layout.width / 2, valueY - layout.height);
|
|
306
276
|
}
|
|
307
277
|
}
|
|
308
278
|
else {
|
|
@@ -315,25 +285,17 @@ class ChartNode extends BoxNode {
|
|
|
315
285
|
});
|
|
316
286
|
}
|
|
317
287
|
}
|
|
318
|
-
}
|
|
288
|
+
}
|
|
319
289
|
// Render labels
|
|
320
290
|
if (chartOptions?.showLabels) {
|
|
321
|
-
const displayLabel = chartOptions.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const labelNode = buildDescriptorTree(preComputedLabelDesc);
|
|
325
|
-
labelNode.processInitialChildren();
|
|
326
|
-
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
327
|
-
const layout = labelNode.node.getComputedLayout();
|
|
328
|
-
labelNode.render(ctx, groupX + (groupWidth - barSpacing) / 2 - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
329
|
-
}
|
|
330
|
-
else if (chartOptions.renderLabelItem) {
|
|
331
|
-
const labelNode = chartOptions.renderLabelItem({ item: label, index });
|
|
291
|
+
const displayLabel = chartOptions.xAxisLabelFormatter ? await chartOptions.xAxisLabelFormatter(label, index) : label;
|
|
292
|
+
if (chartOptions.renderLabelItem) {
|
|
293
|
+
const labelNode = await chartOptions.renderLabelItem({ item: label, index });
|
|
332
294
|
if (labelNode) {
|
|
333
295
|
labelNode.processInitialChildren();
|
|
334
296
|
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
335
297
|
const layout = labelNode.node.getComputedLayout();
|
|
336
|
-
labelNode.render(ctx, groupX + (groupWidth - barSpacing) / 2 - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
298
|
+
await labelNode.render(ctx, groupX + (groupWidth - barSpacing) / 2 - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
337
299
|
}
|
|
338
300
|
}
|
|
339
301
|
else {
|
|
@@ -346,13 +308,13 @@ class ChartNode extends BoxNode {
|
|
|
346
308
|
});
|
|
347
309
|
}
|
|
348
310
|
}
|
|
349
|
-
}
|
|
311
|
+
}
|
|
350
312
|
// Render legend
|
|
351
313
|
if (chartOptions?.showLegend) {
|
|
352
|
-
this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
314
|
+
await this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
353
315
|
}
|
|
354
316
|
}
|
|
355
|
-
renderLineChart(ctx, x, y, width, height) {
|
|
317
|
+
async renderLineChart(ctx, x, y, width, height) {
|
|
356
318
|
if (this.chartType !== 'line')
|
|
357
319
|
return;
|
|
358
320
|
const chartData = this.chartData;
|
|
@@ -367,8 +329,7 @@ class ChartNode extends BoxNode {
|
|
|
367
329
|
if (chartOptions?.showYAxis) {
|
|
368
330
|
const fontSize = chartOptions.yAxisFontSize || 12;
|
|
369
331
|
ctx.font = `${fontSize}px ${this.props.fontFamily || 'sans-serif'}`;
|
|
370
|
-
const maxLabel = chartOptions.
|
|
371
|
-
(chartOptions.yAxisLabelFormatter ? chartOptions.yAxisLabelFormatter(maxValue) : this.getSmartYAxisFormatter(maxValue)(maxValue));
|
|
332
|
+
const maxLabel = chartOptions.yAxisLabelFormatter ? await chartOptions.yAxisLabelFormatter(maxValue) : this.getSmartYAxisFormatter(maxValue)(maxValue);
|
|
372
333
|
const yAxisWidth = ctx.measureText(maxLabel).width + 10;
|
|
373
334
|
chartX += yAxisWidth;
|
|
374
335
|
chartWidth -= yAxisWidth;
|
|
@@ -400,8 +361,7 @@ class ChartNode extends BoxNode {
|
|
|
400
361
|
ctx.stroke();
|
|
401
362
|
if (chartOptions?.showYAxis) {
|
|
402
363
|
const value = maxValue - (maxValue / 5) * i;
|
|
403
|
-
const label = chartOptions.
|
|
404
|
-
(chartOptions.yAxisLabelFormatter ? chartOptions.yAxisLabelFormatter(value) : this.getSmartYAxisFormatter(maxValue)(value));
|
|
364
|
+
const label = chartOptions.yAxisLabelFormatter ? await chartOptions.yAxisLabelFormatter(value) : this.getSmartYAxisFormatter(maxValue)(value);
|
|
405
365
|
TextNode.renderSimpleText(ctx, label, chartX - 5, gridY, {
|
|
406
366
|
color: chartOptions.yAxisColor || chartOptions.axisColor || '#000',
|
|
407
367
|
fontSize: chartOptions.yAxisFontSize || 12,
|
|
@@ -414,11 +374,13 @@ class ChartNode extends BoxNode {
|
|
|
414
374
|
ctx.setLineDash([]);
|
|
415
375
|
}
|
|
416
376
|
// Render lines and points
|
|
417
|
-
datasets.
|
|
377
|
+
for (let datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
|
378
|
+
const dataset = datasets[datasetIndex];
|
|
418
379
|
ctx.strokeStyle = dataset.color || this.generateColor(datasetIndex);
|
|
419
380
|
ctx.lineWidth = 2;
|
|
420
381
|
ctx.beginPath();
|
|
421
|
-
dataset.data.
|
|
382
|
+
for (let index = 0; index < dataset.data.length; index++) {
|
|
383
|
+
const value = dataset.data[index];
|
|
422
384
|
const pointX = chartX + index * pointSpacing;
|
|
423
385
|
const pointY = chartY + finalChartHeight - (value / maxValue) * finalChartHeight;
|
|
424
386
|
if (index === 0) {
|
|
@@ -427,38 +389,31 @@ class ChartNode extends BoxNode {
|
|
|
427
389
|
else {
|
|
428
390
|
ctx.lineTo(pointX, pointY);
|
|
429
391
|
}
|
|
430
|
-
}
|
|
392
|
+
}
|
|
431
393
|
ctx.stroke();
|
|
432
394
|
// Render points
|
|
433
|
-
dataset.data.
|
|
395
|
+
for (let index = 0; index < dataset.data.length; index++) {
|
|
434
396
|
const pointX = chartX + index * pointSpacing;
|
|
435
|
-
const pointY = chartY + finalChartHeight - (
|
|
397
|
+
const pointY = chartY + finalChartHeight - (dataset.data[index] / maxValue) * finalChartHeight;
|
|
436
398
|
ctx.fillStyle = dataset.color || this.generateColor(datasetIndex);
|
|
437
399
|
ctx.beginPath();
|
|
438
400
|
ctx.arc(pointX, pointY, 4, 0, Math.PI * 2);
|
|
439
401
|
ctx.fill();
|
|
440
|
-
}
|
|
441
|
-
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
442
404
|
// Render labels
|
|
443
405
|
if (chartOptions?.showLabels) {
|
|
444
|
-
labels.
|
|
406
|
+
for (let index = 0; index < labels.length; index++) {
|
|
407
|
+
const label = labels[index];
|
|
445
408
|
const pointX = chartX + index * pointSpacing;
|
|
446
|
-
const displayLabel = chartOptions.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const labelNode = buildDescriptorTree(preComputedLabelDesc);
|
|
450
|
-
labelNode.processInitialChildren();
|
|
451
|
-
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
452
|
-
const layout = labelNode.node.getComputedLayout();
|
|
453
|
-
labelNode.render(ctx, pointX - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
454
|
-
}
|
|
455
|
-
else if (chartOptions.renderLabelItem) {
|
|
456
|
-
const labelNode = chartOptions.renderLabelItem({ item: label, index });
|
|
409
|
+
const displayLabel = chartOptions.xAxisLabelFormatter ? await chartOptions.xAxisLabelFormatter(label, index) : label;
|
|
410
|
+
if (chartOptions.renderLabelItem) {
|
|
411
|
+
const labelNode = await chartOptions.renderLabelItem({ item: label, index });
|
|
457
412
|
if (labelNode) {
|
|
458
413
|
labelNode.processInitialChildren();
|
|
459
414
|
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
460
415
|
const layout = labelNode.node.getComputedLayout();
|
|
461
|
-
labelNode.render(ctx, pointX - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
416
|
+
await labelNode.render(ctx, pointX - layout.width / 2, chartY + finalChartHeight + labelHeight / 2 - layout.height / 2);
|
|
462
417
|
}
|
|
463
418
|
}
|
|
464
419
|
else {
|
|
@@ -470,13 +425,13 @@ class ChartNode extends BoxNode {
|
|
|
470
425
|
textBaseline: 'middle',
|
|
471
426
|
});
|
|
472
427
|
}
|
|
473
|
-
}
|
|
428
|
+
}
|
|
474
429
|
}
|
|
475
430
|
if (chartOptions?.showLegend) {
|
|
476
|
-
this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
431
|
+
await this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
477
432
|
}
|
|
478
433
|
}
|
|
479
|
-
renderPieChart(ctx, x, y, width, height) {
|
|
434
|
+
async renderPieChart(ctx, x, y, width, height) {
|
|
480
435
|
if (this.chartType !== 'pie')
|
|
481
436
|
return;
|
|
482
437
|
const data = this.chartData;
|
|
@@ -491,7 +446,8 @@ class ChartNode extends BoxNode {
|
|
|
491
446
|
const radius = Math.min(chartWidth, chartHeight) / 2 - 10;
|
|
492
447
|
const total = data.reduce((sum, point) => sum + point.value, 0);
|
|
493
448
|
let currentAngle = -Math.PI / 2; // Start at top
|
|
494
|
-
data.
|
|
449
|
+
for (let index = 0; index < data.length; index++) {
|
|
450
|
+
const point = data[index];
|
|
495
451
|
const sliceAngle = (point.value / total) * Math.PI * 2;
|
|
496
452
|
const startAngle = currentAngle;
|
|
497
453
|
const endAngle = currentAngle + sliceAngle;
|
|
@@ -511,21 +467,13 @@ class ChartNode extends BoxNode {
|
|
|
511
467
|
const labelRadius = radius * 0.7;
|
|
512
468
|
const labelX = centerX + Math.cos(labelAngle) * labelRadius;
|
|
513
469
|
const labelY = centerY + Math.sin(labelAngle) * labelRadius;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const labelNode = buildDescriptorTree(preComputedLabelDesc);
|
|
517
|
-
labelNode.processInitialChildren();
|
|
518
|
-
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
519
|
-
const layout = labelNode.node.getComputedLayout();
|
|
520
|
-
labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
521
|
-
}
|
|
522
|
-
else if (chartOptions.renderLabelItem) {
|
|
523
|
-
const labelNode = chartOptions.renderLabelItem({ item: point, index });
|
|
470
|
+
if (chartOptions.renderLabelItem) {
|
|
471
|
+
const labelNode = await chartOptions.renderLabelItem({ item: point, index });
|
|
524
472
|
if (labelNode) {
|
|
525
473
|
labelNode.processInitialChildren();
|
|
526
474
|
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
527
475
|
const layout = labelNode.node.getComputedLayout();
|
|
528
|
-
labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
476
|
+
await labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
529
477
|
}
|
|
530
478
|
}
|
|
531
479
|
else {
|
|
@@ -539,12 +487,12 @@ class ChartNode extends BoxNode {
|
|
|
539
487
|
}
|
|
540
488
|
}
|
|
541
489
|
currentAngle = endAngle;
|
|
542
|
-
}
|
|
490
|
+
}
|
|
543
491
|
if (chartOptions?.showLegend) {
|
|
544
|
-
this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
492
|
+
await this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
545
493
|
}
|
|
546
494
|
}
|
|
547
|
-
renderDoughnutChart(ctx, x, y, width, height) {
|
|
495
|
+
async renderDoughnutChart(ctx, x, y, width, height) {
|
|
548
496
|
if (this.chartType !== 'doughnut')
|
|
549
497
|
return;
|
|
550
498
|
const data = this.chartData;
|
|
@@ -560,7 +508,8 @@ class ChartNode extends BoxNode {
|
|
|
560
508
|
const innerRadius = outerRadius * (chartOptions?.innerRadius ?? 0.6);
|
|
561
509
|
const total = data.reduce((sum, point) => sum + point.value, 0);
|
|
562
510
|
let currentAngle = -Math.PI / 2;
|
|
563
|
-
data.
|
|
511
|
+
for (let index = 0; index < data.length; index++) {
|
|
512
|
+
const point = data[index];
|
|
564
513
|
const sliceAngle = (point.value / total) * Math.PI * 2;
|
|
565
514
|
const startAngle = currentAngle;
|
|
566
515
|
const endAngle = currentAngle + sliceAngle;
|
|
@@ -579,21 +528,13 @@ class ChartNode extends BoxNode {
|
|
|
579
528
|
const labelRadius = innerRadius + (outerRadius - innerRadius) / 2;
|
|
580
529
|
const labelX = centerX + Math.cos(labelAngle) * labelRadius;
|
|
581
530
|
const labelY = centerY + Math.sin(labelAngle) * labelRadius;
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const labelNode = buildDescriptorTree(preComputedLabelDesc);
|
|
585
|
-
labelNode.processInitialChildren();
|
|
586
|
-
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
587
|
-
const layout = labelNode.node.getComputedLayout();
|
|
588
|
-
labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
589
|
-
}
|
|
590
|
-
else if (chartOptions.renderLabelItem) {
|
|
591
|
-
const labelNode = chartOptions.renderLabelItem({ item: point, index });
|
|
531
|
+
if (chartOptions.renderLabelItem) {
|
|
532
|
+
const labelNode = await chartOptions.renderLabelItem({ item: point, index });
|
|
592
533
|
if (labelNode) {
|
|
593
534
|
labelNode.processInitialChildren();
|
|
594
535
|
labelNode.node.calculateLayout(undefined, undefined, Style.Direction.LTR);
|
|
595
536
|
const layout = labelNode.node.getComputedLayout();
|
|
596
|
-
labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
537
|
+
await labelNode.render(ctx, labelX - layout.width / 2, labelY - layout.height / 2);
|
|
597
538
|
}
|
|
598
539
|
}
|
|
599
540
|
else {
|
|
@@ -607,48 +548,31 @@ class ChartNode extends BoxNode {
|
|
|
607
548
|
}
|
|
608
549
|
}
|
|
609
550
|
currentAngle = endAngle;
|
|
610
|
-
}
|
|
551
|
+
}
|
|
611
552
|
if (chartOptions?.showLegend) {
|
|
612
|
-
this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
553
|
+
await this.renderLegend(ctx, x + legendLayout.x, y + legendLayout.y, legendLayout.width, legendLayout.height);
|
|
613
554
|
}
|
|
614
555
|
}
|
|
615
|
-
renderLegend(ctx, x, y, width, height) {
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
const finalNodes = _preComputedLegendItems.filter((desc) => !!desc).map(desc => buildDescriptorTree(desc));
|
|
619
|
-
if (finalNodes.length > 0) {
|
|
620
|
-
const legendContainer = new RowNode({
|
|
621
|
-
children: finalNodes,
|
|
622
|
-
width,
|
|
623
|
-
height,
|
|
624
|
-
justifyContent: Style.Justify.Center,
|
|
625
|
-
alignItems: Style.Align.Center,
|
|
626
|
-
flexWrap: Style.Wrap.Wrap,
|
|
627
|
-
gap: 10,
|
|
628
|
-
});
|
|
629
|
-
legendContainer.processInitialChildren();
|
|
630
|
-
legendContainer.node.calculateLayout(width, height, Style.Direction.LTR);
|
|
631
|
-
legendContainer.render(ctx, x, y);
|
|
632
|
-
}
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
556
|
+
async renderLegend(ctx, x, y, width, height) {
|
|
557
|
+
const chartOptions = this.chartOptions;
|
|
558
|
+
const renderLegendItem = chartOptions?.renderLegendItem;
|
|
635
559
|
if (renderLegendItem) {
|
|
636
560
|
let legendNodes;
|
|
637
561
|
if (this.chartType === 'bar' || this.chartType === 'line') {
|
|
638
562
|
const items = this.chartData.datasets;
|
|
639
563
|
const render = renderLegendItem;
|
|
640
|
-
legendNodes = items.map((item, index) => {
|
|
564
|
+
legendNodes = await Promise.all(items.map(async (item, index) => {
|
|
641
565
|
const color = item.color || this.generateColor(index);
|
|
642
566
|
return render({ item, index, color });
|
|
643
|
-
});
|
|
567
|
+
}));
|
|
644
568
|
}
|
|
645
569
|
else {
|
|
646
570
|
const items = this.chartData;
|
|
647
571
|
const render = renderLegendItem;
|
|
648
|
-
legendNodes = items.map((item, index) => {
|
|
572
|
+
legendNodes = await Promise.all(items.map(async (item, index) => {
|
|
649
573
|
const color = item.color || this.generateColor(index);
|
|
650
574
|
return render({ item, index, color });
|
|
651
|
-
});
|
|
575
|
+
}));
|
|
652
576
|
}
|
|
653
577
|
const finalNodes = legendNodes.filter((node) => !!node);
|
|
654
578
|
if (finalNodes.length > 0) {
|
|
@@ -663,11 +587,13 @@ class ChartNode extends BoxNode {
|
|
|
663
587
|
});
|
|
664
588
|
legendContainer.processInitialChildren();
|
|
665
589
|
legendContainer.node.calculateLayout(width, height, Style.Direction.LTR);
|
|
666
|
-
legendContainer.render(ctx, x, y);
|
|
590
|
+
await legendContainer.render(ctx, x, y);
|
|
667
591
|
}
|
|
668
592
|
return;
|
|
669
593
|
}
|
|
670
594
|
// Fallback to default rendering if renderLegendItem is not provided
|
|
595
|
+
if (!this.chartData)
|
|
596
|
+
return;
|
|
671
597
|
const legendItems = 'datasets' in this.chartData
|
|
672
598
|
? this.chartData.datasets.map(d => ({ label: d.label, value: d.data.reduce((a, b) => a + b, 0) }))
|
|
673
599
|
: this.chartData;
|
|
@@ -677,7 +603,7 @@ class ChartNode extends BoxNode {
|
|
|
677
603
|
const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
|
|
678
604
|
const itemHeight = Math.ceil(textHeight + 8);
|
|
679
605
|
const boxSize = Math.min(15, itemHeight - 2);
|
|
680
|
-
const position = this.chartOptions
|
|
606
|
+
const position = this.chartOptions?.legendPosition;
|
|
681
607
|
if (position === 'top' || position === 'bottom') {
|
|
682
608
|
const itemPadding = 20; // horizontal padding between items
|
|
683
609
|
const rows = [];
|
|
@@ -203,8 +203,8 @@ class ImageNode extends BoxNode {
|
|
|
203
203
|
* Renders the image with correct sizing, clipping, and positioning.
|
|
204
204
|
* Handles object-fit, object-position, and visual effects like saturation.
|
|
205
205
|
*/
|
|
206
|
-
_renderContent(ctx, x, y, width, height) {
|
|
207
|
-
super._renderContent(ctx, x, y, width, height);
|
|
206
|
+
async _renderContent(ctx, x, y, width, height) {
|
|
207
|
+
await super._renderContent(ctx, x, y, width, height);
|
|
208
208
|
if (!this.loadedImage || width <= 0 || height <= 0)
|
|
209
209
|
return;
|
|
210
210
|
const img = this.loadedImage;
|
|
@@ -95,9 +95,12 @@ class BoxNode {
|
|
|
95
95
|
'maxLines',
|
|
96
96
|
'fontVariant',
|
|
97
97
|
];
|
|
98
|
+
const initialPropsRec = this.initialProps;
|
|
99
|
+
const parentPropsRec = parentProps;
|
|
100
|
+
const propsRec = this.props;
|
|
98
101
|
for (const key of inheritableKeys) {
|
|
99
|
-
if (
|
|
100
|
-
|
|
102
|
+
if (initialPropsRec[key] === undefined && parentPropsRec[key] !== undefined) {
|
|
103
|
+
propsRec[key] = parentPropsRec[key];
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
if (!this.node.isDirty()) {
|
|
@@ -198,11 +201,12 @@ class BoxNode {
|
|
|
198
201
|
else {
|
|
199
202
|
for (const [edge, value] of Object.entries(position)) {
|
|
200
203
|
if (edge in Style.Edge) {
|
|
204
|
+
const edgeKey = edge;
|
|
201
205
|
if (typeof value === 'string' && value.endsWith('%')) {
|
|
202
|
-
this.node.setPositionPercent(Style.Edge[
|
|
206
|
+
this.node.setPositionPercent(Style.Edge[edgeKey], parseFloat(value));
|
|
203
207
|
}
|
|
204
208
|
else {
|
|
205
|
-
this.node.setPosition(Style.Edge[
|
|
209
|
+
this.node.setPosition(Style.Edge[edgeKey], value);
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
212
|
}
|
|
@@ -218,11 +222,12 @@ class BoxNode {
|
|
|
218
222
|
else {
|
|
219
223
|
for (const [gutter, value] of Object.entries(gap)) {
|
|
220
224
|
if (gutter in Style.Gutter) {
|
|
225
|
+
const gutterKey = gutter;
|
|
221
226
|
if (typeof value === 'string' && value.endsWith('%')) {
|
|
222
|
-
this.node.setGapPercent(Style.Gutter[
|
|
227
|
+
this.node.setGapPercent(Style.Gutter[gutterKey], parseFloat(value));
|
|
223
228
|
}
|
|
224
229
|
else {
|
|
225
|
-
this.node.setGap(Style.Gutter[
|
|
230
|
+
this.node.setGap(Style.Gutter[gutterKey], value);
|
|
226
231
|
}
|
|
227
232
|
}
|
|
228
233
|
}
|
|
@@ -238,11 +243,12 @@ class BoxNode {
|
|
|
238
243
|
else {
|
|
239
244
|
for (const [edge, value] of Object.entries(margin)) {
|
|
240
245
|
if (edge in Style.Edge) {
|
|
246
|
+
const edgeKey = edge;
|
|
241
247
|
if (typeof value === 'string' && value.endsWith('%')) {
|
|
242
|
-
this.node.setMarginPercent(Style.Edge[
|
|
248
|
+
this.node.setMarginPercent(Style.Edge[edgeKey], parseFloat(value));
|
|
243
249
|
}
|
|
244
250
|
else {
|
|
245
|
-
this.node.setMargin(Style.Edge[
|
|
251
|
+
this.node.setMargin(Style.Edge[edgeKey], value);
|
|
246
252
|
}
|
|
247
253
|
}
|
|
248
254
|
}
|
|
@@ -258,11 +264,12 @@ class BoxNode {
|
|
|
258
264
|
else {
|
|
259
265
|
for (const [edge, value] of Object.entries(padding)) {
|
|
260
266
|
if (edge in Style.Edge) {
|
|
267
|
+
const edgeKey = edge;
|
|
261
268
|
if (typeof value === 'string' && value.endsWith('%')) {
|
|
262
|
-
this.node.setPaddingPercent(Style.Edge[
|
|
269
|
+
this.node.setPaddingPercent(Style.Edge[edgeKey], parseFloat(value));
|
|
263
270
|
}
|
|
264
271
|
else {
|
|
265
|
-
this.node.setPadding(Style.Edge[
|
|
272
|
+
this.node.setPadding(Style.Edge[edgeKey], value);
|
|
266
273
|
}
|
|
267
274
|
}
|
|
268
275
|
}
|
|
@@ -299,7 +306,7 @@ class BoxNode {
|
|
|
299
306
|
* @param {number} offsetX X offset for rendering.
|
|
300
307
|
* @param {number} offsetY Y offset for rendering.
|
|
301
308
|
*/
|
|
302
|
-
render(ctx, offsetX = 0, offsetY = 0) {
|
|
309
|
+
async render(ctx, offsetX = 0, offsetY = 0) {
|
|
303
310
|
const layout = this.node.getComputedLayout();
|
|
304
311
|
const x = layout.left + offsetX;
|
|
305
312
|
const y = layout.top + offsetY;
|
|
@@ -354,7 +361,7 @@ class BoxNode {
|
|
|
354
361
|
// --- End Transformation Setup ---
|
|
355
362
|
// --- Step 1: Render Parent Background/Borders/Content ---
|
|
356
363
|
// This renders the current node's own visual appearance first.
|
|
357
|
-
this._renderContent(ctx, x, y, width, height);
|
|
364
|
+
await this._renderContent(ctx, x, y, width, height);
|
|
358
365
|
// --- Step 2: Prepare Children for Stacking ---
|
|
359
366
|
const positionedChildren = [];
|
|
360
367
|
const inFlowChildren = [];
|
|
@@ -411,19 +418,19 @@ class BoxNode {
|
|
|
411
418
|
for (const item of positionedChildren) {
|
|
412
419
|
if (item.zIndex < 0) {
|
|
413
420
|
// Pass parent's layout origin (x, y) as offset
|
|
414
|
-
item.node.render(ctx, x, y);
|
|
421
|
+
await item.node.render(ctx, x, y);
|
|
415
422
|
}
|
|
416
423
|
}
|
|
417
424
|
// 4b: Render in-flow children (recursively)
|
|
418
425
|
for (const child of inFlowChildren) {
|
|
419
426
|
// Pass parent's layout origin (x, y) as offset
|
|
420
|
-
child.render(ctx, x, y);
|
|
427
|
+
await child.render(ctx, x, y);
|
|
421
428
|
}
|
|
422
429
|
// 4c: Render positioned children with zero or positive zIndex
|
|
423
430
|
for (const item of positionedChildren) {
|
|
424
431
|
if (item.zIndex >= 0) {
|
|
425
432
|
// Pass parent's layout origin (x, y) as offset
|
|
426
|
-
item.node.render(ctx, x, y);
|
|
433
|
+
await item.node.render(ctx, x, y);
|
|
427
434
|
}
|
|
428
435
|
}
|
|
429
436
|
// --- End Child Rendering ---
|
|
@@ -455,7 +462,7 @@ class BoxNode {
|
|
|
455
462
|
* @param width The width of the content area to render
|
|
456
463
|
* @param height The height of the content area to render
|
|
457
464
|
*/
|
|
458
|
-
_renderContent(ctx, x, y, width, height) {
|
|
465
|
+
async _renderContent(ctx, x, y, width, height) {
|
|
459
466
|
// Calculate border radius values for all corners
|
|
460
467
|
const radii = { TopLeft: 0, TopRight: 0, BottomRight: 0, BottomLeft: 0 };
|
|
461
468
|
if (this.props.borderRadius) {
|