@wsxjs/wsx-core 0.0.6 → 0.0.8
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/LICENSE +21 -0
- package/dist/index.js +299 -411
- package/dist/index.mjs +297 -404
- package/package.json +46 -46
- package/src/base-component.ts +239 -0
- package/src/index.ts +2 -4
- package/src/light-component.ts +38 -165
- package/src/reactive-decorator.ts +105 -0
- package/src/web-component.ts +157 -110
- package/types/index.d.ts +2 -2
- package/types/wsx-types.d.ts +4 -2
- package/src/reactive-component.ts +0 -306
package/dist/index.js
CHANGED
|
@@ -22,22 +22,17 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Fragment: () => Fragment,
|
|
24
24
|
LightComponent: () => LightComponent,
|
|
25
|
-
ReactiveDebug: () => ReactiveDebug,
|
|
26
|
-
ReactiveWebComponent: () => ReactiveWebComponent,
|
|
27
25
|
StyleManager: () => StyleManager,
|
|
28
26
|
WSXLogger: () => WSXLogger,
|
|
29
27
|
WebComponent: () => WebComponent,
|
|
30
28
|
autoRegister: () => autoRegister,
|
|
31
29
|
createLogger: () => createLogger,
|
|
32
|
-
createReactiveComponent: () => createReactiveComponent,
|
|
33
|
-
createState: () => createState,
|
|
34
30
|
h: () => h,
|
|
35
31
|
jsx: () => h,
|
|
36
32
|
jsxs: () => h,
|
|
37
33
|
logger: () => logger,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
registerComponent: () => registerComponent
|
|
34
|
+
registerComponent: () => registerComponent,
|
|
35
|
+
state: () => state
|
|
41
36
|
});
|
|
42
37
|
module.exports = __toCommonJS(index_exports);
|
|
43
38
|
|
|
@@ -274,172 +269,6 @@ var StyleManager = class {
|
|
|
274
269
|
};
|
|
275
270
|
StyleManager.styleSheets = /* @__PURE__ */ new Map();
|
|
276
271
|
|
|
277
|
-
// src/web-component.ts
|
|
278
|
-
var WebComponent = class extends HTMLElement {
|
|
279
|
-
constructor(config = {}) {
|
|
280
|
-
super();
|
|
281
|
-
this.connected = false;
|
|
282
|
-
this.config = config;
|
|
283
|
-
this.attachShadow({ mode: "open" });
|
|
284
|
-
if (config.styles) {
|
|
285
|
-
const styleName = config.styleName || this.constructor.name;
|
|
286
|
-
StyleManager.applyStyles(this.shadowRoot, styleName, config.styles);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* 子类应该重写这个方法来定义观察的属性
|
|
291
|
-
* @returns 要观察的属性名数组
|
|
292
|
-
*/
|
|
293
|
-
static get observedAttributes() {
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Web Component生命周期:连接到DOM
|
|
298
|
-
*/
|
|
299
|
-
connectedCallback() {
|
|
300
|
-
this.connected = true;
|
|
301
|
-
try {
|
|
302
|
-
const content = this.render();
|
|
303
|
-
this.shadowRoot.appendChild(content);
|
|
304
|
-
this.onConnected?.();
|
|
305
|
-
} catch (error) {
|
|
306
|
-
console.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
307
|
-
this.renderError(error);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Web Component生命周期:从DOM断开
|
|
312
|
-
*/
|
|
313
|
-
disconnectedCallback() {
|
|
314
|
-
this.onDisconnected?.();
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Web Component生命周期:属性变化
|
|
318
|
-
*/
|
|
319
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
320
|
-
this.onAttributeChanged?.(name, oldValue, newValue);
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* 查找Shadow DOM内的元素
|
|
324
|
-
*
|
|
325
|
-
* @param selector - CSS选择器
|
|
326
|
-
* @returns 元素或null
|
|
327
|
-
*/
|
|
328
|
-
querySelector(selector) {
|
|
329
|
-
return this.shadowRoot.querySelector(selector);
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* 查找Shadow DOM内的所有匹配元素
|
|
333
|
-
*
|
|
334
|
-
* @param selector - CSS选择器
|
|
335
|
-
* @returns 元素列表
|
|
336
|
-
*/
|
|
337
|
-
querySelectorAll(selector) {
|
|
338
|
-
return this.shadowRoot.querySelectorAll(selector);
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* 重新渲染组件
|
|
342
|
-
*/
|
|
343
|
-
rerender() {
|
|
344
|
-
if (!this.connected) {
|
|
345
|
-
console.warn(
|
|
346
|
-
`[${this.constructor.name}] Component is not connected, skipping rerender.`
|
|
347
|
-
);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
const adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets || [];
|
|
351
|
-
this.shadowRoot.innerHTML = "";
|
|
352
|
-
if (this.shadowRoot.adoptedStyleSheets) {
|
|
353
|
-
this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
|
|
354
|
-
}
|
|
355
|
-
if (adoptedStyleSheets.length === 0 && this.config.styles) {
|
|
356
|
-
const styleName = this.config.styleName || this.constructor.name;
|
|
357
|
-
StyleManager.applyStyles(this.shadowRoot, styleName, this.config.styles);
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
const content = this.render();
|
|
361
|
-
this.shadowRoot.appendChild(content);
|
|
362
|
-
} catch (error) {
|
|
363
|
-
console.error(`[${this.constructor.name}] Error in rerender:`, error);
|
|
364
|
-
this.renderError(error);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* 渲染错误信息
|
|
369
|
-
*
|
|
370
|
-
* @param error - 错误对象
|
|
371
|
-
*/
|
|
372
|
-
renderError(error) {
|
|
373
|
-
this.shadowRoot.innerHTML = "";
|
|
374
|
-
const errorElement = h(
|
|
375
|
-
"div",
|
|
376
|
-
{
|
|
377
|
-
style: "color: red; padding: 10px; border: 1px solid red; background: #ffe6e6; font-family: monospace;"
|
|
378
|
-
},
|
|
379
|
-
[
|
|
380
|
-
h("strong", {}, `[${this.constructor.name}] Component Error:`),
|
|
381
|
-
h("pre", { style: "margin: 10px 0; white-space: pre-wrap;" }, String(error))
|
|
382
|
-
]
|
|
383
|
-
);
|
|
384
|
-
this.shadowRoot.appendChild(errorElement);
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* 获取配置值
|
|
388
|
-
*
|
|
389
|
-
* @param key - 配置键
|
|
390
|
-
* @param defaultValue - 默认值
|
|
391
|
-
* @returns 配置值
|
|
392
|
-
*/
|
|
393
|
-
getConfig(key, defaultValue) {
|
|
394
|
-
return this.config[key] ?? defaultValue;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* 设置配置值
|
|
398
|
-
*
|
|
399
|
-
* @param key - 配置键
|
|
400
|
-
* @param value - 配置值
|
|
401
|
-
*/
|
|
402
|
-
setConfig(key, value) {
|
|
403
|
-
this.config[key] = value;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* 获取属性值
|
|
407
|
-
*
|
|
408
|
-
* @param name - 属性名
|
|
409
|
-
* @param defaultValue - 默认值
|
|
410
|
-
* @returns 属性值
|
|
411
|
-
*/
|
|
412
|
-
getAttr(name, defaultValue = "") {
|
|
413
|
-
return this.getAttribute(name) || defaultValue;
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* 设置属性值
|
|
417
|
-
*
|
|
418
|
-
* @param name - 属性名
|
|
419
|
-
* @param value - 属性值
|
|
420
|
-
*/
|
|
421
|
-
setAttr(name, value) {
|
|
422
|
-
this.setAttribute(name, value);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* 移除属性
|
|
426
|
-
*
|
|
427
|
-
* @param name - 属性名
|
|
428
|
-
*/
|
|
429
|
-
removeAttr(name) {
|
|
430
|
-
this.removeAttribute(name);
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* 检查是否有属性
|
|
434
|
-
*
|
|
435
|
-
* @param name - 属性名
|
|
436
|
-
* @returns 是否存在
|
|
437
|
-
*/
|
|
438
|
-
hasAttr(name) {
|
|
439
|
-
return this.hasAttribute(name);
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
|
|
443
272
|
// src/utils/logger.ts
|
|
444
273
|
var WSXLogger = class {
|
|
445
274
|
constructor(prefix = "[WSX]", enabled = true, level = "info") {
|
|
@@ -606,16 +435,26 @@ function reactiveWithDebug(obj, onChange, debugName) {
|
|
|
606
435
|
});
|
|
607
436
|
}
|
|
608
437
|
|
|
609
|
-
// src/
|
|
610
|
-
var
|
|
611
|
-
var LightComponent = class extends HTMLElement {
|
|
438
|
+
// src/base-component.ts
|
|
439
|
+
var BaseComponent = class extends HTMLElement {
|
|
612
440
|
constructor(config = {}) {
|
|
613
441
|
super();
|
|
614
442
|
this.connected = false;
|
|
615
443
|
this._isDebugEnabled = false;
|
|
616
444
|
this._reactiveStates = /* @__PURE__ */ new Map();
|
|
617
|
-
this.config = config;
|
|
618
445
|
this._isDebugEnabled = config.debug ?? false;
|
|
446
|
+
const host = this;
|
|
447
|
+
const originalStyles = config.styles;
|
|
448
|
+
this.config = {
|
|
449
|
+
...config,
|
|
450
|
+
get styles() {
|
|
451
|
+
const result = originalStyles || host._autoStyles || "";
|
|
452
|
+
return result;
|
|
453
|
+
},
|
|
454
|
+
set styles(value) {
|
|
455
|
+
config.styles = value;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
619
458
|
}
|
|
620
459
|
/**
|
|
621
460
|
* 子类应该重写这个方法来定义观察的属性
|
|
@@ -624,56 +463,12 @@ var LightComponent = class extends HTMLElement {
|
|
|
624
463
|
static get observedAttributes() {
|
|
625
464
|
return [];
|
|
626
465
|
}
|
|
627
|
-
/**
|
|
628
|
-
* Web Component生命周期:连接到DOM
|
|
629
|
-
*/
|
|
630
|
-
connectedCallback() {
|
|
631
|
-
this.connected = true;
|
|
632
|
-
try {
|
|
633
|
-
if (this.config.styles) {
|
|
634
|
-
const styleName = this.config.styleName || this.constructor.name;
|
|
635
|
-
this.applyScopedStyles(styleName, this.config.styles);
|
|
636
|
-
}
|
|
637
|
-
const content = this.render();
|
|
638
|
-
this.appendChild(content);
|
|
639
|
-
this.onConnected?.();
|
|
640
|
-
} catch (error) {
|
|
641
|
-
logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
642
|
-
this.renderError(error);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* Web Component生命周期:从DOM断开
|
|
647
|
-
*/
|
|
648
|
-
disconnectedCallback() {
|
|
649
|
-
this.cleanupReactiveStates();
|
|
650
|
-
this.cleanupStyles();
|
|
651
|
-
this.onDisconnected?.();
|
|
652
|
-
}
|
|
653
466
|
/**
|
|
654
467
|
* Web Component生命周期:属性变化
|
|
655
468
|
*/
|
|
656
469
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
657
470
|
this.onAttributeChanged?.(name, oldValue, newValue);
|
|
658
471
|
}
|
|
659
|
-
/**
|
|
660
|
-
* 查找组件内的元素
|
|
661
|
-
*
|
|
662
|
-
* @param selector - CSS选择器
|
|
663
|
-
* @returns 元素或null
|
|
664
|
-
*/
|
|
665
|
-
querySelector(selector) {
|
|
666
|
-
return HTMLElement.prototype.querySelector.call(this, selector);
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* 查找组件内的所有匹配元素
|
|
670
|
-
*
|
|
671
|
-
* @param selector - CSS选择器
|
|
672
|
-
* @returns 元素列表
|
|
673
|
-
*/
|
|
674
|
-
querySelectorAll(selector) {
|
|
675
|
-
return HTMLElement.prototype.querySelectorAll.call(this, selector);
|
|
676
|
-
}
|
|
677
472
|
/**
|
|
678
473
|
* 创建响应式对象
|
|
679
474
|
*
|
|
@@ -696,10 +491,16 @@ var LightComponent = class extends HTMLElement {
|
|
|
696
491
|
useState(key, initialValue) {
|
|
697
492
|
if (!this._reactiveStates.has(key)) {
|
|
698
493
|
const [getter, setter] = createState(initialValue, () => this.scheduleRerender());
|
|
699
|
-
this._reactiveStates.set(key, {
|
|
494
|
+
this._reactiveStates.set(key, {
|
|
495
|
+
getter,
|
|
496
|
+
setter
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
const state2 = this._reactiveStates.get(key);
|
|
500
|
+
if (!state2) {
|
|
501
|
+
throw new Error(`State ${key} not found`);
|
|
700
502
|
}
|
|
701
|
-
|
|
702
|
-
return [state.getter, state.setter];
|
|
503
|
+
return [state2.getter, state2.setter];
|
|
703
504
|
}
|
|
704
505
|
/**
|
|
705
506
|
* 调度重渲染
|
|
@@ -711,91 +512,23 @@ var LightComponent = class extends HTMLElement {
|
|
|
711
512
|
}
|
|
712
513
|
}
|
|
713
514
|
/**
|
|
714
|
-
*
|
|
515
|
+
* 获取配置值
|
|
516
|
+
*
|
|
517
|
+
* @param key - 配置键
|
|
518
|
+
* @param defaultValue - 默认值
|
|
519
|
+
* @returns 配置值
|
|
715
520
|
*/
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
logger3.warn(
|
|
719
|
-
`[${this.constructor.name}] Component is not connected, skipping rerender.`
|
|
720
|
-
);
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
this.innerHTML = "";
|
|
724
|
-
try {
|
|
725
|
-
const content = this.render();
|
|
726
|
-
this.appendChild(content);
|
|
727
|
-
} catch (error) {
|
|
728
|
-
logger3.error(`[${this.constructor.name}] Error in rerender:`, error);
|
|
729
|
-
this.renderError(error);
|
|
730
|
-
}
|
|
521
|
+
getConfig(key, defaultValue) {
|
|
522
|
+
return this.config[key] ?? defaultValue;
|
|
731
523
|
}
|
|
732
524
|
/**
|
|
733
|
-
*
|
|
525
|
+
* 设置配置值
|
|
734
526
|
*
|
|
735
|
-
* @param
|
|
527
|
+
* @param key - 配置键
|
|
528
|
+
* @param value - 配置值
|
|
736
529
|
*/
|
|
737
|
-
|
|
738
|
-
this.
|
|
739
|
-
const errorElement = h(
|
|
740
|
-
"div",
|
|
741
|
-
{
|
|
742
|
-
style: "color: red; padding: 10px; border: 1px solid red; background: #ffe6e6; font-family: monospace;"
|
|
743
|
-
},
|
|
744
|
-
[
|
|
745
|
-
h("strong", {}, `[${this.constructor.name}] Component Error:`),
|
|
746
|
-
h("pre", { style: "margin: 10px 0; white-space: pre-wrap;" }, String(error))
|
|
747
|
-
]
|
|
748
|
-
);
|
|
749
|
-
this.appendChild(errorElement);
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* 为Light DOM组件应用样式
|
|
753
|
-
* 直接将样式注入到组件自身,避免全局污染
|
|
754
|
-
*/
|
|
755
|
-
applyScopedStyles(styleName, cssText) {
|
|
756
|
-
const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
|
|
757
|
-
if (existingStyle) {
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
const styleElement = document.createElement("style");
|
|
761
|
-
styleElement.setAttribute("data-wsx-light-component", styleName);
|
|
762
|
-
styleElement.textContent = cssText;
|
|
763
|
-
this.insertBefore(styleElement, this.firstChild);
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* 获取配置值
|
|
767
|
-
*
|
|
768
|
-
* @param key - 配置键
|
|
769
|
-
* @param defaultValue - 默认值
|
|
770
|
-
* @returns 配置值
|
|
771
|
-
*/
|
|
772
|
-
getConfig(key, defaultValue) {
|
|
773
|
-
return this.config[key] ?? defaultValue;
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* 设置配置值
|
|
777
|
-
*
|
|
778
|
-
* @param key - 配置键
|
|
779
|
-
* @param value - 配置值
|
|
780
|
-
*/
|
|
781
|
-
setConfig(key, value) {
|
|
782
|
-
this.config[key] = value;
|
|
783
|
-
}
|
|
784
|
-
/**
|
|
785
|
-
* 清理响应式状态
|
|
786
|
-
*/
|
|
787
|
-
cleanupReactiveStates() {
|
|
788
|
-
this._reactiveStates.clear();
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* 清理组件样式
|
|
792
|
-
*/
|
|
793
|
-
cleanupStyles() {
|
|
794
|
-
const styleName = this.config.styleName || this.constructor.name;
|
|
795
|
-
const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
|
|
796
|
-
if (existingStyle) {
|
|
797
|
-
existingStyle.remove();
|
|
798
|
-
}
|
|
530
|
+
setConfig(key, value) {
|
|
531
|
+
this.config[key] = value;
|
|
799
532
|
}
|
|
800
533
|
/**
|
|
801
534
|
* 获取属性值
|
|
@@ -833,92 +566,102 @@ var LightComponent = class extends HTMLElement {
|
|
|
833
566
|
hasAttr(name) {
|
|
834
567
|
return this.hasAttribute(name);
|
|
835
568
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
|
|
842
|
-
if (!customElements.get(tagName)) {
|
|
843
|
-
customElements.define(tagName, constructor);
|
|
844
|
-
}
|
|
845
|
-
return constructor;
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
function registerComponent(constructor, options = {}) {
|
|
849
|
-
const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
|
|
850
|
-
if (!customElements.get(tagName)) {
|
|
851
|
-
customElements.define(tagName, constructor);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
function deriveTagName(className, prefix) {
|
|
855
|
-
let kebabCase = className.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
856
|
-
if (!kebabCase.includes("-")) {
|
|
857
|
-
kebabCase = `${kebabCase}-component`;
|
|
569
|
+
/**
|
|
570
|
+
* 清理响应式状态
|
|
571
|
+
*/
|
|
572
|
+
cleanupReactiveStates() {
|
|
573
|
+
this._reactiveStates.clear();
|
|
858
574
|
}
|
|
859
|
-
|
|
860
|
-
}
|
|
575
|
+
};
|
|
861
576
|
|
|
862
|
-
// src/
|
|
863
|
-
var
|
|
577
|
+
// src/web-component.ts
|
|
578
|
+
var WebComponent = class extends BaseComponent {
|
|
864
579
|
constructor(config = {}) {
|
|
865
580
|
super(config);
|
|
866
|
-
|
|
581
|
+
// Initialized by BaseComponent constructor
|
|
867
582
|
this._preserveFocus = true;
|
|
868
|
-
this._reactiveStates = /* @__PURE__ */ new Map();
|
|
869
|
-
this._isDebugEnabled = config.debug ?? false;
|
|
870
583
|
this._preserveFocus = config.preserveFocus ?? true;
|
|
584
|
+
this.attachShadow({ mode: "open" });
|
|
871
585
|
}
|
|
872
586
|
/**
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
* @param obj 要变为响应式的对象
|
|
876
|
-
* @param debugName 调试名称(可选)
|
|
877
|
-
* @returns 响应式代理对象
|
|
587
|
+
* Web Component生命周期:连接到DOM
|
|
878
588
|
*/
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
589
|
+
connectedCallback() {
|
|
590
|
+
this.connected = true;
|
|
591
|
+
try {
|
|
592
|
+
const stylesToApply = this._getAutoStyles?.() || this.config.styles;
|
|
593
|
+
if (stylesToApply) {
|
|
594
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
595
|
+
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
596
|
+
}
|
|
597
|
+
const content = this.render();
|
|
598
|
+
this.shadowRoot.appendChild(content);
|
|
599
|
+
this.onConnected?.();
|
|
600
|
+
} catch (error) {
|
|
601
|
+
console.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
602
|
+
this.renderError(error);
|
|
603
|
+
}
|
|
883
604
|
}
|
|
884
605
|
/**
|
|
885
|
-
*
|
|
606
|
+
* Web Component生命周期:从DOM断开
|
|
607
|
+
*/
|
|
608
|
+
disconnectedCallback() {
|
|
609
|
+
this.connected = false;
|
|
610
|
+
this.onDisconnected?.();
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* 查找Shadow DOM内的元素
|
|
886
614
|
*
|
|
887
|
-
* @param
|
|
888
|
-
* @
|
|
889
|
-
* @returns [getter, setter] 元组
|
|
615
|
+
* @param selector - CSS选择器
|
|
616
|
+
* @returns 元素或null
|
|
890
617
|
*/
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const [getter, setter] = createState(initialValue, () => this.scheduleRerender());
|
|
894
|
-
this._reactiveStates.set(key, { getter, setter });
|
|
895
|
-
}
|
|
896
|
-
const state = this._reactiveStates.get(key);
|
|
897
|
-
return [state.getter, state.setter];
|
|
618
|
+
querySelector(selector) {
|
|
619
|
+
return this.shadowRoot.querySelector(selector);
|
|
898
620
|
}
|
|
899
621
|
/**
|
|
900
|
-
*
|
|
901
|
-
*
|
|
622
|
+
* 查找Shadow DOM内的所有匹配元素
|
|
623
|
+
*
|
|
624
|
+
* @param selector - CSS选择器
|
|
625
|
+
* @returns 元素列表
|
|
902
626
|
*/
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
this.rerender();
|
|
906
|
-
}
|
|
627
|
+
querySelectorAll(selector) {
|
|
628
|
+
return this.shadowRoot.querySelectorAll(selector);
|
|
907
629
|
}
|
|
908
630
|
/**
|
|
909
|
-
*
|
|
631
|
+
* 重新渲染组件
|
|
910
632
|
*/
|
|
911
633
|
rerender() {
|
|
912
634
|
if (!this.connected) {
|
|
635
|
+
console.warn(
|
|
636
|
+
`[${this.constructor.name}] Component is not connected, skipping rerender.`
|
|
637
|
+
);
|
|
913
638
|
return;
|
|
914
639
|
}
|
|
915
640
|
let focusData = null;
|
|
916
|
-
if (this._preserveFocus) {
|
|
641
|
+
if (this._preserveFocus && this.shadowRoot) {
|
|
917
642
|
const activeElement = this.shadowRoot.activeElement;
|
|
918
643
|
focusData = this.saveFocusState(activeElement);
|
|
919
644
|
}
|
|
920
|
-
|
|
921
|
-
|
|
645
|
+
const adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets || [];
|
|
646
|
+
this.shadowRoot.innerHTML = "";
|
|
647
|
+
if (this.shadowRoot.adoptedStyleSheets) {
|
|
648
|
+
this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
|
|
649
|
+
}
|
|
650
|
+
if (adoptedStyleSheets.length === 0) {
|
|
651
|
+
const stylesToApply = this._autoStyles || this.config.styles;
|
|
652
|
+
if (stylesToApply) {
|
|
653
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
654
|
+
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
try {
|
|
658
|
+
const content = this.render();
|
|
659
|
+
this.shadowRoot.appendChild(content);
|
|
660
|
+
} catch (error) {
|
|
661
|
+
console.error(`[${this.constructor.name}] Error in rerender:`, error);
|
|
662
|
+
this.renderError(error);
|
|
663
|
+
}
|
|
664
|
+
if (this._preserveFocus && focusData && this.shadowRoot) {
|
|
922
665
|
this.restoreFocusState(focusData);
|
|
923
666
|
}
|
|
924
667
|
}
|
|
@@ -944,8 +687,8 @@ var ReactiveWebComponent = class extends WebComponent {
|
|
|
944
687
|
if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLSelectElement) {
|
|
945
688
|
focusData.value = activeElement.value;
|
|
946
689
|
if ("selectionStart" in activeElement) {
|
|
947
|
-
focusData.selectionStart = activeElement.selectionStart;
|
|
948
|
-
focusData.selectionEnd = activeElement.selectionEnd;
|
|
690
|
+
focusData.selectionStart = activeElement.selectionStart ?? void 0;
|
|
691
|
+
focusData.selectionEnd = activeElement.selectionEnd ?? void 0;
|
|
949
692
|
}
|
|
950
693
|
}
|
|
951
694
|
return focusData;
|
|
@@ -971,10 +714,10 @@ var ReactiveWebComponent = class extends WebComponent {
|
|
|
971
714
|
if (targetElement instanceof HTMLInputElement) {
|
|
972
715
|
targetElement.setSelectionRange(
|
|
973
716
|
focusData.selectionStart,
|
|
974
|
-
focusData.selectionEnd
|
|
717
|
+
focusData.selectionEnd ?? focusData.selectionStart
|
|
975
718
|
);
|
|
976
719
|
} else if (targetElement instanceof HTMLSelectElement) {
|
|
977
|
-
targetElement.value = focusData.value;
|
|
720
|
+
targetElement.value = focusData.value ?? "";
|
|
978
721
|
} else if (targetElement.hasAttribute("contenteditable")) {
|
|
979
722
|
this.setCursorPosition(targetElement, focusData.selectionStart);
|
|
980
723
|
}
|
|
@@ -1004,83 +747,228 @@ var ReactiveWebComponent = class extends WebComponent {
|
|
|
1004
747
|
}
|
|
1005
748
|
}
|
|
1006
749
|
/**
|
|
1007
|
-
*
|
|
750
|
+
* 渲染错误信息
|
|
751
|
+
*
|
|
752
|
+
* @param error - 错误对象
|
|
1008
753
|
*/
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
754
|
+
renderError(error) {
|
|
755
|
+
this.shadowRoot.innerHTML = "";
|
|
756
|
+
const errorElement = h(
|
|
757
|
+
"div",
|
|
758
|
+
{
|
|
759
|
+
style: "color: red; padding: 10px; border: 1px solid red; background: #ffe6e6; font-family: monospace;"
|
|
760
|
+
},
|
|
761
|
+
[
|
|
762
|
+
h("strong", {}, `[${this.constructor.name}] Component Error:`),
|
|
763
|
+
h("pre", { style: "margin: 10px 0; white-space: pre-wrap;" }, String(error))
|
|
764
|
+
]
|
|
765
|
+
);
|
|
766
|
+
this.shadowRoot.appendChild(errorElement);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// src/light-component.ts
|
|
771
|
+
var logger3 = createLogger("LightComponent");
|
|
772
|
+
var LightComponent = class extends BaseComponent {
|
|
773
|
+
// Initialized by BaseComponent constructor
|
|
774
|
+
constructor(config = {}) {
|
|
775
|
+
super(config);
|
|
1015
776
|
}
|
|
1016
777
|
/**
|
|
1017
|
-
*
|
|
778
|
+
* Web Component生命周期:连接到DOM
|
|
1018
779
|
*/
|
|
1019
|
-
|
|
1020
|
-
this.
|
|
780
|
+
connectedCallback() {
|
|
781
|
+
this.connected = true;
|
|
782
|
+
try {
|
|
783
|
+
const stylesToApply = this._autoStyles || this.config.styles;
|
|
784
|
+
if (stylesToApply) {
|
|
785
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
786
|
+
this.applyScopedStyles(styleName, stylesToApply);
|
|
787
|
+
}
|
|
788
|
+
const content = this.render();
|
|
789
|
+
this.appendChild(content);
|
|
790
|
+
this.onConnected?.();
|
|
791
|
+
} catch (error) {
|
|
792
|
+
logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
793
|
+
this.renderError(error);
|
|
794
|
+
}
|
|
1021
795
|
}
|
|
1022
796
|
/**
|
|
1023
|
-
*
|
|
797
|
+
* Web Component生命周期:从DOM断开
|
|
1024
798
|
*/
|
|
1025
799
|
disconnectedCallback() {
|
|
1026
|
-
super.disconnectedCallback();
|
|
1027
800
|
this.cleanupReactiveStates();
|
|
801
|
+
this.cleanupStyles();
|
|
802
|
+
this.onDisconnected?.();
|
|
1028
803
|
}
|
|
1029
804
|
/**
|
|
1030
|
-
*
|
|
805
|
+
* 查找组件内的元素
|
|
806
|
+
*
|
|
807
|
+
* @param selector - CSS选择器
|
|
808
|
+
* @returns 元素或null
|
|
1031
809
|
*/
|
|
1032
|
-
|
|
1033
|
-
this
|
|
810
|
+
querySelector(selector) {
|
|
811
|
+
return HTMLElement.prototype.querySelector.call(this, selector);
|
|
1034
812
|
}
|
|
1035
813
|
/**
|
|
1036
|
-
*
|
|
814
|
+
* 查找组件内的所有匹配元素
|
|
815
|
+
*
|
|
816
|
+
* @param selector - CSS选择器
|
|
817
|
+
* @returns 元素列表
|
|
1037
818
|
*/
|
|
1038
|
-
|
|
1039
|
-
this
|
|
819
|
+
querySelectorAll(selector) {
|
|
820
|
+
return HTMLElement.prototype.querySelectorAll.call(this, selector);
|
|
1040
821
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
822
|
+
/**
|
|
823
|
+
* 重新渲染组件
|
|
824
|
+
*/
|
|
825
|
+
rerender() {
|
|
826
|
+
if (!this.connected) {
|
|
827
|
+
logger3.warn(
|
|
828
|
+
`[${this.constructor.name}] Component is not connected, skipping rerender.`
|
|
829
|
+
);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
this.innerHTML = "";
|
|
833
|
+
if (this.config.styles) {
|
|
834
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
835
|
+
const styleElement = document.createElement("style");
|
|
836
|
+
styleElement.setAttribute("data-wsx-light-component", styleName);
|
|
837
|
+
styleElement.textContent = this.config.styles;
|
|
838
|
+
this.appendChild(styleElement);
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
const content = this.render();
|
|
842
|
+
this.appendChild(content);
|
|
843
|
+
if (this.config.styles && this.children.length > 1) {
|
|
844
|
+
const styleElement = this.querySelector(
|
|
845
|
+
`style[data-wsx-light-component="${this.config.styleName || this.constructor.name}"]`
|
|
846
|
+
);
|
|
847
|
+
if (styleElement && styleElement !== this.firstChild) {
|
|
848
|
+
this.insertBefore(styleElement, this.firstChild);
|
|
1051
849
|
}
|
|
1052
850
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
851
|
+
} catch (error) {
|
|
852
|
+
logger3.error(`[${this.constructor.name}] Error in rerender:`, error);
|
|
853
|
+
this.renderError(error);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* 渲染错误信息
|
|
858
|
+
*
|
|
859
|
+
* @param error - 错误对象
|
|
860
|
+
*/
|
|
861
|
+
renderError(error) {
|
|
862
|
+
this.innerHTML = "";
|
|
863
|
+
const errorElement = h(
|
|
864
|
+
"div",
|
|
865
|
+
{
|
|
866
|
+
style: "color: red; padding: 10px; border: 1px solid red; background: #ffe6e6; font-family: monospace;"
|
|
867
|
+
},
|
|
868
|
+
[
|
|
869
|
+
h("strong", {}, `[${this.constructor.name}] Component Error:`),
|
|
870
|
+
h("pre", { style: "margin: 10px 0; white-space: pre-wrap;" }, String(error))
|
|
871
|
+
]
|
|
872
|
+
);
|
|
873
|
+
this.appendChild(errorElement);
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* 为Light DOM组件应用样式
|
|
877
|
+
* 直接将样式注入到组件自身,避免全局污染
|
|
878
|
+
*/
|
|
879
|
+
applyScopedStyles(styleName, cssText) {
|
|
880
|
+
const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
|
|
881
|
+
if (existingStyle) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const styleElement = document.createElement("style");
|
|
885
|
+
styleElement.setAttribute("data-wsx-light-component", styleName);
|
|
886
|
+
styleElement.textContent = cssText;
|
|
887
|
+
this.insertBefore(styleElement, this.firstChild);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* 清理组件样式
|
|
891
|
+
*/
|
|
892
|
+
cleanupStyles() {
|
|
893
|
+
const styleName = this.config.styleName || this.constructor.name;
|
|
894
|
+
const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
|
|
895
|
+
if (existingStyle) {
|
|
896
|
+
existingStyle.remove();
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
// src/auto-register.ts
|
|
902
|
+
function autoRegister(options = {}) {
|
|
903
|
+
return function(constructor) {
|
|
904
|
+
const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
|
|
905
|
+
if (!customElements.get(tagName)) {
|
|
906
|
+
customElements.define(tagName, constructor);
|
|
907
|
+
}
|
|
908
|
+
return constructor;
|
|
1057
909
|
};
|
|
1058
910
|
}
|
|
1059
|
-
function
|
|
1060
|
-
|
|
1061
|
-
|
|
911
|
+
function registerComponent(constructor, options = {}) {
|
|
912
|
+
const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
|
|
913
|
+
if (!customElements.get(tagName)) {
|
|
914
|
+
customElements.define(tagName, constructor);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function deriveTagName(className, prefix) {
|
|
918
|
+
let kebabCase = className.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
919
|
+
if (!kebabCase.includes("-")) {
|
|
920
|
+
kebabCase = `${kebabCase}-component`;
|
|
921
|
+
}
|
|
922
|
+
return prefix ? `${prefix}${kebabCase}` : kebabCase;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// src/reactive-decorator.ts
|
|
926
|
+
function state(target, propertyKey) {
|
|
927
|
+
let normalizedPropertyKey;
|
|
928
|
+
if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
|
|
929
|
+
normalizedPropertyKey = propertyKey;
|
|
930
|
+
} else {
|
|
931
|
+
const propertyKeyStr = String(propertyKey);
|
|
932
|
+
if (propertyKeyStr === "[object Object]") {
|
|
933
|
+
throw new Error(
|
|
934
|
+
`@state decorator: Invalid propertyKey. This usually means the build tool doesn't support decorators properly. Please ensure Babel plugin is configured in vite.config.ts`
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
normalizedPropertyKey = propertyKeyStr;
|
|
938
|
+
}
|
|
939
|
+
if (target == null) {
|
|
940
|
+
const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
|
|
941
|
+
throw new Error(
|
|
942
|
+
`@state decorator: Cannot access property "${propertyKeyStr}". Target is ${target === null ? "null" : "undefined"}. Please ensure Babel plugin is configured in vite.config.ts`
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
if (typeof target !== "object") {
|
|
946
|
+
const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
|
|
947
|
+
throw new Error(
|
|
948
|
+
`@state decorator: Cannot be used on "${propertyKeyStr}". @state is for properties only, not methods.`
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, normalizedPropertyKey);
|
|
952
|
+
if (descriptor?.get) {
|
|
953
|
+
const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
|
|
954
|
+
throw new Error(
|
|
955
|
+
`@state decorator cannot be used with getter properties. Property: "${propertyKeyStr}"`
|
|
956
|
+
);
|
|
1062
957
|
}
|
|
1063
|
-
const ReactiveComponent = makeReactive(config?.debug)(ComponentClass);
|
|
1064
|
-
return new ReactiveComponent(config);
|
|
1065
958
|
}
|
|
1066
959
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1067
960
|
0 && (module.exports = {
|
|
1068
961
|
Fragment,
|
|
1069
962
|
LightComponent,
|
|
1070
|
-
ReactiveDebug,
|
|
1071
|
-
ReactiveWebComponent,
|
|
1072
963
|
StyleManager,
|
|
1073
964
|
WSXLogger,
|
|
1074
965
|
WebComponent,
|
|
1075
966
|
autoRegister,
|
|
1076
967
|
createLogger,
|
|
1077
|
-
createReactiveComponent,
|
|
1078
|
-
createState,
|
|
1079
968
|
h,
|
|
1080
969
|
jsx,
|
|
1081
970
|
jsxs,
|
|
1082
971
|
logger,
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
registerComponent
|
|
972
|
+
registerComponent,
|
|
973
|
+
state
|
|
1086
974
|
});
|