@btst/plugin-form-builder 1.0.0 → 1.2.0-beta.1
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/api/index.cjs +1 -0
- package/dist/api/index.d.cts +1 -1
- package/dist/api/index.d.mts +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.mjs +1 -1
- package/dist/api/operations.cjs +64 -4
- package/dist/api/operations.mjs +64 -5
- package/dist/api/plugin.cjs +8 -0
- package/dist/api/plugin.mjs +8 -0
- package/dist/api/submissions-csv.cjs +69 -0
- package/dist/api/submissions-csv.mjs +64 -0
- package/dist/client/components/forms/form-renderer.cjs +31 -2
- package/dist/client/components/forms/form-renderer.mjs +31 -2
- package/dist/client/components/forms/media-file-field.cjs +48 -0
- package/dist/client/components/forms/media-file-field.mjs +46 -0
- package/dist/client/components/index.d.cts +4 -2
- package/dist/client/components/index.d.mts +4 -2
- package/dist/client/components/index.d.ts +4 -2
- package/dist/client/components/pages/ai-schema.cjs +120 -0
- package/dist/client/components/pages/ai-schema.mjs +118 -0
- package/dist/client/components/pages/form-builder-page.internal.cjs +40 -1
- package/dist/client/components/pages/form-builder-page.internal.mjs +41 -2
- package/dist/client/components/pages/form-list-page.internal.cjs +1 -0
- package/dist/client/components/pages/form-list-page.internal.mjs +1 -0
- package/dist/client/components/pages/submissions-page.internal.cjs +40 -0
- package/dist/client/components/pages/submissions-page.internal.mjs +41 -1
- package/dist/client/components/shared/pagination.cjs +19 -0
- package/dist/client/components/shared/pagination.mjs +20 -1
- package/dist/client/hooks/form-builder-hooks.cjs +4 -0
- package/dist/client/hooks/form-builder-hooks.mjs +4 -1
- package/dist/client/hooks/index.cjs +1 -0
- package/dist/client/hooks/index.d.cts +7 -2
- package/dist/client/hooks/index.d.mts +7 -2
- package/dist/client/hooks/index.d.ts +7 -2
- package/dist/client/hooks/index.mjs +1 -1
- package/dist/client/index.d.cts +42 -3
- package/dist/client/index.d.mts +42 -3
- package/dist/client/index.d.ts +42 -3
- package/dist/permissions.cjs +8 -0
- package/dist/permissions.d.cts +6 -0
- package/dist/permissions.d.mts +6 -0
- package/dist/permissions.d.ts +6 -0
- package/dist/permissions.mjs +8 -0
- package/dist/query-keys.cjs +7 -0
- package/dist/query-keys.d.cts +29 -1
- package/dist/query-keys.d.mts +29 -1
- package/dist/query-keys.d.ts +29 -1
- package/dist/query-keys.mjs +7 -0
- package/dist/shared/{plugin-form-builder.CSP5HR2O.d.ts → plugin-form-builder.65tLgfcd.d.ts} +46 -29
- package/dist/shared/{plugin-form-builder.Bbl9NufB.d.cts → plugin-form-builder.DXdOj9vM.d.cts} +46 -29
- package/dist/shared/{plugin-form-builder.lp98rFNl.d.mts → plugin-form-builder.PWs25HN1.d.mts} +46 -29
- package/dist/shared/{plugin-form-builder.DWrQ3lRX.d.ts → plugin-form-builder.sdjcoJcH.d.cts} +15 -1
- package/dist/shared/{plugin-form-builder.DWrQ3lRX.d.cts → plugin-form-builder.sdjcoJcH.d.mts} +15 -1
- package/dist/shared/{plugin-form-builder.DWrQ3lRX.d.mts → plugin-form-builder.sdjcoJcH.d.ts} +15 -1
- package/dist/ui/src/components/auto-form/config.cjs +2 -0
- package/dist/ui/src/components/auto-form/config.mjs +2 -0
- package/dist/ui/src/components/auto-form/fields/file.cjs +68 -0
- package/dist/ui/src/components/auto-form/fields/file.mjs +63 -0
- package/dist/ui/src/components/auto-form/fields/object.cjs +9 -2
- package/dist/ui/src/components/auto-form/fields/object.mjs +9 -2
- package/dist/ui/src/components/auto-form/helpers.cjs +9 -0
- package/dist/ui/src/components/auto-form/helpers.mjs +9 -0
- package/dist/ui/src/components/auto-form/index.cjs +29 -4
- package/dist/ui/src/components/auto-form/index.mjs +31 -6
- package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +34 -29
- package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +35 -30
- package/dist/ui/src/components/form-builder/canvas.cjs +9 -0
- package/dist/ui/src/components/form-builder/canvas.mjs +9 -0
- package/dist/ui/src/components/form-builder/components/index.cjs +12 -0
- package/dist/ui/src/components/form-builder/components/index.mjs +12 -1
- package/dist/ui/src/components/form-builder/condition-editor.cjs +73 -0
- package/dist/ui/src/components/form-builder/condition-editor.mjs +71 -0
- package/dist/ui/src/components/form-builder/edit-field-dialog.cjs +6 -2
- package/dist/ui/src/components/form-builder/edit-field-dialog.mjs +6 -2
- package/dist/ui/src/components/form-builder/index.cjs +6 -1
- package/dist/ui/src/components/form-builder/index.mjs +6 -1
- package/dist/ui/src/components/form-builder/schema-utils.cjs +2 -0
- package/dist/ui/src/components/form-builder/schema-utils.mjs +2 -0
- package/dist/ui/src/components/form.mjs +1 -1
- package/dist/ui/src/lib/form-visibility.cjs +20 -0
- package/dist/ui/src/lib/form-visibility.mjs +18 -0
- package/dist/ui/src/lib/schema-converter.cjs +101 -0
- package/dist/ui/src/lib/schema-converter.mjs +100 -1
- package/package.json +4 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import AutoFormFile from './fields/file.mjs';
|
|
1
2
|
import AutoFormCheckbox from './fields/checkbox.mjs';
|
|
2
3
|
import AutoFormDate from './fields/date.mjs';
|
|
3
4
|
import AutoFormEnum from './fields/enum.mjs';
|
|
@@ -8,6 +9,7 @@ import AutoFormSwitch from './fields/switch.mjs';
|
|
|
8
9
|
import AutoFormTextarea from './fields/textarea.mjs';
|
|
9
10
|
|
|
10
11
|
const INPUT_COMPONENTS = {
|
|
12
|
+
file: AutoFormFile,
|
|
11
13
|
checkbox: AutoFormCheckbox,
|
|
12
14
|
date: AutoFormDate,
|
|
13
15
|
select: AutoFormEnum,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
const jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
const React = require('react');
|
|
8
|
+
const reactHookForm = require('react-hook-form');
|
|
9
|
+
const form = require('../../form.cjs');
|
|
10
|
+
const input = require('../../input.cjs');
|
|
11
|
+
const label = require('../common/label.cjs');
|
|
12
|
+
const tooltip = require('../common/tooltip.cjs');
|
|
13
|
+
|
|
14
|
+
const FileUploadStatusContext = React.createContext(() => {
|
|
15
|
+
});
|
|
16
|
+
function AutoFormFile({ field, fieldProps, fieldConfigItem, label: label$1, isRequired }) {
|
|
17
|
+
const [uploading, setUploading] = React.useState(false);
|
|
18
|
+
const [error, setError] = React.useState(null);
|
|
19
|
+
const form$1 = reactHookForm.useFormContext();
|
|
20
|
+
const reportStatus = React.useContext(FileUploadStatusContext);
|
|
21
|
+
React.useEffect(() => () => reportStatus(field.name, null), [field.name, reportStatus]);
|
|
22
|
+
const onUpload = fieldConfigItem.onUpload;
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(form.FormItem, { children: [
|
|
24
|
+
/* @__PURE__ */ jsxRuntime.jsx(label, { label: fieldConfigItem.label || label$1, isRequired }),
|
|
25
|
+
/* @__PURE__ */ jsxRuntime.jsx(form.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26
|
+
input.Input,
|
|
27
|
+
{
|
|
28
|
+
type: "file",
|
|
29
|
+
name: field.name,
|
|
30
|
+
onBlur: field.onBlur,
|
|
31
|
+
accept: fieldProps.accept,
|
|
32
|
+
disabled: fieldProps.disabled || uploading || !onUpload,
|
|
33
|
+
required: isRequired && !field.value,
|
|
34
|
+
onChange: async (event) => {
|
|
35
|
+
const input = event.currentTarget;
|
|
36
|
+
const file = input.files?.[0];
|
|
37
|
+
if (!file || !onUpload) return;
|
|
38
|
+
setUploading(true);
|
|
39
|
+
reportStatus(field.name, "uploading");
|
|
40
|
+
setError(null);
|
|
41
|
+
form$1.setError(field.name, { type: "upload", message: "Upload in progress" });
|
|
42
|
+
try {
|
|
43
|
+
const url = await onUpload(file);
|
|
44
|
+
if (typeof url !== "string" || !url.trim()) throw new Error("Upload must return a URL string");
|
|
45
|
+
field.onChange(url);
|
|
46
|
+
form$1.clearErrors(field.name);
|
|
47
|
+
reportStatus(field.name, null);
|
|
48
|
+
} catch (error2) {
|
|
49
|
+
const message = error2 instanceof Error ? error2.message : "Upload failed";
|
|
50
|
+
setError(message);
|
|
51
|
+
reportStatus(field.name, "failed");
|
|
52
|
+
form$1.setError(field.name, { type: "upload", message });
|
|
53
|
+
} finally {
|
|
54
|
+
setUploading(false);
|
|
55
|
+
input.value = "";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
) }),
|
|
60
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", role: "status", children: uploading ? "Uploading\u2026" : !onUpload ? "File uploads are not configured." : field.value ? "File uploaded" : "" }),
|
|
61
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }),
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsx(tooltip, { fieldConfigItem }),
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx(form.FormMessage, {})
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
exports.FileUploadStatusContext = FileUploadStatusContext;
|
|
68
|
+
exports.default = AutoFormFile;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useState, useContext, useEffect, createContext } from 'react';
|
|
4
|
+
import { useFormContext } from 'react-hook-form';
|
|
5
|
+
import { FormItem, FormControl, FormMessage } from '../../form.mjs';
|
|
6
|
+
import { Input } from '../../input.mjs';
|
|
7
|
+
import AutoFormLabel from '../common/label.mjs';
|
|
8
|
+
import AutoFormTooltip from '../common/tooltip.mjs';
|
|
9
|
+
|
|
10
|
+
const FileUploadStatusContext = createContext(() => {
|
|
11
|
+
});
|
|
12
|
+
function AutoFormFile({ field, fieldProps, fieldConfigItem, label, isRequired }) {
|
|
13
|
+
const [uploading, setUploading] = useState(false);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const form = useFormContext();
|
|
16
|
+
const reportStatus = useContext(FileUploadStatusContext);
|
|
17
|
+
useEffect(() => () => reportStatus(field.name, null), [field.name, reportStatus]);
|
|
18
|
+
const onUpload = fieldConfigItem.onUpload;
|
|
19
|
+
return /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
20
|
+
/* @__PURE__ */ jsx(AutoFormLabel, { label: fieldConfigItem.label || label, isRequired }),
|
|
21
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
22
|
+
Input,
|
|
23
|
+
{
|
|
24
|
+
type: "file",
|
|
25
|
+
name: field.name,
|
|
26
|
+
onBlur: field.onBlur,
|
|
27
|
+
accept: fieldProps.accept,
|
|
28
|
+
disabled: fieldProps.disabled || uploading || !onUpload,
|
|
29
|
+
required: isRequired && !field.value,
|
|
30
|
+
onChange: async (event) => {
|
|
31
|
+
const input = event.currentTarget;
|
|
32
|
+
const file = input.files?.[0];
|
|
33
|
+
if (!file || !onUpload) return;
|
|
34
|
+
setUploading(true);
|
|
35
|
+
reportStatus(field.name, "uploading");
|
|
36
|
+
setError(null);
|
|
37
|
+
form.setError(field.name, { type: "upload", message: "Upload in progress" });
|
|
38
|
+
try {
|
|
39
|
+
const url = await onUpload(file);
|
|
40
|
+
if (typeof url !== "string" || !url.trim()) throw new Error("Upload must return a URL string");
|
|
41
|
+
field.onChange(url);
|
|
42
|
+
form.clearErrors(field.name);
|
|
43
|
+
reportStatus(field.name, null);
|
|
44
|
+
} catch (error2) {
|
|
45
|
+
const message = error2 instanceof Error ? error2.message : "Upload failed";
|
|
46
|
+
setError(message);
|
|
47
|
+
reportStatus(field.name, "failed");
|
|
48
|
+
form.setError(field.name, { type: "upload", message });
|
|
49
|
+
} finally {
|
|
50
|
+
setUploading(false);
|
|
51
|
+
input.value = "";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
) }),
|
|
56
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", role: "status", children: uploading ? "Uploading\u2026" : !onUpload ? "File uploads are not configured." : field.value ? "File uploaded" : "" }),
|
|
57
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }),
|
|
58
|
+
/* @__PURE__ */ jsx(AutoFormTooltip, { fieldConfigItem }),
|
|
59
|
+
/* @__PURE__ */ jsx(FormMessage, {})
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { FileUploadStatusContext, AutoFormFile as default };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
const schemaConverter = require('../../../lib/schema-converter.cjs');
|
|
5
|
+
const formVisibility = require('../../../lib/form-visibility.cjs');
|
|
4
6
|
const accordion = require('../../accordion.cjs');
|
|
5
7
|
const form = require('../../form.cjs');
|
|
6
8
|
const reactHookForm = require('react-hook-form');
|
|
@@ -32,7 +34,8 @@ function AutoFormObject({
|
|
|
32
34
|
form: form$1,
|
|
33
35
|
fieldConfig,
|
|
34
36
|
path = [],
|
|
35
|
-
dependencies: dependencies$1 = []
|
|
37
|
+
dependencies: dependencies$1 = [],
|
|
38
|
+
visibilityValues
|
|
36
39
|
}) {
|
|
37
40
|
const { watch } = reactHookForm.useFormContext();
|
|
38
41
|
if (!schema) {
|
|
@@ -56,6 +59,9 @@ function AutoFormObject({
|
|
|
56
59
|
return item;
|
|
57
60
|
};
|
|
58
61
|
const sortedFieldKeys = helpers.sortFieldsByOrder(fieldConfig, Object.keys(shape));
|
|
62
|
+
const siblingValues = visibilityValues ?? schemaConverter.resolveVisibleSchema({
|
|
63
|
+
properties: Object.fromEntries(Object.keys(shape).map((name) => [name, { showIf: fieldConfig?.[name]?.showIf }]))
|
|
64
|
+
}, path.length ? watch(path.join(".")) ?? {} : watch()).data;
|
|
59
65
|
return /* @__PURE__ */ jsxRuntime.jsx(accordion.Accordion, { type: "multiple", className: "space-y-5 border-none", children: sortedFieldKeys.map((name) => {
|
|
60
66
|
let item = shape[name];
|
|
61
67
|
item = handleIfZodNumber(item);
|
|
@@ -68,7 +74,7 @@ function AutoFormObject({
|
|
|
68
74
|
isRequired: isRequiredByDependency,
|
|
69
75
|
overrideOptions
|
|
70
76
|
} = dependencies(dependencies$1, name, watch);
|
|
71
|
-
if (isHidden) {
|
|
77
|
+
if (isHidden || !formVisibility.matchesShowIf(fieldConfig?.[name]?.showIf, siblingValues)) {
|
|
72
78
|
return null;
|
|
73
79
|
}
|
|
74
80
|
if (zodBaseType === "ZodObject") {
|
|
@@ -202,6 +208,7 @@ function AutoFormObject({
|
|
|
202
208
|
const fieldProps = {
|
|
203
209
|
...helpers.zodToHtmlInputProps(item),
|
|
204
210
|
...field,
|
|
211
|
+
onChange: zodBaseType === "ZodNumber" && inputType === "number" ? (event) => field.onChange(event.target.value === "" ? void 0 : Number(event.target.value)) : field.onChange,
|
|
205
212
|
...safeInputProps,
|
|
206
213
|
disabled: safeInputProps.disabled || isDisabled,
|
|
207
214
|
ref: void 0,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { resolveVisibleSchema } from '../../../lib/schema-converter.mjs';
|
|
3
|
+
import { matchesShowIf } from '../../../lib/form-visibility.mjs';
|
|
2
4
|
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '../../accordion.mjs';
|
|
3
5
|
import { FormField } from '../../form.mjs';
|
|
4
6
|
import { useFormContext } from 'react-hook-form';
|
|
@@ -16,7 +18,8 @@ function AutoFormObject({
|
|
|
16
18
|
form,
|
|
17
19
|
fieldConfig,
|
|
18
20
|
path = [],
|
|
19
|
-
dependencies = []
|
|
21
|
+
dependencies = [],
|
|
22
|
+
visibilityValues
|
|
20
23
|
}) {
|
|
21
24
|
const { watch } = useFormContext();
|
|
22
25
|
if (!schema) {
|
|
@@ -40,6 +43,9 @@ function AutoFormObject({
|
|
|
40
43
|
return item;
|
|
41
44
|
};
|
|
42
45
|
const sortedFieldKeys = sortFieldsByOrder(fieldConfig, Object.keys(shape));
|
|
46
|
+
const siblingValues = visibilityValues ?? resolveVisibleSchema({
|
|
47
|
+
properties: Object.fromEntries(Object.keys(shape).map((name) => [name, { showIf: fieldConfig?.[name]?.showIf }]))
|
|
48
|
+
}, path.length ? watch(path.join(".")) ?? {} : watch()).data;
|
|
43
49
|
return /* @__PURE__ */ jsx(Accordion, { type: "multiple", className: "space-y-5 border-none", children: sortedFieldKeys.map((name) => {
|
|
44
50
|
let item = shape[name];
|
|
45
51
|
item = handleIfZodNumber(item);
|
|
@@ -52,7 +58,7 @@ function AutoFormObject({
|
|
|
52
58
|
isRequired: isRequiredByDependency,
|
|
53
59
|
overrideOptions
|
|
54
60
|
} = resolveDependencies(dependencies, name, watch);
|
|
55
|
-
if (isHidden) {
|
|
61
|
+
if (isHidden || !matchesShowIf(fieldConfig?.[name]?.showIf, siblingValues)) {
|
|
56
62
|
return null;
|
|
57
63
|
}
|
|
58
64
|
if (zodBaseType === "ZodObject") {
|
|
@@ -186,6 +192,7 @@ function AutoFormObject({
|
|
|
186
192
|
const fieldProps = {
|
|
187
193
|
...zodToHtmlInputProps(item),
|
|
188
194
|
...field,
|
|
195
|
+
onChange: zodBaseType === "ZodNumber" && inputType === "number" ? (event) => field.onChange(event.target.value === "" ? void 0 : Number(event.target.value)) : field.onChange,
|
|
189
196
|
...safeInputProps,
|
|
190
197
|
disabled: safeInputProps.disabled || isDisabled,
|
|
191
198
|
ref: void 0,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
const schemaConverter = require('../../lib/schema-converter.cjs');
|
|
4
5
|
require('zod');
|
|
5
6
|
|
|
6
7
|
const BUILTIN_FIELD_TYPES = [
|
|
8
|
+
"file",
|
|
7
9
|
"checkbox",
|
|
8
10
|
"date",
|
|
9
11
|
"select",
|
|
@@ -24,6 +26,8 @@ function beautifyObjectName(string) {
|
|
|
24
26
|
}
|
|
25
27
|
function getBaseSchema(schema) {
|
|
26
28
|
if (!schema) return null;
|
|
29
|
+
const display = schemaConverter.getFormDisplaySchema(schema);
|
|
30
|
+
if (display) return display;
|
|
27
31
|
const def = schema._zod?.def;
|
|
28
32
|
if (!def) return schema;
|
|
29
33
|
if (def.innerType) {
|
|
@@ -107,6 +111,8 @@ function getDefaultValues(schema, fieldConfig) {
|
|
|
107
111
|
}
|
|
108
112
|
function getObjectFormSchema(schema) {
|
|
109
113
|
if (!schema) return schema;
|
|
114
|
+
const displaySchema = schemaConverter.getFormDisplaySchema(schema);
|
|
115
|
+
if (displaySchema) return displaySchema;
|
|
110
116
|
const def = schema._zod?.def;
|
|
111
117
|
if (!def) return schema;
|
|
112
118
|
if (def.type === "pipe") {
|
|
@@ -176,6 +182,7 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
|
|
|
176
182
|
if (!properties) return fieldConfig;
|
|
177
183
|
for (const [key, value] of Object.entries(properties)) {
|
|
178
184
|
const config = {};
|
|
185
|
+
if (value.showIf) config.showIf = value.showIf;
|
|
179
186
|
if (value.label) {
|
|
180
187
|
config.label = value.label;
|
|
181
188
|
} else if (value.title) {
|
|
@@ -218,6 +225,8 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
|
|
|
218
225
|
"inputProps",
|
|
219
226
|
"fieldType",
|
|
220
227
|
"renderParent",
|
|
228
|
+
"showIf",
|
|
229
|
+
"onUpload",
|
|
221
230
|
"order"
|
|
222
231
|
]);
|
|
223
232
|
if (value.properties) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { getFormDisplaySchema } from '../../lib/schema-converter.mjs';
|
|
2
3
|
import 'zod';
|
|
3
4
|
|
|
4
5
|
const BUILTIN_FIELD_TYPES = [
|
|
6
|
+
"file",
|
|
5
7
|
"checkbox",
|
|
6
8
|
"date",
|
|
7
9
|
"select",
|
|
@@ -22,6 +24,8 @@ function beautifyObjectName(string) {
|
|
|
22
24
|
}
|
|
23
25
|
function getBaseSchema(schema) {
|
|
24
26
|
if (!schema) return null;
|
|
27
|
+
const display = getFormDisplaySchema(schema);
|
|
28
|
+
if (display) return display;
|
|
25
29
|
const def = schema._zod?.def;
|
|
26
30
|
if (!def) return schema;
|
|
27
31
|
if (def.innerType) {
|
|
@@ -105,6 +109,8 @@ function getDefaultValues(schema, fieldConfig) {
|
|
|
105
109
|
}
|
|
106
110
|
function getObjectFormSchema(schema) {
|
|
107
111
|
if (!schema) return schema;
|
|
112
|
+
const displaySchema = getFormDisplaySchema(schema);
|
|
113
|
+
if (displaySchema) return displaySchema;
|
|
108
114
|
const def = schema._zod?.def;
|
|
109
115
|
if (!def) return schema;
|
|
110
116
|
if (def.type === "pipe") {
|
|
@@ -174,6 +180,7 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
|
|
|
174
180
|
if (!properties) return fieldConfig;
|
|
175
181
|
for (const [key, value] of Object.entries(properties)) {
|
|
176
182
|
const config = {};
|
|
183
|
+
if (value.showIf) config.showIf = value.showIf;
|
|
177
184
|
if (value.label) {
|
|
178
185
|
config.label = value.label;
|
|
179
186
|
} else if (value.title) {
|
|
@@ -216,6 +223,8 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
|
|
|
216
223
|
"inputProps",
|
|
217
224
|
"fieldType",
|
|
218
225
|
"renderParent",
|
|
226
|
+
"showIf",
|
|
227
|
+
"onUpload",
|
|
219
228
|
"order"
|
|
220
229
|
]);
|
|
221
230
|
if (value.properties) {
|
|
@@ -5,9 +5,11 @@ const jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
const form = require('../form.cjs');
|
|
6
6
|
const React = require('react');
|
|
7
7
|
const reactHookForm = require('react-hook-form');
|
|
8
|
+
const schemaConverter = require('../../lib/schema-converter.cjs');
|
|
8
9
|
require('../button.cjs');
|
|
9
10
|
const utils = require('../../lib/utils.cjs');
|
|
10
11
|
const zod = require('@hookform/resolvers/zod');
|
|
12
|
+
const file = require('./fields/file.cjs');
|
|
11
13
|
const object = require('./fields/object.cjs');
|
|
12
14
|
const helpers = require('./helpers.cjs');
|
|
13
15
|
|
|
@@ -34,6 +36,23 @@ function AutoForm({
|
|
|
34
36
|
defaultValues: defaultValues ?? void 0,
|
|
35
37
|
values: valuesProp
|
|
36
38
|
});
|
|
39
|
+
const [uploadStatuses, setUploadStatuses] = React.useState({});
|
|
40
|
+
const reportUploadStatus = React.useCallback((name, status) => {
|
|
41
|
+
setUploadStatuses((previous) => {
|
|
42
|
+
if (previous[name] === status || !status && !(name in previous)) return previous;
|
|
43
|
+
const next = { ...previous };
|
|
44
|
+
if (status) next[name] = status;
|
|
45
|
+
else Reflect.deleteProperty(next, name);
|
|
46
|
+
return next;
|
|
47
|
+
});
|
|
48
|
+
}, []);
|
|
49
|
+
const uploading = Object.values(uploadStatuses).includes("uploading");
|
|
50
|
+
const uploadBlocked = Object.keys(uploadStatuses).length > 0;
|
|
51
|
+
const watchedValues = reactHookForm.useWatch({ control: form$1.control });
|
|
52
|
+
const metadata = formSchema.meta();
|
|
53
|
+
const visibility = metadata?.properties ? schemaConverter.resolveVisibleSchema(metadata, watchedValues) : void 0;
|
|
54
|
+
const visibleKeys = visibility ? Object.keys(visibility.schema.properties ?? {}) : null;
|
|
55
|
+
const visibleObjectSchema = visibleKeys ? objectFormSchema.pick(Object.fromEntries(visibleKeys.map((key) => [key, true]))) : objectFormSchema;
|
|
37
56
|
const [schemaSubmitError, setSchemaSubmitError] = React.useState(null);
|
|
38
57
|
function onSubmit(values) {
|
|
39
58
|
setSchemaSubmitError(null);
|
|
@@ -58,18 +77,24 @@ function AutoForm({
|
|
|
58
77
|
return () => subscription.unsubscribe();
|
|
59
78
|
}, [form$1, formSchema, onValuesChangeProp, onParsedValuesChange]);
|
|
60
79
|
const renderChildren = typeof children === "function" ? children(form$1.formState) : children;
|
|
61
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsx(form.Form, { ...form$1, children: /* @__PURE__ */ jsxRuntime.
|
|
80
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsx(form.Form, { ...form$1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
62
81
|
"form",
|
|
63
82
|
{
|
|
64
83
|
onSubmit: (e) => {
|
|
84
|
+
if (uploadBlocked) {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
setSchemaSubmitError(uploading ? "Wait for the upload to finish." : "Retry the failed file upload before submitting.");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
65
89
|
form$1.handleSubmit(onSubmit, () => setSchemaSubmitError(null))(e);
|
|
66
90
|
},
|
|
67
91
|
className: utils.cn("space-y-5", className),
|
|
68
|
-
children: [
|
|
92
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(file.FileUploadStatusContext.Provider, { value: reportUploadStatus, children: /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { disabled: uploading, className: "contents", children: [
|
|
69
93
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
70
94
|
object,
|
|
71
95
|
{
|
|
72
|
-
schema:
|
|
96
|
+
schema: visibleObjectSchema,
|
|
97
|
+
visibilityValues: visibility?.data,
|
|
73
98
|
form: form$1,
|
|
74
99
|
dependencies,
|
|
75
100
|
fieldConfig
|
|
@@ -84,7 +109,7 @@ function AutoForm({
|
|
|
84
109
|
}
|
|
85
110
|
) : null,
|
|
86
111
|
renderChildren
|
|
87
|
-
]
|
|
112
|
+
] }) })
|
|
88
113
|
}
|
|
89
114
|
) }) });
|
|
90
115
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { Form } from '../form.mjs';
|
|
4
|
-
import React__default, { useState } from 'react';
|
|
5
|
-
import { useForm } from 'react-hook-form';
|
|
4
|
+
import React__default, { useState, useCallback } from 'react';
|
|
5
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
6
|
+
import { resolveVisibleSchema } from '../../lib/schema-converter.mjs';
|
|
6
7
|
import '../button.mjs';
|
|
7
8
|
import { cn } from '../../lib/utils.mjs';
|
|
8
9
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
10
|
+
import { FileUploadStatusContext } from './fields/file.mjs';
|
|
9
11
|
import AutoFormObject from './fields/object.mjs';
|
|
10
12
|
import { getObjectFormSchema, getDefaultValues } from './helpers.mjs';
|
|
11
13
|
|
|
@@ -28,6 +30,23 @@ function AutoForm({
|
|
|
28
30
|
defaultValues: defaultValues ?? void 0,
|
|
29
31
|
values: valuesProp
|
|
30
32
|
});
|
|
33
|
+
const [uploadStatuses, setUploadStatuses] = useState({});
|
|
34
|
+
const reportUploadStatus = useCallback((name, status) => {
|
|
35
|
+
setUploadStatuses((previous) => {
|
|
36
|
+
if (previous[name] === status || !status && !(name in previous)) return previous;
|
|
37
|
+
const next = { ...previous };
|
|
38
|
+
if (status) next[name] = status;
|
|
39
|
+
else Reflect.deleteProperty(next, name);
|
|
40
|
+
return next;
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
43
|
+
const uploading = Object.values(uploadStatuses).includes("uploading");
|
|
44
|
+
const uploadBlocked = Object.keys(uploadStatuses).length > 0;
|
|
45
|
+
const watchedValues = useWatch({ control: form.control });
|
|
46
|
+
const metadata = formSchema.meta();
|
|
47
|
+
const visibility = metadata?.properties ? resolveVisibleSchema(metadata, watchedValues) : void 0;
|
|
48
|
+
const visibleKeys = visibility ? Object.keys(visibility.schema.properties ?? {}) : null;
|
|
49
|
+
const visibleObjectSchema = visibleKeys ? objectFormSchema.pick(Object.fromEntries(visibleKeys.map((key) => [key, true]))) : objectFormSchema;
|
|
31
50
|
const [schemaSubmitError, setSchemaSubmitError] = useState(null);
|
|
32
51
|
function onSubmit(values) {
|
|
33
52
|
setSchemaSubmitError(null);
|
|
@@ -52,18 +71,24 @@ function AutoForm({
|
|
|
52
71
|
return () => subscription.unsubscribe();
|
|
53
72
|
}, [form, formSchema, onValuesChangeProp, onParsedValuesChange]);
|
|
54
73
|
const renderChildren = typeof children === "function" ? children(form.formState) : children;
|
|
55
|
-
return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */
|
|
74
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */ jsx(
|
|
56
75
|
"form",
|
|
57
76
|
{
|
|
58
77
|
onSubmit: (e) => {
|
|
78
|
+
if (uploadBlocked) {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
setSchemaSubmitError(uploading ? "Wait for the upload to finish." : "Retry the failed file upload before submitting.");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
59
83
|
form.handleSubmit(onSubmit, () => setSchemaSubmitError(null))(e);
|
|
60
84
|
},
|
|
61
85
|
className: cn("space-y-5", className),
|
|
62
|
-
children: [
|
|
86
|
+
children: /* @__PURE__ */ jsx(FileUploadStatusContext.Provider, { value: reportUploadStatus, children: /* @__PURE__ */ jsxs("fieldset", { disabled: uploading, className: "contents", children: [
|
|
63
87
|
/* @__PURE__ */ jsx(
|
|
64
88
|
AutoFormObject,
|
|
65
89
|
{
|
|
66
|
-
schema:
|
|
90
|
+
schema: visibleObjectSchema,
|
|
91
|
+
visibilityValues: visibility?.data,
|
|
67
92
|
form,
|
|
68
93
|
dependencies,
|
|
69
94
|
fieldConfig
|
|
@@ -78,7 +103,7 @@ function AutoForm({
|
|
|
78
103
|
}
|
|
79
104
|
) : null,
|
|
80
105
|
renderChildren
|
|
81
|
-
]
|
|
106
|
+
] }) })
|
|
82
107
|
}
|
|
83
108
|
) }) });
|
|
84
109
|
}
|
|
@@ -5,6 +5,9 @@ const jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
const React = require('react');
|
|
6
6
|
const z = require('zod');
|
|
7
7
|
const index = require('./index.cjs');
|
|
8
|
+
const helpers = require('./helpers.cjs');
|
|
9
|
+
const schemaConverter = require('../../lib/schema-converter.cjs');
|
|
10
|
+
const formVisibility = require('../../lib/form-visibility.cjs');
|
|
8
11
|
const button = require('../button.cjs');
|
|
9
12
|
const separator = require('../separator.cjs');
|
|
10
13
|
const utils = require('../../lib/utils.cjs');
|
|
@@ -15,7 +18,7 @@ const React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
|
15
18
|
|
|
16
19
|
function extractStepsFromSchema(schema) {
|
|
17
20
|
try {
|
|
18
|
-
const jsonSchema = schema.toJSONSchema();
|
|
21
|
+
const jsonSchema = schema.meta()?.steps ? schema.meta() : schema.toJSONSchema();
|
|
19
22
|
if (jsonSchema.steps && Array.isArray(jsonSchema.steps)) {
|
|
20
23
|
return jsonSchema.steps;
|
|
21
24
|
}
|
|
@@ -26,7 +29,7 @@ function extractStepsFromSchema(schema) {
|
|
|
26
29
|
function getFieldStepAssignments(schema) {
|
|
27
30
|
const assignments = /* @__PURE__ */ new Map();
|
|
28
31
|
try {
|
|
29
|
-
const jsonSchema = schema.toJSONSchema();
|
|
32
|
+
const jsonSchema = schema.meta()?.steps ? schema.meta() : schema.toJSONSchema();
|
|
30
33
|
const stepGroupMap = jsonSchema.stepGroupMap;
|
|
31
34
|
if (stepGroupMap) {
|
|
32
35
|
for (const [fieldName, stepGroup] of Object.entries(stepGroupMap)) {
|
|
@@ -60,21 +63,6 @@ function createStepSchema(fullSchema, stepIndex, fieldAssignments) {
|
|
|
60
63
|
}
|
|
61
64
|
return fullSchema.pick(pickObject);
|
|
62
65
|
}
|
|
63
|
-
function getObjectSchema(schema) {
|
|
64
|
-
if (schema instanceof z.z.ZodObject) {
|
|
65
|
-
return schema;
|
|
66
|
-
}
|
|
67
|
-
const schemaDef = schema._zod?.def ?? schema._def;
|
|
68
|
-
if (schemaDef) {
|
|
69
|
-
if (schemaDef.schema) {
|
|
70
|
-
return getObjectSchema(schemaDef.schema);
|
|
71
|
-
}
|
|
72
|
-
if (schemaDef.innerType) {
|
|
73
|
-
return getObjectSchema(schemaDef.innerType);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
66
|
function DefaultStepper({
|
|
79
67
|
steps,
|
|
80
68
|
currentStepIndex,
|
|
@@ -122,16 +110,25 @@ function SteppedAutoForm({
|
|
|
122
110
|
StepperComponent = DefaultStepper,
|
|
123
111
|
isSubmitting = false
|
|
124
112
|
}) {
|
|
125
|
-
const
|
|
126
|
-
const hasMultipleSteps =
|
|
127
|
-
const
|
|
113
|
+
const allSteps = React.useMemo(() => extractStepsFromSchema(formSchema), [formSchema]);
|
|
114
|
+
const hasMultipleSteps = allSteps.length > 1;
|
|
115
|
+
const originalFieldAssignments = React.useMemo(
|
|
128
116
|
() => getFieldStepAssignments(formSchema),
|
|
129
117
|
[formSchema]
|
|
130
118
|
);
|
|
131
119
|
const [currentStepIndex, setCurrentStepIndex] = React.useState(0);
|
|
132
120
|
const [accumulatedValues, setAccumulatedValues] = React.useState(
|
|
133
|
-
initialValues ?? {}
|
|
121
|
+
() => ({ ...helpers.getDefaultValues(helpers.getObjectFormSchema(formSchema), fieldConfig) ?? {}, ...initialValues ?? {} })
|
|
134
122
|
);
|
|
123
|
+
const steps = React.useMemo(() => {
|
|
124
|
+
const metadata = formSchema.meta();
|
|
125
|
+
const visibleValues = metadata?.properties ? schemaConverter.resolveVisibleSchema(metadata, accumulatedValues).data : accumulatedValues;
|
|
126
|
+
return allSteps.map((step, originalIndex) => ({ ...step, originalIndex })).filter((step) => formVisibility.matchesShowIf(step.showIf, visibleValues));
|
|
127
|
+
}, [allSteps, accumulatedValues, formSchema]);
|
|
128
|
+
const fieldAssignments = React.useMemo(() => new Map(Array.from(originalFieldAssignments).map(([key, originalIndex]) => [key, steps.findIndex((step) => step.originalIndex === originalIndex)])), [originalFieldAssignments, steps]);
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
setCurrentStepIndex((index) => Math.min(index, Math.max(0, steps.length - 1)));
|
|
131
|
+
}, [steps.length]);
|
|
135
132
|
const [completedSteps, setCompletedSteps] = React.useState(/* @__PURE__ */ new Set());
|
|
136
133
|
const [schemaErrors, setSchemaErrors] = React.useState(null);
|
|
137
134
|
const accumulatedValuesRef = React.useRef(accumulatedValues);
|
|
@@ -152,13 +149,19 @@ function SteppedAutoForm({
|
|
|
152
149
|
setAccumulatedValues(newValues);
|
|
153
150
|
}
|
|
154
151
|
}, [initialValues]);
|
|
155
|
-
const objectSchema = React.useMemo(() =>
|
|
152
|
+
const objectSchema = React.useMemo(() => helpers.getObjectFormSchema(formSchema), [formSchema]);
|
|
156
153
|
const stepSchemas = React.useMemo(() => {
|
|
157
154
|
if (!hasMultipleSteps || !objectSchema) return [];
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
const metadata = formSchema.meta();
|
|
156
|
+
if (metadata?.properties) {
|
|
157
|
+
const visible = schemaConverter.resolveVisibleSchema(metadata, accumulatedValues).schema;
|
|
158
|
+
return steps.map((_, index) => {
|
|
159
|
+
const properties = Object.fromEntries(Object.entries(visible.properties ?? {}).filter(([key]) => fieldAssignments.get(key) === index));
|
|
160
|
+
return schemaConverter.formSchemaToZod({ ...visible, properties, required: visible.required?.filter((key) => key in properties), steps: void 0 });
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return steps.map((_, index) => createStepSchema(objectSchema, index, fieldAssignments));
|
|
164
|
+
}, [hasMultipleSteps, objectSchema, steps, fieldAssignments, accumulatedValues, formSchema]);
|
|
162
165
|
const currentStep = steps[currentStepIndex];
|
|
163
166
|
const currentStepSchema = stepSchemas[currentStepIndex];
|
|
164
167
|
const isFirst = currentStepIndex === 0;
|
|
@@ -166,14 +169,16 @@ function SteppedAutoForm({
|
|
|
166
169
|
const currentStepFieldConfig = React.useMemo(() => {
|
|
167
170
|
if (!fieldConfig) return void 0;
|
|
168
171
|
const stepFields = Array.from(fieldAssignments.entries()).filter(([, step]) => step === currentStepIndex).map(([field]) => field);
|
|
172
|
+
const properties = formSchema.meta()?.properties;
|
|
169
173
|
const filtered = {};
|
|
170
174
|
for (const field of stepFields) {
|
|
171
175
|
if (field in fieldConfig) {
|
|
172
|
-
|
|
176
|
+
const config = fieldConfig[field];
|
|
177
|
+
filtered[field] = properties?.[field]?.showIf ? { ...config, showIf: void 0 } : config;
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
180
|
return filtered;
|
|
176
|
-
}, [fieldConfig, fieldAssignments, currentStepIndex]);
|
|
181
|
+
}, [fieldConfig, fieldAssignments, currentStepIndex, formSchema]);
|
|
177
182
|
const currentStepValues = React.useMemo(() => {
|
|
178
183
|
const stepFields = Array.from(fieldAssignments.entries()).filter(([, step]) => step === currentStepIndex).map(([field]) => field);
|
|
179
184
|
const values = {};
|
|
@@ -251,7 +256,7 @@ function SteppedAutoForm({
|
|
|
251
256
|
setSchemaErrors(null);
|
|
252
257
|
}
|
|
253
258
|
}, [isFirst]);
|
|
254
|
-
if (!hasMultipleSteps) {
|
|
259
|
+
if (!hasMultipleSteps || steps.length === 0) {
|
|
255
260
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
256
261
|
index,
|
|
257
262
|
{
|