@evanschleret/formforgeclient 1.2.3 → 2.0.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 +10 -0
- package/dist/module.cjs +1 -0
- package/dist/module.d.cts +1 -0
- package/dist/module.d.mts +1 -0
- package/dist/module.d.ts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/api/client.js +4 -2
- package/dist/runtime/api/request.d.ts +1 -0
- package/dist/runtime/api/schema.js +4 -4
- package/dist/runtime/assets/formforge.css +1 -0
- package/dist/runtime/composables/index.d.ts +1 -1
- package/dist/runtime/composables/useFormForgeBuilder.d.ts +24 -2
- package/dist/runtime/composables/useFormForgeBuilder.js +299 -43
- package/dist/runtime/composables/useFormForgeClient.js +3 -5
- package/dist/runtime/composables/useFormForgeForm.js +15 -5
- package/dist/runtime/composables/useFormForgeI18n.d.ts +245 -19
- package/dist/runtime/composables/useFormForgeI18n.js +245 -19
- package/dist/runtime/composables/useFormForgeSubmit.js +31 -9
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilder.vue +689 -738
- package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.d.vue.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue +32 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue.d.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRenderer.vue +344 -294
- package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRendererField.d.vue.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue +237 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue.d.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.d.vue.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue +31 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue.d.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeResponse.vue +4 -3
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue +118 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.d.vue.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue +205 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue.d.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue +37 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue +195 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue +91 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.d.vue.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue +387 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue.d.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.d.vue.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue +328 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue.d.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue +47 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue +595 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.d.ts +3 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.js +4 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/management.d.ts +12 -0
- package/dist/runtime/types/schema.d.ts +72 -4
- package/dist/runtime/utils/defaults.d.ts +7 -0
- package/dist/runtime/utils/defaults.js +86 -0
- package/dist/runtime/utils/page-logic.d.ts +24 -0
- package/dist/runtime/utils/page-logic.js +351 -0
- package/dist/runtime/utils/rich-text.d.ts +3 -0
- package/dist/runtime/utils/rich-text.js +72 -0
- package/dist/runtime/utils/schema.d.ts +1 -1
- package/dist/runtime/utils/schema.js +70 -16
- package/dist/runtime/utils/temporal.d.ts +10 -0
- package/dist/runtime/utils/temporal.js +28 -0
- package/dist/runtime/utils/validation.d.ts +5 -0
- package/dist/runtime/utils/validation.js +36 -0
- package/dist/runtime/validation/zod.d.ts +5 -2
- package/dist/runtime/validation/zod.js +563 -54
- package/dist/types.d.mts +2 -0
- package/package.json +18 -14
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { computed, ref, watch } from "#imports";
|
|
1
|
+
import { computed, ref, toRaw, watch } from "#imports";
|
|
2
2
|
import { useFormForgeClient } from "./useFormForgeClient.js";
|
|
3
|
+
import { isLegacyTemporalFieldType, isTemporalMode, temporalModeFromFieldType } from "../utils/temporal.js";
|
|
4
|
+
import { createPageLogic, ensurePageLogic } from "../utils/page-logic.js";
|
|
5
|
+
import {
|
|
6
|
+
createDefaultAddressFields,
|
|
7
|
+
resolveDefaultAnswerPlaceholder,
|
|
8
|
+
resolveDefaultConsentLabel,
|
|
9
|
+
resolveDefaultFieldLabel
|
|
10
|
+
} from "../utils/defaults.js";
|
|
3
11
|
export const FORM_FORGE_BUILDER_FIELD_TYPES = [
|
|
4
12
|
"text",
|
|
5
|
-
"textarea",
|
|
6
|
-
"email",
|
|
7
13
|
"number",
|
|
8
|
-
"select",
|
|
9
|
-
"select_menu",
|
|
10
14
|
"radio",
|
|
11
|
-
"
|
|
15
|
+
"consent",
|
|
12
16
|
"checkbox_group",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"time",
|
|
16
|
-
"datetime",
|
|
17
|
-
"date_range",
|
|
18
|
-
"datetime_range",
|
|
17
|
+
"address",
|
|
18
|
+
"temporal",
|
|
19
19
|
"file"
|
|
20
20
|
];
|
|
21
21
|
export const FORM_FORGE_BUILDER_CONDITION_TARGET_TYPES = ["page", "field"];
|
|
@@ -38,39 +38,119 @@ export const FORM_FORGE_BUILDER_CONDITION_OPERATORS = [
|
|
|
38
38
|
function shortKey(prefix) {
|
|
39
39
|
return `${prefix}_${Math.random().toString(36).slice(2, 8)}`;
|
|
40
40
|
}
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
function cloneJsonObject(value) {
|
|
42
|
+
const rawValue = toRaw(value);
|
|
43
|
+
if (typeof structuredClone === "function") {
|
|
44
|
+
return structuredClone(rawValue);
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return JSON.parse(JSON.stringify(rawValue));
|
|
47
|
+
}
|
|
48
|
+
function cloneJsonValue(value) {
|
|
49
|
+
const rawValue = toRaw(value);
|
|
50
|
+
if (typeof structuredClone === "function") {
|
|
51
|
+
return structuredClone(rawValue);
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
return JSON.parse(JSON.stringify(rawValue));
|
|
54
|
+
}
|
|
55
|
+
function isChoiceFieldType(type) {
|
|
56
|
+
return type === "radio" || type === "checkbox_group";
|
|
57
|
+
}
|
|
58
|
+
function choiceOptionCount(type) {
|
|
59
|
+
return type === "checkbox_group" ? 3 : 2;
|
|
60
|
+
}
|
|
61
|
+
function minimumChoiceOptionCount(type) {
|
|
62
|
+
return type === "checkbox_group" ? 2 : 1;
|
|
63
|
+
}
|
|
64
|
+
function createChoiceOptions(type) {
|
|
65
|
+
const options = [];
|
|
66
|
+
const count = choiceOptionCount(type);
|
|
67
|
+
for (let index = 0; index < count; index += 1) {
|
|
68
|
+
options.push({
|
|
69
|
+
label: "",
|
|
70
|
+
value: `option_${index + 1}`
|
|
71
|
+
});
|
|
50
72
|
}
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
return options;
|
|
74
|
+
}
|
|
75
|
+
function defaultChoiceDisplay(type) {
|
|
76
|
+
return type === "radio" || type === "checkbox_group" ? "list" : "menu";
|
|
77
|
+
}
|
|
78
|
+
function temporalFieldDefaultMode(type) {
|
|
79
|
+
if (isLegacyTemporalFieldType(type)) {
|
|
80
|
+
return temporalModeFromFieldType(type);
|
|
53
81
|
}
|
|
54
|
-
return
|
|
82
|
+
return "date";
|
|
83
|
+
}
|
|
84
|
+
function createTemporalDefault() {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function normalizeTemporalField(field) {
|
|
88
|
+
const rawType = field.type;
|
|
89
|
+
if (isLegacyTemporalFieldType(field.type)) {
|
|
90
|
+
field.type = "temporal";
|
|
91
|
+
}
|
|
92
|
+
if (field.type !== "temporal") {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const resolvedMode = isTemporalMode(field.temporal_mode) ? field.temporal_mode : temporalModeFromFieldType(rawType);
|
|
96
|
+
field.temporal_mode = resolvedMode;
|
|
97
|
+
field.hour_cycle = resolvedMode === "time" ? field.hour_cycle === 12 ? 12 : 24 : void 0;
|
|
98
|
+
field.default = null;
|
|
55
99
|
}
|
|
56
|
-
function
|
|
100
|
+
function createAddressValue() {
|
|
101
|
+
return {
|
|
102
|
+
line1: null,
|
|
103
|
+
line2: null,
|
|
104
|
+
city: null,
|
|
105
|
+
state: null,
|
|
106
|
+
zip: null,
|
|
107
|
+
country: null
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function createField(type, pageKey, locale) {
|
|
57
111
|
const fieldKey = shortKey("fk");
|
|
58
112
|
const name = fieldKey.replace("fk_", "");
|
|
113
|
+
const temporalMode = temporalFieldDefaultMode(type);
|
|
114
|
+
const resolvedType = isLegacyTemporalFieldType(type) ? "temporal" : type;
|
|
59
115
|
const baseField = {
|
|
60
116
|
field_key: fieldKey,
|
|
61
|
-
type,
|
|
117
|
+
type: resolvedType,
|
|
62
118
|
name,
|
|
63
119
|
page_key: pageKey,
|
|
64
|
-
label:
|
|
120
|
+
label: resolveDefaultFieldLabel(resolvedType, locale, temporalMode),
|
|
65
121
|
required: false,
|
|
66
122
|
nullable: false,
|
|
67
123
|
disabled: false,
|
|
68
124
|
readonly: false,
|
|
69
|
-
default: null,
|
|
125
|
+
default: resolvedType === "temporal" ? createTemporalDefault() : null,
|
|
70
126
|
rules: [],
|
|
71
127
|
meta: {},
|
|
72
|
-
|
|
128
|
+
placeholder: type === "text" || type === "number" ? resolveDefaultAnswerPlaceholder(locale) : void 0,
|
|
129
|
+
options: type === "radio" || type === "checkbox_group" ? [] : void 0
|
|
73
130
|
};
|
|
131
|
+
if (isChoiceFieldType(type)) {
|
|
132
|
+
return {
|
|
133
|
+
...baseField,
|
|
134
|
+
options: createChoiceOptions(type),
|
|
135
|
+
display: defaultChoiceDisplay(type)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (type === "consent") {
|
|
139
|
+
return {
|
|
140
|
+
...baseField,
|
|
141
|
+
type: "consent",
|
|
142
|
+
consent_label: resolveDefaultConsentLabel(locale)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (resolvedType === "temporal") {
|
|
146
|
+
return {
|
|
147
|
+
...baseField,
|
|
148
|
+
type: "temporal",
|
|
149
|
+
temporal_mode: temporalMode,
|
|
150
|
+
hour_cycle: temporalMode === "time" ? 24 : void 0,
|
|
151
|
+
default: createTemporalDefault()
|
|
152
|
+
};
|
|
153
|
+
}
|
|
74
154
|
if (type === "file") {
|
|
75
155
|
return {
|
|
76
156
|
...baseField,
|
|
@@ -79,16 +159,26 @@ function createField(type, pageKey) {
|
|
|
79
159
|
accept: []
|
|
80
160
|
};
|
|
81
161
|
}
|
|
162
|
+
if (type === "address") {
|
|
163
|
+
return {
|
|
164
|
+
...baseField,
|
|
165
|
+
type: "address",
|
|
166
|
+
default: createAddressValue(),
|
|
167
|
+
address_fields: createDefaultAddressFields(locale)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
82
170
|
return baseField;
|
|
83
171
|
}
|
|
84
|
-
function createPage() {
|
|
172
|
+
function createPage(type = "text", locale) {
|
|
85
173
|
const pageKey = shortKey("pg");
|
|
86
174
|
return {
|
|
87
175
|
page_key: pageKey,
|
|
88
|
-
title: "
|
|
176
|
+
title: "",
|
|
89
177
|
description: null,
|
|
90
|
-
meta: {
|
|
91
|
-
|
|
178
|
+
meta: {
|
|
179
|
+
logic: createPageLogic()
|
|
180
|
+
},
|
|
181
|
+
fields: [createField(type, pageKey, locale)]
|
|
92
182
|
};
|
|
93
183
|
}
|
|
94
184
|
function createCondition() {
|
|
@@ -111,7 +201,11 @@ function cloneField(field) {
|
|
|
111
201
|
return {
|
|
112
202
|
...field,
|
|
113
203
|
field_key: shortKey("fk"),
|
|
114
|
-
name: shortKey("field")
|
|
204
|
+
name: shortKey("field"),
|
|
205
|
+
default: cloneJsonValue(field.default),
|
|
206
|
+
meta: cloneJsonObject(field.meta),
|
|
207
|
+
options: Array.isArray(field.options) ? cloneJsonValue(field.options) : field.options,
|
|
208
|
+
address_fields: Array.isArray(field.address_fields) ? cloneJsonValue(field.address_fields) : field.address_fields
|
|
115
209
|
};
|
|
116
210
|
}
|
|
117
211
|
function isUuidLike(value) {
|
|
@@ -144,13 +238,28 @@ function resolveMutationIdentifier(draft) {
|
|
|
144
238
|
}
|
|
145
239
|
return null;
|
|
146
240
|
}
|
|
147
|
-
function sanitizeDraftShape(value) {
|
|
241
|
+
function sanitizeDraftShape(value, locale) {
|
|
148
242
|
if (typeof value.uuid !== "string" || value.uuid === "") {
|
|
149
243
|
value.uuid = null;
|
|
150
244
|
}
|
|
151
245
|
if (typeof value.key !== "string" || value.key === "") {
|
|
152
246
|
value.key = null;
|
|
153
247
|
}
|
|
248
|
+
if (!Number.isInteger(value.schema_version) || value.schema_version <= 0) {
|
|
249
|
+
value.schema_version = 2;
|
|
250
|
+
}
|
|
251
|
+
if (typeof value.api !== "object" || value.api === null || Array.isArray(value.api)) {
|
|
252
|
+
value.api = {};
|
|
253
|
+
}
|
|
254
|
+
if (typeof value.public_url !== "string") {
|
|
255
|
+
value.public_url = null;
|
|
256
|
+
}
|
|
257
|
+
if (typeof value.submission_code_required !== "boolean") {
|
|
258
|
+
value.submission_code_required = false;
|
|
259
|
+
}
|
|
260
|
+
if (typeof value.submission_code !== "string") {
|
|
261
|
+
value.submission_code = null;
|
|
262
|
+
}
|
|
154
263
|
if (value.uuid === null && typeof value.key === "string" && isUuidLike(value.key)) {
|
|
155
264
|
value.uuid = value.key;
|
|
156
265
|
}
|
|
@@ -164,8 +273,12 @@ function sanitizeDraftShape(value) {
|
|
|
164
273
|
page.page_key = shortKey("pg");
|
|
165
274
|
}
|
|
166
275
|
if (typeof page.title !== "string") {
|
|
167
|
-
page.title = "
|
|
276
|
+
page.title = "";
|
|
277
|
+
}
|
|
278
|
+
if (typeof page.meta !== "object" || page.meta === null || Array.isArray(page.meta)) {
|
|
279
|
+
page.meta = {};
|
|
168
280
|
}
|
|
281
|
+
ensurePageLogic(page);
|
|
169
282
|
const fields = (Array.isArray(page.fields) ? page.fields : []).filter((field) => field !== void 0 && field !== null);
|
|
170
283
|
if (fields.length === 0) {
|
|
171
284
|
fields.push(createField("text", page.page_key));
|
|
@@ -188,33 +301,74 @@ function sanitizeDraftShape(value) {
|
|
|
188
301
|
if (typeof field.meta !== "object" || field.meta === null || Array.isArray(field.meta)) {
|
|
189
302
|
field.meta = {};
|
|
190
303
|
}
|
|
304
|
+
normalizeTemporalField(field);
|
|
305
|
+
if (isChoiceFieldType(field.type)) {
|
|
306
|
+
if (!Array.isArray(field.options) || field.options.length === 0) {
|
|
307
|
+
field.options = createChoiceOptions(field.type);
|
|
308
|
+
}
|
|
309
|
+
const minimumOptionCount = minimumChoiceOptionCount(field.type);
|
|
310
|
+
if (field.options.length < minimumOptionCount) {
|
|
311
|
+
const nextOptions = [...field.options];
|
|
312
|
+
for (let index = nextOptions.length; index < minimumOptionCount; index += 1) {
|
|
313
|
+
nextOptions.push({
|
|
314
|
+
label: "",
|
|
315
|
+
value: `option_${index + 1}`
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
field.options = nextOptions;
|
|
319
|
+
}
|
|
320
|
+
if (field.display !== "list" && field.display !== "menu") {
|
|
321
|
+
field.display = defaultChoiceDisplay(field.type);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (field.type === "consent" && typeof field.consent_label !== "string") {
|
|
325
|
+
field.consent_label = resolveDefaultConsentLabel(locale);
|
|
326
|
+
}
|
|
327
|
+
if (field.type === "address") {
|
|
328
|
+
if (!Array.isArray(field.address_fields) || field.address_fields.length === 0) {
|
|
329
|
+
field.address_fields = createDefaultAddressFields(locale);
|
|
330
|
+
}
|
|
331
|
+
if (typeof field.default !== "object" || field.default === null || Array.isArray(field.default)) {
|
|
332
|
+
field.default = createAddressValue();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
191
335
|
}
|
|
192
336
|
}
|
|
193
337
|
}
|
|
194
338
|
export function useFormForgeBuilder(options = {}) {
|
|
195
339
|
const client = options.client ?? useFormForgeClient(options.clientConfig);
|
|
340
|
+
const locale = options.locale ?? options.clientConfig?.locale;
|
|
196
341
|
const autosaveEnabled = ref(options.autosave ?? true);
|
|
197
342
|
const autosaveDelay = ref(options.autosaveDelay ?? 5e3);
|
|
343
|
+
const autoPublishOnSave = ref(options.autoPublishOnSave ?? false);
|
|
198
344
|
const loading = ref(false);
|
|
199
345
|
const saving = ref(false);
|
|
200
346
|
const publishing = ref(false);
|
|
201
347
|
const error = ref(null);
|
|
202
348
|
const lastSavedAt = ref(null);
|
|
203
|
-
const defaultPage = createPage();
|
|
349
|
+
const defaultPage = createPage("text", locale);
|
|
204
350
|
const keyFromOptions = options.formKey ?? options.initial?.key ?? null;
|
|
205
351
|
const uuidFromOptions = options.formUuid ?? options.initial?.uuid ?? null;
|
|
206
352
|
const initialDraft = {
|
|
207
353
|
uuid: uuidFromOptions ?? (typeof keyFromOptions === "string" && isUuidLike(keyFromOptions) ? keyFromOptions : null),
|
|
208
354
|
key: typeof keyFromOptions === "string" && !isUuidLike(keyFromOptions) ? keyFromOptions : options.initial?.key ?? null,
|
|
355
|
+
schema_version: options.initial?.schema_version ?? 2,
|
|
209
356
|
title: options.initial?.title ?? "",
|
|
357
|
+
publish_at: options.initial?.publish_at ?? null,
|
|
358
|
+
pause_at: options.initial?.pause_at ?? null,
|
|
359
|
+
response_limit: options.initial?.response_limit ?? null,
|
|
360
|
+
submission_code_required: options.initial?.submission_code_required ?? false,
|
|
361
|
+
submission_code: options.initial?.submission_code ?? null,
|
|
362
|
+
public_url: options.initial?.public_url ?? null,
|
|
210
363
|
category: options.initial?.category ?? null,
|
|
211
364
|
pages: options.initial?.pages ?? [defaultPage],
|
|
212
365
|
conditions: options.initial?.conditions ?? [],
|
|
213
366
|
drafts: options.initial?.drafts ?? {
|
|
214
367
|
enabled: true
|
|
215
|
-
}
|
|
368
|
+
},
|
|
369
|
+
api: options.initial?.api ?? {}
|
|
216
370
|
};
|
|
217
|
-
sanitizeDraftShape(initialDraft);
|
|
371
|
+
sanitizeDraftShape(initialDraft, locale);
|
|
218
372
|
const draft = ref(initialDraft);
|
|
219
373
|
const publishable = computed(() => {
|
|
220
374
|
const pages = draft.value.pages;
|
|
@@ -245,21 +399,30 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
245
399
|
}
|
|
246
400
|
const input = {
|
|
247
401
|
title: draft.value.title,
|
|
402
|
+
schema_version: draft.value.schema_version,
|
|
403
|
+
publish_at: draft.value.publish_at ?? null,
|
|
404
|
+
pause_at: draft.value.pause_at ?? null,
|
|
405
|
+
response_limit: draft.value.response_limit ?? null,
|
|
406
|
+
submission_code_required: draft.value.submission_code_required ?? false,
|
|
248
407
|
category: draft.value.category,
|
|
249
408
|
pages,
|
|
250
409
|
fields,
|
|
251
410
|
conditions: draft.value.conditions,
|
|
252
|
-
drafts: draft.value.drafts
|
|
411
|
+
drafts: draft.value.drafts,
|
|
412
|
+
api: draft.value.api
|
|
253
413
|
};
|
|
414
|
+
if (draft.value.submission_code_required === true && typeof draft.value.submission_code === "string" && draft.value.submission_code.trim() !== "") {
|
|
415
|
+
input.submission_code = draft.value.submission_code;
|
|
416
|
+
}
|
|
254
417
|
if (autoPublish) {
|
|
255
418
|
input.auto_publish = true;
|
|
256
419
|
}
|
|
257
420
|
return input;
|
|
258
421
|
}
|
|
259
422
|
async function save(saveOptions = {}) {
|
|
260
|
-
const resolvedSaveOptions = typeof saveOptions === "string" ? { idempotencyKey: saveOptions, autoPublish:
|
|
423
|
+
const resolvedSaveOptions = typeof saveOptions === "string" ? { idempotencyKey: saveOptions, autoPublish: autoPublishOnSave.value } : {
|
|
261
424
|
idempotencyKey: saveOptions.idempotencyKey,
|
|
262
|
-
autoPublish: saveOptions.autoPublish === true
|
|
425
|
+
autoPublish: saveOptions.autoPublish === true || autoPublishOnSave.value === true
|
|
263
426
|
};
|
|
264
427
|
if (draft.value.title.trim() === "") {
|
|
265
428
|
throw new Error("Title is required to save");
|
|
@@ -287,6 +450,9 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
287
450
|
if (nextKey !== null) {
|
|
288
451
|
draft.value.key = nextKey;
|
|
289
452
|
}
|
|
453
|
+
if (typeof created.public_url === "string") {
|
|
454
|
+
draft.value.public_url = created.public_url;
|
|
455
|
+
}
|
|
290
456
|
} else {
|
|
291
457
|
const patchInput = input;
|
|
292
458
|
const patched = await client.patchForm(mutationIdentifier, patchInput, {
|
|
@@ -302,6 +468,9 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
302
468
|
if (nextKey !== null) {
|
|
303
469
|
draft.value.key = nextKey;
|
|
304
470
|
}
|
|
471
|
+
if (typeof patched.public_url === "string") {
|
|
472
|
+
draft.value.public_url = patched.public_url;
|
|
473
|
+
}
|
|
305
474
|
}
|
|
306
475
|
lastSavedAt.value = (/* @__PURE__ */ new Date()).toISOString();
|
|
307
476
|
} catch (caughtError) {
|
|
@@ -354,9 +523,11 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
354
523
|
publishing.value = false;
|
|
355
524
|
}
|
|
356
525
|
}
|
|
357
|
-
function addPage() {
|
|
526
|
+
function addPage(type = "text") {
|
|
358
527
|
const pages = draft.value.pages;
|
|
359
|
-
|
|
528
|
+
const page = createPage(type, locale);
|
|
529
|
+
pages.push(page);
|
|
530
|
+
return page;
|
|
360
531
|
}
|
|
361
532
|
function removePage(pageKey) {
|
|
362
533
|
const pages = draft.value.pages;
|
|
@@ -369,6 +540,39 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
369
540
|
}
|
|
370
541
|
pages.splice(pageIndex, 1);
|
|
371
542
|
}
|
|
543
|
+
function movePage(pageKey, direction) {
|
|
544
|
+
const pages = draft.value.pages;
|
|
545
|
+
const pageIndex = pages.findIndex((page2) => page2.page_key === pageKey);
|
|
546
|
+
if (pageIndex < 0) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const nextIndex = pageIndex + direction;
|
|
550
|
+
if (nextIndex < 0 || nextIndex >= pages.length) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const [page] = pages.splice(pageIndex, 1);
|
|
554
|
+
pages.splice(nextIndex, 0, page);
|
|
555
|
+
}
|
|
556
|
+
function duplicatePage(pageKey) {
|
|
557
|
+
const pages = draft.value.pages;
|
|
558
|
+
const pageIndex = pages.findIndex((page2) => page2.page_key === pageKey);
|
|
559
|
+
if (pageIndex < 0) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const page = pages[pageIndex];
|
|
563
|
+
const nextPageKey = shortKey("pg");
|
|
564
|
+
const clonedPage = {
|
|
565
|
+
...page,
|
|
566
|
+
page_key: nextPageKey,
|
|
567
|
+
meta: cloneJsonObject(page.meta),
|
|
568
|
+
fields: page.fields.map((field) => {
|
|
569
|
+
const clonedField = cloneField(field);
|
|
570
|
+
clonedField.page_key = nextPageKey;
|
|
571
|
+
return clonedField;
|
|
572
|
+
})
|
|
573
|
+
};
|
|
574
|
+
pages.splice(pageIndex + 1, 0, clonedPage);
|
|
575
|
+
}
|
|
372
576
|
function mergePageWithPrevious(pageKey) {
|
|
373
577
|
const pages = draft.value.pages;
|
|
374
578
|
if (pages.length <= 1) {
|
|
@@ -392,7 +596,21 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
392
596
|
if (page === void 0) {
|
|
393
597
|
return;
|
|
394
598
|
}
|
|
395
|
-
page.fields.push(createField(type, pageKey));
|
|
599
|
+
page.fields.push(createField(type, pageKey, locale));
|
|
600
|
+
}
|
|
601
|
+
function insertFieldAfter(pageKey, fieldKey, type) {
|
|
602
|
+
const pages = draft.value.pages;
|
|
603
|
+
const page = pages.find((item) => item.page_key === pageKey);
|
|
604
|
+
if (page === void 0) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const fieldIndex = page.fields.findIndex((item) => item.field_key === fieldKey);
|
|
608
|
+
const nextField = createField(type, pageKey, locale);
|
|
609
|
+
if (fieldIndex < 0) {
|
|
610
|
+
page.fields.push(nextField);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
page.fields.splice(fieldIndex + 1, 0, nextField);
|
|
396
614
|
}
|
|
397
615
|
function duplicateField(pageKey, fieldKey) {
|
|
398
616
|
const pages = draft.value.pages;
|
|
@@ -408,6 +626,38 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
408
626
|
clonedField.page_key = page.page_key;
|
|
409
627
|
page.fields.push(clonedField);
|
|
410
628
|
}
|
|
629
|
+
function moveField(pageKey, fieldKey, direction) {
|
|
630
|
+
const pages = draft.value.pages;
|
|
631
|
+
const page = pages.find((item) => item.page_key === pageKey);
|
|
632
|
+
if (page === void 0) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const fieldIndex = page.fields.findIndex((item) => item.field_key === fieldKey);
|
|
636
|
+
if (fieldIndex < 0) {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const nextIndex = fieldIndex + direction;
|
|
640
|
+
if (nextIndex < 0 || nextIndex >= page.fields.length) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const [field] = page.fields.splice(fieldIndex, 1);
|
|
644
|
+
page.fields.splice(nextIndex, 0, field);
|
|
645
|
+
}
|
|
646
|
+
function moveFieldToPage(pageKey, fieldKey, targetPageKey) {
|
|
647
|
+
const pages = draft.value.pages;
|
|
648
|
+
const sourcePage = pages.find((item) => item.page_key === pageKey);
|
|
649
|
+
const targetPage = pages.find((item) => item.page_key === targetPageKey);
|
|
650
|
+
if (sourcePage === void 0 || targetPage === void 0) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const fieldIndex = sourcePage.fields.findIndex((item) => item.field_key === fieldKey);
|
|
654
|
+
if (fieldIndex < 0) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const [field] = sourcePage.fields.splice(fieldIndex, 1);
|
|
658
|
+
field.page_key = targetPage.page_key;
|
|
659
|
+
targetPage.fields.push(field);
|
|
660
|
+
}
|
|
411
661
|
function removeField(pageKey, fieldKey) {
|
|
412
662
|
const pages = draft.value.pages;
|
|
413
663
|
const page = pages.find((item) => item.page_key === pageKey);
|
|
@@ -500,13 +750,19 @@ export function useFormForgeBuilder(options = {}) {
|
|
|
500
750
|
draft,
|
|
501
751
|
autosaveEnabled,
|
|
502
752
|
autosaveDelay,
|
|
753
|
+
autoPublishOnSave,
|
|
503
754
|
lastSavedAt,
|
|
504
755
|
publishable,
|
|
505
756
|
addPage,
|
|
506
757
|
removePage,
|
|
758
|
+
movePage,
|
|
759
|
+
duplicatePage,
|
|
507
760
|
mergePageWithPrevious,
|
|
508
761
|
addField,
|
|
762
|
+
insertFieldAfter,
|
|
509
763
|
duplicateField,
|
|
764
|
+
moveField,
|
|
765
|
+
moveFieldToPage,
|
|
510
766
|
removeField,
|
|
511
767
|
addCondition,
|
|
512
768
|
removeCondition,
|
|
@@ -121,11 +121,10 @@ export function useFormForgeClient(config = {}) {
|
|
|
121
121
|
const nuxtApp = useNuxtApp();
|
|
122
122
|
const route = useRoute();
|
|
123
123
|
const injectedClient = nuxtApp.$formforge;
|
|
124
|
+
const baseConfig = injectedClient?.config ?? useRuntimeConfig().public.formforge;
|
|
124
125
|
if (injectedClient !== void 0 && !hasConfigOverrides(config)) {
|
|
125
126
|
return injectedClient;
|
|
126
127
|
}
|
|
127
|
-
const runtimeConfig = useRuntimeConfig();
|
|
128
|
-
const runtimePublicConfig = runtimeConfig.public.formforge;
|
|
129
128
|
const routeParamsResolver = () => {
|
|
130
129
|
const appRouteQuery = nuxtApp._route?.value?.query;
|
|
131
130
|
const appRouteParams = nuxtApp._route?.value?.params;
|
|
@@ -144,14 +143,13 @@ export function useFormForgeClient(config = {}) {
|
|
|
144
143
|
}
|
|
145
144
|
);
|
|
146
145
|
const resolvedPath = composableRoutePath || appRoutePath;
|
|
147
|
-
const resolvedBaseURL = config.baseURL ??
|
|
148
|
-
const resolvedScopedRoutes = config.scopedRoutes ??
|
|
146
|
+
const resolvedBaseURL = config.baseURL ?? baseConfig?.baseURL;
|
|
147
|
+
const resolvedScopedRoutes = config.scopedRoutes ?? baseConfig?.scopedRoutes;
|
|
149
148
|
const inferredFromBaseURL = inferParamsFromPath(resolvedBaseURL, resolvedPath);
|
|
150
149
|
const withBaseURLValues = withInferredMissingValues(mergedRouteValues, inferredFromBaseURL);
|
|
151
150
|
const inferredFromScopes = inferScopeSourcesFromPath(resolvedScopedRoutes, resolvedPath);
|
|
152
151
|
return withInferredMissingValues(withBaseURLValues, inferredFromScopes);
|
|
153
152
|
};
|
|
154
|
-
const baseConfig = runtimePublicConfig;
|
|
155
153
|
const mergedConfig = {
|
|
156
154
|
...baseConfig,
|
|
157
155
|
...config
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ref } from "#imports";
|
|
2
2
|
import { createFormForgeZodSchema } from "../validation/zod.js";
|
|
3
3
|
import { useFormForgeClient } from "./useFormForgeClient.js";
|
|
4
|
+
import { useFormForgeI18n } from "./useFormForgeI18n.js";
|
|
4
5
|
function clonePayload(payload) {
|
|
5
6
|
if (typeof structuredClone === "function") {
|
|
6
7
|
try {
|
|
@@ -22,7 +23,7 @@ function createInitialPayload(schema) {
|
|
|
22
23
|
payload[field.name] = field.default;
|
|
23
24
|
continue;
|
|
24
25
|
}
|
|
25
|
-
if (field.type === "checkbox" || field.type === "switch") {
|
|
26
|
+
if (field.type === "checkbox" || field.type === "consent" || field.type === "switch") {
|
|
26
27
|
payload[field.name] = false;
|
|
27
28
|
continue;
|
|
28
29
|
}
|
|
@@ -30,10 +31,14 @@ function createInitialPayload(schema) {
|
|
|
30
31
|
payload[field.name] = [];
|
|
31
32
|
continue;
|
|
32
33
|
}
|
|
33
|
-
if (field.type === "
|
|
34
|
+
if (field.type === "address") {
|
|
34
35
|
payload[field.name] = {
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
line1: null,
|
|
37
|
+
line2: null,
|
|
38
|
+
city: null,
|
|
39
|
+
state: null,
|
|
40
|
+
zip: null,
|
|
41
|
+
country: null
|
|
37
42
|
};
|
|
38
43
|
continue;
|
|
39
44
|
}
|
|
@@ -43,6 +48,9 @@ function createInitialPayload(schema) {
|
|
|
43
48
|
}
|
|
44
49
|
export function useFormForgeForm(options) {
|
|
45
50
|
const client = options.client ?? useFormForgeClient(options.clientConfig);
|
|
51
|
+
const { locale } = useFormForgeI18n({
|
|
52
|
+
locale: () => options.clientConfig?.locale
|
|
53
|
+
});
|
|
46
54
|
const schema = ref(null);
|
|
47
55
|
const state = ref({});
|
|
48
56
|
const initialState = ref({});
|
|
@@ -67,7 +75,9 @@ export function useFormForgeForm(options) {
|
|
|
67
75
|
schema.value = nextSchema;
|
|
68
76
|
initialState.value = nextState;
|
|
69
77
|
state.value = clonePayload(nextState);
|
|
70
|
-
zodSchema.value = createFormForgeZodSchema(nextSchema
|
|
78
|
+
zodSchema.value = createFormForgeZodSchema(nextSchema, {
|
|
79
|
+
locale: locale.value
|
|
80
|
+
});
|
|
71
81
|
initialized.value = true;
|
|
72
82
|
return nextSchema;
|
|
73
83
|
} catch (error) {
|