@wsxjs/wsx-core 0.0.22 → 0.0.24
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/{chunk-OXFZ575O.mjs → chunk-5Q2VEEUH.mjs} +352 -186
- package/dist/index.js +418 -215
- package/dist/index.mjs +67 -30
- package/dist/jsx-runtime.js +352 -186
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +352 -186
- package/dist/jsx.mjs +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +2 -2
- package/src/base-component.ts +27 -0
- package/src/light-component.ts +20 -8
- package/src/render-context.ts +4 -0
- package/src/utils/cache-key.ts +27 -21
- package/src/utils/element-creation.ts +5 -0
- package/src/utils/element-update.ts +192 -309
- package/src/utils/update-children-helpers.ts +508 -0
- package/src/web-component.ts +72 -41
package/dist/index.js
CHANGED
|
@@ -112,6 +112,7 @@ var _RenderContext = class _RenderContext {
|
|
|
112
112
|
* @param fn The function to execute (usually the render method).
|
|
113
113
|
*/
|
|
114
114
|
static runInContext(component, fn) {
|
|
115
|
+
resetCounterForNewRenderCycle(component);
|
|
115
116
|
const prev = _RenderContext.current;
|
|
116
117
|
_RenderContext.current = component;
|
|
117
118
|
try {
|
|
@@ -137,21 +138,20 @@ _RenderContext.current = null;
|
|
|
137
138
|
var RenderContext = _RenderContext;
|
|
138
139
|
|
|
139
140
|
// src/utils/cache-key.ts
|
|
140
|
-
var POSITION_ID_KEY = "__wsxPositionId";
|
|
141
141
|
var INDEX_KEY = "__wsxIndex";
|
|
142
142
|
var componentElementCounters = /* @__PURE__ */ new WeakMap();
|
|
143
143
|
var componentIdCache = /* @__PURE__ */ new WeakMap();
|
|
144
144
|
function generateCacheKey(tag, props, componentId, component) {
|
|
145
|
-
const positionId = props?.[POSITION_ID_KEY];
|
|
146
145
|
const userKey = props?.key;
|
|
147
146
|
const index = props?.[INDEX_KEY];
|
|
147
|
+
const positionId = props?.__wsxPositionId;
|
|
148
148
|
if (userKey !== void 0 && userKey !== null) {
|
|
149
149
|
return `${componentId}:${tag}:key-${String(userKey)}`;
|
|
150
150
|
}
|
|
151
151
|
if (index !== void 0 && index !== null) {
|
|
152
152
|
return `${componentId}:${tag}:idx-${String(index)}`;
|
|
153
153
|
}
|
|
154
|
-
if (positionId !== void 0 && positionId !== null
|
|
154
|
+
if (positionId !== void 0 && positionId !== null) {
|
|
155
155
|
return `${componentId}:${tag}:${String(positionId)}`;
|
|
156
156
|
}
|
|
157
157
|
if (component) {
|
|
@@ -162,6 +162,9 @@ function generateCacheKey(tag, props, componentId, component) {
|
|
|
162
162
|
}
|
|
163
163
|
return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
|
|
164
164
|
}
|
|
165
|
+
function resetCounterForNewRenderCycle(component) {
|
|
166
|
+
componentElementCounters.set(component, 0);
|
|
167
|
+
}
|
|
165
168
|
function getComponentId() {
|
|
166
169
|
const component = RenderContext.getCurrentComponent();
|
|
167
170
|
if (component) {
|
|
@@ -579,7 +582,9 @@ function applySingleProp(element, key, value, tag, isSVG) {
|
|
|
579
582
|
}
|
|
580
583
|
if (key.startsWith("on") && typeof value === "function") {
|
|
581
584
|
const eventName = key.slice(2).toLowerCase();
|
|
585
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
582
586
|
element.addEventListener(eventName, value);
|
|
587
|
+
element[listenerKey] = value;
|
|
583
588
|
return;
|
|
584
589
|
}
|
|
585
590
|
if (typeof value === "boolean") {
|
|
@@ -633,10 +638,269 @@ function createElementWithPropsAndChildren(tag, props, children) {
|
|
|
633
638
|
return element;
|
|
634
639
|
}
|
|
635
640
|
|
|
641
|
+
// src/utils/update-children-helpers.ts
|
|
642
|
+
function collectPreservedElements(element) {
|
|
643
|
+
const preserved = [];
|
|
644
|
+
for (let i = 0; i < element.childNodes.length; i++) {
|
|
645
|
+
const child = element.childNodes[i];
|
|
646
|
+
if (shouldPreserveElement(child)) {
|
|
647
|
+
preserved.push(child);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return preserved;
|
|
651
|
+
}
|
|
652
|
+
function findDOMNodeByReference(oldChild, parent) {
|
|
653
|
+
if (oldChild.parentNode === parent && !shouldPreserveElement(oldChild)) {
|
|
654
|
+
return oldChild;
|
|
655
|
+
}
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
function findDOMNodeByCacheKey(cacheKey, parent) {
|
|
659
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
660
|
+
const child = parent.childNodes[i];
|
|
661
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
662
|
+
if (shouldPreserveElement(child)) continue;
|
|
663
|
+
if (getElementCacheKey(child) === cacheKey) {
|
|
664
|
+
return child;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
function findElementNode(oldChild, parent) {
|
|
671
|
+
const byRef = findDOMNodeByReference(oldChild, parent);
|
|
672
|
+
if (byRef) return byRef;
|
|
673
|
+
const cacheKey = getElementCacheKey(oldChild);
|
|
674
|
+
if (cacheKey) {
|
|
675
|
+
return findDOMNodeByCacheKey(cacheKey, parent);
|
|
676
|
+
}
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
function findTextNode(parent, domIndex) {
|
|
680
|
+
while (domIndex.value < parent.childNodes.length) {
|
|
681
|
+
const node = parent.childNodes[domIndex.value];
|
|
682
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent) {
|
|
683
|
+
const textNode = node;
|
|
684
|
+
domIndex.value++;
|
|
685
|
+
return textNode;
|
|
686
|
+
}
|
|
687
|
+
domIndex.value++;
|
|
688
|
+
}
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
function updateOrCreateTextNode(parent, oldNode, newText) {
|
|
692
|
+
if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
|
|
693
|
+
if (oldNode.textContent !== newText) {
|
|
694
|
+
oldNode.textContent = newText;
|
|
695
|
+
}
|
|
696
|
+
return oldNode;
|
|
697
|
+
} else {
|
|
698
|
+
if (!oldNode) {
|
|
699
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
700
|
+
const node = parent.childNodes[i];
|
|
701
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent && node.textContent === newText) {
|
|
702
|
+
return node;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
const newTextNode = document.createTextNode(newText);
|
|
707
|
+
if (oldNode && !shouldPreserveElement(oldNode)) {
|
|
708
|
+
parent.replaceChild(newTextNode, oldNode);
|
|
709
|
+
} else {
|
|
710
|
+
parent.insertBefore(newTextNode, oldNode || null);
|
|
711
|
+
}
|
|
712
|
+
return newTextNode;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function removeNodeIfNotPreserved(parent, node) {
|
|
716
|
+
if (node && !shouldPreserveElement(node) && node.parentNode === parent) {
|
|
717
|
+
parent.removeChild(node);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function replaceOrInsertElement(parent, newChild, oldNode) {
|
|
721
|
+
const targetNextSibling = oldNode && shouldPreserveElement(oldNode) ? oldNode : oldNode?.nextSibling || null;
|
|
722
|
+
replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling);
|
|
723
|
+
}
|
|
724
|
+
function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling) {
|
|
725
|
+
if (newChild.parentNode && newChild.parentNode !== parent) {
|
|
726
|
+
newChild.parentNode.removeChild(newChild);
|
|
727
|
+
}
|
|
728
|
+
const isInCorrectPosition = newChild.parentNode === parent && newChild.nextSibling === targetNextSibling;
|
|
729
|
+
if (isInCorrectPosition) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (newChild.parentNode === parent) {
|
|
733
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
|
|
737
|
+
if (oldNode !== newChild) {
|
|
738
|
+
parent.replaceChild(newChild, oldNode);
|
|
739
|
+
}
|
|
740
|
+
} else {
|
|
741
|
+
if (newChild.parentNode === parent) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const newChildCacheKey = getElementCacheKey(newChild);
|
|
745
|
+
if (!newChildCacheKey) {
|
|
746
|
+
const newChildContent = newChild.textContent || "";
|
|
747
|
+
const newChildTag = newChild.tagName.toLowerCase();
|
|
748
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
749
|
+
const existingNode = parent.childNodes[i];
|
|
750
|
+
if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
|
|
751
|
+
const existingCacheKey = getElementCacheKey(existingNode);
|
|
752
|
+
if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function appendNewChild(parent, child, processedNodes) {
|
|
762
|
+
if (child === null || child === void 0 || child === false) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
766
|
+
const newTextNode = document.createTextNode(String(child));
|
|
767
|
+
parent.appendChild(newTextNode);
|
|
768
|
+
if (processedNodes) {
|
|
769
|
+
processedNodes.add(newTextNode);
|
|
770
|
+
}
|
|
771
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
772
|
+
if (child.parentNode === parent) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (child.parentNode && child.parentNode !== parent) {
|
|
776
|
+
child.parentNode.removeChild(child);
|
|
777
|
+
}
|
|
778
|
+
parent.appendChild(child);
|
|
779
|
+
if (processedNodes) {
|
|
780
|
+
processedNodes.add(child);
|
|
781
|
+
}
|
|
782
|
+
} else if (child instanceof DocumentFragment) {
|
|
783
|
+
parent.appendChild(child);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function buildNewChildrenMaps(flatNew) {
|
|
787
|
+
const elementSet = /* @__PURE__ */ new Set();
|
|
788
|
+
const cacheKeyMap = /* @__PURE__ */ new Map();
|
|
789
|
+
for (const child of flatNew) {
|
|
790
|
+
if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
|
|
791
|
+
elementSet.add(child);
|
|
792
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
793
|
+
const cacheKey = getElementCacheKey(child);
|
|
794
|
+
if (cacheKey) {
|
|
795
|
+
cacheKeyMap.set(cacheKey, child);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return { elementSet, cacheKeyMap };
|
|
801
|
+
}
|
|
802
|
+
function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
|
|
803
|
+
if (shouldPreserveElement(node)) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (node.nodeType === Node.TEXT_NODE && processedNodes && processedNodes.has(node)) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
if (node.nodeType === Node.TEXT_NODE && processedNodes) {
|
|
810
|
+
let parent = node.parentNode;
|
|
811
|
+
while (parent) {
|
|
812
|
+
if (processedNodes.has(parent) && parent.parentNode) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
parent = parent.parentNode;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
|
|
819
|
+
if (elementSet.has(node)) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
823
|
+
const cacheKey = getElementCacheKey(node);
|
|
824
|
+
if (cacheKey && cacheKeyMap.has(cacheKey)) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return true;
|
|
830
|
+
}
|
|
831
|
+
function deduplicateCacheKeys(parent, cacheKeyMap) {
|
|
832
|
+
const processedCacheKeys = /* @__PURE__ */ new Set();
|
|
833
|
+
for (let i = parent.childNodes.length - 1; i >= 0; i--) {
|
|
834
|
+
const child = parent.childNodes[i];
|
|
835
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
836
|
+
if (shouldPreserveElement(child)) {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const cacheKey = getElementCacheKey(child);
|
|
840
|
+
if (cacheKey && cacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
|
|
841
|
+
processedCacheKeys.add(cacheKey);
|
|
842
|
+
const newChild = cacheKeyMap.get(cacheKey);
|
|
843
|
+
if (child !== newChild) {
|
|
844
|
+
parent.replaceChild(newChild, child);
|
|
845
|
+
}
|
|
846
|
+
} else if (cacheKey && cacheKeyMap.has(cacheKey) && processedCacheKeys.has(cacheKey)) {
|
|
847
|
+
const newChild = cacheKeyMap.get(cacheKey);
|
|
848
|
+
if (child !== newChild) {
|
|
849
|
+
parent.removeChild(child);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
function collectNodesToRemove(parent, elementSet, cacheKeyMap, processedNodes) {
|
|
856
|
+
const nodesToRemove = [];
|
|
857
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
858
|
+
const node = parent.childNodes[i];
|
|
859
|
+
if (shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes)) {
|
|
860
|
+
nodesToRemove.push(node);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return nodesToRemove;
|
|
864
|
+
}
|
|
865
|
+
function removeNodes(parent, nodes, cacheManager) {
|
|
866
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
867
|
+
const node = nodes[i];
|
|
868
|
+
if (node.parentNode === parent) {
|
|
869
|
+
if (cacheManager && (node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
870
|
+
const metadata = cacheManager.getMetadata(node);
|
|
871
|
+
const refCallback = metadata?.ref;
|
|
872
|
+
if (typeof refCallback === "function") {
|
|
873
|
+
try {
|
|
874
|
+
refCallback(null);
|
|
875
|
+
} catch {
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
parent.removeChild(node);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function reinsertPreservedElements(parent, preservedElements) {
|
|
884
|
+
for (const element of preservedElements) {
|
|
885
|
+
if (element.parentNode !== parent) {
|
|
886
|
+
parent.appendChild(element);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
function flattenChildrenSafe(children) {
|
|
891
|
+
return flattenChildren(children);
|
|
892
|
+
}
|
|
893
|
+
|
|
636
894
|
// src/utils/element-update.ts
|
|
637
895
|
function removeProp(element, key, oldValue, tag) {
|
|
638
896
|
const isSVG = shouldUseSVGNamespace(tag);
|
|
639
897
|
if (key === "ref") {
|
|
898
|
+
if (typeof oldValue === "function") {
|
|
899
|
+
try {
|
|
900
|
+
oldValue(null);
|
|
901
|
+
} catch {
|
|
902
|
+
}
|
|
903
|
+
}
|
|
640
904
|
return;
|
|
641
905
|
}
|
|
642
906
|
if (key === "className" || key === "class") {
|
|
@@ -652,6 +916,13 @@ function removeProp(element, key, oldValue, tag) {
|
|
|
652
916
|
return;
|
|
653
917
|
}
|
|
654
918
|
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
919
|
+
const eventName = key.slice(2).toLowerCase();
|
|
920
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
921
|
+
const savedListener = element[listenerKey];
|
|
922
|
+
if (savedListener) {
|
|
923
|
+
element.removeEventListener(eventName, savedListener);
|
|
924
|
+
delete element[listenerKey];
|
|
925
|
+
}
|
|
655
926
|
return;
|
|
656
927
|
}
|
|
657
928
|
if (key === "value") {
|
|
@@ -695,7 +966,13 @@ function applySingleProp2(element, key, value, tag, isSVG) {
|
|
|
695
966
|
}
|
|
696
967
|
if (key.startsWith("on") && typeof value === "function") {
|
|
697
968
|
const eventName = key.slice(2).toLowerCase();
|
|
969
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
970
|
+
const oldListener = element[listenerKey];
|
|
971
|
+
if (oldListener) {
|
|
972
|
+
element.removeEventListener(eventName, oldListener);
|
|
973
|
+
}
|
|
698
974
|
element.addEventListener(eventName, value);
|
|
975
|
+
element[listenerKey] = value;
|
|
699
976
|
return;
|
|
700
977
|
}
|
|
701
978
|
if (typeof value === "boolean") {
|
|
@@ -750,49 +1027,36 @@ function updateProps(element, oldProps, newProps, tag) {
|
|
|
750
1027
|
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
751
1028
|
}
|
|
752
1029
|
}
|
|
753
|
-
function updateChildren(element, oldChildren, newChildren) {
|
|
754
|
-
const flatOld =
|
|
755
|
-
const flatNew =
|
|
1030
|
+
function updateChildren(element, oldChildren, newChildren, _cacheManager) {
|
|
1031
|
+
const flatOld = flattenChildrenSafe(oldChildren);
|
|
1032
|
+
const flatNew = flattenChildrenSafe(newChildren);
|
|
1033
|
+
const preservedElements = collectPreservedElements(element);
|
|
756
1034
|
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
757
|
-
|
|
1035
|
+
const domIndex = { value: 0 };
|
|
1036
|
+
const processedNodes = /* @__PURE__ */ new Set();
|
|
758
1037
|
for (let i = 0; i < minLength; i++) {
|
|
759
1038
|
const oldChild = flatOld[i];
|
|
760
1039
|
const newChild = flatNew[i];
|
|
761
1040
|
let oldNode = null;
|
|
762
1041
|
if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
const oldCacheKey = getElementCacheKey(oldChild);
|
|
769
|
-
if (oldCacheKey) {
|
|
770
|
-
for (let j = 0; j < element.childNodes.length; j++) {
|
|
771
|
-
const domChild = element.childNodes[j];
|
|
772
|
-
if (domChild instanceof HTMLElement || domChild instanceof SVGElement) {
|
|
773
|
-
if (shouldPreserveElement(domChild)) {
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
const domCacheKey = getElementCacheKey(domChild);
|
|
777
|
-
if (domCacheKey === oldCacheKey) {
|
|
778
|
-
oldNode = domChild;
|
|
779
|
-
break;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
1042
|
+
oldNode = findElementNode(oldChild, element);
|
|
1043
|
+
if (oldNode && oldNode.parentNode === element) {
|
|
1044
|
+
const nodeIndex = Array.from(element.childNodes).indexOf(oldNode);
|
|
1045
|
+
if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
|
|
1046
|
+
domIndex.value = nodeIndex + 1;
|
|
783
1047
|
}
|
|
784
1048
|
}
|
|
785
1049
|
} else if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1050
|
+
oldNode = findTextNode(element, domIndex);
|
|
1051
|
+
if (!oldNode && element.childNodes.length > 0) {
|
|
1052
|
+
const oldText = String(oldChild);
|
|
1053
|
+
for (let j = domIndex.value; j < element.childNodes.length; j++) {
|
|
1054
|
+
const node = element.childNodes[j];
|
|
1055
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === element && node.textContent === oldText) {
|
|
1056
|
+
oldNode = node;
|
|
1057
|
+
domIndex.value = j + 1;
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
796
1060
|
}
|
|
797
1061
|
}
|
|
798
1062
|
}
|
|
@@ -801,27 +1065,20 @@ function updateChildren(element, oldChildren, newChildren) {
|
|
|
801
1065
|
const oldText = String(oldChild);
|
|
802
1066
|
const newText = String(newChild);
|
|
803
1067
|
const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
|
|
804
|
-
if (
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1068
|
+
if (needsUpdate) {
|
|
1069
|
+
const updatedNode = updateOrCreateTextNode(element, oldNode, newText);
|
|
1070
|
+
if (updatedNode && !processedNodes.has(updatedNode)) {
|
|
1071
|
+
processedNodes.add(updatedNode);
|
|
1072
|
+
}
|
|
809
1073
|
} else {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
element.replaceChild(newTextNode, oldNode);
|
|
813
|
-
} else {
|
|
814
|
-
element.insertBefore(newTextNode, oldNode || null);
|
|
1074
|
+
if (oldNode && oldNode.parentNode === element) {
|
|
1075
|
+
processedNodes.add(oldNode);
|
|
815
1076
|
}
|
|
816
1077
|
}
|
|
817
1078
|
} else {
|
|
818
|
-
|
|
819
|
-
element.removeChild(oldNode);
|
|
820
|
-
}
|
|
1079
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
821
1080
|
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
822
|
-
|
|
823
|
-
element.insertBefore(newChild, oldNode || null);
|
|
824
|
-
}
|
|
1081
|
+
replaceOrInsertElement(element, newChild, oldNode);
|
|
825
1082
|
} else if (newChild instanceof DocumentFragment) {
|
|
826
1083
|
element.insertBefore(newChild, oldNode || null);
|
|
827
1084
|
}
|
|
@@ -830,54 +1087,48 @@ function updateChildren(element, oldChildren, newChildren) {
|
|
|
830
1087
|
if (oldNode && shouldPreserveElement(oldNode)) {
|
|
831
1088
|
continue;
|
|
832
1089
|
}
|
|
833
|
-
if (newChild
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (hasSameCacheKey) {
|
|
844
|
-
if (newChild !== oldNode) {
|
|
845
|
-
element.replaceChild(newChild, oldNode);
|
|
846
|
-
}
|
|
847
|
-
} else {
|
|
848
|
-
element.removeChild(newChild);
|
|
849
|
-
element.replaceChild(newChild, oldNode);
|
|
850
|
-
}
|
|
851
|
-
} else if (newChild.parentNode) {
|
|
852
|
-
newChild.parentNode.removeChild(newChild);
|
|
853
|
-
element.replaceChild(newChild, oldNode);
|
|
854
|
-
} else {
|
|
855
|
-
element.replaceChild(newChild, oldNode);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
} else {
|
|
859
|
-
if (newChild.parentNode !== element) {
|
|
860
|
-
if (newChild.parentNode) {
|
|
861
|
-
newChild.parentNode.removeChild(newChild);
|
|
862
|
-
}
|
|
863
|
-
element.insertBefore(newChild, oldNode.nextSibling);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
} else {
|
|
867
|
-
if (newChild.parentNode !== element) {
|
|
868
|
-
if (newChild.parentNode) {
|
|
869
|
-
newChild.parentNode.removeChild(newChild);
|
|
1090
|
+
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1091
|
+
let targetNextSibling = null;
|
|
1092
|
+
let foundPreviousElement = false;
|
|
1093
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1094
|
+
const prevChild = flatNew[j];
|
|
1095
|
+
if (prevChild instanceof HTMLElement || prevChild instanceof SVGElement) {
|
|
1096
|
+
if (prevChild.parentNode === element) {
|
|
1097
|
+
targetNextSibling = prevChild.nextSibling;
|
|
1098
|
+
foundPreviousElement = true;
|
|
1099
|
+
break;
|
|
870
1100
|
}
|
|
871
|
-
element.appendChild(newChild);
|
|
872
1101
|
}
|
|
873
1102
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1103
|
+
if (!foundPreviousElement) {
|
|
1104
|
+
const firstChild = Array.from(element.childNodes).find(
|
|
1105
|
+
(node) => !shouldPreserveElement(node) && !processedNodes.has(node)
|
|
1106
|
+
);
|
|
1107
|
+
targetNextSibling = firstChild || null;
|
|
1108
|
+
}
|
|
1109
|
+
const isInCorrectPosition = newChild.parentNode === element && newChild.nextSibling === targetNextSibling;
|
|
1110
|
+
if (newChild === oldChild && isInCorrectPosition) {
|
|
1111
|
+
if (oldNode) processedNodes.add(oldNode);
|
|
1112
|
+
processedNodes.add(newChild);
|
|
1113
|
+
continue;
|
|
877
1114
|
}
|
|
1115
|
+
const referenceNode = oldNode && oldNode.parentNode === element ? oldNode : null;
|
|
1116
|
+
replaceOrInsertElementAtPosition(
|
|
1117
|
+
element,
|
|
1118
|
+
newChild,
|
|
1119
|
+
referenceNode,
|
|
1120
|
+
targetNextSibling
|
|
1121
|
+
);
|
|
1122
|
+
if (oldNode && oldNode !== newChild) {
|
|
1123
|
+
processedNodes.delete(oldNode);
|
|
1124
|
+
}
|
|
1125
|
+
processedNodes.add(newChild);
|
|
1126
|
+
} else {
|
|
1127
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
878
1128
|
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
879
1129
|
const newTextNode = document.createTextNode(String(newChild));
|
|
880
1130
|
element.insertBefore(newTextNode, oldNode?.nextSibling || null);
|
|
1131
|
+
processedNodes.add(newTextNode);
|
|
881
1132
|
} else if (newChild instanceof DocumentFragment) {
|
|
882
1133
|
element.insertBefore(newChild, oldNode?.nextSibling || null);
|
|
883
1134
|
}
|
|
@@ -885,98 +1136,13 @@ function updateChildren(element, oldChildren, newChildren) {
|
|
|
885
1136
|
}
|
|
886
1137
|
}
|
|
887
1138
|
for (let i = minLength; i < flatNew.length; i++) {
|
|
888
|
-
|
|
889
|
-
if (newChild === null || newChild === void 0 || newChild === false) {
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
893
|
-
element.appendChild(document.createTextNode(String(newChild)));
|
|
894
|
-
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
895
|
-
if (newChild.parentNode === element) {
|
|
896
|
-
const currentIndex = Array.from(element.childNodes).indexOf(newChild);
|
|
897
|
-
const expectedIndex = element.childNodes.length - 1;
|
|
898
|
-
if (currentIndex !== expectedIndex) {
|
|
899
|
-
element.removeChild(newChild);
|
|
900
|
-
element.appendChild(newChild);
|
|
901
|
-
}
|
|
902
|
-
continue;
|
|
903
|
-
} else if (newChild.parentNode) {
|
|
904
|
-
newChild.parentNode.removeChild(newChild);
|
|
905
|
-
}
|
|
906
|
-
element.appendChild(newChild);
|
|
907
|
-
} else if (newChild instanceof DocumentFragment) {
|
|
908
|
-
element.appendChild(newChild);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
const nodesToRemove = [];
|
|
912
|
-
const newChildSet = /* @__PURE__ */ new Set();
|
|
913
|
-
const newChildCacheKeyMap = /* @__PURE__ */ new Map();
|
|
914
|
-
for (const child of flatNew) {
|
|
915
|
-
if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
|
|
916
|
-
newChildSet.add(child);
|
|
917
|
-
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
918
|
-
const cacheKey = getElementCacheKey(child);
|
|
919
|
-
if (cacheKey) {
|
|
920
|
-
newChildCacheKeyMap.set(cacheKey, child);
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
const processedCacheKeys = /* @__PURE__ */ new Set();
|
|
926
|
-
const newChildToIndexMap = /* @__PURE__ */ new Map();
|
|
927
|
-
for (let i = 0; i < flatNew.length; i++) {
|
|
928
|
-
const child = flatNew[i];
|
|
929
|
-
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
930
|
-
newChildToIndexMap.set(child, i);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
for (let i = element.childNodes.length - 1; i >= 0; i--) {
|
|
934
|
-
const child = element.childNodes[i];
|
|
935
|
-
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
936
|
-
if (shouldPreserveElement(child)) {
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
const cacheKey = getElementCacheKey(child);
|
|
940
|
-
if (cacheKey && newChildCacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
|
|
941
|
-
processedCacheKeys.add(cacheKey);
|
|
942
|
-
const newChild = newChildCacheKeyMap.get(cacheKey);
|
|
943
|
-
if (child !== newChild) {
|
|
944
|
-
if (newChild.parentNode === element) {
|
|
945
|
-
element.replaceChild(newChild, child);
|
|
946
|
-
} else {
|
|
947
|
-
element.replaceChild(newChild, child);
|
|
948
|
-
}
|
|
949
|
-
} else {
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
for (let i = 0; i < element.childNodes.length; i++) {
|
|
955
|
-
const child = element.childNodes[i];
|
|
956
|
-
if (shouldPreserveElement(child)) {
|
|
957
|
-
continue;
|
|
958
|
-
}
|
|
959
|
-
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
960
|
-
if (newChildSet.has(child)) {
|
|
961
|
-
continue;
|
|
962
|
-
}
|
|
963
|
-
const cacheKey = getElementCacheKey(child);
|
|
964
|
-
if (cacheKey && newChildCacheKeyMap.has(cacheKey)) {
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
} else if (child instanceof DocumentFragment) {
|
|
968
|
-
if (newChildSet.has(child)) {
|
|
969
|
-
continue;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
nodesToRemove.push(child);
|
|
973
|
-
}
|
|
974
|
-
for (let i = nodesToRemove.length - 1; i >= 0; i--) {
|
|
975
|
-
const node = nodesToRemove[i];
|
|
976
|
-
if (node.parentNode === element) {
|
|
977
|
-
element.removeChild(node);
|
|
978
|
-
}
|
|
1139
|
+
appendNewChild(element, flatNew[i], processedNodes);
|
|
979
1140
|
}
|
|
1141
|
+
const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
|
|
1142
|
+
deduplicateCacheKeys(element, cacheKeyMap);
|
|
1143
|
+
const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap, processedNodes);
|
|
1144
|
+
removeNodes(element, nodesToRemove, _cacheManager);
|
|
1145
|
+
reinsertPreservedElements(element, preservedElements);
|
|
980
1146
|
}
|
|
981
1147
|
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
982
1148
|
const oldMetadata = cacheManager.getMetadata(element);
|
|
@@ -987,7 +1153,7 @@ function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
|
987
1153
|
children: newChildren
|
|
988
1154
|
});
|
|
989
1155
|
updateProps(element, oldProps, newProps, tag);
|
|
990
|
-
updateChildren(element, oldChildren, newChildren);
|
|
1156
|
+
updateChildren(element, oldChildren, newChildren, cacheManager);
|
|
991
1157
|
}
|
|
992
1158
|
|
|
993
1159
|
// src/jsx-factory.ts
|
|
@@ -1491,6 +1657,12 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1491
1657
|
* @internal
|
|
1492
1658
|
*/
|
|
1493
1659
|
this._isRendering = false;
|
|
1660
|
+
/**
|
|
1661
|
+
* 已调度渲染标志(防止在同一事件循环中重复注册 requestAnimationFrame)
|
|
1662
|
+
* 用于批量更新:同一事件循环中的多个状态变化只触发一次渲染
|
|
1663
|
+
* @internal
|
|
1664
|
+
*/
|
|
1665
|
+
this._hasScheduledRender = false;
|
|
1494
1666
|
/**
|
|
1495
1667
|
* 处理 blur 事件,在用户停止输入时执行待处理的重渲染
|
|
1496
1668
|
* @internal
|
|
@@ -1597,6 +1769,9 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1597
1769
|
if (this._isRendering) {
|
|
1598
1770
|
return;
|
|
1599
1771
|
}
|
|
1772
|
+
if (this._hasScheduledRender) {
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1600
1775
|
const root = this.getActiveRoot();
|
|
1601
1776
|
let activeElement = null;
|
|
1602
1777
|
if (root instanceof ShadowRoot) {
|
|
@@ -1622,8 +1797,16 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1622
1797
|
if (this._pendingRerender) {
|
|
1623
1798
|
this._pendingRerender = false;
|
|
1624
1799
|
}
|
|
1800
|
+
this._hasScheduledRender = true;
|
|
1625
1801
|
requestAnimationFrame(() => {
|
|
1802
|
+
console.warn("[scheduleRerender] RAF callback:", {
|
|
1803
|
+
component: this.constructor.name,
|
|
1804
|
+
connected: this.connected,
|
|
1805
|
+
isRendering: this._isRendering
|
|
1806
|
+
});
|
|
1807
|
+
this._hasScheduledRender = false;
|
|
1626
1808
|
if (this.connected && !this._isRendering) {
|
|
1809
|
+
console.warn("[scheduleRerender] calling _rerender()");
|
|
1627
1810
|
this._isRendering = true;
|
|
1628
1811
|
this._rerender();
|
|
1629
1812
|
} else if (!this.connected) {
|
|
@@ -1880,6 +2063,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
1880
2063
|
(child) => child instanceof HTMLElement && child.style.color === "red" && child.textContent?.includes("Component Error")
|
|
1881
2064
|
);
|
|
1882
2065
|
const hasActualContent = allChildren.length > styleElements.length + slotElements.length;
|
|
2066
|
+
this.onConnected?.();
|
|
1883
2067
|
if (hasActualContent && !hasErrorElement) {
|
|
1884
2068
|
} else {
|
|
1885
2069
|
this.shadowRoot.innerHTML = "";
|
|
@@ -1891,7 +2075,6 @@ var WebComponent = class extends BaseComponent {
|
|
|
1891
2075
|
this.shadowRoot.appendChild(content);
|
|
1892
2076
|
}
|
|
1893
2077
|
this.initializeEventListeners();
|
|
1894
|
-
this.onConnected?.();
|
|
1895
2078
|
if (hasActualContent === false || hasErrorElement) {
|
|
1896
2079
|
requestAnimationFrame(() => {
|
|
1897
2080
|
this.onRendered?.();
|
|
@@ -1943,8 +2126,12 @@ var WebComponent = class extends BaseComponent {
|
|
|
1943
2126
|
const focusState = this.captureFocusState();
|
|
1944
2127
|
this._pendingFocusState = focusState;
|
|
1945
2128
|
const adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets || [];
|
|
2129
|
+
const hasActualAdoptedStyles = this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
|
|
2130
|
+
const hasFallbackStyleElement = Array.from(this.shadowRoot.children).some(
|
|
2131
|
+
(child) => child instanceof HTMLStyleElement
|
|
2132
|
+
);
|
|
1946
2133
|
try {
|
|
1947
|
-
if (
|
|
2134
|
+
if (!hasActualAdoptedStyles && !hasFallbackStyleElement) {
|
|
1948
2135
|
const stylesToApply = this._autoStyles || this.config.styles;
|
|
1949
2136
|
if (stylesToApply) {
|
|
1950
2137
|
const styleName = this.config.styleName || this.constructor.name;
|
|
@@ -1962,31 +2149,40 @@ var WebComponent = class extends BaseComponent {
|
|
|
1962
2149
|
}
|
|
1963
2150
|
}
|
|
1964
2151
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
}
|
|
1968
|
-
requestAnimationFrame(() => {
|
|
2152
|
+
const isContentAlreadyInShadowRoot = content.parentNode === this.shadowRoot;
|
|
2153
|
+
if (!isContentAlreadyInShadowRoot) {
|
|
1969
2154
|
this.shadowRoot.appendChild(content);
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
oldChildren.forEach((child) => child.remove());
|
|
1983
|
-
requestAnimationFrame(() => {
|
|
1984
|
-
this.restoreFocusState(focusState);
|
|
1985
|
-
this._pendingFocusState = null;
|
|
1986
|
-
this.onRendered?.();
|
|
1987
|
-
this._isRendering = false;
|
|
1988
|
-
});
|
|
2155
|
+
}
|
|
2156
|
+
const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
|
|
2157
|
+
if (child === content) {
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
if (child instanceof HTMLStyleElement) {
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
if (shouldPreserveElement(child)) {
|
|
2164
|
+
return false;
|
|
2165
|
+
}
|
|
2166
|
+
return true;
|
|
1989
2167
|
});
|
|
2168
|
+
oldChildren.forEach((child) => child.remove());
|
|
2169
|
+
const hasStylesAfterDOM = this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
|
|
2170
|
+
const hasStyleElementAfterDOM = Array.from(this.shadowRoot.children).some(
|
|
2171
|
+
(child) => child instanceof HTMLStyleElement
|
|
2172
|
+
);
|
|
2173
|
+
if (adoptedStyleSheets.length > 0) {
|
|
2174
|
+
this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
|
|
2175
|
+
} else if (!hasStylesAfterDOM && !hasStyleElementAfterDOM) {
|
|
2176
|
+
const stylesToApply = this._autoStyles || this.config.styles;
|
|
2177
|
+
if (stylesToApply) {
|
|
2178
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
2179
|
+
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
this.restoreFocusState(focusState);
|
|
2183
|
+
this._pendingFocusState = null;
|
|
2184
|
+
this.onRendered?.();
|
|
2185
|
+
this._isRendering = false;
|
|
1990
2186
|
} catch (error) {
|
|
1991
2187
|
logger6.error("Error in _rerender:", error);
|
|
1992
2188
|
this.renderError(error);
|
|
@@ -2048,6 +2244,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
2048
2244
|
const hasActualContent = Array.from(this.children).some(
|
|
2049
2245
|
(child) => child !== styleElement && !(child instanceof HTMLSlotElement)
|
|
2050
2246
|
);
|
|
2247
|
+
this.onConnected?.();
|
|
2051
2248
|
if (hasActualContent && !hasErrorElement) {
|
|
2052
2249
|
this.markJSXChildren();
|
|
2053
2250
|
if (styleElement && styleElement !== this.firstChild) {
|
|
@@ -2065,7 +2262,6 @@ var LightComponent = class extends BaseComponent {
|
|
|
2065
2262
|
}
|
|
2066
2263
|
}
|
|
2067
2264
|
this.initializeEventListeners();
|
|
2068
|
-
this.onConnected?.();
|
|
2069
2265
|
if (hasActualContent === false || hasErrorElement) {
|
|
2070
2266
|
requestAnimationFrame(() => {
|
|
2071
2267
|
this.onRendered?.();
|
|
@@ -2164,11 +2360,18 @@ var LightComponent = class extends BaseComponent {
|
|
|
2164
2360
|
return true;
|
|
2165
2361
|
});
|
|
2166
2362
|
oldChildren.forEach((child) => child.remove());
|
|
2167
|
-
if (stylesToApply
|
|
2168
|
-
|
|
2363
|
+
if (stylesToApply) {
|
|
2364
|
+
let styleElement = this.querySelector(
|
|
2169
2365
|
`style[data-wsx-light-component="${styleName}"]`
|
|
2170
2366
|
);
|
|
2171
|
-
if (styleElement
|
|
2367
|
+
if (!styleElement) {
|
|
2368
|
+
styleElement = document.createElement("style");
|
|
2369
|
+
styleElement.setAttribute("data-wsx-light-component", styleName);
|
|
2370
|
+
styleElement.textContent = stylesToApply;
|
|
2371
|
+
this.insertBefore(styleElement, this.firstChild);
|
|
2372
|
+
} else if (styleElement.textContent !== stylesToApply) {
|
|
2373
|
+
styleElement.textContent = stylesToApply;
|
|
2374
|
+
} else if (styleElement !== this.firstChild) {
|
|
2172
2375
|
this.insertBefore(styleElement, this.firstChild);
|
|
2173
2376
|
}
|
|
2174
2377
|
}
|