You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.1 KiB
167 lines
5.1 KiB
import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, AxisFormat } from '@chartwerk/core'; |
|
|
|
import { BarConfig } from './models/bar_options'; |
|
import { BarSeries } from './models/bar_series'; |
|
import { BarAnnotation } from './models/bar_annotation'; |
|
import { DataProcessor, DataRow, DataItem } from './models/data_processor'; |
|
import { BarGroup } from './models/bar_group'; |
|
import { setBarScaleY, setBarScaleX } from './models/bar_state'; |
|
|
|
import { |
|
BarSerie, BarOptions, DiscreteConfig, NonDiscreteConfig, |
|
BarPodType, SizeType, CallbackEvent, Color, Opacity, |
|
BarData, ColorFormatter, OpacityFormatter, |
|
Datapoint, ValueX, ValueY, |
|
} from './types'; |
|
|
|
import { findClosest } from './utils'; |
|
|
|
import * as d3 from 'd3'; |
|
import * as _ from 'lodash'; |
|
|
|
export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> { |
|
barYScale: null | d3.ScaleLinear<number, number> = null; |
|
dataProcessor: DataProcessor; |
|
series: BarSeries; |
|
options: BarConfig; |
|
|
|
constructor(el: HTMLElement, _series: BarSerie[] = [], _options: BarOptions = {}) { |
|
super(el, _series, _options); |
|
this.series = new BarSeries(_series); |
|
this.options = new BarConfig(_options); |
|
this.dataProcessor = new DataProcessor(this.series.visibleSeries, this.options); |
|
|
|
setBarScaleY(this.state, this.dataProcessor.dataRows, this.options); |
|
setBarScaleX(this.state, this.dataProcessor.dataRows, this.options); |
|
} |
|
|
|
protected renderMetrics(): void { |
|
if(!this.series.isSeriesAvailable) { |
|
this.renderNoDataPointsMessage(); |
|
return; |
|
} |
|
this.dataProcessor.dataRows.forEach((dataRow: DataRow) => { |
|
const barGroup = new BarGroup( |
|
this.overlay, this.metricContainer, |
|
dataRow, this.options, this.state, |
|
{ width: this.width, height: this.height, zeroHorizon: this.state.yScale(0) }, |
|
this.dataProcessor.dataRows.length |
|
); |
|
this.renderAnnotationForGroup(barGroup, dataRow); |
|
}); |
|
} |
|
|
|
renderAnnotationForGroup(barGroup: BarGroup, dataRow: DataRow): void { |
|
if(this.options.barType === BarPodType.DISCRETE) { |
|
return; |
|
} |
|
const target = _.first(dataRow.items).target; |
|
const serie = this.series.getSerieByTarget(target); |
|
if(!serie.annotation?.enable) { |
|
return; |
|
} |
|
const annotationOptions = { |
|
size: barGroup.getGroupWidth(), |
|
max: serie.annotation.size.max, |
|
min: serie.annotation.size.min, |
|
color: serie.annotation.color, |
|
}; |
|
new BarAnnotation(this.overlay, barGroup.getGroupContainer(), annotationOptions); |
|
} |
|
|
|
public renderSharedCrosshair(values: { x?: number, y?: number }): void { |
|
this.crosshair.style('display', null); |
|
|
|
const x = this.state.xScale(values.x); |
|
this.crosshair.select('#crosshair-line-x') |
|
.attr('x1', x) |
|
.attr('x2', x); |
|
} |
|
|
|
public hideSharedCrosshair(): void { |
|
this.crosshair.style('display', 'none'); |
|
} |
|
|
|
onMouseMove(): void { |
|
// TODO: mouse move work bad with matching |
|
const event = d3.mouse(this.chartContainer.node()); |
|
const eventX = event[0]; |
|
this.crosshair.select('#crosshair-line-x') |
|
.attr('x1', eventX) |
|
.attr('x2', eventX); |
|
const dataRow = this.getDataRowFromMousePosition(eventX); |
|
|
|
this.options.callbackMouseMove({ |
|
position: { |
|
eventX: event[0], |
|
eventY: event[1], |
|
pageX: d3.event.pageX, |
|
pageY: d3.event.pageY, |
|
valueX: this.state.xScale.invert(event[0]), |
|
valueY: this.state.yScale.invert(event[1]), |
|
}, |
|
data: dataRow, |
|
}); |
|
} |
|
|
|
getDataRowFromMousePosition(eventX: number): DataRow | undefined { |
|
if(!this.series.isSeriesAvailable) { |
|
return undefined; |
|
} |
|
const mousePositionKey = this.state.xScale.invert(eventX); |
|
const keys = _.map(this.dataProcessor.dataRows, (dataRow: DataRow) => dataRow.key); |
|
let idx: number; |
|
switch(this.options.barType) { |
|
case BarPodType.DISCRETE: |
|
idx = Math.floor(mousePositionKey); |
|
break; |
|
case BarPodType.NON_DISCRETE: |
|
idx = findClosest(keys, mousePositionKey); |
|
break; |
|
default: |
|
break; |
|
} |
|
return this.dataProcessor.dataRows[idx]; |
|
} |
|
|
|
onMouseOver(): void { |
|
this.crosshair.style('display', null); |
|
this.crosshair.raise(); |
|
} |
|
|
|
onMouseOut(): void { |
|
this.options.callbackMouseOut(); |
|
this.crosshair.style('display', 'none'); |
|
} |
|
} |
|
|
|
// it is used with Vue.component, e.g.: Vue.component('chartwerk-bar-chart', VueChartwerkBarChartObject) |
|
export const VueChartwerkBarChartObject = { |
|
// alternative to `template: '<div class="chartwerk-bar-chart" :id="id" />'` |
|
render(createElement) { |
|
return createElement( |
|
'div', |
|
{ |
|
class: { 'chartwerk-bar-chart': true }, |
|
attrs: { id: this.id } |
|
} |
|
) |
|
}, |
|
mixins: [VueChartwerkPodMixin], |
|
methods: { |
|
render() { |
|
console.time('bar-render'); |
|
const pod = new ChartwerkBarPod(document.getElementById(this.id), this.series, this.options); |
|
pod.render(); |
|
console.timeEnd('bar-render'); |
|
} |
|
} |
|
}; |
|
|
|
export { |
|
BarSerie, BarOptions, TimeFormat, AxisFormat, |
|
DiscreteConfig, NonDiscreteConfig, |
|
BarPodType, SizeType, CallbackEvent, Color, Opacity, |
|
BarData, ColorFormatter, OpacityFormatter, |
|
Datapoint, ValueX, ValueY, DataItem, DataRow, |
|
};
|
|
|