@wsxjs/wsx-core 0.0.5 → 0.0.7

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.
Files changed (41) hide show
  1. package/dist/{chunk-YNUVFDKT.mjs → chunk-VZQT7HU5.mjs} +4 -4
  2. package/dist/index.js +370 -24
  3. package/dist/index.mjs +370 -25
  4. package/dist/jsx-runtime.js +4 -3
  5. package/dist/jsx-runtime.mjs +1 -1
  6. package/dist/jsx.mjs +1 -1
  7. package/package.json +1 -1
  8. package/src/index.ts +2 -0
  9. package/src/jsx-factory.ts +5 -4
  10. package/src/light-component.ts +351 -0
  11. package/src/reactive-component.ts +135 -0
  12. package/types/index.d.ts +5 -0
  13. package/types/wsx-types.d.ts +4 -2
  14. package/dist/chunk-3CJEWYVF.mjs +0 -197
  15. package/dist/chunk-5JVEHB6H.mjs +0 -197
  16. package/dist/chunk-7E7KJQSW.mjs +0 -210
  17. package/dist/chunk-A5GYVTI3.mjs +0 -222
  18. package/dist/chunk-A5GYVTI3.mjs.map +0 -1
  19. package/dist/chunk-K6N3JDTI.mjs +0 -216
  20. package/dist/chunk-RVGKV4GP.mjs +0 -79
  21. package/dist/chunk-S3O776FY.mjs +0 -173
  22. package/dist/chunk-VNK4B3FW.mjs +0 -217
  23. package/dist/chunk-YNUVFDKT.mjs.map +0 -1
  24. package/dist/index.d.mts +0 -235
  25. package/dist/index.d.ts +0 -235
  26. package/dist/index.js.map +0 -1
  27. package/dist/index.mjs.map +0 -1
  28. package/dist/jsx-factory-pFUwL2Dz.d.mts +0 -26
  29. package/dist/jsx-factory-pFUwL2Dz.d.ts +0 -26
  30. package/dist/jsx-pFUwL2Dz.d.mts +0 -26
  31. package/dist/jsx-pFUwL2Dz.d.ts +0 -26
  32. package/dist/jsx-runtime-pFUwL2Dz.d.mts +0 -26
  33. package/dist/jsx-runtime-pFUwL2Dz.d.ts +0 -26
  34. package/dist/jsx-runtime.d.mts +0 -1
  35. package/dist/jsx-runtime.d.ts +0 -1
  36. package/dist/jsx-runtime.js.map +0 -1
  37. package/dist/jsx-runtime.mjs.map +0 -1
  38. package/dist/jsx.d.mts +0 -66
  39. package/dist/jsx.d.ts +0 -66
  40. package/dist/jsx.js.map +0 -1
  41. package/dist/jsx.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Fragment,
3
3
  h
4
- } from "./chunk-BV2V6BVN.mjs";
4
+ } from "./chunk-VZQT7HU5.mjs";
5
5
 
6
6
  // src/styles/style-manager.ts
7
7
  var StyleManager = class {
@@ -209,30 +209,6 @@ var WebComponent = class extends HTMLElement {
209
209
  }
210
210
  };
211
211
 
212
- // src/auto-register.ts
213
- function autoRegister(options = {}) {
214
- return function(constructor) {
215
- const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
216
- if (!customElements.get(tagName)) {
217
- customElements.define(tagName, constructor);
218
- }
219
- return constructor;
220
- };
221
- }
222
- function registerComponent(constructor, options = {}) {
223
- const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
224
- if (!customElements.get(tagName)) {
225
- customElements.define(tagName, constructor);
226
- }
227
- }
228
- function deriveTagName(className, prefix) {
229
- let kebabCase = className.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
230
- if (!kebabCase.includes("-")) {
231
- kebabCase = `${kebabCase}-component`;
232
- }
233
- return prefix ? `${prefix}${kebabCase}` : kebabCase;
234
- }
235
-
236
212
  // src/utils/logger.ts
237
213
  var WSXLogger = class {
238
214
  constructor(prefix = "[WSX]", enabled = true, level = "info") {
@@ -399,13 +375,283 @@ function reactiveWithDebug(obj, onChange, debugName) {
399
375
  });
400
376
  }
401
377
 
378
+ // src/light-component.ts
379
+ var logger3 = createLogger("LightComponent");
380
+ var LightComponent = class extends HTMLElement {
381
+ constructor(config = {}) {
382
+ super();
383
+ this.connected = false;
384
+ this._isDebugEnabled = false;
385
+ this._reactiveStates = /* @__PURE__ */ new Map();
386
+ this.config = config;
387
+ this._isDebugEnabled = config.debug ?? false;
388
+ }
389
+ /**
390
+ * 子类应该重写这个方法来定义观察的属性
391
+ * @returns 要观察的属性名数组
392
+ */
393
+ static get observedAttributes() {
394
+ return [];
395
+ }
396
+ /**
397
+ * Web Component生命周期:连接到DOM
398
+ */
399
+ connectedCallback() {
400
+ this.connected = true;
401
+ try {
402
+ if (this.config.styles) {
403
+ const styleName = this.config.styleName || this.constructor.name;
404
+ this.applyScopedStyles(styleName, this.config.styles);
405
+ }
406
+ const content = this.render();
407
+ this.appendChild(content);
408
+ this.onConnected?.();
409
+ } catch (error) {
410
+ logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
411
+ this.renderError(error);
412
+ }
413
+ }
414
+ /**
415
+ * Web Component生命周期:从DOM断开
416
+ */
417
+ disconnectedCallback() {
418
+ this.cleanupReactiveStates();
419
+ this.cleanupStyles();
420
+ this.onDisconnected?.();
421
+ }
422
+ /**
423
+ * Web Component生命周期:属性变化
424
+ */
425
+ attributeChangedCallback(name, oldValue, newValue) {
426
+ this.onAttributeChanged?.(name, oldValue, newValue);
427
+ }
428
+ /**
429
+ * 查找组件内的元素
430
+ *
431
+ * @param selector - CSS选择器
432
+ * @returns 元素或null
433
+ */
434
+ querySelector(selector) {
435
+ return HTMLElement.prototype.querySelector.call(this, selector);
436
+ }
437
+ /**
438
+ * 查找组件内的所有匹配元素
439
+ *
440
+ * @param selector - CSS选择器
441
+ * @returns 元素列表
442
+ */
443
+ querySelectorAll(selector) {
444
+ return HTMLElement.prototype.querySelectorAll.call(this, selector);
445
+ }
446
+ /**
447
+ * 创建响应式对象
448
+ *
449
+ * @param obj 要变为响应式的对象
450
+ * @param debugName 调试名称(可选)
451
+ * @returns 响应式代理对象
452
+ */
453
+ reactive(obj, debugName) {
454
+ const reactiveFn = this._isDebugEnabled ? reactiveWithDebug : reactive;
455
+ const name = debugName || `${this.constructor.name}.reactive`;
456
+ return this._isDebugEnabled ? reactiveFn(obj, () => this.scheduleRerender(), name) : reactiveFn(obj, () => this.scheduleRerender());
457
+ }
458
+ /**
459
+ * 创建响应式状态
460
+ *
461
+ * @param key 状态标识符
462
+ * @param initialValue 初始值
463
+ * @returns [getter, setter] 元组
464
+ */
465
+ useState(key, initialValue) {
466
+ if (!this._reactiveStates.has(key)) {
467
+ const [getter, setter] = createState(initialValue, () => this.scheduleRerender());
468
+ this._reactiveStates.set(key, { getter, setter });
469
+ }
470
+ const state = this._reactiveStates.get(key);
471
+ return [state.getter, state.setter];
472
+ }
473
+ /**
474
+ * 调度重渲染
475
+ * 这个方法被响应式系统调用,开发者通常不需要直接调用
476
+ */
477
+ scheduleRerender() {
478
+ if (this.connected) {
479
+ this.rerender();
480
+ }
481
+ }
482
+ /**
483
+ * 重新渲染组件
484
+ */
485
+ rerender() {
486
+ if (!this.connected) {
487
+ logger3.warn(
488
+ `[${this.constructor.name}] Component is not connected, skipping rerender.`
489
+ );
490
+ return;
491
+ }
492
+ this.innerHTML = "";
493
+ if (this.config.styles) {
494
+ const styleName = this.config.styleName || this.constructor.name;
495
+ const styleElement = document.createElement("style");
496
+ styleElement.setAttribute("data-wsx-light-component", styleName);
497
+ styleElement.textContent = this.config.styles;
498
+ this.appendChild(styleElement);
499
+ }
500
+ try {
501
+ const content = this.render();
502
+ this.appendChild(content);
503
+ if (this.config.styles && this.children.length > 1) {
504
+ const styleElement = this.querySelector(
505
+ `style[data-wsx-light-component="${this.config.styleName || this.constructor.name}"]`
506
+ );
507
+ if (styleElement && styleElement !== this.firstChild) {
508
+ this.insertBefore(styleElement, this.firstChild);
509
+ }
510
+ }
511
+ } catch (error) {
512
+ logger3.error(`[${this.constructor.name}] Error in rerender:`, error);
513
+ this.renderError(error);
514
+ }
515
+ }
516
+ /**
517
+ * 渲染错误信息
518
+ *
519
+ * @param error - 错误对象
520
+ */
521
+ renderError(error) {
522
+ this.innerHTML = "";
523
+ const errorElement = h(
524
+ "div",
525
+ {
526
+ style: "color: red; padding: 10px; border: 1px solid red; background: #ffe6e6; font-family: monospace;"
527
+ },
528
+ [
529
+ h("strong", {}, `[${this.constructor.name}] Component Error:`),
530
+ h("pre", { style: "margin: 10px 0; white-space: pre-wrap;" }, String(error))
531
+ ]
532
+ );
533
+ this.appendChild(errorElement);
534
+ }
535
+ /**
536
+ * 为Light DOM组件应用样式
537
+ * 直接将样式注入到组件自身,避免全局污染
538
+ */
539
+ applyScopedStyles(styleName, cssText) {
540
+ const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
541
+ if (existingStyle) {
542
+ return;
543
+ }
544
+ const styleElement = document.createElement("style");
545
+ styleElement.setAttribute("data-wsx-light-component", styleName);
546
+ styleElement.textContent = cssText;
547
+ this.insertBefore(styleElement, this.firstChild);
548
+ }
549
+ /**
550
+ * 获取配置值
551
+ *
552
+ * @param key - 配置键
553
+ * @param defaultValue - 默认值
554
+ * @returns 配置值
555
+ */
556
+ getConfig(key, defaultValue) {
557
+ return this.config[key] ?? defaultValue;
558
+ }
559
+ /**
560
+ * 设置配置值
561
+ *
562
+ * @param key - 配置键
563
+ * @param value - 配置值
564
+ */
565
+ setConfig(key, value) {
566
+ this.config[key] = value;
567
+ }
568
+ /**
569
+ * 清理响应式状态
570
+ */
571
+ cleanupReactiveStates() {
572
+ this._reactiveStates.clear();
573
+ }
574
+ /**
575
+ * 清理组件样式
576
+ */
577
+ cleanupStyles() {
578
+ const styleName = this.config.styleName || this.constructor.name;
579
+ const existingStyle = this.querySelector(`style[data-wsx-light-component="${styleName}"]`);
580
+ if (existingStyle) {
581
+ existingStyle.remove();
582
+ }
583
+ }
584
+ /**
585
+ * 获取属性值
586
+ *
587
+ * @param name - 属性名
588
+ * @param defaultValue - 默认值
589
+ * @returns 属性值
590
+ */
591
+ getAttr(name, defaultValue = "") {
592
+ return this.getAttribute(name) || defaultValue;
593
+ }
594
+ /**
595
+ * 设置属性值
596
+ *
597
+ * @param name - 属性名
598
+ * @param value - 属性值
599
+ */
600
+ setAttr(name, value) {
601
+ this.setAttribute(name, value);
602
+ }
603
+ /**
604
+ * 移除属性
605
+ *
606
+ * @param name - 属性名
607
+ */
608
+ removeAttr(name) {
609
+ this.removeAttribute(name);
610
+ }
611
+ /**
612
+ * 检查是否有属性
613
+ *
614
+ * @param name - 属性名
615
+ * @returns 是否存在
616
+ */
617
+ hasAttr(name) {
618
+ return this.hasAttribute(name);
619
+ }
620
+ };
621
+
622
+ // src/auto-register.ts
623
+ function autoRegister(options = {}) {
624
+ return function(constructor) {
625
+ const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
626
+ if (!customElements.get(tagName)) {
627
+ customElements.define(tagName, constructor);
628
+ }
629
+ return constructor;
630
+ };
631
+ }
632
+ function registerComponent(constructor, options = {}) {
633
+ const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
634
+ if (!customElements.get(tagName)) {
635
+ customElements.define(tagName, constructor);
636
+ }
637
+ }
638
+ function deriveTagName(className, prefix) {
639
+ let kebabCase = className.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
640
+ if (!kebabCase.includes("-")) {
641
+ kebabCase = `${kebabCase}-component`;
642
+ }
643
+ return prefix ? `${prefix}${kebabCase}` : kebabCase;
644
+ }
645
+
402
646
  // src/reactive-component.ts
403
647
  var ReactiveWebComponent = class extends WebComponent {
404
648
  constructor(config = {}) {
405
649
  super(config);
406
650
  this._isDebugEnabled = false;
651
+ this._preserveFocus = true;
407
652
  this._reactiveStates = /* @__PURE__ */ new Map();
408
653
  this._isDebugEnabled = config.debug ?? false;
654
+ this._preserveFocus = config.preserveFocus ?? true;
409
655
  }
410
656
  /**
411
657
  * 创建响应式对象
@@ -443,6 +689,104 @@ var ReactiveWebComponent = class extends WebComponent {
443
689
  this.rerender();
444
690
  }
445
691
  }
692
+ /**
693
+ * 重写 rerender 方法以支持焦点保持
694
+ */
695
+ rerender() {
696
+ if (!this.connected) {
697
+ return;
698
+ }
699
+ let focusData = null;
700
+ if (this._preserveFocus) {
701
+ const activeElement = this.shadowRoot.activeElement;
702
+ focusData = this.saveFocusState(activeElement);
703
+ }
704
+ super.rerender();
705
+ if (this._preserveFocus && focusData) {
706
+ this.restoreFocusState(focusData);
707
+ }
708
+ }
709
+ /**
710
+ * 保存焦点状态
711
+ */
712
+ saveFocusState(activeElement) {
713
+ if (!activeElement) {
714
+ return null;
715
+ }
716
+ const focusData = {
717
+ tagName: activeElement.tagName.toLowerCase(),
718
+ className: activeElement.className
719
+ };
720
+ if (activeElement.hasAttribute("contenteditable")) {
721
+ const selection = window.getSelection();
722
+ if (selection && selection.rangeCount > 0) {
723
+ const range = selection.getRangeAt(0);
724
+ focusData.selectionStart = range.startOffset;
725
+ focusData.selectionEnd = range.endOffset;
726
+ }
727
+ }
728
+ if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLSelectElement) {
729
+ focusData.value = activeElement.value;
730
+ if ("selectionStart" in activeElement) {
731
+ focusData.selectionStart = activeElement.selectionStart;
732
+ focusData.selectionEnd = activeElement.selectionEnd;
733
+ }
734
+ }
735
+ return focusData;
736
+ }
737
+ /**
738
+ * 恢复焦点状态
739
+ */
740
+ restoreFocusState(focusData) {
741
+ if (!focusData) return;
742
+ try {
743
+ let targetElement = null;
744
+ if (focusData.className) {
745
+ targetElement = this.shadowRoot.querySelector(
746
+ `.${focusData.className.split(" ")[0]}`
747
+ );
748
+ }
749
+ if (!targetElement) {
750
+ targetElement = this.shadowRoot.querySelector(focusData.tagName);
751
+ }
752
+ if (targetElement) {
753
+ targetElement.focus({ preventScroll: true });
754
+ if (focusData.selectionStart !== void 0) {
755
+ if (targetElement instanceof HTMLInputElement) {
756
+ targetElement.setSelectionRange(
757
+ focusData.selectionStart,
758
+ focusData.selectionEnd
759
+ );
760
+ } else if (targetElement instanceof HTMLSelectElement) {
761
+ targetElement.value = focusData.value;
762
+ } else if (targetElement.hasAttribute("contenteditable")) {
763
+ this.setCursorPosition(targetElement, focusData.selectionStart);
764
+ }
765
+ }
766
+ }
767
+ } catch {
768
+ }
769
+ }
770
+ /**
771
+ * 设置光标位置
772
+ */
773
+ setCursorPosition(element, position) {
774
+ try {
775
+ const selection = window.getSelection();
776
+ if (selection) {
777
+ const range = document.createRange();
778
+ const textNode = element.childNodes[0];
779
+ if (textNode) {
780
+ const maxPos = Math.min(position, textNode.textContent?.length || 0);
781
+ range.setStart(textNode, maxPos);
782
+ range.setEnd(textNode, maxPos);
783
+ selection.removeAllRanges();
784
+ selection.addRange(range);
785
+ }
786
+ }
787
+ } catch {
788
+ }
789
+ }
446
790
  /**
447
791
  * 获取所有响应式状态的快照(用于调试)
448
792
  */
@@ -505,6 +849,7 @@ function createReactiveComponent(ComponentClass, config) {
505
849
  }
506
850
  export {
507
851
  Fragment,
852
+ LightComponent,
508
853
  ReactiveDebug,
509
854
  ReactiveWebComponent,
510
855
  StyleManager,
@@ -223,8 +223,9 @@ function jsx(tag, props) {
223
223
  return h(tag, null);
224
224
  }
225
225
  const { children, ...restProps } = props;
226
- if (children !== void 0) {
227
- return h(tag, restProps, children);
226
+ if (children !== void 0 && children !== null) {
227
+ const childrenArray = Array.isArray(children) ? children : [children];
228
+ return h(tag, restProps, ...childrenArray);
228
229
  }
229
230
  return h(tag, restProps);
230
231
  }
@@ -235,7 +236,7 @@ function jsxs(tag, props) {
235
236
  const { children, ...restProps } = props;
236
237
  if (Array.isArray(children)) {
237
238
  return h(tag, restProps, ...children);
238
- } else if (children !== void 0) {
239
+ } else if (children !== void 0 && children !== null) {
239
240
  return h(tag, restProps, children);
240
241
  }
241
242
  return h(tag, restProps);
@@ -2,7 +2,7 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-BV2V6BVN.mjs";
5
+ } from "./chunk-VZQT7HU5.mjs";
6
6
  export {
7
7
  Fragment,
8
8
  jsx,
package/dist/jsx.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Fragment,
3
3
  h
4
- } from "./chunk-BV2V6BVN.mjs";
4
+ } from "./chunk-VZQT7HU5.mjs";
5
5
  export {
6
6
  Fragment,
7
7
  h
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-core",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Core WSX Framework - Web Components with JSX syntax",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Core exports
2
2
  export { WebComponent } from "./web-component";
3
+ export { LightComponent } from "./light-component";
3
4
  export { autoRegister, registerComponent } from "./auto-register";
4
5
  export { h, h as jsx, h as jsxs, Fragment } from "./jsx-factory";
5
6
  export { StyleManager } from "./styles/style-manager";
@@ -11,6 +12,7 @@ export { ReactiveWebComponent, makeReactive, createReactiveComponent } from "./r
11
12
 
12
13
  // Type exports
13
14
  export type { WebComponentConfig } from "./web-component";
15
+ export type { LightComponentConfig } from "./light-component";
14
16
  export type { JSXChildren } from "./jsx-factory";
15
17
  export type { Logger, LogLevel } from "./utils/logger";
16
18
  export type { ReactiveCallback } from "./utils/reactive";
@@ -189,8 +189,9 @@ export function jsx(
189
189
  }
190
190
 
191
191
  const { children, ...restProps } = props;
192
- if (children !== undefined) {
193
- return h(tag, restProps, children);
192
+ if (children !== undefined && children !== null) {
193
+ const childrenArray = Array.isArray(children) ? children : [children];
194
+ return h(tag, restProps, ...childrenArray);
194
195
  }
195
196
  return h(tag, restProps);
196
197
  }
@@ -215,8 +216,8 @@ export function jsxs(
215
216
  const { children, ...restProps } = props;
216
217
  if (Array.isArray(children)) {
217
218
  return h(tag, restProps, ...children);
218
- } else if (children !== undefined) {
219
- return h(tag, restProps, children);
219
+ } else if (children !== undefined && children !== null) {
220
+ return h(tag, restProps, children as JSXChildren);
220
221
  }
221
222
  return h(tag, restProps);
222
223
  }