@evanschleret/formforgeclient 1.2.4 → 2.0.1

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 +299 -19
  17. package/dist/runtime/composables/useFormForgeI18n.js +299 -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
@@ -0,0 +1,595 @@
1
+ <script setup>
2
+ import { toRef } from "#imports";
3
+ import { parseDate, parseTime } from "@internationalized/date";
4
+ import { useFormForgeI18n } from "../../../composables/useFormForgeI18n";
5
+ import { normalizeFormForgeValidationConfig } from "../../../utils/validation";
6
+ import { defaultAddressFields } from "./builderFieldHelpers";
7
+ const props = defineProps({
8
+ field: { type: Object, required: true },
9
+ readonly: { type: Boolean, required: false, default: false },
10
+ mode: { type: String, required: true },
11
+ temporalMode: { type: String, required: false, default: void 0 }
12
+ });
13
+ const field = toRef(props, "field");
14
+ const { t, locale } = useFormForgeI18n();
15
+ function fieldMeta() {
16
+ if (typeof field.value.meta !== "object" || field.value.meta === null || Array.isArray(field.value.meta)) {
17
+ field.value.meta = {};
18
+ }
19
+ return field.value.meta;
20
+ }
21
+ function isValidationConfig(value) {
22
+ return value !== null && typeof value === "object" && !Array.isArray(value) && (value.match === "all" || value.match === "any") && Array.isArray(value.rules);
23
+ }
24
+ function isTemporalRangeValidationValue(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value) && ("start" in value || "end" in value);
26
+ }
27
+ function isTemporalRangeValidationOperator(operator) {
28
+ return operator === "between" || operator === "not_between";
29
+ }
30
+ function validationConfig() {
31
+ const candidate = fieldMeta().validation;
32
+ if (!isValidationConfig(candidate)) {
33
+ return null;
34
+ }
35
+ const normalized = normalizeFormForgeValidationConfig(candidate, props.mode);
36
+ if (normalized !== candidate) {
37
+ fieldMeta().validation = normalized;
38
+ }
39
+ return normalized;
40
+ }
41
+ function validationModeLabel() {
42
+ if (props.mode === "address") {
43
+ return t("builder.addressFields");
44
+ }
45
+ if (props.mode === "temporal") {
46
+ if (props.temporalMode === "time") {
47
+ return t("builder.temporalMode.time");
48
+ }
49
+ return t("builder.temporalMode.date");
50
+ }
51
+ if (field.value.type === "number") {
52
+ return t("builder.validation.target.number");
53
+ }
54
+ return t("builder.validation.target.text");
55
+ }
56
+ function isValidationEnabled() {
57
+ return validationConfig() !== null;
58
+ }
59
+ function isTextValidationMode() {
60
+ return props.mode === "text" || props.mode === "address";
61
+ }
62
+ function isTemporalValidationMode() {
63
+ return props.mode === "temporal";
64
+ }
65
+ function readNumber(value, fallback) {
66
+ if (typeof value === "number") {
67
+ return value;
68
+ }
69
+ if (typeof value === "string" && value !== "") {
70
+ const parsed = Number.parseFloat(value);
71
+ if (!Number.isNaN(parsed)) {
72
+ return parsed;
73
+ }
74
+ }
75
+ return fallback;
76
+ }
77
+ function createValidationRule(operator, value, target = null) {
78
+ return {
79
+ validation_key: `vr_${Math.random().toString(36).slice(2, 8)}`,
80
+ target,
81
+ operator,
82
+ value,
83
+ unit: operator === "min" || operator === "max" ? "characters" : null
84
+ };
85
+ }
86
+ function createTemporalValidationRule(operator) {
87
+ if (isTemporalRangeValidationOperator(operator)) {
88
+ return {
89
+ validation_key: `vr_${Math.random().toString(36).slice(2, 8)}`,
90
+ target: null,
91
+ operator,
92
+ value: {
93
+ start: null,
94
+ end: null
95
+ },
96
+ unit: null
97
+ };
98
+ }
99
+ return {
100
+ validation_key: `vr_${Math.random().toString(36).slice(2, 8)}`,
101
+ target: null,
102
+ operator,
103
+ value: null,
104
+ unit: null
105
+ };
106
+ }
107
+ function ensureValidationConfig() {
108
+ const existing = validationConfig();
109
+ if (existing !== null) {
110
+ return existing;
111
+ }
112
+ const next = {
113
+ match: "all",
114
+ rules: [
115
+ props.mode === "temporal" ? createTemporalValidationRule("after") : createValidationRule(
116
+ "min",
117
+ readNumber(field.value.min, 0),
118
+ props.mode === "address" ? defaultAddressFields(locale.value)[0]?.key ?? "line1" : null
119
+ )
120
+ ]
121
+ };
122
+ fieldMeta().validation = next;
123
+ return next;
124
+ }
125
+ function clearValidationConfig() {
126
+ const meta = fieldMeta();
127
+ delete meta.validation;
128
+ field.value.min = null;
129
+ field.value.max = null;
130
+ }
131
+ function setValidationEnabled(enabled) {
132
+ if (!isTextValidationMode() && !isTemporalValidationMode()) {
133
+ return;
134
+ }
135
+ if (!enabled) {
136
+ clearValidationConfig();
137
+ return;
138
+ }
139
+ ensureValidationConfig();
140
+ }
141
+ function validationRuleItems() {
142
+ if (props.mode === "temporal") {
143
+ return [
144
+ { label: t("builder.validation.operator.after"), value: "after" },
145
+ { label: t("builder.validation.operator.before"), value: "before" },
146
+ { label: t("builder.validation.operator.between"), value: "between" },
147
+ { label: t("builder.validation.operator.notBetween"), value: "not_between" }
148
+ ];
149
+ }
150
+ return [
151
+ { label: t("builder.validation.operator.min"), value: "min" },
152
+ { label: t("builder.validation.operator.max"), value: "max" },
153
+ { label: t("builder.validation.operator.regex"), value: "regex" },
154
+ { label: t("builder.validation.operator.eq"), value: "eq" },
155
+ { label: t("builder.validation.operator.neq"), value: "neq" },
156
+ { label: t("builder.validation.operator.contains"), value: "contains" },
157
+ { label: t("builder.validation.operator.notContains"), value: "not_contains" }
158
+ ];
159
+ }
160
+ function validationTargetItems() {
161
+ if (props.mode !== "address") {
162
+ return [];
163
+ }
164
+ const addressFields = Array.isArray(field.value.address_fields) && field.value.address_fields.length > 0 ? field.value.address_fields : defaultAddressFields(locale.value);
165
+ return addressFields.filter((addressField) => addressField.visible).map((addressField) => ({
166
+ label: addressField.label,
167
+ value: addressField.key
168
+ }));
169
+ }
170
+ function validationRuleTarget(rule) {
171
+ if (typeof rule.target === "string" && rule.target !== "") {
172
+ return rule.target;
173
+ }
174
+ return validationTargetItems()[0]?.value ?? "line1";
175
+ }
176
+ function validationMatchItems() {
177
+ return [
178
+ { label: t("builder.validation.match.all"), value: "all" },
179
+ { label: t("builder.validation.match.any"), value: "any" }
180
+ ];
181
+ }
182
+ function validationRules() {
183
+ return validationConfig()?.rules ?? [];
184
+ }
185
+ function isValidationRuleNumeric(rule) {
186
+ return rule.operator === "min" || rule.operator === "max";
187
+ }
188
+ function validationRuleValue(rule) {
189
+ if (typeof rule.value === "number" || typeof rule.value === "string") {
190
+ return String(rule.value);
191
+ }
192
+ if (isTemporalRangeValidationValue(rule.value)) {
193
+ return rule.value.start ?? "";
194
+ }
195
+ return "";
196
+ }
197
+ function validationRuleNumericValue(rule) {
198
+ if (typeof rule.value === "number") {
199
+ return rule.value;
200
+ }
201
+ if (typeof rule.value === "string" && rule.value !== "") {
202
+ const parsed = Number.parseFloat(rule.value);
203
+ if (!Number.isNaN(parsed)) {
204
+ return parsed;
205
+ }
206
+ }
207
+ return 0;
208
+ }
209
+ function parseTemporalValue(value) {
210
+ if (value === null || value === "") {
211
+ return void 0;
212
+ }
213
+ try {
214
+ if (props.temporalMode === "time") {
215
+ return parseTime(value);
216
+ }
217
+ return parseDate(value);
218
+ } catch {
219
+ return void 0;
220
+ }
221
+ }
222
+ function validationRuleTemporalValue(rule) {
223
+ if (isTemporalRangeValidationValue(rule.value)) {
224
+ return parseTemporalValue(rule.value.start ?? rule.value.end ?? null);
225
+ }
226
+ return parseTemporalValue(typeof rule.value === "string" ? rule.value : null);
227
+ }
228
+ function validationRuleTemporalRangeState(rule) {
229
+ if (isTemporalRangeValidationValue(rule.value)) {
230
+ return {
231
+ start: rule.value.start ?? null,
232
+ end: rule.value.end ?? null
233
+ };
234
+ }
235
+ return {
236
+ start: typeof rule.value === "string" ? rule.value : null,
237
+ end: null
238
+ };
239
+ }
240
+ function validationRuleTemporalRangeStartValue(rule) {
241
+ const range = validationRuleTemporalRangeState(rule);
242
+ return parseTemporalValue(range.start);
243
+ }
244
+ function validationRuleTemporalRangeEndValue(rule) {
245
+ const range = validationRuleTemporalRangeState(rule);
246
+ return parseTemporalValue(range.end);
247
+ }
248
+ function serializeTemporalValue(value) {
249
+ if (value === null || value === void 0) {
250
+ return null;
251
+ }
252
+ return value.toString();
253
+ }
254
+ function validationRulePlaceholder(operator) {
255
+ if (operator === "regex") {
256
+ return "^[A-Z].*";
257
+ }
258
+ return t("builder.validation.valuePlaceholder");
259
+ }
260
+ function validationRuleUnitItems(rule) {
261
+ if (rule.operator !== "min" && rule.operator !== "max") {
262
+ return [];
263
+ }
264
+ return [
265
+ { label: t("builder.validation.unit.characters"), value: "characters" }
266
+ ];
267
+ }
268
+ function updateValidationMatch(value) {
269
+ ensureValidationConfig().match = value;
270
+ }
271
+ function setValidationRuleValue(rule, value) {
272
+ if (isValidationRuleNumeric(rule)) {
273
+ const parsed = Number.parseFloat(value);
274
+ rule.value = Number.isNaN(parsed) ? 0 : parsed;
275
+ if (props.mode !== "address") {
276
+ const config = ensureValidationConfig();
277
+ const minRule = config.rules.find((candidate) => candidate.operator === "min");
278
+ const maxRule = config.rules.find((candidate) => candidate.operator === "max");
279
+ field.value.min = minRule === void 0 ? null : Number(minRule.value);
280
+ field.value.max = maxRule === void 0 ? null : Number(maxRule.value);
281
+ }
282
+ return;
283
+ }
284
+ rule.value = value;
285
+ }
286
+ function setValidationRuleTemporalValue(rule, value) {
287
+ rule.value = serializeTemporalValue(value);
288
+ }
289
+ function setValidationRuleTemporalRangeValue(rule, key, value) {
290
+ const current = validationRuleTemporalRangeState(rule);
291
+ rule.value = {
292
+ ...current,
293
+ [key]: serializeTemporalValue(value)
294
+ };
295
+ }
296
+ function setValidationRuleTarget(rule, target) {
297
+ rule.target = target;
298
+ }
299
+ function setValidationRuleOperator(rule, operator) {
300
+ rule.operator = operator;
301
+ rule.unit = operator === "min" || operator === "max" ? "characters" : null;
302
+ if (props.mode === "temporal") {
303
+ if (isTemporalRangeValidationOperator(operator)) {
304
+ rule.value = isTemporalRangeValidationValue(rule.value) ? rule.value : {
305
+ start: validationRuleValue(rule) || null,
306
+ end: null
307
+ };
308
+ return;
309
+ }
310
+ if (isTemporalRangeValidationValue(rule.value)) {
311
+ rule.value = rule.value.start ?? rule.value.end ?? null;
312
+ }
313
+ return;
314
+ }
315
+ if (isValidationRuleNumeric(rule)) {
316
+ rule.value = validationRuleNumericValue(rule);
317
+ }
318
+ if (props.mode !== "address") {
319
+ const config = ensureValidationConfig();
320
+ const minRule = config.rules.find((candidate) => candidate.operator === "min");
321
+ const maxRule = config.rules.find((candidate) => candidate.operator === "max");
322
+ field.value.min = minRule === void 0 ? null : Number(minRule.value);
323
+ field.value.max = maxRule === void 0 ? null : Number(maxRule.value);
324
+ }
325
+ }
326
+ function addValidationRule(afterIndex) {
327
+ const config = ensureValidationConfig();
328
+ const nextRule = props.mode === "temporal" ? createTemporalValidationRule("after") : createValidationRule("contains", "");
329
+ config.rules.splice(afterIndex + 1, 0, nextRule);
330
+ }
331
+ function removeValidationRule(ruleIndex) {
332
+ const config = validationConfig();
333
+ if (config === null) {
334
+ return;
335
+ }
336
+ if (config.rules.length <= 1) {
337
+ clearValidationConfig();
338
+ return;
339
+ }
340
+ config.rules.splice(ruleIndex, 1);
341
+ }
342
+ function validationDescription() {
343
+ if (props.mode === "temporal") {
344
+ return t("builder.validation.dateDescription");
345
+ }
346
+ return t("builder.validationRulesDescription");
347
+ }
348
+ </script>
349
+
350
+ <template>
351
+ <div class="grid gap-4">
352
+ <UCard
353
+ v-if="mode === 'text'"
354
+ :ui="{ body: 'grid gap-4' }"
355
+ >
356
+ <div class="grid gap-4">
357
+ <div class="flex items-start justify-between gap-4">
358
+ <div class="grid gap-1">
359
+ <span class="text-sm font-medium text-default">{{ t("builder.longResponse") }}</span>
360
+ <p class="text-sm text-muted">
361
+ {{ t("builder.longResponseDescription") }}
362
+ </p>
363
+ </div>
364
+ <USwitch
365
+ :model-value="field.type === 'textarea'"
366
+ :disabled="readonly"
367
+ @update:model-value="(value) => {
368
+ if (field.type === 'text' || field.type === 'textarea') {
369
+ field.type = value ? 'textarea' : 'text';
370
+ }
371
+ }"
372
+ />
373
+ </div>
374
+
375
+ <div class="flex items-start justify-between gap-4">
376
+ <div class="grid gap-1">
377
+ <span class="text-sm font-medium text-default">{{ t("builder.validationRules") }}</span>
378
+ <p class="text-sm text-muted">
379
+ {{ t("builder.validationRulesDescription") }}
380
+ </p>
381
+ </div>
382
+ <USwitch
383
+ :model-value="isValidationEnabled()"
384
+ :disabled="readonly"
385
+ @update:model-value="(value) => setValidationEnabled(value)"
386
+ />
387
+ </div>
388
+ </div>
389
+ </UCard>
390
+
391
+ <UCard
392
+ v-if="mode === 'address' || mode === 'temporal' || mode === 'text'"
393
+ :ui="{ body: 'grid gap-4' }"
394
+ >
395
+ <div
396
+ v-if="mode === 'temporal'"
397
+ class="flex items-center justify-between gap-4"
398
+ >
399
+ <div class="grid gap-1">
400
+ <span class="text-sm font-medium text-default">
401
+ {{ t("builder.temporalHourCycle.label") }}
402
+ </span>
403
+ <p class="text-sm text-muted">
404
+ {{ t("builder.temporalHourCycle.description") }}
405
+ </p>
406
+ </div>
407
+ <USwitch
408
+ :model-value="props.field.hour_cycle !== 12"
409
+ :disabled="readonly"
410
+ :aria-label="t('builder.temporalHourCycle.label')"
411
+ @update:model-value="(value) => {
412
+ field.hour_cycle = value ? 24 : 12;
413
+ }"
414
+ />
415
+ </div>
416
+
417
+ <div class="flex items-start justify-between gap-4">
418
+ <div class="grid gap-1">
419
+ <span class="text-sm font-medium text-default">{{ t("builder.validationRules") }}</span>
420
+ <p class="text-sm text-muted">
421
+ {{ validationDescription() }}
422
+ </p>
423
+ </div>
424
+ <USwitch
425
+ :model-value="isValidationEnabled()"
426
+ :disabled="readonly"
427
+ @update:model-value="(value) => setValidationEnabled(value)"
428
+ />
429
+ </div>
430
+
431
+ <div
432
+ v-if="isValidationEnabled()"
433
+ class="grid gap-4 border-t border-gray-200 pt-4 dark:border-white/10"
434
+ >
435
+ <div class="flex items-center gap-4">
436
+ <div class="min-w-0 flex-1 grid gap-1">
437
+ <span class="text-sm font-medium text-default">{{ t("builder.validationRules") }}</span>
438
+ <p class="text-sm text-muted">
439
+ {{ validationModeLabel() }}
440
+ </p>
441
+ </div>
442
+ <div
443
+ v-if="validationRules().length > 1"
444
+ class="shrink-0"
445
+ >
446
+ <USelect
447
+ :model-value="validationConfig()?.match ?? 'all'"
448
+ :items="validationMatchItems()"
449
+ :disabled="readonly"
450
+ class="w-56"
451
+ @update:model-value="(value) => updateValidationMatch(value)"
452
+ />
453
+ </div>
454
+ </div>
455
+
456
+ <div class="grid gap-3">
457
+ <div
458
+ v-for="(rule, ruleIndex) in validationRules()"
459
+ :key="rule.validation_key"
460
+ class="flex flex-col gap-3 lg:flex-row lg:items-end"
461
+ >
462
+ <USelect
463
+ v-if="mode === 'address'"
464
+ :model-value="validationRuleTarget(rule)"
465
+ :items="validationTargetItems()"
466
+ :disabled="readonly"
467
+ class="w-full lg:w-72 lg:shrink-0"
468
+ @update:model-value="(value) => setValidationRuleTarget(rule, value)"
469
+ />
470
+
471
+ <USelect
472
+ :model-value="rule.operator"
473
+ :items="validationRuleItems()"
474
+ :disabled="readonly"
475
+ class="w-full lg:w-72 lg:shrink-0"
476
+ @update:model-value="(value) => setValidationRuleOperator(rule, value)"
477
+ />
478
+
479
+ <template v-if="mode === 'temporal'">
480
+ <template v-if="isTemporalRangeValidationOperator(rule.operator)">
481
+ <div class="grid min-w-0 flex-1 gap-2 sm:grid-cols-2">
482
+ <div class="grid gap-1">
483
+ <span class="text-sm font-medium text-muted">
484
+ {{ t("builder.validation.rangeStart") }}
485
+ </span>
486
+ <UInputTime
487
+ v-if="temporalMode === 'time'"
488
+ :model-value="validationRuleTemporalRangeStartValue(rule)"
489
+ :disabled="readonly"
490
+ :hour-cycle="props.field.hour_cycle === 12 ? 12 : 24"
491
+ class="min-w-0"
492
+ @update:model-value="(value) => setValidationRuleTemporalRangeValue(rule, 'start', value)"
493
+ />
494
+ <UInputDate
495
+ v-else
496
+ :model-value="validationRuleTemporalRangeStartValue(rule)"
497
+ :disabled="readonly"
498
+ class="min-w-0"
499
+ @update:model-value="(value) => setValidationRuleTemporalRangeValue(rule, 'start', value)"
500
+ />
501
+ </div>
502
+
503
+ <div class="grid gap-1">
504
+ <span class="text-sm font-medium text-muted">
505
+ {{ t("builder.validation.rangeEnd") }}
506
+ </span>
507
+ <UInputTime
508
+ v-if="temporalMode === 'time'"
509
+ :model-value="validationRuleTemporalRangeEndValue(rule)"
510
+ :disabled="readonly"
511
+ :hour-cycle="props.field.hour_cycle === 12 ? 12 : 24"
512
+ class="min-w-0"
513
+ @update:model-value="(value) => setValidationRuleTemporalRangeValue(rule, 'end', value)"
514
+ />
515
+ <UInputDate
516
+ v-else
517
+ :model-value="validationRuleTemporalRangeEndValue(rule)"
518
+ :disabled="readonly"
519
+ class="min-w-0"
520
+ @update:model-value="(value) => setValidationRuleTemporalRangeValue(rule, 'end', value)"
521
+ />
522
+ </div>
523
+ </div>
524
+ </template>
525
+ <template v-else>
526
+ <UInputTime
527
+ v-if="temporalMode === 'time'"
528
+ :model-value="validationRuleTemporalValue(rule)"
529
+ :disabled="readonly"
530
+ :hour-cycle="props.field.hour_cycle === 12 ? 12 : 24"
531
+ class="min-w-0 flex-1"
532
+ @update:model-value="(value) => setValidationRuleTemporalValue(rule, value)"
533
+ />
534
+ <UInputDate
535
+ v-else
536
+ :model-value="validationRuleTemporalValue(rule)"
537
+ :disabled="readonly"
538
+ class="min-w-0 flex-1"
539
+ @update:model-value="(value) => setValidationRuleTemporalValue(rule, value)"
540
+ />
541
+ </template>
542
+ </template>
543
+
544
+ <template v-else-if="isValidationRuleNumeric(rule)">
545
+ <UInputNumber
546
+ :model-value="validationRuleNumericValue(rule)"
547
+ :disabled="readonly"
548
+ :placeholder="validationRulePlaceholder(rule.operator)"
549
+ :min="0"
550
+ class="min-w-0 flex-1"
551
+ @update:model-value="(value) => setValidationRuleValue(rule, String(value ?? 0))"
552
+ />
553
+ <USelect
554
+ :model-value="rule.unit ?? 'characters'"
555
+ :items="validationRuleUnitItems(rule)"
556
+ :disabled="readonly"
557
+ class="w-full lg:w-40 lg:shrink-0"
558
+ @update:model-value="(value) => {
559
+ rule.unit = value;
560
+ }"
561
+ />
562
+ </template>
563
+
564
+ <template v-else>
565
+ <UInput
566
+ :model-value="validationRuleValue(rule)"
567
+ :disabled="readonly"
568
+ :placeholder="validationRulePlaceholder(rule.operator)"
569
+ class="min-w-0 flex-1"
570
+ @update:model-value="(value) => setValidationRuleValue(rule, value)"
571
+ />
572
+ </template>
573
+
574
+ <div class="flex shrink-0 items-center justify-end gap-2">
575
+ <UButton
576
+ color="neutral"
577
+ variant="ghost"
578
+ icon="i-lucide-plus"
579
+ :disabled="readonly"
580
+ @click.stop="addValidationRule(ruleIndex)"
581
+ />
582
+ <UButton
583
+ color="neutral"
584
+ variant="ghost"
585
+ icon="i-lucide-trash-2"
586
+ :disabled="readonly"
587
+ @click.stop="removeValidationRule(ruleIndex)"
588
+ />
589
+ </div>
590
+ </div>
591
+ </div>
592
+ </div>
593
+ </UCard>
594
+ </div>
595
+ </template>
@@ -0,0 +1,14 @@
1
+ import type { FormForgeFieldSchema, FormForgeTemporalMode } from '../../../types/index.js';
2
+ interface Props {
3
+ field: FormForgeFieldSchema;
4
+ readonly?: boolean;
5
+ mode: 'text' | 'address' | 'temporal';
6
+ temporalMode?: FormForgeTemporalMode;
7
+ }
8
+ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
9
+ readonly: boolean;
10
+ temporalMode: FormForgeTemporalMode;
11
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: typeof __VLS_export;
13
+ export default _default;
14
+ //# sourceMappingURL=FormForgeBuilderValidationRulesSection.vue.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { FormForgeAddressFieldSchema } from '../../../types/index.js';
2
+ export declare function defaultAddressFields(locale?: string): FormForgeAddressFieldSchema[];
3
+ //# sourceMappingURL=builderFieldHelpers.d.ts.map
@@ -0,0 +1,4 @@
1
+ import { createDefaultAddressFields } from "../../../utils/defaults.js";
2
+ export function defaultAddressFields(locale) {
3
+ return createDefaultAddressFields(locale);
4
+ }
@@ -1,6 +1,6 @@
1
1
  export type { FormForgeClientError, FormForgeClientErrorCode, FormForgeBusinessErrorCode, FormForgeValidationErrorPayload } from './errors.js';
2
2
  export type { FormForgeCategory, FormForgeCategoryCreateInput, FormForgeCategoryUpdateInput, FormForgeCategoryListQuery, FormForgeCategoryListResponse, FormForgePaginationMeta, FormForgePaginationLinks, FormForgeCategorySelectOption } from './category.js';
3
- export type { FormForgeFieldType, FormForgeFieldOption, FormForgeFieldOptionObject, FormForgeFieldSchema, FormForgeFileFieldSchema, FormForgePageSchema, FormForgeCondition, FormForgeConditionClause, FormForgeConditionAction, FormForgeConditionMatch, FormForgeConditionOperator, FormForgeConditionTargetType, FormForgeDraftSettings, FormForgeFormSchema, FormForgeOptionValue, FormForgeFieldStorage } from './schema.js';
3
+ export type { FormForgeFieldType, FormForgeAddressFieldKey, FormForgeAddressFieldSchema, FormForgeFieldOption, FormForgeFieldOptionObject, FormForgeFieldSchema, FormForgeTemporalFieldSchema, FormForgeTemporalMode, FormForgeTemporalValidationRangeValue, FormForgeFileFieldSchema, FormForgeFieldValidationConfig, FormForgeFieldValidationMatch, FormForgeFieldValidationOperator, FormForgeFieldValidationRule, FormForgePageSchema, FormForgePageLogic, FormForgePageLogicClause, FormForgePageLogicFallback, FormForgePageLogicFallbackAction, FormForgePageLogicMatch, FormForgePageLogicOperator, FormForgePageLogicRule, FormForgePageLogicThen, FormForgePageLogicThenAction, FormForgeCondition, FormForgeConditionClause, FormForgeConditionAction, FormForgeConditionMatch, FormForgeConditionOperator, FormForgeConditionTargetType, FormForgeDraftSettings, FormForgeFormSchema, FormForgeOptionValue, FormForgeFieldStorage } from './schema.js';
4
4
  export type { FormForgeBeforeRequestContext, FormForgeBaseURLParams, FormForgeBaseURLParamsInput, FormForgeClientConfig, FormForgeDraftRecord, FormForgeDraftResponse, FormForgeDraftSaveInput, FormForgeHttpAdapter, FormForgeHttpRequest, FormForgeHttpResponse, FormForgeResolveInput, FormForgeSchemaVersionsResponse, FormForgeScopedRouteDefinition, FormForgeScopedRouteMap, FormForgeScope, FormForgeScopeParams, FormForgeScopeParamsInput, FormForgeResolvedScope, FormForgeStageUploadInput, FormForgeStagedUploadResponse, FormForgeSubmitInput, FormForgeJsonSubmissionPayload, FormForgeJsonSubmissionObject, FormForgeJsonSubmissionValue, FormForgeSubmissionPayload, FormForgeSubmissionResponse, FormForgeResponsesListResponse, FormForgeSubmissionValue, FormForgeUploadMode, FormForgeDatetimeMode, FormForgeRangeValue, FormForgeStagedUploadValue, FormForgeDirectUploadValue } from './api.js';
5
5
  export type { FormForgeClient } from '../api/client.js';
6
6
  export type { FormForgeManagementCreateInput, FormForgeManagementPatchInput, FormForgeManagementForm, FormForgeRevisionSummary, FormForgeDiffResponse } from './management.js';
@@ -3,6 +3,12 @@ import type { FormForgeJsonObject } from './json.js';
3
3
  import type { FormForgeCategory } from './category.js';
4
4
  export interface FormForgeManagementCreateInput {
5
5
  title: string;
6
+ schema_version?: number;
7
+ publish_at?: string | null;
8
+ pause_at?: string | null;
9
+ response_limit?: number | null;
10
+ submission_code?: string | null;
11
+ submission_code_required?: boolean;
6
12
  fields?: FormForgeFieldSchema[];
7
13
  pages?: FormForgePageSchema[];
8
14
  conditions?: FormForgeCondition[];
@@ -15,6 +21,12 @@ export interface FormForgeManagementCreateInput {
15
21
  }
16
22
  export interface FormForgeManagementPatchInput {
17
23
  title?: string;
24
+ schema_version?: number;
25
+ publish_at?: string | null;
26
+ pause_at?: string | null;
27
+ response_limit?: number | null;
28
+ submission_code?: string | null;
29
+ submission_code_required?: boolean;
18
30
  fields?: FormForgeFieldSchema[];
19
31
  pages?: FormForgePageSchema[];
20
32
  conditions?: FormForgeCondition[];