@plcmp/pl-virtual-scroll 0.1.14 → 1.0.1
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 +205 -116
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plcmp/pl-virtual-scroll",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "1.0.1",
|
|
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.1.
|
|
26
|
+
"polylib": "^1.1.12",
|
|
27
27
|
"@plcmp/utils": "^0.1.0"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/pl-virtual-scroll.js
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
import { html, PlElement, TemplateInstance } from
|
|
2
|
-
import { PlaceHolder } from
|
|
3
|
-
import {ContextMixin} from
|
|
4
|
-
import {normalizePath} from
|
|
1
|
+
import { html, PlElement, TemplateInstance } from 'polylib';
|
|
2
|
+
import { PlaceHolder } from '@plcmp/utils';
|
|
3
|
+
import { ContextMixin } from 'polylib/engine/v1/ctx.js';
|
|
4
|
+
import { normalizePath } from 'polylib/common.js';
|
|
5
5
|
|
|
6
6
|
/** @typedef VirtualScrollItem
|
|
7
|
-
* @property {
|
|
8
|
-
* @property {TemplateInstance} ti
|
|
9
|
-
* @property {
|
|
10
|
-
* @property {
|
|
7
|
+
* @property { RepeatItem } ctx
|
|
8
|
+
* @property { TemplateInstance } ti
|
|
9
|
+
* @property { number | null } index
|
|
10
|
+
* @property { number } h - height of rendered item
|
|
11
|
+
* @property { number } offset
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
class PlVirtualScroll extends PlElement {
|
|
14
|
-
/** @type VirtualScrollItem[]*/
|
|
15
|
+
/** @type VirtualScrollItem[] */
|
|
15
16
|
phyPool = [];
|
|
17
|
+
/** @type {number | undefined} */
|
|
18
|
+
elementHeight;
|
|
19
|
+
|
|
16
20
|
constructor() {
|
|
17
21
|
super({ lightDom: true });
|
|
18
22
|
}
|
|
23
|
+
|
|
19
24
|
static properties = {
|
|
20
25
|
as: { value: 'item' },
|
|
21
26
|
items: { type: Array, observer: '_dataChanged' },
|
|
22
|
-
renderedStart: { type: Number, value: 0 },
|
|
23
|
-
renderedCount: { type: Number, value: 0 },
|
|
24
27
|
phyItems: { type: Array, value: () => [] },
|
|
25
|
-
canvas: { type: Object }
|
|
26
|
-
|
|
28
|
+
canvas: { type: Object },
|
|
29
|
+
variableRowHeight: { type: Boolean, value: false },
|
|
30
|
+
rowHeight: { type: Number }
|
|
31
|
+
};
|
|
32
|
+
|
|
27
33
|
static template = html`
|
|
28
34
|
<style>
|
|
29
35
|
pl-virtual-scroll {
|
|
@@ -50,37 +56,32 @@ class PlVirtualScroll extends PlElement {
|
|
|
50
56
|
</style>
|
|
51
57
|
<div id="vsCanvas"></div>
|
|
52
58
|
`;
|
|
59
|
+
|
|
53
60
|
static repTpl = html`<template d:repeat="{{phyItems}}" d:as="[[as]]"><div class="vs-item">[[sTpl]]</div></template>`;
|
|
61
|
+
|
|
54
62
|
connectedCallback() {
|
|
55
63
|
super.connectedCallback();
|
|
56
64
|
|
|
57
65
|
this.canvas = this.canvas ?? this.$.vsCanvas;
|
|
58
|
-
this.canvas.parentNode.addEventListener('scroll', e => this.onScroll(e)
|
|
59
|
-
let tplEl = [...this.childNodes].find(
|
|
66
|
+
this.canvas.parentNode.addEventListener('scroll', e => this.onScroll(e));
|
|
67
|
+
let tplEl = [...this.childNodes].find(n => n.nodeType === document.COMMENT_NODE && n.textContent.startsWith('tpl:'));
|
|
60
68
|
this.sTpl = tplEl?._tpl;
|
|
61
69
|
this._hctx = tplEl?._hctx;
|
|
62
|
-
|
|
63
|
-
/* let ti = new TemplateInstance(PlVirtualScroll.repTpl);
|
|
64
|
-
ti.attach(canvas, this, this);
|
|
65
|
-
*/
|
|
66
|
-
/* render items if them already assigned */
|
|
67
|
-
/*if (Array.isArray(this.items) && this.items.length > 0) {
|
|
68
|
-
this.render();
|
|
69
|
-
}*/
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
_dataChanged(data, old, /** DataMutation */ mutation) {
|
|
72
73
|
// set microtask, element may be not inserted in dom tree yet,
|
|
73
74
|
// but we need to know viewport height to render
|
|
74
75
|
let [, index, ...rest] = normalizePath(mutation.path);
|
|
75
76
|
switch (mutation.action) {
|
|
76
77
|
case 'upd':
|
|
77
|
-
if(mutation.path
|
|
78
|
-
this.phyPool.forEach(i => {
|
|
78
|
+
if (mutation.path === 'items' && Array.isArray(mutation.value) && Array.isArray(mutation.oldValue)) {
|
|
79
|
+
this.phyPool.forEach((i) => {
|
|
79
80
|
if (i.index !== null && i.index < this.items.length) {
|
|
80
81
|
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
81
82
|
|
|
82
83
|
i.ctx.replace(this.items[i.index]);
|
|
83
|
-
i.ctx.applyEffects();
|
|
84
|
+
i.ctx.applyEffects(undefined);
|
|
84
85
|
i.ctx._ti.applyBinds();
|
|
85
86
|
} else if (i.index >= this.items.length) {
|
|
86
87
|
i.index = null;
|
|
@@ -93,119 +94,188 @@ class PlVirtualScroll extends PlElement {
|
|
|
93
94
|
if (el && rest.length > 0) {
|
|
94
95
|
let path = [this.as, ...rest].join('.');
|
|
95
96
|
el.ctx.applyEffects({ ...mutation, path });
|
|
96
|
-
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index])
|
|
97
|
+
if (this.items[el.index] instanceof PlaceHolder) this.items.load?.(this.items[el.index]);
|
|
97
98
|
}
|
|
98
99
|
} else {
|
|
99
100
|
setTimeout(() => this.render(), 0);
|
|
100
101
|
}
|
|
101
102
|
break;
|
|
102
|
-
case 'splice':
|
|
103
|
+
case 'splice': {
|
|
103
104
|
let { index: spliceIndex } = mutation;
|
|
104
|
-
// if mutation is not root try to apply effects to
|
|
105
|
-
if(rest.length > 0) {
|
|
105
|
+
// if mutation is not root try to apply effects to children (need when pushing to array inside array)
|
|
106
|
+
if (rest.length > 0) {
|
|
106
107
|
let path = [this.as, ...rest].join('.');
|
|
107
108
|
this.phyPool[index].ctx.applyEffects({ ...mutation, path });
|
|
108
109
|
} else {
|
|
109
|
-
this.phyPool.forEach(i => {
|
|
110
|
+
this.phyPool.forEach((i) => {
|
|
110
111
|
if (i.index !== null && i.index >= spliceIndex && i.index < this.items.length) {
|
|
111
112
|
if (this.items[i.index] instanceof PlaceHolder) this.items.load?.(this.items[i.index]);
|
|
112
|
-
|
|
113
|
+
|
|
113
114
|
i.ctx.replace(this.items[i.index]);
|
|
114
|
-
i.ctx.applyEffects();
|
|
115
|
+
i.ctx.applyEffects(undefined);
|
|
115
116
|
i.ctx._ti.applyBinds();
|
|
116
117
|
} else if (i.index >= this.items.length) {
|
|
117
118
|
i.index = null;
|
|
119
|
+
i.offset = -10000;
|
|
120
|
+
fixOffset(i);
|
|
118
121
|
}
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
//TODO: add more Heuristic to scroll list if visible elements that not changed? like insert rows before
|
|
125
|
+
// TODO: add more Heuristic to scroll list if visible elements that not changed? like insert rows before
|
|
123
126
|
// visible area
|
|
124
127
|
|
|
125
|
-
//refresh all PHY if they can be affected
|
|
126
|
-
|
|
128
|
+
// refresh all PHY if they can be affected
|
|
127
129
|
setTimeout(() => this.render(), 0);
|
|
128
130
|
|
|
129
131
|
break;
|
|
132
|
+
}
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
/*let first = Math.floor(offset / this.elementHeight);
|
|
148
|
-
let last = Math.ceil((offset+height) / this.elementHeight);*/
|
|
149
|
-
// Reset scroll position if update data smaller than current visible index
|
|
150
|
-
if (!scroll && this.items.length < Math.floor((offset + height) / this.elementHeight)) {
|
|
151
|
-
canvas.parentNode.scrollTop = this.items.length * this.elementHeight - height;
|
|
136
|
+
render() {
|
|
137
|
+
const canvas = this.canvas;
|
|
138
|
+
let visibleStart = canvas.parentNode.scrollTop,
|
|
139
|
+
height = canvas.parentNode.offsetHeight,
|
|
140
|
+
visibleEnd = visibleStart + height,
|
|
141
|
+
// render cant complete on too small window, set minimal shadow window
|
|
142
|
+
shadowSize = Math.max(height / 2, 500),
|
|
143
|
+
shadowStart = visibleStart - shadowSize,
|
|
144
|
+
shadowEnd = visibleEnd + shadowSize;
|
|
145
|
+
|
|
146
|
+
// cancel render on invisible canvas or empty data
|
|
147
|
+
if (height === 0 || !this.items || this.items.length === 0) {
|
|
148
|
+
canvas.style.setProperty('height', 0);
|
|
149
|
+
return;
|
|
152
150
|
}
|
|
153
|
-
let shadowEnd = Math.min(this.items.length, Math.ceil((offset + height * 1.5) / this.elementHeight));
|
|
154
|
-
let shadowBegin = Math.max(0, Math.floor(Math.min((offset - height / 2) / this.elementHeight, shadowEnd - height * 2 / this.elementHeight)));
|
|
155
|
-
|
|
156
|
-
let used = [], unused = [];
|
|
157
|
-
this.phyPool.forEach(x => {
|
|
158
|
-
if (x.index !== null && shadowBegin <= x.index && x.index <= shadowEnd && x.index < this.items.length) {
|
|
159
|
-
used.push(x);
|
|
160
|
-
} else {
|
|
161
|
-
unused.push(x);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
151
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
let lastUsedIndex = prev.index;
|
|
170
|
-
for (let i = lastUsedIndex + 1; i <= Math.min(shadowEnd, this.items.length - 1); i++) {
|
|
171
|
-
prev = this.renderItem(i, unused.pop(), prev);
|
|
152
|
+
let used = this.phyPool
|
|
153
|
+
.filter((i) => {
|
|
154
|
+
if (i.offset + i.h < shadowStart || i.offset > shadowEnd) {
|
|
155
|
+
i.index = null;
|
|
172
156
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
157
|
+
return i.index !== null;
|
|
158
|
+
})
|
|
159
|
+
.sort((a, b) => a.index - b.index);
|
|
160
|
+
|
|
161
|
+
// check items height and offset
|
|
162
|
+
if (this.variableRowHeight) {
|
|
163
|
+
let firstVisible = used.findIndex(i => i.offset >= visibleStart && i.offset < visibleEnd);
|
|
164
|
+
if (firstVisible >= 0) {
|
|
165
|
+
// fix forward
|
|
166
|
+
for (let i = firstVisible + 1; i < used.length && used[i].offset < shadowEnd; i++) {
|
|
167
|
+
const newHeight = calcNodesRect(used[i - 1].ctx._ti._nodes).height;
|
|
168
|
+
if (used[i - 1].h !== newHeight) used[i - 1].h = newHeight;
|
|
169
|
+
if (used[i - 1].offset + newHeight !== used[i].offset) {
|
|
170
|
+
used[i].offset = used[i - 1].offset + newHeight;
|
|
171
|
+
fixOffset(used[i]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// fix last height
|
|
175
|
+
const last = used[used.length - 1];
|
|
176
|
+
last.h = calcNodesRect(last.ctx._ti._nodes).height;
|
|
177
|
+
// fix backward
|
|
178
|
+
for (let i = firstVisible - 1; i >= 0 && used[i].offset > shadowStart; i--) {
|
|
179
|
+
const newHeight = calcNodesRect(used[i].ctx._ti._nodes).height;
|
|
180
|
+
if (used[i].h !== newHeight) used[i].h = newHeight;
|
|
181
|
+
if (used[i].offset + newHeight !== used[i + 1].offset) {
|
|
182
|
+
used[i].offset = used[i + 1].offset - newHeight;
|
|
183
|
+
fixOffset(used[i]);
|
|
184
|
+
}
|
|
179
185
|
}
|
|
186
|
+
used = used
|
|
187
|
+
.filter((i) => {
|
|
188
|
+
if (i.offset + i.h < shadowStart || i.offset > shadowEnd) {
|
|
189
|
+
i.index = null;
|
|
190
|
+
}
|
|
191
|
+
return i.index !== null;
|
|
192
|
+
})
|
|
193
|
+
.sort((a, b) => a.index - b.index);
|
|
180
194
|
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
}
|
|
196
|
+
// filter
|
|
197
|
+
|
|
198
|
+
let unused = this.phyPool.filter(i => i.index === null);
|
|
199
|
+
|
|
200
|
+
let firstShadow = used.find(i => i.offset + i.h > shadowStart && i.offset < shadowEnd);
|
|
201
|
+
let lastShadow = used.findLast(i => i.offset < shadowEnd && i.offset + i.h > shadowStart);
|
|
202
|
+
|
|
203
|
+
if (!firstShadow && !lastShadow) {
|
|
204
|
+
// jump to nowhere,
|
|
205
|
+
if (this.canvas.parentNode.scrollTop === 0)
|
|
206
|
+
firstShadow = lastShadow = this.renderItem(0, unused.pop());
|
|
207
|
+
else {
|
|
208
|
+
const heightForStart
|
|
209
|
+
= this.phyPool.length > 0
|
|
210
|
+
? this.phyPool.reduce((a, i) => a + i.h, 0) / this.phyPool.length
|
|
211
|
+
: 32; // TODO: replace w/o constant
|
|
212
|
+
const predictedStart = Math.min(Math.ceil(this.canvas.parentNode.scrollTop / heightForStart), this.items.length - 1);
|
|
213
|
+
firstShadow = lastShadow = this.renderItem(predictedStart, unused.pop(), this.canvas.parentNode.scrollTop);
|
|
214
|
+
used.unshift(firstShadow);
|
|
185
215
|
}
|
|
186
216
|
}
|
|
187
217
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
218
|
+
// render forward
|
|
219
|
+
while (
|
|
220
|
+
lastShadow.offset + lastShadow.h < shadowEnd // последний нарисованный не дотягивает до конца окна рисования
|
|
221
|
+
&& lastShadow.index < this.items.length - 1 // при этом данные еще не кончились
|
|
222
|
+
) {
|
|
223
|
+
lastShadow = this.renderItem(lastShadow ? lastShadow.index + 1 : 0, unused.pop(), lastShadow);
|
|
224
|
+
used.push(lastShadow);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// render backward
|
|
228
|
+
while (
|
|
229
|
+
firstShadow.offset > shadowStart // последний нарисованный не дотягивает до конца окна рисования
|
|
230
|
+
&& firstShadow.index > 0 // при этом данные еще не кончились
|
|
231
|
+
) {
|
|
232
|
+
firstShadow = this.renderItem(firstShadow.index - 1, unused.pop(), firstShadow, true);
|
|
233
|
+
used.unshift(firstShadow);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// move unused to invisible place
|
|
237
|
+
unused.forEach((i) => {
|
|
238
|
+
i.offset = -10000;
|
|
239
|
+
fixOffset(i);
|
|
191
240
|
});
|
|
192
241
|
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
|
|
242
|
+
// calc offset and canvas size
|
|
243
|
+
// TODO: reduce scroll jump
|
|
244
|
+
const avgHeight = used.reduce((a, i) => a + i.h, 0) / used.length;
|
|
245
|
+
|
|
246
|
+
if (lastShadow && !isNaN(avgHeight) && isFinite(avgHeight)) {
|
|
247
|
+
const
|
|
248
|
+
lastRenderedPixel = lastShadow.offset + lastShadow.h,
|
|
249
|
+
restRows = this.items.length - lastShadow.index - 1,
|
|
250
|
+
currentHeight = canvas.offsetHeight,
|
|
251
|
+
predictedHeight = lastRenderedPixel + restRows * avgHeight;
|
|
252
|
+
|
|
253
|
+
if (Math.abs(predictedHeight - currentHeight) > restRows / 10 * avgHeight) {
|
|
254
|
+
canvas.style.setProperty('height', predictedHeight + 'px');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
196
257
|
|
|
258
|
+
if (firstShadow && !isNaN(avgHeight) && isFinite(avgHeight)) {
|
|
259
|
+
const
|
|
260
|
+
firstRenderedPixel = firstShadow.offset,
|
|
261
|
+
restRows = firstShadow.index,
|
|
262
|
+
predictedOffset = firstRenderedPixel - restRows * avgHeight;
|
|
197
263
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
264
|
+
if (Math.abs(predictedOffset) > restRows / 10 * avgHeight) {
|
|
265
|
+
used.forEach((i) => {
|
|
266
|
+
i.offset -= predictedOffset;
|
|
267
|
+
fixOffset(i);
|
|
268
|
+
});
|
|
269
|
+
this.canvas.parentNode.scrollTop -= predictedOffset;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
202
272
|
}
|
|
203
273
|
|
|
204
274
|
/**
|
|
205
275
|
*
|
|
206
276
|
* @param index
|
|
207
277
|
* @param {VirtualScrollItem} p_item
|
|
208
|
-
* @param prev
|
|
278
|
+
* @param [prev]
|
|
209
279
|
* @param [backward]
|
|
210
280
|
* @return {VirtualScrollItem}
|
|
211
281
|
*/
|
|
@@ -216,52 +286,59 @@ class PlVirtualScroll extends PlElement {
|
|
|
216
286
|
if (p_item) p_item.index = null;
|
|
217
287
|
return p_item;
|
|
218
288
|
}
|
|
219
|
-
if (this.items[index] instanceof PlaceHolder) this.items.load?.(this.items[index])
|
|
289
|
+
if (this.items[index] instanceof PlaceHolder) this.items.load?.(this.items[index]);
|
|
220
290
|
let target = p_item ?? this.createNewItem(this.items[index]);
|
|
221
291
|
|
|
222
292
|
target.index = index;
|
|
223
293
|
if (p_item) {
|
|
224
|
-
p_item.ctx.replace(this.items[index])
|
|
294
|
+
p_item.ctx.replace(this.items[index]);
|
|
225
295
|
p_item.ctx._ti.applyBinds();
|
|
226
|
-
p_item.ctx.applyEffects();
|
|
296
|
+
p_item.ctx.applyEffects(undefined);
|
|
297
|
+
if (!this.variableRowHeight) p_item.h = calcNodesRect(p_item.ctx._ti._nodes).height;
|
|
227
298
|
} else {
|
|
228
299
|
this.phyPool.push(target);
|
|
229
300
|
}
|
|
301
|
+
prev ??= 0;
|
|
230
302
|
target.offset = typeof (prev) == 'number' ? prev : (backward ? prev.offset - target.h : prev.offset + prev.h);
|
|
231
|
-
target.ctx._ti._nodes.forEach(n => {
|
|
303
|
+
target.ctx._ti._nodes.forEach((n) => {
|
|
232
304
|
if (n.style) {
|
|
233
305
|
n.style.transform = `translateY(${target.offset}px)`;
|
|
234
306
|
n.style.position = 'absolute';
|
|
235
|
-
n.setAttribute('virtualOffset', target.offset);
|
|
236
307
|
}
|
|
237
308
|
});
|
|
238
309
|
return target;
|
|
239
310
|
}
|
|
311
|
+
|
|
240
312
|
createNewItem(v) {
|
|
241
313
|
if (!this.sTpl) return;
|
|
242
314
|
let inst = new TemplateInstance(this.sTpl);
|
|
243
315
|
|
|
244
|
-
let ctx = new RepeatItem(v, this.as, (ctx, m) => this.onItemChanged(ctx, m)
|
|
245
|
-
ctx._ti = inst
|
|
246
|
-
inst.attach(this.canvas, undefined, [ctx, ...this._hctx
|
|
247
|
-
let h = this.elementHeight
|
|
316
|
+
let ctx = new RepeatItem(v, this.as, (ctx, m) => this.onItemChanged(ctx, m));
|
|
317
|
+
ctx._ti = inst;
|
|
318
|
+
inst.attach(this.canvas, undefined, [ctx, ...this._hctx]);
|
|
319
|
+
let h = !this.variableRowHeight && this.elementHeight ? this.elementHeight : calcNodesRect(inst._nodes).height;
|
|
320
|
+
|
|
321
|
+
if (!this.variableRowHeight && !this.elementHeight) {
|
|
322
|
+
this.elementHeight = h;
|
|
323
|
+
}
|
|
248
324
|
|
|
249
325
|
return { ctx, h };
|
|
250
326
|
}
|
|
327
|
+
|
|
251
328
|
onScroll() {
|
|
252
329
|
this.render(true);
|
|
253
330
|
}
|
|
331
|
+
|
|
254
332
|
onItemChanged(ctx, m) {
|
|
255
333
|
// skip replace data call
|
|
256
334
|
if (!m) return;
|
|
257
|
-
let ind = this.items.findIndex(
|
|
335
|
+
let ind = this.items.findIndex(i => i === ctx[this.as]);
|
|
258
336
|
if (ind < 0) console.warn('repeat item not found');
|
|
259
337
|
if (m.path === this.as) {
|
|
260
338
|
this.set(['items', ind], m.value, m.wmh);
|
|
261
339
|
} else {
|
|
262
|
-
this.forwardNotify(m,this.as, 'items.'+ind);
|
|
340
|
+
this.forwardNotify(m, this.as, 'items.' + ind);
|
|
263
341
|
}
|
|
264
|
-
|
|
265
342
|
}
|
|
266
343
|
}
|
|
267
344
|
|
|
@@ -270,31 +347,43 @@ class RepeatItem extends ContextMixin(EventTarget) {
|
|
|
270
347
|
super();
|
|
271
348
|
this.as = as;
|
|
272
349
|
this[as] = item;
|
|
273
|
-
this.addEffect(as, m => cb(this, m))
|
|
350
|
+
this.addEffect(as, m => cb(this, m));
|
|
274
351
|
}
|
|
352
|
+
|
|
275
353
|
get model() {
|
|
276
354
|
return this[this.as];
|
|
277
355
|
}
|
|
356
|
+
|
|
278
357
|
replace(v) {
|
|
279
358
|
this[this.as] = v;
|
|
280
359
|
this.wmh = {};
|
|
281
360
|
}
|
|
282
361
|
}
|
|
283
362
|
|
|
363
|
+
function fixOffset(item) {
|
|
364
|
+
item.ctx._ti._nodes.forEach((n) => {
|
|
365
|
+
if (n.style) {
|
|
366
|
+
n.style.transform = `translateY(${item.offset}px)`;
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
284
371
|
function calcNodesRect(nodes) {
|
|
285
372
|
nodes = nodes.filter(n => n.getBoundingClientRect);
|
|
286
373
|
let rect = nodes[0].getBoundingClientRect();
|
|
287
374
|
let { top, bottom, left, right } = rect;
|
|
288
|
-
({ top, bottom, left, right } = nodes.map(n => n.getBoundingClientRect())
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
375
|
+
({ top, bottom, left, right } = nodes.map(n => n.getBoundingClientRect())
|
|
376
|
+
.filter(i => i)
|
|
377
|
+
.reduce((a, c) => (
|
|
378
|
+
{
|
|
379
|
+
top: Math.min(a.top, c.top),
|
|
380
|
+
bottom: Math.max(a.bottom, c.bottom),
|
|
381
|
+
left: Math.min(a.left, c.left),
|
|
382
|
+
right: Math.max(a.right, c.right)
|
|
383
|
+
})
|
|
295
384
|
, { top, bottom, left, right }));
|
|
296
385
|
let { x, y, height, width } = { x: left, y: top, width: right - left, height: bottom - top };
|
|
297
386
|
return { x, y, height, width };
|
|
298
387
|
}
|
|
299
388
|
|
|
300
|
-
customElements.define('pl-virtual-scroll', PlVirtualScroll);
|
|
389
|
+
customElements.define('pl-virtual-scroll', PlVirtualScroll);
|