react-f0rm 0.4.1 → 0.5.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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { a as FieldPath, N as Name, F as Form$1, P as PathValueOf, b as FieldError, c as Path, O as Options } from './form-DeRFdFKE.js';
2
- export { d as FieldErrorEntry, e as FieldState, H as HandleSubmitOptions, f as PathValue, R as ReValidateMode, g as ResetFieldOptions, h as ResetOptions, S as SetFieldOptions, i as SetFocusOptions, V as VALIDATION_OUTCOME, j as ValidateResult, k as ValidationMode, l as ValidationOutcome, m as clearErrors, n as createForm, o as ensureValidate, p as getDirtyFields, q as getError, r as getErrorByPath, s as getErrors, t as getFieldErrors, u as getFieldErrorsByPath, v as getFieldState, w as getFirstError, x as getTouchedFields, y as getValue, z as getValueByPath, A as getValues, B as handleSubmit, C as hasErrors, D as hasTouched, E as hasTouchedByPath, G as incrementSubmitCount, I as isDirty, J as isTouched, K as removeField, L as removeFieldByPath, M as reset, Q as resetField, T as setDisabled, U as setError, W as setErrorByPath, X as setFocus, Y as setInitialValues, Z as setIsSubmitting, _ as setSubmitSuccessful, $ as setTouched, a0 as setTouchedByPath, a1 as setValidatingByPath, a2 as setValue, a3 as setValueByPath, a4 as trigger, a5 as unsetValidatingByPath, a6 as validate } from './form-DeRFdFKE.js';
3
- import { V as Validator } from './validate-B7S9EiRT.js';
1
+ import { a as FieldPath, N as Name, F as Form$1, P as PathValueOf, b as FieldError, c as Path, O as Options } from './form-rTlcIiWH.js';
2
+ export { d as FieldErrorEntry, e as FieldState, H as HandleSubmitOptions, f as PathValue, R as ReValidateMode, g as ResetFieldOptions, h as ResetOptions, S as SetFieldOptions, i as SetFocusOptions, j as SetServerErrorsOptions, V as VALIDATION_OUTCOME, k as ValidateResult, l as ValidationMode, m as ValidationOutcome, n as clearErrors, o as createForm, p as ensureValidate, q as getDirtyFields, r as getError, s as getErrorByPath, t as getErrors, u as getFieldErrors, v as getFieldErrorsByPath, w as getFieldState, x as getFirstError, y as getTouchedFields, z as getValue, A as getValueByPath, B as getValues, C as handleSubmit, D as hasErrors, E as hasTouched, G as hasTouchedByPath, I as incrementSubmitCount, J as isDirty, K as isTouched, L as removeField, M as removeFieldByPath, Q as reset, T as resetField, U as setDisabled, W as setError, X as setErrorByPath, Y as setFocus, Z as setInitialValues, _ as setIsSubmitting, $ as setServerErrors, a0 as setSubmitSuccessful, a1 as setTouched, a2 as setTouchedByPath, a3 as setValidatingByPath, a4 as setValue, a5 as setValueByPath, a6 as trigger, a7 as unsetValidatingByPath, a8 as validate } from './form-rTlcIiWH.js';
3
+ import { V as Validator } from './validate-CQX7BJOD.js';
4
4
  import * as React from 'react';
5
5
  import { ReactNode } from 'react';
6
6
  import { EventEmitter } from '@for-fun/event-emitter';
@@ -431,12 +431,25 @@ interface FieldProps<TValues extends Record<string, any> = any, TPath extends Fi
431
431
  /**
432
432
  * Optional error renderer. When provided and the field has an error,
433
433
  * Field renders `<span id={id} role="alert">{renderError(error, id)}</span>`
434
- * next to the input and points the input's aria-describedby at that span.
435
- * When omitted, no extra element is rendered (headless) and no
436
- * aria-describedby is attached, avoiding dangling id references.
434
+ * next to the input. The input's aria-describedby points at that span's
435
+ * id (the same `fieldErrorId(name)` derivation) whenever the field has
436
+ * an error with or without renderError — so custom error components
437
+ * that render the element themselves (using `fieldErrorId`) get the
438
+ * wiring for free.
437
439
  */
438
440
  renderError?: (error: string, id: string) => React.ReactNode;
439
441
  }
442
+ /**
443
+ * The error-message element id a field's `aria-describedby` points at —
444
+ * `fieldErrorId('a[0].b')` is `'a-0-b'`, the same id `Field`'s built-in
445
+ * `renderError` span carries. This is the library-level wiring convention:
446
+ * whenever a bound field (Field/Checkbox/Select) has an error it sets
447
+ * `aria-invalid` and describes the element with this id, so a custom error
448
+ * component only needs `<span id={fieldErrorId(name)} role="alert">` to
449
+ * complete the accessible-name chain for screen readers.
450
+ * @param name the same field name passed to the bound component
451
+ */
452
+ declare function fieldErrorId(name: Name): string;
440
453
  /**
441
454
  * The callable shape of {@link Field}: `form` + `name` flow their generics
442
455
  * into `validate`'s value argument (`PathValueOf<TValues, TPath>`). A named
@@ -471,5 +484,5 @@ interface SelectComponent {
471
484
  }
472
485
  declare const Select: SelectComponent;
473
486
 
474
- export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, FieldError, FieldPath, Form, FormContext, Form$1 as FormInstance, FormProvider, Name, Options, Path, PathValueOf, Select, Validator, createFormContext, subscribe, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch };
487
+ export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, FieldError, FieldPath, Form, FormContext, Form$1 as FormInstance, FormProvider, Name, Options, Path, PathValueOf, Select, Validator, createFormContext, fieldErrorId, subscribe, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch };
475
488
  export type { FieldRules, SubscribeEvent, SubscribeOptions, WatchScope };
package/dist/index.mjs CHANGED
@@ -144,6 +144,31 @@ function setOwned(root, path, value, owned) {
144
144
  function isPromise(value) {
145
145
  return value && typeof value.then === "function";
146
146
  }
147
+ function isEqual(a, b) {
148
+ if (Object.is(a, b)) return true;
149
+ if (a instanceof Date && b instanceof Date)
150
+ return a.getTime() === b.getTime();
151
+ if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
152
+ const isArray = Array.isArray(a);
153
+ if (isArray !== Array.isArray(b)) return false;
154
+ if (isArray) {
155
+ if (a.length !== b.length) return false;
156
+ for (let i = 0; i < a.length; i++) {
157
+ if (!isEqual(a[i], b[i])) return false;
158
+ }
159
+ return true;
160
+ }
161
+ const proto = Object.getPrototypeOf(a);
162
+ if (proto !== Object.prototype && proto !== null) return false;
163
+ if (Object.getPrototypeOf(b) !== proto) return false;
164
+ const keysA = Object.keys(a);
165
+ const keysB = Object.keys(b);
166
+ if (keysA.length !== keysB.length) return false;
167
+ for (const key of keysA) {
168
+ if (!isEqual(a[key], b[key])) return false;
169
+ }
170
+ return true;
171
+ }
147
172
  function waitUntil(emitter, event, isResolve, isReject) {
148
173
  return new Promise((resolve, reject) => {
149
174
  if (isReject()) return void reject();
@@ -301,6 +326,19 @@ function clearErrors(form, name) {
301
326
  for (const { key } of paths) errors.delete(key);
302
327
  for (const path of paths) emit(emitter, "errors", path);
303
328
  }
329
+ function setServerErrors(form, errors, options) {
330
+ if (!options?.keepExisting) clearErrors(form);
331
+ for (const [name, error] of Object.entries(errors)) {
332
+ setError(
333
+ form,
334
+ name,
335
+ (Array.isArray(error) ? error : [error]).map((message) => ({
336
+ type: "server",
337
+ message
338
+ }))
339
+ );
340
+ }
341
+ }
304
342
  function setTouched(form, name) {
305
343
  setTouchedByPath(form, create$1(name));
306
344
  }
@@ -401,7 +439,9 @@ function reviveBranch(deleted, { key }) {
401
439
  }
402
440
  }
403
441
  function setInitialValues(form, initialValues) {
404
- if (form.initialValues === initialValues) return;
442
+ if (form.initialValues === initialValues || isEqual(form.initialValues, initialValues)) {
443
+ return;
444
+ }
405
445
  form.initialValues = initialValues;
406
446
  form.parsedValues = void 0;
407
447
  form.values.clear();
@@ -1071,31 +1111,6 @@ function useIsSubmitting(form) {
1071
1111
  function useSubmitCount(form) {
1072
1112
  return useWatch(form.emitter, "submitCount", () => form.submitCount);
1073
1113
  }
1074
- function isEqual(a, b) {
1075
- if (Object.is(a, b)) return true;
1076
- if (a instanceof Date && b instanceof Date)
1077
- return a.getTime() === b.getTime();
1078
- if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
1079
- const isArray = Array.isArray(a);
1080
- if (isArray !== Array.isArray(b)) return false;
1081
- if (isArray) {
1082
- if (a.length !== b.length) return false;
1083
- for (let i = 0; i < a.length; i++) {
1084
- if (!isEqual(a[i], b[i])) return false;
1085
- }
1086
- return true;
1087
- }
1088
- const proto = Object.getPrototypeOf(a);
1089
- if (proto !== Object.prototype && proto !== null) return false;
1090
- if (Object.getPrototypeOf(b) !== proto) return false;
1091
- const keysA = Object.keys(a);
1092
- const keysB = Object.keys(b);
1093
- if (keysA.length !== keysB.length) return false;
1094
- for (const key of keysA) {
1095
- if (!isEqual(a[key], b[key])) return false;
1096
- }
1097
- return true;
1098
- }
1099
1114
 
1100
1115
  function usePath(name) {
1101
1116
  const path = useMemo(() => create$1(normalizePath(name)), [name]);
@@ -1478,6 +1493,9 @@ function errorIdFromKey(key) {
1478
1493
  const id = key.replace(/["'[\],\s]+/g, "-").replace(/^-+|-+$/g, "");
1479
1494
  return id || "field";
1480
1495
  }
1496
+ function fieldErrorId(name) {
1497
+ return errorIdFromKey(create$1(name).key);
1498
+ }
1481
1499
  const Field = React.forwardRef(
1482
1500
  ({
1483
1501
  validate,
@@ -1566,16 +1584,17 @@ const Field = React.forwardRef(
1566
1584
  );
1567
1585
  const toValue = eventToValue ?? ((e) => e.target.value);
1568
1586
  const errorId = errorIdFromKey(fieldKey);
1587
+ const describedBy = error ? [props["aria-describedby"], errorId].filter(Boolean).join(" ") : props["aria-describedby"];
1569
1588
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1570
1589
  Component,
1571
1590
  {
1572
- "aria-invalid": error ? true : void 0,
1573
- "aria-describedby": error && renderError ? errorId : void 0,
1574
1591
  ...props,
1575
1592
  name: fieldKey,
1576
1593
  onBlur,
1577
1594
  ...asProps,
1578
1595
  ...valueToProps ? valueToProps(value) : { value },
1596
+ "aria-invalid": error ? true : props["aria-invalid"],
1597
+ "aria-describedby": describedBy,
1579
1598
  disabled: isDisabled,
1580
1599
  onChange: (e) => onChange(toValue(e)),
1581
1600
  ref: mergedRef
@@ -1617,12 +1636,13 @@ const Checkbox = React.forwardRef(
1617
1636
  return /* @__PURE__ */ React.createElement(
1618
1637
  "input",
1619
1638
  {
1620
- "aria-invalid": error ? true : void 0,
1621
1639
  ...props,
1622
1640
  name: fieldKey,
1623
1641
  onBlur,
1624
1642
  type: "checkbox",
1625
1643
  checked: !!value,
1644
+ "aria-invalid": error ? true : props["aria-invalid"],
1645
+ "aria-describedby": error ? [props["aria-describedby"], errorIdFromKey(fieldKey)].filter(Boolean).join(" ") : props["aria-describedby"],
1626
1646
  disabled: isDisabled,
1627
1647
  onChange: (e) => onChange(e.target.checked),
1628
1648
  ref
@@ -1670,12 +1690,13 @@ const Select = React.forwardRef(
1670
1690
  return /* @__PURE__ */ React.createElement(
1671
1691
  "select",
1672
1692
  {
1673
- "aria-invalid": error ? true : void 0,
1674
1693
  ...props,
1675
1694
  name: fieldKey,
1676
1695
  onBlur,
1677
1696
  multiple,
1678
1697
  value: toSelectValue(multiple, value),
1698
+ "aria-invalid": error ? true : props["aria-invalid"],
1699
+ "aria-describedby": error ? [props["aria-describedby"], errorIdFromKey(fieldKey)].filter(Boolean).join(" ") : props["aria-describedby"],
1679
1700
  disabled: isDisabled,
1680
1701
  onChange: (e) => onChange(
1681
1702
  multiple ? Array.from(e.target.selectedOptions, (option) => option.value) : e.target.value
@@ -1687,5 +1708,5 @@ const Select = React.forwardRef(
1687
1708
  }
1688
1709
  );
1689
1710
 
1690
- export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, Form, FormContext, FormProvider, Select, VALIDATION_OUTCOME, clearErrors, create as createForm, createFormContext, ensureValidate, getDirtyFields, getError, getErrorByPath, getErrors, getFieldErrors, getFieldErrorsByPath, getFieldState, getFirstError, getTouchedFields, getValue, getValueByPath, getValues, handleSubmit, hasErrors, hasTouched, hasTouchedByPath, incrementSubmitCount, isDirty, isTouched, removeField, removeFieldByPath, reset, resetField, setDisabled, setError, setErrorByPath, setFocus, setInitialValues, setIsSubmitting, setSubmitSuccessful, setTouched, setTouchedByPath, setValidatingByPath, setValue, setValueByPath, subscribe, trigger, unsetValidatingByPath, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch, validate };
1711
+ export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, Form, FormContext, FormProvider, Select, VALIDATION_OUTCOME, clearErrors, create as createForm, createFormContext, ensureValidate, fieldErrorId, getDirtyFields, getError, getErrorByPath, getErrors, getFieldErrors, getFieldErrorsByPath, getFieldState, getFirstError, getTouchedFields, getValue, getValueByPath, getValues, handleSubmit, hasErrors, hasTouched, hasTouchedByPath, incrementSubmitCount, isDirty, isTouched, removeField, removeFieldByPath, reset, resetField, setDisabled, setError, setErrorByPath, setFocus, setInitialValues, setIsSubmitting, setServerErrors, setSubmitSuccessful, setTouched, setTouchedByPath, setValidatingByPath, setValue, setValueByPath, subscribe, trigger, unsetValidatingByPath, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch, validate };
1691
1712
  //# sourceMappingURL=index.mjs.map