@shwfed/nuxt 0.11.46 → 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 -810
- package/dist/runtime/components/fields.vue +2 -0
- package/dist/runtime/components/fields.vue.d.ts +6 -810
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +10 -1618
- package/dist/runtime/components/ui/fields/Fields.vue +180 -610
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +10 -1618
- 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 +95 -5260
- package/dist/runtime/components/ui/fields/schema.js +89 -82
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +5 -809
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +703 -242
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +5 -809
- 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,48 +129,69 @@ 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
|
}
|
|
181
|
+
function getMarkdownBodyContentStyle(field, body) {
|
|
182
|
+
if (!usesContentsOrientation(body)) {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
...getFieldStyle(field),
|
|
187
|
+
gridColumn: "1 / -1"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
184
190
|
function isPassiveField(field) {
|
|
185
191
|
return field.type === "slot" || field.type === "empty";
|
|
186
192
|
}
|
|
187
|
-
function isLabeledDisplayField(field) {
|
|
188
|
-
return field.type === "markdown";
|
|
189
|
-
}
|
|
190
193
|
function isInteractiveField(field) {
|
|
191
|
-
return
|
|
194
|
+
return field.type !== "slot" && field.type !== "empty" && field.type !== "markdown" && field.type !== "markdown-body" && field.type !== "container";
|
|
192
195
|
}
|
|
193
196
|
function isFieldLabelHidden(field) {
|
|
194
197
|
return field.hideLabel === true;
|
|
@@ -216,13 +219,13 @@ function getFieldValue(field) {
|
|
|
216
219
|
return getProperty(modelValue.value, field.path);
|
|
217
220
|
}
|
|
218
221
|
function initializeFieldValues(config2) {
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
+
visitBodyFields(config2, (field) => {
|
|
223
|
+
if (!isInteractiveField(field) || !field.initialValue) {
|
|
224
|
+
return;
|
|
222
225
|
}
|
|
223
226
|
const initialValueResult = tryEvaluateExpression(field.initialValue, { form: modelValue.value });
|
|
224
227
|
if (!initialValueResult.ok) {
|
|
225
|
-
|
|
228
|
+
return;
|
|
226
229
|
}
|
|
227
230
|
const initialValue = initialValueResult.value;
|
|
228
231
|
setProperty(
|
|
@@ -230,7 +233,7 @@ function initializeFieldValues(config2) {
|
|
|
230
233
|
field.path,
|
|
231
234
|
field.type === "number" && typeof initialValue === "bigint" ? Number(initialValue) : initialValue
|
|
232
235
|
);
|
|
233
|
-
}
|
|
236
|
+
});
|
|
234
237
|
}
|
|
235
238
|
const MIME_LABELS = {
|
|
236
239
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel",
|
|
@@ -535,15 +538,15 @@ function validateField(field) {
|
|
|
535
538
|
}
|
|
536
539
|
function validateFields() {
|
|
537
540
|
const nextValidationErrors = {};
|
|
538
|
-
|
|
539
|
-
if (
|
|
540
|
-
|
|
541
|
+
visitBodyFields(displayConfig.value, (field, visible) => {
|
|
542
|
+
if (!visible || !isInteractiveField(field)) {
|
|
543
|
+
return;
|
|
541
544
|
}
|
|
542
545
|
const failure = getValidationFailure(field);
|
|
543
546
|
if (failure) {
|
|
544
547
|
nextValidationErrors[field.path] = failure;
|
|
545
548
|
}
|
|
546
|
-
}
|
|
549
|
+
});
|
|
547
550
|
validationErrors.value = nextValidationErrors;
|
|
548
551
|
return Object.keys(nextValidationErrors).length === 0;
|
|
549
552
|
}
|
|
@@ -554,7 +557,10 @@ function getFieldLabel(field) {
|
|
|
554
557
|
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
555
558
|
}
|
|
556
559
|
function getDisplayFieldLabel(field) {
|
|
557
|
-
|
|
560
|
+
if ("title" in field && field.title) {
|
|
561
|
+
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
562
|
+
}
|
|
563
|
+
return field.id;
|
|
558
564
|
}
|
|
559
565
|
function isFieldRequired(field) {
|
|
560
566
|
return field.required === true;
|
|
@@ -566,9 +572,19 @@ function renderValidationMessage(field) {
|
|
|
566
572
|
}
|
|
567
573
|
function renderMarkdownField(field) {
|
|
568
574
|
const source = getLocalizedText(field.locale, locale.value) ?? "";
|
|
575
|
+
return renderMarkdownSource(source, false);
|
|
576
|
+
}
|
|
577
|
+
function renderMarkdownBodyField(field) {
|
|
578
|
+
const source = getLocalizedText(field.locale, locale.value) ?? "";
|
|
579
|
+
return renderMarkdownSource(source, field.inline === true);
|
|
580
|
+
}
|
|
581
|
+
function renderMarkdownSource(source, inline) {
|
|
569
582
|
if (source.trim().length === 0) {
|
|
570
583
|
return "";
|
|
571
584
|
}
|
|
585
|
+
if (inline) {
|
|
586
|
+
return $md.inline`${source}`(mergeDslContexts({ form: modelValue.value }, dslContext));
|
|
587
|
+
}
|
|
572
588
|
return $md.block`${source}`(mergeDslContexts({ form: modelValue.value }, dslContext));
|
|
573
589
|
}
|
|
574
590
|
function isCalendarDateDisabled(field, date) {
|
|
@@ -596,6 +612,30 @@ function handleCalendarBlur(field) {
|
|
|
596
612
|
}
|
|
597
613
|
}, 0);
|
|
598
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
|
+
}
|
|
599
639
|
function handleConfiguratorConfirm(nextConfig) {
|
|
600
640
|
displayConfig.value = cloneConfig(nextConfig);
|
|
601
641
|
emit("update:config", nextConfig);
|
|
@@ -627,11 +667,11 @@ watch(config, (value) => {
|
|
|
627
667
|
}, { immediate: true });
|
|
628
668
|
watchEffect(() => {
|
|
629
669
|
const activePaths = /* @__PURE__ */ new Set();
|
|
630
|
-
|
|
631
|
-
if (
|
|
670
|
+
visitBodyFields(displayConfig.value, (field, visible) => {
|
|
671
|
+
if (visible && isInteractiveField(field) && !isFieldDisabled(field)) {
|
|
632
672
|
activePaths.add(field.path);
|
|
633
673
|
}
|
|
634
|
-
}
|
|
674
|
+
});
|
|
635
675
|
for (const path of Object.keys(validationErrors.value)) {
|
|
636
676
|
if (!activePaths.has(path)) {
|
|
637
677
|
clearFieldValidation(path);
|
|
@@ -648,11 +688,71 @@ watchEffect(() => {
|
|
|
648
688
|
}
|
|
649
689
|
}
|
|
650
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
|
+
}));
|
|
651
750
|
</script>
|
|
652
751
|
|
|
653
752
|
<script>
|
|
654
753
|
export {
|
|
655
754
|
CalendarFieldC,
|
|
755
|
+
ContainerFieldC,
|
|
656
756
|
CURRENT_COMPATIBILITY_DATE,
|
|
657
757
|
EmptyFieldC,
|
|
658
758
|
FieldC,
|
|
@@ -662,12 +762,14 @@ export {
|
|
|
662
762
|
FieldsConfigC,
|
|
663
763
|
FieldsConfigInputC,
|
|
664
764
|
KIND,
|
|
765
|
+
MarkdownBodyFieldC,
|
|
665
766
|
MarkdownFieldC,
|
|
666
767
|
NumberFieldC,
|
|
667
768
|
SelectFieldC,
|
|
668
769
|
SlotFieldC,
|
|
669
770
|
SUPPORTED_COMPATIBILITY_DATES,
|
|
670
771
|
StringFieldC,
|
|
772
|
+
TextareaFieldC,
|
|
671
773
|
UploadFieldC,
|
|
672
774
|
ValidationRuleC,
|
|
673
775
|
createFieldsConfig,
|
|
@@ -680,9 +782,9 @@ export {
|
|
|
680
782
|
:class="[
|
|
681
783
|
'relative p-1 -m-1 border border-dashed',
|
|
682
784
|
isCheating ? 'border-(--primary)/20 rounded hover:border-(--primary)/40 transition-colors duration-150 group cursor-pointer' : 'border-transparent',
|
|
683
|
-
|
|
785
|
+
isBodyBordered(displayConfig) ? '!p-0 !m-0 border-transparent' : ''
|
|
684
786
|
]"
|
|
685
|
-
:style="
|
|
787
|
+
:style="getBodyStyle(displayConfig)"
|
|
686
788
|
>
|
|
687
789
|
<Button
|
|
688
790
|
v-if="isCheating"
|
|
@@ -708,552 +810,20 @@ export {
|
|
|
708
810
|
class="absolute inset-0 z-10 w-full h-full"
|
|
709
811
|
/>
|
|
710
812
|
|
|
711
|
-
<
|
|
712
|
-
:
|
|
713
|
-
|
|
714
|
-
:key="entry.key"
|
|
715
|
-
:data-slot="entry.group ? 'fields-group' : 'fields-root-entry'"
|
|
716
|
-
:data-group-id="entry.group?.id"
|
|
717
|
-
:class="entry.group ? [
|
|
718
|
-
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' : ''
|
|
719
|
-
] : void 0"
|
|
720
|
-
:style="entry.group ? getGroupStyle(entry.group, displayConfig) : { display: 'contents' }"
|
|
813
|
+
<FieldsBody
|
|
814
|
+
:body="displayConfig"
|
|
815
|
+
:renderer="renderContext"
|
|
721
816
|
>
|
|
722
817
|
<template
|
|
723
|
-
v-for="
|
|
724
|
-
|
|
818
|
+
v-for="(_, slotName) in slots"
|
|
819
|
+
#[slotName]="slotProps"
|
|
725
820
|
>
|
|
726
|
-
<div
|
|
727
|
-
v-if="field.type === 'slot' && isConfigBordered(displayConfig)"
|
|
728
|
-
:class="'border-b border-r border-zinc-200'"
|
|
729
|
-
:style="getFieldStyle(field)"
|
|
730
|
-
>
|
|
731
|
-
<slot
|
|
732
|
-
:name="field.id"
|
|
733
|
-
:form="slotForm"
|
|
734
|
-
:style="{}"
|
|
735
|
-
:valid="valid"
|
|
736
|
-
/>
|
|
737
|
-
</div>
|
|
738
821
|
<slot
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
:form="slotForm"
|
|
742
|
-
:style="getFieldStyle(field)"
|
|
743
|
-
:valid="valid"
|
|
822
|
+
:name="slotName"
|
|
823
|
+
v-bind="requireSlotProps(slotProps)"
|
|
744
824
|
/>
|
|
745
|
-
<div
|
|
746
|
-
v-else-if="field.type === 'empty'"
|
|
747
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
748
|
-
:style="getFieldStyle(field)"
|
|
749
|
-
/>
|
|
750
|
-
<Field
|
|
751
|
-
v-else-if="field.type === 'markdown' && !isFieldHidden(field)"
|
|
752
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
753
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
754
|
-
>
|
|
755
|
-
<FieldLabel
|
|
756
|
-
v-if="!isFieldLabelHidden(field)"
|
|
757
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
758
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
759
|
-
>
|
|
760
|
-
<span>{{ getDisplayFieldLabel(field) }}</span>
|
|
761
|
-
</FieldLabel>
|
|
762
|
-
<FieldContent
|
|
763
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2 items-center justify-center text-center' : void 0"
|
|
764
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
765
|
-
>
|
|
766
|
-
<div
|
|
767
|
-
class="text-center"
|
|
768
|
-
data-slot="fields-markdown"
|
|
769
|
-
v-html="renderMarkdownField(field)"
|
|
770
|
-
/>
|
|
771
|
-
</FieldContent>
|
|
772
|
-
</Field>
|
|
773
|
-
<Field
|
|
774
|
-
v-else-if="field.type === 'upload' && !isFieldHidden(field)"
|
|
775
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
776
|
-
:data-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
777
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
778
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
779
|
-
>
|
|
780
|
-
<FieldLabel
|
|
781
|
-
v-if="!isFieldLabelHidden(field)"
|
|
782
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
783
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
784
|
-
>
|
|
785
|
-
<span class="inline-flex items-start gap-0.5">
|
|
786
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
787
|
-
<sup
|
|
788
|
-
v-if="isFieldRequired(field)"
|
|
789
|
-
class="text-red-500 leading-none"
|
|
790
|
-
>*</sup>
|
|
791
|
-
</span>
|
|
792
|
-
<span v-if="isCheating">
|
|
793
|
-
<span class="font-mono">{{ field.path }}</span>
|
|
794
|
-
</span>
|
|
795
|
-
</FieldLabel>
|
|
796
|
-
<FieldContent
|
|
797
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
798
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
799
|
-
>
|
|
800
|
-
<div
|
|
801
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
802
|
-
class="flex flex-col gap-2"
|
|
803
|
-
>
|
|
804
|
-
<label
|
|
805
|
-
v-if="getUploadFiles(field).length < getUploadMaxCount(field)"
|
|
806
|
-
:class="[
|
|
807
|
-
'flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-4 py-8 transition-colors',
|
|
808
|
-
isFieldDisabled(field) ? 'cursor-not-allowed border-zinc-200 opacity-50' : 'border-zinc-300 hover:border-zinc-400 hover:bg-zinc-50'
|
|
809
|
-
]"
|
|
810
|
-
@drop="handleUploadDrop(field, $event)"
|
|
811
|
-
@dragover="handleUploadDragOver"
|
|
812
|
-
>
|
|
813
|
-
<Icon
|
|
814
|
-
:icon="field.icon ?? 'fluent:cloud-arrow-up-20-regular'"
|
|
815
|
-
class="text-4xl text-zinc-400"
|
|
816
|
-
/>
|
|
817
|
-
<button
|
|
818
|
-
v-if="field.template"
|
|
819
|
-
type="button"
|
|
820
|
-
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"
|
|
821
|
-
:disabled="templateDownloading[field.id]"
|
|
822
|
-
@click.prevent.stop="handleTemplateDownload(field)"
|
|
823
|
-
>
|
|
824
|
-
<Icon
|
|
825
|
-
:icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : getUploadTemplateIcon(field)"
|
|
826
|
-
class="text-base"
|
|
827
|
-
/>
|
|
828
|
-
{{ getLocalizedText(field.templateName, locale) ?? t("upload-download-template") }}
|
|
829
|
-
</button>
|
|
830
|
-
<p
|
|
831
|
-
v-if="field.description"
|
|
832
|
-
class="text-sm text-zinc-600"
|
|
833
|
-
v-html="$md.inline`${getLocalizedText(field.description, locale) ?? ''}`()"
|
|
834
|
-
/>
|
|
835
|
-
<p
|
|
836
|
-
v-else
|
|
837
|
-
class="text-sm text-zinc-600"
|
|
838
|
-
>
|
|
839
|
-
{{ t("upload-click-or-drag") }}
|
|
840
|
-
</p>
|
|
841
|
-
<p class="text-xs text-zinc-400">
|
|
842
|
-
{{ getUploadDescription(field) || t("upload-accept-all") }}
|
|
843
|
-
</p>
|
|
844
|
-
<input
|
|
845
|
-
type="file"
|
|
846
|
-
class="sr-only"
|
|
847
|
-
:accept="getUploadAcceptString(field)"
|
|
848
|
-
:disabled="isFieldDisabled(field)"
|
|
849
|
-
:multiple="getUploadMaxCount(field) !== 1"
|
|
850
|
-
@change="handleUploadInputChange(field, $event)"
|
|
851
|
-
>
|
|
852
|
-
</label>
|
|
853
|
-
<ul
|
|
854
|
-
v-if="getUploadFiles(field).length > 0"
|
|
855
|
-
class="flex flex-col gap-1"
|
|
856
|
-
>
|
|
857
|
-
<li
|
|
858
|
-
v-for="(file, fileIndex) in getUploadFiles(field)"
|
|
859
|
-
:key="`${field.id}:${fileIndex}:${file.name}`"
|
|
860
|
-
class="flex items-center gap-2 rounded-md border border-zinc-200 px-3 py-2"
|
|
861
|
-
>
|
|
862
|
-
<Icon
|
|
863
|
-
:icon="getFileIcon(file.name)"
|
|
864
|
-
class="shrink-0 text-lg"
|
|
865
|
-
/>
|
|
866
|
-
<span class="flex-1 truncate text-sm text-zinc-700">{{ file.name }}</span>
|
|
867
|
-
<button
|
|
868
|
-
type="button"
|
|
869
|
-
class="shrink-0 text-zinc-300 transition-colors hover:text-red-500"
|
|
870
|
-
:disabled="isFieldDisabled(field)"
|
|
871
|
-
@click="removeUploadFile(field, fileIndex)"
|
|
872
|
-
>
|
|
873
|
-
<Icon
|
|
874
|
-
icon="fluent:delete-20-regular"
|
|
875
|
-
class="text-lg"
|
|
876
|
-
/>
|
|
877
|
-
</button>
|
|
878
|
-
</li>
|
|
879
|
-
</ul>
|
|
880
|
-
</div>
|
|
881
|
-
<FieldError
|
|
882
|
-
v-if="isFieldInvalid(field)"
|
|
883
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
884
|
-
>
|
|
885
|
-
<span v-html="renderValidationMessage(field)" />
|
|
886
|
-
</FieldError>
|
|
887
|
-
</FieldContent>
|
|
888
|
-
</Field>
|
|
889
|
-
<Field
|
|
890
|
-
v-else-if="isInteractiveField(field) && !isFieldHidden(field)"
|
|
891
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
892
|
-
:data-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
893
|
-
:orientation="getConfigOrientation(displayConfig)"
|
|
894
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
895
|
-
>
|
|
896
|
-
<FieldLabel
|
|
897
|
-
v-if="!isFieldLabelHidden(field)"
|
|
898
|
-
:for="['string', 'textarea', 'number', 'select'].includes(field.type) ? `${id}:${field.path}` : void 0"
|
|
899
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
900
|
-
:style="getFieldLabelStyle(field, displayConfig)"
|
|
901
|
-
>
|
|
902
|
-
<span class="inline-flex items-start gap-0.5">
|
|
903
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
904
|
-
<sup
|
|
905
|
-
v-if="isFieldRequired(field)"
|
|
906
|
-
class="text-red-500 leading-none"
|
|
907
|
-
>*</sup>
|
|
908
|
-
</span>
|
|
909
|
-
<span v-if="isCheating">
|
|
910
|
-
<span class="font-mono">{{ field.path }}</span>
|
|
911
|
-
</span>
|
|
912
|
-
</FieldLabel>
|
|
913
|
-
<FieldContent
|
|
914
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
915
|
-
:style="getFieldContentStyle(field, displayConfig)"
|
|
916
|
-
>
|
|
917
|
-
<Popover
|
|
918
|
-
v-if="field.type === 'calendar'"
|
|
919
|
-
@update:open="(open) => handleCalendarOpenChange(field, open)"
|
|
920
|
-
>
|
|
921
|
-
<PopoverAnchor as-child>
|
|
922
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
923
|
-
<PopoverTrigger as-child>
|
|
924
|
-
<InputGroupInput
|
|
925
|
-
:model-value="displayCalendarValue(getProperty(modelValue, field.path), field.display, field.value)"
|
|
926
|
-
class="text-left"
|
|
927
|
-
:disabled="isFieldDisabled(field)"
|
|
928
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
929
|
-
readonly
|
|
930
|
-
@blur="handleCalendarBlur(field)"
|
|
931
|
-
/>
|
|
932
|
-
</PopoverTrigger>
|
|
933
|
-
<InputGroupAddon v-if="field.icon">
|
|
934
|
-
<Icon
|
|
935
|
-
:icon="field.icon"
|
|
936
|
-
/>
|
|
937
|
-
</InputGroupAddon>
|
|
938
|
-
<InputGroupAddon
|
|
939
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
940
|
-
align="inline-end"
|
|
941
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
942
|
-
>
|
|
943
|
-
<Tooltip :delay-duration="800">
|
|
944
|
-
<TooltipTrigger>
|
|
945
|
-
<InputGroupButton as-child>
|
|
946
|
-
<button
|
|
947
|
-
type="button"
|
|
948
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
949
|
-
:disabled="isFieldDisabled(field)"
|
|
950
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
951
|
-
>
|
|
952
|
-
<Icon
|
|
953
|
-
icon="fluent:dismiss-20-regular"
|
|
954
|
-
/>
|
|
955
|
-
</button>
|
|
956
|
-
</InputGroupButton>
|
|
957
|
-
</TooltipTrigger>
|
|
958
|
-
<TooltipContent>
|
|
959
|
-
{{ t("clear") }}
|
|
960
|
-
</TooltipContent>
|
|
961
|
-
</Tooltip>
|
|
962
|
-
</InputGroupAddon>
|
|
963
|
-
</InputGroup>
|
|
964
|
-
</PopoverAnchor>
|
|
965
|
-
<PopoverContent class="w-72">
|
|
966
|
-
<Calendar
|
|
967
|
-
:locale="locale"
|
|
968
|
-
:layout="field.mode"
|
|
969
|
-
:model-value="toCalendarDateValue(getProperty(modelValue, field.path), field.value)"
|
|
970
|
-
:disabled="isFieldDisabled(field)"
|
|
971
|
-
:is-date-disabled="field.disableDate ? (date) => isCalendarDateDisabled(field, date) : void 0"
|
|
972
|
-
@update:model-value="(value) => {
|
|
973
|
-
if (value === void 0) {
|
|
974
|
-
deleteProperty(modelValue, field.path);
|
|
975
|
-
} else {
|
|
976
|
-
setProperty(modelValue, field.path, format(value.toDate(getLocalTimeZone()), field.value));
|
|
977
|
-
}
|
|
978
|
-
}"
|
|
979
|
-
/>
|
|
980
|
-
</PopoverContent>
|
|
981
|
-
</Popover>
|
|
982
|
-
<template v-else>
|
|
983
|
-
<template v-if="field.type === 'select'">
|
|
984
|
-
<Popover
|
|
985
|
-
v-for="selectState in [getSelectFieldState(field)]"
|
|
986
|
-
:key="`${field.id}:select:${selectState.selectedKey ?? 'empty'}`"
|
|
987
|
-
:open="selectOpen[field.path] === true"
|
|
988
|
-
@update:open="(open) => handleSelectOpenChange(field, open)"
|
|
989
|
-
>
|
|
990
|
-
<PopoverAnchor as-child>
|
|
991
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
992
|
-
<PopoverTrigger as-child>
|
|
993
|
-
<InputGroupInput
|
|
994
|
-
:id="`${id}:${field.path}`"
|
|
995
|
-
:model-value="getSelectDisplayValue(selectState, selectState.selectedKey)"
|
|
996
|
-
:disabled="isFieldDisabled(field)"
|
|
997
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
998
|
-
:placeholder="t('select-placeholder')"
|
|
999
|
-
class="text-left"
|
|
1000
|
-
readonly
|
|
1001
|
-
@blur="handleSelectBlur(field)"
|
|
1002
|
-
/>
|
|
1003
|
-
</PopoverTrigger>
|
|
1004
|
-
<InputGroupAddon v-if="field.icon">
|
|
1005
|
-
<Icon
|
|
1006
|
-
:icon="field.icon"
|
|
1007
|
-
/>
|
|
1008
|
-
</InputGroupAddon>
|
|
1009
|
-
<InputGroupAddon
|
|
1010
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1011
|
-
align="inline-end"
|
|
1012
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1013
|
-
>
|
|
1014
|
-
<Tooltip :delay-duration="800">
|
|
1015
|
-
<TooltipTrigger>
|
|
1016
|
-
<InputGroupButton as-child>
|
|
1017
|
-
<button
|
|
1018
|
-
type="button"
|
|
1019
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1020
|
-
:disabled="isFieldDisabled(field)"
|
|
1021
|
-
@click="clearSelectField(field)"
|
|
1022
|
-
>
|
|
1023
|
-
<Icon
|
|
1024
|
-
icon="fluent:dismiss-20-regular"
|
|
1025
|
-
/>
|
|
1026
|
-
</button>
|
|
1027
|
-
</InputGroupButton>
|
|
1028
|
-
</TooltipTrigger>
|
|
1029
|
-
<TooltipContent>
|
|
1030
|
-
{{ t("clear") }}
|
|
1031
|
-
</TooltipContent>
|
|
1032
|
-
</Tooltip>
|
|
1033
|
-
</InputGroupAddon>
|
|
1034
|
-
</InputGroup>
|
|
1035
|
-
</PopoverAnchor>
|
|
1036
|
-
|
|
1037
|
-
<PopoverContent
|
|
1038
|
-
class="p-0"
|
|
1039
|
-
:style="{ width: 'var(--reka-popover-trigger-width)' }"
|
|
1040
|
-
>
|
|
1041
|
-
<Command
|
|
1042
|
-
:model-value="selectState.selectedKey"
|
|
1043
|
-
:disabled="isFieldDisabled(field)"
|
|
1044
|
-
selection-behavior="toggle"
|
|
1045
|
-
@update:model-value="(value) => handleSelectCommandValueChange(field, selectState, value)"
|
|
1046
|
-
>
|
|
1047
|
-
<CommandInput :placeholder="t('select-search-placeholder')" />
|
|
1048
|
-
<CommandList>
|
|
1049
|
-
<CommandEmpty as-child>
|
|
1050
|
-
<section class="h-32 flex flex-col text-lg items-center justify-center gap-2 select-none">
|
|
1051
|
-
<Icon
|
|
1052
|
-
icon="fluent:app-recent-20-regular"
|
|
1053
|
-
class="text-zinc-400 text-2xl!"
|
|
1054
|
-
/>
|
|
1055
|
-
<p class="text-zinc-500">
|
|
1056
|
-
{{ t("select-empty") }}
|
|
1057
|
-
</p>
|
|
1058
|
-
</section>
|
|
1059
|
-
</CommandEmpty>
|
|
1060
|
-
<CommandGroup>
|
|
1061
|
-
<CommandItem
|
|
1062
|
-
v-for="option in selectState.options"
|
|
1063
|
-
:key="option.key"
|
|
1064
|
-
data-slot="select-option"
|
|
1065
|
-
:value="option.key"
|
|
1066
|
-
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"
|
|
1067
|
-
>
|
|
1068
|
-
{{ option.label }}
|
|
1069
|
-
</CommandItem>
|
|
1070
|
-
</CommandGroup>
|
|
1071
|
-
</CommandList>
|
|
1072
|
-
</Command>
|
|
1073
|
-
</PopoverContent>
|
|
1074
|
-
</Popover>
|
|
1075
|
-
</template>
|
|
1076
|
-
|
|
1077
|
-
<template v-else-if="field.type === 'radio'">
|
|
1078
|
-
<RadioGroupRoot
|
|
1079
|
-
v-for="radioState in [getSelectFieldState(field)]"
|
|
1080
|
-
:key="`${field.id}:radio:${radioState.selectedKey ?? 'empty'}`"
|
|
1081
|
-
:model-value="radioState.selectedKey"
|
|
1082
|
-
:disabled="isFieldDisabled(field)"
|
|
1083
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1084
|
-
class="flex flex-wrap gap-x-4 gap-y-1.5"
|
|
1085
|
-
@update:model-value="(value) => handleSelectValueChange(field, radioState, value)"
|
|
1086
|
-
@focusout="validateField(field)"
|
|
1087
|
-
>
|
|
1088
|
-
<label
|
|
1089
|
-
v-for="option in radioState.options"
|
|
1090
|
-
:key="option.key"
|
|
1091
|
-
class="flex items-center gap-1.5 text-sm cursor-pointer data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
|
|
1092
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1093
|
-
>
|
|
1094
|
-
<RadioGroupItem
|
|
1095
|
-
:value="option.key"
|
|
1096
|
-
data-slot="radio-group-item"
|
|
1097
|
-
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"
|
|
1098
|
-
>
|
|
1099
|
-
<RadioGroupIndicator class="flex items-center justify-center">
|
|
1100
|
-
<span class="size-1.5 rounded-full bg-white" />
|
|
1101
|
-
</RadioGroupIndicator>
|
|
1102
|
-
</RadioGroupItem>
|
|
1103
|
-
{{ option.label }}
|
|
1104
|
-
</label>
|
|
1105
|
-
</RadioGroupRoot>
|
|
1106
|
-
</template>
|
|
1107
|
-
|
|
1108
|
-
<InputGroup
|
|
1109
|
-
v-else
|
|
1110
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1111
|
-
:class="field.type === 'textarea' ? 'h-auto flex-col items-stretch' : void 0"
|
|
1112
|
-
>
|
|
1113
|
-
<div
|
|
1114
|
-
v-if="field.type === 'textarea'"
|
|
1115
|
-
class="flex min-w-0 w-full items-center"
|
|
1116
|
-
>
|
|
1117
|
-
<InputGroupTextarea
|
|
1118
|
-
:id="`${id}:${field.path}`"
|
|
1119
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1120
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1121
|
-
:disabled="isFieldDisabled(field)"
|
|
1122
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1123
|
-
@update:model-value="(value) => {
|
|
1124
|
-
if (!value && !field.discardEmptyString) {
|
|
1125
|
-
deleteProperty(modelValue, field.path);
|
|
1126
|
-
} else {
|
|
1127
|
-
setProperty(modelValue, field.path, value);
|
|
1128
|
-
}
|
|
1129
|
-
}"
|
|
1130
|
-
@blur="validateField(field)"
|
|
1131
|
-
/>
|
|
1132
|
-
<InputGroupAddon v-if="field.icon">
|
|
1133
|
-
<Icon
|
|
1134
|
-
:icon="field.icon"
|
|
1135
|
-
/>
|
|
1136
|
-
</InputGroupAddon>
|
|
1137
|
-
</div>
|
|
1138
|
-
<InputGroupInput
|
|
1139
|
-
v-if="field.type === 'string'"
|
|
1140
|
-
:id="`${id}:${field.path}`"
|
|
1141
|
-
:treat-empty-as-different-state-from-null="!!field.discardEmptyString"
|
|
1142
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1143
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1144
|
-
:disabled="isFieldDisabled(field)"
|
|
1145
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1146
|
-
@update:model-value="(value) => {
|
|
1147
|
-
if (!value && !field.discardEmptyString) {
|
|
1148
|
-
deleteProperty(modelValue, field.path);
|
|
1149
|
-
} else {
|
|
1150
|
-
setProperty(modelValue, field.path, value);
|
|
1151
|
-
}
|
|
1152
|
-
}"
|
|
1153
|
-
@blur="validateField(field)"
|
|
1154
|
-
/>
|
|
1155
|
-
<InputGroupNumberField
|
|
1156
|
-
v-if="field.type === 'number'"
|
|
1157
|
-
:id="`${id}:${field.path}`"
|
|
1158
|
-
:model-value="getProperty(modelValue, field.path) ?? null"
|
|
1159
|
-
:min="field.min ? $dsl.evaluate`${field.min}`() : void 0"
|
|
1160
|
-
:max="field.max ? $dsl.evaluate`${field.max}`() : void 0"
|
|
1161
|
-
:step="field.step ? $dsl.evaluate`${field.step}`() : void 0"
|
|
1162
|
-
:disabled="isFieldDisabled(field)"
|
|
1163
|
-
:invalid="isFieldInvalid(field)"
|
|
1164
|
-
@update:model-value="(value) => {
|
|
1165
|
-
if (!value && value !== 0) {
|
|
1166
|
-
deleteProperty(modelValue, field.path);
|
|
1167
|
-
} else {
|
|
1168
|
-
setProperty(modelValue, field.path, value);
|
|
1169
|
-
}
|
|
1170
|
-
}"
|
|
1171
|
-
@blur="validateField(field)"
|
|
1172
|
-
/>
|
|
1173
|
-
<InputGroupAddon v-if="field.type !== 'textarea' && field.icon">
|
|
1174
|
-
<Icon
|
|
1175
|
-
:icon="field.icon"
|
|
1176
|
-
/>
|
|
1177
|
-
</InputGroupAddon>
|
|
1178
|
-
<InputGroupAddon
|
|
1179
|
-
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
1180
|
-
align="inline-end"
|
|
1181
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1182
|
-
>
|
|
1183
|
-
<Tooltip :delay-duration="800">
|
|
1184
|
-
<TooltipTrigger>
|
|
1185
|
-
<InputGroupButton as-child>
|
|
1186
|
-
<button
|
|
1187
|
-
type="button"
|
|
1188
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1189
|
-
:disabled="isFieldDisabled(field)"
|
|
1190
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1191
|
-
>
|
|
1192
|
-
<Icon
|
|
1193
|
-
icon="fluent:dismiss-20-regular"
|
|
1194
|
-
/>
|
|
1195
|
-
</button>
|
|
1196
|
-
</InputGroupButton>
|
|
1197
|
-
</TooltipTrigger>
|
|
1198
|
-
<TooltipContent>
|
|
1199
|
-
{{ t("clear") }}
|
|
1200
|
-
</TooltipContent>
|
|
1201
|
-
</Tooltip>
|
|
1202
|
-
</InputGroupAddon>
|
|
1203
|
-
<InputGroupAddon
|
|
1204
|
-
v-if="field.type === 'string' && field.maxLength && getProperty(modelValue, field.path)"
|
|
1205
|
-
align="inline-end"
|
|
1206
|
-
>
|
|
1207
|
-
<span class="text-xs text-zinc-400 font-mono">
|
|
1208
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1209
|
-
</span>
|
|
1210
|
-
</InputGroupAddon>
|
|
1211
|
-
<InputGroupAddon
|
|
1212
|
-
v-if="field.type === 'textarea' && (hasProperty(modelValue, field.path) || field.maxLength && getProperty(modelValue, field.path))"
|
|
1213
|
-
align="block-end"
|
|
1214
|
-
>
|
|
1215
|
-
<Tooltip
|
|
1216
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1217
|
-
:delay-duration="800"
|
|
1218
|
-
>
|
|
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
|
-
<span
|
|
1238
|
-
v-if="field.maxLength && getProperty(modelValue, field.path)"
|
|
1239
|
-
class="text-xs text-zinc-400 font-mono"
|
|
1240
|
-
>
|
|
1241
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1242
|
-
</span>
|
|
1243
|
-
</InputGroupAddon>
|
|
1244
|
-
</InputGroup>
|
|
1245
|
-
</template>
|
|
1246
|
-
|
|
1247
|
-
<FieldError
|
|
1248
|
-
v-if="isFieldInvalid(field)"
|
|
1249
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
1250
|
-
>
|
|
1251
|
-
<span v-html="renderValidationMessage(field)" />
|
|
1252
|
-
</FieldError>
|
|
1253
|
-
</FieldContent>
|
|
1254
|
-
</Field>
|
|
1255
825
|
</template>
|
|
1256
|
-
</
|
|
826
|
+
</FieldsBody>
|
|
1257
827
|
|
|
1258
828
|
<slot />
|
|
1259
829
|
</div>
|