react-f0rm 0.6.1 → 0.8.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
@@ -228,6 +228,7 @@
228
228
  errors: /* @__PURE__ */ new Map(),
229
229
  touched: /* @__PURE__ */ new Set(),
230
230
  validators: /* @__PURE__ */ new Map(),
231
+ changeHandlers: /* @__PURE__ */ new Map(),
231
232
  validating: /* @__PURE__ */ new Set(),
232
233
  parsedValues: void 0,
233
234
  isSubmitting: false,
@@ -267,6 +268,14 @@
267
268
  if (options?.shouldValidate) form.validators.get(path.key)?.();
268
269
  emit(emitter, "change", path);
269
270
  }
271
+ function changeValue(form, name, value) {
272
+ changeValueByPath(form, create$1(name), value);
273
+ }
274
+ function changeValueByPath(form, path, value) {
275
+ const change = form.changeHandlers.get(path.key);
276
+ if (change) change(value);
277
+ else setValueByPath(form, path, value);
278
+ }
270
279
  function getError(form, name) {
271
280
  return getErrorByPath(form, create$1(name));
272
281
  }
@@ -528,16 +537,13 @@
528
537
  const settle = () => waitUntil(
529
538
  form.emitter,
530
539
  "validating",
531
- () => !form.validating.size,
540
+ () => fieldsSettled(form),
532
541
  () => false
533
542
  );
534
543
  if (name === void 0) {
535
544
  form.validators.forEach((validator) => validator());
536
545
  await settle();
537
- if (form.validate) {
538
- const result = await form.validate(getValues(form));
539
- applyValidateResult(form, result);
540
- }
546
+ if (form.validate) await runFormValidate(form);
541
547
  return !hasErrors(form);
542
548
  }
543
549
  const keys = typeof name === "string" || isSegmentsPath(name) ? [create$1(name).key] : name.map((one) => create$1(one).key);
@@ -580,19 +586,110 @@
580
586
  }
581
587
  setFormErrors(form, result);
582
588
  }
589
+ const FORM_VALIDATING_KEY = "__form_validate__";
590
+ function fieldsSettled(form) {
591
+ for (const key of form.validating) {
592
+ if (key !== FORM_VALIDATING_KEY) return false;
593
+ }
594
+ return true;
595
+ }
596
+ const SETTLED = /* @__PURE__ */ Symbol("form-validate-settled");
597
+ const formValidateStates = /* @__PURE__ */ new WeakMap();
598
+ function getFormValidateState(form) {
599
+ let state = formValidateStates.get(form);
600
+ if (!state) {
601
+ state = {
602
+ timer: null,
603
+ controller: null,
604
+ round: null,
605
+ marked: false,
606
+ waiters: []
607
+ };
608
+ formValidateStates.set(form, state);
609
+ }
610
+ return state;
611
+ }
612
+ function runFormValidate(form) {
613
+ const validate2 = form.validate;
614
+ if (!validate2) return Promise.resolve();
615
+ const debounce = form.validateDebounce ?? 0;
616
+ if (debounce <= 0) {
617
+ const controller = new AbortController();
618
+ return Promise.resolve(
619
+ validate2(getValues(form), { form, signal: controller.signal })
620
+ ).then((result) => {
621
+ applyValidateResult(form, result);
622
+ });
623
+ }
624
+ const state = getFormValidateState(form);
625
+ if (state.timer !== null) clearTimeout(state.timer);
626
+ else {
627
+ state.marked = true;
628
+ form.validating.add(FORM_VALIDATING_KEY);
629
+ emit(form.emitter, "validating");
630
+ }
631
+ state.timer = setTimeout(() => {
632
+ state.timer = null;
633
+ const round = state.round = {};
634
+ runFormValidateRound(form, state, round).then(
635
+ () => settleFormValidate(form, state, round, SETTLED),
636
+ (error) => settleFormValidate(form, state, round, error)
637
+ );
638
+ }, debounce);
639
+ return new Promise((resolve, reject) => {
640
+ state.waiters.push({ resolve, reject });
641
+ });
642
+ }
643
+ function runFormValidateRound(form, state, round) {
644
+ const validate2 = form.validate;
645
+ if (!validate2) return Promise.resolve();
646
+ state.controller?.abort();
647
+ const controller = state.controller = new AbortController();
648
+ let outcome;
649
+ try {
650
+ outcome = Promise.resolve(
651
+ validate2(getValues(form), { form, signal: controller.signal })
652
+ );
653
+ } catch (error) {
654
+ outcome = Promise.reject(error);
655
+ }
656
+ return outcome.then(
657
+ (result) => {
658
+ if (state.round === round) applyValidateResult(form, result);
659
+ },
660
+ (error) => {
661
+ if (state.round === round) throw error;
662
+ }
663
+ );
664
+ }
665
+ function settleFormValidate(form, state, round, outcome) {
666
+ if (state.round !== round) return;
667
+ state.round = null;
668
+ if (state.timer !== null) return;
669
+ if (state.marked) {
670
+ state.marked = false;
671
+ form.validating.delete(FORM_VALIDATING_KEY);
672
+ emit(form.emitter, "validating");
673
+ }
674
+ const waiters = state.waiters;
675
+ state.waiters = [];
676
+ for (const waiter of waiters) {
677
+ if (outcome === SETTLED) waiter.resolve();
678
+ else waiter.reject(outcome);
679
+ }
680
+ }
583
681
  async function ensureValidate(form) {
584
682
  form.validators.forEach((validator) => validator());
585
683
  await waitUntil(
586
684
  form.emitter,
587
685
  "validating",
588
- () => !form.validating.size,
686
+ () => fieldsSettled(form),
589
687
  () => hasErrors(form)
590
688
  ).catch(() => {
591
689
  throw new Error(getFirstError(form));
592
690
  });
593
691
  if (form.validate) {
594
- const result = await form.validate(getValues(form));
595
- applyValidateResult(form, result);
692
+ await runFormValidate(form);
596
693
  if (hasErrors(form)) throw new Error(getFirstError(form));
597
694
  }
598
695
  }
@@ -1130,6 +1227,24 @@
1130
1227
  function useIsSubmitting(form) {
1131
1228
  return useWatch(form.emitter, "submitting", () => form.isSubmitting);
1132
1229
  }
1230
+ function useCanSubmit(form) {
1231
+ const { emitter } = form;
1232
+ const subscribeFactory = React.useCallback(
1233
+ (invalidate) => {
1234
+ const offErrors = on(emitter, "errors", invalidate);
1235
+ const offSubmitting = on(emitter, "submitting", invalidate);
1236
+ return () => {
1237
+ offErrors();
1238
+ offSubmitting();
1239
+ };
1240
+ },
1241
+ [emitter]
1242
+ );
1243
+ return useWatchCore(
1244
+ subscribeFactory,
1245
+ () => !form.isSubmitting && !hasErrors(form)
1246
+ );
1247
+ }
1133
1248
  function useSubmitCount(form) {
1134
1249
  return useWatch(form.emitter, "submitCount", () => form.submitCount);
1135
1250
  }
@@ -1315,6 +1430,13 @@
1315
1430
  if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || liveErrors.length > 0 && form.reValidateMode === "onChange")
1316
1431
  validator();
1317
1432
  });
1433
+ React.useEffect(() => {
1434
+ form.changeHandlers.set(path.key, onChange);
1435
+ return () => {
1436
+ if (form.changeHandlers.get(path.key) === onChange)
1437
+ form.changeHandlers.delete(path.key);
1438
+ };
1439
+ }, [form, path.key, onChange]);
1318
1440
  const onBlur = useStageFn(() => {
1319
1441
  setTouchedByPath(form, path);
1320
1442
  if (mode === "onBlur" || mode === "onTouched" || mode === "all" || liveErrors.length > 0 && form.reValidateMode === "onBlur")
@@ -1749,6 +1871,8 @@
1749
1871
  exports.FormProvider = FormProvider;
1750
1872
  exports.Select = Select;
1751
1873
  exports.VALIDATION_OUTCOME = VALIDATION_OUTCOME;
1874
+ exports.changeValue = changeValue;
1875
+ exports.changeValueByPath = changeValueByPath;
1752
1876
  exports.clearErrors = clearErrors;
1753
1877
  exports.createForm = create;
1754
1878
  exports.createFormContext = createFormContext;
@@ -1793,6 +1917,7 @@
1793
1917
  exports.subscribe = subscribe;
1794
1918
  exports.trigger = trigger;
1795
1919
  exports.unsetValidatingByPath = unsetValidatingByPath;
1920
+ exports.useCanSubmit = useCanSubmit;
1796
1921
  exports.useCheckboxGroupContext = useCheckboxGroupContext;
1797
1922
  exports.useDirtyFields = useDirtyFields;
1798
1923
  exports.useError = useError;