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