@evanschleret/formforgeclient 1.2.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/module.cjs +1 -0
- package/dist/module.d.cts +1 -0
- package/dist/module.d.mts +1 -0
- package/dist/module.d.ts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/api/client.js +4 -2
- package/dist/runtime/api/request.d.ts +1 -0
- package/dist/runtime/api/schema.js +4 -4
- package/dist/runtime/assets/formforge.css +1 -0
- package/dist/runtime/composables/index.d.ts +1 -1
- package/dist/runtime/composables/useFormForgeBuilder.d.ts +24 -2
- package/dist/runtime/composables/useFormForgeBuilder.js +299 -43
- package/dist/runtime/composables/useFormForgeClient.js +3 -5
- package/dist/runtime/composables/useFormForgeForm.js +15 -5
- package/dist/runtime/composables/useFormForgeI18n.d.ts +245 -19
- package/dist/runtime/composables/useFormForgeI18n.js +245 -19
- package/dist/runtime/composables/useFormForgeSubmit.js +31 -9
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilder.vue +689 -738
- package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +21 -2
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.d.vue.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue +32 -0
- package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue.d.ts +17 -0
- package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRenderer.vue +344 -294
- package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +3 -4
- package/dist/runtime/renderers/default/FormForgeRendererField.d.vue.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue +237 -0
- package/dist/runtime/renderers/default/FormForgeRendererField.vue.d.ts +22 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.d.vue.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue +31 -0
- package/dist/runtime/renderers/default/FormForgeRendererPage.vue.d.ts +18 -0
- package/dist/runtime/renderers/default/FormForgeResponse.vue +4 -3
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue +118 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.d.vue.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue +205 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue.d.ts +46 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue +37 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue +195 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue +91 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.d.vue.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue +387 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue.d.ts +13 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.d.vue.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue +328 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue.d.ts +44 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.d.vue.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue +47 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue.d.ts +11 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.d.vue.ts +14 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue +595 -0
- package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts +14 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.d.ts +3 -0
- package/dist/runtime/renderers/default/builder/builderFieldHelpers.js +4 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/management.d.ts +12 -0
- package/dist/runtime/types/schema.d.ts +72 -4
- package/dist/runtime/utils/defaults.d.ts +7 -0
- package/dist/runtime/utils/defaults.js +86 -0
- package/dist/runtime/utils/page-logic.d.ts +24 -0
- package/dist/runtime/utils/page-logic.js +351 -0
- package/dist/runtime/utils/rich-text.d.ts +3 -0
- package/dist/runtime/utils/rich-text.js +72 -0
- package/dist/runtime/utils/schema.d.ts +1 -1
- package/dist/runtime/utils/schema.js +70 -16
- package/dist/runtime/utils/temporal.d.ts +10 -0
- package/dist/runtime/utils/temporal.js +28 -0
- package/dist/runtime/utils/validation.d.ts +5 -0
- package/dist/runtime/utils/validation.js +36 -0
- package/dist/runtime/validation/zod.d.ts +5 -2
- package/dist/runtime/validation/zod.js +563 -54
- package/dist/types.d.mts +2 -0
- package/package.json +18 -14
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useOverlay } from "@nuxt/ui/composables/useOverlay";
|
|
3
|
+
import { useFormForgeI18n } from "../../../composables/useFormForgeI18n";
|
|
4
|
+
import FormForgeBuilderBlockSettingsModal from "../FormForgeBuilderBlockSettingsModal.vue";
|
|
5
|
+
import FormForgeBuilderQuestionRow from "./FormForgeBuilderQuestionRow.vue";
|
|
6
|
+
import { ref } from "#imports";
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
page: { type: Object, required: true },
|
|
9
|
+
pages: { type: Array, required: true },
|
|
10
|
+
pageIndex: { type: Number, required: true },
|
|
11
|
+
totalPages: { type: Number, required: true },
|
|
12
|
+
selectedFieldKey: { type: [String, null], required: false, default: null },
|
|
13
|
+
readonly: { type: Boolean, required: false, default: false },
|
|
14
|
+
fieldTypeItems: { type: Array, required: true }
|
|
15
|
+
});
|
|
16
|
+
const emit = defineEmits(["select-field", "move-page", "duplicate-page", "remove-page", "add-question", "move-field", "duplicate-field", "remove-field", "change-field-type", "add-field-below", "move-field-to-block"]);
|
|
17
|
+
const { t } = useFormForgeI18n();
|
|
18
|
+
const fieldTypeItems = props.fieldTypeItems;
|
|
19
|
+
const overlay = useOverlay();
|
|
20
|
+
const isCollapsed = ref(false);
|
|
21
|
+
function futureBlockItems() {
|
|
22
|
+
return props.pages.filter((page) => page.page_key !== props.page.page_key).map((page) => ({
|
|
23
|
+
label: t("builder.blockCounter", {
|
|
24
|
+
current: props.pages.findIndex((candidate) => candidate.page_key === page.page_key) + 1,
|
|
25
|
+
total: props.totalPages
|
|
26
|
+
}),
|
|
27
|
+
value: page.page_key
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
function isQuestionSelected(fieldKey) {
|
|
31
|
+
return props.selectedFieldKey === fieldKey;
|
|
32
|
+
}
|
|
33
|
+
function addQuestion(type) {
|
|
34
|
+
emit("add-question", props.page.page_key, type);
|
|
35
|
+
}
|
|
36
|
+
function questionPickerItems() {
|
|
37
|
+
return fieldTypeItems.map((item) => ({
|
|
38
|
+
label: item.label,
|
|
39
|
+
icon: item.icon,
|
|
40
|
+
onSelect: () => addQuestion(item.value)
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
function onMoveField(field, direction) {
|
|
44
|
+
emit("move-field", props.page.page_key, field.field_key, direction);
|
|
45
|
+
}
|
|
46
|
+
function onMoveFieldToBlock(field, targetPageKey) {
|
|
47
|
+
emit("move-field-to-block", props.page.page_key, field.field_key, targetPageKey);
|
|
48
|
+
}
|
|
49
|
+
function openBlockSettings() {
|
|
50
|
+
const modal = overlay.create(FormForgeBuilderBlockSettingsModal, {
|
|
51
|
+
destroyOnClose: true
|
|
52
|
+
});
|
|
53
|
+
void modal.open({
|
|
54
|
+
page: props.page,
|
|
55
|
+
pages: props.pages,
|
|
56
|
+
pageIndex: props.pageIndex,
|
|
57
|
+
readonly: props.readonly
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function toggleCollapsed() {
|
|
61
|
+
isCollapsed.value = !isCollapsed.value;
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<UCard
|
|
67
|
+
variant="soft"
|
|
68
|
+
:ui="{
|
|
69
|
+
body: 'p-0 sm:p-0'
|
|
70
|
+
}"
|
|
71
|
+
>
|
|
72
|
+
<div class="flex flex-row">
|
|
73
|
+
<UBadge
|
|
74
|
+
class="page-drag-handle cursor-grab select-none"
|
|
75
|
+
:ui="{
|
|
76
|
+
base: 'rounded-none w-8 flex items-center justify-center'
|
|
77
|
+
}"
|
|
78
|
+
>
|
|
79
|
+
{{ pageIndex + 1 }}
|
|
80
|
+
</UBadge>
|
|
81
|
+
|
|
82
|
+
<div class="min-w-0 w-full">
|
|
83
|
+
<div class="px-5 py-4">
|
|
84
|
+
<div class="flex items-start justify-between gap-4">
|
|
85
|
+
<div class="min-w-0">
|
|
86
|
+
<p class="text-sm font-semibold">
|
|
87
|
+
{{ t("builder.blockLabel") }} {{ pageIndex + 1 }}
|
|
88
|
+
</p>
|
|
89
|
+
<p class="text-sm text-muted">
|
|
90
|
+
{{ t("builder.questionCount", { count: page.fields.length }) }}
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="flex items-center gap-1">
|
|
94
|
+
<UButton
|
|
95
|
+
color="neutral"
|
|
96
|
+
variant="ghost"
|
|
97
|
+
icon="i-lucide-arrow-up"
|
|
98
|
+
:disabled="readonly || pageIndex === 0"
|
|
99
|
+
@click="$emit('move-page', page.page_key, -1)"
|
|
100
|
+
/>
|
|
101
|
+
<UButton
|
|
102
|
+
color="neutral"
|
|
103
|
+
variant="ghost"
|
|
104
|
+
icon="i-lucide-arrow-down"
|
|
105
|
+
:disabled="readonly || pageIndex === totalPages - 1"
|
|
106
|
+
@click="$emit('move-page', page.page_key, 1)"
|
|
107
|
+
/>
|
|
108
|
+
<UButton
|
|
109
|
+
color="neutral"
|
|
110
|
+
variant="ghost"
|
|
111
|
+
icon="i-lucide-copy"
|
|
112
|
+
:disabled="readonly"
|
|
113
|
+
@click="$emit('duplicate-page', page.page_key)"
|
|
114
|
+
/>
|
|
115
|
+
<UButton
|
|
116
|
+
color="neutral"
|
|
117
|
+
variant="ghost"
|
|
118
|
+
icon="i-lucide-trash-2"
|
|
119
|
+
:disabled="readonly || totalPages <= 1"
|
|
120
|
+
@click="$emit('remove-page', page.page_key)"
|
|
121
|
+
/>
|
|
122
|
+
<UTooltip :text="isCollapsed ? t('builder.block.expand') : t('builder.block.collapse')">
|
|
123
|
+
<UButton
|
|
124
|
+
color="neutral"
|
|
125
|
+
variant="ghost"
|
|
126
|
+
:icon="isCollapsed ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
|
|
127
|
+
:disabled="readonly"
|
|
128
|
+
@click="toggleCollapsed"
|
|
129
|
+
/>
|
|
130
|
+
</UTooltip>
|
|
131
|
+
<UTooltip :text="t('builder.block.showSettings')">
|
|
132
|
+
<UButton
|
|
133
|
+
color="neutral"
|
|
134
|
+
variant="ghost"
|
|
135
|
+
icon="i-lucide-settings"
|
|
136
|
+
:disabled="readonly"
|
|
137
|
+
@click="openBlockSettings"
|
|
138
|
+
/>
|
|
139
|
+
</UTooltip>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<template v-if="!isCollapsed">
|
|
145
|
+
<USeparator
|
|
146
|
+
type="dashed"
|
|
147
|
+
orientation="horizontal"
|
|
148
|
+
/>
|
|
149
|
+
|
|
150
|
+
<div>
|
|
151
|
+
<template
|
|
152
|
+
v-for="(field, index) in page.fields"
|
|
153
|
+
:key="field.field_key"
|
|
154
|
+
>
|
|
155
|
+
<FormForgeBuilderQuestionRow
|
|
156
|
+
:page="page"
|
|
157
|
+
:field="field"
|
|
158
|
+
:index="index"
|
|
159
|
+
:selected="isQuestionSelected(field.field_key)"
|
|
160
|
+
:readonly="readonly"
|
|
161
|
+
:field-type-items="fieldTypeItems"
|
|
162
|
+
:future-block-items="futureBlockItems()"
|
|
163
|
+
@select="$emit('select-field', page.page_key, field.field_key)"
|
|
164
|
+
@move-up="onMoveField(field, -1)"
|
|
165
|
+
@move-down="onMoveField(field, 1)"
|
|
166
|
+
@duplicate="$emit('duplicate-field', page.page_key, field.field_key)"
|
|
167
|
+
@remove="$emit('remove-field', page.page_key, field.field_key)"
|
|
168
|
+
@change-type="(type) => $emit('change-field-type', page.page_key, field.field_key, type)"
|
|
169
|
+
@add-below="(type) => $emit('add-field-below', page.page_key, field.field_key, type)"
|
|
170
|
+
@move-to-block="(targetPageKey) => onMoveFieldToBlock(field, targetPageKey)"
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
<USeparator
|
|
174
|
+
v-if="index < page.fields.length - 1"
|
|
175
|
+
type="dashed"
|
|
176
|
+
orientation="horizontal"
|
|
177
|
+
/>
|
|
178
|
+
</template>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<USeparator
|
|
182
|
+
type="dashed"
|
|
183
|
+
orientation="horizontal"
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
<div class="px-5 py-4">
|
|
187
|
+
<UDropdownMenu
|
|
188
|
+
:items="questionPickerItems()"
|
|
189
|
+
:content="{ align: 'start', sideOffset: 8 }"
|
|
190
|
+
>
|
|
191
|
+
<UButton
|
|
192
|
+
color="neutral"
|
|
193
|
+
variant="soft"
|
|
194
|
+
icon="i-lucide-plus"
|
|
195
|
+
:disabled="readonly"
|
|
196
|
+
>
|
|
197
|
+
{{ t("builder.block.addQuestion") }}
|
|
198
|
+
</UButton>
|
|
199
|
+
</UDropdownMenu>
|
|
200
|
+
</div>
|
|
201
|
+
</template>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</UCard>
|
|
205
|
+
</template>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FormForgeFieldType, FormForgePageSchema } from '../../../types/index.js';
|
|
2
|
+
interface QuestionTypeItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: FormForgeFieldType;
|
|
5
|
+
icon?: string;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
page: FormForgePageSchema;
|
|
9
|
+
pages: FormForgePageSchema[];
|
|
10
|
+
pageIndex: number;
|
|
11
|
+
totalPages: number;
|
|
12
|
+
selectedFieldKey?: string | null;
|
|
13
|
+
readonly?: boolean;
|
|
14
|
+
fieldTypeItems: QuestionTypeItem[];
|
|
15
|
+
}
|
|
16
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
|
+
"select-field": (pageKey: string, fieldKey: string) => any;
|
|
18
|
+
"move-page": (pageKey: string, direction: 1 | -1) => any;
|
|
19
|
+
"duplicate-page": (pageKey: string) => any;
|
|
20
|
+
"remove-page": (pageKey: string) => any;
|
|
21
|
+
"add-question": (pageKey: string, type: FormForgeFieldType) => any;
|
|
22
|
+
"move-field": (pageKey: string, fieldKey: string, direction: 1 | -1) => any;
|
|
23
|
+
"duplicate-field": (pageKey: string, fieldKey: string) => any;
|
|
24
|
+
"remove-field": (pageKey: string, fieldKey: string) => any;
|
|
25
|
+
"change-field-type": (pageKey: string, fieldKey: string, type: FormForgeFieldType) => any;
|
|
26
|
+
"add-field-below": (pageKey: string, fieldKey: string, type: FormForgeFieldType) => any;
|
|
27
|
+
"move-field-to-block": (pageKey: string, fieldKey: string, targetPageKey: string) => any;
|
|
28
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
29
|
+
"onSelect-field"?: ((pageKey: string, fieldKey: string) => any) | undefined;
|
|
30
|
+
"onMove-page"?: ((pageKey: string, direction: 1 | -1) => any) | undefined;
|
|
31
|
+
"onDuplicate-page"?: ((pageKey: string) => any) | undefined;
|
|
32
|
+
"onRemove-page"?: ((pageKey: string) => any) | undefined;
|
|
33
|
+
"onAdd-question"?: ((pageKey: string, type: FormForgeFieldType) => any) | undefined;
|
|
34
|
+
"onMove-field"?: ((pageKey: string, fieldKey: string, direction: 1 | -1) => any) | undefined;
|
|
35
|
+
"onDuplicate-field"?: ((pageKey: string, fieldKey: string) => any) | undefined;
|
|
36
|
+
"onRemove-field"?: ((pageKey: string, fieldKey: string) => any) | undefined;
|
|
37
|
+
"onChange-field-type"?: ((pageKey: string, fieldKey: string, type: FormForgeFieldType) => any) | undefined;
|
|
38
|
+
"onAdd-field-below"?: ((pageKey: string, fieldKey: string, type: FormForgeFieldType) => any) | undefined;
|
|
39
|
+
"onMove-field-to-block"?: ((pageKey: string, fieldKey: string, targetPageKey: string) => any) | undefined;
|
|
40
|
+
}>, {
|
|
41
|
+
readonly: boolean;
|
|
42
|
+
selectedFieldKey: string | null;
|
|
43
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
44
|
+
declare const _default: typeof __VLS_export;
|
|
45
|
+
export default _default;
|
|
46
|
+
//# sourceMappingURL=FormForgeBuilderBlockCard.vue.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FormForgeFieldSchema } from '../../../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
field: FormForgeFieldSchema;
|
|
4
|
+
readonly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
7
|
+
readonly: boolean;
|
|
8
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=FormForgeBuilderChoiceDisplayField.vue.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { toRef } from "#imports";
|
|
3
|
+
import { useFormForgeI18n } from "../../../composables/useFormForgeI18n";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
field: { type: Object, required: true },
|
|
6
|
+
readonly: { type: Boolean, required: false, default: false }
|
|
7
|
+
});
|
|
8
|
+
const field = toRef(props, "field");
|
|
9
|
+
const { t } = useFormForgeI18n();
|
|
10
|
+
function choiceDisplayValue() {
|
|
11
|
+
if (field.value.display === "list" || field.value.display === "menu") {
|
|
12
|
+
return field.value.display;
|
|
13
|
+
}
|
|
14
|
+
if (field.value.type === "radio" || field.value.type === "checkbox_group") {
|
|
15
|
+
return "list";
|
|
16
|
+
}
|
|
17
|
+
return "menu";
|
|
18
|
+
}
|
|
19
|
+
function setChoiceDisplay(display) {
|
|
20
|
+
field.value.display = display;
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div class="grid gap-2">
|
|
26
|
+
<span class="text-sm font-medium">{{ t("builder.choiceDisplay") }}</span>
|
|
27
|
+
<USelect
|
|
28
|
+
:model-value="choiceDisplayValue()"
|
|
29
|
+
:items="[
|
|
30
|
+
{ label: t('builder.choiceDisplay.list'), value: 'list' },
|
|
31
|
+
{ label: t('builder.choiceDisplay.menu'), value: 'menu' }
|
|
32
|
+
]"
|
|
33
|
+
:disabled="readonly"
|
|
34
|
+
@update:model-value="(value) => setChoiceDisplay(value)"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FormForgeFieldSchema } from '../../../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
field: FormForgeFieldSchema;
|
|
4
|
+
readonly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
7
|
+
readonly: boolean;
|
|
8
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=FormForgeBuilderChoiceDisplayField.vue.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FormForgeFieldSchema } from '../../../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
field: FormForgeFieldSchema;
|
|
4
|
+
readonly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
7
|
+
readonly: boolean;
|
|
8
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=FormForgeBuilderChoiceOptionsField.vue.d.ts.map
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { toRef } from "#imports";
|
|
3
|
+
import { useFormForgeI18n } from "../../../composables/useFormForgeI18n";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
field: { type: Object, required: true },
|
|
6
|
+
readonly: { type: Boolean, required: false, default: false }
|
|
7
|
+
});
|
|
8
|
+
const field = toRef(props, "field");
|
|
9
|
+
const { t } = useFormForgeI18n();
|
|
10
|
+
function minimumChoiceOptionCount() {
|
|
11
|
+
return field.value.type === "checkbox_group" ? 2 : 1;
|
|
12
|
+
}
|
|
13
|
+
function choiceOptions() {
|
|
14
|
+
return Array.isArray(field.value.options) ? field.value.options : [];
|
|
15
|
+
}
|
|
16
|
+
function optionLabel(option) {
|
|
17
|
+
if (option === void 0 || option === null) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
if (typeof option === "object" && option !== null && "label" in option) {
|
|
21
|
+
return typeof option.label === "string" ? option.label : "";
|
|
22
|
+
}
|
|
23
|
+
return String(option);
|
|
24
|
+
}
|
|
25
|
+
function setOptionLabel(optionIndex, value) {
|
|
26
|
+
const options = [...choiceOptions()];
|
|
27
|
+
const option = options[optionIndex];
|
|
28
|
+
if (option === void 0 || option === null) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (typeof option === "object" && option !== null && "value" in option) {
|
|
32
|
+
options[optionIndex] = {
|
|
33
|
+
...option,
|
|
34
|
+
label: value
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
options[optionIndex] = {
|
|
38
|
+
label: value,
|
|
39
|
+
value: option
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
field.value.options = options;
|
|
43
|
+
}
|
|
44
|
+
function addChoiceOption() {
|
|
45
|
+
const options = [...choiceOptions()];
|
|
46
|
+
options.push({
|
|
47
|
+
label: "",
|
|
48
|
+
value: `option_${options.length + 1}`
|
|
49
|
+
});
|
|
50
|
+
field.value.options = options;
|
|
51
|
+
}
|
|
52
|
+
function removeChoiceOption(optionIndex) {
|
|
53
|
+
const options = [...choiceOptions()];
|
|
54
|
+
if (options.length <= minimumChoiceOptionCount()) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
options.splice(optionIndex, 1);
|
|
58
|
+
field.value.options = options;
|
|
59
|
+
}
|
|
60
|
+
function addSpecialChoiceOption(type) {
|
|
61
|
+
const options = [...choiceOptions()];
|
|
62
|
+
const value = type === "other" ? "other" : "none_of_the_above";
|
|
63
|
+
if (options.some((option) => typeof option === "object" && option !== null && "value" in option && option.value === value)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
options.push({
|
|
67
|
+
label: type === "other" ? t("builder.optionOther") : t("builder.optionNone"),
|
|
68
|
+
value
|
|
69
|
+
});
|
|
70
|
+
field.value.options = options;
|
|
71
|
+
}
|
|
72
|
+
function specialChoiceOptions() {
|
|
73
|
+
const options = choiceOptions();
|
|
74
|
+
const otherOption = options.find((option) => {
|
|
75
|
+
if (typeof option === "object" && option !== null && "value" in option) {
|
|
76
|
+
return option.value === specialChoiceValue("other");
|
|
77
|
+
}
|
|
78
|
+
return option === specialChoiceValue("other");
|
|
79
|
+
});
|
|
80
|
+
const noneOption = options.find((option) => {
|
|
81
|
+
if (typeof option === "object" && option !== null && "value" in option) {
|
|
82
|
+
return option.value === specialChoiceValue("none");
|
|
83
|
+
}
|
|
84
|
+
return option === specialChoiceValue("none");
|
|
85
|
+
});
|
|
86
|
+
return [
|
|
87
|
+
...otherOption === void 0 ? [] : [{ type: "other", option: otherOption }],
|
|
88
|
+
...noneOption === void 0 ? [] : [{ type: "none", option: noneOption }]
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
function specialChoiceValue(type) {
|
|
92
|
+
return type === "other" ? "other" : "none_of_the_above";
|
|
93
|
+
}
|
|
94
|
+
function hasSpecialChoiceOption(type) {
|
|
95
|
+
return specialChoiceOptions().some((option) => option.type === type);
|
|
96
|
+
}
|
|
97
|
+
function removeSpecialChoiceOption(type) {
|
|
98
|
+
const value = type === "other" ? "other" : "none_of_the_above";
|
|
99
|
+
const options = choiceOptions().filter((option) => {
|
|
100
|
+
if (typeof option === "object" && option !== null && "value" in option) {
|
|
101
|
+
return option.value !== value;
|
|
102
|
+
}
|
|
103
|
+
return option !== value;
|
|
104
|
+
});
|
|
105
|
+
if (options.length === choiceOptions().length) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
field.value.options = options;
|
|
109
|
+
}
|
|
110
|
+
function userChoiceOptions() {
|
|
111
|
+
return choiceOptions().filter((option) => !specialChoiceOptions().some((candidate) => candidate.option === option));
|
|
112
|
+
}
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<template>
|
|
116
|
+
<UFormField :label="t('builder.optionsLabel')">
|
|
117
|
+
<div class="grid gap-3">
|
|
118
|
+
<div
|
|
119
|
+
v-for="(option, optionIndex) in userChoiceOptions()"
|
|
120
|
+
:key="optionIndex"
|
|
121
|
+
class="flex flex-row items-center gap-2"
|
|
122
|
+
>
|
|
123
|
+
<UInput
|
|
124
|
+
:model-value="optionLabel(option)"
|
|
125
|
+
:disabled="readonly"
|
|
126
|
+
:placeholder="t('builder.optionPlaceholder', { index: optionIndex + 1 })"
|
|
127
|
+
:ui="{
|
|
128
|
+
root: 'w-full'
|
|
129
|
+
}"
|
|
130
|
+
@update:model-value="(value) => setOptionLabel(optionIndex, value)"
|
|
131
|
+
/>
|
|
132
|
+
<UButton
|
|
133
|
+
color="neutral"
|
|
134
|
+
variant="ghost"
|
|
135
|
+
icon="i-lucide-x"
|
|
136
|
+
:disabled="readonly || (field.options?.length ?? 0) <= minimumChoiceOptionCount()"
|
|
137
|
+
@click.stop="removeChoiceOption(optionIndex)"
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
<div
|
|
141
|
+
v-for="specialOption in specialChoiceOptions()"
|
|
142
|
+
:key="specialOption.type"
|
|
143
|
+
class="flex flex-row items-center gap-2"
|
|
144
|
+
>
|
|
145
|
+
<UInput
|
|
146
|
+
:model-value="optionLabel(specialOption.option)"
|
|
147
|
+
disabled
|
|
148
|
+
:placeholder="specialOption.type === 'other' ? t('builder.optionOther') : t('builder.optionNone')"
|
|
149
|
+
:ui="{
|
|
150
|
+
root: 'w-full'
|
|
151
|
+
}"
|
|
152
|
+
/>
|
|
153
|
+
<UButton
|
|
154
|
+
color="neutral"
|
|
155
|
+
variant="ghost"
|
|
156
|
+
icon="i-lucide-x"
|
|
157
|
+
:disabled="readonly"
|
|
158
|
+
@click.stop="removeSpecialChoiceOption(specialOption.type)"
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
163
|
+
<UButton
|
|
164
|
+
color="neutral"
|
|
165
|
+
variant="soft"
|
|
166
|
+
icon="i-lucide-plus"
|
|
167
|
+
:disabled="readonly"
|
|
168
|
+
@click.stop="addChoiceOption"
|
|
169
|
+
>
|
|
170
|
+
{{ t("builder.addOption") }}
|
|
171
|
+
</UButton>
|
|
172
|
+
<UButton
|
|
173
|
+
v-if="!hasSpecialChoiceOption('other')"
|
|
174
|
+
color="neutral"
|
|
175
|
+
variant="soft"
|
|
176
|
+
icon="i-lucide-plus"
|
|
177
|
+
:disabled="readonly"
|
|
178
|
+
@click.stop="addSpecialChoiceOption('other')"
|
|
179
|
+
>
|
|
180
|
+
{{ t("builder.addOtherOption") }}
|
|
181
|
+
</UButton>
|
|
182
|
+
<UButton
|
|
183
|
+
v-if="!hasSpecialChoiceOption('none')"
|
|
184
|
+
color="neutral"
|
|
185
|
+
variant="soft"
|
|
186
|
+
icon="i-lucide-plus"
|
|
187
|
+
:disabled="readonly"
|
|
188
|
+
@click.stop="addSpecialChoiceOption('none')"
|
|
189
|
+
>
|
|
190
|
+
{{ t("builder.addNoneOption") }}
|
|
191
|
+
</UButton>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</UFormField>
|
|
195
|
+
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FormForgeFieldSchema } from '../../../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
field: FormForgeFieldSchema;
|
|
4
|
+
readonly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
7
|
+
readonly: boolean;
|
|
8
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=FormForgeBuilderChoiceOptionsField.vue.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
modelValue?: string;
|
|
3
|
+
readonly?: boolean;
|
|
4
|
+
}
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
7
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
8
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
9
|
+
}>, {
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=FormForgeBuilderDescriptionField.vue.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "#imports";
|
|
3
|
+
import { useFormForgeI18n } from "../../../composables/useFormForgeI18n";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
modelValue: { type: String, required: false },
|
|
6
|
+
readonly: { type: Boolean, required: false, default: false }
|
|
7
|
+
});
|
|
8
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
9
|
+
const { t } = useFormForgeI18n();
|
|
10
|
+
const hasDescription = computed(() => typeof props.modelValue === "string");
|
|
11
|
+
const description = computed({
|
|
12
|
+
get: () => props.modelValue,
|
|
13
|
+
set: (value) => {
|
|
14
|
+
emit("update:modelValue", value);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
function enableDescription() {
|
|
18
|
+
emit("update:modelValue", "");
|
|
19
|
+
}
|
|
20
|
+
function disableDescription() {
|
|
21
|
+
emit("update:modelValue", void 0);
|
|
22
|
+
}
|
|
23
|
+
function blockEnterKeydown(_editor, event) {
|
|
24
|
+
if (event.key === "Enter") {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="grid gap-3">
|
|
34
|
+
<template v-if="hasDescription">
|
|
35
|
+
<UFormField :label="t('builder.categoryModal.descriptionLabel')">
|
|
36
|
+
<template #hint>
|
|
37
|
+
<div class="flex justify-end">
|
|
38
|
+
<UButton
|
|
39
|
+
color="neutral"
|
|
40
|
+
variant="ghost"
|
|
41
|
+
icon="i-lucide-x"
|
|
42
|
+
:disabled="readonly"
|
|
43
|
+
@click.stop="disableDescription"
|
|
44
|
+
>
|
|
45
|
+
{{ t("builder.removeDescription") }}
|
|
46
|
+
</UButton>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<div class="rounded-lg bg-white p-2 shadow-sm ring-1 ring-gray-900/5 dark:bg-gray-800 dark:ring-white/10">
|
|
51
|
+
<UEditor
|
|
52
|
+
v-slot="{ editor }"
|
|
53
|
+
v-model="description"
|
|
54
|
+
content-type="html"
|
|
55
|
+
:placeholder="t('builder.descriptionPlaceholder')"
|
|
56
|
+
:disabled="readonly"
|
|
57
|
+
class="rounded-xl"
|
|
58
|
+
:editor-props="{
|
|
59
|
+
handleKeyDown: blockEnterKeydown
|
|
60
|
+
}"
|
|
61
|
+
>
|
|
62
|
+
<UEditorToolbar
|
|
63
|
+
v-if="!readonly"
|
|
64
|
+
:editor="editor"
|
|
65
|
+
:items="[
|
|
66
|
+
{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' },
|
|
67
|
+
{ kind: 'mark', mark: 'italic', icon: 'i-lucide-italic' },
|
|
68
|
+
{ kind: 'mark', mark: 'underline', icon: 'i-lucide-underline' },
|
|
69
|
+
{ kind: 'link', icon: 'i-lucide-link' }
|
|
70
|
+
]"
|
|
71
|
+
/>
|
|
72
|
+
</UEditor>
|
|
73
|
+
</div>
|
|
74
|
+
</UFormField>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<UButton
|
|
78
|
+
v-else
|
|
79
|
+
color="neutral"
|
|
80
|
+
variant="soft"
|
|
81
|
+
icon="i-lucide-plus"
|
|
82
|
+
:disabled="readonly"
|
|
83
|
+
:ui="{
|
|
84
|
+
base: 'w-fit'
|
|
85
|
+
}"
|
|
86
|
+
@click.stop="enableDescription"
|
|
87
|
+
>
|
|
88
|
+
{{ t("builder.descriptionButton") }}
|
|
89
|
+
</UButton>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
modelValue?: string;
|
|
3
|
+
readonly?: boolean;
|
|
4
|
+
}
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
7
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
8
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
9
|
+
}>, {
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=FormForgeBuilderDescriptionField.vue.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FormForgePageSchema } from '../../../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
page: FormForgePageSchema;
|
|
4
|
+
pages: FormForgePageSchema[];
|
|
5
|
+
pageIndex: number;
|
|
6
|
+
readonly?: boolean;
|
|
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
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=FormForgeBuilderLogicPanel.vue.d.ts.map
|