hy-virtual-tree 1.1.18 → 1.1.20
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 +17 -2
- package/dist/index.js +277 -297
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### 1.1.20
|
|
4
|
+
|
|
5
|
+
_2025-09-11_
|
|
6
|
+
|
|
7
|
+
- 扩展[VirtualTree] 功能
|
|
8
|
+
(1)修复 CustomRenderFn 函数类型返回对象类型没有内置一层dom元素bug
|
|
9
|
+
|
|
10
|
+
### 1.1.19
|
|
11
|
+
|
|
12
|
+
_2025-09-10_
|
|
13
|
+
|
|
14
|
+
- 扩展[VirtualTree] 功能
|
|
15
|
+
(1)引入 js-booster 代码到本地,并添加 onRender 回调函数
|
|
16
|
+
(2)CustomRenderFn 函数类型添加 { el: Element, $destroy: Function } 返回类型,其中 $destroy 在节点销毁时调用
|
|
17
|
+
|
|
3
18
|
### 1.1.18
|
|
4
19
|
|
|
5
20
|
_2025-09-10_
|
|
6
21
|
|
|
7
22
|
- 扩展[VirtualTree] 功能
|
|
8
|
-
(1)修复CustomRenderFn
|
|
23
|
+
(1)修复CustomRenderFn 函数类型内置一层dom元素bug
|
|
9
24
|
|
|
10
25
|
### 1.1.17
|
|
11
26
|
|
|
12
27
|
_2025-09-10_
|
|
13
28
|
|
|
14
29
|
- 扩展[VirtualTree] 功能
|
|
15
|
-
(1)CustomRenderFn
|
|
30
|
+
(1)CustomRenderFn 函数类型内置一层dom元素,兼容vue2的new Vue()挂载问题
|
|
16
31
|
|
|
17
32
|
### 1.1.16
|
|
18
33
|
|
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, inset = false } = 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) && inset) {
|
|
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,58 @@ 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
|
+
let dom = fn(node.data, node);
|
|
1684
|
+
if (isObject(dom) && dom.el) {
|
|
1685
|
+
$destroy = dom.$destroy;
|
|
1686
|
+
dom = dom.el;
|
|
1687
|
+
}
|
|
1688
|
+
if (isBoolean(dom) && dom) {
|
|
1689
|
+
value = defaultFn && isFunction(defaultFn) && defaultFn(node.data, node);
|
|
1690
|
+
}
|
|
1691
|
+
else if (isElement(dom) && inset) {
|
|
1692
|
+
value = document.createElement('div');
|
|
1693
|
+
className && value.classList.add(className);
|
|
1694
|
+
value.appendChild(dom);
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
value = dom;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
if (isObject(value) && value.el) {
|
|
1701
|
+
$destroy = value.$destroy;
|
|
1702
|
+
value = value.el;
|
|
1703
|
+
}
|
|
1704
|
+
if (isEmpty(value))
|
|
1705
|
+
return;
|
|
1706
|
+
this._destroyDomSet.add(() => {
|
|
1707
|
+
$destroy && $destroy();
|
|
1708
|
+
$destroy = null;
|
|
1709
|
+
value = null;
|
|
1710
|
+
el = null;
|
|
1711
|
+
});
|
|
1712
|
+
if (isElement(value)) {
|
|
1713
|
+
className && value.classList.add(className);
|
|
1714
|
+
if (parent) {
|
|
1715
|
+
parent.appendChild(value);
|
|
1716
|
+
}
|
|
1717
|
+
return value;
|
|
1718
|
+
}
|
|
1719
|
+
el = document.createElement('div');
|
|
1720
|
+
el.innerHTML = value;
|
|
1721
|
+
className && el.classList.add(className);
|
|
1722
|
+
if (parent) {
|
|
1723
|
+
parent.appendChild(el);
|
|
1724
|
+
}
|
|
1725
|
+
return el;
|
|
1726
|
+
}
|
|
1753
1727
|
/** 渲染 */
|
|
1754
1728
|
async _render(config) {
|
|
1755
1729
|
if (this._virtualScroll)
|
|
@@ -1837,14 +1811,14 @@ class VirtualTree {
|
|
|
1837
1811
|
let statusSlot, rightSlot;
|
|
1838
1812
|
// 是否显示状态
|
|
1839
1813
|
if (this._props.showStatus) {
|
|
1840
|
-
statusSlot =
|
|
1814
|
+
statusSlot = this._customRender(config.renderStatus, item, {
|
|
1841
1815
|
inset: true,
|
|
1842
1816
|
className: 'hy-tree-node__status',
|
|
1843
1817
|
defaultFn: (data, node) => useStatus(config, data)
|
|
1844
1818
|
});
|
|
1845
1819
|
}
|
|
1846
1820
|
// 右侧内容
|
|
1847
|
-
rightSlot =
|
|
1821
|
+
rightSlot = this._customRender(config.renderRight, item, { inset: true, className: 'hy-tree-node__right-content' });
|
|
1848
1822
|
if (!isElement(statusSlot) && !isElement(rightSlot))
|
|
1849
1823
|
return;
|
|
1850
1824
|
const el = document.createElement('div');
|
|
@@ -1937,19 +1911,19 @@ class VirtualTree {
|
|
|
1937
1911
|
}
|
|
1938
1912
|
}
|
|
1939
1913
|
// 整个节点内容
|
|
1940
|
-
|
|
1914
|
+
this._customRender(config.renderItem, item, {
|
|
1941
1915
|
inset: true,
|
|
1942
1916
|
parent: content,
|
|
1943
1917
|
className: 'hy-tree-node__node-content',
|
|
1944
1918
|
defaultFn: (data, node) => {
|
|
1945
1919
|
// 图标
|
|
1946
|
-
|
|
1920
|
+
this._customRender(config.renderIcon, node, { inset: true, parent: content, className: 'hy-tree-icon', defaultFn: generateIcon });
|
|
1947
1921
|
// 内容
|
|
1948
|
-
|
|
1922
|
+
this._customRender(config.renderContent, node, { inset: true, parent: content, className: 'hy-tree-node__content', defaultFn: generateContent });
|
|
1949
1923
|
// 统计
|
|
1950
|
-
|
|
1924
|
+
this._customRender(generateStats, node, { parent: content });
|
|
1951
1925
|
// 右侧内容
|
|
1952
|
-
|
|
1926
|
+
this._customRender(generateRight, node, { parent: content });
|
|
1953
1927
|
}
|
|
1954
1928
|
});
|
|
1955
1929
|
nodeContainer.appendChild(content);
|
|
@@ -2030,7 +2004,13 @@ class VirtualTree {
|
|
|
2030
2004
|
items: this._flattenTree,
|
|
2031
2005
|
itemHeight: config.itemHeight + (config.itemGap || 0),
|
|
2032
2006
|
bufferSize: config.bufferSize,
|
|
2033
|
-
renderItem
|
|
2007
|
+
renderItem,
|
|
2008
|
+
onRender: () => {
|
|
2009
|
+
for (const $destroy of this._destroyDomSet) {
|
|
2010
|
+
$destroy && $destroy();
|
|
2011
|
+
}
|
|
2012
|
+
this._destroyDomSet.clear();
|
|
2013
|
+
}
|
|
2034
2014
|
});
|
|
2035
2015
|
config.onLoad && config.onLoad();
|
|
2036
2016
|
}
|
|
@@ -2176,4 +2156,4 @@ class VirtualTree {
|
|
|
2176
2156
|
};
|
|
2177
2157
|
}
|
|
2178
2158
|
|
|
2179
|
-
export { VirtualTree, isArray, isBoolean, isElement, isEmpty, isFunction, isNull, isNumber, isObject, isString, isUndefined };
|
|
2159
|
+
export { VirtualScroll, VirtualTree, isArray, isBoolean, isElement, isEmpty, isFunction, isNull, isNumber, isObject, isString, isUndefined };
|