@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
|
-
|
|
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
|
|
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
|
-
|
|
321
|
-
|
|
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
|
-
|
|
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?.
|
|
332
|
-
result.
|
|
124
|
+
if (overrides?.splitLine) {
|
|
125
|
+
result.splitLine = { ...defaults.splitLine, ...overrides.splitLine };
|
|
333
126
|
}
|
|
334
127
|
return result;
|
|
335
128
|
}
|
|
336
129
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
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:
|
|
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:
|
|
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
|