@tetacom/svg-charts 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -409,22 +409,10 @@ class ZoomService {
409
409
  }
410
410
  }
411
411
  setBroadcastChannel(channel) {
412
- if (this.broadcastSub) {
413
- this.broadcastSub?.unsubscribe();
414
- }
415
412
  this.broadcastChannel = channel;
416
- if (this.broadcastChannel?.length) {
417
- this.broadcastSub = combineLatest([this._broadcast.subscribeToZoom(this.broadcastChannel), this._chart.config])
418
- .pipe(filter(([zoom, config]) => {
419
- return zoom.message?.chartId !== config.id;
420
- }))
421
- .subscribe(([zoom, config]) => {
422
- this.fireZoom(zoom.message);
423
- });
424
- }
425
413
  }
426
414
  getD3Transform(targetDomain, originalDomain, scale, orientation, inverted) {
427
- const zoomScale = Math.abs(originalDomain[1] - originalDomain[0]) / Math.abs(targetDomain[1] - targetDomain[0]);
415
+ const zoomScale = Math.abs(scale(originalDomain[1]) - scale(originalDomain[0])) / Math.abs(scale(targetDomain[1]) - scale(targetDomain[0]));
428
416
  let transform = zoomIdentity.scale(zoomScale);
429
417
  if (orientation === AxisOrientation.x) {
430
418
  if (!!inverted) {
@@ -444,9 +432,6 @@ class ZoomService {
444
432
  }
445
433
  return transform;
446
434
  }
447
- ngOnDestroy() {
448
- this.broadcastSub?.unsubscribe();
449
- }
450
435
  }
451
436
  ZoomService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: ZoomService, deps: [{ token: BroadcastService }, { token: ChartService }], target: i0.ɵɵFactoryTarget.Injectable });
452
437
  ZoomService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: ZoomService, providedIn: 'root' });
@@ -861,7 +846,7 @@ class LinearSeriesBase extends SeriesBaseComponent {
861
846
  }
862
847
  set series(series) {
863
848
  this.__series = series;
864
- this.markers = this.__series.data?.filter((_) => _?.marker && _?.x !== undefined && _?.y !== undefined);
849
+ this.markers = this.__series.data?.filter((_) => _?.marker && _?.x !== undefined && _?.y !== undefined && _?.x !== null && _?.y !== null);
865
850
  }
866
851
  get series() {
867
852
  return this.__series;
@@ -1588,12 +1573,13 @@ class XAxisComponent {
1588
1573
  constructor(scaleService, _svc) {
1589
1574
  this.scaleService = scaleService;
1590
1575
  this._svc = _svc;
1576
+ this.update$ = new BehaviorSubject(null);
1591
1577
  this._alive = true;
1592
1578
  this.x = this.scaleService.scales.pipe(map((_) => {
1593
1579
  return _.x.get(this.axis.index)?.scale;
1594
1580
  }));
1595
- this.ticks = this.x.pipe(withLatestFrom(this._svc.size), map((_) => {
1596
- const [x, size] = _;
1581
+ this.ticks = combineLatest([this.x, this.update$]).pipe(withLatestFrom(this._svc.size), map((_) => {
1582
+ const [[x], size] = _;
1597
1583
  const tickSize = x.ticks().map((_) => getTextWidth(this.axis.options.tickFormat ? this.axis.options.tickFormat(_) : this.axis.defaultFormatter()(_), 0.45, 11));
1598
1584
  return x.ticks(size.width / parseInt(d3.max(tickSize), 10) / 10);
1599
1585
  }));
@@ -1606,9 +1592,14 @@ class XAxisComponent {
1606
1592
  ngOnDestroy() {
1607
1593
  this._alive = false;
1608
1594
  }
1595
+ ngOnChanges(changes) {
1596
+ if (changes.hasOwnProperty('axis')) {
1597
+ this.update$.next();
1598
+ }
1599
+ }
1609
1600
  }
1610
1601
  XAxisComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: XAxisComponent, deps: [{ token: ScaleService }, { token: ChartService }], target: i0.ɵɵFactoryTarget.Component });
1611
- XAxisComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.2", type: XAxisComponent, selector: "[teta-x-axis]", inputs: { axis: "axis", size: "size" }, ngImport: i0, template: "<ng-container *ngIf=\"{x: x | async, ticks: ticks | async} as data\">\n <svg:g text-anchor=\"middle\" *ngFor=\"let tick of data.ticks\" [attr.transform]=\"'translate('+ data.x(tick) +', 0)'\">\n <text fill=\"var(--color-text-70)\" [attr.dy]=\"axis.options.opposite ? '-0.71em' : '0.71em'\" [attr.y]=\"axis.options.opposite ? 0 : 9\">{{ this.axis.options.tickFormat ? this.axis.options.tickFormat(tick) : this.axis.defaultFormatter()(tick) }}</text>\n <line stroke=\"var(--color-text-30)\" [attr.y2]=\"axis.options.opposite ? -6 : 6\"></line>\n </svg:g>\n\n <svg:g class=\"label-axis font-caption\" [attr.transform]=\"getLabelTransform()\">\n <text fill=\"var(--color-text-70)\" text-anchor=\"middle\" dominant-baseline=\"middle\">{{ axis.options.title }}</text>\n </svg:g>\n</ng-container>\n\n", styles: [":host .tick{stroke:var(--color-text-20)}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1602
+ XAxisComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.2", type: XAxisComponent, selector: "[teta-x-axis]", inputs: { axis: "axis", size: "size" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"{x: x | async, ticks: ticks | async} as data\">\n <svg:g text-anchor=\"middle\" *ngFor=\"let tick of data.ticks\" [attr.transform]=\"'translate('+ data.x(tick) +', 0)'\">\n <text fill=\"var(--color-text-70)\" [attr.dy]=\"axis.options.opposite ? '-0.71em' : '0.71em'\" [attr.y]=\"axis.options.opposite ? 0 : 9\">{{ this.axis.options.tickFormat ? this.axis.options.tickFormat(tick) : this.axis.defaultFormatter()(tick) }}</text>\n <line stroke=\"var(--color-text-30)\" [attr.y2]=\"axis.options.opposite ? -6 : 6\"></line>\n </svg:g>\n\n <svg:g class=\"label-axis font-caption\" [attr.transform]=\"getLabelTransform()\">\n <text fill=\"var(--color-text-70)\" text-anchor=\"middle\" dominant-baseline=\"middle\">{{ axis.options.title }}</text>\n </svg:g>\n</ng-container>\n\n", styles: [":host .tick{stroke:var(--color-text-20)}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1612
1603
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: XAxisComponent, decorators: [{
1613
1604
  type: Component,
1614
1605
  args: [{ selector: '[teta-x-axis]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"{x: x | async, ticks: ticks | async} as data\">\n <svg:g text-anchor=\"middle\" *ngFor=\"let tick of data.ticks\" [attr.transform]=\"'translate('+ data.x(tick) +', 0)'\">\n <text fill=\"var(--color-text-70)\" [attr.dy]=\"axis.options.opposite ? '-0.71em' : '0.71em'\" [attr.y]=\"axis.options.opposite ? 0 : 9\">{{ this.axis.options.tickFormat ? this.axis.options.tickFormat(tick) : this.axis.defaultFormatter()(tick) }}</text>\n <line stroke=\"var(--color-text-30)\" [attr.y2]=\"axis.options.opposite ? -6 : 6\"></line>\n </svg:g>\n\n <svg:g class=\"label-axis font-caption\" [attr.transform]=\"getLabelTransform()\">\n <text fill=\"var(--color-text-70)\" text-anchor=\"middle\" dominant-baseline=\"middle\">{{ axis.options.title }}</text>\n </svg:g>\n</ng-container>\n\n", styles: [":host .tick{stroke:var(--color-text-20)}\n"] }]
@@ -1978,37 +1969,41 @@ class BrushMessage {
1978
1969
  }
1979
1970
 
1980
1971
  class ZoomableDirective {
1981
- constructor(elementRef, zoomService, chartService, zone) {
1972
+ constructor(elementRef, zoomService, chartService, broadcast, zone) {
1982
1973
  this.elementRef = elementRef;
1983
1974
  this.zoomService = zoomService;
1984
1975
  this.chartService = chartService;
1976
+ this.broadcast = broadcast;
1985
1977
  this.zone = zone;
1986
1978
  this.zoomable = false;
1987
1979
  this.crosshair = false;
1988
1980
  this.alive = true;
1989
1981
  this.currentTransform = zoomIdentity;
1990
1982
  this.zoomed = (event) => {
1991
- if (event.sourceEvent) {
1992
- if (Object.keys(event.sourceEvent).length !== 0) {
1993
- const origin = this.axis.scale.copy().domain(this.axis.originDomain);
1994
- let domain = this.axis.orientation === AxisOrientation.y
1995
- ? event.transform.rescaleY(origin).domain()
1996
- : event.transform.rescaleX(origin).domain();
1997
- const message = new ZoomMessage({
1998
- eventType: event.type,
1999
- axis: {
2000
- index: this.axis.index,
2001
- orientation: this.axis.orientation,
2002
- },
2003
- element: this.elementRef,
2004
- domain: domain,
2005
- chartId: this.config.id
2006
- });
2007
- this.zoomService.fireZoom(message);
2008
- this.zoomService.broadcastZoom(message);
2009
- }
2010
- this.currentTransform = event.transform;
1983
+ if (!event.sourceEvent)
1984
+ return;
1985
+ const origin = this.axis.scale.copy().domain(this.axis.originDomain);
1986
+ let domain = this.axis.orientation === AxisOrientation.y
1987
+ ? event.transform.rescaleY(origin).domain()
1988
+ : event.transform.rescaleX(origin).domain();
1989
+ const message = new ZoomMessage({
1990
+ eventType: event.type,
1991
+ axis: {
1992
+ index: this.axis.index,
1993
+ orientation: this.axis.orientation,
1994
+ },
1995
+ element: this.elementRef,
1996
+ domain: domain,
1997
+ chartId: this.config.id
1998
+ });
1999
+ if (event.sourceEvent.type === 'broadcast') {
2000
+ this.zoomService.fireZoom(message);
2001
+ }
2002
+ if (event.sourceEvent.type === 'wheeling' || event.sourceEvent instanceof MouseEvent || (window.TouchEvent && event.sourceEvent instanceof TouchEvent)) {
2003
+ this.zoomService.fireZoom(message);
2004
+ this.zoomService.broadcastZoom(message);
2011
2005
  }
2006
+ this.currentTransform = event.transform;
2012
2007
  };
2013
2008
  }
2014
2009
  ngOnInit() {
@@ -2030,7 +2025,7 @@ class ZoomableDirective {
2030
2025
  this.zoomService.zoomed.pipe(takeWhile(() => this.alive)).subscribe((zoomed) => {
2031
2026
  if (this._element && this.elementRef !== zoomed?.element
2032
2027
  && zoomed?.axis?.index === this.axis.index
2033
- && zoomed?.axis?.orientation === this.axis.orientation) {
2028
+ && zoomed?.axis?.orientation === this.axis.orientation && zoomed.eventType === 'end') {
2034
2029
  const scale = this.axis.scale.copy().domain(this.axis.originDomain);
2035
2030
  let transform;
2036
2031
  if (zoomed.domain === null) {
@@ -2061,31 +2056,54 @@ class ZoomableDirective {
2061
2056
  [0, 0],
2062
2057
  [this.size.width, this.size.height],
2063
2058
  ]);
2064
- if (this.config.zoom?.limitTranslateByData) {
2059
+ const min = this.config?.zoom?.minTranslate
2060
+ ? this.axis.scale(this.config?.zoom?.minTranslate)
2061
+ : 0;
2062
+ const max = this.config?.zoom?.maxTranslate
2063
+ ? this.axis.scale(this.config?.zoom?.maxTranslate)
2064
+ : this.axis.orientation === AxisOrientation.x ? this.size.width : this.size.height;
2065
+ if (this.axis.orientation === AxisOrientation.x && this.config.zoom?.type === ZoomType.x) {
2066
+ this.zoom.translateExtent([
2067
+ [min, 0],
2068
+ [max, this.size.height],
2069
+ ]);
2070
+ }
2071
+ if (this.axis.orientation === AxisOrientation.y && this.config.zoom?.type === ZoomType.y) {
2065
2072
  this.zoom.translateExtent([
2066
- [0, 0],
2067
- [this.size.width, this.size.height],
2073
+ [0, min],
2074
+ [this.size.width, max],
2068
2075
  ]);
2069
2076
  }
2070
2077
  if (this.config.zoom?.wheelDelta) {
2071
2078
  this.zoom.wheelDelta(this.config.zoom?.wheelDelta);
2072
2079
  }
2073
2080
  const maxZoom = this.config.zoom?.max
2074
- ? (this.axis.extremes[1] - this.axis.extremes[0]) /
2075
- this.config.zoom?.max
2076
- : this.config.zoom?.limitZoomByData
2077
- ? 1
2078
- : 0;
2081
+ ? (this.axis.extremes[1] - this.axis.extremes[0]) / this.config.zoom?.max : this.config.zoom?.limitZoomByData ? 1 : 0;
2082
+ if (this.config.zoom?.wheelDelta) {
2083
+ this.zoom.wheelDelta(this.config.zoom?.wheelDelta);
2084
+ }
2079
2085
  const minZoom = this.config.zoom?.min
2080
2086
  ? (this.axis.extremes[1] - this.axis.extremes[0]) /
2081
2087
  this.config.zoom?.min
2082
2088
  : Infinity;
2083
2089
  this.zoom.scaleExtent([maxZoom, minZoom]);
2084
2090
  this.zoom.on('zoom end', this.zoomed);
2085
- this._element.call(this.zoom).on('dblclick.zoom', null); // Disable dbclick zoom
2091
+ this._element.call(this.zoom).on('dblclick.zoom', null);
2086
2092
  if (this.config?.zoom?.zoomBehavior === ZoomBehaviorType.wheel) {
2087
2093
  this.runWheelTranslate();
2088
2094
  }
2095
+ this.broadcast.subscribeToZoom(this.config?.zoom.syncChannel).subscribe((zoom) => {
2096
+ if (zoom.message.chartId !== this.config?.id && zoom.message.axis.orientation === this.axis.orientation && zoom.message.axis.index === this.axis.index) {
2097
+ const scale = this.axis.scale.copy().domain(this.axis.originDomain);
2098
+ const transform = this.zoomService.getD3Transform(zoom.message.domain, this.axis.originDomain, scale, this.axis.orientation, this.axis.options.inverted);
2099
+ if (zoom.message.style?.transition) {
2100
+ this._element.transition().duration(300).call(this.zoom.transform, transform, null, { type: 'broadcast' });
2101
+ }
2102
+ else {
2103
+ this._element.call(this.zoom.transform, transform, null, { type: 'broadcast' });
2104
+ }
2105
+ }
2106
+ });
2089
2107
  }
2090
2108
  runWheelTranslate() {
2091
2109
  let type = 'start';
@@ -2102,7 +2120,6 @@ class ZoomableDirective {
2102
2120
  return delta * 0.002;
2103
2121
  });
2104
2122
  const emit = (type, event) => {
2105
- const origin = this.axis.scale.copy().domain(this.axis.originDomain);
2106
2123
  let transform = zoomIdentity;
2107
2124
  const delta = type === 'end'
2108
2125
  ? 0
@@ -2116,22 +2133,7 @@ class ZoomableDirective {
2116
2133
  transform = transform.translate(this.currentTransform.x - delta / 2, 0);
2117
2134
  }
2118
2135
  transform = transform.scale(this.currentTransform.k);
2119
- let domain = this.axis.orientation === AxisOrientation.y
2120
- ? transform.rescaleY(origin).domain()
2121
- : transform.rescaleX(origin).domain();
2122
- const message = new ZoomMessage({
2123
- eventType: type,
2124
- element: this.elementRef,
2125
- axis: {
2126
- index: this.axis.index,
2127
- orientation: this.axis.orientation
2128
- },
2129
- domain,
2130
- chartId: this.config.id,
2131
- });
2132
- this._element?.call(this.zoom.transform, transform);
2133
- this.zoomService.fireZoom(message);
2134
- this.zoomService.broadcastZoom(message);
2136
+ this._element?.call(this.zoom.transform, transform, null, { type: 'wheeling' });
2135
2137
  this.currentTransform = transform;
2136
2138
  };
2137
2139
  this._element.on('wheel', (event) => {
@@ -2151,14 +2153,14 @@ class ZoomableDirective {
2151
2153
  });
2152
2154
  }
2153
2155
  }
2154
- ZoomableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: ZoomableDirective, deps: [{ token: i0.ElementRef }, { token: ZoomService }, { token: ChartService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
2156
+ ZoomableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: ZoomableDirective, deps: [{ token: i0.ElementRef }, { token: ZoomService }, { token: ChartService }, { token: BroadcastService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
2155
2157
  ZoomableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.2", type: ZoomableDirective, selector: "[tetaZoomable]", inputs: { config: "config", axis: "axis", size: "size" }, host: { properties: { "class.zoomable": "this.zoomable", "class.crosshair": "this.crosshair" } }, ngImport: i0 });
2156
2158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.2", ngImport: i0, type: ZoomableDirective, decorators: [{
2157
2159
  type: Directive,
2158
2160
  args: [{
2159
2161
  selector: '[tetaZoomable]',
2160
2162
  }]
2161
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: ZoomService }, { type: ChartService }, { type: i0.NgZone }]; }, propDecorators: { config: [{
2163
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: ZoomService }, { type: ChartService }, { type: BroadcastService }, { type: i0.NgZone }]; }, propDecorators: { config: [{
2162
2164
  type: Input
2163
2165
  }], axis: [{
2164
2166
  type: Input
@@ -2196,6 +2198,13 @@ class BrushableDirective {
2196
2198
  this._alive = false;
2197
2199
  }
2198
2200
  ngAfterViewInit() {
2201
+ if (this.config?.brush?.enable) {
2202
+ const brushMessage = new BrushMessage({
2203
+ chartId: this.config.id,
2204
+ selection: [this.config.brush?.from, this.config.brush?.to],
2205
+ });
2206
+ this.brushService.setBrush(brushMessage);
2207
+ }
2199
2208
  }
2200
2209
  ngOnChanges(changes) {
2201
2210
  if (changes.hasOwnProperty('config')) {
@@ -2286,7 +2295,7 @@ class BrushableDirective {
2286
2295
  }
2287
2296
  this._container.call(this.brush.move, this.selection
2288
2297
  ? this.selection.map(brushScale)
2289
- : domain.map(brushScale), {});
2298
+ : domain.map(brushScale));
2290
2299
  }, 0);
2291
2300
  });
2292
2301
  }
@@ -2388,8 +2397,6 @@ class CrosshairComponent {
2388
2397
  }
2389
2398
  ngOnInit() {
2390
2399
  this.transform = this.chartService.pointerMove.pipe(map((event) => {
2391
- const composedPath = event.composedPath();
2392
- const classes = composedPath.map((_) => _.classList?.contains('crosshair')).filter((_) => _);
2393
2400
  return {
2394
2401
  x: event.type === 'mouseleave' ? -9999 : event.offsetX,
2395
2402
  y: event.type === 'mouseleave' ? -9999 : event.offsetY