@wwog/react 1.1.5 → 1.1.7
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 +141 -5
- package/dist/index.d.mts +119 -2
- package/dist/index.js +1 -196
- package/package.json +1 -1
- package/src/Common/Scope.tsx +77 -0
- package/src/Common/index.ts +1 -0
- package/src/ProcessControl/If.tsx +17 -1
- package/src/ProcessControl/Pipe.tsx +64 -0
- package/src/ProcessControl/index.ts +1 -0
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@wwog/react)
|
|
6
6
|
[](https://nodejs.org/api/esm.html)
|
|
7
7
|
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
[English Documentation](./README_en.md)
|
|
11
|
+
|
|
8
12
|
## 安装
|
|
9
13
|
|
|
10
14
|
```bash
|
|
@@ -35,7 +39,7 @@ pnpm add @wwog/react
|
|
|
35
39
|
声明式的条件渲染组件,类似于 if-else 语句,但在 JSX 中使用。
|
|
36
40
|
|
|
37
41
|
```tsx
|
|
38
|
-
import { If } from
|
|
42
|
+
import { If } from "@wwog/react";
|
|
39
43
|
|
|
40
44
|
function Example({ count }) {
|
|
41
45
|
return (
|
|
@@ -59,7 +63,7 @@ function Example({ count }) {
|
|
|
59
63
|
类似于 JavaScript 的 switch 语句,但更具声明性和类型安全性。
|
|
60
64
|
|
|
61
65
|
```tsx
|
|
62
|
-
import { Switch } from
|
|
66
|
+
import { Switch } from "@wwog/react";
|
|
63
67
|
|
|
64
68
|
function Example({ status }) {
|
|
65
69
|
return (
|
|
@@ -81,6 +85,62 @@ function Example({ status }) {
|
|
|
81
85
|
}
|
|
82
86
|
```
|
|
83
87
|
|
|
88
|
+
#### `<When>` (v1.1.5+)
|
|
89
|
+
|
|
90
|
+
一个简洁的条件渲染组件,支持多条件逻辑组合。比 <If> 更加简洁,适用于简单的条件渲染场景。
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
import { When } from '@wwog/react';
|
|
94
|
+
|
|
95
|
+
function Example() {
|
|
96
|
+
const isAdmin = useIsAdmin();
|
|
97
|
+
const isLoading = useIsLoading();
|
|
98
|
+
const hasErrors = useHasErrors();
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<>
|
|
102
|
+
{/* 所有条件都为真时渲染 */}
|
|
103
|
+
<When all={[isAdmin, !isLoading]}>
|
|
104
|
+
<AdminPanel />
|
|
105
|
+
</When>
|
|
106
|
+
|
|
107
|
+
{/* 任一条件为真时渲染 */}
|
|
108
|
+
<When any={[isLoading, hasErrors]} fallback={<ReadyContent />}>
|
|
109
|
+
<LoadingOrErrorMessage />
|
|
110
|
+
</When>
|
|
111
|
+
|
|
112
|
+
{/* 所有条件都为假时渲染 */}
|
|
113
|
+
<When none={[isAdmin, isLoading]}>
|
|
114
|
+
<RegularUserContent />
|
|
115
|
+
</When>
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `<True>` / `<False>` (v1.1.6+)
|
|
121
|
+
|
|
122
|
+
用于简化条件渲染的辅助组件,适合简单的布尔判断场景。
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { True, False } from "@wwog/react";
|
|
126
|
+
|
|
127
|
+
function Example({ isActive }) {
|
|
128
|
+
return (
|
|
129
|
+
<>
|
|
130
|
+
<True condition={isActive}>
|
|
131
|
+
<p>激活状态</p>
|
|
132
|
+
</True>
|
|
133
|
+
<False condition={isActive}>
|
|
134
|
+
<p>未激活状态</p>
|
|
135
|
+
</False>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- `<True condition={...}>`:当 condition 为 true 时渲染子内容。
|
|
142
|
+
- `<False condition={...}>`:当 condition 为 false 时渲染子内容。
|
|
143
|
+
|
|
84
144
|
### 通用组件
|
|
85
145
|
|
|
86
146
|
#### `<ArrayRender>`
|
|
@@ -88,11 +148,11 @@ function Example({ status }) {
|
|
|
88
148
|
高效渲染数组数据的工具组件,支持过滤和自定义渲染。
|
|
89
149
|
|
|
90
150
|
```tsx
|
|
91
|
-
import { ArrayRender } from
|
|
151
|
+
import { ArrayRender } from "@wwog/react";
|
|
92
152
|
|
|
93
153
|
function UserList({ users }) {
|
|
94
154
|
return (
|
|
95
|
-
<ArrayRender
|
|
155
|
+
<ArrayRender
|
|
96
156
|
items={users}
|
|
97
157
|
filter={(user) => user.active}
|
|
98
158
|
renderItem={(user, index) => (
|
|
@@ -105,12 +165,73 @@ function UserList({ users }) {
|
|
|
105
165
|
}
|
|
106
166
|
```
|
|
107
167
|
|
|
168
|
+
#### `<Pipe>` (v1.1.7+)
|
|
169
|
+
|
|
170
|
+
声明式的数据管道处理组件,适合多步骤数据转换和链式处理。
|
|
171
|
+
|
|
172
|
+
> 声明式数据处理,替代嵌套函数调用。
|
|
173
|
+
> 提高代码可读性,逻辑清晰。
|
|
174
|
+
> 适合数据清洗、格式化等场景。
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { Pipe } from "@wwog/react";
|
|
178
|
+
|
|
179
|
+
function Example({ users }) {
|
|
180
|
+
return (
|
|
181
|
+
<Pipe
|
|
182
|
+
data={users}
|
|
183
|
+
transform={[
|
|
184
|
+
(data) => data.filter(user => user.active),
|
|
185
|
+
(data) => data.map(user => user.name)
|
|
186
|
+
]}
|
|
187
|
+
render={(names) => <div>{names.join(", ")}</div>}
|
|
188
|
+
fallback={<div>No Data</div>}
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- `data`:初始数据。
|
|
195
|
+
- `transform`:数据转换函数数组,按顺序依次处理。
|
|
196
|
+
- `render`:渲染最终结果。
|
|
197
|
+
- `fallback`:结果为 null/undefined 时的兜底内容。
|
|
198
|
+
|
|
199
|
+
#### `<Scope>` (v1.1.7+)
|
|
200
|
+
|
|
201
|
+
为子节点提供局部作用域,声明式定义临时变量,简化复杂渲染逻辑。
|
|
202
|
+
|
|
203
|
+
> 避免在组件外定义临时状态或计算。
|
|
204
|
+
> 声明式定义局部变量,增强代码自包含性。
|
|
205
|
+
> 适合表单、计算密集型渲染等场景。
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { Scope } from "@wwog/react";
|
|
209
|
+
|
|
210
|
+
function Example() {
|
|
211
|
+
return (
|
|
212
|
+
<Scope let={{ count: 1, text: "Hello" }}>
|
|
213
|
+
{({ count, text }) => <div>{text} {count}</div>}
|
|
214
|
+
</Scope>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 支持函数式 let
|
|
219
|
+
<Scope let={(props) => ({ total: props.items.length })} props={{ items: [1, 2] }} fallback={<div>Empty</div>}>
|
|
220
|
+
{({ total }) => <div>Total: {total}</div>}
|
|
221
|
+
</Scope>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- `let`:对象或函数,定义作用域变量。
|
|
225
|
+
- `props`:传递给 let 函数的参数。
|
|
226
|
+
- `children`:作用域变量的渲染函数。
|
|
227
|
+
- `fallback`:无内容时的兜底渲染。
|
|
228
|
+
|
|
108
229
|
#### `<SizeBox>`
|
|
109
230
|
|
|
110
231
|
创建固定尺寸的容器,用于布局调整和间距控制。
|
|
111
232
|
|
|
112
233
|
```tsx
|
|
113
|
-
import { SizeBox } from
|
|
234
|
+
import { SizeBox } from "@wwog/react";
|
|
114
235
|
|
|
115
236
|
function Layout() {
|
|
116
237
|
return (
|
|
@@ -128,6 +249,21 @@ function Layout() {
|
|
|
128
249
|
}
|
|
129
250
|
```
|
|
130
251
|
|
|
252
|
+
### Ideas
|
|
253
|
+
|
|
254
|
+
> 需求不高,但有用的组件
|
|
255
|
+
|
|
256
|
+
- Loop:灵活的迭代渲染,支持数组、对象和范围。
|
|
257
|
+
- Try:封装异步逻辑,处理 Promise 状态。
|
|
258
|
+
- Toggle:基于布尔状态切换 on 和 off 内容。
|
|
259
|
+
- Pick:轻量版值选择渲染,类似枚举匹配。
|
|
260
|
+
- Render:动态渲染函数,简化复杂渲染逻辑。
|
|
261
|
+
- Once:确保内容仅渲染一次,适合初始化。
|
|
262
|
+
- Each:增强列表渲染,支持过滤和排序。
|
|
263
|
+
|
|
264
|
+
|
|
131
265
|
## License
|
|
132
266
|
|
|
133
267
|
MIT
|
|
268
|
+
|
|
269
|
+
|
package/dist/index.d.mts
CHANGED
|
@@ -76,6 +76,16 @@ declare const If: {
|
|
|
76
76
|
Else: (props: ElseProps) => React.ReactElement;
|
|
77
77
|
};
|
|
78
78
|
};
|
|
79
|
+
interface TrueProps {
|
|
80
|
+
condition: boolean;
|
|
81
|
+
children?: ReactNode;
|
|
82
|
+
}
|
|
83
|
+
declare const True: FC<TrueProps>;
|
|
84
|
+
interface FalseProps {
|
|
85
|
+
condition: boolean;
|
|
86
|
+
children?: ReactNode;
|
|
87
|
+
}
|
|
88
|
+
declare const False: FC<FalseProps>;
|
|
79
89
|
|
|
80
90
|
interface WhenProps {
|
|
81
91
|
/**
|
|
@@ -128,6 +138,58 @@ interface WhenProps {
|
|
|
128
138
|
*/
|
|
129
139
|
declare const When: FC<WhenProps>;
|
|
130
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Props for the `Pipe` component.
|
|
143
|
+
*
|
|
144
|
+
* @interface PipeProps
|
|
145
|
+
* @property {any} data - The initial data to process.
|
|
146
|
+
* @property {((input: any) => any)[]} transform - Array of functions to transform the data.
|
|
147
|
+
* @property {(result: any) => ReactNode} render - Function to render the transformed data.
|
|
148
|
+
* @property {ReactNode} [fallback] - Content to render if the result is null or undefined.
|
|
149
|
+
*/
|
|
150
|
+
interface PipeProps {
|
|
151
|
+
/**
|
|
152
|
+
* @description_en The initial data to process.
|
|
153
|
+
* @description_zh 要处理的初始数据。
|
|
154
|
+
*/
|
|
155
|
+
data: any;
|
|
156
|
+
/**
|
|
157
|
+
* @description_en Array of functions to transform the data.
|
|
158
|
+
* @description_zh 转换数据的函数数组。
|
|
159
|
+
*/
|
|
160
|
+
transform: ((input: any) => any)[];
|
|
161
|
+
/**
|
|
162
|
+
* @description_en Function to render the transformed data.
|
|
163
|
+
* @description_zh 渲染转换后数据的函数。
|
|
164
|
+
*/
|
|
165
|
+
render: (result: any) => ReactNode;
|
|
166
|
+
/**
|
|
167
|
+
* @description_en Content to render if the result is null or undefined.
|
|
168
|
+
* @description_zh 如果结果为 null 或 undefined 时渲染的内容。
|
|
169
|
+
* @optional
|
|
170
|
+
* @default null
|
|
171
|
+
*/
|
|
172
|
+
fallback?: ReactNode;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @description_zh 一个声明式组件,用于通过管道式处理数据并渲染,简化多步骤数据转换。
|
|
176
|
+
* @description_en A declarative component for processing data through a pipeline and rendering, simplifying multi-step data transformations.
|
|
177
|
+
* @component
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* <Pipe
|
|
181
|
+
* data={users}
|
|
182
|
+
* transform={[
|
|
183
|
+
* (data) => data.filter(user => user.active),
|
|
184
|
+
* (data) => data.map(user => user.name)
|
|
185
|
+
* ]}
|
|
186
|
+
* render={(names) => <div>{names.join(", ")}</div>}
|
|
187
|
+
* fallback={<div>No Data</div>}
|
|
188
|
+
* />
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare const Pipe: FC<PipeProps>;
|
|
192
|
+
|
|
131
193
|
interface SizeBoxProps {
|
|
132
194
|
size?: number | string;
|
|
133
195
|
height?: number | string;
|
|
@@ -148,11 +210,66 @@ interface ArrayRenderProps<T> {
|
|
|
148
210
|
}
|
|
149
211
|
declare function ArrayRender<T>(props: ArrayRenderProps<T>): ReactNode;
|
|
150
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Props for the `Scope` component.
|
|
215
|
+
*
|
|
216
|
+
* @interface ScopeProps
|
|
217
|
+
* @property {Record<string, any> | ((props: any) => Record<string, any>)} let - An object or function defining local variables for the scope.
|
|
218
|
+
* @property {any} [props] - Optional props passed to the let function.
|
|
219
|
+
* @property {(scope: Record<string, any>) => ReactNode} [children] - Render function to access the scope variables.
|
|
220
|
+
* @property {ReactNode} [fallback] - Content to render if children or scope is empty.
|
|
221
|
+
*/
|
|
222
|
+
interface ScopeProps {
|
|
223
|
+
/**
|
|
224
|
+
* @description_en An object or function defining local variables for the scope.
|
|
225
|
+
* @description_zh 定义作用域局部变量的对象或函数。
|
|
226
|
+
*/
|
|
227
|
+
let: Record<string, any> | ((props: any) => Record<string, any>);
|
|
228
|
+
/**
|
|
229
|
+
* @description_en Optional props passed to the let function.
|
|
230
|
+
* @description_zh 传递给 let 函数的可选属性。
|
|
231
|
+
* @optional
|
|
232
|
+
*/
|
|
233
|
+
props?: any;
|
|
234
|
+
/**
|
|
235
|
+
* @description_en Render function to access the scope variables.
|
|
236
|
+
* @description_zh 访问作用域变量的渲染函数。
|
|
237
|
+
* @optional
|
|
238
|
+
*/
|
|
239
|
+
children?: (scope: Record<string, any>) => ReactNode;
|
|
240
|
+
/**
|
|
241
|
+
* @description_en Content to render if children or scope is empty.
|
|
242
|
+
* @description_zh 如果 children 或 scope 为空时渲染的内容。
|
|
243
|
+
* @optional
|
|
244
|
+
* @default null
|
|
245
|
+
*/
|
|
246
|
+
fallback?: ReactNode;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* @description_zh 一个声明式组件,为子节点提供局部作用域,简化临时状态或上下文管理。
|
|
250
|
+
* @description_en A declarative component that provides a local scope for children, simplifying temporary state or context management.
|
|
251
|
+
* @component
|
|
252
|
+
* @example
|
|
253
|
+
* ```tsx
|
|
254
|
+
* <Scope let={{ count: 1, text: "Hello" }}>
|
|
255
|
+
* {({ count, text }) => <div>{text} {count}</div>}
|
|
256
|
+
* </Scope>
|
|
257
|
+
* ```
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* <Scope let={(props) => ({ total: props.items.length })} props={{ items: [1, 2] }} fallback={<div>Empty</div>}>
|
|
262
|
+
* {({ total }) => <div>Total: {total}</div>}
|
|
263
|
+
* </Scope>
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare const Scope: FC<ScopeProps>;
|
|
267
|
+
|
|
151
268
|
/**
|
|
152
269
|
* @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
|
|
153
270
|
* @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
|
|
154
271
|
*/
|
|
155
272
|
declare function childrenLoop(children: React.ReactNode | undefined, callback: (child: React.ReactNode, index: number) => boolean | void): void;
|
|
156
273
|
|
|
157
|
-
export { ArrayRender, If, SizeBox, Switch, When, childrenLoop };
|
|
158
|
-
export type { ArrayRenderProps, ElseIfProps, ElseProps, IfProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, WhenProps };
|
|
274
|
+
export { ArrayRender, False, If, Pipe, Scope, SizeBox, Switch, True, When, childrenLoop };
|
|
275
|
+
export type { ArrayRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, TrueProps, WhenProps };
|
package/dist/index.js
CHANGED
|
@@ -1,196 +1 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
function childrenLoop(children, callback) {
|
|
4
|
-
if (children === void 0) return;
|
|
5
|
-
let index = 0;
|
|
6
|
-
if (Array.isArray(children)) {
|
|
7
|
-
for (const child of children) {
|
|
8
|
-
const shouldContinue = callback(child, index++);
|
|
9
|
-
if (shouldContinue === false) {
|
|
10
|
-
break;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
} else {
|
|
14
|
-
callback(children, index);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const defaultCompare = (a, b) => {
|
|
19
|
-
return a === b;
|
|
20
|
-
};
|
|
21
|
-
const Case = (props) => {
|
|
22
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
|
|
23
|
-
};
|
|
24
|
-
Case.displayName = "Switch_Case";
|
|
25
|
-
const Default = (props) => {
|
|
26
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
|
|
27
|
-
};
|
|
28
|
-
Default.displayName = "Switch_Default";
|
|
29
|
-
const Switch = (props) => {
|
|
30
|
-
const { value, compare = defaultCompare, children, strict = false } = props;
|
|
31
|
-
const seenValues = /* @__PURE__ */ new Set();
|
|
32
|
-
let matchedChildren = null;
|
|
33
|
-
let defaultChild = null;
|
|
34
|
-
let hasDefault = false;
|
|
35
|
-
childrenLoop(children, (child, index) => {
|
|
36
|
-
if (!React.isValidElement(child)) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
`Switch Children only accepts valid React elements at index ${index}`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
const type = child.type;
|
|
42
|
-
if (type.displayName === Case.displayName) {
|
|
43
|
-
const caseProps = child.props;
|
|
44
|
-
if (seenValues.has(caseProps.value)) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Switch found duplicate Case value at index ${index}: ${JSON.stringify(
|
|
47
|
-
caseProps.value
|
|
48
|
-
)}${strict ? " (detected in strict mode)" : ""}`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
seenValues.add(caseProps.value);
|
|
52
|
-
if (!matchedChildren && compare(value, caseProps.value)) {
|
|
53
|
-
matchedChildren = caseProps.children;
|
|
54
|
-
if (strict === false) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} else if (type.displayName === Default.displayName) {
|
|
59
|
-
if (hasDefault) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
`Switch can only have one Default child at index ${index}`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
hasDefault = true;
|
|
65
|
-
defaultChild = child.props.children;
|
|
66
|
-
if (!strict && matchedChildren) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
throw new Error(
|
|
71
|
-
`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(
|
|
72
|
-
type.displayName || type.name || type
|
|
73
|
-
)} at index ${index}`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, matchedChildren ?? defaultChild);
|
|
78
|
-
};
|
|
79
|
-
Switch.displayName = "Switch";
|
|
80
|
-
Switch.Case = Case;
|
|
81
|
-
Switch.Default = Default;
|
|
82
|
-
Switch.createTyped = function() {
|
|
83
|
-
return {
|
|
84
|
-
Switch,
|
|
85
|
-
Case,
|
|
86
|
-
Default
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const Then = (props) => {
|
|
91
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
|
|
92
|
-
};
|
|
93
|
-
const Else = ({ children }) => {
|
|
94
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
|
|
95
|
-
};
|
|
96
|
-
const ElseIf = (props) => {
|
|
97
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
|
|
98
|
-
};
|
|
99
|
-
Then.displayName = "If_Then";
|
|
100
|
-
Else.displayName = "If_Else";
|
|
101
|
-
ElseIf.displayName = "If_ElseIf";
|
|
102
|
-
const If = ({
|
|
103
|
-
condition,
|
|
104
|
-
children
|
|
105
|
-
}) => {
|
|
106
|
-
let thenChild = null;
|
|
107
|
-
let elseChild = null;
|
|
108
|
-
const elseIfChildren = [];
|
|
109
|
-
React.Children.forEach(children, (child) => {
|
|
110
|
-
if (!React.isValidElement(child)) {
|
|
111
|
-
throw new Error("If component only accepts valid React elements");
|
|
112
|
-
}
|
|
113
|
-
const type = child.type;
|
|
114
|
-
if (type.displayName === Then.displayName) {
|
|
115
|
-
if (thenChild) {
|
|
116
|
-
throw new Error("If component can only have one Then child");
|
|
117
|
-
}
|
|
118
|
-
thenChild = child;
|
|
119
|
-
} else if (type.displayName === ElseIf.displayName) {
|
|
120
|
-
elseIfChildren.push(child);
|
|
121
|
-
} else if (type.displayName === Else.displayName) {
|
|
122
|
-
if (elseChild) {
|
|
123
|
-
throw new Error("If component can only have one Else child");
|
|
124
|
-
}
|
|
125
|
-
elseChild = child;
|
|
126
|
-
} else {
|
|
127
|
-
throw new Error(
|
|
128
|
-
`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(
|
|
129
|
-
type.displayName || type.name || type
|
|
130
|
-
)}`
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
if (condition) {
|
|
135
|
-
return thenChild ? /* @__PURE__ */ React.createElement(React.Fragment, null, thenChild.props.children) : null;
|
|
136
|
-
}
|
|
137
|
-
for (const elseIf of elseIfChildren) {
|
|
138
|
-
if (elseIf.props.condition) {
|
|
139
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, elseIf.props.children);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (elseChild) {
|
|
143
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, elseChild.props.children);
|
|
144
|
-
}
|
|
145
|
-
return null;
|
|
146
|
-
};
|
|
147
|
-
If.displayName = "If";
|
|
148
|
-
If.Then = Then;
|
|
149
|
-
If.ElseIf = ElseIf;
|
|
150
|
-
If.Else = Else;
|
|
151
|
-
If.createTyped = function() {
|
|
152
|
-
return {
|
|
153
|
-
If,
|
|
154
|
-
Then,
|
|
155
|
-
ElseIf,
|
|
156
|
-
Else
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const When = ({ all, any, none, children, fallback }) => {
|
|
161
|
-
const shouldRender = useMemo(() => {
|
|
162
|
-
if (all && (any || none)) {
|
|
163
|
-
console.warn(
|
|
164
|
-
'When: Multiple condition types (all, any, none) provided; "all" takes precedence.'
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
if (all && all.length > 0 && all.every(Boolean)) return true;
|
|
168
|
-
if (any && any.length > 0 && any.some(Boolean)) return true;
|
|
169
|
-
if (none && none.length > 0 && none.every((v) => !v)) return true;
|
|
170
|
-
return false;
|
|
171
|
-
}, [all, any, none]);
|
|
172
|
-
return shouldRender ? /* @__PURE__ */ React.createElement(React.Fragment, null, children) : /* @__PURE__ */ React.createElement(React.Fragment, null, fallback || null);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const SizeBox = (props) => {
|
|
176
|
-
const { children, h, w, size, height, width } = props;
|
|
177
|
-
const widthValue = size || w || width;
|
|
178
|
-
const heightValue = size || h || height;
|
|
179
|
-
return /* @__PURE__ */ React.createElement("div", { style: { width: widthValue, height: heightValue } }, children);
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
function ArrayRender(props) {
|
|
183
|
-
const { items, renderItem, filter } = props;
|
|
184
|
-
if (!items) {
|
|
185
|
-
console.error("ArrayRender: items is null");
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
return /* @__PURE__ */ React.createElement(Fragment, null, items.map((item, index) => {
|
|
189
|
-
if (filter && !filter(item)) {
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
return renderItem(item, index);
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export { ArrayRender, If, SizeBox, Switch, When, childrenLoop };
|
|
1
|
+
import n,{useMemo as g,Fragment as S}from"react";function v(e,l){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const a of e)if(l(a,t++)===!1)break}else l(e,t)}const I=(e,l)=>e===l,f=e=>n.createElement(n.Fragment,null,e.children);f.displayName="Switch_Case";const p=e=>n.createElement(n.Fragment,null,e.children);p.displayName="Switch_Default";const s=e=>{const{value:l,compare:t=I,children:a,strict:r=!1}=e,i=new Set;let c=null,N=null,F=!1;return v(a,(u,h)=>{if(!n.isValidElement(u))throw new Error(`Switch Children only accepts valid React elements at index ${h}`);const d=u.type;if(d.displayName===f.displayName){const m=u.props;if(i.has(m.value))throw new Error(`Switch found duplicate Case value at index ${h}: ${JSON.stringify(m.value)}${r?" (detected in strict mode)":""}`);if(i.add(m.value),!c&&t(l,m.value)&&(c=m.children,r===!1))return!1}else if(d.displayName===p.displayName){if(F)throw new Error(`Switch can only have one Default child at index ${h}`);if(F=!0,N=u.props.children,!r&&c)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(d.displayName||d.name||d)} at index ${h}`)}),n.createElement(n.Fragment,null,c??N)};s.displayName="Switch",s.Case=f,s.Default=p,s.createTyped=function(){return{Switch:s,Case:f,Default:p}};const y=e=>n.createElement(n.Fragment,null,e.children),E=({children:e})=>n.createElement(n.Fragment,null,e),w=e=>n.createElement(n.Fragment,null,e.children);y.displayName="If_Then",E.displayName="If_Else",w.displayName="If_ElseIf";const o=({condition:e,children:l})=>{let t=null,a=null;const r=[];if(n.Children.forEach(l,i=>{if(!n.isValidElement(i))throw new Error("If component only accepts valid React elements");const c=i.type;if(c.displayName===y.displayName){if(t)throw new Error("If component can only have one Then child");t=i}else if(c.displayName===w.displayName)r.push(i);else if(c.displayName===E.displayName){if(a)throw new Error("If component can only have one Else child");a=i}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(c.displayName||c.name||c)}`)}),e)return t?n.createElement(n.Fragment,null,t.props.children):null;for(const i of r)if(i.props.condition)return n.createElement(n.Fragment,null,i.props.children);return a?n.createElement(n.Fragment,null,a.props.children):null};o.displayName="If",o.Then=y,o.ElseIf=w,o.Else=E,o.createTyped=function(){return{If:o,Then:y,ElseIf:w,Else:E}};const C=({condition:e,children:l})=>e?n.createElement(n.Fragment,null,l):null,T=({condition:e,children:l})=>e===!1?n.createElement(n.Fragment,null,l):null,k=({all:e,any:l,none:t,children:a,fallback:r})=>g(()=>(e&&(l||t)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||l&&l.length>0&&l.some(Boolean)||t&&t.length>0&&t.every(i=>!i))),[e,l,t])?n.createElement(n.Fragment,null,a):n.createElement(n.Fragment,null,r||null),x=({data:e,transform:l,render:t,fallback:a})=>{const r=g(()=>l.reduce((i,c)=>c(i),e),[e,l]);return r==null?n.createElement(n.Fragment,null,a||null):n.createElement(n.Fragment,null,t(r))},$=e=>{const{children:l,h:t,w:a,size:r,height:i,width:c}=e;return n.createElement("div",{style:{width:r||a||c,height:r||t||i}},l)};function b(e){const{items:l,renderItem:t,filter:a}=e;return l?n.createElement(S,null,l.map((r,i)=>a&&!a(r)?null:t(r,i))):(console.error("ArrayRender: items is null"),null)}const D=({let:e,props:l,children:t,fallback:a})=>{const r=g(()=>typeof e=="function"?e(l):e,[e,l]);return!t||!Object.keys(r).length?n.createElement(n.Fragment,null,a||null):n.createElement(n.Fragment,null,t(r))};export{b as ArrayRender,T as False,o as If,x as Pipe,D as Scope,$ as SizeBox,s as Switch,C as True,k as When,v as childrenLoop};
|
package/package.json
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { FC, ReactNode, useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the `Scope` component.
|
|
5
|
+
*
|
|
6
|
+
* @interface ScopeProps
|
|
7
|
+
* @property {Record<string, any> | ((props: any) => Record<string, any>)} let - An object or function defining local variables for the scope.
|
|
8
|
+
* @property {any} [props] - Optional props passed to the let function.
|
|
9
|
+
* @property {(scope: Record<string, any>) => ReactNode} [children] - Render function to access the scope variables.
|
|
10
|
+
* @property {ReactNode} [fallback] - Content to render if children or scope is empty.
|
|
11
|
+
*/
|
|
12
|
+
export interface ScopeProps {
|
|
13
|
+
/**
|
|
14
|
+
* @description_en An object or function defining local variables for the scope.
|
|
15
|
+
* @description_zh 定义作用域局部变量的对象或函数。
|
|
16
|
+
*/
|
|
17
|
+
let: Record<string, any> | ((props: any) => Record<string, any>);
|
|
18
|
+
/**
|
|
19
|
+
* @description_en Optional props passed to the let function.
|
|
20
|
+
* @description_zh 传递给 let 函数的可选属性。
|
|
21
|
+
* @optional
|
|
22
|
+
*/
|
|
23
|
+
props?: any;
|
|
24
|
+
/**
|
|
25
|
+
* @description_en Render function to access the scope variables.
|
|
26
|
+
* @description_zh 访问作用域变量的渲染函数。
|
|
27
|
+
* @optional
|
|
28
|
+
*/
|
|
29
|
+
children?: (scope: Record<string, any>) => ReactNode;
|
|
30
|
+
/**
|
|
31
|
+
* @description_en Content to render if children or scope is empty.
|
|
32
|
+
* @description_zh 如果 children 或 scope 为空时渲染的内容。
|
|
33
|
+
* @optional
|
|
34
|
+
* @default null
|
|
35
|
+
*/
|
|
36
|
+
fallback?: ReactNode;
|
|
37
|
+
}
|
|
38
|
+
/*
|
|
39
|
+
设计原因
|
|
40
|
+
避免在组件外定义临时状态或计算。
|
|
41
|
+
声明式定义局部变量,增强代码自包含性。
|
|
42
|
+
适合表单、计算密集型渲染等场景。
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* @description_zh 一个声明式组件,为子节点提供局部作用域,简化临时状态或上下文管理。
|
|
46
|
+
* @description_en A declarative component that provides a local scope for children, simplifying temporary state or context management.
|
|
47
|
+
* @component
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* <Scope let={{ count: 1, text: "Hello" }}>
|
|
51
|
+
* {({ count, text }) => <div>{text} {count}</div>}
|
|
52
|
+
* </Scope>
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <Scope let={(props) => ({ total: props.items.length })} props={{ items: [1, 2] }} fallback={<div>Empty</div>}>
|
|
58
|
+
* {({ total }) => <div>Total: {total}</div>}
|
|
59
|
+
* </Scope>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export const Scope: FC<ScopeProps> = ({
|
|
63
|
+
let: letValue,
|
|
64
|
+
props,
|
|
65
|
+
children,
|
|
66
|
+
fallback,
|
|
67
|
+
}) => {
|
|
68
|
+
const scope = useMemo(() => {
|
|
69
|
+
return typeof letValue === "function" ? letValue(props) : letValue;
|
|
70
|
+
}, [letValue, props]);
|
|
71
|
+
|
|
72
|
+
if (!children || !Object.keys(scope).length) {
|
|
73
|
+
return <>{fallback || null}</>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return <>{children(scope)}</>;
|
|
77
|
+
};
|
package/src/Common/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { type FC } from "react";
|
|
1
|
+
import React, { type FC, type ReactNode } from "react";
|
|
2
2
|
|
|
3
3
|
export interface IfProps {
|
|
4
4
|
condition: boolean;
|
|
@@ -102,3 +102,19 @@ If.createTyped = function () {
|
|
|
102
102
|
Else: (props: ElseProps) => React.ReactElement;
|
|
103
103
|
};
|
|
104
104
|
};
|
|
105
|
+
|
|
106
|
+
export interface TrueProps {
|
|
107
|
+
condition: boolean;
|
|
108
|
+
children?: ReactNode;
|
|
109
|
+
}
|
|
110
|
+
export const True: FC<TrueProps> = ({ condition, children }) => {
|
|
111
|
+
return condition ? <>{children}</> : null;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export interface FalseProps {
|
|
115
|
+
condition: boolean;
|
|
116
|
+
children?: ReactNode;
|
|
117
|
+
}
|
|
118
|
+
export const False: FC<FalseProps> = ({ condition, children }) => {
|
|
119
|
+
return condition === false ? <>{children}</> : null;
|
|
120
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { FC, ReactNode, useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the `Pipe` component.
|
|
5
|
+
*
|
|
6
|
+
* @interface PipeProps
|
|
7
|
+
* @property {any} data - The initial data to process.
|
|
8
|
+
* @property {((input: any) => any)[]} transform - Array of functions to transform the data.
|
|
9
|
+
* @property {(result: any) => ReactNode} render - Function to render the transformed data.
|
|
10
|
+
* @property {ReactNode} [fallback] - Content to render if the result is null or undefined.
|
|
11
|
+
*/
|
|
12
|
+
export interface PipeProps {
|
|
13
|
+
/**
|
|
14
|
+
* @description_en The initial data to process.
|
|
15
|
+
* @description_zh 要处理的初始数据。
|
|
16
|
+
*/
|
|
17
|
+
data: any;
|
|
18
|
+
/**
|
|
19
|
+
* @description_en Array of functions to transform the data.
|
|
20
|
+
* @description_zh 转换数据的函数数组。
|
|
21
|
+
*/
|
|
22
|
+
transform: ((input: any) => any)[];
|
|
23
|
+
/**
|
|
24
|
+
* @description_en Function to render the transformed data.
|
|
25
|
+
* @description_zh 渲染转换后数据的函数。
|
|
26
|
+
*/
|
|
27
|
+
render: (result: any) => ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* @description_en Content to render if the result is null or undefined.
|
|
30
|
+
* @description_zh 如果结果为 null 或 undefined 时渲染的内容。
|
|
31
|
+
* @optional
|
|
32
|
+
* @default null
|
|
33
|
+
*/
|
|
34
|
+
fallback?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @description_zh 一个声明式组件,用于通过管道式处理数据并渲染,简化多步骤数据转换。
|
|
39
|
+
* @description_en A declarative component for processing data through a pipeline and rendering, simplifying multi-step data transformations.
|
|
40
|
+
* @component
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <Pipe
|
|
44
|
+
* data={users}
|
|
45
|
+
* transform={[
|
|
46
|
+
* (data) => data.filter(user => user.active),
|
|
47
|
+
* (data) => data.map(user => user.name)
|
|
48
|
+
* ]}
|
|
49
|
+
* render={(names) => <div>{names.join(", ")}</div>}
|
|
50
|
+
* fallback={<div>No Data</div>}
|
|
51
|
+
* />
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const Pipe: FC<PipeProps> = ({ data, transform, render, fallback }) => {
|
|
55
|
+
const result = useMemo(() => {
|
|
56
|
+
return transform.reduce((acc, fn) => fn(acc), data);
|
|
57
|
+
}, [data, transform]);
|
|
58
|
+
|
|
59
|
+
if (result == null) {
|
|
60
|
+
return <>{fallback || null}</>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return <>{render(result)}</>;
|
|
64
|
+
};
|