@tetacom/svg-charts 1.7.37 → 1.7.39
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,11 +1,11 @@
|
|
|
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,
|
|
3
|
+
import { ReplaySubject, filter, BehaviorSubject, Subject, of, shareReplay, withLatestFrom, map, merge, lastValueFrom, take, combineLatest, takeWhile, tap, 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';
|
|
7
7
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
8
|
-
import
|
|
8
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
9
9
|
import { NgTemplateOutlet, AsyncPipe, KeyValuePipe, NgStyle } from '@angular/common';
|
|
10
10
|
import { DialogService, Align as Align$1, TetaSize, ButtonComponent, DropdownComponent, DropdownContentDirective, DropdownHeadDirective, IconComponent, AccordionComponent, AccordionHeadComponent, AccordionItemComponent, AccordionContentDirective, ColorInputComponent, SelectComponent, SelectOptionDirective, SelectValueDirective, ScrollableComponent, InputComponent } from '@tetacom/ng-components';
|
|
11
11
|
import * as i1 from '@angular/forms';
|
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
301
|
-
this.
|
|
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
|
}
|
|
@@ -1169,29 +1196,24 @@ class PositionUtil {
|
|
|
1169
1196
|
}
|
|
1170
1197
|
|
|
1171
1198
|
class TooltipComponent {
|
|
1172
|
-
constructor(
|
|
1173
|
-
this.svc =
|
|
1174
|
-
this.
|
|
1175
|
-
this.
|
|
1176
|
-
this.
|
|
1177
|
-
this.
|
|
1178
|
-
|
|
1199
|
+
constructor() {
|
|
1200
|
+
this.svc = inject(ChartService);
|
|
1201
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1202
|
+
this.pointerMove = toSignal(this.svc.pointerMove);
|
|
1203
|
+
this.display = computed(() => (this.pointerMove()?.type === 'mousemove' ? 1 : 0), ...(ngDevMode ? [{ debugName: "display" }] : /* istanbul ignore next */ []));
|
|
1204
|
+
this.position = computed(() => {
|
|
1205
|
+
const event = this.pointerMove();
|
|
1206
|
+
if (!this.display() || !event) {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
return this.getPosition(event);
|
|
1210
|
+
}, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
1179
1211
|
this.tooltips = this.svc.tooltips.pipe(map((_) => [..._.values()]));
|
|
1180
1212
|
}
|
|
1181
1213
|
getImplicit(t) {
|
|
1182
1214
|
return { $implicit: t };
|
|
1183
1215
|
}
|
|
1184
1216
|
ngOnInit() {
|
|
1185
|
-
this.display = this.svc.pointerMove.pipe(map((event) => {
|
|
1186
|
-
return event.type === 'mousemove' ? 1 : 0;
|
|
1187
|
-
}), tap(() => {
|
|
1188
|
-
setTimeout(() => {
|
|
1189
|
-
this.cdr.detectChanges();
|
|
1190
|
-
});
|
|
1191
|
-
}));
|
|
1192
|
-
this.position = this.svc.pointerMove.pipe(filter((event) => !!event), map((_) => {
|
|
1193
|
-
return this.getPosition(_);
|
|
1194
|
-
}), tap(() => this.cdr.detectChanges()));
|
|
1195
1217
|
const transformHtml = (html) => {
|
|
1196
1218
|
return this.sanitizer.bypassSecurityTrustHtml(html);
|
|
1197
1219
|
};
|
|
@@ -1216,8 +1238,7 @@ class TooltipComponent {
|
|
|
1216
1238
|
if (tooltipList?.length < 1) {
|
|
1217
1239
|
return '';
|
|
1218
1240
|
}
|
|
1219
|
-
|
|
1220
|
-
return formatted;
|
|
1241
|
+
return formatter ? transformHtml(formatter(tooltipList)) : defaultFormatter(tooltipList);
|
|
1221
1242
|
}));
|
|
1222
1243
|
}
|
|
1223
1244
|
getPosition(event) {
|
|
@@ -1239,13 +1260,13 @@ class TooltipComponent {
|
|
|
1239
1260
|
const format = d3.format(',.5~r');
|
|
1240
1261
|
return format(input);
|
|
1241
1262
|
}
|
|
1242
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TooltipComponent, deps: [
|
|
1243
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TooltipComponent, isStandalone: true, selector: "teta-tooltip", inputs: { size: "size", config: "config" }, viewQueries: [{ propertyName: "tooltip", first: true, predicate: ["tooltip"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if ({ p: position
|
|
1263
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1264
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TooltipComponent, isStandalone: true, selector: "teta-tooltip", inputs: { size: "size", config: "config" }, viewQueries: [{ propertyName: "tooltip", first: true, predicate: ["tooltip"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if ({ p: position() }; as data) {\n @if (tooltips | async; as t) {\n @if (t.length > 0) {\n <div\n class=\"chart-tooltip color-text-90 bg-global-bgcard shadow-2\"\n #tooltip\n [style.position]=\"'fixed'\"\n [style.opacity]=\"display()\"\n [hidden]=\"!data.p\"\n style=\"pointer-events: none; min-width: 200px\"\n [style.left.px]=\"data.p?.left\"\n [style.top.px]=\"data.p?.top\"\n [style.bottom.px]=\"data.p?.bottom\"\n [style.right.px]=\"data.p?.right\"\n >\n @if (config.tooltip?.template) {\n <ng-container *ngTemplateOutlet=\"config.tooltip.template; context: getImplicit(t)\"></ng-container>\n }\n @if (config.tooltip?.format) {\n <div [innerHTML]=\"displayTooltips | async\"></div>\n } @else {\n @if (!config.tooltip?.template) {\n <div class=\"padding-2 border-radius-1\">\n @for (tooltip of t; track $index) {\n <div class=\"display-flex align-center\">\n <span\n class=\"display-block margin-right-1\"\n [style.width.px]=\"10\"\n [style.height.px]=\"2\"\n [style.background-color]=\"tooltip.series?.color\"\n >\n </span>\n <span class=\"font-title-3\">\n {{ tooltip.series?.name }}\n <span class=\"font-body-3\">\n x: {{ format(tooltip.point?.x) }} y: {{ format(tooltip.point?.y) }}\n </span>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n }\n}\n", styles: [":host{position:absolute;z-index:2}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1244
1265
|
}
|
|
1245
1266
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TooltipComponent, decorators: [{
|
|
1246
1267
|
type: Component,
|
|
1247
|
-
args: [{ selector: 'teta-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, NgTemplateOutlet], template: "@if ({ p: position
|
|
1248
|
-
}], ctorParameters: () => [
|
|
1268
|
+
args: [{ selector: 'teta-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, NgTemplateOutlet], template: "@if ({ p: position() }; as data) {\n @if (tooltips | async; as t) {\n @if (t.length > 0) {\n <div\n class=\"chart-tooltip color-text-90 bg-global-bgcard shadow-2\"\n #tooltip\n [style.position]=\"'fixed'\"\n [style.opacity]=\"display()\"\n [hidden]=\"!data.p\"\n style=\"pointer-events: none; min-width: 200px\"\n [style.left.px]=\"data.p?.left\"\n [style.top.px]=\"data.p?.top\"\n [style.bottom.px]=\"data.p?.bottom\"\n [style.right.px]=\"data.p?.right\"\n >\n @if (config.tooltip?.template) {\n <ng-container *ngTemplateOutlet=\"config.tooltip.template; context: getImplicit(t)\"></ng-container>\n }\n @if (config.tooltip?.format) {\n <div [innerHTML]=\"displayTooltips | async\"></div>\n } @else {\n @if (!config.tooltip?.template) {\n <div class=\"padding-2 border-radius-1\">\n @for (tooltip of t; track $index) {\n <div class=\"display-flex align-center\">\n <span\n class=\"display-block margin-right-1\"\n [style.width.px]=\"10\"\n [style.height.px]=\"2\"\n [style.background-color]=\"tooltip.series?.color\"\n >\n </span>\n <span class=\"font-title-3\">\n {{ tooltip.series?.name }}\n <span class=\"font-body-3\">\n x: {{ format(tooltip.point?.x) }} y: {{ format(tooltip.point?.y) }}\n </span>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n }\n}\n", styles: [":host{position:absolute;z-index:2}\n"] }]
|
|
1269
|
+
}], ctorParameters: () => [], propDecorators: { size: [{
|
|
1249
1270
|
type: Input
|
|
1250
1271
|
}], config: [{
|
|
1251
1272
|
type: Input
|
|
@@ -1767,9 +1788,17 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
|
|
|
1767
1788
|
this.markers = computed(() => {
|
|
1768
1789
|
return this.series().data?.filter((_) => _?.marker && _?.x !== undefined && _?.y !== undefined && _?.x !== null && _?.y !== null);
|
|
1769
1790
|
}, ...(ngDevMode ? [{ debugName: "markers" }] : /* istanbul ignore next */ []));
|
|
1791
|
+
this.sortedTooltipData = computed(() => {
|
|
1792
|
+
this.update();
|
|
1793
|
+
const data = [...this.series().data];
|
|
1794
|
+
const tooltipTracking = this.config()?.tooltip?.tracking;
|
|
1795
|
+
return data.sort(tooltipTracking === TooltipTracking.y ? (a, b) => a.y - b.y : (a, b) => a.x - b.x);
|
|
1796
|
+
}, ...(ngDevMode ? [{ debugName: "sortedTooltipData" }] : /* istanbul ignore next */ []));
|
|
1770
1797
|
this.path = computed(() => {
|
|
1771
1798
|
this.update();
|
|
1772
|
-
|
|
1799
|
+
const x = this.x();
|
|
1800
|
+
const y = this.y();
|
|
1801
|
+
if (!x || !y) {
|
|
1773
1802
|
return '';
|
|
1774
1803
|
}
|
|
1775
1804
|
const filter = this.defaultClipPointsMapping.get(this.series().clipPointsDirection);
|
|
@@ -1781,18 +1810,18 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
|
|
|
1781
1810
|
point.y !== undefined &&
|
|
1782
1811
|
!isNaN(point.x) &&
|
|
1783
1812
|
!isNaN(point.y))
|
|
1784
|
-
.x((point) =>
|
|
1785
|
-
.y((point) =>
|
|
1813
|
+
.x((point) => x(point.x))
|
|
1814
|
+
.y((point) => y(point.y));
|
|
1786
1815
|
let filteredData = this.series().data;
|
|
1787
1816
|
const clipOffset = this.getClipOffset();
|
|
1788
1817
|
if (this.series().clipPointsDirection === ClipPointsDirection.x) {
|
|
1789
|
-
let [min, max] =
|
|
1818
|
+
let [min, max] = x.domain();
|
|
1790
1819
|
min = min instanceof Date ? min.getTime() : min;
|
|
1791
1820
|
max = max instanceof Date ? max.getTime() : max;
|
|
1792
1821
|
filteredData = filteredData?.filter(filter(min - clipOffset.x, max - clipOffset.x));
|
|
1793
1822
|
}
|
|
1794
1823
|
if (this.series().clipPointsDirection === ClipPointsDirection.y) {
|
|
1795
|
-
let [min, max] =
|
|
1824
|
+
let [min, max] = y.domain();
|
|
1796
1825
|
min = min instanceof Date ? min.getTime() : min;
|
|
1797
1826
|
max = max instanceof Date ? max.getTime() : max;
|
|
1798
1827
|
filteredData = filteredData?.filter(filter(min - clipOffset.y, max - clipOffset.y));
|
|
@@ -1842,6 +1871,13 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
|
|
|
1842
1871
|
}
|
|
1843
1872
|
const mouse = [event?.offsetX, event?.offsetY];
|
|
1844
1873
|
const tooltipTracking = this.config()?.tooltip?.tracking;
|
|
1874
|
+
const clipOffset = this.getClipOffset();
|
|
1875
|
+
const addOffset = (value, offset) => {
|
|
1876
|
+
return value instanceof Date ? new Date(value.getTime() + offset) : value + offset;
|
|
1877
|
+
};
|
|
1878
|
+
const subtractOffset = (value, offset) => {
|
|
1879
|
+
return value instanceof Date ? value.getTime() - offset : value - offset;
|
|
1880
|
+
};
|
|
1845
1881
|
const lineIntersection = (p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) => {
|
|
1846
1882
|
const rV = {};
|
|
1847
1883
|
const s1_x = p1_x - p0_x;
|
|
@@ -1857,16 +1893,17 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
|
|
|
1857
1893
|
return rV;
|
|
1858
1894
|
};
|
|
1859
1895
|
if (tooltipTracking === TooltipTracking.x) {
|
|
1860
|
-
const sortedData =
|
|
1896
|
+
const sortedData = this.sortedTooltipData();
|
|
1861
1897
|
const bisect = d3.bisector((_) => _.x).right;
|
|
1862
1898
|
const pointer = mouse[0];
|
|
1863
1899
|
let x0 = scaleX.invert(pointer);
|
|
1864
1900
|
if (x0 instanceof Date) {
|
|
1865
1901
|
x0 = x0.getTime();
|
|
1866
1902
|
}
|
|
1867
|
-
|
|
1903
|
+
x0 = subtractOffset(x0, clipOffset.x);
|
|
1904
|
+
const rightId = bisect(sortedData, x0);
|
|
1868
1905
|
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));
|
|
1906
|
+
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
1907
|
const x = scaleX.invert(intersect.x);
|
|
1871
1908
|
const y = scaleY.invert(intersect.y);
|
|
1872
1909
|
if (x !== null && x !== undefined && !isNaN(x) && y !== null && y !== undefined && !isNaN(y)) {
|
|
@@ -1890,15 +1927,16 @@ class LinearSeriesBaseComponent extends SeriesBaseComponent {
|
|
|
1890
1927
|
};
|
|
1891
1928
|
}
|
|
1892
1929
|
if (tooltipTracking === TooltipTracking.y) {
|
|
1893
|
-
const sortedData =
|
|
1930
|
+
const sortedData = this.sortedTooltipData();
|
|
1894
1931
|
const bisect = d3.bisector((_) => _.y).right;
|
|
1895
1932
|
let y0 = scaleY.invert(mouse[1]);
|
|
1896
1933
|
if (y0 instanceof Date) {
|
|
1897
1934
|
y0 = y0.getTime();
|
|
1898
1935
|
}
|
|
1936
|
+
y0 = subtractOffset(y0, clipOffset.y);
|
|
1899
1937
|
const rightId = bisect(sortedData, y0);
|
|
1900
1938
|
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));
|
|
1939
|
+
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
1940
|
const x = scaleX.invert(intersect.x);
|
|
1903
1941
|
const y = scaleY.invert(intersect.y);
|
|
1904
1942
|
if (x !== null && x !== undefined && !isNaN(x) && y !== null && y !== undefined && !isNaN(y)) {
|
|
@@ -2191,12 +2229,16 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2191
2229
|
constructor() {
|
|
2192
2230
|
super(...arguments);
|
|
2193
2231
|
this.seriesPathOffsets = toSignal(this.svc.seriesPathOffsets, { initialValue: new Map() });
|
|
2232
|
+
this.selectedSeriesIds = toSignal(this.svc.selectedSeriesIds, { initialValue: [] });
|
|
2194
2233
|
this.seriesDragStartOffsets = new Map();
|
|
2195
2234
|
this.seriesDragGroup = [];
|
|
2196
2235
|
this.seriesDragMoved = false;
|
|
2197
2236
|
this.seriesOffsetValue = computed(() => {
|
|
2198
2237
|
return this.seriesPathOffsets().get(this.series().id) ?? { x: 0, y: 0 };
|
|
2199
2238
|
}, ...(ngDevMode ? [{ debugName: "seriesOffsetValue" }] : /* istanbul ignore next */ []));
|
|
2239
|
+
this.seriesSelectedForPathDrag = computed(() => {
|
|
2240
|
+
return this.isSeriesSelectedForPathDrag(this.series());
|
|
2241
|
+
}, ...(ngDevMode ? [{ debugName: "seriesSelectedForPathDrag" }] : /* istanbul ignore next */ []));
|
|
2200
2242
|
this.seriesPathTransform = computed(() => {
|
|
2201
2243
|
const offset = this.seriesOffsetValue();
|
|
2202
2244
|
const offsetX = this.getOffsetPixels(this.x(), offset.x);
|
|
@@ -2238,16 +2280,16 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2238
2280
|
this.seriesDragStartOffsets = new Map(this.seriesDragGroup.map((series) => {
|
|
2239
2281
|
return [series.id, this.svc.getSeriesPathOffsets().get(series.id) ?? { x: 0, y: 0 }];
|
|
2240
2282
|
}));
|
|
2241
|
-
this.
|
|
2283
|
+
this.emitSeriesMove('start', event);
|
|
2242
2284
|
}
|
|
2243
2285
|
seriesMoveProcess(event) {
|
|
2244
2286
|
this.seriesDragMoved = this.seriesDragMoved || Math.abs(event.deltaX) > 3 || Math.abs(event.deltaY) > 3;
|
|
2245
2287
|
this.updateSeriesOffset(event);
|
|
2246
|
-
this.
|
|
2288
|
+
this.emitSeriesMove('drag', event);
|
|
2247
2289
|
}
|
|
2248
2290
|
seriesMoveEnd(event) {
|
|
2249
2291
|
this.updateSeriesOffset(event);
|
|
2250
|
-
this.
|
|
2292
|
+
this.emitSeriesMove('end', event);
|
|
2251
2293
|
}
|
|
2252
2294
|
seriesPathClick(event) {
|
|
2253
2295
|
if (this.seriesDragMoved) {
|
|
@@ -2264,8 +2306,7 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2264
2306
|
}
|
|
2265
2307
|
event.stopPropagation();
|
|
2266
2308
|
event.preventDefault();
|
|
2267
|
-
this.
|
|
2268
|
-
this.cdr.markForCheck();
|
|
2309
|
+
this.svc.toggleSelectedSeriesId(this.series().id);
|
|
2269
2310
|
}
|
|
2270
2311
|
moveStart(event, point) {
|
|
2271
2312
|
this.start = { x: point.x, y: point.y };
|
|
@@ -2309,10 +2350,10 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2309
2350
|
label.dx = this.labelStart.dx + event.deltaX;
|
|
2310
2351
|
label.dy = this.labelStart.dy + event.deltaY;
|
|
2311
2352
|
}
|
|
2312
|
-
|
|
2353
|
+
emitSeriesMove(type, event) {
|
|
2313
2354
|
const offsetValue = this.seriesOffsetValue();
|
|
2314
2355
|
const seriesList = this.seriesDragGroup.length ? this.seriesDragGroup : [this.series()];
|
|
2315
|
-
this.svc.
|
|
2356
|
+
this.svc.emitSeriesMove({
|
|
2316
2357
|
event: {
|
|
2317
2358
|
type,
|
|
2318
2359
|
sourceEvent: event,
|
|
@@ -2375,19 +2416,24 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2375
2416
|
}
|
|
2376
2417
|
getPathDragSeriesGroup() {
|
|
2377
2418
|
const currentSeries = this.series();
|
|
2378
|
-
if (!currentSeries
|
|
2419
|
+
if (!this.isSeriesSelectedForPathDrag(currentSeries)) {
|
|
2379
2420
|
return [currentSeries];
|
|
2380
2421
|
}
|
|
2422
|
+
const selectedIds = this.svc.getSelectedSeriesIds();
|
|
2381
2423
|
const selectedSeries = this.config()?.series?.filter((series) => {
|
|
2382
|
-
return (series.
|
|
2424
|
+
return (selectedIds.includes(series.id) &&
|
|
2383
2425
|
series.draggablePath &&
|
|
2384
2426
|
series.visible !== false &&
|
|
2385
2427
|
series.enabled !== false &&
|
|
2386
|
-
series.
|
|
2387
|
-
series.
|
|
2428
|
+
series.pathDragType === currentSeries.pathDragType &&
|
|
2429
|
+
((currentSeries.pathDragType === DragPointType.x && series.xAxisIndex === currentSeries.xAxisIndex) ||
|
|
2430
|
+
(currentSeries.pathDragType === DragPointType.y && series.yAxisIndex === currentSeries.yAxisIndex)));
|
|
2388
2431
|
});
|
|
2389
2432
|
return selectedSeries?.length ? selectedSeries : [currentSeries];
|
|
2390
2433
|
}
|
|
2434
|
+
isSeriesSelectedForPathDrag(series) {
|
|
2435
|
+
return this.selectedSeriesIds().includes(series.id);
|
|
2436
|
+
}
|
|
2391
2437
|
getOffsetPixels(scale, offsetValue) {
|
|
2392
2438
|
if (!scale || !scale?.domain || offsetValue === null || offsetValue === undefined) {
|
|
2393
2439
|
return 0;
|
|
@@ -2398,24 +2444,27 @@ class LineSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2398
2444
|
return scale(nextValue) - scale(domainStart);
|
|
2399
2445
|
}
|
|
2400
2446
|
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]=\"
|
|
2447
|
+
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: x(), y: y() }; as scales) {\n @if (scales.x && scales.y) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + scales.x(point.x) + ',' + scales.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}\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
2448
|
}
|
|
2403
2449
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LineSeriesComponent, decorators: [{
|
|
2404
2450
|
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]=\"
|
|
2451
|
+
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: x(), y: y() }; as scales) {\n @if (scales.x && scales.y) {\n @for (point of draggablePoints; track point) {\n <svg:g [attr.transform]=\"'translate(' + scales.x(point.x) + ',' + scales.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}\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
2452
|
}] });
|
|
2407
2453
|
|
|
2408
2454
|
class BarSeriesComponent extends SeriesBaseComponent {
|
|
2409
2455
|
constructor() {
|
|
2410
2456
|
super(...arguments);
|
|
2411
2457
|
this.x1 = computed(() => {
|
|
2412
|
-
const
|
|
2458
|
+
const series = this.series();
|
|
2459
|
+
const x = this.xScales().get(series.xAxisIndex)?.scale;
|
|
2413
2460
|
const range = x.range();
|
|
2414
|
-
const domain =
|
|
2461
|
+
const domain = series.data.map((_) => _.x);
|
|
2415
2462
|
return d3.scaleBand().range([0, range[1]]).domain(domain).padding(0.1);
|
|
2416
2463
|
}, ...(ngDevMode ? [{ debugName: "x1" }] : /* istanbul ignore next */ []));
|
|
2417
2464
|
this.barSeriesCount = computed(() => {
|
|
2418
|
-
const
|
|
2465
|
+
const config = this.config();
|
|
2466
|
+
const series = this.series();
|
|
2467
|
+
const count = config.series.filter((_) => _.type === SeriesType.bar && _.xAxisIndex === series.xAxisIndex);
|
|
2419
2468
|
return count.length;
|
|
2420
2469
|
}, ...(ngDevMode ? [{ debugName: "barSeriesCount" }] : /* istanbul ignore next */ []));
|
|
2421
2470
|
this.Math = Math;
|
|
@@ -2445,12 +2494,14 @@ class BlockSeriesComponent extends SeriesBaseComponent {
|
|
|
2445
2494
|
constructor() {
|
|
2446
2495
|
super(...arguments);
|
|
2447
2496
|
this.displayPoints = computed(() => {
|
|
2448
|
-
const
|
|
2497
|
+
const config = this.config();
|
|
2498
|
+
const series = this.series();
|
|
2499
|
+
const domain = config.inverted ? this.y().domain() : this.x().domain();
|
|
2449
2500
|
const [min, max] = domain;
|
|
2450
|
-
return
|
|
2501
|
+
return series.data.filter((point, index, arr) => {
|
|
2451
2502
|
const next = arr[index + 1];
|
|
2452
2503
|
const secondNext = arr[index + 2];
|
|
2453
|
-
if (
|
|
2504
|
+
if (config.inverted) {
|
|
2454
2505
|
if (point.y1 === null || point.y1 === undefined) {
|
|
2455
2506
|
point.y1 = next?.y ?? point.y;
|
|
2456
2507
|
}
|
|
@@ -2487,12 +2538,14 @@ class BlockAreaSeriesComponent extends SeriesBaseComponent {
|
|
|
2487
2538
|
constructor() {
|
|
2488
2539
|
super(...arguments);
|
|
2489
2540
|
this.displayPoints = computed(() => {
|
|
2490
|
-
const
|
|
2541
|
+
const config = this.config();
|
|
2542
|
+
const series = this.series();
|
|
2543
|
+
const domain = config.inverted ? this.y().domain() : this.x().domain();
|
|
2491
2544
|
const [min, max] = domain;
|
|
2492
|
-
return
|
|
2545
|
+
return series.data.filter((point, index, arr) => {
|
|
2493
2546
|
const next = arr[index + 1];
|
|
2494
2547
|
const secondNext = arr[index + 2];
|
|
2495
|
-
if (
|
|
2548
|
+
if (config.inverted) {
|
|
2496
2549
|
if (point.y1 === null || point.y1 === undefined) {
|
|
2497
2550
|
point.y1 = next?.y ?? point.y;
|
|
2498
2551
|
}
|
|
@@ -2529,34 +2582,38 @@ class AreaSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2529
2582
|
constructor() {
|
|
2530
2583
|
super(...arguments);
|
|
2531
2584
|
this.areaPath = computed(() => {
|
|
2532
|
-
|
|
2585
|
+
const x = this.x();
|
|
2586
|
+
const y = this.y();
|
|
2587
|
+
const config = this.config();
|
|
2588
|
+
const series = this.series();
|
|
2589
|
+
if (!x || !y) {
|
|
2533
2590
|
return '';
|
|
2534
2591
|
}
|
|
2535
2592
|
const area = d3
|
|
2536
2593
|
.area()
|
|
2537
2594
|
.defined((point) => point.x !== null && point.y !== null && !isNaN(point.x) && !isNaN(point.y));
|
|
2538
|
-
if (
|
|
2595
|
+
if (config.inverted) {
|
|
2539
2596
|
area
|
|
2540
|
-
.x1((_) => (_.x1 !== null && _.x1 !== undefined ?
|
|
2541
|
-
.x0((_) =>
|
|
2542
|
-
.y((_) =>
|
|
2597
|
+
.x1((_) => (_.x1 !== null && _.x1 !== undefined ? x(_.x1) : x(0)))
|
|
2598
|
+
.x0((_) => x(_.x))
|
|
2599
|
+
.y((_) => y(_.y));
|
|
2543
2600
|
}
|
|
2544
2601
|
else {
|
|
2545
2602
|
area
|
|
2546
|
-
.y1((_) => (_.y1 !== null && _.y1 !== undefined ?
|
|
2547
|
-
.y0((_) =>
|
|
2548
|
-
.x((_) =>
|
|
2549
|
-
}
|
|
2550
|
-
const filter = this.defaultClipPointsMapping.get(
|
|
2551
|
-
let filteredData =
|
|
2552
|
-
if (
|
|
2553
|
-
let [min, max] =
|
|
2603
|
+
.y1((_) => (_.y1 !== null && _.y1 !== undefined ? y(_.y1) : y(0)))
|
|
2604
|
+
.y0((_) => y(_.y))
|
|
2605
|
+
.x((_) => x(_.x));
|
|
2606
|
+
}
|
|
2607
|
+
const filter = this.defaultClipPointsMapping.get(series.clipPointsDirection);
|
|
2608
|
+
let filteredData = series.data;
|
|
2609
|
+
if (series.clipPointsDirection === ClipPointsDirection.x) {
|
|
2610
|
+
let [min, max] = x.domain();
|
|
2554
2611
|
min = min instanceof Date ? min.getTime() : min;
|
|
2555
2612
|
max = max instanceof Date ? max.getTime() : max;
|
|
2556
2613
|
filteredData = filteredData?.filter(filter(min, max));
|
|
2557
2614
|
}
|
|
2558
|
-
if (
|
|
2559
|
-
let [min, max] =
|
|
2615
|
+
if (series.clipPointsDirection === ClipPointsDirection.y) {
|
|
2616
|
+
let [min, max] = y.domain();
|
|
2560
2617
|
min = min instanceof Date ? min.getTime() : min;
|
|
2561
2618
|
max = max instanceof Date ? max.getTime() : max;
|
|
2562
2619
|
filteredData = filteredData?.filter(filter(min, max));
|
|
@@ -2567,11 +2624,11 @@ class AreaSeriesComponent extends LinearSeriesBaseComponent {
|
|
|
2567
2624
|
this.FillDirection = FillDirection;
|
|
2568
2625
|
}
|
|
2569
2626
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AreaSeriesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2570
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AreaSeriesComponent, isStandalone: true, selector: "svg:svg[teta-area-series]", usesInheritance: true, ngImport: i0, template: "@if (series()?.fillType === FillType.gradient) {\n <svg:defs>\n <svg:linearGradient [id]=\"'gradient-fill-' + id\"\n gradientUnits=\"userSpaceOnUse\"\n x1=\"0%\"\n [attr.y1]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '0%' : '100%'\"\n [attr.x2]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '100%' : '0%'\"\n y2=\"0%\">\n <svg:stop offset=\"0%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0\"></svg:stop>\n <svg:stop offset=\"5%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.1\"></svg:stop>\n <svg:stop offset=\"20%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.2\"></svg:stop>\n <svg:stop offset=\"60%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.5\"></svg:stop>\n <svg:stop offset=\"100%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.8\"></svg:stop>\n </svg:linearGradient>\n </svg:defs>\n}\n<svg:path class=\"area\"\n [attr.d]=\"areaPath()\"\n [attr.stroke-width]=\"0\"\n [attr.fill-opacity]=\"series().style?.fillOpacity\"\n [attr.fill]=\"series().fillType === FillType.gradient ? 'url(#gradient-fill-' + id + ')' : (series().style?.fill ?? series().color)\"></svg:path>\n<svg:path class=\"area\"\n fill=\"none\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series().color\"\n [attr.stroke-dasharray]=\"series().style?.strokeDasharray\"\n [attr.stroke-width]=\"series().style?.strokeWidth\"></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
|
|
2627
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AreaSeriesComponent, isStandalone: true, selector: "svg:svg[teta-area-series]", usesInheritance: true, ngImport: i0, template: "@if (series()?.fillType === FillType.gradient) {\n <svg:defs>\n <svg:linearGradient [id]=\"'gradient-fill-' + id\"\n gradientUnits=\"userSpaceOnUse\"\n x1=\"0%\"\n [attr.y1]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '0%' : '100%'\"\n [attr.x2]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '100%' : '0%'\"\n y2=\"0%\">\n <svg:stop offset=\"0%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0\"></svg:stop>\n <svg:stop offset=\"5%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.1\"></svg:stop>\n <svg:stop offset=\"20%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.2\"></svg:stop>\n <svg:stop offset=\"60%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.5\"></svg:stop>\n <svg:stop offset=\"100%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.8\"></svg:stop>\n </svg:linearGradient>\n </svg:defs>\n}\n<svg:path class=\"area\"\n [attr.d]=\"areaPath()\"\n [attr.stroke-width]=\"0\"\n [attr.fill-opacity]=\"series().style?.fillOpacity\"\n [attr.fill]=\"series().fillType === FillType.gradient ? 'url(#gradient-fill-' + id + ')' : (series().style?.fill ?? series().color)\"></svg:path>\n<svg:path class=\"area\"\n fill=\"none\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series().color\"\n [attr.stroke-dasharray]=\"series().style?.strokeDasharray\"\n [attr.stroke-width]=\"series().style?.strokeWidth\"></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 ({ points: markers(), x: x(), y: y() }; as data) {\n @if (data.x && data.y) {\n @for (point of data.points; track point) {\n <svg:circle class=\"marker\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\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]=\"data.x(point.x)\"\n [attr.cy]=\"data.y(point.y)\"\n ></svg:circle>\n }\n }\n}\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2571
2628
|
}
|
|
2572
2629
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AreaSeriesComponent, decorators: [{
|
|
2573
2630
|
type: Component,
|
|
2574
|
-
args: [{ selector: 'svg:svg[teta-area-series]', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (series()?.fillType === FillType.gradient) {\n <svg:defs>\n <svg:linearGradient [id]=\"'gradient-fill-' + id\"\n gradientUnits=\"userSpaceOnUse\"\n x1=\"0%\"\n [attr.y1]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '0%' : '100%'\"\n [attr.x2]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '100%' : '0%'\"\n y2=\"0%\">\n <svg:stop offset=\"0%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0\"></svg:stop>\n <svg:stop offset=\"5%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.1\"></svg:stop>\n <svg:stop offset=\"20%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.2\"></svg:stop>\n <svg:stop offset=\"60%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.5\"></svg:stop>\n <svg:stop offset=\"100%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.8\"></svg:stop>\n </svg:linearGradient>\n </svg:defs>\n}\n<svg:path class=\"area\"\n [attr.d]=\"areaPath()\"\n [attr.stroke-width]=\"0\"\n [attr.fill-opacity]=\"series().style?.fillOpacity\"\n [attr.fill]=\"series().fillType === FillType.gradient ? 'url(#gradient-fill-' + id + ')' : (series().style?.fill ?? series().color)\"></svg:path>\n<svg:path class=\"area\"\n fill=\"none\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series().color\"\n [attr.stroke-dasharray]=\"series().style?.strokeDasharray\"\n [attr.stroke-width]=\"series().style?.strokeWidth\"></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
|
|
2631
|
+
args: [{ selector: 'svg:svg[teta-area-series]', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (series()?.fillType === FillType.gradient) {\n <svg:defs>\n <svg:linearGradient [id]=\"'gradient-fill-' + id\"\n gradientUnits=\"userSpaceOnUse\"\n x1=\"0%\"\n [attr.y1]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '0%' : '100%'\"\n [attr.x2]=\"config()?.inverted || series()?.fillDirection === FillDirection.y ? '100%' : '0%'\"\n y2=\"0%\">\n <svg:stop offset=\"0%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0\"></svg:stop>\n <svg:stop offset=\"5%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.1\"></svg:stop>\n <svg:stop offset=\"20%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.2\"></svg:stop>\n <svg:stop offset=\"60%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.5\"></svg:stop>\n <svg:stop offset=\"100%\" [attr.stop-color]=\"series().color\" stop-opacity=\"0.8\"></svg:stop>\n </svg:linearGradient>\n </svg:defs>\n}\n<svg:path class=\"area\"\n [attr.d]=\"areaPath()\"\n [attr.stroke-width]=\"0\"\n [attr.fill-opacity]=\"series().style?.fillOpacity\"\n [attr.fill]=\"series().fillType === FillType.gradient ? 'url(#gradient-fill-' + id + ')' : (series().style?.fill ?? series().color)\"></svg:path>\n<svg:path class=\"area\"\n fill=\"none\"\n [attr.d]=\"path()\"\n [attr.stroke]=\"series().color\"\n [attr.stroke-dasharray]=\"series().style?.strokeDasharray\"\n [attr.stroke-width]=\"series().style?.strokeWidth\"></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 ({ points: markers(), x: x(), y: y() }; as data) {\n @if (data.x && data.y) {\n @for (point of data.points; track point) {\n <svg:circle class=\"marker\"\n [class.draggable-marker]=\"point?.marker?.draggable\"\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]=\"data.x(point.x)\"\n [attr.cy]=\"data.y(point.y)\"\n ></svg:circle>\n }\n }\n}\n" }]
|
|
2575
2632
|
}] });
|
|
2576
2633
|
|
|
2577
2634
|
const defaultSeriesTypeMapping = new Map()
|
|
@@ -3263,6 +3320,9 @@ class ChartComponent {
|
|
|
3263
3320
|
this.chartService.setConfig(config);
|
|
3264
3321
|
this.zoomService.setBroadcastChannel(config?.zoom?.syncChannel);
|
|
3265
3322
|
}
|
|
3323
|
+
set selectedSeriesIds(seriesIds) {
|
|
3324
|
+
this.chartService.setSelectedSeriesIds(seriesIds ?? []);
|
|
3325
|
+
}
|
|
3266
3326
|
constructor(chartService, zoomService, brushService, scaleService) {
|
|
3267
3327
|
this.chartService = chartService;
|
|
3268
3328
|
this.zoomService = zoomService;
|
|
@@ -3274,7 +3334,8 @@ class ChartComponent {
|
|
|
3274
3334
|
this.plotBandContextMenu = new EventEmitter();
|
|
3275
3335
|
this.plotLinesMove = new EventEmitter();
|
|
3276
3336
|
this.pointMove = new EventEmitter();
|
|
3277
|
-
this.
|
|
3337
|
+
this.seriesMove = new EventEmitter();
|
|
3338
|
+
this.selectedSeriesIdsChange = new EventEmitter();
|
|
3278
3339
|
this.chartClick = new EventEmitter();
|
|
3279
3340
|
this.chartContextMenu = new EventEmitter();
|
|
3280
3341
|
this.annotationContextMenu = new EventEmitter();
|
|
@@ -3334,8 +3395,13 @@ class ChartComponent {
|
|
|
3334
3395
|
this.chartService.pointMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
|
|
3335
3396
|
this.pointMove.emit(_);
|
|
3336
3397
|
});
|
|
3337
|
-
this.chartService.
|
|
3338
|
-
this.
|
|
3398
|
+
this.chartService.seriesMove.pipe(takeWhile(() => this._alive)).subscribe((_) => {
|
|
3399
|
+
this.seriesMove.emit(_);
|
|
3400
|
+
});
|
|
3401
|
+
this.chartService.selectedSeriesIds
|
|
3402
|
+
.pipe(takeWhile(() => this._alive), distinctUntilChanged((prev, next) => this.areSeriesIdsEqual(prev, next)))
|
|
3403
|
+
.subscribe((_) => {
|
|
3404
|
+
this.selectedSeriesIdsChange.emit(_);
|
|
3339
3405
|
});
|
|
3340
3406
|
this.chartService.chartClick.pipe(takeWhile(() => this._alive)).subscribe((_) => {
|
|
3341
3407
|
this.chartClick.emit(_);
|
|
@@ -3365,8 +3431,11 @@ class ChartComponent {
|
|
|
3365
3431
|
ngOnDestroy() {
|
|
3366
3432
|
this._alive = false;
|
|
3367
3433
|
}
|
|
3434
|
+
areSeriesIdsEqual(first, second) {
|
|
3435
|
+
return first.length === second.length && first.every((id, index) => id === second[index]);
|
|
3436
|
+
}
|
|
3368
3437
|
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",
|
|
3438
|
+
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
3439
|
}
|
|
3371
3440
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChartComponent, decorators: [{
|
|
3372
3441
|
type: Component,
|
|
@@ -3383,7 +3452,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3383
3452
|
type: Output
|
|
3384
3453
|
}], pointMove: [{
|
|
3385
3454
|
type: Output
|
|
3386
|
-
}],
|
|
3455
|
+
}], seriesMove: [{
|
|
3456
|
+
type: Output
|
|
3457
|
+
}], selectedSeriesIdsChange: [{
|
|
3387
3458
|
type: Output
|
|
3388
3459
|
}], chartClick: [{
|
|
3389
3460
|
type: Output
|
|
@@ -3403,6 +3474,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3403
3474
|
type: Output
|
|
3404
3475
|
}], config: [{
|
|
3405
3476
|
type: Input
|
|
3477
|
+
}], selectedSeriesIds: [{
|
|
3478
|
+
type: Input
|
|
3406
3479
|
}] } });
|
|
3407
3480
|
|
|
3408
3481
|
class PlotBand {
|
|
@@ -3427,8 +3500,9 @@ class BlockHorizontalSeriesComponent extends SeriesBaseComponent {
|
|
|
3427
3500
|
constructor() {
|
|
3428
3501
|
super(...arguments);
|
|
3429
3502
|
this.displayPoints = computed(() => {
|
|
3503
|
+
const x = this.x();
|
|
3504
|
+
const [min, max] = x.domain();
|
|
3430
3505
|
return this.series().data.filter((point, index, arr) => {
|
|
3431
|
-
const [min, max] = this.x().domain();
|
|
3432
3506
|
return ((point.x >= min || point.x1 >= min || arr[index + 1]?.x >= min || arr[index + 1]?.x1 >= min) &&
|
|
3433
3507
|
(point.x <= max || point.x1 <= max || arr[index - 1]?.x <= max || arr[index - 1]?.x1 <= max));
|
|
3434
3508
|
});
|