@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 CHANGED
@@ -1,3 +1,5 @@
1
+ import * as snapshot from './src/snapshot'
2
+ export { snapshot }
1
3
  export {
2
4
  useForm,
3
5
  useFormField,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaliber/forms",
3
- "version": "1.1.1",
3
+ "version": "2.1.0",
4
4
  "main": "index.js",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
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 => updateState(x, initialState))
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 => initialState)
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: field.fields }, fullName),
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 => initialFormFieldState)
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(getForObject(field)),
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(getForArray(field)),
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(({ value, error, invalid }) => f({ value, error, invalid }))
87
+ return field.state.subscribe(_ => f(field))
84
88
  }