@simpleform/render 3.0.13 → 3.0.14

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 CHANGED
@@ -2,14 +2,10 @@
2
2
 
3
3
  English | [中文说明](./README_CN.md)
4
4
 
5
- [![](https://img.shields.io/badge/version-3.0.13-green)](https://www.npmjs.com/package/@simpleform/render)
5
+ [![](https://img.shields.io/badge/version-3.0.14-green)](https://www.npmjs.com/package/@simpleform/render)
6
6
 
7
7
  > A lightweight dynamic forms engine that makes it easy to dynamically render forms.
8
8
 
9
- * Break Change: Version >= 3.x is recommended, providing a better semanticized `JSON` structure.
10
- - Deprecated ~`properties`~ : Replaced by `widgetList`.
11
- - Deprecated ~`onPropertiesChange`~ : replaced by `onRenderChange`.
12
- - All methods of `useSimpleFormRender()` are changed.
13
9
  ## Introduction
14
10
  - Component Registration(`components`): Registration of form controls and non-form components is required before use, in the case of form controls you need to support the `value` and `onChange` `props` inside the control.
15
11
  - Component Description(`widgetList`): We use a list to describe the interface UI structure, each item in the list represents a component node. Support for node nesting
@@ -105,19 +101,7 @@ export default function FormRender(props: CustomFormRenderProps) {
105
101
  import { Button } from 'antd';
106
102
  import React, { useState } from 'react';
107
103
  import FormRender, { useSimpleForm, useSimpleFormRender } from './form-render';
108
- export default function Demo5(props) {
109
-
110
- const watch = {
111
- 'name2': (newValue, oldValue) => {
112
- console.log(newValue, oldValue)
113
- },
114
- 'name3[0]': (newValue, oldValue) => {
115
- console.log(newValue, oldValue)
116
- },
117
- 'name4': (newValue, oldValue) => {
118
- console.log(newValue, oldValue)
119
- }
120
- }
104
+ export default function Demo(props) {
121
105
 
122
106
  const widgetList = [
123
107
  {
@@ -233,7 +217,7 @@ export default function Demo5(props) {
233
217
  form={form}
234
218
  // formrender={formrender}
235
219
  widgetList={widgetList}
236
- watch={watch} />
220
+ />
237
221
  <div style={{ marginLeft: '120px' }}>
238
222
  <Button onClick={onSubmit}>submit</Button>
239
223
  </div>
@@ -375,7 +359,7 @@ const OptionList = React.forwardRef<HTMLElement, any>((props, ref) => {
375
359
  onChange && onChange(newData);
376
360
  };
377
361
 
378
- const addItem = () => {
362
+ const addItem = async () => {
379
363
  const { error } = await form.validate();
380
364
  if (error) {
381
365
  return;
package/README_CN.md CHANGED
@@ -2,15 +2,10 @@
2
2
 
3
3
  [English](./README.md) | 中文说明
4
4
 
5
- [![](https://img.shields.io/badge/version-3.0.13-green)](https://www.npmjs.com/package/@simpleform/render)
5
+ [![](https://img.shields.io/badge/version-3.0.14-green)](https://www.npmjs.com/package/@simpleform/render)
6
6
 
7
7
  > 轻量级动态表单引擎,实现动态渲染表单很简单.
8
8
 
9
- * Break Change: 建议使用3.x以上版本,提供了更好的语义化`JSON`结构。
10
- - 废弃 ~`properties`~ : 由`widgetList`代替.
11
- - 废弃 ~`onPropertiesChange`~ : 由`onRenderChange`代替.
12
- - `useSimpleFormRender()`的方法全部变更使用方式.
13
-
14
9
  ## 介绍
15
10
  - 组件注册(`components`属性): 使用之前需要注册表单控件和非表单组件,如果是表单控件需要控件内部支持`value`和`onChange`两个`props`.
16
11
  - 组件描述(`widgetList`属性):我们使用列表来描述界面UI结构, 列表中的每一项都表示一个组件节点.支持节点嵌套
@@ -25,556 +20,3 @@ npm install @simpleform/render --save
25
20
  # 或者
26
21
  yarn add @simpleform/render
27
22
  ```
28
-
29
- ## 快速启动
30
-
31
- ### 1.首先注册基本组件(以antd@5.x组件库为例)
32
- ```javascript
33
- // register
34
- import DefaultFormRender, { FormChildren as DefaultFormChildren, FormRenderProps, FormChildrenProps } from '@simpleform/render';
35
- import '@simpleform/render/lib/css/main.css';
36
- import React from 'react';
37
- import dayjs from 'dayjs';
38
- import {
39
- Input,
40
- InputNumber,
41
- Checkbox,
42
- DatePicker,
43
- Mentions,
44
- Radio,
45
- Rate,
46
- Select,
47
- Slider,
48
- Switch,
49
- TimePicker,
50
- TreeSelect,
51
- } from 'antd';
52
-
53
- export * from '@simpleform/render';
54
-
55
- export const widgets = {
56
- "Input": Input,
57
- "Input.TextArea": Input.TextArea,
58
- "Input.Password": Input.Password,
59
- "Input.Search": Input.Search,
60
- "InputNumber": InputNumber,
61
- "Checkbox": Checkbox,
62
- 'Checkbox.Group': Checkbox.Group,
63
- "DatePicker": DatePicker,
64
- "DatePicker.RangePicker": DatePicker.RangePicker,
65
- "Mentions": Mentions,
66
- "Mentions.Option": Mentions.Option,
67
- "Radio": Radio,
68
- "Radio.Group": Radio.Group,
69
- "Radio.Button": Radio.Button,
70
- "Rate": Rate,
71
- "Select": Select,
72
- "Select.Option": Select.Option,
73
- "TreeSelect": TreeSelect,
74
- "Slider": Slider,
75
- "Switch": Switch,
76
- "TimePicker": TimePicker,
77
- "TimePicker.RangePicker": TimePicker.RangePicker
78
- };
79
-
80
- export type CustomFormChildrenProps = FormChildrenProps<any>;
81
- export function FormChildren(props: CustomFormChildrenProps) {
82
- const { components, plugins, ...rest } = props;
83
- return (
84
- <DefaultFormChildren
85
- options={{ props: { autoComplete: 'off' } }}
86
- components={{ ...widgets, ...components }}
87
- plugins={{ ...plugins, dayjs }}
88
- {...rest}
89
- />
90
- );
91
- }
92
- export type CustomFormRenderProps = FormRenderProps<any>;
93
- export default function FormRender(props: CustomFormRenderProps) {
94
- const { components, plugins, ...rest } = props;
95
- return (
96
- <DefaultFormRender
97
- options={{ props: { autoComplete: 'off' } }}
98
- components={{ ...widgets, ...components }}
99
- plugins={{ ...plugins, dayjs }}
100
- {...rest}
101
- />
102
- );
103
- };
104
- ```
105
- ### 2. 引入第一步已经注册完的组件
106
- ```javascript
107
- import { Button } from 'antd';
108
- import React, { useState } from 'react';
109
- import FormRender, { useSimpleForm, useSimpleFormRender } from './form-render';
110
- export default function Demo5(props) {
111
-
112
- const watch = {
113
- 'name2': (newValue, oldValue) => {
114
- console.log(newValue, oldValue)
115
- },
116
- 'name3[0]': (newValue, oldValue) => {
117
- console.log(newValue, oldValue)
118
- },
119
- 'name4': (newValue, oldValue) => {
120
- console.log(newValue, oldValue)
121
- }
122
- }
123
-
124
- const widgetList = [
125
- {
126
- label: "readonly",
127
- name: 'name1',
128
- readOnly: true,
129
- readOnlyRender: "readonly component",
130
- initialValue: 1111,
131
- hidden: '{{formvalues && formvalues.name6 == true}}',
132
- type: 'Input',
133
- props: {}
134
- },
135
- {
136
- label: "input",
137
- name: 'name2',
138
- rules: [{ required: true, message: 'input empty' }],
139
- initialValue: 1,
140
- hidden: '{{formvalues && formvalues.name6 == true}}',
141
- type: 'Input',
142
- props: {}
143
- },
144
- {
145
- label: 'list[0]',
146
- name: 'list[0]',
147
- rules: [{ required: true, message: 'list[0] empty' }],
148
- initialValue: { label: 'option1', value: '1', key: '1' },
149
- type: 'Select',
150
- props: {
151
- labelInValue: true,
152
- style: { width: '100%' },
153
- children: [
154
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
155
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
156
- ]
157
- }
158
- },
159
- {
160
- label: 'list[1]',
161
- name: 'list[1]',
162
- rules: [{ required: true, message: 'list[1] empty' }],
163
- type: 'Select',
164
- props: {
165
- labelInValue: true,
166
- style: { width: '100%' },
167
- children: [
168
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
169
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
170
- ]
171
- }
172
- },
173
- {
174
- label: 'first',
175
- name: 'name4.first',
176
- rules: [{ required: true, message: 'first empty' }],
177
- type: 'Select',
178
- props: {
179
- style: { width: '100%' },
180
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
181
- }
182
- },
183
- {
184
- label: 'second',
185
- name: 'name4.second',
186
- rules: [{ required: true, message: 'second empty' }],
187
- type: 'Select',
188
- props: {
189
- style: { width: '100%' },
190
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
191
- }
192
- },
193
- {
194
- label: 'name5',
195
- name: 'name5',
196
- initialValue: { span: 12 },
197
- valueSetter: "{{(value)=> (value && value['span'])}}",
198
- valueGetter: "{{(value) => ({span: value})}}",
199
- type: 'Select',
200
- props: {
201
- style: { width: '100%' },
202
- children: [
203
- { type: 'Select.Option', props: { key: 1, value: 12, children: 'option1' } },
204
- { type: 'Select.Option', props: { key: 2, value: 6, children: 'option2' } },
205
- { type: 'Select.Option', props: { key: 3, value: 4, children: 'option3' } }
206
- ]
207
- }
208
- },
209
- {
210
- label: 'checkbox',
211
- name: 'name6',
212
- valueProp: 'checked',
213
- initialValue: true,
214
- rules: [{ required: true, message: 'checkbox empty' }],
215
- type: 'Checkbox',
216
- props: {
217
- style: { width: '100%' },
218
- children: 'option'
219
- }
220
- },
221
- ]
222
-
223
- const form = useSimpleForm();
224
- // const formrender = useSimpleFormRender();
225
-
226
- const onSubmit = async (e) => {
227
- e?.preventDefault?.();
228
- const result = await form.validate();
229
- console.log(result, 'result');
230
- };
231
-
232
- return (
233
- <div>
234
- <FormRender
235
- form={form}
236
- // formrender={formrender}
237
- widgetList={widgetList}
238
- watch={watch} />
239
- <div style={{ marginLeft: '120px' }}>
240
- <Button onClick={onSubmit}>submit</Button>
241
- </div>
242
- </div>
243
- );
244
- }
245
- ```
246
-
247
- ### 3. 多模块渲染
248
- 表单引擎还支持多个`FormChildren`组件渲染,然后由`Form`组件统一处理表单值.
249
- - `useSimpleForm`: 创建表单值的管理实例.
250
- - `useSimpleFormRender`: 创建渲染表单的实例.
251
- ```javascript
252
- import React, { useState } from 'react';
253
- import { FormChildren, Form, useSimpleForm } from './form-render';
254
- import { Button } from 'antd';
255
- export default function Demo(props) {
256
-
257
- const widgetList1 = [{
258
- label: "part1input",
259
- name: 'part1',
260
- rules: [{ required: true, message: 'part1 empty' }],
261
- initialValue: 1,
262
- type: 'Input',
263
- props: {}
264
- }]
265
-
266
- const widgetList2 = [{
267
- label: "part2input",
268
- name: 'part2',
269
- rules: [{ required: true, message: 'part2 empty' }],
270
- initialValue: 1,
271
- type: 'Input',
272
- props: {}
273
- }]
274
-
275
- const form = useSimpleForm();
276
- // const formrender1 = useSimpleFormRender()
277
- // const formrender2 = useSimpleFormRender()
278
-
279
- const onSubmit = async (e) => {
280
- e?.preventDefault?.();
281
- const result = await form.validate();
282
- console.log(result, 'result');
283
- };
284
-
285
- return (
286
- <div style={{ padding: '0 8px' }}>
287
- <Form form={form}>
288
- <div>
289
- <p>part1</p>
290
- <FormChildren
291
- // formrender={formrender1}
292
- widgetList={widgetList1}
293
- />
294
- </div>
295
- <div>
296
- <p>part2</p>
297
- <FormChildren
298
- // formrender={formrender2}
299
- widgetList={widgetList2}
300
- />
301
- </div>
302
- </Form>
303
- <div style={{ marginLeft: '120px' }}>
304
- <Button onClick={onSubmit}>submit</Button>
305
- </div>
306
- </div>
307
- );
308
- }
309
- ```
310
- ### 4. 数组数据
311
- 复杂的列表渲染增删改功能demo
312
- ```javascript
313
- import React, { useEffect, useState } from 'react';
314
- import FormRender, { CustomFormRenderProps, useSimpleForm } from './form-render';
315
- import { Button } from 'antd';
316
-
317
- const OptionList = React.forwardRef<HTMLElement, any>((props, ref) => {
318
-
319
- const {
320
- value,
321
- onChange,
322
- ...rest
323
- } = props;
324
-
325
- const intialValue = [{ label: '', value: '' }];
326
- const [dataSource, setDataSource] = useState<Array<any>>([]);
327
- const form = useSimpleForm();
328
-
329
- useEffect(() => {
330
- const options = value || [...intialValue];
331
- setDataSource(options);
332
- form.setFieldsValue(options);
333
- }, [value]);
334
-
335
- const widgetList = dataSource.map((item, index) => ({
336
- type: 'row',
337
- props: {
338
- gutter: 12,
339
- align: 'middle',
340
- },
341
- widgetList: [
342
- {
343
- name: `[${index}]label`,
344
- compact: true,
345
- outside: { type: 'col', props: { span: 9 } },
346
- rules: [{ required: true }],
347
- type: 'Input',
348
- props: {
349
- placeholder: 'label',
350
- style: { width: '100%' }
351
- }
352
- },
353
- {
354
- name: `[${index}]value`,
355
- compact: true,
356
- outside: { type: 'col', props: { span: 9 } },
357
- rules: [{ required: true }],
358
- type: 'Input',
359
- props: {
360
- placeholder: 'value',
361
- style: { width: '100%' }
362
- }
363
- },
364
- {
365
- outside: { type: 'col', props: { span: 6 } },
366
- typeRender: <Button type="link" onClick={() => deleteItem(index)}>delete</Button>
367
- },
368
- ]
369
- }));
370
-
371
- const deleteItem = (index: number) => {
372
- const oldData = [...dataSource];
373
- if (!oldData) return;
374
- const newData = [...oldData];
375
- newData.splice(index, 1);
376
- setDataSource(newData);
377
- form.setFieldsValue(newData);
378
- onChange && onChange(newData);
379
- };
380
-
381
- const addItem = () => {
382
- const { error } = await form.validate();
383
- if (error) {
384
- return;
385
- }
386
- const newData = dataSource.concat(intialValue);
387
- form.setFieldsValue(newData);
388
- setDataSource(newData);
389
- };
390
-
391
- const onFieldsChange: CustomFormRenderProps['onFieldsChange'] = (_, values) => {
392
- setDataSource(values);
393
- onChange && onChange(values);
394
- };
395
-
396
- return (
397
- <div>
398
- <FormRender
399
- form={form}
400
- widgetList={widgetList}
401
- onFieldsChange={onFieldsChange}
402
- />
403
- <Button type="link" onClick={addItem}>
404
- add
405
- </Button>
406
- </div>
407
- );
408
- });
409
-
410
- export default OptionList;
411
- ```
412
-
413
- ## API
414
-
415
- ### Form`s props
416
- 来源于[@simpleform/form](../form);
417
-
418
- ### FormChildren's props
419
- - `widgetList`: `WidgetItem[]` 渲染表单的DSL形式的json数据
420
- - `components`:注册表单中的所有组件;
421
- - `plugins`:表单中需要引入的外来库;
422
- - `options`: `GenerateFormNodeProps | ((field: GenerateFormNodeProps) => any)` 传递给表单节点组件的参数信息. 优先级比表单节点自身的参数要低
423
- - `renderList`:提供自定义渲染列表的函数.
424
- - `renderItem`:提供自定义渲染节点的函数.
425
- - `onRenderChange`: `(newValue: WidgetList) => void;` `widgetList`更改时回调函数
426
- - `formrender`: 负责渲染的表单类。通过`useSimpleFormRender()`创建,选填.
427
- - `form`: 负责表单的值的类。通过`useSimpleForm()`创建,选填.
428
- - `uneval`: 不执行表单中的字符串表达式.
429
-
430
- ### SimpleFormRender's Methods
431
- - `updateItemByPath`: `(data?: any, path?: string) => void` 根据`path`获取对应的节点
432
- - `setItemByPath`: `(data?: any, path?: string) => void` 根据`path`设置对应的节点
433
- - `delItemByPath`: `(path?: string) => void` 删除路径`path`对应的节点
434
- - `insertItemByIndex`: `(data: WidgetItem | WidgetItem[], index?: number, parent?: string) => void` 根据序号和父节点路径添加节点
435
- - `getItemByPath`: `(path: string) => void` 获取路径`path`对应的节点
436
- - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` 把树中的选项从一个位置调换到另外一个位置
437
- - `setWidgetList`: `(data?: WidgetList) => void` 设置表单的`widgetList`属性;
438
-
439
- ### Hooks
440
- - `useSimpleFormRender()`: 创建 `new SimpleFormRender()`.
441
- - `useSimpleForm(defaultValues)`: 创建 `new SimpleForm()`
442
-
443
- ## 其他
444
-
445
- ### widgetList结构说明
446
- `widgetList`列表中每一项均为一个渲染节点, 由`type`和`props`渲染而成, 分为表单控件节点和非表单节点
447
- - 表单控件节点:
448
- 具有`name`属性的节点为表单控件节点,默认携带表单域组件(`Form.Item`),举例:
449
- ```javascript
450
- const widgetList = [{
451
- label: "part2input",
452
- name: 'part2',
453
- rules: [{ required: true, message: 'part2 empty' }],
454
- initialValue: 1,
455
- type: 'Input',
456
- props: {}
457
- }]
458
- ```
459
- - 非表单节点:
460
- 无`name`属性的节点。举例:
461
- ```javascript
462
- const widgetList = [{
463
- type: 'CustomCard',
464
- props: {}
465
- }]
466
- ```
467
- - 节点的属性
468
- ```javascript
469
- export type GenerateWidgetItem<T extends Record<string, any> = {}> = FormItemProps & T & {
470
- inside?: CustomUnionType; // 节点的内层
471
- outside?: CustomUnionType; // 节点的外层
472
- readOnly?: boolean; // 只读模式
473
- readOnlyRender?: CustomUnionType; // 只读模式下的组件
474
- typeRender?: CustomUnionType; // 自定义渲染实例
475
- hidden?: boolean; // 隐藏节点
476
- type?: string; // 注册组件
477
- props?: Record<string, any> & { children?: any | Array<CustomWidget> }; // 注册组件的props
478
- widgetList?: WidgetList; // 嵌套的子节点(会覆盖props.children)
479
- }
480
- ```
481
-
482
- ### 参数注入
483
- - 表单节点的属性全局设置:
484
- 通过`options`设置表单节点的全局属性
485
- ```javascript
486
-
487
- ...
488
-
489
- <FormRender
490
- options={{
491
- layout: 'vertical',
492
- props: { disabled: true }
493
- }}
494
- />
495
- ```
496
-
497
- - 表单中的注册组件接收的参数:
498
- 表单中的注册组件会接收到上下文参数
499
- ```javascript
500
- export interface GenerateParams<T = {}> {
501
- path?: string;
502
- widgetItem?: GenerateWidgetItem<T>;
503
- formrender?: SimpleFormRender;
504
- form?: SimpleForm;
505
- };
506
- ```
507
- ### 表单控件name字段的规则
508
- `name`字段可表示数组或对象中某个属性字段的位置路径
509
-
510
- 举例:
511
- - `a[0]`表示数组a下面的第一个选项
512
- - `a.b` 表示a对象的b属性
513
- - `a[0].b`表示数组a下面的第一个选项的b属性
514
-
515
- ### 字符串表达式用法
516
- 表单节点中属性字段除`widgetList`外均可以支持字符串表达式来进行联动
517
- 1. 快速使用:用`{{`和`}}`包裹目标属性值的计算表达式
518
- ```javascript
519
- ...
520
-
521
- const widgetList = [
522
- {
523
- label: 'name1',
524
- name: 'name1',
525
- valueProp: 'checked',
526
- initialValue: true,
527
- type: 'Checkbox',
528
- props: {
529
- children: 'option'
530
- }
531
- },
532
- {
533
- label: "name2",
534
- name: 'name2',
535
- rules: '{{[{ required: formvalues && formvalues.name1 === true, message: "name2 empty" }]}}',
536
- initialValue: 1,
537
- type: 'Input',
538
- props: {}
539
- }
540
- ]
541
-
542
- // OR
543
-
544
- const widgetList = [
545
- {
546
- label: 'name1',
547
- name: 'name1',
548
- valueProp: 'checked',
549
- initialValue: true,
550
- type: 'Checkbox',
551
- props: {
552
- children: 'option'
553
- }
554
- },
555
- {
556
- label: "name2",
557
- name: 'name2',
558
- hidden: '{{formvalues && formvalues.name1 === true}}',
559
- initialValue: 1,
560
- type: 'Input',
561
- props: {}
562
- },
563
- ]
564
- ```
565
- 2. 字符串表达式的使用规则
566
- - 一个字符串有且只能有一对`{{`和`}}`.
567
- - 除了内置的三个变量(`form`(即`useSimpleForm()`), `formrender`(即`useSimpleFormRender()`), `formvalues`(表单值对象))以外, 还可以通过`plugins`引入外部模块, 然后在字符串表达式内直接引用该变量名.
568
- ```javascript
569
- import dayjs from 'dayjs';
570
- import FormRender from "./form-render";
571
-
572
- const widgetList = [{
573
- label: "name3",
574
- initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
575
- type: 'Input',
576
- props: {}
577
- }]
578
-
579
- <FormRender widgetList={widgetList} plugins={{ dayjs }} />
580
- ```