evui 3.4.150 → 3.4.152
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 +523 -177
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +523 -177
- 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.vue +68 -64
- package/src/components/chart/model/model.store.js +101 -97
- package/src/components/chart/plugins/plugins.legend.js +44 -8
- package/src/components/chart/scale/scale.linear.js +156 -25
- package/src/components/chart/scale/scale.time.js +94 -0
- package/src/components/chart/uses.js +7 -0
|
@@ -11,7 +11,8 @@ class LinearScale extends Scale {
|
|
|
11
11
|
* @returns {string} formatted label
|
|
12
12
|
*/
|
|
13
13
|
getTruthyValue(value) {
|
|
14
|
-
|
|
14
|
+
const decimalPoint = this.adjustedDecimalPoint ?? this.decimalPoint;
|
|
15
|
+
return truthyNumber(value) ? Number(value.toFixed(decimalPoint)) : value;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
getLabelFormat(value, data = {}) {
|
|
@@ -31,8 +32,8 @@ class LinearScale extends Scale {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
return Util.labelSignFormat(value,
|
|
35
|
+
const decimalPoint = this.adjustedDecimalPoint ?? this.decimalPoint;
|
|
36
|
+
return Util.labelSignFormat(value, decimalPoint);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
|
|
@@ -58,41 +59,171 @@ class LinearScale extends Scale {
|
|
|
58
59
|
return Math.ceil((max - min) / step);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* }
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get auto decimal point from interval
|
|
65
|
+
* interval을 표현할 수 있는 최소 decimal 반환
|
|
66
|
+
* 너무 긴 decimal은 제한
|
|
67
|
+
* @param {number} interval
|
|
68
68
|
* @returns {number} decimal point
|
|
69
69
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
numberOfSteps,
|
|
73
|
-
}) {
|
|
74
|
-
if (numberOfSteps <= 0 || graphRange === 0) {
|
|
70
|
+
getAutoDecimalPointFromInterval(interval) {
|
|
71
|
+
if (!isFinite(interval) || interval === 0) {
|
|
75
72
|
return 0;
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
const
|
|
79
|
-
|
|
75
|
+
const absInterval = Math.abs(interval);
|
|
76
|
+
|
|
77
|
+
// 1 미만 값 처리 (소수점 최대 10자리 제한)
|
|
78
|
+
if (absInterval < 1) {
|
|
79
|
+
let decimals = 0;
|
|
80
|
+
let temp = absInterval;
|
|
81
|
+
|
|
82
|
+
while (temp < 1) {
|
|
83
|
+
temp *= 10;
|
|
84
|
+
decimals++;
|
|
85
|
+
|
|
86
|
+
if (decimals > 10) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return decimals;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 1 이상 값 처리 (소수점 최대 2자리 제한)
|
|
95
|
+
for (let decimal = 0; decimal <= 6; decimal++) {
|
|
96
|
+
const rounded = Number(absInterval.toFixed(decimal));
|
|
97
|
+
|
|
98
|
+
if (Math.abs(rounded - absInterval) < 1e-10) {
|
|
99
|
+
return Math.min(decimal, 2);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return 2;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* axis interval을 nice number로 변환
|
|
108
|
+
* (1, 2, 5 × 10^n)
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} params
|
|
111
|
+
* @param {number} params.range
|
|
112
|
+
* @param {boolean} params.round
|
|
113
|
+
* @returns {number}
|
|
114
|
+
*/
|
|
115
|
+
getNiceNumber({ range, round = false }) {
|
|
116
|
+
if (!isFinite(range) || range <= 0) {
|
|
80
117
|
return 0;
|
|
81
118
|
}
|
|
82
119
|
|
|
83
|
-
|
|
84
|
-
|
|
120
|
+
const exponent = Math.floor(Math.log10(range));
|
|
121
|
+
const fraction = range / (10 ** exponent);
|
|
122
|
+
|
|
123
|
+
let niceFraction;
|
|
85
124
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
125
|
+
if (round) {
|
|
126
|
+
if (fraction < 1.5) {
|
|
127
|
+
niceFraction = 1;
|
|
128
|
+
} else if (fraction < 3) {
|
|
129
|
+
niceFraction = 2;
|
|
130
|
+
} else if (fraction < 7) {
|
|
131
|
+
niceFraction = 5;
|
|
132
|
+
} else {
|
|
133
|
+
niceFraction = 10;
|
|
134
|
+
}
|
|
135
|
+
} else if (fraction <= 1) {
|
|
136
|
+
niceFraction = 1;
|
|
137
|
+
} else if (fraction <= 2) {
|
|
138
|
+
niceFraction = 2;
|
|
139
|
+
} else if (fraction <= 5) {
|
|
140
|
+
niceFraction = 5;
|
|
141
|
+
} else {
|
|
142
|
+
niceFraction = 10;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return niceFraction * (10 ** exponent);
|
|
146
|
+
}
|
|
89
147
|
|
|
90
|
-
|
|
91
|
-
|
|
148
|
+
/**
|
|
149
|
+
* With range information, calculate how many labels in axis
|
|
150
|
+
* @param {object} range min/max information
|
|
151
|
+
*
|
|
152
|
+
* @returns {object} steps, interval, min/max graph value
|
|
153
|
+
*/
|
|
154
|
+
calculateSteps(range) {
|
|
155
|
+
const { minValue, maxValue } = range;
|
|
156
|
+
const maxSteps = Math.max(1, range.maxSteps);
|
|
157
|
+
|
|
158
|
+
const hasUserRange = Array.isArray(this.range) && this.range.length === 2;
|
|
159
|
+
const hasUserInterval = (
|
|
160
|
+
typeof this.interval === 'number'
|
|
161
|
+
|| (typeof this.interval === 'object' && this.interval !== null)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const resolvedInterval = hasUserInterval ? this.getInterval(range) : null;
|
|
165
|
+
const isValidInterval = (
|
|
166
|
+
resolvedInterval != null
|
|
167
|
+
&& resolvedInterval > 0
|
|
168
|
+
&& isFinite(resolvedInterval)
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const graphMin = +minValue;
|
|
172
|
+
let graphMax = +maxValue;
|
|
173
|
+
const graphRange = graphMax - graphMin;
|
|
174
|
+
|
|
175
|
+
let interval;
|
|
176
|
+
let steps;
|
|
177
|
+
|
|
178
|
+
if (hasUserRange && isValidInterval) {
|
|
179
|
+
// 1) user range + interval
|
|
180
|
+
const candidateSteps = graphRange / resolvedInterval;
|
|
181
|
+
const isExactlyDividable = Math.abs(candidateSteps - Math.round(candidateSteps)) < 1e-10;
|
|
182
|
+
|
|
183
|
+
if (isExactlyDividable && candidateSteps <= maxSteps) {
|
|
184
|
+
interval = resolvedInterval;
|
|
185
|
+
steps = Math.round(candidateSteps);
|
|
186
|
+
} else {
|
|
187
|
+
// interval 호환되지 않음 -> 사용자 interval을 사용하지 않음
|
|
188
|
+
steps = maxSteps;
|
|
189
|
+
interval = graphRange / steps;
|
|
190
|
+
}
|
|
191
|
+
} else if (hasUserRange) {
|
|
192
|
+
// 2) user range only
|
|
193
|
+
steps = maxSteps;
|
|
194
|
+
interval = graphRange / steps;
|
|
195
|
+
} else if (isValidInterval) {
|
|
196
|
+
// 3) user interval only
|
|
197
|
+
interval = resolvedInterval;
|
|
198
|
+
steps = Math.ceil(graphRange / interval);
|
|
199
|
+
|
|
200
|
+
while (steps > maxSteps) {
|
|
201
|
+
interval *= 2;
|
|
202
|
+
steps = Math.ceil(graphRange / interval);
|
|
92
203
|
}
|
|
204
|
+
|
|
205
|
+
graphMax = graphMin + (interval * steps);
|
|
206
|
+
} else {
|
|
207
|
+
// 4) auto
|
|
208
|
+
interval = this.getNiceNumber({
|
|
209
|
+
range: graphRange / maxSteps,
|
|
210
|
+
round: true,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
steps = Math.ceil(graphRange / interval);
|
|
214
|
+
graphMax = graphMin + (interval * steps);
|
|
93
215
|
}
|
|
94
216
|
|
|
95
|
-
|
|
217
|
+
this.adjustedDecimalPoint = this.decimalPoint === 'auto'
|
|
218
|
+
? this.getAutoDecimalPointFromInterval(interval)
|
|
219
|
+
: this.decimalPoint;
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
steps,
|
|
223
|
+
interval,
|
|
224
|
+
graphMin,
|
|
225
|
+
graphMax,
|
|
226
|
+
};
|
|
96
227
|
}
|
|
97
228
|
}
|
|
98
229
|
|
|
@@ -44,6 +44,100 @@ class TimeScale extends Scale {
|
|
|
44
44
|
}
|
|
45
45
|
return Math.ceil((max - min) / step);
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* With range information, calculate how many labels in axis
|
|
50
|
+
* @param {object} range min/max information
|
|
51
|
+
*
|
|
52
|
+
* @returns {object} steps, interval, min/max graph value
|
|
53
|
+
*/
|
|
54
|
+
calculateSteps(range) {
|
|
55
|
+
const { maxValue, minValue, maxSteps } = range;
|
|
56
|
+
|
|
57
|
+
// 사용자 interval로 인식하는 경우: 숫자 또는 객체({ time, unit }) 형태만
|
|
58
|
+
// 문자열('hour', 'second' 등)은 기존 로직(분기 D)으로 처리
|
|
59
|
+
const hasUserRange = Array.isArray(this.range) && this.range.length === 2;
|
|
60
|
+
const hasUserInterval = (
|
|
61
|
+
typeof this.interval === 'number'
|
|
62
|
+
|| (typeof this.interval === 'object' && this.interval !== null)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const resolvedInterval = hasUserInterval ? this.getInterval(range) : null;
|
|
66
|
+
const isValidInterval = (
|
|
67
|
+
resolvedInterval != null
|
|
68
|
+
&& resolvedInterval > 0
|
|
69
|
+
&& isFinite(resolvedInterval)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const graphMin = +minValue;
|
|
73
|
+
let graphMax = +maxValue;
|
|
74
|
+
const graphRange = graphMax - graphMin;
|
|
75
|
+
|
|
76
|
+
let interval;
|
|
77
|
+
let steps;
|
|
78
|
+
|
|
79
|
+
if (hasUserRange && isValidInterval) {
|
|
80
|
+
// 1) user range + interval
|
|
81
|
+
const candidateSteps = graphRange / resolvedInterval;
|
|
82
|
+
const isExactlyDividable = Math.abs(candidateSteps - Math.round(candidateSteps)) < 1e-10;
|
|
83
|
+
if (isExactlyDividable && candidateSteps <= maxSteps) {
|
|
84
|
+
// 1-1) interval 호환되는 경우
|
|
85
|
+
interval = resolvedInterval;
|
|
86
|
+
steps = Math.round(candidateSteps);
|
|
87
|
+
} else {
|
|
88
|
+
// 1-2) interval 호환되지 않음 -> 사용자 interval을 사용하지 않음
|
|
89
|
+
steps = maxSteps;
|
|
90
|
+
interval = graphRange / steps;
|
|
91
|
+
}
|
|
92
|
+
} else if (hasUserRange) {
|
|
93
|
+
// 2) user range only
|
|
94
|
+
steps = maxSteps;
|
|
95
|
+
interval = graphRange / steps;
|
|
96
|
+
} else if (isValidInterval) {
|
|
97
|
+
// 3) user interval only
|
|
98
|
+
interval = resolvedInterval;
|
|
99
|
+
steps = Math.ceil(graphRange / interval);
|
|
100
|
+
while (steps > maxSteps) {
|
|
101
|
+
interval *= 2;
|
|
102
|
+
steps = Math.ceil(graphRange / interval);
|
|
103
|
+
}
|
|
104
|
+
graphMax = graphMin + (interval * steps);
|
|
105
|
+
} else {
|
|
106
|
+
// 4) 기존 로직
|
|
107
|
+
interval = this.getInterval(range);
|
|
108
|
+
let increase = minValue;
|
|
109
|
+
let numberOfSteps;
|
|
110
|
+
|
|
111
|
+
while (increase < maxValue) {
|
|
112
|
+
increase += interval;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
graphMax = increase;
|
|
116
|
+
|
|
117
|
+
numberOfSteps = Math.round(graphRange / interval);
|
|
118
|
+
|
|
119
|
+
while (numberOfSteps > maxSteps) {
|
|
120
|
+
interval *= 2;
|
|
121
|
+
numberOfSteps = Math.round(graphRange / interval);
|
|
122
|
+
const tempInterval = graphRange / numberOfSteps;
|
|
123
|
+
interval = this.decimalPoint ? tempInterval : Math.ceil(tempInterval);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (graphMax - graphMin > (numberOfSteps * interval)) {
|
|
127
|
+
const tempInterval = (graphMax - graphMin) / numberOfSteps;
|
|
128
|
+
interval = this.decimalPoint ? tempInterval : Math.ceil(tempInterval);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
steps = numberOfSteps;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
steps,
|
|
136
|
+
interval,
|
|
137
|
+
graphMin,
|
|
138
|
+
graphMax,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
47
141
|
}
|
|
48
142
|
|
|
49
143
|
export default TimeScale;
|
|
@@ -248,6 +248,9 @@ const DEFAULT_OPTIONS = {
|
|
|
248
248
|
},
|
|
249
249
|
seriesReverse: false,
|
|
250
250
|
coordinateDedupe: true,
|
|
251
|
+
eventBehavior: {
|
|
252
|
+
legendClick: 'update',
|
|
253
|
+
},
|
|
251
254
|
};
|
|
252
255
|
|
|
253
256
|
const DEFAULT_DATA = {
|
|
@@ -402,6 +405,10 @@ export const useModel = (injectGroupSelectedLabel, injectGroupHoveredLabel) => {
|
|
|
402
405
|
injectGroupHoveredLabel.value.label = null;
|
|
403
406
|
}
|
|
404
407
|
},
|
|
408
|
+
'click-legend': async (e) => {
|
|
409
|
+
await nextTick();
|
|
410
|
+
emit('click-legend', e);
|
|
411
|
+
},
|
|
405
412
|
};
|
|
406
413
|
|
|
407
414
|
return {
|