@acorex/charts 19.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +72 -0
  2. package/bar-chart/README.md +3 -0
  3. package/bar-chart/index.d.ts +3 -0
  4. package/bar-chart/lib/bar-chart.component.d.ts +123 -0
  5. package/bar-chart/lib/bar-chart.config.d.ts +6 -0
  6. package/bar-chart/lib/bar-chart.type.d.ts +44 -0
  7. package/chart-tooltip/README.md +3 -0
  8. package/chart-tooltip/index.d.ts +2 -0
  9. package/chart-tooltip/lib/chart-tooltip.component.d.ts +43 -0
  10. package/chart-tooltip/lib/chart-tooltip.type.d.ts +7 -0
  11. package/donut-chart/README.md +3 -0
  12. package/donut-chart/index.d.ts +3 -0
  13. package/donut-chart/lib/donut-chart.component.d.ts +143 -0
  14. package/donut-chart/lib/donut-chart.config.d.ts +6 -0
  15. package/donut-chart/lib/donut-chart.type.d.ts +25 -0
  16. package/fesm2022/acorex-charts-bar-chart.mjs +563 -0
  17. package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -0
  18. package/fesm2022/acorex-charts-chart-tooltip.mjs +75 -0
  19. package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -0
  20. package/fesm2022/acorex-charts-donut-chart.mjs +616 -0
  21. package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -0
  22. package/fesm2022/acorex-charts-gauge-chart.mjs +548 -0
  23. package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -0
  24. package/fesm2022/acorex-charts-hierarchy-chart.mjs +652 -0
  25. package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -0
  26. package/fesm2022/acorex-charts-line-chart.mjs +738 -0
  27. package/fesm2022/acorex-charts-line-chart.mjs.map +1 -0
  28. package/fesm2022/acorex-charts.mjs +8 -0
  29. package/fesm2022/acorex-charts.mjs.map +1 -0
  30. package/gauge-chart/README.md +3 -0
  31. package/gauge-chart/index.d.ts +3 -0
  32. package/gauge-chart/lib/gauge-chart.component.d.ts +110 -0
  33. package/gauge-chart/lib/gauge-chart.config.d.ts +6 -0
  34. package/gauge-chart/lib/gauge-chart.type.d.ts +37 -0
  35. package/hierarchy-chart/README.md +61 -0
  36. package/hierarchy-chart/index.d.ts +3 -0
  37. package/hierarchy-chart/lib/hierarchy-chart.component.d.ts +99 -0
  38. package/hierarchy-chart/lib/hierarchy-chart.config.d.ts +6 -0
  39. package/hierarchy-chart/lib/hierarchy-chart.type.d.ts +227 -0
  40. package/index.d.ts +1 -0
  41. package/line-chart/README.md +3 -0
  42. package/line-chart/index.d.ts +3 -0
  43. package/line-chart/lib/line-chart.component.d.ts +96 -0
  44. package/line-chart/lib/line-chart.config.d.ts +6 -0
  45. package/line-chart/lib/line-chart.type.d.ts +61 -0
  46. package/package.json +48 -0
@@ -0,0 +1,616 @@
1
+ import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { InjectionToken, inject, ChangeDetectorRef, input, output, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, Component } from '@angular/core';
5
+ import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
6
+ import { set } from 'lodash-es';
7
+
8
+ const AXDonutChartDefaultConfig = {
9
+ showTooltip: true,
10
+ donutWidth: 35,
11
+ cornerRadius: 4,
12
+ animationDuration: 800,
13
+ animationEasing: 'cubic-out',
14
+ };
15
+ const AX_DONUT_CHART_CONFIG = new InjectionToken('AX_DONUT_CHART_CONFIG', {
16
+ providedIn: 'root',
17
+ factory: () => {
18
+ const global = inject(AX_GLOBAL_CONFIG);
19
+ set(global, 'chart.donutChart', AXDonutChartDefaultConfig);
20
+ return AXDonutChartDefaultConfig;
21
+ },
22
+ });
23
+ function donutChartConfig(config = {}) {
24
+ const result = {
25
+ ...AXDonutChartDefaultConfig,
26
+ ...config,
27
+ };
28
+ return result;
29
+ }
30
+
31
+ const AXDonutChartColors = {
32
+ // Modern color palette suitable for data visualization
33
+ defaultColors: [
34
+ '#4361ee', // Blue
35
+ '#3a0ca3', // Purple
36
+ '#7209b7', // Violet
37
+ '#f72585', // Pink
38
+ '#4cc9f0', // Light Blue
39
+ '#4895ef', // Sky Blue
40
+ '#560bad', // Deep Purple
41
+ '#f15bb5', // Light Pink
42
+ '#00bbf9', // Cyan
43
+ '#00f5d4', // Teal
44
+ ],
45
+ // Get a color from the palette by index with wraparound
46
+ getColor: (index, customPalette) => {
47
+ const palette = customPalette || AXDonutChartColors.defaultColors;
48
+ return palette[index % palette.length];
49
+ },
50
+ };
51
+ /**
52
+ * Donut Chart Component
53
+ * Displays data in a circular donut chart with interactive segments
54
+ */
55
+ class AXDonutChartComponent {
56
+ // Dependency Injection
57
+ cdr = inject(ChangeDetectorRef);
58
+ // Inputs
59
+ /** Chart data input */
60
+ data = input([]);
61
+ /** Chart options input */
62
+ options = input({});
63
+ // Outputs
64
+ /** Emitted when a segment is clicked */
65
+ segmentClick = output();
66
+ /** Emitted when a segment has mouse hover
67
+ * Can be used by external elements to highlight this segment
68
+ */
69
+ segmentHover = output();
70
+ // Chart container reference
71
+ chartContainerEl = viewChild.required('chartContainer');
72
+ // D3 reference - loaded asynchronously
73
+ d3;
74
+ // Chart SVG reference
75
+ svg;
76
+ pieData = [];
77
+ // State management
78
+ hiddenSegments = new Set();
79
+ _initialized = signal(false);
80
+ _rendered = signal(false);
81
+ // Tooltip state
82
+ _tooltipVisible = signal(false);
83
+ _tooltipPosition = signal({ x: 0, y: 0 });
84
+ _tooltipData = signal({
85
+ title: '',
86
+ value: 0,
87
+ percentage: '0%',
88
+ color: '',
89
+ });
90
+ // Public computed properties for the template
91
+ tooltipVisible = this._tooltipVisible.asReadonly();
92
+ tooltipPosition = this._tooltipPosition.asReadonly();
93
+ tooltipData = this._tooltipData.asReadonly();
94
+ // Inject configuration
95
+ configToken = inject(AX_DONUT_CHART_CONFIG);
96
+ // Computed configuration options
97
+ effectiveOptions = computed(() => {
98
+ return {
99
+ ...this.configToken,
100
+ ...this.options(),
101
+ };
102
+ });
103
+ // Data accessor for handling different incoming data formats
104
+ chartDataArray = computed(() => {
105
+ return this.data() || [];
106
+ });
107
+ // Color accessor method
108
+ getColor(index) {
109
+ return AXDonutChartColors.getColor(index);
110
+ }
111
+ // Segment visibility check
112
+ isSegmentHidden(id) {
113
+ return this.hiddenSegments.has(id);
114
+ }
115
+ constructor() {
116
+ // Dynamically load D3 and initialize the chart when the component is ready
117
+ afterNextRender(() => {
118
+ this._initialized.set(true);
119
+ this.loadD3();
120
+ });
121
+ // Watch for changes to redraw the chart
122
+ effect(() => {
123
+ // Access inputs to track them
124
+ this.data();
125
+ this.effectiveOptions();
126
+ // Only update if already rendered
127
+ if (this._rendered()) {
128
+ this.updateChart();
129
+ }
130
+ });
131
+ }
132
+ /**
133
+ * Highlights a specific segment by ID
134
+ * @param id The segment ID to highlight, or null to clear highlight
135
+ */
136
+ highlightSegment(id) {
137
+ if (!this.svg)
138
+ return;
139
+ // Reset all segments first
140
+ this.svg
141
+ .selectAll('path')
142
+ .classed('ax-donut-chart-highlighted', false)
143
+ .classed('ax-donut-chart-dimmed', false)
144
+ .attr('transform', 'scale(1)');
145
+ if (id !== null) {
146
+ // Highlight the target segment
147
+ this.svg
148
+ .selectAll('path')
149
+ .filter((d) => d?.data?.id === id)
150
+ .classed('ax-donut-chart-highlighted', true)
151
+ .attr('transform', 'scale(1.02)');
152
+ // Dim other segments
153
+ this.svg
154
+ .selectAll('path')
155
+ .filter((d) => d?.data?.id !== id)
156
+ .classed('ax-donut-chart-dimmed', true);
157
+ }
158
+ this.cdr.detectChanges();
159
+ }
160
+ /**
161
+ * Toggles visibility of a segment by ID
162
+ * @param id Segment ID to toggle
163
+ * @returns New visibility state (true = visible, false = hidden)
164
+ */
165
+ toggleSegment(id) {
166
+ this.toggleSegmentVisibility(id);
167
+ return !this.isSegmentHidden(id);
168
+ }
169
+ ngOnDestroy() {
170
+ this.cleanupChart();
171
+ }
172
+ /**
173
+ * Loads D3.js dynamically
174
+ */
175
+ async loadD3() {
176
+ try {
177
+ this.d3 = await import('d3');
178
+ // If container is ready, create chart
179
+ if (this._initialized() && this.chartContainerEl()) {
180
+ this.createChart();
181
+ this._rendered.set(true);
182
+ }
183
+ }
184
+ catch (error) {
185
+ console.error('Failed to load D3.js:', error);
186
+ }
187
+ }
188
+ onSegmentClick(item) {
189
+ this.toggleSegmentVisibility(item.id);
190
+ this.segmentClick.emit(item);
191
+ }
192
+ /**
193
+ * Creates the donut chart
194
+ */
195
+ createChart() {
196
+ if (!this.d3 || !this.chartContainerEl()?.nativeElement)
197
+ return;
198
+ try {
199
+ const containerElement = this.chartContainerEl().nativeElement;
200
+ this.clearChart(containerElement);
201
+ const data = this.chartDataArray();
202
+ if (!data || data.length === 0) {
203
+ this.showNoDataMessage(containerElement);
204
+ return;
205
+ }
206
+ // Filter out hidden segments
207
+ const visibleData = data.filter((item) => !this.hiddenSegments.has(item.id));
208
+ // If all segments are hidden, show message
209
+ if (visibleData.length === 0) {
210
+ this.showAllSegmentsHiddenMessage(containerElement);
211
+ return;
212
+ }
213
+ const options = this.effectiveOptions();
214
+ const { width, height } = this.setupDimensions(containerElement, options);
215
+ this.renderDonutChart(containerElement, width, height, visibleData);
216
+ }
217
+ catch (error) {
218
+ console.error('Error creating donut chart:', error);
219
+ this.handleChartError();
220
+ }
221
+ }
222
+ /**
223
+ * Updates the chart with new data
224
+ */
225
+ updateChart() {
226
+ this.createChart(); // Recreate the chart with updated data
227
+ }
228
+ /**
229
+ * Clears the chart container
230
+ */
231
+ clearChart(container) {
232
+ this.d3.select(container).selectAll('svg').remove();
233
+ this._tooltipVisible.set(false);
234
+ }
235
+ /**
236
+ * Shows a message when no data is available
237
+ */
238
+ showNoDataMessage(container) {
239
+ const messageContainer = this.d3
240
+ .select(container)
241
+ .append('div')
242
+ .attr('class', 'ax-donut-chart-no-data-message')
243
+ .style('width', 'auto')
244
+ .style('text-align', 'center');
245
+ // Add an icon
246
+ messageContainer
247
+ .append('div')
248
+ .attr('class', 'ax-donut-chart-no-data-icon')
249
+ .html('<i class="fa-light fa-chart-pie-simple fa-2x"></i>');
250
+ // Add text message and help text
251
+ messageContainer.append('div').attr('class', 'ax-donut-chart-no-data-text').text('No data available');
252
+ messageContainer
253
+ .append('div')
254
+ .attr('class', 'ax-donut-chart-no-data-help')
255
+ .text('Please provide data in the correct format');
256
+ }
257
+ /**
258
+ * Shows a message when all segments are hidden
259
+ */
260
+ showAllSegmentsHiddenMessage(container) {
261
+ this.clearChart(container);
262
+ // Add a simple div to ensure proper positioning
263
+ const wrapper = this.d3
264
+ .select(container)
265
+ .append('div')
266
+ .style('position', 'relative')
267
+ .style('width', '100%')
268
+ .style('height', '100%');
269
+ const messageContainer = wrapper
270
+ .append('div')
271
+ .attr('class', 'ax-donut-chart-no-data-message')
272
+ .style('position', 'absolute')
273
+ .style('left', '50%')
274
+ .style('top', '50%')
275
+ .style('transform', 'translate(-50%, -50%)')
276
+ .style('text-align', 'center')
277
+ .style('z-index', '10')
278
+ .style('background-color', 'rgba(255, 255, 255, 0.95)')
279
+ .style('padding', '1.5rem')
280
+ .style('border-radius', '0.5rem')
281
+ .style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')
282
+ .style('width', '80%')
283
+ .style('max-width', '300px');
284
+ // Add an icon
285
+ messageContainer
286
+ .append('div')
287
+ .attr('class', 'ax-donut-chart-no-data-icon')
288
+ .style('color', 'var(--ax-text-muted, #999)')
289
+ .style('margin-bottom', '0.75rem')
290
+ .html('<i class="fa-light fa-eye-slash fa-2x"></i>');
291
+ // Add text message and help text
292
+ messageContainer
293
+ .append('div')
294
+ .attr('class', 'ax-donut-chart-no-data-text')
295
+ .style('font-size', '1rem')
296
+ .style('font-weight', '600')
297
+ .style('color', 'var(--ax-text-color, #333)')
298
+ .style('margin-bottom', '0.5rem')
299
+ .text('All segments are hidden');
300
+ messageContainer
301
+ .append('div')
302
+ .attr('class', 'ax-donut-chart-no-data-help')
303
+ .style('font-size', '0.8rem')
304
+ .style('color', 'var(--ax-text-muted, #999)')
305
+ .text('Click on a segment to show data');
306
+ }
307
+ /**
308
+ * Setups chart dimensions based on container and options
309
+ */
310
+ setupDimensions(container, options) {
311
+ // Get container dimensions or use defaults
312
+ const containerWidth = container.clientWidth || 400;
313
+ const containerHeight = container.clientHeight || 400;
314
+ // Ensure minimum dimensions for the chart
315
+ const minDim = 200;
316
+ const width = Math.max(options?.width || containerWidth, minDim);
317
+ const height = Math.max(options?.height || containerHeight, minDim);
318
+ return { width, height };
319
+ }
320
+ /**
321
+ * Renders the donut chart with visible data
322
+ */
323
+ renderDonutChart(container, width, height, visibleData) {
324
+ const total = visibleData.reduce((sum, item) => sum + item.value, 0);
325
+ // Create SVG container with filters
326
+ const svg = this.createSvgWithFilters(container, width, height);
327
+ // Create main chart group
328
+ this.svg = svg.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`);
329
+ // Create donut segments
330
+ this.createDonutSegments(width, height, visibleData, total);
331
+ // Add total in center
332
+ this.addCenterDisplay(total);
333
+ }
334
+ /**
335
+ * Create SVG element with filter definitions for shadows
336
+ */
337
+ createSvgWithFilters(container, width, height) {
338
+ const svg = this.d3
339
+ .select(container)
340
+ .append('svg')
341
+ .attr('width', '100%')
342
+ .attr('height', '100%')
343
+ .attr('viewBox', `0 0 ${width} ${height}`)
344
+ .attr('preserveAspectRatio', 'xMidYMid meet');
345
+ // Add drop shadow filter
346
+ const defs = svg.append('defs');
347
+ const filter = defs.append('filter').attr('id', 'ax-donut-chart-segment-shadow').attr('height', '130%');
348
+ filter.append('feGaussianBlur').attr('in', 'SourceAlpha').attr('stdDeviation', 2).attr('result', 'blur');
349
+ filter.append('feOffset').attr('in', 'blur').attr('dx', 1).attr('dy', 1).attr('result', 'offsetBlur');
350
+ const feComponentTransfer = filter
351
+ .append('feComponentTransfer')
352
+ .attr('in', 'offsetBlur')
353
+ .attr('result', 'offsetBlur');
354
+ feComponentTransfer.append('feFuncA').attr('type', 'linear').attr('slope', 0.3);
355
+ const feMerge = filter.append('feMerge');
356
+ feMerge.append('feMergeNode').attr('in', 'offsetBlur');
357
+ feMerge.append('feMergeNode').attr('in', 'SourceGraphic');
358
+ return svg;
359
+ }
360
+ /**
361
+ * Create and render the donut segments with animations
362
+ */
363
+ createDonutSegments(chartWidth, chartHeight, data, total) {
364
+ // Create pie layout
365
+ const pie = this.d3
366
+ .pie()
367
+ .value((d) => d.value)
368
+ .sort(null)
369
+ .padAngle(0.02);
370
+ // Calculate the radius of the donut chart
371
+ const radius = (Math.min(chartWidth, chartHeight) / 2) * 0.85;
372
+ // Calculate inner radius based on donutWidth percentage
373
+ const donutWidthPercent = this.effectiveOptions().donutWidth / 100;
374
+ const innerRadius = radius * (1 - donutWidthPercent);
375
+ // Create arc generator with the configured radius and corner radius
376
+ const arc = this.d3
377
+ .arc()
378
+ .innerRadius(innerRadius)
379
+ .outerRadius(radius * 0.95)
380
+ .cornerRadius(this.effectiveOptions().cornerRadius);
381
+ // Create hover arc for animation
382
+ const hoverArc = this.d3
383
+ .arc()
384
+ .innerRadius(innerRadius)
385
+ .outerRadius(radius + 10)
386
+ .cornerRadius(this.effectiveOptions().cornerRadius);
387
+ // Get animation options
388
+ const animationDuration = this.effectiveOptions().animationDuration;
389
+ const animationEasing = this.getEasingFunction(this.effectiveOptions().animationEasing);
390
+ // Generate pie data
391
+ this.pieData = pie(data);
392
+ // Add segments with animation
393
+ const segments = this.svg
394
+ .selectAll('path')
395
+ .data(this.pieData)
396
+ .enter()
397
+ .append('path')
398
+ .attr('class', 'ax-donut-chart-segment')
399
+ .attr('fill', (d, i) => this.getColor(i))
400
+ .style('opacity', 0)
401
+ .style('cursor', 'pointer') // Add cursor pointer to segments
402
+ .on('mouseenter', (event, d) => {
403
+ if (!this.effectiveOptions().showTooltip)
404
+ return;
405
+ this.handleSegmentHover(event, d, hoverArc, arc, total);
406
+ })
407
+ .on('mouseleave', (event, d) => {
408
+ this.handleSegmentLeave(event, d, arc);
409
+ })
410
+ .on('mousemove', (event) => {
411
+ if (this._tooltipVisible()) {
412
+ this.updateTooltipPosition(event);
413
+ }
414
+ })
415
+ .on('click', (event, d) => {
416
+ this.onSegmentClick(d.data);
417
+ });
418
+ // Animate segments
419
+ segments
420
+ .transition()
421
+ .duration(animationDuration)
422
+ .ease(animationEasing)
423
+ .delay((d, i) => i * 50)
424
+ .style('opacity', 1)
425
+ .attrTween('d', (d) => {
426
+ const interpolate = this.d3.interpolate({ startAngle: d.startAngle, endAngle: d.startAngle }, d);
427
+ return (t) => arc(interpolate(t));
428
+ });
429
+ }
430
+ /**
431
+ * Gets the appropriate D3 easing function based on the option string
432
+ */
433
+ getEasingFunction(easing) {
434
+ switch (easing) {
435
+ case 'linear':
436
+ return this.d3.easeLinear;
437
+ case 'ease':
438
+ return this.d3.easePolyInOut;
439
+ case 'ease-in':
440
+ return this.d3.easePolyIn;
441
+ case 'ease-out':
442
+ return this.d3.easePolyOut;
443
+ case 'ease-in-out':
444
+ return this.d3.easePolyInOut;
445
+ case 'cubic':
446
+ return this.d3.easeCubic;
447
+ case 'cubic-in':
448
+ return this.d3.easeCubicIn;
449
+ case 'cubic-out':
450
+ return this.d3.easeCubicOut;
451
+ case 'cubic-in-out':
452
+ return this.d3.easeCubicInOut;
453
+ default:
454
+ return this.d3.easeCubicOut; // Default easing
455
+ }
456
+ }
457
+ /**
458
+ * Handle hover effects on a segment
459
+ */
460
+ handleSegmentHover(event, d, hoverArc, normalArc, total) {
461
+ const segmentData = d.data;
462
+ const percentage = ((segmentData.value / total) * 100).toFixed(1);
463
+ const segmentColor = this.d3.select(event.currentTarget).attr('fill');
464
+ // Update tooltip data
465
+ this._tooltipData.set({
466
+ title: segmentData.name,
467
+ value: segmentData.value,
468
+ percentage: `${percentage}%`,
469
+ color: segmentColor,
470
+ });
471
+ // Show tooltip
472
+ this._tooltipVisible.set(true);
473
+ this.updateTooltipPosition(event);
474
+ this.cdr.detectChanges();
475
+ // Emit segment hover event
476
+ this.segmentHover.emit(segmentData);
477
+ // Apply hover effect
478
+ this.d3
479
+ .select(event.currentTarget)
480
+ .transition()
481
+ .duration(200)
482
+ .attr('d', (d) => hoverArc(d));
483
+ }
484
+ /**
485
+ * Handles mouse leave from segments
486
+ */
487
+ handleSegmentLeave(event, d, normalArc) {
488
+ // Hide tooltip
489
+ this._tooltipVisible.set(false);
490
+ this.cdr.detectChanges();
491
+ // Emit null to indicate no segment is hovered
492
+ this.segmentHover.emit(null);
493
+ // Remove hover effect
494
+ this.d3
495
+ .select(event.currentTarget)
496
+ .transition()
497
+ .duration(200)
498
+ .attr('d', (d) => normalArc(d));
499
+ }
500
+ /**
501
+ * Updates tooltip position
502
+ * Ensures the tooltip is visible by adjusting position when near edges
503
+ */
504
+ updateTooltipPosition(event) {
505
+ const container = this.chartContainerEl().nativeElement.getBoundingClientRect();
506
+ const x = event.clientX - container.left;
507
+ const y = event.clientY - container.top;
508
+ // Get container dimensions to check if we're near the edge
509
+ const containerWidth = container.width;
510
+ const containerHeight = container.height;
511
+ // Tooltip dimensions approximation (can't get exact dimensions without rendering)
512
+ const tooltipWidth = 150; // Approximate width of tooltip
513
+ const tooltipHeight = 100; // Approximate height of tooltip
514
+ // Calculate position with edge detection
515
+ let tooltipX = x;
516
+ let tooltipY = y;
517
+ // Check if we're too close to the right edge
518
+ const rightEdgeDistance = containerWidth - x;
519
+ if (rightEdgeDistance < tooltipWidth + 20) {
520
+ // Place tooltip to the left of the cursor with no gap
521
+ tooltipX = x - tooltipWidth + 20; // Overlap more with cursor position
522
+ }
523
+ // Check if we're too close to the bottom edge
524
+ const bottomEdgeDistance = containerHeight - y;
525
+ if (bottomEdgeDistance < tooltipHeight + 10) {
526
+ // Move tooltip up if near bottom edge
527
+ tooltipY = y - tooltipHeight;
528
+ }
529
+ this._tooltipPosition.set({
530
+ x: tooltipX,
531
+ y: tooltipY,
532
+ });
533
+ this.cdr.detectChanges();
534
+ }
535
+ /**
536
+ * Toggles the visibility of a segment
537
+ */
538
+ toggleSegmentVisibility(id) {
539
+ if (this.hiddenSegments.has(id)) {
540
+ this.hiddenSegments.delete(id);
541
+ }
542
+ else {
543
+ this.hiddenSegments.add(id);
544
+ }
545
+ // Hide tooltip when toggling segments
546
+ this._tooltipVisible.set(false);
547
+ this.updateChart();
548
+ }
549
+ /**
550
+ * Adds center display with total value
551
+ */
552
+ addCenterDisplay(total) {
553
+ if (!this.svg)
554
+ return;
555
+ // Calculate appropriate font sizes based on chart dimensions
556
+ const chartContainerWidth = this.chartContainerEl().nativeElement.clientWidth;
557
+ const baseFontSize = Math.max(1.4, Math.min(2.4, chartContainerWidth / 200)); // Scale between 1.4rem and 2.4rem
558
+ const subTextFontSize = baseFontSize * 0.5;
559
+ // Create group for the total display
560
+ const totalDisplay = this.svg
561
+ .append('g')
562
+ .attr('class', 'ax-donut-chart-total-display')
563
+ .attr('text-anchor', 'middle');
564
+ // Add total value
565
+ totalDisplay
566
+ .append('text')
567
+ .attr('class', 'ax-donut-chart-total-value')
568
+ .style('font-size', `${baseFontSize}rem`)
569
+ .style('font-weight', '600')
570
+ .style('fill', 'currentColor')
571
+ .text(total.toLocaleString());
572
+ // Add label
573
+ totalDisplay
574
+ .append('text')
575
+ .attr('class', 'ax-donut-chart-total-label')
576
+ .attr('dy', '1.4em')
577
+ .style('font-size', `${subTextFontSize}rem`)
578
+ .style('fill', 'currentColor')
579
+ .style('fill-opacity', '0.8')
580
+ .text('Total');
581
+ }
582
+ /**
583
+ * Handles chart rendering errors
584
+ */
585
+ handleChartError() {
586
+ const container = this.chartContainerEl()?.nativeElement;
587
+ if (container) {
588
+ this.showNoDataMessage(container);
589
+ }
590
+ }
591
+ /**
592
+ * Cleans up chart resources
593
+ */
594
+ cleanupChart() {
595
+ if (this.svg) {
596
+ this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
597
+ this.svg = null;
598
+ this.pieData = [];
599
+ }
600
+ this.hiddenSegments.clear();
601
+ this._tooltipVisible.set(false);
602
+ }
603
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: AXDonutChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
604
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.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 }], ngImport: i0, template: "<div class=\"ax-donut-chart\" #chartContainer>\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: [":host{display:block;width:100%;height:100%}.ax-donut-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.ax-donut-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.ax-donut-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);font-family:var(--ax-font-family, system-ui, sans-serif);background-color:#ffffffe6;padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px #00000014;width:80%;max-width:300px}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-icon{color:var(--ax-text-muted, #999);margin-bottom:.75rem}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-text{font-size:1rem;font-weight:600;color:var(--ax-text-color, #333);margin-bottom:.5rem}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-help{font-size:.8rem;color:var(--ax-text-muted, #999)}.ax-donut-chart-segment{cursor:pointer;transition:all .3s cubic-bezier(.25,.8,.25,1);stroke:#fff;stroke-width:1.5px;filter:drop-shadow(0px 1px 2px rgba(0,0,0,.1))}.ax-donut-chart-segment:hover{opacity:.92;filter:drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.01)}.ax-donut-chart-highlighted{opacity:1;filter:brightness(1.05) drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.02)}.ax-donut-chart-dimmed{opacity:.4}.ax-donut-chart-total-display{pointer-events:none}.ax-donut-chart-total-value{fill:currentColor}.ax-donut-chart-total-label{fill:currentColor;opacity:.8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
605
+ }
606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: AXDonutChartComponent, decorators: [{
607
+ type: Component,
608
+ args: [{ selector: 'ax-donut-chart', standalone: true, imports: [CommonModule, AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-donut-chart\" #chartContainer>\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: [":host{display:block;width:100%;height:100%}.ax-donut-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.ax-donut-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.ax-donut-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);font-family:var(--ax-font-family, system-ui, sans-serif);background-color:#ffffffe6;padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px #00000014;width:80%;max-width:300px}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-icon{color:var(--ax-text-muted, #999);margin-bottom:.75rem}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-text{font-size:1rem;font-weight:600;color:var(--ax-text-color, #333);margin-bottom:.5rem}.ax-donut-chart-no-data-message .ax-donut-chart-no-data-help{font-size:.8rem;color:var(--ax-text-muted, #999)}.ax-donut-chart-segment{cursor:pointer;transition:all .3s cubic-bezier(.25,.8,.25,1);stroke:#fff;stroke-width:1.5px;filter:drop-shadow(0px 1px 2px rgba(0,0,0,.1))}.ax-donut-chart-segment:hover{opacity:.92;filter:drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.01)}.ax-donut-chart-highlighted{opacity:1;filter:brightness(1.05) drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.02)}.ax-donut-chart-dimmed{opacity:.4}.ax-donut-chart-total-display{pointer-events:none}.ax-donut-chart-total-value{fill:currentColor}.ax-donut-chart-total-label{fill:currentColor;opacity:.8}\n"] }]
609
+ }], ctorParameters: () => [] });
610
+
611
+ /**
612
+ * Generated bundle index. Do not edit.
613
+ */
614
+
615
+ export { AXDonutChartColors, AXDonutChartComponent, AXDonutChartDefaultConfig, AX_DONUT_CHART_CONFIG, donutChartConfig };
616
+ //# sourceMappingURL=acorex-charts-donut-chart.mjs.map