@becklyn/forms 2.0.4 → 3.0.1
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 +221 -166
- package/dist/cjs/components/FormBuilder.d.ts +1 -1
- package/dist/cjs/components/FormBuilder.d.ts.map +1 -1
- package/dist/cjs/components/FormBuilder.js +0 -1
- package/dist/cjs/components/FormCustom.d.ts +4 -4
- package/dist/cjs/components/FormCustom.d.ts.map +1 -1
- package/dist/cjs/components/FormCustom.js +0 -1
- package/dist/cjs/components/FormEntry.d.ts +4 -4
- package/dist/cjs/components/FormEntry.d.ts.map +1 -1
- package/dist/cjs/components/FormEntry.js +0 -1
- package/dist/cjs/components/FormField.d.ts +3 -3
- package/dist/cjs/components/FormField.d.ts.map +1 -1
- package/dist/cjs/components/FormField.js +0 -1
- package/dist/cjs/components/FormRow.d.ts +4 -4
- package/dist/cjs/components/FormRow.d.ts.map +1 -1
- package/dist/cjs/components/FormRow.js +0 -1
- package/dist/cjs/components/FormSection.d.ts +4 -4
- package/dist/cjs/components/FormSection.d.ts.map +1 -1
- package/dist/cjs/components/FormSection.js +0 -1
- package/dist/cjs/condition/index.d.ts +2 -2
- package/dist/cjs/condition/index.d.ts.map +1 -1
- package/dist/cjs/context/FormProvider.d.ts +4 -6
- package/dist/cjs/context/FormProvider.d.ts.map +1 -1
- package/dist/cjs/context/FormProvider.js +24 -5
- package/dist/cjs/context/context.d.ts +8 -10
- package/dist/cjs/context/context.d.ts.map +1 -1
- package/dist/cjs/context/data/FormDataProvider.d.ts +8 -8
- package/dist/cjs/context/data/FormDataProvider.d.ts.map +1 -1
- package/dist/cjs/context/data/FormDataProvider.js +1 -2
- package/dist/cjs/context/data/context.d.ts +8 -8
- package/dist/cjs/context/data/context.d.ts.map +1 -1
- package/dist/cjs/guard/index.d.ts +4 -4
- package/dist/cjs/guard/index.d.ts.map +1 -1
- package/dist/cjs/hook/useFormData.d.ts +1 -2
- package/dist/cjs/hook/useFormData.d.ts.map +1 -1
- package/dist/cjs/hook/useFormValidations.d.ts +6 -5
- package/dist/cjs/hook/useFormValidations.d.ts.map +1 -1
- package/dist/cjs/hook/useFormValidations.js +0 -1
- package/dist/cjs/type/config.d.ts +11 -8
- package/dist/cjs/type/config.d.ts.map +1 -1
- package/dist/cjs/type/extract.d.ts +2 -2
- package/dist/cjs/type/extract.d.ts.map +1 -1
- package/dist/cjs/type/field.d.ts +11 -11
- package/dist/cjs/type/field.d.ts.map +1 -1
- package/dist/cjs/type/form.d.ts +7 -10
- package/dist/cjs/type/form.d.ts.map +1 -1
- package/dist/cjs/type/validation.d.ts +5 -7
- package/dist/cjs/type/validation.d.ts.map +1 -1
- package/dist/cjs/util/index.d.ts +28 -8
- package/dist/cjs/util/index.d.ts.map +1 -1
- package/dist/cjs/util/index.js +17 -21
- package/dist/cjs/validation/index.d.ts +3 -3
- package/dist/cjs/validation/index.d.ts.map +1 -1
- package/dist/es/components/FormBuilder.d.ts +1 -1
- package/dist/es/components/FormBuilder.d.ts.map +1 -1
- package/dist/es/components/FormBuilder.js +0 -1
- package/dist/es/components/FormCustom.d.ts +4 -4
- package/dist/es/components/FormCustom.d.ts.map +1 -1
- package/dist/es/components/FormCustom.js +0 -1
- package/dist/es/components/FormEntry.d.ts +4 -4
- package/dist/es/components/FormEntry.d.ts.map +1 -1
- package/dist/es/components/FormEntry.js +0 -1
- package/dist/es/components/FormField.d.ts +3 -3
- package/dist/es/components/FormField.d.ts.map +1 -1
- package/dist/es/components/FormField.js +0 -1
- package/dist/es/components/FormRow.d.ts +4 -4
- package/dist/es/components/FormRow.d.ts.map +1 -1
- package/dist/es/components/FormRow.js +0 -1
- package/dist/es/components/FormSection.d.ts +4 -4
- package/dist/es/components/FormSection.d.ts.map +1 -1
- package/dist/es/components/FormSection.js +0 -1
- package/dist/es/condition/index.d.ts +2 -2
- package/dist/es/condition/index.d.ts.map +1 -1
- package/dist/es/context/FormProvider.d.ts +4 -6
- package/dist/es/context/FormProvider.d.ts.map +1 -1
- package/dist/es/context/FormProvider.js +11 -5
- package/dist/es/context/context.d.ts +8 -10
- package/dist/es/context/context.d.ts.map +1 -1
- package/dist/es/context/data/FormDataProvider.d.ts +8 -8
- package/dist/es/context/data/FormDataProvider.d.ts.map +1 -1
- package/dist/es/context/data/FormDataProvider.js +0 -2
- package/dist/es/context/data/context.d.ts +8 -8
- package/dist/es/context/data/context.d.ts.map +1 -1
- package/dist/es/guard/index.d.ts +4 -4
- package/dist/es/guard/index.d.ts.map +1 -1
- package/dist/es/hook/useFormData.d.ts +1 -2
- package/dist/es/hook/useFormData.d.ts.map +1 -1
- package/dist/es/hook/useFormValidations.d.ts +6 -5
- package/dist/es/hook/useFormValidations.d.ts.map +1 -1
- package/dist/es/hook/useFormValidations.js +0 -1
- package/dist/es/type/config.d.ts +11 -8
- package/dist/es/type/config.d.ts.map +1 -1
- package/dist/es/type/extract.d.ts +2 -2
- package/dist/es/type/extract.d.ts.map +1 -1
- package/dist/es/type/field.d.ts +11 -11
- package/dist/es/type/field.d.ts.map +1 -1
- package/dist/es/type/form.d.ts +7 -10
- package/dist/es/type/form.d.ts.map +1 -1
- package/dist/es/type/validation.d.ts +5 -7
- package/dist/es/type/validation.d.ts.map +1 -1
- package/dist/es/util/index.d.ts +28 -8
- package/dist/es/util/index.d.ts.map +1 -1
- package/dist/es/util/index.js +17 -21
- package/dist/es/validation/index.d.ts +3 -3
- package/dist/es/validation/index.d.ts.map +1 -1
- package/package.json +1 -5
- package/dist/cjs/hoc/withForm.d.ts +0 -8
- package/dist/cjs/hoc/withForm.d.ts.map +0 -1
- package/dist/cjs/hoc/withForm.js +0 -17
- package/dist/cjs/hoc/withFormData.d.ts +0 -8
- package/dist/cjs/hoc/withFormData.d.ts.map +0 -1
- package/dist/cjs/hoc/withFormData.js +0 -15
- package/dist/es/hoc/withForm.d.ts +0 -8
- package/dist/es/hoc/withForm.d.ts.map +0 -1
- package/dist/es/hoc/withForm.js +0 -17
- package/dist/es/hoc/withFormData.d.ts +0 -8
- package/dist/es/hoc/withFormData.d.ts.map +0 -1
- package/dist/es/hoc/withFormData.js +0 -15
package/README.md
CHANGED
|
@@ -1,90 +1,128 @@
|
|
|
1
1
|
## Table of Contents
|
|
2
2
|
|
|
3
|
-
1. [
|
|
4
|
-
|
|
3
|
+
1. [Getting Started](#getting-started)
|
|
4
|
+
|
|
5
5
|
1. [Install dependencies](#1-install-dependencies)
|
|
6
|
-
2. [Create
|
|
7
|
-
3. [Create a
|
|
8
|
-
4. [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
4. [Guides](#guides)
|
|
6
|
+
2. [Create your fields](#2-create-your-fields)
|
|
7
|
+
3. [Create a field component](#3-create-a-field-component)
|
|
8
|
+
4. [Define your initial state](#4-define-your-initial-state)
|
|
9
|
+
5. [Create a form config](#5-create-a-form-config)
|
|
10
|
+
6. [Render the form](#6-render-the-form)
|
|
11
|
+
|
|
12
|
+
2. [Advanced usecases](#advanced-usecases)
|
|
13
|
+
1. [Conditional fields](#conditional-fields)
|
|
14
|
+
3. [Guides](#guides)
|
|
16
15
|
1. [Multistep](#multistep)
|
|
17
16
|
|
|
18
|
-
##
|
|
17
|
+
## Getting Started
|
|
18
|
+
|
|
19
|
+
### 1. Install dependencies
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
npm i --save @becklyn/forms
|
|
23
|
+
```
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### 2. Create your fields
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
Start by building your form fields:
|
|
23
28
|
|
|
24
29
|
```typescript
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
validation: validateEmail,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
fieldConfig: {
|
|
35
|
-
placeholder: "E-Mail Adresse",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
]);
|
|
39
|
-
```
|
|
30
|
+
interface BaseFieldConfig {
|
|
31
|
+
placeholder: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
maxLength?: number;
|
|
34
|
+
}
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
type StringFieldConfig = FormFieldConfig<"string", BaseFieldConfig, string | null>;
|
|
37
|
+
type NumberFieldConfig = FormFieldConfig<"number", BaseFieldConfig, number>;
|
|
38
|
+
type AllConfigs = StringFieldConfig | NumberFieldConfig;
|
|
39
|
+
|
|
40
|
+
interface TextProps {
|
|
41
|
+
id: string;
|
|
42
|
+
value?: string | null;
|
|
43
|
+
onBlur: () => void;
|
|
44
|
+
name: string;
|
|
45
|
+
onInput: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
46
|
+
fieldConfig: FormFieldConfigFunc<GlobalFormData, string | null, BaseFieldConfig>;
|
|
47
|
+
}
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
const Text = ({ id, value, onBlur, name, onInput, fieldConfig }: TextProps) => {
|
|
50
|
+
return (
|
|
51
|
+
<input
|
|
52
|
+
{...fieldConfig}
|
|
53
|
+
id={id}
|
|
54
|
+
value={value ?? ""}
|
|
55
|
+
onBlur={onBlur}
|
|
56
|
+
name={name}
|
|
57
|
+
type="text"
|
|
58
|
+
onChange={onInput}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
interface NumberProps {
|
|
64
|
+
id: string;
|
|
65
|
+
value?: number;
|
|
66
|
+
onBlur: () => void;
|
|
67
|
+
name: string;
|
|
68
|
+
onInput: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
69
|
+
fieldConfig: FormFieldConfigFunc<GlobalFormData, number, BaseFieldConfig>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const Number = ({ id, value, onBlur, name, onInput, fieldConfig }: NumberProps) => {
|
|
73
|
+
return (
|
|
74
|
+
<input
|
|
75
|
+
{...fieldConfig}
|
|
76
|
+
id={id}
|
|
77
|
+
value={value ?? ""}
|
|
78
|
+
onBlur={onBlur}
|
|
79
|
+
name={name}
|
|
80
|
+
type="number"
|
|
81
|
+
onChange={onInput}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
47
85
|
```
|
|
48
86
|
|
|
49
|
-
###
|
|
87
|
+
### 3. Create a field component
|
|
50
88
|
|
|
51
89
|
Create a single component to handle rendering all of your different input components. It will be called by the `FormBuilder` including the respective field's props.
|
|
52
90
|
|
|
53
91
|
```tsx
|
|
54
|
-
|
|
92
|
+
const FieldComponent: FC<FormBuilderChildrenProps<AllConfigs, GlobalFormData>> = ({
|
|
55
93
|
value,
|
|
56
|
-
error,
|
|
57
94
|
field,
|
|
58
|
-
onInput,
|
|
59
95
|
onBlur,
|
|
96
|
+
onInput,
|
|
60
97
|
}) => {
|
|
61
98
|
const id = useId();
|
|
62
99
|
|
|
63
100
|
switch (field.type) {
|
|
64
|
-
case "
|
|
101
|
+
case "string": {
|
|
65
102
|
const { name, fieldConfig } = field;
|
|
103
|
+
|
|
66
104
|
return (
|
|
67
105
|
<Text
|
|
68
|
-
{...fieldConfig}
|
|
69
106
|
id={id}
|
|
70
|
-
name={name}
|
|
71
107
|
value={value}
|
|
72
|
-
error={error}
|
|
73
|
-
onValueChanged={onInput}
|
|
74
108
|
onBlur={onBlur}
|
|
109
|
+
name={name}
|
|
110
|
+
onInput={onInput}
|
|
111
|
+
fieldConfig={fieldConfig}
|
|
75
112
|
/>
|
|
76
113
|
);
|
|
77
114
|
}
|
|
78
|
-
case "
|
|
115
|
+
case "number": {
|
|
79
116
|
const { name, fieldConfig } = field;
|
|
117
|
+
|
|
80
118
|
return (
|
|
81
|
-
<
|
|
82
|
-
{...fieldConfig}
|
|
119
|
+
<Number
|
|
83
120
|
id={id}
|
|
84
121
|
value={value}
|
|
85
|
-
error={error}
|
|
86
122
|
onBlur={onBlur}
|
|
87
|
-
|
|
123
|
+
name={name}
|
|
124
|
+
onInput={onInput}
|
|
125
|
+
fieldConfig={fieldConfig}
|
|
88
126
|
/>
|
|
89
127
|
);
|
|
90
128
|
}
|
|
@@ -92,36 +130,85 @@ export const FieldComponent: React.FC<FormBuilderChildrenProps<FieldTypes>> = ({
|
|
|
92
130
|
};
|
|
93
131
|
```
|
|
94
132
|
|
|
95
|
-
###
|
|
133
|
+
### 4. Define your initial state
|
|
134
|
+
|
|
135
|
+
After defining all fields that your forms may use you can define the initial state of your specific form in a flat config (field name -> initial value):
|
|
96
136
|
|
|
97
137
|
```typescript
|
|
98
|
-
|
|
138
|
+
const initialState = {
|
|
139
|
+
foo: generateInitialValue<StringFieldConfig>(""),
|
|
140
|
+
bar: generateInitialValue<NumberFieldConfig>(123),
|
|
141
|
+
f1: generateInitialValue<StringFieldConfig>(undefined),
|
|
142
|
+
f2: generateInitialValue<StringFieldConfig>(null),
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 5. Create a form config
|
|
147
|
+
|
|
148
|
+
Finally you can configure the structure of your form:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const userFormConfig = [
|
|
99
152
|
{
|
|
100
|
-
type: "
|
|
101
|
-
|
|
102
|
-
validations: [
|
|
153
|
+
type: "row",
|
|
154
|
+
content: [
|
|
103
155
|
{
|
|
104
|
-
|
|
156
|
+
type: "string",
|
|
157
|
+
name: "Firstname",
|
|
158
|
+
fieldConfig: {
|
|
159
|
+
placeholder: "Enter your firstname",
|
|
160
|
+
label: "Firstname",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
type: "string",
|
|
165
|
+
name: "Lastname",
|
|
166
|
+
valueFn: generateValueFn<FormData>(data => data.Lastname || "default value"),
|
|
167
|
+
onInput: generateOnInput<StringFieldConfig, FormData>(
|
|
168
|
+
({ field, value, previousData }) => {
|
|
169
|
+
console.log("value", value);
|
|
170
|
+
console.log("field", field);
|
|
171
|
+
|
|
172
|
+
return { ...previousData };
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
fieldConfig: generateFormFieldConfigFn<StringFieldConfig, FormData>(
|
|
176
|
+
({ data, value }) => {
|
|
177
|
+
console.log("data", data);
|
|
178
|
+
console.log("value", value);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
placeholder: "Enter your lastname",
|
|
182
|
+
label: "Lastname",
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
),
|
|
105
186
|
},
|
|
106
187
|
],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: "number",
|
|
191
|
+
name: "Age",
|
|
107
192
|
fieldConfig: {
|
|
108
|
-
placeholder: "
|
|
193
|
+
placeholder: "Enter your age",
|
|
194
|
+
label: "Age",
|
|
109
195
|
},
|
|
110
196
|
},
|
|
111
|
-
]
|
|
197
|
+
] as const satisfies FormConfig<AllConfigs, FormData>;
|
|
112
198
|
```
|
|
113
199
|
|
|
114
|
-
###
|
|
200
|
+
### 6. Render the form
|
|
115
201
|
|
|
116
|
-
To render your form config the FormBuilder needs to be a descendant of `FormProvider
|
|
202
|
+
To render your form config the FormBuilder needs to be a descendant of `FormProvider`. An implementation could look something like this where the majority can easily be abstracted.
|
|
117
203
|
|
|
118
204
|
```tsx
|
|
119
|
-
// the form renderer can be reused to render all kinds of forms
|
|
120
205
|
export const FormRenderer: React.FC = () => {
|
|
121
|
-
return
|
|
206
|
+
return (
|
|
207
|
+
<FormBuilder<AllConfigs, FormData>>{props => <FieldComponent {...props} />}</FormBuilder>
|
|
208
|
+
);
|
|
122
209
|
};
|
|
123
210
|
|
|
124
|
-
export const
|
|
211
|
+
export const UserFormHandler = () => {
|
|
125
212
|
const { validateForm } = useForm();
|
|
126
213
|
|
|
127
214
|
const handleSubmit = (e: React.FormEvent) => {
|
|
@@ -139,115 +226,66 @@ export const LoginFormHandler = withForm(() => {
|
|
|
139
226
|
<button type="submit">Submit</button>
|
|
140
227
|
</form>
|
|
141
228
|
);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
export const SomeComponent: React.FC = () => {
|
|
145
|
-
return <LoginFormHandler config={forgotPasswordFormConfig} />;
|
|
146
229
|
};
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## Typescript
|
|
150
|
-
|
|
151
|
-
### Providing your own types
|
|
152
|
-
|
|
153
|
-
Each field comes with default props (e.g `id`, `value`, `onValueChanged`), however it is also possible to pass additional props via `fieldConfig` that are relevant based on the type of the field (e.g `maxLength` for a text input).
|
|
154
|
-
|
|
155
|
-
#### 1. Define the field config
|
|
156
|
-
|
|
157
|
-
Your fields should export a config interface that includes all the specific props for that field:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
export interface TextFieldConfig {
|
|
161
|
-
placeholder: string;
|
|
162
|
-
label?: string;
|
|
163
|
-
maxLength?: number;
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
#### 2. Build intermediate type
|
|
168
|
-
|
|
169
|
-
Create an intermediate type that forms can consume:
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
export type FormFieldText = FormFieldConfig<"text", TextFieldConfig, string>;
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
The first parameter specifies to which field the fieldConfig will be applied. Here `"text"` means that in your config, if you add `type: "text"`, the `fieldConfig` will expect props specified in `TextFieldConfig`.
|
|
176
|
-
|
|
177
|
-
The third generic parameter in `FormFieldConfig` is the type of allowed values this field can handle.
|
|
178
|
-
|
|
179
|
-
Its useful to build a type that unifies all your fields:
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
export type FormFieldText = FormFieldConfig<"text", TextFieldConfig, string>;
|
|
183
|
-
export type FormFieldNumber = FormFieldConfig<"number", NumberFieldConfig, number>;
|
|
184
|
-
export type FormFieldSelect = FormFieldConfig<
|
|
185
|
-
"select",
|
|
186
|
-
SelectFieldConfig,
|
|
187
|
-
{ id: string; value: string }
|
|
188
|
-
>;
|
|
189
|
-
export type FormField = FormFieldText | FormFieldNumber | FormFieldSelect;
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Infer form config
|
|
193
230
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
231
|
+
export const Component = () => {
|
|
232
|
+
return (
|
|
233
|
+
<FormProvider<AllConfigs, typeof initialState>
|
|
234
|
+
config={userFormConfig}
|
|
235
|
+
initialData={initialState}>
|
|
236
|
+
<UserFormHandler />
|
|
237
|
+
</FormProvider>
|
|
238
|
+
);
|
|
239
|
+
};
|
|
198
240
|
```
|
|
199
241
|
|
|
200
|
-
|
|
242
|
+
## Advanced usecases
|
|
201
243
|
|
|
202
|
-
|
|
203
|
-
- Object literals must be readonly
|
|
204
|
-
- Array literals must be readonly tuples
|
|
244
|
+
### Conditional fields
|
|
205
245
|
|
|
206
|
-
|
|
246
|
+
To help out with conditional rendering, a function called `eitherOr` is provided.
|
|
207
247
|
|
|
208
248
|
```typescript
|
|
209
|
-
useMemo(() => {
|
|
210
|
-
return
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
const form = useMemo(() => {
|
|
250
|
+
return [
|
|
251
|
+
{
|
|
252
|
+
type: "row",
|
|
253
|
+
content: [
|
|
254
|
+
eitherOr(
|
|
255
|
+
someCondition,
|
|
256
|
+
{
|
|
257
|
+
type: "string",
|
|
258
|
+
name: "email",
|
|
259
|
+
fieldConfig: {
|
|
260
|
+
placeholder: "Email address",
|
|
261
|
+
label: "Email",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
type: "string",
|
|
266
|
+
name: "phone",
|
|
267
|
+
fieldConfig: {
|
|
268
|
+
placeholder: "Phone number",
|
|
269
|
+
label: "Phone",
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
),
|
|
273
|
+
eitherOr(
|
|
274
|
+
someCondition && {
|
|
275
|
+
type: "string",
|
|
276
|
+
name: "phone",
|
|
277
|
+
fieldConfig: {
|
|
278
|
+
placeholder: "Phone number",
|
|
279
|
+
label: "Phone",
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
),
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
] as const satisfies FormConfig<AllConfigs, FormData>;
|
|
240
286
|
}, [someCondition]);
|
|
241
287
|
```
|
|
242
288
|
|
|
243
|
-
The `FormBuilder` is generic, too:
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
export const FormRenderer: React.FC = () => {
|
|
247
|
-
return <FormBuilder<FieldTypes>>{props => <FieldComponent {...props} />}</FormBuilder>;
|
|
248
|
-
};
|
|
249
|
-
```
|
|
250
|
-
|
|
251
289
|
## Guides
|
|
252
290
|
|
|
253
291
|
### Multistep
|
|
@@ -256,7 +294,7 @@ Multistep can be achieved easily and abstracted into a custom component. The con
|
|
|
256
294
|
While this lib handles the data, validations etc., you must still handle the multistep logic yourself.
|
|
257
295
|
|
|
258
296
|
```tsx
|
|
259
|
-
const Step =
|
|
297
|
+
const Step = () => {
|
|
260
298
|
const { validateForm } = useForm<FormField>();
|
|
261
299
|
|
|
262
300
|
const handleSubmit = (e: React.FormEvent) => {
|
|
@@ -275,19 +313,36 @@ const Step = withForm<FormField>(() => {
|
|
|
275
313
|
<FormRenderer />
|
|
276
314
|
</form>
|
|
277
315
|
);
|
|
278
|
-
}
|
|
316
|
+
};
|
|
279
317
|
|
|
280
|
-
const MultiStep =
|
|
318
|
+
const MultiStep = () => {
|
|
281
319
|
// your custom hook
|
|
282
320
|
const { step } = useMultiStep();
|
|
283
321
|
|
|
322
|
+
let config, initialData;
|
|
323
|
+
|
|
284
324
|
switch (step) {
|
|
285
325
|
case 1:
|
|
286
|
-
|
|
326
|
+
config = firstStepConfig;
|
|
327
|
+
initialData = initialFirstStepState;
|
|
328
|
+
break;
|
|
287
329
|
case 2:
|
|
288
|
-
|
|
330
|
+
config = secondStepConfig;
|
|
331
|
+
initialData = initialSecondStepState;
|
|
332
|
+
break;
|
|
289
333
|
case 3:
|
|
290
|
-
|
|
334
|
+
config = thirdStepConfig;
|
|
335
|
+
initialData = initialThirdStepState;
|
|
336
|
+
break;
|
|
291
337
|
}
|
|
292
|
-
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<FormProvider<AllConfigs, typeof initialData>
|
|
341
|
+
config={config}
|
|
342
|
+
initialData={initialData}
|
|
343
|
+
inheritData>
|
|
344
|
+
<Step />
|
|
345
|
+
</FormProvider>
|
|
346
|
+
);
|
|
347
|
+
};
|
|
293
348
|
```
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { FormBuilderProps, FormFieldConfig } from "../type";
|
|
2
|
-
export declare const FormBuilder: <T extends FormFieldConfig<string, any, any>>({ Components, children, }: FormBuilderProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const FormBuilder: <T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>>({ Components, children, }: FormBuilderProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=FormBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormBuilder.d.ts","sourceRoot":"","sources":["../../../components/FormBuilder.tsx"],"names":[],"mappings":"AASA,OAAO,EAAyB,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"FormBuilder.d.ts","sourceRoot":"","sources":["../../../components/FormBuilder.tsx"],"names":[],"mappings":"AASA,OAAO,EAAyB,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAUnF,eAAO,MAAM,WAAW,GAEpB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,6BAI3C,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,4CAErC,CAAC"}
|
|
@@ -13,7 +13,6 @@ const DefaultComponents = {
|
|
|
13
13
|
RowWrapper: style_1.DefaultRowWrapper,
|
|
14
14
|
FieldWrapper: style_1.DefaultFieldWrapper,
|
|
15
15
|
};
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
16
|
const FormBuilder = ({ Components = DefaultComponents, children, }) => {
|
|
18
17
|
return (0, jsx_runtime_1.jsx)(FormBuilderComponent, { Components: Components, children: children });
|
|
19
18
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FormBuilderComponents, FormBuilderProps, FormCustomConfig, FormFieldConfig } from "../type";
|
|
2
|
-
interface FormCustomProps<T extends FormFieldConfig<string, any, any>> {
|
|
2
|
+
interface FormCustomProps<T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>> {
|
|
3
3
|
Components: FormBuilderComponents;
|
|
4
|
-
custom: FormCustomConfig<T>;
|
|
5
|
-
children: FormBuilderProps<T>["children"];
|
|
4
|
+
custom: FormCustomConfig<T, GlobalFormData>;
|
|
5
|
+
children: FormBuilderProps<T, GlobalFormData>["children"];
|
|
6
6
|
}
|
|
7
|
-
export declare const FormCustom: <T extends FormFieldConfig<string, any, any>>({ Components, custom, children, }: FormCustomProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const FormCustom: <T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>>({ Components, custom, children, }: FormCustomProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=FormCustom.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormCustom.d.ts","sourceRoot":"","sources":["../../../components/FormCustom.tsx"],"names":[],"mappings":"AACA,OAAO,EACH,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EAClB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"FormCustom.d.ts","sourceRoot":"","sources":["../../../components/FormCustom.tsx"],"names":[],"mappings":"AACA,OAAO,EACH,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EAClB,MAAM,SAAS,CAAC;AAGjB,UAAU,eAAe,CAErB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAE1C,UAAU,EAAE,qBAAqB,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAC5C,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,eAAO,MAAM,UAAU,GAEnB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,qCAK3C,eAAe,CAAC,CAAC,EAAE,cAAc,CAAC,4CAYpC,CAAC"}
|
|
@@ -7,7 +7,6 @@ exports.FormCustom = void 0;
|
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const FormEntry_1 = require("./FormEntry");
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
10
|
const FormCustom = ({ Components, custom, children, }) => {
|
|
12
11
|
return ((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: custom.wrapper(custom.content.map((entry, index) => ((0, jsx_runtime_1.jsx)(FormEntry_1.FormEntry, { entry: entry, Components: Components, children: children }, index)))) }));
|
|
13
12
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FormBuilderComponents, FormBuilderProps, FormEntryConfig, FormFieldConfig } from "../type";
|
|
2
|
-
interface FormEntryProps<T extends FormFieldConfig<string, any, any>> {
|
|
2
|
+
interface FormEntryProps<T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>> {
|
|
3
3
|
Components: FormBuilderComponents;
|
|
4
|
-
entry: FormEntryConfig<T>;
|
|
5
|
-
children: FormBuilderProps<T>["children"];
|
|
4
|
+
entry: FormEntryConfig<T, GlobalFormData>;
|
|
5
|
+
children: FormBuilderProps<T, GlobalFormData>["children"];
|
|
6
6
|
}
|
|
7
|
-
export declare const FormEntry: <T extends FormFieldConfig<string, any, any>>({ Components, entry, children, }: FormEntryProps<T>) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export declare const FormEntry: <T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>>({ Components, entry, children, }: FormEntryProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=FormEntry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormEntry.d.ts","sourceRoot":"","sources":["../../../components/FormEntry.tsx"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"FormEntry.d.ts","sourceRoot":"","sources":["../../../components/FormEntry.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAOpG,UAAU,cAAc,CAEpB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAE1C,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAC1C,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,eAAO,MAAM,SAAS,GAElB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,oCAK3C,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,mDA2DnC,CAAC"}
|
|
@@ -11,7 +11,6 @@ const FormCustom_1 = require("./FormCustom");
|
|
|
11
11
|
const FormField_1 = require("./FormField");
|
|
12
12
|
const FormRow_1 = require("./FormRow");
|
|
13
13
|
const FormSection_1 = require("./FormSection");
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
14
|
const FormEntry = ({ Components, entry, children, }) => {
|
|
16
15
|
const { data, fieldConfigs } = (0, context_1.useForm)();
|
|
17
16
|
const getHasChildren = (0, react_1.useCallback)((formData) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FormBuilderComponents, FormBuilderProps, FormFieldConfig } from "../type";
|
|
2
|
-
interface FormFieldProps<T extends FormFieldConfig<string, any, any>> {
|
|
2
|
+
interface FormFieldProps<T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>> {
|
|
3
3
|
Components: FormBuilderComponents;
|
|
4
4
|
field: T;
|
|
5
|
-
children: FormBuilderProps<T>["children"];
|
|
5
|
+
children: FormBuilderProps<T, GlobalFormData>["children"];
|
|
6
6
|
}
|
|
7
|
-
export declare const FormField: <T extends FormFieldConfig<string, any, any>>({ Components, field, children, }: FormFieldProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const FormField: <T extends FormFieldConfig<string, any, any, GlobalFormData>, GlobalFormData extends Record<string, any>>({ Components, field, children, }: FormFieldProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=FormField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../../../components/FormField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEH,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAClB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../../../components/FormField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEH,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAClB,MAAM,SAAS,CAAC;AAEjB,UAAU,cAAc,CAEpB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAE1C,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,eAAO,MAAM,SAAS,GAElB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,EAE3D,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,oCAK3C,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,4CA4GnC,CAAC"}
|
|
@@ -6,7 +6,6 @@ const react_1 = require("react");
|
|
|
6
6
|
const context_1 = require("../context");
|
|
7
7
|
const useFormData_1 = require("../hook/useFormData");
|
|
8
8
|
const useFormErrors_1 = require("../hook/useFormErrors");
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
9
|
const FormField = ({ Components, field, children, }) => {
|
|
11
10
|
const { editData, validationStrategy: contextValidationStrategy, validateField, onInput, } = (0, context_1.useForm)();
|
|
12
11
|
const { name, columns, conditions, validations, valueFn, validationStrategy: fieldValidationStrategy, onInput: onFieldInput, fieldConfig: fieldConfigFunc, } = field;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FormBuilderComponents, FormBuilderProps, FormFieldConfig, FormRowConfig } from "../type";
|
|
2
|
-
interface FormRowProps<T extends FormFieldConfig<string, any, any>> {
|
|
2
|
+
interface FormRowProps<T extends FormFieldConfig<string, any, any>, GlobalFormData extends Record<string, any>> {
|
|
3
3
|
Components: FormBuilderComponents;
|
|
4
|
-
row: FormRowConfig<T>;
|
|
5
|
-
children: FormBuilderProps<T>["children"];
|
|
4
|
+
row: FormRowConfig<T, GlobalFormData>;
|
|
5
|
+
children: FormBuilderProps<T, GlobalFormData>["children"];
|
|
6
6
|
}
|
|
7
|
-
export declare const FormRow: <T extends FormFieldConfig<string, any, any>>({ Components, row, children, }: FormRowProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const FormRow: <T extends FormFieldConfig<string, any, any>, GlobalFormData extends Record<string, any>>({ Components, row, children, }: FormRowProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=FormRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormRow.d.ts","sourceRoot":"","sources":["../../../components/FormRow.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"FormRow.d.ts","sourceRoot":"","sources":["../../../components/FormRow.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlG,UAAU,YAAY,CAElB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,EAE3C,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAE1C,UAAU,EAAE,qBAAqB,CAAC;IAClC,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IACtC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,eAAO,MAAM,OAAO,GAEhB,CAAC,SAAS,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,EAE3C,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,kCAK3C,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC,4CAgBjC,CAAC"}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FormRow = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const FormEntry_1 = require("./FormEntry");
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
6
|
const FormRow = ({ Components, row, children, }) => {
|
|
8
7
|
const { RowWrapper } = Components;
|
|
9
8
|
if (!RowWrapper) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FormBuilderComponents, FormBuilderProps, FormFieldConfig, FormSectionConfig } from "../type";
|
|
2
|
-
interface FormSectionProps<T extends FormFieldConfig<string, any, any>> {
|
|
2
|
+
interface FormSectionProps<T extends FormFieldConfig<string, any, any>, GlobalFormData extends Record<string, any>> {
|
|
3
3
|
Components: FormBuilderComponents;
|
|
4
|
-
section: FormSectionConfig<T>;
|
|
5
|
-
children: FormBuilderProps<T>["children"];
|
|
4
|
+
section: FormSectionConfig<T, GlobalFormData>;
|
|
5
|
+
children: FormBuilderProps<T, GlobalFormData>["children"];
|
|
6
6
|
}
|
|
7
|
-
export declare const FormSection: <T extends FormFieldConfig<string, any, any>>({ Components, section, children, }: FormSectionProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const FormSection: <T extends FormFieldConfig<string, any, any>, GlobalFormData extends Record<string, any>>({ Components, section, children, }: FormSectionProps<T, GlobalFormData>) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=FormSection.d.ts.map
|