@acorex/charts 21.0.2-next.9 → 21.0.3-next.3

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.
@@ -5,10 +5,10 @@ import { InjectionToken, input, output, viewChild, signal, inject, computed, eff
5
5
 
6
6
  const AXLineChartDefaultConfig = {
7
7
  margins: {
8
- top: 20,
9
- right: 25,
10
- bottom: 40,
11
- left: 50,
8
+ top: 12,
9
+ right: 12,
10
+ bottom: 8,
11
+ left: 8,
12
12
  },
13
13
  showXAxis: true,
14
14
  showYAxis: true,
@@ -51,32 +51,33 @@ function lineChartConfig(config = {}) {
51
51
  * Line Chart Component for rendering data as lines with interactive hover effects and animations
52
52
  */
53
53
  class AXLineChartComponent extends AXChartComponent {
54
- data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
55
- options = input({}, ...(ngDevMode ? [{ debugName: "options" }] : []));
54
+ data = input([], ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
55
+ options = input({}, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
56
56
  pointClick = output();
57
57
  chartContainerEl = viewChild.required('chartContainer');
58
58
  d3;
59
59
  svg;
60
60
  chart;
61
61
  xScale;
62
+ xScaleKind = 'linear';
62
63
  yScale;
63
64
  xAxis;
64
65
  yAxis;
65
66
  width;
66
67
  height;
67
68
  margin = { top: 20, right: 25, bottom: 40, left: 50 };
68
- _tooltipVisible = signal(false, ...(ngDevMode ? [{ debugName: "_tooltipVisible" }] : []));
69
- _tooltipPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "_tooltipPosition" }] : []));
69
+ _tooltipVisible = signal(false, ...(ngDevMode ? [{ debugName: "_tooltipVisible" }] : /* istanbul ignore next */ []));
70
+ _tooltipPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "_tooltipPosition" }] : /* istanbul ignore next */ []));
70
71
  _tooltipData = signal({
71
72
  title: '',
72
73
  value: '0',
73
74
  percentage: '0%',
74
75
  color: '',
75
- }, ...(ngDevMode ? [{ debugName: "_tooltipData" }] : []));
76
+ }, ...(ngDevMode ? [{ debugName: "_tooltipData" }] : /* istanbul ignore next */ []));
76
77
  _tooltipRafId = null;
77
78
  _pendingTooltipCoords = null;
78
- _initialized = signal(false, ...(ngDevMode ? [{ debugName: "_initialized" }] : []));
79
- _rendered = signal(false, ...(ngDevMode ? [{ debugName: "_rendered" }] : []));
79
+ _initialized = signal(false, ...(ngDevMode ? [{ debugName: "_initialized" }] : /* istanbul ignore next */ []));
80
+ _rendered = signal(false, ...(ngDevMode ? [{ debugName: "_rendered" }] : /* istanbul ignore next */ []));
80
81
  hiddenSeries = new Set();
81
82
  _fullNormalizedData = [];
82
83
  tooltipVisible = this._tooltipVisible.asReadonly();
@@ -91,7 +92,7 @@ class AXLineChartComponent extends AXChartComponent {
91
92
  ...this.configToken,
92
93
  ...this.options(),
93
94
  };
94
- }, ...(ngDevMode ? [{ debugName: "effectiveOptions" }] : []));
95
+ }, ...(ngDevMode ? [{ debugName: "effectiveOptions" }] : /* istanbul ignore next */ []));
95
96
  // Messages with defaults
96
97
  effectiveMessages = computed(() => {
97
98
  const defaultMessages = {
@@ -106,18 +107,21 @@ class AXLineChartComponent extends AXChartComponent {
106
107
  ...defaultMessages,
107
108
  ...this.effectiveOptions().messages,
108
109
  };
109
- }, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : []));
110
+ }, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : /* istanbul ignore next */ []));
110
111
  // Layout & Dimensions
111
112
  MIN_DIMENSION = 100;
112
- MIN_CONTAINER_DIMENSION = 200;
113
- DEFAULT_MARGIN_TOP = 20;
114
- DEFAULT_MARGIN_RIGHT = 25;
115
- DEFAULT_MARGIN_BOTTOM = 40;
116
- DEFAULT_MARGIN_LEFT = 50;
117
- MIN_MARGIN_BOTTOM = 45;
118
- MIN_MARGIN_LEFT = 55;
119
- MAX_EXTRA_MARGIN = 60;
120
- CHART_EDGE_PADDING = 12;
113
+ DEFAULT_MARGIN_TOP = 12;
114
+ DEFAULT_MARGIN_RIGHT = 12;
115
+ DEFAULT_MARGIN_BOTTOM = 8;
116
+ DEFAULT_MARGIN_LEFT = 8;
117
+ MIN_MARGIN_BOTTOM = 28;
118
+ MIN_MARGIN_LEFT = 36;
119
+ X_AXIS_TITLE_GAP = 10;
120
+ TICK_AREA_PADDING = 4;
121
+ AXIS_TICK_PADDING = 6;
122
+ ROTATION_TOLERANCE = 0.92;
123
+ FORCE_ROTATE_LABEL_LENGTH = 14;
124
+ Y_AXIS_TITLE_PADDING = 10;
121
125
  // Styling & Visual
122
126
  DEFAULT_LINE_WIDTH = 2;
123
127
  DEFAULT_POINT_RADIUS = 4;
@@ -136,16 +140,178 @@ class AXLineChartComponent extends AXChartComponent {
136
140
  POINT_ANIMATION_DELAY_RATIO = 0.5;
137
141
  POINT_ANIMATION_DURATION_RATIO = 0.3;
138
142
  // Text & Labels
139
- AXIS_LABEL_FONT_SIZE = 14;
140
143
  CHAR_WIDTH_RATIO = 0.6;
141
144
  FONT_WIDTH_MULTIPLIER = 0.6;
142
145
  MAX_LABEL_LENGTH = 20;
143
- MIN_FONT_SIZE_X = 11;
144
- MAX_FONT_SIZE_X = 15;
146
+ MIN_FONT_SIZE_X = 10;
147
+ MAX_FONT_SIZE_X = 16;
145
148
  MIN_FONT_SIZE_Y = 11;
146
- MAX_FONT_SIZE_Y = 15;
147
- FONT_PADDING = 10;
148
- Y_AXIS_PADDING = 10;
149
+ MAX_FONT_SIZE_Y = 16;
150
+ FONT_PADDING = 6;
151
+ xAxisLabelLayout = {
152
+ fontSize: 12,
153
+ willRotate: false,
154
+ tickAreaHeight: 24,
155
+ displayedTickCount: 1,
156
+ };
157
+ /**
158
+ * Resolves X-axis tick font size, rotation, and reserved tick area height.
159
+ */
160
+ resolveXAxisLabelLayout(chartWidth, categoricalLabels) {
161
+ if (!categoricalLabels || categoricalLabels.length === 0) {
162
+ const displayedTickCount = Math.min(12, Math.max(5, Math.floor(chartWidth / 80)));
163
+ const fontSize = this.getXAxisTickFontSize(chartWidth, displayedTickCount);
164
+ const tickAreaHeight = fontSize + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
165
+ return { fontSize, willRotate: false, tickAreaHeight, displayedTickCount };
166
+ }
167
+ const itemCount = categoricalLabels.length;
168
+ const longestLabel = categoricalLabels.reduce((a, b) => (a.length > b.length ? a : b), '');
169
+ const effectiveLength = this.getEffectiveXLabelLength(longestLabel, itemCount);
170
+ const step = itemCount > 1 ? chartWidth / (itemCount - 1) : chartWidth;
171
+ let fontSize = this.getXAxisTickFontSize(chartWidth, itemCount);
172
+ let effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
173
+ while (effectiveWidth > step * this.ROTATION_TOLERANCE && fontSize > this.MIN_FONT_SIZE_X) {
174
+ fontSize--;
175
+ effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
176
+ }
177
+ const willRotate = longestLabel.length >= this.FORCE_ROTATE_LABEL_LENGTH ||
178
+ effectiveWidth > step * this.ROTATION_TOLERANCE;
179
+ if (willRotate && effectiveWidth > step) {
180
+ while (effectiveWidth > step * 1.35 && fontSize > this.MIN_FONT_SIZE_X) {
181
+ fontSize--;
182
+ effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
183
+ }
184
+ }
185
+ const tickAreaHeight = this.estimateXAxisTickAreaHeight(fontSize, effectiveWidth, willRotate);
186
+ let displayedTickCount = itemCount;
187
+ if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
188
+ displayedTickCount = Math.ceil(itemCount / 5);
189
+ }
190
+ else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
191
+ displayedTickCount = Math.ceil(itemCount / 2);
192
+ }
193
+ return { fontSize, willRotate, tickAreaHeight, displayedTickCount };
194
+ }
195
+ /**
196
+ * Returns truncated label length used for layout calculations.
197
+ */
198
+ getEffectiveXLabelLength(label, itemCount) {
199
+ const maxLength = itemCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
200
+ return Math.min(label.length, maxLength);
201
+ }
202
+ /**
203
+ * Estimates vertical space required below the X-axis for tick labels.
204
+ */
205
+ estimateXAxisTickAreaHeight(fontSize, effectiveLabelWidth, willRotate) {
206
+ if (willRotate) {
207
+ return effectiveLabelWidth * Math.SQRT1_2 + fontSize * Math.SQRT1_2 + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
208
+ }
209
+ return fontSize + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
210
+ }
211
+ /**
212
+ * Measures rendered X-axis tick label area using the axis group bounding box.
213
+ */
214
+ measureXAxisTickAreaHeight(axisGroup, fontSize, fallbackHeight) {
215
+ if (!axisGroup) {
216
+ return fallbackHeight;
217
+ }
218
+ try {
219
+ const bbox = axisGroup.getBBox();
220
+ const measured = Math.max(0, bbox.y + bbox.height) + this.TICK_AREA_PADDING;
221
+ return Math.max(measured, fallbackHeight);
222
+ }
223
+ catch {
224
+ return fallbackHeight;
225
+ }
226
+ }
227
+ /**
228
+ * Measures rendered Y-axis tick label width using SVG bounding boxes.
229
+ */
230
+ measureMaxYAxisTickLabelWidth(tickNodes, fontSize) {
231
+ try {
232
+ if (tickNodes.length > 0) {
233
+ return Math.max(...tickNodes.map((node) => node.getBBox().width)) + this.FONT_PADDING;
234
+ }
235
+ }
236
+ catch {
237
+ // Fall through to estimation
238
+ }
239
+ return this.calculateMaxYAxisTickLabelWidth(fontSize);
240
+ }
241
+ /**
242
+ * Calculates required bottom margin for X-axis ticks, optional rotation, and title.
243
+ */
244
+ calculateRequiredBottomMargin(options, chartWidth, categoricalLabels) {
245
+ const baseBottom = options.margins?.bottom ?? this.DEFAULT_MARGIN_BOTTOM;
246
+ if (options.showXAxis === false) {
247
+ return Math.max(baseBottom, 8);
248
+ }
249
+ const layout = this.resolveXAxisLabelLayout(chartWidth, categoricalLabels);
250
+ this.xAxisLabelLayout.fontSize = layout.fontSize;
251
+ this.xAxisLabelLayout.willRotate = layout.willRotate;
252
+ this.xAxisLabelLayout.tickAreaHeight = layout.tickAreaHeight;
253
+ this.xAxisLabelLayout.displayedTickCount = layout.displayedTickCount;
254
+ const titleBlock = options.xAxisLabel
255
+ ? this.X_AXIS_TITLE_GAP + this.getAxisTitleFontSize(layout.fontSize) + 2
256
+ : 0;
257
+ const rotatedBuffer = layout.willRotate ? 12 : 4;
258
+ return Math.max(baseBottom, this.MIN_MARGIN_BOTTOM, layout.tickAreaHeight + titleBlock + rotatedBuffer);
259
+ }
260
+ /**
261
+ * Calculates required left margin for Y-axis ticks and title.
262
+ */
263
+ calculateRequiredLeftMargin(options, chartHeight) {
264
+ const baseLeft = options.margins?.left ?? this.DEFAULT_MARGIN_LEFT;
265
+ if (options.showYAxis === false) {
266
+ return baseLeft;
267
+ }
268
+ const fontSize = this.getYAxisTickFontSize(chartHeight);
269
+ const tickLabelWidth = this.calculateMaxYAxisTickLabelWidth(fontSize);
270
+ const titleBlock = options.yAxisLabel
271
+ ? this.Y_AXIS_TITLE_PADDING + this.getAxisTitleFontSize(fontSize) + 4
272
+ : 0;
273
+ return Math.max(baseLeft, this.MIN_MARGIN_LEFT, tickLabelWidth + this.AXIS_TICK_PADDING + titleBlock + 4);
274
+ }
275
+ createXAxisTitle(axesGroup, title, tickAreaHeight, tickFontSize) {
276
+ const titleFontSize = this.getAxisTitleFontSize(tickFontSize);
277
+ const titleY = Math.min(this.height + tickAreaHeight + this.X_AXIS_TITLE_GAP, this.height + this.margin.bottom - titleFontSize - 2);
278
+ axesGroup
279
+ .append('text')
280
+ .attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
281
+ .attr('text-anchor', 'middle')
282
+ .attr('dominant-baseline', 'hanging')
283
+ .attr('x', this.width / 2)
284
+ .attr('y', titleY)
285
+ .attr('direction', 'ltr')
286
+ .attr('style', `
287
+ font-size: ${titleFontSize}px;
288
+ font-weight: 500;
289
+ fill: rgb(var(--ax-comp-line-chart-text-color));
290
+ pointer-events: none;
291
+ `)
292
+ .text(title);
293
+ }
294
+ createYAxisTitle(axesGroup, title, tickFontSize, maxTickLabelWidth) {
295
+ const titleFontSize = this.getAxisTitleFontSize(tickFontSize);
296
+ const labelY = -maxTickLabelWidth - this.Y_AXIS_TITLE_PADDING;
297
+ const labelX = -this.height / 2;
298
+ axesGroup
299
+ .append('text')
300
+ .attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
301
+ .attr('text-anchor', 'middle')
302
+ .attr('dominant-baseline', 'middle')
303
+ .attr('transform', 'rotate(-90)')
304
+ .attr('x', labelX)
305
+ .attr('y', labelY)
306
+ .attr('direction', 'ltr')
307
+ .attr('style', `
308
+ font-size: ${titleFontSize}px;
309
+ font-weight: 500;
310
+ fill: rgb(var(--ax-comp-line-chart-text-color));
311
+ pointer-events: none;
312
+ `)
313
+ .text(title);
314
+ }
149
315
  // Data & Performance
150
316
  MAX_POINTS_TO_RENDER = 100;
151
317
  POINT_COORDINATE_PRECISION = 10;
@@ -172,10 +338,41 @@ class AXLineChartComponent extends AXChartComponent {
172
338
  return series.id || series.label || `series-${series.originalIndex}`;
173
339
  }
174
340
  /**
175
- * Calculates dynamic font size based on available space
341
+ * Calculates adaptive X-axis tick font size based on chart width and label count.
342
+ */
343
+ getXAxisTickFontSize(chartWidth, itemCount = 1) {
344
+ let size = chartWidth / 42;
345
+ if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
346
+ size *= 0.65;
347
+ }
348
+ else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
349
+ size *= 0.8;
350
+ }
351
+ return Math.max(this.MIN_FONT_SIZE_X, Math.min(this.MAX_FONT_SIZE_X, Math.round(size)));
352
+ }
353
+ /**
354
+ * Calculates adaptive Y-axis tick font size based on chart height.
355
+ */
356
+ getYAxisTickFontSize(chartHeight) {
357
+ const size = chartHeight / 28;
358
+ return Math.max(this.MIN_FONT_SIZE_Y, Math.min(this.MAX_FONT_SIZE_Y, Math.round(size)));
359
+ }
360
+ /**
361
+ * Calculates axis title font size relative to tick labels.
362
+ */
363
+ getAxisTitleFontSize(tickFontSize) {
364
+ return Math.min(15, Math.max(12, tickFontSize));
365
+ }
366
+ /**
367
+ * Estimates inner chart dimensions before margins are finalized.
176
368
  */
177
- calculateDynamicFontSize(dimension, divisor, min, max) {
178
- return Math.max(min, Math.min(max, Math.round(dimension / divisor)));
369
+ estimateChartDimensions(containerWidth, containerHeight, options) {
370
+ const outerWidth = options.width ?? Math.max(containerWidth, 1);
371
+ const outerHeight = options.height ?? Math.max(containerHeight, 1);
372
+ return {
373
+ width: Math.max(outerWidth - this.margin.left - this.margin.right, this.MIN_DIMENSION),
374
+ height: Math.max(outerHeight - this.margin.top - this.margin.bottom, this.MIN_DIMENSION),
375
+ };
179
376
  }
180
377
  /**
181
378
  * Creates a unique key for point coordinates (for overlap detection)
@@ -202,7 +399,7 @@ class AXLineChartComponent extends AXChartComponent {
202
399
  /**
203
400
  * Calculates maximum width needed for Y-axis tick labels
204
401
  */
205
- calculateMaxYAxisTickLabelWidth() {
402
+ calculateMaxYAxisTickLabelWidth(fontSize) {
206
403
  const allSeriesData = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
207
404
  let maxValue = 0;
208
405
  let minValue = 0;
@@ -219,7 +416,7 @@ class AXLineChartComponent extends AXChartComponent {
219
416
  // Check both max and min (for negative values)
220
417
  const maxAbsValue = Math.max(Math.abs(maxValue), Math.abs(minValue));
221
418
  const tickLabelText = Number.isFinite(maxAbsValue) ? formatLargeNumber(maxAbsValue) : '00000';
222
- return tickLabelText.length * this.AXIS_LABEL_FONT_SIZE * this.FONT_WIDTH_MULTIPLIER + this.FONT_PADDING;
419
+ return tickLabelText.length * fontSize * this.FONT_WIDTH_MULTIPLIER + this.FONT_PADDING;
223
420
  }
224
421
  ngOnInit() {
225
422
  this.loadD3();
@@ -230,7 +427,7 @@ class AXLineChartComponent extends AXChartComponent {
230
427
  if (this._rendered()) {
231
428
  this.updateChart();
232
429
  }
233
- }, ...(ngDevMode ? [{ debugName: "#effect" }] : []));
430
+ }, ...(ngDevMode ? [{ debugName: "#effect" }] : /* istanbul ignore next */ []));
234
431
  ngAfterViewInit() {
235
432
  this._initialized.set(true);
236
433
  if (this.d3 && this.chartContainerEl()) {
@@ -359,102 +556,64 @@ class AXLineChartComponent extends AXChartComponent {
359
556
  }
360
557
  this._pendingTooltipCoords = null;
361
558
  if (this.svg) {
362
- this.d3?.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
559
+ this.d3
560
+ ?.select(this.chartContainerEl()?.nativeElement)
561
+ .selectAll('svg, .ax-line-chart-no-data-message, .ax-line-chart-all-hidden-message')
562
+ .remove();
363
563
  this.svg = null;
364
564
  this.chart = null;
365
565
  }
366
566
  this._tooltipVisible.set(false);
367
567
  }
368
568
  setupDimensions(containerElement, options) {
369
- this.calculateMargins(options, containerElement.clientWidth);
370
- const containerWidth = containerElement.clientWidth;
371
- const containerHeight = containerElement.clientHeight;
372
- const minDim = Math.min(this.MIN_CONTAINER_DIMENSION, containerWidth, containerHeight);
569
+ const containerWidth = Math.max(containerElement.clientWidth, 1);
570
+ const containerHeight = Math.max(containerElement.clientHeight, 1);
571
+ this.calculateMargins(options, containerWidth, containerHeight);
373
572
  if (options.width && options.height) {
374
573
  this.width = options.width - this.margin.left - this.margin.right;
375
574
  this.height = options.height - this.margin.top - this.margin.bottom;
376
575
  }
377
576
  else {
378
- this.width = Math.max(containerWidth, minDim) - this.margin.left - this.margin.right;
379
- this.height = Math.max(containerHeight, minDim) - this.margin.top - this.margin.bottom;
577
+ this.width = containerWidth - this.margin.left - this.margin.right;
578
+ this.height = containerHeight - this.margin.top - this.margin.bottom;
380
579
  }
381
580
  this.width = Math.max(this.width, this.MIN_DIMENSION);
382
581
  this.height = Math.max(this.height, this.MIN_DIMENSION);
383
582
  const totalWidth = this.width + this.margin.left + this.margin.right;
384
583
  const totalHeight = this.height + this.margin.top + this.margin.bottom;
385
- const viewBoxWidth = totalWidth + this.CHART_EDGE_PADDING * 2;
386
- const viewBoxHeight = totalHeight + this.CHART_EDGE_PADDING * 2;
387
584
  const svg = this.d3
388
585
  .select(containerElement)
389
586
  .append('svg')
390
587
  .attr('width', '100%')
391
588
  .attr('height', '100%')
392
- .attr('viewBox', `0 0 ${viewBoxWidth} ${viewBoxHeight}`)
589
+ .attr('viewBox', `0 0 ${totalWidth} ${totalHeight}`)
393
590
  .attr('preserveAspectRatio', 'xMidYMid meet');
394
591
  this.svg = svg;
395
592
  this.chart = this.svg
396
593
  .append('g')
397
- .attr('transform', `translate(${this.margin.left + this.CHART_EDGE_PADDING},${this.margin.top + this.CHART_EDGE_PADDING})`);
594
+ .attr('transform', `translate(${this.margin.left},${this.margin.top})`);
398
595
  }
399
- calculateMargins(options, containerWidth) {
596
+ calculateMargins(options, containerWidth, containerHeight) {
400
597
  this.margin = {
401
598
  top: options.margins?.top ?? this.DEFAULT_MARGIN_TOP,
402
599
  right: options.margins?.right ?? this.DEFAULT_MARGIN_RIGHT,
403
600
  bottom: options.margins?.bottom ?? this.DEFAULT_MARGIN_BOTTOM,
404
601
  left: options.margins?.left ?? this.DEFAULT_MARGIN_LEFT,
405
602
  };
603
+ const estimatedDimensions = this.estimateChartDimensions(containerWidth, containerHeight, options);
406
604
  const allDataPoints = this._fullNormalizedData.flatMap((series) => series.data);
407
- if (allDataPoints.length > 0) {
408
- const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
409
- if (!allNumericX) {
410
- const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
411
- const allXValues = new Set(visibleSeries.flatMap((series) => series.data.map((d) => String(d.x))));
412
- const labelCount = allXValues.size;
413
- if (labelCount > 0 && containerWidth > 0) {
414
- const workingWidth = containerWidth - this.margin.left - this.margin.right;
415
- if (workingWidth > 0) {
416
- const availableWidthPerLabel = workingWidth / labelCount;
417
- const labels = Array.from(allXValues);
418
- const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
419
- const estimatedFontSize = this.calculateDynamicFontSize(workingWidth, 45, this.MIN_FONT_SIZE_X, this.MAX_FONT_SIZE_X);
420
- // Account for label truncation in width calculation
421
- const maxLabelLength = labelCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
422
- const effectiveLabelLength = Math.min(longestLabel.length, maxLabelLength);
423
- const estimatedLongestLabelWidth = effectiveLabelLength * estimatedFontSize * this.CHAR_WIDTH_RATIO;
424
- if (estimatedLongestLabelWidth > availableWidthPerLabel) {
425
- // Calculate diagonal height when rotated -45 degrees
426
- const diagonalHeight = estimatedLongestLabelWidth * Math.sin(Math.PI / 4);
427
- const requiredExtraMargin = diagonalHeight + 15; // Extra padding for safety
428
- this.margin.bottom += Math.min(this.MAX_EXTRA_MARGIN, requiredExtraMargin);
429
- }
430
- }
431
- }
432
- }
433
- }
434
- if (options.xAxisLabel) {
435
- const xLabelLength = options.xAxisLabel.length;
436
- const extraBottomMargin = Math.min(20, Math.max(10, xLabelLength * 0.8));
437
- this.margin.bottom = Math.max(this.margin.bottom, 40 + extraBottomMargin);
438
- }
439
- if (options.yAxisLabel) {
440
- // Calculate space needed for Y-axis: tick labels + padding + title
441
- const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
442
- const yAxisTitleThickness = 20; // Height of rotated title text
443
- const yAxisTitlePadding = this.Y_AXIS_PADDING;
444
- const totalYAxisWidth = maxTickLabelWidth + yAxisTitlePadding + yAxisTitleThickness;
445
- this.margin.left = Math.max(this.margin.left, totalYAxisWidth);
446
- }
447
- else if (options.showYAxis !== false) {
448
- // Just tick labels, no title
449
- const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
450
- this.margin.left = Math.max(this.margin.left, maxTickLabelWidth + 10);
451
- }
452
- if (options.showXAxis !== false) {
453
- this.margin.bottom = Math.max(this.margin.bottom, this.MIN_MARGIN_BOTTOM);
454
- }
455
- if (options.showYAxis !== false) {
456
- this.margin.left = Math.max(this.margin.left, this.MIN_MARGIN_LEFT);
605
+ const allNumericX = allDataPoints.length === 0 || allDataPoints.every((d) => typeof d.x === 'number');
606
+ let categoricalLabels = null;
607
+ if (!allNumericX) {
608
+ const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
609
+ categoricalLabels = [...new Set(visibleSeries.flatMap((series) => series.data.map((d) => String(d.x))))];
457
610
  }
611
+ this.margin.bottom = this.calculateRequiredBottomMargin(options, estimatedDimensions.width, categoricalLabels);
612
+ this.margin.left = this.calculateRequiredLeftMargin(options, estimatedDimensions.height);
613
+ // Second pass: margins affect inner chart size, which affects label layout
614
+ const refinedDimensions = this.estimateChartDimensions(containerWidth, containerHeight, options);
615
+ this.margin.bottom = this.calculateRequiredBottomMargin(options, refinedDimensions.width, categoricalLabels);
616
+ this.margin.left = this.calculateRequiredLeftMargin(options, refinedDimensions.height);
458
617
  }
459
618
  setupScales(data) {
460
619
  // Expects already filtered data for scales
@@ -469,52 +628,79 @@ class AXLineChartComponent extends AXChartComponent {
469
628
  }
470
629
  const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
471
630
  if (allNumericX) {
631
+ this.xScaleKind = 'linear';
632
+ const xMin = this.d3.min(allDataPoints, (d) => d.x) ?? 0;
472
633
  const xMax = this.d3.max(allDataPoints, (d) => d.x) ?? 0;
473
- if (xMax === 0) {
474
- // If all values are 0, show -1 to 1 range
475
- this.xScale = this.d3.scaleLinear().domain([-1, 1]).range([0, this.width]);
634
+ if (xMin === xMax) {
635
+ this.xScale = this.d3
636
+ .scaleLinear()
637
+ .domain([xMin - 1, xMax + 1])
638
+ .range([0, this.width]);
476
639
  }
477
640
  else {
478
- // Always start X axis from 0 for numeric data
479
- this.xScale = this.d3.scaleLinear().domain([0, xMax]).range([0, this.width]);
641
+ this.xScale = this.d3.scaleLinear().domain([xMin, xMax]).range([0, this.width]);
480
642
  }
481
643
  }
482
644
  else {
645
+ this.xScaleKind = 'point';
646
+ const xDomain = [];
647
+ const seenX = new Set();
648
+ for (const point of allDataPoints) {
649
+ const key = String(point.x);
650
+ if (!seenX.has(key)) {
651
+ seenX.add(key);
652
+ xDomain.push(key);
653
+ }
654
+ }
483
655
  this.xScale = this.d3
484
- .scaleBand()
485
- .domain(allDataPoints.map((d) => String(d.x)))
656
+ .scalePoint()
657
+ .domain(xDomain)
486
658
  .range([0, this.width])
487
- .paddingInner(0.2)
488
- .paddingOuter(0);
659
+ .padding(0);
489
660
  }
490
661
  const yAxisStartsAtZero = chartOptions.yAxisStartsAtZero !== false;
491
- let yMin = this.d3.min(allDataPoints, (d) => d.y) ?? 0;
662
+ const dataYMin = this.d3.min(allDataPoints, (d) => d.y) ?? 0;
663
+ const dataYMax = this.d3.max(allDataPoints, (d) => d.y) ?? 0;
664
+ let yMin;
665
+ let yMax;
492
666
  if (yAxisStartsAtZero) {
493
- yMin = Math.min(0, yMin);
667
+ yMin = Math.min(0, dataYMin);
668
+ yMax = Math.max(0, dataYMax);
494
669
  }
495
- const yMax = this.d3.max(allDataPoints, (d) => d.y) ?? 0;
496
- const yRange = yMax - yMin;
670
+ else {
671
+ yMin = dataYMin;
672
+ yMax = dataYMax;
673
+ }
674
+ const yRange = yMax - yMin || 1;
497
675
  this.yScale = this.d3
498
676
  .scaleLinear()
499
677
  .domain([yMin, yMax + yRange * paddingMultiplier])
500
678
  .nice()
501
679
  .range([this.height, 0]);
680
+ if (yAxisStartsAtZero && dataYMin >= 0) {
681
+ this.yScale.domain([0, this.yScale.domain()[1]]);
682
+ }
502
683
  }
503
684
  createAxes(options) {
504
685
  const showXAxis = options.showXAxis !== false;
505
686
  const showYAxis = options.showYAxis !== false;
506
687
  const showGrid = options.showGrid !== false;
507
- const isBandScale = this.xScale.bandwidth !== undefined;
688
+ const isPointScale = this.xScaleKind === 'point';
689
+ const pointScale = isPointScale ? this.xScale : null;
508
690
  const isRtl = document.documentElement.dir === 'rtl' || document.body.dir === 'rtl';
509
691
  const axesGroup = this.chart.append('g').attr('class', 'ax-line-chart-axes');
510
692
  if (showXAxis) {
511
693
  let xAxisGenerator;
512
694
  let itemCount = 0;
513
- if (isBandScale) {
514
- // Band scale (categorical data)
515
- itemCount = this.xScale.domain().length;
695
+ if (isPointScale && pointScale) {
696
+ itemCount = pointScale.domain().length;
697
+ const pointLayout = this.resolveXAxisLabelLayout(this.width, pointScale.domain());
698
+ this.xAxisLabelLayout.fontSize = pointLayout.fontSize;
699
+ this.xAxisLabelLayout.willRotate = pointLayout.willRotate;
700
+ this.xAxisLabelLayout.tickAreaHeight = pointLayout.tickAreaHeight;
701
+ this.xAxisLabelLayout.displayedTickCount = pointLayout.displayedTickCount;
516
702
  // Smart tick reduction for many items (like bar chart)
517
- let tickValues = this.xScale.domain();
703
+ let tickValues = pointScale.domain();
518
704
  if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
519
705
  // Show every 5th tick for 50+ items
520
706
  tickValues = tickValues.filter((_d, i) => i % 5 === 0);
@@ -524,7 +710,7 @@ class AXLineChartComponent extends AXChartComponent {
524
710
  tickValues = tickValues.filter((_d, i) => i % 2 === 0);
525
711
  }
526
712
  xAxisGenerator = this.d3
527
- .axisBottom(this.xScale)
713
+ .axisBottom(pointScale)
528
714
  .tickValues(tickValues)
529
715
  .tickSize(5)
530
716
  .tickPadding(8)
@@ -567,21 +753,17 @@ class AXLineChartComponent extends AXChartComponent {
567
753
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
568
754
  .attr('stroke-dasharray', '2,2')
569
755
  .attr('stroke-opacity', '0.5');
570
- const dynamicXAxisTickFontSize = this.calculateDynamicFontSize(this.width, 45, this.MIN_FONT_SIZE_X, this.MAX_FONT_SIZE_X);
756
+ const dynamicXAxisTickFontSize = isPointScale
757
+ ? this.xAxisLabelLayout.fontSize
758
+ : this.getXAxisTickFontSize(this.width, itemCount);
571
759
  const xAxisTicks = this.xAxis
572
760
  .selectAll('text')
573
761
  .style('font-size', `${dynamicXAxisTickFontSize}px`)
574
762
  .style('font-weight', '400')
575
- .style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)');
576
- // Automatically rotate labels if they are likely to overlap (only for band scale)
577
- let labelsAreRotated = false;
578
- if (isBandScale && this.xScale.bandwidth && this.xScale.domain().length > 0) {
579
- const step = this.xScale.step();
580
- const longestLabel = this.xScale.domain().reduce((a, b) => (a.length > b.length ? a : b), '');
581
- // Using char width ratio constant for better estimate
582
- const estimatedLongestLabelWidth = longestLabel.length * dynamicXAxisTickFontSize * this.CHAR_WIDTH_RATIO;
583
- if (estimatedLongestLabelWidth > step) {
584
- labelsAreRotated = true;
763
+ .style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)')
764
+ .attr('direction', 'ltr');
765
+ if (isPointScale && pointScale && pointScale.domain().length > 0) {
766
+ if (this.xAxisLabelLayout.willRotate) {
585
767
  xAxisTicks
586
768
  .attr('transform', 'rotate(-45)')
587
769
  .style('text-anchor', 'end')
@@ -589,24 +771,9 @@ class AXLineChartComponent extends AXChartComponent {
589
771
  .attr('dy', '0.15em');
590
772
  }
591
773
  }
592
- // Only show X-axis title if labels are NOT rotated and item count is reasonable
593
- // This prevents overlap with rotated labels and improves readability
594
- const shouldShowXAxisTitle = options.xAxisLabel && !labelsAreRotated && itemCount <= this.MANY_ITEMS_THRESHOLD;
595
- if (shouldShowXAxisTitle) {
596
- axesGroup
597
- .append('text')
598
- .attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
599
- .attr('text-anchor', 'middle')
600
- .attr('x', this.width / 2)
601
- .attr('y', this.height + this.margin.bottom - 5)
602
- .attr('direction', 'ltr')
603
- .attr('style', `
604
- font-size: 14px;
605
- font-weight: 500;
606
- fill: rgb(var(--ax-comp-line-chart-text-color));
607
- pointer-events: none;
608
- `)
609
- .text(options.xAxisLabel);
774
+ const xTickAreaHeight = this.measureXAxisTickAreaHeight(this.xAxis.node(), dynamicXAxisTickFontSize, this.xAxisLabelLayout.tickAreaHeight);
775
+ if (options.xAxisLabel) {
776
+ this.createXAxisTitle(axesGroup, options.xAxisLabel, xTickAreaHeight, dynamicXAxisTickFontSize);
610
777
  }
611
778
  }
612
779
  if (showYAxis) {
@@ -635,7 +802,7 @@ class AXLineChartComponent extends AXChartComponent {
635
802
  .attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
636
803
  .attr('stroke-dasharray', '2,2')
637
804
  .attr('stroke-opacity', '0.5');
638
- const dynamicYAxisTickFontSize = this.calculateDynamicFontSize(this.height, 30, this.MIN_FONT_SIZE_Y, this.MAX_FONT_SIZE_Y);
805
+ const dynamicYAxisTickFontSize = this.getYAxisTickFontSize(this.height);
639
806
  const yTickTexts = this.yAxis.selectAll('text').attr('style', `
640
807
  font-size: ${dynamicYAxisTickFontSize}px;
641
808
  font-weight: 400;
@@ -644,31 +811,13 @@ class AXLineChartComponent extends AXChartComponent {
644
811
  if (isRtl) {
645
812
  yTickTexts.attr('text-anchor', 'start');
646
813
  }
814
+ else {
815
+ yTickTexts.attr('text-anchor', 'end').attr('direction', 'ltr');
816
+ }
817
+ const yTickNodes = (yTickTexts.nodes() || []);
818
+ const maxTickLabelWidth = this.measureMaxYAxisTickLabelWidth(yTickNodes, dynamicYAxisTickFontSize);
647
819
  if (options.yAxisLabel) {
648
- // Calculate proper position for Y-axis title
649
- // Position it to the left of the tick labels with proper spacing
650
- const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
651
- const padding = this.Y_AXIS_PADDING;
652
- // Position title to the left of the tick labels
653
- const labelY = -maxTickLabelWidth - padding;
654
- // Center the title vertically
655
- const labelX = -this.height / 2;
656
- axesGroup
657
- .append('text')
658
- .attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
659
- .attr('text-anchor', 'middle')
660
- .attr('dominant-baseline', 'middle')
661
- .attr('transform', 'rotate(-90)')
662
- .attr('x', labelX)
663
- .attr('y', labelY)
664
- .attr('direction', 'ltr')
665
- .attr('style', `
666
- font-size: 14px;
667
- font-weight: 500;
668
- fill: rgb(var(--ax-comp-line-chart-text-color));
669
- pointer-events: none;
670
- `)
671
- .text(options.yAxisLabel);
820
+ this.createYAxisTitle(axesGroup, options.yAxisLabel, dynamicYAxisTickFontSize, maxTickLabelWidth);
672
821
  }
673
822
  }
674
823
  if (showGrid) {
@@ -701,7 +850,7 @@ class AXLineChartComponent extends AXChartComponent {
701
850
  .axisBottom(this.xScale)
702
851
  .tickSize(-this.height)
703
852
  .tickFormat(() => '')
704
- .tickValues(isBandScale ? undefined : this.xScale.ticks()));
853
+ .tickValues(isPointScale ? undefined : this.xScale.ticks()));
705
854
  // Style the vertical grid path and lines
706
855
  xGrid.selectAll('path').attr('stroke-width', '0');
707
856
  xGrid
@@ -713,16 +862,14 @@ class AXLineChartComponent extends AXChartComponent {
713
862
  }
714
863
  }
715
864
  }
865
+ getXPosition(point) {
866
+ if (this.xScaleKind === 'point') {
867
+ return this.xScale(String(point.x)) ?? 0;
868
+ }
869
+ return this.xScale(point.x);
870
+ }
716
871
  renderLines(allSeriesData) {
717
- const isBandScale = this.xScale.bandwidth !== undefined;
718
- const getX = (d) => {
719
- if (isBandScale) {
720
- return this.xScale(String(d.x)) + this.xScale.bandwidth() / 2;
721
- }
722
- else {
723
- return this.xScale(d.x);
724
- }
725
- };
872
+ const getX = (d) => this.getXPosition(d);
726
873
  const lineGenerator = this.d3
727
874
  .line()
728
875
  .x(getX)
@@ -898,23 +1045,23 @@ class AXLineChartComponent extends AXChartComponent {
898
1045
  else {
899
1046
  this.handlePointHover(event, d, series, series.originalIndex);
900
1047
  }
1048
+ const pointRadius = this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS;
901
1049
  this.d3
902
1050
  .select(event.target)
903
- .transition()
904
- .duration(this.HOVER_TRANSITION_DURATION)
905
- .attr('r', (this.effectiveOptions().pointRadius ?? 4) * 1.5)
906
- .attr('stroke-width', 2)
1051
+ .interrupt()
1052
+ .attr('r', pointRadius * this.POINT_HIGHLIGHT_MULTIPLIER)
1053
+ .attr('stroke-width', this.POINT_HOVER_STROKE_WIDTH)
907
1054
  .attr('stroke', `rgb(var(--ax-comp-line-chart-bg-color))`);
908
1055
  })
909
1056
  .on('mousemove', (event) => this.updateTooltipPosition(event))
910
1057
  .on('mouseleave', (event) => {
911
1058
  this._tooltipVisible.set(false);
1059
+ const pointRadius = this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS;
912
1060
  this.d3
913
1061
  .select(event.target)
914
- .transition()
915
- .duration(this.HOVER_TRANSITION_DURATION)
916
- .attr('r', this.effectiveOptions().pointRadius ?? this.DEFAULT_POINT_RADIUS)
917
- .attr('stroke-width', 1)
1062
+ .interrupt()
1063
+ .attr('r', pointRadius)
1064
+ .attr('stroke-width', this.POINT_STROKE_WIDTH)
918
1065
  .attr('stroke', '#fff');
919
1066
  })
920
1067
  .on('click', (event, d) => {
@@ -1009,14 +1156,7 @@ class AXLineChartComponent extends AXChartComponent {
1009
1156
  }
1010
1157
  }
1011
1158
  showCrosshairLines(dataPoint) {
1012
- const isBandScale = this.xScale.bandwidth !== undefined;
1013
- let x;
1014
- if (isBandScale) {
1015
- x = this.xScale(String(dataPoint.x)) + this.xScale.bandwidth() / 2;
1016
- }
1017
- else {
1018
- x = this.xScale(dataPoint.x);
1019
- }
1159
+ const x = this.getXPosition(dataPoint);
1020
1160
  const y = this.yScale(dataPoint.y);
1021
1161
  let crosshairGroup = this.chart.select('.ax-line-chart-crosshair');
1022
1162
  if (crosshairGroup.empty()) {
@@ -1052,19 +1192,33 @@ class AXLineChartComponent extends AXChartComponent {
1052
1192
  this._pendingTooltipCoords = { x: event.clientX, y: event.clientY };
1053
1193
  if (this._tooltipRafId != null)
1054
1194
  return;
1195
+ this.scheduleTooltipPosition(0);
1196
+ }
1197
+ scheduleTooltipPosition(attempt) {
1055
1198
  this._tooltipRafId = requestAnimationFrame(() => {
1056
- this._tooltipRafId = null;
1057
1199
  const coords = this._pendingTooltipCoords;
1058
- if (!coords)
1200
+ if (!coords) {
1201
+ this._tooltipRafId = null;
1059
1202
  return;
1203
+ }
1060
1204
  const containerEl = this.chartContainerEl()?.nativeElement;
1061
- if (!containerEl)
1205
+ if (!containerEl) {
1206
+ this._tooltipRafId = null;
1062
1207
  return;
1063
- const rect = containerEl.getBoundingClientRect();
1208
+ }
1064
1209
  const tooltipEl = containerEl.querySelector('.chart-tooltip');
1210
+ if (!tooltipEl && this._tooltipVisible() && attempt < 3) {
1211
+ this.scheduleTooltipPosition(attempt + 1);
1212
+ return;
1213
+ }
1214
+ this._tooltipRafId = null;
1215
+ const rect = containerEl.getBoundingClientRect();
1065
1216
  const tooltipRect = tooltipEl ? tooltipEl.getBoundingClientRect() : null;
1066
1217
  const pos = computeTooltipPosition(rect, tooltipRect, coords.x, coords.y, this.TOOLTIP_GAP);
1067
- this._tooltipPosition.set(pos);
1218
+ const prev = this._tooltipPosition();
1219
+ if (prev.x !== pos.x || prev.y !== pos.y) {
1220
+ this._tooltipPosition.set(pos);
1221
+ }
1068
1222
  });
1069
1223
  }
1070
1224
  // computeTooltipPosition moved to shared chart utils
@@ -1161,12 +1315,12 @@ class AXLineChartComponent extends AXChartComponent {
1161
1315
  this.showCrosshairLines(overlappingPoints[0].point);
1162
1316
  }
1163
1317
  }
1164
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1165
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.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:0;box-sizing:border-box;--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;box-sizing:border-box;padding:clamp(.5rem,1.2vw,.875rem);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{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}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 });
1318
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1319
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", 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:0;box-sizing:border-box;--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;box-sizing:border-box;padding:clamp(.25rem,.6vw,.5rem);border-radius:.5rem;overflow:hidden;contain:layout style;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{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;flex-shrink:0}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 });
1166
1320
  }
1167
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXLineChartComponent, decorators: [{
1321
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXLineChartComponent, decorators: [{
1168
1322
  type: Component,
1169
- args: [{ selector: 'ax-line-chart', 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:0;box-sizing:border-box;--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;box-sizing:border-box;padding:clamp(.5rem,1.2vw,.875rem);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{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}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"] }]
1323
+ args: [{ selector: 'ax-line-chart', 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:0;box-sizing:border-box;--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;box-sizing:border-box;padding:clamp(.25rem,.6vw,.5rem);border-radius:.5rem;overflow:hidden;contain:layout style;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{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;flex-shrink:0}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"] }]
1170
1324
  }], 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 }] }] } });
1171
1325
 
1172
1326
  /**