@internetstiftelsen/charts 0.7.1 → 0.9.0
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/README.md +54 -8
- package/area.d.ts +0 -1
- package/area.js +2 -19
- package/bar.d.ts +0 -1
- package/bar.js +64 -136
- package/base-chart.d.ts +54 -5
- package/base-chart.js +260 -73
- package/donut-chart.d.ts +7 -9
- package/donut-chart.js +51 -131
- package/gauge-chart.d.ts +18 -7
- package/gauge-chart.js +315 -106
- package/line.js +3 -25
- package/package.json +3 -1
- package/pie-chart.d.ts +7 -11
- package/pie-chart.js +30 -153
- package/radial-chart-base.d.ts +25 -0
- package/radial-chart-base.js +77 -0
- package/scale-utils.d.ts +3 -0
- package/scale-utils.js +14 -0
- package/types.d.ts +2 -0
- package/utils.d.ts +7 -0
- package/utils.js +24 -0
- package/word-cloud-chart.d.ts +32 -0
- package/word-cloud-chart.js +199 -0
- package/xy-chart.d.ts +8 -4
- package/xy-chart.js +127 -127
package/base-chart.js
CHANGED
|
@@ -139,12 +139,24 @@ export class BaseChart {
|
|
|
139
139
|
writable: true,
|
|
140
140
|
value: null
|
|
141
141
|
});
|
|
142
|
+
Object.defineProperty(this, "readyPromise", {
|
|
143
|
+
enumerable: true,
|
|
144
|
+
configurable: true,
|
|
145
|
+
writable: true,
|
|
146
|
+
value: Promise.resolve()
|
|
147
|
+
});
|
|
142
148
|
Object.defineProperty(this, "disconnectedLegendContainer", {
|
|
143
149
|
enumerable: true,
|
|
144
150
|
configurable: true,
|
|
145
151
|
writable: true,
|
|
146
152
|
value: null
|
|
147
153
|
});
|
|
154
|
+
Object.defineProperty(this, "renderThemeOverride", {
|
|
155
|
+
enumerable: true,
|
|
156
|
+
configurable: true,
|
|
157
|
+
writable: true,
|
|
158
|
+
value: null
|
|
159
|
+
});
|
|
148
160
|
const normalized = normalizeChartData(config.data);
|
|
149
161
|
ChartValidator.validateData(normalized.data);
|
|
150
162
|
this.sourceData = config.data;
|
|
@@ -156,6 +168,13 @@ export class BaseChart {
|
|
|
156
168
|
this.responsiveConfig = config.responsive;
|
|
157
169
|
this.layoutManager = new LayoutManager(this.theme);
|
|
158
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Adds a component (axis, grid, tooltip, etc.) to the chart
|
|
173
|
+
*/
|
|
174
|
+
addChild(component) {
|
|
175
|
+
this.registerBaseComponent(component);
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
159
178
|
/**
|
|
160
179
|
* Renders the chart to the specified target element
|
|
161
180
|
*/
|
|
@@ -203,8 +222,9 @@ export class BaseChart {
|
|
|
203
222
|
const renderTheme = this.resolveRenderTheme(responsiveOverrides);
|
|
204
223
|
const overrideComponents = this.createOverrideComponents(mergedComponentOverrides);
|
|
205
224
|
const restoreComponents = this.applyComponentOverrides(overrideComponents);
|
|
206
|
-
const restoreTheme = this.
|
|
225
|
+
const restoreTheme = this.applyRenderTheme(renderTheme);
|
|
207
226
|
try {
|
|
227
|
+
this.setReadyPromise(Promise.resolve());
|
|
208
228
|
// Clear and setup SVG
|
|
209
229
|
this.container.innerHTML = '';
|
|
210
230
|
this.svg = create('svg')
|
|
@@ -212,20 +232,34 @@ export class BaseChart {
|
|
|
212
232
|
.attr('height', dimensions.svgHeightAttr)
|
|
213
233
|
.style('display', 'block');
|
|
214
234
|
this.container.appendChild(this.svg.node());
|
|
215
|
-
this.
|
|
235
|
+
const svgNode = this.svg.node();
|
|
236
|
+
if (!svgNode) {
|
|
237
|
+
throw new Error('Failed to initialize chart SVG');
|
|
238
|
+
}
|
|
239
|
+
this.prepareLayout({
|
|
240
|
+
svg: this.svg,
|
|
241
|
+
svgNode,
|
|
242
|
+
});
|
|
216
243
|
// Calculate layout
|
|
217
244
|
const layoutTheme = {
|
|
218
|
-
...this.
|
|
245
|
+
...this.renderTheme,
|
|
219
246
|
width: this.width,
|
|
220
247
|
height: this.height,
|
|
221
248
|
};
|
|
222
249
|
this.layoutManager = new LayoutManager(layoutTheme);
|
|
223
250
|
const components = this.getLayoutComponents();
|
|
224
|
-
|
|
251
|
+
const plotArea = this.layoutManager.calculateLayout(components);
|
|
252
|
+
this.plotArea = plotArea;
|
|
225
253
|
// Create plot group
|
|
226
|
-
|
|
254
|
+
const plotGroup = this.svg.append('g').attr('class', 'chart-plot');
|
|
255
|
+
this.plotGroup = plotGroup;
|
|
227
256
|
// Render chart content
|
|
228
|
-
this.renderChart(
|
|
257
|
+
this.renderChart({
|
|
258
|
+
svg: this.svg,
|
|
259
|
+
svgNode,
|
|
260
|
+
plotGroup,
|
|
261
|
+
plotArea,
|
|
262
|
+
});
|
|
229
263
|
this.renderDisconnectedLegend();
|
|
230
264
|
}
|
|
231
265
|
finally {
|
|
@@ -270,58 +304,84 @@ export class BaseChart {
|
|
|
270
304
|
}
|
|
271
305
|
return mergeDeep(this.theme, responsiveOverrides.theme);
|
|
272
306
|
}
|
|
273
|
-
|
|
307
|
+
applyRenderTheme(theme) {
|
|
274
308
|
if (theme === this.theme) {
|
|
275
309
|
return () => { };
|
|
276
310
|
}
|
|
277
|
-
|
|
278
|
-
this.theme = theme;
|
|
311
|
+
this.renderThemeOverride = theme;
|
|
279
312
|
return () => {
|
|
280
|
-
this.
|
|
313
|
+
this.renderThemeOverride = null;
|
|
281
314
|
};
|
|
282
315
|
}
|
|
316
|
+
get renderTheme() {
|
|
317
|
+
return this.renderThemeOverride ?? this.theme;
|
|
318
|
+
}
|
|
283
319
|
/**
|
|
284
320
|
* Get layout-aware components in order
|
|
285
321
|
* Override in subclasses to provide chart-specific components
|
|
286
322
|
*/
|
|
287
323
|
getLayoutComponents() {
|
|
324
|
+
return this.getBaseLayoutComponents({
|
|
325
|
+
title: true,
|
|
326
|
+
xAxis: true,
|
|
327
|
+
yAxis: true,
|
|
328
|
+
inlineLegend: true,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
getBaseLayoutComponents(options) {
|
|
288
332
|
const components = [];
|
|
289
|
-
if (this.title) {
|
|
333
|
+
if (options.title && this.title) {
|
|
290
334
|
components.push(this.title);
|
|
291
335
|
}
|
|
292
|
-
if (this.xAxis) {
|
|
336
|
+
if (options.xAxis && this.xAxis) {
|
|
293
337
|
components.push(this.xAxis);
|
|
294
338
|
}
|
|
295
|
-
if (this.yAxis) {
|
|
339
|
+
if (options.yAxis && this.yAxis) {
|
|
296
340
|
components.push(this.yAxis);
|
|
297
341
|
}
|
|
298
|
-
if (this.legend?.isInlineMode()) {
|
|
342
|
+
if (options.inlineLegend && this.legend?.isInlineMode()) {
|
|
299
343
|
components.push(this.legend);
|
|
300
344
|
}
|
|
301
345
|
return components;
|
|
302
346
|
}
|
|
303
347
|
getExportComponents() {
|
|
348
|
+
return this.getBaseExportComponents({
|
|
349
|
+
title: true,
|
|
350
|
+
grid: true,
|
|
351
|
+
xAxis: true,
|
|
352
|
+
yAxis: true,
|
|
353
|
+
tooltip: true,
|
|
354
|
+
legend: true,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
getOverrideableComponents() {
|
|
358
|
+
return this.getExportComponents();
|
|
359
|
+
}
|
|
360
|
+
getBaseExportComponents(options) {
|
|
304
361
|
const components = [];
|
|
305
|
-
if (this.title) {
|
|
362
|
+
if (options.title && this.title) {
|
|
306
363
|
components.push(this.title);
|
|
307
364
|
}
|
|
308
|
-
if (this.grid) {
|
|
365
|
+
if (options.grid && this.grid) {
|
|
309
366
|
components.push(this.grid);
|
|
310
367
|
}
|
|
311
|
-
if (this.xAxis) {
|
|
368
|
+
if (options.xAxis && this.xAxis) {
|
|
312
369
|
components.push(this.xAxis);
|
|
313
370
|
}
|
|
314
|
-
if (this.yAxis) {
|
|
371
|
+
if (options.yAxis && this.yAxis) {
|
|
315
372
|
components.push(this.yAxis);
|
|
316
373
|
}
|
|
317
|
-
if (this.tooltip) {
|
|
374
|
+
if (options.tooltip && this.tooltip) {
|
|
318
375
|
components.push(this.tooltip);
|
|
319
376
|
}
|
|
320
|
-
if (this.legend) {
|
|
377
|
+
if (options.legend && this.legend) {
|
|
321
378
|
components.push(this.legend);
|
|
322
379
|
}
|
|
323
380
|
return components;
|
|
324
381
|
}
|
|
382
|
+
registerBaseComponent(component) {
|
|
383
|
+
return this.tryRegisterComponent(component, this.getBaseComponentSlots());
|
|
384
|
+
}
|
|
325
385
|
collectExportOverrides(context) {
|
|
326
386
|
const overrides = new Map();
|
|
327
387
|
const components = this.getExportComponents();
|
|
@@ -339,7 +399,7 @@ export class BaseChart {
|
|
|
339
399
|
}
|
|
340
400
|
collectResponsiveOverrides(context) {
|
|
341
401
|
const beforeRender = this.responsiveConfig?.beforeRender;
|
|
342
|
-
const components = this.
|
|
402
|
+
const components = this.getOverrideableComponents();
|
|
343
403
|
const componentOverrides = new Map();
|
|
344
404
|
if (!beforeRender) {
|
|
345
405
|
return {
|
|
@@ -418,38 +478,7 @@ export class BaseChart {
|
|
|
418
478
|
return overrideComponents;
|
|
419
479
|
}
|
|
420
480
|
applyComponentOverrides(overrides) {
|
|
421
|
-
|
|
422
|
-
return () => { };
|
|
423
|
-
}
|
|
424
|
-
const previousState = {
|
|
425
|
-
title: this.title,
|
|
426
|
-
grid: this.grid,
|
|
427
|
-
xAxis: this.xAxis,
|
|
428
|
-
yAxis: this.yAxis,
|
|
429
|
-
tooltip: this.tooltip,
|
|
430
|
-
legend: this.legend,
|
|
431
|
-
};
|
|
432
|
-
const resolve = (component) => {
|
|
433
|
-
if (!component) {
|
|
434
|
-
return component;
|
|
435
|
-
}
|
|
436
|
-
const override = overrides.get(component);
|
|
437
|
-
return override ?? component;
|
|
438
|
-
};
|
|
439
|
-
this.title = resolve(this.title);
|
|
440
|
-
this.grid = resolve(this.grid);
|
|
441
|
-
this.xAxis = resolve(this.xAxis);
|
|
442
|
-
this.yAxis = resolve(this.yAxis);
|
|
443
|
-
this.tooltip = resolve(this.tooltip);
|
|
444
|
-
this.legend = resolve(this.legend);
|
|
445
|
-
return () => {
|
|
446
|
-
this.title = previousState.title;
|
|
447
|
-
this.grid = previousState.grid;
|
|
448
|
-
this.xAxis = previousState.xAxis;
|
|
449
|
-
this.yAxis = previousState.yAxis;
|
|
450
|
-
this.tooltip = previousState.tooltip;
|
|
451
|
-
this.legend = previousState.legend;
|
|
452
|
-
};
|
|
481
|
+
return this.applySlotOverrides(overrides, this.getBaseComponentSlots());
|
|
453
482
|
}
|
|
454
483
|
renderExportChart(chart, width, height) {
|
|
455
484
|
const container = document.createElement('div');
|
|
@@ -465,18 +494,59 @@ export class BaseChart {
|
|
|
465
494
|
container.style.visibility = 'hidden';
|
|
466
495
|
document.body.appendChild(container);
|
|
467
496
|
chart.render(`#${containerId}`);
|
|
468
|
-
|
|
469
|
-
|
|
497
|
+
return chart
|
|
498
|
+
.whenReady()
|
|
499
|
+
.then(() => {
|
|
500
|
+
const svg = chart.svg?.node();
|
|
501
|
+
if (!svg) {
|
|
502
|
+
throw new Error('Failed to render export SVG');
|
|
503
|
+
}
|
|
504
|
+
return svg;
|
|
505
|
+
})
|
|
506
|
+
.finally(() => {
|
|
470
507
|
chart.destroy();
|
|
471
508
|
document.body.removeChild(container);
|
|
472
|
-
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
renderTitle(svg) {
|
|
512
|
+
if (!this.title) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const position = this.layoutManager.getComponentPosition(this.title);
|
|
516
|
+
this.title.render(svg, this.renderTheme, this.width, position.x, position.y);
|
|
517
|
+
}
|
|
518
|
+
renderInlineLegend(svg) {
|
|
519
|
+
if (!this.legend?.isInlineMode()) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const position = this.layoutManager.getComponentPosition(this.legend);
|
|
523
|
+
this.legend.render(svg, this.getLegendSeries(), this.renderTheme, this.width, position.x, position.y);
|
|
524
|
+
}
|
|
525
|
+
measureInlineLegend(svgNode) {
|
|
526
|
+
if (!this.legend?.isInlineMode()) {
|
|
527
|
+
return;
|
|
473
528
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
529
|
+
this.legend.estimateLayoutSpace(this.getLegendSeries(), this.renderTheme, this.width, svgNode);
|
|
530
|
+
}
|
|
531
|
+
filterVisibleItems(items, getDataKey) {
|
|
532
|
+
const { legend } = this;
|
|
533
|
+
if (!legend) {
|
|
534
|
+
return items;
|
|
535
|
+
}
|
|
536
|
+
return items.filter((item) => {
|
|
537
|
+
return legend.isSeriesVisible(getDataKey(item));
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
validateSourceData(_data) { }
|
|
541
|
+
syncDerivedState(_previousData) { }
|
|
542
|
+
initializeDataState() {
|
|
543
|
+
this.validateSourceData(this.sourceData);
|
|
544
|
+
this.syncDerivedState();
|
|
477
545
|
}
|
|
478
546
|
// Hook for subclasses to update component layout estimates before layout calc
|
|
479
|
-
prepareLayout() {
|
|
547
|
+
prepareLayout(context) {
|
|
548
|
+
this.measureInlineLegend(context.svgNode);
|
|
549
|
+
}
|
|
480
550
|
/**
|
|
481
551
|
* Setup ResizeObserver for automatic resize handling
|
|
482
552
|
*/
|
|
@@ -486,9 +556,15 @@ export class BaseChart {
|
|
|
486
556
|
if (this.resizeObserver) {
|
|
487
557
|
this.resizeObserver.disconnect();
|
|
488
558
|
}
|
|
489
|
-
this.resizeObserver = new ResizeObserver(() => this.
|
|
559
|
+
this.resizeObserver = new ResizeObserver(() => this.rerender());
|
|
490
560
|
this.resizeObserver.observe(this.container);
|
|
491
561
|
}
|
|
562
|
+
setReadyPromise(promise) {
|
|
563
|
+
this.readyPromise = promise;
|
|
564
|
+
}
|
|
565
|
+
whenReady() {
|
|
566
|
+
return this.readyPromise;
|
|
567
|
+
}
|
|
492
568
|
getLegendSeries() {
|
|
493
569
|
return [];
|
|
494
570
|
}
|
|
@@ -532,14 +608,17 @@ export class BaseChart {
|
|
|
532
608
|
* Updates the chart with new data
|
|
533
609
|
*/
|
|
534
610
|
update(data) {
|
|
611
|
+
if (!this.container) {
|
|
612
|
+
throw new Error('Chart must be rendered before update()');
|
|
613
|
+
}
|
|
614
|
+
this.validateSourceData(data);
|
|
535
615
|
const normalized = normalizeChartData(data);
|
|
536
616
|
ChartValidator.validateData(normalized.data);
|
|
617
|
+
const previousData = this.data;
|
|
537
618
|
this.sourceData = data;
|
|
538
619
|
this.data = normalized.data;
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
this.performRender();
|
|
620
|
+
this.syncDerivedState(previousData);
|
|
621
|
+
this.rerender();
|
|
543
622
|
}
|
|
544
623
|
/**
|
|
545
624
|
* Destroys the chart and cleans up resources
|
|
@@ -588,10 +667,10 @@ export class BaseChart {
|
|
|
588
667
|
if (!svgNode) {
|
|
589
668
|
return;
|
|
590
669
|
}
|
|
591
|
-
this.legend.estimateLayoutSpace(series, this.
|
|
670
|
+
this.legend.estimateLayoutSpace(series, this.renderTheme, this.width, svgNode);
|
|
592
671
|
const legendHeight = Math.max(1, this.legend.getMeasuredHeight());
|
|
593
672
|
legendSvg.attr('height', legendHeight);
|
|
594
|
-
this.legend.render(legendSvg, series, this.
|
|
673
|
+
this.legend.render(legendSvg, series, this.renderTheme, this.width);
|
|
595
674
|
}
|
|
596
675
|
resolveDisconnectedLegendHost() {
|
|
597
676
|
if (!this.legend || !this.container) {
|
|
@@ -638,6 +717,109 @@ export class BaseChart {
|
|
|
638
717
|
}
|
|
639
718
|
return 0;
|
|
640
719
|
}
|
|
720
|
+
rerender() {
|
|
721
|
+
if (!this.container) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
this.performRender();
|
|
725
|
+
}
|
|
726
|
+
tryRegisterComponent(component, slots) {
|
|
727
|
+
const slot = slots.find((entry) => entry.type === component.type);
|
|
728
|
+
if (!slot) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
slot.set(component);
|
|
732
|
+
slot.onRegister?.(component);
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
applySlotOverrides(overrides, slots) {
|
|
736
|
+
if (overrides.size === 0) {
|
|
737
|
+
return () => { };
|
|
738
|
+
}
|
|
739
|
+
const previousState = new Map();
|
|
740
|
+
slots.forEach((slot) => {
|
|
741
|
+
previousState.set(slot, slot.get());
|
|
742
|
+
const current = slot.get();
|
|
743
|
+
if (!current) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const override = overrides.get(current);
|
|
747
|
+
if (override) {
|
|
748
|
+
slot.set(override);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
return () => {
|
|
752
|
+
slots.forEach((slot) => {
|
|
753
|
+
slot.set(previousState.get(slot) ?? null);
|
|
754
|
+
});
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
applyArrayComponentOverrides(components, overrides, isComponent) {
|
|
758
|
+
if (overrides.size === 0) {
|
|
759
|
+
return () => { };
|
|
760
|
+
}
|
|
761
|
+
const previousState = [...components];
|
|
762
|
+
components.forEach((component, index) => {
|
|
763
|
+
const override = overrides.get(component);
|
|
764
|
+
if (override && isComponent(override)) {
|
|
765
|
+
components[index] = override;
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
return () => {
|
|
769
|
+
components.splice(0, components.length, ...previousState);
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
getBaseComponentSlots() {
|
|
773
|
+
return [
|
|
774
|
+
{
|
|
775
|
+
type: 'title',
|
|
776
|
+
get: () => this.title,
|
|
777
|
+
set: (component) => {
|
|
778
|
+
this.title = component;
|
|
779
|
+
},
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
type: 'grid',
|
|
783
|
+
get: () => this.grid,
|
|
784
|
+
set: (component) => {
|
|
785
|
+
this.grid = component;
|
|
786
|
+
},
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
type: 'xAxis',
|
|
790
|
+
get: () => this.xAxis,
|
|
791
|
+
set: (component) => {
|
|
792
|
+
this.xAxis = component;
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
type: 'yAxis',
|
|
797
|
+
get: () => this.yAxis,
|
|
798
|
+
set: (component) => {
|
|
799
|
+
this.yAxis = component;
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
type: 'tooltip',
|
|
804
|
+
get: () => this.tooltip,
|
|
805
|
+
set: (component) => {
|
|
806
|
+
this.tooltip = component;
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
type: 'legend',
|
|
811
|
+
get: () => this.legend,
|
|
812
|
+
set: (component) => {
|
|
813
|
+
this.legend = component;
|
|
814
|
+
},
|
|
815
|
+
onRegister: (component) => {
|
|
816
|
+
component.setToggleCallback(() => {
|
|
817
|
+
this.rerender();
|
|
818
|
+
});
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
];
|
|
822
|
+
}
|
|
641
823
|
/**
|
|
642
824
|
* Exports the chart in the specified format
|
|
643
825
|
* @param format - The export format
|
|
@@ -647,7 +829,7 @@ export class BaseChart {
|
|
|
647
829
|
async export(format, options) {
|
|
648
830
|
let content;
|
|
649
831
|
if (format === 'svg') {
|
|
650
|
-
content = this.exportSVG(options, 'svg');
|
|
832
|
+
content = await this.exportSVG(options, 'svg');
|
|
651
833
|
}
|
|
652
834
|
else if (format === 'json') {
|
|
653
835
|
content = this.exportJSON();
|
|
@@ -723,7 +905,7 @@ export class BaseChart {
|
|
|
723
905
|
}
|
|
724
906
|
async exportImage(format, options) {
|
|
725
907
|
const { width, height } = this.exportSize(options);
|
|
726
|
-
const svg = this.exportSVG(options, format);
|
|
908
|
+
const svg = await this.exportSVG(options, format);
|
|
727
909
|
const backgroundColor = options?.backgroundColor ??
|
|
728
910
|
(format === 'jpg' ? '#ffffff' : undefined);
|
|
729
911
|
return exportRasterBlob({
|
|
@@ -738,7 +920,7 @@ export class BaseChart {
|
|
|
738
920
|
}
|
|
739
921
|
async exportPDF(options) {
|
|
740
922
|
const { width, height } = this.exportSize(options);
|
|
741
|
-
const svg = this.exportSVG(options, 'pdf');
|
|
923
|
+
const svg = await this.exportSVG(options, 'pdf');
|
|
742
924
|
const pngBlob = await exportRasterBlob({
|
|
743
925
|
format: 'png',
|
|
744
926
|
svg,
|
|
@@ -754,14 +936,19 @@ export class BaseChart {
|
|
|
754
936
|
margin: options?.pdfMargin ?? 0,
|
|
755
937
|
});
|
|
756
938
|
}
|
|
757
|
-
exportSVG(options, formatForHooks = 'svg') {
|
|
939
|
+
async exportSVG(options, formatForHooks = 'svg') {
|
|
758
940
|
if (!this.svg) {
|
|
759
941
|
throw new Error('Chart must be rendered before export');
|
|
760
942
|
}
|
|
943
|
+
await this.whenReady();
|
|
944
|
+
const liveSvg = this.svg?.node();
|
|
945
|
+
if (!liveSvg) {
|
|
946
|
+
throw new Error('Chart must remain mounted until export completes');
|
|
947
|
+
}
|
|
761
948
|
const exportWidth = options?.width ?? this.width;
|
|
762
949
|
const exportHeight = options?.height ?? this.height;
|
|
763
950
|
const requiresExportRender = exportWidth !== this.width || exportHeight !== this.height;
|
|
764
|
-
const clone =
|
|
951
|
+
const clone = liveSvg.cloneNode(true);
|
|
765
952
|
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
766
953
|
clone.setAttribute('width', String(exportWidth));
|
|
767
954
|
clone.setAttribute('height', String(exportHeight));
|
|
@@ -791,7 +978,7 @@ export class BaseChart {
|
|
|
791
978
|
exportChart.addChild(component);
|
|
792
979
|
}
|
|
793
980
|
});
|
|
794
|
-
const exportSvg = this.renderExportChart(exportChart, exportWidth, exportHeight);
|
|
981
|
+
const exportSvg = await this.renderExportChart(exportChart, exportWidth, exportHeight);
|
|
795
982
|
exportSvg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
796
983
|
exportSvg.setAttribute('width', String(exportWidth));
|
|
797
984
|
exportSvg.setAttribute('height', String(exportHeight));
|
package/donut-chart.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DataItem, LegendSeries } from './types.js';
|
|
2
|
-
import {
|
|
3
|
-
import type { ChartComponent
|
|
2
|
+
import { type BaseChartConfig, type BaseRenderContext } from './base-chart.js';
|
|
3
|
+
import type { ChartComponent } from './chart-interface.js';
|
|
4
|
+
import { RadialChartBase } from './radial-chart-base.js';
|
|
4
5
|
export type DonutConfig = {
|
|
5
6
|
innerRadius?: number;
|
|
6
7
|
padAngle?: number;
|
|
@@ -11,7 +12,7 @@ export type DonutChartConfig = BaseChartConfig & {
|
|
|
11
12
|
valueKey?: string;
|
|
12
13
|
labelKey?: string;
|
|
13
14
|
};
|
|
14
|
-
export declare class DonutChart extends
|
|
15
|
+
export declare class DonutChart extends RadialChartBase {
|
|
15
16
|
private readonly innerRadiusRatio;
|
|
16
17
|
private readonly padAngle;
|
|
17
18
|
private readonly cornerRadius;
|
|
@@ -25,14 +26,11 @@ export declare class DonutChart extends BaseChart {
|
|
|
25
26
|
addChild(component: ChartComponent): this;
|
|
26
27
|
protected getExportComponents(): ChartComponent[];
|
|
27
28
|
update(data: DataItem[]): void;
|
|
28
|
-
protected
|
|
29
|
-
protected prepareLayout(): void;
|
|
30
|
-
protected createExportChart(): BaseChart;
|
|
29
|
+
protected createExportChart(): RadialChartBase;
|
|
31
30
|
protected applyComponentOverrides(overrides: Map<ChartComponent, ChartComponent>): () => void;
|
|
32
|
-
protected
|
|
33
|
-
|
|
31
|
+
protected syncDerivedState(): void;
|
|
32
|
+
protected renderChart({ svg, plotGroup, plotArea, }: BaseRenderContext): void;
|
|
34
33
|
protected getLegendSeries(): LegendSeries[];
|
|
35
|
-
private positionTooltip;
|
|
36
34
|
private buildTooltipContent;
|
|
37
35
|
private renderSegments;
|
|
38
36
|
}
|