@zeno-lib/forms 0.10.0 → 0.10.1

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.
Files changed (75) hide show
  1. package/dist/create-zeno-form.d.mts +1 -2
  2. package/dist/create-zeno-form.mjs +1 -2
  3. package/dist/form-element.d.mts +0 -2
  4. package/dist/form-element.mjs +1 -2
  5. package/dist/lib/action-result.d.mts +0 -2
  6. package/dist/lib/action-result.mjs +1 -2
  7. package/dist/lib/apply-validation-error.d.mts +0 -2
  8. package/dist/lib/apply-validation-error.mjs +1 -2
  9. package/dist/lib/aria.d.mts +1 -3
  10. package/dist/lib/aria.mjs +1 -2
  11. package/dist/lib/contexts.d.mts +0 -2
  12. package/dist/lib/contexts.mjs +1 -2
  13. package/dist/lib/date-value.d.mts +0 -2
  14. package/dist/lib/date-value.mjs +2 -3
  15. package/dist/lib/default-props.d.mts +1 -3
  16. package/dist/lib/default-props.mjs +1 -2
  17. package/dist/lib/empty-value.d.mts +0 -2
  18. package/dist/lib/empty-value.mjs +3 -4
  19. package/dist/lib/field-hints.d.mts +0 -2
  20. package/dist/lib/field-hints.mjs +1 -2
  21. package/dist/lib/focus-first-invalid.d.mts +0 -2
  22. package/dist/lib/focus-first-invalid.mjs +1 -2
  23. package/dist/lib/form-disabled.d.mts +0 -2
  24. package/dist/lib/form-disabled.mjs +1 -2
  25. package/dist/lib/formatted-number.d.mts +0 -2
  26. package/dist/lib/formatted-number.mjs +10 -11
  27. package/dist/lib/item-options.d.mts +0 -2
  28. package/dist/lib/item-options.mjs +1 -2
  29. package/dist/lib/schema-defaults.d.mts +0 -2
  30. package/dist/lib/schema-defaults.mjs +1 -2
  31. package/dist/lib/schema-required.d.mts +0 -2
  32. package/dist/lib/schema-required.mjs +3 -4
  33. package/dist/lib/submit-action.d.mts +0 -2
  34. package/dist/lib/submit-action.mjs +1 -2
  35. package/dist/lib/submit-error.d.mts +0 -2
  36. package/dist/lib/submit-error.mjs +1 -2
  37. package/dist/lib/use-field-control.d.mts +0 -2
  38. package/dist/lib/use-field-control.mjs +1 -2
  39. package/dist/lib/use-form-dialog.d.mts +0 -2
  40. package/dist/lib/use-form-dialog.mjs +2 -3
  41. package/dist/lib/use-formatted-number.d.mts +0 -2
  42. package/dist/lib/use-formatted-number.mjs +1 -2
  43. package/dist/lib/use-is-invalid.d.mts +0 -2
  44. package/dist/lib/use-is-invalid.mjs +1 -2
  45. package/dist/lib/use-rebased-default-values.d.mts +0 -2
  46. package/dist/lib/use-rebased-default-values.mjs +1 -2
  47. package/dist/lib/use-unsaved-changes-warning.d.mts +0 -2
  48. package/dist/lib/use-unsaved-changes-warning.mjs +1 -2
  49. package/dist/lib/validation-error.d.mts +0 -2
  50. package/dist/lib/validation-error.mjs +1 -2
  51. package/dist/lib/validation-logic.d.mts +0 -2
  52. package/dist/lib/validation-logic.mjs +1 -2
  53. package/dist/lib/validation-modes.d.mts +0 -2
  54. package/dist/lib/validation-modes.mjs +1 -2
  55. package/package.json +8 -8
  56. package/src/composition.test.tsx +3 -3
  57. package/src/control-field.test.tsx +1 -1
  58. package/src/date-picker-field.test.tsx +3 -3
  59. package/src/form-dialog.test.tsx +3 -3
  60. package/src/form-error.test.tsx +1 -1
  61. package/src/formatted-number-fields.test.tsx +3 -3
  62. package/src/lib/date-value.ts +2 -1
  63. package/src/lib/empty-value.ts +2 -2
  64. package/src/lib/formatted-number.ts +9 -9
  65. package/src/lib/schema-required.ts +2 -2
  66. package/src/lib/use-form-dialog.ts +1 -1
  67. package/src/multi-value-fields.test.tsx +2 -2
  68. package/src/submit-action.test.tsx +4 -4
  69. package/src/use-app-fields.test.tsx +10 -10
  70. package/src/use-form.cascade.test.tsx +2 -2
  71. package/src/use-form.reset-defaults.test.tsx +2 -2
  72. package/src/use-form.test.tsx +3 -3
  73. package/src/use-form.unsaved-warning.test.tsx +1 -1
  74. package/src/use-form.validation-modes.test.tsx +2 -2
  75. package/src/with-form.test.tsx +1 -1
@@ -2,7 +2,6 @@ import { FieldControlProps } from "./lib/use-field-control.mjs";
2
2
  import { ValidationMode } from "./lib/validation-modes.mjs";
3
3
  import { AppFieldExtendedReactFieldGroupApi, AppFieldExtendedReactFormApi, BaseFormOptions, DeepKeys, DeepValue, FieldsMap, FormAsyncValidateOrFn, FormOptions, FormValidateOrFn, WithFormProps } from "@tanstack/react-form";
4
4
  import { ComponentProps, PropsWithChildren, ReactNode } from "react";
5
- //#region src/create-zeno-form.d.ts
6
5
  type StandardSchema<T> = {
7
6
  readonly "~standard": {
8
7
  readonly validate: (value: unknown) => unknown;
@@ -144,4 +143,4 @@ export declare function createZenoForm<FC extends Record<string, AnyComponent>,
144
143
  form: AppFieldExtendedReactFormApi<[unknown] extends [TFormData] ? any : TFormData, [FormValidateOrFn<TFormData> | undefined] extends [TOnMount] ? [TOnMount] extends [TOnMount & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnMount : TOnMount, [FormValidateOrFn<TFormData> | undefined] extends [TOnChange] ? [TOnChange] extends [TOnChange & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnChange : TOnChange, [FormValidateOrFn<TFormData> | undefined] extends [TOnChangeAsync] ? [TOnChangeAsync] extends [TOnChangeAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnChangeAsync : TOnChangeAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnBlur] ? [TOnBlur] extends [TOnBlur & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlur : TOnBlur, [FormValidateOrFn<TFormData> | undefined] extends [TOnBlurAsync] ? [TOnBlurAsync] extends [TOnBlurAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlurAsync : TOnBlurAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnSubmit] ? [TOnSubmit] extends [TOnSubmit & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmit : TOnSubmit, [FormValidateOrFn<TFormData> | undefined] extends [TOnSubmitAsync] ? [TOnSubmitAsync] extends [TOnSubmitAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmitAsync : TOnSubmitAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnDynamic] ? [TOnDynamic] extends [TOnDynamic & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamic : TOnDynamic, [FormValidateOrFn<TFormData> | undefined] extends [TOnDynamicAsync] ? [TOnDynamicAsync] extends [TOnDynamicAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamicAsync : TOnDynamicAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnServer] ? [TOnServer] extends [TOnServer & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnServer : TOnServer, [unknown] extends [TSubmitMeta] ? any : TSubmitMeta, [unknown] extends [FC] ? any : FC, [unknown] extends [FMC] ? any : FMC>;
145
144
  }>>;
146
145
  };
147
- //#endregion
146
+ export {};
@@ -15,7 +15,7 @@ import { blurThenChangeLogic } from "./lib/validation-logic.mjs";
15
15
  import { createFormHook } from "@tanstack/react-form";
16
16
  import { useMemo } from "react";
17
17
  import { jsx } from "react/jsx-runtime";
18
- //#region src/create-zeno-form.tsx
18
+ // src/create-zeno-form.tsx
19
19
  function buildValidatorsFromSchema(schema, mode) {
20
20
  switch (mode) {
21
21
  case "blur": return { onBlur: schema };
@@ -191,5 +191,4 @@ function createZenoForm(config) {
191
191
  withForm
192
192
  };
193
193
  }
194
- //#endregion
195
194
  export { createZenoForm };
@@ -1,5 +1,4 @@
1
1
  import { ComponentProps, ReactNode } from "react";
2
- //#region src/form-element.d.ts
3
2
  type FormProviderProps = {
4
3
  children: ReactNode;
5
4
  /**
@@ -22,5 +21,4 @@ type FormProps = Omit<ComponentProps<"form">, "onSubmit"> & {
22
21
  disabled?: boolean;
23
22
  };
24
23
  declare function Form({ children, className, disabled, ...props }: FormProps): import("react").JSX.Element;
25
- //#endregion
26
24
  export { Form, FormProvider };
@@ -5,7 +5,7 @@ import { reportSubmitError } from "./lib/submit-error.mjs";
5
5
  import { focusFirstInvalid, restoreFocus } from "./lib/focus-first-invalid.mjs";
6
6
  import { useMemo } from "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
- //#region src/form-element.tsx
8
+ // src/form-element.tsx
9
9
  function FormProvider({ children, disabled = false, form }) {
10
10
  const { store } = form;
11
11
  const scope = useMemo(() => ({
@@ -49,5 +49,4 @@ function Form({ children, className, disabled = false, ...props }) {
49
49
  })
50
50
  });
51
51
  }
52
- //#endregion
53
52
  export { Form, FormProvider };
@@ -1,4 +1,3 @@
1
- //#region src/lib/action-result.d.ts
2
1
  type ActionError = {
3
2
  readonly fieldErrors: Readonly<Record<string, readonly string[]>>;
4
3
  readonly formErrors: readonly string[];
@@ -18,5 +17,4 @@ type ActionIssue = {
18
17
  };
19
18
  declare function toFieldName(path: ActionIssue["path"]): string;
20
19
  declare function toActionError(issues: readonly ActionIssue[]): ActionError;
21
- //#endregion
22
20
  export { type ActionError, type ActionIssue, type ActionResult, toActionError, toFieldName };
@@ -1,4 +1,4 @@
1
- //#region src/lib/action-result.ts
1
+ // src/lib/action-result.ts
2
2
  function toFieldName(path) {
3
3
  let name = "";
4
4
  for (const segment of path ?? []) {
@@ -28,5 +28,4 @@ function toActionError(issues) {
28
28
  formErrors
29
29
  };
30
30
  }
31
- //#endregion
32
31
  export { toActionError, toFieldName };
@@ -1,6 +1,4 @@
1
1
  import { ValidationError } from "./validation-error.mjs";
2
2
  import { AnyFormApi } from "@tanstack/react-form";
3
- //#region src/lib/apply-validation-error.d.ts
4
3
  declare function applyValidationError(formApi: AnyFormApi, error: ValidationError): void;
5
- //#endregion
6
4
  export { applyValidationError };
@@ -1,4 +1,4 @@
1
- //#region src/lib/apply-validation-error.ts
1
+ // src/lib/apply-validation-error.ts
2
2
  function clearWhenEdited(formApi, name, entry) {
3
3
  const rejected = formApi.getFieldValue(name);
4
4
  const subscription = formApi.store.subscribe(() => {
@@ -37,5 +37,4 @@ function applyValidationError(formApi, error) {
37
37
  form: error.formError
38
38
  } });
39
39
  }
40
- //#endregion
41
40
  export { applyValidationError };
@@ -1,3 +1 @@
1
- //#region src/lib/aria.d.ts
2
- export declare function describedBy(...entries: [unknown, string][]): string | undefined;
3
- //#endregion
1
+ export declare function describedBy(...entries: [unknown, string][]): string | undefined;
package/dist/lib/aria.mjs CHANGED
@@ -1,7 +1,6 @@
1
- //#region src/lib/aria.ts
1
+ // src/lib/aria.ts
2
2
  function describedBy(...entries) {
3
3
  const ids = entries.filter(([show]) => Boolean(show)).map(([, id]) => id);
4
4
  return ids.length > 0 ? ids.join(" ") : void 0;
5
5
  }
6
- //#endregion
7
6
  export { describedBy };
@@ -1,5 +1,3 @@
1
- //#region src/lib/contexts.d.ts
2
1
  declare const fieldContext: import("react").Context<import("@tanstack/react-form").AnyFieldApi>, formContext: import("react").Context<import("@tanstack/react-form").AnyFormApi>, useFieldContext: <TData>() => import("@tanstack/react-form").FieldApi<any, string, TData, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>, useFormContext: () => import("@tanstack/react-form").ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
3
2
  export declare const FormProvider: import("react").Provider<import("@tanstack/react-form").AnyFormApi>;
4
- //#endregion
5
3
  export { fieldContext, formContext, useFieldContext, useFormContext };
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { createFormHookContexts } from "@tanstack/react-form";
3
- //#region src/lib/contexts.ts
3
+ // src/lib/contexts.ts
4
4
  const { fieldContext, formContext, useFieldContext, useFormContext } = createFormHookContexts();
5
5
  const FormProvider = formContext.Provider;
6
- //#endregion
7
6
  export { FormProvider, fieldContext, formContext, useFieldContext, useFormContext };
@@ -1,4 +1,3 @@
1
- //#region src/lib/date-value.d.ts
2
1
  /**
3
2
  * What a date field stores for a picked day:
4
3
  * - `"date"`: a `Date`, at local midnight.
@@ -20,5 +19,4 @@ declare function parseDateValue(value: unknown): Date | undefined;
20
19
  declare function formatIsoDate(date: Date): string;
21
20
  /** The value to store for `date`, a day picked in local time. */
22
21
  declare function toDateValue(date: Date, type: DateValueType): Date | string;
23
- //#endregion
24
22
  export { type DateValueType, formatIsoDate, parseDateValue, toDateValue };
@@ -1,5 +1,5 @@
1
- //#region src/lib/date-value.ts
2
- const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
1
+ // src/lib/date-value.ts
2
+ const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/u;
3
3
  function pad(value, length) {
4
4
  return String(value).padStart(length, "0");
5
5
  }
@@ -33,5 +33,4 @@ function toDateValue(date, type) {
33
33
  if (type === "iso-datetime") return date.toISOString();
34
34
  return date;
35
35
  }
36
- //#endregion
37
36
  export { formatIsoDate, parseDateValue, toDateValue };
@@ -1,7 +1,5 @@
1
- //#region src/lib/default-props.d.ts
2
1
  /**
3
2
  * The props a component renders with: `props` over `defaults`. A prop passed
4
3
  * as `undefined` keeps its default, as a destructuring default would.
5
4
  */
6
- export declare function withDefaultProps<P extends object>(defaults: Partial<P> | undefined, props: P): P;
7
- //#endregion
5
+ export declare function withDefaultProps<P extends object>(defaults: Partial<P> | undefined, props: P): P;
@@ -1,4 +1,4 @@
1
- //#region src/lib/default-props.ts
1
+ // src/lib/default-props.ts
2
2
  /**
3
3
  * The props a component renders with: `props` over `defaults`. A prop passed
4
4
  * as `undefined` keeps its default, as a destructuring default would.
@@ -9,5 +9,4 @@ function withDefaultProps(defaults, props) {
9
9
  for (const [key, value] of Object.entries(props)) if (value !== void 0) merged[key] = value;
10
10
  return merged;
11
11
  }
12
- //#endregion
13
12
  export { withDefaultProps };
@@ -1,6 +1,5 @@
1
1
  import { StandardSchemaLike } from "./schema-required.mjs";
2
2
  import { AnyFieldApi } from "@tanstack/react-form";
3
- //#region src/lib/empty-value.d.ts
4
3
  declare function toPathKeys(name: string): PropertyKey[];
5
4
  declare function emptyValueFor(schema: StandardSchemaLike | undefined, values: unknown, name: string, fallback: unknown): unknown;
6
5
  declare function withEmptyStartValues(schema: StandardSchemaLike, defaults: Record<string, unknown>): Record<string, unknown>;
@@ -16,5 +15,4 @@ declare function getEmptyValue<T>(field: AnyFieldApi, fallback: T): T;
16
15
  * so the other fields' values never change it.
17
16
  */
18
17
  declare function getNoTextValue(field: AnyFieldApi): string | null;
19
- //#endregion
20
18
  export { emptyValueFor, getEmptyValue, getNoTextValue, toPathKeys, withEmptyStartValues };
@@ -2,9 +2,9 @@ import { toFieldName } from "./action-result.mjs";
2
2
  import { extractZodDefaultAt } from "./schema-defaults.mjs";
3
3
  import { validateSync } from "./schema-required.mjs";
4
4
  import { getFormSchema } from "./validation-modes.mjs";
5
- //#region src/lib/empty-value.ts
6
- const NAME_SEGMENT = /[^.[\]]+/g;
7
- const INDEX = /^\d+$/;
5
+ // src/lib/empty-value.ts
6
+ const NAME_SEGMENT = /[^.[\]]+/gu;
7
+ const INDEX = /^\d+$/u;
8
8
  function toPathKeys(name) {
9
9
  return (name.match(NAME_SEGMENT) ?? []).map((part) => INDEX.test(part) ? Number(part) : part);
10
10
  }
@@ -99,5 +99,4 @@ function getNoTextValue(field) {
99
99
  const start = schema ? extractZodDefaultAt(schema, toPathKeys(field.name)) : { ok: false };
100
100
  return start.ok && start.value === null ? null : "";
101
101
  }
102
- //#endregion
103
102
  export { emptyValueFor, getEmptyValue, getNoTextValue, toPathKeys, withEmptyStartValues };
@@ -1,5 +1,4 @@
1
1
  import { AnyFieldApi } from "@tanstack/react-form";
2
- //#region src/lib/field-hints.d.ts
3
2
  /**
4
3
  * What a field reads from the column its schema comes from, when the column's
5
4
  * hint names it. The `percentage()` and `year()` columns of
@@ -14,5 +13,4 @@ type FieldHints = {
14
13
  readonly scale?: "fraction" | "percent" | undefined;
15
14
  };
16
15
  declare function getFieldHints(field: AnyFieldApi, kind: string): FieldHints;
17
- //#endregion
18
16
  export { type FieldHints, getFieldHints };
@@ -1,7 +1,7 @@
1
1
  import { findZodSchemaAt } from "./schema-defaults.mjs";
2
2
  import { getFormSchema } from "./validation-modes.mjs";
3
3
  import { toPathKeys } from "./empty-value.mjs";
4
- //#region src/lib/field-hints.ts
4
+ // src/lib/field-hints.ts
5
5
  const FIELD_HINTS_KEY = Symbol.for("@zeno-lib/field-hints");
6
6
  const readHint = (node) => {
7
7
  const hint = globalThis[FIELD_HINTS_KEY]?.get(node);
@@ -20,5 +20,4 @@ function getFieldHints(field, kind) {
20
20
  scale: scale === "fraction" || scale === "percent" ? scale : void 0
21
21
  };
22
22
  }
23
- //#endregion
24
23
  export { getFieldHints };
@@ -1,4 +1,3 @@
1
- //#region src/lib/focus-first-invalid.d.ts
2
1
  /**
3
2
  * Move focus to the first invalid control inside `root` (a `<form>`): the
4
3
  * first element with `aria-invalid="true"`, or, for a group root that can't
@@ -19,5 +18,4 @@ declare function focusFirstInvalid(root: ParentNode | null | undefined): HTMLEle
19
18
  * moved focus.
20
19
  */
21
20
  declare function restoreFocus(element: Element | null): boolean;
22
- //#endregion
23
21
  export { focusFirstInvalid, restoreFocus };
@@ -1,4 +1,4 @@
1
- //#region src/lib/focus-first-invalid.ts
1
+ // src/lib/focus-first-invalid.ts
2
2
  /**
3
3
  * Move focus to the first invalid control inside `root` (a `<form>`): the
4
4
  * first element with `aria-invalid="true"`, or, for a group root that can't
@@ -33,5 +33,4 @@ function restoreFocus(element) {
33
33
  element.focus({ preventScroll: true });
34
34
  return element.ownerDocument.activeElement === element;
35
35
  }
36
- //#endregion
37
36
  export { focusFirstInvalid, restoreFocus };
@@ -1,5 +1,4 @@
1
1
  import { AnyFormApi } from "@tanstack/react-form";
2
- //#region src/lib/form-disabled.d.ts
3
2
  /** Apply `useForm({ disabled })` to `form`. */
4
3
  declare function useDisabledOption(form: AnyFormApi, disabled: boolean): void;
5
4
  type FormDisabledScope = {
@@ -24,5 +23,4 @@ declare const FormDisabledContext: import("react").Context<FormDisabledScope | n
24
23
  * mounted.
25
24
  */
26
25
  declare function useIsFormDisabled(form?: AnyFormApi): boolean;
27
- //#endregion
28
26
  export { FormDisabledContext, useDisabledOption, useIsFormDisabled };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { formContext } from "./contexts.mjs";
3
3
  import { createContext, useCallback, useContext, useLayoutEffect, useSyncExternalStore } from "react";
4
- //#region src/lib/form-disabled.ts
4
+ // src/lib/form-disabled.ts
5
5
  const disabledOption = /* @__PURE__ */ new WeakMap();
6
6
  const listeners = /* @__PURE__ */ new WeakMap();
7
7
  function subscribe(key, listener) {
@@ -50,5 +50,4 @@ function useIsFormDisabled(form) {
50
50
  const byOption = useSyncExternalStore(subscribeToKey, getSnapshot, getSnapshot);
51
51
  return scope?.disabled === true && (key === void 0 || scope.key === key) || byOption;
52
52
  }
53
- //#endregion
54
53
  export { FormDisabledContext, useDisabledOption, useIsFormDisabled };
@@ -1,4 +1,3 @@
1
- //#region src/lib/formatted-number.d.ts
2
1
  type NumberSeparators = {
3
2
  /** Thousands separator, e.g. `’` for `de-CH`, `,` for `en-US`. */
4
3
  group: string;
@@ -52,5 +51,4 @@ declare function parseLocaleNumber(text: string, locale?: string): number | null
52
51
  declare function mapCaret(raw: string, caret: number, next: string, locale?: string): number;
53
52
  /** Round to `digits` decimals without the `1.005 → 1.00` float surprise. */
54
53
  declare function roundTo(value: number, digits: number): number;
55
- //#endregion
56
54
  export { type FormatNumberOptions, type NumberSeparators, type ParseTypedOptions, type TypedNumber, formatNumber, getNumberSeparators, mapCaret, parseLocaleNumber, parseTypedNumber, roundTo };
@@ -1,14 +1,14 @@
1
- //#region src/lib/formatted-number.ts
1
+ // src/lib/formatted-number.ts
2
2
  const separatorCache = /* @__PURE__ */ new Map();
3
- const MINUS_SIGNS = /[-−–]/;
4
- const LEADING_MINUS = /^[^\d]*[-−–]/;
5
- const NON_DIGIT = /\D/g;
6
- const LEADING_ZEROS = /^0+(?=\d)/;
7
- const DIGIT = /\d/;
8
- const DIGITS = /\d/g;
9
- const PARENTHESIZED = /^\s*\(.*\)\s*$/;
10
- const NOT_NUMERIC = /[^\d.,]/g;
11
- const UNICODE_MINUS = /\u2212/g;
3
+ const MINUS_SIGNS = /[-−–]/u;
4
+ const LEADING_MINUS = /^[^\d]*[-−–]/u;
5
+ const NON_DIGIT = /\D/gu;
6
+ const LEADING_ZEROS = /^0+(?=\d)/u;
7
+ const DIGIT = /\d/u;
8
+ const DIGITS = /\d/gu;
9
+ const PARENTHESIZED = /^\s*\(.*\)\s*$/u;
10
+ const NOT_NUMERIC = /[^\d.,]/gu;
11
+ const UNICODE_MINUS = /\u2212/gu;
12
12
  function getNumberSeparators(locale) {
13
13
  const key = locale ?? "";
14
14
  const cached = separatorCache.get(key);
@@ -134,5 +134,4 @@ function roundTo(value, digits) {
134
134
  const rounded = Math.round((Math.abs(value) + Number.EPSILON) * factor);
135
135
  return Math.sign(value) * rounded / factor;
136
136
  }
137
- //#endregion
138
137
  export { formatNumber, getNumberSeparators, mapCaret, parseLocaleNumber, parseTypedNumber, roundTo };
@@ -1,4 +1,3 @@
1
- //#region src/lib/item-options.d.ts
2
1
  /** An item whose value isn't its label. */
3
2
  type ItemObject<V> = {
4
3
  value: V;
@@ -41,5 +40,4 @@ declare function toItemList<T>(items: readonly T[]): ItemList<T>;
41
40
  declare function isSameValue(a: unknown, b: unknown, isItemEqualToValue: ValueComparator | undefined): boolean;
42
41
  /** The option whose value is `value`, if `items` has it. */
43
42
  declare function findOption<T>(list: ItemList<T>, value: unknown, isItemEqualToValue: ValueComparator | undefined): ItemOption<T> | undefined;
44
- //#endregion
45
43
  export { type ItemGroup, type ItemList, type ItemObject, type ItemOption, type ItemValue, type ValueComparator, findOption, isItemGroup, isItemObject, isSameValue, itemLabel, itemValue, toItemList, valueKey };
@@ -1,4 +1,4 @@
1
- //#region src/lib/item-options.ts
1
+ // src/lib/item-options.ts
2
2
  function isItemObject(item) {
3
3
  return typeof item === "object" && item !== null && "value" in item && "label" in item;
4
4
  }
@@ -66,5 +66,4 @@ function findOption(list, value, isItemEqualToValue) {
66
66
  if (hit !== void 0 && isSameValue(itemValue(hit), value, isItemEqualToValue)) return hit;
67
67
  return list.options.find((option) => isSameValue(itemValue(option), value, isItemEqualToValue));
68
68
  }
69
- //#endregion
70
69
  export { findOption, isItemGroup, isItemObject, isSameValue, itemLabel, itemValue, toItemList, valueKey };
@@ -1,4 +1,3 @@
1
- //#region src/lib/schema-defaults.d.ts
2
1
  type SchemaNode = {
3
2
  readonly _zod?: {
4
3
  readonly def?: SchemaDef;
@@ -28,5 +27,4 @@ declare function findZodSchemaAt<T>(schema: SchemaNode, keys: readonly PropertyK
28
27
  } | undefined;
29
28
  declare function pickSchemaShape<T>(schema: SchemaNode, value: T): T;
30
29
  declare function deepMergeDefaults(schemaDefaults: Record<string, unknown>, userDefaults: Record<string, unknown> | undefined): Record<string, unknown>;
31
- //#endregion
32
30
  export { deepMergeDefaults, extractZodDefaultAt, extractZodDefaults, findZodSchemaAt, pickSchemaShape };
@@ -1,4 +1,4 @@
1
- //#region src/lib/schema-defaults.ts
1
+ // src/lib/schema-defaults.ts
2
2
  const MAX_WRAPPER_DEPTH = 16;
3
3
  function getDef(node) {
4
4
  return node?._zod?.def;
@@ -178,5 +178,4 @@ function deepMergeDefaults(schemaDefaults, userDefaults) {
178
178
  }
179
179
  return result;
180
180
  }
181
- //#endregion
182
181
  export { deepMergeDefaults, extractZodDefaultAt, extractZodDefaults, findZodSchemaAt, pickSchemaShape };
@@ -1,4 +1,3 @@
1
- //#region src/lib/schema-required.d.ts
2
1
  type PathPart = PropertyKey | {
3
2
  readonly key: PropertyKey;
4
3
  };
@@ -22,5 +21,4 @@ type StandardSchemaLike = {
22
21
  declare function toRequiredPathKey(name: string): string;
23
22
  declare function validateSync(schema: StandardSchemaLike, value: unknown): readonly StandardIssue[] | undefined;
24
23
  declare function getRequiredPaths(schema: StandardSchemaLike): Set<string>;
25
- //#endregion
26
24
  export { type StandardSchemaLike, getRequiredPaths, toRequiredPathKey, validateSync };
@@ -1,7 +1,7 @@
1
- //#region src/lib/schema-required.ts
1
+ // src/lib/schema-required.ts
2
2
  const MAX_PROBE_DEPTH = 16;
3
- const INDEX_SEGMENT = /^\d+$/;
4
- const INDEX_IN_NAME = /\[\d+\]|\.(\d+)(?=\.|\[|$)/g;
3
+ const INDEX_SEGMENT = /^\d+$/u;
4
+ const INDEX_IN_NAME = /\[\d+\]|\.\d+(?=\.|\[|$)/gu;
5
5
  function pathKey(part) {
6
6
  if (typeof part === "object" && part !== null && "key" in part) return part.key;
7
7
  return part;
@@ -134,5 +134,4 @@ function getRequiredPaths(schema) {
134
134
  }
135
135
  return required;
136
136
  }
137
- //#endregion
138
137
  export { getRequiredPaths, toRequiredPathKey, validateSync };
@@ -1,6 +1,5 @@
1
1
  import { ActionError, ActionIssue, ActionResult } from "./action-result.mjs";
2
2
  import { AnyFormApi } from "@tanstack/react-form";
3
- //#region src/lib/submit-action.d.ts
4
3
  declare function applyActionError(formApi: FormApiLike, error: ActionError): void;
5
4
  type SchemaResult<TOutput> = {
6
5
  readonly value: TOutput;
@@ -66,5 +65,4 @@ type SubmitActionSchemaOptions<TValues, TOutput, TData> = SubmitActionOptions<TV
66
65
  */
67
66
  declare function submitAction<TValues, TOutput, TData>(props: SubmitProps<TValues>, action: Action<TOutput, TData>, options: SubmitActionSchemaOptions<TValues, TOutput, TData>): Promise<ActionResult<TData>>;
68
67
  declare function submitAction<TValues, TData>(props: SubmitProps<TValues>, action: Action<NoInfer<TValues>, TData>, options?: SubmitActionOptions<TValues, TData>): Promise<ActionResult<TData>>;
69
- //#endregion
70
68
  export { type FormApiLike, type SubmitActionOptions, type SubmitActionSchema, applyActionError, submitAction };
@@ -3,7 +3,7 @@ import { toActionError } from "./action-result.mjs";
3
3
  import { pickSchemaShape } from "./schema-defaults.mjs";
4
4
  import { getFormSchema } from "./validation-modes.mjs";
5
5
  import { ValidationError } from "./validation-error.mjs";
6
- //#region src/lib/submit-action.ts
6
+ // src/lib/submit-action.ts
7
7
  function applyActionError(formApi, error) {
8
8
  const fields = {};
9
9
  const formErrors = [...error.formErrors];
@@ -46,5 +46,4 @@ function toFormValues(formApi, data) {
46
46
  const schema = getFormSchema(formApi);
47
47
  return schema ? pickSchemaShape(schema, data) : data;
48
48
  }
49
- //#endregion
50
49
  export { applyActionError, submitAction };
@@ -1,4 +1,3 @@
1
- //#region src/lib/submit-error.d.ts
2
1
  type SubmitErrorState = {
3
2
  readonly submissionAttempts: number;
4
3
  readonly values: unknown;
@@ -32,5 +31,4 @@ declare function setSubmitError(form: SubmitErrorForm, error: unknown): void;
32
31
  declare function reportSubmitError(error: unknown): void;
33
32
  /** The error `form`'s last submit threw, until it went stale. */
34
33
  declare function useSubmitError(form: SubmitErrorForm): SubmitError | undefined;
35
- //#endregion
36
34
  export { type SubmitError, type SubmitErrorForm, reportSubmitError, setSubmitError, useSubmitError };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { useCallback, useSyncExternalStore } from "react";
3
- //#region src/lib/submit-error.ts
3
+ // src/lib/submit-error.ts
4
4
  const entries = /* @__PURE__ */ new WeakMap();
5
5
  const listeners = /* @__PURE__ */ new WeakMap();
6
6
  function notify(key) {
@@ -62,5 +62,4 @@ function useSubmitError(form) {
62
62
  }, [key]);
63
63
  return useSyncExternalStore(subscribe, () => entries.get(key)?.submitError, () => void 0);
64
64
  }
65
- //#endregion
66
65
  export { reportSubmitError, setSubmitError, useSubmitError };
@@ -1,6 +1,5 @@
1
1
  import { AnyFieldMeta } from "@tanstack/react-form";
2
2
  import { ReactNode } from "react";
3
- //#region src/lib/use-field-control.d.ts
4
3
  type UseFieldControlOptions = {
5
4
  /**
6
5
  * The description the field renders, if any. The control is described by
@@ -79,5 +78,4 @@ type FieldControl<TValue = unknown> = {
79
78
  * state its label, description and error need.
80
79
  */
81
80
  declare function useFieldControl<TValue = unknown>({ description, disabled, label, required }?: UseFieldControlOptions): FieldControl<TValue>;
82
- //#endregion
83
81
  export { type FieldControl, type FieldControlProps, type UseFieldControlOptions, useFieldControl };
@@ -6,7 +6,7 @@ import { useIsFormDisabled } from "./form-disabled.mjs";
6
6
  import { describedBy } from "./aria.mjs";
7
7
  import { useHideFieldErrors, useIsFieldRequired, useIsFieldRequiredBySchema, useIsInvalid } from "./use-is-invalid.mjs";
8
8
  import { useCallback, useId } from "react";
9
- //#region src/lib/use-field-control.ts
9
+ // src/lib/use-field-control.ts
10
10
  function isEmpty(value) {
11
11
  return value === null || value === void 0 || value === "" || Number.isNaN(value) || Array.isArray(value) && value.length === 0;
12
12
  }
@@ -66,5 +66,4 @@ function useFieldControl({ description, disabled, label, required } = {}) {
66
66
  showRequiredIndicator: required ?? schemaRequired
67
67
  };
68
68
  }
69
- //#endregion
70
69
  export { useFieldControl };
@@ -1,4 +1,3 @@
1
- //#region src/lib/use-form-dialog.d.ts
2
1
  type FormDialogOpenOptions<TValues> = {
3
2
  /**
4
3
  * Values to edit in this session. Omit to start from the hook's
@@ -84,5 +83,4 @@ declare function useLeaveGuard({ hasUnsavedChanges, onSave }: UseLeaveGuardOptio
84
83
  * fields' ids come from `useId`), else that root's first focusable element.
85
84
  */
86
85
  declare function findFieldElement(root: ParentNode | null | undefined, name: string): HTMLElement | null;
87
- //#endregion
88
86
  export { type FormDialogController, type FormDialogOpenOptions, type LeaveGuard, type UseFormDialogOptions, type UseLeaveGuardOptions, findFieldElement, useFormDialog, useLeaveGuard };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { useCallback, useRef, useState } from "react";
3
- //#region src/lib/use-form-dialog.ts
3
+ // src/lib/use-form-dialog.ts
4
4
  /**
5
5
  * Open/close state for a dialog-hosted form, one "session" per `open()`.
6
6
  *
@@ -91,7 +91,7 @@ function useLeaveGuard({ hasUnsavedChanges, onSave }) {
91
91
  }
92
92
  const FOCUSABLE = "input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
93
93
  function attributeSelector(attribute, value) {
94
- return `[${attribute}="${value.replace(/["\\]/g, "\\$&")}"]`;
94
+ return `[${attribute}="${value.replace(/["\\]/gu, "\\$&")}"]`;
95
95
  }
96
96
  /**
97
97
  * Find the element to focus for field `name` inside `root`: the control with
@@ -108,5 +108,4 @@ function findFieldElement(root, name) {
108
108
  const labelFor = fieldRoot.querySelector("label[for]")?.getAttribute("for");
109
109
  return (labelFor ? fieldRoot.querySelector(attributeSelector("id", labelFor)) : null) ?? fieldRoot.querySelector(FOCUSABLE);
110
110
  }
111
- //#endregion
112
111
  export { findFieldElement, useFormDialog, useLeaveGuard };
@@ -1,5 +1,4 @@
1
1
  import { ChangeEvent, ClipboardEvent } from "react";
2
- //#region src/lib/use-formatted-number.d.ts
3
2
  type UseFormattedNumberOptions = {
4
3
  /** The stored value. `null`/`undefined` renders an empty input. */
5
4
  value: number | null | undefined;
@@ -57,5 +56,4 @@ type FormattedNumberInputProps = {
57
56
  * can use it.
58
57
  */
59
58
  declare function useFormattedNumber({ value, onValueChange, onBlur, locale, maximumFractionDigits, padFraction, useGrouping, allowNegative, maxIntegerDigits, min, max, toDisplay, fromDisplay }: UseFormattedNumberOptions): FormattedNumberInputProps;
60
- //#endregion
61
59
  export { type FormattedNumberInputProps, type UseFormattedNumberOptions, useFormattedNumber };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { formatNumber, mapCaret, parseLocaleNumber, parseTypedNumber, roundTo } from "./formatted-number.mjs";
3
3
  import { useLayoutEffect, useReducer, useRef, useState } from "react";
4
- //#region src/lib/use-formatted-number.ts
4
+ // src/lib/use-formatted-number.ts
5
5
  const identity = (value) => value;
6
6
  function clamp(value, min, max) {
7
7
  let next = value;
@@ -100,5 +100,4 @@ function useFormattedNumber({ value, onValueChange, onBlur, locale, maximumFract
100
100
  value: text
101
101
  };
102
102
  }
103
- //#endregion
104
103
  export { useFormattedNumber };
@@ -1,8 +1,6 @@
1
1
  import { AnyFieldApi } from "@tanstack/react-form";
2
- //#region src/lib/use-is-invalid.d.ts
3
2
  declare function useIsInvalid(field: AnyFieldApi): boolean;
4
3
  declare function useHideFieldErrors(field: AnyFieldApi): boolean;
5
4
  declare function useIsFieldRequired(field: AnyFieldApi): boolean;
6
5
  declare function useIsFieldRequiredBySchema(field: AnyFieldApi): boolean;
7
- //#endregion
8
6
  export { useHideFieldErrors, useIsFieldRequired, useIsFieldRequiredBySchema, useIsInvalid };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { getFormHideFieldErrors, getFormValidationMode, isFieldRequired, isFieldRequiredBySchema } from "./validation-modes.mjs";
3
3
  import { useSelector } from "@tanstack/react-form";
4
- //#region src/lib/use-is-invalid.ts
4
+ // src/lib/use-is-invalid.ts
5
5
  function hasFieldLevelError(errorMap, errorSourceMap, cause) {
6
6
  if (!(errorMap && errorSourceMap)) return false;
7
7
  return errorSourceMap[cause] === "field" && errorMap[cause] !== void 0;
@@ -30,5 +30,4 @@ function useIsFieldRequired(field) {
30
30
  function useIsFieldRequiredBySchema(field) {
31
31
  return isFieldRequiredBySchema(field.form, field.name);
32
32
  }
33
- //#endregion
34
33
  export { useHideFieldErrors, useIsFieldRequired, useIsFieldRequiredBySchema, useIsInvalid };
@@ -1,10 +1,8 @@
1
1
  import { AnyFormApi } from "@tanstack/react-form";
2
- //#region src/lib/use-rebased-default-values.d.ts
3
2
  declare function useRebasedDefaultValues<T>(callerDefaults: T): {
4
3
  defaultValues: T;
5
4
  onMount: (props: {
6
5
  formApi: AnyFormApi;
7
6
  }) => void;
8
7
  };
9
- //#endregion
10
8
  export { useRebasedDefaultValues };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { evaluate } from "@tanstack/react-form";
3
3
  import { useCallback, useLayoutEffect, useRef } from "react";
4
- //#region src/lib/use-rebased-default-values.ts
4
+ // src/lib/use-rebased-default-values.ts
5
5
  function useRebasedDefaultValues(callerDefaults) {
6
6
  const liveForm = useRef(null);
7
7
  const lastCallerDefaults = useRef(callerDefaults);
@@ -18,5 +18,4 @@ function useRebasedDefaultValues(callerDefaults) {
18
18
  onMount
19
19
  };
20
20
  }
21
- //#endregion
22
21
  export { useRebasedDefaultValues };
@@ -1,6 +1,4 @@
1
1
  import { AnyFormApi } from "@tanstack/react-form";
2
- //#region src/lib/use-unsaved-changes-warning.d.ts
3
2
  type UnsavedChangesMode = "if-changed" | "if-touched";
4
3
  declare function useUnsavedChangesWarning(form: AnyFormApi, enabled: boolean | UnsavedChangesMode): void;
5
- //#endregion
6
4
  export { type UnsavedChangesMode, useUnsavedChangesWarning };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useSelector } from "@tanstack/react-form";
3
3
  import { useEffect } from "react";
4
- //#region src/lib/use-unsaved-changes-warning.ts
4
+ // src/lib/use-unsaved-changes-warning.ts
5
5
  function useUnsavedChangesWarning(form, enabled) {
6
6
  let mode;
7
7
  if (enabled === false) mode = null;
@@ -20,5 +20,4 @@ function useUnsavedChangesWarning(form, enabled) {
20
20
  return () => window.removeEventListener("beforeunload", handler);
21
21
  }, [active]);
22
22
  }
23
- //#endregion
24
23
  export { useUnsavedChangesWarning };
@@ -1,4 +1,3 @@
1
- //#region src/lib/validation-error.d.ts
2
1
  type FieldMessage = string | readonly string[];
3
2
  declare class ValidationError extends Error {
4
3
  readonly fields: Readonly<Record<string, FieldMessage>>;
@@ -8,5 +7,4 @@ declare class ValidationError extends Error {
8
7
  message?: string;
9
8
  });
10
9
  }
11
- //#endregion
12
10
  export { type FieldMessage, ValidationError };
@@ -1,4 +1,4 @@
1
- //#region src/lib/validation-error.ts
1
+ // src/lib/validation-error.ts
2
2
  var ValidationError = class extends Error {
3
3
  constructor(fields, options = {}) {
4
4
  super(options.message ?? "Validation failed");
@@ -7,5 +7,4 @@ var ValidationError = class extends Error {
7
7
  this.formError = options.formError;
8
8
  }
9
9
  };
10
- //#endregion
11
10
  export { ValidationError };
@@ -1,5 +1,3 @@
1
1
  import { ValidationLogicFn } from "@tanstack/react-form";
2
- //#region src/lib/validation-logic.d.ts
3
2
  declare const blurThenChangeLogic: ValidationLogicFn;
4
- //#endregion
5
3
  export { blurThenChangeLogic };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- //#region src/lib/validation-logic.ts
2
+ // src/lib/validation-logic.ts
3
3
  function pickLiveValidator(validators, isAsync) {
4
4
  const fn = isAsync ? validators?.onChangeAsync ?? validators?.onBlurAsync : validators?.onChange ?? validators?.onBlur;
5
5
  return fn ? {
@@ -110,5 +110,4 @@ function blurThenChangeLogicImpl(props) {
110
110
  });
111
111
  }
112
112
  const blurThenChangeLogic = blurThenChangeLogicImpl;
113
- //#endregion
114
113
  export { blurThenChangeLogic };
@@ -1,6 +1,5 @@
1
1
  import { StandardSchemaLike } from "./schema-required.mjs";
2
2
  import { AnyFormApi } from "@tanstack/react-form";
3
- //#region src/lib/validation-modes.d.ts
4
3
  type ValidationMode = "change" | "blur" | "submit" | "blur-then-change";
5
4
  declare const DEFAULT_VALIDATION_MODE: ValidationMode;
6
5
  type ZenoFormState = {
@@ -16,5 +15,4 @@ declare function getFormHideFieldErrors(form: AnyFormApi): boolean;
16
15
  declare function getFormSchema(form: AnyFormApi): StandardSchemaLike | undefined;
17
16
  declare function isFieldRequiredBySchema(form: AnyFormApi, name: string): boolean;
18
17
  declare function isFieldRequired(form: AnyFormApi, name: string): boolean;
19
- //#endregion
20
18
  export { DEFAULT_VALIDATION_MODE, type ValidationMode, getFormHideFieldErrors, getFormSchema, getFormValidationMode, isFieldRequired, isFieldRequiredBySchema, setFormZenoState };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { toRequiredPathKey } from "./schema-required.mjs";
3
- //#region src/lib/validation-modes.ts
3
+ // src/lib/validation-modes.ts
4
4
  const DEFAULT_VALIDATION_MODE = "blur-then-change";
5
5
  const DEFAULT_FORM_STATE = {
6
6
  hideFieldErrors: false,
@@ -39,5 +39,4 @@ function isFieldRequiredBySchema(form, name) {
39
39
  function isFieldRequired(form, name) {
40
40
  return getFormZenoState(form).requiredIndicator && isFieldRequiredBySchema(form, name);
41
41
  }
42
- //#endregion
43
42
  export { DEFAULT_VALIDATION_MODE, getFormHideFieldErrors, getFormSchema, getFormValidationMode, isFieldRequired, isFieldRequiredBySchema, setFormZenoState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeno-lib/forms",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "repository": {
5
5
  "url": "https://github.com/zeno-lib/zeno.git",
6
6
  "directory": "packages/forms"
@@ -33,18 +33,18 @@
33
33
  "@tanstack/react-form": "1.33.5"
34
34
  },
35
35
  "devDependencies": {
36
- "@types/node": "26.6.4",
36
+ "@types/node": "26.6.5",
37
37
  "@types/react": "19.3.0",
38
38
  "@types/react-dom": "19.3.0",
39
- "@zeno-lib/test": "^0.1.1",
39
+ "@zeno-lib/test": "^0.1.2",
40
40
  "@zeno-lib/typescript": "^1.2.0",
41
41
  "@zeno-lib/ui": "^0.0.1",
42
- "lucide-react": "^1.52.0",
43
- "tsdown": "^0.23.0",
42
+ "lucide-react": "^1.62.0",
43
+ "tsdown": "^0.23.1",
44
44
  "typescript": "7.0.2",
45
- "vite": "8.3.3",
46
- "vitest": "5.0.3",
47
- "zod": "^4.6.5"
45
+ "vite": "8.3.5",
46
+ "vitest": "5.0.5",
47
+ "zod": "^4.6.7"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@tanstack/react-devtools": ">=0.10",
@@ -11,9 +11,9 @@ import {
11
11
  withForm,
12
12
  } from "./create-form"
13
13
 
14
- const STREET_LABEL = /Street/
15
- const CITY_LABEL = /City/
16
- const BILLING_STREET_LABEL = /Billing street/
14
+ const STREET_LABEL = /Street/u
15
+ const CITY_LABEL = /City/u
16
+ const BILLING_STREET_LABEL = /Billing street/u
17
17
 
18
18
  afterEach(() => {
19
19
  cleanup()
@@ -9,7 +9,7 @@ afterEach(() => {
9
9
  cleanup()
10
10
  })
11
11
 
12
- const PHONE = /Phone/
12
+ const PHONE = /Phone/u
13
13
  const SUBMIT = { name: "Submit" }
14
14
 
15
15
  // A control a `<label htmlFor>` can't name: only `aria-labelledby` does.
@@ -9,9 +9,9 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
9
9
  import { Form, FormProvider, useForm } from "./create-form"
10
10
  import type { DateValueType } from "./lib/date-value"
11
11
 
12
- const DAY_LABEL = /Day/
13
- const OCTOBER_9_SELECTED = /October 9th, 2026, selected/
14
- const OCTOBER_10 = /October 10th, 2026/
12
+ const DAY_LABEL = /Day/u
13
+ const OCTOBER_9_SELECTED = /October 9th, 2026, selected/u
14
+ const OCTOBER_10 = /October 10th, 2026/u
15
15
 
16
16
  afterEach(() => {
17
17
  cleanup()
@@ -20,9 +20,9 @@ import type { ActionResult } from "./lib/action-result"
20
20
  import { submitAction } from "./lib/submit-action"
21
21
  import { type FormDialogController, useFormDialog } from "./lib/use-form-dialog"
22
22
 
23
- const NAME = /Name/
24
- const EMAIL = /Email/
25
- const INNER_NAME = /Inner name/
23
+ const NAME = /Name/u
24
+ const EMAIL = /Email/u
25
+ const INNER_NAME = /Inner name/u
26
26
 
27
27
  const animatable = HTMLElement.prototype as {
28
28
  getAnimations?: () => unknown[]
@@ -17,7 +17,7 @@ afterEach(() => {
17
17
  vi.unstubAllGlobals()
18
18
  })
19
19
 
20
- const NAME = /Name/
20
+ const NAME = /Name/u
21
21
  const SUBMIT = { name: "Submit" }
22
22
 
23
23
  const schema = z.object({ name: z.string().min(1, "Required") })
@@ -11,9 +11,9 @@ import { z } from "zod"
11
11
  import { Form, FormProvider, useForm } from "./create-form"
12
12
  import { getNumberSeparators } from "./lib/formatted-number"
13
13
 
14
- const AMOUNT = /Amount/
15
- const RATE = /Rate/
16
- const YEAR = /Year/
14
+ const AMOUNT = /Amount/u
15
+ const RATE = /Rate/u
16
+ const YEAR = /Year/u
17
17
  const CH = getNumberSeparators("de-CH").group
18
18
 
19
19
  afterEach(() => {
@@ -9,7 +9,8 @@
9
9
  */
10
10
  type DateValueType = "date" | "iso-date" | "iso-datetime"
11
11
 
12
- const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/
12
+ // biome-ignore lint/style/useNamedCaptureGroup: named groups need ES2018, past the ES6 target
13
+ const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/u
13
14
 
14
15
  function pad(value: number, length: number): string {
15
16
  return String(value).padStart(length, "0")
@@ -12,8 +12,8 @@ import { getFormSchema } from "./validation-modes"
12
12
  // accepts none of them is required. A cleared field holds its empty value,
13
13
  // and so does an untouched one whose schema rejects `""` or `[]`.
14
14
 
15
- const NAME_SEGMENT = /[^.[\]]+/g
16
- const INDEX = /^\d+$/
15
+ const NAME_SEGMENT = /[^.[\]]+/gu
16
+ const INDEX = /^\d+$/u
17
17
 
18
18
  // `members[2].name` (or `members.2.name`) → `["members", 2, "name"]`.
19
19
  function toPathKeys(name: string): PropertyKey[] {
@@ -43,15 +43,15 @@ const separatorCache = new Map<string, NumberSeparators>()
43
43
 
44
44
  // Minus signs a user may type or paste: ASCII hyphen-minus, U+2212 (what Intl
45
45
  // emits for some locales), and the en dash.
46
- const MINUS_SIGNS = /[-−–]/
47
- const LEADING_MINUS = /^[^\d]*[-−–]/
48
- const NON_DIGIT = /\D/g
49
- const LEADING_ZEROS = /^0+(?=\d)/
50
- const DIGIT = /\d/
51
- const DIGITS = /\d/g
52
- const PARENTHESIZED = /^\s*\(.*\)\s*$/
53
- const NOT_NUMERIC = /[^\d.,]/g
54
- const UNICODE_MINUS = /\u2212/g
46
+ const MINUS_SIGNS = /[-−–]/u
47
+ const LEADING_MINUS = /^[^\d]*[-−–]/u
48
+ const NON_DIGIT = /\D/gu
49
+ const LEADING_ZEROS = /^0+(?=\d)/u
50
+ const DIGIT = /\d/u
51
+ const DIGITS = /\d/gu
52
+ const PARENTHESIZED = /^\s*\(.*\)\s*$/u
53
+ const NOT_NUMERIC = /[^\d.,]/gu
54
+ const UNICODE_MINUS = /\u2212/gu
55
55
 
56
56
  function getNumberSeparators(locale?: string): NumberSeparators {
57
57
  const key = locale ?? ""
@@ -53,10 +53,10 @@ type StandardSchemaLike = {
53
53
  // Each level can take two passes: the empty value, then the descent.
54
54
  const MAX_PROBE_DEPTH = 16
55
55
 
56
- const INDEX_SEGMENT = /^\d+$/
56
+ const INDEX_SEGMENT = /^\d+$/u
57
57
 
58
58
  // Matches `[3]` and `.3` index segments in a field name.
59
- const INDEX_IN_NAME = /\[\d+\]|\.(\d+)(?=\.|\[|$)/g
59
+ const INDEX_IN_NAME = /\[\d+\]|\.\d+(?=\.|\[|$)/gu
60
60
 
61
61
  function pathKey(part: PathPart): PropertyKey {
62
62
  // biome-ignore lint/suspicious/noUnnecessaryConditions: issue paths come from third-party validators at runtime, and a non-conforming `null` segment would make `in` throw.
@@ -199,7 +199,7 @@ const FOCUSABLE =
199
199
  'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])'
200
200
 
201
201
  function attributeSelector(attribute: string, value: string): string {
202
- return `[${attribute}="${value.replace(/["\\]/g, "\\$&")}"]`
202
+ return `[${attribute}="${value.replace(/["\\]/gu, "\\$&")}"]`
203
203
  }
204
204
 
205
205
  /**
@@ -3,8 +3,8 @@ import userEvent from "@zeno-lib/test/user-event"
3
3
  import { afterEach, describe, expect, test, vi } from "vitest"
4
4
  import { Form, FormProvider, useForm } from "./create-form"
5
5
 
6
- const TAGS = /Tags/
7
- const CLEAR = /Clear/
6
+ const TAGS = /Tags/u
7
+ const CLEAR = /Clear/u
8
8
 
9
9
  afterEach(() => {
10
10
  cleanup()
@@ -14,10 +14,10 @@ afterEach(() => {
14
14
  })
15
15
 
16
16
  const SUBMIT = { name: "Submit" }
17
- const EMAIL_LABEL = /Email/
18
- const NAME_LABEL = /Name/
19
- const OWNER_1_SHARE_LABEL = /^Owner 1 share/
20
- const OWNER_2_SHARE_LABEL = /^Owner 2 share/
17
+ const EMAIL_LABEL = /Email/u
18
+ const NAME_LABEL = /Name/u
19
+ const OWNER_1_SHARE_LABEL = /^Owner 1 share/u
20
+ const OWNER_2_SHARE_LABEL = /^Owner 2 share/u
21
21
 
22
22
  function failure(
23
23
  fieldErrors: Record<string, string[]>,
@@ -12,16 +12,16 @@ import { z } from "zod"
12
12
  import { SelectItem } from "@/components/ui/select"
13
13
  import { Form, FormProvider, RadioGroupFieldItem, useForm } from "./create-form"
14
14
 
15
- const CONTACT_EMAIL_LABEL = /Contact email/
16
- const PASSWORD_LABEL = /Password/
17
- const NAME_LABEL = /Name/
18
- const ACCEPT_NAME = /Accept/
19
- const TITLE_LABEL = /Title/
20
- const NOTE_LABEL = /Note/
21
- const DAY_LABEL = /Day/
22
- const PICKED_DAY = /October 9/
23
- const ROLE_LABEL = /Role/
24
- const OWNER_LABEL = /Owner/
15
+ const CONTACT_EMAIL_LABEL = /Contact email/u
16
+ const PASSWORD_LABEL = /Password/u
17
+ const NAME_LABEL = /Name/u
18
+ const ACCEPT_NAME = /Accept/u
19
+ const TITLE_LABEL = /Title/u
20
+ const NOTE_LABEL = /Note/u
21
+ const DAY_LABEL = /Day/u
22
+ const PICKED_DAY = /October 9/u
23
+ const ROLE_LABEL = /Role/u
24
+ const OWNER_LABEL = /Owner/u
25
25
  const PICK_A_ROLE = "Pick a role"
26
26
  const PICK_A_DAY = "Pick a day"
27
27
 
@@ -4,8 +4,8 @@ import { afterEach, describe, expect, test, vi } from "vitest"
4
4
  import { z } from "zod"
5
5
  import { Form, FormProvider, useForm } from "./create-form"
6
6
 
7
- const DIET_LABEL = /Diet/
8
- const SAUSAGE_LABEL = /Sausage/
7
+ const DIET_LABEL = /Diet/u
8
+ const SAUSAGE_LABEL = /Sausage/u
9
9
 
10
10
  afterEach(() => {
11
11
  cleanup()
@@ -14,8 +14,8 @@ afterEach(() => {
14
14
  cleanup()
15
15
  })
16
16
 
17
- const TITLE = /Title/
18
- const SAVE = /Save/
17
+ const TITLE = /Title/u
18
+ const SAVE = /Save/u
19
19
 
20
20
  const schema = z.object({ title: z.string().min(1) })
21
21
 
@@ -6,9 +6,9 @@ import { z } from "zod"
6
6
  import { Form, FormProvider, useForm } from "./create-form"
7
7
  import { ValidationError } from "./lib/validation-error"
8
8
 
9
- const EMAIL_LABEL = /Email/
10
- const VALID_EMAIL_MSG = /Enter a valid email/i
11
- const SUBMIT_NAME = /Submit/
9
+ const EMAIL_LABEL = /Email/u
10
+ const VALID_EMAIL_MSG = /Enter a valid email/iu
11
+ const SUBMIT_NAME = /Submit/u
12
12
 
13
13
  // @testing-library auto-cleanup only runs with `globals: true`. Our vitest
14
14
  // config keeps globals off (we import test helpers explicitly), so add the
@@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
4
4
  import { z } from "zod"
5
5
  import { Form, FormProvider, useForm } from "./create-form"
6
6
 
7
- const NAME_LABEL = /Name/
7
+ const NAME_LABEL = /Name/u
8
8
 
9
9
  let addSpy: ReturnType<typeof vi.spyOn>
10
10
  let removeSpy: ReturnType<typeof vi.spyOn>
@@ -30,8 +30,8 @@ function Harness({ mode }: { mode?: ValidationMode }) {
30
30
  )
31
31
  }
32
32
 
33
- const ERROR = /Enter a valid email/i
34
- const EMAIL_LABEL = /Email/
33
+ const ERROR = /Enter a valid email/iu
34
+ const EMAIL_LABEL = /Email/u
35
35
 
36
36
  function getInput() {
37
37
  return screen.getByLabelText(EMAIL_LABEL) as HTMLInputElement
@@ -4,7 +4,7 @@ import { afterEach, expect, test, vi } from "vitest"
4
4
  import { z } from "zod"
5
5
  import { Form, FormProvider, useForm, withForm } from "./create-form"
6
6
 
7
- const CITY = /City/
7
+ const CITY = /City/u
8
8
 
9
9
  afterEach(() => {
10
10
  cleanup()