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