@tetacom/svg-charts 1.7.36 → 1.7.37

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.
@@ -193,6 +193,8 @@ 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();
197
+ this.seriesPathOffsets$ = new BehaviorSubject(new Map());
196
198
  this.chartClick$ = new Subject();
197
199
  this.chartContextMenu$ = new Subject();
198
200
  this.annotationEvent$ = new Subject();
@@ -216,6 +218,8 @@ class ChartService {
216
218
  this.plotBandEvent = this.plotBandEvent$.asObservable();
217
219
  this.plotLineMove = this.plotLineMove$.asObservable();
218
220
  this.pointMove = this.pointMove$.asObservable();
221
+ this.seriesOffsetMove = this.seriesOffsetMove$.asObservable();
222
+ this.seriesPathOffsets = this.seriesPathOffsets$.asObservable();
219
223
  this.chartClick = this.chartClick$.asObservable();
220
224
  this.chartContextMenu = this.chartContextMenu$.asObservable();
221
225
  this.annotationClick = this.annotationEvent$.asObservable().pipe(filter((_) => _?.event?.type === 'click'));
@@ -293,6 +297,15 @@ class ChartService {
293
297
  emitPoint(event) {
294
298
  this.pointMove$.next(event);
295
299
  }
300
+ emitSeriesOffset(event) {
301
+ this.seriesOffsetMove$.next(event);
302
+ }
303
+ getSeriesPathOffsets() {
304
+ return this.seriesPathOffsets$.value;
305
+ }
306
+ setSeriesPathOffsets(offsets) {
307
+ this.seriesPathOffsets$.next(new Map(offsets));
308
+ }
296
309
  emitChartClick(event) {
297
310
  this.chartClick$.next(event);
298
311
  }
@@ -1740,6 +1753,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1740
1753
  }] } });
1741
1754
 
1742
1755
  class LinearSeriesBaseComponent extends SeriesBaseComponent {
1756
+ getClipOffset() {
1757
+ return { x: 0, y: 0 };
1758
+ }
1743
1759
  constructor() {
1744
1760
  super();
1745
1761
  this.pointerMove = toSignal(this.svc.pointerMove);
@@ -1768,17 +1784,18 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
1768
1784
  .x((point) => this.x()(point.x))
1769
1785
  .y((point) => this.y()(point.y));
1770
1786
  let filteredData = this.series().data;
1787
+ const clipOffset = this.getClipOffset();
1771
1788
  if (this.series().clipPointsDirection === ClipPointsDirection.x) {
1772
1789
  let [min, max] = this.x().domain();
1773
1790
  min = min instanceof Date ? min.getTime() : min;
1774
1791
  max = max instanceof Date ? max.getTime() : max;
1775
- filteredData = filteredData?.filter(filter(min, max));
1792
+ filteredData = filteredData?.filter(filter(min - clipOffset.x, max - clipOffset.x));
1776
1793
  }
1777
1794
  if (this.series().clipPointsDirection === ClipPointsDirection.y) {
1778
1795
  let [min, max] = this.y().domain();
1779
1796
  min = min instanceof Date ? min.getTime() : min;
1780
1797
  max = max instanceof Date ? max.getTime() : max;
1781
- filteredData = filteredData?.filter(filter(min, max));
1798
+ filteredData = filteredData?.filter(filter(min - clipOffset.y, max - clipOffset.y));
1782
1799
  }
1783
1800
  return line(filteredData);
1784
1801
  }, ...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
@@ -2068,9 +2085,127 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2068
2085
  args: ['window:touchmove', ['$event']]
2069
2086
  }] } });
2070
2087
 
2088
+ class DraggableSeriesDirective {
2089
+ constructor() {
2090
+ this.dragDirection = DragPointType.xy;
2091
+ this.moveStart = new EventEmitter();
2092
+ this.moveProcess = new EventEmitter();
2093
+ this.moveEnd = new EventEmitter();
2094
+ }
2095
+ pointerDown(event) {
2096
+ if (!this.tetaDraggableSeries) {
2097
+ return;
2098
+ }
2099
+ event.stopPropagation();
2100
+ event.preventDefault();
2101
+ this.startPosition = {
2102
+ x: event.clientX,
2103
+ y: event.clientY,
2104
+ };
2105
+ this.moveStart.emit({
2106
+ x: event.clientX,
2107
+ y: event.clientY,
2108
+ deltaX: 0,
2109
+ deltaY: 0,
2110
+ });
2111
+ }
2112
+ pointerMove(event) {
2113
+ if (!this.startPosition) {
2114
+ return;
2115
+ }
2116
+ const nextOffset = this.getNextOffset(event);
2117
+ event.stopPropagation();
2118
+ event.preventDefault();
2119
+ this.moveProcess.emit({
2120
+ x: event.clientX,
2121
+ y: event.clientY,
2122
+ deltaX: nextOffset.x,
2123
+ deltaY: nextOffset.y,
2124
+ });
2125
+ }
2126
+ pointerUp(event) {
2127
+ if (!this.startPosition) {
2128
+ return;
2129
+ }
2130
+ const nextOffset = this.getNextOffset(event);
2131
+ this.moveEnd.emit({
2132
+ x: event.clientX,
2133
+ y: event.clientY,
2134
+ deltaX: nextOffset.x,
2135
+ deltaY: nextOffset.y,
2136
+ });
2137
+ this.startPosition = null;
2138
+ }
2139
+ resetTransform() {
2140
+ this.startPosition = null;
2141
+ }
2142
+ getNextOffset(event) {
2143
+ let deltaX = event.clientX - this.startPosition.x;
2144
+ let deltaY = event.clientY - this.startPosition.y;
2145
+ if (this.dragDirection === DragPointType.x) {
2146
+ deltaY = 0;
2147
+ }
2148
+ if (this.dragDirection === DragPointType.y) {
2149
+ deltaX = 0;
2150
+ }
2151
+ return {
2152
+ x: deltaX,
2153
+ y: deltaY,
2154
+ };
2155
+ }
2156
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DraggableSeriesDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2157
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DraggableSeriesDirective, isStandalone: true, selector: "[tetaDraggableSeries]", inputs: { tetaDraggableSeries: "tetaDraggableSeries", dragDirection: "dragDirection" }, outputs: { moveStart: "moveStart", moveProcess: "moveProcess", moveEnd: "moveEnd" }, host: { listeners: { "pointerdown": "pointerDown($event)", "window:pointermove": "pointerMove($event)", "window:pointerup": "pointerUp($event)", "window:pointercancel": "pointerUp($event)" } }, exportAs: ["tetaDraggableSeries"], ngImport: i0 }); }
2158
+ }
2159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DraggableSeriesDirective, decorators: [{
2160
+ type: Directive,
2161
+ args: [{
2162
+ selector: '[tetaDraggableSeries]',
2163
+ exportAs: 'tetaDraggableSeries',
2164
+ standalone: true,
2165
+ }]
2166
+ }], propDecorators: { tetaDraggableSeries: [{
2167
+ type: Input
2168
+ }], dragDirection: [{
2169
+ type: Input
2170
+ }], moveStart: [{
2171
+ type: Output
2172
+ }], moveProcess: [{
2173
+ type: Output
2174
+ }], moveEnd: [{
2175
+ type: Output
2176
+ }], pointerDown: [{
2177
+ type: HostListener,
2178
+ args: ['pointerdown', ['$event']]
2179
+ }], pointerMove: [{
2180
+ type: HostListener,
2181
+ args: ['window:pointermove', ['$event']]
2182
+ }], pointerUp: [{
2183
+ type: HostListener,
2184
+ args: ['window:pointerup', ['$event']]
2185
+ }, {
2186
+ type: HostListener,
2187
+ args: ['window:pointercancel', ['$event']]
2188
+ }] } });
2189
+
2071
2190
  class LineSeriesComponent extends LinearSeriesBaseComponent {
2072
2191
  constructor() {
2073
2192
  super(...arguments);
2193
+ this.seriesPathOffsets = toSignal(this.svc.seriesPathOffsets, { initialValue: new Map() });
2194
+ this.seriesDragStartOffsets = new Map();
2195
+ this.seriesDragGroup = [];
2196
+ this.seriesDragMoved = false;
2197
+ this.seriesOffsetValue = computed(() => {
2198
+ return this.seriesPathOffsets().get(this.series().id) ?? { x: 0, y: 0 };
2199
+ }, ...(ngDevMode ? [{ debugName: "seriesOffsetValue" }] : /* istanbul ignore next */ []));
2200
+ this.seriesPathTransform = computed(() => {
2201
+ const offset = this.seriesOffsetValue();
2202
+ const offsetX = this.getOffsetPixels(this.x(), offset.x);
2203
+ const offsetY = this.getOffsetPixels(this.y(), offset.y);
2204
+ if (!offsetX && !offsetY) {
2205
+ return null;
2206
+ }
2207
+ return `translate(${offsetX}, ${offsetY})`;
2208
+ }, ...(ngDevMode ? [{ debugName: "seriesPathTransform" }] : /* istanbul ignore next */ []));
2074
2209
  this.allowDrag = (point) => {
2075
2210
  return (newPoint) => {
2076
2211
  if (point.marker.minX !== null && point.marker.minX !== undefined) {
@@ -2097,6 +2232,41 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2097
2232
  };
2098
2233
  };
2099
2234
  }
2235
+ seriesMoveStart(event) {
2236
+ this.seriesDragMoved = false;
2237
+ this.seriesDragGroup = this.getPathDragSeriesGroup();
2238
+ this.seriesDragStartOffsets = new Map(this.seriesDragGroup.map((series) => {
2239
+ return [series.id, this.svc.getSeriesPathOffsets().get(series.id) ?? { x: 0, y: 0 }];
2240
+ }));
2241
+ this.emitSeriesOffset('start', event);
2242
+ }
2243
+ seriesMoveProcess(event) {
2244
+ this.seriesDragMoved = this.seriesDragMoved || Math.abs(event.deltaX) > 3 || Math.abs(event.deltaY) > 3;
2245
+ this.updateSeriesOffset(event);
2246
+ this.emitSeriesOffset('drag', event);
2247
+ }
2248
+ seriesMoveEnd(event) {
2249
+ this.updateSeriesOffset(event);
2250
+ this.emitSeriesOffset('end', event);
2251
+ }
2252
+ seriesPathClick(event) {
2253
+ if (this.seriesDragMoved) {
2254
+ event.stopPropagation();
2255
+ event.preventDefault();
2256
+ this.seriesDragMoved = false;
2257
+ return;
2258
+ }
2259
+ if (!event.ctrlKey && !event.metaKey) {
2260
+ return;
2261
+ }
2262
+ if (!this.series().draggablePath) {
2263
+ return;
2264
+ }
2265
+ event.stopPropagation();
2266
+ event.preventDefault();
2267
+ this.series().selectedForPathDrag = !this.series().selectedForPathDrag;
2268
+ this.cdr.markForCheck();
2269
+ }
2100
2270
  moveStart(event, point) {
2101
2271
  this.start = { x: point.x, y: point.y };
2102
2272
  }
@@ -2139,12 +2309,100 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
2139
2309
  label.dx = this.labelStart.dx + event.deltaX;
2140
2310
  label.dy = this.labelStart.dy + event.deltaY;
2141
2311
  }
2312
+ emitSeriesOffset(type, event) {
2313
+ const offsetValue = this.seriesOffsetValue();
2314
+ const seriesList = this.seriesDragGroup.length ? this.seriesDragGroup : [this.series()];
2315
+ this.svc.emitSeriesOffset({
2316
+ event: {
2317
+ type,
2318
+ sourceEvent: event,
2319
+ },
2320
+ target: {
2321
+ series: this.series(),
2322
+ seriesList,
2323
+ seriesIds: seriesList.map((series) => series.id),
2324
+ offsets: seriesList.map((series) => {
2325
+ const seriesOffsetValue = this.svc.getSeriesPathOffsets().get(series.id) ?? { x: 0, y: 0 };
2326
+ return {
2327
+ series,
2328
+ seriesId: series.id,
2329
+ offsetPx: {
2330
+ x: this.getOffsetPixels(this.x(), seriesOffsetValue.x),
2331
+ y: this.getOffsetPixels(this.y(), seriesOffsetValue.y),
2332
+ },
2333
+ offsetValue: seriesOffsetValue,
2334
+ };
2335
+ }),
2336
+ offsetPx: {
2337
+ x: this.getOffsetPixels(this.x(), offsetValue.x),
2338
+ y: this.getOffsetPixels(this.y(), offsetValue.y),
2339
+ },
2340
+ offsetValue: {
2341
+ x: offsetValue.x,
2342
+ y: offsetValue.y,
2343
+ },
2344
+ },
2345
+ });
2346
+ }
2347
+ updateSeriesOffset(event) {
2348
+ const dragOffset = {
2349
+ x: this.getScaleOffset(this.x(), event.deltaX),
2350
+ y: this.getScaleOffset(this.y(), event.deltaY),
2351
+ };
2352
+ const offsets = new Map(this.svc.getSeriesPathOffsets());
2353
+ const seriesList = this.seriesDragGroup.length ? this.seriesDragGroup : [this.series()];
2354
+ seriesList.forEach((series) => {
2355
+ const startOffset = this.seriesDragStartOffsets.get(series.id) ?? { x: 0, y: 0 };
2356
+ offsets.set(series.id, {
2357
+ x: startOffset.x + dragOffset.x,
2358
+ y: startOffset.y + dragOffset.y,
2359
+ });
2360
+ });
2361
+ this.svc.setSeriesPathOffsets(offsets);
2362
+ }
2363
+ getScaleOffset(scale, offsetPx) {
2364
+ if (!scale?.invert || !scale?.domain) {
2365
+ return 0;
2366
+ }
2367
+ const [domainStart] = scale.domain();
2368
+ const startValue = domainStart instanceof Date ? domainStart.getTime() : domainStart;
2369
+ const nextValue = scale.invert(scale(domainStart) + offsetPx);
2370
+ const normalizedNextValue = nextValue instanceof Date ? nextValue.getTime() : nextValue;
2371
+ return normalizedNextValue - startValue;
2372
+ }
2373
+ getClipOffset() {
2374
+ return this.seriesOffsetValue();
2375
+ }
2376
+ getPathDragSeriesGroup() {
2377
+ const currentSeries = this.series();
2378
+ if (!currentSeries.selectedForPathDrag) {
2379
+ return [currentSeries];
2380
+ }
2381
+ const selectedSeries = this.config()?.series?.filter((series) => {
2382
+ return (series.selectedForPathDrag &&
2383
+ series.draggablePath &&
2384
+ series.visible !== false &&
2385
+ series.enabled !== false &&
2386
+ series.xAxisIndex === currentSeries.xAxisIndex &&
2387
+ series.yAxisIndex === currentSeries.yAxisIndex);
2388
+ });
2389
+ return selectedSeries?.length ? selectedSeries : [currentSeries];
2390
+ }
2391
+ getOffsetPixels(scale, offsetValue) {
2392
+ if (!scale || !scale?.domain || offsetValue === null || offsetValue === undefined) {
2393
+ return 0;
2394
+ }
2395
+ const [domainStart] = scale.domain();
2396
+ const startValue = domainStart instanceof Date ? domainStart.getTime() : domainStart;
2397
+ const nextValue = domainStart instanceof Date ? new Date(startValue + offsetValue) : startValue + offsetValue;
2398
+ return scale(nextValue) - scale(domainStart);
2399
+ }
2142
2400
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LineSeriesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2143
- 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:path\n class=\"line\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n fill=\"none\"\n ></svg:path>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"></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 [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 <svg:circle 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\"></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line [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\"></svg:line>\n <svg:foreignObject [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 <div #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 {{ 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}.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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 }); }
2144
2402
  }
2145
2403
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LineSeriesComponent, decorators: [{
2146
2404
  type: Component,
2147
- args: [{ selector: 'svg:svg[teta-line-series]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, DraggablePointDirective], template: "@if (series(); as series) {\n <svg:path\n class=\"line\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series.color\"\n [attr.stroke-dasharray]=\"series.style?.strokeDasharray\"\n [attr.stroke-width]=\"series.style?.strokeWidth\"\n fill=\"none\"\n ></svg:path>\n @if (transform(); as t) {\n @if (t?.x !== null && t?.x !== undefined && t?.y !== null && t?.y !== undefined) {\n <svg:circle r=\"3\"\n [attr.fill]=\"series.color\"\n [attr.transform]=\"'translate(' + t.x + ', ' + t.y + ')'\"></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 [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 <svg:circle 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\"></svg:circle>\n @if (point.marker.label?.text) {\n <svg:line [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\"></svg:line>\n <svg:foreignObject [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 <div #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 {{ 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}.active{stroke-opacity:.5}.marker-grab{opacity:0}\n"] }]
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"] }]
2148
2406
  }] });
2149
2407
 
2150
2408
  class BarSeriesComponent extends SeriesBaseComponent {
@@ -3016,6 +3274,7 @@ class ChartComponent {
3016
3274
  this.plotBandContextMenu = new EventEmitter();
3017
3275
  this.plotLinesMove = new EventEmitter();
3018
3276
  this.pointMove = new EventEmitter();
3277
+ this.seriesOffsetMove = new EventEmitter();
3019
3278
  this.chartClick = new EventEmitter();
3020
3279
  this.chartContextMenu = new EventEmitter();
3021
3280
  this.annotationContextMenu = new EventEmitter();
@@ -3075,6 +3334,9 @@ class ChartComponent {
3075
3334
  this.chartService.pointMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3076
3335
  this.pointMove.emit(_);
3077
3336
  });
3337
+ this.chartService.seriesOffsetMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3338
+ this.seriesOffsetMove.emit(_);
3339
+ });
3078
3340
  this.chartService.chartClick.pipe(takeWhile(() => this._alive)).subscribe((_) => {
3079
3341
  this.chartClick.emit(_);
3080
3342
  });
@@ -3104,7 +3366,7 @@ class ChartComponent {
3104
3366
  this._alive = false;
3105
3367
  }
3106
3368
  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 }); }
3107
- 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", 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 }); }
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 }); }
3108
3370
  }
3109
3371
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChartComponent, decorators: [{
3110
3372
  type: Component,
@@ -3121,6 +3383,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3121
3383
  type: Output
3122
3384
  }], pointMove: [{
3123
3385
  type: Output
3386
+ }], seriesOffsetMove: [{
3387
+ type: Output
3124
3388
  }], chartClick: [{
3125
3389
  type: Output
3126
3390
  }], chartContextMenu: [{