@acorex/charts 19.15.0-next.22 → 19.15.0-next.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bar-chart/lib/bar-chart.component.d.ts +32 -1
- package/chart-legend/index.d.ts +2 -0
- package/chart-legend/lib/chart-legend.component.d.ts +60 -0
- package/chart-legend/lib/chart-legend.type.d.ts +40 -0
- package/donut-chart/lib/donut-chart.component.d.ts +9 -5
- package/fesm2022/acorex-charts-bar-chart.mjs +229 -38
- package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-legend.mjs +109 -0
- package/fesm2022/acorex-charts-chart-legend.mjs.map +1 -0
- package/fesm2022/acorex-charts-chart-tooltip.mjs +4 -4
- package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-charts-donut-chart.mjs +179 -69
- package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-gauge-chart.mjs +3 -3
- package/fesm2022/acorex-charts-hierarchy-chart.mjs +3 -3
- package/fesm2022/acorex-charts-line-chart.mjs +220 -104
- package/fesm2022/acorex-charts-line-chart.mjs.map +1 -1
- package/line-chart/lib/line-chart.component.d.ts +9 -4
- package/package.json +7 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NXComponent } from '@acorex/cdk/common';
|
|
2
|
+
import { AXChartLegendCompatible, AXChartLegendItem } from '@acorex/charts/chart-legend';
|
|
2
3
|
import { AXChartTooltipData } from '@acorex/charts/chart-tooltip';
|
|
3
4
|
import { OnDestroy } from '@angular/core';
|
|
4
5
|
import { AXBarChartClickEvent, AXBarChartData, AXBarChartOption } from './bar-chart.type';
|
|
@@ -7,7 +8,7 @@ import * as i0 from "@angular/core";
|
|
|
7
8
|
* Bar Chart Component
|
|
8
9
|
* Renders data as vertical bars with interactive hover effects and animations
|
|
9
10
|
*/
|
|
10
|
-
export declare class AXBarChartComponent extends NXComponent implements OnDestroy {
|
|
11
|
+
export declare class AXBarChartComponent extends NXComponent implements OnDestroy, AXChartLegendCompatible {
|
|
11
12
|
/** Chart data input */
|
|
12
13
|
data: import("@angular/core").InputSignal<AXBarChartData[]>;
|
|
13
14
|
/** Chart options input */
|
|
@@ -39,6 +40,7 @@ export declare class AXBarChartComponent extends NXComponent implements OnDestro
|
|
|
39
40
|
protected tooltipData: import("@angular/core").Signal<AXChartTooltipData>;
|
|
40
41
|
private configToken;
|
|
41
42
|
private chartColors;
|
|
43
|
+
private platform;
|
|
42
44
|
protected effectiveOptions: import("@angular/core").Signal<{
|
|
43
45
|
width?: number;
|
|
44
46
|
height?: number;
|
|
@@ -54,6 +56,7 @@ export declare class AXBarChartComponent extends NXComponent implements OnDestro
|
|
|
54
56
|
animationDuration?: number;
|
|
55
57
|
animationEasing?: import("@acorex/cdk/common").AXAnimationEasing;
|
|
56
58
|
}>;
|
|
59
|
+
private hiddenBars;
|
|
57
60
|
constructor();
|
|
58
61
|
ngOnDestroy(): void;
|
|
59
62
|
/**
|
|
@@ -112,6 +115,34 @@ export declare class AXBarChartComponent extends NXComponent implements OnDestro
|
|
|
112
115
|
* Shows a message when no data is available
|
|
113
116
|
*/
|
|
114
117
|
private showNoDataMessage;
|
|
118
|
+
/**
|
|
119
|
+
* Shows a message when all bars are hidden
|
|
120
|
+
*/
|
|
121
|
+
private showAllBarsHiddenMessage;
|
|
122
|
+
/**
|
|
123
|
+
* Gets the color for a bar based on its index
|
|
124
|
+
*/
|
|
125
|
+
protected getColor(index: number): string;
|
|
126
|
+
/**
|
|
127
|
+
* Checks if a bar is hidden
|
|
128
|
+
*/
|
|
129
|
+
protected isBarHidden(id: string): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Implementation of AXChartLegendCompatible interface
|
|
132
|
+
* Returns legend items based on the chart data
|
|
133
|
+
*/
|
|
134
|
+
getLegendItems(): AXChartLegendItem[];
|
|
135
|
+
/**
|
|
136
|
+
* Implementation of AXChartLegendCompatible interface
|
|
137
|
+
* Highlights a specific bar by ID
|
|
138
|
+
*/
|
|
139
|
+
highlightSegment(id: string | null): void;
|
|
140
|
+
/**
|
|
141
|
+
* Implementation of AXChartLegendCompatible interface
|
|
142
|
+
* Toggles visibility of a bar by ID
|
|
143
|
+
* @returns New visibility state (true = visible, false = hidden)
|
|
144
|
+
*/
|
|
145
|
+
toggleSegment(id: string): boolean;
|
|
115
146
|
static ɵfac: i0.ɵɵFactoryDeclaration<AXBarChartComponent, never>;
|
|
116
147
|
static ɵcmp: i0.ɵɵComponentDeclaration<AXBarChartComponent, "ax-bar-chart", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; }, { "barClick": "barClick"; }, never, never, true, never>;
|
|
117
148
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ElementRef } from '@angular/core';
|
|
2
|
+
import { AXChartLegendCompatible, AXChartLegendItem, AXChartLegendOptions } from './chart-legend.type';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class AXChartLegendComponent {
|
|
5
|
+
/**
|
|
6
|
+
* Chart component instance
|
|
7
|
+
* Must implement AXChartLegendCompatible interface
|
|
8
|
+
*/
|
|
9
|
+
chart: import("@angular/core").InputSignal<AXChartLegendCompatible>;
|
|
10
|
+
/**
|
|
11
|
+
* Configuration options for the legend
|
|
12
|
+
*/
|
|
13
|
+
options: import("@angular/core").InputSignal<AXChartLegendOptions>;
|
|
14
|
+
/**
|
|
15
|
+
* Event emitted when a legend item is clicked
|
|
16
|
+
* Returns the item that was clicked
|
|
17
|
+
*/
|
|
18
|
+
itemClick: import("@angular/core").OutputEmitterRef<AXChartLegendItem>;
|
|
19
|
+
/**
|
|
20
|
+
* Event emitted when the mouse enters a legend item
|
|
21
|
+
*/
|
|
22
|
+
itemMouseEnter: import("@angular/core").OutputEmitterRef<AXChartLegendItem>;
|
|
23
|
+
/**
|
|
24
|
+
* Event emitted when the mouse leaves a legend item
|
|
25
|
+
*/
|
|
26
|
+
itemMouseLeave: import("@angular/core").OutputEmitterRef<AXChartLegendItem>;
|
|
27
|
+
/**
|
|
28
|
+
* Reference to legend container for measuring dimensions
|
|
29
|
+
*/
|
|
30
|
+
legendContainer?: ElementRef<HTMLDivElement>;
|
|
31
|
+
protected showValues: import("@angular/core").Signal<boolean>;
|
|
32
|
+
protected showPercentage: import("@angular/core").Signal<boolean>;
|
|
33
|
+
protected containerClass: import("@angular/core").Signal<{
|
|
34
|
+
[x: string]: boolean;
|
|
35
|
+
'ax-chart-legend': boolean;
|
|
36
|
+
}>;
|
|
37
|
+
protected isInteractive: import("@angular/core").Signal<boolean>;
|
|
38
|
+
protected displayItems: import("@angular/core").Signal<AXChartLegendItem[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Handle clicks on legend items
|
|
41
|
+
*/
|
|
42
|
+
protected onItemClick(item: AXChartLegendItem): void;
|
|
43
|
+
/**
|
|
44
|
+
* Handle mouse enter on legend items
|
|
45
|
+
*/
|
|
46
|
+
protected onItemMouseEnter(item: AXChartLegendItem): void;
|
|
47
|
+
/**
|
|
48
|
+
* Handle mouse leave on legend items
|
|
49
|
+
*/
|
|
50
|
+
protected onItemMouseLeave(item: AXChartLegendItem): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get legend container dimensions
|
|
53
|
+
*/
|
|
54
|
+
getDimensions(): {
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
};
|
|
58
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AXChartLegendComponent, never>;
|
|
59
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AXChartLegendComponent, "ax-chart-legend", never, { "chart": { "alias": "chart"; "required": true; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; "itemMouseEnter": "itemMouseEnter"; "itemMouseLeave": "itemMouseLeave"; }, never, never, true, never>;
|
|
60
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines a legend item
|
|
3
|
+
*/
|
|
4
|
+
export interface AXChartLegendItem {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
value: number | string;
|
|
8
|
+
percentage?: number;
|
|
9
|
+
color?: string;
|
|
10
|
+
hidden?: boolean;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Interface that chart components should implement to work with the legend
|
|
15
|
+
*/
|
|
16
|
+
export interface AXChartLegendCompatible {
|
|
17
|
+
/**
|
|
18
|
+
* Returns the data items for the legend
|
|
19
|
+
*/
|
|
20
|
+
getLegendItems(): AXChartLegendItem[];
|
|
21
|
+
/**
|
|
22
|
+
* Highlights a specific segment by ID
|
|
23
|
+
*/
|
|
24
|
+
highlightSegment(id: string | null): void;
|
|
25
|
+
/**
|
|
26
|
+
* Toggles visibility of a segment by ID
|
|
27
|
+
* @returns New visibility state (true = visible, false = hidden)
|
|
28
|
+
*/
|
|
29
|
+
toggleSegment(id: string): boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Legend configuration options
|
|
33
|
+
*/
|
|
34
|
+
export interface AXChartLegendOptions {
|
|
35
|
+
showValues?: boolean;
|
|
36
|
+
showPercentage?: boolean;
|
|
37
|
+
className?: string;
|
|
38
|
+
interactive?: boolean;
|
|
39
|
+
mode?: 'vertical' | 'horizontal';
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AXChartLegendCompatible, AXChartLegendItem } from '@acorex/charts/chart-legend';
|
|
1
2
|
import { AXChartTooltipData } from '@acorex/charts/chart-tooltip';
|
|
2
3
|
import { OnDestroy } from '@angular/core';
|
|
3
4
|
import { AXDonutChartData, AXDonutChartOption, AXDonutChartValue } from './donut-chart.type';
|
|
@@ -6,7 +7,8 @@ import * as i0 from "@angular/core";
|
|
|
6
7
|
* Donut Chart Component
|
|
7
8
|
* Displays data in a circular donut chart with interactive segments
|
|
8
9
|
*/
|
|
9
|
-
export declare class AXDonutChartComponent implements OnDestroy {
|
|
10
|
+
export declare class AXDonutChartComponent implements OnDestroy, AXChartLegendCompatible {
|
|
11
|
+
#private;
|
|
10
12
|
private cdr;
|
|
11
13
|
/** Chart data input */
|
|
12
14
|
data: import("@angular/core").InputSignal<AXDonutChartValue>;
|
|
@@ -25,6 +27,7 @@ export declare class AXDonutChartComponent implements OnDestroy {
|
|
|
25
27
|
private hiddenSegments;
|
|
26
28
|
private _initialized;
|
|
27
29
|
private _rendered;
|
|
30
|
+
private _isInitialAnimating;
|
|
28
31
|
private _tooltipVisible;
|
|
29
32
|
private _tooltipPosition;
|
|
30
33
|
private _tooltipData;
|
|
@@ -121,10 +124,6 @@ export declare class AXDonutChartComponent implements OnDestroy {
|
|
|
121
124
|
* Ensures the tooltip is visible by adjusting position when near edges
|
|
122
125
|
*/
|
|
123
126
|
private updateTooltipPosition;
|
|
124
|
-
/**
|
|
125
|
-
* Toggles the visibility of a segment
|
|
126
|
-
*/
|
|
127
|
-
private toggleSegmentVisibility;
|
|
128
127
|
/**
|
|
129
128
|
* Adds center display with total value
|
|
130
129
|
*/
|
|
@@ -141,6 +140,11 @@ export declare class AXDonutChartComponent implements OnDestroy {
|
|
|
141
140
|
* Gets an accessibility label describing the donut chart for screen readers
|
|
142
141
|
*/
|
|
143
142
|
protected getAccessibilityLabel(): string;
|
|
143
|
+
/**
|
|
144
|
+
* Returns the data items for the legend
|
|
145
|
+
* @implements AXChartLegendCompatible
|
|
146
|
+
*/
|
|
147
|
+
getLegendItems(): AXChartLegendItem[];
|
|
144
148
|
static ɵfac: i0.ɵɵFactoryDeclaration<AXDonutChartComponent, never>;
|
|
145
149
|
static ɵcmp: i0.ɵɵComponentDeclaration<AXDonutChartComponent, "ax-donut-chart", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; }, { "segmentClick": "segmentClick"; "segmentHover": "segmentHover"; }, never, never, true, never>;
|
|
146
150
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NXComponent } from '@acorex/cdk/common';
|
|
2
2
|
import { AX_CHART_COLOR_PALETTE, getChartColor } from '@acorex/charts';
|
|
3
3
|
import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
|
|
4
|
+
import { AXPlatform } from '@acorex/core/platform';
|
|
4
5
|
import { CommonModule } from '@angular/common';
|
|
5
6
|
import * as i0 from '@angular/core';
|
|
6
7
|
import { InjectionToken, inject, input, output, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
@@ -84,6 +85,8 @@ class AXBarChartComponent extends NXComponent {
|
|
|
84
85
|
configToken = inject(AX_BAR_CHART_CONFIG);
|
|
85
86
|
// Inject the chart colors
|
|
86
87
|
chartColors = inject(AX_CHART_COLOR_PALETTE);
|
|
88
|
+
// Inject AXPlatform
|
|
89
|
+
platform = inject(AXPlatform);
|
|
87
90
|
// Configuration with defaults
|
|
88
91
|
effectiveOptions = computed(() => {
|
|
89
92
|
return {
|
|
@@ -91,6 +94,8 @@ class AXBarChartComponent extends NXComponent {
|
|
|
91
94
|
...this.options(),
|
|
92
95
|
};
|
|
93
96
|
});
|
|
97
|
+
// Track hidden bars
|
|
98
|
+
hiddenBars = new Set();
|
|
94
99
|
constructor() {
|
|
95
100
|
super();
|
|
96
101
|
// Dynamically load D3 and initialize the chart when the component is ready
|
|
@@ -141,6 +146,8 @@ class AXBarChartComponent extends NXComponent {
|
|
|
141
146
|
return;
|
|
142
147
|
const containerElement = this.chartContainerEl().nativeElement;
|
|
143
148
|
const data = this.data() || [];
|
|
149
|
+
// Filter out hidden bars for visible data
|
|
150
|
+
const visibleData = data.filter((d) => !this.isBarHidden(d.id));
|
|
144
151
|
// Clear existing chart
|
|
145
152
|
this.d3.select(containerElement).selectAll('svg').remove();
|
|
146
153
|
// Early return if no data
|
|
@@ -148,11 +155,16 @@ class AXBarChartComponent extends NXComponent {
|
|
|
148
155
|
this.showNoDataMessage(containerElement);
|
|
149
156
|
return;
|
|
150
157
|
}
|
|
158
|
+
// Early return if all bars are hidden
|
|
159
|
+
if (visibleData.length === 0) {
|
|
160
|
+
this.showAllBarsHiddenMessage(containerElement);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
151
163
|
// Get options and setup dimensions
|
|
152
164
|
const chartOptions = this.effectiveOptions();
|
|
153
165
|
this.setupDimensions(containerElement, chartOptions);
|
|
154
|
-
// Create scales and axes
|
|
155
|
-
this.setupScales(
|
|
166
|
+
// Create scales and axes using visible data
|
|
167
|
+
this.setupScales(visibleData);
|
|
156
168
|
this.createAxes(chartOptions);
|
|
157
169
|
// Render the bars
|
|
158
170
|
this.renderBars(data);
|
|
@@ -266,6 +278,7 @@ class AXBarChartComponent extends NXComponent {
|
|
|
266
278
|
const showXAxis = options.showXAxis !== false;
|
|
267
279
|
const showYAxis = options.showYAxis !== false;
|
|
268
280
|
const showGrid = options.showGrid !== false;
|
|
281
|
+
const isRtl = this.platform.isRtl();
|
|
269
282
|
// Create a group for all axes
|
|
270
283
|
const axesGroup = this.chart.append('g').attr('class', 'ax-bar-chart-axes');
|
|
271
284
|
if (showXAxis) {
|
|
@@ -276,16 +289,17 @@ class AXBarChartComponent extends NXComponent {
|
|
|
276
289
|
.attr('transform', `translate(0,${this.height})`)
|
|
277
290
|
.call(this.d3.axisBottom(this.xScale));
|
|
278
291
|
// Style the axis text
|
|
292
|
+
const dynamicXAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.width / 45)));
|
|
279
293
|
this.xAxis
|
|
280
294
|
.selectAll('text')
|
|
281
|
-
.style('font-size',
|
|
295
|
+
.style('font-size', `${dynamicXAxisTickFontSize}px`)
|
|
282
296
|
.style('font-weight', '400')
|
|
283
297
|
.style('fill', 'rgba(var(--ax-comp-bar-chart-labels-color), 0.7)');
|
|
284
298
|
// Style all lines in the x-axis (path, ticks)
|
|
285
299
|
this.xAxis.selectAll('line, path').style('stroke', 'rgb(var(--ax-comp-bar-chart-grid-lines-color))');
|
|
286
300
|
// Add X axis label if provided
|
|
287
301
|
if (options.xAxisLabel) {
|
|
288
|
-
const labelY = this.height + this.margin.bottom * 0.
|
|
302
|
+
const labelY = this.height + this.margin.bottom * 0.8;
|
|
289
303
|
axesGroup
|
|
290
304
|
.append('text')
|
|
291
305
|
.attr('class', 'ax-bar-chart-axis-label ax-x-axis-label')
|
|
@@ -293,7 +307,7 @@ class AXBarChartComponent extends NXComponent {
|
|
|
293
307
|
.attr('dominant-baseline', 'middle')
|
|
294
308
|
.attr('x', this.width / 2)
|
|
295
309
|
.attr('y', labelY)
|
|
296
|
-
.style('font-size', '
|
|
310
|
+
.style('font-size', '14px')
|
|
297
311
|
.style('font-weight', '500')
|
|
298
312
|
.style('fill', 'rgb(var(--ax-comp-bar-chart-axis-label-color))')
|
|
299
313
|
.text(options.xAxisLabel);
|
|
@@ -303,17 +317,21 @@ class AXBarChartComponent extends NXComponent {
|
|
|
303
317
|
// Create Y axis
|
|
304
318
|
this.yAxis = axesGroup.append('g').attr('class', 'ax-bar-chart-axis-y').call(this.d3.axisLeft(this.yScale));
|
|
305
319
|
// Style the axis text
|
|
306
|
-
this.
|
|
320
|
+
const dynamicYAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.height / 30)));
|
|
321
|
+
const yTickTexts = this.yAxis
|
|
307
322
|
.selectAll('text')
|
|
308
|
-
.style('font-size',
|
|
323
|
+
.style('font-size', `${dynamicYAxisTickFontSize}px`)
|
|
309
324
|
.style('font-weight', '400')
|
|
310
325
|
.style('fill', 'rgba(var(--ax-comp-bar-chart-labels-color), 0.7)');
|
|
326
|
+
if (isRtl) {
|
|
327
|
+
yTickTexts.attr('text-anchor', 'start');
|
|
328
|
+
}
|
|
311
329
|
// Style all lines in the y-axis (path, ticks)
|
|
312
330
|
this.yAxis.selectAll('line, path').style('stroke', 'rgb(var(--ax-comp-bar-chart-grid-lines-color))');
|
|
313
331
|
// Add Y axis label if provided
|
|
314
332
|
if (options.yAxisLabel) {
|
|
315
333
|
const labelX = -this.height / 2;
|
|
316
|
-
const labelY = -this.margin.left * 0.
|
|
334
|
+
const labelY = -this.margin.left * 0.8;
|
|
317
335
|
axesGroup
|
|
318
336
|
.append('text')
|
|
319
337
|
.attr('class', 'ax-bar-chart-axis-label ax-y-axis-label')
|
|
@@ -322,7 +340,7 @@ class AXBarChartComponent extends NXComponent {
|
|
|
322
340
|
.attr('transform', 'rotate(-90)')
|
|
323
341
|
.attr('x', labelX)
|
|
324
342
|
.attr('y', labelY)
|
|
325
|
-
.style('font-size', '
|
|
343
|
+
.style('font-size', '14px')
|
|
326
344
|
.style('font-weight', '500')
|
|
327
345
|
.style('fill', 'rgb(var(--ax-comp-bar-chart-axis-label-color))')
|
|
328
346
|
.text(options.yAxisLabel);
|
|
@@ -348,6 +366,9 @@ class AXBarChartComponent extends NXComponent {
|
|
|
348
366
|
* Renders the bars with animations
|
|
349
367
|
*/
|
|
350
368
|
renderBars(data) {
|
|
369
|
+
// Filter out hidden bars
|
|
370
|
+
const visibleData = data.filter((d) => !this.isBarHidden(d.id));
|
|
371
|
+
const originalFullData = this.data(); // Get the original full data set
|
|
351
372
|
// Reset animation state
|
|
352
373
|
this._initialAnimationComplete.set(false);
|
|
353
374
|
// Get corner radius from options
|
|
@@ -360,7 +381,7 @@ class AXBarChartComponent extends NXComponent {
|
|
|
360
381
|
// Create groups for each bar to handle transformations
|
|
361
382
|
const barGroups = barsContainer
|
|
362
383
|
.selectAll('.ax-bar-chart-bar-group')
|
|
363
|
-
.data(
|
|
384
|
+
.data(visibleData)
|
|
364
385
|
.enter()
|
|
365
386
|
.append('g')
|
|
366
387
|
.style('cursor', 'pointer')
|
|
@@ -375,7 +396,13 @@ class AXBarChartComponent extends NXComponent {
|
|
|
375
396
|
.attr('height', 0) // Start with height 0 for animation
|
|
376
397
|
.attr('rx', radius) // Rounded corners
|
|
377
398
|
.attr('ry', radius) // Rounded corners
|
|
378
|
-
.attr('fill', (d
|
|
399
|
+
.attr('fill', (d) => {
|
|
400
|
+
// Find the index of the current bar (d) in the original data array
|
|
401
|
+
const originalIndex = originalFullData.findIndex((item) => item.id === d.id);
|
|
402
|
+
// Use the original color if provided, otherwise get color from palette using original index
|
|
403
|
+
// Fallback to index 0 if not found, though this shouldn't happen with valid data.
|
|
404
|
+
return d.color || this.getColor(originalIndex !== -1 ? originalIndex : 0);
|
|
405
|
+
});
|
|
379
406
|
// Add data labels if they're enabled
|
|
380
407
|
if (this.effectiveOptions().showDataLabels !== false) {
|
|
381
408
|
barGroups
|
|
@@ -478,7 +505,7 @@ class AXBarChartComponent extends NXComponent {
|
|
|
478
505
|
handleBarHover(event, datum) {
|
|
479
506
|
if (this.effectiveOptions().showTooltip !== false) {
|
|
480
507
|
const index = this.data().findIndex((item) => item.id === datum.id);
|
|
481
|
-
const color = datum.color ||
|
|
508
|
+
const color = datum.color || this.getColor(index);
|
|
482
509
|
// Calculate percentage of total
|
|
483
510
|
const total = this.data().reduce((sum, item) => sum + item.value, 0);
|
|
484
511
|
const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
|
|
@@ -531,47 +558,211 @@ class AXBarChartComponent extends NXComponent {
|
|
|
531
558
|
const messageContainer = this.d3
|
|
532
559
|
.select(containerElement)
|
|
533
560
|
.append('div')
|
|
534
|
-
|
|
535
|
-
.
|
|
536
|
-
.style('left', '50%')
|
|
537
|
-
.style('top', '50%')
|
|
538
|
-
.style('transform', 'translate(-50%, -50%)')
|
|
539
|
-
.style('text-align', 'center')
|
|
540
|
-
.style('background-color', 'rgb(var(--ax-comp-bar-chart-bg-color))')
|
|
541
|
-
.style('padding', '1.5rem')
|
|
542
|
-
.style('border-radius', '0.5rem')
|
|
543
|
-
.style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')
|
|
544
|
-
.style('width', '80%')
|
|
545
|
-
.style('max-width', '300px');
|
|
561
|
+
// Apply generic container class, specific bar chart background class, and existing specific class
|
|
562
|
+
.attr('class', 'ax-chart-message-container ax-bar-chart-message-background ax-bar-chart-no-data-message');
|
|
546
563
|
// Add an icon
|
|
547
564
|
messageContainer
|
|
548
565
|
.append('div')
|
|
549
|
-
|
|
550
|
-
.
|
|
551
|
-
.style('margin-bottom', '0.75rem')
|
|
566
|
+
// Apply generic icon class and specific bar chart icon class
|
|
567
|
+
.attr('class', 'ax-chart-message-icon ax-bar-chart-no-data-icon')
|
|
552
568
|
.html('<i class="fa-light fa-chart-column fa-2x"></i>');
|
|
553
569
|
// Add text message
|
|
554
570
|
messageContainer
|
|
555
571
|
.append('div')
|
|
556
|
-
|
|
557
|
-
.
|
|
558
|
-
.style('font-weight', '600')
|
|
559
|
-
.style('margin-bottom', '0.5rem')
|
|
572
|
+
// Apply generic text class and specific bar chart text class
|
|
573
|
+
.attr('class', 'ax-chart-message-text ax-bar-chart-no-data-text')
|
|
560
574
|
.text('No data available');
|
|
561
575
|
// Add help text
|
|
562
576
|
messageContainer
|
|
563
577
|
.append('div')
|
|
564
|
-
|
|
565
|
-
.
|
|
566
|
-
.style('opacity', '0.6')
|
|
578
|
+
// Apply generic help class and specific bar chart help class
|
|
579
|
+
.attr('class', 'ax-chart-message-help ax-bar-chart-no-data-help')
|
|
567
580
|
.text('Please provide data to display the chart');
|
|
568
581
|
}
|
|
569
|
-
|
|
570
|
-
|
|
582
|
+
/**
|
|
583
|
+
* Shows a message when all bars are hidden
|
|
584
|
+
*/
|
|
585
|
+
showAllBarsHiddenMessage(containerElement) {
|
|
586
|
+
// Clear existing contents first
|
|
587
|
+
this.d3.select(containerElement).selectAll('*').remove();
|
|
588
|
+
const messageContainer = this.d3
|
|
589
|
+
.select(containerElement)
|
|
590
|
+
.append('div')
|
|
591
|
+
// Apply generic container class, specific bar chart background class, and existing specific class
|
|
592
|
+
.attr('class', 'ax-chart-message-container ax-bar-chart-message-background ax-bar-chart-no-data-message');
|
|
593
|
+
// Add an icon
|
|
594
|
+
messageContainer
|
|
595
|
+
.append('div')
|
|
596
|
+
// Apply generic icon class and specific bar chart icon class
|
|
597
|
+
.attr('class', 'ax-chart-message-icon ax-bar-chart-no-data-icon')
|
|
598
|
+
.html('<i class="fa-light fa-eye-slash fa-2x"></i>');
|
|
599
|
+
// Add text message
|
|
600
|
+
messageContainer
|
|
601
|
+
.append('div')
|
|
602
|
+
// Apply generic text class and specific bar chart text class
|
|
603
|
+
.attr('class', 'ax-chart-message-text ax-bar-chart-no-data-text')
|
|
604
|
+
.text('All bars are hidden');
|
|
605
|
+
// Add help text
|
|
606
|
+
messageContainer
|
|
607
|
+
.append('div')
|
|
608
|
+
// Apply generic help class and specific bar chart help class
|
|
609
|
+
.attr('class', 'ax-chart-message-help ax-bar-chart-no-data-help')
|
|
610
|
+
.text('Click on legend items to show bars');
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Gets the color for a bar based on its index
|
|
614
|
+
*/
|
|
615
|
+
getColor(index) {
|
|
616
|
+
return getChartColor(index, this.chartColors);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Checks if a bar is hidden
|
|
620
|
+
*/
|
|
621
|
+
isBarHidden(id) {
|
|
622
|
+
return this.hiddenBars.has(id);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Implementation of AXChartLegendCompatible interface
|
|
626
|
+
* Returns legend items based on the chart data
|
|
627
|
+
*/
|
|
628
|
+
getLegendItems() {
|
|
629
|
+
const data = this.data() || [];
|
|
630
|
+
const total = data.reduce((sum, item) => sum + (typeof item.value === 'number' ? item.value : 0), 0);
|
|
631
|
+
return data.map((item, index) => {
|
|
632
|
+
const value = typeof item.value === 'number' ? item.value : 0;
|
|
633
|
+
const percentage = total > 0 ? (value / total) * 100 : 0;
|
|
634
|
+
return {
|
|
635
|
+
id: item.id,
|
|
636
|
+
name: item.label || `Item ${index + 1}`,
|
|
637
|
+
value: item.value,
|
|
638
|
+
percentage,
|
|
639
|
+
color: item.color || this.getColor(index),
|
|
640
|
+
hidden: this.isBarHidden(item.id),
|
|
641
|
+
};
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Implementation of AXChartLegendCompatible interface
|
|
646
|
+
* Highlights a specific bar by ID
|
|
647
|
+
*/
|
|
648
|
+
highlightSegment(id) {
|
|
649
|
+
if (!this.svg)
|
|
650
|
+
return;
|
|
651
|
+
// If the bar is hidden, we shouldn't try to highlight it
|
|
652
|
+
if (id !== null && this.isBarHidden(id)) {
|
|
653
|
+
// Just unhighlight everything when trying to highlight a hidden bar
|
|
654
|
+
this.svg
|
|
655
|
+
.selectAll('.ax-bar-chart-bar')
|
|
656
|
+
.classed('ax-bar-chart-highlighted', false)
|
|
657
|
+
.classed('ax-bar-chart-dimmed', false)
|
|
658
|
+
.attr('opacity', 1)
|
|
659
|
+
.attr('filter', null)
|
|
660
|
+
.attr('stroke', null)
|
|
661
|
+
.attr('stroke-width', null)
|
|
662
|
+
.attr('stroke-opacity', null);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (id === null) {
|
|
666
|
+
// If id is null, unhighlight everything
|
|
667
|
+
this.svg
|
|
668
|
+
.selectAll('.ax-bar-chart-bar')
|
|
669
|
+
.classed('ax-bar-chart-highlighted', false)
|
|
670
|
+
.classed('ax-bar-chart-dimmed', false)
|
|
671
|
+
.attr('opacity', 1)
|
|
672
|
+
.attr('filter', null)
|
|
673
|
+
.attr('stroke', null)
|
|
674
|
+
.attr('stroke-width', null)
|
|
675
|
+
.attr('stroke-opacity', null);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
// Find the target bar
|
|
679
|
+
const targetBar = this.svg.selectAll('.ax-bar-chart-bar').filter((d) => d?.id === id);
|
|
680
|
+
// Safety check - if no matching bar is found, do nothing
|
|
681
|
+
if (targetBar.empty())
|
|
682
|
+
return;
|
|
683
|
+
// Check if the target bar is currently highlighted
|
|
684
|
+
const isCurrentlyHighlighted = targetBar.classed('ax-bar-chart-highlighted');
|
|
685
|
+
if (isCurrentlyHighlighted) {
|
|
686
|
+
// If already highlighted, unhighlight all bars
|
|
687
|
+
this.svg
|
|
688
|
+
.selectAll('.ax-bar-chart-bar')
|
|
689
|
+
.classed('ax-bar-chart-highlighted', false)
|
|
690
|
+
.classed('ax-bar-chart-dimmed', false)
|
|
691
|
+
.attr('opacity', 1)
|
|
692
|
+
.attr('filter', null)
|
|
693
|
+
.attr('stroke', null)
|
|
694
|
+
.attr('stroke-width', null)
|
|
695
|
+
.attr('stroke-opacity', null);
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
// Reset all bars first
|
|
699
|
+
this.svg
|
|
700
|
+
.selectAll('.ax-bar-chart-bar')
|
|
701
|
+
.classed('ax-bar-chart-highlighted', false)
|
|
702
|
+
.classed('ax-bar-chart-dimmed', false)
|
|
703
|
+
.attr('opacity', 1)
|
|
704
|
+
.attr('filter', null)
|
|
705
|
+
.attr('stroke', null)
|
|
706
|
+
.attr('stroke-width', null)
|
|
707
|
+
.attr('stroke-opacity', null);
|
|
708
|
+
// Highlight the target bar
|
|
709
|
+
targetBar
|
|
710
|
+
.classed('ax-bar-chart-highlighted', true)
|
|
711
|
+
.attr('filter', 'drop-shadow(0 0 4px rgba(0, 0, 0, 0.3))')
|
|
712
|
+
.attr('stroke', '#000')
|
|
713
|
+
.attr('stroke-width', '1px')
|
|
714
|
+
.attr('stroke-opacity', '0.3')
|
|
715
|
+
.style('transition', 'all 0.2s ease-in-out');
|
|
716
|
+
// Dim other bars
|
|
717
|
+
this.svg
|
|
718
|
+
.selectAll('.ax-bar-chart-bar')
|
|
719
|
+
.filter((d) => d?.id !== id)
|
|
720
|
+
.classed('ax-bar-chart-dimmed', true)
|
|
721
|
+
.attr('opacity', 0.5)
|
|
722
|
+
.style('transition', 'opacity 0.2s ease-in-out');
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Implementation of AXChartLegendCompatible interface
|
|
727
|
+
* Toggles visibility of a bar by ID
|
|
728
|
+
* @returns New visibility state (true = visible, false = hidden)
|
|
729
|
+
*/
|
|
730
|
+
toggleSegment(id) {
|
|
731
|
+
const wasAllHidden = this.data().length > 0 && this.data().every((d) => this.isBarHidden(d.id));
|
|
732
|
+
if (this.hiddenBars.has(id)) {
|
|
733
|
+
// Making a bar visible
|
|
734
|
+
this.hiddenBars.delete(id);
|
|
735
|
+
// If all bars were previously hidden, we need to ensure the message is cleared
|
|
736
|
+
if (wasAllHidden) {
|
|
737
|
+
// Force complete DOM cleanup and redraw
|
|
738
|
+
if (this.chartContainerEl()?.nativeElement) {
|
|
739
|
+
// Clear everything in the container
|
|
740
|
+
this.d3?.select(this.chartContainerEl().nativeElement).selectAll('*').remove();
|
|
741
|
+
// Force full redraw
|
|
742
|
+
setTimeout(() => {
|
|
743
|
+
this.createChart();
|
|
744
|
+
}, 0);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
// Normal update for other cases
|
|
749
|
+
this.updateChart();
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
// Hiding a bar
|
|
754
|
+
this.hiddenBars.add(id);
|
|
755
|
+
this.updateChart();
|
|
756
|
+
}
|
|
757
|
+
// Return the new visibility state
|
|
758
|
+
return !this.hiddenBars.has(id);
|
|
759
|
+
}
|
|
760
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AXBarChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
761
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.14", 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\" #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: var(--ax-sys-color-lightest-surface)}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;color:rgb(var(--ax-sys-color-on-lightest-surface));background-color:rgb(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:visible}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;box-shadow:0 2px 12px #00000014;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: "ngmodule", type: CommonModule }, { kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
571
762
|
}
|
|
572
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
763
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AXBarChartComponent, decorators: [{
|
|
573
764
|
type: Component,
|
|
574
|
-
args: [{ selector: 'ax-bar-chart', encapsulation: ViewEncapsulation.None, imports: [CommonModule, AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-bar-chart\" #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: var(--ax-sys-color-lightest-surface)}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;color:rgb(var(--ax-sys-color-on-lightest-surface));background-color:rgb(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:visible}ax-bar-chart .ax-bar-chart-no-data-message{
|
|
765
|
+
args: [{ selector: 'ax-bar-chart', encapsulation: ViewEncapsulation.None, imports: [CommonModule, AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-bar-chart\" #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: var(--ax-sys-color-lightest-surface)}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;color:rgb(var(--ax-sys-color-on-lightest-surface));background-color:rgb(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:visible}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;box-shadow:0 2px 12px #00000014;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"] }]
|
|
575
766
|
}], ctorParameters: () => [] });
|
|
576
767
|
|
|
577
768
|
/**
|