weifuwu 0.51.0 → 0.52.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 +80 -0
- package/dist/client/index.js +183 -120
- package/dist/client/router.d.ts +0 -1
- package/dist/components/style.css +24 -0
- package/dist/layout/weifuwu-layout.css +24 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,6 +66,86 @@ createApp()
|
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
69
|
+
## CDN 快速原型(零构建、纯 HTML)
|
|
70
|
+
|
|
71
|
+
不需要 Node.js 或构建工具,直接在浏览器中用 CDN 使用 weifuwu。创建一个 `.html` 文件即可开始,适合快速原型、Codepen、简单的演示页面。
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!-- cdn-counter.html -->
|
|
75
|
+
<!doctype html>
|
|
76
|
+
<html lang="zh-CN">
|
|
77
|
+
<head>
|
|
78
|
+
<meta charset="UTF-8" />
|
|
79
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
80
|
+
<title>Weifuwu CDN 示例</title>
|
|
81
|
+
|
|
82
|
+
<!-- 组件样式(可选,如只用 weifuwu/client 则不需要) -->
|
|
83
|
+
<link
|
|
84
|
+
rel="stylesheet"
|
|
85
|
+
href="https://unpkg.com/weifuwu@0.51.0/dist/components/style.css"
|
|
86
|
+
/>
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
<div id="root"></div>
|
|
90
|
+
|
|
91
|
+
<!-- Import Map — 将 weifuwu 包名映射到 CDN 地址 -->
|
|
92
|
+
<script type="importmap">
|
|
93
|
+
{
|
|
94
|
+
"imports": {
|
|
95
|
+
"weifuwu/client": "https://unpkg.com/weifuwu@0.51.0/dist/client/index.js",
|
|
96
|
+
"weifuwu/components": "https://unpkg.com/weifuwu@0.51.0/dist/components/index.js"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<script type="module">
|
|
102
|
+
import { createApp, h } from 'weifuwu/client'
|
|
103
|
+
import { Card, Button, Badge } from 'weifuwu/components'
|
|
104
|
+
|
|
105
|
+
// 组件 = (initProps, ctx) => (props) => VNode
|
|
106
|
+
const Counter = (_props, ctx) => {
|
|
107
|
+
const $ = ctx.ui.$()
|
|
108
|
+
$.count = 0 // mount 初始化
|
|
109
|
+
|
|
110
|
+
return () =>
|
|
111
|
+
h(Card, { variant: 'default', padding: 'lg' },
|
|
112
|
+
h('h2', { style: { textAlign: 'center', margin: 0 } }, '⚡ Weifuwu'),
|
|
113
|
+
h('div', { style: { fontSize: '4rem', fontWeight: 600, textAlign: 'center' } },
|
|
114
|
+
String($.count)),
|
|
115
|
+
h('div', { style: { textAlign: 'center', marginTop: '1rem' } },
|
|
116
|
+
h(Badge, {
|
|
117
|
+
variant: $.count % 2 === 0 ? 'success' : 'warning'
|
|
118
|
+
}, $.count % 2 === 0 ? '偶数' : '奇数')),
|
|
119
|
+
h('hr', { style: { margin: '1rem 0', border: 'none', borderTop: '1px solid #eee' } }),
|
|
120
|
+
h('div', { style: { display: 'flex', gap: '0.5rem', justifyContent: 'center' } },
|
|
121
|
+
h(Button, { variant: 'secondary', onClick: () => $.count-- }, '➖ 减 1'),
|
|
122
|
+
h(Button, { variant: 'danger', onClick: () => $.count = 0 }, '↺ 重置'),
|
|
123
|
+
h(Button, { variant: 'primary', onClick: () => $.count++ }, '➕ 加 1'),
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
createApp().mount('#root', Counter)
|
|
129
|
+
</script>
|
|
130
|
+
</body>
|
|
131
|
+
</html>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
将此 HTML 保存到本地用浏览器打开即可运行。完整的 CDN 示例见 [`apps/html/test.html`](./apps/html/test.html)。
|
|
135
|
+
|
|
136
|
+
### CDN 资源地址说明
|
|
137
|
+
|
|
138
|
+
| 资源 | CDN 地址 | 说明 |
|
|
139
|
+
|------|---------|------|
|
|
140
|
+
| `weifuwu/client` | `https://unpkg.com/weifuwu@0.51.0/dist/client/index.js` | 客户端核心(createApp, h, 路由, 状态管理等) |
|
|
141
|
+
| `weifuwu/components` | `https://unpkg.com/weifuwu@0.51.0/dist/components/index.js` | 41 个 UI 组件(Button, Card, Table, Modal 等) |
|
|
142
|
+
| 组件样式 | `https://unpkg.com/weifuwu@0.51.0/dist/components/style.css` | 组件 CSS + 72 个主题 Token + 35 个布局原语 |
|
|
143
|
+
| 独立布局系统 | `https://unpkg.com/weifuwu@0.51.0/dist/layout/weifuwu-layout.css` | 仅 CSS 布局,不依赖 JS |
|
|
144
|
+
|
|
145
|
+
> 提示:将 `@0.51.0` 替换为 `@latest` 始终使用最新版,或固定版本避免意外变更。
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
69
149
|
## 模块总览
|
|
70
150
|
|
|
71
151
|
| 导入路径 | 模块 | 用途 | 依赖 |
|
package/dist/client/index.js
CHANGED
|
@@ -132,7 +132,12 @@ function renderComponent(Comp, props, vnode, ctx) {
|
|
|
132
132
|
return document.createTextNode("");
|
|
133
133
|
}
|
|
134
134
|
vnode._child = childVNode;
|
|
135
|
-
|
|
135
|
+
const domNode = renderValue(childVNode, childCtx);
|
|
136
|
+
if (!vnode._refNode) {
|
|
137
|
+
;
|
|
138
|
+
vnode._refNode = domNode;
|
|
139
|
+
}
|
|
140
|
+
return domNode;
|
|
136
141
|
}
|
|
137
142
|
function renderArray(arr, ctx) {
|
|
138
143
|
const frag = document.createDocumentFragment();
|
|
@@ -288,7 +293,7 @@ function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
|
288
293
|
}
|
|
289
294
|
const _prevChild = oldV._child;
|
|
290
295
|
newV._child = childNew;
|
|
291
|
-
return patchValue(parent, oldNode, _prevChild, childNew,
|
|
296
|
+
return patchValue(parent, oldNode, _prevChild, childNew, childCtx);
|
|
292
297
|
}
|
|
293
298
|
if (newV.type === Fragment) {
|
|
294
299
|
patchChildren(parent, oldV, newV, ctx);
|
|
@@ -521,123 +526,6 @@ function callRefCleanup(input) {
|
|
|
521
526
|
}
|
|
522
527
|
}
|
|
523
528
|
|
|
524
|
-
// src/client/router.ts
|
|
525
|
-
function flattenRoutes(routes, basePath = "", chain = []) {
|
|
526
|
-
const result = [];
|
|
527
|
-
for (const route of routes) {
|
|
528
|
-
const fullPath = joinPaths(basePath, route.path);
|
|
529
|
-
const { re, keys } = compilePath(fullPath);
|
|
530
|
-
const fullChain = [...chain, route];
|
|
531
|
-
result.push({ re, keys, def: route, chain: fullChain });
|
|
532
|
-
if (route.children) {
|
|
533
|
-
result.push(...flattenRoutes(route.children, fullPath, fullChain));
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
return result;
|
|
537
|
-
}
|
|
538
|
-
var layoutDepth = /* @__PURE__ */ new WeakMap();
|
|
539
|
-
function joinPaths(a, b) {
|
|
540
|
-
if (!b || b === "/") return a || "/";
|
|
541
|
-
const left = a.endsWith("/") ? a.slice(0, -1) : a;
|
|
542
|
-
const right = b.startsWith("/") ? b : "/" + b;
|
|
543
|
-
return left + right;
|
|
544
|
-
}
|
|
545
|
-
function compilePath(path) {
|
|
546
|
-
const keys = [];
|
|
547
|
-
const reStr = path.replace(/:(\w+)/g, (_, key) => {
|
|
548
|
-
keys.push(key);
|
|
549
|
-
return "([^/]+)";
|
|
550
|
-
}).replace(/\*/g, ".*");
|
|
551
|
-
return { re: new RegExp(`^${reStr}$`), keys };
|
|
552
|
-
}
|
|
553
|
-
function matchRoute(path, routes) {
|
|
554
|
-
let best = null;
|
|
555
|
-
for (const fr of routes) {
|
|
556
|
-
if (path.match(fr.re)) {
|
|
557
|
-
if (!best || fr.chain.length > best.chain.length) best = fr;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
return best;
|
|
561
|
-
}
|
|
562
|
-
function router(opts) {
|
|
563
|
-
const flatRoutes = flattenRoutes(opts.routes);
|
|
564
|
-
const mode = opts.mode || "history";
|
|
565
|
-
function getPath() {
|
|
566
|
-
if (mode === "hash") {
|
|
567
|
-
const hash = window.location.hash.replace(/^#/, "") || "/";
|
|
568
|
-
return hash;
|
|
569
|
-
}
|
|
570
|
-
return window.location.pathname;
|
|
571
|
-
}
|
|
572
|
-
function resolve(path) {
|
|
573
|
-
const match = matchRoute(path, flatRoutes);
|
|
574
|
-
if (match) {
|
|
575
|
-
const params = {};
|
|
576
|
-
const m = path.match(match.re);
|
|
577
|
-
if (m) {
|
|
578
|
-
for (let i = 0; i < match.keys.length; i++) {
|
|
579
|
-
params[match.keys[i]] = decodeURIComponent(m[i + 1]);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
const last = match.chain[match.chain.length - 1];
|
|
583
|
-
return {
|
|
584
|
-
path: match.def.path,
|
|
585
|
-
params,
|
|
586
|
-
query: Object.fromEntries(new URLSearchParams(window.location.search)),
|
|
587
|
-
chain: match.chain,
|
|
588
|
-
title: last?.title ?? ""
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
return { path, params: {}, query: {}, chain: [], title: "" };
|
|
592
|
-
}
|
|
593
|
-
return (ctx) => {
|
|
594
|
-
const resolved = resolve(getPath());
|
|
595
|
-
ctx.route = resolved;
|
|
596
|
-
if (resolved.title) document.title = resolved.title;
|
|
597
|
-
if (!ctx.app) ctx.app = {};
|
|
598
|
-
ctx.app.navigate = (path) => {
|
|
599
|
-
if (mode === "hash") {
|
|
600
|
-
window.location.hash = "#" + path;
|
|
601
|
-
const resolved2 = resolve(path);
|
|
602
|
-
ctx.route = resolved2;
|
|
603
|
-
} else {
|
|
604
|
-
window.history.pushState({}, "", path);
|
|
605
|
-
const resolved2 = resolve(path);
|
|
606
|
-
ctx.route = resolved2;
|
|
607
|
-
}
|
|
608
|
-
if (ctx.route?.title) document.title = ctx.route.title;
|
|
609
|
-
ctx.ui?.render();
|
|
610
|
-
};
|
|
611
|
-
const onPop = () => {
|
|
612
|
-
const resolved2 = resolve(getPath());
|
|
613
|
-
ctx.route = resolved2;
|
|
614
|
-
if (ctx.route?.title) document.title = ctx.route.title;
|
|
615
|
-
ctx.ui?.render();
|
|
616
|
-
};
|
|
617
|
-
window.addEventListener("popstate", onPop);
|
|
618
|
-
if (mode === "hash") {
|
|
619
|
-
window.addEventListener("hashchange", onPop);
|
|
620
|
-
}
|
|
621
|
-
return ctx;
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
function RouteView(_props, ctx) {
|
|
625
|
-
return () => {
|
|
626
|
-
const route = ctx.route;
|
|
627
|
-
if (!route?.chain?.length) return null;
|
|
628
|
-
const ctxAny = ctx;
|
|
629
|
-
const depth = layoutDepth.get(ctxAny) ?? 0;
|
|
630
|
-
if (depth >= route.chain.length) return null;
|
|
631
|
-
const def = route.chain[depth];
|
|
632
|
-
const Comp = def.layout ?? def.component;
|
|
633
|
-
if (!Comp) return null;
|
|
634
|
-
if (def.layout) {
|
|
635
|
-
layoutDepth.set(ctxAny, depth + 1);
|
|
636
|
-
}
|
|
637
|
-
return { type: Comp, props: {}, key: void 0 };
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
|
|
641
529
|
// src/client/app.ts
|
|
642
530
|
function createApp() {
|
|
643
531
|
const middlewares = [];
|
|
@@ -648,16 +536,20 @@ function createApp() {
|
|
|
648
536
|
let _rendering = false;
|
|
649
537
|
let _dirtyBatch = /* @__PURE__ */ new Set();
|
|
650
538
|
let _dirtyScheduled = false;
|
|
539
|
+
const _mediaRegistry = /* @__PURE__ */ new Map();
|
|
651
540
|
function renderByIds(ids) {
|
|
652
541
|
if (_rendering) return;
|
|
653
542
|
_rendering = true;
|
|
654
|
-
layoutDepth.delete(ctx);
|
|
655
543
|
for (const id of ids) {
|
|
656
544
|
const vnode = idRegistry.get(id);
|
|
657
545
|
if (!vnode || !vnode._render) continue;
|
|
658
546
|
const oldChild = vnode._child;
|
|
659
547
|
const newChild = vnode._render(vnode.props);
|
|
660
548
|
vnode._child = newChild;
|
|
549
|
+
if (!vnode._parentNode && vnode._refNode) {
|
|
550
|
+
;
|
|
551
|
+
vnode._parentNode = vnode._refNode.parentNode;
|
|
552
|
+
}
|
|
661
553
|
if (vnode._parentNode) {
|
|
662
554
|
const newNode = patchValue(
|
|
663
555
|
vnode._parentNode,
|
|
@@ -744,6 +636,63 @@ function createApp() {
|
|
|
744
636
|
if (selfId) ctx.ui.dirty([selfId]);
|
|
745
637
|
});
|
|
746
638
|
},
|
|
639
|
+
/**
|
|
640
|
+
* 响应式媒体查询:注册监听,值变化时自动 dirty
|
|
641
|
+
*
|
|
642
|
+
* 用法:
|
|
643
|
+
* const $ = ctx.ui.$()
|
|
644
|
+
* ctx.ui.useMedia('(max-width: 640px)', (v) => { $.isMobile = v })
|
|
645
|
+
*
|
|
646
|
+
* callback 会立即执行一次(取当前值),之后在变化时再次执行
|
|
647
|
+
*/
|
|
648
|
+
useMedia: function(query, callback) {
|
|
649
|
+
const selfId = getSelfId(this);
|
|
650
|
+
const key = `media:${selfId}:${query}`;
|
|
651
|
+
if (!_mediaRegistry.has(key)) {
|
|
652
|
+
const mql = window.matchMedia(query);
|
|
653
|
+
callback(mql.matches);
|
|
654
|
+
const handler = (e) => callback(e.matches);
|
|
655
|
+
mql.addEventListener("change", handler);
|
|
656
|
+
_mediaRegistry.set(key, { mql, handler });
|
|
657
|
+
}
|
|
658
|
+
},
|
|
659
|
+
/**
|
|
660
|
+
* 响应式断点:注册命名断点监听,值变化时自动 dirty
|
|
661
|
+
*
|
|
662
|
+
* 用法:
|
|
663
|
+
* const $ = ctx.ui.$()
|
|
664
|
+
* ctx.ui.useBreakpoint((vp) => { $.vp = vp })
|
|
665
|
+
* // vp: 'mobile' | 'tablet' | 'desktop'
|
|
666
|
+
*
|
|
667
|
+
* 自定义断点:
|
|
668
|
+
* ctx.ui.useBreakpoint(
|
|
669
|
+
* { narrow: '(max-width: 480px)', wide: '(min-width: 1200px)' },
|
|
670
|
+
* (vp) => { $.size = vp }
|
|
671
|
+
* )
|
|
672
|
+
*/
|
|
673
|
+
useBreakpoint: function(bpsOrCallback, callback) {
|
|
674
|
+
const bps = typeof bpsOrCallback === "function" ? { mobile: "(max-width: 639px)", tablet: "(min-width: 640px) and (max-width: 1023px)", desktop: "(min-width: 1024px)" } : bpsOrCallback;
|
|
675
|
+
const cb = typeof bpsOrCallback === "function" ? bpsOrCallback : callback;
|
|
676
|
+
const selfId = getSelfId(this);
|
|
677
|
+
const key = `bp:${selfId}`;
|
|
678
|
+
function evaluate() {
|
|
679
|
+
for (const [name, query] of Object.entries(bps)) {
|
|
680
|
+
if (window.matchMedia(query).matches) return name;
|
|
681
|
+
}
|
|
682
|
+
return Object.keys(bps)[0] ?? "";
|
|
683
|
+
}
|
|
684
|
+
if (!_mediaRegistry.has(key)) {
|
|
685
|
+
cb(evaluate());
|
|
686
|
+
const handlers = [];
|
|
687
|
+
for (const query of Object.values(bps)) {
|
|
688
|
+
const mql = window.matchMedia(query);
|
|
689
|
+
const handler = () => cb(evaluate());
|
|
690
|
+
mql.addEventListener("change", handler);
|
|
691
|
+
handlers.push(() => mql.removeEventListener("change", handler));
|
|
692
|
+
}
|
|
693
|
+
_mediaRegistry.set(key, { mql: null, handler: null });
|
|
694
|
+
}
|
|
695
|
+
},
|
|
747
696
|
/** 注册组件实例的自定义 ID(用于跨组件精准刷新) */
|
|
748
697
|
selfId: function(name) {
|
|
749
698
|
if (typeof name !== "string" || !name) {
|
|
@@ -815,6 +764,120 @@ function createReactiveState(dirty) {
|
|
|
815
764
|
return reactive({});
|
|
816
765
|
}
|
|
817
766
|
|
|
767
|
+
// src/client/router.ts
|
|
768
|
+
function flattenRoutes(routes, basePath = "", chain = []) {
|
|
769
|
+
const result = [];
|
|
770
|
+
for (const route of routes) {
|
|
771
|
+
const fullPath = joinPaths(basePath, route.path);
|
|
772
|
+
const { re, keys } = compilePath(fullPath);
|
|
773
|
+
const fullChain = [...chain, route];
|
|
774
|
+
result.push({ re, keys, def: route, chain: fullChain });
|
|
775
|
+
if (route.children) {
|
|
776
|
+
result.push(...flattenRoutes(route.children, fullPath, fullChain));
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
function joinPaths(a, b) {
|
|
782
|
+
if (!b || b === "/") return a || "/";
|
|
783
|
+
const left = a.endsWith("/") ? a.slice(0, -1) : a;
|
|
784
|
+
const right = b.startsWith("/") ? b : "/" + b;
|
|
785
|
+
return left + right;
|
|
786
|
+
}
|
|
787
|
+
function compilePath(path) {
|
|
788
|
+
const keys = [];
|
|
789
|
+
const reStr = path.replace(/:(\w+)/g, (_, key) => {
|
|
790
|
+
keys.push(key);
|
|
791
|
+
return "([^/]+)";
|
|
792
|
+
}).replace(/\*/g, ".*");
|
|
793
|
+
return { re: new RegExp(`^${reStr}$`), keys };
|
|
794
|
+
}
|
|
795
|
+
function matchRoute(path, routes) {
|
|
796
|
+
let best = null;
|
|
797
|
+
for (const fr of routes) {
|
|
798
|
+
if (path.match(fr.re)) {
|
|
799
|
+
if (!best || fr.chain.length > best.chain.length) best = fr;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return best;
|
|
803
|
+
}
|
|
804
|
+
function router(opts) {
|
|
805
|
+
const flatRoutes = flattenRoutes(opts.routes);
|
|
806
|
+
const mode = opts.mode || "history";
|
|
807
|
+
function getPath() {
|
|
808
|
+
if (mode === "hash") {
|
|
809
|
+
const hash = window.location.hash.replace(/^#/, "") || "/";
|
|
810
|
+
return hash;
|
|
811
|
+
}
|
|
812
|
+
return window.location.pathname;
|
|
813
|
+
}
|
|
814
|
+
function resolve(path) {
|
|
815
|
+
const match = matchRoute(path, flatRoutes);
|
|
816
|
+
if (match) {
|
|
817
|
+
const params = {};
|
|
818
|
+
const m = path.match(match.re);
|
|
819
|
+
if (m) {
|
|
820
|
+
for (let i = 0; i < match.keys.length; i++) {
|
|
821
|
+
params[match.keys[i]] = decodeURIComponent(m[i + 1]);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
const last = match.chain[match.chain.length - 1];
|
|
825
|
+
return {
|
|
826
|
+
path: match.def.path,
|
|
827
|
+
params,
|
|
828
|
+
query: Object.fromEntries(new URLSearchParams(window.location.search)),
|
|
829
|
+
chain: match.chain,
|
|
830
|
+
title: last?.title ?? ""
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
return { path, params: {}, query: {}, chain: [], title: "" };
|
|
834
|
+
}
|
|
835
|
+
return (ctx) => {
|
|
836
|
+
const resolved = resolve(getPath());
|
|
837
|
+
ctx.route = resolved;
|
|
838
|
+
if (resolved.title) document.title = resolved.title;
|
|
839
|
+
if (!ctx.app) ctx.app = {};
|
|
840
|
+
ctx.app.navigate = (path) => {
|
|
841
|
+
if (mode === "hash") {
|
|
842
|
+
window.location.hash = "#" + path;
|
|
843
|
+
const resolved2 = resolve(path);
|
|
844
|
+
ctx.route = resolved2;
|
|
845
|
+
} else {
|
|
846
|
+
window.history.pushState({}, "", path);
|
|
847
|
+
const resolved2 = resolve(path);
|
|
848
|
+
ctx.route = resolved2;
|
|
849
|
+
}
|
|
850
|
+
if (ctx.route?.title) document.title = ctx.route.title;
|
|
851
|
+
ctx.ui?.render();
|
|
852
|
+
};
|
|
853
|
+
const onPop = () => {
|
|
854
|
+
const resolved2 = resolve(getPath());
|
|
855
|
+
ctx.route = resolved2;
|
|
856
|
+
if (ctx.route?.title) document.title = ctx.route.title;
|
|
857
|
+
ctx.ui?.render();
|
|
858
|
+
};
|
|
859
|
+
window.addEventListener("popstate", onPop);
|
|
860
|
+
if (mode === "hash") {
|
|
861
|
+
window.addEventListener("hashchange", onPop);
|
|
862
|
+
}
|
|
863
|
+
return ctx;
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
function RouteView(_props, ctx) {
|
|
867
|
+
const _depth = ctx.route?._rvDepth ?? 0;
|
|
868
|
+
return () => {
|
|
869
|
+
const route = ctx.route;
|
|
870
|
+
if (!route?.chain?.length || _depth >= route.chain.length) return null;
|
|
871
|
+
const def = route.chain[_depth];
|
|
872
|
+
const Comp = def.layout ?? def.component;
|
|
873
|
+
if (!Comp) return null;
|
|
874
|
+
if (def.layout) {
|
|
875
|
+
route._rvDepth = _depth + 1;
|
|
876
|
+
}
|
|
877
|
+
return { type: Comp, props: {}, key: void 0 };
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
818
881
|
// src/client/types.ts
|
|
819
882
|
function extendCtx(ctx, fields) {
|
|
820
883
|
return Object.assign(Object.create(ctx), fields);
|
package/dist/client/router.d.ts
CHANGED
|
@@ -9,6 +9,5 @@ export interface RouterOptions {
|
|
|
9
9
|
routes: RouteDef[];
|
|
10
10
|
notFound?: (props: any, ctx: WfuiContext) => any;
|
|
11
11
|
}
|
|
12
|
-
export declare const layoutDepth: WeakMap<any, number>;
|
|
13
12
|
export declare function router(opts: RouterOptions): AppMiddleware;
|
|
14
13
|
export declare function RouteView(_props: {}, ctx: WfuiContext): () => any;
|
|
@@ -110,6 +110,12 @@
|
|
|
110
110
|
--wf-accent-color: var(--wf-color-primary);
|
|
111
111
|
--wf-caret-color: var(--wf-color-primary);
|
|
112
112
|
|
|
113
|
+
/* ── 断点 ── */
|
|
114
|
+
--wf-bp-sm: 640px;
|
|
115
|
+
--wf-bp-md: 768px;
|
|
116
|
+
--wf-bp-lg: 1024px;
|
|
117
|
+
--wf-bp-xl: 1280px;
|
|
118
|
+
|
|
113
119
|
/* ── 透明度 ── */
|
|
114
120
|
--wf-opacity-disabled: 0.5;
|
|
115
121
|
--wf-opacity-overlay: 0.4;
|
|
@@ -314,6 +320,9 @@ code {
|
|
|
314
320
|
flex-direction: column;
|
|
315
321
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
316
322
|
}
|
|
323
|
+
@media (min-width: 640px) { .wf-stack\@sm { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
324
|
+
@media (min-width: 768px) { .wf-stack\@md { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
325
|
+
@media (min-width: 1024px) { .wf-stack\@lg { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
317
326
|
|
|
318
327
|
/* stack-reverse — 纵向反向堆叠 */
|
|
319
328
|
.wf-stack-reverse {
|
|
@@ -321,6 +330,9 @@ code {
|
|
|
321
330
|
flex-direction: column-reverse;
|
|
322
331
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
323
332
|
}
|
|
333
|
+
@media (min-width: 640px) { .wf-stack-reverse\@sm { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
334
|
+
@media (min-width: 768px) { .wf-stack-reverse\@md { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
335
|
+
@media (min-width: 1024px) { .wf-stack-reverse\@lg { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
324
336
|
|
|
325
337
|
/* row — 横向排列 */
|
|
326
338
|
.wf-row {
|
|
@@ -329,6 +341,9 @@ code {
|
|
|
329
341
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
330
342
|
align-items: var(--wf-align, center);
|
|
331
343
|
}
|
|
344
|
+
@media (min-width: 640px) { .wf-row\@sm { flex-direction: row; flex-wrap: wrap; } }
|
|
345
|
+
@media (min-width: 768px) { .wf-row\@md { flex-direction: row; flex-wrap: wrap; } }
|
|
346
|
+
@media (min-width: 1024px) { .wf-row\@lg { flex-direction: row; flex-wrap: wrap; } }
|
|
332
347
|
|
|
333
348
|
/* row-reverse — 横向反向排列 */
|
|
334
349
|
.wf-row-reverse {
|
|
@@ -338,6 +353,9 @@ code {
|
|
|
338
353
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
339
354
|
align-items: var(--wf-align, center);
|
|
340
355
|
}
|
|
356
|
+
@media (min-width: 640px) { .wf-row-reverse\@sm { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
357
|
+
@media (min-width: 768px) { .wf-row-reverse\@md { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
358
|
+
@media (min-width: 1024px) { .wf-row-reverse\@lg { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
341
359
|
|
|
342
360
|
/* split — 两端展开 */
|
|
343
361
|
.wf-split {
|
|
@@ -486,11 +504,17 @@ code {
|
|
|
486
504
|
.wf-hidden {
|
|
487
505
|
display: none;
|
|
488
506
|
}
|
|
507
|
+
@media (min-width: 640px) { .wf-hidden\@sm { display: none; } }
|
|
508
|
+
@media (min-width: 768px) { .wf-hidden\@md { display: none; } }
|
|
509
|
+
@media (min-width: 1024px) { .wf-hidden\@lg { display: none; } }
|
|
489
510
|
|
|
490
511
|
/* block — 块级显示 */
|
|
491
512
|
.wf-block {
|
|
492
513
|
display: block;
|
|
493
514
|
}
|
|
515
|
+
@media (min-width: 640px) { .wf-block\@sm { display: block; } }
|
|
516
|
+
@media (min-width: 768px) { .wf-block\@md { display: block; } }
|
|
517
|
+
@media (min-width: 1024px) { .wf-block\@lg { display: block; } }
|
|
494
518
|
|
|
495
519
|
/* inline — 行内显示 */
|
|
496
520
|
.wf-inline {
|
|
@@ -110,6 +110,12 @@
|
|
|
110
110
|
--wf-accent-color: var(--wf-color-primary);
|
|
111
111
|
--wf-caret-color: var(--wf-color-primary);
|
|
112
112
|
|
|
113
|
+
/* ── 断点 ── */
|
|
114
|
+
--wf-bp-sm: 640px;
|
|
115
|
+
--wf-bp-md: 768px;
|
|
116
|
+
--wf-bp-lg: 1024px;
|
|
117
|
+
--wf-bp-xl: 1280px;
|
|
118
|
+
|
|
113
119
|
/* ── 透明度 ── */
|
|
114
120
|
--wf-opacity-disabled: 0.5;
|
|
115
121
|
--wf-opacity-overlay: 0.4;
|
|
@@ -314,6 +320,9 @@ code {
|
|
|
314
320
|
flex-direction: column;
|
|
315
321
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
316
322
|
}
|
|
323
|
+
@media (min-width: 640px) { .wf-stack\@sm { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
324
|
+
@media (min-width: 768px) { .wf-stack\@md { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
325
|
+
@media (min-width: 1024px) { .wf-stack\@lg { flex-direction: row; align-items: var(--wf-align, center); } }
|
|
317
326
|
|
|
318
327
|
/* stack-reverse — 纵向反向堆叠 */
|
|
319
328
|
.wf-stack-reverse {
|
|
@@ -321,6 +330,9 @@ code {
|
|
|
321
330
|
flex-direction: column-reverse;
|
|
322
331
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
323
332
|
}
|
|
333
|
+
@media (min-width: 640px) { .wf-stack-reverse\@sm { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
334
|
+
@media (min-width: 768px) { .wf-stack-reverse\@md { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
335
|
+
@media (min-width: 1024px) { .wf-stack-reverse\@lg { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
|
|
324
336
|
|
|
325
337
|
/* row — 横向排列 */
|
|
326
338
|
.wf-row {
|
|
@@ -329,6 +341,9 @@ code {
|
|
|
329
341
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
330
342
|
align-items: var(--wf-align, center);
|
|
331
343
|
}
|
|
344
|
+
@media (min-width: 640px) { .wf-row\@sm { flex-direction: row; flex-wrap: wrap; } }
|
|
345
|
+
@media (min-width: 768px) { .wf-row\@md { flex-direction: row; flex-wrap: wrap; } }
|
|
346
|
+
@media (min-width: 1024px) { .wf-row\@lg { flex-direction: row; flex-wrap: wrap; } }
|
|
332
347
|
|
|
333
348
|
/* row-reverse — 横向反向排列 */
|
|
334
349
|
.wf-row-reverse {
|
|
@@ -338,6 +353,9 @@ code {
|
|
|
338
353
|
gap: var(--wf-gap, var(--wf-gap-md));
|
|
339
354
|
align-items: var(--wf-align, center);
|
|
340
355
|
}
|
|
356
|
+
@media (min-width: 640px) { .wf-row-reverse\@sm { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
357
|
+
@media (min-width: 768px) { .wf-row-reverse\@md { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
358
|
+
@media (min-width: 1024px) { .wf-row-reverse\@lg { flex-direction: row-reverse; flex-wrap: wrap; } }
|
|
341
359
|
|
|
342
360
|
/* split — 两端展开 */
|
|
343
361
|
.wf-split {
|
|
@@ -486,11 +504,17 @@ code {
|
|
|
486
504
|
.wf-hidden {
|
|
487
505
|
display: none;
|
|
488
506
|
}
|
|
507
|
+
@media (min-width: 640px) { .wf-hidden\@sm { display: none; } }
|
|
508
|
+
@media (min-width: 768px) { .wf-hidden\@md { display: none; } }
|
|
509
|
+
@media (min-width: 1024px) { .wf-hidden\@lg { display: none; } }
|
|
489
510
|
|
|
490
511
|
/* block — 块级显示 */
|
|
491
512
|
.wf-block {
|
|
492
513
|
display: block;
|
|
493
514
|
}
|
|
515
|
+
@media (min-width: 640px) { .wf-block\@sm { display: block; } }
|
|
516
|
+
@media (min-width: 768px) { .wf-block\@md { display: block; } }
|
|
517
|
+
@media (min-width: 1024px) { .wf-block\@lg { display: block; } }
|
|
494
518
|
|
|
495
519
|
/* inline — 行内显示 */
|
|
496
520
|
.wf-inline {
|