@form-engine-ts/react 1.1.0 → 2.1.0
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/README.md +35 -0
- package/dist/index.cjs +943 -403
- package/dist/index.d.cts +151 -6
- package/dist/index.d.ts +151 -6
- package/dist/index.js +923 -375
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,620 @@
|
|
|
1
1
|
// src/builder.tsx
|
|
2
2
|
import {
|
|
3
|
-
populateSchemaTranslations
|
|
4
|
-
sanitizeSchema
|
|
3
|
+
populateSchemaTranslations
|
|
5
4
|
} from "@form-engine-ts/core";
|
|
6
5
|
import { useState } from "react";
|
|
6
|
+
|
|
7
|
+
// src/hooks/useFormBuilder.ts
|
|
8
|
+
import {
|
|
9
|
+
transformFieldType,
|
|
10
|
+
validateFormSchema
|
|
11
|
+
} from "@form-engine-ts/core";
|
|
12
|
+
import { useCallback, useMemo } from "react";
|
|
13
|
+
var DEFAULT_PREFIXES = { field: "q", option: "opt", page: "page" };
|
|
14
|
+
var CHOICE_TYPES = ["select", "radio", "multi-select"];
|
|
15
|
+
function defaultIdFactory(kind, existingIds) {
|
|
16
|
+
const prefix = DEFAULT_PREFIXES[kind];
|
|
17
|
+
let id;
|
|
18
|
+
do
|
|
19
|
+
id = `${prefix}_${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
20
|
+
while (existingIds.has(id));
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
23
|
+
function defaultCreateField(type, id) {
|
|
24
|
+
const base = { id, title: "New question", required: false };
|
|
25
|
+
if (type === "text" || type === "textarea" || type === "number" || type === "checkbox") return { ...base, type };
|
|
26
|
+
if (type === "rating") return { ...base, type, min: 1, max: 5 };
|
|
27
|
+
return { ...base, type, options: [] };
|
|
28
|
+
}
|
|
29
|
+
function defaultCreateOption(field, id) {
|
|
30
|
+
return { id, label: `Option ${"options" in field ? field.options.length + 1 : 1}` };
|
|
31
|
+
}
|
|
32
|
+
function defaultCreatePage(id, questionIds) {
|
|
33
|
+
return { id, title: "New page", questionIds };
|
|
34
|
+
}
|
|
35
|
+
function move(items, sourceIndex, targetIndex) {
|
|
36
|
+
if (sourceIndex < 0 || targetIndex < 0 || targetIndex >= items.length || sourceIndex === targetIndex)
|
|
37
|
+
return void 0;
|
|
38
|
+
const result = [...items];
|
|
39
|
+
const [item] = result.splice(sourceIndex, 1);
|
|
40
|
+
if (item === void 0) return void 0;
|
|
41
|
+
result.splice(targetIndex, 0, item);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
function withoutDisplayCondition(field) {
|
|
45
|
+
const { displayCondition: _displayCondition, ...rest } = field;
|
|
46
|
+
return rest;
|
|
47
|
+
}
|
|
48
|
+
function removeLocalizedProperty(translations, locale, property) {
|
|
49
|
+
const current = translations?.[locale];
|
|
50
|
+
if (current === void 0) return translations;
|
|
51
|
+
const { [property]: _removed, ...remaining } = current;
|
|
52
|
+
return { ...translations, [locale]: remaining };
|
|
53
|
+
}
|
|
54
|
+
function setTranslationMetadata(node, locale, property, metadata, remove) {
|
|
55
|
+
const localeMetadata = node.translationMetadata?.[locale];
|
|
56
|
+
if (metadata === void 0 && !remove) return node;
|
|
57
|
+
const nextLocaleMetadata = remove ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== property)) : { ...localeMetadata, [property]: metadata ?? {} };
|
|
58
|
+
return { ...node, translationMetadata: { ...node.translationMetadata, [locale]: nextLocaleMetadata } };
|
|
59
|
+
}
|
|
60
|
+
function failedId(kind, result) {
|
|
61
|
+
return { success: false, error: result.error ?? { type: "invalid_id", kind, id: "" } };
|
|
62
|
+
}
|
|
63
|
+
function useFormBuilder({
|
|
64
|
+
schema,
|
|
65
|
+
onChange,
|
|
66
|
+
policy,
|
|
67
|
+
idFactory = defaultIdFactory,
|
|
68
|
+
factories = {}
|
|
69
|
+
}) {
|
|
70
|
+
const createId = useCallback(
|
|
71
|
+
(kind, existingIds) => {
|
|
72
|
+
const rawId = idFactory(kind, existingIds);
|
|
73
|
+
const id = rawId.trim();
|
|
74
|
+
return id.length > 0 && !existingIds.has(id) ? { id } : { error: { type: "invalid_id", kind, id: rawId } };
|
|
75
|
+
},
|
|
76
|
+
[idFactory]
|
|
77
|
+
);
|
|
78
|
+
const textPolicyError = useCallback(
|
|
79
|
+
(text) => policy?.maxTextLength !== void 0 && text.length > policy.maxTextLength ? { success: false, error: { type: "max_text_length_exceeded", max: policy.maxTextLength } } : void 0,
|
|
80
|
+
[policy?.maxTextLength]
|
|
81
|
+
);
|
|
82
|
+
const updateField = useCallback(
|
|
83
|
+
(fieldId, updater) => {
|
|
84
|
+
const current = schema.fields.find((field) => field.id === fieldId);
|
|
85
|
+
if (current === void 0)
|
|
86
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
87
|
+
const updated = updater(current);
|
|
88
|
+
if (updated.id !== fieldId)
|
|
89
|
+
return { success: false, error: { type: "invalid_id", kind: "field", id: updated.id } };
|
|
90
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(updated.type))
|
|
91
|
+
return { success: false, error: { type: "disallowed_field_type", fieldType: updated.type } };
|
|
92
|
+
for (const text of [updated.title, updated.description]) {
|
|
93
|
+
if (text !== void 0) {
|
|
94
|
+
const error = textPolicyError(text);
|
|
95
|
+
if (error !== void 0) return error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
onChange({ ...schema, fields: schema.fields.map((field) => field.id === fieldId ? updated : field) });
|
|
99
|
+
return { success: true };
|
|
100
|
+
},
|
|
101
|
+
[onChange, policy?.allowedFieldTypes, schema, textPolicyError]
|
|
102
|
+
);
|
|
103
|
+
const updateOption = useCallback(
|
|
104
|
+
(fieldId, optionId, updater) => {
|
|
105
|
+
const field = schema.fields.find((candidate) => candidate.id === fieldId);
|
|
106
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
107
|
+
if (!("options" in field))
|
|
108
|
+
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
109
|
+
const current = field.options.find((option) => option.id === optionId);
|
|
110
|
+
if (current === void 0)
|
|
111
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: optionId } };
|
|
112
|
+
const updated = updater(current);
|
|
113
|
+
if (updated.id !== optionId)
|
|
114
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: updated.id } };
|
|
115
|
+
const error = textPolicyError(updated.label);
|
|
116
|
+
if (error !== void 0) return error;
|
|
117
|
+
onChange({
|
|
118
|
+
...schema,
|
|
119
|
+
fields: schema.fields.map(
|
|
120
|
+
(candidate) => candidate.id === fieldId && "options" in candidate ? {
|
|
121
|
+
...candidate,
|
|
122
|
+
options: candidate.options.map((option) => option.id === optionId ? updated : option)
|
|
123
|
+
} : candidate
|
|
124
|
+
)
|
|
125
|
+
});
|
|
126
|
+
return { success: true };
|
|
127
|
+
},
|
|
128
|
+
[onChange, schema, textPolicyError]
|
|
129
|
+
);
|
|
130
|
+
const updatePage = useCallback(
|
|
131
|
+
(pageId, updater) => {
|
|
132
|
+
const page = schema.pages?.find((candidate) => candidate.id === pageId);
|
|
133
|
+
if (page === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
134
|
+
const pages = schema.pages;
|
|
135
|
+
if (pages === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
136
|
+
const updated = updater(page);
|
|
137
|
+
if (updated.id !== pageId) return { success: false, error: { type: "invalid_id", kind: "page", id: updated.id } };
|
|
138
|
+
onChange({ ...schema, pages: pages.map((candidate) => candidate.id === pageId ? updated : candidate) });
|
|
139
|
+
return { success: true };
|
|
140
|
+
},
|
|
141
|
+
[onChange, schema]
|
|
142
|
+
);
|
|
143
|
+
const addField = useCallback(
|
|
144
|
+
(type, pageId) => {
|
|
145
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(type))
|
|
146
|
+
return { success: false, error: { type: "disallowed_field_type", fieldType: type } };
|
|
147
|
+
if (policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields)
|
|
148
|
+
return { success: false, error: { type: "max_fields_exceeded", max: policy.maxFields } };
|
|
149
|
+
if (pageId !== void 0 && !schema.pages?.some((page) => page.id === pageId))
|
|
150
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
151
|
+
const fieldIds = new Set(schema.fields.map((field2) => field2.id));
|
|
152
|
+
const fieldId = createId("field", fieldIds);
|
|
153
|
+
if (fieldId.id === void 0) return failedId("field", fieldId);
|
|
154
|
+
let field = (factories.createField ?? defaultCreateField)(type, fieldId.id);
|
|
155
|
+
if (field.id !== fieldId.id || field.type !== type || fieldIds.has(field.id))
|
|
156
|
+
return { success: false, error: { type: "invalid_id", kind: "field", id: field.id } };
|
|
157
|
+
if (CHOICE_TYPES.includes(type) && "options" in field && field.options.length === 0) {
|
|
158
|
+
const optionIds = new Set(
|
|
159
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
160
|
+
);
|
|
161
|
+
const optionId = createId("option", optionIds);
|
|
162
|
+
if (optionId.id === void 0) return failedId("option", optionId);
|
|
163
|
+
const option = (factories.createOption ?? defaultCreateOption)(field, optionId.id);
|
|
164
|
+
if (option.id !== optionId.id || optionIds.has(option.id))
|
|
165
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
166
|
+
field = { ...field, options: [option] };
|
|
167
|
+
}
|
|
168
|
+
const pages = schema.pages?.map((page, index) => ({
|
|
169
|
+
...page,
|
|
170
|
+
questionIds: page.id === pageId || pageId === void 0 && index === (schema.pages?.length ?? 0) - 1 ? [...page.questionIds, field.id] : page.questionIds
|
|
171
|
+
}));
|
|
172
|
+
onChange({ ...schema, fields: [...schema.fields, field], ...pages === void 0 ? {} : { pages } });
|
|
173
|
+
return { success: true };
|
|
174
|
+
},
|
|
175
|
+
[createId, factories, onChange, policy, schema]
|
|
176
|
+
);
|
|
177
|
+
const removeField = useCallback(
|
|
178
|
+
(fieldId) => {
|
|
179
|
+
if (!schema.fields.some((field) => field.id === fieldId))
|
|
180
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
181
|
+
if (schema.fields.length <= 1)
|
|
182
|
+
return { success: false, error: { type: "invalid_operation", message: "A form must contain one field." } };
|
|
183
|
+
const fields = schema.fields.filter((field) => field.id !== fieldId).map((field) => field.displayCondition?.questionId === fieldId ? withoutDisplayCondition(field) : field);
|
|
184
|
+
const pages = schema.pages?.map((page) => ({ ...page, questionIds: page.questionIds.filter((id) => id !== fieldId) })).filter((page) => page.questionIds.length > 0);
|
|
185
|
+
if (schema.pages !== void 0 && pages?.length === 0) {
|
|
186
|
+
const { pages: _pages, ...single } = schema;
|
|
187
|
+
onChange({ ...single, fields });
|
|
188
|
+
} else onChange({ ...schema, fields, ...pages === void 0 ? {} : { pages } });
|
|
189
|
+
return { success: true };
|
|
190
|
+
},
|
|
191
|
+
[onChange, schema]
|
|
192
|
+
);
|
|
193
|
+
const moveField = useCallback(
|
|
194
|
+
(fieldId, targetIndex) => {
|
|
195
|
+
const sourceIndex = schema.fields.findIndex((field) => field.id === fieldId);
|
|
196
|
+
if (sourceIndex < 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
197
|
+
const fields = move(schema.fields, sourceIndex, targetIndex);
|
|
198
|
+
if (fields === void 0)
|
|
199
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid field position." } };
|
|
200
|
+
const indexById = new Map(fields.map((field, index) => [field.id, index]));
|
|
201
|
+
onChange({
|
|
202
|
+
...schema,
|
|
203
|
+
fields: fields.map((field, index) => {
|
|
204
|
+
const source = field.displayCondition?.questionId;
|
|
205
|
+
return source === void 0 || (indexById.get(source) ?? index) < index ? field : withoutDisplayCondition(field);
|
|
206
|
+
})
|
|
207
|
+
});
|
|
208
|
+
return { success: true };
|
|
209
|
+
},
|
|
210
|
+
[onChange, schema]
|
|
211
|
+
);
|
|
212
|
+
const addOption = useCallback(
|
|
213
|
+
(fieldId) => {
|
|
214
|
+
const field = schema.fields.find((candidate) => candidate.id === fieldId);
|
|
215
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
216
|
+
if (!("options" in field))
|
|
217
|
+
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
218
|
+
if (policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField)
|
|
219
|
+
return { success: false, error: { type: "max_options_exceeded", max: policy.maxOptionsPerField } };
|
|
220
|
+
const ids = new Set(
|
|
221
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
222
|
+
);
|
|
223
|
+
const id = createId("option", ids);
|
|
224
|
+
if (id.id === void 0) return failedId("option", id);
|
|
225
|
+
const option = (factories.createOption ?? defaultCreateOption)(field, id.id);
|
|
226
|
+
if (option.id !== id.id || ids.has(option.id))
|
|
227
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
228
|
+
onChange({
|
|
229
|
+
...schema,
|
|
230
|
+
fields: schema.fields.map(
|
|
231
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options: [...item.options, option] } : item
|
|
232
|
+
)
|
|
233
|
+
});
|
|
234
|
+
return { success: true };
|
|
235
|
+
},
|
|
236
|
+
[createId, factories.createOption, onChange, policy?.maxOptionsPerField, schema]
|
|
237
|
+
);
|
|
238
|
+
const removeOption = useCallback(
|
|
239
|
+
(fieldId, optionId) => {
|
|
240
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
241
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
242
|
+
if (!("options" in field) || !field.options.some((option) => option.id === optionId))
|
|
243
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: optionId } };
|
|
244
|
+
if (field.options.length <= 1)
|
|
245
|
+
return { success: false, error: { type: "invalid_operation", message: "A choice field needs one option." } };
|
|
246
|
+
onChange({
|
|
247
|
+
...schema,
|
|
248
|
+
fields: schema.fields.map(
|
|
249
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options: item.options.filter((option) => option.id !== optionId) } : item
|
|
250
|
+
)
|
|
251
|
+
});
|
|
252
|
+
return { success: true };
|
|
253
|
+
},
|
|
254
|
+
[onChange, schema]
|
|
255
|
+
);
|
|
256
|
+
const moveOption = useCallback(
|
|
257
|
+
(fieldId, optionId, targetIndex) => {
|
|
258
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
259
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
260
|
+
if (!("options" in field))
|
|
261
|
+
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
262
|
+
const options = move(
|
|
263
|
+
field.options,
|
|
264
|
+
field.options.findIndex((option) => option.id === optionId),
|
|
265
|
+
targetIndex
|
|
266
|
+
);
|
|
267
|
+
if (options === void 0)
|
|
268
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid option position." } };
|
|
269
|
+
onChange({
|
|
270
|
+
...schema,
|
|
271
|
+
fields: schema.fields.map(
|
|
272
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options } : item
|
|
273
|
+
)
|
|
274
|
+
});
|
|
275
|
+
return { success: true };
|
|
276
|
+
},
|
|
277
|
+
[onChange, schema]
|
|
278
|
+
);
|
|
279
|
+
const changeFieldType = useCallback(
|
|
280
|
+
(fieldId, type) => {
|
|
281
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
282
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
283
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(type))
|
|
284
|
+
return { success: false, error: { type: "disallowed_field_type", fieldType: type } };
|
|
285
|
+
let transformed = transformFieldType(field, type);
|
|
286
|
+
if (CHOICE_TYPES.includes(type) && !("options" in field) && "options" in transformed) {
|
|
287
|
+
const ids = new Set(
|
|
288
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
289
|
+
);
|
|
290
|
+
const id = createId("option", ids);
|
|
291
|
+
if (id.id === void 0) return failedId("option", id);
|
|
292
|
+
const option = (factories.createOption ?? defaultCreateOption)(transformed, id.id);
|
|
293
|
+
if (option.id !== id.id || ids.has(option.id))
|
|
294
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
295
|
+
transformed = { ...transformed, options: [option] };
|
|
296
|
+
}
|
|
297
|
+
onChange({ ...schema, fields: schema.fields.map((item) => item.id === fieldId ? transformed : item) });
|
|
298
|
+
return { success: true };
|
|
299
|
+
},
|
|
300
|
+
[createId, factories.createOption, onChange, policy?.allowedFieldTypes, schema]
|
|
301
|
+
);
|
|
302
|
+
const addPage = useCallback(
|
|
303
|
+
(questionId) => {
|
|
304
|
+
const ids = new Set(schema.pages?.map((page2) => page2.id) ?? []);
|
|
305
|
+
const id = createId("page", ids);
|
|
306
|
+
if (id.id === void 0) return failedId("page", id);
|
|
307
|
+
const questionIds = schema.pages === void 0 ? schema.fields.map((field) => field.id) : [questionId ?? schema.pages.find((page2) => page2.questionIds.length > 1)?.questionIds.at(-1)].filter(
|
|
308
|
+
(value) => value !== void 0
|
|
309
|
+
);
|
|
310
|
+
if (questionIds.length === 0)
|
|
311
|
+
return { success: false, error: { type: "invalid_operation", message: "No question can be moved." } };
|
|
312
|
+
const source = schema.pages?.find((page2) => page2.questionIds.includes(questionIds[0] ?? ""));
|
|
313
|
+
if (schema.pages !== void 0 && (source === void 0 || source.questionIds.length <= 1))
|
|
314
|
+
return { success: false, error: { type: "invalid_operation", message: "A page cannot be left empty." } };
|
|
315
|
+
const page = (factories.createPage ?? defaultCreatePage)(id.id, [...questionIds]);
|
|
316
|
+
if (page.id !== id.id || ids.has(page.id))
|
|
317
|
+
return { success: false, error: { type: "invalid_id", kind: "page", id: page.id } };
|
|
318
|
+
const pages = schema.pages === void 0 ? [page] : [
|
|
319
|
+
...schema.pages.map((item) => ({
|
|
320
|
+
...item,
|
|
321
|
+
questionIds: item.questionIds.filter((fieldId) => !questionIds.includes(fieldId))
|
|
322
|
+
})),
|
|
323
|
+
page
|
|
324
|
+
];
|
|
325
|
+
onChange({ ...schema, pages });
|
|
326
|
+
return { success: true };
|
|
327
|
+
},
|
|
328
|
+
[createId, factories.createPage, onChange, schema]
|
|
329
|
+
);
|
|
330
|
+
const removePage = useCallback(
|
|
331
|
+
(pageId) => {
|
|
332
|
+
const index = schema.pages?.findIndex((page) => page.id === pageId) ?? -1;
|
|
333
|
+
const removed = schema.pages?.[index];
|
|
334
|
+
if (removed === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
335
|
+
const currentPages = schema.pages;
|
|
336
|
+
if (currentPages === void 0)
|
|
337
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
338
|
+
if ((schema.pages?.length ?? 0) === 1) {
|
|
339
|
+
const { pages: _pages, ...single } = schema;
|
|
340
|
+
onChange(single);
|
|
341
|
+
return { success: true };
|
|
342
|
+
}
|
|
343
|
+
const targetIndex = index === 0 ? 1 : index - 1;
|
|
344
|
+
onChange({
|
|
345
|
+
...schema,
|
|
346
|
+
pages: currentPages.map(
|
|
347
|
+
(page, pageIndex) => pageIndex === targetIndex ? { ...page, questionIds: [...page.questionIds, ...removed.questionIds] } : page
|
|
348
|
+
).filter((page) => page.id !== pageId)
|
|
349
|
+
});
|
|
350
|
+
return { success: true };
|
|
351
|
+
},
|
|
352
|
+
[onChange, schema]
|
|
353
|
+
);
|
|
354
|
+
const movePage = useCallback(
|
|
355
|
+
(pageId, targetIndex) => {
|
|
356
|
+
const sourceIndex = schema.pages?.findIndex((page) => page.id === pageId) ?? -1;
|
|
357
|
+
if (sourceIndex < 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
358
|
+
const pages = move(schema.pages ?? [], sourceIndex, targetIndex);
|
|
359
|
+
if (pages === void 0)
|
|
360
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid page position." } };
|
|
361
|
+
const assignment = new Map(pages.flatMap((page, index) => page.questionIds.map((id) => [id, index])));
|
|
362
|
+
onChange({
|
|
363
|
+
...schema,
|
|
364
|
+
pages: pages.map((page, index) => {
|
|
365
|
+
const source = page.displayCondition?.questionId;
|
|
366
|
+
if (source === void 0 || (assignment.get(source) ?? index) < index) return page;
|
|
367
|
+
const { displayCondition: _condition, ...rest } = page;
|
|
368
|
+
return rest;
|
|
369
|
+
})
|
|
370
|
+
});
|
|
371
|
+
return { success: true };
|
|
372
|
+
},
|
|
373
|
+
[onChange, schema]
|
|
374
|
+
);
|
|
375
|
+
const assignFieldToPage = useCallback(
|
|
376
|
+
(fieldId, pageId) => {
|
|
377
|
+
if (!schema.fields.some((field) => field.id === fieldId))
|
|
378
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
379
|
+
if (pageId === null) {
|
|
380
|
+
if (schema.pages !== void 0) {
|
|
381
|
+
const { pages: _pages, ...single } = schema;
|
|
382
|
+
onChange(single);
|
|
383
|
+
}
|
|
384
|
+
return { success: true };
|
|
385
|
+
}
|
|
386
|
+
if (!schema.pages?.some((page) => page.id === pageId))
|
|
387
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
388
|
+
const pages = schema.pages.map((page) => ({
|
|
389
|
+
...page,
|
|
390
|
+
questionIds: page.id === pageId ? schema.fields.filter((field) => page.questionIds.includes(field.id) || field.id === fieldId).map((field) => field.id) : page.questionIds.filter((id) => id !== fieldId)
|
|
391
|
+
})).filter((page) => page.questionIds.length > 0);
|
|
392
|
+
onChange({ ...schema, pages });
|
|
393
|
+
return { success: true };
|
|
394
|
+
},
|
|
395
|
+
[onChange, schema]
|
|
396
|
+
);
|
|
397
|
+
const setDisplayCondition = useCallback(
|
|
398
|
+
(fieldId, condition) => {
|
|
399
|
+
const index = schema.fields.findIndex((field) => field.id === fieldId);
|
|
400
|
+
if (index < 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
401
|
+
if (condition !== void 0) {
|
|
402
|
+
const sourceIndex = schema.fields.findIndex((field) => field.id === condition.questionId);
|
|
403
|
+
if (sourceIndex < 0 || sourceIndex >= index)
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
error: { type: "invalid_operation", message: "Conditions require an earlier field." }
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
onChange({
|
|
410
|
+
...schema,
|
|
411
|
+
fields: schema.fields.map(
|
|
412
|
+
(field) => field.id !== fieldId ? field : condition === void 0 ? withoutDisplayCondition(field) : { ...field, displayCondition: condition }
|
|
413
|
+
)
|
|
414
|
+
});
|
|
415
|
+
return { success: true };
|
|
416
|
+
},
|
|
417
|
+
[onChange, schema]
|
|
418
|
+
);
|
|
419
|
+
const setSourceText = useCallback(
|
|
420
|
+
(target, property, text) => {
|
|
421
|
+
const error = textPolicyError(text);
|
|
422
|
+
if (error !== void 0) return error;
|
|
423
|
+
if (target.kind === "form") {
|
|
424
|
+
if (!["title", "description", "completionMessage"].includes(property))
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
error: { type: "invalid_operation", message: `Unsupported form property: ${property}` }
|
|
428
|
+
};
|
|
429
|
+
if (property === "title") onChange({ ...schema, title: text });
|
|
430
|
+
else if (property === "description") {
|
|
431
|
+
const { description: _description, ...withoutDescription } = schema;
|
|
432
|
+
onChange(text.length === 0 ? withoutDescription : { ...schema, description: text });
|
|
433
|
+
} else {
|
|
434
|
+
const { completionMessage: _completionMessage, ...withoutCompletionMessage } = schema;
|
|
435
|
+
onChange(text.length === 0 ? withoutCompletionMessage : { ...schema, completionMessage: text });
|
|
436
|
+
}
|
|
437
|
+
return { success: true };
|
|
438
|
+
}
|
|
439
|
+
if (target.id === void 0)
|
|
440
|
+
return { success: false, error: { type: "invalid_operation", message: "A target ID is required." } };
|
|
441
|
+
if (target.kind === "field") {
|
|
442
|
+
if (!["title", "description"].includes(property))
|
|
443
|
+
return {
|
|
444
|
+
success: false,
|
|
445
|
+
error: { type: "invalid_operation", message: `Unsupported field property: ${property}` }
|
|
446
|
+
};
|
|
447
|
+
return updateField(target.id, (field) => {
|
|
448
|
+
if (property === "title") return { ...field, title: text };
|
|
449
|
+
const { description: _description, ...withoutDescription } = field;
|
|
450
|
+
return text.length === 0 ? withoutDescription : { ...field, description: text };
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
if (target.kind === "option") {
|
|
454
|
+
if (property !== "label")
|
|
455
|
+
return {
|
|
456
|
+
success: false,
|
|
457
|
+
error: { type: "invalid_operation", message: `Unsupported option property: ${property}` }
|
|
458
|
+
};
|
|
459
|
+
for (const field of schema.fields)
|
|
460
|
+
if ("options" in field && field.options.some((option) => option.id === target.id))
|
|
461
|
+
return updateOption(field.id, target.id, (option) => ({ ...option, label: text }));
|
|
462
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: target.id } };
|
|
463
|
+
}
|
|
464
|
+
if (!["title", "description"].includes(property))
|
|
465
|
+
return {
|
|
466
|
+
success: false,
|
|
467
|
+
error: { type: "invalid_operation", message: `Unsupported page property: ${property}` }
|
|
468
|
+
};
|
|
469
|
+
return updatePage(target.id, (page) => {
|
|
470
|
+
if (property === "title") {
|
|
471
|
+
const { title: _title, ...withoutTitle } = page;
|
|
472
|
+
return text.length === 0 ? withoutTitle : { ...page, title: text };
|
|
473
|
+
}
|
|
474
|
+
const { description: _description, ...withoutDescription } = page;
|
|
475
|
+
return text.length === 0 ? withoutDescription : { ...page, description: text };
|
|
476
|
+
});
|
|
477
|
+
},
|
|
478
|
+
[onChange, schema, textPolicyError, updateField, updateOption, updatePage]
|
|
479
|
+
);
|
|
480
|
+
const setLocaleTranslation = useCallback(
|
|
481
|
+
(locale, target, property, text, options = {}) => {
|
|
482
|
+
const normalized = locale.trim();
|
|
483
|
+
if (normalized.length === 0)
|
|
484
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
485
|
+
const error = textPolicyError(text);
|
|
486
|
+
if (error !== void 0) return error;
|
|
487
|
+
const supportedLocales = [.../* @__PURE__ */ new Set([...schema.supportedLocales ?? [], normalized])];
|
|
488
|
+
const remove = text.length === 0;
|
|
489
|
+
if (target.kind === "form") {
|
|
490
|
+
if (!["title", "description", "completionMessage"].includes(property))
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: { type: "invalid_operation", message: `Unsupported form property: ${property}` }
|
|
494
|
+
};
|
|
495
|
+
const key = property;
|
|
496
|
+
const translations = remove ? removeLocalizedProperty(schema.translations, normalized, key) : { ...schema.translations, [normalized]: { ...schema.translations?.[normalized], [key]: text } };
|
|
497
|
+
onChange(
|
|
498
|
+
setTranslationMetadata(
|
|
499
|
+
{ ...schema, supportedLocales, ...translations === void 0 ? {} : { translations } },
|
|
500
|
+
normalized,
|
|
501
|
+
property,
|
|
502
|
+
options.metadata,
|
|
503
|
+
remove
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
return { success: true };
|
|
507
|
+
}
|
|
508
|
+
if (target.id === void 0)
|
|
509
|
+
return { success: false, error: { type: "invalid_operation", message: "A target ID is required." } };
|
|
510
|
+
let found = false;
|
|
511
|
+
const fields = schema.fields.map((field) => {
|
|
512
|
+
if (target.kind === "field" && field.id === target.id && ["title", "description"].includes(property)) {
|
|
513
|
+
found = true;
|
|
514
|
+
const key = property;
|
|
515
|
+
const translations = remove ? removeLocalizedProperty(field.translations, normalized, key) : { ...field.translations, [normalized]: { ...field.translations?.[normalized], [key]: text } };
|
|
516
|
+
return setTranslationMetadata(
|
|
517
|
+
{ ...field, ...translations === void 0 ? {} : { translations } },
|
|
518
|
+
normalized,
|
|
519
|
+
property,
|
|
520
|
+
options.metadata,
|
|
521
|
+
remove
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
if (target.kind !== "option" || property !== "label" || !("options" in field)) return field;
|
|
525
|
+
return {
|
|
526
|
+
...field,
|
|
527
|
+
options: field.options.map((option) => {
|
|
528
|
+
if (option.id !== target.id) return option;
|
|
529
|
+
found = true;
|
|
530
|
+
const translations = remove ? Object.fromEntries(Object.entries(option.translations ?? {}).filter(([key]) => key !== normalized)) : { ...option.translations, [normalized]: text };
|
|
531
|
+
return setTranslationMetadata({ ...option, translations }, normalized, property, options.metadata, remove);
|
|
532
|
+
})
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
const pages = schema.pages?.map((page) => {
|
|
536
|
+
if (target.kind !== "page" || page.id !== target.id || !["title", "description"].includes(property))
|
|
537
|
+
return page;
|
|
538
|
+
found = true;
|
|
539
|
+
const key = property;
|
|
540
|
+
const translations = remove ? removeLocalizedProperty(page.translations, normalized, key) : { ...page.translations, [normalized]: { ...page.translations?.[normalized], [key]: text } };
|
|
541
|
+
return setTranslationMetadata(
|
|
542
|
+
{ ...page, ...translations === void 0 ? {} : { translations } },
|
|
543
|
+
normalized,
|
|
544
|
+
property,
|
|
545
|
+
options.metadata,
|
|
546
|
+
remove
|
|
547
|
+
);
|
|
548
|
+
});
|
|
549
|
+
if (!found) return { success: false, error: { type: "node_not_found", kind: target.kind, id: target.id } };
|
|
550
|
+
onChange({ ...schema, supportedLocales, fields, ...pages === void 0 ? {} : { pages } });
|
|
551
|
+
return { success: true };
|
|
552
|
+
},
|
|
553
|
+
[onChange, schema, textPolicyError]
|
|
554
|
+
);
|
|
555
|
+
const addLocale = useCallback(
|
|
556
|
+
(locale) => {
|
|
557
|
+
const normalized = locale.trim();
|
|
558
|
+
if (normalized.length === 0)
|
|
559
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
560
|
+
onChange({
|
|
561
|
+
...schema,
|
|
562
|
+
supportedLocales: [
|
|
563
|
+
.../* @__PURE__ */ new Set([
|
|
564
|
+
...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
|
|
565
|
+
...schema.supportedLocales ?? [],
|
|
566
|
+
normalized
|
|
567
|
+
])
|
|
568
|
+
]
|
|
569
|
+
});
|
|
570
|
+
return { success: true };
|
|
571
|
+
},
|
|
572
|
+
[onChange, schema]
|
|
573
|
+
);
|
|
574
|
+
const setDefaultLocale = useCallback(
|
|
575
|
+
(locale) => {
|
|
576
|
+
const normalized = locale.trim();
|
|
577
|
+
if (normalized.length === 0)
|
|
578
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
579
|
+
onChange({
|
|
580
|
+
...schema,
|
|
581
|
+
defaultLocale: normalized,
|
|
582
|
+
supportedLocales: [.../* @__PURE__ */ new Set([normalized, ...schema.supportedLocales ?? []])]
|
|
583
|
+
});
|
|
584
|
+
return { success: true };
|
|
585
|
+
},
|
|
586
|
+
[onChange, schema]
|
|
587
|
+
);
|
|
588
|
+
const validationIssues = useMemo(() => {
|
|
589
|
+
const result = validateFormSchema(schema, policy === void 0 ? {} : { policy });
|
|
590
|
+
return result.valid ? [] : result.issues;
|
|
591
|
+
}, [policy, schema]);
|
|
592
|
+
return {
|
|
593
|
+
schema,
|
|
594
|
+
addField,
|
|
595
|
+
removeField,
|
|
596
|
+
moveField,
|
|
597
|
+
updateField,
|
|
598
|
+
changeFieldType,
|
|
599
|
+
addOption,
|
|
600
|
+
updateOption,
|
|
601
|
+
removeOption,
|
|
602
|
+
moveOption,
|
|
603
|
+
addPage,
|
|
604
|
+
updatePage,
|
|
605
|
+
removePage,
|
|
606
|
+
movePage,
|
|
607
|
+
assignFieldToPage,
|
|
608
|
+
setDisplayCondition,
|
|
609
|
+
setSourceText,
|
|
610
|
+
setLocaleTranslation,
|
|
611
|
+
addLocale,
|
|
612
|
+
setDefaultLocale,
|
|
613
|
+
validationIssues
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// src/builder.tsx
|
|
7
618
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
619
|
var FIELD_TYPES = [
|
|
9
620
|
"text",
|
|
@@ -24,6 +635,7 @@ var BUILDER_DEFAULTS = {
|
|
|
24
635
|
"builder.questionTitle": "\u8CEA\u554F\u6587 / Question Title",
|
|
25
636
|
"builder.questionTitlePlaceholder": "Example: Tell us what we could improve",
|
|
26
637
|
"builder.newQuestionTitle": "New question",
|
|
638
|
+
"builder.completionMessage": "Completion message",
|
|
27
639
|
"builder.type": "Type",
|
|
28
640
|
"builder.required": "Required",
|
|
29
641
|
"builder.minimum": "Minimum",
|
|
@@ -82,33 +694,6 @@ function fieldTypeKey(type) {
|
|
|
82
694
|
function operatorKey(operator) {
|
|
83
695
|
return `builder.operator.${operator}`;
|
|
84
696
|
}
|
|
85
|
-
function createUniqueId(prefix, existingIds) {
|
|
86
|
-
let id;
|
|
87
|
-
do {
|
|
88
|
-
id = `${prefix}_${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
89
|
-
} while (existingIds.has(id));
|
|
90
|
-
return id;
|
|
91
|
-
}
|
|
92
|
-
function baseField(field, type) {
|
|
93
|
-
return {
|
|
94
|
-
id: field.id,
|
|
95
|
-
type,
|
|
96
|
-
title: field.title,
|
|
97
|
-
...field.description === void 0 ? {} : { description: field.description },
|
|
98
|
-
...field.translationKey === void 0 ? {} : { translationKey: field.translationKey },
|
|
99
|
-
required: field.required,
|
|
100
|
-
...field.displayCondition === void 0 ? {} : { displayCondition: field.displayCondition }
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function normalizeField(field, type, newOptionLabel) {
|
|
104
|
-
const base = baseField(field, type);
|
|
105
|
-
if (type === "text" || type === "textarea") return { ...base, type };
|
|
106
|
-
if (type === "number") return { ...base, type };
|
|
107
|
-
if (type === "rating") return { ...base, type, min: 1, max: 5 };
|
|
108
|
-
if (type === "checkbox") return { ...base, type };
|
|
109
|
-
const options = "options" in field && field.options.length > 0 ? field.options : [{ id: createUniqueId("opt", /* @__PURE__ */ new Set()), label: newOptionLabel }];
|
|
110
|
-
return { ...base, type, options };
|
|
111
|
-
}
|
|
112
697
|
function defaultConditionValue(field) {
|
|
113
698
|
if (field.type === "checkbox") return true;
|
|
114
699
|
if (field.type === "number" || field.type === "rating") return field.min ?? 1;
|
|
@@ -122,30 +707,6 @@ function conditionOperators(field) {
|
|
|
122
707
|
}
|
|
123
708
|
return ["equals", "not_equals", "not_empty"];
|
|
124
709
|
}
|
|
125
|
-
function withoutDisplayCondition(field) {
|
|
126
|
-
const { displayCondition: _displayCondition, ...rest } = field;
|
|
127
|
-
return rest;
|
|
128
|
-
}
|
|
129
|
-
function sanitizeBuilderSchema(schema) {
|
|
130
|
-
const sanitized = sanitizeSchema(schema);
|
|
131
|
-
const indexById = new Map(sanitized.fields.map((field, index) => [field.id, index]));
|
|
132
|
-
const fields = sanitized.fields.map((field, index) => {
|
|
133
|
-
const sourceId = field.displayCondition?.questionId;
|
|
134
|
-
if (sourceId === void 0) return field;
|
|
135
|
-
const sourceIndex = indexById.get(sourceId);
|
|
136
|
-
return sourceIndex !== void 0 && sourceIndex < index ? field : withoutDisplayCondition(field);
|
|
137
|
-
});
|
|
138
|
-
return {
|
|
139
|
-
...sanitized,
|
|
140
|
-
fields,
|
|
141
|
-
...sanitized.pages === void 0 ? {} : {
|
|
142
|
-
pages: sanitized.pages.map((page) => ({
|
|
143
|
-
...page,
|
|
144
|
-
questionIds: fields.filter((field) => page.questionIds.includes(field.id)).map((field) => field.id)
|
|
145
|
-
}))
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
710
|
function conditionWithValue(questionId, operator, value) {
|
|
150
711
|
return operator === "not_empty" ? { questionId, operator } : { questionId, operator, value };
|
|
151
712
|
}
|
|
@@ -187,7 +748,25 @@ function ConditionValueEditor({
|
|
|
187
748
|
}
|
|
188
749
|
);
|
|
189
750
|
}
|
|
190
|
-
function FormBuilder({
|
|
751
|
+
function FormBuilder({
|
|
752
|
+
schema,
|
|
753
|
+
onChange,
|
|
754
|
+
locale = "en",
|
|
755
|
+
translator,
|
|
756
|
+
translationAdapter,
|
|
757
|
+
translationOptions,
|
|
758
|
+
onTranslationReport,
|
|
759
|
+
policy,
|
|
760
|
+
idFactory,
|
|
761
|
+
factories
|
|
762
|
+
}) {
|
|
763
|
+
const headless = useFormBuilder({
|
|
764
|
+
schema,
|
|
765
|
+
onChange,
|
|
766
|
+
...policy === void 0 ? {} : { policy },
|
|
767
|
+
...idFactory === void 0 ? {} : { idFactory },
|
|
768
|
+
...factories === void 0 ? {} : { factories }
|
|
769
|
+
});
|
|
191
770
|
const [newPageQuestionId, setNewPageQuestionId] = useState("");
|
|
192
771
|
const [newLocale, setNewLocale] = useState("");
|
|
193
772
|
const [editingLocale, setEditingLocale] = useState("");
|
|
@@ -197,67 +776,17 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
197
776
|
const translated = translator?.translate(key, locale, params);
|
|
198
777
|
return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
|
|
199
778
|
};
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
};
|
|
204
|
-
const changeType = (fieldId, type) => {
|
|
205
|
-
emitSchema({
|
|
206
|
-
...schema,
|
|
207
|
-
fields: schema.fields.map((field) => {
|
|
208
|
-
if (field.id === fieldId) {
|
|
209
|
-
return normalizeField(field, type, translate("builder.newOptionLabel", { index: 1 }));
|
|
210
|
-
}
|
|
211
|
-
if (field.displayCondition?.questionId === fieldId) {
|
|
212
|
-
const { displayCondition: _condition, ...withoutCondition } = field;
|
|
213
|
-
return withoutCondition;
|
|
214
|
-
}
|
|
215
|
-
return field;
|
|
216
|
-
})
|
|
217
|
-
});
|
|
218
|
-
};
|
|
219
|
-
const removeField = (fieldId) => {
|
|
220
|
-
if (schema.fields.length === 1) return;
|
|
221
|
-
emitSchema({ ...schema, fields: schema.fields.filter((field) => field.id !== fieldId) });
|
|
222
|
-
};
|
|
779
|
+
const updateField = headless.updateField;
|
|
780
|
+
const changeType = headless.changeFieldType;
|
|
781
|
+
const removeField = headless.removeField;
|
|
223
782
|
const moveField = (index, offset) => {
|
|
224
783
|
const target = index + offset;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const current = fields[index];
|
|
228
|
-
const other = fields[target];
|
|
229
|
-
if (current === void 0 || other === void 0) return;
|
|
230
|
-
fields[index] = other;
|
|
231
|
-
fields[target] = current;
|
|
232
|
-
emitSchema({ ...schema, fields });
|
|
233
|
-
};
|
|
234
|
-
const addField = () => {
|
|
235
|
-
const id = createUniqueId("q", new Set(schema.fields.map((field) => field.id)));
|
|
236
|
-
const nextSchema = {
|
|
237
|
-
...schema,
|
|
238
|
-
fields: [...schema.fields, { id, type: "text", title: translate("builder.newQuestionTitle"), required: false }]
|
|
239
|
-
};
|
|
240
|
-
emitSchema(
|
|
241
|
-
schema.pages === void 0 ? nextSchema : {
|
|
242
|
-
...nextSchema,
|
|
243
|
-
pages: schema.pages.map(
|
|
244
|
-
(page, index) => index === (schema.pages?.length ?? 0) - 1 ? { ...page, questionIds: [...page.questionIds, id] } : page
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
);
|
|
784
|
+
const field = schema.fields[index];
|
|
785
|
+
if (field !== void 0) headless.moveField(field.id, target);
|
|
248
786
|
};
|
|
787
|
+
const addField = () => headless.addField("text");
|
|
249
788
|
const enablePages = () => {
|
|
250
|
-
if (schema.pages
|
|
251
|
-
emitSchema({
|
|
252
|
-
...schema,
|
|
253
|
-
pages: [
|
|
254
|
-
{
|
|
255
|
-
id: createUniqueId("page", /* @__PURE__ */ new Set()),
|
|
256
|
-
title: translate("builder.newPage"),
|
|
257
|
-
questionIds: schema.fields.map((field) => field.id)
|
|
258
|
-
}
|
|
259
|
-
]
|
|
260
|
-
});
|
|
789
|
+
if (schema.pages === void 0) headless.addPage();
|
|
261
790
|
};
|
|
262
791
|
const pageForField = (fieldId) => schema.pages?.find((page) => page.questionIds.includes(fieldId));
|
|
263
792
|
const movablePageQuestions = schema.pages?.flatMap((page) => page.questionIds.length > 1 ? page.questionIds : []) ?? [];
|
|
@@ -268,73 +797,28 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
268
797
|
}
|
|
269
798
|
const questionId = movablePageQuestions.includes(newPageQuestionId) ? newPageQuestionId : movablePageQuestions[0];
|
|
270
799
|
if (questionId === void 0) return;
|
|
271
|
-
|
|
272
|
-
emitSchema({
|
|
273
|
-
...schema,
|
|
274
|
-
pages: [
|
|
275
|
-
...schema.pages.map((page) => ({
|
|
276
|
-
...page,
|
|
277
|
-
questionIds: page.questionIds.filter((id) => id !== questionId)
|
|
278
|
-
})),
|
|
279
|
-
{ id: pageId, title: translate("builder.newPage"), questionIds: [questionId] }
|
|
280
|
-
]
|
|
281
|
-
});
|
|
800
|
+
headless.addPage(questionId);
|
|
282
801
|
setNewPageQuestionId("");
|
|
283
802
|
};
|
|
284
803
|
const removePage = (pageIndex) => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (removed === void 0) return;
|
|
288
|
-
if (schema.pages.length === 1) {
|
|
289
|
-
const { pages: _pages, ...singlePage } = schema;
|
|
290
|
-
emitSchema(singlePage);
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
const targetIndex = pageIndex === 0 ? 1 : pageIndex - 1;
|
|
294
|
-
emitSchema({
|
|
295
|
-
...schema,
|
|
296
|
-
pages: schema.pages.map(
|
|
297
|
-
(page, index) => index === targetIndex ? { ...page, questionIds: [...page.questionIds, ...removed.questionIds] } : page
|
|
298
|
-
).filter((_page, index) => index !== pageIndex)
|
|
299
|
-
});
|
|
804
|
+
const page = schema.pages?.[pageIndex];
|
|
805
|
+
if (page !== void 0) headless.removePage(page.id);
|
|
300
806
|
};
|
|
301
807
|
const movePage = (pageIndex, offset) => {
|
|
302
|
-
if (schema.pages === void 0) return;
|
|
303
808
|
const target = pageIndex + offset;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const current = pages[pageIndex];
|
|
307
|
-
const other = pages[target];
|
|
308
|
-
if (current === void 0 || other === void 0) return;
|
|
309
|
-
pages[pageIndex] = other;
|
|
310
|
-
pages[target] = current;
|
|
311
|
-
emitSchema({ ...schema, pages });
|
|
809
|
+
const page = schema.pages?.[pageIndex];
|
|
810
|
+
if (page !== void 0) headless.movePage(page.id, target);
|
|
312
811
|
};
|
|
313
812
|
const updatePage = (pageId, update) => {
|
|
314
|
-
|
|
315
|
-
emitSchema({ ...schema, pages: schema.pages.map((page) => page.id === pageId ? update(page) : page) });
|
|
813
|
+
headless.updatePage(pageId, update);
|
|
316
814
|
};
|
|
317
815
|
const assignFieldToPage = (fieldId, pageId) => {
|
|
318
|
-
|
|
319
|
-
emitSchema({
|
|
320
|
-
...schema,
|
|
321
|
-
pages: schema.pages.map((page) => ({
|
|
322
|
-
...page,
|
|
323
|
-
questionIds: page.id === pageId ? schema.fields.filter((field) => page.questionIds.includes(field.id) || field.id === fieldId).map((field) => field.id) : page.questionIds.filter((id) => id !== fieldId)
|
|
324
|
-
})).filter((page) => page.questionIds.length > 0)
|
|
325
|
-
});
|
|
816
|
+
headless.assignFieldToPage(fieldId, pageId);
|
|
326
817
|
};
|
|
327
818
|
const addLocale = () => {
|
|
328
819
|
const normalized = newLocale.trim();
|
|
329
820
|
if (normalized.length === 0) return;
|
|
330
|
-
|
|
331
|
-
.../* @__PURE__ */ new Set([
|
|
332
|
-
...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
|
|
333
|
-
...schema.supportedLocales ?? [],
|
|
334
|
-
normalized
|
|
335
|
-
])
|
|
336
|
-
];
|
|
337
|
-
emitSchema({ ...schema, supportedLocales });
|
|
821
|
+
headless.addLocale(normalized);
|
|
338
822
|
setEditingLocale(normalized);
|
|
339
823
|
setNewLocale("");
|
|
340
824
|
};
|
|
@@ -343,7 +827,14 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
343
827
|
setIsTranslating(true);
|
|
344
828
|
setTranslationError(null);
|
|
345
829
|
try {
|
|
346
|
-
|
|
830
|
+
const populated = await populateSchemaTranslations(
|
|
831
|
+
schema,
|
|
832
|
+
[editingLocale],
|
|
833
|
+
translationAdapter,
|
|
834
|
+
translationOptions ?? { overwrite: "all" }
|
|
835
|
+
);
|
|
836
|
+
onChange(populated.schema);
|
|
837
|
+
onTranslationReport?.(populated.report);
|
|
347
838
|
} catch (cause) {
|
|
348
839
|
setTranslationError(cause instanceof Error ? cause.message : String(cause));
|
|
349
840
|
} finally {
|
|
@@ -352,18 +843,7 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
352
843
|
};
|
|
353
844
|
const updateFormTranslation = (key, value) => {
|
|
354
845
|
if (editingLocale.length === 0) return;
|
|
355
|
-
|
|
356
|
-
const next = key === "title" ? value.length === 0 ? { description: current?.description } : { ...current, title: value } : value.length === 0 ? { title: current?.title } : { ...current, description: value };
|
|
357
|
-
emitSchema({
|
|
358
|
-
...schema,
|
|
359
|
-
translations: {
|
|
360
|
-
...schema.translations,
|
|
361
|
-
[editingLocale]: {
|
|
362
|
-
...next.title === void 0 ? {} : { title: next.title },
|
|
363
|
-
...next.description === void 0 ? {} : { description: next.description }
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
});
|
|
846
|
+
headless.setLocaleTranslation(editingLocale, { kind: "form" }, key, value);
|
|
367
847
|
};
|
|
368
848
|
return /* @__PURE__ */ jsxs("section", { className: "form-engine-builder", "aria-label": translate("builder.formBuilder"), children: [
|
|
369
849
|
/* @__PURE__ */ jsxs("section", { className: "form-engine-builder__pages", "aria-labelledby": "builder-pages-heading", children: [
|
|
@@ -443,19 +923,12 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
443
923
|
"input",
|
|
444
924
|
{
|
|
445
925
|
value: page.translations?.[editingLocale]?.title ?? "",
|
|
446
|
-
onChange: (event) =>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
[editingLocale]: {
|
|
453
|
-
...value.length === 0 ? {} : { title: value },
|
|
454
|
-
...current.translations?.[editingLocale]?.description === void 0 ? {} : { description: current.translations[editingLocale]?.description }
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}));
|
|
458
|
-
}
|
|
926
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
927
|
+
editingLocale,
|
|
928
|
+
{ kind: "page", id: page.id },
|
|
929
|
+
"title",
|
|
930
|
+
event.currentTarget.value
|
|
931
|
+
)
|
|
459
932
|
}
|
|
460
933
|
)
|
|
461
934
|
] }),
|
|
@@ -465,19 +938,12 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
465
938
|
"input",
|
|
466
939
|
{
|
|
467
940
|
value: page.translations?.[editingLocale]?.description ?? "",
|
|
468
|
-
onChange: (event) =>
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
[editingLocale]: {
|
|
475
|
-
...current.translations?.[editingLocale]?.title === void 0 ? {} : { title: current.translations[editingLocale]?.title },
|
|
476
|
-
...value.length === 0 ? {} : { description: value }
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}));
|
|
480
|
-
}
|
|
941
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
942
|
+
editingLocale,
|
|
943
|
+
{ kind: "page", id: page.id },
|
|
944
|
+
"description",
|
|
945
|
+
event.currentTarget.value
|
|
946
|
+
)
|
|
481
947
|
}
|
|
482
948
|
)
|
|
483
949
|
] })
|
|
@@ -565,6 +1031,16 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
565
1031
|
] }),
|
|
566
1032
|
/* @__PURE__ */ jsxs("section", { className: "form-engine-builder__localization", "aria-labelledby": "builder-localization-heading", children: [
|
|
567
1033
|
/* @__PURE__ */ jsx("h2", { id: "builder-localization-heading", children: translate("builder.localization") }),
|
|
1034
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1035
|
+
translate("builder.completionMessage"),
|
|
1036
|
+
/* @__PURE__ */ jsx(
|
|
1037
|
+
"input",
|
|
1038
|
+
{
|
|
1039
|
+
value: schema.completionMessage ?? "",
|
|
1040
|
+
onChange: (event) => headless.setSourceText({ kind: "form" }, "completionMessage", event.currentTarget.value)
|
|
1041
|
+
}
|
|
1042
|
+
)
|
|
1043
|
+
] }),
|
|
568
1044
|
/* @__PURE__ */ jsxs("div", { className: "form-engine-builder__grid", children: [
|
|
569
1045
|
/* @__PURE__ */ jsxs("label", { children: [
|
|
570
1046
|
translate("builder.defaultLocale"),
|
|
@@ -572,16 +1048,7 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
572
1048
|
"input",
|
|
573
1049
|
{
|
|
574
1050
|
value: schema.defaultLocale ?? "",
|
|
575
|
-
onChange: (event) =>
|
|
576
|
-
const value = event.currentTarget.value.trim();
|
|
577
|
-
emitSchema(
|
|
578
|
-
value.length === 0 ? schema : {
|
|
579
|
-
...schema,
|
|
580
|
-
defaultLocale: value,
|
|
581
|
-
supportedLocales: [.../* @__PURE__ */ new Set([value, ...schema.supportedLocales ?? []])]
|
|
582
|
-
}
|
|
583
|
-
);
|
|
584
|
-
}
|
|
1051
|
+
onChange: (event) => headless.setDefaultLocale(event.currentTarget.value)
|
|
585
1052
|
}
|
|
586
1053
|
)
|
|
587
1054
|
] }),
|
|
@@ -629,6 +1096,16 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
629
1096
|
onChange: (event) => updateFormTranslation("description", event.currentTarget.value)
|
|
630
1097
|
}
|
|
631
1098
|
)
|
|
1099
|
+
] }),
|
|
1100
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1101
|
+
translate("builder.completionMessage"),
|
|
1102
|
+
/* @__PURE__ */ jsx(
|
|
1103
|
+
"input",
|
|
1104
|
+
{
|
|
1105
|
+
value: schema.translations?.[editingLocale]?.completionMessage ?? "",
|
|
1106
|
+
onChange: (event) => updateFormTranslation("completionMessage", event.currentTarget.value)
|
|
1107
|
+
}
|
|
1108
|
+
)
|
|
632
1109
|
] })
|
|
633
1110
|
] })
|
|
634
1111
|
] }),
|
|
@@ -692,7 +1169,9 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
692
1169
|
{
|
|
693
1170
|
value: field.type,
|
|
694
1171
|
onChange: (event) => changeType(field.id, event.currentTarget.value),
|
|
695
|
-
children: FIELD_TYPES.
|
|
1172
|
+
children: FIELD_TYPES.filter(
|
|
1173
|
+
(type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
|
|
1174
|
+
).map((type) => /* @__PURE__ */ jsx("option", { value: type, children: translate(fieldTypeKey(type)) }, type))
|
|
696
1175
|
}
|
|
697
1176
|
)
|
|
698
1177
|
] }),
|
|
@@ -728,19 +1207,12 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
728
1207
|
"input",
|
|
729
1208
|
{
|
|
730
1209
|
value: field.translations?.[editingLocale]?.title ?? "",
|
|
731
|
-
onChange: (event) =>
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
[editingLocale]: {
|
|
738
|
-
...value.length === 0 ? {} : { title: value },
|
|
739
|
-
...current.translations?.[editingLocale]?.description === void 0 ? {} : { description: current.translations[editingLocale]?.description }
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
1210
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1211
|
+
editingLocale,
|
|
1212
|
+
{ kind: "field", id: field.id },
|
|
1213
|
+
"title",
|
|
1214
|
+
event.currentTarget.value
|
|
1215
|
+
)
|
|
744
1216
|
}
|
|
745
1217
|
)
|
|
746
1218
|
] }),
|
|
@@ -750,19 +1222,12 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
750
1222
|
"input",
|
|
751
1223
|
{
|
|
752
1224
|
value: field.translations?.[editingLocale]?.description ?? "",
|
|
753
|
-
onChange: (event) =>
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
[editingLocale]: {
|
|
760
|
-
...current.translations?.[editingLocale]?.title === void 0 ? {} : { title: current.translations[editingLocale]?.title },
|
|
761
|
-
...value.length === 0 ? {} : { description: value }
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}));
|
|
765
|
-
}
|
|
1225
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1226
|
+
editingLocale,
|
|
1227
|
+
{ kind: "field", id: field.id },
|
|
1228
|
+
"description",
|
|
1229
|
+
event.currentTarget.value
|
|
1230
|
+
)
|
|
766
1231
|
}
|
|
767
1232
|
)
|
|
768
1233
|
] })
|
|
@@ -776,26 +1241,12 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
776
1241
|
"input",
|
|
777
1242
|
{
|
|
778
1243
|
value: option.translations?.[editingLocale] ?? "",
|
|
779
|
-
onChange: (event) =>
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
options: current.options.map(
|
|
786
|
-
(candidate) => candidate.id === option.id ? {
|
|
787
|
-
...candidate,
|
|
788
|
-
translations: Object.fromEntries([
|
|
789
|
-
...Object.entries(candidate.translations ?? {}).filter(
|
|
790
|
-
([localeKey]) => localeKey !== editingLocale
|
|
791
|
-
),
|
|
792
|
-
...value.length === 0 ? [] : [[editingLocale, value]]
|
|
793
|
-
])
|
|
794
|
-
} : candidate
|
|
795
|
-
)
|
|
796
|
-
};
|
|
797
|
-
});
|
|
798
|
-
}
|
|
1244
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1245
|
+
editingLocale,
|
|
1246
|
+
{ kind: "option", id: option.id },
|
|
1247
|
+
"label",
|
|
1248
|
+
event.currentTarget.value
|
|
1249
|
+
)
|
|
799
1250
|
}
|
|
800
1251
|
)
|
|
801
1252
|
] }, option.id)) : null
|
|
@@ -847,17 +1298,10 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
847
1298
|
"aria-label": translate("builder.optionLabel", { index: optionIndex + 1 }),
|
|
848
1299
|
value: option.label,
|
|
849
1300
|
placeholder: translate("builder.optionLabelPlaceholder"),
|
|
850
|
-
onChange: (event) =>
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return {
|
|
855
|
-
...current,
|
|
856
|
-
options: current.options.map(
|
|
857
|
-
(item, itemIndex) => itemIndex === optionIndex ? { ...item, label } : item
|
|
858
|
-
)
|
|
859
|
-
};
|
|
860
|
-
})
|
|
1301
|
+
onChange: (event) => event.currentTarget.value.trim().length === 0 ? void 0 : headless.updateOption(field.id, option.id, (item) => ({
|
|
1302
|
+
...item,
|
|
1303
|
+
label: event.currentTarget.value
|
|
1304
|
+
}))
|
|
861
1305
|
}
|
|
862
1306
|
),
|
|
863
1307
|
/* @__PURE__ */ jsx(
|
|
@@ -865,13 +1309,7 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
865
1309
|
{
|
|
866
1310
|
type: "button",
|
|
867
1311
|
disabled: field.options.length === 1,
|
|
868
|
-
onClick: () =>
|
|
869
|
-
field.id,
|
|
870
|
-
(current) => "options" in current ? {
|
|
871
|
-
...current,
|
|
872
|
-
options: current.options.filter((_item, itemIndex) => itemIndex !== optionIndex)
|
|
873
|
-
} : current
|
|
874
|
-
),
|
|
1312
|
+
onClick: () => headless.removeOption(field.id, option.id),
|
|
875
1313
|
children: translate("builder.remove")
|
|
876
1314
|
}
|
|
877
1315
|
)
|
|
@@ -880,22 +1318,8 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
880
1318
|
"button",
|
|
881
1319
|
{
|
|
882
1320
|
type: "button",
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
(current) => "options" in current ? (() => {
|
|
886
|
-
const id = createUniqueId("opt", new Set(current.options.map((option) => option.id)));
|
|
887
|
-
return {
|
|
888
|
-
...current,
|
|
889
|
-
options: [
|
|
890
|
-
...current.options,
|
|
891
|
-
{
|
|
892
|
-
id,
|
|
893
|
-
label: translate("builder.newOptionLabel", { index: current.options.length + 1 })
|
|
894
|
-
}
|
|
895
|
-
]
|
|
896
|
-
};
|
|
897
|
-
})() : current
|
|
898
|
-
),
|
|
1321
|
+
disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
|
|
1322
|
+
onClick: () => headless.addOption(field.id),
|
|
899
1323
|
children: translate("builder.addOption")
|
|
900
1324
|
}
|
|
901
1325
|
)
|
|
@@ -909,17 +1333,14 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
909
1333
|
value: condition?.questionId ?? "",
|
|
910
1334
|
onChange: (event) => {
|
|
911
1335
|
const selected = schema.fields.find((item) => item.id === event.currentTarget.value);
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
displayCondition: conditionWithValue(selected.id, operator, defaultConditionValue(selected))
|
|
921
|
-
};
|
|
922
|
-
});
|
|
1336
|
+
headless.setDisplayCondition(
|
|
1337
|
+
field.id,
|
|
1338
|
+
selected === void 0 ? void 0 : conditionWithValue(
|
|
1339
|
+
selected.id,
|
|
1340
|
+
conditionOperators(selected)[0] ?? "not_empty",
|
|
1341
|
+
defaultConditionValue(selected)
|
|
1342
|
+
)
|
|
1343
|
+
);
|
|
923
1344
|
},
|
|
924
1345
|
children: [
|
|
925
1346
|
/* @__PURE__ */ jsx("option", { value: "", children: translate("builder.alwaysVisible") }),
|
|
@@ -936,10 +1357,10 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
936
1357
|
value: condition.operator,
|
|
937
1358
|
onChange: (event) => {
|
|
938
1359
|
const operator = event.currentTarget.value;
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1360
|
+
headless.setDisplayCondition(
|
|
1361
|
+
field.id,
|
|
1362
|
+
conditionWithValue(source.id, operator, defaultConditionValue(source))
|
|
1363
|
+
);
|
|
943
1364
|
},
|
|
944
1365
|
children: conditionOperators(source).map((operator) => /* @__PURE__ */ jsx("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
|
|
945
1366
|
}
|
|
@@ -949,7 +1370,7 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
949
1370
|
{
|
|
950
1371
|
source,
|
|
951
1372
|
condition,
|
|
952
|
-
onChange: (next) =>
|
|
1373
|
+
onChange: (next) => headless.setDisplayCondition(field.id, next),
|
|
953
1374
|
translate
|
|
954
1375
|
}
|
|
955
1376
|
)
|
|
@@ -957,7 +1378,16 @@ function FormBuilder({ schema, onChange, locale = "en", translator, translationA
|
|
|
957
1378
|
] })
|
|
958
1379
|
] }, field.id);
|
|
959
1380
|
}) }),
|
|
960
|
-
/* @__PURE__ */ jsx(
|
|
1381
|
+
/* @__PURE__ */ jsx(
|
|
1382
|
+
"button",
|
|
1383
|
+
{
|
|
1384
|
+
className: "form-engine-builder__add",
|
|
1385
|
+
type: "button",
|
|
1386
|
+
disabled: policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields,
|
|
1387
|
+
onClick: addField,
|
|
1388
|
+
children: translate("builder.addQuestion")
|
|
1389
|
+
}
|
|
1390
|
+
)
|
|
961
1391
|
] });
|
|
962
1392
|
}
|
|
963
1393
|
|
|
@@ -971,7 +1401,7 @@ import {
|
|
|
971
1401
|
validateAnswers,
|
|
972
1402
|
validatePageAnswers
|
|
973
1403
|
} from "@form-engine-ts/core";
|
|
974
|
-
import { createContext, useCallback, useContext, useEffect, useMemo, useState as useState2 } from "react";
|
|
1404
|
+
import { createContext, useCallback as useCallback2, useContext, useEffect, useMemo as useMemo2, useState as useState2 } from "react";
|
|
975
1405
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
976
1406
|
var FormContext = createContext(null);
|
|
977
1407
|
function issuesByField(issues) {
|
|
@@ -988,7 +1418,7 @@ function FormProvider({
|
|
|
988
1418
|
onSubmit,
|
|
989
1419
|
children
|
|
990
1420
|
}) {
|
|
991
|
-
const validSchema =
|
|
1421
|
+
const validSchema = useMemo2(() => {
|
|
992
1422
|
assertValidFormSchema(schema);
|
|
993
1423
|
const localized = resolveLocalizedSchema(schema, locale);
|
|
994
1424
|
assertValidFormSchema(localized);
|
|
@@ -999,13 +1429,13 @@ function FormProvider({
|
|
|
999
1429
|
const [submitStatus, setSubmitStatus] = useState2("idle");
|
|
1000
1430
|
const [submitError, setSubmitError] = useState2(null);
|
|
1001
1431
|
const [validationPageIndex, setValidationPageIndex] = useState2(null);
|
|
1002
|
-
const visibility =
|
|
1003
|
-
const pageVisibility =
|
|
1432
|
+
const visibility = useMemo2(() => calculateFieldVisibility(validSchema, values), [validSchema, values]);
|
|
1433
|
+
const pageVisibility = useMemo2(() => calculatePageVisibility(validSchema, values), [validSchema, values]);
|
|
1004
1434
|
useEffect(() => {
|
|
1005
1435
|
const fieldIds = new Set(validSchema.fields.map((field) => field.id));
|
|
1006
1436
|
setValues((current) => Object.fromEntries(Object.entries(current).filter(([fieldId]) => fieldIds.has(fieldId))));
|
|
1007
1437
|
}, [validSchema]);
|
|
1008
|
-
const setValue =
|
|
1438
|
+
const setValue = useCallback2(
|
|
1009
1439
|
(fieldId, value) => {
|
|
1010
1440
|
setValues((current) => {
|
|
1011
1441
|
const next = { ...current, [fieldId]: value };
|
|
@@ -1021,7 +1451,7 @@ function FormProvider({
|
|
|
1021
1451
|
},
|
|
1022
1452
|
[validSchema, validationPageIndex]
|
|
1023
1453
|
);
|
|
1024
|
-
const restoreValues =
|
|
1454
|
+
const restoreValues = useCallback2(
|
|
1025
1455
|
(restoredValues) => {
|
|
1026
1456
|
const fieldIds = new Set(validSchema.fields.map((field) => field.id));
|
|
1027
1457
|
setValues(Object.fromEntries(Object.entries(restoredValues).filter(([fieldId]) => fieldIds.has(fieldId))));
|
|
@@ -1032,7 +1462,7 @@ function FormProvider({
|
|
|
1032
1462
|
},
|
|
1033
1463
|
[validSchema]
|
|
1034
1464
|
);
|
|
1035
|
-
const validatePage =
|
|
1465
|
+
const validatePage = useCallback2(
|
|
1036
1466
|
(pageIndex) => {
|
|
1037
1467
|
const result = validatePageAnswers(validSchema, pageIndex, values);
|
|
1038
1468
|
setErrors(issuesByField(result.issues));
|
|
@@ -1043,42 +1473,51 @@ function FormProvider({
|
|
|
1043
1473
|
},
|
|
1044
1474
|
[validSchema, values]
|
|
1045
1475
|
);
|
|
1046
|
-
const reset =
|
|
1476
|
+
const reset = useCallback2(() => {
|
|
1047
1477
|
setValues({ ...initialValues });
|
|
1048
1478
|
setErrors({});
|
|
1049
1479
|
setValidationPageIndex(null);
|
|
1050
1480
|
setSubmitStatus("idle");
|
|
1051
1481
|
setSubmitError(null);
|
|
1052
1482
|
}, [initialValues]);
|
|
1053
|
-
const submit =
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1483
|
+
const submit = useCallback2(
|
|
1484
|
+
async (beforeSubmit) => {
|
|
1485
|
+
const validation = validateAnswers(validSchema, values);
|
|
1486
|
+
if (!validation.valid) {
|
|
1487
|
+
setErrors(issuesByField(validation.issues));
|
|
1488
|
+
setValidationPageIndex(null);
|
|
1489
|
+
setSubmitStatus("error");
|
|
1490
|
+
setSubmitError(null);
|
|
1491
|
+
return { status: "invalid", issues: validation.issues };
|
|
1492
|
+
}
|
|
1493
|
+
setErrors({});
|
|
1057
1494
|
setValidationPageIndex(null);
|
|
1058
|
-
setSubmitStatus("error");
|
|
1059
1495
|
setSubmitError(null);
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1496
|
+
const visibleValues = selectVisibleAnswers(validSchema, values);
|
|
1497
|
+
try {
|
|
1498
|
+
if (beforeSubmit !== void 0 && await beforeSubmit(visibleValues) === "cancel") {
|
|
1499
|
+
setSubmitStatus("idle");
|
|
1500
|
+
return { status: "cancelled" };
|
|
1501
|
+
}
|
|
1502
|
+
setSubmitStatus("submitting");
|
|
1503
|
+
await onSubmit(visibleValues);
|
|
1504
|
+
if (resetOnSuccess) setValues({ ...initialValues });
|
|
1505
|
+
setSubmitStatus("success");
|
|
1506
|
+
return { status: "success" };
|
|
1507
|
+
} catch (cause) {
|
|
1508
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
1509
|
+
setSubmitError(error);
|
|
1510
|
+
setSubmitStatus("error");
|
|
1511
|
+
return { status: "error", error };
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
[initialValues, onSubmit, resetOnSuccess, validSchema, values]
|
|
1515
|
+
);
|
|
1516
|
+
const translate = useCallback2(
|
|
1078
1517
|
(key, params) => translator.translate(key, locale, params),
|
|
1079
1518
|
[locale, translator]
|
|
1080
1519
|
);
|
|
1081
|
-
const contextValue =
|
|
1520
|
+
const contextValue = useMemo2(
|
|
1082
1521
|
() => ({
|
|
1083
1522
|
schema: validSchema,
|
|
1084
1523
|
locale,
|
|
@@ -1126,7 +1565,7 @@ function useField(fieldId) {
|
|
|
1126
1565
|
const form = useForm();
|
|
1127
1566
|
const field = form.schema.fields.find((item) => item.id === fieldId);
|
|
1128
1567
|
if (field === void 0) throw new Error(`Unknown form field: ${fieldId}`);
|
|
1129
|
-
const setValue =
|
|
1568
|
+
const setValue = useCallback2((value) => form.setValue(fieldId, value), [fieldId, form]);
|
|
1130
1569
|
return { field, value: form.values[fieldId], error: form.errors[fieldId], setValue };
|
|
1131
1570
|
}
|
|
1132
1571
|
|
|
@@ -1134,8 +1573,15 @@ function useField(fieldId) {
|
|
|
1134
1573
|
import {
|
|
1135
1574
|
validateAnswers as validateAnswers2
|
|
1136
1575
|
} from "@form-engine-ts/core";
|
|
1137
|
-
import {
|
|
1138
|
-
|
|
1576
|
+
import {
|
|
1577
|
+
Fragment as Fragment2,
|
|
1578
|
+
useEffect as useEffect2,
|
|
1579
|
+
useId,
|
|
1580
|
+
useMemo as useMemo3,
|
|
1581
|
+
useRef,
|
|
1582
|
+
useState as useState3
|
|
1583
|
+
} from "react";
|
|
1584
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1139
1585
|
function describedBy(field, error, helpId, errorId) {
|
|
1140
1586
|
const ids = [field.description === void 0 ? void 0 : helpId, error === void 0 ? void 0 : errorId].filter(
|
|
1141
1587
|
Boolean
|
|
@@ -1149,7 +1595,7 @@ function RequiredMark({ required }) {
|
|
|
1149
1595
|
] }) : null;
|
|
1150
1596
|
}
|
|
1151
1597
|
function FieldMessage({ props }) {
|
|
1152
|
-
return /* @__PURE__ */ jsxs2(
|
|
1598
|
+
return /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
1153
1599
|
props.field.description === void 0 ? null : /* @__PURE__ */ jsx3("div", { id: props.helpId, className: "fe-help", children: props.field.description }),
|
|
1154
1600
|
props.error === void 0 ? null : /* @__PURE__ */ jsx3("div", { id: props.errorId, className: "fe-error", children: props.translate(props.error.messageKey, props.error.params) })
|
|
1155
1601
|
] });
|
|
@@ -1337,12 +1783,15 @@ function parseDraft(serialized) {
|
|
|
1337
1783
|
return null;
|
|
1338
1784
|
}
|
|
1339
1785
|
}
|
|
1340
|
-
function
|
|
1786
|
+
function ContextFormRenderer({
|
|
1341
1787
|
components = {},
|
|
1342
1788
|
className = "",
|
|
1343
1789
|
successMessageKey,
|
|
1344
1790
|
errorMessageKey,
|
|
1345
|
-
autoSaveKey
|
|
1791
|
+
autoSaveKey,
|
|
1792
|
+
beforeSubmit,
|
|
1793
|
+
onDraftSave,
|
|
1794
|
+
slots = {}
|
|
1346
1795
|
}) {
|
|
1347
1796
|
const form = useForm();
|
|
1348
1797
|
const prefix = useId().replace(/:/g, "");
|
|
@@ -1352,7 +1801,7 @@ function FormRenderer({
|
|
|
1352
1801
|
const [currentPageIndex, setCurrentPageIndex] = useState3(0);
|
|
1353
1802
|
const [focusFieldId, setFocusFieldId] = useState3(null);
|
|
1354
1803
|
const pages = form.schema.pages;
|
|
1355
|
-
const visiblePageIndexes =
|
|
1804
|
+
const visiblePageIndexes = useMemo3(
|
|
1356
1805
|
() => pages === void 0 ? [] : pages.flatMap((page, index) => form.pageVisibility[page.id] === true ? [index] : []),
|
|
1357
1806
|
[form.pageVisibility, pages]
|
|
1358
1807
|
);
|
|
@@ -1390,9 +1839,10 @@ function FormRenderer({
|
|
|
1390
1839
|
setDraftRestored(true);
|
|
1391
1840
|
}, [autoSaveKey, form.restoreValues, form.schema.id, form.schema.version]);
|
|
1392
1841
|
useEffect2(() => {
|
|
1393
|
-
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
1394
1842
|
if (form.submitStatus === "success") return;
|
|
1395
1843
|
const timeout = globalThis.setTimeout(() => {
|
|
1844
|
+
onDraftSave?.(form.values);
|
|
1845
|
+
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
1396
1846
|
const draft = {
|
|
1397
1847
|
formId: form.schema.id,
|
|
1398
1848
|
formVersion: form.schema.version,
|
|
@@ -1402,7 +1852,7 @@ function FormRenderer({
|
|
|
1402
1852
|
globalThis.localStorage.setItem(autoSaveKey, JSON.stringify(draft));
|
|
1403
1853
|
}, 500);
|
|
1404
1854
|
return () => globalThis.clearTimeout(timeout);
|
|
1405
|
-
}, [autoSaveKey, form.schema.id, form.schema.version, form.submitStatus, form.values]);
|
|
1855
|
+
}, [autoSaveKey, form.schema.id, form.schema.version, form.submitStatus, form.values, onDraftSave]);
|
|
1406
1856
|
const focusFirstIssue = (fieldId) => {
|
|
1407
1857
|
if (fieldId !== void 0) setFocusFieldId(fieldId);
|
|
1408
1858
|
};
|
|
@@ -1415,27 +1865,39 @@ function FormRenderer({
|
|
|
1415
1865
|
const nextPageIndex = visiblePageIndexes[activeVisibleIndex + 1];
|
|
1416
1866
|
if (nextPageIndex !== void 0) setCurrentPageIndex(nextPageIndex);
|
|
1417
1867
|
};
|
|
1418
|
-
const
|
|
1419
|
-
event.preventDefault();
|
|
1868
|
+
const submitValues = async () => {
|
|
1420
1869
|
const validation = validateAnswers2(form.schema, form.values);
|
|
1421
1870
|
const firstInvalidFieldId = validation.issues[0]?.fieldId;
|
|
1422
|
-
const
|
|
1423
|
-
if (
|
|
1871
|
+
const result = await form.submit(beforeSubmit);
|
|
1872
|
+
if (result.status === "invalid") {
|
|
1424
1873
|
const invalidPageIndex = pages?.findIndex(
|
|
1425
1874
|
(page) => firstInvalidFieldId === void 0 ? false : page.questionIds.includes(firstInvalidFieldId)
|
|
1426
1875
|
);
|
|
1427
1876
|
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
1428
1877
|
focusFirstIssue(firstInvalidFieldId);
|
|
1429
|
-
return;
|
|
1878
|
+
return result;
|
|
1430
1879
|
}
|
|
1880
|
+
if (result.status !== "success") return result;
|
|
1431
1881
|
if (autoSaveKey !== void 0 && typeof globalThis.localStorage !== "undefined") {
|
|
1432
1882
|
globalThis.localStorage.removeItem(autoSaveKey);
|
|
1433
1883
|
setDraftRestored(false);
|
|
1434
1884
|
}
|
|
1435
1885
|
setCurrentPageIndex(visiblePageIndexes[0] ?? 0);
|
|
1886
|
+
return result;
|
|
1436
1887
|
};
|
|
1888
|
+
const handleSubmit = (event) => {
|
|
1889
|
+
event.preventDefault();
|
|
1890
|
+
void submitValues();
|
|
1891
|
+
};
|
|
1892
|
+
const validationIssues = Object.values(form.errors).filter((issue) => issue !== void 0);
|
|
1893
|
+
const canPrev = pages !== void 0 && activeVisibleIndex > 0;
|
|
1894
|
+
const canNext = pages !== void 0 && activeVisibleIndex < visiblePageIndexes.length - 1;
|
|
1895
|
+
const renderSubmitButton = () => slots.renderSubmitButton?.({ isSubmitting: form.isSubmitting, onSubmit: () => void submitValues() }) ?? /* @__PURE__ */ jsx3("button", { className: "fe-submit", type: "submit", disabled: form.isSubmitting, children: form.translate(form.schema.submitLabelKey ?? "form.submit") });
|
|
1437
1896
|
return /* @__PURE__ */ jsxs2("form", { ref: formRef, className: `fe-form ${className}`.trim(), noValidate: true, onSubmit: handleSubmit, children: [
|
|
1438
|
-
|
|
1897
|
+
slots.renderHeader?.({
|
|
1898
|
+
title: form.schema.title,
|
|
1899
|
+
...form.schema.description === void 0 ? {} : { description: form.schema.description }
|
|
1900
|
+
}) ?? /* @__PURE__ */ jsxs2("header", { className: "fe-header", children: [
|
|
1439
1901
|
/* @__PURE__ */ jsx3("h1", { children: form.schema.title }),
|
|
1440
1902
|
form.schema.description === void 0 ? null : /* @__PURE__ */ jsx3("p", { children: form.schema.description }),
|
|
1441
1903
|
pages === void 0 ? null : /* @__PURE__ */ jsxs2("div", { className: "fe-progress", children: [
|
|
@@ -1460,44 +1922,130 @@ function FormRenderer({
|
|
|
1460
1922
|
] }),
|
|
1461
1923
|
draftRestored ? /* @__PURE__ */ jsx3("span", { className: "form-draft-badge", children: form.translate("form.draftRestored") }) : null
|
|
1462
1924
|
] }),
|
|
1463
|
-
activePage
|
|
1464
|
-
|
|
1925
|
+
activePage === void 0 ? null : slots.renderPageHeader?.({
|
|
1926
|
+
page: activePage,
|
|
1927
|
+
pageIndex: activeVisibleIndex,
|
|
1928
|
+
totalPages: visiblePageIndexes.length
|
|
1929
|
+
}) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-page-header", children: [
|
|
1930
|
+
activePage.title === void 0 ? null : /* @__PURE__ */ jsx3("h2", { className: "fe-page-title", children: activePage.title }),
|
|
1931
|
+
activePage.description === void 0 ? null : /* @__PURE__ */ jsx3("p", { className: "fe-page-description", children: activePage.description })
|
|
1932
|
+
] }),
|
|
1465
1933
|
/* @__PURE__ */ jsx3("div", { className: "fe-fields", children: form.schema.fields.filter((field) => form.visibility[field.id] === true && (fieldIds === void 0 || fieldIds.has(field.id))).map((field) => {
|
|
1934
|
+
const error = form.errors[field.id];
|
|
1466
1935
|
const props = {
|
|
1467
1936
|
field,
|
|
1468
1937
|
value: form.values[field.id],
|
|
1469
|
-
error
|
|
1938
|
+
error,
|
|
1470
1939
|
setValue: (value) => form.setValue(field.id, value),
|
|
1471
1940
|
translate: form.translate,
|
|
1472
1941
|
inputId: `${prefix}-${field.id}`,
|
|
1473
1942
|
errorId: `${prefix}-${field.id}-error`,
|
|
1474
1943
|
helpId: `${prefix}-${field.id}-help`
|
|
1475
1944
|
};
|
|
1945
|
+
if (slots.renderField !== void 0) {
|
|
1946
|
+
return /* @__PURE__ */ jsx3(Fragment2, { children: slots.renderField({
|
|
1947
|
+
question: field,
|
|
1948
|
+
value: form.values[field.id],
|
|
1949
|
+
onChange: (value) => {
|
|
1950
|
+
if (isFormValue(value)) form.setValue(field.id, value);
|
|
1951
|
+
},
|
|
1952
|
+
...error === void 0 ? {} : { error }
|
|
1953
|
+
}) }, field.id);
|
|
1954
|
+
}
|
|
1476
1955
|
const Component = components[field.type];
|
|
1477
1956
|
return Component === void 0 ? /* @__PURE__ */ jsx3(DefaultField, { ...props }, field.id) : /* @__PURE__ */ jsx3(Component, { ...props }, field.id);
|
|
1478
1957
|
}) }),
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1958
|
+
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
1959
|
+
validationIssues.length,
|
|
1960
|
+
" validation error",
|
|
1961
|
+
validationIssues.length === 1 ? "" : "s",
|
|
1962
|
+
"."
|
|
1963
|
+
] }),
|
|
1964
|
+
pages === void 0 ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
1965
|
+
slots.renderNavigation?.({
|
|
1966
|
+
currentPage: 0,
|
|
1967
|
+
totalPages: 1,
|
|
1968
|
+
canPrev: false,
|
|
1969
|
+
canNext: false,
|
|
1970
|
+
onPrev: () => void 0,
|
|
1971
|
+
onNext: () => void 0
|
|
1972
|
+
}),
|
|
1973
|
+
renderSubmitButton()
|
|
1974
|
+
] }) : /* @__PURE__ */ jsxs2("div", { className: "form-step-navigation", children: [
|
|
1975
|
+
slots.renderNavigation?.({
|
|
1976
|
+
currentPage: activeVisibleIndex,
|
|
1977
|
+
totalPages: visiblePageIndexes.length,
|
|
1978
|
+
canPrev,
|
|
1979
|
+
canNext,
|
|
1980
|
+
onPrev: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
1981
|
+
onNext: handleNext
|
|
1982
|
+
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
1983
|
+
canPrev ? /* @__PURE__ */ jsx3(
|
|
1984
|
+
"button",
|
|
1985
|
+
{
|
|
1986
|
+
className: "btn-prev",
|
|
1987
|
+
type: "button",
|
|
1988
|
+
onClick: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
1989
|
+
children: form.translate("form.back")
|
|
1990
|
+
}
|
|
1991
|
+
) : null,
|
|
1992
|
+
canNext ? /* @__PURE__ */ jsx3("button", { className: "btn-next", type: "button", onClick: handleNext, children: form.translate("form.next") }) : null
|
|
1993
|
+
] }),
|
|
1994
|
+
canNext ? null : renderSubmitButton()
|
|
1490
1995
|
] }),
|
|
1491
1996
|
/* @__PURE__ */ jsxs2("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
1492
|
-
form.submitStatus === "success"
|
|
1493
|
-
|
|
1997
|
+
form.submitStatus === "success" ? slots.renderCompletion?.({
|
|
1998
|
+
message: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey))
|
|
1999
|
+
}) ?? /* @__PURE__ */ jsx3("div", { role: "status", children: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey)) }) : null,
|
|
2000
|
+
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (errorMessageKey === void 0 ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: form.translate(errorMessageKey) })) : null
|
|
1494
2001
|
] })
|
|
1495
2002
|
] });
|
|
1496
2003
|
}
|
|
2004
|
+
var RENDERER_MESSAGES = {
|
|
2005
|
+
"form.submit": "Submit",
|
|
2006
|
+
"form.back": "Back",
|
|
2007
|
+
"form.next": "Next",
|
|
2008
|
+
"form.step": "Step {{current}} / {{total}}",
|
|
2009
|
+
"form.draftRestored": "Draft restored",
|
|
2010
|
+
"validation.required": "This field is required."
|
|
2011
|
+
};
|
|
2012
|
+
var defaultRendererTranslator = {
|
|
2013
|
+
translate(key, _locale, params = {}) {
|
|
2014
|
+
return (RENDERER_MESSAGES[key] ?? key).replace(
|
|
2015
|
+
/\{\{(\w+)\}\}/g,
|
|
2016
|
+
(token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
};
|
|
2020
|
+
function FormRenderer(props) {
|
|
2021
|
+
if (!("schema" in props)) return /* @__PURE__ */ jsx3(ContextFormRenderer, { ...props });
|
|
2022
|
+
const {
|
|
2023
|
+
schema,
|
|
2024
|
+
locale = schema.defaultLocale ?? "en",
|
|
2025
|
+
translator = defaultRendererTranslator,
|
|
2026
|
+
initialValues,
|
|
2027
|
+
resetOnSuccess,
|
|
2028
|
+
onSubmit,
|
|
2029
|
+
...rendererProps
|
|
2030
|
+
} = props;
|
|
2031
|
+
return /* @__PURE__ */ jsx3(
|
|
2032
|
+
FormProvider,
|
|
2033
|
+
{
|
|
2034
|
+
schema,
|
|
2035
|
+
locale,
|
|
2036
|
+
translator,
|
|
2037
|
+
onSubmit,
|
|
2038
|
+
...initialValues === void 0 ? {} : { initialValues },
|
|
2039
|
+
...resetOnSuccess === void 0 ? {} : { resetOnSuccess },
|
|
2040
|
+
children: /* @__PURE__ */ jsx3(ContextFormRenderer, { ...rendererProps })
|
|
2041
|
+
}
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
1497
2044
|
export {
|
|
1498
2045
|
FormBuilder,
|
|
1499
2046
|
FormProvider,
|
|
1500
2047
|
FormRenderer,
|
|
1501
2048
|
useField,
|
|
1502
|
-
useForm
|
|
2049
|
+
useForm,
|
|
2050
|
+
useFormBuilder
|
|
1503
2051
|
};
|