@plcmp/pl-virtual-scroll 1.0.5 → 1.0.7
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/package.json +1 -1
- package/pl-virtual-scroll.js +57 -51
package/package.json
CHANGED
package/pl-virtual-scroll.js
CHANGED
|
@@ -64,7 +64,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
64
64
|
|
|
65
65
|
this.canvas = this.canvas ?? this.$.vsCanvas;
|
|
66
66
|
this.canvas.parentNode.addEventListener('scroll', e => this.onScroll(e));
|
|
67
|
-
|
|
67
|
+
const tplEl = [...this.childNodes].find(n => n.nodeType === document.COMMENT_NODE && n.textContent.startsWith('tpl:'));
|
|
68
68
|
this.sTpl = tplEl?._tpl;
|
|
69
69
|
this._hctx = tplEl?._hctx;
|
|
70
70
|
}
|
|
@@ -72,27 +72,31 @@ class PlVirtualScroll extends PlElement {
|
|
|
72
72
|
_dataChanged(data, old, /** DataMutation */ mutation) {
|
|
73
73
|
// set microtask, element may be not inserted in dom tree yet,
|
|
74
74
|
// but we need to know viewport height to render
|
|
75
|
-
|
|
75
|
+
const [, index, ...rest] = normalizePath(mutation.path);
|
|
76
76
|
switch (mutation.action) {
|
|
77
77
|
case 'upd':
|
|
78
78
|
if (mutation.path === 'items' && Array.isArray(mutation.value) && Array.isArray(mutation.oldValue)) {
|
|
79
79
|
this.phyPool.forEach((i) => {
|
|
80
|
-
if (i.index !== null
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
if (i.index !== null) {
|
|
81
|
+
if (i.index < this.items.length) {
|
|
82
|
+
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
83
|
+
|
|
84
|
+
i.ctx.replace(this.items[i.index]);
|
|
85
|
+
i.ctx.applyEffects(undefined);
|
|
86
|
+
i.ctx._ti.applyBinds();
|
|
87
|
+
} else {
|
|
88
|
+
i.index = null;
|
|
89
|
+
i.offset = -10000;
|
|
90
|
+
fixOffset(i);
|
|
91
|
+
}
|
|
88
92
|
}
|
|
89
93
|
});
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
if (index !== undefined && +index >= 0) {
|
|
93
|
-
|
|
97
|
+
const el = this.phyPool.find(i => i.index === +index);
|
|
94
98
|
if (el && rest.length > 0) {
|
|
95
|
-
|
|
99
|
+
const path = [this.as, ...rest].join('.');
|
|
96
100
|
el.ctx.applyEffects({ ...mutation, path });
|
|
97
101
|
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index]);
|
|
98
102
|
}
|
|
@@ -102,32 +106,34 @@ class PlVirtualScroll extends PlElement {
|
|
|
102
106
|
break;
|
|
103
107
|
|
|
104
108
|
case 'splice': {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
i.index
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
109
|
+
const { index: spliceIndex } = mutation;
|
|
110
|
+
if (Number(spliceIndex) >= 0) {
|
|
111
|
+
// if mutation is not root try to apply effects to children (need when pushing to array inside array)
|
|
112
|
+
if (rest.length > 0) {
|
|
113
|
+
const path = [this.as, ...rest].join('.');
|
|
114
|
+
this.phyPool[index].ctx.applyEffects({ ...mutation, path });
|
|
115
|
+
} else {
|
|
116
|
+
this.phyPool.forEach((i) => {
|
|
117
|
+
if (i.index !== null && i.index >= spliceIndex && i.index < this.items.length) {
|
|
118
|
+
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
119
|
+
|
|
120
|
+
i.ctx.replace(this.items[i.index]);
|
|
121
|
+
i.ctx.applyEffects(undefined);
|
|
122
|
+
i.ctx._ti.applyBinds();
|
|
123
|
+
} else if (i.index >= this.items.length) {
|
|
124
|
+
i.index = null;
|
|
125
|
+
i.offset = -10000;
|
|
126
|
+
fixOffset(i);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
// TODO: add more Heuristic to scroll list if visible elements that not changed? like insert rows before
|
|
132
|
+
// visible area
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
// refresh all PHY if they can be affected
|
|
135
|
+
setTimeout(() => this.render(), 0);
|
|
136
|
+
}
|
|
131
137
|
|
|
132
138
|
break;
|
|
133
139
|
}
|
|
@@ -136,7 +142,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
136
142
|
|
|
137
143
|
render() {
|
|
138
144
|
const canvas = this.canvas;
|
|
139
|
-
|
|
145
|
+
const visibleStart = canvas.parentNode.scrollTop,
|
|
140
146
|
height = canvas.parentNode.offsetHeight,
|
|
141
147
|
visibleEnd = visibleStart + height,
|
|
142
148
|
// render cant complete on too small window, set minimal shadow window
|
|
@@ -161,7 +167,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
161
167
|
|
|
162
168
|
// check items height and offset
|
|
163
169
|
if (this.variableRowHeight) {
|
|
164
|
-
|
|
170
|
+
const firstVisible = used.findIndex(i => i.offset >= visibleStart && i.offset < visibleEnd);
|
|
165
171
|
if (firstVisible >= 0) {
|
|
166
172
|
// fix forward
|
|
167
173
|
for (let i = firstVisible + 1; i < used.length && used[i].offset < shadowEnd; i++) {
|
|
@@ -196,16 +202,16 @@ class PlVirtualScroll extends PlElement {
|
|
|
196
202
|
}
|
|
197
203
|
// filter
|
|
198
204
|
|
|
199
|
-
|
|
205
|
+
const unused = this.phyPool.filter(i => i.index === null);
|
|
200
206
|
|
|
201
207
|
let firstShadow = used.find(i => i.offset + i.h > shadowStart && i.offset < shadowEnd);
|
|
202
208
|
let lastShadow = used.findLast(i => i.offset < shadowEnd && i.offset + i.h > shadowStart);
|
|
203
209
|
|
|
204
210
|
if (!firstShadow && !lastShadow) {
|
|
205
211
|
// jump to nowhere,
|
|
206
|
-
if (this.canvas.parentNode.scrollTop === 0)
|
|
212
|
+
if (this.canvas.parentNode.scrollTop === 0) {
|
|
207
213
|
firstShadow = lastShadow = this.renderItem(0, unused.pop());
|
|
208
|
-
else {
|
|
214
|
+
} else {
|
|
209
215
|
const heightForStart
|
|
210
216
|
= this.phyPool.length > 0
|
|
211
217
|
? this.phyPool.reduce((a, i) => a + i.h, 0) / this.phyPool.length
|
|
@@ -292,7 +298,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
292
298
|
return p_item;
|
|
293
299
|
}
|
|
294
300
|
if (this.items[index] instanceof PlaceHolder) this.items.load?.(this.items[index]);
|
|
295
|
-
|
|
301
|
+
const target = p_item ?? this.createNewItem(this.items[index]);
|
|
296
302
|
|
|
297
303
|
target.index = index;
|
|
298
304
|
if (p_item) {
|
|
@@ -304,7 +310,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
304
310
|
this.phyPool.push(target);
|
|
305
311
|
}
|
|
306
312
|
prev ??= 0;
|
|
307
|
-
target.offset = typeof (prev)
|
|
313
|
+
target.offset = typeof (prev) === 'number' ? prev : (backward ? prev.offset - target.h : prev.offset + prev.h);
|
|
308
314
|
target.ctx._ti._nodes.forEach((n) => {
|
|
309
315
|
if (n.style) {
|
|
310
316
|
n.style.transform = `translateY(${target.offset}px)`;
|
|
@@ -316,12 +322,12 @@ class PlVirtualScroll extends PlElement {
|
|
|
316
322
|
|
|
317
323
|
createNewItem(v) {
|
|
318
324
|
if (!this.sTpl) return;
|
|
319
|
-
|
|
325
|
+
const inst = new TemplateInstance(this.sTpl);
|
|
320
326
|
|
|
321
|
-
|
|
327
|
+
const ctx = new RepeatItem(v, this.as, (ctx, m) => this.onItemChanged(ctx, m));
|
|
322
328
|
ctx._ti = inst;
|
|
323
329
|
inst.attach(this.canvas, undefined, [ctx, ...this._hctx]);
|
|
324
|
-
|
|
330
|
+
const h = !this.variableRowHeight && this.elementHeight ? this.elementHeight : calcNodesRect(inst._nodes).height;
|
|
325
331
|
|
|
326
332
|
if (!this.variableRowHeight && !this.elementHeight) {
|
|
327
333
|
this.elementHeight = h;
|
|
@@ -337,7 +343,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
337
343
|
onItemChanged(ctx, m) {
|
|
338
344
|
// skip replace data call
|
|
339
345
|
if (!m) return;
|
|
340
|
-
|
|
346
|
+
const ind = this.items.findIndex(i => i === ctx[this.as]);
|
|
341
347
|
if (ind < 0) console.warn('repeat item not found');
|
|
342
348
|
if (m.path === this.as) {
|
|
343
349
|
this.set(['items', ind], m.value, m.wmh);
|
|
@@ -375,7 +381,7 @@ function fixOffset(item) {
|
|
|
375
381
|
|
|
376
382
|
function calcNodesRect(nodes) {
|
|
377
383
|
nodes = nodes.filter(n => n.getBoundingClientRect);
|
|
378
|
-
|
|
384
|
+
const rect = nodes[0].getBoundingClientRect();
|
|
379
385
|
let { top, bottom, left, right } = rect;
|
|
380
386
|
({ top, bottom, left, right } = nodes.map(n => n.getBoundingClientRect())
|
|
381
387
|
.filter(i => i)
|
|
@@ -385,9 +391,9 @@ function calcNodesRect(nodes) {
|
|
|
385
391
|
bottom: Math.max(a.bottom, c.bottom),
|
|
386
392
|
left: Math.min(a.left, c.left),
|
|
387
393
|
right: Math.max(a.right, c.right)
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
}),
|
|
395
|
+
{ top, bottom, left, right }));
|
|
396
|
+
const { x, y, height, width } = { x: left, y: top, width: right - left, height: bottom - top };
|
|
391
397
|
return { x, y, height, width };
|
|
392
398
|
}
|
|
393
399
|
|