@c8y/ngx-components 1023.82.8 → 1023.83.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -456,6 +456,7 @@ class EchartsOptionsService {
456
456
  this.severityLabelPipe = severityLabelPipe;
457
457
  this.translate = translate;
458
458
  this.router = router;
459
+ this.highlightedSeriesName = null;
459
460
  this.AGGREGATED_SERIES_TYPE = 'aggr';
460
461
  this.TOOLTIP_WIDTH = 300;
461
462
  }
@@ -828,8 +829,11 @@ class EchartsOptionsService {
828
829
  const XAxisValue = tooltipParams[0].data[0];
829
830
  const YAxisReadings = [];
830
831
  const allSeries = this.echartsInstance?.getOption()['series'];
831
- // filter out alarm and event series
832
- const allDataPointSeries = allSeries.filter(series => series['typeOfSeries'] !== 'alarm' && series['typeOfSeries'] !== 'event');
832
+ const legendSelected = this.echartsInstance?.getOption()['legend']?.[0]?.selected;
833
+ // filter out alarm, event, and hidden series
834
+ const allDataPointSeries = allSeries.filter(series => series['typeOfSeries'] !== 'alarm' &&
835
+ series['typeOfSeries'] !== 'event' &&
836
+ legendSelected?.[series['name']] !== false);
833
837
  this.processSeries(allDataPointSeries, XAxisValue, YAxisReadings, displayOptions);
834
838
  // find event and alarm of the same type as the hovered markedLine or markedPoint
835
839
  const event = allEvents.find(e => e.type === params.data.itemType && params.data.value === e.time);
@@ -1464,7 +1468,7 @@ class EchartsOptionsService {
1464
1468
  data,
1465
1469
  ...(displayOptions.forceMergeDatapoints ? {} : { yAxisIndex: idx }),
1466
1470
  ...this.chartTypesService.getSeriesOptions(dp, isMinMaxChart, renderType),
1467
- ...(isLine && { smooth: displayOptions.smoothLines ?? false })
1471
+ ...(isLine && { smooth: displayOptions.smoothLines ?? false, triggerLineEvent: true })
1468
1472
  };
1469
1473
  }
1470
1474
  /**
@@ -1480,9 +1484,11 @@ class EchartsOptionsService {
1480
1484
  const XAxisValue = data[0];
1481
1485
  const YAxisReadings = [];
1482
1486
  const allSeries = this.echartsInstance?.getOption()['series'];
1487
+ const legendSelected = this.echartsInstance?.getOption()['legend']?.[0]?.selected;
1483
1488
  const allDataPointSeries = allSeries.filter(series => series['typeOfSeries'] !== 'alarm' &&
1484
1489
  series['typeOfSeries'] !== 'event' &&
1485
- series['typeOfSeries'] !== 'fake');
1490
+ series['typeOfSeries'] !== 'fake' &&
1491
+ legendSelected?.[series['name']] !== false);
1486
1492
  allDataPointSeries.forEach((series) => {
1487
1493
  let value;
1488
1494
  const id = series['id'];
@@ -1527,10 +1533,12 @@ class EchartsOptionsService {
1527
1533
  `</div></div>`;
1528
1534
  }
1529
1535
  const itemStyle = series['itemStyle'];
1530
- YAxisReadings.push(`<div class="d-flex a-i-center p-b-8 text-default"><span class='dlt-c8y-icon-circle m-r-4' style='color: ${echarts.format.encodeHTML(itemStyle.color)}'></span>` + // color circle
1536
+ const isDimmed = this.highlightedSeriesName !== null && series['name'] !== this.highlightedSeriesName;
1537
+ YAxisReadings.push(`<div style="opacity: ${isDimmed ? 0.4 : 1}">` +
1538
+ `<div class="d-flex a-i-center p-b-8 text-default"><span class='dlt-c8y-icon-circle m-r-4' style='color: ${echarts.format.encodeHTML(itemStyle.color)}'></span>` + // color circle
1531
1539
  `<strong class="text-truncate">${echarts.format.encodeHTML(series['datapointLabel'])}</strong></div>` + // name
1532
- `${value}` // single value or min-max range
1533
- );
1540
+ `${value}` + // single value or min-max range
1541
+ `</div>`);
1534
1542
  });
1535
1543
  return `<div inner-e-cbg-component style="width: ${this.TOOLTIP_WIDTH}px">${YAxisReadings.join('')}</div>`;
1536
1544
  };
@@ -2151,6 +2159,8 @@ class ChartsComponent {
2151
2159
  this.isChangeFromZoom = false;
2152
2160
  /** Tracks if the slider is being dragged — suppresses config updates mid-drag */
2153
2161
  this.isSliderDragging = false;
2162
+ /** Tracks the currently highlighted series name, null when nothing is highlighted */
2163
+ this.highlightedSeriesName = null;
2154
2164
  this.chartOption$ = this.configChangedSubject.pipe(tap(() => (this.isFetching = true)), switchMap(() => this.loadAlarmsAndEvents()), switchMap(() => this.fetchSeriesForDatapoints$()), switchMap((datapointsWithValues) => {
2155
2165
  if (datapointsWithValues.length === 0 && this.config.alarmsEventsConfigs?.length === 0) {
2156
2166
  this.echartsInstance?.clear();
@@ -2160,22 +2170,28 @@ class ChartsComponent {
2160
2170
  // Use padded range since data is fetched with 60s padding
2161
2171
  this.loadedTimeRange = { ...this.getTimeRange(60_000) };
2162
2172
  return this.getChartOptions(datapointsWithValues);
2163
- }), tap(v => {
2173
+ }), tap(options => {
2164
2174
  this.isFetching = false;
2165
2175
  this.finishLoading.emit(false);
2166
- if (v['empty']) {
2176
+ if (options['empty']) {
2167
2177
  return;
2168
2178
  }
2169
2179
  this.chartRealtimeService.stopRealtime();
2170
2180
  this.startRealtimeIfPossible();
2171
2181
  if (this.echartsInstance) {
2182
+ // If a series is highlighted, set the opacity into the chart options before
2183
+ // rendering so the lazy setOption already carries the correct visual state.
2184
+ // This avoids any async race — the render itself is the source of truth.
2185
+ if (this.highlightedSeriesName) {
2186
+ this.applyHighlightToChartOptions(options, this.highlightedSeriesName);
2187
+ }
2172
2188
  /**
2173
2189
  * * Set the chart options
2174
2190
  * First parameter is the options to set
2175
2191
  * Second parameter is a boolean to indicate that the chart should be re-rendered as we do not want to merge.
2176
2192
  * Third parameter is a boolean to indicate that we want to lazy load the chart.
2177
2193
  */
2178
- this.echartsInstance.setOption(v, true, true);
2194
+ this.echartsInstance.setOption(options, true, true);
2179
2195
  }
2180
2196
  }));
2181
2197
  this.widgetTimeContextDateRangeService.fullReload$
@@ -2315,6 +2331,12 @@ class ChartsComponent {
2315
2331
  tooltip: { triggerOn: 'mousemove' },
2316
2332
  series: [{ markArea: { data: [] }, markLine: { data: [] } }]
2317
2333
  });
2334
+ if (params.componentType === 'series' && params.seriesName) {
2335
+ this.toggleStickyHighlight(params.seriesName);
2336
+ }
2337
+ else {
2338
+ this.setStickyHighlight(null);
2339
+ }
2318
2340
  return;
2319
2341
  }
2320
2342
  const clickedAlarms = this.alarms.filter(alarm => alarm.type === params.data.itemType);
@@ -2352,8 +2374,8 @@ class ChartsComponent {
2352
2374
  this.echartsInstance.setOption(updatedOptions);
2353
2375
  }
2354
2376
  isAlarmOrEventClick(params) {
2355
- return (this.alarms.some(alarm => alarm.type === params.data.itemType) ||
2356
- this.events.some(event => event.type === params.data.itemType));
2377
+ return (this.alarms.some(alarm => alarm.type === params.data?.itemType) ||
2378
+ this.events.some(event => event.type === params.data?.itemType));
2357
2379
  }
2358
2380
  hasMarkArea(options) {
2359
2381
  return options?.series?.[0]?.markArea?.data?.length > 0;
@@ -2461,15 +2483,91 @@ class ChartsComponent {
2461
2483
  }
2462
2484
  const type = alarmOrEvent.filters.type;
2463
2485
  const actionType = alarmOrEvent.__hidden ? 'legendUnSelect' : 'legendSelect';
2464
- const matchingSeries = allSeries.filter(s => s.name?.startsWith(`${type}/`) &&
2465
- (s.name?.endsWith('-markLine') || s.name?.endsWith('-markPoint')));
2486
+ const matchingSeries = allSeries.filter(s => typeof s.name === 'string' &&
2487
+ s.name.startsWith(`${type}/`) &&
2488
+ (s.name.endsWith('-markLine') || s.name.endsWith('-markPoint')));
2466
2489
  matchingSeries.forEach(series => {
2490
+ if (typeof series.name !== 'string') {
2491
+ return;
2492
+ }
2467
2493
  this.echartsInstance.dispatchAction({
2468
2494
  type: actionType,
2469
2495
  name: series.name
2470
2496
  });
2471
2497
  });
2472
2498
  }
2499
+ getDataSeriesNames() {
2500
+ const series = this.echartsInstance.getOption().series;
2501
+ const allSeries = Array.isArray(series) ? series : [series];
2502
+ return allSeries
2503
+ .filter(s => s.name &&
2504
+ s['typeOfSeries'] !== 'alarm' &&
2505
+ s['typeOfSeries'] !== 'event' &&
2506
+ s['typeOfSeries'] !== 'fake' &&
2507
+ s['datapointId'] !== 'aggregated')
2508
+ .map(s => s.name);
2509
+ }
2510
+ /**
2511
+ * Applies highlight opacity directly to an EChartsOption object's series array.
2512
+ * Used to set the highlight into chart options before setOption.
2513
+ * @param options - The chart options object to mutate in-place.
2514
+ * @param highlightedName - The series name that should remain at full opacity.
2515
+ */
2516
+ applyHighlightToChartOptions(options, highlightedName) {
2517
+ if (!Array.isArray(options.series)) {
2518
+ return;
2519
+ }
2520
+ const series = options.series;
2521
+ const dataSeriesNames = this.getDataSeriesNames();
2522
+ series.forEach(s => {
2523
+ const updatedSeries = this.changeSeriesOpacity(dataSeriesNames, highlightedName, s);
2524
+ Object.assign(s, updatedSeries);
2525
+ });
2526
+ }
2527
+ /**
2528
+ * Applies or removes a sticky highlight by directly setting opacity on series styles.
2529
+ * @param seriesName - The series to pin as highlighted, or null to restore all.
2530
+ */
2531
+ setStickyHighlight(seriesName) {
2532
+ this.highlightedSeriesName = seriesName;
2533
+ this.echartsOptionsService.highlightedSeriesName = seriesName;
2534
+ const allSeries = this.echartsInstance.getOption().series;
2535
+ const dataSeriesNames = this.getDataSeriesNames();
2536
+ this.echartsInstance.setOption({
2537
+ series: allSeries.map(s => {
2538
+ return this.changeSeriesOpacity(dataSeriesNames, seriesName, s);
2539
+ })
2540
+ });
2541
+ }
2542
+ changeSeriesOpacity(dataSeriesName, seriesName, series) {
2543
+ const dataSeriesNames = new Set(dataSeriesName);
2544
+ if (typeof series.name !== 'string' || !dataSeriesNames.has(series.name)) {
2545
+ return series;
2546
+ }
2547
+ const isDimmed = seriesName !== null && series.name !== seriesName;
2548
+ const opacity = isDimmed ? 0.2 : 1;
2549
+ return {
2550
+ ...series,
2551
+ lineStyle: { ...(series.lineStyle ?? {}), opacity },
2552
+ itemStyle: { ...(series.itemStyle ?? {}), opacity },
2553
+ emphasis: {
2554
+ ...(series.emphasis ?? {}),
2555
+ // Disable ECharts' automatic focus-blur so hovering other series
2556
+ // does not override the pinned opacity we set here.
2557
+ focus: 'none',
2558
+ lineStyle: { ...(series.emphasis?.lineStyle ?? {}), opacity },
2559
+ itemStyle: { ...(series.emphasis?.itemStyle ?? {}), opacity }
2560
+ }
2561
+ };
2562
+ }
2563
+ toggleStickyHighlight(seriesName) {
2564
+ if (this.highlightedSeriesName === seriesName) {
2565
+ // Same series clicked again — remove the lock
2566
+ this.setStickyHighlight(null);
2567
+ return;
2568
+ }
2569
+ this.setStickyHighlight(seriesName);
2570
+ }
2473
2571
  get displayOptions() {
2474
2572
  const chartDefaults = CHART_DISPLAY_OPTION_DEFAULTS;
2475
2573
  return {
@@ -2724,7 +2822,7 @@ class ChartsComponent {
2724
2822
  }
2725
2823
  this.updateActiveDatapoints.emit(this.activeDatapoints);
2726
2824
  const datapointsWithValuesRequests = [];
2727
- const timeRange = this.getTimeRange(60_000);
2825
+ const timeRange = this.getTimeRange();
2728
2826
  for (const dp of this.activeDatapoints) {
2729
2827
  const request = defer(() => this.measurementService.listSeries({
2730
2828
  revert: true,
@@ -2770,7 +2868,9 @@ class ChartsComponent {
2770
2868
  }));
2771
2869
  }
2772
2870
  fetchAggregatedSeriesForSelectedDatapoint$(customTimeRange, aggregatedDatapoint) {
2773
- const timeRange = this.getTimeRange(60_000);
2871
+ const { dateTo: unpaddedDateTo } = this.getTimeRange();
2872
+ const { dateFrom: paddedDateFrom } = this.getTimeRange(60_000);
2873
+ const timeRange = { dateFrom: paddedDateFrom, dateTo: unpaddedDateTo };
2774
2874
  aggregatedDatapoint = aggregatedDatapoint
2775
2875
  ? aggregatedDatapoint
2776
2876
  : this.config?.datapoints?.filter(dp => dp.__active)[0];