cats4u-charts 0.0.1
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/.editorconfig +17 -0
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +20 -0
- package/.vscode/tasks.json +42 -0
- package/README.md +59 -0
- package/angular.json +118 -0
- package/dist/charts-lib/README.md +63 -0
- package/dist/charts-lib/fesm2022/charts-lib.mjs +3418 -0
- package/dist/charts-lib/fesm2022/charts-lib.mjs.map +1 -0
- package/dist/charts-lib/index.d.ts +110 -0
- package/package.json +58 -0
- package/projects/charts-lib/README.md +63 -0
- package/projects/charts-lib/ng-package.json +8 -0
- package/projects/charts-lib/package.json +12 -0
- package/projects/charts-lib/src/lib/charts-lib.html +1 -0
- package/projects/charts-lib/src/lib/charts-lib.spec.ts +23 -0
- package/projects/charts-lib/src/lib/charts-lib.ts +121 -0
- package/projects/charts-lib/src/lib/component/area-chart/area-chart.html +1 -0
- package/projects/charts-lib/src/lib/component/area-chart/area-chart.scss +0 -0
- package/projects/charts-lib/src/lib/component/area-chart/area-chart.spec.ts +23 -0
- package/projects/charts-lib/src/lib/component/area-chart/area-chart.ts +266 -0
- package/projects/charts-lib/src/lib/component/bar-chart/bar-chart.html +1 -0
- package/projects/charts-lib/src/lib/component/bar-chart/bar-chart.scss +0 -0
- package/projects/charts-lib/src/lib/component/bar-chart/bar-chart.spec.ts +23 -0
- package/projects/charts-lib/src/lib/component/bar-chart/bar-chart.ts +301 -0
- package/projects/charts-lib/src/lib/component/line-chart/line-chart.html +1 -0
- package/projects/charts-lib/src/lib/component/line-chart/line-chart.scss +0 -0
- package/projects/charts-lib/src/lib/component/line-chart/line-chart.spec.ts +23 -0
- package/projects/charts-lib/src/lib/component/line-chart/line-chart.ts +266 -0
- package/projects/charts-lib/src/lib/modal/charts-lib.modal.ts +79 -0
- package/projects/charts-lib/src/lib/services/chart.service.ts +296 -0
- package/projects/charts-lib/src/lib/themes/chalk.ts +357 -0
- package/projects/charts-lib/src/lib/themes/dark.ts +380 -0
- package/projects/charts-lib/src/lib/themes/default.ts +377 -0
- package/projects/charts-lib/src/lib/themes/essos.ts +357 -0
- package/projects/charts-lib/src/lib/themes/roma.ts +399 -0
- package/projects/charts-lib/src/lib/themes/vintage.ts +378 -0
- package/projects/charts-lib/src/public-api.ts +2 -0
- package/projects/charts-lib/tsconfig.lib.json +14 -0
- package/projects/charts-lib/tsconfig.lib.prod.json +11 -0
- package/projects/charts-lib/tsconfig.spec.json +15 -0
- package/projects/demo-app/public/favicon.ico +0 -0
- package/projects/demo-app/src/app/app.config.ts +16 -0
- package/projects/demo-app/src/app/app.html +43 -0
- package/projects/demo-app/src/app/app.routes.ts +3 -0
- package/projects/demo-app/src/app/app.scss +47 -0
- package/projects/demo-app/src/app/app.spec.ts +25 -0
- package/projects/demo-app/src/app/app.ts +98 -0
- package/projects/demo-app/src/index.html +13 -0
- package/projects/demo-app/src/main.ts +6 -0
- package/projects/demo-app/src/styles.scss +4 -0
- package/projects/demo-app/tsconfig.app.json +15 -0
- package/projects/demo-app/tsconfig.spec.json +15 -0
- package/tsconfig.json +43 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<ng-template #chartContainer></ng-template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ChartsLib } from './charts-lib';
|
|
4
|
+
|
|
5
|
+
describe('ChartsLib', () => {
|
|
6
|
+
let component: ChartsLib;
|
|
7
|
+
let fixture: ComponentFixture<ChartsLib>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [ChartsLib]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(ChartsLib);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
Input,
|
|
4
|
+
OnChanges,
|
|
5
|
+
SimpleChanges,
|
|
6
|
+
ViewChild,
|
|
7
|
+
ViewContainerRef,
|
|
8
|
+
ComponentRef,
|
|
9
|
+
Type,
|
|
10
|
+
Output,
|
|
11
|
+
EventEmitter,
|
|
12
|
+
effect,
|
|
13
|
+
OnInit,
|
|
14
|
+
KeyValueDiffers,
|
|
15
|
+
KeyValueDiffer,
|
|
16
|
+
DoCheck,
|
|
17
|
+
} from '@angular/core';
|
|
18
|
+
import { BarChart } from './component/bar-chart/bar-chart';
|
|
19
|
+
import { LineChart } from './component/line-chart/line-chart';
|
|
20
|
+
import { AreaChart } from './component/area-chart/area-chart';
|
|
21
|
+
import { ChartService } from './services/chart.service';
|
|
22
|
+
import { OptionsConfig, ChartsLibType, ContextMenuListItem } from './modal/charts-lib.modal';
|
|
23
|
+
|
|
24
|
+
type ChartType = 'line' | 'bar' | 'area' | 'pie';
|
|
25
|
+
|
|
26
|
+
@Component({
|
|
27
|
+
selector: 'lib-charts-lib',
|
|
28
|
+
imports: [],
|
|
29
|
+
templateUrl: './charts-lib.html',
|
|
30
|
+
styles: ``,
|
|
31
|
+
})
|
|
32
|
+
export class ChartsLib implements OnInit, OnChanges, DoCheck {
|
|
33
|
+
@ViewChild('chartContainer', { read: ViewContainerRef }) container!: ViewContainerRef;
|
|
34
|
+
@Input() chartType!: ChartType;
|
|
35
|
+
@Input() columns: string[] = [];
|
|
36
|
+
@Input() data: any[][] = [];
|
|
37
|
+
@Input() themeName: ChartsLibType['themeName'] = 'default';
|
|
38
|
+
@Input() chartOptionsConfig: OptionsConfig = {};
|
|
39
|
+
@Input() contextMenuList: ContextMenuListItem[] = [];
|
|
40
|
+
@Output() handleSingleClick = new EventEmitter();
|
|
41
|
+
@Output() handleDrillBy = new EventEmitter();
|
|
42
|
+
|
|
43
|
+
private optionsConfigDiffer!: KeyValueDiffer<string, any>;
|
|
44
|
+
private componentRef: ComponentRef<any> | null = null;
|
|
45
|
+
constructor(private chartService: ChartService, private differs: KeyValueDiffers) {
|
|
46
|
+
effect(() => {
|
|
47
|
+
if (this.chartService.clickEvent().seriesName) {
|
|
48
|
+
this.handleSingleClick.emit(this.chartService.clickEvent());
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
effect(() => {
|
|
52
|
+
if (this.chartService.drillByConfig().seriesName) {
|
|
53
|
+
this.handleDrillBy.emit(this.chartService.drillByConfig());
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ngOnInit(): void {
|
|
59
|
+
this.optionsConfigDiffer = this.differs.find(this.chartOptionsConfig).create();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ngAfterViewInit(): void {
|
|
63
|
+
this.loadComponent();
|
|
64
|
+
}
|
|
65
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
66
|
+
if (changes['chartType']) {
|
|
67
|
+
this.loadComponent();
|
|
68
|
+
}
|
|
69
|
+
if (changes['themeName'] && this.componentRef) {
|
|
70
|
+
this.chartService.setTheme(this.themeName);
|
|
71
|
+
}
|
|
72
|
+
if (changes['data']) {
|
|
73
|
+
this.chartService.setData(this.data);
|
|
74
|
+
}
|
|
75
|
+
if (changes['columns']) {
|
|
76
|
+
this.chartService.setColumns(this.columns);
|
|
77
|
+
}
|
|
78
|
+
if (changes['contextMenuList']) {
|
|
79
|
+
this.chartService.setContextMenuList(this.contextMenuList);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ngDoCheck(): void {
|
|
84
|
+
if (this.chartOptionsConfig) {
|
|
85
|
+
const changes = this.optionsConfigDiffer.diff(this.chartOptionsConfig);
|
|
86
|
+
if (changes) {
|
|
87
|
+
this.chartService.setChartOptionsConfig({ ...this.chartOptionsConfig });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private loadComponent(): void {
|
|
93
|
+
if (!this.container) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this.componentRef) {
|
|
97
|
+
this.componentRef.destroy();
|
|
98
|
+
}
|
|
99
|
+
const componentType = this.getComponentType();
|
|
100
|
+
if (componentType) {
|
|
101
|
+
this.componentRef = this.container.createComponent(componentType);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
private getComponentType(): Type<any> | null {
|
|
105
|
+
switch (this.chartType) {
|
|
106
|
+
case 'bar':
|
|
107
|
+
return BarChart;
|
|
108
|
+
case 'line':
|
|
109
|
+
return LineChart;
|
|
110
|
+
case 'area':
|
|
111
|
+
return AreaChart;
|
|
112
|
+
default:
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
ngOnDestroy(): void {
|
|
117
|
+
if (this.componentRef) {
|
|
118
|
+
this.componentRef.destroy();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div #areaChartContainer style="width: 100%; height: 100%"></div>
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { AreaChart } from './area-chart';
|
|
4
|
+
|
|
5
|
+
describe('AreaChart', () => {
|
|
6
|
+
let component: AreaChart;
|
|
7
|
+
let fixture: ComponentFixture<AreaChart>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [AreaChart]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(AreaChart);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
Input,
|
|
4
|
+
OnInit,
|
|
5
|
+
OnDestroy,
|
|
6
|
+
ChangeDetectionStrategy,
|
|
7
|
+
ChangeDetectorRef,
|
|
8
|
+
ViewChild,
|
|
9
|
+
ElementRef,
|
|
10
|
+
inject,
|
|
11
|
+
effect,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
import * as echarts from 'echarts'; // Core ECharts
|
|
14
|
+
import { Subject } from 'rxjs'; // For cleanup
|
|
15
|
+
import { ChartService } from '../../services/chart.service';
|
|
16
|
+
import '../../themes/default';
|
|
17
|
+
import '../../themes/vintage';
|
|
18
|
+
import '../../themes/dark';
|
|
19
|
+
import '../../themes/essos';
|
|
20
|
+
import '../../themes/chalk';
|
|
21
|
+
import '../../themes/roma';
|
|
22
|
+
|
|
23
|
+
@Component({
|
|
24
|
+
selector: 'lib-area-chart',
|
|
25
|
+
imports: [],
|
|
26
|
+
templateUrl: './area-chart.html',
|
|
27
|
+
styleUrl: './area-chart.scss',
|
|
28
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
29
|
+
})
|
|
30
|
+
export class AreaChart implements OnInit, OnDestroy {
|
|
31
|
+
@Input() title: string = '';
|
|
32
|
+
@Input() height = '600px';
|
|
33
|
+
@Input() enableSampling = true; // Sample data if > threshold for perf
|
|
34
|
+
@Input() sampleThreshold = 10000; // Sample if data > 10k (keep full for 50k if hardware allows)
|
|
35
|
+
@Input() isLoading: boolean = true;
|
|
36
|
+
|
|
37
|
+
@ViewChild('areaChartContainer', { static: false }) chartContainer!: ElementRef<HTMLDivElement>;
|
|
38
|
+
|
|
39
|
+
chartInstance: echarts.ECharts | null = null;
|
|
40
|
+
private chartService = inject(ChartService);
|
|
41
|
+
private destroy$ = new Subject<void>();
|
|
42
|
+
private currentData: any[][] = [];
|
|
43
|
+
private currentColumns: string[] = [];
|
|
44
|
+
|
|
45
|
+
// ECharts options optimized for 50k+ data
|
|
46
|
+
chartOptions = {
|
|
47
|
+
title: {
|
|
48
|
+
text: this.title,
|
|
49
|
+
left: 'center',
|
|
50
|
+
textStyle: { fontSize: 16 },
|
|
51
|
+
},
|
|
52
|
+
tooltip: {
|
|
53
|
+
// Hover info (efficient for large data)
|
|
54
|
+
trigger: 'axis',
|
|
55
|
+
axisPointer: { type: 'shadow' },
|
|
56
|
+
// formatter: (params = [{ name: '', value: '' }]) => {
|
|
57
|
+
// const param = params[0];
|
|
58
|
+
// return `${param.name}<br/>Value: ${param.value}`;
|
|
59
|
+
// },
|
|
60
|
+
},
|
|
61
|
+
legend: { show: false }, // Disable for single series; add if multi-series
|
|
62
|
+
dataZoom: [
|
|
63
|
+
// Critical for large data: Zoom/slider to navigate 50k data points
|
|
64
|
+
{
|
|
65
|
+
type: 'slider', // Bottom slider
|
|
66
|
+
start: 0,
|
|
67
|
+
end: 100, // Initial view: First 10% (e.g., 5k of 50k)
|
|
68
|
+
height: 25,
|
|
69
|
+
bottom: 20,
|
|
70
|
+
textStyle: { fontSize: 12 },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: 'inside', // Mouse wheel/pinch zoom
|
|
74
|
+
start: 0,
|
|
75
|
+
end: 10,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
grid: {
|
|
79
|
+
left: '5%',
|
|
80
|
+
right: '5%',
|
|
81
|
+
bottom: '80px', // Space for dataZoom slider
|
|
82
|
+
containLabel: true,
|
|
83
|
+
},
|
|
84
|
+
xAxis: {
|
|
85
|
+
type: 'category',
|
|
86
|
+
data: [], // Populated dynamically
|
|
87
|
+
axisLabel: { rotate: 45, fontSize: 10 },
|
|
88
|
+
axisTick: { show: false },
|
|
89
|
+
},
|
|
90
|
+
yAxis: {
|
|
91
|
+
type: 'value',
|
|
92
|
+
axisLabel: { fontSize: 12 },
|
|
93
|
+
},
|
|
94
|
+
series: [],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
constructor(private cdr: ChangeDetectorRef) {
|
|
98
|
+
effect(() => {
|
|
99
|
+
const theme = this.chartService.theme(); // Read signal
|
|
100
|
+
if (this.chartInstance) {
|
|
101
|
+
this.reinitializeChart();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// Effect for data changes
|
|
105
|
+
effect(() => {
|
|
106
|
+
const data = this.chartService.data(); // Read signal
|
|
107
|
+
const columns = this.chartService.columns();
|
|
108
|
+
this.currentColumns = columns;
|
|
109
|
+
this.currentData = data;
|
|
110
|
+
this.processAndUpdateData();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
ngOnInit(): void {
|
|
115
|
+
if (this.currentData.length > 0) {
|
|
116
|
+
this.processAndUpdateData();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ngAfterViewInit(): void {
|
|
121
|
+
this.initializeChart();
|
|
122
|
+
if (this.currentData.length > 0) {
|
|
123
|
+
this.updateChartWithData();
|
|
124
|
+
}
|
|
125
|
+
this.isLoading = false;
|
|
126
|
+
this.cdr.markForCheck();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
ngOnDestroy(): void {
|
|
130
|
+
this.destroy$.next();
|
|
131
|
+
this.destroy$.complete();
|
|
132
|
+
this.disposeChart();
|
|
133
|
+
if (this.chartInstance) {
|
|
134
|
+
this.chartInstance.dispose(); // Clean up to free memory
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private reinitializeChart(): void {
|
|
139
|
+
this.initializeChart();
|
|
140
|
+
if (this.currentData.length > 0) {
|
|
141
|
+
this.updateChartWithData();
|
|
142
|
+
}
|
|
143
|
+
this.cdr.markForCheck();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private initializeChart(): void {
|
|
147
|
+
if (!this.chartContainer) {
|
|
148
|
+
console.error('Chart container not found!');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const chartDom = this.chartContainer.nativeElement;
|
|
152
|
+
chartDom.innerHTML = '';
|
|
153
|
+
|
|
154
|
+
if (this.chartInstance) {
|
|
155
|
+
this.chartInstance.dispose();
|
|
156
|
+
this.chartInstance = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.chartInstance = echarts.init(chartDom, this.chartService.theme()); // Init ECharts
|
|
160
|
+
this.chartInstance.setOption(this.chartOptions);
|
|
161
|
+
|
|
162
|
+
// Responsive: Resize on window change
|
|
163
|
+
window.addEventListener('resize', () => this.chartInstance?.resize(), { passive: true });
|
|
164
|
+
|
|
165
|
+
// Handle dataZoom events (optional: Log zoom changes)
|
|
166
|
+
this.chartInstance.on('dataZoom', (params) => {
|
|
167
|
+
// console.log('Zoomed to:', params.start, params.end);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.chartInstance.on('rendered', () => {
|
|
171
|
+
// console.log('Chart rendered successfully');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
this.isLoading = false;
|
|
175
|
+
this.cdr.markForCheck();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private updateChartWithData(): void {
|
|
179
|
+
this.processData(); // Process without updating yet
|
|
180
|
+
if (this.chartInstance) {
|
|
181
|
+
const updatedOptions: Partial<echarts.EChartsOption> = {
|
|
182
|
+
xAxis: { data: this.getProcessedCategories() },
|
|
183
|
+
series: this.getProcessedValues(),
|
|
184
|
+
};
|
|
185
|
+
this.chartInstance.setOption(updatedOptions, {
|
|
186
|
+
notMerge: false,
|
|
187
|
+
lazyUpdate: true,
|
|
188
|
+
silent: false,
|
|
189
|
+
replaceMerge: ['series'],
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private processedData: any[][] = [];
|
|
195
|
+
private processedColumns: string[] = [];
|
|
196
|
+
private getProcessedCategories(): string[] {
|
|
197
|
+
return this.processedData.map((d) => d[0]);
|
|
198
|
+
}
|
|
199
|
+
private getProcessedValues(): any[] {
|
|
200
|
+
const columnData = this.processedColumns.slice(1);
|
|
201
|
+
return columnData.map((c, i) => {
|
|
202
|
+
const seriesData = this.processedData.map((d) => d[i + 1]);
|
|
203
|
+
return {
|
|
204
|
+
name: c,
|
|
205
|
+
type: 'line',
|
|
206
|
+
data: seriesData, // Populated dynamically
|
|
207
|
+
large: true, // Enable large mode: Canvas rendering for 50k+ efficiency
|
|
208
|
+
largeThreshold: 5000, // Start large mode at 5k points
|
|
209
|
+
progressive: 300, // Render 300 points per chunk (tune for your hardware)
|
|
210
|
+
progressiveChunkMode: 'sequential', // Sequential rendering for smooth init
|
|
211
|
+
progressiveThreshold: 5000, // Apply progressive above 5k
|
|
212
|
+
animation: true, // Disable initial animation for faster load on large data (re-enable for updates)
|
|
213
|
+
itemStyle: {
|
|
214
|
+
// color: '#5470c6', // line color
|
|
215
|
+
// shadowBlur: 0, // Reduce shadows for perf
|
|
216
|
+
},
|
|
217
|
+
areaStyle: { opacity: 0.2 },
|
|
218
|
+
// emphasis: {
|
|
219
|
+
// // Hover highlight (lightweight)
|
|
220
|
+
// focus: 'series',
|
|
221
|
+
// itemStyle: { opacity: 0.8 },
|
|
222
|
+
// },
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
private processData(): void {
|
|
227
|
+
let dataToProcess = this.currentData;
|
|
228
|
+
if (this.enableSampling && this.currentData.length > this.sampleThreshold) {
|
|
229
|
+
const step = Math.ceil(this.currentData.length / this.sampleThreshold);
|
|
230
|
+
dataToProcess = this.currentData.filter((_, index) => index % step === 0);
|
|
231
|
+
console.warn(
|
|
232
|
+
`Sampled data from ${this.currentData.length} to ${dataToProcess.length} points.`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
this.processedData = dataToProcess; // Cache processed
|
|
236
|
+
this.processedColumns = this.currentColumns;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private processAndUpdateData(): void {
|
|
240
|
+
this.processData();
|
|
241
|
+
if (this.chartInstance) {
|
|
242
|
+
this.updateChartWithData();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private handleResize(): void {
|
|
247
|
+
if (this.chartInstance) {
|
|
248
|
+
this.chartInstance.resize();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private disposeChart(): void {
|
|
253
|
+
if (this.chartInstance) {
|
|
254
|
+
this.chartInstance.dispose();
|
|
255
|
+
this.chartInstance = null;
|
|
256
|
+
// console.log('ECharts instance disposed');
|
|
257
|
+
}
|
|
258
|
+
window.removeEventListener('resize', this.handleResize.bind(this));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Public method: Update data externally (e.g., from API)
|
|
262
|
+
updateData(newData: any[][]): void {
|
|
263
|
+
this.currentData = newData;
|
|
264
|
+
this.processAndUpdateData();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div #barChartContainer style="width: 100%; height: 100%"></div>
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { BarChart } from './bar-chart';
|
|
4
|
+
|
|
5
|
+
describe('BarChart', () => {
|
|
6
|
+
let component: BarChart;
|
|
7
|
+
let fixture: ComponentFixture<BarChart>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [BarChart]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(BarChart);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|