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