@pindoba/react-tanstack-form 0.0.0-alpha-20260619021129 → 0.0.0-alpha-20260906200353
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 +3 -1
- package/dist/index.js +2 -1
- package/dist/src/FormCheckbox.d.ts +9 -12
- package/dist/src/FormCheckbox.d.ts.map +1 -1
- package/dist/src/FormField.d.ts +5 -10
- package/dist/src/FormField.d.ts.map +1 -1
- package/dist/src/FormField.js.map +1 -1
- package/dist/src/FormInput.d.ts +8 -12
- package/dist/src/FormInput.d.ts.map +1 -1
- package/dist/src/FormSelect.d.ts +11 -14
- package/dist/src/FormSelect.d.ts.map +1 -1
- package/dist/src/FormSubmit.d.ts +9 -12
- package/dist/src/FormSubmit.d.ts.map +1 -1
- package/dist/src/FormSubmit.js.map +1 -1
- package/dist/src/types.d.ts +3 -22
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/useFocusInvalidOnSubmit.d.ts +27 -0
- package/dist/src/useFocusInvalidOnSubmit.d.ts.map +1 -0
- package/dist/src/useFocusInvalidOnSubmit.js +48 -0
- package/dist/src/useFocusInvalidOnSubmit.js.map +1 -0
- package/package.json +20 -20
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { FormInput, FormInputProps } from "./src/FormInput.js";
|
|
|
4
4
|
import { FormSelect, FormSelectProps } from "./src/FormSelect.js";
|
|
5
5
|
import { FormCheckbox, FormCheckboxProps } from "./src/FormCheckbox.js";
|
|
6
6
|
import { FormSubmit, FormSubmitProps } from "./src/FormSubmit.js";
|
|
7
|
+
import { useFocusInvalidOnSubmit } from "./src/useFocusInvalidOnSubmit.js";
|
|
7
8
|
import { getVisibleErrors, normalizeErrors } from "./src/field-errors.js";
|
|
8
9
|
import { useForm, useStore } from "@tanstack/react-form";
|
|
9
|
-
|
|
10
|
+
import { FocusInvalidOptions } from "@pindoba/core-tanstack-form";
|
|
11
|
+
export { type AnyFieldApi, type AnyFormApi, type FocusInvalidOptions, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldErrorMode, type FormFieldOwnProps, type FormFieldProps, FormInput, type FormInputProps, FormSelect, type FormSelectProps, FormSubmit, type FormSubmitProps, getVisibleErrors, normalizeErrors, useFocusInvalidOnSubmit, useForm, useStore };
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@ import { FormInput } from "./src/FormInput.js";
|
|
|
4
4
|
import { FormSelect } from "./src/FormSelect.js";
|
|
5
5
|
import { FormCheckbox } from "./src/FormCheckbox.js";
|
|
6
6
|
import { FormSubmit } from "./src/FormSubmit.js";
|
|
7
|
+
import { useFocusInvalidOnSubmit } from "./src/useFocusInvalidOnSubmit.js";
|
|
7
8
|
import { useForm, useStore } from "@tanstack/react-form";
|
|
8
|
-
export { FormCheckbox, FormField, FormInput, FormSelect, FormSubmit, getVisibleErrors, normalizeErrors, useForm, useStore };
|
|
9
|
+
export { FormCheckbox, FormField, FormInput, FormSelect, FormSubmit, getVisibleErrors, normalizeErrors, useFocusInvalidOnSubmit, useForm, useStore };
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { AnyFormApi, FormFieldOwnProps } from "./types.js";
|
|
2
|
-
import { FieldSize } from "@pindoba/core-field";
|
|
3
2
|
import { JSX, ReactNode } from "react";
|
|
4
|
-
|
|
3
|
+
import { FieldSize } from "@pindoba/core-field";
|
|
5
4
|
//#region src/FormCheckbox.d.ts
|
|
6
5
|
type FormCheckboxProps = Omit<FormFieldOwnProps, "size"> & {
|
|
7
|
-
/** The TanStack form instance returned by `useForm(...)`. */
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/** The TanStack form instance returned by `useForm(...)`. */
|
|
7
|
+
form: AnyFormApi;
|
|
8
|
+
/** Field name (a boolean key of the form's data). */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Inline text/content rendered beside the checkbox. */
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
/** Size variant. Checkbox supports `sm`/`md`/`lg` (no `xs`). */
|
|
10
13
|
size?: Exclude<FieldSize, "xs">;
|
|
11
14
|
};
|
|
12
15
|
/** FormCheckbox — a `<Checkbox>` bound to a boolean TanStack form field. */
|
|
13
|
-
declare function FormCheckbox({
|
|
14
|
-
form,
|
|
15
|
-
name,
|
|
16
|
-
children,
|
|
17
|
-
size,
|
|
18
|
-
...fieldProps
|
|
19
|
-
}: FormCheckboxProps): JSX.Element;
|
|
16
|
+
declare function FormCheckbox({ form, name, children, size, ...fieldProps }: FormCheckboxProps): JSX.Element;
|
|
20
17
|
declare namespace FormCheckbox {
|
|
21
18
|
var displayName: string;
|
|
22
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormCheckbox.d.ts","names":[],"sources":["../../src/FormCheckbox.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormCheckbox.d.ts","names":[],"sources":["../../src/FormCheckbox.tsx"],"mappings":";;;;KAMY,oBAAoB,KAAK;;EAEnC,MAAM;;EAEN;;EAEA,WAAW;;EAEX,OAAO,QAAQ;;;iBAID,eACd,MACA,MACA,UACA,SACG,cACF,oBAAoB,IAAI;kBANX;MAAA"}
|
package/dist/src/FormField.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AnyFieldApi, AnyFormApi, FormFieldOwnProps } from "./types.js";
|
|
2
|
-
import { FieldControlProps } from "@pindoba/core-field";
|
|
3
2
|
import { JSX, ReactNode } from "react";
|
|
4
|
-
|
|
3
|
+
import { FieldControlProps } from "@pindoba/core-field";
|
|
5
4
|
//#region src/FormField.d.ts
|
|
6
5
|
type FormFieldProps = FormFieldOwnProps & {
|
|
7
|
-
/** The TanStack form instance returned by `useForm(...)`. */
|
|
6
|
+
/** The TanStack form instance returned by `useForm(...)`. */
|
|
7
|
+
form: AnyFormApi;
|
|
8
|
+
/** Field name (a key of the form's data). */
|
|
8
9
|
name: string;
|
|
9
10
|
/**
|
|
10
11
|
* Render-prop for the control. Receives the props bag to spread onto the
|
|
@@ -19,13 +20,7 @@ type FormFieldProps = FormFieldOwnProps & {
|
|
|
19
20
|
* control via the `control` render-prop, which receives the Field's `controlProps`
|
|
20
21
|
* bag plus the raw `field` API.
|
|
21
22
|
*/
|
|
22
|
-
declare function FormField({
|
|
23
|
-
form,
|
|
24
|
-
name,
|
|
25
|
-
control,
|
|
26
|
-
errorMode,
|
|
27
|
-
...fieldProps
|
|
28
|
-
}: FormFieldProps): JSX.Element;
|
|
23
|
+
declare function FormField({ form, name, control, errorMode, ...fieldProps }: FormFieldProps): JSX.Element;
|
|
29
24
|
declare namespace FormField {
|
|
30
25
|
var displayName: string;
|
|
31
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.d.ts","names":[],"sources":["../../src/FormField.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormField.d.ts","names":[],"sources":["../../src/FormField.tsx"],"mappings":";;;;KAOY,iBAAiB;;EAE3B,MAAM;;EAEN;;;;;EAKA,UAAU,cAAc,mBAAmB,OAAO,gBAAgB;;;;;;;;;iBAUpD,YACd,MACA,MACA,SACA,cACG,cACF,iBAAiB,IAAI;kBANR;MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.js","names":["TanStackField","Field"],"sources":["../../src/FormField.tsx"],"sourcesContent":["import type { FieldControlProps } from \"@pindoba/core-field\";\nimport Field from \"@pindoba/react-field\";\nimport { Field as TanStackField } from \"@tanstack/react-form\";\nimport type { JSX, ReactNode } from \"react\";\nimport { getVisibleErrors } from \"./field-errors\";\nimport type { AnyFieldApi, AnyFormApi, FormFieldOwnProps } from \"./types\";\n\nexport type FormFieldProps = FormFieldOwnProps & {\n /** The TanStack form instance returned by `useForm(...)`. */\n form: AnyFormApi;\n /** Field name (a key of the form's data). */\n name: string;\n /**\n * Render-prop for the control. Receives the props bag to spread onto the\n * control plus the raw TanStack field API for value/handler binding.\n */\n control: (controlProps: FieldControlProps, field: AnyFieldApi) => ReactNode;\n};\n\n/**\n * FormField. Binds a TanStack React Form field to the presentational `<Field>`\n * wrapper. Errors are surfaced through the shared `getVisibleErrors` helper\n * (same visibility logic as the Svelte/Astro blocks); the consumer supplies the\n * control via the `control` render-prop, which receives the Field's `controlProps`\n * bag plus the raw `field` API.\n */\nexport function FormField({\n form,\n name,\n control,\n errorMode = \"touched\",\n ...fieldProps\n}: FormFieldProps): JSX.Element {\n return (\n <TanStackField form={form} name={name}>\n {(field) => (\n <Field\n {...fieldProps}\n error={getVisibleErrors(field, errorMode)}\n control={(controlProps) => control(controlProps, field)}\n />\n )}\n </TanStackField>\n );\n}\n\nFormField.displayName = \"FormField\";\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,UAAU,EACxB,MACA,MACA,SACA,YAAY,WACZ,GAAG,cAC2B;CAC9B,OACE,oBAACA,OAAD;EAAqB;EAAY;
|
|
1
|
+
{"version":3,"file":"FormField.js","names":["TanStackField","Field"],"sources":["../../src/FormField.tsx"],"sourcesContent":["import type { FieldControlProps } from \"@pindoba/core-field\";\nimport Field from \"@pindoba/react-field\";\nimport { Field as TanStackField } from \"@tanstack/react-form\";\nimport type { JSX, ReactNode } from \"react\";\nimport { getVisibleErrors } from \"./field-errors\";\nimport type { AnyFieldApi, AnyFormApi, FormFieldOwnProps } from \"./types\";\n\nexport type FormFieldProps = FormFieldOwnProps & {\n /** The TanStack form instance returned by `useForm(...)`. */\n form: AnyFormApi;\n /** Field name (a key of the form's data). */\n name: string;\n /**\n * Render-prop for the control. Receives the props bag to spread onto the\n * control plus the raw TanStack field API for value/handler binding.\n */\n control: (controlProps: FieldControlProps, field: AnyFieldApi) => ReactNode;\n};\n\n/**\n * FormField. Binds a TanStack React Form field to the presentational `<Field>`\n * wrapper. Errors are surfaced through the shared `getVisibleErrors` helper\n * (same visibility logic as the Svelte/Astro blocks); the consumer supplies the\n * control via the `control` render-prop, which receives the Field's `controlProps`\n * bag plus the raw `field` API.\n */\nexport function FormField({\n form,\n name,\n control,\n errorMode = \"touched\",\n ...fieldProps\n}: FormFieldProps): JSX.Element {\n return (\n <TanStackField form={form} name={name}>\n {(field) => (\n <Field\n {...fieldProps}\n error={getVisibleErrors(field, errorMode)}\n control={(controlProps) => control(controlProps, field)}\n />\n )}\n </TanStackField>\n );\n}\n\nFormField.displayName = \"FormField\";\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,UAAU,EACxB,MACA,MACA,SACA,YAAY,WACZ,GAAG,cAC2B;CAC9B,OACE,oBAACA,OAAD;EAAqB;EAAY;EAC7B,WAAA,UACA,oBAACC,SAAD;GACE,GAAI;GACJ,OAAO,iBAAiB,OAAO,SAAS;GACxC,UAAU,iBAAiB,QAAQ,cAAc,KAAK;EACvD,CAAA;CAEU,CAAA;AAEnB;AAEA,UAAU,cAAc"}
|
package/dist/src/FormInput.d.ts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { AnyFormApi, FormFieldOwnProps } from "./types.js";
|
|
2
2
|
import { InputProps } from "@pindoba/react-input";
|
|
3
3
|
import { JSX } from "react";
|
|
4
|
-
|
|
5
4
|
//#region src/FormInput.d.ts
|
|
6
5
|
type FormInputProps = FormFieldOwnProps & {
|
|
7
|
-
/** The TanStack form instance returned by `useForm(...)`. */
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/** The TanStack form instance returned by `useForm(...)`. */
|
|
7
|
+
form: AnyFormApi;
|
|
8
|
+
/** Field name (a key of the form's data). */
|
|
9
|
+
name: string;
|
|
10
|
+
/** HTML input type forwarded to the underlying `<Input>`. */
|
|
11
|
+
type?: InputProps["type"];
|
|
12
|
+
/** Placeholder forwarded to the underlying `<Input>`. */
|
|
10
13
|
placeholder?: string;
|
|
11
14
|
};
|
|
12
15
|
/** FormInput — a text `<Input>` bound to a TanStack form field. */
|
|
13
|
-
declare function FormInput({
|
|
14
|
-
form,
|
|
15
|
-
name,
|
|
16
|
-
type,
|
|
17
|
-
placeholder,
|
|
18
|
-
size,
|
|
19
|
-
...fieldProps
|
|
20
|
-
}: FormInputProps): JSX.Element;
|
|
16
|
+
declare function FormInput({ form, name, type, placeholder, size, ...fieldProps }: FormInputProps): JSX.Element;
|
|
21
17
|
declare namespace FormInput {
|
|
22
18
|
var displayName: string;
|
|
23
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormInput.d.ts","names":[],"sources":["../../src/FormInput.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormInput.d.ts","names":[],"sources":["../../src/FormInput.tsx"],"mappings":";;;;KAKY,iBAAiB;;EAE3B,MAAM;;EAEN;;EAEA,OAAO;;EAEP;;;iBAIc,YACd,MACA,MACA,MACA,aACA,SACG,cACF,iBAAiB,IAAI;kBAPR;MAAA"}
|
package/dist/src/FormSelect.d.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { AnyFormApi, FormFieldOwnProps } from "./types.js";
|
|
2
|
-
import { FieldSize } from "@pindoba/core-field";
|
|
3
2
|
import { JSX } from "react";
|
|
3
|
+
import { FieldSize } from "@pindoba/core-field";
|
|
4
4
|
import { ListBoxNodeInput } from "@pindoba/core-listbox";
|
|
5
|
-
|
|
6
5
|
//#region src/FormSelect.d.ts
|
|
7
6
|
type FormSelectProps = Omit<FormFieldOwnProps, "size"> & {
|
|
8
|
-
/** The TanStack form instance returned by `useForm(...)`. */
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/** The TanStack form instance returned by `useForm(...)`. */
|
|
8
|
+
form: AnyFormApi;
|
|
9
|
+
/** Field name (a key of the form's data holding the selected option key). */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Options to choose from. */
|
|
12
|
+
items: ListBoxNodeInput[];
|
|
13
|
+
/** Placeholder shown when nothing is selected. */
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/** Size variant. Select supports `sm`/`md`/`lg` (no `xs`). */
|
|
12
16
|
size?: Exclude<FieldSize, "xs">;
|
|
13
17
|
};
|
|
14
18
|
/** FormSelect — a single-select `<Select>` bound to a TanStack form field. */
|
|
15
|
-
declare function FormSelect({
|
|
16
|
-
form,
|
|
17
|
-
name,
|
|
18
|
-
items,
|
|
19
|
-
placeholder,
|
|
20
|
-
size,
|
|
21
|
-
...fieldProps
|
|
22
|
-
}: FormSelectProps): JSX.Element;
|
|
19
|
+
declare function FormSelect({ form, name, items, placeholder, size, ...fieldProps }: FormSelectProps): JSX.Element;
|
|
23
20
|
declare namespace FormSelect {
|
|
24
21
|
var displayName: string;
|
|
25
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormSelect.d.ts","names":[],"sources":["../../src/FormSelect.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormSelect.d.ts","names":[],"sources":["../../src/FormSelect.tsx"],"mappings":";;;;;KAOY,kBAAkB,KAAK;;EAEjC,MAAM;;EAEN;;EAEA,OAAO;;EAEP;;EAEA,OAAO,QAAQ;;;iBAID,aACd,MACA,MACA,OACA,aACA,SACG,cACF,kBAAkB,IAAI;kBAPT;MAAA"}
|
package/dist/src/FormSubmit.d.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { AnyFormApi } from "./types.js";
|
|
2
2
|
import { ButtonProps } from "@pindoba/react-button";
|
|
3
3
|
import { JSX, ReactNode } from "react";
|
|
4
|
-
|
|
5
4
|
//#region src/FormSubmit.d.ts
|
|
6
5
|
type FormSubmitProps = {
|
|
7
|
-
/** The TanStack form instance returned by `useForm(...)`. */
|
|
6
|
+
/** The TanStack form instance returned by `useForm(...)`. */
|
|
7
|
+
form: AnyFormApi;
|
|
8
8
|
/**
|
|
9
9
|
* Also disable while the form is invalid (not just while submitting). Off by
|
|
10
10
|
* default — the button stays clickable so submitting an invalid form surfaces
|
|
11
11
|
* its errors instead of leaving the user stuck on a disabled control.
|
|
12
12
|
* @default false
|
|
13
13
|
*/
|
|
14
|
-
disableWhenInvalid?: boolean;
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
disableWhenInvalid?: boolean;
|
|
15
|
+
/** Emphasis forwarded to the underlying `<Button>`. */
|
|
16
|
+
emphasis?: ButtonProps["emphasis"];
|
|
17
|
+
/** Size forwarded to the underlying `<Button>`. */
|
|
18
|
+
size?: ButtonProps["size"];
|
|
19
|
+
/** Button label content. Falls back to "Submit". */
|
|
17
20
|
children?: ReactNode;
|
|
18
21
|
};
|
|
19
22
|
/**
|
|
@@ -21,13 +24,7 @@ type FormSubmitProps = {
|
|
|
21
24
|
* the `onSubmit` promise is in flight the label is blurred with a corner spinner
|
|
22
25
|
* (`Loading variant="busy"`) and the button is disabled + `aria-busy`.
|
|
23
26
|
*/
|
|
24
|
-
declare function FormSubmit({
|
|
25
|
-
form,
|
|
26
|
-
disableWhenInvalid,
|
|
27
|
-
emphasis,
|
|
28
|
-
size,
|
|
29
|
-
children
|
|
30
|
-
}: FormSubmitProps): JSX.Element;
|
|
27
|
+
declare function FormSubmit({ form, disableWhenInvalid, emphasis, size, children }: FormSubmitProps): JSX.Element;
|
|
31
28
|
declare namespace FormSubmit {
|
|
32
29
|
var displayName: string;
|
|
33
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormSubmit.d.ts","names":[],"sources":["../../src/FormSubmit.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormSubmit.d.ts","names":[],"sources":["../../src/FormSubmit.tsx"],"mappings":";;;;KAMY;;EAEV,MAAM;;;;;;;EAON;;EAEA,WAAW;;EAEX,OAAO;;EAEP,WAAW;;;;;;;iBAQG,aACd,MACA,oBACA,UACA,MACA,YACC,kBAAkB,IAAI;kBANT;MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormSubmit.js","names":[],"sources":["../../src/FormSubmit.tsx"],"sourcesContent":["import Button, { type ButtonProps } from \"@pindoba/react-button\";\nimport Loading from \"@pindoba/react-loading\";\nimport { useStore } from \"@tanstack/react-form\";\nimport type { JSX, ReactNode } from \"react\";\nimport type { AnyFormApi } from \"./types\";\n\nexport type FormSubmitProps = {\n /** The TanStack form instance returned by `useForm(...)`. */\n form: AnyFormApi;\n /**\n * Also disable while the form is invalid (not just while submitting). Off by\n * default — the button stays clickable so submitting an invalid form surfaces\n * its errors instead of leaving the user stuck on a disabled control.\n * @default false\n */\n disableWhenInvalid?: boolean;\n /** Emphasis forwarded to the underlying `<Button>`. */\n emphasis?: ButtonProps[\"emphasis\"];\n /** Size forwarded to the underlying `<Button>`. */\n size?: ButtonProps[\"size\"];\n /** Button label content. Falls back to \"Submit\". */\n children?: ReactNode;\n};\n\n/**\n * FormSubmit — a submit `<Button>` wired to the form's submit-readiness. While\n * the `onSubmit` promise is in flight the label is blurred with a corner spinner\n * (`Loading variant=\"busy\"`) and the button is disabled + `aria-busy`.\n */\nexport function FormSubmit({\n form,\n disableWhenInvalid = false,\n emphasis = \"primary\",\n size,\n children,\n}: FormSubmitProps): JSX.Element {\n const { canSubmit, isSubmitting } = useStore(form.store, (state) => ({\n canSubmit: state.canSubmit,\n isSubmitting: state.isSubmitting,\n }));\n\n return (\n <Loading loading={isSubmitting} variant=\"busy\" indicator>\n <Button\n type=\"submit\"\n emphasis={emphasis}\n size={size}\n disabled={isSubmitting || (disableWhenInvalid && !canSubmit)}\n aria-busy={isSubmitting ? \"true\" : undefined}\n >\n {children ?? \"Submit\"}\n </Button>\n </Loading>\n );\n}\n\nFormSubmit.displayName = \"FormSubmit\";\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,WAAW,EACzB,MACA,qBAAqB,OACrB,WAAW,WACX,MACA,YAC+B;CAC/B,MAAM,EAAE,WAAW,iBAAiB,SAAS,KAAK,QAAQ,WAAW;EACnE,WAAW,MAAM;EACjB,cAAc,MAAM;CACtB,EAAE;CAEF,OACE,oBAAC,SAAD;EAAS,SAAS;EAAc,SAAQ;EAAO,WAAA;
|
|
1
|
+
{"version":3,"file":"FormSubmit.js","names":[],"sources":["../../src/FormSubmit.tsx"],"sourcesContent":["import Button, { type ButtonProps } from \"@pindoba/react-button\";\nimport Loading from \"@pindoba/react-loading\";\nimport { useStore } from \"@tanstack/react-form\";\nimport type { JSX, ReactNode } from \"react\";\nimport type { AnyFormApi } from \"./types\";\n\nexport type FormSubmitProps = {\n /** The TanStack form instance returned by `useForm(...)`. */\n form: AnyFormApi;\n /**\n * Also disable while the form is invalid (not just while submitting). Off by\n * default — the button stays clickable so submitting an invalid form surfaces\n * its errors instead of leaving the user stuck on a disabled control.\n * @default false\n */\n disableWhenInvalid?: boolean;\n /** Emphasis forwarded to the underlying `<Button>`. */\n emphasis?: ButtonProps[\"emphasis\"];\n /** Size forwarded to the underlying `<Button>`. */\n size?: ButtonProps[\"size\"];\n /** Button label content. Falls back to \"Submit\". */\n children?: ReactNode;\n};\n\n/**\n * FormSubmit — a submit `<Button>` wired to the form's submit-readiness. While\n * the `onSubmit` promise is in flight the label is blurred with a corner spinner\n * (`Loading variant=\"busy\"`) and the button is disabled + `aria-busy`.\n */\nexport function FormSubmit({\n form,\n disableWhenInvalid = false,\n emphasis = \"primary\",\n size,\n children,\n}: FormSubmitProps): JSX.Element {\n const { canSubmit, isSubmitting } = useStore(form.store, (state) => ({\n canSubmit: state.canSubmit,\n isSubmitting: state.isSubmitting,\n }));\n\n return (\n <Loading loading={isSubmitting} variant=\"busy\" indicator>\n <Button\n type=\"submit\"\n emphasis={emphasis}\n size={size}\n disabled={isSubmitting || (disableWhenInvalid && !canSubmit)}\n aria-busy={isSubmitting ? \"true\" : undefined}\n >\n {children ?? \"Submit\"}\n </Button>\n </Loading>\n );\n}\n\nFormSubmit.displayName = \"FormSubmit\";\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,WAAW,EACzB,MACA,qBAAqB,OACrB,WAAW,WACX,MACA,YAC+B;CAC/B,MAAM,EAAE,WAAW,iBAAiB,SAAS,KAAK,QAAQ,WAAW;EACnE,WAAW,MAAM;EACjB,cAAc,MAAM;CACtB,EAAE;CAEF,OACE,oBAAC,SAAD;EAAS,SAAS;EAAc,SAAQ;EAAO,WAAA;EAC7C,UAAA,oBAAC,QAAD;GACE,MAAK;GACK;GACJ;GACN,UAAU,gBAAiB,sBAAsB,CAAC;GAClD,aAAW,eAAe,SAAS,KAAA;GAElC,UAAA,YAAY;EACP,CAAA;CACD,CAAA;AAEb;AAEA,WAAW,cAAc"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import { AnyFieldApi, AnyFormApi } from "@tanstack/react-form";
|
|
2
|
-
import {
|
|
3
|
-
import { FieldFeedback, FieldOrientation, FieldPassThrough, FieldSize } from "@pindoba/core-field";
|
|
2
|
+
import { FormFieldBaseProps, FormFieldErrorMode } from "@pindoba/core-tanstack-form";
|
|
4
3
|
import { ReactNode } from "react";
|
|
5
|
-
|
|
4
|
+
import { FieldPassThrough } from "@pindoba/core-field";
|
|
6
5
|
//#region src/types.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* Field-level presentation props shared by `<FormField>` and every convenience
|
|
9
8
|
* wrapper (`<FormInput>`, `<FormSelect>`, `<FormCheckbox>`). These mirror the
|
|
10
9
|
* presentational `<Field>` props; the adapter supplies `error` from TanStack.
|
|
11
10
|
*/
|
|
12
|
-
interface FormFieldOwnProps {
|
|
13
|
-
/** Label text shown above (or beside) the control. */
|
|
14
|
-
label?: string;
|
|
15
|
-
/** Help text rendered under the control. */
|
|
16
|
-
description?: string;
|
|
17
|
-
/** Renders a required `*` after the label. */
|
|
18
|
-
required?: boolean;
|
|
19
|
-
/** Optional-hint text after the label when not required. */
|
|
20
|
-
optionalText?: string;
|
|
21
|
-
/** Disables the field and forwards `disabled` to the control. */
|
|
22
|
-
disabled?: boolean;
|
|
23
|
-
/** Size variant, kept in sync with the control. */
|
|
24
|
-
size?: FieldSize;
|
|
25
|
-
/** Label/control layout direction. */
|
|
26
|
-
orientation?: FieldOrientation;
|
|
27
|
-
/** Explicit feedback override; defaults to error-derived danger / neutral. */
|
|
28
|
-
feedback?: FieldFeedback;
|
|
11
|
+
interface FormFieldOwnProps extends Omit<FormFieldBaseProps, "form" | "name" | "labelTooltip" | "passThrough"> {
|
|
29
12
|
/** Tooltip attached to an info trigger next to the label. */
|
|
30
13
|
labelTooltip?: ReactNode;
|
|
31
|
-
/** When the field's errors become visible. @default "touched" */
|
|
32
|
-
errorMode?: FormFieldErrorMode$1;
|
|
33
14
|
/** Per-slot escape hatch for styling and attributes. */
|
|
34
15
|
passThrough?: FieldPassThrough;
|
|
35
16
|
}
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"mappings":";;;;;;;;;;UAciB,0BAA0B,KACzC;;EAIA,eAAe;;EAEf,cAAc"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AnyFormApi } from "./types.js";
|
|
2
|
+
import { FocusInvalidOptions } from "@pindoba/core-tanstack-form";
|
|
3
|
+
//#region src/useFocusInvalidOnSubmit.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns a ref to attach to your `<form>` that moves focus to the first
|
|
6
|
+
* invalid control after a failed submit:
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const formRef = useFocusInvalidOnSubmit(form);
|
|
10
|
+
* return (
|
|
11
|
+
* <form ref={formRef} onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>
|
|
12
|
+
* …
|
|
13
|
+
* </form>
|
|
14
|
+
* );
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* It reads the form's submit-attempt / validation state, and once an attempt
|
|
18
|
+
* ends invalid (after async validators settle) focuses the first control marked
|
|
19
|
+
* `aria-invalid="true"` inside the form. The state is read via `useStore`, so
|
|
20
|
+
* the focus effect runs after React has committed the error render and the
|
|
21
|
+
* markers are in the DOM. TanStack Form does no DOM focus itself, so without
|
|
22
|
+
* this a keyboard user is stranded on the submit button (WCAG 3.3.1).
|
|
23
|
+
*/
|
|
24
|
+
declare function useFocusInvalidOnSubmit(form: AnyFormApi, options?: FocusInvalidOptions): React.RefObject<HTMLFormElement | null>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { useFocusInvalidOnSubmit };
|
|
27
|
+
//# sourceMappingURL=useFocusInvalidOnSubmit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocusInvalidOnSubmit.d.ts","names":[],"sources":["../../src/useFocusInvalidOnSubmit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;iBA6BgB,wBACd,MAAM,YACN,UAAU,sBACT,MAAM,UAAU"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useStore } from "@tanstack/react-form";
|
|
2
|
+
import { createSubmitFocusTracker, focusFirstInvalidControl } from "@pindoba/core-tanstack-form";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
//#region src/useFocusInvalidOnSubmit.ts
|
|
5
|
+
/**
|
|
6
|
+
* Returns a ref to attach to your `<form>` that moves focus to the first
|
|
7
|
+
* invalid control after a failed submit:
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const formRef = useFocusInvalidOnSubmit(form);
|
|
11
|
+
* return (
|
|
12
|
+
* <form ref={formRef} onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>
|
|
13
|
+
* …
|
|
14
|
+
* </form>
|
|
15
|
+
* );
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* It reads the form's submit-attempt / validation state, and once an attempt
|
|
19
|
+
* ends invalid (after async validators settle) focuses the first control marked
|
|
20
|
+
* `aria-invalid="true"` inside the form. The state is read via `useStore`, so
|
|
21
|
+
* the focus effect runs after React has committed the error render and the
|
|
22
|
+
* markers are in the DOM. TanStack Form does no DOM focus itself, so without
|
|
23
|
+
* this a keyboard user is stranded on the submit button (WCAG 3.3.1).
|
|
24
|
+
*/
|
|
25
|
+
function useFocusInvalidOnSubmit(form, options) {
|
|
26
|
+
const formRef = useRef(null);
|
|
27
|
+
const submissionAttempts = useStore(form.store, (s) => s.submissionAttempts);
|
|
28
|
+
const isValidating = useStore(form.store, (s) => s.isValidating);
|
|
29
|
+
const isValid = useStore(form.store, (s) => s.isValid);
|
|
30
|
+
const trackerRef = useRef(null);
|
|
31
|
+
if (trackerRef.current === null) trackerRef.current = createSubmitFocusTracker(() => focusFirstInvalidControl(formRef.current, options), submissionAttempts);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
trackerRef.current?.({
|
|
34
|
+
submissionAttempts,
|
|
35
|
+
isValidating,
|
|
36
|
+
isValid
|
|
37
|
+
});
|
|
38
|
+
}, [
|
|
39
|
+
submissionAttempts,
|
|
40
|
+
isValidating,
|
|
41
|
+
isValid
|
|
42
|
+
]);
|
|
43
|
+
return formRef;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { useFocusInvalidOnSubmit };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=useFocusInvalidOnSubmit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocusInvalidOnSubmit.js","names":[],"sources":["../../src/useFocusInvalidOnSubmit.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { useStore } from \"@tanstack/react-form\";\nimport {\n createSubmitFocusTracker,\n focusFirstInvalidControl,\n type FocusInvalidOptions,\n} from \"@pindoba/core-tanstack-form\";\nimport type { AnyFormApi } from \"./types\";\n\n/**\n * Returns a ref to attach to your `<form>` that moves focus to the first\n * invalid control after a failed submit:\n *\n * ```tsx\n * const formRef = useFocusInvalidOnSubmit(form);\n * return (\n * <form ref={formRef} onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>\n * …\n * </form>\n * );\n * ```\n *\n * It reads the form's submit-attempt / validation state, and once an attempt\n * ends invalid (after async validators settle) focuses the first control marked\n * `aria-invalid=\"true\"` inside the form. The state is read via `useStore`, so\n * the focus effect runs after React has committed the error render and the\n * markers are in the DOM. TanStack Form does no DOM focus itself, so without\n * this a keyboard user is stranded on the submit button (WCAG 3.3.1).\n */\nexport function useFocusInvalidOnSubmit(\n form: AnyFormApi,\n options?: FocusInvalidOptions,\n): React.RefObject<HTMLFormElement | null> {\n const formRef = useRef<HTMLFormElement>(null);\n\n const submissionAttempts = useStore(form.store, (s) => s.submissionAttempts);\n const isValidating = useStore(form.store, (s) => s.isValidating);\n const isValid = useStore(form.store, (s) => s.isValid);\n\n // The tracker is stateful (it remembers the last attempt count), so it must\n // survive re-renders. Seed it once with the current attempt count so a form\n // mounted mid-flight doesn't focus spuriously.\n const trackerRef = useRef<\n | ((s: {\n submissionAttempts: number;\n isValidating: boolean;\n isValid: boolean;\n }) => void)\n | null\n >(null);\n if (trackerRef.current === null) {\n trackerRef.current = createSubmitFocusTracker(\n () => focusFirstInvalidControl(formRef.current, options),\n submissionAttempts,\n );\n }\n\n // Runs after commit → aria-invalid markers are in the DOM.\n useEffect(() => {\n trackerRef.current?.({ submissionAttempts, isValidating, isValid });\n }, [submissionAttempts, isValidating, isValid]);\n\n return formRef;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,wBACd,MACA,SACyC;CACzC,MAAM,UAAU,OAAwB,IAAI;CAE5C,MAAM,qBAAqB,SAAS,KAAK,QAAQ,MAAM,EAAE,kBAAkB;CAC3E,MAAM,eAAe,SAAS,KAAK,QAAQ,MAAM,EAAE,YAAY;CAC/D,MAAM,UAAU,SAAS,KAAK,QAAQ,MAAM,EAAE,OAAO;CAKrD,MAAM,aAAa,OAOjB,IAAI;CACN,IAAI,WAAW,YAAY,MACzB,WAAW,UAAU,+BACb,yBAAyB,QAAQ,SAAS,OAAO,GACvD,kBACF;CAIF,gBAAgB;EACd,WAAW,UAAU;GAAE;GAAoB;GAAc;EAAQ,CAAC;CACpE,GAAG;EAAC;EAAoB;EAAc;CAAO,CAAC;CAE9C,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pindoba/react-tanstack-form",
|
|
3
|
-
"version": "0.0.0-alpha-
|
|
3
|
+
"version": "0.0.0-alpha-20260906200353",
|
|
4
4
|
"homepage": "https://github.com/EduardoLopes/pindoba/tree/main/packages/blocks/react/tanstack-form#README.md",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,31 +26,31 @@
|
|
|
26
26
|
"./demos/generic-field": "./src/demos/generic-field.tsx"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@
|
|
30
|
-
"@types/react
|
|
31
|
-
"react": "^19.2.
|
|
32
|
-
"react
|
|
33
|
-
"
|
|
29
|
+
"@pindoba/typescript-config": "0.0.0-alpha-20260320034942",
|
|
30
|
+
"@types/react": "^19.2.18",
|
|
31
|
+
"@types/react-dom": "^19.2.5",
|
|
32
|
+
"react": "^19.2.8",
|
|
33
|
+
"react-dom": "^19.2.8",
|
|
34
|
+
"tsdown": "^0.22.14",
|
|
34
35
|
"typescript": "^6.0.3",
|
|
35
|
-
"zod": "^4.
|
|
36
|
-
"@repo/typescript-config": "0.0.0-alpha-20260320034942"
|
|
36
|
+
"zod": "^4.5.1"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"react": "^18.3.1 || ^19.0.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@pindoba/core-
|
|
44
|
-
"@pindoba/core-
|
|
45
|
-
"@pindoba/
|
|
46
|
-
"@pindoba/react-button": "0.0.0-alpha-
|
|
47
|
-
"@pindoba/react-
|
|
48
|
-
"@pindoba/react-
|
|
49
|
-
"@pindoba/react-
|
|
50
|
-
"@pindoba/
|
|
51
|
-
"@pindoba/react-
|
|
52
|
-
"@pindoba/
|
|
53
|
-
"@
|
|
42
|
+
"@pindoba/core-field": "0.0.0-alpha-20260829223243",
|
|
43
|
+
"@pindoba/core-listbox": "0.0.0-alpha-20260829223243",
|
|
44
|
+
"@pindoba/core-tanstack-form": "0.0.0-alpha-20260829223243",
|
|
45
|
+
"@pindoba/react-affix": "0.0.0-alpha-20260829223243",
|
|
46
|
+
"@pindoba/react-button": "0.0.0-alpha-20260829223243",
|
|
47
|
+
"@pindoba/react-checkbox": "0.0.0-alpha-20260829223243",
|
|
48
|
+
"@pindoba/react-field": "0.0.0-alpha-20260906200353",
|
|
49
|
+
"@pindoba/react-input": "0.0.0-alpha-20260829223243",
|
|
50
|
+
"@pindoba/react-loading": "0.0.0-alpha-20260829223243",
|
|
51
|
+
"@pindoba/react-select": "0.0.0-alpha-20260829223243",
|
|
52
|
+
"@pindoba/styled-system": "0.0.0-alpha-20260829223243",
|
|
53
|
+
"@tanstack/react-form": "^1.33.5"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|