@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plcmp/pl-virtual-scroll",
3
- "version": "0.1.0",
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.0.0",
26
+ "polylib": "^1.1.2",
27
27
  "@plcmp/utils": "^0.1.0"
28
28
  }
29
29
  }
@@ -1,5 +1,7 @@
1
- import { html, PlElement, TemplateInstance, createContext } from "polylib";
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 #vsCanvas {
35
+ pl-virtual-scroll #vs-canvas {
34
36
  position: relative;
35
37
  /*noinspection CssUnresolvedCustomProperty*/
36
38
  contain: strict;
37
39
  }
38
40
 
39
- .vsItem {
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
- let canvas = this.canvas ?? this.$.vsCanvas;
52
- canvas.parentNode.addEventListener('scroll', this.onScroll.bind(this));
53
- let tpl = this.querySelector('template');
54
- this.oTpl = tpl;
55
- this.rTpl = tpl.tpl;//new Template(`<div class="vsItem">${tpl.innerHTML}</div>`);
56
- this._pti = tpl._pti;
57
- this._hti = tpl._hti;
58
- this.pctx = tpl._pti?.ctx;
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.split('.');
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.ti.applyEffects({ ...mutation, path });
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.ti.ctx = i.ctx;
91
- i.ti.applyEffects();
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
- // detect window height
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.ti._nodes.forEach(i => { if (i.style) i.style.transform = `translateY(-100%)`; });
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.ti.ctx = p_item.ctx;
204
- p_item.ti.applyBinds();
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.ti._nodes.forEach(n => {
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
- let ctx = createContext(this, v, this.as)
220
- let ti = new TemplateInstance(this.rTpl);
221
- ti._hti = this._hti;
222
- ctx._ti = ti;
223
- ti.attach({ ...ctx, root: this.canvas ?? this.$.vsCanvas }, undefined, this._pti);
224
- let rect = calcNodesRect(ti._nodes);
225
- let h = rect.height;
226
-
227
- return { ctx, ti, h };
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) {