@wsxjs/wsx-core 0.0.23 → 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-ESZYREJK.mjs → chunk-5Q2VEEUH.mjs} +160 -35
- package/dist/index.js +226 -64
- package/dist/index.mjs +67 -30
- package/dist/jsx-runtime.js +160 -35
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +160 -35
- package/dist/jsx.mjs +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- 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 +122 -45
- package/src/utils/update-children-helpers.ts +184 -18
- package/src/web-component.ts +72 -41
- package/dist/chunk-BPQGLNOQ.mjs +0 -1140
- package/dist/chunk-OGMB43J4.mjs +0 -1131
- package/dist/chunk-OXFZ575O.mjs +0 -1091
- package/dist/chunk-TKHKPLBM.mjs +0 -1142
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
createLogger,
|
|
5
5
|
h,
|
|
6
6
|
shouldPreserveElement
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5Q2VEEUH.mjs";
|
|
8
8
|
|
|
9
9
|
// src/styles/style-manager.ts
|
|
10
10
|
var StyleManager = class {
|
|
@@ -401,6 +401,12 @@ var BaseComponent = class extends HTMLElement {
|
|
|
401
401
|
* @internal
|
|
402
402
|
*/
|
|
403
403
|
this._isRendering = false;
|
|
404
|
+
/**
|
|
405
|
+
* 已调度渲染标志(防止在同一事件循环中重复注册 requestAnimationFrame)
|
|
406
|
+
* 用于批量更新:同一事件循环中的多个状态变化只触发一次渲染
|
|
407
|
+
* @internal
|
|
408
|
+
*/
|
|
409
|
+
this._hasScheduledRender = false;
|
|
404
410
|
/**
|
|
405
411
|
* 处理 blur 事件,在用户停止输入时执行待处理的重渲染
|
|
406
412
|
* @internal
|
|
@@ -507,6 +513,9 @@ var BaseComponent = class extends HTMLElement {
|
|
|
507
513
|
if (this._isRendering) {
|
|
508
514
|
return;
|
|
509
515
|
}
|
|
516
|
+
if (this._hasScheduledRender) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
510
519
|
const root = this.getActiveRoot();
|
|
511
520
|
let activeElement = null;
|
|
512
521
|
if (root instanceof ShadowRoot) {
|
|
@@ -532,8 +541,16 @@ var BaseComponent = class extends HTMLElement {
|
|
|
532
541
|
if (this._pendingRerender) {
|
|
533
542
|
this._pendingRerender = false;
|
|
534
543
|
}
|
|
544
|
+
this._hasScheduledRender = true;
|
|
535
545
|
requestAnimationFrame(() => {
|
|
546
|
+
console.warn("[scheduleRerender] RAF callback:", {
|
|
547
|
+
component: this.constructor.name,
|
|
548
|
+
connected: this.connected,
|
|
549
|
+
isRendering: this._isRendering
|
|
550
|
+
});
|
|
551
|
+
this._hasScheduledRender = false;
|
|
536
552
|
if (this.connected && !this._isRendering) {
|
|
553
|
+
console.warn("[scheduleRerender] calling _rerender()");
|
|
537
554
|
this._isRendering = true;
|
|
538
555
|
this._rerender();
|
|
539
556
|
} else if (!this.connected) {
|
|
@@ -790,6 +807,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
790
807
|
(child) => child instanceof HTMLElement && child.style.color === "red" && child.textContent?.includes("Component Error")
|
|
791
808
|
);
|
|
792
809
|
const hasActualContent = allChildren.length > styleElements.length + slotElements.length;
|
|
810
|
+
this.onConnected?.();
|
|
793
811
|
if (hasActualContent && !hasErrorElement) {
|
|
794
812
|
} else {
|
|
795
813
|
this.shadowRoot.innerHTML = "";
|
|
@@ -801,7 +819,6 @@ var WebComponent = class extends BaseComponent {
|
|
|
801
819
|
this.shadowRoot.appendChild(content);
|
|
802
820
|
}
|
|
803
821
|
this.initializeEventListeners();
|
|
804
|
-
this.onConnected?.();
|
|
805
822
|
if (hasActualContent === false || hasErrorElement) {
|
|
806
823
|
requestAnimationFrame(() => {
|
|
807
824
|
this.onRendered?.();
|
|
@@ -853,8 +870,12 @@ var WebComponent = class extends BaseComponent {
|
|
|
853
870
|
const focusState = this.captureFocusState();
|
|
854
871
|
this._pendingFocusState = focusState;
|
|
855
872
|
const adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets || [];
|
|
873
|
+
const hasActualAdoptedStyles = this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
|
|
874
|
+
const hasFallbackStyleElement = Array.from(this.shadowRoot.children).some(
|
|
875
|
+
(child) => child instanceof HTMLStyleElement
|
|
876
|
+
);
|
|
856
877
|
try {
|
|
857
|
-
if (
|
|
878
|
+
if (!hasActualAdoptedStyles && !hasFallbackStyleElement) {
|
|
858
879
|
const stylesToApply = this._autoStyles || this.config.styles;
|
|
859
880
|
if (stylesToApply) {
|
|
860
881
|
const styleName = this.config.styleName || this.constructor.name;
|
|
@@ -872,31 +893,40 @@ var WebComponent = class extends BaseComponent {
|
|
|
872
893
|
}
|
|
873
894
|
}
|
|
874
895
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
requestAnimationFrame(() => {
|
|
896
|
+
const isContentAlreadyInShadowRoot = content.parentNode === this.shadowRoot;
|
|
897
|
+
if (!isContentAlreadyInShadowRoot) {
|
|
879
898
|
this.shadowRoot.appendChild(content);
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
oldChildren.forEach((child) => child.remove());
|
|
893
|
-
requestAnimationFrame(() => {
|
|
894
|
-
this.restoreFocusState(focusState);
|
|
895
|
-
this._pendingFocusState = null;
|
|
896
|
-
this.onRendered?.();
|
|
897
|
-
this._isRendering = false;
|
|
898
|
-
});
|
|
899
|
+
}
|
|
900
|
+
const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
|
|
901
|
+
if (child === content) {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
if (child instanceof HTMLStyleElement) {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
if (shouldPreserveElement(child)) {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
return true;
|
|
899
911
|
});
|
|
912
|
+
oldChildren.forEach((child) => child.remove());
|
|
913
|
+
const hasStylesAfterDOM = this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
|
|
914
|
+
const hasStyleElementAfterDOM = Array.from(this.shadowRoot.children).some(
|
|
915
|
+
(child) => child instanceof HTMLStyleElement
|
|
916
|
+
);
|
|
917
|
+
if (adoptedStyleSheets.length > 0) {
|
|
918
|
+
this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
|
|
919
|
+
} else if (!hasStylesAfterDOM && !hasStyleElementAfterDOM) {
|
|
920
|
+
const stylesToApply = this._autoStyles || this.config.styles;
|
|
921
|
+
if (stylesToApply) {
|
|
922
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
923
|
+
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
this.restoreFocusState(focusState);
|
|
927
|
+
this._pendingFocusState = null;
|
|
928
|
+
this.onRendered?.();
|
|
929
|
+
this._isRendering = false;
|
|
900
930
|
} catch (error) {
|
|
901
931
|
logger3.error("Error in _rerender:", error);
|
|
902
932
|
this.renderError(error);
|
|
@@ -958,6 +988,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
958
988
|
const hasActualContent = Array.from(this.children).some(
|
|
959
989
|
(child) => child !== styleElement && !(child instanceof HTMLSlotElement)
|
|
960
990
|
);
|
|
991
|
+
this.onConnected?.();
|
|
961
992
|
if (hasActualContent && !hasErrorElement) {
|
|
962
993
|
this.markJSXChildren();
|
|
963
994
|
if (styleElement && styleElement !== this.firstChild) {
|
|
@@ -975,7 +1006,6 @@ var LightComponent = class extends BaseComponent {
|
|
|
975
1006
|
}
|
|
976
1007
|
}
|
|
977
1008
|
this.initializeEventListeners();
|
|
978
|
-
this.onConnected?.();
|
|
979
1009
|
if (hasActualContent === false || hasErrorElement) {
|
|
980
1010
|
requestAnimationFrame(() => {
|
|
981
1011
|
this.onRendered?.();
|
|
@@ -1074,11 +1104,18 @@ var LightComponent = class extends BaseComponent {
|
|
|
1074
1104
|
return true;
|
|
1075
1105
|
});
|
|
1076
1106
|
oldChildren.forEach((child) => child.remove());
|
|
1077
|
-
if (stylesToApply
|
|
1078
|
-
|
|
1107
|
+
if (stylesToApply) {
|
|
1108
|
+
let styleElement = this.querySelector(
|
|
1079
1109
|
`style[data-wsx-light-component="${styleName}"]`
|
|
1080
1110
|
);
|
|
1081
|
-
if (styleElement
|
|
1111
|
+
if (!styleElement) {
|
|
1112
|
+
styleElement = document.createElement("style");
|
|
1113
|
+
styleElement.setAttribute("data-wsx-light-component", styleName);
|
|
1114
|
+
styleElement.textContent = stylesToApply;
|
|
1115
|
+
this.insertBefore(styleElement, this.firstChild);
|
|
1116
|
+
} else if (styleElement.textContent !== stylesToApply) {
|
|
1117
|
+
styleElement.textContent = stylesToApply;
|
|
1118
|
+
} else if (styleElement !== this.firstChild) {
|
|
1082
1119
|
this.insertBefore(styleElement, this.firstChild);
|
|
1083
1120
|
}
|
|
1084
1121
|
}
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -101,6 +101,7 @@ var _RenderContext = class _RenderContext {
|
|
|
101
101
|
* @param fn The function to execute (usually the render method).
|
|
102
102
|
*/
|
|
103
103
|
static runInContext(component, fn) {
|
|
104
|
+
resetCounterForNewRenderCycle(component);
|
|
104
105
|
const prev = _RenderContext.current;
|
|
105
106
|
_RenderContext.current = component;
|
|
106
107
|
try {
|
|
@@ -126,21 +127,20 @@ _RenderContext.current = null;
|
|
|
126
127
|
var RenderContext = _RenderContext;
|
|
127
128
|
|
|
128
129
|
// src/utils/cache-key.ts
|
|
129
|
-
var POSITION_ID_KEY = "__wsxPositionId";
|
|
130
130
|
var INDEX_KEY = "__wsxIndex";
|
|
131
131
|
var componentElementCounters = /* @__PURE__ */ new WeakMap();
|
|
132
132
|
var componentIdCache = /* @__PURE__ */ new WeakMap();
|
|
133
133
|
function generateCacheKey(tag, props, componentId, component) {
|
|
134
|
-
const positionId = props?.[POSITION_ID_KEY];
|
|
135
134
|
const userKey = props?.key;
|
|
136
135
|
const index = props?.[INDEX_KEY];
|
|
136
|
+
const positionId = props?.__wsxPositionId;
|
|
137
137
|
if (userKey !== void 0 && userKey !== null) {
|
|
138
138
|
return `${componentId}:${tag}:key-${String(userKey)}`;
|
|
139
139
|
}
|
|
140
140
|
if (index !== void 0 && index !== null) {
|
|
141
141
|
return `${componentId}:${tag}:idx-${String(index)}`;
|
|
142
142
|
}
|
|
143
|
-
if (positionId !== void 0 && positionId !== null
|
|
143
|
+
if (positionId !== void 0 && positionId !== null) {
|
|
144
144
|
return `${componentId}:${tag}:${String(positionId)}`;
|
|
145
145
|
}
|
|
146
146
|
if (component) {
|
|
@@ -151,6 +151,9 @@ function generateCacheKey(tag, props, componentId, component) {
|
|
|
151
151
|
}
|
|
152
152
|
return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
|
|
153
153
|
}
|
|
154
|
+
function resetCounterForNewRenderCycle(component) {
|
|
155
|
+
componentElementCounters.set(component, 0);
|
|
156
|
+
}
|
|
154
157
|
function getComponentId() {
|
|
155
158
|
const component = RenderContext.getCurrentComponent();
|
|
156
159
|
if (component) {
|
|
@@ -568,7 +571,9 @@ function applySingleProp(element, key, value, tag, isSVG) {
|
|
|
568
571
|
}
|
|
569
572
|
if (key.startsWith("on") && typeof value === "function") {
|
|
570
573
|
const eventName = key.slice(2).toLowerCase();
|
|
574
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
571
575
|
element.addEventListener(eventName, value);
|
|
576
|
+
element[listenerKey] = value;
|
|
572
577
|
return;
|
|
573
578
|
}
|
|
574
579
|
if (typeof value === "boolean") {
|
|
@@ -663,7 +668,7 @@ function findElementNode(oldChild, parent) {
|
|
|
663
668
|
function findTextNode(parent, domIndex) {
|
|
664
669
|
while (domIndex.value < parent.childNodes.length) {
|
|
665
670
|
const node = parent.childNodes[domIndex.value];
|
|
666
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
671
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent) {
|
|
667
672
|
const textNode = node;
|
|
668
673
|
domIndex.value++;
|
|
669
674
|
return textNode;
|
|
@@ -677,13 +682,23 @@ function updateOrCreateTextNode(parent, oldNode, newText) {
|
|
|
677
682
|
if (oldNode.textContent !== newText) {
|
|
678
683
|
oldNode.textContent = newText;
|
|
679
684
|
}
|
|
685
|
+
return oldNode;
|
|
680
686
|
} else {
|
|
687
|
+
if (!oldNode) {
|
|
688
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
689
|
+
const node = parent.childNodes[i];
|
|
690
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent && node.textContent === newText) {
|
|
691
|
+
return node;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
681
695
|
const newTextNode = document.createTextNode(newText);
|
|
682
696
|
if (oldNode && !shouldPreserveElement(oldNode)) {
|
|
683
697
|
parent.replaceChild(newTextNode, oldNode);
|
|
684
698
|
} else {
|
|
685
699
|
parent.insertBefore(newTextNode, oldNode || null);
|
|
686
700
|
}
|
|
701
|
+
return newTextNode;
|
|
687
702
|
}
|
|
688
703
|
}
|
|
689
704
|
function removeNodeIfNotPreserved(parent, node) {
|
|
@@ -692,29 +707,66 @@ function removeNodeIfNotPreserved(parent, node) {
|
|
|
692
707
|
}
|
|
693
708
|
}
|
|
694
709
|
function replaceOrInsertElement(parent, newChild, oldNode) {
|
|
710
|
+
const targetNextSibling = oldNode && shouldPreserveElement(oldNode) ? oldNode : oldNode?.nextSibling || null;
|
|
711
|
+
replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling);
|
|
712
|
+
}
|
|
713
|
+
function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling) {
|
|
695
714
|
if (newChild.parentNode && newChild.parentNode !== parent) {
|
|
696
715
|
newChild.parentNode.removeChild(newChild);
|
|
697
716
|
}
|
|
698
|
-
|
|
717
|
+
const isInCorrectPosition = newChild.parentNode === parent && newChild.nextSibling === targetNextSibling;
|
|
718
|
+
if (isInCorrectPosition) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (newChild.parentNode === parent) {
|
|
722
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
|
|
699
726
|
if (oldNode !== newChild) {
|
|
700
727
|
parent.replaceChild(newChild, oldNode);
|
|
701
728
|
}
|
|
702
|
-
} else
|
|
703
|
-
|
|
729
|
+
} else {
|
|
730
|
+
if (newChild.parentNode === parent) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const newChildCacheKey = getElementCacheKey(newChild);
|
|
734
|
+
if (!newChildCacheKey) {
|
|
735
|
+
const newChildContent = newChild.textContent || "";
|
|
736
|
+
const newChildTag = newChild.tagName.toLowerCase();
|
|
737
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
738
|
+
const existingNode = parent.childNodes[i];
|
|
739
|
+
if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
|
|
740
|
+
const existingCacheKey = getElementCacheKey(existingNode);
|
|
741
|
+
if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
704
748
|
}
|
|
705
749
|
}
|
|
706
|
-
function appendNewChild(parent, child) {
|
|
750
|
+
function appendNewChild(parent, child, processedNodes) {
|
|
707
751
|
if (child === null || child === void 0 || child === false) {
|
|
708
752
|
return;
|
|
709
753
|
}
|
|
710
754
|
if (typeof child === "string" || typeof child === "number") {
|
|
711
|
-
|
|
755
|
+
const newTextNode = document.createTextNode(String(child));
|
|
756
|
+
parent.appendChild(newTextNode);
|
|
757
|
+
if (processedNodes) {
|
|
758
|
+
processedNodes.add(newTextNode);
|
|
759
|
+
}
|
|
712
760
|
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
761
|
+
if (child.parentNode === parent) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
713
764
|
if (child.parentNode && child.parentNode !== parent) {
|
|
714
765
|
child.parentNode.removeChild(child);
|
|
715
766
|
}
|
|
716
|
-
|
|
717
|
-
|
|
767
|
+
parent.appendChild(child);
|
|
768
|
+
if (processedNodes) {
|
|
769
|
+
processedNodes.add(child);
|
|
718
770
|
}
|
|
719
771
|
} else if (child instanceof DocumentFragment) {
|
|
720
772
|
parent.appendChild(child);
|
|
@@ -736,10 +788,22 @@ function buildNewChildrenMaps(flatNew) {
|
|
|
736
788
|
}
|
|
737
789
|
return { elementSet, cacheKeyMap };
|
|
738
790
|
}
|
|
739
|
-
function shouldRemoveNode(node, elementSet, cacheKeyMap) {
|
|
791
|
+
function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
|
|
740
792
|
if (shouldPreserveElement(node)) {
|
|
741
793
|
return false;
|
|
742
794
|
}
|
|
795
|
+
if (node.nodeType === Node.TEXT_NODE && processedNodes && processedNodes.has(node)) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
if (node.nodeType === Node.TEXT_NODE && processedNodes) {
|
|
799
|
+
let parent = node.parentNode;
|
|
800
|
+
while (parent) {
|
|
801
|
+
if (processedNodes.has(parent) && parent.parentNode) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
parent = parent.parentNode;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
743
807
|
if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
|
|
744
808
|
if (elementSet.has(node)) {
|
|
745
809
|
return false;
|
|
@@ -768,24 +832,39 @@ function deduplicateCacheKeys(parent, cacheKeyMap) {
|
|
|
768
832
|
if (child !== newChild) {
|
|
769
833
|
parent.replaceChild(newChild, child);
|
|
770
834
|
}
|
|
835
|
+
} else if (cacheKey && cacheKeyMap.has(cacheKey) && processedCacheKeys.has(cacheKey)) {
|
|
836
|
+
const newChild = cacheKeyMap.get(cacheKey);
|
|
837
|
+
if (child !== newChild) {
|
|
838
|
+
parent.removeChild(child);
|
|
839
|
+
}
|
|
771
840
|
}
|
|
772
841
|
}
|
|
773
842
|
}
|
|
774
843
|
}
|
|
775
|
-
function collectNodesToRemove(parent, elementSet, cacheKeyMap) {
|
|
844
|
+
function collectNodesToRemove(parent, elementSet, cacheKeyMap, processedNodes) {
|
|
776
845
|
const nodesToRemove = [];
|
|
777
846
|
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
778
847
|
const node = parent.childNodes[i];
|
|
779
|
-
if (shouldRemoveNode(node, elementSet, cacheKeyMap)) {
|
|
848
|
+
if (shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes)) {
|
|
780
849
|
nodesToRemove.push(node);
|
|
781
850
|
}
|
|
782
851
|
}
|
|
783
852
|
return nodesToRemove;
|
|
784
853
|
}
|
|
785
|
-
function removeNodes(parent, nodes) {
|
|
854
|
+
function removeNodes(parent, nodes, cacheManager) {
|
|
786
855
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
787
856
|
const node = nodes[i];
|
|
788
857
|
if (node.parentNode === parent) {
|
|
858
|
+
if (cacheManager && (node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
859
|
+
const metadata = cacheManager.getMetadata(node);
|
|
860
|
+
const refCallback = metadata?.ref;
|
|
861
|
+
if (typeof refCallback === "function") {
|
|
862
|
+
try {
|
|
863
|
+
refCallback(null);
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
789
868
|
parent.removeChild(node);
|
|
790
869
|
}
|
|
791
870
|
}
|
|
@@ -805,6 +884,12 @@ function flattenChildrenSafe(children) {
|
|
|
805
884
|
function removeProp(element, key, oldValue, tag) {
|
|
806
885
|
const isSVG = shouldUseSVGNamespace(tag);
|
|
807
886
|
if (key === "ref") {
|
|
887
|
+
if (typeof oldValue === "function") {
|
|
888
|
+
try {
|
|
889
|
+
oldValue(null);
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
}
|
|
808
893
|
return;
|
|
809
894
|
}
|
|
810
895
|
if (key === "className" || key === "class") {
|
|
@@ -820,6 +905,13 @@ function removeProp(element, key, oldValue, tag) {
|
|
|
820
905
|
return;
|
|
821
906
|
}
|
|
822
907
|
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
908
|
+
const eventName = key.slice(2).toLowerCase();
|
|
909
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
910
|
+
const savedListener = element[listenerKey];
|
|
911
|
+
if (savedListener) {
|
|
912
|
+
element.removeEventListener(eventName, savedListener);
|
|
913
|
+
delete element[listenerKey];
|
|
914
|
+
}
|
|
823
915
|
return;
|
|
824
916
|
}
|
|
825
917
|
if (key === "value") {
|
|
@@ -863,7 +955,13 @@ function applySingleProp2(element, key, value, tag, isSVG) {
|
|
|
863
955
|
}
|
|
864
956
|
if (key.startsWith("on") && typeof value === "function") {
|
|
865
957
|
const eventName = key.slice(2).toLowerCase();
|
|
958
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
959
|
+
const oldListener = element[listenerKey];
|
|
960
|
+
if (oldListener) {
|
|
961
|
+
element.removeEventListener(eventName, oldListener);
|
|
962
|
+
}
|
|
866
963
|
element.addEventListener(eventName, value);
|
|
964
|
+
element[listenerKey] = value;
|
|
867
965
|
return;
|
|
868
966
|
}
|
|
869
967
|
if (typeof value === "boolean") {
|
|
@@ -918,12 +1016,13 @@ function updateProps(element, oldProps, newProps, tag) {
|
|
|
918
1016
|
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
919
1017
|
}
|
|
920
1018
|
}
|
|
921
|
-
function updateChildren(element, oldChildren, newChildren,
|
|
1019
|
+
function updateChildren(element, oldChildren, newChildren, _cacheManager) {
|
|
922
1020
|
const flatOld = flattenChildrenSafe(oldChildren);
|
|
923
1021
|
const flatNew = flattenChildrenSafe(newChildren);
|
|
924
1022
|
const preservedElements = collectPreservedElements(element);
|
|
925
1023
|
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
926
1024
|
const domIndex = { value: 0 };
|
|
1025
|
+
const processedNodes = /* @__PURE__ */ new Set();
|
|
927
1026
|
for (let i = 0; i < minLength; i++) {
|
|
928
1027
|
const oldChild = flatOld[i];
|
|
929
1028
|
const newChild = flatNew[i];
|
|
@@ -939,9 +1038,10 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
|
|
|
939
1038
|
} else if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
940
1039
|
oldNode = findTextNode(element, domIndex);
|
|
941
1040
|
if (!oldNode && element.childNodes.length > 0) {
|
|
1041
|
+
const oldText = String(oldChild);
|
|
942
1042
|
for (let j = domIndex.value; j < element.childNodes.length; j++) {
|
|
943
1043
|
const node = element.childNodes[j];
|
|
944
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1044
|
+
if (node.nodeType === Node.TEXT_NODE && node.parentNode === element && node.textContent === oldText) {
|
|
945
1045
|
oldNode = node;
|
|
946
1046
|
domIndex.value = j + 1;
|
|
947
1047
|
break;
|
|
@@ -955,7 +1055,14 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
|
|
|
955
1055
|
const newText = String(newChild);
|
|
956
1056
|
const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
|
|
957
1057
|
if (needsUpdate) {
|
|
958
|
-
updateOrCreateTextNode(element, oldNode, newText);
|
|
1058
|
+
const updatedNode = updateOrCreateTextNode(element, oldNode, newText);
|
|
1059
|
+
if (updatedNode && !processedNodes.has(updatedNode)) {
|
|
1060
|
+
processedNodes.add(updatedNode);
|
|
1061
|
+
}
|
|
1062
|
+
} else {
|
|
1063
|
+
if (oldNode && oldNode.parentNode === element) {
|
|
1064
|
+
processedNodes.add(oldNode);
|
|
1065
|
+
}
|
|
959
1066
|
}
|
|
960
1067
|
} else {
|
|
961
1068
|
removeNodeIfNotPreserved(element, oldNode);
|
|
@@ -969,30 +1076,48 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
|
|
|
969
1076
|
if (oldNode && shouldPreserveElement(oldNode)) {
|
|
970
1077
|
continue;
|
|
971
1078
|
}
|
|
972
|
-
if (newChild
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1079
|
+
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1080
|
+
let targetNextSibling = null;
|
|
1081
|
+
let foundPreviousElement = false;
|
|
1082
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1083
|
+
const prevChild = flatNew[j];
|
|
1084
|
+
if (prevChild instanceof HTMLElement || prevChild instanceof SVGElement) {
|
|
1085
|
+
if (prevChild.parentNode === element) {
|
|
1086
|
+
targetNextSibling = prevChild.nextSibling;
|
|
1087
|
+
foundPreviousElement = true;
|
|
1088
|
+
break;
|
|
978
1089
|
}
|
|
979
1090
|
}
|
|
980
|
-
} else {
|
|
981
|
-
if (oldNode === newChild && newChild.parentNode === element) {
|
|
982
|
-
continue;
|
|
983
|
-
}
|
|
984
1091
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1092
|
+
if (!foundPreviousElement) {
|
|
1093
|
+
const firstChild = Array.from(element.childNodes).find(
|
|
1094
|
+
(node) => !shouldPreserveElement(node) && !processedNodes.has(node)
|
|
1095
|
+
);
|
|
1096
|
+
targetNextSibling = firstChild || null;
|
|
1097
|
+
}
|
|
1098
|
+
const isInCorrectPosition = newChild.parentNode === element && newChild.nextSibling === targetNextSibling;
|
|
1099
|
+
if (newChild === oldChild && isInCorrectPosition) {
|
|
1100
|
+
if (oldNode) processedNodes.add(oldNode);
|
|
1101
|
+
processedNodes.add(newChild);
|
|
988
1102
|
continue;
|
|
989
1103
|
}
|
|
990
|
-
|
|
1104
|
+
const referenceNode = oldNode && oldNode.parentNode === element ? oldNode : null;
|
|
1105
|
+
replaceOrInsertElementAtPosition(
|
|
1106
|
+
element,
|
|
1107
|
+
newChild,
|
|
1108
|
+
referenceNode,
|
|
1109
|
+
targetNextSibling
|
|
1110
|
+
);
|
|
1111
|
+
if (oldNode && oldNode !== newChild) {
|
|
1112
|
+
processedNodes.delete(oldNode);
|
|
1113
|
+
}
|
|
1114
|
+
processedNodes.add(newChild);
|
|
991
1115
|
} else {
|
|
992
1116
|
removeNodeIfNotPreserved(element, oldNode);
|
|
993
1117
|
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
994
1118
|
const newTextNode = document.createTextNode(String(newChild));
|
|
995
1119
|
element.insertBefore(newTextNode, oldNode?.nextSibling || null);
|
|
1120
|
+
processedNodes.add(newTextNode);
|
|
996
1121
|
} else if (newChild instanceof DocumentFragment) {
|
|
997
1122
|
element.insertBefore(newChild, oldNode?.nextSibling || null);
|
|
998
1123
|
}
|
|
@@ -1000,12 +1125,12 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
|
|
|
1000
1125
|
}
|
|
1001
1126
|
}
|
|
1002
1127
|
for (let i = minLength; i < flatNew.length; i++) {
|
|
1003
|
-
appendNewChild(element, flatNew[i]);
|
|
1128
|
+
appendNewChild(element, flatNew[i], processedNodes);
|
|
1004
1129
|
}
|
|
1005
1130
|
const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
|
|
1006
1131
|
deduplicateCacheKeys(element, cacheKeyMap);
|
|
1007
|
-
const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap);
|
|
1008
|
-
removeNodes(element, nodesToRemove);
|
|
1132
|
+
const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap, processedNodes);
|
|
1133
|
+
removeNodes(element, nodesToRemove, _cacheManager);
|
|
1009
1134
|
reinsertPreservedElements(element, preservedElements);
|
|
1010
1135
|
}
|
|
1011
1136
|
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|