ng-d3-graphs
Version:
<img src="./assets/ng-d3.png" alt="drawing" width="250" height="250"/>
1,268 lines (1,241 loc) • 45.6 kB
JavaScript
import { __decorate, __param } from 'tslib';
import { ɵɵdefineInjectable, Injectable, ElementRef, Input, HostListener, Component, ViewEncapsulation, NgModule, ChangeDetectionStrategy, Inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { axisLeft, axisBottom, axisRight, axisTop, line, scaleLinear, extent, scaleBand, timeFormat, select, timeParse, scaleTime, min, max, curveStep, area, utcParse, schemeSet2, scaleOrdinal, pie, entries, arc, interpolateCool } from 'd3';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
const axisConfig = {
color: 'lightgrey',
opacity: 1,
rendering: 'crispEdges',
strokeWidth: '1px',
xAxisTimeParser: '%Y-%m-%dT%H:%M:%S.%LZ',
xAxisTimeFormat: '%m/%d/%y',
xAxisTicks: 5,
};
var AxisDirection;
(function (AxisDirection) {
AxisDirection["top"] = "top";
AxisDirection["right"] = "right";
AxisDirection["bottom"] = "bottom";
AxisDirection["left"] = "left";
})(AxisDirection || (AxisDirection = {}));
let D3Service = class D3Service {
constructor() { }
translate(x, y) {
return `translate(${x}, ${y})`;
}
factoryAxis(scale, direction) {
switch (direction) {
case AxisDirection.top:
return axisTop(scale);
case AxisDirection.right:
return axisRight(scale);
case AxisDirection.bottom:
return axisBottom(scale);
case AxisDirection.left:
return axisLeft(scale);
default:
return new Error('No axis Direction Provided');
}
}
factoryLine() {
return line().x((d) => d.x).y((d) => d.y);
}
// ==== Axis =====
scaleLinearX(labels, width) {
return scaleLinear()
.domain(extent(labels)) // does the magic for adjustable axis
.range([0, width]);
}
scaleLinearY(data, height) {
return scaleLinear()
.domain(extent(data)) // does the magic for adjustable axis
.range([height, 0]);
}
scaleBandX(labels, width) {
return scaleBand().domain(labels).rangeRound([0, width]).padding(0.1);
}
scaleLinearYRangeRound(data, height) {
return scaleLinear().domain([0, Math.max(...data)]).rangeRound([
height, 0
]);
}
addLabelAxisY(svg, height, options) {
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 0 - options.margin.left)
.attr('x', 0 - height / 2)
.attr('dy', '1em')
.style('text-anchor', 'middle')
.text(options.yAxisLabel);
}
addLabelAxisX(svg, width, height, options) {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' + (height + options.margin.top) +
')')
.style('text-anchor', 'middle')
.text(options.xAxisLabel);
}
getViewBoxDefault(options) {
const res = {
minX: -options.margin.left,
minY: -25,
width: options.width,
height: options.height - options.margin.top,
};
return res;
}
removeAxisTicks(axis) {
axis.selectAll('.tick').selectAll('line').remove();
}
changeAxisColor(axis, config) {
axis.select('path')
.attr('color', config.color)
.attr('opacity', config.opacity)
.attr('rendering', config.rendering)
.attr('stroke-width', config.strokeWidth);
}
getXaxisTime(svg, height, x, timeFormat$1, xAxisTicks) {
return svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(axisBottom(x)
.tickFormat(timeFormat(timeFormat$1))
.ticks(xAxisTicks));
}
hideTooltip(tooltipText, tooltip) {
tooltipText.selectAll('tspan').remove();
tooltip.attr('visibility', 'hidden');
}
showTooltip(d, xScale, yScale, tooltip, tooltipRect, tooltipText, formatTime) {
const xPos = xScale(d.x) - 150 / 2;
const yPos = yScale(d.y) + 10;
tooltip.attr('transform', `translate(${xPos}, ${yPos})`)
.attr('is', true)
.attr('visibility', 'visible');
tooltipRect.attr('opacity', 0.7);
tooltipText.attr('tranform', 'translate(75,30)')
.attr('fill', 'white')
.attr('font-size', 10)
.attr('font-family', `'Roboto', 'sans-serif'`);
tooltipText.append('tspan')
.attr('text-anchor', 'middle')
.attr('is', true)
.attr('x', 25)
.attr('y', -5)
.text(`${formatTime(d.x)}`);
tooltipText.append('tspan')
.attr('text-anchor', 'middle')
.attr('is', true)
.attr('x', 20)
.attr('dy', 15)
.text(`${d.y}`);
}
addTooltip(container) {
const tooltipConfig = {
width: 100,
height: 40,
fill: '#333',
opacity: 0.7,
rx: 15,
text: {
translateX: 10,
translateY: 20,
},
};
const tooltip = select(container.nativeElement).select('svg').append('g');
const tooltipRect = tooltip.append('rect')
.attr('width', tooltipConfig.width)
.attr('height', tooltipConfig.height)
.attr('fill', tooltipConfig.fill)
.attr('opacity', 0)
.attr('rx', tooltipConfig.rx);
const tooltipText = tooltip.append('text').attr('transform', `translate(
${tooltipConfig.text.translateX},
${tooltipConfig.text.translateY})`);
return { tooltip, tooltipRect, tooltipText, tooltipConfig };
}
};
D3Service.ngInjectableDef = ɵɵdefineInjectable({ factory: function D3Service_Factory() { return new D3Service(); }, token: D3Service, providedIn: "root" });
D3Service = __decorate([
Injectable({ providedIn: 'root' })
], D3Service);
let BandComponent = class BandComponent {
constructor(container, d3Service) {
this.container = container;
this.d3Service = d3Service;
this.data = [];
this.labels = [];
this.options = {};
this.labelsAndData = [];
this.viewBox = {};
this._options = {
width: 879,
height: 804,
margin: { top: 50, right: 50, bottom: 50, left: 50 },
yAxisLabel: '',
gridTicks: 0,
timeParser: axisConfig.xAxisTimeParser,
timeFormat: axisConfig.xAxisTimeFormat,
xAxisTicks: axisConfig.xAxisTicks,
};
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.onResize$ = new Subject();
}
onResize() {
this.onResize$.next();
}
ngOnInit() {
this.options = Object.assign({}, this._options, this.options);
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width + this.options.margin.left +
this.options.margin.right,
height: this.options.height + this.options.margin.top,
};
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.labels = this.formatLabels();
this.labelsAndData = this.combineLabelsDataToOne();
this.onResizeEvent();
this.render();
}
formatLabels() {
return this.labels.map(d => this.parseTime(d));
}
combineLabelsDataToOne() {
const N = this.labels.length;
const result = [];
for (let index = 0; index < N; index++) {
result.push({
x: this.labels[index],
low: this.data[index].low,
high: this.data[index].high,
});
}
return result;
}
render() {
const currentWidth = parseInt(select(this.container.nativeElement).select('div').style('width'), 10);
const currentHeight = parseInt(select(this.container.nativeElement).select('div').style('height'), 10);
const width = this.options.width - this.options.margin.left -
this.options.margin.right;
const height = this.options.height - this.options.margin.top -
this.options.margin.bottom;
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width,
height: this.options.height - this.options.margin.top,
};
const svg = select(this.container.nativeElement)
.select('div')
.append('svg')
.attr('width', currentWidth)
.attr('height', currentHeight)
.attr('viewBox', `${this.viewBox.minX} ${this.viewBox.minY} ${this.viewBox.width} ${this.viewBox.height}`)
.classed('svg-content', true)
.append('g');
const x = scaleTime()
.domain(extent(this.labels, (d) => new Date(d)))
.range([0, width]);
const y = scaleLinear()
.domain([
min(this.data, (d) => d.low), max(this.data, (d) => d.high)
])
.nice(this.options.gridTicks)
.range([height, 0]);
// add the X gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_x_gridlines(x).tickSize(height)
// .tickFormat('')
);
// add the Y gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_y_gridlines(y).tickSize(-width)
// .tickFormat('')
);
const xAxis = this.d3Service.getXaxisTime(svg, height, x, this.options.timeFormat, this.options.xAxisTicks);
const yAxis = (g) => g.attr('transform', `translate(${0},0)`).call(axisLeft(y));
const curve = curveStep;
const area$1 = area()
.curve(curve)
.x((d) => x(d.x))
.y0((d) => y(d.low))
.y1((d) => y(d.high));
const _yAxis = svg.append('g').call(yAxis);
// this.d3Service.addLabelAxisX(svg, width, height, this.options);
// text label for the x axis
this.addLabelAxisX(svg, width, height);
// text label for the y axis
this.addLabelAxisY(svg, height);
svg.append('path')
.datum(this.labelsAndData)
.attr('fill', 'steelblue')
.attr('d', area$1);
this.removeAxisTicks(xAxis);
this.removeAxisTicks(_yAxis);
this.changeAxisColor(xAxis, axisConfig);
this.changeAxisColor(_yAxis, axisConfig);
}
changeAxisColor(axis, config) {
this.d3Service.changeAxisColor(axis, config);
}
removeAxisTicks(axis) {
this.d3Service.removeAxisTicks(axis);
}
addLabelAxisY(svg, height) {
svg.append('text')
.attr('transform', 'rotate(0)')
.attr('y', 0 - this.options.margin.top / 2)
.attr('x', 0)
.attr('dy', '1em')
.style('text-anchor', 'start')
.text(this.options.yAxisLabel);
}
addLabelAxisX(svg, width, height) {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' +
(height + this.options.margin.top - 15) + ')')
.style('text-anchor', 'middle')
.text(this.options.xAxisLabel);
}
// gridlines in x axis function
make_x_gridlines(x) {
return axisBottom(x).ticks(this.options.gridTicks);
}
// gridlines in y axis function
make_y_gridlines(y) {
return axisLeft(y).ticks(this.options.gridTicks);
}
onResizeEvent() {
this.onResize$.pipe(debounceTime(200)).subscribe(() => {
const svgExist = select(this.container.nativeElement).select('svg');
if (svgExist) {
svgExist.remove();
}
this.render();
});
}
};
BandComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: D3Service }
];
__decorate([
Input()
], BandComponent.prototype, "data", void 0);
__decorate([
Input()
], BandComponent.prototype, "labels", void 0);
__decorate([
Input()
], BandComponent.prototype, "options", void 0);
__decorate([
HostListener('window:resize')
], BandComponent.prototype, "onResize", null);
BandComponent = __decorate([
Component({
selector: 'ng-band',
template: "<div class=\"svg-container\"></div>\n",
encapsulation: ViewEncapsulation.None,
styles: [".svg-container{display:inline-block;position:relative;width:100%;height:100%;padding-bottom:100%;vertical-align:top;overflow:hidden}.svg-content{display:inline-block;position:absolute;top:0;left:0}.grid line{stroke:#d3d3d3;stroke-opacity:.4;shape-rendering:crispEdges}.grid path{stroke-width:0}.grid text{display:none}.area{fill:#4682b4}"]
})
], BandComponent);
let BandModule = class BandModule {
};
BandModule = __decorate([
NgModule({
declarations: [BandComponent],
imports: [
CommonModule
],
exports: [BandComponent],
})
], BandModule);
let BarComponent = class BarComponent {
constructor(container, d3Service) {
this.container = container;
this.d3Service = d3Service;
this.data = [];
this.labels = [];
this.options = {};
this.graph = {
xAxis: [],
yAxis: [],
xAxisPath: '',
yAxisPath: '',
rectanglesData: [],
};
this.labelsAndData = [];
this.parseTime = timeParse('%d-%b-%y');
this._options = {
width: 879,
height: 804,
margin: { top: 50, right: 50, bottom: 50, left: 50 },
gridTicks: 0,
};
this.viewBox = {};
this.onResize$ = new Subject();
}
onResize() {
this.onResize$.next();
}
ngOnInit() {
this.options = Object.assign({}, this._options, this.options);
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width + this.options.margin.left +
this.options.margin.right,
height: this.options.height + this.options.margin.top,
};
this.labelsAndData = this.combineLabelsDataToOne();
this.onResizeEvent();
this.render();
}
render() {
const currentWidth = parseInt(select(this.container.nativeElement).select('div').style('width'), 10);
const currentHeight = parseInt(select(this.container.nativeElement).select('div').style('height'), 10);
const width = this.options.width - this.options.margin.left -
this.options.margin.right;
const height = this.options.height - this.options.margin.top -
this.options.margin.bottom;
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width,
height: this.options.height - this.options.margin.top,
};
const svg = select(this.container.nativeElement)
.select('div')
.append('svg')
.attr('width', currentWidth)
.attr('height', currentHeight)
.attr('viewBox', `${this.viewBox.minX} ${this.viewBox.minY} ${this.viewBox.width} ${this.viewBox.height}`)
.classed('svg-content', true)
.append('g');
const x = scaleBand().rangeRound([0, width]).padding(0.1).domain(this.labels);
const y = scaleLinear().rangeRound([height, 0]).domain([
0, Math.max(...this.data.map((d) => Number(d)))
]);
const xAxis = (g) => g.call(axisBottom(x))
.attr('transform', 'translate(0,' + height + ')');
const yAxis = (g) => g.call(axisLeft(y));
// add the X gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_x_gridlines(x).tickSize(height)
// .tickFormat('')
);
// add the Y gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_y_gridlines(y).tickSize(-width)
// .tickFormat('')
);
svg.selectAll('.bar')
.data(this.labelsAndData)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d) => {
return x(d.x);
})
.attr('y', (d) => {
return y(Number(d.y));
})
.attr('width', x.bandwidth())
.attr('height', (d) => {
return height - y(Number(d.y));
});
const _xAxis = svg.append('g').call(xAxis);
// text label for the x axis
this.addLabelAxisX(svg, width, height);
const _yAxis = svg.append('g').call(yAxis);
// text label for the y axis
this.addLabelAxisY(svg, height);
this.removeAxisTicks(_xAxis);
this.removeAxisTicks(_yAxis);
this.changeAxisColor(_xAxis, axisConfig);
this.changeAxisColor(_yAxis, axisConfig);
}
changeAxisColor(axis, config) {
this.d3Service.changeAxisColor(axis, config);
}
removeAxisTicks(axis) {
this.d3Service.removeAxisTicks(axis);
}
addLabelAxisY(svg, height) {
svg.append('text')
.attr('transform', 'rotate(0)')
.attr('y', 0 - this.options.margin.top / 2)
.attr('x', 0)
.attr('dy', '1em')
.style('text-anchor', 'start')
.text(this.options.yAxisLabel);
}
addLabelAxisX(svg, width, height) {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' +
(height + this.options.margin.top - 15) + ')')
.style('text-anchor', 'middle')
.text(this.options.xAxisLabel);
}
combineLabelsDataToOne() {
const result = [];
const N = this.data.length;
for (let index = 0; index < N; index++) {
result.push({ x: this.labels[index], y: this.data[index] });
}
return result;
}
// gridlines in x axis function
make_x_gridlines(x) {
return axisBottom(x).ticks(this.options.gridTicks);
}
// gridlines in y axis function
make_y_gridlines(y) {
return axisLeft(y).ticks(this.options.gridTicks);
}
onResizeEvent() {
this.onResize$.pipe(debounceTime(200)).subscribe(() => {
const svgExist = select(this.container.nativeElement).select('svg');
if (svgExist) {
svgExist.remove();
}
this.render();
});
}
};
BarComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: D3Service }
];
__decorate([
Input()
], BarComponent.prototype, "data", void 0);
__decorate([
Input()
], BarComponent.prototype, "labels", void 0);
__decorate([
Input()
], BarComponent.prototype, "options", void 0);
__decorate([
HostListener('window:resize')
], BarComponent.prototype, "onResize", null);
BarComponent = __decorate([
Component({
selector: 'ng-bar',
template: "\n<div class=\"svg-container\"></div>\n",
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [".svg-container{display:inline-block;position:relative;width:100%;height:100%;padding-bottom:100%;vertical-align:top;overflow:hidden}.svg-content{display:inline-block;position:absolute;top:0;left:0}.grid line{stroke:#d3d3d3;stroke-opacity:.4;shape-rendering:crispEdges}.grid path{stroke-width:0}.grid text{display:none}.bar{fill:#4682b4}.bar:hover{fill:brown}"]
})
], BarComponent);
let BarService = class BarService {
constructor() { }
};
BarService = __decorate([
Injectable()
], BarService);
let BarModule = class BarModule {
};
BarModule = __decorate([
NgModule({
declarations: [BarComponent],
imports: [
CommonModule
],
providers: [
BarService
],
exports: [BarComponent]
})
], BarModule);
let LineComponent = class LineComponent {
constructor(container, d3Service) {
this.container = container;
this.d3Service = d3Service;
this.data = [];
this.labels = [];
this.options = {};
this._options = {
width: 879,
height: 804,
margin: { top: 50, right: 50, bottom: 50, left: 50 },
gridTicks: 0,
yAxisLabel: '',
xAxisLabel: '',
timeParser: axisConfig.xAxisTimeParser,
timeFormat: axisConfig.xAxisTimeFormat,
xAxisTicks: axisConfig.xAxisTicks,
};
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.viewBox = {};
this.labelsAndData = [];
this.AxisDataX = [];
this.onResize$ = new Subject();
}
onResize() {
this.onResize$.next();
}
ngOnInit() {
this.options = Object.assign({}, this._options, this.options);
this.viewBox = this.d3Service.getViewBoxDefault(this.options);
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.labels = this.labels.map(d => this.parseTime(d));
this.labelsAndData = this.combineLabelsDataToOne();
this.onResizeEvent();
this.render();
}
render() {
const currentWidth = parseInt(select(this.container.nativeElement).select('div').style('width'), 10);
const currentHeight = parseInt(select(this.container.nativeElement).select('div').style('height'), 10);
const width = this.options.width - this.options.margin.left -
this.options.margin.right;
const height = this.options.height - this.options.margin.top -
this.options.margin.bottom;
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width,
height: this.options.height - this.options.margin.top,
};
const svg = select(this.container.nativeElement)
.select('div')
.append('svg')
.attr('width', currentWidth)
.attr('height', currentHeight)
.attr('viewBox', `${this.viewBox.minX} ${this.viewBox.minY} ${this.viewBox.width} ${this.viewBox.height}`)
.classed('svg-content', true)
.append('g');
const x = scaleTime().range([0, width]);
const y = scaleLinear().range([height, 0]).nice();
const valueline = line().x((d) => x(d.x)).y((d) => y(d.y));
x.domain(extent(this.labels, (d) => (d)));
y.domain([0, max(this.data, (d) => d)]);
// add the X gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_x_gridlines(x).tickSize(height)
// .tickFormat('')
);
// add the Y gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_y_gridlines(y).tickSize(-width)
// .tickFormat('')
);
svg.append('path')
.datum(this.labelsAndData)
.attr('class', 'line')
.attr('d', valueline);
// add the X Axis
const xAxis = this.d3Service.getXaxisTime(svg, height, x, this.options.timeFormat, this.options.xAxisTicks);
// text label for the x axis
this.addLabelAxisX(svg, width, height);
// add the Y Axis
const yAxis = svg.append('g').call(axisLeft(y));
// text label for the y axis
this.addLabelAxisY(svg, height);
this.removeAxisTicks(xAxis);
this.removeAxisTicks(yAxis);
this.changeAxisColor(xAxis, axisConfig);
this.changeAxisColor(yAxis, axisConfig);
this.addDots(svg, x, y);
}
addDots(svg, x, y) {
const dotRadius = 3;
const dotColor = '#4682b4';
// add tootlip
const { tooltip, tooltipRect, tooltipText, tooltipConfig } = this.d3Service.addTooltip(this.container);
svg.selectAll('dot')
.data(this.labelsAndData)
.enter()
.append('circle')
.attr('r', dotRadius)
.attr('fill', dotColor)
.attr('cx', (d) => {
return x(d.x);
})
.attr('cy', (d) => {
return y(d.y);
})
.on('mouseover', (d) => {
this.onMouseOver(d, x, y, tooltip, tooltipRect, tooltipText);
})
.on('mouseout', (d) => {
this.onMouseOut(d, tooltip, tooltipText, tooltipConfig);
});
}
onMouseOver(d, xScale, yScale, tooltip, tooltipRect, tooltipText) {
// show tooltip
if (tooltip) {
this.d3Service.showTooltip(d, xScale, yScale, tooltip, tooltipRect, tooltipText, this.formatTime);
}
}
onMouseOut(d, tooltip, tooltipText, tooltipConfig) {
// hide tooltip
if (tooltip) {
this.d3Service.hideTooltip(tooltipText, tooltip);
}
}
changeAxisColor(axis, config) {
this.d3Service.changeAxisColor(axis, config);
}
removeAxisTicks(axis) {
this.d3Service.removeAxisTicks(axis);
}
addLabelAxisY(svg, height) {
svg.append('text')
.attr('transform', 'rotate(0)')
.attr('y', 0 - this.options.margin.top / 2)
.attr('x', 0)
.attr('dy', '1em')
.style('text-anchor', 'start')
.text(this.options.yAxisLabel);
}
addLabelAxisX(svg, width, height) {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' +
(height + this.options.margin.top - 15) + ')')
.style('text-anchor', 'middle')
.text(this.options.xAxisLabel);
}
combineLabelsDataToOne() {
const result = [];
const N = this.data.length;
for (let index = 0; index < N; index++) {
result.push({ x: this.labels[index], y: this.data[index] });
}
return result;
}
// gridlines in x axis function
make_x_gridlines(x) {
return axisBottom(x).ticks(this.options.gridTicks);
}
// gridlines in y axis function
make_y_gridlines(y) {
return axisLeft(y).ticks(this.options.gridTicks);
}
onResizeEvent() {
this.onResize$.pipe(debounceTime(200)).subscribe(() => {
const svgExist = select(this.container.nativeElement).select('svg');
if (svgExist) {
svgExist.remove();
}
this.render();
});
}
};
LineComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: D3Service }
];
__decorate([
Input()
], LineComponent.prototype, "data", void 0);
__decorate([
Input()
], LineComponent.prototype, "labels", void 0);
__decorate([
Input()
], LineComponent.prototype, "options", void 0);
__decorate([
HostListener('window:resize')
], LineComponent.prototype, "onResize", null);
LineComponent = __decorate([
Component({
selector: 'ng-line',
template: "<div class=\"svg-container\"></div>\n",
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [".svg-container{display:inline-block;position:relative;width:100%;height:100%;padding-bottom:100%;vertical-align:top;overflow:hidden}.svg-content{display:inline-block;position:absolute;top:0;left:0}.grid line{stroke:#d3d3d3;stroke-opacity:.4;shape-rendering:crispEdges}.grid path{stroke-width:0}.grid text{display:none}.line{fill:none;stroke:#4682b4;stroke-width:2px}div.tooltip{position:absolute;text-align:center;min-width:60px;min-height:28px;padding:5px;font:12px sans-serif;background:#b0c4de;border:0;border-radius:5px;pointer-events:none;color:#000}"]
})
], LineComponent);
let LineService = class LineService {
constructor(config) {
this.config = config;
}
showConfig() {
console.log(this.config);
}
};
LineService.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: ['config',] }] }
];
LineService = __decorate([
Injectable(),
__param(0, Inject('config'))
], LineService);
var LineModule_1;
let LineModule = LineModule_1 = class LineModule {
static forRoot(config) {
return {
ngModule: LineModule_1,
providers: [
LineService,
{ provide: 'config', useValue: config },
]
};
}
};
LineModule = LineModule_1 = __decorate([
NgModule({
declarations: [LineComponent,],
imports: [
CommonModule
],
exports: [LineComponent],
providers: [
LineService
]
})
], LineModule);
let MultilineComponent = class MultilineComponent {
constructor(container, d3Service) {
this.container = container;
this.d3Service = d3Service;
this.data = [];
this.labels = [];
this.options = {};
this.labelsAndData = [];
this.utcParse = utcParse('%Y-%m');
this.viewBox = {};
this._options = {
width: 879,
height: 804,
yAxisLabel: '',
xAxisLabel: '',
margin: { top: 50, right: 50, bottom: 50, left: 50 },
timeParser: axisConfig.xAxisTimeParser,
timeFormat: axisConfig.xAxisTimeFormat,
xAxisTicks: axisConfig.xAxisTicks,
};
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.onResize$ = new Subject();
}
onResize() {
this.onResize$.next();
}
ngOnInit() {
this.options = Object.assign({}, this._options, this.options);
this.viewBox = {
minX: -this.options.margin.left,
minY: -25,
width: this.options.width + this.options.margin.left +
this.options.margin.right,
height: this.options.height + this.options.margin.top,
};
this.parseTime = timeParse(this.options.timeParser);
this.formatTime = timeFormat(this.options.timeFormat);
this.labels = this.formatData();
this.labelsAndData = this.combineLabelsDataToOne();
this.onResizeEvent();
this.render();
}
formatData() {
return this.labels.map(d => this.parseTime(d));
}
combineLabelsDataToOne() {
const result = [];
const N = this.data.length;
for (let index = 0; index < N; index++) {
result.push({ x: this.labels, y: this.data[index] });
}
return result;
}
render() {
const currentWidth = parseInt(select(this.container.nativeElement).select('div').style('width'), 10);
const currentHeight = parseInt(select(this.container.nativeElement).select('div').style('height'), 10);
const width = this.options.width - this.options.margin.left -
this.options.margin.right;
const height = this.options.height - this.options.margin.top -
this.options.margin.bottom;
this.viewBox = {
minX: -this.options.margin.left,
minY: -10,
width: this.options.width,
height: this.options.height - this.options.margin.top,
};
const svg = select(this.container.nativeElement)
.select('div')
.append('svg')
.attr('width', currentWidth)
.attr('height', currentHeight)
.attr('viewBox', `${this.viewBox.minX} ${this.viewBox.minY} ${this.viewBox.width} ${this.viewBox.height}`)
.classed('svg-content', true)
.append('g');
const xDomain = this.getXdomain();
const x = scaleTime().domain(xDomain).range([0, width]);
const y = scaleLinear()
.domain([0, max(this.data, (d) => max(d.values))])
.range([height, 0])
.nice();
// const xAxis = (g) =>
// g.attr('transform', `translate(0,${height})`).call(d3.axisBottom(x));
const xAxis = this.d3Service.getXaxisTime(svg, height, x, this.options.timeFormat, this.options.xAxisTicks);
const yAxis = (g) => g.call(axisLeft(y));
const line$1 = line()
.defined((d) => !isNaN(d))
.x((d, i) => x(this.labels[i]))
.y((d) => y(d));
// add the X gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_x_gridlines(x).tickSize(height)
// .tickFormat('')
);
// add the Y gridlines
svg.append('g')
.attr('class', 'grid')
.call(this.make_y_gridlines(y).tickSize(-width)
// .tickFormat('')
);
const _yAxis = svg.append('g').call(yAxis);
// text label for the x axis
this.addLabelAxisX(svg, width, height);
// text label for the y axis
this.addLabelAxisY(svg, height);
const path = svg.append('g')
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.5)
.attr('stroke-linejoin', 'round')
.attr('stroke-linecap', 'round')
.selectAll('path')
.data(this.data)
.join('path')
.style('mix-blend-mode', 'multiply')
.attr('d', (d) => line$1(d.values))
.text('this is ');
this.removeAxisTicks(xAxis);
this.removeAxisTicks(_yAxis);
this.changeAxisColor(xAxis, axisConfig);
this.changeAxisColor(_yAxis, axisConfig);
// TODO: comment in when issue #61 is fixed
/* svg.call(hover, path, this);
function hover(svg, path, _this) {
if ('ontouchstart' in document) {
svg.style('-webkit-tap-highlight-color', 'transparent')
.on('touchmove', moved)
.on('touchstart', entered)
.on('touchend', left);
} else {
svg.on('mousemove', moved)
.on('mouseenter', entered)
.on('mouseleave', left);
}
const dot = svg.append('g').attr('display', 'none');
dot.append('circle').attr('r', 2.5);
dot.append('text')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('text-anchor', 'middle')
.attr('y', -8);
function moved() {
d3.event.preventDefault();
const ym = y.invert(d3.event.layerY) as any;
const xm = x.invert(d3.event.layerX) as any;
const i1 = d3.bisectLeft(_this.labels, xm, 1);
const i0 = i1 - 1;
const i = xm - _this.labels[i0] > _this.labels[i1] - xm ? i1 : i0;
// const s = d3.least(_this.data, d => Math.abs(d.values[i] - ym));
const s = _this.least(_this.data, d => Math.abs(d.values[i] - ym), i,
ym); path.attr('stroke', d => d === s ? null : '#ddd') .filter(d => d === s)
.raise();
dot.attr(
'transform', `translate(${x(_this.labels[i])},${y(s.values[i])})`);
dot.select('text').text(s.name);
}
function entered() {
path.style('mix-blend-mode', null).attr('stroke', '#ddd');
dot.attr('display', null);
}
function left() {
path.style('mix-blend-mode', 'multiply').attr('stroke', null);
dot.attr('display', 'none');
}
}
*/
}
changeAxisColor(axis, config) {
this.d3Service.changeAxisColor(axis, config);
}
removeAxisTicks(axis) {
this.d3Service.removeAxisTicks(axis);
}
addLabelAxisY(svg, height) {
svg.append('text')
.attr('transform', 'rotate(0)')
.attr('y', 0 - this.options.margin.top / 2)
.attr('x', 0)
.attr('dy', '1em')
.style('text-anchor', 'start')
.text(this.options.yAxisLabel);
}
addLabelAxisX(svg, width, height) {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' +
(height + this.options.margin.top - 15) + ')')
.style('text-anchor', 'middle')
.text(this.options.xAxisLabel);
}
getXdomain() {
const domainExtent = extent(this.labels, (d) => d);
return domainExtent.map((d) => new Date(d));
}
least(arr, filterFun, pos, ym) {
const tempValues = arr.map((d) => filterFun(d));
const minNum = Math.min(...tempValues);
let graphHovered;
let minimax = tempValues[0];
let minPos = 0;
for (let i = 1; i < tempValues.length; i++) {
const element = tempValues[i];
if (element >= minimax) {
minPos = i;
minimax = element;
}
}
graphHovered = arr[minPos];
return graphHovered;
}
// gridlines in x axis function
make_x_gridlines(x) {
return axisBottom(x).ticks(this.options.gridTicks);
}
// gridlines in y axis function
make_y_gridlines(y) {
return axisLeft(y).ticks(this.options.gridTicks);
}
onResizeEvent() {
this.onResize$.pipe(debounceTime(200)).subscribe(() => {
const svgExist = select(this.container.nativeElement).select('svg');
if (svgExist) {
svgExist.remove();
}
this.render();
});
}
};
MultilineComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: D3Service }
];
__decorate([
Input()
], MultilineComponent.prototype, "data", void 0);
__decorate([
Input()
], MultilineComponent.prototype, "labels", void 0);
__decorate([
Input()
], MultilineComponent.prototype, "options", void 0);
__decorate([
HostListener('window:resize')
], MultilineComponent.prototype, "onResize", null);
MultilineComponent = __decorate([
Component({
selector: 'ng-multiline',
template: "<div class=\"svg-container\"></div>\n",
encapsulation: ViewEncapsulation.None,
styles: [".svg-container{display:inline-block;position:relative;width:100%;height:100%;padding-bottom:100%;vertical-align:top;overflow:hidden}.svg-content{display:inline-block;position:absolute;top:0;left:0}.grid line{stroke:#d3d3d3;stroke-opacity:.4;shape-rendering:crispEdges}.grid path{stroke-width:0}.grid text{display:none}"]
})
], MultilineComponent);
let MultilineModule = class MultilineModule {
};
MultilineModule = __decorate([
NgModule({
declarations: [MultilineComponent],
imports: [
CommonModule,
],
exports: [MultilineComponent],
})
], MultilineModule);
let PieComponent = class PieComponent {
constructor(container) {
this.container = container;
this.labels = [];
this.data = [];
this.backgroundColors = schemeSet2;
this.radius = 100;
this.options = {};
this.color = this.interpolateColor(); // range [0,1] -> builtin range of colors.
this.defaultSliceColor = 'steerblue';
this.labelsAndData = [];
this.viewBox = {};
this._options = {
width: 300,
height: 300,
margin: { top: 50, right: 50, bottom: 50, left: 50 },
};
this.onResize$ = new Subject();
}
onResize() {
this.onResize$.next();
}
ngOnInit() {
this.options = Object.assign({}, this._options, this.options);
this.viewBox = {
minX: -this.options.margin.left,
minY: 0,
width: Number(this.options.width) + Number(this.options.margin.left) + Number(this.options.margin.right),
height: this.options.height,
};
this.onBgdColorUndefined();
this.onResizeEvent();
this.render();
}
onBgdColorUndefined() {
if (this.backgroundColors.length === 0) {
// TODO: check linter
// for (let index = 0; index < this.data.length; index++) {
// this.backgroundColors.push(this.defaultSliceColor);
// }
for (const iterator of this.data) {
this.backgroundColors.push(this.defaultSliceColor);
}
}
}
render() {
const currentWidth = parseInt(select(this.container.nativeElement).select('div').style('width'), 10);
const currentHeight = parseInt(select(this.container.nativeElement).select('div').style('height'), 10);
const radius = Math.min(this.options.width, this.options.height) / 2 - this.options.margin.top;
const svg = select(this.container.nativeElement)
.select('div')
.append('svg')
.attr('width', currentWidth)
.attr('height', currentHeight)
.attr('viewBox', `${this.viewBox.minX} ${this.viewBox.minY} ${this.viewBox.width} ${this.viewBox.height}`)
.classed('svg-content', true)
.append('g')
.attr('transform', 'translate(' + this.options.width / 2 + ',' + this.options.height / 2 + ')');
const color = scaleOrdinal().domain(this.data).range(this.backgroundColors);
const pie$1 = pie().value((d) => d.value);
const pieData = pie$1(entries(this.data));
const arcGenerator = arc().innerRadius(0).outerRadius(radius);
svg
.selectAll('slices')
.data(pieData)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', (d) => this.backgroundColors[d.index])
.attr('stroke', 'black')
.style('stroke-width', '2px')
.style('opacity', 0.7);
svg
.selectAll('slices')
.data(pieData)
.enter()
.append('text')
.text((d) => this.labels[d.index])
.attr('transform', (d) => {
return ('translate(' +
arcGenerator.centroid({
startAngle: d.startAngle,
endAngle: d.endAngle,
}) +
')');
})
.style('text-anchor', 'middle')
.style('font-size', 17);
this.addLabelAxisX(svg, this.options.width, this.options.height);
}
addLabelAxisX(svg, width, height) {
svg
.append('text')
.attr('transform', `translate(${0}, ${this.options.margin.top * 2.5})`)
.style('text-anchor', 'middle')
.text(this.options.xAxisLabel);
}
/**
* range [0, 1]
*/
interpolateColor() {
return interpolateCool;
}
combineLabelsDataToOne() {
const result = [];
const N = this.data.length;
for (let index = 0; index < N; index++) {
result.push({ x: this.labels[index], y: this.data[index] });
}
return result;
}
onResizeEvent() {
this.onResize$.pipe(debounceTime(200)).subscribe(() => {
const svgExist = select(this.container.nativeElement).select('svg');
if (svgExist) {
svgExist.remove();
}
this.render();
});
}
};
PieComponent.ctorParameters = () => [
{ type: ElementRef }
];
__decorate([
Input()
], PieComponent.prototype, "labels", void 0);
__decorate([
Input()
], PieComponent.prototype, "data", void 0);
__decorate([
Input()
], PieComponent.prototype, "backgroundColors", void 0);
__decorate([
Input()
], PieComponent.prototype, "radius", void 0);
__decorate([
Input()
], PieComponent.prototype, "options", void 0);
__decorate([
HostListener('window:resize')
], PieComponent.prototype, "onResize", null);
PieComponent = __decorate([
Component({
selector: 'ng-pie',
template: "<div class=\"svg-container\"></div>\n",
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [".svg-container{display:inline-block;position:relative;width:100%;height:100%;padding-bottom:100%;vertical-align:top;overflow:hidden}.svg-content{display:inline-block;position:absolute;top:0;left:0}.grid line{stroke:#d3d3d3;stroke-opacity:.4;shape-rendering:crispEdges}.grid path{stroke-width:0}.grid text{display:none}svg{background-color:transparent!important}.slice text{font-size:16pt;font-family:Arial}"]
})
], PieComponent);
let PieService = class PieService {
constructor() { }
};
PieService = __decorate([
Injectable()
], PieService);
let PieModule = class PieModule {
};
PieModule = __decorate([
NgModule({
declarations: [PieComponent],
imports: [
CommonModule
],
providers: [
PieService,
],
exports: [PieComponent]
})
], PieModule);
// TODO: comment in when issue #96 is done.
/**
* Generated bundle index. Do not edit.
*/
export { BandModule, BarModule, LineModule, MultilineModule, PieModule, BandComponent as ɵa, D3Service as ɵb, BarComponent as ɵc, BarService as ɵd, LineComponent as ɵe, LineService as ɵf, MultilineComponent as ɵg, PieComponent as ɵh, PieService as ɵi };
//# sourceMappingURL=ng-d3-graphs.js.map