@evanschleret/formforgeclient 1.2.4 → 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.
Files changed (83) hide show
  1. package/README.md +10 -0
  2. package/dist/module.cjs +1 -0
  3. package/dist/module.d.cts +1 -0
  4. package/dist/module.d.mts +1 -0
  5. package/dist/module.d.ts +1 -0
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +1 -0
  8. package/dist/runtime/api/client.js +4 -2
  9. package/dist/runtime/api/request.d.ts +1 -0
  10. package/dist/runtime/api/schema.js +4 -4
  11. package/dist/runtime/assets/formforge.css +1 -0
  12. package/dist/runtime/composables/index.d.ts +1 -1
  13. package/dist/runtime/composables/useFormForgeBuilder.d.ts +24 -2
  14. package/dist/runtime/composables/useFormForgeBuilder.js +299 -43
  15. package/dist/runtime/composables/useFormForgeForm.js +15 -5
  16. package/dist/runtime/composables/useFormForgeI18n.d.ts +245 -19
  17. package/dist/runtime/composables/useFormForgeI18n.js +245 -19
  18. package/dist/runtime/composables/useFormForgeSubmit.js +31 -9
  19. package/dist/runtime/index.d.ts +1 -0
  20. package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +21 -2
  21. package/dist/runtime/renderers/default/FormForgeBuilder.vue +689 -738
  22. package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +21 -2
  23. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.d.vue.ts +17 -0
  24. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue +32 -0
  25. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue.d.ts +17 -0
  26. package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +3 -4
  27. package/dist/runtime/renderers/default/FormForgeRenderer.vue +344 -294
  28. package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +3 -4
  29. package/dist/runtime/renderers/default/FormForgeRendererField.d.vue.ts +22 -0
  30. package/dist/runtime/renderers/default/FormForgeRendererField.vue +237 -0
  31. package/dist/runtime/renderers/default/FormForgeRendererField.vue.d.ts +22 -0
  32. package/dist/runtime/renderers/default/FormForgeRendererPage.d.vue.ts +18 -0
  33. package/dist/runtime/renderers/default/FormForgeRendererPage.vue +31 -0
  34. package/dist/runtime/renderers/default/FormForgeRendererPage.vue.d.ts +18 -0
  35. package/dist/runtime/renderers/default/FormForgeResponse.vue +4 -3
  36. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.d.vue.ts +11 -0
  37. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue +118 -0
  38. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue.d.ts +11 -0
  39. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.d.vue.ts +46 -0
  40. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue +205 -0
  41. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue.d.ts +46 -0
  42. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.d.vue.ts +11 -0
  43. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue +37 -0
  44. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue.d.ts +11 -0
  45. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.d.vue.ts +11 -0
  46. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue +195 -0
  47. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue.d.ts +11 -0
  48. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.d.vue.ts +14 -0
  49. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue +91 -0
  50. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue.d.ts +14 -0
  51. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.d.vue.ts +13 -0
  52. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue +387 -0
  53. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue.d.ts +13 -0
  54. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.d.vue.ts +44 -0
  55. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue +328 -0
  56. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue.d.ts +44 -0
  57. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.d.vue.ts +11 -0
  58. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue +47 -0
  59. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue.d.ts +11 -0
  60. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.d.vue.ts +14 -0
  61. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue +595 -0
  62. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts +14 -0
  63. package/dist/runtime/renderers/default/builder/builderFieldHelpers.d.ts +3 -0
  64. package/dist/runtime/renderers/default/builder/builderFieldHelpers.js +4 -0
  65. package/dist/runtime/types/index.d.ts +1 -1
  66. package/dist/runtime/types/management.d.ts +12 -0
  67. package/dist/runtime/types/schema.d.ts +72 -4
  68. package/dist/runtime/utils/defaults.d.ts +7 -0
  69. package/dist/runtime/utils/defaults.js +86 -0
  70. package/dist/runtime/utils/page-logic.d.ts +24 -0
  71. package/dist/runtime/utils/page-logic.js +351 -0
  72. package/dist/runtime/utils/rich-text.d.ts +3 -0
  73. package/dist/runtime/utils/rich-text.js +72 -0
  74. package/dist/runtime/utils/schema.d.ts +1 -1
  75. package/dist/runtime/utils/schema.js +70 -16
  76. package/dist/runtime/utils/temporal.d.ts +10 -0
  77. package/dist/runtime/utils/temporal.js +28 -0
  78. package/dist/runtime/utils/validation.d.ts +5 -0
  79. package/dist/runtime/utils/validation.js +36 -0
  80. package/dist/runtime/validation/zod.d.ts +5 -2
  81. package/dist/runtime/validation/zod.js +563 -54
  82. package/dist/types.d.mts +2 -0
  83. 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
- "checkbox",
15
+ "consent",
12
16
  "checkbox_group",
13
- "switch",
14
- "date",
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 fieldLabelFromType(type) {
42
- if (type === "checkbox_group") {
43
- return "Checkbox Group";
41
+ function cloneJsonObject(value) {
42
+ const rawValue = toRaw(value);
43
+ if (typeof structuredClone === "function") {
44
+ return structuredClone(rawValue);
44
45
  }
45
- if (type === "select_menu") {
46
- return "Select Menu";
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
- if (type === "date_range") {
49
- return "Date Range";
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
- if (type === "datetime_range") {
52
- return "Datetime Range";
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 type.charAt(0).toUpperCase() + type.slice(1);
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 createField(type, pageKey) {
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: fieldLabelFromType(type),
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
- options: type === "select" || type === "select_menu" || type === "radio" || type === "checkbox_group" ? [] : void 0
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: "Page",
176
+ title: "",
89
177
  description: null,
90
- meta: {},
91
- fields: [createField("text", pageKey)]
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 = "Page";
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: false } : {
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
- pages.push(createPage());
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,
@@ -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 === "date_range" || field.type === "datetime_range") {
34
+ if (field.type === "address") {
34
35
  payload[field.name] = {
35
- start: null,
36
- end: null
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) {