@simpleform/render 2.0.8 → 3.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/README.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  English | [中文说明](./README_CN.md)
4
4
 
5
- [![](https://img.shields.io/badge/version-2.0.8-green)](https://www.npmjs.com/package/@simpleform/render)
5
+ [![](https://img.shields.io/badge/version-3.0.0-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
9
  * Break Change: Version >= 3.x is recommended, providing a better semanticized `JSON` structure.
10
-
10
+ - Deprecated ~`properties`~ : Replaced by `widgetList`.
11
+ - Deprecated ~`onPropertiesChange`~ : replaced by `onRenderChange`.
12
+ - All methods of `useSimpleFormRender()` are changed.
11
13
  ## Introduction
12
- - Component Registration: Form controls used in `@simpleform/render` must be controlled components with `value` and `onChange` props.
13
- - Component Description: `properties` supports rendering of object or array types.
14
- - Component Rendering: `Form` component handles form values, `FormChildren` component handles form rendering, a `Form` component can support multiple `FormChildren` components rendering inside.
15
- - Component linkage: All form properties can support string expressions to describe linkage conditions (except `properties`).
14
+ - Component Registration: The form control used in `@simpleform/render` must be a controlled component with `value` and `onChange` `props`.
15
+ - Component Description: `widgetList` array list describing the current form structure.
16
+ - Component Rendering: `Form` component handles the values of the form, `FormChildren` component handles the rendering of the form, a `Form` component can support multiple `FormChildren` components for internal rendering.
17
+ - Component linkage: All form properties can support string expressions to describe linkage conditions (except `widgetList` property).
16
18
 
17
19
  ## install
18
20
  - [Node.js](https://nodejs.org/en/) Version >= 14.0.0
@@ -27,7 +29,7 @@ yarn add @simpleform/render
27
29
  ### 1. First register the basic components (Take antd@5.x as an example)
28
30
  ```javascript
29
31
  // register
30
- import DefaultFormRender, { FormChildren as FormChildrenCore, FormRenderProps, FormChildrenProps } from '@simpleform/render';
32
+ import DefaultFormRender, { FormChildren as DefaultFormChildren, FormRenderProps, FormChildrenProps } from '@simpleform/render';
31
33
  import '@simpleform/render/lib/css/main.css';
32
34
  import React from 'react';
33
35
  import dayjs from 'dayjs';
@@ -117,105 +119,104 @@ export default function Demo5(props) {
117
119
  }
118
120
  }
119
121
 
120
- const properties = {
121
- name1: {
122
- label: "readonly",
123
- readOnly: true,
124
- readOnlyRender: "readonly component",
125
- initialValue: 1111,
126
- hidden: '{{formvalues && formvalues.name6 == true}}',
127
- type: 'Input',
128
- props: {}
129
- },
130
- name2: {
131
- label: "input",
132
- rules: [{ required: true, message: 'input empty' }],
133
- initialValue: 1,
134
- hidden: '{{formvalues && formvalues.name6 == true}}',
135
- type: 'Input',
136
- props: {}
137
- },
138
- name3: {
139
- // type: '',
140
- // props: {},
141
- properties: [{
142
- label: 'list[0]',
143
- rules: [{ required: true, message: 'list[0] empty' }],
144
- initialValue: { label: 'option1', value: '1', key: '1' },
145
- type: 'Select',
146
- props: {
147
- labelInValue: true,
148
- style: { width: '100%' },
149
- children: [
150
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
151
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
152
- ]
153
- }
154
- }, {
155
- label: 'list[1]',
156
- rules: [{ required: true, message: 'list[1] empty' }],
157
- type: 'Select',
158
- props: {
159
- labelInValue: true,
160
- style: { width: '100%' },
161
- children: [
162
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
163
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
164
- ]
165
- }
166
- }]
167
- },
168
- name4: {
169
- // type: '',
170
- // props: {},
171
- properties: {
172
- first: {
173
- label: 'first',
174
- rules: [{ required: true, message: 'first empty' }],
175
- type: 'Select',
176
- props: {
177
- style: { width: '100%' },
178
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
179
- }
180
- },
181
- second: {
182
- label: 'second',
183
- rules: [{ required: true, message: 'second empty' }],
184
- type: 'Select',
185
- props: {
186
- style: { width: '100%' },
187
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
188
- }
189
- }
190
- }
191
- },
192
- name5: {
193
- label: 'name5',
194
- initialValue: { span: 12 },
195
- valueSetter: "{{(value)=> (value && value['span'])}}",
196
- valueGetter: "{{(value) => ({span: value})}}",
197
- type: 'Select',
198
- props: {
199
- style: { width: '100%' },
200
- children: [
201
- { type: 'Select.Option', props: { key: 1, value: 12, children: 'option1' } },
202
- { type: 'Select.Option', props: { key: 2, value: 6, children: 'option2' } },
203
- { type: 'Select.Option', props: { key: 3, value: 4, children: 'option3' } }
204
- ]
205
- }
206
- },
207
- name6: {
208
- label: 'checkbox',
209
- valueProp: 'checked',
210
- initialValue: true,
211
- rules: [{ required: true, message: 'checkbox empty' }],
212
- type: 'Checkbox',
213
- props: {
214
- style: { width: '100%' },
215
- children: 'option'
216
- }
217
- },
218
- }
122
+ const widgetList = [
123
+ {
124
+ label: "readonly",
125
+ name: 'name1',
126
+ readOnly: true,
127
+ readOnlyRender: "readonly component",
128
+ initialValue: 1111,
129
+ hidden: '{{formvalues && formvalues.name6 == true}}',
130
+ type: 'Input',
131
+ props: {}
132
+ },
133
+ {
134
+ label: "input",
135
+ name: 'name2',
136
+ rules: [{ required: true, message: 'input empty' }],
137
+ initialValue: 1,
138
+ hidden: '{{formvalues && formvalues.name6 == true}}',
139
+ type: 'Input',
140
+ props: {}
141
+ },
142
+ {
143
+ label: 'list[0]',
144
+ name: 'list[0]',
145
+ rules: [{ required: true, message: 'list[0] empty' }],
146
+ initialValue: { label: 'option1', value: '1', key: '1' },
147
+ type: 'Select',
148
+ props: {
149
+ labelInValue: true,
150
+ style: { width: '100%' },
151
+ children: [
152
+ { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
153
+ { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
154
+ ]
155
+ }
156
+ },
157
+ {
158
+ label: 'list[1]',
159
+ name: 'list[1]',
160
+ rules: [{ required: true, message: 'list[1] empty' }],
161
+ type: 'Select',
162
+ props: {
163
+ labelInValue: true,
164
+ style: { width: '100%' },
165
+ children: [
166
+ { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
167
+ { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
168
+ ]
169
+ }
170
+ },
171
+ {
172
+ label: 'first',
173
+ name: 'name4.first',
174
+ rules: [{ required: true, message: 'first empty' }],
175
+ type: 'Select',
176
+ props: {
177
+ style: { width: '100%' },
178
+ children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
179
+ }
180
+ },
181
+ {
182
+ label: 'second',
183
+ name: 'name4.second',
184
+ rules: [{ required: true, message: 'second empty' }],
185
+ type: 'Select',
186
+ props: {
187
+ style: { width: '100%' },
188
+ children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
189
+ }
190
+ },
191
+ {
192
+ label: 'name5',
193
+ name: 'name5',
194
+ initialValue: { span: 12 },
195
+ valueSetter: "{{(value)=> (value && value['span'])}}",
196
+ valueGetter: "{{(value) => ({span: value})}}",
197
+ type: 'Select',
198
+ props: {
199
+ style: { width: '100%' },
200
+ children: [
201
+ { type: 'Select.Option', props: { key: 1, value: 12, children: 'option1' } },
202
+ { type: 'Select.Option', props: { key: 2, value: 6, children: 'option2' } },
203
+ { type: 'Select.Option', props: { key: 3, value: 4, children: 'option3' } }
204
+ ]
205
+ }
206
+ },
207
+ {
208
+ label: 'checkbox',
209
+ name: 'name6',
210
+ valueProp: 'checked',
211
+ initialValue: true,
212
+ rules: [{ required: true, message: 'checkbox empty' }],
213
+ type: 'Checkbox',
214
+ props: {
215
+ style: { width: '100%' },
216
+ children: 'option'
217
+ }
218
+ },
219
+ ]
219
220
 
220
221
  const form = useSimpleForm();
221
222
  // const formrender = useSimpleFormRender();
@@ -231,7 +232,7 @@ export default function Demo5(props) {
231
232
  <FormRender
232
233
  form={form}
233
234
  // formrender={formrender}
234
- properties={properties}
235
+ widgetList={widgetList}
235
236
  watch={watch} />
236
237
  <div style={{ marginLeft: '120px' }}>
237
238
  <Button onClick={onSubmit}>submit</Button>
@@ -250,25 +251,23 @@ import { FormChildren, Form, useSimpleForm } from './form-render';
250
251
  import { Button } from 'antd';
251
252
  export default function Demo(props) {
252
253
 
253
- const properties1 = {
254
- part1: {
255
- label: "part1input",
256
- rules: [{ required: true, message: 'name1 empty' }],
257
- initialValue: 1,
258
- type: 'Input',
259
- props: {}
260
- },
261
- }
262
-
263
- const properties2 = {
264
- part2: {
265
- label: "part2input",
266
- rules: [{ required: true, message: 'name1 empty' }],
267
- initialValue: 1,
268
- type: 'Input',
269
- props: {}
270
- },
271
- }
254
+ const widgetList1 = [{
255
+ label: "part1input",
256
+ name: 'part1',
257
+ rules: [{ required: true, message: 'part1 empty' }],
258
+ initialValue: 1,
259
+ type: 'Input',
260
+ props: {}
261
+ }]
262
+
263
+ const widgetList2 = [{
264
+ label: "part2input",
265
+ name: 'part2',
266
+ rules: [{ required: true, message: 'part2 empty' }],
267
+ initialValue: 1,
268
+ type: 'Input',
269
+ props: {}
270
+ }]
272
271
 
273
272
  const form = useSimpleForm();
274
273
  // const formrender1 = useSimpleFormRender()
@@ -287,14 +286,14 @@ export default function Demo(props) {
287
286
  <p>part1</p>
288
287
  <FormChildren
289
288
  // formrender={formrender1}
290
- properties={properties1}
289
+ widgetList={widgetList1}
291
290
  />
292
291
  </div>
293
292
  <div>
294
293
  <p>part2</p>
295
294
  <FormChildren
296
295
  // formrender={formrender2}
297
- properties={properties2}
296
+ widgetList={widgetList2}
298
297
  />
299
298
  </div>
300
299
  </Form>
@@ -313,32 +312,36 @@ import FormRender, { useSimpleForm } from './form-render';
313
312
  import { Button } from 'antd';
314
313
  export default function Demo(props) {
315
314
  const form = useSimpleForm();
316
- const properties =
315
+ const widgetList =
317
316
  [
318
317
  {
319
318
  label: "list-0",
320
- rules: [{ required: true, message: 'name1 empty' }],
319
+ name: 'list[0]',
320
+ rules: [{ required: true, message: 'list[0] empty' }],
321
321
  initialValue: 1,
322
322
  type: 'Input',
323
323
  props: {}
324
324
  },
325
325
  {
326
326
  label: "list-1",
327
- rules: [{ required: true, message: 'name1 empty' }],
327
+ name: 'list[1]',
328
+ rules: [{ required: true, message: 'list[1] empty' }],
328
329
  initialValue: 2,
329
330
  type: 'Input',
330
331
  props: {}
331
332
  },
332
333
  {
333
334
  label: "list-2",
334
- rules: [{ required: true, message: 'name1 empty' }],
335
+ name: 'list[2]',
336
+ rules: [{ required: true, message: 'list[2] empty' }],
335
337
  initialValue: 3,
336
338
  type: 'Input',
337
339
  props: {}
338
340
  },
339
341
  {
340
342
  label: "list-3",
341
- rules: [{ required: true, message: 'name1 empty' }],
343
+ name: 'list[3]',
344
+ rules: [{ required: true, message: 'list[3] empty' }],
342
345
  initialValue: 4,
343
346
  type: 'Input',
344
347
  props: {}
@@ -355,7 +358,7 @@ export default function Demo(props) {
355
358
  <div style={{ padding: '0 8px' }}>
356
359
  <FormRender
357
360
  form={form}
358
- properties={properties}
361
+ widgetList={widgetList}
359
362
  />
360
363
  <div style={{ marginLeft: '120px' }}>
361
364
  <Button onClick={onSubmit}>submit</Button>
@@ -370,26 +373,25 @@ export default function Demo(props) {
370
373
  Sourced from [@simpleform/form](../form);
371
374
 
372
375
  ### FormChildren's props
373
- - `properties`: `{ [name: string]: FormNodeProps } | FormNodeProps[]` Renders the form's DSL form json data
376
+ - `widgetList`: `WidgetItem[]` Renders the form's DSL form json data
374
377
  - `components`: registers all components in the form.
375
378
  - `plugins`: foreign libraries to be introduced in the form.
376
379
  - `options`: `GenerateFormNodeProps | ((field: GenerateFormNodeProps) => any)` Parameter information passed to the form node components. The priority is lower than the form node's own parameters
377
380
  - `renderList`: Provides a function to customize the rendered list.
378
381
  - `renderItem`: provides a function to customize the rendering of the node.
379
- - `onPropertiesChange`: `(newValue: PropertiesData) => void;` `Properties` change callback function.
382
+ - `onRenderChange`: `(newValue: WidgetList) => void;` `onRenderChange` change callback function.
380
383
  - `formrender`: The form class responsible for rendering. Created by `useSimpleFormRender()`, optional.
381
384
  - `form`: Class responsible for the value of the form. Created via `useSimpleForm()`, optional.
382
385
  - `uneval`: does not execute string expressions in the form.
383
386
 
384
387
  ### SimpleFormRender's Methods
385
- - `updateItemByPath`: `(data?: any, path?: string, attributeName?: string) => void` Updates the node corresponding to the path `path`, if updating a specific attribute in the node then the `attributeName` parameter is required.
386
- - `setItemByPath`: `(data?: any, path?: string, attributeName?: string) => void` Sets the node corresponding to the path `path`, the `attributeName` parameter is required if you are updating a specific attribute in the node.
387
- - `updateNameByPath`: `(newName?: string, path: string) => void` Updates the name key of the specified path.
388
- - `delItemByPath`: `(path?: string, attributeName?: string) => void` Deletes the node corresponding to the path `path`, if deleting a specific attribute in the node then the `attributeName` parameter is required.
389
- - `insertItemByIndex`: `(data: InsertItemType, index?: number, parent?: { path?: string, attributeName?: string }) => void` Adds an option based on the serial number and the path of the parent node.
390
- - `getItemByPath`: `(path?: string, attributeName?: string) => void` Get the node that corresponds to the path `path`, or the `attributeName` parameter if it is a specific attribute in the node.
391
- - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` Swap options in the tree from one location to another.
392
- - `setProperties`: `(data?: Partial<FormNodeProps>) => void` sets `properties`.
388
+ - `updateItemByPath`: `(data?: any, path?: string) => void` Get the corresponding node based on `path`.
389
+ - `setItemByPath`: `(data?: any, path?: string) => void` Sets the corresponding node according to `path`.
390
+ - `delItemByPath`: `(path?: string) => void` Removes the node corresponding to the path `path`.
391
+ - `insertItemByIndex`: `(data: WidgetItem | WidgetItem[], index?: number, parent?: string) => void` Adds a node based on the serial number and the path of the parent node.
392
+ - `getItemByPath`: `(path?: string) => void` Get the node corresponding to the path `path`.
393
+ - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` Swap options in the tree from one position to another
394
+ - `setWidgetList`: `(data?: WidgetList) => void` Sets the `widgetList` attribute of the form.
393
395
 
394
396
  ### Hooks
395
397
  - `useSimpleFormRender()`: create `new SimpleFormRender()`.
@@ -397,66 +399,40 @@ Sourced from [@simpleform/form](../form);
397
399
 
398
400
  ## Other
399
401
 
400
- ### Properties structure description
401
- Each item in the `properties` property is a form node, and the nodes are categorized into nested nodes and control nodes.
402
- - Nested nodes.
403
- Nodes that have a `properties` property that describes which component the node is, via the `type` and `props` fields, and do not carry a form field component (`Form.Item`).
404
- - Form control Node.
405
- A node with no `properties` attribute that carries the form field component (`Form.Item`) by default.
402
+ ### widgetList structure description
403
+ Each item in the `widgetList` list is a rendering node, divided into a form control node and nonform node.
404
+ - Form control nodes.
405
+ Nodes with the `name` attribute are form control nodes and carry the form field component (`Form.Item`) by default, for example:
406
406
  ```javascript
407
- const properties = {
408
- name3: {
409
- // Nested nodes
410
- // type: '',
411
- // props: {},
412
- properties: {
413
- // Control node
414
- first: {
415
- label: 'first',
416
- rules: [{ required: true, message: 'first empty' }],
417
- type: 'Select',
418
- props: {
419
- style: { width: '100%' },
420
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
421
- }
422
- }
423
- }
424
- },
425
- }
407
+ const widgetList = [{
408
+ label: "part2input",
409
+ name: 'part2',
410
+ rules: [{ required: true, message: 'part2 empty' }],
411
+ initialValue: 1,
412
+ type: 'Input',
413
+ props: {}
414
+ }]
415
+ ```
416
+ - nonform node.
417
+ Nodes without the `name` attribute. Example:
418
+ ```javascript
419
+ const widgetList = [{
420
+ type: 'CustomCard',
421
+ props: {}
422
+ }]
426
423
  ```
427
424
  - Form Node's types
428
425
  ```javascript
429
- export interface FormComponent {
430
- type?: string;
431
- props?: any & { children?: any | Array<FormComponent> };
432
- }
433
-
434
- export type UnionComponent<P> =
435
- | React.ComponentType<P>
436
- | React.ForwardRefExoticComponent<P>
437
- | React.FC<P>
438
- | keyof React.ReactHTML;
439
-
440
- // Component
441
- export type CustomUnionType = FormComponent | Array<FormComponent> | UnionComponent<any> | Function | ReactNode
442
- // FormRender's properties
443
- export type PropertiesData = { [name: string]: FormNodeProps } | FormNodeProps[]
444
- // field's props(String expressions after compilation)
445
- export type GenerateFormNodeProps<T = {}> = FormComponent & FormItemProps & T & {
446
- ignore?: boolean; // Mark the current node as a non-form node
447
- inside?: CustomUnionType; // Nested components within nodes
448
- outside?: CustomUnionType; // Nested components in the outer layer of the node
426
+ export type GenerateWidgetItem<T extends Record<string, any> = {}> = FormItemProps & T & {
427
+ inside?: CustomUnionType; // The inner layer of the node
428
+ outside?: CustomUnionType; // The outside layer of the node
449
429
  readOnly?: boolean; // read-only mode
450
430
  readOnlyRender?: CustomUnionType; // Read-only mode rendering
451
431
  typeRender?: CustomUnionType; // Registering components for custom rendering
452
- properties?: PropertiesData;
453
432
  hidden?: boolean;
454
- }
455
- // field's props(String expressions before compilation)
456
- export type FormNodeProps = {
457
- [key in keyof GenerateFormNodeProps]: key extends 'rules' ?
458
- (string | Array<{ [key in keyof FormRule]: FormRule[key] | string }> | GenerateFormNodeProps[key])
459
- : (key extends 'properties' ? GenerateFormNodeProps[key] : (string | GenerateFormNodeProps[key]))
433
+ type?: string; // Register the component
434
+ props?: Record<string, any> & { children?: any | Array<CustomWidget> }; // Register the component's props
435
+ widgetList?: WidgetList; // children of the component(will override props.children)
460
436
  }
461
437
  ```
462
438
  ### parameter injection
@@ -478,10 +454,8 @@ Set the global properties of the form node via 'options'
478
454
  The registration component in the form receives a context parameter
479
455
  ```javascript
480
456
  export interface GenerateParams<T = {}> {
481
- name?: string;
482
457
  path?: string;
483
- field?: GenerateFormNodeProps<T>;
484
- parent?: { name?: string; path?: string, field?: GenerateFormNodeProps<T>; };
458
+ widgetItem?: GenerateWidgetItem<T>;
485
459
  formrender?: SimpleFormRender;
486
460
  form?: SimpleForm;
487
461
  };
@@ -494,15 +468,16 @@ Example:
494
468
  - `a[0].b` represents the `b` attribute of the first option below the array `a`
495
469
 
496
470
  ### String Expression Usage
497
- Property fields in a form node can support string expressions for linkage, except for `properties`.
471
+ Property fields in a form node can support string expressions for linkage, except for `widgetList`.
498
472
  1. Quick use: computational expressions wrapping target attribute values in `{{` and `}}`
499
473
  ```javascript
500
474
 
501
475
  ...
502
476
 
503
- const properties = {
504
- name1: {
477
+ const widgetList = [
478
+ {
505
479
  label: 'name1',
480
+ name: 'name1',
506
481
  valueProp: 'checked',
507
482
  initialValue: true,
508
483
  type: 'Checkbox',
@@ -510,20 +485,22 @@ Property fields in a form node can support string expressions for linkage, excep
510
485
  children: 'option'
511
486
  }
512
487
  },
513
- name2: {
488
+ {
514
489
  label: "name2",
490
+ name: 'name2',
515
491
  rules: '{{[{ required: formvalues && formvalues.name1 === true, message: "name2 empty" }]}}',
516
492
  initialValue: 1,
517
493
  type: 'Input',
518
494
  props: {}
519
- },
520
- }
495
+ }
496
+ ]
521
497
 
522
498
  // OR
523
499
 
524
- const properties = {
525
- name1: {
500
+ const widgetList = [
501
+ {
526
502
  label: 'name1',
503
+ name: 'name1',
527
504
  valueProp: 'checked',
528
505
  initialValue: true,
529
506
  type: 'Checkbox',
@@ -531,14 +508,15 @@ Property fields in a form node can support string expressions for linkage, excep
531
508
  children: 'option'
532
509
  }
533
510
  },
534
- name2: {
511
+ {
535
512
  label: "name2",
513
+ name: 'name2',
536
514
  hidden: '{{formvalues && formvalues.name1 === true}}',
537
515
  initialValue: 1,
538
516
  type: 'Input',
539
517
  props: {}
540
518
  },
541
- }
519
+ ]
542
520
  ```
543
521
  2. Rules for the use of string expressions
544
522
  - A string has and can have only one pair of `{{` and `}}`.
@@ -547,14 +525,12 @@ Property fields in a form node can support string expressions for linkage, excep
547
525
  import dayjs from 'dayjs';
548
526
  import FormRender from "./form-render";
549
527
 
550
- const properties = {
551
- name3: {
552
- label: "name3",
553
- initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
554
- type: 'Input',
555
- props: {}
556
- },
557
- }
528
+ const widgetList = [{
529
+ label: "name3",
530
+ initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
531
+ type: 'Input',
532
+ props: {}
533
+ }]
558
534
 
559
- <FormRender properties={properties} plugins={{ dayjs }} />
535
+ <FormRender widgetList={widgetList} plugins={{ dayjs }} />
560
536
  ```