@yoyaflow/yoya-ui 0.3.2 → 0.4.0
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/README.md +407 -124
- package/README.zh-CN.md +375 -131
- package/dist/yoya.core.chunk.js +4275 -0
- package/dist/yoya.core.chunk.min.js +9 -0
- package/dist/yoya.core.js +3 -1779
- package/dist/yoya.core.min.js +1 -0
- package/dist/yoya.devtools.js +3 -0
- package/dist/yoya.devtools.min.js +1 -0
- package/dist/yoya.echart.js +182 -592
- package/dist/yoya.echart.min.js +1 -0
- package/dist/yoya.router.full.js +5271 -0
- package/dist/yoya.router.full.min.js +43 -0
- package/dist/yoya.router.js +1330 -0
- package/dist/yoya.router.min.js +35 -0
- package/dist/yoya.three.js +360 -0
- package/dist/yoya.three.min.js +1 -0
- package/dist/yoya.ui-router.full.js +21843 -0
- package/dist/yoya.ui-router.full.min.js +43 -0
- package/dist/yoya.ui-router.umd.js +43 -0
- package/dist/yoya.ui-router.umd.min.js +43 -0
- package/dist/yoya.ui.css +80 -0
- package/dist/yoya.ui.full.js +20169 -0
- package/dist/yoya.ui.full.min.js +1 -0
- package/dist/yoya.ui.js +12227 -9083
- package/dist/yoya.ui.min.js +1 -0
- package/package.json +27 -11
- package/types/core.d.ts +122 -1
- package/types/data-display.d.ts +44 -2
- package/types/devtools.d.ts +156 -0
- package/types/feedback.d.ts +13 -0
- package/types/form.d.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/ssr.d.ts +27 -7
- package/types/three.d.ts +77 -0
- package/types/yoya.devtools.d.ts +5 -0
- package/types/yoya.router.d.ts +7 -0
- package/types/yoya.three.d.ts +4 -0
- package/types/yoya.ui-router.d.ts +19 -0
- package/types/yoya.ui.d.ts +2 -7
- package/dist/yoya-ui.umd.js +0 -35
- package/dist/yoya.ssr.js +0 -2856
- package/types/yoya.ssr.d.ts +0 -9
|
@@ -0,0 +1,4275 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/core/access.js
|
|
19
|
+
/**
|
|
20
|
+
* Access control: read/write permission with a compact string spec.
|
|
21
|
+
*
|
|
22
|
+
* Components declare bare resource codes only, e.g. "system:member".
|
|
23
|
+
* The level lives entirely in the user's granted set; on the component side
|
|
24
|
+
* every declared resource is write-gated (disabled without write).
|
|
25
|
+
*
|
|
26
|
+
* Matching against the granted set held by createAccess():
|
|
27
|
+
* bare <code> = read + write (full access, aligns with common RBAC)
|
|
28
|
+
* r.<code> = read-only
|
|
29
|
+
* w.<code> = read + write (explicit)
|
|
30
|
+
*
|
|
31
|
+
* canRead(code) = bare | r.<code> | w.<code>
|
|
32
|
+
* canWrite(code) = bare | w.<code>
|
|
33
|
+
*
|
|
34
|
+
* A super admin (roles including superAdmins) bypasses all checks. No declared
|
|
35
|
+
* access on a node means "always allowed" (fail-open), so existing code is
|
|
36
|
+
* unaffected until it opts in.
|
|
37
|
+
*/
|
|
38
|
+
let scopedAccess = null;
|
|
39
|
+
let installedAccess = null;
|
|
40
|
+
/**
|
|
41
|
+
* Normalizes a permission spec into { code, level }.
|
|
42
|
+
* Components pass bare codes to access(); the r./w. prefix is meaningful on
|
|
43
|
+
* the user granted set and is parsed here only to extract the resource code.
|
|
44
|
+
*/
|
|
45
|
+
function parseAccessSpec(spec) {
|
|
46
|
+
if (!spec) return null;
|
|
47
|
+
if (typeof spec === "object" && spec.code) return {
|
|
48
|
+
code: spec.code,
|
|
49
|
+
level: spec.level === "write" ? "write" : "read"
|
|
50
|
+
};
|
|
51
|
+
const value = String(spec).trim();
|
|
52
|
+
if (!value) return null;
|
|
53
|
+
if (value.startsWith("w.")) return {
|
|
54
|
+
code: value.slice(2),
|
|
55
|
+
level: "write"
|
|
56
|
+
};
|
|
57
|
+
if (value.startsWith("r.")) return {
|
|
58
|
+
code: value.slice(2),
|
|
59
|
+
level: "read"
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
code: value,
|
|
63
|
+
level: "write"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Returns the resource code of a spec, without its read/write prefix. */
|
|
67
|
+
function stripAccessCode(spec) {
|
|
68
|
+
const parsed = parseAccessSpec(spec);
|
|
69
|
+
return parsed ? parsed.code : String(spec ?? "");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a per-request access context. It is the single source of granted
|
|
73
|
+
* permissions during a render; inject it with withAccess(access, build).
|
|
74
|
+
*/
|
|
75
|
+
function createAccess({ permissions = [], roles = [], superAdmins = ["super_admin"] } = {}) {
|
|
76
|
+
const state = {
|
|
77
|
+
permissions: [...permissions || []],
|
|
78
|
+
roles: [...roles || []],
|
|
79
|
+
superAdmins: [...superAdmins || []]
|
|
80
|
+
};
|
|
81
|
+
const granted = new Set(state.permissions);
|
|
82
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
83
|
+
const isSuper = () => state.superAdmins.some((role) => state.roles.includes(role));
|
|
84
|
+
function canLevel(bare, level) {
|
|
85
|
+
if (isSuper()) return true;
|
|
86
|
+
if (level === "write") return granted.has(`w.${bare}`) || granted.has(bare);
|
|
87
|
+
return granted.has(`r.${bare}`) || granted.has(`w.${bare}`) || granted.has(bare);
|
|
88
|
+
}
|
|
89
|
+
function canSpec(spec, level) {
|
|
90
|
+
return canLevel(stripAccessCode(spec), level);
|
|
91
|
+
}
|
|
92
|
+
const access = {
|
|
93
|
+
roles() {
|
|
94
|
+
return state.roles.slice();
|
|
95
|
+
},
|
|
96
|
+
permissions() {
|
|
97
|
+
return state.permissions.slice();
|
|
98
|
+
},
|
|
99
|
+
isSuper,
|
|
100
|
+
has(spec) {
|
|
101
|
+
const bare = stripAccessCode(spec);
|
|
102
|
+
return isSuper() || granted.has(`r.${bare}`) || granted.has(`w.${bare}`) || granted.has(bare);
|
|
103
|
+
},
|
|
104
|
+
canRead(spec) {
|
|
105
|
+
return canSpec(spec, "read");
|
|
106
|
+
},
|
|
107
|
+
canWrite(spec) {
|
|
108
|
+
return canSpec(spec, "write");
|
|
109
|
+
},
|
|
110
|
+
setPermissions(next) {
|
|
111
|
+
state.permissions = [...next || []];
|
|
112
|
+
granted.clear();
|
|
113
|
+
(next || []).forEach((entry) => granted.add(entry));
|
|
114
|
+
listeners.forEach((fn) => fn());
|
|
115
|
+
return access;
|
|
116
|
+
},
|
|
117
|
+
subscribe(listener) {
|
|
118
|
+
listeners.add(listener);
|
|
119
|
+
return () => listeners.delete(listener);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return access;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Runs build() with the given access context active, then restores the outer
|
|
126
|
+
* context. Mirrors withI18nStringShortcut for per-request SSR isolation.
|
|
127
|
+
*/
|
|
128
|
+
function withAccess(access, build) {
|
|
129
|
+
const previous = scopedAccess;
|
|
130
|
+
scopedAccess = access || previous;
|
|
131
|
+
try {
|
|
132
|
+
return build();
|
|
133
|
+
} finally {
|
|
134
|
+
scopedAccess = previous;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Installs a global access context (single-user SPA); passes it to SSR entries as options.access otherwise. */
|
|
138
|
+
function installAccess(access) {
|
|
139
|
+
installedAccess = access || null;
|
|
140
|
+
return installedAccess;
|
|
141
|
+
}
|
|
142
|
+
/** Returns the scoped access context, falling back to the globally installed one. */
|
|
143
|
+
function currentAccess() {
|
|
144
|
+
return scopedAccess || installedAccess;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/core/node.js
|
|
149
|
+
const devtoolsBridgeKey$1 = Symbol.for("yoya.devtools.bridge");
|
|
150
|
+
function currentDevtoolsBridge() {
|
|
151
|
+
return typeof globalThis === "undefined" ? null : globalThis[devtoolsBridgeKey$1] || null;
|
|
152
|
+
}
|
|
153
|
+
function isDevtoolsEnabled$1() {
|
|
154
|
+
const bridge = currentDevtoolsBridge();
|
|
155
|
+
return bridge ? bridge.enabled() : false;
|
|
156
|
+
}
|
|
157
|
+
function emitDevtools$1(event) {
|
|
158
|
+
const bridge = currentDevtoolsBridge();
|
|
159
|
+
if (bridge) bridge.emit(event);
|
|
160
|
+
}
|
|
161
|
+
function unregisterDevtoolsNode$1(node) {
|
|
162
|
+
const bridge = currentDevtoolsBridge();
|
|
163
|
+
if (bridge) bridge.unregister(node);
|
|
164
|
+
}
|
|
165
|
+
function captureDevtoolsNodeScope$1(node) {
|
|
166
|
+
const bridge = currentDevtoolsBridge();
|
|
167
|
+
if (bridge) bridge.captureScope(node);
|
|
168
|
+
}
|
|
169
|
+
function ensureDevtoolsNodeId$1(node) {
|
|
170
|
+
const bridge = currentDevtoolsBridge();
|
|
171
|
+
return bridge ? bridge.ensureId(node) : void 0;
|
|
172
|
+
}
|
|
173
|
+
function notifyDevtoolsMutation$1(node, type, details) {
|
|
174
|
+
const bridge = currentDevtoolsBridge();
|
|
175
|
+
if (bridge) bridge.notify(node, type, details);
|
|
176
|
+
}
|
|
177
|
+
function commitDevtoolsNode$1(node) {
|
|
178
|
+
const bridge = currentDevtoolsBridge();
|
|
179
|
+
if (bridge) bridge.commit(node);
|
|
180
|
+
}
|
|
181
|
+
const booleanAttributes = /* @__PURE__ */ new Set([
|
|
182
|
+
"checked",
|
|
183
|
+
"disabled",
|
|
184
|
+
"readonly",
|
|
185
|
+
"selected"
|
|
186
|
+
]);
|
|
187
|
+
const accessDisabledTags = /* @__PURE__ */ new Set([
|
|
188
|
+
"input",
|
|
189
|
+
"select",
|
|
190
|
+
"textarea",
|
|
191
|
+
"button",
|
|
192
|
+
"fieldset",
|
|
193
|
+
"option",
|
|
194
|
+
"optgroup",
|
|
195
|
+
"keygen"
|
|
196
|
+
]);
|
|
197
|
+
const accessReadOnlyTags = /* @__PURE__ */ new Set(["input", "textarea"]);
|
|
198
|
+
const renderScopeStack = [];
|
|
199
|
+
function currentInheritedScope() {
|
|
200
|
+
return renderScopeStack.length > 0 ? renderScopeStack[renderScopeStack.length - 1] : null;
|
|
201
|
+
}
|
|
202
|
+
function withRenderScope(spec, build) {
|
|
203
|
+
renderScopeStack.push(spec);
|
|
204
|
+
try {
|
|
205
|
+
return build();
|
|
206
|
+
} finally {
|
|
207
|
+
renderScopeStack.pop();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
let activeBindingScope = null;
|
|
211
|
+
/**
|
|
212
|
+
* 在指定绑定作用域内执行 build。scope 形如 { bindings: [], getState(): state },
|
|
213
|
+
* 其中的 setter 收到函数值时会登记绑定。
|
|
214
|
+
*/
|
|
215
|
+
function withBindingScope(scope, build) {
|
|
216
|
+
const previous = activeBindingScope;
|
|
217
|
+
activeBindingScope = scope;
|
|
218
|
+
try {
|
|
219
|
+
return build();
|
|
220
|
+
} finally {
|
|
221
|
+
activeBindingScope = previous;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function registerNodeBinding(kind, key, read, commit) {
|
|
225
|
+
if (!activeBindingScope) throw new TypeError(`vStateNode binding scope required for function value (${kind}${key ? ` "${key}"` : ""})`);
|
|
226
|
+
const scope = activeBindingScope;
|
|
227
|
+
scope.bindings.push({
|
|
228
|
+
commit,
|
|
229
|
+
committed: false,
|
|
230
|
+
evaluate: () => read(scope.getState()),
|
|
231
|
+
key,
|
|
232
|
+
kind,
|
|
233
|
+
last: void 0
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const voidElements = /* @__PURE__ */ new Set([
|
|
237
|
+
"area",
|
|
238
|
+
"base",
|
|
239
|
+
"br",
|
|
240
|
+
"col",
|
|
241
|
+
"embed",
|
|
242
|
+
"hr",
|
|
243
|
+
"img",
|
|
244
|
+
"input",
|
|
245
|
+
"link",
|
|
246
|
+
"meta",
|
|
247
|
+
"param",
|
|
248
|
+
"source",
|
|
249
|
+
"track",
|
|
250
|
+
"wbr"
|
|
251
|
+
]);
|
|
252
|
+
/**
|
|
253
|
+
* 支持用 CSS 选择器或真实 DOM 元素作为挂载目标。
|
|
254
|
+
*/
|
|
255
|
+
function resolveTarget(target) {
|
|
256
|
+
if (typeof target === "string") return document.querySelector(target);
|
|
257
|
+
return target;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* toHTML 输出时使用的最小 HTML 转义。
|
|
261
|
+
*/
|
|
262
|
+
function escapeHtml(value) {
|
|
263
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
264
|
+
}
|
|
265
|
+
function isBooleanAttribute(name) {
|
|
266
|
+
return booleanAttributes.has(name);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 把属性写入真实 DOM,并尽量同步同名 DOM property。
|
|
270
|
+
*/
|
|
271
|
+
function applyAttribute(element, name, value) {
|
|
272
|
+
if (value === null || value === void 0 || value === false) {
|
|
273
|
+
element.removeAttribute(name);
|
|
274
|
+
if (name in element) try {
|
|
275
|
+
element[name] = false;
|
|
276
|
+
} catch {}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (value === true || isBooleanAttribute(name)) {
|
|
280
|
+
element.setAttribute(name, name);
|
|
281
|
+
if (name in element) try {
|
|
282
|
+
element[name] = true;
|
|
283
|
+
} catch {}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
element.setAttribute(name, String(value));
|
|
287
|
+
if (name in element) try {
|
|
288
|
+
element[name] = value;
|
|
289
|
+
} catch {}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* 序列化 style 快照,保证 toHTML 和真实 DOM 渲染保持一致。
|
|
293
|
+
*/
|
|
294
|
+
function serializeStyles(styles) {
|
|
295
|
+
return Object.entries(styles).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([name, value]) => `${toKebabStyleName(name)}:${escapeHtml(value)}`).join("; ");
|
|
296
|
+
}
|
|
297
|
+
function toKebabStyleName(name) {
|
|
298
|
+
return String(name).replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
299
|
+
}
|
|
300
|
+
function sameEventListenerOptions(a, b) {
|
|
301
|
+
if (a === b) return true;
|
|
302
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
303
|
+
return Boolean(a.capture) === Boolean(b.capture) && Boolean(a.once) === Boolean(b.once) && Boolean(a.passive) === Boolean(b.passive);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* ViewNode 是 yoya-ui 的基础视图节点。
|
|
307
|
+
* 它只管理视图树通用能力:子节点、事件清理、状态和生命周期。
|
|
308
|
+
*/
|
|
309
|
+
var ViewNode = class ViewNode {
|
|
310
|
+
constructor(setup = null) {
|
|
311
|
+
this._children = [];
|
|
312
|
+
this._childKeys = /* @__PURE__ */ new Map();
|
|
313
|
+
this._events = /* @__PURE__ */ new Map();
|
|
314
|
+
this._domAdapters = /* @__PURE__ */ new Map();
|
|
315
|
+
this._cleanup = [];
|
|
316
|
+
this._states = {};
|
|
317
|
+
this._stateTypes = {};
|
|
318
|
+
this._stateHandlers = /* @__PURE__ */ new Map();
|
|
319
|
+
this._pendingRemovals = /* @__PURE__ */ new Set();
|
|
320
|
+
this._childrenDirty = false;
|
|
321
|
+
this._deleted = false;
|
|
322
|
+
this._access = null;
|
|
323
|
+
this._accessContext = currentAccess();
|
|
324
|
+
if (isDevtoolsEnabled$1()) captureDevtoolsNodeScope$1(this);
|
|
325
|
+
if (setup !== null) this.setup(setup);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 统一初始化入口,支持函数、文本和对象配置三种写法。
|
|
329
|
+
*/
|
|
330
|
+
setup(setup) {
|
|
331
|
+
if (typeof setup === "function") setup(this);
|
|
332
|
+
else if (setup instanceof ViewNode) this.child(setup);
|
|
333
|
+
else if (typeof setup === "string" || typeof setup === "number") this.text(setup);
|
|
334
|
+
else if (setup && typeof setup === "object") this._setupObject(setup);
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 声明当前节点的权限:只写裸资源码(如 "system:member")。
|
|
339
|
+
* 无读不渲染,有读无写则只读/禁用;作用域就近覆盖。
|
|
340
|
+
*/
|
|
341
|
+
access(spec) {
|
|
342
|
+
const parsed = parseAccessSpec(spec);
|
|
343
|
+
this._access = parsed ? {
|
|
344
|
+
code: parsed.code,
|
|
345
|
+
level: "write"
|
|
346
|
+
} : null;
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 计算本节点(在当前作用域下)的权限状态。
|
|
351
|
+
* 规则:自身声明覆盖继承作用域,无声明则继承最近的祖先声明。
|
|
352
|
+
* @returns {'active' | 'readonly' | 'hidden'}
|
|
353
|
+
*/
|
|
354
|
+
_permissionState() {
|
|
355
|
+
const spec = this._access ?? currentInheritedScope();
|
|
356
|
+
if (!spec) return "active";
|
|
357
|
+
const access = this._accessContext || currentAccess();
|
|
358
|
+
if (!access) return "active";
|
|
359
|
+
if (!access.canRead(spec.code)) return "hidden";
|
|
360
|
+
if (spec.level === "write" && !access.canWrite(spec.code)) return "readonly";
|
|
361
|
+
return "active";
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 把权限状态落位到组件自身:只读/禁用由组件按自身语义处理;
|
|
365
|
+
* hidden 由渲染管线统一拒绝挂载,本方法无需处理 hidden。
|
|
366
|
+
* 基类 ViewNode 无操作;ElementNode 处理可交互标签,vInput / vButton 等重写。
|
|
367
|
+
*/
|
|
368
|
+
_applyAccessState(_state) {}
|
|
369
|
+
_setupObject(config) {
|
|
370
|
+
if (config.children) this.child(config.children);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* 返回子节点快照,避免外部直接修改内部数组。
|
|
374
|
+
*/
|
|
375
|
+
children() {
|
|
376
|
+
return [...this._children];
|
|
377
|
+
}
|
|
378
|
+
clearChildren() {
|
|
379
|
+
const removedIds = this._el && isDevtoolsEnabled$1() && !this._devtoolsRendering ? this._children.map((child) => ensureDevtoolsNodeId$1(child)) : [];
|
|
380
|
+
this._dropChildKeys(this._children);
|
|
381
|
+
this._children.forEach((child) => {
|
|
382
|
+
this._pendingRemovals.add(child);
|
|
383
|
+
});
|
|
384
|
+
this._children = [];
|
|
385
|
+
this._childrenDirty = true;
|
|
386
|
+
if (removedIds.length > 0 && !this._devtoolsRendering) notifyDevtoolsMutation$1(this, "child", { removed: removedIds });
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* 带显式 key 添加子节点;key 在同一父节点内必须唯一。
|
|
391
|
+
* 元素子节点会把 key 镜像为 data-row-key,便于 SSR 与调试。
|
|
392
|
+
*/
|
|
393
|
+
addChild(key, child) {
|
|
394
|
+
const rawKey = String(key);
|
|
395
|
+
if (this._childKeys.has(rawKey)) throw new TypeError(`duplicate key "${rawKey}"`);
|
|
396
|
+
const viewNode = normalizeChildWithContext(this, child);
|
|
397
|
+
this._childKeys.set(rawKey, viewNode);
|
|
398
|
+
if (typeof viewNode.attr === "function") viewNode.attr("data-row-key", rawKey);
|
|
399
|
+
this._pendingRemovals.delete(viewNode);
|
|
400
|
+
this._children.push(viewNode);
|
|
401
|
+
this._childrenDirty = true;
|
|
402
|
+
if (this._el) {
|
|
403
|
+
const childElement = withRenderScope(this._access ?? currentInheritedScope(), () => viewNode.renderDom());
|
|
404
|
+
if (childElement && childElement.parentNode !== this._el) this._el.appendChild(childElement);
|
|
405
|
+
if (isDevtoolsEnabled$1() && !this._devtoolsRendering) notifyDevtoolsMutation$1(this, "child", { added: [ensureDevtoolsNodeId$1(viewNode)] });
|
|
406
|
+
}
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
/** 按 key 读取子节点;不存在返回 null。 */
|
|
410
|
+
getChild(key) {
|
|
411
|
+
return this._childKeys.get(String(key)) || null;
|
|
412
|
+
}
|
|
413
|
+
/** 按 key 移除并销毁子节点。 */
|
|
414
|
+
removeChild(key) {
|
|
415
|
+
const rawKey = String(key);
|
|
416
|
+
const viewNode = this._childKeys.get(rawKey);
|
|
417
|
+
if (!viewNode) return this;
|
|
418
|
+
this._childKeys.delete(rawKey);
|
|
419
|
+
const index = this._children.indexOf(viewNode);
|
|
420
|
+
if (index !== -1) this._children.splice(index, 1);
|
|
421
|
+
this._childrenDirty = true;
|
|
422
|
+
const removedId = this._el && isDevtoolsEnabled$1() && !this._devtoolsRendering ? ensureDevtoolsNodeId$1(viewNode) : null;
|
|
423
|
+
viewNode.destroy();
|
|
424
|
+
if (removedId !== null) notifyDevtoolsMutation$1(this, "child", { removed: [removedId] });
|
|
425
|
+
return this;
|
|
426
|
+
}
|
|
427
|
+
_dropChildKeys(nodes) {
|
|
428
|
+
const doomed = new Set(nodes);
|
|
429
|
+
this._childKeys.forEach((child, key) => {
|
|
430
|
+
if (doomed.has(child)) this._childKeys.delete(key);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 添加子节点;字符串和数字会自动转成 VTextNode。
|
|
435
|
+
*/
|
|
436
|
+
child(...children) {
|
|
437
|
+
children.flat(Infinity).forEach((child) => {
|
|
438
|
+
if (child === null || child === void 0) return;
|
|
439
|
+
const viewNode = normalizeChildWithContext(this, child);
|
|
440
|
+
this._pendingRemovals.delete(viewNode);
|
|
441
|
+
this._children.push(viewNode);
|
|
442
|
+
this._childrenDirty = true;
|
|
443
|
+
});
|
|
444
|
+
return this;
|
|
445
|
+
}
|
|
446
|
+
text(content) {
|
|
447
|
+
return this.child(new VTextNode(content));
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* 注册事件。同一节点同一事件只保留最新 handler;
|
|
451
|
+
* 真实 DOM 上每个事件最多挂一个转发 adapter。
|
|
452
|
+
*/
|
|
453
|
+
on(eventName, handler, options) {
|
|
454
|
+
if (typeof handler !== "function") throw new TypeError("ViewNode event handler must be a function");
|
|
455
|
+
const previous = this._events.get(eventName);
|
|
456
|
+
this._events.set(eventName, {
|
|
457
|
+
handler,
|
|
458
|
+
options
|
|
459
|
+
});
|
|
460
|
+
if (this._el) this._bindDomAdapter(eventName, previous?.options, options);
|
|
461
|
+
return this;
|
|
462
|
+
}
|
|
463
|
+
off(eventName) {
|
|
464
|
+
this._events.delete(eventName);
|
|
465
|
+
const entry = this._domAdapters.get(eventName);
|
|
466
|
+
if (entry) {
|
|
467
|
+
entry.cleanup();
|
|
468
|
+
this._removeCleanup(entry.cleanup);
|
|
469
|
+
this._domAdapters.delete(eventName);
|
|
470
|
+
}
|
|
471
|
+
return this;
|
|
472
|
+
}
|
|
473
|
+
_bindDomAdapter(eventName, previousOptions, nextOptions) {
|
|
474
|
+
const existing = this._domAdapters.get(eventName);
|
|
475
|
+
if (existing) {
|
|
476
|
+
if (sameEventListenerOptions(previousOptions, nextOptions)) return;
|
|
477
|
+
existing.cleanup();
|
|
478
|
+
this._removeCleanup(existing.cleanup);
|
|
479
|
+
this._domAdapters.delete(eventName);
|
|
480
|
+
}
|
|
481
|
+
const adapter = (event) => {
|
|
482
|
+
const current = this._events.get(eventName);
|
|
483
|
+
if (!current || typeof current.handler !== "function") return;
|
|
484
|
+
current.handler.call(this, event);
|
|
485
|
+
if (current.options?.once) this.off(eventName);
|
|
486
|
+
};
|
|
487
|
+
const cleanup = () => {
|
|
488
|
+
if (this._el) this._el.removeEventListener(eventName, adapter, nextOptions);
|
|
489
|
+
};
|
|
490
|
+
this._el.addEventListener(eventName, adapter, nextOptions);
|
|
491
|
+
this._domAdapters.set(eventName, { cleanup });
|
|
492
|
+
this._cleanup.push(cleanup);
|
|
493
|
+
}
|
|
494
|
+
_removeCleanup(cleanup) {
|
|
495
|
+
const index = this._cleanup.indexOf(cleanup);
|
|
496
|
+
if (index !== -1) this._cleanup.splice(index, 1);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* 声明节点可识别的状态字段,默认状态类型是 boolean。
|
|
500
|
+
*/
|
|
501
|
+
registerStateAttrs(...attrs) {
|
|
502
|
+
attrs.forEach((attr) => {
|
|
503
|
+
if (typeof attr === "string") {
|
|
504
|
+
this._stateTypes[attr] = "boolean";
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (attr && typeof attr === "object") Object.entries(attr).forEach(([name, type]) => {
|
|
508
|
+
this._stateTypes[name] = type || "boolean";
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
return this;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 注册状态处理器。状态改变时处理器负责同步样式、属性或内部结构。
|
|
515
|
+
*/
|
|
516
|
+
registerStateHandler(stateName, handler) {
|
|
517
|
+
if (!this._stateHandlers.has(stateName)) this._stateHandlers.set(stateName, []);
|
|
518
|
+
this._stateHandlers.get(stateName).push(handler);
|
|
519
|
+
return this;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 设置状态并触发对应处理器。
|
|
523
|
+
*/
|
|
524
|
+
setState(stateName, value = true) {
|
|
525
|
+
const oldValue = this._states[stateName];
|
|
526
|
+
this._states[stateName] = value;
|
|
527
|
+
(this._stateHandlers.get(stateName) || []).forEach((handler) => handler(value, this, oldValue));
|
|
528
|
+
return this;
|
|
529
|
+
}
|
|
530
|
+
getState(stateName) {
|
|
531
|
+
return this._states[stateName];
|
|
532
|
+
}
|
|
533
|
+
getBooleanState(stateName) {
|
|
534
|
+
return Boolean(this.getState(stateName));
|
|
535
|
+
}
|
|
536
|
+
getStringState(stateName) {
|
|
537
|
+
const value = this.getState(stateName);
|
|
538
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
539
|
+
}
|
|
540
|
+
getNumberState(stateName) {
|
|
541
|
+
return Number(this.getState(stateName) || 0);
|
|
542
|
+
}
|
|
543
|
+
renderDom() {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
/** 将当前 ViewNode 树提交到真实 DOM。 */
|
|
547
|
+
commit() {
|
|
548
|
+
return this.renderDom();
|
|
549
|
+
}
|
|
550
|
+
_commitChildren() {
|
|
551
|
+
this._pendingRemovals.forEach((child) => child.destroy());
|
|
552
|
+
this._pendingRemovals.clear();
|
|
553
|
+
this._childrenDirty = false;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* 将当前节点挂载到选择器或 DOM 元素。
|
|
557
|
+
*/
|
|
558
|
+
bindTo(target) {
|
|
559
|
+
const parent = resolveTarget(target);
|
|
560
|
+
const element = this.renderDom();
|
|
561
|
+
if (parent && element) parent.appendChild(element);
|
|
562
|
+
return this;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* 销毁节点:清理事件、递归销毁子节点,并从 DOM 中移除自身。
|
|
566
|
+
*/
|
|
567
|
+
destroy() {
|
|
568
|
+
if (isDevtoolsEnabled$1()) {
|
|
569
|
+
emitDevtools$1({
|
|
570
|
+
type: "destroy",
|
|
571
|
+
node: this
|
|
572
|
+
});
|
|
573
|
+
unregisterDevtoolsNode$1(this);
|
|
574
|
+
}
|
|
575
|
+
this._deleted = true;
|
|
576
|
+
this._cleanup.forEach((cleanup) => cleanup());
|
|
577
|
+
this._cleanup = [];
|
|
578
|
+
this._children.forEach((child) => child.destroy());
|
|
579
|
+
this._pendingRemovals.forEach((child) => child.destroy());
|
|
580
|
+
this._pendingRemovals.clear();
|
|
581
|
+
this._children = [];
|
|
582
|
+
this._childKeys.clear();
|
|
583
|
+
if (this._el?.parentNode) this._el.parentNode.removeChild(this._el);
|
|
584
|
+
return this;
|
|
585
|
+
}
|
|
586
|
+
toHTML() {
|
|
587
|
+
return "";
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* hydration 后同步钩子:子类可在此从真实 DOM 回读状态(如表单控件值)。
|
|
591
|
+
*/
|
|
592
|
+
hydrateSnapshot() {
|
|
593
|
+
return this;
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* VTextNode 表示视图树中的文本节点,渲染时对应真实 Text 节点。
|
|
598
|
+
* 外部可以继续传入原始字符串,内部统一包装为 VTextNode。
|
|
599
|
+
*/
|
|
600
|
+
var VTextNode = class extends ViewNode {
|
|
601
|
+
constructor(content = "") {
|
|
602
|
+
super(null);
|
|
603
|
+
this._content = "";
|
|
604
|
+
this._textNode = null;
|
|
605
|
+
if (typeof content === "function") {
|
|
606
|
+
registerNodeBinding("text", null, content, (next) => this.textContent(next));
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
this._content = String(content);
|
|
610
|
+
}
|
|
611
|
+
textContent(value) {
|
|
612
|
+
if (value === void 0) return this._content;
|
|
613
|
+
if (typeof value === "function") {
|
|
614
|
+
registerNodeBinding("text", null, value, (next) => this.textContent(next));
|
|
615
|
+
return this;
|
|
616
|
+
}
|
|
617
|
+
const previous = this._content;
|
|
618
|
+
this._content = String(value);
|
|
619
|
+
if (this._textNode) this._textNode.textContent = this._content;
|
|
620
|
+
if (this._textNode && isDevtoolsEnabled$1() && !Object.is(previous, this._content)) notifyDevtoolsMutation$1(this, "text", {
|
|
621
|
+
from: previous,
|
|
622
|
+
to: this._content
|
|
623
|
+
});
|
|
624
|
+
return this;
|
|
625
|
+
}
|
|
626
|
+
renderDom() {
|
|
627
|
+
if (this._deleted || this._permissionState() === "hidden") return null;
|
|
628
|
+
if (!this._textNode) {
|
|
629
|
+
this._textNode = document.createTextNode(this._content);
|
|
630
|
+
this._el = this._textNode;
|
|
631
|
+
}
|
|
632
|
+
return this._textNode;
|
|
633
|
+
}
|
|
634
|
+
toHTML() {
|
|
635
|
+
return this._deleted || this._permissionState() === "hidden" ? "" : escapeHtml(this._content);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* ComponentNode 延迟解析函数 Factory 或带 render() 的组件对象。
|
|
640
|
+
* render 返回单个 ViewNode 时按普通组件处理;返回 ViewNode 数组时按
|
|
641
|
+
* 多根 fragment 处理:不产生包装元素,父元素直接落实全部根节点。
|
|
642
|
+
*/
|
|
643
|
+
var ComponentNode = class extends ViewNode {
|
|
644
|
+
constructor(component) {
|
|
645
|
+
super(null);
|
|
646
|
+
this._component = component;
|
|
647
|
+
this._resolved = null;
|
|
648
|
+
this._resolvedList = null;
|
|
649
|
+
this._roots = null;
|
|
650
|
+
this._fragmentDom = null;
|
|
651
|
+
}
|
|
652
|
+
_resolve() {
|
|
653
|
+
if (this._resolvedList) return this._resolved;
|
|
654
|
+
const build = () => typeof this._component === "function" ? this._component() : this._component.render();
|
|
655
|
+
const resolved = withAccess(this._accessContext || currentAccess(), build);
|
|
656
|
+
const list = Array.isArray(resolved) ? resolved.slice() : [resolved];
|
|
657
|
+
const componentInfo = describeComponent(this._component);
|
|
658
|
+
const ownerInfo = this._owner ? ` It was added as a child of ${describeValue(this._owner)}.` : "";
|
|
659
|
+
list.forEach((item) => {
|
|
660
|
+
if (!(item instanceof ViewNode)) throw new TypeError(`Component render must return a ViewNode or an array of ViewNodes. render() of ${componentInfo} returned ${describeValue(item)}.${ownerInfo} If render() returns a component object (for example vPagination({ ... })), attach it with parent.child(...) instead of returning it directly.`);
|
|
661
|
+
});
|
|
662
|
+
this._resolvedList = list;
|
|
663
|
+
this._resolved = list[0] || null;
|
|
664
|
+
this._roots = Array.isArray(resolved) ? list : null;
|
|
665
|
+
if (this._component && typeof this._component === "object" && typeof this._component._attachHost === "function") this._component._attachHost(this);
|
|
666
|
+
return this._resolved;
|
|
667
|
+
}
|
|
668
|
+
_resolveList() {
|
|
669
|
+
this._resolve();
|
|
670
|
+
return this._resolvedList || [];
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* 组件主动替换解析结果:支持单个根或一组根;已挂载时原位换 DOM。
|
|
674
|
+
*/
|
|
675
|
+
_replaceResolved(nextView) {
|
|
676
|
+
const previousList = this._resolvedList || [];
|
|
677
|
+
const nextList = Array.isArray(nextView) ? nextView.slice() : [nextView];
|
|
678
|
+
nextList.forEach((item) => {
|
|
679
|
+
if (!(item instanceof ViewNode)) throw new TypeError("Component render must return a ViewNode or an array of ViewNodes");
|
|
680
|
+
});
|
|
681
|
+
if (previousList.length === nextList.length && previousList.every((root, index) => root === nextList[index])) return;
|
|
682
|
+
const oldNodes = [];
|
|
683
|
+
if (this._fragmentDom && this._fragmentDom.length > 0) oldNodes.push(...this._fragmentDom);
|
|
684
|
+
else if (previousList.length > 0) {
|
|
685
|
+
const previousElement = previousList[0]._el;
|
|
686
|
+
if (previousElement && previousElement.parentNode) oldNodes.push(previousElement);
|
|
687
|
+
}
|
|
688
|
+
this._resolvedList = nextList;
|
|
689
|
+
this._resolved = nextList[0] || null;
|
|
690
|
+
this._roots = Array.isArray(nextView) ? nextList : null;
|
|
691
|
+
this._fragmentDom = null;
|
|
692
|
+
if (oldNodes.length === 0) {
|
|
693
|
+
previousList.forEach((root) => root.destroy());
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const inherited = currentInheritedScope();
|
|
697
|
+
const parentNode = oldNodes[0].parentNode;
|
|
698
|
+
const nextNodes = [];
|
|
699
|
+
nextList.forEach((root) => {
|
|
700
|
+
const element = withRenderScope(this._access ?? inherited, () => root.renderDom());
|
|
701
|
+
if (element) nextNodes.push(element);
|
|
702
|
+
});
|
|
703
|
+
if (nextNodes.length > 0 && parentNode) nextNodes.forEach((element) => parentNode.insertBefore(element, oldNodes[0]));
|
|
704
|
+
oldNodes.forEach((element) => {
|
|
705
|
+
if (element.parentNode === parentNode) parentNode.removeChild(element);
|
|
706
|
+
});
|
|
707
|
+
if (this._roots) this._fragmentDom = nextNodes;
|
|
708
|
+
else this._el = nextNodes[0] || null;
|
|
709
|
+
previousList.forEach((root) => root.destroy());
|
|
710
|
+
}
|
|
711
|
+
children() {
|
|
712
|
+
if (!this._resolvedList) return [];
|
|
713
|
+
if (!this._roots) return this._resolvedList[0] ? this._resolvedList[0].children() : [];
|
|
714
|
+
return this._resolvedList.flatMap((root) => root.children());
|
|
715
|
+
}
|
|
716
|
+
textContent() {
|
|
717
|
+
return this._resolveList().map((root) => typeof root.textContent === "function" ? root.textContent() : "").join("");
|
|
718
|
+
}
|
|
719
|
+
renderDom() {
|
|
720
|
+
if (this._deleted || this._permissionState() === "hidden") return null;
|
|
721
|
+
const inherited = currentInheritedScope();
|
|
722
|
+
const list = this._resolveList();
|
|
723
|
+
if (this._roots) {
|
|
724
|
+
if (this._fragmentDom) return null;
|
|
725
|
+
const nodes = [];
|
|
726
|
+
list.forEach((root) => {
|
|
727
|
+
const element = withRenderScope(this._access ?? inherited, () => root.renderDom());
|
|
728
|
+
if (element) nodes.push(element);
|
|
729
|
+
});
|
|
730
|
+
this._fragmentDom = nodes;
|
|
731
|
+
const fragment = document.createDocumentFragment();
|
|
732
|
+
nodes.forEach((element) => fragment.appendChild(element));
|
|
733
|
+
return fragment;
|
|
734
|
+
}
|
|
735
|
+
const resolved = list[0];
|
|
736
|
+
return withRenderScope(this._access ?? inherited, () => {
|
|
737
|
+
const element = resolved.renderDom();
|
|
738
|
+
this._el = element;
|
|
739
|
+
return element;
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
toHTML() {
|
|
743
|
+
if (this._deleted || this._permissionState() === "hidden") return "";
|
|
744
|
+
const inherited = currentInheritedScope();
|
|
745
|
+
const list = this._resolveList();
|
|
746
|
+
return withRenderScope(this._access ?? inherited, () => list.map((root) => root.toHTML()).join(""));
|
|
747
|
+
}
|
|
748
|
+
destroy() {
|
|
749
|
+
if (this._component && typeof this._component === "object" && typeof this._component.destroy === "function") this._component.destroy();
|
|
750
|
+
(this._resolvedList || []).forEach((root) => root.destroy());
|
|
751
|
+
this._fragmentDom = null;
|
|
752
|
+
return super.destroy();
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
/**
|
|
756
|
+
* 创建文本节点的工厂函数。
|
|
757
|
+
*/
|
|
758
|
+
function vText(content = "") {
|
|
759
|
+
return new VTextNode(content);
|
|
760
|
+
}
|
|
761
|
+
function describeValue(value) {
|
|
762
|
+
if (value === null || value === void 0 || typeof value === "boolean") return String(value);
|
|
763
|
+
if (typeof value === "string" || typeof value === "number") return `${typeof value} ${JSON.stringify(value)}`;
|
|
764
|
+
if (typeof value === "function") return `function ${value.name || "(anonymous)"}`;
|
|
765
|
+
if (value instanceof ViewNode) {
|
|
766
|
+
const tag = typeof value._tagName === "string" ? ` <${value._tagName}>` : "";
|
|
767
|
+
return `${value.constructor.name}${tag}`;
|
|
768
|
+
}
|
|
769
|
+
if (value && typeof value.render === "function") return `component object${value.constructor && value.constructor !== Object ? ` ${value.constructor.name}` : ""} with render()`;
|
|
770
|
+
return `${value && value.constructor && value.constructor !== Object ? value.constructor.name : "Object"} instance`;
|
|
771
|
+
}
|
|
772
|
+
function describeComponent(component) {
|
|
773
|
+
if (typeof component === "function") return `function ${component.name || "(anonymous)"}`;
|
|
774
|
+
if (component && typeof component === "object") return `component object${typeof component.render === "function" && component.render.name ? ` (render ${component.render.name})` : ""}`;
|
|
775
|
+
return String(component);
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* 与父节点上下文一起规范化子节点:输入不合法时立即抛出可定位的错误。
|
|
779
|
+
*/
|
|
780
|
+
function normalizeChildWithContext(parent, child) {
|
|
781
|
+
try {
|
|
782
|
+
const viewNode = normalizeChild(child);
|
|
783
|
+
if (viewNode._owner === void 0) viewNode._owner = parent;
|
|
784
|
+
return viewNode;
|
|
785
|
+
} catch (error) {
|
|
786
|
+
if (error instanceof TypeError && String(error.message).startsWith("ViewNode child must")) throw new TypeError(`Invalid child for ${describeValue(parent)}: ${error.message} (received ${describeValue(child)}). child() accepts a ViewNode, a component object with render(), a function, a string, or a number.`, { cause: error });
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* 统一子节点输入,保证内部树只保存 ViewNode 实例。
|
|
792
|
+
*/
|
|
793
|
+
function normalizeChild(child) {
|
|
794
|
+
if (child instanceof ViewNode) return child;
|
|
795
|
+
if (typeof child === "function" || child && typeof child === "object" && typeof child.render === "function") return new ComponentNode(child);
|
|
796
|
+
if (typeof child === "string" || typeof child === "number") return new VTextNode(child);
|
|
797
|
+
throw new TypeError("ViewNode child must be a ViewNode, component, string, or number");
|
|
798
|
+
}
|
|
799
|
+
function normalizeSetupArguments(first = null, second = null, third = null) {
|
|
800
|
+
if (typeof second === "function" && (third === null || third === void 0)) return {
|
|
801
|
+
first,
|
|
802
|
+
options: null,
|
|
803
|
+
callback: second
|
|
804
|
+
};
|
|
805
|
+
return {
|
|
806
|
+
first,
|
|
807
|
+
options: second,
|
|
808
|
+
callback: third
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function applyElementOptions$1(node, options) {
|
|
812
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) return node;
|
|
813
|
+
if (options.attrs && typeof node.attr === "function") node.attr(options.attrs);
|
|
814
|
+
if (options.style && typeof node.styles === "function") node.styles(options.style);
|
|
815
|
+
return node;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* ElementNode 表示可渲染成真实 DOM Element 的视图节点。
|
|
819
|
+
* 它负责属性、类名、样式、事件和子节点到 DOM 的同步。
|
|
820
|
+
*/
|
|
821
|
+
var ElementNode = class extends ViewNode {
|
|
822
|
+
constructor(tagName, setup = null) {
|
|
823
|
+
super(null);
|
|
824
|
+
this._tagName = tagName;
|
|
825
|
+
this._attrs = {};
|
|
826
|
+
this._styles = {};
|
|
827
|
+
this._classes = /* @__PURE__ */ new Set();
|
|
828
|
+
this._el = null;
|
|
829
|
+
if (setup !== null) this.setup(setup);
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* 对象 setup 支持 class/style/children/onXxx 和普通属性配置。
|
|
833
|
+
*/
|
|
834
|
+
_setupObject(config) {
|
|
835
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
836
|
+
if (key === "class" || key === "className") {
|
|
837
|
+
this.className(value);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (key === "attrs") {
|
|
841
|
+
this.attr(value);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (key === "style") {
|
|
845
|
+
this.styles(value);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
if (key === "children") {
|
|
849
|
+
this.child(value);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
853
|
+
this.on(key.slice(2).toLowerCase(), value);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (typeof this[key] === "function") {
|
|
857
|
+
this[key](value);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (typeof value !== "function") this.attr(key, value);
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
tagName() {
|
|
864
|
+
return this._tagName;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* 获取当前元素及其子节点的聚合文本。
|
|
868
|
+
*/
|
|
869
|
+
textContent() {
|
|
870
|
+
return this._children.map((child) => typeof child.textContent === "function" ? child.textContent() : "").join("");
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* 读写属性。传入 null/undefined/false 时移除属性。
|
|
874
|
+
*/
|
|
875
|
+
attr(name, value) {
|
|
876
|
+
if (value === void 0 && typeof name === "string") return this._attrs[name];
|
|
877
|
+
if (name && typeof name === "object") {
|
|
878
|
+
Object.entries(name).forEach(([key, nextValue]) => this.attr(key, nextValue));
|
|
879
|
+
return this;
|
|
880
|
+
}
|
|
881
|
+
if (typeof value === "function") {
|
|
882
|
+
registerNodeBinding("attr", name, value, (next) => this.attr(name, next));
|
|
883
|
+
return this;
|
|
884
|
+
}
|
|
885
|
+
const previous = this._attrs[name];
|
|
886
|
+
if (value === null || value === void 0 || value === false) delete this._attrs[name];
|
|
887
|
+
else this._attrs[name] = value;
|
|
888
|
+
if (this._el) applyAttribute(this._el, name, value);
|
|
889
|
+
if (this._el && isDevtoolsEnabled$1() && !this._devtoolsRendering && !Object.is(previous, value)) notifyDevtoolsMutation$1(this, "attr", {
|
|
890
|
+
name,
|
|
891
|
+
previous,
|
|
892
|
+
next: value
|
|
893
|
+
});
|
|
894
|
+
return this;
|
|
895
|
+
}
|
|
896
|
+
id(value) {
|
|
897
|
+
return value === void 0 ? this.attr("id") : this.attr("id", value);
|
|
898
|
+
}
|
|
899
|
+
name(value) {
|
|
900
|
+
return value === void 0 ? this.attr("name") : this.attr("name", value);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* 添加类名,支持空格分隔、数组和多参数。
|
|
904
|
+
*/
|
|
905
|
+
className(...classes) {
|
|
906
|
+
if (classes.length === 0) return [...this._classes].join(" ");
|
|
907
|
+
classes.flat(Infinity).forEach((value) => {
|
|
908
|
+
if (!value) return;
|
|
909
|
+
String(value).split(/\s+/).filter(Boolean).forEach((className) => this._classes.add(className));
|
|
910
|
+
});
|
|
911
|
+
this._syncClassName();
|
|
912
|
+
return this;
|
|
913
|
+
}
|
|
914
|
+
class(...classes) {
|
|
915
|
+
return this.className(...classes);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* 替换预设类名:移除 old 并添加 next(支持空格分隔多个)。
|
|
919
|
+
* old 不存在时,tolerate 为 true 则仅添加 next;默认 false 为无操作。
|
|
920
|
+
*/
|
|
921
|
+
replaceClassName(old, next, tolerate = false) {
|
|
922
|
+
if (!old || !next || old === next) return this;
|
|
923
|
+
if (!this._classes.has(old)) return tolerate ? this.className(next) : this;
|
|
924
|
+
this._classes.delete(old);
|
|
925
|
+
return this.className(next);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* 读写单个样式;传入对象时转给 styles() 批量处理。
|
|
929
|
+
*/
|
|
930
|
+
style(name, value) {
|
|
931
|
+
if (value === void 0 && typeof name === "string") return this._styles[name];
|
|
932
|
+
if (name && typeof name === "object") return this.styles(name);
|
|
933
|
+
if (typeof value === "function") {
|
|
934
|
+
registerNodeBinding("style", name, value, (next) => this.style(name, next));
|
|
935
|
+
return this;
|
|
936
|
+
}
|
|
937
|
+
const previous = this._styles[name];
|
|
938
|
+
if (value === null || value === void 0 || value === "") delete this._styles[name];
|
|
939
|
+
else this._styles[name] = value;
|
|
940
|
+
if (this._el) this._el.style[name] = value || "";
|
|
941
|
+
if (this._el && isDevtoolsEnabled$1() && !this._devtoolsRendering && !Object.is(previous, value)) notifyDevtoolsMutation$1(this, "style", {
|
|
942
|
+
name,
|
|
943
|
+
previous,
|
|
944
|
+
next: value
|
|
945
|
+
});
|
|
946
|
+
return this;
|
|
947
|
+
}
|
|
948
|
+
styles(styles) {
|
|
949
|
+
Object.entries(styles || {}).forEach(([name, value]) => this.style(name, value));
|
|
950
|
+
return this;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* 添加子节点。如果当前 DOM 已创建,立即追加对应 DOM。
|
|
954
|
+
*/
|
|
955
|
+
child(...children) {
|
|
956
|
+
const addedIds = this._el && isDevtoolsEnabled$1() && !this._devtoolsRendering ? [] : null;
|
|
957
|
+
children.flat(Infinity).forEach((child) => {
|
|
958
|
+
if (child === null || child === void 0) return;
|
|
959
|
+
const viewNode = normalizeChildWithContext(this, child);
|
|
960
|
+
this._pendingRemovals.delete(viewNode);
|
|
961
|
+
this._children.push(viewNode);
|
|
962
|
+
this._childrenDirty = true;
|
|
963
|
+
if (this._el) {
|
|
964
|
+
const childElement = withRenderScope(this._access ?? currentInheritedScope(), () => viewNode.renderDom());
|
|
965
|
+
if (childElement && childElement.parentNode !== this._el) this._el.appendChild(childElement);
|
|
966
|
+
if (addedIds) addedIds.push(ensureDevtoolsNodeId$1(viewNode));
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
if (addedIds && addedIds.length > 0) notifyDevtoolsMutation$1(this, "child", { added: addedIds });
|
|
970
|
+
return this;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* 把只读/禁用落到原生元素:readonly 给可交互标签加属性,active 撤销,
|
|
974
|
+
* 支持权限热切换时原树复用。复合组件(vInput / vButton 等)各自重写。
|
|
975
|
+
*/
|
|
976
|
+
_applyAccessState(state) {
|
|
977
|
+
if (state === "readonly") {
|
|
978
|
+
this._accessAttrsApplied = true;
|
|
979
|
+
const tag = this._tagName;
|
|
980
|
+
if (accessDisabledTags.has(tag)) this.attr("disabled", true);
|
|
981
|
+
if (accessReadOnlyTags.has(tag)) this.attr("readonly", true);
|
|
982
|
+
this.attr("aria-disabled", "true");
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (this._accessAttrsApplied) {
|
|
986
|
+
this._accessAttrsApplied = false;
|
|
987
|
+
this.attr("disabled", null);
|
|
988
|
+
this.attr("readonly", null);
|
|
989
|
+
this.attr("aria-disabled", null);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* 创建或复用真实 DOM 元素。
|
|
994
|
+
*/
|
|
995
|
+
renderDom() {
|
|
996
|
+
if (this._deleted) return null;
|
|
997
|
+
const state = this._permissionState();
|
|
998
|
+
if (state === "hidden") return null;
|
|
999
|
+
const inherited = this._access ?? currentInheritedScope();
|
|
1000
|
+
if (isDevtoolsEnabled$1()) this._devtoolsRendering = true;
|
|
1001
|
+
try {
|
|
1002
|
+
if (!this._el) {
|
|
1003
|
+
this._el = document.createElement(this._tagName);
|
|
1004
|
+
withRenderScope(inherited, () => this._applySnapshotToElement());
|
|
1005
|
+
}
|
|
1006
|
+
this._applyAccessState(state);
|
|
1007
|
+
this._commitChildren();
|
|
1008
|
+
this._children.forEach((child) => {
|
|
1009
|
+
withRenderScope(inherited, () => {
|
|
1010
|
+
const childElement = child.renderDom();
|
|
1011
|
+
if (childElement && childElement.parentNode !== this._el) this._el.appendChild(childElement);
|
|
1012
|
+
else if (!childElement && child._el && child._el.parentNode === this._el) this._el.removeChild(child._el);
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
if (isDevtoolsEnabled$1()) commitDevtoolsNode$1(this);
|
|
1016
|
+
return this._el;
|
|
1017
|
+
} finally {
|
|
1018
|
+
this._devtoolsRendering = false;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* 将视图树序列化为 HTML 字符串,主要用于服务端模板或测试断言。
|
|
1023
|
+
*/
|
|
1024
|
+
toHTML() {
|
|
1025
|
+
if (this._deleted) return "";
|
|
1026
|
+
const state = this._permissionState();
|
|
1027
|
+
if (state === "hidden") return "";
|
|
1028
|
+
this._applyAccessState(state);
|
|
1029
|
+
return withRenderScope(this._access ?? currentInheritedScope(), () => {
|
|
1030
|
+
const attrs = this._serializeAttributes();
|
|
1031
|
+
const startTag = attrs ? `<${this._tagName} ${attrs}>` : `<${this._tagName}>`;
|
|
1032
|
+
if (voidElements.has(this._tagName)) return startTag;
|
|
1033
|
+
return `${startTag}${this._children.map((child) => child.toHTML()).join("")}</${this._tagName}>`;
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* DOM 首次创建时,把之前记录的属性、样式、事件和子节点一次性同步。
|
|
1038
|
+
*/
|
|
1039
|
+
_applyBindingsToElement() {
|
|
1040
|
+
Object.entries(this._attrs).forEach(([name, value]) => applyAttribute(this._el, name, value));
|
|
1041
|
+
this._syncClassName();
|
|
1042
|
+
Object.entries(this._styles).forEach(([name, value]) => {
|
|
1043
|
+
this._el.style[name] = value;
|
|
1044
|
+
});
|
|
1045
|
+
this._events.forEach((descriptor, eventName) => {
|
|
1046
|
+
this._bindDomAdapter(eventName, void 0, descriptor.options);
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
_applySnapshotToElement() {
|
|
1050
|
+
this._applyBindingsToElement();
|
|
1051
|
+
this._children.forEach((child) => {
|
|
1052
|
+
const childElement = child.renderDom();
|
|
1053
|
+
if (childElement) this._el.appendChild(childElement);
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* 同步 class 集合到属性快照和真实 DOM。
|
|
1058
|
+
*/
|
|
1059
|
+
_syncClassName() {
|
|
1060
|
+
const previous = this._attrs.class;
|
|
1061
|
+
const className = [...this._classes].join(" ");
|
|
1062
|
+
if (className) this._attrs.class = className;
|
|
1063
|
+
else delete this._attrs.class;
|
|
1064
|
+
if (this._el) {
|
|
1065
|
+
if (className) this._el.className = className;
|
|
1066
|
+
else this._el.removeAttribute("class");
|
|
1067
|
+
if (isDevtoolsEnabled$1() && !this._devtoolsRendering && !Object.is(previous, className)) notifyDevtoolsMutation$1(this, "attr", {
|
|
1068
|
+
name: "class",
|
|
1069
|
+
previous,
|
|
1070
|
+
next: className || void 0
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* 序列化属性快照,供 toHTML 使用。
|
|
1076
|
+
*/
|
|
1077
|
+
_serializeAttributes() {
|
|
1078
|
+
const attrs = { ...this._attrs };
|
|
1079
|
+
const styleText = this._serializeStyles();
|
|
1080
|
+
if (styleText) attrs.style = attrs.style ? `${attrs.style}; ${styleText}` : styleText;
|
|
1081
|
+
return Object.entries(attrs).filter(([, value]) => value !== null && value !== void 0 && value !== false).map(([name, value]) => {
|
|
1082
|
+
if (value === true || isBooleanAttribute(name)) return `${name}="${name}"`;
|
|
1083
|
+
return `${name}="${escapeHtml(value)}"`;
|
|
1084
|
+
}).join(" ");
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* 序列化 style 快照,保证 toHTML 和真实 DOM 渲染保持一致。
|
|
1088
|
+
*/
|
|
1089
|
+
_serializeStyles() {
|
|
1090
|
+
return serializeStyles(this._styles);
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
/**
|
|
1094
|
+
* 为标签创建工厂函数;默认使用 ElementNode,HTML/SVG 层可以传入自己的节点类。
|
|
1095
|
+
*/
|
|
1096
|
+
function createElementFactory(tagName, NodeClass = ElementNode) {
|
|
1097
|
+
return function elementFactory(first = null, second = null, third = null) {
|
|
1098
|
+
const args = normalizeSetupArguments(first, second, third);
|
|
1099
|
+
const node = new NodeClass(tagName, args.first);
|
|
1100
|
+
applyElementOptions$1(node, args.options);
|
|
1101
|
+
if (typeof args.callback === "function") args.callback(node);
|
|
1102
|
+
return node;
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* 把工厂函数注册为父节点快捷方法,使 page.h1('标题') 这类 DSL 写法成立。
|
|
1107
|
+
*/
|
|
1108
|
+
function registerChildFactories(NodeClass, factories, options = {}) {
|
|
1109
|
+
const { override = false } = options;
|
|
1110
|
+
Object.entries(factories).forEach(([name, factory]) => {
|
|
1111
|
+
if (!override && NodeClass.prototype[name]) return;
|
|
1112
|
+
NodeClass.prototype[name] = function childFactory(...args) {
|
|
1113
|
+
return this.child(factory(...args));
|
|
1114
|
+
};
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
//#endregion
|
|
1119
|
+
//#region src/core/client-only.js
|
|
1120
|
+
/**
|
|
1121
|
+
* ClientOnlyNode 标记"非服务端渲染"的组件模块(islands):
|
|
1122
|
+
* 服务端 toHTML 只输出占位 div;浏览器端 renderDom/hydration 时
|
|
1123
|
+
* 解析 loader 并替换占位,组件在客户端本地加载与初始化。
|
|
1124
|
+
*/
|
|
1125
|
+
var ClientOnlyNode = class extends ViewNode {
|
|
1126
|
+
constructor(loader) {
|
|
1127
|
+
super(null);
|
|
1128
|
+
this._loader = loader;
|
|
1129
|
+
this._resolved = null;
|
|
1130
|
+
}
|
|
1131
|
+
_resolve() {
|
|
1132
|
+
if (!this._resolved) this._resolved = resolveClientOnly(this._loader);
|
|
1133
|
+
return this._resolved;
|
|
1134
|
+
}
|
|
1135
|
+
toHTML() {
|
|
1136
|
+
return this._deleted ? "" : "<div class=\"yoya-client-only\" data-client-only=\"true\"></div>";
|
|
1137
|
+
}
|
|
1138
|
+
renderDom() {
|
|
1139
|
+
if (this._deleted) return null;
|
|
1140
|
+
const element = this._resolve().renderDom();
|
|
1141
|
+
if (this._el && this._el !== element && this._el.parentNode) this._el.parentNode.replaceChild(element, this._el);
|
|
1142
|
+
this._el = element;
|
|
1143
|
+
return element;
|
|
1144
|
+
}
|
|
1145
|
+
children() {
|
|
1146
|
+
return this._resolved ? this._resolved.children() : [];
|
|
1147
|
+
}
|
|
1148
|
+
textContent() {
|
|
1149
|
+
return this._resolved && typeof this._resolved.textContent === "function" ? this._resolved.textContent() : "";
|
|
1150
|
+
}
|
|
1151
|
+
destroy() {
|
|
1152
|
+
if (this._resolved) this._resolved.destroy();
|
|
1153
|
+
return super.destroy();
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
function vClientOnly(loader) {
|
|
1157
|
+
return new ClientOnlyNode(loader);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* 与 renderToString 的 createRootNode 一致:支持 ViewNode、函数工厂与带 render() 的对象组件。
|
|
1161
|
+
*/
|
|
1162
|
+
function resolveClientOnly(value) {
|
|
1163
|
+
if (value instanceof ViewNode) return value;
|
|
1164
|
+
if (typeof value === "function") return resolveClientOnly(value());
|
|
1165
|
+
if (value && typeof value.render === "function") return resolveClientOnly(value.render());
|
|
1166
|
+
throw new TypeError("vClientOnly loader must return a ViewNode or a component object");
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
//#endregion
|
|
1170
|
+
//#region src/core/document-events.js
|
|
1171
|
+
/**
|
|
1172
|
+
* 文档级事件绑定——组件代码中唯一允许接触 document 的位置。
|
|
1173
|
+
*
|
|
1174
|
+
* 外部点击、拖拽、Esc、滚动这类文档级监听无法用元素 DSL 表达,
|
|
1175
|
+
* 组件统一通过 bindDocumentEvent 注册:
|
|
1176
|
+
* - 自动加 typeof document 守卫,SSR 环境安全;
|
|
1177
|
+
* - 返回 unbind 函数,组件在 destroy / close 时调用即可。
|
|
1178
|
+
*/
|
|
1179
|
+
function bindDocumentEvent(type, handler, options = void 0) {
|
|
1180
|
+
if (typeof document === "undefined") return () => {};
|
|
1181
|
+
document.addEventListener(type, handler, options);
|
|
1182
|
+
return () => {
|
|
1183
|
+
document.removeEventListener(type, handler, options);
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
function unbindDocumentEvent(type, handler, options = void 0) {
|
|
1187
|
+
if (typeof document === "undefined") return;
|
|
1188
|
+
document.removeEventListener(type, handler, options);
|
|
1189
|
+
}
|
|
1190
|
+
/** window 级全局监听(scroll/resize/popstate 等)的同类收敛入口。 */
|
|
1191
|
+
function bindWindowEvent(type, handler, options = void 0) {
|
|
1192
|
+
if (typeof window === "undefined") return () => {};
|
|
1193
|
+
window.addEventListener(type, handler, options);
|
|
1194
|
+
return () => {
|
|
1195
|
+
window.removeEventListener(type, handler, options);
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
/** 注入 <style> 到 <head> 的收敛入口;dataAttribute 用于去重与标识。 */
|
|
1199
|
+
function injectDocumentStyle(styleText, dataAttribute = null) {
|
|
1200
|
+
if (typeof document === "undefined") return null;
|
|
1201
|
+
if (dataAttribute) {
|
|
1202
|
+
const existing = document.querySelector(`[${dataAttribute}]`);
|
|
1203
|
+
if (existing) return existing;
|
|
1204
|
+
}
|
|
1205
|
+
const style = document.createElement("style");
|
|
1206
|
+
if (dataAttribute) style.setAttribute(dataAttribute, "");
|
|
1207
|
+
style.textContent = styleText;
|
|
1208
|
+
document.head?.appendChild(style);
|
|
1209
|
+
return style;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
//#endregion
|
|
1213
|
+
//#region src/core/i18n.js
|
|
1214
|
+
/** 多 key locale 共享存储的默认记录键。 */
|
|
1215
|
+
const DEFAULT_LOCALES_STORAGE_KEY = "yoya-ui:i18n";
|
|
1216
|
+
/** 全局 I18n 实例注册表:key → 实例,供多 locale 场景按 key 查找与使用。 */
|
|
1217
|
+
const i18nRegistry = /* @__PURE__ */ new Map();
|
|
1218
|
+
/**
|
|
1219
|
+
* I18n 是最小国际化管理器:负责语言、词典、订阅通知、持久化和文本翻译。
|
|
1220
|
+
*/
|
|
1221
|
+
var I18n = class {
|
|
1222
|
+
constructor(options = {}) {
|
|
1223
|
+
const defaultLanguage = options.language || "zh-CN";
|
|
1224
|
+
this._key = options.key || null;
|
|
1225
|
+
this._storage = resolveStorage(options.storage);
|
|
1226
|
+
this._storageKey = options.storageKey || null;
|
|
1227
|
+
this._sharedStorageKey = this._key ? DEFAULT_LOCALES_STORAGE_KEY : null;
|
|
1228
|
+
this._fallbackLanguage = options.fallbackLanguage || defaultLanguage;
|
|
1229
|
+
this._language = this._readStoredLanguage(defaultLanguage);
|
|
1230
|
+
this._messages = {};
|
|
1231
|
+
this._listeners = /* @__PURE__ */ new Set();
|
|
1232
|
+
this.registerMessages(options.messages || {});
|
|
1233
|
+
if (this._key) registerI18n(this);
|
|
1234
|
+
}
|
|
1235
|
+
/** 返回该实例的 locale key;未配置时为 null。 */
|
|
1236
|
+
key() {
|
|
1237
|
+
return this._key;
|
|
1238
|
+
}
|
|
1239
|
+
getLanguage() {
|
|
1240
|
+
return this._language;
|
|
1241
|
+
}
|
|
1242
|
+
setLanguage(language) {
|
|
1243
|
+
if (!language || language === this._language) return this;
|
|
1244
|
+
this._language = language;
|
|
1245
|
+
this._persistLanguage();
|
|
1246
|
+
this._notify();
|
|
1247
|
+
return this;
|
|
1248
|
+
}
|
|
1249
|
+
getFallbackLanguage() {
|
|
1250
|
+
return this._fallbackLanguage;
|
|
1251
|
+
}
|
|
1252
|
+
setFallbackLanguage(language) {
|
|
1253
|
+
if (!language) return this;
|
|
1254
|
+
this._fallbackLanguage = language;
|
|
1255
|
+
this._notify();
|
|
1256
|
+
return this;
|
|
1257
|
+
}
|
|
1258
|
+
clearPersistedLanguage() {
|
|
1259
|
+
if (!this._storageKey) {
|
|
1260
|
+
if (!this._sharedStorageKey) return this;
|
|
1261
|
+
const record = readStorageRecord(this._storage, this._sharedStorageKey);
|
|
1262
|
+
delete record[this._key];
|
|
1263
|
+
writeStorageRecord(this._storage, this._sharedStorageKey, record);
|
|
1264
|
+
return this;
|
|
1265
|
+
}
|
|
1266
|
+
try {
|
|
1267
|
+
this._storage.removeItem(this._storageKey);
|
|
1268
|
+
} catch {}
|
|
1269
|
+
return this;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* 懒加载注册语言:loader 返回词典对象或 Promise,加载完成后合并并刷新订阅者。
|
|
1273
|
+
*/
|
|
1274
|
+
registerLocale(name, loader) {
|
|
1275
|
+
if (!name || typeof loader !== "function") return Promise.reject(/* @__PURE__ */ new TypeError("registerLocale requires a language name and a loader"));
|
|
1276
|
+
let result;
|
|
1277
|
+
try {
|
|
1278
|
+
result = loader();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
return Promise.reject(error);
|
|
1281
|
+
}
|
|
1282
|
+
return Promise.resolve(result).then((messages) => {
|
|
1283
|
+
this.register(name, messages || {});
|
|
1284
|
+
return this;
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* 注册或增量合并某个语言的词典。
|
|
1289
|
+
*/
|
|
1290
|
+
register(language, messages = {}) {
|
|
1291
|
+
if (!language) return this;
|
|
1292
|
+
normalizeMessageList(messages).forEach((messagePart) => {
|
|
1293
|
+
this._messages[language] = mergeMessages(this._messages[language] || {}, messagePart);
|
|
1294
|
+
});
|
|
1295
|
+
this._notify();
|
|
1296
|
+
return this;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* 注册一个或多个语料库文件。
|
|
1300
|
+
* 支持 { "zh-CN": {...} } 多语言文件、{ language, messages } 单语言文件和数组。
|
|
1301
|
+
*/
|
|
1302
|
+
registerMessages(corpus = {}) {
|
|
1303
|
+
normalizeCorpusList(corpus).forEach((corpusPart) => {
|
|
1304
|
+
if (corpusPart.language) {
|
|
1305
|
+
this.register(corpusPart.language, corpusPart.messages || {});
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
Object.entries(corpusPart).forEach(([language, messages]) => {
|
|
1309
|
+
this.register(language, messages);
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1312
|
+
return this;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* 翻译 key。支持 dot path、fallback language、默认文案和 {name} 参数替换。
|
|
1316
|
+
*/
|
|
1317
|
+
t(key, params = {}, defaultValue = void 0) {
|
|
1318
|
+
const message = readMessage(this._messages[this._language], key) ?? readMessage(this._messages[this._fallbackLanguage], key) ?? defaultValue ?? key;
|
|
1319
|
+
return interpolate(typeof message === "function" ? message(params, this) : message, params, this);
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* 创建随语言变化自动刷新的文本节点。
|
|
1323
|
+
*/
|
|
1324
|
+
text(key, params = {}, defaultValue = void 0) {
|
|
1325
|
+
return new I18nTextNode(this, key, params, defaultValue);
|
|
1326
|
+
}
|
|
1327
|
+
subscribe(listener) {
|
|
1328
|
+
this._listeners.add(listener);
|
|
1329
|
+
return () => {
|
|
1330
|
+
this._listeners.delete(listener);
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
_readStoredLanguage(defaultLanguage) {
|
|
1334
|
+
if (this._storageKey) try {
|
|
1335
|
+
return this._storage.getItem(this._storageKey) || defaultLanguage;
|
|
1336
|
+
} catch {
|
|
1337
|
+
return defaultLanguage;
|
|
1338
|
+
}
|
|
1339
|
+
if (this._sharedStorageKey) return readStorageRecord(this._storage, this._sharedStorageKey)[this._key] || defaultLanguage;
|
|
1340
|
+
return defaultLanguage;
|
|
1341
|
+
}
|
|
1342
|
+
_persistLanguage() {
|
|
1343
|
+
if (this._storageKey) {
|
|
1344
|
+
try {
|
|
1345
|
+
this._storage.setItem(this._storageKey, this._language);
|
|
1346
|
+
} catch {}
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
if (this._sharedStorageKey) {
|
|
1350
|
+
const record = readStorageRecord(this._storage, this._sharedStorageKey);
|
|
1351
|
+
record[this._key] = this._language;
|
|
1352
|
+
writeStorageRecord(this._storage, this._sharedStorageKey, record);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
_notify() {
|
|
1356
|
+
this._listeners.forEach((listener) => listener(this));
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
/**
|
|
1360
|
+
* I18nTextNode 继承 VTextNode,语言变化时只更新文本节点内容。
|
|
1361
|
+
*/
|
|
1362
|
+
var I18nTextNode = class extends VTextNode {
|
|
1363
|
+
constructor(i18n, key, params = {}, defaultValue = void 0) {
|
|
1364
|
+
super(i18n.t(key, params, defaultValue));
|
|
1365
|
+
this._i18n = i18n;
|
|
1366
|
+
this._key = key;
|
|
1367
|
+
this._params = params || {};
|
|
1368
|
+
this._defaultValue = defaultValue;
|
|
1369
|
+
this._unsubscribe = i18n.subscribe(() => this.refresh());
|
|
1370
|
+
}
|
|
1371
|
+
key(value) {
|
|
1372
|
+
if (value === void 0) return this._key;
|
|
1373
|
+
this._key = value;
|
|
1374
|
+
return this.refresh();
|
|
1375
|
+
}
|
|
1376
|
+
params(value) {
|
|
1377
|
+
if (value === void 0) return { ...this._params };
|
|
1378
|
+
this._params = value || {};
|
|
1379
|
+
return this.refresh();
|
|
1380
|
+
}
|
|
1381
|
+
defaultValue(value) {
|
|
1382
|
+
if (value === void 0) return this._defaultValue;
|
|
1383
|
+
this._defaultValue = value;
|
|
1384
|
+
return this.refresh();
|
|
1385
|
+
}
|
|
1386
|
+
refresh() {
|
|
1387
|
+
this.textContent(this._i18n.t(this._key, this._params, this._defaultValue));
|
|
1388
|
+
return this;
|
|
1389
|
+
}
|
|
1390
|
+
destroy() {
|
|
1391
|
+
if (this._unsubscribe) {
|
|
1392
|
+
this._unsubscribe();
|
|
1393
|
+
this._unsubscribe = null;
|
|
1394
|
+
}
|
|
1395
|
+
return super.destroy();
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
function createI18n(options = {}) {
|
|
1399
|
+
return new I18n(options);
|
|
1400
|
+
}
|
|
1401
|
+
const i18n = createI18n();
|
|
1402
|
+
function i18nText(key, params = {}) {
|
|
1403
|
+
return i18n.text(key, params);
|
|
1404
|
+
}
|
|
1405
|
+
let stringShortcutI18n = i18n;
|
|
1406
|
+
/**
|
|
1407
|
+
* 安装字符串快捷写法:"内容".s("content-key")。
|
|
1408
|
+
* 字符串本身作为默认文案,参数作为翻译 key;未显式指定 locale 时使用安装的默认 I18n 实例。
|
|
1409
|
+
*/
|
|
1410
|
+
function installI18nStringShortcut(locale = i18n) {
|
|
1411
|
+
stringShortcutI18n = locale;
|
|
1412
|
+
if (String.prototype._yoyaUiStringShortcutInstalled) return locale;
|
|
1413
|
+
Object.defineProperty(String.prototype, "_yoyaUiStringShortcutInstalled", {
|
|
1414
|
+
configurable: true,
|
|
1415
|
+
enumerable: false,
|
|
1416
|
+
value: true
|
|
1417
|
+
});
|
|
1418
|
+
Object.defineProperty(String.prototype, "s", {
|
|
1419
|
+
configurable: true,
|
|
1420
|
+
enumerable: false,
|
|
1421
|
+
value: function stringShortcut(key, paramsOrLocale, maybeLocale) {
|
|
1422
|
+
const defaultValue = String(this);
|
|
1423
|
+
let params = {};
|
|
1424
|
+
let locale = stringShortcutI18n;
|
|
1425
|
+
if (isLocaleLike(paramsOrLocale)) locale = paramsOrLocale;
|
|
1426
|
+
else if (typeof paramsOrLocale === "string") locale = getI18n(paramsOrLocale) || stringShortcutI18n;
|
|
1427
|
+
else {
|
|
1428
|
+
params = paramsOrLocale || {};
|
|
1429
|
+
if (isLocaleLike(maybeLocale)) locale = maybeLocale;
|
|
1430
|
+
else if (typeof maybeLocale === "string") locale = getI18n(maybeLocale) || stringShortcutI18n;
|
|
1431
|
+
}
|
|
1432
|
+
return locale.text(key || defaultValue, params, defaultValue);
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
return locale;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* 在指定 I18n 实例作用域内执行构建,使字符串快捷写法 ".s()" 使用该实例;
|
|
1439
|
+
* 结束后恢复外层实例,共享单例不被请求修改。
|
|
1440
|
+
*/
|
|
1441
|
+
function withI18nStringShortcut(locale, build) {
|
|
1442
|
+
const previous = stringShortcutI18n;
|
|
1443
|
+
stringShortcutI18n = locale || previous;
|
|
1444
|
+
try {
|
|
1445
|
+
return build();
|
|
1446
|
+
} finally {
|
|
1447
|
+
stringShortcutI18n = previous;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
function isLocaleLike(value) {
|
|
1451
|
+
return Boolean(value && typeof value.text === "function");
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* 注册 I18n 实例到全局注册表,按实例 key 索引;未配置 key 时不注册。
|
|
1455
|
+
* 返回取消注册函数。
|
|
1456
|
+
*/
|
|
1457
|
+
function registerI18n(instance) {
|
|
1458
|
+
const key = instance?.key?.();
|
|
1459
|
+
if (!key) return () => {};
|
|
1460
|
+
i18nRegistry.set(key, instance);
|
|
1461
|
+
return () => {
|
|
1462
|
+
if (i18nRegistry.get(key) === instance) i18nRegistry.delete(key);
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
/** 从全局注册表移除实例(接受 key 或实例)。 */
|
|
1466
|
+
function unregisterI18n(keyOrInstance) {
|
|
1467
|
+
const key = typeof keyOrInstance === "string" ? keyOrInstance : keyOrInstance?.key?.();
|
|
1468
|
+
if (key) i18nRegistry.delete(key);
|
|
1469
|
+
return key;
|
|
1470
|
+
}
|
|
1471
|
+
/** 按 key 查找全局注册的 I18n 实例;未找到返回 null。 */
|
|
1472
|
+
function getI18n(key) {
|
|
1473
|
+
return key ? i18nRegistry.get(key) || null : null;
|
|
1474
|
+
}
|
|
1475
|
+
/** 返回全局注册的 { key: instance } 副本。 */
|
|
1476
|
+
function listI18n() {
|
|
1477
|
+
return new Map(i18nRegistry);
|
|
1478
|
+
}
|
|
1479
|
+
/** 读取多 key locale 共享存储中的全部标识({ key: language })。 */
|
|
1480
|
+
function getPersistedI18nLocales(storage) {
|
|
1481
|
+
return readStorageRecord(storage || resolveStorage(), DEFAULT_LOCALES_STORAGE_KEY);
|
|
1482
|
+
}
|
|
1483
|
+
function readStorageRecord(storage, key) {
|
|
1484
|
+
if (!storage) return {};
|
|
1485
|
+
try {
|
|
1486
|
+
const raw = storage.getItem(key);
|
|
1487
|
+
if (!raw) return {};
|
|
1488
|
+
const parsed = JSON.parse(raw);
|
|
1489
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1490
|
+
} catch {
|
|
1491
|
+
return {};
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
function writeStorageRecord(storage, key, record) {
|
|
1495
|
+
if (!storage) return;
|
|
1496
|
+
try {
|
|
1497
|
+
storage.setItem(key, JSON.stringify(record));
|
|
1498
|
+
} catch {}
|
|
1499
|
+
}
|
|
1500
|
+
installI18nStringShortcut(i18n);
|
|
1501
|
+
function readMessage(messages, key) {
|
|
1502
|
+
if (!messages || !key) return;
|
|
1503
|
+
if (Object.prototype.hasOwnProperty.call(messages, key)) return messages[key];
|
|
1504
|
+
return String(key).split(".").reduce((value, part) => {
|
|
1505
|
+
if (value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, part)) return value[part];
|
|
1506
|
+
}, messages);
|
|
1507
|
+
}
|
|
1508
|
+
function interpolate(value, params, i18n) {
|
|
1509
|
+
const str = String(value);
|
|
1510
|
+
const parts = [];
|
|
1511
|
+
let index = 0;
|
|
1512
|
+
while (index < str.length) {
|
|
1513
|
+
const ch = str.charAt(index);
|
|
1514
|
+
if (ch === "{") {
|
|
1515
|
+
const end = findBalancedEnd(str, index);
|
|
1516
|
+
const expr = str.slice(index + 1, end).trim();
|
|
1517
|
+
parts.push(resolveInterpolation(expr, params, i18n, str.slice(index, end + 1)));
|
|
1518
|
+
index = end + 1;
|
|
1519
|
+
} else {
|
|
1520
|
+
parts.push(ch);
|
|
1521
|
+
index += 1;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
return parts.join("");
|
|
1525
|
+
}
|
|
1526
|
+
function findBalancedEnd(str, start) {
|
|
1527
|
+
let depth = 0;
|
|
1528
|
+
for (let index = start; index < str.length; index += 1) {
|
|
1529
|
+
const ch = str.charAt(index);
|
|
1530
|
+
if (ch === "{") depth += 1;
|
|
1531
|
+
else if (ch === "}") {
|
|
1532
|
+
depth -= 1;
|
|
1533
|
+
if (depth === 0) return index;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
return str.length - 1;
|
|
1537
|
+
}
|
|
1538
|
+
function resolveInterpolation(expr, params, i18n, fallback) {
|
|
1539
|
+
const parts = splitTopLevel(expr);
|
|
1540
|
+
if (parts.length === 1) return readParam(params, parts[0], fallback);
|
|
1541
|
+
const [key, type, ...rest] = parts;
|
|
1542
|
+
const value = readParam(params, key, void 0);
|
|
1543
|
+
if (value === void 0 || value === null) return fallback;
|
|
1544
|
+
const language = i18n?.getLanguage?.() || "zh-CN";
|
|
1545
|
+
if (/^number$/i.test(type || "")) return formatNumber(value, language);
|
|
1546
|
+
if (/^date$/i.test(type || "")) return formatDate(value, language, rest[0]);
|
|
1547
|
+
if (/^plural$/i.test(type || "")) {
|
|
1548
|
+
const category = pluralCategory(value, language);
|
|
1549
|
+
const map = parsePluralMap(parts.slice(2).join(","));
|
|
1550
|
+
return interpolate(map[`=${value}`] ?? map[category] ?? map.other ?? fallback, params, i18n);
|
|
1551
|
+
}
|
|
1552
|
+
return fallback;
|
|
1553
|
+
}
|
|
1554
|
+
function splitTopLevel(expr) {
|
|
1555
|
+
const parts = [];
|
|
1556
|
+
let depth = 0;
|
|
1557
|
+
let current = "";
|
|
1558
|
+
for (const ch of String(expr)) {
|
|
1559
|
+
if (ch === "{") depth += 1;
|
|
1560
|
+
else if (ch === "}") depth -= 1;
|
|
1561
|
+
if (ch === "," && depth === 0) {
|
|
1562
|
+
parts.push(current.trim());
|
|
1563
|
+
current = "";
|
|
1564
|
+
} else current += ch;
|
|
1565
|
+
}
|
|
1566
|
+
if (current) parts.push(current.trim());
|
|
1567
|
+
return parts;
|
|
1568
|
+
}
|
|
1569
|
+
function readParam(params, key, fallback) {
|
|
1570
|
+
const value = params?.[key];
|
|
1571
|
+
return value === void 0 ? fallback : value;
|
|
1572
|
+
}
|
|
1573
|
+
function parsePluralMap(text) {
|
|
1574
|
+
const map = {};
|
|
1575
|
+
const re = /([^\s{}]+)\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g;
|
|
1576
|
+
let match;
|
|
1577
|
+
while (match = re.exec(text)) map[match[1].trim()] = match[2].trim();
|
|
1578
|
+
return map;
|
|
1579
|
+
}
|
|
1580
|
+
function pluralCategory(count, language) {
|
|
1581
|
+
const numeric = Number(count);
|
|
1582
|
+
if (typeof Intl === "undefined" || typeof Intl.PluralRules !== "function") return numeric === 1 ? "one" : "other";
|
|
1583
|
+
try {
|
|
1584
|
+
return new Intl.PluralRules(language).select(numeric);
|
|
1585
|
+
} catch {
|
|
1586
|
+
return numeric === 1 ? "one" : "other";
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
function formatNumber(value, language) {
|
|
1590
|
+
if (typeof Intl === "undefined" || typeof Intl.NumberFormat !== "function") return String(value);
|
|
1591
|
+
try {
|
|
1592
|
+
return new Intl.NumberFormat(language).format(value);
|
|
1593
|
+
} catch {
|
|
1594
|
+
return String(value);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
const DATE_STYLES = /* @__PURE__ */ new Set([
|
|
1598
|
+
"full",
|
|
1599
|
+
"long",
|
|
1600
|
+
"medium",
|
|
1601
|
+
"short"
|
|
1602
|
+
]);
|
|
1603
|
+
function formatDate(value, language, style = "medium") {
|
|
1604
|
+
if (typeof Intl === "undefined" || typeof Intl.DateTimeFormat !== "function") return String(value);
|
|
1605
|
+
try {
|
|
1606
|
+
return new Intl.DateTimeFormat(language, { dateStyle: DATE_STYLES.has(style) ? style : "medium" }).format(new Date(value));
|
|
1607
|
+
} catch {
|
|
1608
|
+
return String(value);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
function mergeMessages(target, source) {
|
|
1612
|
+
const next = { ...target };
|
|
1613
|
+
Object.entries(source || {}).forEach(([key, value]) => {
|
|
1614
|
+
if (isPlainObject$1(value) && isPlainObject$1(next[key])) next[key] = mergeMessages(next[key], value);
|
|
1615
|
+
else next[key] = value;
|
|
1616
|
+
});
|
|
1617
|
+
return next;
|
|
1618
|
+
}
|
|
1619
|
+
function isPlainObject$1(value) {
|
|
1620
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
1621
|
+
}
|
|
1622
|
+
function normalizeCorpusList(corpus) {
|
|
1623
|
+
return Array.isArray(corpus) ? corpus.flatMap((item) => normalizeCorpusList(item)) : [corpus || {}];
|
|
1624
|
+
}
|
|
1625
|
+
function normalizeMessageList(messages) {
|
|
1626
|
+
return Array.isArray(messages) ? messages.flatMap((messagePart) => normalizeMessageList(messagePart)) : [messages || {}];
|
|
1627
|
+
}
|
|
1628
|
+
function resolveStorage(storage) {
|
|
1629
|
+
if (storage) return storage;
|
|
1630
|
+
try {
|
|
1631
|
+
if (typeof globalThis !== "undefined" && globalThis.localStorage) return globalThis.localStorage;
|
|
1632
|
+
} catch {}
|
|
1633
|
+
return memoryStorage();
|
|
1634
|
+
}
|
|
1635
|
+
const memoryStorageData = /* @__PURE__ */ new Map();
|
|
1636
|
+
function memoryStorage() {
|
|
1637
|
+
return {
|
|
1638
|
+
getItem(key) {
|
|
1639
|
+
return memoryStorageData.has(key) ? memoryStorageData.get(key) : null;
|
|
1640
|
+
},
|
|
1641
|
+
setItem(key, value) {
|
|
1642
|
+
memoryStorageData.set(key, String(value));
|
|
1643
|
+
},
|
|
1644
|
+
removeItem(key) {
|
|
1645
|
+
memoryStorageData.delete(key);
|
|
1646
|
+
}
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region src/core/context.js
|
|
1652
|
+
/**
|
|
1653
|
+
* Generic scoped context: inject per-render providers with withContext(),
|
|
1654
|
+
* read the nearest value with currentContext(). Mirrors the access/i18n
|
|
1655
|
+
* scope-stack pattern so Context also works per-request in SSR.
|
|
1656
|
+
*/
|
|
1657
|
+
const contextStack = [];
|
|
1658
|
+
let installedContext = null;
|
|
1659
|
+
/**
|
|
1660
|
+
* Runs build() with the given providers active, then restores the outer scope.
|
|
1661
|
+
* Providers can be a plain object (key → value) resolved per call site.
|
|
1662
|
+
*/
|
|
1663
|
+
function withContext(providers, build) {
|
|
1664
|
+
if (!providers || typeof providers !== "object") return build();
|
|
1665
|
+
contextStack.push(providers);
|
|
1666
|
+
try {
|
|
1667
|
+
return build();
|
|
1668
|
+
} finally {
|
|
1669
|
+
contextStack.pop();
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Installs a global fallback context (single-user SPA). SSR entries inject a
|
|
1674
|
+
* per-request context through options.context instead, so requests never share.
|
|
1675
|
+
*/
|
|
1676
|
+
function installContext(providers) {
|
|
1677
|
+
installedContext = providers && typeof providers === "object" ? providers : null;
|
|
1678
|
+
return installedContext;
|
|
1679
|
+
}
|
|
1680
|
+
/** Removes the globally installed fallback context. */
|
|
1681
|
+
function clearInstalledContext() {
|
|
1682
|
+
installedContext = null;
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Returns the nearest provided value for key, walking scopes from innermost
|
|
1687
|
+
* to outermost, then the installed fallback; returns defaultValue when absent.
|
|
1688
|
+
*/
|
|
1689
|
+
function currentContext(key, defaultValue = void 0) {
|
|
1690
|
+
for (let index = contextStack.length - 1; index >= 0; index -= 1) {
|
|
1691
|
+
const layer = contextStack[index];
|
|
1692
|
+
if (layer && Object.prototype.hasOwnProperty.call(layer, key)) return layer[key];
|
|
1693
|
+
}
|
|
1694
|
+
if (installedContext && Object.prototype.hasOwnProperty.call(installedContext, key)) return installedContext[key];
|
|
1695
|
+
return defaultValue;
|
|
1696
|
+
}
|
|
1697
|
+
/** Returns a shallow merged snapshot (installed fallback overlaid by active scopes). */
|
|
1698
|
+
function snapshotContext() {
|
|
1699
|
+
const merged = { ...installedContext || {} };
|
|
1700
|
+
contextStack.forEach((layer) => Object.assign(merged, layer));
|
|
1701
|
+
return merged;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
//#endregion
|
|
1705
|
+
//#region src/core/devtools.js
|
|
1706
|
+
/** Dev-facing tree instrumentation. Opt-in, zero-cost when disabled. */
|
|
1707
|
+
let enabled = false;
|
|
1708
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1709
|
+
const nodeIds = /* @__PURE__ */ new WeakMap();
|
|
1710
|
+
const liveNodes = /* @__PURE__ */ new Map();
|
|
1711
|
+
let nextNodeId = 1;
|
|
1712
|
+
let nextEventSeq = 1;
|
|
1713
|
+
const appliedSignatures = /* @__PURE__ */ new WeakMap();
|
|
1714
|
+
function enableDevtools() {
|
|
1715
|
+
enabled = true;
|
|
1716
|
+
return true;
|
|
1717
|
+
}
|
|
1718
|
+
function disableDevtools() {
|
|
1719
|
+
enabled = false;
|
|
1720
|
+
return false;
|
|
1721
|
+
}
|
|
1722
|
+
function isDevtoolsEnabled() {
|
|
1723
|
+
return enabled;
|
|
1724
|
+
}
|
|
1725
|
+
/** Internal: broadcast a lifecycle event; no-op when disabled. */
|
|
1726
|
+
function emitDevtools(event) {
|
|
1727
|
+
if (!enabled) return;
|
|
1728
|
+
const enriched = {
|
|
1729
|
+
seq: nextEventSeq,
|
|
1730
|
+
...event
|
|
1731
|
+
};
|
|
1732
|
+
nextEventSeq += 1;
|
|
1733
|
+
if (enriched.node && typeof enriched.node === "object" && enriched.nodeId === void 0) enriched.nodeId = ensureDevtoolsNode(enriched.node);
|
|
1734
|
+
if (enriched.node && typeof enriched.node === "object" && enriched.nodeLabel === void 0) enriched.nodeLabel = devtoolsNodeLabel(enriched.node);
|
|
1735
|
+
listeners.forEach((listener) => {
|
|
1736
|
+
try {
|
|
1737
|
+
listener(enriched);
|
|
1738
|
+
} catch {}
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
/** Subscribe to lifecycle events emitted while devtools is enabled. */
|
|
1742
|
+
function subscribeDevtools(listener) {
|
|
1743
|
+
listeners.add(listener);
|
|
1744
|
+
return () => listeners.delete(listener);
|
|
1745
|
+
}
|
|
1746
|
+
/** Assigns a stable id and keeps the node resolvable to its live DOM. */
|
|
1747
|
+
function ensureDevtoolsNode(node) {
|
|
1748
|
+
let id = nodeIds.get(node);
|
|
1749
|
+
if (id === void 0) {
|
|
1750
|
+
id = nextNodeId;
|
|
1751
|
+
nextNodeId += 1;
|
|
1752
|
+
nodeIds.set(node, id);
|
|
1753
|
+
}
|
|
1754
|
+
liveNodes.set(id, node);
|
|
1755
|
+
return id;
|
|
1756
|
+
}
|
|
1757
|
+
/** Internal: forget a destroyed node so its id no longer resolves. */
|
|
1758
|
+
function unregisterDevtoolsNode(node) {
|
|
1759
|
+
const id = nodeIds.get(node);
|
|
1760
|
+
if (id !== void 0) liveNodes.delete(id);
|
|
1761
|
+
}
|
|
1762
|
+
/** Internal: capture the context scope visible while a node is built. */
|
|
1763
|
+
function captureDevtoolsNodeScope(node) {
|
|
1764
|
+
const context = snapshotContext();
|
|
1765
|
+
node._devtoolsContext = Object.keys(context).length > 0 ? context : null;
|
|
1766
|
+
}
|
|
1767
|
+
/** Returns scope details (access/context/i18n) for a snapshot id. */
|
|
1768
|
+
function getDevtoolsScope(id) {
|
|
1769
|
+
const node = liveNodes.get(id);
|
|
1770
|
+
if (!node || node._deleted) return null;
|
|
1771
|
+
const declared = node._access;
|
|
1772
|
+
const scope = {
|
|
1773
|
+
id,
|
|
1774
|
+
access: declared ? {
|
|
1775
|
+
code: declared.code,
|
|
1776
|
+
level: declared.level
|
|
1777
|
+
} : null,
|
|
1778
|
+
context: node._devtoolsContext || null,
|
|
1779
|
+
i18n: null
|
|
1780
|
+
};
|
|
1781
|
+
if (typeof node._permissionState === "function") scope.permissionState = node._permissionState();
|
|
1782
|
+
if (node._i18n && typeof node._i18n.getLanguage === "function") scope.i18n = {
|
|
1783
|
+
key: node._key ?? void 0,
|
|
1784
|
+
language: node._i18n.getLanguage()
|
|
1785
|
+
};
|
|
1786
|
+
return scope;
|
|
1787
|
+
}
|
|
1788
|
+
/** Returns the rendered DOM node (element or text) for a snapshot id. */
|
|
1789
|
+
function getDevtoolsDom(id) {
|
|
1790
|
+
const node = liveNodes.get(id);
|
|
1791
|
+
if (!node || node._deleted || !("_el" in node) || !node._el) return null;
|
|
1792
|
+
return node._el;
|
|
1793
|
+
}
|
|
1794
|
+
/** Internal: register/return the stable id for a live node. */
|
|
1795
|
+
function ensureDevtoolsNodeId(node) {
|
|
1796
|
+
return ensureDevtoolsNode(node);
|
|
1797
|
+
}
|
|
1798
|
+
function devtoolsNodeKind(node) {
|
|
1799
|
+
if (typeof node._tagName === "string" && node._attrs) return "element";
|
|
1800
|
+
if (typeof node._content === "string" && "_textNode" in node) return "text";
|
|
1801
|
+
if ("_component" in node && "_resolvedList" in node) return "component";
|
|
1802
|
+
return "view";
|
|
1803
|
+
}
|
|
1804
|
+
function devtoolsNodeChildren(node, kind) {
|
|
1805
|
+
if (kind === "component") return Array.isArray(node._resolvedList) ? node._resolvedList : [];
|
|
1806
|
+
return Array.isArray(node._children) ? node._children : [];
|
|
1807
|
+
}
|
|
1808
|
+
function devtoolsNodeLabel(node) {
|
|
1809
|
+
const kind = devtoolsNodeKind(node);
|
|
1810
|
+
if (kind === "element") {
|
|
1811
|
+
const classText = (node._classes ? [...node._classes].slice(0, 2) : []).map((name) => String(name).replace(/\s+/g, "")).filter(Boolean).map((name) => `.${name}`).join("");
|
|
1812
|
+
return `${node._tagName || "element"}${classText}`;
|
|
1813
|
+
}
|
|
1814
|
+
if (kind === "text") {
|
|
1815
|
+
const content = typeof node._content === "string" ? node._content : "";
|
|
1816
|
+
return `文本 "${content.length > 24 ? `${content.slice(0, 24)}…` : content}"`;
|
|
1817
|
+
}
|
|
1818
|
+
if (kind === "component") return "组件";
|
|
1819
|
+
return "节点";
|
|
1820
|
+
}
|
|
1821
|
+
function serializeDevtoolsNode(node) {
|
|
1822
|
+
const kind = devtoolsNodeKind(node);
|
|
1823
|
+
const base = {
|
|
1824
|
+
kind,
|
|
1825
|
+
id: ensureDevtoolsNode(node)
|
|
1826
|
+
};
|
|
1827
|
+
if (kind === "element") return {
|
|
1828
|
+
...base,
|
|
1829
|
+
tagName: node._tagName,
|
|
1830
|
+
attrs: { ...node._attrs },
|
|
1831
|
+
children: devtoolsNodeChildren(node, kind).map((child) => serializeDevtoolsNode(child))
|
|
1832
|
+
};
|
|
1833
|
+
if (kind === "text") return {
|
|
1834
|
+
...base,
|
|
1835
|
+
text: node._content,
|
|
1836
|
+
children: []
|
|
1837
|
+
};
|
|
1838
|
+
return {
|
|
1839
|
+
...base,
|
|
1840
|
+
children: devtoolsNodeChildren(node, kind).map((child) => serializeDevtoolsNode(child))
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
function devtoolsSignature(node) {
|
|
1844
|
+
const kind = devtoolsNodeKind(node);
|
|
1845
|
+
const baseId = ensureDevtoolsNode(node);
|
|
1846
|
+
if (kind === "element") return {
|
|
1847
|
+
kind,
|
|
1848
|
+
id: baseId,
|
|
1849
|
+
attrs: { ...node._attrs },
|
|
1850
|
+
styles: { ...node._styles },
|
|
1851
|
+
childIds: (node._children || []).map((child) => ensureDevtoolsNode(child))
|
|
1852
|
+
};
|
|
1853
|
+
if (kind === "text") return {
|
|
1854
|
+
kind,
|
|
1855
|
+
id: baseId,
|
|
1856
|
+
text: node._content
|
|
1857
|
+
};
|
|
1858
|
+
if (kind === "component") return {
|
|
1859
|
+
kind,
|
|
1860
|
+
id: baseId,
|
|
1861
|
+
childIds: (node._resolvedList || []).map((child) => ensureDevtoolsNode(child))
|
|
1862
|
+
};
|
|
1863
|
+
return {
|
|
1864
|
+
kind,
|
|
1865
|
+
id: baseId,
|
|
1866
|
+
childIds: (node._children || []).map((child) => ensureDevtoolsNode(child))
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
function diffObjectValues(previous, next) {
|
|
1870
|
+
const changes = {};
|
|
1871
|
+
(/* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])).forEach((key) => {
|
|
1872
|
+
if (!Object.is(previous[key], next[key])) changes[key] = {
|
|
1873
|
+
previous: previous[key],
|
|
1874
|
+
next: next[key]
|
|
1875
|
+
};
|
|
1876
|
+
});
|
|
1877
|
+
return Object.keys(changes).length > 0 ? changes : null;
|
|
1878
|
+
}
|
|
1879
|
+
function diffSignatures(previous, next) {
|
|
1880
|
+
const changes = {};
|
|
1881
|
+
if (previous.kind === "element" && next.kind === "element") {
|
|
1882
|
+
const attrs = diffObjectValues(previous.attrs, next.attrs);
|
|
1883
|
+
if (attrs) changes.attrs = attrs;
|
|
1884
|
+
const styles = diffObjectValues(previous.styles, next.styles);
|
|
1885
|
+
if (styles) changes.styles = styles;
|
|
1886
|
+
}
|
|
1887
|
+
if (previous.kind === "text" && next.kind === "text" && !Object.is(previous.text, next.text)) changes.text = {
|
|
1888
|
+
from: previous.text,
|
|
1889
|
+
to: next.text
|
|
1890
|
+
};
|
|
1891
|
+
const previousChildIds = previous.childIds || [];
|
|
1892
|
+
const nextChildIds = next.childIds || [];
|
|
1893
|
+
const previousSet = new Set(previousChildIds);
|
|
1894
|
+
const nextSet = new Set(nextChildIds);
|
|
1895
|
+
const added = nextChildIds.filter((id) => !previousSet.has(id));
|
|
1896
|
+
const removed = previousChildIds.filter((id) => !nextSet.has(id));
|
|
1897
|
+
const reordered = removed.length === 0 && added.length === 0 && nextChildIds.length > 1 && nextChildIds.some((id, index) => previousChildIds[index] !== id);
|
|
1898
|
+
if (added.length > 0 || removed.length > 0 || reordered) changes.children = {
|
|
1899
|
+
added,
|
|
1900
|
+
removed,
|
|
1901
|
+
reordered
|
|
1902
|
+
};
|
|
1903
|
+
return changes;
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Internal: sync the applied-signature baseline after a live mutation so a
|
|
1907
|
+
* later unchanged renderDom does not replay the same change.
|
|
1908
|
+
*/
|
|
1909
|
+
function refreshDevtoolsSignature(node) {
|
|
1910
|
+
if (node) appliedSignatures.set(node, devtoolsSignature(node));
|
|
1911
|
+
}
|
|
1912
|
+
/** Internal: notify a live mutation that already changed the DOM/view tree. */
|
|
1913
|
+
function notifyDevtoolsMutation(node, type, details) {
|
|
1914
|
+
if (!enabled || !node) return;
|
|
1915
|
+
emitDevtools({
|
|
1916
|
+
type,
|
|
1917
|
+
node,
|
|
1918
|
+
...details
|
|
1919
|
+
});
|
|
1920
|
+
refreshDevtoolsSignature(node);
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Internal: called at the end of renderDom. First render emits a mount
|
|
1924
|
+
* commit; later renders emit granular events only when the signature moved.
|
|
1925
|
+
*/
|
|
1926
|
+
function commitDevtoolsNode(node) {
|
|
1927
|
+
if (!enabled || !node || node._deleted) return;
|
|
1928
|
+
const next = devtoolsSignature(node);
|
|
1929
|
+
const previous = appliedSignatures.get(node);
|
|
1930
|
+
if (!previous) {
|
|
1931
|
+
emitDevtools({
|
|
1932
|
+
type: "commit",
|
|
1933
|
+
kind: "mount",
|
|
1934
|
+
node
|
|
1935
|
+
});
|
|
1936
|
+
appliedSignatures.set(node, next);
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
const changes = diffSignatures(previous, next);
|
|
1940
|
+
Object.entries(changes.attrs || {}).forEach(([name, change]) => {
|
|
1941
|
+
emitDevtools({
|
|
1942
|
+
type: "attr",
|
|
1943
|
+
node,
|
|
1944
|
+
name,
|
|
1945
|
+
...change
|
|
1946
|
+
});
|
|
1947
|
+
});
|
|
1948
|
+
Object.entries(changes.styles || {}).forEach(([name, change]) => {
|
|
1949
|
+
emitDevtools({
|
|
1950
|
+
type: "style",
|
|
1951
|
+
node,
|
|
1952
|
+
name,
|
|
1953
|
+
...change
|
|
1954
|
+
});
|
|
1955
|
+
});
|
|
1956
|
+
if (changes.children) emitDevtools({
|
|
1957
|
+
type: "child",
|
|
1958
|
+
node,
|
|
1959
|
+
...changes.children
|
|
1960
|
+
});
|
|
1961
|
+
if (changes.text) emitDevtools({
|
|
1962
|
+
type: "text",
|
|
1963
|
+
node,
|
|
1964
|
+
...changes.text
|
|
1965
|
+
});
|
|
1966
|
+
appliedSignatures.set(node, next);
|
|
1967
|
+
}
|
|
1968
|
+
/** Returns a plain-shape snapshot of a node subtree for inspection. */
|
|
1969
|
+
function getDevtoolsSnapshot(root) {
|
|
1970
|
+
if (!root) return {
|
|
1971
|
+
kind: "root",
|
|
1972
|
+
children: []
|
|
1973
|
+
};
|
|
1974
|
+
return serializeDevtoolsNode(root);
|
|
1975
|
+
}
|
|
1976
|
+
const devtoolsBridgeKey = Symbol.for("yoya.devtools.bridge");
|
|
1977
|
+
function installDevtoolsBridge() {
|
|
1978
|
+
if (typeof globalThis === "undefined") return;
|
|
1979
|
+
globalThis[devtoolsBridgeKey] = {
|
|
1980
|
+
captureScope: captureDevtoolsNodeScope,
|
|
1981
|
+
commit: commitDevtoolsNode,
|
|
1982
|
+
emit: emitDevtools,
|
|
1983
|
+
enabled: () => enabled,
|
|
1984
|
+
ensureId: ensureDevtoolsNodeId,
|
|
1985
|
+
notify: notifyDevtoolsMutation,
|
|
1986
|
+
unregister: unregisterDevtoolsNode
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
installDevtoolsBridge();
|
|
1990
|
+
|
|
1991
|
+
//#endregion
|
|
1992
|
+
//#region src/core/state-node.js
|
|
1993
|
+
const lifecycleKeys = /* @__PURE__ */ new Set([
|
|
1994
|
+
"state",
|
|
1995
|
+
"render",
|
|
1996
|
+
"update"
|
|
1997
|
+
]);
|
|
1998
|
+
const builtinKeys = /* @__PURE__ */ new Set([
|
|
1999
|
+
"_attachHost",
|
|
2000
|
+
"destroy",
|
|
2001
|
+
"getState",
|
|
2002
|
+
"setState",
|
|
2003
|
+
"subscribe"
|
|
2004
|
+
]);
|
|
2005
|
+
/**
|
|
2006
|
+
* vStateNode 返回带状态的对象组件:不产生自己的 DOM 元素,
|
|
2007
|
+
* render 返回的视图根直接成为父节点落实的子节点;state 保存状态,
|
|
2008
|
+
* update 在状态变化后做局部更新;未提供 update 时,render 中登记的函数值
|
|
2009
|
+
* 绑定(vText/attr/style)会在 setState 后统一求值写回,没有绑定时回退为全量重建。
|
|
2010
|
+
* config 上除 state/render/update 外的自定义函数会挂到返回对象上,
|
|
2011
|
+
* 外部可以直接调用;render 与 update 是内部生命周期函数,不对外暴露。
|
|
2012
|
+
*/
|
|
2013
|
+
function vStateNode(config = {}) {
|
|
2014
|
+
if (typeof config.render !== "function") throw new TypeError("vStateNode requires a render function");
|
|
2015
|
+
const state = typeof config.state === "function" ? config.state() : { ...config.state || {} };
|
|
2016
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2017
|
+
let destroyed = false;
|
|
2018
|
+
let roots = null;
|
|
2019
|
+
let host = null;
|
|
2020
|
+
let bindings = [];
|
|
2021
|
+
const component = {
|
|
2022
|
+
_attachHost(hostNode) {
|
|
2023
|
+
host = hostNode;
|
|
2024
|
+
return component;
|
|
2025
|
+
},
|
|
2026
|
+
destroy() {
|
|
2027
|
+
if (destroyed) return component;
|
|
2028
|
+
destroyed = true;
|
|
2029
|
+
bindings = [];
|
|
2030
|
+
listeners.clear();
|
|
2031
|
+
host = null;
|
|
2032
|
+
if (roots) {
|
|
2033
|
+
const currentRoots = roots;
|
|
2034
|
+
roots = null;
|
|
2035
|
+
currentRoots.forEach((root) => root.destroy());
|
|
2036
|
+
}
|
|
2037
|
+
return component;
|
|
2038
|
+
},
|
|
2039
|
+
getState() {
|
|
2040
|
+
return component.state();
|
|
2041
|
+
},
|
|
2042
|
+
render() {
|
|
2043
|
+
if (destroyed) return renderOutput();
|
|
2044
|
+
if (!roots) rebuild();
|
|
2045
|
+
return renderOutput();
|
|
2046
|
+
},
|
|
2047
|
+
setState(patch) {
|
|
2048
|
+
if (destroyed || patch === null || patch === void 0) return component;
|
|
2049
|
+
const nextPatch = typeof patch === "function" ? patch({ ...state }) : patch;
|
|
2050
|
+
if (!nextPatch || typeof nextPatch !== "object") return component;
|
|
2051
|
+
const changed = /* @__PURE__ */ new Set();
|
|
2052
|
+
const changedDetails = isDevtoolsEnabled() ? {} : null;
|
|
2053
|
+
Object.entries(nextPatch).forEach(([key, value]) => {
|
|
2054
|
+
if (state[key] !== value) {
|
|
2055
|
+
if (changedDetails) changedDetails[key] = {
|
|
2056
|
+
from: state[key],
|
|
2057
|
+
to: value
|
|
2058
|
+
};
|
|
2059
|
+
state[key] = value;
|
|
2060
|
+
changed.add(key);
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
let handling = "none";
|
|
2064
|
+
if (changed.size > 0) {
|
|
2065
|
+
if (roots) handling = applyStateChange(changed);
|
|
2066
|
+
listeners.forEach((listener) => listener(state, component));
|
|
2067
|
+
if (isDevtoolsEnabled() && changedDetails) emitDevtools({
|
|
2068
|
+
type: "state",
|
|
2069
|
+
node: host || void 0,
|
|
2070
|
+
changed: changedDetails,
|
|
2071
|
+
state: { ...state },
|
|
2072
|
+
handling
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
return component;
|
|
2076
|
+
},
|
|
2077
|
+
state() {
|
|
2078
|
+
return { ...state };
|
|
2079
|
+
},
|
|
2080
|
+
subscribe(listener) {
|
|
2081
|
+
if (typeof listener !== "function") throw new TypeError("vStateNode subscriber must be a function");
|
|
2082
|
+
listeners.add(listener);
|
|
2083
|
+
return () => listeners.delete(listener);
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
for (const key of Object.keys(config)) {
|
|
2087
|
+
if (lifecycleKeys.has(key) || typeof config[key] !== "function") continue;
|
|
2088
|
+
if (builtinKeys.has(key)) throw new TypeError(`vStateNode config key "${key}" conflicts with the built-in API`);
|
|
2089
|
+
component[key] = config[key];
|
|
2090
|
+
}
|
|
2091
|
+
return component;
|
|
2092
|
+
function rebuild() {
|
|
2093
|
+
const previousRoots = roots || [];
|
|
2094
|
+
bindings = [];
|
|
2095
|
+
const nextResult = withBindingScope({
|
|
2096
|
+
bindings,
|
|
2097
|
+
getState: () => state
|
|
2098
|
+
}, () => config.render.call(component, state, component));
|
|
2099
|
+
const nextRoots = Array.isArray(nextResult) ? nextResult.slice() : [nextResult];
|
|
2100
|
+
nextRoots.forEach((root) => {
|
|
2101
|
+
if (!(root instanceof ViewNode)) throw new TypeError("vStateNode render must return a ViewNode or an array of ViewNodes");
|
|
2102
|
+
});
|
|
2103
|
+
roots = nextRoots;
|
|
2104
|
+
flushBindings();
|
|
2105
|
+
if (host) {
|
|
2106
|
+
host._replaceResolved(renderOutput());
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
if (previousRoots.length > 0) {
|
|
2110
|
+
const previousSingle = previousRoots.length === 1 ? previousRoots[0] : null;
|
|
2111
|
+
if (previousSingle && previousSingle._el && previousSingle._el.parentNode && nextRoots.length === 1) {
|
|
2112
|
+
const element = nextRoots[0].renderDom();
|
|
2113
|
+
if (element) previousSingle._el.parentNode.replaceChild(element, previousSingle._el);
|
|
2114
|
+
}
|
|
2115
|
+
previousRoots.forEach((root) => root.destroy());
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function renderOutput() {
|
|
2119
|
+
if (!roots) return null;
|
|
2120
|
+
return roots.length === 1 ? roots[0] : roots;
|
|
2121
|
+
}
|
|
2122
|
+
function flushBindings() {
|
|
2123
|
+
bindings.forEach((binding) => {
|
|
2124
|
+
const next = binding.evaluate();
|
|
2125
|
+
if (!binding.committed || !Object.is(next, binding.last)) {
|
|
2126
|
+
binding.committed = true;
|
|
2127
|
+
binding.last = next;
|
|
2128
|
+
binding.commit(next);
|
|
2129
|
+
}
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
function applyStateChange(changed) {
|
|
2133
|
+
if (typeof config.update === "function") {
|
|
2134
|
+
if (config.update.call(component, state, component, changed) === true) {
|
|
2135
|
+
rebuild();
|
|
2136
|
+
return "rebuild";
|
|
2137
|
+
} else if (bindings.length > 0) {
|
|
2138
|
+
flushBindings();
|
|
2139
|
+
return "bindings";
|
|
2140
|
+
}
|
|
2141
|
+
return "update";
|
|
2142
|
+
}
|
|
2143
|
+
if (bindings.length > 0) {
|
|
2144
|
+
flushBindings();
|
|
2145
|
+
return "bindings";
|
|
2146
|
+
}
|
|
2147
|
+
rebuild();
|
|
2148
|
+
return "rebuild";
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
registerChildFactories(ElementNode, { vStateNode });
|
|
2152
|
+
|
|
2153
|
+
//#endregion
|
|
2154
|
+
//#region src/core/a11y.js
|
|
2155
|
+
const FOCUSABLE_SELECTOR = [
|
|
2156
|
+
"a[href]",
|
|
2157
|
+
"button:not([disabled])",
|
|
2158
|
+
"input:not([disabled])",
|
|
2159
|
+
"select:not([disabled])",
|
|
2160
|
+
"textarea:not([disabled])",
|
|
2161
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
2162
|
+
].join(", ");
|
|
2163
|
+
const LIVE_REGION_MARKER = "data-yoya-live";
|
|
2164
|
+
/** Returns focusable descendants in DOM (tab) order, excluding disabled ones. */
|
|
2165
|
+
function getFocusableElements(root) {
|
|
2166
|
+
if (!root || typeof root.querySelectorAll !== "function") return [];
|
|
2167
|
+
return Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR)).filter((el) => el.getAttribute && el.getAttribute("tabindex") !== "-1");
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Generic focus trap: keeps Tab/Shift+Tab cycling inside `root`, fires
|
|
2171
|
+
* onEscape on Escape, and restores the previously focused element on destroy().
|
|
2172
|
+
* SSR-safe (no-ops when document is unavailable).
|
|
2173
|
+
*/
|
|
2174
|
+
function createFocusTrap(root, options = {}) {
|
|
2175
|
+
const { onEscape = null, restoreFocus = true } = options;
|
|
2176
|
+
if (typeof document === "undefined" || !root) return {
|
|
2177
|
+
activate() {},
|
|
2178
|
+
destroy() {}
|
|
2179
|
+
};
|
|
2180
|
+
let active = false;
|
|
2181
|
+
let previous = null;
|
|
2182
|
+
let removeKeydown = null;
|
|
2183
|
+
const handleKeydown = (event) => {
|
|
2184
|
+
if (!active) return;
|
|
2185
|
+
if (event.key === "Escape") {
|
|
2186
|
+
if (onEscape) onEscape(event);
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2189
|
+
if (event.key !== "Tab") return;
|
|
2190
|
+
const focusables = getFocusableElements(root);
|
|
2191
|
+
if (focusables.length === 0) {
|
|
2192
|
+
event.preventDefault();
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const current = document.activeElement;
|
|
2196
|
+
const index = focusables.indexOf(current);
|
|
2197
|
+
let nextIndex;
|
|
2198
|
+
if (event.shiftKey) nextIndex = index <= 0 ? focusables.length - 1 : index - 1;
|
|
2199
|
+
else if (index === -1 || index === focusables.length - 1) nextIndex = 0;
|
|
2200
|
+
else nextIndex = index + 1;
|
|
2201
|
+
event.preventDefault();
|
|
2202
|
+
focusables[nextIndex].focus();
|
|
2203
|
+
};
|
|
2204
|
+
const activate = () => {
|
|
2205
|
+
if (active) return;
|
|
2206
|
+
active = true;
|
|
2207
|
+
previous = document.activeElement;
|
|
2208
|
+
removeKeydown = bindDocumentEvent("keydown", handleKeydown);
|
|
2209
|
+
const first = getFocusableElements(root)[0];
|
|
2210
|
+
if (first) first.focus();
|
|
2211
|
+
};
|
|
2212
|
+
const destroy = () => {
|
|
2213
|
+
if (!active) return;
|
|
2214
|
+
active = false;
|
|
2215
|
+
if (removeKeydown) {
|
|
2216
|
+
removeKeydown();
|
|
2217
|
+
removeKeydown = null;
|
|
2218
|
+
}
|
|
2219
|
+
if (restoreFocus && previous && typeof previous.isConnected === "boolean" && previous.isConnected && typeof previous.focus === "function") previous.focus();
|
|
2220
|
+
};
|
|
2221
|
+
return {
|
|
2222
|
+
activate,
|
|
2223
|
+
destroy
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Announce a message to screen readers through a dedicated aria-live region.
|
|
2228
|
+
* Reuses one region per dedupe key; SSR-safe.
|
|
2229
|
+
*/
|
|
2230
|
+
function announce(message, options = {}) {
|
|
2231
|
+
if (typeof document === "undefined") return null;
|
|
2232
|
+
const { politeness = "polite", dedupeKey = "yoya-announce" } = options;
|
|
2233
|
+
let region = document.querySelector(`[${LIVE_REGION_MARKER}="${dedupeKey}"]`);
|
|
2234
|
+
if (!region) {
|
|
2235
|
+
region = document.createElement("div");
|
|
2236
|
+
region.setAttribute(LIVE_REGION_MARKER, dedupeKey);
|
|
2237
|
+
region.setAttribute("aria-live", politeness);
|
|
2238
|
+
region.setAttribute("aria-atomic", "true");
|
|
2239
|
+
region.className = "yoya-a11y-live";
|
|
2240
|
+
region.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;padding:0;margin:-1px;";
|
|
2241
|
+
document.body.appendChild(region);
|
|
2242
|
+
}
|
|
2243
|
+
region.textContent = "";
|
|
2244
|
+
queueMicrotask(() => {
|
|
2245
|
+
region.textContent = String(message);
|
|
2246
|
+
});
|
|
2247
|
+
return region;
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Generic index-based keyboard movement (arrows/Home/End) with wrapping, so
|
|
2251
|
+
* menus/tables/trees/tabs can share one navigation rule.
|
|
2252
|
+
*/
|
|
2253
|
+
function moveByKey({ key, items, currentIndex = -1, shiftKey = false }) {
|
|
2254
|
+
const count = items?.length || 0;
|
|
2255
|
+
if (count === 0) return -1;
|
|
2256
|
+
const last = count - 1;
|
|
2257
|
+
const at = currentIndex >= 0 && currentIndex < count ? currentIndex : -1;
|
|
2258
|
+
if (key === "Home") return 0;
|
|
2259
|
+
if (key === "End") return last;
|
|
2260
|
+
if (key === "ArrowDown") return shiftKey ? -1 : at === last ? 0 : at + 1;
|
|
2261
|
+
if (key === "ArrowUp") return shiftKey ? last : at <= 0 ? last : at - 1;
|
|
2262
|
+
return at;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
//#endregion
|
|
2266
|
+
//#region src/core/theme.js
|
|
2267
|
+
const MODES = [
|
|
2268
|
+
"light",
|
|
2269
|
+
"dark",
|
|
2270
|
+
"system"
|
|
2271
|
+
];
|
|
2272
|
+
const STORAGE_MODE_KEY = "yoya-theme-mode";
|
|
2273
|
+
const STORAGE_THEME_KEY = "yoya-theme-name";
|
|
2274
|
+
function documentRoot() {
|
|
2275
|
+
return typeof document === "undefined" ? null : document.documentElement;
|
|
2276
|
+
}
|
|
2277
|
+
function hasStorage() {
|
|
2278
|
+
try {
|
|
2279
|
+
return typeof localStorage !== "undefined" && localStorage !== null;
|
|
2280
|
+
} catch {
|
|
2281
|
+
return false;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* 设置明暗模式(light / dark / system),作用于 documentElement 的 data-yoya-mode。
|
|
2286
|
+
* persist 为 true 时同时写入 localStorage,供下次会话恢复。
|
|
2287
|
+
*/
|
|
2288
|
+
function setYoyaMode(mode = "light", options = {}) {
|
|
2289
|
+
const next = MODES.includes(mode) ? mode : "light";
|
|
2290
|
+
const root = documentRoot();
|
|
2291
|
+
if (root) root.dataset.yoyaMode = next;
|
|
2292
|
+
if (options.persist && hasStorage()) try {
|
|
2293
|
+
localStorage.setItem(STORAGE_MODE_KEY, next);
|
|
2294
|
+
} catch {}
|
|
2295
|
+
return next;
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* 读取当前声明的模式;未声明时按主题默认值返回 light。
|
|
2299
|
+
*/
|
|
2300
|
+
function getYoyaMode() {
|
|
2301
|
+
const root = documentRoot();
|
|
2302
|
+
const mode = root && root.dataset.yoyaMode;
|
|
2303
|
+
return MODES.includes(mode) ? mode : "light";
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* 解析实际生效的浅/深色:system 模式下跟随系统偏好(matchMedia)。
|
|
2307
|
+
*/
|
|
2308
|
+
function resolveYoyaMode() {
|
|
2309
|
+
const mode = getYoyaMode();
|
|
2310
|
+
if (mode !== "system") return mode;
|
|
2311
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return "light";
|
|
2312
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
2313
|
+
}
|
|
2314
|
+
/**
|
|
2315
|
+
* 设置命名品牌主题,作用于 documentElement 的 data-yoya-theme;传空值清除。
|
|
2316
|
+
*/
|
|
2317
|
+
function setYoyaTheme(name = "", options = {}) {
|
|
2318
|
+
const root = documentRoot();
|
|
2319
|
+
if (!name) {
|
|
2320
|
+
if (root) delete root.dataset.yoyaTheme;
|
|
2321
|
+
if (options.persist && hasStorage()) try {
|
|
2322
|
+
localStorage.removeItem(STORAGE_THEME_KEY);
|
|
2323
|
+
} catch {}
|
|
2324
|
+
return "";
|
|
2325
|
+
}
|
|
2326
|
+
if (root) root.dataset.yoyaTheme = name;
|
|
2327
|
+
if (options.persist && hasStorage()) try {
|
|
2328
|
+
localStorage.setItem(STORAGE_THEME_KEY, name);
|
|
2329
|
+
} catch {}
|
|
2330
|
+
return name;
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* 读取当前品牌主题;未设置时返回空字符串。
|
|
2334
|
+
*/
|
|
2335
|
+
function getYoyaTheme() {
|
|
2336
|
+
const root = documentRoot();
|
|
2337
|
+
return root && root.dataset.yoyaTheme || "";
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* 初始化主题:显式传入的 mode/theme 优先;未传入且允许持久化时从
|
|
2341
|
+
* localStorage 恢复上次选择,再应用到 documentElement。返回 { mode, theme }。
|
|
2342
|
+
*/
|
|
2343
|
+
function initYoyaTheme(options = {}) {
|
|
2344
|
+
let mode = options.mode;
|
|
2345
|
+
let theme = options.theme;
|
|
2346
|
+
if (mode === void 0 && hasStorage()) {
|
|
2347
|
+
const storedMode = localStorage.getItem(STORAGE_MODE_KEY);
|
|
2348
|
+
if (MODES.includes(storedMode)) mode = storedMode;
|
|
2349
|
+
}
|
|
2350
|
+
if (theme === void 0 && hasStorage()) theme = localStorage.getItem(STORAGE_THEME_KEY) || "";
|
|
2351
|
+
if (mode !== void 0) setYoyaMode(mode, { persist: options.persist });
|
|
2352
|
+
if (theme !== void 0) setYoyaTheme(theme, { persist: options.persist });
|
|
2353
|
+
return {
|
|
2354
|
+
mode: getYoyaMode(),
|
|
2355
|
+
theme: getYoyaTheme()
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
//#endregion
|
|
2360
|
+
//#region src/core/request.js
|
|
2361
|
+
let transport = null;
|
|
2362
|
+
/**
|
|
2363
|
+
* 注册请求传输层:RequestBase.submit() 会调用这里提供的 submit(request)。
|
|
2364
|
+
* 传输层返回统一包装结构,由调用方决定如何解析(如 Result.from(raw, request))。
|
|
2365
|
+
*/
|
|
2366
|
+
function configureRequest({ submit } = {}) {
|
|
2367
|
+
transport = typeof submit === "function" ? submit : null;
|
|
2368
|
+
return transport;
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* 请求基类:定义请求描述与提交的默认逻辑,子类覆写或扩展特殊方法。
|
|
2372
|
+
* 子类像 views 一样在构造器里声明字段,覆写 address/method/params/body 等方法。
|
|
2373
|
+
*/
|
|
2374
|
+
var RequestBase = class {
|
|
2375
|
+
method() {
|
|
2376
|
+
return "GET";
|
|
2377
|
+
}
|
|
2378
|
+
headers() {
|
|
2379
|
+
return {};
|
|
2380
|
+
}
|
|
2381
|
+
cookies() {
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
body() {
|
|
2385
|
+
return null;
|
|
2386
|
+
}
|
|
2387
|
+
params() {
|
|
2388
|
+
return {};
|
|
2389
|
+
}
|
|
2390
|
+
address() {
|
|
2391
|
+
return "";
|
|
2392
|
+
}
|
|
2393
|
+
submit() {
|
|
2394
|
+
if (typeof transport !== "function") throw new Error("RequestBase: 未注册请求传输,请先 configureRequest({ submit })");
|
|
2395
|
+
return transport(this);
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2399
|
+
//#endregion
|
|
2400
|
+
//#region src/core/result.js
|
|
2401
|
+
/**
|
|
2402
|
+
* 统一返回结构:自动判断 detail / list / page 并完成映射。
|
|
2403
|
+
* 失败(ok === false)统一抛错,错误带 code / showType。
|
|
2404
|
+
*/
|
|
2405
|
+
var Result = class Result {
|
|
2406
|
+
constructor({ ok = true, code = "0", msg = null, showType = 0, data = null, kind = "detail", pageNum = null, pageSize = null, total = null } = {}) {
|
|
2407
|
+
this.ok = ok;
|
|
2408
|
+
this.code = code;
|
|
2409
|
+
this.msg = msg;
|
|
2410
|
+
this.showType = showType;
|
|
2411
|
+
this.data = data;
|
|
2412
|
+
this.kind = kind;
|
|
2413
|
+
this.pageNum = pageNum;
|
|
2414
|
+
this.pageSize = pageSize;
|
|
2415
|
+
this.total = total;
|
|
2416
|
+
}
|
|
2417
|
+
get isSuccess() {
|
|
2418
|
+
return Boolean(this.ok);
|
|
2419
|
+
}
|
|
2420
|
+
get pages() {
|
|
2421
|
+
return this.pageSize > 0 ? Math.ceil((this.total ?? 0) / this.pageSize) : 0;
|
|
2422
|
+
}
|
|
2423
|
+
static from(raw, command = {}) {
|
|
2424
|
+
if (!raw || raw.ok === false) {
|
|
2425
|
+
const error = new Error(raw?.msg || "请求失败");
|
|
2426
|
+
error.code = raw?.code;
|
|
2427
|
+
error.showType = raw?.showType;
|
|
2428
|
+
throw error;
|
|
2429
|
+
}
|
|
2430
|
+
const data = raw.data;
|
|
2431
|
+
const isPage = Array.isArray(data) && raw.pageNum != null;
|
|
2432
|
+
const isList = Array.isArray(data);
|
|
2433
|
+
return new Result({
|
|
2434
|
+
ok: raw.ok,
|
|
2435
|
+
code: raw.code,
|
|
2436
|
+
msg: raw.msg,
|
|
2437
|
+
showType: raw.showType,
|
|
2438
|
+
kind: isPage ? "page" : isList ? "list" : "detail",
|
|
2439
|
+
data: isPage || isList ? (data ?? []).map((item) => command.toItem ? command.toItem(item) : item) : command.toDetail ? command.toDetail(data) : data,
|
|
2440
|
+
pageNum: raw.pageNum,
|
|
2441
|
+
pageSize: raw.pageSize,
|
|
2442
|
+
total: raw.total
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
//#endregion
|
|
2448
|
+
//#region src/html/index.js
|
|
2449
|
+
/**
|
|
2450
|
+
* HtmlElementNode 是 HTML DSL 的元素节点。
|
|
2451
|
+
* HTML 子元素快捷方法只注册到这个类上,避免被 SVG 节点继承。
|
|
2452
|
+
*/
|
|
2453
|
+
var HtmlElementNode = class extends ElementNode {};
|
|
2454
|
+
const htmlElementDefinitions = [
|
|
2455
|
+
"a",
|
|
2456
|
+
"abbr",
|
|
2457
|
+
"address",
|
|
2458
|
+
"area",
|
|
2459
|
+
"article",
|
|
2460
|
+
"aside",
|
|
2461
|
+
"audio",
|
|
2462
|
+
"b",
|
|
2463
|
+
"base",
|
|
2464
|
+
"bdi",
|
|
2465
|
+
"bdo",
|
|
2466
|
+
"blockquote",
|
|
2467
|
+
"body",
|
|
2468
|
+
"br",
|
|
2469
|
+
"button",
|
|
2470
|
+
"canvas",
|
|
2471
|
+
"caption",
|
|
2472
|
+
"cite",
|
|
2473
|
+
"code",
|
|
2474
|
+
"col",
|
|
2475
|
+
"colgroup",
|
|
2476
|
+
"data",
|
|
2477
|
+
"datalist",
|
|
2478
|
+
"dd",
|
|
2479
|
+
"del",
|
|
2480
|
+
"details",
|
|
2481
|
+
"dfn",
|
|
2482
|
+
"dialog",
|
|
2483
|
+
"div",
|
|
2484
|
+
"dl",
|
|
2485
|
+
"dt",
|
|
2486
|
+
"em",
|
|
2487
|
+
"embed",
|
|
2488
|
+
"fieldset",
|
|
2489
|
+
"figcaption",
|
|
2490
|
+
"figure",
|
|
2491
|
+
"footer",
|
|
2492
|
+
"form",
|
|
2493
|
+
"h1",
|
|
2494
|
+
"h2",
|
|
2495
|
+
"h3",
|
|
2496
|
+
"h4",
|
|
2497
|
+
"h5",
|
|
2498
|
+
"h6",
|
|
2499
|
+
"head",
|
|
2500
|
+
"header",
|
|
2501
|
+
"hgroup",
|
|
2502
|
+
"hr",
|
|
2503
|
+
"html",
|
|
2504
|
+
"i",
|
|
2505
|
+
"iframe",
|
|
2506
|
+
"img",
|
|
2507
|
+
"input",
|
|
2508
|
+
"ins",
|
|
2509
|
+
"kbd",
|
|
2510
|
+
"label",
|
|
2511
|
+
"legend",
|
|
2512
|
+
"li",
|
|
2513
|
+
"link",
|
|
2514
|
+
"main",
|
|
2515
|
+
"map",
|
|
2516
|
+
"mark",
|
|
2517
|
+
"menu",
|
|
2518
|
+
"meta",
|
|
2519
|
+
"meter",
|
|
2520
|
+
"nav",
|
|
2521
|
+
"noscript",
|
|
2522
|
+
"object",
|
|
2523
|
+
"ol",
|
|
2524
|
+
"optgroup",
|
|
2525
|
+
"option",
|
|
2526
|
+
"output",
|
|
2527
|
+
"p",
|
|
2528
|
+
"picture",
|
|
2529
|
+
"pre",
|
|
2530
|
+
"progress",
|
|
2531
|
+
"q",
|
|
2532
|
+
"rp",
|
|
2533
|
+
"rt",
|
|
2534
|
+
"ruby",
|
|
2535
|
+
"s",
|
|
2536
|
+
"samp",
|
|
2537
|
+
"script",
|
|
2538
|
+
"search",
|
|
2539
|
+
"section",
|
|
2540
|
+
"select",
|
|
2541
|
+
"selectedcontent",
|
|
2542
|
+
"slot",
|
|
2543
|
+
"small",
|
|
2544
|
+
"source",
|
|
2545
|
+
"span",
|
|
2546
|
+
"strong",
|
|
2547
|
+
{
|
|
2548
|
+
name: "style",
|
|
2549
|
+
tagName: "style",
|
|
2550
|
+
aliases: ["styleTag"]
|
|
2551
|
+
},
|
|
2552
|
+
"sub",
|
|
2553
|
+
"summary",
|
|
2554
|
+
"sup",
|
|
2555
|
+
"table",
|
|
2556
|
+
"tbody",
|
|
2557
|
+
"td",
|
|
2558
|
+
"template",
|
|
2559
|
+
"textarea",
|
|
2560
|
+
"tfoot",
|
|
2561
|
+
"th",
|
|
2562
|
+
"thead",
|
|
2563
|
+
"time",
|
|
2564
|
+
"title",
|
|
2565
|
+
"tr",
|
|
2566
|
+
"track",
|
|
2567
|
+
"u",
|
|
2568
|
+
"ul",
|
|
2569
|
+
{
|
|
2570
|
+
name: "varTag",
|
|
2571
|
+
tagName: "var"
|
|
2572
|
+
},
|
|
2573
|
+
"video",
|
|
2574
|
+
"wbr"
|
|
2575
|
+
];
|
|
2576
|
+
/**
|
|
2577
|
+
* 生成 HTML 工厂集合。
|
|
2578
|
+
*/
|
|
2579
|
+
function createHtmlFactories() {
|
|
2580
|
+
return htmlElementDefinitions.reduce((factories, definition) => {
|
|
2581
|
+
const { aliases = [], name, tagName } = normalizeElementDefinition(definition);
|
|
2582
|
+
const factory = createElementFactory(tagName, HtmlElementNode);
|
|
2583
|
+
factories[name] = factory;
|
|
2584
|
+
aliases.forEach((alias) => {
|
|
2585
|
+
factories[alias] = factory;
|
|
2586
|
+
});
|
|
2587
|
+
return factories;
|
|
2588
|
+
}, {});
|
|
2589
|
+
}
|
|
2590
|
+
const factories = createHtmlFactories();
|
|
2591
|
+
registerChildFactories(HtmlElementNode, factories);
|
|
2592
|
+
const { a, abbr, address, area, article, aside, audio, b, base, bdi, bdo, blockquote, body, br, button, canvas, caption, cite, code, col, colgroup, data, datalist, dd, del, details, dfn, dialog, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html, i, iframe, img, input, ins, kbd, label, legend, li, link, main, map, mark, menu, meta, meter, nav, noscript, object, ol, optgroup, option, output, p, picture, pre, progress, q, rp, rt, ruby, s, samp, script, search, section, select, selectedcontent, slot, small, source, span, strong, style, styleTag, sub, summary, sup, table, tbody, td, template, textarea, tfoot, th, thead, time, title, tr, track, u, ul, varTag, video, wbr } = factories;
|
|
2593
|
+
/**
|
|
2594
|
+
* 标签定义默认使用同名工厂;遇到 JS 关键字或节点方法冲突时声明别名。
|
|
2595
|
+
*/
|
|
2596
|
+
function normalizeElementDefinition(definition) {
|
|
2597
|
+
if (typeof definition === "string") return {
|
|
2598
|
+
name: definition,
|
|
2599
|
+
tagName: definition
|
|
2600
|
+
};
|
|
2601
|
+
return definition;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
//#endregion
|
|
2605
|
+
//#region src/svg/icons.js
|
|
2606
|
+
var icons_exports = /* @__PURE__ */ __exportAll({
|
|
2607
|
+
ArrowDownOutlined: () => ArrowDownOutlined,
|
|
2608
|
+
ArrowLeftOutlined: () => ArrowLeftOutlined,
|
|
2609
|
+
ArrowRightOutlined: () => ArrowRightOutlined,
|
|
2610
|
+
ArrowUpOutlined: () => ArrowUpOutlined,
|
|
2611
|
+
BellOutlined: () => BellOutlined,
|
|
2612
|
+
CalendarOutlined: () => CalendarOutlined,
|
|
2613
|
+
CheckOutlined: () => CheckOutlined,
|
|
2614
|
+
ChevronDownOutlined: () => ChevronDownOutlined,
|
|
2615
|
+
ChevronLeftOutlined: () => ChevronLeftOutlined,
|
|
2616
|
+
ChevronRightOutlined: () => ChevronRightOutlined,
|
|
2617
|
+
ChevronUpOutlined: () => ChevronUpOutlined,
|
|
2618
|
+
CloseOutlined: () => CloseOutlined,
|
|
2619
|
+
CodeOutlined: () => CodeOutlined,
|
|
2620
|
+
CopyOutlined: () => CopyOutlined,
|
|
2621
|
+
DownloadOutlined: () => DownloadOutlined,
|
|
2622
|
+
EditOutlined: () => EditOutlined,
|
|
2623
|
+
ExternalOutlined: () => ExternalOutlined,
|
|
2624
|
+
EyeOutlined: () => EyeOutlined,
|
|
2625
|
+
FileOutlined: () => FileOutlined,
|
|
2626
|
+
FolderOpenOutlined: () => FolderOpenOutlined,
|
|
2627
|
+
FolderOutlined: () => FolderOutlined,
|
|
2628
|
+
HeartOutlined: () => HeartOutlined,
|
|
2629
|
+
HomeOutlined: () => HomeOutlined,
|
|
2630
|
+
ImageOutlined: () => ImageOutlined,
|
|
2631
|
+
InfoOutlined: () => InfoOutlined,
|
|
2632
|
+
LockOutlined: () => LockOutlined,
|
|
2633
|
+
LogoutOutlined: () => LogoutOutlined,
|
|
2634
|
+
MailOutlined: () => MailOutlined,
|
|
2635
|
+
MenuOutlined: () => MenuOutlined,
|
|
2636
|
+
MinusOutlined: () => MinusOutlined,
|
|
2637
|
+
MonitorOutlined: () => MonitorOutlined,
|
|
2638
|
+
MoonOutlined: () => MoonOutlined,
|
|
2639
|
+
MoreHorizontalOutlined: () => MoreHorizontalOutlined,
|
|
2640
|
+
PlusOutlined: () => PlusOutlined,
|
|
2641
|
+
RefreshOutlined: () => RefreshOutlined,
|
|
2642
|
+
SearchOutlined: () => SearchOutlined,
|
|
2643
|
+
SettingsOutlined: () => SettingsOutlined,
|
|
2644
|
+
StarOutlined: () => StarOutlined,
|
|
2645
|
+
SunOutlined: () => SunOutlined,
|
|
2646
|
+
TrashOutlined: () => TrashOutlined,
|
|
2647
|
+
UploadOutlined: () => UploadOutlined,
|
|
2648
|
+
UserOutlined: () => UserOutlined,
|
|
2649
|
+
WarningOutlined: () => WarningOutlined
|
|
2650
|
+
});
|
|
2651
|
+
function ArrowDownOutlined() {
|
|
2652
|
+
return svg((root) => {
|
|
2653
|
+
root.className("yoya-icon").attr({
|
|
2654
|
+
"aria-hidden": "true",
|
|
2655
|
+
fill: "none",
|
|
2656
|
+
stroke: "currentColor",
|
|
2657
|
+
"stroke-linecap": "round",
|
|
2658
|
+
"stroke-linejoin": "round",
|
|
2659
|
+
"stroke-width": "2",
|
|
2660
|
+
viewBox: "0 0 24 24"
|
|
2661
|
+
}).styles({
|
|
2662
|
+
height: "24px",
|
|
2663
|
+
width: "24px"
|
|
2664
|
+
});
|
|
2665
|
+
root.path({ d: "M12 5v14" });
|
|
2666
|
+
root.path({ d: "m19 12-7 7-7-7" });
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
function ArrowLeftOutlined() {
|
|
2670
|
+
return svg((root) => {
|
|
2671
|
+
root.className("yoya-icon").attr({
|
|
2672
|
+
"aria-hidden": "true",
|
|
2673
|
+
fill: "none",
|
|
2674
|
+
stroke: "currentColor",
|
|
2675
|
+
"stroke-linecap": "round",
|
|
2676
|
+
"stroke-linejoin": "round",
|
|
2677
|
+
"stroke-width": "2",
|
|
2678
|
+
viewBox: "0 0 24 24"
|
|
2679
|
+
}).styles({
|
|
2680
|
+
height: "24px",
|
|
2681
|
+
width: "24px"
|
|
2682
|
+
});
|
|
2683
|
+
root.path({ d: "M19 12H5" });
|
|
2684
|
+
root.path({ d: "m12 19-7-7 7-7" });
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
function ArrowRightOutlined() {
|
|
2688
|
+
return svg((root) => {
|
|
2689
|
+
root.className("yoya-icon").attr({
|
|
2690
|
+
"aria-hidden": "true",
|
|
2691
|
+
fill: "none",
|
|
2692
|
+
stroke: "currentColor",
|
|
2693
|
+
"stroke-linecap": "round",
|
|
2694
|
+
"stroke-linejoin": "round",
|
|
2695
|
+
"stroke-width": "2",
|
|
2696
|
+
viewBox: "0 0 24 24"
|
|
2697
|
+
}).styles({
|
|
2698
|
+
height: "24px",
|
|
2699
|
+
width: "24px"
|
|
2700
|
+
});
|
|
2701
|
+
root.path({ d: "M5 12h14" });
|
|
2702
|
+
root.path({ d: "m12 5 7 7-7 7" });
|
|
2703
|
+
});
|
|
2704
|
+
}
|
|
2705
|
+
function ArrowUpOutlined() {
|
|
2706
|
+
return svg((root) => {
|
|
2707
|
+
root.className("yoya-icon").attr({
|
|
2708
|
+
"aria-hidden": "true",
|
|
2709
|
+
fill: "none",
|
|
2710
|
+
stroke: "currentColor",
|
|
2711
|
+
"stroke-linecap": "round",
|
|
2712
|
+
"stroke-linejoin": "round",
|
|
2713
|
+
"stroke-width": "2",
|
|
2714
|
+
viewBox: "0 0 24 24"
|
|
2715
|
+
}).styles({
|
|
2716
|
+
height: "24px",
|
|
2717
|
+
width: "24px"
|
|
2718
|
+
});
|
|
2719
|
+
root.path({ d: "M12 19V5" });
|
|
2720
|
+
root.path({ d: "m5 12 7-7 7 7" });
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
function BellOutlined() {
|
|
2724
|
+
return svg((root) => {
|
|
2725
|
+
root.className("yoya-icon").attr({
|
|
2726
|
+
"aria-hidden": "true",
|
|
2727
|
+
fill: "none",
|
|
2728
|
+
stroke: "currentColor",
|
|
2729
|
+
"stroke-linecap": "round",
|
|
2730
|
+
"stroke-linejoin": "round",
|
|
2731
|
+
"stroke-width": "2",
|
|
2732
|
+
viewBox: "0 0 24 24"
|
|
2733
|
+
}).styles({
|
|
2734
|
+
height: "24px",
|
|
2735
|
+
width: "24px"
|
|
2736
|
+
});
|
|
2737
|
+
root.path({ d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" });
|
|
2738
|
+
root.path({ d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" });
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
function CalendarOutlined() {
|
|
2742
|
+
return svg((root) => {
|
|
2743
|
+
root.className("yoya-icon").attr({
|
|
2744
|
+
"aria-hidden": "true",
|
|
2745
|
+
fill: "none",
|
|
2746
|
+
stroke: "currentColor",
|
|
2747
|
+
"stroke-linecap": "round",
|
|
2748
|
+
"stroke-linejoin": "round",
|
|
2749
|
+
"stroke-width": "2",
|
|
2750
|
+
viewBox: "0 0 24 24"
|
|
2751
|
+
}).styles({
|
|
2752
|
+
height: "24px",
|
|
2753
|
+
width: "24px"
|
|
2754
|
+
});
|
|
2755
|
+
root.rect({
|
|
2756
|
+
height: "18",
|
|
2757
|
+
rx: "2",
|
|
2758
|
+
width: "18",
|
|
2759
|
+
x: "3",
|
|
2760
|
+
y: "4"
|
|
2761
|
+
});
|
|
2762
|
+
root.path({ d: "M16 2v4" });
|
|
2763
|
+
root.path({ d: "M8 2v4" });
|
|
2764
|
+
root.path({ d: "M3 10h18" });
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
function CheckOutlined() {
|
|
2768
|
+
return svg((root) => {
|
|
2769
|
+
root.className("yoya-icon").attr({
|
|
2770
|
+
"aria-hidden": "true",
|
|
2771
|
+
fill: "none",
|
|
2772
|
+
stroke: "currentColor",
|
|
2773
|
+
"stroke-linecap": "round",
|
|
2774
|
+
"stroke-linejoin": "round",
|
|
2775
|
+
"stroke-width": "2",
|
|
2776
|
+
viewBox: "0 0 24 24"
|
|
2777
|
+
}).styles({
|
|
2778
|
+
height: "24px",
|
|
2779
|
+
width: "24px"
|
|
2780
|
+
});
|
|
2781
|
+
root.path({ d: "M20 6 9 17l-5-5" });
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
function ChevronDownOutlined() {
|
|
2785
|
+
return svg((root) => {
|
|
2786
|
+
root.className("yoya-icon").attr({
|
|
2787
|
+
"aria-hidden": "true",
|
|
2788
|
+
fill: "none",
|
|
2789
|
+
stroke: "currentColor",
|
|
2790
|
+
"stroke-linecap": "round",
|
|
2791
|
+
"stroke-linejoin": "round",
|
|
2792
|
+
"stroke-width": "2",
|
|
2793
|
+
viewBox: "0 0 24 24"
|
|
2794
|
+
}).styles({
|
|
2795
|
+
height: "24px",
|
|
2796
|
+
width: "24px"
|
|
2797
|
+
});
|
|
2798
|
+
root.path({ d: "m6 9 6 6 6-6" });
|
|
2799
|
+
});
|
|
2800
|
+
}
|
|
2801
|
+
function ChevronLeftOutlined() {
|
|
2802
|
+
return svg((root) => {
|
|
2803
|
+
root.className("yoya-icon").attr({
|
|
2804
|
+
"aria-hidden": "true",
|
|
2805
|
+
fill: "none",
|
|
2806
|
+
stroke: "currentColor",
|
|
2807
|
+
"stroke-linecap": "round",
|
|
2808
|
+
"stroke-linejoin": "round",
|
|
2809
|
+
"stroke-width": "2",
|
|
2810
|
+
viewBox: "0 0 24 24"
|
|
2811
|
+
}).styles({
|
|
2812
|
+
height: "24px",
|
|
2813
|
+
width: "24px"
|
|
2814
|
+
});
|
|
2815
|
+
root.path({ d: "m15 18-6-6 6-6" });
|
|
2816
|
+
});
|
|
2817
|
+
}
|
|
2818
|
+
function ChevronRightOutlined() {
|
|
2819
|
+
return svg((root) => {
|
|
2820
|
+
root.className("yoya-icon").attr({
|
|
2821
|
+
"aria-hidden": "true",
|
|
2822
|
+
fill: "none",
|
|
2823
|
+
stroke: "currentColor",
|
|
2824
|
+
"stroke-linecap": "round",
|
|
2825
|
+
"stroke-linejoin": "round",
|
|
2826
|
+
"stroke-width": "2",
|
|
2827
|
+
viewBox: "0 0 24 24"
|
|
2828
|
+
}).styles({
|
|
2829
|
+
height: "24px",
|
|
2830
|
+
width: "24px"
|
|
2831
|
+
});
|
|
2832
|
+
root.path({ d: "m9 18 6-6-6-6" });
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
function ChevronUpOutlined() {
|
|
2836
|
+
return svg((root) => {
|
|
2837
|
+
root.className("yoya-icon").attr({
|
|
2838
|
+
"aria-hidden": "true",
|
|
2839
|
+
fill: "none",
|
|
2840
|
+
stroke: "currentColor",
|
|
2841
|
+
"stroke-linecap": "round",
|
|
2842
|
+
"stroke-linejoin": "round",
|
|
2843
|
+
"stroke-width": "2",
|
|
2844
|
+
viewBox: "0 0 24 24"
|
|
2845
|
+
}).styles({
|
|
2846
|
+
height: "24px",
|
|
2847
|
+
width: "24px"
|
|
2848
|
+
});
|
|
2849
|
+
root.path({ d: "m18 15-6-6-6 6" });
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
function CloseOutlined() {
|
|
2853
|
+
return svg((root) => {
|
|
2854
|
+
root.className("yoya-icon").attr({
|
|
2855
|
+
"aria-hidden": "true",
|
|
2856
|
+
fill: "none",
|
|
2857
|
+
stroke: "currentColor",
|
|
2858
|
+
"stroke-linecap": "round",
|
|
2859
|
+
"stroke-linejoin": "round",
|
|
2860
|
+
"stroke-width": "2",
|
|
2861
|
+
viewBox: "0 0 24 24"
|
|
2862
|
+
}).styles({
|
|
2863
|
+
height: "24px",
|
|
2864
|
+
width: "24px"
|
|
2865
|
+
});
|
|
2866
|
+
root.path({ d: "M18 6 6 18" });
|
|
2867
|
+
root.path({ d: "m6 6 12 12" });
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
function CodeOutlined() {
|
|
2871
|
+
return svg((root) => {
|
|
2872
|
+
root.className("yoya-icon").attr({
|
|
2873
|
+
"aria-hidden": "true",
|
|
2874
|
+
fill: "none",
|
|
2875
|
+
stroke: "currentColor",
|
|
2876
|
+
"stroke-linecap": "round",
|
|
2877
|
+
"stroke-linejoin": "round",
|
|
2878
|
+
"stroke-width": "2",
|
|
2879
|
+
viewBox: "0 0 24 24"
|
|
2880
|
+
}).styles({
|
|
2881
|
+
height: "24px",
|
|
2882
|
+
width: "24px"
|
|
2883
|
+
});
|
|
2884
|
+
root.path({ d: "m16 18 6-6-6-6" });
|
|
2885
|
+
root.path({ d: "m8 6-6 6 6 6" });
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
function CopyOutlined() {
|
|
2889
|
+
return svg((root) => {
|
|
2890
|
+
root.className("yoya-icon").attr({
|
|
2891
|
+
"aria-hidden": "true",
|
|
2892
|
+
fill: "none",
|
|
2893
|
+
stroke: "currentColor",
|
|
2894
|
+
"stroke-linecap": "round",
|
|
2895
|
+
"stroke-linejoin": "round",
|
|
2896
|
+
"stroke-width": "2",
|
|
2897
|
+
viewBox: "0 0 24 24"
|
|
2898
|
+
}).styles({
|
|
2899
|
+
height: "24px",
|
|
2900
|
+
width: "24px"
|
|
2901
|
+
});
|
|
2902
|
+
root.rect({
|
|
2903
|
+
height: "14",
|
|
2904
|
+
rx: "2",
|
|
2905
|
+
width: "14",
|
|
2906
|
+
x: "8",
|
|
2907
|
+
y: "8"
|
|
2908
|
+
});
|
|
2909
|
+
root.path({ d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" });
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
function DownloadOutlined() {
|
|
2913
|
+
return svg((root) => {
|
|
2914
|
+
root.className("yoya-icon").attr({
|
|
2915
|
+
"aria-hidden": "true",
|
|
2916
|
+
fill: "none",
|
|
2917
|
+
stroke: "currentColor",
|
|
2918
|
+
"stroke-linecap": "round",
|
|
2919
|
+
"stroke-linejoin": "round",
|
|
2920
|
+
"stroke-width": "2",
|
|
2921
|
+
viewBox: "0 0 24 24"
|
|
2922
|
+
}).styles({
|
|
2923
|
+
height: "24px",
|
|
2924
|
+
width: "24px"
|
|
2925
|
+
});
|
|
2926
|
+
root.path({ d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" });
|
|
2927
|
+
root.path({ d: "m7 10 5 5 5-5" });
|
|
2928
|
+
root.path({ d: "M12 15V3" });
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
function EditOutlined() {
|
|
2932
|
+
return svg((root) => {
|
|
2933
|
+
root.className("yoya-icon").attr({
|
|
2934
|
+
"aria-hidden": "true",
|
|
2935
|
+
fill: "none",
|
|
2936
|
+
stroke: "currentColor",
|
|
2937
|
+
"stroke-linecap": "round",
|
|
2938
|
+
"stroke-linejoin": "round",
|
|
2939
|
+
"stroke-width": "2",
|
|
2940
|
+
viewBox: "0 0 24 24"
|
|
2941
|
+
}).styles({
|
|
2942
|
+
height: "24px",
|
|
2943
|
+
width: "24px"
|
|
2944
|
+
});
|
|
2945
|
+
root.path({ d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" });
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
function ExternalOutlined() {
|
|
2949
|
+
return svg((root) => {
|
|
2950
|
+
root.className("yoya-icon").attr({
|
|
2951
|
+
"aria-hidden": "true",
|
|
2952
|
+
fill: "none",
|
|
2953
|
+
stroke: "currentColor",
|
|
2954
|
+
"stroke-linecap": "round",
|
|
2955
|
+
"stroke-linejoin": "round",
|
|
2956
|
+
"stroke-width": "2",
|
|
2957
|
+
viewBox: "0 0 24 24"
|
|
2958
|
+
}).styles({
|
|
2959
|
+
height: "24px",
|
|
2960
|
+
width: "24px"
|
|
2961
|
+
});
|
|
2962
|
+
root.path({ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" });
|
|
2963
|
+
root.path({ d: "m15 3 6 6" });
|
|
2964
|
+
root.path({ d: "m21 3-9 9" });
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
function EyeOutlined() {
|
|
2968
|
+
return svg((root) => {
|
|
2969
|
+
root.className("yoya-icon").attr({
|
|
2970
|
+
"aria-hidden": "true",
|
|
2971
|
+
fill: "none",
|
|
2972
|
+
stroke: "currentColor",
|
|
2973
|
+
"stroke-linecap": "round",
|
|
2974
|
+
"stroke-linejoin": "round",
|
|
2975
|
+
"stroke-width": "2",
|
|
2976
|
+
viewBox: "0 0 24 24"
|
|
2977
|
+
}).styles({
|
|
2978
|
+
height: "24px",
|
|
2979
|
+
width: "24px"
|
|
2980
|
+
});
|
|
2981
|
+
root.path({ d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" });
|
|
2982
|
+
root.circle({
|
|
2983
|
+
cx: "12",
|
|
2984
|
+
cy: "12",
|
|
2985
|
+
r: "3"
|
|
2986
|
+
});
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
function FileOutlined() {
|
|
2990
|
+
return svg((root) => {
|
|
2991
|
+
root.className("yoya-icon").attr({
|
|
2992
|
+
"aria-hidden": "true",
|
|
2993
|
+
fill: "none",
|
|
2994
|
+
stroke: "currentColor",
|
|
2995
|
+
"stroke-linecap": "round",
|
|
2996
|
+
"stroke-linejoin": "round",
|
|
2997
|
+
"stroke-width": "2",
|
|
2998
|
+
viewBox: "0 0 24 24"
|
|
2999
|
+
}).styles({
|
|
3000
|
+
height: "24px",
|
|
3001
|
+
width: "24px"
|
|
3002
|
+
});
|
|
3003
|
+
root.path({ d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" });
|
|
3004
|
+
root.path({ d: "M14 2v4a2 2 0 0 0 2 2h4" });
|
|
3005
|
+
});
|
|
3006
|
+
}
|
|
3007
|
+
function ImageOutlined() {
|
|
3008
|
+
return svg((root) => {
|
|
3009
|
+
root.className("yoya-icon").attr({
|
|
3010
|
+
"aria-hidden": "true",
|
|
3011
|
+
fill: "none",
|
|
3012
|
+
stroke: "currentColor",
|
|
3013
|
+
"stroke-linecap": "round",
|
|
3014
|
+
"stroke-linejoin": "round",
|
|
3015
|
+
"stroke-width": "2",
|
|
3016
|
+
viewBox: "0 0 24 24"
|
|
3017
|
+
}).styles({
|
|
3018
|
+
height: "24px",
|
|
3019
|
+
width: "24px"
|
|
3020
|
+
});
|
|
3021
|
+
root.rect({
|
|
3022
|
+
height: "18",
|
|
3023
|
+
rx: "2",
|
|
3024
|
+
width: "18",
|
|
3025
|
+
x: "3",
|
|
3026
|
+
y: "3"
|
|
3027
|
+
});
|
|
3028
|
+
root.circle({
|
|
3029
|
+
cx: "9",
|
|
3030
|
+
cy: "9",
|
|
3031
|
+
r: "2"
|
|
3032
|
+
});
|
|
3033
|
+
root.path({ d: "m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21" });
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
function FolderOutlined() {
|
|
3037
|
+
return svg((root) => {
|
|
3038
|
+
root.className("yoya-icon").attr({
|
|
3039
|
+
"aria-hidden": "true",
|
|
3040
|
+
fill: "none",
|
|
3041
|
+
stroke: "currentColor",
|
|
3042
|
+
"stroke-linecap": "round",
|
|
3043
|
+
"stroke-linejoin": "round",
|
|
3044
|
+
"stroke-width": "2",
|
|
3045
|
+
viewBox: "0 0 24 24"
|
|
3046
|
+
}).styles({
|
|
3047
|
+
height: "24px",
|
|
3048
|
+
width: "24px"
|
|
3049
|
+
});
|
|
3050
|
+
root.path({ d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" });
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
function FolderOpenOutlined() {
|
|
3054
|
+
return svg((root) => {
|
|
3055
|
+
root.className("yoya-icon").attr({
|
|
3056
|
+
"aria-hidden": "true",
|
|
3057
|
+
fill: "none",
|
|
3058
|
+
stroke: "currentColor",
|
|
3059
|
+
"stroke-linecap": "round",
|
|
3060
|
+
"stroke-linejoin": "round",
|
|
3061
|
+
"stroke-width": "2",
|
|
3062
|
+
viewBox: "0 0 24 24"
|
|
3063
|
+
}).styles({
|
|
3064
|
+
height: "24px",
|
|
3065
|
+
width: "24px"
|
|
3066
|
+
});
|
|
3067
|
+
root.path({ d: "m6 14 1.45-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.55 6a2 2 0 0 1-1.94 1.5H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.93a2 2 0 0 1 1.66.9l.82 1.2a2 2 0 0 0 1.66.9H18a2 2 0 0 1 2 2v2" });
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3070
|
+
function HeartOutlined() {
|
|
3071
|
+
return svg((root) => {
|
|
3072
|
+
root.className("yoya-icon").attr({
|
|
3073
|
+
"aria-hidden": "true",
|
|
3074
|
+
fill: "none",
|
|
3075
|
+
stroke: "currentColor",
|
|
3076
|
+
"stroke-linecap": "round",
|
|
3077
|
+
"stroke-linejoin": "round",
|
|
3078
|
+
"stroke-width": "2",
|
|
3079
|
+
viewBox: "0 0 24 24"
|
|
3080
|
+
}).styles({
|
|
3081
|
+
height: "24px",
|
|
3082
|
+
width: "24px"
|
|
3083
|
+
});
|
|
3084
|
+
root.path({ d: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" });
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
function HomeOutlined() {
|
|
3088
|
+
return svg((root) => {
|
|
3089
|
+
root.className("yoya-icon").attr({
|
|
3090
|
+
"aria-hidden": "true",
|
|
3091
|
+
fill: "none",
|
|
3092
|
+
stroke: "currentColor",
|
|
3093
|
+
"stroke-linecap": "round",
|
|
3094
|
+
"stroke-linejoin": "round",
|
|
3095
|
+
"stroke-width": "2",
|
|
3096
|
+
viewBox: "0 0 24 24"
|
|
3097
|
+
}).styles({
|
|
3098
|
+
height: "24px",
|
|
3099
|
+
width: "24px"
|
|
3100
|
+
});
|
|
3101
|
+
root.path({ d: "m3 9.5 9-6.5 9 6.5" });
|
|
3102
|
+
root.path({ d: "M5 10v10h5v-6h4v6h5V10" });
|
|
3103
|
+
});
|
|
3104
|
+
}
|
|
3105
|
+
function InfoOutlined() {
|
|
3106
|
+
return svg((root) => {
|
|
3107
|
+
root.className("yoya-icon").attr({
|
|
3108
|
+
"aria-hidden": "true",
|
|
3109
|
+
fill: "none",
|
|
3110
|
+
stroke: "currentColor",
|
|
3111
|
+
"stroke-linecap": "round",
|
|
3112
|
+
"stroke-linejoin": "round",
|
|
3113
|
+
"stroke-width": "2",
|
|
3114
|
+
viewBox: "0 0 24 24"
|
|
3115
|
+
}).styles({
|
|
3116
|
+
height: "24px",
|
|
3117
|
+
width: "24px"
|
|
3118
|
+
});
|
|
3119
|
+
root.circle({
|
|
3120
|
+
cx: "12",
|
|
3121
|
+
cy: "12",
|
|
3122
|
+
r: "10"
|
|
3123
|
+
});
|
|
3124
|
+
root.path({ d: "M12 16v-4" });
|
|
3125
|
+
root.path({ d: "M12 8h.01" });
|
|
3126
|
+
});
|
|
3127
|
+
}
|
|
3128
|
+
function LockOutlined() {
|
|
3129
|
+
return svg((root) => {
|
|
3130
|
+
root.className("yoya-icon").attr({
|
|
3131
|
+
"aria-hidden": "true",
|
|
3132
|
+
fill: "none",
|
|
3133
|
+
stroke: "currentColor",
|
|
3134
|
+
"stroke-linecap": "round",
|
|
3135
|
+
"stroke-linejoin": "round",
|
|
3136
|
+
"stroke-width": "2",
|
|
3137
|
+
viewBox: "0 0 24 24"
|
|
3138
|
+
}).styles({
|
|
3139
|
+
height: "24px",
|
|
3140
|
+
width: "24px"
|
|
3141
|
+
});
|
|
3142
|
+
root.rect({
|
|
3143
|
+
height: "11",
|
|
3144
|
+
rx: "2",
|
|
3145
|
+
width: "18",
|
|
3146
|
+
x: "3",
|
|
3147
|
+
y: "11"
|
|
3148
|
+
});
|
|
3149
|
+
root.path({ d: "M7 11V7a5 5 0 0 1 10 0v4" });
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3152
|
+
function LogoutOutlined() {
|
|
3153
|
+
return svg((root) => {
|
|
3154
|
+
root.className("yoya-icon").attr({
|
|
3155
|
+
"aria-hidden": "true",
|
|
3156
|
+
fill: "none",
|
|
3157
|
+
stroke: "currentColor",
|
|
3158
|
+
"stroke-linecap": "round",
|
|
3159
|
+
"stroke-linejoin": "round",
|
|
3160
|
+
"stroke-width": "2",
|
|
3161
|
+
viewBox: "0 0 24 24"
|
|
3162
|
+
}).styles({
|
|
3163
|
+
height: "24px",
|
|
3164
|
+
width: "24px"
|
|
3165
|
+
});
|
|
3166
|
+
root.path({ d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" });
|
|
3167
|
+
root.path({ d: "m16 17 5-5-5-5" });
|
|
3168
|
+
root.path({ d: "M21 12H9" });
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
function MailOutlined() {
|
|
3172
|
+
return svg((root) => {
|
|
3173
|
+
root.className("yoya-icon").attr({
|
|
3174
|
+
"aria-hidden": "true",
|
|
3175
|
+
fill: "none",
|
|
3176
|
+
stroke: "currentColor",
|
|
3177
|
+
"stroke-linecap": "round",
|
|
3178
|
+
"stroke-linejoin": "round",
|
|
3179
|
+
"stroke-width": "2",
|
|
3180
|
+
viewBox: "0 0 24 24"
|
|
3181
|
+
}).styles({
|
|
3182
|
+
height: "24px",
|
|
3183
|
+
width: "24px"
|
|
3184
|
+
});
|
|
3185
|
+
root.rect({
|
|
3186
|
+
height: "16",
|
|
3187
|
+
rx: "2",
|
|
3188
|
+
width: "20",
|
|
3189
|
+
x: "2",
|
|
3190
|
+
y: "4"
|
|
3191
|
+
});
|
|
3192
|
+
root.path({ d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" });
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
function MenuOutlined() {
|
|
3196
|
+
return svg((root) => {
|
|
3197
|
+
root.className("yoya-icon").attr({
|
|
3198
|
+
"aria-hidden": "true",
|
|
3199
|
+
fill: "none",
|
|
3200
|
+
stroke: "currentColor",
|
|
3201
|
+
"stroke-linecap": "round",
|
|
3202
|
+
"stroke-linejoin": "round",
|
|
3203
|
+
"stroke-width": "2",
|
|
3204
|
+
viewBox: "0 0 24 24"
|
|
3205
|
+
}).styles({
|
|
3206
|
+
height: "24px",
|
|
3207
|
+
width: "24px"
|
|
3208
|
+
});
|
|
3209
|
+
root.path({ d: "M4 6h16" });
|
|
3210
|
+
root.path({ d: "M4 12h16" });
|
|
3211
|
+
root.path({ d: "M4 18h16" });
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
function MinusOutlined() {
|
|
3215
|
+
return svg((root) => {
|
|
3216
|
+
root.className("yoya-icon").attr({
|
|
3217
|
+
"aria-hidden": "true",
|
|
3218
|
+
fill: "none",
|
|
3219
|
+
stroke: "currentColor",
|
|
3220
|
+
"stroke-linecap": "round",
|
|
3221
|
+
"stroke-linejoin": "round",
|
|
3222
|
+
"stroke-width": "2",
|
|
3223
|
+
viewBox: "0 0 24 24"
|
|
3224
|
+
}).styles({
|
|
3225
|
+
height: "24px",
|
|
3226
|
+
width: "24px"
|
|
3227
|
+
});
|
|
3228
|
+
root.path({ d: "M5 12h14" });
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
function MoreHorizontalOutlined() {
|
|
3232
|
+
return svg((root) => {
|
|
3233
|
+
root.className("yoya-icon").attr({
|
|
3234
|
+
"aria-hidden": "true",
|
|
3235
|
+
fill: "none",
|
|
3236
|
+
stroke: "currentColor",
|
|
3237
|
+
"stroke-linecap": "round",
|
|
3238
|
+
"stroke-linejoin": "round",
|
|
3239
|
+
"stroke-width": "2",
|
|
3240
|
+
viewBox: "0 0 24 24"
|
|
3241
|
+
}).styles({
|
|
3242
|
+
height: "24px",
|
|
3243
|
+
width: "24px"
|
|
3244
|
+
});
|
|
3245
|
+
root.circle({
|
|
3246
|
+
cx: "5",
|
|
3247
|
+
cy: "12",
|
|
3248
|
+
r: "1",
|
|
3249
|
+
fill: "currentColor"
|
|
3250
|
+
});
|
|
3251
|
+
root.circle({
|
|
3252
|
+
cx: "12",
|
|
3253
|
+
cy: "12",
|
|
3254
|
+
r: "1",
|
|
3255
|
+
fill: "currentColor"
|
|
3256
|
+
});
|
|
3257
|
+
root.circle({
|
|
3258
|
+
cx: "19",
|
|
3259
|
+
cy: "12",
|
|
3260
|
+
r: "1",
|
|
3261
|
+
fill: "currentColor"
|
|
3262
|
+
});
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
function PlusOutlined() {
|
|
3266
|
+
return svg((root) => {
|
|
3267
|
+
root.className("yoya-icon").attr({
|
|
3268
|
+
"aria-hidden": "true",
|
|
3269
|
+
fill: "none",
|
|
3270
|
+
stroke: "currentColor",
|
|
3271
|
+
"stroke-linecap": "round",
|
|
3272
|
+
"stroke-linejoin": "round",
|
|
3273
|
+
"stroke-width": "2",
|
|
3274
|
+
viewBox: "0 0 24 24"
|
|
3275
|
+
}).styles({
|
|
3276
|
+
height: "24px",
|
|
3277
|
+
width: "24px"
|
|
3278
|
+
});
|
|
3279
|
+
root.path({ d: "M5 12h14" });
|
|
3280
|
+
root.path({ d: "M12 5v14" });
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
function RefreshOutlined() {
|
|
3284
|
+
return svg((root) => {
|
|
3285
|
+
root.className("yoya-icon").attr({
|
|
3286
|
+
"aria-hidden": "true",
|
|
3287
|
+
fill: "none",
|
|
3288
|
+
stroke: "currentColor",
|
|
3289
|
+
"stroke-linecap": "round",
|
|
3290
|
+
"stroke-linejoin": "round",
|
|
3291
|
+
"stroke-width": "2",
|
|
3292
|
+
viewBox: "0 0 24 24"
|
|
3293
|
+
}).styles({
|
|
3294
|
+
height: "24px",
|
|
3295
|
+
width: "24px"
|
|
3296
|
+
});
|
|
3297
|
+
root.path({ d: "M3 12a9 9 0 0 1 15-6.7L21 8" });
|
|
3298
|
+
root.path({ d: "M21 3v5h-5" });
|
|
3299
|
+
root.path({ d: "M21 12a9 9 0 0 1-15 6.7L3 16" });
|
|
3300
|
+
root.path({ d: "M3 21v-5h5" });
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
function SearchOutlined() {
|
|
3304
|
+
return svg((root) => {
|
|
3305
|
+
root.className("yoya-icon").attr({
|
|
3306
|
+
"aria-hidden": "true",
|
|
3307
|
+
fill: "none",
|
|
3308
|
+
stroke: "currentColor",
|
|
3309
|
+
"stroke-linecap": "round",
|
|
3310
|
+
"stroke-linejoin": "round",
|
|
3311
|
+
"stroke-width": "2",
|
|
3312
|
+
viewBox: "0 0 24 24"
|
|
3313
|
+
}).styles({
|
|
3314
|
+
height: "24px",
|
|
3315
|
+
width: "24px"
|
|
3316
|
+
});
|
|
3317
|
+
root.circle({
|
|
3318
|
+
cx: "11",
|
|
3319
|
+
cy: "11",
|
|
3320
|
+
r: "7"
|
|
3321
|
+
});
|
|
3322
|
+
root.path({ d: "m20 20-4-4" });
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3325
|
+
function SettingsOutlined() {
|
|
3326
|
+
return svg((root) => {
|
|
3327
|
+
root.className("yoya-icon").attr({
|
|
3328
|
+
"aria-hidden": "true",
|
|
3329
|
+
fill: "none",
|
|
3330
|
+
stroke: "currentColor",
|
|
3331
|
+
"stroke-linecap": "round",
|
|
3332
|
+
"stroke-linejoin": "round",
|
|
3333
|
+
"stroke-width": "2",
|
|
3334
|
+
viewBox: "0 0 24 24"
|
|
3335
|
+
}).styles({
|
|
3336
|
+
height: "24px",
|
|
3337
|
+
width: "24px"
|
|
3338
|
+
});
|
|
3339
|
+
root.circle({
|
|
3340
|
+
cx: "12",
|
|
3341
|
+
cy: "12",
|
|
3342
|
+
r: "3"
|
|
3343
|
+
});
|
|
3344
|
+
root.path({ d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2Z" });
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
function StarOutlined() {
|
|
3348
|
+
return svg((root) => {
|
|
3349
|
+
root.className("yoya-icon").attr({
|
|
3350
|
+
"aria-hidden": "true",
|
|
3351
|
+
fill: "none",
|
|
3352
|
+
stroke: "currentColor",
|
|
3353
|
+
"stroke-linecap": "round",
|
|
3354
|
+
"stroke-linejoin": "round",
|
|
3355
|
+
"stroke-width": "2",
|
|
3356
|
+
viewBox: "0 0 24 24"
|
|
3357
|
+
}).styles({
|
|
3358
|
+
height: "24px",
|
|
3359
|
+
width: "24px"
|
|
3360
|
+
});
|
|
3361
|
+
root.path({
|
|
3362
|
+
d: "m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
|
|
3363
|
+
fill: "currentColor",
|
|
3364
|
+
stroke: "none"
|
|
3365
|
+
});
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
function TrashOutlined() {
|
|
3369
|
+
return svg((root) => {
|
|
3370
|
+
root.className("yoya-icon").attr({
|
|
3371
|
+
"aria-hidden": "true",
|
|
3372
|
+
fill: "none",
|
|
3373
|
+
stroke: "currentColor",
|
|
3374
|
+
"stroke-linecap": "round",
|
|
3375
|
+
"stroke-linejoin": "round",
|
|
3376
|
+
"stroke-width": "2",
|
|
3377
|
+
viewBox: "0 0 24 24"
|
|
3378
|
+
}).styles({
|
|
3379
|
+
height: "24px",
|
|
3380
|
+
width: "24px"
|
|
3381
|
+
});
|
|
3382
|
+
root.path({ d: "M3 6h18" });
|
|
3383
|
+
root.path({ d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" });
|
|
3384
|
+
root.path({ d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" });
|
|
3385
|
+
root.path({ d: "M10 10v6" });
|
|
3386
|
+
root.path({ d: "M14 10v6" });
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
function UploadOutlined() {
|
|
3390
|
+
return svg((root) => {
|
|
3391
|
+
root.className("yoya-icon").attr({
|
|
3392
|
+
"aria-hidden": "true",
|
|
3393
|
+
fill: "none",
|
|
3394
|
+
stroke: "currentColor",
|
|
3395
|
+
"stroke-linecap": "round",
|
|
3396
|
+
"stroke-linejoin": "round",
|
|
3397
|
+
"stroke-width": "2",
|
|
3398
|
+
viewBox: "0 0 24 24"
|
|
3399
|
+
}).styles({
|
|
3400
|
+
height: "24px",
|
|
3401
|
+
width: "24px"
|
|
3402
|
+
});
|
|
3403
|
+
root.path({ d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" });
|
|
3404
|
+
root.path({ d: "m17 8-5-5-5 5" });
|
|
3405
|
+
root.path({ d: "M12 3v12" });
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
function UserOutlined() {
|
|
3409
|
+
return svg((root) => {
|
|
3410
|
+
root.className("yoya-icon").attr({
|
|
3411
|
+
"aria-hidden": "true",
|
|
3412
|
+
fill: "none",
|
|
3413
|
+
stroke: "currentColor",
|
|
3414
|
+
"stroke-linecap": "round",
|
|
3415
|
+
"stroke-linejoin": "round",
|
|
3416
|
+
"stroke-width": "2",
|
|
3417
|
+
viewBox: "0 0 24 24"
|
|
3418
|
+
}).styles({
|
|
3419
|
+
height: "24px",
|
|
3420
|
+
width: "24px"
|
|
3421
|
+
});
|
|
3422
|
+
root.path({ d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" });
|
|
3423
|
+
root.circle({
|
|
3424
|
+
cx: "12",
|
|
3425
|
+
cy: "7",
|
|
3426
|
+
r: "4"
|
|
3427
|
+
});
|
|
3428
|
+
});
|
|
3429
|
+
}
|
|
3430
|
+
function WarningOutlined() {
|
|
3431
|
+
return svg((root) => {
|
|
3432
|
+
root.className("yoya-icon").attr({
|
|
3433
|
+
"aria-hidden": "true",
|
|
3434
|
+
fill: "none",
|
|
3435
|
+
stroke: "currentColor",
|
|
3436
|
+
"stroke-linecap": "round",
|
|
3437
|
+
"stroke-linejoin": "round",
|
|
3438
|
+
"stroke-width": "2",
|
|
3439
|
+
viewBox: "0 0 24 24"
|
|
3440
|
+
}).styles({
|
|
3441
|
+
height: "24px",
|
|
3442
|
+
width: "24px"
|
|
3443
|
+
});
|
|
3444
|
+
root.path({ d: "M21.73 18 13.34 3.7a2 2 0 0 0-3.44 0L2.27 18A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" });
|
|
3445
|
+
root.path({ d: "M12 9v4" });
|
|
3446
|
+
root.path({ d: "M12 17h.01" });
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
function SunOutlined() {
|
|
3450
|
+
return svg((root) => {
|
|
3451
|
+
root.className("yoya-icon").attr({
|
|
3452
|
+
"aria-hidden": "true",
|
|
3453
|
+
fill: "none",
|
|
3454
|
+
stroke: "currentColor",
|
|
3455
|
+
"stroke-linecap": "round",
|
|
3456
|
+
"stroke-linejoin": "round",
|
|
3457
|
+
"stroke-width": "2",
|
|
3458
|
+
viewBox: "0 0 24 24"
|
|
3459
|
+
}).styles({
|
|
3460
|
+
height: "24px",
|
|
3461
|
+
width: "24px"
|
|
3462
|
+
});
|
|
3463
|
+
root.circle({
|
|
3464
|
+
cx: "12",
|
|
3465
|
+
cy: "12",
|
|
3466
|
+
r: "4"
|
|
3467
|
+
});
|
|
3468
|
+
root.path({ d: "M12 2v2" });
|
|
3469
|
+
root.path({ d: "M12 20v2" });
|
|
3470
|
+
root.path({ d: "m4.93 4.93 1.41 1.41" });
|
|
3471
|
+
root.path({ d: "m17.66 17.66 1.41 1.41" });
|
|
3472
|
+
root.path({ d: "M2 12h2" });
|
|
3473
|
+
root.path({ d: "M20 12h2" });
|
|
3474
|
+
root.path({ d: "m6.34 17.66-1.41 1.41" });
|
|
3475
|
+
root.path({ d: "m19.07 4.93-1.41 1.41" });
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3478
|
+
function MoonOutlined() {
|
|
3479
|
+
return svg((root) => {
|
|
3480
|
+
root.className("yoya-icon").attr({
|
|
3481
|
+
"aria-hidden": "true",
|
|
3482
|
+
fill: "none",
|
|
3483
|
+
stroke: "currentColor",
|
|
3484
|
+
"stroke-linecap": "round",
|
|
3485
|
+
"stroke-linejoin": "round",
|
|
3486
|
+
"stroke-width": "2",
|
|
3487
|
+
viewBox: "0 0 24 24"
|
|
3488
|
+
}).styles({
|
|
3489
|
+
height: "24px",
|
|
3490
|
+
width: "24px"
|
|
3491
|
+
});
|
|
3492
|
+
root.path({ d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" });
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
function MonitorOutlined() {
|
|
3496
|
+
return svg((root) => {
|
|
3497
|
+
root.className("yoya-icon").attr({
|
|
3498
|
+
"aria-hidden": "true",
|
|
3499
|
+
fill: "none",
|
|
3500
|
+
stroke: "currentColor",
|
|
3501
|
+
"stroke-linecap": "round",
|
|
3502
|
+
"stroke-linejoin": "round",
|
|
3503
|
+
"stroke-width": "2",
|
|
3504
|
+
viewBox: "0 0 24 24"
|
|
3505
|
+
}).styles({
|
|
3506
|
+
height: "24px",
|
|
3507
|
+
width: "24px"
|
|
3508
|
+
});
|
|
3509
|
+
root.rect({
|
|
3510
|
+
height: "14",
|
|
3511
|
+
rx: "2",
|
|
3512
|
+
width: "20",
|
|
3513
|
+
x: "2",
|
|
3514
|
+
y: "3"
|
|
3515
|
+
});
|
|
3516
|
+
root.path({ d: "M8 21h8" });
|
|
3517
|
+
root.path({ d: "M12 17v4" });
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
//#endregion
|
|
3522
|
+
//#region src/svg/index.js
|
|
3523
|
+
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
3524
|
+
const svgTextContentHosts = /* @__PURE__ */ new Set([
|
|
3525
|
+
"desc",
|
|
3526
|
+
"metadata",
|
|
3527
|
+
"text",
|
|
3528
|
+
"textPath",
|
|
3529
|
+
"title",
|
|
3530
|
+
"tspan"
|
|
3531
|
+
]);
|
|
3532
|
+
/**
|
|
3533
|
+
* SvgElementNode 表示 SVG 命名空间下的元素节点。
|
|
3534
|
+
* SVG 子元素快捷方法只注册到这个类上,避免和 HTML DSL 互相干扰。
|
|
3535
|
+
*/
|
|
3536
|
+
var SvgElementNode = class SvgElementNode extends ElementNode {
|
|
3537
|
+
/**
|
|
3538
|
+
* SVG 标签方法可以使用 text/style 原名,所以字符串 setup 直接写入文本节点。
|
|
3539
|
+
*/
|
|
3540
|
+
setup(setup) {
|
|
3541
|
+
if (typeof setup === "string" || typeof setup === "number") return this.child(setup);
|
|
3542
|
+
return super.setup(setup);
|
|
3543
|
+
}
|
|
3544
|
+
/**
|
|
3545
|
+
* 在 SVG 容器上创建 <text>;在文本类 SVG 元素内部继续表示文本内容。
|
|
3546
|
+
*/
|
|
3547
|
+
text(...args) {
|
|
3548
|
+
if (svgTextContentHosts.has(this._tagName)) {
|
|
3549
|
+
const [content, setup] = args;
|
|
3550
|
+
if (args.length > 0) this.child(content);
|
|
3551
|
+
if (setup !== null && setup !== void 0) this.setup(setup);
|
|
3552
|
+
return this;
|
|
3553
|
+
}
|
|
3554
|
+
return this._svgChild("text", ...args);
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* 单参数字符串/函数创建 SVG <style>,双参数或对象仍保留 CSS 样式设置能力。
|
|
3558
|
+
*/
|
|
3559
|
+
style(...args) {
|
|
3560
|
+
const [name, value] = args;
|
|
3561
|
+
if (this._tagName === "style" && args.length <= 1) {
|
|
3562
|
+
if (args.length === 1) this.child(name);
|
|
3563
|
+
return this;
|
|
3564
|
+
}
|
|
3565
|
+
if (args.length <= 1 && isSvgStyleTagSetup(name)) return this._svgChild("style", name);
|
|
3566
|
+
return super.style(name, value);
|
|
3567
|
+
}
|
|
3568
|
+
renderDom() {
|
|
3569
|
+
if (this._deleted) return null;
|
|
3570
|
+
if (!this._el) {
|
|
3571
|
+
this._el = document.createElementNS(SVG_NAMESPACE, this._tagName);
|
|
3572
|
+
this._applySnapshotToElement();
|
|
3573
|
+
}
|
|
3574
|
+
return this._el;
|
|
3575
|
+
}
|
|
3576
|
+
/**
|
|
3577
|
+
* SVGElement.className 通常是 SVGAnimatedString,不能像 HTMLElement 一样直接赋值。
|
|
3578
|
+
*/
|
|
3579
|
+
_syncClassName() {
|
|
3580
|
+
const className = [...this._classes].join(" ");
|
|
3581
|
+
if (className) this._attrs.class = className;
|
|
3582
|
+
else delete this._attrs.class;
|
|
3583
|
+
if (this._el) {
|
|
3584
|
+
if (className) this._el.setAttribute("class", className);
|
|
3585
|
+
else this._el.removeAttribute("class");
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
_svgChild(tagName, ...setups) {
|
|
3589
|
+
const child = new SvgElementNode(tagName);
|
|
3590
|
+
setups.forEach((setup) => {
|
|
3591
|
+
if (setup !== null && setup !== void 0) child.setup(setup);
|
|
3592
|
+
});
|
|
3593
|
+
return this.child(child);
|
|
3594
|
+
}
|
|
3595
|
+
};
|
|
3596
|
+
function isSvgStyleTagSetup(value) {
|
|
3597
|
+
return value === null || value === void 0 || typeof value === "function" || typeof value === "string" || typeof value === "number" || Array.isArray(value);
|
|
3598
|
+
}
|
|
3599
|
+
const svgChildElementDefinitions = [
|
|
3600
|
+
"animate",
|
|
3601
|
+
"animateMotion",
|
|
3602
|
+
"animateTransform",
|
|
3603
|
+
"circle",
|
|
3604
|
+
"clipPath",
|
|
3605
|
+
"defs",
|
|
3606
|
+
"desc",
|
|
3607
|
+
"ellipse",
|
|
3608
|
+
"feBlend",
|
|
3609
|
+
"feColorMatrix",
|
|
3610
|
+
"feComponentTransfer",
|
|
3611
|
+
"feComposite",
|
|
3612
|
+
"feConvolveMatrix",
|
|
3613
|
+
"feDiffuseLighting",
|
|
3614
|
+
"feDisplacementMap",
|
|
3615
|
+
"feDistantLight",
|
|
3616
|
+
"feDropShadow",
|
|
3617
|
+
"feFlood",
|
|
3618
|
+
"feFuncA",
|
|
3619
|
+
"feFuncB",
|
|
3620
|
+
"feFuncG",
|
|
3621
|
+
"feFuncR",
|
|
3622
|
+
"feGaussianBlur",
|
|
3623
|
+
"feImage",
|
|
3624
|
+
"feMerge",
|
|
3625
|
+
"feMergeNode",
|
|
3626
|
+
"feMorphology",
|
|
3627
|
+
"feOffset",
|
|
3628
|
+
"fePointLight",
|
|
3629
|
+
"feSpecularLighting",
|
|
3630
|
+
"feSpotLight",
|
|
3631
|
+
"feTile",
|
|
3632
|
+
"feTurbulence",
|
|
3633
|
+
"filter",
|
|
3634
|
+
"foreignObject",
|
|
3635
|
+
"g",
|
|
3636
|
+
"image",
|
|
3637
|
+
"line",
|
|
3638
|
+
"linearGradient",
|
|
3639
|
+
"marker",
|
|
3640
|
+
"mask",
|
|
3641
|
+
"metadata",
|
|
3642
|
+
"mpath",
|
|
3643
|
+
"path",
|
|
3644
|
+
"pattern",
|
|
3645
|
+
"polygon",
|
|
3646
|
+
"polyline",
|
|
3647
|
+
"radialGradient",
|
|
3648
|
+
"rect",
|
|
3649
|
+
"set",
|
|
3650
|
+
"stop",
|
|
3651
|
+
"a",
|
|
3652
|
+
"script",
|
|
3653
|
+
"style",
|
|
3654
|
+
"switch",
|
|
3655
|
+
"text",
|
|
3656
|
+
"title",
|
|
3657
|
+
"symbol",
|
|
3658
|
+
"textPath",
|
|
3659
|
+
"tspan",
|
|
3660
|
+
"use",
|
|
3661
|
+
"view"
|
|
3662
|
+
];
|
|
3663
|
+
/**
|
|
3664
|
+
* 为 SVG 标签创建工厂函数。
|
|
3665
|
+
*/
|
|
3666
|
+
function createSvgElementFactory(tagName) {
|
|
3667
|
+
return function svgElementFactory(...setups) {
|
|
3668
|
+
const node = new SvgElementNode(tagName);
|
|
3669
|
+
if (tagName === "svg") node.style("display", "block");
|
|
3670
|
+
setups.forEach((setup) => {
|
|
3671
|
+
if (setup !== null && setup !== void 0) node.setup(setup);
|
|
3672
|
+
});
|
|
3673
|
+
return node;
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
/**
|
|
3677
|
+
* 生成 SVG 工厂集合。
|
|
3678
|
+
*/
|
|
3679
|
+
function createSvgFactories() {
|
|
3680
|
+
return svgChildElementDefinitions.reduce((factories, definition) => {
|
|
3681
|
+
const { aliases = [], name, tagName } = normalizeSvgElementDefinition(definition);
|
|
3682
|
+
const factory = createSvgElementFactory(tagName);
|
|
3683
|
+
factories[name] = factory;
|
|
3684
|
+
aliases.forEach((alias) => {
|
|
3685
|
+
factories[alias] = factory;
|
|
3686
|
+
});
|
|
3687
|
+
return factories;
|
|
3688
|
+
}, {});
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* 只有 <svg> 是 HTML DSL 可见入口;SVG 子标签必须通过 svg 节点内部方法添加。
|
|
3692
|
+
*/
|
|
3693
|
+
const svg = createSvgElementFactory("svg");
|
|
3694
|
+
const svgChildFactories = createSvgFactories();
|
|
3695
|
+
registerChildFactories(HtmlElementNode, { svg });
|
|
3696
|
+
registerChildFactories(SvgElementNode, {
|
|
3697
|
+
svg,
|
|
3698
|
+
...svgChildFactories
|
|
3699
|
+
});
|
|
3700
|
+
function normalizeSvgElementDefinition(definition) {
|
|
3701
|
+
if (typeof definition === "string") return {
|
|
3702
|
+
name: definition,
|
|
3703
|
+
tagName: definition
|
|
3704
|
+
};
|
|
3705
|
+
return definition;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
//#endregion
|
|
3709
|
+
//#region src/components/shared.js
|
|
3710
|
+
const componentClass = "yoya-component";
|
|
3711
|
+
function themeValue(token, fallback) {
|
|
3712
|
+
return `var(--yoya-${token}, ${fallback})`;
|
|
3713
|
+
}
|
|
3714
|
+
function themeBorder(token, fallback, width = "1px") {
|
|
3715
|
+
return `${width} solid ${themeValue(token, fallback)}`;
|
|
3716
|
+
}
|
|
3717
|
+
function normalizeComponentArguments(first = null, second = null, third = null) {
|
|
3718
|
+
return normalizeSetupArguments(first, second, third);
|
|
3719
|
+
}
|
|
3720
|
+
function applyElementOptions(node, options) {
|
|
3721
|
+
if (typeof node.attr === "function" || typeof node.styles === "function") return applyElementOptions$1(node, options);
|
|
3722
|
+
if (typeof node.render === "function") applyElementOptions$1(node.render(), options);
|
|
3723
|
+
return node;
|
|
3724
|
+
}
|
|
3725
|
+
function applyComponentArguments(node, options = null, callback = null) {
|
|
3726
|
+
applyElementOptions(node, options);
|
|
3727
|
+
if (typeof callback === "function") callback(node);
|
|
3728
|
+
return node;
|
|
3729
|
+
}
|
|
3730
|
+
function createComponentFactory(Component, first = null, second = null, third = null) {
|
|
3731
|
+
const { first: setup, options, callback } = normalizeComponentArguments(first, second, third);
|
|
3732
|
+
return applyComponentArguments(setup instanceof Component ? setup : new Component(setup), options, callback);
|
|
3733
|
+
}
|
|
3734
|
+
function applyComponentSetup(node, setup) {
|
|
3735
|
+
if (setup === null || setup === void 0) return node;
|
|
3736
|
+
if (typeof setup === "function") {
|
|
3737
|
+
setup(node);
|
|
3738
|
+
return node;
|
|
3739
|
+
}
|
|
3740
|
+
if (setup instanceof ViewNode || Array.isArray(setup) || typeof setup === "string" || typeof setup === "number") {
|
|
3741
|
+
node.child(setup);
|
|
3742
|
+
return node;
|
|
3743
|
+
}
|
|
3744
|
+
if (isPlainObject(setup)) node.setup(setup);
|
|
3745
|
+
return node;
|
|
3746
|
+
}
|
|
3747
|
+
function normalizeChildren(content) {
|
|
3748
|
+
if (content === null || content === void 0) return [];
|
|
3749
|
+
return Array.isArray(content) ? content : [content];
|
|
3750
|
+
}
|
|
3751
|
+
function replaceChildren(node, children) {
|
|
3752
|
+
node.children().forEach((child) => child.destroy());
|
|
3753
|
+
node._children = [];
|
|
3754
|
+
if (node._el) node._el.replaceChildren();
|
|
3755
|
+
if (children.length > 0) node.child(children);
|
|
3756
|
+
return node;
|
|
3757
|
+
}
|
|
3758
|
+
function removeChild(parent, child) {
|
|
3759
|
+
parent._children = parent.children().filter((existingChild) => existingChild !== child);
|
|
3760
|
+
return parent;
|
|
3761
|
+
}
|
|
3762
|
+
function setupButtonSlot(button, setup) {
|
|
3763
|
+
if (setup === null || setup === void 0) return button;
|
|
3764
|
+
if (typeof setup === "function") {
|
|
3765
|
+
setup(button);
|
|
3766
|
+
return button;
|
|
3767
|
+
}
|
|
3768
|
+
if (isPlainObject(setup)) {
|
|
3769
|
+
button._setupButton(setup);
|
|
3770
|
+
return button;
|
|
3771
|
+
}
|
|
3772
|
+
button.label(setup);
|
|
3773
|
+
return button;
|
|
3774
|
+
}
|
|
3775
|
+
function setupContentSlot(node, setup) {
|
|
3776
|
+
replaceChildren(node, []);
|
|
3777
|
+
if (setup === null || setup === void 0) return node;
|
|
3778
|
+
if (typeof setup === "function") {
|
|
3779
|
+
setup(node);
|
|
3780
|
+
return node;
|
|
3781
|
+
}
|
|
3782
|
+
applyComponentSetup(node, setup);
|
|
3783
|
+
return node;
|
|
3784
|
+
}
|
|
3785
|
+
function dropdownPlacementStyles(placement) {
|
|
3786
|
+
const base = {
|
|
3787
|
+
bottom: null,
|
|
3788
|
+
left: null,
|
|
3789
|
+
right: null,
|
|
3790
|
+
top: null
|
|
3791
|
+
};
|
|
3792
|
+
const placements = {
|
|
3793
|
+
"bottom-end": {
|
|
3794
|
+
right: "0",
|
|
3795
|
+
top: "calc(100% + 6px)"
|
|
3796
|
+
},
|
|
3797
|
+
"bottom-start": {
|
|
3798
|
+
left: "0",
|
|
3799
|
+
top: "calc(100% + 6px)"
|
|
3800
|
+
},
|
|
3801
|
+
"top-end": {
|
|
3802
|
+
bottom: "calc(100% + 6px)",
|
|
3803
|
+
right: "0"
|
|
3804
|
+
},
|
|
3805
|
+
"top-start": {
|
|
3806
|
+
bottom: "calc(100% + 6px)",
|
|
3807
|
+
left: "0"
|
|
3808
|
+
}
|
|
3809
|
+
};
|
|
3810
|
+
return {
|
|
3811
|
+
...base,
|
|
3812
|
+
...placements[placement] || placements["bottom-start"]
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function normalizePoint(pointOrX, y) {
|
|
3816
|
+
if (pointOrX && typeof pointOrX === "object") return {
|
|
3817
|
+
x: Number(pointOrX.clientX ?? pointOrX.x ?? 0),
|
|
3818
|
+
y: Number(pointOrX.clientY ?? pointOrX.y ?? 0)
|
|
3819
|
+
};
|
|
3820
|
+
return {
|
|
3821
|
+
x: Number(pointOrX || 0),
|
|
3822
|
+
y: Number(y || 0)
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
function messageTypeStyles(type) {
|
|
3826
|
+
const styles = {
|
|
3827
|
+
error: {
|
|
3828
|
+
background: themeValue("color-danger-subtle", "#fef2f2"),
|
|
3829
|
+
borderColor: themeValue("color-danger-border", "#fecaca"),
|
|
3830
|
+
color: themeValue("color-danger-text", "#991b1b")
|
|
3831
|
+
},
|
|
3832
|
+
info: {
|
|
3833
|
+
background: themeValue("color-info-subtle", "#eff6ff"),
|
|
3834
|
+
borderColor: themeValue("color-info-border", "#bfdbfe"),
|
|
3835
|
+
color: themeValue("color-info-text", "#1e3a8a")
|
|
3836
|
+
},
|
|
3837
|
+
success: {
|
|
3838
|
+
background: themeValue("color-success-subtle", "#ecfdf5"),
|
|
3839
|
+
borderColor: themeValue("color-success-border", "#bbf7d0"),
|
|
3840
|
+
color: themeValue("color-success-text", "#166534")
|
|
3841
|
+
},
|
|
3842
|
+
warning: {
|
|
3843
|
+
background: themeValue("color-warning-subtle", "#fffbeb"),
|
|
3844
|
+
borderColor: themeValue("color-warning-border", "#fde68a"),
|
|
3845
|
+
color: themeValue("color-warning-text", "#92400e")
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3848
|
+
return styles[type] || styles.info;
|
|
3849
|
+
}
|
|
3850
|
+
function placementStyles(placement) {
|
|
3851
|
+
const base = {
|
|
3852
|
+
bottom: null,
|
|
3853
|
+
left: null,
|
|
3854
|
+
right: null,
|
|
3855
|
+
top: null,
|
|
3856
|
+
transform: null
|
|
3857
|
+
};
|
|
3858
|
+
const placements = {
|
|
3859
|
+
"bottom-left": {
|
|
3860
|
+
bottom: "16px",
|
|
3861
|
+
left: "16px"
|
|
3862
|
+
},
|
|
3863
|
+
"bottom-right": {
|
|
3864
|
+
bottom: "16px",
|
|
3865
|
+
right: "16px"
|
|
3866
|
+
},
|
|
3867
|
+
bottom: {
|
|
3868
|
+
bottom: "16px",
|
|
3869
|
+
left: "50%",
|
|
3870
|
+
transform: "translateX(-50%)"
|
|
3871
|
+
},
|
|
3872
|
+
"top-left": {
|
|
3873
|
+
left: "16px",
|
|
3874
|
+
top: "16px"
|
|
3875
|
+
},
|
|
3876
|
+
"top-right": {
|
|
3877
|
+
right: "16px",
|
|
3878
|
+
top: "16px"
|
|
3879
|
+
},
|
|
3880
|
+
top: {
|
|
3881
|
+
left: "50%",
|
|
3882
|
+
top: "16px",
|
|
3883
|
+
transform: "translateX(-50%)"
|
|
3884
|
+
}
|
|
3885
|
+
};
|
|
3886
|
+
return {
|
|
3887
|
+
...base,
|
|
3888
|
+
...placements[placement] || placements["top-right"]
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
function normalizeMessageOptions(options = {}) {
|
|
3892
|
+
if (typeof options === "number") return { duration: options };
|
|
3893
|
+
return options || {};
|
|
3894
|
+
}
|
|
3895
|
+
function isPlainObject(value) {
|
|
3896
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
3897
|
+
const prototype = Object.getPrototypeOf(value);
|
|
3898
|
+
return prototype === Object.prototype || prototype === null;
|
|
3899
|
+
}
|
|
3900
|
+
function resolveTextValue(value) {
|
|
3901
|
+
if (value === null || value === void 0) return "";
|
|
3902
|
+
if (Array.isArray(value)) return value.map((item) => resolveTextValue(item)).join("");
|
|
3903
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
|
|
3904
|
+
if (typeof value.textContent === "function") return value.textContent();
|
|
3905
|
+
return String(value);
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
//#endregion
|
|
3909
|
+
//#region src/core/id.js
|
|
3910
|
+
let activeAllocator = null;
|
|
3911
|
+
let fallbackSequence = 0;
|
|
3912
|
+
/**
|
|
3913
|
+
* 创建一次渲染/挂载使用的 id 分配器。分配器在渲染上下文中共享,
|
|
3914
|
+
* 保证同一次渲染内 id 唯一且只依赖本次渲染的树结构(确定性、跨请求隔离)。
|
|
3915
|
+
*/
|
|
3916
|
+
function createIdAllocator() {
|
|
3917
|
+
let value = 0;
|
|
3918
|
+
return { next() {
|
|
3919
|
+
value += 1;
|
|
3920
|
+
return value;
|
|
3921
|
+
} };
|
|
3922
|
+
}
|
|
3923
|
+
/**
|
|
3924
|
+
* 分配一个数字序号。渲染上下文中使用上下文分配器,否则退回模块级计数器。
|
|
3925
|
+
*/
|
|
3926
|
+
function allocateNumber() {
|
|
3927
|
+
if (activeAllocator) return activeAllocator.next();
|
|
3928
|
+
fallbackSequence += 1;
|
|
3929
|
+
return fallbackSequence;
|
|
3930
|
+
}
|
|
3931
|
+
/**
|
|
3932
|
+
* 分配一个带前缀的确定性 id。
|
|
3933
|
+
*/
|
|
3934
|
+
function allocateId(prefix) {
|
|
3935
|
+
return `${prefix}-${allocateNumber()}`;
|
|
3936
|
+
}
|
|
3937
|
+
/**
|
|
3938
|
+
* 在指定分配器作用域内执行构建,结束后恢复外层上下文。
|
|
3939
|
+
*/
|
|
3940
|
+
function withIdAllocator(allocator, build) {
|
|
3941
|
+
const previous = activeAllocator;
|
|
3942
|
+
activeAllocator = allocator;
|
|
3943
|
+
try {
|
|
3944
|
+
return build();
|
|
3945
|
+
} finally {
|
|
3946
|
+
activeAllocator = previous;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
//#endregion
|
|
3951
|
+
//#region src/core/ssr.js
|
|
3952
|
+
/**
|
|
3953
|
+
* 从已取好的请求字段解析语言标识,优先级:cookie > query > Accept-Language > 默认值。
|
|
3954
|
+
* 不依赖具体请求对象形态:cookie / url / acceptLanguage 由调用方按框架自行提取
|
|
3955
|
+
* (Node 系取 req.headers.cookie,Fetch 系取 request.headers.get('cookie') 等)。
|
|
3956
|
+
*/
|
|
3957
|
+
function resolveLocale(input = {}, options = {}) {
|
|
3958
|
+
const { cookie = "", url = "", acceptLanguage = "" } = input || {};
|
|
3959
|
+
const { cookieKey = "yoya-lang", queryKey = "locale", defaultLanguage = "zh-CN" } = options;
|
|
3960
|
+
const cookieLocale = readCookieValue(cookie, cookieKey);
|
|
3961
|
+
if (cookieLocale) return cookieLocale;
|
|
3962
|
+
const queryLocale = readQueryValue(url, queryKey);
|
|
3963
|
+
if (queryLocale) return queryLocale;
|
|
3964
|
+
const acceptLocale = readAcceptLanguage(acceptLanguage);
|
|
3965
|
+
if (acceptLocale) return acceptLocale;
|
|
3966
|
+
return defaultLanguage;
|
|
3967
|
+
}
|
|
3968
|
+
function readCookieValue(cookieHeader, key) {
|
|
3969
|
+
if (!cookieHeader || !key) return null;
|
|
3970
|
+
for (const part of String(cookieHeader).split(";")) {
|
|
3971
|
+
const separator = part.indexOf("=");
|
|
3972
|
+
if (separator === -1) continue;
|
|
3973
|
+
if (part.slice(0, separator).trim() !== key) continue;
|
|
3974
|
+
const raw = part.slice(separator + 1).trim();
|
|
3975
|
+
if (!raw) return null;
|
|
3976
|
+
try {
|
|
3977
|
+
return decodeURIComponent(raw);
|
|
3978
|
+
} catch {
|
|
3979
|
+
return raw;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
return null;
|
|
3983
|
+
}
|
|
3984
|
+
function readQueryValue(url, key) {
|
|
3985
|
+
if (!url || !key) return null;
|
|
3986
|
+
const queryStart = String(url).indexOf("?");
|
|
3987
|
+
if (queryStart === -1) return null;
|
|
3988
|
+
return new URLSearchParams(String(url).slice(queryStart + 1)).get(key) || null;
|
|
3989
|
+
}
|
|
3990
|
+
function readAcceptLanguage(header) {
|
|
3991
|
+
if (!header) return null;
|
|
3992
|
+
const first = String(header).split(",")[0];
|
|
3993
|
+
if (!first) return null;
|
|
3994
|
+
return first.split(";")[0].trim() || null;
|
|
3995
|
+
}
|
|
3996
|
+
/**
|
|
3997
|
+
* 有 i18n 配置时,在构建期间把 ".s()" 快捷方式作用域到指定 I18n 实例;
|
|
3998
|
+
* i18n 可传 createI18n 工厂(接收 state)或直接传实例,构建结束后恢复外层实例。
|
|
3999
|
+
*/
|
|
4000
|
+
function scopeAccessBuild(access, build) {
|
|
4001
|
+
if (!access) return build();
|
|
4002
|
+
const ctx = typeof access === "function" ? access() : access;
|
|
4003
|
+
return withAccess(ctx, build);
|
|
4004
|
+
}
|
|
4005
|
+
function scopeBuild(access, context, i18n, state, build) {
|
|
4006
|
+
return scopeAccessBuild(access, () => scopeContextBuild(context, state, () => scopeI18nBuild(i18n, state, build)));
|
|
4007
|
+
}
|
|
4008
|
+
function scopeContextBuild(context, state, build) {
|
|
4009
|
+
if (!context) return build();
|
|
4010
|
+
const providers = typeof context === "function" ? context(state) : context;
|
|
4011
|
+
return withContext(providers, build);
|
|
4012
|
+
}
|
|
4013
|
+
function scopeI18nBuild(i18n, state, build) {
|
|
4014
|
+
if (!i18n) return build();
|
|
4015
|
+
const locale = typeof i18n === "function" ? i18n(state) : i18n;
|
|
4016
|
+
return withI18nStringShortcut(locale, build);
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* 统一解析组件为 ViewNode,支持三种形态:
|
|
4020
|
+
* 函数工厂(接收 initialState)、带 render() 的对象组件、ViewNode 实例。
|
|
4021
|
+
*/
|
|
4022
|
+
function createRootNode(component, state = null) {
|
|
4023
|
+
if (component instanceof ViewNode) return component;
|
|
4024
|
+
if (typeof component === "function") return createRootNode(component(state), state);
|
|
4025
|
+
if (component && typeof component.render === "function") return createRootNode(component.render(), state);
|
|
4026
|
+
throw new TypeError("renderToString/mount requires a ViewNode, a component object with render(), or a factory function");
|
|
4027
|
+
}
|
|
4028
|
+
/**
|
|
4029
|
+
* 统计视图树节点数,供服务端输出上限策略使用。
|
|
4030
|
+
*/
|
|
4031
|
+
function countNodes(node) {
|
|
4032
|
+
let count = 1;
|
|
4033
|
+
if (typeof node.children === "function") node.children().forEach((child) => {
|
|
4034
|
+
(child instanceof ComponentNode ? child._resolveList() : [child]).forEach((root) => {
|
|
4035
|
+
count += countNodes(root);
|
|
4036
|
+
});
|
|
4037
|
+
});
|
|
4038
|
+
return count;
|
|
4039
|
+
}
|
|
4040
|
+
/**
|
|
4041
|
+
* 服务端把组件渲染成 HTML 字符串,并把初始状态序列化(安全内联到 script)。
|
|
4042
|
+
* maxNodes 超限时返回 exceeded,服务端可回退客户端渲染。
|
|
4043
|
+
*/
|
|
4044
|
+
function renderToString(component, options = {}) {
|
|
4045
|
+
const { access = null, context = null, maxNodes = Infinity, state = null, i18n = null } = options || {};
|
|
4046
|
+
const serialized = serializeState(state);
|
|
4047
|
+
return withIdAllocator(createIdAllocator(), () => {
|
|
4048
|
+
const build = () => {
|
|
4049
|
+
const ownsTree = typeof component === "function";
|
|
4050
|
+
const node = createRootNode(component, state);
|
|
4051
|
+
let result;
|
|
4052
|
+
try {
|
|
4053
|
+
if (countNodes(node) > maxNodes) result = {
|
|
4054
|
+
exceeded: true,
|
|
4055
|
+
html: "",
|
|
4056
|
+
state: serialized
|
|
4057
|
+
};
|
|
4058
|
+
else result = {
|
|
4059
|
+
exceeded: false,
|
|
4060
|
+
html: node.toHTML(),
|
|
4061
|
+
state: serialized
|
|
4062
|
+
};
|
|
4063
|
+
} finally {
|
|
4064
|
+
if (ownsTree) node.destroy();
|
|
4065
|
+
}
|
|
4066
|
+
return result;
|
|
4067
|
+
};
|
|
4068
|
+
return scopeBuild(access, context, i18n, state, build);
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
/**
|
|
4072
|
+
* 页面文档构建节点:暴露 head(cb) / body(cb) 两个结构方法与 vBody 快捷写法。
|
|
4073
|
+
* 只用于 renderPage 内部,不直接渲染为 DOM 元素。
|
|
4074
|
+
*/
|
|
4075
|
+
var PageDocumentNode = class extends HtmlElementNode {
|
|
4076
|
+
constructor() {
|
|
4077
|
+
super("html", null);
|
|
4078
|
+
this._headCallback = null;
|
|
4079
|
+
this._bodyCallback = null;
|
|
4080
|
+
}
|
|
4081
|
+
head(callback) {
|
|
4082
|
+
this._headCallback = typeof callback === "function" ? callback : null;
|
|
4083
|
+
return this;
|
|
4084
|
+
}
|
|
4085
|
+
body(callback) {
|
|
4086
|
+
this._bodyCallback = typeof callback === "function" ? callback : null;
|
|
4087
|
+
return this;
|
|
4088
|
+
}
|
|
4089
|
+
/** vBody 快捷写法:等价 page.body((body) => body.vBody(...))。 */
|
|
4090
|
+
vBody(...args) {
|
|
4091
|
+
return this.body((body) => body.vBody(...args));
|
|
4092
|
+
}
|
|
4093
|
+
};
|
|
4094
|
+
function escapeHtmlAttribute(value) {
|
|
4095
|
+
return String(value ?? "").replace(/&/g, "&").replace(/"/g, """);
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* 渲染整个 HTML 文档:page.head / page.body 分别用 DSL 定义,
|
|
4099
|
+
* 状态序列化进可自定义 id 的 script(默认 __YOYA_DATA__),末尾挂客户端入口。
|
|
4100
|
+
* state 是唯一请求状态来源,回调签名 (node, state);options.messages 或 i18n
|
|
4101
|
+
* 二选一用于按 state.lang 建每请求实例。
|
|
4102
|
+
*/
|
|
4103
|
+
function renderPage(pageConfig, state = {}, options = {}) {
|
|
4104
|
+
const { client = "/client.js", containerId = "app", access = null, context = null, i18n = null, maxNodes = Infinity, messages, stateId = "__YOYA_DATA__" } = options || {};
|
|
4105
|
+
const pageState = state || {};
|
|
4106
|
+
if (!pageConfig || typeof pageConfig.page !== "function") throw new TypeError("renderPage requires { page: (page, state) => {} }");
|
|
4107
|
+
const i18nFactory = i18n || (messages ? () => createI18n({
|
|
4108
|
+
language: pageState.lang || "zh-CN",
|
|
4109
|
+
messages
|
|
4110
|
+
}) : null);
|
|
4111
|
+
const serialized = serializeState(pageState);
|
|
4112
|
+
return withIdAllocator(createIdAllocator(), () => scopeBuild(access, context, i18nFactory, pageState, () => {
|
|
4113
|
+
const page = new PageDocumentNode();
|
|
4114
|
+
pageConfig.page(page, pageState);
|
|
4115
|
+
const headNode = new HtmlElementNode("head");
|
|
4116
|
+
if (page._headCallback) page._headCallback(headNode, pageState);
|
|
4117
|
+
const bodyNode = new HtmlElementNode("body");
|
|
4118
|
+
if (page._bodyCallback) page._bodyCallback(bodyNode, pageState);
|
|
4119
|
+
const bodyExceeded = countNodes(bodyNode) > maxNodes;
|
|
4120
|
+
const headHtml = headNode.toHTML();
|
|
4121
|
+
const appContainer = `<div id="${escapeHtmlAttribute(containerId)}">`;
|
|
4122
|
+
const bodyHtml = bodyExceeded ? `${appContainer}</div>` : `${appContainer}${bodyNode.toHTML()}</div>`;
|
|
4123
|
+
const stateScript = `<script type="application/json" id="${escapeHtmlAttribute(stateId)}">${serialized}<\/script>`;
|
|
4124
|
+
const clientScript = `<script type="module" src="${escapeHtmlAttribute(client)}"><\/script>`;
|
|
4125
|
+
headNode.destroy();
|
|
4126
|
+
bodyNode.destroy();
|
|
4127
|
+
return `<!doctype html>
|
|
4128
|
+
<html lang="${escapeHtmlAttribute(pageState.lang || "zh-CN")}">
|
|
4129
|
+
${headHtml}
|
|
4130
|
+
<body>
|
|
4131
|
+
${bodyHtml}
|
|
4132
|
+
${stateScript}
|
|
4133
|
+
${clientScript}
|
|
4134
|
+
</body>
|
|
4135
|
+
</html>`;
|
|
4136
|
+
}));
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* 客户端一行接入:读取 stateId 对应的序列化状态,目标容器有服务端 HTML 时
|
|
4140
|
+
* hydrate(收养 DOM、绑事件),否则 mount(全量客户端渲染)。
|
|
4141
|
+
* options:{ messages?, i18n?, stateId = '__YOYA_DATA__', target = '#app' }。
|
|
4142
|
+
*/
|
|
4143
|
+
function hydrateOrMount(component, options = {}) {
|
|
4144
|
+
if (typeof document === "undefined") return null;
|
|
4145
|
+
const { messages, i18n = null, stateId = "__YOYA_DATA__", target = "#app" } = options || {};
|
|
4146
|
+
const stateElement = document.getElementById(stateId);
|
|
4147
|
+
const state = parseState(stateElement ? stateElement.textContent : "");
|
|
4148
|
+
const i18nOption = i18n || (messages ? () => createI18n({
|
|
4149
|
+
language: state?.lang || "zh-CN",
|
|
4150
|
+
messages
|
|
4151
|
+
}) : null);
|
|
4152
|
+
const parent = resolveTarget(target);
|
|
4153
|
+
if (parent && parent.firstElementChild) return hydrate(component, target, state, {
|
|
4154
|
+
access: options.access,
|
|
4155
|
+
context: options.context,
|
|
4156
|
+
i18n: i18nOption
|
|
4157
|
+
});
|
|
4158
|
+
return mount(component, target, state, {
|
|
4159
|
+
access: options.access,
|
|
4160
|
+
context: options.context,
|
|
4161
|
+
i18n: i18nOption
|
|
4162
|
+
});
|
|
4163
|
+
}
|
|
4164
|
+
/**
|
|
4165
|
+
* 序列化首屏状态为 JSON 字符串,`<` 转义为 \u003c,可安全嵌入 <script>。
|
|
4166
|
+
*/
|
|
4167
|
+
function serializeState(state) {
|
|
4168
|
+
if (state === null || state === void 0) return null;
|
|
4169
|
+
return JSON.stringify(state).replace(/</g, "\\u003c");
|
|
4170
|
+
}
|
|
4171
|
+
/**
|
|
4172
|
+
* 解析序列化状态;null/空串返回 null。
|
|
4173
|
+
*/
|
|
4174
|
+
function parseState(serialized) {
|
|
4175
|
+
if (serialized === null || serialized === void 0 || serialized === "") return null;
|
|
4176
|
+
return JSON.parse(serialized);
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* 客户端全量重建挂载:以 initialState 创建组件树,替换目标容器内容并绑定事件。
|
|
4180
|
+
*/
|
|
4181
|
+
function mount(component, target, state = null, options = {}) {
|
|
4182
|
+
return withIdAllocator(createIdAllocator(), () => {
|
|
4183
|
+
const build = () => {
|
|
4184
|
+
const node = createRootNode(component, state);
|
|
4185
|
+
const parent = resolveTarget(target);
|
|
4186
|
+
if (parent) {
|
|
4187
|
+
parent.replaceChildren();
|
|
4188
|
+
parent.appendChild(node.renderDom());
|
|
4189
|
+
}
|
|
4190
|
+
return node;
|
|
4191
|
+
};
|
|
4192
|
+
return scopeBuild(options.access, options.context, options.i18n, state, build);
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
/**
|
|
4196
|
+
* 客户端 hydration:收养服务端生成的 DOM(不重建元素),绑定 pending 事件,
|
|
4197
|
+
* 并让属性/文本按客户端树对齐。渲染确定性的前提下,节点身份保持不变。
|
|
4198
|
+
*/
|
|
4199
|
+
function hydrate(component, target, state = null, options = {}) {
|
|
4200
|
+
return withIdAllocator(createIdAllocator(), () => {
|
|
4201
|
+
const build = () => {
|
|
4202
|
+
const node = createRootNode(component, state);
|
|
4203
|
+
const parent = resolveTarget(target);
|
|
4204
|
+
if (parent) {
|
|
4205
|
+
const rootElement = parent.firstElementChild;
|
|
4206
|
+
if (rootElement) {
|
|
4207
|
+
adoptElement(node, rootElement);
|
|
4208
|
+
syncSnapshots(node);
|
|
4209
|
+
bindElement(node);
|
|
4210
|
+
node.renderDom();
|
|
4211
|
+
} else parent.appendChild(node.renderDom());
|
|
4212
|
+
}
|
|
4213
|
+
return node;
|
|
4214
|
+
};
|
|
4215
|
+
return scopeBuild(options.access, options.context, options.i18n, state, build);
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
function adoptElement(node, existing) {
|
|
4219
|
+
if (node instanceof ComponentNode) {
|
|
4220
|
+
const roots = node._resolveList();
|
|
4221
|
+
if (roots.length === 1) {
|
|
4222
|
+
adoptElement(roots[0], existing);
|
|
4223
|
+
return;
|
|
4224
|
+
}
|
|
4225
|
+
const childNodes = existing ? Array.from(existing.childNodes) : [];
|
|
4226
|
+
roots.forEach((root, index) => adoptElement(root, childNodes[index]));
|
|
4227
|
+
return;
|
|
4228
|
+
}
|
|
4229
|
+
if (node instanceof VTextNode) {
|
|
4230
|
+
if (existing && existing.nodeType === 3) {
|
|
4231
|
+
node._textNode = existing;
|
|
4232
|
+
node._el = existing;
|
|
4233
|
+
if (existing.textContent !== node._content) existing.textContent = node._content;
|
|
4234
|
+
} else replaceExisting(existing, node.renderDom());
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
if (existing && existing.nodeType === 1 && existing.tagName.toLowerCase() === node._tagName) {
|
|
4238
|
+
node._el = existing;
|
|
4239
|
+
node._hydrated = true;
|
|
4240
|
+
const childNodes = Array.from(existing.childNodes);
|
|
4241
|
+
let cursor = 0;
|
|
4242
|
+
node.children().forEach((child) => {
|
|
4243
|
+
(child instanceof ComponentNode ? child._resolveList() : [child]).forEach((root) => {
|
|
4244
|
+
adoptElement(root, childNodes[cursor]);
|
|
4245
|
+
cursor += 1;
|
|
4246
|
+
});
|
|
4247
|
+
});
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
4250
|
+
replaceExisting(existing, node.renderDom());
|
|
4251
|
+
}
|
|
4252
|
+
function bindElement(node) {
|
|
4253
|
+
if (node instanceof ComponentNode) {
|
|
4254
|
+
node._resolveList().forEach((root) => bindElement(root));
|
|
4255
|
+
return;
|
|
4256
|
+
}
|
|
4257
|
+
if (node instanceof VTextNode) return;
|
|
4258
|
+
if (node._el && node._hydrated) node._applyBindingsToElement();
|
|
4259
|
+
node.children().forEach(bindElement);
|
|
4260
|
+
}
|
|
4261
|
+
function syncSnapshots(node) {
|
|
4262
|
+
if (node instanceof ComponentNode) {
|
|
4263
|
+
node._resolveList().forEach((root) => syncSnapshots(root));
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
if (node instanceof VTextNode) return;
|
|
4267
|
+
node.children().forEach(syncSnapshots);
|
|
4268
|
+
if (typeof node.hydrateSnapshot === "function") node.hydrateSnapshot();
|
|
4269
|
+
}
|
|
4270
|
+
function replaceExisting(existing, created) {
|
|
4271
|
+
if (existing && existing.parentNode) existing.parentNode.replaceChild(created, existing);
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
//#endregion
|
|
4275
|
+
export { FileOutlined as $, s as $n, subscribeDevtools as $r, dfn as $t, themeBorder as A, normalizeChild as Ai, li as An, varTag as Ar, article as At, CalendarOutlined as B, withAccess as Bi, object as Bn, setYoyaMode as Br, button as Bt, normalizePoint as C, ComponentNode as Ci, iframe as Cn, thead as Cr, WarningOutlined as Ct, resolveTextValue as D, applyElementOptions$1 as Di, kbd as Dn, track as Dr, abbr as Dt, replaceChildren as E, ViewNode as Ei, ins as En, tr as Er, a as Et, ArrowDownOutlined as F, createAccess as Fi, menu as Fn, configureRequest as Fr, bdi as Ft, ChevronUpOutlined as G, p as Gn, moveByKey as Gr, col as Gt, ChevronDownOutlined as H, optgroup as Hn, announce as Hr, caption as Ht, ArrowLeftOutlined as I, currentAccess as Ii, meta as In, getYoyaMode as Ir, bdo as It, CopyOutlined as J, progress as Jn, enableDevtools as Jr, data as Jt, CloseOutlined as K, picture as Kn, vStateNode as Kr, colgroup as Kt, ArrowRightOutlined as L, installAccess as Li, meter as Ln, getYoyaTheme as Lr, blockquote as Lt, SVG_NAMESPACE as M, registerChildFactories as Mi, main as Mn, wbr as Mr, audio as Mt, SvgElementNode as N, resolveTarget as Ni, map as Nn, Result as Nr, b as Nt, setupButtonSlot as O, createElementFactory as Oi, label as On, u as Or, address as Ot, svg as P, vText as Pi, mark as Pn, RequestBase as Pr, base as Pt, EyeOutlined as Q, ruby as Qn, isDevtoolsEnabled as Qr, details as Qt, ArrowUpOutlined as R, parseAccessSpec as Ri, nav as Rn, initYoyaTheme as Rr, body as Rt, normalizeMessageOptions as S, vClientOnly as Si, i as Sn, th as Sr, UserOutlined as St, removeChild as T, VTextNode as Ti, input as Tn, title as Tr, HtmlElementNode as Tt, ChevronLeftOutlined as U, option as Un, createFocusTrap as Ur, cite as Ut, CheckOutlined as V, ol as Vn, setYoyaTheme as Vr, canvas as Vt, ChevronRightOutlined as W, output as Wn, getFocusableElements as Wr, code as Wt, EditOutlined as X, rp as Xn, getDevtoolsScope as Xr, dd as Xt, DownloadOutlined as Y, q as Yn, getDevtoolsDom as Yr, datalist as Yt, ExternalOutlined as Z, rt as Zn, getDevtoolsSnapshot as Zr, del as Zt, dropdownPlacementStyles as _, bindDocumentEvent as _i, head as _n, tbody as _r, SettingsOutlined as _t, parseState as a, I18n as ai, embed as an, selectedcontent as ar, InfoOutlined as at, normalizeChildren as b, unbindDocumentEvent as bi, hr as bn, textarea as br, TrashOutlined as bt, resolveLocale as c, getI18n as ci, figure as cn, source as cr, MailOutlined as ct, allocateNumber as d, i18nText as di, h1 as dn, style as dr, MonitorOutlined as dt, clearInstalledContext as ei, dialog as en, samp as er, FolderOpenOutlined as et, applyComponentArguments as f, installI18nStringShortcut as fi, h2 as fn, styleTag as fr, MoonOutlined as ft, createComponentFactory as g, withI18nStringShortcut as gi, h6 as gn, table as gr, SearchOutlined as gt, componentClass as h, unregisterI18n as hi, h5 as hn, sup as hr, RefreshOutlined as ht, mount as i, withContext as ii, em as in, select as ir, ImageOutlined as it, themeValue as j, normalizeSetupArguments as ji, link as jn, video as jr, aside as jt, setupContentSlot as k, escapeHtml as ki, legend as kn, ul as kr, area as kt, serializeState as l, getPersistedI18nLocales as li, footer as ln, span as lr, MenuOutlined as lt, applyElementOptions as m, registerI18n as mi, h4 as mn, summary as mr, PlusOutlined as mt, hydrate as n, installContext as ni, dl as nn, search as nr, HeartOutlined as nt, renderPage as o, I18nTextNode as oi, fieldset as on, slot as or, LockOutlined as ot, applyComponentSetup as p, listI18n as pi, h3 as pn, sub as pr, MoreHorizontalOutlined as pt, CodeOutlined as q, pre as qn, disableDevtools as qr, createHtmlFactories as qt, hydrateOrMount as r, snapshotContext as ri, dt as rn, section as rr, HomeOutlined as rt, renderToString as s, createI18n as si, figcaption as sn, small as sr, LogoutOutlined as st, PageDocumentNode as t, currentContext as ti, div as tn, script as tr, FolderOutlined as tt, allocateId as u, i18n as ui, form as un, strong as ur, MinusOutlined as ut, isPlainObject as v, bindWindowEvent as vi, header as vn, td as vr, StarOutlined as vt, placementStyles as w, ElementNode as wi, img as wn, time as wr, icons_exports as wt, normalizeComponentArguments as x, ClientOnlyNode as xi, html as xn, tfoot as xr, UploadOutlined as xt, messageTypeStyles as y, injectDocumentStyle as yi, hgroup as yn, template as yr, SunOutlined as yt, BellOutlined as z, stripAccessCode as zi, noscript as zn, resolveYoyaMode as zr, br as zt };
|