hy-virtual-tree 1.1.17 → 1.1.19
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/CHANGELOG.md +16 -1
- package/dist/index.js +275 -299
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### 1.1.19
|
|
4
|
+
|
|
5
|
+
_2025-09-10_
|
|
6
|
+
|
|
7
|
+
- 扩展[VirtualTree] 功能
|
|
8
|
+
(1)引入 js-booster 代码到本地,并添加 onRender 回调函数
|
|
9
|
+
(2)CustomRenderFn 函数类型添加 { el: Element, $destroy: Function } 返回类型,其中 $destroy 在节点销毁时调用
|
|
10
|
+
|
|
11
|
+
### 1.1.18
|
|
12
|
+
|
|
13
|
+
_2025-09-10_
|
|
14
|
+
|
|
15
|
+
- 扩展[VirtualTree] 功能
|
|
16
|
+
(1)修复CustomRenderFn 函数类型内置一层dom元素bug
|
|
17
|
+
|
|
3
18
|
### 1.1.17
|
|
4
19
|
|
|
5
20
|
_2025-09-10_
|
|
6
21
|
|
|
7
22
|
- 扩展[VirtualTree] 功能
|
|
8
|
-
(1)CustomRenderFn
|
|
23
|
+
(1)CustomRenderFn 函数类型内置一层dom元素,兼容vue2的new Vue()挂载问题
|
|
9
24
|
|
|
10
25
|
### 1.1.16
|
|
11
26
|
|
package/dist/index.js
CHANGED
|
@@ -1,259 +1,218 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* js-booster - High-performance frontend library
|
|
3
|
-
* VirtualScroll - Virtual scrolling implementation
|
|
4
|
-
* @version "1.1.4"
|
|
5
|
-
* @author https://cg-zhou.top/
|
|
6
|
-
* @license MIT
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
class VirtualScroll {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (header) {
|
|
69
|
-
this.scrollContainer.appendChild(header);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Create content wrapper
|
|
74
|
-
this.contentWrapper = document.createElement('div');
|
|
75
|
-
// Add inline styles
|
|
76
|
-
Object.assign(this.contentWrapper.style, {
|
|
77
|
-
position: 'relative',
|
|
78
|
-
width: '100%'
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Use scaled height to ensure it doesn't exceed browser limits
|
|
82
|
-
const scaledHeight = this.totalHeight * this.heightScale;
|
|
83
|
-
this.contentWrapper.style.height = `${scaledHeight}px`;
|
|
84
|
-
|
|
85
|
-
// Create content container
|
|
86
|
-
this.contentContainer = document.createElement('div');
|
|
87
|
-
// Add inline styles
|
|
88
|
-
Object.assign(this.contentContainer.style, {
|
|
89
|
-
position: 'absolute',
|
|
90
|
-
width: '100%',
|
|
91
|
-
left: '0'
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Add scroll event listener
|
|
95
|
-
this.scrollContainer.addEventListener('scroll', this.handleScroll.bind(this));
|
|
96
|
-
|
|
97
|
-
// Assemble DOM
|
|
98
|
-
this.contentWrapper.appendChild(this.contentContainer);
|
|
99
|
-
this.scrollContainer.appendChild(this.contentWrapper);
|
|
100
|
-
this.container.appendChild(this.scrollContainer);
|
|
101
|
-
|
|
102
|
-
// Render initial visible items
|
|
103
|
-
this.renderVisibleItems(0, Math.min(100, this.items.length));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Handle scroll event
|
|
108
|
-
* @private
|
|
109
|
-
*/
|
|
110
|
-
handleScroll() {
|
|
111
|
-
const scrollTop = this.scrollContainer.scrollTop;
|
|
112
|
-
const containerHeight = this.scrollContainer.clientHeight;
|
|
113
|
-
|
|
114
|
-
// Consider scaling factor in calculations
|
|
115
|
-
const realScrollTop = scrollTop / this.heightScale;
|
|
116
|
-
|
|
117
|
-
// Calculate visible range
|
|
118
|
-
const startIndex = Math.max(0, Math.floor(realScrollTop / this.itemHeight) - this.bufferSize);
|
|
119
|
-
const endIndex = Math.min(this.items.length, Math.ceil((realScrollTop + containerHeight / this.heightScale) / this.itemHeight) + this.bufferSize);
|
|
120
|
-
|
|
121
|
-
// Only update when visible range changes
|
|
122
|
-
if (startIndex !== this.visibleStartIndex || endIndex !== this.visibleEndIndex || endIndex === 0) {
|
|
123
|
-
this.renderVisibleItems(startIndex, endIndex);
|
|
124
|
-
this.visibleStartIndex = startIndex;
|
|
125
|
-
this.visibleEndIndex = endIndex;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Render visible items
|
|
131
|
-
* @param {number} startIndex Start index
|
|
132
|
-
* @param {number} endIndex End index
|
|
133
|
-
* @private
|
|
134
|
-
*/
|
|
135
|
-
renderVisibleItems(startIndex, endIndex) {
|
|
136
|
-
// Clear content container
|
|
137
|
-
this.contentContainer.innerHTML = '';
|
|
138
|
-
|
|
139
|
-
// Set position considering scaling factor
|
|
140
|
-
this.contentContainer.style.transform = `translateY(${startIndex * this.itemHeight * this.heightScale}px)`;
|
|
141
|
-
|
|
142
|
-
// Render visible items
|
|
143
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
144
|
-
const item = this.items[i];
|
|
145
|
-
if (this.customRenderItem) {
|
|
146
|
-
// Use custom render function
|
|
147
|
-
const itemElement = this.customRenderItem(item, i);
|
|
148
|
-
if (itemElement) {
|
|
149
|
-
// Only set necessary height styles, other styles are determined by the caller
|
|
150
|
-
itemElement.style.height = `${this.itemHeight * this.heightScale}px`;
|
|
151
|
-
itemElement.style.boxSizing = 'border-box';
|
|
152
|
-
itemElement.style.width = '100%';
|
|
153
|
-
this.contentContainer.appendChild(itemElement);
|
|
2
|
+
/**
|
|
3
|
+
* Create a virtual scroll instance
|
|
4
|
+
* @param {Object} options Configuration options
|
|
5
|
+
* @param {HTMLElement} options.container Scroll container element
|
|
6
|
+
* @param {Array} options.items Data items to display
|
|
7
|
+
* @param {number} [options.itemHeight=20] Height of each list item (pixels)
|
|
8
|
+
* @param {number} [options.bufferSize=10] Number of buffer items outside the visible area
|
|
9
|
+
* @param {Function} [options.renderItem] Custom item rendering function
|
|
10
|
+
* @param {Function} [options.renderHeader] Custom header rendering function
|
|
11
|
+
* @param {number} [options.maxHeight=26840000] Maximum height in pixels for the content wrapper
|
|
12
|
+
* @param {Function} [options.onRender] 节点刷新回调
|
|
13
|
+
*/
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.container = options.container;
|
|
16
|
+
this.items = options.items || [];
|
|
17
|
+
this.itemHeight = options.itemHeight || 20;
|
|
18
|
+
this.bufferSize = options.bufferSize || 10;
|
|
19
|
+
this.customRenderItem = options.renderItem;
|
|
20
|
+
this.customRenderHeader = options.renderHeader;
|
|
21
|
+
this.maxHeight = options.maxHeight || 26840000; // Add maximum height limit to prevent DOM height overflow
|
|
22
|
+
this.visibleStartIndex = 0;
|
|
23
|
+
this.visibleEndIndex = 0;
|
|
24
|
+
this.scrollContainer = null;
|
|
25
|
+
this.contentWrapper = null;
|
|
26
|
+
this.contentContainer = null;
|
|
27
|
+
this.totalHeight = this.items.length * this.itemHeight;
|
|
28
|
+
this.heightScale = 1; // Height scaling factor
|
|
29
|
+
this._onRender = options.onRender;
|
|
30
|
+
// If total height exceeds maximum height, calculate scaling factor
|
|
31
|
+
if (this.totalHeight > this.maxHeight) {
|
|
32
|
+
this.heightScale = this.maxHeight / this.totalHeight;
|
|
33
|
+
}
|
|
34
|
+
this.initialize();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initialize virtual scroll component
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
initialize() {
|
|
41
|
+
// Clear container
|
|
42
|
+
this.container.innerHTML = '';
|
|
43
|
+
// Create scroll container
|
|
44
|
+
this.scrollContainer = document.createElement('div');
|
|
45
|
+
// Add inline styles
|
|
46
|
+
Object.assign(this.scrollContainer.style, {
|
|
47
|
+
flex: '1',
|
|
48
|
+
overflow: 'auto',
|
|
49
|
+
position: 'relative',
|
|
50
|
+
minHeight: '0',
|
|
51
|
+
height: '100%',
|
|
52
|
+
boxSizing: 'border-box'
|
|
53
|
+
});
|
|
54
|
+
// If there's a custom header render function, render the header
|
|
55
|
+
if (this.customRenderHeader) {
|
|
56
|
+
const header = this.customRenderHeader();
|
|
57
|
+
if (header) {
|
|
58
|
+
this.scrollContainer.appendChild(header);
|
|
59
|
+
}
|
|
154
60
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Object.assign(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
61
|
+
// Create content wrapper
|
|
62
|
+
this.contentWrapper = document.createElement('div');
|
|
63
|
+
// Add inline styles
|
|
64
|
+
Object.assign(this.contentWrapper.style, {
|
|
65
|
+
position: 'relative',
|
|
66
|
+
width: '100%'
|
|
67
|
+
});
|
|
68
|
+
// Use scaled height to ensure it doesn't exceed browser limits
|
|
69
|
+
const scaledHeight = this.totalHeight * this.heightScale;
|
|
70
|
+
this.contentWrapper.style.height = `${scaledHeight}px`;
|
|
71
|
+
// Create content container
|
|
72
|
+
this.contentContainer = document.createElement('div');
|
|
73
|
+
// Add inline styles
|
|
74
|
+
Object.assign(this.contentContainer.style, {
|
|
75
|
+
position: 'absolute',
|
|
76
|
+
width: '100%',
|
|
77
|
+
left: '0'
|
|
164
78
|
});
|
|
165
|
-
|
|
166
|
-
this.
|
|
167
|
-
|
|
79
|
+
// Add scroll event listener
|
|
80
|
+
this.scrollContainer.addEventListener('scroll', this.handleScroll.bind(this));
|
|
81
|
+
// Assemble DOM
|
|
82
|
+
this.contentWrapper.appendChild(this.contentContainer);
|
|
83
|
+
this.scrollContainer.appendChild(this.contentWrapper);
|
|
84
|
+
this.container.appendChild(this.scrollContainer);
|
|
85
|
+
// Render initial visible items
|
|
86
|
+
this.renderVisibleItems(0, Math.min(100, this.items.length));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Handle scroll event
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
handleScroll() {
|
|
93
|
+
const scrollTop = this.scrollContainer.scrollTop;
|
|
94
|
+
const containerHeight = this.scrollContainer.clientHeight;
|
|
95
|
+
// Consider scaling factor in calculations
|
|
96
|
+
const realScrollTop = scrollTop / this.heightScale;
|
|
97
|
+
// Calculate visible range
|
|
98
|
+
const startIndex = Math.max(0, Math.floor(realScrollTop / this.itemHeight) - this.bufferSize);
|
|
99
|
+
const endIndex = Math.min(this.items.length, Math.ceil((realScrollTop + containerHeight / this.heightScale) / this.itemHeight) + this.bufferSize);
|
|
100
|
+
// Only update when visible range changes
|
|
101
|
+
if (startIndex !== this.visibleStartIndex ||
|
|
102
|
+
endIndex !== this.visibleEndIndex ||
|
|
103
|
+
endIndex === 0) {
|
|
104
|
+
this.renderVisibleItems(startIndex, endIndex);
|
|
105
|
+
this.visibleStartIndex = startIndex;
|
|
106
|
+
this.visibleEndIndex = endIndex;
|
|
107
|
+
}
|
|
168
108
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Render visible items
|
|
111
|
+
* @param {number} startIndex Start index
|
|
112
|
+
* @param {number} endIndex End index
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
renderVisibleItems(startIndex, endIndex) {
|
|
116
|
+
this._onRender && this._onRender();
|
|
117
|
+
// Clear content container
|
|
118
|
+
this.contentContainer.innerHTML = '';
|
|
119
|
+
// Set position considering scaling factor
|
|
120
|
+
this.contentContainer.style.transform = `translateY(${startIndex * this.itemHeight * this.heightScale}px)`;
|
|
121
|
+
// Render visible items
|
|
122
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
123
|
+
const item = this.items[i];
|
|
124
|
+
if (this.customRenderItem) {
|
|
125
|
+
// Use custom render function
|
|
126
|
+
const itemElement = this.customRenderItem(item, i);
|
|
127
|
+
if (itemElement) {
|
|
128
|
+
// Only set necessary height styles, other styles are determined by the caller
|
|
129
|
+
itemElement.style.height = `${this.itemHeight * this.heightScale}px`;
|
|
130
|
+
itemElement.style.boxSizing = 'border-box';
|
|
131
|
+
itemElement.style.width = '100%';
|
|
132
|
+
this.contentContainer.appendChild(itemElement);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Use default rendering - very simple default implementation
|
|
137
|
+
const row = document.createElement('div');
|
|
138
|
+
Object.assign(row.style, {
|
|
139
|
+
height: `${this.itemHeight * this.heightScale}px`,
|
|
140
|
+
width: '100%',
|
|
141
|
+
boxSizing: 'border-box',
|
|
142
|
+
padding: '8px',
|
|
143
|
+
borderBottom: '1px solid #eee'
|
|
144
|
+
});
|
|
145
|
+
row.textContent = JSON.stringify(item);
|
|
146
|
+
this.contentContainer.appendChild(row);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
184
149
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Update data items and re-render
|
|
152
|
+
* @param {Array} items New data items array
|
|
153
|
+
* @public
|
|
154
|
+
*/
|
|
155
|
+
updateItems(items) {
|
|
156
|
+
this.items = items || [];
|
|
157
|
+
this.totalHeight = this.items.length * this.itemHeight;
|
|
158
|
+
// Recalculate scaling factor
|
|
159
|
+
this.heightScale = 1;
|
|
160
|
+
if (this.totalHeight > this.maxHeight) {
|
|
161
|
+
this.heightScale = this.maxHeight / this.totalHeight;
|
|
162
|
+
}
|
|
163
|
+
// Ensure height is set correctly
|
|
164
|
+
if (this.contentWrapper) {
|
|
165
|
+
this.contentWrapper.style.height = `${this.totalHeight * this.heightScale}px`;
|
|
166
|
+
}
|
|
167
|
+
this.visibleStartIndex = 0;
|
|
168
|
+
this.visibleEndIndex = 0;
|
|
169
|
+
// Force recalculation of visible items
|
|
170
|
+
this.handleScroll();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Scroll to specified index
|
|
174
|
+
* @param {number} index Index of the item to scroll to
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
scrollToIndex(index) {
|
|
178
|
+
if (index >= 0 && index < this.items.length) {
|
|
179
|
+
// Apply scaling factor when scrolling
|
|
180
|
+
this.scrollContainer.scrollTop =
|
|
181
|
+
index * this.itemHeight * this.heightScale;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Destroy component, remove event listeners, etc.
|
|
186
|
+
* @public
|
|
187
|
+
*/
|
|
188
|
+
destroy() {
|
|
189
|
+
if (this.scrollContainer) {
|
|
190
|
+
this.scrollContainer.removeEventListener('scroll', this.handleScroll);
|
|
191
|
+
}
|
|
192
|
+
if (this.container) {
|
|
193
|
+
this.container.innerHTML = '';
|
|
194
|
+
}
|
|
195
|
+
this.items = null;
|
|
196
|
+
this.container = null;
|
|
197
|
+
this.scrollContainer = null;
|
|
198
|
+
this.contentWrapper = null;
|
|
199
|
+
this.contentContainer = null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Refresh virtual scroll, re-render current visible items
|
|
203
|
+
* @public
|
|
204
|
+
*/
|
|
205
|
+
refresh() {
|
|
206
|
+
this.handleScroll();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get scroll container element
|
|
210
|
+
* @returns {HTMLElement} Scroll container element
|
|
211
|
+
* @public
|
|
212
|
+
*/
|
|
213
|
+
getScrollContainer() {
|
|
214
|
+
return this.scrollContainer;
|
|
189
215
|
}
|
|
190
|
-
this.visibleStartIndex = 0;
|
|
191
|
-
this.visibleEndIndex = 0;
|
|
192
|
-
|
|
193
|
-
// Force recalculation of visible items
|
|
194
|
-
this.handleScroll();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Scroll to specified index
|
|
199
|
-
* @param {number} index Index of the item to scroll to
|
|
200
|
-
* @public
|
|
201
|
-
*/
|
|
202
|
-
scrollToIndex(index) {
|
|
203
|
-
if (index >= 0 && index < this.items.length) {
|
|
204
|
-
// Apply scaling factor when scrolling
|
|
205
|
-
this.scrollContainer.scrollTop = index * this.itemHeight * this.heightScale;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Destroy component, remove event listeners, etc.
|
|
211
|
-
* @public
|
|
212
|
-
*/
|
|
213
|
-
destroy() {
|
|
214
|
-
if (this.scrollContainer) {
|
|
215
|
-
this.scrollContainer.removeEventListener('scroll', this.handleScroll);
|
|
216
|
-
}
|
|
217
|
-
if (this.container) {
|
|
218
|
-
this.container.innerHTML = '';
|
|
219
|
-
}
|
|
220
|
-
this.items = null;
|
|
221
|
-
this.container = null;
|
|
222
|
-
this.scrollContainer = null;
|
|
223
|
-
this.contentWrapper = null;
|
|
224
|
-
this.contentContainer = null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Refresh virtual scroll, re-render current visible items
|
|
229
|
-
* @public
|
|
230
|
-
*/
|
|
231
|
-
refresh() {
|
|
232
|
-
this.handleScroll();
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Get scroll container element
|
|
237
|
-
* @returns {HTMLElement} Scroll container element
|
|
238
|
-
* @public
|
|
239
|
-
*/
|
|
240
|
-
getScrollContainer() {
|
|
241
|
-
return this.scrollContainer;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* js-booster - High-performance frontend library
|
|
247
|
-
* @version "1.1.4"
|
|
248
|
-
* @author https://cg-zhou.top/
|
|
249
|
-
* @license MIT
|
|
250
|
-
*/
|
|
251
|
-
|
|
252
|
-
// If in browser environment, add to global object
|
|
253
|
-
if (typeof window !== 'undefined') {
|
|
254
|
-
window.JsBooster = {
|
|
255
|
-
VirtualScroll
|
|
256
|
-
};
|
|
257
216
|
}
|
|
258
217
|
|
|
259
218
|
const isString = (e) => typeof e === 'string';
|
|
@@ -1399,44 +1358,6 @@ function getContainer(config) {
|
|
|
1399
1358
|
}
|
|
1400
1359
|
return el;
|
|
1401
1360
|
}
|
|
1402
|
-
/** 获取自定义元素函数的值 */
|
|
1403
|
-
function customRender(fn, node, options) {
|
|
1404
|
-
const { parent, className, defaultFn } = options || {};
|
|
1405
|
-
let value;
|
|
1406
|
-
if (!fn || !isFunction(fn)) {
|
|
1407
|
-
value = defaultFn && isFunction(defaultFn) && defaultFn(node.data, node);
|
|
1408
|
-
}
|
|
1409
|
-
else {
|
|
1410
|
-
const dom = fn(node.data, node);
|
|
1411
|
-
if (isBoolean(dom) && dom) {
|
|
1412
|
-
value = defaultFn && isFunction(defaultFn) && defaultFn(node.data, node);
|
|
1413
|
-
}
|
|
1414
|
-
else if (isElement(dom)) {
|
|
1415
|
-
value = document.createElement('div');
|
|
1416
|
-
className && value.classList.add(className);
|
|
1417
|
-
value.appendChild(dom);
|
|
1418
|
-
}
|
|
1419
|
-
else {
|
|
1420
|
-
value = dom;
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
if (isEmpty(value))
|
|
1424
|
-
return;
|
|
1425
|
-
if (isElement(value)) {
|
|
1426
|
-
className && value.classList.add(className);
|
|
1427
|
-
if (parent) {
|
|
1428
|
-
parent.appendChild(value);
|
|
1429
|
-
}
|
|
1430
|
-
return value;
|
|
1431
|
-
}
|
|
1432
|
-
const el = document.createElement('div');
|
|
1433
|
-
el.innerHTML = value;
|
|
1434
|
-
className && el.classList.add(className);
|
|
1435
|
-
if (parent) {
|
|
1436
|
-
parent.appendChild(el);
|
|
1437
|
-
}
|
|
1438
|
-
return el;
|
|
1439
|
-
}
|
|
1440
1361
|
class VirtualTree {
|
|
1441
1362
|
_virtualScroll;
|
|
1442
1363
|
_el = null;
|
|
@@ -1462,6 +1383,7 @@ class VirtualTree {
|
|
|
1462
1383
|
_hiddenExpandIconKeySet = new Set(); // 隐藏展开图标
|
|
1463
1384
|
_hiddenNodeKeySet = new Set(); // 隐藏的key
|
|
1464
1385
|
_flattenTree = []; // 平铺树列表
|
|
1386
|
+
_destroyDomSet = new Set();
|
|
1465
1387
|
_handleMethod;
|
|
1466
1388
|
_filterMethod;
|
|
1467
1389
|
_isForceHiddenExpandIcon;
|
|
@@ -1750,6 +1672,54 @@ class VirtualTree {
|
|
|
1750
1672
|
this._onNodeExpand && this._onNodeExpand(node.data, node);
|
|
1751
1673
|
}
|
|
1752
1674
|
}
|
|
1675
|
+
/** 获取自定义元素函数的值 */
|
|
1676
|
+
_customRender(fn, node, options) {
|
|
1677
|
+
const { parent, className, defaultFn, inset = false } = options || {};
|
|
1678
|
+
let value, el, $destroy;
|
|
1679
|
+
if (!fn || !isFunction(fn)) {
|
|
1680
|
+
value = defaultFn && isFunction(defaultFn) && defaultFn(node.data, node);
|
|
1681
|
+
}
|
|
1682
|
+
else {
|
|
1683
|
+
const dom = fn(node.data, node);
|
|
1684
|
+
if (isBoolean(dom) && dom) {
|
|
1685
|
+
value = defaultFn && isFunction(defaultFn) && defaultFn(node.data, node);
|
|
1686
|
+
}
|
|
1687
|
+
else if (isElement(dom) && inset) {
|
|
1688
|
+
value = document.createElement('div');
|
|
1689
|
+
className && value.classList.add(className);
|
|
1690
|
+
value.appendChild(dom);
|
|
1691
|
+
}
|
|
1692
|
+
else {
|
|
1693
|
+
value = dom;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
if (isObject(value) && value.el) {
|
|
1697
|
+
$destroy = value.$destroy;
|
|
1698
|
+
value = value.el;
|
|
1699
|
+
}
|
|
1700
|
+
if (isEmpty(value))
|
|
1701
|
+
return;
|
|
1702
|
+
this._destroyDomSet.add(() => {
|
|
1703
|
+
$destroy && $destroy();
|
|
1704
|
+
$destroy = null;
|
|
1705
|
+
value = null;
|
|
1706
|
+
el = null;
|
|
1707
|
+
});
|
|
1708
|
+
if (isElement(value)) {
|
|
1709
|
+
className && value.classList.add(className);
|
|
1710
|
+
if (parent) {
|
|
1711
|
+
parent.appendChild(value);
|
|
1712
|
+
}
|
|
1713
|
+
return value;
|
|
1714
|
+
}
|
|
1715
|
+
el = document.createElement('div');
|
|
1716
|
+
el.innerHTML = value;
|
|
1717
|
+
className && el.classList.add(className);
|
|
1718
|
+
if (parent) {
|
|
1719
|
+
parent.appendChild(el);
|
|
1720
|
+
}
|
|
1721
|
+
return el;
|
|
1722
|
+
}
|
|
1753
1723
|
/** 渲染 */
|
|
1754
1724
|
async _render(config) {
|
|
1755
1725
|
if (this._virtualScroll)
|
|
@@ -1837,15 +1807,14 @@ class VirtualTree {
|
|
|
1837
1807
|
let statusSlot, rightSlot;
|
|
1838
1808
|
// 是否显示状态
|
|
1839
1809
|
if (this._props.showStatus) {
|
|
1840
|
-
statusSlot =
|
|
1810
|
+
statusSlot = this._customRender(config.renderStatus, item, {
|
|
1811
|
+
inset: true,
|
|
1841
1812
|
className: 'hy-tree-node__status',
|
|
1842
1813
|
defaultFn: (data, node) => useStatus(config, data)
|
|
1843
1814
|
});
|
|
1844
1815
|
}
|
|
1845
1816
|
// 右侧内容
|
|
1846
|
-
|
|
1847
|
-
rightSlot = customRender(config.renderRight, item, { className: 'hy-tree-node__right-content' });
|
|
1848
|
-
}
|
|
1817
|
+
rightSlot = this._customRender(config.renderRight, item, { inset: true, className: 'hy-tree-node__right-content' });
|
|
1849
1818
|
if (!isElement(statusSlot) && !isElement(rightSlot))
|
|
1850
1819
|
return;
|
|
1851
1820
|
const el = document.createElement('div');
|
|
@@ -1938,18 +1907,19 @@ class VirtualTree {
|
|
|
1938
1907
|
}
|
|
1939
1908
|
}
|
|
1940
1909
|
// 整个节点内容
|
|
1941
|
-
|
|
1910
|
+
this._customRender(config.renderItem, item, {
|
|
1911
|
+
inset: true,
|
|
1942
1912
|
parent: content,
|
|
1943
1913
|
className: 'hy-tree-node__node-content',
|
|
1944
1914
|
defaultFn: (data, node) => {
|
|
1945
1915
|
// 图标
|
|
1946
|
-
|
|
1916
|
+
this._customRender(config.renderIcon, node, { inset: true, parent: content, className: 'hy-tree-icon', defaultFn: generateIcon });
|
|
1947
1917
|
// 内容
|
|
1948
|
-
|
|
1918
|
+
this._customRender(config.renderContent, node, { inset: true, parent: content, className: 'hy-tree-node__content', defaultFn: generateContent });
|
|
1949
1919
|
// 统计
|
|
1950
|
-
|
|
1920
|
+
this._customRender(generateStats, node, { parent: content });
|
|
1951
1921
|
// 右侧内容
|
|
1952
|
-
|
|
1922
|
+
this._customRender(generateRight, node, { parent: content });
|
|
1953
1923
|
}
|
|
1954
1924
|
});
|
|
1955
1925
|
nodeContainer.appendChild(content);
|
|
@@ -2030,7 +2000,13 @@ class VirtualTree {
|
|
|
2030
2000
|
items: this._flattenTree,
|
|
2031
2001
|
itemHeight: config.itemHeight + (config.itemGap || 0),
|
|
2032
2002
|
bufferSize: config.bufferSize,
|
|
2033
|
-
renderItem
|
|
2003
|
+
renderItem,
|
|
2004
|
+
onRender: () => {
|
|
2005
|
+
for (const $destroy of this._destroyDomSet) {
|
|
2006
|
+
$destroy && $destroy();
|
|
2007
|
+
}
|
|
2008
|
+
this._destroyDomSet.clear();
|
|
2009
|
+
}
|
|
2034
2010
|
});
|
|
2035
2011
|
config.onLoad && config.onLoad();
|
|
2036
2012
|
}
|
|
@@ -2176,4 +2152,4 @@ class VirtualTree {
|
|
|
2176
2152
|
};
|
|
2177
2153
|
}
|
|
2178
2154
|
|
|
2179
|
-
export { VirtualTree, isArray, isBoolean, isElement, isEmpty, isFunction, isNull, isNumber, isObject, isString, isUndefined };
|
|
2155
|
+
export { VirtualScroll, VirtualTree, isArray, isBoolean, isElement, isEmpty, isFunction, isNull, isNumber, isObject, isString, isUndefined };
|