@wsxjs/wsx-core 0.0.28 → 0.0.30
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-2ER76KOQ.mjs +1422 -0
- package/dist/chunk-FAPFH5ON.mjs +1372 -0
- package/dist/chunk-U74WFVRE.mjs +1308 -0
- package/dist/chunk-UTWWJJ4C.mjs +1360 -0
- package/dist/index.js +171 -42
- package/dist/index.mjs +47 -30
- package/dist/jsx-runtime.js +77 -13
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +77 -13
- package/dist/jsx.mjs +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/base-component.ts +14 -10
- package/src/index.ts +2 -0
- package/src/light-component.ts +61 -31
- package/src/utils/dom-utils.ts +13 -1
- package/src/utils/element-marking.ts +5 -3
- package/src/utils/element-update.ts +92 -14
- package/src/utils/update-children-helpers.ts +40 -13
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __export(index_exports, {
|
|
|
32
32
|
jsx: () => h,
|
|
33
33
|
jsxs: () => h,
|
|
34
34
|
logger: () => import_wsx_logger4.logger,
|
|
35
|
+
parseHTMLToNodes: () => parseHTMLToNodes,
|
|
35
36
|
registerComponent: () => registerComponent,
|
|
36
37
|
state: () => state
|
|
37
38
|
});
|
|
@@ -44,6 +45,7 @@ function parseHTMLToNodes(html) {
|
|
|
44
45
|
temp.innerHTML = html;
|
|
45
46
|
return Array.from(temp.childNodes).map((node) => {
|
|
46
47
|
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
48
|
+
node.__wsxManaged = true;
|
|
47
49
|
return node;
|
|
48
50
|
} else {
|
|
49
51
|
return node.textContent || "";
|
|
@@ -56,7 +58,13 @@ function isHTMLString(str) {
|
|
|
56
58
|
const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
|
|
57
59
|
const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
|
|
58
60
|
if (looksLikeMath) return false;
|
|
59
|
-
|
|
61
|
+
const result = htmlTagPattern.test(trimmed);
|
|
62
|
+
if (result) {
|
|
63
|
+
console.log(`[WSX Debug] isHTMLString("${trimmed.substring(0, 50)}..."): ${result}`, {
|
|
64
|
+
looksLikeMath
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
60
68
|
}
|
|
61
69
|
function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
|
|
62
70
|
if (depth > 10) {
|
|
@@ -210,10 +218,10 @@ function shouldPreserveElement(element) {
|
|
|
210
218
|
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
211
219
|
return false;
|
|
212
220
|
}
|
|
213
|
-
if (!isCreatedByH(element)) {
|
|
221
|
+
if (!isCreatedByH(element) && element.__wsxManaged !== true) {
|
|
214
222
|
return true;
|
|
215
223
|
}
|
|
216
|
-
if (element.hasAttribute("data-wsx-preserve")) {
|
|
224
|
+
if (element instanceof HTMLElement && element.hasAttribute("data-wsx-preserve")) {
|
|
217
225
|
return true;
|
|
218
226
|
}
|
|
219
227
|
return false;
|
|
@@ -760,6 +768,64 @@ function removeNodeIfNotPreserved(parent, node) {
|
|
|
760
768
|
parent.removeChild(node);
|
|
761
769
|
}
|
|
762
770
|
}
|
|
771
|
+
function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling, processedNodes) {
|
|
772
|
+
if (newChild.parentNode && newChild.parentNode !== parent) {
|
|
773
|
+
newChild.parentNode.removeChild(newChild);
|
|
774
|
+
}
|
|
775
|
+
const isInCorrectPosition = newChild.parentNode === parent && (newChild.nextSibling === targetNextSibling || targetNextSibling === newChild);
|
|
776
|
+
if (isInCorrectPosition) {
|
|
777
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (newChild.parentNode === parent) {
|
|
781
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
782
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
|
|
786
|
+
if (oldNode !== newChild) {
|
|
787
|
+
const oldCacheKey = getElementCacheKey(oldNode);
|
|
788
|
+
const newCacheKey = getElementCacheKey(newChild);
|
|
789
|
+
if (!oldCacheKey && !newCacheKey && oldNode.__wsxManaged === true) {
|
|
790
|
+
const oldTag = oldNode.tagName.toLowerCase();
|
|
791
|
+
const newTag = newChild.tagName.toLowerCase();
|
|
792
|
+
if (oldTag === newTag && oldNode.textContent === newChild.textContent) {
|
|
793
|
+
if (processedNodes) processedNodes.add(oldNode);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
parent.replaceChild(newChild, oldNode);
|
|
798
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
799
|
+
} else {
|
|
800
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
if (newChild.parentNode === parent) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const newChildCacheKey = getElementCacheKey(newChild);
|
|
807
|
+
if (!newChildCacheKey) {
|
|
808
|
+
const newChildContent = newChild.textContent || "";
|
|
809
|
+
const newChildTag = newChild.tagName.toLowerCase();
|
|
810
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
811
|
+
const existingNode = parent.childNodes[i];
|
|
812
|
+
if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
|
|
813
|
+
const existingCacheKey = getElementCacheKey(existingNode);
|
|
814
|
+
if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
|
|
815
|
+
console.log(
|
|
816
|
+
"[WSX Debug] Found duplicate content, keeping existing:",
|
|
817
|
+
existingNode.tagName,
|
|
818
|
+
existingNode.textContent
|
|
819
|
+
);
|
|
820
|
+
if (processedNodes) processedNodes.add(existingNode);
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
763
829
|
function appendNewChild(parent, child, processedNodes) {
|
|
764
830
|
if (child === null || child === void 0 || child === false) {
|
|
765
831
|
return;
|
|
@@ -828,10 +894,10 @@ function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
|
|
|
828
894
|
}
|
|
829
895
|
}
|
|
830
896
|
const isProcessed = processedNodes && processedNodes.has(node);
|
|
831
|
-
if (
|
|
897
|
+
if (isProcessed) {
|
|
832
898
|
return false;
|
|
833
899
|
}
|
|
834
|
-
if (node
|
|
900
|
+
if (shouldPreserveElement(node)) {
|
|
835
901
|
return false;
|
|
836
902
|
}
|
|
837
903
|
if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
|
|
@@ -1149,14 +1215,13 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
|
|
|
1149
1215
|
}
|
|
1150
1216
|
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1151
1217
|
const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
processedNodes.add(newChild);
|
|
1218
|
+
replaceOrInsertElementAtPosition(
|
|
1219
|
+
element,
|
|
1220
|
+
newChild,
|
|
1221
|
+
oldNode,
|
|
1222
|
+
insertBeforeNode,
|
|
1223
|
+
processedNodes
|
|
1224
|
+
);
|
|
1160
1225
|
insertionIndex.value++;
|
|
1161
1226
|
} else {
|
|
1162
1227
|
const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
@@ -1203,6 +1268,55 @@ function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
|
1203
1268
|
updateProps(element, oldProps, newProps, tag);
|
|
1204
1269
|
updateChildren(element, oldChildren, newChildren, cacheManager);
|
|
1205
1270
|
}
|
|
1271
|
+
function reconcileElement(oldParent, newParent) {
|
|
1272
|
+
const oldChildren = Array.from(oldParent.childNodes);
|
|
1273
|
+
const newChildren = Array.from(newParent.childNodes);
|
|
1274
|
+
const maxLength = Math.max(oldChildren.length, newChildren.length);
|
|
1275
|
+
for (let i = 0; i < maxLength; i++) {
|
|
1276
|
+
const oldChild = oldChildren[i];
|
|
1277
|
+
const newChild = newChildren[i];
|
|
1278
|
+
if (!newChild) {
|
|
1279
|
+
oldChild?.remove();
|
|
1280
|
+
} else if (!oldChild) {
|
|
1281
|
+
oldParent.appendChild(newChild.cloneNode(true));
|
|
1282
|
+
} else if (oldChild.nodeType !== newChild.nodeType) {
|
|
1283
|
+
oldParent.replaceChild(newChild.cloneNode(true), oldChild);
|
|
1284
|
+
} else if (oldChild.nodeType === Node.TEXT_NODE) {
|
|
1285
|
+
if (oldChild.textContent !== newChild.textContent) {
|
|
1286
|
+
oldChild.textContent = newChild.textContent;
|
|
1287
|
+
}
|
|
1288
|
+
} else if (oldChild.nodeType === Node.ELEMENT_NODE) {
|
|
1289
|
+
const oldEl = oldChild;
|
|
1290
|
+
const newEl = newChild;
|
|
1291
|
+
if (oldEl.tagName !== newEl.tagName) {
|
|
1292
|
+
oldParent.replaceChild(newEl.cloneNode(true), oldEl);
|
|
1293
|
+
} else {
|
|
1294
|
+
Array.from(oldEl.attributes).forEach((attr) => {
|
|
1295
|
+
if (!newEl.hasAttribute(attr.name)) {
|
|
1296
|
+
oldEl.removeAttribute(attr.name);
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
Array.from(newEl.attributes).forEach((attr) => {
|
|
1300
|
+
if (oldEl.getAttribute(attr.name) !== attr.value) {
|
|
1301
|
+
oldEl.setAttribute(attr.name, attr.value);
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
1304
|
+
if (oldEl.className !== newEl.className) {
|
|
1305
|
+
oldEl.className = newEl.className;
|
|
1306
|
+
}
|
|
1307
|
+
if (oldEl instanceof HTMLInputElement && newEl instanceof HTMLInputElement) {
|
|
1308
|
+
if (oldEl.checked !== newEl.checked) {
|
|
1309
|
+
oldEl.checked = newEl.checked;
|
|
1310
|
+
}
|
|
1311
|
+
if (oldEl.value !== newEl.value) {
|
|
1312
|
+
oldEl.value = newEl.value;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
reconcileElement(oldEl, newEl);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1206
1320
|
|
|
1207
1321
|
// src/jsx-factory.ts
|
|
1208
1322
|
var logger3 = createLogger("JSX Factory");
|
|
@@ -1833,7 +1947,7 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1833
1947
|
}
|
|
1834
1948
|
}
|
|
1835
1949
|
if (activeElement) {
|
|
1836
|
-
const isInputElement = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
|
|
1950
|
+
const isInputElement = activeElement instanceof HTMLInputElement && activeElement.type !== "radio" && activeElement.type !== "checkbox" || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
|
|
1837
1951
|
const forceRender = activeElement.hasAttribute("data-wsx-force-render");
|
|
1838
1952
|
if (isInputElement && !forceRender) {
|
|
1839
1953
|
this._pendingRerender = true;
|
|
@@ -1849,14 +1963,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1849
1963
|
}
|
|
1850
1964
|
this._hasScheduledRender = true;
|
|
1851
1965
|
requestAnimationFrame(() => {
|
|
1852
|
-
console.warn("[scheduleRerender] RAF callback:", {
|
|
1853
|
-
component: this.constructor.name,
|
|
1854
|
-
connected: this.connected,
|
|
1855
|
-
isRendering: this._isRendering
|
|
1856
|
-
});
|
|
1857
1966
|
this._hasScheduledRender = false;
|
|
1858
1967
|
if (this.connected && !this._isRendering) {
|
|
1859
|
-
console.warn("[scheduleRerender] calling _rerender()");
|
|
1860
1968
|
this._isRendering = true;
|
|
1861
1969
|
this._rerender();
|
|
1862
1970
|
} else if (!this.connected) {
|
|
@@ -1885,7 +1993,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1885
1993
|
clearTimeout(this._rerenderDebounceTimer);
|
|
1886
1994
|
this._rerenderDebounceTimer = null;
|
|
1887
1995
|
}
|
|
1888
|
-
|
|
1996
|
+
const root = this.getActiveRoot();
|
|
1997
|
+
root.removeEventListener("blur", this.handleGlobalBlur, true);
|
|
1889
1998
|
this._pendingRerender = false;
|
|
1890
1999
|
}
|
|
1891
2000
|
/**
|
|
@@ -1893,7 +2002,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
1893
2002
|
* @internal
|
|
1894
2003
|
*/
|
|
1895
2004
|
initializeEventListeners() {
|
|
1896
|
-
|
|
2005
|
+
const root = this.getActiveRoot();
|
|
2006
|
+
root.addEventListener("blur", this.handleGlobalBlur, true);
|
|
1897
2007
|
}
|
|
1898
2008
|
/**
|
|
1899
2009
|
* 获取配置值
|
|
@@ -2350,6 +2460,10 @@ var LightComponent = class extends BaseComponent {
|
|
|
2350
2460
|
querySelectorAll(selector) {
|
|
2351
2461
|
return HTMLElement.prototype.querySelectorAll.call(this, selector);
|
|
2352
2462
|
}
|
|
2463
|
+
/**
|
|
2464
|
+
* 递归协调子元素
|
|
2465
|
+
* 更新现有子元素的属性和内容,而不是替换整个子树
|
|
2466
|
+
*/
|
|
2353
2467
|
/**
|
|
2354
2468
|
* 内部重渲染实现
|
|
2355
2469
|
* 包含从 rerender() 方法迁移的实际渲染逻辑
|
|
@@ -2366,9 +2480,9 @@ var LightComponent = class extends BaseComponent {
|
|
|
2366
2480
|
this._pendingFocusState = focusState;
|
|
2367
2481
|
const jsxChildren = this.getJSXChildren();
|
|
2368
2482
|
try {
|
|
2369
|
-
const
|
|
2483
|
+
const newContent = RenderContext.runInContext(this, () => this.render());
|
|
2370
2484
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
2371
|
-
const target =
|
|
2485
|
+
const target = newContent.querySelector(
|
|
2372
2486
|
`[data-wsx-key="${focusState.key}"]`
|
|
2373
2487
|
);
|
|
2374
2488
|
if (target) {
|
|
@@ -2393,36 +2507,50 @@ var LightComponent = class extends BaseComponent {
|
|
|
2393
2507
|
}
|
|
2394
2508
|
}
|
|
2395
2509
|
requestAnimationFrame(() => {
|
|
2396
|
-
this.appendChild(content);
|
|
2397
2510
|
const oldChildren = Array.from(this.children).filter((child) => {
|
|
2398
|
-
if (child === content) {
|
|
2399
|
-
return false;
|
|
2400
|
-
}
|
|
2401
2511
|
if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
|
|
2402
2512
|
return false;
|
|
2403
2513
|
}
|
|
2404
2514
|
if (child instanceof HTMLElement && jsxChildren.includes(child)) {
|
|
2405
2515
|
return false;
|
|
2406
2516
|
}
|
|
2407
|
-
if (shouldPreserveElement(child)) {
|
|
2408
|
-
return false;
|
|
2409
|
-
}
|
|
2410
2517
|
return true;
|
|
2411
2518
|
});
|
|
2412
|
-
oldChildren.
|
|
2519
|
+
if (oldChildren.length === 1 && newContent instanceof HTMLElement) {
|
|
2520
|
+
const oldElement = oldChildren[0];
|
|
2521
|
+
if (oldElement instanceof HTMLElement && oldElement.tagName === newContent.tagName) {
|
|
2522
|
+
Array.from(oldElement.attributes).forEach((attr) => {
|
|
2523
|
+
if (!newContent.hasAttribute(attr.name)) {
|
|
2524
|
+
oldElement.removeAttribute(attr.name);
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
Array.from(newContent.attributes).forEach((attr) => {
|
|
2528
|
+
if (oldElement.getAttribute(attr.name) !== attr.value) {
|
|
2529
|
+
oldElement.setAttribute(attr.name, attr.value);
|
|
2530
|
+
}
|
|
2531
|
+
});
|
|
2532
|
+
reconcileElement(oldElement, newContent);
|
|
2533
|
+
} else {
|
|
2534
|
+
oldElement.remove();
|
|
2535
|
+
this.appendChild(newContent);
|
|
2536
|
+
}
|
|
2537
|
+
} else {
|
|
2538
|
+
oldChildren.forEach((child) => child.remove());
|
|
2539
|
+
this.appendChild(newContent);
|
|
2540
|
+
}
|
|
2413
2541
|
if (stylesToApply) {
|
|
2414
|
-
let
|
|
2542
|
+
let styleEl = this.querySelector(
|
|
2415
2543
|
`style[data-wsx-light-component="${styleName}"]`
|
|
2416
2544
|
);
|
|
2417
|
-
if (!
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
this.insertBefore(
|
|
2422
|
-
} else if (
|
|
2423
|
-
|
|
2424
|
-
} else if (
|
|
2425
|
-
this.insertBefore(
|
|
2545
|
+
if (!styleEl) {
|
|
2546
|
+
styleEl = document.createElement("style");
|
|
2547
|
+
styleEl.setAttribute("data-wsx-light-component", styleName);
|
|
2548
|
+
styleEl.textContent = stylesToApply;
|
|
2549
|
+
this.insertBefore(styleEl, this.firstChild);
|
|
2550
|
+
} else if (styleEl.textContent !== stylesToApply) {
|
|
2551
|
+
styleEl.textContent = stylesToApply;
|
|
2552
|
+
} else if (styleEl !== this.firstChild) {
|
|
2553
|
+
this.insertBefore(styleEl, this.firstChild);
|
|
2426
2554
|
}
|
|
2427
2555
|
}
|
|
2428
2556
|
requestAnimationFrame(() => {
|
|
@@ -2626,6 +2754,7 @@ function state(targetOrContext, propertyKey) {
|
|
|
2626
2754
|
jsx,
|
|
2627
2755
|
jsxs,
|
|
2628
2756
|
logger,
|
|
2757
|
+
parseHTMLToNodes,
|
|
2629
2758
|
registerComponent,
|
|
2630
2759
|
state
|
|
2631
2760
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
RenderContext,
|
|
4
4
|
createLogger,
|
|
5
5
|
h,
|
|
6
|
+
parseHTMLToNodes,
|
|
7
|
+
reconcileElement,
|
|
6
8
|
shouldPreserveElement
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2ER76KOQ.mjs";
|
|
8
10
|
|
|
9
11
|
// src/styles/style-manager.ts
|
|
10
12
|
var StyleManager = class {
|
|
@@ -527,7 +529,7 @@ var BaseComponent = class extends HTMLElement {
|
|
|
527
529
|
}
|
|
528
530
|
}
|
|
529
531
|
if (activeElement) {
|
|
530
|
-
const isInputElement = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
|
|
532
|
+
const isInputElement = activeElement instanceof HTMLInputElement && activeElement.type !== "radio" && activeElement.type !== "checkbox" || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
|
|
531
533
|
const forceRender = activeElement.hasAttribute("data-wsx-force-render");
|
|
532
534
|
if (isInputElement && !forceRender) {
|
|
533
535
|
this._pendingRerender = true;
|
|
@@ -543,14 +545,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
543
545
|
}
|
|
544
546
|
this._hasScheduledRender = true;
|
|
545
547
|
requestAnimationFrame(() => {
|
|
546
|
-
console.warn("[scheduleRerender] RAF callback:", {
|
|
547
|
-
component: this.constructor.name,
|
|
548
|
-
connected: this.connected,
|
|
549
|
-
isRendering: this._isRendering
|
|
550
|
-
});
|
|
551
548
|
this._hasScheduledRender = false;
|
|
552
549
|
if (this.connected && !this._isRendering) {
|
|
553
|
-
console.warn("[scheduleRerender] calling _rerender()");
|
|
554
550
|
this._isRendering = true;
|
|
555
551
|
this._rerender();
|
|
556
552
|
} else if (!this.connected) {
|
|
@@ -579,7 +575,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
579
575
|
clearTimeout(this._rerenderDebounceTimer);
|
|
580
576
|
this._rerenderDebounceTimer = null;
|
|
581
577
|
}
|
|
582
|
-
|
|
578
|
+
const root = this.getActiveRoot();
|
|
579
|
+
root.removeEventListener("blur", this.handleGlobalBlur, true);
|
|
583
580
|
this._pendingRerender = false;
|
|
584
581
|
}
|
|
585
582
|
/**
|
|
@@ -587,7 +584,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
587
584
|
* @internal
|
|
588
585
|
*/
|
|
589
586
|
initializeEventListeners() {
|
|
590
|
-
|
|
587
|
+
const root = this.getActiveRoot();
|
|
588
|
+
root.addEventListener("blur", this.handleGlobalBlur, true);
|
|
591
589
|
}
|
|
592
590
|
/**
|
|
593
591
|
* 获取配置值
|
|
@@ -1044,6 +1042,10 @@ var LightComponent = class extends BaseComponent {
|
|
|
1044
1042
|
querySelectorAll(selector) {
|
|
1045
1043
|
return HTMLElement.prototype.querySelectorAll.call(this, selector);
|
|
1046
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* 递归协调子元素
|
|
1047
|
+
* 更新现有子元素的属性和内容,而不是替换整个子树
|
|
1048
|
+
*/
|
|
1047
1049
|
/**
|
|
1048
1050
|
* 内部重渲染实现
|
|
1049
1051
|
* 包含从 rerender() 方法迁移的实际渲染逻辑
|
|
@@ -1060,9 +1062,9 @@ var LightComponent = class extends BaseComponent {
|
|
|
1060
1062
|
this._pendingFocusState = focusState;
|
|
1061
1063
|
const jsxChildren = this.getJSXChildren();
|
|
1062
1064
|
try {
|
|
1063
|
-
const
|
|
1065
|
+
const newContent = RenderContext.runInContext(this, () => this.render());
|
|
1064
1066
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
1065
|
-
const target =
|
|
1067
|
+
const target = newContent.querySelector(
|
|
1066
1068
|
`[data-wsx-key="${focusState.key}"]`
|
|
1067
1069
|
);
|
|
1068
1070
|
if (target) {
|
|
@@ -1087,36 +1089,50 @@ var LightComponent = class extends BaseComponent {
|
|
|
1087
1089
|
}
|
|
1088
1090
|
}
|
|
1089
1091
|
requestAnimationFrame(() => {
|
|
1090
|
-
this.appendChild(content);
|
|
1091
1092
|
const oldChildren = Array.from(this.children).filter((child) => {
|
|
1092
|
-
if (child === content) {
|
|
1093
|
-
return false;
|
|
1094
|
-
}
|
|
1095
1093
|
if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
|
|
1096
1094
|
return false;
|
|
1097
1095
|
}
|
|
1098
1096
|
if (child instanceof HTMLElement && jsxChildren.includes(child)) {
|
|
1099
1097
|
return false;
|
|
1100
1098
|
}
|
|
1101
|
-
if (shouldPreserveElement(child)) {
|
|
1102
|
-
return false;
|
|
1103
|
-
}
|
|
1104
1099
|
return true;
|
|
1105
1100
|
});
|
|
1106
|
-
oldChildren.
|
|
1101
|
+
if (oldChildren.length === 1 && newContent instanceof HTMLElement) {
|
|
1102
|
+
const oldElement = oldChildren[0];
|
|
1103
|
+
if (oldElement instanceof HTMLElement && oldElement.tagName === newContent.tagName) {
|
|
1104
|
+
Array.from(oldElement.attributes).forEach((attr) => {
|
|
1105
|
+
if (!newContent.hasAttribute(attr.name)) {
|
|
1106
|
+
oldElement.removeAttribute(attr.name);
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
Array.from(newContent.attributes).forEach((attr) => {
|
|
1110
|
+
if (oldElement.getAttribute(attr.name) !== attr.value) {
|
|
1111
|
+
oldElement.setAttribute(attr.name, attr.value);
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
reconcileElement(oldElement, newContent);
|
|
1115
|
+
} else {
|
|
1116
|
+
oldElement.remove();
|
|
1117
|
+
this.appendChild(newContent);
|
|
1118
|
+
}
|
|
1119
|
+
} else {
|
|
1120
|
+
oldChildren.forEach((child) => child.remove());
|
|
1121
|
+
this.appendChild(newContent);
|
|
1122
|
+
}
|
|
1107
1123
|
if (stylesToApply) {
|
|
1108
|
-
let
|
|
1124
|
+
let styleEl = this.querySelector(
|
|
1109
1125
|
`style[data-wsx-light-component="${styleName}"]`
|
|
1110
1126
|
);
|
|
1111
|
-
if (!
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
this.insertBefore(
|
|
1116
|
-
} else if (
|
|
1117
|
-
|
|
1118
|
-
} else if (
|
|
1119
|
-
this.insertBefore(
|
|
1127
|
+
if (!styleEl) {
|
|
1128
|
+
styleEl = document.createElement("style");
|
|
1129
|
+
styleEl.setAttribute("data-wsx-light-component", styleName);
|
|
1130
|
+
styleEl.textContent = stylesToApply;
|
|
1131
|
+
this.insertBefore(styleEl, this.firstChild);
|
|
1132
|
+
} else if (styleEl.textContent !== stylesToApply) {
|
|
1133
|
+
styleEl.textContent = stylesToApply;
|
|
1134
|
+
} else if (styleEl !== this.firstChild) {
|
|
1135
|
+
this.insertBefore(styleEl, this.firstChild);
|
|
1120
1136
|
}
|
|
1121
1137
|
}
|
|
1122
1138
|
requestAnimationFrame(() => {
|
|
@@ -1319,6 +1335,7 @@ export {
|
|
|
1319
1335
|
h as jsx,
|
|
1320
1336
|
h as jsxs,
|
|
1321
1337
|
logger5 as logger,
|
|
1338
|
+
parseHTMLToNodes,
|
|
1322
1339
|
registerComponent,
|
|
1323
1340
|
state
|
|
1324
1341
|
};
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -33,6 +33,7 @@ function parseHTMLToNodes(html) {
|
|
|
33
33
|
temp.innerHTML = html;
|
|
34
34
|
return Array.from(temp.childNodes).map((node) => {
|
|
35
35
|
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
36
|
+
node.__wsxManaged = true;
|
|
36
37
|
return node;
|
|
37
38
|
} else {
|
|
38
39
|
return node.textContent || "";
|
|
@@ -45,7 +46,13 @@ function isHTMLString(str) {
|
|
|
45
46
|
const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
|
|
46
47
|
const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
|
|
47
48
|
if (looksLikeMath) return false;
|
|
48
|
-
|
|
49
|
+
const result = htmlTagPattern.test(trimmed);
|
|
50
|
+
if (result) {
|
|
51
|
+
console.log(`[WSX Debug] isHTMLString("${trimmed.substring(0, 50)}..."): ${result}`, {
|
|
52
|
+
looksLikeMath
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
49
56
|
}
|
|
50
57
|
function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
|
|
51
58
|
if (depth > 10) {
|
|
@@ -199,10 +206,10 @@ function shouldPreserveElement(element) {
|
|
|
199
206
|
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
200
207
|
return false;
|
|
201
208
|
}
|
|
202
|
-
if (!isCreatedByH(element)) {
|
|
209
|
+
if (!isCreatedByH(element) && element.__wsxManaged !== true) {
|
|
203
210
|
return true;
|
|
204
211
|
}
|
|
205
|
-
if (element.hasAttribute("data-wsx-preserve")) {
|
|
212
|
+
if (element instanceof HTMLElement && element.hasAttribute("data-wsx-preserve")) {
|
|
206
213
|
return true;
|
|
207
214
|
}
|
|
208
215
|
return false;
|
|
@@ -749,6 +756,64 @@ function removeNodeIfNotPreserved(parent, node) {
|
|
|
749
756
|
parent.removeChild(node);
|
|
750
757
|
}
|
|
751
758
|
}
|
|
759
|
+
function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling, processedNodes) {
|
|
760
|
+
if (newChild.parentNode && newChild.parentNode !== parent) {
|
|
761
|
+
newChild.parentNode.removeChild(newChild);
|
|
762
|
+
}
|
|
763
|
+
const isInCorrectPosition = newChild.parentNode === parent && (newChild.nextSibling === targetNextSibling || targetNextSibling === newChild);
|
|
764
|
+
if (isInCorrectPosition) {
|
|
765
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (newChild.parentNode === parent) {
|
|
769
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
770
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
|
|
774
|
+
if (oldNode !== newChild) {
|
|
775
|
+
const oldCacheKey = getElementCacheKey(oldNode);
|
|
776
|
+
const newCacheKey = getElementCacheKey(newChild);
|
|
777
|
+
if (!oldCacheKey && !newCacheKey && oldNode.__wsxManaged === true) {
|
|
778
|
+
const oldTag = oldNode.tagName.toLowerCase();
|
|
779
|
+
const newTag = newChild.tagName.toLowerCase();
|
|
780
|
+
if (oldTag === newTag && oldNode.textContent === newChild.textContent) {
|
|
781
|
+
if (processedNodes) processedNodes.add(oldNode);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
parent.replaceChild(newChild, oldNode);
|
|
786
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
787
|
+
} else {
|
|
788
|
+
if (processedNodes) processedNodes.add(newChild);
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
if (newChild.parentNode === parent) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const newChildCacheKey = getElementCacheKey(newChild);
|
|
795
|
+
if (!newChildCacheKey) {
|
|
796
|
+
const newChildContent = newChild.textContent || "";
|
|
797
|
+
const newChildTag = newChild.tagName.toLowerCase();
|
|
798
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
799
|
+
const existingNode = parent.childNodes[i];
|
|
800
|
+
if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
|
|
801
|
+
const existingCacheKey = getElementCacheKey(existingNode);
|
|
802
|
+
if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
|
|
803
|
+
console.log(
|
|
804
|
+
"[WSX Debug] Found duplicate content, keeping existing:",
|
|
805
|
+
existingNode.tagName,
|
|
806
|
+
existingNode.textContent
|
|
807
|
+
);
|
|
808
|
+
if (processedNodes) processedNodes.add(existingNode);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
parent.insertBefore(newChild, targetNextSibling);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
752
817
|
function appendNewChild(parent, child, processedNodes) {
|
|
753
818
|
if (child === null || child === void 0 || child === false) {
|
|
754
819
|
return;
|
|
@@ -817,10 +882,10 @@ function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
|
|
|
817
882
|
}
|
|
818
883
|
}
|
|
819
884
|
const isProcessed = processedNodes && processedNodes.has(node);
|
|
820
|
-
if (
|
|
885
|
+
if (isProcessed) {
|
|
821
886
|
return false;
|
|
822
887
|
}
|
|
823
|
-
if (node
|
|
888
|
+
if (shouldPreserveElement(node)) {
|
|
824
889
|
return false;
|
|
825
890
|
}
|
|
826
891
|
if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
|
|
@@ -1138,14 +1203,13 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
|
|
|
1138
1203
|
}
|
|
1139
1204
|
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1140
1205
|
const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
processedNodes.add(newChild);
|
|
1206
|
+
replaceOrInsertElementAtPosition(
|
|
1207
|
+
element,
|
|
1208
|
+
newChild,
|
|
1209
|
+
oldNode,
|
|
1210
|
+
insertBeforeNode,
|
|
1211
|
+
processedNodes
|
|
1212
|
+
);
|
|
1149
1213
|
insertionIndex.value++;
|
|
1150
1214
|
} else {
|
|
1151
1215
|
const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|