@evanschleret/formforgeclient 1.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/module.cjs +112 -0
  4. package/dist/module.d.cts +20 -0
  5. package/dist/module.d.mts +20 -0
  6. package/dist/module.d.ts +20 -0
  7. package/dist/module.json +12 -0
  8. package/dist/module.mjs +109 -0
  9. package/dist/runtime/api/categories.d.ts +9 -0
  10. package/dist/runtime/api/categories.js +83 -0
  11. package/dist/runtime/api/client.d.ts +45 -0
  12. package/dist/runtime/api/client.js +148 -0
  13. package/dist/runtime/api/drafts.d.ts +6 -0
  14. package/dist/runtime/api/drafts.js +77 -0
  15. package/dist/runtime/api/http.d.ts +3 -0
  16. package/dist/runtime/api/http.js +138 -0
  17. package/dist/runtime/api/index.d.ts +9 -0
  18. package/dist/runtime/api/index.js +11 -0
  19. package/dist/runtime/api/management.d.ts +19 -0
  20. package/dist/runtime/api/management.js +180 -0
  21. package/dist/runtime/api/request.d.ts +8 -0
  22. package/dist/runtime/api/request.js +52 -0
  23. package/dist/runtime/api/responses.d.ts +6 -0
  24. package/dist/runtime/api/responses.js +61 -0
  25. package/dist/runtime/api/schema.d.ts +7 -0
  26. package/dist/runtime/api/schema.js +56 -0
  27. package/dist/runtime/api/submission.d.ts +11 -0
  28. package/dist/runtime/api/submission.js +47 -0
  29. package/dist/runtime/api/upload.d.ts +8 -0
  30. package/dist/runtime/api/upload.js +37 -0
  31. package/dist/runtime/composables/index.d.ts +31 -0
  32. package/dist/runtime/composables/index.js +16 -0
  33. package/dist/runtime/composables/useFormForgeApi.d.ts +3 -0
  34. package/dist/runtime/composables/useFormForgeApi.js +4 -0
  35. package/dist/runtime/composables/useFormForgeBuilder.d.ts +57 -0
  36. package/dist/runtime/composables/useFormForgeBuilder.js +515 -0
  37. package/dist/runtime/composables/useFormForgeCategory.d.ts +61 -0
  38. package/dist/runtime/composables/useFormForgeCategory.js +248 -0
  39. package/dist/runtime/composables/useFormForgeClient.d.ts +3 -0
  40. package/dist/runtime/composables/useFormForgeClient.js +200 -0
  41. package/dist/runtime/composables/useFormForgeDrafts.d.ts +20 -0
  42. package/dist/runtime/composables/useFormForgeDrafts.js +78 -0
  43. package/dist/runtime/composables/useFormForgeForm.d.ts +26 -0
  44. package/dist/runtime/composables/useFormForgeForm.js +114 -0
  45. package/dist/runtime/composables/useFormForgeGetForm.d.ts +22 -0
  46. package/dist/runtime/composables/useFormForgeGetForm.js +36 -0
  47. package/dist/runtime/composables/useFormForgeI18n.d.ts +250 -0
  48. package/dist/runtime/composables/useFormForgeI18n.js +324 -0
  49. package/dist/runtime/composables/useFormForgeManagement.d.ts +40 -0
  50. package/dist/runtime/composables/useFormForgeManagement.js +153 -0
  51. package/dist/runtime/composables/useFormForgeResolver.d.ts +28 -0
  52. package/dist/runtime/composables/useFormForgeResolver.js +88 -0
  53. package/dist/runtime/composables/useFormForgeResponses.d.ts +45 -0
  54. package/dist/runtime/composables/useFormForgeResponses.js +206 -0
  55. package/dist/runtime/composables/useFormForgeSchema.d.ts +24 -0
  56. package/dist/runtime/composables/useFormForgeSchema.js +69 -0
  57. package/dist/runtime/composables/useFormForgeSubmission.d.ts +12 -0
  58. package/dist/runtime/composables/useFormForgeSubmission.js +4 -0
  59. package/dist/runtime/composables/useFormForgeSubmit.d.ts +29 -0
  60. package/dist/runtime/composables/useFormForgeSubmit.js +291 -0
  61. package/dist/runtime/composables/useFormForgeUploads.d.ts +21 -0
  62. package/dist/runtime/composables/useFormForgeUploads.js +37 -0
  63. package/dist/runtime/composables/useFormForgeWizard.d.ts +20 -0
  64. package/dist/runtime/composables/useFormForgeWizard.js +83 -0
  65. package/dist/runtime/index.d.ts +11 -0
  66. package/dist/runtime/index.js +14 -0
  67. package/dist/runtime/plugin.d.ts +3 -0
  68. package/dist/runtime/plugin.js +175 -0
  69. package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +40 -0
  70. package/dist/runtime/renderers/default/FormForgeBuilder.vue +1159 -0
  71. package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +40 -0
  72. package/dist/runtime/renderers/default/FormForgeCategoryCreateModal.d.vue.ts +16 -0
  73. package/dist/runtime/renderers/default/FormForgeCategoryCreateModal.vue +129 -0
  74. package/dist/runtime/renderers/default/FormForgeCategoryCreateModal.vue.d.ts +16 -0
  75. package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +72 -0
  76. package/dist/runtime/renderers/default/FormForgeRenderer.vue +1188 -0
  77. package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +72 -0
  78. package/dist/runtime/renderers/default/FormForgeResponse.d.vue.ts +18 -0
  79. package/dist/runtime/renderers/default/FormForgeResponse.vue +744 -0
  80. package/dist/runtime/renderers/default/FormForgeResponse.vue.d.ts +18 -0
  81. package/dist/runtime/renderers/default/index.d.ts +5 -0
  82. package/dist/runtime/renderers/default/index.js +4 -0
  83. package/dist/runtime/renderers/index.d.ts +2 -0
  84. package/dist/runtime/renderers/index.js +1 -0
  85. package/dist/runtime/types/api.d.ts +129 -0
  86. package/dist/runtime/types/api.js +0 -0
  87. package/dist/runtime/types/category.d.ts +42 -0
  88. package/dist/runtime/types/category.js +0 -0
  89. package/dist/runtime/types/errors.d.ts +16 -0
  90. package/dist/runtime/types/errors.js +0 -0
  91. package/dist/runtime/types/index.d.ts +8 -0
  92. package/dist/runtime/types/index.js +0 -0
  93. package/dist/runtime/types/json.d.ts +6 -0
  94. package/dist/runtime/types/json.js +0 -0
  95. package/dist/runtime/types/management.d.ts +46 -0
  96. package/dist/runtime/types/management.js +0 -0
  97. package/dist/runtime/types/nuxt.d.ts +13 -0
  98. package/dist/runtime/types/nuxt.js +1 -0
  99. package/dist/runtime/types/schema.d.ts +93 -0
  100. package/dist/runtime/types/schema.js +0 -0
  101. package/dist/runtime/utils/category.d.ts +5 -0
  102. package/dist/runtime/utils/category.js +101 -0
  103. package/dist/runtime/utils/form-data.d.ts +8 -0
  104. package/dist/runtime/utils/form-data.js +64 -0
  105. package/dist/runtime/utils/object.d.ts +8 -0
  106. package/dist/runtime/utils/object.js +43 -0
  107. package/dist/runtime/utils/schema.d.ts +3 -0
  108. package/dist/runtime/utils/schema.js +309 -0
  109. package/dist/runtime/utils/submission.d.ts +4 -0
  110. package/dist/runtime/utils/submission.js +45 -0
  111. package/dist/runtime/validation/errors.d.ts +5 -0
  112. package/dist/runtime/validation/errors.js +130 -0
  113. package/dist/runtime/validation/zod.d.ts +6 -0
  114. package/dist/runtime/validation/zod.js +203 -0
  115. package/dist/runtime.cjs +16 -0
  116. package/dist/runtime.d.cts +1 -0
  117. package/dist/runtime.d.mts +1 -0
  118. package/dist/runtime.d.ts +1 -0
  119. package/dist/runtime.mjs +1 -0
  120. package/dist/types.d.mts +3 -0
  121. package/package.json +60 -0
@@ -0,0 +1,1188 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "#imports";
3
+ import { getLocalTimeZone, parseAbsoluteToLocal, parseDate, parseDateTime, parseTime } from "@internationalized/date";
4
+ import UCheckbox from "@nuxt/ui/components/Checkbox.vue";
5
+ import UCheckboxGroup from "@nuxt/ui/components/CheckboxGroup.vue";
6
+ import UFileUpload from "@nuxt/ui/components/FileUpload.vue";
7
+ import UInput from "@nuxt/ui/components/Input.vue";
8
+ import UInputDate from "@nuxt/ui/components/InputDate.vue";
9
+ import UInputNumber from "@nuxt/ui/components/InputNumber.vue";
10
+ import UInputTime from "@nuxt/ui/components/InputTime.vue";
11
+ import UProgress from "@nuxt/ui/components/Progress.vue";
12
+ import URadioGroup from "@nuxt/ui/components/RadioGroup.vue";
13
+ import USelect from "@nuxt/ui/components/Select.vue";
14
+ import USelectMenu from "@nuxt/ui/components/SelectMenu.vue";
15
+ import UStepper from "@nuxt/ui/components/Stepper.vue";
16
+ import USwitch from "@nuxt/ui/components/Switch.vue";
17
+ import UTextarea from "@nuxt/ui/components/Textarea.vue";
18
+ import { isFormForgeJsonObject } from "../../utils/object";
19
+ import { useFormForgeForm } from "../../composables/useFormForgeForm";
20
+ import { useFormForgeI18n } from "../../composables/useFormForgeI18n";
21
+ import { useFormForgeSubmit } from "../../composables/useFormForgeSubmit";
22
+ const props = defineProps({
23
+ schema: { type: Object, required: false, default: void 0 },
24
+ modelValue: { type: Object, required: false, default: void 0 },
25
+ disabled: { type: Boolean, required: false, default: false },
26
+ zodSchema: { type: Object, required: false, default: void 0 },
27
+ validate: { type: Function, required: false, default: void 0 },
28
+ datetimeMode: { type: String, required: false, default: "offset" },
29
+ formKey: { type: String, required: false, default: void 0 },
30
+ formVersion: { type: String, required: false, default: void 0 },
31
+ endpoint: { type: String, required: false, default: void 0 },
32
+ clientConfig: { type: Object, required: false, default: void 0 },
33
+ submitLabel: { type: String, required: false, default: void 0 },
34
+ showSubmit: { type: Boolean, required: false, default: true },
35
+ simulation: { type: Boolean, required: false, default: false },
36
+ uploadMode: { type: String, required: false, default: void 0 },
37
+ clearAfterSubmit: { type: Boolean, required: false, default: false },
38
+ showProgress: { type: Boolean, required: false, default: false },
39
+ progressVariant: { type: String, required: false, default: "stepper" },
40
+ showAlertOnError: { type: Boolean, required: false, default: false }
41
+ });
42
+ const { t } = useFormForgeI18n({
43
+ locale: () => props.clientConfig?.locale
44
+ });
45
+ const emit = defineEmits(["update:modelValue", "submit", "submitted", "error"]);
46
+ function isRefLike(value) {
47
+ if (typeof value !== "object" || value === null) {
48
+ return false;
49
+ }
50
+ if (!("__v_isRef" in value) || !("value" in value)) {
51
+ return false;
52
+ }
53
+ return value.__v_isRef === true;
54
+ }
55
+ function unwrapSchemaProp(value) {
56
+ if (value === void 0) {
57
+ return null;
58
+ }
59
+ if (isRefLike(value)) {
60
+ return value.value;
61
+ }
62
+ return value;
63
+ }
64
+ function unwrapModelValueProp(value) {
65
+ if (value === void 0) {
66
+ return {};
67
+ }
68
+ if (isRefLike(value)) {
69
+ return value.value;
70
+ }
71
+ return value;
72
+ }
73
+ function unwrapZodSchemaProp(value) {
74
+ if (value === void 0) {
75
+ return void 0;
76
+ }
77
+ if (isRefLike(value)) {
78
+ return value.value;
79
+ }
80
+ return value;
81
+ }
82
+ const usesExternalState = computed(() => {
83
+ return props.schema !== void 0 && props.modelValue !== void 0;
84
+ });
85
+ const internalFormKey = computed(() => {
86
+ if (props.formKey === void 0 || props.formKey.trim() === "") {
87
+ return "";
88
+ }
89
+ return props.formKey.trim();
90
+ });
91
+ const internalForm = useFormForgeForm({
92
+ key: internalFormKey.value === "" ? "__missing_form_key__" : internalFormKey.value,
93
+ version: props.formVersion,
94
+ endpoint: props.endpoint,
95
+ immediate: false,
96
+ clientConfig: props.clientConfig
97
+ });
98
+ const internalSubmit = useFormForgeSubmit({
99
+ key: internalFormKey.value === "" ? "__missing_form_key__" : internalFormKey.value,
100
+ version: props.formVersion,
101
+ endpoint: props.endpoint,
102
+ schema: () => internalForm.schema.value,
103
+ state: () => internalForm.state.value,
104
+ clientConfig: props.clientConfig
105
+ });
106
+ const submittedResponse = ref(null);
107
+ const rendererErrors = ref([]);
108
+ watch(
109
+ () => [usesExternalState.value, internalFormKey.value, props.formVersion],
110
+ async ([externalState, formKey]) => {
111
+ submittedResponse.value = null;
112
+ if (externalState || formKey === "") {
113
+ return;
114
+ }
115
+ await internalForm.fetchSchema().catch(() => {
116
+ });
117
+ },
118
+ {
119
+ immediate: true
120
+ }
121
+ );
122
+ function getResolvedSchema() {
123
+ if (usesExternalState.value) {
124
+ return unwrapSchemaProp(props.schema);
125
+ }
126
+ return internalForm.schema.value;
127
+ }
128
+ function getResolvedZodSchema() {
129
+ if (usesExternalState.value) {
130
+ return unwrapZodSchemaProp(props.zodSchema);
131
+ }
132
+ return internalForm.zodSchema.value;
133
+ }
134
+ const formState = computed({
135
+ get: () => {
136
+ const value = usesExternalState.value ? unwrapModelValueProp(props.modelValue) : internalForm.state.value;
137
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
138
+ return {};
139
+ }
140
+ return value;
141
+ },
142
+ set: (value) => {
143
+ if (usesExternalState.value) {
144
+ emit("update:modelValue", value);
145
+ return;
146
+ }
147
+ internalForm.replaceState(value);
148
+ }
149
+ });
150
+ const displayPages = computed(() => {
151
+ const schema = getResolvedSchema();
152
+ if (schema === null) {
153
+ return [];
154
+ }
155
+ const pages = Array.isArray(schema.pages) ? schema.pages : [];
156
+ if (pages.length > 0) {
157
+ return pages.map((page) => ({
158
+ ...page,
159
+ fields: Array.isArray(page.fields) ? page.fields : []
160
+ }));
161
+ }
162
+ const fallbackFields = Array.isArray(schema.fields) ? schema.fields : [];
163
+ return [
164
+ {
165
+ page_key: "page_1",
166
+ title: "",
167
+ description: null,
168
+ meta: {},
169
+ fields: fallbackFields
170
+ }
171
+ ];
172
+ });
173
+ const fieldsByKey = computed(() => {
174
+ const map = {};
175
+ for (const page of displayPages.value) {
176
+ for (const field of page.fields) {
177
+ map[field.field_key] = field;
178
+ }
179
+ }
180
+ return map;
181
+ });
182
+ function isPlainObject(value) {
183
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
184
+ return false;
185
+ }
186
+ if (typeof File !== "undefined" && value instanceof File) {
187
+ return false;
188
+ }
189
+ return true;
190
+ }
191
+ function isEmptyValue(value) {
192
+ if (value === void 0 || value === null) {
193
+ return true;
194
+ }
195
+ if (typeof value === "string") {
196
+ return value.trim() === "";
197
+ }
198
+ if (Array.isArray(value)) {
199
+ return value.length === 0;
200
+ }
201
+ if (isPlainObject(value)) {
202
+ if ("start" in value || "end" in value) {
203
+ const rangeValue = value;
204
+ return isEmptyValue(rangeValue.start) && isEmptyValue(rangeValue.end);
205
+ }
206
+ return Object.keys(value).length === 0;
207
+ }
208
+ return false;
209
+ }
210
+ function areValuesEqual(left, right) {
211
+ if (Object.is(left, right)) {
212
+ return true;
213
+ }
214
+ if (Array.isArray(left) && Array.isArray(right)) {
215
+ if (left.length !== right.length) {
216
+ return false;
217
+ }
218
+ return left.every((leftItem, index) => areValuesEqual(leftItem, right[index]));
219
+ }
220
+ if (isPlainObject(left) && isPlainObject(right)) {
221
+ const leftKeys = Object.keys(left);
222
+ const rightKeys = Object.keys(right);
223
+ if (leftKeys.length !== rightKeys.length) {
224
+ return false;
225
+ }
226
+ for (const key of leftKeys) {
227
+ if (!areValuesEqual(left[key], right[key])) {
228
+ return false;
229
+ }
230
+ }
231
+ return true;
232
+ }
233
+ return false;
234
+ }
235
+ function compareValues(left, right) {
236
+ if (typeof left === "number" && typeof right === "number") {
237
+ if (left < right) {
238
+ return -1;
239
+ }
240
+ if (left > right) {
241
+ return 1;
242
+ }
243
+ return 0;
244
+ }
245
+ if (typeof left === "string" && typeof right === "string") {
246
+ const leftAsNumber = Number(left);
247
+ const rightAsNumber = Number(right);
248
+ if (Number.isFinite(leftAsNumber) && Number.isFinite(rightAsNumber)) {
249
+ if (leftAsNumber < rightAsNumber) {
250
+ return -1;
251
+ }
252
+ if (leftAsNumber > rightAsNumber) {
253
+ return 1;
254
+ }
255
+ return 0;
256
+ }
257
+ const leftTime = Date.parse(left);
258
+ const rightTime = Date.parse(right);
259
+ if (!Number.isNaN(leftTime) && !Number.isNaN(rightTime)) {
260
+ if (leftTime < rightTime) {
261
+ return -1;
262
+ }
263
+ if (leftTime > rightTime) {
264
+ return 1;
265
+ }
266
+ return 0;
267
+ }
268
+ if (left < right) {
269
+ return -1;
270
+ }
271
+ if (left > right) {
272
+ return 1;
273
+ }
274
+ return 0;
275
+ }
276
+ return null;
277
+ }
278
+ function getFieldStateValueByFieldKey(fieldKey) {
279
+ const field = fieldsByKey.value[fieldKey];
280
+ if (field === void 0) {
281
+ return void 0;
282
+ }
283
+ return formState.value[field.name];
284
+ }
285
+ function evaluateConditionClause(clause) {
286
+ const actualValue = getFieldStateValueByFieldKey(clause.field_key);
287
+ const expectedValue = clause.value;
288
+ if (clause.operator === "eq") {
289
+ return areValuesEqual(actualValue, expectedValue);
290
+ }
291
+ if (clause.operator === "neq") {
292
+ return !areValuesEqual(actualValue, expectedValue);
293
+ }
294
+ if (clause.operator === "in") {
295
+ if (Array.isArray(expectedValue)) {
296
+ if (Array.isArray(actualValue)) {
297
+ return actualValue.some((item) => expectedValue.some((entry) => areValuesEqual(item, entry)));
298
+ }
299
+ return expectedValue.some((entry) => areValuesEqual(actualValue, entry));
300
+ }
301
+ if (Array.isArray(actualValue)) {
302
+ return actualValue.some((entry) => areValuesEqual(entry, expectedValue));
303
+ }
304
+ return areValuesEqual(actualValue, expectedValue);
305
+ }
306
+ if (clause.operator === "not_in") {
307
+ return !evaluateConditionClause({
308
+ ...clause,
309
+ operator: "in"
310
+ });
311
+ }
312
+ if (clause.operator === "gt" || clause.operator === "gte" || clause.operator === "lt" || clause.operator === "lte") {
313
+ const comparison = compareValues(actualValue, expectedValue);
314
+ if (comparison === null) {
315
+ return false;
316
+ }
317
+ if (clause.operator === "gt") {
318
+ return comparison > 0;
319
+ }
320
+ if (clause.operator === "gte") {
321
+ return comparison >= 0;
322
+ }
323
+ if (clause.operator === "lt") {
324
+ return comparison < 0;
325
+ }
326
+ return comparison <= 0;
327
+ }
328
+ if (clause.operator === "contains") {
329
+ if (typeof actualValue === "string") {
330
+ return actualValue.includes(String(expectedValue ?? ""));
331
+ }
332
+ if (Array.isArray(actualValue)) {
333
+ return actualValue.some((entry) => areValuesEqual(entry, expectedValue));
334
+ }
335
+ if (isPlainObject(actualValue) && typeof expectedValue === "string") {
336
+ return expectedValue in actualValue;
337
+ }
338
+ return false;
339
+ }
340
+ if (clause.operator === "not_contains") {
341
+ return !evaluateConditionClause({
342
+ ...clause,
343
+ operator: "contains"
344
+ });
345
+ }
346
+ if (clause.operator === "is_empty") {
347
+ return isEmptyValue(actualValue);
348
+ }
349
+ if (clause.operator === "not_empty") {
350
+ return !isEmptyValue(actualValue);
351
+ }
352
+ return false;
353
+ }
354
+ function evaluateCondition(condition) {
355
+ if (!Array.isArray(condition.when) || condition.when.length === 0) {
356
+ return false;
357
+ }
358
+ if (condition.match === "any") {
359
+ return condition.when.some((clause) => evaluateConditionClause(clause));
360
+ }
361
+ return condition.when.every((clause) => evaluateConditionClause(clause));
362
+ }
363
+ const runtimeConditions = computed(() => {
364
+ const schema = getResolvedSchema();
365
+ const pagesState = {};
366
+ const fieldsState = {};
367
+ for (const page of displayPages.value) {
368
+ pagesState[page.page_key] = {
369
+ visible: true,
370
+ disabled: false
371
+ };
372
+ for (const field of page.fields) {
373
+ fieldsState[field.field_key] = {
374
+ visible: true,
375
+ disabled: field.disabled === true,
376
+ required: field.required === true
377
+ };
378
+ }
379
+ }
380
+ if (schema === null || !Array.isArray(schema.conditions)) {
381
+ return {
382
+ pages: pagesState,
383
+ fields: fieldsState
384
+ };
385
+ }
386
+ const hasShowByPageKey = /* @__PURE__ */ new Set();
387
+ const hasShowByFieldKey = /* @__PURE__ */ new Set();
388
+ for (const condition of schema.conditions) {
389
+ if (condition.action !== "show") {
390
+ continue;
391
+ }
392
+ if (condition.target_type === "page") {
393
+ hasShowByPageKey.add(condition.target_key);
394
+ continue;
395
+ }
396
+ if (condition.target_type === "field") {
397
+ hasShowByFieldKey.add(condition.target_key);
398
+ }
399
+ }
400
+ for (const pageKey of hasShowByPageKey) {
401
+ const pageState = pagesState[pageKey];
402
+ if (pageState !== void 0) {
403
+ pageState.visible = false;
404
+ }
405
+ }
406
+ for (const fieldKey of hasShowByFieldKey) {
407
+ const fieldState = fieldsState[fieldKey];
408
+ if (fieldState !== void 0) {
409
+ fieldState.visible = false;
410
+ }
411
+ }
412
+ for (const condition of schema.conditions) {
413
+ if (!evaluateCondition(condition)) {
414
+ continue;
415
+ }
416
+ if (condition.target_type === "page") {
417
+ const pageState = pagesState[condition.target_key];
418
+ if (pageState === void 0) {
419
+ continue;
420
+ }
421
+ if (condition.action === "show") {
422
+ pageState.visible = true;
423
+ }
424
+ if (condition.action === "hide" || condition.action === "skip") {
425
+ pageState.visible = false;
426
+ }
427
+ if (condition.action === "disable") {
428
+ pageState.disabled = true;
429
+ }
430
+ continue;
431
+ }
432
+ if (condition.target_type !== "field") {
433
+ continue;
434
+ }
435
+ const fieldState = fieldsState[condition.target_key];
436
+ if (fieldState === void 0) {
437
+ continue;
438
+ }
439
+ if (condition.action === "show") {
440
+ fieldState.visible = true;
441
+ }
442
+ if (condition.action === "hide" || condition.action === "skip") {
443
+ fieldState.visible = false;
444
+ }
445
+ if (condition.action === "disable") {
446
+ fieldState.disabled = true;
447
+ }
448
+ if (condition.action === "require") {
449
+ fieldState.required = true;
450
+ }
451
+ }
452
+ return {
453
+ pages: pagesState,
454
+ fields: fieldsState
455
+ };
456
+ });
457
+ const visiblePages = computed(() => {
458
+ return displayPages.value.filter((page) => isPageVisible(page));
459
+ });
460
+ const pageKeyByFieldName = computed(() => {
461
+ const map = {};
462
+ for (const page of displayPages.value) {
463
+ for (const field of page.fields) {
464
+ map[field.name] = page.page_key;
465
+ }
466
+ }
467
+ return map;
468
+ });
469
+ const activePageKey = ref(null);
470
+ watch(
471
+ () => visiblePages.value.map((page) => page.page_key),
472
+ (pageKeys) => {
473
+ if (pageKeys.length === 0) {
474
+ activePageKey.value = null;
475
+ return;
476
+ }
477
+ if (activePageKey.value === null || !pageKeys.includes(activePageKey.value)) {
478
+ activePageKey.value = pageKeys[0];
479
+ }
480
+ },
481
+ {
482
+ immediate: true
483
+ }
484
+ );
485
+ const activePageIndex = computed(() => {
486
+ if (visiblePages.value.length === 0) {
487
+ return 0;
488
+ }
489
+ const currentPageKey = activePageKey.value;
490
+ if (currentPageKey === null) {
491
+ return 0;
492
+ }
493
+ const index = visiblePages.value.findIndex((page) => page.page_key === currentPageKey);
494
+ if (index < 0) {
495
+ return 0;
496
+ }
497
+ return index;
498
+ });
499
+ const shouldShowProgress = computed(() => {
500
+ return props.showProgress && visiblePages.value.length > 1;
501
+ });
502
+ const stepperItems = computed(() => {
503
+ return visiblePages.value.map((page, index) => ({
504
+ title: page.title !== "" ? page.title : t("renderer.pageTitle", { index: index + 1 }),
505
+ description: typeof page.description === "string" && page.description.trim() !== "" ? page.description : void 0,
506
+ value: index + 1
507
+ }));
508
+ });
509
+ const pagedMode = computed(() => {
510
+ return shouldShowProgress.value;
511
+ });
512
+ const currentVisiblePage = computed(() => {
513
+ if (visiblePages.value.length === 0) {
514
+ return null;
515
+ }
516
+ const page = visiblePages.value[activePageIndex.value];
517
+ return page ?? null;
518
+ });
519
+ const renderedPages = computed(() => {
520
+ if (!pagedMode.value) {
521
+ return visiblePages.value;
522
+ }
523
+ if (currentVisiblePage.value === null) {
524
+ return [];
525
+ }
526
+ return [currentVisiblePage.value];
527
+ });
528
+ const canGoPrev = computed(() => {
529
+ return activePageIndex.value > 0;
530
+ });
531
+ const canGoNext = computed(() => {
532
+ return activePageIndex.value < visiblePages.value.length - 1;
533
+ });
534
+ const isLastVisiblePage = computed(() => {
535
+ return visiblePages.value.length > 0 && activePageIndex.value === visiblePages.value.length - 1;
536
+ });
537
+ const shouldShowErrorAlert = computed(() => {
538
+ return props.showAlertOnError && rendererErrors.value.length > 0;
539
+ });
540
+ const resolvedSubmitLabel = computed(() => {
541
+ if (typeof props.submitLabel === "string" && props.submitLabel.trim() !== "") {
542
+ return props.submitLabel;
543
+ }
544
+ return t("renderer.submit");
545
+ });
546
+ function isPageVisible(page) {
547
+ const runtimePage = runtimeConditions.value.pages[page.page_key];
548
+ if (runtimePage === void 0) {
549
+ return true;
550
+ }
551
+ return runtimePage.visible;
552
+ }
553
+ function isPageDisabled(page) {
554
+ const runtimePage = runtimeConditions.value.pages[page.page_key];
555
+ if (runtimePage === void 0) {
556
+ return false;
557
+ }
558
+ return runtimePage.disabled;
559
+ }
560
+ function isFieldVisible(field) {
561
+ const runtimeField = runtimeConditions.value.fields[field.field_key];
562
+ if (runtimeField === void 0) {
563
+ return true;
564
+ }
565
+ return runtimeField.visible;
566
+ }
567
+ function isFieldRequired(field) {
568
+ const runtimeField = runtimeConditions.value.fields[field.field_key];
569
+ if (runtimeField === void 0) {
570
+ return field.required === true;
571
+ }
572
+ return runtimeField.required;
573
+ }
574
+ function isFieldDisabled(field, page) {
575
+ const runtimeField = runtimeConditions.value.fields[field.field_key];
576
+ const fieldDisabled = runtimeField?.disabled ?? field.disabled === true;
577
+ return fieldDisabled || isPageDisabled(page);
578
+ }
579
+ function setActivePage(pageKey) {
580
+ activePageKey.value = pageKey;
581
+ }
582
+ function setActivePageIndex(index) {
583
+ if (visiblePages.value.length === 0) {
584
+ activePageKey.value = null;
585
+ return;
586
+ }
587
+ const safeIndex = Math.max(0, Math.min(index, visiblePages.value.length - 1));
588
+ const page = visiblePages.value[safeIndex];
589
+ activePageKey.value = page.page_key;
590
+ }
591
+ function onStepperModelUpdate(value) {
592
+ if (typeof value !== "number") {
593
+ return;
594
+ }
595
+ setActivePageIndex(value - 1);
596
+ }
597
+ function goToPrevPage() {
598
+ if (!canGoPrev.value) {
599
+ return;
600
+ }
601
+ setActivePageIndex(activePageIndex.value - 1);
602
+ }
603
+ function goToNextPage() {
604
+ if (!canGoNext.value) {
605
+ return;
606
+ }
607
+ setActivePageIndex(activePageIndex.value + 1);
608
+ }
609
+ function resolvePageIndexByFieldName(fieldName) {
610
+ const pageKey = pageKeyByFieldName.value[fieldName];
611
+ if (pageKey === void 0) {
612
+ return -1;
613
+ }
614
+ const visibleIndex = visiblePages.value.findIndex((page) => page.page_key === pageKey);
615
+ if (visibleIndex >= 0) {
616
+ return visibleIndex;
617
+ }
618
+ const allIndex = displayPages.value.findIndex((page) => page.page_key === pageKey);
619
+ return allIndex;
620
+ }
621
+ function navigateToFirstErrorPage(errors) {
622
+ for (const error of errors) {
623
+ if (typeof error.name !== "string" || error.name === "") {
624
+ continue;
625
+ }
626
+ const pageIndex = resolvePageIndexByFieldName(error.name);
627
+ if (pageIndex >= 0) {
628
+ setActivePageIndex(pageIndex);
629
+ return;
630
+ }
631
+ }
632
+ }
633
+ function onFormError(event) {
634
+ const errors = Array.isArray(event.errors) ? event.errors.filter((error) => typeof error.message === "string" && error.message !== "") : [];
635
+ rendererErrors.value = errors;
636
+ navigateToFirstErrorPage(errors);
637
+ }
638
+ function hasDateMethod(value) {
639
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
640
+ return false;
641
+ }
642
+ return typeof value.toDate === "function";
643
+ }
644
+ function hasToStringMethod(value) {
645
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
646
+ return false;
647
+ }
648
+ return typeof value.toString === "function";
649
+ }
650
+ function isRangeInput(value) {
651
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
652
+ return false;
653
+ }
654
+ return "start" in value && "end" in value;
655
+ }
656
+ function isFileValue(value) {
657
+ if (typeof File === "undefined") {
658
+ return false;
659
+ }
660
+ return value instanceof File;
661
+ }
662
+ function stripDatetimeOffset(value) {
663
+ if (value.endsWith("Z")) {
664
+ return value.slice(0, -1);
665
+ }
666
+ const match = value.match(/^(.*)([+-]\d{2}:\d{2})$/);
667
+ if (match === null) {
668
+ return value;
669
+ }
670
+ return match[1];
671
+ }
672
+ function formatTwoDigits(value) {
673
+ return String(value).padStart(2, "0");
674
+ }
675
+ function serializeDateWithOffset(date) {
676
+ const year = date.getFullYear();
677
+ const month = formatTwoDigits(date.getMonth() + 1);
678
+ const day = formatTwoDigits(date.getDate());
679
+ const hours = formatTwoDigits(date.getHours());
680
+ const minutes = formatTwoDigits(date.getMinutes());
681
+ const seconds = formatTwoDigits(date.getSeconds());
682
+ const offsetMinutes = -date.getTimezoneOffset();
683
+ const sign = offsetMinutes >= 0 ? "+" : "-";
684
+ const absoluteOffset = Math.abs(offsetMinutes);
685
+ const offsetHours = formatTwoDigits(Math.floor(absoluteOffset / 60));
686
+ const offsetRemainder = formatTwoDigits(absoluteOffset % 60);
687
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${sign}${offsetHours}:${offsetRemainder}`;
688
+ }
689
+ function serializeDateAsUtc(date) {
690
+ return date.toISOString().replace(".000Z", "Z");
691
+ }
692
+ function parseSingleDateValue(type, value) {
693
+ try {
694
+ if (type === "date") {
695
+ return parseDate(value);
696
+ }
697
+ if (type === "time") {
698
+ return parseTime(value);
699
+ }
700
+ if (type === "datetime") {
701
+ if (value.endsWith("Z") || /[+-]\d{2}:\d{2}$/.test(value)) {
702
+ return parseAbsoluteToLocal(value);
703
+ }
704
+ return parseDateTime(stripDatetimeOffset(value));
705
+ }
706
+ } catch {
707
+ return null;
708
+ }
709
+ return null;
710
+ }
711
+ function serializeSingleDateValue(type, value, mode) {
712
+ if (type === "date" || type === "time") {
713
+ if (hasToStringMethod(value)) {
714
+ return value.toString();
715
+ }
716
+ if (typeof value === "string") {
717
+ return value;
718
+ }
719
+ return null;
720
+ }
721
+ if (type === "datetime") {
722
+ if (hasDateMethod(value)) {
723
+ const date = value.toDate(getLocalTimeZone());
724
+ return mode === "utc" ? serializeDateAsUtc(date) : serializeDateWithOffset(date);
725
+ }
726
+ if (typeof value === "string") {
727
+ return value;
728
+ }
729
+ return null;
730
+ }
731
+ return null;
732
+ }
733
+ function parseRangeValue(type, value) {
734
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
735
+ return null;
736
+ }
737
+ const rangeValue = value;
738
+ const startValue = rangeValue.start;
739
+ const endValue = rangeValue.end;
740
+ const parsedStart = typeof startValue === "string" ? parseSingleDateValue(type === "date_range" ? "date" : "datetime", startValue) : null;
741
+ const parsedEnd = typeof endValue === "string" ? parseSingleDateValue(type === "date_range" ? "date" : "datetime", endValue) : null;
742
+ return {
743
+ start: parsedStart,
744
+ end: parsedEnd
745
+ };
746
+ }
747
+ function serializeRangeValue(type, value) {
748
+ if (!isRangeInput(value)) {
749
+ return {
750
+ start: null,
751
+ end: null
752
+ };
753
+ }
754
+ const startValue = serializeSingleDateValue(type === "date_range" ? "date" : "datetime", value.start, props.datetimeMode);
755
+ const endValue = serializeSingleDateValue(type === "date_range" ? "date" : "datetime", value.end, props.datetimeMode);
756
+ return {
757
+ start: startValue,
758
+ end: endValue
759
+ };
760
+ }
761
+ function normalizeSelectOptions(options) {
762
+ if (options === void 0) {
763
+ return [];
764
+ }
765
+ const items = [];
766
+ for (const option of options) {
767
+ if (typeof option === "string" || typeof option === "number" || typeof option === "boolean" || option === null) {
768
+ items.push({
769
+ label: String(option ?? ""),
770
+ value: option
771
+ });
772
+ continue;
773
+ }
774
+ const label = typeof option.label === "string" ? option.label : String(option.value ?? "");
775
+ items.push({
776
+ label,
777
+ value: option.value,
778
+ description: typeof option.description === "string" ? option.description : void 0,
779
+ disabled: typeof option.disabled === "boolean" ? option.disabled : void 0
780
+ });
781
+ }
782
+ return items;
783
+ }
784
+ function getFieldMetaUi(field) {
785
+ const uiValue = field.meta.ui;
786
+ if (!isFormForgeJsonObject(uiValue)) {
787
+ return {};
788
+ }
789
+ const formFieldUi = isFormForgeJsonObject(uiValue.formField) ? uiValue.formField : void 0;
790
+ const componentUi = isFormForgeJsonObject(uiValue.component) ? uiValue.component : uiValue;
791
+ return {
792
+ formField: formFieldUi,
793
+ component: componentUi
794
+ };
795
+ }
796
+ function getFieldValue(field) {
797
+ return formState.value[field.name];
798
+ }
799
+ function setFieldValue(fieldName, value) {
800
+ formState.value = {
801
+ ...formState.value,
802
+ [fieldName]: value
803
+ };
804
+ }
805
+ function getComponentModelValue(field) {
806
+ const value = getFieldValue(field);
807
+ if (field.type === "date" || field.type === "time" || field.type === "datetime") {
808
+ if (typeof value === "string") {
809
+ return parseSingleDateValue(field.type, value);
810
+ }
811
+ return null;
812
+ }
813
+ if (field.type === "date_range" || field.type === "datetime_range") {
814
+ return parseRangeValue(field.type, value);
815
+ }
816
+ if (field.type === "checkbox" || field.type === "switch") {
817
+ return typeof value === "boolean" ? value : false;
818
+ }
819
+ if (field.type === "checkbox_group") {
820
+ return Array.isArray(value) ? value : [];
821
+ }
822
+ if (field.type === "file") {
823
+ if (field.multiple === true) {
824
+ if (Array.isArray(value)) {
825
+ const files = [];
826
+ for (const item of value) {
827
+ if (isFileValue(item)) {
828
+ files.push(item);
829
+ }
830
+ }
831
+ return files;
832
+ }
833
+ return [];
834
+ }
835
+ if (isFileValue(value)) {
836
+ return value;
837
+ }
838
+ return null;
839
+ }
840
+ return value;
841
+ }
842
+ function getComponentProps(field, page) {
843
+ const metaUi = getFieldMetaUi(field);
844
+ const isDisabled = props.disabled || !usesExternalState.value && internalSubmit.submitting.value || isFieldDisabled(field, page);
845
+ const componentProps = {
846
+ name: field.name,
847
+ required: isFieldRequired(field),
848
+ disabled: isDisabled,
849
+ placeholder: field.placeholder
850
+ };
851
+ if (field.type === "text") {
852
+ componentProps.type = "text";
853
+ }
854
+ if (field.type === "email") {
855
+ componentProps.type = "email";
856
+ }
857
+ if (field.type === "number") {
858
+ componentProps.min = field.min ?? void 0;
859
+ componentProps.max = field.max ?? void 0;
860
+ componentProps.step = field.step ?? void 0;
861
+ }
862
+ if (field.type === "select" || field.type === "select_menu" || field.type === "radio" || field.type === "checkbox_group") {
863
+ componentProps.items = normalizeSelectOptions(field.options);
864
+ }
865
+ if (field.type === "date_range" || field.type === "datetime_range") {
866
+ componentProps.range = true;
867
+ }
868
+ if (field.type === "datetime" || field.type === "datetime_range") {
869
+ componentProps.granularity = "second";
870
+ }
871
+ if (field.type === "file") {
872
+ componentProps.multiple = field.multiple === true;
873
+ componentProps.accept = field.accept?.join(",");
874
+ }
875
+ if (metaUi.component !== void 0) {
876
+ return {
877
+ ...componentProps,
878
+ ...metaUi.component
879
+ };
880
+ }
881
+ return componentProps;
882
+ }
883
+ function onFieldUpdate(field, value) {
884
+ if (field.type === "date" || field.type === "time" || field.type === "datetime") {
885
+ setFieldValue(field.name, serializeSingleDateValue(field.type, value, props.datetimeMode));
886
+ return;
887
+ }
888
+ if (field.type === "date_range" || field.type === "datetime_range") {
889
+ setFieldValue(field.name, serializeRangeValue(field.type, value));
890
+ return;
891
+ }
892
+ setFieldValue(field.name, value);
893
+ }
894
+ function getLooseModelValue(field) {
895
+ return getComponentModelValue(field);
896
+ }
897
+ function onFieldModelUpdate(field, nextValue) {
898
+ onFieldUpdate(field, nextValue);
899
+ }
900
+ async function onSubmit() {
901
+ rendererErrors.value = [];
902
+ emit("submit", formState.value);
903
+ if (usesExternalState.value) {
904
+ return;
905
+ }
906
+ if (internalFormKey.value === "") {
907
+ emit("error", t("renderer.error.missingFormKey"));
908
+ return;
909
+ }
910
+ try {
911
+ const response = await internalSubmit.submit({
912
+ test: props.simulation,
913
+ version: props.formVersion,
914
+ mode: props.uploadMode
915
+ });
916
+ submittedResponse.value = response;
917
+ if (props.clearAfterSubmit) {
918
+ internalForm.resetState();
919
+ }
920
+ emit("submitted", response);
921
+ } catch (error) {
922
+ const submitErrors = [];
923
+ for (const [fieldName, messages] of Object.entries(internalSubmit.fieldErrors.value)) {
924
+ if (!Array.isArray(messages)) {
925
+ continue;
926
+ }
927
+ for (const message2 of messages) {
928
+ submitErrors.push({
929
+ name: fieldName,
930
+ message: message2
931
+ });
932
+ }
933
+ }
934
+ if (submitErrors.length > 0) {
935
+ rendererErrors.value = submitErrors;
936
+ navigateToFirstErrorPage(submitErrors);
937
+ }
938
+ const message = error instanceof Error ? error.message : t("renderer.error.submit");
939
+ emit("error", message);
940
+ }
941
+ }
942
+ </script>
943
+
944
+ <template>
945
+ <UForm
946
+ :state="formState"
947
+ :schema="getResolvedZodSchema()"
948
+ :validate="validate"
949
+ @submit="onSubmit"
950
+ @error="onFormError"
951
+ >
952
+ <div class="space-y-6">
953
+ <UStepper
954
+ v-if="shouldShowProgress && progressVariant === 'stepper'"
955
+ class="w-full"
956
+ :items="stepperItems"
957
+ :model-value="activePageIndex + 1"
958
+ :linear="false"
959
+ @update:model-value="onStepperModelUpdate"
960
+ />
961
+
962
+ <UProgress
963
+ v-if="shouldShowProgress && progressVariant === 'progress'"
964
+ :model-value="activePageIndex + 1"
965
+ :max="visiblePages.length"
966
+ status
967
+ />
968
+
969
+ <UAlert
970
+ v-if="!usesExternalState && internalForm.loading.value"
971
+ color="neutral"
972
+ variant="soft"
973
+ :title="t('renderer.loadingForm')"
974
+ />
975
+
976
+ <UAlert
977
+ v-if="!usesExternalState && internalForm.error.value"
978
+ color="error"
979
+ variant="soft"
980
+ :title="t('renderer.error.loadForm')"
981
+ :description="internalForm.error.value"
982
+ />
983
+
984
+ <UAlert
985
+ v-if="!usesExternalState && internalSubmit.error.value"
986
+ color="error"
987
+ variant="soft"
988
+ :title="t('renderer.error.submit')"
989
+ :description="internalSubmit.error.value.message"
990
+ />
991
+
992
+ <UAlert
993
+ v-if="shouldShowErrorAlert"
994
+ color="error"
995
+ variant="soft"
996
+ :title="t('renderer.alert.fixFields')"
997
+ >
998
+ <template #description>
999
+ <ul class="list-disc space-y-1 pl-5">
1000
+ <li
1001
+ v-for="(error, index) in rendererErrors"
1002
+ :key="`${error.name ?? error.id ?? 'form'}-${index}`"
1003
+ >
1004
+ <span v-if="typeof error.name === 'string' && error.name !== ''">{{ error.name }}:</span>
1005
+ {{ error.message }}
1006
+ </li>
1007
+ </ul>
1008
+ </template>
1009
+ </UAlert>
1010
+
1011
+ <UAlert
1012
+ v-if="!usesExternalState && submittedResponse !== null"
1013
+ color="success"
1014
+ variant="soft"
1015
+ :title="t('renderer.alert.submitted')"
1016
+ />
1017
+
1018
+ <section
1019
+ v-for="page in renderedPages"
1020
+ :key="page.page_key"
1021
+ class="space-y-4"
1022
+ @focusin="setActivePage(page.page_key)"
1023
+ @pointerdown="setActivePage(page.page_key)"
1024
+ >
1025
+ <div
1026
+ v-if="page.title !== '' || typeof page.description === 'string' && page.description !== ''"
1027
+ class="space-y-1"
1028
+ >
1029
+ <h3
1030
+ v-if="page.title !== ''"
1031
+ class="text-base font-semibold"
1032
+ >
1033
+ {{ page.title }}
1034
+ </h3>
1035
+ <p
1036
+ v-if="typeof page.description === 'string' && page.description !== ''"
1037
+ class="text-sm text-muted"
1038
+ >
1039
+ {{ page.description }}
1040
+ </p>
1041
+ </div>
1042
+
1043
+ <div class="space-y-4">
1044
+ <UFormField
1045
+ v-for="field in page.fields"
1046
+ v-show="isFieldVisible(field)"
1047
+ :key="field.field_key"
1048
+ :name="field.name"
1049
+ :label="field.label"
1050
+ :help="field.help_text"
1051
+ :required="isFieldRequired(field)"
1052
+ :ui="getFieldMetaUi(field).formField"
1053
+ >
1054
+ <UInput
1055
+ v-if="field.type === 'text' || field.type === 'email'"
1056
+ :model-value="getLooseModelValue(field)"
1057
+ v-bind="getComponentProps(field, page)"
1058
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1059
+ />
1060
+
1061
+ <UTextarea
1062
+ v-else-if="field.type === 'textarea'"
1063
+ :model-value="getLooseModelValue(field)"
1064
+ v-bind="getComponentProps(field, page)"
1065
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1066
+ />
1067
+
1068
+ <UInputNumber
1069
+ v-else-if="field.type === 'number'"
1070
+ :model-value="getLooseModelValue(field)"
1071
+ v-bind="getComponentProps(field, page)"
1072
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1073
+ />
1074
+
1075
+ <USelect
1076
+ v-else-if="field.type === 'select'"
1077
+ :model-value="getLooseModelValue(field)"
1078
+ v-bind="getComponentProps(field, page)"
1079
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1080
+ />
1081
+
1082
+ <USelectMenu
1083
+ v-else-if="field.type === 'select_menu'"
1084
+ :model-value="getLooseModelValue(field)"
1085
+ v-bind="getComponentProps(field, page)"
1086
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1087
+ />
1088
+
1089
+ <URadioGroup
1090
+ v-else-if="field.type === 'radio'"
1091
+ :model-value="getLooseModelValue(field)"
1092
+ v-bind="getComponentProps(field, page)"
1093
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1094
+ />
1095
+
1096
+ <UCheckbox
1097
+ v-else-if="field.type === 'checkbox'"
1098
+ :model-value="getLooseModelValue(field)"
1099
+ v-bind="getComponentProps(field, page)"
1100
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1101
+ />
1102
+
1103
+ <UCheckboxGroup
1104
+ v-else-if="field.type === 'checkbox_group'"
1105
+ :model-value="getLooseModelValue(field)"
1106
+ v-bind="getComponentProps(field, page)"
1107
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1108
+ />
1109
+
1110
+ <USwitch
1111
+ v-else-if="field.type === 'switch'"
1112
+ :model-value="getLooseModelValue(field)"
1113
+ v-bind="getComponentProps(field, page)"
1114
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1115
+ />
1116
+
1117
+ <UInputDate
1118
+ v-else-if="field.type === 'date' || field.type === 'datetime' || field.type === 'date_range' || field.type === 'datetime_range'"
1119
+ :model-value="getLooseModelValue(field)"
1120
+ v-bind="getComponentProps(field, page)"
1121
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1122
+ />
1123
+
1124
+ <UInputTime
1125
+ v-else-if="field.type === 'time'"
1126
+ :model-value="getLooseModelValue(field)"
1127
+ v-bind="getComponentProps(field, page)"
1128
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1129
+ />
1130
+
1131
+ <UFileUpload
1132
+ v-else
1133
+ :model-value="getLooseModelValue(field)"
1134
+ v-bind="getComponentProps(field, page)"
1135
+ @update:model-value="(nextValue) => onFieldModelUpdate(field, nextValue)"
1136
+ />
1137
+ </UFormField>
1138
+ </div>
1139
+ </section>
1140
+
1141
+ <div
1142
+ v-if="pagedMode"
1143
+ class="flex items-center justify-between gap-2"
1144
+ >
1145
+ <UButton
1146
+ type="button"
1147
+ variant="outline"
1148
+ color="neutral"
1149
+ :disabled="!canGoPrev"
1150
+ @click="goToPrevPage"
1151
+ >
1152
+ {{ t("renderer.navigation.previous") }}
1153
+ </UButton>
1154
+
1155
+ <UButton
1156
+ v-if="!isLastVisiblePage"
1157
+ type="button"
1158
+ :disabled="!canGoNext"
1159
+ @click="goToNextPage"
1160
+ >
1161
+ {{ t("renderer.navigation.next") }}
1162
+ </UButton>
1163
+
1164
+ <UButton
1165
+ v-else-if="!usesExternalState && showSubmit"
1166
+ type="submit"
1167
+ :loading="internalSubmit.submitting.value"
1168
+ :disabled="internalForm.loading.value || getResolvedSchema() === null"
1169
+ >
1170
+ {{ resolvedSubmitLabel }}
1171
+ </UButton>
1172
+ </div>
1173
+
1174
+ <div
1175
+ v-else-if="!usesExternalState && showSubmit"
1176
+ class="flex justify-end"
1177
+ >
1178
+ <UButton
1179
+ type="submit"
1180
+ :loading="internalSubmit.submitting.value"
1181
+ :disabled="internalForm.loading.value || getResolvedSchema() === null"
1182
+ >
1183
+ {{ resolvedSubmitLabel }}
1184
+ </UButton>
1185
+ </div>
1186
+ </div>
1187
+ </UForm>
1188
+ </template>