@acorex/charts 21.0.0-next.2 → 21.0.0-next.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,7 @@
1
- import { NXComponent } from '@acorex/cdk/common';
2
- import { AX_CHART_COLOR_PALETTE, getEasingFunction, computeTooltipPosition, getChartColor } from '@acorex/charts';
1
+ import { AXChartComponent, AX_CHART_COLOR_PALETTE, formatLargeNumber, getEasingFunction, getChartColor } from '@acorex/charts';
3
2
  import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
4
3
  import * as i0 from '@angular/core';
5
- import { InjectionToken, inject, input, output, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
6
- import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
7
- import { set } from 'lodash-es';
4
+ import { InjectionToken, input, output, viewChild, signal, inject, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
8
5
 
9
6
  const AXBarChartDefaultConfig = {
10
7
  showXAxis: true,
@@ -28,11 +25,7 @@ const AXBarChartDefaultConfig = {
28
25
  };
29
26
  const AX_BAR_CHART_CONFIG = new InjectionToken('AX_BAR_CHART_CONFIG', {
30
27
  providedIn: 'root',
31
- factory: () => {
32
- const global = inject(AX_GLOBAL_CONFIG);
33
- set(global, 'chart.barChart', AXBarChartDefaultConfig);
34
- return AXBarChartDefaultConfig;
35
- },
28
+ factory: () => AXBarChartDefaultConfig,
36
29
  });
37
30
  function barChartConfig(config = {}) {
38
31
  const result = {
@@ -46,7 +39,7 @@ function barChartConfig(config = {}) {
46
39
  * Bar Chart Component
47
40
  * Renders data as vertical bars with interactive hover effects and animations
48
41
  */
49
- class AXBarChartComponent extends NXComponent {
42
+ class AXBarChartComponent extends AXChartComponent {
50
43
  // Inputs
51
44
  /** Chart data input */
52
45
  data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
@@ -59,7 +52,7 @@ class AXBarChartComponent extends NXComponent {
59
52
  chartContainerEl = viewChild.required('chartContainer');
60
53
  // D3 reference - loaded asynchronously
61
54
  d3;
62
- // Chart elements
55
+ // Chart elements - using proper D3 types
63
56
  svg;
64
57
  chart;
65
58
  xScale;
@@ -71,10 +64,42 @@ class AXBarChartComponent extends NXComponent {
71
64
  width;
72
65
  height;
73
66
  margin = { top: 0, right: 0, bottom: 0, left: 0 };
74
- // Character width ratio heuristic for mixed Latin/Persian texts
67
+ // Constants for layout calculations
75
68
  CHAR_WIDTH_RATIO = 0.65;
76
- // Tooltip gap in pixels for consistent spacing
77
69
  TOOLTIP_GAP = 10;
70
+ AXIS_TICK_PADDING = 10;
71
+ Y_AXIS_TITLE_THICKNESS = 14;
72
+ Y_AXIS_TITLE_PADDING = 14;
73
+ X_AXIS_TITLE_FONT_SIZE = 14;
74
+ X_AXIS_TITLE_GAP = 14;
75
+ X_AXIS_TITLE_BLOCK_HEIGHT = 24;
76
+ TOP_EDGE_PADDING = 6;
77
+ FALLBACK_PLOT_HEIGHT = 240;
78
+ MIN_PLOT_HEIGHT = 120;
79
+ MIN_TOTAL_WIDTH = 200;
80
+ OUTER_PADDING_CLUSTERED = 0.2;
81
+ INNER_PADDING_CLUSTERED = 0.1;
82
+ MIN_PADDING = 0.1;
83
+ HOVER_TRANSITION_DURATION = 150;
84
+ DEFAULT_ANIMATION_DELAY = 50;
85
+ LABEL_ANIMATION_DELAY = 100;
86
+ MIN_TOP_SPACE_FOR_LABEL = 20;
87
+ LABEL_OFFSET = 8;
88
+ ROTATION_TOLERANCE_SMALL_DATASET = 1.4;
89
+ SMALL_DATASET_THRESHOLD = 6;
90
+ Y_AXIS_PADDING = 20;
91
+ TICK_AREA_PADDING = 6;
92
+ TICK_AREA_GAP = 8;
93
+ DEFAULT_FONT_SIZE = 13;
94
+ FONT_WIDTH_MULTIPLIER = 0.6;
95
+ FONT_PADDING = 8;
96
+ MAX_LABEL_LENGTH = 20;
97
+ MIN_FONT_SIZE_X_AXIS = 8;
98
+ MAX_FONT_SIZE_X_AXIS = 14;
99
+ MIN_FONT_SIZE_Y_AXIS = 9;
100
+ MAX_FONT_SIZE_Y_AXIS = 15;
101
+ MANY_ITEMS_THRESHOLD = 20;
102
+ VERY_MANY_ITEMS_THRESHOLD = 50;
78
103
  // Animation state
79
104
  _initialAnimationComplete = signal(false, ...(ngDevMode ? [{ debugName: "_initialAnimationComplete" }] : []));
80
105
  // Tooltip state
@@ -121,17 +146,39 @@ class AXBarChartComponent extends NXComponent {
121
146
  ...this.effectiveOptions().messages,
122
147
  };
123
148
  }, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : []));
124
- // Track hidden bars
149
+ // Track hidden bars and series
125
150
  hiddenBars = new Set();
126
- // Track hidden series for clustered charts (by series label)
127
151
  hiddenSeries = new Set();
128
- // Helpers
152
+ // Computed values for better performance
153
+ isClusteredMode = computed(() => {
154
+ const value = this.data();
155
+ return Array.isArray(value) && value.length > 0 && 'chartData' in value[0];
156
+ }, ...(ngDevMode ? [{ debugName: "isClusteredMode" }] : []));
157
+ visibleData = computed(() => {
158
+ const value = this.data() || [];
159
+ if (this.isClusteredMode()) {
160
+ return value;
161
+ }
162
+ return value.filter((d) => !this.hiddenBars.has(d.id));
163
+ }, ...(ngDevMode ? [{ debugName: "visibleData" }] : []));
164
+ hasVisibleData = computed(() => {
165
+ const value = this.data();
166
+ if (!Array.isArray(value) || value.length === 0)
167
+ return false;
168
+ if (this.isClusteredMode()) {
169
+ const groups = value;
170
+ const seriesLabels = this.getClusterSeriesLabels(groups);
171
+ const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
172
+ return groups.some((g) => visibleSeries.some((_, idx) => !!g.chartData[idx]));
173
+ }
174
+ return value.some((d) => !this.hiddenBars.has(d.id));
175
+ }, ...(ngDevMode ? [{ debugName: "hasVisibleData" }] : []));
176
+ // Type guard helper
129
177
  isClusteredData(value) {
130
- return Array.isArray(value) && value.length > 0 && value[0] && 'chartData' in value[0];
178
+ return Array.isArray(value) && value.length > 0 && 'chartData' in value[0];
131
179
  }
132
180
  getClusterSeriesLabels(groups) {
133
- const first = groups[0]?.chartData ?? [];
134
- return first.map((d) => d.label);
181
+ return groups[0]?.chartData?.map((d) => d.label) ?? [];
135
182
  }
136
183
  constructor() {
137
184
  super();
@@ -184,25 +231,14 @@ class AXBarChartComponent extends NXComponent {
184
231
  const containerElement = this.chartContainerEl().nativeElement;
185
232
  const inputValue = (this.data() || []);
186
233
  const isClustered = this.isClusteredData(inputValue);
187
- // Visibility calculation
188
- let hasAnyVisible = false;
189
- let visibleSingleData = [];
190
- let groupedData = [];
191
- if (isClustered) {
192
- groupedData = inputValue;
193
- const seriesLabels = groupedData.length > 0 ? this.getClusterSeriesLabels(groupedData) : [];
194
- const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
195
- hasAnyVisible = groupedData.some((g) => visibleSeries.some((_, idx) => !!g.chartData[idx]));
196
- }
197
- else {
198
- const single = inputValue || [];
199
- visibleSingleData = single.filter((d) => !this.isBarHidden(d.id));
200
- hasAnyVisible = visibleSingleData.length > 0;
201
- }
234
+ // Use computed visibility
235
+ const hasAnyVisible = this.hasVisibleData();
236
+ const visibleSingleData = isClustered ? [] : this.visibleData();
237
+ const groupedData = isClustered ? inputValue : [];
202
238
  // Clear existing chart SVG and messages (do not remove tooltip component)
203
239
  this.d3.select(containerElement).selectAll('svg, .ax-chart-message-container').remove();
204
240
  // Early return if no data
205
- if (!inputValue.length) {
241
+ if (!Array.isArray(inputValue) || inputValue.length === 0) {
206
242
  this.showNoDataMessage(containerElement);
207
243
  return;
208
244
  }
@@ -241,8 +277,8 @@ class AXBarChartComponent extends NXComponent {
241
277
  // Only remove chart SVG and message containers to preserve tooltip component
242
278
  this.d3?.select(container).selectAll('svg, .ax-chart-message-container').remove();
243
279
  }
244
- this.svg = null;
245
- this.chart = null;
280
+ this.svg = undefined;
281
+ this.chart = undefined;
246
282
  this._tooltipVisible.set(false);
247
283
  }
248
284
  /**
@@ -258,11 +294,10 @@ class AXBarChartComponent extends NXComponent {
258
294
  const showXAxis = options.showXAxis !== false;
259
295
  const showYAxis = options.showYAxis !== false;
260
296
  // Internal left padding for Y-axis ticks/labels/title
261
- const axisAndTickPadding = 10;
262
- const yAxisTitleThickness = options.yAxisLabel ? 14 : 0; // rotated label thickness along X
263
- const yAxisTitlePadding = options.yAxisLabel ? 14 : 0; // gap between ticks and title
297
+ const yAxisTitleThickness = options.yAxisLabel ? this.Y_AXIS_TITLE_THICKNESS : 0;
298
+ const yAxisTitlePadding = options.yAxisLabel ? this.Y_AXIS_TITLE_PADDING : 0;
264
299
  const leftPaddingForYAxis = showYAxis
265
- ? this.calculateMaxYAxisTickLabelWidth() + axisAndTickPadding + yAxisTitlePadding + yAxisTitleThickness
300
+ ? this.calculateMaxYAxisTickLabelWidth() + this.AXIS_TICK_PADDING + yAxisTitlePadding + yAxisTitleThickness
266
301
  : 0;
267
302
  // Estimate internal bottom padding for X-axis ticks/labels/title
268
303
  let bottomPaddingForXAxis = 0;
@@ -281,41 +316,29 @@ class AXBarChartComponent extends NXComponent {
281
316
  const baseWidth = options.width ?? containerWidth;
282
317
  const workingWidth = Math.max(1, baseWidth - leftPaddingForYAxis);
283
318
  const dynamicFontSize = Math.max(10, Math.min(14, Math.round(workingWidth / 50)));
284
- const estimatedLongestLabelWidth = longestLabel.length * dynamicFontSize * 0.65;
319
+ // Use truncated label length for calculation (same logic as actual rendering)
320
+ const maxLabelLength = barCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
321
+ const effectiveLabelLength = Math.min(longestLabel.length, maxLabelLength);
322
+ const estimatedLongestLabelWidth = effectiveLabelLength * dynamicFontSize * 0.65;
285
323
  const availableWidthPerBar = barCount > 0 ? workingWidth / barCount : workingWidth;
286
- const willRotate = options.rotateXAxisLabels === true
287
- ? true
288
- : options.rotateXAxisLabels === false
289
- ? false
290
- : estimatedLongestLabelWidth > availableWidthPerBar;
324
+ const rotateOption = typeof options.rotateXAxisLabels === 'boolean' ? options.rotateXAxisLabels : undefined;
325
+ const willRotate = this.shouldLabelsRotate(rotateOption, estimatedLongestLabelWidth, availableWidthPerBar);
291
326
  const tickAreaHeight = willRotate
292
- ? estimatedLongestLabelWidth * Math.SQRT1_2 + dynamicFontSize * Math.SQRT1_2 + 6
293
- : dynamicFontSize + 8;
294
- const gapBelowTicks = options.xAxisLabel && !willRotate ? 14 : 0; // matches renderer
295
- const titleBlockHeight = options.xAxisLabel && !willRotate ? 24 : 0; // 14px font + ~10px gap
327
+ ? estimatedLongestLabelWidth * Math.SQRT1_2 + dynamicFontSize * Math.SQRT1_2 + this.TICK_AREA_PADDING
328
+ : dynamicFontSize + this.TICK_AREA_PADDING + 2;
329
+ const gapBelowTicks = options.xAxisLabel && !willRotate ? this.X_AXIS_TITLE_GAP : 0;
330
+ const titleBlockHeight = options.xAxisLabel && !willRotate ? this.X_AXIS_TITLE_BLOCK_HEIGHT : 0;
296
331
  bottomPaddingForXAxis = tickAreaHeight + gapBelowTicks + titleBlockHeight;
297
332
  }
298
333
  // Determine plotting dimensions with sensible fallbacks
299
- const fallbackPlotHeight = 240; // ensures visible plot when container has no height
300
334
  const baseWidth = options.width ?? containerWidth;
301
- const totalWidth = Math.max(200, baseWidth > 0 ? baseWidth : 200);
335
+ const totalWidth = Math.max(this.MIN_TOTAL_WIDTH, baseWidth > 0 ? baseWidth : this.MIN_TOTAL_WIDTH);
302
336
  const plotWidth = Math.max(0, totalWidth - leftPaddingForYAxis);
303
- let plotHeight;
304
- if (options.height && options.height > 0) {
305
- plotHeight = Math.max(120, options.height - bottomPaddingForXAxis);
306
- }
307
- else if (containerHeight > 0) {
308
- plotHeight = Math.max(120, containerHeight - bottomPaddingForXAxis, fallbackPlotHeight);
309
- }
310
- else {
311
- plotHeight = fallbackPlotHeight;
312
- }
337
+ const plotHeight = this.calculatePlotHeight(options.height, containerHeight, bottomPaddingForXAxis);
313
338
  this.width = plotWidth;
314
339
  this.height = plotHeight;
315
340
  // Create SVG with explicit pixel dimensions to avoid collapse
316
- // Add a small internal top padding to avoid clipping the top-most Y-axis label
317
- const topPaddingForTopEdge = 6;
318
- const totalHeight = this.height + bottomPaddingForXAxis + topPaddingForTopEdge;
341
+ const totalHeight = this.height + bottomPaddingForXAxis + this.TOP_EDGE_PADDING;
319
342
  const svg = this.d3
320
343
  .select(containerElement)
321
344
  .append('svg')
@@ -328,17 +351,38 @@ class AXBarChartComponent extends NXComponent {
328
351
  this.chart = this.svg
329
352
  .append('g')
330
353
  .attr('class', 'chart-content')
331
- .attr('transform', `translate(${leftPaddingForYAxis},${topPaddingForTopEdge})`);
354
+ .attr('transform', `translate(${leftPaddingForYAxis},${this.TOP_EDGE_PADDING})`);
355
+ }
356
+ /**
357
+ * Calculates plot height based on options and container dimensions
358
+ */
359
+ calculatePlotHeight(optionHeight, containerHeight, bottomPadding) {
360
+ if (optionHeight && optionHeight > 0) {
361
+ return Math.max(this.MIN_PLOT_HEIGHT, optionHeight - bottomPadding);
362
+ }
363
+ if (containerHeight > 0) {
364
+ return Math.max(this.MIN_PLOT_HEIGHT, containerHeight - bottomPadding, this.FALLBACK_PLOT_HEIGHT);
365
+ }
366
+ return this.FALLBACK_PLOT_HEIGHT;
367
+ }
368
+ /**
369
+ * Determines if X-axis labels should rotate
370
+ */
371
+ shouldLabelsRotate(rotateOption, estimatedWidth, availableWidth) {
372
+ if (rotateOption === true)
373
+ return true;
374
+ if (rotateOption === false)
375
+ return false;
376
+ return estimatedWidth > availableWidth;
332
377
  }
333
- // calculateMargins was unused; removed to simplify and reduce maintenance surface
334
378
  /**
335
379
  * Creates x and y scales for the chart
336
380
  */
337
381
  setupScales(data) {
338
- // Get the bar width percentage (default 80%)
339
- const barWidthPercent = this.effectiveOptions().barWidth ?? 60 / 100;
382
+ // Get the bar width percentage (default 60%)
383
+ const barWidthPercent = this.effectiveOptions().barWidth ?? 0.6;
340
384
  // Calculate padding based on barWidth (inverse relationship)
341
- const padding = Math.max(0.1, 1 - barWidthPercent);
385
+ const padding = Math.max(this.MIN_PADDING, 1 - barWidthPercent);
342
386
  // Create x scale (band scale for categorical data)
343
387
  this.xScale = this.d3
344
388
  .scaleBand()
@@ -346,9 +390,12 @@ class AXBarChartComponent extends NXComponent {
346
390
  .range([0, this.width])
347
391
  .padding(padding);
348
392
  // Create y scale (linear scale for values)
393
+ // Support negative values by calculating min/max
394
+ const minValue = this.d3.min(data, (d) => d.value) || 0;
395
+ const maxValue = this.d3.max(data, (d) => d.value) || 0;
349
396
  this.yScale = this.d3
350
397
  .scaleLinear()
351
- .domain([0, this.d3.max(data, (d) => d.value) || 0])
398
+ .domain([Math.min(0, minValue), Math.max(0, maxValue)])
352
399
  .nice()
353
400
  .range([this.height, 0]);
354
401
  }
@@ -356,18 +403,21 @@ class AXBarChartComponent extends NXComponent {
356
403
  * Creates x and y scales for clustered charts
357
404
  */
358
405
  setupScalesClustered(groups) {
359
- const seriesLabels = groups.length > 0 ? this.getClusterSeriesLabels(groups) : [];
406
+ const seriesLabels = this.getClusterSeriesLabels(groups);
360
407
  const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
361
408
  // Outer band for clusters
362
- const outerPadding = 0.2;
363
409
  this.xScale = this.d3
364
410
  .scaleBand()
365
411
  .domain(groups.map((g) => g.id))
366
412
  .range([0, this.width])
367
- .padding(outerPadding);
413
+ .padding(this.OUTER_PADDING_CLUSTERED);
368
414
  // Inner band for series
369
- this.xSubScale = this.d3.scaleBand().domain(visibleSeries).range([0, this.xScale.bandwidth()]).padding(0.1);
370
- // Y scale across all visible values
415
+ this.xSubScale = this.d3
416
+ .scaleBand()
417
+ .domain(visibleSeries)
418
+ .range([0, this.xScale.bandwidth()])
419
+ .padding(this.INNER_PADDING_CLUSTERED);
420
+ // Y scale across all visible values (support negative values)
371
421
  const allVisibleValues = [];
372
422
  for (const g of groups) {
373
423
  visibleSeries.forEach((_, idx) => {
@@ -376,8 +426,13 @@ class AXBarChartComponent extends NXComponent {
376
426
  allVisibleValues.push(item.value);
377
427
  });
378
428
  }
429
+ const yMin = this.d3.min(allVisibleValues) || 0;
379
430
  const yMax = this.d3.max(allVisibleValues) || 0;
380
- this.yScale = this.d3.scaleLinear().domain([0, yMax]).nice().range([this.height, 0]);
431
+ this.yScale = this.d3
432
+ .scaleLinear()
433
+ .domain([Math.min(0, yMin), Math.max(0, yMax)])
434
+ .nice()
435
+ .range([this.height, 0]);
381
436
  }
382
437
  /**
383
438
  * Creates x and y axes with grid lines
@@ -403,8 +458,20 @@ class AXBarChartComponent extends NXComponent {
403
458
  this.buildXAxisCommon(axesGroup, idToLabel, options);
404
459
  }
405
460
  createYAxis(axesGroup, options) {
406
- // Create Y axis
407
- this.yAxis = axesGroup.append('g').attr('class', 'ax-bar-chart-axis-y').call(this.d3.axisLeft(this.yScale));
461
+ // Create Y axis with smart number formatting
462
+ const yAxisGenerator = this.d3.axisLeft(this.yScale).tickFormat((value) => {
463
+ const numValue = typeof value === 'number' ? value : value.valueOf();
464
+ return formatLargeNumber(numValue);
465
+ });
466
+ // Reduce tick count for better readability
467
+ const maxValue = this.yScale.domain()[1];
468
+ if (maxValue > 1e6) {
469
+ yAxisGenerator.ticks(6); // Fewer ticks for large numbers
470
+ }
471
+ else if (maxValue > 1e3) {
472
+ yAxisGenerator.ticks(8);
473
+ }
474
+ this.yAxis = axesGroup.append('g').attr('class', 'ax-bar-chart-axis-y').call(yAxisGenerator);
408
475
  // Style the axis text
409
476
  const dynamicFontSize = this.getYAxisTickFontSizeBasedOnHeight();
410
477
  this.yAxis
@@ -435,14 +502,14 @@ class AXBarChartComponent extends NXComponent {
435
502
  const labels = Array.from(idToLabel.values());
436
503
  const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
437
504
  const estimatedWidth = longestLabel.length * fontSize * this.CHAR_WIDTH_RATIO;
438
- // Heuristic: avoid rotation when there are few categories (<= 6) even if estimation is slightly larger
505
+ // Heuristic: avoid rotation when there are few categories even if estimation is slightly larger
439
506
  const domainCount = this.xScale.domain().length;
440
- if (domainCount <= 6) {
441
- return estimatedWidth > step * 1.4; // be more tolerant for small datasets
507
+ if (domainCount <= this.SMALL_DATASET_THRESHOLD) {
508
+ return estimatedWidth > step * this.ROTATION_TOLERANCE_SMALL_DATASET;
442
509
  }
443
510
  return estimatedWidth > step;
444
511
  }
445
- createXAxisTitle(axesGroup, title, tickAreaHeight, gapBelowTicks = 8) {
512
+ createXAxisTitle(axesGroup, title, tickAreaHeight, gapBelowTicks = this.TICK_AREA_GAP) {
446
513
  // Title positioned directly below tick area with a consistent gap
447
514
  const labelY = this.height + tickAreaHeight + gapBelowTicks;
448
515
  axesGroup
@@ -453,7 +520,7 @@ class AXBarChartComponent extends NXComponent {
453
520
  .attr('x', this.width / 2)
454
521
  .attr('y', labelY)
455
522
  .attr('direction', 'ltr')
456
- .style('font-size', '14px')
523
+ .style('font-size', `${this.X_AXIS_TITLE_FONT_SIZE}px`)
457
524
  .style('font-weight', '500')
458
525
  .style('fill', 'rgb(var(--ax-comp-bar-chart-axis-label-color))')
459
526
  .text(title);
@@ -465,7 +532,7 @@ class AXBarChartComponent extends NXComponent {
465
532
  // Chart group is translated by (margin.left, margin.top)
466
533
  // So Y-axis is at X = margin.left in SVG coordinates
467
534
  const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
468
- const padding = 20; // Increased padding between tick labels and axis title
535
+ const padding = this.Y_AXIS_PADDING;
469
536
  // Position title to the left of the tick labels
470
537
  // Since text-anchor: end aligns the text to the Y-axis, we need to go further left
471
538
  const labelY = -maxTickLabelWidth - padding;
@@ -506,9 +573,9 @@ class AXBarChartComponent extends NXComponent {
506
573
  const visibleData = raw.filter((d) => !this.isBarHidden(d.id));
507
574
  maxValue = visibleData.reduce((acc, d) => (d.value > acc ? d.value : acc), 0);
508
575
  }
509
- const tickLabelText = Number.isFinite(maxValue) ? maxValue.toLocaleString() : '00000';
510
- const fontSize = 13; // Approximate font size for Y-axis tick labels
511
- return tickLabelText.length * fontSize * 0.6 + 8; // Character width + padding
576
+ // Use formatted number for width calculation
577
+ const tickLabelText = Number.isFinite(maxValue) ? formatLargeNumber(maxValue) : '00000';
578
+ return tickLabelText.length * this.DEFAULT_FONT_SIZE * this.FONT_WIDTH_MULTIPLIER + this.FONT_PADDING;
512
579
  }
513
580
  createGridLines() {
514
581
  this.chart
@@ -599,14 +666,30 @@ class AXBarChartComponent extends NXComponent {
599
666
  .style('font-weight', '500')
600
667
  .style('fill', 'rgb(var(--ax-comp-bar-chart-data-labels-color))')
601
668
  .style('opacity', 0) // Start invisible for animation
602
- .text((d) => d.value);
603
- // Animate data labels
669
+ .text((d) => {
670
+ // Format large numbers in data labels
671
+ return d.value >= 1000 ? formatLargeNumber(d.value) : d.value;
672
+ });
673
+ // Animate data labels with smart positioning to avoid clipping
674
+ const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
604
675
  barGroups
605
676
  .selectAll('.ax-bar-chart-data-label')
606
677
  .transition()
607
678
  .duration(animationDuration)
608
- .delay((d, i) => i * 50 + 100) // Slightly delayed after bar animation
609
- .attr('y', (d) => this.yScale(d.value) - 8) // Position above bar
679
+ .delay((d, i) => i * animationDelay + this.LABEL_ANIMATION_DELAY)
680
+ .attr('y', (d) => {
681
+ const barTop = this.yScale(d.value);
682
+ // If bar is near the top (100% or close), position label inside the bar
683
+ if (barTop < this.MIN_TOP_SPACE_FOR_LABEL) {
684
+ return barTop + this.LABEL_OFFSET;
685
+ }
686
+ // Otherwise, position above the bar
687
+ return barTop - this.LABEL_OFFSET;
688
+ })
689
+ .attr('data-inside-bar', (d) => {
690
+ const barTop = this.yScale(d.value);
691
+ return barTop < this.MIN_TOP_SPACE_FOR_LABEL ? 'true' : 'false';
692
+ })
610
693
  .style('opacity', 1)
611
694
  .ease(animationEasing);
612
695
  }
@@ -618,7 +701,10 @@ class AXBarChartComponent extends NXComponent {
618
701
  return;
619
702
  const barEl = this.d3.select(event.currentTarget).select('path');
620
703
  // Standard hover effect - darken the bar slightly and add a subtle shadow
621
- barEl.transition().duration(150).style('filter', 'brightness(0.7) drop-shadow(0 0 2px rgba(0,0,0,0.1))');
704
+ barEl
705
+ .transition()
706
+ .duration(this.HOVER_TRANSITION_DURATION)
707
+ .style('filter', 'brightness(0.7) drop-shadow(0 0 2px rgba(0,0,0,0.1))');
622
708
  this.handleBarHover(event, d);
623
709
  })
624
710
  .on('mousemove', (event) => {
@@ -633,7 +719,7 @@ class AXBarChartComponent extends NXComponent {
633
719
  return;
634
720
  const barEl = this.d3.select(event.currentTarget).select('path');
635
721
  // Remove hover effect
636
- barEl.transition().duration(150).style('filter', null);
722
+ barEl.transition().duration(this.HOVER_TRANSITION_DURATION).style('filter', null);
637
723
  this._tooltipVisible.set(false);
638
724
  })
639
725
  .on('click', (event, d) => {
@@ -643,10 +729,11 @@ class AXBarChartComponent extends NXComponent {
643
729
  }
644
730
  });
645
731
  // Add animation
732
+ const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
646
733
  bars
647
734
  .transition()
648
735
  .duration(animationDuration)
649
- .delay((d, i) => i * 50) // Stagger each bar animation
736
+ .delay((d, i) => i * animationDelay)
650
737
  .attrTween('d', (d) => {
651
738
  const x = this.xScale(d.id);
652
739
  const width = this.xScale.bandwidth();
@@ -676,7 +763,7 @@ class AXBarChartComponent extends NXComponent {
676
763
  * Renders bars for clustered charts
677
764
  */
678
765
  renderBarsClustered(groups) {
679
- const seriesLabels = groups.length > 0 ? this.getClusterSeriesLabels(groups) : [];
766
+ const seriesLabels = this.getClusterSeriesLabels(groups);
680
767
  const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
681
768
  this._initialAnimationComplete.set(false);
682
769
  const radius = this.effectiveOptions().cornerRadius;
@@ -704,13 +791,12 @@ class AXBarChartComponent extends NXComponent {
704
791
  .attr('d', (d) => {
705
792
  const x = this.xSubScale(d.seriesLabel);
706
793
  const width = this.xSubScale.bandwidth();
707
- const y = this.height - 0.5; // initial height is zero
794
+ const y = this.height - 0.5;
708
795
  return `M${x},${y} L${x},${y} L${x + width},${y} L${x + width},${y} Z`;
709
796
  })
710
797
  .attr('fill', (d) => {
711
798
  const item = d.group.chartData[d.seriesIndex];
712
- const colorIndex = d.seriesIndex;
713
- return item?.color || this.getColor(colorIndex);
799
+ return item?.color || this.getColor(d.seriesIndex);
714
800
  });
715
801
  if (this.effectiveOptions().showDataLabels !== false) {
716
802
  barGroups
@@ -724,17 +810,36 @@ class AXBarChartComponent extends NXComponent {
724
810
  .style('fill', 'rgb(var(--ax-comp-bar-chart-data-labels-color))')
725
811
  .style('opacity', 0)
726
812
  .text((d) => {
727
- const item = d.group.chartData[d.seriesIndex];
728
- return item ? item.value : '';
813
+ const value = d.group.chartData[d.seriesIndex]?.value;
814
+ if (value === undefined)
815
+ return '';
816
+ // Format large numbers in data labels
817
+ return value >= 1000 ? formatLargeNumber(value) : value;
729
818
  });
819
+ const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
730
820
  barGroups
731
821
  .selectAll('.ax-bar-chart-data-label')
732
822
  .transition()
733
823
  .duration(animationDuration)
734
- .delay((_, i) => i * 50 + 100)
824
+ .delay((_d, i) => i * animationDelay + this.LABEL_ANIMATION_DELAY)
735
825
  .attr('y', (d) => {
736
826
  const item = d.group.chartData[d.seriesIndex];
737
- return item ? this.yScale(item.value) - 8 : this.height;
827
+ if (!item)
828
+ return this.height;
829
+ const barTop = this.yScale(item.value);
830
+ // If bar is near the top (100% or close), position label inside the bar
831
+ if (barTop < this.MIN_TOP_SPACE_FOR_LABEL) {
832
+ return barTop + this.LABEL_OFFSET;
833
+ }
834
+ // Otherwise, position above the bar
835
+ return barTop - this.LABEL_OFFSET;
836
+ })
837
+ .attr('data-inside-bar', (d) => {
838
+ const item = d.group.chartData[d.seriesIndex];
839
+ if (!item)
840
+ return 'false';
841
+ const barTop = this.yScale(item.value);
842
+ return barTop < this.MIN_TOP_SPACE_FOR_LABEL ? 'true' : 'false';
738
843
  })
739
844
  .style('opacity', 1)
740
845
  .ease(animationEasing);
@@ -744,7 +849,10 @@ class AXBarChartComponent extends NXComponent {
744
849
  if (!this._initialAnimationComplete())
745
850
  return;
746
851
  const barEl = this.d3.select(event.currentTarget).select('path');
747
- barEl.transition().duration(150).style('filter', 'brightness(0.7) drop-shadow(0 0 2px rgba(0,0,0,0.1))');
852
+ barEl
853
+ .transition()
854
+ .duration(this.HOVER_TRANSITION_DURATION)
855
+ .style('filter', 'brightness(0.7) drop-shadow(0 0 2px rgba(0,0,0,0.1))');
748
856
  const item = d.group.chartData[d.seriesIndex];
749
857
  if (item)
750
858
  this.handleBarHover(event, item, d.group);
@@ -757,7 +865,7 @@ class AXBarChartComponent extends NXComponent {
757
865
  if (!this._initialAnimationComplete())
758
866
  return;
759
867
  const barEl = this.d3.select(event.currentTarget).select('path');
760
- barEl.transition().duration(150).style('filter', null);
868
+ barEl.transition().duration(this.HOVER_TRANSITION_DURATION).style('filter', null);
761
869
  this._tooltipVisible.set(false);
762
870
  })
763
871
  .on('click', (event, d) => {
@@ -767,10 +875,11 @@ class AXBarChartComponent extends NXComponent {
767
875
  if (item)
768
876
  this.handleBarClick(event, item);
769
877
  });
878
+ const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
770
879
  bars
771
880
  .transition()
772
881
  .duration(animationDuration)
773
- .delay((_, i) => i * 50)
882
+ .delay((_d, i) => i * animationDelay)
774
883
  .attrTween('d', (d) => {
775
884
  const x = this.xSubScale(d.seriesLabel);
776
885
  const width = this.xSubScale.bandwidth();
@@ -817,7 +926,7 @@ class AXBarChartComponent extends NXComponent {
817
926
  const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
818
927
  this._tooltipData.set({
819
928
  title: group ? `${datum.tooltipLabel || datum.label} — ${group.label}` : datum.tooltipLabel || datum.label,
820
- value: datum.value.toString(),
929
+ value: formatLargeNumber(datum.value),
821
930
  percentage: `${percentage}%`,
822
931
  color: color,
823
932
  });
@@ -827,6 +936,7 @@ class AXBarChartComponent extends NXComponent {
827
936
  }
828
937
  /**
829
938
  * Updates tooltip position based on mouse coordinates
939
+ * Enhanced to handle overflow issues by positioning tooltips more intelligently
830
940
  */
831
941
  updateTooltipPosition(event) {
832
942
  const containerEl = this.chartContainerEl()?.nativeElement;
@@ -835,9 +945,61 @@ class AXBarChartComponent extends NXComponent {
835
945
  const containerRect = containerEl.getBoundingClientRect();
836
946
  const tooltipEl = containerEl.querySelector('.chart-tooltip');
837
947
  const tooltipRect = tooltipEl ? tooltipEl.getBoundingClientRect() : null;
838
- const pos = computeTooltipPosition(containerRect, tooltipRect, event.clientX, event.clientY, this.TOOLTIP_GAP);
948
+ // Enhanced positioning logic for better tooltip placement
949
+ const pos = this.computeEnhancedTooltipPosition(containerRect, tooltipRect, event.clientX, event.clientY);
839
950
  this._tooltipPosition.set(pos);
840
951
  }
952
+ /**
953
+ * Enhanced tooltip positioning that considers chart boundaries and prevents overflow
954
+ */
955
+ computeEnhancedTooltipPosition(containerRect, tooltipRect, clientX, clientY) {
956
+ const cursorX = clientX - containerRect.left;
957
+ const cursorY = clientY - containerRect.top;
958
+ if (!tooltipRect) {
959
+ // Default positioning when tooltip dimensions are unknown
960
+ return {
961
+ x: Math.min(cursorX + this.TOOLTIP_GAP, containerRect.width - this.TOOLTIP_GAP),
962
+ y: Math.max(this.TOOLTIP_GAP, Math.min(cursorY, containerRect.height - this.TOOLTIP_GAP)),
963
+ };
964
+ }
965
+ // Calculate available space in each direction
966
+ const spaceRight = containerRect.width - cursorX;
967
+ const spaceLeft = cursorX;
968
+ const spaceBelow = containerRect.height - cursorY;
969
+ const spaceAbove = cursorY;
970
+ // Determine best horizontal position
971
+ let x;
972
+ if (spaceRight >= tooltipRect.width + this.TOOLTIP_GAP) {
973
+ // Position to the right of cursor
974
+ x = cursorX + this.TOOLTIP_GAP;
975
+ }
976
+ else if (spaceLeft >= tooltipRect.width + this.TOOLTIP_GAP) {
977
+ // Position to the left of cursor
978
+ x = cursorX - tooltipRect.width - this.TOOLTIP_GAP;
979
+ }
980
+ else {
981
+ // Center horizontally if neither side has enough space
982
+ x = Math.max(this.TOOLTIP_GAP, Math.min((containerRect.width - tooltipRect.width) / 2, containerRect.width - tooltipRect.width - this.TOOLTIP_GAP));
983
+ }
984
+ // Calculate best vertical position
985
+ let y;
986
+ if (spaceBelow >= tooltipRect.height + this.TOOLTIP_GAP) {
987
+ // Position below cursor
988
+ y = cursorY + this.TOOLTIP_GAP;
989
+ }
990
+ else if (spaceAbove >= tooltipRect.height + this.TOOLTIP_GAP) {
991
+ // Position above cursor
992
+ y = cursorY - tooltipRect.height - this.TOOLTIP_GAP;
993
+ }
994
+ else {
995
+ // Center vertically if neither direction has enough space
996
+ y = Math.max(this.TOOLTIP_GAP, Math.min((containerRect.height - tooltipRect.height) / 2, containerRect.height - tooltipRect.height - this.TOOLTIP_GAP));
997
+ }
998
+ // Ensure tooltip stays within container bounds
999
+ x = Math.max(this.TOOLTIP_GAP, Math.min(x, containerRect.width - tooltipRect.width - this.TOOLTIP_GAP));
1000
+ y = Math.max(this.TOOLTIP_GAP, Math.min(y, containerRect.height - tooltipRect.height - this.TOOLTIP_GAP));
1001
+ return { x, y };
1002
+ }
841
1003
  /**
842
1004
  * Handles bar click event
843
1005
  */
@@ -942,7 +1104,9 @@ class AXBarChartComponent extends NXComponent {
942
1104
  resetAll();
943
1105
  return;
944
1106
  }
945
- const targetBar = this.svg.selectAll('.ax-bar-chart-bar').filter((d) => d?.id === id);
1107
+ const targetBar = this.svg
1108
+ .selectAll('.ax-bar-chart-bar')
1109
+ .filter((d) => d?.id === id);
946
1110
  if (targetBar.empty())
947
1111
  return;
948
1112
  const isCurrentlyHighlighted = targetBar.classed('ax-bar-chart-highlighted');
@@ -967,7 +1131,9 @@ class AXBarChartComponent extends NXComponent {
967
1131
  }
968
1132
  return;
969
1133
  }
970
- const targetBars = this.svg.selectAll('.ax-bar-chart-bar').filter((_, i, nodes) => {
1134
+ const targetBars = this.svg
1135
+ .selectAll('.ax-bar-chart-bar')
1136
+ .filter((_d, i, nodes) => {
971
1137
  const node = nodes[i];
972
1138
  return node.getAttribute('data-series') === id;
973
1139
  });
@@ -988,7 +1154,7 @@ class AXBarChartComponent extends NXComponent {
988
1154
  .style('transition', 'all 0.2s ease-in-out');
989
1155
  this.svg
990
1156
  .selectAll('.ax-bar-chart-bar')
991
- .filter((_, i, nodes) => nodes[i].getAttribute('data-series') !== id)
1157
+ .filter((_d, i, nodes) => nodes[i].getAttribute('data-series') !== id)
992
1158
  .classed('ax-bar-chart-dimmed', true)
993
1159
  .attr('opacity', 0.5)
994
1160
  .style('transition', 'opacity 0.2s ease-in-out');
@@ -1059,18 +1225,63 @@ class AXBarChartComponent extends NXComponent {
1059
1225
  clearChartArea(containerElement) {
1060
1226
  this.d3.select(containerElement).selectAll('svg, .ax-chart-message-container').remove();
1061
1227
  }
1228
+ /**
1229
+ * Truncates long labels with ellipsis
1230
+ */
1231
+ truncateLabel(label, maxLength = this.MAX_LABEL_LENGTH) {
1232
+ if (label.length <= maxLength)
1233
+ return label;
1234
+ return label.substring(0, maxLength - 1) + '…';
1235
+ }
1236
+ /**
1237
+ * Gets adaptive font size for X-axis based on width and item count
1238
+ */
1062
1239
  getXAxisTickFontSizeBasedOnWidth() {
1063
- return Math.max(10, Math.min(14, Math.round(this.width / 50)));
1240
+ const itemCount = this.xScale.domain().length;
1241
+ const baseSize = Math.round(this.width / 50);
1242
+ // Reduce font size for many items
1243
+ let adjustedSize = baseSize;
1244
+ if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
1245
+ adjustedSize = Math.round(baseSize * 0.7);
1246
+ }
1247
+ else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
1248
+ adjustedSize = Math.round(baseSize * 0.85);
1249
+ }
1250
+ return Math.max(this.MIN_FONT_SIZE_X_AXIS, Math.min(this.MAX_FONT_SIZE_X_AXIS, adjustedSize));
1064
1251
  }
1252
+ /**
1253
+ * Gets adaptive font size for Y-axis based on height
1254
+ */
1065
1255
  getYAxisTickFontSizeBasedOnHeight() {
1066
- return Math.max(11, Math.min(15, Math.round(this.height / 30)));
1256
+ const baseSize = Math.round(this.height / 30);
1257
+ return Math.max(this.MIN_FONT_SIZE_Y_AXIS, Math.min(this.MAX_FONT_SIZE_Y_AXIS, baseSize));
1067
1258
  }
1068
1259
  buildXAxisCommon(axesGroup, idToLabel, options) {
1260
+ const itemCount = this.xScale.domain().length;
1261
+ // Smart tick reduction for many items
1262
+ let tickValues = this.xScale.domain();
1263
+ if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
1264
+ // Show every 5th tick
1265
+ tickValues = tickValues.filter((_d, i) => i % 5 === 0);
1266
+ }
1267
+ else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
1268
+ // Show every 2nd tick
1269
+ tickValues = tickValues.filter((_d, i) => i % 2 === 0);
1270
+ }
1271
+ const axis = this.d3
1272
+ .axisBottom(this.xScale)
1273
+ .tickValues(tickValues)
1274
+ .tickFormat((id) => {
1275
+ const label = idToLabel.get(id) ?? String(id);
1276
+ // Truncate long labels intelligently
1277
+ const maxLength = itemCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
1278
+ return this.truncateLabel(label, maxLength);
1279
+ });
1069
1280
  this.xAxis = axesGroup
1070
1281
  .append('g')
1071
1282
  .attr('class', 'ax-bar-chart-axis-x')
1072
1283
  .attr('transform', `translate(0,${this.height})`)
1073
- .call(this.d3.axisBottom(this.xScale).tickFormat((id) => idToLabel.get(id) ?? String(id)));
1284
+ .call(axis);
1074
1285
  const dynamicFontSize = this.getXAxisTickFontSizeBasedOnWidth();
1075
1286
  const xAxisTicks = this.xAxis
1076
1287
  .selectAll('text')
@@ -1086,10 +1297,17 @@ class AXBarChartComponent extends NXComponent {
1086
1297
  if (options.xAxisLabel && !isRotated) {
1087
1298
  const tickNodes = (this.xAxis.selectAll('text').nodes() || []);
1088
1299
  const measuredTickHeight = tickNodes.length > 0
1089
- ? Math.max(...tickNodes.map((n) => (n && n.getBBox ? n.getBBox().height : dynamicFontSize)))
1300
+ ? Math.max(...tickNodes.map((n) => {
1301
+ try {
1302
+ return n?.getBBox?.()?.height ?? dynamicFontSize;
1303
+ }
1304
+ catch {
1305
+ return dynamicFontSize;
1306
+ }
1307
+ }))
1090
1308
  : dynamicFontSize;
1091
- const tickAreaHeight = measuredTickHeight + 6;
1092
- const gapBelowTicks = 14;
1309
+ const tickAreaHeight = measuredTickHeight + this.TICK_AREA_PADDING;
1310
+ const gapBelowTicks = this.X_AXIS_TITLE_GAP;
1093
1311
  this.createXAxisTitle(axesGroup, options.xAxisLabel, tickAreaHeight, gapBelowTicks);
1094
1312
  }
1095
1313
  }
@@ -1106,13 +1324,13 @@ class AXBarChartComponent extends NXComponent {
1106
1324
  container.append('div').attr('class', 'ax-chart-message-text ax-bar-chart-no-data-text').text(text);
1107
1325
  container.append('div').attr('class', 'ax-chart-message-help ax-bar-chart-no-data-help').text(helpText);
1108
1326
  }
1109
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXBarChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1110
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.3", type: AXBarChartComponent, isStandalone: true, selector: "ax-bar-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: { barClick: "barClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-bar-chart\" role=\"img\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-bar-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-bar-chart-axis-label-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-bg-color: 0, 0, 0, 0}ax-bar-chart .ax-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;background-color:rgba(var(--ax-comp-bar-chart-bg-color))}ax-bar-chart .ax-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-bar-chart .ax-bar-chart svg g:has(text){font-family:inherit}ax-bar-chart .ax-bar-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-bar-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-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 });
1327
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXBarChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1328
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.9", type: AXBarChartComponent, isStandalone: true, selector: "ax-bar-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: { barClick: "barClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-bar-chart\" role=\"img\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-bar-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-bar-chart-axis-label-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-bg-color: 0, 0, 0, 0}ax-bar-chart .ax-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:rgba(var(--ax-comp-bar-chart-bg-color))}ax-bar-chart .ax-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-bar-chart .ax-bar-chart svg g:has(text){font-family:inherit}ax-bar-chart .ax-bar-chart .ax-bar-chart-data-label{pointer-events:none;-webkit-user-select:none;user-select:none}ax-bar-chart .ax-bar-chart .ax-bar-chart-data-label[data-inside-bar=true]{fill:#fff;font-weight:600;text-shadow:0 0 2px rgba(0,0,0,.3)}ax-bar-chart .ax-bar-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-bar-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-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 });
1111
1329
  }
1112
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXBarChartComponent, decorators: [{
1330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXBarChartComponent, decorators: [{
1113
1331
  type: Component,
1114
- args: [{ selector: 'ax-bar-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-bar-chart\" role=\"img\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-bar-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-bar-chart-axis-label-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-bg-color: 0, 0, 0, 0}ax-bar-chart .ax-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;background-color:rgba(var(--ax-comp-bar-chart-bg-color))}ax-bar-chart .ax-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-bar-chart .ax-bar-chart svg g:has(text){font-family:inherit}ax-bar-chart .ax-bar-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-bar-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-help{font-size:.8rem;opacity:.6}\n"] }]
1115
- }], ctorParameters: () => [] });
1332
+ args: [{ selector: 'ax-bar-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-bar-chart\" role=\"img\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-bar-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-bar-chart-axis-label-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-bar-chart-bg-color: 0, 0, 0, 0}ax-bar-chart .ax-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:rgba(var(--ax-comp-bar-chart-bg-color))}ax-bar-chart .ax-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-bar-chart .ax-bar-chart svg g:has(text){font-family:inherit}ax-bar-chart .ax-bar-chart .ax-bar-chart-data-label{pointer-events:none;-webkit-user-select:none;user-select:none}ax-bar-chart .ax-bar-chart .ax-bar-chart-data-label[data-inside-bar=true]{fill:#fff;font-weight:600;text-shadow:0 0 2px rgba(0,0,0,.3)}ax-bar-chart .ax-bar-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-bar-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-bar-chart .ax-bar-chart-no-data-message .ax-bar-chart-no-data-help{font-size:.8rem;opacity:.6}\n"] }]
1333
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], barClick: [{ type: i0.Output, args: ["barClick"] }], chartContainerEl: [{ type: i0.ViewChild, args: ['chartContainer', { isSignal: true }] }] } });
1116
1334
 
1117
1335
  /**
1118
1336
  * Generated bundle index. Do not edit.