@shwfed/nuxt 0.11.47 → 0.11.48
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/dist/module.json +1 -1
- package/dist/runtime/components/fields.d.vue.ts +6 -850
- package/dist/runtime/components/fields.vue +2 -0
- package/dist/runtime/components/fields.vue.d.ts +6 -850
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +10 -1698
- package/dist/runtime/components/ui/fields/Fields.vue +162 -627
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +10 -1698
- package/dist/runtime/components/ui/fields/FieldsBody.d.vue.ts +17 -0
- package/dist/runtime/components/ui/fields/FieldsBody.vue +720 -0
- package/dist/runtime/components/ui/fields/FieldsBody.vue.d.ts +17 -0
- package/dist/runtime/components/ui/fields/render-context.d.ts +120 -0
- package/dist/runtime/components/ui/fields/render-context.js +0 -0
- package/dist/runtime/components/ui/fields/schema.d.ts +134 -5606
- package/dist/runtime/components/ui/fields/schema.js +80 -83
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +5 -849
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +618 -224
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +5 -849
- package/package.json +1 -1
|
@@ -6,21 +6,15 @@ import { CalendarDate, getLocalTimeZone } from "@internationalized/date";
|
|
|
6
6
|
import { Icon } from "@iconify/vue";
|
|
7
7
|
import { Effect } from "effect";
|
|
8
8
|
import { format, parse } from "date-fns";
|
|
9
|
-
import { deleteProperty, getProperty,
|
|
9
|
+
import { deleteProperty, getProperty, setProperty } from "dot-prop";
|
|
10
10
|
import { computed, nextTick, readonly, ref, toRaw, useId, watch, watchEffect } from "vue";
|
|
11
11
|
import { useI18n } from "vue-i18n";
|
|
12
12
|
import { useCheating } from "#imports";
|
|
13
|
-
import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from "reka-ui";
|
|
14
13
|
import { mergeDslContexts, useCELContext } from "../../../plugins/cel/context";
|
|
15
|
-
import { Calendar } from "../calendar";
|
|
16
14
|
import { Button } from "../button";
|
|
17
|
-
import
|
|
18
|
-
import { Field, FieldContent, FieldError, FieldLabel, FieldSet } from "../field";
|
|
15
|
+
import FieldsBody from "./FieldsBody.vue";
|
|
19
16
|
import FieldsConfiguratorDialog from "../fields-configurator/FieldsConfiguratorDialog.vue";
|
|
20
|
-
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupNumberField, InputGroupTextarea } from "../input-group";
|
|
21
|
-
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../popover";
|
|
22
17
|
import { Skeleton } from "../skeleton";
|
|
23
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip";
|
|
24
18
|
import { getLocalizedText } from "../../../utils/coders";
|
|
25
19
|
import { FieldsConfigC, createFieldsConfig } from "./schema";
|
|
26
20
|
const id = useId();
|
|
@@ -31,7 +25,7 @@ const defaultConfig = createFieldsConfig({
|
|
|
31
25
|
const props = defineProps({
|
|
32
26
|
config: { type: null, required: true }
|
|
33
27
|
});
|
|
34
|
-
defineSlots();
|
|
28
|
+
const slots = defineSlots();
|
|
35
29
|
const emit = defineEmits(["update:config", "initial-value-ready"]);
|
|
36
30
|
const config = computedAsync(async () => FieldsConfigC.parse(await props.config.pipe(Effect.runPromise) ?? defaultConfig));
|
|
37
31
|
const { t, locale } = useI18n();
|
|
@@ -50,35 +44,23 @@ const isReady = ref(false);
|
|
|
50
44
|
const hasInitializedFieldValues = ref(false);
|
|
51
45
|
const hasEmittedInitialValueReady = ref(false);
|
|
52
46
|
const readyResolvers = [];
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
compatibilityDate: config2.compatibilityDate,
|
|
57
|
-
fields: config2.fields.slice(),
|
|
58
|
-
groups: config2.groups.map((group) => ({
|
|
59
|
-
...group,
|
|
60
|
-
fields: group.fields.slice()
|
|
61
|
-
}))
|
|
62
|
-
};
|
|
63
|
-
if (config2.orientation) {
|
|
64
|
-
nextConfig.orientation = config2.orientation;
|
|
65
|
-
}
|
|
66
|
-
if (config2.bordered) {
|
|
67
|
-
nextConfig.bordered = config2.bordered;
|
|
47
|
+
function requireSlotProps(slotProps) {
|
|
48
|
+
if (!slotProps) {
|
|
49
|
+
throw new TypeError("missing slot props");
|
|
68
50
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
51
|
+
return slotProps;
|
|
52
|
+
}
|
|
53
|
+
function cloneConfig(config2) {
|
|
54
|
+
return structuredClone(toRaw(config2));
|
|
73
55
|
}
|
|
74
|
-
function
|
|
75
|
-
return
|
|
56
|
+
function getBodyOrientation(body) {
|
|
57
|
+
return body.orientation ?? "horizontal";
|
|
76
58
|
}
|
|
77
|
-
function usesContentsOrientation(
|
|
78
|
-
return
|
|
59
|
+
function usesContentsOrientation(body) {
|
|
60
|
+
return getBodyOrientation(body) === "contents";
|
|
79
61
|
}
|
|
80
|
-
function
|
|
81
|
-
return usesContentsOrientation(
|
|
62
|
+
function isBodyBordered(body) {
|
|
63
|
+
return usesContentsOrientation(body) && body.bordered === true;
|
|
82
64
|
}
|
|
83
65
|
function tryEvaluateExpression(source, context) {
|
|
84
66
|
try {
|
|
@@ -99,12 +81,12 @@ function evaluateExpression(source, context, fallback) {
|
|
|
99
81
|
}
|
|
100
82
|
return result.value;
|
|
101
83
|
}
|
|
102
|
-
function
|
|
84
|
+
function getBodyStyle(body) {
|
|
103
85
|
const style = {};
|
|
104
|
-
if (!
|
|
86
|
+
if (!body.style) {
|
|
105
87
|
return style;
|
|
106
88
|
}
|
|
107
|
-
const styleMap = evaluateExpression(
|
|
89
|
+
const styleMap = evaluateExpression(body.style, { form: modelValue.value }, {});
|
|
108
90
|
if (!styleMap || typeof styleMap !== "object" || Array.isArray(styleMap)) {
|
|
109
91
|
return style;
|
|
110
92
|
}
|
|
@@ -134,8 +116,8 @@ function getFieldPartStyle(styleExpression) {
|
|
|
134
116
|
function getFieldStyle(field) {
|
|
135
117
|
return getFieldPartStyle(field.style);
|
|
136
118
|
}
|
|
137
|
-
function getGroupStyle(group,
|
|
138
|
-
const style = usesContentsOrientation(
|
|
119
|
+
function getGroupStyle(group, body) {
|
|
120
|
+
const style = usesContentsOrientation(body) ? getBodyStyle(body) : {};
|
|
139
121
|
const groupStyle = group.style ? evaluateExpression(group.style, { form: modelValue.value, id: group.id }, {}) : {};
|
|
140
122
|
if (!groupStyle || typeof groupStyle !== "object" || Array.isArray(groupStyle)) {
|
|
141
123
|
return style;
|
|
@@ -147,42 +129,57 @@ function getGroupStyle(group, config2) {
|
|
|
147
129
|
}
|
|
148
130
|
return style;
|
|
149
131
|
}
|
|
150
|
-
function
|
|
132
|
+
function getBodyEntries(body) {
|
|
151
133
|
return [
|
|
152
|
-
...
|
|
134
|
+
...body.fields.map((field) => ({
|
|
153
135
|
key: `field:${field.id}`,
|
|
154
136
|
field
|
|
155
137
|
})),
|
|
156
|
-
...
|
|
138
|
+
...(body.groups ?? []).map((group) => ({
|
|
157
139
|
key: `group:${group.id}`,
|
|
158
140
|
group
|
|
159
141
|
}))
|
|
160
142
|
];
|
|
161
143
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
144
|
+
function visitFields(fields, visitor, visible) {
|
|
145
|
+
for (const field of fields) {
|
|
146
|
+
const fieldVisible = field.type === "empty" ? visible : visible && !isFieldHidden(field);
|
|
147
|
+
visitor(field, fieldVisible);
|
|
148
|
+
if (field.type === "container") {
|
|
149
|
+
visitFields(field.fields, visitor, fieldVisible);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function visitBodyFields(body, visitor) {
|
|
154
|
+
visitFields(body.fields, visitor, true);
|
|
155
|
+
for (const group of body.groups ?? []) {
|
|
156
|
+
visitFields(group.fields, visitor, true);
|
|
157
|
+
}
|
|
164
158
|
}
|
|
165
|
-
function getFieldContainerStyle(field,
|
|
166
|
-
if (
|
|
159
|
+
function getFieldContainerStyle(field, body) {
|
|
160
|
+
if (field.type === "slot" || field.type === "container") {
|
|
161
|
+
return {};
|
|
162
|
+
}
|
|
163
|
+
if (!isPassiveField(field) && usesContentsOrientation(body)) {
|
|
167
164
|
return {};
|
|
168
165
|
}
|
|
169
166
|
return getFieldStyle(field);
|
|
170
167
|
}
|
|
171
|
-
function getFieldLabelStyle(field,
|
|
172
|
-
if (!usesContentsOrientation(
|
|
168
|
+
function getFieldLabelStyle(field, body) {
|
|
169
|
+
if (!usesContentsOrientation(body)) {
|
|
173
170
|
return {};
|
|
174
171
|
}
|
|
175
172
|
return getFieldPartStyle(field.labelStyle);
|
|
176
173
|
}
|
|
177
|
-
function getFieldContentStyle(field,
|
|
178
|
-
const style = usesContentsOrientation(
|
|
179
|
-
if (usesContentsOrientation(
|
|
174
|
+
function getFieldContentStyle(field, body) {
|
|
175
|
+
const style = usesContentsOrientation(body) ? getFieldPartStyle(field.contentStyle) : {};
|
|
176
|
+
if (usesContentsOrientation(body) && isFieldLabelHidden(field) && style.gridColumn === void 0) {
|
|
180
177
|
style.gridColumn = "1 / -1";
|
|
181
178
|
}
|
|
182
179
|
return style;
|
|
183
180
|
}
|
|
184
|
-
function getMarkdownBodyContentStyle(field,
|
|
185
|
-
if (!usesContentsOrientation(
|
|
181
|
+
function getMarkdownBodyContentStyle(field, body) {
|
|
182
|
+
if (!usesContentsOrientation(body)) {
|
|
186
183
|
return {};
|
|
187
184
|
}
|
|
188
185
|
return {
|
|
@@ -193,11 +190,8 @@ function getMarkdownBodyContentStyle(field, config2) {
|
|
|
193
190
|
function isPassiveField(field) {
|
|
194
191
|
return field.type === "slot" || field.type === "empty";
|
|
195
192
|
}
|
|
196
|
-
function isMarkdownDisplayField(field) {
|
|
197
|
-
return field.type === "markdown" || field.type === "markdown-body";
|
|
198
|
-
}
|
|
199
193
|
function isInteractiveField(field) {
|
|
200
|
-
return
|
|
194
|
+
return field.type !== "slot" && field.type !== "empty" && field.type !== "markdown" && field.type !== "markdown-body" && field.type !== "container";
|
|
201
195
|
}
|
|
202
196
|
function isFieldLabelHidden(field) {
|
|
203
197
|
return field.hideLabel === true;
|
|
@@ -225,13 +219,13 @@ function getFieldValue(field) {
|
|
|
225
219
|
return getProperty(modelValue.value, field.path);
|
|
226
220
|
}
|
|
227
221
|
function initializeFieldValues(config2) {
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
|
|
222
|
+
visitBodyFields(config2, (field) => {
|
|
223
|
+
if (!isInteractiveField(field) || !field.initialValue) {
|
|
224
|
+
return;
|
|
231
225
|
}
|
|
232
226
|
const initialValueResult = tryEvaluateExpression(field.initialValue, { form: modelValue.value });
|
|
233
227
|
if (!initialValueResult.ok) {
|
|
234
|
-
|
|
228
|
+
return;
|
|
235
229
|
}
|
|
236
230
|
const initialValue = initialValueResult.value;
|
|
237
231
|
setProperty(
|
|
@@ -239,7 +233,7 @@ function initializeFieldValues(config2) {
|
|
|
239
233
|
field.path,
|
|
240
234
|
field.type === "number" && typeof initialValue === "bigint" ? Number(initialValue) : initialValue
|
|
241
235
|
);
|
|
242
|
-
}
|
|
236
|
+
});
|
|
243
237
|
}
|
|
244
238
|
const MIME_LABELS = {
|
|
245
239
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel",
|
|
@@ -544,15 +538,15 @@ function validateField(field) {
|
|
|
544
538
|
}
|
|
545
539
|
function validateFields() {
|
|
546
540
|
const nextValidationErrors = {};
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
541
|
+
visitBodyFields(displayConfig.value, (field, visible) => {
|
|
542
|
+
if (!visible || !isInteractiveField(field)) {
|
|
543
|
+
return;
|
|
550
544
|
}
|
|
551
545
|
const failure = getValidationFailure(field);
|
|
552
546
|
if (failure) {
|
|
553
547
|
nextValidationErrors[field.path] = failure;
|
|
554
548
|
}
|
|
555
|
-
}
|
|
549
|
+
});
|
|
556
550
|
validationErrors.value = nextValidationErrors;
|
|
557
551
|
return Object.keys(nextValidationErrors).length === 0;
|
|
558
552
|
}
|
|
@@ -563,7 +557,10 @@ function getFieldLabel(field) {
|
|
|
563
557
|
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
564
558
|
}
|
|
565
559
|
function getDisplayFieldLabel(field) {
|
|
566
|
-
|
|
560
|
+
if ("title" in field && field.title) {
|
|
561
|
+
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
562
|
+
}
|
|
563
|
+
return field.id;
|
|
567
564
|
}
|
|
568
565
|
function isFieldRequired(field) {
|
|
569
566
|
return field.required === true;
|
|
@@ -615,6 +612,30 @@ function handleCalendarBlur(field) {
|
|
|
615
612
|
}
|
|
616
613
|
}, 0);
|
|
617
614
|
}
|
|
615
|
+
function getFieldMaxLength(field) {
|
|
616
|
+
if (!field.maxLength) {
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
return evaluateExpression(field.maxLength, void 0, void 0);
|
|
620
|
+
}
|
|
621
|
+
function getNumberFieldMin(field) {
|
|
622
|
+
if (!field.min) {
|
|
623
|
+
return void 0;
|
|
624
|
+
}
|
|
625
|
+
return evaluateExpression(field.min, void 0, void 0);
|
|
626
|
+
}
|
|
627
|
+
function getNumberFieldMax(field) {
|
|
628
|
+
if (!field.max) {
|
|
629
|
+
return void 0;
|
|
630
|
+
}
|
|
631
|
+
return evaluateExpression(field.max, void 0, void 0);
|
|
632
|
+
}
|
|
633
|
+
function getNumberFieldStep(field) {
|
|
634
|
+
if (!field.step) {
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
return evaluateExpression(field.step, void 0, void 0);
|
|
638
|
+
}
|
|
618
639
|
function handleConfiguratorConfirm(nextConfig) {
|
|
619
640
|
displayConfig.value = cloneConfig(nextConfig);
|
|
620
641
|
emit("update:config", nextConfig);
|
|
@@ -646,11 +667,11 @@ watch(config, (value) => {
|
|
|
646
667
|
}, { immediate: true });
|
|
647
668
|
watchEffect(() => {
|
|
648
669
|
const activePaths = /* @__PURE__ */ new Set();
|
|
649
|
-
|
|
650
|
-
if (
|
|
670
|
+
visitBodyFields(displayConfig.value, (field, visible) => {
|
|
671
|
+
if (visible && isInteractiveField(field) && !isFieldDisabled(field)) {
|
|
651
672
|
activePaths.add(field.path);
|
|
652
673
|
}
|
|
653
|
-
}
|
|
674
|
+
});
|
|
654
675
|
for (const path of Object.keys(validationErrors.value)) {
|
|
655
676
|
if (!activePaths.has(path)) {
|
|
656
677
|
clearFieldValidation(path);
|
|
@@ -667,11 +688,71 @@ watchEffect(() => {
|
|
|
667
688
|
}
|
|
668
689
|
}
|
|
669
690
|
});
|
|
691
|
+
const renderContext = computed(() => ({
|
|
692
|
+
id,
|
|
693
|
+
isCheating: isCheating.value,
|
|
694
|
+
modelValue: modelValue.value,
|
|
695
|
+
slotForm: slotForm.value,
|
|
696
|
+
valid,
|
|
697
|
+
calendarOpen: calendarOpen.value,
|
|
698
|
+
selectOpen: selectOpen.value,
|
|
699
|
+
templateDownloading: templateDownloading.value,
|
|
700
|
+
getBodyEntries,
|
|
701
|
+
getBodyOrientation,
|
|
702
|
+
isBodyBordered,
|
|
703
|
+
getBodyStyle,
|
|
704
|
+
getGroupStyle,
|
|
705
|
+
getFieldStyle,
|
|
706
|
+
getFieldContainerStyle,
|
|
707
|
+
getFieldLabelStyle,
|
|
708
|
+
getFieldContentStyle,
|
|
709
|
+
getMarkdownBodyContentStyle,
|
|
710
|
+
isInteractiveField,
|
|
711
|
+
isFieldLabelHidden,
|
|
712
|
+
isFieldHidden,
|
|
713
|
+
isFieldDisabled,
|
|
714
|
+
isFieldInvalid,
|
|
715
|
+
getFieldLabel,
|
|
716
|
+
getDisplayFieldLabel,
|
|
717
|
+
isFieldRequired,
|
|
718
|
+
renderValidationMessage,
|
|
719
|
+
renderMarkdownField,
|
|
720
|
+
renderMarkdownBodyField,
|
|
721
|
+
toCalendarDateValue,
|
|
722
|
+
displayCalendarValue,
|
|
723
|
+
isCalendarDateDisabled,
|
|
724
|
+
handleCalendarOpenChange,
|
|
725
|
+
handleCalendarBlur,
|
|
726
|
+
validateField,
|
|
727
|
+
getFieldMaxLength,
|
|
728
|
+
getNumberFieldMin,
|
|
729
|
+
getNumberFieldMax,
|
|
730
|
+
getNumberFieldStep,
|
|
731
|
+
getSelectFieldState,
|
|
732
|
+
getSelectDisplayValue,
|
|
733
|
+
handleSelectValueChange,
|
|
734
|
+
handleSelectOpenChange,
|
|
735
|
+
handleSelectBlur,
|
|
736
|
+
handleSelectCommandValueChange,
|
|
737
|
+
clearSelectField,
|
|
738
|
+
getUploadTemplateIcon,
|
|
739
|
+
getUploadAcceptString,
|
|
740
|
+
getUploadDescription,
|
|
741
|
+
getUploadMaxCount,
|
|
742
|
+
getUploadFiles,
|
|
743
|
+
handleUploadInputChange,
|
|
744
|
+
handleUploadDrop,
|
|
745
|
+
handleUploadDragOver,
|
|
746
|
+
handleTemplateDownload,
|
|
747
|
+
getFileIcon,
|
|
748
|
+
removeUploadFile
|
|
749
|
+
}));
|
|
670
750
|
</script>
|
|
671
751
|
|
|
672
752
|
<script>
|
|
673
753
|
export {
|
|
674
754
|
CalendarFieldC,
|
|
755
|
+
ContainerFieldC,
|
|
675
756
|
CURRENT_COMPATIBILITY_DATE,
|
|
676
757
|
EmptyFieldC,
|
|
677
758
|
FieldC,
|
|
@@ -688,6 +769,7 @@ export {
|
|
|
688
769
|
SlotFieldC,
|
|
689
770
|
SUPPORTED_COMPATIBILITY_DATES,
|
|
690
771
|
StringFieldC,
|
|
772
|
+
TextareaFieldC,
|
|
691
773
|
UploadFieldC,
|
|
692
774
|
ValidationRuleC,
|
|
693
775
|
createFieldsConfig,
|
|
@@ -700,9 +782,9 @@ export {
|
|
|
700
782
|
:class="[
|
|
701
783
|
'relative p-1 -m-1 border border-dashed',
|
|
702
784
|
isCheating ? 'border-(--primary)/20 rounded hover:border-(--primary)/40 transition-colors duration-150 group cursor-pointer' : 'border-transparent',
|
|
703
|
-
|
|
785
|
+
isBodyBordered(displayConfig) ? '!p-0 !m-0 border-transparent' : ''
|
|
704
786
|
]"
|
|
705
|
-
:style="
|
|
787
|
+
:style="getBodyStyle(displayConfig)"
|
|
706
788
|
>
|
|
707
789
|
<Button
|
|
708
790
|
v-if="isCheating"
|
|
@@ -728,567 +810,20 @@ export {
|
|
|
728
810
|
class="absolute inset-0 z-10 w-full h-full"
|
|
729
811
|
/>
|
|
730
812
|
|
|
731
|
-
<
|
|
732
|
-
:
|
|
733
|
-
|
|
734
|
-
:key="entry.key"
|
|
735
|
-
:data-slot="entry.group ? 'fields-group' : 'fields-root-entry'"
|
|
736
|
-
:data-group-id="entry.group?.id"
|
|
737
|
-
:class="entry.group ? [
|
|
738
|
-
isConfigBordered(displayConfig) ? 'overflow-hidden border border-zinc-200 [[data-slot=fields-group]+&]:border-t-0 [&>[data-slot=field]:last-child>[data-slot=field-label]]:border-b-0 [&>[data-slot=field]:last-child>[data-slot=field-content]]:border-r-0 [&>[data-slot=field]:last-child>[data-slot=field-content]]:border-b-0 [&>div:last-child]:border-r-0 [&>div:last-child]:border-b-0' : ''
|
|
739
|
-
] : void 0"
|
|
740
|
-
:style="entry.group ? getGroupStyle(entry.group, displayConfig) : { display: 'contents' }"
|
|
813
|
+
<FieldsBody
|
|
814
|
+
:body="displayConfig"
|
|
815
|
+
:renderer="renderContext"
|
|
741
816
|
>
|
|
742
817
|
<template
|
|
743
|
-
v-for="
|
|
744
|
-
|
|
818
|
+
v-for="(_, slotName) in slots"
|
|
819
|
+
#[slotName]="slotProps"
|
|
745
820
|
>
|
|
746
|
-
<div
|
|
747
|
-
v-if="field.type === 'slot' && isConfigBordered(displayConfig)"
|
|
748
|
-
:class="'border-b border-r border-zinc-200'"
|
|
749
|
-
:style="getFieldStyle(field)"
|
|
750
|
-
>
|
|
751
|
-
<slot
|
|
752
|
-
:name="field.id"
|
|
753
|
-
:form="slotForm"
|
|
754
|
-
:style="{}"
|
|
755
|
-
:valid="valid"
|
|
756
|
-
/>
|
|
757
|
-
</div>
|
|
758
821
|
<slot
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
:form="slotForm"
|
|
762
|
-
:style="getFieldStyle(field)"
|
|
763
|
-
:valid="valid"
|
|
764
|
-
/>
|
|
765
|
-
<div
|
|
766
|
-
v-else-if="field.type === 'empty'"
|
|
767
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
768
|
-
:style="getFieldStyle(field)"
|
|
822
|
+
:name="slotName"
|
|
823
|
+
v-bind="requireSlotProps(slotProps)"
|
|
769
824
|
/>
|
|
770
|
-
<Field
|
|
771
|
-
v-else-if="field.type === 'markdown' && !isFieldHidden(field)"
|
|
772
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
773
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
774
|
-
>
|
|
775
|
-
<FieldLabel
|
|
776
|
-
v-if="!isFieldLabelHidden(field)"
|
|
777
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
778
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
779
|
-
>
|
|
780
|
-
<span>{{ getDisplayFieldLabel(field) }}</span>
|
|
781
|
-
</FieldLabel>
|
|
782
|
-
<FieldContent
|
|
783
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2 items-center justify-center text-center' : void 0"
|
|
784
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
785
|
-
>
|
|
786
|
-
<div
|
|
787
|
-
class="text-center"
|
|
788
|
-
data-slot="fields-markdown"
|
|
789
|
-
v-html="renderMarkdownField(field)"
|
|
790
|
-
/>
|
|
791
|
-
</FieldContent>
|
|
792
|
-
</Field>
|
|
793
|
-
<Field
|
|
794
|
-
v-else-if="field.type === 'markdown-body' && !isFieldHidden(field)"
|
|
795
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
796
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
797
|
-
>
|
|
798
|
-
<FieldContent
|
|
799
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
800
|
-
:style="getMarkdownBodyContentStyle(field, displayConfig)"
|
|
801
|
-
>
|
|
802
|
-
<div
|
|
803
|
-
data-slot="fields-markdown-body"
|
|
804
|
-
v-html="renderMarkdownBodyField(field)"
|
|
805
|
-
/>
|
|
806
|
-
</FieldContent>
|
|
807
|
-
</Field>
|
|
808
|
-
<Field
|
|
809
|
-
v-else-if="field.type === 'upload' && !isFieldHidden(field)"
|
|
810
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
811
|
-
:data-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
812
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
813
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
814
|
-
>
|
|
815
|
-
<FieldLabel
|
|
816
|
-
v-if="!isFieldLabelHidden(field)"
|
|
817
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
818
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
819
|
-
>
|
|
820
|
-
<span class="inline-flex items-start gap-0.5">
|
|
821
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
822
|
-
<sup
|
|
823
|
-
v-if="isFieldRequired(field)"
|
|
824
|
-
class="text-red-500 leading-none"
|
|
825
|
-
>*</sup>
|
|
826
|
-
</span>
|
|
827
|
-
<span v-if="isCheating">
|
|
828
|
-
<span class="font-mono">{{ field.path }}</span>
|
|
829
|
-
</span>
|
|
830
|
-
</FieldLabel>
|
|
831
|
-
<FieldContent
|
|
832
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
833
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
834
|
-
>
|
|
835
|
-
<div
|
|
836
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
837
|
-
class="flex flex-col gap-2"
|
|
838
|
-
>
|
|
839
|
-
<label
|
|
840
|
-
v-if="getUploadFiles(field).length < getUploadMaxCount(field)"
|
|
841
|
-
:class="[
|
|
842
|
-
'flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-4 py-8 transition-colors',
|
|
843
|
-
isFieldDisabled(field) ? 'cursor-not-allowed border-zinc-200 opacity-50' : 'border-zinc-300 hover:border-zinc-400 hover:bg-zinc-50'
|
|
844
|
-
]"
|
|
845
|
-
@drop="handleUploadDrop(field, $event)"
|
|
846
|
-
@dragover="handleUploadDragOver"
|
|
847
|
-
>
|
|
848
|
-
<Icon
|
|
849
|
-
:icon="field.icon ?? 'fluent:cloud-arrow-up-20-regular'"
|
|
850
|
-
class="text-4xl text-zinc-400"
|
|
851
|
-
/>
|
|
852
|
-
<button
|
|
853
|
-
v-if="field.template"
|
|
854
|
-
type="button"
|
|
855
|
-
class="inline-flex items-center gap-1.5 rounded-md border border-zinc-300 bg-white px-3 py-1.5 text-sm text-zinc-600 transition-colors hover:border-[--el-color-primary] hover:text-[--el-color-primary] disabled:opacity-50"
|
|
856
|
-
:disabled="templateDownloading[field.id]"
|
|
857
|
-
@click.prevent.stop="handleTemplateDownload(field)"
|
|
858
|
-
>
|
|
859
|
-
<Icon
|
|
860
|
-
:icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : getUploadTemplateIcon(field)"
|
|
861
|
-
class="text-base"
|
|
862
|
-
/>
|
|
863
|
-
{{ getLocalizedText(field.templateName, locale) ?? t("upload-download-template") }}
|
|
864
|
-
</button>
|
|
865
|
-
<p
|
|
866
|
-
v-if="field.description"
|
|
867
|
-
class="text-sm text-zinc-600"
|
|
868
|
-
v-html="$md.inline`${getLocalizedText(field.description, locale) ?? ''}`()"
|
|
869
|
-
/>
|
|
870
|
-
<p
|
|
871
|
-
v-else
|
|
872
|
-
class="text-sm text-zinc-600"
|
|
873
|
-
>
|
|
874
|
-
{{ t("upload-click-or-drag") }}
|
|
875
|
-
</p>
|
|
876
|
-
<p class="text-xs text-zinc-400">
|
|
877
|
-
{{ getUploadDescription(field) || t("upload-accept-all") }}
|
|
878
|
-
</p>
|
|
879
|
-
<input
|
|
880
|
-
type="file"
|
|
881
|
-
class="sr-only"
|
|
882
|
-
:accept="getUploadAcceptString(field)"
|
|
883
|
-
:disabled="isFieldDisabled(field)"
|
|
884
|
-
:multiple="getUploadMaxCount(field) !== 1"
|
|
885
|
-
@change="handleUploadInputChange(field, $event)"
|
|
886
|
-
>
|
|
887
|
-
</label>
|
|
888
|
-
<ul
|
|
889
|
-
v-if="getUploadFiles(field).length > 0"
|
|
890
|
-
class="flex flex-col gap-1"
|
|
891
|
-
>
|
|
892
|
-
<li
|
|
893
|
-
v-for="(file, fileIndex) in getUploadFiles(field)"
|
|
894
|
-
:key="`${field.id}:${fileIndex}:${file.name}`"
|
|
895
|
-
class="flex items-center gap-2 rounded-md border border-zinc-200 px-3 py-2"
|
|
896
|
-
>
|
|
897
|
-
<Icon
|
|
898
|
-
:icon="getFileIcon(file.name)"
|
|
899
|
-
class="shrink-0 text-lg"
|
|
900
|
-
/>
|
|
901
|
-
<span class="flex-1 truncate text-sm text-zinc-700">{{ file.name }}</span>
|
|
902
|
-
<button
|
|
903
|
-
type="button"
|
|
904
|
-
class="shrink-0 text-zinc-300 transition-colors hover:text-red-500"
|
|
905
|
-
:disabled="isFieldDisabled(field)"
|
|
906
|
-
@click="removeUploadFile(field, fileIndex)"
|
|
907
|
-
>
|
|
908
|
-
<Icon
|
|
909
|
-
icon="fluent:delete-20-regular"
|
|
910
|
-
class="text-lg"
|
|
911
|
-
/>
|
|
912
|
-
</button>
|
|
913
|
-
</li>
|
|
914
|
-
</ul>
|
|
915
|
-
</div>
|
|
916
|
-
<FieldError
|
|
917
|
-
v-if="isFieldInvalid(field)"
|
|
918
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
919
|
-
>
|
|
920
|
-
<span v-html="renderValidationMessage(field)" />
|
|
921
|
-
</FieldError>
|
|
922
|
-
</FieldContent>
|
|
923
|
-
</Field>
|
|
924
|
-
<Field
|
|
925
|
-
v-else-if="isInteractiveField(field) && !isFieldHidden(field)"
|
|
926
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
927
|
-
:data-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
928
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
929
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
930
|
-
>
|
|
931
|
-
<FieldLabel
|
|
932
|
-
v-if="!isFieldLabelHidden(field)"
|
|
933
|
-
:for="['string', 'textarea', 'number', 'select'].includes(field.type) ? `${id}:${field.path}` : void 0"
|
|
934
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
935
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
936
|
-
>
|
|
937
|
-
<span class="inline-flex items-start gap-0.5">
|
|
938
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
939
|
-
<sup
|
|
940
|
-
v-if="isFieldRequired(field)"
|
|
941
|
-
class="text-red-500 leading-none"
|
|
942
|
-
>*</sup>
|
|
943
|
-
</span>
|
|
944
|
-
<span v-if="isCheating">
|
|
945
|
-
<span class="font-mono">{{ field.path }}</span>
|
|
946
|
-
</span>
|
|
947
|
-
</FieldLabel>
|
|
948
|
-
<FieldContent
|
|
949
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
950
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
951
|
-
>
|
|
952
|
-
<Popover
|
|
953
|
-
v-if="field.type === 'calendar'"
|
|
954
|
-
@update:open="(open) => handleCalendarOpenChange(field, open)"
|
|
955
|
-
>
|
|
956
|
-
<PopoverAnchor as-child>
|
|
957
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
958
|
-
<PopoverTrigger as-child>
|
|
959
|
-
<InputGroupInput
|
|
960
|
-
:model-value="displayCalendarValue(getProperty(modelValue, field.path), field.display, field.value)"
|
|
961
|
-
class="text-left"
|
|
962
|
-
:disabled="isFieldDisabled(field)"
|
|
963
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
964
|
-
readonly
|
|
965
|
-
@blur="handleCalendarBlur(field)"
|
|
966
|
-
/>
|
|
967
|
-
</PopoverTrigger>
|
|
968
|
-
<InputGroupAddon v-if="field.icon">
|
|
969
|
-
<Icon
|
|
970
|
-
:icon="field.icon"
|
|
971
|
-
/>
|
|
972
|
-
</InputGroupAddon>
|
|
973
|
-
<InputGroupAddon
|
|
974
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
975
|
-
align="inline-end"
|
|
976
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
977
|
-
>
|
|
978
|
-
<Tooltip :delay-duration="800">
|
|
979
|
-
<TooltipTrigger>
|
|
980
|
-
<InputGroupButton as-child>
|
|
981
|
-
<button
|
|
982
|
-
type="button"
|
|
983
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
984
|
-
:disabled="isFieldDisabled(field)"
|
|
985
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
986
|
-
>
|
|
987
|
-
<Icon
|
|
988
|
-
icon="fluent:dismiss-20-regular"
|
|
989
|
-
/>
|
|
990
|
-
</button>
|
|
991
|
-
</InputGroupButton>
|
|
992
|
-
</TooltipTrigger>
|
|
993
|
-
<TooltipContent>
|
|
994
|
-
{{ t("clear") }}
|
|
995
|
-
</TooltipContent>
|
|
996
|
-
</Tooltip>
|
|
997
|
-
</InputGroupAddon>
|
|
998
|
-
</InputGroup>
|
|
999
|
-
</PopoverAnchor>
|
|
1000
|
-
<PopoverContent class="w-72">
|
|
1001
|
-
<Calendar
|
|
1002
|
-
:locale="locale"
|
|
1003
|
-
:layout="field.mode"
|
|
1004
|
-
:model-value="toCalendarDateValue(getProperty(modelValue, field.path), field.value)"
|
|
1005
|
-
:disabled="isFieldDisabled(field)"
|
|
1006
|
-
:is-date-disabled="field.disableDate ? (date) => isCalendarDateDisabled(field, date) : void 0"
|
|
1007
|
-
@update:model-value="(value) => {
|
|
1008
|
-
if (value === void 0) {
|
|
1009
|
-
deleteProperty(modelValue, field.path);
|
|
1010
|
-
} else {
|
|
1011
|
-
setProperty(modelValue, field.path, format(value.toDate(getLocalTimeZone()), field.value));
|
|
1012
|
-
}
|
|
1013
|
-
}"
|
|
1014
|
-
/>
|
|
1015
|
-
</PopoverContent>
|
|
1016
|
-
</Popover>
|
|
1017
|
-
<template v-else>
|
|
1018
|
-
<template v-if="field.type === 'select'">
|
|
1019
|
-
<Popover
|
|
1020
|
-
v-for="selectState in [getSelectFieldState(field)]"
|
|
1021
|
-
:key="`${field.id}:select:${selectState.selectedKey ?? 'empty'}`"
|
|
1022
|
-
:open="selectOpen[field.path] === true"
|
|
1023
|
-
@update:open="(open) => handleSelectOpenChange(field, open)"
|
|
1024
|
-
>
|
|
1025
|
-
<PopoverAnchor as-child>
|
|
1026
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
1027
|
-
<PopoverTrigger as-child>
|
|
1028
|
-
<InputGroupInput
|
|
1029
|
-
:id="`${id}:${field.path}`"
|
|
1030
|
-
:model-value="getSelectDisplayValue(selectState, selectState.selectedKey)"
|
|
1031
|
-
:disabled="isFieldDisabled(field)"
|
|
1032
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1033
|
-
:placeholder="t('select-placeholder')"
|
|
1034
|
-
class="text-left"
|
|
1035
|
-
readonly
|
|
1036
|
-
@blur="handleSelectBlur(field)"
|
|
1037
|
-
/>
|
|
1038
|
-
</PopoverTrigger>
|
|
1039
|
-
<InputGroupAddon v-if="field.icon">
|
|
1040
|
-
<Icon
|
|
1041
|
-
:icon="field.icon"
|
|
1042
|
-
/>
|
|
1043
|
-
</InputGroupAddon>
|
|
1044
|
-
<InputGroupAddon
|
|
1045
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1046
|
-
align="inline-end"
|
|
1047
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1048
|
-
>
|
|
1049
|
-
<Tooltip :delay-duration="800">
|
|
1050
|
-
<TooltipTrigger>
|
|
1051
|
-
<InputGroupButton as-child>
|
|
1052
|
-
<button
|
|
1053
|
-
type="button"
|
|
1054
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1055
|
-
:disabled="isFieldDisabled(field)"
|
|
1056
|
-
@click="clearSelectField(field)"
|
|
1057
|
-
>
|
|
1058
|
-
<Icon
|
|
1059
|
-
icon="fluent:dismiss-20-regular"
|
|
1060
|
-
/>
|
|
1061
|
-
</button>
|
|
1062
|
-
</InputGroupButton>
|
|
1063
|
-
</TooltipTrigger>
|
|
1064
|
-
<TooltipContent>
|
|
1065
|
-
{{ t("clear") }}
|
|
1066
|
-
</TooltipContent>
|
|
1067
|
-
</Tooltip>
|
|
1068
|
-
</InputGroupAddon>
|
|
1069
|
-
</InputGroup>
|
|
1070
|
-
</PopoverAnchor>
|
|
1071
|
-
|
|
1072
|
-
<PopoverContent
|
|
1073
|
-
class="p-0"
|
|
1074
|
-
:style="{ width: 'var(--reka-popover-trigger-width)' }"
|
|
1075
|
-
>
|
|
1076
|
-
<Command
|
|
1077
|
-
:model-value="selectState.selectedKey"
|
|
1078
|
-
:disabled="isFieldDisabled(field)"
|
|
1079
|
-
selection-behavior="toggle"
|
|
1080
|
-
@update:model-value="(value) => handleSelectCommandValueChange(field, selectState, value)"
|
|
1081
|
-
>
|
|
1082
|
-
<CommandInput :placeholder="t('select-search-placeholder')" />
|
|
1083
|
-
<CommandList>
|
|
1084
|
-
<CommandEmpty as-child>
|
|
1085
|
-
<section class="h-32 flex flex-col text-lg items-center justify-center gap-2 select-none">
|
|
1086
|
-
<Icon
|
|
1087
|
-
icon="fluent:app-recent-20-regular"
|
|
1088
|
-
class="text-zinc-400 text-2xl!"
|
|
1089
|
-
/>
|
|
1090
|
-
<p class="text-zinc-500">
|
|
1091
|
-
{{ t("select-empty") }}
|
|
1092
|
-
</p>
|
|
1093
|
-
</section>
|
|
1094
|
-
</CommandEmpty>
|
|
1095
|
-
<CommandGroup>
|
|
1096
|
-
<CommandItem
|
|
1097
|
-
v-for="option in selectState.options"
|
|
1098
|
-
:key="option.key"
|
|
1099
|
-
data-slot="select-option"
|
|
1100
|
-
:value="option.key"
|
|
1101
|
-
class="data-highlighted:bg-zinc-50 data-highlighted:text-zinc-700 data-[state=checked]:bg-zinc-100 data-[state=checked]:text-zinc-700 transition cursor-pointer relative flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none"
|
|
1102
|
-
>
|
|
1103
|
-
{{ option.label }}
|
|
1104
|
-
</CommandItem>
|
|
1105
|
-
</CommandGroup>
|
|
1106
|
-
</CommandList>
|
|
1107
|
-
</Command>
|
|
1108
|
-
</PopoverContent>
|
|
1109
|
-
</Popover>
|
|
1110
|
-
</template>
|
|
1111
|
-
|
|
1112
|
-
<template v-else-if="field.type === 'radio'">
|
|
1113
|
-
<RadioGroupRoot
|
|
1114
|
-
v-for="radioState in [getSelectFieldState(field)]"
|
|
1115
|
-
:key="`${field.id}:radio:${radioState.selectedKey ?? 'empty'}`"
|
|
1116
|
-
:model-value="radioState.selectedKey"
|
|
1117
|
-
:disabled="isFieldDisabled(field)"
|
|
1118
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1119
|
-
class="flex flex-wrap gap-x-4 gap-y-1.5"
|
|
1120
|
-
@update:model-value="(value) => handleSelectValueChange(field, radioState, value)"
|
|
1121
|
-
@focusout="validateField(field)"
|
|
1122
|
-
>
|
|
1123
|
-
<label
|
|
1124
|
-
v-for="option in radioState.options"
|
|
1125
|
-
:key="option.key"
|
|
1126
|
-
class="flex items-center gap-1.5 text-sm cursor-pointer data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
|
|
1127
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1128
|
-
>
|
|
1129
|
-
<RadioGroupItem
|
|
1130
|
-
:value="option.key"
|
|
1131
|
-
data-slot="radio-group-item"
|
|
1132
|
-
class="size-4 rounded-full border border-zinc-300 data-[state=checked]:border-(--primary) data-[state=checked]:bg-(--primary) focus:outline-none focus-visible:ring-1 focus-visible:ring-(--primary) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 transition-colors"
|
|
1133
|
-
>
|
|
1134
|
-
<RadioGroupIndicator class="flex items-center justify-center">
|
|
1135
|
-
<span class="size-1.5 rounded-full bg-white" />
|
|
1136
|
-
</RadioGroupIndicator>
|
|
1137
|
-
</RadioGroupItem>
|
|
1138
|
-
{{ option.label }}
|
|
1139
|
-
</label>
|
|
1140
|
-
</RadioGroupRoot>
|
|
1141
|
-
</template>
|
|
1142
|
-
|
|
1143
|
-
<InputGroup
|
|
1144
|
-
v-else
|
|
1145
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1146
|
-
:class="field.type === 'textarea' ? 'h-auto flex-col items-stretch' : void 0"
|
|
1147
|
-
>
|
|
1148
|
-
<div
|
|
1149
|
-
v-if="field.type === 'textarea'"
|
|
1150
|
-
class="flex min-w-0 w-full items-center"
|
|
1151
|
-
>
|
|
1152
|
-
<InputGroupTextarea
|
|
1153
|
-
:id="`${id}:${field.path}`"
|
|
1154
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1155
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1156
|
-
:disabled="isFieldDisabled(field)"
|
|
1157
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1158
|
-
@update:model-value="(value) => {
|
|
1159
|
-
if (!value && !field.discardEmptyString) {
|
|
1160
|
-
deleteProperty(modelValue, field.path);
|
|
1161
|
-
} else {
|
|
1162
|
-
setProperty(modelValue, field.path, value);
|
|
1163
|
-
}
|
|
1164
|
-
}"
|
|
1165
|
-
@blur="validateField(field)"
|
|
1166
|
-
/>
|
|
1167
|
-
<InputGroupAddon v-if="field.icon">
|
|
1168
|
-
<Icon
|
|
1169
|
-
:icon="field.icon"
|
|
1170
|
-
/>
|
|
1171
|
-
</InputGroupAddon>
|
|
1172
|
-
</div>
|
|
1173
|
-
<InputGroupInput
|
|
1174
|
-
v-if="field.type === 'string'"
|
|
1175
|
-
:id="`${id}:${field.path}`"
|
|
1176
|
-
:treat-empty-as-different-state-from-null="!!field.discardEmptyString"
|
|
1177
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1178
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1179
|
-
:disabled="isFieldDisabled(field)"
|
|
1180
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1181
|
-
@update:model-value="(value) => {
|
|
1182
|
-
if (!value && !field.discardEmptyString) {
|
|
1183
|
-
deleteProperty(modelValue, field.path);
|
|
1184
|
-
} else {
|
|
1185
|
-
setProperty(modelValue, field.path, value);
|
|
1186
|
-
}
|
|
1187
|
-
}"
|
|
1188
|
-
@blur="validateField(field)"
|
|
1189
|
-
/>
|
|
1190
|
-
<InputGroupNumberField
|
|
1191
|
-
v-if="field.type === 'number'"
|
|
1192
|
-
:id="`${id}:${field.path}`"
|
|
1193
|
-
:model-value="getProperty(modelValue, field.path) ?? null"
|
|
1194
|
-
:min="field.min ? $dsl.evaluate`${field.min}`() : void 0"
|
|
1195
|
-
:max="field.max ? $dsl.evaluate`${field.max}`() : void 0"
|
|
1196
|
-
:step="field.step ? $dsl.evaluate`${field.step}`() : void 0"
|
|
1197
|
-
:disabled="isFieldDisabled(field)"
|
|
1198
|
-
:invalid="isFieldInvalid(field)"
|
|
1199
|
-
@update:model-value="(value) => {
|
|
1200
|
-
if (!value && value !== 0) {
|
|
1201
|
-
deleteProperty(modelValue, field.path);
|
|
1202
|
-
} else {
|
|
1203
|
-
setProperty(modelValue, field.path, value);
|
|
1204
|
-
}
|
|
1205
|
-
}"
|
|
1206
|
-
@blur="validateField(field)"
|
|
1207
|
-
/>
|
|
1208
|
-
<InputGroupAddon v-if="field.type !== 'textarea' && field.icon">
|
|
1209
|
-
<Icon
|
|
1210
|
-
:icon="field.icon"
|
|
1211
|
-
/>
|
|
1212
|
-
</InputGroupAddon>
|
|
1213
|
-
<InputGroupAddon
|
|
1214
|
-
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
1215
|
-
align="inline-end"
|
|
1216
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1217
|
-
>
|
|
1218
|
-
<Tooltip :delay-duration="800">
|
|
1219
|
-
<TooltipTrigger>
|
|
1220
|
-
<InputGroupButton as-child>
|
|
1221
|
-
<button
|
|
1222
|
-
type="button"
|
|
1223
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1224
|
-
:disabled="isFieldDisabled(field)"
|
|
1225
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1226
|
-
>
|
|
1227
|
-
<Icon
|
|
1228
|
-
icon="fluent:dismiss-20-regular"
|
|
1229
|
-
/>
|
|
1230
|
-
</button>
|
|
1231
|
-
</InputGroupButton>
|
|
1232
|
-
</TooltipTrigger>
|
|
1233
|
-
<TooltipContent>
|
|
1234
|
-
{{ t("clear") }}
|
|
1235
|
-
</TooltipContent>
|
|
1236
|
-
</Tooltip>
|
|
1237
|
-
</InputGroupAddon>
|
|
1238
|
-
<InputGroupAddon
|
|
1239
|
-
v-if="field.type === 'string' && field.maxLength && getProperty(modelValue, field.path)"
|
|
1240
|
-
align="inline-end"
|
|
1241
|
-
>
|
|
1242
|
-
<span class="text-xs text-zinc-400 font-mono">
|
|
1243
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1244
|
-
</span>
|
|
1245
|
-
</InputGroupAddon>
|
|
1246
|
-
<InputGroupAddon
|
|
1247
|
-
v-if="field.type === 'textarea' && (hasProperty(modelValue, field.path) || field.maxLength && getProperty(modelValue, field.path))"
|
|
1248
|
-
align="block-end"
|
|
1249
|
-
>
|
|
1250
|
-
<Tooltip
|
|
1251
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1252
|
-
:delay-duration="800"
|
|
1253
|
-
>
|
|
1254
|
-
<TooltipTrigger>
|
|
1255
|
-
<InputGroupButton as-child>
|
|
1256
|
-
<button
|
|
1257
|
-
type="button"
|
|
1258
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1259
|
-
:disabled="isFieldDisabled(field)"
|
|
1260
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1261
|
-
>
|
|
1262
|
-
<Icon
|
|
1263
|
-
icon="fluent:dismiss-20-regular"
|
|
1264
|
-
/>
|
|
1265
|
-
</button>
|
|
1266
|
-
</InputGroupButton>
|
|
1267
|
-
</TooltipTrigger>
|
|
1268
|
-
<TooltipContent>
|
|
1269
|
-
{{ t("clear") }}
|
|
1270
|
-
</TooltipContent>
|
|
1271
|
-
</Tooltip>
|
|
1272
|
-
<span
|
|
1273
|
-
v-if="field.maxLength && getProperty(modelValue, field.path)"
|
|
1274
|
-
class="text-xs text-zinc-400 font-mono"
|
|
1275
|
-
>
|
|
1276
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1277
|
-
</span>
|
|
1278
|
-
</InputGroupAddon>
|
|
1279
|
-
</InputGroup>
|
|
1280
|
-
</template>
|
|
1281
|
-
|
|
1282
|
-
<FieldError
|
|
1283
|
-
v-if="isFieldInvalid(field)"
|
|
1284
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
1285
|
-
>
|
|
1286
|
-
<span v-html="renderValidationMessage(field)" />
|
|
1287
|
-
</FieldError>
|
|
1288
|
-
</FieldContent>
|
|
1289
|
-
</Field>
|
|
1290
825
|
</template>
|
|
1291
|
-
</
|
|
826
|
+
</FieldsBody>
|
|
1292
827
|
|
|
1293
828
|
<slot />
|
|
1294
829
|
</div>
|