|
|
@ -1,6 +1,7 @@ |
|
|
|
import { ChartwerkPod, VueChartwerkPodMixin, TickOrientation, TimeFormat, AxisFormat } from '@chartwerk/core'; |
|
|
|
import { ChartwerkPod, VueChartwerkPodMixin, TickOrientation, TimeFormat, AxisFormat } from '@chartwerk/core'; |
|
|
|
|
|
|
|
|
|
|
|
import { BarTimeSerie, BarOptions, RowValues } from './types'; |
|
|
|
import { BarTimeSerie, BarOptions, RowValues } from './types'; |
|
|
|
|
|
|
|
import { findClosest } from './utils'; |
|
|
|
|
|
|
|
|
|
|
|
import * as d3 from 'd3'; |
|
|
|
import * as d3 from 'd3'; |
|
|
|
import * as _ from 'lodash'; |
|
|
|
import * as _ from 'lodash'; |
|
|
@ -14,6 +15,7 @@ const DEFAULT_BAR_OPTIONS: BarOptions = { |
|
|
|
|
|
|
|
|
|
|
|
export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
barYScale: null | d3.ScaleLinear<number, number> = null; |
|
|
|
barYScale: null | d3.ScaleLinear<number, number> = null; |
|
|
|
|
|
|
|
_seriesDataForRendring = []; |
|
|
|
|
|
|
|
|
|
|
|
constructor(el: HTMLElement, _series: BarTimeSerie[] = [], _options: BarOptions = {}) { |
|
|
|
constructor(el: HTMLElement, _series: BarTimeSerie[] = [], _options: BarOptions = {}) { |
|
|
|
super(d3, el, _series, _options); |
|
|
|
super(d3, el, _series, _options); |
|
|
@ -27,17 +29,23 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.setBarPodScales(); |
|
|
|
this.setBarPodScales(); |
|
|
|
|
|
|
|
this.setSeriesDataForRendering(); |
|
|
|
|
|
|
|
this.renderSerie(this._seriesDataForRendring); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(this.options.matching === false || this.seriesUniqKeys.length === 0) { |
|
|
|
get isMatchingDisabled(): boolean { |
|
|
|
const zippedData = this.getZippedDataForRender(this.visibleSeries); |
|
|
|
return this.options.matching === false || this.seriesUniqKeys.length === 0; |
|
|
|
this.renderSerie(zippedData); |
|
|
|
} |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
setSeriesDataForRendering(): void { |
|
|
|
|
|
|
|
if(this.isMatchingDisabled) { |
|
|
|
|
|
|
|
this._seriesDataForRendring = this.getZippedDataForRender(this.visibleSeries); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
const matchedSeries = this.seriesForMatching.map( |
|
|
|
|
|
|
|
(series: BarTimeSerie[], idx: number) => this.getZippedDataForRender(series) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries); |
|
|
|
} |
|
|
|
} |
|
|
|
const matchedSeries = this.seriesForMatching.map((series: BarTimeSerie[], idx: number) => { |
|
|
|
|
|
|
|
return this.getZippedDataForRender(series); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
const concatedSeries = this.mergeMacthedSeriesAndSort(matchedSeries); |
|
|
|
|
|
|
|
this.renderSerie(concatedSeries); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setBarPodScales(): void { |
|
|
|
setBarPodScales(): void { |
|
|
@ -89,16 +97,17 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
.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'))); |
|
|
|
return this.getTrianglePath1(x, y, this.barWidth); |
|
|
|
const options = { max: this.options.maxAnnotationSize, min: this.options.minAnnotationSize }; |
|
|
|
|
|
|
|
return this.getTrianglePath(x, y, this.barWidth, options); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.attr('fill', annotation.color); |
|
|
|
.attr('fill', annotation.color); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getTrianglePath1(x: number, y: number, length: 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 = 6; |
|
|
|
const minTriangleSize = options?.min || 6; |
|
|
|
const maxTriagleSize = 10; |
|
|
|
const maxTriagleSize = options?.max || 10; |
|
|
|
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); |
|
|
@ -122,36 +131,6 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
L ${bottomMiddleCorner.x} ${bottomMiddleCorner.y} z`;
|
|
|
|
L ${bottomMiddleCorner.x} ${bottomMiddleCorner.y} z`;
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getTrianglePath(x: number, y: number, length: number): string { |
|
|
|
|
|
|
|
// (x, y) - top left corner
|
|
|
|
|
|
|
|
const Y_OFFSET = 2; |
|
|
|
|
|
|
|
const correctedY = Math.max(y, Y_OFFSET); |
|
|
|
|
|
|
|
const correctedLength = length <= 10 ? length : 10; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let x1 = x - Y_OFFSET; |
|
|
|
|
|
|
|
let x2 = x + length + Y_OFFSET; |
|
|
|
|
|
|
|
let x1x2Len = x2 - x1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let y1 = correctedY - Y_OFFSET; |
|
|
|
|
|
|
|
let yM = correctedY + length - Y_OFFSET; |
|
|
|
|
|
|
|
if (x1x2Len > correctedLength) { |
|
|
|
|
|
|
|
const xDiff = x1x2Len - correctedLength; |
|
|
|
|
|
|
|
x1 += xDiff / 2; |
|
|
|
|
|
|
|
x2 -= xDiff / 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const yDiff = yM - y1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (xDiff < yDiff) { |
|
|
|
|
|
|
|
y1 += (yDiff - xDiff) + Y_OFFSET*3; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const x3 = x + length / 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return `M ${x1} ${y1} |
|
|
|
|
|
|
|
L ${x2} ${y1} |
|
|
|
|
|
|
|
L ${x3} ${yM} z`;
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getBarOpacity(rowValues: RowValues): number { |
|
|
|
getBarOpacity(rowValues: RowValues): number { |
|
|
|
if(this.options.opacityFormatter === undefined) { |
|
|
|
if(this.options.opacityFormatter === undefined) { |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
@ -254,29 +233,15 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TODO: not any[]
|
|
|
|
|
|
|
|
getSeriesPointFromMousePosition(eventX: number): any[] | undefined { |
|
|
|
getSeriesPointFromMousePosition(eventX: number): any[] | undefined { |
|
|
|
if(this.series === undefined || this.series.length === 0) { |
|
|
|
if(this.series === undefined || this.series.length === 0) { |
|
|
|
return undefined; |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
} |
|
|
|
const mouseDate = Math.ceil(this.xScale.invert(eventX)); |
|
|
|
const mousePoisitionKey = Math.ceil(this.xScale.invert(eventX)); |
|
|
|
|
|
|
|
const keys = _.map(this._seriesDataForRendring, el => el.key); |
|
|
|
const zippedData = this.getZippedDataForRender(this.visibleSeries); |
|
|
|
const idx = findClosest(keys, mousePoisitionKey); |
|
|
|
// get the nearest RowValues element for mouseDate
|
|
|
|
|
|
|
|
const keys = _.map(zippedData, 'key').sort((a, b) => (a - mouseDate) - (mouseDate - b)); |
|
|
|
|
|
|
|
const idx = _.findIndex(zippedData, (a) => a.key == keys[0]); |
|
|
|
|
|
|
|
const series: any[] = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(let i = 0; i < zippedData[idx].values.length; i++) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
series.push({ |
|
|
|
return this._seriesDataForRendring[idx]; |
|
|
|
value: zippedData[idx].key, |
|
|
|
|
|
|
|
xval: zippedData[idx].values[i], |
|
|
|
|
|
|
|
color: zippedData[idx].colors[i], |
|
|
|
|
|
|
|
label: zippedData[idx].serieTarget[i] |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return series; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getBarColor(serie: any) { |
|
|
|
getBarColor(serie: any) { |
|
|
|