@visionaris-bruno/vs-echarts 7.2.0 → 8.2.3

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.
@@ -1,7 +1,7 @@
1
1
  import { provideEchartsCore, NgxEchartsDirective } from 'ngx-echarts';
2
2
  import * as echarts from 'echarts/core';
3
- import { BarChart, PieChart, LineChart, ScatterChart, FunnelChart, SunburstChart } from 'echarts/charts';
4
- import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GraphicComponent, ToolboxComponent, PolarComponent } from 'echarts/components';
3
+ import { BarChart, PieChart, LineChart, ScatterChart, FunnelChart, SunburstChart, SankeyChart } from 'echarts/charts';
4
+ import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GraphicComponent, ToolboxComponent, PolarComponent, DatasetComponent } from 'echarts/components';
5
5
  import { CanvasRenderer, SVGRenderer } from 'echarts/renderers';
6
6
  import * as i0 from '@angular/core';
7
7
  import { EventEmitter, Output, Input, ViewChild, Directive, Component } from '@angular/core';
@@ -66,6 +66,7 @@ function initializeEcharts() {
66
66
  ScatterChart,
67
67
  FunnelChart,
68
68
  SunburstChart,
69
+ SankeyChart,
69
70
  TitleComponent,
70
71
  TooltipComponent,
71
72
  GridComponent,
@@ -75,6 +76,7 @@ function initializeEcharts() {
75
76
  GraphicComponent,
76
77
  ToolboxComponent,
77
78
  PolarComponent,
79
+ DatasetComponent,
78
80
  ]);
79
81
  initialized = true;
80
82
  }
@@ -161,6 +163,9 @@ function defaultOptionsOverrides() {
161
163
  sunburst: {
162
164
  series: {},
163
165
  },
166
+ sankey: {
167
+ series: {}
168
+ },
164
169
  };
165
170
  }
166
171
 
@@ -175,7 +180,7 @@ function defaultOptionsOverrides() {
175
180
  class BaseEchartsComponent {
176
181
  chartContainer;
177
182
  /** Datos normalizados para graficar */
178
- data = { categories: [], series: [] };
183
+ data = { dimensions: [], source: [] };
179
184
  optionsOverrides = defaultOptionsOverrides();
180
185
  /** Paleta de colores básica */
181
186
  palette;
@@ -406,12 +411,12 @@ function getLegendOptions(overrides) {
406
411
  return merge({}, defaults, overrides);
407
412
  }
408
413
  function getCategoryAxisOptions(overrides) {
409
- const categories = resolveCategoryNames(overrides?.data);
410
- const autoRotate = categories.length > 10 ? 45 : 0;
414
+ const categories = overrides?.data ? overrides.data.map(c => typeof c === 'string' ? c : (c?.name || String(c))) : undefined;
415
+ const autoRotate = categories && categories.length > 10 ? 45 : 0;
411
416
  const defaults = {
412
417
  type: 'category',
413
418
  triggerEvent: true,
414
- data: categories,
419
+ ...(categories ? { data: categories } : {}),
415
420
  axisLabel: {
416
421
  rotate: autoRotate,
417
422
  interval: 0,
@@ -429,7 +434,7 @@ function getCategoryAxisOptions(overrides) {
429
434
  }
430
435
  }
431
436
  };
432
- return merge({}, defaults, { ...overrides, data: categories });
437
+ return merge({}, defaults, overrides);
433
438
  }
434
439
  function getValueAxisOptions(overrides) {
435
440
  const defaults = {
@@ -598,12 +603,11 @@ function applyLineSelectionStyle(series, selectedCategoryIndex, selectedSeriesIn
598
603
  /**
599
604
  * Resolves the unique key of a category safely.
600
605
  */
601
- function getCategoryKey(categories, index) {
602
- if (!categories || !categories[index]) {
606
+ function getCategoryKey(dimensions, index) {
607
+ if (!dimensions || !dimensions[index]) {
603
608
  return String(index);
604
609
  }
605
- const node = categories[index];
606
- return node.key || node.name || String(index);
610
+ return dimensions[index].name;
607
611
  }
608
612
  /**
609
613
  * Resolves the numeric value from a series data point.
@@ -613,126 +617,56 @@ function getCellValue(val) {
613
617
  return 0;
614
618
  return val;
615
619
  }
616
- function flattenCategoriesToFirstLevel(categories) {
617
- if (!categories)
618
- return [];
619
- return categories.map(cat => ({
620
- name: cat.name,
621
- key: cat.key
622
- }));
623
- }
624
- function getLeafIndicesMap(categories) {
625
- let leafCount = 0;
626
- const topLevelLeaves = [];
627
- const countLeaves = (node) => {
628
- if (!node.children || node.children.length === 0) {
629
- return 1;
630
- }
631
- let sum = 0;
632
- for (const child of node.children) {
633
- sum += countLeaves(child);
634
- }
635
- return sum;
636
- };
637
- for (const cat of categories) {
638
- const leavesUnderCat = countLeaves(cat);
639
- topLevelLeaves.push({
640
- start: leafCount,
641
- end: leafCount + leavesUnderCat
642
- });
643
- leafCount += leavesUnderCat;
644
- }
645
- return {
646
- topLevelLeaves,
647
- totalLeaves: leafCount
648
- };
649
- }
650
- function collectLeafSeries(nodes, parentNamePath = []) {
651
- const leaves = [];
652
- for (const node of nodes) {
653
- const currentPath = [...parentNamePath, node.name];
654
- if (node.children && node.children.length > 0) {
655
- leaves.push(...collectLeafSeries(node.children, currentPath));
656
- }
657
- else {
658
- const combinedName = currentPath.join(' ');
659
- leaves.push({
660
- ...node,
661
- name: combinedName
662
- });
663
- }
664
- }
665
- return leaves;
666
- }
667
- function flattenEChartData(data) {
668
- if (!data)
669
- return { categories: [], series: [] };
670
- const categories = data.categories || [];
671
- const series = data.series || [];
672
- const topLevelCategories = flattenCategoriesToFirstLevel(categories);
673
- const { topLevelLeaves } = getLeafIndicesMap(categories);
674
- const flatSeries = collectLeafSeries(series).map(s => {
675
- const aggregatedData = [];
676
- for (const range of topLevelLeaves) {
677
- let sum = 0;
678
- for (let j = range.start; j < range.end; j++) {
679
- sum += s.data[j] ?? 0;
680
- }
681
- aggregatedData.push(sum);
682
- }
683
- return {
684
- ...s,
685
- data: aggregatedData
686
- };
687
- });
688
- return {
689
- categories: topLevelCategories,
690
- series: flatSeries
691
- };
692
- }
693
- /**
694
- * Normalizes category nodes into an array of string names for category axes.
695
- */
696
- function resolveCategoryNames(categories) {
697
- if (!categories)
698
- return [];
699
- return categories.map(c => {
700
- if (!c)
701
- return '';
702
- if (typeof c === 'string')
703
- return c;
704
- if (typeof c === 'object' && 'name' in c)
705
- return c.name;
706
- return String(c);
707
- });
708
- }
709
- /**
710
- * Maps categories and series data into a flat array of objects suitable for Pie/Ring/Funnel charts.
711
- */
712
- function mapToChartItems(categories, seriesNode) {
713
- if (!categories || !seriesNode || !seriesNode.data)
714
- return [];
715
- return categories.map((catNode, catIndex) => ({
716
- name: catNode.name,
717
- value: getCellValue(seriesNode.data[catIndex]),
718
- originalMeassureKey: seriesNode.originalKey || getCategoryKey(categories, catIndex)
719
- }));
720
- }
721
620
  /**
722
621
  * Formatter generator for 'item' trigger strategy in tooltips.
723
622
  */
724
623
  function getItemTooltipFormatter(data, formatCellValue, opts) {
725
- const totalSeries = data.series.length;
726
624
  return (params) => {
625
+ let title = '', subtitle = '', valFormatted = '', percentVal = '';
727
626
  const showSerieName = opts?.showSerieName ?? true;
728
- const seriesObj = data.series[params.seriesIndex];
729
- const key = params.data?.originalMeassureKey || seriesObj?.originalKey || getCategoryKey(data.categories, params.dataIndex);
730
- const valFormatted = formatCellValue(params.value, key);
627
+ const row = params.value;
628
+ const dimension = data.dimensions[params.seriesIndex + 1];
629
+ const dimensionName = dimension ? dimension.name : '';
630
+ if (!dimensionName)
631
+ return '';
632
+ const rawValue = (row && typeof row === 'object') ? row[dimensionName] : params.value;
731
633
  const serieName = showSerieName ? `${params.marker} ${params.seriesName}<br/ >` : '';
732
- const title = params.name;
733
- const subtitle = (params.percent !== undefined && totalSeries <= 1) ? '' : serieName;
734
- const percentText = params.percent !== undefined ? ` (${params.percent}%)` : '';
735
- return `<div style="text-align: center;"><b>${title}</b><br/>${subtitle}<b>${valFormatted}</b>${percentText}</div>`;
634
+ title = params.name || (row && typeof row === 'object' ? row.category : '');
635
+ subtitle = serieName;
636
+ valFormatted = formatCellValue(Number(rawValue ?? 0), dimensionName);
637
+ // pie/funnel series
638
+ if (params.percent != undefined) {
639
+ percentVal = ` (${params.percent}%)`;
640
+ if (data.dimensions.length <= 2) {
641
+ subtitle = '';
642
+ }
643
+ }
644
+ // sankey
645
+ if (params.dataType != undefined) {
646
+ const dataType = params.dataType;
647
+ subtitle = `${params.marker}<br/>`;
648
+ if (dataType === 'edge') {
649
+ const sourceName = params.data.source.split('___')[0];
650
+ const targetName = params.data.target.split('___')[0];
651
+ title = `${sourceName} &rarr; ${targetName}`;
652
+ valFormatted = formatCellValue(Number(params.value ?? 0), dimensionName);
653
+ }
654
+ else if (dataType === 'node') {
655
+ title = params.name.split('___')[0];
656
+ valFormatted = formatCellValue(Number(params.value ?? 0), dimensionName);
657
+ }
658
+ }
659
+ // sunburst treemap
660
+ if (params.treePathInfo) {
661
+ const lastLevelIndex = params.treePathInfo.length - 1, parentNodeIndex = lastLevelIndex - 1;
662
+ const parentNode = params.treePathInfo[parentNodeIndex];
663
+ const oldParentNode = params.treePathInfo[1];
664
+ if (parentNode.name) {
665
+ title = `${parentNode.name}->${params.name}`;
666
+ subtitle = `${params.marker} ${oldParentNode.name}<br/ >`;
667
+ }
668
+ }
669
+ return `<div style="text-align: center;"><b>${title}</b><br/>${subtitle}<b>${valFormatted}</b>${percentVal}</div>`;
736
670
  };
737
671
  }
738
672
  /**
@@ -746,13 +680,17 @@ function getAxisTooltipFormatter(data, formatCellValue, opts) {
746
680
  if (items.length === 0)
747
681
  return '';
748
682
  const firstItem = items[0];
749
- let html = `<b>${firstItem.name}</b><br/>`;
683
+ const row = firstItem.value;
684
+ const title = firstItem.name || (row && typeof row === 'object' ? row.category : '');
685
+ let html = `<b>${title}</b><br/>`;
750
686
  for (const item of items) {
751
- const seriesObj = data.series[item.seriesIndex];
752
- if (!seriesObj)
687
+ const dimension = data.dimensions[item.seriesIndex + 1];
688
+ const dimensionName = dimension ? dimension.name : '';
689
+ if (!dimensionName)
753
690
  continue;
754
- const key = seriesObj.originalKey || getCategoryKey(data.categories, item.dataIndex);
755
- const valFormatted = formatCellValue(item.value, key);
691
+ const itemRow = item.value;
692
+ const rawValue = (itemRow && typeof itemRow === 'object') ? itemRow[dimensionName] : item.value;
693
+ const valFormatted = formatCellValue(Number(rawValue ?? 0), dimensionName);
756
694
  html += `${item.marker} ${item.seriesName}: <b>${valFormatted}</b><br/>`;
757
695
  }
758
696
  return html;
@@ -786,63 +724,43 @@ function getCommons(opts) {
786
724
  return common;
787
725
  }
788
726
  /**
789
- * Maps hierarchical category nodes and an array of series into a nested structure
790
- * suitable for Sunburst/Treemap charts.
791
- * The categories form the levels closest to the center, and the series form the leaf level.
727
+ * Maps hierarchical category nodes and their measures into a nested structure
728
+ * suitable for Sunburst/Treemap charts from the IEChartDataNode children.
792
729
  */
793
- function mapHierarchicalData(categories, series) {
794
- if (!categories) {
730
+ function mapHierarchicalData(source, dimensions) {
731
+ if (!source) {
795
732
  return [];
796
733
  }
797
- let leafIndex = 0;
798
- function mapSeriesTree(seriesNodes, index) {
799
- if (!seriesNodes) {
800
- return [];
801
- }
802
- return seriesNodes.map(s => {
803
- const node = {
804
- name: s.name,
805
- };
806
- if (s.children && s.children.length > 0) {
807
- node.children = mapSeriesTree(s.children, index);
808
- }
809
- else {
810
- // Leaf series node: extract value from s.data[index]
811
- const dataVal = s.data?.[index];
812
- if (dataVal !== undefined && dataVal !== null) {
813
- const value = typeof dataVal === 'object' && 'value' in dataVal ? dataVal.value : dataVal;
814
- node.value = getCellValue(value);
815
- }
816
- else {
817
- node.value = 0;
818
- }
819
- node.originalMeassureKey = s.originalKey;
820
- }
821
- return node;
822
- });
823
- }
734
+ const measureDims = dimensions.filter(d => d.name !== 'category');
824
735
  function traverse(nodes) {
825
- return nodes.map(cat => {
826
- const node = {
827
- name: cat.name,
736
+ return nodes.map(node => {
737
+ const item = {
738
+ name: node.category,
828
739
  };
829
- if (cat.children && cat.children.length > 0) {
830
- node.children = traverse(cat.children);
740
+ if (node.children && node.children.length > 0) {
741
+ item.children = traverse(node.children);
831
742
  }
832
743
  else {
833
- // Leaf category node: append the series tree as children
834
- if (series && series.length > 0) {
835
- node.children = mapSeriesTree(series, leafIndex);
744
+ // Leaf category node: append the series (measures) as the final level of children
745
+ if (measureDims.length > 0) {
746
+ item.children = measureDims.map(dim => {
747
+ const rawValue = node[dim.name];
748
+ const value = typeof rawValue === 'object' && rawValue !== null && 'value' in rawValue ? rawValue.value : rawValue;
749
+ return {
750
+ name: dim.displayName || dim.name,
751
+ value: getCellValue(Number(value ?? 0)),
752
+ originalMeassureKey: dim.name
753
+ };
754
+ });
836
755
  }
837
756
  else {
838
- node.value = 0;
757
+ item.value = 0;
839
758
  }
840
- leafIndex++;
841
759
  }
842
- return node;
760
+ return item;
843
761
  });
844
762
  }
845
- return traverse(categories);
763
+ return traverse(source);
846
764
  }
847
765
  /**
848
766
  * Calculates the maximum depth of a tree structure.
@@ -889,10 +807,14 @@ class RingBuilder {
889
807
  merge(this.result, common);
890
808
  }
891
809
  addSeries(data, overrides) {
892
- const flatData = flattenEChartData(data);
893
- if (!flatData || !flatData.series.length)
810
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
894
811
  return;
895
- const totalRings = flatData.series.length; // Cada serie es un anillo
812
+ this.result.dataset = {
813
+ dimensions: data.dimensions,
814
+ source: data.source
815
+ };
816
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
817
+ const totalRings = measureDims.length; // Cada serie es un anillo
896
818
  // Configuración dinámica de radios y márgenes
897
819
  const margin = totalRings > 1 ? Math.max(0.5, 2.5 - (totalRings * 0.1)) : 0;
898
820
  const minInnerRadius = totalRings === 1 ? 45 : (totalRings > 5 ? Math.max(15, 40 - (totalRings * 1.2)) : 40);
@@ -901,12 +823,14 @@ class RingBuilder {
901
823
  const thickness = availableSpan / totalRings;
902
824
  const borderRadius = totalRings === 1 ? 10 : Math.max(2, Math.min(10, thickness * 0.8));
903
825
  const borderWidth = totalRings === 1 ? 2 : Math.max(0.5, Math.min(2, thickness * 0.15));
904
- const series = flatData.series.map((s, ringIndex) => {
826
+ const series = measureDims.map((dim, ringIndex) => {
827
+ const dimKey = dim.name;
828
+ const friendlyName = dim.displayName || dim.name;
905
829
  const inner = minInnerRadius + (ringIndex * (thickness + margin));
906
830
  const outer = inner + thickness;
907
- const pieData = mapToChartItems(flatData.categories, s);
908
831
  const dynamicRingSeriesOptions = {
909
- name: s.name,
832
+ name: friendlyName,
833
+ type: 'pie',
910
834
  radius: [`${inner}%`, `${outer}%`],
911
835
  itemStyle: {
912
836
  borderRadius: borderRadius,
@@ -922,7 +846,10 @@ class RingBuilder {
922
846
  borderWidth: borderWidth,
923
847
  },
924
848
  },
925
- data: pieData,
849
+ encode: {
850
+ itemName: 'category',
851
+ value: dimKey
852
+ },
926
853
  id: `ring_${ringIndex}`,
927
854
  };
928
855
  const seriesOption = merge(dynamicRingSeriesOptions, overrides);
@@ -958,9 +885,8 @@ class RingBuilder {
958
885
  }];
959
886
  }
960
887
  addTooltip(data, overrides) {
961
- const flatData = flattenEChartData(data);
962
888
  merge(overrides, {
963
- formatter: getTooltipFormatter(overrides.trigger, flatData, this.formatCellValue.bind(this)),
889
+ formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
964
890
  });
965
891
  const tooltip = getTooltipOptions(overrides);
966
892
  this.result.tooltip = tooltip;
@@ -1022,7 +948,8 @@ class VSECDirector {
1022
948
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1023
949
  // chart components
1024
950
  this.builder.addCommons();
1025
- this.builder.addSeries(data, seriesOverrides);
951
+ const layoutOpts = { axisTypes };
952
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1026
953
  this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1027
954
  this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1028
955
  this.builder.addTooltip(data, overrides.tooltip);
@@ -1044,7 +971,8 @@ class VSECDirector {
1044
971
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1045
972
  // chart components
1046
973
  this.builder.addCommons();
1047
- this.builder.addSeries(data, seriesOverrides);
974
+ const layoutOpts = { axisTypes, coordinateSystem: 'polar' };
975
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1048
976
  this.builder.addPolar();
1049
977
  this.builder.addAngleAxis(data, overrides['axis']);
1050
978
  this.builder.addRadiusAxis(data, overrides['axis']);
@@ -1067,7 +995,8 @@ class VSECDirector {
1067
995
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1068
996
  // chart components
1069
997
  this.builder.addCommons();
1070
- this.builder.addSeries(data, seriesOverrides);
998
+ const layoutOpts = { axisTypes };
999
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1071
1000
  this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1072
1001
  this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1073
1002
  this.builder.addTooltip(data, overrides.tooltip);
@@ -1087,7 +1016,8 @@ class VSECDirector {
1087
1016
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1088
1017
  // chart components
1089
1018
  this.builder.addCommons();
1090
- this.builder.addSeries(data, seriesOverrides);
1019
+ const layoutOpts = { axisTypes };
1020
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1091
1021
  this.builder.addXAxis(data, overrides.axis, axisTypes.x);
1092
1022
  this.builder.addYAxis(data, overrides.axis, axisTypes.y);
1093
1023
  this.builder.addTooltip(data, overrides.tooltip);
@@ -1104,7 +1034,8 @@ class VSECDirector {
1104
1034
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1105
1035
  // chart components
1106
1036
  this.builder.addCommons();
1107
- this.builder.addSeries(data, seriesOverrides);
1037
+ const layoutOpts = {};
1038
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1108
1039
  this.builder.addGraphic();
1109
1040
  this.builder.addTooltip(data, overrides.tooltip);
1110
1041
  this.builder.addLegend();
@@ -1120,7 +1051,8 @@ class VSECDirector {
1120
1051
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1121
1052
  // chart components
1122
1053
  this.builder.addCommons();
1123
- this.builder.addSeries(data, seriesOverrides);
1054
+ const layoutOpts = {};
1055
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1124
1056
  this.builder.addTooltip(data, overrides.tooltip);
1125
1057
  this.builder.addLegend();
1126
1058
  }
@@ -1135,7 +1067,8 @@ class VSECDirector {
1135
1067
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1136
1068
  // chart components
1137
1069
  this.builder.addCommons();
1138
- this.builder.addSeries(data, seriesOverrides);
1070
+ const layoutOpts = {};
1071
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1139
1072
  this.builder.addTooltip(data, overrides.tooltip);
1140
1073
  this.builder.addLegend();
1141
1074
  }
@@ -1150,10 +1083,26 @@ class VSECDirector {
1150
1083
  const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1151
1084
  // chart components
1152
1085
  this.builder.addCommons();
1153
- this.builder.addSeries(data, seriesOverrides);
1086
+ const layoutOpts = {};
1087
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1154
1088
  this.builder.addTooltip(data, overrides.tooltip);
1155
1089
  // this.builder.addLegend();
1156
1090
  }
1091
+ makeSankey(data, overrides, opts = {}) {
1092
+ const { valueFormatter = undefined, palette = undefined, colorResolver = undefined, } = opts;
1093
+ this.builder.reset();
1094
+ // El orden importa, primero callbacks y despues componentes.
1095
+ this.builder.setValueFormatter(valueFormatter);
1096
+ this.builder.setPalette(palette);
1097
+ this.builder.setColorResolver(colorResolver);
1098
+ const product = this.builder.baseProduct;
1099
+ const seriesOverrides = merge$1({}, product.baseOptions.series, overrides[product.chartKey].series);
1100
+ // chart components
1101
+ this.builder.addCommons();
1102
+ const layoutOpts = {};
1103
+ this.builder.addSeries(data, seriesOverrides, layoutOpts);
1104
+ this.builder.addTooltip(data, overrides.tooltip);
1105
+ }
1157
1106
  }
1158
1107
 
1159
1108
  /**
@@ -1316,18 +1265,26 @@ class PieBuilder {
1316
1265
  merge(this.result, common);
1317
1266
  }
1318
1267
  addSeries(data, overrides) {
1319
- const flatData = flattenEChartData(data);
1320
- if (!flatData || !flatData.series.length)
1268
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
1321
1269
  return;
1322
- const totalSeries = flatData.series.length;
1270
+ this.result.dataset = {
1271
+ dimensions: data.dimensions,
1272
+ source: data.source
1273
+ };
1274
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
1275
+ const totalSeries = measureDims.length;
1323
1276
  let series = [];
1324
1277
  if (totalSeries === 1) {
1325
- const s = flatData.series[0];
1326
- const pieData = mapToChartItems(flatData.categories, s);
1278
+ const dim = measureDims[0];
1279
+ const dimKey = dim.name;
1280
+ const friendlyName = dim.displayName || dim.name;
1327
1281
  const dynamicPieSeriesOptions = {
1328
- name: s.name,
1282
+ name: friendlyName,
1329
1283
  type: 'pie',
1330
- data: pieData,
1284
+ encode: {
1285
+ itemName: 'category',
1286
+ value: dimKey
1287
+ },
1331
1288
  id: `pie_0`,
1332
1289
  radius: [0, '50%'],
1333
1290
  label: {
@@ -1352,8 +1309,9 @@ class PieBuilder {
1352
1309
  const numRings = totalSeries - 1;
1353
1310
  const availableSpan = maxOuterRadius - (centerPieRadius + margin) - (margin * (numRings - 1));
1354
1311
  const thickness = availableSpan / numRings;
1355
- series = flatData.series.map((s, idx) => {
1356
- const pieData = mapToChartItems(flatData.categories, s);
1312
+ series = measureDims.map((dim, idx) => {
1313
+ const dimKey = dim.name;
1314
+ const friendlyName = dim.displayName || dim.name;
1357
1315
  let radius;
1358
1316
  let labelPosition;
1359
1317
  let labelLineShow;
@@ -1374,7 +1332,7 @@ class PieBuilder {
1374
1332
  labelLineShow = true;
1375
1333
  }
1376
1334
  const dynamicPieSeriesOptions = {
1377
- name: s.name,
1335
+ name: friendlyName,
1378
1336
  type: 'pie',
1379
1337
  radius: radius,
1380
1338
  label: {
@@ -1384,7 +1342,10 @@ class PieBuilder {
1384
1342
  labelLine: {
1385
1343
  show: labelLineShow
1386
1344
  },
1387
- data: pieData,
1345
+ encode: {
1346
+ itemName: 'category',
1347
+ value: dimKey
1348
+ },
1388
1349
  id: `pie_${idx}`,
1389
1350
  };
1390
1351
  const seriesOption = merge(dynamicPieSeriesOptions, overrides);
@@ -1398,9 +1359,8 @@ class PieBuilder {
1398
1359
  this.result.series = series;
1399
1360
  }
1400
1361
  addTooltip(data, overrides) {
1401
- const flatData = flattenEChartData(data);
1402
1362
  merge(overrides, {
1403
- formatter: getTooltipFormatter(overrides.trigger, flatData, this.formatCellValue.bind(this)),
1363
+ formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1404
1364
  });
1405
1365
  const tooltip = getTooltipOptions(overrides);
1406
1366
  this.result.tooltip = tooltip;
@@ -1524,39 +1484,45 @@ class FunnelBuilder {
1524
1484
  merge(this.result, common);
1525
1485
  }
1526
1486
  addSeries(data, overrides) {
1527
- const flatData = flattenEChartData(data);
1528
- if (!flatData || !flatData.series.length)
1487
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
1529
1488
  return;
1530
- const series = flatData.series.map((s, seriesIndex) => {
1531
- const funnelData = mapToChartItems(flatData.categories, s);
1532
- let left = '10%';
1533
- let width = '80%';
1534
- const seriesValues = s.data.filter(val => typeof val === 'number');
1535
- const minVal = seriesValues.length > 0 ? Math.min(...seriesValues) : 0;
1536
- const maxVal = seriesValues.length > 0 ? Math.max(...seriesValues) : 100;
1537
- const dynamicFunnelSeriesOptions = {
1538
- name: s.name,
1539
- type: 'funnel',
1540
- left: left,
1541
- width: width,
1542
- min: minVal,
1543
- max: maxVal,
1544
- data: funnelData,
1545
- id: `funnel_${seriesIndex}`
1546
- };
1547
- const seriesOption = merge({}, overrides, dynamicFunnelSeriesOptions);
1548
- // Inject color resolver if provided
1549
- if (this.colorResolver && seriesOption.itemStyle) {
1550
- seriesOption.itemStyle.color = this.colorResolver;
1551
- }
1552
- return seriesOption;
1553
- });
1554
- this.result.series = series.shift();
1489
+ this.result.dataset = {
1490
+ dimensions: data.dimensions,
1491
+ source: data.source
1492
+ };
1493
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
1494
+ if (measureDims.length === 0)
1495
+ return;
1496
+ const dim = measureDims[0];
1497
+ const dimKey = dim.name;
1498
+ const friendlyName = dim.displayName || dim.name;
1499
+ // Obtener los valores para min/max
1500
+ const seriesValues = data.source.map(row => Number(row[dimKey] ?? 0));
1501
+ const minVal = seriesValues.length > 0 ? Math.min(...seriesValues) : 0;
1502
+ const maxVal = seriesValues.length > 0 ? Math.max(...seriesValues) : 100;
1503
+ const dynamicFunnelSeriesOptions = {
1504
+ name: friendlyName,
1505
+ type: 'funnel',
1506
+ left: '10%',
1507
+ width: '80%',
1508
+ min: minVal,
1509
+ max: maxVal,
1510
+ encode: {
1511
+ itemName: 'category',
1512
+ value: dimKey
1513
+ },
1514
+ id: `funnel_0`
1515
+ };
1516
+ const seriesOption = merge({}, overrides, dynamicFunnelSeriesOptions);
1517
+ // Inject color resolver if provided
1518
+ if (this.colorResolver && seriesOption.itemStyle) {
1519
+ seriesOption.itemStyle.color = this.colorResolver;
1520
+ }
1521
+ this.result.series = seriesOption;
1555
1522
  }
1556
1523
  addTooltip(data, overrides) {
1557
- const flatData = flattenEChartData(data);
1558
1524
  merge(overrides, {
1559
- formatter: getTooltipFormatter(overrides.trigger, flatData, this.formatCellValue.bind(this)),
1525
+ formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1560
1526
  });
1561
1527
  const tooltip = getTooltipOptions(overrides);
1562
1528
  this.result.tooltip = tooltip;
@@ -1700,18 +1666,43 @@ class EChartBuilder {
1700
1666
  * @param overrides
1701
1667
  * @returns
1702
1668
  */
1703
- addSeries(data, overrides) {
1704
- const flatData = flattenEChartData(data);
1705
- if (!flatData || !flatData.series.length)
1669
+ addSeries(data, overrides, opts) {
1670
+ if (!data || !data.dimensions || !data.source || data.source.length === 0)
1706
1671
  return;
1707
- const series = flatData.series.map((s) => {
1672
+ this.result.dataset = {
1673
+ dimensions: data.dimensions,
1674
+ source: data.source
1675
+ };
1676
+ const measureDims = data.dimensions.filter(d => d.name !== 'category');
1677
+ const isPolar = opts?.coordinateSystem === 'polar';
1678
+ const isHorizontal = opts?.axisTypes?.x === 'value' && opts?.axisTypes?.y === 'category';
1679
+ const series = measureDims.map((dim) => {
1680
+ const friendlyName = dim.displayName || dim.name;
1681
+ const dimKey = dim.name;
1682
+ let encode = {
1683
+ x: 'category',
1684
+ y: dimKey
1685
+ };
1686
+ if (isPolar) {
1687
+ encode = {
1688
+ angle: 'category',
1689
+ radius: dimKey
1690
+ };
1691
+ }
1692
+ else if (isHorizontal) {
1693
+ encode = {
1694
+ x: dimKey,
1695
+ y: 'category'
1696
+ };
1697
+ }
1708
1698
  const dynamicOverrides = {
1709
- name: s.name,
1710
- data: s.data,
1699
+ name: friendlyName,
1700
+ encode,
1711
1701
  label: {
1712
1702
  formatter: (params) => {
1713
- const key = s.originalKey || getCategoryKey(flatData.categories, params.dataIndex);
1714
- return this.formatCellValue(params.value, key);
1703
+ const row = params.value;
1704
+ const rawValue = (row && typeof row === 'object') ? row[dimKey] : params.value;
1705
+ return this.formatCellValue(Number(rawValue ?? 0), dimKey);
1715
1706
  }
1716
1707
  }
1717
1708
  };
@@ -1731,9 +1722,8 @@ class EChartBuilder {
1731
1722
  */
1732
1723
  addTooltip(data, overrides) {
1733
1724
  // inyecto formateador a overrides de tooltip
1734
- const flatData = flattenEChartData(data);
1735
1725
  merge$1(overrides, {
1736
- formatter: getTooltipFormatter(overrides.trigger, flatData, this.formatCellValue.bind(this)),
1726
+ formatter: getTooltipFormatter(overrides.trigger, data, this.formatCellValue.bind(this)),
1737
1727
  });
1738
1728
  const tooltip = getTooltipOptions(overrides);
1739
1729
  this.result.tooltip = tooltip;
@@ -1850,10 +1840,9 @@ class EChartBuilder {
1850
1840
  }
1851
1841
  // Utils
1852
1842
  getCategoryAxisOptions(data, overrides) {
1853
- const flatCategories = flattenCategoriesToFirstLevel(data.categories);
1843
+ // No explicit data needed on category axis when using ECharts dataset
1854
1844
  const categoryAxisOptionsOverrides = {
1855
- ...overrides.categoryAxis[0],
1856
- data: resolveCategoryNames(flatCategories),
1845
+ ...overrides.categoryAxis[0]
1857
1846
  };
1858
1847
  const categoryAxisOptions = getCategoryAxisOptions(categoryAxisOptionsOverrides);
1859
1848
  return categoryAxisOptions;
@@ -2282,10 +2271,10 @@ class SunburstBuilder {
2282
2271
  this.result = {};
2283
2272
  }
2284
2273
  addSeries(data, overrides) {
2285
- if (!data || !data.series || data.series.length === 0) {
2274
+ if (!data || !data.source || data.source.length === 0) {
2286
2275
  return;
2287
2276
  }
2288
- const sunburstData = mapHierarchicalData(data.categories, data.series);
2277
+ const sunburstData = mapHierarchicalData(data.source, data.dimensions);
2289
2278
  const depth = getTreeDepth(sunburstData);
2290
2279
  const levels = [];
2291
2280
  for (let i = 0; i <= depth; i++) {
@@ -2408,6 +2397,202 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2408
2397
  ], 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"] }]
2409
2398
  }] });
2410
2399
 
2400
+ /**
2401
+ * SankeyBuilder
2402
+ *
2403
+ * Builder concreto para el gráfico Sankey (diagrama de flujos de izquierda a derecha).
2404
+ */
2405
+ class SankeyBuilder {
2406
+ baseProduct;
2407
+ valueFormatter = (value) => value.toLocaleString();
2408
+ palette = [];
2409
+ colorResolver;
2410
+ result = {};
2411
+ constructor(baseProduct) {
2412
+ this.baseProduct = baseProduct;
2413
+ }
2414
+ reset() {
2415
+ this.result = {};
2416
+ }
2417
+ addSeries(data, overrides) {
2418
+ if (!data || !data.source || data.source.length === 0) {
2419
+ return;
2420
+ }
2421
+ // Identificar medidas (todas las dimensiones excepto 'category')
2422
+ const measureKeys = data.dimensions
2423
+ .filter((d) => d.name !== "category")
2424
+ .map((d) => d.name);
2425
+ // Función auxiliar para sumarizar valores de medidas en caso de haber más de una
2426
+ const getNodeValue = (node) => {
2427
+ return measureKeys.reduce((sum, key) => sum + (Number(node[key]) || 0), 0);
2428
+ };
2429
+ const nodesMap = new Map();
2430
+ const linksMap = new Map();
2431
+ // Función recursiva para aplanar datos jerárquicos a nodos y enlaces
2432
+ const traverse = (nodeList, level, parentId) => {
2433
+ for (const node of nodeList) {
2434
+ const category = node.category;
2435
+ const currentId = `${category}___${level}`;
2436
+ const value = getNodeValue(node);
2437
+ nodesMap.set(currentId, (nodesMap.get(currentId) || 0) + value);
2438
+ if (parentId) {
2439
+ const linkKey = `${parentId}--->${currentId}`;
2440
+ if (linksMap.has(linkKey)) {
2441
+ linksMap.get(linkKey).value += value;
2442
+ }
2443
+ else {
2444
+ linksMap.set(linkKey, {
2445
+ source: parentId,
2446
+ target: currentId,
2447
+ value: value,
2448
+ });
2449
+ }
2450
+ }
2451
+ if (node.children && node.children.length > 0) {
2452
+ traverse(node.children, level + 1, currentId);
2453
+ }
2454
+ }
2455
+ };
2456
+ // Comenzar el recorrido en el nivel 0
2457
+ traverse(data.source, 0);
2458
+ // al menos un nivel de links para dibujar.
2459
+ if (linksMap.size === 0)
2460
+ return;
2461
+ // Mapear nodos acumulados a la estructura requerida por ECharts (name y opcionalmente color, sin value redundante)
2462
+ const nodes = Array.from(nodesMap.keys()).map((currentId) => {
2463
+ const category = currentId.split("___")[0];
2464
+ const nodeItem = {
2465
+ name: currentId,
2466
+ };
2467
+ if (this.colorResolver) {
2468
+ const color = this.colorResolver({ name: category, data: nodeItem });
2469
+ if (color) {
2470
+ nodeItem.itemStyle = { color };
2471
+ }
2472
+ }
2473
+ return nodeItem;
2474
+ });
2475
+ const dynamicSerieOptions = {
2476
+ type: "sankey",
2477
+ orient: "horizontal", // De izquierda a derecha
2478
+ draggable: true,
2479
+ emphasis: {
2480
+ focus: "adjacency",
2481
+ },
2482
+ lineStyle: {
2483
+ color: "source",
2484
+ opacity: 0.25,
2485
+ curveness: 0.5,
2486
+ },
2487
+ label: {
2488
+ show: true,
2489
+ position: "right",
2490
+ fontFamily: "'Inter', 'Roboto', 'Open Sans', sans-serif",
2491
+ fontSize: 10,
2492
+ formatter: (params) => {
2493
+ // Remover el sufijo de nivel de la etiqueta visual
2494
+ return params.name.split("___")[0];
2495
+ },
2496
+ },
2497
+ data: nodes,
2498
+ links: Array.from(linksMap.values()),
2499
+ };
2500
+ const serie = merge$1({}, dynamicSerieOptions, overrides);
2501
+ if (this.colorResolver && !serie.itemStyle) {
2502
+ serie.itemStyle = {};
2503
+ }
2504
+ if (this.colorResolver && serie.itemStyle) {
2505
+ serie.itemStyle.color = this.colorResolver;
2506
+ }
2507
+ this.result.series = serie;
2508
+ }
2509
+ addTooltip(data, overrides) {
2510
+ merge$1(overrides, {
2511
+ formatter: getTooltipFormatter(overrides.trigger || 'item', data, this.valueFormatter),
2512
+ });
2513
+ const tooltip = getTooltipOptions(overrides);
2514
+ this.result.tooltip = tooltip;
2515
+ }
2516
+ addPolar() { }
2517
+ addXAxis(data, overrides, type) { }
2518
+ addYAxis(data, overrides, type) { }
2519
+ addRadiusAxis(data, overrides) { }
2520
+ addAngleAxis(data, overrides) { }
2521
+ addLegend() { }
2522
+ addCommons() {
2523
+ const opts = {
2524
+ palette: this.palette,
2525
+ };
2526
+ merge$1(this.result, getCommons(opts));
2527
+ }
2528
+ addGraphic() { }
2529
+ getResult() {
2530
+ return this.result;
2531
+ }
2532
+ setValueFormatter(formatter) {
2533
+ if (formatter) {
2534
+ this.valueFormatter = formatter;
2535
+ }
2536
+ }
2537
+ setPalette(palette) {
2538
+ if (palette) {
2539
+ this.palette = palette;
2540
+ }
2541
+ }
2542
+ setColorResolver(resolver) {
2543
+ if (resolver) {
2544
+ this.colorResolver = resolver;
2545
+ }
2546
+ }
2547
+ }
2548
+
2549
+ class EchartsSankeyComponent extends BaseEchartsComponent {
2550
+ baseSeriesOptions = {
2551
+ type: 'sankey',
2552
+ orient: 'horizontal',
2553
+ draggable: true,
2554
+ emphasis: {
2555
+ focus: 'adjacency'
2556
+ },
2557
+ lineStyle: {
2558
+ color: 'source',
2559
+ opacity: 0.25,
2560
+ curveness: 0.5
2561
+ },
2562
+ label: {
2563
+ show: true,
2564
+ position: 'right',
2565
+ fontFamily: "'Inter', 'Roboto', 'Open Sans', sans-serif",
2566
+ fontSize: 10
2567
+ }
2568
+ };
2569
+ baseProduct = {
2570
+ chartKey: 'sankey',
2571
+ baseOptions: {
2572
+ series: this.baseSeriesOptions,
2573
+ },
2574
+ };
2575
+ builder = new SankeyBuilder(this.baseProduct);
2576
+ director = new VSECDirector(this.builder);
2577
+ make() {
2578
+ const makeOpts = {
2579
+ palette: this.palette,
2580
+ colorResolver: this.colorResolver,
2581
+ valueFormatter: this.valueFormatter,
2582
+ };
2583
+ this.director.makeSankey(this.data, this.optionsOverrides, makeOpts);
2584
+ }
2585
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsSankeyComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2586
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: EchartsSankeyComponent, isStandalone: true, selector: "vs-echarts-sankey", 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></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"] }] });
2587
+ }
2588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: EchartsSankeyComponent, decorators: [{
2589
+ type: Component,
2590
+ args: [{ selector: 'vs-echarts-sankey', standalone: true, imports: [
2591
+ CommonModule,
2592
+ NgxEchartsDirective,
2593
+ ], 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></div>\n", styles: [".echarts-container{width:100%;height:100%;position:relative}\n"] }]
2594
+ }] });
2595
+
2411
2596
  // Interfaces de Inputs de Base EChart Component //
2412
2597
 
2413
2598
  ;
@@ -2422,5 +2607,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2422
2607
  * Generated bundle index. Do not edit.
2423
2608
  */
2424
2609
 
2425
- export { BaseEchartsComponent, EChartsAreaComponent, EChartsAreaStackComponent, EChartsBarStackedComponent, EChartsBarStackedRadialComponent, EChartsHBarComponent, EChartsHBarStackedComponent, EChartsSunburstComponent, EchartsBarComponent, EchartsFunnelComponent, EchartsLineComponent, EchartsPieComponent, EchartsRingComponent, EchartsScatterComponent, defaultOptionsOverrides, initializeEcharts, provideVSEcharts };
2610
+ export { BaseEchartsComponent, EChartsAreaComponent, EChartsAreaStackComponent, EChartsBarStackedComponent, EChartsBarStackedRadialComponent, EChartsHBarComponent, EChartsHBarStackedComponent, EChartsSunburstComponent, EchartsBarComponent, EchartsFunnelComponent, EchartsLineComponent, EchartsPieComponent, EchartsRingComponent, EchartsSankeyComponent, EchartsScatterComponent, defaultOptionsOverrides, initializeEcharts, provideVSEcharts };
2426
2611
  //# sourceMappingURL=visionaris-bruno-vs-echarts.mjs.map