@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plcmp/pl-virtual-scroll",
3
- "version": "0.0.2",
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.0.0",
27
- "@plcmp/utils": "^0.0.2"
26
+ "polylib": "^1.1.0",
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,53 @@ 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
+
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.split('.');
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.ti.applyEffects({ ...mutation, path });
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.ti.ctx = i.ctx;
91
- i.ti.applyEffects();
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
- // detect window height
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.ti._nodes.forEach(i => { if (i.style) i.style.transform = `translateY(-100%)`; });
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.ti.ctx = p_item.ctx;
204
- p_item.ti.applyBinds();
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.ti._nodes.forEach(n => {
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
- 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 };
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) {