@shwfed/nuxt 0.11.36 → 0.11.38
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 +396 -342
- package/dist/runtime/components/fields.vue +2 -1
- package/dist/runtime/components/fields.vue.d.ts +396 -342
- package/dist/runtime/components/modal.vue +2 -2
- package/dist/runtime/components/ui/button-configurator/ButtonConfiguratorDialog.vue +3 -3
- package/dist/runtime/components/ui/command/CommandDialog.vue +3 -3
- package/dist/runtime/components/ui/dialog/DialogScrollContent.d.vue.ts +8 -3
- package/dist/runtime/components/ui/dialog/DialogScrollContent.vue +167 -14
- package/dist/runtime/components/ui/dialog/DialogScrollContent.vue.d.ts +8 -3
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +846 -350
- package/dist/runtime/components/ui/fields/Fields.vue +538 -435
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +846 -350
- package/dist/runtime/components/ui/fields/schema.d.ts +3337 -30
- package/dist/runtime/components/ui/fields/schema.js +86 -9
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +394 -340
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +767 -175
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +394 -340
- package/dist/runtime/components/ui/menu-tabs-configurator/MenuTabsConfiguratorDialog.vue +3 -3
- package/dist/runtime/components/ui/table/Table.vue +1 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +3 -3
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ import { mergeDslContexts, useCELContext } from "../../../plugins/cel/context";
|
|
|
15
15
|
import { Calendar } from "../calendar";
|
|
16
16
|
import { Button } from "../button";
|
|
17
17
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../command";
|
|
18
|
-
import { Field, FieldContent, FieldError, FieldLabel } from "../field";
|
|
18
|
+
import { Field, FieldContent, FieldError, FieldLabel, FieldLegend, FieldSet } from "../field";
|
|
19
19
|
import FieldsConfiguratorDialog from "../fields-configurator/FieldsConfiguratorDialog.vue";
|
|
20
20
|
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupNumberField, InputGroupTextarea } from "../input-group";
|
|
21
21
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../popover";
|
|
@@ -25,7 +25,7 @@ import { getLocalizedText } from "../../../utils/coders";
|
|
|
25
25
|
import { FieldsConfigC, createFieldsConfig } from "./schema";
|
|
26
26
|
const id = useId();
|
|
27
27
|
const defaultConfig = createFieldsConfig({
|
|
28
|
-
|
|
28
|
+
groups: []
|
|
29
29
|
});
|
|
30
30
|
const props = defineProps({
|
|
31
31
|
config: { type: null, required: true }
|
|
@@ -53,7 +53,10 @@ function cloneConfig(config2) {
|
|
|
53
53
|
const nextConfig = {
|
|
54
54
|
kind: config2.kind,
|
|
55
55
|
compatibilityDate: config2.compatibilityDate,
|
|
56
|
-
|
|
56
|
+
groups: config2.groups.map((group) => ({
|
|
57
|
+
...group,
|
|
58
|
+
fields: group.fields.slice()
|
|
59
|
+
}))
|
|
57
60
|
};
|
|
58
61
|
if (config2.orientation) {
|
|
59
62
|
nextConfig.orientation = config2.orientation;
|
|
@@ -129,6 +132,30 @@ function getFieldPartStyle(styleExpression) {
|
|
|
129
132
|
function getFieldStyle(field) {
|
|
130
133
|
return getFieldPartStyle(field.style);
|
|
131
134
|
}
|
|
135
|
+
function getGroupStyle(group, config2) {
|
|
136
|
+
const style = usesContentsOrientation(config2) ? getConfigStyle(config2) : {};
|
|
137
|
+
const groupStyle = group.style ? evaluateExpression(group.style, { form: modelValue.value, id: group.id }, {}) : {};
|
|
138
|
+
if (!groupStyle || typeof groupStyle !== "object" || Array.isArray(groupStyle)) {
|
|
139
|
+
return style;
|
|
140
|
+
}
|
|
141
|
+
for (const [key, value] of Object.entries(groupStyle)) {
|
|
142
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
143
|
+
Reflect.set(style, key, value);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return style;
|
|
147
|
+
}
|
|
148
|
+
function getGroupTitle(group) {
|
|
149
|
+
const title = getLocalizedText(group.title, locale.value);
|
|
150
|
+
if (!title) {
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
153
|
+
const normalizedTitle = title.trim();
|
|
154
|
+
return normalizedTitle.length > 0 ? normalizedTitle : void 0;
|
|
155
|
+
}
|
|
156
|
+
function getConfigFields(config2) {
|
|
157
|
+
return config2.groups.flatMap((group) => group.fields);
|
|
158
|
+
}
|
|
132
159
|
function getFieldContainerStyle(field, config2) {
|
|
133
160
|
if (!isPassiveField(field) && usesContentsOrientation(config2)) {
|
|
134
161
|
return {};
|
|
@@ -150,6 +177,12 @@ function getFieldContentStyle(field, config2) {
|
|
|
150
177
|
function isPassiveField(field) {
|
|
151
178
|
return field.type === "slot" || field.type === "empty";
|
|
152
179
|
}
|
|
180
|
+
function isLabeledDisplayField(field) {
|
|
181
|
+
return field.type === "markdown";
|
|
182
|
+
}
|
|
183
|
+
function isInteractiveField(field) {
|
|
184
|
+
return !isLabeledDisplayField(field);
|
|
185
|
+
}
|
|
153
186
|
function toCalendarDateValue(value, valueFormat) {
|
|
154
187
|
if (typeof value !== "string") return void 0;
|
|
155
188
|
const date = parse(value, valueFormat, /* @__PURE__ */ new Date());
|
|
@@ -173,8 +206,8 @@ function getFieldValue(field) {
|
|
|
173
206
|
return getProperty(modelValue.value, field.path);
|
|
174
207
|
}
|
|
175
208
|
function initializeFieldValues(config2) {
|
|
176
|
-
for (const field of config2
|
|
177
|
-
if (isPassiveField(field) || !field.initialValue) {
|
|
209
|
+
for (const field of getConfigFields(config2)) {
|
|
210
|
+
if (isPassiveField(field) || isLabeledDisplayField(field) || !field.initialValue) {
|
|
178
211
|
continue;
|
|
179
212
|
}
|
|
180
213
|
const initialValueResult = tryEvaluateExpression(field.initialValue, { form: modelValue.value });
|
|
@@ -226,6 +259,24 @@ function getFileIcon(filename) {
|
|
|
226
259
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
227
260
|
return FILE_EXTENSION_ICONS[ext] ?? "vscode-icons:default-file";
|
|
228
261
|
}
|
|
262
|
+
const MIME_TO_ICON = {
|
|
263
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "vscode-icons:file-type-excel",
|
|
264
|
+
"application/vnd.ms-excel": "vscode-icons:file-type-excel",
|
|
265
|
+
"application/pdf": "vscode-icons:file-type-pdf2",
|
|
266
|
+
"application/msword": "vscode-icons:file-type-word",
|
|
267
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "vscode-icons:file-type-word",
|
|
268
|
+
"application/vnd.ms-powerpoint": "vscode-icons:file-type-powerpoint",
|
|
269
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "vscode-icons:file-type-powerpoint",
|
|
270
|
+
"application/x-zip-compressed": "vscode-icons:file-type-zip",
|
|
271
|
+
"application/zip": "vscode-icons:file-type-zip",
|
|
272
|
+
"image/png": "vscode-icons:file-type-image",
|
|
273
|
+
"image/jpg": "vscode-icons:file-type-image",
|
|
274
|
+
"image/jpeg": "vscode-icons:file-type-image"
|
|
275
|
+
};
|
|
276
|
+
function getUploadTemplateIcon(field) {
|
|
277
|
+
const first = field.accept?.[0];
|
|
278
|
+
return (first && MIME_TO_ICON[first]) ?? "fluent:arrow-download-20-regular";
|
|
279
|
+
}
|
|
229
280
|
function getUploadAcceptString(field) {
|
|
230
281
|
if (!field.accept || field.accept.length === 0) {
|
|
231
282
|
return void 0;
|
|
@@ -474,8 +525,8 @@ function validateField(field) {
|
|
|
474
525
|
}
|
|
475
526
|
function validateFields() {
|
|
476
527
|
const nextValidationErrors = {};
|
|
477
|
-
for (const field of displayConfig.value
|
|
478
|
-
if (isPassiveField(field)) {
|
|
528
|
+
for (const field of getConfigFields(displayConfig.value)) {
|
|
529
|
+
if (isPassiveField(field) || isLabeledDisplayField(field)) {
|
|
479
530
|
continue;
|
|
480
531
|
}
|
|
481
532
|
const failure = getValidationFailure(field);
|
|
@@ -492,6 +543,9 @@ function isFieldInvalid(field) {
|
|
|
492
543
|
function getFieldLabel(field) {
|
|
493
544
|
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
494
545
|
}
|
|
546
|
+
function getDisplayFieldLabel(field) {
|
|
547
|
+
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
548
|
+
}
|
|
495
549
|
function isFieldRequired(field) {
|
|
496
550
|
return field.required === true;
|
|
497
551
|
}
|
|
@@ -500,6 +554,13 @@ function renderValidationMessage(field) {
|
|
|
500
554
|
if (!error) return "";
|
|
501
555
|
return $md.inline`${error.message}`(mergeDslContexts(error.context, dslContext));
|
|
502
556
|
}
|
|
557
|
+
function renderMarkdownField(field) {
|
|
558
|
+
const source = getLocalizedText(field.locale, locale.value) ?? "";
|
|
559
|
+
if (source.trim().length === 0) {
|
|
560
|
+
return "";
|
|
561
|
+
}
|
|
562
|
+
return $md.block`${source}`(mergeDslContexts({ form: modelValue.value }, dslContext));
|
|
563
|
+
}
|
|
503
564
|
function isCalendarDateDisabled(field, date) {
|
|
504
565
|
if (!field.disableDate) {
|
|
505
566
|
return false;
|
|
@@ -556,8 +617,8 @@ watch(config, (value) => {
|
|
|
556
617
|
}, { immediate: true });
|
|
557
618
|
watchEffect(() => {
|
|
558
619
|
const activePaths = /* @__PURE__ */ new Set();
|
|
559
|
-
for (const field of displayConfig.value
|
|
560
|
-
if (!isPassiveField(field) && !isFieldHidden(field) && !isFieldDisabled(field)) {
|
|
620
|
+
for (const field of getConfigFields(displayConfig.value)) {
|
|
621
|
+
if (!isPassiveField(field) && !isFieldHidden(field) && !isLabeledDisplayField(field) && !isFieldDisabled(field)) {
|
|
561
622
|
activePaths.add(field.path);
|
|
562
623
|
}
|
|
563
624
|
}
|
|
@@ -585,11 +646,13 @@ export {
|
|
|
585
646
|
CURRENT_COMPATIBILITY_DATE,
|
|
586
647
|
EmptyFieldC,
|
|
587
648
|
FieldC,
|
|
649
|
+
FieldGroupC,
|
|
588
650
|
FieldsBodyC,
|
|
589
651
|
FieldsBodyInputC,
|
|
590
652
|
FieldsConfigC,
|
|
591
653
|
FieldsConfigInputC,
|
|
592
654
|
KIND,
|
|
655
|
+
MarkdownFieldC,
|
|
593
656
|
NumberFieldC,
|
|
594
657
|
SelectFieldC,
|
|
595
658
|
SlotFieldC,
|
|
@@ -607,7 +670,7 @@ export {
|
|
|
607
670
|
:class="[
|
|
608
671
|
'relative p-1 -m-1 border border-dashed',
|
|
609
672
|
isCheating ? 'border-(--primary)/20 rounded hover:border-(--primary)/40 transition-colors duration-150 group cursor-pointer' : 'border-transparent',
|
|
610
|
-
isConfigBordered(displayConfig) ? '!p-0 !m-0 border-
|
|
673
|
+
isConfigBordered(displayConfig) ? '!p-0 !m-0 border-transparent' : ''
|
|
611
674
|
]"
|
|
612
675
|
:style="getConfigStyle(displayConfig)"
|
|
613
676
|
>
|
|
@@ -635,176 +698,455 @@ export {
|
|
|
635
698
|
class="absolute inset-0 z-10 w-full h-full"
|
|
636
699
|
/>
|
|
637
700
|
|
|
638
|
-
<
|
|
639
|
-
v-for="
|
|
640
|
-
:key="
|
|
701
|
+
<FieldSet
|
|
702
|
+
v-for="group in displayConfig.groups"
|
|
703
|
+
:key="group.id"
|
|
704
|
+
data-slot="fields-group"
|
|
705
|
+
:data-group-id="group.id"
|
|
706
|
+
:class="[
|
|
707
|
+
isConfigBordered(displayConfig) ? 'overflow-hidden rounded-md border border-zinc-200' : ''
|
|
708
|
+
]"
|
|
709
|
+
:style="getGroupStyle(group, displayConfig)"
|
|
641
710
|
>
|
|
642
|
-
<
|
|
643
|
-
v-if="
|
|
644
|
-
|
|
645
|
-
|
|
711
|
+
<FieldLegend
|
|
712
|
+
v-if="getGroupTitle(group)"
|
|
713
|
+
data-slot="fields-group-legend"
|
|
714
|
+
class="px-2 pt-2"
|
|
646
715
|
>
|
|
716
|
+
{{ getGroupTitle(group) }}
|
|
717
|
+
</FieldLegend>
|
|
718
|
+
|
|
719
|
+
<template
|
|
720
|
+
v-for="field in group.fields"
|
|
721
|
+
:key="field.id"
|
|
722
|
+
>
|
|
723
|
+
<div
|
|
724
|
+
v-if="field.type === 'slot' && isConfigBordered(displayConfig)"
|
|
725
|
+
:class="'border-b border-r border-zinc-200'"
|
|
726
|
+
:style="getFieldStyle(field)"
|
|
727
|
+
>
|
|
728
|
+
<slot
|
|
729
|
+
:name="field.id"
|
|
730
|
+
:form="slotForm"
|
|
731
|
+
:style="{}"
|
|
732
|
+
:valid="valid"
|
|
733
|
+
/>
|
|
734
|
+
</div>
|
|
647
735
|
<slot
|
|
736
|
+
v-else-if="field.type === 'slot'"
|
|
648
737
|
:name="field.id"
|
|
649
738
|
:form="slotForm"
|
|
650
|
-
:style="
|
|
739
|
+
:style="getFieldStyle(field)"
|
|
651
740
|
:valid="valid"
|
|
652
741
|
/>
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
v-else-if="field.type === 'empty'"
|
|
663
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
664
|
-
:style="getFieldStyle(field)"
|
|
665
|
-
/>
|
|
666
|
-
<div
|
|
667
|
-
v-else-if="field.type === 'upload' && !isFieldHidden(field)"
|
|
668
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
669
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
670
|
-
class="flex flex-col gap-2 p-2"
|
|
671
|
-
>
|
|
672
|
-
<p class="text-sm font-medium text-zinc-700">
|
|
673
|
-
<span class="inline-flex items-start gap-0.5">
|
|
674
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
675
|
-
<sup
|
|
676
|
-
v-if="isFieldRequired(field)"
|
|
677
|
-
class="text-red-500 leading-none"
|
|
678
|
-
>*</sup>
|
|
679
|
-
</span>
|
|
680
|
-
</p>
|
|
681
|
-
<button
|
|
682
|
-
v-if="field.template"
|
|
683
|
-
type="button"
|
|
684
|
-
class="inline-flex items-center gap-1 self-start text-sm text-[--el-color-primary] hover:underline disabled:opacity-50"
|
|
685
|
-
:disabled="templateDownloading[field.id]"
|
|
686
|
-
@click="handleTemplateDownload(field)"
|
|
687
|
-
>
|
|
688
|
-
<Icon
|
|
689
|
-
:icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : 'fluent:arrow-download-20-regular'"
|
|
690
|
-
class="text-base"
|
|
691
|
-
/>
|
|
692
|
-
{{ t("upload-download-template") }}
|
|
693
|
-
</button>
|
|
694
|
-
<label
|
|
695
|
-
v-if="getUploadFiles(field).length < getUploadMaxCount(field)"
|
|
696
|
-
:class="[
|
|
697
|
-
'flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-4 py-8 transition-colors',
|
|
698
|
-
isFieldDisabled(field) ? 'cursor-not-allowed border-zinc-200 opacity-50' : 'border-zinc-300 hover:border-zinc-400 hover:bg-zinc-50'
|
|
699
|
-
]"
|
|
700
|
-
@drop="handleUploadDrop(field, $event)"
|
|
701
|
-
@dragover="handleUploadDragOver"
|
|
742
|
+
<div
|
|
743
|
+
v-else-if="field.type === 'empty'"
|
|
744
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
745
|
+
:style="getFieldStyle(field)"
|
|
746
|
+
/>
|
|
747
|
+
<Field
|
|
748
|
+
v-else-if="field.type === 'markdown' && !isFieldHidden(field)"
|
|
749
|
+
:orientation="getConfigOrientation(displayConfig)"
|
|
750
|
+
:style="getFieldContainerStyle(field, displayConfig)"
|
|
702
751
|
>
|
|
703
|
-
<
|
|
704
|
-
:
|
|
705
|
-
|
|
706
|
-
/>
|
|
707
|
-
<p
|
|
708
|
-
v-if="field.description"
|
|
709
|
-
class="text-sm text-zinc-600"
|
|
710
|
-
v-html="$md.inline`${getLocalizedText(field.description, locale) ?? ''}`()"
|
|
711
|
-
/>
|
|
712
|
-
<p
|
|
713
|
-
v-else
|
|
714
|
-
class="text-sm text-zinc-600"
|
|
752
|
+
<FieldLabel
|
|
753
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
754
|
+
:style="getFieldLabelStyle(field, displayConfig)"
|
|
715
755
|
>
|
|
716
|
-
{{
|
|
717
|
-
</
|
|
718
|
-
<
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
<input
|
|
722
|
-
type="file"
|
|
723
|
-
class="sr-only"
|
|
724
|
-
:accept="getUploadAcceptString(field)"
|
|
725
|
-
:disabled="isFieldDisabled(field)"
|
|
726
|
-
:multiple="getUploadMaxCount(field) !== 1"
|
|
727
|
-
@change="handleUploadInputChange(field, $event)"
|
|
756
|
+
<span>{{ getDisplayFieldLabel(field) }}</span>
|
|
757
|
+
</FieldLabel>
|
|
758
|
+
<FieldContent
|
|
759
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
760
|
+
:style="getFieldContentStyle(field, displayConfig)"
|
|
728
761
|
>
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
762
|
+
<div
|
|
763
|
+
data-slot="fields-markdown"
|
|
764
|
+
v-html="renderMarkdownField(field)"
|
|
765
|
+
/>
|
|
766
|
+
</FieldContent>
|
|
767
|
+
</Field>
|
|
768
|
+
<div
|
|
769
|
+
v-else-if="field.type === 'upload' && !isFieldHidden(field)"
|
|
770
|
+
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
771
|
+
:style="getFieldContainerStyle(field, displayConfig)"
|
|
772
|
+
class="flex flex-col gap-2 p-2"
|
|
733
773
|
>
|
|
734
|
-
<
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
774
|
+
<p class="text-sm font-medium text-zinc-700">
|
|
775
|
+
<span class="inline-flex items-start gap-0.5">
|
|
776
|
+
<span>{{ getFieldLabel(field) }}</span>
|
|
777
|
+
<sup
|
|
778
|
+
v-if="isFieldRequired(field)"
|
|
779
|
+
class="text-red-500 leading-none"
|
|
780
|
+
>*</sup>
|
|
781
|
+
</span>
|
|
782
|
+
</p>
|
|
783
|
+
<label
|
|
784
|
+
v-if="getUploadFiles(field).length < getUploadMaxCount(field)"
|
|
785
|
+
:class="[
|
|
786
|
+
'flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-4 py-8 transition-colors',
|
|
787
|
+
isFieldDisabled(field) ? 'cursor-not-allowed border-zinc-200 opacity-50' : 'border-zinc-300 hover:border-zinc-400 hover:bg-zinc-50'
|
|
788
|
+
]"
|
|
789
|
+
@drop="handleUploadDrop(field, $event)"
|
|
790
|
+
@dragover="handleUploadDragOver"
|
|
738
791
|
>
|
|
739
792
|
<Icon
|
|
740
|
-
:icon="
|
|
741
|
-
class="
|
|
793
|
+
:icon="field.icon ?? 'fluent:cloud-arrow-up-20-regular'"
|
|
794
|
+
class="text-4xl text-zinc-400"
|
|
742
795
|
/>
|
|
743
|
-
<span class="flex-1 truncate text-sm text-zinc-700">{{ file.name }}</span>
|
|
744
796
|
<button
|
|
797
|
+
v-if="field.template"
|
|
745
798
|
type="button"
|
|
746
|
-
class="
|
|
747
|
-
:disabled="
|
|
748
|
-
@click="
|
|
799
|
+
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"
|
|
800
|
+
:disabled="templateDownloading[field.id]"
|
|
801
|
+
@click.prevent.stop="handleTemplateDownload(field)"
|
|
749
802
|
>
|
|
750
803
|
<Icon
|
|
751
|
-
icon="
|
|
752
|
-
class="text-
|
|
804
|
+
:icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : getUploadTemplateIcon(field)"
|
|
805
|
+
class="text-base"
|
|
753
806
|
/>
|
|
807
|
+
{{ getLocalizedText(field.templateName, locale) ?? t("upload-download-template") }}
|
|
754
808
|
</button>
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
809
|
+
<p
|
|
810
|
+
v-if="field.description"
|
|
811
|
+
class="text-sm text-zinc-600"
|
|
812
|
+
v-html="$md.inline`${getLocalizedText(field.description, locale) ?? ''}`()"
|
|
813
|
+
/>
|
|
814
|
+
<p
|
|
815
|
+
v-else
|
|
816
|
+
class="text-sm text-zinc-600"
|
|
817
|
+
>
|
|
818
|
+
{{ t("upload-click-or-drag") }}
|
|
819
|
+
</p>
|
|
820
|
+
<p class="text-xs text-zinc-400">
|
|
821
|
+
{{ getUploadDescription(field) || t("upload-accept-all") }}
|
|
822
|
+
</p>
|
|
823
|
+
<input
|
|
824
|
+
type="file"
|
|
825
|
+
class="sr-only"
|
|
826
|
+
:accept="getUploadAcceptString(field)"
|
|
827
|
+
:disabled="isFieldDisabled(field)"
|
|
828
|
+
:multiple="getUploadMaxCount(field) !== 1"
|
|
829
|
+
@change="handleUploadInputChange(field, $event)"
|
|
830
|
+
>
|
|
831
|
+
</label>
|
|
832
|
+
<ul
|
|
833
|
+
v-if="getUploadFiles(field).length > 0"
|
|
834
|
+
class="flex flex-col gap-1"
|
|
835
|
+
>
|
|
836
|
+
<li
|
|
837
|
+
v-for="(file, fileIndex) in getUploadFiles(field)"
|
|
838
|
+
:key="`${field.id}:${fileIndex}:${file.name}`"
|
|
839
|
+
class="flex items-center gap-2 rounded-md border border-zinc-200 px-3 py-2"
|
|
840
|
+
>
|
|
841
|
+
<Icon
|
|
842
|
+
:icon="getFileIcon(file.name)"
|
|
843
|
+
class="shrink-0 text-lg"
|
|
844
|
+
/>
|
|
845
|
+
<span class="flex-1 truncate text-sm text-zinc-700">{{ file.name }}</span>
|
|
846
|
+
<button
|
|
847
|
+
type="button"
|
|
848
|
+
class="shrink-0 text-zinc-300 transition-colors hover:text-red-500"
|
|
849
|
+
:disabled="isFieldDisabled(field)"
|
|
850
|
+
@click="removeUploadFile(field, fileIndex)"
|
|
851
|
+
>
|
|
852
|
+
<Icon
|
|
853
|
+
icon="fluent:delete-20-regular"
|
|
854
|
+
class="text-lg"
|
|
855
|
+
/>
|
|
856
|
+
</button>
|
|
857
|
+
</li>
|
|
858
|
+
</ul>
|
|
859
|
+
</div>
|
|
860
|
+
<Field
|
|
861
|
+
v-else-if="isInteractiveField(field) && !isFieldHidden(field)"
|
|
862
|
+
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
863
|
+
:data-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
864
|
+
:orientation="getConfigOrientation(displayConfig)"
|
|
865
|
+
:style="getFieldContainerStyle(field, displayConfig)"
|
|
784
866
|
>
|
|
785
|
-
<
|
|
786
|
-
|
|
787
|
-
|
|
867
|
+
<FieldLabel
|
|
868
|
+
:for="['string', 'textarea', 'number', 'select'].includes(field.type) ? `${id}:${field.path}` : void 0"
|
|
869
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
870
|
+
:style="getFieldLabelStyle(field, displayConfig)"
|
|
871
|
+
>
|
|
872
|
+
<span class="inline-flex items-start gap-0.5">
|
|
873
|
+
<span>{{ getFieldLabel(field) }}</span>
|
|
874
|
+
<sup
|
|
875
|
+
v-if="isFieldRequired(field)"
|
|
876
|
+
class="text-red-500 leading-none"
|
|
877
|
+
>*</sup>
|
|
878
|
+
</span>
|
|
879
|
+
<span v-if="isCheating">
|
|
880
|
+
<span class="font-mono">{{ field.path }}</span>
|
|
881
|
+
</span>
|
|
882
|
+
</FieldLabel>
|
|
883
|
+
<FieldContent
|
|
884
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200 p-2' : void 0"
|
|
885
|
+
:style="getFieldContentStyle(field, displayConfig)"
|
|
788
886
|
>
|
|
789
|
-
<
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
887
|
+
<Popover
|
|
888
|
+
v-if="field.type === 'calendar'"
|
|
889
|
+
@update:open="(open) => handleCalendarOpenChange(field, open)"
|
|
890
|
+
>
|
|
891
|
+
<PopoverAnchor as-child>
|
|
892
|
+
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
893
|
+
<PopoverTrigger as-child>
|
|
894
|
+
<InputGroupInput
|
|
895
|
+
:model-value="displayCalendarValue(getProperty(modelValue, field.path), field.display, field.value)"
|
|
896
|
+
class="text-left"
|
|
897
|
+
:disabled="isFieldDisabled(field)"
|
|
898
|
+
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
899
|
+
readonly
|
|
900
|
+
@blur="handleCalendarBlur(field)"
|
|
901
|
+
/>
|
|
902
|
+
</PopoverTrigger>
|
|
903
|
+
<InputGroupAddon v-if="field.icon">
|
|
904
|
+
<Icon
|
|
905
|
+
:icon="field.icon"
|
|
906
|
+
/>
|
|
907
|
+
</InputGroupAddon>
|
|
908
|
+
<InputGroupAddon
|
|
909
|
+
v-if="hasProperty(modelValue, field.path)"
|
|
910
|
+
align="inline-end"
|
|
911
|
+
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
912
|
+
>
|
|
913
|
+
<Tooltip :delay-duration="800">
|
|
914
|
+
<TooltipTrigger>
|
|
915
|
+
<InputGroupButton as-child>
|
|
916
|
+
<button
|
|
917
|
+
type="button"
|
|
918
|
+
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
919
|
+
:disabled="isFieldDisabled(field)"
|
|
920
|
+
@click="deleteProperty(modelValue, field.path)"
|
|
921
|
+
>
|
|
922
|
+
<Icon
|
|
923
|
+
icon="fluent:dismiss-20-regular"
|
|
924
|
+
/>
|
|
925
|
+
</button>
|
|
926
|
+
</InputGroupButton>
|
|
927
|
+
</TooltipTrigger>
|
|
928
|
+
<TooltipContent>
|
|
929
|
+
{{ t("clear") }}
|
|
930
|
+
</TooltipContent>
|
|
931
|
+
</Tooltip>
|
|
932
|
+
</InputGroupAddon>
|
|
933
|
+
</InputGroup>
|
|
934
|
+
</PopoverAnchor>
|
|
935
|
+
<PopoverContent class="w-72">
|
|
936
|
+
<Calendar
|
|
937
|
+
:locale="locale"
|
|
938
|
+
:layout="field.mode"
|
|
939
|
+
:model-value="toCalendarDateValue(getProperty(modelValue, field.path), field.value)"
|
|
940
|
+
:disabled="isFieldDisabled(field)"
|
|
941
|
+
:is-date-disabled="field.disableDate ? (date) => isCalendarDateDisabled(field, date) : void 0"
|
|
942
|
+
@update:model-value="(value) => {
|
|
943
|
+
if (value === void 0) {
|
|
944
|
+
deleteProperty(modelValue, field.path);
|
|
945
|
+
} else {
|
|
946
|
+
setProperty(modelValue, field.path, format(value.toDate(getLocalTimeZone()), field.value));
|
|
947
|
+
}
|
|
948
|
+
}"
|
|
949
|
+
/>
|
|
950
|
+
</PopoverContent>
|
|
951
|
+
</Popover>
|
|
952
|
+
<template v-else>
|
|
953
|
+
<template v-if="field.type === 'select'">
|
|
954
|
+
<Popover
|
|
955
|
+
v-for="selectState in [getSelectFieldState(field)]"
|
|
956
|
+
:key="`${field.id}:select:${selectState.selectedKey ?? 'empty'}`"
|
|
957
|
+
:open="selectOpen[field.path] === true"
|
|
958
|
+
@update:open="(open) => handleSelectOpenChange(field, open)"
|
|
959
|
+
>
|
|
960
|
+
<PopoverAnchor as-child>
|
|
961
|
+
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
962
|
+
<PopoverTrigger as-child>
|
|
963
|
+
<InputGroupInput
|
|
964
|
+
:id="`${id}:${field.path}`"
|
|
965
|
+
:model-value="getSelectDisplayValue(selectState, selectState.selectedKey)"
|
|
966
|
+
:disabled="isFieldDisabled(field)"
|
|
967
|
+
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
968
|
+
:placeholder="t('select-placeholder')"
|
|
969
|
+
class="text-left"
|
|
970
|
+
readonly
|
|
971
|
+
@blur="handleSelectBlur(field)"
|
|
972
|
+
/>
|
|
973
|
+
</PopoverTrigger>
|
|
974
|
+
<InputGroupAddon v-if="field.icon">
|
|
975
|
+
<Icon
|
|
976
|
+
:icon="field.icon"
|
|
977
|
+
/>
|
|
978
|
+
</InputGroupAddon>
|
|
979
|
+
<InputGroupAddon
|
|
980
|
+
v-if="hasProperty(modelValue, field.path)"
|
|
981
|
+
align="inline-end"
|
|
982
|
+
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
983
|
+
>
|
|
984
|
+
<Tooltip :delay-duration="800">
|
|
985
|
+
<TooltipTrigger>
|
|
986
|
+
<InputGroupButton as-child>
|
|
987
|
+
<button
|
|
988
|
+
type="button"
|
|
989
|
+
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
990
|
+
:disabled="isFieldDisabled(field)"
|
|
991
|
+
@click="clearSelectField(field)"
|
|
992
|
+
>
|
|
993
|
+
<Icon
|
|
994
|
+
icon="fluent:dismiss-20-regular"
|
|
995
|
+
/>
|
|
996
|
+
</button>
|
|
997
|
+
</InputGroupButton>
|
|
998
|
+
</TooltipTrigger>
|
|
999
|
+
<TooltipContent>
|
|
1000
|
+
{{ t("clear") }}
|
|
1001
|
+
</TooltipContent>
|
|
1002
|
+
</Tooltip>
|
|
1003
|
+
</InputGroupAddon>
|
|
1004
|
+
</InputGroup>
|
|
1005
|
+
</PopoverAnchor>
|
|
1006
|
+
|
|
1007
|
+
<PopoverContent
|
|
1008
|
+
class="p-0"
|
|
1009
|
+
:style="{ width: 'var(--reka-popover-trigger-width)' }"
|
|
1010
|
+
>
|
|
1011
|
+
<Command
|
|
1012
|
+
:model-value="selectState.selectedKey"
|
|
1013
|
+
:disabled="isFieldDisabled(field)"
|
|
1014
|
+
selection-behavior="toggle"
|
|
1015
|
+
@update:model-value="(value) => handleSelectCommandValueChange(field, selectState, value)"
|
|
1016
|
+
>
|
|
1017
|
+
<CommandInput :placeholder="t('select-search-placeholder')" />
|
|
1018
|
+
<CommandList>
|
|
1019
|
+
<CommandEmpty as-child>
|
|
1020
|
+
<section class="h-32 flex flex-col text-lg items-center justify-center gap-2 select-none">
|
|
1021
|
+
<Icon
|
|
1022
|
+
icon="fluent:app-recent-20-regular"
|
|
1023
|
+
class="text-zinc-400 text-2xl!"
|
|
1024
|
+
/>
|
|
1025
|
+
<p class="text-zinc-500">
|
|
1026
|
+
{{ t("select-empty") }}
|
|
1027
|
+
</p>
|
|
1028
|
+
</section>
|
|
1029
|
+
</CommandEmpty>
|
|
1030
|
+
<CommandGroup>
|
|
1031
|
+
<CommandItem
|
|
1032
|
+
v-for="option in selectState.options"
|
|
1033
|
+
:key="option.key"
|
|
1034
|
+
data-slot="select-option"
|
|
1035
|
+
:value="option.key"
|
|
1036
|
+
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"
|
|
1037
|
+
>
|
|
1038
|
+
{{ option.label }}
|
|
1039
|
+
</CommandItem>
|
|
1040
|
+
</CommandGroup>
|
|
1041
|
+
</CommandList>
|
|
1042
|
+
</Command>
|
|
1043
|
+
</PopoverContent>
|
|
1044
|
+
</Popover>
|
|
1045
|
+
</template>
|
|
1046
|
+
|
|
1047
|
+
<template v-else-if="field.type === 'radio'">
|
|
1048
|
+
<RadioGroupRoot
|
|
1049
|
+
v-for="radioState in [getSelectFieldState(field)]"
|
|
1050
|
+
:key="`${field.id}:radio:${radioState.selectedKey ?? 'empty'}`"
|
|
1051
|
+
:model-value="radioState.selectedKey"
|
|
1052
|
+
:disabled="isFieldDisabled(field)"
|
|
1053
|
+
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1054
|
+
class="flex flex-wrap gap-x-4 gap-y-1.5"
|
|
1055
|
+
@update:model-value="(value) => handleSelectValueChange(field, radioState, value)"
|
|
1056
|
+
@focusout="validateField(field)"
|
|
1057
|
+
>
|
|
1058
|
+
<label
|
|
1059
|
+
v-for="option in radioState.options"
|
|
1060
|
+
:key="option.key"
|
|
1061
|
+
class="flex items-center gap-1.5 text-sm cursor-pointer data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
|
|
1062
|
+
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1063
|
+
>
|
|
1064
|
+
<RadioGroupItem
|
|
1065
|
+
:value="option.key"
|
|
1066
|
+
data-slot="radio-group-item"
|
|
1067
|
+
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"
|
|
1068
|
+
>
|
|
1069
|
+
<RadioGroupIndicator class="flex items-center justify-center">
|
|
1070
|
+
<span class="size-1.5 rounded-full bg-white" />
|
|
1071
|
+
</RadioGroupIndicator>
|
|
1072
|
+
</RadioGroupItem>
|
|
1073
|
+
{{ option.label }}
|
|
1074
|
+
</label>
|
|
1075
|
+
</RadioGroupRoot>
|
|
1076
|
+
</template>
|
|
1077
|
+
|
|
1078
|
+
<InputGroup
|
|
1079
|
+
v-else
|
|
1080
|
+
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
1081
|
+
:class="field.type === 'textarea' ? 'h-auto flex-col items-stretch' : void 0"
|
|
1082
|
+
>
|
|
1083
|
+
<div
|
|
1084
|
+
v-if="field.type === 'textarea'"
|
|
1085
|
+
class="flex min-w-0 w-full items-center"
|
|
1086
|
+
>
|
|
1087
|
+
<InputGroupTextarea
|
|
1088
|
+
:id="`${id}:${field.path}`"
|
|
1089
|
+
:model-value="getProperty(modelValue, field.path)"
|
|
1090
|
+
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
795
1091
|
:disabled="isFieldDisabled(field)"
|
|
796
1092
|
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
797
|
-
|
|
798
|
-
|
|
1093
|
+
@update:model-value="(value) => {
|
|
1094
|
+
if (!value && !field.discardEmptyString) {
|
|
1095
|
+
deleteProperty(modelValue, field.path);
|
|
1096
|
+
} else {
|
|
1097
|
+
setProperty(modelValue, field.path, value);
|
|
1098
|
+
}
|
|
1099
|
+
}"
|
|
1100
|
+
@blur="validateField(field)"
|
|
799
1101
|
/>
|
|
800
|
-
|
|
801
|
-
|
|
1102
|
+
<InputGroupAddon v-if="field.icon">
|
|
1103
|
+
<Icon
|
|
1104
|
+
:icon="field.icon"
|
|
1105
|
+
/>
|
|
1106
|
+
</InputGroupAddon>
|
|
1107
|
+
</div>
|
|
1108
|
+
<InputGroupInput
|
|
1109
|
+
v-if="field.type === 'string'"
|
|
1110
|
+
:id="`${id}:${field.path}`"
|
|
1111
|
+
:treat-empty-as-different-state-from-null="!!field.discardEmptyString"
|
|
1112
|
+
:model-value="getProperty(modelValue, field.path)"
|
|
1113
|
+
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1114
|
+
:disabled="isFieldDisabled(field)"
|
|
1115
|
+
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1116
|
+
@update:model-value="(value) => {
|
|
1117
|
+
if (!value && !field.discardEmptyString) {
|
|
1118
|
+
deleteProperty(modelValue, field.path);
|
|
1119
|
+
} else {
|
|
1120
|
+
setProperty(modelValue, field.path, value);
|
|
1121
|
+
}
|
|
1122
|
+
}"
|
|
1123
|
+
@blur="validateField(field)"
|
|
1124
|
+
/>
|
|
1125
|
+
<InputGroupNumberField
|
|
1126
|
+
v-if="field.type === 'number'"
|
|
1127
|
+
:id="`${id}:${field.path}`"
|
|
1128
|
+
:model-value="getProperty(modelValue, field.path) ?? null"
|
|
1129
|
+
:min="field.min ? $dsl.evaluate`${field.min}`() : void 0"
|
|
1130
|
+
:max="field.max ? $dsl.evaluate`${field.max}`() : void 0"
|
|
1131
|
+
:step="field.step ? $dsl.evaluate`${field.step}`() : void 0"
|
|
1132
|
+
:disabled="isFieldDisabled(field)"
|
|
1133
|
+
:invalid="isFieldInvalid(field)"
|
|
1134
|
+
@update:model-value="(value) => {
|
|
1135
|
+
if (!value && value !== 0) {
|
|
1136
|
+
deleteProperty(modelValue, field.path);
|
|
1137
|
+
} else {
|
|
1138
|
+
setProperty(modelValue, field.path, value);
|
|
1139
|
+
}
|
|
1140
|
+
}"
|
|
1141
|
+
@blur="validateField(field)"
|
|
1142
|
+
/>
|
|
1143
|
+
<InputGroupAddon v-if="field.type !== 'textarea' && field.icon">
|
|
802
1144
|
<Icon
|
|
803
1145
|
:icon="field.icon"
|
|
804
1146
|
/>
|
|
805
1147
|
</InputGroupAddon>
|
|
806
1148
|
<InputGroupAddon
|
|
807
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1149
|
+
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
808
1150
|
align="inline-end"
|
|
809
1151
|
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
810
1152
|
>
|
|
@@ -828,299 +1170,60 @@ export {
|
|
|
828
1170
|
</TooltipContent>
|
|
829
1171
|
</Tooltip>
|
|
830
1172
|
</InputGroupAddon>
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
<Calendar
|
|
835
|
-
:locale="locale"
|
|
836
|
-
:layout="field.mode"
|
|
837
|
-
:model-value="toCalendarDateValue(getProperty(modelValue, field.path), field.value)"
|
|
838
|
-
:disabled="isFieldDisabled(field)"
|
|
839
|
-
:is-date-disabled="field.disableDate ? (date) => isCalendarDateDisabled(field, date) : void 0"
|
|
840
|
-
@update:model-value="(value) => {
|
|
841
|
-
if (value === void 0) {
|
|
842
|
-
deleteProperty(modelValue, field.path);
|
|
843
|
-
} else {
|
|
844
|
-
setProperty(modelValue, field.path, format(value.toDate(getLocalTimeZone()), field.value));
|
|
845
|
-
}
|
|
846
|
-
}"
|
|
847
|
-
/>
|
|
848
|
-
</PopoverContent>
|
|
849
|
-
</Popover>
|
|
850
|
-
<template v-else>
|
|
851
|
-
<template v-if="field.type === 'select'">
|
|
852
|
-
<Popover
|
|
853
|
-
v-for="selectState in [getSelectFieldState(field)]"
|
|
854
|
-
:key="`${field.id}:select:${selectState.selectedKey ?? 'empty'}`"
|
|
855
|
-
:open="selectOpen[field.path] === true"
|
|
856
|
-
@update:open="(open) => handleSelectOpenChange(field, open)"
|
|
857
|
-
>
|
|
858
|
-
<PopoverAnchor as-child>
|
|
859
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
860
|
-
<PopoverTrigger as-child>
|
|
861
|
-
<InputGroupInput
|
|
862
|
-
:id="`${id}:${field.path}`"
|
|
863
|
-
:model-value="getSelectDisplayValue(selectState, selectState.selectedKey)"
|
|
864
|
-
:disabled="isFieldDisabled(field)"
|
|
865
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
866
|
-
:placeholder="t('select-placeholder')"
|
|
867
|
-
class="text-left"
|
|
868
|
-
readonly
|
|
869
|
-
@blur="handleSelectBlur(field)"
|
|
870
|
-
/>
|
|
871
|
-
</PopoverTrigger>
|
|
872
|
-
<InputGroupAddon v-if="field.icon">
|
|
873
|
-
<Icon
|
|
874
|
-
:icon="field.icon"
|
|
875
|
-
/>
|
|
876
|
-
</InputGroupAddon>
|
|
877
|
-
<InputGroupAddon
|
|
878
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
879
|
-
align="inline-end"
|
|
880
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
881
|
-
>
|
|
882
|
-
<Tooltip :delay-duration="800">
|
|
883
|
-
<TooltipTrigger>
|
|
884
|
-
<InputGroupButton as-child>
|
|
885
|
-
<button
|
|
886
|
-
type="button"
|
|
887
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
888
|
-
:disabled="isFieldDisabled(field)"
|
|
889
|
-
@click="clearSelectField(field)"
|
|
890
|
-
>
|
|
891
|
-
<Icon
|
|
892
|
-
icon="fluent:dismiss-20-regular"
|
|
893
|
-
/>
|
|
894
|
-
</button>
|
|
895
|
-
</InputGroupButton>
|
|
896
|
-
</TooltipTrigger>
|
|
897
|
-
<TooltipContent>
|
|
898
|
-
{{ t("clear") }}
|
|
899
|
-
</TooltipContent>
|
|
900
|
-
</Tooltip>
|
|
901
|
-
</InputGroupAddon>
|
|
902
|
-
</InputGroup>
|
|
903
|
-
</PopoverAnchor>
|
|
904
|
-
|
|
905
|
-
<PopoverContent
|
|
906
|
-
class="p-0"
|
|
907
|
-
:style="{ width: 'var(--reka-popover-trigger-width)' }"
|
|
1173
|
+
<InputGroupAddon
|
|
1174
|
+
v-if="field.type === 'string' && field.maxLength && getProperty(modelValue, field.path)"
|
|
1175
|
+
align="inline-end"
|
|
908
1176
|
>
|
|
909
|
-
<
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1177
|
+
<span class="text-xs text-zinc-400 font-mono">
|
|
1178
|
+
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1179
|
+
</span>
|
|
1180
|
+
</InputGroupAddon>
|
|
1181
|
+
<InputGroupAddon
|
|
1182
|
+
v-if="field.type === 'textarea' && (hasProperty(modelValue, field.path) || field.maxLength && getProperty(modelValue, field.path))"
|
|
1183
|
+
align="block-end"
|
|
1184
|
+
>
|
|
1185
|
+
<Tooltip
|
|
1186
|
+
v-if="hasProperty(modelValue, field.path)"
|
|
1187
|
+
:delay-duration="800"
|
|
914
1188
|
>
|
|
915
|
-
<
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1189
|
+
<TooltipTrigger>
|
|
1190
|
+
<InputGroupButton as-child>
|
|
1191
|
+
<button
|
|
1192
|
+
type="button"
|
|
1193
|
+
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1194
|
+
:disabled="isFieldDisabled(field)"
|
|
1195
|
+
@click="deleteProperty(modelValue, field.path)"
|
|
1196
|
+
>
|
|
919
1197
|
<Icon
|
|
920
|
-
icon="fluent:
|
|
921
|
-
class="text-zinc-400 text-2xl!"
|
|
1198
|
+
icon="fluent:dismiss-20-regular"
|
|
922
1199
|
/>
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
:value="option.key"
|
|
934
|
-
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"
|
|
935
|
-
>
|
|
936
|
-
{{ option.label }}
|
|
937
|
-
</CommandItem>
|
|
938
|
-
</CommandGroup>
|
|
939
|
-
</CommandList>
|
|
940
|
-
</Command>
|
|
941
|
-
</PopoverContent>
|
|
942
|
-
</Popover>
|
|
943
|
-
</template>
|
|
944
|
-
|
|
945
|
-
<template v-else-if="field.type === 'radio'">
|
|
946
|
-
<RadioGroupRoot
|
|
947
|
-
v-for="radioState in [getSelectFieldState(field)]"
|
|
948
|
-
:key="`${field.id}:radio:${radioState.selectedKey ?? 'empty'}`"
|
|
949
|
-
:model-value="radioState.selectedKey"
|
|
950
|
-
:disabled="isFieldDisabled(field)"
|
|
951
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
952
|
-
class="flex flex-wrap gap-x-4 gap-y-1.5"
|
|
953
|
-
@update:model-value="(value) => handleSelectValueChange(field, radioState, value)"
|
|
954
|
-
@focusout="validateField(field)"
|
|
955
|
-
>
|
|
956
|
-
<label
|
|
957
|
-
v-for="option in radioState.options"
|
|
958
|
-
:key="option.key"
|
|
959
|
-
class="flex items-center gap-1.5 text-sm cursor-pointer data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
|
|
960
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
961
|
-
>
|
|
962
|
-
<RadioGroupItem
|
|
963
|
-
:value="option.key"
|
|
964
|
-
data-slot="radio-group-item"
|
|
965
|
-
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"
|
|
1200
|
+
</button>
|
|
1201
|
+
</InputGroupButton>
|
|
1202
|
+
</TooltipTrigger>
|
|
1203
|
+
<TooltipContent>
|
|
1204
|
+
{{ t("clear") }}
|
|
1205
|
+
</TooltipContent>
|
|
1206
|
+
</Tooltip>
|
|
1207
|
+
<span
|
|
1208
|
+
v-if="field.maxLength && getProperty(modelValue, field.path)"
|
|
1209
|
+
class="text-xs text-zinc-400 font-mono"
|
|
966
1210
|
>
|
|
967
|
-
<
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
{{ option.label }}
|
|
972
|
-
</label>
|
|
973
|
-
</RadioGroupRoot>
|
|
1211
|
+
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1212
|
+
</span>
|
|
1213
|
+
</InputGroupAddon>
|
|
1214
|
+
</InputGroup>
|
|
974
1215
|
</template>
|
|
975
1216
|
|
|
976
|
-
<
|
|
977
|
-
v-
|
|
978
|
-
:
|
|
979
|
-
:class="field.type === 'textarea' ? 'h-auto flex-col items-stretch' : void 0"
|
|
1217
|
+
<FieldError
|
|
1218
|
+
v-if="isFieldInvalid(field)"
|
|
1219
|
+
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
980
1220
|
>
|
|
981
|
-
<
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
988
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
989
|
-
:disabled="isFieldDisabled(field)"
|
|
990
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
991
|
-
@update:model-value="(value) => {
|
|
992
|
-
if (!value && !field.discardEmptyString) {
|
|
993
|
-
deleteProperty(modelValue, field.path);
|
|
994
|
-
} else {
|
|
995
|
-
setProperty(modelValue, field.path, value);
|
|
996
|
-
}
|
|
997
|
-
}"
|
|
998
|
-
@blur="validateField(field)"
|
|
999
|
-
/>
|
|
1000
|
-
<InputGroupAddon v-if="field.icon">
|
|
1001
|
-
<Icon
|
|
1002
|
-
:icon="field.icon"
|
|
1003
|
-
/>
|
|
1004
|
-
</InputGroupAddon>
|
|
1005
|
-
</div>
|
|
1006
|
-
<InputGroupInput
|
|
1007
|
-
v-if="field.type === 'string'"
|
|
1008
|
-
:id="`${id}:${field.path}`"
|
|
1009
|
-
:treat-empty-as-different-state-from-null="!!field.discardEmptyString"
|
|
1010
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1011
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1012
|
-
:disabled="isFieldDisabled(field)"
|
|
1013
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1014
|
-
@update:model-value="(value) => {
|
|
1015
|
-
if (!value && !field.discardEmptyString) {
|
|
1016
|
-
deleteProperty(modelValue, field.path);
|
|
1017
|
-
} else {
|
|
1018
|
-
setProperty(modelValue, field.path, value);
|
|
1019
|
-
}
|
|
1020
|
-
}"
|
|
1021
|
-
@blur="validateField(field)"
|
|
1022
|
-
/>
|
|
1023
|
-
<InputGroupNumberField
|
|
1024
|
-
v-if="field.type === 'number'"
|
|
1025
|
-
:id="`${id}:${field.path}`"
|
|
1026
|
-
:model-value="getProperty(modelValue, field.path) ?? null"
|
|
1027
|
-
:min="field.min ? $dsl.evaluate`${field.min}`() : void 0"
|
|
1028
|
-
:max="field.max ? $dsl.evaluate`${field.max}`() : void 0"
|
|
1029
|
-
:step="field.step ? $dsl.evaluate`${field.step}`() : void 0"
|
|
1030
|
-
:disabled="isFieldDisabled(field)"
|
|
1031
|
-
:invalid="isFieldInvalid(field)"
|
|
1032
|
-
@update:model-value="(value) => {
|
|
1033
|
-
if (!value && value !== 0) {
|
|
1034
|
-
deleteProperty(modelValue, field.path);
|
|
1035
|
-
} else {
|
|
1036
|
-
setProperty(modelValue, field.path, value);
|
|
1037
|
-
}
|
|
1038
|
-
}"
|
|
1039
|
-
@blur="validateField(field)"
|
|
1040
|
-
/>
|
|
1041
|
-
<InputGroupAddon v-if="field.type !== 'textarea' && field.icon">
|
|
1042
|
-
<Icon
|
|
1043
|
-
:icon="field.icon"
|
|
1044
|
-
/>
|
|
1045
|
-
</InputGroupAddon>
|
|
1046
|
-
<InputGroupAddon
|
|
1047
|
-
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
1048
|
-
align="inline-end"
|
|
1049
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1050
|
-
>
|
|
1051
|
-
<Tooltip :delay-duration="800">
|
|
1052
|
-
<TooltipTrigger>
|
|
1053
|
-
<InputGroupButton as-child>
|
|
1054
|
-
<button
|
|
1055
|
-
type="button"
|
|
1056
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1057
|
-
:disabled="isFieldDisabled(field)"
|
|
1058
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1059
|
-
>
|
|
1060
|
-
<Icon
|
|
1061
|
-
icon="fluent:dismiss-20-regular"
|
|
1062
|
-
/>
|
|
1063
|
-
</button>
|
|
1064
|
-
</InputGroupButton>
|
|
1065
|
-
</TooltipTrigger>
|
|
1066
|
-
<TooltipContent>
|
|
1067
|
-
{{ t("clear") }}
|
|
1068
|
-
</TooltipContent>
|
|
1069
|
-
</Tooltip>
|
|
1070
|
-
</InputGroupAddon>
|
|
1071
|
-
<InputGroupAddon
|
|
1072
|
-
v-if="field.type === 'string' && field.maxLength && getProperty(modelValue, field.path)"
|
|
1073
|
-
align="inline-end"
|
|
1074
|
-
>
|
|
1075
|
-
<span class="text-xs text-zinc-400 font-mono">
|
|
1076
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1077
|
-
</span>
|
|
1078
|
-
</InputGroupAddon>
|
|
1079
|
-
<InputGroupAddon
|
|
1080
|
-
v-if="field.type === 'textarea' && (hasProperty(modelValue, field.path) || field.maxLength && getProperty(modelValue, field.path))"
|
|
1081
|
-
align="block-end"
|
|
1082
|
-
>
|
|
1083
|
-
<Tooltip
|
|
1084
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1085
|
-
:delay-duration="800"
|
|
1086
|
-
>
|
|
1087
|
-
<TooltipTrigger>
|
|
1088
|
-
<InputGroupButton as-child>
|
|
1089
|
-
<button
|
|
1090
|
-
type="button"
|
|
1091
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1092
|
-
:disabled="isFieldDisabled(field)"
|
|
1093
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1094
|
-
>
|
|
1095
|
-
<Icon
|
|
1096
|
-
icon="fluent:dismiss-20-regular"
|
|
1097
|
-
/>
|
|
1098
|
-
</button>
|
|
1099
|
-
</InputGroupButton>
|
|
1100
|
-
</TooltipTrigger>
|
|
1101
|
-
<TooltipContent>
|
|
1102
|
-
{{ t("clear") }}
|
|
1103
|
-
</TooltipContent>
|
|
1104
|
-
</Tooltip>
|
|
1105
|
-
<span
|
|
1106
|
-
v-if="field.maxLength && getProperty(modelValue, field.path)"
|
|
1107
|
-
class="text-xs text-zinc-400 font-mono"
|
|
1108
|
-
>
|
|
1109
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1110
|
-
</span>
|
|
1111
|
-
</InputGroupAddon>
|
|
1112
|
-
</InputGroup>
|
|
1113
|
-
</template>
|
|
1114
|
-
|
|
1115
|
-
<FieldError
|
|
1116
|
-
v-if="isFieldInvalid(field)"
|
|
1117
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
1118
|
-
>
|
|
1119
|
-
<span v-html="renderValidationMessage(field)" />
|
|
1120
|
-
</FieldError>
|
|
1121
|
-
</FieldContent>
|
|
1122
|
-
</Field>
|
|
1123
|
-
</template>
|
|
1221
|
+
<span v-html="renderValidationMessage(field)" />
|
|
1222
|
+
</FieldError>
|
|
1223
|
+
</FieldContent>
|
|
1224
|
+
</Field>
|
|
1225
|
+
</template>
|
|
1226
|
+
</FieldSet>
|
|
1124
1227
|
|
|
1125
1228
|
<slot />
|
|
1126
1229
|
</div>
|