@goodie-forms/react 1.0.0-alpha → 1.1.0-alpha
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/package.json +32 -32
- package/src/components/FieldRenderer.tsx +120 -107
- package/src/hooks/useForm.tsx +46 -51
- package/src/hooks/useFormErrorObserver.ts +40 -38
- package/src/hooks/useFormField.tsx +55 -46
- package/src/hooks/useFormValuesObserver.ts +45 -34
- package/src/hooks/useRenderControl.tsx +26 -12
- package/src/index.ts +7 -7
- package/src/utils/composeFns.ts +7 -7
- package/src/utils/groupBy.ts +13 -13
- package/tsconfig.json +8 -8
- package/vite.config.ts +23 -23
- package/dist/components/FieldRenderer.d.ts +0 -22
- package/dist/components/FieldRenderer.d.ts.map +0 -1
- package/dist/hooks/useForm.d.ts +0 -22
- package/dist/hooks/useForm.d.ts.map +0 -1
- package/dist/hooks/useFormErrorObserver.d.ts +0 -6
- package/dist/hooks/useFormErrorObserver.d.ts.map +0 -1
- package/dist/hooks/useFormField.d.ts +0 -4
- package/dist/hooks/useFormField.d.ts.map +0 -1
- package/dist/hooks/useFormValuesObserver.d.ts +0 -6
- package/dist/hooks/useFormValuesObserver.d.ts.map +0 -1
- package/dist/hooks/useRenderControl.d.ts +0 -5
- package/dist/hooks/useRenderControl.d.ts.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -755
- package/dist/index.js.map +0 -1
- package/dist/utils/composeFns.d.ts +0 -2
- package/dist/utils/composeFns.d.ts.map +0 -1
- package/dist/utils/groupBy.d.ts +0 -2
- package/dist/utils/groupBy.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@goodie-forms/react",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"default": "./dist/index.js"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "vite build",
|
|
15
|
-
"prepublish": "npm run build"
|
|
16
|
-
},
|
|
17
|
-
"peerDependencies": {
|
|
18
|
-
"react": "^18.0.0"
|
|
19
|
-
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"@goodie-forms/core": "workspace:*",
|
|
22
|
-
"@vitejs/plugin-react": "^5.1.2",
|
|
23
|
-
"react-dom": "^19.2.3"
|
|
24
|
-
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"@types/react": "^19.2.9",
|
|
27
|
-
"@types/react-dom": "^19.2.3",
|
|
28
|
-
"typescript": "^5.9.3",
|
|
29
|
-
"vite": "^7.3.1",
|
|
30
|
-
"vite-plugin-dts": "^4.5.4"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodie-forms/react",
|
|
3
|
+
"version": "1.1.0-alpha",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "vite build",
|
|
15
|
+
"prepublish": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react": "^18.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@goodie-forms/core": "workspace:*",
|
|
22
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
23
|
+
"react-dom": "^19.2.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/react": "^19.2.9",
|
|
27
|
+
"@types/react-dom": "^19.2.3",
|
|
28
|
+
"typescript": "^5.9.3",
|
|
29
|
+
"vite": "^7.3.1",
|
|
30
|
+
"vite-plugin-dts": "^4.5.4"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,107 +1,120 @@
|
|
|
1
|
-
import { Field, FormField } from "@goodie-forms/core";
|
|
2
|
-
import {
|
|
3
|
-
ChangeEvent,
|
|
4
|
-
FocusEvent,
|
|
5
|
-
ReactNode,
|
|
6
|
-
Ref,
|
|
7
|
-
useEffect,
|
|
8
|
-
useRef,
|
|
9
|
-
} from "react";
|
|
10
|
-
import { UseForm } from "../hooks/useForm";
|
|
11
|
-
import { useFormField } from "../hooks/useFormField";
|
|
12
|
-
import { composeFns } from "../utils/composeFns";
|
|
13
|
-
|
|
14
|
-
export interface RenderParams<
|
|
15
|
-
TShape extends object,
|
|
16
|
-
TPath extends Field.Paths<TShape
|
|
17
|
-
> {
|
|
18
|
-
ref: Ref<any | null>;
|
|
19
|
-
|
|
20
|
-
value: Field.GetValue<TShape, TPath> | undefined;
|
|
21
|
-
|
|
22
|
-
handlers: {
|
|
23
|
-
onChange: (event: ChangeEvent<EventTarget>) => void;
|
|
24
|
-
onFocus: (event: FocusEvent) => void;
|
|
25
|
-
onBlur: (event: FocusEvent) => void;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
field:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
1
|
+
import { Field, FormField, NonnullFormField } from "@goodie-forms/core";
|
|
2
|
+
import {
|
|
3
|
+
ChangeEvent,
|
|
4
|
+
FocusEvent,
|
|
5
|
+
ReactNode,
|
|
6
|
+
Ref,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
} from "react";
|
|
10
|
+
import { UseForm } from "../hooks/useForm";
|
|
11
|
+
import { useFormField } from "../hooks/useFormField";
|
|
12
|
+
import { composeFns } from "../utils/composeFns";
|
|
13
|
+
|
|
14
|
+
export interface RenderParams<
|
|
15
|
+
TShape extends object,
|
|
16
|
+
TPath extends Field.Paths<TShape>
|
|
17
|
+
> {
|
|
18
|
+
ref: Ref<any | null>;
|
|
19
|
+
|
|
20
|
+
value: Field.GetValue<TShape, TPath> | undefined;
|
|
21
|
+
|
|
22
|
+
handlers: {
|
|
23
|
+
onChange: (event: ChangeEvent<EventTarget>) => void;
|
|
24
|
+
onFocus: (event: FocusEvent) => void;
|
|
25
|
+
onBlur: (event: FocusEvent) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
field: undefined extends Field.GetValue<TShape, TPath>
|
|
29
|
+
? FormField<TShape, TPath>
|
|
30
|
+
: NonnullFormField<TShape, TPath>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type DefaultValueProps<TValue> = undefined extends TValue
|
|
34
|
+
? { defaultValue?: TValue | (() => TValue) }
|
|
35
|
+
: { defaultValue: TValue | (() => TValue) };
|
|
36
|
+
|
|
37
|
+
export type FieldRendererProps<
|
|
38
|
+
TShape extends object,
|
|
39
|
+
TPath extends Field.Paths<TShape>
|
|
40
|
+
> = {
|
|
41
|
+
form: UseForm<TShape>;
|
|
42
|
+
path: TPath;
|
|
43
|
+
overrideInitialValue?: boolean;
|
|
44
|
+
unbindOnUnmount?: boolean;
|
|
45
|
+
render: (params: RenderParams<TShape, TPath>) => ReactNode;
|
|
46
|
+
} & DefaultValueProps<Field.GetValue<TShape, TPath>>;
|
|
47
|
+
|
|
48
|
+
export function FieldRenderer<
|
|
49
|
+
TShape extends object,
|
|
50
|
+
TPath extends Field.Paths<TShape>
|
|
51
|
+
>(props: FieldRendererProps<TShape, TPath>) {
|
|
52
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
53
|
+
|
|
54
|
+
const field = useFormField(props.form, props.path, {
|
|
55
|
+
overrideInitialValue: props.overrideInitialValue ?? true,
|
|
56
|
+
defaultValue:
|
|
57
|
+
typeof props.defaultValue === "function"
|
|
58
|
+
? (props.defaultValue as any)()
|
|
59
|
+
: props.defaultValue,
|
|
60
|
+
})!;
|
|
61
|
+
|
|
62
|
+
const handlers: RenderParams<TShape, TPath>["handlers"] = {
|
|
63
|
+
onChange(event) {
|
|
64
|
+
const { target } = event;
|
|
65
|
+
if (target !== field.boundElement) return;
|
|
66
|
+
if (!("value" in target)) return;
|
|
67
|
+
if (typeof target.value !== "string") return;
|
|
68
|
+
field.setValue(target.value as Field.GetValue<TShape, TPath>, {
|
|
69
|
+
shouldTouch: true,
|
|
70
|
+
shouldMarkDirty: true,
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
onFocus() {
|
|
74
|
+
field.touch();
|
|
75
|
+
},
|
|
76
|
+
onBlur() {
|
|
77
|
+
if (
|
|
78
|
+
props.form.hookConfigs?.validateMode === "onBlur" ||
|
|
79
|
+
props.form.hookConfigs?.validateMode === "onChange"
|
|
80
|
+
) {
|
|
81
|
+
props.form.controller.validateField(props.path);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const { events } = props.form.controller;
|
|
88
|
+
|
|
89
|
+
return composeFns(
|
|
90
|
+
events.on("valueChanged", (path) => {
|
|
91
|
+
if (path !== props.path && !Field.isDescendant(path, props.path))
|
|
92
|
+
return;
|
|
93
|
+
if (props.form.hookConfigs?.validateMode === "onChange") {
|
|
94
|
+
props.form.controller.validateField(props.path);
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
field.bindElement(elementRef.current!);
|
|
102
|
+
|
|
103
|
+
return () => {
|
|
104
|
+
if (props.unbindOnUnmount) {
|
|
105
|
+
props.form.controller.unbindField(props.path);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}, []);
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<>
|
|
112
|
+
{props.render({
|
|
113
|
+
ref: elementRef,
|
|
114
|
+
value: field.value,
|
|
115
|
+
handlers: handlers,
|
|
116
|
+
field: field as any,
|
|
117
|
+
})}
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
120
|
+
}
|
package/src/hooks/useForm.tsx
CHANGED
|
@@ -1,51 +1,46 @@
|
|
|
1
|
-
import { FormController, type Form } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { composeFns } from "../utils/composeFns";
|
|
4
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
-
|
|
6
|
-
export function useForm<TShape extends object>(
|
|
7
|
-
formConfigs: Form.FormConfigs<TShape>,
|
|
8
|
-
hookConfigs?: {
|
|
9
|
-
validateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
10
|
-
revalidateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
11
|
-
watchIssues?: boolean;
|
|
12
|
-
watchValues?: boolean;
|
|
13
|
-
},
|
|
14
|
-
) {
|
|
15
|
-
const [controller] = useState(() => new FormController(formConfigs));
|
|
16
|
-
|
|
17
|
-
const renderControl = useRenderControl();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
hookConfigs?.
|
|
32
|
-
? controller.events.on("
|
|
33
|
-
renderControl.forceRerender(),
|
|
34
|
-
)
|
|
35
|
-
: noop,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
controller,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type UseForm<TShape extends object> = ReturnType<typeof useForm<TShape>>;
|
|
1
|
+
import { FormController, type Form } from "@goodie-forms/core";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { composeFns } from "../utils/composeFns";
|
|
4
|
+
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
+
|
|
6
|
+
export function useForm<TShape extends object>(
|
|
7
|
+
formConfigs: Form.FormConfigs<TShape>,
|
|
8
|
+
hookConfigs?: {
|
|
9
|
+
validateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
10
|
+
revalidateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
11
|
+
watchIssues?: boolean;
|
|
12
|
+
watchValues?: boolean;
|
|
13
|
+
},
|
|
14
|
+
) {
|
|
15
|
+
const [controller] = useState(() => new FormController(formConfigs));
|
|
16
|
+
|
|
17
|
+
const renderControl = useRenderControl();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const noop = () => {};
|
|
21
|
+
|
|
22
|
+
return composeFns(
|
|
23
|
+
controller.events.on("submissionStatusChange", () => {
|
|
24
|
+
renderControl.forceRerender();
|
|
25
|
+
}),
|
|
26
|
+
hookConfigs?.watchIssues
|
|
27
|
+
? controller.events.on("fieldIssuesUpdated", () =>
|
|
28
|
+
renderControl.forceRerender(),
|
|
29
|
+
)
|
|
30
|
+
: noop,
|
|
31
|
+
hookConfigs?.watchValues
|
|
32
|
+
? controller.events.on("valueChanged", () =>
|
|
33
|
+
renderControl.forceRerender(),
|
|
34
|
+
)
|
|
35
|
+
: noop,
|
|
36
|
+
);
|
|
37
|
+
}, [controller]);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
formConfigs,
|
|
41
|
+
hookConfigs,
|
|
42
|
+
controller,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type UseForm<TShape extends object> = ReturnType<typeof useForm<TShape>>;
|
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { composeFns } from "../utils/composeFns";
|
|
4
|
-
import { groupBy } from "../utils/groupBy";
|
|
5
|
-
import type { UseForm } from "./useForm";
|
|
6
|
-
import { useRenderControl } from "./useRenderControl";
|
|
7
|
-
|
|
8
|
-
export function useFormErrorObserver<TShape extends object>(
|
|
9
|
-
form: UseForm<TShape>,
|
|
10
|
-
options?: {
|
|
11
|
-
include?: Field.Paths<TShape>[];
|
|
12
|
-
},
|
|
13
|
-
) {
|
|
14
|
-
const renderControl = useRenderControl();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
|
|
1
|
+
import { Field } from "@goodie-forms/core";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { composeFns } from "../utils/composeFns";
|
|
4
|
+
import { groupBy } from "../utils/groupBy";
|
|
5
|
+
import type { UseForm } from "./useForm";
|
|
6
|
+
import { useRenderControl } from "./useRenderControl";
|
|
7
|
+
|
|
8
|
+
export function useFormErrorObserver<TShape extends object>(
|
|
9
|
+
form: UseForm<TShape>,
|
|
10
|
+
options?: {
|
|
11
|
+
include?: Field.Paths<TShape>[];
|
|
12
|
+
},
|
|
13
|
+
) {
|
|
14
|
+
const renderControl = useRenderControl();
|
|
15
|
+
|
|
16
|
+
const filteredIssues = form.controller._issues.filter((issue) => {
|
|
17
|
+
if (options?.include == null) return true;
|
|
18
|
+
const path = Field.parsePath(issue.path!) as Field.Paths<TShape>;
|
|
19
|
+
return options.include.includes(path);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const observedIssues = groupBy(
|
|
23
|
+
filteredIssues,
|
|
24
|
+
(issue) => Field.parsePath(issue.path!) as Field.Paths<TShape>,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const { events } = form.controller;
|
|
29
|
+
|
|
30
|
+
return composeFns(
|
|
31
|
+
events.on("fieldIssuesUpdated", (path) => {
|
|
32
|
+
if (options?.include?.includes?.(path) ?? true) {
|
|
33
|
+
renderControl.forceRerender();
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
return observedIssues;
|
|
40
|
+
}
|
|
@@ -1,46 +1,55 @@
|
|
|
1
|
-
import { Field } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { UseForm } from "../hooks/useForm";
|
|
4
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
-
import { composeFns } from "../utils/composeFns";
|
|
6
|
-
|
|
7
|
-
export function useFormField<
|
|
8
|
-
TShape extends object,
|
|
9
|
-
TPath extends Field.Paths<TShape
|
|
10
|
-
>(
|
|
11
|
-
form: UseForm<TShape>,
|
|
12
|
-
path: TPath,
|
|
13
|
-
|
|
14
|
-
) {
|
|
15
|
-
const renderControl = useRenderControl();
|
|
16
|
-
|
|
17
|
-
const [field] = useState(() => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
form.controller.bindField(path,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
import { Field } from "@goodie-forms/core";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { UseForm } from "../hooks/useForm";
|
|
4
|
+
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
+
import { composeFns } from "../utils/composeFns";
|
|
6
|
+
|
|
7
|
+
export function useFormField<
|
|
8
|
+
TShape extends object,
|
|
9
|
+
TPath extends Field.Paths<TShape>
|
|
10
|
+
>(
|
|
11
|
+
form: UseForm<TShape>,
|
|
12
|
+
path: TPath,
|
|
13
|
+
bindingConfig?: Parameters<typeof form.controller.bindField<TPath>>[1]
|
|
14
|
+
) {
|
|
15
|
+
const renderControl = useRenderControl();
|
|
16
|
+
|
|
17
|
+
const [field, setField] = useState(() => {
|
|
18
|
+
let field = form.controller.getField(path);
|
|
19
|
+
if (field == null && bindingConfig != null) {
|
|
20
|
+
field = form.controller.bindField(path, bindingConfig);
|
|
21
|
+
}
|
|
22
|
+
return field;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const { events } = form.controller;
|
|
27
|
+
|
|
28
|
+
setField(form.controller.getField(path));
|
|
29
|
+
|
|
30
|
+
return composeFns(
|
|
31
|
+
events.on("fieldBound", (_path) => {
|
|
32
|
+
if (_path === path) setField(form.controller.getField(path));
|
|
33
|
+
}),
|
|
34
|
+
events.on("fieldUnbound", (_path) => {
|
|
35
|
+
if (_path === path) setField(undefined);
|
|
36
|
+
}),
|
|
37
|
+
events.on("valueChanged", (changedPath) => {
|
|
38
|
+
if (changedPath === path || Field.isDescendant(changedPath, path)) {
|
|
39
|
+
renderControl.forceRerender();
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
events.on("fieldTouchUpdated", (_path) => {
|
|
43
|
+
if (_path === path) renderControl.forceRerender();
|
|
44
|
+
}),
|
|
45
|
+
events.on("fieldDirtyUpdated", (_path) => {
|
|
46
|
+
if (_path === path) renderControl.forceRerender();
|
|
47
|
+
}),
|
|
48
|
+
events.on("fieldIssuesUpdated", (_path) => {
|
|
49
|
+
if (_path === path) renderControl.forceRerender();
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
return field;
|
|
55
|
+
}
|
|
@@ -1,34 +1,45 @@
|
|
|
1
|
-
import { Field } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { composeFns } from "../utils/composeFns";
|
|
4
|
-
import type { UseForm } from "./useForm";
|
|
5
|
-
import { useRenderControl } from "./useRenderControl";
|
|
6
|
-
|
|
7
|
-
export function useFormValuesObserver<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import { Field } from "@goodie-forms/core";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { composeFns } from "../utils/composeFns";
|
|
4
|
+
import type { UseForm } from "./useForm";
|
|
5
|
+
import { useRenderControl } from "./useRenderControl";
|
|
6
|
+
|
|
7
|
+
export function useFormValuesObserver<
|
|
8
|
+
TShape extends object,
|
|
9
|
+
TPaths extends Field.Paths<TShape>[] | undefined = undefined
|
|
10
|
+
>(
|
|
11
|
+
form: UseForm<TShape>,
|
|
12
|
+
options?: {
|
|
13
|
+
include?: TPaths;
|
|
14
|
+
}
|
|
15
|
+
) {
|
|
16
|
+
const renderControl = useRenderControl();
|
|
17
|
+
|
|
18
|
+
const observedValues =
|
|
19
|
+
options?.include == null
|
|
20
|
+
? form.controller._data
|
|
21
|
+
: options.include.reduce((data, path) => {
|
|
22
|
+
const value = Field.getValue(form.controller._data as TShape, path)!;
|
|
23
|
+
Field.setValue(data, path, value);
|
|
24
|
+
return data;
|
|
25
|
+
}, {} as TShape);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const { events } = form.controller;
|
|
29
|
+
|
|
30
|
+
return composeFns(
|
|
31
|
+
events.on("valueChanged", (changedPath) => {
|
|
32
|
+
const watchingChange =
|
|
33
|
+
options?.include == null
|
|
34
|
+
? true
|
|
35
|
+
: options.include.some(
|
|
36
|
+
(path) =>
|
|
37
|
+
path === changedPath || Field.isDescendant(path, changedPath)
|
|
38
|
+
);
|
|
39
|
+
if (watchingChange) renderControl.forceRerender();
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
return observedValues;
|
|
45
|
+
}
|