@wsxjs/wsx-core 0.0.14 → 0.0.16

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
@@ -898,6 +898,12 @@ var WebComponent = class extends BaseComponent {
898
898
  }
899
899
  /**
900
900
  * Web Component生命周期:连接到DOM
901
+ *
902
+ * 渲染策略:
903
+ * 1. 检查 Shadow DOM 中是否已有实际内容(排除样式和 slot)
904
+ * 2. 如果有内容,先清空再渲染(避免重复元素)
905
+ * 3. 如果没有内容,直接渲染
906
+ * 4. Slot 元素会被重新添加,浏览器会自动将 Light DOM 中的内容分配到 slot
901
907
  */
902
908
  connectedCallback() {
903
909
  this.connected = true;
@@ -907,8 +913,23 @@ var WebComponent = class extends BaseComponent {
907
913
  const styleName = this.config.styleName || this.constructor.name;
908
914
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
909
915
  }
910
- const content = this.render();
911
- this.shadowRoot.appendChild(content);
916
+ const allChildren = Array.from(this.shadowRoot.children);
917
+ const styleElements = allChildren.filter((child) => child instanceof HTMLStyleElement);
918
+ const slotElements = allChildren.filter((child) => child instanceof HTMLSlotElement);
919
+ const hasErrorElement = allChildren.some(
920
+ (child) => child instanceof HTMLElement && child.style.color === "red" && child.textContent?.includes("Component Error")
921
+ );
922
+ const hasActualContent = allChildren.length > styleElements.length + slotElements.length;
923
+ if (hasActualContent && !hasErrorElement) {
924
+ } else {
925
+ this.shadowRoot.innerHTML = "";
926
+ if (stylesToApply) {
927
+ const styleName = this.config.styleName || this.constructor.name;
928
+ StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
929
+ }
930
+ const content = this.render();
931
+ this.shadowRoot.appendChild(content);
932
+ }
912
933
  this.initializeEventListeners();
913
934
  this.onConnected?.();
914
935
  } catch (error) {
@@ -1021,17 +1042,45 @@ var LightComponent = class extends BaseComponent {
1021
1042
  }
1022
1043
  /**
1023
1044
  * Web Component生命周期:连接到DOM
1045
+ *
1046
+ * 渲染策略:
1047
+ * 1. 检查组件中是否已有实际内容(排除样式元素)
1048
+ * 2. 如果有内容且完整,跳过渲染(避免重复元素和性能优化)
1049
+ * 3. 如果没有内容或不完整,清空后重新渲染
1050
+ * 4. 样式元素需要保留
1024
1051
  */
1025
1052
  connectedCallback() {
1026
1053
  this.connected = true;
1027
1054
  try {
1028
1055
  const stylesToApply = this._autoStyles || this.config.styles;
1056
+ const styleName = this.config.styleName || this.constructor.name;
1029
1057
  if (stylesToApply) {
1030
- const styleName = this.config.styleName || this.constructor.name;
1031
1058
  this.applyScopedStyles(styleName, stylesToApply);
1032
1059
  }
1033
- const content = this.render();
1034
- this.appendChild(content);
1060
+ const styleElement = this.querySelector(
1061
+ `style[data-wsx-light-component="${styleName}"]`
1062
+ );
1063
+ const hasErrorElement = Array.from(this.children).some(
1064
+ (child) => child instanceof HTMLElement && child !== styleElement && child.style.color === "red" && child.textContent?.includes("Component Error")
1065
+ );
1066
+ const hasActualContent = Array.from(this.children).some(
1067
+ (child) => child !== styleElement
1068
+ );
1069
+ if (hasActualContent && !hasErrorElement) {
1070
+ if (styleElement && styleElement !== this.firstChild) {
1071
+ this.insertBefore(styleElement, this.firstChild);
1072
+ }
1073
+ } else {
1074
+ const childrenToRemove = Array.from(this.children).filter(
1075
+ (child) => child !== styleElement
1076
+ );
1077
+ childrenToRemove.forEach((child) => child.remove());
1078
+ const content = this.render();
1079
+ this.appendChild(content);
1080
+ if (styleElement && styleElement !== this.firstChild) {
1081
+ this.insertBefore(styleElement, this.firstChild);
1082
+ }
1083
+ }
1035
1084
  this.initializeEventListeners();
1036
1085
  this.onConnected?.();
1037
1086
  } catch (error) {
@@ -1206,230 +1255,76 @@ function deriveTagName(className, prefix) {
1206
1255
  }
1207
1256
 
1208
1257
  // src/reactive-decorator.ts
1258
+ function createBabelPluginError(propertyName) {
1259
+ return `@state decorator on property "${propertyName}" MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1260
+
1261
+ The @state decorator cannot work without the Babel plugin.
1262
+
1263
+ To fix this, please:
1264
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1265
+ 2. Configure it in vite.config.ts:
1266
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1267
+ export default defineConfig({ plugins: [wsx()] });
1268
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1269
+ npm install --save-dev @wsxjs/wsx-tsconfig
1270
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1271
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1272
+
1273
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`;
1274
+ }
1209
1275
  function state(targetOrContext, propertyKey) {
1210
- let propertyName;
1276
+ let propertyName = "unknown";
1211
1277
  const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
1212
1278
  const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
1213
1279
  const hasStage3Indicators = targetIsObject && ("kind" in targetOrContext || "addInitializer" in targetOrContext || "access" in targetOrContext);
1214
1280
  const hasName = hasStage3Indicators && "name" in targetOrContext;
1215
1281
  const isStage3Decorator = propertyKeyIsObject || hasName || hasStage3Indicators && (propertyKey === void 0 || propertyKey === null) || targetIsObject && "addInitializer" in targetOrContext;
1216
1282
  if (isStage3Decorator) {
1217
- const context = targetOrContext;
1218
- if (context.name) {
1219
- propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1283
+ if (hasStage3Indicators && targetOrContext && typeof targetOrContext === "object") {
1284
+ const context = targetOrContext;
1285
+ if (context.name) {
1286
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1287
+ }
1220
1288
  } else if (propertyKeyIsObject) {
1221
- if ("name" in propertyKey) {
1222
- const keyObj = propertyKey;
1223
- propertyName = keyObj.name ? typeof keyObj.name === "string" ? keyObj.name : keyObj.name.toString() : "unknown";
1224
- } else if ("key" in propertyKey) {
1225
- const keyObj = propertyKey;
1226
- propertyName = keyObj.key ? typeof keyObj.key === "string" ? keyObj.key : keyObj.key.toString() : "unknown";
1289
+ const keyObj = propertyKey;
1290
+ const targetObj = targetOrContext;
1291
+ const nameValue = targetObj?.name || keyObj.name || keyObj.key;
1292
+ if (nameValue) {
1293
+ propertyName = typeof nameValue === "string" ? nameValue : nameValue.toString();
1227
1294
  } else {
1228
1295
  const keyStr = String(propertyKey);
1229
1296
  if (keyStr !== "[object Object]") {
1230
1297
  propertyName = keyStr;
1231
- } else {
1232
- propertyName = "unknown";
1233
1298
  }
1234
1299
  }
1235
- } else {
1236
- propertyName = "unknown";
1237
1300
  }
1238
- } else {
1239
- if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1240
- propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1241
- } else if (propertyKey != null) {
1242
- const propertyKeyStr = String(propertyKey);
1243
- if (propertyKeyStr === "[object Object]") {
1244
- propertyName = "unknown";
1245
- } else {
1246
- propertyName = propertyKeyStr;
1301
+ if (targetIsObject && "kind" in targetOrContext) {
1302
+ const context = targetOrContext;
1303
+ if (context.kind && context.kind !== "field") {
1304
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1305
+ throw new Error(
1306
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1307
+ );
1247
1308
  }
1248
- } else {
1249
- propertyName = "unknown";
1250
1309
  }
1310
+ throw new Error(createBabelPluginError(propertyName));
1251
1311
  }
1252
- console.warn(
1253
- `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
1254
-
1255
- To fix this and enable compile-time processing, please:
1256
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1257
- 2. Configure it in vite.config.ts:
1258
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1259
- export default defineConfig({ plugins: [wsx()] });
1260
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1261
- npm install --save-dev @wsxjs/wsx-tsconfig
1262
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1263
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1264
-
1265
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1266
- );
1267
- if (isStage3Decorator) {
1268
- let context;
1269
- if (hasStage3Indicators) {
1270
- context = targetOrContext;
1271
- } else if (propertyKeyIsObject) {
1272
- const keyObj = propertyKey;
1273
- const targetObj = targetOrContext;
1274
- const nameValue = targetObj.name || keyObj.name || keyObj.key || "unknown";
1275
- context = {
1276
- kind: targetObj.kind || "field",
1277
- name: nameValue,
1278
- addInitializer: targetObj.addInitializer || keyObj.addInitializer,
1279
- access: targetObj.access || keyObj.access
1280
- };
1281
- } else {
1282
- context = targetOrContext;
1283
- }
1284
- if (context.kind && context.kind !== "field") {
1285
- const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1286
- throw new Error(
1287
- `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1288
- );
1289
- }
1290
- if (!context.addInitializer) {
1291
- console.warn(
1292
- `[WSX] @state decorator: addInitializer not available for property "${propertyName}". The property will not be reactive. This usually means the decorator system is not properly configured.`
1293
- );
1294
- } else {
1295
- context.addInitializer(function() {
1296
- if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1297
- throw new Error(
1298
- `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1299
-
1300
- The @state decorator can only be used in classes that extend WebComponent or LightComponent. Please ensure your component class extends one of these base classes.`
1301
- );
1302
- }
1303
- let initialValue = void 0;
1304
- if (context.access?.get) {
1305
- try {
1306
- initialValue = context.access.get();
1307
- } catch {
1308
- initialValue = this[propertyName];
1309
- }
1310
- } else {
1311
- initialValue = this[propertyName];
1312
- }
1313
- const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1314
- if (isObject) {
1315
- let reactiveValue = this.reactive(initialValue);
1316
- Object.defineProperty(this, propertyName, {
1317
- get: () => reactiveValue,
1318
- set: (newValue) => {
1319
- if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1320
- reactiveValue = this.reactive(newValue);
1321
- this.scheduleRerender();
1322
- } else {
1323
- reactiveValue = newValue;
1324
- this.scheduleRerender();
1325
- }
1326
- },
1327
- enumerable: true,
1328
- configurable: true
1329
- });
1330
- } else {
1331
- const [getState, setState] = this.useState(propertyName, initialValue);
1332
- Object.defineProperty(this, propertyName, {
1333
- get: getState,
1334
- set: setState,
1335
- enumerable: true,
1336
- configurable: true
1337
- });
1338
- }
1339
- });
1340
- }
1341
- return context;
1342
- }
1343
- const target = targetOrContext;
1344
- let normalizedPropertyKey;
1345
1312
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1346
- normalizedPropertyKey = propertyKey;
1313
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1347
1314
  } else if (propertyKey != null) {
1348
1315
  const propertyKeyStr = String(propertyKey);
1349
1316
  if (propertyKeyStr === "[object Object]") {
1350
1317
  if (typeof targetOrContext === "object" && targetOrContext !== null && ("kind" in targetOrContext || "addInitializer" in targetOrContext)) {
1351
- console.warn(
1352
- `[WSX] @state decorator: Detected potential Stage 3 decorator format but with unexpected propertyKey. This might be a compatibility issue. The decorator will attempt to work in runtime fallback mode.`
1353
- );
1354
1318
  const context = targetOrContext;
1355
1319
  if (context.name) {
1356
- const name = typeof context.name === "string" ? context.name : context.name.toString();
1357
- throw new Error(
1358
- `@state decorator: Detected Stage 3 decorator format but with invalid propertyKey. Property name: "${name}".
1359
-
1360
- This usually means the decorator is being called in an unexpected format. Please ensure you have configured the Babel plugin correctly.
1361
-
1362
- To fix this, please:
1363
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1364
- 2. Configure it in vite.config.ts:
1365
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1366
- export default defineConfig({ plugins: [wsx()] });
1367
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1368
- npm install --save-dev @wsxjs/wsx-tsconfig
1369
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1370
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1371
-
1372
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1373
- );
1320
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1374
1321
  }
1375
1322
  }
1376
- throw new Error(
1377
- `@state decorator: Invalid propertyKey detected (received object instead of string/symbol).
1378
-
1379
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1380
-
1381
- To fix this, please:
1382
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1383
- 2. Configure it in vite.config.ts:
1384
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1385
- export default defineConfig({ plugins: [wsx()] });
1386
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1387
- npm install --save-dev @wsxjs/wsx-tsconfig
1388
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1389
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1390
-
1391
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1392
- );
1323
+ } else {
1324
+ propertyName = propertyKeyStr;
1393
1325
  }
1394
- normalizedPropertyKey = propertyKeyStr;
1395
- } else {
1396
- throw new Error(
1397
- `@state decorator: propertyKey is missing. This usually means the decorator is not being called correctly. Please ensure you're using @state on a class field, not a method or other construct.`
1398
- );
1399
- }
1400
- if (target == null) {
1401
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1402
- throw new Error(
1403
- `@state decorator: Cannot access property "${propertyKeyStr}". Target is ${target === null ? "null" : "undefined"}.
1404
-
1405
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1406
-
1407
- To fix this, please:
1408
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1409
- 2. Configure it in vite.config.ts:
1410
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1411
- export default defineConfig({ plugins: [wsx()] });
1412
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1413
- npm install --save-dev @wsxjs/wsx-tsconfig
1414
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1415
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1416
-
1417
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1418
- );
1419
- }
1420
- if (typeof target !== "object") {
1421
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1422
- throw new Error(
1423
- `@state decorator: Cannot be used on "${propertyKeyStr}". @state is for properties only, not methods.`
1424
- );
1425
- }
1426
- const descriptor = Object.getOwnPropertyDescriptor(target, normalizedPropertyKey);
1427
- if (descriptor?.get) {
1428
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1429
- throw new Error(
1430
- `@state decorator cannot be used with getter properties. Property: "${propertyKeyStr}"`
1431
- );
1432
1326
  }
1327
+ throw new Error(createBabelPluginError(propertyName));
1433
1328
  }
1434
1329
  // Annotate the CommonJS export names for ESM import in node:
1435
1330
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -665,6 +665,12 @@ var WebComponent = class extends BaseComponent {
665
665
  }
666
666
  /**
667
667
  * Web Component生命周期:连接到DOM
668
+ *
669
+ * 渲染策略:
670
+ * 1. 检查 Shadow DOM 中是否已有实际内容(排除样式和 slot)
671
+ * 2. 如果有内容,先清空再渲染(避免重复元素)
672
+ * 3. 如果没有内容,直接渲染
673
+ * 4. Slot 元素会被重新添加,浏览器会自动将 Light DOM 中的内容分配到 slot
668
674
  */
669
675
  connectedCallback() {
670
676
  this.connected = true;
@@ -674,8 +680,23 @@ var WebComponent = class extends BaseComponent {
674
680
  const styleName = this.config.styleName || this.constructor.name;
675
681
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
676
682
  }
677
- const content = this.render();
678
- this.shadowRoot.appendChild(content);
683
+ const allChildren = Array.from(this.shadowRoot.children);
684
+ const styleElements = allChildren.filter((child) => child instanceof HTMLStyleElement);
685
+ const slotElements = allChildren.filter((child) => child instanceof HTMLSlotElement);
686
+ const hasErrorElement = allChildren.some(
687
+ (child) => child instanceof HTMLElement && child.style.color === "red" && child.textContent?.includes("Component Error")
688
+ );
689
+ const hasActualContent = allChildren.length > styleElements.length + slotElements.length;
690
+ if (hasActualContent && !hasErrorElement) {
691
+ } else {
692
+ this.shadowRoot.innerHTML = "";
693
+ if (stylesToApply) {
694
+ const styleName = this.config.styleName || this.constructor.name;
695
+ StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
696
+ }
697
+ const content = this.render();
698
+ this.shadowRoot.appendChild(content);
699
+ }
679
700
  this.initializeEventListeners();
680
701
  this.onConnected?.();
681
702
  } catch (error) {
@@ -788,17 +809,45 @@ var LightComponent = class extends BaseComponent {
788
809
  }
789
810
  /**
790
811
  * Web Component生命周期:连接到DOM
812
+ *
813
+ * 渲染策略:
814
+ * 1. 检查组件中是否已有实际内容(排除样式元素)
815
+ * 2. 如果有内容且完整,跳过渲染(避免重复元素和性能优化)
816
+ * 3. 如果没有内容或不完整,清空后重新渲染
817
+ * 4. 样式元素需要保留
791
818
  */
792
819
  connectedCallback() {
793
820
  this.connected = true;
794
821
  try {
795
822
  const stylesToApply = this._autoStyles || this.config.styles;
823
+ const styleName = this.config.styleName || this.constructor.name;
796
824
  if (stylesToApply) {
797
- const styleName = this.config.styleName || this.constructor.name;
798
825
  this.applyScopedStyles(styleName, stylesToApply);
799
826
  }
800
- const content = this.render();
801
- this.appendChild(content);
827
+ const styleElement = this.querySelector(
828
+ `style[data-wsx-light-component="${styleName}"]`
829
+ );
830
+ const hasErrorElement = Array.from(this.children).some(
831
+ (child) => child instanceof HTMLElement && child !== styleElement && child.style.color === "red" && child.textContent?.includes("Component Error")
832
+ );
833
+ const hasActualContent = Array.from(this.children).some(
834
+ (child) => child !== styleElement
835
+ );
836
+ if (hasActualContent && !hasErrorElement) {
837
+ if (styleElement && styleElement !== this.firstChild) {
838
+ this.insertBefore(styleElement, this.firstChild);
839
+ }
840
+ } else {
841
+ const childrenToRemove = Array.from(this.children).filter(
842
+ (child) => child !== styleElement
843
+ );
844
+ childrenToRemove.forEach((child) => child.remove());
845
+ const content = this.render();
846
+ this.appendChild(content);
847
+ if (styleElement && styleElement !== this.firstChild) {
848
+ this.insertBefore(styleElement, this.firstChild);
849
+ }
850
+ }
802
851
  this.initializeEventListeners();
803
852
  this.onConnected?.();
804
853
  } catch (error) {
@@ -973,230 +1022,76 @@ function deriveTagName(className, prefix) {
973
1022
  }
974
1023
 
975
1024
  // src/reactive-decorator.ts
1025
+ function createBabelPluginError(propertyName) {
1026
+ return `@state decorator on property "${propertyName}" MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1027
+
1028
+ The @state decorator cannot work without the Babel plugin.
1029
+
1030
+ To fix this, please:
1031
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1032
+ 2. Configure it in vite.config.ts:
1033
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1034
+ export default defineConfig({ plugins: [wsx()] });
1035
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1036
+ npm install --save-dev @wsxjs/wsx-tsconfig
1037
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1038
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1039
+
1040
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`;
1041
+ }
976
1042
  function state(targetOrContext, propertyKey) {
977
- let propertyName;
1043
+ let propertyName = "unknown";
978
1044
  const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
979
1045
  const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
980
1046
  const hasStage3Indicators = targetIsObject && ("kind" in targetOrContext || "addInitializer" in targetOrContext || "access" in targetOrContext);
981
1047
  const hasName = hasStage3Indicators && "name" in targetOrContext;
982
1048
  const isStage3Decorator = propertyKeyIsObject || hasName || hasStage3Indicators && (propertyKey === void 0 || propertyKey === null) || targetIsObject && "addInitializer" in targetOrContext;
983
1049
  if (isStage3Decorator) {
984
- const context = targetOrContext;
985
- if (context.name) {
986
- propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1050
+ if (hasStage3Indicators && targetOrContext && typeof targetOrContext === "object") {
1051
+ const context = targetOrContext;
1052
+ if (context.name) {
1053
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1054
+ }
987
1055
  } else if (propertyKeyIsObject) {
988
- if ("name" in propertyKey) {
989
- const keyObj = propertyKey;
990
- propertyName = keyObj.name ? typeof keyObj.name === "string" ? keyObj.name : keyObj.name.toString() : "unknown";
991
- } else if ("key" in propertyKey) {
992
- const keyObj = propertyKey;
993
- propertyName = keyObj.key ? typeof keyObj.key === "string" ? keyObj.key : keyObj.key.toString() : "unknown";
1056
+ const keyObj = propertyKey;
1057
+ const targetObj = targetOrContext;
1058
+ const nameValue = targetObj?.name || keyObj.name || keyObj.key;
1059
+ if (nameValue) {
1060
+ propertyName = typeof nameValue === "string" ? nameValue : nameValue.toString();
994
1061
  } else {
995
1062
  const keyStr = String(propertyKey);
996
1063
  if (keyStr !== "[object Object]") {
997
1064
  propertyName = keyStr;
998
- } else {
999
- propertyName = "unknown";
1000
1065
  }
1001
1066
  }
1002
- } else {
1003
- propertyName = "unknown";
1004
1067
  }
1005
- } else {
1006
- if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1007
- propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1008
- } else if (propertyKey != null) {
1009
- const propertyKeyStr = String(propertyKey);
1010
- if (propertyKeyStr === "[object Object]") {
1011
- propertyName = "unknown";
1012
- } else {
1013
- propertyName = propertyKeyStr;
1068
+ if (targetIsObject && "kind" in targetOrContext) {
1069
+ const context = targetOrContext;
1070
+ if (context.kind && context.kind !== "field") {
1071
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1072
+ throw new Error(
1073
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1074
+ );
1014
1075
  }
1015
- } else {
1016
- propertyName = "unknown";
1017
1076
  }
1077
+ throw new Error(createBabelPluginError(propertyName));
1018
1078
  }
1019
- console.warn(
1020
- `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
1021
-
1022
- To fix this and enable compile-time processing, please:
1023
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1024
- 2. Configure it in vite.config.ts:
1025
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1026
- export default defineConfig({ plugins: [wsx()] });
1027
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1028
- npm install --save-dev @wsxjs/wsx-tsconfig
1029
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1030
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1031
-
1032
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1033
- );
1034
- if (isStage3Decorator) {
1035
- let context;
1036
- if (hasStage3Indicators) {
1037
- context = targetOrContext;
1038
- } else if (propertyKeyIsObject) {
1039
- const keyObj = propertyKey;
1040
- const targetObj = targetOrContext;
1041
- const nameValue = targetObj.name || keyObj.name || keyObj.key || "unknown";
1042
- context = {
1043
- kind: targetObj.kind || "field",
1044
- name: nameValue,
1045
- addInitializer: targetObj.addInitializer || keyObj.addInitializer,
1046
- access: targetObj.access || keyObj.access
1047
- };
1048
- } else {
1049
- context = targetOrContext;
1050
- }
1051
- if (context.kind && context.kind !== "field") {
1052
- const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1053
- throw new Error(
1054
- `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1055
- );
1056
- }
1057
- if (!context.addInitializer) {
1058
- console.warn(
1059
- `[WSX] @state decorator: addInitializer not available for property "${propertyName}". The property will not be reactive. This usually means the decorator system is not properly configured.`
1060
- );
1061
- } else {
1062
- context.addInitializer(function() {
1063
- if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1064
- throw new Error(
1065
- `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1066
-
1067
- The @state decorator can only be used in classes that extend WebComponent or LightComponent. Please ensure your component class extends one of these base classes.`
1068
- );
1069
- }
1070
- let initialValue = void 0;
1071
- if (context.access?.get) {
1072
- try {
1073
- initialValue = context.access.get();
1074
- } catch {
1075
- initialValue = this[propertyName];
1076
- }
1077
- } else {
1078
- initialValue = this[propertyName];
1079
- }
1080
- const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1081
- if (isObject) {
1082
- let reactiveValue = this.reactive(initialValue);
1083
- Object.defineProperty(this, propertyName, {
1084
- get: () => reactiveValue,
1085
- set: (newValue) => {
1086
- if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1087
- reactiveValue = this.reactive(newValue);
1088
- this.scheduleRerender();
1089
- } else {
1090
- reactiveValue = newValue;
1091
- this.scheduleRerender();
1092
- }
1093
- },
1094
- enumerable: true,
1095
- configurable: true
1096
- });
1097
- } else {
1098
- const [getState, setState] = this.useState(propertyName, initialValue);
1099
- Object.defineProperty(this, propertyName, {
1100
- get: getState,
1101
- set: setState,
1102
- enumerable: true,
1103
- configurable: true
1104
- });
1105
- }
1106
- });
1107
- }
1108
- return context;
1109
- }
1110
- const target = targetOrContext;
1111
- let normalizedPropertyKey;
1112
1079
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1113
- normalizedPropertyKey = propertyKey;
1080
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1114
1081
  } else if (propertyKey != null) {
1115
1082
  const propertyKeyStr = String(propertyKey);
1116
1083
  if (propertyKeyStr === "[object Object]") {
1117
1084
  if (typeof targetOrContext === "object" && targetOrContext !== null && ("kind" in targetOrContext || "addInitializer" in targetOrContext)) {
1118
- console.warn(
1119
- `[WSX] @state decorator: Detected potential Stage 3 decorator format but with unexpected propertyKey. This might be a compatibility issue. The decorator will attempt to work in runtime fallback mode.`
1120
- );
1121
1085
  const context = targetOrContext;
1122
1086
  if (context.name) {
1123
- const name = typeof context.name === "string" ? context.name : context.name.toString();
1124
- throw new Error(
1125
- `@state decorator: Detected Stage 3 decorator format but with invalid propertyKey. Property name: "${name}".
1126
-
1127
- This usually means the decorator is being called in an unexpected format. Please ensure you have configured the Babel plugin correctly.
1128
-
1129
- To fix this, please:
1130
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1131
- 2. Configure it in vite.config.ts:
1132
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1133
- export default defineConfig({ plugins: [wsx()] });
1134
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1135
- npm install --save-dev @wsxjs/wsx-tsconfig
1136
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1137
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1138
-
1139
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1140
- );
1087
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1141
1088
  }
1142
1089
  }
1143
- throw new Error(
1144
- `@state decorator: Invalid propertyKey detected (received object instead of string/symbol).
1145
-
1146
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1147
-
1148
- To fix this, please:
1149
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1150
- 2. Configure it in vite.config.ts:
1151
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1152
- export default defineConfig({ plugins: [wsx()] });
1153
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1154
- npm install --save-dev @wsxjs/wsx-tsconfig
1155
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1156
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1157
-
1158
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1159
- );
1090
+ } else {
1091
+ propertyName = propertyKeyStr;
1160
1092
  }
1161
- normalizedPropertyKey = propertyKeyStr;
1162
- } else {
1163
- throw new Error(
1164
- `@state decorator: propertyKey is missing. This usually means the decorator is not being called correctly. Please ensure you're using @state on a class field, not a method or other construct.`
1165
- );
1166
- }
1167
- if (target == null) {
1168
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1169
- throw new Error(
1170
- `@state decorator: Cannot access property "${propertyKeyStr}". Target is ${target === null ? "null" : "undefined"}.
1171
-
1172
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1173
-
1174
- To fix this, please:
1175
- 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1176
- 2. Configure it in vite.config.ts:
1177
- import { wsx } from '@wsxjs/wsx-vite-plugin';
1178
- export default defineConfig({ plugins: [wsx()] });
1179
- 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1180
- npm install --save-dev @wsxjs/wsx-tsconfig
1181
- Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1182
- Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1183
-
1184
- See: https://github.com/wsxjs/wsxjs#setup for more details.`
1185
- );
1186
- }
1187
- if (typeof target !== "object") {
1188
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1189
- throw new Error(
1190
- `@state decorator: Cannot be used on "${propertyKeyStr}". @state is for properties only, not methods.`
1191
- );
1192
- }
1193
- const descriptor = Object.getOwnPropertyDescriptor(target, normalizedPropertyKey);
1194
- if (descriptor?.get) {
1195
- const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
1196
- throw new Error(
1197
- `@state decorator cannot be used with getter properties. Property: "${propertyKeyStr}"`
1198
- );
1199
1093
  }
1094
+ throw new Error(createBabelPluginError(propertyName));
1200
1095
  }
1201
1096
  export {
1202
1097
  Fragment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-core",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Core WSX Framework - Web Components with JSX syntax",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -119,7 +119,7 @@ export abstract class BaseComponent extends HTMLElement {
119
119
  *
120
120
  * @returns JSX元素
121
121
  */
122
- abstract render(): HTMLElement;
122
+ abstract render(): HTMLElement | SVGElement;
123
123
 
124
124
  /**
125
125
  * 可选生命周期钩子:组件已连接
@@ -34,10 +34,16 @@ export abstract class LightComponent extends BaseComponent {
34
34
  *
35
35
  * @returns JSX元素
36
36
  */
37
- abstract render(): HTMLElement;
37
+ abstract render(): HTMLElement | SVGElement;
38
38
 
39
39
  /**
40
40
  * Web Component生命周期:连接到DOM
41
+ *
42
+ * 渲染策略:
43
+ * 1. 检查组件中是否已有实际内容(排除样式元素)
44
+ * 2. 如果有内容且完整,跳过渲染(避免重复元素和性能优化)
45
+ * 3. 如果没有内容或不完整,清空后重新渲染
46
+ * 4. 样式元素需要保留
41
47
  */
42
48
  connectedCallback(): void {
43
49
  this.connected = true;
@@ -48,19 +54,57 @@ export abstract class LightComponent extends BaseComponent {
48
54
  // So we need to check _autoStyles directly first, then fallback to config.styles getter
49
55
  // The getter will dynamically check _autoStyles when accessed
50
56
  const stylesToApply = this._autoStyles || this.config.styles;
57
+ const styleName = this.config.styleName || this.constructor.name;
51
58
  if (stylesToApply) {
52
- const styleName = this.config.styleName || this.constructor.name;
53
59
  this.applyScopedStyles(styleName, stylesToApply);
54
60
  }
55
61
 
56
- // 渲染JSX内容到Light DOM
57
- const content = this.render();
58
- this.appendChild(content);
62
+ // 检查是否有实际内容(排除样式元素)
63
+ // 错误元素:如果存在错误信息,需要重新渲染以恢复正常
64
+ const styleElement = this.querySelector(
65
+ `style[data-wsx-light-component="${styleName}"]`
66
+ ) as HTMLStyleElement | null;
67
+ const hasErrorElement = Array.from(this.children).some(
68
+ (child) =>
69
+ child instanceof HTMLElement &&
70
+ child !== styleElement &&
71
+ child.style.color === "red" &&
72
+ child.textContent?.includes("Component Error")
73
+ );
74
+ const hasActualContent = Array.from(this.children).some(
75
+ (child) => child !== styleElement
76
+ );
77
+
78
+ // 如果有错误元素,需要重新渲染以恢复正常
79
+ // 如果有实际内容且没有错误,跳过渲染(避免重复元素)
80
+ if (hasActualContent && !hasErrorElement) {
81
+ // 已经有内容且没有错误,跳过渲染(避免重复元素)
82
+ // 但确保样式元素在正确位置
83
+ if (styleElement && styleElement !== this.firstChild) {
84
+ this.insertBefore(styleElement, this.firstChild);
85
+ }
86
+ } else {
87
+ // 没有内容,需要渲染
88
+ // 清空旧内容(保留样式元素)
89
+ const childrenToRemove = Array.from(this.children).filter(
90
+ (child) => child !== styleElement
91
+ );
92
+ childrenToRemove.forEach((child) => child.remove());
93
+
94
+ // 渲染JSX内容到Light DOM
95
+ const content = this.render();
96
+ this.appendChild(content);
97
+
98
+ // 确保样式元素在第一个位置(如果存在)
99
+ if (styleElement && styleElement !== this.firstChild) {
100
+ this.insertBefore(styleElement, this.firstChild);
101
+ }
102
+ }
59
103
 
60
- // 初始化事件监听器
104
+ // 初始化事件监听器(无论是否渲染,都需要重新初始化,因为 DOM 可能被移动)
61
105
  this.initializeEventListeners();
62
106
 
63
- // 调用子类的初始化钩子
107
+ // 调用子类的初始化钩子(无论是否渲染,都需要调用,因为组件已连接)
64
108
  this.onConnected?.();
65
109
  } catch (error) {
66
110
  logger.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
@@ -101,7 +101,7 @@ function unwrapProxy(value: unknown): unknown {
101
101
  // 直接访问原始对象的属性,不通过 Proxy
102
102
  for (const key in original) {
103
103
  if (Object.prototype.hasOwnProperty.call(original, key)) {
104
- const propValue = original[key];
104
+ const propValue = (original as Record<string, unknown>)[key];
105
105
  // 如果属性值是 Proxy,先获取原始对象再递归
106
106
  if (
107
107
  propValue != null &&
@@ -40,25 +40,66 @@ export abstract class WebComponent extends BaseComponent {
40
40
  *
41
41
  * @returns JSX元素
42
42
  */
43
- abstract render(): HTMLElement;
43
+ abstract render(): HTMLElement | SVGElement;
44
44
 
45
45
  /**
46
46
  * Web Component生命周期:连接到DOM
47
+ *
48
+ * 渲染策略:
49
+ * 1. 检查 Shadow DOM 中是否已有实际内容(排除样式和 slot)
50
+ * 2. 如果有内容,先清空再渲染(避免重复元素)
51
+ * 3. 如果没有内容,直接渲染
52
+ * 4. Slot 元素会被重新添加,浏览器会自动将 Light DOM 中的内容分配到 slot
47
53
  */
48
54
  connectedCallback(): void {
49
55
  this.connected = true;
50
56
  try {
51
- // 应用CSS样式到Shadow DOM
52
- // Check both _autoStyles getter and config.styles getter
57
+ // 应用CSS样式到Shadow DOM(先应用,因为样式可能使用 fallback 添加 style 元素)
53
58
  const stylesToApply = this._autoStyles || this.config.styles;
54
59
  if (stylesToApply) {
55
60
  const styleName = this.config.styleName || this.constructor.name;
56
61
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
57
62
  }
58
63
 
59
- // 渲染JSX内容到Shadow DOM
60
- const content = this.render();
61
- this.shadowRoot.appendChild(content);
64
+ // 检查是否有实际内容(排除样式和 slot)
65
+ // 样式元素:可能由 StyleManager fallback 添加
66
+ // Slot 元素:不算"内容",因为 slot 的内容在 Light DOM 中
67
+ // 错误元素:如果存在错误信息,需要重新渲染以恢复正常
68
+ const allChildren = Array.from(this.shadowRoot.children);
69
+ const styleElements = allChildren.filter((child) => child instanceof HTMLStyleElement);
70
+ const slotElements = allChildren.filter((child) => child instanceof HTMLSlotElement);
71
+ const hasErrorElement = allChildren.some(
72
+ (child) =>
73
+ child instanceof HTMLElement &&
74
+ child.style.color === "red" &&
75
+ child.textContent?.includes("Component Error")
76
+ );
77
+ const hasActualContent =
78
+ allChildren.length > styleElements.length + slotElements.length;
79
+
80
+ // 如果有错误元素,需要重新渲染以恢复正常
81
+ // 如果有实际内容且没有错误,跳过渲染(避免重复元素)
82
+ if (hasActualContent && !hasErrorElement) {
83
+ // 已经有内容且没有错误,跳过渲染(避免重复元素)
84
+ // 样式已在上方应用(StyleManager.applyStyles 是幂等的)
85
+ // Slot 元素已存在,浏览器会自动将 Light DOM 中的内容分配到 slot
86
+ } else {
87
+ // 没有内容,需要渲染
88
+ // 清空 Shadow DOM(包括可能的旧内容)
89
+ this.shadowRoot.innerHTML = "";
90
+
91
+ // 重新应用样式(因为上面清空了)
92
+ if (stylesToApply) {
93
+ const styleName = this.config.styleName || this.constructor.name;
94
+ StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
95
+ }
96
+
97
+ // 渲染JSX内容到Shadow DOM
98
+ // render() 应该返回包含 slot 元素的内容(如果需要)
99
+ // 浏览器会自动将 Light DOM 中的内容分配到 slot
100
+ const content = this.render();
101
+ this.shadowRoot.appendChild(content);
102
+ }
62
103
 
63
104
  // 初始化事件监听器
64
105
  this.initializeEventListeners();
@@ -1,221 +0,0 @@
1
- // src/utils/svg-utils.ts
2
- var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
3
- var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
4
- // 结构元素 (Structural elements)
5
- "svg",
6
- "defs",
7
- "g",
8
- "symbol",
9
- "use",
10
- // 图形元素 (Graphics elements)
11
- "circle",
12
- "ellipse",
13
- "line",
14
- "path",
15
- "polygon",
16
- "polyline",
17
- "rect",
18
- // 文本元素 (Text elements)
19
- "textPath",
20
- "tspan",
21
- // 渐变和模式 (Gradients and patterns)
22
- "linearGradient",
23
- "radialGradient",
24
- "stop",
25
- "pattern",
26
- // 滤镜 (Filter elements)
27
- "filter",
28
- "feBlend",
29
- "feColorMatrix",
30
- "feComponentTransfer",
31
- "feComposite",
32
- "feConvolveMatrix",
33
- "feDiffuseLighting",
34
- "feDisplacementMap",
35
- "feDistantLight",
36
- "feDropShadow",
37
- "feFlood",
38
- "feFuncA",
39
- "feFuncB",
40
- "feFuncG",
41
- "feFuncR",
42
- "feGaussianBlur",
43
- "feImage",
44
- "feMerge",
45
- "feMergeNode",
46
- "feMorphology",
47
- "feOffset",
48
- "fePointLight",
49
- "feSpecularLighting",
50
- "feSpotLight",
51
- "feTile",
52
- "feTurbulence",
53
- // 动画元素 (Animation elements)
54
- "animate",
55
- "animateMotion",
56
- "animateTransform",
57
- "set",
58
- // 其他元素 (Other elements)
59
- "clipPath",
60
- "foreignObject",
61
- "marker",
62
- "mask",
63
- "metadata",
64
- "switch",
65
- "desc"
66
- ]);
67
- var DUAL_ELEMENTS = /* @__PURE__ */ new Set(["image", "style", "title", "text"]);
68
- var FORCE_HTML_ELEMENTS = /* @__PURE__ */ new Set(["a"]);
69
- var SVG_ELEMENTS = /* @__PURE__ */ new Set([
70
- ...SVG_ONLY_ELEMENTS,
71
- ...DUAL_ELEMENTS,
72
- ...FORCE_HTML_ELEMENTS
73
- ]);
74
- var svgContext = false;
75
- function isSVGOnlyElement(tagName) {
76
- return SVG_ONLY_ELEMENTS.has(tagName);
77
- }
78
- function isDualElement(tagName) {
79
- return DUAL_ELEMENTS.has(tagName);
80
- }
81
- function isForceHTMLElement(tagName) {
82
- return FORCE_HTML_ELEMENTS.has(tagName);
83
- }
84
- function setSVGContext(inSVG) {
85
- svgContext = inSVG;
86
- }
87
- function createElement(tagName) {
88
- if (isForceHTMLElement(tagName)) {
89
- return document.createElement(tagName);
90
- }
91
- if (isSVGOnlyElement(tagName)) {
92
- setSVGContext(true);
93
- return document.createElementNS(SVG_NAMESPACE, tagName);
94
- }
95
- if (isDualElement(tagName)) {
96
- if (svgContext) {
97
- return document.createElementNS(SVG_NAMESPACE, tagName);
98
- }
99
- }
100
- return document.createElement(tagName);
101
- }
102
- function shouldUseSVGNamespace(tagName) {
103
- return isSVGOnlyElement(tagName) || isDualElement(tagName) && svgContext;
104
- }
105
- var SVG_ATTRIBUTE_MAP = /* @__PURE__ */ new Map([
106
- ["className", "class"],
107
- ["htmlFor", "for"]
108
- ]);
109
- function getSVGAttributeName(attributeName) {
110
- return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
111
- }
112
-
113
- // src/jsx-factory.ts
114
- function h(tag, props = {}, ...children) {
115
- if (typeof tag === "function") {
116
- return tag(props, children);
117
- }
118
- const element = createElement(tag);
119
- if (props) {
120
- const isSVG = shouldUseSVGNamespace(tag);
121
- Object.entries(props).forEach(([key, value]) => {
122
- if (value === null || value === void 0 || value === false) {
123
- return;
124
- }
125
- if (key === "ref" && typeof value === "function") {
126
- value(element);
127
- } else if (key === "className" || key === "class") {
128
- if (isSVG) {
129
- element.setAttribute("class", value);
130
- } else {
131
- element.className = value;
132
- }
133
- } else if (key === "style" && typeof value === "string") {
134
- element.setAttribute("style", value);
135
- } else if (key.startsWith("on") && typeof value === "function") {
136
- const eventName = key.slice(2).toLowerCase();
137
- element.addEventListener(eventName, value);
138
- } else if (typeof value === "boolean") {
139
- if (value) {
140
- element.setAttribute(key, "");
141
- }
142
- } else {
143
- const attributeName = isSVG ? getSVGAttributeName(key) : key;
144
- element.setAttribute(attributeName, String(value));
145
- }
146
- });
147
- }
148
- const flatChildren = flattenChildren(children);
149
- flatChildren.forEach((child) => {
150
- if (child === null || child === void 0 || child === false) {
151
- return;
152
- }
153
- if (typeof child === "string" || typeof child === "number") {
154
- element.appendChild(document.createTextNode(String(child)));
155
- } else if (child instanceof HTMLElement || child instanceof SVGElement) {
156
- element.appendChild(child);
157
- } else if (child instanceof DocumentFragment) {
158
- element.appendChild(child);
159
- }
160
- });
161
- return element;
162
- }
163
- function flattenChildren(children) {
164
- const result = [];
165
- for (const child of children) {
166
- if (child === null || child === void 0 || child === false) {
167
- continue;
168
- } else if (Array.isArray(child)) {
169
- result.push(...flattenChildren(child));
170
- } else {
171
- result.push(child);
172
- }
173
- }
174
- return result;
175
- }
176
- function Fragment(_props, children) {
177
- const fragment = document.createDocumentFragment();
178
- const flatChildren = flattenChildren(children);
179
- flatChildren.forEach((child) => {
180
- if (child === null || child === void 0 || child === false) {
181
- return;
182
- }
183
- if (typeof child === "string" || typeof child === "number") {
184
- fragment.appendChild(document.createTextNode(String(child)));
185
- } else if (child instanceof HTMLElement || child instanceof SVGElement) {
186
- fragment.appendChild(child);
187
- } else if (child instanceof DocumentFragment) {
188
- fragment.appendChild(child);
189
- }
190
- });
191
- return fragment;
192
- }
193
- function jsx(tag, props) {
194
- if (!props) {
195
- return h(tag, null);
196
- }
197
- const { children, ...restProps } = props;
198
- if (children !== void 0) {
199
- return h(tag, restProps, children);
200
- }
201
- return h(tag, restProps);
202
- }
203
- function jsxs(tag, props) {
204
- if (!props) {
205
- return h(tag, null);
206
- }
207
- const { children, ...restProps } = props;
208
- if (Array.isArray(children)) {
209
- return h(tag, restProps, ...children);
210
- } else if (children !== void 0) {
211
- return h(tag, restProps, children);
212
- }
213
- return h(tag, restProps);
214
- }
215
-
216
- export {
217
- h,
218
- Fragment,
219
- jsx,
220
- jsxs
221
- };
@@ -1,222 +0,0 @@
1
- // src/utils/svg-utils.ts
2
- var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
3
- var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
4
- // 结构元素 (Structural elements)
5
- "svg",
6
- "defs",
7
- "g",
8
- "symbol",
9
- "use",
10
- // 图形元素 (Graphics elements)
11
- "circle",
12
- "ellipse",
13
- "line",
14
- "path",
15
- "polygon",
16
- "polyline",
17
- "rect",
18
- // 文本元素 (Text elements)
19
- "textPath",
20
- "tspan",
21
- // 渐变和模式 (Gradients and patterns)
22
- "linearGradient",
23
- "radialGradient",
24
- "stop",
25
- "pattern",
26
- // 滤镜 (Filter elements)
27
- "filter",
28
- "feBlend",
29
- "feColorMatrix",
30
- "feComponentTransfer",
31
- "feComposite",
32
- "feConvolveMatrix",
33
- "feDiffuseLighting",
34
- "feDisplacementMap",
35
- "feDistantLight",
36
- "feDropShadow",
37
- "feFlood",
38
- "feFuncA",
39
- "feFuncB",
40
- "feFuncG",
41
- "feFuncR",
42
- "feGaussianBlur",
43
- "feImage",
44
- "feMerge",
45
- "feMergeNode",
46
- "feMorphology",
47
- "feOffset",
48
- "fePointLight",
49
- "feSpecularLighting",
50
- "feSpotLight",
51
- "feTile",
52
- "feTurbulence",
53
- // 动画元素 (Animation elements)
54
- "animate",
55
- "animateMotion",
56
- "animateTransform",
57
- "set",
58
- // 其他元素 (Other elements)
59
- "clipPath",
60
- "foreignObject",
61
- "marker",
62
- "mask",
63
- "metadata",
64
- "switch",
65
- "desc"
66
- ]);
67
- var DUAL_ELEMENTS = /* @__PURE__ */ new Set(["image", "style", "title", "text"]);
68
- var FORCE_HTML_ELEMENTS = /* @__PURE__ */ new Set(["a"]);
69
- var SVG_ELEMENTS = /* @__PURE__ */ new Set([
70
- ...SVG_ONLY_ELEMENTS,
71
- ...DUAL_ELEMENTS,
72
- ...FORCE_HTML_ELEMENTS
73
- ]);
74
- var svgContext = false;
75
- function isSVGOnlyElement(tagName) {
76
- return SVG_ONLY_ELEMENTS.has(tagName);
77
- }
78
- function isDualElement(tagName) {
79
- return DUAL_ELEMENTS.has(tagName);
80
- }
81
- function isForceHTMLElement(tagName) {
82
- return FORCE_HTML_ELEMENTS.has(tagName);
83
- }
84
- function setSVGContext(inSVG) {
85
- svgContext = inSVG;
86
- }
87
- function createElement(tagName) {
88
- if (isForceHTMLElement(tagName)) {
89
- return document.createElement(tagName);
90
- }
91
- if (isSVGOnlyElement(tagName)) {
92
- setSVGContext(true);
93
- return document.createElementNS(SVG_NAMESPACE, tagName);
94
- }
95
- if (isDualElement(tagName)) {
96
- if (svgContext) {
97
- return document.createElementNS(SVG_NAMESPACE, tagName);
98
- }
99
- }
100
- return document.createElement(tagName);
101
- }
102
- function shouldUseSVGNamespace(tagName) {
103
- return isSVGOnlyElement(tagName) || isDualElement(tagName) && svgContext;
104
- }
105
- var SVG_ATTRIBUTE_MAP = /* @__PURE__ */ new Map([
106
- ["className", "class"],
107
- ["htmlFor", "for"]
108
- ]);
109
- function getSVGAttributeName(attributeName) {
110
- return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
111
- }
112
-
113
- // src/jsx-factory.ts
114
- function h(tag, props = {}, ...children) {
115
- if (typeof tag === "function") {
116
- return tag(props, children);
117
- }
118
- const element = createElement(tag);
119
- if (props) {
120
- const isSVG = shouldUseSVGNamespace(tag);
121
- Object.entries(props).forEach(([key, value]) => {
122
- if (value === null || value === void 0 || value === false) {
123
- return;
124
- }
125
- if (key === "ref" && typeof value === "function") {
126
- value(element);
127
- } else if (key === "className" || key === "class") {
128
- if (isSVG) {
129
- element.setAttribute("class", value);
130
- } else {
131
- element.className = value;
132
- }
133
- } else if (key === "style" && typeof value === "string") {
134
- element.setAttribute("style", value);
135
- } else if (key.startsWith("on") && typeof value === "function") {
136
- const eventName = key.slice(2).toLowerCase();
137
- element.addEventListener(eventName, value);
138
- } else if (typeof value === "boolean") {
139
- if (value) {
140
- element.setAttribute(key, "");
141
- }
142
- } else {
143
- const attributeName = isSVG ? getSVGAttributeName(key) : key;
144
- element.setAttribute(attributeName, String(value));
145
- }
146
- });
147
- }
148
- const flatChildren = flattenChildren(children);
149
- flatChildren.forEach((child) => {
150
- if (child === null || child === void 0 || child === false) {
151
- return;
152
- }
153
- if (typeof child === "string" || typeof child === "number") {
154
- element.appendChild(document.createTextNode(String(child)));
155
- } else if (child instanceof HTMLElement || child instanceof SVGElement) {
156
- element.appendChild(child);
157
- } else if (child instanceof DocumentFragment) {
158
- element.appendChild(child);
159
- }
160
- });
161
- return element;
162
- }
163
- function flattenChildren(children) {
164
- const result = [];
165
- for (const child of children) {
166
- if (child === null || child === void 0 || child === false) {
167
- continue;
168
- } else if (Array.isArray(child)) {
169
- result.push(...flattenChildren(child));
170
- } else {
171
- result.push(child);
172
- }
173
- }
174
- return result;
175
- }
176
- function Fragment(_props, children) {
177
- const fragment = document.createDocumentFragment();
178
- const flatChildren = flattenChildren(children);
179
- flatChildren.forEach((child) => {
180
- if (child === null || child === void 0 || child === false) {
181
- return;
182
- }
183
- if (typeof child === "string" || typeof child === "number") {
184
- fragment.appendChild(document.createTextNode(String(child)));
185
- } else if (child instanceof HTMLElement || child instanceof SVGElement) {
186
- fragment.appendChild(child);
187
- } else if (child instanceof DocumentFragment) {
188
- fragment.appendChild(child);
189
- }
190
- });
191
- return fragment;
192
- }
193
- function jsx(tag, props) {
194
- if (!props) {
195
- return h(tag, null);
196
- }
197
- const { children, ...restProps } = props;
198
- if (children !== void 0 && children !== null) {
199
- const childrenArray = Array.isArray(children) ? children : [children];
200
- return h(tag, restProps, ...childrenArray);
201
- }
202
- return h(tag, restProps);
203
- }
204
- function jsxs(tag, props) {
205
- if (!props) {
206
- return h(tag, null);
207
- }
208
- const { children, ...restProps } = props;
209
- if (Array.isArray(children)) {
210
- return h(tag, restProps, ...children);
211
- } else if (children !== void 0 && children !== null) {
212
- return h(tag, restProps, children);
213
- }
214
- return h(tag, restProps);
215
- }
216
-
217
- export {
218
- h,
219
- Fragment,
220
- jsx,
221
- jsxs
222
- };