@arcote.tech/arc-react 0.1.9 → 0.1.10
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/form/field.d.ts +6 -4
- package/dist/form/form.d.ts +7 -4
- package/dist/index.js +71 -11
- package/package.json +1 -1
package/dist/form/field.d.ts
CHANGED
|
@@ -9,16 +9,18 @@ export declare function useFormField(): FormFieldContext;
|
|
|
9
9
|
type Translations<E extends ArcElement> = {
|
|
10
10
|
[K in keyof Exclude<ReturnType<E["validate"]>, false>]: (data: Exclude<Exclude<ReturnType<E["validate"]>, false>[K], undefined | false>) => string;
|
|
11
11
|
} | ((data: any) => string) | string;
|
|
12
|
-
type FormFieldProps<E extends ArcElement> = {
|
|
12
|
+
type FormFieldProps<E extends ArcElement, S = undefined> = {
|
|
13
13
|
translations: Translations<E>;
|
|
14
|
-
render: (field: FormFieldData<E>) => React.ReactNode;
|
|
14
|
+
render: (field: FormFieldData<E, S>) => React.ReactNode;
|
|
15
15
|
};
|
|
16
|
-
export type FormFieldData<T> = {
|
|
16
|
+
export type FormFieldData<T, S = undefined> = {
|
|
17
17
|
onChange: (value: any) => void;
|
|
18
18
|
value: any;
|
|
19
19
|
defaultValue?: any;
|
|
20
20
|
name: string;
|
|
21
|
+
subFields?: S;
|
|
22
|
+
setFieldValue: (field: string, value: any) => void;
|
|
21
23
|
};
|
|
22
|
-
export declare function FormField<E extends ArcElement>(name: string, defaultValue?: any): ({ translations, render }: FormFieldProps<E>) => import("react/jsx-dev-runtime").JSX.Element;
|
|
24
|
+
export declare function FormField<E extends ArcElement, S = undefined>(name: string, defaultValue?: any, subFields?: S): ({ translations, render }: FormFieldProps<E, S>) => import("react/jsx-dev-runtime").JSX.Element;
|
|
23
25
|
export {};
|
|
24
26
|
//# sourceMappingURL=field.d.ts.map
|
package/dist/form/form.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type $type, type ArcObjectAny, type ArcObjectKeys, type ArcObjectValueByKey } from "@arcote.tech/arc";
|
|
1
|
+
import { type $type, type ArcObjectAny, type ArcObjectKeys, type ArcObjectValueByKey, ArcOptional } from "@arcote.tech/arc";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { FormField } from "./field";
|
|
4
4
|
export type FormContextValue<T extends ArcObjectAny> = {
|
|
@@ -14,16 +14,19 @@ export type FormRef<T extends ArcObjectAny> = {
|
|
|
14
14
|
submit: () => Promise<void>;
|
|
15
15
|
getValues: () => Partial<$type<T>>;
|
|
16
16
|
validate: () => boolean;
|
|
17
|
+
setFieldValue: (field: ArcObjectKeys<T>, value: any) => void;
|
|
18
|
+
};
|
|
19
|
+
export type FormFields<T extends ArcObjectAny> = {
|
|
20
|
+
[K in ArcObjectKeys<T> as Capitalize<`${K}`>]: ArcObjectValueByKey<T, K> extends ArcObjectAny ? ReturnType<typeof FormField<ArcObjectValueByKey<T, K>, FormFields<ArcObjectValueByKey<T, K>>>> : ArcObjectValueByKey<T, K> extends ArcOptional<infer U extends ArcObjectAny> ? ReturnType<typeof FormField<ArcObjectValueByKey<T, K>, FormFields<U>>> : ReturnType<typeof FormField<ArcObjectValueByKey<T, K>>>;
|
|
17
21
|
};
|
|
18
22
|
export type FormProps<T extends ArcObjectAny> = {
|
|
19
|
-
render: (props:
|
|
20
|
-
[K in ArcObjectKeys<T> as Capitalize<`${K}`>]: ReturnType<typeof FormField<ArcObjectValueByKey<T, K>>>;
|
|
21
|
-
}, values: Partial<$type<T>>) => React.ReactNode;
|
|
23
|
+
render: (props: FormFields<T>, values: Partial<$type<T>>) => React.ReactNode;
|
|
22
24
|
schema: T;
|
|
23
25
|
onSubmit: (values: $type<T>) => void | Promise<void>;
|
|
24
26
|
onUnvalidatedSubmit?: (values: Partial<$type<T>>, errors: any) => void;
|
|
25
27
|
defaults?: Partial<$type<T>> | null;
|
|
26
28
|
};
|
|
27
29
|
export declare const FormContext: React.Context<FormContextValue<any> | null>;
|
|
30
|
+
export declare function useForm<T extends ArcObjectAny>(): FormContextValue<T>;
|
|
28
31
|
export declare const Form: <T extends ArcObjectAny>(props: FormProps<T> & React.RefAttributes<FormRef<T>>) => JSX.Element;
|
|
29
32
|
//# sourceMappingURL=form.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createContext as createContext3, useCallback as useCallback3, useContex
|
|
|
6
6
|
import {
|
|
7
7
|
deepMerge
|
|
8
8
|
} from "@arcote.tech/arc";
|
|
9
|
-
import {
|
|
9
|
+
import React, {
|
|
10
10
|
createContext,
|
|
11
11
|
forwardRef,
|
|
12
12
|
useCallback,
|
|
@@ -17,6 +17,32 @@ import {
|
|
|
17
17
|
} from "react";
|
|
18
18
|
import { jsx } from "react/jsx-runtime";
|
|
19
19
|
var FormContext = createContext(null);
|
|
20
|
+
function useForm() {
|
|
21
|
+
const context = React.useContext(FormContext);
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw new Error("useForm must be used within a Form component");
|
|
24
|
+
}
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
27
|
+
function setNestedValue(obj, path, value) {
|
|
28
|
+
const keys = path.split(".");
|
|
29
|
+
const result = { ...obj };
|
|
30
|
+
let current = result;
|
|
31
|
+
for (let i = 0;i < keys.length - 1; i++) {
|
|
32
|
+
const key = keys[i];
|
|
33
|
+
if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
|
|
34
|
+
current[key] = {};
|
|
35
|
+
} else {
|
|
36
|
+
current[key] = { ...current[key] };
|
|
37
|
+
}
|
|
38
|
+
current = current[key];
|
|
39
|
+
}
|
|
40
|
+
current[keys[keys.length - 1]] = value;
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
function getNestedValue(obj, path) {
|
|
44
|
+
return path.split(".").reduce((current, key) => current?.[key], obj);
|
|
45
|
+
}
|
|
20
46
|
var Form = forwardRef(function Form2(props, ref) {
|
|
21
47
|
const { render, schema, onSubmit, defaults, onUnvalidatedSubmit } = props;
|
|
22
48
|
const [values, setValues] = useState({});
|
|
@@ -38,7 +64,13 @@ var Form = forwardRef(function Form2(props, ref) {
|
|
|
38
64
|
return Object.values(errors2).some((result) => result);
|
|
39
65
|
}, [schema, values]);
|
|
40
66
|
const validatePartial = useCallback((keys) => {
|
|
41
|
-
const partialValues = keys.reduce((acc, key) =>
|
|
67
|
+
const partialValues = keys.reduce((acc, key) => {
|
|
68
|
+
const value = getNestedValue(values, key);
|
|
69
|
+
if (value !== undefined) {
|
|
70
|
+
return setNestedValue(acc, key, value);
|
|
71
|
+
}
|
|
72
|
+
return acc;
|
|
73
|
+
}, {});
|
|
42
74
|
const errors2 = schema.validatePartial(partialValues);
|
|
43
75
|
if (errors2)
|
|
44
76
|
setErrors((prev) => deepMerge(prev, errors2));
|
|
@@ -52,13 +84,19 @@ var Form = forwardRef(function Form2(props, ref) {
|
|
|
52
84
|
return Object.values(errors2).some((result) => result);
|
|
53
85
|
}, [schema, values, dirty]);
|
|
54
86
|
const setFieldValue = useCallback((field, value) => {
|
|
55
|
-
setValues((prev) => (
|
|
87
|
+
setValues((prev) => setNestedValue(prev, field, value));
|
|
56
88
|
}, []);
|
|
57
89
|
const setFieldDirty = useCallback((field) => {
|
|
58
90
|
setDirty((prev) => new Set([...prev, field]));
|
|
59
91
|
}, []);
|
|
60
92
|
useEffect(() => {
|
|
61
|
-
const partialValues = Array.from(dirty).reduce((acc, key) =>
|
|
93
|
+
const partialValues = Array.from(dirty).reduce((acc, key) => {
|
|
94
|
+
const value = getNestedValue(values, key);
|
|
95
|
+
if (value !== undefined) {
|
|
96
|
+
return setNestedValue(acc, key, value);
|
|
97
|
+
}
|
|
98
|
+
return acc;
|
|
99
|
+
}, {});
|
|
62
100
|
const errors2 = schema.validatePartial(partialValues);
|
|
63
101
|
setErrors(errors2);
|
|
64
102
|
}, [values, dirty]);
|
|
@@ -77,14 +115,30 @@ var Form = forwardRef(function Form2(props, ref) {
|
|
|
77
115
|
useImperativeHandle(ref, () => ({
|
|
78
116
|
submit: handleSubmit,
|
|
79
117
|
getValues: () => values,
|
|
80
|
-
validate
|
|
118
|
+
validate,
|
|
119
|
+
setFieldValue
|
|
81
120
|
}), [handleSubmit, values, validate]);
|
|
121
|
+
const buildFieldsStructure = useCallback((element, fieldName, defaultValue) => {
|
|
122
|
+
const isOptional = element?.toJsonSchema && typeof element.toJsonSchema === "function" && element.parent !== undefined;
|
|
123
|
+
if (isOptional) {
|
|
124
|
+
return buildFieldsStructure(element.parent, fieldName, defaultValue);
|
|
125
|
+
}
|
|
126
|
+
const isObject = element?.entries && typeof element.entries === "function";
|
|
127
|
+
if (isObject) {
|
|
128
|
+
const subFields = Object.fromEntries(element.entries().map(([key, value]) => [
|
|
129
|
+
key.charAt(0).toUpperCase() + key.slice(1),
|
|
130
|
+
buildFieldsStructure(value, fieldName ? `${fieldName}.${key}` : key, defaultValue?.[key])
|
|
131
|
+
]));
|
|
132
|
+
return FormField(fieldName, defaultValue, subFields);
|
|
133
|
+
}
|
|
134
|
+
return FormField(fieldName, defaultValue);
|
|
135
|
+
}, []);
|
|
82
136
|
const Fields = useMemo(() => {
|
|
83
137
|
return Object.fromEntries(schema.entries().map(([key, value]) => [
|
|
84
138
|
key.charAt(0).toUpperCase() + key.slice(1),
|
|
85
|
-
|
|
139
|
+
buildFieldsStructure(value, key, defaults?.[key])
|
|
86
140
|
]));
|
|
87
|
-
}, [schema, defaults]);
|
|
141
|
+
}, [schema, defaults, buildFieldsStructure]);
|
|
88
142
|
const contextValue = useMemo(() => ({
|
|
89
143
|
values,
|
|
90
144
|
errors,
|
|
@@ -167,6 +221,9 @@ function useFormPartField(fieldName) {
|
|
|
167
221
|
|
|
168
222
|
// form/field.tsx
|
|
169
223
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
224
|
+
function getNestedValue2(obj, path) {
|
|
225
|
+
return path.split(".").reduce((current, key) => current?.[key], obj);
|
|
226
|
+
}
|
|
170
227
|
var FormFieldContext = createContext3(null);
|
|
171
228
|
function useFormField() {
|
|
172
229
|
const context = useContext2(FormFieldContext);
|
|
@@ -174,7 +231,7 @@ function useFormField() {
|
|
|
174
231
|
throw new Error("useFormField must be used within a FormFieldProvider");
|
|
175
232
|
return context;
|
|
176
233
|
}
|
|
177
|
-
function FormField(name, defaultValue) {
|
|
234
|
+
function FormField(name, defaultValue, subFields) {
|
|
178
235
|
return ({ translations, render }) => {
|
|
179
236
|
const form = useContext2(FormContext);
|
|
180
237
|
if (!form)
|
|
@@ -183,7 +240,7 @@ function FormField(name, defaultValue) {
|
|
|
183
240
|
const { values, errors, dirty, isSubmitted, setFieldValue, setFieldDirty } = form;
|
|
184
241
|
const schemaErrors = errors?.["schema"] || {};
|
|
185
242
|
const fieldErrors = schemaErrors[name] || false;
|
|
186
|
-
const value = values
|
|
243
|
+
const value = getNestedValue2(values, name) ?? defaultValue ?? "";
|
|
187
244
|
const isDirty = dirty.has(name);
|
|
188
245
|
const handleChange = useCallback3((value2) => {
|
|
189
246
|
if (value2?.target?.value !== undefined) {
|
|
@@ -218,7 +275,9 @@ function FormField(name, defaultValue) {
|
|
|
218
275
|
onChange: handleChange,
|
|
219
276
|
name: name.toString(),
|
|
220
277
|
value,
|
|
221
|
-
defaultValue
|
|
278
|
+
defaultValue,
|
|
279
|
+
subFields,
|
|
280
|
+
setFieldValue
|
|
222
281
|
})
|
|
223
282
|
}, undefined, false, undefined, this);
|
|
224
283
|
};
|
|
@@ -456,6 +515,7 @@ export {
|
|
|
456
515
|
useFormPartField,
|
|
457
516
|
useFormPart,
|
|
458
517
|
useFormField,
|
|
518
|
+
useForm,
|
|
459
519
|
reactModel,
|
|
460
520
|
formResolver,
|
|
461
521
|
FormPartContext,
|
|
@@ -467,4 +527,4 @@ export {
|
|
|
467
527
|
Form
|
|
468
528
|
};
|
|
469
529
|
|
|
470
|
-
//# debugId=
|
|
530
|
+
//# debugId=93653667973401D564756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.10",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Przemysław Krasiński [arcote.tech]",
|
|
7
7
|
"description": "React client for the Arc framework, providing utilities for querying data and executing commands, enhancing the development of reactive and efficient user interfaces.",
|