@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.
@@ -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
@@ -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) => ({ ...acc, [key]: values[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) => ({ ...prev, [field]: value }));
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) => ({ ...acc, [key]: values[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
- FormField(key, defaults?.[key])
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[name] ?? defaultValue ?? "";
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=64B909B4D47850D764756E2164756E21
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.9",
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.",