@wwog/react 1.0.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/LICENSE +21 -0
- package/README.md +133 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.js +5 -0
- package/package.json +36 -0
- package/src/Common/ArrayRender.tsx +31 -0
- package/src/Common/SizeBox.tsx +22 -0
- package/src/Common/index.ts +2 -0
- package/src/ProcessControl/If.tsx +109 -0
- package/src/ProcessControl/Switch.tsx +124 -0
- package/src/ProcessControl/index.ts +2 -0
- package/src/index copy.ts +3 -0
- package/src/index.ts +3 -0
- package/src/utils/index.ts +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) github/wwog
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# @wwog/react
|
|
2
|
+
|
|
3
|
+
一个实用的 React 组件库,提供声明式流程控制组件和常用 UI 工具组件,使您的 React 代码更加简洁和可读。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@wwog/react)
|
|
6
|
+
[](https://nodejs.org/api/esm.html)
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# 使用 npm
|
|
12
|
+
npm install @wwog/react
|
|
13
|
+
|
|
14
|
+
# 使用 yarn
|
|
15
|
+
yarn add @wwog/react
|
|
16
|
+
|
|
17
|
+
# 使用 pnpm
|
|
18
|
+
pnpm add @wwog/react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 功能特性
|
|
22
|
+
|
|
23
|
+
- **仅 ESModule**:现代化的模块系统支持
|
|
24
|
+
- **完全类型支持**:用 TypeScript 编写,提供完整的类型定义
|
|
25
|
+
- **零依赖**:仅依赖 React 和 React DOM 作为 peer dependencies
|
|
26
|
+
- **声明式流程控制**:JSX 风格的条件渲染和流程控制组件
|
|
27
|
+
- **通用工具组件**:简单实用的常见 UI 工具组件
|
|
28
|
+
|
|
29
|
+
## 组件和用法
|
|
30
|
+
|
|
31
|
+
### 流程控制组件
|
|
32
|
+
|
|
33
|
+
#### `<If>`, `<Then>`, `<Else>`, `<ElseIf>`
|
|
34
|
+
|
|
35
|
+
声明式的条件渲染组件,类似于 if-else 语句,但在 JSX 中使用。
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { If, Then, Else, ElseIf } from '@wwog/react';
|
|
39
|
+
|
|
40
|
+
function Example({ count }) {
|
|
41
|
+
return (
|
|
42
|
+
<If condition={count > 10}>
|
|
43
|
+
<Then>
|
|
44
|
+
<p>Count is greater than 10</p>
|
|
45
|
+
</Then>
|
|
46
|
+
<ElseIf condition={count > 5}>
|
|
47
|
+
<p>Count is greater than 5</p>
|
|
48
|
+
</ElseIf>
|
|
49
|
+
<Else>
|
|
50
|
+
<p>Count is 5 or less</p>
|
|
51
|
+
</Else>
|
|
52
|
+
</If>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### `<Switch>`, `<Case>`, `<Default>`
|
|
58
|
+
|
|
59
|
+
类似于 JavaScript 的 switch 语句,但更具声明性和类型安全性。
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { Switch, Case, Default } from '@wwog/react';
|
|
63
|
+
|
|
64
|
+
function Example({ status }) {
|
|
65
|
+
return (
|
|
66
|
+
<Switch value={status}>
|
|
67
|
+
<Case value="loading">
|
|
68
|
+
<Loading />
|
|
69
|
+
</Case>
|
|
70
|
+
<Case value="success">
|
|
71
|
+
<Success />
|
|
72
|
+
</Case>
|
|
73
|
+
<Case value="error">
|
|
74
|
+
<Error />
|
|
75
|
+
</Case>
|
|
76
|
+
<Default>
|
|
77
|
+
<p>Unknown status</p>
|
|
78
|
+
</Default>
|
|
79
|
+
</Switch>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 通用组件
|
|
85
|
+
|
|
86
|
+
#### `<ArrayRender>`
|
|
87
|
+
|
|
88
|
+
高效渲染数组数据的工具组件,支持过滤和自定义渲染。
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { ArrayRender } from '@wwog/react';
|
|
92
|
+
|
|
93
|
+
function UserList({ users }) {
|
|
94
|
+
return (
|
|
95
|
+
<ArrayRender
|
|
96
|
+
items={users}
|
|
97
|
+
filter={(user) => user.active}
|
|
98
|
+
renderItem={(user, index) => (
|
|
99
|
+
<div key={user.id}>
|
|
100
|
+
{index + 1}. {user.name}
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `<SizeBox>`
|
|
109
|
+
|
|
110
|
+
创建固定尺寸的容器,用于布局调整和间距控制。
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { SizeBox } from '@wwog/react';
|
|
114
|
+
|
|
115
|
+
function Layout() {
|
|
116
|
+
return (
|
|
117
|
+
<div>
|
|
118
|
+
<Header />
|
|
119
|
+
{/* 创建垂直间距 */}
|
|
120
|
+
<SizeBox height={20} />
|
|
121
|
+
<Content />
|
|
122
|
+
{/* 创建具有固定尺寸的容器 */}
|
|
123
|
+
<SizeBox width={200} height={150}>
|
|
124
|
+
<SideContent />
|
|
125
|
+
</SizeBox>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
package/dist/index.d.mts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wwog/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"author": "wwog",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.mts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/wwog/react",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "unbuild",
|
|
19
|
+
"format": "biome format --write src",
|
|
20
|
+
"check": "biome check --apply src",
|
|
21
|
+
"test": "pnpm check && pnpm test:types && vitest run --coverage",
|
|
22
|
+
"test:types": "tsc --noEmit --skipLibCheck",
|
|
23
|
+
"preinstall": "npx only-allow pnpm"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@biomejs/biome": "^1.9.4",
|
|
27
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
28
|
+
"typescript": "^5.8.3",
|
|
29
|
+
"unbuild": "^3.5.0",
|
|
30
|
+
"vitest": "^3.1.1"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">= 20.0.0",
|
|
34
|
+
"pnpm": ">=8.15.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Fragment, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region component Types
|
|
4
|
+
export interface ArrayRenderProps<T> {
|
|
5
|
+
items: T[];
|
|
6
|
+
renderItem: (item: T, index: number) => React.ReactNode;
|
|
7
|
+
filter?: (item: T) => boolean;
|
|
8
|
+
}
|
|
9
|
+
//#endregion component Types
|
|
10
|
+
|
|
11
|
+
//#region component
|
|
12
|
+
export function ArrayRender<T>(props: ArrayRenderProps<T>): ReactNode {
|
|
13
|
+
const { items, renderItem, filter } = props;
|
|
14
|
+
|
|
15
|
+
if (!items) {
|
|
16
|
+
console.error("ArrayRender: items is null");
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Fragment>
|
|
22
|
+
{items.map((item, index) => {
|
|
23
|
+
if (filter && !filter(item)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return renderItem(item, index);
|
|
27
|
+
})}
|
|
28
|
+
</Fragment>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
//#endregion component
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface SizeBoxProps {
|
|
4
|
+
size?: number | string;
|
|
5
|
+
height?: number | string;
|
|
6
|
+
width?: number | string;
|
|
7
|
+
h?: number | string;
|
|
8
|
+
w?: number | string;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description SizeBox 组件用于设置一个固定大小的盒子
|
|
13
|
+
*/
|
|
14
|
+
export const SizeBox: FC<SizeBoxProps> = (props) => {
|
|
15
|
+
const { children, h, w, size, height, width } = props;
|
|
16
|
+
|
|
17
|
+
const widthValue = size || w || width;
|
|
18
|
+
const heightValue = size || h || height;
|
|
19
|
+
return (
|
|
20
|
+
<div style={{ width: widthValue, height: heightValue }}>{children}</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { type FC } from "react";
|
|
2
|
+
|
|
3
|
+
export interface IfProps {
|
|
4
|
+
condition: boolean;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ThenProps {
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ElseProps {
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ElseIfProps {
|
|
17
|
+
condition: boolean;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Then: FC<ThenProps> = (props) => {
|
|
22
|
+
return <>{props.children}</>;
|
|
23
|
+
};
|
|
24
|
+
export const Else: FC<ElseProps> = ({ children }) => {
|
|
25
|
+
return <>{children}</>;
|
|
26
|
+
};
|
|
27
|
+
export const ElseIf: FC<ElseIfProps> = (props) => {
|
|
28
|
+
return <>{props.children}</>;
|
|
29
|
+
};
|
|
30
|
+
Then.displayName = "If_Then";
|
|
31
|
+
Else.displayName = "If_Else";
|
|
32
|
+
ElseIf.displayName = "If_ElseIf";
|
|
33
|
+
|
|
34
|
+
export const If = ({
|
|
35
|
+
condition,
|
|
36
|
+
children,
|
|
37
|
+
}: IfProps): React.ReactElement | null => {
|
|
38
|
+
let thenChild: React.ReactElement<ThenProps> | null =
|
|
39
|
+
null as React.ReactElement<ThenProps> | null;
|
|
40
|
+
let elseChild: React.ReactElement<ElseProps> | null = null;
|
|
41
|
+
const elseIfChildren: React.ReactElement<ElseIfProps>[] = [];
|
|
42
|
+
|
|
43
|
+
React.Children.forEach(children, (child) => {
|
|
44
|
+
if (!React.isValidElement(child)) {
|
|
45
|
+
throw new Error("If component only accepts valid React elements");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const type = child.type as any;
|
|
49
|
+
|
|
50
|
+
if (type.displayName === Then.displayName) {
|
|
51
|
+
if (thenChild) {
|
|
52
|
+
throw new Error("If component can only have one Then child");
|
|
53
|
+
}
|
|
54
|
+
thenChild = child as React.ReactElement<ThenProps>;
|
|
55
|
+
} else if (type.displayName === ElseIf.displayName) {
|
|
56
|
+
elseIfChildren.push(child as React.ReactElement<ElseIfProps>);
|
|
57
|
+
} else if (type.displayName === Else.displayName) {
|
|
58
|
+
if (elseChild) {
|
|
59
|
+
throw new Error("If component can only have one Else child");
|
|
60
|
+
}
|
|
61
|
+
elseChild = child as React.ReactElement<ElseProps>;
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(
|
|
65
|
+
type.displayName || type.name || type
|
|
66
|
+
)}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (condition) {
|
|
72
|
+
return thenChild ? <>{thenChild.props.children}</> : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const matchedElseIf = elseIfChildren.find((child) => {
|
|
76
|
+
const elseIfProps = child.props as ElseIfProps;
|
|
77
|
+
return elseIfProps.condition;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (matchedElseIf) {
|
|
81
|
+
return <>{matchedElseIf.props.children}</>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (elseChild) {
|
|
85
|
+
return elseChild ? (
|
|
86
|
+
<>{(elseChild as React.ReactElement<ElseProps>).props.children}</>
|
|
87
|
+
) : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
If.displayName = "If";
|
|
94
|
+
If.Then = Then;
|
|
95
|
+
If.ElseIf = ElseIf;
|
|
96
|
+
If.Else = Else;
|
|
97
|
+
If.createTyped = function () {
|
|
98
|
+
return {
|
|
99
|
+
If,
|
|
100
|
+
Then,
|
|
101
|
+
ElseIf,
|
|
102
|
+
Else,
|
|
103
|
+
} as {
|
|
104
|
+
If: (props: IfProps) => React.ReactElement | null;
|
|
105
|
+
Then: (props: ThenProps) => React.ReactElement;
|
|
106
|
+
ElseIf: (props: ElseIfProps) => React.ReactElement;
|
|
107
|
+
Else: (props: ElseProps) => React.ReactElement;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { childrenLoop } from "../utils";
|
|
3
|
+
|
|
4
|
+
export interface SwitchProps<T> {
|
|
5
|
+
value: T;
|
|
6
|
+
/**
|
|
7
|
+
* @description 可选的比对函数,默认() => a===b
|
|
8
|
+
* @description_en optional compare function, default to () => a === b
|
|
9
|
+
**/
|
|
10
|
+
compare?: (a: T, b: T) => boolean;
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* @description 是否严格模式,默认 false.建议跟随开发环境变化,严格模式下,会循环所有节点来提供更多的错误提示
|
|
14
|
+
* @description_en strict mode, default to false. It is recommended to follow the development environment changes. In strict mode, all nodes will be looped to provide more error prompts
|
|
15
|
+
* @extra
|
|
16
|
+
* 1.严格模式下,无论什么情况都会循环所有节点,以确保所有的 case 和 default 都能被检查到
|
|
17
|
+
* 2.非严格模式下,如果有一个 case 匹配成功,就不会继续循环
|
|
18
|
+
* @extra_en
|
|
19
|
+
* 1. In strict mode, all nodes will be looped regardless of the situation to ensure that all cases and defaults can be checked
|
|
20
|
+
* 2. In non-strict mode, if a case matches successfully, it will not continue to loop
|
|
21
|
+
*/
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SwitchCaseProps<T> {
|
|
26
|
+
value: T;
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SwitchDefaultProps {
|
|
31
|
+
children?: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const defaultCompare = <T,>(a: T, b: T): boolean => {
|
|
35
|
+
return a === b;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const Case = <T,>(props: SwitchCaseProps<T>): React.ReactElement => {
|
|
39
|
+
return <>{props.children}</>;
|
|
40
|
+
};
|
|
41
|
+
Case.displayName = "Switch_Case";
|
|
42
|
+
|
|
43
|
+
const Default = (props: SwitchDefaultProps): React.ReactElement => {
|
|
44
|
+
return <>{props.children}</>;
|
|
45
|
+
};
|
|
46
|
+
Default.displayName = "Switch_Default";
|
|
47
|
+
|
|
48
|
+
type SwitchChildType = typeof Case | typeof Default;
|
|
49
|
+
/**
|
|
50
|
+
* @description Switch 组件用于根据传入的 value 渲染不同的子组件
|
|
51
|
+
* @description_en The Switch component is used to render different child components based on the passed value
|
|
52
|
+
*/
|
|
53
|
+
export const Switch = <T,>(
|
|
54
|
+
props: SwitchProps<T>
|
|
55
|
+
): React.ReactElement | null => {
|
|
56
|
+
const { value, compare = defaultCompare, children, strict = false } = props;
|
|
57
|
+
const seenValues = new Set<T>();
|
|
58
|
+
let matchedChildren: React.ReactNode = null;
|
|
59
|
+
let defaultChild: React.ReactNode = null;
|
|
60
|
+
let hasDefault = false;
|
|
61
|
+
|
|
62
|
+
childrenLoop(children, (child, index) => {
|
|
63
|
+
if (!React.isValidElement(child)) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Switch Children only accepts valid React elements at index ${index}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const type = child.type as SwitchChildType;
|
|
69
|
+
|
|
70
|
+
if (type.displayName === Case.displayName) {
|
|
71
|
+
const caseProps = child.props as SwitchCaseProps<T>;
|
|
72
|
+
|
|
73
|
+
if (seenValues.has(caseProps.value)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Switch found duplicate Case value at index ${index}: ${JSON.stringify(
|
|
76
|
+
caseProps.value
|
|
77
|
+
)}${strict ? " (detected in strict mode)" : ""}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
seenValues.add(caseProps.value);
|
|
81
|
+
|
|
82
|
+
if (!matchedChildren && compare(value, caseProps.value)) {
|
|
83
|
+
matchedChildren = caseProps.children;
|
|
84
|
+
if (strict === false) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else if (type.displayName === Default.displayName) {
|
|
89
|
+
if (hasDefault) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Switch can only have one Default child at index ${index}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
hasDefault = true;
|
|
95
|
+
defaultChild = (child.props as SwitchDefaultProps).children;
|
|
96
|
+
if (!strict && matchedChildren) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(
|
|
102
|
+
type.displayName || type.name || type
|
|
103
|
+
)} at index ${index}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return <>{matchedChildren ?? defaultChild}</>;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
Switch.displayName = "Switch";
|
|
112
|
+
Switch.Case = Case;
|
|
113
|
+
Switch.Default = Default;
|
|
114
|
+
Switch.createTyped = function <T>() {
|
|
115
|
+
return {
|
|
116
|
+
Switch: Switch<T>,
|
|
117
|
+
Case: Case<T>,
|
|
118
|
+
Default: Default,
|
|
119
|
+
} as {
|
|
120
|
+
Switch: (props: SwitchProps<T>) => React.ReactElement | null;
|
|
121
|
+
Case: (props: SwitchCaseProps<T>) => React.ReactElement;
|
|
122
|
+
Default: (props: SwitchDefaultProps) => React.ReactElement;
|
|
123
|
+
}
|
|
124
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
|
|
5
|
+
* @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
|
|
6
|
+
*/
|
|
7
|
+
export function childrenLoop(
|
|
8
|
+
children: React.ReactNode | undefined,
|
|
9
|
+
callback: (child: React.ReactNode, index: number) => boolean | void
|
|
10
|
+
): void {
|
|
11
|
+
if (children === undefined) return;
|
|
12
|
+
let index = 0;
|
|
13
|
+
if (Array.isArray(children)) {
|
|
14
|
+
for (const child of children) {
|
|
15
|
+
const shouldContinue = callback(child, index++);
|
|
16
|
+
if (shouldContinue === false) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
callback(children, index);
|
|
22
|
+
}
|
|
23
|
+
}
|