weifuwu 0.38.2 → 0.40.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 +96 -66
- package/dist/client/app.d.ts +2 -6
- package/dist/client/error-boundary.d.ts +2 -2
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.js +350 -147
- 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/render.d.ts +3 -3
- package/dist/client/router.d.ts +1 -1
- package/dist/client/types.d.ts +11 -5
- package/dist/client/vnode.d.ts +11 -2
- package/dist/components/Chart/Chart.d.ts +15 -0
- package/dist/components/Chart/chart-utils.d.ts +53 -0
- package/dist/components/DatePicker/DatePicker.d.ts +18 -0
- package/dist/components/DatePicker/calendar-utils.d.ts +30 -0
- package/dist/components/Dropdown/Dropdown.d.ts +3 -0
- package/dist/components/Editor/Editor.d.ts +13 -0
- package/dist/components/Editor/tools/format.d.ts +10 -0
- package/dist/components/Editor/tools/table.d.ts +7 -0
- package/dist/components/Editor/tools/toolbar.d.ts +9 -0
- package/dist/components/Editor/tools/types.d.ts +15 -0
- package/dist/components/FileUpload/FileUpload.d.ts +3 -0
- package/dist/components/Img/Img.d.ts +0 -2
- package/dist/components/InView/InView.d.ts +2 -17
- package/dist/components/Popover/Popover.d.ts +2 -12
- package/dist/components/Skeleton/Skeleton.d.ts +0 -8
- package/dist/components/Tooltip/Tooltip.d.ts +5 -1
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +1603 -343
- package/dist/components/style.css +734 -153
- package/dist/layout/weifuwu-layout.css +14 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -709,21 +709,30 @@ app.destroy()
|
|
|
709
709
|
```tsx
|
|
710
710
|
import type { Component, WfuiContext } from 'weifuwu/client'
|
|
711
711
|
|
|
712
|
-
//
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
712
|
+
// 两阶段组件:mount(只一次)→ render(每次 dirty/props 变化)
|
|
713
|
+
const Counter: Component = (_init, ctx) => {
|
|
714
|
+
// ── mount ──
|
|
715
|
+
let count = 0
|
|
716
|
+
|
|
717
|
+
ctx.ui.onmounted((el) => {
|
|
718
|
+
console.log('DOM 已创建', el)
|
|
719
|
+
})
|
|
716
720
|
|
|
717
|
-
//
|
|
718
|
-
|
|
721
|
+
// ── render ──
|
|
722
|
+
return (props) =>
|
|
723
|
+
h('button', { onClick: () => { count++; ctx.ui.render() } }, count)
|
|
724
|
+
}
|
|
719
725
|
```
|
|
720
726
|
|
|
721
727
|
| 规则 | 说明 |
|
|
722
728
|
|------|------|
|
|
723
|
-
| 组件签名 | `(
|
|
729
|
+
| 组件签名 | `(initProps: P, ctx: WfuiContext) => (props: P) => VNode \| null` |
|
|
730
|
+
| mount 阶段 | 外层函数只执行一次,初始化状态/注册生命周期 |
|
|
731
|
+
| render 阶段 | 内层函数每次 dirty/props 变化时执行,返回 VNode |
|
|
724
732
|
| 无 class | 无 `this`,无实例方法 |
|
|
725
733
|
| 无 hook | 无 `useState` / `useEffect` / `useMemo` |
|
|
726
|
-
|
|
|
734
|
+
| 状态 | 闭包变量 + `ctx.ui.render()` 手动触发,或 `ctx.ui.$()` 响应式容器 |
|
|
735
|
+
| 生命周期 | `ctx.ui.onmount / onmounted / onunmount / onupdate` |
|
|
727
736
|
|
|
728
737
|
### JSX 工厂
|
|
729
738
|
|
|
@@ -744,53 +753,40 @@ h('div', { class: 'x' }, child1, child2)
|
|
|
744
753
|
| `jsx` / `jsxs` / `jsxDEV` | JSX 编译目标 |
|
|
745
754
|
| `Fragment` | 片段 |
|
|
746
755
|
|
|
747
|
-
|
|
756
|
+
---
|
|
748
757
|
|
|
749
|
-
|
|
750
|
-
interface VNode {
|
|
751
|
-
type: string | Component | typeof Fragment
|
|
752
|
-
props: Record<string, any>
|
|
753
|
-
key?: string
|
|
754
|
-
el?: Node // 对应 DOM(框架内部)
|
|
755
|
-
_$?: Record<string, any> // 组件状态(框架内部)
|
|
756
|
-
_child?: any // 子 VNode 缓存
|
|
757
|
-
_cleanup?: (() => void) // ref 清理函数
|
|
758
|
-
}
|
|
759
|
-
```
|
|
758
|
+
## 状态管理
|
|
760
759
|
|
|
761
|
-
|
|
760
|
+
### 闭包变量 + `ctx.ui.render()`(推荐)
|
|
762
761
|
|
|
763
|
-
|
|
762
|
+
```tsx
|
|
763
|
+
const Counter: Component = (_init, ctx) => {
|
|
764
|
+
let count = 0
|
|
765
|
+
return (props) =>
|
|
766
|
+
h('button', { onClick: () => { count++; ctx.ui.render() } }, count)
|
|
767
|
+
}
|
|
768
|
+
```
|
|
764
769
|
|
|
765
|
-
`ctx.ui
|
|
770
|
+
### `ctx.ui.$()` — 响应式状态容器
|
|
766
771
|
|
|
767
|
-
|
|
768
|
-
- `$.arr.push(val)` → Proxy 拦截数组突变 → 自动渲染
|
|
769
|
-
- `$.arr[0].x = y` → 深度拦截嵌套对象 → 自动渲染
|
|
770
|
-
- 每个组件实例有独立 Proxy(`vnode._$`),同名变量不冲突
|
|
772
|
+
`ctx.ui.$()` 返回浅 Proxy,赋值自动触发渲染(微任务批量合并):
|
|
771
773
|
|
|
772
774
|
```tsx
|
|
773
|
-
|
|
774
|
-
const $ = ctx.ui.$
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
<div>
|
|
779
|
-
<span>{$.count}</span>
|
|
780
|
-
<button onClick={() => $.count++}>+</button>
|
|
781
|
-
</div>
|
|
782
|
-
)
|
|
775
|
+
const FormPage: Component = (_init, ctx) => {
|
|
776
|
+
const $ = ctx.ui.$()
|
|
777
|
+
$.email = ''
|
|
778
|
+
return (props) =>
|
|
779
|
+
h('input', { value: $.email, onInput: (e: any) => { $.email = e.target.value } })
|
|
783
780
|
}
|
|
784
781
|
```
|
|
785
782
|
|
|
783
|
+
**注意**:mount/render/生命周期回调中 `$.x = val` 不触发渲染。仅事件/timer/Promise.then 中生效。
|
|
784
|
+
|
|
786
785
|
| API | 说明 |
|
|
787
786
|
|-----|------|
|
|
788
|
-
| `ctx.ui
|
|
789
|
-
| `ctx.ui.
|
|
790
|
-
| `ctx.ui.dirty()` |
|
|
791
|
-
| `ctx.ui.render()` | 手动触发渲染(框架内部使用,页面代码不用) |
|
|
792
|
-
|
|
793
|
-
**重要规则**:render 函数内部不写 `$`。`$` 的写入只在事件回调(onClick/onInput)、ref 回调、或 `if (!ctx.ui.ready)` 初始化块中进行。
|
|
787
|
+
| `ctx.ui.$()` | 创建响应式状态容器 |
|
|
788
|
+
| `ctx.ui.render()` | 立即同步渲染 |
|
|
789
|
+
| `ctx.ui.dirty()` | 标记脏状态,下个微任务批量渲染 |
|
|
794
790
|
|
|
795
791
|
---
|
|
796
792
|
|
|
@@ -811,27 +807,62 @@ function Counter(_props: {}, ctx: WfuiContext) {
|
|
|
811
807
|
|
|
812
808
|
---
|
|
813
809
|
|
|
814
|
-
## 生命周期
|
|
810
|
+
## 生命周期
|
|
815
811
|
|
|
816
|
-
`ref`
|
|
812
|
+
框架不提供 `ref` prop。使用 `ctx.ui.onmount / onmounted / onunmount / onupdate`:
|
|
813
|
+
|
|
814
|
+
| API | 触发时机 | 参数 | 返回值 |
|
|
815
|
+
|-----|---------|------|--------|
|
|
816
|
+
| `ctx.ui.onmount(fn)` | render 前(DOM 未创建) | `() => void` | — |
|
|
817
|
+
| `ctx.ui.onmounted(fn)` | 首次 render 后(DOM 已创建) | `(el: Element) => cleanup` | `() => void`(可选 cleanup)|
|
|
818
|
+
| `ctx.ui.onunmount(fn)` | 组件移除前 | `() => void` | — |
|
|
819
|
+
| `ctx.ui.onupdate(fn)` | props 变化时 | `(prevProps) => void` | — |
|
|
817
820
|
|
|
818
821
|
```tsx
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
)
|
|
822
|
+
const Timer: Component = (_init, ctx) => {
|
|
823
|
+
let timer: ReturnType<typeof setInterval> | undefined
|
|
824
|
+
|
|
825
|
+
ctx.ui.onmounted((el) => {
|
|
826
|
+
timer = setInterval(() => console.log('tick'), 1000)
|
|
827
|
+
return () => clearInterval(timer)
|
|
828
|
+
})
|
|
829
|
+
|
|
830
|
+
return (props) => h('div', {}, 'Timer')
|
|
826
831
|
}
|
|
827
832
|
```
|
|
828
833
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
834
|
+
**注意**:`onmounted` 在首次渲染后触发,此时 DOM 已创建。生命周期回调中 `$.x = val` 不触发渲染(仅事件/timer 中生效)。
|
|
835
|
+
|
|
836
|
+
对于**内嵌元素**(非根元素),用 `el.querySelector()`:
|
|
837
|
+
|
|
838
|
+
```tsx
|
|
839
|
+
ctx.ui.onmounted((el) => {
|
|
840
|
+
const input = el.querySelector('input[type="text"]') as HTMLElement
|
|
841
|
+
input?.focus()
|
|
842
|
+
})
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
所有钩子遵循替换模式:多次注册只保留最后一次。
|
|
846
|
+
|
|
847
|
+
### 异步组件
|
|
848
|
+
|
|
849
|
+
在 mount 阶段发起请求,数据通过 `$.x = val` 自动触发渲染:
|
|
850
|
+
|
|
851
|
+
```tsx
|
|
852
|
+
const UserProfile: Component = (initProps, ctx) => {
|
|
853
|
+
const $ = ctx.ui.$()
|
|
854
|
+
$.loading = true
|
|
855
|
+
|
|
856
|
+
fetch(`/api/user/${initProps.id}`)
|
|
857
|
+
.then(r => r.json())
|
|
858
|
+
.then(user => { $.user = user; $.loading = false })
|
|
859
|
+
|
|
860
|
+
return (props) =>
|
|
861
|
+
$.loading
|
|
862
|
+
? h('div', {}, '加载中...')
|
|
863
|
+
: h('div', {}, $.user?.name ?? '')
|
|
864
|
+
}
|
|
865
|
+
```
|
|
835
866
|
|
|
836
867
|
---
|
|
837
868
|
|
|
@@ -1122,7 +1153,7 @@ import { ErrorBoundary } from 'weifuwu/client'
|
|
|
1122
1153
|
<ErrorBoundary fallback={({ error }) => (
|
|
1123
1154
|
<div>
|
|
1124
1155
|
<p>出错了: {String(error)}</p>
|
|
1125
|
-
<button onClick={() =>
|
|
1156
|
+
<button onClick={() => location.reload()}>重试</button>
|
|
1126
1157
|
</div>
|
|
1127
1158
|
)}>
|
|
1128
1159
|
<UserProfile />
|
|
@@ -1134,7 +1165,7 @@ import { ErrorBoundary } from 'weifuwu/client'
|
|
|
1134
1165
|
| `fallback` | `VNode \| ((props: { error }) => VNode) \| null` | `null` | 错误时渲染的内容 |
|
|
1135
1166
|
| `children` | `any` | — | 子组件 |
|
|
1136
1167
|
|
|
1137
|
-
捕获子组件 render 时的错误 →
|
|
1168
|
+
捕获子组件 render 时的错误 → 渲染 fallback。清除 `error` 即可重试。
|
|
1138
1169
|
|
|
1139
1170
|
---
|
|
1140
1171
|
|
|
@@ -1148,8 +1179,7 @@ createApp()
|
|
|
1148
1179
|
.mount('#root', App)
|
|
1149
1180
|
|
|
1150
1181
|
// 在组件中使用
|
|
1151
|
-
|
|
1152
|
-
async function handleDelete() {
|
|
1182
|
+
async function handleDelete(ctx: WfuiContext) {
|
|
1153
1183
|
const ok = await ctx.confirm?.('确定删除这条记录?', {
|
|
1154
1184
|
title: '确认删除',
|
|
1155
1185
|
confirmText: '删除',
|
|
@@ -1157,7 +1187,7 @@ async function handleDelete() {
|
|
|
1157
1187
|
variant: 'danger', // 'primary' | 'danger'
|
|
1158
1188
|
})
|
|
1159
1189
|
if (ok) {
|
|
1160
|
-
|
|
1190
|
+
// 执行删除...
|
|
1161
1191
|
}
|
|
1162
1192
|
}
|
|
1163
1193
|
```
|
|
@@ -1228,9 +1258,9 @@ import type { RouterOptions } from 'weifuwu/client'
|
|
|
1228
1258
|
|
|
1229
1259
|
| 类型 | 说明 |
|
|
1230
1260
|
|------|------|
|
|
1231
|
-
| `VNode` | `{ type, props, key
|
|
1261
|
+
| `VNode` | `{ type, props, key? }` |
|
|
1232
1262
|
| `VNodeType` | `string \| Component \| typeof Fragment` |
|
|
1233
|
-
| `Component<P>` | `(
|
|
1263
|
+
| `Component<P>` | `(initProps: P, ctx: WfuiContext) => (props: P) => VNode \| null` |
|
|
1234
1264
|
| `WfuiContext` | `{ ui, route?, app?, ws?, api?, auth?, i18n?, confirm?, [key]: unknown }` |
|
|
1235
1265
|
| `AppMiddleware` | `(ctx: WfuiContext) => WfuiContext` |
|
|
1236
1266
|
| `RouteDef` | `{ path, component?, layout?, children?, auth?, title? }` |
|
|
@@ -1471,7 +1501,7 @@ document.documentElement.setAttribute('data-theme', 'dark')
|
|
|
1471
1501
|
|------|------|------|
|
|
1472
1502
|
| 注入 | 中间件注入 ctx.field | 中间件注入 ctx.field |
|
|
1473
1503
|
| 读取 | handler 读取 ctx | 组件读取 ctx |
|
|
1474
|
-
| 渲染 | 返回 Response |
|
|
1504
|
+
| 渲染 | 返回 Response | `ctx.ui.render()` / `ctx.ui.dirty()` 触发 VDOM patch |
|
|
1475
1505
|
|
|
1476
1506
|
## Closeable 接口
|
|
1477
1507
|
|
package/dist/client/app.d.ts
CHANGED
|
@@ -5,12 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* ctx.ui 在 mount 时注入:
|
|
7
7
|
* ctx.ui.render() 触发组件重渲染
|
|
8
|
-
* ctx.ui
|
|
9
|
-
* ctx.ui
|
|
10
|
-
*
|
|
11
|
-
* ctx.ui.$ 是组件级 Proxy(组件间隔离):
|
|
12
|
-
* $.x = val → 设置当前组件状态,自动触发渲染
|
|
13
|
-
* ctx.ui.dirty() → 标记脏状态(仅深层突变时需要)
|
|
8
|
+
* ctx.ui.dirty() 标记脏状态,下个微任务批量渲染
|
|
9
|
+
* ctx.ui.$() 创建响应式状态容器($.x = val 自动 dirty)
|
|
14
10
|
*/
|
|
15
11
|
import type { WfuiContext, AppMiddleware } from './types.ts';
|
|
16
12
|
import type { Component } from './vnode.ts';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* ```
|
|
11
11
|
*
|
|
12
12
|
* 基于 ctx.ui._errorHandler 机制:在渲染子组件前注入错误处理器,
|
|
13
|
-
* 子组件 render 抛错时设置
|
|
13
|
+
* 子组件 render 抛错时设置 error → 重新渲染 fallback。
|
|
14
14
|
*/
|
|
15
15
|
import type { WfuiContext } from './types.ts';
|
|
16
16
|
import type { VNode } from './vnode.ts';
|
|
@@ -20,4 +20,4 @@ export interface ErrorBoundaryProps {
|
|
|
20
20
|
}) => VNode) | null;
|
|
21
21
|
children?: any;
|
|
22
22
|
}
|
|
23
|
-
export declare function ErrorBoundary(props: ErrorBoundaryProps, ctx: WfuiContext): VNode | null;
|
|
23
|
+
export declare function ErrorBoundary(props: ErrorBoundaryProps, ctx: WfuiContext): (props2: ErrorBoundaryProps) => VNode | null;
|
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';
|