@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plcmp/pl-virtual-scroll",
3
- "version": "0.1.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.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,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
+ 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 });
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.ti.ctx = i.ctx;
93
- i.ti.applyEffects();
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
- // detect window height
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.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(-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.ti.ctx = p_item.ctx;
206
- p_item.ti.applyBinds();
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.ti._nodes.forEach(n => {
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
- let ctx = createContext(this, v, this.as)
222
- let ti = new TemplateInstance(this.rTpl);
223
- ti._hti = this._hti;
224
- ctx._ti = ti;
225
- ti.attach({ ...ctx, root: this.canvas ?? this.$.vsCanvas }, undefined, this._pti);
226
- let h = this.elementHeight ?? calcNodesRect(ti._nodes).height;
227
-
228
- 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 };
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) {