@rolster/react-forms 19.4.3 → 19.4.6

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.
@@ -133,7 +133,7 @@ function replaceControl(controls, control) {
133
133
  controls[key] = control;
134
134
  }
135
135
  return controls;
136
- }, controls);
136
+ }, { ...controls });
137
137
  }
138
138
 
139
139
  function refactorForValid$2(controls, validators) {
@@ -217,6 +217,11 @@ class RolsterArrayGroup {
217
217
  setResource(resource) {
218
218
  this.refresh('resource', { resource });
219
219
  }
220
+ reset() {
221
+ Object.values(this.controls).forEach((control) => {
222
+ control.reset();
223
+ });
224
+ }
220
225
  refresh(action, options) {
221
226
  this.subscriber &&
222
227
  this.subscriber(action, new RolsterArrayGroup({ ...this, ...options }));
@@ -251,12 +256,14 @@ function formArrayGroup(options, validators) {
251
256
 
252
257
  class RolsterArrayList extends RolsterArrayControl {
253
258
  constructor(options) {
254
- const { controls, valueToControls, validators } = options;
259
+ const { controls, controlsUuid, valueToControls, valueToUuid, validators } = options;
255
260
  const value = controls.map(helpers.controlsToValue);
256
261
  const errors = validators ? helpers.formControlIsValid({ value, validators }) : [];
257
262
  super({ ...options, errors, value });
258
263
  this.valueToControls = valueToControls;
264
+ this.valueToUuid = valueToUuid;
259
265
  this.controls = controls;
266
+ this.controlsUuid = controlsUuid ?? controls.map(() => uuid.v4());
260
267
  this.valid =
261
268
  errors.length === 0 &&
262
269
  controls.reduce((valid, controls) => valid && helpers.verifyAllTrueInControls(controls, 'valid'), true);
@@ -267,49 +274,76 @@ class RolsterArrayList extends RolsterArrayControl {
267
274
  });
268
275
  }
269
276
  setDefaultValue(value) {
277
+ console.log(value);
278
+ console.log(this.generateControlsUuid(value));
270
279
  this.refresh('list', {
271
280
  controls: value.map(this.valueToControls),
281
+ controlsUuid: this.generateControlsUuid(value),
272
282
  defaultValue: value
273
283
  });
274
284
  }
275
285
  setStartValue(value) {
276
286
  this.refresh('list', {
277
- controls: value.map(this.valueToControls)
287
+ controls: value.map(this.valueToControls),
288
+ controlsUuid: this.generateControlsUuid(value)
278
289
  });
279
290
  }
280
291
  setValue(value) {
292
+ console.log(value);
293
+ console.log(this.generateControlsUuid(value));
281
294
  this.refresh('list', {
282
295
  controls: value.map(this.valueToControls),
296
+ controlsUuid: this.generateControlsUuid(value),
283
297
  dirty: true
284
298
  });
285
299
  }
286
300
  reset() {
287
301
  this.refresh('list', {
288
302
  controls: this.defaultValue.map(this.valueToControls),
303
+ controlsUuid: this.generateControlsUuid(this.defaultValue),
289
304
  dirty: false,
290
305
  touched: false
291
306
  });
292
307
  }
293
308
  push(controls) {
309
+ const _uuid = this.valueToUuid?.(helpers.controlsToValue(controls)) ?? uuid.v4();
294
310
  this.refresh('list', {
295
- controls: [...this.controls, controls]
311
+ controls: [...this.controls, controls],
312
+ controlsUuid: [...this.controlsUuid, _uuid]
296
313
  });
297
314
  }
298
315
  remove(controls) {
316
+ const _controls = [];
317
+ const _controlsUuid = [];
318
+ this.controls.forEach((currentControls, index) => {
319
+ if (currentControls !== controls) {
320
+ _controls.push(currentControls);
321
+ _controlsUuid.push(this.controlsUuid[index]);
322
+ }
323
+ });
299
324
  this.refresh('list', {
300
- controls: this.controls.filter((_controls) => _controls !== controls)
325
+ controls: _controls,
326
+ controlsUuid: _controlsUuid
301
327
  });
302
328
  }
329
+ findControlsByUuid(uuid) {
330
+ const index = this.controlsUuid.indexOf(uuid);
331
+ return index >= 0 ? this.controls[index] : undefined;
332
+ }
303
333
  builder(options) {
304
334
  return new RolsterArrayList({
305
335
  ...this,
306
336
  ...options,
307
- valueToControls: this.valueToControls
337
+ valueToControls: this.valueToControls,
338
+ valueToUuid: this.valueToUuid
308
339
  });
309
340
  }
310
341
  refresh(action, options) {
311
342
  super.refresh(action, options);
312
343
  }
344
+ generateControlsUuid(values) {
345
+ return values.map((value) => this.valueToUuid?.(value) ?? uuid.v4());
346
+ }
313
347
  _subscribe(reactControls) {
314
348
  Object.values(reactControls).forEach((control) => {
315
349
  control.subscribe((action, _control) => {
@@ -322,10 +356,12 @@ class RolsterArrayList extends RolsterArrayControl {
322
356
  }
323
357
  }
324
358
  function formArrayList(options) {
359
+ const { valueToControls, valueToUuid } = options;
325
360
  const value = options?.value || [];
326
361
  return new RolsterArrayList({
327
362
  ...options,
328
- controls: value.map((value) => options.valueToControls(value)),
363
+ controls: value.map(valueToControls),
364
+ controlsUuid: value.map((v) => valueToUuid?.(v) ?? uuid.v4()),
329
365
  defaultValue: value,
330
366
  uuid: uuid.v4()
331
367
  });
@@ -360,29 +396,22 @@ function refactorForControls(action, state, groups) {
360
396
  }
361
397
  function useFormArray(options, validators) {
362
398
  const formArray = helpers.createFormArrayOptions(options, validators);
363
- const defaultValue = react.useRef(formArray.groups || []);
364
- const formGroups = react.useRef(new Map());
399
+ const refArrayValue = react.useRef(formArray.groups || []);
400
+ const refArrayGroups = react.useRef(new Map());
365
401
  const [state, setState] = react.useState(() => {
366
402
  return {
367
- ...refactorForValid$1(defaultValue.current, formArray.validators),
368
- controls: defaultValue.current.map(({ controls }) => controls),
403
+ ...refactorForValid$1(refArrayValue.current, formArray.validators),
404
+ controls: refArrayValue.current.map(({ controls }) => controls),
369
405
  dirty: false,
370
406
  dirties: false,
371
407
  disabled: false,
372
- groups: defaultValue.current,
408
+ groups: refArrayValue.current,
373
409
  touched: false,
374
410
  toucheds: false,
375
- value: defaultValue.current.map(({ controls }) => helpers.controlsToValue(controls)),
411
+ value: refArrayValue.current.map(({ controls }) => helpers.controlsToValue(controls)),
376
412
  validators: formArray.validators
377
413
  };
378
414
  });
379
- react.useEffect(() => {
380
- formGroups.current.clear();
381
- state.groups.forEach((group) => {
382
- formGroups.current.set(group.uuid, group);
383
- group.subscribe(subscriber);
384
- });
385
- }, [state.groups]);
386
415
  const subscriber = react.useCallback((action, group) => {
387
416
  setState((state) => {
388
417
  const groups = state.groups.map((currentGroup) => {
@@ -394,6 +423,17 @@ function useFormArray(options, validators) {
394
423
  };
395
424
  });
396
425
  }, []);
426
+ react.useEffect(() => {
427
+ const previousGroups = refArrayGroups.current;
428
+ const currentGroups = new Map();
429
+ state.groups.forEach((group) => {
430
+ currentGroups.set(group.uuid, group);
431
+ if (previousGroups.get(group.uuid) !== group) {
432
+ group.subscribe(subscriber);
433
+ }
434
+ });
435
+ refArrayGroups.current = currentGroups;
436
+ }, [state.groups]);
397
437
  const disable = react.useCallback(() => {
398
438
  setState((state) => ({ ...state, disabled: true }));
399
439
  }, []);
@@ -417,29 +457,36 @@ function useFormArray(options, validators) {
417
457
  ...state,
418
458
  ...refactorForGroups(groups, state.validators)
419
459
  }));
420
- defaultValue.current = groups;
460
+ refArrayValue.current = groups;
421
461
  }, []);
422
462
  const push = react.useCallback((group) => {
463
+ refArrayGroups.current.set(group.uuid, group);
464
+ group.subscribe(subscriber);
423
465
  setState((state) => ({
424
466
  ...state,
425
467
  ...refactorForGroups([...state.groups, group], state.validators)
426
468
  }));
427
469
  }, []);
428
470
  const merge = react.useCallback((groups) => {
471
+ groups.forEach((group) => {
472
+ group.subscribe(subscriber);
473
+ refArrayGroups.current.set(group.uuid, group);
474
+ });
429
475
  setState((state) => ({
430
476
  ...state,
431
477
  ...refactorForGroups([...state.groups, ...groups], state.validators)
432
478
  }));
433
479
  }, []);
434
480
  const remove = react.useCallback(({ uuid }) => {
481
+ refArrayGroups.current.delete(uuid);
435
482
  setState((state) => ({
436
483
  ...state,
437
484
  ...refactorForGroups(state.groups.filter((group) => group.uuid !== uuid), state.validators)
438
485
  }));
439
486
  }, []);
440
487
  const findByUuid = react.useCallback((uuid) => {
441
- return formGroups.current.get(uuid);
442
- }, [state.groups]);
488
+ return refArrayGroups.current.get(uuid);
489
+ }, []);
443
490
  const setValidators = react.useCallback((validators) => {
444
491
  setState((state) => ({
445
492
  ...state,
@@ -454,9 +501,10 @@ function useFormArray(options, validators) {
454
501
  return helpers.someErrors(state.errors, keys);
455
502
  }, [state.errors]);
456
503
  const reset = react.useCallback(() => {
504
+ refArrayValue.current.forEach((group) => group.reset());
457
505
  setState((state) => ({
458
506
  ...state,
459
- ...refactorForGroups(defaultValue.current, state.validators)
507
+ ...refactorForGroups(refArrayValue.current, state.validators)
460
508
  }));
461
509
  }, []);
462
510
  return {
@@ -608,36 +656,48 @@ function refactorForValid(controls, validators) {
608
656
  };
609
657
  }
610
658
  function checkAllSuccess(status) {
611
- return status.reduce((success, status) => success && status, true);
659
+ return status.every((value) => value);
612
660
  }
613
661
  function checkPartialSuccess(status) {
614
- return status.reduce((success, status) => success || status, false);
662
+ return status.some((value) => value);
663
+ }
664
+ function arraysShallowEqual(a, b) {
665
+ if (a === b) {
666
+ return true;
667
+ }
668
+ if (a.length !== b.length) {
669
+ return false;
670
+ }
671
+ for (let i = 0; i < a.length; i++) {
672
+ if (a[i] !== b[i]) {
673
+ return false;
674
+ }
675
+ }
676
+ return true;
615
677
  }
616
678
  function useFormGroup(options, validators) {
617
679
  const formGroup = helpers.createFormGroupOptions(options, validators);
618
- const formInitialized = react.useRef({
619
- dirty: false,
620
- touched: false,
621
- value: false,
622
- visual: false
623
- });
680
+ const refControls = react.useRef(formGroup.controls);
681
+ refControls.current = formGroup.controls;
624
682
  const formGroupStatus = react.useMemo(() => {
625
683
  const dirty = [];
684
+ const disabled = [];
685
+ const focused = [];
626
686
  const touched = [];
627
687
  const value = [];
628
- const visual = [];
629
688
  Object.values(formGroup.controls).forEach((control) => {
630
689
  dirty.push(control.dirty);
690
+ disabled.push(control.disabled);
691
+ focused.push(control.focused);
631
692
  touched.push(control.touched);
632
693
  value.push(control.value);
633
- visual.push(control.disabled);
634
- visual.push(control.focused);
635
694
  });
636
695
  return {
637
696
  dirty,
697
+ disabled,
698
+ focused,
638
699
  touched,
639
- value,
640
- visual
700
+ value
641
701
  };
642
702
  }, [formGroup.controls]);
643
703
  const [state, setState] = react.useState(() => {
@@ -652,75 +712,65 @@ function useFormGroup(options, validators) {
652
712
  value: helpers.controlsToValue(formGroup.controls)
653
713
  };
654
714
  });
715
+ const refPrevFormGroupStatus = react.useRef(null);
655
716
  react.useEffect(() => {
656
- if (formInitialized.current.value) {
657
- setState((state) => ({
658
- ...state,
659
- ...refactorForValid(formGroup.controls, state.validators),
660
- controls: formGroup.controls,
661
- value: helpers.controlsToValue(formGroup.controls)
662
- }));
663
- }
664
- else {
665
- formInitialized.current.value = true;
666
- }
667
- }, formGroupStatus.value);
668
- react.useEffect(() => {
669
- if (formInitialized.current.dirty) {
670
- setState((state) => ({
671
- ...state,
672
- controls: formGroup.controls,
673
- dirty: checkPartialSuccess(formGroupStatus.dirty),
674
- dirties: checkAllSuccess(formGroupStatus.dirty)
675
- }));
676
- }
677
- else {
678
- formInitialized.current.dirty = true;
679
- }
680
- }, formGroupStatus.dirty);
681
- react.useEffect(() => {
682
- if (formInitialized.current.touched) {
683
- setState((state) => ({
684
- ...state,
685
- controls: formGroup.controls,
686
- touched: checkPartialSuccess(formGroupStatus.touched),
687
- toucheds: checkAllSuccess(formGroupStatus.touched)
688
- }));
689
- }
690
- else {
691
- formInitialized.current.touched = true;
692
- }
693
- }, formGroupStatus.touched);
694
- react.useEffect(() => {
695
- if (formInitialized.current.visual) {
696
- setState((state) => ({
697
- ...state,
698
- controls: formGroup.controls
699
- }));
717
+ const formGroupPrev = refPrevFormGroupStatus.current;
718
+ refPrevFormGroupStatus.current = formGroupStatus;
719
+ if (!formGroupPrev) {
720
+ return;
700
721
  }
701
- else {
702
- formInitialized.current.visual = true;
722
+ const valueChanged = !arraysShallowEqual(formGroupPrev.value, formGroupStatus.value);
723
+ const dirtyChanged = !arraysShallowEqual(formGroupPrev.dirty, formGroupStatus.dirty);
724
+ const touchedChanged = !arraysShallowEqual(formGroupPrev.touched, formGroupStatus.touched);
725
+ const disabledChanged = !arraysShallowEqual(formGroupPrev.disabled, formGroupStatus.disabled);
726
+ const focusedChanged = !arraysShallowEqual(formGroupPrev.focused, formGroupStatus.focused);
727
+ if (!valueChanged &&
728
+ !dirtyChanged &&
729
+ !touchedChanged &&
730
+ !disabledChanged &&
731
+ !focusedChanged) {
732
+ return;
703
733
  }
704
- }, formGroupStatus.visual);
734
+ setState((state) => {
735
+ const next = { ...state, controls: formGroup.controls };
736
+ if (valueChanged) {
737
+ const validResult = refactorForValid(formGroup.controls, state.validators);
738
+ next.errors = validResult.errors;
739
+ next.valid = validResult.valid;
740
+ next.value = helpers.controlsToValue(formGroup.controls);
741
+ }
742
+ if (dirtyChanged) {
743
+ next.dirty = checkPartialSuccess(formGroupStatus.dirty);
744
+ next.dirties = checkAllSuccess(formGroupStatus.dirty);
745
+ }
746
+ if (touchedChanged) {
747
+ next.touched = checkPartialSuccess(formGroupStatus.touched);
748
+ next.toucheds = checkAllSuccess(formGroupStatus.touched);
749
+ }
750
+ return next;
751
+ });
752
+ });
705
753
  const setValidators = react.useCallback((validators) => {
706
754
  setState((state) => ({
707
755
  ...state,
708
- ...refactorForValid(state.controls, validators)
756
+ ...refactorForValid(refControls.current, validators),
757
+ validators
709
758
  }));
710
759
  }, []);
711
760
  const setValue = react.useCallback((value) => {
712
761
  Object.entries(value).forEach(([key, valueControl]) => {
713
- const formControl = formGroup.controls[key];
762
+ const formControl = refControls.current[key];
714
763
  formControl?.setValue(valueControl);
715
764
  });
716
765
  }, []);
717
766
  const reset = react.useCallback(() => {
718
- Object.values(formGroup.controls).forEach((control) => {
767
+ Object.values(refControls.current).forEach((control) => {
719
768
  control.reset();
720
769
  });
721
770
  }, []);
722
771
  return {
723
772
  ...state,
773
+ controls: formGroup.controls,
724
774
  error: state.errors[0],
725
775
  invalid: !state.valid,
726
776
  pristine: !state.dirty,
@@ -736,16 +786,31 @@ function useFormGroup(options, validators) {
736
786
 
737
787
  function useInputRefControl(options) {
738
788
  const formRef = useInputControl(options);
789
+ const setValueRef = react.useRef(options.setValue);
790
+ setValueRef.current = options.setValue;
739
791
  react.useEffect(() => {
740
- formRef.elementRef?.current?.addEventListener('focus', () => {
792
+ const element = formRef.elementRef?.current;
793
+ if (!element) {
794
+ return;
795
+ }
796
+ const handleFocus = () => {
741
797
  formRef.focus();
742
- });
743
- formRef.elementRef?.current?.addEventListener('blur', () => {
798
+ };
799
+ const handleBlur = () => {
744
800
  formRef.blur();
745
- });
746
- formRef.elementRef?.current?.addEventListener('change', ({ target }) => {
747
- options.setValue(formRef, target.value);
748
- });
801
+ };
802
+ const handleChange = (event) => {
803
+ const target = event.target;
804
+ setValueRef.current(formRef, target.value);
805
+ };
806
+ element.addEventListener('focus', handleFocus);
807
+ element.addEventListener('blur', handleBlur);
808
+ element.addEventListener('change', handleChange);
809
+ return () => {
810
+ element.removeEventListener('focus', handleFocus);
811
+ element.removeEventListener('blur', handleBlur);
812
+ element.removeEventListener('change', handleChange);
813
+ };
749
814
  }, []);
750
815
  return formRef;
751
816
  }