@shwfed/nuxt 0.11.50 → 0.11.51
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 +850 -6
- package/dist/runtime/components/fields.vue +0 -2
- package/dist/runtime/components/fields.vue.d.ts +850 -6
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +1698 -10
- package/dist/runtime/components/ui/fields/Fields.vue +627 -162
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +1698 -10
- package/dist/runtime/components/ui/fields/schema.d.ts +5625 -153
- package/dist/runtime/components/ui/fields/schema.js +83 -80
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +849 -5
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +224 -618
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +849 -5
- package/package.json +1 -1
- package/dist/runtime/components/ui/fields/FieldsBody.d.vue.ts +0 -17
- package/dist/runtime/components/ui/fields/FieldsBody.vue +0 -720
- package/dist/runtime/components/ui/fields/FieldsBody.vue.d.ts +0 -17
- package/dist/runtime/components/ui/fields/render-context.d.ts +0 -120
- package/dist/runtime/components/ui/fields/render-context.js +0 -0
|
@@ -6,15 +6,21 @@ 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, setProperty } from "dot-prop";
|
|
9
|
+
import { deleteProperty, getProperty, hasProperty, 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";
|
|
13
14
|
import { mergeDslContexts, useCELContext } from "../../../plugins/cel/context";
|
|
15
|
+
import { Calendar } from "../calendar";
|
|
14
16
|
import { Button } from "../button";
|
|
15
|
-
import
|
|
17
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../command";
|
|
18
|
+
import { Field, FieldContent, FieldError, FieldLabel, FieldSet } from "../field";
|
|
16
19
|
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";
|
|
17
22
|
import { Skeleton } from "../skeleton";
|
|
23
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip";
|
|
18
24
|
import { getLocalizedText } from "../../../utils/coders";
|
|
19
25
|
import { FieldsConfigC, createFieldsConfig } from "./schema";
|
|
20
26
|
const id = useId();
|
|
@@ -25,7 +31,7 @@ const defaultConfig = createFieldsConfig({
|
|
|
25
31
|
const props = defineProps({
|
|
26
32
|
config: { type: null, required: true }
|
|
27
33
|
});
|
|
28
|
-
|
|
34
|
+
defineSlots();
|
|
29
35
|
const emit = defineEmits(["update:config", "initial-value-ready"]);
|
|
30
36
|
const config = computedAsync(async () => FieldsConfigC.parse(await props.config.pipe(Effect.runPromise) ?? defaultConfig));
|
|
31
37
|
const { t, locale } = useI18n();
|
|
@@ -44,23 +50,35 @@ const isReady = ref(false);
|
|
|
44
50
|
const hasInitializedFieldValues = ref(false);
|
|
45
51
|
const hasEmittedInitialValueReady = ref(false);
|
|
46
52
|
const readyResolvers = [];
|
|
47
|
-
function requireSlotProps(slotProps) {
|
|
48
|
-
if (!slotProps) {
|
|
49
|
-
throw new TypeError("missing slot props");
|
|
50
|
-
}
|
|
51
|
-
return slotProps;
|
|
52
|
-
}
|
|
53
53
|
function cloneConfig(config2) {
|
|
54
|
-
|
|
54
|
+
const nextConfig = {
|
|
55
|
+
kind: config2.kind,
|
|
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;
|
|
68
|
+
}
|
|
69
|
+
if (config2.style) {
|
|
70
|
+
nextConfig.style = config2.style;
|
|
71
|
+
}
|
|
72
|
+
return nextConfig;
|
|
55
73
|
}
|
|
56
|
-
function
|
|
57
|
-
return
|
|
74
|
+
function getConfigOrientation(config2) {
|
|
75
|
+
return config2.orientation ?? "horizontal";
|
|
58
76
|
}
|
|
59
|
-
function usesContentsOrientation(
|
|
60
|
-
return
|
|
77
|
+
function usesContentsOrientation(config2) {
|
|
78
|
+
return getConfigOrientation(config2) === "contents";
|
|
61
79
|
}
|
|
62
|
-
function
|
|
63
|
-
return usesContentsOrientation(
|
|
80
|
+
function isConfigBordered(config2) {
|
|
81
|
+
return usesContentsOrientation(config2) && config2.bordered === true;
|
|
64
82
|
}
|
|
65
83
|
function tryEvaluateExpression(source, context) {
|
|
66
84
|
try {
|
|
@@ -81,12 +99,12 @@ function evaluateExpression(source, context, fallback) {
|
|
|
81
99
|
}
|
|
82
100
|
return result.value;
|
|
83
101
|
}
|
|
84
|
-
function
|
|
102
|
+
function getConfigStyle(config2) {
|
|
85
103
|
const style = {};
|
|
86
|
-
if (!
|
|
104
|
+
if (!config2.style) {
|
|
87
105
|
return style;
|
|
88
106
|
}
|
|
89
|
-
const styleMap = evaluateExpression(
|
|
107
|
+
const styleMap = evaluateExpression(config2.style, { form: modelValue.value }, {});
|
|
90
108
|
if (!styleMap || typeof styleMap !== "object" || Array.isArray(styleMap)) {
|
|
91
109
|
return style;
|
|
92
110
|
}
|
|
@@ -116,8 +134,8 @@ function getFieldPartStyle(styleExpression) {
|
|
|
116
134
|
function getFieldStyle(field) {
|
|
117
135
|
return getFieldPartStyle(field.style);
|
|
118
136
|
}
|
|
119
|
-
function getGroupStyle(group,
|
|
120
|
-
const style = usesContentsOrientation(
|
|
137
|
+
function getGroupStyle(group, config2) {
|
|
138
|
+
const style = usesContentsOrientation(config2) ? getConfigStyle(config2) : {};
|
|
121
139
|
const groupStyle = group.style ? evaluateExpression(group.style, { form: modelValue.value, id: group.id }, {}) : {};
|
|
122
140
|
if (!groupStyle || typeof groupStyle !== "object" || Array.isArray(groupStyle)) {
|
|
123
141
|
return style;
|
|
@@ -129,57 +147,42 @@ function getGroupStyle(group, body) {
|
|
|
129
147
|
}
|
|
130
148
|
return style;
|
|
131
149
|
}
|
|
132
|
-
function
|
|
150
|
+
function getConfigEntries(config2) {
|
|
133
151
|
return [
|
|
134
|
-
...
|
|
152
|
+
...config2.fields.map((field) => ({
|
|
135
153
|
key: `field:${field.id}`,
|
|
136
154
|
field
|
|
137
155
|
})),
|
|
138
|
-
...
|
|
156
|
+
...config2.groups.map((group) => ({
|
|
139
157
|
key: `group:${group.id}`,
|
|
140
158
|
group
|
|
141
159
|
}))
|
|
142
160
|
];
|
|
143
161
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
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
|
-
}
|
|
162
|
+
function getConfigFields(config2) {
|
|
163
|
+
return [...config2.fields, ...config2.groups.flatMap((group) => group.fields)];
|
|
158
164
|
}
|
|
159
|
-
function getFieldContainerStyle(field,
|
|
160
|
-
if (field
|
|
161
|
-
return {};
|
|
162
|
-
}
|
|
163
|
-
if (!isPassiveField(field) && usesContentsOrientation(body)) {
|
|
165
|
+
function getFieldContainerStyle(field, config2) {
|
|
166
|
+
if (!isPassiveField(field) && usesContentsOrientation(config2)) {
|
|
164
167
|
return {};
|
|
165
168
|
}
|
|
166
169
|
return getFieldStyle(field);
|
|
167
170
|
}
|
|
168
|
-
function getFieldLabelStyle(field,
|
|
169
|
-
if (!usesContentsOrientation(
|
|
171
|
+
function getFieldLabelStyle(field, config2) {
|
|
172
|
+
if (!usesContentsOrientation(config2)) {
|
|
170
173
|
return {};
|
|
171
174
|
}
|
|
172
175
|
return getFieldPartStyle(field.labelStyle);
|
|
173
176
|
}
|
|
174
|
-
function getFieldContentStyle(field,
|
|
175
|
-
const style = usesContentsOrientation(
|
|
176
|
-
if (usesContentsOrientation(
|
|
177
|
+
function getFieldContentStyle(field, config2) {
|
|
178
|
+
const style = usesContentsOrientation(config2) ? getFieldPartStyle(field.contentStyle) : {};
|
|
179
|
+
if (usesContentsOrientation(config2) && isFieldLabelHidden(field) && style.gridColumn === void 0) {
|
|
177
180
|
style.gridColumn = "1 / -1";
|
|
178
181
|
}
|
|
179
182
|
return style;
|
|
180
183
|
}
|
|
181
|
-
function getMarkdownBodyContentStyle(field,
|
|
182
|
-
if (!usesContentsOrientation(
|
|
184
|
+
function getMarkdownBodyContentStyle(field, config2) {
|
|
185
|
+
if (!usesContentsOrientation(config2)) {
|
|
183
186
|
return {};
|
|
184
187
|
}
|
|
185
188
|
return {
|
|
@@ -190,8 +193,11 @@ function getMarkdownBodyContentStyle(field, body) {
|
|
|
190
193
|
function isPassiveField(field) {
|
|
191
194
|
return field.type === "slot" || field.type === "empty";
|
|
192
195
|
}
|
|
196
|
+
function isMarkdownDisplayField(field) {
|
|
197
|
+
return field.type === "markdown" || field.type === "markdown-body";
|
|
198
|
+
}
|
|
193
199
|
function isInteractiveField(field) {
|
|
194
|
-
return field
|
|
200
|
+
return !isMarkdownDisplayField(field);
|
|
195
201
|
}
|
|
196
202
|
function isFieldLabelHidden(field) {
|
|
197
203
|
return field.hideLabel === true;
|
|
@@ -219,13 +225,13 @@ function getFieldValue(field) {
|
|
|
219
225
|
return getProperty(modelValue.value, field.path);
|
|
220
226
|
}
|
|
221
227
|
function initializeFieldValues(config2) {
|
|
222
|
-
|
|
223
|
-
if (
|
|
224
|
-
|
|
228
|
+
for (const field of getConfigFields(config2)) {
|
|
229
|
+
if (isPassiveField(field) || isMarkdownDisplayField(field) || !field.initialValue) {
|
|
230
|
+
continue;
|
|
225
231
|
}
|
|
226
232
|
const initialValueResult = tryEvaluateExpression(field.initialValue, { form: modelValue.value });
|
|
227
233
|
if (!initialValueResult.ok) {
|
|
228
|
-
|
|
234
|
+
continue;
|
|
229
235
|
}
|
|
230
236
|
const initialValue = initialValueResult.value;
|
|
231
237
|
setProperty(
|
|
@@ -233,7 +239,7 @@ function initializeFieldValues(config2) {
|
|
|
233
239
|
field.path,
|
|
234
240
|
field.type === "number" && typeof initialValue === "bigint" ? Number(initialValue) : initialValue
|
|
235
241
|
);
|
|
236
|
-
}
|
|
242
|
+
}
|
|
237
243
|
}
|
|
238
244
|
const MIME_LABELS = {
|
|
239
245
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel",
|
|
@@ -538,15 +544,15 @@ function validateField(field) {
|
|
|
538
544
|
}
|
|
539
545
|
function validateFields() {
|
|
540
546
|
const nextValidationErrors = {};
|
|
541
|
-
|
|
542
|
-
if (
|
|
543
|
-
|
|
547
|
+
for (const field of getConfigFields(displayConfig.value)) {
|
|
548
|
+
if (isPassiveField(field) || isMarkdownDisplayField(field)) {
|
|
549
|
+
continue;
|
|
544
550
|
}
|
|
545
551
|
const failure = getValidationFailure(field);
|
|
546
552
|
if (failure) {
|
|
547
553
|
nextValidationErrors[field.path] = failure;
|
|
548
554
|
}
|
|
549
|
-
}
|
|
555
|
+
}
|
|
550
556
|
validationErrors.value = nextValidationErrors;
|
|
551
557
|
return Object.keys(nextValidationErrors).length === 0;
|
|
552
558
|
}
|
|
@@ -557,10 +563,7 @@ function getFieldLabel(field) {
|
|
|
557
563
|
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
558
564
|
}
|
|
559
565
|
function getDisplayFieldLabel(field) {
|
|
560
|
-
|
|
561
|
-
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
562
|
-
}
|
|
563
|
-
return field.id;
|
|
566
|
+
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
564
567
|
}
|
|
565
568
|
function isFieldRequired(field) {
|
|
566
569
|
return field.required === true;
|
|
@@ -612,30 +615,6 @@ function handleCalendarBlur(field) {
|
|
|
612
615
|
}
|
|
613
616
|
}, 0);
|
|
614
617
|
}
|
|
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
|
-
}
|
|
639
618
|
function handleConfiguratorConfirm(nextConfig) {
|
|
640
619
|
displayConfig.value = cloneConfig(nextConfig);
|
|
641
620
|
emit("update:config", nextConfig);
|
|
@@ -667,11 +646,11 @@ watch(config, (value) => {
|
|
|
667
646
|
}, { immediate: true });
|
|
668
647
|
watchEffect(() => {
|
|
669
648
|
const activePaths = /* @__PURE__ */ new Set();
|
|
670
|
-
|
|
671
|
-
if (
|
|
649
|
+
for (const field of getConfigFields(displayConfig.value)) {
|
|
650
|
+
if (!isPassiveField(field) && !isFieldHidden(field) && !isMarkdownDisplayField(field) && !isFieldDisabled(field)) {
|
|
672
651
|
activePaths.add(field.path);
|
|
673
652
|
}
|
|
674
|
-
}
|
|
653
|
+
}
|
|
675
654
|
for (const path of Object.keys(validationErrors.value)) {
|
|
676
655
|
if (!activePaths.has(path)) {
|
|
677
656
|
clearFieldValidation(path);
|
|
@@ -688,71 +667,11 @@ watchEffect(() => {
|
|
|
688
667
|
}
|
|
689
668
|
}
|
|
690
669
|
});
|
|
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
|
-
}));
|
|
750
670
|
</script>
|
|
751
671
|
|
|
752
672
|
<script>
|
|
753
673
|
export {
|
|
754
674
|
CalendarFieldC,
|
|
755
|
-
ContainerFieldC,
|
|
756
675
|
CURRENT_COMPATIBILITY_DATE,
|
|
757
676
|
EmptyFieldC,
|
|
758
677
|
FieldC,
|
|
@@ -769,7 +688,6 @@ export {
|
|
|
769
688
|
SlotFieldC,
|
|
770
689
|
SUPPORTED_COMPATIBILITY_DATES,
|
|
771
690
|
StringFieldC,
|
|
772
|
-
TextareaFieldC,
|
|
773
691
|
UploadFieldC,
|
|
774
692
|
ValidationRuleC,
|
|
775
693
|
createFieldsConfig,
|
|
@@ -782,9 +700,9 @@ export {
|
|
|
782
700
|
:class="[
|
|
783
701
|
'relative p-1 -m-1 border border-dashed',
|
|
784
702
|
isCheating ? 'border-(--primary)/20 rounded hover:border-(--primary)/40 transition-colors duration-150 group cursor-pointer' : 'border-transparent',
|
|
785
|
-
|
|
703
|
+
isConfigBordered(displayConfig) ? '!p-0 !m-0 border-transparent' : ''
|
|
786
704
|
]"
|
|
787
|
-
:style="
|
|
705
|
+
:style="getConfigStyle(displayConfig)"
|
|
788
706
|
>
|
|
789
707
|
<Button
|
|
790
708
|
v-if="isCheating"
|
|
@@ -810,20 +728,567 @@ export {
|
|
|
810
728
|
class="absolute inset-0 z-10 w-full h-full"
|
|
811
729
|
/>
|
|
812
730
|
|
|
813
|
-
<
|
|
814
|
-
:
|
|
815
|
-
|
|
731
|
+
<component
|
|
732
|
+
:is="entry.group ? FieldSet : 'div'"
|
|
733
|
+
v-for="entry in getConfigEntries(displayConfig)"
|
|
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' }"
|
|
816
741
|
>
|
|
817
742
|
<template
|
|
818
|
-
v-for="
|
|
819
|
-
|
|
743
|
+
v-for="field in entry.group ? entry.group.fields : entry.field ? [entry.field] : []"
|
|
744
|
+
:key="field.id"
|
|
820
745
|
>
|
|
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>
|
|
821
758
|
<slot
|
|
822
|
-
|
|
823
|
-
|
|
759
|
+
v-else-if="field.type === 'slot'"
|
|
760
|
+
:name="field.id"
|
|
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)"
|
|
824
769
|
/>
|
|
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>
|
|
825
1290
|
</template>
|
|
826
|
-
</
|
|
1291
|
+
</component>
|
|
827
1292
|
|
|
828
1293
|
<slot />
|
|
829
1294
|
</div>
|