@btst/plugin-cms 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/getters.cjs +37 -25
- package/dist/api/getters.mjs +37 -25
- package/dist/api/index.cjs +1 -0
- package/dist/api/index.d.cts +17 -8
- package/dist/api/index.d.mts +17 -8
- package/dist/api/index.d.ts +17 -8
- package/dist/api/index.mjs +1 -1
- package/dist/api/mutations.cjs +46 -19
- package/dist/api/mutations.mjs +46 -20
- package/dist/api/operations.cjs +586 -177
- package/dist/api/operations.mjs +585 -178
- package/dist/api/plugin.cjs +56 -7
- package/dist/api/plugin.mjs +57 -8
- package/dist/api/publication.cjs +8 -0
- package/dist/api/publication.mjs +6 -0
- package/dist/client/components/forms/content-form.cjs +107 -1
- package/dist/client/components/forms/content-form.mjs +108 -2
- package/dist/client/components/pages/content-editor-page.internal.cjs +23 -5
- package/dist/client/components/pages/content-editor-page.internal.mjs +23 -5
- package/dist/client/components/pages/content-list-page.internal.cjs +3 -0
- package/dist/client/components/pages/content-list-page.internal.mjs +3 -0
- package/dist/client/components/pages/dashboard-page.internal.cjs +10 -30
- package/dist/client/components/pages/dashboard-page.internal.mjs +10 -30
- package/dist/client/components/revision-history.cjs +161 -0
- package/dist/client/components/revision-history.mjs +159 -0
- package/dist/client/components/shared/content-type-card.cjs +56 -0
- package/dist/client/components/shared/content-type-card.mjs +54 -0
- package/dist/client/components/shared/empty-state.cjs +18 -1
- package/dist/client/components/shared/empty-state.mjs +19 -2
- package/dist/client/hooks/cms-hooks.cjs +18 -2
- package/dist/client/hooks/cms-hooks.mjs +17 -3
- package/dist/client/hooks/index.cjs +2 -0
- package/dist/client/hooks/index.d.cts +306 -2
- package/dist/client/hooks/index.d.mts +306 -2
- package/dist/client/hooks/index.d.ts +306 -2
- package/dist/client/hooks/index.mjs +1 -1
- package/dist/client/index.d.cts +55 -2
- package/dist/client/index.d.mts +55 -2
- package/dist/client/index.d.ts +55 -2
- package/dist/db.cjs +25 -0
- package/dist/db.mjs +25 -0
- package/dist/permissions.cjs +2 -0
- package/dist/permissions.d.cts +1 -0
- package/dist/permissions.d.mts +1 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.mjs +2 -0
- package/dist/query-keys.cjs +31 -1
- package/dist/query-keys.d.cts +1 -1
- package/dist/query-keys.d.mts +1 -1
- package/dist/query-keys.d.ts +1 -1
- package/dist/query-keys.mjs +31 -1
- package/dist/shared/{plugin-cms.W4Le8D91.d.mts → plugin-cms.DAAUZ41w.d.mts} +377 -144
- package/dist/shared/{plugin-cms.c-GeWvTf.d.cts → plugin-cms.DJ_293QU.d.cts} +377 -144
- package/dist/shared/{plugin-cms.DQLAoogy.d.ts → plugin-cms.DlUSv7wz.d.ts} +377 -144
- package/dist/types.d.cts +38 -2
- package/dist/types.d.mts +38 -2
- package/dist/types.d.ts +38 -2
- 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/sheet.cjs +126 -0
- package/dist/ui/src/components/sheet.mjs +105 -0
- 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 +5 -4
|
@@ -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
|
{
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import React__default, { useMemo, useState,
|
|
3
|
+
import React__default, { useMemo, useState, useEffect, useRef, useCallback } from 'react';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import AutoForm from './index.mjs';
|
|
6
|
+
import { getDefaultValues, getObjectFormSchema } from './helpers.mjs';
|
|
7
|
+
import { resolveVisibleSchema, formSchemaToZod } from '../../lib/schema-converter.mjs';
|
|
8
|
+
import { matchesShowIf } from '../../lib/form-visibility.mjs';
|
|
6
9
|
import { Button } from '../button.mjs';
|
|
7
10
|
import { Separator } from '../separator.mjs';
|
|
8
11
|
import { cn } from '../../lib/utils.mjs';
|
|
9
12
|
|
|
10
13
|
function extractStepsFromSchema(schema) {
|
|
11
14
|
try {
|
|
12
|
-
const jsonSchema = schema.toJSONSchema();
|
|
15
|
+
const jsonSchema = schema.meta()?.steps ? schema.meta() : schema.toJSONSchema();
|
|
13
16
|
if (jsonSchema.steps && Array.isArray(jsonSchema.steps)) {
|
|
14
17
|
return jsonSchema.steps;
|
|
15
18
|
}
|
|
@@ -20,7 +23,7 @@ function extractStepsFromSchema(schema) {
|
|
|
20
23
|
function getFieldStepAssignments(schema) {
|
|
21
24
|
const assignments = /* @__PURE__ */ new Map();
|
|
22
25
|
try {
|
|
23
|
-
const jsonSchema = schema.toJSONSchema();
|
|
26
|
+
const jsonSchema = schema.meta()?.steps ? schema.meta() : schema.toJSONSchema();
|
|
24
27
|
const stepGroupMap = jsonSchema.stepGroupMap;
|
|
25
28
|
if (stepGroupMap) {
|
|
26
29
|
for (const [fieldName, stepGroup] of Object.entries(stepGroupMap)) {
|
|
@@ -54,21 +57,6 @@ function createStepSchema(fullSchema, stepIndex, fieldAssignments) {
|
|
|
54
57
|
}
|
|
55
58
|
return fullSchema.pick(pickObject);
|
|
56
59
|
}
|
|
57
|
-
function getObjectSchema(schema) {
|
|
58
|
-
if (schema instanceof z.ZodObject) {
|
|
59
|
-
return schema;
|
|
60
|
-
}
|
|
61
|
-
const schemaDef = schema._zod?.def ?? schema._def;
|
|
62
|
-
if (schemaDef) {
|
|
63
|
-
if (schemaDef.schema) {
|
|
64
|
-
return getObjectSchema(schemaDef.schema);
|
|
65
|
-
}
|
|
66
|
-
if (schemaDef.innerType) {
|
|
67
|
-
return getObjectSchema(schemaDef.innerType);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
60
|
function DefaultStepper({
|
|
73
61
|
steps,
|
|
74
62
|
currentStepIndex,
|
|
@@ -116,16 +104,25 @@ function SteppedAutoForm({
|
|
|
116
104
|
StepperComponent = DefaultStepper,
|
|
117
105
|
isSubmitting = false
|
|
118
106
|
}) {
|
|
119
|
-
const
|
|
120
|
-
const hasMultipleSteps =
|
|
121
|
-
const
|
|
107
|
+
const allSteps = useMemo(() => extractStepsFromSchema(formSchema), [formSchema]);
|
|
108
|
+
const hasMultipleSteps = allSteps.length > 1;
|
|
109
|
+
const originalFieldAssignments = useMemo(
|
|
122
110
|
() => getFieldStepAssignments(formSchema),
|
|
123
111
|
[formSchema]
|
|
124
112
|
);
|
|
125
113
|
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
|
126
114
|
const [accumulatedValues, setAccumulatedValues] = useState(
|
|
127
|
-
initialValues ?? {}
|
|
115
|
+
() => ({ ...getDefaultValues(getObjectFormSchema(formSchema), fieldConfig) ?? {}, ...initialValues ?? {} })
|
|
128
116
|
);
|
|
117
|
+
const steps = useMemo(() => {
|
|
118
|
+
const metadata = formSchema.meta();
|
|
119
|
+
const visibleValues = metadata?.properties ? resolveVisibleSchema(metadata, accumulatedValues).data : accumulatedValues;
|
|
120
|
+
return allSteps.map((step, originalIndex) => ({ ...step, originalIndex })).filter((step) => matchesShowIf(step.showIf, visibleValues));
|
|
121
|
+
}, [allSteps, accumulatedValues, formSchema]);
|
|
122
|
+
const fieldAssignments = useMemo(() => new Map(Array.from(originalFieldAssignments).map(([key, originalIndex]) => [key, steps.findIndex((step) => step.originalIndex === originalIndex)])), [originalFieldAssignments, steps]);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
setCurrentStepIndex((index) => Math.min(index, Math.max(0, steps.length - 1)));
|
|
125
|
+
}, [steps.length]);
|
|
129
126
|
const [completedSteps, setCompletedSteps] = useState(/* @__PURE__ */ new Set());
|
|
130
127
|
const [schemaErrors, setSchemaErrors] = useState(null);
|
|
131
128
|
const accumulatedValuesRef = useRef(accumulatedValues);
|
|
@@ -146,13 +143,19 @@ function SteppedAutoForm({
|
|
|
146
143
|
setAccumulatedValues(newValues);
|
|
147
144
|
}
|
|
148
145
|
}, [initialValues]);
|
|
149
|
-
const objectSchema = useMemo(() =>
|
|
146
|
+
const objectSchema = useMemo(() => getObjectFormSchema(formSchema), [formSchema]);
|
|
150
147
|
const stepSchemas = useMemo(() => {
|
|
151
148
|
if (!hasMultipleSteps || !objectSchema) return [];
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
const metadata = formSchema.meta();
|
|
150
|
+
if (metadata?.properties) {
|
|
151
|
+
const visible = resolveVisibleSchema(metadata, accumulatedValues).schema;
|
|
152
|
+
return steps.map((_, index) => {
|
|
153
|
+
const properties = Object.fromEntries(Object.entries(visible.properties ?? {}).filter(([key]) => fieldAssignments.get(key) === index));
|
|
154
|
+
return formSchemaToZod({ ...visible, properties, required: visible.required?.filter((key) => key in properties), steps: void 0 });
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return steps.map((_, index) => createStepSchema(objectSchema, index, fieldAssignments));
|
|
158
|
+
}, [hasMultipleSteps, objectSchema, steps, fieldAssignments, accumulatedValues, formSchema]);
|
|
156
159
|
const currentStep = steps[currentStepIndex];
|
|
157
160
|
const currentStepSchema = stepSchemas[currentStepIndex];
|
|
158
161
|
const isFirst = currentStepIndex === 0;
|
|
@@ -160,14 +163,16 @@ function SteppedAutoForm({
|
|
|
160
163
|
const currentStepFieldConfig = useMemo(() => {
|
|
161
164
|
if (!fieldConfig) return void 0;
|
|
162
165
|
const stepFields = Array.from(fieldAssignments.entries()).filter(([, step]) => step === currentStepIndex).map(([field]) => field);
|
|
166
|
+
const properties = formSchema.meta()?.properties;
|
|
163
167
|
const filtered = {};
|
|
164
168
|
for (const field of stepFields) {
|
|
165
169
|
if (field in fieldConfig) {
|
|
166
|
-
|
|
170
|
+
const config = fieldConfig[field];
|
|
171
|
+
filtered[field] = properties?.[field]?.showIf ? { ...config, showIf: void 0 } : config;
|
|
167
172
|
}
|
|
168
173
|
}
|
|
169
174
|
return filtered;
|
|
170
|
-
}, [fieldConfig, fieldAssignments, currentStepIndex]);
|
|
175
|
+
}, [fieldConfig, fieldAssignments, currentStepIndex, formSchema]);
|
|
171
176
|
const currentStepValues = useMemo(() => {
|
|
172
177
|
const stepFields = Array.from(fieldAssignments.entries()).filter(([, step]) => step === currentStepIndex).map(([field]) => field);
|
|
173
178
|
const values = {};
|
|
@@ -245,7 +250,7 @@ function SteppedAutoForm({
|
|
|
245
250
|
setSchemaErrors(null);
|
|
246
251
|
}
|
|
247
252
|
}, [isFirst]);
|
|
248
|
-
if (!hasMultipleSteps) {
|
|
253
|
+
if (!hasMultipleSteps || steps.length === 0) {
|
|
249
254
|
return /* @__PURE__ */ jsxs(
|
|
250
255
|
AutoForm,
|
|
251
256
|
{
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
const DialogPrimitive = require('@radix-ui/react-dialog');
|
|
6
|
+
const lucideReact = require('lucide-react');
|
|
7
|
+
const utils = require('../lib/utils.cjs');
|
|
8
|
+
|
|
9
|
+
function _interopNamespaceCompat(e) {
|
|
10
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
11
|
+
const n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
for (const k in e) {
|
|
14
|
+
n[k] = e[k];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
n.default = e;
|
|
18
|
+
return n;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DialogPrimitive__namespace = /*#__PURE__*/_interopNamespaceCompat(DialogPrimitive);
|
|
22
|
+
|
|
23
|
+
function Sheet({ ...props }) {
|
|
24
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Root, { "data-slot": "sheet", ...props });
|
|
25
|
+
}
|
|
26
|
+
function SheetTrigger({
|
|
27
|
+
...props
|
|
28
|
+
}) {
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
30
|
+
}
|
|
31
|
+
function SheetPortal({
|
|
32
|
+
...props
|
|
33
|
+
}) {
|
|
34
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Portal, { "data-slot": "sheet-portal", ...props });
|
|
35
|
+
}
|
|
36
|
+
function SheetOverlay({
|
|
37
|
+
className,
|
|
38
|
+
...props
|
|
39
|
+
}) {
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
41
|
+
DialogPrimitive__namespace.Overlay,
|
|
42
|
+
{
|
|
43
|
+
"data-slot": "sheet-overlay",
|
|
44
|
+
className: utils.cn(
|
|
45
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
46
|
+
className
|
|
47
|
+
),
|
|
48
|
+
...props
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
function SheetContent({
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
side = "right",
|
|
56
|
+
...props
|
|
57
|
+
}) {
|
|
58
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(SheetPortal, { children: [
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsx(SheetOverlay, {}),
|
|
60
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
61
|
+
DialogPrimitive__namespace.Content,
|
|
62
|
+
{
|
|
63
|
+
"data-slot": "sheet-content",
|
|
64
|
+
className: utils.cn(
|
|
65
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
66
|
+
side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
|
67
|
+
side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
|
68
|
+
side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
|
69
|
+
side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
|
70
|
+
className
|
|
71
|
+
),
|
|
72
|
+
...props,
|
|
73
|
+
children: [
|
|
74
|
+
children,
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [
|
|
76
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-4" }),
|
|
77
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
78
|
+
] })
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
function SheetHeader({ className, ...props }) {
|
|
85
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
"data-slot": "sheet-header",
|
|
89
|
+
className: utils.cn("flex flex-col gap-1.5 p-4", className),
|
|
90
|
+
...props
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function SheetTitle({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}) {
|
|
98
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
99
|
+
DialogPrimitive__namespace.Title,
|
|
100
|
+
{
|
|
101
|
+
"data-slot": "sheet-title",
|
|
102
|
+
className: utils.cn("text-foreground font-semibold", className),
|
|
103
|
+
...props
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function SheetDescription({
|
|
108
|
+
className,
|
|
109
|
+
...props
|
|
110
|
+
}) {
|
|
111
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
112
|
+
DialogPrimitive__namespace.Description,
|
|
113
|
+
{
|
|
114
|
+
"data-slot": "sheet-description",
|
|
115
|
+
className: utils.cn("text-muted-foreground text-sm", className),
|
|
116
|
+
...props
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
exports.Sheet = Sheet;
|
|
122
|
+
exports.SheetContent = SheetContent;
|
|
123
|
+
exports.SheetDescription = SheetDescription;
|
|
124
|
+
exports.SheetHeader = SheetHeader;
|
|
125
|
+
exports.SheetTitle = SheetTitle;
|
|
126
|
+
exports.SheetTrigger = SheetTrigger;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
4
|
+
import { XIcon } from 'lucide-react';
|
|
5
|
+
import { cn } from '../lib/utils.mjs';
|
|
6
|
+
|
|
7
|
+
function Sheet({ ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
9
|
+
}
|
|
10
|
+
function SheetTrigger({
|
|
11
|
+
...props
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
14
|
+
}
|
|
15
|
+
function SheetPortal({
|
|
16
|
+
...props
|
|
17
|
+
}) {
|
|
18
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
19
|
+
}
|
|
20
|
+
function SheetOverlay({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}) {
|
|
24
|
+
return /* @__PURE__ */ jsx(
|
|
25
|
+
DialogPrimitive.Overlay,
|
|
26
|
+
{
|
|
27
|
+
"data-slot": "sheet-overlay",
|
|
28
|
+
className: cn(
|
|
29
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
30
|
+
className
|
|
31
|
+
),
|
|
32
|
+
...props
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function SheetContent({
|
|
37
|
+
className,
|
|
38
|
+
children,
|
|
39
|
+
side = "right",
|
|
40
|
+
...props
|
|
41
|
+
}) {
|
|
42
|
+
return /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
43
|
+
/* @__PURE__ */ jsx(SheetOverlay, {}),
|
|
44
|
+
/* @__PURE__ */ jsxs(
|
|
45
|
+
DialogPrimitive.Content,
|
|
46
|
+
{
|
|
47
|
+
"data-slot": "sheet-content",
|
|
48
|
+
className: cn(
|
|
49
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
50
|
+
side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
|
51
|
+
side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
|
52
|
+
side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
|
53
|
+
side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
|
54
|
+
className
|
|
55
|
+
),
|
|
56
|
+
...props,
|
|
57
|
+
children: [
|
|
58
|
+
children,
|
|
59
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [
|
|
60
|
+
/* @__PURE__ */ jsx(XIcon, { className: "size-4" }),
|
|
61
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
62
|
+
] })
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] });
|
|
67
|
+
}
|
|
68
|
+
function SheetHeader({ className, ...props }) {
|
|
69
|
+
return /* @__PURE__ */ jsx(
|
|
70
|
+
"div",
|
|
71
|
+
{
|
|
72
|
+
"data-slot": "sheet-header",
|
|
73
|
+
className: cn("flex flex-col gap-1.5 p-4", className),
|
|
74
|
+
...props
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function SheetTitle({
|
|
79
|
+
className,
|
|
80
|
+
...props
|
|
81
|
+
}) {
|
|
82
|
+
return /* @__PURE__ */ jsx(
|
|
83
|
+
DialogPrimitive.Title,
|
|
84
|
+
{
|
|
85
|
+
"data-slot": "sheet-title",
|
|
86
|
+
className: cn("text-foreground font-semibold", className),
|
|
87
|
+
...props
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function SheetDescription({
|
|
92
|
+
className,
|
|
93
|
+
...props
|
|
94
|
+
}) {
|
|
95
|
+
return /* @__PURE__ */ jsx(
|
|
96
|
+
DialogPrimitive.Description,
|
|
97
|
+
{
|
|
98
|
+
"data-slot": "sheet-description",
|
|
99
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
100
|
+
...props
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function matchesShowIf(condition, values) {
|
|
4
|
+
if (!condition) return true;
|
|
5
|
+
const value = Object.hasOwn(values, condition.field) ? values[condition.field] : void 0;
|
|
6
|
+
switch (condition.operator) {
|
|
7
|
+
case "equals":
|
|
8
|
+
return value === condition.value;
|
|
9
|
+
case "notEquals":
|
|
10
|
+
return value !== condition.value;
|
|
11
|
+
case "in":
|
|
12
|
+
return Array.isArray(condition.value) && condition.value.includes(value);
|
|
13
|
+
case "isTruthy":
|
|
14
|
+
return Boolean(value);
|
|
15
|
+
default:
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.matchesShowIf = matchesShowIf;
|