@visionaris-bruno/vs-echarts 8.4.0 → 8.4.2

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.
@@ -188,11 +188,11 @@ class BaseEchartsComponent {
188
188
  data = { dimensions: [], source: [] };
189
189
  optionsOverrides = defaultOptionsOverrides();
190
190
  /** Paleta de colores básica */
191
- palette;
191
+ palette = getDefaultPalette();
192
192
  /** Resolver de colores dinámico (Callback) */
193
193
  colorResolver;
194
194
  /** Formateador de valores para etiquetas y tooltips */
195
- valueFormatter;
195
+ valueFormatter = (value) => value.toLocaleString();
196
196
  chartClick = new EventEmitter();
197
197
  /** Subject para debouncing de actualizaciones. ReplaySubject asegura no perder el primer renderizado. */
198
198
  updateSubject = new ReplaySubject(1);
@@ -1030,11 +1030,10 @@ class BoxPlotBuilder {
1030
1030
  this.result.series = [serie];
1031
1031
  }
1032
1032
  addTooltip(data, overrides) {
1033
- const trigger = overrides.trigger;
1034
1033
  const mainMeassureKey = data.dimensions.filter(d => d.name != 'category')[0].name;
1035
1034
  const valueFormatter = this.valueFormatter;
1036
1035
  function tooltipFormatter(params) {
1037
- const tooltipEventData = params[0];
1036
+ const tooltipEventData = Array.isArray(params) ? params[0] : params;
1038
1037
  const title = `<b>${tooltipEventData.name}</b></br>`;
1039
1038
  let htmlbody = ``;
1040
1039
  for (const datakey in tooltipEventData.data) {
@@ -1129,344 +1128,236 @@ class BoxPlotBuilder {
1129
1128
  }
1130
1129
 
1131
1130
  /**
1132
- * Director de Builds.
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.
1133
1136
  */
1134
- class VSECDirector {
1135
- builder = undefined;
1136
- constructor(builder) {
1137
- this.builder = builder;
1138
- }
1139
- makeBar(data, overrides, opts = {}) {
1140
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1141
- x: 'category',
1142
- y: 'value',
1143
- // TODO agregar radius y angle axis
1144
- } } = opts;
1145
- this.builder.reset();
1146
- // El orden importa, primero callbaks y despues componentes.
1147
- //chart callbacks
1148
- this.builder.setValueFormatter(valueFormatter);
1149
- this.builder.setPalette(palette);
1150
- this.builder.setColorResolver(colorResolver);
1151
- const product = this.builder.baseProduct;
1152
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1153
- // chart components
1154
- this.builder.addCommons();
1155
- const layoutOpts = { axisTypes };
1156
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1157
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1158
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1159
- this.builder.addTooltip(data, overrides.tooltip);
1160
- this.builder.addLegend();
1161
- }
1162
- makeBarRadial(data, overrides, opts = {}) {
1163
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1164
- x: 'category',
1165
- y: 'value',
1166
- // TODO agregar radius y angle axis
1167
- } } = opts;
1168
- this.builder.reset();
1169
- // El orden importa, primero callbaks y despues componentes.
1170
- //chart callbacks
1171
- this.builder.setValueFormatter(valueFormatter);
1172
- this.builder.setPalette(palette);
1173
- this.builder.setColorResolver(colorResolver);
1174
- const product = this.builder.baseProduct;
1175
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1176
- // chart components
1177
- this.builder.addCommons();
1178
- const layoutOpts = { axisTypes, coordinateSystem: 'polar' };
1179
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1180
- this.builder.addPolar();
1181
- this.builder.addAngleAxis(data, overrides['axis']);
1182
- this.builder.addRadiusAxis(data, overrides['axis']);
1183
- this.builder.addTooltip(data, overrides.tooltip);
1184
- this.builder.addLegend();
1185
- }
1186
- makeLine(data, overrides, opts = {}) {
1187
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1188
- x: 'category',
1189
- y: 'value',
1190
- // TODO agregar radius y angle axis
1191
- } } = opts;
1192
- this.builder.reset();
1193
- // El orden importa, primero callbaks y despues componentes.
1194
- //chart callbacks
1195
- this.builder.setValueFormatter(valueFormatter);
1196
- this.builder.setPalette(palette);
1197
- this.builder.setColorResolver(colorResolver);
1198
- const product = this.builder.baseProduct;
1199
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1200
- // chart components
1201
- this.builder.addCommons();
1202
- const layoutOpts = { axisTypes };
1203
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1204
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1205
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1206
- this.builder.addTooltip(data, overrides.tooltip);
1207
- this.builder.addLegend();
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;
1208
1146
  }
1209
- makeScatter(data, overrides, opts = {}) {
1210
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1211
- x: 'category',
1212
- y: 'value',
1213
- } } = opts;
1214
- this.builder.reset();
1215
- // El orden importa, primero callbacks y despues componentes.
1216
- this.builder.setValueFormatter(valueFormatter);
1217
- this.builder.setPalette(palette);
1218
- this.builder.setColorResolver(colorResolver);
1219
- const product = this.builder.baseProduct;
1220
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1221
- // chart components
1222
- this.builder.addCommons();
1223
- const layoutOpts = { axisTypes };
1224
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1225
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1226
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1227
- this.builder.addTooltip(data, overrides.tooltip);
1228
- this.builder.addLegend();
1147
+ reset() {
1148
+ this.result = {};
1229
1149
  }
1230
- makeRing(data, overrides, opts = {}) {
1231
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1232
- this.builder.reset();
1233
- // El orden importa, primero callbacks y despues componentes.
1234
- this.builder.setValueFormatter(valueFormatter);
1235
- this.builder.setPalette(palette);
1236
- this.builder.setColorResolver(colorResolver);
1237
- const product = this.builder.baseProduct;
1238
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1239
- // chart components
1240
- this.builder.addCommons();
1241
- const layoutOpts = {};
1242
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1243
- this.builder.addGraphic();
1244
- this.builder.addTooltip(data, overrides.tooltip);
1245
- this.builder.addLegend();
1150
+ addCommons() {
1151
+ const opts = {
1152
+ palette: this.palette,
1153
+ };
1154
+ merge$1(this.result, getCommons(opts));
1246
1155
  }
1247
- makePie(data, overrides, opts = {}) {
1248
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1249
- this.builder.reset();
1250
- // El orden importa, primero callbacks y despues componentes.
1251
- this.builder.setValueFormatter(valueFormatter);
1252
- this.builder.setPalette(palette);
1253
- this.builder.setColorResolver(colorResolver);
1254
- const product = this.builder.baseProduct;
1255
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1256
- // chart components
1257
- this.builder.addCommons();
1258
- const layoutOpts = {};
1259
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1260
- this.builder.addTooltip(data, overrides.tooltip);
1261
- this.builder.addLegend();
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;
1262
1210
  }
1263
- makeFunnel(data, overrides, opts = {}) {
1264
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1265
- this.builder.reset();
1266
- // El orden importa, primero callbacks y despues componentes.
1267
- this.builder.setValueFormatter(valueFormatter);
1268
- this.builder.setPalette(palette);
1269
- this.builder.setColorResolver(colorResolver);
1270
- const product = this.builder.baseProduct;
1271
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1272
- // chart components
1273
- this.builder.addCommons();
1274
- const layoutOpts = {};
1275
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1276
- this.builder.addTooltip(data, overrides.tooltip);
1277
- this.builder.addLegend();
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;
1278
1223
  }
1279
- makeSunburst(data, overrides, opts = {}) {
1280
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1281
- this.builder.reset();
1282
- // El orden importa, primero callbacks y despues componentes.
1283
- this.builder.setValueFormatter(valueFormatter);
1284
- this.builder.setPalette(palette);
1285
- this.builder.setColorResolver(colorResolver);
1286
- const product = this.builder.baseProduct;
1287
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1288
- // chart components
1289
- this.builder.addCommons();
1290
- const layoutOpts = {};
1291
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1292
- this.builder.addTooltip(data, overrides.tooltip);
1293
- // this.builder.addLegend();
1224
+ addPolar() {
1225
+ const polar = [];
1226
+ polar.push({
1227
+ radius: '65%',
1228
+ center: ["50%", "44%"],
1229
+ });
1230
+ this.result.polar = polar;
1294
1231
  }
1295
- makeSankey(data, overrides, opts = {}) {
1296
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1297
- this.builder.reset();
1298
- // El orden importa, primero callbacks y despues componentes.
1299
- this.builder.setValueFormatter(valueFormatter);
1300
- this.builder.setPalette(palette);
1301
- this.builder.setColorResolver(colorResolver);
1302
- const product = this.builder.baseProduct;
1303
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1304
- // chart components
1305
- this.builder.addCommons();
1306
- const layoutOpts = {};
1307
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1308
- this.builder.addTooltip(data, overrides.tooltip);
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;
1309
1243
  }
1310
- makeBoxplot(data, overrides, opts = {}) {
1311
- if (this.builder instanceof BoxPlotBuilder == false) {
1312
- return;
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);
1313
1250
  }
1314
- const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, axisTypes = {
1315
- x: 'value',
1316
- y: 'category',
1317
- }, statisticsKeys = undefined, } = opts;
1318
- this.builder.reset();
1319
- // El orden importa, primero callbacks y despues componentes.
1320
- this.builder.setValueFormatter(valueFormatter);
1321
- this.builder.setPalette(palette);
1322
- this.builder.setColorResolver(colorResolver);
1323
- const product = this.builder.baseProduct;
1324
- const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1325
- // chart components
1326
- this.builder.addCommons();
1327
- const layoutOpts = { axisTypes };
1328
- this.builder.addDataset(data, { statisticsKeys });
1329
- this.builder.addSeries(data, seriesOverrides, layoutOpts);
1330
- this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1331
- this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1332
- this.builder.addTooltip(data, overrides.tooltip);
1333
- this.builder.addDataZoom();
1251
+ else if (type == 'value') {
1252
+ yAxis.push(valueAxisOptions);
1253
+ }
1254
+ this.result.yAxis = yAxis;
1334
1255
  }
1335
- }
1336
-
1337
- /**
1338
- * EchartsRingComponent
1339
- *
1340
- * Especialista en visualización de tipo Ring. Soporta multi-medidas y KPI central.
1341
- * @see {@link vs-echarts/docs/charts/ring-patterns.md}
1342
- */
1343
- class EchartsRingComponent extends BaseEchartsComponent {
1344
- baseSeriesOptions = {
1345
- type: 'pie',
1346
- center: ['50%', '50%'],
1347
- avoidLabelOverlap: true,
1348
- minAngle: 3,
1349
- selectedMode: 'single',
1350
- selectedOffset: 4,
1351
- itemStyle: {
1352
- borderColor: '#fff',
1353
- },
1354
- label: { show: false },
1355
- emphasis: {
1356
- scale: true,
1357
- scaleSize: 2,
1358
- },
1359
- select: {
1360
- itemStyle: {
1361
- borderColor: EChartsTokens.sBorderColor,
1362
- shadowColor: EChartsTokens.sShadowColor,
1363
- borderWidth: 1,
1364
- shadowBlur: 4,
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,
1365
1263
  },
1366
- },
1367
- animationType: 'scale',
1368
- animationEasing: 'elasticOut',
1369
- };
1370
- baseProduct = {
1371
- chartKey: 'ring',
1372
- baseOptions: {
1373
- series: this.baseSeriesOptions,
1374
- }
1375
- };
1376
- builder = new RingBuilder(this.baseProduct);
1377
- director = new VSECDirector(this.builder);
1378
- lastSelectedSeriesIndex = null;
1379
- lastSelectedDataIndex = null;
1380
- selectedPercent = null;
1381
- currentGraphicText = '';
1382
- constructor() {
1383
- super();
1384
- }
1385
- make() {
1386
- const makeOpts = {
1387
- valueFormatter: this.valueFormatter,
1388
- palette: this.palette,
1389
- colorResolver: this.colorResolver,
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
+ }
1390
1283
  };
1391
- this.director.makeRing(this.data, this.optionsOverrides, makeOpts);
1284
+ const radialAxisOptions = getValueAxisOptions(radiusAxisOverrides);
1285
+ radiusAxis.push(radialAxisOptions);
1286
+ this.result.radiusAxis = radiusAxis;
1392
1287
  }
1393
- onInputChanges(changes) {
1394
- // Reset selection only if data changed
1395
- if (changes['data']) {
1396
- this.lastSelectedSeriesIndex = null;
1397
- this.lastSelectedDataIndex = null;
1398
- this.selectedPercent = null;
1399
- this.currentGraphicText = '';
1400
- }
1401
- super.onInputChanges(changes);
1288
+ addAngleAxis(data, overrides) {
1289
+ const angleAxis = [];
1290
+ const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1291
+ angleAxis.push(categoryAxisOptions);
1292
+ this.result.angleAxis = angleAxis;
1293
+ }
1294
+ addLegend() {
1295
+ this.result.legend = getLegendOptions();
1296
+ }
1297
+ // No-ops for ring charts
1298
+ addGraphic() { }
1299
+ addDataset(data) { }
1300
+ addDataZoom() {
1301
+ this.result.dataZoom = [
1302
+ {
1303
+ type: 'inside'
1304
+ },
1305
+ {
1306
+ type: 'slider',
1307
+ height: 20,
1308
+ bottom: 60
1309
+ }
1310
+ ];
1311
+ }
1312
+ getResult() {
1313
+ return this.result;
1402
1314
  }
1315
+ ;
1403
1316
  /**
1404
- * Maneja clics en los sectores del ring.
1405
- * Soporta múltiples series (anillos) y actualiza el KPI central.
1317
+ * Formatea un valor utilizando el callback inyectado.
1406
1318
  */
1407
- onChartClick(event) {
1408
- if (this.chartInstance && event && event.dataIndex !== undefined) {
1409
- const isSameSelection = event.seriesIndex === this.lastSelectedSeriesIndex &&
1410
- event.dataIndex === this.lastSelectedDataIndex;
1411
- if (isSameSelection) {
1412
- // Toggle OFF
1413
- this.lastSelectedSeriesIndex = null;
1414
- this.lastSelectedDataIndex = null;
1415
- this.selectedPercent = null;
1416
- this.setGraphicText('');
1417
- }
1418
- else {
1419
- // SELECT
1420
- this.lastSelectedSeriesIndex = event.seriesIndex;
1421
- this.lastSelectedDataIndex = event.dataIndex;
1422
- this.selectedPercent = (event.percent !== undefined) ? event.percent + '%' : '';
1423
- this.setGraphicText(this.selectedPercent);
1424
- }
1425
- this.chartClick.emit({
1426
- type: 'cross-filter',
1427
- data: {
1428
- category: event.name,
1429
- serie: event.seriesName,
1430
- value: event.value
1431
- }
1432
- });
1433
- }
1319
+ formatCellValue(value, key) {
1320
+ return this.valueFormatter(value, key);
1434
1321
  }
1435
- onChartMouseOver(event) {
1436
- if (this.selectedPercent === null && event && event.percent !== undefined) {
1437
- this.setGraphicText(event.percent + '%');
1322
+ // Setters
1323
+ /**
1324
+ * Permite inyectar un formateador de valores externo.
1325
+ */
1326
+ setValueFormatter(formatter) {
1327
+ if (formatter) {
1328
+ this.valueFormatter = formatter;
1438
1329
  }
1439
1330
  }
1440
- onChartMouseOut(event) {
1441
- if (this.selectedPercent === null) {
1442
- this.setGraphicText('');
1331
+ /**
1332
+ * Permite inyectar una paleta de colores básica.
1333
+ */
1334
+ setPalette(palette) {
1335
+ if (palette) {
1336
+ this.palette = palette;
1443
1337
  }
1444
1338
  }
1445
1339
  /**
1446
- * Actualiza el texto del Graphic central y persiste la selección en el modelo de opciones.
1340
+ * Permite inyectar un resolver de colores dinámico.
1447
1341
  */
1448
- setGraphicText(text) {
1449
- this.currentGraphicText = text;
1450
- // Persistencia de GRAPHIC (KPI central)
1451
- this.chartInstance?.setOption({
1452
- graphic: {
1453
- style: {
1454
- text: this.currentGraphicText,
1455
- opacity: this.currentGraphicText ? 1 : 0,
1456
- }
1457
- }
1458
- });
1342
+ setColorResolver(resolver) {
1343
+ if (resolver) {
1344
+ this.colorResolver = resolver;
1345
+ }
1346
+ }
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;
1355
+ }
1356
+ getValueAxisOptions(data, overrides) {
1357
+ const valueAxisOptions = getValueAxisOptions();
1358
+ return valueAxisOptions;
1459
1359
  }
1460
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1461
- 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"] }] });
1462
1360
  }
1463
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsRingComponent, decorators: [{
1464
- type: Component,
1465
- args: [{ selector: 'vs-echarts-ring', standalone: true, imports: [
1466
- CommonModule,
1467
- NgxEchartsDirective,
1468
- ], 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"] }]
1469
- }], ctorParameters: () => [] });
1470
1361
 
1471
1362
  /**
1472
1363
  * PieBuilder
@@ -1631,78 +1522,18 @@ class PieBuilder {
1631
1522
  }
1632
1523
 
1633
1524
  /**
1634
- * EchartsPieComponent
1525
+ * FunnelBuilder
1635
1526
  *
1636
- * Especialista en visualización de tipo Pie (Torta y Anillos concéntricos).
1637
- * La primera serie se dibuja como un gráfico de torta tradicional (lleno) y
1638
- * las subsecuentes como anillos concéntricos alrededor.
1527
+ * Concrete builder for Funnel charts.
1639
1528
  */
1640
- class EchartsPieComponent extends BaseEchartsComponent {
1641
- baseSeriesOptions = {
1642
- type: 'pie',
1643
- center: ['50%', '50%'],
1644
- avoidLabelOverlap: true,
1645
- minAngle: 3,
1646
- selectedMode: 'single',
1647
- selectedOffset: 4,
1648
- emphasis: {
1649
- scale: true,
1650
- scaleSize: 2,
1651
- },
1652
- select: {
1653
- itemStyle: {
1654
- borderColor: EChartsTokens.sBorderColor,
1655
- shadowColor: EChartsTokens.sShadowColor,
1656
- borderWidth: 1,
1657
- shadowBlur: 4,
1658
- },
1659
- },
1660
- animationType: 'scale',
1661
- animationEasing: 'elasticOut',
1662
- };
1663
- baseProduct = {
1664
- chartKey: 'pie',
1665
- baseOptions: {
1666
- series: this.baseSeriesOptions,
1667
- }
1668
- };
1669
- builder = new PieBuilder(this.baseProduct);
1670
- director = new VSECDirector(this.builder);
1671
- constructor() {
1672
- super();
1673
- }
1674
- make() {
1675
- const makeOpts = {
1676
- valueFormatter: this.valueFormatter,
1677
- palette: this.palette,
1678
- colorResolver: this.colorResolver,
1679
- };
1680
- this.director.makePie(this.data, this.optionsOverrides, makeOpts);
1681
- }
1682
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1683
- 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"] }] });
1684
- }
1685
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsPieComponent, decorators: [{
1686
- type: Component,
1687
- args: [{ selector: 'vs-echarts-pie', standalone: true, imports: [
1688
- CommonModule,
1689
- NgxEchartsDirective,
1690
- ], 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"] }]
1691
- }], ctorParameters: () => [] });
1692
-
1693
- /**
1694
- * FunnelBuilder
1695
- *
1696
- * Concrete builder for Funnel charts.
1697
- */
1698
- class FunnelBuilder {
1699
- baseProduct;
1700
- valueFormatter = (value) => value.toLocaleString();
1701
- palette = [];
1702
- colorResolver;
1703
- result = {};
1704
- constructor(baseProduct) {
1705
- this.baseProduct = baseProduct;
1529
+ class FunnelBuilder {
1530
+ baseProduct;
1531
+ valueFormatter = (value) => value.toLocaleString();
1532
+ palette = [];
1533
+ colorResolver;
1534
+ result = {};
1535
+ constructor(baseProduct) {
1536
+ this.baseProduct = baseProduct;
1706
1537
  }
1707
1538
  reset() {
1708
1539
  this.result = {};
@@ -1794,91 +1625,10 @@ class FunnelBuilder {
1794
1625
  }
1795
1626
  }
1796
1627
 
1797
- /**
1798
- * EchartsFunnelComponent
1799
- *
1800
- * Component for Funnel visualization. Supports single and multiple measures.
1801
- */
1802
- class EchartsFunnelComponent extends BaseEchartsComponent {
1803
- baseSeriesOptions = {
1804
- type: 'funnel',
1805
- left: '10%',
1806
- width: '80%',
1807
- minSize: '0.01%',
1808
- maxSize: '100%',
1809
- sort: 'descending',
1810
- gap: 2,
1811
- label: {
1812
- show: true,
1813
- position: 'inside'
1814
- },
1815
- labelLine: {
1816
- show: false
1817
- },
1818
- itemStyle: {
1819
- borderColor: '#fff',
1820
- borderWidth: 1
1821
- },
1822
- emphasis: {
1823
- label: {
1824
- fontSize: 16
1825
- }
1826
- },
1827
- selectedMode: 'single',
1828
- select: {
1829
- label: {
1830
- fontSize: 16
1831
- },
1832
- itemStyle: {
1833
- borderWidth: 1,
1834
- borderColor: EChartsTokens.sBorderColor,
1835
- shadowColor: EChartsTokens.sShadowColor,
1836
- shadowBlur: 6,
1837
- }
1838
- },
1839
- };
1840
- baseProduct = {
1841
- chartKey: 'funnel',
1842
- baseOptions: {
1843
- series: this.baseSeriesOptions,
1844
- }
1845
- };
1846
- builder = new FunnelBuilder(this.baseProduct);
1847
- director = new VSECDirector(this.builder);
1848
- constructor() {
1849
- super();
1850
- }
1851
- make() {
1852
- const makeOpts = {
1853
- valueFormatter: this.valueFormatter,
1854
- palette: this.palette,
1855
- colorResolver: this.colorResolver,
1856
- };
1857
- this.director.makeFunnel(this.data, this.optionsOverrides, makeOpts);
1858
- }
1859
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1860
- 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"] }] });
1861
- }
1862
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsFunnelComponent, decorators: [{
1863
- type: Component,
1864
- args: [{ selector: 'vs-echarts-funnel', standalone: true, imports: [
1865
- CommonModule,
1866
- NgxEchartsDirective,
1867
- ], 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"] }]
1868
- }], ctorParameters: () => [] });
1869
-
1870
- /**
1871
- * Builder principal.
1872
- *
1873
- * Es muy completo, tiene soporte para ejes y sistema cartesiano polar.
1874
- *
1875
- * Puede verse utilizado en graficos como Lineas y Bars. Siempre que se pueda priorizar utilizar este.
1876
- */
1877
- class EChartBuilder {
1878
- baseProduct = undefined;
1628
+ class SunburstBuilder {
1629
+ baseProduct;
1879
1630
  valueFormatter = (value) => value.toLocaleString();
1880
1631
  palette = [];
1881
- // TODO: Hay que implementar un valor por defecto.
1882
1632
  colorResolver;
1883
1633
  result = {};
1884
1634
  constructor(baseProduct) {
@@ -1887,217 +1637,779 @@ class EChartBuilder {
1887
1637
  reset() {
1888
1638
  this.result = {};
1889
1639
  }
1890
- addCommons() {
1891
- const opts = {
1892
- palette: this.palette,
1893
- };
1894
- merge$1(this.result, getCommons(opts));
1895
- }
1896
- /**
1897
- *
1898
- * @param data
1899
- * @param overrides
1900
- * @returns
1901
- */
1902
- addSeries(data, overrides, opts) {
1903
- if (!data || !data.dimensions || !data.source || data.source.length === 0)
1640
+ addSeries(data, overrides) {
1641
+ if (!data || !data.source || data.source.length === 0) {
1904
1642
  return;
1905
- this.result.dataset = {
1906
- dimensions: data.dimensions,
1907
- source: data.source
1908
- };
1909
- const measureDims = data.dimensions.filter(d => d.name !== 'category');
1910
- const isPolar = opts?.coordinateSystem === 'polar';
1911
- const isHorizontal = opts?.axisTypes?.x === 'value' && opts?.axisTypes?.y === 'category';
1912
- const series = measureDims.map((dim) => {
1913
- const friendlyName = dim.displayName || dim.name;
1914
- const dimKey = dim.name;
1915
- let encode = {
1916
- x: 'category',
1917
- y: dimKey
1918
- };
1919
- if (isPolar) {
1920
- encode = {
1921
- angle: 'category',
1922
- radius: dimKey
1923
- };
1924
- }
1925
- else if (isHorizontal) {
1926
- encode = {
1927
- x: dimKey,
1928
- y: 'category'
1929
- };
1930
- }
1931
- const dynamicOverrides = {
1932
- name: friendlyName,
1933
- encode,
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({
1934
1649
  label: {
1935
- formatter: (params) => {
1936
- const row = params.value;
1937
- const rawValue = (row && typeof row === 'object') ? row[dimKey] : params.value;
1938
- return this.formatCellValue(Number(rawValue ?? 0), dimKey);
1939
- }
1940
- }
1941
- };
1942
- const seriesOption = merge$1(dynamicOverrides, overrides);
1943
- // Inyectar el resolver de color si existe
1944
- if (this.colorResolver && seriesOption.itemStyle) {
1945
- seriesOption.itemStyle.color = this.colorResolver;
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 = {};
1946
1663
  }
1947
- return seriesOption;
1948
- });
1949
- this.result.series = series;
1664
+ serie.itemStyle.color = this.colorResolver;
1665
+ }
1666
+ this.result.series = serie;
1950
1667
  }
1951
- /**
1952
- * TODO: Mejorar funcion, no me convence como se implemento el tooltip formatter.
1953
- * @param data
1954
- * @param overrides
1955
- */
1956
1668
  addTooltip(data, overrides) {
1957
- // inyecto formateador a overrides de tooltip
1958
1669
  merge$1(overrides, {
1959
- formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1670
+ formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
1960
1671
  });
1961
1672
  const tooltip = getTooltipOptions(overrides);
1962
1673
  this.result.tooltip = tooltip;
1963
1674
  }
1964
- addPolar() {
1965
- const polar = [];
1966
- polar.push({
1967
- radius: '65%',
1968
- center: ["50%", "44%"],
1969
- });
1970
- this.result.polar = polar;
1675
+ addCommons() {
1676
+ const opts = {
1677
+ palette: this.palette,
1678
+ };
1679
+ merge$1(this.result, getCommons(opts));
1971
1680
  }
1972
- addXAxis(data, overrides, type = 'category') {
1973
- const xAxis = [];
1974
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1975
- const valueAxisOptions = this.getValueAxisOptions(data, overrides);
1976
- if (type == 'category') {
1977
- xAxis.push(categoryAxisOptions);
1978
- }
1979
- else if (type == 'value') {
1980
- xAxis.push(valueAxisOptions);
1981
- }
1982
- this.result.xAxis = xAxis;
1681
+ getResult() {
1682
+ return this.result;
1983
1683
  }
1984
- addYAxis(data, overrides, type = 'value') {
1985
- const yAxis = [];
1986
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
1987
- const valueAxisOptions = this.getValueAxisOptions(data, overrides);
1988
- if (type == 'category') {
1989
- yAxis.push(categoryAxisOptions);
1990
- }
1991
- else if (type == 'value') {
1992
- yAxis.push(valueAxisOptions);
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;
1993
1702
  }
1994
- this.result.yAxis = yAxis;
1995
1703
  }
1996
- addRadiusAxis(data, overrides) {
1997
- const radiusAxis = [];
1998
- /** estilos exclusivos hardcodeados para el eje de valores del sistema de coordenadas polar*/
1999
- const radiusAxisOverrides = {
2000
- zlevel: 10,
2001
- axisTick: {
2002
- show: false,
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
+ }
1719
+ }
1720
+ }
1721
+
1722
+ /**
1723
+ * SankeyBuilder
1724
+ *
1725
+ * Builder concreto para el gráfico Sankey (diagrama de flujos de izquierda a derecha).
1726
+ */
1727
+ class SankeyBuilder {
1728
+ baseProduct;
1729
+ valueFormatter = (value) => value.toLocaleString();
1730
+ palette = [];
1731
+ colorResolver;
1732
+ result = {};
1733
+ constructor(baseProduct) {
1734
+ this.baseProduct = baseProduct;
1735
+ }
1736
+ reset() {
1737
+ this.result = {};
1738
+ }
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
+ }
1777
+ };
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)
1782
+ 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,
1788
+ };
1789
+ if (this.colorResolver) {
1790
+ const color = this.colorResolver({ name: category, data: nodeItem });
1791
+ if (color) {
1792
+ nodeItem.itemStyle = { color };
1793
+ }
1794
+ }
1795
+ return nodeItem;
1796
+ });
1797
+ const dynamicSerieOptions = {
1798
+ type: "sankey",
1799
+ orient: "horizontal", // De izquierda a derecha
1800
+ draggable: true,
1801
+ emphasis: {
1802
+ focus: "adjacency",
2003
1803
  },
2004
- axisLabel: {
2005
- margin: 2,
2006
- fontSize: 8,
2007
- align: 'right',
2008
- rotate: 20,
2009
- verticalAlign: 'top',
1804
+ lineStyle: {
1805
+ color: "source",
1806
+ opacity: 0.25,
1807
+ curveness: 0.5,
2010
1808
  },
2011
- splitLine: {
1809
+ label: {
2012
1810
  show: true,
2013
- lineStyle: {
2014
- opacity: 0.2,
2015
- type: 'solid',
2016
- }
2017
- },
2018
- axisLine: {
2019
- lineStyle: {
2020
- type: 'dashed',
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];
2021
1817
  },
2022
- }
1818
+ },
1819
+ roam: true,
1820
+ data: nodes,
1821
+ links: Array.from(linksMap.values()),
2023
1822
  };
2024
- const radialAxisOptions = getValueAxisOptions(radiusAxisOverrides);
2025
- radiusAxis.push(radialAxisOptions);
2026
- this.result.radiusAxis = radiusAxis;
2027
- }
2028
- addAngleAxis(data, overrides) {
2029
- const angleAxis = [];
2030
- const categoryAxisOptions = this.getCategoryAxisOptions(data, overrides);
2031
- angleAxis.push(categoryAxisOptions);
2032
- this.result.angleAxis = angleAxis;
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;
2033
1831
  }
2034
- addLegend() {
2035
- this.result.legend = getLegendOptions();
1832
+ addTooltip(data, overrides) {
1833
+ merge$1(overrides, {
1834
+ formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
1835
+ });
1836
+ const tooltip = getTooltipOptions(overrides);
1837
+ this.result.tooltip = tooltip;
2036
1838
  }
2037
- // No-ops for ring charts
2038
- addGraphic() { }
1839
+ addPolar() { }
1840
+ addXAxis(data, overrides, type) { }
1841
+ addYAxis(data, overrides, type) { }
1842
+ addRadiusAxis(data, overrides) { }
1843
+ addAngleAxis(data, overrides) { }
1844
+ addLegend() { }
1845
+ addDataZoom() { }
2039
1846
  addDataset(data, opts) { }
2040
- addDataZoom() {
2041
- this.result.dataZoom = [
2042
- {
2043
- type: 'inside'
2044
- },
2045
- {
2046
- type: 'slider',
2047
- height: 20,
2048
- bottom: 60
2049
- }
2050
- ];
1847
+ addCommons() {
1848
+ const opts = {
1849
+ palette: this.palette,
1850
+ };
1851
+ merge$1(this.result, getCommons(opts));
2051
1852
  }
1853
+ addGraphic() { }
2052
1854
  getResult() {
2053
1855
  return this.result;
2054
1856
  }
2055
- ;
2056
- /**
2057
- * Formatea un valor utilizando el callback inyectado.
2058
- */
2059
- formatCellValue(value, key) {
2060
- return this.valueFormatter(value, key);
2061
- }
2062
- // Setters
2063
- /**
2064
- * Permite inyectar un formateador de valores externo.
2065
- */
2066
1857
  setValueFormatter(formatter) {
2067
1858
  if (formatter) {
2068
1859
  this.valueFormatter = formatter;
2069
1860
  }
2070
1861
  }
2071
- /**
2072
- * Permite inyectar una paleta de colores básica.
2073
- */
2074
1862
  setPalette(palette) {
2075
1863
  if (palette) {
2076
1864
  this.palette = palette;
2077
1865
  }
2078
1866
  }
2079
- /**
2080
- * Permite inyectar un resolver de colores dinámico.
2081
- */
2082
1867
  setColorResolver(resolver) {
2083
1868
  if (resolver) {
2084
1869
  this.colorResolver = resolver;
2085
1870
  }
2086
1871
  }
2087
- // Utils
2088
- getCategoryAxisOptions(data, overrides) {
2089
- // No explicit data needed on category axis when using ECharts dataset
2090
- const categoryAxisOptionsOverrides = {
2091
- ...overrides.categoryAxis[0]
2092
- };
2093
- const categoryAxisOptions = getCategoryAxisOptions(categoryAxisOptionsOverrides);
2094
- return categoryAxisOptions;
1872
+ }
1873
+
1874
+ /**
1875
+ * Director de Builds.
1876
+ */
1877
+ class VSECDirector {
1878
+ builder = undefined;
1879
+ constructor(builder) {
1880
+ this.builder = builder;
2095
1881
  }
2096
- getValueAxisOptions(data, overrides) {
2097
- const valueAxisOptions = getValueAxisOptions();
2098
- return valueAxisOptions;
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
+ /**
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.
2257
+ */
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,
2315
+ }
2316
+ };
2317
+ builder = new PieBuilder(this.baseProduct);
2318
+ director = new VSECDirector(this.builder);
2319
+ constructor() {
2320
+ super();
2321
+ }
2322
+ make() {
2323
+ const makeOpts = {
2324
+ valueFormatter: this.valueFormatter,
2325
+ palette: this.palette,
2326
+ colorResolver: this.colorResolver,
2327
+ };
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();
2394
+ }
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);
2099
2402
  }
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"] }] });
2100
2405
  }
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: () => [] });
2101
2413
 
2102
2414
  class EchartsBarComponent extends BaseEchartsComponent {
2103
2415
  /**
@@ -2503,100 +2815,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2503
2815
  ], 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"] }]
2504
2816
  }], ctorParameters: () => [] });
2505
2817
 
2506
- class SunburstBuilder {
2507
- baseProduct;
2508
- valueFormatter = (value) => value.toLocaleString();
2509
- palette = [];
2510
- // TODO: Hay que implementar un valor por defecto.
2511
- colorResolver;
2512
- result = {};
2513
- constructor(baseProduct) {
2514
- this.baseProduct = baseProduct;
2515
- }
2516
- reset() {
2517
- this.result = {};
2518
- }
2519
- addSeries(data, overrides) {
2520
- if (!data || !data.source || data.source.length === 0) {
2521
- return;
2522
- }
2523
- const sunburstData = mapHierarchicalData(data.source, data.dimensions);
2524
- const depth = getTreeDepth(sunburstData);
2525
- const levels = [];
2526
- for (let i = 0; i <= depth; i++) {
2527
- levels.push({
2528
- label: {
2529
- show: false,
2530
- },
2531
- });
2532
- }
2533
- const dynamiSerieOptions = {
2534
- name: '',
2535
- data: sunburstData,
2536
- levels: levels
2537
- };
2538
- const serie = merge$1({}, dynamiSerieOptions, overrides);
2539
- if (this.colorResolver) {
2540
- if (!serie.itemStyle) {
2541
- serie.itemStyle = {};
2542
- }
2543
- serie.itemStyle.color = this.colorResolver;
2544
- }
2545
- this.result.series = serie;
2546
- }
2547
- addTooltip(data, overrides) {
2548
- merge$1(overrides, {
2549
- formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2550
- });
2551
- const tooltip = getTooltipOptions(overrides);
2552
- this.result.tooltip = tooltip;
2553
- }
2554
- addCommons() {
2555
- const opts = {
2556
- palette: this.palette,
2557
- };
2558
- merge$1(this.result, getCommons(opts));
2559
- }
2560
- addGraphic() { }
2561
- getResult() {
2562
- return this.result;
2563
- }
2564
- ;
2565
- addPolar() { }
2566
- addXAxis(data, overrides, type) { }
2567
- addYAxis(data, overrides, type) { }
2568
- addRadiusAxis(data, overrides) { }
2569
- addAngleAxis(data, overrides) { }
2570
- addLegend() { }
2571
- addDataZoom() { }
2572
- addDataset(data, opts) { }
2573
- // Setters
2574
- /**
2575
- * Permite inyectar un formateador de valores externo.
2576
- */
2577
- setValueFormatter(formatter) {
2578
- if (formatter) {
2579
- this.valueFormatter = formatter;
2580
- }
2581
- }
2582
- /**
2583
- * Permite inyectar una paleta de colores básica.
2584
- */
2585
- setPalette(palette) {
2586
- if (palette) {
2587
- this.palette = palette;
2588
- }
2589
- }
2590
- /**
2591
- * Permite inyectar un resolver de colores dinámico.
2592
- */
2593
- setColorResolver(resolver) {
2594
- if (resolver) {
2595
- this.colorResolver = resolver;
2596
- }
2597
- }
2598
- }
2599
-
2600
2818
  class EChartsSunburstComponent extends BaseEchartsComponent {
2601
2819
  baseSeriesOptions = {
2602
2820
  type: 'sunburst',
@@ -2645,158 +2863,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2645
2863
  ], 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"] }]
2646
2864
  }] });
2647
2865
 
2648
- /**
2649
- * SankeyBuilder
2650
- *
2651
- * Builder concreto para el gráfico Sankey (diagrama de flujos de izquierda a derecha).
2652
- */
2653
- class SankeyBuilder {
2654
- baseProduct;
2655
- valueFormatter = (value) => value.toLocaleString();
2656
- palette = [];
2657
- colorResolver;
2658
- result = {};
2659
- constructor(baseProduct) {
2660
- this.baseProduct = baseProduct;
2661
- }
2662
- reset() {
2663
- this.result = {};
2664
- }
2665
- addSeries(data, overrides) {
2666
- if (!data || !data.source || data.source.length === 0) {
2667
- return;
2668
- }
2669
- // Identificar medidas (todas las dimensiones excepto 'category')
2670
- const measureKeys = data.dimensions
2671
- .filter((d) => d.name !== "category")
2672
- .map((d) => d.name);
2673
- // Función auxiliar para sumarizar valores de medidas en caso de haber más de una
2674
- const getNodeValue = (node) => {
2675
- return measureKeys.reduce((sum, key) => sum + (Number(node[key]) || 0), 0);
2676
- };
2677
- const nodesMap = new Map();
2678
- const linksMap = new Map();
2679
- // Función recursiva para aplanar datos jerárquicos a nodos y enlaces
2680
- const traverse = (nodeList, level, parentId) => {
2681
- for (const node of nodeList) {
2682
- const category = node.category;
2683
- const currentId = `${category}___${level}`;
2684
- const value = getNodeValue(node);
2685
- nodesMap.set(currentId, (nodesMap.get(currentId) || 0) + value);
2686
- if (parentId) {
2687
- const linkKey = `${parentId}--->${currentId}`;
2688
- if (linksMap.has(linkKey)) {
2689
- linksMap.get(linkKey).value += value;
2690
- }
2691
- else {
2692
- linksMap.set(linkKey, {
2693
- source: parentId,
2694
- target: currentId,
2695
- value: value,
2696
- });
2697
- }
2698
- }
2699
- if (node.children && node.children.length > 0) {
2700
- traverse(node.children, level + 1, currentId);
2701
- }
2702
- }
2703
- };
2704
- // Comenzar el recorrido en el nivel 0
2705
- traverse(data.source, 0);
2706
- // al menos un nivel de links para dibujar.
2707
- if (linksMap.size === 0)
2708
- return;
2709
- // Mapear nodos acumulados a la estructura requerida por ECharts (name y opcionalmente color, sin value redundante)
2710
- const nodes = Array.from(nodesMap.keys()).map((currentId) => {
2711
- const category = currentId.split("___")[0];
2712
- const nodeItem = {
2713
- name: currentId,
2714
- };
2715
- if (this.colorResolver) {
2716
- const color = this.colorResolver({ name: category, data: nodeItem });
2717
- if (color) {
2718
- nodeItem.itemStyle = { color };
2719
- }
2720
- }
2721
- return nodeItem;
2722
- });
2723
- const dynamicSerieOptions = {
2724
- type: "sankey",
2725
- orient: "horizontal", // De izquierda a derecha
2726
- draggable: true,
2727
- emphasis: {
2728
- focus: "adjacency",
2729
- },
2730
- lineStyle: {
2731
- color: "source",
2732
- opacity: 0.25,
2733
- curveness: 0.5,
2734
- },
2735
- label: {
2736
- show: true,
2737
- position: "right",
2738
- fontFamily: "'Inter', 'Roboto', 'Open Sans', sans-serif",
2739
- fontSize: 10,
2740
- formatter: (params) => {
2741
- // Remover el sufijo de nivel de la etiqueta visual
2742
- return params.name.split("___")[0];
2743
- },
2744
- },
2745
- roam: true,
2746
- data: nodes,
2747
- links: Array.from(linksMap.values()),
2748
- };
2749
- const serie = merge$1({}, dynamicSerieOptions, overrides);
2750
- if (this.colorResolver && !serie.itemStyle) {
2751
- serie.itemStyle = {};
2752
- }
2753
- if (this.colorResolver && serie.itemStyle) {
2754
- serie.itemStyle.color = this.colorResolver;
2755
- }
2756
- this.result.series = serie;
2757
- }
2758
- addTooltip(data, overrides) {
2759
- merge$1(overrides, {
2760
- formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2761
- });
2762
- const tooltip = getTooltipOptions(overrides);
2763
- this.result.tooltip = tooltip;
2764
- }
2765
- addPolar() { }
2766
- addXAxis(data, overrides, type) { }
2767
- addYAxis(data, overrides, type) { }
2768
- addRadiusAxis(data, overrides) { }
2769
- addAngleAxis(data, overrides) { }
2770
- addLegend() { }
2771
- addDataZoom() { }
2772
- addDataset(data, opts) { }
2773
- addCommons() {
2774
- const opts = {
2775
- palette: this.palette,
2776
- };
2777
- merge$1(this.result, getCommons(opts));
2778
- }
2779
- addGraphic() { }
2780
- getResult() {
2781
- return this.result;
2782
- }
2783
- setValueFormatter(formatter) {
2784
- if (formatter) {
2785
- this.valueFormatter = formatter;
2786
- }
2787
- }
2788
- setPalette(palette) {
2789
- if (palette) {
2790
- this.palette = palette;
2791
- }
2792
- }
2793
- setColorResolver(resolver) {
2794
- if (resolver) {
2795
- this.colorResolver = resolver;
2796
- }
2797
- }
2798
- }
2799
-
2800
2866
  class EchartsSankeyComponent extends BaseEchartsComponent {
2801
2867
  baseSeriesOptions = {
2802
2868
  type: 'sankey',