evui 3.4.70 → 3.4.72
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 +273 -81
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +273 -81
- 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 +13 -0
- package/src/components/chart/element/element.line.js +2 -2
- package/src/components/chart/plugins/plugins.legend.js +178 -23
- package/src/components/chart/uses.js +1 -0
package/package.json
CHANGED
|
@@ -960,6 +960,10 @@ class EvChart {
|
|
|
960
960
|
if (this.options.legend.type === 'gradient') {
|
|
961
961
|
this.legendBoxDOM.removeEventListener('mousedown', this.onLegendMouseDown);
|
|
962
962
|
}
|
|
963
|
+
if (this.options.legend.virtualScroll && !this.useTable) {
|
|
964
|
+
this.legendBoxDOM.removeEventListener('resize', this.updateVisibleRowCount);
|
|
965
|
+
this.legendBoxDOM.removeEventListener('scroll', this.renderVisibleLegends);
|
|
966
|
+
}
|
|
963
967
|
}
|
|
964
968
|
|
|
965
969
|
if (this.resizeDOM) {
|
|
@@ -984,6 +988,15 @@ class EvChart {
|
|
|
984
988
|
this.tooltipDOM = null;
|
|
985
989
|
}
|
|
986
990
|
|
|
991
|
+
if (this.renderVisibleLegendsFrameId != null) {
|
|
992
|
+
cancelAnimationFrame(this.renderVisibleLegendsFrameId);
|
|
993
|
+
this.renderVisibleLegendsFrameId = null;
|
|
994
|
+
}
|
|
995
|
+
if (this.updateVisibleRowCountFrameId != null) {
|
|
996
|
+
cancelAnimationFrame(this.updateVisibleRowCountFrameId);
|
|
997
|
+
this.updateVisibleRowCountFrameId = null;
|
|
998
|
+
}
|
|
999
|
+
|
|
987
1000
|
this.wrapperDOM = null;
|
|
988
1001
|
this.chartDOM = null;
|
|
989
1002
|
this.legendDOM = null;
|
|
@@ -16,7 +16,7 @@ class Line {
|
|
|
16
16
|
|
|
17
17
|
['color', 'pointFill', 'fillColor'].forEach((colorProp) => {
|
|
18
18
|
if (this[colorProp] === undefined) {
|
|
19
|
-
this[colorProp] = colorProp === 'pointFill' ? this.color : COLOR[sIdx];
|
|
19
|
+
this[colorProp] = colorProp === 'pointFill' ? this.color : COLOR[(sIdx) % COLOR.length];
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
this.type = 'line';
|
|
@@ -72,7 +72,7 @@ class Line {
|
|
|
72
72
|
extent = this.extent.normal;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const getOpacity = colorStr => (colorStr
|
|
75
|
+
const getOpacity = colorStr => (colorStr?.includes('rgba') ? Util.getOpacity(colorStr) : extent.opacity);
|
|
76
76
|
const mainColor = this.color;
|
|
77
77
|
const mainColorOpacity = getOpacity(mainColor);
|
|
78
78
|
const pointFillColor = this.pointFill;
|
|
@@ -29,10 +29,30 @@ const modules = {
|
|
|
29
29
|
} else {
|
|
30
30
|
this.legendBoxDOM.style.overflowX = 'hidden';
|
|
31
31
|
this.legendBoxDOM.style.overflowY = 'auto';
|
|
32
|
+
this.legendBoxDOM.style.height = '100%';
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
this.legendDOM.appendChild(this.legendBoxDOM);
|
|
35
36
|
this.wrapperDOM.appendChild(this.legendDOM);
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if (this.options.legend.virtualScroll && !this.useTable) {
|
|
40
|
+
this.legendTopSpacer = document.createElement('div');
|
|
41
|
+
this.legendTopSpacer.className = 'ev-chart-legend--top-spacer';
|
|
42
|
+
this.legendTopSpacer.style.clear = 'both';
|
|
43
|
+
this.legendTopSpacer.style.opacity = 0;
|
|
44
|
+
|
|
45
|
+
this.legendBottomSpacer = document.createElement('div');
|
|
46
|
+
this.legendBottomSpacer.className = 'ev-chart-legend--bottom-spacer';
|
|
47
|
+
this.legendBottomSpacer.style.clear = 'both';
|
|
48
|
+
this.legendBottomSpacer.style.opacity = 0;
|
|
49
|
+
|
|
50
|
+
this.legendBoxDOM.appendChild(this.legendTopSpacer);
|
|
51
|
+
this.legendBoxDOM.appendChild(this.legendBottomSpacer);
|
|
52
|
+
this.updateVisibleRowCountFrameId = requestAnimationFrame(() => {
|
|
53
|
+
this.updateVisibleRowCount();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
36
56
|
},
|
|
37
57
|
|
|
38
58
|
/**
|
|
@@ -75,6 +95,7 @@ const modules = {
|
|
|
75
95
|
initLegend() {
|
|
76
96
|
this.isHeatMapType = this.options.type === 'heatMap';
|
|
77
97
|
this.useTable = !!this.options.legend?.table?.use && this.options.type !== 'heatmap' && this.options.type !== 'scatter';
|
|
98
|
+
this.legendItemHeight = 18;
|
|
78
99
|
|
|
79
100
|
if (!this.isInitLegend) {
|
|
80
101
|
this.createLegendLayout();
|
|
@@ -94,6 +115,86 @@ const modules = {
|
|
|
94
115
|
this.isLegendMove = false;
|
|
95
116
|
},
|
|
96
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Calculate and update the number of rows and items per row that are visible
|
|
120
|
+
* within the legend container based on its dimensions and the layout of the legend.
|
|
121
|
+
* If the legend is positioned on the right or left, only one item per row is shown.
|
|
122
|
+
* Otherwise, the number of items per row is determined by dividing the container width
|
|
123
|
+
* by the item width.
|
|
124
|
+
*
|
|
125
|
+
* @returns {undefined}
|
|
126
|
+
*/
|
|
127
|
+
updateVisibleRowCount() {
|
|
128
|
+
const isLeftOrRight = this.options.legend.position === 'right' || this.options.legend.position === 'left';
|
|
129
|
+
const legendBoxHeight = this.legendBoxDOM.clientHeight;
|
|
130
|
+
const legendBoxWidth = this.legendBoxDOM.clientWidth;
|
|
131
|
+
|
|
132
|
+
const itemWidth = Math.max(this.options.legend.width - 8, 1);
|
|
133
|
+
const useLegendSeriesCount = Object.values(this.seriesList)
|
|
134
|
+
.filter(series => series.showLegend !== false)
|
|
135
|
+
.length;
|
|
136
|
+
|
|
137
|
+
this.itemsPerRow = isLeftOrRight ? 1 : Math.floor(legendBoxWidth / itemWidth);
|
|
138
|
+
this.totalRowCount = Math.ceil(useLegendSeriesCount / this.itemsPerRow);
|
|
139
|
+
this.visibleRowCount = legendBoxHeight > this.legendItemHeight
|
|
140
|
+
? Math.round(legendBoxHeight / this.legendItemHeight) + 1 : this.totalRowCount;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Calculate and set the start and end row indexes for visible items within the
|
|
145
|
+
* scrollable legend area. Determines the row range that should be displayed based
|
|
146
|
+
* on the current scroll position and the height of each legend item.
|
|
147
|
+
*
|
|
148
|
+
* @returns {undefined}
|
|
149
|
+
*/
|
|
150
|
+
updateStartEndRowIndex() {
|
|
151
|
+
const index = Math.max(Math.floor(this.legendBoxDOM.scrollTop / this.legendItemHeight), 0);
|
|
152
|
+
this.startRowIndex = index > this.totalRowCount - 1 ? 0 : index;
|
|
153
|
+
this.endRowIndex = this.startRowIndex + this.visibleRowCount;
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Render only the visible legend items in the legend container based on the
|
|
158
|
+
* calculated start and end row indexes. Removes existing legend items,
|
|
159
|
+
* adjusts spacer heights to enable smooth scrolling, and adds only the items
|
|
160
|
+
* within the visible range.
|
|
161
|
+
*
|
|
162
|
+
* @returns {undefined}
|
|
163
|
+
*/
|
|
164
|
+
renderVisibleLegends() {
|
|
165
|
+
this.updateStartEndRowIndex();
|
|
166
|
+
|
|
167
|
+
const elementsToRemove = this.legendBoxDOM.querySelectorAll('.ev-chart-legend-container');
|
|
168
|
+
elementsToRemove.forEach(element => element.remove());
|
|
169
|
+
|
|
170
|
+
const totalScrollHeight = this.totalRowCount * this.legendItemHeight;
|
|
171
|
+
const top = this.startRowIndex * this.legendItemHeight;
|
|
172
|
+
const bottom = Math.max(
|
|
173
|
+
totalScrollHeight - this.visibleRowCount * this.legendItemHeight - top,
|
|
174
|
+
0,
|
|
175
|
+
);
|
|
176
|
+
this.legendTopSpacer.style.height = `${top}px`;
|
|
177
|
+
this.legendBottomSpacer.style.height = `${bottom}px`;
|
|
178
|
+
|
|
179
|
+
const startIndex = this.startRowIndex * this.itemsPerRow;
|
|
180
|
+
const endIndex = this.endRowIndex * this.itemsPerRow;
|
|
181
|
+
|
|
182
|
+
const groups = this.data.groups.at(0);
|
|
183
|
+
|
|
184
|
+
let useLegendSeries = [];
|
|
185
|
+
if (groups) {
|
|
186
|
+
useLegendSeries = groups.slice().reverse()
|
|
187
|
+
.filter(sId => this.seriesList[sId].showLegend)
|
|
188
|
+
.map(sId => [sId, this.seriesList[sId]]);
|
|
189
|
+
} else {
|
|
190
|
+
useLegendSeries = Object.entries(this.seriesList)
|
|
191
|
+
.filter(([, series]) => series.showLegend);
|
|
192
|
+
}
|
|
193
|
+
useLegendSeries.slice(startIndex, endIndex).forEach(([, series]) => {
|
|
194
|
+
this.addLegend(series);
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
|
|
97
198
|
/**
|
|
98
199
|
* Add legend with group information to align each series properly.
|
|
99
200
|
* Especially if a chart is stacked,
|
|
@@ -102,35 +203,75 @@ const modules = {
|
|
|
102
203
|
* @returns {undefined}
|
|
103
204
|
*/
|
|
104
205
|
addLegendList() {
|
|
105
|
-
const groups = this.data
|
|
106
|
-
const seriesList = this
|
|
206
|
+
const { groups } = this.data;
|
|
207
|
+
const { seriesList } = this;
|
|
107
208
|
|
|
209
|
+
if (this.options.legend.virtualScroll) {
|
|
210
|
+
if (this.useTable) {
|
|
211
|
+
this.addLegendForGroups(groups, seriesList, true);
|
|
212
|
+
this.addStandaloneLegends(seriesList, true);
|
|
213
|
+
} else {
|
|
214
|
+
this.renderVisibleLegendsFrameId = requestAnimationFrame(() => {
|
|
215
|
+
this.renderVisibleLegends();
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
this.addLegendForGroups(groups, seriesList, this.useTable);
|
|
220
|
+
this.addStandaloneLegends(seriesList, this.useTable);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Adds legends for each group in `groups` array, iterating through each series
|
|
225
|
+
* within the group in reverse order. This ensures the legends align with the series
|
|
226
|
+
* order as displayed in the chart. Only adds series with `showLegend` set to `true`.
|
|
227
|
+
*
|
|
228
|
+
* @param {Array} groups - Array of groups containing series identifiers.
|
|
229
|
+
* @param {Object} seriesList - Object containing all series, keyed by series ID.
|
|
230
|
+
* @param {boolean} useTable - Determines whether to add legends with additional values.
|
|
231
|
+
* @returns {undefined}
|
|
232
|
+
*/
|
|
233
|
+
addLegendForGroups(groups, seriesList, useTable) {
|
|
108
234
|
groups.forEach((group) => {
|
|
109
235
|
group.slice().reverse().forEach((sId) => {
|
|
110
236
|
const series = seriesList[sId];
|
|
111
|
-
|
|
112
237
|
if (series && series.showLegend) {
|
|
113
|
-
|
|
114
|
-
this.addLegendWithValues(series);
|
|
115
|
-
} else {
|
|
116
|
-
this.addLegend(series);
|
|
117
|
-
}
|
|
238
|
+
this.addLegendBasedOnType(series, useTable);
|
|
118
239
|
}
|
|
119
240
|
});
|
|
120
241
|
});
|
|
121
|
-
|
|
242
|
+
},
|
|
243
|
+
/**
|
|
244
|
+
* Adds legends for series that are not part of any group. Iterates through each series
|
|
245
|
+
* in `seriesList` and only adds those that are not assigned to any group (based on `isExistGrp`)
|
|
246
|
+
* and have `showLegend` set to `true`.
|
|
247
|
+
*
|
|
248
|
+
* @param {Object} seriesList - Object containing all series, keyed by series ID.
|
|
249
|
+
* @param {boolean} useTable - Determines whether to add legends with additional values.
|
|
250
|
+
* @returns {undefined}
|
|
251
|
+
*/
|
|
252
|
+
addStandaloneLegends(seriesList, useTable) {
|
|
122
253
|
Object.values(seriesList).forEach((series) => {
|
|
123
|
-
if (series.isExistGrp
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (this.useTable) {
|
|
128
|
-
this.addLegendWithValues(series);
|
|
129
|
-
} else {
|
|
130
|
-
this.addLegend(series);
|
|
254
|
+
if (!series.isExistGrp && series.showLegend) {
|
|
255
|
+
this.addLegendBasedOnType(series, useTable);
|
|
131
256
|
}
|
|
132
257
|
});
|
|
133
258
|
},
|
|
259
|
+
/**
|
|
260
|
+
* Adds a legend item for a specific series, determining whether to include additional
|
|
261
|
+
* values based on the `useTable` parameter. Calls `addLegendWithValues` if `useTable` is true,
|
|
262
|
+
* otherwise calls `addLegend`.
|
|
263
|
+
*
|
|
264
|
+
* @param {Object} series - Series object containing data to display in the legend.
|
|
265
|
+
* @param {boolean} useTable - Determines whether to add legends with additional values.
|
|
266
|
+
* @returns {undefined}
|
|
267
|
+
*/
|
|
268
|
+
addLegendBasedOnType(series, useTable) {
|
|
269
|
+
if (useTable) {
|
|
270
|
+
this.addLegendWithValues(series);
|
|
271
|
+
} else {
|
|
272
|
+
this.addLegend(series);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
134
275
|
|
|
135
276
|
/**
|
|
136
277
|
* Add Legend with Color Information
|
|
@@ -353,6 +494,11 @@ const modules = {
|
|
|
353
494
|
this.legendBoxDOM.addEventListener('mouseover', this.onLegendBoxOver);
|
|
354
495
|
this.legendBoxDOM.addEventListener('mouseleave', this.onLegendBoxLeave);
|
|
355
496
|
|
|
497
|
+
if (this.options.legend.virtualScroll && !this.useTable) {
|
|
498
|
+
this.legendBoxDOM.addEventListener('resize', this.updateVisibleRowCount);
|
|
499
|
+
this.legendBoxDOM.addEventListener('scroll', this.renderVisibleLegends.bind(this));
|
|
500
|
+
}
|
|
501
|
+
|
|
356
502
|
this.initResizeEvent();
|
|
357
503
|
},
|
|
358
504
|
|
|
@@ -573,6 +719,7 @@ const modules = {
|
|
|
573
719
|
|
|
574
720
|
/**
|
|
575
721
|
* To update legend, remove all of legendBoxDOM's children
|
|
722
|
+
* (except spacers when virtualScroll is enabled)
|
|
576
723
|
*
|
|
577
724
|
* @returns {undefined}
|
|
578
725
|
*/
|
|
@@ -589,12 +736,14 @@ const modules = {
|
|
|
589
736
|
legendTableDOM.removeChild(legendTableDOM.firstChild);
|
|
590
737
|
}
|
|
591
738
|
this.setLegendColumnHeader();
|
|
739
|
+
} else if (this.options.legend.virtualScroll) {
|
|
740
|
+
this.updateVisibleRowCount();
|
|
741
|
+
this.renderVisibleLegends();
|
|
592
742
|
} else {
|
|
593
743
|
while (legendBoxDOM.hasChildNodes()) {
|
|
594
744
|
legendBoxDOM.removeChild(legendBoxDOM.firstChild);
|
|
595
745
|
}
|
|
596
746
|
}
|
|
597
|
-
|
|
598
747
|
this.seriesInfo.count = 0;
|
|
599
748
|
},
|
|
600
749
|
|
|
@@ -606,10 +755,18 @@ const modules = {
|
|
|
606
755
|
destroyLegend() {
|
|
607
756
|
const legendDOM = this.legendDOM;
|
|
608
757
|
|
|
758
|
+
if (this.renderVisibleLegendsFrameId != null) {
|
|
759
|
+
cancelAnimationFrame(this.renderVisibleLegendsFrameId);
|
|
760
|
+
this.renderVisibleLegendsFrameId = null;
|
|
761
|
+
}
|
|
762
|
+
if (this.updateVisibleRowCountFrameId != null) {
|
|
763
|
+
cancelAnimationFrame(this.updateVisibleRowCountFrameId);
|
|
764
|
+
this.updateVisibleRowCountFrameId = null;
|
|
765
|
+
}
|
|
766
|
+
|
|
609
767
|
if (!legendDOM) {
|
|
610
768
|
return;
|
|
611
769
|
}
|
|
612
|
-
|
|
613
770
|
legendDOM.remove();
|
|
614
771
|
|
|
615
772
|
this.legendDOM = null;
|
|
@@ -666,8 +823,6 @@ const modules = {
|
|
|
666
823
|
nameDOM.setAttribute('title', series.name);
|
|
667
824
|
nameDOM.dataset.type = 'name';
|
|
668
825
|
|
|
669
|
-
this.legendDOM.style.padding = '5px 0 0 0';
|
|
670
|
-
|
|
671
826
|
containerDOM.appendChild(colorDOM);
|
|
672
827
|
containerDOM.appendChild(nameDOM);
|
|
673
828
|
|
|
@@ -677,12 +832,12 @@ const modules = {
|
|
|
677
832
|
} else {
|
|
678
833
|
containerDOM.style.width = '100%';
|
|
679
834
|
}
|
|
680
|
-
containerDOM.style.height =
|
|
835
|
+
containerDOM.style.height = `${this.legendItemHeight}px`;
|
|
681
836
|
containerDOM.style.display = 'inline-block';
|
|
682
837
|
containerDOM.style.overflow = 'hidden';
|
|
683
838
|
containerDOM.dataset.type = 'container';
|
|
684
839
|
|
|
685
|
-
this.legendBoxDOM.
|
|
840
|
+
this.legendBoxDOM.insertBefore(containerDOM, this.legendBottomSpacer);
|
|
686
841
|
if (series.show) {
|
|
687
842
|
this.seriesInfo.count++;
|
|
688
843
|
}
|