@acorex/charts 19.15.0-next.22 → 19.15.0-next.25

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.
@@ -1,6 +1,7 @@
1
1
  import { NXComponent } from '@acorex/cdk/common';
2
2
  import { AX_CHART_COLOR_PALETTE, getChartColor } from '@acorex/charts';
3
3
  import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
4
+ import { AXPlatform } from '@acorex/core/platform';
4
5
  import { CommonModule } from '@angular/common';
5
6
  import * as i0 from '@angular/core';
6
7
  import { InjectionToken, inject, input, output, viewChild, signal, computed, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
@@ -75,6 +76,8 @@ class AXLineChartComponent extends NXComponent {
75
76
  });
76
77
  _initialized = signal(false);
77
78
  _rendered = signal(false);
79
+ hiddenSeries = new Set();
80
+ _fullNormalizedData = [];
78
81
  tooltipVisible = this._tooltipVisible.asReadonly();
79
82
  tooltipPosition = this._tooltipPosition.asReadonly();
80
83
  tooltipData = this._tooltipData.asReadonly();
@@ -82,6 +85,8 @@ class AXLineChartComponent extends NXComponent {
82
85
  configToken = inject(AX_LINE_CHART_CONFIG);
83
86
  // Inject the chart colors
84
87
  chartColors = inject(AX_CHART_COLOR_PALETTE);
88
+ // Inject AXPlatform
89
+ platform = inject(AXPlatform);
85
90
  effectiveOptions = computed(() => {
86
91
  return {
87
92
  ...this.configToken,
@@ -92,7 +97,16 @@ class AXLineChartComponent extends NXComponent {
92
97
  this.loadD3();
93
98
  }
94
99
  #effect = effect(() => {
95
- this.data();
100
+ const rawData = this.data();
101
+ if (Array.isArray(rawData)) {
102
+ this._fullNormalizedData = rawData.map((s, i) => ({ ...s, originalIndex: i }));
103
+ }
104
+ else if (rawData && 'data' in rawData) {
105
+ this._fullNormalizedData = [{ ...rawData, originalIndex: 0 }];
106
+ }
107
+ else {
108
+ this._fullNormalizedData = [];
109
+ }
96
110
  this.effectiveOptions();
97
111
  if (this._rendered()) {
98
112
  this.updateChart();
@@ -108,6 +122,68 @@ class AXLineChartComponent extends NXComponent {
108
122
  ngOnDestroy() {
109
123
  this.cleanupChart();
110
124
  }
125
+ // AXChartLegendCompatible Implementation
126
+ getLegendItems() {
127
+ const totalSum = this._fullNormalizedData.reduce((sum, series) => {
128
+ return sum + series.data.reduce((sSum, p) => sSum + p.y, 0);
129
+ }, 0);
130
+ return this._fullNormalizedData.map((series) => {
131
+ const seriesSum = series.data.reduce((sum, p) => sum + p.y, 0);
132
+ const percentage = totalSum > 0 ? (seriesSum / totalSum) * 100 : 0;
133
+ return {
134
+ id: series.id || series.label || `series-${series.originalIndex}`,
135
+ name: series.label || `Series ${series.originalIndex + 1}`,
136
+ value: seriesSum,
137
+ color: series.lineColor || getChartColor(series.originalIndex, this.chartColors),
138
+ hidden: this.hiddenSeries.has(series.id || series.label || `series-${series.originalIndex}`),
139
+ percentage: parseFloat(percentage.toFixed(2)),
140
+ };
141
+ });
142
+ }
143
+ highlightSegment(id) {
144
+ if (!this.svg)
145
+ return;
146
+ const allSeriesGroups = this.svg.selectAll('g.ax-line-chart-series');
147
+ const allPointsGroups = this.svg.selectAll('g.ax-line-chart-points');
148
+ if (id === null) {
149
+ allSeriesGroups.classed('ax-chart-dimmed', false).style('opacity', 1);
150
+ allPointsGroups.classed('ax-chart-dimmed', false).style('opacity', 1);
151
+ allSeriesGroups
152
+ .selectAll('.ax-line-chart-line')
153
+ .attr('stroke-width', this.effectiveOptions().lineWidth ?? 2)
154
+ .classed('ax-chart-highlighted-path', false);
155
+ allPointsGroups
156
+ .selectAll('circle.ax-line-chart-point')
157
+ .attr('r', this.effectiveOptions().pointRadius ?? 4)
158
+ .classed('ax-chart-highlighted-point', false);
159
+ return;
160
+ }
161
+ allSeriesGroups.classed('ax-chart-dimmed', true).style('opacity', 0.3);
162
+ allPointsGroups.classed('ax-chart-dimmed', true).style('opacity', 0.3);
163
+ const targetSeriesGroup = this.svg.selectAll(`g.ax-line-chart-series[data-series-identifier="${id}"]`);
164
+ const targetPointsGroup = this.svg.selectAll(`g.ax-line-chart-points[data-series-identifier="${id}"]`);
165
+ targetSeriesGroup.classed('ax-chart-dimmed', false).style('opacity', 1);
166
+ targetPointsGroup.classed('ax-chart-dimmed', false).style('opacity', 1);
167
+ targetSeriesGroup
168
+ .selectAll('.ax-line-chart-line')
169
+ .attr('stroke-width', (this.effectiveOptions().lineWidth ?? 2) * 1.5)
170
+ .classed('ax-chart-highlighted-path', true);
171
+ targetPointsGroup
172
+ .selectAll('circle.ax-line-chart-point')
173
+ .attr('r', (this.effectiveOptions().pointRadius ?? 4) * 1.5)
174
+ .classed('ax-chart-highlighted-point', true);
175
+ }
176
+ toggleSegment(id) {
177
+ const seriesId = id;
178
+ if (this.hiddenSeries.has(seriesId)) {
179
+ this.hiddenSeries.delete(seriesId);
180
+ }
181
+ else {
182
+ this.hiddenSeries.add(seriesId);
183
+ }
184
+ this.updateChart();
185
+ return !this.hiddenSeries.has(seriesId);
186
+ }
111
187
  async loadD3() {
112
188
  try {
113
189
  this.d3 = await import('d3');
@@ -124,24 +200,37 @@ class AXLineChartComponent extends NXComponent {
124
200
  if (!this.d3 || !this.chartContainerEl()?.nativeElement)
125
201
  return;
126
202
  const containerElement = this.chartContainerEl().nativeElement;
127
- const chartData = this.data();
128
- let normalizedData = [];
129
- if (Array.isArray(chartData)) {
130
- normalizedData = chartData;
131
- }
132
- else if (chartData && 'data' in chartData) {
133
- normalizedData = [chartData];
134
- }
135
- this.d3.select(containerElement).selectAll('svg').remove();
136
- if (normalizedData.length === 0 || normalizedData.some((series) => !series.data || series.data.length === 0)) {
203
+ const allSeriesData = this._fullNormalizedData;
204
+ this.d3.select(containerElement).selectAll('*').remove();
205
+ if (allSeriesData.length === 0 || allSeriesData.every((series) => !series.data || series.data.length === 0)) {
137
206
  this.showNoDataMessage(containerElement);
138
207
  return;
139
208
  }
209
+ const visibleDataForRendering = allSeriesData.filter((series) => !this.hiddenSeries.has(series.id || series.label || `series-${series.originalIndex}`));
210
+ if (visibleDataForRendering.length === 0) {
211
+ // All data is present, but all series are hidden by the legend
212
+ this.showAllSeriesHiddenMessage(containerElement);
213
+ // Still setup basic SVG and chart group for consistency if needed, or return
214
+ // For now, we will show the message and return, as scales/axes can't be drawn.
215
+ return;
216
+ }
217
+ // Scales should be based on potentially visible data to avoid errors with empty domains
218
+ // If all are hidden, scales might become an issue. Let's use all data for scales,
219
+ // and rendering will handle visibility.
220
+ const dataForScales = allSeriesData.flatMap((s) => s.data).length > 0 ? allSeriesData : [{ data: [{ x: 0, y: 0 }] }];
140
221
  const chartOptions = this.effectiveOptions();
141
222
  this.setupDimensions(containerElement, chartOptions);
142
- this.setupScales(normalizedData);
223
+ // Use allSeriesData for domain calculation to keep scales consistent even if some series are hidden
224
+ // but filter out series with no data points for scale calculation.
225
+ const dataForScaleSetup = allSeriesData.filter((s) => s.data && s.data.length > 0);
226
+ if (dataForScaleSetup.length === 0) {
227
+ // If after filtering, there's no data with points (e.g. all series have empty data arrays)
228
+ this.showNoDataMessage(containerElement);
229
+ return;
230
+ }
231
+ this.setupScales(dataForScaleSetup);
143
232
  this.createAxes(chartOptions);
144
- this.renderLines(normalizedData);
233
+ this.renderLines(allSeriesData); // RenderLines will handle hiding based on hiddenSeries
145
234
  }
146
235
  updateChart() {
147
236
  this.createChart();
@@ -213,15 +302,21 @@ class AXLineChartComponent extends NXComponent {
213
302
  }
214
303
  }
215
304
  setupScales(data) {
305
+ // Expects already filtered data for scales
216
306
  const chartOptions = this.effectiveOptions();
217
307
  const padding = chartOptions.axisPadding ?? 0;
218
308
  const paddingMultiplier = padding / 100;
219
- const allDataPoints = data.flatMap((series) => series.data);
309
+ // Ensure data is not empty for scale domain calculation
310
+ const allDataPoints = data.length > 0 ? data.flatMap((series) => series.data) : [{ x: 0, y: 0 }];
311
+ if (allDataPoints.length === 0) {
312
+ // Should ideally not happen if createChart filters correctly
313
+ allDataPoints.push({ x: 0, y: 0 }); // Default fallback to prevent crash with empty domain
314
+ }
220
315
  const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
221
316
  const allDates = !allNumericX && allDataPoints.every((d) => !isNaN(new Date(d.x).getTime()));
222
317
  if (allNumericX) {
223
- const xMin = this.d3.min(allDataPoints, (d) => d.x) || 0;
224
- const xMax = this.d3.max(allDataPoints, (d) => d.x) || 0;
318
+ const xMin = this.d3.min(allDataPoints, (d) => d.x) ?? 0;
319
+ const xMax = this.d3.max(allDataPoints, (d) => d.x) ?? 0;
225
320
  if (xMin === xMax) {
226
321
  this.xScale = this.d3
227
322
  .scaleLinear()
@@ -233,8 +328,8 @@ class AXLineChartComponent extends NXComponent {
233
328
  }
234
329
  }
235
330
  else if (allDates) {
236
- const xMin = this.d3.min(allDataPoints, (d) => new Date(d.x)) || new Date();
237
- const xMax = this.d3.max(allDataPoints, (d) => new Date(d.x)) || new Date();
331
+ const xMin = this.d3.min(allDataPoints, (d) => new Date(d.x)) ?? new Date();
332
+ const xMax = this.d3.max(allDataPoints, (d) => new Date(d.x)) ?? new Date();
238
333
  if (xMin.getTime() === xMax.getTime()) {
239
334
  const oneDayMs = 86400000;
240
335
  this.xScale = this.d3
@@ -255,8 +350,11 @@ class AXLineChartComponent extends NXComponent {
255
350
  .paddingOuter(0);
256
351
  }
257
352
  const yAxisStartsAtZero = chartOptions.yAxisStartsAtZero !== false;
258
- const yMin = yAxisStartsAtZero ? 0 : this.d3.min(allDataPoints, (d) => d.y) || 0;
259
- const yMax = this.d3.max(allDataPoints, (d) => d.y) || 0;
353
+ let yMin = this.d3.min(allDataPoints, (d) => d.y) ?? 0;
354
+ if (yAxisStartsAtZero) {
355
+ yMin = Math.min(0, yMin);
356
+ }
357
+ const yMax = this.d3.max(allDataPoints, (d) => d.y) ?? 0;
260
358
  const yRange = yMax - yMin;
261
359
  this.yScale = this.d3
262
360
  .scaleLinear()
@@ -269,6 +367,7 @@ class AXLineChartComponent extends NXComponent {
269
367
  const showYAxis = options.showYAxis !== false;
270
368
  const showGrid = options.showGrid !== false;
271
369
  const isBandScale = this.xScale.bandwidth !== undefined;
370
+ const isRtl = this.platform.isRtl();
272
371
  const axesGroup = this.chart.append('g').attr('class', 'ax-line-chart-axes');
273
372
  if (showXAxis) {
274
373
  this.xAxis = axesGroup
@@ -283,13 +382,14 @@ class AXLineChartComponent extends NXComponent {
283
382
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
284
383
  .attr('stroke-dasharray', '2,2')
285
384
  .attr('stroke-opacity', '0.5');
385
+ const dynamicXAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.width / 45)));
286
386
  this.xAxis.selectAll('text').attr('style', `
287
- font-size: 11px;
387
+ font-size: ${dynamicXAxisTickFontSize}px;
288
388
  font-weight: 400;
289
389
  fill: rgba(var(--ax-comp-line-chart-labels-color), 0.7);
290
390
  `);
291
391
  if (options.xAxisLabel) {
292
- const labelY = this.height + this.margin.bottom * 0.65;
392
+ const labelY = this.height + this.margin.bottom * 0.8;
293
393
  axesGroup
294
394
  .append('text')
295
395
  .attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
@@ -298,9 +398,8 @@ class AXLineChartComponent extends NXComponent {
298
398
  .attr('x', this.width / 2)
299
399
  .attr('y', labelY)
300
400
  .attr('transform', 'translate(0, 5)')
301
- .attr('font-family', 'var(--ax-font-family, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)')
302
401
  .attr('style', `
303
- font-size: 13px;
402
+ font-size: 14px;
304
403
  font-weight: 500;
305
404
  fill: rgb(var(--ax-comp-line-chart-text-color));
306
405
  pointer-events: none;
@@ -320,14 +419,18 @@ class AXLineChartComponent extends NXComponent {
320
419
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
321
420
  .attr('stroke-dasharray', '2,2')
322
421
  .attr('stroke-opacity', '0.5');
323
- this.yAxis.selectAll('text').attr('style', `
324
- font-size: 11px;
422
+ const dynamicYAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.height / 30)));
423
+ const yTickTexts = this.yAxis.selectAll('text').attr('style', `
424
+ font-size: ${dynamicYAxisTickFontSize}px;
325
425
  font-weight: 400;
326
426
  fill: rgba(var(--ax-comp-line-chart-labels-color), 0.7);
327
427
  `);
428
+ if (isRtl) {
429
+ yTickTexts.attr('text-anchor', 'start');
430
+ }
328
431
  if (options.yAxisLabel) {
329
432
  const labelX = -this.height / 2;
330
- const labelY = -this.margin.left * 0.65;
433
+ const labelY = -this.margin.left * 0.8;
331
434
  axesGroup
332
435
  .append('text')
333
436
  .attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
@@ -336,9 +439,8 @@ class AXLineChartComponent extends NXComponent {
336
439
  .attr('transform', 'rotate(-90) translate(-5, 0)')
337
440
  .attr('x', labelX)
338
441
  .attr('y', labelY)
339
- .attr('font-family', 'var(--ax-font-family, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)')
340
442
  .attr('style', `
341
- font-size: 13px;
443
+ font-size: 14px;
342
444
  font-weight: 500;
343
445
  fill: rgb(var(--ax-comp-line-chart-text-color));
344
446
  pointer-events: none;
@@ -388,7 +490,7 @@ class AXLineChartComponent extends NXComponent {
388
490
  }
389
491
  }
390
492
  }
391
- renderLines(data) {
493
+ renderLines(allSeriesData) {
392
494
  const isBandScale = this.xScale.bandwidth !== undefined;
393
495
  const getX = (d) => {
394
496
  if (isBandScale) {
@@ -413,34 +515,35 @@ class AXLineChartComponent extends NXComponent {
413
515
  if (this.effectiveOptions().smoothLine !== false) {
414
516
  areaGenerator.curve(this.d3.curveMonotoneX);
415
517
  }
416
- // Add class to chart container to disable pointer events during animation
417
518
  this.svg.attr('class', 'ax-line-chart-animating');
418
519
  const allSeriesGroup = this.chart
419
520
  .append('g')
420
521
  .attr('class', 'ax-line-chart-all-series')
421
- .attr('pointer-events', 'none'); // Disable pointer events during animation
522
+ .attr('pointer-events', 'none');
422
523
  const allPointsGroup = this.chart
423
524
  .append('g')
424
525
  .attr('class', 'ax-line-chart-all-points')
425
- .attr('pointer-events', 'none'); // Disable pointer events during animation
526
+ .attr('pointer-events', 'none');
426
527
  if (this.effectiveOptions().showCrosshair === true) {
427
- const crosshairGroup = this.chart
428
- .append('g')
429
- .attr('class', 'ax-line-chart-crosshair')
430
- .attr('pointer-events', 'none');
528
+ this.chart.append('g').attr('class', 'ax-line-chart-crosshair').attr('pointer-events', 'none');
431
529
  }
432
- // Get animation options
433
530
  const animationDuration = this.effectiveOptions().animationDuration;
434
531
  const animationEasing = this.getEasingFunction(this.effectiveOptions().animationEasing);
435
- data.forEach((series, seriesIndex) => {
532
+ allSeriesData.forEach((series) => {
533
+ const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
534
+ if (this.hiddenSeries.has(seriesIdentifier)) {
535
+ this.chart.selectAll(`.ax-line-chart-series[data-series-identifier="${seriesIdentifier}"]`).remove();
536
+ this.chart.selectAll(`.ax-line-chart-points[data-series-identifier="${seriesIdentifier}"]`).remove();
537
+ return;
538
+ }
436
539
  if (!series.data || series.data.length === 0)
437
540
  return;
438
541
  const seriesGroup = allSeriesGroup
439
542
  .append('g')
440
- .attr('class', `ax-line-chart-series ax-line-chart-series-${seriesIndex}`)
441
- .attr('data-series', series.label || `series-${seriesIndex}`)
442
- .attr('pointer-events', 'none'); // Disable pointer events during animation
443
- const lineColor = series.lineColor || getChartColor(seriesIndex, this.chartColors);
543
+ .attr('class', `ax-line-chart-series ax-line-chart-series-${series.originalIndex}`)
544
+ .attr('data-series-identifier', seriesIdentifier)
545
+ .attr('pointer-events', 'none');
546
+ const lineColor = series.lineColor || getChartColor(series.originalIndex, this.chartColors);
444
547
  const fillColor = series.fillColor || lineColor;
445
548
  const line = seriesGroup
446
549
  .append('path')
@@ -453,8 +556,7 @@ class AXLineChartComponent extends NXComponent {
453
556
  .attr('d', lineGenerator)
454
557
  .attr('fill', 'none')
455
558
  .attr('style', 'transition: stroke-width 0.3s ease;')
456
- .attr('pointer-events', 'none'); // Disable pointer events during animation
457
- // Setup hover listener
559
+ .attr('pointer-events', 'none');
458
560
  line
459
561
  .on('mouseenter', () => {
460
562
  line
@@ -477,7 +579,6 @@ class AXLineChartComponent extends NXComponent {
477
579
  .ease(animationEasing)
478
580
  .attr('stroke-dashoffset', 0)
479
581
  .on('end', function () {
480
- // Enable pointer events after animation completes
481
582
  line.attr('pointer-events', 'all');
482
583
  });
483
584
  if (this.effectiveOptions().fillArea) {
@@ -489,8 +590,7 @@ class AXLineChartComponent extends NXComponent {
489
590
  .attr('opacity', 0)
490
591
  .attr('d', areaGenerator)
491
592
  .attr('style', 'transition: opacity 0.3s ease;')
492
- .attr('pointer-events', 'none'); // Disable pointer events during animation
493
- // Setup hover listener
593
+ .attr('pointer-events', 'none');
494
594
  area
495
595
  .on('mouseenter', () => {
496
596
  area
@@ -510,17 +610,18 @@ class AXLineChartComponent extends NXComponent {
510
610
  .ease(animationEasing)
511
611
  .attr('opacity', (this.effectiveOptions().fillOpacity ?? 20) / 100)
512
612
  .on('end', function () {
513
- // Enable pointer events after animation completes
514
613
  area.attr('pointer-events', 'all');
515
614
  });
516
615
  }
517
616
  });
518
617
  if (this.effectiveOptions().showPoints !== false) {
519
618
  const pointMap = new Map();
520
- data.forEach((series, seriesIndex) => {
521
- if (!series.data || series.data.length === 0)
619
+ allSeriesData.forEach((series) => {
620
+ const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
621
+ if (this.hiddenSeries.has(seriesIdentifier) || !series.data || series.data.length === 0) {
522
622
  return;
523
- const lineColor = series.lineColor || getChartColor(seriesIndex, this.chartColors);
623
+ }
624
+ const lineColor = series.lineColor || getChartColor(series.originalIndex, this.chartColors);
524
625
  const maxPoints = 100;
525
626
  const pointData = series.data.length > maxPoints ? this.getReducedDataPoints(series.data, maxPoints) : series.data;
526
627
  pointData.forEach((point) => {
@@ -530,26 +631,24 @@ class AXLineChartComponent extends NXComponent {
530
631
  if (!pointMap.has(key)) {
531
632
  pointMap.set(key, []);
532
633
  }
533
- pointMap.get(key)?.push({
534
- point,
535
- series,
536
- seriesIndex,
537
- });
634
+ pointMap.get(key)?.push({ point, series, originalIndex: series.originalIndex });
538
635
  });
539
636
  });
540
- // Create a counter to track animation completion
541
637
  let animationCounter = 0;
542
- const totalSeriesCount = data.length;
543
- data.forEach((series, seriesIndex) => {
544
- if (!series.data || series.data.length === 0)
638
+ const totalVisibleSeriesCount = allSeriesData.filter((s) => !this.hiddenSeries.has(s.id || s.label || `series-${s.originalIndex}`) && s.data && s.data.length > 0).length;
639
+ allSeriesData.forEach((series) => {
640
+ const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
641
+ if (this.hiddenSeries.has(seriesIdentifier) || !series.data || series.data.length === 0) {
545
642
  return;
546
- const lineColor = series.lineColor || getChartColor(seriesIndex, this.chartColors);
643
+ }
644
+ const lineColor = series.lineColor || getChartColor(series.originalIndex, this.chartColors);
547
645
  const pointsGroup = allPointsGroup
548
646
  .append('g')
549
- .attr('class', `ax-line-chart-points ax-line-chart-points-${seriesIndex}`)
550
- .attr('pointer-events', 'none'); // Disable pointer events during animation
647
+ .attr('class', `ax-line-chart-points ax-line-chart-points-${series.originalIndex}`)
648
+ .attr('data-series-identifier', seriesIdentifier)
649
+ .attr('pointer-events', 'none');
551
650
  pointsGroup
552
- .on('mouseenter', () => this.handlePointGroupEnter(seriesIndex, lineColor))
651
+ .on('mouseenter', () => this.handlePointGroupEnter(series.originalIndex, lineColor))
553
652
  .on('mouseleave', () => this.handlePointGroupLeave());
554
653
  const maxPoints = 100;
555
654
  const pointData = series.data.length > maxPoints ? this.getReducedDataPoints(series.data, maxPoints) : series.data;
@@ -568,7 +667,7 @@ class AXLineChartComponent extends NXComponent {
568
667
  .attr('data-x', (d) => d.x)
569
668
  .attr('data-y', (d) => d.y)
570
669
  .style('cursor', 'pointer')
571
- .attr('pointer-events', 'none') // Disable pointer events during animation
670
+ .attr('pointer-events', 'none')
572
671
  .on('mouseenter', (event, d) => {
573
672
  const x = getX(d);
574
673
  const y = this.yScale(d.y);
@@ -578,7 +677,7 @@ class AXLineChartComponent extends NXComponent {
578
677
  this.handleOverlappingPointsHover(event, overlappingPoints);
579
678
  }
580
679
  else {
581
- this.handlePointHover(event, d, series, seriesIndex);
680
+ this.handlePointHover(event, d, series, series.originalIndex);
582
681
  }
583
682
  this.d3
584
683
  .select(event.target)
@@ -614,17 +713,14 @@ class AXLineChartComponent extends NXComponent {
614
713
  }
615
714
  })
616
715
  .transition()
617
- .delay((d, i) => i * Math.min(50, animationDuration * 0.05) + animationDuration * 0.5)
618
- .duration(animationDuration * 0.3)
716
+ .delay((d, i) => i * Math.min(50, (animationDuration || 0) * 0.05) + (animationDuration || 0) * 0.5)
717
+ .duration((animationDuration || 0) * 0.3)
619
718
  .ease(animationEasing)
620
719
  .attr('r', this.effectiveOptions().pointRadius ?? 4)
621
720
  .on('end', (d, i, nodes) => {
622
- // When the last point finishes animating
623
721
  if (i === nodes.length - 1) {
624
722
  animationCounter++;
625
- // If this is the last series, enable all pointer events
626
- if (animationCounter >= totalSeriesCount) {
627
- // Enable pointer events for all elements after all animations have completed
723
+ if (animationCounter >= totalVisibleSeriesCount) {
628
724
  this.enablePointerEventsAfterAnimation();
629
725
  }
630
726
  }
@@ -632,18 +728,14 @@ class AXLineChartComponent extends NXComponent {
632
728
  });
633
729
  }
634
730
  else {
635
- // If not showing points, wait for line animations to complete
636
731
  setTimeout(() => {
637
- // Enable pointer events for all elements after animation duration
638
732
  this.enablePointerEventsAfterAnimation();
639
- }, animationDuration + 100);
733
+ }, animationDuration || 0 + 100);
640
734
  }
641
735
  }
642
736
  enablePointerEventsAfterAnimation() {
643
- // Guard against null chart or svg
644
737
  if (!this.chart || !this.svg)
645
738
  return;
646
- // Enable pointer events for all chart elements after animation completes
647
739
  this.chart.selectAll('.ax-line-chart-all-series').attr('pointer-events', 'all');
648
740
  this.chart.selectAll('.ax-line-chart-series').attr('pointer-events', 'all');
649
741
  this.chart.selectAll('.ax-line-chart-line').attr('pointer-events', 'all');
@@ -651,12 +743,11 @@ class AXLineChartComponent extends NXComponent {
651
743
  this.chart.selectAll('.ax-line-chart-all-points').attr('pointer-events', 'all');
652
744
  this.chart.selectAll('.ax-line-chart-points').attr('pointer-events', 'all');
653
745
  this.chart.selectAll('.ax-line-chart-point').attr('pointer-events', 'all');
654
- // Remove the animating class from the SVG
655
746
  this.svg.classed('ax-line-chart-animating', false);
656
747
  }
657
- handlePointGroupEnter(seriesIndex, color) {
748
+ handlePointGroupEnter(originalIndex, color) {
658
749
  this.chart
659
- .select(`.ax-line-chart-series-${seriesIndex} .ax-line-chart-line`)
750
+ .select(`.ax-line-chart-series-${originalIndex} .ax-line-chart-line`)
660
751
  .transition()
661
752
  .duration(150)
662
753
  .attr('stroke-width', (this.effectiveOptions().lineWidth ?? 2) * 1.5);
@@ -684,11 +775,11 @@ class AXLineChartComponent extends NXComponent {
684
775
  }
685
776
  return result;
686
777
  }
687
- handlePointHover(event, dataPoint, series, seriesIndex) {
778
+ handlePointHover(event, dataPoint, series, originalIndex) {
688
779
  if (this.effectiveOptions().showTooltip !== false) {
689
- const color = series.lineColor || getChartColor(seriesIndex, this.chartColors);
780
+ const color = series.lineColor || getChartColor(originalIndex, this.chartColors);
690
781
  this._tooltipData.set({
691
- title: dataPoint.tooltipLabel || series.tooltipLabel || series.label || `Series ${seriesIndex + 1}`,
782
+ title: dataPoint.tooltipLabel || series.tooltipLabel || series.label || `Series ${originalIndex + 1}`,
692
783
  value: dataPoint.y.toString(),
693
784
  seriesName: `x: ${dataPoint.x}`,
694
785
  color: color,
@@ -755,16 +846,11 @@ class AXLineChartComponent extends NXComponent {
755
846
  if (x + tooltipRect.width + 20 > container.width) {
756
847
  x = x - tooltipRect.width - 15;
757
848
  }
758
- // if (y + tooltipRect.height / 2 > container.height) {
759
- // y = container.height - tooltipRect.height / 2;
760
- // } else if (y - tooltipRect.height / 2 < 0) {
761
- // y = tooltipRect.height / 2;
762
- // }
763
849
  this._tooltipPosition.set({ x, y });
764
850
  }
765
851
  handlePointClick(event, dataPoint, series) {
766
852
  this.pointClick.emit({
767
- series: series,
853
+ series: series, // series still refers to the full series data with originalIndex
768
854
  point: dataPoint,
769
855
  event: {
770
856
  nativeEvent: event,
@@ -773,7 +859,6 @@ class AXLineChartComponent extends NXComponent {
773
859
  });
774
860
  }
775
861
  showNoDataMessage(containerElement) {
776
- // Clear existing contents first
777
862
  this.d3.select(containerElement).selectAll('*').remove();
778
863
  const messageContainer = this.d3
779
864
  .select(containerElement)
@@ -790,14 +875,12 @@ class AXLineChartComponent extends NXComponent {
790
875
  .style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')
791
876
  .style('width', '80%')
792
877
  .style('max-width', '300px');
793
- // Add an icon
794
878
  messageContainer
795
879
  .append('div')
796
880
  .attr('class', 'ax-line-chart-no-data-icon')
797
881
  .style('opacity', '0.6')
798
882
  .style('margin-bottom', '0.75rem')
799
883
  .html('<i class="fa-light fa-chart-line fa-2x"></i>');
800
- // Add text message
801
884
  messageContainer
802
885
  .append('div')
803
886
  .attr('class', 'ax-line-chart-no-data-text')
@@ -805,7 +888,6 @@ class AXLineChartComponent extends NXComponent {
805
888
  .style('font-weight', '600')
806
889
  .style('margin-bottom', '0.5rem')
807
890
  .text('No data available');
808
- // Add help text
809
891
  messageContainer
810
892
  .append('div')
811
893
  .attr('class', 'ax-line-chart-no-data-help')
@@ -813,6 +895,43 @@ class AXLineChartComponent extends NXComponent {
813
895
  .style('opacity', '0.6')
814
896
  .text('Please provide data to display the chart');
815
897
  }
898
+ showAllSeriesHiddenMessage(containerElement) {
899
+ this.d3.select(containerElement).selectAll('*').remove(); // Clear existing chart elements
900
+ const messageContainer = this.d3
901
+ .select(containerElement)
902
+ .append('div')
903
+ .attr('class', 'ax-line-chart-all-hidden-message') // Different class
904
+ .style('position', 'absolute')
905
+ .style('left', '50%')
906
+ .style('top', '50%')
907
+ .style('transform', 'translate(-50%, -50%)')
908
+ .style('text-align', 'center')
909
+ .style('background-color', 'rgb(var(--ax-comp-line-chart-bg-color))')
910
+ .style('padding', '1.5rem')
911
+ .style('border-radius', '0.5rem')
912
+ .style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')
913
+ .style('width', '80%')
914
+ .style('max-width', '350px'); // Slightly wider for longer text
915
+ messageContainer
916
+ .append('div')
917
+ .attr('class', 'ax-line-chart-all-hidden-icon')
918
+ .style('opacity', '0.6')
919
+ .style('margin-bottom', '0.75rem')
920
+ .html('<i class="fa-light fa-eye-slash fa-2x"></i>'); // Different icon
921
+ messageContainer
922
+ .append('div')
923
+ .attr('class', 'ax-line-chart-all-hidden-text')
924
+ .style('font-size', '1rem')
925
+ .style('font-weight', '600')
926
+ .style('margin-bottom', '0.5rem')
927
+ .text('All series are hidden');
928
+ messageContainer
929
+ .append('div')
930
+ .attr('class', 'ax-line-chart-all-hidden-help')
931
+ .style('font-size', '0.8rem')
932
+ .style('opacity', '0.6')
933
+ .text('Click legend items to show series in the chart.');
934
+ }
816
935
  handleOverlappingPointsHover(event, overlappingPoints) {
817
936
  if (this.effectiveOptions().showTooltip === false)
818
937
  return;
@@ -821,10 +940,10 @@ class AXLineChartComponent extends NXComponent {
821
940
  value: overlappingPoints[0].point.y.toString(),
822
941
  seriesName: '',
823
942
  color: '',
824
- items: overlappingPoints.map(({ point, series, seriesIndex }) => {
825
- const color = series.lineColor || getChartColor(seriesIndex, this.chartColors);
943
+ items: overlappingPoints.map(({ point, series, originalIndex }) => {
944
+ const color = series.lineColor || getChartColor(originalIndex, this.chartColors);
826
945
  return {
827
- label: series.label || `Series ${seriesIndex + 1}`,
946
+ label: series.label || `Series ${originalIndex + 1}`,
828
947
  value: point.y.toString(),
829
948
  color: color,
830
949
  };
@@ -837,9 +956,6 @@ class AXLineChartComponent extends NXComponent {
837
956
  this.showCrosshairLines(overlappingPoints[0].point);
838
957
  }
839
958
  }
840
- /**
841
- * Gets the appropriate D3 easing function based on the option string
842
- */
843
959
  getEasingFunction(easing) {
844
960
  switch (easing) {
845
961
  case 'linear':
@@ -861,13 +977,13 @@ class AXLineChartComponent extends NXComponent {
861
977
  case 'cubic-in-out':
862
978
  return this.d3.easeCubicInOut;
863
979
  default:
864
- return this.d3.easeCubicOut; // Default easing
980
+ return this.d3.easeCubicOut;
865
981
  }
866
982
  }
867
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
868
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.13", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-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: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: var(--ax-sys-color-lightest-surface);--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;color:rgb(var(--ax-comp-line-chart-text-color));background-color:rgb(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}ax-line-chart .ax-line-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px #00000014;width:80%;max-width:300px}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-help{font-size:.8rem;opacity:.6}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
984
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.14", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-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: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: var(--ax-sys-color-lightest-surface);--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;color:rgb(var(--ax-comp-line-chart-text-color));background-color:rgb(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}ax-line-chart .ax-line-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px #00000014;width:80%;max-width:300px}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-help{font-size:.8rem;opacity:.6}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
869
985
  }
870
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: AXLineChartComponent, decorators: [{
986
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AXLineChartComponent, decorators: [{
871
987
  type: Component,
872
988
  args: [{ selector: 'ax-line-chart', standalone: true, encapsulation: ViewEncapsulation.None, imports: [CommonModule, AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: var(--ax-sys-color-lightest-surface);--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;color:rgb(var(--ax-comp-line-chart-text-color));background-color:rgb(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}ax-line-chart .ax-line-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px #00000014;width:80%;max-width:300px}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-help{font-size:.8rem;opacity:.6}\n"] }]
873
989
  }] });