From e2fe205fcec721b0a2c125c661615b2104941075 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Wed, 1 Apr 2020 20:57:52 +0100 Subject: [PATCH 01/21] Set timeline (internal) view size based on its element size --- .../src/lib/ngx-d3timeline.component.ts | 14 +++++++------ .../src/lib/ngx-d3timeline.service.ts | 20 ++++++++++++++++++- src/app/app.component.html | 19 +++++++++--------- src/app/app.component.scss | 9 +++++++++ src/app/app.component.ts | 6 +++--- 5 files changed, 49 insertions(+), 19 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index bcbdcb82..f5ff7f67 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -8,7 +8,8 @@ import { ChangeDetectionStrategy, OnInit, Output, - EventEmitter + EventEmitter, + ChangeDetectorRef } from '@angular/core'; import { Activity } from './activity/activity'; @@ -55,10 +56,6 @@ export class NgxD3timelineComponent implements OnInit, AfterViewInit { this.timeline.setActivities(value); } - @Input() set view([width, height]: [number, number]) { - this.timeline.setView([width, height]); - } - @Input() set options(options: Options) { this.timeline.setOptions(options); } @@ -69,10 +66,15 @@ export class NgxD3timelineComponent implements OnInit, AfterViewInit { @ViewChild('svgEl') svgEl: ElementRef; - constructor(public timeline: NgxD3TimelineService) {} + constructor( + public timeline: NgxD3TimelineService, + private hostElement: ElementRef, + private changeDetector: ChangeDetectorRef + ) {} ngOnInit(): void { this.initEventEmitters(); + this.timeline.setupResizing(this.hostElement, this.changeDetector); } ngAfterViewInit(): void { diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 05c61306..8711e53b 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -1,4 +1,10 @@ -import { Injectable, ElementRef, EventEmitter, OnDestroy } from '@angular/core'; +import { + Injectable, + ElementRef, + EventEmitter, + OnDestroy, + ChangeDetectorRef +} from '@angular/core'; import { Store } from './store-lib/store'; import { selectView } from './store/state'; import { @@ -19,6 +25,8 @@ import { selectResourceShowRectangles } from './options/selectors/resource-optio import { Subject } from 'rxjs'; import { outputOnObservableEmit } from './core/observable-utils'; +declare var ResizeObserver: any; // typings not yet available in Typescript + @Injectable() export class NgxD3TimelineService implements OnDestroy { view$ = this.store.select(selectView); @@ -92,4 +100,14 @@ export class NgxD3TimelineService implements OnDestroy { private zoomed() { this.store.dispatch(new fromActions.ZoomedAction(event)); } + + setupResizing(hostElement: ElementRef, changeDetector: ChangeDetectorRef) { + const observer = new ResizeObserver(entries => { + entries.forEach(entry => { + this.setView([entry.contentRect.width, entry.contentRect.height]); + changeDetector.detectChanges(); // not sure why change detection does not ordinarily pick this up + }); + }); + observer.observe(hostElement.nativeElement); + } } diff --git a/src/app/app.component.html b/src/app/app.component.html index f40cf6a5..c373d951 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,7 +1,8 @@ + type="checkbox" + [checked]="options.resource.showRectangles" + (change)=" + update('resource', options.resource, { + showRectangles: !options.resource.showRectangles + }) + " + /> diff --git a/src/app/app.component.scss b/src/app/app.component.scss index cd5d7c30..79eea905 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,3 +1,12 @@ +html, +body { + height: 100%; +} + +ngx-d3timeline { + display: block; +} + .ngx-d3timeline { .activity-rectangle { rect { diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9b95916c..509808d6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,7 +9,7 @@ import { scan, tap } from 'rxjs/operators'; selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], - encapsulation: ViewEncapsulation.ShadowDom + encapsulation: ViewEncapsulation.None }) export class AppComponent { activities = deliveryData; @@ -57,8 +57,8 @@ export class AppComponent { tap(console.log) ); - width = 800; - height = 600; + width = '800px'; + height = '600px'; onDropped(activity: Activity) { console.log(activity); From e8d3a03e06aa6affae179d5be819953f7fe887d3 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Wed, 1 Apr 2020 22:53:01 +0100 Subject: [PATCH 02/21] prevent rendering when view has not been initialised --- .../src/lib/ngx-d3timeline.component.ts | 52 ++++++++++--------- projects/ngx-d3timeline/src/lib/view/view.ts | 14 +++-- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index f5ff7f67..f971b53d 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -19,32 +19,36 @@ import { Options } from './options/options'; @Component({ selector: 'ngx-d3timeline', template: ` - - - + + - - - - + + + + + + + `, styleUrls: ['./ngx-d3timeline.component.scss'], encapsulation: ViewEncapsulation.None, diff --git a/projects/ngx-d3timeline/src/lib/view/view.ts b/projects/ngx-d3timeline/src/lib/view/view.ts index 5fdf3c70..b04b0aa0 100644 --- a/projects/ngx-d3timeline/src/lib/view/view.ts +++ b/projects/ngx-d3timeline/src/lib/view/view.ts @@ -4,8 +4,8 @@ export class View { readonly width: number; readonly height: number; - readonly left = View.margin; - readonly top = View.margin; + readonly left: number; + readonly top: number; readonly right: number; readonly bottom: number; @@ -13,7 +13,13 @@ export class View { this.width = width; this.height = height; - this.bottom = height - View.margin; - this.right = width - View.margin; + this.left = Math.min(View.margin, width); + this.top = Math.min(View.margin, height); + this.bottom = Math.max(height - View.margin, 0); + this.right = Math.max(width - View.margin, 0); + } + + get isEmpty(): boolean { + return !(this.width && this.height); } } From 78e60d680e88f6d4e531298cf8c36462d4589ce1 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Wed, 1 Apr 2020 22:53:14 +0100 Subject: [PATCH 03/21] remove console logs --- src/app/app.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 509808d6..a75fd042 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -53,8 +53,7 @@ export class AppComponent { }; options$ = this.optionUpdateSubject.pipe( - scan(this.updateOptions, this.initialOptions), - tap(console.log) + scan(this.updateOptions, this.initialOptions) ); width = '800px'; @@ -87,7 +86,6 @@ export class AppComponent { } private updateOptions(options: Options, value: { [key: string]: any }) { - console.log(value); return { ...{ ...options, ...value } }; } } From 0437697db24d5e06b573d43f13d5afea8229608d Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Wed, 1 Apr 2020 23:28:25 +0100 Subject: [PATCH 04/21] handle resizing more comprehensively --- .../src/lib/ngx-d3timeline.service.ts | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 8711e53b..4ee7defc 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -16,16 +16,23 @@ import { Options } from './options/options'; import { zoom } from 'd3-zoom'; import { select, event } from 'd3-selection'; import { AxisService } from './axis/axis.service'; -import { map, filter, distinctUntilChanged } from 'rxjs/operators'; +import { + map, + filter, + distinctUntilChanged, + debounceTime, + takeUntil +} from 'rxjs/operators'; import { selectLastDraggedActivity } from './activity/activity.selectors'; import { selectHoveredActivity } from './hover/hover.selectors'; import { HoverAction } from './hover/hover-event'; import { selectResourceRectangles } from './resource-rectangle/resource-rectangle.selectors'; import { selectResourceShowRectangles } from './options/selectors/resource-options.selectors'; -import { Subject } from 'rxjs'; +import { Subject, BehaviorSubject } from 'rxjs'; import { outputOnObservableEmit } from './core/observable-utils'; declare var ResizeObserver: any; // typings not yet available in Typescript +declare type ResizeObserver = any; @Injectable() export class NgxD3TimelineService implements OnDestroy { @@ -33,6 +40,8 @@ export class NgxD3TimelineService implements OnDestroy { resourceAxis$ = this.axisService.resourceAxis$; timeAxis$ = this.axisService.timeAxis$; showRectangles$ = this.store.select(selectResourceShowRectangles); + resizeObserver: ResizeObserver; + resizes$ = new BehaviorSubject<[number, number]>([0, 0]); activityDropped$ = this.store.select(selectLastDraggedActivity).pipe( filter(activity => !!activity), @@ -55,6 +64,7 @@ export class NgxD3TimelineService implements OnDestroy { constructor(private store: Store, private axisService: AxisService) {} ngOnDestroy(): void { + this.resizeObserver.disconnect(); this.destroySubject.next(true); } @@ -102,12 +112,20 @@ export class NgxD3TimelineService implements OnDestroy { } setupResizing(hostElement: ElementRef, changeDetector: ChangeDetectorRef) { - const observer = new ResizeObserver(entries => { - entries.forEach(entry => { - this.setView([entry.contentRect.width, entry.contentRect.height]); - changeDetector.detectChanges(); // not sure why change detection does not ordinarily pick this up + this.resizes$ + .pipe(takeUntil(this.destroySubject), debounceTime(100)) + .subscribe(view => { + this.setView(view); + + // possibly necessary due to current lack of monkey patching for ResizeObserver + // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 + changeDetector.detectChanges(); }); + + this.resizeObserver = new ResizeObserver(entries => { + const entry = entries[0]; + this.resizes$.next([entry.contentRect.width, entry.contentRect.height]); }); - observer.observe(hostElement.nativeElement); + this.resizeObserver.observe(hostElement.nativeElement); } } From 7cd1ac8b1857249f6f9fb2a92b087b3ef557ac3e Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Thu, 2 Apr 2020 10:48:26 +0100 Subject: [PATCH 05/21] refactor streaming of resize events --- .../src/lib/core/resize-observable.ts | 26 +++++++++++++++++++ .../src/lib/ngx-d3timeline.service.ts | 19 ++++---------- 2 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 projects/ngx-d3timeline/src/lib/core/resize-observable.ts diff --git a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts new file mode 100644 index 00000000..190ac841 --- /dev/null +++ b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts @@ -0,0 +1,26 @@ +import { ElementRef } from '@angular/core'; +import { fromEventPattern } from 'rxjs'; +import { NodeEventHandler } from 'rxjs/internal/observable/fromEvent'; + +declare var ResizeObserver: any; // typings not yet available in Typescript +declare type ResizeObserver = any; + +export function createResizeObservable(hostElement: ElementRef) { + return fromEventPattern<[number, number]>( + handler => addResizeHandler(handler, hostElement), + (handler, token) => removeResizeHandler(handler, token) + ); +} + +function addResizeHandler(handler: NodeEventHandler, hostElement: ElementRef) { + const resizeObserver = new ResizeObserver(entries => { + const entry = entries[0]; + handler([entry.contentRect.width, entry.contentRect.height]); + }); + resizeObserver.observe(hostElement.nativeElement); + return resizeObserver; +} + +function removeResizeHandler(_: NodeEventHandler, token: ResizeObserver) { + token.disconnect(); +} diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 089e626f..2eb86fe9 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -29,19 +29,15 @@ import { selectResourceRectangles, selectResourceTickRectangles } from './resource-rectangle/selectors/resource-rectangle.selectors'; -import { Subject, BehaviorSubject } from 'rxjs'; +import { Subject } from 'rxjs'; import { identifier } from './core/types'; - -declare var ResizeObserver: any; // typings not yet available in Typescript -declare type ResizeObserver = any; +import { createResizeObservable } from './core/resize-observable'; @Injectable() export class NgxD3TimelineService implements OnDestroy { view$ = this.store.select(selectView); resourceAxis$ = this.axisService.resourceAxis$; timeAxis$ = this.axisService.timeAxis$; - resizeObserver: ResizeObserver; - resizes$ = new BehaviorSubject<[number, number]>([0, 0]); activityDropped$ = this.store.select(selectLastDraggedActivity).pipe( filter(activity => !!activity), @@ -57,7 +53,6 @@ export class NgxD3TimelineService implements OnDestroy { constructor(private store: Store, private axisService: AxisService) {} ngOnDestroy(): void { - this.resizeObserver.disconnect(); this.destroySubject.next(true); } @@ -99,7 +94,9 @@ export class NgxD3TimelineService implements OnDestroy { } setupResizing(hostElement: ElementRef, changeDetector: ChangeDetectorRef) { - this.resizes$ + const resizes$ = createResizeObservable(hostElement); + + resizes$ .pipe(takeUntil(this.destroySubject), debounceTime(100)) .subscribe(view => { this.setView(view); @@ -108,11 +105,5 @@ export class NgxD3TimelineService implements OnDestroy { // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 changeDetector.detectChanges(); }); - - this.resizeObserver = new ResizeObserver(entries => { - const entry = entries[0]; - this.resizes$.next([entry.contentRect.width, entry.contentRect.height]); - }); - this.resizeObserver.observe(hostElement.nativeElement); } } From ec9e8c6a5d3a8c74374de45d950312a330b3c662 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Thu, 2 Apr 2020 10:52:10 +0100 Subject: [PATCH 06/21] change paramater type for createResizeObservable --- .../ngx-d3timeline/src/lib/core/resize-observable.ts | 9 ++++----- .../ngx-d3timeline/src/lib/ngx-d3timeline.service.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts index 190ac841..02c28917 100644 --- a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts +++ b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts @@ -1,23 +1,22 @@ -import { ElementRef } from '@angular/core'; import { fromEventPattern } from 'rxjs'; import { NodeEventHandler } from 'rxjs/internal/observable/fromEvent'; declare var ResizeObserver: any; // typings not yet available in Typescript declare type ResizeObserver = any; -export function createResizeObservable(hostElement: ElementRef) { +export function createResizeObservable(element: Element) { return fromEventPattern<[number, number]>( - handler => addResizeHandler(handler, hostElement), + handler => addResizeHandler(handler, element), (handler, token) => removeResizeHandler(handler, token) ); } -function addResizeHandler(handler: NodeEventHandler, hostElement: ElementRef) { +function addResizeHandler(handler: NodeEventHandler, element: Element) { const resizeObserver = new ResizeObserver(entries => { const entry = entries[0]; handler([entry.contentRect.width, entry.contentRect.height]); }); - resizeObserver.observe(hostElement.nativeElement); + resizeObserver.observe(element); return resizeObserver; } diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 2eb86fe9..60242700 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -94,7 +94,7 @@ export class NgxD3TimelineService implements OnDestroy { } setupResizing(hostElement: ElementRef, changeDetector: ChangeDetectorRef) { - const resizes$ = createResizeObservable(hostElement); + const resizes$ = createResizeObservable(hostElement.nativeElement); resizes$ .pipe(takeUntil(this.destroySubject), debounceTime(100)) From 8f11f4f40aafccdb9653d5f6aab1f6431ba34b35 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Thu, 2 Apr 2020 11:43:14 +0100 Subject: [PATCH 07/21] update view not null snapshot (white space only) --- .../lib/__snapshots__/ngx-d3timeline.component.spec.ts.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/ngx-d3timeline/src/lib/__snapshots__/ngx-d3timeline.component.spec.ts.snap b/projects/ngx-d3timeline/src/lib/__snapshots__/ngx-d3timeline.component.spec.ts.snap index 64720c41..bfeb8936 100644 --- a/projects/ngx-d3timeline/src/lib/__snapshots__/ngx-d3timeline.component.spec.ts.snap +++ b/projects/ngx-d3timeline/src/lib/__snapshots__/ngx-d3timeline.component.spec.ts.snap @@ -13,6 +13,8 @@ exports[`NgxD3timelineComponent view not null should render correctly 1`] = ` id="root1" > + + + `; From 68379f268e6ea534e4e079ffe25db46f96490d60 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sat, 4 Apr 2020 21:22:34 +0100 Subject: [PATCH 08/21] re-enable zoom and pan behaviour --- .../src/lib/ngx-d3timeline.component.ts | 17 ++++++++--------- .../src/lib/ngx-d3timeline.service.ts | 13 ++++++------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index b51be7d0..bbfadb57 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -9,7 +9,7 @@ import { OnInit, Output, EventEmitter, - ChangeDetectorRef + NgZone } from '@angular/core'; import { Activity } from './activity/activity'; @@ -75,7 +75,7 @@ import { ResourceRectangle } from './resource-rectangle/resource-rectangle'; changeDetection: ChangeDetectionStrategy.OnPush, providers: [NgxD3TimelineService] }) -export class NgxD3timelineComponent implements OnInit, AfterViewInit { +export class NgxD3timelineComponent implements OnInit { @Input() set activities(value: Activity[]) { this.timeline.setActivities(value); } @@ -100,21 +100,20 @@ export class NgxD3timelineComponent implements OnInit, AfterViewInit { @Output() activityUnhovered = new EventEmitter(); @Output() activitySelected = new EventEmitter(); - @ViewChild('svgEl') svgEl: ElementRef; + @ViewChild('svgEl') set svgElement(el: ElementRef) { + // TODO remove zoom events from old SVG? + this.timeline.setupZoom(el); + } constructor( public timeline: NgxD3TimelineService, private hostElement: ElementRef, - private changeDetector: ChangeDetectorRef + private zone: NgZone ) {} ngOnInit(): void { this.timeline.onActivityDropped(this.activityDropped); - this.timeline.setupResizing(this.hostElement, this.changeDetector); - } - - ngAfterViewInit(): void { - this.timeline.setupZoom(this.svgEl); + this.timeline.setupResizing(this.hostElement, this.zone); } trackByFn(resourceRectangle: ResourceRectangle) { diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index d1ef8c0c..29b1bcf3 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -3,7 +3,7 @@ import { ElementRef, EventEmitter, OnDestroy, - ChangeDetectorRef + NgZone } from '@angular/core'; import { Store } from './store-lib/store'; import { selectView } from './store/state'; @@ -49,7 +49,6 @@ export class NgxD3TimelineService implements OnDestroy { resourceTickMarkRectangles$ = this.store.select(selectResourceTickRectangles); private destroySubject = new Subject(); - constructor(private store: Store, private axisService: AxisService) {} ngOnDestroy(): void { @@ -93,17 +92,17 @@ export class NgxD3TimelineService implements OnDestroy { this.store.dispatch(new fromActions.ZoomedAction(event)); } - setupResizing(hostElement: ElementRef, changeDetector: ChangeDetectorRef) { + setupResizing(hostElement: ElementRef, zone: NgZone) { const resizes$ = createResizeObservable(hostElement.nativeElement); resizes$ .pipe(takeUntil(this.destroySubject), debounceTime(100)) .subscribe(view => { - this.setView(view); - - // possibly necessary due to current lack of monkey patching for ResizeObserver + // zone.run is necessary due to current lack of monkey patching for ResizeObserver // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 - changeDetector.detectChanges(); + zone.run(() => { + this.setView(view); + }); }); } } From e1cff72f3bd638595f01f1179c448c24dc6c58c9 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sat, 4 Apr 2020 22:46:29 +0100 Subject: [PATCH 09/21] remove unused imports --- projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts | 1 - src/app/app.component.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index bbfadb57..890fdf3c 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -1,7 +1,6 @@ import { Component, Input, - AfterViewInit, ViewChild, ElementRef, ViewEncapsulation, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index db50ea0e..83ff57f5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -3,7 +3,7 @@ import { deliveryData } from './data'; import { Activity } from 'ngx-d3timeline'; import { Options } from 'ngx-d3timeline'; import { BehaviorSubject } from 'rxjs'; -import { scan, tap } from 'rxjs/operators'; +import { scan } from 'rxjs/operators'; @Component({ selector: 'app-root', From 25f816436fbdb222374f519c81eb22bc11f3464f Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sat, 4 Apr 2020 23:05:17 +0100 Subject: [PATCH 10/21] restore .demo-app selector so that resource header backgrounds appear black --- src/app/app.component.scss | 131 +++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/src/app/app.component.scss b/src/app/app.component.scss index dec42273..86aab660 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -6,93 +6,94 @@ body { ngx-d3timeline { display: block; } - -.ngx-d3timeline { - .activity-rectangle { - rect { - rx: 5px; - } - - &.Driving { +.demo-app { + .ngx-d3timeline { + .activity-rectangle { rect { - rx: 0; - fill: #dadee0; - stroke: #4e585e; + rx: 5px; } - .activity-content-div { - text-align: right; - } - } + &.Driving { + rect { + rx: 0; + fill: #dadee0; + stroke: #4e585e; + } - &.Waiting rect { - fill: #bcd1d7; - stroke: #76858d; - } + .activity-content-div { + text-align: right; + } + } - &.DriveBreak rect { - fill: #bcd1d7; - stroke: #76858d; - } + &.Waiting rect { + fill: #bcd1d7; + stroke: #76858d; + } - &.Handling rect { - fill: #00a1c5; - stroke: #006176; - } + &.DriveBreak rect { + fill: #bcd1d7; + stroke: #76858d; + } - &.Resting rect { - fill: #bcd1d7; - stroke: #76858d; - } + &.Handling rect { + fill: #00a1c5; + stroke: #006176; + } - &.hovered rect { - fill: orange; - } + &.Resting rect { + fill: #bcd1d7; + stroke: #76858d; + } - &.selected { - rect { - fill: blue; + &.hovered rect { + fill: orange; } - .activity-content-div { - color: #fff; + &.selected { + rect { + fill: blue; + } + + .activity-content-div { + color: #fff; + } } } - } - .from-activity-rectangle .activity-rectangle { - fill: none; - rect { - stroke: #f00; - stroke-width: 3px; + .from-activity-rectangle .activity-rectangle { fill: none; + rect { + stroke: #f00; + stroke-width: 3px; + fill: none; + } } - } - .drop-activity-rectangle .activity-rectangle { - fill: none; - rect { - stroke: #0f0; - stroke-width: 3px; + .drop-activity-rectangle .activity-rectangle { fill: none; + rect { + stroke: #0f0; + stroke-width: 3px; + fill: none; + } } - } - .resources-axis .tick-mark-text { - fill: white; - } + .resources-axis .tick-mark-text { + fill: white; + } - .resource-title-background rect { - fill: #000; - } + .resource-title-background rect { + fill: #000; + } - rect.hovered { - fill: orange; - fill-opacity: 0.3; - } + rect.hovered { + fill: orange; + fill-opacity: 0.3; + } - rect.selected { - fill: blue; - fill-opacity: 0.3; + rect.selected { + fill: blue; + fill-opacity: 0.3; + } } } From 816058ba480ea76021867663479699fb463f8fe5 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sat, 4 Apr 2020 23:28:03 +0100 Subject: [PATCH 11/21] remove use of internal rxjs type --- projects/ngx-d3timeline/src/lib/core/resize-observable.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts index 02c28917..c722c240 100644 --- a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts +++ b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts @@ -1,5 +1,4 @@ import { fromEventPattern } from 'rxjs'; -import { NodeEventHandler } from 'rxjs/internal/observable/fromEvent'; declare var ResizeObserver: any; // typings not yet available in Typescript declare type ResizeObserver = any; @@ -11,7 +10,10 @@ export function createResizeObservable(element: Element) { ); } -function addResizeHandler(handler: NodeEventHandler, element: Element) { +function addResizeHandler( + handler: ([width, height]: [number, number]) => void, + element: Element +) { const resizeObserver = new ResizeObserver(entries => { const entry = entries[0]; handler([entry.contentRect.width, entry.contentRect.height]); @@ -20,6 +22,6 @@ function addResizeHandler(handler: NodeEventHandler, element: Element) { return resizeObserver; } -function removeResizeHandler(_: NodeEventHandler, token: ResizeObserver) { +function removeResizeHandler(_: any, token: ResizeObserver) { token.disconnect(); } From 14a290214f8cf76a316916002446d7a6cbb20524 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sat, 4 Apr 2020 23:52:16 +0100 Subject: [PATCH 12/21] move hostElementRef and NgZone to service --- .../ngx-d3timeline/src/lib/ngx-d3timeline.component.ts | 7 +------ .../ngx-d3timeline/src/lib/ngx-d3timeline.service.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index 890fdf3c..56683d4e 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -104,15 +104,10 @@ export class NgxD3timelineComponent implements OnInit { this.timeline.setupZoom(el); } - constructor( - public timeline: NgxD3TimelineService, - private hostElement: ElementRef, - private zone: NgZone - ) {} + constructor(public timeline: NgxD3TimelineService) {} ngOnInit(): void { this.timeline.onActivityDropped(this.activityDropped); - this.timeline.setupResizing(this.hostElement, this.zone); } trackByFn(resourceRectangle: ResourceRectangle) { diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 29b1bcf3..08be8fad 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -49,7 +49,14 @@ export class NgxD3TimelineService implements OnDestroy { resourceTickMarkRectangles$ = this.store.select(selectResourceTickRectangles); private destroySubject = new Subject(); - constructor(private store: Store, private axisService: AxisService) {} + constructor( + private store: Store, + private axisService: AxisService, + hostElement: ElementRef, + zone: NgZone + ) { + this.setupResizing(hostElement, zone); + } ngOnDestroy(): void { this.destroySubject.next(true); From 3879228ef0ec5b17045e926e535d28bb41de8ff7 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 00:04:15 +0100 Subject: [PATCH 13/21] refactor of setupResizing --- .../src/lib/ngx-d3timeline.service.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 08be8fad..75bcc46b 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -100,16 +100,16 @@ export class NgxD3TimelineService implements OnDestroy { } setupResizing(hostElement: ElementRef, zone: NgZone) { - const resizes$ = createResizeObservable(hostElement.nativeElement); - - resizes$ + createResizeObservable(hostElement.nativeElement) .pipe(takeUntil(this.destroySubject), debounceTime(100)) - .subscribe(view => { - // zone.run is necessary due to current lack of monkey patching for ResizeObserver - // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 - zone.run(() => { - this.setView(view); - }); - }); + .subscribe(dimensions => this.updateView(dimensions, zone)); + } + + updateView(dimensions: [number, number], zone: NgZone) { + // zone.run is necessary due to current lack of monkey patching for ResizeObserver + // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 + zone.run(() => { + this.setView(dimensions); + }); } } From a47df1c59bc0589720587fcd48ca87d76f7d8f1c Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 15:51:36 +0100 Subject: [PATCH 14/21] refactor createResizeObservable to use nested functions --- .../src/lib/core/resize-observable.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts index c722c240..f8c0e595 100644 --- a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts +++ b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts @@ -4,24 +4,24 @@ declare var ResizeObserver: any; // typings not yet available in Typescript declare type ResizeObserver = any; export function createResizeObservable(element: Element) { + let resizeObserver: ResizeObserver; + return fromEventPattern<[number, number]>( - handler => addResizeHandler(handler, element), - (handler, token) => removeResizeHandler(handler, token) + handler => addResizeHandler(handler), + handler => removeResizeHandler(handler) ); -} -function addResizeHandler( - handler: ([width, height]: [number, number]) => void, - element: Element -) { - const resizeObserver = new ResizeObserver(entries => { - const entry = entries[0]; - handler([entry.contentRect.width, entry.contentRect.height]); - }); - resizeObserver.observe(element); - return resizeObserver; -} + function addResizeHandler( + handler: ([width, height]: [number, number]) => void + ) { + resizeObserver = new ResizeObserver(entries => { + const entry = entries[0]; + handler([entry.contentRect.width, entry.contentRect.height]); + }); + resizeObserver.observe(element); + } -function removeResizeHandler(_: any, token: ResizeObserver) { - token.disconnect(); + function removeResizeHandler(_: any) { + resizeObserver.unobserve(element); + } } From 0aebd0930d511d6df16d95ea54f9a369fe95dfdf Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 16:06:37 +0100 Subject: [PATCH 15/21] remove TODO comment --- projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index 56683d4e..843c2575 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -100,7 +100,6 @@ export class NgxD3timelineComponent implements OnInit { @Output() activitySelected = new EventEmitter(); @ViewChild('svgEl') set svgElement(el: ElementRef) { - // TODO remove zoom events from old SVG? this.timeline.setupZoom(el); } From 7565ee8556dbe834d24fdb59c02ea39218887f44 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 16:13:15 +0100 Subject: [PATCH 16/21] call setupResizing from OnInit instead of service constructor --- .../src/lib/ngx-d3timeline.component.ts | 1 + .../src/lib/ngx-d3timeline.service.ts | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts index 843c2575..f0c57472 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts @@ -107,6 +107,7 @@ export class NgxD3timelineComponent implements OnInit { ngOnInit(): void { this.timeline.onActivityDropped(this.activityDropped); + this.timeline.setupResizing(); } trackByFn(resourceRectangle: ResourceRectangle) { diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 75bcc46b..1989f7a3 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -52,11 +52,9 @@ export class NgxD3TimelineService implements OnDestroy { constructor( private store: Store, private axisService: AxisService, - hostElement: ElementRef, - zone: NgZone - ) { - this.setupResizing(hostElement, zone); - } + private hostElement: ElementRef, + private zone: NgZone + ) {} ngOnDestroy(): void { this.destroySubject.next(true); @@ -99,10 +97,10 @@ export class NgxD3TimelineService implements OnDestroy { this.store.dispatch(new fromActions.ZoomedAction(event)); } - setupResizing(hostElement: ElementRef, zone: NgZone) { - createResizeObservable(hostElement.nativeElement) + setupResizing() { + createResizeObservable(this.hostElement.nativeElement) .pipe(takeUntil(this.destroySubject), debounceTime(100)) - .subscribe(dimensions => this.updateView(dimensions, zone)); + .subscribe(dimensions => this.updateView(dimensions, this.zone)); } updateView(dimensions: [number, number], zone: NgZone) { From ad6b7f5edfd61acb5a55e3e3b28a39859f3142ae Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 16:16:29 +0100 Subject: [PATCH 17/21] make updateView private --- projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index 1989f7a3..bbb866fb 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -103,7 +103,7 @@ export class NgxD3TimelineService implements OnDestroy { .subscribe(dimensions => this.updateView(dimensions, this.zone)); } - updateView(dimensions: [number, number], zone: NgZone) { + private updateView(dimensions: [number, number], zone: NgZone) { // zone.run is necessary due to current lack of monkey patching for ResizeObserver // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5 zone.run(() => { From 759bd2302423c2c0990f76437686c5b63d356d9f Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 16:24:52 +0100 Subject: [PATCH 18/21] change order of takeUntil in setupResizing --- projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index bbb866fb..a3443106 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -99,7 +99,7 @@ export class NgxD3TimelineService implements OnDestroy { setupResizing() { createResizeObservable(this.hostElement.nativeElement) - .pipe(takeUntil(this.destroySubject), debounceTime(100)) + .pipe(debounceTime(100), takeUntil(this.destroySubject)) .subscribe(dimensions => this.updateView(dimensions, this.zone)); } From 3a7a42d63063ecd7be2ca9f69032be0e0ee02ce5 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 16:58:55 +0100 Subject: [PATCH 19/21] mock ResizeObserve to allow tests to run --- .../src/lib/ngx-d3timeline.component.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.spec.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.spec.ts index 7106e423..98367972 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.spec.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.spec.ts @@ -7,6 +7,14 @@ import { NgxD3TimelineService } from './ngx-d3timeline.service'; import { View } from './view/view'; import { ResourceRectangle } from './resource-rectangle/resource-rectangle'; +Object.defineProperty(window, 'ResizeObserver', { + value: jest.fn(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn() + })) +}); + @Component({ selector: '[ngx-d3timeline-axis]', template: ` @@ -59,6 +67,7 @@ describe('NgxD3timelineComponent', () => { resourceRectangles$: jest.fn(), resourceTickMarkRectangles$: jest.fn(), setupZoom: jest.fn(), + setupResizing: jest.fn(), onActivityDropped: jest.fn() } } From 4c364fff8fef473a652cfc76847cd99a2bec95e5 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 18:00:32 +0100 Subject: [PATCH 20/21] simplify parameters in resize observable --- projects/ngx-d3timeline/src/lib/core/resize-observable.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts index f8c0e595..101d2016 100644 --- a/projects/ngx-d3timeline/src/lib/core/resize-observable.ts +++ b/projects/ngx-d3timeline/src/lib/core/resize-observable.ts @@ -7,8 +7,8 @@ export function createResizeObservable(element: Element) { let resizeObserver: ResizeObserver; return fromEventPattern<[number, number]>( - handler => addResizeHandler(handler), - handler => removeResizeHandler(handler) + addResizeHandler, + removeResizeHandler ); function addResizeHandler( @@ -21,7 +21,7 @@ export function createResizeObservable(element: Element) { resizeObserver.observe(element); } - function removeResizeHandler(_: any) { + function removeResizeHandler() { resizeObserver.unobserve(element); } } From 8d7d2add8ccef8859551c6201ee9dbed23911a86 Mon Sep 17 00:00:00 2001 From: Wade Fleming Date: Sun, 5 Apr 2020 18:03:09 +0100 Subject: [PATCH 21/21] move method position in timeline service --- projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts index a3443106..5720a630 100644 --- a/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts +++ b/projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts @@ -93,16 +93,16 @@ export class NgxD3TimelineService implements OnDestroy { .subscribe(activity => activityDropped.emit(activity)); } - private zoomed() { - this.store.dispatch(new fromActions.ZoomedAction(event)); - } - setupResizing() { createResizeObservable(this.hostElement.nativeElement) .pipe(debounceTime(100), takeUntil(this.destroySubject)) .subscribe(dimensions => this.updateView(dimensions, this.zone)); } + private zoomed() { + this.store.dispatch(new fromActions.ZoomedAction(event)); + } + private updateView(dimensions: [number, number], zone: NgZone) { // zone.run is necessary due to current lack of monkey patching for ResizeObserver // https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5