@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.
- package/README.md +10 -0
- package/dist/module.cjs +1 -0
- package/dist/module.d.cts +1 -0
- package/dist/module.d.mts +1 -0
- package/dist/module.d.ts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/api/client.js +4 -2
- package/dist/runtime/api/request.d.ts +1 -0
- package/dist/runtime/api/schema.js +4 -4
- package/dist/runtime/assets/formforge.css +1 -0
- package/dist/runtime/composables/index.d.ts +1 -1
- package/dist/runtime/composables/useFormForgeBuilder.d.ts +24 -2
- package/dist/runtime/composables/useFormForgeBuilder.js +299 -43
- package/dist/runtime/composables/useFormForgeForm.js +15 -5
- package/dist/runtime/composables/useFormForgeI18n.d.ts +299 -19
- package/dist/runtime/composables/useFormForgeI18n.js +299 -19
- package/dist/runtime/composables/useFormForgeSubmit.js +31 -9
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilder.vue +689 -738
- package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.d.vue.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue +32 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue.d.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRenderer.vue +344 -294
- package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRendererField.d.vue.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue +237 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue.d.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.d.vue.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue +31 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue.d.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeResponse.vue +4 -3
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue +118 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.d.vue.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue +205 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue.d.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue +37 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue +195 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue +91 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.d.vue.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue +387 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue.d.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.d.vue.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue +328 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue.d.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue +47 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue +595 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.d.ts +3 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.js +4 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/management.d.ts +12 -0
- package/dist/runtime/types/schema.d.ts +72 -4
- package/dist/runtime/utils/defaults.d.ts +7 -0
- package/dist/runtime/utils/defaults.js +86 -0
- package/dist/runtime/utils/page-logic.d.ts +24 -0
- package/dist/runtime/utils/page-logic.js +351 -0
- package/dist/runtime/utils/rich-text.d.ts +3 -0
- package/dist/runtime/utils/rich-text.js +72 -0
- package/dist/runtime/utils/schema.d.ts +1 -1
- package/dist/runtime/utils/schema.js +70 -16
- package/dist/runtime/utils/temporal.d.ts +10 -0
- package/dist/runtime/utils/temporal.js +28 -0
- package/dist/runtime/utils/validation.d.ts +5 -0
- package/dist/runtime/utils/validation.js +36 -0
- package/dist/runtime/validation/zod.d.ts +5 -2
- package/dist/runtime/validation/zod.js +563 -54
- package/dist/types.d.mts +2 -0
- package/package.json +18 -14
|
@@ -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>
|
package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts
ADDED
|
@@ -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
|
|
@@ -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[];
|