@geektech/tsone 0.0.2 → 0.2.1
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/README-zh.md +424 -0
- package/README.md +222 -53
- package/dist/core/animation/TransitionGroup.d.ts +7 -0
- package/dist/core/animation/index.d.ts +2 -0
- package/dist/core/animation/list-animation-controller.d.ts +16 -0
- package/dist/core/animation/transition-group-strategy.d.ts +15 -0
- package/dist/core/animation/types.d.ts +25 -0
- package/dist/core/app.d.ts +25 -8
- package/dist/core/index.d.ts +1 -0
- package/dist/core/renderer/element-strategy.d.ts +25 -0
- package/dist/core/renderer.d.ts +2 -21
- package/dist/core/vnode.d.ts +36 -0
- package/dist/dom/index.d.ts +534 -0
- package/dist/dom/index.js +2181 -0
- package/dist/dom/index.js.map +10 -0
- package/dist/{index-ycmc7ga1.js → index-1x4ectvr.js} +1031 -396
- package/dist/index-1x4ectvr.js.map +27 -0
- package/dist/{index-8wjswsye.js → index-ffzday7h.js} +31 -3
- package/dist/{index-8wjswsye.js.map → index-ffzday7h.js.map} +5 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +151 -337
- package/dist/index.js.map +5 -6
- package/dist/router/index.js +7 -7
- package/dist/router/index.js.map +1 -1
- package/dist/style/index.js +5 -7
- package/dist/style/index.js.map +1 -1
- package/package.json +11 -2
- package/dist/index-vpx80nq5.js +0 -32
- package/dist/index-vpx80nq5.js.map +0 -10
- package/dist/index-ycmc7ga1.js.map +0 -21
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
StyleManager
|
|
3
|
-
|
|
2
|
+
StyleManager,
|
|
3
|
+
renderStyleSheet
|
|
4
|
+
} from "./index-ffzday7h.js";
|
|
4
5
|
|
|
5
6
|
// lib/core/reactive/types.ts
|
|
6
7
|
var IS_REACTIVE = Symbol("is_reactive");
|
|
@@ -345,38 +346,100 @@ function isReadonly(value) {
|
|
|
345
346
|
return isObject(value) && hasReactiveFlag(value, IS_READONLY);
|
|
346
347
|
}
|
|
347
348
|
|
|
348
|
-
// lib/core/
|
|
349
|
-
function
|
|
350
|
-
return
|
|
349
|
+
// lib/core/vnode.ts
|
|
350
|
+
function isComponentNode(vnode) {
|
|
351
|
+
return typeof vnode === "object" && vnode !== null && "component" in vnode;
|
|
351
352
|
}
|
|
352
|
-
function
|
|
353
|
-
return
|
|
353
|
+
function isHTMLNode(vnode) {
|
|
354
|
+
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag !== "slot";
|
|
354
355
|
}
|
|
355
|
-
function
|
|
356
|
-
|
|
357
|
-
return { eventName, modifiers: new Set(modifiers) };
|
|
356
|
+
function isSlotProvider(vnode) {
|
|
357
|
+
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag === "slot";
|
|
358
358
|
}
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (modifiers.has("self") && event.currentTarget !== event.target) {
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
if (modifiers.has("once")) {
|
|
371
|
-
event.currentTarget.removeEventListener(event.type, eventHandler);
|
|
372
|
-
}
|
|
373
|
-
handler(event);
|
|
359
|
+
function h(tag, props, children, listeners, key, directions) {
|
|
360
|
+
return {
|
|
361
|
+
tag,
|
|
362
|
+
props,
|
|
363
|
+
children,
|
|
364
|
+
listeners,
|
|
365
|
+
key,
|
|
366
|
+
directions
|
|
374
367
|
};
|
|
375
|
-
return eventHandler;
|
|
376
368
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
|
|
369
|
+
function Tag(tag, options = {}) {
|
|
370
|
+
return {
|
|
371
|
+
tag,
|
|
372
|
+
...options
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function createElementFactory(tag) {
|
|
376
|
+
return (options = {}) => Tag(tag, options);
|
|
377
|
+
}
|
|
378
|
+
var Div = createElementFactory("div");
|
|
379
|
+
var Span = createElementFactory("span");
|
|
380
|
+
var P = createElementFactory("p");
|
|
381
|
+
var Button = createElementFactory("button");
|
|
382
|
+
var Input = createElementFactory("input");
|
|
383
|
+
var Section = createElementFactory("section");
|
|
384
|
+
var Main = createElementFactory("main");
|
|
385
|
+
var Header = createElementFactory("header");
|
|
386
|
+
var Footer = createElementFactory("footer");
|
|
387
|
+
var Nav = createElementFactory("nav");
|
|
388
|
+
var Article = createElementFactory("article");
|
|
389
|
+
var Aside = createElementFactory("aside");
|
|
390
|
+
var H1 = createElementFactory("h1");
|
|
391
|
+
var H2 = createElementFactory("h2");
|
|
392
|
+
var H3 = createElementFactory("h3");
|
|
393
|
+
var H4 = createElementFactory("h4");
|
|
394
|
+
var H5 = createElementFactory("h5");
|
|
395
|
+
var H6 = createElementFactory("h6");
|
|
396
|
+
var Strong = createElementFactory("strong");
|
|
397
|
+
var Em = createElementFactory("em");
|
|
398
|
+
var Small = createElementFactory("small");
|
|
399
|
+
var Pre = createElementFactory("pre");
|
|
400
|
+
var Code = createElementFactory("code");
|
|
401
|
+
var Blockquote = createElementFactory("blockquote");
|
|
402
|
+
var Ul = createElementFactory("ul");
|
|
403
|
+
var Ol = createElementFactory("ol");
|
|
404
|
+
var Li = createElementFactory("li");
|
|
405
|
+
var A = createElementFactory("a");
|
|
406
|
+
var Img = createElementFactory("img");
|
|
407
|
+
var Form = createElementFactory("form");
|
|
408
|
+
var Label = createElementFactory("label");
|
|
409
|
+
var Textarea = createElementFactory("textarea");
|
|
410
|
+
var Select = createElementFactory("select");
|
|
411
|
+
var Option = createElementFactory("option");
|
|
412
|
+
var Table = createElementFactory("table");
|
|
413
|
+
var Thead = createElementFactory("thead");
|
|
414
|
+
var Tbody = createElementFactory("tbody");
|
|
415
|
+
var Tr = createElementFactory("tr");
|
|
416
|
+
var Th = createElementFactory("th");
|
|
417
|
+
var Td = createElementFactory("td");
|
|
418
|
+
function createComponent(componentClass, props, children, key, directions) {
|
|
419
|
+
return {
|
|
420
|
+
component: componentClass,
|
|
421
|
+
props,
|
|
422
|
+
children,
|
|
423
|
+
key,
|
|
424
|
+
directions
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function slot(name, key, directions) {
|
|
428
|
+
return {
|
|
429
|
+
tag: "slot",
|
|
430
|
+
props: { name },
|
|
431
|
+
key,
|
|
432
|
+
directions
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function each(items, render, key) {
|
|
436
|
+
return items.map((item, index) => {
|
|
437
|
+
const vnode = render(item, index);
|
|
438
|
+
if (typeof vnode === "string") {
|
|
439
|
+
throw new Error("each render callback must return a VNode");
|
|
440
|
+
}
|
|
441
|
+
return { ...vnode, key: key(item, index) };
|
|
442
|
+
});
|
|
380
443
|
}
|
|
381
444
|
|
|
382
445
|
// lib/core/model.ts
|
|
@@ -560,328 +623,78 @@ class ModelBindingController {
|
|
|
560
623
|
}
|
|
561
624
|
}
|
|
562
625
|
|
|
563
|
-
// lib/core/
|
|
564
|
-
function
|
|
565
|
-
return
|
|
566
|
-
}
|
|
567
|
-
function isHTMLNode(vnode) {
|
|
568
|
-
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag !== "slot";
|
|
569
|
-
}
|
|
570
|
-
function isSlotProvider(vnode) {
|
|
571
|
-
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag === "slot";
|
|
572
|
-
}
|
|
573
|
-
function h(tag, props, children, listeners, key, directions) {
|
|
574
|
-
return {
|
|
575
|
-
tag,
|
|
576
|
-
props,
|
|
577
|
-
children,
|
|
578
|
-
listeners,
|
|
579
|
-
key,
|
|
580
|
-
directions
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function createElementFactory(tag) {
|
|
584
|
-
return (options = {}) => ({
|
|
585
|
-
tag,
|
|
586
|
-
...options
|
|
587
|
-
});
|
|
626
|
+
// lib/core/renderer/props.ts
|
|
627
|
+
function isEventProp(key) {
|
|
628
|
+
return /^on[A-Z]/.test(key) || /^on[a-z]/.test(key);
|
|
588
629
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
var P = createElementFactory("p");
|
|
592
|
-
var Button = createElementFactory("button");
|
|
593
|
-
var Input = createElementFactory("input");
|
|
594
|
-
function createComponent(componentClass, props, children, key, directions) {
|
|
595
|
-
return {
|
|
596
|
-
component: componentClass,
|
|
597
|
-
props,
|
|
598
|
-
children,
|
|
599
|
-
key,
|
|
600
|
-
directions
|
|
601
|
-
};
|
|
630
|
+
function eventNameFromProp(key) {
|
|
631
|
+
return key.slice(2).toLowerCase();
|
|
602
632
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
props: { name },
|
|
607
|
-
key,
|
|
608
|
-
directions
|
|
609
|
-
};
|
|
633
|
+
function parseEventName(event) {
|
|
634
|
+
const [eventName, ...modifiers] = event.split(".");
|
|
635
|
+
return { eventName, modifiers: new Set(modifiers) };
|
|
610
636
|
}
|
|
611
|
-
function
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
throw new Error("each render callback must return a VNode");
|
|
637
|
+
function wrapEventHandler(handler, modifiers) {
|
|
638
|
+
const eventHandler = (event) => {
|
|
639
|
+
if (modifiers.has("stop")) {
|
|
640
|
+
event.stopPropagation();
|
|
616
641
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// lib/core/renderer.ts
|
|
622
|
-
class RendererContext {
|
|
623
|
-
strategies;
|
|
624
|
-
constructor() {
|
|
625
|
-
this.strategies = [
|
|
626
|
-
new TextRenderStrategy,
|
|
627
|
-
new ComponentRenderStrategy,
|
|
628
|
-
new SlotRenderStrategy,
|
|
629
|
-
new ElementRenderStrategy
|
|
630
|
-
];
|
|
631
|
-
}
|
|
632
|
-
mount(vnode, context) {
|
|
633
|
-
return this.findStrategy(vnode).mount(vnode, context);
|
|
634
|
-
}
|
|
635
|
-
patch(oldVNode, newVNode, currentNode, context) {
|
|
636
|
-
const oldStrategy = this.findStrategy(oldVNode);
|
|
637
|
-
const newStrategy = this.findStrategy(newVNode);
|
|
638
|
-
if (oldStrategy !== newStrategy) {
|
|
639
|
-
const nextNode = newStrategy.mount(newVNode, context);
|
|
640
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
641
|
-
oldStrategy.unmount(oldVNode, currentNode, context);
|
|
642
|
-
return nextNode;
|
|
642
|
+
if (modifiers.has("prevent")) {
|
|
643
|
+
event.preventDefault();
|
|
643
644
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
unmount(vnode, currentNode, context) {
|
|
647
|
-
this.findStrategy(vnode).unmount(vnode, currentNode, context);
|
|
648
|
-
}
|
|
649
|
-
findStrategy(vnode) {
|
|
650
|
-
const strategy = this.strategies.find((item) => item.matches(vnode));
|
|
651
|
-
if (!strategy) {
|
|
652
|
-
throw new Error("No render strategy found for vnode");
|
|
645
|
+
if (modifiers.has("self") && event.currentTarget !== event.target) {
|
|
646
|
+
return;
|
|
653
647
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
class TextRenderStrategy {
|
|
659
|
-
matches(vnode) {
|
|
660
|
-
return typeof vnode === "string";
|
|
661
|
-
}
|
|
662
|
-
mount(vnode, context) {
|
|
663
|
-
return context.templateEngine.parseTemplate(vnode);
|
|
664
|
-
}
|
|
665
|
-
patch(oldVNode, newVNode, currentNode, context) {
|
|
666
|
-
if (oldVNode === newVNode) {
|
|
667
|
-
return currentNode;
|
|
648
|
+
if (modifiers.has("once")) {
|
|
649
|
+
event.currentTarget.removeEventListener(event.type, eventHandler);
|
|
668
650
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
651
|
+
handler(event);
|
|
652
|
+
};
|
|
653
|
+
return eventHandler;
|
|
654
|
+
}
|
|
655
|
+
function setStyleValue(style, property, value) {
|
|
656
|
+
const cssProperty = property.includes("-") ? property : property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
657
|
+
style.setProperty(cssProperty, String(value));
|
|
674
658
|
}
|
|
675
659
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
660
|
+
// lib/core/renderer/element-strategy.ts
|
|
661
|
+
class ElementRenderStrategy {
|
|
662
|
+
listeners = new WeakMap;
|
|
663
|
+
effects = new WeakMap;
|
|
664
|
+
modelBindings = new ModelBindingController;
|
|
680
665
|
matches(vnode) {
|
|
681
|
-
return typeof vnode === "object" && vnode !== null &&
|
|
666
|
+
return typeof vnode === "object" && vnode !== null && isHTMLNode(vnode);
|
|
682
667
|
}
|
|
683
668
|
mount(vnode, context) {
|
|
684
669
|
if (vnode.directions?.if === false) {
|
|
685
670
|
return document.createComment("if");
|
|
686
671
|
}
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
context.registerChild(instance);
|
|
694
|
-
const node = instance.mountToNode();
|
|
695
|
-
this.trackInstanceNode(instance, node);
|
|
696
|
-
instance.setElementChangeListener?.((previousNode, nextNode) => {
|
|
697
|
-
this.trackInstanceNode(instance, previousNode);
|
|
698
|
-
this.trackInstanceNode(instance, nextNode);
|
|
699
|
-
});
|
|
700
|
-
return node;
|
|
672
|
+
const element = document.createElement(vnode.tag);
|
|
673
|
+
this.applyProps(element, {}, vnode.props ?? {}, context);
|
|
674
|
+
this.updateListeners(element, {}, this.collectListeners(vnode));
|
|
675
|
+
this.mountChildren(element, vnode, context);
|
|
676
|
+
this.applyDirections(element, undefined, vnode.directions, context);
|
|
677
|
+
return element;
|
|
701
678
|
}
|
|
702
679
|
patch(oldVNode, newVNode, currentNode, context) {
|
|
703
|
-
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
704
|
-
const
|
|
705
|
-
currentNode.parentNode?.replaceChild(
|
|
706
|
-
|
|
680
|
+
if (oldVNode.tag !== newVNode.tag || currentNode.nodeType === Node.COMMENT_NODE) {
|
|
681
|
+
const nextNode = this.mount(newVNode, context);
|
|
682
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
683
|
+
this.unmount(oldVNode, currentNode, context);
|
|
684
|
+
return nextNode;
|
|
685
|
+
}
|
|
686
|
+
if (!(currentNode instanceof HTMLElement)) {
|
|
687
|
+
return currentNode;
|
|
707
688
|
}
|
|
708
689
|
if (newVNode.directions?.if === false) {
|
|
709
|
-
const
|
|
710
|
-
currentNode.parentNode?.replaceChild(
|
|
690
|
+
const nextNode = document.createComment("if");
|
|
691
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
711
692
|
this.unmount(oldVNode, currentNode, context);
|
|
712
|
-
return
|
|
713
|
-
}
|
|
714
|
-
const instance = this.instances.get(currentNode);
|
|
715
|
-
if (instance && oldVNode.component === newVNode.component) {
|
|
716
|
-
this.syncEmitters(instance, newVNode.emitters ?? {});
|
|
717
|
-
instance.setProps(this.createProps(newVNode));
|
|
718
|
-
const nextNode2 = instance.getElement() ?? currentNode;
|
|
719
|
-
this.trackInstanceNode(instance, nextNode2);
|
|
720
|
-
return nextNode2;
|
|
721
|
-
}
|
|
722
|
-
const nextNode = this.mount(newVNode, context);
|
|
723
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
724
|
-
this.unmount(oldVNode, currentNode, context);
|
|
725
|
-
return nextNode;
|
|
726
|
-
}
|
|
727
|
-
unmount(_vnode, currentNode, context) {
|
|
728
|
-
const instance = this.instances.get(currentNode);
|
|
729
|
-
if (instance) {
|
|
730
|
-
this.clearEmitters(instance);
|
|
731
|
-
instance.unmount();
|
|
732
|
-
this.clearInstanceNodes(instance);
|
|
733
|
-
context.unregisterChild(instance);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
createProps(vnode) {
|
|
737
|
-
return {
|
|
738
|
-
...vnode.props ?? {},
|
|
739
|
-
children: vnode.children ?? []
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
syncEmitters(instance, emitters) {
|
|
743
|
-
const current = this.emitterUnsubscribers.get(instance) ?? new Map;
|
|
744
|
-
current.forEach(({ listener: currentListener, unsubscribe }, eventName) => {
|
|
745
|
-
const listener = emitters[eventName];
|
|
746
|
-
if (!listener || listener !== currentListener) {
|
|
747
|
-
unsubscribe();
|
|
748
|
-
current.delete(eventName);
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
Object.entries(emitters).forEach(([eventName, listener]) => {
|
|
752
|
-
if (current.get(eventName)?.listener === listener) {
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
current.set(eventName, {
|
|
756
|
-
listener,
|
|
757
|
-
unsubscribe: instance.on(eventName, listener)
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
this.emitterUnsubscribers.set(instance, current);
|
|
761
|
-
}
|
|
762
|
-
clearEmitters(instance) {
|
|
763
|
-
this.emitterUnsubscribers.get(instance)?.forEach(({ unsubscribe }) => {
|
|
764
|
-
unsubscribe();
|
|
765
|
-
});
|
|
766
|
-
this.emitterUnsubscribers.delete(instance);
|
|
767
|
-
}
|
|
768
|
-
trackInstanceNode(instance, node) {
|
|
769
|
-
this.instances.set(node, instance);
|
|
770
|
-
const nodes = this.instanceNodes.get(instance) ?? new Set;
|
|
771
|
-
nodes.add(node);
|
|
772
|
-
this.instanceNodes.set(instance, nodes);
|
|
773
|
-
}
|
|
774
|
-
clearInstanceNodes(instance) {
|
|
775
|
-
this.instanceNodes.get(instance)?.forEach((node) => {
|
|
776
|
-
this.instances.delete(node);
|
|
777
|
-
});
|
|
778
|
-
this.instanceNodes.delete(instance);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
class SlotRenderStrategy {
|
|
783
|
-
renderedChildren = new WeakMap;
|
|
784
|
-
matches(vnode) {
|
|
785
|
-
return typeof vnode === "object" && vnode !== null && isSlotProvider(vnode);
|
|
786
|
-
}
|
|
787
|
-
mount(vnode, context) {
|
|
788
|
-
if (vnode.directions?.if === false) {
|
|
789
|
-
return document.createComment("if");
|
|
790
|
-
}
|
|
791
|
-
const slotContainer = document.createElement("div");
|
|
792
|
-
slotContainer.setAttribute("data-slot", vnode.props.name);
|
|
793
|
-
this.mountSlotChildren(slotContainer, this.resolveChildren(vnode, context), context);
|
|
794
|
-
return slotContainer;
|
|
795
|
-
}
|
|
796
|
-
patch(oldVNode, newVNode, currentNode, context) {
|
|
797
|
-
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
798
|
-
const nextNode = this.mount(newVNode, context);
|
|
799
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
800
|
-
return nextNode;
|
|
801
|
-
}
|
|
802
|
-
if (newVNode.directions?.if === false) {
|
|
803
|
-
const nextNode = document.createComment("if");
|
|
804
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
805
|
-
this.unmount(oldVNode, currentNode, context);
|
|
806
|
-
return nextNode;
|
|
807
|
-
}
|
|
808
|
-
if (currentNode instanceof HTMLElement) {
|
|
809
|
-
currentNode.setAttribute("data-slot", newVNode.props.name);
|
|
810
|
-
this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);
|
|
811
|
-
}
|
|
812
|
-
return currentNode;
|
|
813
|
-
}
|
|
814
|
-
unmount(_vnode, currentNode, context) {
|
|
815
|
-
if (!(currentNode instanceof HTMLElement)) {
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
this.unmountSlotChildren(currentNode, context);
|
|
819
|
-
this.renderedChildren.delete(currentNode);
|
|
820
|
-
}
|
|
821
|
-
resolveChildren(vnode, context) {
|
|
822
|
-
return context.slots[vnode.props.name] ?? vnode.children ?? [];
|
|
823
|
-
}
|
|
824
|
-
replaceSlotChildren(element, _oldVNode, newVNode, context) {
|
|
825
|
-
this.unmountSlotChildren(element, context);
|
|
826
|
-
element.textContent = "";
|
|
827
|
-
this.mountSlotChildren(element, this.resolveChildren(newVNode, context), context);
|
|
828
|
-
}
|
|
829
|
-
mountSlotChildren(element, children, context) {
|
|
830
|
-
children.forEach((child) => {
|
|
831
|
-
element.appendChild(context.renderer.mount(child, context));
|
|
832
|
-
});
|
|
833
|
-
this.renderedChildren.set(element, children);
|
|
834
|
-
}
|
|
835
|
-
unmountSlotChildren(element, context) {
|
|
836
|
-
const children = this.renderedChildren.get(element) ?? [];
|
|
837
|
-
children.forEach((child, index) => {
|
|
838
|
-
const childNode = element.childNodes[index];
|
|
839
|
-
if (childNode) {
|
|
840
|
-
context.renderer.unmount(child, childNode, context);
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
class ElementRenderStrategy {
|
|
847
|
-
listeners = new WeakMap;
|
|
848
|
-
effects = new WeakMap;
|
|
849
|
-
modelBindings = new ModelBindingController;
|
|
850
|
-
matches(vnode) {
|
|
851
|
-
return typeof vnode === "object" && vnode !== null && isHTMLNode(vnode);
|
|
852
|
-
}
|
|
853
|
-
mount(vnode, context) {
|
|
854
|
-
if (vnode.directions?.if === false) {
|
|
855
|
-
return document.createComment("if");
|
|
856
|
-
}
|
|
857
|
-
const element = document.createElement(vnode.tag);
|
|
858
|
-
this.applyProps(element, {}, vnode.props ?? {}, context);
|
|
859
|
-
this.updateListeners(element, {}, this.collectListeners(vnode));
|
|
860
|
-
(vnode.children ?? []).forEach((child) => {
|
|
861
|
-
element.appendChild(context.renderer.mount(child, context));
|
|
862
|
-
});
|
|
863
|
-
this.applyDirections(element, undefined, vnode.directions, context);
|
|
864
|
-
return element;
|
|
865
|
-
}
|
|
866
|
-
patch(oldVNode, newVNode, currentNode, context) {
|
|
867
|
-
if (oldVNode.tag !== newVNode.tag || currentNode.nodeType === Node.COMMENT_NODE) {
|
|
868
|
-
const nextNode = this.mount(newVNode, context);
|
|
869
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
870
|
-
this.unmount(oldVNode, currentNode, context);
|
|
871
|
-
return nextNode;
|
|
872
|
-
}
|
|
873
|
-
if (!(currentNode instanceof HTMLElement)) {
|
|
874
|
-
return currentNode;
|
|
875
|
-
}
|
|
876
|
-
if (newVNode.directions?.if === false) {
|
|
877
|
-
const nextNode = document.createComment("if");
|
|
878
|
-
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
879
|
-
this.unmount(oldVNode, currentNode, context);
|
|
880
|
-
return nextNode;
|
|
693
|
+
return nextNode;
|
|
881
694
|
}
|
|
882
695
|
this.applyProps(currentNode, oldVNode.props ?? {}, newVNode.props ?? {}, context);
|
|
883
696
|
this.updateListeners(currentNode, this.collectListeners(oldVNode), this.collectListeners(newVNode));
|
|
884
|
-
this.updateChildren(currentNode, oldVNode
|
|
697
|
+
this.updateChildren(currentNode, oldVNode, newVNode, context);
|
|
885
698
|
this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
|
|
886
699
|
return currentNode;
|
|
887
700
|
}
|
|
@@ -896,8 +709,19 @@ class ElementRenderStrategy {
|
|
|
896
709
|
});
|
|
897
710
|
this.listeners.delete(currentNode);
|
|
898
711
|
this.modelBindings.cleanup(currentNode);
|
|
712
|
+
this.unmountChildren(currentNode, vnode, context);
|
|
713
|
+
}
|
|
714
|
+
mountChildren(element, vnode, context) {
|
|
715
|
+
(vnode.children ?? []).forEach((child) => {
|
|
716
|
+
element.appendChild(context.renderer.mount(child, context));
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
updateChildren(element, oldVNode, newVNode, context) {
|
|
720
|
+
this.updateOrdinaryChildren(element, oldVNode.children ?? [], newVNode.children ?? [], context);
|
|
721
|
+
}
|
|
722
|
+
unmountChildren(element, vnode, context) {
|
|
899
723
|
(vnode.children ?? []).forEach((child, index) => {
|
|
900
|
-
const childNode =
|
|
724
|
+
const childNode = element.childNodes[index];
|
|
901
725
|
if (childNode) {
|
|
902
726
|
context.renderer.unmount(child, childNode, context);
|
|
903
727
|
}
|
|
@@ -958,7 +782,7 @@ class ElementRenderStrategy {
|
|
|
958
782
|
}
|
|
959
783
|
this.modelBindings.bind(element, newDirections.model, context.templateEngine.state);
|
|
960
784
|
}
|
|
961
|
-
|
|
785
|
+
updateOrdinaryChildren(element, oldChildren, newChildren, context) {
|
|
962
786
|
this.assertNoDuplicateKeys(oldChildren);
|
|
963
787
|
this.assertNoDuplicateKeys(newChildren);
|
|
964
788
|
if (this.hasOnlyKeyedChildren(oldChildren, newChildren)) {
|
|
@@ -1020,83 +844,554 @@ class ElementRenderStrategy {
|
|
|
1020
844
|
element.insertBefore(nextNode, referenceNode);
|
|
1021
845
|
}
|
|
1022
846
|
});
|
|
1023
|
-
oldEntries.forEach((entry) => {
|
|
1024
|
-
if (!entry.node || usedOldIndexes.has(entry.index)) {
|
|
847
|
+
oldEntries.forEach((entry) => {
|
|
848
|
+
if (!entry.node || usedOldIndexes.has(entry.index)) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
context.renderer.unmount(entry.vnode, entry.node, context);
|
|
852
|
+
if (entry.node.parentNode === element) {
|
|
853
|
+
element.removeChild(entry.node);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
hasOnlyKeyedChildren(oldChildren, newChildren) {
|
|
858
|
+
return [...oldChildren, ...newChildren].every((child) => this.getVNodeKey(child) !== undefined);
|
|
859
|
+
}
|
|
860
|
+
assertNoDuplicateKeys(children) {
|
|
861
|
+
const keys = new Set;
|
|
862
|
+
children.forEach((child) => {
|
|
863
|
+
const key = this.getVNodeKey(child);
|
|
864
|
+
if (key === undefined) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (keys.has(key)) {
|
|
868
|
+
throw new Error(`Duplicate key "${key}"`);
|
|
869
|
+
}
|
|
870
|
+
keys.add(key);
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
getVNodeKey(vnode) {
|
|
874
|
+
if (typeof vnode === "string") {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
return vnode.key;
|
|
878
|
+
}
|
|
879
|
+
collectListeners(vnode) {
|
|
880
|
+
const listeners = {};
|
|
881
|
+
Object.entries(vnode.props ?? {}).forEach(([key, value]) => {
|
|
882
|
+
if (isEventProp(key) && typeof value === "function") {
|
|
883
|
+
listeners[eventNameFromProp(key)] = value;
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
return {
|
|
887
|
+
...listeners,
|
|
888
|
+
...vnode.listeners ?? {}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
updateListeners(element, oldListeners, newListeners) {
|
|
892
|
+
const store = this.listeners.get(element) ?? new Map;
|
|
893
|
+
const oldKeys = new Set(Object.keys(oldListeners));
|
|
894
|
+
const newKeys = new Set(Object.keys(newListeners));
|
|
895
|
+
oldKeys.forEach((event) => {
|
|
896
|
+
if (!newKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
897
|
+
const stored = store.get(event);
|
|
898
|
+
if (stored) {
|
|
899
|
+
element.removeEventListener(stored.eventName, stored.listener);
|
|
900
|
+
store.delete(event);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
newKeys.forEach((event) => {
|
|
905
|
+
if (!oldKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
906
|
+
const { eventName, modifiers } = parseEventName(event);
|
|
907
|
+
const listener = wrapEventHandler(newListeners[event], modifiers);
|
|
908
|
+
element.addEventListener(eventName, listener);
|
|
909
|
+
store.set(event, { eventName, listener });
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
this.listeners.set(element, store);
|
|
913
|
+
}
|
|
914
|
+
setupReactiveAttribute(element, attrName, attrValue, context) {
|
|
915
|
+
const effectRef = effect(() => {
|
|
916
|
+
element.setAttribute(attrName, context.templateEngine.evaluateTemplateValue(attrValue));
|
|
917
|
+
});
|
|
918
|
+
this.trackEffect(element, effectRef);
|
|
919
|
+
}
|
|
920
|
+
trackEffect(element, effectRef) {
|
|
921
|
+
const effects = this.effects.get(element) ?? new Set;
|
|
922
|
+
effects.add(effectRef);
|
|
923
|
+
this.effects.set(element, effects);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// lib/core/animation/list-animation-controller.ts
|
|
928
|
+
var ENTER_KEYFRAMES = {
|
|
929
|
+
fade: [{ opacity: 0 }, { opacity: 1 }],
|
|
930
|
+
"slide-up": [
|
|
931
|
+
{ opacity: 0, transform: "translateY(12px)" },
|
|
932
|
+
{ opacity: 1, transform: "translateY(0)" }
|
|
933
|
+
],
|
|
934
|
+
"slide-down": [
|
|
935
|
+
{ opacity: 0, transform: "translateY(-12px)" },
|
|
936
|
+
{ opacity: 1, transform: "translateY(0)" }
|
|
937
|
+
],
|
|
938
|
+
"slide-left": [
|
|
939
|
+
{ opacity: 0, transform: "translateX(12px)" },
|
|
940
|
+
{ opacity: 1, transform: "translateX(0)" }
|
|
941
|
+
],
|
|
942
|
+
"slide-right": [
|
|
943
|
+
{ opacity: 0, transform: "translateX(-12px)" },
|
|
944
|
+
{ opacity: 1, transform: "translateX(0)" }
|
|
945
|
+
],
|
|
946
|
+
scale: [
|
|
947
|
+
{ opacity: 0, transform: "scale(0.95)" },
|
|
948
|
+
{ opacity: 1, transform: "scale(1)" }
|
|
949
|
+
]
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
class ListAnimationController {
|
|
953
|
+
runs = new WeakMap;
|
|
954
|
+
playEnter(element, options) {
|
|
955
|
+
return this.play(element, options, "enter");
|
|
956
|
+
}
|
|
957
|
+
playExit(element, options) {
|
|
958
|
+
return this.play(element, options, "exit");
|
|
959
|
+
}
|
|
960
|
+
cancel(element) {
|
|
961
|
+
const current = this.runs.get(element);
|
|
962
|
+
if (!current) {
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
this.runs.delete(element);
|
|
966
|
+
current.animation.cancel();
|
|
967
|
+
}
|
|
968
|
+
play(element, transition, phase) {
|
|
969
|
+
this.cancel(element);
|
|
970
|
+
if (!this.canAnimate(element)) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
const enterKeyframes = ENTER_KEYFRAMES[transition.type];
|
|
974
|
+
const keyframes = phase === "enter" ? [...enterKeyframes] : [...enterKeyframes].reverse();
|
|
975
|
+
const options = {
|
|
976
|
+
duration: transition.duration,
|
|
977
|
+
easing: "ease",
|
|
978
|
+
fill: "both"
|
|
979
|
+
};
|
|
980
|
+
const animation = element.animate(keyframes, options);
|
|
981
|
+
const token = Symbol("list-animation");
|
|
982
|
+
const finished = animation.finished.then(() => "finished", () => "cancelled");
|
|
983
|
+
const run = {
|
|
984
|
+
animation,
|
|
985
|
+
token,
|
|
986
|
+
keyframes,
|
|
987
|
+
options,
|
|
988
|
+
finished
|
|
989
|
+
};
|
|
990
|
+
this.runs.set(element, run);
|
|
991
|
+
finished.then((result) => {
|
|
992
|
+
if (this.runs.get(element)?.token !== token) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
this.runs.delete(element);
|
|
996
|
+
if (phase === "enter" && result === "finished") {
|
|
997
|
+
animation.cancel();
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
return run;
|
|
1001
|
+
}
|
|
1002
|
+
canAnimate(element) {
|
|
1003
|
+
const reduced = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1004
|
+
return !reduced && typeof element.animate === "function";
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// lib/core/animation/types.ts
|
|
1009
|
+
var TRANSITION_ANIMATION_TYPES = [
|
|
1010
|
+
"fade",
|
|
1011
|
+
"slide-up",
|
|
1012
|
+
"slide-down",
|
|
1013
|
+
"slide-left",
|
|
1014
|
+
"slide-right",
|
|
1015
|
+
"scale"
|
|
1016
|
+
];
|
|
1017
|
+
function normalizeTransitionGroupProps(props) {
|
|
1018
|
+
const tag = (props.tag ?? "div").trim();
|
|
1019
|
+
const type = props.type ?? "fade";
|
|
1020
|
+
const duration = props.duration ?? 300;
|
|
1021
|
+
if (!tag) {
|
|
1022
|
+
throw new Error("TransitionGroup tag must not be empty");
|
|
1023
|
+
}
|
|
1024
|
+
if (!TRANSITION_ANIMATION_TYPES.includes(type)) {
|
|
1025
|
+
throw new Error(`Unknown TransitionGroup animation type "${type}"`);
|
|
1026
|
+
}
|
|
1027
|
+
if (!Number.isFinite(duration) || duration < 0) {
|
|
1028
|
+
throw new Error("TransitionGroup duration must be a non-negative finite number");
|
|
1029
|
+
}
|
|
1030
|
+
return { tag, type, duration };
|
|
1031
|
+
}
|
|
1032
|
+
function validateTransitionGroupChildren(children) {
|
|
1033
|
+
const keys = new Set;
|
|
1034
|
+
return children.map((child) => {
|
|
1035
|
+
if (typeof child === "string" || child.key === undefined || keys.has(child.key)) {
|
|
1036
|
+
throw new Error("TransitionGroup children must have unique keys");
|
|
1037
|
+
}
|
|
1038
|
+
keys.add(child.key);
|
|
1039
|
+
return child;
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function isTransitionGroupNode(vnode) {
|
|
1043
|
+
return typeof vnode === "object" && vnode !== null && "transitionGroup" in vnode && isHTMLNode(vnode);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// lib/core/animation/transition-group-strategy.ts
|
|
1047
|
+
class TransitionGroupRenderStrategy extends ElementRenderStrategy {
|
|
1048
|
+
entries = new WeakMap;
|
|
1049
|
+
animations = new ListAnimationController;
|
|
1050
|
+
matches(vnode) {
|
|
1051
|
+
return isTransitionGroupNode(vnode);
|
|
1052
|
+
}
|
|
1053
|
+
mountChildren(element, groupVNode, context) {
|
|
1054
|
+
const keyedChildren = validateTransitionGroupChildren(groupVNode.children ?? []);
|
|
1055
|
+
const entries = new Map;
|
|
1056
|
+
keyedChildren.forEach((childVNode) => {
|
|
1057
|
+
const node = context.renderer.mount(childVNode, context);
|
|
1058
|
+
element.appendChild(node);
|
|
1059
|
+
const entry = {
|
|
1060
|
+
key: childVNode.key,
|
|
1061
|
+
vnode: childVNode,
|
|
1062
|
+
node,
|
|
1063
|
+
status: "active"
|
|
1064
|
+
};
|
|
1065
|
+
entries.set(entry.key, entry);
|
|
1066
|
+
this.playEnter(entry, node, groupVNode.transitionGroup);
|
|
1067
|
+
});
|
|
1068
|
+
this.entries.set(element, entries);
|
|
1069
|
+
}
|
|
1070
|
+
updateChildren(element, _oldVNode, newVNode, context) {
|
|
1071
|
+
const entries = this.entries.get(element) ?? new Map;
|
|
1072
|
+
const nextChildren = validateTransitionGroupChildren(newVNode.children ?? []);
|
|
1073
|
+
const nextKeys = new Set(nextChildren.map((child) => child.key));
|
|
1074
|
+
const ordered = [];
|
|
1075
|
+
nextChildren.forEach((childVNode) => {
|
|
1076
|
+
const key = childVNode.key;
|
|
1077
|
+
const current = entries.get(key);
|
|
1078
|
+
if (current) {
|
|
1079
|
+
const wasExiting = current.status === "exiting";
|
|
1080
|
+
if (wasExiting && current.node instanceof HTMLElement) {
|
|
1081
|
+
this.animations.cancel(current.node);
|
|
1082
|
+
}
|
|
1083
|
+
current.status = "active";
|
|
1084
|
+
current.animationToken = undefined;
|
|
1085
|
+
current.node = context.renderer.patch(current.vnode, childVNode, current.node, context);
|
|
1086
|
+
current.vnode = childVNode;
|
|
1087
|
+
ordered.push(current);
|
|
1088
|
+
if (wasExiting) {
|
|
1089
|
+
this.playEnter(current, current.node, newVNode.transitionGroup);
|
|
1090
|
+
}
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const node = context.renderer.mount(childVNode, context);
|
|
1094
|
+
const entry = {
|
|
1095
|
+
key,
|
|
1096
|
+
vnode: childVNode,
|
|
1097
|
+
node,
|
|
1098
|
+
status: "active"
|
|
1099
|
+
};
|
|
1100
|
+
entries.set(key, entry);
|
|
1101
|
+
ordered.push(entry);
|
|
1102
|
+
this.playEnter(entry, node, newVNode.transitionGroup);
|
|
1103
|
+
});
|
|
1104
|
+
entries.forEach((entry, key) => {
|
|
1105
|
+
if (nextKeys.has(key) || entry.status !== "active") {
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
this.startExit(element, entry, newVNode.transitionGroup, context);
|
|
1109
|
+
});
|
|
1110
|
+
this.placeActiveEntries(element, ordered);
|
|
1111
|
+
this.entries.set(element, entries);
|
|
1112
|
+
}
|
|
1113
|
+
unmountChildren(element, vnode, context) {
|
|
1114
|
+
const entries = this.entries.get(element);
|
|
1115
|
+
if (!entries) {
|
|
1116
|
+
super.unmountChildren(element, vnode, context);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
entries.forEach((entry) => {
|
|
1120
|
+
if (entry.node instanceof HTMLElement) {
|
|
1121
|
+
this.animations.cancel(entry.node);
|
|
1122
|
+
}
|
|
1123
|
+
context.renderer.unmount(entry.vnode, entry.node, context);
|
|
1124
|
+
if (entry.node.parentNode === element) {
|
|
1125
|
+
element.removeChild(entry.node);
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
entries.clear();
|
|
1129
|
+
this.entries.delete(element);
|
|
1130
|
+
}
|
|
1131
|
+
playEnter(entry, node, options) {
|
|
1132
|
+
if (!(node instanceof HTMLElement)) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
const run = this.animations.playEnter(node, options);
|
|
1136
|
+
entry.animationToken = run?.token;
|
|
1137
|
+
}
|
|
1138
|
+
startExit(wrapper, entry, options, context) {
|
|
1139
|
+
entry.status = "exiting";
|
|
1140
|
+
const run = entry.node instanceof HTMLElement ? this.animations.playExit(entry.node, options) : null;
|
|
1141
|
+
if (!run) {
|
|
1142
|
+
this.finishExit(wrapper, entry, context);
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
entry.animationToken = run.token;
|
|
1146
|
+
run.finished.then((result) => {
|
|
1147
|
+
if (result === "finished" && entry.status === "exiting" && entry.animationToken === run.token) {
|
|
1148
|
+
this.finishExit(wrapper, entry, context);
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
finishExit(wrapper, entry, context) {
|
|
1153
|
+
const entries = this.entries.get(wrapper);
|
|
1154
|
+
if (entries?.get(entry.key) !== entry) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
context.renderer.unmount(entry.vnode, entry.node, context);
|
|
1158
|
+
if (entry.node.parentNode === wrapper) {
|
|
1159
|
+
wrapper.removeChild(entry.node);
|
|
1160
|
+
}
|
|
1161
|
+
entries.delete(entry.key);
|
|
1162
|
+
}
|
|
1163
|
+
placeActiveEntries(wrapper, ordered) {
|
|
1164
|
+
let reference = null;
|
|
1165
|
+
for (let index = ordered.length - 1;index >= 0; index -= 1) {
|
|
1166
|
+
wrapper.insertBefore(ordered[index].node, reference);
|
|
1167
|
+
reference = ordered[index].node;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// lib/core/renderer.ts
|
|
1173
|
+
class RendererContext {
|
|
1174
|
+
strategies;
|
|
1175
|
+
constructor() {
|
|
1176
|
+
this.strategies = [
|
|
1177
|
+
new TextRenderStrategy,
|
|
1178
|
+
new ComponentRenderStrategy,
|
|
1179
|
+
new SlotRenderStrategy,
|
|
1180
|
+
new TransitionGroupRenderStrategy,
|
|
1181
|
+
new ElementRenderStrategy
|
|
1182
|
+
];
|
|
1183
|
+
}
|
|
1184
|
+
mount(vnode, context) {
|
|
1185
|
+
return this.findStrategy(vnode).mount(vnode, context);
|
|
1186
|
+
}
|
|
1187
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
1188
|
+
const oldStrategy = this.findStrategy(oldVNode);
|
|
1189
|
+
const newStrategy = this.findStrategy(newVNode);
|
|
1190
|
+
if (oldStrategy !== newStrategy) {
|
|
1191
|
+
const nextNode = newStrategy.mount(newVNode, context);
|
|
1192
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
1193
|
+
oldStrategy.unmount(oldVNode, currentNode, context);
|
|
1194
|
+
return nextNode;
|
|
1195
|
+
}
|
|
1196
|
+
return oldStrategy.patch(oldVNode, newVNode, currentNode, context);
|
|
1197
|
+
}
|
|
1198
|
+
unmount(vnode, currentNode, context) {
|
|
1199
|
+
this.findStrategy(vnode).unmount(vnode, currentNode, context);
|
|
1200
|
+
}
|
|
1201
|
+
findStrategy(vnode) {
|
|
1202
|
+
const strategy = this.strategies.find((item) => item.matches(vnode));
|
|
1203
|
+
if (!strategy) {
|
|
1204
|
+
throw new Error("No render strategy found for vnode");
|
|
1205
|
+
}
|
|
1206
|
+
return strategy;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
class TextRenderStrategy {
|
|
1211
|
+
matches(vnode) {
|
|
1212
|
+
return typeof vnode === "string";
|
|
1213
|
+
}
|
|
1214
|
+
mount(vnode, context) {
|
|
1215
|
+
return context.templateEngine.parseTemplate(vnode);
|
|
1216
|
+
}
|
|
1217
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
1218
|
+
if (oldVNode === newVNode) {
|
|
1219
|
+
return currentNode;
|
|
1220
|
+
}
|
|
1221
|
+
const nextNode = this.mount(newVNode, context);
|
|
1222
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
1223
|
+
return nextNode;
|
|
1224
|
+
}
|
|
1225
|
+
unmount() {}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
class ComponentRenderStrategy {
|
|
1229
|
+
instances = new WeakMap;
|
|
1230
|
+
instanceNodes = new Map;
|
|
1231
|
+
emitterUnsubscribers = new WeakMap;
|
|
1232
|
+
matches(vnode) {
|
|
1233
|
+
return typeof vnode === "object" && vnode !== null && isComponentNode(vnode);
|
|
1234
|
+
}
|
|
1235
|
+
mount(vnode, context) {
|
|
1236
|
+
if (vnode.directions?.if === false) {
|
|
1237
|
+
return document.createComment("if");
|
|
1238
|
+
}
|
|
1239
|
+
const ComponentClass = vnode.component;
|
|
1240
|
+
const instance = new ComponentClass(this.createProps(vnode));
|
|
1241
|
+
if (context.appContext && instance.setAppContext) {
|
|
1242
|
+
instance.setAppContext(context.appContext);
|
|
1243
|
+
}
|
|
1244
|
+
this.syncEmitters(instance, vnode.emitters ?? {});
|
|
1245
|
+
context.registerChild(instance);
|
|
1246
|
+
const node = instance.mountToNode();
|
|
1247
|
+
this.trackInstanceNode(instance, node);
|
|
1248
|
+
instance.setElementChangeListener?.((previousNode, nextNode) => {
|
|
1249
|
+
this.trackInstanceNode(instance, previousNode);
|
|
1250
|
+
this.trackInstanceNode(instance, nextNode);
|
|
1251
|
+
});
|
|
1252
|
+
return node;
|
|
1253
|
+
}
|
|
1254
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
1255
|
+
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
1256
|
+
const nextNode2 = this.mount(newVNode, context);
|
|
1257
|
+
currentNode.parentNode?.replaceChild(nextNode2, currentNode);
|
|
1258
|
+
return nextNode2;
|
|
1259
|
+
}
|
|
1260
|
+
if (newVNode.directions?.if === false) {
|
|
1261
|
+
const nextNode2 = document.createComment("if");
|
|
1262
|
+
currentNode.parentNode?.replaceChild(nextNode2, currentNode);
|
|
1263
|
+
this.unmount(oldVNode, currentNode, context);
|
|
1264
|
+
return nextNode2;
|
|
1265
|
+
}
|
|
1266
|
+
const instance = this.instances.get(currentNode);
|
|
1267
|
+
if (instance && oldVNode.component === newVNode.component) {
|
|
1268
|
+
this.syncEmitters(instance, newVNode.emitters ?? {});
|
|
1269
|
+
instance.setProps(this.createProps(newVNode));
|
|
1270
|
+
const nextNode2 = instance.getElement() ?? currentNode;
|
|
1271
|
+
this.trackInstanceNode(instance, nextNode2);
|
|
1272
|
+
return nextNode2;
|
|
1273
|
+
}
|
|
1274
|
+
const nextNode = this.mount(newVNode, context);
|
|
1275
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
1276
|
+
this.unmount(oldVNode, currentNode, context);
|
|
1277
|
+
return nextNode;
|
|
1278
|
+
}
|
|
1279
|
+
unmount(_vnode, currentNode, context) {
|
|
1280
|
+
const instance = this.instances.get(currentNode);
|
|
1281
|
+
if (instance) {
|
|
1282
|
+
this.clearEmitters(instance);
|
|
1283
|
+
instance.unmount();
|
|
1284
|
+
this.clearInstanceNodes(instance);
|
|
1285
|
+
context.unregisterChild(instance);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
createProps(vnode) {
|
|
1289
|
+
return {
|
|
1290
|
+
...vnode.props ?? {},
|
|
1291
|
+
children: vnode.children ?? []
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
syncEmitters(instance, emitters) {
|
|
1295
|
+
const current = this.emitterUnsubscribers.get(instance) ?? new Map;
|
|
1296
|
+
current.forEach(({ listener: currentListener, unsubscribe }, eventName) => {
|
|
1297
|
+
const listener = emitters[eventName];
|
|
1298
|
+
if (!listener || listener !== currentListener) {
|
|
1299
|
+
unsubscribe();
|
|
1300
|
+
current.delete(eventName);
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
Object.entries(emitters).forEach(([eventName, listener]) => {
|
|
1304
|
+
if (current.get(eventName)?.listener === listener) {
|
|
1025
1305
|
return;
|
|
1026
1306
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1307
|
+
current.set(eventName, {
|
|
1308
|
+
listener,
|
|
1309
|
+
unsubscribe: instance.on(eventName, listener)
|
|
1310
|
+
});
|
|
1031
1311
|
});
|
|
1312
|
+
this.emitterUnsubscribers.set(instance, current);
|
|
1032
1313
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1314
|
+
clearEmitters(instance) {
|
|
1315
|
+
this.emitterUnsubscribers.get(instance)?.forEach(({ unsubscribe }) => {
|
|
1316
|
+
unsubscribe();
|
|
1317
|
+
});
|
|
1318
|
+
this.emitterUnsubscribers.delete(instance);
|
|
1035
1319
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
keys.add(key);
|
|
1320
|
+
trackInstanceNode(instance, node) {
|
|
1321
|
+
this.instances.set(node, instance);
|
|
1322
|
+
const nodes = this.instanceNodes.get(instance) ?? new Set;
|
|
1323
|
+
nodes.add(node);
|
|
1324
|
+
this.instanceNodes.set(instance, nodes);
|
|
1325
|
+
}
|
|
1326
|
+
clearInstanceNodes(instance) {
|
|
1327
|
+
this.instanceNodes.get(instance)?.forEach((node) => {
|
|
1328
|
+
this.instances.delete(node);
|
|
1047
1329
|
});
|
|
1330
|
+
this.instanceNodes.delete(instance);
|
|
1048
1331
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
class SlotRenderStrategy {
|
|
1335
|
+
renderedChildren = new WeakMap;
|
|
1336
|
+
matches(vnode) {
|
|
1337
|
+
return typeof vnode === "object" && vnode !== null && isSlotProvider(vnode);
|
|
1338
|
+
}
|
|
1339
|
+
mount(vnode, context) {
|
|
1340
|
+
if (vnode.directions?.if === false) {
|
|
1341
|
+
return document.createComment("if");
|
|
1342
|
+
}
|
|
1343
|
+
const slotContainer = document.createElement("div");
|
|
1344
|
+
slotContainer.setAttribute("data-slot", vnode.props.name);
|
|
1345
|
+
this.mountSlotChildren(slotContainer, this.resolveChildren(vnode, context), context);
|
|
1346
|
+
return slotContainer;
|
|
1347
|
+
}
|
|
1348
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
1349
|
+
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
1350
|
+
const nextNode = this.mount(newVNode, context);
|
|
1351
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
1352
|
+
return nextNode;
|
|
1353
|
+
}
|
|
1354
|
+
if (newVNode.directions?.if === false) {
|
|
1355
|
+
const nextNode = document.createComment("if");
|
|
1356
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
1357
|
+
this.unmount(oldVNode, currentNode, context);
|
|
1358
|
+
return nextNode;
|
|
1359
|
+
}
|
|
1360
|
+
if (currentNode instanceof HTMLElement) {
|
|
1361
|
+
currentNode.setAttribute("data-slot", newVNode.props.name);
|
|
1362
|
+
this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);
|
|
1363
|
+
}
|
|
1364
|
+
return currentNode;
|
|
1365
|
+
}
|
|
1366
|
+
unmount(_vnode, currentNode, context) {
|
|
1367
|
+
if (!(currentNode instanceof HTMLElement)) {
|
|
1051
1368
|
return;
|
|
1052
1369
|
}
|
|
1053
|
-
|
|
1370
|
+
this.unmountSlotChildren(currentNode, context);
|
|
1371
|
+
this.renderedChildren.delete(currentNode);
|
|
1054
1372
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
Object.entries(vnode.props ?? {}).forEach(([key, value]) => {
|
|
1058
|
-
if (isEventProp(key) && typeof value === "function") {
|
|
1059
|
-
listeners[eventNameFromProp(key)] = value;
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
return {
|
|
1063
|
-
...listeners,
|
|
1064
|
-
...vnode.listeners ?? {}
|
|
1065
|
-
};
|
|
1373
|
+
resolveChildren(vnode, context) {
|
|
1374
|
+
return context.slots[vnode.props.name] ?? vnode.children ?? [];
|
|
1066
1375
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
oldKeys.forEach((event) => {
|
|
1072
|
-
if (!newKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
1073
|
-
const stored = store.get(event);
|
|
1074
|
-
if (stored) {
|
|
1075
|
-
element.removeEventListener(stored.eventName, stored.listener);
|
|
1076
|
-
store.delete(event);
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
newKeys.forEach((event) => {
|
|
1081
|
-
if (!oldKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
1082
|
-
const { eventName, modifiers } = parseEventName(event);
|
|
1083
|
-
const listener = wrapEventHandler(newListeners[event], modifiers);
|
|
1084
|
-
element.addEventListener(eventName, listener);
|
|
1085
|
-
store.set(event, { eventName, listener });
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
this.listeners.set(element, store);
|
|
1376
|
+
replaceSlotChildren(element, _oldVNode, newVNode, context) {
|
|
1377
|
+
this.unmountSlotChildren(element, context);
|
|
1378
|
+
element.textContent = "";
|
|
1379
|
+
this.mountSlotChildren(element, this.resolveChildren(newVNode, context), context);
|
|
1089
1380
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
element.
|
|
1381
|
+
mountSlotChildren(element, children, context) {
|
|
1382
|
+
children.forEach((child) => {
|
|
1383
|
+
element.appendChild(context.renderer.mount(child, context));
|
|
1093
1384
|
});
|
|
1094
|
-
this.
|
|
1385
|
+
this.renderedChildren.set(element, children);
|
|
1095
1386
|
}
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1387
|
+
unmountSlotChildren(element, context) {
|
|
1388
|
+
const children = this.renderedChildren.get(element) ?? [];
|
|
1389
|
+
children.forEach((child, index) => {
|
|
1390
|
+
const childNode = element.childNodes[index];
|
|
1391
|
+
if (childNode) {
|
|
1392
|
+
context.renderer.unmount(child, childNode, context);
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1100
1395
|
}
|
|
1101
1396
|
}
|
|
1102
1397
|
|
|
@@ -1812,7 +2107,347 @@ function createRouter(options) {
|
|
|
1812
2107
|
return new Router(options);
|
|
1813
2108
|
}
|
|
1814
2109
|
|
|
1815
|
-
|
|
2110
|
+
// lib/core/document.ts
|
|
2111
|
+
var VOID_HEAD_TAGS = new Set(["base", "link", "meta"]);
|
|
2112
|
+
function renderHtmlDocument(options) {
|
|
2113
|
+
const lang = options.lang ?? "en";
|
|
2114
|
+
const charset = options.charset ?? "utf-8";
|
|
2115
|
+
const viewport = options.viewport ?? "width=device-width, initial-scale=1";
|
|
2116
|
+
const htmlAttributes = renderAttributes({
|
|
2117
|
+
lang,
|
|
2118
|
+
...options.htmlAttributes ?? {}
|
|
2119
|
+
});
|
|
2120
|
+
const bodyAttributes = renderAttributes(options.bodyAttributes);
|
|
2121
|
+
const bodyHtml = renderDocumentBody(options.body);
|
|
2122
|
+
return [
|
|
2123
|
+
"<!doctype html>",
|
|
2124
|
+
`<html${htmlAttributes}>`,
|
|
2125
|
+
"<head>",
|
|
2126
|
+
` <meta charset="${escapeHtml(charset)}">`,
|
|
2127
|
+
` <meta name="viewport" content="${escapeHtml(viewport)}">`,
|
|
2128
|
+
` <title>${escapeHtml(options.title)}</title>`,
|
|
2129
|
+
options.description ? ` <meta name="description" content="${escapeHtml(options.description)}">` : "",
|
|
2130
|
+
...(options.head ?? []).map((element) => ` ${renderHeadElement(element)}`),
|
|
2131
|
+
options.styles && options.styles.length > 0 ? ` <style>${renderStyleSheet(options.styles)}</style>` : "",
|
|
2132
|
+
"</head>",
|
|
2133
|
+
`<body${bodyAttributes}>`,
|
|
2134
|
+
bodyHtml,
|
|
2135
|
+
...(options.scripts ?? []).map((script) => ` ${renderScript(script)}`),
|
|
2136
|
+
"</body>",
|
|
2137
|
+
"</html>"
|
|
2138
|
+
].filter((line) => line !== "").join(`
|
|
2139
|
+
`);
|
|
2140
|
+
}
|
|
2141
|
+
function renderDocumentBody(body) {
|
|
2142
|
+
if (typeof document === "undefined") {
|
|
2143
|
+
throw new Error("renderHtmlDocument requires a DOM-like document");
|
|
2144
|
+
}
|
|
2145
|
+
const container = document.createElement("div");
|
|
2146
|
+
const renderer = new RendererContext;
|
|
2147
|
+
const mountedComponents = new Set;
|
|
2148
|
+
const renderables = Array.isArray(body) ? body : [body];
|
|
2149
|
+
const context = {
|
|
2150
|
+
templateEngine: new TemplateEngine({}),
|
|
2151
|
+
renderer,
|
|
2152
|
+
slots: { default: [] },
|
|
2153
|
+
registerChild: (component2) => {
|
|
2154
|
+
mountedComponents.add(component2);
|
|
2155
|
+
},
|
|
2156
|
+
unregisterChild: (component2) => {
|
|
2157
|
+
mountedComponents.delete(component2);
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
renderables.forEach((renderable) => {
|
|
2161
|
+
container.appendChild(renderer.mount(renderable, context));
|
|
2162
|
+
});
|
|
2163
|
+
const html = container.innerHTML;
|
|
2164
|
+
mountedComponents.forEach((component2) => {
|
|
2165
|
+
component2.unmount();
|
|
2166
|
+
});
|
|
2167
|
+
return html;
|
|
2168
|
+
}
|
|
2169
|
+
function renderHeadElement(element) {
|
|
2170
|
+
const attributes = renderAttributes(element.attributes);
|
|
2171
|
+
if (VOID_HEAD_TAGS.has(element.tag) && !element.text) {
|
|
2172
|
+
return `<${element.tag}${attributes}>`;
|
|
2173
|
+
}
|
|
2174
|
+
return `<${element.tag}${attributes}>${escapeHtml(element.text ?? "")}</${element.tag}>`;
|
|
2175
|
+
}
|
|
2176
|
+
function renderScript(script) {
|
|
2177
|
+
const attributes = renderAttributes({
|
|
2178
|
+
type: script.type,
|
|
2179
|
+
src: script.src,
|
|
2180
|
+
async: script.async,
|
|
2181
|
+
defer: script.defer,
|
|
2182
|
+
...script.attributes ?? {}
|
|
2183
|
+
});
|
|
2184
|
+
return `<script${attributes}></script>`;
|
|
2185
|
+
}
|
|
2186
|
+
function renderAttributes(attributes = {}) {
|
|
2187
|
+
const rendered = Object.entries(attributes).flatMap(([name, value]) => {
|
|
2188
|
+
if (value === false || value === null || value === undefined) {
|
|
2189
|
+
return [];
|
|
2190
|
+
}
|
|
2191
|
+
return value === true ? [name] : [`${name}="${escapeHtml(String(value))}"`];
|
|
2192
|
+
}).join(" ");
|
|
2193
|
+
return rendered ? ` ${rendered}` : "";
|
|
2194
|
+
}
|
|
2195
|
+
function escapeHtml(value) {
|
|
2196
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// lib/core/app.ts
|
|
2200
|
+
var DEFAULT_ROOT_ELEMENT = "#app";
|
|
2201
|
+
|
|
2202
|
+
class OneApp {
|
|
2203
|
+
options;
|
|
2204
|
+
container = null;
|
|
2205
|
+
rootInstance = null;
|
|
2206
|
+
mounted = false;
|
|
2207
|
+
templateEngine = null;
|
|
2208
|
+
appContext;
|
|
2209
|
+
providers = new Map;
|
|
2210
|
+
plugins = [];
|
|
2211
|
+
unmountedCallback;
|
|
2212
|
+
router;
|
|
2213
|
+
constructor(options = {}) {
|
|
2214
|
+
this.options = options;
|
|
2215
|
+
this.appContext = {
|
|
2216
|
+
app: this,
|
|
2217
|
+
version: "0.2.1",
|
|
2218
|
+
config: options.config || {}
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
handleError(error) {
|
|
2222
|
+
console.error("应用错误:", error);
|
|
2223
|
+
this.renderErrorUI(error);
|
|
2224
|
+
}
|
|
2225
|
+
renderErrorUI(error) {
|
|
2226
|
+
if (!this.container) {
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
this.container.innerHTML = `
|
|
2230
|
+
<div style="padding: 20px; background-color: #ffebee; color: #c62828; font-family: Arial, sans-serif;">
|
|
2231
|
+
<h3>应用错误</h3>
|
|
2232
|
+
<p>${error.message}</p>
|
|
2233
|
+
<pre style="background-color: #fff; padding: 10px; border-radius: 4px; overflow: auto;">${error.stack}</pre>
|
|
2234
|
+
</div>
|
|
2235
|
+
`;
|
|
2236
|
+
}
|
|
2237
|
+
use(plugin, ...args) {
|
|
2238
|
+
if (typeof plugin.install !== "function") {
|
|
2239
|
+
throw new Error("插件必须提供 install 方法");
|
|
2240
|
+
}
|
|
2241
|
+
plugin.install(this, ...args);
|
|
2242
|
+
this.plugins.push({ plugin, args });
|
|
2243
|
+
return this;
|
|
2244
|
+
}
|
|
2245
|
+
mount() {
|
|
2246
|
+
if (this.mounted) {
|
|
2247
|
+
console.warn("应用已经处于运行状态");
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
const mountContainer = this.resolveMountContainer();
|
|
2251
|
+
if (!mountContainer) {
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
try {
|
|
2255
|
+
this.container = mountContainer;
|
|
2256
|
+
globalThis.__APP__ = this;
|
|
2257
|
+
if (this.options.root) {
|
|
2258
|
+
this.rootInstance = new this.options.root(this.options.rootProps);
|
|
2259
|
+
if ("setAppContext" in this.rootInstance) {
|
|
2260
|
+
this.rootInstance.setAppContext(this.appContext);
|
|
2261
|
+
}
|
|
2262
|
+
if (this.options.state && "setState" in this.rootInstance) {
|
|
2263
|
+
this.rootInstance.setState(this.options.state);
|
|
2264
|
+
}
|
|
2265
|
+
this.rootInstance.mount(this.container);
|
|
2266
|
+
this.templateEngine = new TemplateEngine(this.options.state || {});
|
|
2267
|
+
}
|
|
2268
|
+
this.mounted = true;
|
|
2269
|
+
this.onMounted();
|
|
2270
|
+
} catch (error) {
|
|
2271
|
+
this.handleError(error);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
unmount() {
|
|
2275
|
+
if (!this.mounted) {
|
|
2276
|
+
console.warn("应用未处于运行状态");
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
try {
|
|
2280
|
+
this.onBeforeUnmount();
|
|
2281
|
+
if (this.rootInstance) {
|
|
2282
|
+
if ("unmount" in this.rootInstance) {
|
|
2283
|
+
this.rootInstance.unmount();
|
|
2284
|
+
}
|
|
2285
|
+
this.rootInstance = null;
|
|
2286
|
+
this.mounted = false;
|
|
2287
|
+
delete globalThis.__APP__;
|
|
2288
|
+
}
|
|
2289
|
+
if (this.templateEngine) {
|
|
2290
|
+
this.templateEngine.clearBindings();
|
|
2291
|
+
this.templateEngine = null;
|
|
2292
|
+
}
|
|
2293
|
+
if (this.container) {
|
|
2294
|
+
this.container.innerHTML = "";
|
|
2295
|
+
}
|
|
2296
|
+
if (this.unmountedCallback) {
|
|
2297
|
+
this.unmountedCallback();
|
|
2298
|
+
}
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
console.error("Failed to unmount app:", error);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
isRunning() {
|
|
2304
|
+
return this.mounted;
|
|
2305
|
+
}
|
|
2306
|
+
updateRootComponent(component2) {
|
|
2307
|
+
if (this.mounted) {
|
|
2308
|
+
this.unmount();
|
|
2309
|
+
}
|
|
2310
|
+
this.options.root = component2;
|
|
2311
|
+
this.mount();
|
|
2312
|
+
}
|
|
2313
|
+
update(state) {
|
|
2314
|
+
if (!this.mounted) {
|
|
2315
|
+
console.warn("Cannot update unmounted app");
|
|
2316
|
+
return this;
|
|
2317
|
+
}
|
|
2318
|
+
try {
|
|
2319
|
+
if (state && this.options.state) {
|
|
2320
|
+
this.options.state = { ...this.options.state, ...state };
|
|
2321
|
+
if (this.rootInstance && "setState" in this.rootInstance) {
|
|
2322
|
+
this.rootInstance.setState(state);
|
|
2323
|
+
}
|
|
2324
|
+
if (this.templateEngine) {
|
|
2325
|
+
this.templateEngine.state = this.options.state;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
this.onUpdated();
|
|
2329
|
+
} catch (error) {
|
|
2330
|
+
console.error("Failed to update app:", error);
|
|
2331
|
+
}
|
|
2332
|
+
return this;
|
|
2333
|
+
}
|
|
2334
|
+
getContext() {
|
|
2335
|
+
return this.appContext;
|
|
2336
|
+
}
|
|
2337
|
+
provide(key, value) {
|
|
2338
|
+
this.providers.set(key, value);
|
|
2339
|
+
return this;
|
|
2340
|
+
}
|
|
2341
|
+
inject(key, fallback) {
|
|
2342
|
+
const result = this.resolveInjection(key);
|
|
2343
|
+
return result.found ? result.value : fallback;
|
|
2344
|
+
}
|
|
2345
|
+
resolveInjection(key) {
|
|
2346
|
+
if (!this.providers.has(key)) {
|
|
2347
|
+
return { found: false, value: undefined };
|
|
2348
|
+
}
|
|
2349
|
+
return { found: true, value: this.providers.get(key) };
|
|
2350
|
+
}
|
|
2351
|
+
getState() {
|
|
2352
|
+
return this.options.state;
|
|
2353
|
+
}
|
|
2354
|
+
setState(newState) {
|
|
2355
|
+
this.options.state = newState;
|
|
2356
|
+
if (this.mounted) {
|
|
2357
|
+
this.update();
|
|
2358
|
+
}
|
|
2359
|
+
return this;
|
|
2360
|
+
}
|
|
2361
|
+
onUnmounted(callback) {
|
|
2362
|
+
this.unmountedCallback = callback;
|
|
2363
|
+
return this;
|
|
2364
|
+
}
|
|
2365
|
+
renderHtmlDocument(options = {}) {
|
|
2366
|
+
const appDocument = this.options.document ?? {};
|
|
2367
|
+
const scripts = this.mergeDocumentScripts(appDocument.scripts, options.scripts);
|
|
2368
|
+
return renderHtmlDocument({
|
|
2369
|
+
...appDocument,
|
|
2370
|
+
...options,
|
|
2371
|
+
title: options.title ?? appDocument.title ?? "TSone App",
|
|
2372
|
+
body: options.body ?? appDocument.body ?? this.createMountDocumentBody(),
|
|
2373
|
+
scripts
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
resolveRootElement(selector) {
|
|
2377
|
+
if (!selector) {
|
|
2378
|
+
return null;
|
|
2379
|
+
}
|
|
2380
|
+
if (typeof selector === "string") {
|
|
2381
|
+
if (typeof document === "undefined") {
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
return document.querySelector(selector);
|
|
2385
|
+
}
|
|
2386
|
+
return typeof Element !== "undefined" && selector instanceof Element ? selector : null;
|
|
2387
|
+
}
|
|
2388
|
+
resolveMountContainer() {
|
|
2389
|
+
if (typeof document === "undefined") {
|
|
2390
|
+
return null;
|
|
2391
|
+
}
|
|
2392
|
+
const rootElement = this.resolveRootElement(this.options.rootElement ?? DEFAULT_ROOT_ELEMENT);
|
|
2393
|
+
return rootElement instanceof HTMLElement ? rootElement : null;
|
|
2394
|
+
}
|
|
2395
|
+
createMountDocumentBody() {
|
|
2396
|
+
const rootElement = this.options.rootElement ?? DEFAULT_ROOT_ELEMENT;
|
|
2397
|
+
if (typeof rootElement === "string") {
|
|
2398
|
+
return this.createMountElementFromSelector(rootElement);
|
|
2399
|
+
}
|
|
2400
|
+
if (typeof Element !== "undefined" && rootElement instanceof Element) {
|
|
2401
|
+
const props = {};
|
|
2402
|
+
if (rootElement.id) {
|
|
2403
|
+
props.id = rootElement.id;
|
|
2404
|
+
}
|
|
2405
|
+
if (rootElement.className) {
|
|
2406
|
+
props.className = rootElement.className;
|
|
2407
|
+
}
|
|
2408
|
+
return { tag: rootElement.tagName.toLowerCase(), props };
|
|
2409
|
+
}
|
|
2410
|
+
return Div({ props: { id: "app" } });
|
|
2411
|
+
}
|
|
2412
|
+
createMountElementFromSelector(selector) {
|
|
2413
|
+
if (selector.startsWith("#") && selector.length > 1) {
|
|
2414
|
+
return Div({ props: { id: selector.slice(1) } });
|
|
2415
|
+
}
|
|
2416
|
+
if (selector.startsWith(".") && selector.length > 1) {
|
|
2417
|
+
return Div({ props: { className: selector.slice(1) } });
|
|
2418
|
+
}
|
|
2419
|
+
return Div({ props: { "data-tsone-root": selector } });
|
|
2420
|
+
}
|
|
2421
|
+
mergeDocumentScripts(baseScripts, extraScripts) {
|
|
2422
|
+
if (!baseScripts && !extraScripts) {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
return [...baseScripts ?? [], ...extraScripts ?? []];
|
|
2426
|
+
}
|
|
2427
|
+
onMounted() {
|
|
2428
|
+
this.plugins.forEach(({ plugin: pluginObj }) => {
|
|
2429
|
+
if (pluginObj && typeof pluginObj.onMounted === "function") {
|
|
2430
|
+
pluginObj.onMounted(this);
|
|
2431
|
+
}
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
onUpdated() {
|
|
2435
|
+
this.plugins.forEach(({ plugin: pluginObj }) => {
|
|
2436
|
+
if (pluginObj && typeof pluginObj.onUpdated === "function") {
|
|
2437
|
+
pluginObj.onUpdated(this);
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
onBeforeUnmount() {
|
|
2442
|
+
this.plugins.forEach(({ plugin: pluginObj }) => {
|
|
2443
|
+
if (pluginObj && typeof pluginObj.onBeforeUnmount === "function") {
|
|
2444
|
+
pluginObj.onBeforeUnmount(this);
|
|
2445
|
+
}
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
export { ReactiveSystem, reactive, readonly, effect, computed, ref, isRef, unref, stop, isReactive, isReadonly, isComponentNode, isHTMLNode, isSlotProvider, h, Tag, Div, Span, P, Button, Input, Section, Main, Header, Footer, Nav, Article, Aside, H1, H2, H3, H4, H5, H6, Strong, Em, Small, Pre, Code, Blockquote, Ul, Ol, Li, A, Img, Form, Label, Textarea, Select, Option, Table, Thead, Tbody, Tr, Th, Td, createComponent, slot, each, modelPath, getModelValue, setModelValue, ModelBindingController, ElementRenderStrategy, normalizeTransitionGroupProps, validateTransitionGroupChildren, RendererContext, TextRenderStrategy, ComponentRenderStrategy, SlotRenderStrategy, TemplateEngine, Component, renderHtmlDocument, OneApp, useRouter, Router, RouterLink, RouterView, createRouter };
|
|
1816
2451
|
|
|
1817
|
-
//# debugId=
|
|
1818
|
-
//# sourceMappingURL=index-
|
|
2452
|
+
//# debugId=ABE10854C25EBD6364756E2164756E21
|
|
2453
|
+
//# sourceMappingURL=index-1x4ectvr.js.map
|