@acorex/charts 21.0.0-next.6 → 21.0.0-next.61

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,11 +1,7 @@
1
- import { NXComponent } from '@acorex/cdk/common';
2
- import { AX_CHART_COLOR_PALETTE, getChartColor, computeTooltipPosition } from '@acorex/charts';
1
+ import { AXChartComponent, AX_CHART_COLOR_PALETTE, formatLargeNumber, getChartColor, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
3
2
  import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
4
- import { AXPlatform } from '@acorex/core/platform';
5
3
  import * as i0 from '@angular/core';
6
- import { InjectionToken, inject, input, output, viewChild, signal, computed, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
7
- import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
8
- import { set } from 'lodash-es';
4
+ import { InjectionToken, input, output, viewChild, signal, inject, computed, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
9
5
 
10
6
  const AXLineChartDefaultConfig = {
11
7
  margins: {
@@ -41,11 +37,7 @@ const AXLineChartDefaultConfig = {
41
37
  };
42
38
  const AX_LINE_CHART_CONFIG = new InjectionToken('AX_LINE_CHART_CONFIG', {
43
39
  providedIn: 'root',
44
- factory: () => {
45
- const global = inject(AX_GLOBAL_CONFIG);
46
- set(global, 'chart.lineChart', AXLineChartDefaultConfig);
47
- return AXLineChartDefaultConfig;
48
- },
40
+ factory: () => AXLineChartDefaultConfig,
49
41
  });
50
42
  function lineChartConfig(config = {}) {
51
43
  const result = {
@@ -58,7 +50,7 @@ function lineChartConfig(config = {}) {
58
50
  /**
59
51
  * Line Chart Component for rendering data as lines with interactive hover effects and animations
60
52
  */
61
- class AXLineChartComponent extends NXComponent {
53
+ class AXLineChartComponent extends AXChartComponent {
62
54
  data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
63
55
  options = input({}, ...(ngDevMode ? [{ debugName: "options" }] : []));
64
56
  pointClick = output();
@@ -92,8 +84,6 @@ class AXLineChartComponent extends NXComponent {
92
84
  configToken = inject(AX_LINE_CHART_CONFIG);
93
85
  // Inject the chart colors
94
86
  chartColors = inject(AX_CHART_COLOR_PALETTE);
95
- // Inject AXPlatform
96
- platform = inject(AXPlatform);
97
87
  effectiveOptions = computed(() => {
98
88
  return {
99
89
  ...this.configToken,
@@ -115,22 +105,124 @@ class AXLineChartComponent extends NXComponent {
115
105
  ...this.effectiveOptions().messages,
116
106
  };
117
107
  }, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : []));
118
- // Tooltip gap in pixels for consistent spacing
108
+ // Layout & Dimensions
109
+ MIN_DIMENSION = 100;
110
+ MIN_CONTAINER_DIMENSION = 200;
111
+ DEFAULT_MARGIN_TOP = 20;
112
+ DEFAULT_MARGIN_RIGHT = 25;
113
+ DEFAULT_MARGIN_BOTTOM = 40;
114
+ DEFAULT_MARGIN_LEFT = 50;
115
+ MIN_MARGIN_BOTTOM = 45;
116
+ MIN_MARGIN_LEFT = 55;
117
+ MAX_EXTRA_MARGIN = 60;
118
+ // Styling & Visual
119
+ DEFAULT_LINE_WIDTH = 2;
120
+ DEFAULT_POINT_RADIUS = 4;
121
+ DEFAULT_FILL_OPACITY = 20;
122
+ LINE_HIGHLIGHT_MULTIPLIER = 1.5;
123
+ POINT_HIGHLIGHT_MULTIPLIER = 1.5;
124
+ POINT_STROKE_WIDTH = 1;
125
+ POINT_HOVER_STROKE_WIDTH = 2;
126
+ GRID_DASH_ARRAY = '2,2';
127
+ CROSSHAIR_DASH_ARRAY = '3,3';
128
+ CROSSHAIR_OPACITY = 0.7;
129
+ // Animation
130
+ HOVER_TRANSITION_DURATION = 150;
131
+ ANIMATION_END_BUFFER = 100;
132
+ POINT_ANIMATION_DELAY_MS = 50;
133
+ POINT_ANIMATION_DELAY_RATIO = 0.5;
134
+ POINT_ANIMATION_DURATION_RATIO = 0.3;
135
+ // Text & Labels
136
+ AXIS_LABEL_FONT_SIZE = 14;
137
+ CHAR_WIDTH_RATIO = 0.6;
138
+ FONT_WIDTH_MULTIPLIER = 0.6;
139
+ MAX_LABEL_LENGTH = 20;
140
+ MIN_FONT_SIZE_X = 11;
141
+ MAX_FONT_SIZE_X = 15;
142
+ MIN_FONT_SIZE_Y = 11;
143
+ MAX_FONT_SIZE_Y = 15;
144
+ FONT_PADDING = 10;
145
+ Y_AXIS_PADDING = 10;
146
+ // Data & Performance
147
+ MAX_POINTS_TO_RENDER = 100;
148
+ POINT_COORDINATE_PRECISION = 10;
149
+ MANY_ITEMS_THRESHOLD = 20;
150
+ VERY_MANY_ITEMS_THRESHOLD = 50;
151
+ // Tooltip
119
152
  TOOLTIP_GAP = 10;
120
- ngOnInit() {
121
- this.loadD3();
122
- }
123
- #effect = effect(() => {
124
- const rawData = this.data();
153
+ /**
154
+ * Normalizes input data to consistent array format with originalIndex
155
+ */
156
+ normalizeData(rawData) {
125
157
  if (Array.isArray(rawData)) {
126
- this._fullNormalizedData = rawData.map((s, i) => ({ ...s, originalIndex: i }));
158
+ return rawData.map((s, i) => ({ ...s, originalIndex: i }));
127
159
  }
128
- else if (rawData && 'data' in rawData) {
129
- this._fullNormalizedData = [{ ...rawData, originalIndex: 0 }];
160
+ if (rawData && 'data' in rawData) {
161
+ return [{ ...rawData, originalIndex: 0 }];
130
162
  }
131
- else {
132
- this._fullNormalizedData = [];
163
+ return [];
164
+ }
165
+ /**
166
+ * Gets unique identifier for a series
167
+ */
168
+ getSeriesIdentifier(series) {
169
+ return series.id || series.label || `series-${series.originalIndex}`;
170
+ }
171
+ /**
172
+ * Calculates dynamic font size based on available space
173
+ */
174
+ calculateDynamicFontSize(dimension, divisor, min, max) {
175
+ return Math.max(min, Math.min(max, Math.round(dimension / divisor)));
176
+ }
177
+ /**
178
+ * Creates a unique key for point coordinates (for overlap detection)
179
+ */
180
+ createPointKey(x, y) {
181
+ const precision = this.POINT_COORDINATE_PRECISION;
182
+ return `${Math.round(x * precision) / precision},${Math.round(y * precision) / precision}`;
183
+ }
184
+ /**
185
+ * Removes series elements from the chart
186
+ */
187
+ removeSeriesElements(seriesIdentifier) {
188
+ this.chart.selectAll(`.ax-line-chart-series[data-series-identifier="${seriesIdentifier}"]`).remove();
189
+ this.chart.selectAll(`.ax-line-chart-points[data-series-identifier="${seriesIdentifier}"]`).remove();
190
+ }
191
+ /**
192
+ * Truncates long labels with ellipsis
193
+ */
194
+ truncateLabel(label, maxLength = this.MAX_LABEL_LENGTH) {
195
+ if (label.length <= maxLength)
196
+ return label;
197
+ return label.substring(0, maxLength - 1) + '…';
198
+ }
199
+ /**
200
+ * Calculates maximum width needed for Y-axis tick labels
201
+ */
202
+ calculateMaxYAxisTickLabelWidth() {
203
+ const allSeriesData = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
204
+ let maxValue = 0;
205
+ let minValue = 0;
206
+ for (const series of allSeriesData) {
207
+ if (series.data && series.data.length > 0) {
208
+ for (const point of series.data) {
209
+ if (typeof point.y === 'number') {
210
+ maxValue = Math.max(maxValue, point.y);
211
+ minValue = Math.min(minValue, point.y);
212
+ }
213
+ }
214
+ }
133
215
  }
216
+ // Check both max and min (for negative values)
217
+ const maxAbsValue = Math.max(Math.abs(maxValue), Math.abs(minValue));
218
+ const tickLabelText = Number.isFinite(maxAbsValue) ? formatLargeNumber(maxAbsValue) : '00000';
219
+ return tickLabelText.length * this.AXIS_LABEL_FONT_SIZE * this.FONT_WIDTH_MULTIPLIER + this.FONT_PADDING;
220
+ }
221
+ ngOnInit() {
222
+ this.loadD3();
223
+ }
224
+ #effect = effect(() => {
225
+ this._fullNormalizedData = this.normalizeData(this.data());
134
226
  this.effectiveOptions();
135
227
  if (this._rendered()) {
136
228
  this.updateChart();
@@ -152,14 +244,15 @@ class AXLineChartComponent extends NXComponent {
152
244
  return sum + series.data.reduce((sSum, p) => sSum + p.y, 0);
153
245
  }, 0);
154
246
  return this._fullNormalizedData.map((series) => {
247
+ const seriesIdentifier = this.getSeriesIdentifier(series);
155
248
  const seriesSum = series.data.reduce((sum, p) => sum + p.y, 0);
156
249
  const percentage = totalSum > 0 ? (seriesSum / totalSum) * 100 : 0;
157
250
  return {
158
- id: series.id || series.label || `series-${series.originalIndex}`,
251
+ id: seriesIdentifier,
159
252
  name: series.label || `Series ${series.originalIndex + 1}`,
160
253
  value: seriesSum,
161
254
  color: series.lineColor || getChartColor(series.originalIndex, this.chartColors),
162
- hidden: this.hiddenSeries.has(series.id || series.label || `series-${series.originalIndex}`),
255
+ hidden: this.hiddenSeries.has(seriesIdentifier),
163
256
  percentage: parseFloat(percentage.toFixed(2)),
164
257
  };
165
258
  });
@@ -174,11 +267,11 @@ class AXLineChartComponent extends NXComponent {
174
267
  allPointsGroups.classed('ax-chart-dimmed', false).style('opacity', 1);
175
268
  allSeriesGroups
176
269
  .selectAll('.ax-line-chart-line')
177
- .attr('stroke-width', this.effectiveOptions().lineWidth ?? 2)
270
+ .attr('stroke-width', this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH)
178
271
  .classed('ax-chart-highlighted-path', false);
179
272
  allPointsGroups
180
273
  .selectAll('circle.ax-line-chart-point')
181
- .attr('r', this.effectiveOptions().pointRadius ?? 4)
274
+ .attr('r', this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS)
182
275
  .classed('ax-chart-highlighted-point', false);
183
276
  return;
184
277
  }
@@ -190,23 +283,22 @@ class AXLineChartComponent extends NXComponent {
190
283
  targetPointsGroup.classed('ax-chart-dimmed', false).style('opacity', 1);
191
284
  targetSeriesGroup
192
285
  .selectAll('.ax-line-chart-line')
193
- .attr('stroke-width', (this.effectiveOptions().lineWidth ?? 2) * 1.5)
286
+ .attr('stroke-width', (this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH) * this.LINE_HIGHLIGHT_MULTIPLIER)
194
287
  .classed('ax-chart-highlighted-path', true);
195
288
  targetPointsGroup
196
289
  .selectAll('circle.ax-line-chart-point')
197
- .attr('r', (this.effectiveOptions().pointRadius ?? 4) * 1.5)
198
- .classed('ax-chart-highlighted-point', true);
290
+ .attr('r', (this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS) * this.POINT_HIGHLIGHT_MULTIPLIER)
291
+ .classed('ax-chart-highlighted-path', true);
199
292
  }
200
293
  toggleSegment(id) {
201
- const seriesId = id;
202
- if (this.hiddenSeries.has(seriesId)) {
203
- this.hiddenSeries.delete(seriesId);
294
+ if (this.hiddenSeries.has(id)) {
295
+ this.hiddenSeries.delete(id);
204
296
  }
205
297
  else {
206
- this.hiddenSeries.add(seriesId);
298
+ this.hiddenSeries.add(id);
207
299
  }
208
300
  this.updateChart();
209
- return !this.hiddenSeries.has(seriesId);
301
+ return !this.hiddenSeries.has(id);
210
302
  }
211
303
  async loadD3() {
212
304
  try {
@@ -234,7 +326,7 @@ class AXLineChartComponent extends NXComponent {
234
326
  this.showNoDataMessage(containerElement);
235
327
  return;
236
328
  }
237
- const visibleDataForRendering = allSeriesData.filter((series) => !this.hiddenSeries.has(series.id || series.label || `series-${series.originalIndex}`));
329
+ const visibleDataForRendering = allSeriesData.filter((series) => !this.hiddenSeries.has(this.getSeriesIdentifier(series)));
238
330
  if (visibleDataForRendering.length === 0) {
239
331
  // All data is present, but all series are hidden by the legend
240
332
  this.showAllSeriesHiddenMessage(containerElement);
@@ -242,17 +334,11 @@ class AXLineChartComponent extends NXComponent {
242
334
  // For now, we will show the message and return, as scales/axes can't be drawn.
243
335
  return;
244
336
  }
245
- // Scales should be based on potentially visible data to avoid errors with empty domains
246
- // If all are hidden, scales might become an issue. Let's use all data for scales,
247
- // and rendering will handle visibility.
248
- const dataForScales = allSeriesData.flatMap((s) => s.data).length > 0 ? allSeriesData : [{ data: [{ x: 0, y: 0 }] }];
249
337
  const chartOptions = this.effectiveOptions();
250
338
  this.setupDimensions(containerElement, chartOptions);
251
- // Use allSeriesData for domain calculation to keep scales consistent even if some series are hidden
252
- // but filter out series with no data points for scale calculation.
339
+ // Use allSeriesData for domain calculation to keep scales consistent
253
340
  const dataForScaleSetup = allSeriesData.filter((s) => s.data && s.data.length > 0);
254
341
  if (dataForScaleSetup.length === 0) {
255
- // If after filtering, there's no data with points (e.g. all series have empty data arrays)
256
342
  this.showNoDataMessage(containerElement);
257
343
  return;
258
344
  }
@@ -275,7 +361,7 @@ class AXLineChartComponent extends NXComponent {
275
361
  this.calculateMargins(options, containerElement.clientWidth);
276
362
  const containerWidth = containerElement.clientWidth;
277
363
  const containerHeight = containerElement.clientHeight;
278
- const minDim = Math.min(200, containerWidth, containerHeight);
364
+ const minDim = Math.min(this.MIN_CONTAINER_DIMENSION, containerWidth, containerHeight);
279
365
  if (options.width && options.height) {
280
366
  this.width = options.width - this.margin.left - this.margin.right;
281
367
  this.height = options.height - this.margin.top - this.margin.bottom;
@@ -284,8 +370,8 @@ class AXLineChartComponent extends NXComponent {
284
370
  this.width = Math.max(containerWidth, minDim) - this.margin.left - this.margin.right;
285
371
  this.height = Math.max(containerHeight, minDim) - this.margin.top - this.margin.bottom;
286
372
  }
287
- this.width = Math.max(this.width, 100);
288
- this.height = Math.max(this.height, 100);
373
+ this.width = Math.max(this.width, this.MIN_DIMENSION);
374
+ this.height = Math.max(this.height, this.MIN_DIMENSION);
289
375
  const totalWidth = this.width + this.margin.left + this.margin.right;
290
376
  const totalHeight = this.height + this.margin.top + this.margin.bottom;
291
377
  const svg = this.d3
@@ -307,16 +393,16 @@ class AXLineChartComponent extends NXComponent {
307
393
  }
308
394
  calculateMargins(options, containerWidth) {
309
395
  this.margin = {
310
- top: options.margins?.top ?? 20,
311
- right: options.margins?.right ?? 25,
312
- bottom: options.margins?.bottom ?? 40,
313
- left: options.margins?.left ?? 50,
396
+ top: options.margins?.top ?? this.DEFAULT_MARGIN_TOP,
397
+ right: options.margins?.right ?? this.DEFAULT_MARGIN_RIGHT,
398
+ bottom: options.margins?.bottom ?? this.DEFAULT_MARGIN_BOTTOM,
399
+ left: options.margins?.left ?? this.DEFAULT_MARGIN_LEFT,
314
400
  };
315
401
  const allDataPoints = this._fullNormalizedData.flatMap((series) => series.data);
316
402
  if (allDataPoints.length > 0) {
317
403
  const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
318
404
  if (!allNumericX) {
319
- const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(s.id || s.label || `series-${s.originalIndex}`));
405
+ const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
320
406
  const allXValues = new Set(visibleSeries.flatMap((series) => series.data.map((d) => String(d.x))));
321
407
  const labelCount = allXValues.size;
322
408
  if (labelCount > 0 && containerWidth > 0) {
@@ -325,11 +411,16 @@ class AXLineChartComponent extends NXComponent {
325
411
  const availableWidthPerLabel = workingWidth / labelCount;
326
412
  const labels = Array.from(allXValues);
327
413
  const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
328
- const estimatedFontSize = Math.max(11, Math.min(15, Math.round(workingWidth / 45)));
329
- const estimatedLongestLabelWidth = longestLabel.length * estimatedFontSize * 0.6;
414
+ const estimatedFontSize = this.calculateDynamicFontSize(workingWidth, 45, this.MIN_FONT_SIZE_X, this.MAX_FONT_SIZE_X);
415
+ // Account for label truncation in width calculation
416
+ const maxLabelLength = labelCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
417
+ const effectiveLabelLength = Math.min(longestLabel.length, maxLabelLength);
418
+ const estimatedLongestLabelWidth = effectiveLabelLength * estimatedFontSize * this.CHAR_WIDTH_RATIO;
330
419
  if (estimatedLongestLabelWidth > availableWidthPerLabel) {
331
- const requiredExtraMargin = estimatedLongestLabelWidth * Math.sin(Math.PI / 4) + 10;
332
- this.margin.bottom += Math.min(60, requiredExtraMargin);
420
+ // Calculate diagonal height when rotated -45 degrees
421
+ const diagonalHeight = estimatedLongestLabelWidth * Math.sin(Math.PI / 4);
422
+ const requiredExtraMargin = diagonalHeight + 15; // Extra padding for safety
423
+ this.margin.bottom += Math.min(this.MAX_EXTRA_MARGIN, requiredExtraMargin);
333
424
  }
334
425
  }
335
426
  }
@@ -341,15 +432,23 @@ class AXLineChartComponent extends NXComponent {
341
432
  this.margin.bottom = Math.max(this.margin.bottom, 40 + extraBottomMargin);
342
433
  }
343
434
  if (options.yAxisLabel) {
344
- const yLabelLength = options.yAxisLabel.length;
345
- const extraLeftMargin = Math.min(20, Math.max(10, yLabelLength * 0.8));
346
- this.margin.left = Math.max(this.margin.left, 50 + extraLeftMargin);
435
+ // Calculate space needed for Y-axis: tick labels + padding + title
436
+ const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
437
+ const yAxisTitleThickness = 20; // Height of rotated title text
438
+ const yAxisTitlePadding = this.Y_AXIS_PADDING;
439
+ const totalYAxisWidth = maxTickLabelWidth + yAxisTitlePadding + yAxisTitleThickness;
440
+ this.margin.left = Math.max(this.margin.left, totalYAxisWidth);
441
+ }
442
+ else if (options.showYAxis !== false) {
443
+ // Just tick labels, no title
444
+ const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
445
+ this.margin.left = Math.max(this.margin.left, maxTickLabelWidth + 10);
347
446
  }
348
447
  if (options.showXAxis !== false) {
349
- this.margin.bottom = Math.max(this.margin.bottom, 45);
448
+ this.margin.bottom = Math.max(this.margin.bottom, this.MIN_MARGIN_BOTTOM);
350
449
  }
351
450
  if (options.showYAxis !== false) {
352
- this.margin.left = Math.max(this.margin.left, 55);
451
+ this.margin.left = Math.max(this.margin.left, this.MIN_MARGIN_LEFT);
353
452
  }
354
453
  }
355
454
  setupScales(data) {
@@ -365,16 +464,17 @@ class AXLineChartComponent extends NXComponent {
365
464
  }
366
465
  const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
367
466
  if (allNumericX) {
368
- const xMin = this.d3.min(allDataPoints, (d) => d.x) ?? 0;
369
467
  const xMax = this.d3.max(allDataPoints, (d) => d.x) ?? 0;
370
- if (xMin === xMax) {
468
+ if (xMax === 0) {
469
+ // If all values are 0, show -1 to 1 range
371
470
  this.xScale = this.d3
372
471
  .scaleLinear()
373
- .domain([xMin - 1, xMax + 1])
472
+ .domain([-1, 1])
374
473
  .range([0, this.width]);
375
474
  }
376
475
  else {
377
- this.xScale = this.d3.scaleLinear().domain([xMin, xMax]).range([0, this.width]);
476
+ // Always start X axis from 0 for numeric data
477
+ this.xScale = this.d3.scaleLinear().domain([0, xMax]).range([0, this.width]);
378
478
  }
379
479
  }
380
480
  else {
@@ -403,14 +503,61 @@ class AXLineChartComponent extends NXComponent {
403
503
  const showYAxis = options.showYAxis !== false;
404
504
  const showGrid = options.showGrid !== false;
405
505
  const isBandScale = this.xScale.bandwidth !== undefined;
406
- const isRtl = this.platform.isRtl();
506
+ const isRtl = document.documentElement.dir === 'rtl' || document.body.dir === 'rtl';
407
507
  const axesGroup = this.chart.append('g').attr('class', 'ax-line-chart-axes');
408
508
  if (showXAxis) {
509
+ let xAxisGenerator;
510
+ let itemCount = 0;
511
+ if (isBandScale) {
512
+ // Band scale (categorical data)
513
+ itemCount = this.xScale.domain().length;
514
+ // Smart tick reduction for many items (like bar chart)
515
+ let tickValues = this.xScale.domain();
516
+ if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
517
+ // Show every 5th tick for 50+ items
518
+ tickValues = tickValues.filter((_d, i) => i % 5 === 0);
519
+ }
520
+ else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
521
+ // Show every 2nd tick for 20-50 items
522
+ tickValues = tickValues.filter((_d, i) => i % 2 === 0);
523
+ }
524
+ xAxisGenerator = this.d3
525
+ .axisBottom(this.xScale)
526
+ .tickValues(tickValues)
527
+ .tickSize(5)
528
+ .tickPadding(8)
529
+ .tickFormat((d) => {
530
+ const label = String(d);
531
+ // Truncate long labels intelligently
532
+ const maxLength = itemCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
533
+ return this.truncateLabel(label, maxLength);
534
+ });
535
+ }
536
+ else {
537
+ // Linear scale (numeric data)
538
+ // Let D3 determine optimal tick count, but ensure we show reasonable number
539
+ // Calculate optimal tick count based on width
540
+ const optimalTickCount = Math.min(12, Math.max(5, Math.floor(this.width / 80)));
541
+ xAxisGenerator = this.d3
542
+ .axisBottom(this.xScale)
543
+ .ticks(optimalTickCount)
544
+ .tickSize(5)
545
+ .tickPadding(8)
546
+ .tickFormat((d) => {
547
+ // Format numbers nicely
548
+ const num = Number(d);
549
+ if (Number.isInteger(num)) {
550
+ return String(num);
551
+ }
552
+ return num.toFixed(1);
553
+ });
554
+ itemCount = optimalTickCount;
555
+ }
409
556
  this.xAxis = axesGroup
410
557
  .append('g')
411
558
  .attr('class', 'ax-line-chart-axis-x')
412
559
  .attr('transform', `translate(0,${this.height})`)
413
- .call(this.d3.axisBottom(this.xScale).tickSize(5).tickPadding(8));
560
+ .call(xAxisGenerator);
414
561
  // Style the x-axis path and lines
415
562
  this.xAxis.selectAll('path').attr('stroke', 'rgba(var(--ax-comp-line-chart-axis-color), 0.2)');
416
563
  this.xAxis
@@ -418,19 +565,21 @@ class AXLineChartComponent extends NXComponent {
418
565
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
419
566
  .attr('stroke-dasharray', '2,2')
420
567
  .attr('stroke-opacity', '0.5');
421
- const dynamicXAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.width / 45)));
568
+ const dynamicXAxisTickFontSize = this.calculateDynamicFontSize(this.width, 45, this.MIN_FONT_SIZE_X, this.MAX_FONT_SIZE_X);
422
569
  const xAxisTicks = this.xAxis
423
570
  .selectAll('text')
424
571
  .style('font-size', `${dynamicXAxisTickFontSize}px`)
425
572
  .style('font-weight', '400')
426
573
  .style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)');
427
- // Automatically rotate labels if they are likely to overlap
428
- if (isBandScale && this.xScale.domain().length > 0) {
574
+ // Automatically rotate labels if they are likely to overlap (only for band scale)
575
+ let labelsAreRotated = false;
576
+ if (isBandScale && this.xScale.bandwidth && this.xScale.domain().length > 0) {
429
577
  const step = this.xScale.step();
430
578
  const longestLabel = this.xScale.domain().reduce((a, b) => (a.length > b.length ? a : b), '');
431
- // Using 0.6 as a better estimate for char width-to-height ratio
432
- const estimatedLongestLabelWidth = longestLabel.length * dynamicXAxisTickFontSize * 0.6;
579
+ // Using char width ratio constant for better estimate
580
+ const estimatedLongestLabelWidth = longestLabel.length * dynamicXAxisTickFontSize * this.CHAR_WIDTH_RATIO;
433
581
  if (estimatedLongestLabelWidth > step) {
582
+ labelsAreRotated = true;
434
583
  xAxisTicks
435
584
  .attr('transform', 'rotate(-45)')
436
585
  .style('text-anchor', 'end')
@@ -438,16 +587,17 @@ class AXLineChartComponent extends NXComponent {
438
587
  .attr('dy', '0.15em');
439
588
  }
440
589
  }
441
- if (options.xAxisLabel) {
442
- const labelY = this.height + this.margin.bottom * 0.7;
590
+ // Only show X-axis title if labels are NOT rotated and item count is reasonable
591
+ // This prevents overlap with rotated labels and improves readability
592
+ const shouldShowXAxisTitle = options.xAxisLabel && !labelsAreRotated && itemCount <= this.MANY_ITEMS_THRESHOLD;
593
+ if (shouldShowXAxisTitle) {
443
594
  axesGroup
444
595
  .append('text')
445
596
  .attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
446
597
  .attr('text-anchor', 'middle')
447
- .attr('dominant-baseline', 'middle')
448
598
  .attr('x', this.width / 2)
449
- .attr('y', labelY)
450
- .attr('transform', 'translate(0, 5)')
599
+ .attr('y', this.height + this.margin.bottom - 5)
600
+ .attr('direction', 'ltr')
451
601
  .attr('style', `
452
602
  font-size: 14px;
453
603
  font-weight: 500;
@@ -458,10 +608,24 @@ class AXLineChartComponent extends NXComponent {
458
608
  }
459
609
  }
460
610
  if (showYAxis) {
461
- this.yAxis = axesGroup
462
- .append('g')
463
- .attr('class', 'ax-line-chart-axis-y')
464
- .call(this.d3.axisLeft(this.yScale).tickSize(5).tickPadding(8));
611
+ // Create Y axis with smart number formatting
612
+ const yAxisGenerator = this.d3
613
+ .axisLeft(this.yScale)
614
+ .tickSize(5)
615
+ .tickPadding(8)
616
+ .tickFormat((value) => {
617
+ const numValue = typeof value === 'number' ? value : value.valueOf();
618
+ return formatLargeNumber(numValue);
619
+ });
620
+ // Reduce tick count for better readability with large numbers
621
+ const maxValue = this.yScale.domain()[1];
622
+ if (maxValue > 1e6) {
623
+ yAxisGenerator.ticks(6); // Fewer ticks for millions+
624
+ }
625
+ else if (maxValue > 1e3) {
626
+ yAxisGenerator.ticks(8);
627
+ }
628
+ this.yAxis = axesGroup.append('g').attr('class', 'ax-line-chart-axis-y').call(yAxisGenerator);
465
629
  // Style the y-axis path and lines
466
630
  this.yAxis.selectAll('path').attr('stroke', 'rgba(var(--ax-comp-line-chart-axis-color), 0.2)');
467
631
  this.yAxis
@@ -469,7 +633,7 @@ class AXLineChartComponent extends NXComponent {
469
633
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
470
634
  .attr('stroke-dasharray', '2,2')
471
635
  .attr('stroke-opacity', '0.5');
472
- const dynamicYAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.height / 30)));
636
+ const dynamicYAxisTickFontSize = this.calculateDynamicFontSize(this.height, 30, this.MIN_FONT_SIZE_Y, this.MAX_FONT_SIZE_Y);
473
637
  const yTickTexts = this.yAxis.selectAll('text').attr('style', `
474
638
  font-size: ${dynamicYAxisTickFontSize}px;
475
639
  font-weight: 400;
@@ -479,16 +643,23 @@ class AXLineChartComponent extends NXComponent {
479
643
  yTickTexts.attr('text-anchor', 'start');
480
644
  }
481
645
  if (options.yAxisLabel) {
646
+ // Calculate proper position for Y-axis title
647
+ // Position it to the left of the tick labels with proper spacing
648
+ const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
649
+ const padding = this.Y_AXIS_PADDING;
650
+ // Position title to the left of the tick labels
651
+ const labelY = -maxTickLabelWidth - padding;
652
+ // Center the title vertically
482
653
  const labelX = -this.height / 2;
483
- const labelY = -this.margin.left * 0.8;
484
654
  axesGroup
485
655
  .append('text')
486
656
  .attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
487
657
  .attr('text-anchor', 'middle')
488
658
  .attr('dominant-baseline', 'middle')
489
- .attr('transform', 'rotate(-90) translate(-5, 0)')
659
+ .attr('transform', 'rotate(-90)')
490
660
  .attr('x', labelX)
491
661
  .attr('y', labelY)
662
+ .attr('direction', 'ltr')
492
663
  .attr('style', `
493
664
  font-size: 14px;
494
665
  font-weight: 500;
@@ -578,12 +749,11 @@ class AXLineChartComponent extends NXComponent {
578
749
  this.chart.append('g').attr('class', 'ax-line-chart-crosshair').attr('pointer-events', 'none');
579
750
  }
580
751
  const animationDuration = this.effectiveOptions().animationDuration;
581
- const animationEasing = this.getEasingFunction(this.effectiveOptions().animationEasing);
752
+ const animationEasing = getEasingFunction(this.d3, this.effectiveOptions().animationEasing);
582
753
  allSeriesData.forEach((series) => {
583
- const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
754
+ const seriesIdentifier = this.getSeriesIdentifier(series);
584
755
  if (this.hiddenSeries.has(seriesIdentifier)) {
585
- this.chart.selectAll(`.ax-line-chart-series[data-series-identifier="${seriesIdentifier}"]`).remove();
586
- this.chart.selectAll(`.ax-line-chart-points[data-series-identifier="${seriesIdentifier}"]`).remove();
756
+ this.removeSeriesElements(seriesIdentifier);
587
757
  return;
588
758
  }
589
759
  if (!series.data || series.data.length === 0)
@@ -600,7 +770,7 @@ class AXLineChartComponent extends NXComponent {
600
770
  .datum(series.data)
601
771
  .attr('class', 'ax-line-chart-line')
602
772
  .attr('stroke', lineColor)
603
- .attr('stroke-width', this.effectiveOptions().lineWidth ?? 2)
773
+ .attr('stroke-width', this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH)
604
774
  .attr('stroke-linejoin', 'round')
605
775
  .attr('stroke-linecap', 'round')
606
776
  .attr('d', lineGenerator)
@@ -611,14 +781,14 @@ class AXLineChartComponent extends NXComponent {
611
781
  .on('mouseenter', () => {
612
782
  line
613
783
  .transition()
614
- .duration(150)
615
- .attr('stroke-width', (this.effectiveOptions().lineWidth ?? 2) * 1.5);
784
+ .duration(this.HOVER_TRANSITION_DURATION)
785
+ .attr('stroke-width', (this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH) * this.LINE_HIGHLIGHT_MULTIPLIER);
616
786
  })
617
787
  .on('mouseleave', () => {
618
788
  line
619
789
  .transition()
620
- .duration(150)
621
- .attr('stroke-width', this.effectiveOptions().lineWidth ?? 2);
790
+ .duration(this.HOVER_TRANSITION_DURATION)
791
+ .attr('stroke-width', this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH);
622
792
  });
623
793
  const totalLength = line.node().getTotalLength();
624
794
  line
@@ -645,13 +815,13 @@ class AXLineChartComponent extends NXComponent {
645
815
  .on('mouseenter', () => {
646
816
  area
647
817
  .transition()
648
- .duration(150)
818
+ .duration(this.HOVER_TRANSITION_DURATION)
649
819
  .attr('opacity', ((this.effectiveOptions().fillOpacity ?? 20) / 100) * 1.5);
650
820
  })
651
821
  .on('mouseleave', () => {
652
822
  area
653
823
  .transition()
654
- .duration(150)
824
+ .duration(this.HOVER_TRANSITION_DURATION)
655
825
  .attr('opacity', (this.effectiveOptions().fillOpacity ?? 20) / 100);
656
826
  });
657
827
  area
@@ -667,17 +837,15 @@ class AXLineChartComponent extends NXComponent {
667
837
  if (this.effectiveOptions().showPoints !== false) {
668
838
  const pointMap = new Map();
669
839
  allSeriesData.forEach((series) => {
670
- const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
840
+ const seriesIdentifier = this.getSeriesIdentifier(series);
671
841
  if (this.hiddenSeries.has(seriesIdentifier) || !series.data || series.data.length === 0) {
672
842
  return;
673
843
  }
674
- const lineColor = series.lineColor || getChartColor(series.originalIndex, this.chartColors);
675
- const maxPoints = 100;
676
- const pointData = series.data.length > maxPoints ? this.getReducedDataPoints(series.data, maxPoints) : series.data;
844
+ const pointData = this.getReducedDataPoints(series.data, this.MAX_POINTS_TO_RENDER);
677
845
  pointData.forEach((point) => {
678
846
  const x = getX(point);
679
847
  const y = this.yScale(point.y);
680
- const key = `${Math.round(x * 10) / 10},${Math.round(y * 10) / 10}`;
848
+ const key = this.createPointKey(x, y);
681
849
  if (!pointMap.has(key)) {
682
850
  pointMap.set(key, []);
683
851
  }
@@ -685,9 +853,9 @@ class AXLineChartComponent extends NXComponent {
685
853
  });
686
854
  });
687
855
  let animationCounter = 0;
688
- const totalVisibleSeriesCount = allSeriesData.filter((s) => !this.hiddenSeries.has(s.id || s.label || `series-${s.originalIndex}`) && s.data && s.data.length > 0).length;
856
+ const totalVisibleSeriesCount = allSeriesData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)) && s.data && s.data.length > 0).length;
689
857
  allSeriesData.forEach((series) => {
690
- const seriesIdentifier = series.id || series.label || `series-${series.originalIndex}`;
858
+ const seriesIdentifier = this.getSeriesIdentifier(series);
691
859
  if (this.hiddenSeries.has(seriesIdentifier) || !series.data || series.data.length === 0) {
692
860
  return;
693
861
  }
@@ -698,10 +866,9 @@ class AXLineChartComponent extends NXComponent {
698
866
  .attr('data-series-identifier', seriesIdentifier)
699
867
  .attr('pointer-events', 'none');
700
868
  pointsGroup
701
- .on('mouseenter', () => this.handlePointGroupEnter(series.originalIndex, lineColor))
869
+ .on('mouseenter', () => this.handlePointGroupEnter(series.originalIndex))
702
870
  .on('mouseleave', () => this.handlePointGroupLeave());
703
- const maxPoints = 100;
704
- const pointData = series.data.length > maxPoints ? this.getReducedDataPoints(series.data, maxPoints) : series.data;
871
+ const pointData = this.getReducedDataPoints(series.data, this.MAX_POINTS_TO_RENDER);
705
872
  pointsGroup
706
873
  .selectAll('circle')
707
874
  .data(pointData)
@@ -721,7 +888,7 @@ class AXLineChartComponent extends NXComponent {
721
888
  .on('mouseenter', (event, d) => {
722
889
  const x = getX(d);
723
890
  const y = this.yScale(d.y);
724
- const key = `${Math.round(x * 10) / 10},${Math.round(y * 10) / 10}`;
891
+ const key = this.createPointKey(x, y);
725
892
  const overlappingPoints = pointMap.get(key) || [];
726
893
  if (overlappingPoints.length > 1) {
727
894
  this.handleOverlappingPointsHover(event, overlappingPoints);
@@ -732,7 +899,7 @@ class AXLineChartComponent extends NXComponent {
732
899
  this.d3
733
900
  .select(event.target)
734
901
  .transition()
735
- .duration(150)
902
+ .duration(this.HOVER_TRANSITION_DURATION)
736
903
  .attr('r', (this.effectiveOptions().pointRadius ?? 4) * 1.5)
737
904
  .attr('stroke-width', 2)
738
905
  .attr('stroke', `rgb(var(--ax-comp-line-chart-bg-color))`);
@@ -743,15 +910,15 @@ class AXLineChartComponent extends NXComponent {
743
910
  this.d3
744
911
  .select(event.target)
745
912
  .transition()
746
- .duration(150)
747
- .attr('r', this.effectiveOptions().pointRadius ?? 4)
913
+ .duration(this.HOVER_TRANSITION_DURATION)
914
+ .attr('r', this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS)
748
915
  .attr('stroke-width', 1)
749
916
  .attr('stroke', '#fff');
750
917
  })
751
918
  .on('click', (event, d) => {
752
919
  const x = getX(d);
753
920
  const y = this.yScale(d.y);
754
- const key = `${Math.round(x * 10) / 10},${Math.round(y * 10) / 10}`;
921
+ const key = this.createPointKey(x, y);
755
922
  const overlappingPoints = pointMap.get(key) || [];
756
923
  if (overlappingPoints.length > 1) {
757
924
  overlappingPoints.forEach(({ point, series }) => {
@@ -763,10 +930,11 @@ class AXLineChartComponent extends NXComponent {
763
930
  }
764
931
  })
765
932
  .transition()
766
- .delay((d, i) => i * Math.min(50, (animationDuration || 0) * 0.05) + (animationDuration || 0) * 0.5)
767
- .duration((animationDuration || 0) * 0.3)
933
+ .delay((_d, i) => i * Math.min(this.POINT_ANIMATION_DELAY_MS, (animationDuration || 0) * 0.05) +
934
+ (animationDuration || 0) * this.POINT_ANIMATION_DELAY_RATIO)
935
+ .duration((animationDuration || 0) * this.POINT_ANIMATION_DURATION_RATIO)
768
936
  .ease(animationEasing)
769
- .attr('r', this.effectiveOptions().pointRadius ?? 4)
937
+ .attr('r', this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS)
770
938
  .on('end', (d, i, nodes) => {
771
939
  if (i === nodes.length - 1) {
772
940
  animationCounter++;
@@ -778,9 +946,7 @@ class AXLineChartComponent extends NXComponent {
778
946
  });
779
947
  }
780
948
  else {
781
- setTimeout(() => {
782
- this.enablePointerEventsAfterAnimation();
783
- }, animationDuration || 0 + 100);
949
+ setTimeout(() => this.enablePointerEventsAfterAnimation(), (animationDuration || 0) + this.ANIMATION_END_BUFFER);
784
950
  }
785
951
  }
786
952
  enablePointerEventsAfterAnimation() {
@@ -795,19 +961,19 @@ class AXLineChartComponent extends NXComponent {
795
961
  this.chart.selectAll('.ax-line-chart-point').attr('pointer-events', 'all');
796
962
  this.svg.classed('ax-line-chart-animating', false);
797
963
  }
798
- handlePointGroupEnter(originalIndex, color) {
964
+ handlePointGroupEnter(originalIndex) {
799
965
  this.chart
800
966
  .select(`.ax-line-chart-series-${originalIndex} .ax-line-chart-line`)
801
967
  .transition()
802
- .duration(150)
803
- .attr('stroke-width', (this.effectiveOptions().lineWidth ?? 2) * 1.5);
968
+ .duration(this.HOVER_TRANSITION_DURATION)
969
+ .attr('stroke-width', (this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH) * this.LINE_HIGHLIGHT_MULTIPLIER);
804
970
  }
805
971
  handlePointGroupLeave() {
806
972
  this.chart
807
973
  .selectAll('.ax-line-chart-line')
808
974
  .transition()
809
- .duration(150)
810
- .attr('stroke-width', this.effectiveOptions().lineWidth ?? 2);
975
+ .duration(this.HOVER_TRANSITION_DURATION)
976
+ .attr('stroke-width', this.effectiveOptions().lineWidth ?? this.DEFAULT_LINE_WIDTH);
811
977
  if (this.effectiveOptions().showCrosshair === true) {
812
978
  this.chart.select('.ax-line-chart-crosshair').selectAll('*').remove();
813
979
  }
@@ -830,7 +996,7 @@ class AXLineChartComponent extends NXComponent {
830
996
  const color = series.lineColor || getChartColor(originalIndex, this.chartColors);
831
997
  this._tooltipData.set({
832
998
  title: dataPoint.tooltipLabel || series.tooltipLabel || series.label || `Series ${originalIndex + 1}`,
833
- value: dataPoint.y.toString(),
999
+ value: formatLargeNumber(dataPoint.y),
834
1000
  color: color,
835
1001
  });
836
1002
  this._tooltipVisible.set(true);
@@ -974,7 +1140,7 @@ class AXLineChartComponent extends NXComponent {
974
1140
  return;
975
1141
  const tooltipData = {
976
1142
  title: overlappingPoints.map(({ series }) => series.label),
977
- value: overlappingPoints[0].point.y.toString(),
1143
+ value: formatLargeNumber(overlappingPoints[0].point.y),
978
1144
  color: overlappingPoints.map(({ series }) => series.lineColor || getChartColor(series.originalIndex, this.chartColors)),
979
1145
  };
980
1146
  this._tooltipData.set(tooltipData);
@@ -984,37 +1150,13 @@ class AXLineChartComponent extends NXComponent {
984
1150
  this.showCrosshairLines(overlappingPoints[0].point);
985
1151
  }
986
1152
  }
987
- getEasingFunction(easing) {
988
- switch (easing) {
989
- case 'linear':
990
- return this.d3.easeLinear;
991
- case 'ease':
992
- return this.d3.easePolyInOut;
993
- case 'ease-in':
994
- return this.d3.easePolyIn;
995
- case 'ease-out':
996
- return this.d3.easePolyOut;
997
- case 'ease-in-out':
998
- return this.d3.easePolyInOut;
999
- case 'cubic':
1000
- return this.d3.easeCubic;
1001
- case 'cubic-in':
1002
- return this.d3.easeCubicIn;
1003
- case 'cubic-out':
1004
- return this.d3.easeCubicOut;
1005
- case 'cubic-in-out':
1006
- return this.d3.easeCubicInOut;
1007
- default:
1008
- return this.d3.easeCubicOut;
1009
- }
1010
- }
1011
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1012
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.3", 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: 0, 0, 0, 0;--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:rgba(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 svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));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: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1153
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1154
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.15", 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: 0, 0, 0, 0;--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;color:rgba(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 svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));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: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1013
1155
  }
1014
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXLineChartComponent, decorators: [{
1156
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXLineChartComponent, decorators: [{
1015
1157
  type: Component,
1016
- args: [{ selector: 'ax-line-chart', standalone: true, encapsulation: ViewEncapsulation.None, imports: [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: 0, 0, 0, 0;--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:rgba(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 svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));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"] }]
1017
- }] });
1158
+ args: [{ selector: 'ax-line-chart', standalone: true, encapsulation: ViewEncapsulation.None, imports: [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: 0, 0, 0, 0;--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;color:rgba(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 svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));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"] }]
1159
+ }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], chartContainerEl: [{ type: i0.ViewChild, args: ['chartContainer', { isSignal: true }] }] } });
1018
1160
 
1019
1161
  /**
1020
1162
  * Generated bundle index. Do not edit.