@wsxjs/wsx-core 0.0.5
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/chunk-3CJEWYVF.mjs +197 -0
- package/dist/chunk-5JVEHB6H.mjs +197 -0
- package/dist/chunk-7E7KJQSW.mjs +210 -0
- package/dist/chunk-A5GYVTI3.mjs +222 -0
- package/dist/chunk-A5GYVTI3.mjs.map +1 -0
- package/dist/chunk-BV2V6BVN.mjs +221 -0
- package/dist/chunk-K6N3JDTI.mjs +216 -0
- package/dist/chunk-RVGKV4GP.mjs +79 -0
- package/dist/chunk-S3O776FY.mjs +173 -0
- package/dist/chunk-VNK4B3FW.mjs +217 -0
- package/dist/chunk-YNUVFDKT.mjs +222 -0
- package/dist/chunk-YNUVFDKT.mjs.map +1 -0
- package/dist/index.d.mts +235 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +755 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +524 -0
- package/dist/index.mjs.map +1 -0
- package/dist/jsx-factory-pFUwL2Dz.d.mts +26 -0
- package/dist/jsx-factory-pFUwL2Dz.d.ts +26 -0
- package/dist/jsx-pFUwL2Dz.d.mts +26 -0
- package/dist/jsx-pFUwL2Dz.d.ts +26 -0
- package/dist/jsx-runtime-pFUwL2Dz.d.mts +26 -0
- package/dist/jsx-runtime-pFUwL2Dz.d.ts +26 -0
- package/dist/jsx-runtime.d.mts +1 -0
- package/dist/jsx-runtime.d.ts +1 -0
- package/dist/jsx-runtime.js +248 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/jsx-runtime.mjs +10 -0
- package/dist/jsx-runtime.mjs.map +1 -0
- package/dist/jsx.d.mts +66 -0
- package/dist/jsx.d.ts +66 -0
- package/dist/jsx.js +224 -0
- package/dist/jsx.js.map +1 -0
- package/dist/jsx.mjs +8 -0
- package/dist/jsx.mjs.map +1 -0
- package/package.json +49 -0
- package/src/auto-register.ts +149 -0
- package/src/index.ts +17 -0
- package/src/jsx-factory.ts +222 -0
- package/src/jsx-runtime.ts +6 -0
- package/src/jsx.ts +90 -0
- package/src/reactive-component.ts +171 -0
- package/src/styles/style-manager.ts +54 -0
- package/src/utils/logger.ts +69 -0
- package/src/utils/reactive.ts +214 -0
- package/src/utils/svg-utils.ts +184 -0
- package/src/web-component.ts +250 -0
- package/types/css-inline.d.ts +4 -0
- package/types/index.d.ts +32 -0
- package/types/jsx-runtime.d.ts +2 -0
- package/types/jsx.d.ts +28 -0
- package/types/wsx-types.d.ts +43 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 纯原生JSX工厂 - 零依赖的EditorJS Web Component支持
|
|
3
|
+
*
|
|
4
|
+
* 特点:
|
|
5
|
+
* - 完全独立,不依赖React或任何框架
|
|
6
|
+
* - 支持标准JSX语法
|
|
7
|
+
* - 原生DOM操作,性能优异
|
|
8
|
+
* - 完全通用,适用于任何Web Components
|
|
9
|
+
* - TypeScript类型安全
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// JSX 类型声明已移至 types/wsx-types.d.ts
|
|
13
|
+
|
|
14
|
+
import { createElement, shouldUseSVGNamespace, getSVGAttributeName } from "./utils/svg-utils";
|
|
15
|
+
|
|
16
|
+
// JSX子元素类型
|
|
17
|
+
export type JSXChildren =
|
|
18
|
+
| string
|
|
19
|
+
| number
|
|
20
|
+
| HTMLElement
|
|
21
|
+
| SVGElement
|
|
22
|
+
| DocumentFragment
|
|
23
|
+
| JSXChildren[]
|
|
24
|
+
| null
|
|
25
|
+
| undefined
|
|
26
|
+
| boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 纯原生JSX工厂函数
|
|
30
|
+
*
|
|
31
|
+
* @param tag - HTML标签名或组件函数
|
|
32
|
+
* @param props - 属性对象
|
|
33
|
+
* @param children - 子元素
|
|
34
|
+
* @returns DOM元素
|
|
35
|
+
*/
|
|
36
|
+
export function h(
|
|
37
|
+
tag:
|
|
38
|
+
| string
|
|
39
|
+
| ((
|
|
40
|
+
props: Record<string, unknown> | null,
|
|
41
|
+
children: JSXChildren[]
|
|
42
|
+
) => HTMLElement | SVGElement),
|
|
43
|
+
props: Record<string, unknown> | null = {},
|
|
44
|
+
...children: JSXChildren[]
|
|
45
|
+
): HTMLElement | SVGElement {
|
|
46
|
+
// 处理组件函数
|
|
47
|
+
if (typeof tag === "function") {
|
|
48
|
+
return tag(props, children);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 创建DOM元素 - 自动检测SVG命名空间
|
|
52
|
+
const element = createElement(tag);
|
|
53
|
+
|
|
54
|
+
// 处理属性
|
|
55
|
+
if (props) {
|
|
56
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
57
|
+
|
|
58
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
59
|
+
if (value === null || value === undefined || value === false) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 处理ref回调
|
|
64
|
+
if (key === "ref" && typeof value === "function") {
|
|
65
|
+
value(element);
|
|
66
|
+
}
|
|
67
|
+
// 处理className和class
|
|
68
|
+
else if (key === "className" || key === "class") {
|
|
69
|
+
if (isSVG) {
|
|
70
|
+
// SVG元素使用class属性
|
|
71
|
+
element.setAttribute("class", value as string);
|
|
72
|
+
} else {
|
|
73
|
+
// HTML元素可以使用className
|
|
74
|
+
(element as HTMLElement).className = value as string;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 处理style
|
|
78
|
+
else if (key === "style" && typeof value === "string") {
|
|
79
|
+
element.setAttribute("style", value);
|
|
80
|
+
}
|
|
81
|
+
// 处理事件监听器
|
|
82
|
+
else if (key.startsWith("on") && typeof value === "function") {
|
|
83
|
+
const eventName = key.slice(2).toLowerCase();
|
|
84
|
+
element.addEventListener(eventName, value as EventListener);
|
|
85
|
+
}
|
|
86
|
+
// 处理布尔属性
|
|
87
|
+
else if (typeof value === "boolean") {
|
|
88
|
+
if (value) {
|
|
89
|
+
element.setAttribute(key, "");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 处理其他属性
|
|
93
|
+
else {
|
|
94
|
+
// 对SVG元素使用正确的属性名
|
|
95
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
96
|
+
element.setAttribute(attributeName, String(value));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 处理子元素
|
|
102
|
+
const flatChildren = flattenChildren(children);
|
|
103
|
+
flatChildren.forEach((child) => {
|
|
104
|
+
if (child === null || child === undefined || child === false) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
109
|
+
element.appendChild(document.createTextNode(String(child)));
|
|
110
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
111
|
+
element.appendChild(child);
|
|
112
|
+
} else if (child instanceof DocumentFragment) {
|
|
113
|
+
element.appendChild(child);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return element;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 扁平化子元素数组
|
|
122
|
+
*/
|
|
123
|
+
function flattenChildren(
|
|
124
|
+
children: JSXChildren[]
|
|
125
|
+
): (string | number | HTMLElement | SVGElement | DocumentFragment | boolean | null | undefined)[] {
|
|
126
|
+
const result: (
|
|
127
|
+
| string
|
|
128
|
+
| number
|
|
129
|
+
| HTMLElement
|
|
130
|
+
| SVGElement
|
|
131
|
+
| DocumentFragment
|
|
132
|
+
| boolean
|
|
133
|
+
| null
|
|
134
|
+
| undefined
|
|
135
|
+
)[] = [];
|
|
136
|
+
|
|
137
|
+
for (const child of children) {
|
|
138
|
+
if (child === null || child === undefined || child === false) {
|
|
139
|
+
continue;
|
|
140
|
+
} else if (Array.isArray(child)) {
|
|
141
|
+
result.push(...flattenChildren(child));
|
|
142
|
+
} else {
|
|
143
|
+
result.push(child);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* JSX Fragment支持 - 用于包装多个子元素
|
|
152
|
+
*/
|
|
153
|
+
export function Fragment(_props: unknown, children: JSXChildren[]): DocumentFragment {
|
|
154
|
+
const fragment = document.createDocumentFragment();
|
|
155
|
+
const flatChildren = flattenChildren(children);
|
|
156
|
+
|
|
157
|
+
flatChildren.forEach((child) => {
|
|
158
|
+
if (child === null || child === undefined || child === false) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
163
|
+
fragment.appendChild(document.createTextNode(String(child)));
|
|
164
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
165
|
+
fragment.appendChild(child);
|
|
166
|
+
} else if (child instanceof DocumentFragment) {
|
|
167
|
+
fragment.appendChild(child);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return fragment;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* JSX function for React's new JSX transform
|
|
176
|
+
* Handles the new format: jsx(tag, { children: child, ...props })
|
|
177
|
+
*/
|
|
178
|
+
export function jsx(
|
|
179
|
+
tag:
|
|
180
|
+
| string
|
|
181
|
+
| ((
|
|
182
|
+
props: Record<string, unknown> | null,
|
|
183
|
+
children: JSXChildren[]
|
|
184
|
+
) => HTMLElement | SVGElement),
|
|
185
|
+
props: Record<string, unknown> | null
|
|
186
|
+
): HTMLElement | SVGElement {
|
|
187
|
+
if (!props) {
|
|
188
|
+
return h(tag, null);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const { children, ...restProps } = props;
|
|
192
|
+
if (children !== undefined) {
|
|
193
|
+
return h(tag, restProps, children);
|
|
194
|
+
}
|
|
195
|
+
return h(tag, restProps);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* JSX function for multiple children in React's new JSX transform
|
|
200
|
+
* Handles the new format: jsxs(tag, { children: [child1, child2], ...props })
|
|
201
|
+
*/
|
|
202
|
+
export function jsxs(
|
|
203
|
+
tag:
|
|
204
|
+
| string
|
|
205
|
+
| ((
|
|
206
|
+
props: Record<string, unknown> | null,
|
|
207
|
+
children: JSXChildren[]
|
|
208
|
+
) => HTMLElement | SVGElement),
|
|
209
|
+
props: Record<string, unknown> | null
|
|
210
|
+
): HTMLElement | SVGElement {
|
|
211
|
+
if (!props) {
|
|
212
|
+
return h(tag, null);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const { children, ...restProps } = props;
|
|
216
|
+
if (Array.isArray(children)) {
|
|
217
|
+
return h(tag, restProps, ...children);
|
|
218
|
+
} else if (children !== undefined) {
|
|
219
|
+
return h(tag, restProps, children);
|
|
220
|
+
}
|
|
221
|
+
return h(tag, restProps);
|
|
222
|
+
}
|
package/src/jsx.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-namespace */
|
|
2
|
+
/**
|
|
3
|
+
* JSX 入口点
|
|
4
|
+
* 专门为 jsxImportSource 机制提供 JSX 工厂函数
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// 导入 JSX 工厂函数
|
|
8
|
+
import { h, Fragment } from "./jsx-factory";
|
|
9
|
+
|
|
10
|
+
// 全局 JSX 命名空间声明 - 这是 TypeScript jsxImportSource 机制的关键
|
|
11
|
+
declare global {
|
|
12
|
+
namespace JSX {
|
|
13
|
+
interface IntrinsicElements {
|
|
14
|
+
// 标准HTML元素
|
|
15
|
+
div: HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
button: HTMLAttributes<HTMLButtonElement>;
|
|
17
|
+
a: HTMLAttributes<HTMLAnchorElement>;
|
|
18
|
+
span: HTMLAttributes<HTMLSpanElement>;
|
|
19
|
+
input: HTMLAttributes<HTMLInputElement>;
|
|
20
|
+
p: HTMLAttributes<HTMLParagraphElement>;
|
|
21
|
+
h1: HTMLAttributes<HTMLHeadingElement>;
|
|
22
|
+
h2: HTMLAttributes<HTMLHeadingElement>;
|
|
23
|
+
h3: HTMLAttributes<HTMLHeadingElement>;
|
|
24
|
+
ul: HTMLAttributes<HTMLUListElement>;
|
|
25
|
+
li: HTMLAttributes<HTMLLIElement>;
|
|
26
|
+
section: HTMLAttributes<HTMLElement>;
|
|
27
|
+
// Web Components 元素
|
|
28
|
+
slot: HTMLAttributes<HTMLSlotElement>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface HTMLAttributes<T extends HTMLElement = HTMLElement> {
|
|
32
|
+
// 基础属性
|
|
33
|
+
className?: string;
|
|
34
|
+
class?: string;
|
|
35
|
+
id?: string;
|
|
36
|
+
style?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
title?: string;
|
|
39
|
+
type?: string;
|
|
40
|
+
value?: string;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
src?: string;
|
|
43
|
+
alt?: string;
|
|
44
|
+
href?: string | null;
|
|
45
|
+
target?: string;
|
|
46
|
+
rel?: string;
|
|
47
|
+
download?: string;
|
|
48
|
+
|
|
49
|
+
// Ref callback with proper typing
|
|
50
|
+
ref?: (element: T) => void;
|
|
51
|
+
|
|
52
|
+
// Data attributes
|
|
53
|
+
[dataAttr: `data-${string}`]: string;
|
|
54
|
+
|
|
55
|
+
// Event handlers
|
|
56
|
+
onClick?: (event: Event) => void;
|
|
57
|
+
onInput?: (event: Event) => void;
|
|
58
|
+
onChange?: (event: Event) => void;
|
|
59
|
+
onMouseOver?: (event: Event) => void;
|
|
60
|
+
onMouseOut?: (event: Event) => void;
|
|
61
|
+
onFocus?: (event: Event) => void;
|
|
62
|
+
onBlur?: (event: Event) => void;
|
|
63
|
+
onMouseDown?: (event: MouseEvent) => void;
|
|
64
|
+
onKeyDown?: (event: KeyboardEvent) => void;
|
|
65
|
+
|
|
66
|
+
// 允许任意属性
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 核心 JSX 接口
|
|
71
|
+
type Element = HTMLElement;
|
|
72
|
+
|
|
73
|
+
interface ElementClass {
|
|
74
|
+
render(): Element;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface ElementAttributesProperty {
|
|
78
|
+
props: object;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ElementChildrenAttribute {
|
|
82
|
+
children: object;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type LibraryManagedAttributes<_C, P> = P;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 导出 JSX 工厂函数
|
|
90
|
+
export { h, Fragment };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 响应式 WSX Web Component
|
|
3
|
+
*
|
|
4
|
+
* 扩展基础 WebComponent,提供响应式状态支持
|
|
5
|
+
* 遵循 WSX 理念:可选使用,不破坏现有组件
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { WebComponent, type WebComponentConfig } from "./web-component";
|
|
9
|
+
import { reactive, createState, reactiveWithDebug } from "./utils/reactive";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 响应式 WebComponent 配置
|
|
13
|
+
*/
|
|
14
|
+
export interface ReactiveWebComponentConfig extends WebComponentConfig {
|
|
15
|
+
/** 是否启用响应式调试模式 */
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 响应式 WebComponent 基类
|
|
21
|
+
*
|
|
22
|
+
* 提供响应式状态管理能力,同时保持与标准 WebComponent 的完全兼容
|
|
23
|
+
*/
|
|
24
|
+
export abstract class ReactiveWebComponent extends WebComponent {
|
|
25
|
+
private _isDebugEnabled: boolean = false;
|
|
26
|
+
private _reactiveStates = new Map<string, any>();
|
|
27
|
+
|
|
28
|
+
constructor(config: ReactiveWebComponentConfig = {}) {
|
|
29
|
+
super(config);
|
|
30
|
+
|
|
31
|
+
this._isDebugEnabled = config.debug ?? false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 创建响应式对象
|
|
36
|
+
*
|
|
37
|
+
* @param obj 要变为响应式的对象
|
|
38
|
+
* @param debugName 调试名称(可选)
|
|
39
|
+
* @returns 响应式代理对象
|
|
40
|
+
*/
|
|
41
|
+
protected reactive<T extends object>(obj: T, debugName?: string): T {
|
|
42
|
+
const reactiveFn = this._isDebugEnabled ? reactiveWithDebug : reactive;
|
|
43
|
+
const name = debugName || `${this.constructor.name}.reactive`;
|
|
44
|
+
|
|
45
|
+
return this._isDebugEnabled
|
|
46
|
+
? reactiveFn(obj, () => this.scheduleRerender(), name)
|
|
47
|
+
: reactiveFn(obj, () => this.scheduleRerender());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 创建响应式状态
|
|
52
|
+
*
|
|
53
|
+
* @param key 状态标识符
|
|
54
|
+
* @param initialValue 初始值
|
|
55
|
+
* @returns [getter, setter] 元组
|
|
56
|
+
*/
|
|
57
|
+
protected useState<T>(
|
|
58
|
+
key: string,
|
|
59
|
+
initialValue: T
|
|
60
|
+
): [() => T, (value: T | ((prev: T) => T)) => void] {
|
|
61
|
+
if (!this._reactiveStates.has(key)) {
|
|
62
|
+
const [getter, setter] = createState(initialValue, () => this.scheduleRerender());
|
|
63
|
+
this._reactiveStates.set(key, { getter, setter });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const state = this._reactiveStates.get(key);
|
|
67
|
+
return [state.getter, state.setter];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 调度重渲染
|
|
72
|
+
* 这个方法被响应式系统调用,开发者通常不需要直接调用
|
|
73
|
+
*/
|
|
74
|
+
protected scheduleRerender(): void {
|
|
75
|
+
// 确保组件已连接到 DOM
|
|
76
|
+
if (this.connected) {
|
|
77
|
+
this.rerender();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 获取所有响应式状态的快照(用于调试)
|
|
83
|
+
*/
|
|
84
|
+
protected getStateSnapshot(): Record<string, any> {
|
|
85
|
+
const snapshot: Record<string, any> = {};
|
|
86
|
+
|
|
87
|
+
this._reactiveStates.forEach((state, key) => {
|
|
88
|
+
snapshot[key] = state.getter();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return snapshot;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 清理响应式状态(组件销毁时)
|
|
96
|
+
*/
|
|
97
|
+
protected cleanupReactiveStates(): void {
|
|
98
|
+
this._reactiveStates.clear();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 重写 disconnectedCallback 以清理状态
|
|
103
|
+
*/
|
|
104
|
+
disconnectedCallback(): void {
|
|
105
|
+
super.disconnectedCallback();
|
|
106
|
+
this.cleanupReactiveStates();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 启用调试模式
|
|
111
|
+
*/
|
|
112
|
+
protected enableDebug(): void {
|
|
113
|
+
this._isDebugEnabled = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 禁用调试模式
|
|
118
|
+
*/
|
|
119
|
+
protected disableDebug(): void {
|
|
120
|
+
this._isDebugEnabled = false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 装饰器:自动使组件变为响应式
|
|
126
|
+
*
|
|
127
|
+
* @param debugMode 是否启用调试模式
|
|
128
|
+
*/
|
|
129
|
+
export function makeReactive(_debugMode: boolean = false) {
|
|
130
|
+
return function <T extends new (...args: any[]) => WebComponent>(constructor: T) {
|
|
131
|
+
return class ReactiveComponent extends constructor {
|
|
132
|
+
constructor(...args: any[]) {
|
|
133
|
+
super(...args);
|
|
134
|
+
|
|
135
|
+
// 如果不是 ReactiveWebComponent 的实例,则混入响应式能力
|
|
136
|
+
if (!(this instanceof ReactiveWebComponent)) {
|
|
137
|
+
// 添加响应式方法
|
|
138
|
+
(this as any).reactive = function <U extends object>(obj: U): U {
|
|
139
|
+
return reactive(obj, () => (this as any).rerender());
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
render(): HTMLElement {
|
|
145
|
+
// 抽象方法必须由子类实现
|
|
146
|
+
throw new Error("render() method must be implemented by subclass");
|
|
147
|
+
}
|
|
148
|
+
} as T;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 工具函数:创建响应式组件实例
|
|
154
|
+
*/
|
|
155
|
+
export function createReactiveComponent<T extends WebComponent>(
|
|
156
|
+
ComponentClass: new (...args: any[]) => T,
|
|
157
|
+
config?: ReactiveWebComponentConfig
|
|
158
|
+
): T {
|
|
159
|
+
// 如果已经是响应式组件,直接创建实例
|
|
160
|
+
if (ComponentClass.prototype instanceof ReactiveWebComponent) {
|
|
161
|
+
return new ComponentClass(config);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 否则使用装饰器包装
|
|
165
|
+
const ReactiveComponent = makeReactive(config?.debug)(ComponentClass);
|
|
166
|
+
return new ReactiveComponent(config);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 导出响应式相关的所有功能
|
|
170
|
+
export { reactive, createState, ReactiveDebug } from "./utils/reactive";
|
|
171
|
+
export type { ReactiveCallback } from "./utils/reactive";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style Manager for TextColorTool Components
|
|
3
|
+
*
|
|
4
|
+
* This class manages CSS styles for Web Components using modern techniques:
|
|
5
|
+
* 1. CSS imports with Vite/bundler support
|
|
6
|
+
* 2. Constructable StyleSheets for better performance
|
|
7
|
+
* 3. Shared style sheets across component instances
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class StyleManager {
|
|
11
|
+
private static styleSheets: Map<string, CSSStyleSheet> = new Map();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create or get a cached CSSStyleSheet for a component
|
|
15
|
+
*/
|
|
16
|
+
static getStyleSheet(componentName: string, cssText: string): CSSStyleSheet {
|
|
17
|
+
if (this.styleSheets.has(componentName)) {
|
|
18
|
+
return this.styleSheets.get(componentName) as CSSStyleSheet;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if browser supports constructable stylesheets
|
|
22
|
+
if ("adoptedStyleSheets" in Document.prototype && "CSSStyleSheet" in window) {
|
|
23
|
+
const styleSheet = new CSSStyleSheet();
|
|
24
|
+
styleSheet.replaceSync(cssText);
|
|
25
|
+
this.styleSheets.set(componentName, styleSheet);
|
|
26
|
+
return styleSheet;
|
|
27
|
+
} else {
|
|
28
|
+
// Fallback for older browsers - this won't be cached but works
|
|
29
|
+
throw new Error("Constructable StyleSheets not supported. Use fallback method.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Apply styles to a shadow root using constructable stylesheets
|
|
35
|
+
*/
|
|
36
|
+
static applyStyles(shadowRoot: ShadowRoot, componentName: string, cssText: string): void {
|
|
37
|
+
try {
|
|
38
|
+
const styleSheet = this.getStyleSheet(componentName, cssText);
|
|
39
|
+
shadowRoot.adoptedStyleSheets = [styleSheet];
|
|
40
|
+
} catch {
|
|
41
|
+
// Fallback to traditional <style> element
|
|
42
|
+
this.applyStylesFallback(shadowRoot, cssText);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fallback method for browsers that don't support constructable stylesheets
|
|
48
|
+
*/
|
|
49
|
+
static applyStylesFallback(shadowRoot: ShadowRoot, cssText: string): void {
|
|
50
|
+
const style = document.createElement("style");
|
|
51
|
+
style.textContent = cssText;
|
|
52
|
+
shadowRoot.appendChild(style);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WSX Framework Logger
|
|
3
|
+
*
|
|
4
|
+
* A lightweight logging utility for the WSX framework.
|
|
5
|
+
* Can be extended or replaced by consuming applications.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
9
|
+
|
|
10
|
+
export interface Logger {
|
|
11
|
+
debug(message: string, ...args: unknown[]): void;
|
|
12
|
+
info(message: string, ...args: unknown[]): void;
|
|
13
|
+
warn(message: string, ...args: unknown[]): void;
|
|
14
|
+
error(message: string, ...args: unknown[]): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class WSXLogger implements Logger {
|
|
18
|
+
private prefix: string;
|
|
19
|
+
private enabled: boolean;
|
|
20
|
+
private level: LogLevel;
|
|
21
|
+
|
|
22
|
+
constructor(prefix: string = "[WSX]", enabled: boolean = true, level: LogLevel = "info") {
|
|
23
|
+
this.prefix = prefix;
|
|
24
|
+
this.enabled = enabled;
|
|
25
|
+
this.level = level;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private shouldLog(level: LogLevel): boolean {
|
|
29
|
+
if (!this.enabled) return false;
|
|
30
|
+
|
|
31
|
+
const levels: LogLevel[] = ["debug", "info", "warn", "error"];
|
|
32
|
+
const currentLevelIndex = levels.indexOf(this.level);
|
|
33
|
+
const messageLevelIndex = levels.indexOf(level);
|
|
34
|
+
|
|
35
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
debug(message: string, ...args: unknown[]): void {
|
|
39
|
+
if (this.shouldLog("debug")) {
|
|
40
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
info(message: string, ...args: unknown[]): void {
|
|
45
|
+
if (this.shouldLog("info")) {
|
|
46
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
warn(message: string, ...args: unknown[]): void {
|
|
51
|
+
if (this.shouldLog("warn")) {
|
|
52
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
error(message: string, ...args: unknown[]): void {
|
|
57
|
+
if (this.shouldLog("error")) {
|
|
58
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Default logger instance
|
|
64
|
+
export const logger = new WSXLogger();
|
|
65
|
+
|
|
66
|
+
// Factory function for creating component-specific loggers
|
|
67
|
+
export function createLogger(componentName: string): Logger {
|
|
68
|
+
return new WSXLogger(`[WSX:${componentName}]`);
|
|
69
|
+
}
|