@visionaris-bruno/vs-echarts 8.4.2 → 8.6.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.
@@ -4,12 +4,18 @@ import { BarChart, PieChart, LineChart, ScatterChart, FunnelChart, SunburstChart
4
4
  import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GraphicComponent, ToolboxComponent, PolarComponent, DatasetComponent, DataZoomComponent } from 'echarts/components';
5
5
  import { CanvasRenderer, SVGRenderer } from 'echarts/renderers';
6
6
  import * as i0 from '@angular/core';
7
- import { EventEmitter, Output, Input, ViewChild, Directive, Component } from '@angular/core';
7
+ import { InjectionToken, Inject, Injectable, EventEmitter, inject, Output, Input, ViewChild, Directive, Component } from '@angular/core';
8
+ import langES from 'echarts/lib/i18n/langES.js';
9
+ import langDE from 'echarts/lib/i18n/langDE.js';
10
+ import langFR from 'echarts/lib/i18n/langFR.js';
11
+ import langIT from 'echarts/lib/i18n/langIT.js';
12
+ import langPTbr from 'echarts/lib/i18n/langPT-br.js';
8
13
  import { ReplaySubject } from 'rxjs';
9
14
  import { debounceTime } from 'rxjs/operators';
10
15
  import merge from 'lodash/merge';
11
16
  import { CommonModule } from '@angular/common';
12
17
  import { merge as merge$1 } from 'lodash';
18
+ import { TranslateService } from '@ngx-translate/core';
13
19
 
14
20
  /**
15
21
  * Global Design Tokens for ECharts
@@ -51,6 +57,11 @@ const fixPieDataIndexInside = (e) => {
51
57
  return e;
52
58
  };
53
59
 
60
+ /// <reference path="./typings.d.ts" />
61
+ const VS_ECHARTS_CONFIG = new InjectionToken('VS_ECHARTS_CONFIG', {
62
+ providedIn: 'root',
63
+ factory: () => ({ renderer: 'svg' }),
64
+ });
54
65
  /**
55
66
  * Inicialización centralizada de ECharts para evitar duplicación y efectos secundarios
56
67
  * no controlados en componentes standalone.
@@ -80,6 +91,13 @@ function initializeEcharts() {
80
91
  DatasetComponent,
81
92
  DataZoomComponent,
82
93
  ]);
94
+ // Register additional locales in ECharts
95
+ echarts.registerLocale('ES', langES);
96
+ echarts.registerLocale('DE', langDE);
97
+ echarts.registerLocale('FR', langFR);
98
+ echarts.registerLocale('IT', langIT);
99
+ echarts.registerLocale('PT-br', langPTbr);
100
+ echarts.registerLocale('PT', langPTbr);
83
101
  initialized = true;
84
102
  }
85
103
  /**
@@ -174,6 +192,43 @@ function defaultOptionsOverrides() {
174
192
  };
175
193
  }
176
194
 
195
+ // TODO: Exportar valores por defecto de vs-echarts.config.ts por este servicio y actualizar en logica de frontend.
196
+ class VSEchartsConfigService {
197
+ vsEchartsConfig;
198
+ mapping = {
199
+ es: 'ES',
200
+ en: 'EN',
201
+ us: 'EN',
202
+ de: 'DE',
203
+ fr: 'FR',
204
+ it: 'IT',
205
+ pt: 'PT-br',
206
+ br: 'PT-br',
207
+ };
208
+ constructor(vsEchartsConfig) {
209
+ this.vsEchartsConfig = vsEchartsConfig;
210
+ }
211
+ setLocale(language) {
212
+ if (!language) {
213
+ this.vsEchartsConfig.locale = 'ES';
214
+ return;
215
+ }
216
+ const echartsLocale = this.mapping[language.toLowerCase()] || 'ES';
217
+ this.vsEchartsConfig.locale = echartsLocale;
218
+ }
219
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: VSEchartsConfigService, deps: [{ token: VS_ECHARTS_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable });
220
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: VSEchartsConfigService, providedIn: 'root' });
221
+ }
222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: VSEchartsConfigService, decorators: [{
223
+ type: Injectable,
224
+ args: [{
225
+ providedIn: 'root'
226
+ }]
227
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
228
+ type: Inject,
229
+ args: [VS_ECHARTS_CONFIG]
230
+ }] }] });
231
+
177
232
  /**
178
233
  * BaseEchartsComponent
179
234
  *
@@ -188,22 +243,31 @@ class BaseEchartsComponent {
188
243
  data = { dimensions: [], source: [] };
189
244
  optionsOverrides = defaultOptionsOverrides();
190
245
  /** Paleta de colores básica */
191
- palette = getDefaultPalette();
246
+ palette;
192
247
  /** Resolver de colores dinámico (Callback) */
193
248
  colorResolver;
194
249
  /** Formateador de valores para etiquetas y tooltips */
195
- valueFormatter = (value) => value.toLocaleString();
250
+ valueFormatter;
196
251
  chartClick = new EventEmitter();
197
252
  /** Subject para debouncing de actualizaciones. ReplaySubject asegura no perder el primer renderizado. */
198
253
  updateSubject = new ReplaySubject(1);
199
254
  updateSubscription;
200
255
  chartInstance;
256
+ vsEchartsConfig = (() => {
257
+ try {
258
+ return inject(VS_ECHARTS_CONFIG, { optional: true });
259
+ }
260
+ catch {
261
+ return null;
262
+ }
263
+ })();
201
264
  /** Opciones de inicializacion de echarts
202
265
  *
203
266
  * NgxEchartsDirective.initOpts
204
267
  */
205
268
  initOptions = {
206
- renderer: 'svg', // svg se visualiza mejor en el reescalado de tableros.
269
+ renderer: this.vsEchartsConfig?.renderer || 'svg',
270
+ locale: this.vsEchartsConfig?.locale,
207
271
  };
208
272
  constructor() {
209
273
  this.updateSubscription = this.updateSubject.pipe(debounceTime(150)).subscribe(options => {
@@ -933,6 +997,7 @@ class RingBuilder {
933
997
 
934
998
  class BoxPlotBuilder {
935
999
  baseProduct;
1000
+ translateService;
936
1001
  valueFormatter = (value, key) => value.toLocaleString();
937
1002
  palette = [];
938
1003
  // TODO: Hay que implementar un valor por defecto.
@@ -940,25 +1005,24 @@ class BoxPlotBuilder {
940
1005
  result = {};
941
1006
  constructor(baseProduct) {
942
1007
  this.baseProduct = baseProduct;
1008
+ try {
1009
+ this.translateService = inject(TranslateService);
1010
+ }
1011
+ catch {
1012
+ // Ignored if called outside an injection context (e.g. unit tests)
1013
+ }
943
1014
  }
944
1015
  reset() {
945
1016
  this.result = {};
946
1017
  }
947
1018
  addDataset(data, opts) {
948
- const statisticKeys = {
949
- min: 'Min',
950
- q1: 'Q1',
951
- median: 'Median',
952
- q3: 'Q3',
953
- max: 'Max',
954
- };
955
1019
  /**
956
1020
  *
957
1021
  * @param arr
958
1022
  * @param statisticsKeys etiquetas personalizadas para los valores de estadistica.
959
1023
  * @returns
960
1024
  */
961
- function calcularResumen5Numeros(arr, statisticsKeys) {
1025
+ function calcularResumen5Numeros(arr) {
962
1026
  if (arr.length === 0)
963
1027
  return null;
964
1028
  // 1. Ordenar los números de menor a mayor
@@ -976,11 +1040,11 @@ class BoxPlotBuilder {
976
1040
  }
977
1041
  };
978
1042
  return {
979
- [statisticsKeys.min]: ordenados[0],
980
- [statisticsKeys.q1]: obtenerPercentil(0.25),
981
- [statisticsKeys.median]: obtenerPercentil(0.50),
982
- [statisticsKeys.q3]: obtenerPercentil(0.75),
983
- [statisticsKeys.max]: ordenados[ordenados.length - 1],
1043
+ min: ordenados[0],
1044
+ q1: obtenerPercentil(0.25),
1045
+ median: obtenerPercentil(0.50),
1046
+ q3: obtenerPercentil(0.75),
1047
+ max: ordenados[ordenados.length - 1],
984
1048
  };
985
1049
  }
986
1050
  ;
@@ -990,7 +1054,7 @@ class BoxPlotBuilder {
990
1054
  .filter(([key, val]) => key !== 'category' && !isNaN(val))
991
1055
  .map(([_, val]) => Number(val));
992
1056
  // resumen5NumerosXCat
993
- const resObj = Object.assign({ category: s.category }, calcularResumen5Numeros(values, statisticKeys));
1057
+ const resObj = Object.assign({ category: s.category }, calcularResumen5Numeros(values));
994
1058
  return resObj;
995
1059
  });
996
1060
  const dataset = [{
@@ -1032,17 +1096,31 @@ class BoxPlotBuilder {
1032
1096
  addTooltip(data, overrides) {
1033
1097
  const mainMeassureKey = data.dimensions.filter(d => d.name != 'category')[0].name;
1034
1098
  const valueFormatter = this.valueFormatter;
1099
+ const translate = (key, fallback) => {
1100
+ if (!this.translateService)
1101
+ return fallback;
1102
+ const res = this.translateService.instant(key);
1103
+ return res === key ? fallback : res;
1104
+ };
1105
+ const labelMap = {
1106
+ min: translate('VS_ECHARTS.BOXPLOT.MIN', 'Min'),
1107
+ q1: translate('VS_ECHARTS.BOXPLOT.Q1', 'Q1'),
1108
+ median: translate('VS_ECHARTS.BOXPLOT.MEDIAN', 'Median'),
1109
+ q3: translate('VS_ECHARTS.BOXPLOT.Q3', 'Q3'),
1110
+ max: translate('VS_ECHARTS.BOXPLOT.MAX', 'Max'),
1111
+ };
1035
1112
  function tooltipFormatter(params) {
1036
1113
  const tooltipEventData = Array.isArray(params) ? params[0] : params;
1037
1114
  const title = `<b>${tooltipEventData.name}</b></br>`;
1038
1115
  let htmlbody = ``;
1039
1116
  for (const datakey in tooltipEventData.data) {
1040
- const label = datakey;
1117
+ const rawLabel = datakey;
1041
1118
  const value = tooltipEventData.data[datakey];
1042
- if (label == 'category') {
1119
+ if (rawLabel == 'category') {
1043
1120
  continue;
1044
1121
  }
1045
1122
  ;
1123
+ const label = labelMap[rawLabel] || rawLabel;
1046
1124
  const formattedValue = valueFormatter(value, mainMeassureKey);
1047
1125
  const bodyText = `${tooltipEventData.marker}${label}: ${formattedValue}</br>`;
1048
1126
  htmlbody += bodyText;
@@ -1128,236 +1206,344 @@ class BoxPlotBuilder {
1128
1206
  }
1129
1207
 
1130
1208
  /**
1131
- * Builder principal.
1132
- *
1133
- * Es muy completo, tiene soporte para ejes y sistema cartesiano polar.
1134
- *
1135
- * Puede verse utilizado en graficos como Lineas y Bars. Siempre que se pueda priorizar utilizar este.
1209
+ * Director de Builds.
1136
1210
  */
1137
- class EChartBuilder {
1138
- baseProduct = undefined;
1139
- valueFormatter = (value) => value.toLocaleString();
1140
- palette = [];
1141
- // TODO: Hay que implementar un valor por defecto.
1142
- colorResolver;
1143
- result = {};
1144
- constructor(baseProduct) {
1145
- this.baseProduct = baseProduct;
1211
+ class VSECDirector {
1212
+ builder = undefined;
1213
+ constructor(builder) {
1214
+ this.builder = builder;
1146
1215
  }
1147
- reset() {
1148
- this.result = {};
1216
+ makeBar(data, overrides, opts = {}) {
1217
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1218
+ x: 'category',
1219
+ y: 'value',
1220
+ // TODO agregar radius y angle axis
1221
+ } } = opts;
1222
+ this.builder.reset();
1223
+ // El orden importa, primero callbaks y despues componentes.
1224
+ //chart callbacks
1225
+ this.builder.setValueFormatter(valueFormatter);
1226
+ this.builder.setPalette(palette);
1227
+ this.builder.setColorResolver(colorResolver);
1228
+ const product = this.builder.baseProduct;
1229
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1230
+ // chart components
1231
+ this.builder.addCommons();
1232
+ const layoutOpts = { axisTypes };
1233
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1234
+ this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1235
+ this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1236
+ this.builder.addTooltip(data, overrides.tooltip);
1237
+ this.builder.addLegend();
1149
1238
  }
1150
- addCommons() {
1151
- const opts = {
1152
- palette: this.palette,
1153
- };
1154
- merge$1(this.result, getCommons(opts));
1239
+ makeBarRadial(data, overrides, opts = {}) {
1240
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1241
+ x: 'category',
1242
+ y: 'value',
1243
+ // TODO agregar radius y angle axis
1244
+ } } = opts;
1245
+ this.builder.reset();
1246
+ // El orden importa, primero callbaks y despues componentes.
1247
+ //chart callbacks
1248
+ this.builder.setValueFormatter(valueFormatter);
1249
+ this.builder.setPalette(palette);
1250
+ this.builder.setColorResolver(colorResolver);
1251
+ const product = this.builder.baseProduct;
1252
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1253
+ // chart components
1254
+ this.builder.addCommons();
1255
+ const layoutOpts = { axisTypes, coordinateSystem: 'polar' };
1256
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1257
+ this.builder.addPolar();
1258
+ this.builder.addAngleAxis(data, overrides['axis']);
1259
+ this.builder.addRadiusAxis(data, overrides['axis']);
1260
+ this.builder.addTooltip(data, overrides.tooltip);
1261
+ this.builder.addLegend();
1155
1262
  }
1156
- /**
1157
- *
1158
- * @param data
1159
- * @param overrides
1160
- * @returns
1161
- */
1162
- addSeries(data, overrides, opts) {
1163
- if (!data || !data.dimensions || !data.source || data.source.length === 0)
1164
- return;
1165
- this.result.dataset = {
1166
- dimensions: data.dimensions,
1167
- source: data.source
1168
- };
1169
- const measureDims = data.dimensions.filter(d => d.name !== 'category');
1170
- const isPolar = opts?.coordinateSystem === 'polar';
1171
- const isHorizontal = opts?.axisTypes?.x === 'value' && opts?.axisTypes?.y === 'category';
1172
- const series = measureDims.map((dim) => {
1173
- const friendlyName = dim.displayName || dim.name;
1174
- const dimKey = dim.name;
1175
- let encode = {
1176
- x: 'category',
1177
- y: dimKey
1178
- };
1179
- if (isPolar) {
1180
- encode = {
1181
- angle: 'category',
1182
- radius: dimKey
1183
- };
1184
- }
1185
- else if (isHorizontal) {
1186
- encode = {
1187
- x: dimKey,
1188
- y: 'category'
1189
- };
1190
- }
1191
- const dynamicOverrides = {
1192
- name: friendlyName,
1193
- encode,
1194
- label: {
1195
- formatter: (params) => {
1196
- const row = params.value;
1197
- const rawValue = (row && typeof row === 'object') ? row[dimKey] : params.value;
1198
- return this.formatCellValue(Number(rawValue ?? 0), dimKey);
1199
- }
1200
- }
1201
- };
1202
- const seriesOption = merge$1(dynamicOverrides, overrides);
1203
- // Inyectar el resolver de color si existe
1204
- if (this.colorResolver && seriesOption.itemStyle) {
1205
- seriesOption.itemStyle.color = this.colorResolver;
1206
- }
1207
- return seriesOption;
1208
- });
1209
- this.result.series = series;
1263
+ makeLine(data, overrides, opts = {}) {
1264
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1265
+ x: 'category',
1266
+ y: 'value',
1267
+ // TODO agregar radius y angle axis
1268
+ } } = opts;
1269
+ this.builder.reset();
1270
+ // El orden importa, primero callbaks y despues componentes.
1271
+ //chart callbacks
1272
+ this.builder.setValueFormatter(valueFormatter);
1273
+ this.builder.setPalette(palette);
1274
+ this.builder.setColorResolver(colorResolver);
1275
+ const product = this.builder.baseProduct;
1276
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1277
+ // chart components
1278
+ this.builder.addCommons();
1279
+ const layoutOpts = { axisTypes };
1280
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1281
+ this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1282
+ this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1283
+ this.builder.addTooltip(data, overrides.tooltip);
1284
+ this.builder.addLegend();
1210
1285
  }
1211
- /**
1212
- * TODO: Mejorar funcion, no me convence como se implemento el tooltip formatter.
1213
- * @param data
1214
- * @param overrides
1215
- */
1216
- addTooltip(data, overrides) {
1217
- // inyecto formateador a overrides de tooltip
1218
- merge$1(overrides, {
1219
- formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1220
- });
1221
- const tooltip = getTooltipOptions(overrides);
1222
- this.result.tooltip = tooltip;
1286
+ makeScatter(data, overrides, opts = {}) {
1287
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1288
+ x: 'category',
1289
+ y: 'value',
1290
+ } } = opts;
1291
+ this.builder.reset();
1292
+ // El orden importa, primero callbacks y despues componentes.
1293
+ this.builder.setValueFormatter(valueFormatter);
1294
+ this.builder.setPalette(palette);
1295
+ this.builder.setColorResolver(colorResolver);
1296
+ const product = this.builder.baseProduct;
1297
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1298
+ // chart components
1299
+ this.builder.addCommons();
1300
+ const layoutOpts = { axisTypes };
1301
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1302
+ this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1303
+ this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1304
+ this.builder.addTooltip(data, overrides.tooltip);
1305
+ this.builder.addLegend();
1223
1306
  }
1224
- addPolar() {
1225
- const polar = [];
1226
- polar.push({
1227
- radius: '65%',
1228
- center: ["50%", "44%"],
1229
- });
1230
- this.result.polar = polar;
1307
+ makeRing(data, overrides, opts = {}) {
1308
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1309
+ this.builder.reset();
1310
+ // El orden importa, primero callbacks y despues componentes.
1311
+ this.builder.setValueFormatter(valueFormatter);
1312
+ this.builder.setPalette(palette);
1313
+ this.builder.setColorResolver(colorResolver);
1314
+ const product = this.builder.baseProduct;
1315
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1316
+ // chart components
1317
+ this.builder.addCommons();
1318
+ const layoutOpts = {};
1319
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1320
+ this.builder.addGraphic();
1321
+ this.builder.addTooltip(data, overrides.tooltip);
1322
+ this.builder.addLegend();
1231
1323
  }
1232
- addXAxis(data, overrides, type = 'category') {
1233
- const xAxis = [];
1234
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1235
- const valueAxisOptions = this.getValueAxisOptions(data, overrides);
1236
- if (type == 'category') {
1237
- xAxis.push(categoryAxisOptions);
1238
- }
1239
- else if (type == 'value') {
1240
- xAxis.push(valueAxisOptions);
1241
- }
1242
- this.result.xAxis = xAxis;
1324
+ makePie(data, overrides, opts = {}) {
1325
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1326
+ this.builder.reset();
1327
+ // El orden importa, primero callbacks y despues componentes.
1328
+ this.builder.setValueFormatter(valueFormatter);
1329
+ this.builder.setPalette(palette);
1330
+ this.builder.setColorResolver(colorResolver);
1331
+ const product = this.builder.baseProduct;
1332
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1333
+ // chart components
1334
+ this.builder.addCommons();
1335
+ const layoutOpts = {};
1336
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1337
+ this.builder.addTooltip(data, overrides.tooltip);
1338
+ this.builder.addLegend();
1243
1339
  }
1244
- addYAxis(data, overrides, type = 'value') {
1245
- const yAxis = [];
1246
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1247
- const valueAxisOptions = this.getValueAxisOptions(data, overrides);
1248
- if (type == 'category') {
1249
- yAxis.push(categoryAxisOptions);
1250
- }
1251
- else if (type == 'value') {
1252
- yAxis.push(valueAxisOptions);
1253
- }
1254
- this.result.yAxis = yAxis;
1340
+ makeFunnel(data, overrides, opts = {}) {
1341
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1342
+ this.builder.reset();
1343
+ // El orden importa, primero callbacks y despues componentes.
1344
+ this.builder.setValueFormatter(valueFormatter);
1345
+ this.builder.setPalette(palette);
1346
+ this.builder.setColorResolver(colorResolver);
1347
+ const product = this.builder.baseProduct;
1348
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1349
+ // chart components
1350
+ this.builder.addCommons();
1351
+ const layoutOpts = {};
1352
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1353
+ this.builder.addTooltip(data, overrides.tooltip);
1354
+ this.builder.addLegend();
1255
1355
  }
1256
- addRadiusAxis(data, overrides) {
1257
- const radiusAxis = [];
1258
- /** estilos exclusivos hardcodeados para el eje de valores del sistema de coordenadas polar*/
1259
- const radiusAxisOverrides = {
1260
- zlevel: 10,
1261
- axisTick: {
1262
- show: false,
1263
- },
1264
- axisLabel: {
1265
- margin: 2,
1266
- fontSize: 8,
1267
- align: 'right',
1268
- rotate: 20,
1269
- verticalAlign: 'top',
1270
- },
1271
- splitLine: {
1272
- show: true,
1273
- lineStyle: {
1274
- opacity: 0.2,
1275
- type: 'solid',
1276
- }
1277
- },
1278
- axisLine: {
1279
- lineStyle: {
1280
- type: 'dashed',
1281
- },
1282
- }
1283
- };
1284
- const radialAxisOptions = getValueAxisOptions(radiusAxisOverrides);
1285
- radiusAxis.push(radialAxisOptions);
1286
- this.result.radiusAxis = radiusAxis;
1356
+ makeSunburst(data, overrides, opts = {}) {
1357
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1358
+ this.builder.reset();
1359
+ // El orden importa, primero callbacks y despues componentes.
1360
+ this.builder.setValueFormatter(valueFormatter);
1361
+ this.builder.setPalette(palette);
1362
+ this.builder.setColorResolver(colorResolver);
1363
+ const product = this.builder.baseProduct;
1364
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1365
+ // chart components
1366
+ this.builder.addCommons();
1367
+ const layoutOpts = {};
1368
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1369
+ this.builder.addTooltip(data, overrides.tooltip);
1370
+ // this.builder.addLegend();
1287
1371
  }
1288
- addAngleAxis(data, overrides) {
1289
- const angleAxis = [];
1290
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1291
- angleAxis.push(categoryAxisOptions);
1292
- this.result.angleAxis = angleAxis;
1372
+ makeSankey(data, overrides, opts = {}) {
1373
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1374
+ this.builder.reset();
1375
+ // El orden importa, primero callbacks y despues componentes.
1376
+ this.builder.setValueFormatter(valueFormatter);
1377
+ this.builder.setPalette(palette);
1378
+ this.builder.setColorResolver(colorResolver);
1379
+ const product = this.builder.baseProduct;
1380
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1381
+ // chart components
1382
+ this.builder.addCommons();
1383
+ const layoutOpts = {};
1384
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1385
+ this.builder.addTooltip(data, overrides.tooltip);
1293
1386
  }
1294
- addLegend() {
1295
- this.result.legend = getLegendOptions();
1387
+ makeBoxplot(data, overrides, opts = {}) {
1388
+ if (this.builder instanceof BoxPlotBuilder == false) {
1389
+ return;
1390
+ }
1391
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1392
+ x: 'value',
1393
+ y: 'category',
1394
+ }, } = opts;
1395
+ this.builder.reset();
1396
+ // El orden importa, primero callbacks y despues componentes.
1397
+ this.builder.setValueFormatter(valueFormatter);
1398
+ this.builder.setPalette(palette);
1399
+ this.builder.setColorResolver(colorResolver);
1400
+ const product = this.builder.baseProduct;
1401
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1402
+ // chart components
1403
+ this.builder.addCommons();
1404
+ const layoutOpts = { axisTypes };
1405
+ this.builder.addDataset(data);
1406
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1407
+ this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1408
+ this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1409
+ this.builder.addTooltip(data, overrides.tooltip);
1410
+ this.builder.addDataZoom();
1296
1411
  }
1297
- // No-ops for ring charts
1298
- addGraphic() { }
1299
- addDataset(data) { }
1300
- addDataZoom() {
1301
- this.result.dataZoom = [
1302
- {
1303
- type: 'inside'
1412
+ }
1413
+
1414
+ /**
1415
+ * EchartsRingComponent
1416
+ *
1417
+ * Especialista en visualización de tipo Ring. Soporta multi-medidas y KPI central.
1418
+ * @see {@link vs-echarts/docs/charts/ring-patterns.md}
1419
+ */
1420
+ class EchartsRingComponent extends BaseEchartsComponent {
1421
+ baseSeriesOptions = {
1422
+ type: 'pie',
1423
+ center: ['50%', '50%'],
1424
+ avoidLabelOverlap: true,
1425
+ minAngle: 3,
1426
+ selectedMode: 'single',
1427
+ selectedOffset: 4,
1428
+ itemStyle: {
1429
+ borderColor: '#fff',
1430
+ },
1431
+ label: { show: false },
1432
+ emphasis: {
1433
+ scale: true,
1434
+ scaleSize: 2,
1435
+ },
1436
+ select: {
1437
+ itemStyle: {
1438
+ borderColor: EChartsTokens.sBorderColor,
1439
+ shadowColor: EChartsTokens.sShadowColor,
1440
+ borderWidth: 1,
1441
+ shadowBlur: 4,
1304
1442
  },
1305
- {
1306
- type: 'slider',
1307
- height: 20,
1308
- bottom: 60
1309
- }
1310
- ];
1311
- }
1312
- getResult() {
1313
- return this.result;
1443
+ },
1444
+ animationType: 'scale',
1445
+ animationEasing: 'elasticOut',
1446
+ };
1447
+ baseProduct = {
1448
+ chartKey: 'ring',
1449
+ baseOptions: {
1450
+ series: this.baseSeriesOptions,
1451
+ }
1452
+ };
1453
+ builder = new RingBuilder(this.baseProduct);
1454
+ director = new VSECDirector(this.builder);
1455
+ lastSelectedSeriesIndex = null;
1456
+ lastSelectedDataIndex = null;
1457
+ selectedPercent = null;
1458
+ currentGraphicText = '';
1459
+ constructor() {
1460
+ super();
1314
1461
  }
1315
- ;
1316
- /**
1317
- * Formatea un valor utilizando el callback inyectado.
1318
- */
1319
- formatCellValue(value, key) {
1320
- return this.valueFormatter(value, key);
1462
+ make() {
1463
+ const makeOpts = {
1464
+ valueFormatter: this.valueFormatter,
1465
+ palette: this.palette,
1466
+ colorResolver: this.colorResolver,
1467
+ };
1468
+ this.director.makeRing(this.data, this.optionsOverrides, makeOpts);
1321
1469
  }
1322
- // Setters
1323
- /**
1324
- * Permite inyectar un formateador de valores externo.
1325
- */
1326
- setValueFormatter(formatter) {
1327
- if (formatter) {
1328
- this.valueFormatter = formatter;
1470
+ onInputChanges(changes) {
1471
+ // Reset selection only if data changed
1472
+ if (changes['data']) {
1473
+ this.lastSelectedSeriesIndex = null;
1474
+ this.lastSelectedDataIndex = null;
1475
+ this.selectedPercent = null;
1476
+ this.currentGraphicText = '';
1329
1477
  }
1478
+ super.onInputChanges(changes);
1330
1479
  }
1331
1480
  /**
1332
- * Permite inyectar una paleta de colores básica.
1481
+ * Maneja clics en los sectores del ring.
1482
+ * Soporta múltiples series (anillos) y actualiza el KPI central.
1333
1483
  */
1334
- setPalette(palette) {
1335
- if (palette) {
1336
- this.palette = palette;
1484
+ onChartClick(event) {
1485
+ if (this.chartInstance && event && event.dataIndex !== undefined) {
1486
+ const isSameSelection = event.seriesIndex === this.lastSelectedSeriesIndex &&
1487
+ event.dataIndex === this.lastSelectedDataIndex;
1488
+ if (isSameSelection) {
1489
+ // Toggle OFF
1490
+ this.lastSelectedSeriesIndex = null;
1491
+ this.lastSelectedDataIndex = null;
1492
+ this.selectedPercent = null;
1493
+ this.setGraphicText('');
1494
+ }
1495
+ else {
1496
+ // SELECT
1497
+ this.lastSelectedSeriesIndex = event.seriesIndex;
1498
+ this.lastSelectedDataIndex = event.dataIndex;
1499
+ this.selectedPercent = (event.percent !== undefined) ? event.percent + '%' : '';
1500
+ this.setGraphicText(this.selectedPercent);
1501
+ }
1502
+ this.chartClick.emit({
1503
+ type: 'cross-filter',
1504
+ data: {
1505
+ category: event.name,
1506
+ serie: event.seriesName,
1507
+ value: event.value
1508
+ }
1509
+ });
1337
1510
  }
1338
1511
  }
1339
- /**
1340
- * Permite inyectar un resolver de colores dinámico.
1341
- */
1342
- setColorResolver(resolver) {
1343
- if (resolver) {
1344
- this.colorResolver = resolver;
1512
+ onChartMouseOver(event) {
1513
+ if (this.selectedPercent === null && event && event.percent !== undefined) {
1514
+ this.setGraphicText(event.percent + '%');
1345
1515
  }
1346
1516
  }
1347
- // Utils
1348
- getCategoryAxisOptions(data, overrides) {
1349
- // No explicit data needed on category axis when using ECharts dataset
1350
- const categoryAxisOptionsOverrides = {
1351
- ...overrides.categoryAxis[0]
1352
- };
1353
- const categoryAxisOptions = getCategoryAxisOptions(categoryAxisOptionsOverrides);
1354
- return categoryAxisOptions;
1517
+ onChartMouseOut(event) {
1518
+ if (this.selectedPercent === null) {
1519
+ this.setGraphicText('');
1520
+ }
1355
1521
  }
1356
- getValueAxisOptions(data, overrides) {
1357
- const valueAxisOptions = getValueAxisOptions();
1358
- return valueAxisOptions;
1522
+ /**
1523
+ * Actualiza el texto del Graphic central y persiste la selección en el modelo de opciones.
1524
+ */
1525
+ setGraphicText(text) {
1526
+ this.currentGraphicText = text;
1527
+ // Persistencia de GRAPHIC (KPI central)
1528
+ this.chartInstance?.setOption({
1529
+ graphic: {
1530
+ style: {
1531
+ text: this.currentGraphicText,
1532
+ opacity: this.currentGraphicText ? 1 : 0,
1533
+ }
1534
+ }
1535
+ });
1359
1536
  }
1537
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1538
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsRingComponent, isStandalone: true, selector: "vs-echarts-ring", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div class=\"echarts-container\" \n echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartMouseOver)=\"onChartMouseOver($event)\" \n (chartMouseOut)=\"onChartMouseOut($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n ", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
1360
1539
  }
1540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, decorators: [{
1541
+ type: Component,
1542
+ args: [{ selector: 'vs-echarts-ring', standalone: true, imports: [
1543
+ CommonModule,
1544
+ NgxEchartsDirective,
1545
+ ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\" \n echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartMouseOver)=\"onChartMouseOver($event)\" \n (chartMouseOut)=\"onChartMouseOut($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n ", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
1546
+ }], ctorParameters: () => [] });
1361
1547
 
1362
1548
  /**
1363
1549
  * PieBuilder
@@ -1522,42 +1708,102 @@ class PieBuilder {
1522
1708
  }
1523
1709
 
1524
1710
  /**
1525
- * FunnelBuilder
1711
+ * EchartsPieComponent
1526
1712
  *
1527
- * Concrete builder for Funnel charts.
1713
+ * Especialista en visualización de tipo Pie (Torta y Anillos concéntricos).
1714
+ * La primera serie se dibuja como un gráfico de torta tradicional (lleno) y
1715
+ * las subsecuentes como anillos concéntricos alrededor.
1528
1716
  */
1529
- class FunnelBuilder {
1530
- baseProduct;
1531
- valueFormatter = (value) => value.toLocaleString();
1532
- palette = [];
1533
- colorResolver;
1534
- result = {};
1535
- constructor(baseProduct) {
1536
- this.baseProduct = baseProduct;
1537
- }
1538
- reset() {
1539
- this.result = {};
1540
- }
1541
- addCommons() {
1542
- const opts = {
1543
- palette: this.palette,
1544
- };
1545
- const common = getCommons(opts);
1546
- merge(this.result, common);
1547
- }
1548
- addSeries(data, overrides) {
1549
- if (!data || !data.dimensions || !data.source || data.source.length === 0)
1550
- return;
1551
- this.result.dataset = {
1552
- dimensions: data.dimensions,
1553
- source: data.source
1554
- };
1555
- const measureDims = data.dimensions.filter(d => d.name !== 'category');
1556
- if (measureDims.length === 0)
1557
- return;
1558
- const dim = measureDims[0];
1559
- const dimKey = dim.name;
1560
- const friendlyName = dim.displayName || dim.name;
1717
+ class EchartsPieComponent extends BaseEchartsComponent {
1718
+ baseSeriesOptions = {
1719
+ type: 'pie',
1720
+ center: ['50%', '50%'],
1721
+ avoidLabelOverlap: true,
1722
+ minAngle: 3,
1723
+ selectedMode: 'single',
1724
+ selectedOffset: 4,
1725
+ emphasis: {
1726
+ scale: true,
1727
+ scaleSize: 2,
1728
+ },
1729
+ select: {
1730
+ itemStyle: {
1731
+ borderColor: EChartsTokens.sBorderColor,
1732
+ shadowColor: EChartsTokens.sShadowColor,
1733
+ borderWidth: 1,
1734
+ shadowBlur: 4,
1735
+ },
1736
+ },
1737
+ animationType: 'scale',
1738
+ animationEasing: 'elasticOut',
1739
+ };
1740
+ baseProduct = {
1741
+ chartKey: 'pie',
1742
+ baseOptions: {
1743
+ series: this.baseSeriesOptions,
1744
+ }
1745
+ };
1746
+ builder = new PieBuilder(this.baseProduct);
1747
+ director = new VSECDirector(this.builder);
1748
+ constructor() {
1749
+ super();
1750
+ }
1751
+ make() {
1752
+ const makeOpts = {
1753
+ valueFormatter: this.valueFormatter,
1754
+ palette: this.palette,
1755
+ colorResolver: this.colorResolver,
1756
+ };
1757
+ this.director.makePie(this.data, this.optionsOverrides, makeOpts);
1758
+ }
1759
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1760
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsPieComponent, isStandalone: true, selector: "vs-echarts-pie", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div\n class=\"echarts-container\"\n echarts\n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
1761
+ }
1762
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, decorators: [{
1763
+ type: Component,
1764
+ args: [{ selector: 'vs-echarts-pie', standalone: true, imports: [
1765
+ CommonModule,
1766
+ NgxEchartsDirective,
1767
+ ], providers: [provideVSEcharts()], template: "<div\n class=\"echarts-container\"\n echarts\n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
1768
+ }], ctorParameters: () => [] });
1769
+
1770
+ /**
1771
+ * FunnelBuilder
1772
+ *
1773
+ * Concrete builder for Funnel charts.
1774
+ */
1775
+ class FunnelBuilder {
1776
+ baseProduct;
1777
+ valueFormatter = (value) => value.toLocaleString();
1778
+ palette = [];
1779
+ colorResolver;
1780
+ result = {};
1781
+ constructor(baseProduct) {
1782
+ this.baseProduct = baseProduct;
1783
+ }
1784
+ reset() {
1785
+ this.result = {};
1786
+ }
1787
+ addCommons() {
1788
+ const opts = {
1789
+ palette: this.palette,
1790
+ };
1791
+ const common = getCommons(opts);
1792
+ merge(this.result, common);
1793
+ }
1794
+ addSeries(data, overrides) {
1795
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
1796
+ return;
1797
+ this.result.dataset = {
1798
+ dimensions: data.dimensions,
1799
+ source: data.source
1800
+ };
1801
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
1802
+ if (measureDims.length === 0)
1803
+ return;
1804
+ const dim = measureDims[0];
1805
+ const dimKey = dim.name;
1806
+ const friendlyName = dim.displayName || dim.name;
1561
1807
  // Obtener los valores para min/max
1562
1808
  const seriesValues = data.source.map(row => Number(row[dimKey] ?? 0));
1563
1809
  const minVal = seriesValues.length > 0 ? Math.min(...seriesValues) : 0;
@@ -1625,109 +1871,91 @@ class FunnelBuilder {
1625
1871
  }
1626
1872
  }
1627
1873
 
1628
- class SunburstBuilder {
1629
- baseProduct;
1630
- valueFormatter = (value) => value.toLocaleString();
1631
- palette = [];
1632
- colorResolver;
1633
- result = {};
1634
- constructor(baseProduct) {
1635
- this.baseProduct = baseProduct;
1636
- }
1637
- reset() {
1638
- this.result = {};
1639
- }
1640
- addSeries(data, overrides) {
1641
- if (!data || !data.source || data.source.length === 0) {
1642
- return;
1643
- }
1644
- const sunburstData = mapHierarchicalData(data.source, data.dimensions);
1645
- const depth = getTreeDepth(sunburstData);
1646
- const levels = [];
1647
- for (let i = 0; i <= depth; i++) {
1648
- levels.push({
1649
- label: {
1650
- show: false,
1651
- },
1652
- });
1653
- }
1654
- const dynamiSerieOptions = {
1655
- name: '',
1656
- data: sunburstData,
1657
- levels: levels
1658
- };
1659
- const serie = merge$1({}, dynamiSerieOptions, overrides);
1660
- if (this.colorResolver) {
1661
- if (!serie.itemStyle) {
1662
- serie.itemStyle = {};
1874
+ /**
1875
+ * EchartsFunnelComponent
1876
+ *
1877
+ * Component for Funnel visualization. Supports single and multiple measures.
1878
+ */
1879
+ class EchartsFunnelComponent extends BaseEchartsComponent {
1880
+ baseSeriesOptions = {
1881
+ type: 'funnel',
1882
+ left: '10%',
1883
+ width: '80%',
1884
+ minSize: '0.01%',
1885
+ maxSize: '100%',
1886
+ sort: 'descending',
1887
+ gap: 2,
1888
+ label: {
1889
+ show: true,
1890
+ position: 'inside'
1891
+ },
1892
+ labelLine: {
1893
+ show: false
1894
+ },
1895
+ itemStyle: {
1896
+ borderColor: '#fff',
1897
+ borderWidth: 1
1898
+ },
1899
+ emphasis: {
1900
+ label: {
1901
+ fontSize: 16
1663
1902
  }
1664
- serie.itemStyle.color = this.colorResolver;
1903
+ },
1904
+ selectedMode: 'single',
1905
+ select: {
1906
+ label: {
1907
+ fontSize: 16
1908
+ },
1909
+ itemStyle: {
1910
+ borderWidth: 1,
1911
+ borderColor: EChartsTokens.sBorderColor,
1912
+ shadowColor: EChartsTokens.sShadowColor,
1913
+ shadowBlur: 6,
1914
+ }
1915
+ },
1916
+ };
1917
+ baseProduct = {
1918
+ chartKey: 'funnel',
1919
+ baseOptions: {
1920
+ series: this.baseSeriesOptions,
1665
1921
  }
1666
- this.result.series = serie;
1667
- }
1668
- addTooltip(data, overrides) {
1669
- merge$1(overrides, {
1670
- formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
1671
- });
1672
- const tooltip = getTooltipOptions(overrides);
1673
- this.result.tooltip = tooltip;
1922
+ };
1923
+ builder = new FunnelBuilder(this.baseProduct);
1924
+ director = new VSECDirector(this.builder);
1925
+ constructor() {
1926
+ super();
1674
1927
  }
1675
- addCommons() {
1676
- const opts = {
1928
+ make() {
1929
+ const makeOpts = {
1930
+ valueFormatter: this.valueFormatter,
1677
1931
  palette: this.palette,
1932
+ colorResolver: this.colorResolver,
1678
1933
  };
1679
- merge$1(this.result, getCommons(opts));
1680
- }
1681
- getResult() {
1682
- return this.result;
1683
- }
1684
- ;
1685
- // No-ops
1686
- addPolar() { }
1687
- addXAxis(data, overrides, type) { }
1688
- addYAxis(data, overrides, type) { }
1689
- addRadiusAxis(data, overrides) { }
1690
- addAngleAxis(data, overrides) { }
1691
- addLegend() { }
1692
- addDataZoom() { }
1693
- addDataset(data, opts) { }
1694
- addGraphic() { }
1695
- // Setters
1696
- /**
1697
- * Permite inyectar un formateador de valores externo.
1698
- */
1699
- setValueFormatter(formatter) {
1700
- if (formatter) {
1701
- this.valueFormatter = formatter;
1702
- }
1703
- }
1704
- /**
1705
- * Permite inyectar una paleta de colores básica.
1706
- */
1707
- setPalette(palette) {
1708
- if (palette) {
1709
- this.palette = palette;
1710
- }
1711
- }
1712
- /**
1713
- * Permite inyectar un resolver de colores dinámico.
1714
- */
1715
- setColorResolver(resolver) {
1716
- if (resolver) {
1717
- this.colorResolver = resolver;
1718
- }
1934
+ this.director.makeFunnel(this.data, this.optionsOverrides, makeOpts);
1719
1935
  }
1936
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1937
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsFunnelComponent, isStandalone: true, selector: "vs-echarts-funnel", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div class=\"echarts-container\" echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\" \n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
1720
1938
  }
1939
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, decorators: [{
1940
+ type: Component,
1941
+ args: [{ selector: 'vs-echarts-funnel', standalone: true, imports: [
1942
+ CommonModule,
1943
+ NgxEchartsDirective,
1944
+ ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\" echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\" \n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
1945
+ }], ctorParameters: () => [] });
1721
1946
 
1722
1947
  /**
1723
- * SankeyBuilder
1948
+ * Builder principal.
1724
1949
  *
1725
- * Builder concreto para el gráfico Sankey (diagrama de flujos de izquierda a derecha).
1950
+ * Es muy completo, tiene soporte para ejes y sistema cartesiano polar.
1951
+ *
1952
+ * Puede verse utilizado en graficos como Lineas y Bars. Siempre que se pueda priorizar utilizar este.
1726
1953
  */
1727
- class SankeyBuilder {
1728
- baseProduct;
1954
+ class EChartBuilder {
1955
+ baseProduct = undefined;
1729
1956
  valueFormatter = (value) => value.toLocaleString();
1730
1957
  palette = [];
1958
+ // TODO: Hay que implementar un valor por defecto.
1731
1959
  colorResolver;
1732
1960
  result = {};
1733
1961
  constructor(baseProduct) {
@@ -1736,680 +1964,217 @@ class SankeyBuilder {
1736
1964
  reset() {
1737
1965
  this.result = {};
1738
1966
  }
1739
- addSeries(data, overrides) {
1740
- if (!data || !data.source || data.source.length === 0) {
1741
- return;
1742
- }
1743
- // Identificar medidas (todas las dimensiones excepto 'category')
1744
- const measureKeys = data.dimensions
1745
- .filter((d) => d.name !== "category")
1746
- .map((d) => d.name);
1747
- // Función auxiliar para sumarizar valores de medidas en caso de haber más de una
1748
- const getNodeValue = (node) => {
1749
- return measureKeys.reduce((sum, key) => sum + (Number(node[key]) || 0), 0);
1750
- };
1751
- const nodesMap = new Map();
1752
- const linksMap = new Map();
1753
- // Función recursiva para aplanar datos jerárquicos a nodos y enlaces
1754
- const traverse = (nodeList, level, parentId) => {
1755
- for (const node of nodeList) {
1756
- const category = node.category;
1757
- const currentId = `${category}___${level}`;
1758
- const value = getNodeValue(node);
1759
- nodesMap.set(currentId, (nodesMap.get(currentId) || 0) + value);
1760
- if (parentId) {
1761
- const linkKey = `${parentId}--->${currentId}`;
1762
- if (linksMap.has(linkKey)) {
1763
- linksMap.get(linkKey).value += value;
1764
- }
1765
- else {
1766
- linksMap.set(linkKey, {
1767
- source: parentId,
1768
- target: currentId,
1769
- value: value,
1770
- });
1771
- }
1772
- }
1773
- if (node.children && node.children.length > 0) {
1774
- traverse(node.children, level + 1, currentId);
1775
- }
1776
- }
1967
+ addCommons() {
1968
+ const opts = {
1969
+ palette: this.palette,
1777
1970
  };
1778
- // Comenzar el recorrido en el nivel 0
1779
- traverse(data.source, 0);
1780
- // al menos un nivel de links para dibujar.
1781
- if (linksMap.size === 0)
1971
+ merge$1(this.result, getCommons(opts));
1972
+ }
1973
+ /**
1974
+ *
1975
+ * @param data
1976
+ * @param overrides
1977
+ * @returns
1978
+ */
1979
+ addSeries(data, overrides, opts) {
1980
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
1782
1981
  return;
1783
- // Mapear nodos acumulados a la estructura requerida por ECharts (name y opcionalmente color, sin value redundante)
1784
- const nodes = Array.from(nodesMap.keys()).map((currentId) => {
1785
- const category = currentId.split("___")[0];
1786
- const nodeItem = {
1787
- name: currentId,
1982
+ this.result.dataset = {
1983
+ dimensions: data.dimensions,
1984
+ source: data.source
1985
+ };
1986
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
1987
+ const isPolar = opts?.coordinateSystem === 'polar';
1988
+ const isHorizontal = opts?.axisTypes?.x === 'value' && opts?.axisTypes?.y === 'category';
1989
+ const series = measureDims.map((dim) => {
1990
+ const friendlyName = dim.displayName || dim.name;
1991
+ const dimKey = dim.name;
1992
+ let encode = {
1993
+ x: 'category',
1994
+ y: dimKey
1788
1995
  };
1789
- if (this.colorResolver) {
1790
- const color = this.colorResolver({ name: category, data: nodeItem });
1791
- if (color) {
1792
- nodeItem.itemStyle = { color };
1996
+ if (isPolar) {
1997
+ encode = {
1998
+ angle: 'category',
1999
+ radius: dimKey
2000
+ };
2001
+ }
2002
+ else if (isHorizontal) {
2003
+ encode = {
2004
+ x: dimKey,
2005
+ y: 'category'
2006
+ };
2007
+ }
2008
+ const dynamicOverrides = {
2009
+ name: friendlyName,
2010
+ encode,
2011
+ label: {
2012
+ formatter: (params) => {
2013
+ const row = params.value;
2014
+ const rawValue = (row && typeof row === 'object') ? row[dimKey] : params.value;
2015
+ return this.formatCellValue(Number(rawValue ?? 0), dimKey);
2016
+ }
1793
2017
  }
2018
+ };
2019
+ const seriesOption = merge$1(dynamicOverrides, overrides);
2020
+ // Inyectar el resolver de color si existe
2021
+ if (this.colorResolver && seriesOption.itemStyle) {
2022
+ seriesOption.itemStyle.color = this.colorResolver;
1794
2023
  }
1795
- return nodeItem;
2024
+ return seriesOption;
1796
2025
  });
1797
- const dynamicSerieOptions = {
1798
- type: "sankey",
1799
- orient: "horizontal", // De izquierda a derecha
1800
- draggable: true,
1801
- emphasis: {
1802
- focus: "adjacency",
1803
- },
1804
- lineStyle: {
1805
- color: "source",
1806
- opacity: 0.25,
1807
- curveness: 0.5,
1808
- },
1809
- label: {
1810
- show: true,
1811
- position: "right",
1812
- fontFamily: "'Inter', 'Roboto', 'Open Sans', sans-serif",
1813
- fontSize: 10,
1814
- formatter: (params) => {
1815
- // Remover el sufijo de nivel de la etiqueta visual
1816
- return params.name.split("___")[0];
1817
- },
1818
- },
1819
- roam: true,
1820
- data: nodes,
1821
- links: Array.from(linksMap.values()),
1822
- };
1823
- const serie = merge$1({}, dynamicSerieOptions, overrides);
1824
- if (this.colorResolver && !serie.itemStyle) {
1825
- serie.itemStyle = {};
1826
- }
1827
- if (this.colorResolver && serie.itemStyle) {
1828
- serie.itemStyle.color = this.colorResolver;
1829
- }
1830
- this.result.series = serie;
2026
+ this.result.series = series;
1831
2027
  }
2028
+ /**
2029
+ * TODO: Mejorar funcion, no me convence como se implemento el tooltip formatter.
2030
+ * @param data
2031
+ * @param overrides
2032
+ */
1832
2033
  addTooltip(data, overrides) {
2034
+ // inyecto formateador a overrides de tooltip
1833
2035
  merge$1(overrides, {
1834
- formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2036
+ formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1835
2037
  });
1836
2038
  const tooltip = getTooltipOptions(overrides);
1837
2039
  this.result.tooltip = tooltip;
1838
2040
  }
1839
- addPolar() { }
1840
- addXAxis(data, overrides, type) { }
1841
- addYAxis(data, overrides, type) { }
1842
- addRadiusAxis(data, overrides) { }
1843
- addAngleAxis(data, overrides) { }
1844
- addLegend() { }
1845
- addDataZoom() { }
1846
- addDataset(data, opts) { }
1847
- addCommons() {
1848
- const opts = {
1849
- palette: this.palette,
2041
+ addPolar() {
2042
+ const polar = [];
2043
+ polar.push({
2044
+ radius: '65%',
2045
+ center: ["50%", "44%"],
2046
+ });
2047
+ this.result.polar = polar;
2048
+ }
2049
+ addXAxis(data, overrides, type = 'category') {
2050
+ const xAxis = [];
2051
+ const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
2052
+ const valueAxisOptions = this.getValueAxisOptions(data, overrides);
2053
+ if (type == 'category') {
2054
+ xAxis.push(categoryAxisOptions);
2055
+ }
2056
+ else if (type == 'value') {
2057
+ xAxis.push(valueAxisOptions);
2058
+ }
2059
+ this.result.xAxis = xAxis;
2060
+ }
2061
+ addYAxis(data, overrides, type = 'value') {
2062
+ const yAxis = [];
2063
+ const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
2064
+ const valueAxisOptions = this.getValueAxisOptions(data, overrides);
2065
+ if (type == 'category') {
2066
+ yAxis.push(categoryAxisOptions);
2067
+ }
2068
+ else if (type == 'value') {
2069
+ yAxis.push(valueAxisOptions);
2070
+ }
2071
+ this.result.yAxis = yAxis;
2072
+ }
2073
+ addRadiusAxis(data, overrides) {
2074
+ const radiusAxis = [];
2075
+ /** estilos exclusivos hardcodeados para el eje de valores del sistema de coordenadas polar*/
2076
+ const radiusAxisOverrides = {
2077
+ zlevel: 10,
2078
+ axisTick: {
2079
+ show: false,
2080
+ },
2081
+ axisLabel: {
2082
+ margin: 2,
2083
+ fontSize: 8,
2084
+ align: 'right',
2085
+ rotate: 20,
2086
+ verticalAlign: 'top',
2087
+ },
2088
+ splitLine: {
2089
+ show: true,
2090
+ lineStyle: {
2091
+ opacity: 0.2,
2092
+ type: 'solid',
2093
+ }
2094
+ },
2095
+ axisLine: {
2096
+ lineStyle: {
2097
+ type: 'dashed',
2098
+ },
2099
+ }
1850
2100
  };
1851
- merge$1(this.result, getCommons(opts));
2101
+ const radialAxisOptions = getValueAxisOptions(radiusAxisOverrides);
2102
+ radiusAxis.push(radialAxisOptions);
2103
+ this.result.radiusAxis = radiusAxis;
2104
+ }
2105
+ addAngleAxis(data, overrides) {
2106
+ const angleAxis = [];
2107
+ const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
2108
+ angleAxis.push(categoryAxisOptions);
2109
+ this.result.angleAxis = angleAxis;
2110
+ }
2111
+ addLegend() {
2112
+ this.result.legend = getLegendOptions();
1852
2113
  }
2114
+ // No-ops for ring charts
1853
2115
  addGraphic() { }
2116
+ addDataset(data, opts) { }
2117
+ addDataZoom() {
2118
+ this.result.dataZoom = [
2119
+ {
2120
+ type: 'inside'
2121
+ },
2122
+ {
2123
+ type: 'slider',
2124
+ height: 20,
2125
+ bottom: 60
2126
+ }
2127
+ ];
2128
+ }
1854
2129
  getResult() {
1855
2130
  return this.result;
1856
2131
  }
2132
+ ;
2133
+ /**
2134
+ * Formatea un valor utilizando el callback inyectado.
2135
+ */
2136
+ formatCellValue(value, key) {
2137
+ return this.valueFormatter(value, key);
2138
+ }
2139
+ // Setters
2140
+ /**
2141
+ * Permite inyectar un formateador de valores externo.
2142
+ */
1857
2143
  setValueFormatter(formatter) {
1858
2144
  if (formatter) {
1859
2145
  this.valueFormatter = formatter;
1860
2146
  }
1861
2147
  }
2148
+ /**
2149
+ * Permite inyectar una paleta de colores básica.
2150
+ */
1862
2151
  setPalette(palette) {
1863
2152
  if (palette) {
1864
2153
  this.palette = palette;
1865
2154
  }
1866
2155
  }
1867
- setColorResolver(resolver) {
1868
- if (resolver) {
1869
- this.colorResolver = resolver;
1870
- }
1871
- }
1872
- }
1873
-
1874
- /**
1875
- * Director de Builds.
1876
- */
1877
- class VSECDirector {
1878
- builder = undefined;
1879
- constructor(builder) {
1880
- this.builder = builder;
1881
- }
1882
- makeBar(data, overrides, opts = {}) {
1883
- if (this.builder instanceof EChartBuilder == false) {
1884
- return;
1885
- }
1886
- ;
1887
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1888
- x: 'category',
1889
- y: 'value',
1890
- // TODO agregar radius y angle axis
1891
- } } = opts;
1892
- this.builder.reset();
1893
- // El orden importa, primero callbacks y despues componentes.
1894
- // chart callbaks
1895
- if (valueFormatter)
1896
- this.builder.setValueFormatter(valueFormatter);
1897
- if (palette)
1898
- this.builder.setPalette(palette);
1899
- if (colorResolver)
1900
- this.builder.setColorResolver(colorResolver);
1901
- const product = this.builder.baseProduct;
1902
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1903
- // chart components
1904
- this.builder.addCommons();
1905
- const seriesOptions = { axisTypes };
1906
- this.builder.addSeries(data, seriesOverrides, seriesOptions);
1907
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1908
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1909
- this.builder.addTooltip(data, overrides.tooltip);
1910
- this.builder.addLegend();
1911
- }
1912
- makeBarRadial(data, overrides, opts = {}) {
1913
- if (this.builder instanceof EChartBuilder == false) {
1914
- return;
1915
- }
1916
- ;
1917
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1918
- x: 'category',
1919
- y: 'value',
1920
- // TODO agregar radius y angle axis
1921
- } } = opts;
1922
- this.builder.reset();
1923
- // El orden importa, primero callbacks y despues componentes.
1924
- // chart callbaks
1925
- if (valueFormatter)
1926
- this.builder.setValueFormatter(valueFormatter);
1927
- if (palette)
1928
- this.builder.setPalette(palette);
1929
- if (colorResolver)
1930
- this.builder.setColorResolver(colorResolver);
1931
- const product = this.builder.baseProduct;
1932
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1933
- // chart components
1934
- this.builder.addCommons();
1935
- const seriesOptions = { axisTypes, coordinateSystem: 'polar' };
1936
- this.builder.addSeries(data, seriesOverrides, seriesOptions);
1937
- this.builder.addPolar();
1938
- this.builder.addAngleAxis(data, overrides['axis']);
1939
- this.builder.addRadiusAxis(data, overrides['axis']);
1940
- this.builder.addTooltip(data, overrides.tooltip);
1941
- this.builder.addLegend();
1942
- }
1943
- makeLine(data, overrides, opts = {}) {
1944
- if (this.builder instanceof EChartBuilder == false) {
1945
- return;
1946
- }
1947
- ;
1948
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1949
- x: 'category',
1950
- y: 'value',
1951
- // TODO agregar radius y angle axis
1952
- } } = opts;
1953
- this.builder.reset();
1954
- // El orden importa, primero callbacks y despues componentes.
1955
- // chart callbaks
1956
- if (valueFormatter)
1957
- this.builder.setValueFormatter(valueFormatter);
1958
- if (palette)
1959
- this.builder.setPalette(palette);
1960
- if (colorResolver)
1961
- this.builder.setColorResolver(colorResolver);
1962
- const product = this.builder.baseProduct;
1963
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1964
- // chart components
1965
- this.builder.addCommons();
1966
- const seriesOptions = { axisTypes };
1967
- this.builder.addSeries(data, seriesOverrides, seriesOptions);
1968
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1969
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1970
- this.builder.addTooltip(data, overrides.tooltip);
1971
- this.builder.addLegend();
1972
- }
1973
- makeScatter(data, overrides, opts = {}) {
1974
- if (this.builder instanceof EChartBuilder == false) {
1975
- return;
1976
- }
1977
- ;
1978
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1979
- x: 'category',
1980
- y: 'value',
1981
- } } = opts;
1982
- this.builder.reset();
1983
- // El orden importa, primero callbacks y despues componentes.
1984
- // chart callbaks
1985
- if (valueFormatter)
1986
- this.builder.setValueFormatter(valueFormatter);
1987
- if (palette)
1988
- this.builder.setPalette(palette);
1989
- if (colorResolver)
1990
- this.builder.setColorResolver(colorResolver);
1991
- const product = this.builder.baseProduct;
1992
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1993
- // chart components
1994
- this.builder.addCommons();
1995
- const seriesOptions = { axisTypes };
1996
- this.builder.addSeries(data, seriesOverrides, seriesOptions);
1997
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1998
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1999
- this.builder.addTooltip(data, overrides.tooltip);
2000
- this.builder.addLegend();
2001
- }
2002
- makeRing(data, overrides, opts = {}) {
2003
- if (this.builder instanceof RingBuilder == false) {
2004
- return;
2005
- }
2006
- ;
2007
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
2008
- this.builder.reset();
2009
- // El orden importa, primero callbacks y despues componentes.
2010
- // chart callbaks
2011
- if (valueFormatter)
2012
- this.builder.setValueFormatter(valueFormatter);
2013
- if (palette)
2014
- this.builder.setPalette(palette);
2015
- if (colorResolver)
2016
- this.builder.setColorResolver(colorResolver);
2017
- const product = this.builder.baseProduct;
2018
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2019
- // chart components
2020
- this.builder.addCommons();
2021
- this.builder.addSeries(data, seriesOverrides);
2022
- this.builder.addGraphic();
2023
- this.builder.addTooltip(data, overrides.tooltip);
2024
- this.builder.addLegend();
2025
- }
2026
- makePie(data, overrides, opts = {}) {
2027
- if (this.builder instanceof PieBuilder == false) {
2028
- return;
2029
- }
2030
- ;
2031
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
2032
- this.builder.reset();
2033
- // El orden importa, primero callbacks y despues componentes.
2034
- // chart callbaks
2035
- if (valueFormatter)
2036
- this.builder.setValueFormatter(valueFormatter);
2037
- if (palette)
2038
- this.builder.setPalette(palette);
2039
- if (colorResolver)
2040
- this.builder.setColorResolver(colorResolver);
2041
- const product = this.builder.baseProduct;
2042
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2043
- // chart components
2044
- this.builder.addCommons();
2045
- this.builder.addSeries(data, seriesOverrides);
2046
- this.builder.addTooltip(data, overrides.tooltip);
2047
- this.builder.addLegend();
2048
- }
2049
- makeFunnel(data, overrides, opts = {}) {
2050
- if (this.builder instanceof FunnelBuilder == false) {
2051
- return;
2052
- }
2053
- ;
2054
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
2055
- this.builder.reset();
2056
- // El orden importa, primero callbacks y despues componentes.
2057
- // chart callbaks
2058
- if (valueFormatter)
2059
- this.builder.setValueFormatter(valueFormatter);
2060
- if (palette)
2061
- this.builder.setPalette(palette);
2062
- if (colorResolver)
2063
- this.builder.setColorResolver(colorResolver);
2064
- const product = this.builder.baseProduct;
2065
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2066
- // chart components
2067
- this.builder.addCommons();
2068
- this.builder.addSeries(data, seriesOverrides);
2069
- this.builder.addTooltip(data, overrides.tooltip);
2070
- this.builder.addLegend();
2071
- }
2072
- makeSunburst(data, overrides, opts = {}) {
2073
- if (this.builder instanceof SunburstBuilder == false) {
2074
- return;
2075
- }
2076
- ;
2077
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
2078
- this.builder.reset();
2079
- // El orden importa, primero callbacks y despues componentes.
2080
- // chart callbaks
2081
- if (valueFormatter)
2082
- this.builder.setValueFormatter(valueFormatter);
2083
- if (palette)
2084
- this.builder.setPalette(palette);
2085
- if (colorResolver)
2086
- this.builder.setColorResolver(colorResolver);
2087
- const product = this.builder.baseProduct;
2088
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2089
- // chart components
2090
- this.builder.addCommons();
2091
- this.builder.addSeries(data, seriesOverrides);
2092
- this.builder.addTooltip(data, overrides.tooltip);
2093
- }
2094
- makeSankey(data, overrides, opts = {}) {
2095
- if (this.builder instanceof SankeyBuilder == false) {
2096
- return;
2097
- }
2098
- ;
2099
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
2100
- this.builder.reset();
2101
- // El orden importa, primero callbacks y despues componentes.
2102
- // chart callbaks
2103
- if (valueFormatter)
2104
- this.builder.setValueFormatter(valueFormatter);
2105
- if (palette)
2106
- this.builder.setPalette(palette);
2107
- if (colorResolver)
2108
- this.builder.setColorResolver(colorResolver);
2109
- const product = this.builder.baseProduct;
2110
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2111
- // chart components
2112
- this.builder.addCommons();
2113
- this.builder.addSeries(data, seriesOverrides);
2114
- this.builder.addTooltip(data, overrides.tooltip);
2115
- }
2116
- makeBoxplot(data, overrides, opts = {}) {
2117
- if (this.builder instanceof BoxPlotBuilder == false) {
2118
- return;
2119
- }
2120
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
2121
- x: 'value',
2122
- y: 'category',
2123
- }, statisticsKeys = undefined, } = opts;
2124
- this.builder.reset();
2125
- // El orden importa, primero callbacks y despues componentes.
2126
- // chart callbaks
2127
- if (valueFormatter)
2128
- this.builder.setValueFormatter(valueFormatter);
2129
- if (palette)
2130
- this.builder.setPalette(palette);
2131
- if (colorResolver)
2132
- this.builder.setColorResolver(colorResolver);
2133
- const product = this.builder.baseProduct;
2134
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
2135
- // chart components
2136
- this.builder.addCommons();
2137
- const configOpts = { axisTypes };
2138
- this.builder.addDataset(data, { statisticsKeys });
2139
- this.builder.addSeries(data, seriesOverrides, configOpts);
2140
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
2141
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
2142
- this.builder.addTooltip(data, overrides.tooltip);
2143
- this.builder.addDataZoom();
2144
- }
2145
- }
2146
-
2147
- /**
2148
- * EchartsRingComponent
2149
- *
2150
- * Especialista en visualización de tipo Ring. Soporta multi-medidas y KPI central.
2151
- * @see {@link vs-echarts/docs/charts/ring-patterns.md}
2152
- */
2153
- class EchartsRingComponent extends BaseEchartsComponent {
2154
- baseSeriesOptions = {
2155
- type: 'pie',
2156
- center: ['50%', '50%'],
2157
- avoidLabelOverlap: true,
2158
- minAngle: 3,
2159
- selectedMode: 'single',
2160
- selectedOffset: 4,
2161
- itemStyle: {
2162
- borderColor: '#fff',
2163
- },
2164
- label: { show: false },
2165
- emphasis: {
2166
- scale: true,
2167
- scaleSize: 2,
2168
- },
2169
- select: {
2170
- itemStyle: {
2171
- borderColor: EChartsTokens.sBorderColor,
2172
- shadowColor: EChartsTokens.sShadowColor,
2173
- borderWidth: 1,
2174
- shadowBlur: 4,
2175
- },
2176
- },
2177
- animationType: 'scale',
2178
- animationEasing: 'elasticOut',
2179
- };
2180
- baseProduct = {
2181
- chartKey: 'ring',
2182
- baseOptions: {
2183
- series: this.baseSeriesOptions,
2184
- }
2185
- };
2186
- builder = new RingBuilder(this.baseProduct);
2187
- director = new VSECDirector(this.builder);
2188
- lastSelectedSeriesIndex = null;
2189
- lastSelectedDataIndex = null;
2190
- selectedPercent = null;
2191
- currentGraphicText = '';
2192
- constructor() {
2193
- super();
2194
- }
2195
- make() {
2196
- const makeOpts = {
2197
- valueFormatter: this.valueFormatter,
2198
- palette: this.palette,
2199
- colorResolver: this.colorResolver,
2200
- };
2201
- this.director.makeRing(this.data, this.optionsOverrides, makeOpts);
2202
- }
2203
- onInputChanges(changes) {
2204
- // Reset selection only if data changed
2205
- if (changes['data']) {
2206
- this.lastSelectedSeriesIndex = null;
2207
- this.lastSelectedDataIndex = null;
2208
- this.selectedPercent = null;
2209
- this.currentGraphicText = '';
2210
- }
2211
- super.onInputChanges(changes);
2212
- }
2213
2156
  /**
2214
- * Maneja clics en los sectores del ring.
2215
- * Soporta múltiples series (anillos) y actualiza el KPI central.
2216
- */
2217
- onChartClick(event) {
2218
- if (this.chartInstance && event && event.dataIndex !== undefined) {
2219
- const isSameSelection = event.seriesIndex === this.lastSelectedSeriesIndex &&
2220
- event.dataIndex === this.lastSelectedDataIndex;
2221
- if (isSameSelection) {
2222
- // Toggle OFF
2223
- this.lastSelectedSeriesIndex = null;
2224
- this.lastSelectedDataIndex = null;
2225
- this.selectedPercent = null;
2226
- this.setGraphicText('');
2227
- }
2228
- else {
2229
- // SELECT
2230
- this.lastSelectedSeriesIndex = event.seriesIndex;
2231
- this.lastSelectedDataIndex = event.dataIndex;
2232
- this.selectedPercent = (event.percent !== undefined) ? event.percent + '%' : '';
2233
- this.setGraphicText(this.selectedPercent);
2234
- }
2235
- this.chartClick.emit({
2236
- type: 'cross-filter',
2237
- data: {
2238
- category: event.name,
2239
- serie: event.seriesName,
2240
- value: event.value
2241
- }
2242
- });
2243
- }
2244
- }
2245
- onChartMouseOver(event) {
2246
- if (this.selectedPercent === null && event && event.percent !== undefined) {
2247
- this.setGraphicText(event.percent + '%');
2248
- }
2249
- }
2250
- onChartMouseOut(event) {
2251
- if (this.selectedPercent === null) {
2252
- this.setGraphicText('');
2253
- }
2254
- }
2255
- /**
2256
- * Actualiza el texto del Graphic central y persiste la selección en el modelo de opciones.
2157
+ * Permite inyectar un resolver de colores dinámico.
2257
2158
  */
2258
- setGraphicText(text) {
2259
- this.currentGraphicText = text;
2260
- // Persistencia de GRAPHIC (KPI central)
2261
- this.chartInstance?.setOption({
2262
- graphic: {
2263
- style: {
2264
- text: this.currentGraphicText,
2265
- opacity: this.currentGraphicText ? 1 : 0,
2266
- }
2267
- }
2268
- });
2269
- }
2270
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2271
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsRingComponent, isStandalone: true, selector: "vs-echarts-ring", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div class=\"echarts-container\" \n echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartMouseOver)=\"onChartMouseOver($event)\" \n (chartMouseOut)=\"onChartMouseOut($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n ", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
2272
- }
2273
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, decorators: [{
2274
- type: Component,
2275
- args: [{ selector: 'vs-echarts-ring', standalone: true, imports: [
2276
- CommonModule,
2277
- NgxEchartsDirective,
2278
- ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\" \n echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartMouseOver)=\"onChartMouseOver($event)\" \n (chartMouseOut)=\"onChartMouseOut($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n ", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2279
- }], ctorParameters: () => [] });
2280
-
2281
- /**
2282
- * EchartsPieComponent
2283
- *
2284
- * Especialista en visualización de tipo Pie (Torta y Anillos concéntricos).
2285
- * La primera serie se dibuja como un gráfico de torta tradicional (lleno) y
2286
- * las subsecuentes como anillos concéntricos alrededor.
2287
- */
2288
- class EchartsPieComponent extends BaseEchartsComponent {
2289
- baseSeriesOptions = {
2290
- type: 'pie',
2291
- center: ['50%', '50%'],
2292
- avoidLabelOverlap: true,
2293
- minAngle: 3,
2294
- selectedMode: 'single',
2295
- selectedOffset: 4,
2296
- emphasis: {
2297
- scale: true,
2298
- scaleSize: 2,
2299
- },
2300
- select: {
2301
- itemStyle: {
2302
- borderColor: EChartsTokens.sBorderColor,
2303
- shadowColor: EChartsTokens.sShadowColor,
2304
- borderWidth: 1,
2305
- shadowBlur: 4,
2306
- },
2307
- },
2308
- animationType: 'scale',
2309
- animationEasing: 'elasticOut',
2310
- };
2311
- baseProduct = {
2312
- chartKey: 'pie',
2313
- baseOptions: {
2314
- series: this.baseSeriesOptions,
2159
+ setColorResolver(resolver) {
2160
+ if (resolver) {
2161
+ this.colorResolver = resolver;
2315
2162
  }
2316
- };
2317
- builder = new PieBuilder(this.baseProduct);
2318
- director = new VSECDirector(this.builder);
2319
- constructor() {
2320
- super();
2321
2163
  }
2322
- make() {
2323
- const makeOpts = {
2324
- valueFormatter: this.valueFormatter,
2325
- palette: this.palette,
2326
- colorResolver: this.colorResolver,
2164
+ // Utils
2165
+ getCategoryAxisOptions(data, overrides) {
2166
+ // No explicit data needed on category axis when using ECharts dataset
2167
+ const categoryAxisOptionsOverrides = {
2168
+ ...overrides.categoryAxis[0]
2327
2169
  };
2328
- this.director.makePie(this.data, this.optionsOverrides, makeOpts);
2329
- }
2330
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2331
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsPieComponent, isStandalone: true, selector: "vs-echarts-pie", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div\n class=\"echarts-container\"\n echarts\n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
2332
- }
2333
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, decorators: [{
2334
- type: Component,
2335
- args: [{ selector: 'vs-echarts-pie', standalone: true, imports: [
2336
- CommonModule,
2337
- NgxEchartsDirective,
2338
- ], providers: [provideVSEcharts()], template: "<div\n class=\"echarts-container\"\n echarts\n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2339
- }], ctorParameters: () => [] });
2340
-
2341
- /**
2342
- * EchartsFunnelComponent
2343
- *
2344
- * Component for Funnel visualization. Supports single and multiple measures.
2345
- */
2346
- class EchartsFunnelComponent extends BaseEchartsComponent {
2347
- baseSeriesOptions = {
2348
- type: 'funnel',
2349
- left: '10%',
2350
- width: '80%',
2351
- minSize: '0.01%',
2352
- maxSize: '100%',
2353
- sort: 'descending',
2354
- gap: 2,
2355
- label: {
2356
- show: true,
2357
- position: 'inside'
2358
- },
2359
- labelLine: {
2360
- show: false
2361
- },
2362
- itemStyle: {
2363
- borderColor: '#fff',
2364
- borderWidth: 1
2365
- },
2366
- emphasis: {
2367
- label: {
2368
- fontSize: 16
2369
- }
2370
- },
2371
- selectedMode: 'single',
2372
- select: {
2373
- label: {
2374
- fontSize: 16
2375
- },
2376
- itemStyle: {
2377
- borderWidth: 1,
2378
- borderColor: EChartsTokens.sBorderColor,
2379
- shadowColor: EChartsTokens.sShadowColor,
2380
- shadowBlur: 6,
2381
- }
2382
- },
2383
- };
2384
- baseProduct = {
2385
- chartKey: 'funnel',
2386
- baseOptions: {
2387
- series: this.baseSeriesOptions,
2388
- }
2389
- };
2390
- builder = new FunnelBuilder(this.baseProduct);
2391
- director = new VSECDirector(this.builder);
2392
- constructor() {
2393
- super();
2170
+ const categoryAxisOptions = getCategoryAxisOptions(categoryAxisOptionsOverrides);
2171
+ return categoryAxisOptions;
2394
2172
  }
2395
- make() {
2396
- const makeOpts = {
2397
- valueFormatter: this.valueFormatter,
2398
- palette: this.palette,
2399
- colorResolver: this.colorResolver,
2400
- };
2401
- this.director.makeFunnel(this.data, this.optionsOverrides, makeOpts);
2173
+ getValueAxisOptions(data, overrides) {
2174
+ const valueAxisOptions = getValueAxisOptions();
2175
+ return valueAxisOptions;
2402
2176
  }
2403
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2404
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsFunnelComponent, isStandalone: true, selector: "vs-echarts-funnel", providers: [provideVSEcharts()], usesInheritance: true, ngImport: i0, template: "<div class=\"echarts-container\" echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\" \n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\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"] }] });
2405
2177
  }
2406
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, decorators: [{
2407
- type: Component,
2408
- args: [{ selector: 'vs-echarts-funnel', standalone: true, imports: [
2409
- CommonModule,
2410
- NgxEchartsDirective,
2411
- ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\" echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\" \n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event, { fixPieDataIndexInside: true })\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2412
- }], ctorParameters: () => [] });
2413
2178
 
2414
2179
  class EchartsBarComponent extends BaseEchartsComponent {
2415
2180
  /**
@@ -2815,6 +2580,100 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2815
2580
  ], providers: [provideVSEcharts()], template: "<div class=\"echarts-container\" echarts \n [options]=\"{}\"\n [initOpts]=\"initOptions\" \n [autoResize]=\"true\" \n (chartInit)=\"onChartInit($event)\" \n (chartClick)=\"onChartClick($event)\" \n (chartSelectChanged)=\"onChartSelectChanged($event)\"\n></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2816
2581
  }], ctorParameters: () => [] });
2817
2582
 
2583
+ class SunburstBuilder {
2584
+ baseProduct;
2585
+ valueFormatter = (value) => value.toLocaleString();
2586
+ palette = [];
2587
+ // TODO: Hay que implementar un valor por defecto.
2588
+ colorResolver;
2589
+ result = {};
2590
+ constructor(baseProduct) {
2591
+ this.baseProduct = baseProduct;
2592
+ }
2593
+ reset() {
2594
+ this.result = {};
2595
+ }
2596
+ addSeries(data, overrides) {
2597
+ if (!data || !data.source || data.source.length === 0) {
2598
+ return;
2599
+ }
2600
+ const sunburstData = mapHierarchicalData(data.source, data.dimensions);
2601
+ const depth = getTreeDepth(sunburstData);
2602
+ const levels = [];
2603
+ for (let i = 0; i <= depth; i++) {
2604
+ levels.push({
2605
+ label: {
2606
+ show: false,
2607
+ },
2608
+ });
2609
+ }
2610
+ const dynamiSerieOptions = {
2611
+ name: '',
2612
+ data: sunburstData,
2613
+ levels: levels
2614
+ };
2615
+ const serie = merge$1({}, dynamiSerieOptions, overrides);
2616
+ if (this.colorResolver) {
2617
+ if (!serie.itemStyle) {
2618
+ serie.itemStyle = {};
2619
+ }
2620
+ serie.itemStyle.color = this.colorResolver;
2621
+ }
2622
+ this.result.series = serie;
2623
+ }
2624
+ addTooltip(data, overrides) {
2625
+ merge$1(overrides, {
2626
+ formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2627
+ });
2628
+ const tooltip = getTooltipOptions(overrides);
2629
+ this.result.tooltip = tooltip;
2630
+ }
2631
+ addCommons() {
2632
+ const opts = {
2633
+ palette: this.palette,
2634
+ };
2635
+ merge$1(this.result, getCommons(opts));
2636
+ }
2637
+ addGraphic() { }
2638
+ getResult() {
2639
+ return this.result;
2640
+ }
2641
+ ;
2642
+ addPolar() { }
2643
+ addXAxis(data, overrides, type) { }
2644
+ addYAxis(data, overrides, type) { }
2645
+ addRadiusAxis(data, overrides) { }
2646
+ addAngleAxis(data, overrides) { }
2647
+ addLegend() { }
2648
+ addDataZoom() { }
2649
+ addDataset(data, opts) { }
2650
+ // Setters
2651
+ /**
2652
+ * Permite inyectar un formateador de valores externo.
2653
+ */
2654
+ setValueFormatter(formatter) {
2655
+ if (formatter) {
2656
+ this.valueFormatter = formatter;
2657
+ }
2658
+ }
2659
+ /**
2660
+ * Permite inyectar una paleta de colores básica.
2661
+ */
2662
+ setPalette(palette) {
2663
+ if (palette) {
2664
+ this.palette = palette;
2665
+ }
2666
+ }
2667
+ /**
2668
+ * Permite inyectar un resolver de colores dinámico.
2669
+ */
2670
+ setColorResolver(resolver) {
2671
+ if (resolver) {
2672
+ this.colorResolver = resolver;
2673
+ }
2674
+ }
2675
+ }
2676
+
2818
2677
  class EChartsSunburstComponent extends BaseEchartsComponent {
2819
2678
  baseSeriesOptions = {
2820
2679
  type: 'sunburst',
@@ -2863,6 +2722,158 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2863
2722
  ], providers: [provideVSEcharts()], template: "<div\n class=\"echarts-container\"\n echarts\n [options]=\"{}\"\n [initOpts]=\"initOptions\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\"\n></div>", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2864
2723
  }] });
2865
2724
 
2725
+ /**
2726
+ * SankeyBuilder
2727
+ *
2728
+ * Builder concreto para el gráfico Sankey (diagrama de flujos de izquierda a derecha).
2729
+ */
2730
+ class SankeyBuilder {
2731
+ baseProduct;
2732
+ valueFormatter = (value) => value.toLocaleString();
2733
+ palette = [];
2734
+ colorResolver;
2735
+ result = {};
2736
+ constructor(baseProduct) {
2737
+ this.baseProduct = baseProduct;
2738
+ }
2739
+ reset() {
2740
+ this.result = {};
2741
+ }
2742
+ addSeries(data, overrides) {
2743
+ if (!data || !data.source || data.source.length === 0) {
2744
+ return;
2745
+ }
2746
+ // Identificar medidas (todas las dimensiones excepto 'category')
2747
+ const measureKeys = data.dimensions
2748
+ .filter((d) => d.name !== "category")
2749
+ .map((d) => d.name);
2750
+ // Función auxiliar para sumarizar valores de medidas en caso de haber más de una
2751
+ const getNodeValue = (node) => {
2752
+ return measureKeys.reduce((sum, key) => sum + (Number(node[key]) || 0), 0);
2753
+ };
2754
+ const nodesMap = new Map();
2755
+ const linksMap = new Map();
2756
+ // Función recursiva para aplanar datos jerárquicos a nodos y enlaces
2757
+ const traverse = (nodeList, level, parentId) => {
2758
+ for (const node of nodeList) {
2759
+ const category = node.category;
2760
+ const currentId = `${category}___${level}`;
2761
+ const value = getNodeValue(node);
2762
+ nodesMap.set(currentId, (nodesMap.get(currentId) || 0) + value);
2763
+ if (parentId) {
2764
+ const linkKey = `${parentId}--->${currentId}`;
2765
+ if (linksMap.has(linkKey)) {
2766
+ linksMap.get(linkKey).value += value;
2767
+ }
2768
+ else {
2769
+ linksMap.set(linkKey, {
2770
+ source: parentId,
2771
+ target: currentId,
2772
+ value: value,
2773
+ });
2774
+ }
2775
+ }
2776
+ if (node.children && node.children.length > 0) {
2777
+ traverse(node.children, level + 1, currentId);
2778
+ }
2779
+ }
2780
+ };
2781
+ // Comenzar el recorrido en el nivel 0
2782
+ traverse(data.source, 0);
2783
+ // al menos un nivel de links para dibujar.
2784
+ if (linksMap.size === 0)
2785
+ return;
2786
+ // Mapear nodos acumulados a la estructura requerida por ECharts (name y opcionalmente color, sin value redundante)
2787
+ const nodes = Array.from(nodesMap.keys()).map((currentId) => {
2788
+ const category = currentId.split("___")[0];
2789
+ const nodeItem = {
2790
+ name: currentId,
2791
+ };
2792
+ if (this.colorResolver) {
2793
+ const color = this.colorResolver({ name: category, data: nodeItem });
2794
+ if (color) {
2795
+ nodeItem.itemStyle = { color };
2796
+ }
2797
+ }
2798
+ return nodeItem;
2799
+ });
2800
+ const dynamicSerieOptions = {
2801
+ type: "sankey",
2802
+ orient: "horizontal", // De izquierda a derecha
2803
+ draggable: true,
2804
+ emphasis: {
2805
+ focus: "adjacency",
2806
+ },
2807
+ lineStyle: {
2808
+ color: "source",
2809
+ opacity: 0.25,
2810
+ curveness: 0.5,
2811
+ },
2812
+ label: {
2813
+ show: true,
2814
+ position: "right",
2815
+ fontFamily: "'Inter', 'Roboto', 'Open Sans', sans-serif",
2816
+ fontSize: 10,
2817
+ formatter: (params) => {
2818
+ // Remover el sufijo de nivel de la etiqueta visual
2819
+ return params.name.split("___")[0];
2820
+ },
2821
+ },
2822
+ roam: true,
2823
+ data: nodes,
2824
+ links: Array.from(linksMap.values()),
2825
+ };
2826
+ const serie = merge$1({}, dynamicSerieOptions, overrides);
2827
+ if (this.colorResolver && !serie.itemStyle) {
2828
+ serie.itemStyle = {};
2829
+ }
2830
+ if (this.colorResolver && serie.itemStyle) {
2831
+ serie.itemStyle.color = this.colorResolver;
2832
+ }
2833
+ this.result.series = serie;
2834
+ }
2835
+ addTooltip(data, overrides) {
2836
+ merge$1(overrides, {
2837
+ formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2838
+ });
2839
+ const tooltip = getTooltipOptions(overrides);
2840
+ this.result.tooltip = tooltip;
2841
+ }
2842
+ addPolar() { }
2843
+ addXAxis(data, overrides, type) { }
2844
+ addYAxis(data, overrides, type) { }
2845
+ addRadiusAxis(data, overrides) { }
2846
+ addAngleAxis(data, overrides) { }
2847
+ addLegend() { }
2848
+ addDataZoom() { }
2849
+ addDataset(data, opts) { }
2850
+ addCommons() {
2851
+ const opts = {
2852
+ palette: this.palette,
2853
+ };
2854
+ merge$1(this.result, getCommons(opts));
2855
+ }
2856
+ addGraphic() { }
2857
+ getResult() {
2858
+ return this.result;
2859
+ }
2860
+ setValueFormatter(formatter) {
2861
+ if (formatter) {
2862
+ this.valueFormatter = formatter;
2863
+ }
2864
+ }
2865
+ setPalette(palette) {
2866
+ if (palette) {
2867
+ this.palette = palette;
2868
+ }
2869
+ }
2870
+ setColorResolver(resolver) {
2871
+ if (resolver) {
2872
+ this.colorResolver = resolver;
2873
+ }
2874
+ }
2875
+ }
2876
+
2866
2877
  class EchartsSankeyComponent extends BaseEchartsComponent {
2867
2878
  baseSeriesOptions = {
2868
2879
  type: 'sankey',
@@ -2962,5 +2973,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2962
2973
  * Generated bundle index. Do not edit.
2963
2974
  */
2964
2975
 
2965
- export { BaseEchartsComponent, EChartsAreaComponent, EChartsAreaStackComponent, EChartsBarStackedComponent, EChartsBarStackedRadialComponent, EChartsHBarComponent, EChartsHBarStackedComponent, EChartsHBoxplotComponent, EChartsSunburstComponent, EchartsBarComponent, EchartsFunnelComponent, EchartsLineComponent, EchartsPieComponent, EchartsRingComponent, EchartsSankeyComponent, EchartsScatterComponent, defaultOptionsOverrides, initializeEcharts, provideVSEcharts };
2976
+ export { BaseEchartsComponent, EChartsAreaComponent, EChartsAreaStackComponent, EChartsBarStackedComponent, EChartsBarStackedRadialComponent, EChartsHBarComponent, EChartsHBarStackedComponent, EChartsHBoxplotComponent, EChartsSunburstComponent, EchartsBarComponent, EchartsFunnelComponent, EchartsLineComponent, EchartsPieComponent, EchartsRingComponent, EchartsSankeyComponent, EchartsScatterComponent, VSEchartsConfigService, defaultOptionsOverrides };
2966
2977
  //# sourceMappingURL=visionaris-bruno-vs-echarts.mjs.map