conditional-selection 1.1.4 → 1.1.6

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 (3) hide show
  1. package/README.md +356 -124
  2. package/README.zh-CN.md +197 -0
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,163 +1,395 @@
1
- # Conditional Selection 组件库
1
+ # conditional-selection
2
2
 
3
- ## 简介
4
- 本项目为一个支持条件树编辑的 React 组件库,支持递归嵌套、条件分组、动态增删、禁用控制等功能,适用于复杂表单、规则配置等场景。
3
+ <p>
4
+ <a href="#english">English</a> | <a href="#中文">中文</a>
5
+ </p>
5
6
 
6
- ## 主要功能
7
- - 条件树递归编辑
8
- - 条件分组(AND/OR)切换
9
- - 条件项动态增删
10
- - 层级/数量/禁用等参数控制
11
- - 插槽自定义条件行内容
12
- - 状态管理采用 useImmer,数据流统一
7
+ <h2 id="english">English</h2>
13
8
 
14
- ## 使用方法
9
+ A lightweight React component library for building conditional rule trees. Supports recursive nesting, AND/OR grouping, dynamic add/remove, and fully customizable condition rows via render props.
15
10
 
16
- ### 基本用法
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install conditional-selection
15
+ ```
16
+
17
+ > **Peer dependencies:** `react >= 16.8.0`, `react-dom >= 16.8.0`
18
+
19
+ ## Quick Start
17
20
 
18
21
  ```tsx
19
- import React, { useMemo, useRef, useState } from 'react';
20
- import { ConditionalSelection } from '../packages/ConditionalSelection';
21
- import type { TConditionalSelection } from '../packages/ConditionalSelection/types';
22
-
23
- const fieldOptions = [
24
- { label: '测试字段1', value: 1 },
25
- { label: '测试字段2', value: 2 },
26
- ];
27
- const functionOptions = [
28
- { label: '等于', value: 1 },
29
- { label: '不等于', value: 2 },
30
- ];
31
- const valueOptions = [
32
- { label: '测试值1', value: 1 },
33
- { label: '测试值2', value: 2 },
34
- ];
35
-
36
- const FlexSelect: React.FC<{ item: TConditionalSelection; onChange: (val: Record<string, any>) => void }> = ({ item, onChange }) => {
37
- // field / function / value 存放在节点的 individual 里
38
- const data = useMemo(() => item.individual ?? {}, [item.individual]);
39
-
40
- const handleFieldChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
41
- const val = e.target.value ? Number(e.target.value) : undefined;
42
- onChange({ field: val, function: '', value: undefined });
43
- };
44
-
45
- const handleFunctionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
46
- const val = e.target.value ? Number(e.target.value) : undefined;
47
- onChange({ ...data, function: val, value: undefined });
48
- };
49
-
50
- const handleValueChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
51
- const val = e.target.value ? Number(e.target.value) : undefined;
52
- onChange({ ...data, value: val });
53
- };
22
+ import { ConditionalSelection } from 'conditional-selection';
23
+ import type { TConditionalSelection } from 'conditional-selection';
24
+
25
+ export default function App() {
26
+ const [rules, setRules] = useState<TConditionalSelection | undefined>();
54
27
 
55
28
  return (
56
- <div style={{ display: 'flex', width: '100%' }}>
57
- <select style={{ width: '33%', marginRight: 16 }} value={data.field ?? ''} onChange={handleFieldChange}>
58
- <option value="">请选择字段</option>
59
- {fieldOptions.map(opt => (
60
- <option key={opt.value} value={opt.value}>{opt.label}</option>
61
- ))}
62
- </select>
63
- {data.field && (
64
- <select style={{ width: '33%', marginRight: 16 }} value={data.function ?? ''} onChange={handleFunctionChange}>
65
- <option value="">请选择函数</option>
66
- {functionOptions.map(opt => (
67
- <option key={opt.value} value={opt.value}>{opt.label}</option>
68
- ))}
69
- </select>
70
- )}
71
- {data.function && (
72
- <select style={{ width: '33%', marginRight: 16 }} value={data.value ?? ''} onChange={handleValueChange}>
73
- <option value="">请选择值</option>
74
- {valueOptions.map(opt => (
75
- <option key={opt.value} value={opt.value}>{opt.label}</option>
76
- ))}
77
- </select>
29
+ <ConditionalSelection
30
+ conditionalRules={rules}
31
+ maxDeep={3}
32
+ onChange={setRules}
33
+ renderConditionRules={(item, change) => (
34
+ <MyConditionRow item={item} onChange={change} />
78
35
  )}
79
- </div>
36
+ />
80
37
  );
81
38
  }
39
+ ```
40
+
41
+ ## Props
42
+
43
+ | Prop | Type | Default | Description |
44
+ |------|------|---------|-------------|
45
+ | `conditionalRules` | `TConditionalSelection \| null` | `undefined` | The condition tree data. Automatically initialized if `null` or `undefined`. |
46
+ | `maxDeep` | `number` | `1` | Maximum recursion depth. Must be greater than `0`. |
47
+ | `disabled` | `boolean \| TConditionalSelectionDisabledProps` | `false` | Disable all or specific operations. |
48
+ | `onChange` | `(value: TConditionalSelection \| undefined) => void` | — | Callback fired when the tree data changes. |
49
+ | `renderConditionRules` | `(item, change) => ReactNode` | — | Render prop for each condition row. Receives the current `INDIVIDUAL` node and an update handler. |
50
+ | `renderCreateCondition` | `(params) => ReactNode` | — | Render prop to customize the "Add condition" button. |
51
+
52
+ ### `disabled` object shape
53
+
54
+ ```ts
55
+ type TConditionalSelectionDisabledProps = {
56
+ addItem?: boolean; // disable adding new conditions
57
+ delItem?: boolean; // disable deleting conditions
58
+ linkChange?: boolean; // disable AND/OR toggle
59
+ };
60
+ ```
61
+
62
+ ## Data Structure
63
+
64
+ ```ts
65
+ type TConditionalSelection<T = any> = {
66
+ _id: string;
67
+ framework: 'group' | 'individual';
68
+ link?: 'and' | 'or';
69
+ group?: TConditionalSelection<T>[];
70
+ individual?: Record<string, any>;
71
+ level: number;
72
+ };
73
+ ```
74
+
75
+ - **`group`** node — contains child nodes and an AND/OR relationship toggle.
76
+ - **`individual`** node — a single condition row; its data lives in the `individual` field.
77
+
78
+ ### Example Output
79
+
80
+ **Flat structure** — two `individual` conditions joined by AND:
81
+
82
+ ```json
83
+ {
84
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
85
+ "framework": "group",
86
+ "level": 0,
87
+ "link": "and",
88
+ "group": [
89
+ {
90
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
91
+ "framework": "individual",
92
+ "individual": { "field": 1, "function": 1, "value": 1 },
93
+ "level": 1
94
+ },
95
+ {
96
+ "_id": "6c627903-b970-43a8-a3f5-f5aba89f2200",
97
+ "framework": "individual",
98
+ "individual": { "field": 1, "function": 1, "value": 1 },
99
+ "level": 1
100
+ }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ **Nested structure** — an `individual` AND a nested `group` (OR) at level 1:
106
+
107
+ ```json
108
+ {
109
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
110
+ "framework": "group",
111
+ "level": 0,
112
+ "link": "and",
113
+ "group": [
114
+ {
115
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
116
+ "framework": "individual",
117
+ "individual": { "field": 1, "function": 1, "value": 1 },
118
+ "level": 1
119
+ },
120
+ {
121
+ "_id": "2f808fe6-4dd8-488d-9b79-d2888915fddc",
122
+ "framework": "group",
123
+ "level": 1,
124
+ "link": "or",
125
+ "group": [
126
+ {
127
+ "_id": "17336bf2-2885-4350-8055-233b0058ec39",
128
+ "framework": "individual",
129
+ "individual": { "field": 1, "function": 1, "value": 1 },
130
+ "level": 2
131
+ },
132
+ {
133
+ "_id": "6e21bd9a-f994-474a-bba5-d77ac9013a65",
134
+ "framework": "individual",
135
+ "individual": { "field": 1, "function": 1, "value": 1 },
136
+ "level": 2
137
+ }
138
+ ]
139
+ }
140
+ ]
141
+ }
142
+ ```
143
+
144
+ ## Accessing Data via Ref
145
+
146
+ ```tsx
147
+ const ref = useRef<any>(null);
148
+
149
+ <ConditionalSelection ref={ref} ... />
150
+
151
+ // Returns a deep clone of the current tree
152
+ const data = await ref.current.getConditionalSelectionData();
153
+ ```
154
+
155
+ ## Custom Condition Row Example
156
+
157
+ ```tsx
158
+ const MyConditionRow = ({ item, onChange }) => {
159
+ const data = item.individual ?? {};
160
+ return (
161
+ <div style={{ display: 'flex', gap: 8 }}>
162
+ <select value={data.field ?? ''} onChange={e => onChange({ field: e.target.value })}>
163
+ <option value="">Select field</option>
164
+ <option value="name">Name</option>
165
+ </select>
166
+ <select value={data.operator ?? ''} onChange={e => onChange({ ...data, operator: e.target.value })}>
167
+ <option value="">Select operator</option>
168
+ <option value="eq">Equals</option>
169
+ <option value="ne">Not equals</option>
170
+ </select>
171
+ <input
172
+ value={data.value ?? ''}
173
+ onChange={e => onChange({ ...data, value: e.target.value })}
174
+ placeholder="Value"
175
+ />
176
+ </div>
177
+ );
178
+ };
179
+ ```
180
+
181
+ ## Performance Tips
182
+
183
+ - Wrap handlers and render props with `useCallback` to avoid unnecessary re-renders.
184
+ - Use [React Profiler](https://react.dev/reference/react/Profiler) for deeper performance analysis.
185
+
186
+ ## Contributing
187
+
188
+ ```bash
189
+ git clone https://github.com/1512-Yolo/conditional-selection.git
190
+ cd conditional-selection
191
+ npm install
192
+ npm run dev
193
+ ```
194
+
195
+ Components are exported from `packages/index.ts`. Styles are written in Less (`packages/ConditionalSelection/index.less`).
196
+
197
+ ## License
198
+
199
+ MIT
200
+
201
+ ---
202
+
203
+ <h2 id="中文">中文</h2>
204
+
205
+ 一个轻量级的 React 条件规则树组件库,支持递归嵌套、AND/OR 分组、动态增删,以及通过 render props 完全自定义条件行内容。
206
+
207
+ ## 安装
208
+
209
+ ```bash
210
+ npm install conditional-selection
211
+ ```
212
+
213
+ > **同级依赖(peerDependencies):** `react >= 16.8.0`,`react-dom >= 16.8.0`
214
+
215
+ ## 快速开始
216
+
217
+ ```tsx
218
+ import { ConditionalSelection } from 'conditional-selection';
219
+ import type { TConditionalSelection } from 'conditional-selection';
82
220
 
83
221
  export default function App() {
84
- const refConditionalSelection = useRef<any>(null);
85
222
  const [rules, setRules] = useState<TConditionalSelection | undefined>();
86
- const [maxDeep, setMaxLevel] = useState(3);
87
- const [disabled, setDisabled] = useState(false);
88
- const [disabledConfig, setDisabledConfig] = useState({
89
- addItem: false,
90
- delItem: false,
91
- linkChange: false,
92
- });
93
-
94
- const getResult = async () => {
95
- const data = await refConditionalSelection.current.getConditionalSelectionData()
96
- console.log('当前数据:', data);
97
- }
98
223
 
99
224
  return (
100
- <ConditionalSelection
101
- ref={refConditionalSelection}
102
- conditionalRules={rules}
103
- maxDeep={maxDeep}
104
- disabled={disabled || disabledConfig}
105
- onChange={setRules}
106
- renderConditionRules={(item, change) => (
107
- <FlexSelect
108
- item={item}
109
- onChange={val => change(val)}
110
- />
111
- )}
112
- />
225
+ <ConditionalSelection
226
+ conditionalRules={rules}
227
+ maxDeep={3}
228
+ onChange={setRules}
229
+ renderConditionRules={(item, change) => (
230
+ <MyConditionRow item={item} onChange={change} />
231
+ )}
232
+ />
113
233
  );
114
234
  }
115
235
  ```
116
236
 
117
- ### 参数说明
118
- - `conditionalRules`:条件树数据(可为 null/undefined,自动初始化)
119
- - `maxDeep`:最大递归层级(默认 1,需大于 0)
120
- - `disabled`:是否禁用全部/部分操作(可传布尔或对象)
121
- - `onChange`:数据变更回调
122
- - `renderConditionRules`:插槽,渲染每个条件行内容,参数为当前节点和变更 handler
237
+ ## Props 参数说明
123
238
 
124
- ### 插槽自定义
239
+ | 参数 | 类型 | 默认值 | 说明 |
240
+ |------|------|--------|------|
241
+ | `conditionalRules` | `TConditionalSelection \| null` | `undefined` | 条件树数据,传入 `null` 或 `undefined` 时自动初始化 |
242
+ | `maxDeep` | `number` | `1` | 最大递归层级,必须大于 `0` |
243
+ | `disabled` | `boolean \| TConditionalSelectionDisabledProps` | `false` | 禁用全部或部分操作 |
244
+ | `onChange` | `(value: TConditionalSelection \| undefined) => void` | — | 数据变更回调 |
245
+ | `renderConditionRules` | `(item, change) => ReactNode` | — | 条件行渲染插槽,参数为当前 `INDIVIDUAL` 节点和更新 handler |
246
+ | `renderCreateCondition` | `(params) => ReactNode` | — | 自定义"添加条件"按钮的渲染插槽 |
125
247
 
126
- 插槽 `renderConditionRules` 支持自定义条件行内容,推荐用原生表单控件或自定义组件。
248
+ ### `disabled` 对象格式
127
249
 
128
- ```tsx
129
- const FlexSelect = ({ item, onChange }) => (
130
- <div>
131
- {/* 字段选择、函数选择、值选择等 */}
132
- <select ... onChange={...} />
133
- ...
134
- </div>
135
- );
250
+ ```ts
251
+ type TConditionalSelectionDisabledProps = {
252
+ addItem?: boolean; // 禁用添加条件
253
+ delItem?: boolean; // 禁用删除条件
254
+ linkChange?: boolean; // 禁用 AND/OR 切换
255
+ };
256
+ ```
257
+
258
+ ## 数据结构
259
+
260
+ ```ts
261
+ type TConditionalSelection<T = any> = {
262
+ _id: string;
263
+ framework: 'group' | 'individual';
264
+ link?: 'and' | 'or';
265
+ group?: TConditionalSelection<T>[];
266
+ individual?: Record<string, any>;
267
+ level: number;
268
+ };
136
269
  ```
137
270
 
138
- ### 获取数据
271
+ - **`group`** 节点 — 包含子节点,支持 AND/OR 关系切换
272
+ - **`individual`** 节点 — 单个条件行,自定义数据存放在 `individual` 字段中
273
+
274
+ ### 数据示例
139
275
 
140
- 可通过 ref 获取当前条件树数据:
276
+ **扁平结构** 两个 `individual` 条件以 AND 连接:
277
+
278
+ ```json
279
+ {
280
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
281
+ "framework": "group",
282
+ "level": 0,
283
+ "link": "and",
284
+ "group": [
285
+ {
286
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
287
+ "framework": "individual",
288
+ "individual": { "field": 1, "function": 1, "value": 1 },
289
+ "level": 1
290
+ },
291
+ {
292
+ "_id": "6c627903-b970-43a8-a3f5-f5aba89f2200",
293
+ "framework": "individual",
294
+ "individual": { "field": 1, "function": 1, "value": 1 },
295
+ "level": 1
296
+ }
297
+ ]
298
+ }
299
+ ```
300
+
301
+ **嵌套结构** — 一个 `individual` 与一个嵌套 `group`(OR)并列于第 1 层:
302
+
303
+ ```json
304
+ {
305
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
306
+ "framework": "group",
307
+ "level": 0,
308
+ "link": "and",
309
+ "group": [
310
+ {
311
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
312
+ "framework": "individual",
313
+ "individual": { "field": 1, "function": 1, "value": 1 },
314
+ "level": 1
315
+ },
316
+ {
317
+ "_id": "2f808fe6-4dd8-488d-9b79-d2888915fddc",
318
+ "framework": "group",
319
+ "level": 1,
320
+ "link": "or",
321
+ "group": [
322
+ {
323
+ "_id": "17336bf2-2885-4350-8055-233b0058ec39",
324
+ "framework": "individual",
325
+ "individual": { "field": 1, "function": 1, "value": 1 },
326
+ "level": 2
327
+ },
328
+ {
329
+ "_id": "6e21bd9a-f994-474a-bba5-d77ac9013a65",
330
+ "framework": "individual",
331
+ "individual": { "field": 1, "function": 1, "value": 1 },
332
+ "level": 2
333
+ }
334
+ ]
335
+ }
336
+ ]
337
+ }
338
+ ```
339
+
340
+ ## 通过 Ref 获取数据
141
341
 
142
342
  ```tsx
143
- const ref = useRef<any>();
144
- ...
343
+ const ref = useRef<any>(null);
344
+
145
345
  <ConditionalSelection ref={ref} ... />
146
- ...
346
+
347
+ // 返回当前条件树的深拷贝
147
348
  const data = await ref.current.getConditionalSelectionData();
148
349
  ```
149
350
 
351
+ ## 自定义条件行示例
352
+
353
+ ```tsx
354
+ const MyConditionRow = ({ item, onChange }) => {
355
+ const data = item.individual ?? {};
356
+ return (
357
+ <div style={{ display: 'flex', gap: 8 }}>
358
+ <select value={data.field ?? ''} onChange={e => onChange({ field: e.target.value })}>
359
+ <option value="">选择字段</option>
360
+ <option value="name">名称</option>
361
+ </select>
362
+ <select value={data.operator ?? ''} onChange={e => onChange({ ...data, operator: e.target.value })}>
363
+ <option value="">选择操作符</option>
364
+ <option value="eq">等于</option>
365
+ <option value="ne">不等于</option>
366
+ </select>
367
+ <input
368
+ value={data.value ?? ''}
369
+ onChange={e => onChange({ ...data, value: e.target.value })}
370
+ placeholder="输入值"
371
+ />
372
+ </div>
373
+ );
374
+ };
375
+ ```
376
+
150
377
  ## 性能优化建议
151
- - 递归 handler 用 useCallback 包裹,减少无谓重渲染
152
- - 父组件插槽建议也用 useCallback
153
- - 如需进一步性能分析,建议用 React Profiler
378
+
379
+ - handler 和 render props 用 `useCallback` 包裹,避免不必要的重渲染
380
+ - 如需深度性能分析,可借助 [React Profiler](https://react.dev/reference/react/Profiler)
154
381
 
155
382
  ## 贡献与开发
156
383
 
157
- https://github.com/1512-Yolo/conditional-selection.git
158
- 1. 修改源码后可直接运行 `npm run dev` 查看效果
159
- 2. 支持 monorepo 结构,组件统一从 packages/index.ts 导出
160
- 3. 支持 less 样式
384
+ ```bash
385
+ git clone https://github.com/1512-Yolo/conditional-selection.git
386
+ cd conditional-selection
387
+ npm install
388
+ npm run dev
389
+ ```
390
+
391
+ 组件统一从 `packages/index.ts` 导出,样式使用 Less(`packages/ConditionalSelection/index.less`)编写。
161
392
 
162
393
  ## License
394
+
163
395
  MIT
@@ -0,0 +1,197 @@
1
+ # conditional-selection
2
+
3
+ <p>
4
+ <a href="./README.md">English</a> | <a href="./README.zh-CN.md">中文</a>
5
+ </p>
6
+
7
+ 一个轻量级的 React 条件规则树组件库,支持递归嵌套、AND/OR 分组、动态增删,以及通过 render props 完全自定义条件行内容。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ npm install conditional-selection
13
+ ```
14
+
15
+ > **同级依赖(peerDependencies):** `react >= 16.8.0`,`react-dom >= 16.8.0`
16
+
17
+ ## 快速开始
18
+
19
+ ```tsx
20
+ import { ConditionalSelection } from 'conditional-selection';
21
+ import type { TConditionalSelection } from 'conditional-selection';
22
+
23
+ export default function App() {
24
+ const [rules, setRules] = useState<TConditionalSelection | undefined>();
25
+
26
+ return (
27
+ <ConditionalSelection
28
+ conditionalRules={rules}
29
+ maxDeep={3}
30
+ onChange={setRules}
31
+ renderConditionRules={(item, change) => (
32
+ <MyConditionRow item={item} onChange={change} />
33
+ )}
34
+ />
35
+ );
36
+ }
37
+ ```
38
+
39
+ ## Props 参数说明
40
+
41
+ | 参数 | 类型 | 默认值 | 说明 |
42
+ |------|------|--------|------|
43
+ | `conditionalRules` | `TConditionalSelection \| null` | `undefined` | 条件树数据,传入 `null` 或 `undefined` 时自动初始化 |
44
+ | `maxDeep` | `number` | `1` | 最大递归层级,必须大于 `0` |
45
+ | `disabled` | `boolean \| TConditionalSelectionDisabledProps` | `false` | 禁用全部或部分操作 |
46
+ | `onChange` | `(value: TConditionalSelection \| undefined) => void` | — | 数据变更回调 |
47
+ | `renderConditionRules` | `(item, change) => ReactNode` | — | 条件行渲染插槽,参数为当前 `INDIVIDUAL` 节点和更新 handler |
48
+ | `renderCreateCondition` | `(params) => ReactNode` | — | 自定义"添加条件"按钮的渲染插槽 |
49
+
50
+ ### `disabled` 对象格式
51
+
52
+ ```ts
53
+ type TConditionalSelectionDisabledProps = {
54
+ addItem?: boolean; // 禁用添加条件
55
+ delItem?: boolean; // 禁用删除条件
56
+ linkChange?: boolean; // 禁用 AND/OR 切换
57
+ };
58
+ ```
59
+
60
+ ## 数据结构
61
+
62
+ ```ts
63
+ type TConditionalSelection<T = any> = {
64
+ _id: string;
65
+ framework: 'group' | 'individual';
66
+ link?: 'and' | 'or';
67
+ group?: TConditionalSelection<T>[];
68
+ individual?: Record<string, any>;
69
+ level: number;
70
+ };
71
+ ```
72
+
73
+ - **`group`** 节点 — 包含子节点,支持 AND/OR 关系切换
74
+ - **`individual`** 节点 — 单个条件行,自定义数据存放在 `individual` 字段中
75
+
76
+ ### 数据示例
77
+
78
+ **扁平结构** — 两个 `individual` 条件以 AND 连接:
79
+
80
+ ```json
81
+ {
82
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
83
+ "framework": "group",
84
+ "level": 0,
85
+ "link": "and",
86
+ "group": [
87
+ {
88
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
89
+ "framework": "individual",
90
+ "individual": { "field": 1, "function": 1, "value": 1 },
91
+ "level": 1
92
+ },
93
+ {
94
+ "_id": "6c627903-b970-43a8-a3f5-f5aba89f2200",
95
+ "framework": "individual",
96
+ "individual": { "field": 1, "function": 1, "value": 1 },
97
+ "level": 1
98
+ }
99
+ ]
100
+ }
101
+ ```
102
+
103
+ **嵌套结构** — 一个 `individual` 与一个嵌套 `group`(OR)并列于第 1 层:
104
+
105
+ ```json
106
+ {
107
+ "_id": "2dfd8d9d-4bbf-4bc3-aae6-6c747ccbdd10",
108
+ "framework": "group",
109
+ "level": 0,
110
+ "link": "and",
111
+ "group": [
112
+ {
113
+ "_id": "02399b6c-10a3-41d4-9722-cce2db9ae056",
114
+ "framework": "individual",
115
+ "individual": { "field": 1, "function": 1, "value": 1 },
116
+ "level": 1
117
+ },
118
+ {
119
+ "_id": "2f808fe6-4dd8-488d-9b79-d2888915fddc",
120
+ "framework": "group",
121
+ "level": 1,
122
+ "link": "or",
123
+ "group": [
124
+ {
125
+ "_id": "17336bf2-2885-4350-8055-233b0058ec39",
126
+ "framework": "individual",
127
+ "individual": { "field": 1, "function": 1, "value": 1 },
128
+ "level": 2
129
+ },
130
+ {
131
+ "_id": "6e21bd9a-f994-474a-bba5-d77ac9013a65",
132
+ "framework": "individual",
133
+ "individual": { "field": 1, "function": 1, "value": 1 },
134
+ "level": 2
135
+ }
136
+ ]
137
+ }
138
+ ]
139
+ }
140
+ ```
141
+
142
+ ## 通过 Ref 获取数据
143
+
144
+ ```tsx
145
+ const ref = useRef<any>(null);
146
+
147
+ <ConditionalSelection ref={ref} ... />
148
+
149
+ // 返回当前条件树的深拷贝
150
+ const data = await ref.current.getConditionalSelectionData();
151
+ ```
152
+
153
+ ## 自定义条件行示例
154
+
155
+ ```tsx
156
+ const MyConditionRow = ({ item, onChange }) => {
157
+ const data = item.individual ?? {};
158
+ return (
159
+ <div style={{ display: 'flex', gap: 8 }}>
160
+ <select value={data.field ?? ''} onChange={e => onChange({ field: e.target.value })}>
161
+ <option value="">选择字段</option>
162
+ <option value="name">名称</option>
163
+ </select>
164
+ <select value={data.operator ?? ''} onChange={e => onChange({ ...data, operator: e.target.value })}>
165
+ <option value="">选择操作符</option>
166
+ <option value="eq">等于</option>
167
+ <option value="ne">不等于</option>
168
+ </select>
169
+ <input
170
+ value={data.value ?? ''}
171
+ onChange={e => onChange({ ...data, value: e.target.value })}
172
+ placeholder="输入值"
173
+ />
174
+ </div>
175
+ );
176
+ };
177
+ ```
178
+
179
+ ## 性能优化建议
180
+
181
+ - 将 handler 和 render props 用 `useCallback` 包裹,避免不必要的重渲染
182
+ - 如需深度性能分析,可借助 [React Profiler](https://react.dev/reference/react/Profiler)
183
+
184
+ ## 贡献与开发
185
+
186
+ ```bash
187
+ git clone https://github.com/1512-Yolo/conditional-selection.git
188
+ cd conditional-selection
189
+ npm install
190
+ npm run dev
191
+ ```
192
+
193
+ 组件统一从 `packages/index.ts` 导出,样式使用 Less(`packages/ConditionalSelection/index.less`)编写。
194
+
195
+ ## License
196
+
197
+ MIT
package/package.json CHANGED
@@ -12,8 +12,8 @@
12
12
  "vite-plugin-lib-inject-css": "^2.2.2"
13
13
  },
14
14
  "name": "conditional-selection",
15
- "version": "1.1.4",
16
- "description": "",
15
+ "version": "1.1.6",
16
+ "description": "conditional-selection",
17
17
  "main": "dist/ui.cjs.js",
18
18
  "module": "dist/ui.es.js",
19
19
  "types": "dist/types/ui.es.d.ts",