@internetstiftelsen/charts 0.8.0 → 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/pie-chart.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { DataItem, LegendSeries } from './types.js';
2
- import { BaseChart, type BaseChartConfig } from './base-chart.js';
3
- import type { ChartComponent, LayoutAwareComponent } from './chart-interface.js';
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 PieSort = 'none' | 'ascending' | 'descending' | ((a: PieSegmentData, b: PieSegmentData) => number);
5
6
  export type PieConfig = {
6
7
  innerRadius?: number;
@@ -40,7 +41,7 @@ type PieSegmentData = {
40
41
  color: string;
41
42
  source: DataItem;
42
43
  };
43
- export declare class PieChart extends BaseChart {
44
+ export declare class PieChart extends RadialChartBase {
44
45
  private readonly innerRadiusRatio;
45
46
  private readonly startAngle;
46
47
  private readonly endAngle;
@@ -55,22 +56,17 @@ export declare class PieChart extends BaseChart {
55
56
  private validatePieData;
56
57
  private prepareSegments;
57
58
  private warnOnTinySlices;
58
- addChild(component: ChartComponent): this;
59
59
  protected getExportComponents(): ChartComponent[];
60
60
  update(data: DataItem[]): void;
61
- protected getLayoutComponents(): LayoutAwareComponent[];
62
- protected prepareLayout(): void;
63
- protected createExportChart(): BaseChart;
64
- protected renderChart(): void;
65
- private resolveFontScale;
61
+ protected createExportChart(): RadialChartBase;
62
+ protected syncDerivedState(): void;
63
+ protected renderChart({ svg, plotGroup, plotArea, }: BaseRenderContext): void;
66
64
  protected getLegendSeries(): LegendSeries[];
67
65
  private resolveSortComparator;
68
66
  private renderSegments;
69
67
  private handleSegmentKeyNavigation;
70
68
  private getRenderedSegments;
71
69
  private buildAriaLabel;
72
- private positionTooltip;
73
- private positionTooltipAtElement;
74
70
  private buildTooltipContent;
75
71
  private getArcPoint;
76
72
  private renderLabels;
package/pie-chart.js CHANGED
@@ -1,16 +1,14 @@
1
1
  import { arc, pie, select } from 'd3';
2
- import { BaseChart } from './base-chart.js';
3
2
  import { ChartValidator } from './validation.js';
4
3
  import { getContrastTextColor, measureTextWidth, sanitizeForCSS, } from './utils.js';
4
+ import { RadialChartBase } from './radial-chart-base.js';
5
5
  const HOVER_EXPAND_PX = 8;
6
6
  const ANIMATION_DURATION_MS = 150;
7
- const TOOLTIP_OFFSET_PX = 12;
8
- const EDGE_MARGIN_PX = 10;
9
7
  const FULL_CIRCLE_RADIANS = Math.PI * 2;
10
8
  const OUTSIDE_LABEL_TEXT_OFFSET_PX = 10;
11
9
  const OUTSIDE_LABEL_LINE_INSET_PX = 4;
12
10
  const TINY_SLICE_THRESHOLD_RATIO = 0.03;
13
- export class PieChart extends BaseChart {
11
+ export class PieChart extends RadialChartBase {
14
12
  constructor(config) {
15
13
  super(config);
16
14
  Object.defineProperty(this, "innerRadiusRatio", {
@@ -99,8 +97,7 @@ export class PieChart extends BaseChart {
99
97
  legacyLabels?.minVerticalSpacing ??
100
98
  14,
101
99
  };
102
- this.validatePieData();
103
- this.prepareSegments();
100
+ this.initializeDataState();
104
101
  }
105
102
  validatePieData() {
106
103
  ChartValidator.validateDataKey(this.data, this.labelKey, 'PieChart');
@@ -176,60 +173,16 @@ export class PieChart extends BaseChart {
176
173
  ChartValidator.warn(`PieChart: ${tinySliceCount} slices are below ${(TINY_SLICE_THRESHOLD_RATIO * 100).toFixed(0)}% and label readability may degrade`);
177
174
  }
178
175
  }
179
- addChild(component) {
180
- const type = component.type;
181
- if (type === 'tooltip') {
182
- this.tooltip = component;
183
- }
184
- else if (type === 'legend') {
185
- this.legend = component;
186
- this.legend.setToggleCallback(() => {
187
- if (!this.container) {
188
- return;
189
- }
190
- this.update(this.data);
191
- });
192
- }
193
- else if (type === 'title') {
194
- this.title = component;
195
- }
196
- return this;
197
- }
198
176
  getExportComponents() {
199
- const components = [];
200
- if (this.title) {
201
- components.push(this.title);
202
- }
203
- if (this.tooltip) {
204
- components.push(this.tooltip);
205
- }
206
- if (this.legend?.isInlineMode()) {
207
- components.push(this.legend);
208
- }
209
- return components;
177
+ return this.getBaseExportComponents({
178
+ title: true,
179
+ tooltip: true,
180
+ legend: this.legend?.isInlineMode(),
181
+ });
210
182
  }
211
183
  update(data) {
212
- this.data = data;
213
- this.validatePieData();
214
- this.prepareSegments();
215
184
  super.update(data);
216
185
  }
217
- getLayoutComponents() {
218
- const components = [];
219
- if (this.title) {
220
- components.push(this.title);
221
- }
222
- if (this.legend) {
223
- components.push(this.legend);
224
- }
225
- return components;
226
- }
227
- prepareLayout() {
228
- const svgNode = this.svg?.node();
229
- if (svgNode && this.legend?.isInlineMode()) {
230
- this.legend.estimateLayoutSpace(this.getLegendSeries(), this.theme, this.width, svgNode);
231
- }
232
- }
233
186
  createExportChart() {
234
187
  return new PieChart({
235
188
  data: this.data,
@@ -248,53 +201,27 @@ export class PieChart extends BaseChart {
248
201
  labelKey: this.labelKey,
249
202
  });
250
203
  }
251
- renderChart() {
252
- if (!this.plotArea || !this.svg || !this.plotGroup) {
253
- throw new Error('Plot area not calculated');
254
- }
255
- if (this.title) {
256
- const pos = this.layoutManager.getComponentPosition(this.title);
257
- this.title.render(this.svg, this.theme, this.width, pos.x, pos.y);
258
- }
259
- const visibleSegments = this.legend
260
- ? this.segments.filter((seg) => this.legend.isSeriesVisible(seg.label))
261
- : this.segments;
262
- if (this.tooltip) {
263
- this.tooltip.initialize(this.theme);
264
- }
204
+ syncDerivedState() {
205
+ this.validatePieData();
206
+ this.prepareSegments();
207
+ }
208
+ renderChart({ svg, plotGroup, plotArea, }) {
209
+ this.renderTitle(svg);
210
+ const visibleSegments = this.getVisibleRadialItems(this.segments);
211
+ this.initializeTooltip();
265
212
  if (visibleSegments.length > 0) {
266
- const cx = this.plotArea.left + this.plotArea.width / 2;
267
- const cy = this.plotArea.top + this.plotArea.height / 2;
268
- const outerRadius = Math.min(this.plotArea.width, this.plotArea.height) / 2;
269
- const innerRadius = outerRadius * this.innerRadiusRatio;
270
- const fontScale = this.resolveFontScale(outerRadius);
271
- const { segmentGroup, pieData } = this.renderSegments(visibleSegments, cx, cy, innerRadius, outerRadius);
213
+ const { cx, cy, outerRadius, innerRadius, fontScale } = this.getRadialLayout(plotArea, this.innerRadiusRatio);
214
+ const { segmentGroup, pieData } = this.renderSegments(plotGroup, visibleSegments, cx, cy, innerRadius, outerRadius);
272
215
  if (this.valueLabel.show) {
273
216
  this.renderLabels(segmentGroup, pieData, innerRadius, outerRadius, visibleSegments.reduce((sum, segment) => {
274
217
  return sum + segment.value;
275
218
  }, 0), fontScale);
276
219
  }
277
220
  }
278
- if (this.legend?.isInlineMode()) {
279
- const pos = this.layoutManager.getComponentPosition(this.legend);
280
- this.legend.render(this.svg, this.getLegendSeries(), this.theme, this.width, pos.x, pos.y);
281
- }
282
- }
283
- resolveFontScale(outerRadius) {
284
- const plotHeight = Math.max(1, this.theme.height -
285
- this.theme.margins.top -
286
- this.theme.margins.bottom);
287
- const referenceRadius = Math.max(1, plotHeight / 2);
288
- const rawScale = outerRadius / referenceRadius;
289
- return Math.max(0.5, Math.min(1, rawScale));
221
+ this.renderInlineLegend(svg);
290
222
  }
291
223
  getLegendSeries() {
292
- return this.segments.map((segment) => {
293
- return {
294
- dataKey: segment.label,
295
- fill: segment.color,
296
- };
297
- });
224
+ return this.getRadialLegendSeries(this.segments);
298
225
  }
299
226
  resolveSortComparator() {
300
227
  if (typeof this.sort === 'function') {
@@ -308,10 +235,7 @@ export class PieChart extends BaseChart {
308
235
  }
309
236
  return null;
310
237
  }
311
- renderSegments(segments, cx, cy, innerRadius, outerRadius) {
312
- if (!this.plotGroup || !this.svg) {
313
- throw new Error('Plot group not initialized');
314
- }
238
+ renderSegments(plotGroup, segments, cx, cy, innerRadius, outerRadius) {
315
239
  const pieGenerator = pie()
316
240
  .value((d) => d.value)
317
241
  .startAngle(this.startAngle)
@@ -333,7 +257,7 @@ export class PieChart extends BaseChart {
333
257
  .outerRadius(outerRadius + HOVER_EXPAND_PX)
334
258
  .cornerRadius(this.cornerRadius);
335
259
  const pieData = pieGenerator(segments);
336
- const segmentGroup = this.plotGroup
260
+ const segmentGroup = plotGroup
337
261
  .append('g')
338
262
  .attr('class', 'pie-segments')
339
263
  .attr('transform', `translate(${cx}, ${cy})`);
@@ -367,13 +291,13 @@ export class PieChart extends BaseChart {
367
291
  tooltipDiv
368
292
  .style('visibility', 'visible')
369
293
  .html(this.buildTooltipContent(d, segments));
370
- this.positionTooltip(event, tooltipDiv);
294
+ this.positionTooltipFromPointer(event, tooltipDiv);
371
295
  }
372
296
  })
373
297
  .on('mousemove', (event) => {
374
298
  const tooltipDiv = resolveTooltipDiv();
375
299
  if (tooltipDiv && !tooltipDiv.empty()) {
376
- this.positionTooltip(event, tooltipDiv);
300
+ this.positionTooltipFromPointer(event, tooltipDiv);
377
301
  }
378
302
  })
379
303
  .on('mouseleave', (event, d) => {
@@ -458,53 +382,6 @@ export class PieChart extends BaseChart {
458
382
  const percentage = total > 0 ? ((d.data.value / total) * 100).toFixed(1) : '0.0';
459
383
  return `${d.data.label}: ${d.data.value} (${percentage}%)`;
460
384
  }
461
- positionTooltip(event, tooltipDiv) {
462
- const node = tooltipDiv.node();
463
- if (!node) {
464
- return;
465
- }
466
- const rect = node.getBoundingClientRect();
467
- let x = event.pageX + TOOLTIP_OFFSET_PX;
468
- let y = event.pageY - rect.height / 2;
469
- if (x + rect.width > window.innerWidth - EDGE_MARGIN_PX) {
470
- x = event.pageX - rect.width - TOOLTIP_OFFSET_PX;
471
- }
472
- x = Math.max(EDGE_MARGIN_PX, x);
473
- y = Math.max(EDGE_MARGIN_PX, Math.min(y, window.innerHeight +
474
- window.scrollY -
475
- rect.height -
476
- EDGE_MARGIN_PX));
477
- tooltipDiv.style('left', `${x}px`).style('top', `${y}px`);
478
- }
479
- positionTooltipAtElement(target, tooltipDiv) {
480
- const node = tooltipDiv.node();
481
- if (!node) {
482
- return;
483
- }
484
- const targetRect = target.getBoundingClientRect();
485
- const tooltipRect = node.getBoundingClientRect();
486
- let x = targetRect.left +
487
- window.scrollX +
488
- targetRect.width / 2 +
489
- TOOLTIP_OFFSET_PX;
490
- let y = targetRect.top +
491
- window.scrollY +
492
- targetRect.height / 2 -
493
- tooltipRect.height / 2;
494
- if (x + tooltipRect.width > window.innerWidth - EDGE_MARGIN_PX) {
495
- x =
496
- targetRect.left +
497
- window.scrollX -
498
- tooltipRect.width -
499
- TOOLTIP_OFFSET_PX;
500
- }
501
- x = Math.max(EDGE_MARGIN_PX, x);
502
- y = Math.max(EDGE_MARGIN_PX, Math.min(y, window.innerHeight +
503
- window.scrollY -
504
- tooltipRect.height -
505
- EDGE_MARGIN_PX));
506
- tooltipDiv.style('left', `${x}px`).style('top', `${y}px`);
507
- }
508
385
  buildTooltipContent(d, segments) {
509
386
  const total = segments.reduce((sum, segment) => sum + segment.value, 0);
510
387
  const percentage = total > 0 ? ((d.data.value / total) * 100).toFixed(1) : '0.0';
@@ -534,8 +411,8 @@ export class PieChart extends BaseChart {
534
411
  const insideArc = arc()
535
412
  .innerRadius(insideLabelRadius)
536
413
  .outerRadius(insideLabelRadius);
537
- const fontSize = this.theme.legend.fontSize * fontScale;
538
- const fontWeight = this.theme.axis.fontWeight ?? 'normal';
414
+ const fontSize = this.renderTheme.legend.fontSize * fontScale;
415
+ const fontWeight = this.renderTheme.axis.fontWeight ?? 'normal';
539
416
  const outsideLabels = [];
540
417
  pieData.forEach((d) => {
541
418
  const percentage = total > 0 ? (d.data.value / total) * 100 : 0;
@@ -549,7 +426,7 @@ export class PieChart extends BaseChart {
549
426
  .attr('y', y)
550
427
  .attr('text-anchor', 'middle')
551
428
  .attr('dominant-baseline', 'middle')
552
- .attr('font-family', this.theme.axis.fontFamily)
429
+ .attr('font-family', this.renderTheme.axis.fontFamily)
553
430
  .attr('font-size', `${fontSize}px`)
554
431
  .attr('font-weight', fontWeight)
555
432
  .attr('fill', getContrastTextColor(d.data.color))
@@ -594,10 +471,10 @@ export class PieChart extends BaseChart {
594
471
  .attr('y', outsideLabel.y)
595
472
  .attr('text-anchor', outsideLabel.textAnchor)
596
473
  .attr('dominant-baseline', 'middle')
597
- .attr('font-family', this.theme.axis.fontFamily)
474
+ .attr('font-family', this.renderTheme.axis.fontFamily)
598
475
  .attr('font-size', `${fontSize}px`)
599
476
  .attr('font-weight', fontWeight)
600
- .attr('fill', this.theme.valueLabel.color)
477
+ .attr('fill', this.renderTheme.valueLabel.color)
601
478
  .text(outsideLabel.datum.data.label);
602
479
  });
603
480
  }
@@ -625,7 +502,7 @@ export class PieChart extends BaseChart {
625
502
  if (!svgNode) {
626
503
  return false;
627
504
  }
628
- const textWidth = measureTextWidth(datum.data.label, fontSize, this.theme.axis.fontFamily, fontWeight, svgNode);
505
+ const textWidth = measureTextWidth(datum.data.label, fontSize, this.renderTheme.axis.fontFamily, fontWeight, svgNode);
629
506
  const angle = Math.max(0, datum.endAngle - datum.startAngle);
630
507
  const availableArcLength = angle * insideLabelRadius - this.valueLabel.insideMargin * 2;
631
508
  const availableRadialThickness = outerRadius - innerRadius - this.valueLabel.insideMargin * 2;
@@ -0,0 +1,25 @@
1
+ import type { Selection } from 'd3';
2
+ import { BaseChart } from './base-chart.js';
3
+ import type { PlotAreaBounds } from './layout-manager.js';
4
+ import type { LegendSeries } from './types.js';
5
+ type RadialLegendItem = {
6
+ label: string;
7
+ color: string;
8
+ };
9
+ export declare abstract class RadialChartBase extends BaseChart {
10
+ protected initializeTooltip(): void;
11
+ protected getVisibleRadialItems<T extends RadialLegendItem>(items: T[]): T[];
12
+ protected getRadialLayout(plotArea: PlotAreaBounds, innerRadiusRatio: number): {
13
+ cx: number;
14
+ cy: number;
15
+ outerRadius: number;
16
+ innerRadius: number;
17
+ fontScale: number;
18
+ };
19
+ protected getRadialLegendSeries<T extends RadialLegendItem>(items: T[]): LegendSeries[];
20
+ protected positionTooltipFromPointer(event: MouseEvent, tooltipDiv: Selection<HTMLDivElement, unknown, HTMLElement, undefined>): void;
21
+ protected positionTooltipAtElement(target: Element, tooltipDiv: Selection<HTMLDivElement, unknown, HTMLElement, undefined>): void;
22
+ private applyTooltipPosition;
23
+ private resolveRadialFontScale;
24
+ }
25
+ export {};
@@ -0,0 +1,77 @@
1
+ import { BaseChart } from './base-chart.js';
2
+ const TOOLTIP_OFFSET_PX = 12;
3
+ const EDGE_MARGIN_PX = 10;
4
+ export class RadialChartBase extends BaseChart {
5
+ initializeTooltip() {
6
+ this.tooltip?.initialize(this.renderTheme);
7
+ }
8
+ getVisibleRadialItems(items) {
9
+ return this.filterVisibleItems(items, (item) => item.label);
10
+ }
11
+ getRadialLayout(plotArea, innerRadiusRatio) {
12
+ const cx = plotArea.left + plotArea.width / 2;
13
+ const cy = plotArea.top + plotArea.height / 2;
14
+ const outerRadius = Math.min(plotArea.width, plotArea.height) / 2;
15
+ return {
16
+ cx,
17
+ cy,
18
+ outerRadius,
19
+ innerRadius: outerRadius * innerRadiusRatio,
20
+ fontScale: this.resolveRadialFontScale(outerRadius, this.renderTheme),
21
+ };
22
+ }
23
+ getRadialLegendSeries(items) {
24
+ return items.map((item) => ({
25
+ dataKey: item.label,
26
+ fill: item.color,
27
+ }));
28
+ }
29
+ positionTooltipFromPointer(event, tooltipDiv) {
30
+ const node = tooltipDiv.node();
31
+ if (!node) {
32
+ return;
33
+ }
34
+ const rect = node.getBoundingClientRect();
35
+ let x = event.pageX + TOOLTIP_OFFSET_PX;
36
+ const y = event.pageY - rect.height / 2;
37
+ if (x + rect.width > window.innerWidth - EDGE_MARGIN_PX) {
38
+ x = event.pageX - rect.width - TOOLTIP_OFFSET_PX;
39
+ }
40
+ this.applyTooltipPosition(tooltipDiv, x, y, rect.height, rect.width);
41
+ }
42
+ positionTooltipAtElement(target, tooltipDiv) {
43
+ const node = tooltipDiv.node();
44
+ if (!node) {
45
+ return;
46
+ }
47
+ const targetRect = target.getBoundingClientRect();
48
+ const tooltipRect = node.getBoundingClientRect();
49
+ let x = targetRect.left +
50
+ window.scrollX +
51
+ targetRect.width / 2 +
52
+ TOOLTIP_OFFSET_PX;
53
+ const y = targetRect.top +
54
+ window.scrollY +
55
+ targetRect.height / 2 -
56
+ tooltipRect.height / 2;
57
+ if (x + tooltipRect.width > window.innerWidth - EDGE_MARGIN_PX) {
58
+ x =
59
+ targetRect.left +
60
+ window.scrollX -
61
+ tooltipRect.width -
62
+ TOOLTIP_OFFSET_PX;
63
+ }
64
+ this.applyTooltipPosition(tooltipDiv, x, y, tooltipRect.height, tooltipRect.width);
65
+ }
66
+ applyTooltipPosition(tooltipDiv, rawX, rawY, height, width) {
67
+ const x = Math.max(EDGE_MARGIN_PX, Math.min(rawX, window.innerWidth + window.scrollX - width - EDGE_MARGIN_PX));
68
+ const y = Math.max(EDGE_MARGIN_PX, Math.min(rawY, window.innerHeight + window.scrollY - height - EDGE_MARGIN_PX));
69
+ tooltipDiv.style('left', `${x}px`).style('top', `${y}px`);
70
+ }
71
+ resolveRadialFontScale(outerRadius, theme) {
72
+ const plotHeight = Math.max(1, theme.height - theme.margins.top - theme.margins.bottom);
73
+ const referenceRadius = Math.max(1, plotHeight / 2);
74
+ const rawScale = outerRadius / referenceRadius;
75
+ return Math.max(0.5, Math.min(1, rawScale));
76
+ }
77
+ }
@@ -0,0 +1,3 @@
1
+ import type { D3Scale, ScaleType } from './types.js';
2
+ export declare function resolveScaleValue(value: unknown, scaleType: ScaleType): string | number | Date;
3
+ export declare function getScalePosition(scale: D3Scale, value: unknown, scaleType: ScaleType): number;
package/scale-utils.js ADDED
@@ -0,0 +1,14 @@
1
+ export function resolveScaleValue(value, scaleType) {
2
+ switch (scaleType) {
3
+ case 'band':
4
+ return String(value);
5
+ case 'time':
6
+ return value instanceof Date ? value : new Date(String(value));
7
+ case 'linear':
8
+ case 'log':
9
+ return typeof value === 'number' ? value : Number(value);
10
+ }
11
+ }
12
+ export function getScalePosition(scale, value, scaleType) {
13
+ return scale(resolveScaleValue(value, scaleType)) || 0;
14
+ }
package/utils.d.ts CHANGED
@@ -11,6 +11,13 @@ export declare function cn(...inputs: ClassValue[]): string;
11
11
  * @returns A valid CSS class/ID string
12
12
  */
13
13
  export declare function sanitizeForCSS(str: string): string;
14
+ /**
15
+ * Escapes a string for safe insertion into HTML content.
16
+ *
17
+ * @param str - The string to escape
18
+ * @returns The escaped HTML string
19
+ */
20
+ export declare function escapeHtml(str: string): string;
14
21
  /**
15
22
  * Measures the width of text in pixels using an SVG text element.
16
23
  *
package/utils.js CHANGED
@@ -20,6 +20,30 @@ export function sanitizeForCSS(str) {
20
20
  .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
21
21
  .toLowerCase(); // Convert to lowercase for consistency
22
22
  }
23
+ /**
24
+ * Escapes a string for safe insertion into HTML content.
25
+ *
26
+ * @param str - The string to escape
27
+ * @returns The escaped HTML string
28
+ */
29
+ export function escapeHtml(str) {
30
+ return str.replace(/[&<>"']/g, (character) => {
31
+ switch (character) {
32
+ case '&':
33
+ return '&amp;';
34
+ case '<':
35
+ return '&lt;';
36
+ case '>':
37
+ return '&gt;';
38
+ case '"':
39
+ return '&quot;';
40
+ case "'":
41
+ return '&#39;';
42
+ default:
43
+ return character;
44
+ }
45
+ });
46
+ }
23
47
  /**
24
48
  * Measures the width of text in pixels using an SVG text element.
25
49
  *
@@ -0,0 +1,32 @@
1
+ import { BaseChart, type BaseChartConfig, type BaseRenderContext } from './base-chart.js';
2
+ import type { ChartData } from './types.js';
3
+ export type WordCloudRotationMode = 'none' | 'right-angle';
4
+ export type WordCloudSpiral = 'archimedean' | 'rectangular';
5
+ export type WordCloudConfig = {
6
+ maxWords?: number;
7
+ minWordLength?: number;
8
+ minValue?: number;
9
+ minFontSize?: number;
10
+ maxFontSize?: number;
11
+ padding?: number;
12
+ rotation?: WordCloudRotationMode;
13
+ spiral?: WordCloudSpiral;
14
+ };
15
+ export type WordCloudChartConfig = Pick<BaseChartConfig, 'data' | 'theme' | 'responsive'> & {
16
+ wordCloud?: WordCloudConfig;
17
+ };
18
+ export declare class WordCloudChart extends BaseChart {
19
+ private readonly options;
20
+ private layout;
21
+ private layoutRunId;
22
+ private resolvePendingReady;
23
+ constructor(config: WordCloudChartConfig);
24
+ destroy(): void;
25
+ protected validateSourceData(data: ChartData): void;
26
+ protected renderChart({ svg, plotArea }: BaseRenderContext): void;
27
+ protected createExportChart(): BaseChart;
28
+ private startLayout;
29
+ private renderWords;
30
+ private stopLayout;
31
+ private finishReady;
32
+ }