@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/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 && positionId !== "no-id") {
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 = flattenChildren(oldChildren);
755
- const flatNew = flattenChildren(newChildren);
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
- let domIndex = 0;
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
- if (oldChild.parentNode === element) {
764
- if (!shouldPreserveElement(oldChild)) {
765
- oldNode = oldChild;
766
- }
767
- } else {
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
- while (domIndex < element.childNodes.length) {
787
- const node = element.childNodes[domIndex];
788
- if (node.nodeType === Node.TEXT_NODE) {
789
- oldNode = node;
790
- domIndex++;
791
- break;
792
- } else if (node.nodeType === Node.ELEMENT_NODE) {
793
- domIndex++;
794
- } else {
795
- domIndex++;
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 (!needsUpdate) {
805
- continue;
806
- }
807
- if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
808
- oldNode.textContent = newText;
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
- const newTextNode = document.createTextNode(newText);
811
- if (oldNode && !shouldPreserveElement(oldNode)) {
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
- if (oldNode && !shouldPreserveElement(oldNode)) {
819
- element.removeChild(oldNode);
820
- }
1079
+ removeNodeIfNotPreserved(element, oldNode);
821
1080
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
822
- if (newChild.parentNode !== element) {
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 === oldChild) {
834
- continue;
835
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
836
- const oldCacheKey = oldNode && (oldNode instanceof HTMLElement || oldNode instanceof SVGElement) ? getElementCacheKey(oldNode) : null;
837
- const newCacheKey = getElementCacheKey(newChild);
838
- const hasSameCacheKey = oldCacheKey && newCacheKey && oldCacheKey === newCacheKey;
839
- if (oldNode) {
840
- if (!shouldPreserveElement(oldNode)) {
841
- if (oldNode !== newChild) {
842
- if (newChild.parentNode === element) {
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
- } else {
875
- if (oldNode && !shouldPreserveElement(oldNode)) {
876
- element.removeChild(oldNode);
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
- const newChild = flatNew[i];
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 (adoptedStyleSheets.length === 0) {
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
- if (this.shadowRoot.adoptedStyleSheets) {
1966
- this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
1967
- }
1968
- requestAnimationFrame(() => {
2152
+ const isContentAlreadyInShadowRoot = content.parentNode === this.shadowRoot;
2153
+ if (!isContentAlreadyInShadowRoot) {
1969
2154
  this.shadowRoot.appendChild(content);
1970
- const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
1971
- if (child === content) {
1972
- return false;
1973
- }
1974
- if (child instanceof HTMLStyleElement) {
1975
- return false;
1976
- }
1977
- if (shouldPreserveElement(child)) {
1978
- return false;
1979
- }
1980
- return true;
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 && this.children.length > 1) {
2168
- const styleElement = this.querySelector(
2363
+ if (stylesToApply) {
2364
+ let styleElement = this.querySelector(
2169
2365
  `style[data-wsx-light-component="${styleName}"]`
2170
2366
  );
2171
- if (styleElement && styleElement !== this.firstChild) {
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
  }