@arcote.tech/arc-react 0.0.29 → 0.0.31
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 +24 -0
- package/dist/form/form.d.ts +23 -0
- package/dist/form/index.d.ts +6 -0
- package/dist/{form.d.ts → form/legacy_form_resolver.d.ts} +2 -2
- package/dist/form/message.d.ts +3 -0
- package/dist/form/test.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +209 -26
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ArcObjectAny, type ArcObjectKeys, type ArcObjectValueByKey } from "@arcote.tech/arc";
|
|
2
|
+
import type { ArcAbstractAny } from "@arcote.tech/arc/elements/abstract";
|
|
3
|
+
import React from "react";
|
|
4
|
+
export type FormFieldContext = {
|
|
5
|
+
errors: any;
|
|
6
|
+
messages: any;
|
|
7
|
+
};
|
|
8
|
+
export declare const FormFieldContext: React.Context<FormFieldContext | null>;
|
|
9
|
+
export declare function useFormField(): FormFieldContext;
|
|
10
|
+
type Translations<E extends ArcAbstractAny> = {
|
|
11
|
+
[K in keyof ReturnType<E["validate"]>]: (data: Exclude<ReturnType<E["validate"]>[K], undefined>) => string;
|
|
12
|
+
};
|
|
13
|
+
type FormFieldProps<T extends ArcObjectAny, K extends ArcObjectKeys<T>> = {
|
|
14
|
+
name: K;
|
|
15
|
+
translations: Translations<ArcObjectValueByKey<T, K>>;
|
|
16
|
+
render: (field: FormFieldData<T>) => React.ReactNode;
|
|
17
|
+
};
|
|
18
|
+
export type FormFieldData<T> = {
|
|
19
|
+
onChange: (value: any) => void;
|
|
20
|
+
value: any;
|
|
21
|
+
};
|
|
22
|
+
export declare function FormField<T extends ArcObjectAny, K extends ArcObjectKeys<T>>({ name, translations, render, }: FormFieldProps<T, K>): import("react/jsx-dev-runtime").JSX.Element;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=field.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ArcObjectAny, type ArcObjectKeys } from "@arcote.tech/arc";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { FormField } from "./field";
|
|
4
|
+
export type FormContextValue<T extends ArcObjectAny> = {
|
|
5
|
+
values: Partial<Record<ArcObjectKeys<T>, any>>;
|
|
6
|
+
errors: Partial<Record<ArcObjectKeys<T>, Record<string, any>>>;
|
|
7
|
+
dirty: Set<ArcObjectKeys<T>>;
|
|
8
|
+
isSubmitted: boolean;
|
|
9
|
+
setFieldValue: (field: ArcObjectKeys<T>, value: any) => void;
|
|
10
|
+
setFieldDirty: (field: ArcObjectKeys<T>) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare const FormContext: React.Context<FormContextValue<any> | null>;
|
|
13
|
+
export declare function Form<T extends ArcObjectAny>({ render, schema, onSubmit, }: {
|
|
14
|
+
render: (props: {
|
|
15
|
+
FormField: typeof FormField<T, ArcObjectKeys<T>>;
|
|
16
|
+
}) => React.ReactNode;
|
|
17
|
+
schema: T;
|
|
18
|
+
onSubmit: (values: Record<ArcObjectKeys<T>, any>) => void | Promise<void>;
|
|
19
|
+
}): import("react/jsx-dev-runtime").JSX.Element;
|
|
20
|
+
export declare namespace Form {
|
|
21
|
+
var displayName: string;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ArcObjectAny } from "@arcote.tech/arc";
|
|
2
2
|
export declare function formResolver(schema: ArcObjectAny): (data: any) => Promise<{
|
|
3
3
|
values: any;
|
|
4
4
|
errors: {};
|
|
5
5
|
}>;
|
|
6
|
-
//# sourceMappingURL=
|
|
6
|
+
//# sourceMappingURL=legacy_form_resolver.d.ts.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,124 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
'use client';
|
|
3
|
+
// form/field.tsx
|
|
4
|
+
import { createContext as createContext2, useCallback as useCallback2, useContext, useMemo as useMemo2 } from "react";
|
|
5
|
+
|
|
6
|
+
// form/form.tsx
|
|
7
|
+
import {
|
|
8
|
+
createContext,
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useMemo,
|
|
12
|
+
useState
|
|
13
|
+
} from "react";
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
|
+
"use client";
|
|
16
|
+
var FormContext = createContext(null);
|
|
17
|
+
function Form({
|
|
18
|
+
render,
|
|
19
|
+
schema,
|
|
20
|
+
onSubmit
|
|
21
|
+
}) {
|
|
22
|
+
const [values, setValues] = useState({});
|
|
23
|
+
const [errors, setErrors] = useState({});
|
|
24
|
+
const [dirty, setDirty] = useState(new Set);
|
|
25
|
+
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
26
|
+
const validate = useCallback(() => {
|
|
27
|
+
console.log("validate", values, schema);
|
|
28
|
+
const errors2 = schema.validate(values);
|
|
29
|
+
setErrors(errors2);
|
|
30
|
+
return Object.values(errors2).some((result) => result);
|
|
31
|
+
}, [schema, values]);
|
|
32
|
+
const setFieldValue = useCallback((field, value) => {
|
|
33
|
+
setValues((prev) => ({ ...prev, [field]: value }));
|
|
34
|
+
}, [schema, isSubmitted]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isSubmitted) {
|
|
37
|
+
validate();
|
|
38
|
+
}
|
|
39
|
+
}, [isSubmitted, values]);
|
|
40
|
+
const setFieldDirty = useCallback((field) => {
|
|
41
|
+
setDirty((prev) => new Set([...prev, field]));
|
|
42
|
+
}, []);
|
|
43
|
+
const hasErrors = useMemo(() => Object.values(errors).some((result) => result), [errors]);
|
|
44
|
+
const handleSubmit = useCallback(async (e) => {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
setIsSubmitted(true);
|
|
47
|
+
validate();
|
|
48
|
+
if (!hasErrors) {
|
|
49
|
+
await onSubmit(values);
|
|
50
|
+
}
|
|
51
|
+
}, [schema, values, onSubmit, hasErrors]);
|
|
52
|
+
const FF = useCallback(FormField, []);
|
|
53
|
+
const contextValue = useMemo(() => ({
|
|
54
|
+
values,
|
|
55
|
+
errors,
|
|
56
|
+
dirty,
|
|
57
|
+
isSubmitted,
|
|
58
|
+
setFieldValue,
|
|
59
|
+
setFieldDirty
|
|
60
|
+
}), [values, errors, dirty, isSubmitted, setFieldValue, setFieldDirty]);
|
|
61
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
62
|
+
value: contextValue,
|
|
63
|
+
children: /* @__PURE__ */ jsx("form", {
|
|
64
|
+
onSubmit: handleSubmit,
|
|
65
|
+
children: render({ FormField: FF })
|
|
66
|
+
}, undefined, false, undefined, this)
|
|
67
|
+
}, undefined, false, undefined, this);
|
|
68
|
+
}
|
|
69
|
+
Form.displayName = "Form";
|
|
70
|
+
|
|
71
|
+
// form/field.tsx
|
|
72
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
73
|
+
"use client";
|
|
74
|
+
var FormFieldContext = createContext2(null);
|
|
75
|
+
function useFormField() {
|
|
76
|
+
const context = useContext(FormFieldContext);
|
|
77
|
+
if (!context)
|
|
78
|
+
throw new Error("useFormField must be used within a FormFieldProvider");
|
|
79
|
+
return context;
|
|
80
|
+
}
|
|
81
|
+
function FormField({
|
|
82
|
+
name,
|
|
83
|
+
translations,
|
|
84
|
+
render
|
|
85
|
+
}) {
|
|
86
|
+
const form = useContext(FormContext);
|
|
87
|
+
if (!form)
|
|
88
|
+
throw new Error("FormField must be used within a Form");
|
|
89
|
+
const { values, errors, dirty, isSubmitted, setFieldValue, setFieldDirty } = form;
|
|
90
|
+
const fieldErrors = errors[name] || {};
|
|
91
|
+
const value = values[name] || "";
|
|
92
|
+
const isDirty = dirty.has(name);
|
|
93
|
+
const handleChange = useCallback2((value2) => {
|
|
94
|
+
if (typeof value2 === "string") {
|
|
95
|
+
setFieldValue(name, value2);
|
|
96
|
+
} else if (value2?.target?.value !== undefined) {
|
|
97
|
+
setFieldValue(name, value2.target.value);
|
|
98
|
+
}
|
|
99
|
+
if (!isDirty) {
|
|
100
|
+
setFieldDirty(name);
|
|
101
|
+
}
|
|
102
|
+
}, [name, isDirty, setFieldValue, setFieldDirty]);
|
|
103
|
+
const errorMessages = Object.entries(fieldErrors).map(([key, value2]) => {
|
|
104
|
+
if (!value2)
|
|
105
|
+
return;
|
|
106
|
+
const translation = translations[key];
|
|
107
|
+
return translation?.(value2);
|
|
108
|
+
}).filter(Boolean);
|
|
109
|
+
const contextValue = useMemo2(() => ({
|
|
110
|
+
errors: isSubmitted ? fieldErrors : {},
|
|
111
|
+
messages: errorMessages
|
|
112
|
+
}), [fieldErrors, isSubmitted, errorMessages]);
|
|
113
|
+
return /* @__PURE__ */ jsx2(FormFieldContext.Provider, {
|
|
114
|
+
value: contextValue,
|
|
115
|
+
children: render({
|
|
116
|
+
onChange: handleChange,
|
|
117
|
+
value
|
|
118
|
+
})
|
|
119
|
+
}, undefined, false, undefined, this);
|
|
120
|
+
}
|
|
121
|
+
// form/legacy_form_resolver.ts
|
|
2
122
|
function formResolver(schema) {
|
|
3
123
|
return async (data) => {
|
|
4
124
|
return {
|
|
@@ -7,6 +127,61 @@ function formResolver(schema) {
|
|
|
7
127
|
};
|
|
8
128
|
};
|
|
9
129
|
}
|
|
130
|
+
// form/message.tsx
|
|
131
|
+
import React3 from "react";
|
|
132
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
133
|
+
"use client";
|
|
134
|
+
var FormMessage = React3.forwardRef(({}, ref) => {
|
|
135
|
+
const { messages } = useFormField();
|
|
136
|
+
if (messages.length === 0)
|
|
137
|
+
return null;
|
|
138
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
139
|
+
className: "text-destructive",
|
|
140
|
+
ref,
|
|
141
|
+
children: messages[0]
|
|
142
|
+
}, undefined, false, undefined, this);
|
|
143
|
+
});
|
|
144
|
+
FormMessage.displayName = "FormMessage";
|
|
145
|
+
// form/test.tsx
|
|
146
|
+
import { object, string } from "@arcote.tech/arc";
|
|
147
|
+
import { jsx as jsx4, Fragment } from "react/jsx-runtime";
|
|
148
|
+
"use client";
|
|
149
|
+
function Test() {
|
|
150
|
+
const schema = object({
|
|
151
|
+
username: string().minLength(3).maxLength(10)
|
|
152
|
+
});
|
|
153
|
+
const handleSubmit = async (values) => {
|
|
154
|
+
console.log("Form submitted:", values);
|
|
155
|
+
};
|
|
156
|
+
return /* @__PURE__ */ jsx4(Form, {
|
|
157
|
+
schema,
|
|
158
|
+
onSubmit: handleSubmit,
|
|
159
|
+
render: ({ FormField: FormField2 }) => /* @__PURE__ */ jsx4("div", {
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsx4(FormField2, {
|
|
162
|
+
name: "username",
|
|
163
|
+
translations: {
|
|
164
|
+
minLength: ({ minLength }) => `Username must be at least ${minLength} characters long`,
|
|
165
|
+
maxLength: ({ maxLength }) => `Username must be at most ${maxLength} characters long`
|
|
166
|
+
},
|
|
167
|
+
render: (field) => /* @__PURE__ */ jsx4(Fragment, {
|
|
168
|
+
children: [
|
|
169
|
+
/* @__PURE__ */ jsx4("input", {
|
|
170
|
+
className: "border border-gray-300 rounded-md p-2",
|
|
171
|
+
...field
|
|
172
|
+
}, undefined, false, undefined, this),
|
|
173
|
+
/* @__PURE__ */ jsx4(FormMessage, {}, undefined, false, undefined, this)
|
|
174
|
+
]
|
|
175
|
+
}, undefined, true, undefined, this)
|
|
176
|
+
}, undefined, false, undefined, this),
|
|
177
|
+
/* @__PURE__ */ jsx4("button", {
|
|
178
|
+
type: "submit",
|
|
179
|
+
children: "Submit"
|
|
180
|
+
}, undefined, false, undefined, this)
|
|
181
|
+
]
|
|
182
|
+
}, undefined, true, undefined, this)
|
|
183
|
+
}, undefined, false, undefined, this);
|
|
184
|
+
}
|
|
10
185
|
// idb.ts
|
|
11
186
|
import {
|
|
12
187
|
ArcCollection,
|
|
@@ -166,21 +341,22 @@ import {
|
|
|
166
341
|
rtcClientFactory
|
|
167
342
|
} from "@arcote.tech/arc";
|
|
168
343
|
import {
|
|
169
|
-
createContext,
|
|
170
|
-
useContext,
|
|
171
|
-
useEffect,
|
|
172
|
-
useMemo,
|
|
344
|
+
createContext as createContext3,
|
|
345
|
+
useContext as useContext2,
|
|
346
|
+
useEffect as useEffect2,
|
|
347
|
+
useMemo as useMemo3,
|
|
173
348
|
useRef,
|
|
174
|
-
useState
|
|
349
|
+
useState as useState2
|
|
175
350
|
} from "react";
|
|
176
|
-
import { jsx } from "react/jsx-runtime";
|
|
351
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
352
|
+
"use client";
|
|
177
353
|
var reactModel = (arcContext, databaseName) => {
|
|
178
|
-
const LiveModelContext =
|
|
179
|
-
const LocalModelContext =
|
|
354
|
+
const LiveModelContext = createContext3(null);
|
|
355
|
+
const LocalModelContext = createContext3(null);
|
|
180
356
|
let modelMasterDataStorage = null;
|
|
181
357
|
return [
|
|
182
358
|
function LiveModelProvider(props) {
|
|
183
|
-
const dataStorage =
|
|
359
|
+
const dataStorage = useMemo3(() => {
|
|
184
360
|
if (typeof window === "undefined")
|
|
185
361
|
return null;
|
|
186
362
|
const dbAdapterPromise = idbAdapterFactory(databaseName, arcContext.version)(arcContext);
|
|
@@ -188,9 +364,9 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
188
364
|
modelMasterDataStorage = dataStorage2;
|
|
189
365
|
return dataStorage2;
|
|
190
366
|
}, [arcContext, databaseName, arcContext.version]);
|
|
191
|
-
const [syncProgress, setSyncProgress] =
|
|
192
|
-
const [syncDone, setSyncDone] =
|
|
193
|
-
|
|
367
|
+
const [syncProgress, setSyncProgress] = useState2([]);
|
|
368
|
+
const [syncDone, setSyncDone] = useState2(false);
|
|
369
|
+
useEffect2(() => {
|
|
194
370
|
if (typeof window === "undefined" || !dataStorage)
|
|
195
371
|
return;
|
|
196
372
|
const sync = async () => {
|
|
@@ -202,10 +378,10 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
202
378
|
sync();
|
|
203
379
|
}, [dataStorage]);
|
|
204
380
|
if (!dataStorage || !syncDone)
|
|
205
|
-
return /* @__PURE__ */
|
|
381
|
+
return /* @__PURE__ */ jsx5(props.syncView, {
|
|
206
382
|
progress: syncProgress
|
|
207
383
|
}, undefined, false, undefined, this);
|
|
208
|
-
return /* @__PURE__ */
|
|
384
|
+
return /* @__PURE__ */ jsx5(LiveModelContext.Provider, {
|
|
209
385
|
value: {
|
|
210
386
|
dataStorage,
|
|
211
387
|
client: props.client,
|
|
@@ -215,14 +391,14 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
215
391
|
}, undefined, false, undefined, this);
|
|
216
392
|
},
|
|
217
393
|
function LocalModelProvider({ children }) {
|
|
218
|
-
const parentContext =
|
|
394
|
+
const parentContext = useContext2(LiveModelContext);
|
|
219
395
|
if (!parentContext) {
|
|
220
396
|
throw new Error("LocalModelProvider must be used within a LiveModelProvider");
|
|
221
397
|
}
|
|
222
|
-
const [localContext] =
|
|
398
|
+
const [localContext] = useState2(() => ({
|
|
223
399
|
dataStorage: parentContext.dataStorage.fork()
|
|
224
400
|
}));
|
|
225
|
-
return /* @__PURE__ */
|
|
401
|
+
return /* @__PURE__ */ jsx5(LocalModelContext.Provider, {
|
|
226
402
|
value: {
|
|
227
403
|
dataStorage: localContext.dataStorage,
|
|
228
404
|
client: parentContext.client,
|
|
@@ -232,14 +408,14 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
232
408
|
}, undefined, false, undefined, this);
|
|
233
409
|
},
|
|
234
410
|
function useQuery(queryBuilderFn, dependencies = []) {
|
|
235
|
-
const context =
|
|
411
|
+
const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
|
|
236
412
|
if (!context) {
|
|
237
413
|
throw new Error("useQuery must be used within a ModelProvider");
|
|
238
414
|
}
|
|
239
|
-
const [result, setResult] =
|
|
240
|
-
const [loading, setLoading] =
|
|
415
|
+
const [result, setResult] = useState2(null);
|
|
416
|
+
const [loading, setLoading] = useState2(true);
|
|
241
417
|
const queryRef = useRef(null);
|
|
242
|
-
|
|
418
|
+
useEffect2(() => {
|
|
243
419
|
if (queryRef.current)
|
|
244
420
|
queryRef.current.unsubscribe();
|
|
245
421
|
const queryBuilder = arcContext.queryBuilder();
|
|
@@ -262,7 +438,7 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
262
438
|
return [result, loading];
|
|
263
439
|
},
|
|
264
440
|
function useCommands() {
|
|
265
|
-
const context =
|
|
441
|
+
const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
|
|
266
442
|
if (!context) {
|
|
267
443
|
throw new Error("useQuery must be used within a ModelProvider");
|
|
268
444
|
}
|
|
@@ -279,7 +455,7 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
279
455
|
return result;
|
|
280
456
|
},
|
|
281
457
|
function useLocalDataStorage() {
|
|
282
|
-
const context =
|
|
458
|
+
const context = useContext2(LocalModelContext);
|
|
283
459
|
if (!context) {
|
|
284
460
|
return null;
|
|
285
461
|
}
|
|
@@ -288,9 +464,16 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
288
464
|
];
|
|
289
465
|
};
|
|
290
466
|
export {
|
|
467
|
+
useFormField,
|
|
291
468
|
reactModel,
|
|
292
469
|
idbAdapterFactory,
|
|
293
|
-
formResolver
|
|
470
|
+
formResolver,
|
|
471
|
+
Test,
|
|
472
|
+
FormMessage,
|
|
473
|
+
FormFieldContext,
|
|
474
|
+
FormField,
|
|
475
|
+
FormContext,
|
|
476
|
+
Form
|
|
294
477
|
};
|
|
295
478
|
|
|
296
|
-
//# debugId=
|
|
479
|
+
//# debugId=1DDD66A12E066C4464756E2164756E21
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.0.
|
|
7
|
+
"version": "0.0.31",
|
|
8
8
|
"private": false,
|
|
9
9
|
"author": "Przemysław Krasiński [arcote.tech]",
|
|
10
10
|
"description": "React client for the Arc framework, providing utilities for querying data and executing commands, enhancing the development of reactive and efficient user interfaces.",
|