mdt-charts 1.12.7 → 1.12.8

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.
@@ -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 = stack().keys(chart.data.valueFields.map(field => field.name))(data);
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 = stack().keys(chart.data.valueFields.map(field => field.name))(newData);
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 = stack().keys(chart.data.valueFields.map(field => field.name))(data);
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 = stack().keys(chart.data.valueFields.map(field => field.name))(newData);
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 => margin.top + scaleValue(d[0]);
88
- attrs.height = d => Helper.getValueOrZero(scaleValue(d[1] - d[0]));
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 => Helper.getValueOrZero(blockSize.height - margin.top - margin.bottom - scaleValue(d[1] - d[0]));
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 => margin.left + scaleValue(d[0]) + 1;
96
- attrs.width = d => Helper.getValueOrZero(scaleValue(d[1] - d[0]));
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 => Helper.getValueOrZero(blockSize.width - margin.left - margin.right - scaleValue(d[1] - d[0]));
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,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,5 @@
1
+ import { StackedDataFull } from "./dataStacker";
2
+ export declare class DataStackerService {
3
+ getValue0(stackedData: StackedDataFull, vfIndex: number, drIndex: number, valueFromData: number): number;
4
+ getValue1(value0: number, valueFromData: number): number;
5
+ }
@@ -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
+ }
@@ -111,11 +111,15 @@ export class AxisModel {
111
111
  let translateY;
112
112
  let translateX;
113
113
  if (chartOrientation === "vertical") {
114
- translateY = getZeroCoordinate() + canvasModel.getMarginSide("top");
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() + canvasModel.getMarginSide("left");
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
125
  return {
@@ -86,7 +86,7 @@ export class ScaleModel {
86
86
  dataRows.forEach(dataRow => {
87
87
  let sumInRow = 0;
88
88
  chart.data.valueFields.forEach(field => {
89
- if (chart.isSegmented)
89
+ if (chart.isSegmented && dataRow[field.name] > 0)
90
90
  sumInRow += dataRow[field.name];
91
91
  else if (dataRow[field.name] > sumInRow)
92
92
  sumInRow = dataRow[field.name];
@@ -103,7 +103,10 @@ export class ScaleModel {
103
103
  dataRows.forEach(dataRow => {
104
104
  let sumInRow = 0;
105
105
  chart.data.valueFields.forEach(field => {
106
- if (dataRow[field.name] < sumInRow)
106
+ if (chart.isSegmented && dataRow[field.name] < 0) {
107
+ sumInRow += dataRow[field.name];
108
+ }
109
+ else if (dataRow[field.name] < sumInRow)
107
110
  sumInRow = dataRow[field.name];
108
111
  });
109
112
  if (min > sumInRow)
@@ -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: 15px;
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: 5px;
76
+ margin: 2px;
77
77
  }
78
78
  .legend-item-row {
79
79
  display: flex;
@@ -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: 15px;
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: 5px;
76
+ margin: 2px;
77
77
  }
78
78
  .legend-item-row {
79
79
  display: flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdt-charts",
3
- "version": "1.12.7",
3
+ "version": "1.12.8",
4
4
  "description": "",
5
5
  "main": "lib/main.js",
6
6
  "scripts": {