@simpleform/render 1.0.7 → 1.0.9

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
@@ -1,551 +1,550 @@
1
- # `@simpleform/render`
2
-
3
- English | [中文说明](./README_CN.md)
4
-
5
- [![](https://img.shields.io/badge/version-1.0.1-green)](https://www.npmjs.com/package/@simpleform/render)
6
-
7
- > A lightweight dynamic forms engine that makes it easy to dynamically render forms.
8
-
9
- ## Introduction
10
- - Component Registration: Form controls used in `@simpleform/render` must be controlled components with `value` and `onChange` props.
11
- - Component Description: `properties` supports rendering of object or array types, and adding nested object fields via the `properties` property.
12
- - Component Rendering: `Form` component handles form values, `FormChildren` component handles form rendering, a `Form` component can support multiple `FormChildren` components rendering inside.
13
- - Component linkage: All form properties can support string expressions to describe linkage conditions (except `properties`).
14
-
15
- ## install
16
- - [Node.js](https://nodejs.org/en/) Version >= 14.0.0
17
- - [react](https://react.docschina.org/) Version >= 16.8.0
18
- ```bash
19
- npm install @simpleform/render --save
20
- # 或者
21
- yarn add @simpleform/render
22
- ```
23
- ## Quick Start
24
-
25
- ### 1. First register the basic components (Take antd@5.x as an example)
26
- ```javascript
27
- // register
28
- import FormRenderCore, { FormChildren as FormChildrenCore, FormNodeProps, FormRenderProps, FormChildrenProps, GenerateParams } from '@simpleform/render';
29
- import '@simpleform/render/lib/css/main.css';
30
- import React from 'react';
31
- import dayjs from 'dayjs';
32
- import {
33
- Input,
34
- InputNumber,
35
- Checkbox,
36
- DatePicker,
37
- Mentions,
38
- Radio,
39
- Rate,
40
- Select,
41
- Slider,
42
- Switch,
43
- TimePicker,
44
- } from 'antd';
45
-
46
- export * from '@simpleform/render';
47
-
48
- export const widgets = {
49
- "Input": Input,
50
- "Input.TextArea": Input.TextArea,
51
- "Input.Password": Input.Password,
52
- "Input.Search": Input.Search,
53
- "InputNumber": InputNumber,
54
- "Checkbox": Checkbox,
55
- 'Checkbox.Group': Checkbox.Group,
56
- "DatePicker": DatePicker,
57
- "DatePicker.RangePicker": DatePicker.RangePicker,
58
- "Mentions": Mentions,
59
- "Mentions.Option": Mentions.Option,
60
- "Radio": Radio,
61
- "Radio.Group": Radio.Group,
62
- "Radio.Button": Radio.Button,
63
- "Rate": Rate,
64
- "Select": Select,
65
- "Select.Option": Select.Option,
66
- "TreeSelect": TreeSelect,
67
- "Slider": Slider,
68
- "Switch": Switch,
69
- "TimePicker": TimePicker,
70
- "TimePicker.RangePicker": TimePicker.RangePicker
71
- }
72
-
73
- export type CustomFormChildrenProps = FormChildrenProps<any>;
74
- export function FormChildren(props: CustomFormChildrenProps) {
75
- const { components, plugins, ...rest } = props;
76
- return (
77
- <FormChildrenCore
78
- options={{ props: { autoComplete: 'off' } }}
79
- components={{ ...widgets, ...components }}
80
- plugins={{ ...plugins, dayjs }}
81
- {...rest}
82
- />
83
- );
84
- }
85
- export type CustomFormRenderProps = FormRenderProps<any>;
86
- export default function FormRender(props: CustomFormRenderProps) {
87
- const { components, plugins, ...rest } = props;
88
- return (
89
- <FormRenderCore
90
- options={{ props: { autoComplete: 'off' } }}
91
- components={{ ...widgets, ...components }}
92
- plugins={{ ...plugins, dayjs }}
93
- {...rest}
94
- />
95
- );
96
- }
97
- ```
98
- ### 2. Introduce the components that were registered in the first step.
99
- ```javascript
100
- import { Button } from 'antd';
101
- import React, { useState } from 'react';
102
- import FormRender, { useSimpleForm, useSimpleFormRender } from './form-render';
103
- export default function Demo5(props) {
104
-
105
- const watch = {
106
- 'name2': (newValue, oldValue) => {
107
- console.log(newValue, oldValue)
108
- },
109
- 'name3[0]': (newValue, oldValue) => {
110
- console.log(newValue, oldValue)
111
- },
112
- 'name4': (newValue, oldValue) => {
113
- console.log(newValue, oldValue)
114
- }
115
- }
116
-
117
- const properties = {
118
- name1: {
119
- label: "readonly",
120
- readOnly: true,
121
- readOnlyRender: "readonly component",
122
- initialValue: 1111,
123
- hidden: '{{formvalues && formvalues.name6 == true}}',
124
- type: 'Input',
125
- props: {}
126
- },
127
- name2: {
128
- label: "input",
129
- rules: [{ required: true, message: 'input empty' }],
130
- initialValue: 1,
131
- hidden: '{{formvalues && formvalues.name6 == true}}',
132
- type: 'Input',
133
- props: {}
134
- },
135
- name3: {
136
- // type: '',
137
- // props: {},
138
- properties: [{
139
- label: 'list[0]',
140
- rules: [{ required: true, message: 'list[0] empty' }],
141
- initialValue: { label: 'option1', value: '1', key: '1' },
142
- type: 'Select',
143
- props: {
144
- labelInValue: true,
145
- style: { width: '100%' },
146
- children: [
147
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
148
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
149
- ]
150
- }
151
- }, {
152
- label: 'list[1]',
153
- rules: [{ required: true, message: 'list[1] empty' }],
154
- type: 'Select',
155
- props: {
156
- labelInValue: true,
157
- style: { width: '100%' },
158
- children: [
159
- { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
160
- { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
161
- ]
162
- }
163
- }]
164
- },
165
- name4: {
166
- // type: '',
167
- // props: {},
168
- properties: {
169
- first: {
170
- label: 'first',
171
- rules: [{ required: true, message: 'first empty' }],
172
- type: 'Select',
173
- props: {
174
- style: { width: '100%' },
175
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
176
- }
177
- },
178
- second: {
179
- label: 'second',
180
- rules: [{ required: true, message: 'second empty' }],
181
- type: 'Select',
182
- props: {
183
- style: { width: '100%' },
184
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
185
- }
186
- }
187
- }
188
- },
189
- name5: {
190
- label: 'name5',
191
- initialValue: { span: 12 },
192
- valueSetter: "{{(value)=> (value && value['span'])}}",
193
- valueGetter: "{{(value) => ({span: value})}}",
194
- type: 'Select',
195
- props: {
196
- style: { width: '100%' },
197
- children: [
198
- { type: 'Select.Option', props: { key: 1, value: 12, children: 'option1' } },
199
- { type: 'Select.Option', props: { key: 2, value: 6, children: 'option2' } },
200
- { type: 'Select.Option', props: { key: 3, value: 4, children: 'option3' } }
201
- ]
202
- }
203
- },
204
- name6: {
205
- label: 'checkbox',
206
- valueProp: 'checked',
207
- initialValue: true,
208
- rules: [{ required: true, message: 'checkbox empty' }],
209
- type: 'Checkbox',
210
- props: {
211
- style: { width: '100%' },
212
- children: 'option'
213
- }
214
- },
215
- }
216
-
217
- const form = useSimpleForm();
218
- // const formrender = useSimpleFormRender();
219
-
220
- const onSubmit = async (e) => {
221
- e?.preventDefault?.();
222
- const result = await form.validate();
223
- console.log(result, 'result');
224
- };
225
-
226
- return (
227
- <div>
228
- <FormRender
229
- form={form}
230
- // formrender={formrender}
231
- properties={properties}
232
- watch={watch} />
233
- <div style={{ marginLeft: '120px' }}>
234
- <Button onClick={onSubmit}>submit</Button>
235
- </div>
236
- </div>
237
- );
238
- }
239
- ```
240
- ### 3. Multi-module rendering
241
- The form engine also supports rendering by multiple `FormChildren` components, which then unify the processing of form values by the `Form` component.
242
- - `useSimpleForm`: Creates a managed instance of a form value.
243
- - `useSimpleFormRender`: creates an instance that renders the form.
244
- ```javascript
245
- import React, { useState } from 'react';
246
- import { FormChildren, Form, useSimpleForm } from './form-render';
247
- import { Button } from 'antd';
248
- export default function Demo(props) {
249
-
250
- const properties1 = {
251
- part1: {
252
- label: "part1input",
253
- rules: [{ required: true, message: 'name1 empty' }],
254
- initialValue: 1,
255
- type: 'Input',
256
- props: {}
257
- },
258
- }
259
-
260
- const properties2 = {
261
- part2: {
262
- label: "part2input",
263
- rules: [{ required: true, message: 'name1 empty' }],
264
- initialValue: 1,
265
- type: 'Input',
266
- props: {}
267
- },
268
- }
269
-
270
- const form = useSimpleForm();
271
- // const formrender1 = useSimpleFormRender()
272
- // const formrender2 = useSimpleFormRender()
273
-
274
- const onSubmit = async (e) => {
275
- e?.preventDefault?.();
276
- const result = await form.validate();
277
- console.log(result, 'result');
278
- };
279
-
280
- return (
281
- <div style={{ padding: '0 8px' }}>
282
- <Form form={form}>
283
- <div>
284
- <p>part1</p>
285
- <FormChildren
286
- // formrender={formrender1}
287
- properties={properties1}
288
- />
289
- </div>
290
- <div>
291
- <p>part2</p>
292
- <FormChildren
293
- // formrender={formrender2}
294
- properties={properties2}
295
- />
296
- </div>
297
- </Form>
298
- <div style={{ marginLeft: '120px' }}>
299
- <Button onClick={onSubmit}>submit</Button>
300
- </div>
301
- </div>
302
- );
303
- }
304
- ```
305
- ### 4. List Demo
306
- 支持数组渲染
307
- ```javascript
308
- import React, { useState } from 'react';
309
- import FormRender, { useSimpleForm } from './form-render';
310
- import { Button } from 'antd';
311
- export default function Demo(props) {
312
- const form = useSimpleForm();
313
- const properties =
314
- [
315
- {
316
- label: "list-0",
317
- rules: [{ required: true, message: 'name1 empty' }],
318
- initialValue: 1,
319
- type: 'Input',
320
- props: {}
321
- },
322
- {
323
- label: "list-1",
324
- rules: [{ required: true, message: 'name1 empty' }],
325
- initialValue: 2,
326
- type: 'Input',
327
- props: {}
328
- },
329
- {
330
- label: "list-2",
331
- rules: [{ required: true, message: 'name1 empty' }],
332
- initialValue: 3,
333
- type: 'Input',
334
- props: {}
335
- },
336
- {
337
- label: "list-3",
338
- rules: [{ required: true, message: 'name1 empty' }],
339
- initialValue: 4,
340
- type: 'Input',
341
- props: {}
342
- },
343
- ];
344
-
345
- const onSubmit = async (e) => {
346
- e?.preventDefault?.();
347
- const result = await form.validate();
348
- console.log(result, 'result');
349
- };
350
-
351
- return (
352
- <div style={{ padding: '0 8px' }}>
353
- <FormRender
354
- form={form}
355
- properties={properties}
356
- />
357
- <div style={{ marginLeft: '120px' }}>
358
- <Button onClick={onSubmit}>submit</Button>
359
- </div>
360
- </div>
361
- );
362
- }
363
- ```
364
- ## API
365
-
366
- ### Form`s props
367
- Sourced from [@simpleform/form](... /form);
368
-
369
- ### FormChildren's props
370
- - `properties`: `{ [name: string]: FormNodeProps } | FormNodeProps[]` Renders the form's DSL form json data
371
- - `components`: registers all components in the form.
372
- - `plugins`: foreign libraries to be introduced in the form.
373
- - `options`: `GenerateFormNodeProps | ((field: GenerateFormNodeProps) => any)` Parameter information passed to the form node components. The priority is lower than the form node's own parameters
374
- - `renderList`: Provides a function to customize the rendered list.
375
- - `renderItem`: provides a function to customize the rendering of the node.
376
- - `onPropertiesChange`: `(newValue: PropertiesData) => void;` `Properties` change callback function.
377
- - `formrender`: The form class responsible for rendering. Created by `useSimpleFormRender()`, optional.
378
- - `form`: Class responsible for the value of the form. Created via `useSimpleForm()`, optional.
379
- - `uneval`: does not execute string expressions in the form.
380
- - `plugin`: External library to be introduced in the string expression.
381
-
382
- ### SimpleFormRender's Methods
383
- - `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.
384
- - `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.
385
- - `updateNameByPath`: `(newName?: string, path: string) => void` Updates the name key of the specified path.
386
- - `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.
387
- - `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.
388
- - `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.
389
- - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` Swap options in the tree from one location to another.
390
- - `setProperties`: `(data?: Partial<FormNodeProps>) => void` sets `properties`.
391
-
392
- ### Hooks
393
- - `useSimpleFormRender()`: create `new SimpleFormRender()`.
394
- - `useSimpleForm(defaultValues)`: create `new SimpleForm()`
395
-
396
- ## Other
397
-
398
- ### Properties structure description
399
- Each item in the `properties` property is a form node, and the nodes are categorized into nested nodes and control nodes.
400
- - Nested nodes.
401
- 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`).
402
- - Node.
403
- A node with no `properties` attribute that carries the form field component (`Form.Item`) by default.
404
- ```javascript
405
- const properties = {
406
- name3: {
407
- // Nested nodes
408
- // type: '',
409
- // props: {},
410
- properties: {
411
- // Control node
412
- first: {
413
- label: 'first',
414
- rules: [{ required: true, message: 'first empty' }],
415
- type: 'Select',
416
- props: {
417
- style: { width: '100%' },
418
- children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
419
- }
420
- }
421
- }
422
- },
423
- }
424
- ```
425
- - Form Node's types
426
- ```javascript
427
- export interface FormComponent {
428
- type?: string;
429
- props?: any & { children?: any | Array<FormComponent> };
430
- }
431
-
432
- export type UnionComponent<P> =
433
- | React.ComponentType<P>
434
- | React.ForwardRefExoticComponent<P>
435
- | React.FC<P>
436
- | keyof React.ReactHTML;
437
-
438
- // Component
439
- export type CustomUnionType = FormComponent | Array<FormComponent> | UnionComponent<any> | Function | ReactNode
440
- // FormRender's properties
441
- export type PropertiesData = { [name: string]: FormNodeProps } | FormNodeProps[]
442
- // field's props(String expressions after compilation)
443
- export type GenerateFormNodeProps<T = {}> = FormComponent & FormItemProps & T & {
444
- ignore?: boolean; // Mark the current node as a non-form node
445
- inside?: CustomUnionType; // Nested components within nodes
446
- outside?: CustomUnionType; // Nested components in the outer layer of the node
447
- readOnly?: boolean; // read-only mode
448
- readOnlyRender?: CustomUnionType; // Read-only mode rendering
449
- typeRender?: CustomUnionType; // Registering components for custom rendering
450
- properties?: PropertiesData;
451
- hidden?: boolean;
452
- }
453
- // field's props(String expressions before compilation)
454
- export type FormNodeProps = {
455
- [key in keyof GenerateFormNodeProps]: key extends 'rules' ?
456
- (string | Array<{ [key in keyof FormRule]: FormRule[key] | string }> | GenerateFormNodeProps[key])
457
- : (key extends 'properties' ? GenerateFormNodeProps[key] : (string | GenerateFormNodeProps[key]))
458
- }
459
- ```
460
- ### parameter injection
461
- - The properties of the form node are set globally:
462
- ```javascript
463
-
464
- <FormRender
465
- options={{
466
- layout: 'vertical',
467
- props: { disabled: true }
468
- }}
469
- />
470
- ```
471
-
472
- - Parameters received by the registration component of the form:
473
- ```javascript
474
- export interface GenerateParams<T = {}> {
475
- name?: string;
476
- path?: string;
477
- field?: GenerateFormNodeProps<T>;
478
- parent?: { name?: string; path?: string, field?: GenerateFormNodeProps<T>; };
479
- formrender?: SimpleFormRender;
480
- form?: SimpleForm;
481
- };
482
- ```
483
- ### Path path rules involved in forms
484
- Forms are allowed to be nested, so the form involves looking for a certain property. The paths follow certain rules
485
- Example:
486
- - `a[0]` denotes the first option below the array `a`
487
- - `a.b` denotes the `b` attribute of the `a` object
488
- - `a[0].b` represents the `b` attribute of the first option below the array `a`
489
-
490
- ### String Expression Usage
491
- Property fields in a form node can support string expressions for linkage, except for `properties`.
492
- 1. Quick use: computational expressions wrapping target attribute values in `{{` and `}}`
493
- ```javascript
494
- const properties = {
495
- name1: {
496
- label: 'name1',
497
- valueProp: 'checked',
498
- initialValue: true,
499
- type: 'Checkbox',
500
- props: {
501
- children: 'option'
502
- }
503
- },
504
- name2: {
505
- label: "name2",
506
- rules: '{{[{ required: formvalues && formvalues.name1 === true, message: "name2 empty" }]}}',
507
- initialValue: 1,
508
- type: 'Input',
509
- props: {}
510
- },
511
- }
512
-
513
- // OR
514
-
515
- const properties = {
516
- name1: {
517
- label: 'name1',
518
- valueProp: 'checked',
519
- initialValue: true,
520
- type: 'Checkbox',
521
- props: {
522
- children: 'option'
523
- }
524
- },
525
- name2: {
526
- label: "name2",
527
- hidden: '{{formvalues && formvalues.name1 === true}}',
528
- initialValue: 1,
529
- type: 'Input',
530
- props: {}
531
- },
532
- }
533
- ```
534
- 2. Rules for the use of string expressions
535
- - A string has and can have only one pair of `{{` and `}}`.
536
- - In addition to the three built-in variables (`form` (i.e., `useSimpleForm()`), `formrender` (i.e., `useSimpleFormRender()`), `formvalues`)), you can introduce an external variable via `plugins` and then reference the variable name directly in the string expression. in a string expression.
537
- ```javascript
538
- import dayjs from 'dayjs';
539
- import FormRender from "./form-render";
540
-
541
- const properties = {
542
- name3: {
543
- label: "name3",
544
- initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
545
- type: 'Input',
546
- props: {}
547
- },
548
- }
549
-
550
- <FormRender properties={properties} plugins={{ dayjs }} />
1
+ # `@simpleform/render`
2
+
3
+ English | [中文说明](./README_CN.md)
4
+
5
+ [![](https://img.shields.io/badge/version-1.0.1-green)](https://www.npmjs.com/package/@simpleform/render)
6
+
7
+ > A lightweight dynamic forms engine that makes it easy to dynamically render forms.
8
+
9
+ ## Introduction
10
+ - Component Registration: Form controls used in `@simpleform/render` must be controlled components with `value` and `onChange` props.
11
+ - Component Description: `properties` supports rendering of object or array types, and adding nested object fields via the `properties` property.
12
+ - Component Rendering: `Form` component handles form values, `FormChildren` component handles form rendering, a `Form` component can support multiple `FormChildren` components rendering inside.
13
+ - Component linkage: All form properties can support string expressions to describe linkage conditions (except `properties`).
14
+
15
+ ## install
16
+ - [Node.js](https://nodejs.org/en/) Version >= 14.0.0
17
+ - [react](https://react.docschina.org/) Version >= 16.8.0
18
+ ```bash
19
+ npm install @simpleform/render --save
20
+ # 或者
21
+ yarn add @simpleform/render
22
+ ```
23
+ ## Quick Start
24
+
25
+ ### 1. First register the basic components (Take antd@5.x as an example)
26
+ ```javascript
27
+ // register
28
+ import FormRenderCore, { FormChildren as FormChildrenCore, FormNodeProps, FormRenderProps, FormChildrenProps, GenerateParams } from '@simpleform/render';
29
+ import '@simpleform/render/lib/css/main.css';
30
+ import React from 'react';
31
+ import dayjs from 'dayjs';
32
+ import {
33
+ Input,
34
+ InputNumber,
35
+ Checkbox,
36
+ DatePicker,
37
+ Mentions,
38
+ Radio,
39
+ Rate,
40
+ Select,
41
+ Slider,
42
+ Switch,
43
+ TimePicker,
44
+ } from 'antd';
45
+
46
+ export * from '@simpleform/render';
47
+
48
+ export const widgets = {
49
+ "Input": Input,
50
+ "Input.TextArea": Input.TextArea,
51
+ "Input.Password": Input.Password,
52
+ "Input.Search": Input.Search,
53
+ "InputNumber": InputNumber,
54
+ "Checkbox": Checkbox,
55
+ 'Checkbox.Group': Checkbox.Group,
56
+ "DatePicker": DatePicker,
57
+ "DatePicker.RangePicker": DatePicker.RangePicker,
58
+ "Mentions": Mentions,
59
+ "Mentions.Option": Mentions.Option,
60
+ "Radio": Radio,
61
+ "Radio.Group": Radio.Group,
62
+ "Radio.Button": Radio.Button,
63
+ "Rate": Rate,
64
+ "Select": Select,
65
+ "Select.Option": Select.Option,
66
+ "TreeSelect": TreeSelect,
67
+ "Slider": Slider,
68
+ "Switch": Switch,
69
+ "TimePicker": TimePicker,
70
+ "TimePicker.RangePicker": TimePicker.RangePicker
71
+ }
72
+
73
+ export type CustomFormChildrenProps = FormChildrenProps<any>;
74
+ export function FormChildren(props: CustomFormChildrenProps) {
75
+ const { components, plugins, ...rest } = props;
76
+ return (
77
+ <FormChildrenCore
78
+ options={{ props: { autoComplete: 'off' } }}
79
+ components={{ ...widgets, ...components }}
80
+ plugins={{ ...plugins, dayjs }}
81
+ {...rest}
82
+ />
83
+ );
84
+ }
85
+ export type CustomFormRenderProps = FormRenderProps<any>;
86
+ export default function FormRender(props: CustomFormRenderProps) {
87
+ const { components, plugins, ...rest } = props;
88
+ return (
89
+ <FormRenderCore
90
+ options={{ props: { autoComplete: 'off' } }}
91
+ components={{ ...widgets, ...components }}
92
+ plugins={{ ...plugins, dayjs }}
93
+ {...rest}
94
+ />
95
+ );
96
+ }
97
+ ```
98
+ ### 2. Introduce the components that were registered in the first step.
99
+ ```javascript
100
+ import { Button } from 'antd';
101
+ import React, { useState } from 'react';
102
+ import FormRender, { useSimpleForm, useSimpleFormRender } from './form-render';
103
+ export default function Demo5(props) {
104
+
105
+ const watch = {
106
+ 'name2': (newValue, oldValue) => {
107
+ console.log(newValue, oldValue)
108
+ },
109
+ 'name3[0]': (newValue, oldValue) => {
110
+ console.log(newValue, oldValue)
111
+ },
112
+ 'name4': (newValue, oldValue) => {
113
+ console.log(newValue, oldValue)
114
+ }
115
+ }
116
+
117
+ const properties = {
118
+ name1: {
119
+ label: "readonly",
120
+ readOnly: true,
121
+ readOnlyRender: "readonly component",
122
+ initialValue: 1111,
123
+ hidden: '{{formvalues && formvalues.name6 == true}}',
124
+ type: 'Input',
125
+ props: {}
126
+ },
127
+ name2: {
128
+ label: "input",
129
+ rules: [{ required: true, message: 'input empty' }],
130
+ initialValue: 1,
131
+ hidden: '{{formvalues && formvalues.name6 == true}}',
132
+ type: 'Input',
133
+ props: {}
134
+ },
135
+ name3: {
136
+ // type: '',
137
+ // props: {},
138
+ properties: [{
139
+ label: 'list[0]',
140
+ rules: [{ required: true, message: 'list[0] empty' }],
141
+ initialValue: { label: 'option1', value: '1', key: '1' },
142
+ type: 'Select',
143
+ props: {
144
+ labelInValue: true,
145
+ style: { width: '100%' },
146
+ children: [
147
+ { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
148
+ { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
149
+ ]
150
+ }
151
+ }, {
152
+ label: 'list[1]',
153
+ rules: [{ required: true, message: 'list[1] empty' }],
154
+ type: 'Select',
155
+ props: {
156
+ labelInValue: true,
157
+ style: { width: '100%' },
158
+ children: [
159
+ { type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } },
160
+ { type: 'Select.Option', props: { key: 2, value: '2', children: 'option2' } }
161
+ ]
162
+ }
163
+ }]
164
+ },
165
+ name4: {
166
+ // type: '',
167
+ // props: {},
168
+ properties: {
169
+ first: {
170
+ label: 'first',
171
+ rules: [{ required: true, message: 'first empty' }],
172
+ type: 'Select',
173
+ props: {
174
+ style: { width: '100%' },
175
+ children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
176
+ }
177
+ },
178
+ second: {
179
+ label: 'second',
180
+ rules: [{ required: true, message: 'second empty' }],
181
+ type: 'Select',
182
+ props: {
183
+ style: { width: '100%' },
184
+ children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
185
+ }
186
+ }
187
+ }
188
+ },
189
+ name5: {
190
+ label: 'name5',
191
+ initialValue: { span: 12 },
192
+ valueSetter: "{{(value)=> (value && value['span'])}}",
193
+ valueGetter: "{{(value) => ({span: value})}}",
194
+ type: 'Select',
195
+ props: {
196
+ style: { width: '100%' },
197
+ children: [
198
+ { type: 'Select.Option', props: { key: 1, value: 12, children: 'option1' } },
199
+ { type: 'Select.Option', props: { key: 2, value: 6, children: 'option2' } },
200
+ { type: 'Select.Option', props: { key: 3, value: 4, children: 'option3' } }
201
+ ]
202
+ }
203
+ },
204
+ name6: {
205
+ label: 'checkbox',
206
+ valueProp: 'checked',
207
+ initialValue: true,
208
+ rules: [{ required: true, message: 'checkbox empty' }],
209
+ type: 'Checkbox',
210
+ props: {
211
+ style: { width: '100%' },
212
+ children: 'option'
213
+ }
214
+ },
215
+ }
216
+
217
+ const form = useSimpleForm();
218
+ // const formrender = useSimpleFormRender();
219
+
220
+ const onSubmit = async (e) => {
221
+ e?.preventDefault?.();
222
+ const result = await form.validate();
223
+ console.log(result, 'result');
224
+ };
225
+
226
+ return (
227
+ <div>
228
+ <FormRender
229
+ form={form}
230
+ // formrender={formrender}
231
+ properties={properties}
232
+ watch={watch} />
233
+ <div style={{ marginLeft: '120px' }}>
234
+ <Button onClick={onSubmit}>submit</Button>
235
+ </div>
236
+ </div>
237
+ );
238
+ }
239
+ ```
240
+ ### 3. Multi-module rendering
241
+ The form engine also supports rendering by multiple `FormChildren` components, which then unify the processing of form values by the `Form` component.
242
+ - `useSimpleForm`: Creates a managed instance of a form value.
243
+ - `useSimpleFormRender`: creates an instance that renders the form.
244
+ ```javascript
245
+ import React, { useState } from 'react';
246
+ import { FormChildren, Form, useSimpleForm } from './form-render';
247
+ import { Button } from 'antd';
248
+ export default function Demo(props) {
249
+
250
+ const properties1 = {
251
+ part1: {
252
+ label: "part1input",
253
+ rules: [{ required: true, message: 'name1 empty' }],
254
+ initialValue: 1,
255
+ type: 'Input',
256
+ props: {}
257
+ },
258
+ }
259
+
260
+ const properties2 = {
261
+ part2: {
262
+ label: "part2input",
263
+ rules: [{ required: true, message: 'name1 empty' }],
264
+ initialValue: 1,
265
+ type: 'Input',
266
+ props: {}
267
+ },
268
+ }
269
+
270
+ const form = useSimpleForm();
271
+ // const formrender1 = useSimpleFormRender()
272
+ // const formrender2 = useSimpleFormRender()
273
+
274
+ const onSubmit = async (e) => {
275
+ e?.preventDefault?.();
276
+ const result = await form.validate();
277
+ console.log(result, 'result');
278
+ };
279
+
280
+ return (
281
+ <div style={{ padding: '0 8px' }}>
282
+ <Form form={form}>
283
+ <div>
284
+ <p>part1</p>
285
+ <FormChildren
286
+ // formrender={formrender1}
287
+ properties={properties1}
288
+ />
289
+ </div>
290
+ <div>
291
+ <p>part2</p>
292
+ <FormChildren
293
+ // formrender={formrender2}
294
+ properties={properties2}
295
+ />
296
+ </div>
297
+ </Form>
298
+ <div style={{ marginLeft: '120px' }}>
299
+ <Button onClick={onSubmit}>submit</Button>
300
+ </div>
301
+ </div>
302
+ );
303
+ }
304
+ ```
305
+ ### 4. List Demo
306
+ 支持数组渲染
307
+ ```javascript
308
+ import React, { useState } from 'react';
309
+ import FormRender, { useSimpleForm } from './form-render';
310
+ import { Button } from 'antd';
311
+ export default function Demo(props) {
312
+ const form = useSimpleForm();
313
+ const properties =
314
+ [
315
+ {
316
+ label: "list-0",
317
+ rules: [{ required: true, message: 'name1 empty' }],
318
+ initialValue: 1,
319
+ type: 'Input',
320
+ props: {}
321
+ },
322
+ {
323
+ label: "list-1",
324
+ rules: [{ required: true, message: 'name1 empty' }],
325
+ initialValue: 2,
326
+ type: 'Input',
327
+ props: {}
328
+ },
329
+ {
330
+ label: "list-2",
331
+ rules: [{ required: true, message: 'name1 empty' }],
332
+ initialValue: 3,
333
+ type: 'Input',
334
+ props: {}
335
+ },
336
+ {
337
+ label: "list-3",
338
+ rules: [{ required: true, message: 'name1 empty' }],
339
+ initialValue: 4,
340
+ type: 'Input',
341
+ props: {}
342
+ },
343
+ ];
344
+
345
+ const onSubmit = async (e) => {
346
+ e?.preventDefault?.();
347
+ const result = await form.validate();
348
+ console.log(result, 'result');
349
+ };
350
+
351
+ return (
352
+ <div style={{ padding: '0 8px' }}>
353
+ <FormRender
354
+ form={form}
355
+ properties={properties}
356
+ />
357
+ <div style={{ marginLeft: '120px' }}>
358
+ <Button onClick={onSubmit}>submit</Button>
359
+ </div>
360
+ </div>
361
+ );
362
+ }
363
+ ```
364
+ ## API
365
+
366
+ ### Form`s props
367
+ Sourced from [@simpleform/form](../form);
368
+
369
+ ### FormChildren's props
370
+ - `properties`: `{ [name: string]: FormNodeProps } | FormNodeProps[]` Renders the form's DSL form json data
371
+ - `components`: registers all components in the form.
372
+ - `plugins`: foreign libraries to be introduced in the form.
373
+ - `options`: `GenerateFormNodeProps | ((field: GenerateFormNodeProps) => any)` Parameter information passed to the form node components. The priority is lower than the form node's own parameters
374
+ - `renderList`: Provides a function to customize the rendered list.
375
+ - `renderItem`: provides a function to customize the rendering of the node.
376
+ - `onPropertiesChange`: `(newValue: PropertiesData) => void;` `Properties` change callback function.
377
+ - `formrender`: The form class responsible for rendering. Created by `useSimpleFormRender()`, optional.
378
+ - `form`: Class responsible for the value of the form. Created via `useSimpleForm()`, optional.
379
+ - `uneval`: does not execute string expressions in the form.
380
+
381
+ ### SimpleFormRender's Methods
382
+ - `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.
383
+ - `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.
384
+ - `updateNameByPath`: `(newName?: string, path: string) => void` Updates the name key of the specified path.
385
+ - `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.
386
+ - `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.
387
+ - `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.
388
+ - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` Swap options in the tree from one location to another.
389
+ - `setProperties`: `(data?: Partial<FormNodeProps>) => void` sets `properties`.
390
+
391
+ ### Hooks
392
+ - `useSimpleFormRender()`: create `new SimpleFormRender()`.
393
+ - `useSimpleForm(defaultValues)`: create `new SimpleForm()`
394
+
395
+ ## Other
396
+
397
+ ### Properties structure description
398
+ Each item in the `properties` property is a form node, and the nodes are categorized into nested nodes and control nodes.
399
+ - Nested nodes.
400
+ 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`).
401
+ - Node.
402
+ A node with no `properties` attribute that carries the form field component (`Form.Item`) by default.
403
+ ```javascript
404
+ const properties = {
405
+ name3: {
406
+ // Nested nodes
407
+ // type: '',
408
+ // props: {},
409
+ properties: {
410
+ // Control node
411
+ first: {
412
+ label: 'first',
413
+ rules: [{ required: true, message: 'first empty' }],
414
+ type: 'Select',
415
+ props: {
416
+ style: { width: '100%' },
417
+ children: [{ type: 'Select.Option', props: { key: 1, value: '1', children: 'option1' } }]
418
+ }
419
+ }
420
+ }
421
+ },
422
+ }
423
+ ```
424
+ - Form Node's types
425
+ ```javascript
426
+ export interface FormComponent {
427
+ type?: string;
428
+ props?: any & { children?: any | Array<FormComponent> };
429
+ }
430
+
431
+ export type UnionComponent<P> =
432
+ | React.ComponentType<P>
433
+ | React.ForwardRefExoticComponent<P>
434
+ | React.FC<P>
435
+ | keyof React.ReactHTML;
436
+
437
+ // Component
438
+ export type CustomUnionType = FormComponent | Array<FormComponent> | UnionComponent<any> | Function | ReactNode
439
+ // FormRender's properties
440
+ export type PropertiesData = { [name: string]: FormNodeProps } | FormNodeProps[]
441
+ // field's props(String expressions after compilation)
442
+ export type GenerateFormNodeProps<T = {}> = FormComponent & FormItemProps & T & {
443
+ ignore?: boolean; // Mark the current node as a non-form node
444
+ inside?: CustomUnionType; // Nested components within nodes
445
+ outside?: CustomUnionType; // Nested components in the outer layer of the node
446
+ readOnly?: boolean; // read-only mode
447
+ readOnlyRender?: CustomUnionType; // Read-only mode rendering
448
+ typeRender?: CustomUnionType; // Registering components for custom rendering
449
+ properties?: PropertiesData;
450
+ hidden?: boolean;
451
+ }
452
+ // field's props(String expressions before compilation)
453
+ export type FormNodeProps = {
454
+ [key in keyof GenerateFormNodeProps]: key extends 'rules' ?
455
+ (string | Array<{ [key in keyof FormRule]: FormRule[key] | string }> | GenerateFormNodeProps[key])
456
+ : (key extends 'properties' ? GenerateFormNodeProps[key] : (string | GenerateFormNodeProps[key]))
457
+ }
458
+ ```
459
+ ### parameter injection
460
+ - The properties of the form node are set globally:
461
+ ```javascript
462
+
463
+ <FormRender
464
+ options={{
465
+ layout: 'vertical',
466
+ props: { disabled: true }
467
+ }}
468
+ />
469
+ ```
470
+
471
+ - Parameters received by the registration component of the form:
472
+ ```javascript
473
+ export interface GenerateParams<T = {}> {
474
+ name?: string;
475
+ path?: string;
476
+ field?: GenerateFormNodeProps<T>;
477
+ parent?: { name?: string; path?: string, field?: GenerateFormNodeProps<T>; };
478
+ formrender?: SimpleFormRender;
479
+ form?: SimpleForm;
480
+ };
481
+ ```
482
+ ### Path path rules involved in forms
483
+ Forms are allowed to be nested, so the form involves looking for a certain property. The paths follow certain rules
484
+ Example:
485
+ - `a[0]` denotes the first option below the array `a`
486
+ - `a.b` denotes the `b` attribute of the `a` object
487
+ - `a[0].b` represents the `b` attribute of the first option below the array `a`
488
+
489
+ ### String Expression Usage
490
+ Property fields in a form node can support string expressions for linkage, except for `properties`.
491
+ 1. Quick use: computational expressions wrapping target attribute values in `{{` and `}}`
492
+ ```javascript
493
+ const properties = {
494
+ name1: {
495
+ label: 'name1',
496
+ valueProp: 'checked',
497
+ initialValue: true,
498
+ type: 'Checkbox',
499
+ props: {
500
+ children: 'option'
501
+ }
502
+ },
503
+ name2: {
504
+ label: "name2",
505
+ rules: '{{[{ required: formvalues && formvalues.name1 === true, message: "name2 empty" }]}}',
506
+ initialValue: 1,
507
+ type: 'Input',
508
+ props: {}
509
+ },
510
+ }
511
+
512
+ // OR
513
+
514
+ const properties = {
515
+ name1: {
516
+ label: 'name1',
517
+ valueProp: 'checked',
518
+ initialValue: true,
519
+ type: 'Checkbox',
520
+ props: {
521
+ children: 'option'
522
+ }
523
+ },
524
+ name2: {
525
+ label: "name2",
526
+ hidden: '{{formvalues && formvalues.name1 === true}}',
527
+ initialValue: 1,
528
+ type: 'Input',
529
+ props: {}
530
+ },
531
+ }
532
+ ```
533
+ 2. Rules for the use of string expressions
534
+ - A string has and can have only one pair of `{{` and `}}`.
535
+ - In addition to the three built-in variables (`form` (i.e., `useSimpleForm()`), `formrender` (i.e., `useSimpleFormRender()`), `formvalues`)), you can introduce an external variable via `plugins` and then reference the variable name directly in the string expression. in a string expression.
536
+ ```javascript
537
+ import dayjs from 'dayjs';
538
+ import FormRender from "./form-render";
539
+
540
+ const properties = {
541
+ name3: {
542
+ label: "name3",
543
+ initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
544
+ type: 'Input',
545
+ props: {}
546
+ },
547
+ }
548
+
549
+ <FormRender properties={properties} plugins={{ dayjs }} />
551
550
  ```