@arcote.tech/arc-react 0.0.29 → 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.
@@ -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
@@ -0,0 +1,6 @@
1
+ export * from "./field";
2
+ export * from "./form";
3
+ export * from "./legacy_form_resolver";
4
+ export * from "./message";
5
+ export * from "./test";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,6 @@
1
- import { type ArcObjectAny } from "@arcote.tech/arc";
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=form.d.ts.map
6
+ //# sourceMappingURL=legacy_form_resolver.d.ts.map
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ export declare const FormMessage: React.ForwardRefExoticComponent<React.RefAttributes<HTMLDivElement>>;
3
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1,2 @@
1
+ export declare function Test(): import("react/jsx-dev-runtime").JSX.Element;
2
+ //# sourceMappingURL=test.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./form.ts";
1
+ export * from "./form/";
2
2
  export * from "./idb.ts";
3
3
  export * from "./reactModel.tsx";
4
4
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,122 @@
1
- // form.ts
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,
@@ -166,21 +339,22 @@ import {
166
339
  rtcClientFactory
167
340
  } from "@arcote.tech/arc";
168
341
  import {
169
- createContext,
170
- useContext,
171
- useEffect,
172
- useMemo,
342
+ createContext as createContext3,
343
+ useContext as useContext2,
344
+ useEffect as useEffect2,
345
+ useMemo as useMemo3,
173
346
  useRef,
174
- useState
347
+ useState as useState2
175
348
  } from "react";
176
- import { jsx } from "react/jsx-runtime";
349
+ import { jsx as jsx5 } from "react/jsx-runtime";
350
+ "use client";
177
351
  var reactModel = (arcContext, databaseName) => {
178
- const LiveModelContext = createContext(null);
179
- const LocalModelContext = createContext(null);
352
+ const LiveModelContext = createContext3(null);
353
+ const LocalModelContext = createContext3(null);
180
354
  let modelMasterDataStorage = null;
181
355
  return [
182
356
  function LiveModelProvider(props) {
183
- const dataStorage = useMemo(() => {
357
+ const dataStorage = useMemo3(() => {
184
358
  if (typeof window === "undefined")
185
359
  return null;
186
360
  const dbAdapterPromise = idbAdapterFactory(databaseName, arcContext.version)(arcContext);
@@ -188,9 +362,9 @@ var reactModel = (arcContext, databaseName) => {
188
362
  modelMasterDataStorage = dataStorage2;
189
363
  return dataStorage2;
190
364
  }, [arcContext, databaseName, arcContext.version]);
191
- const [syncProgress, setSyncProgress] = useState([]);
192
- const [syncDone, setSyncDone] = useState(false);
193
- useEffect(() => {
365
+ const [syncProgress, setSyncProgress] = useState2([]);
366
+ const [syncDone, setSyncDone] = useState2(false);
367
+ useEffect2(() => {
194
368
  if (typeof window === "undefined" || !dataStorage)
195
369
  return;
196
370
  const sync = async () => {
@@ -202,10 +376,10 @@ var reactModel = (arcContext, databaseName) => {
202
376
  sync();
203
377
  }, [dataStorage]);
204
378
  if (!dataStorage || !syncDone)
205
- return /* @__PURE__ */ jsx(props.syncView, {
379
+ return /* @__PURE__ */ jsx5(props.syncView, {
206
380
  progress: syncProgress
207
381
  }, undefined, false, undefined, this);
208
- return /* @__PURE__ */ jsx(LiveModelContext.Provider, {
382
+ return /* @__PURE__ */ jsx5(LiveModelContext.Provider, {
209
383
  value: {
210
384
  dataStorage,
211
385
  client: props.client,
@@ -215,14 +389,14 @@ var reactModel = (arcContext, databaseName) => {
215
389
  }, undefined, false, undefined, this);
216
390
  },
217
391
  function LocalModelProvider({ children }) {
218
- const parentContext = useContext(LiveModelContext);
392
+ const parentContext = useContext2(LiveModelContext);
219
393
  if (!parentContext) {
220
394
  throw new Error("LocalModelProvider must be used within a LiveModelProvider");
221
395
  }
222
- const [localContext] = useState(() => ({
396
+ const [localContext] = useState2(() => ({
223
397
  dataStorage: parentContext.dataStorage.fork()
224
398
  }));
225
- return /* @__PURE__ */ jsx(LocalModelContext.Provider, {
399
+ return /* @__PURE__ */ jsx5(LocalModelContext.Provider, {
226
400
  value: {
227
401
  dataStorage: localContext.dataStorage,
228
402
  client: parentContext.client,
@@ -232,14 +406,14 @@ var reactModel = (arcContext, databaseName) => {
232
406
  }, undefined, false, undefined, this);
233
407
  },
234
408
  function useQuery(queryBuilderFn, dependencies = []) {
235
- const context = useContext(LocalModelContext) || useContext(LiveModelContext);
409
+ const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
236
410
  if (!context) {
237
411
  throw new Error("useQuery must be used within a ModelProvider");
238
412
  }
239
- const [result, setResult] = useState(null);
240
- const [loading, setLoading] = useState(true);
413
+ const [result, setResult] = useState2(null);
414
+ const [loading, setLoading] = useState2(true);
241
415
  const queryRef = useRef(null);
242
- useEffect(() => {
416
+ useEffect2(() => {
243
417
  if (queryRef.current)
244
418
  queryRef.current.unsubscribe();
245
419
  const queryBuilder = arcContext.queryBuilder();
@@ -262,7 +436,7 @@ var reactModel = (arcContext, databaseName) => {
262
436
  return [result, loading];
263
437
  },
264
438
  function useCommands() {
265
- const context = useContext(LocalModelContext) || useContext(LiveModelContext);
439
+ const context = useContext2(LocalModelContext) || useContext2(LiveModelContext);
266
440
  if (!context) {
267
441
  throw new Error("useQuery must be used within a ModelProvider");
268
442
  }
@@ -279,7 +453,7 @@ var reactModel = (arcContext, databaseName) => {
279
453
  return result;
280
454
  },
281
455
  function useLocalDataStorage() {
282
- const context = useContext(LocalModelContext);
456
+ const context = useContext2(LocalModelContext);
283
457
  if (!context) {
284
458
  return null;
285
459
  }
@@ -288,9 +462,16 @@ var reactModel = (arcContext, databaseName) => {
288
462
  ];
289
463
  };
290
464
  export {
465
+ useFormField,
291
466
  reactModel,
292
467
  idbAdapterFactory,
293
- formResolver
468
+ formResolver,
469
+ Test,
470
+ FormMessage,
471
+ FormFieldContext,
472
+ FormField,
473
+ FormContext,
474
+ Form
294
475
  };
295
476
 
296
- //# debugId=EB08DA6873B8C68E64756E2164756E21
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.29",
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.",