cats-charts 0.0.6 → 0.0.8

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/README.md CHANGED
@@ -1,63 +1,59 @@
1
- # ChartsLib
1
+ # CATS4U Charts
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ This library was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0 and it also usages [Apache Echarts](https://www.npmjs.com/package/echarts) for charts.
4
4
 
5
- ## Code scaffolding
5
+ ## Install
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
-
9
- ```bash
10
- ng generate component component-name
11
7
  ```
12
-
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
-
15
- ```bash
16
- ng generate --help
8
+ npm install cats-charts
17
9
  ```
18
10
 
19
- ## Building
11
+ ## Usages
20
12
 
21
- To build the library, run:
13
+ ### TS file
22
14
 
23
- ```bash
24
- ng build charts-lib
15
+ ```
16
+ import { ChartsLib } from 'cats-charts';
25
17
  ```
26
18
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
-
29
- ### Publishing the Library
19
+ ### HTML template
30
20
 
31
- Once the project is built, you can publish your library by following these steps:
21
+ ```
22
+ <lib-charts-lib></lib-charts-lib>
23
+ ```
32
24
 
33
- 1. Navigate to the `dist` directory:
34
- ```bash
35
- cd dist/charts-lib
36
- ```
25
+ ### Inputs
37
26
 
38
- 2. Run the `npm publish` command to publish your library to the npm registry:
39
- ```bash
40
- npm publish
41
- ```
27
+ ```
28
+ chartOptionsConfig: {} // accepts all options accepted by echarts
29
+ chartType: 'bar' | 'area' | 'line' | 'pie' // by default chart type is bar
30
+ columns: [] // list of columns
31
+ contextMenuList: [] // list of context menu
32
+ data: [] // list of set of data
33
+ themeName: "default" | "dark" | "vintage" | "essos" | "chalk" | "roma" // default theme is default
42
34
 
43
- ## Running unit tests
35
+ ```
44
36
 
45
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
37
+ ### Outputs
46
38
 
47
- ```bash
48
- ng test
39
+ ```
40
+ handleSingleClick: (params) => {}
41
+ handleDrillBy: (params) => {}
49
42
  ```
50
43
 
51
- ## Running end-to-end tests
52
-
53
- For end-to-end (e2e) testing, run:
44
+ ### Inputs Types
54
45
 
55
- ```bash
56
- ng e2e
57
46
  ```
58
-
59
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
47
+ import type { ChartsLibType, OptionsConfig } from 'cats-charts';
48
+
49
+ chartOptionsConfig: OptionsConfig
50
+ chartType: ChartsLibType['chartType']
51
+ columns: string[]
52
+ data: any[][]
53
+ themeName: ChartsLibType['themeName']
54
+ contextMenuList: ContextMenuListItem[]
55
+ ```
60
56
 
61
57
  ## Additional Resources
62
58
 
63
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
59
+ For more information on Echarts, including detailed references, visit the [Official Apache Echarts](https://echarts.apache.org/en/api.html#echarts) page.
@@ -66,6 +66,22 @@ class ContextMenuListItem {
66
66
  action;
67
67
  disabled = false;
68
68
  }
69
+ class PieChartOptionsConfig {
70
+ title = {
71
+ show: true,
72
+ text: '',
73
+ left: 'center',
74
+ textStyle: { fontSize: 16 },
75
+ };
76
+ legend = { show: true };
77
+ tooltip = { trigger: 'item' };
78
+ series = [];
79
+ grid = {
80
+ left: '5%',
81
+ right: '5%',
82
+ containLabel: true,
83
+ };
84
+ }
69
85
 
70
86
  class ChartService {
71
87
  themeSignal = signal('default', ...(ngDevMode ? [{ debugName: "themeSignal" }] : []));
@@ -3338,6 +3354,250 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3338
3354
  args: ['areaChartContainer', { static: false }]
3339
3355
  }] } });
3340
3356
 
3357
+ class PieChart {
3358
+ cdr;
3359
+ title = '';
3360
+ height = '600px';
3361
+ enableSampling = true; // Sample data if > threshold for perf
3362
+ sampleThreshold = 10000; // Sample if data > 10k (keep full for 50k if hardware allows)
3363
+ isLoading = true;
3364
+ defaultConfig = new PieChartOptionsConfig();
3365
+ optionsConfig = {};
3366
+ chartContainer;
3367
+ chartInstance = null;
3368
+ chartService = inject(ChartService);
3369
+ destroy$ = new Subject();
3370
+ currentData = [];
3371
+ currentColumns = [];
3372
+ boundHandleContextMenu;
3373
+ constructor(cdr) {
3374
+ this.cdr = cdr;
3375
+ this.boundHandleContextMenu = this.handleContextMenu.bind(this);
3376
+ // Effect for theme changes
3377
+ effect(() => {
3378
+ this.chartService.theme(); // Read signal
3379
+ if (this.chartInstance) {
3380
+ this.reinitializeChart();
3381
+ }
3382
+ });
3383
+ // Effect for data changes
3384
+ effect(() => {
3385
+ const data = this.chartService.data(); // Read signal
3386
+ const columns = this.chartService.columns();
3387
+ this.currentColumns = columns;
3388
+ this.currentData = data;
3389
+ this.processAndUpdateData();
3390
+ });
3391
+ // Effect for chartOptionsConfig changes
3392
+ effect(() => {
3393
+ this.chartService.chartOptionsConfig();
3394
+ this.optionsConfig = this.chartService.chartOptionsConfig();
3395
+ this.reinitializeChart();
3396
+ });
3397
+ }
3398
+ ngOnInit() {
3399
+ if (this.currentData.length > 0) {
3400
+ this.processAndUpdateData();
3401
+ }
3402
+ }
3403
+ ngAfterViewInit() {
3404
+ this.initializeChart();
3405
+ if (this.currentData.length > 0) {
3406
+ this.updateChartWithData();
3407
+ }
3408
+ this.isLoading = false;
3409
+ this.cdr.markForCheck();
3410
+ }
3411
+ ngOnDestroy() {
3412
+ this.destroy$.next();
3413
+ this.destroy$.complete();
3414
+ this.chartService.hideContextMenu();
3415
+ this.disposeChart();
3416
+ if (this.chartInstance) {
3417
+ this.chartInstance.dispose(); // Clean up to free memory
3418
+ }
3419
+ if (this.chartContainer) {
3420
+ this.chartContainer.nativeElement.removeEventListener('contextmenu', this.boundHandleContextMenu);
3421
+ }
3422
+ document.removeEventListener('click', () => {
3423
+ this.chartService.hideContextMenu();
3424
+ this.chartService.resetContextEvent();
3425
+ });
3426
+ }
3427
+ reinitializeChart() {
3428
+ this.initializeChart();
3429
+ if (this.currentData.length > 0) {
3430
+ this.updateChartWithData();
3431
+ }
3432
+ this.cdr.markForCheck();
3433
+ }
3434
+ initializeChart() {
3435
+ if (!this.chartContainer) {
3436
+ return;
3437
+ }
3438
+ const chartDom = this.chartContainer.nativeElement;
3439
+ chartDom.innerHTML = '';
3440
+ if (this.chartInstance) {
3441
+ this.chartInstance.dispose();
3442
+ this.chartInstance = null;
3443
+ }
3444
+ this.chartInstance = echarts.init(chartDom, this.chartService.theme()); // Init ECharts
3445
+ this.chartInstance.setOption({
3446
+ ...this.defaultConfig,
3447
+ ...this.optionsConfig,
3448
+ title: {
3449
+ ...this.defaultConfig.title,
3450
+ ...this.optionsConfig.title,
3451
+ textStyle: {
3452
+ ...this.defaultConfig.title?.textStyle,
3453
+ ...this.optionsConfig.title?.textStyle,
3454
+ },
3455
+ },
3456
+ tooltip: {
3457
+ ...this.defaultConfig.tooltip,
3458
+ ...this.optionsConfig.tooltip,
3459
+ },
3460
+ legend: { ...this.defaultConfig.legend, ...this.optionsConfig.legend },
3461
+ // No dataZoom for pie charts
3462
+ grid: {
3463
+ ...this.defaultConfig.grid,
3464
+ ...this.optionsConfig.grid,
3465
+ },
3466
+ // No xAxis or yAxis for pie charts
3467
+ series: [],
3468
+ });
3469
+ // Responsive: Resize on window change
3470
+ window.addEventListener('resize', () => this.chartInstance?.resize(), { passive: true });
3471
+ this.chartInstance.on('rendered', () => {
3472
+ // console.log('Chart rendered successfully');
3473
+ });
3474
+ this.chartInstance.on('click', (params) => {
3475
+ this.chartService.hideContextMenu();
3476
+ this.chartService.resetContextEvent();
3477
+ this.chartService.handleClick(params);
3478
+ });
3479
+ this.chartInstance.on('contextmenu', (params) => {
3480
+ this.chartService.openContextMenu(params, this.chartContainer);
3481
+ });
3482
+ chartDom.addEventListener('contextmenu', this.boundHandleContextMenu);
3483
+ document.addEventListener('click', () => {
3484
+ this.chartService.hideContextMenu();
3485
+ this.chartService.resetContextEvent();
3486
+ }, {
3487
+ passive: true,
3488
+ });
3489
+ this.isLoading = false;
3490
+ this.cdr.markForCheck();
3491
+ }
3492
+ handleContextMenu(event) {
3493
+ event.preventDefault();
3494
+ setTimeout(() => {
3495
+ if (!this.chartService.contextMenuEvent().seriesName) {
3496
+ this.chartService.openContextMenu(null, this.chartContainer, event);
3497
+ }
3498
+ }, 0);
3499
+ }
3500
+ updateChartWithData() {
3501
+ this.processData(); // Process without updating yet
3502
+ if (this.chartInstance) {
3503
+ const updatedOptions = {
3504
+ series: this.getProcessedValues(),
3505
+ };
3506
+ this.chartInstance.setOption(updatedOptions, {
3507
+ notMerge: false,
3508
+ lazyUpdate: true,
3509
+ silent: false,
3510
+ replaceMerge: ['series'],
3511
+ });
3512
+ }
3513
+ }
3514
+ processedData = [];
3515
+ processedColumns = [];
3516
+ getProcessedValues() {
3517
+ // For pie chart, assume single series using the first value column (index 1)
3518
+ // If multiple value columns, you could sum them or create multiple pies, but here we use the first one
3519
+ const columnList = [...this.processedColumns];
3520
+ columnList.shift();
3521
+ const seriesData = columnList.map((column, index) => ({
3522
+ name: column, // Category from first column
3523
+ value: this.processedData[0][index + 1] || 0, // Value from second column
3524
+ }));
3525
+ return [
3526
+ {
3527
+ name: `${this.processedColumns[0]}: ${this.processedData[0][0]}`,
3528
+ type: 'pie',
3529
+ data: seriesData,
3530
+ radius: '50%', // Default radius, can be customized via optionsConfig
3531
+ // Add other pie-specific options as needed
3532
+ itemStyle: {
3533
+ // color: '#5470c6', // Slice color, can be customized
3534
+ },
3535
+ emphasis: {
3536
+ itemStyle: {
3537
+ shadowBlur: 10,
3538
+ shadowOffsetX: 0,
3539
+ shadowColor: 'rgba(0, 0, 0, 0.5)',
3540
+ },
3541
+ },
3542
+ animation: true,
3543
+ },
3544
+ ];
3545
+ }
3546
+ processData() {
3547
+ let dataToProcess = this.currentData;
3548
+ // if (this.enableSampling && this.currentData.length > this.sampleThreshold) {
3549
+ // const step = Math.ceil(this.currentData.length / this.sampleThreshold);
3550
+ // dataToProcess = this.currentData.filter((_, index) => index % step === 0);
3551
+ // }
3552
+ this.processedData = dataToProcess; // Cache processed
3553
+ this.processedColumns = this.currentColumns;
3554
+ }
3555
+ processAndUpdateData() {
3556
+ this.processData();
3557
+ if (this.chartInstance) {
3558
+ this.updateChartWithData();
3559
+ }
3560
+ }
3561
+ handleResize() {
3562
+ if (this.chartInstance) {
3563
+ this.chartInstance.resize();
3564
+ }
3565
+ }
3566
+ disposeChart() {
3567
+ if (this.chartInstance) {
3568
+ this.chartInstance.dispose();
3569
+ this.chartInstance.off('click');
3570
+ this.chartInstance = null;
3571
+ // console.log('ECharts instance disposed');
3572
+ }
3573
+ window.removeEventListener('resize', this.handleResize.bind(this));
3574
+ }
3575
+ // Public method: Update data externally (e.g., from API)
3576
+ updateData(newData) {
3577
+ this.currentData = newData;
3578
+ this.processAndUpdateData();
3579
+ }
3580
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PieChart, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3581
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PieChart, isStandalone: true, selector: "lib-pie-chart", inputs: { title: "title", height: "height", enableSampling: "enableSampling", sampleThreshold: "sampleThreshold", isLoading: "isLoading" }, viewQueries: [{ propertyName: "chartContainer", first: true, predicate: ["pieChartContainer"], descendants: true }], ngImport: i0, template: "<div #pieChartContainer style=\"width: 100%; height: 100%\"></div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3582
+ }
3583
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PieChart, decorators: [{
3584
+ type: Component,
3585
+ args: [{ selector: 'lib-pie-chart', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #pieChartContainer style=\"width: 100%; height: 100%\"></div>\n" }]
3586
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { title: [{
3587
+ type: Input
3588
+ }], height: [{
3589
+ type: Input
3590
+ }], enableSampling: [{
3591
+ type: Input
3592
+ }], sampleThreshold: [{
3593
+ type: Input
3594
+ }], isLoading: [{
3595
+ type: Input
3596
+ }], chartContainer: [{
3597
+ type: ViewChild,
3598
+ args: ['pieChartContainer', { static: false }]
3599
+ }] } });
3600
+
3341
3601
  class ChartsLib {
3342
3602
  chartService;
3343
3603
  differs;
@@ -3417,6 +3677,8 @@ class ChartsLib {
3417
3677
  return LineChart;
3418
3678
  case 'area':
3419
3679
  return AreaChart;
3680
+ case 'pie':
3681
+ return PieChart;
3420
3682
  default:
3421
3683
  return null;
3422
3684
  }
@@ -3457,5 +3719,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3457
3719
  * Generated bundle index. Do not edit.
3458
3720
  */
3459
3721
 
3460
- export { ChartsLib, ChartsLibType, ClickEvent, ContextMenuListItem, OptionsConfig };
3722
+ export { ChartsLib, ChartsLibType, ClickEvent, ContextMenuListItem, OptionsConfig, PieChartOptionsConfig };
3461
3723
  //# sourceMappingURL=cats-charts.mjs.map