react-f0rm 0.7.0 → 0.9.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/README.md +63 -4
- package/dist/devtools/index.cjs.js +1 -1
- package/dist/devtools/index.cjs.js.map +1 -1
- package/dist/devtools/index.mjs +1 -1
- package/dist/devtools/index.mjs.map +1 -1
- package/dist/{form-DHKns6j_.d.ts → form-7sbcY2uT.d.ts} +58 -12
- package/dist/form-DbDDJ8bt.cjs.js +2 -0
- package/dist/form-DbDDJ8bt.cjs.js.map +1 -0
- package/dist/form-DsydpBhT.mjs +2 -0
- package/dist/form-DsydpBhT.mjs.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +15 -4
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +161 -17
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +2 -2
- package/dist/index.umd.min.js.map +1 -1
- package/dist/resolvers/standard-schema.cjs.js +1 -1
- package/dist/resolvers/standard-schema.mjs +1 -1
- package/dist/resolvers/yup.cjs.js +1 -1
- package/dist/resolvers/yup.d.ts +1 -1
- package/dist/resolvers/yup.mjs +1 -1
- package/dist/resolvers/zod.cjs.js +1 -1
- package/dist/resolvers/zod.d.ts +1 -1
- package/dist/resolvers/zod.mjs +1 -1
- package/dist/{validate-BoNZhOMY.d.ts → validate-DXGZBon4.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/form-CGRvih_c.cjs.js +0 -2
- package/dist/form-CGRvih_c.cjs.js.map +0 -1
- package/dist/form-CfBBPz6p.mjs +0 -2
- package/dist/form-CfBBPz6p.mjs.map +0 -1
package/dist/index.umd.js
CHANGED
|
@@ -263,18 +263,21 @@
|
|
|
263
263
|
const { emitter, values, deleted } = form;
|
|
264
264
|
values.set(path.key, value);
|
|
265
265
|
reviveBranch(deleted, path);
|
|
266
|
+
pruneDirtyBaselines(form, path);
|
|
267
|
+
if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
|
|
266
268
|
bumpDirtyVersion(form);
|
|
267
269
|
if (options?.shouldTouch) setTouchedByPath(form, path);
|
|
268
270
|
if (options?.shouldValidate) form.validators.get(path.key)?.();
|
|
269
271
|
emit(emitter, "change", path);
|
|
270
272
|
}
|
|
271
|
-
function changeValue(form, name, value) {
|
|
272
|
-
changeValueByPath(form, create$1(name), value);
|
|
273
|
+
function changeValue(form, name, value, options) {
|
|
274
|
+
changeValueByPath(form, create$1(name), value, options);
|
|
273
275
|
}
|
|
274
|
-
function changeValueByPath(form, path, value) {
|
|
276
|
+
function changeValueByPath(form, path, value, options) {
|
|
277
|
+
if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
|
|
275
278
|
const change = form.changeHandlers.get(path.key);
|
|
276
279
|
if (change) change(value);
|
|
277
|
-
else setValueByPath(form, path, value);
|
|
280
|
+
else setValueByPath(form, path, value, options);
|
|
278
281
|
}
|
|
279
282
|
function getError(form, name) {
|
|
280
283
|
return getErrorByPath(form, create$1(name));
|
|
@@ -302,13 +305,15 @@
|
|
|
302
305
|
}
|
|
303
306
|
function getFieldState(form, name) {
|
|
304
307
|
const path = create$1(name);
|
|
305
|
-
const {
|
|
308
|
+
const { values, touched, validating } = form;
|
|
306
309
|
const live = values.get(path.key);
|
|
307
310
|
return {
|
|
308
311
|
value: getValueByPath(form, path),
|
|
309
312
|
error: getErrorByPath(form, path),
|
|
310
313
|
errors: getFieldErrorsByPath(form, path),
|
|
311
|
-
|
|
314
|
+
// Same rule as getDirtyFields, committed baselines included: the field
|
|
315
|
+
// is dirty while its live value differs from its effective baseline.
|
|
316
|
+
isDirty: values.has(path.key) && getDirtyBaseline(form, path.key, path.value) !== live,
|
|
312
317
|
isTouched: touched.has(path.key),
|
|
313
318
|
isValidating: validating.has(path.key)
|
|
314
319
|
};
|
|
@@ -391,11 +396,39 @@
|
|
|
391
396
|
});
|
|
392
397
|
return dirty;
|
|
393
398
|
}
|
|
394
|
-
function forEachDirtyField(
|
|
395
|
-
for (const [key, value] of values) {
|
|
399
|
+
function forEachDirtyField(form, fn) {
|
|
400
|
+
for (const [key, value] of form.values) {
|
|
396
401
|
const path = JSON.parse(key);
|
|
397
|
-
if (
|
|
402
|
+
if (getDirtyBaseline(form, key, path) !== value) fn(path.join("."));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const dirtyBaselines = /* @__PURE__ */ new WeakMap();
|
|
406
|
+
function getDirtyBaseline(form, key, segments) {
|
|
407
|
+
const baselines = dirtyBaselines.get(form);
|
|
408
|
+
if (baselines?.has(key)) return baselines.get(key);
|
|
409
|
+
return get(form.initialValues, segments);
|
|
410
|
+
}
|
|
411
|
+
function setDirtyBaseline(form, { key }, value) {
|
|
412
|
+
let baselines = dirtyBaselines.get(form);
|
|
413
|
+
if (!baselines) {
|
|
414
|
+
baselines = /* @__PURE__ */ new Map();
|
|
415
|
+
dirtyBaselines.set(form, baselines);
|
|
398
416
|
}
|
|
417
|
+
baselines.set(key, value);
|
|
418
|
+
}
|
|
419
|
+
function pruneDirtyBaselines(form, { key }) {
|
|
420
|
+
const baselines = dirtyBaselines.get(form);
|
|
421
|
+
if (!baselines?.size) return;
|
|
422
|
+
const stem = `${key.slice(0, -1)},`;
|
|
423
|
+
for (const baselineKey of baselines.keys()) {
|
|
424
|
+
if (baselineKey.startsWith(stem)) baselines.delete(baselineKey);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function clearDirtyBaselines(form, key) {
|
|
428
|
+
const baselines = dirtyBaselines.get(form);
|
|
429
|
+
if (!baselines) return;
|
|
430
|
+
if (key === void 0) baselines.clear();
|
|
431
|
+
else baselines.delete(key);
|
|
399
432
|
}
|
|
400
433
|
const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
|
|
401
434
|
function bumpDirtyVersion(form) {
|
|
@@ -444,6 +477,7 @@
|
|
|
444
477
|
touched.delete(key);
|
|
445
478
|
errors.delete(key);
|
|
446
479
|
validating.delete(key);
|
|
480
|
+
clearDirtyBaselines(form, key);
|
|
447
481
|
if (!hasLiveBranch(values, segments)) deleted.add(key);
|
|
448
482
|
bumpDirtyVersion(form);
|
|
449
483
|
emit(emitter, "change");
|
|
@@ -477,6 +511,7 @@
|
|
|
477
511
|
form.parsedValues = void 0;
|
|
478
512
|
form.values.clear();
|
|
479
513
|
form.deleted.clear();
|
|
514
|
+
clearDirtyBaselines(form);
|
|
480
515
|
bumpDirtyVersion(form);
|
|
481
516
|
emit(form.emitter, "change");
|
|
482
517
|
}
|
|
@@ -491,6 +526,7 @@
|
|
|
491
526
|
const { emitter, touched, values, deleted, validating } = form;
|
|
492
527
|
values.clear();
|
|
493
528
|
deleted.clear();
|
|
529
|
+
clearDirtyBaselines(form);
|
|
494
530
|
if (!options?.keepTouched) touched.clear();
|
|
495
531
|
validating.clear();
|
|
496
532
|
if (!options?.keepIsSubmitting) form.isSubmitting = false;
|
|
@@ -512,6 +548,7 @@
|
|
|
512
548
|
const path = create$1(name);
|
|
513
549
|
const { emitter, values, touched, errors, deleted } = form;
|
|
514
550
|
values.delete(path.key);
|
|
551
|
+
clearDirtyBaselines(form, path.key);
|
|
515
552
|
if (form.parsedValues !== void 0) {
|
|
516
553
|
form.parsedValues = unset(form.parsedValues, path.value);
|
|
517
554
|
const initial = get(form.initialValues, path.value);
|
|
@@ -537,16 +574,13 @@
|
|
|
537
574
|
const settle = () => waitUntil(
|
|
538
575
|
form.emitter,
|
|
539
576
|
"validating",
|
|
540
|
-
() =>
|
|
577
|
+
() => fieldsSettled(form),
|
|
541
578
|
() => false
|
|
542
579
|
);
|
|
543
580
|
if (name === void 0) {
|
|
544
581
|
form.validators.forEach((validator) => validator());
|
|
545
582
|
await settle();
|
|
546
|
-
if (form.validate)
|
|
547
|
-
const result = await form.validate(getValues(form));
|
|
548
|
-
applyValidateResult(form, result);
|
|
549
|
-
}
|
|
583
|
+
if (form.validate) await runFormValidate(form);
|
|
550
584
|
return !hasErrors(form);
|
|
551
585
|
}
|
|
552
586
|
const keys = typeof name === "string" || isSegmentsPath(name) ? [create$1(name).key] : name.map((one) => create$1(one).key);
|
|
@@ -589,19 +623,110 @@
|
|
|
589
623
|
}
|
|
590
624
|
setFormErrors(form, result);
|
|
591
625
|
}
|
|
626
|
+
const FORM_VALIDATING_KEY = "__form_validate__";
|
|
627
|
+
function fieldsSettled(form) {
|
|
628
|
+
for (const key of form.validating) {
|
|
629
|
+
if (key !== FORM_VALIDATING_KEY) return false;
|
|
630
|
+
}
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
const SETTLED = /* @__PURE__ */ Symbol("form-validate-settled");
|
|
634
|
+
const formValidateStates = /* @__PURE__ */ new WeakMap();
|
|
635
|
+
function getFormValidateState(form) {
|
|
636
|
+
let state = formValidateStates.get(form);
|
|
637
|
+
if (!state) {
|
|
638
|
+
state = {
|
|
639
|
+
timer: null,
|
|
640
|
+
controller: null,
|
|
641
|
+
round: null,
|
|
642
|
+
marked: false,
|
|
643
|
+
waiters: []
|
|
644
|
+
};
|
|
645
|
+
formValidateStates.set(form, state);
|
|
646
|
+
}
|
|
647
|
+
return state;
|
|
648
|
+
}
|
|
649
|
+
function runFormValidate(form) {
|
|
650
|
+
const validate2 = form.validate;
|
|
651
|
+
if (!validate2) return Promise.resolve();
|
|
652
|
+
const debounce = form.validateDebounce ?? 0;
|
|
653
|
+
if (debounce <= 0) {
|
|
654
|
+
const controller = new AbortController();
|
|
655
|
+
return Promise.resolve(
|
|
656
|
+
validate2(getValues(form), { form, signal: controller.signal })
|
|
657
|
+
).then((result) => {
|
|
658
|
+
applyValidateResult(form, result);
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
const state = getFormValidateState(form);
|
|
662
|
+
if (state.timer !== null) clearTimeout(state.timer);
|
|
663
|
+
else {
|
|
664
|
+
state.marked = true;
|
|
665
|
+
form.validating.add(FORM_VALIDATING_KEY);
|
|
666
|
+
emit(form.emitter, "validating");
|
|
667
|
+
}
|
|
668
|
+
state.timer = setTimeout(() => {
|
|
669
|
+
state.timer = null;
|
|
670
|
+
const round = state.round = {};
|
|
671
|
+
runFormValidateRound(form, state, round).then(
|
|
672
|
+
() => settleFormValidate(form, state, round, SETTLED),
|
|
673
|
+
(error) => settleFormValidate(form, state, round, error)
|
|
674
|
+
);
|
|
675
|
+
}, debounce);
|
|
676
|
+
return new Promise((resolve, reject) => {
|
|
677
|
+
state.waiters.push({ resolve, reject });
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
function runFormValidateRound(form, state, round) {
|
|
681
|
+
const validate2 = form.validate;
|
|
682
|
+
if (!validate2) return Promise.resolve();
|
|
683
|
+
state.controller?.abort();
|
|
684
|
+
const controller = state.controller = new AbortController();
|
|
685
|
+
let outcome;
|
|
686
|
+
try {
|
|
687
|
+
outcome = Promise.resolve(
|
|
688
|
+
validate2(getValues(form), { form, signal: controller.signal })
|
|
689
|
+
);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
outcome = Promise.reject(error);
|
|
692
|
+
}
|
|
693
|
+
return outcome.then(
|
|
694
|
+
(result) => {
|
|
695
|
+
if (state.round === round) applyValidateResult(form, result);
|
|
696
|
+
},
|
|
697
|
+
(error) => {
|
|
698
|
+
if (state.round === round) throw error;
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
function settleFormValidate(form, state, round, outcome) {
|
|
703
|
+
if (state.round !== round) return;
|
|
704
|
+
state.round = null;
|
|
705
|
+
if (state.timer !== null) return;
|
|
706
|
+
if (state.marked) {
|
|
707
|
+
state.marked = false;
|
|
708
|
+
form.validating.delete(FORM_VALIDATING_KEY);
|
|
709
|
+
emit(form.emitter, "validating");
|
|
710
|
+
}
|
|
711
|
+
const waiters = state.waiters;
|
|
712
|
+
state.waiters = [];
|
|
713
|
+
for (const waiter of waiters) {
|
|
714
|
+
if (outcome === SETTLED) waiter.resolve();
|
|
715
|
+
else waiter.reject(outcome);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
592
718
|
async function ensureValidate(form) {
|
|
593
719
|
form.validators.forEach((validator) => validator());
|
|
594
720
|
await waitUntil(
|
|
595
721
|
form.emitter,
|
|
596
722
|
"validating",
|
|
597
|
-
() =>
|
|
723
|
+
() => fieldsSettled(form),
|
|
598
724
|
() => hasErrors(form)
|
|
599
725
|
).catch(() => {
|
|
600
726
|
throw new Error(getFirstError(form));
|
|
601
727
|
});
|
|
602
728
|
if (form.validate) {
|
|
603
|
-
|
|
604
|
-
applyValidateResult(form, result);
|
|
729
|
+
await runFormValidate(form);
|
|
605
730
|
if (hasErrors(form)) throw new Error(getFirstError(form));
|
|
606
731
|
}
|
|
607
732
|
}
|
|
@@ -1139,6 +1264,24 @@
|
|
|
1139
1264
|
function useIsSubmitting(form) {
|
|
1140
1265
|
return useWatch(form.emitter, "submitting", () => form.isSubmitting);
|
|
1141
1266
|
}
|
|
1267
|
+
function useCanSubmit(form) {
|
|
1268
|
+
const { emitter } = form;
|
|
1269
|
+
const subscribeFactory = React.useCallback(
|
|
1270
|
+
(invalidate) => {
|
|
1271
|
+
const offErrors = on(emitter, "errors", invalidate);
|
|
1272
|
+
const offSubmitting = on(emitter, "submitting", invalidate);
|
|
1273
|
+
return () => {
|
|
1274
|
+
offErrors();
|
|
1275
|
+
offSubmitting();
|
|
1276
|
+
};
|
|
1277
|
+
},
|
|
1278
|
+
[emitter]
|
|
1279
|
+
);
|
|
1280
|
+
return useWatchCore(
|
|
1281
|
+
subscribeFactory,
|
|
1282
|
+
() => !form.isSubmitting && !hasErrors(form)
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1142
1285
|
function useSubmitCount(form) {
|
|
1143
1286
|
return useWatch(form.emitter, "submitCount", () => form.submitCount);
|
|
1144
1287
|
}
|
|
@@ -1811,6 +1954,7 @@
|
|
|
1811
1954
|
exports.subscribe = subscribe;
|
|
1812
1955
|
exports.trigger = trigger;
|
|
1813
1956
|
exports.unsetValidatingByPath = unsetValidatingByPath;
|
|
1957
|
+
exports.useCanSubmit = useCanSubmit;
|
|
1814
1958
|
exports.useCheckboxGroupContext = useCheckboxGroupContext;
|
|
1815
1959
|
exports.useDirtyFields = useDirtyFields;
|
|
1816
1960
|
exports.useError = useError;
|