yw-charts-library 0.0.2 → 0.0.4

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.
@@ -94,7 +94,8 @@ const YwDashboardWidgetTypes = [
94
94
  { code: "chart-bar", description: "Chart Bar" },
95
95
  { code: "chart-line", description: "Chart Bar" },
96
96
  { code: "chart-doughnut", description: "Chart Doughnut" },
97
- { code: "chart-gauge", description: "Chart Gauge" }
97
+ { code: "chart-gauge", description: "Chart Gauge" },
98
+ { code: "chart-bubble", description: "Chart Bubble" }
98
99
  ];
99
100
 
100
101
  const YwDashboardWidgetFilterTypes = [
@@ -250,14 +251,14 @@ const resolveJoinedTableData = (allTablesData, configJoins) => {
250
251
  return out;
251
252
  };
252
253
  let currentRows = baseData.map((r) => prefixRow(r, baseTable));
253
- relations.forEach((rel) => {
254
+ for (const rel of relations) {
254
255
  const { leftTable, leftId, rightTable, rightId } = rel;
255
256
  if (!allTablesData[rightTable]) {
256
257
  throw new Error(`La tabla destino "${rightTable}" especificada en el join no existe en los datos.`);
257
258
  }
258
259
  const targetData = allTablesData[rightTable] || [];
259
260
  if (targetData.length === 0) {
260
- throw new Error(`La tabla destino "${rightTable}" está vacía.`);
261
+ return [];
261
262
  }
262
263
  if (targetData[0][rightId] === undefined) {
263
264
  throw new Error(`La columna "${rightId}" no existe en la tabla destino "${rightTable}".`);
@@ -270,18 +271,10 @@ const resolveJoinedTableData = (allTablesData, configJoins) => {
270
271
  }
271
272
  });
272
273
  let matchCount = 0;
273
- currentRows = currentRows.map((row) => {
274
- // let foreignKeyVal = row[leftId];
275
- // if (foreignKeyVal === undefined) {
276
- // const foundKey = Object.keys(row).find(
277
- // (k) => k === leftId || k.endsWith(`.${leftId}`),
278
- // );
279
- // if (foundKey) foreignKeyVal = row[foundKey];
280
- // }
281
- // REVSISAR COMO ESTA FUNCIONADO EL PARCHE
274
+ const nextRows = [];
275
+ currentRows.forEach((row) => {
282
276
  let foreignKeyVal = row[`${leftTable}.${leftId}`];
283
277
  if (foreignKeyVal === undefined) {
284
- // Si no está, aplicas tu respaldo anterior
285
278
  const foundKey = Object.keys(row).find((k) => k === leftId || k.endsWith(`.${leftId}`));
286
279
  if (foundKey)
287
280
  foreignKeyVal = row[foundKey];
@@ -289,14 +282,14 @@ const resolveJoinedTableData = (allTablesData, configJoins) => {
289
282
  if (foreignKeyVal !== undefined && targetMap.has(String(foreignKeyVal))) {
290
283
  matchCount++;
291
284
  const matchedRow = targetMap.get(String(foreignKeyVal));
292
- return { ...row, ...matchedRow };
285
+ nextRows.push({ ...row, ...matchedRow });
293
286
  }
294
- return row;
295
287
  });
288
+ currentRows = nextRows;
296
289
  if (matchCount === 0) {
297
- throw new Error(`Error de Join: No se encontraron coincidencias al intentar unir la tabla acumulada con "${rightTable}".`);
290
+ return [];
298
291
  }
299
- });
292
+ }
300
293
  return currentRows;
301
294
  };
302
295
  // ACCESO A PROPIEDADES ANIDADAS (SOPORTA NOTACIÓN DE PUNTOS Y TOLERANCIA DE PREFIJOS)
@@ -1716,13 +1709,17 @@ const parseDataPie = parseSyncFusionData$2;
1716
1709
  const detectCategoryKey = (row) => {
1717
1710
  if (!row)
1718
1711
  return undefined;
1712
+ const isDateKey = (k) => {
1713
+ const lk = k.toLowerCase();
1714
+ return lk.includes('fecha') || lk.includes('date') || lk.includes('time') || lk.includes('timestamp');
1715
+ };
1719
1716
  const candidates = ['x', 'name', 'label', 'category', 'categoria', 'sucursal', 'producto', 'key', 'title'];
1720
1717
  for (const c of candidates) {
1721
- if (row[c] !== undefined && row[c] !== null && typeof row[c] === 'string')
1718
+ if (row[c] !== undefined && row[c] !== null && typeof row[c] === 'string' && !isDateKey(c))
1722
1719
  return c;
1723
1720
  }
1724
1721
  for (const k of Object.keys(row)) {
1725
- if (typeof row[k] === 'string')
1722
+ if (typeof row[k] === 'string' && !isDateKey(k))
1726
1723
  return k;
1727
1724
  }
1728
1725
  return undefined;
@@ -1821,7 +1818,7 @@ const parseForSyncfusion$1 = (rawData, config) => {
1821
1818
  if (!row)
1822
1819
  return;
1823
1820
  let xVal = effectiveXKey ? getNestedValue(row, effectiveXKey) : undefined;
1824
- if (xVal === undefined) {
1821
+ if (xVal === undefined && !xKey) {
1825
1822
  const autoX = detectCategoryKey(row);
1826
1823
  if (autoX)
1827
1824
  xVal = row[autoX];
@@ -1912,6 +1909,7 @@ const buildPieResult = (dataSource, total, config, cfg, chartTitle, startAngle,
1912
1909
  : (config?.showLegend ?? true);
1913
1910
  const legendPosition = legends?.position ?? config?.legendPosition ?? 'Bottom';
1914
1911
  const visualConfig = {
1912
+ title: chartTitle,
1915
1913
  legendSettings: {
1916
1914
  visible: isLegendVisible,
1917
1915
  position: legendPosition,
@@ -1970,12 +1968,26 @@ class PieChartSyncfusionComponent {
1970
1968
  this._idComponent = '';
1971
1969
  this.chartData = [];
1972
1970
  this.dataChartParse = [];
1971
+ this.chartTitle = '';
1973
1972
  this._lastWidth = 0;
1974
1973
  this._lastHeight = 0;
1974
+ this._dataSnapshot = '';
1975
1975
  }
1976
1976
  ngOnInit() {
1977
1977
  this.updateParsedData();
1978
1978
  }
1979
+ ngDoCheck() {
1980
+ try {
1981
+ const snap = JSON.stringify(this.chartData);
1982
+ if (snap !== this._dataSnapshot) {
1983
+ this._dataSnapshot = snap;
1984
+ this.updateParsedData();
1985
+ }
1986
+ }
1987
+ catch {
1988
+ // Ignorar si hay referencias circulares en los datos
1989
+ }
1990
+ }
1979
1991
  ngAfterViewInit() {
1980
1992
  if (typeof ResizeObserver === 'undefined')
1981
1993
  return;
@@ -2036,12 +2048,23 @@ class PieChartSyncfusionComponent {
2036
2048
  this.legendSettings = parsedResult.visualConfig.legendSettings;
2037
2049
  this.tooltip = parsedResult.visualConfig.tooltip;
2038
2050
  this.dataLabel = parsedResult.visualConfig.dataLabel;
2051
+ this.chartTitle = parsedResult.visualConfig.title || parsedResult.labelData?.title || this.config.title || '';
2052
+ }
2053
+ else {
2054
+ this.chartTitle = parsedResult.labelData?.title || this.config.title || '';
2039
2055
  }
2040
2056
  }
2041
2057
  else {
2042
2058
  this.dataChartParse = [];
2043
2059
  this.centerLabel = undefined;
2044
2060
  this.centerLabelSettings = undefined;
2061
+ this.chartTitle = this.config?.title || '';
2062
+ }
2063
+ this.cdr.markForCheck();
2064
+ if (this.chartInstance) {
2065
+ setTimeout(() => {
2066
+ this.chartInstance?.refresh();
2067
+ });
2045
2068
  }
2046
2069
  }
2047
2070
  onChartLoaded(args) {
@@ -2061,13 +2084,13 @@ class PieChartSyncfusionComponent {
2061
2084
  }
2062
2085
  }
2063
2086
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: PieChartSyncfusionComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2064
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.30", type: PieChartSyncfusionComponent, isStandalone: true, selector: "yw-pie-chart-syncfusion", inputs: { idComponent: "idComponent", chartData: "chartData", config: "config" }, providers: [
2087
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: PieChartSyncfusionComponent, isStandalone: true, selector: "yw-pie-chart-syncfusion", inputs: { idComponent: "idComponent", chartData: "chartData", config: "config" }, providers: [
2065
2088
  AccumulationLegendService,
2066
2089
  AccumulationTooltipService,
2067
2090
  AccumulationDataLabelService,
2068
2091
  PieSeriesService,
2069
2092
  AccumulationAnnotationService,
2070
- ], viewQueries: [{ propertyName: "chartInstance", first: true, predicate: ["accChart"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"control-section\">\r\n <ejs-accumulationchart\r\n #accChart\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n [legendSettings]=\"legendSettings\"\r\n [tooltip]=\"tooltip\"\r\n [centerLabel]=\"centerLabelSettings\"\r\n (loaded)=\"onChartLoaded($event)\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n [enableAnimation]=\"true\">\r\n\r\n <e-accumulation-series-collection>\r\n <e-accumulation-series\r\n [dataSource]=\"dataChartParse\"\r\n xName=\"x\"\r\n yName=\"y\"\r\n pointColorMapping=\"fill\"\r\n [type]=\"'Pie'\"\r\n [radius]=\"seriesConfig.radius\"\r\n [innerRadius]=\"seriesConfig.innerRadius\"\r\n [startAngle]=\"seriesConfig.startAngle\"\r\n [endAngle]=\"seriesConfig.endAngle\"\r\n [explode]=\"config.explodeSlice ?? true\"\r\n [explodeIndex]=\"config.explodeIndex || 0\"\r\n [explodeOffset]=\"config.explodeOffset || '10%'\"\r\n [dataLabel]=\"dataLabel\">\r\n </e-accumulation-series>\r\n </e-accumulation-series-collection>\r\n\r\n </ejs-accumulationchart>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px;flex-grow:1}:host ::ng-deep ejs-accumulationchart{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AccumulationChartModule }, { kind: "component", type: i1.AccumulationChartComponent, selector: "ejs-accumulationchart", inputs: ["accessibility", "allowExport", "annotations", "background", "backgroundImage", "border", "center", "centerLabel", "currencyCode", "dataSource", "enableAnimation", "enableBorderOnMouseMove", "enableExport", "enableHtmlSanitizer", "enablePersistence", "enableRtl", "enableSmartLabels", "focusBorderColor", "focusBorderMargin", "focusBorderWidth", "height", "highlightColor", "highlightMode", "highlightPattern", "isMultiSelect", "legendSettings", "locale", "margin", "selectedDataIndexes", "selectionMode", "selectionPattern", "series", "subTitle", "subTitleStyle", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width"], outputs: ["afterExport", "animationComplete", "annotationRender", "beforePrint", "beforeResize", "chartDoubleClick", "chartMouseClick", "chartMouseDown", "chartMouseLeave", "chartMouseMove", "chartMouseUp", "legendClick", "legendRender", "load", "loaded", "pointClick", "pointMove", "pointRender", "resized", "selectionComplete", "seriesRender", "textRender", "tooltipRender", "dataSourceChange"] }, { kind: "directive", type: i1.AccumulationSeriesDirective, selector: "e-accumulation-series-collection>e-accumulation-series", inputs: ["accessibility", "animation", "applyPattern", "border", "borderRadius", "dashArray", "dataLabel", "dataSource", "emptyPointSettings", "enableTooltip", "endAngle", "explode", "explodeAll", "explodeIndex", "explodeOffset", "gapRatio", "groupMode", "groupTo", "height", "innerRadius", "legendImageUrl", "legendShape", "name", "neckHeight", "neckWidth", "opacity", "palettes", "pointColorMapping", "pyramidMode", "query", "radius", "selectionStyle", "startAngle", "tooltipMappingName", "type", "visible", "width", "xName", "yName"] }, { kind: "directive", type: i1.AccumulationSeriesCollectionDirective, selector: "ej-accumulationchart>e-accumulation-series-collection" }] }); }
2093
+ ], viewQueries: [{ propertyName: "chartInstance", first: true, predicate: ["accChart"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"pie-container\">\r\n @if (chartTitle) {\r\n <div class=\"pie-header-title\" [title]=\"chartTitle\">\r\n {{ chartTitle }}\r\n </div>\r\n }\r\n\r\n <div class=\"pie-wrapper\">\r\n <ejs-accumulationchart\r\n #accChart\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n [legendSettings]=\"legendSettings\"\r\n [tooltip]=\"tooltip\"\r\n [centerLabel]=\"centerLabelSettings\"\r\n (loaded)=\"onChartLoaded($event)\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n [enableAnimation]=\"true\">\r\n\r\n <e-accumulation-series-collection>\r\n <e-accumulation-series\r\n [dataSource]=\"dataChartParse\"\r\n xName=\"x\"\r\n yName=\"y\"\r\n pointColorMapping=\"fill\"\r\n [type]=\"'Pie'\"\r\n [radius]=\"seriesConfig.radius || '80%'\"\r\n [innerRadius]=\"seriesConfig.innerRadius || '0%'\"\r\n [startAngle]=\"seriesConfig.startAngle || 0\"\r\n [endAngle]=\"seriesConfig.endAngle || 360\"\r\n [explode]=\"config.explodeSlice ?? true\"\r\n [explodeIndex]=\"config.explodeIndex || 0\"\r\n [explodeOffset]=\"config.explodeOffset || '10%'\"\r\n [dataLabel]=\"dataLabel\">\r\n </e-accumulation-series>\r\n </e-accumulation-series-collection>\r\n\r\n </ejs-accumulationchart>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.pie-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px;flex-grow:1}.pie-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.pie-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-accumulationchart{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AccumulationChartModule }, { kind: "component", type: i1.AccumulationChartComponent, selector: "ejs-accumulationchart", inputs: ["accessibility", "allowExport", "annotations", "background", "backgroundImage", "border", "center", "centerLabel", "currencyCode", "dataSource", "enableAnimation", "enableBorderOnMouseMove", "enableExport", "enableHtmlSanitizer", "enablePersistence", "enableRtl", "enableSmartLabels", "focusBorderColor", "focusBorderMargin", "focusBorderWidth", "height", "highlightColor", "highlightMode", "highlightPattern", "isMultiSelect", "legendSettings", "locale", "margin", "selectedDataIndexes", "selectionMode", "selectionPattern", "series", "subTitle", "subTitleStyle", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width"], outputs: ["afterExport", "animationComplete", "annotationRender", "beforePrint", "beforeResize", "chartDoubleClick", "chartMouseClick", "chartMouseDown", "chartMouseLeave", "chartMouseMove", "chartMouseUp", "legendClick", "legendRender", "load", "loaded", "pointClick", "pointMove", "pointRender", "resized", "selectionComplete", "seriesRender", "textRender", "tooltipRender", "dataSourceChange"] }, { kind: "directive", type: i1.AccumulationSeriesDirective, selector: "e-accumulation-series-collection>e-accumulation-series", inputs: ["accessibility", "animation", "applyPattern", "border", "borderRadius", "dashArray", "dataLabel", "dataSource", "emptyPointSettings", "enableTooltip", "endAngle", "explode", "explodeAll", "explodeIndex", "explodeOffset", "gapRatio", "groupMode", "groupTo", "height", "innerRadius", "legendImageUrl", "legendShape", "name", "neckHeight", "neckWidth", "opacity", "palettes", "pointColorMapping", "pyramidMode", "query", "radius", "selectionStyle", "startAngle", "tooltipMappingName", "type", "visible", "width", "xName", "yName"] }, { kind: "directive", type: i1.AccumulationSeriesCollectionDirective, selector: "ej-accumulationchart>e-accumulation-series-collection" }] }); }
2071
2094
  }
2072
2095
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: PieChartSyncfusionComponent, decorators: [{
2073
2096
  type: Component,
@@ -2077,7 +2100,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
2077
2100
  AccumulationDataLabelService,
2078
2101
  PieSeriesService,
2079
2102
  AccumulationAnnotationService,
2080
- ], template: "<div class=\"control-section\">\r\n <ejs-accumulationchart\r\n #accChart\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n [legendSettings]=\"legendSettings\"\r\n [tooltip]=\"tooltip\"\r\n [centerLabel]=\"centerLabelSettings\"\r\n (loaded)=\"onChartLoaded($event)\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n [enableAnimation]=\"true\">\r\n\r\n <e-accumulation-series-collection>\r\n <e-accumulation-series\r\n [dataSource]=\"dataChartParse\"\r\n xName=\"x\"\r\n yName=\"y\"\r\n pointColorMapping=\"fill\"\r\n [type]=\"'Pie'\"\r\n [radius]=\"seriesConfig.radius\"\r\n [innerRadius]=\"seriesConfig.innerRadius\"\r\n [startAngle]=\"seriesConfig.startAngle\"\r\n [endAngle]=\"seriesConfig.endAngle\"\r\n [explode]=\"config.explodeSlice ?? true\"\r\n [explodeIndex]=\"config.explodeIndex || 0\"\r\n [explodeOffset]=\"config.explodeOffset || '10%'\"\r\n [dataLabel]=\"dataLabel\">\r\n </e-accumulation-series>\r\n </e-accumulation-series-collection>\r\n\r\n </ejs-accumulationchart>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px;flex-grow:1}:host ::ng-deep ejs-accumulationchart{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
2103
+ ], template: "<div class=\"pie-container\">\r\n @if (chartTitle) {\r\n <div class=\"pie-header-title\" [title]=\"chartTitle\">\r\n {{ chartTitle }}\r\n </div>\r\n }\r\n\r\n <div class=\"pie-wrapper\">\r\n <ejs-accumulationchart\r\n #accChart\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n [legendSettings]=\"legendSettings\"\r\n [tooltip]=\"tooltip\"\r\n [centerLabel]=\"centerLabelSettings\"\r\n (loaded)=\"onChartLoaded($event)\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n [enableAnimation]=\"true\">\r\n\r\n <e-accumulation-series-collection>\r\n <e-accumulation-series\r\n [dataSource]=\"dataChartParse\"\r\n xName=\"x\"\r\n yName=\"y\"\r\n pointColorMapping=\"fill\"\r\n [type]=\"'Pie'\"\r\n [radius]=\"seriesConfig.radius || '80%'\"\r\n [innerRadius]=\"seriesConfig.innerRadius || '0%'\"\r\n [startAngle]=\"seriesConfig.startAngle || 0\"\r\n [endAngle]=\"seriesConfig.endAngle || 360\"\r\n [explode]=\"config.explodeSlice ?? true\"\r\n [explodeIndex]=\"config.explodeIndex || 0\"\r\n [explodeOffset]=\"config.explodeOffset || '10%'\"\r\n [dataLabel]=\"dataLabel\">\r\n </e-accumulation-series>\r\n </e-accumulation-series-collection>\r\n\r\n </ejs-accumulationchart>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.pie-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px;flex-grow:1}.pie-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.pie-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-accumulationchart{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
2081
2104
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { chartInstance: [{
2082
2105
  type: ViewChild,
2083
2106
  args: ['accChart']
@@ -2522,6 +2545,18 @@ class GaugeChartSyncfusionComponent {
2522
2545
  get chartData() {
2523
2546
  return this._chartData;
2524
2547
  }
2548
+ /** HTML con estilos inline para la annotation central del gauge */
2549
+ get annotationHtml() {
2550
+ const text = this.animatedLabelText || (this.gaugePoint?.text ?? '');
2551
+ return this.buildAnnotationHtml(text);
2552
+ }
2553
+ buildAnnotationHtml(text) {
2554
+ const size = this.currentLabelSize || this.visualConfig?.labelSize || '24px';
2555
+ const isBold = this.visualConfig?.labelBold || this.visualConfig?.labelWeight === 'bold' || this.visualConfig?.labelWeight === '700';
2556
+ const weight = isBold ? 'bold' : (this.visualConfig?.labelWeight || 'normal');
2557
+ const color = this.visualConfig?.labelColor || '#1F2937';
2558
+ return `<div style="font-size:${size};font-weight:${weight};color:${color};text-align:center;font-family:inherit;line-height:1.1;white-space:nowrap;">${text}</div>`;
2559
+ }
2525
2560
  constructor(cdr, elRef) {
2526
2561
  this.cdr = cdr;
2527
2562
  this.elRef = elRef;
@@ -2648,6 +2683,12 @@ class GaugeChartSyncfusionComponent {
2648
2683
  else {
2649
2684
  this.currentLabelSize = `${parsedSize}px`;
2650
2685
  }
2686
+ if (this.gaugeInstance && this.visualConfig?.showLabel && !this._isAnimating) {
2687
+ try {
2688
+ this.gaugeInstance.setAnnotationValue(0, 0, this.annotationHtml);
2689
+ }
2690
+ catch { }
2691
+ }
2651
2692
  this.cdr.markForCheck();
2652
2693
  return;
2653
2694
  }
@@ -2655,6 +2696,12 @@ class GaugeChartSyncfusionComponent {
2655
2696
  // Proporción estándar ideal (~7.5% del ancho del contenedor, acotado entre 13px y 26px)
2656
2697
  const base = Math.max(13, Math.min(26, Math.round(width * 0.075)));
2657
2698
  this.currentLabelSize = `${base}px`;
2699
+ if (this.gaugeInstance && this.visualConfig?.showLabel && !this._isAnimating) {
2700
+ try {
2701
+ this.gaugeInstance.setAnnotationValue(0, 0, this.annotationHtml);
2702
+ }
2703
+ catch { }
2704
+ }
2658
2705
  this.cdr.markForCheck();
2659
2706
  }
2660
2707
  /**
@@ -2744,7 +2791,7 @@ class GaugeChartSyncfusionComponent {
2744
2791
  this.animatedLabelText = labelText;
2745
2792
  if (this.gaugeInstance) {
2746
2793
  try {
2747
- this.gaugeInstance.setAnnotationValue(0, 0, labelText);
2794
+ this.gaugeInstance.setAnnotationValue(0, 0, this.buildAnnotationHtml(labelText));
2748
2795
  }
2749
2796
  catch {
2750
2797
  // Ignorar si la anotación aún no se ha montado
@@ -2761,7 +2808,7 @@ class GaugeChartSyncfusionComponent {
2761
2808
  this.animatedLabelText = finalLabel;
2762
2809
  if (this.gaugeInstance) {
2763
2810
  try {
2764
- this.gaugeInstance.setAnnotationValue(0, 0, finalLabel);
2811
+ this.gaugeInstance.setAnnotationValue(0, 0, this.buildAnnotationHtml(finalLabel));
2765
2812
  }
2766
2813
  catch { }
2767
2814
  }
@@ -2789,11 +2836,11 @@ class GaugeChartSyncfusionComponent {
2789
2836
  return index;
2790
2837
  }
2791
2838
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: GaugeChartSyncfusionComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
2792
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: GaugeChartSyncfusionComponent, isStandalone: true, selector: "yw-gauge-chart-syncfusion", inputs: { idComponent: "idComponent", chartData: "chartData", config: "config" }, providers: [AnnotationsService, GaugeTooltipService], viewQueries: [{ propertyName: "gaugeInstance", first: true, predicate: ["circulargauge"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"gauge-container\">\r\n\r\n @if (visualConfig && visualConfig.title) {\r\n <div class=\"gauge-header-title\" [title]=\"visualConfig.title\">\r\n {{ visualConfig.title }}\r\n </div>\r\n }\r\n\r\n <div class=\"gauge-wrapper\">\r\n <ejs-circulargauge\r\n #circulargauge\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n height=\"100%\"\r\n width=\"100%\"\r\n [enablePointerDrag]=\"false\"\r\n [tooltip]=\"{ enable: visualConfig.enableTooltip }\"\r\n (loaded)=\"onGaugeLoaded($event)\">\r\n\r\n <e-axes>\r\n <e-axis\r\n [minimum]=\"visualConfig.minimum\"\r\n [maximum]=\"visualConfig.maximum\"\r\n [startAngle]=\"visualConfig.startAngle\"\r\n [endAngle]=\"visualConfig.endAngle\"\r\n [lineStyle]=\"{ width: 0 }\"\r\n [labelStyle]=\"visualConfig.showTicks ? { position: 'Outside', offset: visualConfig.labelOffset, font: { size: '11px' } } : { position: 'Outside', font: { size: '0px' } }\"\r\n [majorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 2, height: 10 } : { width: 0 }\"\r\n [minorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 1, height: 5 } : { width: 0 }\">\r\n\r\n <!-- Rangos de color sobre el arco (modo convencional por rangos o modo ancho variable) -->\r\n @if (!visualConfig.enableProgressTrack || visualConfig.isVariableWidth) {\r\n <e-ranges>\r\n @for (range of visualConfig.ranges; track $index) {\r\n <e-range\r\n [start]=\"range.start\"\r\n [end]=\"range.end\"\r\n [color]=\"range.color\"\r\n [startWidth]=\"range.startWidth ?? range.width ?? 8\"\r\n [endWidth]=\"range.endWidth ?? range.width ?? 8\"\r\n [roundedCornerRadius]=\"range.roundedCornerRadius ?? 0\">\r\n </e-range>\r\n }\r\n </e-ranges>\r\n }\r\n\r\n <!-- Punteros: tanto la l\u00EDnea de fondo (vac\u00EDo) como la de llenado (l\u00EDquido) usan RangeBar para alineaci\u00F3n perfecta -->\r\n <e-pointers>\r\n @if (visualConfig.enableProgressTrack && !visualConfig.isVariableWidth) {\r\n <!-- L\u00EDnea 1: Fondo / vac\u00EDo (del m\u00EDnimo al m\u00E1ximo) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"visualConfig.maximum\"\r\n [color]=\"visualConfig.trackColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: false }\">\r\n </e-pointer>\r\n\r\n <!-- L\u00EDnea 2: Llenado progresivo / l\u00EDquido (del m\u00EDnimo al valor actual) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"gaugePoint.value\"\r\n [color]=\"visualConfig.progressColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: true, duration: 900 }\">\r\n </e-pointer>\r\n }\r\n\r\n @if ((!visualConfig.enableProgressTrack || visualConfig.showNeedleWithProgress) && (!visualConfig.isVariableWidth || visualConfig.showNeedleWithProgress)) {\r\n <e-pointer\r\n [value]=\"gaugePoint.value\"\r\n type=\"Needle\"\r\n [color]=\"visualConfig.pointerColor\"\r\n [needleTail]=\"{ length: '18%' }\"\r\n [cap]=\"{ radius: 8, color: visualConfig.pointerColor }\">\r\n </e-pointer>\r\n }\r\n </e-pointers>\r\n\r\n <!-- Etiqueta central con el valor -->\r\n @if (visualConfig.showLabel) {\r\n <e-annotations>\r\n <e-annotation\r\n [angle]=\"visualConfig.labelAngle\"\r\n [radius]=\"visualConfig.labelRadius\"\r\n zIndex=\"1\"\r\n [content]=\"animatedLabelText || gaugePoint.text\"\r\n [textStyle]=\"{\r\n size: currentLabelSize || visualConfig.labelSize,\r\n fontWeight: visualConfig.labelWeight,\r\n color: visualConfig.labelColor,\r\n fontFamily: 'inherit',\r\n fontStyle: 'normal',\r\n opacity: 1\r\n }\">\r\n </e-annotation>\r\n </e-annotations>\r\n }\r\n\r\n </e-axis>\r\n </e-axes>\r\n\r\n </ejs-circulargauge>\r\n </div>\r\n\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.gauge-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}.gauge-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.gauge-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:240px;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CircularGaugeModule }, { kind: "component", type: i1$1.CircularGaugeComponent, selector: "ejs-circulargauge", inputs: ["allowImageExport", "allowMargin", "allowPdfExport", "allowPrint", "allowRangePreRender", "animationDuration", "axes", "background", "border", "centerX", "centerY", "description", "enablePersistence", "enablePointerDrag", "enableRangeDrag", "enableRtl", "height", "legendSettings", "locale", "margin", "moveToCenter", "tabIndex", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width"], outputs: ["animationComplete", "annotationRender", "axisLabelRender", "beforePrint", "dragEnd", "dragMove", "dragStart", "gaugeMouseDown", "gaugeMouseLeave", "gaugeMouseMove", "gaugeMouseUp", "legendRender", "load", "loaded", "radiusCalculate", "resized", "tooltipRender"] }, { kind: "directive", type: i1$1.AnnotationDirective, selector: "e-annotations>e-annotation", inputs: ["angle", "autoAngle", "content", "description", "radius", "textStyle", "zIndex"] }, { kind: "directive", type: i1$1.AnnotationsDirective, selector: "ej-circulargauge>e-axes>e-axis>e-annotations" }, { kind: "directive", type: i1$1.RangeDirective, selector: "e-ranges>e-range", inputs: ["color", "end", "endWidth", "legendText", "linearGradient", "offset", "opacity", "position", "radialGradient", "radius", "roundedCornerRadius", "start", "startWidth"] }, { kind: "directive", type: i1$1.RangesDirective, selector: "ej-circulargauge>e-axes>e-axis>e-ranges" }, { kind: "directive", type: i1$1.PointerDirective, selector: "e-pointers>e-pointer", inputs: ["animation", "border", "cap", "color", "description", "enableDrag", "imageUrl", "linearGradient", "markerHeight", "markerShape", "markerWidth", "needleEndWidth", "needleStartWidth", "needleTail", "offset", "pointerWidth", "position", "radialGradient", "radius", "roundedCornerRadius", "text", "textStyle", "type", "value"] }, { kind: "directive", type: i1$1.PointersDirective, selector: "ej-circulargauge>e-axes>e-axis>e-pointers" }, { kind: "directive", type: i1$1.AxisDirective, selector: "e-axes>e-axis", inputs: ["annotations", "background", "direction", "endAngle", "hideIntersectingLabel", "labelStyle", "lineStyle", "majorTicks", "maximum", "minimum", "minorTicks", "pointers", "radius", "rangeGap", "ranges", "roundingPlaces", "showLastLabel", "startAndEndRangeGap", "startAngle"] }, { kind: "directive", type: i1$1.AxesDirective, selector: "ej-circulargauge>e-axes" }] }); }
2839
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: GaugeChartSyncfusionComponent, isStandalone: true, selector: "yw-gauge-chart-syncfusion", inputs: { idComponent: "idComponent", chartData: "chartData", config: "config" }, providers: [AnnotationsService, GaugeTooltipService], viewQueries: [{ propertyName: "gaugeInstance", first: true, predicate: ["circulargauge"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"gauge-container\">\r\n\r\n @if (visualConfig && visualConfig.title) {\r\n <div class=\"gauge-header-title\" [title]=\"visualConfig.title\">\r\n {{ visualConfig.title }}\r\n </div>\r\n }\r\n\r\n <div class=\"gauge-wrapper\">\r\n <ejs-circulargauge\r\n #circulargauge\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n height=\"100%\"\r\n width=\"100%\"\r\n [enablePointerDrag]=\"false\"\r\n [tooltip]=\"{ enable: visualConfig.enableTooltip }\"\r\n (loaded)=\"onGaugeLoaded($event)\">\r\n\r\n <e-axes>\r\n <e-axis\r\n [minimum]=\"visualConfig.minimum\"\r\n [maximum]=\"visualConfig.maximum\"\r\n [startAngle]=\"visualConfig.startAngle\"\r\n [endAngle]=\"visualConfig.endAngle\"\r\n [lineStyle]=\"{ width: 0 }\"\r\n [labelStyle]=\"visualConfig.showTicks ? { position: 'Outside', offset: visualConfig.labelOffset, font: { size: '11px' } } : { position: 'Outside', font: { size: '0px' } }\"\r\n [majorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 2, height: 10 } : { width: 0 }\"\r\n [minorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 1, height: 5 } : { width: 0 }\">\r\n\r\n <!-- Rangos de color sobre el arco (modo convencional por rangos o modo ancho variable) -->\r\n @if (!visualConfig.enableProgressTrack || visualConfig.isVariableWidth) {\r\n <e-ranges>\r\n @for (range of visualConfig.ranges; track $index) {\r\n <e-range\r\n [start]=\"range.start\"\r\n [end]=\"range.end\"\r\n [color]=\"range.color\"\r\n [startWidth]=\"range.startWidth ?? range.width ?? 8\"\r\n [endWidth]=\"range.endWidth ?? range.width ?? 8\"\r\n [roundedCornerRadius]=\"range.roundedCornerRadius ?? 0\">\r\n </e-range>\r\n }\r\n </e-ranges>\r\n }\r\n\r\n <!-- Punteros: tanto la l\u00EDnea de fondo (vac\u00EDo) como la de llenado (l\u00EDquido) usan RangeBar para alineaci\u00F3n perfecta -->\r\n <e-pointers>\r\n @if (visualConfig.enableProgressTrack && !visualConfig.isVariableWidth) {\r\n <!-- L\u00EDnea 1: Fondo / vac\u00EDo (del m\u00EDnimo al m\u00E1ximo) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"visualConfig.maximum\"\r\n [color]=\"visualConfig.trackColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: false }\">\r\n </e-pointer>\r\n\r\n <!-- L\u00EDnea 2: Llenado progresivo / l\u00EDquido (del m\u00EDnimo al valor actual) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"gaugePoint.value\"\r\n [color]=\"visualConfig.progressColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: true, duration: 900 }\">\r\n </e-pointer>\r\n }\r\n\r\n @if ((!visualConfig.enableProgressTrack || visualConfig.showNeedleWithProgress) && (!visualConfig.isVariableWidth || visualConfig.showNeedleWithProgress)) {\r\n <e-pointer\r\n [value]=\"gaugePoint.value\"\r\n type=\"Needle\"\r\n [color]=\"visualConfig.pointerColor\"\r\n [needleTail]=\"{ length: '18%' }\"\r\n [cap]=\"{ radius: 8, color: visualConfig.pointerColor }\">\r\n </e-pointer>\r\n }\r\n </e-pointers>\r\n\r\n <!-- Etiqueta central con el valor -->\r\n @if (visualConfig.showLabel) {\r\n <e-annotations>\r\n <e-annotation\r\n [angle]=\"visualConfig.labelAngle\"\r\n [radius]=\"visualConfig.labelRadius\"\r\n zIndex=\"1\"\r\n [content]=\"annotationHtml\">\r\n </e-annotation>\r\n </e-annotations>\r\n }\r\n\r\n </e-axis>\r\n </e-axes>\r\n\r\n </ejs-circulargauge>\r\n </div>\r\n\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.gauge-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}.gauge-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.gauge-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:240px;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CircularGaugeModule }, { kind: "component", type: i1$1.CircularGaugeComponent, selector: "ejs-circulargauge", inputs: ["allowImageExport", "allowMargin", "allowPdfExport", "allowPrint", "allowRangePreRender", "animationDuration", "axes", "background", "border", "centerX", "centerY", "description", "enablePersistence", "enablePointerDrag", "enableRangeDrag", "enableRtl", "height", "legendSettings", "locale", "margin", "moveToCenter", "tabIndex", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width"], outputs: ["animationComplete", "annotationRender", "axisLabelRender", "beforePrint", "dragEnd", "dragMove", "dragStart", "gaugeMouseDown", "gaugeMouseLeave", "gaugeMouseMove", "gaugeMouseUp", "legendRender", "load", "loaded", "radiusCalculate", "resized", "tooltipRender"] }, { kind: "directive", type: i1$1.AnnotationDirective, selector: "e-annotations>e-annotation", inputs: ["angle", "autoAngle", "content", "description", "radius", "textStyle", "zIndex"] }, { kind: "directive", type: i1$1.AnnotationsDirective, selector: "ej-circulargauge>e-axes>e-axis>e-annotations" }, { kind: "directive", type: i1$1.RangeDirective, selector: "e-ranges>e-range", inputs: ["color", "end", "endWidth", "legendText", "linearGradient", "offset", "opacity", "position", "radialGradient", "radius", "roundedCornerRadius", "start", "startWidth"] }, { kind: "directive", type: i1$1.RangesDirective, selector: "ej-circulargauge>e-axes>e-axis>e-ranges" }, { kind: "directive", type: i1$1.PointerDirective, selector: "e-pointers>e-pointer", inputs: ["animation", "border", "cap", "color", "description", "enableDrag", "imageUrl", "linearGradient", "markerHeight", "markerShape", "markerWidth", "needleEndWidth", "needleStartWidth", "needleTail", "offset", "pointerWidth", "position", "radialGradient", "radius", "roundedCornerRadius", "text", "textStyle", "type", "value"] }, { kind: "directive", type: i1$1.PointersDirective, selector: "ej-circulargauge>e-axes>e-axis>e-pointers" }, { kind: "directive", type: i1$1.AxisDirective, selector: "e-axes>e-axis", inputs: ["annotations", "background", "direction", "endAngle", "hideIntersectingLabel", "labelStyle", "lineStyle", "majorTicks", "maximum", "minimum", "minorTicks", "pointers", "radius", "rangeGap", "ranges", "roundingPlaces", "showLastLabel", "startAndEndRangeGap", "startAngle"] }, { kind: "directive", type: i1$1.AxesDirective, selector: "ej-circulargauge>e-axes" }] }); }
2793
2840
  }
2794
2841
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: GaugeChartSyncfusionComponent, decorators: [{
2795
2842
  type: Component,
2796
- args: [{ selector: 'yw-gauge-chart-syncfusion', standalone: true, imports: [CommonModule, CircularGaugeModule], providers: [AnnotationsService, GaugeTooltipService], template: "<div class=\"gauge-container\">\r\n\r\n @if (visualConfig && visualConfig.title) {\r\n <div class=\"gauge-header-title\" [title]=\"visualConfig.title\">\r\n {{ visualConfig.title }}\r\n </div>\r\n }\r\n\r\n <div class=\"gauge-wrapper\">\r\n <ejs-circulargauge\r\n #circulargauge\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n height=\"100%\"\r\n width=\"100%\"\r\n [enablePointerDrag]=\"false\"\r\n [tooltip]=\"{ enable: visualConfig.enableTooltip }\"\r\n (loaded)=\"onGaugeLoaded($event)\">\r\n\r\n <e-axes>\r\n <e-axis\r\n [minimum]=\"visualConfig.minimum\"\r\n [maximum]=\"visualConfig.maximum\"\r\n [startAngle]=\"visualConfig.startAngle\"\r\n [endAngle]=\"visualConfig.endAngle\"\r\n [lineStyle]=\"{ width: 0 }\"\r\n [labelStyle]=\"visualConfig.showTicks ? { position: 'Outside', offset: visualConfig.labelOffset, font: { size: '11px' } } : { position: 'Outside', font: { size: '0px' } }\"\r\n [majorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 2, height: 10 } : { width: 0 }\"\r\n [minorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 1, height: 5 } : { width: 0 }\">\r\n\r\n <!-- Rangos de color sobre el arco (modo convencional por rangos o modo ancho variable) -->\r\n @if (!visualConfig.enableProgressTrack || visualConfig.isVariableWidth) {\r\n <e-ranges>\r\n @for (range of visualConfig.ranges; track $index) {\r\n <e-range\r\n [start]=\"range.start\"\r\n [end]=\"range.end\"\r\n [color]=\"range.color\"\r\n [startWidth]=\"range.startWidth ?? range.width ?? 8\"\r\n [endWidth]=\"range.endWidth ?? range.width ?? 8\"\r\n [roundedCornerRadius]=\"range.roundedCornerRadius ?? 0\">\r\n </e-range>\r\n }\r\n </e-ranges>\r\n }\r\n\r\n <!-- Punteros: tanto la l\u00EDnea de fondo (vac\u00EDo) como la de llenado (l\u00EDquido) usan RangeBar para alineaci\u00F3n perfecta -->\r\n <e-pointers>\r\n @if (visualConfig.enableProgressTrack && !visualConfig.isVariableWidth) {\r\n <!-- L\u00EDnea 1: Fondo / vac\u00EDo (del m\u00EDnimo al m\u00E1ximo) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"visualConfig.maximum\"\r\n [color]=\"visualConfig.trackColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: false }\">\r\n </e-pointer>\r\n\r\n <!-- L\u00EDnea 2: Llenado progresivo / l\u00EDquido (del m\u00EDnimo al valor actual) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"gaugePoint.value\"\r\n [color]=\"visualConfig.progressColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: true, duration: 900 }\">\r\n </e-pointer>\r\n }\r\n\r\n @if ((!visualConfig.enableProgressTrack || visualConfig.showNeedleWithProgress) && (!visualConfig.isVariableWidth || visualConfig.showNeedleWithProgress)) {\r\n <e-pointer\r\n [value]=\"gaugePoint.value\"\r\n type=\"Needle\"\r\n [color]=\"visualConfig.pointerColor\"\r\n [needleTail]=\"{ length: '18%' }\"\r\n [cap]=\"{ radius: 8, color: visualConfig.pointerColor }\">\r\n </e-pointer>\r\n }\r\n </e-pointers>\r\n\r\n <!-- Etiqueta central con el valor -->\r\n @if (visualConfig.showLabel) {\r\n <e-annotations>\r\n <e-annotation\r\n [angle]=\"visualConfig.labelAngle\"\r\n [radius]=\"visualConfig.labelRadius\"\r\n zIndex=\"1\"\r\n [content]=\"animatedLabelText || gaugePoint.text\"\r\n [textStyle]=\"{\r\n size: currentLabelSize || visualConfig.labelSize,\r\n fontWeight: visualConfig.labelWeight,\r\n color: visualConfig.labelColor,\r\n fontFamily: 'inherit',\r\n fontStyle: 'normal',\r\n opacity: 1\r\n }\">\r\n </e-annotation>\r\n </e-annotations>\r\n }\r\n\r\n </e-axis>\r\n </e-axes>\r\n\r\n </ejs-circulargauge>\r\n </div>\r\n\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.gauge-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}.gauge-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.gauge-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:240px;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
2843
+ args: [{ selector: 'yw-gauge-chart-syncfusion', standalone: true, imports: [CommonModule, CircularGaugeModule], providers: [AnnotationsService, GaugeTooltipService], template: "<div class=\"gauge-container\">\r\n\r\n @if (visualConfig && visualConfig.title) {\r\n <div class=\"gauge-header-title\" [title]=\"visualConfig.title\">\r\n {{ visualConfig.title }}\r\n </div>\r\n }\r\n\r\n <div class=\"gauge-wrapper\">\r\n <ejs-circulargauge\r\n #circulargauge\r\n [id]=\"idComponent\"\r\n [title]=\"''\"\r\n height=\"100%\"\r\n width=\"100%\"\r\n [enablePointerDrag]=\"false\"\r\n [tooltip]=\"{ enable: visualConfig.enableTooltip }\"\r\n (loaded)=\"onGaugeLoaded($event)\">\r\n\r\n <e-axes>\r\n <e-axis\r\n [minimum]=\"visualConfig.minimum\"\r\n [maximum]=\"visualConfig.maximum\"\r\n [startAngle]=\"visualConfig.startAngle\"\r\n [endAngle]=\"visualConfig.endAngle\"\r\n [lineStyle]=\"{ width: 0 }\"\r\n [labelStyle]=\"visualConfig.showTicks ? { position: 'Outside', offset: visualConfig.labelOffset, font: { size: '11px' } } : { position: 'Outside', font: { size: '0px' } }\"\r\n [majorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 2, height: 10 } : { width: 0 }\"\r\n [minorTicks]=\"visualConfig.showTicks ? { position: visualConfig.tickPosition, width: 1, height: 5 } : { width: 0 }\">\r\n\r\n <!-- Rangos de color sobre el arco (modo convencional por rangos o modo ancho variable) -->\r\n @if (!visualConfig.enableProgressTrack || visualConfig.isVariableWidth) {\r\n <e-ranges>\r\n @for (range of visualConfig.ranges; track $index) {\r\n <e-range\r\n [start]=\"range.start\"\r\n [end]=\"range.end\"\r\n [color]=\"range.color\"\r\n [startWidth]=\"range.startWidth ?? range.width ?? 8\"\r\n [endWidth]=\"range.endWidth ?? range.width ?? 8\"\r\n [roundedCornerRadius]=\"range.roundedCornerRadius ?? 0\">\r\n </e-range>\r\n }\r\n </e-ranges>\r\n }\r\n\r\n <!-- Punteros: tanto la l\u00EDnea de fondo (vac\u00EDo) como la de llenado (l\u00EDquido) usan RangeBar para alineaci\u00F3n perfecta -->\r\n <e-pointers>\r\n @if (visualConfig.enableProgressTrack && !visualConfig.isVariableWidth) {\r\n <!-- L\u00EDnea 1: Fondo / vac\u00EDo (del m\u00EDnimo al m\u00E1ximo) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"visualConfig.maximum\"\r\n [color]=\"visualConfig.trackColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: false }\">\r\n </e-pointer>\r\n\r\n <!-- L\u00EDnea 2: Llenado progresivo / l\u00EDquido (del m\u00EDnimo al valor actual) -->\r\n <e-pointer\r\n type=\"RangeBar\"\r\n [value]=\"gaugePoint.value\"\r\n [color]=\"visualConfig.progressColor\"\r\n [pointerWidth]=\"visualConfig.progressWidth\"\r\n [roundedCornerRadius]=\"visualConfig.cornerRadius\"\r\n [animation]=\"{ enable: true, duration: 900 }\">\r\n </e-pointer>\r\n }\r\n\r\n @if ((!visualConfig.enableProgressTrack || visualConfig.showNeedleWithProgress) && (!visualConfig.isVariableWidth || visualConfig.showNeedleWithProgress)) {\r\n <e-pointer\r\n [value]=\"gaugePoint.value\"\r\n type=\"Needle\"\r\n [color]=\"visualConfig.pointerColor\"\r\n [needleTail]=\"{ length: '18%' }\"\r\n [cap]=\"{ radius: 8, color: visualConfig.pointerColor }\">\r\n </e-pointer>\r\n }\r\n </e-pointers>\r\n\r\n <!-- Etiqueta central con el valor -->\r\n @if (visualConfig.showLabel) {\r\n <e-annotations>\r\n <e-annotation\r\n [angle]=\"visualConfig.labelAngle\"\r\n [radius]=\"visualConfig.labelRadius\"\r\n zIndex=\"1\"\r\n [content]=\"annotationHtml\">\r\n </e-annotation>\r\n </e-annotations>\r\n }\r\n\r\n </e-axis>\r\n </e-axes>\r\n\r\n </ejs-circulargauge>\r\n </div>\r\n\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.gauge-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}.gauge-header-title{padding:10px 14px 2px;font-size:clamp(.85rem,2.2vw,1.05rem);font-weight:600;color:#1e293b;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;flex-shrink:0}.gauge-wrapper{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:240px;flex:1;min-width:0;overflow:hidden}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
2797
2844
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { gaugeInstance: [{
2798
2845
  type: ViewChild,
2799
2846
  args: ['circulargauge']
@@ -2870,1822 +2917,1840 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
2870
2917
  args: ['config']
2871
2918
  }] } });
2872
2919
 
2873
- /*BEGIN - COMPONENTES VALIDOS PARA WIDGET*/
2874
- class YwDashboardWidgetComponent {
2875
- constructor() {
2876
- this.componentWidgetId = '';
2877
- this.typeComponent = '';
2878
- this.varShowConfigButtons = false;
2879
- this.styleWidget = input(DEFAULT_WIDGET_STYLE, ...(ngDevMode ? [{ debugName: "styleWidget" }] : []));
2880
- // Signal calculada que combina los valores recibidos con los valores por defecto
2881
- this.instanceStyleWidget = computed(() => ({
2882
- widgetBackgroundColor: this.styleWidget()?.widgetBackgroundColor ?? DEFAULT_WIDGET_STYLE.widgetBackgroundColor,
2883
- widgetBorderColor: this.styleWidget()?.widgetBorderColor ?? DEFAULT_WIDGET_STYLE.widgetBorderColor
2884
- }), ...(ngDevMode ? [{ debugName: "instanceStyleWidget" }] : []));
2885
- this.configWidget = {
2886
- widgetId: '',
2887
- title: '',
2888
- typeComponent: '',
2889
- showConfigButtons: false,
2890
- widgetStyle: DEFAULT_WIDGET_STYLE,
2891
- layoutConfig: { id: '', w: 0, h: 0, content: undefined },
2892
- controlConfig: {},
2893
- controlTables: []
2894
- };
2895
- this.controlConfig = {};
2896
- this.controlData = {};
2897
- this.isLocked = model(false, ...(ngDevMode ? [{ debugName: "isLocked" }] : []));
2898
- this.isMaximized = signal(false, ...(ngDevMode ? [{ debugName: "isMaximized" }] : []));
2899
- this.eventDeleteRequest = output();
2900
- this.eventLockToggleRequest = output();
2901
- this.eventMaximizeToggleRequest = output();
2902
- this.eventShowConfigFormRequest = output();
2920
+ const parseSyncFusionData = (allTablesData, configData_) => {
2921
+ if (!allTablesData || !configData_) {
2922
+ return { series: [], labelData: {} };
2903
2923
  }
2904
- ngOnInit() {
2905
- this.componentWidgetId = 'cmpnt-' + this.configWidget.widgetId;
2906
- this.typeComponent = this.configWidget.typeComponent;
2907
- this.dataWidget = { idComponent: this.componentWidgetId,
2908
- controlConfig: this.controlConfig,
2909
- controlData: this.controlData
2910
- };
2911
- console.log('DATOS INICIALES INIT:');
2912
- console.log(this.dataWidget);
2924
+ console.log("bubble data:--", allTablesData, configData_);
2925
+ // SI LO MANDAN COMO UN ARREGLO PLANO, LO ENVOLVEMOS AUTOMÁTICAMENTE
2926
+ if (Array.isArray(allTablesData)) {
2927
+ const defaultTableName = configData_?.configData?.table || 'default';
2928
+ allTablesData = { [defaultTableName]: allTablesData };
2913
2929
  }
2914
- ngAfterViewInit() {
2915
- this.varShowConfigButtons = this.configWidget.showConfigButtons || false;
2930
+ const { ruleColorPoint, configData, xLabelText, yLabelText, title, chartoColoredLine, opacity, emptyPointSettings } = configData_;
2931
+ console.log("configData_:.::", configData_);
2932
+ const cfg = configData || {};
2933
+ const { xAxis, yAxis, joins, configJoins, table, serie, tooltip, legends } = cfg;
2934
+ let series = [];
2935
+ // Función auxiliar robusta para obtener propiedades (soporta notación con puntos de los joins)
2936
+ // const getNestedValue = (obj: any, path: string) => {
2937
+ // if (!obj || !path) return undefined;
2938
+ // if (obj[path] !== undefined) return obj[path];
2939
+ // return path.split('.').reduce((acc, part) => (acc && acc[part] !== undefined ? acc[part] : undefined), obj);
2940
+ // };
2941
+ const sortByXAxis = (data) => {
2942
+ if (!xAxis)
2943
+ return data;
2944
+ return [...data].sort((a, b) => {
2945
+ const aValue = getNestedValue(a, xAxis);
2946
+ const bValue = getNestedValue(b, xAxis);
2947
+ if (aValue == null)
2948
+ return 1;
2949
+ if (bValue == null)
2950
+ return -1;
2951
+ return String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' });
2952
+ });
2953
+ };
2954
+ if (joins === true) {
2955
+ if (!configJoins) {
2956
+ series = [];
2957
+ }
2958
+ else {
2959
+ const unifiedTable = resolveJoinedTableData(allTablesData, configJoins);
2960
+ console.log('unifiedTable bubble', unifiedTable);
2961
+ if (unifiedTable && unifiedTable.length > 0) {
2962
+ // const validRawData = unifiedTable.filter(
2963
+ // (item: any) =>
2964
+ // item &&
2965
+ // xAxis &&
2966
+ // getNestedValue(item, xAxis) !== undefined &&
2967
+ // getNestedValue(item, xAxis) !== null,
2968
+ // );
2969
+ const validRawData = unifiedTable.filter((item) => item &&
2970
+ xAxis &&
2971
+ getNestedValue(item, xAxis) !== undefined &&
2972
+ getNestedValue(item, xAxis) !== null);
2973
+ const orderedRawData = sortByXAxis(validRawData);
2974
+ if (orderedRawData.length > 0) {
2975
+ if (serie) {
2976
+ // 2. Agrupamos los datos por el valor de la serie (ej. sucursal o vendedor)
2977
+ const groupedBySerie = orderedRawData.reduce((acc, item) => {
2978
+ const serieVal = getNestedValue(item, serie);
2979
+ const serieKey = serieVal !== undefined && serieVal !== null
2980
+ ? String(serieVal)
2981
+ : 'Desconocido';
2982
+ if (!acc[serieKey]) {
2983
+ acc[serieKey] = [];
2984
+ }
2985
+ acc[serieKey].push(item);
2986
+ return acc;
2987
+ }, {});
2988
+ series = Object.entries(groupedBySerie).map(([serieName, groupItems]) => {
2989
+ const orderedGroupItems = [...groupItems].sort((a, b) => {
2990
+ const aValue = getNestedValue(a, xAxis);
2991
+ const bValue = getNestedValue(b, xAxis);
2992
+ return String(aValue).localeCompare(String(bValue), undefined, {
2993
+ numeric: true,
2994
+ sensitivity: 'base'
2995
+ });
2996
+ });
2997
+ console.log("SERIES NAME:", orderedGroupItems);
2998
+ const dataSource = orderedGroupItems.map((item) => {
2999
+ const rawVal = getNestedValue(item, yAxis);
3000
+ const valorFinal = rawVal !== undefined
3001
+ ? typeof rawVal === 'number'
3002
+ ? rawVal
3003
+ : Number(rawVal) || 0
3004
+ : 0;
3005
+ const sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
3006
+ return {
3007
+ x: String(getNestedValue(item, xAxis)),
3008
+ y: valorFinal,
3009
+ size: sizeFinal,
3010
+ name: String(getNestedValue(item, xAxis)),
3011
+ // ...(chartoColoredLine
3012
+ // ? { color: getRandomProfessionalHex() }
3013
+ // : {})
3014
+ };
3015
+ });
3016
+ return {
3017
+ name: serieName === 'default' ? 'Serie 1' : serieName,
3018
+ dataSource: aggregateByX(dataSource),
3019
+ opacity: opacity,
3020
+ emptyPointSettings: emptyPointSettings,
3021
+ // dashArray: dashArray !== undefined ? dashArray : undefined,
3022
+ // width: width !== undefined ? width : undefined,
3023
+ // widthLine: widthLine !== undefined ? widthLine : undefined,
3024
+ };
3025
+ });
3026
+ }
3027
+ else {
3028
+ const dataSource = orderedRawData.map((item) => {
3029
+ let valorFinal = 0;
3030
+ let sizeFinal = 0;
3031
+ // const shouldColorLine = chartoColoredLine;
3032
+ const rawVal = getNestedValue(item, yAxis);
3033
+ // const rawSize = getNestedValue(item, size); // <--- Extraer el valor real
3034
+ if (rawVal !== undefined) {
3035
+ valorFinal = typeof rawVal === 'number' ? rawVal : Number(rawVal) || 0;
3036
+ }
3037
+ // if (rawSize !== undefined) {
3038
+ sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
3039
+ // }
3040
+ return {
3041
+ x: String(getNestedValue(item, xAxis)),
3042
+ y: valorFinal,
3043
+ size: sizeFinal, // <--- Valor numérico extraído
3044
+ name: String(getNestedValue(item, xAxis)),
3045
+ // ...(shouldColorLine ? { color: getRandomProfessionalHex() } : {})
3046
+ };
3047
+ });
3048
+ series = [
3049
+ {
3050
+ name: xAxis.split(".")[0] && xAxis !== 'default' ? xAxis.split(".")[0] : 'Serie Join',
3051
+ dataSource: aggregateByX(dataSource),
3052
+ opacity: opacity,
3053
+ emptyPointSettings: emptyPointSettings,
3054
+ // dashArray: dashArray !== undefined ? dashArray : undefined,
3055
+ // width: width !== undefined ? width : undefined,
3056
+ // widthLine: widthLine !== undefined ? widthLine : undefined,
3057
+ }
3058
+ ];
3059
+ }
3060
+ }
3061
+ }
3062
+ }
2916
3063
  }
2917
- ngOnChanges(changes) {
2918
- if (changes['configWidget']) {
2919
- //console.log('Nuevo valor configWidget:', this.configWidget);
2920
- this.varShowConfigButtons = this.configWidget.showConfigButtons || false;
2921
- ;
3064
+ else {
3065
+ let normalizedTables = [];
3066
+ if (Array.isArray(table)) {
3067
+ normalizedTables = table.filter((t) => typeof t === 'string' && t.trim() !== '');
2922
3068
  }
2923
- if (changes['controlConfig']) {
2924
- console.log('Nuevo valor controlConfig:', this.controlConfig);
3069
+ else if (typeof table === 'string' && table.trim() !== '') {
3070
+ normalizedTables = [table];
2925
3071
  }
2926
- if (changes['controlData']) {
2927
- //console.log('Nuevo valor controlData:', this.controlData);
2928
- this.dataWidget = { idComponent: this.componentWidgetId,
2929
- controlConfig: this.controlConfig,
2930
- controlData: this.controlData
2931
- };
3072
+ else {
3073
+ normalizedTables = Object.keys(allTablesData);
2932
3074
  }
2933
- }
2934
- /* BEGIN - BARRA DE EVENTOS DEL WIDGET */
2935
- //Eliminacion de Widget - Conectado al Dashboard
2936
- onDeleteClick() {
2937
- this.eventDeleteRequest.emit(this.configWidget.widgetId);
2938
- }
2939
- onLockClick() {
2940
- // Invertimos el estado local y notificamos al padre
2941
- const nextState = !this.isLocked();
2942
- this.isLocked.set(nextState);
2943
- this.eventLockToggleRequest.emit({ widgetId: this.configWidget.widgetId, shouldLock: nextState });
2944
- }
2945
- onMaximizeClick() {
2946
- const nextState = !this.isMaximized();
2947
- this.isMaximized.set(nextState);
2948
- this.eventMaximizeToggleRequest.emit({ widgetId: this.configWidget.widgetId, shouldMaximize: nextState });
2949
- }
2950
- onShowFormConfigClick() {
2951
- this.eventShowConfigFormRequest.emit(this.configWidget.widgetId);
2952
- }
2953
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2954
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardWidgetComponent, isStandalone: true, selector: "yw-dashboard-widget", inputs: { styleWidget: { classPropertyName: "styleWidget", publicName: "styleWidget", isSignal: true, isRequired: false, transformFunction: null }, configWidget: { classPropertyName: "configWidget", publicName: "configWidget", isSignal: false, isRequired: false, transformFunction: null }, controlConfig: { classPropertyName: "controlConfig", publicName: "controlConfig", isSignal: false, isRequired: false, transformFunction: null }, controlData: { classPropertyName: "controlData", publicName: "controlData", isSignal: false, isRequired: false, transformFunction: null }, isLocked: { classPropertyName: "isLocked", publicName: "isLocked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isLocked: "isLockedChange", eventDeleteRequest: "eventDeleteRequest", eventLockToggleRequest: "eventLockToggleRequest", eventMaximizeToggleRequest: "eventMaximizeToggleRequest", eventShowConfigFormRequest: "eventShowConfigFormRequest" }, host: { properties: { "style.--widget-bg": "instanceStyleWidget().widgetBackgroundColor", "style.--widget-border-color": "instanceStyleWidget().widgetBorderColor" } }, usesOnChanges: true, ngImport: i0, template: "\r\n<!-- <div class=\"widget-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\">{{ isLocked() ? '\uD83D\uDD12' : '\uD83D\uDD13' }}</button>\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\">{{ isMaximized() ? '\uD83D\uDDD7' : '\uD83D\uDDD6' }}</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div> -->\r\n\r\n<div class=\"widget-action-bar\">\r\n <!-- Bot\u00F3n Bloquear / Desbloquear -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\" [title]=\"isLocked() ? 'Desbloquear' : 'Bloquear'\">\r\n <svg *ngIf=\"isLocked(); else unlockedIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path></svg>\r\n <ng-template #unlockedIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Maximizar / Restaurar -->\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\" [title]=\"isMaximized() ? 'Restaurar' : 'Maximizar'\">\r\n <svg *ngIf=\"isMaximized(); else maxIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\"></path></svg>\r\n <ng-template #maxIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Eliminar -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\" title=\"Eliminar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path></svg>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Configuraci\u00F3n -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\" title=\"Configurar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"></circle><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"></path></svg>\r\n </button>\r\n</div>\r\n\r\n<div class=\"widget-content-wrapper\">\r\n @if ( typeComponent == 'dummy' ) {\r\n <ng-container *ngTemplateOutlet=\"testdummy; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'empty-visual' ) {\r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-bar' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbar; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-line' ) {\r\n <ng-container *ngTemplateOutlet=\"chartline; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-doughnut' ) {\r\n <ng-container *ngTemplateOutlet=\"chartdoughnut; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-gauge' ) {\r\n <ng-container *ngTemplateOutlet=\"chartgauge; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- DUMMY TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #testdummy let-control = \"control\">\r\n <lib-test-chart [idComponent]=\"control.idComponent\" > \r\n </lib-test-chart>\r\n</ng-template>\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BAR CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbar let-control = \"control\">\r\n <yw-bar-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-bar-chart>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartline let-control = \"control\">\r\n <yw-line-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-line-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE DOUGHNUT TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartdoughnut let-control = \"control\">\r\n <yw-pie-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-pie-chart>\r\n</ng-template>\r\n\r\n \r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE GAUGE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartgauge let-control = \"control\">\r\n <yw-gauge-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-gauge-chart>\r\n</ng-template>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;flex-direction:column;width:100%;overflow:hidden;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #007bff)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-content-wrapper{width:100%;flex-grow:1;height:0;display:flex;flex-direction:column}.widget-action-bar button,.widget-action-bar .btn-action,.widget-action-bar .btn-delete,.widget-action-bar .btn-config{background:transparent;border:1px solid #d9d9d9;border-radius:4px;cursor:pointer;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:#595959;padding:0;transition:all .2s ease}.widget-action-bar button svg,.widget-action-bar .btn-action svg,.widget-action-bar .btn-delete svg,.widget-action-bar .btn-config svg{width:14px;height:14px}.widget-action-bar button:hover,.widget-action-bar .btn-action:hover,.widget-action-bar .btn-delete:hover,.widget-action-bar .btn-config:hover{background-color:#f5f5f5;color:#262626;border-color:#bfbfbf}.widget-action-bar button:active,.widget-action-bar .btn-action:active,.widget-action-bar .btn-delete:active,.widget-action-bar .btn-config:active{background-color:#e6e6e6}.widget-action-bar .btn-delete:hover{background-color:#fff1f0;color:#ff4d4f;border-color:#ffa39e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TestChartComponent, selector: "lib-test-chart", inputs: ["idComponent"] }, { kind: "component", type: YwEmptyVisualComponent, selector: "yw-empty-visual", inputs: ["idComponent", "config"] }, { kind: "component", type: LineChartComponent, selector: "yw-line-chart", inputs: ["chartData", "config", "idComponent"] }, { kind: "component", type: BarChartComponent, selector: "yw-bar-chart", inputs: ["chartData", "config", "idComponent"] }, { kind: "component", type: PieChartComponent, selector: "yw-pie-chart", inputs: ["idComponent", "chartData", "config"] }, { kind: "component", type: GaugeChartComponent, selector: "yw-gauge-chart", inputs: ["idComponent", "chartData", "config"] }] }); }
2955
- }
2956
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardWidgetComponent, decorators: [{
2957
- type: Component,
2958
- args: [{ selector: 'yw-dashboard-widget', standalone: true, imports: [CommonModule,
2959
- TestChartComponent,
2960
- YwEmptyVisualComponent,
2961
- LineChartComponent,
2962
- BarChartComponent,
2963
- PieChartComponent,
2964
- GaugeChartComponent
2965
- ], host: {
2966
- '[style.--widget-bg]': 'instanceStyleWidget().widgetBackgroundColor',
2967
- '[style.--widget-border-color]': 'instanceStyleWidget().widgetBorderColor'
2968
- }, template: "\r\n<!-- <div class=\"widget-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\">{{ isLocked() ? '\uD83D\uDD12' : '\uD83D\uDD13' }}</button>\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\">{{ isMaximized() ? '\uD83D\uDDD7' : '\uD83D\uDDD6' }}</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div> -->\r\n\r\n<div class=\"widget-action-bar\">\r\n <!-- Bot\u00F3n Bloquear / Desbloquear -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\" [title]=\"isLocked() ? 'Desbloquear' : 'Bloquear'\">\r\n <svg *ngIf=\"isLocked(); else unlockedIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path></svg>\r\n <ng-template #unlockedIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Maximizar / Restaurar -->\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\" [title]=\"isMaximized() ? 'Restaurar' : 'Maximizar'\">\r\n <svg *ngIf=\"isMaximized(); else maxIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\"></path></svg>\r\n <ng-template #maxIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Eliminar -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\" title=\"Eliminar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path></svg>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Configuraci\u00F3n -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\" title=\"Configurar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"></circle><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"></path></svg>\r\n </button>\r\n</div>\r\n\r\n<div class=\"widget-content-wrapper\">\r\n @if ( typeComponent == 'dummy' ) {\r\n <ng-container *ngTemplateOutlet=\"testdummy; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'empty-visual' ) {\r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-bar' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbar; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-line' ) {\r\n <ng-container *ngTemplateOutlet=\"chartline; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-doughnut' ) {\r\n <ng-container *ngTemplateOutlet=\"chartdoughnut; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-gauge' ) {\r\n <ng-container *ngTemplateOutlet=\"chartgauge; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- DUMMY TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #testdummy let-control = \"control\">\r\n <lib-test-chart [idComponent]=\"control.idComponent\" > \r\n </lib-test-chart>\r\n</ng-template>\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BAR CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbar let-control = \"control\">\r\n <yw-bar-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-bar-chart>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartline let-control = \"control\">\r\n <yw-line-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-line-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE DOUGHNUT TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartdoughnut let-control = \"control\">\r\n <yw-pie-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-pie-chart>\r\n</ng-template>\r\n\r\n \r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE GAUGE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartgauge let-control = \"control\">\r\n <yw-gauge-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-gauge-chart>\r\n</ng-template>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;flex-direction:column;width:100%;overflow:hidden;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #007bff)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-content-wrapper{width:100%;flex-grow:1;height:0;display:flex;flex-direction:column}.widget-action-bar button,.widget-action-bar .btn-action,.widget-action-bar .btn-delete,.widget-action-bar .btn-config{background:transparent;border:1px solid #d9d9d9;border-radius:4px;cursor:pointer;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:#595959;padding:0;transition:all .2s ease}.widget-action-bar button svg,.widget-action-bar .btn-action svg,.widget-action-bar .btn-delete svg,.widget-action-bar .btn-config svg{width:14px;height:14px}.widget-action-bar button:hover,.widget-action-bar .btn-action:hover,.widget-action-bar .btn-delete:hover,.widget-action-bar .btn-config:hover{background-color:#f5f5f5;color:#262626;border-color:#bfbfbf}.widget-action-bar button:active,.widget-action-bar .btn-action:active,.widget-action-bar .btn-delete:active,.widget-action-bar .btn-config:active{background-color:#e6e6e6}.widget-action-bar .btn-delete:hover{background-color:#fff1f0;color:#ff4d4f;border-color:#ffa39e}\n"] }]
2969
- }], ctorParameters: () => [], propDecorators: { styleWidget: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleWidget", required: false }] }], configWidget: [{
2970
- type: Input
2971
- }], controlConfig: [{
2972
- type: Input
2973
- }], controlData: [{
2974
- type: Input
2975
- }], isLocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLocked", required: false }] }, { type: i0.Output, args: ["isLockedChange"] }], eventDeleteRequest: [{ type: i0.Output, args: ["eventDeleteRequest"] }], eventLockToggleRequest: [{ type: i0.Output, args: ["eventLockToggleRequest"] }], eventMaximizeToggleRequest: [{ type: i0.Output, args: ["eventMaximizeToggleRequest"] }], eventShowConfigFormRequest: [{ type: i0.Output, args: ["eventShowConfigFormRequest"] }] } });
3075
+ series = normalizedTables
3076
+ .map((tableName) => {
3077
+ const rawData = (allTablesData && allTablesData[tableName]) ? allTablesData[tableName] : [];
3078
+ if (!rawData || rawData.length === 0)
3079
+ return null;
3080
+ const orderedRawData = rawData.filter((item) => item && xAxis && getNestedValue(item, xAxis) !== undefined && getNestedValue(item, xAxis) !== null);
3081
+ if (orderedRawData.length === 0)
3082
+ return null;
3083
+ const dataSource = orderedRawData.map((item) => {
3084
+ let valorFinal = 0;
3085
+ let sizeFinal = 0;
3086
+ const shouldColorLine = chartoColoredLine;
3087
+ const rawVal = getNestedValue(item, yAxis);
3088
+ // const rawSize = getNestedValue(item, size);
3089
+ if (rawVal !== undefined) {
3090
+ valorFinal = typeof rawVal === "number" ? rawVal : Number(rawVal) || 0;
3091
+ }
3092
+ // if (rawSize !== undefined) {
3093
+ sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
3094
+ // }
3095
+ return {
3096
+ x: String(getNestedValue(item, xAxis)),
3097
+ y: valorFinal,
3098
+ size: sizeFinal, // <--- Valor numérico extraído
3099
+ name: String(getNestedValue(item, xAxis))
3100
+ // ...(shouldColorLine ? { color: getRandomProfessionalHex() } : {})
3101
+ };
3102
+ });
3103
+ return {
3104
+ name: tableName === 'default' ? xAxis : tableName,
3105
+ dataSource: aggregateByX(dataSource),
3106
+ opacity: opacity,
3107
+ emptyPointSettings: emptyPointSettings,
3108
+ // dashArray: dashArray !== undefined ? dashArray : undefined,
3109
+ // width: width !== undefined ? width : undefined,
3110
+ // widthLine: widthLine !== undefined ? widthLine : undefined,
3111
+ };
3112
+ })
3113
+ .filter((item) => item !== null);
3114
+ }
3115
+ const labelData = {
3116
+ title: title || "",
3117
+ titleStyle: {
3118
+ size: '16px',
3119
+ fontWeight: '600',
3120
+ color: '#2b2b2b',
3121
+ fontFamily: 'Inherit',
3122
+ textAlignment: 'Center'
3123
+ },
3124
+ axisX: {
3125
+ valueType: 'Category',
3126
+ // title: typeof xLabelText === 'string' ? xLabelText : 'Categoría',
3127
+ // labelIntersectAction: 'Rotate90',
3128
+ // labelIntersectAction: 'Rotate45',
3129
+ majorGridLines: { width: 0 },
3130
+ majorTickLines: { width: 0 },
3131
+ lineStyle: { width: 0 },
3132
+ labelStyle: { color: '#64748B', size: '12px', fontFamily: 'Inherit' }
3133
+ },
3134
+ axisY: {
3135
+ title: typeof yLabelText === 'string' ? yLabelText : 'Valor',
3136
+ minimum: 0,
3137
+ edgeLabelPlacement: 'Shift',
3138
+ lineStyle: { width: 0 },
3139
+ majorTickLines: { width: 0 },
3140
+ minorTickLines: { width: 0 },
3141
+ majorGridLines: { width: 0.5, color: '#E2E8F0', dashArray: '4,4' },
3142
+ labelStyle: { color: '#94A3B8', size: '12px', fontFamily: 'Inherit' },
3143
+ titleStyle: { size: '12px', color: '#64748B', fontFamily: 'Inherit' }
3144
+ },
3145
+ marker: {
3146
+ visible: true,
3147
+ width: 8,
3148
+ height: 8,
3149
+ dataLabel: {
3150
+ visible: true,
3151
+ position: 'Top', // Centra el texto exactamente en el medio de la burbuja
3152
+ name: 'text', // Asegúrate de que apunte al campo de tu data que trae el nombre (ej: 'text' o 'name')
3153
+ font: {
3154
+ fontWeight: '600',
3155
+ color: '#334155', // Color del texto (oscuro para que contraste con el fondo claro) o '#FFFFFF' si prefieres blanco
3156
+ size: '12px',
3157
+ fontFamily: 'Inherit'
3158
+ }
3159
+ }
3160
+ },
3161
+ palettes: ['#6366F1', '#22D3EE', '#F59E0B', '#10B981', '#F43F5E'],
3162
+ border: { width: 0 },
3163
+ chartArea: { border: { width: 0 }, margin: { bottom: 12 } },
3164
+ tooltip: {
3165
+ ...(tooltip?.enable !== undefined ? { enable: tooltip.enable } : {}),
3166
+ ...(tooltip?.format ? { format: tooltip.format } : {}),
3167
+ ...(tooltip?.header ? { header: tooltip.header } : {}),
3168
+ ...(tooltip?.shared !== undefined ? { shared: tooltip.shared } : {}),
3169
+ enableHighlight: true,
3170
+ },
3171
+ legendSettings: {
3172
+ visible: legends?.visible !== undefined ? legends.visible : undefined,
3173
+ position: legends?.position !== undefined ? legends.position : undefined
3174
+ },
3175
+ ruleColorPoint: ruleColorPoint !== undefined ? ruleColorPoint : undefined
3176
+ };
3177
+ return {
3178
+ series,
3179
+ labelData
3180
+ };
3181
+ };
2976
3182
 
2977
- class YwBaseSlicerComponent {
2978
- constructor() {
2979
- // Inputs de configuración del rango de fechas
3183
+ class BubbleChartSyncfusionComponent {
3184
+ constructor(elRef, cdr) {
3185
+ this.elRef = elRef;
3186
+ this.cdr = cdr;
2980
3187
  this.idComponent = '';
2981
- this.dataType = 'date';
2982
- this.label = input('Fecha', ...(ngDevMode ? [{ debugName: "label" }] : []));
2983
- this.min = input(...(ngDevMode ? [undefined, { debugName: "min" }] : []));
2984
- this.max = input(...(ngDevMode ? [undefined, { debugName: "max" }] : []));
2985
- this.step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
2986
- this.minCurrent = input(...(ngDevMode ? [undefined, { debugName: "minCurrent" }] : []));
2987
- this.maxCurrent = input(...(ngDevMode ? [undefined, { debugName: "maxCurrent" }] : []));
2988
- this.minNumber = 0;
2989
- this.maxNumber = 1000;
2990
- this.minDate = '2020-01-01';
2991
- this.maxDate = '2022-12-31';
2992
- // Evento de salida
2993
- this.rangeChange = output();
2994
- // Signals para las fechas seleccionadas
2995
- this.startDate = signal('2020-01-01', ...(ngDevMode ? [{ debugName: "startDate" }] : []));
2996
- this.endDate = signal('2022-09-22', ...(ngDevMode ? [{ debugName: "endDate" }] : []));
2997
- // Signals para los numeros seleccionadas
2998
- this.startVal = signal(0, ...(ngDevMode ? [{ debugName: "startVal" }] : []));
2999
- this.endVal = signal(1000, ...(ngDevMode ? [{ debugName: "endVal" }] : []));
3000
- this.minTimestamp = computed(() => new Date(this.minDate).getTime(), ...(ngDevMode ? [{ debugName: "minTimestamp" }] : []));
3001
- this.maxTimestamp = computed(() => new Date(this.maxDate).getTime(), ...(ngDevMode ? [{ debugName: "maxTimestamp" }] : []));
3002
- this.startTimestamp = computed(() => new Date(this.startDate()).getTime(), ...(ngDevMode ? [{ debugName: "startTimestamp" }] : []));
3003
- this.endTimestamp = computed(() => new Date(this.endDate()).getTime(), ...(ngDevMode ? [{ debugName: "endTimestamp" }] : []));
3004
- // Inicializar valores al arrancar
3005
- effect(() => {
3006
- if (this.dataType == 'date') {
3007
- this.startDate.set(this.minCurrent() || this.minDate);
3008
- this.endDate.set(this.maxCurrent() || this.maxDate);
3009
- }
3010
- if (this.dataType == 'number') {
3011
- this.startVal.set(this.minCurrent() || this.minNumber);
3012
- this.endVal.set(this.maxCurrent() || this.maxNumber);
3013
- }
3014
- }, { allowSignalWrites: true });
3015
- // Notificar al padre cada vez que cambien los valores
3016
- effect(() => {
3017
- if (this.dataType == 'date') {
3018
- this.rangeChange.emit({
3019
- startDate: this.startDate(),
3020
- endDate: this.endDate()
3021
- });
3022
- }
3023
- if (this.dataType == 'number') {
3024
- this.rangeChange.emit({
3025
- minValue: this.startVal(),
3026
- maxValue: this.endVal()
3027
- });
3028
- }
3029
- });
3188
+ this.dataChartParse = [];
3189
+ this.currentLabelSize = '';
3190
+ this.currentWidgetWidth = 0; // Valor inicial por defecto
3030
3191
  }
3031
3192
  ngOnInit() {
3032
- if (this.dataType == 'date') {
3033
- this.minDate = this.min();
3034
- this.maxDate = this.max();
3035
- this.minTimestamp = computed(() => new Date(this.minDate).getTime(), ...(ngDevMode ? [{ debugName: "minTimestamp" }] : []));
3036
- this.maxTimestamp = computed(() => new Date(this.maxDate).getTime(), ...(ngDevMode ? [{ debugName: "maxTimestamp" }] : []));
3037
- this.startTimestamp = computed(() => new Date(this.startDate()).getTime(), ...(ngDevMode ? [{ debugName: "startTimestamp" }] : []));
3038
- this.endTimestamp = computed(() => new Date(this.endDate()).getTime(), ...(ngDevMode ? [{ debugName: "endTimestamp" }] : []));
3039
- }
3040
- if (this.dataType == 'number') {
3041
- this.minNumber = this.min();
3042
- this.maxNumber = this.max();
3043
- }
3044
- }
3045
- // Handlers para la entrada manual de fechas
3046
- onStartDateInput(value) {
3047
- if (value && value <= this.endDate()) {
3048
- this.startDate.set(value);
3049
- }
3193
+ this.updateParsedData();
3050
3194
  }
3051
- onEndDateInput(value) {
3052
- if (value && value >= this.startDate()) {
3053
- this.endDate.set(value);
3195
+ ngOnChanges(changes) {
3196
+ if (changes['chartData'] || changes['config']) {
3197
+ this.updateParsedData();
3054
3198
  }
3055
3199
  }
3056
- // Métodos para cambiar desde el Input Numérico
3057
- onStartInput(event) {
3058
- const value = Number(event.target.value);
3059
- if (!isNaN(value) && value <= this.endVal() && value >= this.min()) {
3060
- this.startVal.set(value);
3061
- }
3200
+ ngAfterViewInit() {
3201
+ this._startResizeObserver();
3062
3202
  }
3063
- onEndInput(event) {
3064
- const value = Number(event.target.value);
3065
- if (!isNaN(value) && value >= this.startVal() && value <= this.max()) {
3066
- this.endVal.set(value);
3067
- }
3203
+ ngOnDestroy() {
3204
+ this._resizeObserver?.disconnect();
3068
3205
  }
3069
- // Handlers para el movimiento del slider
3070
- onStartSliderChange(event) {
3071
- if (this.dataType == 'date') {
3072
- const val = Number(event.target.value);
3073
- const dateStr = new Date(val).toISOString().split('T')[0];
3074
- if (val < this.endTimestamp()) {
3075
- this.startDate.set(dateStr);
3206
+ updateParsedData() {
3207
+ if (this.chartData && this.config) {
3208
+ const rawParsed = parseSyncFusionData(this.chartData, this.config);
3209
+ this.dataChartParse = rawParsed.series;
3210
+ this.labelData = rawParsed.labelData;
3211
+ if (this.dataLabel && this.labelData) {
3212
+ this.labelData.dataLabel = this.dataLabel;
3076
3213
  }
3077
- }
3078
- if (this.dataType == 'number') {
3079
- const val = Number(event.target.value);
3080
- if (val < this.endVal()) {
3081
- this.startVal.set(val);
3214
+ this.cdr.markForCheck();
3215
+ if (this.chartInstance) {
3216
+ setTimeout(() => {
3217
+ this.chartInstance?.refresh();
3218
+ });
3082
3219
  }
3083
3220
  }
3084
3221
  }
3085
- onEndSliderChange(event) {
3086
- if (this.dataType == 'date') {
3087
- const val = Number(event.target.value);
3088
- const dateStr = new Date(val).toISOString().split('T')[0];
3089
- if (val > this.startTimestamp()) {
3090
- this.endDate.set(dateStr);
3222
+ pointRender(args) {
3223
+ const yValue = Number(args.point.y);
3224
+ // =====================================================
3225
+ // EMPTY POINT / VALOR 0
3226
+ // Tiene prioridad sobre ruleColorPoint
3227
+ // =====================================================
3228
+ if (yValue === 0) {
3229
+ const emptyPoint = this.config.emptyPointSettings;
3230
+ if (emptyPoint && emptyPoint !== '') {
3231
+ args.fill = emptyPoint;
3091
3232
  }
3233
+ return;
3092
3234
  }
3093
- if (this.dataType == 'number') {
3094
- const val = Number(event.target.value);
3095
- if (val > this.startVal()) {
3096
- this.endVal.set(val);
3097
- }
3235
+ // =====================================================
3236
+ // RULE COLOR POINT
3237
+ // =====================================================
3238
+ const configPoint = this.config.ruleColorPoint || null;
3239
+ if (!configPoint) {
3240
+ return;
3241
+ }
3242
+ let passesRule = false;
3243
+ switch (configPoint.rule) {
3244
+ case 'threshold':
3245
+ passesRule =
3246
+ yValue >= (configPoint.thresholdValue || 0);
3247
+ break;
3248
+ case 'pair':
3249
+ passesRule = yValue % 2 === 0;
3250
+ break;
3251
+ case 'range':
3252
+ const min = configPoint.minRange || 0;
3253
+ const max = configPoint.maxRange || 100;
3254
+ passesRule =
3255
+ yValue >= min &&
3256
+ yValue <= max;
3257
+ break;
3258
+ default:
3259
+ passesRule = true;
3098
3260
  }
3261
+ args.fill = passesRule
3262
+ ? (configPoint.trueColor || '#10B981')
3263
+ : (configPoint.falseColor || '#EF4444');
3099
3264
  }
3100
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwBaseSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3101
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwBaseSlicerComponent, isStandalone: true, selector: "yw-base-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, minCurrent: { classPropertyName: "minCurrent", publicName: "minCurrent", isSignal: true, isRequired: false, transformFunction: null }, maxCurrent: { classPropertyName: "maxCurrent", publicName: "maxCurrent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n <label class=\"filter-label\">{{ label() }}</label>\r\n\r\n <!-- Inputs de fecha superiores -->\r\n <div class=\"date-inputs-row\">\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"startDate()\"\r\n [min]=\"minDate\"\r\n [max]=\"endDate()\"\r\n (change)=\"onStartDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"startVal()\"\r\n [min]=\"minNumber\"\r\n [max]=\"endVal()\"\r\n [step]=\"step()\"\r\n (input)=\"onStartInput($event)\" \r\n />\r\n } \r\n </div>\r\n\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"endDate()\"\r\n [min]=\"startDate()\"\r\n [max]=\"maxDate\"\r\n (change)=\"onEndDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"endVal()\"\r\n [min]=\"startVal()\"\r\n [max]=\"maxNumber\"\r\n [step]=\"step()\"\r\n (input)=\"onEndInput($event)\" \r\n />\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Dual Slider personalizado -->\r\n <div class=\"slider-container\">\r\n <div class=\"slider-track\"></div>\r\n @if (dataType === 'date') { \r\n <!-- Slider Inicio -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"startTimestamp()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider Fin -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"endTimestamp()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n }\r\n\r\n @if (dataType === 'number') { \r\n\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"startVal()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider M\u00E1ximo -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"endVal()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n\r\n }\r\n\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:360px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-label{font-size:13px;font-weight:600;color:#333}.date-inputs-row,.number-inputs-row{display:flex;gap:8px}.input-wrapper input[type=date]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=date]:focus{border-color:#a0aec0}.input-wrapper input[type=number]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;width:90px;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=number]:focus{border-color:#a0aec0}.slider-container{position:relative;width:100%;height:30px;display:flex;align-items:center;margin-top:4px}.slider-track{position:absolute;width:100%;height:2px;background-color:#666;border-radius:1px;z-index:1}.thumb{position:absolute;width:100%;appearance:none;-webkit-appearance:none;background:none;pointer-events:none;outline:none;margin:0;z-index:2}.thumb-right{z-index:3}.thumb::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box;transition:transform .1s ease}.thumb::-webkit-slider-thumb:hover{transform:scale(1.1);border-color:#333}.thumb::-moz-range-thumb{pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box}\n"] }); }
3102
- }
3103
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwBaseSlicerComponent, decorators: [{
3104
- type: Component,
3105
- args: [{ selector: 'yw-base-slicer', imports: [], host: {
3106
- '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3107
- }, template: "<div class=\"filter-container\">\r\n <label class=\"filter-label\">{{ label() }}</label>\r\n\r\n <!-- Inputs de fecha superiores -->\r\n <div class=\"date-inputs-row\">\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"startDate()\"\r\n [min]=\"minDate\"\r\n [max]=\"endDate()\"\r\n (change)=\"onStartDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"startVal()\"\r\n [min]=\"minNumber\"\r\n [max]=\"endVal()\"\r\n [step]=\"step()\"\r\n (input)=\"onStartInput($event)\" \r\n />\r\n } \r\n </div>\r\n\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"endDate()\"\r\n [min]=\"startDate()\"\r\n [max]=\"maxDate\"\r\n (change)=\"onEndDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"endVal()\"\r\n [min]=\"startVal()\"\r\n [max]=\"maxNumber\"\r\n [step]=\"step()\"\r\n (input)=\"onEndInput($event)\" \r\n />\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Dual Slider personalizado -->\r\n <div class=\"slider-container\">\r\n <div class=\"slider-track\"></div>\r\n @if (dataType === 'date') { \r\n <!-- Slider Inicio -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"startTimestamp()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider Fin -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"endTimestamp()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n }\r\n\r\n @if (dataType === 'number') { \r\n\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"startVal()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider M\u00E1ximo -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"endVal()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n\r\n }\r\n\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:360px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-label{font-size:13px;font-weight:600;color:#333}.date-inputs-row,.number-inputs-row{display:flex;gap:8px}.input-wrapper input[type=date]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=date]:focus{border-color:#a0aec0}.input-wrapper input[type=number]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;width:90px;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=number]:focus{border-color:#a0aec0}.slider-container{position:relative;width:100%;height:30px;display:flex;align-items:center;margin-top:4px}.slider-track{position:absolute;width:100%;height:2px;background-color:#666;border-radius:1px;z-index:1}.thumb{position:absolute;width:100%;appearance:none;-webkit-appearance:none;background:none;pointer-events:none;outline:none;margin:0;z-index:2}.thumb-right{z-index:3}.thumb::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box;transition:transform .1s ease}.thumb::-webkit-slider-thumb:hover{transform:scale(1.1);border-color:#333}.thumb::-moz-range-thumb{pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box}\n"] }]
3108
- }], ctorParameters: () => [], propDecorators: { idComponent: [{
3109
- type: Input
3110
- }], dataType: [{
3111
- type: Input
3112
- }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], minCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "minCurrent", required: false }] }], maxCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCurrent", required: false }] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }] } });
3113
-
3114
- class YwButtonSlicerComponent {
3115
- constructor() {
3116
- // Configuración de entradas
3117
- this.idComponent = '';
3118
- this.label = input('Tipo de producto', ...(ngDevMode ? [{ debugName: "label" }] : []));
3119
- this.options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
3120
- this.allowMultiple = input(true, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : [])); // Permite cambiar a selección única si es necesario
3121
- // Evento de salida con la lista de IDs seleccionados
3122
- this.selectionChange = output();
3123
- // Signal con los IDs seleccionados
3124
- this.selectedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedIds" }] : []));
3125
- // Current IDs seleccionados
3126
- this.currentIds = input([], ...(ngDevMode ? [{ debugName: "currentIds" }] : []));
3127
- // Emitir el listado en Array cada vez que la Signal de selección cambie
3128
- effect(() => {
3129
- this.selectionChange.emit(Array.from(this.selectedIds()));
3265
+ _startResizeObserver() {
3266
+ if (typeof ResizeObserver === 'undefined')
3267
+ return;
3268
+ const hostEl = this.elRef.nativeElement;
3269
+ this._resizeObserver = new ResizeObserver((entries) => {
3270
+ window.requestAnimationFrame(() => {
3271
+ for (const entry of entries) {
3272
+ this.currentWidgetWidth = entry.contentRect.width;
3273
+ if (this.currentWidgetWidth > 0) {
3274
+ this._updateAdaptiveLabelSize(this.currentWidgetWidth);
3275
+ if (this.chartInstance) {
3276
+ // SI ACTIVAMSO EL REFRESH DA UN REBOTE AL REDIMENCIONAR
3277
+ this.chartInstance.refresh();
3278
+ }
3279
+ }
3280
+ }
3281
+ });
3130
3282
  });
3283
+ this._resizeObserver.observe(hostEl);
3131
3284
  }
3132
- ngOnInit() {
3133
- if (this.currentIds() && this.currentIds().length > 0) {
3134
- this.selectedIds().clear();
3135
- this.currentIds().forEach((itemId) => {
3136
- this.selectedIds().add(itemId);
3137
- });
3285
+ _updateAdaptiveLabelSize(width) {
3286
+ if (this.config?.labelSize) {
3287
+ const parsedSize = typeof this.config.labelSize === 'number'
3288
+ ? this.config.labelSize
3289
+ : parseFloat(this.config.labelSize);
3290
+ if (!isNaN(parsedSize)) {
3291
+ if (width < 260) {
3292
+ const scale = Math.max(0.65, width / 260);
3293
+ this.currentLabelSize = `${Math.round(parsedSize * scale)}px`;
3294
+ }
3295
+ else {
3296
+ this.currentLabelSize = `${parsedSize}px`;
3297
+ }
3298
+ this.cdr.markForCheck();
3299
+ return;
3300
+ }
3138
3301
  }
3302
+ // Proporción estándar ideal (~7.5% del ancho del contenedor, acotado entre 13px y 26px)
3303
+ const base = Math.max(13, Math.min(26, Math.round(width * 0.075)));
3304
+ this.currentLabelSize = `${base}px`;
3305
+ this.cdr.markForCheck();
3139
3306
  }
3140
- clearSelection() {
3141
- this.selectedIds().clear();
3142
- this.selectionChange.emit([]);
3307
+ refresh() {
3308
+ this.updateParsedData();
3143
3309
  }
3144
- // Toggle para seleccionar / deseleccionar
3145
- toggleSelection(id) {
3146
- const current = new Set(this.selectedIds());
3147
- if (this.allowMultiple()) {
3148
- if (current.has(id)) {
3149
- current.delete(id);
3310
+ // pointRender(args: any) {
3311
+ // if (args.point?.y === 0) {
3312
+ // args.fill = this.emptyPointSettings.fill;
3313
+ // args.border = {
3314
+ // width: this.emptyPointSettings.border?.width ?? 0,
3315
+ // color: this.emptyPointSettings.border?.color ?? ''
3316
+ // };
3317
+ // }
3318
+ // }
3319
+ axisLabelRender(args) {
3320
+ const umbralWidget = 700;
3321
+ if (this.currentWidgetWidth > umbralWidget) {
3322
+ return;
3323
+ }
3324
+ // Si la tarjeta es estrecha, aplicamos el recorte de etiquetas
3325
+ if (args.text && typeof args.text === 'string') {
3326
+ const palabras = args.text.split(' ');
3327
+ if (palabras.length > 1) {
3328
+ const primeraPalabra = palabras[0];
3329
+ const inicialSegunda = palabras[1].charAt(0).toUpperCase();
3330
+ args.text = `${primeraPalabra} ${inicialSegunda}.`;
3150
3331
  }
3151
3332
  else {
3152
- current.add(id);
3333
+ if (args.text.length > 10) {
3334
+ args.text = args.text.substring(0, 9) + '.';
3335
+ }
3153
3336
  }
3154
3337
  }
3155
- else {
3156
- // Si es selección única
3157
- current.clear();
3158
- current.add(id);
3159
- }
3160
- this.selectedIds.set(current);
3161
- }
3162
- isSelected(id) {
3163
- return this.selectedIds().has(id);
3164
3338
  }
3165
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwButtonSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3166
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwButtonSlicerComponent, isStandalone: true, selector: "yw-button-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, allowMultiple: { classPropertyName: "allowMultiple", publicName: "allowMultiple", isSignal: true, isRequired: false, transformFunction: null }, currentIds: { classPropertyName: "currentIds", publicName: "currentIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n \r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\"> \r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" >\uD83E\uDDF9</button> \r\n </div>\r\n </div>\r\n\r\n\r\n <!-- Contenedor con barra de desplazamiento interna -->\r\n <div class=\"grid-scroll-container\">\r\n <div class=\"buttons-grid\">\r\n @for (item of options(); track item.id) {\r\n <button \r\n type=\"button\" \r\n class=\"option-btn\" \r\n [class.selected]=\"isSelected(item.id)\"\r\n (click)=\"toggleSelection(item.id)\"\r\n [title]=\"item.label\">\r\n <span class=\"btn-text\">{{ item.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:320px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#333}.header-actions{display:flex;gap:4px}.grid-scroll-container{max-height:145px;overflow-y:auto;padding-right:4px}.buttons-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.option-btn{display:flex;align-items:center;justify-content:center;height:38px;padding:4px 6px;background-color:#fff;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;transition:all .15s ease-in-out;outline:none;box-sizing:border-box}.option-btn:hover{border-color:#9ca3af;background-color:#f9fafb}.btn-text{font-size:12px;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center}.option-btn.selected{border-color:#2563eb;background-color:#eff6ff;box-shadow:0 0 0 1px #2563eb}.option-btn.selected .btn-text{color:#1d4ed8;font-weight:600}.grid-scroll-container::-webkit-scrollbar{width:6px}.grid-scroll-container::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb{background:#71717a;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb:hover{background:#52525b}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
3339
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartSyncfusionComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3340
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: BubbleChartSyncfusionComponent, isStandalone: true, selector: "yw-bubble-chart-syncfusion", inputs: { chartData: "chartData", config: "config", idComponent: "idComponent" }, providers: [BubbleSeriesService, CategoryService, CategoryService,
3341
+ TooltipService,
3342
+ LegendService,
3343
+ DateTimeService, DataLabelService], viewQueries: [{ propertyName: "chartInstance", first: true, predicate: ["chart"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"control-section\">\r\n <ejs-chart \r\n #chart \r\n [primaryXAxis]=\"labelData.axisX\" \r\n [primaryYAxis]=\"labelData.axisY\" \r\n [title]=\"labelData.title\" \r\n [palettes]=\"labelData.palettes\"\r\n [chartArea]=\"labelData.chartArea\"\r\n [titleStyle]=\"labelData.titleStyle\"\r\n\r\n (pointRender)=\"pointRender($event)\" \r\n height=\"100%\"\r\n width=\"100%\"\r\n [legendSettings]=\"labelData.legendSettings\"\r\n (axisLabelRender)=\"axisLabelRender($event)\"\r\n\r\n [tooltip]=\"labelData.tooltip\">\r\n <e-series-collection>\r\n @for(series of dataChartParse; track $index){\r\n <e-series \r\n [opacity]=\"series.opacity\"\r\n [dataSource]='series.dataSource' \r\n type='Bubble' \r\n xName='x' \r\n yName='y' \r\n size='size'\r\n [name]='series.name' \r\n\r\n [marker]=\"labelData.marker\"\r\n \r\n >\r\n <!-- [dataLabel]='series.dataLabel' -->\r\n </e-series>\r\n\r\n }\r\n </e-series-collection>\r\n</ejs-chart>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartModule }, { kind: "component", type: i1.ChartComponent, selector: "ejs-chart", inputs: ["accessibility", "allowExport", "allowMultiSelection", "annotations", "axes", "background", "backgroundImage", "border", "chartArea", "columns", "crosshair", "currencyCode", "dataSource", "description", "enableAnimation", "enableAutoIntervalOnBothAxis", "enableCanvas", "enableExport", "enableHtmlSanitizer", "enablePersistence", "enableRtl", "enableSideBySidePlacement", "focusBorderColor", "focusBorderMargin", "focusBorderWidth", "height", "highlightColor", "highlightMode", "highlightPattern", "indicators", "isMultiSelect", "isTransposed", "legendSettings", "locale", "margin", "palettes", "primaryXAxis", "primaryYAxis", "rangeColorSettings", "rows", "selectedDataIndexes", "selectionMode", "selectionPattern", "series", "subTitle", "subTitleStyle", "tabIndex", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width", "zoomSettings"], outputs: ["afterExport", "animationComplete", "annotationRender", "axisLabelClick", "axisLabelRender", "axisMultiLabelRender", "axisRangeCalculated", "beforeExport", "beforePrint", "beforeResize", "chartDoubleClick", "chartMouseClick", "chartMouseDown", "chartMouseLeave", "chartMouseMove", "chartMouseUp", "drag", "dragComplete", "dragEnd", "dragStart", "legendClick", "legendRender", "load", "loaded", "multiLevelLabelClick", "onZooming", "pointClick", "pointDoubleClick", "pointMove", "pointRender", "resized", "scrollChanged", "scrollEnd", "scrollStart", "selectionComplete", "seriesRender", "sharedTooltipRender", "textRender", "tooltipRender", "zoomComplete", "dataSourceChange"] }, { kind: "directive", type: i1.SeriesDirective, selector: "e-series-collection>e-series", inputs: ["accessibility", "animation", "bearFillColor", "binInterval", "border", "boxPlotMode", "bullFillColor", "cardinalSplineTension", "close", "colorName", "columnFacet", "columnSpacing", "columnWidth", "columnWidthInPixel", "connector", "cornerRadius", "dashArray", "dataSource", "dragSettings", "drawType", "emptyPointSettings", "enableComplexProperty", "enableSolidCandles", "enableTooltip", "errorBar", "fill", "groupName", "high", "intermediateSumIndexes", "isClosed", "legendImageUrl", "legendShape", "low", "marker", "maxRadius", "minRadius", "name", "negativeFillColor", "noRisers", "nonHighlightStyle", "opacity", "open", "paretoOptions", "pointColorMapping", "query", "segmentAxis", "segments", "selectionStyle", "showMean", "showNormalDistribution", "size", "splineType", "stackingGroup", "step", "sumIndexes", "summaryFillColor", "tooltipFormat", "tooltipMappingName", "trendlines", "type", "unSelectedStyle", "visible", "volume", "width", "xAxisName", "xName", "yAxisName", "yName", "zOrder"] }, { kind: "directive", type: i1.SeriesCollectionDirective, selector: "ej-chart>e-series-collection" }] }); }
3167
3344
  }
3168
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwButtonSlicerComponent, decorators: [{
3345
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartSyncfusionComponent, decorators: [{
3169
3346
  type: Component,
3170
- args: [{ selector: 'yw-button-slicer', standalone: true, imports: [CommonModule], host: {
3171
- '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3172
- }, template: "<div class=\"filter-container\">\r\n \r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\"> \r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" >\uD83E\uDDF9</button> \r\n </div>\r\n </div>\r\n\r\n\r\n <!-- Contenedor con barra de desplazamiento interna -->\r\n <div class=\"grid-scroll-container\">\r\n <div class=\"buttons-grid\">\r\n @for (item of options(); track item.id) {\r\n <button \r\n type=\"button\" \r\n class=\"option-btn\" \r\n [class.selected]=\"isSelected(item.id)\"\r\n (click)=\"toggleSelection(item.id)\"\r\n [title]=\"item.label\">\r\n <span class=\"btn-text\">{{ item.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:320px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#333}.header-actions{display:flex;gap:4px}.grid-scroll-container{max-height:145px;overflow-y:auto;padding-right:4px}.buttons-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.option-btn{display:flex;align-items:center;justify-content:center;height:38px;padding:4px 6px;background-color:#fff;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;transition:all .15s ease-in-out;outline:none;box-sizing:border-box}.option-btn:hover{border-color:#9ca3af;background-color:#f9fafb}.btn-text{font-size:12px;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center}.option-btn.selected{border-color:#2563eb;background-color:#eff6ff;box-shadow:0 0 0 1px #2563eb}.option-btn.selected .btn-text{color:#1d4ed8;font-weight:600}.grid-scroll-container::-webkit-scrollbar{width:6px}.grid-scroll-container::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb{background:#71717a;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb:hover{background:#52525b}\n"] }]
3173
- }], ctorParameters: () => [], propDecorators: { idComponent: [{
3174
- type: Input
3175
- }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], allowMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowMultiple", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], currentIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentIds", required: false }] }] } });
3347
+ args: [{ selector: 'yw-bubble-chart-syncfusion', standalone: true, imports: [CommonModule, ChartModule], providers: [BubbleSeriesService, CategoryService, CategoryService,
3348
+ TooltipService,
3349
+ LegendService,
3350
+ DateTimeService, DataLabelService], template: "<div class=\"control-section\">\r\n <ejs-chart \r\n #chart \r\n [primaryXAxis]=\"labelData.axisX\" \r\n [primaryYAxis]=\"labelData.axisY\" \r\n [title]=\"labelData.title\" \r\n [palettes]=\"labelData.palettes\"\r\n [chartArea]=\"labelData.chartArea\"\r\n [titleStyle]=\"labelData.titleStyle\"\r\n\r\n (pointRender)=\"pointRender($event)\" \r\n height=\"100%\"\r\n width=\"100%\"\r\n [legendSettings]=\"labelData.legendSettings\"\r\n (axisLabelRender)=\"axisLabelRender($event)\"\r\n\r\n [tooltip]=\"labelData.tooltip\">\r\n <e-series-collection>\r\n @for(series of dataChartParse; track $index){\r\n <e-series \r\n [opacity]=\"series.opacity\"\r\n [dataSource]='series.dataSource' \r\n type='Bubble' \r\n xName='x' \r\n yName='y' \r\n size='size'\r\n [name]='series.name' \r\n\r\n [marker]=\"labelData.marker\"\r\n \r\n >\r\n <!-- [dataLabel]='series.dataLabel' -->\r\n </e-series>\r\n\r\n }\r\n </e-series-collection>\r\n</ejs-chart>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
3351
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { chartInstance: [{
3352
+ type: ViewChild,
3353
+ args: ['chart']
3354
+ }], chartData: [{
3355
+ type: Input,
3356
+ args: ['chartData']
3357
+ }], config: [{
3358
+ type: Input,
3359
+ args: ['config']
3360
+ }], idComponent: [{
3361
+ type: Input,
3362
+ args: ['idComponent']
3363
+ }] } });
3176
3364
 
3177
- class YwListSlicerComponent {
3365
+ // import { LineChartSyncfusionComponent } from '../../../../../adapters/charts/line-chart/syncfusion/visual/yw-line-chart-syncfusion.component';
3366
+ // import { YwLineMappingConfig } from '../../../../../models/config/chart/yw-line-chart-config.interface';
3367
+ class BubbleChartComponent {
3178
3368
  constructor() {
3179
- // Entradas generales
3180
3369
  this.idComponent = '';
3181
- this.label = input('Filtro Jerárquico', ...(ngDevMode ? [{ debugName: "label" }] : []));
3182
- this.clearTitleLabel = input('Limpiar Seleccion', ...(ngDevMode ? [{ debugName: "clearTitleLabel" }] : []));
3183
- // Opción 1: Entregar datos en árbol de texto directo
3184
- this.rawData = input([], ...(ngDevMode ? [{ debugName: "rawData" }] : []));
3185
- // Opción 2: Generar desde fechas
3186
- this.dataType = input('date', ...(ngDevMode ? [{ debugName: "dataType" }] : []));
3187
- this.minDate = input('', ...(ngDevMode ? [{ debugName: "minDate" }] : [])); // Formato 'YYYY-MM-DD'
3188
- this.maxDate = input('', ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
3189
- this.dateHierarchyType = input('YEAR_TRIMESTER_MONTH', ...(ngDevMode ? [{ debugName: "dateHierarchyType" }] : []));
3190
- this.monthNames = input(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], ...(ngDevMode ? [{ debugName: "monthNames" }] : []));
3191
- // Evento de salida con el ID/Nodo seleccionado
3192
- this.selectionChange = output();
3193
- this.filterValueChange = output(); // Output 2 (Rangos o Hojas)
3194
- // ID del elemento seleccionado (Selección Simple)
3195
- this.selectedId = signal(null, ...(ngDevMode ? [{ debugName: "selectedId" }] : []));
3196
- this.currentSelectedId = input(null, ...(ngDevMode ? [{ debugName: "currentSelectedId" }] : []));
3197
- // Arbol de nodos computado
3198
- this.treeNodes = computed(() => {
3199
- if (this.dataType() == 'date' && this.minDate() && this.maxDate()) {
3200
- return this.generateDateHierarchy(this.minDate(), this.maxDate(), this.dateHierarchyType());
3201
- }
3202
- return this.rawData();
3203
- }, ...(ngDevMode ? [{ debugName: "treeNodes" }] : []));
3204
- effect(() => {
3205
- const currentId = this.selectedId();
3206
- if (!currentId) {
3207
- this.selectionChange.emit(null);
3208
- this.filterValueChange.emit(null);
3209
- return;
3210
- }
3211
- const node = this.findNodeById(this.treeNodes(), currentId);
3212
- this.selectionChange.emit(node);
3213
- if (node) {
3214
- // Emitir el segundo Output según el modo
3215
- const calculatedValue = this.processFilterOutput(node);
3216
- this.filterValueChange.emit(calculatedValue);
3217
- }
3218
- });
3219
3370
  }
3220
3371
  ngOnInit() {
3221
- if (this.currentSelectedId()) {
3222
- this.selectedId.set(this.currentSelectedId());
3223
- }
3224
- }
3225
- // Métodos de Selección y Expansión
3226
- toggleExpand(node, event) {
3227
- event.stopPropagation();
3228
- node.expanded = !node.expanded;
3229
- }
3230
- selectNode(node, event) {
3231
- event.stopPropagation();
3232
- this.selectedId.set(this.selectedId() === node.id ? null : node.id);
3372
+ console.log("EL ID DEL COMPONENTE:", this.idComponent);
3233
3373
  }
3234
- clearSelection() {
3235
- this.selectedId.set(null);
3236
- this.filterValueChange.emit(null);
3374
+ ngOnChanges(changes) {
3375
+ if (changes['chartData']) {
3376
+ console.log('change chartDataBubble:', changes['chartData'].currentValue);
3377
+ }
3378
+ if (changes['config']) {
3379
+ console.log('change configBubble:', changes['config'].currentValue);
3380
+ }
3237
3381
  }
3238
- // --- LÓGICA DE PROCESAMIENTO DE LAS HOJAS / RANGOS ---
3239
- processFilterOutput(selectedNode) {
3240
- // 1. Obtener todas las hojas (nodos del nivel más bajo) pertenecientes al bloque seleccionado
3241
- const leaves = this.getLeafNodes(selectedNode);
3242
- if (this.dataType() == 'date') {
3243
- // Extraer todas las fechas de las hojas del bloque
3244
- const dates = leaves
3245
- .map(leaf => leaf.value)
3246
- .filter(d => d instanceof Date)
3247
- .sort((a, b) => a.getTime() - b.getTime());
3248
- if (dates.length > 0) {
3249
- const minD = dates[0];
3250
- // Para la fecha máxima, tomamos el último día del mes correspondiente
3251
- const maxD = new Date(dates[dates.length - 1].getFullYear(), dates[dates.length - 1].getMonth() + 1, 0);
3252
- return {
3253
- type: 'DATE_RANGE',
3254
- dateRange: {
3255
- minDate: minD.toISOString().split('T')[0],
3256
- maxDate: maxD.toISOString().split('T')[0]
3257
- },
3258
- currentNodeId: selectedNode.id
3259
- };
3260
- }
3261
- }
3262
- // Modo Texto: Entregar arreglo con las hojas
3263
- return {
3264
- type: 'TEXT_LEAVES',
3265
- leafNodes: leaves,
3266
- leafIds: leaves.map(l => l.id),
3267
- currentNodeId: selectedNode.id
3268
- };
3269
- }
3270
- // Recorrido recursivo para obtener solo los nodos terminales (sin hijos)
3271
- getLeafNodes(node) {
3272
- if (!node.children || node.children.length === 0) {
3273
- return [node];
3274
- }
3275
- let leaves = [];
3276
- for (const child of node.children) {
3277
- leaves = leaves.concat(this.getLeafNodes(child));
3278
- }
3279
- return leaves;
3280
- }
3281
- findNodeById(nodes, id) {
3282
- for (const node of nodes) {
3283
- if (node.id === id)
3284
- return node;
3285
- if (node.children) {
3286
- const found = this.findNodeById(node.children, id);
3287
- if (found)
3288
- return found;
3289
- }
3290
- }
3291
- return null;
3292
- }
3293
- // Generador de Jerarquía por Fechas
3294
- generateDateHierarchy(startStr, endStr, type) {
3295
- const start = new Date(startStr);
3296
- const end = new Date(endStr);
3297
- const tree = [];
3298
- const monthNames = this.monthNames();
3299
- if (type === 'YEAR_TRIMESTER_MONTH') {
3300
- let current = new Date(start);
3301
- while (current <= end) {
3302
- const year = current.getFullYear();
3303
- let yearNode = tree.find(n => n.id === `Y_${year}`);
3304
- if (!yearNode) {
3305
- yearNode = { id: `Y_${year}`, label: `${year}`, children: [], expanded: false };
3306
- tree.push(yearNode);
3307
- }
3308
- const month = current.getMonth();
3309
- const qtrNum = Math.floor(month / 3) + 1;
3310
- let qtrNode = yearNode.children.find(n => n.id === `Q_${year}_${qtrNum}`);
3311
- if (!qtrNode) {
3312
- qtrNode = { id: `Q_${year}_${qtrNum}`, label: `Qtr ${qtrNum}`, children: [], expanded: false };
3313
- yearNode.children.push(qtrNode);
3314
- }
3315
- const monthId = `M_${year}_${month + 1}`;
3316
- if (!qtrNode.children.some(n => n.id === monthId)) {
3317
- qtrNode.children.push({
3318
- id: monthId,
3319
- label: monthNames[month],
3320
- value: new Date(year, month, 1) // Fecha inicial del mes
3321
- });
3322
- }
3323
- current.setMonth(current.getMonth() + 1);
3324
- }
3325
- }
3326
- if (type === 'YEAR_SEMESTER_MONTH') {
3327
- let current = new Date(start);
3328
- while (current <= end) {
3329
- const year = current.getFullYear();
3330
- let yearNode = tree.find(n => n.id === `Y_${year}`);
3331
- if (!yearNode) {
3332
- yearNode = { id: `Y_${year}`, label: `${year}`, children: [], expanded: false };
3333
- tree.push(yearNode);
3334
- }
3335
- const month = current.getMonth();
3336
- const qtrNum = Math.floor(month / 6) + 1;
3337
- let qtrNode = yearNode.children.find(n => n.id === `S_${year}_${qtrNum}`);
3338
- if (!qtrNode) {
3339
- qtrNode = { id: `S_${year}_${qtrNum}`, label: `S ${qtrNum}`, children: [], expanded: false };
3340
- yearNode.children.push(qtrNode);
3341
- }
3342
- const monthId = `M_${year}_${month + 1}`;
3343
- if (!qtrNode.children.some(n => n.id === monthId)) {
3344
- qtrNode.children.push({
3345
- id: monthId,
3346
- label: monthNames[month],
3347
- value: new Date(year, month, 1) // Fecha inicial del mes
3348
- });
3349
- }
3350
- current.setMonth(current.getMonth() + 1);
3351
- }
3352
- }
3353
- return tree;
3354
- }
3355
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwListSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3356
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwListSlicerComponent, isStandalone: true, selector: "yw-list-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, clearTitleLabel: { classPropertyName: "clearTitleLabel", publicName: "clearTitleLabel", isSignal: true, isRequired: false, transformFunction: null }, rawData: { classPropertyName: "rawData", publicName: "rawData", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateHierarchyType: { classPropertyName: "dateHierarchyType", publicName: "dateHierarchyType", isSignal: true, isRequired: false, transformFunction: null }, monthNames: { classPropertyName: "monthNames", publicName: "monthNames", isSignal: true, isRequired: false, transformFunction: null }, currentSelectedId: { classPropertyName: "currentSelectedId", publicName: "currentSelectedId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", filterValueChange: "filterValueChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n <!-- Cabecera con t\u00EDtulo y acciones de filtro -->\r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\">\r\n <!--<button class=\"icon-btn\" title=\"Filtro\">\uD83D\uDD3B</button>-->\r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" title=\"{{clearTitleLabel}}\">\uD83E\uDDF9</button>\r\n <!--<button class=\"icon-btn\" title=\"M\u00E1s opciones\">\u22EE</button>-->\r\n </div>\r\n </div>\r\n\r\n <!-- Vista en \u00C1rbol con scroll interno -->\r\n <div class=\"tree-scroll-container\">\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: treeNodes(), level: 0 }\"></ng-container>\r\n </div>\r\n</div>\r\n\r\n<!-- Plantilla Recursiva para renderizar niveles del \u00C1rbol -->\r\n<ng-template #nodeListTemplate let-nodes let-level=\"level\">\r\n <div class=\"tree-level\">\r\n @for (node of nodes; track node.id) {\r\n <div class=\"tree-node-wrapper\">\r\n <!-- Fila de opci\u00F3n -->\r\n <div \r\n class=\"tree-node-row\" \r\n [class.selected]=\"selectedId() === node.id\"\r\n [style.padding-left.px]=\"level * 18 + 6\"\r\n (click)=\"selectNode(node, $event)\">\r\n \r\n <!-- Flecha expandir/colapsar (si tiene hijos) -->\r\n @if (node.children && node.children.length > 0) {\r\n <span class=\"chevron-icon\" (click)=\"toggleExpand(node, $event)\">\r\n {{ node.expanded ? '\uD83D\uDF83' : '\uD83D\uDF82' }}\r\n </span>\r\n } @else {\r\n <span class=\"chevron-spacer\"></span>\r\n }\r\n\r\n <!-- Radio Button (Selecci\u00F3n simple) -->\r\n <div class=\"radio-circle\" [class.checked]=\"selectedId() === node.id\">\r\n <div class=\"radio-inner\"></div>\r\n </div>\r\n\r\n <!-- Etiqueta del Nodo -->\r\n <span class=\"node-label\">{{ node.label }}</span>\r\n </div>\r\n\r\n <!-- Renderizado de hijos si est\u00E1 expandido -->\r\n @if (node.children && node.children.length > 0 && node.expanded) {\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: node.children, level: level + 1 }\"></ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;width:100%;max-width:320px;background:#fff}.filter-container{display:flex;flex-direction:column;border:1px solid #e0e0e0;border-radius:4px;background-color:#fff}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#252423}.header-actions{display:flex;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;font-size:12px;color:#605e5c;padding:2px 4px;border-radius:2px}.icon-btn:hover{background-color:#f3f2f1}.tree-scroll-container{max-height:220px;overflow-y:auto;padding:4px 0}.tree-level{display:flex;flex-direction:column}.tree-node-row{display:flex;align-items:center;height:28px;cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px;transition:background-color .1s ease}.tree-node-row:hover{background-color:#f3f2f1}.tree-node-row.selected{background-color:#e1dfdd}.chevron-icon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:10px;color:#323130}.chevron-spacer{width:16px}.radio-circle{width:14px;height:14px;border-radius:50%;border:1.5px solid #605e5c;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:#fff}.radio-circle.checked{border-color:#0078d4}.radio-circle.checked .radio-inner{width:6px;height:6px;border-radius:50%;background-color:#0078d4}.node-label{font-size:13px;color:#323130;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-scroll-container::-webkit-scrollbar{width:6px}.tree-scroll-container::-webkit-scrollbar-track{background:#f1f1f1}.tree-scroll-container::-webkit-scrollbar-thumb{background:#8a8886;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
3382
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3383
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: BubbleChartComponent, isStandalone: true, selector: "yw-bubble-chart", inputs: { chartData: "chartData", config: "config", idComponent: "idComponent" }, usesOnChanges: true, ngImport: i0, template: "@if(config.provider==='syncfusion'){\r\n <div style=\"position: relative; width: 100%; height: 100%;\">\r\n <yw-bubble-chart-syncfusion \r\n [idComponent]=\"idComponent\"\r\n [chartData]=\"chartData\" \r\n [config]=\"config\" style=\"display: block; width: 100%; height: 100%;\">\r\n </yw-bubble-chart-syncfusion>\r\n</div>\r\n}", styles: [":host{display:block;width:100%;height:100%;min-height:260px;min-width:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BubbleChartSyncfusionComponent, selector: "yw-bubble-chart-syncfusion", inputs: ["chartData", "config", "idComponent"] }] }); }
3357
3384
  }
3358
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwListSlicerComponent, decorators: [{
3385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartComponent, decorators: [{
3359
3386
  type: Component,
3360
- args: [{ selector: 'yw-list-slicer', standalone: true, imports: [CommonModule], host: {
3361
- '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3362
- }, template: "<div class=\"filter-container\">\r\n <!-- Cabecera con t\u00EDtulo y acciones de filtro -->\r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\">\r\n <!--<button class=\"icon-btn\" title=\"Filtro\">\uD83D\uDD3B</button>-->\r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" title=\"{{clearTitleLabel}}\">\uD83E\uDDF9</button>\r\n <!--<button class=\"icon-btn\" title=\"M\u00E1s opciones\">\u22EE</button>-->\r\n </div>\r\n </div>\r\n\r\n <!-- Vista en \u00C1rbol con scroll interno -->\r\n <div class=\"tree-scroll-container\">\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: treeNodes(), level: 0 }\"></ng-container>\r\n </div>\r\n</div>\r\n\r\n<!-- Plantilla Recursiva para renderizar niveles del \u00C1rbol -->\r\n<ng-template #nodeListTemplate let-nodes let-level=\"level\">\r\n <div class=\"tree-level\">\r\n @for (node of nodes; track node.id) {\r\n <div class=\"tree-node-wrapper\">\r\n <!-- Fila de opci\u00F3n -->\r\n <div \r\n class=\"tree-node-row\" \r\n [class.selected]=\"selectedId() === node.id\"\r\n [style.padding-left.px]=\"level * 18 + 6\"\r\n (click)=\"selectNode(node, $event)\">\r\n \r\n <!-- Flecha expandir/colapsar (si tiene hijos) -->\r\n @if (node.children && node.children.length > 0) {\r\n <span class=\"chevron-icon\" (click)=\"toggleExpand(node, $event)\">\r\n {{ node.expanded ? '\uD83D\uDF83' : '\uD83D\uDF82' }}\r\n </span>\r\n } @else {\r\n <span class=\"chevron-spacer\"></span>\r\n }\r\n\r\n <!-- Radio Button (Selecci\u00F3n simple) -->\r\n <div class=\"radio-circle\" [class.checked]=\"selectedId() === node.id\">\r\n <div class=\"radio-inner\"></div>\r\n </div>\r\n\r\n <!-- Etiqueta del Nodo -->\r\n <span class=\"node-label\">{{ node.label }}</span>\r\n </div>\r\n\r\n <!-- Renderizado de hijos si est\u00E1 expandido -->\r\n @if (node.children && node.children.length > 0 && node.expanded) {\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: node.children, level: level + 1 }\"></ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;width:100%;max-width:320px;background:#fff}.filter-container{display:flex;flex-direction:column;border:1px solid #e0e0e0;border-radius:4px;background-color:#fff}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#252423}.header-actions{display:flex;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;font-size:12px;color:#605e5c;padding:2px 4px;border-radius:2px}.icon-btn:hover{background-color:#f3f2f1}.tree-scroll-container{max-height:220px;overflow-y:auto;padding:4px 0}.tree-level{display:flex;flex-direction:column}.tree-node-row{display:flex;align-items:center;height:28px;cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px;transition:background-color .1s ease}.tree-node-row:hover{background-color:#f3f2f1}.tree-node-row.selected{background-color:#e1dfdd}.chevron-icon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:10px;color:#323130}.chevron-spacer{width:16px}.radio-circle{width:14px;height:14px;border-radius:50%;border:1.5px solid #605e5c;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:#fff}.radio-circle.checked{border-color:#0078d4}.radio-circle.checked .radio-inner{width:6px;height:6px;border-radius:50%;background-color:#0078d4}.node-label{font-size:13px;color:#323130;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-scroll-container::-webkit-scrollbar{width:6px}.tree-scroll-container::-webkit-scrollbar-track{background:#f1f1f1}.tree-scroll-container::-webkit-scrollbar-thumb{background:#8a8886;border-radius:3px}\n"] }]
3363
- }], ctorParameters: () => [], propDecorators: { idComponent: [{
3364
- type: Input
3365
- }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], clearTitleLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearTitleLabel", required: false }] }], rawData: [{ type: i0.Input, args: [{ isSignal: true, alias: "rawData", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateHierarchyType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateHierarchyType", required: false }] }], monthNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthNames", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filterValueChange: [{ type: i0.Output, args: ["filterValueChange"] }], currentSelectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentSelectedId", required: false }] }] } });
3387
+ args: [{ selector: 'yw-bubble-chart', standalone: true, imports: [CommonModule, BubbleChartSyncfusionComponent], template: "@if(config.provider==='syncfusion'){\r\n <div style=\"position: relative; width: 100%; height: 100%;\">\r\n <yw-bubble-chart-syncfusion \r\n [idComponent]=\"idComponent\"\r\n [chartData]=\"chartData\" \r\n [config]=\"config\" style=\"display: block; width: 100%; height: 100%;\">\r\n </yw-bubble-chart-syncfusion>\r\n</div>\r\n}", styles: [":host{display:block;width:100%;height:100%;min-height:260px;min-width:0;overflow:hidden}\n"] }]
3388
+ }], propDecorators: { chartData: [{
3389
+ type: Input,
3390
+ args: ['chartData']
3391
+ }], config: [{
3392
+ type: Input,
3393
+ args: ['config']
3394
+ }], idComponent: [{
3395
+ type: Input,
3396
+ args: ['idComponent']
3397
+ }] } });
3366
3398
 
3367
- class YwDashboardFilterWidgetComponent {
3399
+ /*BEGIN - COMPONENTES VALIDOS PARA WIDGET*/
3400
+ class YwDashboardWidgetComponent {
3368
3401
  constructor() {
3369
3402
  this.componentWidgetId = '';
3370
3403
  this.typeComponent = '';
3371
3404
  this.varShowConfigButtons = false;
3405
+ this.styleWidget = input(DEFAULT_WIDGET_STYLE, ...(ngDevMode ? [{ debugName: "styleWidget" }] : []));
3406
+ // Signal calculada que combina los valores recibidos con los valores por defecto
3407
+ this.instanceStyleWidget = computed(() => ({
3408
+ widgetBackgroundColor: this.styleWidget()?.widgetBackgroundColor ?? DEFAULT_WIDGET_STYLE.widgetBackgroundColor,
3409
+ widgetBorderColor: this.styleWidget()?.widgetBorderColor ?? DEFAULT_WIDGET_STYLE.widgetBorderColor
3410
+ }), ...(ngDevMode ? [{ debugName: "instanceStyleWidget" }] : []));
3372
3411
  this.configWidget = {
3373
3412
  widgetId: '',
3374
- label: '',
3413
+ title: '',
3375
3414
  typeComponent: '',
3376
- controlTable: '',
3415
+ showConfigButtons: false,
3416
+ widgetStyle: DEFAULT_WIDGET_STYLE,
3417
+ layoutConfig: { id: '', w: 0, h: 0, content: undefined },
3418
+ controlConfig: {},
3419
+ controlTables: []
3377
3420
  };
3378
- this.controlConfig = input(...(ngDevMode ? [undefined, { debugName: "controlConfig" }] : []));
3379
- this.controlData = [];
3421
+ this.controlConfig = {};
3422
+ this.controlData = {};
3423
+ this.isLocked = model(false, ...(ngDevMode ? [{ debugName: "isLocked" }] : []));
3424
+ this.isMaximized = signal(false, ...(ngDevMode ? [{ debugName: "isMaximized" }] : []));
3380
3425
  this.eventDeleteRequest = output();
3426
+ this.eventLockToggleRequest = output();
3427
+ this.eventMaximizeToggleRequest = output();
3381
3428
  this.eventShowConfigFormRequest = output();
3382
- this.selectionChange = output();
3383
- effect(() => {
3384
- const value_controlConfig = this.fnFormatConfigData();
3385
- this.dataWidget = { idComponent: this.componentWidgetId,
3386
- configWidget: this.configWidget,
3387
- controlConfig: value_controlConfig
3388
- };
3389
- });
3390
3429
  }
3391
3430
  ngOnInit() {
3392
- this.componentWidgetId = this.configWidget.widgetId;
3431
+ this.componentWidgetId = 'cmpnt-' + this.configWidget.widgetId;
3393
3432
  this.typeComponent = this.configWidget.typeComponent;
3394
- const value_controlConfig = this.fnFormatConfigData();
3395
3433
  this.dataWidget = { idComponent: this.componentWidgetId,
3396
- configWidget: this.configWidget,
3397
- controlConfig: value_controlConfig
3434
+ controlConfig: this.controlConfig,
3435
+ controlData: this.controlData
3398
3436
  };
3437
+ // console.log('DATOS INICIALES INIT:');
3438
+ // console.log(this.dataWidget);
3399
3439
  }
3400
- /*
3401
- Los Datos a este componente llegan a un nivel basico, sin embargo es necesario configurar la informacion, para que se adapte a los componentes.
3402
- */
3403
- fnFormatConfigData() {
3404
- let responsedata = {};
3405
- const typeComponent = this.configWidget.typeComponent;
3406
- if (typeComponent == 'filter-slicer') {
3407
- const dataType = this.controlConfig().dataType;
3408
- const currentData = this.controlConfig();
3409
- if (dataType == 'date') {
3410
- const fieldName = this.controlConfig().filterField;
3411
- const fechas = this.controlData.map((d) => new Date(d[fieldName]).getTime());
3412
- const val_min = new Date(Math.min(...fechas)).toISOString().split("T")[0];
3413
- const val_max = new Date(Math.max(...fechas)).toISOString().split("T")[0];
3414
- responsedata = { dataType: dataType, min: val_min, max: val_max, minCurrent: currentData?.minCurrent, maxCurrent: currentData?.maxCurrent };
3415
- }
3416
- if (dataType == 'number') {
3417
- const fieldName = this.controlConfig().filterField;
3418
- const valStep = this.controlConfig().step;
3419
- const DataNumbers = this.controlData.map((d) => d[fieldName]);
3420
- const val_min = Math.min(...DataNumbers);
3421
- const val_max = Math.max(...DataNumbers);
3422
- responsedata = { dataType: dataType, min: val_min, max: val_max, step: valStep, minCurrent: currentData?.minCurrent, maxCurrent: currentData?.maxCurrent };
3423
- }
3440
+ ngAfterViewInit() {
3441
+ this.varShowConfigButtons = this.configWidget.showConfigButtons || false;
3442
+ }
3443
+ ngOnChanges(changes) {
3444
+ if (changes['configWidget']) {
3445
+ //console.log('Nuevo valor configWidget:', this.configWidget);
3446
+ this.varShowConfigButtons = this.configWidget.showConfigButtons || false;
3447
+ ;
3424
3448
  }
3425
- if (typeComponent == 'filter-button') {
3426
- const fieldName = this.controlConfig().filterField;
3427
- const fieldNameId = this.controlConfig().filterFieldId;
3428
- const val_allowMultiple = this.controlConfig().allowMultiple;
3429
- const currentData = this.controlConfig();
3430
- let listoptions = this.controlData.map((item) => { return { 'id': item[fieldNameId], 'label': item[fieldName] }; });
3431
- const listoptionsunicos = [...new Map(listoptions.map(d => [d.id, d])).values()];
3432
- responsedata = { options: listoptionsunicos, allowMultiple: val_allowMultiple, currentIds: currentData?.currentIds };
3449
+ if (changes['controlConfig']) {
3450
+ // console.log('Nuevo valor controlConfig:', this.controlConfig);
3433
3451
  }
3434
- if (typeComponent == 'filter-list') {
3435
- const dataType = this.controlConfig().dataType;
3436
- const currentData = this.controlConfig();
3437
- if (dataType == 'date') {
3438
- const fieldName = this.controlConfig().filterField;
3439
- const val_dateHierarchyType = this.controlConfig().dateHierarchyType;
3440
- const fechas = this.controlData.map((d) => new Date(d[fieldName]).getTime());
3441
- const val_min = new Date(Math.min(...fechas)).toISOString().split("T")[0];
3442
- const val_max = new Date(Math.max(...fechas)).toISOString().split("T")[0];
3443
- responsedata = { dataType: dataType, minDate: val_min, maxDate: val_max, dateHierarchyType: val_dateHierarchyType, currentSelectedId: currentData.currentSelectedId };
3444
- }
3445
- if (dataType == 'text') {
3446
- const fieldName1 = this.controlConfig().filterField;
3447
- const fieldName2 = this.controlConfig().filterFieldN2;
3448
- const fieldName3 = this.controlConfig().filterFieldN3;
3449
- let foundN2 = false;
3450
- let foundN3 = false;
3451
- let lsizeN2 = 0;
3452
- let lsizeN3 = 0;
3453
- let nodebase = { id: '', label: '' };
3454
- let nodeN2 = { id: '', label: '' };
3455
- let nodeN3 = { id: '', label: '' };
3456
- let listNivel1 = [];
3457
- let listNivel2 = [];
3458
- let listNivel3 = [];
3459
- this.controlData.forEach((item) => {
3460
- nodebase = { id: item[fieldName1], label: item[fieldName1] };
3461
- //NIVEL 2
3462
- if (fieldName2) {
3463
- foundN2 = false;
3464
- lsizeN2 = Object.keys(listNivel2).length;
3465
- for (let i = 0; i < lsizeN2; i++) {
3466
- const itemN2 = listNivel2[i];
3467
- if (itemN2.id == item[fieldName2]) {
3468
- itemN2.children.push(nodebase);
3469
- nodeN2 = itemN2;
3470
- foundN2 = true;
3471
- break;
3472
- }
3473
- }
3474
- ;
3475
- if (foundN2 == false) {
3476
- nodeN2 = { id: item[fieldName2], label: item[fieldName2], expanded: true, children: [nodebase] };
3477
- if (fieldName3) {
3478
- nodeN2.parentId = item[fieldName3];
3479
- }
3480
- listNivel2.push(nodeN2);
3481
- }
3482
- }
3483
- else {
3484
- listNivel1.push(nodebase);
3485
- }
3486
- });
3487
- //NIVEL 1 - Basico no hay Jerarquia
3488
- responsedata = { dataType: dataType, rawData: listNivel1, currentSelectedId: currentData.currentSelectedId };
3489
- //NIVEL 2 - Jerarquia de 2 niveles
3490
- if (fieldName2) {
3491
- responsedata = { dataType: dataType, rawData: listNivel2, currentSelectedId: currentData.currentSelectedId };
3492
- }
3493
- //NIVEL 3 - Jerarquia de 3 niveles
3494
- if (fieldName3) {
3495
- listNivel2.forEach((item) => {
3496
- nodebase = item;
3497
- foundN3 = false;
3498
- lsizeN3 = Object.keys(listNivel3).length;
3499
- for (let i = 0; i < lsizeN3; i++) {
3500
- const itemN3 = listNivel3[i];
3501
- if (itemN3.id == item.parentId) {
3502
- itemN3.children.push(nodebase);
3503
- foundN3 = true;
3504
- break;
3505
- }
3506
- }
3507
- ;
3508
- if (foundN3 == false) {
3509
- nodeN3 = { id: item[fieldName3], label: item[fieldName3], expanded: true, children: [nodebase] };
3510
- listNivel3.push(nodeN3);
3511
- }
3512
- });
3513
- responsedata = { dataType: dataType, rawData: listNivel3, currentSelectedId: currentData.currentSelectedId };
3514
- }
3515
- }
3452
+ if (changes['controlData']) {
3453
+ //console.log('Nuevo valor controlData:', this.controlData);
3454
+ this.dataWidget = { idComponent: this.componentWidgetId,
3455
+ controlConfig: this.controlConfig,
3456
+ controlData: this.controlData
3457
+ };
3516
3458
  }
3517
- return responsedata;
3518
- }
3519
- //Filtro Slicer
3520
- onFilterSlicerChange(range) {
3521
- console.log('Filtro actualizado:', range);
3522
- this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: range });
3523
- }
3524
- //Filtro Button
3525
- onFilterButtonChange(selectedIds) {
3526
- console.log('Tipos seleccionados:', selectedIds);
3527
- this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: selectedIds });
3528
3459
  }
3529
- //Filtro Lista - 1
3530
- onFilterListChange(selectedData) {
3531
- console.log('Tipos seleccionados:', selectedData);
3532
- // Filtrar la información del dashboard
3533
- //this.selectionChange.emit({widgetId:this.componentWidgetId,eventResponse:selectedData});
3460
+ /* BEGIN - BARRA DE EVENTOS DEL WIDGET */
3461
+ //Eliminacion de Widget - Conectado al Dashboard
3462
+ onDeleteClick() {
3463
+ this.eventDeleteRequest.emit(this.configWidget.widgetId);
3534
3464
  }
3535
- //Filtro Lista - 2
3536
- onFilterListCalculated(data) {
3537
- if (!data) {
3538
- this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: null });
3539
- return;
3540
- }
3541
- if (data.type === 'DATE_RANGE') {
3542
- //console.log('Rango a filtrar en SQL/Dashboard:', data.dateRange?.minDate, 'hasta', data.dateRange?.maxDate);
3543
- this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: { minDate: data.dateRange?.minDate, maxDate: data.dateRange?.maxDate, currentNodeId: data.currentNodeId } });
3544
- }
3545
- else if (data.type === 'TEXT_LEAVES') {
3546
- //console.log('Lista de elementos terminales seleccionados:', data.leafIds);
3547
- this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: { ids: data.leafIds, currentNodeId: data.currentNodeId } });
3548
- }
3465
+ onLockClick() {
3466
+ // Invertimos el estado local y notificamos al padre
3467
+ const nextState = !this.isLocked();
3468
+ this.isLocked.set(nextState);
3469
+ this.eventLockToggleRequest.emit({ widgetId: this.configWidget.widgetId, shouldLock: nextState });
3549
3470
  }
3550
- onDeleteClick() {
3471
+ onMaximizeClick() {
3472
+ const nextState = !this.isMaximized();
3473
+ this.isMaximized.set(nextState);
3474
+ this.eventMaximizeToggleRequest.emit({ widgetId: this.configWidget.widgetId, shouldMaximize: nextState });
3551
3475
  }
3552
3476
  onShowFormConfigClick() {
3477
+ this.eventShowConfigFormRequest.emit(this.configWidget.widgetId);
3553
3478
  }
3554
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3555
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardFilterWidgetComponent, isStandalone: true, selector: "yw-dashboard-filter-widget", inputs: { configWidget: { classPropertyName: "configWidget", publicName: "configWidget", isSignal: false, isRequired: false, transformFunction: null }, controlConfig: { classPropertyName: "controlConfig", publicName: "controlConfig", isSignal: true, isRequired: false, transformFunction: null }, controlData: { classPropertyName: "controlData", publicName: "controlData", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { eventDeleteRequest: "eventDeleteRequest", eventShowConfigFormRequest: "eventShowConfigFormRequest", selectionChange: "selectionChange" }, ngImport: i0, template: "\r\n<div class=\"widget-filter-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div>\r\n<div class=\"widget-filter-content-wrapper\">\r\n @if ( typeComponent == 'empty-visual' ) { \r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-slicer' ) { \r\n <ng-container *ngTemplateOutlet=\"filterslicer; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-button' ) { \r\n <ng-container *ngTemplateOutlet=\"filterbutton; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-list' ) { \r\n <ng-container *ngTemplateOutlet=\"filterlist; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER SLICER TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterslicer let-control = \"control\"> \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\" \r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\" \r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n @if (control.controlConfig.dataType === 'number') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\"\r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\"\r\n [step]=\"control.controlConfig.step\"\r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER BUTTON TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterbutton let-control = \"control\"> \r\n <yw-button-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [options]=\"control.controlConfig.options\"\r\n [allowMultiple]=\"control.controlConfig.allowMultiple\"\r\n [currentIds]=\"control.controlConfig.currentIds\"\r\n (selectionChange)=\"onFilterButtonChange($event)\" \r\n >\r\n </yw-button-slicer>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER LIST TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterlist let-control = \"control\"> \r\n \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [minDate]=\"control.controlConfig.minDate\"\r\n [maxDate]=\"control.controlConfig.maxDate\"\r\n [dateHierarchyType]=\"control.controlConfig.dateHierarchyType\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n @if (control.controlConfig.dataType === 'text') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\"\r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [rawData]=\"control.controlConfig.rawData\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #040404);border-radius:var(--widget-border-radius, 8px);padding:var(--widget-padding, 10px)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-filter-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-filter-content-wrapper{width:100%;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: YwEmptyVisualComponent, selector: "yw-empty-visual", inputs: ["idComponent", "config"] }, { kind: "component", type: YwBaseSlicerComponent, selector: "yw-base-slicer", inputs: ["idComponent", "dataType", "label", "min", "max", "step", "minCurrent", "maxCurrent"], outputs: ["rangeChange"] }, { kind: "component", type: YwButtonSlicerComponent, selector: "yw-button-slicer", inputs: ["idComponent", "label", "options", "allowMultiple", "currentIds"], outputs: ["selectionChange"] }, { kind: "component", type: YwListSlicerComponent, selector: "yw-list-slicer", inputs: ["idComponent", "label", "clearTitleLabel", "rawData", "dataType", "minDate", "maxDate", "dateHierarchyType", "monthNames", "currentSelectedId"], outputs: ["selectionChange", "filterValueChange"] }] }); }
3479
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3480
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardWidgetComponent, isStandalone: true, selector: "yw-dashboard-widget", inputs: { styleWidget: { classPropertyName: "styleWidget", publicName: "styleWidget", isSignal: true, isRequired: false, transformFunction: null }, configWidget: { classPropertyName: "configWidget", publicName: "configWidget", isSignal: false, isRequired: false, transformFunction: null }, controlConfig: { classPropertyName: "controlConfig", publicName: "controlConfig", isSignal: false, isRequired: false, transformFunction: null }, controlData: { classPropertyName: "controlData", publicName: "controlData", isSignal: false, isRequired: false, transformFunction: null }, isLocked: { classPropertyName: "isLocked", publicName: "isLocked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isLocked: "isLockedChange", eventDeleteRequest: "eventDeleteRequest", eventLockToggleRequest: "eventLockToggleRequest", eventMaximizeToggleRequest: "eventMaximizeToggleRequest", eventShowConfigFormRequest: "eventShowConfigFormRequest" }, host: { properties: { "style.--widget-bg": "instanceStyleWidget().widgetBackgroundColor", "style.--widget-border-color": "instanceStyleWidget().widgetBorderColor" } }, usesOnChanges: true, ngImport: i0, template: "\r\n<!-- <div class=\"widget-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\">{{ isLocked() ? '\uD83D\uDD12' : '\uD83D\uDD13' }}</button>\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\">{{ isMaximized() ? '\uD83D\uDDD7' : '\uD83D\uDDD6' }}</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div> -->\r\n\r\n<div class=\"widget-action-bar\">\r\n <!-- Bot\u00F3n Bloquear / Desbloquear -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\" [title]=\"isLocked() ? 'Desbloquear' : 'Bloquear'\">\r\n <svg *ngIf=\"isLocked(); else unlockedIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path></svg>\r\n <ng-template #unlockedIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Maximizar / Restaurar -->\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\" [title]=\"isMaximized() ? 'Restaurar' : 'Maximizar'\">\r\n <svg *ngIf=\"isMaximized(); else maxIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\"></path></svg>\r\n <ng-template #maxIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Eliminar -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\" title=\"Eliminar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path></svg>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Configuraci\u00F3n -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\" title=\"Configurar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"></circle><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"></path></svg>\r\n </button>\r\n</div>\r\n\r\n<div class=\"widget-content-wrapper\">\r\n @if ( typeComponent == 'dummy' ) {\r\n <ng-container *ngTemplateOutlet=\"testdummy; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'empty-visual' ) {\r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-bar' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbar; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-line' ) {\r\n <ng-container *ngTemplateOutlet=\"chartline; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-doughnut' ) {\r\n <ng-container *ngTemplateOutlet=\"chartdoughnut; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-gauge' ) {\r\n <ng-container *ngTemplateOutlet=\"chartgauge; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-bubble' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbubble; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- DUMMY TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #testdummy let-control = \"control\">\r\n <lib-test-chart [idComponent]=\"control.idComponent\" > \r\n </lib-test-chart>\r\n</ng-template>\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BAR CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbar let-control = \"control\">\r\n <yw-bar-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-bar-chart>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartline let-control = \"control\">\r\n <yw-line-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-line-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE DOUGHNUT TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartdoughnut let-control = \"control\">\r\n <yw-pie-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-pie-chart>\r\n</ng-template>\r\n\r\n \r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE GAUGE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartgauge let-control = \"control\">\r\n <yw-gauge-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-gauge-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BUBBLE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbubble let-control=\"control\">\r\n \r\n <yw-bubble-chart \r\n [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\">\r\n </yw-bubble-chart>\r\n</ng-template> ", styles: ["@charset \"UTF-8\";:host{display:block;flex-direction:column;width:100%;overflow:hidden;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #007bff)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-content-wrapper{width:100%;flex-grow:1;height:0;display:flex;flex-direction:column}.widget-action-bar button,.widget-action-bar .btn-action,.widget-action-bar .btn-delete,.widget-action-bar .btn-config{background:transparent;border:1px solid #d9d9d9;border-radius:4px;cursor:pointer;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:#595959;padding:0;transition:all .2s ease}.widget-action-bar button svg,.widget-action-bar .btn-action svg,.widget-action-bar .btn-delete svg,.widget-action-bar .btn-config svg{width:14px;height:14px}.widget-action-bar button:hover,.widget-action-bar .btn-action:hover,.widget-action-bar .btn-delete:hover,.widget-action-bar .btn-config:hover{background-color:#f5f5f5;color:#262626;border-color:#bfbfbf}.widget-action-bar button:active,.widget-action-bar .btn-action:active,.widget-action-bar .btn-delete:active,.widget-action-bar .btn-config:active{background-color:#e6e6e6}.widget-action-bar .btn-delete:hover{background-color:#fff1f0;color:#ff4d4f;border-color:#ffa39e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TestChartComponent, selector: "lib-test-chart", inputs: ["idComponent"] }, { kind: "component", type: YwEmptyVisualComponent, selector: "yw-empty-visual", inputs: ["idComponent", "config"] }, { kind: "component", type: LineChartComponent, selector: "yw-line-chart", inputs: ["chartData", "config", "idComponent"] }, { kind: "component", type: BarChartComponent, selector: "yw-bar-chart", inputs: ["chartData", "config", "idComponent"] }, { kind: "component", type: PieChartComponent, selector: "yw-pie-chart", inputs: ["idComponent", "chartData", "config"] }, { kind: "component", type: GaugeChartComponent, selector: "yw-gauge-chart", inputs: ["idComponent", "chartData", "config"] }, { kind: "component", type: BubbleChartComponent, selector: "yw-bubble-chart", inputs: ["chartData", "config", "idComponent"] }] }); }
3556
3481
  }
3557
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterWidgetComponent, decorators: [{
3482
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardWidgetComponent, decorators: [{
3558
3483
  type: Component,
3559
- args: [{ selector: 'yw-dashboard-filter-widget', standalone: true, imports: [CommonModule,
3484
+ args: [{ selector: 'yw-dashboard-widget', standalone: true, imports: [CommonModule,
3485
+ TestChartComponent,
3560
3486
  YwEmptyVisualComponent,
3561
- YwBaseSlicerComponent,
3562
- YwButtonSlicerComponent,
3563
- YwListSlicerComponent,
3564
- ], template: "\r\n<div class=\"widget-filter-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div>\r\n<div class=\"widget-filter-content-wrapper\">\r\n @if ( typeComponent == 'empty-visual' ) { \r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-slicer' ) { \r\n <ng-container *ngTemplateOutlet=\"filterslicer; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-button' ) { \r\n <ng-container *ngTemplateOutlet=\"filterbutton; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-list' ) { \r\n <ng-container *ngTemplateOutlet=\"filterlist; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER SLICER TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterslicer let-control = \"control\"> \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\" \r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\" \r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n @if (control.controlConfig.dataType === 'number') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\"\r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\"\r\n [step]=\"control.controlConfig.step\"\r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER BUTTON TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterbutton let-control = \"control\"> \r\n <yw-button-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [options]=\"control.controlConfig.options\"\r\n [allowMultiple]=\"control.controlConfig.allowMultiple\"\r\n [currentIds]=\"control.controlConfig.currentIds\"\r\n (selectionChange)=\"onFilterButtonChange($event)\" \r\n >\r\n </yw-button-slicer>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER LIST TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterlist let-control = \"control\"> \r\n \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [minDate]=\"control.controlConfig.minDate\"\r\n [maxDate]=\"control.controlConfig.maxDate\"\r\n [dateHierarchyType]=\"control.controlConfig.dateHierarchyType\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n @if (control.controlConfig.dataType === 'text') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\"\r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [rawData]=\"control.controlConfig.rawData\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #040404);border-radius:var(--widget-border-radius, 8px);padding:var(--widget-padding, 10px)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-filter-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-filter-content-wrapper{width:100%;display:flex;flex-direction:column}\n"] }]
3565
- }], ctorParameters: () => [], propDecorators: { configWidget: [{
3487
+ LineChartComponent,
3488
+ BarChartComponent,
3489
+ PieChartComponent,
3490
+ GaugeChartComponent, BubbleChartComponent], host: {
3491
+ '[style.--widget-bg]': 'instanceStyleWidget().widgetBackgroundColor',
3492
+ '[style.--widget-border-color]': 'instanceStyleWidget().widgetBorderColor'
3493
+ }, template: "\r\n<!-- <div class=\"widget-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\">{{ isLocked() ? '\uD83D\uDD12' : '\uD83D\uDD13' }}</button>\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\">{{ isMaximized() ? '\uD83D\uDDD7' : '\uD83D\uDDD6' }}</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div> -->\r\n\r\n<div class=\"widget-action-bar\">\r\n <!-- Bot\u00F3n Bloquear / Desbloquear -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-action\" (click)=\"onLockClick()\" [title]=\"isLocked() ? 'Desbloquear' : 'Bloquear'\">\r\n <svg *ngIf=\"isLocked(); else unlockedIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path></svg>\r\n <ng-template #unlockedIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Maximizar / Restaurar -->\r\n <button class=\"btn-action\" (click)=\"onMaximizeClick()\" [title]=\"isMaximized() ? 'Restaurar' : 'Maximizar'\">\r\n <svg *ngIf=\"isMaximized(); else maxIcon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\"></path></svg>\r\n <ng-template #maxIcon>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"></path></svg>\r\n </ng-template>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Eliminar -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\" title=\"Eliminar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path></svg>\r\n </button>\r\n\r\n <!-- Bot\u00F3n Configuraci\u00F3n -->\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\" title=\"Configurar\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"></circle><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"></path></svg>\r\n </button>\r\n</div>\r\n\r\n<div class=\"widget-content-wrapper\">\r\n @if ( typeComponent == 'dummy' ) {\r\n <ng-container *ngTemplateOutlet=\"testdummy; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'empty-visual' ) {\r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-bar' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbar; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-line' ) {\r\n <ng-container *ngTemplateOutlet=\"chartline; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'chart-doughnut' ) {\r\n <ng-container *ngTemplateOutlet=\"chartdoughnut; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-gauge' ) {\r\n <ng-container *ngTemplateOutlet=\"chartgauge; context: { control: dataWidget }\"></ng-container>\r\n } \r\n @if ( typeComponent == 'chart-bubble' ) {\r\n <ng-container *ngTemplateOutlet=\"chartbubble; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- DUMMY TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #testdummy let-control = \"control\">\r\n <lib-test-chart [idComponent]=\"control.idComponent\" > \r\n </lib-test-chart>\r\n</ng-template>\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BAR CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbar let-control = \"control\">\r\n <yw-bar-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-bar-chart>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE CHART TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartline let-control = \"control\">\r\n <yw-line-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-line-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE DOUGHNUT TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartdoughnut let-control = \"control\">\r\n <yw-pie-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-pie-chart>\r\n</ng-template>\r\n\r\n \r\n<!--------------------------------------------------------------->\r\n<!---------------------- LINE GAUGE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartgauge let-control = \"control\">\r\n <yw-gauge-chart [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\" \r\n >\r\n </yw-gauge-chart>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- BUBBLE TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #chartbubble let-control=\"control\">\r\n \r\n <yw-bubble-chart \r\n [idComponent]=\"control.idComponent\"\r\n [chartData]=\"control.controlData\"\r\n [config]=\"control.controlConfig\">\r\n </yw-bubble-chart>\r\n</ng-template> ", styles: ["@charset \"UTF-8\";:host{display:block;flex-direction:column;width:100%;overflow:hidden;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #007bff)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-content-wrapper{width:100%;flex-grow:1;height:0;display:flex;flex-direction:column}.widget-action-bar button,.widget-action-bar .btn-action,.widget-action-bar .btn-delete,.widget-action-bar .btn-config{background:transparent;border:1px solid #d9d9d9;border-radius:4px;cursor:pointer;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:#595959;padding:0;transition:all .2s ease}.widget-action-bar button svg,.widget-action-bar .btn-action svg,.widget-action-bar .btn-delete svg,.widget-action-bar .btn-config svg{width:14px;height:14px}.widget-action-bar button:hover,.widget-action-bar .btn-action:hover,.widget-action-bar .btn-delete:hover,.widget-action-bar .btn-config:hover{background-color:#f5f5f5;color:#262626;border-color:#bfbfbf}.widget-action-bar button:active,.widget-action-bar .btn-action:active,.widget-action-bar .btn-delete:active,.widget-action-bar .btn-config:active{background-color:#e6e6e6}.widget-action-bar .btn-delete:hover{background-color:#fff1f0;color:#ff4d4f;border-color:#ffa39e}\n"] }]
3494
+ }], ctorParameters: () => [], propDecorators: { styleWidget: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleWidget", required: false }] }], configWidget: [{
3566
3495
  type: Input
3567
- }], controlConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlConfig", required: false }] }], controlData: [{
3496
+ }], controlConfig: [{
3568
3497
  type: Input
3569
- }], eventDeleteRequest: [{ type: i0.Output, args: ["eventDeleteRequest"] }], eventShowConfigFormRequest: [{ type: i0.Output, args: ["eventShowConfigFormRequest"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
3498
+ }], controlData: [{
3499
+ type: Input
3500
+ }], isLocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLocked", required: false }] }, { type: i0.Output, args: ["isLockedChange"] }], eventDeleteRequest: [{ type: i0.Output, args: ["eventDeleteRequest"] }], eventLockToggleRequest: [{ type: i0.Output, args: ["eventLockToggleRequest"] }], eventMaximizeToggleRequest: [{ type: i0.Output, args: ["eventMaximizeToggleRequest"] }], eventShowConfigFormRequest: [{ type: i0.Output, args: ["eventShowConfigFormRequest"] }] } });
3570
3501
 
3571
- class YwDashboardFilterComponent {
3502
+ class YwBaseSlicerComponent {
3572
3503
  constructor() {
3573
- this.dashboardFilterConfig = {
3574
- showConfigButtons: false,
3575
- lockPositionWidgets: false,
3576
- controlListConfig: []
3577
- };
3578
- this.dashboardData = [];
3579
- this.filterSelectionChange = output();
3580
- this.controlListConfig = [];
3581
- }
3582
- ngOnInit() {
3583
- this.controlListConfig = [];
3584
- this.dashboardFilterConfig.controlListConfig.forEach((item) => {
3585
- const tableName = item.controlTable;
3586
- const prmcontrolData = this.fn_getDataForWidgetFilterComponent(tableName, this.dashboardData);
3587
- const prmconfigWidget = { ...item };
3588
- if (item.controlFilterData) {
3589
- item.configComponent = this.fnSetControlFilterCurrentValues(prmconfigWidget);
3590
- }
3591
- const configControl = { configWidget: prmconfigWidget,
3592
- controlData: prmcontrolData,
3593
- controlConfig: item.configComponent
3594
- };
3595
- this.controlListConfig.push(configControl);
3596
- });
3597
- }
3598
- //Filtrar Datos por Componente del Widget
3599
- fn_getDataForWidgetFilterComponent(prmTableName, prmdashboardData) {
3600
- let dataresponse = [];
3601
- const ldsize = prmdashboardData.length;
3602
- prmdashboardData.forEach((curTableRef) => {
3603
- if (prmTableName.localeCompare(curTableRef.tableName) == 0) {
3604
- dataresponse = curTableRef.tableData;
3504
+ // Inputs de configuración del rango de fechas
3505
+ this.idComponent = '';
3506
+ this.dataType = 'date';
3507
+ this.label = input('Fecha', ...(ngDevMode ? [{ debugName: "label" }] : []));
3508
+ this.min = input(...(ngDevMode ? [undefined, { debugName: "min" }] : []));
3509
+ this.max = input(...(ngDevMode ? [undefined, { debugName: "max" }] : []));
3510
+ this.step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
3511
+ this.minCurrent = input(...(ngDevMode ? [undefined, { debugName: "minCurrent" }] : []));
3512
+ this.maxCurrent = input(...(ngDevMode ? [undefined, { debugName: "maxCurrent" }] : []));
3513
+ this.minNumber = 0;
3514
+ this.maxNumber = 1000;
3515
+ this.minDate = '2020-01-01';
3516
+ this.maxDate = '2022-12-31';
3517
+ // Evento de salida
3518
+ this.rangeChange = output();
3519
+ // Signals para las fechas seleccionadas
3520
+ this.startDate = signal('2020-01-01', ...(ngDevMode ? [{ debugName: "startDate" }] : []));
3521
+ this.endDate = signal('2022-09-22', ...(ngDevMode ? [{ debugName: "endDate" }] : []));
3522
+ // Signals para los numeros seleccionadas
3523
+ this.startVal = signal(0, ...(ngDevMode ? [{ debugName: "startVal" }] : []));
3524
+ this.endVal = signal(1000, ...(ngDevMode ? [{ debugName: "endVal" }] : []));
3525
+ this.minTimestamp = computed(() => new Date(this.minDate).getTime(), ...(ngDevMode ? [{ debugName: "minTimestamp" }] : []));
3526
+ this.maxTimestamp = computed(() => new Date(this.maxDate).getTime(), ...(ngDevMode ? [{ debugName: "maxTimestamp" }] : []));
3527
+ this.startTimestamp = computed(() => new Date(this.startDate()).getTime(), ...(ngDevMode ? [{ debugName: "startTimestamp" }] : []));
3528
+ this.endTimestamp = computed(() => new Date(this.endDate()).getTime(), ...(ngDevMode ? [{ debugName: "endTimestamp" }] : []));
3529
+ // Inicializar valores al arrancar
3530
+ effect(() => {
3531
+ if (this.dataType == 'date') {
3532
+ this.startDate.set(this.minCurrent() || this.minDate);
3533
+ this.endDate.set(this.maxCurrent() || this.maxDate);
3605
3534
  }
3606
- });
3607
- return dataresponse;
3608
- }
3609
- onFilterChange(eventData) {
3610
- this.filterSelectionChange.emit({ widgetId: eventData.widgetId, eventResponse: eventData.eventResponse });
3611
- }
3612
- fnSetControlFilterCurrentValues(prmControlConfig) {
3613
- if (prmControlConfig.typeComponent == 'filter-slicer') {
3614
- let curConfigComponent = prmControlConfig.configComponent;
3615
- if (prmControlConfig.configComponent.dataType == 'date') {
3616
- curConfigComponent.minCurrent = prmControlConfig.controlFilterData['startDate'];
3617
- curConfigComponent.maxCurrent = prmControlConfig.controlFilterData['endDate'];
3535
+ if (this.dataType == 'number') {
3536
+ this.startVal.set(this.minCurrent() || this.minNumber);
3537
+ this.endVal.set(this.maxCurrent() || this.maxNumber);
3618
3538
  }
3619
- if (prmControlConfig.configComponent.dataType == 'number') {
3620
- curConfigComponent.minCurrent = prmControlConfig.controlFilterData['minValue'];
3621
- curConfigComponent.maxCurrent = prmControlConfig.controlFilterData['maxValue'];
3539
+ }, { allowSignalWrites: true });
3540
+ // Notificar al padre cada vez que cambien los valores
3541
+ effect(() => {
3542
+ if (this.dataType == 'date') {
3543
+ this.rangeChange.emit({
3544
+ startDate: this.startDate(),
3545
+ endDate: this.endDate()
3546
+ });
3622
3547
  }
3623
- return curConfigComponent;
3624
- }
3625
- if (prmControlConfig.typeComponent == 'filter-button') {
3626
- let curConfigComponent = prmControlConfig.configComponent;
3627
- curConfigComponent.currentIds = prmControlConfig.controlFilterData || [];
3628
- return curConfigComponent;
3629
- }
3630
- if (prmControlConfig.typeComponent == 'filter-list') {
3631
- let curConfigComponent = prmControlConfig.configComponent;
3632
- curConfigComponent.currentSelectedId = prmControlConfig.controlFilterData['currentNodeId'];
3633
- return curConfigComponent;
3634
- }
3635
- }
3636
- handleClearFilter() {
3637
- this.controlListConfig = [];
3638
- this.dashboardFilterConfig.controlListConfig.forEach((item) => {
3639
- const tableName = item.controlTable;
3640
- const prmcontrolData = this.fn_getDataForWidgetFilterComponent(tableName, this.dashboardData);
3641
- const prmconfigWidget = { ...item };
3642
- if (item.controlFilterData) {
3643
- item.configComponent = this.fnSetControlFilterClearValues(prmconfigWidget);
3548
+ if (this.dataType == 'number') {
3549
+ this.rangeChange.emit({
3550
+ minValue: this.startVal(),
3551
+ maxValue: this.endVal()
3552
+ });
3644
3553
  }
3645
- const configControl = { configWidget: prmconfigWidget,
3646
- controlData: prmcontrolData,
3647
- controlConfig: item.configComponent
3648
- };
3649
- this.controlListConfig.push(configControl);
3650
3554
  });
3651
3555
  }
3652
- fnSetControlFilterClearValues(prmControlConfig) {
3653
- if (prmControlConfig.typeComponent == 'filter-slicer') {
3654
- let curConfigComponent = prmControlConfig.configComponent;
3655
- curConfigComponent.minCurrent = null;
3656
- curConfigComponent.maxCurrent = null;
3657
- return curConfigComponent;
3658
- }
3659
- if (prmControlConfig.typeComponent == 'filter-button') {
3660
- let curConfigComponent = prmControlConfig.configComponent;
3661
- curConfigComponent.currentIds = [];
3662
- return curConfigComponent;
3556
+ ngOnInit() {
3557
+ if (this.dataType == 'date') {
3558
+ this.minDate = this.min();
3559
+ this.maxDate = this.max();
3560
+ this.minTimestamp = computed(() => new Date(this.minDate).getTime(), ...(ngDevMode ? [{ debugName: "minTimestamp" }] : []));
3561
+ this.maxTimestamp = computed(() => new Date(this.maxDate).getTime(), ...(ngDevMode ? [{ debugName: "maxTimestamp" }] : []));
3562
+ this.startTimestamp = computed(() => new Date(this.startDate()).getTime(), ...(ngDevMode ? [{ debugName: "startTimestamp" }] : []));
3563
+ this.endTimestamp = computed(() => new Date(this.endDate()).getTime(), ...(ngDevMode ? [{ debugName: "endTimestamp" }] : []));
3663
3564
  }
3664
- if (prmControlConfig.typeComponent == 'filter-list') {
3665
- let curConfigComponent = prmControlConfig.configComponent;
3666
- curConfigComponent.currentSelectedId = null;
3667
- return curConfigComponent;
3565
+ if (this.dataType == 'number') {
3566
+ this.minNumber = this.min();
3567
+ this.maxNumber = this.max();
3668
3568
  }
3669
3569
  }
3670
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3671
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardFilterComponent, isStandalone: true, selector: "yw-dashboard-filter", inputs: { dashboardFilterConfig: "dashboardFilterConfig", dashboardData: "dashboardData" }, outputs: { filterSelectionChange: "filterSelectionChange" }, ngImport: i0, template: "\r\n <div class=\"dashboard-filter-content-bar\">\r\n @if ( dashboardFilterConfig.showConfigButtons ) { \r\n <button nz-button nzType=\"primary\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n }\r\n <button nz-button \r\n nzType=\"default\" \r\n nzShape=\"circle\" \r\n (click)=\"handleClearFilter()\" \r\n class=\"btn-clean\" \r\n > \r\n <!-- Icono de Borrador (Eraser) activo / relleno --> \r\n <svg \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M16.24 3.56a2.12 2.12 0 0 1 3 0l1.2 1.2a2.12 2.12 0 0 1 0 3l-8.5 8.5H6.5a1.5 1.5 0 0 1-1.5-1.5v-5.44l11.24-11.24zM19 19H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2z\"/>\r\n </svg>\r\n \r\n </button>\r\n </div>\r\n\r\n <div class=\"dashboard-filter-content-wrapper\">\r\n @for ( colFilter of controlListConfig; track colFilter) { \r\n <div class=\"dashboard-filter-content-wrapper-item\">\r\n \r\n <yw-dashboard-filter-widget \r\n [configWidget]=\"colFilter.configWidget\"\r\n [controlConfig]=\"colFilter.controlConfig\"\r\n [controlData]=\"colFilter.controlData\"\r\n (selectionChange)=\"onFilterChange($event)\"\r\n > \r\n </yw-dashboard-filter-widget>\r\n\r\n </div>\r\n }\r\n </div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden;box-sizing:border-box}.dashboard-filter-content-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%;box-sizing:border-box}.dashboard-filter-content-wrapper{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;width:100%;display:flex;flex-direction:column;box-sizing:border-box}.dashboard-filter-content-wrapper-item{padding-bottom:5px}.btn-clean{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.btn-clean.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.btn-clean.active:hover{background-color:#40a9ff;border-color:#40a9ff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: YwDashboardFilterWidgetComponent, selector: "yw-dashboard-filter-widget", inputs: ["configWidget", "controlConfig", "controlData"], outputs: ["eventDeleteRequest", "eventShowConfigFormRequest", "selectionChange"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i1$3.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i5.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i6.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i7.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }] }); }
3570
+ // Handlers para la entrada manual de fechas
3571
+ onStartDateInput(value) {
3572
+ if (value && value <= this.endDate()) {
3573
+ this.startDate.set(value);
3574
+ }
3575
+ }
3576
+ onEndDateInput(value) {
3577
+ if (value && value >= this.startDate()) {
3578
+ this.endDate.set(value);
3579
+ }
3580
+ }
3581
+ // Métodos para cambiar desde el Input Numérico
3582
+ onStartInput(event) {
3583
+ const value = Number(event.target.value);
3584
+ if (!isNaN(value) && value <= this.endVal() && value >= this.min()) {
3585
+ this.startVal.set(value);
3586
+ }
3587
+ }
3588
+ onEndInput(event) {
3589
+ const value = Number(event.target.value);
3590
+ if (!isNaN(value) && value >= this.startVal() && value <= this.max()) {
3591
+ this.endVal.set(value);
3592
+ }
3593
+ }
3594
+ // Handlers para el movimiento del slider
3595
+ onStartSliderChange(event) {
3596
+ if (this.dataType == 'date') {
3597
+ const val = Number(event.target.value);
3598
+ const dateStr = new Date(val).toISOString().split('T')[0];
3599
+ if (val < this.endTimestamp()) {
3600
+ this.startDate.set(dateStr);
3601
+ }
3602
+ }
3603
+ if (this.dataType == 'number') {
3604
+ const val = Number(event.target.value);
3605
+ if (val < this.endVal()) {
3606
+ this.startVal.set(val);
3607
+ }
3608
+ }
3609
+ }
3610
+ onEndSliderChange(event) {
3611
+ if (this.dataType == 'date') {
3612
+ const val = Number(event.target.value);
3613
+ const dateStr = new Date(val).toISOString().split('T')[0];
3614
+ if (val > this.startTimestamp()) {
3615
+ this.endDate.set(dateStr);
3616
+ }
3617
+ }
3618
+ if (this.dataType == 'number') {
3619
+ const val = Number(event.target.value);
3620
+ if (val > this.startVal()) {
3621
+ this.endVal.set(val);
3622
+ }
3623
+ }
3624
+ }
3625
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwBaseSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3626
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwBaseSlicerComponent, isStandalone: true, selector: "yw-base-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, minCurrent: { classPropertyName: "minCurrent", publicName: "minCurrent", isSignal: true, isRequired: false, transformFunction: null }, maxCurrent: { classPropertyName: "maxCurrent", publicName: "maxCurrent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n <label class=\"filter-label\">{{ label() }}</label>\r\n\r\n <!-- Inputs de fecha superiores -->\r\n <div class=\"date-inputs-row\">\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"startDate()\"\r\n [min]=\"minDate\"\r\n [max]=\"endDate()\"\r\n (change)=\"onStartDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"startVal()\"\r\n [min]=\"minNumber\"\r\n [max]=\"endVal()\"\r\n [step]=\"step()\"\r\n (input)=\"onStartInput($event)\" \r\n />\r\n } \r\n </div>\r\n\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"endDate()\"\r\n [min]=\"startDate()\"\r\n [max]=\"maxDate\"\r\n (change)=\"onEndDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"endVal()\"\r\n [min]=\"startVal()\"\r\n [max]=\"maxNumber\"\r\n [step]=\"step()\"\r\n (input)=\"onEndInput($event)\" \r\n />\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Dual Slider personalizado -->\r\n <div class=\"slider-container\">\r\n <div class=\"slider-track\"></div>\r\n @if (dataType === 'date') { \r\n <!-- Slider Inicio -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"startTimestamp()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider Fin -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"endTimestamp()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n }\r\n\r\n @if (dataType === 'number') { \r\n\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"startVal()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider M\u00E1ximo -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"endVal()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n\r\n }\r\n\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:360px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-label{font-size:13px;font-weight:600;color:#333}.date-inputs-row,.number-inputs-row{display:flex;gap:8px}.input-wrapper input[type=date]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=date]:focus{border-color:#a0aec0}.input-wrapper input[type=number]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;width:90px;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=number]:focus{border-color:#a0aec0}.slider-container{position:relative;width:100%;height:30px;display:flex;align-items:center;margin-top:4px}.slider-track{position:absolute;width:100%;height:2px;background-color:#666;border-radius:1px;z-index:1}.thumb{position:absolute;width:100%;appearance:none;-webkit-appearance:none;background:none;pointer-events:none;outline:none;margin:0;z-index:2}.thumb-right{z-index:3}.thumb::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box;transition:transform .1s ease}.thumb::-webkit-slider-thumb:hover{transform:scale(1.1);border-color:#333}.thumb::-moz-range-thumb{pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box}\n"] }); }
3672
3627
  }
3673
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterComponent, decorators: [{
3628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwBaseSlicerComponent, decorators: [{
3674
3629
  type: Component,
3675
- args: [{ selector: 'yw-dashboard-filter', standalone: true, imports: [CommonModule,
3676
- YwDashboardFilterWidgetComponent,
3677
- NzIconModule,
3678
- NzButtonModule,
3679
- ], template: "\r\n <div class=\"dashboard-filter-content-bar\">\r\n @if ( dashboardFilterConfig.showConfigButtons ) { \r\n <button nz-button nzType=\"primary\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n }\r\n <button nz-button \r\n nzType=\"default\" \r\n nzShape=\"circle\" \r\n (click)=\"handleClearFilter()\" \r\n class=\"btn-clean\" \r\n > \r\n <!-- Icono de Borrador (Eraser) activo / relleno --> \r\n <svg \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M16.24 3.56a2.12 2.12 0 0 1 3 0l1.2 1.2a2.12 2.12 0 0 1 0 3l-8.5 8.5H6.5a1.5 1.5 0 0 1-1.5-1.5v-5.44l11.24-11.24zM19 19H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2z\"/>\r\n </svg>\r\n \r\n </button>\r\n </div>\r\n\r\n <div class=\"dashboard-filter-content-wrapper\">\r\n @for ( colFilter of controlListConfig; track colFilter) { \r\n <div class=\"dashboard-filter-content-wrapper-item\">\r\n \r\n <yw-dashboard-filter-widget \r\n [configWidget]=\"colFilter.configWidget\"\r\n [controlConfig]=\"colFilter.controlConfig\"\r\n [controlData]=\"colFilter.controlData\"\r\n (selectionChange)=\"onFilterChange($event)\"\r\n > \r\n </yw-dashboard-filter-widget>\r\n\r\n </div>\r\n }\r\n </div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden;box-sizing:border-box}.dashboard-filter-content-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%;box-sizing:border-box}.dashboard-filter-content-wrapper{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;width:100%;display:flex;flex-direction:column;box-sizing:border-box}.dashboard-filter-content-wrapper-item{padding-bottom:5px}.btn-clean{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.btn-clean.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.btn-clean.active:hover{background-color:#40a9ff;border-color:#40a9ff}\n"] }]
3680
- }], ctorParameters: () => [], propDecorators: { dashboardFilterConfig: [{
3630
+ args: [{ selector: 'yw-base-slicer', imports: [], host: {
3631
+ '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3632
+ }, template: "<div class=\"filter-container\">\r\n <label class=\"filter-label\">{{ label() }}</label>\r\n\r\n <!-- Inputs de fecha superiores -->\r\n <div class=\"date-inputs-row\">\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"startDate()\"\r\n [min]=\"minDate\"\r\n [max]=\"endDate()\"\r\n (change)=\"onStartDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"startVal()\"\r\n [min]=\"minNumber\"\r\n [max]=\"endVal()\"\r\n [step]=\"step()\"\r\n (input)=\"onStartInput($event)\" \r\n />\r\n } \r\n </div>\r\n\r\n <div class=\"input-wrapper\">\r\n @if (dataType === 'date') { \r\n <input \r\n type=\"date\" \r\n [value]=\"endDate()\"\r\n [min]=\"startDate()\"\r\n [max]=\"maxDate\"\r\n (change)=\"onEndDateInput($any($event.target).value)\" \r\n />\r\n }\r\n @if (dataType === 'number') { \r\n <input \r\n type=\"number\" \r\n [value]=\"endVal()\"\r\n [min]=\"startVal()\"\r\n [max]=\"maxNumber\"\r\n [step]=\"step()\"\r\n (input)=\"onEndInput($event)\" \r\n />\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Dual Slider personalizado -->\r\n <div class=\"slider-container\">\r\n <div class=\"slider-track\"></div>\r\n @if (dataType === 'date') { \r\n <!-- Slider Inicio -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"startTimestamp()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider Fin -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minTimestamp()\" \r\n [max]=\"maxTimestamp()\" \r\n [value]=\"endTimestamp()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n }\r\n\r\n @if (dataType === 'number') { \r\n\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"startVal()\"\r\n (input)=\"onStartSliderChange($event)\"\r\n class=\"thumb thumb-left\"\r\n />\r\n\r\n <!-- Slider M\u00E1ximo -->\r\n <input \r\n type=\"range\" \r\n [min]=\"minNumber\" \r\n [max]=\"maxNumber\" \r\n [step]=\"step()\"\r\n [value]=\"endVal()\"\r\n (input)=\"onEndSliderChange($event)\"\r\n class=\"thumb thumb-right\"\r\n />\r\n\r\n }\r\n\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:360px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-label{font-size:13px;font-weight:600;color:#333}.date-inputs-row,.number-inputs-row{display:flex;gap:8px}.input-wrapper input[type=date]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=date]:focus{border-color:#a0aec0}.input-wrapper input[type=number]{border:1px solid #e2e8f0;border-radius:2px;padding:4px 8px;font-size:13px;color:#333;width:90px;outline:none;background-color:#fff;box-shadow:0 1px 2px #0000000d}.input-wrapper input[type=number]:focus{border-color:#a0aec0}.slider-container{position:relative;width:100%;height:30px;display:flex;align-items:center;margin-top:4px}.slider-track{position:absolute;width:100%;height:2px;background-color:#666;border-radius:1px;z-index:1}.thumb{position:absolute;width:100%;appearance:none;-webkit-appearance:none;background:none;pointer-events:none;outline:none;margin:0;z-index:2}.thumb-right{z-index:3}.thumb::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box;transition:transform .1s ease}.thumb::-webkit-slider-thumb:hover{transform:scale(1.1);border-color:#333}.thumb::-moz-range-thumb{pointer-events:auto;width:22px;height:22px;border-radius:50%;background:#fff;border:3px solid #666666;cursor:pointer;box-sizing:border-box}\n"] }]
3633
+ }], ctorParameters: () => [], propDecorators: { idComponent: [{
3681
3634
  type: Input
3682
- }], dashboardData: [{
3635
+ }], dataType: [{
3683
3636
  type: Input
3684
- }], filterSelectionChange: [{ type: i0.Output, args: ["filterSelectionChange"] }] } });
3637
+ }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], minCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "minCurrent", required: false }] }], maxCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCurrent", required: false }] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }] } });
3685
3638
 
3686
- /* END - FILTROS */
3687
- class YwDashboardComponent {
3688
- /* */
3689
- constructor(environmentInjector, appRef) {
3690
- //Configurar Bloqueo de Widgets
3691
- /*
3639
+ class YwButtonSlicerComponent {
3640
+ constructor() {
3641
+ // Configuración de entradas
3642
+ this.idComponent = '';
3643
+ this.label = input('Tipo de producto', ...(ngDevMode ? [{ debugName: "label" }] : []));
3644
+ this.options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
3645
+ this.allowMultiple = input(true, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : [])); // Permite cambiar a selección única si es necesario
3646
+ // Evento de salida con la lista de IDs seleccionados
3647
+ this.selectionChange = output();
3648
+ // Signal con los IDs seleccionados
3649
+ this.selectedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedIds" }] : []));
3650
+ // Current IDs seleccionados
3651
+ this.currentIds = input([], ...(ngDevMode ? [{ debugName: "currentIds" }] : []));
3652
+ // Emitir el listado en Array cada vez que la Signal de selección cambie
3692
3653
  effect(() => {
3693
- const isLocked = this.allIsLocked();
3694
- console.log('El estado de bloqueo cambió a:', isLocked);
3695
- this.fn_lockPositionWidgets(isLocked);
3654
+ this.selectionChange.emit(Array.from(this.selectedIds()));
3696
3655
  });
3697
- */
3698
- this.environmentInjector = environmentInjector;
3699
- this.appRef = appRef;
3700
- this.widgetsActivos = new Map();
3701
- this.showControls = input(false, ...(ngDevMode ? [{ debugName: "showControls" }] : []));
3702
- this.showFilter = input(false, ...(ngDevMode ? [{ debugName: "showFilter" }] : []));
3703
- this.allIsLocked = signal(false, ...(ngDevMode ? [{ debugName: "allIsLocked" }] : []));
3704
- this.drawerfiltervisible = false;
3705
- this.drawerconfigvisible = false;
3706
- this.drawerCurrentWidgetObject = {};
3707
- this.isLocked = false;
3708
- this.countWidget = 1;
3709
- this.layoutConfig = {
3710
- margin: 10,
3711
- cellHeight: 100,
3712
- columns: 24
3713
- };
3714
- /* ejemplo de Items
3715
- items: [
3716
- { id: 'div-1', x: 0, y: 0, w: 14, h: 2, content: 'DIV 1' , contentData : {}},
3717
- { id: 'div-2', x: 5, y: 2, w: 19, h: 1, content: 'DIV 2' , contentData : {}},
3718
- { id: 'div-3', x: 0, y: 3, w: 22, h: 1, content: 'DIV 3', contentData : {} }
3719
- ]
3720
- */
3721
- this.dashboardStyle = {
3722
- canvasBackgroundColor: "#f5f5f5",
3723
- widgetBackgroundColor: "#ffffff",
3724
- widgetBorderColor: "#007bff"
3725
- };
3726
- this.dashboardConfig = {
3727
- showConfigButtons: false,
3728
- lockPositionWidgets: false,
3729
- layoutConfig: {
3730
- margin: 0,
3731
- cellHeight: 0,
3732
- columns: 0
3733
- },
3734
- controlListConfig: []
3735
- };
3736
- this.dashboardData = [];
3737
- this.dashboardItems = [];
3738
- /* REF FILTROS */
3739
- this.dashboardFilterConfig = {
3740
- showConfigButtons: false,
3741
- lockPositionWidgets: false,
3742
- controlListConfig: []
3743
- };
3744
3656
  }
3745
3657
  ngOnInit() {
3746
- }
3747
- ngOnChanges(changes) {
3748
- if (changes['dashboardConfig']) {
3749
- console.log('Nuevo valor dashboardConfig:', this.dashboardConfig);
3750
- //this.initCanvasGrid();
3751
- }
3752
- if (changes['dashboardData']) {
3753
- console.log('Nuevo valor dashboardData:', this.dashboardData);
3754
- //this.initCanvasGrid();
3658
+ if (this.currentIds() && this.currentIds().length > 0) {
3659
+ this.selectedIds().clear();
3660
+ this.currentIds().forEach((itemId) => {
3661
+ this.selectedIds().add(itemId);
3662
+ });
3755
3663
  }
3756
3664
  }
3757
- ngAfterViewInit() {
3758
- this.initCanvasGrid();
3759
- }
3760
- initCanvasGrid() {
3761
- this.layoutConfig = this.dashboardConfig.layoutConfig;
3762
- // Opciones para restringir colisiones y comportamiento
3763
- let options = {
3764
- column: this.layoutConfig.columns,
3765
- cellHeight: this.layoutConfig.cellHeight,
3766
- margin: this.layoutConfig.margin,
3767
- float: true, // Evita que los divs floten automáticamente hacia arriba
3768
- //noOverlap: true, // CLAVE: Bloquea el movimiento si colisiona con otro DIV
3769
- resizable: { handles: 'e, se, s, sw, w, nw, n, ne', autoHide: true } // Permite redimensionar desde todos los bordes
3770
- };
3771
- if (this.layoutConfig.columnOpts) {
3772
- options = {
3773
- column: this.layoutConfig.columns,
3774
- cellHeight: this.layoutConfig.cellHeight,
3775
- margin: this.layoutConfig.margin,
3776
- columnOpts: {
3777
- breakpointForWindow: this.layoutConfig.columnOpts.breakpointForWindow, // Mide el ancho de la ventana gráfica
3778
- breakpoints: this.layoutConfig.columnOpts.breakpoints,
3779
- layout: this.layoutConfig.columnOpts.layout || 'list', // Mantiene el orden de los widgets secuencialmente al colapsar
3780
- },
3781
- float: true, // Evita que los divs floten automáticamente hacia arriba
3782
- //noOverlap: true, // CLAVE: Bloquea el movimiento si colisiona con otro DIV
3783
- resizable: { handles: 'e, se, s, sw, w, nw, n, ne', autoHide: true } // Permite redimensionar desde todos los bordes
3784
- };
3785
- }
3786
- /*EXAMPLE OF "columnOpts"
3787
- columnOpts: {
3788
- breakpointForWindow: true,
3789
- breakpoints: [
3790
- { w: 768, c: 1 },
3791
- { w: 992, c: 6 },
3792
- { w: 1200, c: this.layoutConfig.columns } // 👈 Usa tu variable para consistencia en Desktop
3793
- ],
3794
- layout: 'list'
3795
- }
3796
- */
3797
- // Inicializar el canvas
3798
- this.grid = GridStack.init(options, this.gridContainer.nativeElement);
3799
- // Cargar los elementos iniciales desde el JSON
3800
- //this.loadLayoutFromJSON(this.dashboardItems);
3801
- this.loadLayoutFromJSON(this.dashboardConfig, this.dashboardStyle);
3802
- //Evaluar Bloqueo de Widgets en Inicializacion
3803
- if (this.dashboardConfig.lockPositionWidgets) {
3804
- this.fn_lockPositionWidgets(this.dashboardConfig.lockPositionWidgets);
3805
- }
3806
- // Listener para detectar cambios y actualizar el JSON
3807
- this.grid.on('change', (event, items) => {
3808
- this.saveLayoutToJSON();
3809
- });
3810
- //Listener para detectar cuando cambia las dimenciones de un widget
3811
- this.grid.on('resizestop', (event, el) => {
3812
- // Disparamos el evento nativo del navegador de forma controlada
3813
- window.dispatchEvent(new Event('resize'));
3814
- });
3665
+ clearSelection() {
3666
+ this.selectedIds().clear();
3667
+ this.selectionChange.emit([]);
3815
3668
  }
3816
- //Filtrar Datos por Componente del Widget
3817
- fn_getDataForWidgetComponent(listTables, prmDashboardData) {
3818
- let dataresponse = new Map();
3819
- const ldsize = prmDashboardData.length;
3820
- const ltsize = listTables.length;
3821
- if (ltsize > 0) {
3822
- listTables.forEach((curTableName) => {
3823
- prmDashboardData.forEach((curTableRef) => {
3824
- if (curTableName.localeCompare(curTableRef.tableName) == 0) {
3825
- dataresponse.set(curTableRef.tableName, curTableRef.tableData);
3826
- }
3827
- });
3828
- });
3669
+ // Toggle para seleccionar / deseleccionar
3670
+ toggleSelection(id) {
3671
+ const current = new Set(this.selectedIds());
3672
+ if (this.allowMultiple()) {
3673
+ if (current.has(id)) {
3674
+ current.delete(id);
3675
+ }
3676
+ else {
3677
+ current.add(id);
3678
+ }
3829
3679
  }
3830
3680
  else {
3831
- prmDashboardData.forEach((curTableRef) => {
3832
- dataresponse.set(curTableRef.tableName, curTableRef.tableData);
3833
- });
3681
+ // Si es selección única
3682
+ current.clear();
3683
+ current.add(id);
3834
3684
  }
3835
- const object_base_response = Object.fromEntries(dataresponse);
3836
- return object_base_response;
3685
+ this.selectedIds.set(current);
3837
3686
  }
3838
- fn_getDataForWidgetComponent_back(listTables) {
3839
- let dataresponse = [];
3840
- const ldsize = this.dashboardData.length;
3841
- const ltsize = listTables.length;
3842
- if (ltsize > 0) {
3843
- listTables.forEach((curTableName) => {
3844
- this.dashboardData.forEach((curTableData) => {
3845
- if (curTableName.localeCompare(curTableData.tableName) == 0) {
3846
- dataresponse.push(curTableData);
3847
- }
3848
- });
3849
- });
3687
+ isSelected(id) {
3688
+ return this.selectedIds().has(id);
3689
+ }
3690
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwButtonSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3691
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwButtonSlicerComponent, isStandalone: true, selector: "yw-button-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, allowMultiple: { classPropertyName: "allowMultiple", publicName: "allowMultiple", isSignal: true, isRequired: false, transformFunction: null }, currentIds: { classPropertyName: "currentIds", publicName: "currentIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n \r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\"> \r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" >\uD83E\uDDF9</button> \r\n </div>\r\n </div>\r\n\r\n\r\n <!-- Contenedor con barra de desplazamiento interna -->\r\n <div class=\"grid-scroll-container\">\r\n <div class=\"buttons-grid\">\r\n @for (item of options(); track item.id) {\r\n <button \r\n type=\"button\" \r\n class=\"option-btn\" \r\n [class.selected]=\"isSelected(item.id)\"\r\n (click)=\"toggleSelection(item.id)\"\r\n [title]=\"item.label\">\r\n <span class=\"btn-text\">{{ item.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:320px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#333}.header-actions{display:flex;gap:4px}.grid-scroll-container{max-height:145px;overflow-y:auto;padding-right:4px}.buttons-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.option-btn{display:flex;align-items:center;justify-content:center;height:38px;padding:4px 6px;background-color:#fff;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;transition:all .15s ease-in-out;outline:none;box-sizing:border-box}.option-btn:hover{border-color:#9ca3af;background-color:#f9fafb}.btn-text{font-size:12px;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center}.option-btn.selected{border-color:#2563eb;background-color:#eff6ff;box-shadow:0 0 0 1px #2563eb}.option-btn.selected .btn-text{color:#1d4ed8;font-weight:600}.grid-scroll-container::-webkit-scrollbar{width:6px}.grid-scroll-container::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb{background:#71717a;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb:hover{background:#52525b}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
3692
+ }
3693
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwButtonSlicerComponent, decorators: [{
3694
+ type: Component,
3695
+ args: [{ selector: 'yw-button-slicer', standalone: true, imports: [CommonModule], host: {
3696
+ '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3697
+ }, template: "<div class=\"filter-container\">\r\n \r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\"> \r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" >\uD83E\uDDF9</button> \r\n </div>\r\n </div>\r\n\r\n\r\n <!-- Contenedor con barra de desplazamiento interna -->\r\n <div class=\"grid-scroll-container\">\r\n <div class=\"buttons-grid\">\r\n @for (item of options(); track item.id) {\r\n <button \r\n type=\"button\" \r\n class=\"option-btn\" \r\n [class.selected]=\"isSelected(item.id)\"\r\n (click)=\"toggleSelection(item.id)\"\r\n [title]=\"item.label\">\r\n <span class=\"btn-text\">{{ item.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Arial,Helvetica,sans-serif;width:100%;max-width:320px}.filter-container{display:flex;flex-direction:column;gap:8px}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#333}.header-actions{display:flex;gap:4px}.grid-scroll-container{max-height:145px;overflow-y:auto;padding-right:4px}.buttons-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.option-btn{display:flex;align-items:center;justify-content:center;height:38px;padding:4px 6px;background-color:#fff;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;transition:all .15s ease-in-out;outline:none;box-sizing:border-box}.option-btn:hover{border-color:#9ca3af;background-color:#f9fafb}.btn-text{font-size:12px;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center}.option-btn.selected{border-color:#2563eb;background-color:#eff6ff;box-shadow:0 0 0 1px #2563eb}.option-btn.selected .btn-text{color:#1d4ed8;font-weight:600}.grid-scroll-container::-webkit-scrollbar{width:6px}.grid-scroll-container::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb{background:#71717a;border-radius:4px}.grid-scroll-container::-webkit-scrollbar-thumb:hover{background:#52525b}\n"] }]
3698
+ }], ctorParameters: () => [], propDecorators: { idComponent: [{
3699
+ type: Input
3700
+ }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], allowMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowMultiple", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], currentIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentIds", required: false }] }] } });
3701
+
3702
+ class YwListSlicerComponent {
3703
+ constructor() {
3704
+ // Entradas generales
3705
+ this.idComponent = '';
3706
+ this.label = input('Filtro Jerárquico', ...(ngDevMode ? [{ debugName: "label" }] : []));
3707
+ this.clearTitleLabel = input('Limpiar Seleccion', ...(ngDevMode ? [{ debugName: "clearTitleLabel" }] : []));
3708
+ // Opción 1: Entregar datos en árbol de texto directo
3709
+ this.rawData = input([], ...(ngDevMode ? [{ debugName: "rawData" }] : []));
3710
+ // Opción 2: Generar desde fechas
3711
+ this.dataType = input('date', ...(ngDevMode ? [{ debugName: "dataType" }] : []));
3712
+ this.minDate = input('', ...(ngDevMode ? [{ debugName: "minDate" }] : [])); // Formato 'YYYY-MM-DD'
3713
+ this.maxDate = input('', ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
3714
+ this.dateHierarchyType = input('YEAR_TRIMESTER_MONTH', ...(ngDevMode ? [{ debugName: "dateHierarchyType" }] : []));
3715
+ this.monthNames = input(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], ...(ngDevMode ? [{ debugName: "monthNames" }] : []));
3716
+ // Evento de salida con el ID/Nodo seleccionado
3717
+ this.selectionChange = output();
3718
+ this.filterValueChange = output(); // Output 2 (Rangos o Hojas)
3719
+ // ID del elemento seleccionado (Selección Simple)
3720
+ this.selectedId = signal(null, ...(ngDevMode ? [{ debugName: "selectedId" }] : []));
3721
+ this.currentSelectedId = input(null, ...(ngDevMode ? [{ debugName: "currentSelectedId" }] : []));
3722
+ // Arbol de nodos computado
3723
+ this.treeNodes = computed(() => {
3724
+ if (this.dataType() == 'date' && this.minDate() && this.maxDate()) {
3725
+ return this.generateDateHierarchy(this.minDate(), this.maxDate(), this.dateHierarchyType());
3726
+ }
3727
+ return this.rawData();
3728
+ }, ...(ngDevMode ? [{ debugName: "treeNodes" }] : []));
3729
+ effect(() => {
3730
+ const currentId = this.selectedId();
3731
+ if (!currentId) {
3732
+ this.selectionChange.emit(null);
3733
+ this.filterValueChange.emit(null);
3734
+ return;
3735
+ }
3736
+ const node = this.findNodeById(this.treeNodes(), currentId);
3737
+ this.selectionChange.emit(node);
3738
+ if (node) {
3739
+ // Emitir el segundo Output según el modo
3740
+ const calculatedValue = this.processFilterOutput(node);
3741
+ this.filterValueChange.emit(calculatedValue);
3742
+ }
3743
+ });
3744
+ }
3745
+ ngOnInit() {
3746
+ if (this.currentSelectedId()) {
3747
+ this.selectedId.set(this.currentSelectedId());
3850
3748
  }
3851
- else {
3852
- return this.dashboardData;
3749
+ }
3750
+ // Métodos de Selección y Expansión
3751
+ toggleExpand(node, event) {
3752
+ event.stopPropagation();
3753
+ node.expanded = !node.expanded;
3754
+ }
3755
+ selectNode(node, event) {
3756
+ event.stopPropagation();
3757
+ this.selectedId.set(this.selectedId() === node.id ? null : node.id);
3758
+ }
3759
+ clearSelection() {
3760
+ this.selectedId.set(null);
3761
+ this.filterValueChange.emit(null);
3762
+ }
3763
+ // --- LÓGICA DE PROCESAMIENTO DE LAS HOJAS / RANGOS ---
3764
+ processFilterOutput(selectedNode) {
3765
+ // 1. Obtener todas las hojas (nodos del nivel más bajo) pertenecientes al bloque seleccionado
3766
+ const leaves = this.getLeafNodes(selectedNode);
3767
+ if (this.dataType() == 'date') {
3768
+ // Extraer todas las fechas de las hojas del bloque
3769
+ const dates = leaves
3770
+ .map(leaf => leaf.value)
3771
+ .filter(d => d instanceof Date)
3772
+ .sort((a, b) => a.getTime() - b.getTime());
3773
+ if (dates.length > 0) {
3774
+ const minD = dates[0];
3775
+ // Para la fecha máxima, tomamos el último día del mes correspondiente
3776
+ const maxD = new Date(dates[dates.length - 1].getFullYear(), dates[dates.length - 1].getMonth() + 1, 0);
3777
+ return {
3778
+ type: 'DATE_RANGE',
3779
+ dateRange: {
3780
+ minDate: minD.toISOString().split('T')[0],
3781
+ maxDate: maxD.toISOString().split('T')[0]
3782
+ },
3783
+ currentNodeId: selectedNode.id
3784
+ };
3785
+ }
3853
3786
  }
3854
- return dataresponse;
3787
+ // Modo Texto: Entregar arreglo con las hojas
3788
+ return {
3789
+ type: 'TEXT_LEAVES',
3790
+ leafNodes: leaves,
3791
+ leafIds: leaves.map(l => l.id),
3792
+ currentNodeId: selectedNode.id
3793
+ };
3855
3794
  }
3856
- // Carga elementos desde la configuración JSON
3857
- //public loadLayoutFromJSON(dashboardItems: YwWidgetLayoutInterface[], ): void {
3858
- loadLayoutFromJSON(prmDashboardConfig, prmDashboardStyle) {
3859
- this.grid.removeAll();
3860
- const widgets = [];
3861
- this.grid.load(widgets);
3862
- const lsize = prmDashboardConfig.controlListConfig.length;
3863
- if (lsize > 0) {
3864
- prmDashboardConfig.controlListConfig.forEach((widgetConfig) => {
3865
- //Agregar uno por uno, para poder cargar los parametros internos de los componentes.
3866
- const controlData = this.fn_getDataForWidgetComponent(widgetConfig.controlTables, this.dashboardData);
3867
- //Sobre Escribir el ID Correcto en Layaout
3868
- widgetConfig.layoutConfig.id = widgetConfig.widgetId;
3869
- //Evaluar Mostrar Eventos
3870
- if (widgetConfig.showConfigButtons == null || widgetConfig.showConfigButtons == undefined) {
3871
- widgetConfig.showConfigButtons = prmDashboardConfig.showConfigButtons;
3795
+ // Recorrido recursivo para obtener solo los nodos terminales (sin hijos)
3796
+ getLeafNodes(node) {
3797
+ if (!node.children || node.children.length === 0) {
3798
+ return [node];
3799
+ }
3800
+ let leaves = [];
3801
+ for (const child of node.children) {
3802
+ leaves = leaves.concat(this.getLeafNodes(child));
3803
+ }
3804
+ return leaves;
3805
+ }
3806
+ findNodeById(nodes, id) {
3807
+ for (const node of nodes) {
3808
+ if (node.id === id)
3809
+ return node;
3810
+ if (node.children) {
3811
+ const found = this.findNodeById(node.children, id);
3812
+ if (found)
3813
+ return found;
3814
+ }
3815
+ }
3816
+ return null;
3817
+ }
3818
+ // Generador de Jerarquía por Fechas
3819
+ generateDateHierarchy(startStr, endStr, type) {
3820
+ const start = new Date(startStr);
3821
+ const end = new Date(endStr);
3822
+ const tree = [];
3823
+ const monthNames = this.monthNames();
3824
+ if (type === 'YEAR_TRIMESTER_MONTH') {
3825
+ let current = new Date(start);
3826
+ while (current <= end) {
3827
+ const year = current.getFullYear();
3828
+ let yearNode = tree.find(n => n.id === `Y_${year}`);
3829
+ if (!yearNode) {
3830
+ yearNode = { id: `Y_${year}`, label: `${year}`, children: [], expanded: false };
3831
+ tree.push(yearNode);
3872
3832
  }
3873
- //Evalua Asignacion de Estilos
3874
- if (widgetConfig.widgetStyle == null || widgetConfig.widgetStyle == undefined) {
3875
- widgetConfig['widgetStyle'] = {};
3876
- widgetConfig['widgetStyle']['widgetBackgroundColor'] = prmDashboardStyle.widgetBackgroundColor;
3877
- widgetConfig['widgetStyle']['widgetBorderColor'] = prmDashboardStyle.widgetBorderColor;
3833
+ const month = current.getMonth();
3834
+ const qtrNum = Math.floor(month / 3) + 1;
3835
+ let qtrNode = yearNode.children.find(n => n.id === `Q_${year}_${qtrNum}`);
3836
+ if (!qtrNode) {
3837
+ qtrNode = { id: `Q_${year}_${qtrNum}`, label: `Qtr ${qtrNum}`, children: [], expanded: false };
3838
+ yearNode.children.push(qtrNode);
3878
3839
  }
3879
- this.eventAddWidget(widgetConfig, widgetConfig.controlConfig, controlData, widgetConfig.layoutConfig);
3880
- });
3840
+ const monthId = `M_${year}_${month + 1}`;
3841
+ if (!qtrNode.children.some(n => n.id === monthId)) {
3842
+ qtrNode.children.push({
3843
+ id: monthId,
3844
+ label: monthNames[month],
3845
+ value: new Date(year, month, 1) // Fecha inicial del mes
3846
+ });
3847
+ }
3848
+ current.setMonth(current.getMonth() + 1);
3849
+ }
3881
3850
  }
3851
+ if (type === 'YEAR_SEMESTER_MONTH') {
3852
+ let current = new Date(start);
3853
+ while (current <= end) {
3854
+ const year = current.getFullYear();
3855
+ let yearNode = tree.find(n => n.id === `Y_${year}`);
3856
+ if (!yearNode) {
3857
+ yearNode = { id: `Y_${year}`, label: `${year}`, children: [], expanded: false };
3858
+ tree.push(yearNode);
3859
+ }
3860
+ const month = current.getMonth();
3861
+ const qtrNum = Math.floor(month / 6) + 1;
3862
+ let qtrNode = yearNode.children.find(n => n.id === `S_${year}_${qtrNum}`);
3863
+ if (!qtrNode) {
3864
+ qtrNode = { id: `S_${year}_${qtrNum}`, label: `S ${qtrNum}`, children: [], expanded: false };
3865
+ yearNode.children.push(qtrNode);
3866
+ }
3867
+ const monthId = `M_${year}_${month + 1}`;
3868
+ if (!qtrNode.children.some(n => n.id === monthId)) {
3869
+ qtrNode.children.push({
3870
+ id: monthId,
3871
+ label: monthNames[month],
3872
+ value: new Date(year, month, 1) // Fecha inicial del mes
3873
+ });
3874
+ }
3875
+ current.setMonth(current.getMonth() + 1);
3876
+ }
3877
+ }
3878
+ return tree;
3882
3879
  }
3883
- // Guarda los elementos desde la configuración JSON
3884
- saveLayoutToJSON() {
3885
- const currentWidgets = this.grid.save(false);
3886
- this.dashboardItems = currentWidgets.map(w => ({
3887
- id: w.id,
3888
- x: w.x || 0,
3889
- y: w.y || 0,
3890
- w: w.w || 1,
3891
- h: w.h || 1,
3892
- content: w.content,
3893
- contentData: {}
3894
- }));
3895
- console.log('JSON Guardado:', JSON.stringify(this.dashboardItems));
3896
- return this.dashboardItems;
3897
- }
3898
- /*BUTTON ADD WIDGET*/
3899
- fn_btn_addWidget() {
3900
- this.countWidget++;
3901
- const newWidgetId = 'widget-' + this.countWidget;
3902
- let newItem = {
3903
- id: newWidgetId, autoPosition: true, w: 6, h: 3,
3904
- content: ''
3905
- };
3906
- //Datos de Widget y sus Contenidos
3907
- const widgetConfig = {
3908
- widgetId: newWidgetId,
3909
- title: '',
3910
- typeComponent: 'empty-visual',
3911
- showConfigButtons: true,
3912
- widgetStyle: DEFAULT_WIDGET_STYLE,
3913
- layoutConfig: {
3914
- id: '',
3915
- w: 0,
3916
- h: 0,
3917
- content: undefined
3918
- },
3919
- controlConfig: {},
3920
- controlTables: []
3880
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwListSlicerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3881
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwListSlicerComponent, isStandalone: true, selector: "yw-list-slicer", inputs: { idComponent: { classPropertyName: "idComponent", publicName: "idComponent", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, clearTitleLabel: { classPropertyName: "clearTitleLabel", publicName: "clearTitleLabel", isSignal: true, isRequired: false, transformFunction: null }, rawData: { classPropertyName: "rawData", publicName: "rawData", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateHierarchyType: { classPropertyName: "dateHierarchyType", publicName: "dateHierarchyType", isSignal: true, isRequired: false, transformFunction: null }, monthNames: { classPropertyName: "monthNames", publicName: "monthNames", isSignal: true, isRequired: false, transformFunction: null }, currentSelectedId: { classPropertyName: "currentSelectedId", publicName: "currentSelectedId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", filterValueChange: "filterValueChange" }, host: { properties: { "attr.id": "idComponent" } }, ngImport: i0, template: "<div class=\"filter-container\">\r\n <!-- Cabecera con t\u00EDtulo y acciones de filtro -->\r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\">\r\n <!--<button class=\"icon-btn\" title=\"Filtro\">\uD83D\uDD3B</button>-->\r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" title=\"{{clearTitleLabel}}\">\uD83E\uDDF9</button>\r\n <!--<button class=\"icon-btn\" title=\"M\u00E1s opciones\">\u22EE</button>-->\r\n </div>\r\n </div>\r\n\r\n <!-- Vista en \u00C1rbol con scroll interno -->\r\n <div class=\"tree-scroll-container\">\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: treeNodes(), level: 0 }\"></ng-container>\r\n </div>\r\n</div>\r\n\r\n<!-- Plantilla Recursiva para renderizar niveles del \u00C1rbol -->\r\n<ng-template #nodeListTemplate let-nodes let-level=\"level\">\r\n <div class=\"tree-level\">\r\n @for (node of nodes; track node.id) {\r\n <div class=\"tree-node-wrapper\">\r\n <!-- Fila de opci\u00F3n -->\r\n <div \r\n class=\"tree-node-row\" \r\n [class.selected]=\"selectedId() === node.id\"\r\n [style.padding-left.px]=\"level * 18 + 6\"\r\n (click)=\"selectNode(node, $event)\">\r\n \r\n <!-- Flecha expandir/colapsar (si tiene hijos) -->\r\n @if (node.children && node.children.length > 0) {\r\n <span class=\"chevron-icon\" (click)=\"toggleExpand(node, $event)\">\r\n {{ node.expanded ? '\uD83D\uDF83' : '\uD83D\uDF82' }}\r\n </span>\r\n } @else {\r\n <span class=\"chevron-spacer\"></span>\r\n }\r\n\r\n <!-- Radio Button (Selecci\u00F3n simple) -->\r\n <div class=\"radio-circle\" [class.checked]=\"selectedId() === node.id\">\r\n <div class=\"radio-inner\"></div>\r\n </div>\r\n\r\n <!-- Etiqueta del Nodo -->\r\n <span class=\"node-label\">{{ node.label }}</span>\r\n </div>\r\n\r\n <!-- Renderizado de hijos si est\u00E1 expandido -->\r\n @if (node.children && node.children.length > 0 && node.expanded) {\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: node.children, level: level + 1 }\"></ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;width:100%;max-width:320px;background:#fff}.filter-container{display:flex;flex-direction:column;border:1px solid #e0e0e0;border-radius:4px;background-color:#fff}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#252423}.header-actions{display:flex;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;font-size:12px;color:#605e5c;padding:2px 4px;border-radius:2px}.icon-btn:hover{background-color:#f3f2f1}.tree-scroll-container{max-height:220px;overflow-y:auto;padding:4px 0}.tree-level{display:flex;flex-direction:column}.tree-node-row{display:flex;align-items:center;height:28px;cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px;transition:background-color .1s ease}.tree-node-row:hover{background-color:#f3f2f1}.tree-node-row.selected{background-color:#e1dfdd}.chevron-icon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:10px;color:#323130}.chevron-spacer{width:16px}.radio-circle{width:14px;height:14px;border-radius:50%;border:1.5px solid #605e5c;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:#fff}.radio-circle.checked{border-color:#0078d4}.radio-circle.checked .radio-inner{width:6px;height:6px;border-radius:50%;background-color:#0078d4}.node-label{font-size:13px;color:#323130;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-scroll-container::-webkit-scrollbar{width:6px}.tree-scroll-container::-webkit-scrollbar-track{background:#f1f1f1}.tree-scroll-container::-webkit-scrollbar-thumb{background:#8a8886;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
3882
+ }
3883
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwListSlicerComponent, decorators: [{
3884
+ type: Component,
3885
+ args: [{ selector: 'yw-list-slicer', standalone: true, imports: [CommonModule], host: {
3886
+ '[attr.id]': 'idComponent' // Vincula el atributo id al valor de la propiedad
3887
+ }, template: "<div class=\"filter-container\">\r\n <!-- Cabecera con t\u00EDtulo y acciones de filtro -->\r\n <div class=\"filter-header\">\r\n <span class=\"filter-label\">{{ label() }}</span>\r\n <div class=\"header-actions\">\r\n <!--<button class=\"icon-btn\" title=\"Filtro\">\uD83D\uDD3B</button>-->\r\n <button class=\"icon-btn\" (click)=\"clearSelection()\" title=\"{{clearTitleLabel}}\">\uD83E\uDDF9</button>\r\n <!--<button class=\"icon-btn\" title=\"M\u00E1s opciones\">\u22EE</button>-->\r\n </div>\r\n </div>\r\n\r\n <!-- Vista en \u00C1rbol con scroll interno -->\r\n <div class=\"tree-scroll-container\">\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: treeNodes(), level: 0 }\"></ng-container>\r\n </div>\r\n</div>\r\n\r\n<!-- Plantilla Recursiva para renderizar niveles del \u00C1rbol -->\r\n<ng-template #nodeListTemplate let-nodes let-level=\"level\">\r\n <div class=\"tree-level\">\r\n @for (node of nodes; track node.id) {\r\n <div class=\"tree-node-wrapper\">\r\n <!-- Fila de opci\u00F3n -->\r\n <div \r\n class=\"tree-node-row\" \r\n [class.selected]=\"selectedId() === node.id\"\r\n [style.padding-left.px]=\"level * 18 + 6\"\r\n (click)=\"selectNode(node, $event)\">\r\n \r\n <!-- Flecha expandir/colapsar (si tiene hijos) -->\r\n @if (node.children && node.children.length > 0) {\r\n <span class=\"chevron-icon\" (click)=\"toggleExpand(node, $event)\">\r\n {{ node.expanded ? '\uD83D\uDF83' : '\uD83D\uDF82' }}\r\n </span>\r\n } @else {\r\n <span class=\"chevron-spacer\"></span>\r\n }\r\n\r\n <!-- Radio Button (Selecci\u00F3n simple) -->\r\n <div class=\"radio-circle\" [class.checked]=\"selectedId() === node.id\">\r\n <div class=\"radio-inner\"></div>\r\n </div>\r\n\r\n <!-- Etiqueta del Nodo -->\r\n <span class=\"node-label\">{{ node.label }}</span>\r\n </div>\r\n\r\n <!-- Renderizado de hijos si est\u00E1 expandido -->\r\n @if (node.children && node.children.length > 0 && node.expanded) {\r\n <ng-container *ngTemplateOutlet=\"nodeListTemplate; context: { $implicit: node.children, level: level + 1 }\"></ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:block;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;width:100%;max-width:320px;background:#fff}.filter-container{display:flex;flex-direction:column;border:1px solid #e0e0e0;border-radius:4px;background-color:#fff}.filter-header{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #f0f0f0}.filter-label{font-size:13px;font-weight:600;color:#252423}.header-actions{display:flex;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;font-size:12px;color:#605e5c;padding:2px 4px;border-radius:2px}.icon-btn:hover{background-color:#f3f2f1}.tree-scroll-container{max-height:220px;overflow-y:auto;padding:4px 0}.tree-level{display:flex;flex-direction:column}.tree-node-row{display:flex;align-items:center;height:28px;cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px;transition:background-color .1s ease}.tree-node-row:hover{background-color:#f3f2f1}.tree-node-row.selected{background-color:#e1dfdd}.chevron-icon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:10px;color:#323130}.chevron-spacer{width:16px}.radio-circle{width:14px;height:14px;border-radius:50%;border:1.5px solid #605e5c;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:#fff}.radio-circle.checked{border-color:#0078d4}.radio-circle.checked .radio-inner{width:6px;height:6px;border-radius:50%;background-color:#0078d4}.node-label{font-size:13px;color:#323130;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-scroll-container::-webkit-scrollbar{width:6px}.tree-scroll-container::-webkit-scrollbar-track{background:#f1f1f1}.tree-scroll-container::-webkit-scrollbar-thumb{background:#8a8886;border-radius:3px}\n"] }]
3888
+ }], ctorParameters: () => [], propDecorators: { idComponent: [{
3889
+ type: Input
3890
+ }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], clearTitleLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearTitleLabel", required: false }] }], rawData: [{ type: i0.Input, args: [{ isSignal: true, alias: "rawData", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateHierarchyType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateHierarchyType", required: false }] }], monthNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthNames", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filterValueChange: [{ type: i0.Output, args: ["filterValueChange"] }], currentSelectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentSelectedId", required: false }] }] } });
3891
+
3892
+ class YwDashboardFilterWidgetComponent {
3893
+ constructor() {
3894
+ this.componentWidgetId = '';
3895
+ this.typeComponent = '';
3896
+ this.varShowConfigButtons = false;
3897
+ this.configWidget = {
3898
+ widgetId: '',
3899
+ label: '',
3900
+ typeComponent: '',
3901
+ controlTable: '',
3921
3902
  };
3922
- const controlConfig = {};
3923
- const controlData = {};
3924
- this.eventAddWidget(widgetConfig, controlConfig, controlData, newItem);
3925
- }
3926
- /*BUTTON DELETE WIDGET*/
3927
- fn_btn_deleteWidget() {
3928
- const newWidgetId = 'widget-' + this.countWidget;
3929
- this.eventRemoveWidgetById(newWidgetId);
3930
- this.countWidget--;
3903
+ this.controlConfig = input(...(ngDevMode ? [undefined, { debugName: "controlConfig" }] : []));
3904
+ this.controlData = [];
3905
+ this.eventDeleteRequest = output();
3906
+ this.eventShowConfigFormRequest = output();
3907
+ this.selectionChange = output();
3908
+ effect(() => {
3909
+ const value_controlConfig = this.fnFormatConfigData();
3910
+ this.dataWidget = { idComponent: this.componentWidgetId,
3911
+ configWidget: this.configWidget,
3912
+ controlConfig: value_controlConfig
3913
+ };
3914
+ });
3931
3915
  }
3932
- /*BUTTON BLOCK WIDGETS*/
3933
- fn_btn_lockPositionWidgets() {
3934
- this.isLocked = !this.isLocked;
3935
- const shouldLock = !this.allIsLocked();
3936
- this.allIsLocked.set(shouldLock);
3937
- this.fn_lockPositionWidgets(shouldLock);
3938
- console.log(`All widgets have been visually and functionally locked: ${shouldLock}`);
3916
+ ngOnInit() {
3917
+ this.componentWidgetId = this.configWidget.widgetId;
3918
+ this.typeComponent = this.configWidget.typeComponent;
3919
+ const value_controlConfig = this.fnFormatConfigData();
3920
+ this.dataWidget = { idComponent: this.componentWidgetId,
3921
+ configWidget: this.configWidget,
3922
+ controlConfig: value_controlConfig
3923
+ };
3939
3924
  }
3940
- fn_lockPositionWidgets(prmAllIsLocked) {
3941
- const shouldLock = prmAllIsLocked;
3942
- // 1. Perform the functional Gridstack task on the core grid layout
3943
- this.grid.movable('.grid-stack-item', !shouldLock);
3944
- this.grid.resizable('.grid-stack-item', !shouldLock);
3945
- // 2. Broadcast the state change down to every active dynamic child component
3946
- this.widgetsActivos.forEach((componentRef) => {
3947
- componentRef.setInput('isLocked', shouldLock);
3948
- });
3949
- console.log(`All widgets have been visually and functionally locked - por TS Config: ${shouldLock}`);
3925
+ /*
3926
+ Los Datos a este componente llegan a un nivel basico, sin embargo es necesario configurar la informacion, para que se adapte a los componentes.
3927
+ */
3928
+ fnFormatConfigData() {
3929
+ let responsedata = {};
3930
+ const typeComponent = this.configWidget.typeComponent;
3931
+ if (typeComponent == 'filter-slicer') {
3932
+ const dataType = this.controlConfig().dataType;
3933
+ const currentData = this.controlConfig();
3934
+ if (dataType == 'date') {
3935
+ const fieldName = this.controlConfig().filterField;
3936
+ const fechas = this.controlData.map((d) => new Date(d[fieldName]).getTime());
3937
+ const val_min = new Date(Math.min(...fechas)).toISOString().split("T")[0];
3938
+ const val_max = new Date(Math.max(...fechas)).toISOString().split("T")[0];
3939
+ responsedata = { dataType: dataType, min: val_min, max: val_max, minCurrent: currentData?.minCurrent, maxCurrent: currentData?.maxCurrent };
3940
+ }
3941
+ if (dataType == 'number') {
3942
+ const fieldName = this.controlConfig().filterField;
3943
+ const valStep = this.controlConfig().step;
3944
+ const DataNumbers = this.controlData.map((d) => d[fieldName]);
3945
+ const val_min = Math.min(...DataNumbers);
3946
+ const val_max = Math.max(...DataNumbers);
3947
+ responsedata = { dataType: dataType, min: val_min, max: val_max, step: valStep, minCurrent: currentData?.minCurrent, maxCurrent: currentData?.maxCurrent };
3948
+ }
3949
+ }
3950
+ if (typeComponent == 'filter-button') {
3951
+ const fieldName = this.controlConfig().filterField;
3952
+ const fieldNameId = this.controlConfig().filterFieldId;
3953
+ const val_allowMultiple = this.controlConfig().allowMultiple;
3954
+ const currentData = this.controlConfig();
3955
+ let listoptions = this.controlData.map((item) => { return { 'id': item[fieldNameId], 'label': item[fieldName] }; });
3956
+ const listoptionsunicos = [...new Map(listoptions.map(d => [d.id, d])).values()];
3957
+ responsedata = { options: listoptionsunicos, allowMultiple: val_allowMultiple, currentIds: currentData?.currentIds };
3958
+ }
3959
+ if (typeComponent == 'filter-list') {
3960
+ const dataType = this.controlConfig().dataType;
3961
+ const currentData = this.controlConfig();
3962
+ if (dataType == 'date') {
3963
+ const fieldName = this.controlConfig().filterField;
3964
+ const val_dateHierarchyType = this.controlConfig().dateHierarchyType;
3965
+ const fechas = this.controlData.map((d) => new Date(d[fieldName]).getTime());
3966
+ const val_min = new Date(Math.min(...fechas)).toISOString().split("T")[0];
3967
+ const val_max = new Date(Math.max(...fechas)).toISOString().split("T")[0];
3968
+ responsedata = { dataType: dataType, minDate: val_min, maxDate: val_max, dateHierarchyType: val_dateHierarchyType, currentSelectedId: currentData.currentSelectedId };
3969
+ }
3970
+ if (dataType == 'text') {
3971
+ const fieldName1 = this.controlConfig().filterField;
3972
+ const fieldName2 = this.controlConfig().filterFieldN2;
3973
+ const fieldName3 = this.controlConfig().filterFieldN3;
3974
+ let foundN2 = false;
3975
+ let foundN3 = false;
3976
+ let lsizeN2 = 0;
3977
+ let lsizeN3 = 0;
3978
+ let nodebase = { id: '', label: '' };
3979
+ let nodeN2 = { id: '', label: '' };
3980
+ let nodeN3 = { id: '', label: '' };
3981
+ let listNivel1 = [];
3982
+ let listNivel2 = [];
3983
+ let listNivel3 = [];
3984
+ this.controlData.forEach((item) => {
3985
+ nodebase = { id: item[fieldName1], label: item[fieldName1] };
3986
+ //NIVEL 2
3987
+ if (fieldName2) {
3988
+ foundN2 = false;
3989
+ lsizeN2 = Object.keys(listNivel2).length;
3990
+ for (let i = 0; i < lsizeN2; i++) {
3991
+ const itemN2 = listNivel2[i];
3992
+ if (itemN2.id == item[fieldName2]) {
3993
+ itemN2.children.push(nodebase);
3994
+ nodeN2 = itemN2;
3995
+ foundN2 = true;
3996
+ break;
3997
+ }
3998
+ }
3999
+ ;
4000
+ if (foundN2 == false) {
4001
+ nodeN2 = { id: item[fieldName2], label: item[fieldName2], expanded: true, children: [nodebase] };
4002
+ if (fieldName3) {
4003
+ nodeN2.parentId = item[fieldName3];
4004
+ }
4005
+ listNivel2.push(nodeN2);
4006
+ }
4007
+ }
4008
+ else {
4009
+ listNivel1.push(nodebase);
4010
+ }
4011
+ });
4012
+ //NIVEL 1 - Basico no hay Jerarquia
4013
+ responsedata = { dataType: dataType, rawData: listNivel1, currentSelectedId: currentData.currentSelectedId };
4014
+ //NIVEL 2 - Jerarquia de 2 niveles
4015
+ if (fieldName2) {
4016
+ responsedata = { dataType: dataType, rawData: listNivel2, currentSelectedId: currentData.currentSelectedId };
4017
+ }
4018
+ //NIVEL 3 - Jerarquia de 3 niveles
4019
+ if (fieldName3) {
4020
+ listNivel2.forEach((item) => {
4021
+ nodebase = item;
4022
+ foundN3 = false;
4023
+ lsizeN3 = Object.keys(listNivel3).length;
4024
+ for (let i = 0; i < lsizeN3; i++) {
4025
+ const itemN3 = listNivel3[i];
4026
+ if (itemN3.id == item.parentId) {
4027
+ itemN3.children.push(nodebase);
4028
+ foundN3 = true;
4029
+ break;
4030
+ }
4031
+ }
4032
+ ;
4033
+ if (foundN3 == false) {
4034
+ nodeN3 = { id: item[fieldName3], label: item[fieldName3], expanded: true, children: [nodebase] };
4035
+ listNivel3.push(nodeN3);
4036
+ }
4037
+ });
4038
+ responsedata = { dataType: dataType, rawData: listNivel3, currentSelectedId: currentData.currentSelectedId };
4039
+ }
4040
+ }
4041
+ }
4042
+ return responsedata;
3950
4043
  }
3951
- /*BUTTON UPDATE WIDGET - CONTROL CONFIG */
3952
- fn_btn_updateWidgetControlConfig(idWidget) {
3953
- let newControlConfig = {};
3954
- /*MODIFICAR METODO PARA CARGAR INFO DEL FORMULARIO*/
3955
- newControlConfig['provider'] = 'syncfusion';
3956
- this.eventUpdateWidgetControlConfig(idWidget, newControlConfig);
4044
+ //Filtro Slicer
4045
+ onFilterSlicerChange(range) {
4046
+ console.log('Filtro actualizado:', range);
4047
+ this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: range });
3957
4048
  }
3958
- /*BUTTON UPDATE WIDGET - CONTROL CONFIG */
3959
- fn_btn_updateWidgetControlData(idWidget) {
3960
- let newControlData = [];
3961
- /*MODIFICAR METODO PARA CARGAR INFO DEL FORMULARIO*/
3962
- newControlData = [
3963
- { month: 'Ene', sales: 35, expenses: 28 },
3964
- { month: 'Feb', sales: 28, expenses: 22 },
3965
- { month: 'Mar', sales: 34, expenses: 25 },
3966
- { month: 'Abr', sales: 32, expenses: 27 },
3967
- { month: 'May', sales: 40, expenses: 30 },
3968
- { month: 'Jun', sales: 32, expenses: 26 },
3969
- { month: 'Jul', sales: 45, expenses: 32 },
3970
- { month: 'Ago', sales: 38, expenses: 29 },
3971
- { month: 'Sep', sales: 42, expenses: 31 },
3972
- { month: 'Oct', sales: 48, expenses: 35 },
3973
- { month: 'Nov', sales: 52, expenses: 38 },
3974
- { month: 'Dic', sales: 58, expenses: 42 },
3975
- ];
3976
- this.eventUpdateWidgetControlData(idWidget, newControlData);
4049
+ //Filtro Button
4050
+ onFilterButtonChange(selectedIds) {
4051
+ console.log('Tipos seleccionados:', selectedIds);
4052
+ this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: selectedIds });
3977
4053
  }
3978
- /****************************************************** */
3979
- /*AGREGAR WIDGET*/
3980
- eventAddWidget(widgetConfig, controlConfig, controlData, item) {
3981
- // 1. Agregar el contenedor del widget a Gridstack
3982
- const el = this.grid.addWidget(item);
3983
- // 2. Obtener el contenedor interno generado por Gridstack
3984
- const contentElement = el.querySelector('.grid-stack-item-content');
3985
- if (contentElement) {
3986
- // 3. Crear dinámicamente la instancia del componente Angular
3987
- const componentRef = createComponent(YwDashboardWidgetComponent, {
3988
- environmentInjector: this.environmentInjector, // Requerido para resolver dependencias
3989
- hostElement: contentElement // Renderiza directamente aquí dentro
3990
- });
3991
- // 4. Pasar las propiedades/Inputs al componente creado
3992
- //Evaluar Estilos
3993
- componentRef.setInput('styleWidget', widgetConfig.widgetStyle);
3994
- //componentRef.instance.configWidget = widgetConfig;
3995
- componentRef.setInput('configWidget', widgetConfig);
3996
- componentRef.setInput('controlConfig', controlConfig);
3997
- componentRef.setInput('controlData', controlData);
3998
- /* BEGIN - Eventos que provienen desde widget */
3999
- componentRef.instance.eventDeleteRequest.subscribe((idWidget) => {
4000
- // Ejecutamos la función de borrado que ya programamos en el paso anterior
4001
- this.eventRemoveWidgetById(idWidget);
4002
- });
4003
- componentRef.instance.eventLockToggleRequest.subscribe((event) => {
4004
- this.eventToggleLockWidget(event.widgetId, event.shouldLock);
4005
- });
4006
- componentRef.instance.eventMaximizeToggleRequest.subscribe((event) => {
4007
- this.eventToggleMaximizeWidget(event.widgetId, event.shouldMaximize);
4008
- });
4009
- //Mostrar
4010
- componentRef.instance.eventShowConfigFormRequest.subscribe((idWidget) => {
4011
- this.eventShowFormConfigWidget(idWidget);
4012
- });
4013
- /* END - Eventos que provienen desde widget */
4014
- // 5. Mover los nodos del DOM generados por Angular dentro del DIV del Widget
4015
- this.appRef.attachView(componentRef.hostView);
4016
- // Guardar la referencia de Componentes para hacer operaciones con estas.
4017
- this.widgetsActivos.set(item.id, componentRef);
4018
- //El componente interno no aparece bien dimensionado, con esto disparamos el evento para que se ajuste a las dimenciones correctas
4019
- setTimeout(() => {
4020
- window.dispatchEvent(new Event('resize'));
4021
- }, 50);
4022
- }
4054
+ //Filtro Lista - 1
4055
+ onFilterListChange(selectedData) {
4056
+ console.log('Tipos seleccionados:', selectedData);
4057
+ // Filtrar la información del dashboard
4058
+ //this.selectionChange.emit({widgetId:this.componentWidgetId,eventResponse:selectedData});
4023
4059
  }
4024
- /*UPDATE CONTROLCONFIG WIDGET*/
4025
- eventUpdateWidgetControlConfig(idWidget, newControlConfig) {
4026
- //Obtener la referencia del componente almacenada en el Map
4027
- const componentRef = this.widgetsActivos.get(idWidget);
4028
- if (!componentRef) {
4029
- console.warn(`No se encontró un widget activo con el ID: ${idWidget}`);
4060
+ //Filtro Lista - 2
4061
+ onFilterListCalculated(data) {
4062
+ if (!data) {
4063
+ this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: null });
4030
4064
  return;
4031
4065
  }
4032
- //Actualizar el Input usando la API oficial setInput
4033
- componentRef.setInput('controlConfig', newControlConfig);
4034
- // NOTA: setInput dispara automáticamente la detección de cambios (ChangeDetectionStrategy.OnPush).
4035
- // Si deseas asegurarte manualmente, puedes forzar un render:
4036
- // componentRef.changeDetectorRef.detectChanges();
4037
- }
4038
- /*UPDATE CONTROL DATA WIDGET*/
4039
- eventUpdateWidgetControlData(idWidget, newControlData) {
4040
- //Obtener la referencia del componente almacenada en el Map
4041
- const componentRef = this.widgetsActivos.get(idWidget);
4042
- if (!componentRef) {
4043
- console.warn(`No se encontró un widget activo con el ID: ${idWidget}`);
4044
- return;
4066
+ if (data.type === 'DATE_RANGE') {
4067
+ //console.log('Rango a filtrar en SQL/Dashboard:', data.dateRange?.minDate, 'hasta', data.dateRange?.maxDate);
4068
+ this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: { minDate: data.dateRange?.minDate, maxDate: data.dateRange?.maxDate, currentNodeId: data.currentNodeId } });
4069
+ }
4070
+ else if (data.type === 'TEXT_LEAVES') {
4071
+ //console.log('Lista de elementos terminales seleccionados:', data.leafIds);
4072
+ this.selectionChange.emit({ widgetId: this.componentWidgetId, eventResponse: { ids: data.leafIds, currentNodeId: data.currentNodeId } });
4045
4073
  }
4046
- //Actualizar el Input usando la API oficial setInput
4047
- componentRef.setInput('controlData', newControlData);
4048
- // NOTA: setInput dispara automáticamente la detección de cambios (ChangeDetectionStrategy.OnPush).
4049
- // Si deseas asegurarte manualmente, puedes forzar un render:
4050
- componentRef.changeDetectorRef.detectChanges();
4051
- }
4052
- /****************************************************** */
4053
- /* BEGIN - DRAWER */
4054
- handleShowFormFilterDrawer() {
4055
- this.drawerfiltervisible = true;
4056
4074
  }
4057
- handleCloseFormFilterDrawer() {
4058
- this.drawerfiltervisible = false;
4075
+ onDeleteClick() {
4059
4076
  }
4060
- handleCloseFormDrawer() {
4061
- this.drawerconfigvisible = false;
4077
+ onShowFormConfigClick() {
4062
4078
  }
4063
- fnGetDrawerWidth(prm_percent) {
4064
- const curWidth = window.innerWidth;
4065
- let newWidth = curWidth;
4066
- if (prm_percent) {
4067
- newWidth = Math.floor(curWidth * prm_percent / 100);
4068
- }
4069
- if (newWidth < 400) {
4070
- newWidth = curWidth;
4071
- }
4072
- return newWidth;
4079
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4080
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardFilterWidgetComponent, isStandalone: true, selector: "yw-dashboard-filter-widget", inputs: { configWidget: { classPropertyName: "configWidget", publicName: "configWidget", isSignal: false, isRequired: false, transformFunction: null }, controlConfig: { classPropertyName: "controlConfig", publicName: "controlConfig", isSignal: true, isRequired: false, transformFunction: null }, controlData: { classPropertyName: "controlData", publicName: "controlData", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { eventDeleteRequest: "eventDeleteRequest", eventShowConfigFormRequest: "eventShowConfigFormRequest", selectionChange: "selectionChange" }, ngImport: i0, template: "\r\n<div class=\"widget-filter-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div>\r\n<div class=\"widget-filter-content-wrapper\">\r\n @if ( typeComponent == 'empty-visual' ) { \r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-slicer' ) { \r\n <ng-container *ngTemplateOutlet=\"filterslicer; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-button' ) { \r\n <ng-container *ngTemplateOutlet=\"filterbutton; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-list' ) { \r\n <ng-container *ngTemplateOutlet=\"filterlist; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER SLICER TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterslicer let-control = \"control\"> \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\" \r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\" \r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n @if (control.controlConfig.dataType === 'number') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\"\r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\"\r\n [step]=\"control.controlConfig.step\"\r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER BUTTON TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterbutton let-control = \"control\"> \r\n <yw-button-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [options]=\"control.controlConfig.options\"\r\n [allowMultiple]=\"control.controlConfig.allowMultiple\"\r\n [currentIds]=\"control.controlConfig.currentIds\"\r\n (selectionChange)=\"onFilterButtonChange($event)\" \r\n >\r\n </yw-button-slicer>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER LIST TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterlist let-control = \"control\"> \r\n \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [minDate]=\"control.controlConfig.minDate\"\r\n [maxDate]=\"control.controlConfig.maxDate\"\r\n [dateHierarchyType]=\"control.controlConfig.dateHierarchyType\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n @if (control.controlConfig.dataType === 'text') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\"\r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [rawData]=\"control.controlConfig.rawData\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #040404);border-radius:var(--widget-border-radius, 8px);padding:var(--widget-padding, 10px)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-filter-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-filter-content-wrapper{width:100%;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: YwEmptyVisualComponent, selector: "yw-empty-visual", inputs: ["idComponent", "config"] }, { kind: "component", type: YwBaseSlicerComponent, selector: "yw-base-slicer", inputs: ["idComponent", "dataType", "label", "min", "max", "step", "minCurrent", "maxCurrent"], outputs: ["rangeChange"] }, { kind: "component", type: YwButtonSlicerComponent, selector: "yw-button-slicer", inputs: ["idComponent", "label", "options", "allowMultiple", "currentIds"], outputs: ["selectionChange"] }, { kind: "component", type: YwListSlicerComponent, selector: "yw-list-slicer", inputs: ["idComponent", "label", "clearTitleLabel", "rawData", "dataType", "minDate", "maxDate", "dateHierarchyType", "monthNames", "currentSelectedId"], outputs: ["selectionChange", "filterValueChange"] }] }); }
4081
+ }
4082
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterWidgetComponent, decorators: [{
4083
+ type: Component,
4084
+ args: [{ selector: 'yw-dashboard-filter-widget', standalone: true, imports: [CommonModule,
4085
+ YwEmptyVisualComponent,
4086
+ YwBaseSlicerComponent,
4087
+ YwButtonSlicerComponent,
4088
+ YwListSlicerComponent,
4089
+ ], template: "\r\n<div class=\"widget-filter-action-bar\"> \r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-delete\" (click)=\"onDeleteClick()\">\uD83D\uDDD1\uFE0F</button>\r\n <button [hidden]=\"!varShowConfigButtons\" class=\"btn-config\" (click)=\"onShowFormConfigClick()\">\u2699\uFE0F</button>\r\n</div>\r\n<div class=\"widget-filter-content-wrapper\">\r\n @if ( typeComponent == 'empty-visual' ) { \r\n <ng-container *ngTemplateOutlet=\"emptyvisual; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-slicer' ) { \r\n <ng-container *ngTemplateOutlet=\"filterslicer; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-button' ) { \r\n <ng-container *ngTemplateOutlet=\"filterbutton; context: { control: dataWidget }\"></ng-container>\r\n }\r\n @if ( typeComponent == 'filter-list' ) { \r\n <ng-container *ngTemplateOutlet=\"filterlist; context: { control: dataWidget }\"></ng-container>\r\n } \r\n \r\n</div>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- EMPTY VISUAL TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #emptyvisual let-control = \"control\">\r\n <yw-empty-visual [idComponent]=\"control.idComponent\"\r\n [config]=\"control.controlConfig\" \r\n > \r\n </yw-empty-visual>\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER SLICER TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterslicer let-control = \"control\"> \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\" \r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\" \r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n @if (control.controlConfig.dataType === 'number') { \r\n <yw-base-slicer [idComponent]=\"control.idComponent\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [label]=\"control.configWidget.label\"\r\n [min]=\"control.controlConfig.min\"\r\n [max]=\"control.controlConfig.max\"\r\n [minCurrent]=\"control.controlConfig.minCurrent\"\r\n [maxCurrent]=\"control.controlConfig.maxCurrent\"\r\n [step]=\"control.controlConfig.step\"\r\n (rangeChange)=\"onFilterSlicerChange($event)\"\r\n >\r\n </yw-base-slicer>\r\n }\r\n</ng-template>\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER BUTTON TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterbutton let-control = \"control\"> \r\n <yw-button-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [options]=\"control.controlConfig.options\"\r\n [allowMultiple]=\"control.controlConfig.allowMultiple\"\r\n [currentIds]=\"control.controlConfig.currentIds\"\r\n (selectionChange)=\"onFilterButtonChange($event)\" \r\n >\r\n </yw-button-slicer>\r\n</ng-template>\r\n\r\n\r\n<!--------------------------------------------------------------->\r\n<!---------------------- FILTER LIST TEMPLATE ---------------------->\r\n<!--------------------------------------------------------------->\r\n<ng-template #filterlist let-control = \"control\"> \r\n \r\n @if (control.controlConfig.dataType === 'date') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\" \r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [minDate]=\"control.controlConfig.minDate\"\r\n [maxDate]=\"control.controlConfig.maxDate\"\r\n [dateHierarchyType]=\"control.controlConfig.dateHierarchyType\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n @if (control.controlConfig.dataType === 'text') { \r\n <yw-list-slicer [idComponent]=\"control.idComponent\"\r\n [label]=\"control.configWidget.label\"\r\n [dataType]=\"control.controlConfig.dataType\"\r\n [rawData]=\"control.controlConfig.rawData\"\r\n [currentSelectedId]=\"control.controlConfig.currentSelectedId\"\r\n (selectionChange)=\"onFilterListChange($event)\"\r\n (filterValueChange)=\"onFilterListCalculated($event)\" \r\n >\r\n </yw-list-slicer>\r\n }\r\n\r\n\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;box-sizing:border-box;background-color:var(--widget-bg, #ffffff);border:1px solid var(--widget-border-color, #040404);border-radius:var(--widget-border-radius, 8px);padding:var(--widget-padding, 10px)}:host *,:host *:before,:host *:after{box-sizing:border-box}.widget-filter-action-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%}.widget-filter-content-wrapper{width:100%;display:flex;flex-direction:column}\n"] }]
4090
+ }], ctorParameters: () => [], propDecorators: { configWidget: [{
4091
+ type: Input
4092
+ }], controlConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlConfig", required: false }] }], controlData: [{
4093
+ type: Input
4094
+ }], eventDeleteRequest: [{ type: i0.Output, args: ["eventDeleteRequest"] }], eventShowConfigFormRequest: [{ type: i0.Output, args: ["eventShowConfigFormRequest"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
4095
+
4096
+ class YwDashboardFilterComponent {
4097
+ constructor() {
4098
+ this.dashboardFilterConfig = {
4099
+ showConfigButtons: false,
4100
+ lockPositionWidgets: false,
4101
+ controlListConfig: []
4102
+ };
4103
+ this.dashboardData = [];
4104
+ this.filterSelectionChange = output();
4105
+ this.controlListConfig = [];
4073
4106
  }
4074
- onFilterSelectionChange(eventData) {
4075
- const lsize = this.widgetsActivos.size;
4076
- //Validar que ya se crearon y renderizaron los componentes graficos.
4077
- if (lsize > 0) {
4078
- const currentFilterController = this.dashboardFilterConfig.controlListConfig.find((x) => x.widgetId == eventData['widgetId']);
4079
- if (currentFilterController) {
4080
- currentFilterController.controlFilterData = eventData['eventResponse'];
4081
- this.fnUpdateDataControllerBasedOnFilter(this.dashboardFilterConfig.controlListConfig);
4107
+ ngOnInit() {
4108
+ this.controlListConfig = [];
4109
+ this.dashboardFilterConfig.controlListConfig.forEach((item) => {
4110
+ const tableName = item.controlTable;
4111
+ const prmcontrolData = this.fn_getDataForWidgetFilterComponent(tableName, this.dashboardData);
4112
+ const prmconfigWidget = { ...item };
4113
+ if (item.controlFilterData) {
4114
+ item.configComponent = this.fnSetControlFilterCurrentValues(prmconfigWidget);
4082
4115
  }
4083
- }
4116
+ const configControl = { configWidget: prmconfigWidget,
4117
+ controlData: prmcontrolData,
4118
+ controlConfig: item.configComponent
4119
+ };
4120
+ this.controlListConfig.push(configControl);
4121
+ });
4084
4122
  }
4085
- fnUpdateDataControllerBasedOnFilter(filterControlListConfig) {
4086
- let copyDashboardData = structuredClone(this.dashboardData);
4087
- filterControlListConfig.forEach((item) => {
4088
- let tableSelected = copyDashboardData.find((x) => x.tableName == item.controlTable);
4089
- if (item.typeComponent == 'filter-slicer' && tableSelected && item.controlFilterData) {
4090
- const fieldName = item.configComponent.filterField;
4091
- if (item.configComponent.dataType == 'date') {
4092
- const minDate = item.controlFilterData['startDate'];
4093
- const maxDate = item.controlFilterData['endDate'];
4094
- if (minDate && maxDate) {
4095
- const filterData = tableSelected.tableData.filter((y) => (minDate <= y[fieldName] && y[fieldName] <= maxDate));
4096
- tableSelected.tableData = filterData;
4097
- }
4098
- }
4099
- if (item.configComponent.dataType == 'number') {
4100
- const minNum = Number(item.controlFilterData['minValue']);
4101
- const maxNum = Number(item.controlFilterData['maxValue']);
4102
- if (minNum && maxNum) {
4103
- const filterData = tableSelected.tableData.filter((y) => (minNum <= y[fieldName] && y[fieldName] <= maxNum));
4104
- tableSelected.tableData = filterData;
4105
- }
4106
- }
4107
- }
4108
- if (item.typeComponent == 'filter-button' && tableSelected && item.controlFilterData) {
4109
- const fieldName = item.configComponent.filterField;
4110
- const listValues = item.controlFilterData || [];
4111
- if (listValues && listValues.length > 0) {
4112
- const filterData = tableSelected.tableData.filter((y) => (listValues.includes(y[fieldName])));
4113
- tableSelected.tableData = filterData;
4114
- }
4115
- }
4116
- if (item.typeComponent == 'filter-list' && tableSelected && item.controlFilterData) {
4117
- const fieldName = item.configComponent.filterField;
4118
- if (item.configComponent.dataType == 'text') {
4119
- const listValues = item.controlFilterData['ids'] || [];
4120
- if (listValues && listValues.length > 0) {
4121
- const listValues1D = listValues;
4122
- const filterData = tableSelected.tableData.filter((y) => (listValues1D.includes(y[fieldName])));
4123
- tableSelected.tableData = filterData;
4124
- }
4125
- }
4126
- if (item.configComponent.dataType == 'date') {
4127
- const minDate = item.controlFilterData['minDate'];
4128
- const maxDate = item.controlFilterData['maxDate'];
4129
- if (minDate && maxDate) {
4130
- const filterData = tableSelected.tableData.filter((y) => (minDate <= y[fieldName] && y[fieldName] <= maxDate));
4131
- tableSelected.tableData = filterData;
4132
- }
4133
- }
4123
+ //Filtrar Datos por Componente del Widget
4124
+ fn_getDataForWidgetFilterComponent(prmTableName, prmdashboardData) {
4125
+ let dataresponse = [];
4126
+ const ldsize = prmdashboardData.length;
4127
+ prmdashboardData.forEach((curTableRef) => {
4128
+ if (prmTableName.localeCompare(curTableRef.tableName) == 0) {
4129
+ dataresponse = curTableRef.tableData;
4134
4130
  }
4135
4131
  });
4136
- //Actualizacion de Data Componentes Graficos
4137
- this.dashboardConfig.controlListConfig.forEach((item) => {
4138
- const controlData = this.fn_getDataForWidgetComponent(item.controlTables, copyDashboardData);
4139
- this.eventUpdateWidgetControlData(item.widgetId, controlData);
4140
- });
4132
+ return dataresponse;
4141
4133
  }
4142
- /* END - DRAWER */
4143
- /* BEGIN - Eventos que provienen desde widget */
4144
- eventRemoveWidgetById(widgetId) {
4145
- // 1. Buscar el elemento HTML físico dentro de Gridstack usando su atributo interno [gs-id]
4146
- const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4147
- if (elementoHtml) {
4148
- // 2. Remover el elemento de Gridstack (esto limpia el HTML y reordena el tablero si float es false)
4149
- // El parámetro false indica que remueva el nodo del DOM pero no dispare eventos internos destructivos pesados
4150
- this.grid.removeWidget(elementoHtml, false);
4134
+ onFilterChange(eventData) {
4135
+ this.filterSelectionChange.emit({ widgetId: eventData.widgetId, eventResponse: eventData.eventResponse });
4136
+ }
4137
+ fnSetControlFilterCurrentValues(prmControlConfig) {
4138
+ if (prmControlConfig.typeComponent == 'filter-slicer') {
4139
+ let curConfigComponent = prmControlConfig.configComponent;
4140
+ if (prmControlConfig.configComponent.dataType == 'date') {
4141
+ curConfigComponent.minCurrent = prmControlConfig.controlFilterData['startDate'];
4142
+ curConfigComponent.maxCurrent = prmControlConfig.controlFilterData['endDate'];
4143
+ }
4144
+ if (prmControlConfig.configComponent.dataType == 'number') {
4145
+ curConfigComponent.minCurrent = prmControlConfig.controlFilterData['minValue'];
4146
+ curConfigComponent.maxCurrent = prmControlConfig.controlFilterData['maxValue'];
4147
+ }
4148
+ return curConfigComponent;
4151
4149
  }
4152
- // 3. Buscar y destruir la instancia de Angular en memoria RAM
4153
- const componentRef = this.widgetsActivos.get(widgetId);
4154
- if (componentRef) {
4155
- // Desengancha la vista del ciclo de renderizado global de Angular
4156
- this.appRef.detachView(componentRef.hostView);
4157
- // Destruye el componente y libera la memoria RAM
4158
- componentRef.destroy();
4159
- // Eliminamos el registro de nuestro Mapa
4160
- this.widgetsActivos.delete(widgetId);
4150
+ if (prmControlConfig.typeComponent == 'filter-button') {
4151
+ let curConfigComponent = prmControlConfig.configComponent;
4152
+ curConfigComponent.currentIds = prmControlConfig.controlFilterData || [];
4153
+ return curConfigComponent;
4161
4154
  }
4162
- //console.log(`Widget ${widgetId} eliminado con éxito de Gridstack y Angular.`);
4163
- }
4164
- eventToggleLockWidget(widgetId, shouldLock) {
4165
- const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4166
- if (elementoHtml) {
4167
- // La API de Gridstack nos permite congelar un nodo específico pasándole true/false
4168
- this.grid.movable(elementoHtml, !shouldLock);
4169
- this.grid.resizable(elementoHtml, !shouldLock);
4170
- //console.log(`Widget ${widgetId} cambiado a estado bloqueado: ${shouldLock}`);
4155
+ if (prmControlConfig.typeComponent == 'filter-list') {
4156
+ let curConfigComponent = prmControlConfig.configComponent;
4157
+ curConfigComponent.currentSelectedId = prmControlConfig.controlFilterData['currentNodeId'];
4158
+ return curConfigComponent;
4171
4159
  }
4172
4160
  }
4173
- eventToggleMaximizeWidget(widgetId, shouldMaximize) {
4174
- const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4175
- if (elementoHtml) {
4176
- if (shouldMaximize) {
4177
- // Aplicamos estilos CSS nativos para forzar al contenedor a flotar sobre todo el dashboard
4178
- elementoHtml.classList.add('widget-maximized');
4179
- // Deshabilitamos temporalmente que Gridstack intente moverlo mientras está maximizado
4180
- this.grid.movable(elementoHtml, false);
4181
- this.grid.resizable(elementoHtml, false);
4182
- }
4183
- else {
4184
- elementoHtml.classList.remove('widget-maximized');
4185
- // Restauramos los permisos normales leyendo si el componente de Angular estaba bloqueado previamente
4186
- const componentRef = this.widgetsActivos.get(widgetId);
4187
- const isCurrentlyLocked = componentRef?.instance.isLocked() || false;
4188
- this.grid.movable(elementoHtml, !isCurrentlyLocked);
4189
- this.grid.resizable(elementoHtml, !isCurrentlyLocked);
4161
+ handleClearFilter() {
4162
+ this.controlListConfig = [];
4163
+ this.dashboardFilterConfig.controlListConfig.forEach((item) => {
4164
+ const tableName = item.controlTable;
4165
+ const prmcontrolData = this.fn_getDataForWidgetFilterComponent(tableName, this.dashboardData);
4166
+ const prmconfigWidget = { ...item };
4167
+ if (item.controlFilterData) {
4168
+ item.configComponent = this.fnSetControlFilterClearValues(prmconfigWidget);
4190
4169
  }
4191
- // Forzamos un evento resize simulado para que los gráficos de Syncfusion recalculen su nuevo tamaño gigante/pequeño
4192
- setTimeout(() => {
4193
- window.dispatchEvent(new Event('resize'));
4194
- }, 50);
4195
- }
4170
+ const configControl = { configWidget: prmconfigWidget,
4171
+ controlData: prmcontrolData,
4172
+ controlConfig: item.configComponent
4173
+ };
4174
+ this.controlListConfig.push(configControl);
4175
+ });
4196
4176
  }
4197
- eventShowFormConfigWidget(widgetId) {
4198
- this.drawerCurrentWidgetObject['widgetId'] = widgetId;
4199
- this.drawerconfigvisible = true;
4177
+ fnSetControlFilterClearValues(prmControlConfig) {
4178
+ if (prmControlConfig.typeComponent == 'filter-slicer') {
4179
+ let curConfigComponent = prmControlConfig.configComponent;
4180
+ curConfigComponent.minCurrent = null;
4181
+ curConfigComponent.maxCurrent = null;
4182
+ return curConfigComponent;
4183
+ }
4184
+ if (prmControlConfig.typeComponent == 'filter-button') {
4185
+ let curConfigComponent = prmControlConfig.configComponent;
4186
+ curConfigComponent.currentIds = [];
4187
+ return curConfigComponent;
4188
+ }
4189
+ if (prmControlConfig.typeComponent == 'filter-list') {
4190
+ let curConfigComponent = prmControlConfig.configComponent;
4191
+ curConfigComponent.currentSelectedId = null;
4192
+ return curConfigComponent;
4193
+ }
4200
4194
  }
4201
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardComponent, deps: [{ token: i0.EnvironmentInjector }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component }); }
4202
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardComponent, isStandalone: true, selector: "yw-dashboard", inputs: { showControls: { classPropertyName: "showControls", publicName: "showControls", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, dashboardStyle: { classPropertyName: "dashboardStyle", publicName: "dashboardStyle", isSignal: false, isRequired: false, transformFunction: null }, dashboardConfig: { classPropertyName: "dashboardConfig", publicName: "dashboardConfig", isSignal: false, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: false, isRequired: false, transformFunction: null }, dashboardFilterConfig: { classPropertyName: "dashboardFilterConfig", publicName: "dashboardFilterConfig", isSignal: false, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "gridContainer", first: true, predicate: ["gridContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- <div class=\"controls\" [hidden]=\"!showControls()\">\r\n <button (click)=\"saveLayoutToJSON()\">Guardar JSON en Consola</button>\r\n <button (click)=\"fn_btn_addWidget()\">add widget</button>\r\n <button (click)=\"fn_btn_lockPositionWidgets()\">Lock widgets</button>\r\n <button (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\">\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n</div> -->\r\n\r\n<div class=\"controls-container\" >\r\n @if ( showControls() ) { \r\n <!-- Botones principales con nz-button y tipos de Ant Design -->\r\n <button nz-button nzType=\"default\" (click)=\"saveLayoutToJSON()\" >\r\n <span nz-icon nz-type=\"code\"></span> Guardar JSON\r\n </button>\r\n\r\n <button nz-button nzType=\"primary\" (click)=\"fn_btn_addWidget()\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n\r\n <button nz-button [nzType]=\"isLocked ? 'primary' : 'default'\" nzDanger (click)=\"fn_btn_lockPositionWidgets()\" >\r\n <span nz-icon [nzType]=\"isLocked ? 'lock' : 'unlock'\"></span> \r\n {{ isLocked ? 'Bloqueado' : 'Bloquear' }}\r\n </button>\r\n }\r\n @if ( showFilter() ) { \r\n <!-- Bot\u00F3n de filtro con estilo ic\u00F3nico -->\r\n <button nz-button nzType=\"default\" nzShape=\"circle\" (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\" [class.active]=\"drawerfiltervisible\" >\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n }\r\n</div>\r\n<div #gridContainer class=\"grid-stack canvas-container\"\r\n [style.--canvas-bg]=\"dashboardStyle.canvasBackgroundColor\"\r\n></div>\r\n\r\n\r\n\r\n\r\n<!-- DRAWER - FILTER -->\r\n\r\n<nz-drawer\r\n[nzVisible]=\"drawerfiltervisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormFilterDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>Filtros</h6>\r\n <yw-dashboard-filter\r\n [dashboardFilterConfig]=\"dashboardFilterConfig\"\r\n [dashboardData]=\"dashboardData\"\r\n (filterSelectionChange)=\"onFilterSelectionChange($event)\"\r\n >\r\n </yw-dashboard-filter>\r\n </div>\r\n\r\n <!--\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"Secion de Filtros\">\r\n <div class=\"controls\">\r\n aqui botones de control:\r\n\r\n </div>\r\n <div>\r\n \r\n\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n-->\r\n</ng-container>\r\n</nz-drawer>\r\n\r\n\r\n\r\n<!-- DRAWER - CONFIGURACION -->\r\n<nz-drawer\r\n[nzVisible]=\"drawerconfigvisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>{{ drawerCurrentWidgetObject.widgetId }}</h6>\r\n </div>\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"{{ drawerCurrentWidgetObject.widgetId }}\">\r\n <div class=\"controls\">\r\n <button (click)=\"fn_btn_updateWidgetControlConfig(drawerCurrentWidgetObject.widgetId)\">Actualiza Configuracion</button>\r\n <button (click)=\"fn_btn_updateWidgetControlData(drawerCurrentWidgetObject.widgetId)\">Actualiza Data</button> \r\n </div>\r\n <div>\r\n bla bla bla\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n</ng-container>\r\n</nz-drawer>", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.canvas-container{background-color:var(--canvas-bg, #f5f5f5);min-height:500px;padding:10px}::vdeep .card-inner{background:#fff;border:1px solid #ccc;height:100%;display:flex;align-items:center;justify-content:center}:host ::ng-deep .grid-stack-item-content{border-radius:6px;box-shadow:0 2px 5px #0000001a;display:flex;align-items:center;justify-content:center;font-weight:700;overflow:hidden}::ng-deep .grid-stack-item.widget-maximized{position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;z-index:9999!important;transform:none!important;margin:0!important;padding:5px}::ng-deep .grid-stack-item.widget-maximized .grid-stack-item-content{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;margin:0!important;padding:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;background-color:#fff!important;overflow:hidden}::ng-deep .grid-stack>.grid-stack-item>.ui-resizable-handle{opacity:0!important;background-image:none!important}.widget-details-info{padding-top:30px;text-align:center}.widget-details-info h6{font-family:manrope,sans-serif;font-size:20px;font-weight:700;line-height:26px;color:#2d3144}.widget-details-info p{font-family:manrope,sans-serif;font-size:14px;font-weight:400;line-height:21px;color:#4b5064}.controls-container{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:12px 16px;background:#fff;border-bottom:1px solid #f0f0f0;box-shadow:0 2px 8px #0000000a;border-radius:6px;margin-bottom:16px;width:100%;box-sizing:border-box}.controls-container button{white-space:nowrap}.controls-container .btn-filtro{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.controls-container .btn-filtro.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.controls-container .btn-filtro.active:hover{background-color:#40a9ff;border-color:#40a9ff}@media(max-width:576px){.controls-container{gap:8px;padding:8px 10px}.controls-container button[nz-button]{font-size:13px;padding:0 10px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: NzDrawerModule }, { kind: "component", type: i2.NzDrawerComponent, selector: "nz-drawer", inputs: ["nzContent", "nzCloseIcon", "nzClosable", "nzMaskClosable", "nzMask", "nzCloseOnNavigation", "nzNoAnimation", "nzKeyboard", "nzTitle", "nzExtra", "nzFooter", "nzPlacement", "nzSize", "nzMaskStyle", "nzBodyStyle", "nzWrapClassName", "nzWidth", "nzHeight", "nzZIndex", "nzOffsetX", "nzOffsetY", "nzVisible"], outputs: ["nzOnViewInit", "nzOnClose", "nzVisibleChange"], exportAs: ["nzDrawer"] }, { kind: "directive", type: i2.NzDrawerContentDirective, selector: "[nzDrawerContent]", exportAs: ["nzDrawerContent"] }, { kind: "ngmodule", type: NzTabsModule }, { kind: "component", type: i3.NzTabsComponent, selector: "nz-tabs,nz-tabset", inputs: ["nzSelectedIndex", "nzTabPosition", "nzTabBarExtraContent", "nzCanDeactivate", "nzAddIcon", "nzTabBarStyle", "nzType", "nzSize", "nzAnimated", "nzTabBarGutter", "nzHideAdd", "nzCentered", "nzHideAll", "nzLinkRouter", "nzLinkExact", "nzDestroyInactiveTabPane"], outputs: ["nzSelectChange", "nzSelectedIndexChange", "nzTabListScroll", "nzClose", "nzAdd"], exportAs: ["nzTabs"] }, { kind: "component", type: i3.NzTabComponent, selector: "nz-tab", inputs: ["nzTitle", "nzClosable", "nzCloseIcon", "nzDisabled", "nzForceRender"], outputs: ["nzSelect", "nzDeselect", "nzClick", "nzContextmenu"], exportAs: ["nzTab"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i1$3.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i5.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i6.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i7.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "component", type: YwDashboardFilterComponent, selector: "yw-dashboard-filter", inputs: ["dashboardFilterConfig", "dashboardData"], outputs: ["filterSelectionChange"] }] }); }
4195
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4196
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardFilterComponent, isStandalone: true, selector: "yw-dashboard-filter", inputs: { dashboardFilterConfig: "dashboardFilterConfig", dashboardData: "dashboardData" }, outputs: { filterSelectionChange: "filterSelectionChange" }, ngImport: i0, template: "\r\n <div class=\"dashboard-filter-content-bar\">\r\n @if ( dashboardFilterConfig.showConfigButtons ) { \r\n <button nz-button nzType=\"primary\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n }\r\n <button nz-button \r\n nzType=\"default\" \r\n nzShape=\"circle\" \r\n (click)=\"handleClearFilter()\" \r\n class=\"btn-clean\" \r\n > \r\n <!-- Icono de Borrador (Eraser) activo / relleno --> \r\n <svg \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M16.24 3.56a2.12 2.12 0 0 1 3 0l1.2 1.2a2.12 2.12 0 0 1 0 3l-8.5 8.5H6.5a1.5 1.5 0 0 1-1.5-1.5v-5.44l11.24-11.24zM19 19H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2z\"/>\r\n </svg>\r\n \r\n </button>\r\n </div>\r\n\r\n <div class=\"dashboard-filter-content-wrapper\">\r\n @for ( colFilter of controlListConfig; track colFilter) { \r\n <div class=\"dashboard-filter-content-wrapper-item\">\r\n \r\n <yw-dashboard-filter-widget \r\n [configWidget]=\"colFilter.configWidget\"\r\n [controlConfig]=\"colFilter.controlConfig\"\r\n [controlData]=\"colFilter.controlData\"\r\n (selectionChange)=\"onFilterChange($event)\"\r\n > \r\n </yw-dashboard-filter-widget>\r\n\r\n </div>\r\n }\r\n </div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden;box-sizing:border-box}.dashboard-filter-content-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%;box-sizing:border-box}.dashboard-filter-content-wrapper{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;width:100%;display:flex;flex-direction:column;box-sizing:border-box}.dashboard-filter-content-wrapper-item{padding-bottom:5px}.btn-clean{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.btn-clean.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.btn-clean.active:hover{background-color:#40a9ff;border-color:#40a9ff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: YwDashboardFilterWidgetComponent, selector: "yw-dashboard-filter-widget", inputs: ["configWidget", "controlConfig", "controlData"], outputs: ["eventDeleteRequest", "eventShowConfigFormRequest", "selectionChange"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i1$3.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i5.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i6.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i7.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }] }); }
4203
4197
  }
4204
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardComponent, decorators: [{
4198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardFilterComponent, decorators: [{
4205
4199
  type: Component,
4206
- args: [{ selector: 'yw-dashboard', standalone: true, imports: [CommonModule,
4207
- YwDashboardWidgetComponent,
4208
- NzDrawerModule,
4209
- NzTabsModule,
4210
- YwBaseSlicerComponent,
4200
+ args: [{ selector: 'yw-dashboard-filter', standalone: true, imports: [CommonModule,
4201
+ YwDashboardFilterWidgetComponent,
4211
4202
  NzIconModule,
4212
4203
  NzButtonModule,
4213
- YwButtonSlicerComponent,
4214
- YwListSlicerComponent,
4215
- YwDashboardFilterComponent
4216
- ], template: "<!-- <div class=\"controls\" [hidden]=\"!showControls()\">\r\n <button (click)=\"saveLayoutToJSON()\">Guardar JSON en Consola</button>\r\n <button (click)=\"fn_btn_addWidget()\">add widget</button>\r\n <button (click)=\"fn_btn_lockPositionWidgets()\">Lock widgets</button>\r\n <button (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\">\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n</div> -->\r\n\r\n<div class=\"controls-container\" >\r\n @if ( showControls() ) { \r\n <!-- Botones principales con nz-button y tipos de Ant Design -->\r\n <button nz-button nzType=\"default\" (click)=\"saveLayoutToJSON()\" >\r\n <span nz-icon nz-type=\"code\"></span> Guardar JSON\r\n </button>\r\n\r\n <button nz-button nzType=\"primary\" (click)=\"fn_btn_addWidget()\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n\r\n <button nz-button [nzType]=\"isLocked ? 'primary' : 'default'\" nzDanger (click)=\"fn_btn_lockPositionWidgets()\" >\r\n <span nz-icon [nzType]=\"isLocked ? 'lock' : 'unlock'\"></span> \r\n {{ isLocked ? 'Bloqueado' : 'Bloquear' }}\r\n </button>\r\n }\r\n @if ( showFilter() ) { \r\n <!-- Bot\u00F3n de filtro con estilo ic\u00F3nico -->\r\n <button nz-button nzType=\"default\" nzShape=\"circle\" (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\" [class.active]=\"drawerfiltervisible\" >\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n }\r\n</div>\r\n<div #gridContainer class=\"grid-stack canvas-container\"\r\n [style.--canvas-bg]=\"dashboardStyle.canvasBackgroundColor\"\r\n></div>\r\n\r\n\r\n\r\n\r\n<!-- DRAWER - FILTER -->\r\n\r\n<nz-drawer\r\n[nzVisible]=\"drawerfiltervisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormFilterDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>Filtros</h6>\r\n <yw-dashboard-filter\r\n [dashboardFilterConfig]=\"dashboardFilterConfig\"\r\n [dashboardData]=\"dashboardData\"\r\n (filterSelectionChange)=\"onFilterSelectionChange($event)\"\r\n >\r\n </yw-dashboard-filter>\r\n </div>\r\n\r\n <!--\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"Secion de Filtros\">\r\n <div class=\"controls\">\r\n aqui botones de control:\r\n\r\n </div>\r\n <div>\r\n \r\n\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n-->\r\n</ng-container>\r\n</nz-drawer>\r\n\r\n\r\n\r\n<!-- DRAWER - CONFIGURACION -->\r\n<nz-drawer\r\n[nzVisible]=\"drawerconfigvisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>{{ drawerCurrentWidgetObject.widgetId }}</h6>\r\n </div>\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"{{ drawerCurrentWidgetObject.widgetId }}\">\r\n <div class=\"controls\">\r\n <button (click)=\"fn_btn_updateWidgetControlConfig(drawerCurrentWidgetObject.widgetId)\">Actualiza Configuracion</button>\r\n <button (click)=\"fn_btn_updateWidgetControlData(drawerCurrentWidgetObject.widgetId)\">Actualiza Data</button> \r\n </div>\r\n <div>\r\n bla bla bla\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n</ng-container>\r\n</nz-drawer>", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.canvas-container{background-color:var(--canvas-bg, #f5f5f5);min-height:500px;padding:10px}::vdeep .card-inner{background:#fff;border:1px solid #ccc;height:100%;display:flex;align-items:center;justify-content:center}:host ::ng-deep .grid-stack-item-content{border-radius:6px;box-shadow:0 2px 5px #0000001a;display:flex;align-items:center;justify-content:center;font-weight:700;overflow:hidden}::ng-deep .grid-stack-item.widget-maximized{position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;z-index:9999!important;transform:none!important;margin:0!important;padding:5px}::ng-deep .grid-stack-item.widget-maximized .grid-stack-item-content{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;margin:0!important;padding:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;background-color:#fff!important;overflow:hidden}::ng-deep .grid-stack>.grid-stack-item>.ui-resizable-handle{opacity:0!important;background-image:none!important}.widget-details-info{padding-top:30px;text-align:center}.widget-details-info h6{font-family:manrope,sans-serif;font-size:20px;font-weight:700;line-height:26px;color:#2d3144}.widget-details-info p{font-family:manrope,sans-serif;font-size:14px;font-weight:400;line-height:21px;color:#4b5064}.controls-container{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:12px 16px;background:#fff;border-bottom:1px solid #f0f0f0;box-shadow:0 2px 8px #0000000a;border-radius:6px;margin-bottom:16px;width:100%;box-sizing:border-box}.controls-container button{white-space:nowrap}.controls-container .btn-filtro{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.controls-container .btn-filtro.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.controls-container .btn-filtro.active:hover{background-color:#40a9ff;border-color:#40a9ff}@media(max-width:576px){.controls-container{gap:8px;padding:8px 10px}.controls-container button[nz-button]{font-size:13px;padding:0 10px}}\n"] }]
4217
- }], ctorParameters: () => [{ type: i0.EnvironmentInjector }, { type: i0.ApplicationRef }], propDecorators: { gridContainer: [{
4218
- type: ViewChild,
4219
- args: ['gridContainer', { static: true }]
4220
- }], showControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "showControls", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], dashboardStyle: [{
4221
- type: Input
4222
- }], dashboardConfig: [{
4204
+ ], template: "\r\n <div class=\"dashboard-filter-content-bar\">\r\n @if ( dashboardFilterConfig.showConfigButtons ) { \r\n <button nz-button nzType=\"primary\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n }\r\n <button nz-button \r\n nzType=\"default\" \r\n nzShape=\"circle\" \r\n (click)=\"handleClearFilter()\" \r\n class=\"btn-clean\" \r\n > \r\n <!-- Icono de Borrador (Eraser) activo / relleno --> \r\n <svg \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M16.24 3.56a2.12 2.12 0 0 1 3 0l1.2 1.2a2.12 2.12 0 0 1 0 3l-8.5 8.5H6.5a1.5 1.5 0 0 1-1.5-1.5v-5.44l11.24-11.24zM19 19H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2z\"/>\r\n </svg>\r\n \r\n </button>\r\n </div>\r\n\r\n <div class=\"dashboard-filter-content-wrapper\">\r\n @for ( colFilter of controlListConfig; track colFilter) { \r\n <div class=\"dashboard-filter-content-wrapper-item\">\r\n \r\n <yw-dashboard-filter-widget \r\n [configWidget]=\"colFilter.configWidget\"\r\n [controlConfig]=\"colFilter.controlConfig\"\r\n [controlData]=\"colFilter.controlData\"\r\n (selectionChange)=\"onFilterChange($event)\"\r\n > \r\n </yw-dashboard-filter-widget>\r\n\r\n </div>\r\n }\r\n </div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden;box-sizing:border-box}.dashboard-filter-content-bar{display:flex;justify-content:flex-end;gap:4px;padding:5px 10px;flex-shrink:0;width:100%;box-sizing:border-box}.dashboard-filter-content-wrapper{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;width:100%;display:flex;flex-direction:column;box-sizing:border-box}.dashboard-filter-content-wrapper-item{padding-bottom:5px}.btn-clean{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.btn-clean.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.btn-clean.active:hover{background-color:#40a9ff;border-color:#40a9ff}\n"] }]
4205
+ }], ctorParameters: () => [], propDecorators: { dashboardFilterConfig: [{
4223
4206
  type: Input
4224
4207
  }], dashboardData: [{
4225
4208
  type: Input
4226
- }], dashboardFilterConfig: [{
4227
- type: Input
4228
- }] } });
4209
+ }], filterSelectionChange: [{ type: i0.Output, args: ["filterSelectionChange"] }] } });
4229
4210
 
4230
- const parseSyncFusionData = (allTablesData, configData_) => {
4231
- if (!allTablesData || !configData_) {
4232
- return { series: [], labelData: {} };
4211
+ /* END - FILTROS */
4212
+ class YwDashboardComponent {
4213
+ /* */
4214
+ constructor(environmentInjector, appRef) {
4215
+ //Configurar Bloqueo de Widgets
4216
+ /*
4217
+ effect(() => {
4218
+ const isLocked = this.allIsLocked();
4219
+ console.log('El estado de bloqueo cambió a:', isLocked);
4220
+ this.fn_lockPositionWidgets(isLocked);
4221
+ });
4222
+ */
4223
+ this.environmentInjector = environmentInjector;
4224
+ this.appRef = appRef;
4225
+ this.widgetsActivos = new Map();
4226
+ this.showControls = input(false, ...(ngDevMode ? [{ debugName: "showControls" }] : []));
4227
+ this.showFilter = input(false, ...(ngDevMode ? [{ debugName: "showFilter" }] : []));
4228
+ this.allIsLocked = signal(false, ...(ngDevMode ? [{ debugName: "allIsLocked" }] : []));
4229
+ this.drawerfiltervisible = false;
4230
+ this.drawerconfigvisible = false;
4231
+ this.drawerCurrentWidgetObject = {};
4232
+ this.isLocked = false;
4233
+ this.countWidget = 1;
4234
+ this.layoutConfig = {
4235
+ margin: 10,
4236
+ cellHeight: 100,
4237
+ columns: 24
4238
+ };
4239
+ /* ejemplo de Items
4240
+ items: [
4241
+ { id: 'div-1', x: 0, y: 0, w: 14, h: 2, content: 'DIV 1' , contentData : {}},
4242
+ { id: 'div-2', x: 5, y: 2, w: 19, h: 1, content: 'DIV 2' , contentData : {}},
4243
+ { id: 'div-3', x: 0, y: 3, w: 22, h: 1, content: 'DIV 3', contentData : {} }
4244
+ ]
4245
+ */
4246
+ this.dashboardStyle = {
4247
+ canvasBackgroundColor: "#f5f5f5",
4248
+ widgetBackgroundColor: "#ffffff",
4249
+ widgetBorderColor: "#007bff"
4250
+ };
4251
+ this.dashboardConfig = {
4252
+ showConfigButtons: false,
4253
+ lockPositionWidgets: false,
4254
+ layoutConfig: {
4255
+ margin: 0,
4256
+ cellHeight: 0,
4257
+ columns: 0
4258
+ },
4259
+ controlListConfig: []
4260
+ };
4261
+ this.dashboardData = [];
4262
+ this.dashboardItems = [];
4263
+ /* REF FILTROS */
4264
+ this.dashboardFilterConfig = {
4265
+ showConfigButtons: false,
4266
+ lockPositionWidgets: false,
4267
+ controlListConfig: []
4268
+ };
4233
4269
  }
4234
- console.log("bubble data:--", allTablesData, configData_);
4235
- // SI LO MANDAN COMO UN ARREGLO PLANO, LO ENVOLVEMOS AUTOMÁTICAMENTE
4236
- if (Array.isArray(allTablesData)) {
4237
- const defaultTableName = configData_?.configData?.table || 'default';
4238
- allTablesData = { [defaultTableName]: allTablesData };
4270
+ ngOnInit() {
4271
+ // console.log("DATA QUE LLEGA AL DASHBOARD:",this.dashboardConfig)
4239
4272
  }
4240
- const { ruleColorPoint, configData, xLabelText, yLabelText, title, chartoColoredLine, opacity, emptyPointSettings } = configData_;
4241
- console.log("configData_:.::", configData_);
4242
- const cfg = configData || {};
4243
- const { xAxis, yAxis, joins, configJoins, table, serie, tooltip, legends } = cfg;
4244
- let series = [];
4245
- // Función auxiliar robusta para obtener propiedades (soporta notación con puntos de los joins)
4246
- // const getNestedValue = (obj: any, path: string) => {
4247
- // if (!obj || !path) return undefined;
4248
- // if (obj[path] !== undefined) return obj[path];
4249
- // return path.split('.').reduce((acc, part) => (acc && acc[part] !== undefined ? acc[part] : undefined), obj);
4250
- // };
4251
- const sortByXAxis = (data) => {
4252
- if (!xAxis)
4253
- return data;
4254
- return [...data].sort((a, b) => {
4255
- const aValue = getNestedValue(a, xAxis);
4256
- const bValue = getNestedValue(b, xAxis);
4257
- if (aValue == null)
4258
- return 1;
4259
- if (bValue == null)
4260
- return -1;
4261
- return String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' });
4262
- });
4263
- };
4264
- if (joins === true) {
4265
- if (!configJoins) {
4266
- series = [];
4273
+ ngOnChanges(changes) {
4274
+ if (changes['dashboardConfig']) {
4275
+ console.log('Nuevo valor dashboardConfig:', this.dashboardConfig);
4276
+ //this.initCanvasGrid();
4277
+ }
4278
+ if (changes['dashboardData']) {
4279
+ console.log('Nuevo valor dashboardData:', this.dashboardData);
4280
+ //this.initCanvasGrid();
4281
+ }
4282
+ }
4283
+ ngAfterViewInit() {
4284
+ this.initCanvasGrid();
4285
+ }
4286
+ initCanvasGrid() {
4287
+ this.layoutConfig = this.dashboardConfig.layoutConfig;
4288
+ // Opciones para restringir colisiones y comportamiento
4289
+ let options = {
4290
+ column: this.layoutConfig.columns,
4291
+ cellHeight: this.layoutConfig.cellHeight,
4292
+ margin: this.layoutConfig.margin,
4293
+ float: true, // Evita que los divs floten automáticamente hacia arriba
4294
+ //noOverlap: true, // CLAVE: Bloquea el movimiento si colisiona con otro DIV
4295
+ resizable: { handles: 'e, se, s, sw, w, nw, n, ne', autoHide: true } // Permite redimensionar desde todos los bordes
4296
+ };
4297
+ if (this.layoutConfig.columnOpts) {
4298
+ options = {
4299
+ column: this.layoutConfig.columns,
4300
+ cellHeight: this.layoutConfig.cellHeight,
4301
+ margin: this.layoutConfig.margin,
4302
+ columnOpts: {
4303
+ breakpointForWindow: this.layoutConfig.columnOpts.breakpointForWindow, // Mide el ancho de la ventana gráfica
4304
+ breakpoints: this.layoutConfig.columnOpts.breakpoints,
4305
+ layout: this.layoutConfig.columnOpts.layout || 'list', // Mantiene el orden de los widgets secuencialmente al colapsar
4306
+ },
4307
+ float: true, // Evita que los divs floten automáticamente hacia arriba
4308
+ //noOverlap: true, // CLAVE: Bloquea el movimiento si colisiona con otro DIV
4309
+ resizable: { handles: 'e, se, s, sw, w, nw, n, ne', autoHide: true } // Permite redimensionar desde todos los bordes
4310
+ };
4267
4311
  }
4268
- else {
4269
- const unifiedTable = resolveJoinedTableData(allTablesData, configJoins);
4270
- console.log('unifiedTable bubble', unifiedTable);
4271
- if (unifiedTable && unifiedTable.length > 0) {
4272
- // const validRawData = unifiedTable.filter(
4273
- // (item: any) =>
4274
- // item &&
4275
- // xAxis &&
4276
- // getNestedValue(item, xAxis) !== undefined &&
4277
- // getNestedValue(item, xAxis) !== null,
4278
- // );
4279
- const validRawData = unifiedTable.filter((item) => item &&
4280
- xAxis &&
4281
- getNestedValue(item, xAxis) !== undefined &&
4282
- getNestedValue(item, xAxis) !== null);
4283
- const orderedRawData = sortByXAxis(validRawData);
4284
- if (orderedRawData.length > 0) {
4285
- if (serie) {
4286
- // 2. Agrupamos los datos por el valor de la serie (ej. sucursal o vendedor)
4287
- const groupedBySerie = orderedRawData.reduce((acc, item) => {
4288
- const serieVal = getNestedValue(item, serie);
4289
- const serieKey = serieVal !== undefined && serieVal !== null
4290
- ? String(serieVal)
4291
- : 'Desconocido';
4292
- if (!acc[serieKey]) {
4293
- acc[serieKey] = [];
4294
- }
4295
- acc[serieKey].push(item);
4296
- return acc;
4297
- }, {});
4298
- series = Object.entries(groupedBySerie).map(([serieName, groupItems]) => {
4299
- const orderedGroupItems = [...groupItems].sort((a, b) => {
4300
- const aValue = getNestedValue(a, xAxis);
4301
- const bValue = getNestedValue(b, xAxis);
4302
- return String(aValue).localeCompare(String(bValue), undefined, {
4303
- numeric: true,
4304
- sensitivity: 'base'
4305
- });
4306
- });
4307
- console.log("SERIES NAME:", orderedGroupItems);
4308
- const dataSource = orderedGroupItems.map((item) => {
4309
- const rawVal = getNestedValue(item, yAxis);
4310
- const valorFinal = rawVal !== undefined
4311
- ? typeof rawVal === 'number'
4312
- ? rawVal
4313
- : Number(rawVal) || 0
4314
- : 0;
4315
- const sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
4316
- return {
4317
- x: String(getNestedValue(item, xAxis)),
4318
- y: valorFinal,
4319
- size: sizeFinal,
4320
- name: String(getNestedValue(item, xAxis)),
4321
- // ...(chartoColoredLine
4322
- // ? { color: getRandomProfessionalHex() }
4323
- // : {})
4324
- };
4325
- });
4326
- return {
4327
- name: serieName === 'default' ? 'Serie 1' : serieName,
4328
- dataSource,
4329
- opacity: opacity,
4330
- emptyPointSettings: emptyPointSettings
4331
- // dashArray: dashArray !== undefined ? dashArray : undefined,
4332
- // width: width !== undefined ? width : undefined,
4333
- // widthLine: widthLine !== undefined ? widthLine : undefined,
4334
- };
4335
- });
4336
- }
4337
- else {
4338
- const dataSource = orderedRawData.map((item) => {
4339
- let valorFinal = 0;
4340
- let sizeFinal = 0;
4341
- // const shouldColorLine = chartoColoredLine;
4342
- const rawVal = getNestedValue(item, yAxis);
4343
- // const rawSize = getNestedValue(item, size); // <--- Extraer el valor real
4344
- if (rawVal !== undefined) {
4345
- valorFinal = typeof rawVal === 'number' ? rawVal : Number(rawVal) || 0;
4346
- }
4347
- // if (rawSize !== undefined) {
4348
- sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
4349
- // }
4350
- return {
4351
- x: String(getNestedValue(item, xAxis)),
4352
- y: valorFinal,
4353
- size: sizeFinal, // <--- Valor numérico extraído
4354
- name: String(getNestedValue(item, xAxis)),
4355
- // ...(shouldColorLine ? { color: getRandomProfessionalHex() } : {})
4356
- };
4357
- });
4358
- series = [
4359
- {
4360
- name: xAxis.split(".")[0] && xAxis !== 'default' ? xAxis.split(".")[0] : 'Serie Join',
4361
- dataSource: dataSource,
4362
- opacity: opacity,
4363
- emptyPointSettings: emptyPointSettings
4364
- // dashArray: dashArray !== undefined ? dashArray : undefined,
4365
- // width: width !== undefined ? width : undefined,
4366
- // widthLine: widthLine !== undefined ? widthLine : undefined,
4367
- }
4368
- ];
4312
+ /*EXAMPLE OF "columnOpts"
4313
+ columnOpts: {
4314
+ breakpointForWindow: true,
4315
+ breakpoints: [
4316
+ { w: 768, c: 1 },
4317
+ { w: 992, c: 6 },
4318
+ { w: 1200, c: this.layoutConfig.columns } // 👈 Usa tu variable para consistencia en Desktop
4319
+ ],
4320
+ layout: 'list'
4369
4321
  }
4370
- }
4371
- }
4322
+ */
4323
+ // Inicializar el canvas
4324
+ this.grid = GridStack.init(options, this.gridContainer.nativeElement);
4325
+ // Cargar los elementos iniciales desde el JSON
4326
+ //this.loadLayoutFromJSON(this.dashboardItems);
4327
+ this.loadLayoutFromJSON(this.dashboardConfig, this.dashboardStyle);
4328
+ //Evaluar Bloqueo de Widgets en Inicializacion
4329
+ if (this.dashboardConfig.lockPositionWidgets) {
4330
+ this.fn_lockPositionWidgets(this.dashboardConfig.lockPositionWidgets);
4372
4331
  }
4332
+ // Listener para detectar cambios y actualizar el JSON
4333
+ this.grid.on('change', (event, items) => {
4334
+ this.saveLayoutToJSON();
4335
+ });
4336
+ //Listener para detectar cuando cambia las dimenciones de un widget
4337
+ this.grid.on('resizestop', (event, el) => {
4338
+ // Disparamos el evento nativo del navegador de forma controlada
4339
+ window.dispatchEvent(new Event('resize'));
4340
+ });
4373
4341
  }
4374
- else {
4375
- let normalizedTables = [];
4376
- if (Array.isArray(table)) {
4377
- normalizedTables = table.filter((t) => typeof t === 'string' && t.trim() !== '');
4342
+ //Filtrar Datos por Componente del Widget
4343
+ fn_getDataForWidgetComponent(listTables, prmDashboardData) {
4344
+ let dataresponse = new Map();
4345
+ const ldsize = prmDashboardData.length;
4346
+ const ltsize = listTables.length;
4347
+ if (ltsize > 0) {
4348
+ listTables.forEach((curTableName) => {
4349
+ prmDashboardData.forEach((curTableRef) => {
4350
+ if (curTableName.localeCompare(curTableRef.tableName) == 0) {
4351
+ dataresponse.set(curTableRef.tableName, curTableRef.tableData);
4352
+ }
4353
+ });
4354
+ });
4378
4355
  }
4379
- else if (typeof table === 'string' && table.trim() !== '') {
4380
- normalizedTables = [table];
4356
+ else {
4357
+ prmDashboardData.forEach((curTableRef) => {
4358
+ dataresponse.set(curTableRef.tableName, curTableRef.tableData);
4359
+ });
4360
+ }
4361
+ const object_base_response = Object.fromEntries(dataresponse);
4362
+ return object_base_response;
4363
+ }
4364
+ fn_getDataForWidgetComponent_back(listTables) {
4365
+ let dataresponse = [];
4366
+ const ldsize = this.dashboardData.length;
4367
+ const ltsize = listTables.length;
4368
+ if (ltsize > 0) {
4369
+ listTables.forEach((curTableName) => {
4370
+ this.dashboardData.forEach((curTableData) => {
4371
+ if (curTableName.localeCompare(curTableData.tableName) == 0) {
4372
+ dataresponse.push(curTableData);
4373
+ }
4374
+ });
4375
+ });
4381
4376
  }
4382
4377
  else {
4383
- normalizedTables = Object.keys(allTablesData);
4378
+ return this.dashboardData;
4384
4379
  }
4385
- series = normalizedTables
4386
- .map((tableName) => {
4387
- const rawData = (allTablesData && allTablesData[tableName]) ? allTablesData[tableName] : [];
4388
- if (!rawData || rawData.length === 0)
4389
- return null;
4390
- const orderedRawData = rawData.filter((item) => item && xAxis && getNestedValue(item, xAxis) !== undefined && getNestedValue(item, xAxis) !== null);
4391
- if (orderedRawData.length === 0)
4392
- return null;
4393
- const dataSource = orderedRawData.map((item) => {
4394
- let valorFinal = 0;
4395
- let sizeFinal = 0;
4396
- const shouldColorLine = chartoColoredLine;
4397
- const rawVal = getNestedValue(item, yAxis);
4398
- // const rawSize = getNestedValue(item, size);
4399
- if (rawVal !== undefined) {
4400
- valorFinal = typeof rawVal === "number" ? rawVal : Number(rawVal) || 0;
4380
+ return dataresponse;
4381
+ }
4382
+ // Carga elementos desde la configuración JSON
4383
+ //public loadLayoutFromJSON(dashboardItems: YwWidgetLayoutInterface[], ): void {
4384
+ loadLayoutFromJSON(prmDashboardConfig, prmDashboardStyle) {
4385
+ this.grid.removeAll();
4386
+ const widgets = [];
4387
+ this.grid.load(widgets);
4388
+ const lsize = prmDashboardConfig.controlListConfig.length;
4389
+ if (lsize > 0) {
4390
+ prmDashboardConfig.controlListConfig.forEach((widgetConfig) => {
4391
+ //Agregar uno por uno, para poder cargar los parametros internos de los componentes.
4392
+ const controlData = this.fn_getDataForWidgetComponent(widgetConfig.controlTables, this.dashboardData);
4393
+ //Sobre Escribir el ID Correcto en Layaout
4394
+ widgetConfig.layoutConfig.id = widgetConfig.widgetId;
4395
+ //Evaluar Mostrar Eventos
4396
+ if (widgetConfig.showConfigButtons == null || widgetConfig.showConfigButtons == undefined) {
4397
+ widgetConfig.showConfigButtons = prmDashboardConfig.showConfigButtons;
4401
4398
  }
4402
- // if (rawSize !== undefined) {
4403
- sizeFinal = Math.abs(valorFinal) !== 0 ? Math.abs(valorFinal) * 0.05 : 0;
4404
- // }
4405
- return {
4406
- x: String(getNestedValue(item, xAxis)),
4407
- y: valorFinal,
4408
- size: sizeFinal, // <--- Valor numérico extraído
4409
- name: String(getNestedValue(item, xAxis))
4410
- // ...(shouldColorLine ? { color: getRandomProfessionalHex() } : {})
4411
- };
4399
+ //Evalua Asignacion de Estilos
4400
+ if (widgetConfig.widgetStyle == null || widgetConfig.widgetStyle == undefined) {
4401
+ widgetConfig['widgetStyle'] = {};
4402
+ widgetConfig['widgetStyle']['widgetBackgroundColor'] = prmDashboardStyle.widgetBackgroundColor;
4403
+ widgetConfig['widgetStyle']['widgetBorderColor'] = prmDashboardStyle.widgetBorderColor;
4404
+ }
4405
+ this.eventAddWidget(widgetConfig, widgetConfig.controlConfig, controlData, widgetConfig.layoutConfig);
4412
4406
  });
4413
- return {
4414
- name: tableName === 'default' ? xAxis : tableName,
4415
- dataSource: dataSource,
4416
- opacity: opacity,
4417
- emptyPointSettings: emptyPointSettings
4418
- // dashArray: dashArray !== undefined ? dashArray : undefined,
4419
- // width: width !== undefined ? width : undefined,
4420
- // widthLine: widthLine !== undefined ? widthLine : undefined,
4421
- };
4422
- })
4423
- .filter((item) => item !== null);
4407
+ }
4408
+ }
4409
+ // Guarda los elementos desde la configuración JSON
4410
+ saveLayoutToJSON() {
4411
+ const currentWidgets = this.grid.save(false);
4412
+ this.dashboardItems = currentWidgets.map(w => ({
4413
+ id: w.id,
4414
+ x: w.x || 0,
4415
+ y: w.y || 0,
4416
+ w: w.w || 1,
4417
+ h: w.h || 1,
4418
+ content: w.content,
4419
+ contentData: {}
4420
+ }));
4421
+ console.log('JSON Guardado:', JSON.stringify(this.dashboardItems));
4422
+ return this.dashboardItems;
4424
4423
  }
4425
- const labelData = {
4426
- title: title || "",
4427
- axisX: {
4428
- valueType: 'Category',
4429
- title: typeof xLabelText === 'string' ? xLabelText : 'Categoría',
4430
- // labelIntersectAction: 'Rotate90',
4431
- // labelIntersectAction: 'Rotate45',
4432
- },
4433
- axisY: {
4434
- title: typeof yLabelText === 'string' ? yLabelText : 'Valor'
4435
- },
4436
- tooltip: {
4437
- ...(tooltip?.enable !== undefined ? { enable: tooltip.enable } : {}),
4438
- ...(tooltip?.format ? { format: tooltip.format } : {}),
4439
- ...(tooltip?.header ? { header: tooltip.header } : {}),
4440
- ...(tooltip?.shared !== undefined ? { shared: tooltip.shared } : {}),
4441
- enableHighlight: true,
4442
- },
4443
- legendSettings: {
4444
- visible: legends?.visible !== undefined ? legends.visible : undefined,
4445
- position: legends?.position !== undefined ? legends.position : undefined
4446
- },
4447
- ruleColorPoint: ruleColorPoint !== undefined ? ruleColorPoint : undefined
4448
- };
4449
- return {
4450
- series,
4451
- labelData
4452
- };
4453
- };
4454
-
4455
- class BubbleChartSyncfusionComponent {
4456
- constructor(elRef, cdr) {
4457
- this.elRef = elRef;
4458
- this.cdr = cdr;
4459
- this.idComponent = '';
4460
- this.dataChartParse = [];
4461
- this.currentLabelSize = '';
4462
- this.currentWidgetWidth = 0; // Valor inicial por defecto
4424
+ /*BUTTON ADD WIDGET*/
4425
+ fn_btn_addWidget() {
4426
+ this.countWidget++;
4427
+ const newWidgetId = 'widget-' + this.countWidget;
4428
+ let newItem = {
4429
+ id: newWidgetId, autoPosition: true, w: 6, h: 3,
4430
+ content: ''
4431
+ };
4432
+ //Datos de Widget y sus Contenidos
4433
+ const widgetConfig = {
4434
+ widgetId: newWidgetId,
4435
+ title: '',
4436
+ typeComponent: 'empty-visual',
4437
+ showConfigButtons: true,
4438
+ widgetStyle: DEFAULT_WIDGET_STYLE,
4439
+ layoutConfig: {
4440
+ id: '',
4441
+ w: 0,
4442
+ h: 0,
4443
+ content: undefined
4444
+ },
4445
+ controlConfig: {},
4446
+ controlTables: []
4447
+ };
4448
+ const controlConfig = {};
4449
+ const controlData = {};
4450
+ this.eventAddWidget(widgetConfig, controlConfig, controlData, newItem);
4463
4451
  }
4464
- ngOnInit() {
4465
- this.updateParsedData();
4452
+ /*BUTTON DELETE WIDGET*/
4453
+ fn_btn_deleteWidget() {
4454
+ const newWidgetId = 'widget-' + this.countWidget;
4455
+ this.eventRemoveWidgetById(newWidgetId);
4456
+ this.countWidget--;
4466
4457
  }
4467
- ngOnChanges(changes) {
4468
- if (changes['chartData'] || changes['config']) {
4469
- this.updateParsedData();
4470
- }
4458
+ /*BUTTON BLOCK WIDGETS*/
4459
+ fn_btn_lockPositionWidgets() {
4460
+ this.isLocked = !this.isLocked;
4461
+ const shouldLock = !this.allIsLocked();
4462
+ this.allIsLocked.set(shouldLock);
4463
+ this.fn_lockPositionWidgets(shouldLock);
4464
+ console.log(`All widgets have been visually and functionally locked: ${shouldLock}`);
4471
4465
  }
4472
- ngAfterViewInit() {
4473
- this._startResizeObserver();
4466
+ fn_lockPositionWidgets(prmAllIsLocked) {
4467
+ const shouldLock = prmAllIsLocked;
4468
+ // 1. Perform the functional Gridstack task on the core grid layout
4469
+ this.grid.movable('.grid-stack-item', !shouldLock);
4470
+ this.grid.resizable('.grid-stack-item', !shouldLock);
4471
+ // 2. Broadcast the state change down to every active dynamic child component
4472
+ this.widgetsActivos.forEach((componentRef) => {
4473
+ componentRef.setInput('isLocked', shouldLock);
4474
+ });
4475
+ console.log(`All widgets have been visually and functionally locked - por TS Config: ${shouldLock}`);
4474
4476
  }
4475
- ngOnDestroy() {
4476
- this._resizeObserver?.disconnect();
4477
+ /*BUTTON UPDATE WIDGET - CONTROL CONFIG */
4478
+ fn_btn_updateWidgetControlConfig(idWidget) {
4479
+ let newControlConfig = {};
4480
+ /*MODIFICAR METODO PARA CARGAR INFO DEL FORMULARIO*/
4481
+ newControlConfig['provider'] = 'syncfusion';
4482
+ this.eventUpdateWidgetControlConfig(idWidget, newControlConfig);
4477
4483
  }
4478
- updateParsedData() {
4479
- if (this.chartData && this.config) {
4480
- const rawParsed = parseSyncFusionData(this.chartData, this.config);
4481
- this.dataChartParse = rawParsed.series;
4482
- this.labelData = rawParsed.labelData;
4483
- console.log("DATA CREADA PARA AEL BUBBLE:", this.dataChartParse, this.labelData);
4484
- if (this.dataLabel && this.labelData) {
4485
- this.labelData.dataLabel = this.dataLabel;
4486
- }
4487
- this.cdr.markForCheck();
4488
- if (this.chartInstance) {
4489
- setTimeout(() => {
4490
- this.chartInstance?.refresh();
4491
- });
4492
- }
4484
+ /*BUTTON UPDATE WIDGET - CONTROL CONFIG */
4485
+ fn_btn_updateWidgetControlData(idWidget) {
4486
+ let newControlData = [];
4487
+ /*MODIFICAR METODO PARA CARGAR INFO DEL FORMULARIO*/
4488
+ newControlData = [
4489
+ { month: 'Ene', sales: 35, expenses: 28 },
4490
+ { month: 'Feb', sales: 28, expenses: 22 },
4491
+ { month: 'Mar', sales: 34, expenses: 25 },
4492
+ { month: 'Abr', sales: 32, expenses: 27 },
4493
+ { month: 'May', sales: 40, expenses: 30 },
4494
+ { month: 'Jun', sales: 32, expenses: 26 },
4495
+ { month: 'Jul', sales: 45, expenses: 32 },
4496
+ { month: 'Ago', sales: 38, expenses: 29 },
4497
+ { month: 'Sep', sales: 42, expenses: 31 },
4498
+ { month: 'Oct', sales: 48, expenses: 35 },
4499
+ { month: 'Nov', sales: 52, expenses: 38 },
4500
+ { month: 'Dic', sales: 58, expenses: 42 },
4501
+ ];
4502
+ this.eventUpdateWidgetControlData(idWidget, newControlData);
4503
+ }
4504
+ /****************************************************** */
4505
+ /*AGREGAR WIDGET*/
4506
+ eventAddWidget(widgetConfig, controlConfig, controlData, item) {
4507
+ // 1. Agregar el contenedor del widget a Gridstack
4508
+ const el = this.grid.addWidget(item);
4509
+ // 2. Obtener el contenedor interno generado por Gridstack
4510
+ const contentElement = el.querySelector('.grid-stack-item-content');
4511
+ if (contentElement) {
4512
+ // 3. Crear dinámicamente la instancia del componente Angular
4513
+ const componentRef = createComponent(YwDashboardWidgetComponent, {
4514
+ environmentInjector: this.environmentInjector, // Requerido para resolver dependencias
4515
+ hostElement: contentElement // Renderiza directamente aquí dentro
4516
+ });
4517
+ // 4. Pasar las propiedades/Inputs al componente creado
4518
+ //Evaluar Estilos
4519
+ componentRef.setInput('styleWidget', widgetConfig.widgetStyle);
4520
+ //componentRef.instance.configWidget = widgetConfig;
4521
+ componentRef.setInput('configWidget', widgetConfig);
4522
+ componentRef.setInput('controlConfig', controlConfig);
4523
+ componentRef.setInput('controlData', controlData);
4524
+ /* BEGIN - Eventos que provienen desde widget */
4525
+ componentRef.instance.eventDeleteRequest.subscribe((idWidget) => {
4526
+ // Ejecutamos la función de borrado que ya programamos en el paso anterior
4527
+ this.eventRemoveWidgetById(idWidget);
4528
+ });
4529
+ componentRef.instance.eventLockToggleRequest.subscribe((event) => {
4530
+ this.eventToggleLockWidget(event.widgetId, event.shouldLock);
4531
+ });
4532
+ componentRef.instance.eventMaximizeToggleRequest.subscribe((event) => {
4533
+ this.eventToggleMaximizeWidget(event.widgetId, event.shouldMaximize);
4534
+ });
4535
+ //Mostrar
4536
+ componentRef.instance.eventShowConfigFormRequest.subscribe((idWidget) => {
4537
+ this.eventShowFormConfigWidget(idWidget);
4538
+ });
4539
+ /* END - Eventos que provienen desde widget */
4540
+ // 5. Mover los nodos del DOM generados por Angular dentro del DIV del Widget
4541
+ this.appRef.attachView(componentRef.hostView);
4542
+ // Guardar la referencia de Componentes para hacer operaciones con estas.
4543
+ this.widgetsActivos.set(item.id, componentRef);
4544
+ //El componente interno no aparece bien dimensionado, con esto disparamos el evento para que se ajuste a las dimenciones correctas
4545
+ setTimeout(() => {
4546
+ window.dispatchEvent(new Event('resize'));
4547
+ }, 50);
4493
4548
  }
4494
4549
  }
4495
- // public pointRender(args: IPointRenderEventArgs): void {
4496
- // const yValue = Number(args.point.y);
4497
- // const configPoint: PointRenderConfig = this.config.ruleColorPoint || null;
4498
- // if (!configPoint) return;
4499
- // let passesRule = false;
4500
- // switch (configPoint.rule) {
4501
- // case 'threshold':
4502
- // passesRule = yValue >= (configPoint.thresholdValue || 0);
4503
- // break;
4504
- // case 'pair':
4505
- // passesRule = yValue % 2 === 0;
4506
- // break;
4507
- // case 'range':
4508
- // const min = configPoint.minRange || 0;
4509
- // const max = configPoint.maxRange || 100;
4510
- // passesRule = yValue >= min && yValue <= max;
4511
- // break;
4512
- // default:
4513
- // passesRule = true;
4514
- // }
4515
- // args.fill = passesRule ? (configPoint.trueColor || '#10B981') : (configPoint.falseColor || '#EF4444');
4516
- // }
4517
- pointRender(args) {
4518
- const yValue = Number(args.point.y);
4519
- // =====================================================
4520
- // EMPTY POINT / VALOR 0
4521
- // Tiene prioridad sobre ruleColorPoint
4522
- // =====================================================
4523
- if (yValue === 0) {
4524
- const emptyPoint = this.config.emptyPointSettings;
4525
- if (emptyPoint && emptyPoint !== '') {
4526
- args.fill = emptyPoint;
4527
- }
4550
+ /*UPDATE CONTROLCONFIG WIDGET*/
4551
+ eventUpdateWidgetControlConfig(idWidget, newControlConfig) {
4552
+ //Obtener la referencia del componente almacenada en el Map
4553
+ const componentRef = this.widgetsActivos.get(idWidget);
4554
+ if (!componentRef) {
4555
+ console.warn(`No se encontró un widget activo con el ID: ${idWidget}`);
4528
4556
  return;
4529
4557
  }
4530
- // =====================================================
4531
- // RULE COLOR POINT
4532
- // =====================================================
4533
- const configPoint = this.config.ruleColorPoint || null;
4534
- if (!configPoint) {
4558
+ //Actualizar el Input usando la API oficial setInput
4559
+ componentRef.setInput('controlConfig', newControlConfig);
4560
+ // NOTA: setInput dispara automáticamente la detección de cambios (ChangeDetectionStrategy.OnPush).
4561
+ // Si deseas asegurarte manualmente, puedes forzar un render:
4562
+ // componentRef.changeDetectorRef.detectChanges();
4563
+ }
4564
+ /*UPDATE CONTROL DATA WIDGET*/
4565
+ eventUpdateWidgetControlData(idWidget, newControlData) {
4566
+ //Obtener la referencia del componente almacenada en el Map
4567
+ const componentRef = this.widgetsActivos.get(idWidget);
4568
+ if (!componentRef) {
4569
+ console.warn(`No se encontró un widget activo con el ID: ${idWidget}`);
4535
4570
  return;
4536
4571
  }
4537
- let passesRule = false;
4538
- switch (configPoint.rule) {
4539
- case 'threshold':
4540
- passesRule =
4541
- yValue >= (configPoint.thresholdValue || 0);
4542
- break;
4543
- case 'pair':
4544
- passesRule = yValue % 2 === 0;
4545
- break;
4546
- case 'range':
4547
- const min = configPoint.minRange || 0;
4548
- const max = configPoint.maxRange || 100;
4549
- passesRule =
4550
- yValue >= min &&
4551
- yValue <= max;
4552
- break;
4553
- default:
4554
- passesRule = true;
4572
+ //Actualizar el Input usando la API oficial setInput
4573
+ componentRef.setInput('controlData', newControlData);
4574
+ // NOTA: setInput dispara automáticamente la detección de cambios (ChangeDetectionStrategy.OnPush).
4575
+ // Si deseas asegurarte manualmente, puedes forzar un render:
4576
+ componentRef.changeDetectorRef.detectChanges();
4577
+ }
4578
+ /****************************************************** */
4579
+ /* BEGIN - DRAWER */
4580
+ handleShowFormFilterDrawer() {
4581
+ this.drawerfiltervisible = true;
4582
+ }
4583
+ handleCloseFormFilterDrawer() {
4584
+ this.drawerfiltervisible = false;
4585
+ }
4586
+ handleCloseFormDrawer() {
4587
+ this.drawerconfigvisible = false;
4588
+ }
4589
+ fnGetDrawerWidth(prm_percent) {
4590
+ const curWidth = window.innerWidth;
4591
+ let newWidth = curWidth;
4592
+ if (prm_percent) {
4593
+ newWidth = Math.floor(curWidth * prm_percent / 100);
4555
4594
  }
4556
- args.fill = passesRule
4557
- ? (configPoint.trueColor || '#10B981')
4558
- : (configPoint.falseColor || '#EF4444');
4595
+ if (newWidth < 400) {
4596
+ newWidth = curWidth;
4597
+ }
4598
+ return newWidth;
4559
4599
  }
4560
- _startResizeObserver() {
4561
- if (typeof ResizeObserver === 'undefined')
4562
- return;
4563
- const hostEl = this.elRef.nativeElement;
4564
- this._resizeObserver = new ResizeObserver((entries) => {
4565
- window.requestAnimationFrame(() => {
4566
- for (const entry of entries) {
4567
- this.currentWidgetWidth = entry.contentRect.width;
4568
- if (this.currentWidgetWidth > 0) {
4569
- this._updateAdaptiveLabelSize(this.currentWidgetWidth);
4570
- if (this.chartInstance) {
4571
- // SI ACTIVAMSO EL REFRESH DA UN REBOTE AL REDIMENCIONAR
4572
- this.chartInstance.refresh();
4573
- }
4600
+ onFilterSelectionChange(eventData) {
4601
+ const lsize = this.widgetsActivos.size;
4602
+ //Validar que ya se crearon y renderizaron los componentes graficos.
4603
+ if (lsize > 0) {
4604
+ const currentFilterController = this.dashboardFilterConfig.controlListConfig.find((x) => x.widgetId == eventData['widgetId']);
4605
+ if (currentFilterController) {
4606
+ currentFilterController.controlFilterData = eventData['eventResponse'];
4607
+ this.fnUpdateDataControllerBasedOnFilter(this.dashboardFilterConfig.controlListConfig);
4608
+ }
4609
+ }
4610
+ }
4611
+ fnUpdateDataControllerBasedOnFilter(filterControlListConfig) {
4612
+ let copyDashboardData = structuredClone(this.dashboardData);
4613
+ filterControlListConfig.forEach((item) => {
4614
+ let tableSelected = copyDashboardData.find((x) => x.tableName == item.controlTable);
4615
+ if (item.typeComponent == 'filter-slicer' && tableSelected && item.controlFilterData) {
4616
+ const fieldName = item.configComponent.filterField;
4617
+ if (item.configComponent.dataType == 'date') {
4618
+ const minDate = item.controlFilterData['startDate'];
4619
+ const maxDate = item.controlFilterData['endDate'];
4620
+ if (minDate && maxDate) {
4621
+ const filterData = tableSelected.tableData.filter((y) => (minDate <= y[fieldName] && y[fieldName] <= maxDate));
4622
+ tableSelected.tableData = filterData;
4574
4623
  }
4575
4624
  }
4576
- });
4577
- });
4578
- this._resizeObserver.observe(hostEl);
4579
- }
4580
- _updateAdaptiveLabelSize(width) {
4581
- if (this.config?.labelSize) {
4582
- const parsedSize = typeof this.config.labelSize === 'number'
4583
- ? this.config.labelSize
4584
- : parseFloat(this.config.labelSize);
4585
- if (!isNaN(parsedSize)) {
4586
- if (width < 260) {
4587
- const scale = Math.max(0.65, width / 260);
4588
- this.currentLabelSize = `${Math.round(parsedSize * scale)}px`;
4625
+ if (item.configComponent.dataType == 'number') {
4626
+ const minNum = Number(item.controlFilterData['minValue']);
4627
+ const maxNum = Number(item.controlFilterData['maxValue']);
4628
+ if (minNum && maxNum) {
4629
+ const filterData = tableSelected.tableData.filter((y) => (minNum <= y[fieldName] && y[fieldName] <= maxNum));
4630
+ tableSelected.tableData = filterData;
4631
+ }
4589
4632
  }
4590
- else {
4591
- this.currentLabelSize = `${parsedSize}px`;
4633
+ }
4634
+ if (item.typeComponent == 'filter-button' && tableSelected && item.controlFilterData) {
4635
+ const fieldName = item.configComponent.filterField;
4636
+ const listValues = item.controlFilterData || [];
4637
+ if (listValues && listValues.length > 0) {
4638
+ const filterData = tableSelected.tableData.filter((y) => (listValues.includes(y[fieldName])));
4639
+ tableSelected.tableData = filterData;
4592
4640
  }
4593
- this.cdr.markForCheck();
4594
- return;
4595
4641
  }
4596
- }
4597
- // Proporción estándar ideal (~7.5% del ancho del contenedor, acotado entre 13px y 26px)
4598
- const base = Math.max(13, Math.min(26, Math.round(width * 0.075)));
4599
- this.currentLabelSize = `${base}px`;
4600
- this.cdr.markForCheck();
4642
+ if (item.typeComponent == 'filter-list' && tableSelected && item.controlFilterData) {
4643
+ const fieldName = item.configComponent.filterField;
4644
+ if (item.configComponent.dataType == 'text') {
4645
+ const listValues = item.controlFilterData['ids'] || [];
4646
+ if (listValues && listValues.length > 0) {
4647
+ const listValues1D = listValues;
4648
+ const filterData = tableSelected.tableData.filter((y) => (listValues1D.includes(y[fieldName])));
4649
+ tableSelected.tableData = filterData;
4650
+ }
4651
+ }
4652
+ if (item.configComponent.dataType == 'date') {
4653
+ const minDate = item.controlFilterData['minDate'];
4654
+ const maxDate = item.controlFilterData['maxDate'];
4655
+ if (minDate && maxDate) {
4656
+ const filterData = tableSelected.tableData.filter((y) => (minDate <= y[fieldName] && y[fieldName] <= maxDate));
4657
+ tableSelected.tableData = filterData;
4658
+ }
4659
+ }
4660
+ }
4661
+ });
4662
+ //Actualizacion de Data Componentes Graficos
4663
+ this.dashboardConfig.controlListConfig.forEach((item) => {
4664
+ const controlData = this.fn_getDataForWidgetComponent(item.controlTables, copyDashboardData);
4665
+ this.eventUpdateWidgetControlData(item.widgetId, controlData);
4666
+ });
4601
4667
  }
4602
- refresh() {
4603
- this.updateParsedData();
4668
+ /* END - DRAWER */
4669
+ /* BEGIN - Eventos que provienen desde widget */
4670
+ eventRemoveWidgetById(widgetId) {
4671
+ // 1. Buscar el elemento HTML físico dentro de Gridstack usando su atributo interno [gs-id]
4672
+ const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4673
+ if (elementoHtml) {
4674
+ // 2. Remover el elemento de Gridstack (esto limpia el HTML y reordena el tablero si float es false)
4675
+ // El parámetro false indica que remueva el nodo del DOM pero no dispare eventos internos destructivos pesados
4676
+ this.grid.removeWidget(elementoHtml, false);
4677
+ }
4678
+ // 3. Buscar y destruir la instancia de Angular en memoria RAM
4679
+ const componentRef = this.widgetsActivos.get(widgetId);
4680
+ if (componentRef) {
4681
+ // Desengancha la vista del ciclo de renderizado global de Angular
4682
+ this.appRef.detachView(componentRef.hostView);
4683
+ // Destruye el componente y libera la memoria RAM
4684
+ componentRef.destroy();
4685
+ // Eliminamos el registro de nuestro Mapa
4686
+ this.widgetsActivos.delete(widgetId);
4687
+ }
4688
+ //console.log(`Widget ${widgetId} eliminado con éxito de Gridstack y Angular.`);
4604
4689
  }
4605
- // pointRender(args: any) {
4606
- // if (args.point?.y === 0) {
4607
- // args.fill = this.emptyPointSettings.fill;
4608
- // args.border = {
4609
- // width: this.emptyPointSettings.border?.width ?? 0,
4610
- // color: this.emptyPointSettings.border?.color ?? ''
4611
- // };
4612
- // }
4613
- // }
4614
- axisLabelRender(args) {
4615
- const umbralWidget = 700;
4616
- if (this.currentWidgetWidth > umbralWidget) {
4617
- return;
4690
+ eventToggleLockWidget(widgetId, shouldLock) {
4691
+ const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4692
+ if (elementoHtml) {
4693
+ // La API de Gridstack nos permite congelar un nodo específico pasándole true/false
4694
+ this.grid.movable(elementoHtml, !shouldLock);
4695
+ this.grid.resizable(elementoHtml, !shouldLock);
4696
+ //console.log(`Widget ${widgetId} cambiado a estado bloqueado: ${shouldLock}`);
4618
4697
  }
4619
- // Si la tarjeta es estrecha, aplicamos el recorte de etiquetas
4620
- if (args.text && typeof args.text === 'string') {
4621
- const palabras = args.text.split(' ');
4622
- if (palabras.length > 1) {
4623
- const primeraPalabra = palabras[0];
4624
- const inicialSegunda = palabras[1].charAt(0).toUpperCase();
4625
- args.text = `${primeraPalabra} ${inicialSegunda}.`;
4698
+ }
4699
+ eventToggleMaximizeWidget(widgetId, shouldMaximize) {
4700
+ const elementoHtml = this.gridContainer.nativeElement.querySelector(`[gs-id="${widgetId}"]`);
4701
+ if (elementoHtml) {
4702
+ if (shouldMaximize) {
4703
+ // Aplicamos estilos CSS nativos para forzar al contenedor a flotar sobre todo el dashboard
4704
+ elementoHtml.classList.add('widget-maximized');
4705
+ // Deshabilitamos temporalmente que Gridstack intente moverlo mientras está maximizado
4706
+ this.grid.movable(elementoHtml, false);
4707
+ this.grid.resizable(elementoHtml, false);
4626
4708
  }
4627
4709
  else {
4628
- if (args.text.length > 10) {
4629
- args.text = args.text.substring(0, 9) + '.';
4630
- }
4710
+ elementoHtml.classList.remove('widget-maximized');
4711
+ // Restauramos los permisos normales leyendo si el componente de Angular estaba bloqueado previamente
4712
+ const componentRef = this.widgetsActivos.get(widgetId);
4713
+ const isCurrentlyLocked = componentRef?.instance.isLocked() || false;
4714
+ this.grid.movable(elementoHtml, !isCurrentlyLocked);
4715
+ this.grid.resizable(elementoHtml, !isCurrentlyLocked);
4631
4716
  }
4717
+ // Forzamos un evento resize simulado para que los gráficos de Syncfusion recalculen su nuevo tamaño gigante/pequeño
4718
+ setTimeout(() => {
4719
+ window.dispatchEvent(new Event('resize'));
4720
+ }, 50);
4632
4721
  }
4633
4722
  }
4634
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartSyncfusionComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
4635
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: BubbleChartSyncfusionComponent, isStandalone: true, selector: "yw-bubble-chart-syncfusion", inputs: { chartData: "chartData", config: "config", idComponent: "idComponent" }, providers: [BubbleSeriesService, CategoryService, CategoryService,
4636
- TooltipService,
4637
- LegendService,
4638
- DateTimeService], viewQueries: [{ propertyName: "chartInstance", first: true, predicate: ["chart"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"control-section\">\r\n\r\n <ejs-chart \r\n #chart \r\n [primaryXAxis]=\"labelData.axisX\" \r\n [primaryYAxis]=\"labelData.axisY\" \r\n [title]=\"labelData.title\" \r\n (pointRender)=\"pointRender($event)\" \r\n height=\"100%\"\r\n width=\"100%\"\r\n [legendSettings]=\"labelData.legendSettings\"\r\n (axisLabelRender)=\"axisLabelRender($event)\"\r\n\r\n [tooltip]=\"labelData.tooltip\">\r\n <e-series-collection>\r\n @for(series of dataChartParse; track $index){\r\n <e-series \r\n [opacity]=\"series.opacity\"\r\n [dataSource]='series.dataSource' \r\n type='Bubble' \r\n xName='x' \r\n yName='y' \r\n size='size' \r\n [name]='series.name'>\r\n </e-series>\r\n\r\n }\r\n </e-series-collection>\r\n</ejs-chart>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartModule }, { kind: "component", type: i1.ChartComponent, selector: "ejs-chart", inputs: ["accessibility", "allowExport", "allowMultiSelection", "annotations", "axes", "background", "backgroundImage", "border", "chartArea", "columns", "crosshair", "currencyCode", "dataSource", "description", "enableAnimation", "enableAutoIntervalOnBothAxis", "enableCanvas", "enableExport", "enableHtmlSanitizer", "enablePersistence", "enableRtl", "enableSideBySidePlacement", "focusBorderColor", "focusBorderMargin", "focusBorderWidth", "height", "highlightColor", "highlightMode", "highlightPattern", "indicators", "isMultiSelect", "isTransposed", "legendSettings", "locale", "margin", "palettes", "primaryXAxis", "primaryYAxis", "rangeColorSettings", "rows", "selectedDataIndexes", "selectionMode", "selectionPattern", "series", "subTitle", "subTitleStyle", "tabIndex", "theme", "title", "titleStyle", "tooltip", "useGroupingSeparator", "width", "zoomSettings"], outputs: ["afterExport", "animationComplete", "annotationRender", "axisLabelClick", "axisLabelRender", "axisMultiLabelRender", "axisRangeCalculated", "beforeExport", "beforePrint", "beforeResize", "chartDoubleClick", "chartMouseClick", "chartMouseDown", "chartMouseLeave", "chartMouseMove", "chartMouseUp", "drag", "dragComplete", "dragEnd", "dragStart", "legendClick", "legendRender", "load", "loaded", "multiLevelLabelClick", "onZooming", "pointClick", "pointDoubleClick", "pointMove", "pointRender", "resized", "scrollChanged", "scrollEnd", "scrollStart", "selectionComplete", "seriesRender", "sharedTooltipRender", "textRender", "tooltipRender", "zoomComplete", "dataSourceChange"] }, { kind: "directive", type: i1.SeriesDirective, selector: "e-series-collection>e-series", inputs: ["accessibility", "animation", "bearFillColor", "binInterval", "border", "boxPlotMode", "bullFillColor", "cardinalSplineTension", "close", "colorName", "columnFacet", "columnSpacing", "columnWidth", "columnWidthInPixel", "connector", "cornerRadius", "dashArray", "dataSource", "dragSettings", "drawType", "emptyPointSettings", "enableComplexProperty", "enableSolidCandles", "enableTooltip", "errorBar", "fill", "groupName", "high", "intermediateSumIndexes", "isClosed", "legendImageUrl", "legendShape", "low", "marker", "maxRadius", "minRadius", "name", "negativeFillColor", "noRisers", "nonHighlightStyle", "opacity", "open", "paretoOptions", "pointColorMapping", "query", "segmentAxis", "segments", "selectionStyle", "showMean", "showNormalDistribution", "size", "splineType", "stackingGroup", "step", "sumIndexes", "summaryFillColor", "tooltipFormat", "tooltipMappingName", "trendlines", "type", "unSelectedStyle", "visible", "volume", "width", "xAxisName", "xName", "yAxisName", "yName", "zOrder"] }, { kind: "directive", type: i1.SeriesCollectionDirective, selector: "ej-chart>e-series-collection" }] }); }
4639
- }
4640
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartSyncfusionComponent, decorators: [{
4641
- type: Component,
4642
- args: [{ selector: 'yw-bubble-chart-syncfusion', standalone: true, imports: [CommonModule, ChartModule], providers: [BubbleSeriesService, CategoryService, CategoryService,
4643
- TooltipService,
4644
- LegendService,
4645
- DateTimeService], template: "<div class=\"control-section\">\r\n\r\n <ejs-chart \r\n #chart \r\n [primaryXAxis]=\"labelData.axisX\" \r\n [primaryYAxis]=\"labelData.axisY\" \r\n [title]=\"labelData.title\" \r\n (pointRender)=\"pointRender($event)\" \r\n height=\"100%\"\r\n width=\"100%\"\r\n [legendSettings]=\"labelData.legendSettings\"\r\n (axisLabelRender)=\"axisLabelRender($event)\"\r\n\r\n [tooltip]=\"labelData.tooltip\">\r\n <e-series-collection>\r\n @for(series of dataChartParse; track $index){\r\n <e-series \r\n [opacity]=\"series.opacity\"\r\n [dataSource]='series.dataSource' \r\n type='Bubble' \r\n xName='x' \r\n yName='y' \r\n size='size' \r\n [name]='series.name'>\r\n </e-series>\r\n\r\n }\r\n </e-series-collection>\r\n</ejs-chart>\r\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden}.control-section{display:flex;flex-direction:column;width:100%;height:100%;min-height:260px;box-sizing:border-box;overflow:hidden;background:#fff;border-radius:8px}:host ::ng-deep ejs-circulargauge{display:block;width:100%!important;height:100%!important;flex-grow:1}\n"] }]
4646
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { chartInstance: [{
4647
- type: ViewChild,
4648
- args: ['chart']
4649
- }], chartData: [{
4650
- type: Input,
4651
- args: ['chartData']
4652
- }], config: [{
4653
- type: Input,
4654
- args: ['config']
4655
- }], idComponent: [{
4656
- type: Input,
4657
- args: ['idComponent']
4658
- }] } });
4659
-
4660
- // import { LineChartSyncfusionComponent } from '../../../../../adapters/charts/line-chart/syncfusion/visual/yw-line-chart-syncfusion.component';
4661
- // import { YwLineMappingConfig } from '../../../../../models/config/chart/yw-line-chart-config.interface';
4662
- class BubbleChartComponent {
4663
- constructor() {
4664
- this.idComponent = '';
4665
- }
4666
- ngOnChanges(changes) {
4667
- if (changes['chartData']) {
4668
- console.log('change chartData:', changes['chartData'].currentValue);
4669
- }
4670
- if (changes['config']) {
4671
- console.log('change config:', changes['config'].currentValue);
4672
- }
4723
+ eventShowFormConfigWidget(widgetId) {
4724
+ this.drawerCurrentWidgetObject['widgetId'] = widgetId;
4725
+ this.drawerconfigvisible = true;
4673
4726
  }
4674
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4675
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: BubbleChartComponent, isStandalone: true, selector: "yw-bubble-chart", inputs: { chartData: "chartData", config: "config", idComponent: "idComponent" }, usesOnChanges: true, ngImport: i0, template: "llegamos\r\n@if(config.provider==='syncfusion'){\r\n <div style=\"position: relative; width: 100%; height: 100%;\">\r\n <yw-bubble-chart-syncfusion \r\n [idComponent]=\"idComponent\"\r\n [chartData]=\"chartData\" \r\n [config]=\"config\" style=\"display: block; width: 100%; height: 100%;\">\r\n </yw-bubble-chart-syncfusion>\r\n</div>\r\n}", styles: [":host{display:block;width:100%;height:100%;min-height:260px;min-width:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BubbleChartSyncfusionComponent, selector: "yw-bubble-chart-syncfusion", inputs: ["chartData", "config", "idComponent"] }] }); }
4727
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardComponent, deps: [{ token: i0.EnvironmentInjector }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component }); }
4728
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: YwDashboardComponent, isStandalone: true, selector: "yw-dashboard", inputs: { showControls: { classPropertyName: "showControls", publicName: "showControls", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, dashboardStyle: { classPropertyName: "dashboardStyle", publicName: "dashboardStyle", isSignal: false, isRequired: false, transformFunction: null }, dashboardConfig: { classPropertyName: "dashboardConfig", publicName: "dashboardConfig", isSignal: false, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: false, isRequired: false, transformFunction: null }, dashboardFilterConfig: { classPropertyName: "dashboardFilterConfig", publicName: "dashboardFilterConfig", isSignal: false, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "gridContainer", first: true, predicate: ["gridContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- <div class=\"controls\" [hidden]=\"!showControls()\">\r\n <button (click)=\"saveLayoutToJSON()\">Guardar JSON en Consola</button>\r\n <button (click)=\"fn_btn_addWidget()\">add widget</button>\r\n <button (click)=\"fn_btn_lockPositionWidgets()\">Lock widgets</button>\r\n <button (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\">\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n</div> -->\r\n\r\n<div class=\"controls-container\" >\r\n @if ( showControls() ) { \r\n <!-- Botones principales con nz-button y tipos de Ant Design -->\r\n <button nz-button nzType=\"default\" (click)=\"saveLayoutToJSON()\" >\r\n <span nz-icon nz-type=\"code\"></span> Guardar JSON\r\n </button>\r\n\r\n <button nz-button nzType=\"primary\" (click)=\"fn_btn_addWidget()\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n\r\n <button nz-button [nzType]=\"isLocked ? 'primary' : 'default'\" nzDanger (click)=\"fn_btn_lockPositionWidgets()\" >\r\n <span nz-icon [nzType]=\"isLocked ? 'lock' : 'unlock'\"></span> \r\n {{ isLocked ? 'Bloqueado' : 'Bloquear' }}\r\n </button>\r\n }\r\n @if ( showFilter() ) { \r\n <!-- Bot\u00F3n de filtro con estilo ic\u00F3nico -->\r\n <button nz-button nzType=\"default\" nzShape=\"circle\" (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\" [class.active]=\"drawerfiltervisible\" >\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n }\r\n</div>\r\n<div #gridContainer class=\"grid-stack canvas-container\"\r\n [style.--canvas-bg]=\"dashboardStyle.canvasBackgroundColor\"\r\n></div>\r\n\r\n\r\n\r\n\r\n<!-- DRAWER - FILTER -->\r\n\r\n<nz-drawer\r\n[nzVisible]=\"drawerfiltervisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormFilterDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>Filtros</h6>\r\n <yw-dashboard-filter\r\n [dashboardFilterConfig]=\"dashboardFilterConfig\"\r\n [dashboardData]=\"dashboardData\"\r\n (filterSelectionChange)=\"onFilterSelectionChange($event)\"\r\n >\r\n </yw-dashboard-filter>\r\n </div>\r\n\r\n <!--\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"Secion de Filtros\">\r\n <div class=\"controls\">\r\n aqui botones de control:\r\n\r\n </div>\r\n <div>\r\n \r\n\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n-->\r\n</ng-container>\r\n</nz-drawer>\r\n\r\n\r\n\r\n<!-- DRAWER - CONFIGURACION -->\r\n<nz-drawer\r\n[nzVisible]=\"drawerconfigvisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>{{ drawerCurrentWidgetObject.widgetId }}</h6>\r\n </div>\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"{{ drawerCurrentWidgetObject.widgetId }}\">\r\n <div class=\"controls\">\r\n <button (click)=\"fn_btn_updateWidgetControlConfig(drawerCurrentWidgetObject.widgetId)\">Actualiza Configuracion</button>\r\n <button (click)=\"fn_btn_updateWidgetControlData(drawerCurrentWidgetObject.widgetId)\">Actualiza Data</button> \r\n </div>\r\n <div>\r\n bla bla bla\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n</ng-container>\r\n</nz-drawer>", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.canvas-container{background-color:var(--canvas-bg, #f5f5f5);min-height:500px;padding:10px}::vdeep .card-inner{background:#fff;border:1px solid #ccc;height:100%;display:flex;align-items:center;justify-content:center}:host ::ng-deep .grid-stack-item-content{border-radius:6px;box-shadow:0 2px 5px #0000001a;display:flex;align-items:center;justify-content:center;font-weight:700;overflow:hidden}::ng-deep .grid-stack-item.widget-maximized{position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;z-index:9999!important;transform:none!important;margin:0!important;padding:5px}::ng-deep .grid-stack-item.widget-maximized .grid-stack-item-content{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;margin:0!important;padding:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;background-color:#fff!important;overflow:hidden}::ng-deep .grid-stack>.grid-stack-item>.ui-resizable-handle{opacity:0!important;background-image:none!important}.widget-details-info{padding-top:30px;text-align:center}.widget-details-info h6{font-family:manrope,sans-serif;font-size:20px;font-weight:700;line-height:26px;color:#2d3144}.widget-details-info p{font-family:manrope,sans-serif;font-size:14px;font-weight:400;line-height:21px;color:#4b5064}.controls-container{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:12px 16px;background:#fff;border-bottom:1px solid #f0f0f0;box-shadow:0 2px 8px #0000000a;border-radius:6px;margin-bottom:16px;width:100%;box-sizing:border-box;position:sticky;top:0}.controls-container button{white-space:nowrap}.controls-container .btn-filtro{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.controls-container .btn-filtro.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.controls-container .btn-filtro.active:hover{background-color:#40a9ff;border-color:#40a9ff}@media(max-width:576px){.controls-container{gap:8px;padding:8px 10px}.controls-container button[nz-button]{font-size:13px;padding:0 10px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: NzDrawerModule }, { kind: "component", type: i2.NzDrawerComponent, selector: "nz-drawer", inputs: ["nzContent", "nzCloseIcon", "nzClosable", "nzMaskClosable", "nzMask", "nzCloseOnNavigation", "nzNoAnimation", "nzKeyboard", "nzTitle", "nzExtra", "nzFooter", "nzPlacement", "nzSize", "nzMaskStyle", "nzBodyStyle", "nzWrapClassName", "nzWidth", "nzHeight", "nzZIndex", "nzOffsetX", "nzOffsetY", "nzVisible"], outputs: ["nzOnViewInit", "nzOnClose", "nzVisibleChange"], exportAs: ["nzDrawer"] }, { kind: "directive", type: i2.NzDrawerContentDirective, selector: "[nzDrawerContent]", exportAs: ["nzDrawerContent"] }, { kind: "ngmodule", type: NzTabsModule }, { kind: "component", type: i3.NzTabsComponent, selector: "nz-tabs,nz-tabset", inputs: ["nzSelectedIndex", "nzTabPosition", "nzTabBarExtraContent", "nzCanDeactivate", "nzAddIcon", "nzTabBarStyle", "nzType", "nzSize", "nzAnimated", "nzTabBarGutter", "nzHideAdd", "nzCentered", "nzHideAll", "nzLinkRouter", "nzLinkExact", "nzDestroyInactiveTabPane"], outputs: ["nzSelectChange", "nzSelectedIndexChange", "nzTabListScroll", "nzClose", "nzAdd"], exportAs: ["nzTabs"] }, { kind: "component", type: i3.NzTabComponent, selector: "nz-tab", inputs: ["nzTitle", "nzClosable", "nzCloseIcon", "nzDisabled", "nzForceRender"], outputs: ["nzSelect", "nzDeselect", "nzClick", "nzContextmenu"], exportAs: ["nzTab"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i1$3.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i5.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i6.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i7.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "component", type: YwDashboardFilterComponent, selector: "yw-dashboard-filter", inputs: ["dashboardFilterConfig", "dashboardData"], outputs: ["filterSelectionChange"] }] }); }
4676
4729
  }
4677
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: BubbleChartComponent, decorators: [{
4730
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: YwDashboardComponent, decorators: [{
4678
4731
  type: Component,
4679
- args: [{ selector: 'yw-bubble-chart', standalone: true, imports: [CommonModule, BubbleChartSyncfusionComponent], template: "llegamos\r\n@if(config.provider==='syncfusion'){\r\n <div style=\"position: relative; width: 100%; height: 100%;\">\r\n <yw-bubble-chart-syncfusion \r\n [idComponent]=\"idComponent\"\r\n [chartData]=\"chartData\" \r\n [config]=\"config\" style=\"display: block; width: 100%; height: 100%;\">\r\n </yw-bubble-chart-syncfusion>\r\n</div>\r\n}", styles: [":host{display:block;width:100%;height:100%;min-height:260px;min-width:0;overflow:hidden}\n"] }]
4680
- }], propDecorators: { chartData: [{
4681
- type: Input,
4682
- args: ['chartData']
4683
- }], config: [{
4684
- type: Input,
4685
- args: ['config']
4686
- }], idComponent: [{
4687
- type: Input,
4688
- args: ['idComponent']
4732
+ args: [{ selector: 'yw-dashboard', standalone: true, imports: [CommonModule,
4733
+ YwDashboardWidgetComponent,
4734
+ NzDrawerModule,
4735
+ NzTabsModule,
4736
+ YwBaseSlicerComponent,
4737
+ NzIconModule,
4738
+ NzButtonModule,
4739
+ YwButtonSlicerComponent,
4740
+ YwListSlicerComponent,
4741
+ YwDashboardFilterComponent
4742
+ ], template: "<!-- <div class=\"controls\" [hidden]=\"!showControls()\">\r\n <button (click)=\"saveLayoutToJSON()\">Guardar JSON en Consola</button>\r\n <button (click)=\"fn_btn_addWidget()\">add widget</button>\r\n <button (click)=\"fn_btn_lockPositionWidgets()\">Lock widgets</button>\r\n <button (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\">\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"20\" height=\"20\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n</div> -->\r\n\r\n<div class=\"controls-container\" >\r\n @if ( showControls() ) { \r\n <!-- Botones principales con nz-button y tipos de Ant Design -->\r\n <button nz-button nzType=\"default\" (click)=\"saveLayoutToJSON()\" >\r\n <span nz-icon nz-type=\"code\"></span> Guardar JSON\r\n </button>\r\n\r\n <button nz-button nzType=\"primary\" (click)=\"fn_btn_addWidget()\" >\r\n <span nz-icon nz-type=\"plus\"></span> A\u00F1adir widget\r\n </button>\r\n\r\n <button nz-button [nzType]=\"isLocked ? 'primary' : 'default'\" nzDanger (click)=\"fn_btn_lockPositionWidgets()\" >\r\n <span nz-icon [nzType]=\"isLocked ? 'lock' : 'unlock'\"></span> \r\n {{ isLocked ? 'Bloqueado' : 'Bloquear' }}\r\n </button>\r\n }\r\n @if ( showFilter() ) { \r\n <!-- Bot\u00F3n de filtro con estilo ic\u00F3nico -->\r\n <button nz-button nzType=\"default\" nzShape=\"circle\" (click)=\"handleShowFormFilterDrawer()\" class=\"btn-filtro\" [class.active]=\"drawerfiltervisible\" >\r\n <svg *ngIf=\"drawerfiltervisible; else filtroInactivo\" \r\n xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"currentColor\">\r\n <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\"/>\r\n </svg>\r\n <ng-template #filtroInactivo>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" \r\n width=\"16\" height=\"16\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\">\r\n <polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/>\r\n </svg>\r\n </ng-template>\r\n </button>\r\n }\r\n</div>\r\n<div #gridContainer class=\"grid-stack canvas-container\"\r\n [style.--canvas-bg]=\"dashboardStyle.canvasBackgroundColor\"\r\n></div>\r\n\r\n\r\n\r\n\r\n<!-- DRAWER - FILTER -->\r\n\r\n<nz-drawer\r\n[nzVisible]=\"drawerfiltervisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormFilterDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>Filtros</h6>\r\n <yw-dashboard-filter\r\n [dashboardFilterConfig]=\"dashboardFilterConfig\"\r\n [dashboardData]=\"dashboardData\"\r\n (filterSelectionChange)=\"onFilterSelectionChange($event)\"\r\n >\r\n </yw-dashboard-filter>\r\n </div>\r\n\r\n <!--\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"Secion de Filtros\">\r\n <div class=\"controls\">\r\n aqui botones de control:\r\n\r\n </div>\r\n <div>\r\n \r\n\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n-->\r\n</ng-container>\r\n</nz-drawer>\r\n\r\n\r\n\r\n<!-- DRAWER - CONFIGURACION -->\r\n<nz-drawer\r\n[nzVisible]=\"drawerconfigvisible\"\r\n[nzMaskClosable]=\"false\"\r\nnzPlacement=\"right\"\r\n[nzWidth]=\"fnGetDrawerWidth(50)\"\r\n(nzOnClose)=\"handleCloseFormDrawer()\"\r\n>\r\n<ng-container *nzDrawerContent>\r\n <div class=\"widget-details-info\">\r\n <h6 nz-typography>{{ drawerCurrentWidgetObject.widgetId }}</h6>\r\n </div>\r\n <nz-tabset nzCentered>\r\n <nz-tab nzTitle=\"{{ drawerCurrentWidgetObject.widgetId }}\">\r\n <div class=\"controls\">\r\n <button (click)=\"fn_btn_updateWidgetControlConfig(drawerCurrentWidgetObject.widgetId)\">Actualiza Configuracion</button>\r\n <button (click)=\"fn_btn_updateWidgetControlData(drawerCurrentWidgetObject.widgetId)\">Actualiza Data</button> \r\n </div>\r\n <div>\r\n bla bla bla\r\n </div>\r\n\r\n </nz-tab>\r\n \r\n </nz-tabset>\r\n</ng-container>\r\n</nz-drawer>", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.canvas-container{background-color:var(--canvas-bg, #f5f5f5);min-height:500px;padding:10px}::vdeep .card-inner{background:#fff;border:1px solid #ccc;height:100%;display:flex;align-items:center;justify-content:center}:host ::ng-deep .grid-stack-item-content{border-radius:6px;box-shadow:0 2px 5px #0000001a;display:flex;align-items:center;justify-content:center;font-weight:700;overflow:hidden}::ng-deep .grid-stack-item.widget-maximized{position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;z-index:9999!important;transform:none!important;margin:0!important;padding:5px}::ng-deep .grid-stack-item.widget-maximized .grid-stack-item-content{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;margin:0!important;padding:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;background-color:#fff!important;overflow:hidden}::ng-deep .grid-stack>.grid-stack-item>.ui-resizable-handle{opacity:0!important;background-image:none!important}.widget-details-info{padding-top:30px;text-align:center}.widget-details-info h6{font-family:manrope,sans-serif;font-size:20px;font-weight:700;line-height:26px;color:#2d3144}.widget-details-info p{font-family:manrope,sans-serif;font-size:14px;font-weight:400;line-height:21px;color:#4b5064}.controls-container{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:12px 16px;background:#fff;border-bottom:1px solid #f0f0f0;box-shadow:0 2px 8px #0000000a;border-radius:6px;margin-bottom:16px;width:100%;box-sizing:border-box;position:sticky;top:0}.controls-container button{white-space:nowrap}.controls-container .btn-filtro{display:inline-flex;align-items:center;justify-content:center;padding:0;width:32px;height:32px;border-radius:50%;transition:all .3s ease}.controls-container .btn-filtro.active{color:#fff;background-color:#1890ff;border-color:#1890ff}.controls-container .btn-filtro.active:hover{background-color:#40a9ff;border-color:#40a9ff}@media(max-width:576px){.controls-container{gap:8px;padding:8px 10px}.controls-container button[nz-button]{font-size:13px;padding:0 10px}}\n"] }]
4743
+ }], ctorParameters: () => [{ type: i0.EnvironmentInjector }, { type: i0.ApplicationRef }], propDecorators: { gridContainer: [{
4744
+ type: ViewChild,
4745
+ args: ['gridContainer', { static: true }]
4746
+ }], showControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "showControls", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], dashboardStyle: [{
4747
+ type: Input
4748
+ }], dashboardConfig: [{
4749
+ type: Input
4750
+ }], dashboardData: [{
4751
+ type: Input
4752
+ }], dashboardFilterConfig: [{
4753
+ type: Input
4689
4754
  }] } });
4690
4755
 
4691
4756
  // Configuraciones especificas de pie chart -> acumulation chart en syncfusion