@acorex/charts 21.0.0-next.14 → 21.0.0-next.16
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.
- package/bar-chart/index.d.ts +58 -2
- package/donut-chart/index.d.ts +42 -3
- package/fesm2022/acorex-charts-bar-chart.mjs +261 -122
- package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-legend.mjs +4 -4
- package/fesm2022/acorex-charts-chart-legend.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-tooltip.mjs +3 -3
- package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-charts-donut-chart.mjs +292 -189
- package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-gauge-chart.mjs +83 -59
- package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-hierarchy-chart.mjs +3 -3
- package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-line-chart.mjs +289 -137
- package/fesm2022/acorex-charts-line-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts.mjs +26 -4
- package/fesm2022/acorex-charts.mjs.map +1 -1
- package/gauge-chart/index.d.ts +4 -1
- package/index.d.ts +5 -1
- package/line-chart/index.d.ts +66 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AXChartComponent, AX_CHART_COLOR_PALETTE, getEasingFunction, getChartColor } from '@acorex/charts';
|
|
1
|
+
import { AXChartComponent, AX_CHART_COLOR_PALETTE, formatLargeNumber, getEasingFunction, getChartColor } from '@acorex/charts';
|
|
2
2
|
import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { InjectionToken, input, output, viewChild, signal, inject, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
@@ -52,7 +52,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
52
52
|
chartContainerEl = viewChild.required('chartContainer');
|
|
53
53
|
// D3 reference - loaded asynchronously
|
|
54
54
|
d3;
|
|
55
|
-
// Chart elements
|
|
55
|
+
// Chart elements - using proper D3 types
|
|
56
56
|
svg;
|
|
57
57
|
chart;
|
|
58
58
|
xScale;
|
|
@@ -64,10 +64,42 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
64
64
|
width;
|
|
65
65
|
height;
|
|
66
66
|
margin = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
67
|
-
//
|
|
67
|
+
// Constants for layout calculations
|
|
68
68
|
CHAR_WIDTH_RATIO = 0.65;
|
|
69
|
-
// Tooltip gap in pixels for consistent spacing
|
|
70
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;
|
|
71
103
|
// Animation state
|
|
72
104
|
_initialAnimationComplete = signal(false, ...(ngDevMode ? [{ debugName: "_initialAnimationComplete" }] : []));
|
|
73
105
|
// Tooltip state
|
|
@@ -114,17 +146,39 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
114
146
|
...this.effectiveOptions().messages,
|
|
115
147
|
};
|
|
116
148
|
}, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : []));
|
|
117
|
-
// Track hidden bars
|
|
149
|
+
// Track hidden bars and series
|
|
118
150
|
hiddenBars = new Set();
|
|
119
|
-
// Track hidden series for clustered charts (by series label)
|
|
120
151
|
hiddenSeries = new Set();
|
|
121
|
-
//
|
|
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
|
|
122
177
|
isClusteredData(value) {
|
|
123
|
-
return Array.isArray(value) && value.length > 0 &&
|
|
178
|
+
return Array.isArray(value) && value.length > 0 && 'chartData' in value[0];
|
|
124
179
|
}
|
|
125
180
|
getClusterSeriesLabels(groups) {
|
|
126
|
-
|
|
127
|
-
return first.map((d) => d.label);
|
|
181
|
+
return groups[0]?.chartData?.map((d) => d.label) ?? [];
|
|
128
182
|
}
|
|
129
183
|
constructor() {
|
|
130
184
|
super();
|
|
@@ -177,25 +231,14 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
177
231
|
const containerElement = this.chartContainerEl().nativeElement;
|
|
178
232
|
const inputValue = (this.data() || []);
|
|
179
233
|
const isClustered = this.isClusteredData(inputValue);
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (isClustered) {
|
|
185
|
-
groupedData = inputValue;
|
|
186
|
-
const seriesLabels = groupedData.length > 0 ? this.getClusterSeriesLabels(groupedData) : [];
|
|
187
|
-
const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
|
|
188
|
-
hasAnyVisible = groupedData.some((g) => visibleSeries.some((_, idx) => !!g.chartData[idx]));
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
const single = inputValue || [];
|
|
192
|
-
visibleSingleData = single.filter((d) => !this.isBarHidden(d.id));
|
|
193
|
-
hasAnyVisible = visibleSingleData.length > 0;
|
|
194
|
-
}
|
|
234
|
+
// Use computed visibility
|
|
235
|
+
const hasAnyVisible = this.hasVisibleData();
|
|
236
|
+
const visibleSingleData = isClustered ? [] : this.visibleData();
|
|
237
|
+
const groupedData = isClustered ? inputValue : [];
|
|
195
238
|
// Clear existing chart SVG and messages (do not remove tooltip component)
|
|
196
239
|
this.d3.select(containerElement).selectAll('svg, .ax-chart-message-container').remove();
|
|
197
240
|
// Early return if no data
|
|
198
|
-
if (!inputValue.length) {
|
|
241
|
+
if (!Array.isArray(inputValue) || inputValue.length === 0) {
|
|
199
242
|
this.showNoDataMessage(containerElement);
|
|
200
243
|
return;
|
|
201
244
|
}
|
|
@@ -234,8 +277,8 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
234
277
|
// Only remove chart SVG and message containers to preserve tooltip component
|
|
235
278
|
this.d3?.select(container).selectAll('svg, .ax-chart-message-container').remove();
|
|
236
279
|
}
|
|
237
|
-
this.svg =
|
|
238
|
-
this.chart =
|
|
280
|
+
this.svg = undefined;
|
|
281
|
+
this.chart = undefined;
|
|
239
282
|
this._tooltipVisible.set(false);
|
|
240
283
|
}
|
|
241
284
|
/**
|
|
@@ -251,11 +294,10 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
251
294
|
const showXAxis = options.showXAxis !== false;
|
|
252
295
|
const showYAxis = options.showYAxis !== false;
|
|
253
296
|
// Internal left padding for Y-axis ticks/labels/title
|
|
254
|
-
const
|
|
255
|
-
const
|
|
256
|
-
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;
|
|
257
299
|
const leftPaddingForYAxis = showYAxis
|
|
258
|
-
? this.calculateMaxYAxisTickLabelWidth() +
|
|
300
|
+
? this.calculateMaxYAxisTickLabelWidth() + this.AXIS_TICK_PADDING + yAxisTitlePadding + yAxisTitleThickness
|
|
259
301
|
: 0;
|
|
260
302
|
// Estimate internal bottom padding for X-axis ticks/labels/title
|
|
261
303
|
let bottomPaddingForXAxis = 0;
|
|
@@ -274,41 +316,29 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
274
316
|
const baseWidth = options.width ?? containerWidth;
|
|
275
317
|
const workingWidth = Math.max(1, baseWidth - leftPaddingForYAxis);
|
|
276
318
|
const dynamicFontSize = Math.max(10, Math.min(14, Math.round(workingWidth / 50)));
|
|
277
|
-
|
|
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;
|
|
278
323
|
const availableWidthPerBar = barCount > 0 ? workingWidth / barCount : workingWidth;
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
: options.rotateXAxisLabels === false
|
|
282
|
-
? false
|
|
283
|
-
: estimatedLongestLabelWidth > availableWidthPerBar;
|
|
324
|
+
const rotateOption = typeof options.rotateXAxisLabels === 'boolean' ? options.rotateXAxisLabels : undefined;
|
|
325
|
+
const willRotate = this.shouldLabelsRotate(rotateOption, estimatedLongestLabelWidth, availableWidthPerBar);
|
|
284
326
|
const tickAreaHeight = willRotate
|
|
285
|
-
? estimatedLongestLabelWidth * Math.SQRT1_2 + dynamicFontSize * Math.SQRT1_2 +
|
|
286
|
-
: dynamicFontSize +
|
|
287
|
-
const gapBelowTicks = options.xAxisLabel && !willRotate ?
|
|
288
|
-
const titleBlockHeight = options.xAxisLabel && !willRotate ?
|
|
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;
|
|
289
331
|
bottomPaddingForXAxis = tickAreaHeight + gapBelowTicks + titleBlockHeight;
|
|
290
332
|
}
|
|
291
333
|
// Determine plotting dimensions with sensible fallbacks
|
|
292
|
-
const fallbackPlotHeight = 240; // ensures visible plot when container has no height
|
|
293
334
|
const baseWidth = options.width ?? containerWidth;
|
|
294
|
-
const totalWidth = Math.max(
|
|
335
|
+
const totalWidth = Math.max(this.MIN_TOTAL_WIDTH, baseWidth > 0 ? baseWidth : this.MIN_TOTAL_WIDTH);
|
|
295
336
|
const plotWidth = Math.max(0, totalWidth - leftPaddingForYAxis);
|
|
296
|
-
|
|
297
|
-
if (options.height && options.height > 0) {
|
|
298
|
-
plotHeight = Math.max(120, options.height - bottomPaddingForXAxis);
|
|
299
|
-
}
|
|
300
|
-
else if (containerHeight > 0) {
|
|
301
|
-
plotHeight = Math.max(120, containerHeight - bottomPaddingForXAxis, fallbackPlotHeight);
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
plotHeight = fallbackPlotHeight;
|
|
305
|
-
}
|
|
337
|
+
const plotHeight = this.calculatePlotHeight(options.height, containerHeight, bottomPaddingForXAxis);
|
|
306
338
|
this.width = plotWidth;
|
|
307
339
|
this.height = plotHeight;
|
|
308
340
|
// Create SVG with explicit pixel dimensions to avoid collapse
|
|
309
|
-
|
|
310
|
-
const topPaddingForTopEdge = 6;
|
|
311
|
-
const totalHeight = this.height + bottomPaddingForXAxis + topPaddingForTopEdge;
|
|
341
|
+
const totalHeight = this.height + bottomPaddingForXAxis + this.TOP_EDGE_PADDING;
|
|
312
342
|
const svg = this.d3
|
|
313
343
|
.select(containerElement)
|
|
314
344
|
.append('svg')
|
|
@@ -321,17 +351,38 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
321
351
|
this.chart = this.svg
|
|
322
352
|
.append('g')
|
|
323
353
|
.attr('class', 'chart-content')
|
|
324
|
-
.attr('transform', `translate(${leftPaddingForYAxis},${
|
|
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;
|
|
325
377
|
}
|
|
326
|
-
// calculateMargins was unused; removed to simplify and reduce maintenance surface
|
|
327
378
|
/**
|
|
328
379
|
* Creates x and y scales for the chart
|
|
329
380
|
*/
|
|
330
381
|
setupScales(data) {
|
|
331
|
-
// Get the bar width percentage (default
|
|
332
|
-
const barWidthPercent = this.effectiveOptions().barWidth ??
|
|
382
|
+
// Get the bar width percentage (default 60%)
|
|
383
|
+
const barWidthPercent = this.effectiveOptions().barWidth ?? 0.6;
|
|
333
384
|
// Calculate padding based on barWidth (inverse relationship)
|
|
334
|
-
const padding = Math.max(
|
|
385
|
+
const padding = Math.max(this.MIN_PADDING, 1 - barWidthPercent);
|
|
335
386
|
// Create x scale (band scale for categorical data)
|
|
336
387
|
this.xScale = this.d3
|
|
337
388
|
.scaleBand()
|
|
@@ -339,9 +390,12 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
339
390
|
.range([0, this.width])
|
|
340
391
|
.padding(padding);
|
|
341
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;
|
|
342
396
|
this.yScale = this.d3
|
|
343
397
|
.scaleLinear()
|
|
344
|
-
.domain([0,
|
|
398
|
+
.domain([Math.min(0, minValue), Math.max(0, maxValue)])
|
|
345
399
|
.nice()
|
|
346
400
|
.range([this.height, 0]);
|
|
347
401
|
}
|
|
@@ -349,18 +403,21 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
349
403
|
* Creates x and y scales for clustered charts
|
|
350
404
|
*/
|
|
351
405
|
setupScalesClustered(groups) {
|
|
352
|
-
const seriesLabels =
|
|
406
|
+
const seriesLabels = this.getClusterSeriesLabels(groups);
|
|
353
407
|
const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
|
|
354
408
|
// Outer band for clusters
|
|
355
|
-
const outerPadding = 0.2;
|
|
356
409
|
this.xScale = this.d3
|
|
357
410
|
.scaleBand()
|
|
358
411
|
.domain(groups.map((g) => g.id))
|
|
359
412
|
.range([0, this.width])
|
|
360
|
-
.padding(
|
|
413
|
+
.padding(this.OUTER_PADDING_CLUSTERED);
|
|
361
414
|
// Inner band for series
|
|
362
|
-
this.xSubScale = this.d3
|
|
363
|
-
|
|
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)
|
|
364
421
|
const allVisibleValues = [];
|
|
365
422
|
for (const g of groups) {
|
|
366
423
|
visibleSeries.forEach((_, idx) => {
|
|
@@ -369,8 +426,13 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
369
426
|
allVisibleValues.push(item.value);
|
|
370
427
|
});
|
|
371
428
|
}
|
|
429
|
+
const yMin = this.d3.min(allVisibleValues) || 0;
|
|
372
430
|
const yMax = this.d3.max(allVisibleValues) || 0;
|
|
373
|
-
this.yScale = this.d3
|
|
431
|
+
this.yScale = this.d3
|
|
432
|
+
.scaleLinear()
|
|
433
|
+
.domain([Math.min(0, yMin), Math.max(0, yMax)])
|
|
434
|
+
.nice()
|
|
435
|
+
.range([this.height, 0]);
|
|
374
436
|
}
|
|
375
437
|
/**
|
|
376
438
|
* Creates x and y axes with grid lines
|
|
@@ -396,8 +458,20 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
396
458
|
this.buildXAxisCommon(axesGroup, idToLabel, options);
|
|
397
459
|
}
|
|
398
460
|
createYAxis(axesGroup, options) {
|
|
399
|
-
// Create Y axis
|
|
400
|
-
|
|
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);
|
|
401
475
|
// Style the axis text
|
|
402
476
|
const dynamicFontSize = this.getYAxisTickFontSizeBasedOnHeight();
|
|
403
477
|
this.yAxis
|
|
@@ -428,14 +502,14 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
428
502
|
const labels = Array.from(idToLabel.values());
|
|
429
503
|
const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
430
504
|
const estimatedWidth = longestLabel.length * fontSize * this.CHAR_WIDTH_RATIO;
|
|
431
|
-
// Heuristic: avoid rotation when there are few categories
|
|
505
|
+
// Heuristic: avoid rotation when there are few categories even if estimation is slightly larger
|
|
432
506
|
const domainCount = this.xScale.domain().length;
|
|
433
|
-
if (domainCount <=
|
|
434
|
-
return estimatedWidth > step *
|
|
507
|
+
if (domainCount <= this.SMALL_DATASET_THRESHOLD) {
|
|
508
|
+
return estimatedWidth > step * this.ROTATION_TOLERANCE_SMALL_DATASET;
|
|
435
509
|
}
|
|
436
510
|
return estimatedWidth > step;
|
|
437
511
|
}
|
|
438
|
-
createXAxisTitle(axesGroup, title, tickAreaHeight, gapBelowTicks =
|
|
512
|
+
createXAxisTitle(axesGroup, title, tickAreaHeight, gapBelowTicks = this.TICK_AREA_GAP) {
|
|
439
513
|
// Title positioned directly below tick area with a consistent gap
|
|
440
514
|
const labelY = this.height + tickAreaHeight + gapBelowTicks;
|
|
441
515
|
axesGroup
|
|
@@ -446,7 +520,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
446
520
|
.attr('x', this.width / 2)
|
|
447
521
|
.attr('y', labelY)
|
|
448
522
|
.attr('direction', 'ltr')
|
|
449
|
-
.style('font-size',
|
|
523
|
+
.style('font-size', `${this.X_AXIS_TITLE_FONT_SIZE}px`)
|
|
450
524
|
.style('font-weight', '500')
|
|
451
525
|
.style('fill', 'rgb(var(--ax-comp-bar-chart-axis-label-color))')
|
|
452
526
|
.text(title);
|
|
@@ -458,7 +532,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
458
532
|
// Chart group is translated by (margin.left, margin.top)
|
|
459
533
|
// So Y-axis is at X = margin.left in SVG coordinates
|
|
460
534
|
const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
|
|
461
|
-
const padding =
|
|
535
|
+
const padding = this.Y_AXIS_PADDING;
|
|
462
536
|
// Position title to the left of the tick labels
|
|
463
537
|
// Since text-anchor: end aligns the text to the Y-axis, we need to go further left
|
|
464
538
|
const labelY = -maxTickLabelWidth - padding;
|
|
@@ -499,9 +573,9 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
499
573
|
const visibleData = raw.filter((d) => !this.isBarHidden(d.id));
|
|
500
574
|
maxValue = visibleData.reduce((acc, d) => (d.value > acc ? d.value : acc), 0);
|
|
501
575
|
}
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
return tickLabelText.length *
|
|
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;
|
|
505
579
|
}
|
|
506
580
|
createGridLines() {
|
|
507
581
|
this.chart
|
|
@@ -592,28 +666,29 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
592
666
|
.style('font-weight', '500')
|
|
593
667
|
.style('fill', 'rgb(var(--ax-comp-bar-chart-data-labels-color))')
|
|
594
668
|
.style('opacity', 0) // Start invisible for animation
|
|
595
|
-
.text((d) =>
|
|
669
|
+
.text((d) => {
|
|
670
|
+
// Format large numbers in data labels
|
|
671
|
+
return d.value >= 1000 ? formatLargeNumber(d.value) : d.value;
|
|
672
|
+
});
|
|
596
673
|
// Animate data labels with smart positioning to avoid clipping
|
|
674
|
+
const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
|
|
597
675
|
barGroups
|
|
598
676
|
.selectAll('.ax-bar-chart-data-label')
|
|
599
677
|
.transition()
|
|
600
678
|
.duration(animationDuration)
|
|
601
|
-
.delay((d, i) => i *
|
|
679
|
+
.delay((d, i) => i * animationDelay + this.LABEL_ANIMATION_DELAY)
|
|
602
680
|
.attr('y', (d) => {
|
|
603
681
|
const barTop = this.yScale(d.value);
|
|
604
|
-
const labelOffset = 8;
|
|
605
|
-
const minTopSpace = 20; // Minimum space from top of chart
|
|
606
682
|
// If bar is near the top (100% or close), position label inside the bar
|
|
607
|
-
if (barTop <
|
|
608
|
-
return barTop +
|
|
683
|
+
if (barTop < this.MIN_TOP_SPACE_FOR_LABEL) {
|
|
684
|
+
return barTop + this.LABEL_OFFSET;
|
|
609
685
|
}
|
|
610
686
|
// Otherwise, position above the bar
|
|
611
|
-
return barTop -
|
|
687
|
+
return barTop - this.LABEL_OFFSET;
|
|
612
688
|
})
|
|
613
689
|
.attr('data-inside-bar', (d) => {
|
|
614
690
|
const barTop = this.yScale(d.value);
|
|
615
|
-
|
|
616
|
-
return barTop < minTopSpace ? 'true' : 'false';
|
|
691
|
+
return barTop < this.MIN_TOP_SPACE_FOR_LABEL ? 'true' : 'false';
|
|
617
692
|
})
|
|
618
693
|
.style('opacity', 1)
|
|
619
694
|
.ease(animationEasing);
|
|
@@ -626,7 +701,10 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
626
701
|
return;
|
|
627
702
|
const barEl = this.d3.select(event.currentTarget).select('path');
|
|
628
703
|
// Standard hover effect - darken the bar slightly and add a subtle shadow
|
|
629
|
-
barEl
|
|
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))');
|
|
630
708
|
this.handleBarHover(event, d);
|
|
631
709
|
})
|
|
632
710
|
.on('mousemove', (event) => {
|
|
@@ -641,7 +719,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
641
719
|
return;
|
|
642
720
|
const barEl = this.d3.select(event.currentTarget).select('path');
|
|
643
721
|
// Remove hover effect
|
|
644
|
-
barEl.transition().duration(
|
|
722
|
+
barEl.transition().duration(this.HOVER_TRANSITION_DURATION).style('filter', null);
|
|
645
723
|
this._tooltipVisible.set(false);
|
|
646
724
|
})
|
|
647
725
|
.on('click', (event, d) => {
|
|
@@ -651,10 +729,11 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
651
729
|
}
|
|
652
730
|
});
|
|
653
731
|
// Add animation
|
|
732
|
+
const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
|
|
654
733
|
bars
|
|
655
734
|
.transition()
|
|
656
735
|
.duration(animationDuration)
|
|
657
|
-
.delay((d, i) => i *
|
|
736
|
+
.delay((d, i) => i * animationDelay)
|
|
658
737
|
.attrTween('d', (d) => {
|
|
659
738
|
const x = this.xScale(d.id);
|
|
660
739
|
const width = this.xScale.bandwidth();
|
|
@@ -684,7 +763,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
684
763
|
* Renders bars for clustered charts
|
|
685
764
|
*/
|
|
686
765
|
renderBarsClustered(groups) {
|
|
687
|
-
const seriesLabels =
|
|
766
|
+
const seriesLabels = this.getClusterSeriesLabels(groups);
|
|
688
767
|
const visibleSeries = seriesLabels.filter((s) => !this.hiddenSeries.has(s));
|
|
689
768
|
this._initialAnimationComplete.set(false);
|
|
690
769
|
const radius = this.effectiveOptions().cornerRadius;
|
|
@@ -712,13 +791,12 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
712
791
|
.attr('d', (d) => {
|
|
713
792
|
const x = this.xSubScale(d.seriesLabel);
|
|
714
793
|
const width = this.xSubScale.bandwidth();
|
|
715
|
-
const y = this.height - 0.5;
|
|
794
|
+
const y = this.height - 0.5;
|
|
716
795
|
return `M${x},${y} L${x},${y} L${x + width},${y} L${x + width},${y} Z`;
|
|
717
796
|
})
|
|
718
797
|
.attr('fill', (d) => {
|
|
719
798
|
const item = d.group.chartData[d.seriesIndex];
|
|
720
|
-
|
|
721
|
-
return item?.color || this.getColor(colorIndex);
|
|
799
|
+
return item?.color || this.getColor(d.seriesIndex);
|
|
722
800
|
});
|
|
723
801
|
if (this.effectiveOptions().showDataLabels !== false) {
|
|
724
802
|
barGroups
|
|
@@ -732,35 +810,36 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
732
810
|
.style('fill', 'rgb(var(--ax-comp-bar-chart-data-labels-color))')
|
|
733
811
|
.style('opacity', 0)
|
|
734
812
|
.text((d) => {
|
|
735
|
-
const
|
|
736
|
-
|
|
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;
|
|
737
818
|
});
|
|
819
|
+
const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
|
|
738
820
|
barGroups
|
|
739
821
|
.selectAll('.ax-bar-chart-data-label')
|
|
740
822
|
.transition()
|
|
741
823
|
.duration(animationDuration)
|
|
742
|
-
.delay((
|
|
824
|
+
.delay((_d, i) => i * animationDelay + this.LABEL_ANIMATION_DELAY)
|
|
743
825
|
.attr('y', (d) => {
|
|
744
826
|
const item = d.group.chartData[d.seriesIndex];
|
|
745
827
|
if (!item)
|
|
746
828
|
return this.height;
|
|
747
829
|
const barTop = this.yScale(item.value);
|
|
748
|
-
const labelOffset = 8;
|
|
749
|
-
const minTopSpace = 20; // Minimum space from top of chart
|
|
750
830
|
// If bar is near the top (100% or close), position label inside the bar
|
|
751
|
-
if (barTop <
|
|
752
|
-
return barTop +
|
|
831
|
+
if (barTop < this.MIN_TOP_SPACE_FOR_LABEL) {
|
|
832
|
+
return barTop + this.LABEL_OFFSET;
|
|
753
833
|
}
|
|
754
834
|
// Otherwise, position above the bar
|
|
755
|
-
return barTop -
|
|
835
|
+
return barTop - this.LABEL_OFFSET;
|
|
756
836
|
})
|
|
757
837
|
.attr('data-inside-bar', (d) => {
|
|
758
838
|
const item = d.group.chartData[d.seriesIndex];
|
|
759
839
|
if (!item)
|
|
760
840
|
return 'false';
|
|
761
841
|
const barTop = this.yScale(item.value);
|
|
762
|
-
|
|
763
|
-
return barTop < minTopSpace ? 'true' : 'false';
|
|
842
|
+
return barTop < this.MIN_TOP_SPACE_FOR_LABEL ? 'true' : 'false';
|
|
764
843
|
})
|
|
765
844
|
.style('opacity', 1)
|
|
766
845
|
.ease(animationEasing);
|
|
@@ -770,7 +849,10 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
770
849
|
if (!this._initialAnimationComplete())
|
|
771
850
|
return;
|
|
772
851
|
const barEl = this.d3.select(event.currentTarget).select('path');
|
|
773
|
-
barEl
|
|
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))');
|
|
774
856
|
const item = d.group.chartData[d.seriesIndex];
|
|
775
857
|
if (item)
|
|
776
858
|
this.handleBarHover(event, item, d.group);
|
|
@@ -783,7 +865,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
783
865
|
if (!this._initialAnimationComplete())
|
|
784
866
|
return;
|
|
785
867
|
const barEl = this.d3.select(event.currentTarget).select('path');
|
|
786
|
-
barEl.transition().duration(
|
|
868
|
+
barEl.transition().duration(this.HOVER_TRANSITION_DURATION).style('filter', null);
|
|
787
869
|
this._tooltipVisible.set(false);
|
|
788
870
|
})
|
|
789
871
|
.on('click', (event, d) => {
|
|
@@ -793,10 +875,11 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
793
875
|
if (item)
|
|
794
876
|
this.handleBarClick(event, item);
|
|
795
877
|
});
|
|
878
|
+
const animationDelay = this.effectiveOptions().animationDelay ?? this.DEFAULT_ANIMATION_DELAY;
|
|
796
879
|
bars
|
|
797
880
|
.transition()
|
|
798
881
|
.duration(animationDuration)
|
|
799
|
-
.delay((
|
|
882
|
+
.delay((_d, i) => i * animationDelay)
|
|
800
883
|
.attrTween('d', (d) => {
|
|
801
884
|
const x = this.xSubScale(d.seriesLabel);
|
|
802
885
|
const width = this.xSubScale.bandwidth();
|
|
@@ -843,7 +926,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
843
926
|
const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
|
|
844
927
|
this._tooltipData.set({
|
|
845
928
|
title: group ? `${datum.tooltipLabel || datum.label} — ${group.label}` : datum.tooltipLabel || datum.label,
|
|
846
|
-
value: datum.value
|
|
929
|
+
value: formatLargeNumber(datum.value),
|
|
847
930
|
percentage: `${percentage}%`,
|
|
848
931
|
color: color,
|
|
849
932
|
});
|
|
@@ -1021,7 +1104,9 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1021
1104
|
resetAll();
|
|
1022
1105
|
return;
|
|
1023
1106
|
}
|
|
1024
|
-
const targetBar = this.svg
|
|
1107
|
+
const targetBar = this.svg
|
|
1108
|
+
.selectAll('.ax-bar-chart-bar')
|
|
1109
|
+
.filter((d) => d?.id === id);
|
|
1025
1110
|
if (targetBar.empty())
|
|
1026
1111
|
return;
|
|
1027
1112
|
const isCurrentlyHighlighted = targetBar.classed('ax-bar-chart-highlighted');
|
|
@@ -1046,7 +1131,9 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1046
1131
|
}
|
|
1047
1132
|
return;
|
|
1048
1133
|
}
|
|
1049
|
-
const targetBars = this.svg
|
|
1134
|
+
const targetBars = this.svg
|
|
1135
|
+
.selectAll('.ax-bar-chart-bar')
|
|
1136
|
+
.filter((_d, i, nodes) => {
|
|
1050
1137
|
const node = nodes[i];
|
|
1051
1138
|
return node.getAttribute('data-series') === id;
|
|
1052
1139
|
});
|
|
@@ -1067,7 +1154,7 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1067
1154
|
.style('transition', 'all 0.2s ease-in-out');
|
|
1068
1155
|
this.svg
|
|
1069
1156
|
.selectAll('.ax-bar-chart-bar')
|
|
1070
|
-
.filter((
|
|
1157
|
+
.filter((_d, i, nodes) => nodes[i].getAttribute('data-series') !== id)
|
|
1071
1158
|
.classed('ax-bar-chart-dimmed', true)
|
|
1072
1159
|
.attr('opacity', 0.5)
|
|
1073
1160
|
.style('transition', 'opacity 0.2s ease-in-out');
|
|
@@ -1138,18 +1225,63 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1138
1225
|
clearChartArea(containerElement) {
|
|
1139
1226
|
this.d3.select(containerElement).selectAll('svg, .ax-chart-message-container').remove();
|
|
1140
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
|
+
*/
|
|
1141
1239
|
getXAxisTickFontSizeBasedOnWidth() {
|
|
1142
|
-
|
|
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));
|
|
1143
1251
|
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Gets adaptive font size for Y-axis based on height
|
|
1254
|
+
*/
|
|
1144
1255
|
getYAxisTickFontSizeBasedOnHeight() {
|
|
1145
|
-
|
|
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));
|
|
1146
1258
|
}
|
|
1147
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
|
+
});
|
|
1148
1280
|
this.xAxis = axesGroup
|
|
1149
1281
|
.append('g')
|
|
1150
1282
|
.attr('class', 'ax-bar-chart-axis-x')
|
|
1151
1283
|
.attr('transform', `translate(0,${this.height})`)
|
|
1152
|
-
.call(
|
|
1284
|
+
.call(axis);
|
|
1153
1285
|
const dynamicFontSize = this.getXAxisTickFontSizeBasedOnWidth();
|
|
1154
1286
|
const xAxisTicks = this.xAxis
|
|
1155
1287
|
.selectAll('text')
|
|
@@ -1165,10 +1297,17 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1165
1297
|
if (options.xAxisLabel && !isRotated) {
|
|
1166
1298
|
const tickNodes = (this.xAxis.selectAll('text').nodes() || []);
|
|
1167
1299
|
const measuredTickHeight = tickNodes.length > 0
|
|
1168
|
-
? Math.max(...tickNodes.map((n) =>
|
|
1300
|
+
? Math.max(...tickNodes.map((n) => {
|
|
1301
|
+
try {
|
|
1302
|
+
return n?.getBBox?.()?.height ?? dynamicFontSize;
|
|
1303
|
+
}
|
|
1304
|
+
catch {
|
|
1305
|
+
return dynamicFontSize;
|
|
1306
|
+
}
|
|
1307
|
+
}))
|
|
1169
1308
|
: dynamicFontSize;
|
|
1170
|
-
const tickAreaHeight = measuredTickHeight +
|
|
1171
|
-
const gapBelowTicks =
|
|
1309
|
+
const tickAreaHeight = measuredTickHeight + this.TICK_AREA_PADDING;
|
|
1310
|
+
const gapBelowTicks = this.X_AXIS_TITLE_GAP;
|
|
1172
1311
|
this.createXAxisTitle(axesGroup, options.xAxisLabel, tickAreaHeight, gapBelowTicks);
|
|
1173
1312
|
}
|
|
1174
1313
|
}
|
|
@@ -1185,10 +1324,10 @@ class AXBarChartComponent extends AXChartComponent {
|
|
|
1185
1324
|
container.append('div').attr('class', 'ax-chart-message-text ax-bar-chart-no-data-text').text(text);
|
|
1186
1325
|
container.append('div').attr('class', 'ax-chart-message-help ax-bar-chart-no-data-help').text(helpText);
|
|
1187
1326
|
}
|
|
1188
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1189
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.
|
|
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 });
|
|
1190
1329
|
}
|
|
1191
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1330
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXBarChartComponent, decorators: [{
|
|
1192
1331
|
type: Component,
|
|
1193
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"] }]
|
|
1194
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 }] }] } });
|