@shwfed/nuxt 0.11.37 → 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 -350
- package/dist/runtime/components/fields.vue +2 -1
- package/dist/runtime/components/fields.vue.d.ts +396 -350
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +838 -358
- package/dist/runtime/components/ui/fields/Fields.vue +520 -435
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +838 -358
- package/dist/runtime/components/ui/fields/schema.d.ts +3283 -30
- package/dist/runtime/components/ui/fields/schema.js +85 -9
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +394 -348
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +734 -172
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +394 -348
- package/dist/runtime/components/ui/table/Table.vue +1 -0
- 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 });
|
|
@@ -492,8 +525,8 @@ function validateField(field) {
|
|
|
492
525
|
}
|
|
493
526
|
function validateFields() {
|
|
494
527
|
const nextValidationErrors = {};
|
|
495
|
-
for (const field of displayConfig.value
|
|
496
|
-
if (isPassiveField(field)) {
|
|
528
|
+
for (const field of getConfigFields(displayConfig.value)) {
|
|
529
|
+
if (isPassiveField(field) || isLabeledDisplayField(field)) {
|
|
497
530
|
continue;
|
|
498
531
|
}
|
|
499
532
|
const failure = getValidationFailure(field);
|
|
@@ -510,6 +543,9 @@ function isFieldInvalid(field) {
|
|
|
510
543
|
function getFieldLabel(field) {
|
|
511
544
|
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
512
545
|
}
|
|
546
|
+
function getDisplayFieldLabel(field) {
|
|
547
|
+
return getLocalizedText(field.title, locale.value) ?? field.id;
|
|
548
|
+
}
|
|
513
549
|
function isFieldRequired(field) {
|
|
514
550
|
return field.required === true;
|
|
515
551
|
}
|
|
@@ -518,6 +554,13 @@ function renderValidationMessage(field) {
|
|
|
518
554
|
if (!error) return "";
|
|
519
555
|
return $md.inline`${error.message}`(mergeDslContexts(error.context, dslContext));
|
|
520
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
|
+
}
|
|
521
564
|
function isCalendarDateDisabled(field, date) {
|
|
522
565
|
if (!field.disableDate) {
|
|
523
566
|
return false;
|
|
@@ -574,8 +617,8 @@ watch(config, (value) => {
|
|
|
574
617
|
}, { immediate: true });
|
|
575
618
|
watchEffect(() => {
|
|
576
619
|
const activePaths = /* @__PURE__ */ new Set();
|
|
577
|
-
for (const field of displayConfig.value
|
|
578
|
-
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)) {
|
|
579
622
|
activePaths.add(field.path);
|
|
580
623
|
}
|
|
581
624
|
}
|
|
@@ -603,11 +646,13 @@ export {
|
|
|
603
646
|
CURRENT_COMPATIBILITY_DATE,
|
|
604
647
|
EmptyFieldC,
|
|
605
648
|
FieldC,
|
|
649
|
+
FieldGroupC,
|
|
606
650
|
FieldsBodyC,
|
|
607
651
|
FieldsBodyInputC,
|
|
608
652
|
FieldsConfigC,
|
|
609
653
|
FieldsConfigInputC,
|
|
610
654
|
KIND,
|
|
655
|
+
MarkdownFieldC,
|
|
611
656
|
NumberFieldC,
|
|
612
657
|
SelectFieldC,
|
|
613
658
|
SlotFieldC,
|
|
@@ -625,7 +670,7 @@ export {
|
|
|
625
670
|
:class="[
|
|
626
671
|
'relative p-1 -m-1 border border-dashed',
|
|
627
672
|
isCheating ? 'border-(--primary)/20 rounded hover:border-(--primary)/40 transition-colors duration-150 group cursor-pointer' : 'border-transparent',
|
|
628
|
-
isConfigBordered(displayConfig) ? '!p-0 !m-0 border-
|
|
673
|
+
isConfigBordered(displayConfig) ? '!p-0 !m-0 border-transparent' : ''
|
|
629
674
|
]"
|
|
630
675
|
:style="getConfigStyle(displayConfig)"
|
|
631
676
|
>
|
|
@@ -653,176 +698,455 @@ export {
|
|
|
653
698
|
class="absolute inset-0 z-10 w-full h-full"
|
|
654
699
|
/>
|
|
655
700
|
|
|
656
|
-
<
|
|
657
|
-
v-for="
|
|
658
|
-
: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)"
|
|
659
710
|
>
|
|
660
|
-
<
|
|
661
|
-
v-if="
|
|
662
|
-
|
|
663
|
-
|
|
711
|
+
<FieldLegend
|
|
712
|
+
v-if="getGroupTitle(group)"
|
|
713
|
+
data-slot="fields-group-legend"
|
|
714
|
+
class="px-2 pt-2"
|
|
715
|
+
>
|
|
716
|
+
{{ getGroupTitle(group) }}
|
|
717
|
+
</FieldLegend>
|
|
718
|
+
|
|
719
|
+
<template
|
|
720
|
+
v-for="field in group.fields"
|
|
721
|
+
:key="field.id"
|
|
664
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>
|
|
665
735
|
<slot
|
|
736
|
+
v-else-if="field.type === 'slot'"
|
|
666
737
|
:name="field.id"
|
|
667
738
|
:form="slotForm"
|
|
668
|
-
:style="
|
|
739
|
+
:style="getFieldStyle(field)"
|
|
669
740
|
:valid="valid"
|
|
670
741
|
/>
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
v-else-if="field.type === 'empty'"
|
|
681
|
-
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
682
|
-
:style="getFieldStyle(field)"
|
|
683
|
-
/>
|
|
684
|
-
<div
|
|
685
|
-
v-else-if="field.type === 'upload' && !isFieldHidden(field)"
|
|
686
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
687
|
-
:style="getFieldContainerStyle(field, displayConfig)"
|
|
688
|
-
class="flex flex-col gap-2 p-2"
|
|
689
|
-
>
|
|
690
|
-
<p class="text-sm font-medium text-zinc-700">
|
|
691
|
-
<span class="inline-flex items-start gap-0.5">
|
|
692
|
-
<span>{{ getFieldLabel(field) }}</span>
|
|
693
|
-
<sup
|
|
694
|
-
v-if="isFieldRequired(field)"
|
|
695
|
-
class="text-red-500 leading-none"
|
|
696
|
-
>*</sup>
|
|
697
|
-
</span>
|
|
698
|
-
</p>
|
|
699
|
-
<label
|
|
700
|
-
v-if="getUploadFiles(field).length < getUploadMaxCount(field)"
|
|
701
|
-
:class="[
|
|
702
|
-
'flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-4 py-8 transition-colors',
|
|
703
|
-
isFieldDisabled(field) ? 'cursor-not-allowed border-zinc-200 opacity-50' : 'border-zinc-300 hover:border-zinc-400 hover:bg-zinc-50'
|
|
704
|
-
]"
|
|
705
|
-
@drop="handleUploadDrop(field, $event)"
|
|
706
|
-
@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)"
|
|
707
751
|
>
|
|
708
|
-
<
|
|
709
|
-
:
|
|
710
|
-
|
|
711
|
-
/>
|
|
712
|
-
<button
|
|
713
|
-
v-if="field.template"
|
|
714
|
-
type="button"
|
|
715
|
-
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"
|
|
716
|
-
:disabled="templateDownloading[field.id]"
|
|
717
|
-
@click.prevent.stop="handleTemplateDownload(field)"
|
|
752
|
+
<FieldLabel
|
|
753
|
+
:class="isConfigBordered(displayConfig) ? 'border-b border-r border-zinc-200' : void 0"
|
|
754
|
+
:style="getFieldLabelStyle(field, displayConfig)"
|
|
718
755
|
>
|
|
719
|
-
<
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
</button>
|
|
725
|
-
<p
|
|
726
|
-
v-if="field.description"
|
|
727
|
-
class="text-sm text-zinc-600"
|
|
728
|
-
v-html="$md.inline`${getLocalizedText(field.description, locale) ?? ''}`()"
|
|
729
|
-
/>
|
|
730
|
-
<p
|
|
731
|
-
v-else
|
|
732
|
-
class="text-sm text-zinc-600"
|
|
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)"
|
|
733
761
|
>
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
</
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
@change="handleUploadInputChange(field, $event)"
|
|
746
|
-
>
|
|
747
|
-
</label>
|
|
748
|
-
<ul
|
|
749
|
-
v-if="getUploadFiles(field).length > 0"
|
|
750
|
-
class="flex flex-col gap-1"
|
|
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"
|
|
751
773
|
>
|
|
752
|
-
<
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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"
|
|
756
791
|
>
|
|
757
792
|
<Icon
|
|
758
|
-
:icon="
|
|
759
|
-
class="
|
|
793
|
+
:icon="field.icon ?? 'fluent:cloud-arrow-up-20-regular'"
|
|
794
|
+
class="text-4xl text-zinc-400"
|
|
760
795
|
/>
|
|
761
|
-
<span class="flex-1 truncate text-sm text-zinc-700">{{ file.name }}</span>
|
|
762
796
|
<button
|
|
797
|
+
v-if="field.template"
|
|
763
798
|
type="button"
|
|
764
|
-
class="
|
|
765
|
-
:disabled="
|
|
766
|
-
@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)"
|
|
767
802
|
>
|
|
768
803
|
<Icon
|
|
769
|
-
icon="
|
|
770
|
-
class="text-
|
|
804
|
+
:icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : getUploadTemplateIcon(field)"
|
|
805
|
+
class="text-base"
|
|
771
806
|
/>
|
|
807
|
+
{{ getLocalizedText(field.templateName, locale) ?? t("upload-download-template") }}
|
|
772
808
|
</button>
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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)"
|
|
802
866
|
>
|
|
803
|
-
<
|
|
804
|
-
|
|
805
|
-
|
|
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)"
|
|
806
886
|
>
|
|
807
|
-
<
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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"
|
|
813
1091
|
:disabled="isFieldDisabled(field)"
|
|
814
1092
|
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
815
|
-
|
|
816
|
-
|
|
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)"
|
|
817
1101
|
/>
|
|
818
|
-
|
|
819
|
-
|
|
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">
|
|
820
1144
|
<Icon
|
|
821
1145
|
:icon="field.icon"
|
|
822
1146
|
/>
|
|
823
1147
|
</InputGroupAddon>
|
|
824
1148
|
<InputGroupAddon
|
|
825
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1149
|
+
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
826
1150
|
align="inline-end"
|
|
827
1151
|
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
828
1152
|
>
|
|
@@ -846,299 +1170,60 @@ export {
|
|
|
846
1170
|
</TooltipContent>
|
|
847
1171
|
</Tooltip>
|
|
848
1172
|
</InputGroupAddon>
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
<Calendar
|
|
853
|
-
:locale="locale"
|
|
854
|
-
:layout="field.mode"
|
|
855
|
-
:model-value="toCalendarDateValue(getProperty(modelValue, field.path), field.value)"
|
|
856
|
-
:disabled="isFieldDisabled(field)"
|
|
857
|
-
:is-date-disabled="field.disableDate ? (date) => isCalendarDateDisabled(field, date) : void 0"
|
|
858
|
-
@update:model-value="(value) => {
|
|
859
|
-
if (value === void 0) {
|
|
860
|
-
deleteProperty(modelValue, field.path);
|
|
861
|
-
} else {
|
|
862
|
-
setProperty(modelValue, field.path, format(value.toDate(getLocalTimeZone()), field.value));
|
|
863
|
-
}
|
|
864
|
-
}"
|
|
865
|
-
/>
|
|
866
|
-
</PopoverContent>
|
|
867
|
-
</Popover>
|
|
868
|
-
<template v-else>
|
|
869
|
-
<template v-if="field.type === 'select'">
|
|
870
|
-
<Popover
|
|
871
|
-
v-for="selectState in [getSelectFieldState(field)]"
|
|
872
|
-
:key="`${field.id}:select:${selectState.selectedKey ?? 'empty'}`"
|
|
873
|
-
:open="selectOpen[field.path] === true"
|
|
874
|
-
@update:open="(open) => handleSelectOpenChange(field, open)"
|
|
875
|
-
>
|
|
876
|
-
<PopoverAnchor as-child>
|
|
877
|
-
<InputGroup :data-disabled="isFieldDisabled(field) ? 'true' : void 0">
|
|
878
|
-
<PopoverTrigger as-child>
|
|
879
|
-
<InputGroupInput
|
|
880
|
-
:id="`${id}:${field.path}`"
|
|
881
|
-
:model-value="getSelectDisplayValue(selectState, selectState.selectedKey)"
|
|
882
|
-
:disabled="isFieldDisabled(field)"
|
|
883
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
884
|
-
:placeholder="t('select-placeholder')"
|
|
885
|
-
class="text-left"
|
|
886
|
-
readonly
|
|
887
|
-
@blur="handleSelectBlur(field)"
|
|
888
|
-
/>
|
|
889
|
-
</PopoverTrigger>
|
|
890
|
-
<InputGroupAddon v-if="field.icon">
|
|
891
|
-
<Icon
|
|
892
|
-
:icon="field.icon"
|
|
893
|
-
/>
|
|
894
|
-
</InputGroupAddon>
|
|
895
|
-
<InputGroupAddon
|
|
896
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
897
|
-
align="inline-end"
|
|
898
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
899
|
-
>
|
|
900
|
-
<Tooltip :delay-duration="800">
|
|
901
|
-
<TooltipTrigger>
|
|
902
|
-
<InputGroupButton as-child>
|
|
903
|
-
<button
|
|
904
|
-
type="button"
|
|
905
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
906
|
-
:disabled="isFieldDisabled(field)"
|
|
907
|
-
@click="clearSelectField(field)"
|
|
908
|
-
>
|
|
909
|
-
<Icon
|
|
910
|
-
icon="fluent:dismiss-20-regular"
|
|
911
|
-
/>
|
|
912
|
-
</button>
|
|
913
|
-
</InputGroupButton>
|
|
914
|
-
</TooltipTrigger>
|
|
915
|
-
<TooltipContent>
|
|
916
|
-
{{ t("clear") }}
|
|
917
|
-
</TooltipContent>
|
|
918
|
-
</Tooltip>
|
|
919
|
-
</InputGroupAddon>
|
|
920
|
-
</InputGroup>
|
|
921
|
-
</PopoverAnchor>
|
|
922
|
-
|
|
923
|
-
<PopoverContent
|
|
924
|
-
class="p-0"
|
|
925
|
-
: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"
|
|
926
1176
|
>
|
|
927
|
-
<
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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"
|
|
932
1188
|
>
|
|
933
|
-
<
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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
|
+
>
|
|
937
1197
|
<Icon
|
|
938
|
-
icon="fluent:
|
|
939
|
-
class="text-zinc-400 text-2xl!"
|
|
1198
|
+
icon="fluent:dismiss-20-regular"
|
|
940
1199
|
/>
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
:value="option.key"
|
|
952
|
-
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"
|
|
953
|
-
>
|
|
954
|
-
{{ option.label }}
|
|
955
|
-
</CommandItem>
|
|
956
|
-
</CommandGroup>
|
|
957
|
-
</CommandList>
|
|
958
|
-
</Command>
|
|
959
|
-
</PopoverContent>
|
|
960
|
-
</Popover>
|
|
961
|
-
</template>
|
|
962
|
-
|
|
963
|
-
<template v-else-if="field.type === 'radio'">
|
|
964
|
-
<RadioGroupRoot
|
|
965
|
-
v-for="radioState in [getSelectFieldState(field)]"
|
|
966
|
-
:key="`${field.id}:radio:${radioState.selectedKey ?? 'empty'}`"
|
|
967
|
-
:model-value="radioState.selectedKey"
|
|
968
|
-
:disabled="isFieldDisabled(field)"
|
|
969
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
970
|
-
class="flex flex-wrap gap-x-4 gap-y-1.5"
|
|
971
|
-
@update:model-value="(value) => handleSelectValueChange(field, radioState, value)"
|
|
972
|
-
@focusout="validateField(field)"
|
|
973
|
-
>
|
|
974
|
-
<label
|
|
975
|
-
v-for="option in radioState.options"
|
|
976
|
-
:key="option.key"
|
|
977
|
-
class="flex items-center gap-1.5 text-sm cursor-pointer data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
|
|
978
|
-
:data-disabled="isFieldDisabled(field) ? 'true' : void 0"
|
|
979
|
-
>
|
|
980
|
-
<RadioGroupItem
|
|
981
|
-
:value="option.key"
|
|
982
|
-
data-slot="radio-group-item"
|
|
983
|
-
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"
|
|
984
1210
|
>
|
|
985
|
-
<
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
{{ option.label }}
|
|
990
|
-
</label>
|
|
991
|
-
</RadioGroupRoot>
|
|
1211
|
+
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1212
|
+
</span>
|
|
1213
|
+
</InputGroupAddon>
|
|
1214
|
+
</InputGroup>
|
|
992
1215
|
</template>
|
|
993
1216
|
|
|
994
|
-
<
|
|
995
|
-
v-
|
|
996
|
-
:
|
|
997
|
-
: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"
|
|
998
1220
|
>
|
|
999
|
-
<
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1006
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1007
|
-
:disabled="isFieldDisabled(field)"
|
|
1008
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1009
|
-
@update:model-value="(value) => {
|
|
1010
|
-
if (!value && !field.discardEmptyString) {
|
|
1011
|
-
deleteProperty(modelValue, field.path);
|
|
1012
|
-
} else {
|
|
1013
|
-
setProperty(modelValue, field.path, value);
|
|
1014
|
-
}
|
|
1015
|
-
}"
|
|
1016
|
-
@blur="validateField(field)"
|
|
1017
|
-
/>
|
|
1018
|
-
<InputGroupAddon v-if="field.icon">
|
|
1019
|
-
<Icon
|
|
1020
|
-
:icon="field.icon"
|
|
1021
|
-
/>
|
|
1022
|
-
</InputGroupAddon>
|
|
1023
|
-
</div>
|
|
1024
|
-
<InputGroupInput
|
|
1025
|
-
v-if="field.type === 'string'"
|
|
1026
|
-
:id="`${id}:${field.path}`"
|
|
1027
|
-
:treat-empty-as-different-state-from-null="!!field.discardEmptyString"
|
|
1028
|
-
:model-value="getProperty(modelValue, field.path)"
|
|
1029
|
-
:maxlength="field.maxLength ? $dsl.evaluate`${field.maxLength}`() : void 0"
|
|
1030
|
-
:disabled="isFieldDisabled(field)"
|
|
1031
|
-
:aria-invalid="isFieldInvalid(field) ? 'true' : void 0"
|
|
1032
|
-
@update:model-value="(value) => {
|
|
1033
|
-
if (!value && !field.discardEmptyString) {
|
|
1034
|
-
deleteProperty(modelValue, field.path);
|
|
1035
|
-
} else {
|
|
1036
|
-
setProperty(modelValue, field.path, value);
|
|
1037
|
-
}
|
|
1038
|
-
}"
|
|
1039
|
-
@blur="validateField(field)"
|
|
1040
|
-
/>
|
|
1041
|
-
<InputGroupNumberField
|
|
1042
|
-
v-if="field.type === 'number'"
|
|
1043
|
-
:id="`${id}:${field.path}`"
|
|
1044
|
-
:model-value="getProperty(modelValue, field.path) ?? null"
|
|
1045
|
-
:min="field.min ? $dsl.evaluate`${field.min}`() : void 0"
|
|
1046
|
-
:max="field.max ? $dsl.evaluate`${field.max}`() : void 0"
|
|
1047
|
-
:step="field.step ? $dsl.evaluate`${field.step}`() : void 0"
|
|
1048
|
-
:disabled="isFieldDisabled(field)"
|
|
1049
|
-
:invalid="isFieldInvalid(field)"
|
|
1050
|
-
@update:model-value="(value) => {
|
|
1051
|
-
if (!value && value !== 0) {
|
|
1052
|
-
deleteProperty(modelValue, field.path);
|
|
1053
|
-
} else {
|
|
1054
|
-
setProperty(modelValue, field.path, value);
|
|
1055
|
-
}
|
|
1056
|
-
}"
|
|
1057
|
-
@blur="validateField(field)"
|
|
1058
|
-
/>
|
|
1059
|
-
<InputGroupAddon v-if="field.type !== 'textarea' && field.icon">
|
|
1060
|
-
<Icon
|
|
1061
|
-
:icon="field.icon"
|
|
1062
|
-
/>
|
|
1063
|
-
</InputGroupAddon>
|
|
1064
|
-
<InputGroupAddon
|
|
1065
|
-
v-if="field.type !== 'textarea' && hasProperty(modelValue, field.path)"
|
|
1066
|
-
align="inline-end"
|
|
1067
|
-
:class="getConfigOrientation(displayConfig) === 'floating' ? 'group-data-[disabled=true]/input-group:hidden' : void 0"
|
|
1068
|
-
>
|
|
1069
|
-
<Tooltip :delay-duration="800">
|
|
1070
|
-
<TooltipTrigger>
|
|
1071
|
-
<InputGroupButton as-child>
|
|
1072
|
-
<button
|
|
1073
|
-
type="button"
|
|
1074
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1075
|
-
:disabled="isFieldDisabled(field)"
|
|
1076
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1077
|
-
>
|
|
1078
|
-
<Icon
|
|
1079
|
-
icon="fluent:dismiss-20-regular"
|
|
1080
|
-
/>
|
|
1081
|
-
</button>
|
|
1082
|
-
</InputGroupButton>
|
|
1083
|
-
</TooltipTrigger>
|
|
1084
|
-
<TooltipContent>
|
|
1085
|
-
{{ t("clear") }}
|
|
1086
|
-
</TooltipContent>
|
|
1087
|
-
</Tooltip>
|
|
1088
|
-
</InputGroupAddon>
|
|
1089
|
-
<InputGroupAddon
|
|
1090
|
-
v-if="field.type === 'string' && field.maxLength && getProperty(modelValue, field.path)"
|
|
1091
|
-
align="inline-end"
|
|
1092
|
-
>
|
|
1093
|
-
<span class="text-xs text-zinc-400 font-mono">
|
|
1094
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1095
|
-
</span>
|
|
1096
|
-
</InputGroupAddon>
|
|
1097
|
-
<InputGroupAddon
|
|
1098
|
-
v-if="field.type === 'textarea' && (hasProperty(modelValue, field.path) || field.maxLength && getProperty(modelValue, field.path))"
|
|
1099
|
-
align="block-end"
|
|
1100
|
-
>
|
|
1101
|
-
<Tooltip
|
|
1102
|
-
v-if="hasProperty(modelValue, field.path)"
|
|
1103
|
-
:delay-duration="800"
|
|
1104
|
-
>
|
|
1105
|
-
<TooltipTrigger>
|
|
1106
|
-
<InputGroupButton as-child>
|
|
1107
|
-
<button
|
|
1108
|
-
type="button"
|
|
1109
|
-
class="text-zinc-300 hover:text-zinc-500 transition-colors"
|
|
1110
|
-
:disabled="isFieldDisabled(field)"
|
|
1111
|
-
@click="deleteProperty(modelValue, field.path)"
|
|
1112
|
-
>
|
|
1113
|
-
<Icon
|
|
1114
|
-
icon="fluent:dismiss-20-regular"
|
|
1115
|
-
/>
|
|
1116
|
-
</button>
|
|
1117
|
-
</InputGroupButton>
|
|
1118
|
-
</TooltipTrigger>
|
|
1119
|
-
<TooltipContent>
|
|
1120
|
-
{{ t("clear") }}
|
|
1121
|
-
</TooltipContent>
|
|
1122
|
-
</Tooltip>
|
|
1123
|
-
<span
|
|
1124
|
-
v-if="field.maxLength && getProperty(modelValue, field.path)"
|
|
1125
|
-
class="text-xs text-zinc-400 font-mono"
|
|
1126
|
-
>
|
|
1127
|
-
<span class="inline-block text-right">{{ String(getProperty(modelValue, field.path) ?? "").length }}</span>/{{ field.maxLength }}
|
|
1128
|
-
</span>
|
|
1129
|
-
</InputGroupAddon>
|
|
1130
|
-
</InputGroup>
|
|
1131
|
-
</template>
|
|
1132
|
-
|
|
1133
|
-
<FieldError
|
|
1134
|
-
v-if="isFieldInvalid(field)"
|
|
1135
|
-
:class="usesContentsOrientation(displayConfig) ? 'static pt-1' : void 0"
|
|
1136
|
-
>
|
|
1137
|
-
<span v-html="renderValidationMessage(field)" />
|
|
1138
|
-
</FieldError>
|
|
1139
|
-
</FieldContent>
|
|
1140
|
-
</Field>
|
|
1141
|
-
</template>
|
|
1221
|
+
<span v-html="renderValidationMessage(field)" />
|
|
1222
|
+
</FieldError>
|
|
1223
|
+
</FieldContent>
|
|
1224
|
+
</Field>
|
|
1225
|
+
</template>
|
|
1226
|
+
</FieldSet>
|
|
1142
1227
|
|
|
1143
1228
|
<slot />
|
|
1144
1229
|
</div>
|