@simpleform/render 3.0.13 → 3.0.15

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,18 +2,14 @@
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.15-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
- - 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
- - 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
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.
10
+ - Component registration (`components` property): We need to register form controls and non-form components before using them, in case of form controls we need to support `value` and `onChange` `props` inside the control.
11
+ - Component Description (`widgetList` property): We use a list to describe the UI structure, each item in the list represents a component node. Node nesting is supported.
12
+ - Component Rendering: `Form` component handles form values, `FormChildren` component handles form rendering, a `Form` component can support multiple `FormChildren` components for internal rendering.
17
13
  - Component linkage: All form properties can support string expressions to describe linkage conditions (except `widgetList` property).
18
14
 
19
15
  ## install
@@ -24,553 +20,3 @@ npm install @simpleform/render --save
24
20
  # 或者
25
21
  yarn add @simpleform/render
26
22
  ```
27
- ## Quick Start
28
-
29
- ### 1. First register the basic components (Take antd@5.x as an example)
30
- ```javascript
31
- // register
32
- import DefaultFormRender, { FormChildren as DefaultFormChildren, FormRenderProps, FormChildrenProps } from '@simpleform/render';
33
- import '@simpleform/render/lib/css/main.css';
34
- import React from 'react';
35
- import dayjs from 'dayjs';
36
- import {
37
- Input,
38
- InputNumber,
39
- Checkbox,
40
- DatePicker,
41
- Mentions,
42
- Radio,
43
- Rate,
44
- Select,
45
- Slider,
46
- Switch,
47
- TimePicker,
48
- TreeSelect,
49
- } from 'antd';
50
-
51
- export * from '@simpleform/render';
52
-
53
- export const widgets = {
54
- "Input": Input,
55
- "Input.TextArea": Input.TextArea,
56
- "Input.Password": Input.Password,
57
- "Input.Search": Input.Search,
58
- "InputNumber": InputNumber,
59
- "Checkbox": Checkbox,
60
- 'Checkbox.Group': Checkbox.Group,
61
- "DatePicker": DatePicker,
62
- "DatePicker.RangePicker": DatePicker.RangePicker,
63
- "Mentions": Mentions,
64
- "Mentions.Option": Mentions.Option,
65
- "Radio": Radio,
66
- "Radio.Group": Radio.Group,
67
- "Radio.Button": Radio.Button,
68
- "Rate": Rate,
69
- "Select": Select,
70
- "Select.Option": Select.Option,
71
- "TreeSelect": TreeSelect,
72
- "Slider": Slider,
73
- "Switch": Switch,
74
- "TimePicker": TimePicker,
75
- "TimePicker.RangePicker": TimePicker.RangePicker
76
- };
77
-
78
- export type CustomFormChildrenProps = FormChildrenProps<any>;
79
- export function FormChildren(props: CustomFormChildrenProps) {
80
- const { components, plugins, ...rest } = props;
81
- return (
82
- <DefaultFormChildren
83
- options={{ props: { autoComplete: 'off' } }}
84
- components={{ ...widgets, ...components }}
85
- plugins={{ ...plugins, dayjs }}
86
- {...rest}
87
- />
88
- );
89
- }
90
- export type CustomFormRenderProps = FormRenderProps<any>;
91
- export default function FormRender(props: CustomFormRenderProps) {
92
- const { components, plugins, ...rest } = props;
93
- return (
94
- <DefaultFormRender
95
- options={{ props: { autoComplete: 'off' } }}
96
- components={{ ...widgets, ...components }}
97
- plugins={{ ...plugins, dayjs }}
98
- {...rest}
99
- />
100
- );
101
- };
102
- ```
103
- ### 2. Introduce the components that were registered in the first step.
104
- ```javascript
105
- import { Button } from 'antd';
106
- import React, { useState } from 'react';
107
- 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
- }
121
-
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
- ]
220
-
221
- const form = useSimpleForm();
222
- // const formrender = useSimpleFormRender();
223
-
224
- const onSubmit = async (e) => {
225
- e?.preventDefault?.();
226
- const result = await form.validate();
227
- console.log(result, 'result');
228
- };
229
-
230
- return (
231
- <div>
232
- <FormRender
233
- form={form}
234
- // formrender={formrender}
235
- widgetList={widgetList}
236
- watch={watch} />
237
- <div style={{ marginLeft: '120px' }}>
238
- <Button onClick={onSubmit}>submit</Button>
239
- </div>
240
- </div>
241
- );
242
- }
243
- ```
244
- ### 3. Multi-module rendering
245
- The form engine also supports rendering by multiple `FormChildren` components, which then unify the processing of form values by the `Form` component.
246
- - `useSimpleForm`: Creates a managed instance of a form value.
247
- - `useSimpleFormRender`: creates an instance that renders the form.
248
- ```javascript
249
- import React, { useState } from 'react';
250
- import { FormChildren, Form, useSimpleForm } from './form-render';
251
- import { Button } from 'antd';
252
- export default function Demo(props) {
253
-
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
- }]
271
-
272
- const form = useSimpleForm();
273
- // const formrender1 = useSimpleFormRender()
274
- // const formrender2 = useSimpleFormRender()
275
-
276
- const onSubmit = async (e) => {
277
- e?.preventDefault?.();
278
- const result = await form.validate();
279
- console.log(result, 'result');
280
- };
281
-
282
- return (
283
- <div style={{ padding: '0 8px' }}>
284
- <Form form={form}>
285
- <div>
286
- <p>part1</p>
287
- <FormChildren
288
- // formrender={formrender1}
289
- widgetList={widgetList1}
290
- />
291
- </div>
292
- <div>
293
- <p>part2</p>
294
- <FormChildren
295
- // formrender={formrender2}
296
- widgetList={widgetList2}
297
- />
298
- </div>
299
- </Form>
300
- <div style={{ marginLeft: '120px' }}>
301
- <Button onClick={onSubmit}>submit</Button>
302
- </div>
303
- </div>
304
- );
305
- }
306
- ```
307
- ### 4. List Demo
308
- Support for array rendering
309
- ```javascript
310
- import React, { useEffect, useState } from 'react';
311
- import FormRender, { CustomFormRenderProps, useSimpleForm } from './form-render';
312
- import { Button } from 'antd';
313
-
314
- const OptionList = React.forwardRef<HTMLElement, any>((props, ref) => {
315
-
316
- const {
317
- value,
318
- onChange,
319
- ...rest
320
- } = props;
321
-
322
- const intialValue = [{ label: '', value: '' }];
323
- const [dataSource, setDataSource] = useState<Array<any>>([]);
324
- const form = useSimpleForm();
325
-
326
- useEffect(() => {
327
- const options = value || [...intialValue];
328
- setDataSource(options);
329
- form.setFieldsValue(options);
330
- }, [value]);
331
-
332
- const widgetList = dataSource.map((item, index) => ({
333
- type: 'row',
334
- props: {
335
- gutter: 12,
336
- align: 'middle'
337
- },
338
- widgetList: [
339
- {
340
- name: `[${index}]label`,
341
- compact: true,
342
- outside: { type: 'col', props: { span: 9 } },
343
- rules: [{ required: true }],
344
- type: 'Input',
345
- props: {
346
- placeholder: 'label',
347
- style: { width: '100%' }
348
- }
349
- },
350
- {
351
- name: `[${index}]value`,
352
- compact: true,
353
- outside: { type: 'col', props: { span: 9 } },
354
- rules: [{ required: true }],
355
- type: 'Input',
356
- props: {
357
- placeholder: 'value',
358
- style: { width: '100%' }
359
- }
360
- },
361
- {
362
- outside: { type: 'col', props: { span: 6 } },
363
- typeRender: <Button type="link" onClick={() => deleteItem(index)}>delete</Button>
364
- },
365
- ]
366
- }));
367
-
368
- const deleteItem = (index: number) => {
369
- const oldData = [...dataSource];
370
- if (!oldData) return;
371
- const newData = [...oldData];
372
- newData.splice(index, 1);
373
- setDataSource(newData);
374
- form.setFieldsValue(newData);
375
- onChange && onChange(newData);
376
- };
377
-
378
- const addItem = () => {
379
- const { error } = await form.validate();
380
- if (error) {
381
- return;
382
- }
383
- const newData = dataSource.concat(intialValue);
384
- form.setFieldsValue(newData);
385
- setDataSource(newData);
386
- };
387
-
388
- const onFieldsChange: CustomFormRenderProps['onFieldsChange'] = (_, values) => {
389
- setDataSource(values);
390
- onChange && onChange(values);
391
- };
392
-
393
- return (
394
- <div>
395
- <FormRender
396
- form={form}
397
- widgetList={widgetList}
398
- onFieldsChange={onFieldsChange}
399
- />
400
- <Button type="link" onClick={addItem}>
401
- add
402
- </Button>
403
- </div>
404
- );
405
- });
406
-
407
- export default OptionList;
408
- ```
409
- ## API
410
-
411
- ### Form`s props
412
- Sourced from [@simpleform/form](../form);
413
-
414
- ### FormChildren's props
415
- - `widgetList`: `WidgetItem[]` Renders the form's DSL form json data
416
- - `components`: registers all components in the form.
417
- - `plugins`: foreign libraries to be introduced in the form.
418
- - `options`: `GenerateFormNodeProps | ((field: GenerateFormNodeProps) => any)` Parameter information passed to the form node components. The priority is lower than the form node's own parameters
419
- - `renderList`: Provides a function to customize the rendered list.
420
- - `renderItem`: provides a function to customize the rendering of the node.
421
- - `onRenderChange`: `(newValue: WidgetList) => void;` `onRenderChange` change callback function.
422
- - `formrender`: The form class responsible for rendering. Created by `useSimpleFormRender()`, optional.
423
- - `form`: Class responsible for the value of the form. Created via `useSimpleForm()`, optional.
424
- - `uneval`: does not execute string expressions in the form.
425
-
426
- ### SimpleFormRender's Methods
427
- - `updateItemByPath`: `(data?: any, path?: string) => void` Get the corresponding node based on `path`.
428
- - `setItemByPath`: `(data?: any, path?: string) => void` Sets the corresponding node according to `path`.
429
- - `delItemByPath`: `(path?: string) => void` Removes the node corresponding to the path `path`.
430
- - `insertItemByIndex`: `(data: WidgetItem | WidgetItem[], index?: number, parent?: string) => void` Adds a node based on the serial number and the path of the parent node.
431
- - `getItemByPath`: `(path: string) => void` Get the node corresponding to the path `path`.
432
- - `moveItemByPath`: `(from: { parent?: string, index: number }, to: { parent?: string, index?: number })` Swap options in the tree from one position to another
433
- - `setWidgetList`: `(data?: WidgetList) => void` Sets the `widgetList` attribute of the form.
434
-
435
- ### Hooks
436
- - `useSimpleFormRender()`: create `new SimpleFormRender()`.
437
- - `useSimpleForm(defaultValues)`: create `new SimpleForm()`
438
-
439
- ## Other
440
-
441
- ### widgetList structure description
442
- Each item in the `widgetList` list is a rendering node, rendered from `type` and `props`. divided into a form control node and nonform node.
443
- - Form control nodes.
444
- Nodes with the `name` attribute are form control nodes and carry the form field component (`Form.Item`) by default, for example:
445
- ```javascript
446
- const widgetList = [{
447
- label: "part2input",
448
- name: 'part2',
449
- rules: [{ required: true, message: 'part2 empty' }],
450
- initialValue: 1,
451
- type: 'Input',
452
- props: {}
453
- }]
454
- ```
455
- - nonform node.
456
- Nodes without the `name` attribute. Example:
457
- ```javascript
458
- const widgetList = [{
459
- type: 'CustomCard',
460
- props: {}
461
- }]
462
- ```
463
- - Form Node's types
464
- ```javascript
465
- export type GenerateWidgetItem<T extends Record<string, any> = {}> = FormItemProps & T & {
466
- inside?: CustomUnionType; // The inner layer of the node
467
- outside?: CustomUnionType; // The outside layer of the node
468
- readOnly?: boolean; // read-only mode
469
- readOnlyRender?: CustomUnionType; // Read-only mode rendering
470
- typeRender?: CustomUnionType; // Registering components for custom rendering
471
- hidden?: boolean;
472
- type?: string; // Register the component
473
- props?: Record<string, any> & { children?: any | Array<CustomWidget> }; // Register the component's props
474
- widgetList?: WidgetList; // children of the component(will override props.children)
475
- }
476
- ```
477
- ### parameter injection
478
- - The properties of the form node are set globally:
479
- Set the global properties of the form node via 'options'
480
- ```javascript
481
-
482
- ...
483
-
484
- <FormRender
485
- options={{
486
- layout: 'vertical',
487
- props: { disabled: true }
488
- }}
489
- />
490
- ```
491
-
492
- - Parameters received by the registration component of the form:
493
- The registration component in the form receives a context parameter
494
- ```javascript
495
- export interface GenerateParams<T = {}> {
496
- path?: string;
497
- widgetItem?: GenerateWidgetItem<T>;
498
- formrender?: SimpleFormRender;
499
- form?: SimpleForm;
500
- };
501
- ```
502
- ### Rules for the name field of form controls
503
- The `name` field can indicate the location path of a field in an array or object.
504
-
505
- Example:
506
- - `a[0]` denotes the first option below the array a
507
- - `a.b` indicates the b attribute of the a object
508
- - `a[0].b` denotes the b attribute of the first option below array a
509
-
510
- ### String Expression Usage
511
- Property fields in a form node can support string expressions for linkage, except for `widgetList`.
512
- 1. Quick use: computational expressions wrapping target attribute values in `{{` and `}}`
513
- ```javascript
514
-
515
- ...
516
-
517
- const widgetList = [
518
- {
519
- label: 'name1',
520
- name: 'name1',
521
- valueProp: 'checked',
522
- initialValue: true,
523
- type: 'Checkbox',
524
- props: {
525
- children: 'option'
526
- }
527
- },
528
- {
529
- label: "name2",
530
- name: 'name2',
531
- rules: '{{[{ required: formvalues && formvalues.name1 === true, message: "name2 empty" }]}}',
532
- initialValue: 1,
533
- type: 'Input',
534
- props: {}
535
- }
536
- ]
537
-
538
- // OR
539
-
540
- const widgetList = [
541
- {
542
- label: 'name1',
543
- name: 'name1',
544
- valueProp: 'checked',
545
- initialValue: true,
546
- type: 'Checkbox',
547
- props: {
548
- children: 'option'
549
- }
550
- },
551
- {
552
- label: "name2",
553
- name: 'name2',
554
- hidden: '{{formvalues && formvalues.name1 === true}}',
555
- initialValue: 1,
556
- type: 'Input',
557
- props: {}
558
- },
559
- ]
560
- ```
561
- 2. Rules for the use of string expressions
562
- - A string has and can have only one pair of `{{` and `}}`.
563
- - 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.
564
- ```javascript
565
- import dayjs from 'dayjs';
566
- import FormRender from "./form-render";
567
-
568
- const widgetList = [{
569
- label: "name3",
570
- initialValue: "{{dayjs().format('YYYY-MM-DD')}}",
571
- type: 'Input',
572
- props: {}
573
- }]
574
-
575
- <FormRender widgetList={widgetList} plugins={{ dayjs }} />
576
- ```