@goodie-forms/react 1.2.6-alpha → 1.3.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.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ <!-- Logo -->
2
+ <p align="center">
3
+ <img src="https://raw.githubusercontent.com/iGoodie/goodie-forms/master/.github/assets/logo.svg" height="200px" alt="Logo"/>
4
+ </p>
5
+
6
+ <!-- Slogan -->
7
+ <p align="center">
8
+ An unopinionated modern form state and data management library
9
+ </p>
10
+ <!-- Badges -->
11
+ <p align="center">
12
+
13
+ <!-- Main Badges -->
14
+ <img src="https://raw.githubusercontent.com/iGoodie/paper-editor/master/.github/assets/main-badge.svg" height="20px"/>
15
+ <a href="https://www.npmjs.com/package/@goodie-forms/react">
16
+ <img src="https://img.shields.io/npm/v/@goodie-forms/react"/>
17
+ </a>
18
+ <a href="https://github.com/iGoodie/goodie-forms/tags">
19
+ <img src="https://img.shields.io/github/v/tag/iGoodie/goodie-forms"/>
20
+ </a>
21
+ <a href="https://github.com/iGoodie/goodie-forms">
22
+ <img src="https://img.shields.io/github/languages/top/iGoodie/goodie-forms"/>
23
+ </a>
24
+
25
+ <br/>
26
+
27
+ <!-- Github Badges -->
28
+ <img src="https://raw.githubusercontent.com/iGoodie/paper-editor/master/.github/assets/github-badge.svg" height="20px"/>
29
+ <a href="https://github.com/iGoodie/goodie-forms/commits/master">
30
+ <img src="https://img.shields.io/github/last-commit/iGoodie/goodie-forms"/>
31
+ </a>
32
+ <a href="https://github.com/iGoodie/goodie-forms/issues">
33
+ <img src="https://img.shields.io/github/issues/iGoodie/goodie-forms"/>
34
+ </a>
35
+ <a href="https://github.com/iGoodie/goodie-forms/tree/master/src">
36
+ <img src="https://img.shields.io/github/languages/code-size/iGoodie/goodie-forms"/>
37
+ </a>
38
+
39
+ <br/>
40
+
41
+ <!-- Support Badges -->
42
+ <img src="https://raw.githubusercontent.com/iGoodie/paper-editor/master/.github/assets/support-badge.svg" height="20px"/>
43
+ <a href="https://discord.gg/KNxxdvN">
44
+ <img src="https://img.shields.io/discord/610497509437210624?label=discord"/>
45
+ </a>
46
+ <a href="https://www.patreon.com/iGoodie">
47
+ <img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DiGoodie%26type%3Dpatrons"/>
48
+ </a>
49
+ </p>
50
+
51
+ # Description
52
+
53
+ React package of Goodie Forms.
54
+
55
+ # Quick Example
56
+
57
+ ```tsx
58
+ import { useForm } from "@goodie-forms/react";
59
+ import z from "zod";
60
+
61
+ // 1. Define a schema for your form data
62
+ const userRegisterSchema = z.object({
63
+ name: z.string().min(1, "Name is required"),
64
+ email: z.string().email("Invalid email address"),
65
+ password: z.string().min(6, "Password must be at least 6 characters"),
66
+ });
67
+
68
+ export function App() {
69
+ // 2. Create a form with the schema
70
+ const form = useForm(
71
+ {
72
+ validationSchema: userRegisterSchema,
73
+ },
74
+ {
75
+ validationMode: "onBlur",
76
+ revalidationMode: "onChange",
77
+ },
78
+ );
79
+
80
+ // 3. Create a submit handler
81
+ const handleSubmit = form.createSubmitHandler(async (data) => {
82
+ console.log("Form submitted successfully with data:", data);
83
+ });
84
+
85
+ return (
86
+ // 4. Bind submit handler to the form element
87
+ <form onClick={handleSubmit}>
88
+ {/* 5. Render fields */}
89
+ <FieldRenderer
90
+ form={form}
91
+ path={form.path.of("name")}
92
+ defaultValue=""
93
+ render={({ fieldProps, field, form }) => (
94
+ <div>
95
+ <input
96
+ {...fieldProps}
97
+ type="text"
98
+ disabled={form.controller.isSubmitting}
99
+ />
100
+ {field.issues.length > 0 && (
101
+ <div className="issues">
102
+ {field.issues.map((issue) => (
103
+ <p key={issue.id}>{issue.message}</p>
104
+ ))}
105
+ </div>
106
+ )}
107
+ </div>
108
+ )}
109
+ />
110
+
111
+ {/* 5. Render fields */}
112
+ <FieldRenderer
113
+ form={form}
114
+ path={form.path.of("email")}
115
+ defaultValue=""
116
+ render={({ fieldProps, field }) => (
117
+ <div>
118
+ <input
119
+ {...fieldProps}
120
+ type="email"
121
+ disabled={form.controller.isSubmitting}
122
+ />
123
+ {field.issues.length > 0 && (
124
+ <div className="issues">
125
+ {field.issues.map((issue) => (
126
+ <p key={issue.id}>{issue.message}</p>
127
+ ))}
128
+ </div>
129
+ )}
130
+ </div>
131
+ )}
132
+ />
133
+
134
+ {/* 5. Render fields */}
135
+ <FieldRenderer
136
+ form={form}
137
+ path={form.path.of("password")}
138
+ defaultValue=""
139
+ render={({ fieldProps, field }) => (
140
+ <div>
141
+ <input
142
+ {...fieldProps}
143
+ type="password"
144
+ disabled={form.controller.isSubmitting}
145
+ />
146
+ {field.issues.length > 0 && (
147
+ <div className="issues">
148
+ {field.issues.map((issue) => (
149
+ <p key={issue.id}>{issue.message}</p>
150
+ ))}
151
+ </div>
152
+ )}
153
+ </div>
154
+ )}
155
+ />
156
+
157
+ <button type="submit">Submit</button>
158
+ </form>
159
+ );
160
+ }
161
+ ```
162
+
163
+ ## License
164
+
165
+ &copy; 2026 Taha Anılcan Metinyurt (iGoodie)
166
+
167
+ For any part of this work for which the license is applicable, this work is licensed under the [Attribution-ShareAlike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/) license. (See LICENSE).
168
+
169
+ <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a>
@@ -13,18 +13,13 @@ export interface RenderParams<TOutput extends object, TValue> {
13
13
  field: FormField<TOutput, TValue>;
14
14
  form: UseForm<TOutput>;
15
15
  }
16
- type DefaultValueProps<TValue> = undefined extends TValue ? {
17
- defaultValue?: Suppliable<TValue>;
18
- } : {
19
- defaultValue: Suppliable<TValue>;
20
- };
21
- export type FieldRendererProps<TOutput extends object, TPath extends FieldPath.Segments> = DefaultValueProps<FieldPath.Resolve<TOutput, TPath>> & {
16
+ export interface FieldRendererProps<TOutput extends object, TPath extends FieldPath.Segments> {
22
17
  form: UseForm<TOutput>;
23
18
  path: TPath;
19
+ defaultValue?: Suppliable<FieldPath.Resolve<TOutput, TPath>>;
24
20
  overrideInitialValue?: boolean;
25
- unbindOnUnmount?: boolean;
21
+ unregisterOnUnmount?: boolean;
26
22
  render: (params: RenderParams<TOutput, FieldPath.Resolve<TOutput, TPath>>) => ReactNode;
27
- };
23
+ }
28
24
  export declare function FieldRenderer<TOutput extends object, const TPath extends FieldPath.Segments>(props: FieldRendererProps<TOutput, TPath>): import("react/jsx-runtime").JSX.Element;
29
- export {};
30
25
  //# sourceMappingURL=FieldRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FieldRenderer.d.ts","sourceRoot":"","sources":["../../src/components/FieldRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,SAAS,EACT,SAAS,EACT,UAAU,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAqB,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3C,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM;IAC1D,UAAU,EAAE;QACV,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QAErB,IAAI,EAAE,MAAM,CAAC;QAEb,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;QAExC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;QAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;QACpB,MAAM,EAAE,MAAM,IAAI,CAAC;KACpB,CAAC;IAEF,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAElC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,KAAK,iBAAiB,CAAC,MAAM,IAAI,SAAS,SAAS,MAAM,GACrD;IAAE,YAAY,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,GACrC;IAAE,YAAY,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAEzC,MAAM,MAAM,kBAAkB,CAC5B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,IAC9B,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG;IACzD,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,CACN,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,KAC7D,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,aAAa,CAC3B,OAAO,SAAS,MAAM,EACtB,KAAK,CAAC,KAAK,SAAS,SAAS,CAAC,QAAQ,EACtC,KAAK,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,2CAqF1C"}
1
+ {"version":3,"file":"FieldRenderer.d.ts","sourceRoot":"","sources":["../../src/components/FieldRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,SAAS,EACT,SAAS,EACT,UAAU,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAqB,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3C,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM;IAC1D,UAAU,EAAE;QACV,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QAErB,IAAI,EAAE,MAAM,CAAC;QAEb,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;QAExC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;QAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;QACpB,MAAM,EAAE,MAAM,IAAI,CAAC;KACpB,CAAC;IAEF,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAElC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB,CACjC,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ;IAEhC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,YAAY,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC7D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CACN,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,KAC7D,SAAS,CAAC;CAChB;AAED,wBAAgB,aAAa,CAC3B,OAAO,SAAS,MAAM,EACtB,KAAK,CAAC,KAAK,SAAS,SAAS,CAAC,QAAQ,EACtC,KAAK,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,2CAqF1C"}
@@ -2,15 +2,11 @@ import { FormController } from '../../../core/src';
2
2
  export declare function useForm<TOutput extends object>(formConfigs: FormController.Configs<TOutput>, hookConfigs?: {
3
3
  validateMode?: "onChange" | "onBlur" | "onSubmit";
4
4
  revalidateMode?: "onChange" | "onBlur" | "onSubmit";
5
- watchIssues?: boolean;
6
- watchValues?: boolean;
7
5
  }): {
8
6
  formConfigs: FormController.Configs<TOutput>;
9
7
  hookConfigs: {
10
8
  validateMode?: "onChange" | "onBlur" | "onSubmit";
11
9
  revalidateMode?: "onChange" | "onBlur" | "onSubmit";
12
- watchIssues?: boolean;
13
- watchValues?: boolean;
14
10
  } | undefined;
15
11
  controller: FormController<TOutput>;
16
12
  path: import('../../../core/src').FieldPathBuilder<TOutput>;
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../src/hooks/useForm.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,wBAAgB,OAAO,CAAC,OAAO,SAAS,MAAM,EAC5C,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,EAC5C,WAAW,CAAC,EAAE;IACZ,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;IAClD,cAAc,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;;;uBAJgB,UAAU,GAAG,QAAQ,GAAG,UAAU;yBAChC,UAAU,GAAG,QAAQ,GAAG,UAAU;sBACrC,OAAO;sBACP,OAAO;;;;;;iBA0CA,CAAC,4RACX,CAAC,aACD,WAAW;;;;;;;;;;;;;UAAsC,CAAC,MAAM,CAAC;EAqBvE;AAED,MAAM,MAAM,OAAO,CAAC,OAAO,SAAS,MAAM,IAAI,UAAU,CACtD,OAAO,OAAO,CAAC,OAAO,CAAC,CACxB,CAAC"}
1
+ {"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../src/hooks/useForm.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,wBAAgB,OAAO,CAAC,OAAO,SAAS,MAAM,EAC5C,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,EAC5C,WAAW,CAAC,EAAE;IACZ,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;IAClD,cAAc,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;CACrD;;;uBAFgB,UAAU,GAAG,QAAQ,GAAG,UAAU;yBAChC,UAAU,GAAG,QAAQ,GAAG,UAAU;;;;;;iBAgC9B,CAAC,4RACX,CAAC,aACD,WAAW;;;;;;;;;;;;;UAAsC,CAAC,MAAM,CAAC;EAqBvE;AAED,MAAM,MAAM,OAAO,CAAC,OAAO,SAAS,MAAM,IAAI,UAAU,CACtD,OAAO,OAAO,CAAC,OAAO,CAAC,CACxB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { FieldPath, FormField } from '../../../core/src';
2
2
  import { UseForm } from '../hooks/useForm';
3
3
  export declare function useFormField<TOutput extends object, TPath extends FieldPath.Segments>(form: UseForm<TOutput>, path: TPath): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>> | undefined;
4
- export declare function useFormField<TOutput extends object, TPath extends FieldPath.Segments>(form: UseForm<TOutput>, path: TPath, bindingConfig: Parameters<typeof form.controller.registerField<TPath>>[1]): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>>;
4
+ export declare function useFormField<TOutput extends object, TPath extends FieldPath.Segments>(form: UseForm<TOutput>, path: TPath, registerConfig: Parameters<typeof form.controller.registerField<TPath>>[1]): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>>;
5
5
  //# sourceMappingURL=useFormField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFormField.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,wBAAgB,YAAY,CAC1B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,EAEhC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,IAAI,EAAE,KAAK,GACV,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;AAErE,wBAAgB,YAAY,CAC1B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,EAEhC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,IAAI,EAAE,KAAK,EACX,aAAa,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GACxE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"useFormField.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3C,wBAAgB,YAAY,CAC1B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,EAEhC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,IAAI,EAAE,KAAK,GACV,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;AAErE,wBAAgB,YAAY,CAC1B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,EAEhC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,IAAI,EAAE,KAAK,EACX,cAAc,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GACzE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,5 @@ export * from './hooks/useForm';
2
2
  export * from './hooks/useFormField';
3
3
  export * from './hooks/useFieldValue';
4
4
  export * from './hooks/useFieldIssues';
5
- export * from './hooks/useRenderControl';
6
5
  export * from './components/FieldRenderer';
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -1,164 +1,141 @@
1
- import { FormController as q, FieldPath as o } from "@goodie-forms/core";
2
- import { useRef as m, useCallback as f, useSyncExternalStore as a, useState as F, startTransition as I, useEffect as V } from "react";
3
- import { jsx as R, Fragment as U } from "react/jsx-runtime";
4
- function b(n, t) {
5
- const e = m(0), s = f(
6
- (l) => n(() => {
7
- e.current++, l();
1
+ import { FormController as V, FieldPath as i } from "@goodie-forms/core";
2
+ import { useRef as v, useCallback as d, useSyncExternalStore as f, useState as g, useEffect as m } from "react";
3
+ import { jsx as U, Fragment as I } from "react/jsx-runtime";
4
+ function a(u, n) {
5
+ const e = v(0), l = d(
6
+ (r) => u(() => {
7
+ e.current++, r();
8
8
  }),
9
- [n]
10
- ), u = f(() => e.current, []);
11
- return a(s, u, u), t();
9
+ [u]
10
+ ), o = d(() => e.current, []);
11
+ return f(l, o, o), n();
12
12
  }
13
- function v(...n) {
14
- return () => {
15
- for (const t of n)
16
- t();
17
- };
18
- }
19
- function E(n, t) {
20
- const [e] = F(() => new q(n)), s = f(
21
- (r) => {
22
- const d = () => {
23
- };
24
- return v(
25
- e.events.on("submissionStatusChange", r),
26
- t?.watchIssues ? e.events.on("fieldIssuesUpdated", r) : d,
27
- t?.watchValues ? e.events.on("fieldValueChanged", r) : d
28
- );
29
- },
30
- [e, t?.watchIssues, t?.watchValues]
13
+ function M(u, n) {
14
+ const [e] = g(() => new V(u)), l = d(
15
+ (t) => e.events.on("submissionStatusChange", t),
16
+ [e]
31
17
  );
32
- b(s, () => e);
33
- const u = () => a(
34
- (r) => e.events.on("fieldValueChanged", r),
18
+ a(l, () => e);
19
+ const o = () => f(
20
+ (t) => e.events.on("fieldValueChanged", t),
35
21
  () => e.data,
36
22
  () => e.data
37
- ), l = () => a(
38
- (r) => e.events.on("fieldIssuesUpdated", r),
23
+ ), r = () => f(
24
+ (t) => e.events.on("fieldIssuesUpdated", t),
39
25
  () => e.issues,
40
26
  () => e.issues
41
- ), i = (r, d) => b(
42
- (c) => e.events.on(r, (...g) => {
43
- d?.(...g), c();
27
+ ), s = (t, F) => a(
28
+ (b) => e.events.on(t, (...q) => {
29
+ F?.(...q), b();
44
30
  }),
45
31
  () => {
46
32
  }
47
33
  );
48
34
  return {
49
- formConfigs: n,
50
- hookConfigs: t,
35
+ formConfigs: u,
36
+ hookConfigs: n,
51
37
  controller: e,
52
38
  path: e.path,
53
- watchValues: u,
54
- watchIssues: l,
55
- watchEvent: i
39
+ watchValues: o,
40
+ watchIssues: r,
41
+ watchEvent: s
42
+ };
43
+ }
44
+ function c(...u) {
45
+ return () => {
46
+ for (const n of u)
47
+ n();
56
48
  };
57
49
  }
58
- function w(n, t, e) {
59
- const { controller: s } = n, u = m(0);
60
- F(() => (!s.getField(t) && e && s.registerField(t, e), null));
61
- const l = f(
50
+ function R(u, n, e) {
51
+ const { controller: l } = u;
52
+ g(() => (!l.getField(n) && e && l.registerField(n, e), null));
53
+ const o = d(
62
54
  (r) => {
63
- const { events: d } = s;
64
- return v(
65
- d.on("fieldRegistered", (c) => {
66
- o.equals(c, t) && (u.current++, r());
55
+ const { events: s } = l;
56
+ return c(
57
+ s.on("fieldRegistered", (t) => {
58
+ i.equals(t, n) && r();
67
59
  }),
68
- d.on("fieldUnregistered", (c) => {
69
- o.equals(c, t) && (u.current++, r());
60
+ s.on("fieldUnregistered", (t) => {
61
+ i.equals(t, n) && r();
70
62
  }),
71
- d.on("fieldValueChanged", (c) => {
72
- (o.equals(c, t) || o.isDescendant(c, t)) && (u.current++, r());
63
+ s.on("fieldValueChanged", (t) => {
64
+ (i.equals(t, n) || i.isDescendant(t, n)) && r();
73
65
  }),
74
- d.on("fieldTouchUpdated", (c) => {
75
- o.equals(c, t) && (u.current++, r());
66
+ s.on("fieldTouchUpdated", (t) => {
67
+ i.equals(t, n) && r();
76
68
  }),
77
- d.on("fieldDirtyUpdated", (c) => {
78
- o.equals(c, t) && (u.current++, r());
69
+ s.on("fieldDirtyUpdated", (t) => {
70
+ i.equals(t, n) && r();
79
71
  }),
80
- d.on("fieldIssuesUpdated", (c) => {
81
- o.equals(c, t) && (u.current++, r());
72
+ s.on("fieldIssuesUpdated", (t) => {
73
+ i.equals(t, n) && r();
82
74
  })
83
75
  );
84
76
  },
85
- [s, t]
86
- ), i = f(() => u.current, [s, t]);
87
- return a(l, i, i), s.getField(t);
77
+ [l, n]
78
+ );
79
+ return a(o, () => l.getField(n));
88
80
  }
89
- function h(n, t) {
90
- const { controller: e } = n, s = f(
91
- (l) => {
92
- const { events: i } = e;
93
- return v(
94
- i.on("fieldRegistered", (r) => {
95
- o.equals(t, r) && l();
81
+ function S(u, n) {
82
+ const { controller: e } = u, l = d(
83
+ (r) => {
84
+ const { events: s } = e;
85
+ return c(
86
+ s.on("fieldRegistered", (t) => {
87
+ i.equals(n, t) && r();
96
88
  }),
97
- i.on("fieldUnregistered", (r) => {
98
- o.equals(t, r) && l();
89
+ s.on("fieldUnregistered", (t) => {
90
+ i.equals(n, t) && r();
99
91
  }),
100
- i.on("fieldValueChanged", (r) => {
101
- (o.equals(r, t) || o.isDescendant(r, t)) && l();
92
+ s.on("fieldValueChanged", (t) => {
93
+ (i.equals(t, n) || i.isDescendant(t, n)) && r();
102
94
  })
103
95
  );
104
96
  },
105
- [e, t]
106
- ), u = f(() => e.getField(t)?.value, [e, t]);
107
- return a(s, u, u);
97
+ [e, n]
98
+ ), o = d(() => e.getField(n)?.value, [e, n]);
99
+ return f(l, o, o);
108
100
  }
109
- function D(n, t) {
110
- const { controller: e } = n, s = f(
111
- (l) => {
112
- const { events: i } = e;
113
- return v(
114
- i.on("fieldRegistered", (r) => {
115
- o.equals(t, r) && l();
101
+ function k(u, n) {
102
+ const { controller: e } = u, l = d(
103
+ (r) => {
104
+ const { events: s } = e;
105
+ return c(
106
+ s.on("fieldRegistered", (t) => {
107
+ i.equals(n, t) && r();
116
108
  }),
117
- i.on("fieldUnregistered", (r) => {
118
- o.equals(t, r) && l();
109
+ s.on("fieldUnregistered", (t) => {
110
+ i.equals(n, t) && r();
119
111
  }),
120
- i.on("fieldIssuesUpdated", (r) => {
121
- o.equals(t, r) && l();
112
+ s.on("fieldIssuesUpdated", (t) => {
113
+ i.equals(n, t) && r();
122
114
  })
123
115
  );
124
116
  },
125
- [e, t]
126
- ), u = f(() => e.getField(t)?.issues ?? [], [e, t]);
127
- return a(s, u, u);
128
- }
129
- function W() {
130
- const [, n] = F(0), t = m(0), e = m(!1);
131
- t.current++;
132
- const s = () => {
133
- e.current || (e.current = !0, queueMicrotask(() => {
134
- I(() => {
135
- n((u) => u + 1);
136
- }), e.current = !1;
137
- }));
138
- };
139
- return {
140
- renderCount: t.current,
141
- forceRerender: s
142
- };
117
+ [e, n]
118
+ ), o = d(() => e.getField(n)?.issues ?? [], [e, n]);
119
+ return f(l, o, o);
143
120
  }
144
- function S(n) {
145
- const t = m(null), e = w(n.form, n.path, {
146
- overrideInitialValue: n.overrideInitialValue ?? !0,
147
- defaultValue: n.defaultValue
148
- }), s = n.form.controller.triedSubmitting ? n.form.hookConfigs?.revalidateMode ?? n.form.hookConfigs?.validateMode : n.form.hookConfigs?.validateMode, u = n.render({
121
+ function w(u) {
122
+ const n = v(null), e = R(u.form, u.path, {
123
+ overrideInitialValue: u.overrideInitialValue ?? !0,
124
+ defaultValue: u.defaultValue
125
+ }), l = u.form.controller.triedSubmitting ? u.form.hookConfigs?.revalidateMode ?? u.form.hookConfigs?.validateMode : u.form.hookConfigs?.validateMode, o = u.render({
149
126
  fieldProps: {
150
- ref: t,
127
+ ref: n,
151
128
  name: e.stringPath,
152
129
  value: e.value,
153
- onChange(l) {
154
- let i;
155
- if (typeof l == "object" && "target" in l) {
156
- const { target: r } = l;
157
- if (r !== e.boundElement || !("value" in r) || typeof r.value != "string") return;
158
- i = r.value;
130
+ onChange(r) {
131
+ let s;
132
+ if (typeof r == "object" && "target" in r) {
133
+ const { target: t } = r;
134
+ if (t !== e.boundElement || !("value" in t) || typeof t.value != "string") return;
135
+ s = t.value;
159
136
  } else
160
- i = l;
161
- e.setValue(i, {
137
+ s = r;
138
+ e.setValue(s, {
162
139
  shouldTouch: !0,
163
140
  shouldMarkDirty: !0
164
141
  });
@@ -167,29 +144,28 @@ function S(n) {
167
144
  e.touch();
168
145
  },
169
146
  onBlur() {
170
- (e.issues.length !== 0 || s === "onBlur" || s === "onChange") && n.form.controller.validateField(n.path);
147
+ (e.issues.length !== 0 || l === "onBlur" || l === "onChange") && u.form.controller.validateField(u.path);
171
148
  }
172
149
  },
173
150
  field: e,
174
- form: n.form
151
+ form: u.form
175
152
  });
176
- return V(() => {
177
- const { events: l } = n.form.controller;
178
- return v(
179
- l.on("fieldValueChanged", (i) => {
180
- !o.equals(i, n.path) && !o.isDescendant(i, n.path) || (e.issues.length !== 0 || s === "onChange") && n.form.controller.validateField(n.path);
153
+ return m(() => {
154
+ const { events: r } = u.form.controller;
155
+ return c(
156
+ r.on("fieldValueChanged", (s) => {
157
+ !i.equals(s, u.path) && !i.isDescendant(s, u.path) || (e.issues.length !== 0 || l === "onChange") && u.form.controller.validateField(u.path);
181
158
  })
182
159
  );
183
- }, [s]), V(() => (e.bindElement(t.current), () => {
184
- n.unbindOnUnmount && n.form.controller.unregisterField(n.path);
185
- }), []), /* @__PURE__ */ R(U, { children: u });
160
+ }, [l]), m(() => (e.bindElement(n.current), () => {
161
+ u.unregisterOnUnmount && u.form.controller.unregisterField(u.path);
162
+ }), []), /* @__PURE__ */ U(I, { children: o });
186
163
  }
187
164
  export {
188
- S as FieldRenderer,
189
- D as useFieldIssues,
190
- h as useFieldValue,
191
- E as useForm,
192
- w as useFormField,
193
- W as useRenderControl
165
+ w as FieldRenderer,
166
+ k as useFieldIssues,
167
+ S as useFieldValue,
168
+ M as useForm,
169
+ R as useFormField
194
170
  };
195
171
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/hooks/useSyncMutableStore.ts","../src/utils/composeFns.ts","../src/hooks/useForm.tsx","../src/hooks/useFormField.tsx","../src/hooks/useFieldValue.ts","../src/hooks/useFieldIssues.ts","../src/hooks/useRenderControl.tsx","../src/components/FieldRenderer.tsx"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from \"react\";\r\n\r\nexport function useSyncMutableStore<T>(\r\n subscribe: (onVersionChange: () => void) => () => void,\r\n getValue: () => T,\r\n) {\r\n const versionRef = useRef(0);\r\n\r\n const subscribeWithVersion = useCallback(\r\n (onStoreChange: () => void) => {\r\n return subscribe(() => {\r\n versionRef.current++;\r\n onStoreChange();\r\n });\r\n },\r\n [subscribe],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return versionRef.current;\r\n }, []);\r\n\r\n useSyncExternalStore(subscribeWithVersion, getSnapshot, getSnapshot);\r\n\r\n return getValue();\r\n}\r\n","export function composeFns<TFns extends (() => void)[]>(...fns: TFns) {\r\n return () => {\r\n for (const fn of fns) {\r\n fn();\r\n }\r\n };\r\n}\r\n","import { FormController } from \"@goodie-forms/core\";\r\nimport { useCallback, useState, useSyncExternalStore } from \"react\";\r\nimport { useSyncMutableStore } from \"../hooks/useSyncMutableStore\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useForm<TOutput extends object>(\r\n formConfigs: FormController.Configs<TOutput>,\r\n hookConfigs?: {\r\n validateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n revalidateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n watchIssues?: boolean;\r\n watchValues?: boolean;\r\n },\r\n) {\r\n const [controller] = useState(() => new FormController(formConfigs));\r\n\r\n const subscribe = useCallback(\r\n (onVersionChange: () => void) => {\r\n const noop = () => {};\r\n\r\n return composeFns(\r\n controller.events.on(\"submissionStatusChange\", onVersionChange),\r\n hookConfigs?.watchIssues\r\n ? controller.events.on(\"fieldIssuesUpdated\", onVersionChange)\r\n : noop,\r\n hookConfigs?.watchValues\r\n ? controller.events.on(\"fieldValueChanged\", onVersionChange)\r\n : noop,\r\n );\r\n },\r\n [controller, hookConfigs?.watchIssues, hookConfigs?.watchValues],\r\n );\r\n\r\n useSyncMutableStore(subscribe, () => controller);\r\n\r\n const useWatchValues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldValueChanged\", onStoreChange),\r\n () => controller.data,\r\n () => controller.data,\r\n );\r\n };\r\n\r\n const useWatchIssues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldIssuesUpdated\", onStoreChange),\r\n () => controller.issues,\r\n () => controller.issues,\r\n );\r\n };\r\n\r\n const useWatchEvent = <E extends keyof typeof controller.events.events>(\r\n eventName: E,\r\n listener?: NonNullable<(typeof controller.events.events)[E]>[number],\r\n ) => {\r\n return useSyncMutableStore(\r\n (onVersionChange) =>\r\n controller.events.on(eventName, (...args: any[]) => {\r\n (listener as any)?.(...args);\r\n onVersionChange();\r\n }),\r\n () => undefined,\r\n );\r\n };\r\n\r\n return {\r\n formConfigs,\r\n hookConfigs,\r\n controller,\r\n path: controller.path,\r\n watchValues: useWatchValues,\r\n watchIssues: useWatchIssues,\r\n watchEvent: useWatchEvent,\r\n };\r\n}\r\n\r\nexport type UseForm<TOutput extends object> = ReturnType<\r\n typeof useForm<TOutput>\r\n>;\r\n","import { FieldPath, FormField } from \"@goodie-forms/core\";\r\nimport { useCallback, useRef, useState, useSyncExternalStore } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>> | undefined;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n bindingConfig: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>>;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n bindingConfig?: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n) {\r\n const { controller } = form;\r\n\r\n const version = useRef(0);\r\n\r\n useState(() => {\r\n let existing = controller.getField(path);\r\n\r\n if (!existing && bindingConfig) {\r\n controller.registerField(path, bindingConfig);\r\n }\r\n\r\n return null;\r\n });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldTouchUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldDirtyUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n version.current++;\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => version.current, [controller, path]);\r\n\r\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n\r\n return controller.getField(path);\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFieldValue<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.value;\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\nimport { UseForm } from \"./useForm\";\r\n\r\nexport function useFieldIssues<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.issues ?? [];\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import { startTransition, useRef, useState } from \"react\";\r\n\r\nexport function useRenderControl() {\r\n const [, rerender] = useState(0);\r\n const renderCount = useRef(0);\r\n const renderScheduled = useRef(false);\r\n renderCount.current++;\r\n\r\n const scheduleRerender = () => {\r\n if (renderScheduled.current) return;\r\n renderScheduled.current = true;\r\n\r\n queueMicrotask(() => {\r\n startTransition(() => {\r\n rerender((i) => i + 1);\r\n });\r\n\r\n renderScheduled.current = false;\r\n });\r\n };\r\n\r\n return {\r\n renderCount: renderCount.current,\r\n forceRerender: scheduleRerender,\r\n };\r\n}\r\n","import {\r\n DeepReadonly,\r\n FieldPath,\r\n FormField,\r\n Suppliable,\r\n} from \"@goodie-forms/core\";\r\nimport { ChangeEvent, ReactNode, Ref, useEffect, useRef } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useFormField } from \"../hooks/useFormField\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport interface RenderParams<TOutput extends object, TValue> {\r\n fieldProps: {\r\n ref: Ref<any | null>;\r\n\r\n name: string;\r\n\r\n value: DeepReadonly<TValue> | undefined;\r\n\r\n onChange: (event: ChangeEvent<EventTarget> | TValue) => void;\r\n onFocus: () => void;\r\n onBlur: () => void;\r\n };\r\n\r\n field: FormField<TOutput, TValue>;\r\n\r\n form: UseForm<TOutput>;\r\n}\r\n\r\ntype DefaultValueProps<TValue> = undefined extends TValue\r\n ? { defaultValue?: Suppliable<TValue> }\r\n : { defaultValue: Suppliable<TValue> };\r\n\r\nexport type FieldRendererProps<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n> = DefaultValueProps<FieldPath.Resolve<TOutput, TPath>> & {\r\n form: UseForm<TOutput>;\r\n path: TPath;\r\n overrideInitialValue?: boolean;\r\n unbindOnUnmount?: boolean;\r\n render: (\r\n params: RenderParams<TOutput, FieldPath.Resolve<TOutput, TPath>>,\r\n ) => ReactNode;\r\n};\r\n\r\nexport function FieldRenderer<\r\n TOutput extends object,\r\n const TPath extends FieldPath.Segments,\r\n>(props: FieldRendererProps<TOutput, TPath>) {\r\n type TValue = FieldPath.Resolve<TOutput, TPath>;\r\n\r\n const elementRef = useRef<HTMLElement>(null);\r\n\r\n const field = useFormField(props.form, props.path, {\r\n overrideInitialValue: props.overrideInitialValue ?? true,\r\n defaultValue: props.defaultValue,\r\n })!;\r\n\r\n const currentValidateMode = props.form.controller.triedSubmitting\r\n ? (props.form.hookConfigs?.revalidateMode ??\r\n props.form.hookConfigs?.validateMode)\r\n : props.form.hookConfigs?.validateMode;\r\n\r\n const renderedJsx = props.render({\r\n fieldProps: {\r\n ref: elementRef,\r\n name: field.stringPath,\r\n value: field.value,\r\n onChange(arg) {\r\n let newValue: TValue;\r\n\r\n if (typeof arg === \"object\" && \"target\" in arg) {\r\n const { target } = arg;\r\n if (target !== field.boundElement) return;\r\n if (!(\"value\" in target)) return;\r\n if (typeof target.value !== \"string\") return;\r\n newValue = target.value as TValue;\r\n } else {\r\n newValue = arg;\r\n }\r\n\r\n field.setValue(newValue, {\r\n shouldTouch: true,\r\n shouldMarkDirty: true,\r\n });\r\n },\r\n onFocus() {\r\n field.touch();\r\n },\r\n onBlur() {\r\n if (\r\n field.issues.length !== 0 ||\r\n currentValidateMode === \"onBlur\" ||\r\n currentValidateMode === \"onChange\"\r\n ) {\r\n props.form.controller.validateField(props.path);\r\n }\r\n },\r\n },\r\n field: field as any,\r\n form: props.form,\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = props.form.controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldValueChanged\", (_path) => {\r\n if (\r\n !FieldPath.equals(_path, props.path) &&\r\n !FieldPath.isDescendant(_path, props.path)\r\n ) {\r\n return;\r\n }\r\n\r\n if (field.issues.length !== 0 || currentValidateMode === \"onChange\") {\r\n props.form.controller.validateField(props.path);\r\n }\r\n }),\r\n );\r\n }, [currentValidateMode]);\r\n\r\n useEffect(() => {\r\n field.bindElement(elementRef.current!);\r\n\r\n return () => {\r\n if (props.unbindOnUnmount) {\r\n props.form.controller.unregisterField(props.path);\r\n }\r\n };\r\n }, []);\r\n\r\n return <>{renderedJsx}</>;\r\n}\r\n\r\n/* ---- TESTS ---------------- */\r\n\r\n// function TestComp() {\r\n// const form = useForm<{ a?: { b: 99 } }>({});\r\n\r\n// const jsx = (\r\n// <>\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a.b)}\r\n// defaultValue={() => 99 as const}\r\n// render={({ fieldProps, field }) => {\r\n// // ^?\r\n// return <input {...fieldProps} />;\r\n// }}\r\n// />\r\n\r\n// {/* defaultField olmayabilir, çünkü \"a\" nullable */}\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a)}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromStringPath(\"a.b\")}\r\n// defaultValue={() => 99 as const}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n// </>\r\n// );\r\n// }\r\n"],"names":["useSyncMutableStore","subscribe","getValue","versionRef","useRef","subscribeWithVersion","useCallback","onStoreChange","getSnapshot","useSyncExternalStore","composeFns","fns","fn","useForm","formConfigs","hookConfigs","controller","useState","FormController","onVersionChange","noop","useWatchValues","useWatchIssues","useWatchEvent","eventName","listener","args","useFormField","form","path","bindingConfig","version","events","_path","FieldPath","changedPath","useFieldValue","fieldPath","useFieldIssues","useRenderControl","rerender","renderCount","renderScheduled","scheduleRerender","startTransition","i","FieldRenderer","props","elementRef","field","currentValidateMode","renderedJsx","arg","newValue","target","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/hooks/useSyncMutableStore.ts","../src/hooks/useForm.tsx","../src/utils/composeFns.ts","../src/hooks/useFormField.tsx","../src/hooks/useFieldValue.ts","../src/hooks/useFieldIssues.ts","../src/components/FieldRenderer.tsx"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from \"react\";\r\n\r\nexport function useSyncMutableStore<T>(\r\n subscribe: (onVersionChange: () => void) => () => void,\r\n getValue: () => T,\r\n) {\r\n const versionRef = useRef(0);\r\n\r\n const subscribeWithVersion = useCallback(\r\n (onStoreChange: () => void) => {\r\n return subscribe(() => {\r\n versionRef.current++;\r\n onStoreChange();\r\n });\r\n },\r\n [subscribe],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return versionRef.current;\r\n }, []);\r\n\r\n useSyncExternalStore(subscribeWithVersion, getSnapshot, getSnapshot);\r\n\r\n return getValue();\r\n}\r\n","import { FormController } from \"@goodie-forms/core\";\r\nimport { useCallback, useState, useSyncExternalStore } from \"react\";\r\nimport { useSyncMutableStore } from \"../hooks/useSyncMutableStore\";\r\n\r\nexport function useForm<TOutput extends object>(\r\n formConfigs: FormController.Configs<TOutput>,\r\n hookConfigs?: {\r\n validateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n revalidateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n },\r\n) {\r\n const [controller] = useState(() => new FormController(formConfigs));\r\n\r\n const subscribe = useCallback(\r\n (onVersionChange: () => void) => {\r\n return controller.events.on(\"submissionStatusChange\", onVersionChange);\r\n },\r\n [controller],\r\n );\r\n\r\n useSyncMutableStore(subscribe, () => controller);\r\n\r\n const useWatchValues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldValueChanged\", onStoreChange),\r\n () => controller.data,\r\n () => controller.data,\r\n );\r\n };\r\n\r\n const useWatchIssues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldIssuesUpdated\", onStoreChange),\r\n () => controller.issues,\r\n () => controller.issues,\r\n );\r\n };\r\n\r\n const useWatchEvent = <E extends keyof typeof controller.events.events>(\r\n eventName: E,\r\n listener?: NonNullable<(typeof controller.events.events)[E]>[number],\r\n ) => {\r\n return useSyncMutableStore(\r\n (onVersionChange) =>\r\n controller.events.on(eventName, (...args: any[]) => {\r\n (listener as any)?.(...args);\r\n onVersionChange();\r\n }),\r\n () => undefined,\r\n );\r\n };\r\n\r\n return {\r\n formConfigs,\r\n hookConfigs,\r\n controller,\r\n path: controller.path,\r\n watchValues: useWatchValues,\r\n watchIssues: useWatchIssues,\r\n watchEvent: useWatchEvent,\r\n };\r\n}\r\n\r\nexport type UseForm<TOutput extends object> = ReturnType<\r\n typeof useForm<TOutput>\r\n>;\r\n","export function composeFns<TFns extends (() => void)[]>(...fns: TFns) {\r\n return () => {\r\n for (const fn of fns) {\r\n fn();\r\n }\r\n };\r\n}\r\n","import { FieldPath, FormField } from \"@goodie-forms/core\";\r\nimport { useCallback, useState } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useSyncMutableStore } from \"../hooks/useSyncMutableStore\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>> | undefined;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n registerConfig: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>>;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n registerConfig?: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n) {\r\n const { controller } = form;\r\n\r\n useState(() => {\r\n let existing = controller.getField(path);\r\n\r\n if (!existing && registerConfig) {\r\n controller.registerField(path, registerConfig);\r\n }\r\n\r\n return null;\r\n });\r\n\r\n const subscribe = useCallback(\r\n (onVersionChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldTouchUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldDirtyUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n return useSyncMutableStore(subscribe, () => controller.getField(path));\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFieldValue<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.value;\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\nimport { UseForm } from \"./useForm\";\r\n\r\nexport function useFieldIssues<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.issues ?? [];\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import {\r\n DeepReadonly,\r\n FieldPath,\r\n FormField,\r\n Suppliable,\r\n} from \"@goodie-forms/core\";\r\nimport { ChangeEvent, ReactNode, Ref, useEffect, useRef } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useFormField } from \"../hooks/useFormField\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport interface RenderParams<TOutput extends object, TValue> {\r\n fieldProps: {\r\n ref: Ref<any | null>;\r\n\r\n name: string;\r\n\r\n value: DeepReadonly<TValue> | undefined;\r\n\r\n onChange: (event: ChangeEvent<EventTarget> | TValue) => void;\r\n onFocus: () => void;\r\n onBlur: () => void;\r\n };\r\n\r\n field: FormField<TOutput, TValue>;\r\n\r\n form: UseForm<TOutput>;\r\n}\r\n\r\nexport interface FieldRendererProps<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n> {\r\n form: UseForm<TOutput>;\r\n path: TPath;\r\n defaultValue?: Suppliable<FieldPath.Resolve<TOutput, TPath>>;\r\n overrideInitialValue?: boolean;\r\n unregisterOnUnmount?: boolean;\r\n render: (\r\n params: RenderParams<TOutput, FieldPath.Resolve<TOutput, TPath>>,\r\n ) => ReactNode;\r\n}\r\n\r\nexport function FieldRenderer<\r\n TOutput extends object,\r\n const TPath extends FieldPath.Segments,\r\n>(props: FieldRendererProps<TOutput, TPath>) {\r\n type TValue = FieldPath.Resolve<TOutput, TPath>;\r\n\r\n const elementRef = useRef<HTMLElement>(null);\r\n\r\n const field = useFormField(props.form, props.path, {\r\n overrideInitialValue: props.overrideInitialValue ?? true,\r\n defaultValue: props.defaultValue,\r\n })!;\r\n\r\n const currentValidateMode = props.form.controller.triedSubmitting\r\n ? (props.form.hookConfigs?.revalidateMode ??\r\n props.form.hookConfigs?.validateMode)\r\n : props.form.hookConfigs?.validateMode;\r\n\r\n const renderedJsx = props.render({\r\n fieldProps: {\r\n ref: elementRef,\r\n name: field.stringPath,\r\n value: field.value,\r\n onChange(arg) {\r\n let newValue: TValue;\r\n\r\n if (typeof arg === \"object\" && \"target\" in arg) {\r\n const { target } = arg;\r\n if (target !== field.boundElement) return;\r\n if (!(\"value\" in target)) return;\r\n if (typeof target.value !== \"string\") return;\r\n newValue = target.value as TValue;\r\n } else {\r\n newValue = arg;\r\n }\r\n\r\n field.setValue(newValue, {\r\n shouldTouch: true,\r\n shouldMarkDirty: true,\r\n });\r\n },\r\n onFocus() {\r\n field.touch();\r\n },\r\n onBlur() {\r\n if (\r\n field.issues.length !== 0 ||\r\n currentValidateMode === \"onBlur\" ||\r\n currentValidateMode === \"onChange\"\r\n ) {\r\n props.form.controller.validateField(props.path);\r\n }\r\n },\r\n },\r\n field: field as any,\r\n form: props.form,\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = props.form.controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldValueChanged\", (_path) => {\r\n if (\r\n !FieldPath.equals(_path, props.path) &&\r\n !FieldPath.isDescendant(_path, props.path)\r\n ) {\r\n return;\r\n }\r\n\r\n if (field.issues.length !== 0 || currentValidateMode === \"onChange\") {\r\n props.form.controller.validateField(props.path);\r\n }\r\n }),\r\n );\r\n }, [currentValidateMode]);\r\n\r\n useEffect(() => {\r\n field.bindElement(elementRef.current!);\r\n\r\n return () => {\r\n if (props.unregisterOnUnmount) {\r\n props.form.controller.unregisterField(props.path);\r\n }\r\n };\r\n }, []);\r\n\r\n return <>{renderedJsx}</>;\r\n}\r\n\r\n/* ---- TESTS ---------------- */\r\n\r\n// function TestComp() {\r\n// const form = useForm<{ a?: { b: 99 } }>({});\r\n\r\n// const jsx = (\r\n// <>\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a.b)}\r\n// defaultValue={() => 99 as const}\r\n// render={({ fieldProps, field }) => {\r\n// // ^?\r\n// return <input {...fieldProps} />;\r\n// }}\r\n// />\r\n\r\n// {/* defaultField olmayabilir, çünkü \"a\" nullable */}\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a)}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromStringPath(\"a.b\")}\r\n// defaultValue={() => 99 as const}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n// </>\r\n// );\r\n// }\r\n"],"names":["useSyncMutableStore","subscribe","getValue","versionRef","useRef","subscribeWithVersion","useCallback","onStoreChange","getSnapshot","useSyncExternalStore","useForm","formConfigs","hookConfigs","controller","useState","FormController","onVersionChange","useWatchValues","useWatchIssues","useWatchEvent","eventName","listener","args","composeFns","fns","fn","useFormField","form","path","registerConfig","events","_path","FieldPath","changedPath","useFieldValue","fieldPath","useFieldIssues","FieldRenderer","props","elementRef","field","currentValidateMode","renderedJsx","arg","newValue","target","useEffect"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,16 @@
1
1
  {
2
2
  "name": "@goodie-forms/react",
3
- "version": "1.2.6-alpha",
3
+ "version": "1.3.1",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/iGoodie/goodie-forms"
7
+ },
8
+ "author": {
9
+ "name": "Taha Anılcan Metinyurt",
10
+ "email": "igoodie@programmer.net",
11
+ "url": "https://github.com/iGoodie"
12
+ },
13
+ "license": "CC BY-SA 4.0",
4
14
  "type": "module",
5
15
  "main": "dist/index.js",
6
16
  "types": "dist/index.d.ts",
@@ -21,7 +31,7 @@
21
31
  "react-dom": "^18 || ^19"
22
32
  },
23
33
  "dependencies": {
24
- "@goodie-forms/core": "1.2.6-alpha"
34
+ "@goodie-forms/core": "1.3.1"
25
35
  },
26
36
  "devDependencies": {
27
37
  "@types/react": "^19.2.9",
@@ -1,5 +0,0 @@
1
- export declare function useRenderControl(): {
2
- renderCount: number;
3
- forceRerender: () => void;
4
- };
5
- //# sourceMappingURL=useRenderControl.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useRenderControl.d.ts","sourceRoot":"","sources":["../../src/hooks/useRenderControl.tsx"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB;;;EAuB/B"}