|
|
@ -1,48 +1,48 @@ |
|
|
|
import { ChartwerkPod, VueChartwerkPodMixin, TickOrientation, TimeFormat, AxisFormat } from '@chartwerk/core'; |
|
|
|
import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, AxisFormat } from '@chartwerk/core'; |
|
|
|
|
|
|
|
|
|
|
|
import { BarTimeSerie, BarOptions, RowValues } from './types'; |
|
|
|
import { BarConfig } from './models/bar_options'; |
|
|
|
|
|
|
|
import { BarSeries } from './models/bar_series'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { BarSerie, BarOptions, RowValues } from './types'; |
|
|
|
import { findClosest } from './utils'; |
|
|
|
import { findClosest } from './utils'; |
|
|
|
|
|
|
|
|
|
|
|
import * as d3 from 'd3'; |
|
|
|
import * as d3 from 'd3'; |
|
|
|
import * as _ from 'lodash'; |
|
|
|
import * as _ from 'lodash'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DEFAULT_BAR_OPTIONS: BarOptions = { |
|
|
|
export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> { |
|
|
|
renderBarLabels: false, |
|
|
|
|
|
|
|
stacked: false, |
|
|
|
|
|
|
|
matching: false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
|
|
|
|
barYScale: null | d3.ScaleLinear<number, number> = null; |
|
|
|
barYScale: null | d3.ScaleLinear<number, number> = null; |
|
|
|
_seriesDataForRendring = []; |
|
|
|
_seriesDataForRendring = []; |
|
|
|
|
|
|
|
series: BarSeries; |
|
|
|
|
|
|
|
options: BarConfig; |
|
|
|
|
|
|
|
|
|
|
|
constructor(el: HTMLElement, _series: BarTimeSerie[] = [], _options: BarOptions = {}) { |
|
|
|
constructor(el: HTMLElement, _series: BarSerie[] = [], _options: BarOptions = {}) { |
|
|
|
super(el, _series, _options); |
|
|
|
super(el, _series, _options); |
|
|
|
_.defaults(this.options, DEFAULT_BAR_OPTIONS); |
|
|
|
this.series = new BarSeries(_series); |
|
|
|
|
|
|
|
this.options = new BarConfig(_options); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected renderMetrics(): void { |
|
|
|
protected renderMetrics(): void { |
|
|
|
if(this.series.length === 0 || this.series[0].datapoints.length === 0) { |
|
|
|
if(!this.series.isSeriesAvailable) { |
|
|
|
this.renderNoDataPointsMessage(); |
|
|
|
this.renderNoDataPointsMessage(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.setBarPodScales(); |
|
|
|
this.setBarPodScales(); |
|
|
|
this.setSeriesDataForRendering(); |
|
|
|
this.setSeriesDataForRendering(); |
|
|
|
this.renderSerie(this._seriesDataForRendring); |
|
|
|
this.renderSerie(this._seriesDataForRendring); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
get isMatchingDisabled(): boolean { |
|
|
|
get isMatchingDisabled(): boolean { |
|
|
|
return this.options.matching === false || this.seriesUniqKeys.length === 0; |
|
|
|
return this.options.barOptions.matching === false || this.seriesUniqKeys.length === 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setSeriesDataForRendering(): void { |
|
|
|
setSeriesDataForRendering(): void { |
|
|
|
if(this.isMatchingDisabled) { |
|
|
|
if(this.isMatchingDisabled) { |
|
|
|
this._seriesDataForRendring = this.getZippedDataForRender(this.visibleSeries); |
|
|
|
this._seriesDataForRendring = this.getZippedDataForRender(this.series.visibleSeries); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const matchedSeries = this.seriesForMatching.map( |
|
|
|
const matchedSeries = this.seriesForMatching.map( |
|
|
|
(series: BarTimeSerie[], idx: number) => this.getZippedDataForRender(series) |
|
|
|
(series: BarSerie[], idx: number) => this.getZippedDataForRender(series) |
|
|
|
); |
|
|
|
); |
|
|
|
this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries); |
|
|
|
this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries); |
|
|
|
} |
|
|
|
} |
|
|
@ -59,51 +59,63 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
.data(data) |
|
|
|
.data(data) |
|
|
|
.enter().append('g') |
|
|
|
.enter().append('g') |
|
|
|
.attr('class', 'rects-container') |
|
|
|
.attr('class', 'rects-container') |
|
|
|
.each((d: RowValues, i: number, nodes: any) => { |
|
|
|
.each((d: RowValues, rowIndex: number, nodes: any) => { |
|
|
|
const container = d3.select(nodes[i]); |
|
|
|
const container = d3.select(nodes[rowIndex]); |
|
|
|
container.selectAll('rect') |
|
|
|
container.selectAll('rect') |
|
|
|
.data(d.values) |
|
|
|
.data(d.values) |
|
|
|
.enter().append('rect') |
|
|
|
.enter().append('rect') |
|
|
|
.style('fill', (val, i) => d.colors[i]) |
|
|
|
.style('fill', (val, idx) => this.getBarColor(d, val, idx, rowIndex)) |
|
|
|
.attr('opacity', () => this.getBarOpacity(d)) |
|
|
|
.attr('opacity', () => this.getBarOpacity(d)) |
|
|
|
.attr('x', (val: number, idx: number) => { |
|
|
|
.attr('x', (val: number, idx: number) => { |
|
|
|
return this.getBarPositionX(d.key, idx); |
|
|
|
return this.getBarPositionX(d.key, idx); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.attr('y', (val: number, idx: number) => { |
|
|
|
.attr('y', (val: number, idx: number) => { |
|
|
|
return this.getBarPositionY(val, idx, d.values); |
|
|
|
return this.getBarPositionY(val, idx, d.values); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.attr('width', this.barWidth) |
|
|
|
.attr('width', this.barWidth) |
|
|
|
.attr('height', (val: number) => this.getBarHeight(val)) |
|
|
|
.attr('height', (val: number) => this.getBarHeight(val)) |
|
|
|
.on('contextmenu', this.contextMenu.bind(this)); |
|
|
|
.on('contextmenu', this.contextMenu.bind(this)) |
|
|
|
|
|
|
|
.on('mouseover', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mousemove', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mouseout', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mousedown', () => { d3.event.stopPropagation(); }); |
|
|
|
|
|
|
|
|
|
|
|
// render bar annotations, its all hardcoded
|
|
|
|
// render bar annotations, its all hardcoded
|
|
|
|
if(_.isEmpty(this.options.annotations)) { |
|
|
|
if(_.isEmpty(this.options.barOptions.annotations)) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
// find all series for single matchedKey
|
|
|
|
// find all series for single matchedKey
|
|
|
|
const series = _.filter(this.series, serie => _.includes(d.serieTarget, serie.target)); |
|
|
|
const series = _.filter(this.series.visibleSeries, serie => _.includes(d.serieTarget, serie.target)); |
|
|
|
const matchedKeys = _.map(series, serie => serie.matchedKey); // here matchedKeys should be equal
|
|
|
|
const matchedKeys = _.map(series, serie => serie.matchedKey); // here matchedKeys should be equal
|
|
|
|
const key = matchedKeys[0]; |
|
|
|
const key = matchedKeys[0]; |
|
|
|
|
|
|
|
|
|
|
|
const lastRect = _.last(container.selectAll('rect')?.nodes()); |
|
|
|
const lastRect = _.last(container.selectAll('rect')?.nodes()); |
|
|
|
const annotation = _.find(this.options.annotations, a => a.key === key); |
|
|
|
const annotation = _.find(this.options.barOptions.annotations, a => a.key === key); |
|
|
|
if(!lastRect || !key || !annotation) { |
|
|
|
if(!lastRect || !key || !annotation) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const rectSelection = d3.select(lastRect); |
|
|
|
const rectSelection = d3.select(lastRect); |
|
|
|
// render triangle
|
|
|
|
// render triangle
|
|
|
|
container.append('path') |
|
|
|
container.append('path') |
|
|
|
.attr('d', () => { |
|
|
|
.attr('d', () => { |
|
|
|
const x = Math.ceil(_.toNumber(rectSelection.attr('x'))); |
|
|
|
const x = Math.ceil(_.toNumber(rectSelection.attr('x'))); |
|
|
|
const y = Math.ceil(_.toNumber(rectSelection.attr('y'))); |
|
|
|
const y = Math.ceil(_.toNumber(rectSelection.attr('y'))); |
|
|
|
const options = { max: this.options.maxAnnotationSize, min: this.options.minAnnotationSize }; |
|
|
|
const options = { max: this.options.barOptions.maxAnnotationSize, min: this.options.barOptions.minAnnotationSize }; |
|
|
|
return this.getTrianglePath(x, y, this.barWidth, options); |
|
|
|
return this.getTrianglePath(x, y, this.barWidth, options); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.attr('fill', annotation.color); |
|
|
|
.attr('fill', annotation.color) |
|
|
|
|
|
|
|
.on('mouseover', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mousemove', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mouseout', this.redirectEventToOverlay.bind(this)) |
|
|
|
|
|
|
|
.on('mousedown', () => { d3.event.stopPropagation(); }); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
redirectEventToOverlay(): void { |
|
|
|
|
|
|
|
this.overlay?.node().dispatchEvent(new MouseEvent(d3.event.type, d3.event)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getTrianglePath(x: number, y: number, length: number, options?: { max: number, min: number }): string { |
|
|
|
getTrianglePath(x: number, y: number, length: number, options?: { max: number, min: number }): string { |
|
|
|
// (x, y) - top left corner of bar
|
|
|
|
// (x, y) - top left corner of bar
|
|
|
|
const minTriangleSize = options?.min || 6; |
|
|
|
const minTriangleSize = options?.min || 6; |
|
|
@ -111,8 +123,8 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
const yOffset = 4; // offset between triangle and bar
|
|
|
|
const yOffset = 4; // offset between triangle and bar
|
|
|
|
const centerX = x + length / 2; |
|
|
|
const centerX = x + length / 2; |
|
|
|
const correctedLength = _.clamp(length, minTriangleSize, maxTriagleSize); |
|
|
|
const correctedLength = _.clamp(length, minTriangleSize, maxTriagleSize); |
|
|
|
|
|
|
|
|
|
|
|
const topY = Math.max(y - correctedLength - yOffset, 4);
|
|
|
|
const topY = Math.max(y - correctedLength - yOffset, 4); |
|
|
|
const topLeftCorner = { |
|
|
|
const topLeftCorner = { |
|
|
|
x: centerX - correctedLength / 2, |
|
|
|
x: centerX - correctedLength / 2, |
|
|
|
y: topY, |
|
|
|
y: topY, |
|
|
@ -132,10 +144,17 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getBarOpacity(rowValues: RowValues): number { |
|
|
|
getBarOpacity(rowValues: RowValues): number { |
|
|
|
if(this.options.opacityFormatter === undefined) { |
|
|
|
if(this.options.barOptions.opacityFormatter === undefined) { |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
return this.options.opacityFormatter(rowValues); |
|
|
|
return this.options.barOptions.opacityFormatter(rowValues); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getBarColor(rowValues: RowValues, val: number, i: number, rowIndex: number): string { |
|
|
|
|
|
|
|
if(_.isFunction(rowValues.colors[i])) { |
|
|
|
|
|
|
|
return (rowValues.colors[i] as Function)({ rowData: rowValues, val, stackedIndex: i, rowIndex }); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return (rowValues.colors[i] as string); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mergeMacthedSeriesAndSort(matchedSeries: any[]) { |
|
|
|
mergeMacthedSeriesAndSort(matchedSeries: any[]) { |
|
|
@ -155,27 +174,27 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
get seriesUniqKeys(): string[] { |
|
|
|
get seriesUniqKeys(): string[] { |
|
|
|
if(this.visibleSeries.length === 0) { |
|
|
|
if(!this.series.isSeriesAvailable) { |
|
|
|
return []; |
|
|
|
return []; |
|
|
|
} |
|
|
|
} |
|
|
|
const keys = this.visibleSeries.map(serie => serie.matchedKey); |
|
|
|
const keys = this.series.visibleSeries.map(serie => serie.matchedKey); |
|
|
|
const uniqKeys = _.uniq(keys); |
|
|
|
const uniqKeys = _.uniq(keys); |
|
|
|
const filteredKeys = _.filter(uniqKeys, key => key !== undefined); |
|
|
|
const filteredKeys = _.filter(uniqKeys, key => key !== undefined); |
|
|
|
return filteredKeys; |
|
|
|
return filteredKeys; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
get seriesForMatching(): BarTimeSerie[][] { |
|
|
|
get seriesForMatching(): BarSerie[][] { |
|
|
|
if(this.seriesUniqKeys.length === 0) { |
|
|
|
if(this.seriesUniqKeys.length === 0) { |
|
|
|
return [this.visibleSeries]; |
|
|
|
return [this.series.visibleSeries]; |
|
|
|
} |
|
|
|
} |
|
|
|
const seriesList = this.seriesUniqKeys.map(key => { |
|
|
|
const seriesList = this.seriesUniqKeys.map(key => { |
|
|
|
const seriesWithKey = _.filter(this.visibleSeries, serie => serie.matchedKey === key); |
|
|
|
const seriesWithKey = _.filter(this.series.visibleSeries, serie => serie.matchedKey === key); |
|
|
|
return seriesWithKey; |
|
|
|
return seriesWithKey; |
|
|
|
}); |
|
|
|
}); |
|
|
|
return seriesList; |
|
|
|
return seriesList; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getZippedDataForRender(series: BarTimeSerie[]): RowValues[] { |
|
|
|
getZippedDataForRender(series: BarSerie[]): RowValues[] { |
|
|
|
if(series.length === 0) { |
|
|
|
if(series.length === 0) { |
|
|
|
throw new Error('There is no visible series'); |
|
|
|
throw new Error('There is no visible series'); |
|
|
|
} |
|
|
|
} |
|
|
@ -185,7 +204,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
const additionalValuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[2] !== undefined ? row[2] : null)); |
|
|
|
const additionalValuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[2] !== undefined ? row[2] : null)); |
|
|
|
const zippedAdditionalValuesColumn = _.zip(...additionalValuesColumns); |
|
|
|
const zippedAdditionalValuesColumn = _.zip(...additionalValuesColumns); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
|
const colors = _.map(series, serie => this.getBarColor(serie)); |
|
|
|
const colors = _.map(series, serie => serie.colorFormatter || serie.color); |
|
|
|
const tagrets = _.map(series, serie => serie.target); |
|
|
|
const tagrets = _.map(series, serie => serie.target); |
|
|
|
const zippedData = _.zip(keysColumn, zippedValuesColumn, zippedAdditionalValuesColumn, tagrets); |
|
|
|
const zippedData = _.zip(keysColumn, zippedValuesColumn, zippedAdditionalValuesColumn, tagrets); |
|
|
|
const data = _.map(zippedData, row => { return { key: row[0], values: row[1], additionalValues: row[2], colors, serieTarget: tagrets } }); |
|
|
|
const data = _.map(zippedData, row => { return { key: row[0], values: row[1], additionalValues: row[2], colors, serieTarget: tagrets } }); |
|
|
@ -195,7 +214,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
public renderSharedCrosshair(values: { x?: number, y?: number }): void { |
|
|
|
public renderSharedCrosshair(values: { x?: number, y?: number }): void { |
|
|
|
this.crosshair.style('display', null); |
|
|
|
this.crosshair.style('display', null); |
|
|
|
|
|
|
|
|
|
|
|
const x = this.xScale(values.x); |
|
|
|
const x = this.state.xScale(values.x); |
|
|
|
this.crosshair.select('#crosshair-line-x') |
|
|
|
this.crosshair.select('#crosshair-line-x') |
|
|
|
.attr('x1', x) |
|
|
|
.attr('x1', x) |
|
|
|
.attr('x2', x); |
|
|
|
.attr('x2', x); |
|
|
@ -209,59 +228,40 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
// TODO: mouse move work bad with matching
|
|
|
|
// TODO: mouse move work bad with matching
|
|
|
|
const event = d3.mouse(this.chartContainer.node()); |
|
|
|
const event = d3.mouse(this.chartContainer.node()); |
|
|
|
const eventX = event[0]; |
|
|
|
const eventX = event[0]; |
|
|
|
if(this.isOutOfChart() === true) { |
|
|
|
|
|
|
|
this.crosshair.style('display', 'none'); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.crosshair.select('#crosshair-line-x') |
|
|
|
this.crosshair.select('#crosshair-line-x') |
|
|
|
.attr('x1', eventX) |
|
|
|
.attr('x1', eventX) |
|
|
|
.attr('x2', eventX); |
|
|
|
.attr('x2', eventX); |
|
|
|
|
|
|
|
|
|
|
|
const series = this.getSeriesPointFromMousePosition(eventX); |
|
|
|
const series = this.getSeriesPointFromMousePosition(eventX); |
|
|
|
|
|
|
|
|
|
|
|
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseMove !== undefined) { |
|
|
|
this.options.callbackMouseMove({ |
|
|
|
this.options.eventsCallbacks.mouseMove({ |
|
|
|
x: d3.event.pageX, |
|
|
|
x: d3.event.pageX, |
|
|
|
y: d3.event.pageY, |
|
|
|
y: d3.event.pageY, |
|
|
|
time: this.state.xScale.invert(eventX), |
|
|
|
time: this.xScale.invert(eventX), |
|
|
|
series, |
|
|
|
series, |
|
|
|
chartX: eventX, |
|
|
|
chartX: eventX, |
|
|
|
chartWidth: this.width |
|
|
|
chartWidth: this.width |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log('mouse move, but there is no callback'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getSeriesPointFromMousePosition(eventX: number): any[] | undefined { |
|
|
|
getSeriesPointFromMousePosition(eventX: number): any[] | undefined { |
|
|
|
if(this.series === undefined || this.series.length === 0) { |
|
|
|
if(!this.series.isSeriesAvailable) { |
|
|
|
return undefined; |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
} |
|
|
|
const mousePoisitionKey = Math.ceil(this.xScale.invert(eventX)); |
|
|
|
const mousePoisitionKey = Math.ceil(this.state.xScale.invert(eventX)); |
|
|
|
const keys = _.map(this._seriesDataForRendring, el => el.key); |
|
|
|
const keys = _.map(this._seriesDataForRendring, el => el.key); |
|
|
|
const idx = findClosest(keys, mousePoisitionKey); |
|
|
|
const idx = findClosest(keys, mousePoisitionKey); |
|
|
|
|
|
|
|
|
|
|
|
return this._seriesDataForRendring[idx]; |
|
|
|
return this._seriesDataForRendring[idx]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getBarColor(serie: any) { |
|
|
|
|
|
|
|
if(serie.color === undefined) { |
|
|
|
|
|
|
|
return this.getSerieColor(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return serie.color; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onMouseOver(): void { |
|
|
|
onMouseOver(): void { |
|
|
|
this.crosshair.style('display', null); |
|
|
|
this.crosshair.style('display', null); |
|
|
|
this.crosshair.raise(); |
|
|
|
this.crosshair.raise(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
onMouseOut(): void { |
|
|
|
onMouseOut(): void { |
|
|
|
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseOut !== undefined) { |
|
|
|
this.options.callbackMouseOut(); |
|
|
|
this.options.eventsCallbacks.mouseOut(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log('mouse out, but there is no callback'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.crosshair.style('display', 'none'); |
|
|
|
this.crosshair.style('display', 'none'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -272,30 +272,26 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
const event = d3.mouse(this.chartContainer.node()); |
|
|
|
const event = d3.mouse(this.chartContainer.node()); |
|
|
|
const eventX = event[0]; |
|
|
|
const eventX = event[0]; |
|
|
|
const series = this.getSeriesPointFromMousePosition(eventX); |
|
|
|
const series = this.getSeriesPointFromMousePosition(eventX); |
|
|
|
|
|
|
|
this.options.callbackContextMenu({ |
|
|
|
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.contextMenu !== undefined) { |
|
|
|
pageX: d3.event.pageX, |
|
|
|
this.options.eventsCallbacks.contextMenu({ |
|
|
|
pageY: d3.event.pageY, |
|
|
|
x: d3.event.pageX, |
|
|
|
xVal: this.state.xScale.invert(eventX), |
|
|
|
y: d3.event.pageY, |
|
|
|
series, |
|
|
|
time: this.xScale.invert(eventX), |
|
|
|
chartX: eventX |
|
|
|
series, |
|
|
|
}); |
|
|
|
chartX: eventX |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log('contextmenu, but there is no callback'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
get barWidth(): number { |
|
|
|
get barWidth(): number { |
|
|
|
// TODO: here we use first value + timeInterval as bar width. It is not a good idea
|
|
|
|
const xAxisStartValue = _.first(this.series.visibleSeries[0].datapoints)[0]; |
|
|
|
const xAxisStartValue = _.first(this.series[0].datapoints)[0]; |
|
|
|
const xAxisEndValue = _.last(this.series.visibleSeries[0].datapoints)[0]; |
|
|
|
let width = this.xScale(xAxisStartValue + this.timeInterval) / 2; |
|
|
|
const timeInterval = (xAxisEndValue - xAxisStartValue) / this.series.visibleSeries[0].datapoints.length; |
|
|
|
if(this.options.barWidth !== undefined) { |
|
|
|
let width = this.state.xScale(timeInterval) / 2; |
|
|
|
|
|
|
|
if(this.options.barOptions.barWidth !== undefined) { |
|
|
|
// barWidth now has axis-x dimension
|
|
|
|
// barWidth now has axis-x dimension
|
|
|
|
width = this.xScale(this.state.getMinValueX() + this.options.barWidth); |
|
|
|
width = this.state.xScale(this.state.getMinValueX() + this.options.barOptions.barWidth); |
|
|
|
} |
|
|
|
} |
|
|
|
let rectColumns = this.visibleSeries.length; |
|
|
|
let rectColumns = this.series.visibleSeries.length; |
|
|
|
if(this.options.stacked === true) { |
|
|
|
if(this.options.barOptions.stacked === true) { |
|
|
|
rectColumns = 1; |
|
|
|
rectColumns = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
return this.updateBarWidthWithBorders(width / rectColumns); |
|
|
|
return this.updateBarWidthWithBorders(width / rectColumns); |
|
|
@ -303,11 +299,11 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
|
|
|
|
|
|
|
|
updateBarWidthWithBorders(width: number): number { |
|
|
|
updateBarWidthWithBorders(width: number): number { |
|
|
|
let barWidth = width; |
|
|
|
let barWidth = width; |
|
|
|
if(this.options.minBarWidth !== undefined) { |
|
|
|
if(this.options.barOptions.minBarWidth !== undefined) { |
|
|
|
barWidth = Math.max(barWidth, this.options.minBarWidth); |
|
|
|
barWidth = Math.max(barWidth, this.options.barOptions.minBarWidth); |
|
|
|
} |
|
|
|
} |
|
|
|
if(this.options.maxBarWidth !== undefined) { |
|
|
|
if(this.options.barOptions.maxBarWidth !== undefined) { |
|
|
|
barWidth = Math.min(barWidth, this.options.maxBarWidth); |
|
|
|
barWidth = Math.min(barWidth, this.options.barOptions.maxBarWidth); |
|
|
|
} |
|
|
|
} |
|
|
|
return barWidth; |
|
|
|
return barWidth; |
|
|
|
} |
|
|
|
} |
|
|
@ -320,8 +316,8 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getBarPositionX(key: number, idx: number): number { |
|
|
|
getBarPositionX(key: number, idx: number): number { |
|
|
|
let xPosition: number = this.xScale(key); |
|
|
|
let xPosition: number = this.state.xScale(key); |
|
|
|
if(this.options.stacked === false) { |
|
|
|
if(this.options.barOptions.stacked === false) { |
|
|
|
xPosition += idx * this.barWidth; |
|
|
|
xPosition += idx * this.barWidth; |
|
|
|
} |
|
|
|
} |
|
|
|
return xPosition; |
|
|
|
return xPosition; |
|
|
@ -329,7 +325,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
|
|
|
|
|
|
|
|
getBarPositionY(val: number, idx: number, values: number[]): number { |
|
|
|
getBarPositionY(val: number, idx: number, values: number[]): number { |
|
|
|
let yPosition: number = this.barYScale(Math.max(val, 0)); |
|
|
|
let yPosition: number = this.barYScale(Math.max(val, 0)); |
|
|
|
if(this.options.stacked === true) { |
|
|
|
if(this.options.barOptions.stacked === true) { |
|
|
|
const previousBarsHeight = _.sum( |
|
|
|
const previousBarsHeight = _.sum( |
|
|
|
_.map(_.range(idx), i => this.getBarHeight(values[i])) |
|
|
|
_.map(_.range(idx), i => this.getBarHeight(values[i])) |
|
|
|
); |
|
|
|
); |
|
|
@ -354,15 +350,15 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getYMaxValue(): number | undefined { |
|
|
|
getYMaxValue(): number | undefined { |
|
|
|
if(this.series === undefined || this.series.length === 0 || this.series[0].datapoints.length === 0) { |
|
|
|
if(!this.series.isSeriesAvailable) { |
|
|
|
return undefined; |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
} |
|
|
|
if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) { |
|
|
|
if(this.options.axis.y.range) { |
|
|
|
return _.max(this.options.axis.y.range); |
|
|
|
return _.max(this.options.axis.y.range); |
|
|
|
} |
|
|
|
} |
|
|
|
let maxValue: number; |
|
|
|
let maxValue: number; |
|
|
|
if(this.options.stacked === true) { |
|
|
|
if(this.options.barOptions.stacked === true) { |
|
|
|
if(this.options.matching === true && this.seriesUniqKeys.length > 0) { |
|
|
|
if(this.options.barOptions.matching === true && this.seriesUniqKeys.length > 0) { |
|
|
|
const maxValues = this.seriesForMatching.map(series => { |
|
|
|
const maxValues = this.seriesForMatching.map(series => { |
|
|
|
const valuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[1])); |
|
|
|
const valuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[1])); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
@ -370,14 +366,13 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
}); |
|
|
|
}); |
|
|
|
return _.max(maxValues); |
|
|
|
return _.max(maxValues); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const valuesColumns = _.map(this.visibleSeries, serie => _.map(serie.datapoints, row => row[1])); |
|
|
|
const valuesColumns = _.map(this.series.visibleSeries, serie => _.map(serie.datapoints, row => row[1])); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
|
const zippedValuesColumn = _.zip(...valuesColumns); |
|
|
|
maxValue = _.max(_.map(zippedValuesColumn, row => _.sum(row)));
|
|
|
|
maxValue = _.max(_.map(zippedValuesColumn, row => _.sum(row))); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
console.log('else') |
|
|
|
|
|
|
|
maxValue = _.max( |
|
|
|
maxValue = _.max( |
|
|
|
this.visibleSeries.map( |
|
|
|
this.series.visibleSeries.map( |
|
|
|
serie => _.maxBy<number[]>(serie.datapoints, dp => dp[1])[0] |
|
|
|
serie => _.maxBy<number[]>(serie.datapoints, dp => dp[1])[0] |
|
|
|
) |
|
|
|
) |
|
|
|
); |
|
|
|
); |
|
|
@ -409,4 +404,4 @@ export const VueChartwerkBarChartObject = { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export { BarTimeSerie, BarOptions, TickOrientation, TimeFormat, AxisFormat }; |
|
|
|
export { BarSerie, BarOptions, TimeFormat, AxisFormat }; |
|
|
|