@wsxjs/wsx-core 0.0.5 → 0.0.6

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 +355 -24
  3. package/dist/index.mjs +355 -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 +46 -46
  8. package/src/index.ts +2 -0
  9. package/src/jsx-factory.ts +5 -4
  10. package/src/light-component.ts +328 -0
  11. package/src/reactive-component.ts +135 -0
  12. package/types/index.d.ts +5 -0
  13. package/LICENSE +0 -21
  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
@@ -195,8 +195,9 @@ function jsx(tag, props) {
195
195
  return h(tag, null);
196
196
  }
197
197
  const { children, ...restProps } = props;
198
- if (children !== void 0) {
199
- return h(tag, restProps, children);
198
+ if (children !== void 0 && children !== null) {
199
+ const childrenArray = Array.isArray(children) ? children : [children];
200
+ return h(tag, restProps, ...childrenArray);
200
201
  }
201
202
  return h(tag, restProps);
202
203
  }
@@ -207,7 +208,7 @@ function jsxs(tag, props) {
207
208
  const { children, ...restProps } = props;
208
209
  if (Array.isArray(children)) {
209
210
  return h(tag, restProps, ...children);
210
- } else if (children !== void 0) {
211
+ } else if (children !== void 0 && children !== null) {
211
212
  return h(tag, restProps, children);
212
213
  }
213
214
  return h(tag, restProps);
@@ -219,4 +220,3 @@ export {
219
220
  jsx,
220
221
  jsxs
221
222
  };
222
- //# sourceMappingURL=chunk-YNUVFDKT.mjs.map
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Fragment: () => Fragment,
24
+ LightComponent: () => LightComponent,
24
25
  ReactiveDebug: () => ReactiveDebug,
25
26
  ReactiveWebComponent: () => ReactiveWebComponent,
26
27
  StyleManager: () => StyleManager,
@@ -439,30 +440,6 @@ var WebComponent = class extends HTMLElement {
439
440
  }
440
441
  };
441
442
 
442
- // src/auto-register.ts
443
- function autoRegister(options = {}) {
444
- return function(constructor) {
445
- const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
446
- if (!customElements.get(tagName)) {
447
- customElements.define(tagName, constructor);
448
- }
449
- return constructor;
450
- };
451
- }
452
- function registerComponent(constructor, options = {}) {
453
- const tagName = options.tagName || deriveTagName(constructor.name, options.prefix);
454
- if (!customElements.get(tagName)) {
455
- customElements.define(tagName, constructor);
456
- }
457
- }
458
- function deriveTagName(className, prefix) {
459
- let kebabCase = className.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
460
- if (!kebabCase.includes("-")) {
461
- kebabCase = `${kebabCase}-component`;
462
- }
463
- return prefix ? `${prefix}${kebabCase}` : kebabCase;
464
- }
465
-
466
443
  // src/utils/logger.ts
467
444
  var WSXLogger = class {
468
445
  constructor(prefix = "[WSX]", enabled = true, level = "info") {
@@ -629,13 +606,268 @@ function reactiveWithDebug(obj, onChange, debugName) {
629
606
  });
630
607
  }
631
608
 
609
+ // src/light-component.ts
610
+ var logger3 = createLogger("LightComponent");
611
+ var LightComponent = class extends HTMLElement {
612
+ constructor(config = {}) {
613
+ super();
614
+ this.connected = false;
615
+ this._isDebugEnabled = false;
616
+ this._reactiveStates = /* @__PURE__ */ new Map();
617
+ this.config = config;
618
+ this._isDebugEnabled = config.debug ?? false;
619
+ }
620
+ /**
621
+ * 子类应该重写这个方法来定义观察的属性
622
+ * @returns 要观察的属性名数组
623
+ */
624
+ static get observedAttributes() {
625
+ return [];
626
+ }
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
+ /**
654
+ * Web Component生命周期:属性变化
655
+ */
656
+ attributeChangedCallback(name, oldValue, newValue) {
657
+ this.onAttributeChanged?.(name, oldValue, newValue);
658
+ }
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
+ /**
678
+ * 创建响应式对象
679
+ *
680
+ * @param obj 要变为响应式的对象
681
+ * @param debugName 调试名称(可选)
682
+ * @returns 响应式代理对象
683
+ */
684
+ reactive(obj, debugName) {
685
+ const reactiveFn = this._isDebugEnabled ? reactiveWithDebug : reactive;
686
+ const name = debugName || `${this.constructor.name}.reactive`;
687
+ return this._isDebugEnabled ? reactiveFn(obj, () => this.scheduleRerender(), name) : reactiveFn(obj, () => this.scheduleRerender());
688
+ }
689
+ /**
690
+ * 创建响应式状态
691
+ *
692
+ * @param key 状态标识符
693
+ * @param initialValue 初始值
694
+ * @returns [getter, setter] 元组
695
+ */
696
+ useState(key, initialValue) {
697
+ if (!this._reactiveStates.has(key)) {
698
+ const [getter, setter] = createState(initialValue, () => this.scheduleRerender());
699
+ this._reactiveStates.set(key, { getter, setter });
700
+ }
701
+ const state = this._reactiveStates.get(key);
702
+ return [state.getter, state.setter];
703
+ }
704
+ /**
705
+ * 调度重渲染
706
+ * 这个方法被响应式系统调用,开发者通常不需要直接调用
707
+ */
708
+ scheduleRerender() {
709
+ if (this.connected) {
710
+ this.rerender();
711
+ }
712
+ }
713
+ /**
714
+ * 重新渲染组件
715
+ */
716
+ rerender() {
717
+ if (!this.connected) {
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
+ }
731
+ }
732
+ /**
733
+ * 渲染错误信息
734
+ *
735
+ * @param error - 错误对象
736
+ */
737
+ renderError(error) {
738
+ this.innerHTML = "";
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
+ }
799
+ }
800
+ /**
801
+ * 获取属性值
802
+ *
803
+ * @param name - 属性名
804
+ * @param defaultValue - 默认值
805
+ * @returns 属性值
806
+ */
807
+ getAttr(name, defaultValue = "") {
808
+ return this.getAttribute(name) || defaultValue;
809
+ }
810
+ /**
811
+ * 设置属性值
812
+ *
813
+ * @param name - 属性名
814
+ * @param value - 属性值
815
+ */
816
+ setAttr(name, value) {
817
+ this.setAttribute(name, value);
818
+ }
819
+ /**
820
+ * 移除属性
821
+ *
822
+ * @param name - 属性名
823
+ */
824
+ removeAttr(name) {
825
+ this.removeAttribute(name);
826
+ }
827
+ /**
828
+ * 检查是否有属性
829
+ *
830
+ * @param name - 属性名
831
+ * @returns 是否存在
832
+ */
833
+ hasAttr(name) {
834
+ return this.hasAttribute(name);
835
+ }
836
+ };
837
+
838
+ // src/auto-register.ts
839
+ function autoRegister(options = {}) {
840
+ return function(constructor) {
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`;
858
+ }
859
+ return prefix ? `${prefix}${kebabCase}` : kebabCase;
860
+ }
861
+
632
862
  // src/reactive-component.ts
633
863
  var ReactiveWebComponent = class extends WebComponent {
634
864
  constructor(config = {}) {
635
865
  super(config);
636
866
  this._isDebugEnabled = false;
867
+ this._preserveFocus = true;
637
868
  this._reactiveStates = /* @__PURE__ */ new Map();
638
869
  this._isDebugEnabled = config.debug ?? false;
870
+ this._preserveFocus = config.preserveFocus ?? true;
639
871
  }
640
872
  /**
641
873
  * 创建响应式对象
@@ -673,6 +905,104 @@ var ReactiveWebComponent = class extends WebComponent {
673
905
  this.rerender();
674
906
  }
675
907
  }
908
+ /**
909
+ * 重写 rerender 方法以支持焦点保持
910
+ */
911
+ rerender() {
912
+ if (!this.connected) {
913
+ return;
914
+ }
915
+ let focusData = null;
916
+ if (this._preserveFocus) {
917
+ const activeElement = this.shadowRoot.activeElement;
918
+ focusData = this.saveFocusState(activeElement);
919
+ }
920
+ super.rerender();
921
+ if (this._preserveFocus && focusData) {
922
+ this.restoreFocusState(focusData);
923
+ }
924
+ }
925
+ /**
926
+ * 保存焦点状态
927
+ */
928
+ saveFocusState(activeElement) {
929
+ if (!activeElement) {
930
+ return null;
931
+ }
932
+ const focusData = {
933
+ tagName: activeElement.tagName.toLowerCase(),
934
+ className: activeElement.className
935
+ };
936
+ if (activeElement.hasAttribute("contenteditable")) {
937
+ const selection = window.getSelection();
938
+ if (selection && selection.rangeCount > 0) {
939
+ const range = selection.getRangeAt(0);
940
+ focusData.selectionStart = range.startOffset;
941
+ focusData.selectionEnd = range.endOffset;
942
+ }
943
+ }
944
+ if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLSelectElement) {
945
+ focusData.value = activeElement.value;
946
+ if ("selectionStart" in activeElement) {
947
+ focusData.selectionStart = activeElement.selectionStart;
948
+ focusData.selectionEnd = activeElement.selectionEnd;
949
+ }
950
+ }
951
+ return focusData;
952
+ }
953
+ /**
954
+ * 恢复焦点状态
955
+ */
956
+ restoreFocusState(focusData) {
957
+ if (!focusData) return;
958
+ try {
959
+ let targetElement = null;
960
+ if (focusData.className) {
961
+ targetElement = this.shadowRoot.querySelector(
962
+ `.${focusData.className.split(" ")[0]}`
963
+ );
964
+ }
965
+ if (!targetElement) {
966
+ targetElement = this.shadowRoot.querySelector(focusData.tagName);
967
+ }
968
+ if (targetElement) {
969
+ targetElement.focus({ preventScroll: true });
970
+ if (focusData.selectionStart !== void 0) {
971
+ if (targetElement instanceof HTMLInputElement) {
972
+ targetElement.setSelectionRange(
973
+ focusData.selectionStart,
974
+ focusData.selectionEnd
975
+ );
976
+ } else if (targetElement instanceof HTMLSelectElement) {
977
+ targetElement.value = focusData.value;
978
+ } else if (targetElement.hasAttribute("contenteditable")) {
979
+ this.setCursorPosition(targetElement, focusData.selectionStart);
980
+ }
981
+ }
982
+ }
983
+ } catch {
984
+ }
985
+ }
986
+ /**
987
+ * 设置光标位置
988
+ */
989
+ setCursorPosition(element, position) {
990
+ try {
991
+ const selection = window.getSelection();
992
+ if (selection) {
993
+ const range = document.createRange();
994
+ const textNode = element.childNodes[0];
995
+ if (textNode) {
996
+ const maxPos = Math.min(position, textNode.textContent?.length || 0);
997
+ range.setStart(textNode, maxPos);
998
+ range.setEnd(textNode, maxPos);
999
+ selection.removeAllRanges();
1000
+ selection.addRange(range);
1001
+ }
1002
+ }
1003
+ } catch {
1004
+ }
1005
+ }
676
1006
  /**
677
1007
  * 获取所有响应式状态的快照(用于调试)
678
1008
  */
@@ -736,6 +1066,7 @@ function createReactiveComponent(ComponentClass, config) {
736
1066
  // Annotate the CommonJS export names for ESM import in node:
737
1067
  0 && (module.exports = {
738
1068
  Fragment,
1069
+ LightComponent,
739
1070
  ReactiveDebug,
740
1071
  ReactiveWebComponent,
741
1072
  StyleManager,