@thi.ng/rdom
Version:
Lightweight, reactive, VDOM-less UI/DOM components with async lifecycle and @thi.ng/hiccup compatible
114 lines (113 loc) • 3.18 kB
JavaScript
import { $compile } from "./compile.js";
import { Component } from "./component.js";
import { __nextID } from "./idgen.js";
import { $subWithID } from "./sub.js";
const $klist = (src, tag, attribs, childCtor, keyFn) => $subWithID(
src,
new KList(tag, attribs, childCtor, keyFn),
__nextID("klist", src)
);
class KList extends Component {
constructor(tag, attribs, ctor, keyFn = (_, i) => i) {
super();
this.tag = tag;
this.attribs = attribs;
this.ctor = ctor;
this.keyFn = keyFn;
}
items = [];
cache;
async mount(parent, index, state) {
this.items = [];
this.cache = /* @__PURE__ */ new Map();
this.el = this.$el(this.tag, this.attribs, null, parent, index);
this.update(state);
return this.el;
}
async unmount() {
this.items.forEach((c) => c.v.unmount());
this.$remove();
this.el = void 0;
this.items = void 0;
this.cache = void 0;
}
async update(curr) {
if (!curr) return;
const { keyFn, items, ctor, cache, el: parent } = this;
const currItems = [];
const currCache = /* @__PURE__ */ new Map();
const offsets = /* @__PURE__ */ new Map();
const deltas = /* @__PURE__ */ new Map();
let numPrev = items.length;
let numCurr = curr.length;
let i;
for (i = numPrev; i-- > 0; ) {
offsets.set(items[i].k, i);
}
for (i = numCurr; i-- > 0; ) {
const val = curr[i];
const key = keyFn(val, i);
let item = cache.get(key);
item ? item.v.update(val) : item = {
k: key,
v: $compile(ctor(val))
};
currCache.set(key, currItems[i] = item);
const off = offsets.get(key);
off != void 0 && deltas.set(key, Math.abs(i - off));
}
const willMove = /* @__PURE__ */ new Set();
const didMove = /* @__PURE__ */ new Set();
let next;
const insert = async (item) => {
if (cache.has(item.k)) {
this.$moveTo(parent, item.v.el, next);
next = item.v.el;
} else {
cache.set(item.k, item);
next = await item.v.mount(parent, next);
}
numCurr--;
};
while (numPrev && numCurr) {
const prevItem = items[numPrev - 1];
const prevKey = prevItem.k;
const currItem = currItems[numCurr - 1];
const currKey = currItem.k;
if (currItem === prevItem) {
next = currItem.v.el;
numPrev--;
numCurr--;
} else if (!currCache.has(prevKey)) {
await prevItem.v.unmount();
cache.delete(prevKey);
numPrev--;
} else if (!cache.has(currKey) || willMove.has(currKey)) {
await insert(currItem);
} else if (didMove.has(prevKey)) {
numPrev--;
} else if (deltas.get(currKey) > deltas.get(prevKey)) {
await insert(currItem);
didMove.add(currKey);
} else {
willMove.add(prevKey);
numPrev--;
}
}
while (numPrev--) {
const item = items[numPrev];
if (!currCache.has(item.k)) {
await item.v.unmount();
cache.delete(item.k);
}
}
while (numCurr) {
await insert(currItems[numCurr - 1]);
}
this.items = currItems;
}
}
export {
$klist,
KList
};