evui 3.4.129 → 3.4.131
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 +949 -856
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +949 -856
- 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 +2 -2
- package/src/components/chart/element/element.bar.js +153 -211
- package/src/components/chart/element/element.heatmap.js +3 -4
- package/src/components/chart/element/element.line.js +189 -68
- package/src/components/chart/element/element.tip.js +60 -120
- package/src/components/chart/helpers/helpers.util.js +15 -19
- package/src/components/chart/model/model.store.js +17 -40
- package/src/components/chart/plugins/plugins.interaction.js +231 -71
- package/src/components/chart/plugins/plugins.legend.js +6 -4
- package/src/components/chart/plugins/plugins.tooltip.js +75 -16
- package/src/components/chart/scale/scale.js +7 -17
- package/src/components/chart/scale/scale.linear.js +3 -183
- package/src/components/chart/uses.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { convertToPercent } from '@/common/utils';
|
|
2
2
|
import debounce from '@/common/utils.debounce';
|
|
3
|
-
import { inRange
|
|
3
|
+
import { inRange } from 'lodash-es';
|
|
4
4
|
import Canvas from '../helpers/helpers.canvas';
|
|
5
5
|
import Util from '../helpers/helpers.util';
|
|
6
6
|
|
|
@@ -783,8 +783,8 @@ const modules = {
|
|
|
783
783
|
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
784
784
|
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
785
785
|
};
|
|
786
|
-
const mouseXIp =
|
|
787
|
-
const mouseYIp =
|
|
786
|
+
const mouseXIp = 15; // mouseInterpolation - 더 넓은 범위에서 감지
|
|
787
|
+
const mouseYIp = 15; // Y축도 동일하게 증가
|
|
788
788
|
const options = this.options;
|
|
789
789
|
|
|
790
790
|
if (offsetX >= (graphPos.x1 - mouseXIp) && offsetX <= (graphPos.x2 + mouseXIp)
|
|
@@ -858,10 +858,18 @@ const modules = {
|
|
|
858
858
|
*
|
|
859
859
|
* @returns {undefined}
|
|
860
860
|
*/
|
|
861
|
-
drawSyncedIndicator({ horizontal, label, mousePosition,
|
|
861
|
+
drawSyncedIndicator({ horizontal, label, mousePosition, dataLabel, isTooltipBased }) {
|
|
862
862
|
if (!mousePosition || !!horizontal !== !!this.options.horizontal) {
|
|
863
863
|
return;
|
|
864
864
|
}
|
|
865
|
+
|
|
866
|
+
// tooltip 기반 동기화인 경우
|
|
867
|
+
if (isTooltipBased) {
|
|
868
|
+
this.drawSyncedIndicatorForTooltip({ dataLabel, mousePosition });
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// 기존 시간 기반 동기화
|
|
865
873
|
if (
|
|
866
874
|
this.options.syncHover === false
|
|
867
875
|
|| (!horizontal && !this.options.axesX.every(({ type }) => type === 'time'))
|
|
@@ -882,7 +890,6 @@ const modules = {
|
|
|
882
890
|
}
|
|
883
891
|
|
|
884
892
|
this.overlayClear();
|
|
885
|
-
|
|
886
893
|
const graphPos = {
|
|
887
894
|
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
888
895
|
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
@@ -890,17 +897,7 @@ const modules = {
|
|
|
890
897
|
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
891
898
|
};
|
|
892
899
|
|
|
893
|
-
if (
|
|
894
|
-
const matchIndex = this.data.labels?.findIndex(l => l?.valueOf() === label?.valueOf());
|
|
895
|
-
if (matchIndex >= 0) {
|
|
896
|
-
const seriesId = Object.keys(this.seriesList)?.[0];
|
|
897
|
-
const dataPoint = this.seriesList?.[seriesId]?.data?.[matchIndex];
|
|
898
|
-
if (dataPoint?.xp !== undefined && dataPoint?.xp !== null) {
|
|
899
|
-
const yPosition = !isNil(dataPoint.yp) ? dataPoint.yp : (graphPos.y1 + graphPos.y2) / 2;
|
|
900
|
-
this.drawIndicator([dataPoint.xp, yPosition], this.options.indicator.color);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
} else if (horizontal) {
|
|
900
|
+
if (horizontal) {
|
|
904
901
|
const chartHeight = graphPos.y2 - graphPos.y1;
|
|
905
902
|
const offsetY = (chartHeight * (label - fromTime)) / (toTime - fromTime) + graphPos.y1;
|
|
906
903
|
this.drawIndicator([graphPos.x2, offsetY], this.options.indicator.color);
|
|
@@ -911,6 +908,68 @@ const modules = {
|
|
|
911
908
|
}
|
|
912
909
|
},
|
|
913
910
|
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* 제공된 dataLabel과 일치하는 Label이 있다면 indicator를 그림
|
|
914
|
+
* @param {object} dataLabel data label
|
|
915
|
+
* @param {object} mousePosition mouse position
|
|
916
|
+
*
|
|
917
|
+
* @returns {undefined}
|
|
918
|
+
*/
|
|
919
|
+
drawSyncedIndicatorForTooltip({ dataLabel, mousePosition }) {
|
|
920
|
+
if (!this.data?.labels || !dataLabel) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const matchingLabelIndex = this.data.labels.findIndex(
|
|
925
|
+
label => label?.valueOf() === dataLabel?.valueOf(),
|
|
926
|
+
);
|
|
927
|
+
if (matchingLabelIndex === -1) {
|
|
928
|
+
this.overlayClear();
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const { horizontal } = this.options;
|
|
933
|
+
const { top, bottom, left, right } = this.chartDOM.getBoundingClientRect();
|
|
934
|
+
const isHoveredChart = inRange(mousePosition[0], left, right)
|
|
935
|
+
&& inRange(mousePosition[1], bottom, top);
|
|
936
|
+
if (isHoveredChart) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
this.overlayClear();
|
|
941
|
+
|
|
942
|
+
const graphPos = {
|
|
943
|
+
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
944
|
+
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
945
|
+
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
946
|
+
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
const labelsCount = this.data.labels.length;
|
|
950
|
+
let indicatorPosition;
|
|
951
|
+
|
|
952
|
+
if (horizontal) {
|
|
953
|
+
const chartHeight = graphPos.y2 - graphPos.y1;
|
|
954
|
+
// CategoryMode인 경우 라벨들이 균등 간격으로 배치됨
|
|
955
|
+
const isCategoryMode = this.options.axesY?.some(axis => axis.categoryMode);
|
|
956
|
+
const positionY = isCategoryMode
|
|
957
|
+
? graphPos.y1 + (chartHeight * (matchingLabelIndex + 0.5)) / labelsCount
|
|
958
|
+
: graphPos.y1 + (chartHeight * matchingLabelIndex) / (labelsCount - 1);
|
|
959
|
+
indicatorPosition = [graphPos.x2, positionY];
|
|
960
|
+
} else {
|
|
961
|
+
const chartWidth = graphPos.x2 - graphPos.x1;
|
|
962
|
+
// CategoryMode인 경우 라벨들이 균등 간격으로 배치됨
|
|
963
|
+
const isCategoryMode = this.options.axesX?.some(axis => axis.categoryMode);
|
|
964
|
+
const positionX = isCategoryMode
|
|
965
|
+
? graphPos.x1 + (chartWidth * (matchingLabelIndex + 0.5)) / labelsCount
|
|
966
|
+
: graphPos.x1 + (chartWidth * matchingLabelIndex) / (labelsCount - 1);
|
|
967
|
+
indicatorPosition = [positionX, graphPos.y2];
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
this.drawIndicator(indicatorPosition, this.options.indicator.color);
|
|
971
|
+
},
|
|
972
|
+
|
|
914
973
|
/**
|
|
915
974
|
* Clear tooltip canvas
|
|
916
975
|
*
|
|
@@ -93,8 +93,8 @@ class Scale {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
if (this.startToZero) {
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
minValue = 0;
|
|
97
|
+
}
|
|
98
98
|
|
|
99
99
|
if (maxValue === minValue) {
|
|
100
100
|
maxValue += 1;
|
|
@@ -138,7 +138,6 @@ class Scale {
|
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
140
|
* With range information, calculate how many labels in axis
|
|
141
|
-
* linear type은 scale.linear.js에서 처리
|
|
142
141
|
* @param {object} range min/max information
|
|
143
142
|
*
|
|
144
143
|
* @returns {object} steps, interval, min/max graph value
|
|
@@ -340,13 +339,6 @@ class Scale {
|
|
|
340
339
|
ctx.beginPath();
|
|
341
340
|
ticks[ix] = axisMinForLabel + (ix * stepValue);
|
|
342
341
|
|
|
343
|
-
const isZeroLine = ticks[ix] === 0;
|
|
344
|
-
if (isZeroLine && this.zeroLineColor) {
|
|
345
|
-
ctx.strokeStyle = this.zeroLineColor;
|
|
346
|
-
} else {
|
|
347
|
-
ctx.strokeStyle = this.gridLineColor;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
342
|
linePosition = labelCenter + aliasPixel;
|
|
351
343
|
labelText = this.getLabelFormat(Math.min(axisMax, ticks[ix]), {
|
|
352
344
|
prev: ticks[ix - 1] ?? '',
|
|
@@ -443,17 +435,15 @@ class Scale {
|
|
|
443
435
|
}
|
|
444
436
|
|
|
445
437
|
const mergedPlotBandOpt = defaultsDeep({}, plotBand, PLOT_BAND_OPTION);
|
|
446
|
-
const { from
|
|
447
|
-
const from = userDefinedFrom ? Math.max(userDefinedFrom, axisMin) : axisMin;
|
|
448
|
-
const to = userDefinedTo ? Math.min(userDefinedTo, axisMax) : axisMax;
|
|
438
|
+
const { from, to, label: labelOpt } = mergedPlotBandOpt;
|
|
449
439
|
|
|
450
440
|
this.setPlotBandStyle(mergedPlotBandOpt);
|
|
451
441
|
|
|
452
442
|
let fromPos;
|
|
453
443
|
let toPos;
|
|
454
444
|
if (this.type === 'x') {
|
|
455
|
-
fromPos = Canvas.calculateX(from, axisMin, axisMax, xArea, minX);
|
|
456
|
-
toPos = Canvas.calculateX(to, axisMin, axisMax, xArea, minX);
|
|
445
|
+
fromPos = Canvas.calculateX(from ?? minX, axisMin, axisMax, xArea, minX);
|
|
446
|
+
toPos = Canvas.calculateX(to ?? maxX, axisMin, axisMax, xArea, minX);
|
|
457
447
|
|
|
458
448
|
if (fromPos === null || toPos === null) {
|
|
459
449
|
return;
|
|
@@ -461,8 +451,8 @@ class Scale {
|
|
|
461
451
|
|
|
462
452
|
this.drawXPlotBand(fromPos, toPos, minX, maxX, minY, maxY);
|
|
463
453
|
} else {
|
|
464
|
-
fromPos = Canvas.calculateY(from, axisMin, axisMax, yArea, maxY);
|
|
465
|
-
toPos = Canvas.calculateY(to, axisMin, axisMax, yArea, maxY);
|
|
454
|
+
fromPos = Canvas.calculateY(from ?? axisMin, axisMin, axisMax, yArea, maxY);
|
|
455
|
+
toPos = Canvas.calculateY(to ?? axisMax, axisMin, axisMax, yArea, maxY);
|
|
466
456
|
|
|
467
457
|
if (fromPos === null || toPos === null) {
|
|
468
458
|
return;
|
|
@@ -25,195 +25,15 @@ class LinearScale extends Scale {
|
|
|
25
25
|
* Calculate interval
|
|
26
26
|
* @param {object} range range information
|
|
27
27
|
*
|
|
28
|
-
* @returns {number}
|
|
28
|
+
* @returns {number} interval
|
|
29
29
|
*/
|
|
30
30
|
getInterval(range) {
|
|
31
|
-
if (this.interval) return this.interval;
|
|
32
|
-
|
|
33
31
|
const max = range.maxValue;
|
|
34
32
|
const min = range.minValue;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
// step이 0이면 interval 계산 불가
|
|
38
|
-
if (!steps || steps <= 0) return 0;
|
|
39
|
-
|
|
40
|
-
// startToZero이고, 최소값이 음수일 경우 0을 반드시 포함
|
|
41
|
-
if (this.startToZero && min < 0) {
|
|
42
|
-
const totalRange = Math.abs(min) + Math.abs(max);
|
|
43
|
-
|
|
44
|
-
// 비율로 나눔
|
|
45
|
-
const negativeRatio = Math.abs(min) / totalRange;
|
|
46
|
-
const positiveRatio = Math.abs(max) / totalRange;
|
|
47
|
-
|
|
48
|
-
// 각 방향에 최소 1칸 이상 배정되도록 보장
|
|
49
|
-
let negativeSteps = Math.max(1, Math.round(negativeRatio * steps));
|
|
50
|
-
let positiveSteps = Math.max(1, steps - negativeSteps);
|
|
51
|
-
|
|
52
|
-
// 다시 합이 steps보다 커질 수도 있으니, 조정
|
|
53
|
-
if (negativeSteps + positiveSteps > steps) {
|
|
54
|
-
// 가장 큰 쪽에서 하나 줄임
|
|
55
|
-
if (negativeRatio > positiveRatio) {
|
|
56
|
-
negativeSteps -= 1;
|
|
57
|
-
} else {
|
|
58
|
-
positiveSteps -= 1;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return Math.ceil(
|
|
63
|
-
Math.max(
|
|
64
|
-
Math.abs(min) / (negativeSteps || 1),
|
|
65
|
-
Math.abs(max) / (positiveSteps || 1),
|
|
66
|
-
),
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
return Math.ceil((max - min) / steps);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* With range information, calculate how many labels in axis
|
|
74
|
-
* @param {object} range min/max information
|
|
75
|
-
*
|
|
76
|
-
* @returns {object} steps, interval, min/max graph value
|
|
77
|
-
*/
|
|
78
|
-
calculateSteps(range) {
|
|
79
|
-
const { maxValue, minValue } = range;
|
|
80
|
-
let { maxSteps = 1 } = range;
|
|
81
|
-
|
|
82
|
-
let interval = this.getInterval(range);
|
|
83
|
-
let graphMin = 0;
|
|
84
|
-
let graphMax = 0;
|
|
85
|
-
|
|
86
|
-
// 그래프 최대/최소 값 계산
|
|
87
|
-
if (minValue >= 0) {
|
|
88
|
-
// 전부 양수
|
|
89
|
-
graphMin = +minValue;
|
|
90
|
-
graphMax = Math.ceil(maxValue / interval) * interval;
|
|
91
|
-
} else if (maxValue >= 0) {
|
|
92
|
-
// 양수/음수 혼합
|
|
93
|
-
graphMin = Math.floor(minValue / interval) * interval;
|
|
94
|
-
graphMax = Math.ceil(maxValue / interval) * interval;
|
|
95
|
-
} else {
|
|
96
|
-
// 전부 음수
|
|
97
|
-
graphMax = +maxValue;
|
|
98
|
-
graphMin = Math.floor(minValue / interval) * interval;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const graphRange = graphMax - graphMin;
|
|
102
|
-
let numberOfSteps = Math.round(graphRange / interval);
|
|
103
|
-
|
|
104
|
-
// 특수 케이스: 양수 최소값, 최대값이 1일 경우
|
|
105
|
-
if (minValue > 0 && maxValue === 1) {
|
|
106
|
-
if (!this.decimalPoint) {
|
|
107
|
-
interval = 1;
|
|
108
|
-
numberOfSteps = 1;
|
|
109
|
-
maxSteps = 1;
|
|
110
|
-
} else if (maxSteps > 2) {
|
|
111
|
-
interval = 0.2;
|
|
112
|
-
numberOfSteps = 5;
|
|
113
|
-
maxSteps = 5;
|
|
114
|
-
} else {
|
|
115
|
-
interval = 0.5;
|
|
116
|
-
numberOfSteps = 2;
|
|
117
|
-
maxSteps = 2;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
33
|
+
const step = range.maxSteps;
|
|
120
34
|
|
|
121
|
-
|
|
122
|
-
while (numberOfSteps > maxSteps) {
|
|
123
|
-
interval *= 2;
|
|
124
|
-
numberOfSteps = Math.round(graphRange / interval);
|
|
125
|
-
interval = Math.ceil(graphRange / numberOfSteps);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (graphRange > (numberOfSteps * interval)) {
|
|
129
|
-
interval = Math.ceil(graphRange / numberOfSteps);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
steps: numberOfSteps,
|
|
134
|
-
interval,
|
|
135
|
-
graphMin,
|
|
136
|
-
graphMax,
|
|
137
|
-
};
|
|
35
|
+
return this.interval ? this.interval : Math.ceil((max - min) / step);
|
|
138
36
|
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Calculate min/max value, label and size information for axis
|
|
142
|
-
* @param {object} minMax min/max information
|
|
143
|
-
* @param {object} scrollbarOpt scrollbar option
|
|
144
|
-
*
|
|
145
|
-
* @returns {object} min/max value and label
|
|
146
|
-
*/
|
|
147
|
-
calculateScaleRange(minMax, scrollbarOpt) {
|
|
148
|
-
let maxValue;
|
|
149
|
-
let minValue;
|
|
150
|
-
let isDefaultMaxSameAsMin = false;
|
|
151
|
-
|
|
152
|
-
const range = scrollbarOpt?.use ? scrollbarOpt?.range : this.range;
|
|
153
|
-
if (Array.isArray(range) && range?.length === 2) {
|
|
154
|
-
if (this.options.type === 'heatMap') {
|
|
155
|
-
maxValue = range[1] > +minMax.max ? +minMax.max : range[1];
|
|
156
|
-
minValue = range[0] < +minMax.min ? +minMax.min : range[0];
|
|
157
|
-
} else {
|
|
158
|
-
maxValue = range[1];
|
|
159
|
-
minValue = range[0];
|
|
160
|
-
}
|
|
161
|
-
} else if (typeof range === 'function') {
|
|
162
|
-
[minValue, maxValue] = range(minMax.min, minMax.max);
|
|
163
|
-
} else {
|
|
164
|
-
maxValue = minMax.max;
|
|
165
|
-
minValue = minMax.min;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// autoScaleRatio 적용 케이스
|
|
169
|
-
if (this.autoScaleRatio) {
|
|
170
|
-
const temp = maxValue;
|
|
171
|
-
// 양수 방향에만 autoScaleRatio 적용
|
|
172
|
-
maxValue = Math.ceil(maxValue * (this.autoScaleRatio + 1));
|
|
173
|
-
|
|
174
|
-
if (maxValue > 0 && minValue < 0) {
|
|
175
|
-
// 양수/음수 혼합 케이스 -- 음수 방향에도 maxValue 증가분만큼 더하기
|
|
176
|
-
const diff = temp - maxValue;
|
|
177
|
-
minValue += diff;
|
|
178
|
-
} else if (maxValue < 0 && minValue < 0) {
|
|
179
|
-
// 전부 음수 케이스 -- 음수 방향에도 autoScaleRatio 적용
|
|
180
|
-
minValue = Math.ceil(minValue * (this.autoScaleRatio + 1));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// 0 기준 축 설정 케이스
|
|
185
|
-
if (this.startToZero) {
|
|
186
|
-
if (minValue > 0) {
|
|
187
|
-
minValue = 0;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (maxValue < 0) {
|
|
191
|
-
maxValue = 0;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (maxValue === minValue) {
|
|
196
|
-
maxValue += 1;
|
|
197
|
-
isDefaultMaxSameAsMin = true;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const minLabel = this.getLabelFormat(minValue);
|
|
201
|
-
const maxLabel = this.getLabelFormat(maxValue, {
|
|
202
|
-
isMaxValueSameAsMin: isDefaultMaxSameAsMin,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
min: minValue,
|
|
207
|
-
max: maxValue,
|
|
208
|
-
minLabel,
|
|
209
|
-
maxLabel,
|
|
210
|
-
size: Util.calcTextSize(
|
|
211
|
-
maxLabel,
|
|
212
|
-
Util.getLabelStyle(this.labelStyle),
|
|
213
|
-
this.labelStyle?.padding,
|
|
214
|
-
),
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
37
|
}
|
|
218
38
|
|
|
219
39
|
export default LinearScale;
|