@plcmp/pl-virtual-scroll 0.1.1 → 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 +2 -2
- package/pl-virtual-scroll.js +69 -44
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.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.
|
|
26
|
+
"polylib": "^1.1.0",
|
|
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,52 @@ 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 });
|
|
75
81
|
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index])
|
|
76
82
|
}
|
|
77
83
|
} else {
|
|
@@ -89,9 +95,8 @@ class PlVirtualScroll extends PlElement {
|
|
|
89
95
|
if (i.index !== null && i.index >= spliceIndex && i.index < this.items.length) {
|
|
90
96
|
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
91
97
|
i.ctx.replace(this.items[i.index]);
|
|
92
|
-
i.
|
|
93
|
-
i.
|
|
94
|
-
i.ti.applyBinds();
|
|
98
|
+
i.ctx.applyEffects();
|
|
99
|
+
i.ctx._ti.applyBinds();
|
|
95
100
|
} else if (i.index >= this.items.length) {
|
|
96
101
|
i.index = null;
|
|
97
102
|
}
|
|
@@ -107,9 +112,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
107
112
|
* @param {Boolean} [scroll] - render for new scroll position
|
|
108
113
|
*/
|
|
109
114
|
render(scroll) {
|
|
110
|
-
|
|
111
|
-
// detect new position,
|
|
112
|
-
let canvas = this.canvas ?? this.$.vsCanvas;
|
|
115
|
+
let canvas = this.canvas;
|
|
113
116
|
let offset = canvas.parentNode.scrollTop;
|
|
114
117
|
let height = canvas.parentNode.offsetHeight;
|
|
115
118
|
if (height === 0 || !this.items) return;
|
|
@@ -130,12 +133,6 @@ class PlVirtualScroll extends PlElement {
|
|
|
130
133
|
let used = [], unused = [];
|
|
131
134
|
this.phyPool.forEach(x => {
|
|
132
135
|
if (x.index !== null && shadowBegin <= x.index && x.index <= shadowEnd && x.index < this.items.length) {
|
|
133
|
-
if (x.ctx.model !== this.items[x.index]) {
|
|
134
|
-
x.ctx.replace(this.items[x.index]);
|
|
135
|
-
x.ti.ctx = x.ctx;
|
|
136
|
-
x.ti.applyBinds();
|
|
137
|
-
x.ti.applyEffects();
|
|
138
|
-
}
|
|
139
136
|
used.push(x);
|
|
140
137
|
} else {
|
|
141
138
|
unused.push(x);
|
|
@@ -167,7 +164,7 @@ class PlVirtualScroll extends PlElement {
|
|
|
167
164
|
|
|
168
165
|
unused.forEach(u => {
|
|
169
166
|
u.index = null;
|
|
170
|
-
u.
|
|
167
|
+
u.ctx._ti._nodes.forEach(i => { if (i.style) i.style.transform = `translateY(-100%)`; });
|
|
171
168
|
});
|
|
172
169
|
|
|
173
170
|
// fill .5 height window in background
|
|
@@ -202,14 +199,13 @@ class PlVirtualScroll extends PlElement {
|
|
|
202
199
|
target.index = index;
|
|
203
200
|
if (p_item) {
|
|
204
201
|
p_item.ctx.replace(this.items[index])
|
|
205
|
-
p_item.
|
|
206
|
-
p_item.
|
|
207
|
-
p_item.ti.applyEffects();
|
|
202
|
+
p_item.ctx._ti.applyBinds();
|
|
203
|
+
p_item.ctx.applyEffects();
|
|
208
204
|
} else {
|
|
209
205
|
this.phyPool.push(target);
|
|
210
206
|
}
|
|
211
207
|
target.offset = typeof (prev) == 'number' ? prev : (backward ? prev.offset - target.h : prev.offset + prev.h);
|
|
212
|
-
target.
|
|
208
|
+
target.ctx._ti._nodes.forEach(n => {
|
|
213
209
|
if (n.style) {
|
|
214
210
|
n.style.transform = `translateY(${target.offset}px)`;
|
|
215
211
|
n.style.position = 'absolute';
|
|
@@ -218,18 +214,47 @@ class PlVirtualScroll extends PlElement {
|
|
|
218
214
|
return target;
|
|
219
215
|
}
|
|
220
216
|
createNewItem(v) {
|
|
221
|
-
|
|
222
|
-
let
|
|
223
|
-
|
|
224
|
-
ctx
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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 };
|
|
229
226
|
}
|
|
230
227
|
onScroll() {
|
|
231
228
|
this.render(true);
|
|
232
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
|
+
}
|
|
233
258
|
}
|
|
234
259
|
|
|
235
260
|
function calcNodesRect(nodes) {
|