@form-engine-ts/react 2.0.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 +12 -7
- package/dist/index.cjs +568 -499
- package/dist/index.d.cts +69 -27
- package/dist/index.d.ts +69 -27
- package/dist/index.js +570 -501
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -37,21 +37,26 @@ var import_react2 = require("react");
|
|
|
37
37
|
var import_core = require("@form-engine-ts/core");
|
|
38
38
|
var import_react = require("react");
|
|
39
39
|
var DEFAULT_PREFIXES = { field: "q", option: "opt", page: "page" };
|
|
40
|
+
var CHOICE_TYPES = ["select", "radio", "multi-select"];
|
|
40
41
|
function defaultIdFactory(kind, existingIds) {
|
|
41
42
|
const prefix = DEFAULT_PREFIXES[kind];
|
|
42
43
|
let id;
|
|
43
|
-
do
|
|
44
|
+
do
|
|
44
45
|
id = `${prefix}_${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
45
|
-
|
|
46
|
+
while (existingIds.has(id));
|
|
46
47
|
return id;
|
|
47
48
|
}
|
|
48
|
-
function
|
|
49
|
+
function defaultCreateField(type, id) {
|
|
49
50
|
const base = { id, title: "New question", required: false };
|
|
50
|
-
if (type === "text" || type === "textarea") return { ...base, type };
|
|
51
|
-
if (type === "number") return { ...base, type };
|
|
51
|
+
if (type === "text" || type === "textarea" || type === "number" || type === "checkbox") return { ...base, type };
|
|
52
52
|
if (type === "rating") return { ...base, type, min: 1, max: 5 };
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return { ...base, type, options: [] };
|
|
54
|
+
}
|
|
55
|
+
function defaultCreateOption(field, id) {
|
|
56
|
+
return { id, label: `Option ${"options" in field ? field.options.length + 1 : 1}` };
|
|
57
|
+
}
|
|
58
|
+
function defaultCreatePage(id, questionIds) {
|
|
59
|
+
return { id, title: "New page", questionIds };
|
|
55
60
|
}
|
|
56
61
|
function move(items, sourceIndex, targetIndex) {
|
|
57
62
|
if (sourceIndex < 0 || targetIndex < 0 || targetIndex >= items.length || sourceIndex === targetIndex)
|
|
@@ -62,334 +67,553 @@ function move(items, sourceIndex, targetIndex) {
|
|
|
62
67
|
result.splice(targetIndex, 0, item);
|
|
63
68
|
return result;
|
|
64
69
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
["title", field.title],
|
|
84
|
-
["description", field.description]
|
|
85
|
-
]) {
|
|
86
|
-
if (text !== void 0 && text.length > policy.maxTextLength) {
|
|
87
|
-
issues.push({
|
|
88
|
-
path: `fields[${index}].${property}`,
|
|
89
|
-
code: "max_text_length_exceeded",
|
|
90
|
-
message: `Text must be at most ${policy.maxTextLength} characters.`
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (policy?.maxOptionsPerField !== void 0 && "options" in field && field.options.length > policy.maxOptionsPerField) {
|
|
96
|
-
issues.push({
|
|
97
|
-
path: `fields[${index}].options`,
|
|
98
|
-
code: "max_options_exceeded",
|
|
99
|
-
message: `At most ${policy.maxOptionsPerField} options are allowed.`
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
for (const locale of policy?.requiredLocales ?? []) {
|
|
104
|
-
if (!(schema.supportedLocales ?? []).includes(locale)) {
|
|
105
|
-
issues.push({
|
|
106
|
-
path: "supportedLocales",
|
|
107
|
-
code: "required_locale_missing",
|
|
108
|
-
message: `Required locale ${locale} is missing.`
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
if (locale === schema.defaultLocale) continue;
|
|
112
|
-
const requiredTranslations = [
|
|
113
|
-
{ path: `translations.${locale}.title`, value: schema.translations?.[locale]?.title },
|
|
114
|
-
...schema.fields.map((field, index) => ({
|
|
115
|
-
path: `fields[${index}].translations.${locale}.title`,
|
|
116
|
-
value: field.translations?.[locale]?.title
|
|
117
|
-
})),
|
|
118
|
-
...schema.fields.flatMap(
|
|
119
|
-
(field, fieldIndex) => "options" in field ? field.options.map((option, optionIndex) => ({
|
|
120
|
-
path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`,
|
|
121
|
-
value: option.translations?.[locale]
|
|
122
|
-
})) : []
|
|
123
|
-
),
|
|
124
|
-
...schema.pages?.flatMap(
|
|
125
|
-
(page, pageIndex) => page.title === void 0 ? [] : [{ path: `pages[${pageIndex}].translations.${locale}.title`, value: page.translations?.[locale]?.title }]
|
|
126
|
-
) ?? []
|
|
127
|
-
];
|
|
128
|
-
for (const translation of requiredTranslations) {
|
|
129
|
-
if (translation.value === void 0 || translation.value.trim().length === 0) {
|
|
130
|
-
issues.push({
|
|
131
|
-
path: translation.path,
|
|
132
|
-
code: "required_translation_missing",
|
|
133
|
-
message: `A translation for required locale ${locale} is missing.`
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
70
|
+
function withoutDisplayCondition(field) {
|
|
71
|
+
const { displayCondition: _displayCondition, ...rest } = field;
|
|
72
|
+
return rest;
|
|
73
|
+
}
|
|
74
|
+
function removeLocalizedProperty(translations, locale, property) {
|
|
75
|
+
const current = translations?.[locale];
|
|
76
|
+
if (current === void 0) return translations;
|
|
77
|
+
const { [property]: _removed, ...remaining } = current;
|
|
78
|
+
return { ...translations, [locale]: remaining };
|
|
79
|
+
}
|
|
80
|
+
function setTranslationMetadata(node, locale, property, metadata, remove) {
|
|
81
|
+
const localeMetadata = node.translationMetadata?.[locale];
|
|
82
|
+
if (metadata === void 0 && !remove) return node;
|
|
83
|
+
const nextLocaleMetadata = remove ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== property)) : { ...localeMetadata, [property]: metadata ?? {} };
|
|
84
|
+
return { ...node, translationMetadata: { ...node.translationMetadata, [locale]: nextLocaleMetadata } };
|
|
85
|
+
}
|
|
86
|
+
function failedId(kind, result) {
|
|
87
|
+
return { success: false, error: result.error ?? { type: "invalid_id", kind, id: "" } };
|
|
138
88
|
}
|
|
139
89
|
function useFormBuilder({
|
|
140
90
|
schema,
|
|
141
91
|
onChange,
|
|
142
92
|
policy,
|
|
143
|
-
idFactory = defaultIdFactory
|
|
93
|
+
idFactory = defaultIdFactory,
|
|
94
|
+
factories = {}
|
|
144
95
|
}) {
|
|
145
96
|
const createId = (0, import_react.useCallback)(
|
|
146
97
|
(kind, existingIds) => {
|
|
147
|
-
const
|
|
148
|
-
|
|
98
|
+
const rawId = idFactory(kind, existingIds);
|
|
99
|
+
const id = rawId.trim();
|
|
100
|
+
return id.length > 0 && !existingIds.has(id) ? { id } : { error: { type: "invalid_id", kind, id: rawId } };
|
|
149
101
|
},
|
|
150
102
|
[idFactory]
|
|
151
103
|
);
|
|
104
|
+
const textPolicyError = (0, import_react.useCallback)(
|
|
105
|
+
(text) => policy?.maxTextLength !== void 0 && text.length > policy.maxTextLength ? { success: false, error: { type: "max_text_length_exceeded", max: policy.maxTextLength } } : void 0,
|
|
106
|
+
[policy?.maxTextLength]
|
|
107
|
+
);
|
|
108
|
+
const updateField = (0, import_react.useCallback)(
|
|
109
|
+
(fieldId, updater) => {
|
|
110
|
+
const current = schema.fields.find((field) => field.id === fieldId);
|
|
111
|
+
if (current === void 0)
|
|
112
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
113
|
+
const updated = updater(current);
|
|
114
|
+
if (updated.id !== fieldId)
|
|
115
|
+
return { success: false, error: { type: "invalid_id", kind: "field", id: updated.id } };
|
|
116
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(updated.type))
|
|
117
|
+
return { success: false, error: { type: "disallowed_field_type", fieldType: updated.type } };
|
|
118
|
+
for (const text of [updated.title, updated.description]) {
|
|
119
|
+
if (text !== void 0) {
|
|
120
|
+
const error = textPolicyError(text);
|
|
121
|
+
if (error !== void 0) return error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
onChange({ ...schema, fields: schema.fields.map((field) => field.id === fieldId ? updated : field) });
|
|
125
|
+
return { success: true };
|
|
126
|
+
},
|
|
127
|
+
[onChange, policy?.allowedFieldTypes, schema, textPolicyError]
|
|
128
|
+
);
|
|
129
|
+
const updateOption = (0, import_react.useCallback)(
|
|
130
|
+
(fieldId, optionId, updater) => {
|
|
131
|
+
const field = schema.fields.find((candidate) => candidate.id === fieldId);
|
|
132
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
133
|
+
if (!("options" in field))
|
|
134
|
+
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
135
|
+
const current = field.options.find((option) => option.id === optionId);
|
|
136
|
+
if (current === void 0)
|
|
137
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: optionId } };
|
|
138
|
+
const updated = updater(current);
|
|
139
|
+
if (updated.id !== optionId)
|
|
140
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: updated.id } };
|
|
141
|
+
const error = textPolicyError(updated.label);
|
|
142
|
+
if (error !== void 0) return error;
|
|
143
|
+
onChange({
|
|
144
|
+
...schema,
|
|
145
|
+
fields: schema.fields.map(
|
|
146
|
+
(candidate) => candidate.id === fieldId && "options" in candidate ? {
|
|
147
|
+
...candidate,
|
|
148
|
+
options: candidate.options.map((option) => option.id === optionId ? updated : option)
|
|
149
|
+
} : candidate
|
|
150
|
+
)
|
|
151
|
+
});
|
|
152
|
+
return { success: true };
|
|
153
|
+
},
|
|
154
|
+
[onChange, schema, textPolicyError]
|
|
155
|
+
);
|
|
156
|
+
const updatePage = (0, import_react.useCallback)(
|
|
157
|
+
(pageId, updater) => {
|
|
158
|
+
const page = schema.pages?.find((candidate) => candidate.id === pageId);
|
|
159
|
+
if (page === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
160
|
+
const pages = schema.pages;
|
|
161
|
+
if (pages === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
162
|
+
const updated = updater(page);
|
|
163
|
+
if (updated.id !== pageId) return { success: false, error: { type: "invalid_id", kind: "page", id: updated.id } };
|
|
164
|
+
onChange({ ...schema, pages: pages.map((candidate) => candidate.id === pageId ? updated : candidate) });
|
|
165
|
+
return { success: true };
|
|
166
|
+
},
|
|
167
|
+
[onChange, schema]
|
|
168
|
+
);
|
|
152
169
|
const addField = (0, import_react.useCallback)(
|
|
153
170
|
(type, pageId) => {
|
|
154
|
-
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(type))
|
|
171
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(type))
|
|
155
172
|
return { success: false, error: { type: "disallowed_field_type", fieldType: type } };
|
|
156
|
-
|
|
157
|
-
if (policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields) {
|
|
173
|
+
if (policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields)
|
|
158
174
|
return { success: false, error: { type: "max_fields_exceeded", max: policy.maxFields } };
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
if (pageId !== void 0 && !schema.pages?.some((page) => page.id === pageId))
|
|
176
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
177
|
+
const fieldIds = new Set(schema.fields.map((field2) => field2.id));
|
|
178
|
+
const fieldId = createId("field", fieldIds);
|
|
179
|
+
if (fieldId.id === void 0) return failedId("field", fieldId);
|
|
180
|
+
let field = (factories.createField ?? defaultCreateField)(type, fieldId.id);
|
|
181
|
+
if (field.id !== fieldId.id || field.type !== type || fieldIds.has(field.id))
|
|
182
|
+
return { success: false, error: { type: "invalid_id", kind: "field", id: field.id } };
|
|
183
|
+
if (CHOICE_TYPES.includes(type) && "options" in field && field.options.length === 0) {
|
|
184
|
+
const optionIds = new Set(
|
|
185
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
186
|
+
);
|
|
187
|
+
const optionId = createId("option", optionIds);
|
|
188
|
+
if (optionId.id === void 0) return failedId("option", optionId);
|
|
189
|
+
const option = (factories.createOption ?? defaultCreateOption)(field, optionId.id);
|
|
190
|
+
if (option.id !== optionId.id || optionIds.has(option.id))
|
|
191
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
192
|
+
field = { ...field, options: [option] };
|
|
177
193
|
}
|
|
178
194
|
const pages = schema.pages?.map((page, index) => ({
|
|
179
195
|
...page,
|
|
180
|
-
questionIds: page.id === pageId || pageId === void 0 && index === (schema.pages?.length ?? 0) - 1 ? [...page.questionIds,
|
|
196
|
+
questionIds: page.id === pageId || pageId === void 0 && index === (schema.pages?.length ?? 0) - 1 ? [...page.questionIds, field.id] : page.questionIds
|
|
181
197
|
}));
|
|
182
|
-
if (schema.pages !== void 0 && !schema.pages.some((page) => page.id === pageId) && pageId !== void 0) {
|
|
183
|
-
return { success: false, error: { type: "invalid_operation", message: `Unknown page: ${pageId}` } };
|
|
184
|
-
}
|
|
185
198
|
onChange({ ...schema, fields: [...schema.fields, field], ...pages === void 0 ? {} : { pages } });
|
|
186
199
|
return { success: true };
|
|
187
200
|
},
|
|
188
|
-
[createId, onChange, policy, schema]
|
|
201
|
+
[createId, factories, onChange, policy, schema]
|
|
189
202
|
);
|
|
190
203
|
const removeField = (0, import_react.useCallback)(
|
|
191
204
|
(fieldId) => {
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
205
|
+
if (!schema.fields.some((field) => field.id === fieldId))
|
|
206
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
207
|
+
if (schema.fields.length <= 1)
|
|
208
|
+
return { success: false, error: { type: "invalid_operation", message: "A form must contain one field." } };
|
|
209
|
+
const fields = schema.fields.filter((field) => field.id !== fieldId).map((field) => field.displayCondition?.questionId === fieldId ? withoutDisplayCondition(field) : field);
|
|
210
|
+
const pages = schema.pages?.map((page) => ({ ...page, questionIds: page.questionIds.filter((id) => id !== fieldId) })).filter((page) => page.questionIds.length > 0);
|
|
211
|
+
if (schema.pages !== void 0 && pages?.length === 0) {
|
|
212
|
+
const { pages: _pages, ...single } = schema;
|
|
213
|
+
onChange({ ...single, fields });
|
|
214
|
+
} else onChange({ ...schema, fields, ...pages === void 0 ? {} : { pages } });
|
|
215
|
+
return { success: true };
|
|
203
216
|
},
|
|
204
217
|
[onChange, schema]
|
|
205
218
|
);
|
|
206
219
|
const moveField = (0, import_react.useCallback)(
|
|
207
220
|
(fieldId, targetIndex) => {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
221
|
+
const sourceIndex = schema.fields.findIndex((field) => field.id === fieldId);
|
|
222
|
+
if (sourceIndex < 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
223
|
+
const fields = move(schema.fields, sourceIndex, targetIndex);
|
|
224
|
+
if (fields === void 0)
|
|
225
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid field position." } };
|
|
226
|
+
const indexById = new Map(fields.map((field, index) => [field.id, index]));
|
|
227
|
+
onChange({
|
|
228
|
+
...schema,
|
|
229
|
+
fields: fields.map((field, index) => {
|
|
230
|
+
const source = field.displayCondition?.questionId;
|
|
231
|
+
return source === void 0 || (indexById.get(source) ?? index) < index ? field : withoutDisplayCondition(field);
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
return { success: true };
|
|
223
235
|
},
|
|
224
236
|
[onChange, schema]
|
|
225
237
|
);
|
|
226
|
-
const updateField = (0, import_react.useCallback)(
|
|
227
|
-
(fieldId, updater) => {
|
|
228
|
-
const current = schema.fields.find((field) => field.id === fieldId);
|
|
229
|
-
if (current === void 0) return;
|
|
230
|
-
const updated = updater(current);
|
|
231
|
-
if (updated.id !== fieldId) return;
|
|
232
|
-
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(updated.type)) return;
|
|
233
|
-
const maxTextLength = policy?.maxTextLength;
|
|
234
|
-
if (maxTextLength !== void 0 && [updated.title, updated.description].some((text) => text !== void 0 && text.length > maxTextLength)) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
onChange({ ...schema, fields: schema.fields.map((field) => field.id === fieldId ? updated : field) });
|
|
238
|
-
},
|
|
239
|
-
[onChange, policy, schema]
|
|
240
|
-
);
|
|
241
238
|
const addOption = (0, import_react.useCallback)(
|
|
242
239
|
(fieldId) => {
|
|
243
240
|
const field = schema.fields.find((candidate) => candidate.id === fieldId);
|
|
244
|
-
if (field === void 0
|
|
241
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
242
|
+
if (!("options" in field))
|
|
245
243
|
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
246
|
-
|
|
247
|
-
if (policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField) {
|
|
244
|
+
if (policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField)
|
|
248
245
|
return { success: false, error: { type: "max_options_exceeded", max: policy.maxOptionsPerField } };
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
schema.fields.flatMap(
|
|
252
|
-
(candidate) => "options" in candidate ? candidate.options.map((option2) => option2.id) : []
|
|
253
|
-
)
|
|
246
|
+
const ids = new Set(
|
|
247
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
254
248
|
);
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
const option = { id: optionId, label: `Option ${field.options.length + 1}` };
|
|
249
|
+
const id = createId("option", ids);
|
|
250
|
+
if (id.id === void 0) return failedId("option", id);
|
|
251
|
+
const option = (factories.createOption ?? defaultCreateOption)(field, id.id);
|
|
252
|
+
if (option.id !== id.id || ids.has(option.id))
|
|
253
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
263
254
|
onChange({
|
|
264
255
|
...schema,
|
|
265
256
|
fields: schema.fields.map(
|
|
266
|
-
(
|
|
257
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options: [...item.options, option] } : item
|
|
267
258
|
)
|
|
268
259
|
});
|
|
269
260
|
return { success: true };
|
|
270
261
|
},
|
|
271
|
-
[createId, onChange, policy, schema]
|
|
262
|
+
[createId, factories.createOption, onChange, policy?.maxOptionsPerField, schema]
|
|
272
263
|
);
|
|
273
264
|
const removeOption = (0, import_react.useCallback)(
|
|
274
265
|
(fieldId, optionId) => {
|
|
275
|
-
const field = schema.fields.find((
|
|
276
|
-
if (field === void 0
|
|
266
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
267
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
268
|
+
if (!("options" in field) || !field.options.some((option) => option.id === optionId))
|
|
269
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: optionId } };
|
|
270
|
+
if (field.options.length <= 1)
|
|
271
|
+
return { success: false, error: { type: "invalid_operation", message: "A choice field needs one option." } };
|
|
277
272
|
onChange({
|
|
278
273
|
...schema,
|
|
279
274
|
fields: schema.fields.map(
|
|
280
|
-
(
|
|
275
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options: item.options.filter((option) => option.id !== optionId) } : item
|
|
281
276
|
)
|
|
282
277
|
});
|
|
278
|
+
return { success: true };
|
|
283
279
|
},
|
|
284
280
|
[onChange, schema]
|
|
285
281
|
);
|
|
286
282
|
const moveOption = (0, import_react.useCallback)(
|
|
287
283
|
(fieldId, optionId, targetIndex) => {
|
|
288
|
-
const field = schema.fields.find((
|
|
289
|
-
if (field === void 0
|
|
284
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
285
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
286
|
+
if (!("options" in field))
|
|
287
|
+
return { success: false, error: { type: "invalid_operation", message: `Field ${fieldId} has no options.` } };
|
|
290
288
|
const options = move(
|
|
291
289
|
field.options,
|
|
292
290
|
field.options.findIndex((option) => option.id === optionId),
|
|
293
291
|
targetIndex
|
|
294
292
|
);
|
|
295
|
-
if (options === void 0)
|
|
293
|
+
if (options === void 0)
|
|
294
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid option position." } };
|
|
296
295
|
onChange({
|
|
297
296
|
...schema,
|
|
298
297
|
fields: schema.fields.map(
|
|
299
|
-
(
|
|
298
|
+
(item) => item.id === fieldId && "options" in item ? { ...item, options } : item
|
|
300
299
|
)
|
|
301
300
|
});
|
|
301
|
+
return { success: true };
|
|
302
302
|
},
|
|
303
303
|
[onChange, schema]
|
|
304
304
|
);
|
|
305
|
+
const changeFieldType = (0, import_react.useCallback)(
|
|
306
|
+
(fieldId, type) => {
|
|
307
|
+
const field = schema.fields.find((item) => item.id === fieldId);
|
|
308
|
+
if (field === void 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
309
|
+
if (policy?.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(type))
|
|
310
|
+
return { success: false, error: { type: "disallowed_field_type", fieldType: type } };
|
|
311
|
+
let transformed = (0, import_core.transformFieldType)(field, type);
|
|
312
|
+
if (CHOICE_TYPES.includes(type) && !("options" in field) && "options" in transformed) {
|
|
313
|
+
const ids = new Set(
|
|
314
|
+
schema.fields.flatMap((item) => "options" in item ? item.options.map((option2) => option2.id) : [])
|
|
315
|
+
);
|
|
316
|
+
const id = createId("option", ids);
|
|
317
|
+
if (id.id === void 0) return failedId("option", id);
|
|
318
|
+
const option = (factories.createOption ?? defaultCreateOption)(transformed, id.id);
|
|
319
|
+
if (option.id !== id.id || ids.has(option.id))
|
|
320
|
+
return { success: false, error: { type: "invalid_id", kind: "option", id: option.id } };
|
|
321
|
+
transformed = { ...transformed, options: [option] };
|
|
322
|
+
}
|
|
323
|
+
onChange({ ...schema, fields: schema.fields.map((item) => item.id === fieldId ? transformed : item) });
|
|
324
|
+
return { success: true };
|
|
325
|
+
},
|
|
326
|
+
[createId, factories.createOption, onChange, policy?.allowedFieldTypes, schema]
|
|
327
|
+
);
|
|
305
328
|
const addPage = (0, import_react.useCallback)(
|
|
306
329
|
(questionId) => {
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
const ids = new Set(schema.pages?.map((page2) => page2.id) ?? []);
|
|
331
|
+
const id = createId("page", ids);
|
|
332
|
+
if (id.id === void 0) return failedId("page", id);
|
|
333
|
+
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(
|
|
334
|
+
(value) => value !== void 0
|
|
335
|
+
);
|
|
336
|
+
if (questionIds.length === 0)
|
|
337
|
+
return { success: false, error: { type: "invalid_operation", message: "No question can be moved." } };
|
|
338
|
+
const source = schema.pages?.find((page2) => page2.questionIds.includes(questionIds[0] ?? ""));
|
|
339
|
+
if (schema.pages !== void 0 && (source === void 0 || source.questionIds.length <= 1))
|
|
340
|
+
return { success: false, error: { type: "invalid_operation", message: "A page cannot be left empty." } };
|
|
341
|
+
const page = (factories.createPage ?? defaultCreatePage)(id.id, [...questionIds]);
|
|
342
|
+
if (page.id !== id.id || ids.has(page.id))
|
|
343
|
+
return { success: false, error: { type: "invalid_id", kind: "page", id: page.id } };
|
|
344
|
+
const pages = schema.pages === void 0 ? [page] : [
|
|
345
|
+
...schema.pages.map((item) => ({
|
|
346
|
+
...item,
|
|
347
|
+
questionIds: item.questionIds.filter((fieldId) => !questionIds.includes(fieldId))
|
|
322
348
|
})),
|
|
323
|
-
|
|
349
|
+
page
|
|
324
350
|
];
|
|
325
351
|
onChange({ ...schema, pages });
|
|
352
|
+
return { success: true };
|
|
326
353
|
},
|
|
327
|
-
[createId, onChange, schema]
|
|
354
|
+
[createId, factories.createPage, onChange, schema]
|
|
328
355
|
);
|
|
329
356
|
const removePage = (0, import_react.useCallback)(
|
|
330
357
|
(pageId) => {
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
358
|
+
const index = schema.pages?.findIndex((page) => page.id === pageId) ?? -1;
|
|
359
|
+
const removed = schema.pages?.[index];
|
|
360
|
+
if (removed === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
361
|
+
const currentPages = schema.pages;
|
|
362
|
+
if (currentPages === void 0)
|
|
363
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
364
|
+
if ((schema.pages?.length ?? 0) === 1) {
|
|
365
|
+
const { pages: _pages, ...single } = schema;
|
|
366
|
+
onChange(single);
|
|
367
|
+
return { success: true };
|
|
339
368
|
}
|
|
340
369
|
const targetIndex = index === 0 ? 1 : index - 1;
|
|
341
370
|
onChange({
|
|
342
371
|
...schema,
|
|
343
|
-
pages:
|
|
372
|
+
pages: currentPages.map(
|
|
344
373
|
(page, pageIndex) => pageIndex === targetIndex ? { ...page, questionIds: [...page.questionIds, ...removed.questionIds] } : page
|
|
345
374
|
).filter((page) => page.id !== pageId)
|
|
346
375
|
});
|
|
376
|
+
return { success: true };
|
|
347
377
|
},
|
|
348
378
|
[onChange, schema]
|
|
349
379
|
);
|
|
350
|
-
const
|
|
351
|
-
(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
380
|
+
const movePage = (0, import_react.useCallback)(
|
|
381
|
+
(pageId, targetIndex) => {
|
|
382
|
+
const sourceIndex = schema.pages?.findIndex((page) => page.id === pageId) ?? -1;
|
|
383
|
+
if (sourceIndex < 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
384
|
+
const pages = move(schema.pages ?? [], sourceIndex, targetIndex);
|
|
385
|
+
if (pages === void 0)
|
|
386
|
+
return { success: false, error: { type: "invalid_operation", message: "Invalid page position." } };
|
|
387
|
+
const assignment = new Map(pages.flatMap((page, index) => page.questionIds.map((id) => [id, index])));
|
|
388
|
+
onChange({
|
|
389
|
+
...schema,
|
|
390
|
+
pages: pages.map((page, index) => {
|
|
391
|
+
const source = page.displayCondition?.questionId;
|
|
392
|
+
if (source === void 0 || (assignment.get(source) ?? index) < index) return page;
|
|
393
|
+
const { displayCondition: _condition, ...rest } = page;
|
|
394
|
+
return rest;
|
|
395
|
+
})
|
|
396
|
+
});
|
|
397
|
+
return { success: true };
|
|
398
|
+
},
|
|
399
|
+
[onChange, schema]
|
|
400
|
+
);
|
|
401
|
+
const assignFieldToPage = (0, import_react.useCallback)(
|
|
402
|
+
(fieldId, pageId) => {
|
|
403
|
+
if (!schema.fields.some((field) => field.id === fieldId))
|
|
404
|
+
return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
405
|
+
if (pageId === null) {
|
|
406
|
+
if (schema.pages !== void 0) {
|
|
407
|
+
const { pages: _pages, ...single } = schema;
|
|
408
|
+
onChange(single);
|
|
409
|
+
}
|
|
410
|
+
return { success: true };
|
|
411
|
+
}
|
|
412
|
+
if (!schema.pages?.some((page) => page.id === pageId))
|
|
413
|
+
return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
|
|
414
|
+
const pages = schema.pages.map((page) => ({
|
|
415
|
+
...page,
|
|
416
|
+
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)
|
|
417
|
+
})).filter((page) => page.questionIds.length > 0);
|
|
418
|
+
onChange({ ...schema, pages });
|
|
419
|
+
return { success: true };
|
|
420
|
+
},
|
|
421
|
+
[onChange, schema]
|
|
422
|
+
);
|
|
423
|
+
const setDisplayCondition = (0, import_react.useCallback)(
|
|
424
|
+
(fieldId, condition) => {
|
|
425
|
+
const index = schema.fields.findIndex((field) => field.id === fieldId);
|
|
426
|
+
if (index < 0) return { success: false, error: { type: "node_not_found", kind: "field", id: fieldId } };
|
|
427
|
+
if (condition !== void 0) {
|
|
428
|
+
const sourceIndex = schema.fields.findIndex((field) => field.id === condition.questionId);
|
|
429
|
+
if (sourceIndex < 0 || sourceIndex >= index)
|
|
430
|
+
return {
|
|
431
|
+
success: false,
|
|
432
|
+
error: { type: "invalid_operation", message: "Conditions require an earlier field." }
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
onChange({
|
|
436
|
+
...schema,
|
|
437
|
+
fields: schema.fields.map(
|
|
438
|
+
(field) => field.id !== fieldId ? field : condition === void 0 ? withoutDisplayCondition(field) : { ...field, displayCondition: condition }
|
|
439
|
+
)
|
|
440
|
+
});
|
|
441
|
+
return { success: true };
|
|
442
|
+
},
|
|
443
|
+
[onChange, schema]
|
|
444
|
+
);
|
|
445
|
+
const setSourceText = (0, import_react.useCallback)(
|
|
446
|
+
(target, property, text) => {
|
|
447
|
+
const error = textPolicyError(text);
|
|
448
|
+
if (error !== void 0) return error;
|
|
449
|
+
if (target.kind === "form") {
|
|
450
|
+
if (!["title", "description", "completionMessage"].includes(property))
|
|
451
|
+
return {
|
|
452
|
+
success: false,
|
|
453
|
+
error: { type: "invalid_operation", message: `Unsupported form property: ${property}` }
|
|
454
|
+
};
|
|
455
|
+
if (property === "title") onChange({ ...schema, title: text });
|
|
456
|
+
else if (property === "description") {
|
|
457
|
+
const { description: _description, ...withoutDescription } = schema;
|
|
458
|
+
onChange(text.length === 0 ? withoutDescription : { ...schema, description: text });
|
|
459
|
+
} else {
|
|
460
|
+
const { completionMessage: _completionMessage, ...withoutCompletionMessage } = schema;
|
|
461
|
+
onChange(text.length === 0 ? withoutCompletionMessage : { ...schema, completionMessage: text });
|
|
462
|
+
}
|
|
463
|
+
return { success: true };
|
|
464
|
+
}
|
|
465
|
+
if (target.id === void 0)
|
|
466
|
+
return { success: false, error: { type: "invalid_operation", message: "A target ID is required." } };
|
|
467
|
+
if (target.kind === "field") {
|
|
468
|
+
if (!["title", "description"].includes(property))
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
error: { type: "invalid_operation", message: `Unsupported field property: ${property}` }
|
|
472
|
+
};
|
|
473
|
+
return updateField(target.id, (field) => {
|
|
474
|
+
if (property === "title") return { ...field, title: text };
|
|
475
|
+
const { description: _description, ...withoutDescription } = field;
|
|
476
|
+
return text.length === 0 ? withoutDescription : { ...field, description: text };
|
|
360
477
|
});
|
|
361
|
-
return;
|
|
362
478
|
}
|
|
363
|
-
|
|
364
|
-
if (
|
|
479
|
+
if (target.kind === "option") {
|
|
480
|
+
if (property !== "label")
|
|
481
|
+
return {
|
|
482
|
+
success: false,
|
|
483
|
+
error: { type: "invalid_operation", message: `Unsupported option property: ${property}` }
|
|
484
|
+
};
|
|
485
|
+
for (const field of schema.fields)
|
|
486
|
+
if ("options" in field && field.options.some((option) => option.id === target.id))
|
|
487
|
+
return updateOption(field.id, target.id, (option) => ({ ...option, label: text }));
|
|
488
|
+
return { success: false, error: { type: "node_not_found", kind: "option", id: target.id } };
|
|
489
|
+
}
|
|
490
|
+
if (!["title", "description"].includes(property))
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: { type: "invalid_operation", message: `Unsupported page property: ${property}` }
|
|
494
|
+
};
|
|
495
|
+
return updatePage(target.id, (page) => {
|
|
496
|
+
if (property === "title") {
|
|
497
|
+
const { title: _title, ...withoutTitle } = page;
|
|
498
|
+
return text.length === 0 ? withoutTitle : { ...page, title: text };
|
|
499
|
+
}
|
|
500
|
+
const { description: _description, ...withoutDescription } = page;
|
|
501
|
+
return text.length === 0 ? withoutDescription : { ...page, description: text };
|
|
502
|
+
});
|
|
503
|
+
},
|
|
504
|
+
[onChange, schema, textPolicyError, updateField, updateOption, updatePage]
|
|
505
|
+
);
|
|
506
|
+
const setLocaleTranslation = (0, import_react.useCallback)(
|
|
507
|
+
(locale, target, property, text, options = {}) => {
|
|
508
|
+
const normalized = locale.trim();
|
|
509
|
+
if (normalized.length === 0)
|
|
510
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
511
|
+
const error = textPolicyError(text);
|
|
512
|
+
if (error !== void 0) return error;
|
|
513
|
+
const supportedLocales = [.../* @__PURE__ */ new Set([...schema.supportedLocales ?? [], normalized])];
|
|
514
|
+
const remove = text.length === 0;
|
|
515
|
+
if (target.kind === "form") {
|
|
516
|
+
if (!["title", "description", "completionMessage"].includes(property))
|
|
365
517
|
return {
|
|
366
|
-
|
|
367
|
-
|
|
518
|
+
success: false,
|
|
519
|
+
error: { type: "invalid_operation", message: `Unsupported form property: ${property}` }
|
|
368
520
|
};
|
|
521
|
+
const key = property;
|
|
522
|
+
const translations = remove ? removeLocalizedProperty(schema.translations, normalized, key) : { ...schema.translations, [normalized]: { ...schema.translations?.[normalized], [key]: text } };
|
|
523
|
+
onChange(
|
|
524
|
+
setTranslationMetadata(
|
|
525
|
+
{ ...schema, supportedLocales, ...translations === void 0 ? {} : { translations } },
|
|
526
|
+
normalized,
|
|
527
|
+
property,
|
|
528
|
+
options.metadata,
|
|
529
|
+
remove
|
|
530
|
+
)
|
|
531
|
+
);
|
|
532
|
+
return { success: true };
|
|
533
|
+
}
|
|
534
|
+
if (target.id === void 0)
|
|
535
|
+
return { success: false, error: { type: "invalid_operation", message: "A target ID is required." } };
|
|
536
|
+
let found = false;
|
|
537
|
+
const fields = schema.fields.map((field) => {
|
|
538
|
+
if (target.kind === "field" && field.id === target.id && ["title", "description"].includes(property)) {
|
|
539
|
+
found = true;
|
|
540
|
+
const key = property;
|
|
541
|
+
const translations = remove ? removeLocalizedProperty(field.translations, normalized, key) : { ...field.translations, [normalized]: { ...field.translations?.[normalized], [key]: text } };
|
|
542
|
+
return setTranslationMetadata(
|
|
543
|
+
{ ...field, ...translations === void 0 ? {} : { translations } },
|
|
544
|
+
normalized,
|
|
545
|
+
property,
|
|
546
|
+
options.metadata,
|
|
547
|
+
remove
|
|
548
|
+
);
|
|
369
549
|
}
|
|
370
|
-
if (
|
|
550
|
+
if (target.kind !== "option" || property !== "label" || !("options" in field)) return field;
|
|
371
551
|
return {
|
|
372
552
|
...field,
|
|
373
|
-
options: field.options.map(
|
|
374
|
-
(option
|
|
375
|
-
|
|
553
|
+
options: field.options.map((option) => {
|
|
554
|
+
if (option.id !== target.id) return option;
|
|
555
|
+
found = true;
|
|
556
|
+
const translations = remove ? Object.fromEntries(Object.entries(option.translations ?? {}).filter(([key]) => key !== normalized)) : { ...option.translations, [normalized]: text };
|
|
557
|
+
return setTranslationMetadata({ ...option, translations }, normalized, property, options.metadata, remove);
|
|
558
|
+
})
|
|
376
559
|
};
|
|
377
560
|
});
|
|
378
|
-
const pages = schema.pages?.map(
|
|
379
|
-
(page
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
561
|
+
const pages = schema.pages?.map((page) => {
|
|
562
|
+
if (target.kind !== "page" || page.id !== target.id || !["title", "description"].includes(property))
|
|
563
|
+
return page;
|
|
564
|
+
found = true;
|
|
565
|
+
const key = property;
|
|
566
|
+
const translations = remove ? removeLocalizedProperty(page.translations, normalized, key) : { ...page.translations, [normalized]: { ...page.translations?.[normalized], [key]: text } };
|
|
567
|
+
return setTranslationMetadata(
|
|
568
|
+
{ ...page, ...translations === void 0 ? {} : { translations } },
|
|
569
|
+
normalized,
|
|
570
|
+
property,
|
|
571
|
+
options.metadata,
|
|
572
|
+
remove
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
if (!found) return { success: false, error: { type: "node_not_found", kind: target.kind, id: target.id } };
|
|
384
576
|
onChange({ ...schema, supportedLocales, fields, ...pages === void 0 ? {} : { pages } });
|
|
577
|
+
return { success: true };
|
|
385
578
|
},
|
|
386
|
-
[onChange,
|
|
579
|
+
[onChange, schema, textPolicyError]
|
|
580
|
+
);
|
|
581
|
+
const addLocale = (0, import_react.useCallback)(
|
|
582
|
+
(locale) => {
|
|
583
|
+
const normalized = locale.trim();
|
|
584
|
+
if (normalized.length === 0)
|
|
585
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
586
|
+
onChange({
|
|
587
|
+
...schema,
|
|
588
|
+
supportedLocales: [
|
|
589
|
+
.../* @__PURE__ */ new Set([
|
|
590
|
+
...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
|
|
591
|
+
...schema.supportedLocales ?? [],
|
|
592
|
+
normalized
|
|
593
|
+
])
|
|
594
|
+
]
|
|
595
|
+
});
|
|
596
|
+
return { success: true };
|
|
597
|
+
},
|
|
598
|
+
[onChange, schema]
|
|
599
|
+
);
|
|
600
|
+
const setDefaultLocale = (0, import_react.useCallback)(
|
|
601
|
+
(locale) => {
|
|
602
|
+
const normalized = locale.trim();
|
|
603
|
+
if (normalized.length === 0)
|
|
604
|
+
return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
|
|
605
|
+
onChange({
|
|
606
|
+
...schema,
|
|
607
|
+
defaultLocale: normalized,
|
|
608
|
+
supportedLocales: [.../* @__PURE__ */ new Set([normalized, ...schema.supportedLocales ?? []])]
|
|
609
|
+
});
|
|
610
|
+
return { success: true };
|
|
611
|
+
},
|
|
612
|
+
[onChange, schema]
|
|
387
613
|
);
|
|
388
614
|
const validationIssues = (0, import_react.useMemo)(() => {
|
|
389
|
-
const result = (0, import_core.validateFormSchema)(schema);
|
|
390
|
-
|
|
391
|
-
addPolicyIssues(schema, policy, issues);
|
|
392
|
-
return issues;
|
|
615
|
+
const result = (0, import_core.validateFormSchema)(schema, policy === void 0 ? {} : { policy });
|
|
616
|
+
return result.valid ? [] : result.issues;
|
|
393
617
|
}, [policy, schema]);
|
|
394
618
|
return {
|
|
395
619
|
schema,
|
|
@@ -397,12 +621,21 @@ function useFormBuilder({
|
|
|
397
621
|
removeField,
|
|
398
622
|
moveField,
|
|
399
623
|
updateField,
|
|
624
|
+
changeFieldType,
|
|
400
625
|
addOption,
|
|
626
|
+
updateOption,
|
|
401
627
|
removeOption,
|
|
402
628
|
moveOption,
|
|
403
629
|
addPage,
|
|
630
|
+
updatePage,
|
|
404
631
|
removePage,
|
|
632
|
+
movePage,
|
|
633
|
+
assignFieldToPage,
|
|
634
|
+
setDisplayCondition,
|
|
635
|
+
setSourceText,
|
|
405
636
|
setLocaleTranslation,
|
|
637
|
+
addLocale,
|
|
638
|
+
setDefaultLocale,
|
|
406
639
|
validationIssues
|
|
407
640
|
};
|
|
408
641
|
}
|
|
@@ -428,6 +661,7 @@ var BUILDER_DEFAULTS = {
|
|
|
428
661
|
"builder.questionTitle": "\u8CEA\u554F\u6587 / Question Title",
|
|
429
662
|
"builder.questionTitlePlaceholder": "Example: Tell us what we could improve",
|
|
430
663
|
"builder.newQuestionTitle": "New question",
|
|
664
|
+
"builder.completionMessage": "Completion message",
|
|
431
665
|
"builder.type": "Type",
|
|
432
666
|
"builder.required": "Required",
|
|
433
667
|
"builder.minimum": "Minimum",
|
|
@@ -486,33 +720,6 @@ function fieldTypeKey(type) {
|
|
|
486
720
|
function operatorKey(operator) {
|
|
487
721
|
return `builder.operator.${operator}`;
|
|
488
722
|
}
|
|
489
|
-
function createUniqueId(prefix, existingIds) {
|
|
490
|
-
let id;
|
|
491
|
-
do {
|
|
492
|
-
id = `${prefix}_${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
493
|
-
} while (existingIds.has(id));
|
|
494
|
-
return id;
|
|
495
|
-
}
|
|
496
|
-
function baseField(field, type) {
|
|
497
|
-
return {
|
|
498
|
-
id: field.id,
|
|
499
|
-
type,
|
|
500
|
-
title: field.title,
|
|
501
|
-
...field.description === void 0 ? {} : { description: field.description },
|
|
502
|
-
...field.translationKey === void 0 ? {} : { translationKey: field.translationKey },
|
|
503
|
-
required: field.required,
|
|
504
|
-
...field.displayCondition === void 0 ? {} : { displayCondition: field.displayCondition }
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
function normalizeField(field, type, newOptionLabel) {
|
|
508
|
-
const base = baseField(field, type);
|
|
509
|
-
if (type === "text" || type === "textarea") return { ...base, type };
|
|
510
|
-
if (type === "number") return { ...base, type };
|
|
511
|
-
if (type === "rating") return { ...base, type, min: 1, max: 5 };
|
|
512
|
-
if (type === "checkbox") return { ...base, type };
|
|
513
|
-
const options = "options" in field && field.options.length > 0 ? field.options : [{ id: createUniqueId("opt", /* @__PURE__ */ new Set()), label: newOptionLabel }];
|
|
514
|
-
return { ...base, type, options };
|
|
515
|
-
}
|
|
516
723
|
function defaultConditionValue(field) {
|
|
517
724
|
if (field.type === "checkbox") return true;
|
|
518
725
|
if (field.type === "number" || field.type === "rating") return field.min ?? 1;
|
|
@@ -526,30 +733,6 @@ function conditionOperators(field) {
|
|
|
526
733
|
}
|
|
527
734
|
return ["equals", "not_equals", "not_empty"];
|
|
528
735
|
}
|
|
529
|
-
function withoutDisplayCondition(field) {
|
|
530
|
-
const { displayCondition: _displayCondition, ...rest } = field;
|
|
531
|
-
return rest;
|
|
532
|
-
}
|
|
533
|
-
function sanitizeBuilderSchema(schema) {
|
|
534
|
-
const sanitized = (0, import_core2.sanitizeSchema)(schema);
|
|
535
|
-
const indexById = new Map(sanitized.fields.map((field, index) => [field.id, index]));
|
|
536
|
-
const fields = sanitized.fields.map((field, index) => {
|
|
537
|
-
const sourceId = field.displayCondition?.questionId;
|
|
538
|
-
if (sourceId === void 0) return field;
|
|
539
|
-
const sourceIndex = indexById.get(sourceId);
|
|
540
|
-
return sourceIndex !== void 0 && sourceIndex < index ? field : withoutDisplayCondition(field);
|
|
541
|
-
});
|
|
542
|
-
return {
|
|
543
|
-
...sanitized,
|
|
544
|
-
fields,
|
|
545
|
-
...sanitized.pages === void 0 ? {} : {
|
|
546
|
-
pages: sanitized.pages.map((page) => ({
|
|
547
|
-
...page,
|
|
548
|
-
questionIds: fields.filter((field) => page.questionIds.includes(field.id)).map((field) => field.id)
|
|
549
|
-
}))
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
736
|
function conditionWithValue(questionId, operator, value) {
|
|
554
737
|
return operator === "not_empty" ? { questionId, operator } : { questionId, operator, value };
|
|
555
738
|
}
|
|
@@ -597,14 +780,18 @@ function FormBuilder({
|
|
|
597
780
|
locale = "en",
|
|
598
781
|
translator,
|
|
599
782
|
translationAdapter,
|
|
783
|
+
translationOptions,
|
|
784
|
+
onTranslationReport,
|
|
600
785
|
policy,
|
|
601
|
-
idFactory
|
|
786
|
+
idFactory,
|
|
787
|
+
factories
|
|
602
788
|
}) {
|
|
603
789
|
const headless = useFormBuilder({
|
|
604
790
|
schema,
|
|
605
791
|
onChange,
|
|
606
792
|
...policy === void 0 ? {} : { policy },
|
|
607
|
-
...idFactory === void 0 ? {} : { idFactory }
|
|
793
|
+
...idFactory === void 0 ? {} : { idFactory },
|
|
794
|
+
...factories === void 0 ? {} : { factories }
|
|
608
795
|
});
|
|
609
796
|
const [newPageQuestionId, setNewPageQuestionId] = (0, import_react2.useState)("");
|
|
610
797
|
const [newLocale, setNewLocale] = (0, import_react2.useState)("");
|
|
@@ -615,48 +802,17 @@ function FormBuilder({
|
|
|
615
802
|
const translated = translator?.translate(key, locale, params);
|
|
616
803
|
return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
|
|
617
804
|
};
|
|
618
|
-
const emitSchema = (candidate) => onChange(sanitizeBuilderSchema(candidate));
|
|
619
805
|
const updateField = headless.updateField;
|
|
620
|
-
const changeType =
|
|
621
|
-
emitSchema({
|
|
622
|
-
...schema,
|
|
623
|
-
fields: schema.fields.map((field) => {
|
|
624
|
-
if (field.id === fieldId) {
|
|
625
|
-
return normalizeField(field, type, translate("builder.newOptionLabel", { index: 1 }));
|
|
626
|
-
}
|
|
627
|
-
if (field.displayCondition?.questionId === fieldId) {
|
|
628
|
-
const { displayCondition: _condition, ...withoutCondition } = field;
|
|
629
|
-
return withoutCondition;
|
|
630
|
-
}
|
|
631
|
-
return field;
|
|
632
|
-
})
|
|
633
|
-
});
|
|
634
|
-
};
|
|
806
|
+
const changeType = headless.changeFieldType;
|
|
635
807
|
const removeField = headless.removeField;
|
|
636
808
|
const moveField = (index, offset) => {
|
|
637
809
|
const target = index + offset;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const current = fields[index];
|
|
641
|
-
const other = fields[target];
|
|
642
|
-
if (current === void 0 || other === void 0) return;
|
|
643
|
-
fields[index] = other;
|
|
644
|
-
fields[target] = current;
|
|
645
|
-
emitSchema({ ...schema, fields });
|
|
810
|
+
const field = schema.fields[index];
|
|
811
|
+
if (field !== void 0) headless.moveField(field.id, target);
|
|
646
812
|
};
|
|
647
813
|
const addField = () => headless.addField("text");
|
|
648
814
|
const enablePages = () => {
|
|
649
|
-
if (schema.pages
|
|
650
|
-
emitSchema({
|
|
651
|
-
...schema,
|
|
652
|
-
pages: [
|
|
653
|
-
{
|
|
654
|
-
id: createUniqueId("page", /* @__PURE__ */ new Set()),
|
|
655
|
-
title: translate("builder.newPage"),
|
|
656
|
-
questionIds: schema.fields.map((field) => field.id)
|
|
657
|
-
}
|
|
658
|
-
]
|
|
659
|
-
});
|
|
815
|
+
if (schema.pages === void 0) headless.addPage();
|
|
660
816
|
};
|
|
661
817
|
const pageForField = (fieldId) => schema.pages?.find((page) => page.questionIds.includes(fieldId));
|
|
662
818
|
const movablePageQuestions = schema.pages?.flatMap((page) => page.questionIds.length > 1 ? page.questionIds : []) ?? [];
|
|
@@ -667,73 +823,28 @@ function FormBuilder({
|
|
|
667
823
|
}
|
|
668
824
|
const questionId = movablePageQuestions.includes(newPageQuestionId) ? newPageQuestionId : movablePageQuestions[0];
|
|
669
825
|
if (questionId === void 0) return;
|
|
670
|
-
|
|
671
|
-
emitSchema({
|
|
672
|
-
...schema,
|
|
673
|
-
pages: [
|
|
674
|
-
...schema.pages.map((page) => ({
|
|
675
|
-
...page,
|
|
676
|
-
questionIds: page.questionIds.filter((id) => id !== questionId)
|
|
677
|
-
})),
|
|
678
|
-
{ id: pageId, title: translate("builder.newPage"), questionIds: [questionId] }
|
|
679
|
-
]
|
|
680
|
-
});
|
|
826
|
+
headless.addPage(questionId);
|
|
681
827
|
setNewPageQuestionId("");
|
|
682
828
|
};
|
|
683
829
|
const removePage = (pageIndex) => {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if (removed === void 0) return;
|
|
687
|
-
if (schema.pages.length === 1) {
|
|
688
|
-
const { pages: _pages, ...singlePage } = schema;
|
|
689
|
-
emitSchema(singlePage);
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
const targetIndex = pageIndex === 0 ? 1 : pageIndex - 1;
|
|
693
|
-
emitSchema({
|
|
694
|
-
...schema,
|
|
695
|
-
pages: schema.pages.map(
|
|
696
|
-
(page, index) => index === targetIndex ? { ...page, questionIds: [...page.questionIds, ...removed.questionIds] } : page
|
|
697
|
-
).filter((_page, index) => index !== pageIndex)
|
|
698
|
-
});
|
|
830
|
+
const page = schema.pages?.[pageIndex];
|
|
831
|
+
if (page !== void 0) headless.removePage(page.id);
|
|
699
832
|
};
|
|
700
833
|
const movePage = (pageIndex, offset) => {
|
|
701
|
-
if (schema.pages === void 0) return;
|
|
702
834
|
const target = pageIndex + offset;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const current = pages[pageIndex];
|
|
706
|
-
const other = pages[target];
|
|
707
|
-
if (current === void 0 || other === void 0) return;
|
|
708
|
-
pages[pageIndex] = other;
|
|
709
|
-
pages[target] = current;
|
|
710
|
-
emitSchema({ ...schema, pages });
|
|
835
|
+
const page = schema.pages?.[pageIndex];
|
|
836
|
+
if (page !== void 0) headless.movePage(page.id, target);
|
|
711
837
|
};
|
|
712
838
|
const updatePage = (pageId, update) => {
|
|
713
|
-
|
|
714
|
-
emitSchema({ ...schema, pages: schema.pages.map((page) => page.id === pageId ? update(page) : page) });
|
|
839
|
+
headless.updatePage(pageId, update);
|
|
715
840
|
};
|
|
716
841
|
const assignFieldToPage = (fieldId, pageId) => {
|
|
717
|
-
|
|
718
|
-
emitSchema({
|
|
719
|
-
...schema,
|
|
720
|
-
pages: schema.pages.map((page) => ({
|
|
721
|
-
...page,
|
|
722
|
-
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)
|
|
723
|
-
})).filter((page) => page.questionIds.length > 0)
|
|
724
|
-
});
|
|
842
|
+
headless.assignFieldToPage(fieldId, pageId);
|
|
725
843
|
};
|
|
726
844
|
const addLocale = () => {
|
|
727
845
|
const normalized = newLocale.trim();
|
|
728
846
|
if (normalized.length === 0) return;
|
|
729
|
-
|
|
730
|
-
.../* @__PURE__ */ new Set([
|
|
731
|
-
...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
|
|
732
|
-
...schema.supportedLocales ?? [],
|
|
733
|
-
normalized
|
|
734
|
-
])
|
|
735
|
-
];
|
|
736
|
-
emitSchema({ ...schema, supportedLocales });
|
|
847
|
+
headless.addLocale(normalized);
|
|
737
848
|
setEditingLocale(normalized);
|
|
738
849
|
setNewLocale("");
|
|
739
850
|
};
|
|
@@ -742,10 +853,14 @@ function FormBuilder({
|
|
|
742
853
|
setIsTranslating(true);
|
|
743
854
|
setTranslationError(null);
|
|
744
855
|
try {
|
|
745
|
-
const populated = await (0, import_core2.populateSchemaTranslations)(
|
|
746
|
-
|
|
747
|
-
|
|
856
|
+
const populated = await (0, import_core2.populateSchemaTranslations)(
|
|
857
|
+
schema,
|
|
858
|
+
[editingLocale],
|
|
859
|
+
translationAdapter,
|
|
860
|
+
translationOptions ?? { overwrite: "all" }
|
|
861
|
+
);
|
|
748
862
|
onChange(populated.schema);
|
|
863
|
+
onTranslationReport?.(populated.report);
|
|
749
864
|
} catch (cause) {
|
|
750
865
|
setTranslationError(cause instanceof Error ? cause.message : String(cause));
|
|
751
866
|
} finally {
|
|
@@ -754,18 +869,7 @@ function FormBuilder({
|
|
|
754
869
|
};
|
|
755
870
|
const updateFormTranslation = (key, value) => {
|
|
756
871
|
if (editingLocale.length === 0) return;
|
|
757
|
-
|
|
758
|
-
const next = key === "title" ? value.length === 0 ? { description: current?.description } : { ...current, title: value } : value.length === 0 ? { title: current?.title } : { ...current, description: value };
|
|
759
|
-
emitSchema({
|
|
760
|
-
...schema,
|
|
761
|
-
translations: {
|
|
762
|
-
...schema.translations,
|
|
763
|
-
[editingLocale]: {
|
|
764
|
-
...next.title === void 0 ? {} : { title: next.title },
|
|
765
|
-
...next.description === void 0 ? {} : { description: next.description }
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
});
|
|
872
|
+
headless.setLocaleTranslation(editingLocale, { kind: "form" }, key, value);
|
|
769
873
|
};
|
|
770
874
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder", "aria-label": translate("builder.formBuilder"), children: [
|
|
771
875
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__pages", "aria-labelledby": "builder-pages-heading", children: [
|
|
@@ -845,19 +949,12 @@ function FormBuilder({
|
|
|
845
949
|
"input",
|
|
846
950
|
{
|
|
847
951
|
value: page.translations?.[editingLocale]?.title ?? "",
|
|
848
|
-
onChange: (event) =>
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
[editingLocale]: {
|
|
855
|
-
...value.length === 0 ? {} : { title: value },
|
|
856
|
-
...current.translations?.[editingLocale]?.description === void 0 ? {} : { description: current.translations[editingLocale]?.description }
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}));
|
|
860
|
-
}
|
|
952
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
953
|
+
editingLocale,
|
|
954
|
+
{ kind: "page", id: page.id },
|
|
955
|
+
"title",
|
|
956
|
+
event.currentTarget.value
|
|
957
|
+
)
|
|
861
958
|
}
|
|
862
959
|
)
|
|
863
960
|
] }),
|
|
@@ -867,19 +964,12 @@ function FormBuilder({
|
|
|
867
964
|
"input",
|
|
868
965
|
{
|
|
869
966
|
value: page.translations?.[editingLocale]?.description ?? "",
|
|
870
|
-
onChange: (event) =>
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
[editingLocale]: {
|
|
877
|
-
...current.translations?.[editingLocale]?.title === void 0 ? {} : { title: current.translations[editingLocale]?.title },
|
|
878
|
-
...value.length === 0 ? {} : { description: value }
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}));
|
|
882
|
-
}
|
|
967
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
968
|
+
editingLocale,
|
|
969
|
+
{ kind: "page", id: page.id },
|
|
970
|
+
"description",
|
|
971
|
+
event.currentTarget.value
|
|
972
|
+
)
|
|
883
973
|
}
|
|
884
974
|
)
|
|
885
975
|
] })
|
|
@@ -967,6 +1057,16 @@ function FormBuilder({
|
|
|
967
1057
|
] }),
|
|
968
1058
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__localization", "aria-labelledby": "builder-localization-heading", children: [
|
|
969
1059
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "builder-localization-heading", children: translate("builder.localization") }),
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
1061
|
+
translate("builder.completionMessage"),
|
|
1062
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1063
|
+
"input",
|
|
1064
|
+
{
|
|
1065
|
+
value: schema.completionMessage ?? "",
|
|
1066
|
+
onChange: (event) => headless.setSourceText({ kind: "form" }, "completionMessage", event.currentTarget.value)
|
|
1067
|
+
}
|
|
1068
|
+
)
|
|
1069
|
+
] }),
|
|
970
1070
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
|
|
971
1071
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
972
1072
|
translate("builder.defaultLocale"),
|
|
@@ -974,16 +1074,7 @@ function FormBuilder({
|
|
|
974
1074
|
"input",
|
|
975
1075
|
{
|
|
976
1076
|
value: schema.defaultLocale ?? "",
|
|
977
|
-
onChange: (event) =>
|
|
978
|
-
const value = event.currentTarget.value.trim();
|
|
979
|
-
emitSchema(
|
|
980
|
-
value.length === 0 ? schema : {
|
|
981
|
-
...schema,
|
|
982
|
-
defaultLocale: value,
|
|
983
|
-
supportedLocales: [.../* @__PURE__ */ new Set([value, ...schema.supportedLocales ?? []])]
|
|
984
|
-
}
|
|
985
|
-
);
|
|
986
|
-
}
|
|
1077
|
+
onChange: (event) => headless.setDefaultLocale(event.currentTarget.value)
|
|
987
1078
|
}
|
|
988
1079
|
)
|
|
989
1080
|
] }),
|
|
@@ -1031,6 +1122,16 @@ function FormBuilder({
|
|
|
1031
1122
|
onChange: (event) => updateFormTranslation("description", event.currentTarget.value)
|
|
1032
1123
|
}
|
|
1033
1124
|
)
|
|
1125
|
+
] }),
|
|
1126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
1127
|
+
translate("builder.completionMessage"),
|
|
1128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1129
|
+
"input",
|
|
1130
|
+
{
|
|
1131
|
+
value: schema.translations?.[editingLocale]?.completionMessage ?? "",
|
|
1132
|
+
onChange: (event) => updateFormTranslation("completionMessage", event.currentTarget.value)
|
|
1133
|
+
}
|
|
1134
|
+
)
|
|
1034
1135
|
] })
|
|
1035
1136
|
] })
|
|
1036
1137
|
] }),
|
|
@@ -1132,19 +1233,12 @@ function FormBuilder({
|
|
|
1132
1233
|
"input",
|
|
1133
1234
|
{
|
|
1134
1235
|
value: field.translations?.[editingLocale]?.title ?? "",
|
|
1135
|
-
onChange: (event) =>
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
[editingLocale]: {
|
|
1142
|
-
...value.length === 0 ? {} : { title: value },
|
|
1143
|
-
...current.translations?.[editingLocale]?.description === void 0 ? {} : { description: current.translations[editingLocale]?.description }
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}));
|
|
1147
|
-
}
|
|
1236
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1237
|
+
editingLocale,
|
|
1238
|
+
{ kind: "field", id: field.id },
|
|
1239
|
+
"title",
|
|
1240
|
+
event.currentTarget.value
|
|
1241
|
+
)
|
|
1148
1242
|
}
|
|
1149
1243
|
)
|
|
1150
1244
|
] }),
|
|
@@ -1154,19 +1248,12 @@ function FormBuilder({
|
|
|
1154
1248
|
"input",
|
|
1155
1249
|
{
|
|
1156
1250
|
value: field.translations?.[editingLocale]?.description ?? "",
|
|
1157
|
-
onChange: (event) =>
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
[editingLocale]: {
|
|
1164
|
-
...current.translations?.[editingLocale]?.title === void 0 ? {} : { title: current.translations[editingLocale]?.title },
|
|
1165
|
-
...value.length === 0 ? {} : { description: value }
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
}));
|
|
1169
|
-
}
|
|
1251
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1252
|
+
editingLocale,
|
|
1253
|
+
{ kind: "field", id: field.id },
|
|
1254
|
+
"description",
|
|
1255
|
+
event.currentTarget.value
|
|
1256
|
+
)
|
|
1170
1257
|
}
|
|
1171
1258
|
)
|
|
1172
1259
|
] })
|
|
@@ -1180,26 +1267,12 @@ function FormBuilder({
|
|
|
1180
1267
|
"input",
|
|
1181
1268
|
{
|
|
1182
1269
|
value: option.translations?.[editingLocale] ?? "",
|
|
1183
|
-
onChange: (event) =>
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
options: current.options.map(
|
|
1190
|
-
(candidate) => candidate.id === option.id ? {
|
|
1191
|
-
...candidate,
|
|
1192
|
-
translations: Object.fromEntries([
|
|
1193
|
-
...Object.entries(candidate.translations ?? {}).filter(
|
|
1194
|
-
([localeKey]) => localeKey !== editingLocale
|
|
1195
|
-
),
|
|
1196
|
-
...value.length === 0 ? [] : [[editingLocale, value]]
|
|
1197
|
-
])
|
|
1198
|
-
} : candidate
|
|
1199
|
-
)
|
|
1200
|
-
};
|
|
1201
|
-
});
|
|
1202
|
-
}
|
|
1270
|
+
onChange: (event) => headless.setLocaleTranslation(
|
|
1271
|
+
editingLocale,
|
|
1272
|
+
{ kind: "option", id: option.id },
|
|
1273
|
+
"label",
|
|
1274
|
+
event.currentTarget.value
|
|
1275
|
+
)
|
|
1203
1276
|
}
|
|
1204
1277
|
)
|
|
1205
1278
|
] }, option.id)) : null
|
|
@@ -1251,17 +1324,10 @@ function FormBuilder({
|
|
|
1251
1324
|
"aria-label": translate("builder.optionLabel", { index: optionIndex + 1 }),
|
|
1252
1325
|
value: option.label,
|
|
1253
1326
|
placeholder: translate("builder.optionLabelPlaceholder"),
|
|
1254
|
-
onChange: (event) =>
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
return {
|
|
1259
|
-
...current,
|
|
1260
|
-
options: current.options.map(
|
|
1261
|
-
(item, itemIndex) => itemIndex === optionIndex ? { ...item, label } : item
|
|
1262
|
-
)
|
|
1263
|
-
};
|
|
1264
|
-
})
|
|
1327
|
+
onChange: (event) => event.currentTarget.value.trim().length === 0 ? void 0 : headless.updateOption(field.id, option.id, (item) => ({
|
|
1328
|
+
...item,
|
|
1329
|
+
label: event.currentTarget.value
|
|
1330
|
+
}))
|
|
1265
1331
|
}
|
|
1266
1332
|
),
|
|
1267
1333
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1293,17 +1359,14 @@ function FormBuilder({
|
|
|
1293
1359
|
value: condition?.questionId ?? "",
|
|
1294
1360
|
onChange: (event) => {
|
|
1295
1361
|
const selected = schema.fields.find((item) => item.id === event.currentTarget.value);
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
displayCondition: conditionWithValue(selected.id, operator, defaultConditionValue(selected))
|
|
1305
|
-
};
|
|
1306
|
-
});
|
|
1362
|
+
headless.setDisplayCondition(
|
|
1363
|
+
field.id,
|
|
1364
|
+
selected === void 0 ? void 0 : conditionWithValue(
|
|
1365
|
+
selected.id,
|
|
1366
|
+
conditionOperators(selected)[0] ?? "not_empty",
|
|
1367
|
+
defaultConditionValue(selected)
|
|
1368
|
+
)
|
|
1369
|
+
);
|
|
1307
1370
|
},
|
|
1308
1371
|
children: [
|
|
1309
1372
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: translate("builder.alwaysVisible") }),
|
|
@@ -1320,10 +1383,10 @@ function FormBuilder({
|
|
|
1320
1383
|
value: condition.operator,
|
|
1321
1384
|
onChange: (event) => {
|
|
1322
1385
|
const operator = event.currentTarget.value;
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1386
|
+
headless.setDisplayCondition(
|
|
1387
|
+
field.id,
|
|
1388
|
+
conditionWithValue(source.id, operator, defaultConditionValue(source))
|
|
1389
|
+
);
|
|
1327
1390
|
},
|
|
1328
1391
|
children: conditionOperators(source).map((operator) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
|
|
1329
1392
|
}
|
|
@@ -1333,7 +1396,7 @@ function FormBuilder({
|
|
|
1333
1396
|
{
|
|
1334
1397
|
source,
|
|
1335
1398
|
condition,
|
|
1336
|
-
onChange: (next) =>
|
|
1399
|
+
onChange: (next) => headless.setDisplayCondition(field.id, next),
|
|
1337
1400
|
translate
|
|
1338
1401
|
}
|
|
1339
1402
|
)
|
|
@@ -1868,8 +1931,14 @@ function ContextFormRenderer({
|
|
|
1868
1931
|
] }),
|
|
1869
1932
|
draftRestored ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "form-draft-badge", children: form.translate("form.draftRestored") }) : null
|
|
1870
1933
|
] }),
|
|
1871
|
-
activePage
|
|
1872
|
-
|
|
1934
|
+
activePage === void 0 ? null : slots.renderPageHeader?.({
|
|
1935
|
+
page: activePage,
|
|
1936
|
+
pageIndex: activeVisibleIndex,
|
|
1937
|
+
totalPages: visiblePageIndexes.length
|
|
1938
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-page-header", children: [
|
|
1939
|
+
activePage.title === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "fe-page-title", children: activePage.title }),
|
|
1940
|
+
activePage.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "fe-page-description", children: activePage.description })
|
|
1941
|
+
] }),
|
|
1873
1942
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-fields", children: form.schema.fields.filter((field) => form.visibility[field.id] === true && (fieldIds === void 0 || fieldIds.has(field.id))).map((field) => {
|
|
1874
1943
|
const error = form.errors[field.id];
|
|
1875
1944
|
const props = {
|
|
@@ -1937,7 +2006,7 @@ function ContextFormRenderer({
|
|
|
1937
2006
|
form.submitStatus === "success" ? slots.renderCompletion?.({
|
|
1938
2007
|
message: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey))
|
|
1939
2008
|
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "status", children: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey)) }) : null,
|
|
1940
|
-
form.submitStatus === "error" && form.submitError !== null
|
|
2009
|
+
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (errorMessageKey === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: form.translate(errorMessageKey) })) : null
|
|
1941
2010
|
] })
|
|
1942
2011
|
] });
|
|
1943
2012
|
}
|