Browse Source

update state with max values && remove clear state

merge-requests/6/head
vargburz 3 years ago
parent
commit
3931ae9e11
  1. 145
      src/index.ts
  2. 150
      src/state.ts

145
src/index.ts

@ -119,7 +119,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
protected brush?: d3.BrushBehavior<unknown>; protected brush?: d3.BrushBehavior<unknown>;
protected zoom?: any; protected zoom?: any;
protected svg?: d3.Selection<SVGElement, unknown, null, undefined>; protected svg?: d3.Selection<SVGElement, unknown, null, undefined>;
protected state?: PodState; protected state?: PodState<T, O>;
protected clipPath?: any; protected clipPath?: any;
protected isPanning = false; protected isPanning = false;
protected isBrushing = false; protected isBrushing = false;
@ -162,7 +162,6 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
this.series = cloneDeep(_series); this.series = cloneDeep(_series);
this.d3 = _d3; this.d3 = _d3;
// TODO: mb move it to render();
this.initPodState(); this.initPodState();
this.d3Node = this.d3.select(this.el); this.d3Node = this.d3.select(this.el);
@ -181,8 +180,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
} }
public render(): void { public render(): void {
this.clearScaleCache(); console.log('render');
this.renderAxes(); this.renderAxes();
this.renderGrid(); this.renderGrid();
@ -235,7 +233,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
public abstract hideSharedCrosshair(): void; public abstract hideSharedCrosshair(): void;
protected initPodState(): void { protected initPodState(): void {
this.state = new PodState(this.options); this.state = new PodState(this.series, this.options);
} }
protected initComponents(): void { protected initComponents(): void {
@ -673,9 +671,6 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
const signX = Math.sign(event.transform.x); const signX = Math.sign(event.transform.x);
const transformX = this.absXScale.invert(Math.abs(transformStep)); const transformX = this.absXScale.invert(Math.abs(transformStep));
let rangeX = this.state.xValueRange; let rangeX = this.state.xValueRange;
if(this.state.xValueRange === undefined) {
rangeX = [this.maxValueX, this.minValueX];
}
this.state.xValueRange = [rangeX[0] + signX * transformX, rangeX[1] + signX * transformX]; this.state.xValueRange = [rangeX[0] + signX * transformX, rangeX[1] + signX * transformX];
const translateX = this.state.transform.x + signX * transformStep; const translateX = this.state.transform.x + signX * transformStep;
this.state.transform = { x: translateX }; this.state.transform = { x: translateX };
@ -687,7 +682,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
if(this.options.axis.y.invert === true) { if(this.options.axis.y.invert === true) {
signY = -signY; signY = -signY;
} }
let rangeY = this.state.yValueRange || [this.maxValue, this.minValue]; let rangeY = this.state.yValueRange;
const transformY = this.absYScale.invert(deltaY); const transformY = this.absYScale.invert(deltaY);
this.deltaYTransform = this.deltaYTransform + deltaY; this.deltaYTransform = this.deltaYTransform + deltaY;
// TODO: not hardcoded bounds // TODO: not hardcoded bounds
@ -840,22 +835,23 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
} }
get absXScale(): d3.ScaleLinear<number, number> { get absXScale(): d3.ScaleLinear<number, number> {
const domain = [0, Math.abs(this.maxValueX - this.minValueX)]; const domain = [0, Math.abs(this.state.maxValueX - this.state.minValueX)];
return this.d3.scaleLinear() return this.d3.scaleLinear()
.domain(domain) .domain(domain)
.range([0, this.width]); .range([0, this.width]);
} }
get absYScale(): d3.ScaleLinear<number, number> { get absYScale(): d3.ScaleLinear<number, number> {
const domain = [0, Math.abs(this.maxValue - this.minValue)]; const domain = [0, Math.abs(this.state.maxValueY - this.state.minValueY)];
return this.d3.scaleLinear() return this.d3.scaleLinear()
.domain(domain) .domain(domain)
.range([0, this.height]); .range([0, this.height]);
} }
// TODO: scales should be moved to State. Because it dependes on changeable ranges
get xScale(): d3.ScaleLinear<number, number> { get xScale(): d3.ScaleLinear<number, number> {
if(this._xScale === null) { if(this._xScale === null) {
const domain = this.state.xValueRange || [this.minValueX, this.maxValueX]; const domain = this.state.xValueRange;
this._xScale = this.d3.scaleLinear() this._xScale = this.d3.scaleLinear()
.domain(domain) .domain(domain)
.range([0, this.width]); .range([0, this.width]);
@ -865,7 +861,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
get yScale(): d3.ScaleLinear<number, number> { get yScale(): d3.ScaleLinear<number, number> {
if(this._yScale === null) { if(this._yScale === null) {
let domain = this.state.yValueRange || [this.maxValue, this.minValue]; let domain = this.state.yValueRange;
domain = sortBy(domain) as [number, number]; domain = sortBy(domain) as [number, number];
if(this.options.axis.y.invert === true) { if(this.options.axis.y.invert === true) {
domain = reverse(domain); domain = reverse(domain);
@ -878,12 +874,12 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
} }
protected get y1Scale(): d3.ScaleLinear<number, number> { protected get y1Scale(): d3.ScaleLinear<number, number> {
if(this.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) { if(this.state.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) {
return null; return null;
} }
// scale for y1 axis(right y axis) // scale for y1 axis(right y axis)
if(this._y1Scale === null) { if(this._y1Scale === null) {
let domain = this.state.y1ValueRange || [this.y1MaxValue, this.y1MinValue]; let domain = this.state.y1ValueRange;
domain = sortBy(domain) as [number, number]; domain = sortBy(domain) as [number, number];
if(this.options.axis.y1.invert === true) { if(this.options.axis.y1.invert === true) {
domain = reverse(domain); domain = reverse(domain);
@ -895,119 +891,6 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
return this._y1Scale; return this._y1Scale;
} }
filterSerieByYAxisOrientation(serie: T, orientation: yAxisOrientation): boolean {
if(serie.yOrientation === undefined || serie.yOrientation === yAxisOrientation.BOTH) {
return true;
}
return serie.yOrientation === orientation;
}
get minValue(): number {
// y min value
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) {
return min(this.options.axis.y.range);
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.LEFT))
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return minValue;
}
get maxValue(): number {
// y max value
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) {
return max(this.options.axis.y.range);
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.LEFT))
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return maxValue;
}
get y1MinValue(): number {
// TODO: remove duplicates
if(this.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.y1.range !== undefined) {
return min(this.options.axis.y1.range);
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.RIGHT))
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return minValue;
}
get y1MaxValue(): number {
if(this.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.y1 !== undefined && this.options.axis.y1.range !== undefined) {
return max(this.options.axis.y1.range);
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.RIGHT))
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return maxValue;
}
get minValueX(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.x !== undefined && this.options.axis.x.range !== undefined) {
return min(this.options.axis.x.range)
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false)
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[0])[0]
)
);
return minValue;
}
get maxValueX(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.x !== undefined && this.options.axis.x.range !== undefined) {
return max(this.options.axis.x.range)
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false)
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[0])[0]
)
);
return maxValue;
}
getd3TimeRangeEvery(count: number): d3.TimeInterval { getd3TimeRangeEvery(count: number): d3.TimeInterval {
if(this.options.timeInterval === undefined || this.options.timeInterval.timeFormat === undefined) { if(this.options.timeInterval === undefined || this.options.timeInterval.timeFormat === undefined) {
return this.d3.timeMinute.every(count); return this.d3.timeMinute.every(count);
@ -1146,12 +1029,6 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
return mergeWith({}, DEFAULT_MARGIN, this.extraMargin, add); return mergeWith({}, DEFAULT_MARGIN, this.extraMargin, add);
} }
get isSeriesUnavailable(): boolean {
// TODO: Use one && throw error
return this.series === undefined || this.series.length === 0 ||
max(this.series.map(serie => serie.datapoints.length)) === 0;
}
formatedBound(alias: string, target: string): string { formatedBound(alias: string, target: string): string {
const confidenceMetric = replace(alias, '$__metric_name', target); const confidenceMetric = replace(alias, '$__metric_name', target);
return confidenceMetric; return confidenceMetric;

150
src/state.ts

@ -1,27 +1,42 @@
import { Options, TimeFormat, TickOrientation, AxisFormat } from './types'; import { TimeSerie, Options, yAxisOrientation } from './types';
import lodashGet from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import min from 'lodash/min';
import minBy from 'lodash/minBy';
import max from 'lodash/max';
import maxBy from 'lodash/maxBy';
const DEFAULT_AXIS_RANGE = [0, 1];
const DEFAULT_TRANSFORM = { const DEFAULT_TRANSFORM = {
x: 0, x: 0,
y: 0, y: 0,
k: 1 k: 1
} }
export class PodState { // TODO: replace all getters with fields. Because getters will be recalculated on each call
private _xValueRange: [number, number] | undefined = undefined; // TODO: remove duplicates in max/min values.
private _yValueRange: [number, number] | undefined = undefined; // TODO: add scales
private _y1ValueRange: [number, number] | undefined = undefined; // TODO: PodState can be divided in two classes, but it is hard now.
export class PodState<T extends TimeSerie, O extends Options> {
private _xValueRange: [number, number];
private _yValueRange: [number, number];
private _y1ValueRange: [number, number] | undefined = undefined; // can be undefined, because y1 - is an optional param
private _transform: { x: number, y: number, k: number } = cloneDeep(DEFAULT_TRANSFORM); private _transform: { x: number, y: number, k: number } = cloneDeep(DEFAULT_TRANSFORM);
constructor( constructor(
options: Options protected series: T[],
protected options: O,
) { ) {
this._xValueRange = lodashGet(options, 'axis.x.range'); this.initRanges();
this._yValueRange = lodashGet(options, 'axis.y.range'); }
this._y1ValueRange = lodashGet(options, 'axis.y1.range');
protected initRanges(): void {
this._xValueRange = [this.minValueX, this.maxValueX];
this._yValueRange = [this.minValueY, this.maxValueY];
if(this.options.axis.y1.isActive) {
this._y1ValueRange = [this.minValueY1, this.maxValueY1];
}
} }
get xValueRange(): [number, number] | undefined { get xValueRange(): [number, number] | undefined {
@ -57,4 +72,119 @@ export class PodState {
this._transform.y = transform.y !== undefined ? transform.y : this._transform.y; this._transform.y = transform.y !== undefined ? transform.y : this._transform.y;
this._transform.k = transform.k !== undefined ? transform.k : this._transform.k; this._transform.k = transform.k !== undefined ? transform.k : this._transform.k;
} }
get minValueY(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) {
return min(this.options.axis.y.range);
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.LEFT))
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return minValue;
}
get maxValueY(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.y !== undefined && this.options.axis.y.range !== undefined) {
return max(this.options.axis.y.range);
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.LEFT))
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return maxValue;
}
get minValueX(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.x !== undefined && this.options.axis.x.range !== undefined) {
return min(this.options.axis.x.range);
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false)
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[0])[0]
)
);
return minValue;
}
get maxValueX(): number {
if(this.isSeriesUnavailable) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.x !== undefined && this.options.axis.x.range !== undefined) {
return max(this.options.axis.x.range)
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false)
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[0])[0]
)
);
return maxValue;
}
get minValueY1(): number {
if(this.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) {
return DEFAULT_AXIS_RANGE[0];
}
if(this.options.axis.y1.range !== undefined) {
return min(this.options.axis.y1.range);
}
const minValue = min(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.RIGHT))
.map(
serie => minBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return minValue;
}
get maxValueY1(): number {
if(this.isSeriesUnavailable || this.options.axis.y1 === undefined || this.options.axis.y1.isActive === false) {
return DEFAULT_AXIS_RANGE[1];
}
if(this.options.axis.y1 !== undefined && this.options.axis.y1.range !== undefined) {
return max(this.options.axis.y1.range);
}
const maxValue = max(
this.series
.filter(serie => serie.visible !== false && this.filterSerieByYAxisOrientation(serie, yAxisOrientation.RIGHT))
.map(
serie => maxBy<number[]>(serie.datapoints, dp => dp[1])[1]
)
);
return maxValue;
}
get isSeriesUnavailable(): boolean {
return this.series === undefined || this.series.length === 0 ||
max(this.series.map(serie => serie.datapoints.length)) === 0;
}
protected filterSerieByYAxisOrientation(serie: T, orientation: yAxisOrientation): boolean {
if(serie.yOrientation === undefined || serie.yOrientation === yAxisOrientation.BOTH) {
return true;
}
return serie.yOrientation === orientation;
}
} }

Loading…
Cancel
Save