@acorex/charts 21.0.0-next.2 → 21.0.0-next.21
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/bar-chart/index.d.ts +67 -7
- package/donut-chart/index.d.ts +46 -8
- package/fesm2022/acorex-charts-bar-chart.mjs +342 -124
- package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-legend.mjs +4 -4
- package/fesm2022/acorex-charts-chart-legend.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-tooltip.mjs +4 -4
- package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-charts-donut-chart.mjs +298 -200
- package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-gauge-chart.mjs +93 -68
- package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-hierarchy-chart.mjs +22 -16
- package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-line-chart.mjs +295 -153
- package/fesm2022/acorex-charts-line-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts.mjs +93 -7
- package/fesm2022/acorex-charts.mjs.map +1 -1
- package/gauge-chart/index.d.ts +10 -7
- package/hierarchy-chart/index.d.ts +4 -5
- package/index.d.ts +43 -4
- package/line-chart/index.d.ts +70 -7
- package/package.json +1 -3
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { AX_CHART_COLOR_PALETTE, getChartColor, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
|
|
1
|
+
import { AXChartComponent, AX_CHART_COLOR_PALETTE, getChartColor, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
|
|
2
2
|
import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { InjectionToken, inject, ChangeDetectorRef, input, output, viewChild, signal, computed, afterNextRender, effect, untracked, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
5
|
-
import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
|
|
6
|
-
import { set } from 'lodash-es';
|
|
7
5
|
|
|
8
6
|
const AXDonutChartDefaultConfig = {
|
|
9
7
|
showTooltip: true,
|
|
@@ -23,11 +21,7 @@ const AXDonutChartDefaultConfig = {
|
|
|
23
21
|
};
|
|
24
22
|
const AX_DONUT_CHART_CONFIG = new InjectionToken('AX_DONUT_CHART_CONFIG', {
|
|
25
23
|
providedIn: 'root',
|
|
26
|
-
factory: () =>
|
|
27
|
-
const global = inject(AX_GLOBAL_CONFIG);
|
|
28
|
-
set(global, 'chart.donutChart', AXDonutChartDefaultConfig);
|
|
29
|
-
return AXDonutChartDefaultConfig;
|
|
30
|
-
},
|
|
24
|
+
factory: () => AXDonutChartDefaultConfig,
|
|
31
25
|
});
|
|
32
26
|
function donutChartConfig(config = {}) {
|
|
33
27
|
const result = {
|
|
@@ -41,7 +35,7 @@ function donutChartConfig(config = {}) {
|
|
|
41
35
|
* Donut Chart Component
|
|
42
36
|
* Displays data in a circular donut chart with interactive segments
|
|
43
37
|
*/
|
|
44
|
-
class AXDonutChartComponent {
|
|
38
|
+
class AXDonutChartComponent extends AXChartComponent {
|
|
45
39
|
// Dependency Injection
|
|
46
40
|
cdr = inject(ChangeDetectorRef);
|
|
47
41
|
// Inputs
|
|
@@ -60,8 +54,10 @@ class AXDonutChartComponent {
|
|
|
60
54
|
chartContainerEl = viewChild.required('chartContainer');
|
|
61
55
|
// D3 reference - loaded asynchronously
|
|
62
56
|
d3;
|
|
63
|
-
// Chart SVG reference
|
|
64
|
-
|
|
57
|
+
// Chart SVG reference (using 'any' since D3 is loaded dynamically at runtime)
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
svg = null;
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
61
|
pieData = [];
|
|
66
62
|
// State management
|
|
67
63
|
hiddenSegments = new Set();
|
|
@@ -93,8 +89,16 @@ class AXDonutChartComponent {
|
|
|
93
89
|
...this.options(),
|
|
94
90
|
};
|
|
95
91
|
}, ...(ngDevMode ? [{ debugName: "effectiveOptions" }] : []));
|
|
96
|
-
//
|
|
92
|
+
// Constants
|
|
97
93
|
TOOLTIP_GAP = 10;
|
|
94
|
+
HIGHLIGHT_TRANSITION_DURATION = 150;
|
|
95
|
+
HIGHLIGHT_TRANSITION_NAME = 'highlight';
|
|
96
|
+
SEGMENT_ANIMATION_DELAY = 50;
|
|
97
|
+
LABEL_ANIMATION_DELAY = 200;
|
|
98
|
+
MIN_CHART_DIMENSION = 200;
|
|
99
|
+
MIN_CHART_FOR_LABELS = 260;
|
|
100
|
+
DIMMED_OPACITY = 0.5;
|
|
101
|
+
VISIBLE_OPACITY = 1;
|
|
98
102
|
// Messages with defaults
|
|
99
103
|
effectiveMessages = computed(() => {
|
|
100
104
|
const defaultMessages = {
|
|
@@ -123,16 +127,16 @@ class AXDonutChartComponent {
|
|
|
123
127
|
return this.hiddenSegments.has(id);
|
|
124
128
|
}
|
|
125
129
|
constructor() {
|
|
130
|
+
super();
|
|
126
131
|
// Dynamically load D3 and initialize the chart when the component is ready
|
|
127
132
|
afterNextRender(() => {
|
|
128
133
|
this._initialized.set(true);
|
|
129
134
|
this.loadD3();
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
|
-
|
|
133
|
-
//
|
|
137
|
+
dataChangeEffect = effect(() => {
|
|
138
|
+
// Track data and options changes
|
|
134
139
|
this.data();
|
|
135
|
-
// Track options changes to trigger re-render on updates
|
|
136
140
|
this.effectiveOptions();
|
|
137
141
|
// Only update if already rendered
|
|
138
142
|
untracked(() => {
|
|
@@ -140,71 +144,75 @@ class AXDonutChartComponent {
|
|
|
140
144
|
this.updateChart();
|
|
141
145
|
}
|
|
142
146
|
});
|
|
143
|
-
}, ...(ngDevMode ? [{ debugName: "
|
|
147
|
+
}, ...(ngDevMode ? [{ debugName: "dataChangeEffect" }] : []));
|
|
144
148
|
/**
|
|
145
149
|
* Highlights a specific segment by ID
|
|
146
150
|
* @param id The segment ID to highlight, or null to clear highlight
|
|
147
151
|
*/
|
|
148
152
|
highlightSegment(id) {
|
|
149
|
-
if (this._isInitialAnimating())
|
|
150
|
-
return;
|
|
151
|
-
if (!this.svg)
|
|
153
|
+
if (this._isInitialAnimating() || !this.svg)
|
|
152
154
|
return;
|
|
153
|
-
|
|
154
|
-
// If the same segment is already highlighted, do nothing
|
|
155
|
-
if (currentlyHighlighted === id) {
|
|
155
|
+
if (this._currentlyHighlightedSegment() === id)
|
|
156
156
|
return;
|
|
157
|
-
}
|
|
158
|
-
// Update the currently highlighted segment state
|
|
159
157
|
this._currentlyHighlightedSegment.set(id);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const getSegmentDataById = (segmentId) => {
|
|
163
|
-
return untracked(() => this.chartDataArray()).find((d) => d.id === segmentId) || null;
|
|
164
|
-
};
|
|
165
|
-
// Always interrupt any ongoing transitions first
|
|
166
|
-
this.svg.selectAll('path').interrupt();
|
|
158
|
+
// Only interrupt ongoing HIGHLIGHT transitions (not initial animations)
|
|
159
|
+
this.svg.selectAll('path').interrupt(this.HIGHLIGHT_TRANSITION_NAME);
|
|
167
160
|
if (id === null) {
|
|
168
|
-
|
|
169
|
-
this.svg
|
|
170
|
-
.selectAll('path')
|
|
171
|
-
.classed('ax-donut-chart-highlighted', false)
|
|
172
|
-
.classed('ax-donut-chart-dimmed', false)
|
|
173
|
-
.transition()
|
|
174
|
-
.duration(transitionDuration)
|
|
175
|
-
.attr('transform', null)
|
|
176
|
-
.style('filter', null)
|
|
177
|
-
.style('opacity', 1);
|
|
178
|
-
this.segmentHover.emit(null);
|
|
161
|
+
this.clearAllHighlights();
|
|
179
162
|
return;
|
|
180
163
|
}
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
165
|
const targetSegment = this.svg.selectAll('path').filter((d) => d?.data?.id === id);
|
|
182
166
|
if (targetSegment.empty()) {
|
|
183
|
-
// Target not found, clear highlights
|
|
184
167
|
this._currentlyHighlightedSegment.set(null);
|
|
185
168
|
this.highlightSegment(null);
|
|
186
169
|
return;
|
|
187
170
|
}
|
|
188
|
-
|
|
171
|
+
this.dimNonTargetSegments(id);
|
|
172
|
+
this.highlightTargetSegment(targetSegment);
|
|
173
|
+
this.segmentHover.emit(this.getSegmentDataById(id));
|
|
174
|
+
}
|
|
175
|
+
clearAllHighlights() {
|
|
176
|
+
if (!this.svg)
|
|
177
|
+
return;
|
|
189
178
|
this.svg
|
|
190
179
|
.selectAll('path')
|
|
191
|
-
.
|
|
180
|
+
.classed('ax-donut-chart-highlighted', false)
|
|
181
|
+
.classed('ax-donut-chart-dimmed', false)
|
|
182
|
+
.transition(this.HIGHLIGHT_TRANSITION_NAME)
|
|
183
|
+
.duration(this.HIGHLIGHT_TRANSITION_DURATION)
|
|
184
|
+
.attr('transform', null)
|
|
185
|
+
.style('filter', null)
|
|
186
|
+
.style('opacity', this.VISIBLE_OPACITY);
|
|
187
|
+
this.segmentHover.emit(null);
|
|
188
|
+
}
|
|
189
|
+
dimNonTargetSegments(targetId) {
|
|
190
|
+
if (!this.svg)
|
|
191
|
+
return;
|
|
192
|
+
this.svg
|
|
193
|
+
.selectAll('path')
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
195
|
+
.filter((d) => d?.data?.id !== targetId)
|
|
192
196
|
.classed('ax-donut-chart-highlighted', false)
|
|
193
197
|
.classed('ax-donut-chart-dimmed', true)
|
|
194
|
-
.transition()
|
|
195
|
-
.duration(
|
|
198
|
+
.transition(this.HIGHLIGHT_TRANSITION_NAME)
|
|
199
|
+
.duration(this.HIGHLIGHT_TRANSITION_DURATION)
|
|
196
200
|
.attr('transform', null)
|
|
197
201
|
.style('filter', null)
|
|
198
|
-
.style('opacity',
|
|
199
|
-
|
|
202
|
+
.style('opacity', this.DIMMED_OPACITY);
|
|
203
|
+
}
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
205
|
+
highlightTargetSegment(targetSegment) {
|
|
200
206
|
targetSegment
|
|
201
207
|
.classed('ax-donut-chart-dimmed', false)
|
|
202
208
|
.classed('ax-donut-chart-highlighted', true)
|
|
203
|
-
.transition()
|
|
204
|
-
.duration(
|
|
209
|
+
.transition(this.HIGHLIGHT_TRANSITION_NAME)
|
|
210
|
+
.duration(this.HIGHLIGHT_TRANSITION_DURATION)
|
|
205
211
|
.style('filter', 'url(#ax-donut-chart-segment-shadow)')
|
|
206
|
-
.style('opacity',
|
|
207
|
-
|
|
212
|
+
.style('opacity', this.VISIBLE_OPACITY);
|
|
213
|
+
}
|
|
214
|
+
getSegmentDataById(segmentId) {
|
|
215
|
+
return untracked(() => this.chartDataArray()).find((d) => d.id === segmentId) || null;
|
|
208
216
|
}
|
|
209
217
|
/**
|
|
210
218
|
* Toggles visibility of a segment by ID
|
|
@@ -212,7 +220,8 @@ class AXDonutChartComponent {
|
|
|
212
220
|
* @returns New visibility state (true = visible, false = hidden)
|
|
213
221
|
*/
|
|
214
222
|
toggleSegment(id) {
|
|
215
|
-
const
|
|
223
|
+
const chartData = this.chartDataArray();
|
|
224
|
+
const wasAllHidden = chartData.length > 0 && chartData.every((d) => this.isSegmentHidden(d.id));
|
|
216
225
|
if (this.hiddenSegments.has(id)) {
|
|
217
226
|
// Making a segment visible
|
|
218
227
|
this.hiddenSegments.delete(id);
|
|
@@ -262,11 +271,10 @@ class AXDonutChartComponent {
|
|
|
262
271
|
}
|
|
263
272
|
}
|
|
264
273
|
onSegmentClick(item) {
|
|
265
|
-
// this.toggleSegmentVisibility(item.id);
|
|
266
274
|
this.segmentClick.emit(item);
|
|
267
275
|
}
|
|
268
276
|
/**
|
|
269
|
-
* Creates the donut chart
|
|
277
|
+
* Creates the donut chart from current data
|
|
270
278
|
*/
|
|
271
279
|
createChart() {
|
|
272
280
|
if (!this.d3 || !this.chartContainerEl()?.nativeElement)
|
|
@@ -279,9 +287,7 @@ class AXDonutChartComponent {
|
|
|
279
287
|
this.showNoDataMessage();
|
|
280
288
|
return;
|
|
281
289
|
}
|
|
282
|
-
// Filter out hidden segments
|
|
283
290
|
const visibleData = data.filter((item) => !this.hiddenSegments.has(item.id));
|
|
284
|
-
// If all segments are hidden, show message
|
|
285
291
|
if (visibleData.length === 0) {
|
|
286
292
|
this.showAllSegmentsHiddenMessage();
|
|
287
293
|
return;
|
|
@@ -332,13 +338,10 @@ class AXDonutChartComponent {
|
|
|
332
338
|
* Setups chart dimensions based on container and options
|
|
333
339
|
*/
|
|
334
340
|
setupDimensions(container, options) {
|
|
335
|
-
// Get container dimensions or use defaults
|
|
336
341
|
const containerWidth = container.clientWidth || 400;
|
|
337
342
|
const containerHeight = container.clientHeight || 400;
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
const width = Math.max(options?.width || containerWidth, minDim);
|
|
341
|
-
const height = Math.max(options?.height || containerHeight, minDim);
|
|
343
|
+
const width = Math.max(options?.width || containerWidth, this.MIN_CHART_DIMENSION);
|
|
344
|
+
const height = Math.max(options?.height || containerHeight, this.MIN_CHART_DIMENSION);
|
|
342
345
|
return { width, height };
|
|
343
346
|
}
|
|
344
347
|
/**
|
|
@@ -357,7 +360,9 @@ class AXDonutChartComponent {
|
|
|
357
360
|
}
|
|
358
361
|
/**
|
|
359
362
|
* Create SVG element with filter definitions for shadows
|
|
363
|
+
* @returns D3 SVG selection (any type due to dynamic D3 loading)
|
|
360
364
|
*/
|
|
365
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
361
366
|
createSvgWithFilters(container, width, height) {
|
|
362
367
|
const svg = this.d3
|
|
363
368
|
.select(container)
|
|
@@ -420,33 +425,24 @@ class AXDonutChartComponent {
|
|
|
420
425
|
.enter()
|
|
421
426
|
.append('path')
|
|
422
427
|
.attr('class', 'ax-donut-chart-segment')
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (chartItem.color) {
|
|
426
|
-
// Prioritize explicit color on the item
|
|
427
|
-
return chartItem.color;
|
|
428
|
-
}
|
|
429
|
-
// Fallback: find original index for consistent palette color
|
|
430
|
-
const originalFullData = untracked(() => this.chartDataArray());
|
|
431
|
-
const originalIndex = originalFullData.findIndex((item) => item.id === chartItem.id);
|
|
432
|
-
return this.getColor(originalIndex !== -1 ? originalIndex : 0); // Ensure valid index
|
|
433
|
-
})
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
429
|
+
.attr('fill', (d) => this.getSegmentColor(d.data))
|
|
434
430
|
.style('opacity', 0)
|
|
435
|
-
.style('cursor', 'pointer')
|
|
431
|
+
.style('cursor', 'pointer')
|
|
432
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
436
433
|
.on('mouseenter', (event, d) => {
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
})
|
|
441
|
-
.on('mouseleave', () => {
|
|
442
|
-
this.handleSegmentLeave();
|
|
434
|
+
if (this.effectiveOptions().showTooltip) {
|
|
435
|
+
this.handleSliceHover(event, d.data);
|
|
436
|
+
}
|
|
443
437
|
})
|
|
438
|
+
.on('mouseleave', () => this.handleSegmentLeave())
|
|
444
439
|
.on('mousemove', (event) => {
|
|
445
440
|
if (this._tooltipVisible()) {
|
|
446
441
|
this.updateTooltipPosition(event);
|
|
447
442
|
}
|
|
448
443
|
})
|
|
449
|
-
|
|
444
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
445
|
+
.on('click', (_event, d) => {
|
|
450
446
|
this.onSegmentClick(d.data);
|
|
451
447
|
});
|
|
452
448
|
// Animate segments
|
|
@@ -456,8 +452,10 @@ class AXDonutChartComponent {
|
|
|
456
452
|
.transition()
|
|
457
453
|
.duration(animationDuration)
|
|
458
454
|
.ease(animationEasing)
|
|
459
|
-
|
|
460
|
-
.
|
|
455
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
456
|
+
.delay((_d, i) => i * this.SEGMENT_ANIMATION_DELAY)
|
|
457
|
+
.style('opacity', this.VISIBLE_OPACITY)
|
|
458
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
461
459
|
.attrTween('d', (d) => {
|
|
462
460
|
const interpolate = this.d3.interpolate({ startAngle: d.startAngle, endAngle: d.startAngle }, d);
|
|
463
461
|
return (t) => arc(interpolate(t));
|
|
@@ -478,79 +476,8 @@ class AXDonutChartComponent {
|
|
|
478
476
|
}
|
|
479
477
|
});
|
|
480
478
|
// Add data labels if enabled and chart size is sufficient
|
|
481
|
-
if (this.effectiveOptions().showDataLabels) {
|
|
482
|
-
|
|
483
|
-
const isTooSmallChartForLabels = minChartDimension < 260; // Hide labels entirely on small charts
|
|
484
|
-
if (!isTooSmallChartForLabels) {
|
|
485
|
-
// Calculate optimal font size based on segment size and chart dimensions
|
|
486
|
-
const calculateFontSize = (d) => {
|
|
487
|
-
const angleSize = d.endAngle - d.startAngle; // radians
|
|
488
|
-
const segmentPercentage = (d.data.value / total) * 100;
|
|
489
|
-
// Drop labels for very small segments
|
|
490
|
-
if (segmentPercentage < 2)
|
|
491
|
-
return 0;
|
|
492
|
-
// Minimum readable font size; avoid tiny unreadable text
|
|
493
|
-
const minReadable = segmentPercentage < 8 ? 0 : 9;
|
|
494
|
-
// Base size scaled by arc angle and radius, capped
|
|
495
|
-
const scaled = (angleSize * radius) / 9;
|
|
496
|
-
return Math.min(Math.max(minReadable, scaled), 13);
|
|
497
|
-
};
|
|
498
|
-
const formatPercentage = (value) => {
|
|
499
|
-
if (value < 1)
|
|
500
|
-
return '<1%';
|
|
501
|
-
if (value < 10)
|
|
502
|
-
return `${value.toFixed(1)}%`;
|
|
503
|
-
return `${Math.round(value)}%`;
|
|
504
|
-
};
|
|
505
|
-
const labelRadius = innerRadius + (radius * 0.95 - innerRadius) / 2;
|
|
506
|
-
const approximateTextWidth = (text, fontSize) => {
|
|
507
|
-
// Approximate width: 0.55em per char
|
|
508
|
-
return text.length * fontSize * 0.55;
|
|
509
|
-
};
|
|
510
|
-
const arcLength = (d) => (d.endAngle - d.startAngle) * labelRadius;
|
|
511
|
-
const buildLabelText = (d) => {
|
|
512
|
-
const percentage = (d.data.value / total) * 100;
|
|
513
|
-
if (percentage < 1)
|
|
514
|
-
return '';
|
|
515
|
-
const label = d.data.label || '';
|
|
516
|
-
const percentageText = formatPercentage(percentage);
|
|
517
|
-
return label ? `${label} (${percentageText})` : percentageText;
|
|
518
|
-
};
|
|
519
|
-
const dataThatFits = this.pieData.filter((d) => {
|
|
520
|
-
const fontSize = calculateFontSize(d);
|
|
521
|
-
if (!fontSize)
|
|
522
|
-
return false;
|
|
523
|
-
const text = buildLabelText(d);
|
|
524
|
-
if (!text)
|
|
525
|
-
return false;
|
|
526
|
-
const available = arcLength(d);
|
|
527
|
-
const needed = approximateTextWidth(text, fontSize) + 6; // small padding
|
|
528
|
-
return needed <= available;
|
|
529
|
-
});
|
|
530
|
-
const labels = this.svg
|
|
531
|
-
.selectAll('.ax-donut-chart-data-label')
|
|
532
|
-
.data(dataThatFits)
|
|
533
|
-
.enter()
|
|
534
|
-
.append('text')
|
|
535
|
-
.attr('class', 'ax-donut-chart-data-label')
|
|
536
|
-
.style('opacity', 0)
|
|
537
|
-
.style('pointer-events', 'none')
|
|
538
|
-
.style('fill', 'rgb(var(--ax-comp-donut-chart-data-labels-color))')
|
|
539
|
-
.attr('transform', (d) => {
|
|
540
|
-
const centroid = labelArc.centroid(d);
|
|
541
|
-
return `translate(${centroid[0]}, ${centroid[1]})`;
|
|
542
|
-
})
|
|
543
|
-
.attr('text-anchor', 'middle')
|
|
544
|
-
.attr('dominant-baseline', 'middle')
|
|
545
|
-
.style('font-size', (d) => `${calculateFontSize(d)}px`)
|
|
546
|
-
.style('font-weight', (d) => ((d.data.value / total) * 100 >= 15 ? '600' : '500'))
|
|
547
|
-
.text((d) => buildLabelText(d));
|
|
548
|
-
labels
|
|
549
|
-
.transition()
|
|
550
|
-
.duration(animationDuration)
|
|
551
|
-
.delay((d, i) => i * 50 + 200)
|
|
552
|
-
.style('opacity', 1);
|
|
553
|
-
}
|
|
479
|
+
if (this.effectiveOptions().showDataLabels && this.isChartLargeEnoughForLabels(chartWidth, chartHeight)) {
|
|
480
|
+
this.addDataLabels(labelArc, radius, innerRadius, total, animationDuration);
|
|
554
481
|
}
|
|
555
482
|
// Determine when all segment animations are complete (handle empty case)
|
|
556
483
|
if (numSegments === 0) {
|
|
@@ -558,10 +485,184 @@ class AXDonutChartComponent {
|
|
|
558
485
|
this.cdr.detectChanges();
|
|
559
486
|
}
|
|
560
487
|
}
|
|
488
|
+
isChartLargeEnoughForLabels(width, height) {
|
|
489
|
+
return Math.min(width, height) >= this.MIN_CHART_FOR_LABELS;
|
|
490
|
+
}
|
|
491
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
492
|
+
addDataLabels(labelArc, radius, innerRadius, total, animationDuration) {
|
|
493
|
+
if (!this.svg)
|
|
494
|
+
return;
|
|
495
|
+
const labelRadius = innerRadius + (radius * 0.95 - innerRadius) / 2;
|
|
496
|
+
// Show labels for all segments with valid font size
|
|
497
|
+
const dataWithLabels = this.pieData.filter((d) => {
|
|
498
|
+
const fontSize = this.calculateLabelFontSize(d, radius, total, labelRadius);
|
|
499
|
+
return fontSize > 0; // Only show if font size is valid
|
|
500
|
+
});
|
|
501
|
+
const labels = this.svg
|
|
502
|
+
.selectAll('.ax-donut-chart-data-label')
|
|
503
|
+
.data(dataWithLabels)
|
|
504
|
+
.enter()
|
|
505
|
+
.append('text')
|
|
506
|
+
.attr('class', 'ax-donut-chart-data-label')
|
|
507
|
+
.style('opacity', 0)
|
|
508
|
+
.style('pointer-events', 'none')
|
|
509
|
+
.style('fill', 'rgb(var(--ax-comp-donut-chart-data-labels-color))')
|
|
510
|
+
.attr('transform', (d) => {
|
|
511
|
+
const centroid = labelArc.centroid(d);
|
|
512
|
+
return `translate(${centroid[0]}, ${centroid[1]})`;
|
|
513
|
+
})
|
|
514
|
+
.attr('text-anchor', 'middle')
|
|
515
|
+
.attr('dominant-baseline', 'middle')
|
|
516
|
+
.style('font-size', (d) => `${this.calculateLabelFontSize(d, radius, total, labelRadius)}px`)
|
|
517
|
+
.style('font-weight', (d) => ((d.data.value / total) * 100 >= 15 ? '600' : '500'))
|
|
518
|
+
.text((d) => this.getTruncatedLabelText(d, radius, labelRadius, total));
|
|
519
|
+
labels
|
|
520
|
+
.transition()
|
|
521
|
+
.duration(animationDuration)
|
|
522
|
+
.delay((_d, i) => i * this.SEGMENT_ANIMATION_DELAY + this.LABEL_ANIMATION_DELAY)
|
|
523
|
+
.style('opacity', this.VISIBLE_OPACITY);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Calculates optimal font size for label based on segment size
|
|
527
|
+
* Uses smart dynamic sizing with readability constraints
|
|
528
|
+
*/
|
|
529
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
530
|
+
calculateLabelFontSize(d, radius, total, labelRadius) {
|
|
531
|
+
const angle = d.endAngle - d.startAngle;
|
|
532
|
+
const segmentPercentage = (d.data.value / total) * 100;
|
|
533
|
+
// Don't show labels for very small segments (< 1%)
|
|
534
|
+
if (segmentPercentage < 1)
|
|
535
|
+
return 0;
|
|
536
|
+
// Calculate chord length (available horizontal space)
|
|
537
|
+
const chordLength = 2 * labelRadius * Math.sin(angle / 2);
|
|
538
|
+
// Constants for font sizing
|
|
539
|
+
const MIN_FONT_SIZE = 9; // Minimum readable size
|
|
540
|
+
const MAX_FONT_SIZE = 14; // Maximum for aesthetics
|
|
541
|
+
const MIN_CHORD_FOR_LABEL = 20; // Minimum space needed for any text
|
|
542
|
+
// Don't show labels if space is too small
|
|
543
|
+
if (chordLength < MIN_CHORD_FOR_LABEL)
|
|
544
|
+
return 0;
|
|
545
|
+
// Calculate font size based on multiple factors
|
|
546
|
+
// 1. Based on arc angle (larger segments = larger font)
|
|
547
|
+
const angleBasedSize = (angle * radius) / 8;
|
|
548
|
+
// 2. Based on chord length (available horizontal space)
|
|
549
|
+
const chordBasedSize = chordLength / 8;
|
|
550
|
+
// 3. Based on segment percentage (proportional to data importance)
|
|
551
|
+
const percentageBasedSize = 9 + (segmentPercentage / 100) * 5; // 9-14px range
|
|
552
|
+
// Take the minimum of all calculations to ensure fit
|
|
553
|
+
const calculatedSize = Math.min(angleBasedSize, chordBasedSize, percentageBasedSize);
|
|
554
|
+
// Apply min/max constraints
|
|
555
|
+
const finalSize = Math.max(MIN_FONT_SIZE, Math.min(calculatedSize, MAX_FONT_SIZE));
|
|
556
|
+
// Round to .5 for crisp rendering
|
|
557
|
+
return Math.round(finalSize * 2) / 2;
|
|
558
|
+
}
|
|
559
|
+
formatPercentage(value) {
|
|
560
|
+
if (value < 1)
|
|
561
|
+
return '<1%';
|
|
562
|
+
if (value < 10)
|
|
563
|
+
return `${value.toFixed(1)}%`;
|
|
564
|
+
return `${Math.round(value)}%`;
|
|
565
|
+
}
|
|
566
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
567
|
+
buildLabelText(d, total) {
|
|
568
|
+
const percentage = (d.data.value / total) * 100;
|
|
569
|
+
if (percentage < 1)
|
|
570
|
+
return '';
|
|
571
|
+
const label = d.data.label || '';
|
|
572
|
+
const percentageText = this.formatPercentage(percentage);
|
|
573
|
+
return label ? `${label} (${percentageText})` : percentageText;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Truncates label text to fit within the arc segment
|
|
577
|
+
* Uses smart truncation: tries full label, then label only, then percentage only, then truncated percentage
|
|
578
|
+
* Calculates chord length (straight line) since text is rendered horizontally, not along the arc
|
|
579
|
+
*/
|
|
580
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
581
|
+
getTruncatedLabelText(d, radius, labelRadius, total) {
|
|
582
|
+
const fontSize = this.calculateLabelFontSize(d, radius, total, labelRadius);
|
|
583
|
+
// If font size is 0, segment is too small for labels
|
|
584
|
+
if (fontSize === 0)
|
|
585
|
+
return '';
|
|
586
|
+
// Calculate chord length (straight line distance) instead of arc length
|
|
587
|
+
// Formula: chord = 2 * radius * sin(angle/2)
|
|
588
|
+
const angle = d.endAngle - d.startAngle;
|
|
589
|
+
const chordLength = 2 * labelRadius * Math.sin(angle / 2);
|
|
590
|
+
// Use chord length as available width with dynamic safety margin
|
|
591
|
+
// Smaller segments need larger safety margins (percentage-based)
|
|
592
|
+
const safetyMarginPercent = Math.max(0.15, Math.min(0.3, 1 / angle)); // 15-30% margin
|
|
593
|
+
const availableWidth = chordLength * (1 - safetyMarginPercent);
|
|
594
|
+
const percentage = (d.data.value / total) * 100;
|
|
595
|
+
if (percentage < 1)
|
|
596
|
+
return '';
|
|
597
|
+
const label = d.data.label || '';
|
|
598
|
+
const percentageText = this.formatPercentage(percentage);
|
|
599
|
+
// Try different label formats in order of preference
|
|
600
|
+
const fullText = label ? `${label} (${percentageText})` : percentageText;
|
|
601
|
+
const labelOnly = label;
|
|
602
|
+
const percentageOnly = percentageText;
|
|
603
|
+
// 1. Try full text (label + percentage)
|
|
604
|
+
if (this.doesTextFit(fullText, fontSize, availableWidth)) {
|
|
605
|
+
return fullText;
|
|
606
|
+
}
|
|
607
|
+
// 2. Try label only (if exists)
|
|
608
|
+
if (label && this.doesTextFit(labelOnly, fontSize, availableWidth)) {
|
|
609
|
+
return labelOnly;
|
|
610
|
+
}
|
|
611
|
+
// 3. Try percentage only
|
|
612
|
+
if (this.doesTextFit(percentageOnly, fontSize, availableWidth)) {
|
|
613
|
+
return percentageOnly;
|
|
614
|
+
}
|
|
615
|
+
// 4. Truncate the label with ellipsis
|
|
616
|
+
if (label) {
|
|
617
|
+
return this.truncateTextToFit(label, fontSize, availableWidth);
|
|
618
|
+
}
|
|
619
|
+
// 5. Last resort: truncate percentage (very small segments)
|
|
620
|
+
return this.truncateTextToFit(percentageText, fontSize, availableWidth);
|
|
621
|
+
}
|
|
561
622
|
/**
|
|
562
|
-
*
|
|
623
|
+
* Checks if text fits within available width using conservative estimation
|
|
624
|
+
* Uses 0.65em per character to account for font variations
|
|
563
625
|
*/
|
|
564
|
-
|
|
626
|
+
doesTextFit(text, fontSize, availableWidth) {
|
|
627
|
+
if (!text)
|
|
628
|
+
return false;
|
|
629
|
+
// Use conservative character width (0.65em for safety)
|
|
630
|
+
// Wider fonts like bold text need more space
|
|
631
|
+
const estimatedWidth = text.length * fontSize * 0.65;
|
|
632
|
+
return estimatedWidth <= availableWidth;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Truncates text to fit within available width with ellipsis
|
|
636
|
+
* Uses conservative character width to prevent overflow
|
|
637
|
+
* Adapts character width estimation based on font size
|
|
638
|
+
*/
|
|
639
|
+
truncateTextToFit(text, fontSize, availableWidth) {
|
|
640
|
+
if (!text)
|
|
641
|
+
return '';
|
|
642
|
+
// Adaptive character width: smaller fonts are relatively wider
|
|
643
|
+
// Larger fonts: ~0.6em, Smaller fonts: ~0.7em
|
|
644
|
+
const charWidthRatio = fontSize <= 10 ? 0.7 : 0.65;
|
|
645
|
+
const charWidth = fontSize * charWidthRatio;
|
|
646
|
+
// Reserve space for ellipsis
|
|
647
|
+
const ellipsisWidth = fontSize * 0.7;
|
|
648
|
+
const availableForText = availableWidth - ellipsisWidth;
|
|
649
|
+
if (availableForText <= 0)
|
|
650
|
+
return '';
|
|
651
|
+
const maxChars = Math.floor(availableForText / charWidth);
|
|
652
|
+
if (maxChars <= 0)
|
|
653
|
+
return '';
|
|
654
|
+
if (text.length <= maxChars)
|
|
655
|
+
return text;
|
|
656
|
+
// Ensure at least 1 character before ellipsis
|
|
657
|
+
return text.substring(0, Math.max(1, maxChars)) + '…';
|
|
658
|
+
}
|
|
659
|
+
getSegmentColor(data) {
|
|
660
|
+
if (data.color)
|
|
661
|
+
return data.color;
|
|
662
|
+
const originalFullData = untracked(() => this.chartDataArray());
|
|
663
|
+
const originalIndex = originalFullData.findIndex((item) => item.id === data.id);
|
|
664
|
+
return this.getColor(originalIndex !== -1 ? originalIndex : 0);
|
|
665
|
+
}
|
|
565
666
|
/**
|
|
566
667
|
* Handle hover effects on a segment
|
|
567
668
|
*/
|
|
@@ -569,23 +670,22 @@ class AXDonutChartComponent {
|
|
|
569
670
|
if (this._isInitialAnimating())
|
|
570
671
|
return;
|
|
571
672
|
if (this.effectiveOptions().showTooltip !== false) {
|
|
572
|
-
|
|
573
|
-
const color = datum.color || getChartColor(index, this.chartColors);
|
|
574
|
-
// Calculate percentage of total
|
|
575
|
-
// Ensure data() is accessed within a tracking context if it's a signal, or use untracked if appropriate
|
|
576
|
-
const total = untracked(() => this.data()).reduce((sum, item) => sum + item.value, 0);
|
|
577
|
-
const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
|
|
578
|
-
this._tooltipData.set({
|
|
579
|
-
title: datum.tooltipLabel || datum.label,
|
|
580
|
-
value: datum.value.toString(),
|
|
581
|
-
percentage: `${percentage}%`,
|
|
582
|
-
color: color,
|
|
583
|
-
});
|
|
584
|
-
this.updateTooltipPosition(event);
|
|
585
|
-
this._tooltipVisible.set(true);
|
|
673
|
+
this.showTooltip(event, datum);
|
|
586
674
|
}
|
|
587
|
-
this.highlightSegment(datum.id);
|
|
588
|
-
|
|
675
|
+
this.highlightSegment(datum.id);
|
|
676
|
+
}
|
|
677
|
+
showTooltip(event, datum) {
|
|
678
|
+
const total = untracked(() => this.data()).reduce((sum, item) => sum + item.value, 0);
|
|
679
|
+
const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
|
|
680
|
+
const color = this.getSegmentColor(datum);
|
|
681
|
+
this._tooltipData.set({
|
|
682
|
+
title: datum.tooltipLabel || datum.label,
|
|
683
|
+
value: datum.value.toString(),
|
|
684
|
+
percentage: `${percentage}%`,
|
|
685
|
+
color,
|
|
686
|
+
});
|
|
687
|
+
this.updateTooltipPosition(event);
|
|
688
|
+
this._tooltipVisible.set(true);
|
|
589
689
|
}
|
|
590
690
|
/**
|
|
591
691
|
* Handles mouse leave from segments
|
|
@@ -619,9 +719,9 @@ class AXDonutChartComponent {
|
|
|
619
719
|
addCenterDisplay(total) {
|
|
620
720
|
if (!this.svg)
|
|
621
721
|
return;
|
|
622
|
-
// Calculate appropriate font sizes based on chart dimensions
|
|
623
722
|
const chartContainerWidth = this.chartContainerEl().nativeElement.clientWidth;
|
|
624
|
-
|
|
723
|
+
// Improved font scaling: better minimum for small sizes, better ratio for scaling
|
|
724
|
+
const baseFontSize = Math.max(1.8, Math.min(3.2, chartContainerWidth / 150));
|
|
625
725
|
const subTextFontSize = baseFontSize * 0.5;
|
|
626
726
|
// Create group for the total display
|
|
627
727
|
const totalDisplay = this.svg
|
|
@@ -659,11 +759,11 @@ class AXDonutChartComponent {
|
|
|
659
759
|
* Cleans up chart resources
|
|
660
760
|
*/
|
|
661
761
|
cleanupChart() {
|
|
662
|
-
if (this.svg)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
762
|
+
if (!this.svg)
|
|
763
|
+
return;
|
|
764
|
+
this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
|
|
765
|
+
this.svg = null;
|
|
766
|
+
this.pieData = [];
|
|
667
767
|
this.hiddenSegments.clear();
|
|
668
768
|
this._tooltipVisible.set(false);
|
|
669
769
|
this._currentlyHighlightedSegment.set(null);
|
|
@@ -686,18 +786,17 @@ class AXDonutChartComponent {
|
|
|
686
786
|
// computeTooltipPosition moved to shared chart utils
|
|
687
787
|
/**
|
|
688
788
|
* Gets an accessibility label describing the donut chart for screen readers
|
|
789
|
+
* @returns Descriptive string for screen readers
|
|
689
790
|
*/
|
|
690
791
|
getAccessibilityLabel() {
|
|
691
792
|
const data = this.chartDataArray();
|
|
692
793
|
if (!data || data.length === 0) {
|
|
693
794
|
return 'Empty donut chart. No data available.';
|
|
694
795
|
}
|
|
695
|
-
// Calculate total
|
|
696
796
|
const total = data.reduce((sum, item) => sum + item.value, 0);
|
|
697
|
-
// Generate a description of the chart with percentages
|
|
698
797
|
const segmentDescriptions = data
|
|
699
798
|
.map((segment) => {
|
|
700
|
-
const percentage = ((segment.value / total) * 100).toFixed(1);
|
|
799
|
+
const percentage = total > 0 ? ((segment.value / total) * 100).toFixed(1) : '0';
|
|
701
800
|
return `${segment.label}: ${segment.value} (${percentage}%)`;
|
|
702
801
|
})
|
|
703
802
|
.join('; ');
|
|
@@ -708,24 +807,23 @@ class AXDonutChartComponent {
|
|
|
708
807
|
* @implements AXChartLegendCompatible
|
|
709
808
|
*/
|
|
710
809
|
getLegendItems() {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
});
|
|
810
|
+
const total = this.data().reduce((sum, item) => sum + item.value, 0);
|
|
811
|
+
return this.chartDataArray().map((item, index) => ({
|
|
812
|
+
id: item.id,
|
|
813
|
+
name: item.label || `Segment ${index + 1}`,
|
|
814
|
+
value: item.value,
|
|
815
|
+
color: this.getSegmentColor(item),
|
|
816
|
+
percentage: total > 0 ? (item.value / total) * 100 : 0,
|
|
817
|
+
hidden: this.isSegmentHidden(item.id),
|
|
818
|
+
}));
|
|
721
819
|
}
|
|
722
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
723
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.
|
|
820
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXDonutChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
821
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.9", type: AXDonutChartComponent, isStandalone: true, selector: "ax-donut-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { segmentClick: "segmentClick", segmentHover: "segmentHover" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-donut-chart{display:block;width:100%;height:100%;--ax-comp-donut-chart-bg-color: 0, 0, 0, 0;--ax-comp-donut-chart-value-color: var(--ax-sys-color-on-lightest-surface)}ax-donut-chart .ax-donut-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:rgba(var(--ax-comp-donut-chart-bg-color))}ax-donut-chart .ax-donut-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}ax-donut-chart .ax-donut-chart svg g:has(text){font-family:inherit}ax-donut-chart .ax-donut-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-donut-chart-bg-color));border:1px solid rgba(var(--ax-sys-color-surface));padding:1.5rem;border-radius:.5rem;width:80%;max-width:300px}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-help{font-size:.8rem;opacity:.6}\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
724
822
|
}
|
|
725
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
823
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXDonutChartComponent, decorators: [{
|
|
726
824
|
type: Component,
|
|
727
|
-
args: [{ selector: 'ax-donut-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-donut-chart{display:block;width:100%;height:100%;--ax-comp-donut-chart-bg-color: 0, 0, 0, 0;--ax-comp-donut-chart-value-color: var(--ax-sys-color-on-lightest-surface)}ax-donut-chart .ax-donut-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;
|
|
728
|
-
}], ctorParameters: () => [] });
|
|
825
|
+
args: [{ selector: 'ax-donut-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-donut-chart{display:block;width:100%;height:100%;--ax-comp-donut-chart-bg-color: 0, 0, 0, 0;--ax-comp-donut-chart-value-color: var(--ax-sys-color-on-lightest-surface)}ax-donut-chart .ax-donut-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:rgba(var(--ax-comp-donut-chart-bg-color))}ax-donut-chart .ax-donut-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}ax-donut-chart .ax-donut-chart svg g:has(text){font-family:inherit}ax-donut-chart .ax-donut-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-donut-chart-bg-color));border:1px solid rgba(var(--ax-sys-color-surface));padding:1.5rem;border-radius:.5rem;width:80%;max-width:300px}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-donut-chart .ax-donut-chart-no-data-message .ax-donut-chart-no-data-help{font-size:.8rem;opacity:.6}\n"] }]
|
|
826
|
+
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], segmentClick: [{ type: i0.Output, args: ["segmentClick"] }], segmentHover: [{ type: i0.Output, args: ["segmentHover"] }], chartContainerEl: [{ type: i0.ViewChild, args: ['chartContainer', { isSignal: true }] }] } });
|
|
729
827
|
|
|
730
828
|
/**
|
|
731
829
|
* Generated bundle index. Do not edit.
|