weifuwu 0.38.2 → 0.39.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/dist/client/index.d.ts +3 -1
- package/dist/client/index.js +198 -45
- package/dist/client/locale/en_US.d.ts +15 -0
- package/dist/client/locale/zh_CN.d.ts +15 -0
- package/dist/client/popup.d.ts +19 -0
- package/dist/client/vnode.d.ts +8 -1
- package/dist/components/Chart/Chart.d.ts +18 -0
- package/dist/components/Chart/chart-utils.d.ts +53 -0
- package/dist/components/DatePicker/DatePicker.d.ts +16 -0
- package/dist/components/DatePicker/calendar-utils.d.ts +30 -0
- package/dist/components/Popover/Popover.d.ts +2 -12
- package/dist/components/Tooltip/Tooltip.d.ts +2 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +755 -39
- package/dist/components/style.css +422 -153
- package/dist/layout/weifuwu-layout.css +14 -0
- package/package.json +1 -1
package/dist/client/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* extendCtx → ctx 扩展
|
|
16
16
|
* WfuiContext / AppMiddleware / RouteDef → 类型
|
|
17
17
|
*/
|
|
18
|
-
export { h, jsx, jsxs, jsxDEV, Fragment } from './vnode.ts';
|
|
18
|
+
export { h, jsx, jsxs, jsxDEV, Fragment, Portal, createPortal } from './vnode.ts';
|
|
19
19
|
export type { VNode, VNodeType, Component } from './vnode.ts';
|
|
20
20
|
export { createApp } from './app.ts';
|
|
21
21
|
export { router, RouteView } from './router.ts';
|
|
@@ -33,6 +33,8 @@ export { i18n } from './i18n.ts';
|
|
|
33
33
|
export type { I18nOptions, I18nState } from './i18n.ts';
|
|
34
34
|
export { lockScroll, unlockScroll } from './scroll-lock.ts';
|
|
35
35
|
export { trapFocus } from './focus-trap.ts';
|
|
36
|
+
export { computeFixedPos } from './popup.ts';
|
|
37
|
+
export type { FixedPos, Placement } from './popup.ts';
|
|
36
38
|
export { confirm } from './confirm.ts';
|
|
37
39
|
export type { ConfirmOptions, ConfirmState } from './confirm.ts';
|
|
38
40
|
export { zhCN } from './locale/zh_CN.ts';
|
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/client/vnode.ts
|
|
2
2
|
var Fragment = /* @__PURE__ */ Symbol("Fragment");
|
|
3
|
+
var Portal = /* @__PURE__ */ Symbol("Portal");
|
|
3
4
|
function jsx(type, props, key) {
|
|
4
5
|
return {
|
|
5
6
|
type,
|
|
@@ -27,17 +28,25 @@ function normalizeProps(props) {
|
|
|
27
28
|
}
|
|
28
29
|
return result;
|
|
29
30
|
}
|
|
31
|
+
function createPortal(children, portalKey) {
|
|
32
|
+
return {
|
|
33
|
+
type: Portal,
|
|
34
|
+
props: { children, portalKey },
|
|
35
|
+
key: portalKey ?? void 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
30
38
|
|
|
31
39
|
// src/client/render.ts
|
|
40
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
41
|
+
var SVG_TAGS = /* @__PURE__ */ new Set(["svg", "path", "circle", "line", "rect", "text", "g", "polyline", "polygon", "ellipse", "defs", "use", "clipPath", "mask", "linearGradient", "radialGradient", "stop", "tspan"]);
|
|
42
|
+
var _renderCount = 0;
|
|
32
43
|
var mutationMethods = ["push", "pop", "splice", "shift", "unshift", "sort", "reverse"];
|
|
33
44
|
var wrappedCache = /* @__PURE__ */ new WeakMap();
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (typeof Blob !== "undefined" && val instanceof Blob) return val;
|
|
38
|
-
if (wrappedCache.has(val)) return wrappedCache.get(val);
|
|
39
|
-
const handler = Array.isArray(val) ? {
|
|
45
|
+
function makeProxyHandler(dirty) {
|
|
46
|
+
const isSkip = (k) => typeof k === "string" && k.startsWith("_");
|
|
47
|
+
return {
|
|
40
48
|
get(target, key, receiver) {
|
|
49
|
+
if (isSkip(key)) return Reflect.get(target, key, receiver);
|
|
41
50
|
const v = Reflect.get(target, key, receiver);
|
|
42
51
|
if (typeof key === "string" && mutationMethods.includes(key) && typeof v === "function") {
|
|
43
52
|
return function(...args) {
|
|
@@ -49,37 +58,25 @@ function wrapDeep(val, dirty) {
|
|
|
49
58
|
return wrapDeep(v, dirty);
|
|
50
59
|
},
|
|
51
60
|
set(target, key, v) {
|
|
52
|
-
Reflect.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} : {
|
|
57
|
-
get(_target, key, receiver) {
|
|
58
|
-
const v = Reflect.get(_target, key, receiver);
|
|
59
|
-
return wrapDeep(v, dirty);
|
|
60
|
-
},
|
|
61
|
-
set(target, key, v) {
|
|
62
|
-
Reflect.set(target, key, wrapDeep(v, dirty));
|
|
63
|
-
dirty();
|
|
61
|
+
const old = Reflect.get(target, key);
|
|
62
|
+
if (old === v) return true;
|
|
63
|
+
Reflect.set(target, key, isSkip(key) ? v : wrapDeep(v, dirty));
|
|
64
|
+
if (!isSkip(key)) dirty();
|
|
64
65
|
return true;
|
|
65
66
|
}
|
|
66
67
|
};
|
|
67
|
-
|
|
68
|
+
}
|
|
69
|
+
function wrapDeep(val, dirty) {
|
|
70
|
+
if (val === null || typeof val !== "object") return val;
|
|
71
|
+
if (val instanceof Node) return val;
|
|
72
|
+
if (typeof Blob !== "undefined" && val instanceof Blob) return val;
|
|
73
|
+
if (wrappedCache.has(val)) return wrappedCache.get(val);
|
|
74
|
+
const proxy = new Proxy(val, makeProxyHandler(dirty));
|
|
68
75
|
wrappedCache.set(val, proxy);
|
|
69
76
|
return proxy;
|
|
70
77
|
}
|
|
71
78
|
function createComponentProxy(target, dirty) {
|
|
72
|
-
return new Proxy(target,
|
|
73
|
-
set(t, k, v) {
|
|
74
|
-
if (t[k] === v) return true;
|
|
75
|
-
t[k] = wrapDeep(v, dirty);
|
|
76
|
-
dirty();
|
|
77
|
-
return true;
|
|
78
|
-
},
|
|
79
|
-
get(t, k) {
|
|
80
|
-
return wrapDeep(t[k], dirty);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
79
|
+
return new Proxy(target, makeProxyHandler(dirty));
|
|
83
80
|
}
|
|
84
81
|
function render(input, ctx) {
|
|
85
82
|
return renderValue(input, ctx);
|
|
@@ -89,6 +86,9 @@ function renderValue(v, ctx) {
|
|
|
89
86
|
if (typeof v === "string" || typeof v === "number") return document.createTextNode(String(v));
|
|
90
87
|
if (Array.isArray(v)) return renderArray(v, ctx);
|
|
91
88
|
const vnode = v;
|
|
89
|
+
if (vnode.type === Portal) {
|
|
90
|
+
return renderPortal(vnode, ctx);
|
|
91
|
+
}
|
|
92
92
|
if (vnode.type === Fragment) {
|
|
93
93
|
const frag = document.createDocumentFragment();
|
|
94
94
|
forEach(vnode.props?.children, (child) => frag.appendChild(renderValue(child, ctx)));
|
|
@@ -97,16 +97,27 @@ function renderValue(v, ctx) {
|
|
|
97
97
|
if (typeof vnode.type === "function") {
|
|
98
98
|
return renderComponent(vnode.type, vnode.props, vnode, ctx);
|
|
99
99
|
}
|
|
100
|
-
const
|
|
100
|
+
const tag = vnode.type;
|
|
101
|
+
const el = SVG_TAGS.has(tag) ? document.createElementNS(SVG_NS, tag) : document.createElement(tag);
|
|
101
102
|
vnode.el = el;
|
|
103
|
+
let selectValue;
|
|
102
104
|
for (const [key, value] of Object.entries(vnode.props ?? {})) {
|
|
103
|
-
if (key === "children" || key === "key" || key === "ref") continue;
|
|
105
|
+
if (key === "children" || key === "key" || key === "ref" || key === "value") continue;
|
|
104
106
|
setProp(el, key, value);
|
|
105
107
|
}
|
|
108
|
+
if ("value" in (vnode.props ?? {}) && el instanceof HTMLSelectElement) {
|
|
109
|
+
selectValue = vnode.props.value;
|
|
110
|
+
} else if ("value" in (vnode.props ?? {})) {
|
|
111
|
+
setProp(el, "value", vnode.props.value);
|
|
112
|
+
}
|
|
106
113
|
const flatChildren = flattenChildren(vnode.props?.children);
|
|
107
114
|
for (const child of flatChildren) {
|
|
108
115
|
el.appendChild(renderValue(child, ctx));
|
|
109
116
|
}
|
|
117
|
+
if (selectValue !== void 0) {
|
|
118
|
+
;
|
|
119
|
+
el.value = String(selectValue);
|
|
120
|
+
}
|
|
110
121
|
if (vnode.props?.ref) {
|
|
111
122
|
const result = vnode.props.ref(el);
|
|
112
123
|
if (typeof result === "function") vnode._cleanup = result;
|
|
@@ -119,8 +130,13 @@ function renderComponent(Comp, props, vnode, ctx) {
|
|
|
119
130
|
ctx.ui = ctx.ui ?? {};
|
|
120
131
|
ctx.ui.ready = !!prev$;
|
|
121
132
|
const _target = vnode._$;
|
|
122
|
-
|
|
133
|
+
const _dirtyFn = () => {
|
|
134
|
+
if (_renderCount > 0) return;
|
|
135
|
+
ctx.ui?.dirty?.();
|
|
136
|
+
};
|
|
137
|
+
ctx.ui.$ = createComponentProxy(_target, _dirtyFn);
|
|
123
138
|
let childVNode;
|
|
139
|
+
_renderCount++;
|
|
124
140
|
try {
|
|
125
141
|
childVNode = Comp(props, ctx);
|
|
126
142
|
} catch (e) {
|
|
@@ -132,6 +148,8 @@ function renderComponent(Comp, props, vnode, ctx) {
|
|
|
132
148
|
console.error("Component render error:", e);
|
|
133
149
|
childVNode = null;
|
|
134
150
|
}
|
|
151
|
+
} finally {
|
|
152
|
+
_renderCount--;
|
|
135
153
|
}
|
|
136
154
|
if (childVNode == null) {
|
|
137
155
|
vnode._child = null;
|
|
@@ -145,6 +163,41 @@ function renderArray(arr, ctx) {
|
|
|
145
163
|
for (const item of arr) frag.appendChild(renderValue(item, ctx));
|
|
146
164
|
return frag;
|
|
147
165
|
}
|
|
166
|
+
function ensurePortalContainer() {
|
|
167
|
+
let c = document.getElementById("__wf_portal");
|
|
168
|
+
if (!c) {
|
|
169
|
+
c = document.createElement("div");
|
|
170
|
+
c.id = "__wf_portal";
|
|
171
|
+
c.style.cssText = "position:fixed;inset:0;pointer-events:none;z-index:9999";
|
|
172
|
+
document.body.appendChild(c);
|
|
173
|
+
}
|
|
174
|
+
return c;
|
|
175
|
+
}
|
|
176
|
+
function renderPortal(vnode, ctx) {
|
|
177
|
+
const container = ensurePortalContainer();
|
|
178
|
+
const sub = document.createElement("div");
|
|
179
|
+
sub.style.pointerEvents = "auto";
|
|
180
|
+
container.appendChild(sub);
|
|
181
|
+
vnode._portalEl = sub;
|
|
182
|
+
const children = normalize(vnode.props?.children);
|
|
183
|
+
vnode._child = children;
|
|
184
|
+
for (const child of children) {
|
|
185
|
+
sub.appendChild(renderValue(child, ctx));
|
|
186
|
+
}
|
|
187
|
+
const placeholder = document.createTextNode("");
|
|
188
|
+
vnode.el = placeholder;
|
|
189
|
+
return placeholder;
|
|
190
|
+
}
|
|
191
|
+
function patchPortal(_parent, oldNode, oldV, newV, ctx) {
|
|
192
|
+
const sub = oldV._portalEl;
|
|
193
|
+
newV._portalEl = sub;
|
|
194
|
+
if (!sub) return renderPortal(newV, ctx);
|
|
195
|
+
const newChildren = normalize(newV.props?.children);
|
|
196
|
+
const oldChildren = oldV._child || [];
|
|
197
|
+
newV._child = newChildren;
|
|
198
|
+
patchSimpleChildren(sub, oldChildren, newChildren, ctx);
|
|
199
|
+
return oldNode ?? document.createTextNode("");
|
|
200
|
+
}
|
|
148
201
|
function forEach(children, fn) {
|
|
149
202
|
if (children == null) return;
|
|
150
203
|
if (Array.isArray(children)) {
|
|
@@ -168,12 +221,17 @@ function flattenChildren(children) {
|
|
|
168
221
|
}
|
|
169
222
|
function setProp(el, key, value) {
|
|
170
223
|
if (key === "class" || key === "className") {
|
|
171
|
-
el.
|
|
224
|
+
if (el instanceof SVGElement) el.setAttribute("class", String(value ?? ""));
|
|
225
|
+
else el.className = String(value ?? "");
|
|
172
226
|
} else if (key === "style" && typeof value === "object" && value !== null) {
|
|
173
|
-
|
|
227
|
+
const st = el.style;
|
|
228
|
+
for (const sk of Object.keys(value)) {
|
|
229
|
+
const sv = value[sk];
|
|
230
|
+
if (sv != null) st[sk] = typeof sv === "number" ? sv + "px" : String(sv);
|
|
231
|
+
}
|
|
174
232
|
} else if (key.startsWith("on") && typeof value === "function") {
|
|
175
233
|
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
176
|
-
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
234
|
+
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)) {
|
|
177
235
|
;
|
|
178
236
|
el.value = String(value ?? "");
|
|
179
237
|
} else if (value === true) {
|
|
@@ -224,8 +282,18 @@ function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
|
224
282
|
ctx.ui = ctx.ui ?? {};
|
|
225
283
|
ctx.ui.ready = !!newV._$;
|
|
226
284
|
const _tgt = newV._$;
|
|
227
|
-
|
|
228
|
-
|
|
285
|
+
const _dirtyFn2 = () => {
|
|
286
|
+
if (_renderCount > 0) return;
|
|
287
|
+
ctx.ui?.dirty?.();
|
|
288
|
+
};
|
|
289
|
+
ctx.ui.$ = createComponentProxy(_tgt, _dirtyFn2);
|
|
290
|
+
_renderCount++;
|
|
291
|
+
let childNew;
|
|
292
|
+
try {
|
|
293
|
+
childNew = comp(newV.props, ctx);
|
|
294
|
+
} finally {
|
|
295
|
+
_renderCount--;
|
|
296
|
+
}
|
|
229
297
|
newV._child = childNew;
|
|
230
298
|
return patchValue(parent, oldNode, oldV._child, childNew, ctx);
|
|
231
299
|
}
|
|
@@ -246,6 +314,9 @@ function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
|
246
314
|
}
|
|
247
315
|
return oldNode;
|
|
248
316
|
}
|
|
317
|
+
if (newV.type === Portal) {
|
|
318
|
+
return patchPortal(parent, oldNode, oldV, newV, ctx);
|
|
319
|
+
}
|
|
249
320
|
if (Array.isArray(newInput)) {
|
|
250
321
|
const oldArr = Array.isArray(oldInput) ? oldInput : [];
|
|
251
322
|
patchSimpleChildren(parent, oldArr, newInput, ctx);
|
|
@@ -260,6 +331,7 @@ function typeOf(input) {
|
|
|
260
331
|
const v = input;
|
|
261
332
|
if (typeof v.type === "function") return `fn:${v.type.name || "anon"}`;
|
|
262
333
|
if (v.type === Fragment) return "fragment";
|
|
334
|
+
if (v.type === Portal) return "portal";
|
|
263
335
|
if (typeof v.type === "string") return "tag:" + v.type;
|
|
264
336
|
return "unknown";
|
|
265
337
|
}
|
|
@@ -270,7 +342,7 @@ function patchProps(el, oldProps, newProps) {
|
|
|
270
342
|
if (!newKeys.includes(key)) {
|
|
271
343
|
if (key.startsWith("on") && typeof oldProps[key] === "function") {
|
|
272
344
|
el.removeEventListener(key.slice(2).toLowerCase(), oldProps[key]);
|
|
273
|
-
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
345
|
+
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)) {
|
|
274
346
|
;
|
|
275
347
|
el.value = "";
|
|
276
348
|
} else {
|
|
@@ -283,14 +355,19 @@ function patchProps(el, oldProps, newProps) {
|
|
|
283
355
|
const newVal = newProps?.[key];
|
|
284
356
|
if (newVal !== oldVal) {
|
|
285
357
|
if (key === "class" || key === "className") {
|
|
286
|
-
el.
|
|
358
|
+
if (el instanceof SVGElement) el.setAttribute("class", String(newVal ?? ""));
|
|
359
|
+
else el.className = String(newVal ?? "");
|
|
287
360
|
} else if (key === "style" && typeof newVal === "object") {
|
|
288
|
-
|
|
361
|
+
const st = el.style;
|
|
362
|
+
for (const sk of Object.keys(newVal)) {
|
|
363
|
+
const sv = newVal[sk];
|
|
364
|
+
if (sv != null) st[sk] = typeof sv === "number" ? sv + "px" : String(sv);
|
|
365
|
+
}
|
|
289
366
|
} else if (key.startsWith("on") && typeof newVal === "function") {
|
|
290
367
|
const eventName = key.slice(2).toLowerCase();
|
|
291
368
|
if (typeof oldVal === "function") el.removeEventListener(eventName, oldVal);
|
|
292
369
|
el.addEventListener(eventName, newVal);
|
|
293
|
-
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
370
|
+
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)) {
|
|
294
371
|
;
|
|
295
372
|
el.value = String(newVal ?? "");
|
|
296
373
|
} else if (newVal === true) {
|
|
@@ -298,7 +375,7 @@ function patchProps(el, oldProps, newProps) {
|
|
|
298
375
|
} else if (newVal != null && newVal !== false) {
|
|
299
376
|
el.setAttribute(key, String(newVal));
|
|
300
377
|
} else {
|
|
301
|
-
if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
378
|
+
if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)) {
|
|
302
379
|
;
|
|
303
380
|
el.value = "";
|
|
304
381
|
} else {
|
|
@@ -411,9 +488,25 @@ function runRefCleanup(vnode) {
|
|
|
411
488
|
}
|
|
412
489
|
});
|
|
413
490
|
}
|
|
491
|
+
function cleanupPortalChildren(vnode) {
|
|
492
|
+
const child = vnode._child;
|
|
493
|
+
if (child == null) return;
|
|
494
|
+
if (Array.isArray(child)) {
|
|
495
|
+
for (const c of child) {
|
|
496
|
+
if (c && typeof c === "object") callRefCleanup(c);
|
|
497
|
+
}
|
|
498
|
+
} else if (typeof child === "object") {
|
|
499
|
+
callRefCleanup(child);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
414
502
|
function callRefCleanup(input) {
|
|
415
503
|
if (input == null || typeof input !== "object") return;
|
|
416
504
|
const vnode = input;
|
|
505
|
+
if (vnode._portalEl) {
|
|
506
|
+
cleanupPortalChildren(vnode);
|
|
507
|
+
vnode._portalEl.remove();
|
|
508
|
+
vnode._portalEl = void 0;
|
|
509
|
+
}
|
|
417
510
|
if (vnode._cleanup) runRefCleanup(vnode);
|
|
418
511
|
}
|
|
419
512
|
|
|
@@ -914,7 +1007,22 @@ var zhCN = {
|
|
|
914
1007
|
Switch: { ariaLabel: "\u5207\u6362" },
|
|
915
1008
|
Breadcrumb: { ariaLabel: "\u9762\u5305\u5C51" },
|
|
916
1009
|
Modal: { ariaLabel: "\u5F39\u7A97" },
|
|
917
|
-
Drawer: { ariaLabel: "\u4FA7\u8FB9\u9762\u677F" }
|
|
1010
|
+
Drawer: { ariaLabel: "\u4FA7\u8FB9\u9762\u677F" },
|
|
1011
|
+
DatePicker: {
|
|
1012
|
+
w0: "\u65E5",
|
|
1013
|
+
w1: "\u4E00",
|
|
1014
|
+
w2: "\u4E8C",
|
|
1015
|
+
w3: "\u4E09",
|
|
1016
|
+
w4: "\u56DB",
|
|
1017
|
+
w5: "\u4E94",
|
|
1018
|
+
w6: "\u516D",
|
|
1019
|
+
hour: "\u65F6",
|
|
1020
|
+
minute: "\u5206",
|
|
1021
|
+
time: "\u65F6\u95F4",
|
|
1022
|
+
confirm: "\u786E\u5B9A",
|
|
1023
|
+
cancel: "\u53D6\u6D88",
|
|
1024
|
+
placeholder: "\u9009\u62E9\u65E5\u671F"
|
|
1025
|
+
}
|
|
918
1026
|
}
|
|
919
1027
|
};
|
|
920
1028
|
|
|
@@ -933,7 +1041,22 @@ var enUS = {
|
|
|
933
1041
|
Switch: { ariaLabel: "Toggle" },
|
|
934
1042
|
Breadcrumb: { ariaLabel: "Breadcrumb" },
|
|
935
1043
|
Modal: { ariaLabel: "Dialog" },
|
|
936
|
-
Drawer: { ariaLabel: "Panel" }
|
|
1044
|
+
Drawer: { ariaLabel: "Panel" },
|
|
1045
|
+
DatePicker: {
|
|
1046
|
+
w0: "Su",
|
|
1047
|
+
w1: "Mo",
|
|
1048
|
+
w2: "Tu",
|
|
1049
|
+
w3: "We",
|
|
1050
|
+
w4: "Th",
|
|
1051
|
+
w5: "Fr",
|
|
1052
|
+
w6: "Sa",
|
|
1053
|
+
hour: "Hour",
|
|
1054
|
+
minute: "Min",
|
|
1055
|
+
time: "Time",
|
|
1056
|
+
confirm: "OK",
|
|
1057
|
+
cancel: "Cancel",
|
|
1058
|
+
placeholder: "Pick a date"
|
|
1059
|
+
}
|
|
937
1060
|
}
|
|
938
1061
|
};
|
|
939
1062
|
|
|
@@ -1060,6 +1183,33 @@ function trapFocus(container) {
|
|
|
1060
1183
|
};
|
|
1061
1184
|
}
|
|
1062
1185
|
|
|
1186
|
+
// src/client/popup.ts
|
|
1187
|
+
function computeFixedPos(el, placement = "bottom", gap = 6, center = true) {
|
|
1188
|
+
const rect = el.getBoundingClientRect();
|
|
1189
|
+
switch (placement) {
|
|
1190
|
+
case "bottom":
|
|
1191
|
+
return {
|
|
1192
|
+
top: rect.bottom + gap,
|
|
1193
|
+
left: center ? rect.left + rect.width / 2 : rect.left
|
|
1194
|
+
};
|
|
1195
|
+
case "top":
|
|
1196
|
+
return {
|
|
1197
|
+
top: rect.top - gap,
|
|
1198
|
+
left: center ? rect.left + rect.width / 2 : rect.left
|
|
1199
|
+
};
|
|
1200
|
+
case "left":
|
|
1201
|
+
return {
|
|
1202
|
+
top: center ? rect.top + rect.height / 2 : rect.top,
|
|
1203
|
+
left: rect.left - gap
|
|
1204
|
+
};
|
|
1205
|
+
case "right":
|
|
1206
|
+
return {
|
|
1207
|
+
top: center ? rect.top + rect.height / 2 : rect.top,
|
|
1208
|
+
left: rect.right + gap
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1063
1213
|
// src/client/confirm.ts
|
|
1064
1214
|
function createConfirmModal(message, options) {
|
|
1065
1215
|
return new Promise((resolve) => {
|
|
@@ -1135,11 +1285,14 @@ export {
|
|
|
1135
1285
|
ApiError,
|
|
1136
1286
|
ErrorBoundary,
|
|
1137
1287
|
Fragment,
|
|
1288
|
+
Portal,
|
|
1138
1289
|
RouteView,
|
|
1139
1290
|
api,
|
|
1140
1291
|
auth,
|
|
1292
|
+
computeFixedPos,
|
|
1141
1293
|
confirm,
|
|
1142
1294
|
createApp,
|
|
1295
|
+
createPortal,
|
|
1143
1296
|
enUS,
|
|
1144
1297
|
extendCtx,
|
|
1145
1298
|
h,
|
|
@@ -30,5 +30,20 @@ export declare const enUS: {
|
|
|
30
30
|
Drawer: {
|
|
31
31
|
ariaLabel: string;
|
|
32
32
|
};
|
|
33
|
+
DatePicker: {
|
|
34
|
+
w0: string;
|
|
35
|
+
w1: string;
|
|
36
|
+
w2: string;
|
|
37
|
+
w3: string;
|
|
38
|
+
w4: string;
|
|
39
|
+
w5: string;
|
|
40
|
+
w6: string;
|
|
41
|
+
hour: string;
|
|
42
|
+
minute: string;
|
|
43
|
+
time: string;
|
|
44
|
+
confirm: string;
|
|
45
|
+
cancel: string;
|
|
46
|
+
placeholder: string;
|
|
47
|
+
};
|
|
33
48
|
};
|
|
34
49
|
};
|
|
@@ -30,5 +30,20 @@ export declare const zhCN: {
|
|
|
30
30
|
Drawer: {
|
|
31
31
|
ariaLabel: string;
|
|
32
32
|
};
|
|
33
|
+
DatePicker: {
|
|
34
|
+
w0: string;
|
|
35
|
+
w1: string;
|
|
36
|
+
w2: string;
|
|
37
|
+
w3: string;
|
|
38
|
+
w4: string;
|
|
39
|
+
w5: string;
|
|
40
|
+
w6: string;
|
|
41
|
+
hour: string;
|
|
42
|
+
minute: string;
|
|
43
|
+
time: string;
|
|
44
|
+
confirm: string;
|
|
45
|
+
cancel: string;
|
|
46
|
+
placeholder: string;
|
|
47
|
+
};
|
|
33
48
|
};
|
|
34
49
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/client — 弹出层定位工具
|
|
3
|
+
*
|
|
4
|
+
* 基于 position: fixed 的坐标计算,替代 CSS absolute 定位。
|
|
5
|
+
* 配合 createPortal 使用,让弹出层不受父级 overflow/transform 影响。
|
|
6
|
+
*/
|
|
7
|
+
export interface FixedPos {
|
|
8
|
+
top: number;
|
|
9
|
+
left: number;
|
|
10
|
+
}
|
|
11
|
+
export type Placement = 'top' | 'bottom' | 'left' | 'right';
|
|
12
|
+
/**
|
|
13
|
+
* 根据触发元素和方向计算弹出层的 fixed 坐标
|
|
14
|
+
* @param el 触发元素
|
|
15
|
+
* @param placement 弹出方向
|
|
16
|
+
* @param gap 间距(px),默认 6
|
|
17
|
+
* @param center 是否居中于触发元素,默认 true
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeFixedPos(el: HTMLElement, placement?: Placement, gap?: number, center?: boolean): FixedPos;
|
package/dist/client/vnode.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* --jsxImportSource=weifuwu/client
|
|
8
8
|
*/
|
|
9
9
|
import type { WfuiContext } from './types.ts';
|
|
10
|
-
export type VNodeType = string | Component | typeof Fragment;
|
|
10
|
+
export type VNodeType = string | Component | typeof Fragment | typeof Portal;
|
|
11
11
|
export interface VNode {
|
|
12
12
|
type: VNodeType;
|
|
13
13
|
props: Record<string, any>;
|
|
@@ -18,9 +18,13 @@ export interface VNode {
|
|
|
18
18
|
_child?: any;
|
|
19
19
|
/** ref 回调返回的清理函数,卸载时由框架调用 */
|
|
20
20
|
_cleanup?: (() => void) | undefined;
|
|
21
|
+
/** Portal 子容器 DOM */
|
|
22
|
+
_portalEl?: HTMLDivElement | undefined;
|
|
21
23
|
}
|
|
22
24
|
export type Component<P = {}> = (props: P, ctx: WfuiContext) => VNode | null;
|
|
23
25
|
export declare const Fragment: unique symbol;
|
|
26
|
+
/** Portal — 将子 VNode 渲染到 document.body 下的独立容器 */
|
|
27
|
+
export declare const Portal: unique symbol;
|
|
24
28
|
/** JSX 类型声明 — 使 TypeScript 理解自定义 JSX 运行时 */
|
|
25
29
|
declare global {
|
|
26
30
|
namespace JSX {
|
|
@@ -39,3 +43,6 @@ export declare function h(type: VNodeType, props: Record<string, any> | null, ..
|
|
|
39
43
|
export declare function isNative(vnode: VNode): boolean;
|
|
40
44
|
export declare function isComponent(vnode: VNode): boolean;
|
|
41
45
|
export declare function isFragment(vnode: VNode): boolean;
|
|
46
|
+
export declare function isPortal(vnode: VNode): boolean;
|
|
47
|
+
/** Portal VNode — 子节点渲染到 document.body#__wf_portal 中 */
|
|
48
|
+
export declare function createPortal(children: any, portalKey?: string): VNode;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/components — Chart
|
|
3
|
+
*
|
|
4
|
+
* SVG 图表组件,支持 line / bar / pie / donut 四种模式。
|
|
5
|
+
* 纯函数坐标计算,tooltip 通过 DOM 事件。
|
|
6
|
+
*/
|
|
7
|
+
import type { Component } from '../../client/vnode.ts';
|
|
8
|
+
import type { DataPoint, ChartType, ChartOptions } from './chart-utils.ts';
|
|
9
|
+
export type { DataPoint, ChartType, ChartOptions };
|
|
10
|
+
export interface ChartProps {
|
|
11
|
+
type?: ChartType;
|
|
12
|
+
data: DataPoint[];
|
|
13
|
+
options?: ChartOptions;
|
|
14
|
+
title?: string;
|
|
15
|
+
area?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const Chart: Component<ChartProps>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/components — Chart 纯函数坐标计算
|
|
3
|
+
*
|
|
4
|
+
* 无 DOM 依赖,可测试。
|
|
5
|
+
*/
|
|
6
|
+
export type ChartType = 'line' | 'bar' | 'pie';
|
|
7
|
+
export interface DataPoint {
|
|
8
|
+
label: string;
|
|
9
|
+
value: number;
|
|
10
|
+
color?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ChartOptions {
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
padding?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ScaleLinear {
|
|
18
|
+
(v: number): number;
|
|
19
|
+
invert?(v: number): number;
|
|
20
|
+
domain: [number, number];
|
|
21
|
+
range: [number, number];
|
|
22
|
+
}
|
|
23
|
+
export declare function scaleLinear(domain: [number, number], range: [number, number]): ScaleLinear;
|
|
24
|
+
export declare function linePath(data: DataPoint[], xScale: ScaleLinear, yScale: ScaleLinear): string;
|
|
25
|
+
export declare function areaPath(data: DataPoint[], xScale: ScaleLinear, yScale: ScaleLinear, yBase: number): string;
|
|
26
|
+
export interface BarRect {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
color: string;
|
|
32
|
+
label: string;
|
|
33
|
+
value: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function barRects(data: DataPoint[], xScale: ScaleLinear, yScale: ScaleLinear, barWidth?: number): BarRect[];
|
|
36
|
+
export interface Arc {
|
|
37
|
+
d: string;
|
|
38
|
+
color: string;
|
|
39
|
+
label: string;
|
|
40
|
+
value: number;
|
|
41
|
+
centroid: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export declare function pieArcs(data: DataPoint[], cx: number, cy: number, radius: number): Arc[];
|
|
47
|
+
export declare function getDefaultColor(index: number): string;
|
|
48
|
+
export interface Tick {
|
|
49
|
+
value: number;
|
|
50
|
+
y: number;
|
|
51
|
+
label: string;
|
|
52
|
+
}
|
|
53
|
+
export declare function getYTicks(yScale: ScaleLinear, tickCount?: number): Tick[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/components — DatePicker
|
|
3
|
+
*
|
|
4
|
+
* 四合一日期选择器,支持 mode: date | datetime | time | range
|
|
5
|
+
* 使用 createPortal + position:fixed 定位弹出层。
|
|
6
|
+
*/
|
|
7
|
+
import type { Component } from '../../client/vnode.ts';
|
|
8
|
+
export type DatePickerMode = 'date' | 'datetime' | 'time' | 'range';
|
|
9
|
+
export interface DatePickerProps {
|
|
10
|
+
mode?: DatePickerMode;
|
|
11
|
+
value?: string;
|
|
12
|
+
onChange?: (value: string) => void;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const DatePicker: Component<DatePickerProps>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/components — DatePicker 日期计算工具
|
|
3
|
+
*
|
|
4
|
+
* 纯函数,无 DOM 依赖,可测试。
|
|
5
|
+
*/
|
|
6
|
+
export interface CalendarDay {
|
|
7
|
+
year: number;
|
|
8
|
+
month: number;
|
|
9
|
+
day: number;
|
|
10
|
+
isOtherMonth: boolean;
|
|
11
|
+
isToday: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function getWeekdays(): string[];
|
|
14
|
+
export declare function isToday(year: number, month: number, day: number): boolean;
|
|
15
|
+
export declare function getDaysInMonth(year: number, month: number): number;
|
|
16
|
+
export declare function getFirstDayOfMonth(year: number, month: number): number;
|
|
17
|
+
/** 生成一个月历网格(6 行 × 7 列 = 42 格) */
|
|
18
|
+
export declare function getCalendarGrid(year: number, month: number): CalendarDay[][];
|
|
19
|
+
/** 格式化日期 */
|
|
20
|
+
export declare function formatDate(year: number, month: number, day: number): string;
|
|
21
|
+
export declare function formatTime(hour: number, minute: number): string;
|
|
22
|
+
export declare function formatDateTime(year: number, month: number, day: number, hour: number, minute: number): string;
|
|
23
|
+
/** 小时选项(00-23) */
|
|
24
|
+
export declare function hourOptions(): number[];
|
|
25
|
+
/** 分钟选项(00-59,步长 5) */
|
|
26
|
+
export declare function minuteOptions(): number[];
|
|
27
|
+
/** 日期比较:a 是否在 b 之前 */
|
|
28
|
+
export declare function isBefore(a: Date, b: Date): boolean;
|
|
29
|
+
/** 日期比较:a 是否在 b 之后 */
|
|
30
|
+
export declare function isAfter(a: Date, b: Date): boolean;
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* weifuwu/components — Popover
|
|
3
|
-
*
|
|
4
|
-
* 通用弹出层组件。触发点击后在目标元素附近弹出浮动面板。
|
|
5
|
-
* 点击面板外部关闭。
|
|
6
|
-
*
|
|
7
|
-
* 触发方式:
|
|
8
|
-
* 'click' — 点击触发元素切换显示
|
|
9
|
-
* 'hover' — 悬停触发元素显示,移出关闭
|
|
10
|
-
*
|
|
11
|
-
* 受控模式:
|
|
12
|
-
* 传入 open + onOpenChange 可外部控制显示状态
|
|
13
|
-
* (Dropdown 使用此模式)
|
|
14
3
|
*/
|
|
15
4
|
import type { Component } from '../../client/vnode.ts';
|
|
16
|
-
|
|
5
|
+
import type { Placement } from '../../client/popup.ts';
|
|
6
|
+
export type PopoverPosition = Placement;
|
|
17
7
|
export interface PopoverProps {
|
|
18
8
|
content?: any;
|
|
19
9
|
trigger?: 'click' | 'hover';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Component } from '../../client/vnode.ts';
|
|
2
|
-
|
|
2
|
+
import type { Placement } from '../../client/popup.ts';
|
|
3
|
+
export type TooltipPosition = Placement;
|
|
3
4
|
export interface TooltipProps {
|
|
4
5
|
content: string;
|
|
5
6
|
position?: TooltipPosition;
|