@arcote.tech/arc-react 0.0.28 → 0.0.30
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 -27
- 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,122 @@
|
|
|
1
|
-
// form.
|
|
1
|
+
// form/field.tsx
|
|
2
|
+
import { createContext as createContext2, useCallback as useCallback2, useContext, useMemo as useMemo2 } from "react";
|
|
3
|
+
|
|
4
|
+
// form/form.tsx
|
|
5
|
+
import {
|
|
6
|
+
createContext,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
"use client";
|
|
14
|
+
var FormContext = createContext(null);
|
|
15
|
+
function Form({
|
|
16
|
+
render,
|
|
17
|
+
schema,
|
|
18
|
+
onSubmit
|
|
19
|
+
}) {
|
|
20
|
+
const [values, setValues] = useState({});
|
|
21
|
+
const [errors, setErrors] = useState({});
|
|
22
|
+
const [dirty, setDirty] = useState(new Set);
|
|
23
|
+
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
24
|
+
const validate = useCallback(() => {
|
|
25
|
+
console.log("validate", values, schema);
|
|
26
|
+
const errors2 = schema.validate(values);
|
|
27
|
+
setErrors(errors2);
|
|
28
|
+
return Object.values(errors2).some((result) => result);
|
|
29
|
+
}, [schema, values]);
|
|
30
|
+
const setFieldValue = useCallback((field, value) => {
|
|
31
|
+
setValues((prev) => ({ ...prev, [field]: value }));
|
|
32
|
+
}, [schema, isSubmitted]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (isSubmitted) {
|
|
35
|
+
validate();
|
|
36
|
+
}
|
|
37
|
+
}, [isSubmitted, values]);
|
|
38
|
+
const setFieldDirty = useCallback((field) => {
|
|
39
|
+
setDirty((prev) => new Set([...prev, field]));
|
|
40
|
+
}, []);
|
|
41
|
+
const hasErrors = useMemo(() => Object.values(errors).some((result) => result), [errors]);
|
|
42
|
+
const handleSubmit = useCallback(async (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
setIsSubmitted(true);
|
|
45
|
+
validate();
|
|
46
|
+
if (!hasErrors) {
|
|
47
|
+
await onSubmit(values);
|
|
48
|
+
}
|
|
49
|
+
}, [schema, values, onSubmit, hasErrors]);
|
|
50
|
+
const FF = useCallback(FormField, []);
|
|
51
|
+
const contextValue = useMemo(() => ({
|
|
52
|
+
values,
|
|
53
|
+
errors,
|
|
54
|
+
dirty,
|
|
55
|
+
isSubmitted,
|
|
56
|
+
setFieldValue,
|
|
57
|
+
setFieldDirty
|
|
58
|
+
}), [values, errors, dirty, isSubmitted, setFieldValue, setFieldDirty]);
|
|
59
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
60
|
+
value: contextValue,
|
|
61
|
+
children: /* @__PURE__ */ jsx("form", {
|
|
62
|
+
onSubmit: handleSubmit,
|
|
63
|
+
children: render({ FormField: FF })
|
|
64
|
+
}, undefined, false, undefined, this)
|
|
65
|
+
}, undefined, false, undefined, this);
|
|
66
|
+
}
|
|
67
|
+
Form.displayName = "Form";
|
|
68
|
+
|
|
69
|
+
// form/field.tsx
|
|
70
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
71
|
+
"use client";
|
|
72
|
+
var FormFieldContext = createContext2(null);
|
|
73
|
+
function useFormField() {
|
|
74
|
+
const context = useContext(FormFieldContext);
|
|
75
|
+
if (!context)
|
|
76
|
+
throw new Error("useFormField must be used within a FormFieldProvider");
|
|
77
|
+
return context;
|
|
78
|
+
}
|
|
79
|
+
function FormField({
|
|
80
|
+
name,
|
|
81
|
+
translations,
|
|
82
|
+
render
|
|
83
|
+
}) {
|
|
84
|
+
const form = useContext(FormContext);
|
|
85
|
+
if (!form)
|
|
86
|
+
throw new Error("FormField must be used within a Form");
|
|
87
|
+
const { values, errors, dirty, isSubmitted, setFieldValue, setFieldDirty } = form;
|
|
88
|
+
const fieldErrors = errors[name] || {};
|
|
89
|
+
const value = values[name] || "";
|
|
90
|
+
const isDirty = dirty.has(name);
|
|
91
|
+
const handleChange = useCallback2((value2) => {
|
|
92
|
+
if (typeof value2 === "string") {
|
|
93
|
+
setFieldValue(name, value2);
|
|
94
|
+
} else if (value2?.target?.value !== undefined) {
|
|
95
|
+
setFieldValue(name, value2.target.value);
|
|
96
|
+
}
|
|
97
|
+
if (!isDirty) {
|
|
98
|
+
setFieldDirty(name);
|
|
99
|
+
}
|
|
100
|
+
}, [name, isDirty, setFieldValue, setFieldDirty]);
|
|
101
|
+
const errorMessages = Object.entries(fieldErrors).map(([key, value2]) => {
|
|
102
|
+
if (!value2)
|
|
103
|
+
return;
|
|
104
|
+
const translation = translations[key];
|
|
105
|
+
return translation?.(value2);
|
|
106
|
+
}).filter(Boolean);
|
|
107
|
+
const contextValue = useMemo2(() => ({
|
|
108
|
+
errors: isSubmitted ? fieldErrors : {},
|
|
109
|
+
messages: errorMessages
|
|
110
|
+
}), [fieldErrors, isSubmitted, errorMessages]);
|
|
111
|
+
return /* @__PURE__ */ jsx2(FormFieldContext.Provider, {
|
|
112
|
+
value: contextValue,
|
|
113
|
+
children: render({
|
|
114
|
+
onChange: handleChange,
|
|
115
|
+
value
|
|
116
|
+
})
|
|
117
|
+
}, undefined, false, undefined, this);
|
|
118
|
+
}
|
|
119
|
+
// form/legacy_form_resolver.ts
|
|
2
120
|
function formResolver(schema) {
|
|
3
121
|
return async (data) => {
|
|
4
122
|
return {
|
|
@@ -7,6 +125,61 @@ function formResolver(schema) {
|
|
|
7
125
|
};
|
|
8
126
|
};
|
|
9
127
|
}
|
|
128
|
+
// form/message.tsx
|
|
129
|
+
import React3 from "react";
|
|
130
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
131
|
+
"use client";
|
|
132
|
+
var FormMessage = React3.forwardRef(({}, ref) => {
|
|
133
|
+
const { messages } = useFormField();
|
|
134
|
+
if (messages.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
137
|
+
className: "text-destructive",
|
|
138
|
+
ref,
|
|
139
|
+
children: messages[0]
|
|
140
|
+
}, undefined, false, undefined, this);
|
|
141
|
+
});
|
|
142
|
+
FormMessage.displayName = "FormMessage";
|
|
143
|
+
// form/test.tsx
|
|
144
|
+
import { object, string } from "@arcote.tech/arc";
|
|
145
|
+
import { jsx as jsx4, Fragment } from "react/jsx-runtime";
|
|
146
|
+
"use client";
|
|
147
|
+
function Test() {
|
|
148
|
+
const schema = object({
|
|
149
|
+
username: string().minLength(3).maxLength(10)
|
|
150
|
+
});
|
|
151
|
+
const handleSubmit = async (values) => {
|
|
152
|
+
console.log("Form submitted:", values);
|
|
153
|
+
};
|
|
154
|
+
return /* @__PURE__ */ jsx4(Form, {
|
|
155
|
+
schema,
|
|
156
|
+
onSubmit: handleSubmit,
|
|
157
|
+
render: ({ FormField: FormField2 }) => /* @__PURE__ */ jsx4("div", {
|
|
158
|
+
children: [
|
|
159
|
+
/* @__PURE__ */ jsx4(FormField2, {
|
|
160
|
+
name: "username",
|
|
161
|
+
translations: {
|
|
162
|
+
minLength: ({ minLength }) => `Username must be at least ${minLength} characters long`,
|
|
163
|
+
maxLength: ({ maxLength }) => `Username must be at most ${maxLength} characters long`
|
|
164
|
+
},
|
|
165
|
+
render: (field) => /* @__PURE__ */ jsx4(Fragment, {
|
|
166
|
+
children: [
|
|
167
|
+
/* @__PURE__ */ jsx4("input", {
|
|
168
|
+
className: "border border-gray-300 rounded-md p-2",
|
|
169
|
+
...field
|
|
170
|
+
}, undefined, false, undefined, this),
|
|
171
|
+
/* @__PURE__ */ jsx4(FormMessage, {}, undefined, false, undefined, this)
|
|
172
|
+
]
|
|
173
|
+
}, undefined, true, undefined, this)
|
|
174
|
+
}, undefined, false, undefined, this),
|
|
175
|
+
/* @__PURE__ */ jsx4("button", {
|
|
176
|
+
type: "submit",
|
|
177
|
+
children: "Submit"
|
|
178
|
+
}, undefined, false, undefined, this)
|
|
179
|
+
]
|
|
180
|
+
}, undefined, true, undefined, this)
|
|
181
|
+
}, undefined, false, undefined, this);
|
|
182
|
+
}
|
|
10
183
|
// idb.ts
|
|
11
184
|
import {
|
|
12
185
|
ArcCollection,
|
|
@@ -70,6 +243,8 @@ class IDBReadTransaction {
|
|
|
70
243
|
const keyPath = idbIndex.keyPath;
|
|
71
244
|
let keyRange;
|
|
72
245
|
const getKey = (obj) => {
|
|
246
|
+
if (!obj)
|
|
247
|
+
return;
|
|
73
248
|
const key = keyPath.map((key2) => obj[key2]);
|
|
74
249
|
if (key.some((value) => value === undefined))
|
|
75
250
|
return;
|
|
@@ -91,7 +266,6 @@ class IDBReadTransaction {
|
|
|
91
266
|
} else {
|
|
92
267
|
keyRange = IDBKeyRange.only(simpleKey);
|
|
93
268
|
}
|
|
94
|
-
console.log(keyRange);
|
|
95
269
|
const result = idbIndex.getAll(keyRange);
|
|
96
270
|
result.onsuccess = (e) => {
|
|
97
271
|
resolve(e.target.result);
|
|
@@ -165,21 +339,22 @@ import {
|
|
|
165
339
|
rtcClientFactory
|
|
166
340
|
} from "@arcote.tech/arc";
|
|
167
341
|
import {
|
|
168
|
-
createContext,
|
|
169
|
-
useContext,
|
|
170
|
-
useEffect,
|
|
171
|
-
useMemo,
|
|
342
|
+
createContext as createContext3,
|
|
343
|
+
useContext as useContext2,
|
|
344
|
+
useEffect as useEffect2,
|
|
345
|
+
useMemo as useMemo3,
|
|
172
346
|
useRef,
|
|
173
|
-
useState
|
|
347
|
+
useState as useState2
|
|
174
348
|
} from "react";
|
|
175
|
-
import { jsx } from "react/jsx-runtime";
|
|
349
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
350
|
+
"use client";
|
|
176
351
|
var reactModel = (arcContext, databaseName) => {
|
|
177
|
-
const LiveModelContext =
|
|
178
|
-
const LocalModelContext =
|
|
352
|
+
const LiveModelContext = createContext3(null);
|
|
353
|
+
const LocalModelContext = createContext3(null);
|
|
179
354
|
let modelMasterDataStorage = null;
|
|
180
355
|
return [
|
|
181
356
|
function LiveModelProvider(props) {
|
|
182
|
-
const dataStorage =
|
|
357
|
+
const dataStorage = useMemo3(() => {
|
|
183
358
|
if (typeof window === "undefined")
|
|
184
359
|
return null;
|
|
185
360
|
const dbAdapterPromise = idbAdapterFactory(databaseName, arcContext.version)(arcContext);
|
|
@@ -187,9 +362,9 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
187
362
|
modelMasterDataStorage = dataStorage2;
|
|
188
363
|
return dataStorage2;
|
|
189
364
|
}, [arcContext, databaseName, arcContext.version]);
|
|
190
|
-
const [syncProgress, setSyncProgress] =
|
|
191
|
-
const [syncDone, setSyncDone] =
|
|
192
|
-
|
|
365
|
+
const [syncProgress, setSyncProgress] = useState2([]);
|
|
366
|
+
const [syncDone, setSyncDone] = useState2(false);
|
|
367
|
+
useEffect2(() => {
|
|
193
368
|
if (typeof window === "undefined" || !dataStorage)
|
|
194
369
|
return;
|
|
195
370
|
const sync = async () => {
|
|
@@ -201,10 +376,10 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
201
376
|
sync();
|
|
202
377
|
}, [dataStorage]);
|
|
203
378
|
if (!dataStorage || !syncDone)
|
|
204
|
-
return /* @__PURE__ */
|
|
379
|
+
return /* @__PURE__ */ jsx5(props.syncView, {
|
|
205
380
|
progress: syncProgress
|
|
206
381
|
}, undefined, false, undefined, this);
|
|
207
|
-
return /* @__PURE__ */
|
|
382
|
+
return /* @__PURE__ */ jsx5(LiveModelContext.Provider, {
|
|
208
383
|
value: {
|
|
209
384
|
dataStorage,
|
|
210
385
|
client: props.client,
|
|
@@ -214,14 +389,14 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
214
389
|
}, undefined, false, undefined, this);
|
|
215
390
|
},
|
|
216
391
|
function LocalModelProvider({ children }) {
|
|
217
|
-
const parentContext =
|
|
392
|
+
const parentContext = useContext2(LiveModelContext);
|
|
218
393
|
if (!parentContext) {
|
|
219
394
|
throw new Error("LocalModelProvider must be used within a LiveModelProvider");
|
|
220
395
|
}
|
|
221
|
-
const [localContext] =
|
|
396
|
+
const [localContext] = useState2(() => ({
|
|
222
397
|
dataStorage: parentContext.dataStorage.fork()
|
|
223
398
|
}));
|
|
224
|
-
return /* @__PURE__ */
|
|
399
|
+
return /* @__PURE__ */ jsx5(LocalModelContext.Provider, {
|
|
225
400
|
value: {
|
|
226
401
|
dataStorage: localContext.dataStorage,
|
|
227
402
|
client: parentContext.client,
|
|
@@ -231,14 +406,14 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
231
406
|
}, undefined, false, undefined, this);
|
|
232
407
|
},
|
|
233
408
|
function useQuery(queryBuilderFn, dependencies = []) {
|
|
234
|
-
const context =
|
|
409
|
+
const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
|
|
235
410
|
if (!context) {
|
|
236
411
|
throw new Error("useQuery must be used within a ModelProvider");
|
|
237
412
|
}
|
|
238
|
-
const [result, setResult] =
|
|
239
|
-
const [loading, setLoading] =
|
|
413
|
+
const [result, setResult] = useState2(null);
|
|
414
|
+
const [loading, setLoading] = useState2(true);
|
|
240
415
|
const queryRef = useRef(null);
|
|
241
|
-
|
|
416
|
+
useEffect2(() => {
|
|
242
417
|
if (queryRef.current)
|
|
243
418
|
queryRef.current.unsubscribe();
|
|
244
419
|
const queryBuilder = arcContext.queryBuilder();
|
|
@@ -261,7 +436,7 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
261
436
|
return [result, loading];
|
|
262
437
|
},
|
|
263
438
|
function useCommands() {
|
|
264
|
-
const context =
|
|
439
|
+
const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
|
|
265
440
|
if (!context) {
|
|
266
441
|
throw new Error("useQuery must be used within a ModelProvider");
|
|
267
442
|
}
|
|
@@ -278,7 +453,7 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
278
453
|
return result;
|
|
279
454
|
},
|
|
280
455
|
function useLocalDataStorage() {
|
|
281
|
-
const context =
|
|
456
|
+
const context = useContext2(LocalModelContext);
|
|
282
457
|
if (!context) {
|
|
283
458
|
return null;
|
|
284
459
|
}
|
|
@@ -287,9 +462,16 @@ var reactModel = (arcContext, databaseName) => {
|
|
|
287
462
|
];
|
|
288
463
|
};
|
|
289
464
|
export {
|
|
465
|
+
useFormField,
|
|
290
466
|
reactModel,
|
|
291
467
|
idbAdapterFactory,
|
|
292
|
-
formResolver
|
|
468
|
+
formResolver,
|
|
469
|
+
Test,
|
|
470
|
+
FormMessage,
|
|
471
|
+
FormFieldContext,
|
|
472
|
+
FormField,
|
|
473
|
+
FormContext,
|
|
474
|
+
Form
|
|
293
475
|
};
|
|
294
476
|
|
|
295
|
-
//# debugId=
|
|
477
|
+
//# 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.30",
|
|
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.",
|