Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

371 set view to dimensions of parent #510

Merged
merged 23 commits into from
Apr 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ exports[`NgxD3timelineComponent view not null should render correctly 1`] = `
id="root1"
>



<svg
class="ngx-d3timeline"
height="600"
Expand Down Expand Up @@ -68,5 +70,6 @@ exports[`NgxD3timelineComponent view not null should render correctly 1`] = `
/>
</g>
</svg>

</div>
`;
27 changes: 27 additions & 0 deletions projects/ngx-d3timeline/src/lib/core/resize-observable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { fromEventPattern } from 'rxjs';

declare var ResizeObserver: any; // typings not yet available in Typescript
declare type ResizeObserver = any;

export function createResizeObservable(element: Element) {
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
let resizeObserver: ResizeObserver;

return fromEventPattern<[number, number]>(
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
addResizeHandler,
removeResizeHandler
);

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() {
resizeObserver.unobserve(element);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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: `
Expand Down Expand Up @@ -59,6 +67,7 @@ describe('NgxD3timelineComponent', () => {
resourceRectangles$: jest.fn(),
resourceTickMarkRectangles$: jest.fn(),
setupZoom: jest.fn(),
setupResizing: jest.fn(),
onActivityDropped: jest.fn()
}
}
Expand Down
113 changes: 55 additions & 58 deletions projects/ngx-d3timeline/src/lib/ngx-d3timeline.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
Component,
Input,
AfterViewInit,
ViewChild,
ElementRef,
ViewEncapsulation,
ChangeDetectionStrategy,
OnInit,
Output,
EventEmitter
EventEmitter,
NgZone
} from '@angular/core';
import { Activity } from './activity/activity';

Expand All @@ -20,67 +20,65 @@ import { ResourceRectangle } from './resource-rectangle/resource-rectangle';
@Component({
selector: 'ngx-d3timeline',
template: `
<svg
#svgEl
*ngIf="timeline.view$ | async as view"
[attr.width]="view.width"
[attr.height]="view.height"
class="ngx-d3timeline"
>
<g
ngx-d3timeline-axis
class="time-axis"
[axis]="timeline.timeAxis$ | async"
></g>
<g
ngx-d3timeline-resource-rectangle
class="resource-rectangle"
*ngFor="
let resourceRectangle of timeline.resourceRectangles$ | async;
trackBy: trackByFn
"
[resourceRectangle]="resourceRectangle"
(mouseenter)="resourceHovered.emit(resourceRectangle.id)"
(mouseleave)="resourceUnhovered.emit(resourceRectangle.id)"
(click)="resourceSelected.emit(resourceRectangle.id)"
></g>
<g
*ngFor="
let tickMarkRectangle of timeline.resourceTickMarkRectangles$ | async;
trackBy: trackByFn
"
class="resource-title-background"
ngx-d3timeline-resource-rectangle
[resourceRectangle]="tickMarkRectangle"
></g>
<g
ngx-d3timeline-axis
class="resources-axis"
[axis]="timeline.resourceAxis$ | async"
></g>

<g
ngx-d3timeline-content
(hovered)="activityHovered.emit($event)"
(unhovered)="activityUnhovered.emit($event)"
(selected)="activitySelected.emit($event)"
></g>
</svg>
<ng-container *ngIf="timeline.view$ | async as view">
<svg
#svgEl
*ngIf="!view.isEmpty"
[attr.width]="view.width"
[attr.height]="view.height"
class="ngx-d3timeline"
>
<g
ngx-d3timeline-axis
class="time-axis"
[axis]="timeline.timeAxis$ | async"
></g>
<g
ngx-d3timeline-resource-rectangle
class="resource-rectangle"
*ngFor="
let resourceRectangle of timeline.resourceRectangles$ | async;
trackBy: trackByFn
"
[resourceRectangle]="resourceRectangle"
(mouseenter)="resourceHovered.emit(resourceRectangle.id)"
(mouseleave)="resourceUnhovered.emit(resourceRectangle.id)"
(click)="resourceSelected.emit(resourceRectangle.id)"
></g>
<g
*ngFor="
let tickMarkRectangle of timeline.resourceTickMarkRectangles$
| async;
trackBy: trackByFn
"
class="resource-title-background"
ngx-d3timeline-resource-rectangle
[resourceRectangle]="tickMarkRectangle"
></g>
<g
ngx-d3timeline-axis
class="resources-axis"
[axis]="timeline.resourceAxis$ | async"
></g>
<g
ngx-d3timeline-content
(hovered)="activityHovered.emit($event)"
(unhovered)="activityUnhovered.emit($event)"
(selected)="activitySelected.emit($event)"
></g>
</svg>
</ng-container>
`,
styleUrls: ['./ngx-d3timeline.component.scss'],
encapsulation: ViewEncapsulation.None,
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);
}

@Input() set view([width, height]: [number, number]) {
this.timeline.setView([width, height]);
}

@Input() set options(options: Options) {
this.timeline.setOptions(options);
}
Expand All @@ -101,16 +99,15 @@ export class NgxD3timelineComponent implements OnInit, AfterViewInit {
@Output() activityUnhovered = new EventEmitter<Identifier>();
@Output() activitySelected = new EventEmitter<Identifier>();

@ViewChild('svgEl') svgEl: ElementRef<SVGElement>;
@ViewChild('svgEl') set svgElement(el: ElementRef<SVGElement>) {
this.timeline.setupZoom(el);
}

constructor(public timeline: NgxD3TimelineService) {}

ngOnInit(): void {
this.timeline.onActivityDropped(this.activityDropped);
}

ngAfterViewInit(): void {
this.timeline.setupZoom(this.svgEl);
this.timeline.setupResizing();
}

trackByFn(resourceRectangle: ResourceRectangle) {
Expand Down
39 changes: 35 additions & 4 deletions projects/ngx-d3timeline/src/lib/ngx-d3timeline.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Injectable, ElementRef, OnDestroy, EventEmitter } from '@angular/core';
import {
Injectable,
ElementRef,
EventEmitter,
OnDestroy,
NgZone
} from '@angular/core';
import { Store } from './store-lib/store';
import { selectView } from './store/state';
import {
Expand All @@ -10,7 +16,13 @@ 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, takeUntil } from 'rxjs/operators';
import {
map,
filter,
distinctUntilChanged,
debounceTime,
takeUntil
} from 'rxjs/operators';
import { selectLastDraggedActivity } from './activity/activity.selectors';

import {
Expand All @@ -19,6 +31,7 @@ import {
} from './resource-rectangle/selectors/resource-rectangle.selectors';
import { Subject } from 'rxjs';
import { Identifier } from './core/identifiable';
import { createResizeObservable } from './core/resize-observable';

@Injectable()
export class NgxD3TimelineService implements OnDestroy {
Expand All @@ -36,8 +49,12 @@ export class NgxD3TimelineService implements OnDestroy {
resourceTickMarkRectangles$ = this.store.select(selectResourceTickRectangles);

private destroySubject = new Subject<boolean>();

constructor(private store: Store, private axisService: AxisService) {}
constructor(
private store: Store,
private axisService: AxisService,
private hostElement: ElementRef,
private zone: NgZone
) {}

ngOnDestroy(): void {
this.destroySubject.next(true);
Expand Down Expand Up @@ -76,7 +93,21 @@ export class NgxD3TimelineService implements OnDestroy {
.subscribe(activity => activityDropped.emit(activity));
}

setupResizing() {
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
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
zone.run(() => {
this.setView(dimensions);
});
}
}
14 changes: 10 additions & 4 deletions projects/ngx-d3timeline/src/lib/view/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ 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;

constructor([width, height]: [number, number]) {
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);
}
}
3 changes: 2 additions & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<ng-container *ngIf="options$ | async as options">
<ngx-d3timeline
[activities]="activities"
[view]="[width, height]"
[style.width]="width"
[style.height]="height"
[options]="options"
[selectedId]="selectedId"
[hoveredId]="hoveredId"
Expand Down
8 changes: 8 additions & 0 deletions src/app/app.component.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
html,
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
body {
height: 100%;
}

ngx-d3timeline {
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
display: block;
}
.demo-app {
.ngx-d3timeline {
.activity-rectangle {
Expand Down
12 changes: 5 additions & 7 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ 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',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
encapsulation: ViewEncapsulation.None
umarmohammed marked this conversation as resolved.
Show resolved Hide resolved
})
export class AppComponent {
activities = deliveryData;
Expand Down Expand Up @@ -53,12 +53,11 @@ export class AppComponent {
hoveredId = null;

options$ = this.optionUpdateSubject.pipe(
scan(this.updateOptions, this.initialOptions),
tap(console.log)
scan(this.updateOptions, this.initialOptions)
);

width = 800;
height = 600;
width = '800px';
height = '600px';

onDropped(activity: Activity) {
console.log(activity);
Expand Down Expand Up @@ -94,7 +93,6 @@ export class AppComponent {
}

private updateOptions(options: Options, value: { [key: string]: any }) {
console.log(value);
return { ...{ ...options, ...value } };
}
}