@plcmp/pl-virtual-scroll 0.1.0 → 0.1.3
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 +2 -2
- package/pl-virtual-scroll.js +72 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plcmp/pl-virtual-scroll",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Component for lazy list render.",
|
|
5
5
|
"main": "pl-virtual-scroll.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"polylib": "^1.
|
|
26
|
+
"polylib": "^1.1.2",
|
|
27
27
|
"@plcmp/utils": "^0.1.0"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/pl-virtual-scroll.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { html, PlElement, TemplateInstance
|
|
1
|
+
import { html, PlElement, TemplateInstance } from "polylib";
|
|
2
2
|
import { PlaceHolder } from "@plcmp/utils";
|
|
3
|
+
import {ContextMixin} from "polylib/engine/v1/ctx.js";
|
|
4
|
+
import {normalizePath} from "polylib/common.js";
|
|
3
5
|
|
|
4
6
|
/** @typedef VirtualScrollItem
|
|
5
7
|
* @property {LightDataContext} ctx
|
|
@@ -20,7 +22,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
20
22
|
renderedStart: { type: Number, value: 0 },
|
|
21
23
|
renderedCount: { type: Number, value: 0 },
|
|
22
24
|
phyItems: { type: Array, value: () => [] },
|
|
23
|
-
canvas: { type: Object }
|
|
25
|
+
canvas: { type: Object },
|
|
24
26
|
}
|
|
25
27
|
static template = html`
|
|
26
28
|
<style>
|
|
@@ -30,48 +32,54 @@ class PlVirtualScroll extends PlElement {
|
|
|
30
32
|
display: block;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
pl-virtual-scroll #
|
|
35
|
+
pl-virtual-scroll #vs-canvas {
|
|
34
36
|
position: relative;
|
|
35
37
|
/*noinspection CssUnresolvedCustomProperty*/
|
|
36
38
|
contain: strict;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
.
|
|
41
|
+
.vs-item {
|
|
40
42
|
position: absolute;
|
|
41
43
|
left: 0;
|
|
42
44
|
top: 0;
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
47
|
+
#vsCanvas {
|
|
48
|
+
position: relative;
|
|
43
49
|
}
|
|
44
50
|
</style>
|
|
45
|
-
<div id="vsCanvas">
|
|
46
|
-
</div>
|
|
51
|
+
<div id="vsCanvas"></div>
|
|
47
52
|
`;
|
|
48
|
-
|
|
53
|
+
static repTpl = html`<template d:repeat="{{phyItems}}" d:as="[[as]]"><div class="vs-item">[[sTpl]]</div></template>`;
|
|
49
54
|
connectedCallback() {
|
|
50
55
|
super.connectedCallback();
|
|
51
|
-
|
|
52
|
-
canvas
|
|
53
|
-
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
|
|
57
|
+
this.canvas = this.canvas ?? this.$.vsCanvas;
|
|
58
|
+
this.canvas.parentNode.addEventListener('scroll', e => this.onScroll(e) );
|
|
59
|
+
let tplEl = [...this.childNodes].find( n => n.nodeType === document.COMMENT_NODE && n.textContent.startsWith('tpl:'));
|
|
60
|
+
this.sTpl = tplEl?._tpl;
|
|
61
|
+
this._hctx = tplEl?._hctx;
|
|
62
|
+
|
|
63
|
+
/* let ti = new TemplateInstance(PlVirtualScroll.repTpl);
|
|
64
|
+
ti.attach(canvas, this, this);
|
|
65
|
+
*/
|
|
59
66
|
/* render items if them already assigned */
|
|
60
|
-
if (Array.isArray(this.items) && this.items.length > 0) {
|
|
67
|
+
/*if (Array.isArray(this.items) && this.items.length > 0) {
|
|
61
68
|
this.render();
|
|
62
|
-
}
|
|
69
|
+
}*/
|
|
63
70
|
}
|
|
64
71
|
_dataChanged(data, old, mutation) {
|
|
65
72
|
// set microtask, element may be not inserted in dom tree yet,
|
|
66
73
|
// but we need to know viewport height to render
|
|
67
|
-
let [, index, ...rest] = mutation.path
|
|
74
|
+
let [, index, ...rest] = normalizePath(mutation.path);
|
|
68
75
|
switch (mutation.action) {
|
|
69
76
|
case 'upd':
|
|
70
77
|
if (index !== undefined && +index >= 0) {
|
|
71
78
|
let el = this.phyPool.find(i => i.index === +index);
|
|
72
79
|
if (el && rest.length > 0) {
|
|
73
80
|
let path = [this.as, ...rest].join('.');
|
|
74
|
-
el.
|
|
81
|
+
el.ctx.applyEffects({ ...mutation, path });
|
|
82
|
+
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index])
|
|
75
83
|
}
|
|
76
84
|
} else {
|
|
77
85
|
setTimeout(() => this.render(), 0);
|
|
@@ -86,10 +94,10 @@ class PlVirtualScroll extends PlElement {
|
|
|
86
94
|
//refresh all PHY if they can be affected
|
|
87
95
|
this.phyPool.forEach(i => {
|
|
88
96
|
if (i.index !== null && i.index >= spliceIndex && i.index < this.items.length) {
|
|
97
|
+
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
89
98
|
i.ctx.replace(this.items[i.index]);
|
|
90
|
-
i.
|
|
91
|
-
i.
|
|
92
|
-
i.ti.applyBinds();
|
|
99
|
+
i.ctx.applyEffects();
|
|
100
|
+
i.ctx._ti.applyBinds();
|
|
93
101
|
} else if (i.index >= this.items.length) {
|
|
94
102
|
i.index = null;
|
|
95
103
|
}
|
|
@@ -105,9 +113,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
105
113
|
* @param {Boolean} [scroll] - render for new scroll position
|
|
106
114
|
*/
|
|
107
115
|
render(scroll) {
|
|
108
|
-
|
|
109
|
-
// detect new position,
|
|
110
|
-
let canvas = this.canvas ?? this.$.vsCanvas;
|
|
116
|
+
let canvas = this.canvas;
|
|
111
117
|
let offset = canvas.parentNode.scrollTop;
|
|
112
118
|
let height = canvas.parentNode.offsetHeight;
|
|
113
119
|
if (height === 0 || !this.items) return;
|
|
@@ -128,12 +134,6 @@ class PlVirtualScroll extends PlElement {
|
|
|
128
134
|
let used = [], unused = [];
|
|
129
135
|
this.phyPool.forEach(x => {
|
|
130
136
|
if (x.index !== null && shadowBegin <= x.index && x.index <= shadowEnd && x.index < this.items.length) {
|
|
131
|
-
if (x.ctx.model !== this.items[x.index]) {
|
|
132
|
-
x.ctx.replace(this.items[x.index]);
|
|
133
|
-
x.ti.ctx = x.ctx;
|
|
134
|
-
x.ti.applyBinds();
|
|
135
|
-
x.ti.applyEffects();
|
|
136
|
-
}
|
|
137
137
|
used.push(x);
|
|
138
138
|
} else {
|
|
139
139
|
unused.push(x);
|
|
@@ -165,7 +165,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
165
165
|
|
|
166
166
|
unused.forEach(u => {
|
|
167
167
|
u.index = null;
|
|
168
|
-
u.
|
|
168
|
+
u.ctx._ti._nodes.forEach(i => { if (i.style) i.style.transform = `translateY(-100%)`; });
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
// fill .5 height window in background
|
|
@@ -200,14 +200,13 @@ class PlVirtualScroll extends PlElement {
|
|
|
200
200
|
target.index = index;
|
|
201
201
|
if (p_item) {
|
|
202
202
|
p_item.ctx.replace(this.items[index])
|
|
203
|
-
p_item.
|
|
204
|
-
p_item.
|
|
205
|
-
p_item.ti.applyEffects();
|
|
203
|
+
p_item.ctx._ti.applyBinds();
|
|
204
|
+
p_item.ctx.applyEffects();
|
|
206
205
|
} else {
|
|
207
206
|
this.phyPool.push(target);
|
|
208
207
|
}
|
|
209
208
|
target.offset = typeof (prev) == 'number' ? prev : (backward ? prev.offset - target.h : prev.offset + prev.h);
|
|
210
|
-
target.
|
|
209
|
+
target.ctx._ti._nodes.forEach(n => {
|
|
211
210
|
if (n.style) {
|
|
212
211
|
n.style.transform = `translateY(${target.offset}px)`;
|
|
213
212
|
n.style.position = 'absolute';
|
|
@@ -216,19 +215,47 @@ class PlVirtualScroll extends PlElement {
|
|
|
216
215
|
return target;
|
|
217
216
|
}
|
|
218
217
|
createNewItem(v) {
|
|
219
|
-
|
|
220
|
-
let
|
|
221
|
-
|
|
222
|
-
ctx
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
let h =
|
|
226
|
-
|
|
227
|
-
return { ctx,
|
|
218
|
+
if (!this.sTpl) return;
|
|
219
|
+
let inst = new TemplateInstance(this.sTpl);
|
|
220
|
+
|
|
221
|
+
let ctx = new RepeatItem(v, this.as, (ctx, m) => this.onItemChanged(ctx, m) );
|
|
222
|
+
ctx._ti = inst
|
|
223
|
+
inst.attach(this.canvas, undefined, [ctx, ...this._hctx ]);
|
|
224
|
+
let h = this.elementHeight ?? calcNodesRect(inst._nodes).height;
|
|
225
|
+
|
|
226
|
+
return { ctx, h };
|
|
228
227
|
}
|
|
229
228
|
onScroll() {
|
|
230
229
|
this.render(true);
|
|
231
230
|
}
|
|
231
|
+
onItemChanged(ctx, m) {
|
|
232
|
+
// skip replace data call
|
|
233
|
+
if (!m) return;
|
|
234
|
+
let ind = this.items.findIndex( i => i === ctx[this.as]);
|
|
235
|
+
if (ind < 0) console.warn('repeat item not found');
|
|
236
|
+
if (m.path === this.as) {
|
|
237
|
+
this.set(['items', ind], m.value, m.wmh);
|
|
238
|
+
} else {
|
|
239
|
+
this.forwardNotify(m,this.as, 'items.'+ind);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class RepeatItem extends ContextMixin(EventTarget) {
|
|
246
|
+
constructor(item, as, cb) {
|
|
247
|
+
super();
|
|
248
|
+
this.as = as;
|
|
249
|
+
this[as] = item;
|
|
250
|
+
this.addEffect(as, m => cb(this, m))
|
|
251
|
+
}
|
|
252
|
+
get model() {
|
|
253
|
+
return this[this.as];
|
|
254
|
+
}
|
|
255
|
+
replace(v) {
|
|
256
|
+
this[this.as] = v;
|
|
257
|
+
this.wmh = {};
|
|
258
|
+
}
|
|
232
259
|
}
|
|
233
260
|
|
|
234
261
|
function calcNodesRect(nodes) {
|