@rolster/react-forms 18.11.3 → 18.12.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/dist/es/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { createFormControlOptions, createFormGroupOptions, createFormArrayOptions } from '@rolster/forms/arguments';
2
- import { controlIsValid, hasError, someErrors, groupIsValid, controlsAllChecked, controlsPartialChecked, controlsToValue, groupAllChecked, arrayIsValid, reduceControlsToArray } from '@rolster/forms/helpers';
2
+ import { controlIsValid, hasError, someErrors, groupIsValid, controlsToValue, controlsAllChecked, controlsPartialChecked, arrayIsValid, groupAllChecked, reduceControlsToArray } from '@rolster/forms/helpers';
3
3
  import { v4 } from 'uuid';
4
4
  import { useRef, useState, useEffect, useCallback } from 'react';
5
5
 
6
- class RolsterReactArrayControl {
6
+ class RolsterArrayControl {
7
7
  constructor(options) {
8
- this.initialValue = options.initialValue;
9
8
  this.uuid = options.uuid;
9
+ this.value = options.value;
10
+ this.initialValue = options.initialValue;
10
11
  this.focused = !!options.focused;
11
12
  this.unfocused = !this.focused;
12
13
  this.touched = !!options.touched;
@@ -15,36 +16,42 @@ class RolsterReactArrayControl {
15
16
  this.pristine = !this.dirty;
16
17
  this.disabled = !!options.disabled;
17
18
  this.enabled = !this.disabled;
18
- const { value, validators } = options;
19
- this.value = value;
20
- this.validators = validators;
21
- this.errors = validators ? controlIsValid({ value, validators }) : [];
19
+ this.valid = this.isValid(options.errors);
20
+ this.invalid = !this.valid;
21
+ this.wrong = this.touched && this.invalid;
22
+ this.errors = options.errors;
22
23
  this.error = this.errors[0];
24
+ this.validators = options.validators;
23
25
  }
24
26
  focus() {
25
- this.unfocused && this.refresh({ focused: true });
27
+ this.unfocused && this.refresh('focused', { focused: true });
26
28
  }
27
29
  blur() {
28
- this.focused && this.refresh({ focused: false, touched: true });
30
+ this.focused && this.refresh('focused', { focused: false, touched: true });
29
31
  }
30
32
  disable() {
31
- this.enabled && this.refresh({ disabled: true });
33
+ this.enabled && this.refresh('disabled', { disabled: true });
32
34
  }
33
35
  enable() {
34
- this.disabled && this.refresh({ disabled: false });
36
+ this.disabled && this.refresh('disabled', { disabled: false });
35
37
  }
36
38
  touch() {
37
- this.untouched && this.refresh({ touched: true });
39
+ this.untouched && this.refresh('touched', { touched: true });
38
40
  }
39
41
  setInitialValue(value) {
40
- this.initialValue = value;
41
- this.setValue(value);
42
+ const { validators } = this;
43
+ const errors = validators ? controlIsValid({ value, validators }) : [];
44
+ this.refresh('value', { dirty: false, errors, initialValue: value, value });
42
45
  }
43
46
  setValue(value) {
44
- this.refresh({ value });
47
+ const { validators } = this;
48
+ const errors = validators ? controlIsValid({ value, validators }) : [];
49
+ this.refresh('value', { dirty: true, errors, value });
45
50
  }
46
51
  setValidators(validators) {
47
- this.refresh({ validators });
52
+ const { value } = this;
53
+ const errors = validators ? controlIsValid({ value, validators }) : [];
54
+ this.refresh('validators', { errors, validators });
48
55
  }
49
56
  subscribe(subscriber) {
50
57
  this.subscriber = subscriber;
@@ -56,37 +63,37 @@ class RolsterReactArrayControl {
56
63
  return someErrors(this.errors, keys);
57
64
  }
58
65
  reset() {
59
- this.refresh({
66
+ const { initialValue: value, validators } = this;
67
+ const errors = validators ? controlIsValid({ value, validators }) : [];
68
+ this.refresh('reset', {
60
69
  dirty: false,
70
+ errors,
61
71
  touched: false,
62
- value: this.initialValue
72
+ value
63
73
  });
64
74
  }
65
- refresh(changes) {
66
- this.subscriber &&
67
- this.subscriber({
68
- ...this,
69
- ...changes,
70
- initialValue: this.initialValue
71
- });
75
+ isValid(errors) {
76
+ return errors.length === 0;
77
+ }
78
+ builder(options) {
79
+ return new RolsterArrayControl({ ...this, ...options });
80
+ }
81
+ refresh(action, options) {
82
+ this.subscriber && this.subscriber(action, this.builder(options));
72
83
  }
73
84
  }
74
- class RolsterArrayControl extends RolsterReactArrayControl {
85
+ class ReactRolsterArrayControl extends RolsterArrayControl {
75
86
  constructor(options) {
76
- super(options);
77
- this.valid = this.errors.length === 0;
78
- this.invalid = !this.valid;
79
- this.wrong = this.touched && this.invalid;
80
- }
81
- clone(options) {
82
- return new RolsterArrayControl(options);
87
+ const { value, validators } = options;
88
+ const errors = validators ? controlIsValid({ value, validators }) : [];
89
+ super({ ...options, errors });
83
90
  }
84
91
  }
85
92
  function rolsterArrayControl(options, validators) {
86
- const controlOptions = createFormControlOptions(options, validators);
87
- return new RolsterArrayControl({
88
- ...controlOptions,
89
- initialValue: controlOptions.value,
93
+ const _options = createFormControlOptions(options, validators);
94
+ return new ReactRolsterArrayControl({
95
+ ..._options,
96
+ initialValue: _options.value,
90
97
  uuid: v4()
91
98
  });
92
99
  }
@@ -100,49 +107,112 @@ function inputArrayControl(options, validators) {
100
107
  return rolsterArrayControl(options, validators);
101
108
  }
102
109
 
103
- class RolsterArrayGroup {
110
+ function replaceControl(controls, control) {
111
+ return Object.entries(controls).reduce((controls, [key, _control]) => {
112
+ if (_control.uuid === control.uuid) {
113
+ controls[key] = control;
114
+ }
115
+ return controls;
116
+ }, controls);
117
+ }
118
+
119
+ function refactorForValid$2(controls, validators) {
120
+ const errors = validators ? groupIsValid({ controls, validators }) : [];
121
+ return {
122
+ errors,
123
+ valid: errors.length === 0 && controlsAllChecked(controls, 'valid')
124
+ };
125
+ }
126
+ function refactorForControls$1(group, controls, action) {
127
+ switch (action) {
128
+ case 'focused':
129
+ case 'touched':
130
+ return {
131
+ touched: controlsPartialChecked(controls, 'touched'),
132
+ toucheds: controlsAllChecked(controls, 'touched')
133
+ };
134
+ case 'validators':
135
+ return refactorForValid$2(controls, group.validators);
136
+ case 'reset':
137
+ return {
138
+ ...refactorForValid$2(controls, group.validators),
139
+ dirty: controlsPartialChecked(controls, 'dirty'),
140
+ dirties: controlsAllChecked(controls, 'dirty'),
141
+ touched: controlsPartialChecked(controls, 'touched'),
142
+ toucheds: controlsAllChecked(controls, 'touched'),
143
+ value: controlsToValue(controls)
144
+ };
145
+ case 'list':
146
+ case 'value':
147
+ return {
148
+ ...refactorForValid$2(controls, group.validators),
149
+ dirty: controlsPartialChecked(controls, 'dirty'),
150
+ dirties: controlsAllChecked(controls, 'dirty'),
151
+ value: controlsToValue(controls)
152
+ };
153
+ default:
154
+ return {};
155
+ }
156
+ }
157
+ class BaseArrayGroup {
104
158
  constructor(options) {
105
- const { controls, resource, uuid, validators } = options;
106
- this.uuid = uuid;
107
- this.controls = controls;
108
- this.validators = validators;
109
- this.resource = resource;
110
- this.errors = validators ? groupIsValid({ controls, validators }) : [];
111
- this.error = this.errors[0];
112
- this.valid =
113
- this.errors.length === 0 && controlsAllChecked(controls, 'valid');
114
- this.invalid = !this.valid;
115
- this.dirty = controlsPartialChecked(controls, 'dirty');
116
- this.dirties = controlsAllChecked(controls, 'dirty');
117
- this.touched = controlsPartialChecked(controls, 'touched');
118
- this.toucheds = controlsAllChecked(controls, 'touched');
159
+ this.uuid = options.uuid;
160
+ this.controls = options.controls;
161
+ this.value = options.value;
162
+ this.resource = options.resource;
163
+ this.dirty = !!options.dirty;
164
+ this.dirties = !!options.dirties;
119
165
  this.pristine = !this.dirty;
120
166
  this.pristines = !this.dirties;
167
+ this.touched = !!options.touched;
168
+ this.toucheds = !!options.toucheds;
121
169
  this.untouched = !this.touched;
122
170
  this.untoucheds = !this.toucheds;
171
+ this.valid = !!options.valid;
172
+ this.invalid = !this.valid;
123
173
  this.wrong = this.touched && this.invalid;
124
- this.value = controlsToValue(controls);
125
- const subscriber = (options) => {
126
- this.refresh({
127
- controls: Object.entries(this.controls).reduce((controls, [key, control]) => {
128
- controls[key] =
129
- control.uuid === options.uuid ? control.clone(options) : control;
130
- return controls;
131
- }, {})
174
+ this.errors = options.errors;
175
+ this.error = this.errors[0];
176
+ this.validators = options.validators;
177
+ const subscriber = (action, control) => {
178
+ const controls = replaceControl(this.controls, control);
179
+ this.refresh(action, {
180
+ ...refactorForControls$1(this, controls, action),
181
+ controls
132
182
  });
133
183
  };
134
- Object.values(controls).forEach((control) => {
184
+ Object.values(this.controls).forEach((control) => {
135
185
  control.subscribe(subscriber);
136
186
  });
137
187
  }
138
- setValidators(validators) {
139
- this.refresh({ validators });
140
- }
141
188
  subscribe(subscriber) {
142
189
  this.subscriber = subscriber;
143
190
  }
144
- refresh(changes) {
145
- this.subscriber && this.subscriber({ ...this, ...changes });
191
+ setValidators(validators) {
192
+ this.refresh('validators', {
193
+ ...refactorForValid$2(this.controls, validators),
194
+ validators
195
+ });
196
+ }
197
+ refresh(action, options) {
198
+ this.subscriber &&
199
+ this.subscriber(action, new BaseArrayGroup({ ...this, ...options }));
200
+ }
201
+ }
202
+ class RolsterArrayGroup extends BaseArrayGroup {
203
+ constructor(options) {
204
+ const { controls, validators } = options;
205
+ const errors = validators ? groupIsValid({ controls, validators }) : [];
206
+ super({
207
+ ...options,
208
+ errors,
209
+ value: controlsToValue(controls),
210
+ dirties: controlsAllChecked(controls, 'dirty'),
211
+ dirty: controlsPartialChecked(controls, 'dirty'),
212
+ touched: controlsPartialChecked(controls, 'touched'),
213
+ toucheds: controlsAllChecked(controls, 'touched'),
214
+ valid: errors.length === 0 && controlsAllChecked(controls, 'valid')
215
+ });
146
216
  }
147
217
  }
148
218
  function formArrayGroup(options, validators) {
@@ -152,94 +222,102 @@ function formArrayGroup(options, validators) {
152
222
  });
153
223
  }
154
224
 
155
- class RolsterArrayList extends RolsterReactArrayControl {
225
+ class RolsterArrayList extends RolsterArrayControl {
156
226
  constructor(options) {
157
- const value = options.controls.map((controls) => controlsToValue(controls));
158
- super({ ...options, value });
159
- this.valueToControls = options.valueToControls;
160
- this.controls = options.controls;
227
+ const { controls, valueToControls, validators } = options;
228
+ const value = controls.map((controls) => controlsToValue(controls));
229
+ const errors = validators ? controlIsValid({ value, validators }) : [];
230
+ super({ ...options, errors, value });
231
+ this.valueToControls = valueToControls;
232
+ this.controls = controls;
161
233
  this.valid =
162
- this.errors.length === 0 &&
163
- this.controls.reduce((valid, controls) => valid && controlsAllChecked(controls, 'valid'), true);
234
+ errors.length === 0 &&
235
+ controls.reduce((valid, controls) => valid && controlsAllChecked(controls, 'valid'), true);
164
236
  this.invalid = !this.valid;
165
237
  this.wrong = this.touched && this.invalid;
166
- options.controls.forEach((controls) => {
167
- this.subscribeControls(controls);
238
+ controls.forEach((reactControls) => {
239
+ this._subscribe(reactControls);
168
240
  });
169
241
  }
170
242
  setValue(value) {
171
- this.refresh({
243
+ this.refresh('list', {
172
244
  controls: value.map((value) => this.valueToControls(value))
173
245
  });
174
246
  }
175
- clone(options) {
176
- return new RolsterArrayList(options);
177
- }
178
247
  push(controls) {
179
- this.refresh({
248
+ this.refresh('list', {
180
249
  controls: [...this.controls, controls]
181
250
  });
182
251
  }
183
252
  remove(controls) {
184
- this.refresh({
185
- controls: this.controls.filter((currentControls) => currentControls !== controls)
253
+ this.refresh('list', {
254
+ controls: this.controls.filter((_controls) => _controls !== controls)
255
+ });
256
+ }
257
+ builder(options) {
258
+ return new RolsterArrayList({
259
+ ...this,
260
+ ...options,
261
+ valueToControls: this.valueToControls
186
262
  });
187
263
  }
188
- refresh(changes) {
189
- super.refresh(changes);
264
+ refresh(action, options) {
265
+ super.refresh(action, options);
190
266
  }
191
- subscribeControls(newControls) {
192
- Object.values(newControls).forEach((control) => {
193
- control.subscribe((options) => {
194
- const controls = this.controls.map((_controls) => _controls !== newControls
267
+ _subscribe(reactControls) {
268
+ Object.values(reactControls).forEach((control) => {
269
+ control.subscribe((action, _control) => {
270
+ const _reactControls = this.controls.map((_controls) => reactControls !== _controls
195
271
  ? _controls
196
- : Object.entries(newControls).reduce((controls, [key, control]) => {
197
- controls[key] =
198
- control.uuid === options.uuid
199
- ? control.clone(options)
200
- : control;
201
- return controls;
202
- }, {}));
203
- this.refresh({ controls });
272
+ : replaceControl(reactControls, _control));
273
+ this.refresh(action, { controls: _reactControls });
204
274
  });
205
275
  });
206
276
  }
207
277
  }
208
- function formArrayList(valueToControls, options) {
278
+ function formArrayList(options) {
209
279
  const value = options?.value || [];
210
280
  return new RolsterArrayList({
211
281
  ...options,
212
- valueToControls,
213
- controls: value.map((value) => valueToControls(value)),
282
+ controls: value.map((value) => options.valueToControls(value)),
214
283
  initialValue: value,
215
284
  uuid: v4()
216
285
  });
217
286
  }
218
287
 
219
- function errorsInArray(groups, validators) {
220
- return validators ? arrayIsValid({ groups, validators }) : [];
221
- }
222
- function validStateInArray(groups, validators) {
223
- const errors = errorsInArray(groups, validators);
288
+ function refactorForValid$1(groups, validators) {
289
+ const errors = validators ? arrayIsValid({ groups, validators }) : [];
224
290
  return {
225
291
  errors,
226
292
  valid: errors.length === 0 && groupAllChecked(groups, 'valid')
227
293
  };
228
294
  }
229
- function replaceStateInArray(groups, validators) {
295
+ function refactorForGroups(groups, validators) {
230
296
  return {
231
- ...validStateInArray(groups, validators),
297
+ ...refactorForValid$1(groups, validators),
232
298
  groups,
233
299
  controls: groups.map(({ controls }) => controls),
234
300
  value: groups.map(({ controls }) => controlsToValue(controls))
235
301
  };
236
302
  }
303
+ function refactorForControls(state, groups, action) {
304
+ switch (action) {
305
+ case 'validators':
306
+ return refactorForValid$1(groups, state.validators);
307
+ case 'reset':
308
+ case 'list':
309
+ case 'value':
310
+ return refactorForGroups(groups, state.validators);
311
+ default:
312
+ return { groups };
313
+ }
314
+ }
237
315
  function useFormArray(options, validators) {
238
316
  const _options = createFormArrayOptions(options, validators);
239
317
  const groups = _options.groups || [];
240
318
  const initialValue = useRef(groups);
241
319
  const [state, setState] = useState({
242
- ...validStateInArray(groups, _options.validators),
320
+ ...refactorForValid$1(groups, _options.validators),
243
321
  controls: groups.map(({ controls }) => controls),
244
322
  dirty: false,
245
323
  dirties: false,
@@ -251,13 +329,14 @@ function useFormArray(options, validators) {
251
329
  validators: _options.validators
252
330
  });
253
331
  useEffect(() => {
254
- const subscriber = (options) => {
255
- setState((state) => ({
256
- ...state,
257
- ...replaceStateInArray(state.groups.map((group) => group.uuid === options.uuid
258
- ? new RolsterArrayGroup(options)
259
- : group), state.validators)
260
- }));
332
+ const subscriber = (action, group) => {
333
+ setState((state) => {
334
+ const groups = state.groups.map((_group) => _group.uuid === group.uuid ? group : _group);
335
+ return {
336
+ ...state,
337
+ ...refactorForControls(state, groups, action)
338
+ };
339
+ });
261
340
  };
262
341
  state.groups.forEach((group) => {
263
342
  group.subscribe(subscriber);
@@ -272,49 +351,49 @@ function useFormArray(options, validators) {
272
351
  const setValue = useCallback((groups) => {
273
352
  setState((state) => ({
274
353
  ...state,
275
- ...replaceStateInArray(groups, state.validators)
354
+ ...refactorForGroups(groups, state.validators)
276
355
  }));
277
356
  }, []);
278
357
  const setInitialValue = useCallback((groups) => {
279
358
  setState((state) => ({
280
359
  ...state,
281
- ...replaceStateInArray(groups, state.validators)
360
+ ...refactorForGroups(groups, state.validators)
282
361
  }));
283
362
  initialValue.current = groups;
284
363
  }, []);
285
364
  const push = useCallback((group) => {
286
365
  setState((state) => ({
287
366
  ...state,
288
- ...replaceStateInArray([...state.groups, group], state.validators)
367
+ ...refactorForGroups([...state.groups, group], state.validators)
289
368
  }));
290
369
  }, []);
291
370
  const merge = useCallback((groups) => {
292
371
  setState((state) => ({
293
372
  ...state,
294
- ...replaceStateInArray([...state.groups, ...groups], state.validators)
373
+ ...refactorForGroups([...state.groups, ...groups], state.validators)
295
374
  }));
296
375
  }, []);
297
376
  const remove = useCallback(({ uuid }) => {
298
377
  setState((state) => ({
299
378
  ...state,
300
- ...replaceStateInArray(state.groups.filter((group) => group.uuid !== uuid), state.validators)
301
- }));
302
- }, []);
303
- const reset = useCallback(() => {
304
- setState((state) => ({
305
- ...state,
306
- ...replaceStateInArray(initialValue.current, state.validators)
379
+ ...refactorForGroups(state.groups.filter((group) => group.uuid !== uuid), state.validators)
307
380
  }));
308
381
  }, []);
309
382
  const setValidators = useCallback((validators) => {
310
383
  setState((state) => ({
311
384
  ...state,
312
- ...validStateInArray(state.groups, validators),
385
+ ...refactorForValid$1(state.groups, validators),
313
386
  validators
314
387
  }));
315
388
  }, []);
316
389
  const hasError$1 = useCallback((key) => hasError(state.errors, key), [state.errors]);
317
390
  const someErrors$1 = useCallback((keys) => someErrors(state.errors, keys), [state.errors]);
391
+ const reset = useCallback(() => {
392
+ setState((state) => ({
393
+ ...state,
394
+ ...refactorForGroups(initialValue.current, state.validators)
395
+ }));
396
+ }, []);
318
397
  return {
319
398
  ...state,
320
399
  disable,
@@ -440,11 +519,8 @@ function useInputControl(options, validators) {
440
519
  return useControl(options, validators);
441
520
  }
442
521
 
443
- function errorsInGroup(controls, validators) {
444
- return validators ? groupIsValid({ controls, validators }) : [];
445
- }
446
- function validStateInGroup(controls, validators) {
447
- const errors = errorsInGroup(controls, validators);
522
+ function refactorForValid(controls, validators) {
523
+ const errors = validators ? groupIsValid({ controls, validators }) : [];
448
524
  return {
449
525
  errors,
450
526
  valid: errors.length === 0 && controlsAllChecked(controls, 'valid')
@@ -453,8 +529,14 @@ function validStateInGroup(controls, validators) {
453
529
  function useFormGroup(options, validators) {
454
530
  const _options = createFormGroupOptions(options, validators);
455
531
  const { controls } = _options;
532
+ const firstEffects = useRef({
533
+ dirty: true,
534
+ disabledFocused: true,
535
+ touched: true,
536
+ value: true
537
+ });
456
538
  const [state, setState] = useState({
457
- ...validStateInGroup(controls, _options.validators),
539
+ ...refactorForValid(controls, _options.validators),
458
540
  controls,
459
541
  dirties: controlsAllChecked(controls, 'dirty'),
460
542
  dirty: controlsPartialChecked(controls, 'dirty'),
@@ -464,42 +546,62 @@ function useFormGroup(options, validators) {
464
546
  validators: _options.validators
465
547
  });
466
548
  useEffect(() => {
467
- setState((state) => ({
468
- ...state,
469
- ...validStateInGroup(controls, state.validators),
470
- controls,
471
- value: controlsToValue(controls)
472
- }));
549
+ if (!firstEffects.current.value) {
550
+ setState((state) => ({
551
+ ...state,
552
+ ...refactorForValid(controls, state.validators),
553
+ controls,
554
+ value: controlsToValue(controls)
555
+ }));
556
+ }
557
+ else {
558
+ firstEffects.current.value = false;
559
+ }
473
560
  }, reduceControlsToArray(controls, 'value'));
474
561
  useEffect(() => {
475
- setState((state) => ({
476
- ...state,
477
- controls
478
- }));
562
+ if (!firstEffects.current.disabledFocused) {
563
+ setState((state) => ({
564
+ ...state,
565
+ controls
566
+ }));
567
+ }
568
+ else {
569
+ firstEffects.current.disabledFocused = false;
570
+ }
479
571
  }, [
480
572
  ...reduceControlsToArray(controls, 'disabled'),
481
573
  ...reduceControlsToArray(controls, 'focused')
482
574
  ]);
483
575
  useEffect(() => {
484
- setState((state) => ({
485
- ...state,
486
- controls,
487
- dirty: controlsPartialChecked(controls, 'dirty'),
488
- dirties: controlsAllChecked(controls, 'dirty')
489
- }));
576
+ if (!firstEffects.current.dirty) {
577
+ setState((state) => ({
578
+ ...state,
579
+ controls,
580
+ dirty: controlsPartialChecked(controls, 'dirty'),
581
+ dirties: controlsAllChecked(controls, 'dirty')
582
+ }));
583
+ }
584
+ else {
585
+ firstEffects.current.dirty = false;
586
+ }
490
587
  }, reduceControlsToArray(controls, 'dirty'));
491
588
  useEffect(() => {
492
- setState((state) => ({
493
- ...state,
494
- controls,
495
- touched: controlsPartialChecked(controls, 'touched'),
496
- toucheds: controlsAllChecked(controls, 'touched')
497
- }));
589
+ if (!firstEffects.current.touched) {
590
+ setState((state) => ({
591
+ ...state,
592
+ controls,
593
+ touched: controlsPartialChecked(controls, 'touched'),
594
+ toucheds: controlsAllChecked(controls, 'touched')
595
+ }));
596
+ }
597
+ else {
598
+ firstEffects.current.touched = false;
599
+ }
498
600
  }, reduceControlsToArray(controls, 'touched'));
499
601
  const setValidators = useCallback((validators) => {
500
602
  setState((state) => ({
501
603
  ...state,
502
- ...validStateInGroup(state.controls, validators)
604
+ ...refactorForValid(state.controls, validators)
503
605
  }));
504
606
  }, []);
505
607
  const reset = useCallback(() => {