Browse Source

use new core with models

pull/1/head
vargburz 2 years ago
parent
commit
fd3e15d30f
  1. 7
      package.json
  2. 157
      src/index.ts
  3. 46
      src/models/bar_options.ts
  4. 15
      src/models/bar_series.ts
  5. 33
      src/types.ts
  6. 10
      yarn.lock

7
package.json

@ -1,12 +1,13 @@
{
"name": "@chartwerk/bar-pod",
"version": "0.6.0-beta",
"version": "0.6.1",
"description": "Chartwerk bar pod",
"main": "dist/index.js",
"scripts": {
"build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js",
"dev": "webpack --watch --config build/webpack.dev.conf.js",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"update-core": "yarn up @chartwerk/core && yarn up @chartwerk/core@latest"
},
"repository": {
"type": "git",
@ -15,7 +16,7 @@
"author": "CorpGlory",
"license": "Apache-2.0",
"dependencies": {
"@chartwerk/core": "0.6.3"
"@chartwerk/core": "latest"
},
"devDependencies": {
"css-loader": "^3.4.2",

157
src/index.ts

@ -1,29 +1,29 @@
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 * as d3 from 'd3';
import * as _ from 'lodash';
const DEFAULT_BAR_OPTIONS: BarOptions = {
renderBarLabels: false,
stacked: false,
matching: false
}
export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
export class ChartwerkBarPod extends ChartwerkPod<BarSerie, BarOptions> {
barYScale: null | d3.ScaleLinear<number, number> = null;
_seriesDataForRendring = [];
series: BarSeries;
options: BarConfig;
constructor(el: HTMLElement, _series: BarTimeSerie[] = [], _options: BarOptions = {}) {
constructor(el: HTMLElement, _series: BarSerie[] = [], _options: BarOptions = {}) {
super(el, _series, _options);
_.defaults(this.options, DEFAULT_BAR_OPTIONS);
this.series = new BarSeries(_series);
this.options = new BarConfig(_options);
}
protected renderMetrics(): void {
if(this.series.length === 0 || this.series[0].datapoints.length === 0) {
if(!this.series.isSeriesAvailable) {
this.renderNoDataPointsMessage();
return;
}
@ -34,15 +34,15 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
}
get isMatchingDisabled(): boolean {
return this.options.matching === false || this.seriesUniqKeys.length === 0;
return this.options.barOptions.matching === false || this.seriesUniqKeys.length === 0;
}
setSeriesDataForRendering(): void {
if(this.isMatchingDisabled) {
this._seriesDataForRendring = this.getZippedDataForRender(this.visibleSeries);
this._seriesDataForRendring = this.getZippedDataForRender(this.series.visibleSeries);
} else {
const matchedSeries = this.seriesForMatching.map(
(series: BarTimeSerie[], idx: number) => this.getZippedDataForRender(series)
(series: BarSerie[], idx: number) => this.getZippedDataForRender(series)
);
this._seriesDataForRendring = this.mergeMacthedSeriesAndSort(matchedSeries);
}
@ -77,7 +77,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
.on('contextmenu', this.contextMenu.bind(this));
// render bar annotations, its all hardcoded
if(_.isEmpty(this.options.annotations)) {
if(_.isEmpty(this.options.barOptions.annotations)) {
return;
}
// find all series for single matchedKey
@ -86,7 +86,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
const key = matchedKeys[0];
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) {
return;
}
@ -97,7 +97,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
.attr('d', () => {
const x = Math.ceil(_.toNumber(rectSelection.attr('x')));
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);
})
.attr('fill', annotation.color);
@ -132,10 +132,10 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
}
getBarOpacity(rowValues: RowValues): number {
if(this.options.opacityFormatter === undefined) {
if(this.options.barOptions.opacityFormatter === undefined) {
return 1;
}
return this.options.opacityFormatter(rowValues);
return this.options.barOptions.opacityFormatter(rowValues);
}
mergeMacthedSeriesAndSort(matchedSeries: any[]) {
@ -155,27 +155,27 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
}
get seriesUniqKeys(): string[] {
if(this.visibleSeries.length === 0) {
if(!this.series.isSeriesAvailable) {
return [];
}
const keys = this.visibleSeries.map(serie => serie.matchedKey);
const keys = this.series.visibleSeries.map(serie => serie.matchedKey);
const uniqKeys = _.uniq(keys);
const filteredKeys = _.filter(uniqKeys, key => key !== undefined);
return filteredKeys;
}
get seriesForMatching(): BarTimeSerie[][] {
get seriesForMatching(): BarSerie[][] {
if(this.seriesUniqKeys.length === 0) {
return [this.visibleSeries];
return [this.series.visibleSeries];
}
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 seriesList;
}
getZippedDataForRender(series: BarTimeSerie[]): RowValues[] {
getZippedDataForRender(series: BarSerie[]): RowValues[] {
if(series.length === 0) {
throw new Error('There is no visible series');
}
@ -185,7 +185,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
const additionalValuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[2] !== undefined ? row[2] : null));
const zippedAdditionalValuesColumn = _.zip(...additionalValuesColumns);
const zippedValuesColumn = _.zip(...valuesColumns);
const colors = _.map(series, serie => this.getBarColor(serie));
const colors = _.map(series, serie => serie.color);
const tagrets = _.map(series, serie => serie.target);
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 } });
@ -195,7 +195,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
public renderSharedCrosshair(values: { x?: number, y?: number }): void {
this.crosshair.style('display', null);
const x = this.xScale(values.x);
const x = this.state.xScale(values.x);
this.crosshair.select('#crosshair-line-x')
.attr('x1', x)
.attr('x2', x);
@ -209,59 +209,40 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
// TODO: mouse move work bad with matching
const event = d3.mouse(this.chartContainer.node());
const eventX = event[0];
if(this.isOutOfChart() === true) {
this.crosshair.style('display', 'none');
return;
}
this.crosshair.select('#crosshair-line-x')
.attr('x1', eventX)
.attr('x2', eventX);
const series = this.getSeriesPointFromMousePosition(eventX);
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseMove !== undefined) {
this.options.eventsCallbacks.mouseMove({
x: d3.event.pageX,
y: d3.event.pageY,
time: this.xScale.invert(eventX),
series,
chartX: eventX,
chartWidth: this.width
});
} else {
console.log('mouse move, but there is no callback');
}
this.options.callbackMouseMove({
x: d3.event.pageX,
y: d3.event.pageY,
time: this.state.xScale.invert(eventX),
series,
chartX: eventX,
chartWidth: this.width
});
}
getSeriesPointFromMousePosition(eventX: number): any[] | undefined {
if(this.series === undefined || this.series.length === 0) {
if(!this.series.isSeriesAvailable) {
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 idx = findClosest(keys, mousePoisitionKey);
return this._seriesDataForRendring[idx];
}
getBarColor(serie: any) {
if(serie.color === undefined) {
return this.getSerieColor(0);
}
return serie.color;
}
onMouseOver(): void {
this.crosshair.style('display', null);
this.crosshair.raise();
}
onMouseOut(): void {
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.options.callbackMouseOut();
this.crosshair.style('display', 'none');
}
@ -272,30 +253,25 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
const event = d3.mouse(this.chartContainer.node());
const eventX = event[0];
const series = this.getSeriesPointFromMousePosition(eventX);
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.contextMenu !== undefined) {
this.options.eventsCallbacks.contextMenu({
x: d3.event.pageX,
y: d3.event.pageY,
time: this.xScale.invert(eventX),
series,
chartX: eventX
});
} else {
console.log('contextmenu, but there is no callback');
}
this.options.callbackContextMenu({
pageX: d3.event.pageX,
pageY: d3.event.pageY,
xVal: this.state.xScale.invert(eventX),
series,
chartX: eventX
});
}
get barWidth(): number {
// TODO: here we use first value + timeInterval as bar width. It is not a good idea
const xAxisStartValue = _.first(this.series[0].datapoints)[0];
let width = this.xScale(xAxisStartValue + this.timeInterval) / 2;
if(this.options.barWidth !== undefined) {
const xAxisStartValue = _.first(this.series.visibleSeries[0].datapoints)[0];
let width = this.state.xScale(xAxisStartValue + this.timeInterval) / 2;
if(this.options.barOptions.barWidth !== undefined) {
// 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;
if(this.options.stacked === true) {
let rectColumns = this.series.visibleSeries.length;
if(this.options.barOptions.stacked === true) {
rectColumns = 1;
}
return this.updateBarWidthWithBorders(width / rectColumns);
@ -303,11 +279,11 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
updateBarWidthWithBorders(width: number): number {
let barWidth = width;
if(this.options.minBarWidth !== undefined) {
barWidth = Math.max(barWidth, this.options.minBarWidth);
if(this.options.barOptions.minBarWidth !== undefined) {
barWidth = Math.max(barWidth, this.options.barOptions.minBarWidth);
}
if(this.options.maxBarWidth !== undefined) {
barWidth = Math.min(barWidth, this.options.maxBarWidth);
if(this.options.barOptions.maxBarWidth !== undefined) {
barWidth = Math.min(barWidth, this.options.barOptions.maxBarWidth);
}
return barWidth;
}
@ -320,8 +296,8 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
}
getBarPositionX(key: number, idx: number): number {
let xPosition: number = this.xScale(key);
if(this.options.stacked === false) {
let xPosition: number = this.state.xScale(key);
if(this.options.barOptions.stacked === false) {
xPosition += idx * this.barWidth;
}
return xPosition;
@ -329,7 +305,7 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
getBarPositionY(val: number, idx: number, values: number[]): number {
let yPosition: number = this.barYScale(Math.max(val, 0));
if(this.options.stacked === true) {
if(this.options.barOptions.stacked === true) {
const previousBarsHeight = _.sum(
_.map(_.range(idx), i => this.getBarHeight(values[i]))
);
@ -354,15 +330,15 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
}
getYMaxValue(): number | undefined {
if(this.series === undefined || this.series.length === 0 || this.series[0].datapoints.length === 0) {
if(!this.series.isSeriesAvailable) {
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);
}
let maxValue: number;
if(this.options.stacked === true) {
if(this.options.matching === true && this.seriesUniqKeys.length > 0) {
if(this.options.barOptions.stacked === true) {
if(this.options.barOptions.matching === true && this.seriesUniqKeys.length > 0) {
const maxValues = this.seriesForMatching.map(series => {
const valuesColumns = _.map(series, serie => _.map(serie.datapoints, row => row[1]));
const zippedValuesColumn = _.zip(...valuesColumns);
@ -370,14 +346,13 @@ export class ChartwerkBarPod extends ChartwerkPod<BarTimeSerie, BarOptions> {
});
return _.max(maxValues);
} 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);
maxValue = _.max(_.map(zippedValuesColumn, row => _.sum(row)));
}
} else {
console.log('else')
maxValue = _.max(
this.visibleSeries.map(
this.series.visibleSeries.map(
serie => _.maxBy<number[]>(serie.datapoints, dp => dp[1])[0]
)
);
@ -409,4 +384,4 @@ export const VueChartwerkBarChartObject = {
}
};
export { BarTimeSerie, BarOptions, TickOrientation, TimeFormat, AxisFormat };
export { BarSerie, BarOptions, TimeFormat, AxisFormat };

46
src/models/bar_options.ts

@ -0,0 +1,46 @@
import { CoreOptions } from '@chartwerk/core';
import { BarOptions, BarAdditionalOptions } from '../types';
import * as _ from 'lodash';
const BAR_SERIE_DEFAULTS = {
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> {
constructor(options: BarOptions) {
super(options, BAR_SERIE_DEFAULTS);
}
get barOptions(): BarAdditionalOptions {
return {
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
callbackContextMenu(data: any): void {
if(_.has(this._options.eventsCallbacks, 'contextMenu')) {
this._options.eventsCallbacks.contextMenu(data);
}
}
}

15
src/models/bar_series.ts

@ -0,0 +1,15 @@
import { CoreSeries } from '@chartwerk/core';
import { BarSerie } from '../types';
const BAR_SERIE_DEFAULTS = {
matchedKey: undefined,
colorFormatter: undefined
};
export class BarSeries extends CoreSeries<BarSerie> {
constructor(series: BarSerie[]) {
super(series, BAR_SERIE_DEFAULTS);
}
}

33
src/types.ts

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

10
yarn.lock

@ -9,7 +9,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@chartwerk/bar-pod@workspace:."
dependencies:
"@chartwerk/core": 0.6.3
"@chartwerk/core": latest
css-loader: ^3.4.2
style-loader: ^1.1.3
ts-loader: ^6.2.1
@ -19,13 +19,13 @@ __metadata:
languageName: unknown
linkType: soft
"@chartwerk/core@npm:0.6.3":
version: 0.6.3
resolution: "@chartwerk/core@npm:0.6.3"
"@chartwerk/core@npm:latest":
version: 0.6.5
resolution: "@chartwerk/core@npm:0.6.5"
dependencies:
d3: ^5.7.2
lodash: ^4.14.149
checksum: bb804b1a2339fc19857e5caa07d16ed78fb1b0739878d3f7488e9f8661667ed78ada3a63afd15206bd4210746b50916b253e98f077451cc8d899728ecf08ba50
checksum: 2757ae8be04b84c3624bd807a437d6bf3c67fef0808e3192f0a97ba819627f3b28821628cfefdfabe1951e8b7d0d45d1b9f6ade13cdc4a5bca5e539fc70850df
languageName: node
linkType: hard

Loading…
Cancel
Save