@kaliber/forms 1.1.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -0
- package/package.json +1 -1
- package/readme.md +36 -1
- package/src/fields.js +20 -6
- package/src/hooks.js +1 -3
- package/src/snapshot.js +7 -3
package/index.js
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -132,6 +132,7 @@ const {
|
|
|
132
132
|
initialValues, // (optional) initial form values
|
|
133
133
|
validate, // (optional) validation for the complete form
|
|
134
134
|
onSubmit, // called when the form is submitted
|
|
135
|
+
formId, // (optional) a custom id, can be useful when multiple forms are placed on the same page
|
|
135
136
|
})
|
|
136
137
|
```
|
|
137
138
|
|
|
@@ -312,6 +313,40 @@ array(validation, fields)
|
|
|
312
313
|
array(fields)
|
|
313
314
|
```
|
|
314
315
|
|
|
316
|
+
If you want to use heterogeneous arrays (different types) you can use a `function` instead of a `fields` object:
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
array(initialValue => ({
|
|
320
|
+
_type: required,
|
|
321
|
+
...(
|
|
322
|
+
initialValue._type === 'content' ? { text: required } :
|
|
323
|
+
initialValue._type === 'image' ? { image: required } :
|
|
324
|
+
null
|
|
325
|
+
)
|
|
326
|
+
}))
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
When rendering the `array` field you can render a different component based on the value of the field:
|
|
330
|
+
|
|
331
|
+
```jsx
|
|
332
|
+
const { state: { children }, helpers } = useArrayFormField(field)
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<>
|
|
336
|
+
{children.map(field => {
|
|
337
|
+
const { _type } = field.value.get()
|
|
338
|
+
return (
|
|
339
|
+
_type === 'content' ? <ContentForm key={field.name} {...{ field }} /> :
|
|
340
|
+
_type === 'image' ? <ImageForm key={field.name} {...{ field }} /> :
|
|
341
|
+
null
|
|
342
|
+
)
|
|
343
|
+
})}
|
|
344
|
+
<button type='button' onClick={_ => helpers.add({ _type: 'content' })}>Add content</button>
|
|
345
|
+
<button type='button' onClick={_ => helpers.add({ _type: 'image' })}>Add image</button>
|
|
346
|
+
</>
|
|
347
|
+
)
|
|
348
|
+
```
|
|
349
|
+
|
|
315
350
|
#### object
|
|
316
351
|
|
|
317
352
|
Used to create an object form field.
|
|
@@ -360,7 +395,7 @@ import { ... } from '@kaliber/forms/validation'
|
|
|
360
395
|
|
|
361
396
|
### Components
|
|
362
397
|
|
|
363
|
-
When you want to conditionally render or set some props based on your current form state, you should avoid the use of `useFormFieldSnapshot` or `useFormFieldValue` in your form root, since that will re-render your entire form with each change. Rather you should create a specialised component and make the values available through a render prop.
|
|
398
|
+
When you want to conditionally render or set some props based on your current form state, you should avoid the use of `useFormFieldSnapshot` or `useFormFieldValue` in your form root, since that will re-render your entire form with each change. Rather you should create a specialised component and make the values available through a render prop.
|
|
364
399
|
|
|
365
400
|
Because this is such a common usecase, we provide several of these components.
|
|
366
401
|
|
package/src/fields.js
CHANGED
|
@@ -41,7 +41,9 @@ export function createObjectFormField({ name = '', initialValue = {}, field }) {
|
|
|
41
41
|
children.forEach(x => x.setSubmitted(isSubmitted))
|
|
42
42
|
},
|
|
43
43
|
reset() {
|
|
44
|
-
internalState.update(x =>
|
|
44
|
+
internalState.update(x =>
|
|
45
|
+
updateState(x, pick(initialState, ['isSubmitted']))
|
|
46
|
+
)
|
|
45
47
|
children.forEach(x => x.reset())
|
|
46
48
|
},
|
|
47
49
|
value,
|
|
@@ -65,6 +67,8 @@ export function createObjectFormField({ name = '', initialValue = {}, field }) {
|
|
|
65
67
|
|
|
66
68
|
function createArrayFormField({ name, initialValue = [], field }) {
|
|
67
69
|
|
|
70
|
+
let index = 0
|
|
71
|
+
|
|
68
72
|
const initialChildren = initialValue.map(createFormField)
|
|
69
73
|
const initialState = deriveFormFieldState({
|
|
70
74
|
children: initialChildren,
|
|
@@ -72,8 +76,6 @@ function createArrayFormField({ name, initialValue = [], field }) {
|
|
|
72
76
|
const internalState = createState(initialState)
|
|
73
77
|
const validate = bindValidate(field.validate, internalState)
|
|
74
78
|
|
|
75
|
-
let index = 0
|
|
76
|
-
|
|
77
79
|
const value = {
|
|
78
80
|
get() {
|
|
79
81
|
const { children } = internalState.get()
|
|
@@ -103,7 +105,9 @@ function createArrayFormField({ name, initialValue = [], field }) {
|
|
|
103
105
|
children.forEach(x => x.setSubmitted(isSubmitted))
|
|
104
106
|
},
|
|
105
107
|
reset() {
|
|
106
|
-
const { children } = internalState.update(x =>
|
|
108
|
+
const { children } = internalState.update(x =>
|
|
109
|
+
updateState(x, pick(initialState, ['children', 'isSubmitted']))
|
|
110
|
+
)
|
|
107
111
|
children.forEach(x => x.reset())
|
|
108
112
|
},
|
|
109
113
|
value,
|
|
@@ -126,10 +130,11 @@ function createArrayFormField({ name, initialValue = [], field }) {
|
|
|
126
130
|
|
|
127
131
|
function createFormField(initialValue) {
|
|
128
132
|
const fullName = `${name}[${index++}]`
|
|
133
|
+
const fields = typeof field.fields == 'function' ? field.fields(initialValue) : field.fields
|
|
129
134
|
return createObjectFormField({
|
|
130
135
|
name: fullName,
|
|
131
136
|
initialValue,
|
|
132
|
-
field: normalize({ type: 'object', fields
|
|
137
|
+
field: normalize({ type: 'object', fields }, fullName),
|
|
133
138
|
})
|
|
134
139
|
}
|
|
135
140
|
}
|
|
@@ -160,7 +165,9 @@ function createBasicFormField({ name, initialValue, field }) {
|
|
|
160
165
|
internalState.update(x => updateState(x, { isSubmitted }))
|
|
161
166
|
},
|
|
162
167
|
reset() {
|
|
163
|
-
internalState.update(x =>
|
|
168
|
+
internalState.update(x =>
|
|
169
|
+
updateState(x, pick(initialFormFieldState, ['value', 'isSubmitted', 'isVisited']))
|
|
170
|
+
)
|
|
164
171
|
},
|
|
165
172
|
value,
|
|
166
173
|
state: { get: internalState.get, subscribe: internalState.subscribe },
|
|
@@ -186,6 +193,13 @@ function updateState(formFieldState, update) {
|
|
|
186
193
|
return deriveFormFieldState({ ...formFieldState, ...update })
|
|
187
194
|
}
|
|
188
195
|
|
|
196
|
+
function pick(o, properties) {
|
|
197
|
+
return properties.reduce(
|
|
198
|
+
(result, property) => ({ ...result, [property]: o[property] }),
|
|
199
|
+
{}
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
189
203
|
function deriveFormFieldState({
|
|
190
204
|
error = false,
|
|
191
205
|
isSubmitted = false,
|
package/src/hooks.js
CHANGED
|
@@ -7,10 +7,9 @@ let formCounter = 0 // This will stop working when we need a number greater than
|
|
|
7
7
|
function useFormId() { return React.useMemo(() => `form${++formCounter}`, []) }
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
export function useForm({ initialValues = undefined, fields, validate = undefined, onSubmit }) {
|
|
10
|
+
export function useForm({ initialValues = undefined, fields, validate = undefined, onSubmit, formId = useFormId() }) {
|
|
11
11
|
const initialValuesRef = React.useRef(null)
|
|
12
12
|
const formRef = React.useRef(null)
|
|
13
|
-
const formId = useFormId()
|
|
14
13
|
|
|
15
14
|
if (!isEqual(initialValuesRef.current, initialValues)) {
|
|
16
15
|
initialValuesRef.current = initialValues
|
|
@@ -37,7 +36,6 @@ export function useForm({ initialValues = undefined, fields, validate = undefine
|
|
|
37
36
|
|
|
38
37
|
function handleReset() {
|
|
39
38
|
formRef.current.reset()
|
|
40
|
-
formRef.current.validate({ form: initialValues, parents: [] })
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
41
|
|
package/src/snapshot.js
CHANGED
|
@@ -9,6 +9,10 @@ export function get(field) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function subscribe(field, f) {
|
|
12
|
+
return subscribeToFieldState(field, x => f(get(x)))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function subscribeToFieldState(field, f) {
|
|
12
16
|
return {
|
|
13
17
|
'object': subscribeForObject,
|
|
14
18
|
'array': subscribeForArray,
|
|
@@ -65,7 +69,7 @@ function getForBasic(field) {
|
|
|
65
69
|
function subscribeForObject(field, f) {
|
|
66
70
|
return subscribeToChildren({
|
|
67
71
|
children: Object.values(field.fields),
|
|
68
|
-
notify: _ => f(
|
|
72
|
+
notify: _ => f(field),
|
|
69
73
|
subscribeToChild: subscribe,
|
|
70
74
|
})
|
|
71
75
|
}
|
|
@@ -74,11 +78,11 @@ function subscribeForArray(field, f) {
|
|
|
74
78
|
return subscribeToAll({
|
|
75
79
|
state: field.state,
|
|
76
80
|
childrenFromState: x => x.children,
|
|
77
|
-
notify: _ => f(
|
|
81
|
+
notify: _ => f(field),
|
|
78
82
|
subscribeToChild: subscribe,
|
|
79
83
|
})
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
function subscribeForBasic(field, f) {
|
|
83
|
-
return field.state.subscribe(
|
|
87
|
+
return field.state.subscribe(_ => f(field))
|
|
84
88
|
}
|