@swimlane/ngx-charts
Version:
Declarative Charting Framework for Angular2 and beyond!
204 lines (169 loc) • 4.81 kB
text/typescript
import {
Component, Input, Output, EventEmitter, ElementRef,
OnChanges, ChangeDetectionStrategy, NgZone,
ChangeDetectorRef, SimpleChanges
} from '@angular/core';
import * as moment from 'moment';
import d3 from '../d3';
import { id } from '../utils/id';
export class Timeline implements OnChanges {
view;
state;
results;
scheme;
customColors;
legend;
miniChart;
autoScale;
scaleType;
height: number = 50;
select = new EventEmitter();
onDomainChange = new EventEmitter();
element: HTMLElement;
dims: any;
xDomain: any[];
xScale: any;
brush: any;
transform: string;
initialized: boolean = false;
filterId: any;
filter: any;
constructor(element: ElementRef, private zone: NgZone, private cd: ChangeDetectorRef) {
this.element = element.nativeElement;
}
ngOnChanges(changes: SimpleChanges): void {
this.update();
if (!this.initialized) {
this.addBrush();
this.initialized = true;
}
}
update(): void {
this.zone.run(() => {
this.dims = this.getDims();
this.height = this.dims.height;
let offsetY = this.view[1] - this.height;
this.xDomain = this.getXDomain();
this.xScale = this.getXScale();
if (this.brush) {
this.updateBrush();
}
this.transform = `translate(0 , ${ offsetY })`;
let pageUrl = window.location.href;
this.filterId = 'filter' + id().toString();
this.filter = `url(${pageUrl}#${this.filterId})`;
this.cd.markForCheck();
});
}
getXDomain(): any[] {
let values = [];
for (let results of this.results) {
for (let d of results.series){
if (!values.includes(d.name)) {
values.push(d.name);
}
}
}
let domain = [];
if (this.scaleType === 'time') {
values = values.map(v => moment(v).toDate());
let min = Math.min(...values);
let max = Math.max(...values);
domain = [min, max];
} else if (this.scaleType === 'linear') {
values = values.map(v => Number(v));
let min = Math.min(...values);
let max = Math.max(...values);
domain = [min, max];
} else {
domain = values;
}
return domain;
}
getXScale() {
let scale;
if (this.scaleType === 'time') {
scale = d3.scaleTime()
.range([0, this.dims.width])
.domain(this.xDomain);
} else if (this.scaleType === 'linear') {
scale = d3.scaleLinear()
.range([0, this.dims.width])
.domain(this.xDomain);
} else if (this.scaleType === 'ordinal') {
scale = d3.scalePoint()
.range([0, this.dims.width])
.padding(0.1)
.domain(this.xDomain);
}
return scale;
}
addBrush(): void {
if (this.brush) return;
const height = this.height;
const width = this.view[0];
this.brush = d3.brushX()
.extent([[0, 0], [width, height]])
.on('brush end', () => {
this.zone.run(() => {
const selection = d3.selection.event.selection || this.xScale.range();
const newDomain = selection.map(this.xScale.invert);
this.onDomainChange.emit(newDomain);
this.cd.markForCheck();
});
});
d3.select(this.element)
.select('.brush')
.call(this.brush);
}
updateBrush(): void {
if (!this.brush) return;
const height = this.height;
const width = this.view[0];
this.zone.run(() => {
this.brush.extent([[0, 0], [width, height]]);
d3.select(this.element)
.select('.brush')
.call(this.brush);
// clear hardcoded properties so they can be defined by CSS
d3.select(this.element).select('.selection')
.attr('fill', undefined)
.attr('stroke', undefined)
.attr('fill-opacity', undefined);
this.cd.markForCheck();
});
}
getDims(): any {
let width = this.view[0];
let dims = {
width,
height: this.height
};
return dims;
}
}