react-f0rm 0.8.0 → 0.10.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/index.umd.js CHANGED
@@ -222,6 +222,7 @@
222
222
  mode: options?.mode ?? "onSubmit",
223
223
  reValidateMode: options?.reValidateMode ?? "onChange",
224
224
  disabled: options?.disabled ?? false,
225
+ validateDeps: options?.validateDeps ? new Set(options.validateDeps.map((dep) => create$1(dep).key)) : void 0,
225
226
  initialValues: options?.initialValues ?? {},
226
227
  values: /* @__PURE__ */ new Map(),
227
228
  deleted: /* @__PURE__ */ new Set(),
@@ -263,18 +264,21 @@
263
264
  const { emitter, values, deleted } = form;
264
265
  values.set(path.key, value);
265
266
  reviveBranch(deleted, path);
267
+ pruneDirtyBaselines(form, path);
268
+ if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
266
269
  bumpDirtyVersion(form);
267
270
  if (options?.shouldTouch) setTouchedByPath(form, path);
268
271
  if (options?.shouldValidate) form.validators.get(path.key)?.();
269
272
  emit(emitter, "change", path);
270
273
  }
271
- function changeValue(form, name, value) {
272
- changeValueByPath(form, create$1(name), value);
274
+ function changeValue(form, name, value, options) {
275
+ changeValueByPath(form, create$1(name), value, options);
273
276
  }
274
- function changeValueByPath(form, path, value) {
277
+ function changeValueByPath(form, path, value, options) {
278
+ if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
275
279
  const change = form.changeHandlers.get(path.key);
276
280
  if (change) change(value);
277
- else setValueByPath(form, path, value);
281
+ else setValueByPath(form, path, value, options);
278
282
  }
279
283
  function getError(form, name) {
280
284
  return getErrorByPath(form, create$1(name));
@@ -302,13 +306,15 @@
302
306
  }
303
307
  function getFieldState(form, name) {
304
308
  const path = create$1(name);
305
- const { initialValues, values, touched, validating } = form;
309
+ const { values, touched, validating } = form;
306
310
  const live = values.get(path.key);
307
311
  return {
308
312
  value: getValueByPath(form, path),
309
313
  error: getErrorByPath(form, path),
310
314
  errors: getFieldErrorsByPath(form, path),
311
- isDirty: values.has(path.key) && get(initialValues, path.value) !== live,
315
+ // Same rule as getDirtyFields, committed baselines included: the field
316
+ // is dirty while its live value differs from its effective baseline.
317
+ isDirty: values.has(path.key) && getDirtyBaseline(form, path.key, path.value) !== live,
312
318
  isTouched: touched.has(path.key),
313
319
  isValidating: validating.has(path.key)
314
320
  };
@@ -391,11 +397,39 @@
391
397
  });
392
398
  return dirty;
393
399
  }
394
- function forEachDirtyField({ initialValues, values }, fn) {
395
- for (const [key, value] of values) {
400
+ function forEachDirtyField(form, fn) {
401
+ for (const [key, value] of form.values) {
396
402
  const path = JSON.parse(key);
397
- if (get(initialValues, path) !== value) fn(path.join("."));
403
+ if (getDirtyBaseline(form, key, path) !== value) fn(path.join("."));
404
+ }
405
+ }
406
+ const dirtyBaselines = /* @__PURE__ */ new WeakMap();
407
+ function getDirtyBaseline(form, key, segments) {
408
+ const baselines = dirtyBaselines.get(form);
409
+ if (baselines?.has(key)) return baselines.get(key);
410
+ return get(form.initialValues, segments);
411
+ }
412
+ function setDirtyBaseline(form, { key }, value) {
413
+ let baselines = dirtyBaselines.get(form);
414
+ if (!baselines) {
415
+ baselines = /* @__PURE__ */ new Map();
416
+ dirtyBaselines.set(form, baselines);
398
417
  }
418
+ baselines.set(key, value);
419
+ }
420
+ function pruneDirtyBaselines(form, { key }) {
421
+ const baselines = dirtyBaselines.get(form);
422
+ if (!baselines?.size) return;
423
+ const stem = `${key.slice(0, -1)},`;
424
+ for (const baselineKey of baselines.keys()) {
425
+ if (baselineKey.startsWith(stem)) baselines.delete(baselineKey);
426
+ }
427
+ }
428
+ function clearDirtyBaselines(form, key) {
429
+ const baselines = dirtyBaselines.get(form);
430
+ if (!baselines) return;
431
+ if (key === void 0) baselines.clear();
432
+ else baselines.delete(key);
399
433
  }
400
434
  const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
401
435
  function bumpDirtyVersion(form) {
@@ -444,6 +478,7 @@
444
478
  touched.delete(key);
445
479
  errors.delete(key);
446
480
  validating.delete(key);
481
+ clearDirtyBaselines(form, key);
447
482
  if (!hasLiveBranch(values, segments)) deleted.add(key);
448
483
  bumpDirtyVersion(form);
449
484
  emit(emitter, "change");
@@ -477,6 +512,7 @@
477
512
  form.parsedValues = void 0;
478
513
  form.values.clear();
479
514
  form.deleted.clear();
515
+ clearDirtyBaselines(form);
480
516
  bumpDirtyVersion(form);
481
517
  emit(form.emitter, "change");
482
518
  }
@@ -491,6 +527,7 @@
491
527
  const { emitter, touched, values, deleted, validating } = form;
492
528
  values.clear();
493
529
  deleted.clear();
530
+ clearDirtyBaselines(form);
494
531
  if (!options?.keepTouched) touched.clear();
495
532
  validating.clear();
496
533
  if (!options?.keepIsSubmitting) form.isSubmitting = false;
@@ -512,6 +549,7 @@
512
549
  const path = create$1(name);
513
550
  const { emitter, values, touched, errors, deleted } = form;
514
551
  values.delete(path.key);
552
+ clearDirtyBaselines(form, path.key);
515
553
  if (form.parsedValues !== void 0) {
516
554
  form.parsedValues = unset(form.parsedValues, path.value);
517
555
  const initial = get(form.initialValues, path.value);
@@ -557,34 +595,75 @@
557
595
  function isFieldError(value) {
558
596
  return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.message === "string";
559
597
  }
560
- function setFormErrors(form, result, segments = []) {
598
+ function setFormErrors(form, result, segments = [], footprint) {
561
599
  Object.entries(result).forEach(([key, value]) => {
562
600
  const path = [...segments, ...normalizePath(key)];
563
601
  if (typeof value === "string") {
564
- if (value) setError(form, path, value);
602
+ if (value) {
603
+ setError(form, path, value);
604
+ recordFootprint(form, path, footprint);
605
+ }
565
606
  } else if (Array.isArray(value)) {
566
607
  setError(form, path, value);
608
+ recordFootprint(form, path, footprint);
567
609
  } else if (isFieldError(value)) {
568
610
  setError(form, path, value);
611
+ recordFootprint(form, path, footprint);
569
612
  } else if (value && typeof value === "object") {
570
- setFormErrors(form, value, path);
613
+ setFormErrors(form, value, path, footprint);
571
614
  }
572
615
  });
573
616
  }
617
+ function recordFootprint(form, segments, footprint) {
618
+ if (!footprint) return;
619
+ const path = create$1(segments);
620
+ const stored = form.errors.get(path.key);
621
+ if (stored) footprint.set(path.key, stored);
622
+ }
574
623
  function setParsedValues(form, values) {
575
624
  if (values === void 0 || values === form.parsedValues) return;
576
625
  form.parsedValues = values;
577
626
  emit(form.emitter, "change");
578
627
  }
579
628
  function applyValidateResult(form, result) {
629
+ const footprint = form.validateDeps ? getFormErrorFootprint(form) : void 0;
630
+ if (footprint) {
631
+ clearFormValidateErrors(form, footprint);
632
+ footprint.clear();
633
+ }
580
634
  if (!result) return;
581
635
  if (typeof result === "object" && VALIDATION_OUTCOME in result) {
582
636
  const outcome = result;
583
- if (outcome.errors) setFormErrors(form, outcome.errors);
637
+ if (outcome.errors) setFormErrors(form, outcome.errors, [], footprint);
584
638
  setParsedValues(form, outcome.values);
585
639
  return;
586
640
  }
587
- setFormErrors(form, result);
641
+ setFormErrors(form, result, [], footprint);
642
+ }
643
+ const formErrorFootprints = /* @__PURE__ */ new WeakMap();
644
+ function getFormErrorFootprint(form) {
645
+ let footprint = formErrorFootprints.get(form);
646
+ if (!footprint) {
647
+ footprint = /* @__PURE__ */ new Map();
648
+ formErrorFootprints.set(form, footprint);
649
+ }
650
+ return footprint;
651
+ }
652
+ function hasFormValidateErrors(form) {
653
+ const footprint = formErrorFootprints.get(form);
654
+ if (!footprint) return false;
655
+ for (const [key, written] of footprint) {
656
+ if (form.errors.get(key) === written) return true;
657
+ }
658
+ return false;
659
+ }
660
+ function clearFormValidateErrors(form, footprint) {
661
+ for (const [key, written] of footprint) {
662
+ const stored = form.errors.get(key);
663
+ if (stored !== written) continue;
664
+ form.errors.delete(key);
665
+ emit(form.emitter, "errors", create$1(JSON.parse(key)));
666
+ }
588
667
  }
589
668
  const FORM_VALIDATING_KEY = "__form_validate__";
590
669
  function fieldsSettled(form) {
@@ -678,6 +757,13 @@
678
757
  else waiter.reject(outcome);
679
758
  }
680
759
  }
760
+ function revalidateFormOnChange(form, path, mode) {
761
+ if (!form.validateDeps?.has(path.key) || !form.validate) return;
762
+ if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || form.reValidateMode === "onChange" && hasFormValidateErrors(form)) {
763
+ runFormValidate(form).catch(() => {
764
+ });
765
+ }
766
+ }
681
767
  async function ensureValidate(form) {
682
768
  form.validators.forEach((validator) => validator());
683
769
  await waitUntil(
@@ -1429,6 +1515,7 @@
1429
1515
  setValueByPath(form, path, v);
1430
1516
  if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || liveErrors.length > 0 && form.reValidateMode === "onChange")
1431
1517
  validator();
1518
+ revalidateFormOnChange(form, path, mode);
1432
1519
  });
1433
1520
  React.useEffect(() => {
1434
1521
  form.changeHandlers.set(path.key, onChange);
@@ -1901,6 +1988,7 @@
1901
1988
  exports.removeFieldByPath = removeFieldByPath;
1902
1989
  exports.reset = reset;
1903
1990
  exports.resetField = resetField;
1991
+ exports.revalidateFormOnChange = revalidateFormOnChange;
1904
1992
  exports.setDisabled = setDisabled;
1905
1993
  exports.setError = setError;
1906
1994
  exports.setErrorByPath = setErrorByPath;