mdt-charts 1.12.5 → 1.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/engine/features/axis/axis.d.ts +3 -1
- package/lib/engine/features/axis/axis.js +8 -0
- package/lib/engine/features/gridLine/gidLineHelper.d.ts +2 -0
- package/lib/engine/features/gridLine/gidLineHelper.js +22 -0
- package/lib/engine/features/gridLine/gridLine.d.ts +5 -3
- package/lib/engine/features/gridLine/gridLine.js +16 -17
- package/lib/engine/intervalNotation/intervalManager.js +1 -1
- package/lib/engine/twoDimensionalNotation/area/area.js +3 -3
- package/lib/engine/twoDimensionalNotation/bar/bar.js +3 -3
- package/lib/engine/twoDimensionalNotation/bar/barHelper.js +8 -8
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStacker.d.ts +13 -0
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStacker.js +32 -0
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStacker.test.d.ts +1 -0
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStacker.test.js +199 -0
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStackerService.d.ts +5 -0
- package/lib/engine/twoDimensionalNotation/bar/stackedData/dataStackerService.js +19 -0
- package/lib/engine/twoDimensionalNotation/twoDimensionalManager.js +3 -2
- package/lib/model/featuresModel/axisModel.js +6 -3
- package/lib/model/featuresModel/scaleModel/scaleAxisRecalcer.d.ts +20 -0
- package/lib/model/featuresModel/scaleModel/scaleAxisRecalcer.js +31 -0
- package/lib/model/featuresModel/scaleModel/scaleModel.d.ts +19 -0
- package/lib/model/featuresModel/scaleModel/scaleModel.js +118 -0
- package/lib/model/marginModel.js +7 -4
- package/lib/model/modelInstance/canvasModel/canvasModel.d.ts +5 -4
- package/lib/model/modelInstance/canvasModel/canvasModel.js +6 -2
- package/lib/model/modelInstance/canvasModel/marginModelService.d.ts +11 -0
- package/lib/model/modelInstance/canvasModel/marginModelService.js +26 -0
- package/lib/model/notations/intervalModel.js +1 -1
- package/lib/model/notations/twoDimensionalModel.js +9 -11
- package/lib/style/charts-main.css +3 -3
- package/lib/style/charts-main.less +3 -3
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAxisModel, IScaleModel } from "../../../model/model";
|
|
1
|
+
import { AxisModelOptions, IAxisModel, IScaleModel } from "../../../model/model";
|
|
2
2
|
import { Block } from "../../block/block";
|
|
3
3
|
import { Scales } from "../scale/scale";
|
|
4
4
|
import { Size } from '../../../config/config';
|
|
@@ -6,6 +6,8 @@ export declare class Axis {
|
|
|
6
6
|
static axesClass: string;
|
|
7
7
|
static render(block: Block, scales: Scales, scaleModel: IScaleModel, axisModel: IAxisModel, blockSize: Size): void;
|
|
8
8
|
static update(block: Block, scales: Scales, scalesOptions: IScaleModel, axisModel: IAxisModel, blockSize: Size, keyDomainsEquality: boolean): void;
|
|
9
|
+
static raiseKeyAxis(block: Block, axisOptions: AxisModelOptions): void;
|
|
10
|
+
private static findAxis;
|
|
9
11
|
private static renderAxis;
|
|
10
12
|
private static updateValueAxis;
|
|
11
13
|
private static updateKeyAxis;
|
|
@@ -20,6 +20,14 @@ export class Axis {
|
|
|
20
20
|
if (axisModel.key.visibility)
|
|
21
21
|
this.updateKeyAxis(block, scales.key, scalesOptions.key, axisModel.key, blockSize, keyDomainsEquality);
|
|
22
22
|
}
|
|
23
|
+
static raiseKeyAxis(block, axisOptions) {
|
|
24
|
+
const axisElement = this.findAxis(block, axisOptions.cssClass);
|
|
25
|
+
axisElement.raise();
|
|
26
|
+
}
|
|
27
|
+
static findAxis(block, axisCssClass) {
|
|
28
|
+
return block.getSvg()
|
|
29
|
+
.select(`g.${axisCssClass}`);
|
|
30
|
+
}
|
|
23
31
|
static renderAxis(block, scale, scaleOptions, axisOptions, blockSize) {
|
|
24
32
|
const axisGenerator = AxisHelper.getBaseAxisGenerator(axisOptions, scale, scaleOptions);
|
|
25
33
|
if (axisOptions.type === 'value' && (scaleOptions.type === 'linear' || scaleOptions.type === 'datetime'))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AxisScale } from "d3-axis";
|
|
1
2
|
import { Size } from "../../../config/config";
|
|
2
3
|
import { AxisModelOptions, BlockMargin } from "../../../model/model";
|
|
3
4
|
export declare type GridLineType = 'key' | 'value';
|
|
@@ -10,4 +11,5 @@ export interface GridLineAttributes {
|
|
|
10
11
|
export declare class GridLineHelper {
|
|
11
12
|
static getGridLineLength(gridLineType: GridLineType, keyAxis: AxisModelOptions, valueAxis: AxisModelOptions, blockSize: Size, margin: BlockMargin): number;
|
|
12
13
|
static getLineAttributes(axis: AxisModelOptions, lineLength: number): GridLineAttributes;
|
|
14
|
+
static getKeyLineAttributes(axis: AxisModelOptions, scaleValue: AxisScale<any>): GridLineAttributes;
|
|
13
15
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { max, min } from "d3-array";
|
|
1
2
|
export class GridLineHelper {
|
|
2
3
|
static getGridLineLength(gridLineType, keyAxis, valueAxis, blockSize, margin) {
|
|
3
4
|
let axis;
|
|
@@ -27,4 +28,25 @@ export class GridLineHelper {
|
|
|
27
28
|
attributes.y2 = lineLength;
|
|
28
29
|
return attributes;
|
|
29
30
|
}
|
|
31
|
+
static getKeyLineAttributes(axis, scaleValue) {
|
|
32
|
+
const attributes = {
|
|
33
|
+
x1: 0,
|
|
34
|
+
y1: 0,
|
|
35
|
+
x2: 0,
|
|
36
|
+
y2: 0
|
|
37
|
+
};
|
|
38
|
+
const scaledStart = scaleValue(scaleValue.domain()[0]);
|
|
39
|
+
const scaledEnd = scaleValue(scaleValue.domain()[1]);
|
|
40
|
+
const minCoord = min([scaledStart, scaledEnd]) - scaleValue(0);
|
|
41
|
+
const maxCoord = max([scaledStart, scaledEnd]) - scaleValue(0);
|
|
42
|
+
if (axis.orient === 'left' || axis.orient === 'right') {
|
|
43
|
+
attributes.x1 = minCoord;
|
|
44
|
+
attributes.x2 = maxCoord;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
attributes.y1 = minCoord;
|
|
48
|
+
attributes.y2 = maxCoord;
|
|
49
|
+
}
|
|
50
|
+
return attributes;
|
|
51
|
+
}
|
|
30
52
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Size } from "../../../config/config";
|
|
2
|
-
import { BlockMargin, GridLineFlag, IAxisModel
|
|
2
|
+
import { BlockMargin, GridLineFlag, IAxisModel } from "../../../model/model";
|
|
3
3
|
import { Block } from "../../block/block";
|
|
4
|
+
import { Scales } from "../scale/scale";
|
|
4
5
|
export declare class GridLine {
|
|
5
6
|
private static readonly gridLineClass;
|
|
6
|
-
static render(block: Block, gridLineFlag: GridLineFlag, axes: IAxisModel, blockSize: Size, margin: BlockMargin,
|
|
7
|
-
static update(block: Block, gridLineFlag: GridLineFlag, axes: IAxisModel, blockSize: Size, margin: BlockMargin,
|
|
7
|
+
static render(block: Block, gridLineFlag: GridLineFlag, axes: IAxisModel, blockSize: Size, margin: BlockMargin, scales: Scales): void;
|
|
8
|
+
static update(block: Block, gridLineFlag: GridLineFlag, axes: IAxisModel, blockSize: Size, margin: BlockMargin, scales: Scales): void;
|
|
8
9
|
private static renderLine;
|
|
9
10
|
private static clear;
|
|
10
11
|
private static removeGridLinesOnAxes;
|
|
12
|
+
private static rmGridLineOnTick;
|
|
11
13
|
}
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import { GridLineHelper } from "./gidLineHelper";
|
|
2
2
|
export class GridLine {
|
|
3
|
-
static render(block, gridLineFlag, axes, blockSize, margin,
|
|
3
|
+
static render(block, gridLineFlag, axes, blockSize, margin, scales) {
|
|
4
4
|
if (gridLineFlag.value) {
|
|
5
5
|
const lineLength = GridLineHelper.getGridLineLength('value', axes.key, axes.value, blockSize, margin);
|
|
6
6
|
const lineAttributes = GridLineHelper.getLineAttributes(axes.value, lineLength);
|
|
7
7
|
this.renderLine(block, axes.value, lineAttributes);
|
|
8
8
|
}
|
|
9
9
|
if (gridLineFlag.key) {
|
|
10
|
-
const
|
|
11
|
-
const lineAttributes = GridLineHelper.getLineAttributes(axes.key, lineLength);
|
|
10
|
+
const lineAttributes = GridLineHelper.getKeyLineAttributes(axes.key, scales.value);
|
|
12
11
|
this.renderLine(block, axes.key, lineAttributes);
|
|
13
12
|
}
|
|
14
|
-
|
|
15
|
-
this.removeGridLinesOnAxes(block, axes.key, axes.value, false);
|
|
16
|
-
else if (gridLineFlag.key || gridLineFlag.value)
|
|
17
|
-
this.removeGridLinesOnAxes(block, axes.key, axes.value, true);
|
|
13
|
+
this.removeGridLinesOnAxes(block, axes.key, axes.value, true);
|
|
18
14
|
}
|
|
19
|
-
static update(block, gridLineFlag, axes, blockSize, margin,
|
|
15
|
+
static update(block, gridLineFlag, axes, blockSize, margin, scales) {
|
|
20
16
|
this.clear(block, axes.key.cssClass, axes.value.cssClass);
|
|
21
|
-
this.render(block, gridLineFlag, axes, blockSize, margin,
|
|
17
|
+
this.render(block, gridLineFlag, axes, blockSize, margin, scales);
|
|
22
18
|
}
|
|
23
19
|
static renderLine(block, axis, lineAttributes) {
|
|
24
20
|
block
|
|
@@ -51,17 +47,20 @@ export class GridLine {
|
|
|
51
47
|
tickOnValueAxisSelector = ':last-of-type';
|
|
52
48
|
if (keyAxis.orient === 'bottom' || keyAxis.orient === 'right')
|
|
53
49
|
tickOnKeyAxisSelector = ':last-of-type';
|
|
54
|
-
block.getSvg()
|
|
50
|
+
const tickOnKey = block.getSvg()
|
|
55
51
|
.select(`.${valueAxis.cssClass}`)
|
|
56
|
-
.select(`g.tick${tickOnKeyAxisSelector}`)
|
|
52
|
+
.select(`g.tick${tickOnKeyAxisSelector}`);
|
|
53
|
+
const tickOnValue = block.getSvg()
|
|
54
|
+
.select(`.${keyAxis.cssClass}`)
|
|
55
|
+
.select(`g.tick${tickOnValueAxisSelector}`);
|
|
56
|
+
// this.rmGridLineOnTick(tickOnKey);
|
|
57
|
+
// if (!excludeKey)
|
|
58
|
+
// this.rmGridLineOnTick(tickOnValue);
|
|
59
|
+
}
|
|
60
|
+
static rmGridLineOnTick(tick) {
|
|
61
|
+
tick
|
|
57
62
|
.select(`.${this.gridLineClass}`)
|
|
58
63
|
.remove();
|
|
59
|
-
if (!excludeKey)
|
|
60
|
-
block.getSvg()
|
|
61
|
-
.select(`.${keyAxis.cssClass}`)
|
|
62
|
-
.select(`g.tick${tickOnValueAxisSelector}`)
|
|
63
|
-
.select(`.${this.gridLineClass}`)
|
|
64
|
-
.remove();
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
GridLine.gridLineClass = 'grid-line';
|
|
@@ -11,7 +11,7 @@ export class IntervalManager {
|
|
|
11
11
|
block.renderSvg(model.blockCanvas.size);
|
|
12
12
|
const scales = Scale.getScales(options.scale.key, options.scale.value, options.chartSettings.bar);
|
|
13
13
|
Axis.render(block, scales, options.scale, options.axis, model.blockCanvas.size);
|
|
14
|
-
GridLine.render(block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin,
|
|
14
|
+
GridLine.render(block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin, scales);
|
|
15
15
|
this.renderCharts(block, options.charts, scales, data, options.data, model.chartBlock.margin, options.axis.key.orient, options.chartSettings);
|
|
16
16
|
Title.render(block, options.title, model.otherComponents.titleBlock, model.blockCanvas.size);
|
|
17
17
|
Legend.render(block, data, options, model);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { stack } from 'd3-shape';
|
|
2
1
|
import { select } from 'd3-selection';
|
|
3
2
|
import { MarkDot } from "../../features/markDots/markDot";
|
|
4
3
|
import { AreaHelper } from './areaHelper';
|
|
5
4
|
import { DomHelper } from '../../helpers/domHelper';
|
|
6
5
|
import { Helper } from '../../helpers/helper';
|
|
6
|
+
import { getStackedDataWithOwn } from '../bar/stackedData/dataStacker';
|
|
7
7
|
export class Area {
|
|
8
8
|
static render(block, scales, data, keyField, margin, keyAxisOrient, chart, blockSize) {
|
|
9
9
|
if (chart.isSegmented)
|
|
@@ -44,7 +44,7 @@ export class Area {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
static renderSegmented(block, scales, data, keyField, margin, keyAxisOrient, chart) {
|
|
47
|
-
const stackedData =
|
|
47
|
+
const stackedData = getStackedDataWithOwn(data, chart.data.valueFields.map(field => field.name));
|
|
48
48
|
const areaGenerator = AreaHelper.getSegmentedAreaGenerator(keyAxisOrient, scales, margin, keyField.name);
|
|
49
49
|
const areas = block.getChartGroup(chart.index)
|
|
50
50
|
.selectAll(`.${this.areaChartClass}${Helper.getCssClassesLine(chart.cssClasses)}`)
|
|
@@ -76,7 +76,7 @@ export class Area {
|
|
|
76
76
|
return promises;
|
|
77
77
|
}
|
|
78
78
|
static updateSegmented(block, scales, newData, keyField, margin, chart, keyAxisOrient) {
|
|
79
|
-
const stackedData =
|
|
79
|
+
const stackedData = getStackedDataWithOwn(newData, chart.data.valueFields.map(field => field.name));
|
|
80
80
|
const areaGenerator = AreaHelper.getSegmentedAreaGenerator(keyAxisOrient, scales, margin, keyField.name);
|
|
81
81
|
const areas = block.getChartGroup(chart.index)
|
|
82
82
|
.selectAll(`path.${this.areaChartClass}${Helper.getCssClassesLine(chart.cssClasses)}`)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { stack } from 'd3-shape';
|
|
2
1
|
import { select } from 'd3-selection';
|
|
3
2
|
import { EmbeddedLabels } from "../../features/embeddedLabels/embeddedLabels";
|
|
4
3
|
import { EmbeddedLabelsHelper } from "../../features/embeddedLabels/embeddedLabelsHelper";
|
|
@@ -6,6 +5,7 @@ import { BarHelper } from "./barHelper";
|
|
|
6
5
|
import { sum } from "d3-array";
|
|
7
6
|
import { DomHelper } from "../../helpers/domHelper";
|
|
8
7
|
import { Helper } from "../../helpers/helper";
|
|
8
|
+
import { getStackedDataWithOwn } from './stackedData/dataStacker';
|
|
9
9
|
export class Bar {
|
|
10
10
|
static render(block, scales, data, keyField, margin, keyAxisOrient, chart, blockSize, barSettings, barsAmounts, isSegmented, firstBarIndex) {
|
|
11
11
|
if (isSegmented)
|
|
@@ -52,7 +52,7 @@ export class Bar {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
static renderSegmented(block, scales, data, keyField, margin, keyAxisOrient, chart, barsAmounts, blockSize, firstBarIndex, barSettings) {
|
|
55
|
-
const stackedData =
|
|
55
|
+
const stackedData = getStackedDataWithOwn(data, chart.data.valueFields.map(field => field.name));
|
|
56
56
|
let groups = block.getChartGroup(chart.index)
|
|
57
57
|
.selectAll(`g.${this.barSegmentGroupClass}${Helper.getCssClassesLine(chart.cssClasses)}`)
|
|
58
58
|
.data(stackedData);
|
|
@@ -129,7 +129,7 @@ export class Bar {
|
|
|
129
129
|
return promises;
|
|
130
130
|
}
|
|
131
131
|
static updateSegmented(block, newData, scales, margin, keyAxisOrient, chart, blockSize, barsAmounts, keyField, firstBarIndex, barSettings) {
|
|
132
|
-
const stackedData =
|
|
132
|
+
const stackedData = getStackedDataWithOwn(newData, chart.data.valueFields.map(field => field.name));
|
|
133
133
|
block.getChartGroup(chart.index)
|
|
134
134
|
.selectAll(`.${this.barItemClass}${Helper.getCssClassesLine(chart.cssClasses)}`)
|
|
135
135
|
.filter(d => newData.findIndex(row => row[keyField.name] === d.data[keyField.name]) === -1)
|
|
@@ -84,20 +84,20 @@ export class BarHelper {
|
|
|
84
84
|
}
|
|
85
85
|
static setSegmentedBarAttrsByValue(attrs, keyAxisOrient, scaleValue, margin, blockSize) {
|
|
86
86
|
if (keyAxisOrient === 'top') {
|
|
87
|
-
attrs.y = d =>
|
|
88
|
-
attrs.height = d =>
|
|
87
|
+
attrs.y = d => scaleValue(Math.min(d[1], d[0])) + margin.top;
|
|
88
|
+
attrs.height = d => Math.abs(scaleValue(d[1]) - scaleValue(d[0]));
|
|
89
89
|
}
|
|
90
90
|
if (keyAxisOrient === 'bottom') {
|
|
91
|
-
attrs.y = d => scaleValue(d[1]) + margin.top;
|
|
92
|
-
attrs.height = d =>
|
|
91
|
+
attrs.y = d => scaleValue(Math.max(d[1], d[0])) + margin.top;
|
|
92
|
+
attrs.height = d => Math.abs(scaleValue(d[1]) - scaleValue(d[0]));
|
|
93
93
|
}
|
|
94
94
|
if (keyAxisOrient === 'left') {
|
|
95
|
-
attrs.x = d =>
|
|
96
|
-
attrs.width = d =>
|
|
95
|
+
attrs.x = d => scaleValue(Math.min(d[1], d[0])) + margin.left;
|
|
96
|
+
attrs.width = d => Math.abs(scaleValue(d[1]) - scaleValue(d[0]));
|
|
97
97
|
}
|
|
98
98
|
if (keyAxisOrient === 'right') {
|
|
99
|
-
attrs.x = d => scaleValue(d[1]) + margin.left;
|
|
100
|
-
attrs.width = d =>
|
|
99
|
+
attrs.x = d => scaleValue(Math.max(d[1], d[0])) + margin.left;
|
|
100
|
+
attrs.width = d => Math.abs(scaleValue(d[1]) - scaleValue(d[0]));
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MdtChartsDataRow } from "../../../../config/config";
|
|
2
|
+
export interface StackedDataRow {
|
|
3
|
+
0: number;
|
|
4
|
+
1: number;
|
|
5
|
+
data: MdtChartsDataRow;
|
|
6
|
+
}
|
|
7
|
+
export declare type StackedDataFull = StackedDataRow[][];
|
|
8
|
+
export declare class DataStacker {
|
|
9
|
+
private service;
|
|
10
|
+
getStackedData(rawData: MdtChartsDataRow[], valueFields: string[]): StackedDataFull;
|
|
11
|
+
}
|
|
12
|
+
export declare function getStackedDataWithOwn(rawData: MdtChartsDataRow[], valueFields: string[]): StackedDataFull;
|
|
13
|
+
export declare function getStackedDataWithD3(rawData: MdtChartsDataRow[], valueFields: string[]): StackedDataFull;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { stack } from "d3-shape";
|
|
2
|
+
import { DataStackerService } from "./dataStackerService";
|
|
3
|
+
export class DataStacker {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.service = new DataStackerService();
|
|
6
|
+
}
|
|
7
|
+
getStackedData(rawData, valueFields) {
|
|
8
|
+
const stackedData = [];
|
|
9
|
+
valueFields.forEach((vField, vfIndex) => {
|
|
10
|
+
const fieldStack = [];
|
|
11
|
+
rawData.forEach((dataRow, drIndex) => {
|
|
12
|
+
const valueFromData = dataRow[vField];
|
|
13
|
+
const value0 = this.service.getValue0(stackedData, vfIndex, drIndex, valueFromData);
|
|
14
|
+
const value1 = this.service.getValue1(value0, valueFromData);
|
|
15
|
+
fieldStack.push({
|
|
16
|
+
"0": value0,
|
|
17
|
+
"1": value1,
|
|
18
|
+
data: dataRow
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
stackedData.push(fieldStack);
|
|
22
|
+
});
|
|
23
|
+
return stackedData;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function getStackedDataWithOwn(rawData, valueFields) {
|
|
27
|
+
const stacker = new DataStacker();
|
|
28
|
+
return stacker.getStackedData(rawData, valueFields);
|
|
29
|
+
}
|
|
30
|
+
export function getStackedDataWithD3(rawData, valueFields) {
|
|
31
|
+
return stack().keys(valueFields)(rawData);
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { DataStacker } from "./dataStacker";
|
|
2
|
+
import { DataStackerService } from "./dataStackerService";
|
|
3
|
+
describe('DataStacker', () => {
|
|
4
|
+
const stacker = new DataStacker();
|
|
5
|
+
describe('getStackedData', () => {
|
|
6
|
+
describe('for positive values', () => {
|
|
7
|
+
let dataRows = [
|
|
8
|
+
{ price: 12, count: 30 },
|
|
9
|
+
{ price: 30, count: 12 },
|
|
10
|
+
{ price: 0, count: 100 },
|
|
11
|
+
{ price: 10, count: 0 }
|
|
12
|
+
];
|
|
13
|
+
const valueFields = ["price", "count"];
|
|
14
|
+
test('should return right stack', () => {
|
|
15
|
+
const res = stacker.getStackedData(dataRows, valueFields);
|
|
16
|
+
expect(res).toEqual([
|
|
17
|
+
[
|
|
18
|
+
{ "0": 0, "1": 12, data: dataRows[0] },
|
|
19
|
+
{ "0": 0, "1": 30, data: dataRows[1] },
|
|
20
|
+
{ "0": 0, "1": 0, data: dataRows[2] },
|
|
21
|
+
{ "0": 0, "1": 10, data: dataRows[3] },
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
{ "0": 12, "1": 42, data: dataRows[0] },
|
|
25
|
+
{ "0": 30, "1": 42, data: dataRows[1] },
|
|
26
|
+
{ "0": 0, "1": 100, data: dataRows[2] },
|
|
27
|
+
{ "0": 10, "1": 10, data: dataRows[3] },
|
|
28
|
+
]
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('for negative values', () => {
|
|
33
|
+
let dataRows = [
|
|
34
|
+
{ price: -12, count: -30 },
|
|
35
|
+
{ price: -30, count: -12 },
|
|
36
|
+
{ price: 0, count: -100 },
|
|
37
|
+
{ price: -10, count: 0 }
|
|
38
|
+
];
|
|
39
|
+
const valueFields = ["price", "count"];
|
|
40
|
+
test('should return right stack', () => {
|
|
41
|
+
const res = stacker.getStackedData(dataRows, valueFields);
|
|
42
|
+
expect(res).toEqual([
|
|
43
|
+
[
|
|
44
|
+
{ "0": 0, "1": -12, data: dataRows[0] },
|
|
45
|
+
{ "0": 0, "1": -30, data: dataRows[1] },
|
|
46
|
+
{ "0": 0, "1": 0, data: dataRows[2] },
|
|
47
|
+
{ "0": 0, "1": -10, data: dataRows[3] },
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
{ "0": -12, "1": -42, data: dataRows[0] },
|
|
51
|
+
{ "0": -30, "1": -42, data: dataRows[1] },
|
|
52
|
+
{ "0": 0, "1": -100, data: dataRows[2] },
|
|
53
|
+
{ "0": -10, "1": -10, data: dataRows[3] },
|
|
54
|
+
]
|
|
55
|
+
]);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('DataStackerService', () => {
|
|
61
|
+
const service = new DataStackerService();
|
|
62
|
+
let data = [
|
|
63
|
+
[
|
|
64
|
+
{ "0": 0, "1": 42, data: {} },
|
|
65
|
+
{ "0": 0, "1": -123, data: {} }
|
|
66
|
+
]
|
|
67
|
+
];
|
|
68
|
+
describe('getLastValue', () => {
|
|
69
|
+
test('should return 0 if vfIndex is 0', () => {
|
|
70
|
+
const res = service.getValue0(data, 0, 0, 0);
|
|
71
|
+
expect(res).toBe(0);
|
|
72
|
+
});
|
|
73
|
+
test('should return last positive values if positive value exists and need positive', () => {
|
|
74
|
+
const res = service.getValue0(data, 1, 0, 12);
|
|
75
|
+
expect(res).toBe(42);
|
|
76
|
+
});
|
|
77
|
+
test('should return 0 if positive value not exists and need positive', () => {
|
|
78
|
+
const res = service.getValue0(data, 1, 1, 12);
|
|
79
|
+
expect(res).toBe(0);
|
|
80
|
+
});
|
|
81
|
+
test('should return last negative value if negative value exists and need negative', () => {
|
|
82
|
+
const res = service.getValue0(data, 1, 1, -12);
|
|
83
|
+
expect(res).toBe(-123);
|
|
84
|
+
});
|
|
85
|
+
test('should return 0 if negative value not exists and need negative', () => {
|
|
86
|
+
const res = service.getValue0(data, 1, 0, -12);
|
|
87
|
+
expect(res).toBe(0);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('getValue1', () => {
|
|
91
|
+
test('should return sum of value0 + value from data', () => {
|
|
92
|
+
let res = service.getValue1(0, 12);
|
|
93
|
+
expect(res).toBe(12);
|
|
94
|
+
res = service.getValue1(-12, -30);
|
|
95
|
+
expect(res).toBe(-42);
|
|
96
|
+
res = service.getValue1(0, 30);
|
|
97
|
+
expect(res).toBe(30);
|
|
98
|
+
res = service.getValue1(0, -30);
|
|
99
|
+
expect(res).toBe(-30);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('real example (positive only)', () => {
|
|
104
|
+
const stacker = new DataStacker();
|
|
105
|
+
const data = [
|
|
106
|
+
{
|
|
107
|
+
$id: 1,
|
|
108
|
+
brand: "BMW BMW",
|
|
109
|
+
price: 100000,
|
|
110
|
+
count: 12000,
|
|
111
|
+
color: "red"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
$id: 2,
|
|
115
|
+
brand: "LADA",
|
|
116
|
+
price: 0,
|
|
117
|
+
count: 1000,
|
|
118
|
+
color: "green"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
$id: 3,
|
|
122
|
+
brand: "MERCEDES",
|
|
123
|
+
price: 15000,
|
|
124
|
+
count: 1200,
|
|
125
|
+
color: "blue"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
$id: 4,
|
|
129
|
+
brand: "AUDI",
|
|
130
|
+
price: 20000,
|
|
131
|
+
count: 500,
|
|
132
|
+
color: "yellow"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
$id: 5,
|
|
136
|
+
brand: "VOLKSWAGEN",
|
|
137
|
+
price: 115000,
|
|
138
|
+
count: 6000,
|
|
139
|
+
color: "cyan"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
$id: 6,
|
|
143
|
+
brand: "DODGE",
|
|
144
|
+
price: 115000,
|
|
145
|
+
count: 4000,
|
|
146
|
+
color: "red"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
$id: 7,
|
|
150
|
+
brand: "SAAB",
|
|
151
|
+
price: 50000,
|
|
152
|
+
count: 11000,
|
|
153
|
+
color: "orange"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
$id: 8,
|
|
157
|
+
brand: "HONDA",
|
|
158
|
+
price: 20000,
|
|
159
|
+
count: 2000,
|
|
160
|
+
color: "brown"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
$id: 9,
|
|
164
|
+
brand: "TOYOTA",
|
|
165
|
+
price: 40000,
|
|
166
|
+
count: 15000,
|
|
167
|
+
color: "pink"
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
const valueFields = ["price", "count"];
|
|
171
|
+
const stackedData = [
|
|
172
|
+
[
|
|
173
|
+
{ "0": 0, "1": 100000, data: data[0] },
|
|
174
|
+
{ "0": 0, "1": 0, data: data[1] },
|
|
175
|
+
{ "0": 0, "1": 15000, data: data[2] },
|
|
176
|
+
{ "0": 0, "1": 20000, data: data[3] },
|
|
177
|
+
{ "0": 0, "1": 115000, data: data[4] },
|
|
178
|
+
{ "0": 0, "1": 115000, data: data[5] },
|
|
179
|
+
{ "0": 0, "1": 50000, data: data[6] },
|
|
180
|
+
{ "0": 0, "1": 20000, data: data[7] },
|
|
181
|
+
{ "0": 0, "1": 40000, data: data[8] }
|
|
182
|
+
],
|
|
183
|
+
[
|
|
184
|
+
{ "0": 100000, "1": 112000, data: data[0] },
|
|
185
|
+
{ "0": 0, "1": 1000, data: data[1] },
|
|
186
|
+
{ "0": 15000, "1": 16200, data: data[2] },
|
|
187
|
+
{ "0": 20000, "1": 20500, data: data[3] },
|
|
188
|
+
{ "0": 115000, "1": 121000, data: data[4] },
|
|
189
|
+
{ "0": 115000, "1": 119000, data: data[5] },
|
|
190
|
+
{ "0": 50000, "1": 61000, data: data[6] },
|
|
191
|
+
{ "0": 20000, "1": 22000, data: data[7] },
|
|
192
|
+
{ "0": 40000, "1": 55000, data: data[8] }
|
|
193
|
+
]
|
|
194
|
+
];
|
|
195
|
+
test('check on real example', () => {
|
|
196
|
+
const res = stacker.getStackedData(data, valueFields);
|
|
197
|
+
expect(res).toEqual(stackedData);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class DataStackerService {
|
|
2
|
+
getValue0(stackedData, vfIndex, drIndex, valueFromData) {
|
|
3
|
+
if (vfIndex === 0)
|
|
4
|
+
return 0;
|
|
5
|
+
for (let i = vfIndex - 1; i >= 0; i--) {
|
|
6
|
+
if (valueFromData === 0) {
|
|
7
|
+
return stackedData[i][drIndex][1];
|
|
8
|
+
}
|
|
9
|
+
const needed = stackedData[i][drIndex][1] >= 0 === valueFromData >= 0;
|
|
10
|
+
if (needed) {
|
|
11
|
+
return stackedData[i][drIndex][1];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
getValue1(value0, valueFromData) {
|
|
17
|
+
return value0 + valueFromData;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -20,8 +20,9 @@ export class TwoDimensionalManager {
|
|
|
20
20
|
engine.block.scales = scales;
|
|
21
21
|
engine.block.renderSvg(model.blockCanvas.size);
|
|
22
22
|
Axis.render(engine.block, scales, options.scale, options.axis, model.blockCanvas.size);
|
|
23
|
-
GridLine.render(engine.block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin,
|
|
23
|
+
GridLine.render(engine.block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin, scales);
|
|
24
24
|
this.renderCharts(engine.block, options.charts, scales, engine.data, options.data, model.chartBlock.margin, options.axis.key.orient, options.chartSettings.bar, model.blockCanvas.size);
|
|
25
|
+
Axis.raiseKeyAxis(engine.block, options.axis.key);
|
|
25
26
|
engine.block.filterEventManager.registerEventFor2D(scales.key, model.chartBlock.margin, model.blockCanvas.size, options);
|
|
26
27
|
engine.block.filterEventManager.event2DUpdate(options);
|
|
27
28
|
Title.render(engine.block, options.title, model.otherComponents.titleBlock, model.blockCanvas.size);
|
|
@@ -49,7 +50,7 @@ export class TwoDimensionalManager {
|
|
|
49
50
|
const keyDomainEquality = Helper.checkDomainsEquality(block.scales.key.domain(), scales.key.domain());
|
|
50
51
|
block.scales = scales;
|
|
51
52
|
Axis.update(block, scales, options.scale, options.axis, model.blockCanvas.size, keyDomainEquality);
|
|
52
|
-
GridLine.update(block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin,
|
|
53
|
+
GridLine.update(block, options.additionalElements.gridLine.flag, options.axis, model.blockCanvas.size, model.chartBlock.margin, scales);
|
|
53
54
|
const promises = this.updateCharts(block, options.charts, scales, data, model.options.data, model.chartBlock.margin, options.axis.key.orient, model.blockCanvas.size, options.chartSettings.bar);
|
|
54
55
|
Promise.all(promises)
|
|
55
56
|
.then(() => {
|
|
@@ -111,14 +111,17 @@ export class AxisModel {
|
|
|
111
111
|
let translateY;
|
|
112
112
|
let translateX;
|
|
113
113
|
if (chartOrientation === "vertical") {
|
|
114
|
-
translateY = getZeroCoordinate
|
|
114
|
+
translateY = getZeroCoordinate
|
|
115
|
+
? getZeroCoordinate() + canvasModel.getMarginSide("top")
|
|
116
|
+
: AxisModel.getAxisTranslateY(AxisType.Key, chartOrientation, axisPosition, canvasModel);
|
|
115
117
|
translateX = AxisModel.getAxisTranslateX(AxisType.Key, chartOrientation, axisPosition, canvasModel);
|
|
116
118
|
}
|
|
117
119
|
else {
|
|
118
|
-
translateX = getZeroCoordinate
|
|
120
|
+
translateX = getZeroCoordinate
|
|
121
|
+
? getZeroCoordinate() + canvasModel.getMarginSide("left")
|
|
122
|
+
: AxisModel.getAxisTranslateX(AxisType.Key, chartOrientation, axisPosition, canvasModel);
|
|
119
123
|
translateY = AxisModel.getAxisTranslateY(AxisType.Key, chartOrientation, axisPosition, canvasModel);
|
|
120
124
|
}
|
|
121
|
-
console.log(translateX, translateY);
|
|
122
125
|
return {
|
|
123
126
|
translateX,
|
|
124
127
|
translateY
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ScaleLinear, ScaleTime } from "d3-scale";
|
|
2
|
+
import { ChartOrientation, DiscreteAxisOptions } from "../../../config/config";
|
|
3
|
+
import { ScaleValueModel } from "../../model";
|
|
4
|
+
import { CanvasModel } from "../../modelInstance/canvasModel/canvasModel";
|
|
5
|
+
export declare const keyAxisLabelVerticalLog = "keyAxisLabel_vertical_margin_log";
|
|
6
|
+
export declare const keyAxisLabelHorizontalLog = "keyAxisLabel_horizontal_margin_log";
|
|
7
|
+
interface ScaleInfo {
|
|
8
|
+
scale: ScaleValueModel;
|
|
9
|
+
scaleFn: ScaleLinear<number, number, never> | ScaleTime<number, number, never>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Предназначен для получения нового scaleValue и уменьшения globalMargin, если ось ключей находится где-то внутри chartBlock, а не на его границе
|
|
13
|
+
*/
|
|
14
|
+
export declare class ScaleAxisRecalcer {
|
|
15
|
+
private generateScaleLinear;
|
|
16
|
+
constructor(generateScaleLinear: () => ScaleValueModel);
|
|
17
|
+
recalculateMargin(canvasModel: CanvasModel, chartOrientation: ChartOrientation, keyAxis: DiscreteAxisOptions): void;
|
|
18
|
+
getScaleValue(): ScaleInfo;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Scale } from "../../../engine/features/scale/scale";
|
|
2
|
+
export const keyAxisLabelVerticalLog = "keyAxisLabel_vertical_margin_log";
|
|
3
|
+
export const keyAxisLabelHorizontalLog = "keyAxisLabel_horizontal_margin_log";
|
|
4
|
+
/**
|
|
5
|
+
* Предназначен для получения нового scaleValue и уменьшения globalMargin, если ось ключей находится где-то внутри chartBlock, а не на его границе
|
|
6
|
+
*/
|
|
7
|
+
export class ScaleAxisRecalcer {
|
|
8
|
+
constructor(generateScaleLinear) {
|
|
9
|
+
this.generateScaleLinear = generateScaleLinear;
|
|
10
|
+
}
|
|
11
|
+
recalculateMargin(canvasModel, chartOrientation, keyAxis) {
|
|
12
|
+
const scaleValue = this.generateScaleLinear();
|
|
13
|
+
//TODO: rm import from engine
|
|
14
|
+
const scaleValueFn = Scale.getScaleValue(scaleValue);
|
|
15
|
+
const coordinateOnChartBlock = (keyAxis.position === "start"
|
|
16
|
+
? scaleValueFn(0)
|
|
17
|
+
: (chartOrientation === "vertical"
|
|
18
|
+
? canvasModel.getChartBlockHeight()
|
|
19
|
+
: canvasModel.getChartBlockWidth()) - scaleValueFn(0));
|
|
20
|
+
const key = chartOrientation === "vertical" ? keyAxisLabelVerticalLog : keyAxisLabelHorizontalLog;
|
|
21
|
+
const logInfo = canvasModel.marginService.getDataByKey(key);
|
|
22
|
+
canvasModel.decreaseMarginSide(logInfo.side, logInfo.byValue - coordinateOnChartBlock < 0 ? logInfo.byValue : coordinateOnChartBlock);
|
|
23
|
+
}
|
|
24
|
+
getScaleValue() {
|
|
25
|
+
const scale = this.generateScaleLinear();
|
|
26
|
+
return {
|
|
27
|
+
scale,
|
|
28
|
+
scaleFn: Scale.getScaleValue(scale)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ScaleKeyModel, ScaleKeyType, ScaleValueModel, ScaleValueType } from "../../model";
|
|
2
|
+
import { AxisPosition, NumberDomain, IntervalChart, MdtChartsTwoDimensionalChart, MdtChartsTwoDimensionalOptions, ChartOrientation, MdtChartsDataSource, MdtChartsDataRow } from "../../../config/config";
|
|
3
|
+
import { CanvasModel } from "../../modelInstance/canvasModel/canvasModel";
|
|
4
|
+
export declare enum ScaleType {
|
|
5
|
+
Key = 0,
|
|
6
|
+
Value = 1
|
|
7
|
+
}
|
|
8
|
+
export declare class ScaleModel {
|
|
9
|
+
static getScaleKey(allowableKeys: string[], orient: ChartOrientation, canvasModel: CanvasModel, charts: MdtChartsTwoDimensionalChart[], barCharts: MdtChartsTwoDimensionalChart[]): ScaleKeyModel;
|
|
10
|
+
static getScaleLinear(options: MdtChartsTwoDimensionalOptions, data: MdtChartsDataSource, canvasModel: CanvasModel): ScaleValueModel;
|
|
11
|
+
static getRangePeek(scaleType: ScaleType, chartOrientation: string, canvasModel: CanvasModel): number;
|
|
12
|
+
static getDateValueDomain(data: MdtChartsDataSource, chart: IntervalChart, keyAxisPosition: AxisPosition, dataSource: string): [Date, Date];
|
|
13
|
+
static getLinearDomain(configDomain: NumberDomain, data: MdtChartsDataSource, configOptions: MdtChartsTwoDimensionalOptions): [number, number];
|
|
14
|
+
static getScaleKeyType(charts: MdtChartsTwoDimensionalChart[]): ScaleKeyType;
|
|
15
|
+
static getScaleValueType(charts: MdtChartsTwoDimensionalChart[] | IntervalChart[]): ScaleValueType;
|
|
16
|
+
static getElementsAmount(barCharts: MdtChartsTwoDimensionalChart[]): number;
|
|
17
|
+
static getScaleMaxValue(charts: MdtChartsTwoDimensionalChart[], dataRows: MdtChartsDataRow[]): number;
|
|
18
|
+
static getScaleMinValue(charts: MdtChartsTwoDimensionalChart[], dataRows: MdtChartsDataRow[]): number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ModelHelper } from "../../modelHelper";
|
|
2
|
+
export var ScaleType;
|
|
3
|
+
(function (ScaleType) {
|
|
4
|
+
ScaleType[ScaleType["Key"] = 0] = "Key";
|
|
5
|
+
ScaleType[ScaleType["Value"] = 1] = "Value";
|
|
6
|
+
})(ScaleType || (ScaleType = {}));
|
|
7
|
+
export class ScaleModel {
|
|
8
|
+
static getScaleKey(allowableKeys, orient, canvasModel, charts, barCharts) {
|
|
9
|
+
return {
|
|
10
|
+
domain: allowableKeys,
|
|
11
|
+
range: {
|
|
12
|
+
start: 0,
|
|
13
|
+
end: ScaleModel.getRangePeek(ScaleType.Key, orient, canvasModel)
|
|
14
|
+
},
|
|
15
|
+
type: ScaleModel.getScaleKeyType(charts),
|
|
16
|
+
elementsAmount: this.getElementsAmount(barCharts)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
static getScaleLinear(options, data, canvasModel) {
|
|
20
|
+
return {
|
|
21
|
+
domain: ScaleModel.getLinearDomain(options.axis.value.domain, data, options),
|
|
22
|
+
range: {
|
|
23
|
+
start: 0,
|
|
24
|
+
end: ScaleModel.getRangePeek(ScaleType.Value, options.orientation, canvasModel)
|
|
25
|
+
},
|
|
26
|
+
type: ScaleModel.getScaleValueType(options.charts)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
static getRangePeek(scaleType, chartOrientation, canvasModel) {
|
|
30
|
+
if (chartOrientation === 'vertical')
|
|
31
|
+
return scaleType === ScaleType.Key
|
|
32
|
+
? canvasModel.getChartBlockWidth()
|
|
33
|
+
: canvasModel.getChartBlockHeight();
|
|
34
|
+
return scaleType === ScaleType.Key
|
|
35
|
+
? canvasModel.getChartBlockHeight()
|
|
36
|
+
: canvasModel.getChartBlockWidth();
|
|
37
|
+
}
|
|
38
|
+
static getDateValueDomain(data, chart, keyAxisPosition, dataSource) {
|
|
39
|
+
const minMax = ModelHelper.getMinAndMaxOfIntervalData(data, dataSource, chart);
|
|
40
|
+
let domainPeekMin = minMax[0];
|
|
41
|
+
let domainPeekMax = minMax[1];
|
|
42
|
+
if (keyAxisPosition === 'start')
|
|
43
|
+
return [domainPeekMin, domainPeekMax];
|
|
44
|
+
return [domainPeekMax, domainPeekMin];
|
|
45
|
+
}
|
|
46
|
+
static getLinearDomain(configDomain, data, configOptions) {
|
|
47
|
+
let domainPeekMin;
|
|
48
|
+
let domainPeekMax;
|
|
49
|
+
if (configDomain.start === -1)
|
|
50
|
+
domainPeekMin = this.getScaleMinValue(configOptions.charts, data[configOptions.data.dataSource]);
|
|
51
|
+
else
|
|
52
|
+
domainPeekMin = configDomain.start;
|
|
53
|
+
if (configDomain.end === -1)
|
|
54
|
+
domainPeekMax = this.getScaleMaxValue(configOptions.charts, data[configOptions.data.dataSource]);
|
|
55
|
+
else
|
|
56
|
+
domainPeekMax = configDomain.end;
|
|
57
|
+
if (configOptions.axis.key.position === 'start')
|
|
58
|
+
return [domainPeekMin, domainPeekMax];
|
|
59
|
+
return [domainPeekMax, domainPeekMin];
|
|
60
|
+
}
|
|
61
|
+
static getScaleKeyType(charts) {
|
|
62
|
+
if (charts.findIndex((chart) => chart.type === 'bar') === -1)
|
|
63
|
+
return 'point';
|
|
64
|
+
return 'band';
|
|
65
|
+
}
|
|
66
|
+
static getScaleValueType(charts) {
|
|
67
|
+
if (charts.findIndex((chart) => chart.type === 'gantt') !== -1)
|
|
68
|
+
return 'datetime';
|
|
69
|
+
return 'linear';
|
|
70
|
+
}
|
|
71
|
+
static getElementsAmount(barCharts) {
|
|
72
|
+
if (barCharts.length === 0)
|
|
73
|
+
return 1;
|
|
74
|
+
let barsAmount = 0;
|
|
75
|
+
barCharts.forEach(chart => {
|
|
76
|
+
if (chart.isSegmented)
|
|
77
|
+
barsAmount += 1; // Если бар сегментированный, то все valueFields являются частями одного бара
|
|
78
|
+
else
|
|
79
|
+
barsAmount += chart.data.valueFields.length;
|
|
80
|
+
});
|
|
81
|
+
return barsAmount;
|
|
82
|
+
}
|
|
83
|
+
static getScaleMaxValue(charts, dataRows) {
|
|
84
|
+
let max = 0;
|
|
85
|
+
charts.forEach(chart => {
|
|
86
|
+
dataRows.forEach(dataRow => {
|
|
87
|
+
let sumInRow = 0;
|
|
88
|
+
chart.data.valueFields.forEach(field => {
|
|
89
|
+
if (chart.isSegmented && dataRow[field.name] > 0)
|
|
90
|
+
sumInRow += dataRow[field.name];
|
|
91
|
+
else if (dataRow[field.name] > sumInRow)
|
|
92
|
+
sumInRow = dataRow[field.name];
|
|
93
|
+
});
|
|
94
|
+
if (max < sumInRow)
|
|
95
|
+
max = sumInRow;
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return max;
|
|
99
|
+
}
|
|
100
|
+
static getScaleMinValue(charts, dataRows) {
|
|
101
|
+
let min = 0;
|
|
102
|
+
charts.forEach(chart => {
|
|
103
|
+
dataRows.forEach(dataRow => {
|
|
104
|
+
let sumInRow = 0;
|
|
105
|
+
chart.data.valueFields.forEach(field => {
|
|
106
|
+
if (chart.isSegmented && dataRow[field.name] < 0) {
|
|
107
|
+
sumInRow += dataRow[field.name];
|
|
108
|
+
}
|
|
109
|
+
else if (dataRow[field.name] < sumInRow)
|
|
110
|
+
sumInRow = dataRow[field.name];
|
|
111
|
+
});
|
|
112
|
+
if (min > sumInRow)
|
|
113
|
+
min = sumInRow;
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
return min;
|
|
117
|
+
}
|
|
118
|
+
}
|
package/lib/model/marginModel.js
CHANGED
|
@@ -3,6 +3,7 @@ import { DataManagerModel } from "./dataManagerModel/dataManagerModel";
|
|
|
3
3
|
import { LegendModel } from "./featuresModel/legendModel/legendModel";
|
|
4
4
|
import { AxisType } from "./modelBuilder";
|
|
5
5
|
import { TwoDimensionalModel } from "./notations/twoDimensionalModel";
|
|
6
|
+
import { keyAxisLabelHorizontalLog, keyAxisLabelVerticalLog } from "./featuresModel/scaleModel/scaleAxisRecalcer";
|
|
6
7
|
export const AXIS_HORIZONTAL_LABEL_PADDING = 15;
|
|
7
8
|
export const AXIS_VERTICAL_LABEL_PADDING = 10;
|
|
8
9
|
export class MarginModel {
|
|
@@ -28,8 +29,10 @@ export class MarginModel {
|
|
|
28
29
|
const axisLabelSize = AxisModel.getLabelSize(designerConfig.canvas.axisLabel.maxSize.main, dataScope.allowableKeys);
|
|
29
30
|
const axisConfig = AxisModel.getKeyAxisLabelPosition(modelInstance.canvasModel, dataScope.allowableKeys.length, config.options.axis.key);
|
|
30
31
|
const marginOrient = config.options.axis.key.position === 'end' ? 'bottom' : 'top';
|
|
31
|
-
if (axisConfig === 'rotated')
|
|
32
|
-
modelInstance.canvasModel.
|
|
32
|
+
if (axisConfig === 'rotated') {
|
|
33
|
+
modelInstance.canvasModel.decreaseMarginSide(marginOrient, axisLabelSize.height);
|
|
34
|
+
modelInstance.canvasModel.increaseMarginSide(marginOrient, axisLabelSize.width, keyAxisLabelVerticalLog);
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
static getHorizontalMarginByAxisLabels(labelsMaxWidth, axis, data, options) {
|
|
@@ -48,7 +51,7 @@ export class MarginModel {
|
|
|
48
51
|
const valueAxisOrient = AxisModel.getAxisOrient(AxisType.Value, orientation, axis.value.position);
|
|
49
52
|
if ((keyAxisOrient === 'bottom' || keyAxisOrient === 'top')) {
|
|
50
53
|
if (axis.key.visibility)
|
|
51
|
-
canvasModel.increaseMarginSide(keyAxisOrient, labelSize.height + AXIS_HORIZONTAL_LABEL_PADDING);
|
|
54
|
+
canvasModel.increaseMarginSide(keyAxisOrient, labelSize.height + AXIS_HORIZONTAL_LABEL_PADDING, keyAxisLabelVerticalLog);
|
|
52
55
|
}
|
|
53
56
|
else if (axis.value.visibility)
|
|
54
57
|
canvasModel.increaseMarginSide(valueAxisOrient, labelSize.height + AXIS_HORIZONTAL_LABEL_PADDING);
|
|
@@ -57,7 +60,7 @@ export class MarginModel {
|
|
|
57
60
|
const keyAxisOrient = AxisModel.getAxisOrient(AxisType.Key, orientation, axis.key.position);
|
|
58
61
|
const valueAxisOrient = AxisModel.getAxisOrient(AxisType.Value, orientation, axis.value.position);
|
|
59
62
|
if ((keyAxisOrient === 'left' || keyAxisOrient === 'right') && isShow && axis.key.visibility) {
|
|
60
|
-
canvasModel.increaseMarginSide(keyAxisOrient, labelSize.width + AXIS_VERTICAL_LABEL_PADDING);
|
|
63
|
+
canvasModel.increaseMarginSide(keyAxisOrient, labelSize.width + AXIS_VERTICAL_LABEL_PADDING, keyAxisLabelHorizontalLog);
|
|
61
64
|
}
|
|
62
65
|
else if ((valueAxisOrient === 'left' || valueAxisOrient === 'right') && axis.value.visibility) {
|
|
63
66
|
canvasModel.increaseMarginSide(valueAxisOrient, labelSize.width + AXIS_VERTICAL_LABEL_PADDING);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Size } from "../../../config/config";
|
|
2
2
|
import { BlockMargin } from "../../model";
|
|
3
3
|
import { LegendCanvasModelInstance } from "./legendCanvasModel";
|
|
4
|
+
import { MarginModelService } from "./marginModelService";
|
|
4
5
|
import { TitleCanvasModel } from "./titleCanvas";
|
|
5
|
-
declare type MarginSide = keyof BlockMargin;
|
|
6
|
+
export declare type MarginSide = keyof BlockMargin;
|
|
6
7
|
export declare class CanvasModel {
|
|
7
8
|
titleCanvas: TitleCanvasModel;
|
|
8
9
|
legendCanvas: LegendCanvasModelInstance;
|
|
10
|
+
marginService: MarginModelService;
|
|
9
11
|
private blockSize;
|
|
10
12
|
private margin;
|
|
11
13
|
constructor();
|
|
@@ -13,12 +15,11 @@ export declare class CanvasModel {
|
|
|
13
15
|
getMargin(): BlockMargin;
|
|
14
16
|
getMarginSide(side: MarginSide): number;
|
|
15
17
|
setMarginSide(side: MarginSide, size: number): void;
|
|
16
|
-
increaseMarginSide(side: MarginSide, byValue: number): void;
|
|
17
|
-
|
|
18
|
+
increaseMarginSide(side: MarginSide, byValue: number, key?: string): void;
|
|
19
|
+
decreaseMarginSide(side: MarginSide, byValue: number): void;
|
|
18
20
|
roundMargin(): void;
|
|
19
21
|
initBlockSize(blockSize: Size): void;
|
|
20
22
|
getBlockSize(): Size;
|
|
21
23
|
getChartBlockWidth(): number;
|
|
22
24
|
getChartBlockHeight(): number;
|
|
23
25
|
}
|
|
24
|
-
export {};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { LegendCanvasModelInstance } from "./legendCanvasModel";
|
|
2
|
+
import { MarginModelService } from "./marginModelService";
|
|
2
3
|
import { TitleCanvasModel } from "./titleCanvas";
|
|
3
4
|
export class CanvasModel {
|
|
4
5
|
constructor() {
|
|
5
6
|
this.titleCanvas = new TitleCanvasModel();
|
|
6
7
|
this.legendCanvas = new LegendCanvasModelInstance();
|
|
8
|
+
this.marginService = new MarginModelService();
|
|
7
9
|
}
|
|
8
10
|
initMargin(margin) {
|
|
9
11
|
this.margin = margin;
|
|
@@ -17,10 +19,12 @@ export class CanvasModel {
|
|
|
17
19
|
setMarginSide(side, size) {
|
|
18
20
|
this.margin[side] = size;
|
|
19
21
|
}
|
|
20
|
-
increaseMarginSide(side, byValue) {
|
|
22
|
+
increaseMarginSide(side, byValue, key) {
|
|
21
23
|
this.margin[side] += byValue;
|
|
24
|
+
if (key)
|
|
25
|
+
this.marginService.appendLog(key, side, byValue);
|
|
22
26
|
}
|
|
23
|
-
|
|
27
|
+
decreaseMarginSide(side, byValue) {
|
|
24
28
|
this.margin[side] -= byValue;
|
|
25
29
|
}
|
|
26
30
|
roundMargin() {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MarginSide } from "./canvasModel";
|
|
2
|
+
export interface MarginLogData {
|
|
3
|
+
side: MarginSide;
|
|
4
|
+
byValue: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class MarginModelService {
|
|
7
|
+
private log;
|
|
8
|
+
appendLog(key: string, side: MarginSide, byValue: number): void;
|
|
9
|
+
getDataByKey(key: string): MarginLogData;
|
|
10
|
+
private findLogByKey;
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class MarginModelService {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.log = [];
|
|
4
|
+
}
|
|
5
|
+
appendLog(key, side, byValue) {
|
|
6
|
+
const log = this.findLogByKey(key);
|
|
7
|
+
if (log) {
|
|
8
|
+
log.data = { side, byValue };
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
this.log.push({
|
|
12
|
+
key,
|
|
13
|
+
data: {
|
|
14
|
+
side,
|
|
15
|
+
byValue
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
getDataByKey(key) {
|
|
20
|
+
var _a;
|
|
21
|
+
return (_a = this.findLogByKey(key)) === null || _a === void 0 ? void 0 : _a.data;
|
|
22
|
+
}
|
|
23
|
+
findLogByKey(key) {
|
|
24
|
+
return this.log.find(l => l.key === key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -2,7 +2,7 @@ import { AxisModel } from "../featuresModel/axisModel";
|
|
|
2
2
|
import { ChartStyleModelService } from "../chartStyleModel/chartStyleModel";
|
|
3
3
|
import { DataManagerModel } from "../dataManagerModel/dataManagerModel";
|
|
4
4
|
import { AxisType } from "../modelBuilder";
|
|
5
|
-
import { ScaleModel, ScaleType } from "../featuresModel/scaleModel";
|
|
5
|
+
import { ScaleModel, ScaleType } from "../featuresModel/scaleModel/scaleModel";
|
|
6
6
|
import { TwoDimensionalModel } from "./twoDimensionalModel";
|
|
7
7
|
export class IntervalModel {
|
|
8
8
|
static getOptions(config, designerConfig, margin, dataScope, data, modelInstance) {
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import { Scale } from "../../engine/features/scale/scale";
|
|
2
1
|
import { ChartStyleModelService } from "../chartStyleModel/chartStyleModel";
|
|
3
2
|
import { TwoDimensionalChartStyleModel } from "../chartStyleModel/TwoDimensionalChartStyleModel";
|
|
4
3
|
import { AxisModel } from "../featuresModel/axisModel";
|
|
5
|
-
import {
|
|
4
|
+
import { ScaleAxisRecalcer } from "../featuresModel/scaleModel/scaleAxisRecalcer";
|
|
5
|
+
import { ScaleModel } from "../featuresModel/scaleModel/scaleModel";
|
|
6
6
|
export class TwoDimensionalModel {
|
|
7
7
|
static getOptions(options, designerConfig, data, modelInstance) {
|
|
8
8
|
const canvasModel = modelInstance.canvasModel;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
//TODO: rm import from engine
|
|
13
|
-
const scaleValueFn = Scale.getScaleValue(scaleValue);
|
|
9
|
+
const scaleMarginRecalcer = new ScaleAxisRecalcer(() => ScaleModel.getScaleLinear(options, data, canvasModel));
|
|
10
|
+
scaleMarginRecalcer.recalculateMargin(canvasModel, options.orientation, options.axis.key);
|
|
11
|
+
const scaleValueInfo = scaleMarginRecalcer.getScaleValue();
|
|
14
12
|
return {
|
|
15
13
|
legend: canvasModel.legendCanvas.getModel(),
|
|
16
14
|
title: options.title,
|
|
17
15
|
selectable: !!options.selectable,
|
|
18
16
|
orient: options.orientation,
|
|
19
17
|
scale: {
|
|
20
|
-
key:
|
|
21
|
-
value:
|
|
18
|
+
key: ScaleModel.getScaleKey(modelInstance.dataModel.getAllowableKeys(), options.orientation, canvasModel, options.charts, this.getChartsByType(options.charts, 'bar')),
|
|
19
|
+
value: scaleValueInfo.scale
|
|
22
20
|
},
|
|
23
21
|
axis: {
|
|
24
|
-
key: AxisModel.getKeyAxis(options, data, designerConfig.canvas.axisLabel, canvasModel, designerConfig.elementsOptions.tooltip, () =>
|
|
22
|
+
key: AxisModel.getKeyAxis(options, data, designerConfig.canvas.axisLabel, canvasModel, designerConfig.elementsOptions.tooltip, () => scaleValueInfo.scaleFn(0)),
|
|
25
23
|
value: AxisModel.getValueAxis(options.orientation, options.axis.value, designerConfig.canvas.axisLabel, canvasModel)
|
|
26
24
|
},
|
|
27
25
|
type: options.type,
|
|
@@ -29,7 +27,7 @@ export class TwoDimensionalModel {
|
|
|
29
27
|
charts: this.getChartsModel(options.charts, options.orientation, designerConfig.chartStyle),
|
|
30
28
|
additionalElements: this.getAdditionalElements(options),
|
|
31
29
|
tooltip: options.tooltip,
|
|
32
|
-
chartSettings
|
|
30
|
+
chartSettings: this.getChartsSettings(designerConfig.canvas.chartOptions.bar)
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
33
|
static getChartsEmbeddedLabelsFlag(charts, chartOrientation) {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
/* will-change: opacity; */
|
|
63
63
|
}
|
|
64
64
|
.legend-block-column .legend-item:not(:first-of-type) {
|
|
65
|
-
margin-top:
|
|
65
|
+
margin-top: 10px;
|
|
66
66
|
}
|
|
67
67
|
.legend-item-inline {
|
|
68
68
|
white-space: nowrap;
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
margin-left: 20px;
|
|
74
74
|
}
|
|
75
75
|
.legend-wrapper-with-wrap .legend-item-inline {
|
|
76
|
-
margin:
|
|
76
|
+
margin: 2px;
|
|
77
77
|
}
|
|
78
78
|
.legend-item-row {
|
|
79
79
|
display: flex;
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
/* Record overflow */
|
|
222
222
|
.record-overflow-alert {
|
|
223
223
|
background-color: #FFFFFF;;
|
|
224
|
-
padding:
|
|
224
|
+
padding: 2px 8px;
|
|
225
225
|
border: 1px solid #0F6698;
|
|
226
226
|
box-sizing: border-box;
|
|
227
227
|
border-radius: 100px;
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
/* will-change: opacity; */
|
|
63
63
|
}
|
|
64
64
|
.legend-block-column .legend-item:not(:first-of-type) {
|
|
65
|
-
margin-top:
|
|
65
|
+
margin-top: 10px;
|
|
66
66
|
}
|
|
67
67
|
.legend-item-inline {
|
|
68
68
|
white-space: nowrap;
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
margin-left: 20px;
|
|
74
74
|
}
|
|
75
75
|
.legend-wrapper-with-wrap .legend-item-inline {
|
|
76
|
-
margin:
|
|
76
|
+
margin: 2px;
|
|
77
77
|
}
|
|
78
78
|
.legend-item-row {
|
|
79
79
|
display: flex;
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
/* Record overflow */
|
|
222
222
|
.record-overflow-alert {
|
|
223
223
|
background-color: #FFFFFF;;
|
|
224
|
-
padding:
|
|
224
|
+
padding: 2px 8px;
|
|
225
225
|
border: 1px solid #0F6698;
|
|
226
226
|
box-sizing: border-box;
|
|
227
227
|
border-radius: 100px;
|