weifuwu 0.36.2 → 0.38.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.
Files changed (51) hide show
  1. package/README.md +141 -3
  2. package/dist/client/app.d.ts +4 -7
  3. package/dist/client/confirm.d.ts +26 -0
  4. package/dist/client/focus-trap.d.ts +4 -0
  5. package/dist/client/i18n.d.ts +27 -0
  6. package/dist/client/index.d.ts +8 -0
  7. package/dist/client/index.js +322 -60
  8. package/dist/client/locale/en_US.d.ts +34 -0
  9. package/dist/client/locale/zh_CN.d.ts +34 -0
  10. package/dist/client/scroll-lock.d.ts +5 -0
  11. package/dist/components/Accordion/Accordion.d.ts +11 -0
  12. package/dist/components/Alert/Alert.d.ts +9 -0
  13. package/dist/components/Avatar/Avatar.d.ts +7 -0
  14. package/dist/components/Badge/Badge.d.ts +8 -0
  15. package/dist/components/Breadcrumb/Breadcrumb.d.ts +9 -0
  16. package/dist/components/Button/Button.d.ts +12 -0
  17. package/dist/components/Card/Card.d.ts +9 -0
  18. package/dist/components/Checkbox/Checkbox.d.ts +8 -0
  19. package/dist/components/Divider/Divider.d.ts +6 -0
  20. package/dist/components/Drawer/Drawer.d.ts +14 -0
  21. package/dist/components/Dropdown/Dropdown.d.ts +14 -0
  22. package/dist/components/EmptyState/EmptyState.d.ts +8 -0
  23. package/dist/components/Field/Field.d.ts +9 -0
  24. package/dist/components/FileUpload/FileUpload.d.ts +13 -0
  25. package/dist/components/Form/Form.d.ts +6 -0
  26. package/dist/components/Img/Img.d.ts +17 -0
  27. package/dist/components/Input/Input.d.ts +14 -0
  28. package/dist/components/Loading/Loading.d.ts +5 -0
  29. package/dist/components/Modal/Modal.d.ts +12 -0
  30. package/dist/components/PageHeader/PageHeader.d.ts +7 -0
  31. package/dist/components/Pagination/Pagination.d.ts +8 -0
  32. package/dist/components/Popover/Popover.d.ts +26 -0
  33. package/dist/components/ProgressBar/ProgressBar.d.ts +8 -0
  34. package/dist/components/RadioGroup/RadioGroup.d.ts +14 -0
  35. package/dist/components/SearchInput/SearchInput.d.ts +8 -0
  36. package/dist/components/Select/Select.d.ts +18 -0
  37. package/dist/components/Skeleton/Skeleton.d.ts +21 -0
  38. package/dist/components/Slider/Slider.d.ts +10 -0
  39. package/dist/components/StatCard/StatCard.d.ts +9 -0
  40. package/dist/components/Steps/Steps.d.ts +12 -0
  41. package/dist/components/Switch/Switch.d.ts +8 -0
  42. package/dist/components/Table/Table.d.ts +13 -0
  43. package/dist/components/Tabs/Tabs.d.ts +12 -0
  44. package/dist/components/Tag/Tag.d.ts +8 -0
  45. package/dist/components/Textarea/Textarea.d.ts +13 -0
  46. package/dist/components/Toast/Toast.d.ts +12 -0
  47. package/dist/components/Tooltip/Tooltip.d.ts +9 -0
  48. package/dist/components/index.d.ts +81 -0
  49. package/dist/components/index.js +1064 -0
  50. package/dist/components/style.css +2005 -0
  51. package/package.json +7 -2
package/README.md CHANGED
@@ -22,7 +22,8 @@ npm install weifuwu
22
22
  | **redis** | `weifuwu` | Redis 客户端 → `ctx.redis` | `Router` |
23
23
  | **ui** | `weifuwu` | SSR 渲染 + 动态 JS/CSS 编译 → `ctx.ui` | `Router` |
24
24
  | **graphql** | `weifuwu` | GraphQL 端点 | `Router` |
25
- | **client** | `weifuwu/client` | 前端 VDOM + Proxy 框架 | — |
25
+ | **client** | `weifuwu/client` | 前端 VDOM + Proxy 框架 + i18n + ErrorBoundary | — |
26
+ | **components** | `weifuwu/components` | 36 个 HTML 原语组件(Button/Table/Modal/...) | `client` |
26
27
  | **layout** | `weifuwu/layout` | 纯 CSS 布局原语 + 主题 Token | — |
27
28
 
28
29
  ---
@@ -39,6 +40,7 @@ npm install weifuwu
39
40
  ctx.sql ctx.ws
40
41
  ctx.redis ctx.route
41
42
  ctx.ui ctx.api / ctx.auth
43
+ ctx.i18n
42
44
  ```
43
45
 
44
46
  ---
@@ -71,7 +73,7 @@ serve(app, { port: 3000 })
71
73
 
72
74
  ```tsx
73
75
  // src/main.tsx —— 前端
74
- import { createApp, router, RouteView } from 'weifuwu/client'
76
+ import { createApp, router, RouteView, i18n } from 'weifuwu/client'
75
77
  import type { WfuiContext, RouteDef } from 'weifuwu/client'
76
78
 
77
79
  function Home(_props: {}, ctx: WfuiContext) {
@@ -497,9 +499,81 @@ import { ErrorBoundary } from 'weifuwu/client'
497
499
  |------|------|
498
500
  | `extendCtx(ctx, fields)` | 创建新 ctx,继承原 ctx 的 getter |
499
501
 
502
+ ### 国际化 — `ctx.i18n`
503
+
504
+ `i18n()` 中间件注入 `ctx.i18n`,支持运行时语言切换:
505
+
506
+ ```ts
507
+ import { createApp, i18n } from 'weifuwu/client'
508
+
509
+ createApp()
510
+ .use(i18n({
511
+ locale: 'zh-CN',
512
+ messages: { 'users.title': '用户管理' },
513
+ }))
514
+ .mount('#root', () => <App />)
515
+
516
+ // 页面中使用
517
+ ctx.i18n?.t('users.title') // → '用户管理'
518
+
519
+ // 运行时切换
520
+ ctx.i18n?.setLocale('en-US') // → 自动触发重渲染
521
+ ```
522
+
523
+ 内置语言包:`zh-CN`(默认)、`en-US`。组件文案(Button 的 `加载中...`、FileUpload 的 `点击或拖拽上传文件`)随语言自动切换。组件通过 `ctx.i18n?.components?.ComponentName.field` 读取,支持 `props.locale` 局部覆盖。
524
+
525
+ ```ts
526
+ import { i18n, zhCN, enUS } from 'weifuwu/client'
527
+ ```
528
+
529
+ ### confirm —— 确认对话框
530
+
531
+ ```ts
532
+ import { createApp, confirm } from 'weifuwu/client'
533
+
534
+ createApp()
535
+ .use(confirm())
536
+ .mount('#root', App)
537
+
538
+ // 在组件中使用
539
+ async function handleDelete(ctx: WfuiContext) {
540
+ const ok = await ctx.confirm?.('确定删除?', {
541
+ title: '确认',
542
+ confirmText: '删除',
543
+ cancelText: '取消',
544
+ variant: 'danger', // 'default' | 'danger'
545
+ })
546
+ if (ok) { /* 执行 */ }
547
+ }
548
+ ```
549
+
550
+ - 直接 DOM 渲染(不依赖 VDOM)
551
+ - 返回 `Promise<boolean>` — `await ctx.confirm(msg, opts?)`
552
+ - 自动锁定背景滚动(ScrollLock)
553
+ - ESC / 点击遮罩关闭
554
+
555
+ ### ScrollLock / FocusTrap 工具
556
+
557
+ ```ts
558
+ import { lockScroll, unlockScroll } from 'weifuwu/client'
559
+ import { trapFocus } from 'weifuwu/client'
560
+
561
+ // 锁定/解锁滚动(支持嵌套计数)
562
+ lockScroll()
563
+ unlockScroll()
564
+
565
+ // 焦点陷阱,返回 cleanup 函数
566
+ const cleanup = trapFocus(containerEl)
567
+ // 组件卸载时
568
+ cleanup() // 恢复焦点
569
+ ```
570
+
571
+ - `lockScroll()`: 多层级可嵌套锁定,iOS Safari `position:fixed` 兼容
572
+ - `trapFocus(el)`: Tab/Shift+Tab 在容器内循环,restore 之前焦点
573
+
500
574
  ### 前端类型
501
575
 
502
- `VNode`, `VNodeType`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `ApiOptions`, `ApiRequestOptions`, `ApiError`, `AuthClient`, `AuthOptions`, `ErrorBoundaryProps`
576
+ `VNode`, `VNodeType`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `ApiOptions`, `ApiRequestOptions`, `ApiError`, `AuthClient`, `AuthOptions`, `ErrorBoundaryProps`, `I18nOptions`, `I18nState`, `LocalePackage`
503
577
 
504
578
  ---
505
579
 
@@ -614,6 +688,64 @@ document.documentElement.setAttribute('data-theme', 'dark')
614
688
 
615
689
  ---
616
690
 
691
+ ## 组件库 — `weifuwu/components`
692
+
693
+ 36 个 **HTML 原语**,覆盖 90% 的 SaaS 页面 HTML 需求。每个组件是 `(props, ctx) => VNode` 纯函数,引用 `weifuwu/layout` 的 CSS 变量做主题。
694
+
695
+ ```ts
696
+ import { Button, Input, Table, Modal, Toast } from 'weifuwu/components'
697
+ import 'weifuwu/components/style.css'
698
+ ```
699
+
700
+ ### 模块总览
701
+
702
+ | 类别 | 组件 | 用途 |
703
+ |------|------|------|
704
+ | **表单核心** | `Button` `Input` `Textarea` `Select` | 4 个最常用的表单元素 |
705
+ | **表单选择** | `Checkbox` `Switch` `RadioGroup` `Slider` | 选择类输入 |
706
+ | **表单增强** | `Form` `Field` `FileUpload` `SearchInput` `ProgressBar` | 文件上传、搜索、进度 |
707
+ | **数据展示** | `Table` `Card` `Badge` `Tag` `Avatar` `StatCard` `PageHeader` `Img` | 数据展示与页面标题 |
708
+ | **数据反馈** | `Modal` `Drawer` `Tooltip` `Popover` `Toast` `Alert` `Loading` `EmptyState` `Skeleton` | 弹窗、抽屉、弹出层、骨架屏 |
709
+ | **导航组件** | `Breadcrumb` `Tabs` `Dropdown` `Pagination` `Steps` `Accordion` | 面包屑、标签页、分页 |
710
+ | **布局** | `Divider` | 分割线 (水平/垂直/带文字) |
711
+
712
+ ### 状态管理说明
713
+
714
+ `ctx.ui.$` 是**组件级**状态——每个组件实例有独立的 Proxy(基于 `vnode._$`),同名变量不会冲突:
715
+
716
+ ```tsx
717
+ // 组件 A
718
+ const $ = ctx.ui.$
719
+ $.open = true // 只影响组件 A
720
+
721
+ // 组件 B(在同一页面)
722
+ const $ = ctx.ui.$
723
+ $.open = false // 只影响组件 B,不影响 A
724
+ ```
725
+
726
+ 跨组件共享状态请使用 `ctx` 直接挂载属性(延续中间件模式):
727
+
728
+ ```ts
729
+ ctx.theme = 'dark' // 所有组件可读
730
+ ctx.toast?.success('成功') // 如已注入 toast 中间件
731
+ ```
732
+
733
+ ### 页面模板 — `docs/pages/`
734
+
735
+ | 模板 | 文件 | 用途 |
736
+ |------|------|------|
737
+ | **列表页** | `docs/pages/list-page.md` | 搜索 + 表格 + 分页 + 加载/空/错误状态 |
738
+ | **表单页** | `docs/pages/form-page.md` | 表单 + 字段 + 校验 + 提交 |
739
+ | **详情页** | `docs/pages/detail-page.md` | 信息展示 + Tabs + 操作 |
740
+ | **设置页** | `docs/pages/settings-page.md` | 分组设置 + 独立保存 |
741
+ | **仪表盘** | `docs/pages/dashboard-page.md` | KPI 卡片 + 图表 + 列表 |
742
+ | **认证页** | `docs/pages/auth-page.md` | 居中卡片 + 表单 + 错误提示 |
743
+ | **应用壳** | `docs/pages/app-layout.md` | 侧边栏 + 导航菜单 + 认证守卫 |
744
+
745
+ 每个模板标注了「改这里」——复制代码后改 API 路径、字段定义、操作按钮、导航项即可使用。
746
+
747
+ ---
748
+
617
749
  ## 环境变量
618
750
 
619
751
  | 变量 | 用途 | 默认值 |
@@ -654,6 +786,12 @@ src/
654
786
  │ ├── api.ts
655
787
  │ ├── auth.ts
656
788
  │ └── ws.ts
789
+ ├── components/ # 29 个 HTML 原语组件
790
+ │ ├── index.ts
791
+ │ ├── Button/ # Button.ts + .css + .test.ts
792
+ │ ├── Input/
793
+ │ ├── ...
794
+ │ └── PageHeader/
657
795
  └── layout/ # 纯 CSS 布局 + 主题
658
796
  ├── weifuwu-layout.css
659
797
  ├── _tokens.css
@@ -5,15 +5,12 @@
5
5
  *
6
6
  * ctx.ui 在 mount 时注入:
7
7
  * ctx.ui.render() 触发组件重渲染
8
- * ctx.ui.$ 持久化组件状态(由渲染器管理)
8
+ * ctx.ui.$ 组件级状态 Proxy(由 renderComponent 创建)
9
9
  * ctx.ui.ready 首次执行标记(由渲染器管理)
10
10
  *
11
- * ctx.ui.$ 是深度 Proxy
12
- * $.x = val → 自动渲染(顶层属性赋值)
13
- * $.items.push(val) 自动渲染(数组突变方法)
14
- * $.items[0].x = val → 自动渲染(对象属性突变)
15
- * $.items.push({x: 1}) → 新对象自动深度包装
16
- * ctx.ui.dirty() → 极少需要(仅当绕过 Proxy 直接操作底层对象)
11
+ * ctx.ui.$ 是组件级 Proxy(组件间隔离):
12
+ * $.x = val → 设置当前组件状态,自动触发渲染
13
+ * ctx.ui.dirty() 标记脏状态(仅深层突变时需要)
17
14
  */
18
15
  import type { WfuiContext, AppMiddleware } from './types.ts';
19
16
  import type { Component } from './vnode.ts';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * weifuwu/client — Confirm 对话框
3
+ *
4
+ * 使用:
5
+ * import { createApp, confirm } from 'weifuwu/client'
6
+ *
7
+ * createApp()
8
+ * .use(confirm())
9
+ * .mount('#root', () => <App />)
10
+ *
11
+ * // 页面中使用
12
+ * if (await ctx.confirm?.('确定删除?')) {
13
+ * // 执行删除
14
+ * }
15
+ */
16
+ import type { AppMiddleware } from './types.ts';
17
+ export interface ConfirmOptions {
18
+ title?: string;
19
+ confirmText?: string;
20
+ cancelText?: string;
21
+ variant?: 'primary' | 'danger';
22
+ }
23
+ export interface ConfirmState {
24
+ confirm: (message: string, options?: ConfirmOptions) => Promise<boolean>;
25
+ }
26
+ export declare function confirm(): AppMiddleware;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * weifuwu/client — FocusTrap
3
+ */
4
+ export declare function trapFocus(container: HTMLElement): () => void;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * weifuwu i18n — 中间件
3
+ *
4
+ * 注入 ctx.i18n 支持运行时语言切换。
5
+ *
6
+ * 使用:
7
+ * app.use(i18n({ locale: 'en', messages: { 'title': 'Dashboard' } }))
8
+ * ctx.i18n?.t('title') // → 'Dashboard'
9
+ * ctx.i18n?.setLocale('zh-CN') // → 切换语言,自动重渲染
10
+ */
11
+ import type { AppMiddleware } from './types.ts';
12
+ export interface I18nOptions {
13
+ locale?: string;
14
+ messages?: Record<string, string>;
15
+ components?: Record<string, Record<string, string>>;
16
+ }
17
+ export interface LocalePackage {
18
+ messages?: Record<string, string>;
19
+ components?: Record<string, Record<string, string>>;
20
+ }
21
+ export interface I18nState {
22
+ locale: string;
23
+ t: (key: string, fallback?: string) => string;
24
+ setLocale: (lang: string) => void;
25
+ components: Record<string, Record<string, string>>;
26
+ }
27
+ export declare function i18n(opts?: I18nOptions): AppMiddleware;
@@ -29,3 +29,11 @@ export { extendCtx } from './types.ts';
29
29
  export type { WfuiContext, AppMiddleware, RouteDef } from './types.ts';
30
30
  export { ErrorBoundary } from './error-boundary.ts';
31
31
  export type { ErrorBoundaryProps } from './error-boundary.ts';
32
+ export { i18n } from './i18n.ts';
33
+ export type { I18nOptions, I18nState } from './i18n.ts';
34
+ export { lockScroll, unlockScroll } from './scroll-lock.ts';
35
+ export { trapFocus } from './focus-trap.ts';
36
+ export { confirm } from './confirm.ts';
37
+ export type { ConfirmOptions, ConfirmState } from './confirm.ts';
38
+ export { zhCN } from './locale/zh_CN.ts';
39
+ export { enUS } from './locale/en_US.ts';