Compare commits

..

2 Commits

Author SHA1 Message Date
vargburz 43ecf4ae78 init models 3 years ago
vargburz 50f9b43dfc try to use new core 3 years ago
  1. 17
      examples/demo.html
  2. 17
      package.json
  3. 223
      src/index.ts
  4. 55
      src/models/bar_options.ts
  5. 26
      src/models/bar_series.ts
  6. 29
      src/types.ts
  7. 5130
      yarn.lock

17
examples/demo.html

@ -13,10 +13,10 @@
var pod = new ChartwerkBarPod( var pod = new ChartwerkBarPod(
document.getElementById('chart'), document.getElementById('chart'),
[ [
{ target: 'test11', datapoints: getData(), matchedKey: 'm-1', color: 'red', colorFormatter: (data) => ['green', 'yellow'][data.rowIndex] }, { target: 'test11', datapoints: getData(), matchedKey: 'm-1', color: 'red' },
// { target: 'test12', datapoints: [[100, 10], [200, 20], [300, 10]], matchedKey: 'm-1', color: 'green' }, { target: 'test12', datapoints: [[100, 10], [200, 20], [300, 10]], matchedKey: 'm-1', color: 'green' },
// { target: 'test21', datapoints: [[130, 10], [230, 26], [330, 15]], matchedKey: 'm-2', color: 'yellow'}, { target: 'test21', datapoints: [[130, 10], [230, 26], [330, 15]], matchedKey: 'm-2', color: 'yellow'},
// { target: 'test22', datapoints: [[130, 10], [230, 27], [330, 10]], matchedKey: 'm-2', color: 'blue' }, { target: 'test22', datapoints: [[130, 10], [230, 27], [330, 10]], matchedKey: 'm-2', color: 'blue' },
], ],
{ {
usePanning: false, usePanning: false,
@ -24,8 +24,8 @@
x: { format: 'custom', invert: false, valueFormatter: (value) => { return 'L' + value; } }, x: { format: 'custom', invert: false, valueFormatter: (value) => { return 'L' + value; } },
y: { format: 'custom', invert: false, range: [0, 30], valueFormatter: (value) => { return value + '%'; } } y: { format: 'custom', invert: false, range: [0, 30], valueFormatter: (value) => { return value + '%'; } }
}, },
stacked: false, stacked: true,
matching: false, matching: true,
maxBarWidth: 20, maxBarWidth: 20,
minBarWidth: 4, minBarWidth: 4,
zoomEvents: { zoomEvents: {
@ -37,8 +37,7 @@
], ],
eventsCallbacks: { eventsCallbacks: {
zoomIn: (range) => { console.log('range', range) } zoomIn: (range) => { console.log('range', range) }
}, }
renderLegend: false,
} }
); );
console.time('render'); console.time('render');
@ -47,7 +46,7 @@
function getData() { function getData() {
return [ return [
[100, 15], [200, 20], [300, 10], [100, 15], [110, 20], [300, 10],
]; ];
} }
</script> </script>

17
package.json

@ -1,11 +1,8 @@
{ {
"name": "@chartwerk/bar-pod", "name": "@chartwerk/bar-pod",
"version": "0.8.1", "version": "0.5.1",
"description": "Chartwerk bar pod", "description": "Chartwerk bar pod",
"main": "dist/index.js", "main": "dist/index.js",
"files": [
"/dist"
],
"scripts": { "scripts": {
"build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js", "build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js",
"dev": "webpack --watch --config build/webpack.dev.conf.js", "dev": "webpack --watch --config build/webpack.dev.conf.js",
@ -22,12 +19,12 @@
"@chartwerk/core": "latest" "@chartwerk/core": "latest"
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^6.8.1", "css-loader": "^3.4.2",
"style-loader": "^3.3.3", "style-loader": "^1.1.3",
"ts-loader": "^9.4.3", "ts-loader": "^6.2.1",
"typescript": "^5.1.3", "typescript": "^3.8.3",
"webpack": "^5.87.0", "webpack": "^4.42.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^3.3.11"
}, },
"packageManager": "yarn@3.2.1" "packageManager": "yarn@3.2.1"
} }

223
src/index.ts

@ -1,48 +1,48 @@
import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, AxisFormat } from '@chartwerk/core'; import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, AxisFormat } from '@chartwerk/core';
import { BarConfig } from './models/bar_options'; import { BarTimeSerie, BarOptions, RowValues } from './types';
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';
export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> { const DEFAULT_BAR_OPTIONS: 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: BarSerie[] = [], _options: BarOptions = {}) { constructor(el: HTMLElement, _series: BarTimeSerie[] = [], _options: BarOptions = {}) {
super(el, _series, _options); super(el, _series, _options);
this.series = new BarSeries(_series); _.defaults(this.options, DEFAULT_BAR_OPTIONS);
this.options = new BarConfig(_options);
} }
protected renderMetrics(): void { protected renderMetrics(): void {
if(!this.series.isSeriesAvailable) { if(this.series.length === 0 || this.series[0].datapoints.length === 0) {
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.barOptions.matching === false || this.seriesUniqKeys.length === 0; return this.options.matching === false || this.seriesUniqKeys.length === 0;
} }
setSeriesDataForRendering(): void { setSeriesDataForRendering(): void {
if(this.isMatchingDisabled) { if(this.isMatchingDisabled) {
this._seriesDataForRendring = this.getZippedDataForRender(this.series.visibleSeries); this._seriesDataForRendring = this.getZippedDataForRender(this.visibleSeries);
} else { } else {
const matchedSeries = this.seriesForMatching.map( const matchedSeries = this.seriesForMatching.map(
(series: BarSerie[], idx: number) => this.getZippedDataForRender(series) (series: BarTimeSerie[], idx: number) => this.getZippedDataForRender(series)
); );
this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries); this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries);
} }
@ -59,63 +59,51 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
.data(data) .data(data)
.enter().append('g') .enter().append('g')
.attr('class', 'rects-container') .attr('class', 'rects-container')
.each((d: RowValues, rowIndex: number, nodes: any) => { .each((d: RowValues, i: number, nodes: any) => {
const container = d3.select(nodes[rowIndex]); const container = d3.select(nodes[i]);
container.selectAll('rect') container.selectAll('rect')
.data(d.values) .data(d.values)
.enter().append('rect') .enter().append('rect')
.style('fill', (val, idx) => this.getBarColor(d, val, idx, rowIndex)) .style('fill', (val, i) => d.colors[i])
.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.barOptions.annotations)) { if(_.isEmpty(this.options.annotations)) {
return; return;
} }
// find all series for single matchedKey // find all series for single matchedKey
const series = _.filter(this.series.visibleSeries, serie => _.includes(d.serieTarget, serie.target)); const series = _.filter(this.series, 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.barOptions.annotations, a => a.key === key); const annotation = _.find(this.options.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.barOptions.maxAnnotationSize, min: this.options.barOptions.minAnnotationSize }; const options = { max: this.options.maxAnnotationSize, min: this.options.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;
@ -123,8 +111,8 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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,
@ -144,17 +132,10 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
} }
getBarOpacity(rowValues: RowValues): number { getBarOpacity(rowValues: RowValues): number {
if(this.options.barOptions.opacityFormatter === undefined) { if(this.options.opacityFormatter === undefined) {
return 1; return 1;
} }
return this.options.barOptions.opacityFormatter(rowValues); return this.options.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[]) {
@ -174,27 +155,27 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
} }
get seriesUniqKeys(): string[] { get seriesUniqKeys(): string[] {
if(!this.series.isSeriesAvailable) { if(this.visibleSeries.length === 0) {
return []; return [];
} }
const keys = this.series.visibleSeries.map(serie => serie.matchedKey); const keys = this.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(): BarSerie[][] { get seriesForMatching(): BarTimeSerie[][] {
if(this.seriesUniqKeys.length === 0) { if(this.seriesUniqKeys.length === 0) {
return [this.series.visibleSeries]; return [this.visibleSeries];
} }
const seriesList = this.seriesUniqKeys.map(key => { const seriesList = this.seriesUniqKeys.map(key => {
const seriesWithKey = _.filter(this.series.visibleSeries, serie => serie.matchedKey === key); const seriesWithKey = _.filter(this.visibleSeries, serie => serie.matchedKey === key);
return seriesWithKey; return seriesWithKey;
}); });
return seriesList; return seriesList;
} }
getZippedDataForRender(series: BarSerie[]): RowValues[] { getZippedDataForRender(series: BarTimeSerie[]): 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');
} }
@ -204,7 +185,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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 => serie.colorFormatter || serie.color); const colors = _.map(series, serie => this.getBarColor(serie));
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 } });
@ -214,7 +195,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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.state.xScale(values.x); const x = this.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);
@ -228,40 +209,59 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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);
this.options.callbackMouseMove({ if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseMove !== undefined) {
x: d3.event.pageX, this.options.eventsCallbacks.mouseMove({
y: d3.event.pageY, x: d3.event.pageX,
time: this.state.xScale.invert(eventX), y: d3.event.pageY,
series, time: this.xScale.invert(eventX),
chartX: eventX, series,
chartWidth: this.width chartX: eventX,
}); 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.isSeriesAvailable) { if(this.series === undefined || this.series.length === 0) {
return undefined; return undefined;
} }
const mousePoisitionKey = Math.ceil(this.state.xScale.invert(eventX)); const mousePoisitionKey = Math.ceil(this.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 {
this.options.callbackMouseOut(); if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseOut !== undefined) {
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,26 +272,30 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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({
pageX: d3.event.pageX, if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.contextMenu !== undefined) {
pageY: d3.event.pageY, this.options.eventsCallbacks.contextMenu({
xVal: this.state.xScale.invert(eventX), x: d3.event.pageX,
series, y: d3.event.pageY,
chartX: eventX time: this.xScale.invert(eventX),
}); series,
chartX: eventX
});
} else {
console.log('contextmenu, but there is no callback');
}
} }
get barWidth(): number { get barWidth(): number {
const xAxisStartValue = _.first(this.series.visibleSeries[0].datapoints)[0]; // TODO: here we use first value + timeInterval as bar width. It is not a good idea
const xAxisEndValue = _.last(this.series.visibleSeries[0].datapoints)[0]; const xAxisStartValue = _.first(this.series[0].datapoints)[0];
const timeInterval = (xAxisEndValue - xAxisStartValue) / this.series.visibleSeries[0].datapoints.length; let width = this.xScale(xAxisStartValue + this.timeInterval) / 2;
let width = this.state.xScale(timeInterval) / 2; if(this.options.barWidth !== undefined) {
if(this.options.barOptions.barWidth !== undefined) {
// barWidth now has axis-x dimension // barWidth now has axis-x dimension
width = this.state.xScale(this.state.getMinValueX() + this.options.barOptions.barWidth); width = this.xScale(this.state.getMinValueX() + this.options.barWidth);
} }
let rectColumns = this.series.visibleSeries.length; let rectColumns = this.visibleSeries.length;
if(this.options.barOptions.stacked === true) { if(this.options.stacked === true) {
rectColumns = 1; rectColumns = 1;
} }
return this.updateBarWidthWithBorders(width / rectColumns); return this.updateBarWidthWithBorders(width / rectColumns);
@ -299,11 +303,11 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
updateBarWidthWithBorders(width: number): number { updateBarWidthWithBorders(width: number): number {
let barWidth = width; let barWidth = width;
if(this.options.barOptions.minBarWidth !== undefined) { if(this.options.minBarWidth !== undefined) {
barWidth = Math.max(barWidth, this.options.barOptions.minBarWidth); barWidth = Math.max(barWidth, this.options.minBarWidth);
} }
if(this.options.barOptions.maxBarWidth !== undefined) { if(this.options.maxBarWidth !== undefined) {
barWidth = Math.min(barWidth, this.options.barOptions.maxBarWidth); barWidth = Math.min(barWidth, this.options.maxBarWidth);
} }
return barWidth; return barWidth;
} }
@ -316,8 +320,8 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
} }
getBarPositionX(key: number, idx: number): number { getBarPositionX(key: number, idx: number): number {
let xPosition: number = this.state.xScale(key); let xPosition: number = this.xScale(key);
if(this.options.barOptions.stacked === false) { if(this.options.stacked === false) {
xPosition += idx * this.barWidth; xPosition += idx * this.barWidth;
} }
return xPosition; return xPosition;
@ -325,7 +329,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, 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.barOptions.stacked === true) { if(this.options.stacked === true) {
const previousBarsHeight = _.sum( const previousBarsHeight = _.sum(
_.map(_.range(idx), i => this.getBarHeight(values[i])) _.map(_.range(idx), i => this.getBarHeight(values[i]))
); );
@ -350,15 +354,15 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
} }
getYMaxValue(): number | undefined { getYMaxValue(): number | undefined {
if(!this.series.isSeriesAvailable) { if(this.series === undefined || this.series.length === 0 || this.series[0].datapoints.length === 0) {
return undefined; return undefined;
} }
if(this.options.axis.y.range) { if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) {
return _.max(this.options.axis.y.range); return _.max(this.options.axis.y.range);
} }
let maxValue: number; let maxValue: number;
if(this.options.barOptions.stacked === true) { if(this.options.stacked === true) {
if(this.options.barOptions.matching === true && this.seriesUniqKeys.length > 0) { if(this.options.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);
@ -366,13 +370,14 @@ export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
}); });
return _.max(maxValues); return _.max(maxValues);
} else { } else {
const valuesColumns = _.map(this.series.visibleSeries, serie => _.map(serie.datapoints, row => row[1])); const valuesColumns = _.map(this.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.series.visibleSeries.map( this.visibleSeries.map(
serie => _.maxBy<number[]>(serie.datapoints, dp => dp[1])[0] serie => _.maxBy<number[]>(serie.datapoints, dp => dp[1])[0]
) )
); );
@ -404,4 +409,4 @@ export const VueChartwerkBarChartObject = {
} }
}; };
export { BarSerie, BarOptions, TimeFormat, AxisFormat }; export { BarTimeSerie, BarOptions, TimeFormat, AxisFormat };

55
src/models/bar_options.ts

@ -1,50 +1,29 @@
import { CoreOptions } from '@chartwerk/core'; import { CoreSeries } from '@chartwerk/core';
import { BarOptions, BarAdditionalOptions } from '../types'; import { ScatterData, PointType, LineType } from '../types';
import * as _ from 'lodash'; import * as _ from 'lodash';
const BAR_SERIE_DEFAULTS = { const DEFAULT_POINT_SIZE = 4;
renderBarLabels: false,
stacked: false,
barWidth: undefined,
maxBarWidth: undefined,
minBarWidth: undefined,
maxAnnotationSize: undefined,
minAnnotationSize: undefined,
matching: false,
opacityFormatter: undefined,
annotations: [],
};
export class BarConfig extends CoreOptions<BarOptions> { const SCATTER_DATA_DEFAULTS = {
pointType: PointType.CIRCLE,
lineType: LineType.NONE,
pointSize: DEFAULT_POINT_SIZE,
colorFormatter: undefined
};
constructor(options: BarOptions) { export class ScatterSeries extends CoreSeries<ScatterData> {
super(options, BAR_SERIE_DEFAULTS);
}
get barOptions(): BarAdditionalOptions { constructor(series: ScatterData[]) {
return { super(series);
renderBarLabels: this._options.renderBarLabels,
stacked: this._options.stacked,
barWidth: this._options.barWidth,
maxBarWidth: this._options.maxBarWidth,
minBarWidth: this._options.minBarWidth,
maxAnnotationSize: this._options.maxAnnotationSize,
minAnnotationSize: this._options.minAnnotationSize,
matching: this._options.matching,
opacityFormatter: this._options.opacityFormatter,
annotations: this._options.annotations,
}
} }
// event callbacks protected get defaults(): ScatterData {
callbackContextMenu(data: any): void { return { ...this._coreDefaults, ...SCATTER_DATA_DEFAULTS };
if(_.has(this._options.eventsCallbacks, 'contextMenu')) {
this._options.eventsCallbacks.contextMenu(data);
}
} }
get contextMenu(): (evt: any) => void { // move to parent
return this._options.eventsCallbacks.contextMenu; public getSerieByTarget(target: string): ScatterData | undefined {
return _.find(this.visibleSeries, serie => serie.target === target);
} }
} }

26
src/models/bar_series.ts

@ -1,15 +1,29 @@
import { CoreSeries } from '@chartwerk/core'; import { CoreSeries } from '@chartwerk/core';
import { BarSerie } from '../types'; import { ScatterData, PointType, LineType } from '../types';
import * as _ from 'lodash';
const BAR_SERIE_DEFAULTS = { const DEFAULT_POINT_SIZE = 4;
matchedKey: undefined,
const SCATTER_DATA_DEFAULTS = {
pointType: PointType.CIRCLE,
lineType: LineType.NONE,
pointSize: DEFAULT_POINT_SIZE,
colorFormatter: undefined colorFormatter: undefined
}; };
export class BarSeries extends CoreSeries<BarSerie> { export class ScatterSeries extends CoreSeries<ScatterData> {
constructor(series: ScatterData[]) {
super(series);
}
protected get defaults(): ScatterData {
return { ...this._coreDefaults, ...SCATTER_DATA_DEFAULTS };
}
constructor(series: BarSerie[]) { // move to parent
super(series, BAR_SERIE_DEFAULTS); public getSerieByTarget(target: string): ScatterData | undefined {
return _.find(this.visibleSeries, serie => serie.target === target);
} }
} }

29
src/types.ts

@ -5,30 +5,27 @@ export type BarSerieParams = {
colorFormatter: (serie: BarSerie) => string; colorFormatter: (serie: BarSerie) => string;
} }
export type BarSerie = Serie & Partial<BarSerieParams>; export type BarSerie = Serie & Partial<BarSerieParams>;
export type BarAdditionalOptions = { export type BarOptionsParams = {
renderBarLabels?: boolean; renderBarLabels: boolean;
stacked?: boolean; stacked: boolean;
barWidth?: number; // width in x axis unit barWidth: number; // width in x axis unit
maxBarWidth?: number; // in px maxBarWidth: number; // in px
minBarWidth?: number; // in px minBarWidth: number; // in px
maxAnnotationSize?: number; // in px TODO: move to annotaions maxAnnotationSize: number; // in px TODO: move to annotaions
minAnnotationSize?: number; // in px minAnnotationSize: number; // in px
matching?: boolean; matching: boolean;
opacityFormatter?: (data: RowValues) => number; opacityFormatter: (data: RowValues) => number;
annotations?: { annotations: {
key: string, // matchedKey from series key: string, // matchedKey from series
// TODO: add enum with "triangle" option // TODO: add enum with "triangle" option
color: string, color: string,
}[]; }[];
eventsCallbacks?: {
contextMenu?: (data: any) => void;
}
} }
export type BarOptions = Options & Partial<BarAdditionalOptions>; export type BarOptions = Options & Partial<BarOptionsParams>;
export type RowValues = { export type RowValues = {
key: number, key: number,
values: number[], values: number[],
additionalValues: (null | number)[], // values in datapoints third column additionalValues: (null | number)[], // values in datapoints third column
colors: (string | ((data: any) => string))[], colors: string[],
serieTarget: string[], serieTarget: string[],
} }

5130
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save