@streetui/renderer 1.0.0 → 1.3.0
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/dist/index.cjs +170 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -3
- package/dist/index.d.ts +42 -3
- package/dist/index.js +169 -17
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -115,6 +115,7 @@ function patchProp(dom, element, name, oldValue, newValue) {
|
|
|
115
115
|
|
|
116
116
|
// src/events.ts
|
|
117
117
|
function wireEvents(dom, graph, node, element, instance) {
|
|
118
|
+
if (node.events.length === 0) return;
|
|
118
119
|
for (const eventDesc of node.events) {
|
|
119
120
|
const handler = graph.getHandler(eventDesc.handlerKey);
|
|
120
121
|
if (handler === void 0) continue;
|
|
@@ -244,6 +245,109 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
|
244
245
|
reorderDom(ctx, parentDom, newInstances);
|
|
245
246
|
return { instances: newInstances, removed };
|
|
246
247
|
}
|
|
248
|
+
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
249
|
+
const oldByKey = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const inst of oldInstances) {
|
|
251
|
+
oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
|
|
252
|
+
}
|
|
253
|
+
const newInstances = [];
|
|
254
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
255
|
+
const built = [];
|
|
256
|
+
for (const entry of plan) {
|
|
257
|
+
const existing = oldByKey.get(entry.key);
|
|
258
|
+
if (existing !== void 0) {
|
|
259
|
+
usedKeys.add(entry.key);
|
|
260
|
+
const oldItem = existing.graphNode.getProp("_item");
|
|
261
|
+
if (!Object.is(oldItem, entry.item)) {
|
|
262
|
+
const newSig = entry.sig();
|
|
263
|
+
const oldSig = existing.graphNode.getProp("_sig");
|
|
264
|
+
if (!Object.is(oldSig, newSig)) {
|
|
265
|
+
const freshNode = entry.build();
|
|
266
|
+
built.push(freshNode);
|
|
267
|
+
patchExistingInstance(ctx, existing, freshNode);
|
|
268
|
+
reconcileItemChildren(ctx, existing, freshNode, mountFn);
|
|
269
|
+
existing.graphNode.setProp("_sig", newSig);
|
|
270
|
+
}
|
|
271
|
+
existing.graphNode.setProp("_item", entry.item);
|
|
272
|
+
}
|
|
273
|
+
newInstances.push(existing);
|
|
274
|
+
} else {
|
|
275
|
+
const freshNode = entry.build();
|
|
276
|
+
built.push(freshNode);
|
|
277
|
+
const inst = mountFn(freshNode, parentDom);
|
|
278
|
+
newInstances.push(inst);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const removed = [];
|
|
282
|
+
for (const inst of oldInstances) {
|
|
283
|
+
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
284
|
+
if (!usedKeys.has(key)) removed.push(inst);
|
|
285
|
+
}
|
|
286
|
+
for (const inst of removed) {
|
|
287
|
+
const parent = ctx.dom.parentNode(inst.domNode);
|
|
288
|
+
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
289
|
+
inst.dispose();
|
|
290
|
+
}
|
|
291
|
+
reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
|
|
292
|
+
return { instances: newInstances, removed, built };
|
|
293
|
+
}
|
|
294
|
+
function reorderDomMinimal(ctx, parentDom, oldInstances, newInstances) {
|
|
295
|
+
const n = newInstances.length;
|
|
296
|
+
if (n === 0) return;
|
|
297
|
+
const oldIndexOf = /* @__PURE__ */ new Map();
|
|
298
|
+
for (let i = 0; i < oldInstances.length; i++) oldIndexOf.set(oldInstances[i], i);
|
|
299
|
+
const source = new Array(n);
|
|
300
|
+
let moved = false;
|
|
301
|
+
let lastSeen = -1;
|
|
302
|
+
for (let i = 0; i < n; i++) {
|
|
303
|
+
const oi = oldIndexOf.get(newInstances[i]);
|
|
304
|
+
if (oi === void 0) {
|
|
305
|
+
source[i] = -1;
|
|
306
|
+
moved = true;
|
|
307
|
+
} else {
|
|
308
|
+
source[i] = oi;
|
|
309
|
+
if (oi < lastSeen) moved = true;
|
|
310
|
+
else lastSeen = oi;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!moved) return;
|
|
314
|
+
const keep = longestIncreasingSubsequence(source);
|
|
315
|
+
let refNode = null;
|
|
316
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
317
|
+
const domNode = newInstances[i].domNode;
|
|
318
|
+
if (source[i] === -1 || !keep.has(i)) {
|
|
319
|
+
if (ctx.dom.nextSibling(domNode) !== refNode) {
|
|
320
|
+
ctx.dom.insertBefore(parentDom, domNode, refNode);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
refNode = domNode;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function longestIncreasingSubsequence(source) {
|
|
327
|
+
const keep = /* @__PURE__ */ new Set();
|
|
328
|
+
const n = source.length;
|
|
329
|
+
const tails = [];
|
|
330
|
+
const prev = new Array(n).fill(-1);
|
|
331
|
+
for (let i = 0; i < n; i++) {
|
|
332
|
+
const v = source[i];
|
|
333
|
+
if (v < 0) continue;
|
|
334
|
+
let lo = 0;
|
|
335
|
+
let hi = tails.length;
|
|
336
|
+
while (lo < hi) {
|
|
337
|
+
const mid = lo + hi >> 1;
|
|
338
|
+
if (source[tails[mid]] < v) lo = mid + 1;
|
|
339
|
+
else hi = mid;
|
|
340
|
+
}
|
|
341
|
+
if (lo > 0) prev[i] = tails[lo - 1];
|
|
342
|
+
tails[lo] = i;
|
|
343
|
+
}
|
|
344
|
+
let idx = tails.length > 0 ? tails[tails.length - 1] : -1;
|
|
345
|
+
while (idx >= 0) {
|
|
346
|
+
keep.add(idx);
|
|
347
|
+
idx = prev[idx];
|
|
348
|
+
}
|
|
349
|
+
return keep;
|
|
350
|
+
}
|
|
247
351
|
function reconcileItemChildren(ctx, itemInstance, newItemNode, mountFn) {
|
|
248
352
|
const el = itemInstance.domNode;
|
|
249
353
|
if (!ctx.dom.isElement(el)) return;
|
|
@@ -345,10 +449,12 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
345
449
|
const textNode = dom.createTextNode(text);
|
|
346
450
|
dom.appendChild(el2, textNode);
|
|
347
451
|
applyNodeProps(ctx, graphNode, el2);
|
|
348
|
-
wireEvents(dom, graph, graphNode, el2, new NodeInstance(graphNode, el2));
|
|
349
452
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
350
453
|
ctx.instances.set(graphNode.id, instance2);
|
|
351
|
-
|
|
454
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
455
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
456
|
+
wireSignalBindings(ctx, graphNode, instance2, textUpdate(dom, el2, textNode));
|
|
457
|
+
}
|
|
352
458
|
dom.appendChild(parentDom, el2);
|
|
353
459
|
return instance2;
|
|
354
460
|
}
|
|
@@ -362,7 +468,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
362
468
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
363
469
|
ctx.instances.set(graphNode.id, instance2);
|
|
364
470
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
365
|
-
|
|
471
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
472
|
+
wireSignalBindings(ctx, graphNode, instance2, headingUpdate(dom, el2));
|
|
473
|
+
}
|
|
366
474
|
dom.appendChild(parentDom, el2);
|
|
367
475
|
return instance2;
|
|
368
476
|
}
|
|
@@ -378,7 +486,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
378
486
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
379
487
|
ctx.instances.set(graphNode.id, instance2);
|
|
380
488
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
381
|
-
|
|
489
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
490
|
+
wireSignalBindings(ctx, graphNode, instance2, inputUpdate(dom, el2));
|
|
491
|
+
}
|
|
382
492
|
dom.appendChild(parentDom, el2);
|
|
383
493
|
return instance2;
|
|
384
494
|
}
|
|
@@ -426,7 +536,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
426
536
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
427
537
|
ctx.instances.set(graphNode.id, instance2);
|
|
428
538
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
429
|
-
|
|
539
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
540
|
+
wireSignalBindings(ctx, graphNode, instance2, buttonUpdate(dom, el2));
|
|
541
|
+
}
|
|
430
542
|
dom.appendChild(parentDom, el2);
|
|
431
543
|
return instance2;
|
|
432
544
|
}
|
|
@@ -453,11 +565,11 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
453
565
|
dom.setAttribute(el, "data-streetui-key", String(itemKey));
|
|
454
566
|
}
|
|
455
567
|
}
|
|
456
|
-
if (graphNode.type === "form") {
|
|
457
|
-
wireEvents(dom, graph, graphNode, el, new NodeInstance(graphNode, el));
|
|
458
|
-
}
|
|
459
568
|
const instance = new NodeInstance(graphNode, el);
|
|
460
569
|
ctx.instances.set(graphNode.id, instance);
|
|
570
|
+
if (graphNode.type === "form") {
|
|
571
|
+
wireEvents(dom, graph, graphNode, el, instance);
|
|
572
|
+
}
|
|
461
573
|
for (const child of graphNode.children) {
|
|
462
574
|
const childInstance = mountNode(ctx, child, el);
|
|
463
575
|
instance.addChild(childInstance);
|
|
@@ -508,12 +620,15 @@ function buttonUpdate(dom, el) {
|
|
|
508
620
|
};
|
|
509
621
|
}
|
|
510
622
|
function applyNodeProps(ctx, graphNode, el) {
|
|
511
|
-
|
|
623
|
+
const props = graphNode.props;
|
|
624
|
+
for (const key in props) {
|
|
625
|
+
if (!Object.hasOwn(props, key)) continue;
|
|
512
626
|
if (SKIP_PROP_KEYS.has(key)) continue;
|
|
513
|
-
applyProp(ctx.dom, el, key,
|
|
627
|
+
applyProp(ctx.dom, el, key, props[key]);
|
|
514
628
|
}
|
|
515
629
|
}
|
|
516
630
|
function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
631
|
+
if (graphNode.stateRefs.length === 0) return;
|
|
517
632
|
for (const stateRef of graphNode.stateRefs) {
|
|
518
633
|
const signalKey = `__signal__${stateRef.signalId}`;
|
|
519
634
|
const maybeSig = ctx.graph.getHandler(signalKey);
|
|
@@ -525,18 +640,45 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
|
525
640
|
}
|
|
526
641
|
}
|
|
527
642
|
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
643
|
+
const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
|
|
528
644
|
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
529
|
-
if (build === void 0) return;
|
|
645
|
+
if (plan === void 0 && build === void 0) return;
|
|
530
646
|
for (const stateRef of graphNode.stateRefs) {
|
|
531
647
|
if (stateRef.propKey !== "items") continue;
|
|
532
648
|
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
533
649
|
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
534
650
|
const unsub = sig.subscribe((value) => {
|
|
535
|
-
|
|
651
|
+
if (plan !== void 0) {
|
|
652
|
+
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
|
|
653
|
+
} else {
|
|
654
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
655
|
+
}
|
|
536
656
|
});
|
|
537
657
|
instance.trackCleanup(unsub);
|
|
538
658
|
}
|
|
539
659
|
}
|
|
660
|
+
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
|
|
661
|
+
const oldInstances = [...listInstance.children];
|
|
662
|
+
const result = reconcileChildrenByPlan(
|
|
663
|
+
ctx,
|
|
664
|
+
listEl,
|
|
665
|
+
oldInstances,
|
|
666
|
+
plan,
|
|
667
|
+
(node, parent) => mountNode(ctx, node, parent)
|
|
668
|
+
);
|
|
669
|
+
listInstance.children.length = 0;
|
|
670
|
+
for (const inst of result.instances) listInstance.children.push(inst);
|
|
671
|
+
for (const removed of result.removed) {
|
|
672
|
+
forgetInstance(ctx, removed);
|
|
673
|
+
ctx.graph.detachNode(removed.graphNode);
|
|
674
|
+
}
|
|
675
|
+
const adopted = new Set(result.instances.map((i) => i.graphNode));
|
|
676
|
+
for (const node of result.built ?? []) {
|
|
677
|
+
if (!adopted.has(node)) ctx.graph.detachNode(node);
|
|
678
|
+
}
|
|
679
|
+
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
680
|
+
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
681
|
+
}
|
|
540
682
|
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
541
683
|
const oldInstances = [...listInstance.children];
|
|
542
684
|
const result = reconcileChildren(
|
|
@@ -619,14 +761,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
619
761
|
const instance = new NodeInstance(graphNode, domNode);
|
|
620
762
|
ctx.instances.set(graphNode.id, instance);
|
|
621
763
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
622
|
-
|
|
764
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
765
|
+
wireSignalBindings(ctx, graphNode, instance, textUpdate(dom, domNode, textNode));
|
|
766
|
+
}
|
|
623
767
|
return instance;
|
|
624
768
|
}
|
|
625
769
|
case "heading": {
|
|
626
770
|
const instance = new NodeInstance(graphNode, domNode);
|
|
627
771
|
ctx.instances.set(graphNode.id, instance);
|
|
628
772
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
629
|
-
|
|
773
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
774
|
+
wireSignalBindings(ctx, graphNode, instance, headingUpdate(dom, domNode));
|
|
775
|
+
}
|
|
630
776
|
return instance;
|
|
631
777
|
}
|
|
632
778
|
case "input": {
|
|
@@ -635,14 +781,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
635
781
|
const value = graphNode.getProp("value");
|
|
636
782
|
if (value !== void 0) dom.setProperty(domNode, "value", String(value));
|
|
637
783
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
638
|
-
|
|
784
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
785
|
+
wireSignalBindings(ctx, graphNode, instance, inputUpdate(dom, domNode));
|
|
786
|
+
}
|
|
639
787
|
return instance;
|
|
640
788
|
}
|
|
641
789
|
case "button": {
|
|
642
790
|
const instance = new NodeInstance(graphNode, domNode);
|
|
643
791
|
ctx.instances.set(graphNode.id, instance);
|
|
644
792
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
645
|
-
|
|
793
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
794
|
+
wireSignalBindings(ctx, graphNode, instance, buttonUpdate(dom, domNode));
|
|
795
|
+
}
|
|
646
796
|
return instance;
|
|
647
797
|
}
|
|
648
798
|
case "image":
|
|
@@ -678,10 +828,11 @@ function hydrateChildren(ctx, parentGraphNode, parentInstance, parentDom, parent
|
|
|
678
828
|
const expected = parentGraphNode.children;
|
|
679
829
|
const actual = elementChildren(ctx, parentDom);
|
|
680
830
|
let cursor = 0;
|
|
831
|
+
const diag = ctx.hydrationDiagnostics !== void 0;
|
|
681
832
|
for (let i = 0; i < expected.length; i++) {
|
|
682
833
|
const childNode = expected[i];
|
|
683
834
|
const want = expectedTag(ctx, childNode);
|
|
684
|
-
const childPath = `${parentPath} / ${childNode.type}[${i}]
|
|
835
|
+
const childPath = diag ? `${parentPath} / ${childNode.type}[${i}]` : parentPath;
|
|
685
836
|
const actualEl = actual[cursor];
|
|
686
837
|
if (actualEl !== void 0 && ctx.dom.isElement(actualEl) && ctx.dom.tagName(actualEl) === want) {
|
|
687
838
|
const inst = hydrateNode(ctx, childNode, actualEl, childPath);
|
|
@@ -907,6 +1058,7 @@ export {
|
|
|
907
1058
|
patchProp,
|
|
908
1059
|
readState,
|
|
909
1060
|
reconcileChildren,
|
|
1061
|
+
reconcileChildrenByPlan,
|
|
910
1062
|
renderToString,
|
|
911
1063
|
resolveTag,
|
|
912
1064
|
serializeState,
|