@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.cjs
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
patchProp: () => patchProp,
|
|
42
42
|
readState: () => readState,
|
|
43
43
|
reconcileChildren: () => reconcileChildren,
|
|
44
|
+
reconcileChildrenByPlan: () => reconcileChildrenByPlan,
|
|
44
45
|
renderToString: () => renderToString,
|
|
45
46
|
resolveTag: () => resolveTag,
|
|
46
47
|
serializeState: () => serializeState,
|
|
@@ -168,6 +169,7 @@ function patchProp(dom, element, name, oldValue, newValue) {
|
|
|
168
169
|
|
|
169
170
|
// src/events.ts
|
|
170
171
|
function wireEvents(dom, graph, node, element, instance) {
|
|
172
|
+
if (node.events.length === 0) return;
|
|
171
173
|
for (const eventDesc of node.events) {
|
|
172
174
|
const handler = graph.getHandler(eventDesc.handlerKey);
|
|
173
175
|
if (handler === void 0) continue;
|
|
@@ -297,6 +299,109 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
|
297
299
|
reorderDom(ctx, parentDom, newInstances);
|
|
298
300
|
return { instances: newInstances, removed };
|
|
299
301
|
}
|
|
302
|
+
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
303
|
+
const oldByKey = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const inst of oldInstances) {
|
|
305
|
+
oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
|
|
306
|
+
}
|
|
307
|
+
const newInstances = [];
|
|
308
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
309
|
+
const built = [];
|
|
310
|
+
for (const entry of plan) {
|
|
311
|
+
const existing = oldByKey.get(entry.key);
|
|
312
|
+
if (existing !== void 0) {
|
|
313
|
+
usedKeys.add(entry.key);
|
|
314
|
+
const oldItem = existing.graphNode.getProp("_item");
|
|
315
|
+
if (!Object.is(oldItem, entry.item)) {
|
|
316
|
+
const newSig = entry.sig();
|
|
317
|
+
const oldSig = existing.graphNode.getProp("_sig");
|
|
318
|
+
if (!Object.is(oldSig, newSig)) {
|
|
319
|
+
const freshNode = entry.build();
|
|
320
|
+
built.push(freshNode);
|
|
321
|
+
patchExistingInstance(ctx, existing, freshNode);
|
|
322
|
+
reconcileItemChildren(ctx, existing, freshNode, mountFn);
|
|
323
|
+
existing.graphNode.setProp("_sig", newSig);
|
|
324
|
+
}
|
|
325
|
+
existing.graphNode.setProp("_item", entry.item);
|
|
326
|
+
}
|
|
327
|
+
newInstances.push(existing);
|
|
328
|
+
} else {
|
|
329
|
+
const freshNode = entry.build();
|
|
330
|
+
built.push(freshNode);
|
|
331
|
+
const inst = mountFn(freshNode, parentDom);
|
|
332
|
+
newInstances.push(inst);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const removed = [];
|
|
336
|
+
for (const inst of oldInstances) {
|
|
337
|
+
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
338
|
+
if (!usedKeys.has(key)) removed.push(inst);
|
|
339
|
+
}
|
|
340
|
+
for (const inst of removed) {
|
|
341
|
+
const parent = ctx.dom.parentNode(inst.domNode);
|
|
342
|
+
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
343
|
+
inst.dispose();
|
|
344
|
+
}
|
|
345
|
+
reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
|
|
346
|
+
return { instances: newInstances, removed, built };
|
|
347
|
+
}
|
|
348
|
+
function reorderDomMinimal(ctx, parentDom, oldInstances, newInstances) {
|
|
349
|
+
const n = newInstances.length;
|
|
350
|
+
if (n === 0) return;
|
|
351
|
+
const oldIndexOf = /* @__PURE__ */ new Map();
|
|
352
|
+
for (let i = 0; i < oldInstances.length; i++) oldIndexOf.set(oldInstances[i], i);
|
|
353
|
+
const source = new Array(n);
|
|
354
|
+
let moved = false;
|
|
355
|
+
let lastSeen = -1;
|
|
356
|
+
for (let i = 0; i < n; i++) {
|
|
357
|
+
const oi = oldIndexOf.get(newInstances[i]);
|
|
358
|
+
if (oi === void 0) {
|
|
359
|
+
source[i] = -1;
|
|
360
|
+
moved = true;
|
|
361
|
+
} else {
|
|
362
|
+
source[i] = oi;
|
|
363
|
+
if (oi < lastSeen) moved = true;
|
|
364
|
+
else lastSeen = oi;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (!moved) return;
|
|
368
|
+
const keep = longestIncreasingSubsequence(source);
|
|
369
|
+
let refNode = null;
|
|
370
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
371
|
+
const domNode = newInstances[i].domNode;
|
|
372
|
+
if (source[i] === -1 || !keep.has(i)) {
|
|
373
|
+
if (ctx.dom.nextSibling(domNode) !== refNode) {
|
|
374
|
+
ctx.dom.insertBefore(parentDom, domNode, refNode);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
refNode = domNode;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function longestIncreasingSubsequence(source) {
|
|
381
|
+
const keep = /* @__PURE__ */ new Set();
|
|
382
|
+
const n = source.length;
|
|
383
|
+
const tails = [];
|
|
384
|
+
const prev = new Array(n).fill(-1);
|
|
385
|
+
for (let i = 0; i < n; i++) {
|
|
386
|
+
const v = source[i];
|
|
387
|
+
if (v < 0) continue;
|
|
388
|
+
let lo = 0;
|
|
389
|
+
let hi = tails.length;
|
|
390
|
+
while (lo < hi) {
|
|
391
|
+
const mid = lo + hi >> 1;
|
|
392
|
+
if (source[tails[mid]] < v) lo = mid + 1;
|
|
393
|
+
else hi = mid;
|
|
394
|
+
}
|
|
395
|
+
if (lo > 0) prev[i] = tails[lo - 1];
|
|
396
|
+
tails[lo] = i;
|
|
397
|
+
}
|
|
398
|
+
let idx = tails.length > 0 ? tails[tails.length - 1] : -1;
|
|
399
|
+
while (idx >= 0) {
|
|
400
|
+
keep.add(idx);
|
|
401
|
+
idx = prev[idx];
|
|
402
|
+
}
|
|
403
|
+
return keep;
|
|
404
|
+
}
|
|
300
405
|
function reconcileItemChildren(ctx, itemInstance, newItemNode, mountFn) {
|
|
301
406
|
const el = itemInstance.domNode;
|
|
302
407
|
if (!ctx.dom.isElement(el)) return;
|
|
@@ -398,10 +503,12 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
398
503
|
const textNode = dom.createTextNode(text);
|
|
399
504
|
dom.appendChild(el2, textNode);
|
|
400
505
|
applyNodeProps(ctx, graphNode, el2);
|
|
401
|
-
wireEvents(dom, graph, graphNode, el2, new NodeInstance(graphNode, el2));
|
|
402
506
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
403
507
|
ctx.instances.set(graphNode.id, instance2);
|
|
404
|
-
|
|
508
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
509
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
510
|
+
wireSignalBindings(ctx, graphNode, instance2, textUpdate(dom, el2, textNode));
|
|
511
|
+
}
|
|
405
512
|
dom.appendChild(parentDom, el2);
|
|
406
513
|
return instance2;
|
|
407
514
|
}
|
|
@@ -415,7 +522,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
415
522
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
416
523
|
ctx.instances.set(graphNode.id, instance2);
|
|
417
524
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
418
|
-
|
|
525
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
526
|
+
wireSignalBindings(ctx, graphNode, instance2, headingUpdate(dom, el2));
|
|
527
|
+
}
|
|
419
528
|
dom.appendChild(parentDom, el2);
|
|
420
529
|
return instance2;
|
|
421
530
|
}
|
|
@@ -431,7 +540,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
431
540
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
432
541
|
ctx.instances.set(graphNode.id, instance2);
|
|
433
542
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
434
|
-
|
|
543
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
544
|
+
wireSignalBindings(ctx, graphNode, instance2, inputUpdate(dom, el2));
|
|
545
|
+
}
|
|
435
546
|
dom.appendChild(parentDom, el2);
|
|
436
547
|
return instance2;
|
|
437
548
|
}
|
|
@@ -479,7 +590,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
479
590
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
480
591
|
ctx.instances.set(graphNode.id, instance2);
|
|
481
592
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
482
|
-
|
|
593
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
594
|
+
wireSignalBindings(ctx, graphNode, instance2, buttonUpdate(dom, el2));
|
|
595
|
+
}
|
|
483
596
|
dom.appendChild(parentDom, el2);
|
|
484
597
|
return instance2;
|
|
485
598
|
}
|
|
@@ -506,11 +619,11 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
506
619
|
dom.setAttribute(el, "data-streetui-key", String(itemKey));
|
|
507
620
|
}
|
|
508
621
|
}
|
|
509
|
-
if (graphNode.type === "form") {
|
|
510
|
-
wireEvents(dom, graph, graphNode, el, new NodeInstance(graphNode, el));
|
|
511
|
-
}
|
|
512
622
|
const instance = new NodeInstance(graphNode, el);
|
|
513
623
|
ctx.instances.set(graphNode.id, instance);
|
|
624
|
+
if (graphNode.type === "form") {
|
|
625
|
+
wireEvents(dom, graph, graphNode, el, instance);
|
|
626
|
+
}
|
|
514
627
|
for (const child of graphNode.children) {
|
|
515
628
|
const childInstance = mountNode(ctx, child, el);
|
|
516
629
|
instance.addChild(childInstance);
|
|
@@ -561,12 +674,15 @@ function buttonUpdate(dom, el) {
|
|
|
561
674
|
};
|
|
562
675
|
}
|
|
563
676
|
function applyNodeProps(ctx, graphNode, el) {
|
|
564
|
-
|
|
677
|
+
const props = graphNode.props;
|
|
678
|
+
for (const key in props) {
|
|
679
|
+
if (!Object.hasOwn(props, key)) continue;
|
|
565
680
|
if (SKIP_PROP_KEYS.has(key)) continue;
|
|
566
|
-
applyProp(ctx.dom, el, key,
|
|
681
|
+
applyProp(ctx.dom, el, key, props[key]);
|
|
567
682
|
}
|
|
568
683
|
}
|
|
569
684
|
function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
685
|
+
if (graphNode.stateRefs.length === 0) return;
|
|
570
686
|
for (const stateRef of graphNode.stateRefs) {
|
|
571
687
|
const signalKey = `__signal__${stateRef.signalId}`;
|
|
572
688
|
const maybeSig = ctx.graph.getHandler(signalKey);
|
|
@@ -578,18 +694,45 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
|
578
694
|
}
|
|
579
695
|
}
|
|
580
696
|
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
697
|
+
const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
|
|
581
698
|
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
582
|
-
if (build === void 0) return;
|
|
699
|
+
if (plan === void 0 && build === void 0) return;
|
|
583
700
|
for (const stateRef of graphNode.stateRefs) {
|
|
584
701
|
if (stateRef.propKey !== "items") continue;
|
|
585
702
|
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
586
703
|
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
587
704
|
const unsub = sig.subscribe((value) => {
|
|
588
|
-
|
|
705
|
+
if (plan !== void 0) {
|
|
706
|
+
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
|
|
707
|
+
} else {
|
|
708
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
709
|
+
}
|
|
589
710
|
});
|
|
590
711
|
instance.trackCleanup(unsub);
|
|
591
712
|
}
|
|
592
713
|
}
|
|
714
|
+
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
|
|
715
|
+
const oldInstances = [...listInstance.children];
|
|
716
|
+
const result = reconcileChildrenByPlan(
|
|
717
|
+
ctx,
|
|
718
|
+
listEl,
|
|
719
|
+
oldInstances,
|
|
720
|
+
plan,
|
|
721
|
+
(node, parent) => mountNode(ctx, node, parent)
|
|
722
|
+
);
|
|
723
|
+
listInstance.children.length = 0;
|
|
724
|
+
for (const inst of result.instances) listInstance.children.push(inst);
|
|
725
|
+
for (const removed of result.removed) {
|
|
726
|
+
forgetInstance(ctx, removed);
|
|
727
|
+
ctx.graph.detachNode(removed.graphNode);
|
|
728
|
+
}
|
|
729
|
+
const adopted = new Set(result.instances.map((i) => i.graphNode));
|
|
730
|
+
for (const node of result.built ?? []) {
|
|
731
|
+
if (!adopted.has(node)) ctx.graph.detachNode(node);
|
|
732
|
+
}
|
|
733
|
+
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
734
|
+
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
735
|
+
}
|
|
593
736
|
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
594
737
|
const oldInstances = [...listInstance.children];
|
|
595
738
|
const result = reconcileChildren(
|
|
@@ -672,14 +815,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
672
815
|
const instance = new NodeInstance(graphNode, domNode);
|
|
673
816
|
ctx.instances.set(graphNode.id, instance);
|
|
674
817
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
675
|
-
|
|
818
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
819
|
+
wireSignalBindings(ctx, graphNode, instance, textUpdate(dom, domNode, textNode));
|
|
820
|
+
}
|
|
676
821
|
return instance;
|
|
677
822
|
}
|
|
678
823
|
case "heading": {
|
|
679
824
|
const instance = new NodeInstance(graphNode, domNode);
|
|
680
825
|
ctx.instances.set(graphNode.id, instance);
|
|
681
826
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
682
|
-
|
|
827
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
828
|
+
wireSignalBindings(ctx, graphNode, instance, headingUpdate(dom, domNode));
|
|
829
|
+
}
|
|
683
830
|
return instance;
|
|
684
831
|
}
|
|
685
832
|
case "input": {
|
|
@@ -688,14 +835,18 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
688
835
|
const value = graphNode.getProp("value");
|
|
689
836
|
if (value !== void 0) dom.setProperty(domNode, "value", String(value));
|
|
690
837
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
691
|
-
|
|
838
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
839
|
+
wireSignalBindings(ctx, graphNode, instance, inputUpdate(dom, domNode));
|
|
840
|
+
}
|
|
692
841
|
return instance;
|
|
693
842
|
}
|
|
694
843
|
case "button": {
|
|
695
844
|
const instance = new NodeInstance(graphNode, domNode);
|
|
696
845
|
ctx.instances.set(graphNode.id, instance);
|
|
697
846
|
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
698
|
-
|
|
847
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
848
|
+
wireSignalBindings(ctx, graphNode, instance, buttonUpdate(dom, domNode));
|
|
849
|
+
}
|
|
699
850
|
return instance;
|
|
700
851
|
}
|
|
701
852
|
case "image":
|
|
@@ -731,10 +882,11 @@ function hydrateChildren(ctx, parentGraphNode, parentInstance, parentDom, parent
|
|
|
731
882
|
const expected = parentGraphNode.children;
|
|
732
883
|
const actual = elementChildren(ctx, parentDom);
|
|
733
884
|
let cursor = 0;
|
|
885
|
+
const diag = ctx.hydrationDiagnostics !== void 0;
|
|
734
886
|
for (let i = 0; i < expected.length; i++) {
|
|
735
887
|
const childNode = expected[i];
|
|
736
888
|
const want = expectedTag(ctx, childNode);
|
|
737
|
-
const childPath = `${parentPath} / ${childNode.type}[${i}]
|
|
889
|
+
const childPath = diag ? `${parentPath} / ${childNode.type}[${i}]` : parentPath;
|
|
738
890
|
const actualEl = actual[cursor];
|
|
739
891
|
if (actualEl !== void 0 && ctx.dom.isElement(actualEl) && ctx.dom.tagName(actualEl) === want) {
|
|
740
892
|
const inst = hydrateNode(ctx, childNode, actualEl, childPath);
|
|
@@ -961,6 +1113,7 @@ function renderToString(compiled, options = {}) {
|
|
|
961
1113
|
patchProp,
|
|
962
1114
|
readState,
|
|
963
1115
|
reconcileChildren,
|
|
1116
|
+
reconcileChildrenByPlan,
|
|
964
1117
|
renderToString,
|
|
965
1118
|
resolveTag,
|
|
966
1119
|
serializeState,
|