@shwfed/nuxt 0.9.1 → 0.10.0
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 +23 -3
- package/dist/runtime/components/fields.vue +1 -0
- package/dist/runtime/components/fields.vue.d.ts +23 -3
- package/dist/runtime/components/ui/field/FieldError.vue +1 -1
- package/dist/runtime/components/ui/field/index.js +1 -1
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +43 -2
- package/dist/runtime/components/ui/fields/Fields.vue +51 -11
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +43 -2
- package/dist/runtime/components/ui/fields/schema.d.ts +30 -2
- package/dist/runtime/components/ui/fields/schema.js +16 -1
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +18 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +667 -151
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +18 -0
- package/package.json +1 -1
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { useNuxtApp } from "#app";
|
|
3
|
+
import z, {} from "zod";
|
|
2
4
|
import { useSortable } from "@vueuse/integrations/useSortable";
|
|
3
5
|
import { Icon } from "@iconify/vue";
|
|
4
6
|
import { computed, nextTick, ref, toRaw, watch } from "vue";
|
|
5
7
|
import { useI18n } from "vue-i18n";
|
|
6
8
|
import {
|
|
7
9
|
CalendarFieldC,
|
|
10
|
+
FieldsConfigC,
|
|
8
11
|
NumberFieldC,
|
|
9
12
|
SelectFieldC,
|
|
10
|
-
|
|
13
|
+
SlotFieldC,
|
|
14
|
+
StringFieldC,
|
|
15
|
+
FieldsStyleC
|
|
11
16
|
} from "../fields/schema";
|
|
12
17
|
import { cn } from "../../../utils/cn";
|
|
13
18
|
import { Button } from "../button";
|
|
@@ -25,7 +30,6 @@ import {
|
|
|
25
30
|
DropdownMenuItem,
|
|
26
31
|
DropdownMenuTrigger
|
|
27
32
|
} from "../dropdown-menu";
|
|
28
|
-
import { IconPicker } from "../icon-picker";
|
|
29
33
|
import { Input } from "../input";
|
|
30
34
|
import Locale from "../locale/Locale.vue";
|
|
31
35
|
import { NativeSelect, NativeSelectOption } from "../native-select";
|
|
@@ -38,8 +42,11 @@ const emit = defineEmits(["confirm"]);
|
|
|
38
42
|
const open = defineModel("open", { type: Boolean, ...{
|
|
39
43
|
default: false
|
|
40
44
|
} });
|
|
45
|
+
const { $toast } = useNuxtApp();
|
|
41
46
|
const { t } = useI18n();
|
|
42
47
|
const draftOrientation = ref("horizontal");
|
|
48
|
+
const draftStyle = ref();
|
|
49
|
+
const search = ref("");
|
|
43
50
|
const selectedItemId = ref("general");
|
|
44
51
|
const draftFields = ref([]);
|
|
45
52
|
const sortableListRef = ref(null);
|
|
@@ -49,17 +56,28 @@ const fieldTypeOptions = computed(() => [
|
|
|
49
56
|
{ type: "string", label: t("field-type-string") },
|
|
50
57
|
{ type: "number", label: t("field-type-number") },
|
|
51
58
|
{ type: "select", label: t("field-type-select") },
|
|
52
|
-
{ type: "calendar", label: t("field-type-calendar") }
|
|
59
|
+
{ type: "calendar", label: t("field-type-calendar") },
|
|
60
|
+
{ type: "slot", label: t("field-type-slot") }
|
|
53
61
|
]);
|
|
54
62
|
const generalItem = computed(() => ({
|
|
55
63
|
id: "general",
|
|
56
64
|
label: t("general")
|
|
57
65
|
}));
|
|
66
|
+
const normalizedSearch = computed(() => search.value.trim().toLocaleLowerCase());
|
|
58
67
|
const selectedField = computed(() => draftFields.value.find((field) => field.draftId === selectedItemId.value));
|
|
59
|
-
const selectedFieldValidationRules = computed(() =>
|
|
68
|
+
const selectedFieldValidationRules = computed(() => {
|
|
69
|
+
const field = selectedField.value?.field;
|
|
70
|
+
if (!field || field.type === "slot") {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
return field.validation ?? [];
|
|
74
|
+
});
|
|
60
75
|
function createDraftId() {
|
|
61
76
|
return crypto.randomUUID();
|
|
62
77
|
}
|
|
78
|
+
function createFieldId() {
|
|
79
|
+
return crypto.randomUUID();
|
|
80
|
+
}
|
|
63
81
|
function createDefaultLocaleValue() {
|
|
64
82
|
return [{ locale: "zh", message: "" }];
|
|
65
83
|
}
|
|
@@ -81,12 +99,20 @@ function normalizeOrientation(value) {
|
|
|
81
99
|
function getFieldTypeLabel(type) {
|
|
82
100
|
return t(`field-type-${type}`);
|
|
83
101
|
}
|
|
102
|
+
function getSlotFieldLabel(field) {
|
|
103
|
+
return t("slot-field", {
|
|
104
|
+
id: field.id.slice(0, 8)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
84
107
|
function getUnnamedFieldLabel(field) {
|
|
85
108
|
return t("unnamed-field", {
|
|
86
109
|
type: getFieldTypeLabel(field.type)
|
|
87
110
|
});
|
|
88
111
|
}
|
|
89
112
|
function getFieldChineseTitle(field) {
|
|
113
|
+
if (field.type === "slot") {
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
90
116
|
const zhTitle = field.title.find((item) => item.locale === "zh");
|
|
91
117
|
if (!zhTitle) {
|
|
92
118
|
return void 0;
|
|
@@ -95,14 +121,28 @@ function getFieldChineseTitle(field) {
|
|
|
95
121
|
return message.length > 0 ? message : void 0;
|
|
96
122
|
}
|
|
97
123
|
function getFieldListLabel(field) {
|
|
124
|
+
if (field.type === "slot") {
|
|
125
|
+
return getSlotFieldLabel(field);
|
|
126
|
+
}
|
|
98
127
|
return getFieldChineseTitle(field) ?? getUnnamedFieldLabel(field);
|
|
99
128
|
}
|
|
100
129
|
const fieldItems = computed(() => draftFields.value.map((item) => ({
|
|
101
130
|
itemId: item.draftId,
|
|
131
|
+
fieldId: item.field.id,
|
|
102
132
|
label: getFieldListLabel(item.field),
|
|
103
|
-
path: item.field.path,
|
|
133
|
+
path: item.field.type === "slot" ? void 0 : item.field.path,
|
|
134
|
+
searchMeta: item.field.type === "slot" ? item.field.id : [item.field.path, item.field.id].filter(Boolean).join(" "),
|
|
104
135
|
type: item.field.type
|
|
105
136
|
})));
|
|
137
|
+
const filteredFieldItems = computed(() => {
|
|
138
|
+
if (!normalizedSearch.value) {
|
|
139
|
+
return fieldItems.value;
|
|
140
|
+
}
|
|
141
|
+
return fieldItems.value.filter((item) => {
|
|
142
|
+
const haystack = [item.label, item.searchMeta].filter(Boolean).join(" ").toLocaleLowerCase();
|
|
143
|
+
return haystack.includes(normalizedSearch.value);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
106
146
|
const selectedItemLabel = computed(() => selectedField.value ? getFieldListLabel(selectedField.value.field) : generalItem.value.label);
|
|
107
147
|
const sortable = useSortable(sortableListRef, sortableItemIds);
|
|
108
148
|
function getFieldErrorKey(draftId, fieldKey) {
|
|
@@ -111,6 +151,9 @@ function getFieldErrorKey(draftId, fieldKey) {
|
|
|
111
151
|
function getValidationRuleErrorKey(draftId, index, control) {
|
|
112
152
|
return `${draftId}:validation:${index}:${control}`;
|
|
113
153
|
}
|
|
154
|
+
function getGeneralErrorKey(fieldKey) {
|
|
155
|
+
return `general:${fieldKey}`;
|
|
156
|
+
}
|
|
114
157
|
function clearError(key) {
|
|
115
158
|
Reflect.deleteProperty(validationErrors.value, key);
|
|
116
159
|
}
|
|
@@ -209,25 +252,34 @@ function normalizeField(field) {
|
|
|
209
252
|
disabled: normalizeOptionalString(field.disabled ?? ""),
|
|
210
253
|
validation: normalizeValidationRules(field.validation)
|
|
211
254
|
};
|
|
255
|
+
case "slot":
|
|
256
|
+
return {
|
|
257
|
+
...field,
|
|
258
|
+
style: normalizeOptionalString(field.style ?? "")
|
|
259
|
+
};
|
|
212
260
|
}
|
|
213
261
|
}
|
|
214
262
|
function createField(type) {
|
|
215
263
|
const title = createDefaultLocaleValue();
|
|
264
|
+
const id = createFieldId();
|
|
216
265
|
switch (type) {
|
|
217
266
|
case "string":
|
|
218
267
|
return {
|
|
268
|
+
id,
|
|
219
269
|
type,
|
|
220
270
|
path: "",
|
|
221
271
|
title
|
|
222
272
|
};
|
|
223
273
|
case "number":
|
|
224
274
|
return {
|
|
275
|
+
id,
|
|
225
276
|
type,
|
|
226
277
|
path: "",
|
|
227
278
|
title
|
|
228
279
|
};
|
|
229
280
|
case "select":
|
|
230
281
|
return {
|
|
282
|
+
id,
|
|
231
283
|
type,
|
|
232
284
|
path: "",
|
|
233
285
|
title,
|
|
@@ -238,17 +290,28 @@ function createField(type) {
|
|
|
238
290
|
};
|
|
239
291
|
case "calendar":
|
|
240
292
|
return {
|
|
293
|
+
id,
|
|
241
294
|
type,
|
|
242
295
|
path: "",
|
|
243
296
|
title,
|
|
244
297
|
mode: "date",
|
|
245
298
|
value: "yyyy-MM-dd"
|
|
246
299
|
};
|
|
300
|
+
case "slot":
|
|
301
|
+
return {
|
|
302
|
+
id,
|
|
303
|
+
type
|
|
304
|
+
};
|
|
247
305
|
}
|
|
248
306
|
}
|
|
249
307
|
function resetDraftConfig() {
|
|
250
|
-
|
|
251
|
-
|
|
308
|
+
applyDraftConfig(props.config);
|
|
309
|
+
}
|
|
310
|
+
function applyDraftConfig(config) {
|
|
311
|
+
draftOrientation.value = config.orientation ?? "horizontal";
|
|
312
|
+
draftStyle.value = normalizeOptionalString(config.style ?? "");
|
|
313
|
+
search.value = "";
|
|
314
|
+
draftFields.value = cloneFields(config.fields);
|
|
252
315
|
selectedItemId.value = "general";
|
|
253
316
|
validationErrors.value = {};
|
|
254
317
|
}
|
|
@@ -283,7 +346,7 @@ function configureSortable() {
|
|
|
283
346
|
}
|
|
284
347
|
async function refreshSortable() {
|
|
285
348
|
sortable.stop();
|
|
286
|
-
if (!open.value || draftFields.value.length === 0) {
|
|
349
|
+
if (!open.value || draftFields.value.length === 0 || normalizedSearch.value) {
|
|
287
350
|
return;
|
|
288
351
|
}
|
|
289
352
|
await nextTick();
|
|
@@ -318,6 +381,21 @@ watch(fieldItems, async (items) => {
|
|
|
318
381
|
await refreshSortable();
|
|
319
382
|
}
|
|
320
383
|
}, { immediate: true });
|
|
384
|
+
watch(filteredFieldItems, (items) => {
|
|
385
|
+
if (!normalizedSearch.value || selectedItemId.value === "general") {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (items.some((item) => item.itemId === selectedItemId.value)) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
selectedItemId.value = items[0]?.itemId ?? "general";
|
|
392
|
+
}, { immediate: true });
|
|
393
|
+
watch(normalizedSearch, async () => {
|
|
394
|
+
if (!open.value) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
await refreshSortable();
|
|
398
|
+
});
|
|
321
399
|
function discardChanges() {
|
|
322
400
|
resetDraftConfig();
|
|
323
401
|
open.value = false;
|
|
@@ -338,6 +416,10 @@ function selectItem(itemId) {
|
|
|
338
416
|
function updateDraftOrientation(value) {
|
|
339
417
|
draftOrientation.value = normalizeOrientation(value);
|
|
340
418
|
}
|
|
419
|
+
function updateDraftStyle(value) {
|
|
420
|
+
clearError(getGeneralErrorKey("style"));
|
|
421
|
+
draftStyle.value = normalizeOptionalString(String(value));
|
|
422
|
+
}
|
|
341
423
|
function updateDraftField(draftId, updater) {
|
|
342
424
|
draftFields.value = draftFields.value.map((item) => item.draftId === draftId ? {
|
|
343
425
|
draftId: item.draftId,
|
|
@@ -375,26 +457,25 @@ function updateSelectedFieldTitle(value) {
|
|
|
375
457
|
title: value
|
|
376
458
|
}));
|
|
377
459
|
}
|
|
378
|
-
function
|
|
460
|
+
async function copySelectedFieldId() {
|
|
379
461
|
const selected = selectedField.value;
|
|
380
462
|
if (!selected) {
|
|
381
463
|
return;
|
|
382
464
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}));
|
|
465
|
+
await copyFieldId(selected.field.id);
|
|
466
|
+
}
|
|
467
|
+
async function copyFieldId(fieldId) {
|
|
468
|
+
await writeClipboardText(fieldId, t("copy-field-id-failed"));
|
|
388
469
|
}
|
|
389
|
-
function
|
|
470
|
+
function updateSelectedFieldPath(value) {
|
|
390
471
|
const selected = selectedField.value;
|
|
391
|
-
if (!selected) {
|
|
472
|
+
if (!selected || selected.field.type === "slot") {
|
|
392
473
|
return;
|
|
393
474
|
}
|
|
394
|
-
clearFieldError(selected.draftId, "
|
|
475
|
+
clearFieldError(selected.draftId, "path");
|
|
395
476
|
updateDraftField(selected.draftId, (field) => ({
|
|
396
477
|
...field,
|
|
397
|
-
|
|
478
|
+
path: String(value).trim()
|
|
398
479
|
}));
|
|
399
480
|
}
|
|
400
481
|
function updateSelectedFieldStyle(value) {
|
|
@@ -643,23 +724,28 @@ function updateSelectedCalendarDisableDate(value) {
|
|
|
643
724
|
}
|
|
644
725
|
function addValidationRule() {
|
|
645
726
|
const selected = selectedField.value;
|
|
646
|
-
if (!selected) {
|
|
727
|
+
if (!selected || selected.field.type === "slot") {
|
|
647
728
|
return;
|
|
648
729
|
}
|
|
649
|
-
updateDraftField(selected.draftId, (field) =>
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
730
|
+
updateDraftField(selected.draftId, (field) => {
|
|
731
|
+
if (field.type === "slot") {
|
|
732
|
+
return field;
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
...field,
|
|
736
|
+
validation: [
|
|
737
|
+
...field.validation ?? [],
|
|
738
|
+
{
|
|
739
|
+
expression: "",
|
|
740
|
+
message: ""
|
|
741
|
+
}
|
|
742
|
+
]
|
|
743
|
+
};
|
|
744
|
+
});
|
|
659
745
|
}
|
|
660
746
|
function updateValidationRule(index, updater) {
|
|
661
747
|
const selected = selectedField.value;
|
|
662
|
-
if (!selected) {
|
|
748
|
+
if (!selected || selected.field.type === "slot") {
|
|
663
749
|
return;
|
|
664
750
|
}
|
|
665
751
|
const validation = selected.field.validation ?? [];
|
|
@@ -668,10 +754,15 @@ function updateValidationRule(index, updater) {
|
|
|
668
754
|
return;
|
|
669
755
|
}
|
|
670
756
|
const nextValidation = validation.map((rule, ruleIndex) => ruleIndex === index ? updater(rule) : rule);
|
|
671
|
-
updateDraftField(selected.draftId, (field) =>
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
757
|
+
updateDraftField(selected.draftId, (field) => {
|
|
758
|
+
if (field.type === "slot") {
|
|
759
|
+
return field;
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
...field,
|
|
763
|
+
validation: nextValidation
|
|
764
|
+
};
|
|
765
|
+
});
|
|
675
766
|
}
|
|
676
767
|
function updateSelectedValidationExpression(index, value) {
|
|
677
768
|
const selected = selectedField.value;
|
|
@@ -697,7 +788,7 @@ function updateSelectedValidationMessage(index, value) {
|
|
|
697
788
|
}
|
|
698
789
|
function moveValidationRule(index, offset) {
|
|
699
790
|
const selected = selectedField.value;
|
|
700
|
-
if (!selected) {
|
|
791
|
+
if (!selected || selected.field.type === "slot") {
|
|
701
792
|
return;
|
|
702
793
|
}
|
|
703
794
|
const validation = selected.field.validation ?? [];
|
|
@@ -713,15 +804,20 @@ function moveValidationRule(index, offset) {
|
|
|
713
804
|
}
|
|
714
805
|
nextValidation[index] = targetRule;
|
|
715
806
|
nextValidation[nextIndex] = currentRule;
|
|
716
|
-
updateDraftField(selected.draftId, (field) =>
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
807
|
+
updateDraftField(selected.draftId, (field) => {
|
|
808
|
+
if (field.type === "slot") {
|
|
809
|
+
return field;
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
...field,
|
|
813
|
+
validation: nextValidation
|
|
814
|
+
};
|
|
815
|
+
});
|
|
720
816
|
clearValidationRuleErrors(selected.draftId);
|
|
721
817
|
}
|
|
722
818
|
function deleteValidationRule(index) {
|
|
723
819
|
const selected = selectedField.value;
|
|
724
|
-
if (!selected) {
|
|
820
|
+
if (!selected || selected.field.type === "slot") {
|
|
725
821
|
return;
|
|
726
822
|
}
|
|
727
823
|
const validation = selected.field.validation ?? [];
|
|
@@ -731,7 +827,14 @@ function deleteValidationRule(index) {
|
|
|
731
827
|
clearValidationRuleError(selected.draftId, index, "expression");
|
|
732
828
|
clearValidationRuleError(selected.draftId, index, "message");
|
|
733
829
|
updateDraftField(selected.draftId, (field) => {
|
|
734
|
-
|
|
830
|
+
if (field.type === "slot") {
|
|
831
|
+
return field;
|
|
832
|
+
}
|
|
833
|
+
const validation2 = field.validation ?? [];
|
|
834
|
+
const nextValidation = [
|
|
835
|
+
...validation2.slice(0, index),
|
|
836
|
+
...validation2.slice(index + 1)
|
|
837
|
+
];
|
|
735
838
|
return {
|
|
736
839
|
...field,
|
|
737
840
|
validation: nextValidation.length > 0 ? nextValidation : void 0
|
|
@@ -757,6 +860,10 @@ function getSchemaIssues(field) {
|
|
|
757
860
|
const result = CalendarFieldC.safeParse(field);
|
|
758
861
|
return result.success ? [] : result.error.issues;
|
|
759
862
|
}
|
|
863
|
+
case "slot": {
|
|
864
|
+
const result = SlotFieldC.safeParse(field);
|
|
865
|
+
return result.success ? [] : result.error.issues;
|
|
866
|
+
}
|
|
760
867
|
}
|
|
761
868
|
}
|
|
762
869
|
function normalizeIssuePath(path) {
|
|
@@ -784,19 +891,30 @@ function validateDraftFields() {
|
|
|
784
891
|
draftId: item.draftId,
|
|
785
892
|
field: normalizeField(item.field)
|
|
786
893
|
}));
|
|
894
|
+
const idOwners = {};
|
|
787
895
|
const pathOwners = {};
|
|
788
896
|
let firstInvalidItemId;
|
|
789
897
|
for (const item of normalizedFields) {
|
|
790
|
-
const
|
|
791
|
-
if (
|
|
792
|
-
setError(errors, getFieldErrorKey(item.draftId, "
|
|
793
|
-
|
|
794
|
-
} else if (existingOwner !== void 0) {
|
|
795
|
-
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-duplicate"));
|
|
796
|
-
setError(errors, getFieldErrorKey(existingOwner, "path"), t("field-path-duplicate"));
|
|
898
|
+
const existingIdOwner = idOwners[item.field.id];
|
|
899
|
+
if (existingIdOwner !== void 0) {
|
|
900
|
+
setError(errors, getFieldErrorKey(item.draftId, "id"), t("field-id-duplicate"));
|
|
901
|
+
setError(errors, getFieldErrorKey(existingIdOwner, "id"), t("field-id-duplicate"));
|
|
797
902
|
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
798
903
|
} else {
|
|
799
|
-
|
|
904
|
+
idOwners[item.field.id] = item.draftId;
|
|
905
|
+
}
|
|
906
|
+
if (item.field.type !== "slot") {
|
|
907
|
+
const existingPathOwner = pathOwners[item.field.path];
|
|
908
|
+
if (item.field.path.length === 0) {
|
|
909
|
+
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-required"));
|
|
910
|
+
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
911
|
+
} else if (existingPathOwner !== void 0) {
|
|
912
|
+
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-duplicate"));
|
|
913
|
+
setError(errors, getFieldErrorKey(existingPathOwner, "path"), t("field-path-duplicate"));
|
|
914
|
+
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
915
|
+
} else {
|
|
916
|
+
pathOwners[item.field.path] = item.draftId;
|
|
917
|
+
}
|
|
800
918
|
}
|
|
801
919
|
if (item.field.type === "calendar" && item.field.value.length === 0) {
|
|
802
920
|
setError(errors, getFieldErrorKey(item.draftId, "value"), t("calendar-value-required"));
|
|
@@ -818,22 +936,258 @@ function validateDraftFields() {
|
|
|
818
936
|
}
|
|
819
937
|
return normalizedFields;
|
|
820
938
|
}
|
|
821
|
-
function
|
|
939
|
+
function buildDraftConfig() {
|
|
822
940
|
const normalizedFields = validateDraftFields();
|
|
823
941
|
if (!normalizedFields) {
|
|
942
|
+
return void 0;
|
|
943
|
+
}
|
|
944
|
+
const generalStyleResult = FieldsStyleC.safeParse(draftStyle.value);
|
|
945
|
+
if (!generalStyleResult.success) {
|
|
946
|
+
validationErrors.value = {
|
|
947
|
+
...validationErrors.value,
|
|
948
|
+
[getGeneralErrorKey("style")]: generalStyleResult.error.issues[0]?.message ?? t("general-style-invalid")
|
|
949
|
+
};
|
|
950
|
+
selectedItemId.value = "general";
|
|
951
|
+
return void 0;
|
|
952
|
+
}
|
|
953
|
+
const nextConfig = {
|
|
954
|
+
fields: normalizedFields.map((item) => item.field)
|
|
955
|
+
};
|
|
956
|
+
if (draftOrientation.value !== "horizontal") {
|
|
957
|
+
nextConfig.orientation = draftOrientation.value;
|
|
958
|
+
}
|
|
959
|
+
if (generalStyleResult.data) {
|
|
960
|
+
nextConfig.style = generalStyleResult.data;
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
config: nextConfig,
|
|
964
|
+
normalizedFields
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
function showImportError(message) {
|
|
968
|
+
$toast.error(message);
|
|
969
|
+
}
|
|
970
|
+
function showCopyError(message) {
|
|
971
|
+
$toast.error(message);
|
|
972
|
+
}
|
|
973
|
+
function showImportErrorWithCopyAction(message, onClick) {
|
|
974
|
+
$toast.error(message, {
|
|
975
|
+
action: {
|
|
976
|
+
label: t("copy-paste-error"),
|
|
977
|
+
onClick
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
function getValidDraftConfig(errorMessage) {
|
|
982
|
+
const result = buildDraftConfig();
|
|
983
|
+
if (!result) {
|
|
984
|
+
showCopyError(errorMessage);
|
|
985
|
+
return void 0;
|
|
986
|
+
}
|
|
987
|
+
return result.config;
|
|
988
|
+
}
|
|
989
|
+
function buildFieldsConfigJsonSchema() {
|
|
990
|
+
return z.toJSONSchema(FieldsConfigC, {
|
|
991
|
+
io: "input",
|
|
992
|
+
unrepresentable: "any"
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
function buildAiPromptHeaderMarkdown() {
|
|
996
|
+
return [
|
|
997
|
+
"# \u5B57\u6BB5\u914D\u7F6E AI \u4E0A\u4E0B\u6587",
|
|
998
|
+
"\u4F60\u662F\u4E00\u4E2A\u5E2E\u52A9\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\u7EC4\u4EF6\u7684 AI \u52A9\u624B\uFF0C\u8D1F\u8D23\u89E3\u91CA\u3001\u4FEE\u6539\u5E76\u751F\u6210\u53EF\u7C98\u8D34\u7684\u5B57\u6BB5\u914D\u7F6E\u3002",
|
|
999
|
+
"\u5982\u679C\u4F60\u4E0D\u786E\u5B9A\u67D0\u4E2A\u5B57\u6BB5\u3001\u8868\u8FBE\u5F0F\u53D8\u91CF\u3001\u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587\u6216\u4E1A\u52A1\u542B\u4E49\uFF0C\u5FC5\u987B\u660E\u786E\u8BF4\u660E\u4E0D\u786E\u5B9A\u70B9\uFF0C\u4E0D\u8981\u731C\u6D4B\u3002",
|
|
1000
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u751F\u6210\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\u6216\u914D\u7F6E\u7247\u6BB5\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002"
|
|
1001
|
+
].join("\n");
|
|
1002
|
+
}
|
|
1003
|
+
function buildDslGuideMarkdown() {
|
|
1004
|
+
return [
|
|
1005
|
+
"## DSL / CEL \u7F16\u5199\u8BF4\u660E",
|
|
1006
|
+
"\u672C\u914D\u7F6E\u4E2D\u7684\u8868\u8FBE\u5F0F\u5B57\u6BB5\u5FC5\u987B\u586B\u5199 CEL \u5B57\u7B26\u4E32\uFF0C\u4E0D\u8981\u751F\u6210 JavaScript\u3001TypeScript\u3001\u7BAD\u5934\u51FD\u6570\u6216\u4F2A\u4EE3\u7801\u3002",
|
|
1007
|
+
"",
|
|
1008
|
+
"### 1. \u57FA\u7840\u8BED\u6CD5",
|
|
1009
|
+
'- \u5B57\u9762\u91CF\uFF1A`"text"`\u3001`123`\u3001`true`\u3001`false`\u3001`null`\u3002',
|
|
1010
|
+
'- \u5217\u8868 / \u5BF9\u8C61\uFF1A`[1, 2]`\u3001`{"display": "grid"}`\u3002',
|
|
1011
|
+
"- \u8BBF\u95EE\uFF1A`.`\u3001`[]`\u3001`.?`\u3001`[?]`\uFF0C\u4F8B\u5982 `form.name`\u3001`ctx.?user.orValue(null)`\u3002",
|
|
1012
|
+
"- \u6761\u4EF6\u4E0E\u903B\u8F91\uFF1A`&&`\u3001`||`\u3001`!`\u3001`condition ? a : b`\u3002",
|
|
1013
|
+
'- \u65B9\u6CD5\u8C03\u7528\uFF1A`value.method(args)`\uFF0C\u4F8B\u5982 `now.format("yyyy-MM-dd")`\u3002',
|
|
1014
|
+
"",
|
|
1015
|
+
"### 2. \u5E38\u89C1\u5B57\u6BB5\u4E0E\u7528\u9014",
|
|
1016
|
+
'- `style` \u76F8\u5173\u5B57\u6BB5\u9700\u8981\u8FD4\u56DE style map\uFF0C\u4F8B\u5982 `{"display": "grid"}`\u3002',
|
|
1017
|
+
"- `hidden` / `disabled` / `disableDate` \u9700\u8981\u8FD4\u56DE `bool`\u3002",
|
|
1018
|
+
"- `validation[].expression` \u9700\u8981\u8FD4\u56DE `bool`\uFF1B\u8FD4\u56DE `false` \u65F6\u5C55\u793A\u5BF9\u5E94\u6D88\u606F\u3002",
|
|
1019
|
+
"- `select.options` \u901A\u5E38\u8FD4\u56DE\u5217\u8868\uFF1B`label` / `value` / `key` \u7528\u6765\u63CF\u8FF0\u5355\u4E2A\u9009\u9879\u5982\u4F55\u6620\u5C04\u3002",
|
|
1020
|
+
"",
|
|
1021
|
+
"### 3. \u5B57\u6BB5\u7C7B\u578B\u7EA6\u675F",
|
|
1022
|
+
"- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u5305\u542B\u7A33\u5B9A\u7684 UUID `id`\u3002",
|
|
1023
|
+
"- \u53EA\u6709\u975E `slot` \u5B57\u6BB5\u53EF\u4EE5\u914D\u7F6E `path`\uFF0C\u5E76\u53C2\u4E0E\u8868\u5355\u503C\u8BFB\u5199\u3002",
|
|
1024
|
+
"- `slot` \u5B57\u6BB5\u4E0D\u4F1A\u7ED1\u5B9A\u6A21\u578B\u503C\uFF0C\u53EA\u901A\u8FC7 `field.id` \u5BF9\u5E94\u7684\u5177\u540D\u63D2\u69FD\u6E32\u67D3\u3002"
|
|
1025
|
+
].join("\n");
|
|
1026
|
+
}
|
|
1027
|
+
function buildMarkdownNotes() {
|
|
1028
|
+
return [
|
|
1029
|
+
"## \u6CE8\u610F\u4E8B\u9879",
|
|
1030
|
+
"- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u4FDD\u7559\u73B0\u6709 `id`\uFF0C\u4E0D\u8981\u751F\u6210\u65B0\u7684 `id` \u66FF\u6362\u5DF2\u6709\u5B57\u6BB5\u3002",
|
|
1031
|
+
"- `slot` \u5B57\u6BB5\u53EA\u80FD\u4F7F\u7528 `id`\u3001`type` \u548C\u53EF\u9009\u7684 `style`\u3002",
|
|
1032
|
+
"- \u975E `slot` \u5B57\u6BB5\u7684 `path` \u5FC5\u987B\u552F\u4E00\u4E14\u4E0D\u80FD\u4E3A\u7A7A\u3002",
|
|
1033
|
+
"- \u8868\u8FBE\u5F0F\u5B57\u6BB5\u5FC5\u987B\u4E25\u683C\u9075\u5B88 schema \u7EA6\u675F\uFF1B\u5982\u679C schema \u4E0D\u652F\u6301\uFF0C\u5C31\u76F4\u63A5\u8BF4\u660E\u9650\u5236\u3002"
|
|
1034
|
+
].join("\n");
|
|
1035
|
+
}
|
|
1036
|
+
function buildMarkdownCopyContent(config) {
|
|
1037
|
+
return [
|
|
1038
|
+
buildAiPromptHeaderMarkdown(),
|
|
1039
|
+
"",
|
|
1040
|
+
"## \u5F53\u524D\u914D\u7F6E",
|
|
1041
|
+
"```json",
|
|
1042
|
+
JSON.stringify(config, null, 2),
|
|
1043
|
+
"```",
|
|
1044
|
+
"",
|
|
1045
|
+
buildDslGuideMarkdown(),
|
|
1046
|
+
"",
|
|
1047
|
+
buildMarkdownNotes(),
|
|
1048
|
+
"",
|
|
1049
|
+
"## FieldsConfig JSON Schema",
|
|
1050
|
+
"```json",
|
|
1051
|
+
JSON.stringify(buildFieldsConfigJsonSchema(), null, 2),
|
|
1052
|
+
"```"
|
|
1053
|
+
].join("\n");
|
|
1054
|
+
}
|
|
1055
|
+
function formatIssuePath(path) {
|
|
1056
|
+
if (path.length === 0) {
|
|
1057
|
+
return "(root)";
|
|
1058
|
+
}
|
|
1059
|
+
return path.map((segment) => {
|
|
1060
|
+
if (typeof segment === "number") {
|
|
1061
|
+
return `${segment}`;
|
|
1062
|
+
}
|
|
1063
|
+
if (typeof segment === "string") {
|
|
1064
|
+
return segment;
|
|
1065
|
+
}
|
|
1066
|
+
return String(segment);
|
|
1067
|
+
}).join(".");
|
|
1068
|
+
}
|
|
1069
|
+
function formatIssueExtraFields(issue) {
|
|
1070
|
+
const lines = [];
|
|
1071
|
+
for (const [key, value] of Object.entries(issue)) {
|
|
1072
|
+
if (key === "code" || key === "message" || key === "path") {
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
lines.push(` - ${key}: ${JSON.stringify(value)}`);
|
|
1076
|
+
}
|
|
1077
|
+
return lines;
|
|
1078
|
+
}
|
|
1079
|
+
function buildPasteConfigErrorDetails(source, error) {
|
|
1080
|
+
if (error instanceof SyntaxError) {
|
|
1081
|
+
return [
|
|
1082
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
1083
|
+
"- \u7C7B\u578B\uFF1AJSON \u89E3\u6790\u5931\u8D25",
|
|
1084
|
+
`- message: ${error.message}`,
|
|
1085
|
+
"",
|
|
1086
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
1087
|
+
"```text",
|
|
1088
|
+
source,
|
|
1089
|
+
"```"
|
|
1090
|
+
].join("\n");
|
|
1091
|
+
}
|
|
1092
|
+
const issueLines = error.issues.flatMap((issue, index) => [
|
|
1093
|
+
`### Issue ${index + 1}`,
|
|
1094
|
+
`- path: ${formatIssuePath(issue.path)}`,
|
|
1095
|
+
`- code: ${issue.code}`,
|
|
1096
|
+
`- message: ${issue.message}`,
|
|
1097
|
+
...formatIssueExtraFields(issue)
|
|
1098
|
+
]);
|
|
1099
|
+
return [
|
|
1100
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
1101
|
+
"- \u7C7B\u578B\uFF1ASchema \u6821\u9A8C\u5931\u8D25",
|
|
1102
|
+
"",
|
|
1103
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
1104
|
+
"```json",
|
|
1105
|
+
source,
|
|
1106
|
+
"```",
|
|
1107
|
+
"",
|
|
1108
|
+
"## Schema \u62A5\u9519",
|
|
1109
|
+
...issueLines
|
|
1110
|
+
].join("\n");
|
|
1111
|
+
}
|
|
1112
|
+
function buildPasteConfigErrorMarkdown(source, error) {
|
|
1113
|
+
return [
|
|
1114
|
+
buildAiPromptHeaderMarkdown(),
|
|
1115
|
+
"",
|
|
1116
|
+
"## \u5F53\u524D\u4EFB\u52A1",
|
|
1117
|
+
"\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE\u5B57\u6BB5\u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
|
|
1118
|
+
"\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u53D1\u660E schema \u4E2D\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5\uFF0C\u4E5F\u4E0D\u8981\u66FF\u73B0\u6709\u5B57\u6BB5\u751F\u6210\u65B0\u7684 ID\u3002",
|
|
1119
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
|
|
1120
|
+
"",
|
|
1121
|
+
buildPasteConfigErrorDetails(source, error),
|
|
1122
|
+
"",
|
|
1123
|
+
buildMarkdownNotes()
|
|
1124
|
+
].join("\n");
|
|
1125
|
+
}
|
|
1126
|
+
async function writeClipboardText(value, errorMessage) {
|
|
1127
|
+
try {
|
|
1128
|
+
await navigator.clipboard.writeText(value);
|
|
1129
|
+
} catch {
|
|
1130
|
+
showCopyError(errorMessage);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
async function copyPasteConfigError(source, error) {
|
|
1134
|
+
await writeClipboardText(buildPasteConfigErrorMarkdown(source, error), t("copy-paste-error-failed"));
|
|
1135
|
+
}
|
|
1136
|
+
async function pasteConfigFromClipboard() {
|
|
1137
|
+
let source = "";
|
|
1138
|
+
try {
|
|
1139
|
+
source = await navigator.clipboard.readText();
|
|
1140
|
+
} catch {
|
|
1141
|
+
showImportError(t("paste-config-read-failed"));
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
let parsedValue;
|
|
1145
|
+
try {
|
|
1146
|
+
parsedValue = JSON.parse(source);
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
if (error instanceof SyntaxError) {
|
|
1149
|
+
showImportErrorWithCopyAction(
|
|
1150
|
+
t("paste-config-invalid-json"),
|
|
1151
|
+
async () => copyPasteConfigError(source, error)
|
|
1152
|
+
);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
showImportError(t("paste-config-invalid-json"));
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
const result = FieldsConfigC.safeParse(parsedValue);
|
|
1159
|
+
if (!result.success) {
|
|
1160
|
+
showImportErrorWithCopyAction(
|
|
1161
|
+
t("paste-config-invalid-schema"),
|
|
1162
|
+
async () => copyPasteConfigError(source, result.error)
|
|
1163
|
+
);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
applyDraftConfig(result.data);
|
|
1167
|
+
await nextTick();
|
|
1168
|
+
await refreshSortable();
|
|
1169
|
+
}
|
|
1170
|
+
async function copyConfig() {
|
|
1171
|
+
const config = getValidDraftConfig(t("copy-config-failed"));
|
|
1172
|
+
if (!config) {
|
|
824
1173
|
return;
|
|
825
1174
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1175
|
+
await writeClipboardText(JSON.stringify(config, null, 2), t("copy-config-failed"));
|
|
1176
|
+
}
|
|
1177
|
+
async function copyMarkdown() {
|
|
1178
|
+
const config = getValidDraftConfig(t("copy-markdown-failed"));
|
|
1179
|
+
if (!config) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
await writeClipboardText(buildMarkdownCopyContent(config), t("copy-markdown-failed"));
|
|
1183
|
+
}
|
|
1184
|
+
function confirmChanges() {
|
|
1185
|
+
const result = buildDraftConfig();
|
|
1186
|
+
if (!result) {
|
|
1187
|
+
return;
|
|
836
1188
|
}
|
|
1189
|
+
draftFields.value = result.normalizedFields.map((item) => createDraftField(item.field));
|
|
1190
|
+
emit("confirm", result.config);
|
|
837
1191
|
open.value = false;
|
|
838
1192
|
}
|
|
839
1193
|
</script>
|
|
@@ -848,9 +1202,22 @@ function confirmChanges() {
|
|
|
848
1202
|
:show-close-button="true"
|
|
849
1203
|
>
|
|
850
1204
|
<DialogHeader class="gap-1 border-b border-zinc-200 px-6 py-5">
|
|
851
|
-
<
|
|
852
|
-
|
|
853
|
-
|
|
1205
|
+
<div class="flex items-center gap-3">
|
|
1206
|
+
<DialogTitle class="text-xl font-semibold text-zinc-800">
|
|
1207
|
+
{{ t("configure-fields") }}
|
|
1208
|
+
</DialogTitle>
|
|
1209
|
+
<Button
|
|
1210
|
+
type="button"
|
|
1211
|
+
variant="ghost"
|
|
1212
|
+
size="sm"
|
|
1213
|
+
data-slot="fields-configurator-paste"
|
|
1214
|
+
class="shrink-0"
|
|
1215
|
+
@click="pasteConfigFromClipboard"
|
|
1216
|
+
>
|
|
1217
|
+
<Icon icon="fluent:clipboard-paste-20-regular" />
|
|
1218
|
+
{{ t("paste-config") }}
|
|
1219
|
+
</Button>
|
|
1220
|
+
</div>
|
|
854
1221
|
<DialogDescription class="text-sm text-zinc-500">
|
|
855
1222
|
{{ t("configure-fields-description") }}
|
|
856
1223
|
</DialogDescription>
|
|
@@ -858,7 +1225,42 @@ function confirmChanges() {
|
|
|
858
1225
|
|
|
859
1226
|
<div class="grid min-h-0 flex-1 grid-cols-[19rem_minmax(0,1fr)]">
|
|
860
1227
|
<section class="flex min-h-0 flex-col border-r border-zinc-200 px-4 py-4">
|
|
861
|
-
<
|
|
1228
|
+
<Input
|
|
1229
|
+
v-model="search"
|
|
1230
|
+
data-slot="fields-configurator-search"
|
|
1231
|
+
:placeholder="t('search-fields')"
|
|
1232
|
+
/>
|
|
1233
|
+
|
|
1234
|
+
<DropdownMenu>
|
|
1235
|
+
<DropdownMenuTrigger as-child>
|
|
1236
|
+
<Button
|
|
1237
|
+
type="button"
|
|
1238
|
+
data-slot="fields-configurator-add"
|
|
1239
|
+
class="mt-3 w-full justify-center"
|
|
1240
|
+
>
|
|
1241
|
+
<Icon icon="fluent:add-20-regular" />
|
|
1242
|
+
{{ t("add-field") }}
|
|
1243
|
+
</Button>
|
|
1244
|
+
</DropdownMenuTrigger>
|
|
1245
|
+
|
|
1246
|
+
<DropdownMenuContent
|
|
1247
|
+
align="start"
|
|
1248
|
+
:style="{
|
|
1249
|
+
width: 'var(--reka-dropdown-menu-trigger-width)'
|
|
1250
|
+
}"
|
|
1251
|
+
>
|
|
1252
|
+
<DropdownMenuItem
|
|
1253
|
+
v-for="option in fieldTypeOptions"
|
|
1254
|
+
:key="option.type"
|
|
1255
|
+
:data-slot="`fields-configurator-add-item-${option.type}`"
|
|
1256
|
+
@select="addField(option.type)"
|
|
1257
|
+
>
|
|
1258
|
+
{{ option.label }}
|
|
1259
|
+
</DropdownMenuItem>
|
|
1260
|
+
</DropdownMenuContent>
|
|
1261
|
+
</DropdownMenu>
|
|
1262
|
+
|
|
1263
|
+
<div class="mt-4 flex min-h-0 flex-1 flex-col overflow-hidden">
|
|
862
1264
|
<div class="flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto pr-1">
|
|
863
1265
|
<button
|
|
864
1266
|
type="button"
|
|
@@ -880,16 +1282,17 @@ function confirmChanges() {
|
|
|
880
1282
|
</button>
|
|
881
1283
|
|
|
882
1284
|
<div
|
|
883
|
-
v-if="
|
|
1285
|
+
v-if="filteredFieldItems.length > 0"
|
|
884
1286
|
ref="sortableListRef"
|
|
885
1287
|
data-slot="fields-configurator-list"
|
|
886
1288
|
class="flex flex-col gap-1"
|
|
887
1289
|
>
|
|
888
1290
|
<div
|
|
889
|
-
v-for="item in
|
|
1291
|
+
v-for="item in filteredFieldItems"
|
|
890
1292
|
:key="item.itemId"
|
|
891
1293
|
data-slot="fields-configurator-field-item"
|
|
892
1294
|
:data-item-id="item.itemId"
|
|
1295
|
+
:data-field-id="item.fieldId"
|
|
893
1296
|
:data-field-path="item.path"
|
|
894
1297
|
:data-selected="selectedItemId === item.itemId ? 'true' : 'false'"
|
|
895
1298
|
:class="cn(
|
|
@@ -933,6 +1336,14 @@ function confirmChanges() {
|
|
|
933
1336
|
</div>
|
|
934
1337
|
</div>
|
|
935
1338
|
|
|
1339
|
+
<p
|
|
1340
|
+
v-else-if="normalizedSearch"
|
|
1341
|
+
data-slot="fields-configurator-empty"
|
|
1342
|
+
class="px-1 pt-2 text-xs text-zinc-400"
|
|
1343
|
+
>
|
|
1344
|
+
{{ t("no-matches") }}
|
|
1345
|
+
</p>
|
|
1346
|
+
|
|
936
1347
|
<p
|
|
937
1348
|
v-else
|
|
938
1349
|
data-slot="fields-configurator-empty"
|
|
@@ -942,46 +1353,30 @@ function confirmChanges() {
|
|
|
942
1353
|
</p>
|
|
943
1354
|
</div>
|
|
944
1355
|
</div>
|
|
945
|
-
|
|
946
|
-
<div
|
|
947
|
-
data-slot="fields-configurator-add-container"
|
|
948
|
-
class="mt-4"
|
|
949
|
-
>
|
|
950
|
-
<DropdownMenu>
|
|
951
|
-
<DropdownMenuTrigger as-child>
|
|
952
|
-
<Button
|
|
953
|
-
type="button"
|
|
954
|
-
data-slot="fields-configurator-add"
|
|
955
|
-
size="sm"
|
|
956
|
-
variant="default"
|
|
957
|
-
class="w-full justify-center"
|
|
958
|
-
>
|
|
959
|
-
<Icon icon="fluent:add-20-regular" />
|
|
960
|
-
{{ t("add-field") }}
|
|
961
|
-
</Button>
|
|
962
|
-
</DropdownMenuTrigger>
|
|
963
|
-
|
|
964
|
-
<DropdownMenuContent align="start">
|
|
965
|
-
<DropdownMenuItem
|
|
966
|
-
v-for="option in fieldTypeOptions"
|
|
967
|
-
:key="option.type"
|
|
968
|
-
:data-slot="`fields-configurator-add-item-${option.type}`"
|
|
969
|
-
@select="addField(option.type)"
|
|
970
|
-
>
|
|
971
|
-
{{ option.label }}
|
|
972
|
-
</DropdownMenuItem>
|
|
973
|
-
</DropdownMenuContent>
|
|
974
|
-
</DropdownMenu>
|
|
975
|
-
</div>
|
|
976
1356
|
</section>
|
|
977
1357
|
|
|
978
1358
|
<section class="flex min-h-0 flex-col overflow-y-auto px-6 py-6">
|
|
979
|
-
<
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1359
|
+
<div class="flex items-center gap-2">
|
|
1360
|
+
<h3
|
|
1361
|
+
data-slot="fields-configurator-detail-title"
|
|
1362
|
+
class="text-lg font-semibold text-zinc-800"
|
|
1363
|
+
>
|
|
1364
|
+
{{ selectedItemLabel }}
|
|
1365
|
+
</h3>
|
|
1366
|
+
<Button
|
|
1367
|
+
v-if="selectedField"
|
|
1368
|
+
type="button"
|
|
1369
|
+
variant="ghost"
|
|
1370
|
+
size="sm"
|
|
1371
|
+
data-slot="fields-configurator-copy-id"
|
|
1372
|
+
class="size-7 p-0 text-zinc-400 hover:text-zinc-700"
|
|
1373
|
+
:aria-label="t('copy-field-id', { field: selectedItemLabel })"
|
|
1374
|
+
:title="selectedField.field.id"
|
|
1375
|
+
@click="copySelectedFieldId"
|
|
1376
|
+
>
|
|
1377
|
+
<Icon icon="fluent:copy-20-regular" />
|
|
1378
|
+
</Button>
|
|
1379
|
+
</div>
|
|
985
1380
|
|
|
986
1381
|
<p
|
|
987
1382
|
v-if="selectedItemId === 'general'"
|
|
@@ -1019,6 +1414,30 @@ function confirmChanges() {
|
|
|
1019
1414
|
</NativeSelectOption>
|
|
1020
1415
|
</NativeSelect>
|
|
1021
1416
|
</label>
|
|
1417
|
+
|
|
1418
|
+
<label
|
|
1419
|
+
data-slot="fields-configurator-general-style-section"
|
|
1420
|
+
class="flex flex-col gap-2 md:col-span-2"
|
|
1421
|
+
>
|
|
1422
|
+
<span class="text-xs font-medium text-zinc-500">
|
|
1423
|
+
{{ t("general-style") }}
|
|
1424
|
+
</span>
|
|
1425
|
+
<Textarea
|
|
1426
|
+
data-slot="fields-configurator-general-style-input"
|
|
1427
|
+
:model-value="draftStyle ?? ''"
|
|
1428
|
+
:aria-invalid="validationErrors[getGeneralErrorKey('style')] ? 'true' : void 0"
|
|
1429
|
+
:placeholder="t('general-style-placeholder')"
|
|
1430
|
+
class="min-h-20 font-mono text-sm"
|
|
1431
|
+
@update:model-value="updateDraftStyle"
|
|
1432
|
+
/>
|
|
1433
|
+
<p
|
|
1434
|
+
v-if="validationErrors[getGeneralErrorKey('style')]"
|
|
1435
|
+
data-slot="fields-configurator-general-style-error"
|
|
1436
|
+
class="text-xs text-red-500"
|
|
1437
|
+
>
|
|
1438
|
+
{{ validationErrors[getGeneralErrorKey("style")] }}
|
|
1439
|
+
</p>
|
|
1440
|
+
</label>
|
|
1022
1441
|
</section>
|
|
1023
1442
|
|
|
1024
1443
|
<div
|
|
@@ -1026,24 +1445,20 @@ function confirmChanges() {
|
|
|
1026
1445
|
data-slot="fields-configurator-field-main"
|
|
1027
1446
|
class="mt-6 flex flex-col gap-6"
|
|
1028
1447
|
>
|
|
1029
|
-
<
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
</span>
|
|
1037
|
-
<div
|
|
1038
|
-
data-slot="fields-configurator-field-type"
|
|
1039
|
-
class="flex h-9 items-center rounded-md border border-zinc-200 bg-zinc-50 px-3 text-sm text-zinc-600"
|
|
1040
|
-
>
|
|
1041
|
-
{{ getFieldTypeLabel(selectedField.field.type) }}
|
|
1042
|
-
</div>
|
|
1043
|
-
</label>
|
|
1448
|
+
<p
|
|
1449
|
+
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, 'id')]"
|
|
1450
|
+
data-slot="fields-configurator-field-id-error"
|
|
1451
|
+
class="-mt-4 text-xs text-red-500"
|
|
1452
|
+
>
|
|
1453
|
+
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "id")] }}
|
|
1454
|
+
</p>
|
|
1044
1455
|
|
|
1456
|
+
<section
|
|
1457
|
+
v-if="selectedField.field.type !== 'slot'"
|
|
1458
|
+
data-slot="fields-configurator-field-path-section"
|
|
1459
|
+
class="flex flex-col gap-2"
|
|
1460
|
+
>
|
|
1045
1461
|
<label
|
|
1046
|
-
data-slot="fields-configurator-field-path-section"
|
|
1047
1462
|
class="flex flex-col gap-2"
|
|
1048
1463
|
>
|
|
1049
1464
|
<span class="text-xs font-medium text-zinc-500">
|
|
@@ -1068,6 +1483,7 @@ function confirmChanges() {
|
|
|
1068
1483
|
</section>
|
|
1069
1484
|
|
|
1070
1485
|
<section
|
|
1486
|
+
v-if="selectedField.field.type !== 'slot'"
|
|
1071
1487
|
data-slot="fields-configurator-field-label-section"
|
|
1072
1488
|
class="flex flex-col gap-2"
|
|
1073
1489
|
>
|
|
@@ -1091,28 +1507,36 @@ function confirmChanges() {
|
|
|
1091
1507
|
</section>
|
|
1092
1508
|
|
|
1093
1509
|
<section
|
|
1094
|
-
|
|
1510
|
+
v-if="selectedField.field.type === 'slot'"
|
|
1511
|
+
data-slot="fields-configurator-slot-options"
|
|
1095
1512
|
class="grid gap-4 md:grid-cols-2"
|
|
1096
1513
|
>
|
|
1097
|
-
<label class="flex flex-col gap-2">
|
|
1514
|
+
<label class="flex flex-col gap-2 md:col-span-2">
|
|
1098
1515
|
<span class="text-xs font-medium text-zinc-500">
|
|
1099
|
-
{{ t("field-
|
|
1516
|
+
{{ t("field-style") }}
|
|
1100
1517
|
</span>
|
|
1101
|
-
<
|
|
1102
|
-
data-slot="fields-configurator-field-
|
|
1103
|
-
:model-value="selectedField.field.
|
|
1104
|
-
:invalid="validationErrors[getFieldErrorKey(selectedField.draftId, '
|
|
1105
|
-
:placeholder="t('field-
|
|
1106
|
-
|
|
1518
|
+
<Textarea
|
|
1519
|
+
data-slot="fields-configurator-field-style-input"
|
|
1520
|
+
:model-value="selectedField.field.style ?? ''"
|
|
1521
|
+
:aria-invalid="validationErrors[getFieldErrorKey(selectedField.draftId, 'style')] ? 'true' : void 0"
|
|
1522
|
+
:placeholder="t('field-style-placeholder')"
|
|
1523
|
+
class="min-h-20 font-mono text-sm"
|
|
1524
|
+
@update:model-value="updateSelectedFieldStyle"
|
|
1107
1525
|
/>
|
|
1108
1526
|
<p
|
|
1109
|
-
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, '
|
|
1527
|
+
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, 'style')]"
|
|
1110
1528
|
class="text-xs text-red-500"
|
|
1111
1529
|
>
|
|
1112
|
-
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "
|
|
1530
|
+
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "style")] }}
|
|
1113
1531
|
</p>
|
|
1114
1532
|
</label>
|
|
1533
|
+
</section>
|
|
1115
1534
|
|
|
1535
|
+
<section
|
|
1536
|
+
v-else
|
|
1537
|
+
data-slot="fields-configurator-field-general-options"
|
|
1538
|
+
class="grid gap-4 md:grid-cols-2"
|
|
1539
|
+
>
|
|
1116
1540
|
<label class="flex flex-col gap-2">
|
|
1117
1541
|
<span class="text-xs font-medium text-zinc-500">
|
|
1118
1542
|
{{ t("field-style") }}
|
|
@@ -1443,6 +1867,7 @@ function confirmChanges() {
|
|
|
1443
1867
|
</section>
|
|
1444
1868
|
|
|
1445
1869
|
<section
|
|
1870
|
+
v-if="selectedField.field.type !== 'slot'"
|
|
1446
1871
|
data-slot="fields-configurator-validation"
|
|
1447
1872
|
class="flex flex-col gap-4"
|
|
1448
1873
|
>
|
|
@@ -1574,25 +1999,50 @@ function confirmChanges() {
|
|
|
1574
1999
|
</section>
|
|
1575
2000
|
</div>
|
|
1576
2001
|
|
|
1577
|
-
<DialogFooter class="border-t border-zinc-200 px-6 py-4">
|
|
1578
|
-
<
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
variant="default"
|
|
1582
|
-
@click="discardChanges"
|
|
1583
|
-
>
|
|
1584
|
-
<Icon icon="fluent:dismiss-20-regular" />
|
|
1585
|
-
{{ t("cancel") }}
|
|
1586
|
-
</Button>
|
|
1587
|
-
<Button
|
|
1588
|
-
type="button"
|
|
1589
|
-
data-slot="fields-configurator-confirm"
|
|
1590
|
-
variant="primary"
|
|
1591
|
-
@click="confirmChanges"
|
|
2002
|
+
<DialogFooter class="border-t border-zinc-200 px-6 py-4 sm:justify-between">
|
|
2003
|
+
<div
|
|
2004
|
+
data-slot="fields-configurator-copy-actions"
|
|
2005
|
+
class="flex items-center gap-2"
|
|
1592
2006
|
>
|
|
1593
|
-
<
|
|
1594
|
-
|
|
1595
|
-
|
|
2007
|
+
<Button
|
|
2008
|
+
type="button"
|
|
2009
|
+
data-slot="fields-configurator-copy-markdown"
|
|
2010
|
+
variant="ghost"
|
|
2011
|
+
@click="copyMarkdown"
|
|
2012
|
+
>
|
|
2013
|
+
<Icon icon="simple-icons:markdown" />
|
|
2014
|
+
{{ t("copy-markdown") }}
|
|
2015
|
+
</Button>
|
|
2016
|
+
<Button
|
|
2017
|
+
type="button"
|
|
2018
|
+
data-slot="fields-configurator-copy-config"
|
|
2019
|
+
variant="ghost"
|
|
2020
|
+
@click="copyConfig"
|
|
2021
|
+
>
|
|
2022
|
+
<Icon icon="fluent:copy-20-regular" />
|
|
2023
|
+
{{ t("copy-config") }}
|
|
2024
|
+
</Button>
|
|
2025
|
+
</div>
|
|
2026
|
+
<div class="flex items-center gap-2">
|
|
2027
|
+
<Button
|
|
2028
|
+
type="button"
|
|
2029
|
+
data-slot="fields-configurator-cancel"
|
|
2030
|
+
variant="default"
|
|
2031
|
+
@click="discardChanges"
|
|
2032
|
+
>
|
|
2033
|
+
<Icon icon="fluent:dismiss-20-regular" />
|
|
2034
|
+
{{ t("cancel") }}
|
|
2035
|
+
</Button>
|
|
2036
|
+
<Button
|
|
2037
|
+
type="button"
|
|
2038
|
+
data-slot="fields-configurator-confirm"
|
|
2039
|
+
variant="primary"
|
|
2040
|
+
@click="confirmChanges"
|
|
2041
|
+
>
|
|
2042
|
+
<Icon icon="fluent:checkmark-20-regular" />
|
|
2043
|
+
{{ t("confirm") }}
|
|
2044
|
+
</Button>
|
|
2045
|
+
</div>
|
|
1596
2046
|
</DialogFooter>
|
|
1597
2047
|
</DialogContent>
|
|
1598
2048
|
</Dialog>
|
|
@@ -1602,24 +2052,44 @@ function confirmChanges() {
|
|
|
1602
2052
|
{
|
|
1603
2053
|
"zh": {
|
|
1604
2054
|
"configure-fields": "配置字段",
|
|
1605
|
-
"configure-fields-description": "
|
|
2055
|
+
"configure-fields-description": "在这里管理字段列表,并编辑当前选中的通用项或字段配置。",
|
|
1606
2056
|
"field-list": "字段列表",
|
|
1607
2057
|
"general": "通用",
|
|
1608
2058
|
"general-description": "在这里配置字段集合级别的公共选项。",
|
|
2059
|
+
"general-style": "通用样式表达式",
|
|
2060
|
+
"general-style-placeholder": "例如返回一个 style map,例如 display: grid",
|
|
2061
|
+
"general-style-invalid": "样式表达式无效",
|
|
1609
2062
|
"fields-orientation": "布局方向",
|
|
1610
2063
|
"fields-orientation-horizontal": "水平",
|
|
1611
2064
|
"fields-orientation-vertical": "垂直",
|
|
1612
2065
|
"fields-orientation-floating": "浮动标签",
|
|
2066
|
+
"paste-config": "粘贴配置",
|
|
2067
|
+
"paste-config-invalid-json": "粘贴失败,剪贴板不是有效的 JSON。",
|
|
2068
|
+
"paste-config-invalid-schema": "粘贴失败,配置格式无效。",
|
|
2069
|
+
"paste-config-read-failed": "读取剪贴板失败,请检查剪贴板权限。",
|
|
2070
|
+
"copy-paste-error": "复制错误",
|
|
2071
|
+
"copy-paste-error-failed": "复制错误详情失败,请检查剪贴板权限。",
|
|
2072
|
+
"copy-markdown": "复制为 Markdown",
|
|
2073
|
+
"copy-markdown-failed": "复制 Markdown 失败,请先修正当前配置中的错误。",
|
|
2074
|
+
"copy-config": "仅复制配置",
|
|
2075
|
+
"copy-config-failed": "复制配置失败,请先修正当前配置中的错误。",
|
|
2076
|
+
"search-fields": "搜索字段名称……",
|
|
1613
2077
|
"add-field": "新增字段",
|
|
1614
2078
|
"field-type": "字段类型",
|
|
1615
2079
|
"field-type-string": "文本",
|
|
1616
2080
|
"field-type-number": "数字",
|
|
1617
2081
|
"field-type-select": "选择",
|
|
1618
2082
|
"field-type-calendar": "日期",
|
|
2083
|
+
"field-type-slot": "插槽",
|
|
2084
|
+
"field-id": "字段 ID",
|
|
2085
|
+
"field-id-duplicate": "字段 ID 不能重复",
|
|
1619
2086
|
"field-path": "字段路径",
|
|
1620
2087
|
"field-path-placeholder": "例如 profile.name",
|
|
1621
2088
|
"field-path-required": "字段路径不能为空",
|
|
1622
2089
|
"field-path-duplicate": "字段路径不能重复",
|
|
2090
|
+
"copy-field-id": "复制字段 ID:{field}",
|
|
2091
|
+
"copy-field-id-short": "复制 ID",
|
|
2092
|
+
"copy-field-id-failed": "复制字段 ID 失败,请检查剪贴板权限。",
|
|
1623
2093
|
"field-label": "字段标题",
|
|
1624
2094
|
"field-icon": "图标",
|
|
1625
2095
|
"field-icon-placeholder": "例如 fluent:person-20-regular",
|
|
@@ -1666,8 +2136,10 @@ function confirmChanges() {
|
|
|
1666
2136
|
"validation-expression-placeholder": "返回 false 时展示下面的消息",
|
|
1667
2137
|
"validation-message": "失败消息",
|
|
1668
2138
|
"validation-message-placeholder": "支持 Markdown 与双花括号表达式",
|
|
2139
|
+
"no-matches": "没有匹配的字段。",
|
|
1669
2140
|
"no-validation-rules": "暂未配置校验规则。",
|
|
1670
2141
|
"unnamed-field": "未命名{type}字段",
|
|
2142
|
+
"slot-field": "插槽 {id}",
|
|
1671
2143
|
"no-fields": "还没有字段。",
|
|
1672
2144
|
"drag-field": "拖拽调整字段顺序:{field}",
|
|
1673
2145
|
"delete-field": "删除字段:{field}",
|
|
@@ -1676,24 +2148,44 @@ function confirmChanges() {
|
|
|
1676
2148
|
},
|
|
1677
2149
|
"ja": {
|
|
1678
2150
|
"configure-fields": "フィールドを設定",
|
|
1679
|
-
"configure-fields-description": "
|
|
2151
|
+
"configure-fields-description": "ここではフィールド一覧を管理し、選択中の共通設定またはフィールド設定を編集できます。",
|
|
1680
2152
|
"field-list": "フィールド一覧",
|
|
1681
2153
|
"general": "共通",
|
|
1682
2154
|
"general-description": "ここではフィールド群全体に適用される共通設定を編集できます。",
|
|
2155
|
+
"general-style": "共通スタイル式",
|
|
2156
|
+
"general-style-placeholder": "例: style map を返す式。例: display: grid",
|
|
2157
|
+
"general-style-invalid": "スタイル式が無効です",
|
|
1683
2158
|
"fields-orientation": "レイアウト方向",
|
|
1684
2159
|
"fields-orientation-horizontal": "横並び",
|
|
1685
2160
|
"fields-orientation-vertical": "縦並び",
|
|
1686
2161
|
"fields-orientation-floating": "フローティングラベル",
|
|
2162
|
+
"paste-config": "設定を貼り付け",
|
|
2163
|
+
"paste-config-invalid-json": "貼り付けに失敗しました。クリップボードの内容が有効な JSON ではありません。",
|
|
2164
|
+
"paste-config-invalid-schema": "貼り付けに失敗しました。設定形式が無効です。",
|
|
2165
|
+
"paste-config-read-failed": "クリップボードの読み取りに失敗しました。権限を確認してください。",
|
|
2166
|
+
"copy-paste-error": "エラーをコピー",
|
|
2167
|
+
"copy-paste-error-failed": "エラー詳細のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
2168
|
+
"copy-markdown": "Markdown としてコピー",
|
|
2169
|
+
"copy-markdown-failed": "Markdown のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
2170
|
+
"copy-config": "設定のみコピー",
|
|
2171
|
+
"copy-config-failed": "設定のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
2172
|
+
"search-fields": "フィールド名を検索…",
|
|
1687
2173
|
"add-field": "フィールドを追加",
|
|
1688
2174
|
"field-type": "フィールド種別",
|
|
1689
2175
|
"field-type-string": "テキスト",
|
|
1690
2176
|
"field-type-number": "数値",
|
|
1691
2177
|
"field-type-select": "選択",
|
|
1692
2178
|
"field-type-calendar": "日付",
|
|
2179
|
+
"field-type-slot": "スロット",
|
|
2180
|
+
"field-id": "フィールド ID",
|
|
2181
|
+
"field-id-duplicate": "フィールド ID は重複できません",
|
|
1693
2182
|
"field-path": "フィールドパス",
|
|
1694
2183
|
"field-path-placeholder": "例: profile.name",
|
|
1695
2184
|
"field-path-required": "フィールドパスは必須です",
|
|
1696
2185
|
"field-path-duplicate": "フィールドパスは重複できません",
|
|
2186
|
+
"copy-field-id": "{field} のフィールド ID をコピー",
|
|
2187
|
+
"copy-field-id-short": "ID をコピー",
|
|
2188
|
+
"copy-field-id-failed": "フィールド ID のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
1697
2189
|
"field-label": "フィールドラベル",
|
|
1698
2190
|
"field-icon": "アイコン",
|
|
1699
2191
|
"field-icon-placeholder": "例: fluent:person-20-regular",
|
|
@@ -1740,8 +2232,10 @@ function confirmChanges() {
|
|
|
1740
2232
|
"validation-expression-placeholder": "false を返すと下のメッセージを表示します",
|
|
1741
2233
|
"validation-message": "失敗メッセージ",
|
|
1742
2234
|
"validation-message-placeholder": "Markdown と二重波括弧式を利用できます",
|
|
2235
|
+
"no-matches": "一致するフィールドがありません。",
|
|
1743
2236
|
"no-validation-rules": "検証ルールはまだありません。",
|
|
1744
2237
|
"unnamed-field": "未命名の{type}フィールド",
|
|
2238
|
+
"slot-field": "スロット {id}",
|
|
1745
2239
|
"no-fields": "フィールドがありません。",
|
|
1746
2240
|
"drag-field": "{field} の順序をドラッグで変更",
|
|
1747
2241
|
"delete-field": "{field} を削除",
|
|
@@ -1750,24 +2244,44 @@ function confirmChanges() {
|
|
|
1750
2244
|
},
|
|
1751
2245
|
"en": {
|
|
1752
2246
|
"configure-fields": "Configure Fields",
|
|
1753
|
-
"configure-fields-description": "
|
|
2247
|
+
"configure-fields-description": "Manage the field list and edit the selected general or field settings here.",
|
|
1754
2248
|
"field-list": "Field list",
|
|
1755
2249
|
"general": "General",
|
|
1756
2250
|
"general-description": "Edit the shared settings that apply to the whole field group here.",
|
|
2251
|
+
"general-style": "Shared style expression",
|
|
2252
|
+
"general-style-placeholder": "Return a style map, for example display: grid",
|
|
2253
|
+
"general-style-invalid": "The style expression is invalid",
|
|
1757
2254
|
"fields-orientation": "Layout orientation",
|
|
1758
2255
|
"fields-orientation-horizontal": "Horizontal",
|
|
1759
2256
|
"fields-orientation-vertical": "Vertical",
|
|
1760
2257
|
"fields-orientation-floating": "Floating label",
|
|
2258
|
+
"paste-config": "Paste Config",
|
|
2259
|
+
"paste-config-invalid-json": "Paste failed because the clipboard does not contain valid JSON.",
|
|
2260
|
+
"paste-config-invalid-schema": "Paste failed because the config shape is invalid.",
|
|
2261
|
+
"paste-config-read-failed": "Failed to read from the clipboard. Check clipboard permissions.",
|
|
2262
|
+
"copy-paste-error": "Copy Error",
|
|
2263
|
+
"copy-paste-error-failed": "Failed to copy the error details. Check clipboard permissions.",
|
|
2264
|
+
"copy-markdown": "Copy as Markdown",
|
|
2265
|
+
"copy-markdown-failed": "Failed to copy Markdown. Fix the current config errors first.",
|
|
2266
|
+
"copy-config": "Copy Config Only",
|
|
2267
|
+
"copy-config-failed": "Failed to copy the config. Fix the current config errors first.",
|
|
2268
|
+
"search-fields": "Search fields…",
|
|
1761
2269
|
"add-field": "Add field",
|
|
1762
2270
|
"field-type": "Field type",
|
|
1763
2271
|
"field-type-string": "Text",
|
|
1764
2272
|
"field-type-number": "Number",
|
|
1765
2273
|
"field-type-select": "Select",
|
|
1766
2274
|
"field-type-calendar": "Date",
|
|
2275
|
+
"field-type-slot": "Slot",
|
|
2276
|
+
"field-id": "Field ID",
|
|
2277
|
+
"field-id-duplicate": "Field ID must be unique",
|
|
1767
2278
|
"field-path": "Field path",
|
|
1768
2279
|
"field-path-placeholder": "For example profile.name",
|
|
1769
2280
|
"field-path-required": "Field path is required",
|
|
1770
2281
|
"field-path-duplicate": "Field path must be unique",
|
|
2282
|
+
"copy-field-id": "Copy field ID: {field}",
|
|
2283
|
+
"copy-field-id-short": "Copy ID",
|
|
2284
|
+
"copy-field-id-failed": "Failed to copy the field ID. Check clipboard permissions.",
|
|
1771
2285
|
"field-label": "Field label",
|
|
1772
2286
|
"field-icon": "Icon",
|
|
1773
2287
|
"field-icon-placeholder": "For example fluent:person-20-regular",
|
|
@@ -1814,8 +2328,10 @@ function confirmChanges() {
|
|
|
1814
2328
|
"validation-expression-placeholder": "Return false to show the message below",
|
|
1815
2329
|
"validation-message": "Failure message",
|
|
1816
2330
|
"validation-message-placeholder": "Supports Markdown and double-curly expressions",
|
|
2331
|
+
"no-matches": "No matching fields.",
|
|
1817
2332
|
"no-validation-rules": "No validation rules yet.",
|
|
1818
2333
|
"unnamed-field": "Untitled {type} field",
|
|
2334
|
+
"slot-field": "Slot {id}",
|
|
1819
2335
|
"no-fields": "No fields yet.",
|
|
1820
2336
|
"drag-field": "Drag to reorder field {field}",
|
|
1821
2337
|
"delete-field": "Delete field {field}",
|