@tetacom/svg-charts 1.7.37 → 1.7.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, input, inject, ChangeDetectorRef, ElementRef, computed, Component, ViewChild, Input, ChangeDetectionStrategy, HostListener, HostBinding, Directive, signal, effect, EventEmitter, Output, ViewContainerRef } from '@angular/core';
3
- import { ReplaySubject, filter, BehaviorSubject, Subject, of, shareReplay, withLatestFrom, map, merge, lastValueFrom, take, combineLatest, tap, takeWhile, observeOn, animationFrameScheduler } from 'rxjs';
3
+ import { ReplaySubject, filter, BehaviorSubject, Subject, of, shareReplay, withLatestFrom, map, merge, lastValueFrom, take, combineLatest, tap, takeWhile, observeOn, animationFrameScheduler, distinctUntilChanged } from 'rxjs';
4
4
  import * as d3 from 'd3';
5
5
  import { zoomIdentity } from 'd3';
6
6
  import { maxIndex } from 'd3-array';
@@ -193,8 +193,9 @@ class ChartService {
193
193
  this.plotBandEvent$ = new Subject();
194
194
  this.plotLineMove$ = new Subject();
195
195
  this.pointMove$ = new Subject();
196
- this.seriesOffsetMove$ = new Subject();
196
+ this.seriesMove$ = new Subject();
197
197
  this.seriesPathOffsets$ = new BehaviorSubject(new Map());
198
+ this.selectedSeriesIds$ = new BehaviorSubject([]);
198
199
  this.chartClick$ = new Subject();
199
200
  this.chartContextMenu$ = new Subject();
200
201
  this.annotationEvent$ = new Subject();
@@ -218,8 +219,9 @@ class ChartService {
218
219
  this.plotBandEvent = this.plotBandEvent$.asObservable();
219
220
  this.plotLineMove = this.plotLineMove$.asObservable();
220
221
  this.pointMove = this.pointMove$.asObservable();
221
- this.seriesOffsetMove = this.seriesOffsetMove$.asObservable();
222
+ this.seriesMove = this.seriesMove$.asObservable();
222
223
  this.seriesPathOffsets = this.seriesPathOffsets$.asObservable();
224
+ this.selectedSeriesIds = this.selectedSeriesIds$.asObservable();
223
225
  this.chartClick = this.chartClick$.asObservable();
224
226
  this.chartContextMenu = this.chartContextMenu$.asObservable();
225
227
  this.annotationClick = this.annotationEvent$.asObservable().pipe(filter((_) => _?.event?.type === 'click'));
@@ -235,6 +237,7 @@ class ChartService {
235
237
  }
236
238
  setConfig(config) {
237
239
  this.clearTooltips();
240
+ this.clearSeriesPathOffsets();
238
241
  this.config$.next(config);
239
242
  }
240
243
  setSize(size) {
@@ -297,8 +300,8 @@ class ChartService {
297
300
  emitPoint(event) {
298
301
  this.pointMove$.next(event);
299
302
  }
300
- emitSeriesOffset(event) {
301
- this.seriesOffsetMove$.next(event);
303
+ emitSeriesMove(event) {
304
+ this.seriesMove$.next(event);
302
305
  }
303
306
  getSeriesPathOffsets() {
304
307
  return this.seriesPathOffsets$.value;
@@ -306,6 +309,30 @@ class ChartService {
306
309
  setSeriesPathOffsets(offsets) {
307
310
  this.seriesPathOffsets$.next(new Map(offsets));
308
311
  }
312
+ clearSeriesPathOffsets() {
313
+ this.seriesPathOffsets$.next(new Map());
314
+ }
315
+ getSelectedSeriesIds() {
316
+ return this.selectedSeriesIds$.value;
317
+ }
318
+ setSelectedSeriesIds(seriesIds) {
319
+ const nextSeriesIds = [...(seriesIds ?? [])];
320
+ if (this.areSeriesIdsEqual(this.selectedSeriesIds$.value, nextSeriesIds)) {
321
+ return;
322
+ }
323
+ this.selectedSeriesIds$.next(nextSeriesIds);
324
+ }
325
+ toggleSelectedSeriesId(seriesId) {
326
+ const selectedIds = this.selectedSeriesIds$.value;
327
+ if (selectedIds.includes(seriesId)) {
328
+ this.setSelectedSeriesIds(selectedIds.filter((id) => id !== seriesId));
329
+ return;
330
+ }
331
+ this.setSelectedSeriesIds([...selectedIds, seriesId]);
332
+ }
333
+ areSeriesIdsEqual(first, second) {
334
+ return first.length === second.length && first.every((id, index) => id === second[index]);
335
+ }
309
336
  emitChartClick(event) {
310
337
  this.chartClick$.next(event);
311
338
  }
@@ -1842,6 +1869,13 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
1842
1869
  }
1843
1870
  const mouse = [event?.offsetX, event?.offsetY];
1844
1871
  const tooltipTracking = this.config()?.tooltip?.tracking;
1872
+ const clipOffset = this.getClipOffset();
1873
+ const addOffset = (value, offset) => {
1874
+ return value instanceof Date ? new Date(value.getTime() + offset) : value + offset;
1875
+ };
1876
+ const subtractOffset = (value, offset) => {
1877
+ return value instanceof Date ? value.getTime() - offset : value - offset;
1878
+ };
1845
1879
  const lineIntersection = (p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) => {
1846
1880
  const rV = {};
1847
1881
  const s1_x = p1_x - p0_x;
@@ -1864,9 +1898,10 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
1864
1898
  if (x0 instanceof Date) {
1865
1899
  x0 = x0.getTime();
1866
1900
  }
1901
+ x0 = subtractOffset(x0, clipOffset.x);
1867
1902
  const rightId = bisect([...this.series().data].sort((a, b) => a.x - b.x), x0);
1868
1903
  const range = scaleY.range();
1869
- const intersect = lineIntersection(pointer, range[0], pointer, Number.MAX_SAFE_INTEGER, scaleX(sortedData[rightId - 1]?.x), scaleY(sortedData[rightId - 1]?.y), scaleX(sortedData[rightId]?.x), scaleY(sortedData[rightId]?.y));
1904
+ const intersect = lineIntersection(pointer, range[0], pointer, Number.MAX_SAFE_INTEGER, scaleX(addOffset(sortedData[rightId - 1]?.x, clipOffset.x)), scaleY(addOffset(sortedData[rightId - 1]?.y, clipOffset.y)), scaleX(addOffset(sortedData[rightId]?.x, clipOffset.x)), scaleY(addOffset(sortedData[rightId]?.y, clipOffset.y)));
1870
1905
  const x = scaleX.invert(intersect.x);
1871
1906
  const y = scaleY.invert(intersect.y);
1872
1907
  if (x !== null && x !== undefined && !isNaN(x) && y !== null && y !== undefined && !isNaN(y)) {
@@ -1896,9 +1931,10 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
1896
1931
  if (y0 instanceof Date) {
1897
1932
  y0 = y0.getTime();
1898
1933
  }
1934
+ y0 = subtractOffset(y0, clipOffset.y);
1899
1935
  const rightId = bisect(sortedData, y0);
1900
1936
  const range = scaleX.range();
1901
- const intersect = lineIntersection(range[0], mouse[1], Number.MAX_SAFE_INTEGER, mouse[1], scaleX(sortedData[rightId - 1]?.x), scaleY(sortedData[rightId - 1]?.y), scaleX(sortedData[rightId]?.x), scaleY(sortedData[rightId]?.y));
1937
+ const intersect = lineIntersection(range[0], mouse[1], Number.MAX_SAFE_INTEGER, mouse[1], scaleX(addOffset(sortedData[rightId - 1]?.x, clipOffset.x)), scaleY(addOffset(sortedData[rightId - 1]?.y, clipOffset.y)), scaleX(addOffset(sortedData[rightId]?.x, clipOffset.x)), scaleY(addOffset(sortedData[rightId]?.y, clipOffset.y)));
1902
1938
  const x = scaleX.invert(intersect.x);
1903
1939
  const y = scaleY.invert(intersect.y);
1904
1940
  if (x !== null && x !== undefined && !isNaN(x) && y !== null && y !== undefined && !isNaN(y)) {
@@ -2191,12 +2227,16 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2191
2227
  constructor() {
2192
2228
  super(...arguments);
2193
2229
  this.seriesPathOffsets = toSignal(this.svc.seriesPathOffsets, { initialValue: new Map() });
2230
+ this.selectedSeriesIds = toSignal(this.svc.selectedSeriesIds, { initialValue: [] });
2194
2231
  this.seriesDragStartOffsets = new Map();
2195
2232
  this.seriesDragGroup = [];
2196
2233
  this.seriesDragMoved = false;
2197
2234
  this.seriesOffsetValue = computed(() => {
2198
2235
  return this.seriesPathOffsets().get(this.series().id) ?? { x: 0, y: 0 };
2199
2236
  }, ...(ngDevMode ? [{ debugName: "seriesOffsetValue" }] : /* istanbul ignore next */ []));
2237
+ this.seriesSelectedForPathDrag = computed(() => {
2238
+ return this.isSeriesSelectedForPathDrag(this.series());
2239
+ }, ...(ngDevMode ? [{ debugName: "seriesSelectedForPathDrag" }] : /* istanbul ignore next */ []));
2200
2240
  this.seriesPathTransform = computed(() => {
2201
2241
  const offset = this.seriesOffsetValue();
2202
2242
  const offsetX = this.getOffsetPixels(this.x(), offset.x);
@@ -2238,16 +2278,16 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2238
2278
  this.seriesDragStartOffsets = new Map(this.seriesDragGroup.map((series) => {
2239
2279
  return [series.id, this.svc.getSeriesPathOffsets().get(series.id) ?? { x: 0, y: 0 }];
2240
2280
  }));
2241
- this.emitSeriesOffset('start', event);
2281
+ this.emitSeriesMove('start', event);
2242
2282
  }
2243
2283
  seriesMoveProcess(event) {
2244
2284
  this.seriesDragMoved = this.seriesDragMoved || Math.abs(event.deltaX) > 3 || Math.abs(event.deltaY) > 3;
2245
2285
  this.updateSeriesOffset(event);
2246
- this.emitSeriesOffset('drag', event);
2286
+ this.emitSeriesMove('drag', event);
2247
2287
  }
2248
2288
  seriesMoveEnd(event) {
2249
2289
  this.updateSeriesOffset(event);
2250
- this.emitSeriesOffset('end', event);
2290
+ this.emitSeriesMove('end', event);
2251
2291
  }
2252
2292
  seriesPathClick(event) {
2253
2293
  if (this.seriesDragMoved) {
@@ -2264,8 +2304,7 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2264
2304
  }
2265
2305
  event.stopPropagation();
2266
2306
  event.preventDefault();
2267
- this.series().selectedForPathDrag = !this.series().selectedForPathDrag;
2268
- this.cdr.markForCheck();
2307
+ this.svc.toggleSelectedSeriesId(this.series().id);
2269
2308
  }
2270
2309
  moveStart(event, point) {
2271
2310
  this.start = { x: point.x, y: point.y };
@@ -2309,10 +2348,10 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2309
2348
  label.dx = this.labelStart.dx + event.deltaX;
2310
2349
  label.dy = this.labelStart.dy + event.deltaY;
2311
2350
  }
2312
- emitSeriesOffset(type, event) {
2351
+ emitSeriesMove(type, event) {
2313
2352
  const offsetValue = this.seriesOffsetValue();
2314
2353
  const seriesList = this.seriesDragGroup.length ? this.seriesDragGroup : [this.series()];
2315
- this.svc.emitSeriesOffset({
2354
+ this.svc.emitSeriesMove({
2316
2355
  event: {
2317
2356
  type,
2318
2357
  sourceEvent: event,
@@ -2375,19 +2414,24 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2375
2414
  }
2376
2415
  getPathDragSeriesGroup() {
2377
2416
  const currentSeries = this.series();
2378
- if (!currentSeries.selectedForPathDrag) {
2417
+ if (!this.isSeriesSelectedForPathDrag(currentSeries)) {
2379
2418
  return [currentSeries];
2380
2419
  }
2420
+ const selectedIds = this.svc.getSelectedSeriesIds();
2381
2421
  const selectedSeries = this.config()?.series?.filter((series) => {
2382
- return (series.selectedForPathDrag &&
2422
+ return (selectedIds.includes(series.id) &&
2383
2423
  series.draggablePath &&
2384
2424
  series.visible !== false &&
2385
2425
  series.enabled !== false &&
2386
- series.xAxisIndex === currentSeries.xAxisIndex &&
2387
- series.yAxisIndex === currentSeries.yAxisIndex);
2426
+ series.pathDragType === currentSeries.pathDragType &&
2427
+ ((currentSeries.pathDragType === DragPointType.x && series.xAxisIndex === currentSeries.xAxisIndex) ||
2428
+ (currentSeries.pathDragType === DragPointType.y && series.yAxisIndex === currentSeries.yAxisIndex)));
2388
2429
  });
2389
2430
  return selectedSeries?.length ? selectedSeries : [currentSeries];
2390
2431
  }
2432
+ isSeriesSelectedForPathDrag(series) {
2433
+ return this.selectedSeriesIds().includes(series.id);
2434
+ }
2391
2435
  getOffsetPixels(scale, offsetValue) {
2392
2436
  if (!scale || !scale?.domain || offsetValue === null || offsetValue === undefined) {
2393
2437
  return 0;
@@ -2398,11 +2442,11 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2398
2442
  return scale(nextValue) - scale(domainStart);
2399
2443
  }
2400
2444
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LineSeriesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2401
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LineSeriesComponent, isStandalone: true, selector: "svg:svg[teta-line-series]", usesInheritance: true, ngImport: i0, template: "@if (series(); as series) {\n <svg:g\n [tetaDraggableSeries]=\"series.draggablePath\"\n [dragDirection]=\"series.pathDragType\"\n [attr.transform]=\"seriesPathTransform()\"\n [class.draggable-path]=\"series.draggablePath\"\n [class.selected-path]=\"series.selectedForPathDrag\"\n (click)=\"seriesPathClick($event)\"\n (moveStart)=\"seriesMoveStart($event)\"\n (moveProcess)=\"seriesMoveProcess($event)\"\n (moveEnd)=\"seriesMoveEnd($event)\"\n >\n <svg:path\n class=\"line\"\n [class.line-selected]=\"series.selectedForPathDrag\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n [style.--series-color]=\"series.color\"\n fill=\"none\"\n ></svg:path>\n @if (series.draggablePath) {\n <svg:path\n class=\"line-hitbox\"\n [attr.d]=\"path()\"\n fill=\"none\"\n stroke=\"transparent\"\n stroke-width=\"14\"\n pointer-events=\"stroke\"\n ></svg:path>\n }\n </svg:g>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle\n r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"\n ></svg:circle>\n }\n }\n @if (markers(); as draggablePoints) {\n @if (x() && y()) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + x()(point.x) + ',' + y()(point.y) + ')'\">\n <svg:g\n [tetaDraggablePoint]=\"point.marker.draggable\"\n [dragDirection]=\"point.marker.dragType\"\n [allowDrag]=\"allowDrag(point)\"\n #dragPoint=\"tetaDraggablePoint\"\n (moveStart)=\"moveStart($event, point)\"\n (moveEnd)=\"moveEnd($event, point); dragPoint.resetTransform()\"\n (moveProcess)=\"moveProcess($event, point); dragPoint.resetTransform()\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\n >\n <svg:circle\n class=\"marker\"\n [attr.r]=\"point.marker.style?.radius ?? 5\"\n [attr.fill]=\"point.marker.style?.fill ?? 'transparent'\"\n [attr.stroke]=\"point.marker.style?.stroke ?? 'none'\"\n [attr.stroke-width]=\"point.marker.style?.strokeWidth\"\n [attr.stroke-dasharray]=\"point.marker.style?.strokeDasharray\"\n [attr.cx]=\"0\"\n [attr.cy]=\"0\"\n ></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line\n [attr.x1]=\"0\"\n [attr.y1]=\"0\"\n [attr.x2]=\"point.marker.label?.dx\"\n [attr.y2]=\"point.marker.label?.dy\"\n [attr.stroke]=\"point.marker.label?.style?.stroke ?? 'var(--color-text-90)'\"\n [attr.stroke-width]=\"point.marker.label?.style?.strokeWidth ?? 1\"\n [attr.stroke-dasharray]=\"point.marker.label?.style?.strokeDasharray ?? null\"\n ></svg:line>\n <svg:foreignObject\n [tetaDraggablePoint]=\"point.marker.label?.draggable\"\n [dragDirection]=\"point.marker.label.dragType\"\n #labelPoint=\"tetaDraggablePoint\"\n (moveStart)=\"startLabel($event, point.marker.label)\"\n (moveProcess)=\"moveLabel($event, point.marker.label); labelPoint.resetTransform()\"\n (moveEnd)=\"labelPoint.resetTransform()\"\n [attr.width]=\"annotationNode?.offsetWidth ?? 0\"\n [attr.height]=\"annotationNode?.offsetHeight ?? 0\"\n [attr.x]=\"point.marker.label?.dx\"\n [attr.y]=\"point.marker.label?.dy\"\n class=\"position-absolute\"\n >\n <div\n #annotationNode\n class=\"shadow-2 padding-2\"\n [style.color]=\"'var(--color-text-90)'\"\n [style.background-color]=\"'var(--color-global-bgcard)'\"\n [style.cursor]=\"'move'\"\n style=\"border-radius: 2px; display: inline-block\"\n >\n {{ point.marker.label?.text }}\n </div>\n </svg:foreignObject>\n }\n </svg:g>\n </svg:g>\n }\n }\n }\n}\n", styles: [".draggable-marker{cursor:move}.draggable-path,.selected-path,.selected-path:active{cursor:ew-resize}.line-selected{filter:drop-shadow(0 0 3px var(--series-color))}.active{stroke-opacity:.5}.marker-grab{opacity:0}\n"], dependencies: [{ kind: "directive", type: DraggablePointDirective, selector: "[tetaDraggablePoint]", inputs: ["tetaDraggablePoint", "dragDirection", "allowDrag"], outputs: ["moveStart", "moveProcess", "moveEnd"], exportAs: ["tetaDraggablePoint"] }, { kind: "directive", type: DraggableSeriesDirective, selector: "[tetaDraggableSeries]", inputs: ["tetaDraggableSeries", "dragDirection"], outputs: ["moveStart", "moveProcess", "moveEnd"], exportAs: ["tetaDraggableSeries"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2445
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LineSeriesComponent, isStandalone: true, selector: "svg:svg[teta-line-series]", usesInheritance: true, ngImport: i0, template: "@if (series(); as series) {\n <svg:g\n [tetaDraggableSeries]=\"series.draggablePath\"\n [dragDirection]=\"series.pathDragType\"\n [attr.transform]=\"seriesPathTransform()\"\n [class.draggable-path]=\"series.draggablePath\"\n [class.selected-path]=\"seriesSelectedForPathDrag()\"\n (click)=\"seriesPathClick($event)\"\n (moveStart)=\"seriesMoveStart($event)\"\n (moveProcess)=\"seriesMoveProcess($event)\"\n (moveEnd)=\"seriesMoveEnd($event)\"\n >\n <svg:path\n class=\"line\"\n [class.line-selected]=\"seriesSelectedForPathDrag()\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n [style.--series-color]=\"series.color\"\n fill=\"none\"\n ></svg:path>\n @if (series.draggablePath) {\n <svg:path\n class=\"line-hitbox\"\n [attr.d]=\"path()\"\n fill=\"none\"\n stroke=\"transparent\"\n stroke-width=\"14\"\n pointer-events=\"stroke\"\n ></svg:path>\n }\n </svg:g>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle\n r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"\n ></svg:circle>\n }\n }\n @if (markers(); as draggablePoints) {\n @if (x() && y()) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + x()(point.x) + ',' + y()(point.y) + ')'\">\n <svg:g\n [tetaDraggablePoint]=\"point.marker.draggable\"\n [dragDirection]=\"point.marker.dragType\"\n [allowDrag]=\"allowDrag(point)\"\n #dragPoint=\"tetaDraggablePoint\"\n (moveStart)=\"moveStart($event, point)\"\n (moveEnd)=\"moveEnd($event, point); dragPoint.resetTransform()\"\n (moveProcess)=\"moveProcess($event, point); dragPoint.resetTransform()\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\n >\n <svg:circle\n class=\"marker\"\n [attr.r]=\"point.marker.style?.radius ?? 5\"\n [attr.fill]=\"point.marker.style?.fill ?? 'transparent'\"\n [attr.stroke]=\"point.marker.style?.stroke ?? 'none'\"\n [attr.stroke-width]=\"point.marker.style?.strokeWidth\"\n [attr.stroke-dasharray]=\"point.marker.style?.strokeDasharray\"\n [attr.cx]=\"0\"\n [attr.cy]=\"0\"\n ></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line\n [attr.x1]=\"0\"\n [attr.y1]=\"0\"\n [attr.x2]=\"point.marker.label?.dx\"\n [attr.y2]=\"point.marker.label?.dy\"\n [attr.stroke]=\"point.marker.label?.style?.stroke ?? 'var(--color-text-90)'\"\n [attr.stroke-width]=\"point.marker.label?.style?.strokeWidth ?? 1\"\n [attr.stroke-dasharray]=\"point.marker.label?.style?.strokeDasharray ?? null\"\n ></svg:line>\n <svg:foreignObject\n [tetaDraggablePoint]=\"point.marker.label?.draggable\"\n [dragDirection]=\"point.marker.label.dragType\"\n #labelPoint=\"tetaDraggablePoint\"\n (moveStart)=\"startLabel($event, point.marker.label)\"\n (moveProcess)=\"moveLabel($event, point.marker.label); labelPoint.resetTransform()\"\n (moveEnd)=\"labelPoint.resetTransform()\"\n [attr.width]=\"annotationNode?.offsetWidth ?? 0\"\n [attr.height]=\"annotationNode?.offsetHeight ?? 0\"\n [attr.x]=\"point.marker.label?.dx\"\n [attr.y]=\"point.marker.label?.dy\"\n class=\"position-absolute\"\n >\n <div\n #annotationNode\n class=\"shadow-2 padding-2\"\n [style.color]=\"'var(--color-text-90)'\"\n [style.background-color]=\"'var(--color-global-bgcard)'\"\n [style.cursor]=\"'move'\"\n style=\"border-radius: 2px; display: inline-block\"\n >\n {{ point.marker.label?.text }}\n </div>\n </svg:foreignObject>\n }\n </svg:g>\n </svg:g>\n }\n }\n }\n}\n", styles: [".draggable-marker{cursor:move}.draggable-path,.selected-path,.selected-path:active{cursor:ew-resize}.line-selected{filter:drop-shadow(0 0 3px var(--series-color))}.active{stroke-opacity:.5}.marker-grab{opacity:0}\n"], dependencies: [{ kind: "directive", type: DraggablePointDirective, selector: "[tetaDraggablePoint]", inputs: ["tetaDraggablePoint", "dragDirection", "allowDrag"], outputs: ["moveStart", "moveProcess", "moveEnd"], exportAs: ["tetaDraggablePoint"] }, { kind: "directive", type: DraggableSeriesDirective, selector: "[tetaDraggableSeries]", inputs: ["tetaDraggableSeries", "dragDirection"], outputs: ["moveStart", "moveProcess", "moveEnd"], exportAs: ["tetaDraggableSeries"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2402
2446
  }
2403
2447
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LineSeriesComponent, decorators: [{
2404
2448
  type: Component,
2405
- args: [{ selector: 'svg:svg[teta-line-series]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, DraggablePointDirective, DraggableSeriesDirective], template: "@if (series(); as series) {\n <svg:g\n [tetaDraggableSeries]=\"series.draggablePath\"\n [dragDirection]=\"series.pathDragType\"\n [attr.transform]=\"seriesPathTransform()\"\n [class.draggable-path]=\"series.draggablePath\"\n [class.selected-path]=\"series.selectedForPathDrag\"\n (click)=\"seriesPathClick($event)\"\n (moveStart)=\"seriesMoveStart($event)\"\n (moveProcess)=\"seriesMoveProcess($event)\"\n (moveEnd)=\"seriesMoveEnd($event)\"\n >\n <svg:path\n class=\"line\"\n [class.line-selected]=\"series.selectedForPathDrag\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n [style.--series-color]=\"series.color\"\n fill=\"none\"\n ></svg:path>\n @if (series.draggablePath) {\n <svg:path\n class=\"line-hitbox\"\n [attr.d]=\"path()\"\n fill=\"none\"\n stroke=\"transparent\"\n stroke-width=\"14\"\n pointer-events=\"stroke\"\n ></svg:path>\n }\n </svg:g>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle\n r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"\n ></svg:circle>\n }\n }\n @if (markers(); as draggablePoints) {\n @if (x() && y()) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + x()(point.x) + ',' + y()(point.y) + ')'\">\n <svg:g\n [tetaDraggablePoint]=\"point.marker.draggable\"\n [dragDirection]=\"point.marker.dragType\"\n [allowDrag]=\"allowDrag(point)\"\n #dragPoint=\"tetaDraggablePoint\"\n (moveStart)=\"moveStart($event, point)\"\n (moveEnd)=\"moveEnd($event, point); dragPoint.resetTransform()\"\n (moveProcess)=\"moveProcess($event, point); dragPoint.resetTransform()\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\n >\n <svg:circle\n class=\"marker\"\n [attr.r]=\"point.marker.style?.radius ?? 5\"\n [attr.fill]=\"point.marker.style?.fill ?? 'transparent'\"\n [attr.stroke]=\"point.marker.style?.stroke ?? 'none'\"\n [attr.stroke-width]=\"point.marker.style?.strokeWidth\"\n [attr.stroke-dasharray]=\"point.marker.style?.strokeDasharray\"\n [attr.cx]=\"0\"\n [attr.cy]=\"0\"\n ></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line\n [attr.x1]=\"0\"\n [attr.y1]=\"0\"\n [attr.x2]=\"point.marker.label?.dx\"\n [attr.y2]=\"point.marker.label?.dy\"\n [attr.stroke]=\"point.marker.label?.style?.stroke ?? 'var(--color-text-90)'\"\n [attr.stroke-width]=\"point.marker.label?.style?.strokeWidth ?? 1\"\n [attr.stroke-dasharray]=\"point.marker.label?.style?.strokeDasharray ?? null\"\n ></svg:line>\n <svg:foreignObject\n [tetaDraggablePoint]=\"point.marker.label?.draggable\"\n [dragDirection]=\"point.marker.label.dragType\"\n #labelPoint=\"tetaDraggablePoint\"\n (moveStart)=\"startLabel($event, point.marker.label)\"\n (moveProcess)=\"moveLabel($event, point.marker.label); labelPoint.resetTransform()\"\n (moveEnd)=\"labelPoint.resetTransform()\"\n [attr.width]=\"annotationNode?.offsetWidth ?? 0\"\n [attr.height]=\"annotationNode?.offsetHeight ?? 0\"\n [attr.x]=\"point.marker.label?.dx\"\n [attr.y]=\"point.marker.label?.dy\"\n class=\"position-absolute\"\n >\n <div\n #annotationNode\n class=\"shadow-2 padding-2\"\n [style.color]=\"'var(--color-text-90)'\"\n [style.background-color]=\"'var(--color-global-bgcard)'\"\n [style.cursor]=\"'move'\"\n style=\"border-radius: 2px; display: inline-block\"\n >\n {{ point.marker.label?.text }}\n </div>\n </svg:foreignObject>\n }\n </svg:g>\n </svg:g>\n }\n }\n }\n}\n", styles: [".draggable-marker{cursor:move}.draggable-path,.selected-path,.selected-path:active{cursor:ew-resize}.line-selected{filter:drop-shadow(0 0 3px var(--series-color))}.active{stroke-opacity:.5}.marker-grab{opacity:0}\n"] }]
2449
+ args: [{ selector: 'svg:svg[teta-line-series]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, DraggablePointDirective, DraggableSeriesDirective], template: "@if (series(); as series) {\n <svg:g\n [tetaDraggableSeries]=\"series.draggablePath\"\n [dragDirection]=\"series.pathDragType\"\n [attr.transform]=\"seriesPathTransform()\"\n [class.draggable-path]=\"series.draggablePath\"\n [class.selected-path]=\"seriesSelectedForPathDrag()\"\n (click)=\"seriesPathClick($event)\"\n (moveStart)=\"seriesMoveStart($event)\"\n (moveProcess)=\"seriesMoveProcess($event)\"\n (moveEnd)=\"seriesMoveEnd($event)\"\n >\n <svg:path\n class=\"line\"\n [class.line-selected]=\"seriesSelectedForPathDrag()\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n [style.--series-color]=\"series.color\"\n fill=\"none\"\n ></svg:path>\n @if (series.draggablePath) {\n <svg:path\n class=\"line-hitbox\"\n [attr.d]=\"path()\"\n fill=\"none\"\n stroke=\"transparent\"\n stroke-width=\"14\"\n pointer-events=\"stroke\"\n ></svg:path>\n }\n </svg:g>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle\n r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"\n ></svg:circle>\n }\n }\n @if (markers(); as draggablePoints) {\n @if (x() && y()) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + x()(point.x) + ',' + y()(point.y) + ')'\">\n <svg:g\n [tetaDraggablePoint]=\"point.marker.draggable\"\n [dragDirection]=\"point.marker.dragType\"\n [allowDrag]=\"allowDrag(point)\"\n #dragPoint=\"tetaDraggablePoint\"\n (moveStart)=\"moveStart($event, point)\"\n (moveEnd)=\"moveEnd($event, point); dragPoint.resetTransform()\"\n (moveProcess)=\"moveProcess($event, point); dragPoint.resetTransform()\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\n >\n <svg:circle\n class=\"marker\"\n [attr.r]=\"point.marker.style?.radius ?? 5\"\n [attr.fill]=\"point.marker.style?.fill ?? 'transparent'\"\n [attr.stroke]=\"point.marker.style?.stroke ?? 'none'\"\n [attr.stroke-width]=\"point.marker.style?.strokeWidth\"\n [attr.stroke-dasharray]=\"point.marker.style?.strokeDasharray\"\n [attr.cx]=\"0\"\n [attr.cy]=\"0\"\n ></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line\n [attr.x1]=\"0\"\n [attr.y1]=\"0\"\n [attr.x2]=\"point.marker.label?.dx\"\n [attr.y2]=\"point.marker.label?.dy\"\n [attr.stroke]=\"point.marker.label?.style?.stroke ?? 'var(--color-text-90)'\"\n [attr.stroke-width]=\"point.marker.label?.style?.strokeWidth ?? 1\"\n [attr.stroke-dasharray]=\"point.marker.label?.style?.strokeDasharray ?? null\"\n ></svg:line>\n <svg:foreignObject\n [tetaDraggablePoint]=\"point.marker.label?.draggable\"\n [dragDirection]=\"point.marker.label.dragType\"\n #labelPoint=\"tetaDraggablePoint\"\n (moveStart)=\"startLabel($event, point.marker.label)\"\n (moveProcess)=\"moveLabel($event, point.marker.label); labelPoint.resetTransform()\"\n (moveEnd)=\"labelPoint.resetTransform()\"\n [attr.width]=\"annotationNode?.offsetWidth ?? 0\"\n [attr.height]=\"annotationNode?.offsetHeight ?? 0\"\n [attr.x]=\"point.marker.label?.dx\"\n [attr.y]=\"point.marker.label?.dy\"\n class=\"position-absolute\"\n >\n <div\n #annotationNode\n class=\"shadow-2 padding-2\"\n [style.color]=\"'var(--color-text-90)'\"\n [style.background-color]=\"'var(--color-global-bgcard)'\"\n [style.cursor]=\"'move'\"\n style=\"border-radius: 2px; display: inline-block\"\n >\n {{ point.marker.label?.text }}\n </div>\n </svg:foreignObject>\n }\n </svg:g>\n </svg:g>\n }\n }\n }\n}\n", styles: [".draggable-marker{cursor:move}.draggable-path,.selected-path,.selected-path:active{cursor:ew-resize}.line-selected{filter:drop-shadow(0 0 3px var(--series-color))}.active{stroke-opacity:.5}.marker-grab{opacity:0}\n"] }]
2406
2450
  }] });
2407
2451
 
2408
2452
  class BarSeriesComponent extends SeriesBaseComponent {
@@ -3263,6 +3307,9 @@ class ChartComponent {
3263
3307
  this.chartService.setConfig(config);
3264
3308
  this.zoomService.setBroadcastChannel(config?.zoom?.syncChannel);
3265
3309
  }
3310
+ set selectedSeriesIds(seriesIds) {
3311
+ this.chartService.setSelectedSeriesIds(seriesIds ?? []);
3312
+ }
3266
3313
  constructor(chartService, zoomService, brushService, scaleService) {
3267
3314
  this.chartService = chartService;
3268
3315
  this.zoomService = zoomService;
@@ -3274,7 +3321,8 @@ class ChartComponent {
3274
3321
  this.plotBandContextMenu = new EventEmitter();
3275
3322
  this.plotLinesMove = new EventEmitter();
3276
3323
  this.pointMove = new EventEmitter();
3277
- this.seriesOffsetMove = new EventEmitter();
3324
+ this.seriesMove = new EventEmitter();
3325
+ this.selectedSeriesIdsChange = new EventEmitter();
3278
3326
  this.chartClick = new EventEmitter();
3279
3327
  this.chartContextMenu = new EventEmitter();
3280
3328
  this.annotationContextMenu = new EventEmitter();
@@ -3334,8 +3382,13 @@ class ChartComponent {
3334
3382
  this.chartService.pointMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3335
3383
  this.pointMove.emit(_);
3336
3384
  });
3337
- this.chartService.seriesOffsetMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3338
- this.seriesOffsetMove.emit(_);
3385
+ this.chartService.seriesMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3386
+ this.seriesMove.emit(_);
3387
+ });
3388
+ this.chartService.selectedSeriesIds
3389
+ .pipe(takeWhile(() => this._alive), distinctUntilChanged((prev, next) => this.areSeriesIdsEqual(prev, next)))
3390
+ .subscribe((_) => {
3391
+ this.selectedSeriesIdsChange.emit(_);
3339
3392
  });
3340
3393
  this.chartService.chartClick.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3341
3394
  this.chartClick.emit(_);
@@ -3365,8 +3418,11 @@ class ChartComponent {
3365
3418
  ngOnDestroy() {
3366
3419
  this._alive = false;
3367
3420
  }
3421
+ areSeriesIdsEqual(first, second) {
3422
+ return first.length === second.length && first.every((id, index) => id === second[index]);
3423
+ }
3368
3424
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChartComponent, deps: [{ token: ChartService }, { token: ZoomService }, { token: BrushService }, { token: ScaleService }], target: i0.ɵɵFactoryTarget.Component }); }
3369
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ChartComponent, isStandalone: true, selector: "teta-svg-chart", inputs: { config: "config" }, outputs: { pointerMove: "pointerMove", plotBandsMove: "plotBandsMove", plotBandClick: "plotBandClick", plotBandContextMenu: "plotBandContextMenu", plotLinesMove: "plotLinesMove", pointMove: "pointMove", seriesOffsetMove: "seriesOffsetMove", chartClick: "chartClick", chartContextMenu: "chartContextMenu", annotationContextMenu: "annotationContextMenu", annotationClick: "annotationClick", annotationMove: "annotationMove", zoomServiceInstance: "zoomServiceInstance", brushServiceInstance: "brushServiceInstance", configUpdated: "configUpdated" }, providers: [ChartService, ZoomService, ScaleService, BrushService], ngImport: i0, template: "@if ({ hasSeriesData: hasSeriesData | async, svcConfig: svcConfig | async }; as data) {\n @if (data.hasSeriesData === true) {\n <div class=\"column column_auto\">\n <teta-chart-container class=\"chart-container position-relative\"></teta-chart-container>\n </div>\n @if (data.svcConfig.legend?.enable === true) {\n <teta-legend [series]=\"data.svcConfig.series\"></teta-legend>\n }\n } @else {\n <div class=\"column column_auto justify-content-center\">\n <span class=\"font-body-3 color-text-40 overflow-hidden text-overflow-ellipsis nowrap text-align-center\">\n <div #ref><ng-content></ng-content></div>\n @if (!ref.hasChildNodes()) {\n <span> No data </span>\n }\n </span>\n </div>\n }\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;height:100%;width:100%}\n"], dependencies: [{ kind: "component", type: ChartContainerComponent, selector: "teta-chart-container" }, { kind: "component", type: LegendComponent, selector: "teta-legend", inputs: ["series"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3425
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ChartComponent, isStandalone: true, selector: "teta-svg-chart", inputs: { config: "config", selectedSeriesIds: "selectedSeriesIds" }, outputs: { pointerMove: "pointerMove", plotBandsMove: "plotBandsMove", plotBandClick: "plotBandClick", plotBandContextMenu: "plotBandContextMenu", plotLinesMove: "plotLinesMove", pointMove: "pointMove", seriesMove: "seriesMove", selectedSeriesIdsChange: "selectedSeriesIdsChange", chartClick: "chartClick", chartContextMenu: "chartContextMenu", annotationContextMenu: "annotationContextMenu", annotationClick: "annotationClick", annotationMove: "annotationMove", zoomServiceInstance: "zoomServiceInstance", brushServiceInstance: "brushServiceInstance", configUpdated: "configUpdated" }, providers: [ChartService, ZoomService, ScaleService, BrushService], ngImport: i0, template: "@if ({ hasSeriesData: hasSeriesData | async, svcConfig: svcConfig | async }; as data) {\n @if (data.hasSeriesData === true) {\n <div class=\"column column_auto\">\n <teta-chart-container class=\"chart-container position-relative\"></teta-chart-container>\n </div>\n @if (data.svcConfig.legend?.enable === true) {\n <teta-legend [series]=\"data.svcConfig.series\"></teta-legend>\n }\n } @else {\n <div class=\"column column_auto justify-content-center\">\n <span class=\"font-body-3 color-text-40 overflow-hidden text-overflow-ellipsis nowrap text-align-center\">\n <div #ref><ng-content></ng-content></div>\n @if (!ref.hasChildNodes()) {\n <span> No data </span>\n }\n </span>\n </div>\n }\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;height:100%;width:100%}\n"], dependencies: [{ kind: "component", type: ChartContainerComponent, selector: "teta-chart-container" }, { kind: "component", type: LegendComponent, selector: "teta-legend", inputs: ["series"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3370
3426
  }
3371
3427
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChartComponent, decorators: [{
3372
3428
  type: Component,
@@ -3383,7 +3439,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3383
3439
  type: Output
3384
3440
  }], pointMove: [{
3385
3441
  type: Output
3386
- }], seriesOffsetMove: [{
3442
+ }], seriesMove: [{
3443
+ type: Output
3444
+ }], selectedSeriesIdsChange: [{
3387
3445
  type: Output
3388
3446
  }], chartClick: [{
3389
3447
  type: Output
@@ -3403,6 +3461,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3403
3461
  type: Output
3404
3462
  }], config: [{
3405
3463
  type: Input
3464
+ }], selectedSeriesIds: [{
3465
+ type: Input
3406
3466
  }] } });
3407
3467
 
3408
3468
  class PlotBand {