evui 3.4.116 → 3.4.117
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/dist/evui.common.js +204 -67
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +204 -67
- package/dist/evui.umd.js.map +1 -1
- package/dist/evui.umd.min.js +1 -1
- package/dist/evui.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chart/chart.core.js +4 -2
- package/src/components/chart/element/element.line.js +54 -46
- package/src/components/chart/helpers/helpers.canvas.js +4 -4
- package/src/components/chart/helpers/helpers.constant.js +1 -0
- package/src/components/chart/model/index.js +46 -0
- package/src/components/chart/model/model.store.js +36 -10
- package/src/components/chart/plugins/plugins.interaction.js +1 -1
package/package.json
CHANGED
|
@@ -576,8 +576,9 @@ class EvChart {
|
|
|
576
576
|
|
|
577
577
|
/**
|
|
578
578
|
* Get chart DOM size and set canvas size
|
|
579
|
+
* @typedef {import('./model/index').ChartDOMSize} ChartDOMSize
|
|
579
580
|
*
|
|
580
|
-
* @returns {
|
|
581
|
+
* @returns {ChartDOMSize} chart size information
|
|
581
582
|
*/
|
|
582
583
|
getChartDOMRect() {
|
|
583
584
|
const rect = this.chartDOM?.getBoundingClientRect();
|
|
@@ -592,8 +593,9 @@ class EvChart {
|
|
|
592
593
|
|
|
593
594
|
/**
|
|
594
595
|
* Calculate chart size
|
|
596
|
+
* @typedef {import('./model/index').ChartRect} ChartRect
|
|
595
597
|
*
|
|
596
|
-
* @returns {
|
|
598
|
+
* @returns {ChartRect} chart size information
|
|
597
599
|
*/
|
|
598
600
|
getChartRect() {
|
|
599
601
|
const { width, height } = this.getChartDOMRect();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defaultsDeep } from 'lodash-es';
|
|
1
|
+
import { defaultsDeep, isNil, isUndefined } from 'lodash-es';
|
|
2
2
|
import { COLOR, LINE_OPTION } from '../helpers/helpers.constant';
|
|
3
3
|
import Util from '../helpers/helpers.util';
|
|
4
4
|
import Canvas from '../helpers/helpers.canvas';
|
|
@@ -26,6 +26,7 @@ class Line {
|
|
|
26
26
|
normal: { opacity: 1, lineWidth: 1 },
|
|
27
27
|
highlight: { opacity: 1, lineWidth: 2 },
|
|
28
28
|
};
|
|
29
|
+
/** @type {import('../model/index').ChartSeriesDataPoint[]} */
|
|
29
30
|
this.data = [];
|
|
30
31
|
this.beforeMouseXp = 0;
|
|
31
32
|
this.beforeMouseYp = 0;
|
|
@@ -33,12 +34,26 @@ class Line {
|
|
|
33
34
|
this.size = {
|
|
34
35
|
comboOffset: 0,
|
|
35
36
|
};
|
|
36
|
-
this.usePassingValue = !!this.passingValue;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
useLinearInterpolation() {
|
|
40
|
+
return this.interpolation === 'linear' || (this.interpolation === 'none' && !!this.passingValue && this.hasPassingValueInData);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} LineDrawParam
|
|
45
|
+
* @property {CanvasRenderingContext2D} ctx - 캔버스 렌더링 컨텍스트
|
|
46
|
+
* @property {object} chartRect - 차트 영역 정보
|
|
47
|
+
* @property {object} labelOffset - 라벨 오프셋 정보
|
|
48
|
+
* @property {object} axesSteps - 축 스텝 정보
|
|
49
|
+
* @property {object} [selectLabel] - 선택된 라벨 정보
|
|
50
|
+
* @property {object} [selectSeries] - 선택된 시리즈 정보
|
|
51
|
+
* @property {object} [legendHitInfo] - 범례 히트 정보
|
|
52
|
+
* @property {boolean} [isBrush] - 브러시 사용 여부
|
|
53
|
+
*/
|
|
39
54
|
/**
|
|
40
55
|
* Draw series data
|
|
41
|
-
* @param {
|
|
56
|
+
* @param {LineDrawParam} param object for drawing series data
|
|
42
57
|
*
|
|
43
58
|
* @returns {undefined}
|
|
44
59
|
*/
|
|
@@ -91,8 +106,8 @@ class Line {
|
|
|
91
106
|
|
|
92
107
|
const endPoint = chartRect.y2 - labelOffset.bottom;
|
|
93
108
|
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
const isLinearInterpolation = this.useLinearInterpolation();
|
|
110
|
+
|
|
96
111
|
let barAreaByCombo = 0;
|
|
97
112
|
|
|
98
113
|
const minmaxX = axesSteps.x[this.xAxisIndex];
|
|
@@ -114,51 +129,37 @@ class Line {
|
|
|
114
129
|
const getYPos = val => Canvas.calculateY(val, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp);
|
|
115
130
|
|
|
116
131
|
// draw line
|
|
117
|
-
let
|
|
118
|
-
this.data.
|
|
119
|
-
x = getXPos(curr.x);
|
|
120
|
-
y = getYPos(curr.y);
|
|
132
|
+
let prevValid;
|
|
133
|
+
this.data.forEach((curr) => {
|
|
134
|
+
let x = getXPos(curr.x);
|
|
135
|
+
let y = getYPos(curr.y);
|
|
136
|
+
|
|
137
|
+
if (this.isExistGrp && isLinearInterpolation && curr.o === null) {
|
|
138
|
+
y = getYPos(curr.b ?? 0);
|
|
139
|
+
}
|
|
121
140
|
|
|
122
141
|
if (x !== null) {
|
|
123
142
|
x += Util.aliasPixel(x);
|
|
124
143
|
}
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
y = getYPos(prev.y);
|
|
129
|
-
|
|
130
|
-
if (prev.o === null) {
|
|
131
|
-
needCutoff = true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (this.isExistGrp && !needCutoff) {
|
|
135
|
-
y = getYPos(curr.b ?? 0);
|
|
136
|
-
ctx.lineTo(x, y);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
curr.xp = x;
|
|
140
|
-
curr.yp = y;
|
|
145
|
+
curr.xp = x;
|
|
146
|
+
curr.yp = y;
|
|
141
147
|
|
|
142
|
-
|
|
148
|
+
if (isLinearInterpolation && curr.o === null) {
|
|
149
|
+
if (!this.isExistGrp) {
|
|
150
|
+
return;
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
|
|
146
|
-
|
|
147
|
-
||
|
|
148
|
-
|| Util.isNullOrUndefined(curr.x)
|
|
149
|
-
|| Util.isNullOrUndefined(curr.y);
|
|
150
|
-
if (isNullValue || needCutoff) {
|
|
154
|
+
if ((isNil(prevValid?.y) && !this.isExistGrp)
|
|
155
|
+
|| (!isLinearInterpolation && (isNil(prevValid?.y) || isNil(curr.o)))) {
|
|
151
156
|
ctx.moveTo(x, y);
|
|
152
|
-
needCutoff = false;
|
|
153
157
|
} else {
|
|
154
158
|
ctx.lineTo(x, y);
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return curr;
|
|
161
|
-
}, this.data[0]);
|
|
161
|
+
prevValid = curr;
|
|
162
|
+
});
|
|
162
163
|
|
|
163
164
|
ctx.stroke();
|
|
164
165
|
if (this.segments) {
|
|
@@ -195,19 +196,21 @@ class Line {
|
|
|
195
196
|
// ex) [10, passing, null, 10, 10, passing, 10] -> [[0, 1], [3, 6]]
|
|
196
197
|
let start = null;
|
|
197
198
|
let end = null;
|
|
198
|
-
const valueArray = this.data.map(item => item?.o);
|
|
199
|
+
const valueArray = this.data.map(item => (item?.o));
|
|
200
|
+
/** @type {Array<[number, number]>} */
|
|
199
201
|
const needFillDataIndexList = [];
|
|
200
202
|
for (let i = 0; i < valueArray.length + 1; i++) {
|
|
201
|
-
if (
|
|
203
|
+
if ((isLinearInterpolation && isUndefined(valueArray[i]))
|
|
204
|
+
|| (!isLinearInterpolation && isNil(valueArray[i]))) {
|
|
202
205
|
if (start !== null && end !== null) {
|
|
203
206
|
const temp = valueArray.slice(start, i);
|
|
204
207
|
const lastNormalValueIndex = temp.findLastIndex(
|
|
205
|
-
item =>
|
|
208
|
+
item => !isNil(item) && item !== null);
|
|
206
209
|
needFillDataIndexList.push([start, start + lastNormalValueIndex]);
|
|
207
210
|
start = null;
|
|
208
211
|
end = null;
|
|
209
212
|
}
|
|
210
|
-
} else if (valueArray[i] ===
|
|
213
|
+
} else if (isLinearInterpolation && valueArray[i] === null) {
|
|
211
214
|
end = i;
|
|
212
215
|
} else {
|
|
213
216
|
start = start === null ? i : start;
|
|
@@ -231,7 +234,7 @@ class Line {
|
|
|
231
234
|
|
|
232
235
|
if (ix === startIndex) {
|
|
233
236
|
ctx.moveTo(currData.xp, currData.yp);
|
|
234
|
-
} else if (this.isExistGrp ||
|
|
237
|
+
} else if (this.isExistGrp || currData.o !== null) {
|
|
235
238
|
ctx.lineTo(currData.xp, currData.yp);
|
|
236
239
|
}
|
|
237
240
|
|
|
@@ -256,14 +259,18 @@ class Line {
|
|
|
256
259
|
ctx.strokeStyle = Util.colorStringToRgba(mainColor, mainColorOpacity);
|
|
257
260
|
const focusStyle = Util.colorStringToRgba(pointFillColor, 1);
|
|
258
261
|
const blurStyle = Util.colorStringToRgba(pointFillColor, pointFillColorOpacity);
|
|
262
|
+
const isLinearSingle = this.interpolation === 'linear' && this.data.filter(item => item.o !== null).length === 1;
|
|
259
263
|
|
|
260
264
|
this.data.forEach((curr, ix) => {
|
|
261
|
-
if (curr.xp === null || curr.yp === null || curr.o ===
|
|
265
|
+
if (curr.xp === null || curr.yp === null || curr.o === null) {
|
|
262
266
|
return;
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
const
|
|
266
|
-
|
|
269
|
+
const prevData = this.data[ix - 1]?.o;
|
|
270
|
+
const nextData = this.data[ix + 1]?.o;
|
|
271
|
+
|
|
272
|
+
const isSingle = (!isLinearInterpolation && isNil(prevData) && isNil(nextData))
|
|
273
|
+
|| isLinearSingle;
|
|
267
274
|
const isSelectedLabel = selectedLabelIndexList.includes(ix);
|
|
268
275
|
if (this.point || isSingle || isSelectedLabel) {
|
|
269
276
|
ctx.fillStyle = isSelectedLabel && !legendHitInfo ? focusStyle : blurStyle;
|
|
@@ -290,7 +297,7 @@ class Line {
|
|
|
290
297
|
const { xp, yp, o } = gdata;
|
|
291
298
|
|
|
292
299
|
ctx.save();
|
|
293
|
-
if (xp !== null && yp !== null && o !==
|
|
300
|
+
if (xp !== null && yp !== null && o !== null && this.pointHighlight) {
|
|
294
301
|
ctx.strokeStyle = Util.colorStringToRgba(this.color, 0);
|
|
295
302
|
ctx.fillStyle = Util.colorStringToRgba(this.color, this.highlight.maxShadowOpacity);
|
|
296
303
|
Canvas.drawPoint(ctx, this.pointStyle, this.highlight.maxShadowSize, xp, yp);
|
|
@@ -320,6 +327,7 @@ class Line {
|
|
|
320
327
|
const item = { data: null, hit: false, color: this.color };
|
|
321
328
|
const gdata = this.data.filter(data => !Util.isNullOrUndefined(data.x));
|
|
322
329
|
const SPARE_XP = 0.5;
|
|
330
|
+
const isLinearInterpolation = this.useLinearInterpolation();
|
|
323
331
|
|
|
324
332
|
if (gdata?.length) {
|
|
325
333
|
if (typeof dataIndex === 'number' && this.show) {
|
|
@@ -402,7 +410,7 @@ class Line {
|
|
|
402
410
|
}
|
|
403
411
|
}
|
|
404
412
|
|
|
405
|
-
if (
|
|
413
|
+
if (isLinearInterpolation && item?.data?.o === null) {
|
|
406
414
|
item.data = null;
|
|
407
415
|
}
|
|
408
416
|
|
|
@@ -3,13 +3,13 @@ import Util from './helpers.util';
|
|
|
3
3
|
export default {
|
|
4
4
|
/**
|
|
5
5
|
* Calculate X position
|
|
6
|
-
* @param {
|
|
6
|
+
* @param {number|null|undefined} value graph value
|
|
7
7
|
* @param {number} min min value
|
|
8
8
|
* @param {number} max max value
|
|
9
9
|
* @param {number} area height for axis
|
|
10
10
|
* @param {number} startPoint startPoint
|
|
11
11
|
*
|
|
12
|
-
* @returns {
|
|
12
|
+
* @returns {number|null} position
|
|
13
13
|
*/
|
|
14
14
|
calculateX(value, min, max, area, startPoint = 0) {
|
|
15
15
|
if (value === null || value === undefined) {
|
|
@@ -45,13 +45,13 @@ export default {
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Calculate Y position
|
|
48
|
-
* @param {
|
|
48
|
+
* @param {number|null|undefined} value graph value
|
|
49
49
|
* @param {number} min min value
|
|
50
50
|
* @param {number} max max value
|
|
51
51
|
* @param {number} area height for axis
|
|
52
52
|
* @param {number} startPoint startPoint
|
|
53
53
|
*
|
|
54
|
-
* @returns {
|
|
54
|
+
* @returns {number|null} position
|
|
55
55
|
*/
|
|
56
56
|
calculateY(value, min, max, area, startPoint = 0) {
|
|
57
57
|
let calcY;
|
|
@@ -2,3 +2,49 @@ import Store from './model.store';
|
|
|
2
2
|
import Series from './model.series';
|
|
3
3
|
|
|
4
4
|
export default { Store, Series };
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} ChartDOMSize
|
|
9
|
+
* @property {number} width - 차트 DOM의 너비
|
|
10
|
+
* @property {number} height - 차트 DOM의 높이
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} ChartRect
|
|
15
|
+
* @property {number} x1 - 차트 영역의 시작 X 좌표
|
|
16
|
+
* @property {number} x2 - 차트 영역의 끝 X 좌표
|
|
17
|
+
* @property {number} y1 - 차트 영역의 시작 Y 좌표
|
|
18
|
+
* @property {number} y2 - 차트 영역의 끝 Y 좌표
|
|
19
|
+
* @property {number} chartWidth - 실제 차트 그리기 영역의 너비
|
|
20
|
+
* @property {number} chartHeight - 실제 차트 그리기 영역의 높이
|
|
21
|
+
* @property {number} width - 전체 차트 컨테이너의 너비
|
|
22
|
+
* @property {number} height - 전체 차트 컨테이너의 높이
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} MouseLabelValue
|
|
28
|
+
* @property {string|number} labelVal - 마우스 위치에 해당하는 라벨 값
|
|
29
|
+
* @property {number} labelIdx - 라벨 인덱스 (없으면 -1)
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} ChartSeriesDataPoint
|
|
35
|
+
* @property {number|null} x - x축 값 또는 라벨
|
|
36
|
+
* @property {number|null} y - y축 값 또는 데이터 값
|
|
37
|
+
* @property {number|null} o - 원본 데이터 값
|
|
38
|
+
* @property {number|null} b - 스택형 차트의 베이스 값
|
|
39
|
+
* @property {number|null} xp - x좌표 위치(픽셀 등)
|
|
40
|
+
* @property {number|null} yp - y좌표 위치(픽셀 등)
|
|
41
|
+
* @property {number|null} w - 너비
|
|
42
|
+
* @property {number|null} h - 높이
|
|
43
|
+
* @property {string|null} dataColor - 데이터 색상
|
|
44
|
+
* @property {string|null} dataTextColor - 텍스트 색상
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {'none' | 'linear' | 'zero'} InterpolationType
|
|
50
|
+
*/
|
|
@@ -46,7 +46,19 @@ const modules = {
|
|
|
46
46
|
} else {
|
|
47
47
|
seriesIDs.forEach((seriesID) => {
|
|
48
48
|
const series = this.seriesList[seriesID];
|
|
49
|
-
|
|
49
|
+
|
|
50
|
+
const hasPassingValueInData = data[seriesID].some(item => item === series.passingValue);
|
|
51
|
+
series.hasPassingValueInData = hasPassingValueInData;
|
|
52
|
+
|
|
53
|
+
const sData = data[seriesID].map((item) => {
|
|
54
|
+
if (series.interpolation === 'zero' && !item) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
if (item === series.passingValue) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return item;
|
|
61
|
+
});
|
|
50
62
|
|
|
51
63
|
if (series && sData) {
|
|
52
64
|
if (series.isExistGrp && series.stackIndex && !series.isOverlapping) {
|
|
@@ -386,7 +398,9 @@ const modules = {
|
|
|
386
398
|
* @param {array} bsIds stacked base data ID List
|
|
387
399
|
* @param {number} sIdx series ordered index
|
|
388
400
|
*
|
|
389
|
-
* @
|
|
401
|
+
* @typedef {import('./index').ChartSeriesDataPoint} ChartSeriesDataPoint
|
|
402
|
+
*
|
|
403
|
+
* @returns {ChartSeriesDataPoint[]} data for each series
|
|
390
404
|
*/
|
|
391
405
|
addSeriesStackDS(data, label, bsIds, sIdx = 0) {
|
|
392
406
|
const isHorizontal = this.options.horizontal;
|
|
@@ -429,8 +443,7 @@ const modules = {
|
|
|
429
443
|
if (oData != null) {
|
|
430
444
|
gdata = bdata + oData;
|
|
431
445
|
} else {
|
|
432
|
-
gdata =
|
|
433
|
-
bdata = 0;
|
|
446
|
+
gdata = odata;
|
|
434
447
|
}
|
|
435
448
|
} else {
|
|
436
449
|
bdata = 0;
|
|
@@ -450,7 +463,9 @@ const modules = {
|
|
|
450
463
|
* @param {object} label chart label
|
|
451
464
|
* @param {boolean} isBase is Base(bottommost) series at stack chart
|
|
452
465
|
*
|
|
453
|
-
* @
|
|
466
|
+
* @typedef {import('./index').ChartSeriesDataPoint} ChartSeriesDataPoint
|
|
467
|
+
*
|
|
468
|
+
* @returns {ChartSeriesDataPoint[]} data for each series
|
|
454
469
|
*/
|
|
455
470
|
addSeriesDS(data, label, isBase) {
|
|
456
471
|
const isHorizontal = this.options.horizontal;
|
|
@@ -470,7 +485,8 @@ const modules = {
|
|
|
470
485
|
const isPassingValueWithStack = isBase
|
|
471
486
|
&& !Util.isNullOrUndefined(passingValue)
|
|
472
487
|
&& gdata === passingValue;
|
|
473
|
-
sdata.push(this.addData(isPassingValueWithStack ? 0 : gdata, ldata, gdata
|
|
488
|
+
sdata.push(this.addData(isPassingValueWithStack ? 0 : gdata, ldata, gdata,
|
|
489
|
+
));
|
|
474
490
|
}
|
|
475
491
|
});
|
|
476
492
|
|
|
@@ -520,8 +536,10 @@ const modules = {
|
|
|
520
536
|
* @param {object} ldata label data (x-axis value for vertical chart)
|
|
521
537
|
* @param {object} odata original data (without stacked value)
|
|
522
538
|
* @param {object} bdata base data (stacked value)
|
|
523
|
-
|
|
524
|
-
* @
|
|
539
|
+
*
|
|
540
|
+
* @typedef {import('./index').ChartSeriesDataPoint} ChartSeriesDataPoint
|
|
541
|
+
*
|
|
542
|
+
* @returns {ChartSeriesDataPoint} data for each graph point
|
|
525
543
|
*/
|
|
526
544
|
addData(gdata, ldata, odata = null, bdata = null) {
|
|
527
545
|
let data;
|
|
@@ -1008,12 +1026,17 @@ const modules = {
|
|
|
1008
1026
|
|
|
1009
1027
|
return result;
|
|
1010
1028
|
},
|
|
1029
|
+
/**
|
|
1030
|
+
* @typedef {Object} LabelInfoResult
|
|
1031
|
+
* @property {number} labelIndex - 선택된 라벨의 인덱스
|
|
1032
|
+
* @property {object} hitInfo - 해당 위치에서의 히트 정보 (getItemByPosition 반환값)
|
|
1033
|
+
*/
|
|
1011
1034
|
/**
|
|
1012
1035
|
* Find label info by position x and y
|
|
1013
1036
|
* @param {array} offset position x and y
|
|
1014
1037
|
* @param {string | null} targetAxis target Axis Location ('xAxis', 'yAxis' , null)
|
|
1015
1038
|
*
|
|
1016
|
-
* @returns {
|
|
1039
|
+
* @returns {LabelInfoResult} clicked label information
|
|
1017
1040
|
*/
|
|
1018
1041
|
getLabelInfoByPosition(offset, targetAxis) {
|
|
1019
1042
|
const [x, y] = offset;
|
|
@@ -1097,11 +1120,14 @@ const modules = {
|
|
|
1097
1120
|
|
|
1098
1121
|
/**
|
|
1099
1122
|
* Get current mouse target label value in label array or calculated using mouse position
|
|
1123
|
+
*
|
|
1124
|
+
* @typedef {import('./index').MouseLabelValue} MouseLabelValue
|
|
1125
|
+
*
|
|
1100
1126
|
* @param {string} targetAxis target Axis Location ('xAxis', 'yAxis')
|
|
1101
1127
|
* @param {array} offset return value from getMousePosition()
|
|
1102
1128
|
* @param {number} labelIndex
|
|
1103
1129
|
*
|
|
1104
|
-
* @returns {
|
|
1130
|
+
* @returns {MouseLabelValue} current mouse target label value
|
|
1105
1131
|
*/
|
|
1106
1132
|
getCurMouseLabelVal(targetAxis, offset, labelIndex) {
|
|
1107
1133
|
const { type: chartType, horizontal } = this.options;
|