@visionaris-bruno/vs-echarts 3.0.0 → 3.3.0

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.
@@ -9,236 +9,6 @@ import { ReplaySubject } from 'rxjs';
9
9
  import { debounceTime } from 'rxjs/operators';
10
10
  import { CommonModule } from '@angular/common';
11
11
 
12
- /**
13
- * Inicialización centralizada de ECharts para evitar duplicación y efectos secundarios
14
- * no controlados en componentes standalone.
15
- */
16
- let initialized = false;
17
- function initializeEcharts() {
18
- if (initialized)
19
- return;
20
- echarts.use([
21
- BarChart,
22
- PieChart,
23
- LineChart,
24
- TitleComponent,
25
- TooltipComponent,
26
- GridComponent,
27
- LegendComponent,
28
- CanvasRenderer,
29
- GraphicComponent,
30
- ToolboxComponent,
31
- ]);
32
- initialized = true;
33
- }
34
- /**
35
- * Provee la configuración de ECharts para ngx-echarts.
36
- * Llama automáticamente a la inicialización de módulos.
37
- */
38
- function provideVSEcharts() {
39
- initializeEcharts();
40
- return provideEchartsCore({ echarts });
41
- }
42
- function defaultOptionsOverrides() {
43
- return {
44
- line: {
45
- series: {
46
- smooth: true,
47
- areaStyle: undefined,
48
- stack: undefined,
49
- },
50
- },
51
- area: {
52
- series: {
53
- smooth: true,
54
- areaStyle: {},
55
- stack: undefined,
56
- },
57
- },
58
- areaStack: {
59
- series: {
60
- smooth: true,
61
- areaStyle: {},
62
- stack: 'total',
63
- },
64
- },
65
- };
66
- }
67
-
68
- /**
69
- * BaseEchartsComponent
70
- *
71
- * Clase base para componentes especialistas de ECharts.
72
- * Gestiona el ciclo de vida, redimensionado dinámico y debouncing de actualizaciones.
73
- *
74
- * @see {@link vs-echarts/docs/internal-architecture.md}
75
- */
76
- class BaseEchartsComponent {
77
- chartContainer;
78
- /** Datos normalizados para graficar */
79
- data = { categories: [], series: [] };
80
- optionsOverrides = defaultOptionsOverrides();
81
- /** Paleta de colores básica */
82
- palette;
83
- /** Resolver de colores dinámico (Callback) */
84
- colorResolver;
85
- /** Formateador de valores para etiquetas y tooltips */
86
- valueFormatter;
87
- chartClick = new EventEmitter();
88
- /** Opciones configuradas para ngx-echarts */
89
- chartOptions = {};
90
- /** Subject para debouncing de actualizaciones. ReplaySubject asegura no perder el primer renderizado. */
91
- updateSubject = new ReplaySubject(1);
92
- updateSubscription;
93
- constructor() {
94
- this.updateSubscription = this.updateSubject.pipe(debounceTime(150)).subscribe(options => {
95
- this.updateOptions(options);
96
- });
97
- }
98
- chartInstance;
99
- currentLegendSelected = null;
100
- /** Estado de selección para filtros cruzados */
101
- selectedCategoryIndex = null;
102
- selectedSeriesIndex = null;
103
- ngOnChanges(changes) {
104
- this.onInputChanges(changes);
105
- }
106
- ngOnDestroy() {
107
- this.updateSubscription?.unsubscribe();
108
- this.chartInstance?.dispose();
109
- }
110
- /**
111
- * Captura la instancia de echarts (usado por ngx-echarts)
112
- */
113
- onChartInit(instance) {
114
- this.chartInstance = instance;
115
- this.setupBaseInteractions();
116
- this.updateChartOptions();
117
- }
118
- /**
119
- * Configura interacciones base como el clic en el fondo para limpiar selección.
120
- */
121
- setupBaseInteractions() {
122
- if (!this.chartInstance)
123
- return;
124
- const zr = this.chartInstance.getZr();
125
- zr.on('click', (params) => {
126
- if (!params.target) {
127
- this.clearSelection();
128
- }
129
- });
130
- }
131
- onChartClick(event) {
132
- if (event.componentType === 'series') {
133
- const index = event.dataIndex;
134
- const seriesIndex = event.seriesIndex;
135
- if (index !== undefined && index >= 0) {
136
- this.handleSelection(index, seriesIndex);
137
- // Emit standardized payload for cross-filtering
138
- this.chartClick.emit({
139
- type: 'cross-filter',
140
- data: {
141
- category: event.name,
142
- serie: event.seriesName,
143
- value: event.value
144
- }
145
- });
146
- }
147
- }
148
- }
149
- /**
150
- * Maneja la lógica de alternancia de selección.
151
- */
152
- handleSelection(dataIndex, seriesIndex) {
153
- const isSamePoint = this.selectedCategoryIndex === dataIndex && this.selectedSeriesIndex === seriesIndex;
154
- if (isSamePoint) {
155
- this.clearSelection();
156
- }
157
- else {
158
- this.selectedCategoryIndex = dataIndex;
159
- this.selectedSeriesIndex = seriesIndex;
160
- this.updateChartOptions();
161
- }
162
- }
163
- /**
164
- * Limpia el estado de selección actual.
165
- */
166
- clearSelection() {
167
- this.selectedCategoryIndex = null;
168
- this.selectedSeriesIndex = null;
169
- this.updateChartOptions();
170
- }
171
- dispatchAction(type, d) {
172
- if (this.chartInstance == undefined)
173
- return;
174
- const { seriesIndex = [], dataIndex = [], } = d;
175
- this.chartInstance.dispatchAction({
176
- type,
177
- seriesIndex,
178
- dataIndex,
179
- });
180
- }
181
- /**
182
- * Maneja clics en la leyenda para persistir filtros.
183
- */
184
- onLegendSelectChanged(event) {
185
- if (event && event.selected) {
186
- this.currentLegendSelected = event.selected;
187
- this.updateChartOptions();
188
- }
189
- }
190
- updateOptions(options) {
191
- this.chartOptions = { ...options };
192
- this.chartInstance?.setOption(options, { notMerge: true });
193
- }
194
- /**
195
- * Hook de template method invocado por `ngOnChanges`.
196
- * Las subclases lo sobreescriben para añadir lógica propia
197
- * y deben llamar a `super.onInputChanges(changes)` para preservar
198
- * el comportamiento base (detectar inputs relevantes y actualizar el chart).
199
- */
200
- onInputChanges(changes) {
201
- if (changes['data'] || changes['optionsOverrides'] || changes['palette'] || changes['colorResolver'] || changes['valueFormatter']) {
202
- this.updateChartOptions();
203
- }
204
- }
205
- /**
206
- * Gatilla actualización en ngx-echarts
207
- */
208
- triggerUpdate(options) {
209
- this.updateSubject.next(options);
210
- }
211
- /**
212
- * Método público para forzar el redimensionado desde el padre
213
- */
214
- resize() {
215
- this.chartInstance?.resize();
216
- }
217
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseEchartsComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
218
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: BaseEchartsComponent, isStandalone: true, inputs: { data: "data", optionsOverrides: "optionsOverrides", palette: "palette", colorResolver: "colorResolver", valueFormatter: "valueFormatter" }, outputs: { chartClick: "chartClick" }, viewQueries: [{ propertyName: "chartContainer", first: true, predicate: ["chartContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0 });
219
- }
220
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseEchartsComponent, decorators: [{
221
- type: Directive,
222
- args: [{
223
- standalone: true
224
- }]
225
- }], ctorParameters: () => [], propDecorators: { chartContainer: [{
226
- type: ViewChild,
227
- args: ['chartContainer', { static: true }]
228
- }], data: [{
229
- type: Input
230
- }], optionsOverrides: [{
231
- type: Input
232
- }], palette: [{
233
- type: Input
234
- }], colorResolver: [{
235
- type: Input
236
- }], valueFormatter: [{
237
- type: Input
238
- }], chartClick: [{
239
- type: Output
240
- }] } });
241
-
242
12
  /**
243
13
  * ECharts Options Standard Library
244
14
  *
@@ -304,21 +74,23 @@ function getLegendOptions(overrides) {
304
74
  };
305
75
  return { ...defaults, ...overrides };
306
76
  }
307
- /**
308
- * Common XAxis configuration for category-based charts.
309
- * Atomic Pattern: Handles category mapping and adaptive label rotation.
310
- */
311
- function getXAxisOptions(overrides) {
312
- const categories = overrides?.data || [];
77
+ function getCategoryAxisOptions(overrides) {
78
+ const categories = overrides?.data ?? [];
313
79
  const autoRotate = categories.length > 10 ? 45 : 0;
314
- const defaults = {
80
+ const axisOpts = {
315
81
  type: 'category',
316
82
  triggerEvent: true,
83
+ data: categories,
317
84
  axisLabel: {
318
85
  rotate: autoRotate,
319
86
  interval: 0,
320
- color: EChartsTokens.axisColor,
321
- fontSize: EChartsTokens.fontSize
87
+ fontFamily: overrides?.axisLabel?.fontFamily ?? EChartsTokens.fontFamily,
88
+ color: overrides?.axisLabel?.color ?? EChartsTokens.axisColor,
89
+ fontSize: overrides?.axisLabel?.fontSize ?? EChartsTokens.fontSize,
90
+ margin: overrides?.axisLabel?.margin ?? 8,
91
+ fontWeight: overrides?.axisLabel?.fontWeight ?? 'normal',
92
+ overflow: 'break',
93
+ width: overrides?.axisLabel?.width ?? 1000,
322
94
  },
323
95
  axisLine: {
324
96
  lineStyle: {
@@ -326,16 +98,38 @@ function getXAxisOptions(overrides) {
326
98
  }
327
99
  }
328
100
  };
329
- // Deep merge for axisLabel if provided
101
+ return axisOpts;
102
+ }
103
+ /**
104
+ *
105
+ *TODO: mejorar sistema de overrides
106
+ */
107
+ function getValueAxisOptions(overrides) {
108
+ const defaults = {
109
+ type: 'value',
110
+ splitLine: {
111
+ show: true,
112
+ lineStyle: {
113
+ type: 'dashed',
114
+ color: '#f0f0f0'
115
+ }
116
+ },
117
+ axisLabel: {
118
+ formatter: (value) => formatAxisValue(value),
119
+ color: EChartsTokens.axisColor,
120
+ fontSize: EChartsTokens.fontSize
121
+ }
122
+ };
330
123
  const result = { ...defaults, ...overrides };
331
- if (overrides?.axisLabel) {
332
- result.axisLabel = { ...defaults.axisLabel, ...overrides.axisLabel };
124
+ if (overrides?.splitLine) {
125
+ result.splitLine = { ...defaults.splitLine, ...overrides.splitLine };
333
126
  }
334
127
  return result;
335
128
  }
336
129
  /**
337
- * Common YAxis configuration for value-based charts.
338
- * Atomic Pattern: Standardizes value formatting and grid lines.
130
+ *
131
+ *TODO: Eliminar funcion. Se debe eliminar la funcion y reemplazarla por getValueAxis o
132
+ * getCategoryAxis segun corresponda.
339
133
  */
340
134
  function getYAxisOptions(overrides) {
341
135
  const defaults = {
@@ -458,6 +252,277 @@ function applyLineSelectionStyle(series, selectedCategoryIndex, selectedSeriesIn
458
252
  };
459
253
  });
460
254
  }
255
+ /**
256
+ * Formatter generator for 'item' trigger strategy in tooltips.
257
+ */
258
+ function getItemTooltipFormatter(data, formatCellValue) {
259
+ return (params) => {
260
+ const seriesObj = data.series[params.seriesIndex];
261
+ const key = seriesObj?.originalKey || (data.categoryKeys ? data.categoryKeys[params.dataIndex] : String(params.dataIndex));
262
+ const valFormatted = formatCellValue(params.value, key);
263
+ return `<b>${params.name}</b><br/>${params.marker} ${params.seriesName}: <b>${valFormatted}</b><br/>`;
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Inicialización centralizada de ECharts para evitar duplicación y efectos secundarios
269
+ * no controlados en componentes standalone.
270
+ */
271
+ let initialized = false;
272
+ function initializeEcharts() {
273
+ if (initialized)
274
+ return;
275
+ echarts.use([
276
+ BarChart,
277
+ PieChart,
278
+ LineChart,
279
+ TitleComponent,
280
+ TooltipComponent,
281
+ GridComponent,
282
+ LegendComponent,
283
+ CanvasRenderer,
284
+ GraphicComponent,
285
+ ToolboxComponent,
286
+ ]);
287
+ initialized = true;
288
+ }
289
+ /**
290
+ * Provee la configuración de ECharts para ngx-echarts.
291
+ * Llama automáticamente a la inicialización de módulos.
292
+ */
293
+ function provideVSEcharts() {
294
+ initializeEcharts();
295
+ return provideEchartsCore({ echarts });
296
+ }
297
+ function defaultOptionsOverrides() {
298
+ return {
299
+ mainAxis: {
300
+ categoryAxis: [
301
+ {
302
+ axisLabel: {
303
+ fontFamily: EChartsTokens.fontFamily,
304
+ width: 100,
305
+ fontSize: 9,
306
+ margin: 12,
307
+ color: EChartsTokens.axisColor,
308
+ fontWeight: 'normal',
309
+ }
310
+ }
311
+ ]
312
+ },
313
+ line: {
314
+ series: {
315
+ smooth: true,
316
+ areaStyle: undefined,
317
+ stack: undefined,
318
+ },
319
+ },
320
+ area: {
321
+ series: {
322
+ smooth: true,
323
+ areaStyle: {},
324
+ stack: undefined,
325
+ },
326
+ },
327
+ areaStack: {
328
+ series: {
329
+ smooth: true,
330
+ areaStyle: {},
331
+ stack: 'total',
332
+ },
333
+ },
334
+ bar: {
335
+ orientation: 'v',
336
+ series: {
337
+ itemStyle: {
338
+ borderRadius: [0, 4, 4, 0],
339
+ }
340
+ },
341
+ },
342
+ hBar: {
343
+ orientation: 'h',
344
+ series: {
345
+ itemStyle: {
346
+ borderRadius: [0, 4, 4, 0],
347
+ }
348
+ }
349
+ },
350
+ };
351
+ }
352
+
353
+ /**
354
+ * BaseEchartsComponent
355
+ *
356
+ * Clase base para componentes especialistas de ECharts.
357
+ * Gestiona el ciclo de vida, redimensionado dinámico y debouncing de actualizaciones.
358
+ *
359
+ * @see {@link vs-echarts/docs/internal-architecture.md}
360
+ */
361
+ class BaseEchartsComponent {
362
+ chartContainer;
363
+ /** Datos normalizados para graficar */
364
+ data = { categories: [], series: [] };
365
+ optionsOverrides = defaultOptionsOverrides();
366
+ /** Paleta de colores básica */
367
+ palette;
368
+ /** Resolver de colores dinámico (Callback) */
369
+ colorResolver;
370
+ /** Formateador de valores para etiquetas y tooltips */
371
+ valueFormatter;
372
+ chartClick = new EventEmitter();
373
+ /** Opciones configuradas para ngx-echarts */
374
+ chartOptions = {};
375
+ /** Subject para debouncing de actualizaciones. ReplaySubject asegura no perder el primer renderizado. */
376
+ updateSubject = new ReplaySubject(1);
377
+ updateSubscription;
378
+ constructor() {
379
+ this.updateSubscription = this.updateSubject.pipe(debounceTime(150)).subscribe(options => {
380
+ this.updateOptions(options);
381
+ });
382
+ }
383
+ chartInstance;
384
+ currentLegendSelected = null;
385
+ /** Estado de selección para filtros cruzados */
386
+ selectedCategoryIndex = null;
387
+ selectedSeriesIndex = null;
388
+ ngOnChanges(changes) {
389
+ this.onInputChanges(changes);
390
+ }
391
+ ngOnDestroy() {
392
+ this.updateSubscription?.unsubscribe();
393
+ this.chartInstance?.dispose();
394
+ }
395
+ /**
396
+ * Captura la instancia de echarts (usado por ngx-echarts)
397
+ */
398
+ onChartInit(instance) {
399
+ this.chartInstance = instance;
400
+ this.setupBaseInteractions();
401
+ this.updateChartOptions();
402
+ }
403
+ /**
404
+ * Configura interacciones base como el clic en el fondo para limpiar selección.
405
+ */
406
+ setupBaseInteractions() {
407
+ if (!this.chartInstance)
408
+ return;
409
+ const zr = this.chartInstance.getZr();
410
+ zr.on('click', (params) => {
411
+ if (!params.target) {
412
+ this.clearSelection();
413
+ }
414
+ });
415
+ }
416
+ onChartClick(event) {
417
+ if (event.componentType === 'series') {
418
+ const index = event.dataIndex;
419
+ const seriesIndex = event.seriesIndex;
420
+ if (index !== undefined && index >= 0) {
421
+ this.handleSelection(index, seriesIndex);
422
+ // Emit standardized payload for cross-filtering
423
+ this.chartClick.emit({
424
+ type: 'cross-filter',
425
+ data: {
426
+ category: event.name,
427
+ serie: event.seriesName,
428
+ value: event.value
429
+ }
430
+ });
431
+ }
432
+ }
433
+ }
434
+ /**
435
+ * Maneja la lógica de alternancia de selección.
436
+ */
437
+ handleSelection(dataIndex, seriesIndex) {
438
+ const isSamePoint = this.selectedCategoryIndex === dataIndex && this.selectedSeriesIndex === seriesIndex;
439
+ if (isSamePoint) {
440
+ this.clearSelection();
441
+ }
442
+ else {
443
+ this.selectedCategoryIndex = dataIndex;
444
+ this.selectedSeriesIndex = seriesIndex;
445
+ this.updateChartOptions();
446
+ }
447
+ }
448
+ /**
449
+ * Limpia el estado de selección actual.
450
+ */
451
+ clearSelection() {
452
+ this.selectedCategoryIndex = null;
453
+ this.selectedSeriesIndex = null;
454
+ this.updateChartOptions();
455
+ }
456
+ dispatchAction(type, d) {
457
+ if (this.chartInstance == undefined)
458
+ return;
459
+ const { seriesIndex = [], dataIndex = [], } = d;
460
+ this.chartInstance.dispatchAction({
461
+ type,
462
+ seriesIndex,
463
+ dataIndex,
464
+ });
465
+ }
466
+ /**
467
+ * Maneja clics en la leyenda para persistir filtros.
468
+ */
469
+ onLegendSelectChanged(event) {
470
+ if (event && event.selected) {
471
+ this.currentLegendSelected = event.selected;
472
+ this.updateChartOptions();
473
+ }
474
+ }
475
+ updateOptions(options) {
476
+ this.chartOptions = { ...options };
477
+ this.chartInstance?.setOption(options, { notMerge: true });
478
+ }
479
+ /**
480
+ * Hook de template method invocado por `ngOnChanges`.
481
+ * Las subclases lo sobreescriben para añadir lógica propia
482
+ * y deben llamar a `super.onInputChanges(changes)` para preservar
483
+ * el comportamiento base (detectar inputs relevantes y actualizar el chart).
484
+ */
485
+ onInputChanges(changes) {
486
+ if (changes['data'] || changes['optionsOverrides'] || changes['palette'] || changes['colorResolver'] || changes['valueFormatter']) {
487
+ this.updateChartOptions();
488
+ }
489
+ }
490
+ /**
491
+ * Gatilla actualización en ngx-echarts
492
+ */
493
+ triggerUpdate(options) {
494
+ this.updateSubject.next(options);
495
+ }
496
+ /**
497
+ * Método público para forzar el redimensionado desde el padre
498
+ */
499
+ resize() {
500
+ this.chartInstance?.resize();
501
+ }
502
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseEchartsComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
503
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: BaseEchartsComponent, isStandalone: true, inputs: { data: "data", optionsOverrides: "optionsOverrides", palette: "palette", colorResolver: "colorResolver", valueFormatter: "valueFormatter" }, outputs: { chartClick: "chartClick" }, viewQueries: [{ propertyName: "chartContainer", first: true, predicate: ["chartContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0 });
504
+ }
505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseEchartsComponent, decorators: [{
506
+ type: Directive,
507
+ args: [{
508
+ standalone: true
509
+ }]
510
+ }], ctorParameters: () => [], propDecorators: { chartContainer: [{
511
+ type: ViewChild,
512
+ args: ['chartContainer', { static: true }]
513
+ }], data: [{
514
+ type: Input
515
+ }], optionsOverrides: [{
516
+ type: Input
517
+ }], palette: [{
518
+ type: Input
519
+ }], colorResolver: [{
520
+ type: Input
521
+ }], valueFormatter: [{
522
+ type: Input
523
+ }], chartClick: [{
524
+ type: Output
525
+ }] } });
461
526
 
462
527
  /**
463
528
  * BaseEchartBuilder
@@ -843,6 +908,10 @@ class VerticalBandsBuilder extends BaseEchartBuilder {
843
908
  return seriesOption;
844
909
  });
845
910
  const common = this.getCommonOptions(overrides);
911
+ const categoryAxisOptions = {
912
+ ...overrides.mainAxis.categoryAxis[0],
913
+ data: data.categories,
914
+ };
846
915
  return {
847
916
  ...common,
848
917
  tooltip: {
@@ -859,7 +928,7 @@ class VerticalBandsBuilder extends BaseEchartBuilder {
859
928
  }
860
929
  },
861
930
  legend: getLegendOptions(),
862
- xAxis: getXAxisOptions({ data: data.categories }),
931
+ xAxis: getCategoryAxisOptions(categoryAxisOptions),
863
932
  yAxis: getYAxisOptions(),
864
933
  series: series
865
934
  };
@@ -1004,6 +1073,223 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
1004
1073
  ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\">\n <div echarts [options]=\"chartOptions\" [autoResize]=\"true\" (chartInit)=\"onChartInit($event)\" (chartClick)=\"onChartClick($event)\" (legendSelectChanged)=\"onLegendSelectChanged($event)\" class=\"demo-chart\"></div>\n</div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative;background-color:#fff;border-radius:8px}.demo-chart{width:100%;height:100%}\n"] }]
1005
1074
  }], ctorParameters: () => [] });
1006
1075
 
1076
+ /**
1077
+ * BarBuilder
1078
+ *
1079
+ * Especializado en construir opciones para gráficos de barras generales.
1080
+ */
1081
+ class BarBuilder extends BaseEchartBuilder {
1082
+ constructor() {
1083
+ super();
1084
+ }
1085
+ build(data, overrides) {
1086
+ if (!data || !data.series.length)
1087
+ return {};
1088
+ const { itemStyle, } = overrides.bar.series;
1089
+ const itStBorderRadius = itemStyle?.borderRadius ?? [4, 4, 0, 0];
1090
+ const series = data.series.map((s) => {
1091
+ const seriesOption = {
1092
+ name: s.name,
1093
+ type: 'bar',
1094
+ barMaxWidth: 50,
1095
+ barGap: '15%',
1096
+ barCategoryGap: '35%',
1097
+ animation: true,
1098
+ animationDuration: 1000,
1099
+ animationEasing: 'cubicOut',
1100
+ itemStyle: {
1101
+ borderRadius: itStBorderRadius,
1102
+ },
1103
+ data: s.data,
1104
+ emphasis: {
1105
+ focus: 'none'
1106
+ },
1107
+ blur: {
1108
+ itemStyle: {
1109
+ opacity: 0.2
1110
+ }
1111
+ },
1112
+ select: {
1113
+ itemStyle: {
1114
+ opacity: 1,
1115
+ shadowBlur: 10,
1116
+ shadowColor: 'rgba(0,0,0,0.3)'
1117
+ }
1118
+ },
1119
+ label: {
1120
+ show: false,
1121
+ position: 'top',
1122
+ formatter: (params) => {
1123
+ const key = s.originalKey || (data.categoryKeys ? data.categoryKeys[params.dataIndex] : String(params.dataIndex));
1124
+ return this.formatCellValue(params.value, key);
1125
+ }
1126
+ }
1127
+ };
1128
+ // Inyectar el resolver de color si existe
1129
+ if (this.colorResolver) {
1130
+ seriesOption.itemStyle.color = this.colorResolver;
1131
+ }
1132
+ return seriesOption;
1133
+ });
1134
+ const common = this.getCommonOptions(overrides);
1135
+ const xAxis = [];
1136
+ const yAxis = [];
1137
+ const categoryAxisOptions = {
1138
+ ...overrides.mainAxis.categoryAxis[0],
1139
+ data: data.categories,
1140
+ };
1141
+ if (overrides.bar.orientation === 'h') {
1142
+ xAxis.push(getValueAxisOptions());
1143
+ yAxis.push(getCategoryAxisOptions(categoryAxisOptions));
1144
+ }
1145
+ else {
1146
+ xAxis.push(getCategoryAxisOptions(categoryAxisOptions));
1147
+ yAxis.push(getValueAxisOptions());
1148
+ }
1149
+ return {
1150
+ ...common,
1151
+ tooltip: {
1152
+ ...getTooltipOptions({ trigger: 'item' }),
1153
+ formatter: getItemTooltipFormatter(data, this.formatCellValue.bind(this))
1154
+ },
1155
+ legend: getLegendOptions(),
1156
+ xAxis,
1157
+ yAxis,
1158
+ series: series
1159
+ };
1160
+ }
1161
+ }
1162
+
1163
+ /**
1164
+ * EchartsBarComponent
1165
+ *
1166
+ * Componente especialista para visualización de gráficos de barras.
1167
+ * Implementa patrones de interacción ZRender y resaltado de grupos.
1168
+ */
1169
+ class EchartsBarComponent extends BaseEchartsComponent {
1170
+ builder;
1171
+ constructor() {
1172
+ super();
1173
+ this.builder = new BarBuilder();
1174
+ }
1175
+ onChartInit(instance) {
1176
+ super.onChartInit(instance);
1177
+ this.setupAdditionalInteractions();
1178
+ }
1179
+ onInputChanges(changes) {
1180
+ if (changes['data']) {
1181
+ this.selectedCategoryIndex = null;
1182
+ this.selectedSeriesIndex = null;
1183
+ this.currentLegendSelected = null;
1184
+ }
1185
+ super.onInputChanges(changes);
1186
+ }
1187
+ setupAdditionalInteractions() {
1188
+ if (!this.chartInstance)
1189
+ return;
1190
+ // 1. ZRender click para sectores de fondo (específico para barras)
1191
+ const zr = this.chartInstance.getZr();
1192
+ zr.on('click', (params) => {
1193
+ if (!params.target) {
1194
+ const pointInPixel = [params.offsetX, params.offsetY];
1195
+ if (this.chartInstance.containPixel('grid', pointInPixel)) {
1196
+ const xIndexResult = this.chartInstance.convertFromPixel({ xAxisIndex: 0 }, pointInPixel);
1197
+ const xIndex = Array.isArray(xIndexResult) ? xIndexResult[0] : xIndexResult;
1198
+ if (typeof xIndex === 'number' && xIndex >= 0) {
1199
+ this.handleSelection(xIndex, -1);
1200
+ }
1201
+ }
1202
+ }
1203
+ });
1204
+ // 2. Mouseover para resaltar grupo completo
1205
+ this.chartInstance.on('mouseover', (params) => {
1206
+ if (params.dataIndex !== undefined) {
1207
+ this.highlightSector(params.dataIndex);
1208
+ }
1209
+ });
1210
+ this.chartInstance.on('mouseout', () => {
1211
+ this.clearHighlight();
1212
+ });
1213
+ }
1214
+ highlightSector(index) {
1215
+ if (!this.chartInstance || this.selectedCategoryIndex !== null)
1216
+ return;
1217
+ const seriesCount = Array.isArray(this.chartOptions.series) ? this.chartOptions.series.length : 0;
1218
+ this.chartInstance.dispatchAction({
1219
+ type: 'highlight',
1220
+ seriesIndex: Array.from({ length: seriesCount }, (_, i) => i),
1221
+ dataIndex: index
1222
+ });
1223
+ }
1224
+ clearHighlight() {
1225
+ if (!this.chartInstance)
1226
+ return;
1227
+ const seriesCount = Array.isArray(this.chartOptions.series) ? this.chartOptions.series.length : 0;
1228
+ this.chartInstance.dispatchAction({
1229
+ type: 'downplay',
1230
+ seriesIndex: Array.from({ length: seriesCount }, (_, i) => i)
1231
+ });
1232
+ }
1233
+ onLegendSelectChanged(event) {
1234
+ if (event && event.selected) {
1235
+ this.currentLegendSelected = event.selected;
1236
+ this.updateChartOptions();
1237
+ }
1238
+ }
1239
+ updateChartOptions() {
1240
+ if (!this.chartInstance)
1241
+ return;
1242
+ if (!this.data)
1243
+ return;
1244
+ if (this.valueFormatter)
1245
+ this.builder.setValueFormatter(this.valueFormatter);
1246
+ if (this.palette)
1247
+ this.builder.setPalette(this.palette);
1248
+ if (this.colorResolver)
1249
+ this.builder.setColorResolver(this.colorResolver);
1250
+ let baseOptions = this.builder.build(this.data, this.optionsOverrides);
1251
+ if (this.currentLegendSelected && baseOptions.legend) {
1252
+ baseOptions.legend.selected = { ...this.currentLegendSelected };
1253
+ }
1254
+ if (this.selectedCategoryIndex !== null && baseOptions.series) {
1255
+ const seriesArray = baseOptions.series;
1256
+ baseOptions.series = seriesArray.map((s, sIdx) => {
1257
+ if (s.type === 'bar' && Array.isArray(s.data)) {
1258
+ return {
1259
+ ...s,
1260
+ emphasis: {
1261
+ disabled: true
1262
+ },
1263
+ data: s.data.map((val, idx) => {
1264
+ const isSelected = idx === this.selectedCategoryIndex;
1265
+ const isBarSelected = isSelected && sIdx === this.selectedSeriesIndex;
1266
+ const itemVal = (val && typeof val === 'object' && val.value !== undefined) ? val.value : val;
1267
+ return {
1268
+ value: itemVal,
1269
+ itemStyle: {
1270
+ opacity: isSelected ? (isBarSelected ? 1 : 0.5) : 0.15,
1271
+ borderWidth: 0
1272
+ }
1273
+ };
1274
+ })
1275
+ };
1276
+ }
1277
+ return s;
1278
+ });
1279
+ }
1280
+ this.triggerUpdate(baseOptions);
1281
+ }
1282
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1283
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsBarComponent, isStandalone: true, selector: "vs-echarts-bar", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div class=\"echarts-container\">\n <div echarts [options]=\"chartOptions\" [autoResize]=\"true\" (chartInit)=\"onChartInit($event)\" (chartClick)=\"onChartClick($event)\" (legendSelectChanged)=\"onLegendSelectChanged($event)\" class=\"demo-chart\"></div>\n</div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative;background-color:#fff;border-radius:8px}.demo-chart{width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }] });
1284
+ }
1285
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsBarComponent, decorators: [{
1286
+ type: Component,
1287
+ args: [{ selector: 'vs-echarts-bar', standalone: true, imports: [
1288
+ CommonModule,
1289
+ NgxEchartsDirective,
1290
+ ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\">\n <div echarts [options]=\"chartOptions\" [autoResize]=\"true\" (chartInit)=\"onChartInit($event)\" (chartClick)=\"onChartClick($event)\" (legendSelectChanged)=\"onLegendSelectChanged($event)\" class=\"demo-chart\"></div>\n</div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative;background-color:#fff;border-radius:8px}.demo-chart{width:100%;height:100%}\n"] }]
1291
+ }], ctorParameters: () => [] });
1292
+
1007
1293
  class LineBuilder extends BaseEchartBuilder {
1008
1294
  constructor() {
1009
1295
  super();
@@ -1057,6 +1343,10 @@ class LineBuilder extends BaseEchartBuilder {
1057
1343
  return seriesOption;
1058
1344
  });
1059
1345
  const common = this.getCommonOptions(overrides);
1346
+ const categoryAxisOptions = {
1347
+ ...overrides.mainAxis.categoryAxis[0],
1348
+ data: data.categories,
1349
+ };
1060
1350
  return {
1061
1351
  ...common,
1062
1352
  tooltip: {
@@ -1073,8 +1363,8 @@ class LineBuilder extends BaseEchartBuilder {
1073
1363
  }
1074
1364
  },
1075
1365
  legend: getLegendOptions(),
1076
- xAxis: getXAxisOptions({ data: data.categories }),
1077
- yAxis: getYAxisOptions(),
1366
+ xAxis: [getCategoryAxisOptions(categoryAxisOptions)],
1367
+ yAxis: [getYAxisOptions()],
1078
1368
  series: series
1079
1369
  };
1080
1370
  }
@@ -1139,5 +1429,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
1139
1429
  * Generated bundle index. Do not edit.
1140
1430
  */
1141
1431
 
1142
- export { BaseEchartsComponent, EchartsLineComponent, EchartsRingComponent, EchartsVerticalBandsComponent, defaultOptionsOverrides, initializeEcharts, provideVSEcharts };
1432
+ export { BaseEchartsComponent, EchartsBarComponent, EchartsLineComponent, EchartsRingComponent, EchartsVerticalBandsComponent, defaultOptionsOverrides, initializeEcharts, provideVSEcharts };
1143
1433
  //# sourceMappingURL=visionaris-bruno-vs-echarts.mjs.map