@plcmp/pl-virtual-scroll 0.0.2 → 0.1.2
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 +3 -3
- package/pl-virtual-scroll.js +71 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plcmp/pl-virtual-scroll",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.2",
|
|
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.
|
|
27
|
-
"@plcmp/utils": "^0.0
|
|
26
|
+
"polylib": "^1.1.0",
|
|
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,53 @@ 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
|
-
|
|
55
|
-
this.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
|
|
57
|
+
this.canvas = this.canvas ?? this.$.vsCanvas;
|
|
58
|
+
this.canvas.parentNode.addEventListener('scroll', e => this.onScroll(e) );
|
|
59
|
+
|
|
60
|
+
this.sTpl = [...this.childNodes].find( n => n.nodeType === document.COMMENT_NODE && n.textContent.startsWith('tpl:'))?._tpl;
|
|
61
|
+
|
|
62
|
+
/* let ti = new TemplateInstance(PlVirtualScroll.repTpl);
|
|
63
|
+
ti.attach(canvas, this, this);
|
|
64
|
+
*/
|
|
59
65
|
/* render items if them already assigned */
|
|
60
|
-
if (Array.isArray(this.items) && this.items.length > 0) {
|
|
66
|
+
/*if (Array.isArray(this.items) && this.items.length > 0) {
|
|
61
67
|
this.render();
|
|
62
|
-
}
|
|
68
|
+
}*/
|
|
63
69
|
}
|
|
64
70
|
_dataChanged(data, old, mutation) {
|
|
65
71
|
// set microtask, element may be not inserted in dom tree yet,
|
|
66
72
|
// but we need to know viewport height to render
|
|
67
|
-
let [, index, ...rest] = mutation.path
|
|
73
|
+
let [, index, ...rest] = normalizePath(mutation.path);
|
|
68
74
|
switch (mutation.action) {
|
|
69
75
|
case 'upd':
|
|
70
76
|
if (index !== undefined && +index >= 0) {
|
|
71
77
|
let el = this.phyPool.find(i => i.index === +index);
|
|
72
78
|
if (el && rest.length > 0) {
|
|
73
79
|
let path = [this.as, ...rest].join('.');
|
|
74
|
-
el.
|
|
80
|
+
el.ctx.applyEffects({ ...mutation, path });
|
|
81
|
+
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index])
|
|
75
82
|
}
|
|
76
83
|
} else {
|
|
77
84
|
setTimeout(() => this.render(), 0);
|
|
@@ -86,10 +93,10 @@ class PlVirtualScroll extends PlElement {
|
|
|
86
93
|
//refresh all PHY if they can be affected
|
|
87
94
|
this.phyPool.forEach(i => {
|
|
88
95
|
if (i.index !== null && i.index >= spliceIndex && i.index < this.items.length) {
|
|
96
|
+
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
89
97
|
i.ctx.replace(this.items[i.index]);
|
|
90
|
-
i.
|
|
91
|
-
i.
|
|
92
|
-
i.ti.applyBinds();
|
|
98
|
+
i.ctx.applyEffects();
|
|
99
|
+
i.ctx._ti.applyBinds();
|
|
93
100
|
} else if (i.index >= this.items.length) {
|
|
94
101
|
i.index = null;
|
|
95
102
|
}
|
|
@@ -105,9 +112,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
105
112
|
* @param {Boolean} [scroll] - render for new scroll position
|
|
106
113
|
*/
|
|
107
114
|
render(scroll) {
|
|
108
|
-
|
|
109
|
-
// detect new position,
|
|
110
|
-
let canvas = this.canvas ?? this.$.vsCanvas;
|
|
115
|
+
let canvas = this.canvas;
|
|
111
116
|
let offset = canvas.parentNode.scrollTop;
|
|
112
117
|
let height = canvas.parentNode.offsetHeight;
|
|
113
118
|
if (height === 0 || !this.items) return;
|
|
@@ -128,12 +133,6 @@ class PlVirtualScroll extends PlElement {
|
|
|
128
133
|
let used = [], unused = [];
|
|
129
134
|
this.phyPool.forEach(x => {
|
|
130
135
|
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
136
|
used.push(x);
|
|
138
137
|
} else {
|
|
139
138
|
unused.push(x);
|
|
@@ -165,7 +164,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
165
164
|
|
|
166
165
|
unused.forEach(u => {
|
|
167
166
|
u.index = null;
|
|
168
|
-
u.
|
|
167
|
+
u.ctx._ti._nodes.forEach(i => { if (i.style) i.style.transform = `translateY(-100%)`; });
|
|
169
168
|
});
|
|
170
169
|
|
|
171
170
|
// fill .5 height window in background
|
|
@@ -200,14 +199,13 @@ class PlVirtualScroll extends PlElement {
|
|
|
200
199
|
target.index = index;
|
|
201
200
|
if (p_item) {
|
|
202
201
|
p_item.ctx.replace(this.items[index])
|
|
203
|
-
p_item.
|
|
204
|
-
p_item.
|
|
205
|
-
p_item.ti.applyEffects();
|
|
202
|
+
p_item.ctx._ti.applyBinds();
|
|
203
|
+
p_item.ctx.applyEffects();
|
|
206
204
|
} else {
|
|
207
205
|
this.phyPool.push(target);
|
|
208
206
|
}
|
|
209
207
|
target.offset = typeof (prev) == 'number' ? prev : (backward ? prev.offset - target.h : prev.offset + prev.h);
|
|
210
|
-
target.
|
|
208
|
+
target.ctx._ti._nodes.forEach(n => {
|
|
211
209
|
if (n.style) {
|
|
212
210
|
n.style.transform = `translateY(${target.offset}px)`;
|
|
213
211
|
n.style.position = 'absolute';
|
|
@@ -216,19 +214,47 @@ class PlVirtualScroll extends PlElement {
|
|
|
216
214
|
return target;
|
|
217
215
|
}
|
|
218
216
|
createNewItem(v) {
|
|
219
|
-
|
|
220
|
-
let
|
|
221
|
-
|
|
222
|
-
ctx
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
let h =
|
|
226
|
-
|
|
227
|
-
return { ctx,
|
|
217
|
+
if (!this.sTpl) return;
|
|
218
|
+
let inst = new TemplateInstance(this.sTpl);
|
|
219
|
+
|
|
220
|
+
let ctx = new RepeatItem(v, this.as, (ctx, m) => this.onItemChanged(ctx, m) );
|
|
221
|
+
ctx._ti = inst
|
|
222
|
+
inst.attach(this.canvas, undefined, [ctx, ...this.sTpl._hctx ]);
|
|
223
|
+
let h = this.elementHeight ?? calcNodesRect(inst._nodes).height;
|
|
224
|
+
|
|
225
|
+
return { ctx, h };
|
|
228
226
|
}
|
|
229
227
|
onScroll() {
|
|
230
228
|
this.render(true);
|
|
231
229
|
}
|
|
230
|
+
onItemChanged(ctx, m) {
|
|
231
|
+
// skip replace data call
|
|
232
|
+
if (!m) return;
|
|
233
|
+
let ind = this.items.findIndex( i => i === ctx[this.as]);
|
|
234
|
+
if (ind < 0) console.warn('repeat item not found');
|
|
235
|
+
if (m.path === this.as) {
|
|
236
|
+
this.set(['items', ind], m.value, m.wmh);
|
|
237
|
+
} else {
|
|
238
|
+
this.forwardNotify(m,this.as, 'items.'+ind);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
class RepeatItem extends ContextMixin(EventTarget) {
|
|
245
|
+
constructor(item, as, cb) {
|
|
246
|
+
super();
|
|
247
|
+
this.as = as;
|
|
248
|
+
this[as] = item;
|
|
249
|
+
this.addEffect(as, m => cb(this, m))
|
|
250
|
+
}
|
|
251
|
+
get model() {
|
|
252
|
+
return this[this.as];
|
|
253
|
+
}
|
|
254
|
+
replace(v) {
|
|
255
|
+
this[this.as] = v;
|
|
256
|
+
this.wmh = {};
|
|
257
|
+
}
|
|
232
258
|
}
|
|
233
259
|
|
|
234
260
|
function calcNodesRect(nodes) {
|