@evanschleret/formforgeclient 1.2.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +10 -0
  2. package/dist/module.cjs +1 -0
  3. package/dist/module.d.cts +1 -0
  4. package/dist/module.d.mts +1 -0
  5. package/dist/module.d.ts +1 -0
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +1 -0
  8. package/dist/runtime/api/client.js +4 -2
  9. package/dist/runtime/api/request.d.ts +1 -0
  10. package/dist/runtime/api/schema.js +4 -4
  11. package/dist/runtime/assets/formforge.css +1 -0
  12. package/dist/runtime/composables/index.d.ts +1 -1
  13. package/dist/runtime/composables/useFormForgeBuilder.d.ts +24 -2
  14. package/dist/runtime/composables/useFormForgeBuilder.js +299 -43
  15. package/dist/runtime/composables/useFormForgeForm.js +15 -5
  16. package/dist/runtime/composables/useFormForgeI18n.d.ts +245 -19
  17. package/dist/runtime/composables/useFormForgeI18n.js +245 -19
  18. package/dist/runtime/composables/useFormForgeSubmit.js +31 -9
  19. package/dist/runtime/index.d.ts +1 -0
  20. package/dist/runtime/renderers/default/FormForgeBuilder.d.vue.ts +21 -2
  21. package/dist/runtime/renderers/default/FormForgeBuilder.vue +689 -738
  22. package/dist/runtime/renderers/default/FormForgeBuilder.vue.d.ts +21 -2
  23. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.d.vue.ts +17 -0
  24. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue +32 -0
  25. package/dist/runtime/renderers/default/FormForgeBuilderBlockSettingsModal.vue.d.ts +17 -0
  26. package/dist/runtime/renderers/default/FormForgeRenderer.d.vue.ts +3 -4
  27. package/dist/runtime/renderers/default/FormForgeRenderer.vue +344 -294
  28. package/dist/runtime/renderers/default/FormForgeRenderer.vue.d.ts +3 -4
  29. package/dist/runtime/renderers/default/FormForgeRendererField.d.vue.ts +22 -0
  30. package/dist/runtime/renderers/default/FormForgeRendererField.vue +237 -0
  31. package/dist/runtime/renderers/default/FormForgeRendererField.vue.d.ts +22 -0
  32. package/dist/runtime/renderers/default/FormForgeRendererPage.d.vue.ts +18 -0
  33. package/dist/runtime/renderers/default/FormForgeRendererPage.vue +31 -0
  34. package/dist/runtime/renderers/default/FormForgeRendererPage.vue.d.ts +18 -0
  35. package/dist/runtime/renderers/default/FormForgeResponse.vue +4 -3
  36. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.d.vue.ts +11 -0
  37. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue +118 -0
  38. package/dist/runtime/renderers/default/builder/FormForgeBuilderAddressFieldsCard.vue.d.ts +11 -0
  39. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.d.vue.ts +46 -0
  40. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue +205 -0
  41. package/dist/runtime/renderers/default/builder/FormForgeBuilderBlockCard.vue.d.ts +46 -0
  42. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.d.vue.ts +11 -0
  43. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue +37 -0
  44. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceDisplayField.vue.d.ts +11 -0
  45. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.d.vue.ts +11 -0
  46. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue +195 -0
  47. package/dist/runtime/renderers/default/builder/FormForgeBuilderChoiceOptionsField.vue.d.ts +11 -0
  48. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.d.vue.ts +14 -0
  49. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue +91 -0
  50. package/dist/runtime/renderers/default/builder/FormForgeBuilderDescriptionField.vue.d.ts +14 -0
  51. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.d.vue.ts +13 -0
  52. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue +387 -0
  53. package/dist/runtime/renderers/default/builder/FormForgeBuilderLogicPanel.vue.d.ts +13 -0
  54. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.d.vue.ts +44 -0
  55. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue +328 -0
  56. package/dist/runtime/renderers/default/builder/FormForgeBuilderQuestionRow.vue.d.ts +44 -0
  57. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.d.vue.ts +11 -0
  58. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue +47 -0
  59. package/dist/runtime/renderers/default/builder/FormForgeBuilderTemporalModeField.vue.d.ts +11 -0
  60. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.d.vue.ts +14 -0
  61. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue +595 -0
  62. package/dist/runtime/renderers/default/builder/FormForgeBuilderValidationRulesSection.vue.d.ts +14 -0
  63. package/dist/runtime/renderers/default/builder/builderFieldHelpers.d.ts +3 -0
  64. package/dist/runtime/renderers/default/builder/builderFieldHelpers.js +4 -0
  65. package/dist/runtime/types/index.d.ts +1 -1
  66. package/dist/runtime/types/management.d.ts +12 -0
  67. package/dist/runtime/types/schema.d.ts +72 -4
  68. package/dist/runtime/utils/defaults.d.ts +7 -0
  69. package/dist/runtime/utils/defaults.js +86 -0
  70. package/dist/runtime/utils/page-logic.d.ts +24 -0
  71. package/dist/runtime/utils/page-logic.js +351 -0
  72. package/dist/runtime/utils/rich-text.d.ts +3 -0
  73. package/dist/runtime/utils/rich-text.js +72 -0
  74. package/dist/runtime/utils/schema.d.ts +1 -1
  75. package/dist/runtime/utils/schema.js +70 -16
  76. package/dist/runtime/utils/temporal.d.ts +10 -0
  77. package/dist/runtime/utils/temporal.js +28 -0
  78. package/dist/runtime/utils/validation.d.ts +5 -0
  79. package/dist/runtime/utils/validation.js +36 -0
  80. package/dist/runtime/validation/zod.d.ts +5 -2
  81. package/dist/runtime/validation/zod.js +563 -54
  82. package/dist/types.d.mts +2 -0
  83. package/package.json +18 -14
@@ -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