@mapomodule/form 2.0.0-beta.5 → 2.0.0-beta.6
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.d.ts.map +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +4 -1
- package/dist/runtime/components/MapoFocusPortal.vue +3 -1
- package/dist/runtime/components/MapoFocusPortal.vue.d.ts.map +1 -1
- package/dist/runtime/components/MapoFormTabs.vue +4 -2
- package/dist/runtime/components/MapoFormTabs.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoDateField.vue +3 -1
- package/dist/runtime/components/fields/MapoDateField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoDateTimeField.vue +5 -3
- package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoEnhancedMediaField.vue +7 -5
- package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoFileField.vue +3 -1
- package/dist/runtime/components/fields/MapoFileField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoFksField.vue +6 -2
- package/dist/runtime/components/fields/MapoFksField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoMapField.vue +4 -2
- package/dist/runtime/components/fields/MapoMapField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoMediaField.vue +3 -1
- package/dist/runtime/components/fields/MapoMediaField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoMediaM2mField.vue +5 -1
- package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoRepeater.vue +32 -22
- package/dist/runtime/components/fields/MapoRepeater.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoRepeaterItem.vue +13 -8
- package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoSeoPreview.vue +16 -12
- package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts.map +1 -1
- package/dist/runtime/components/fields/MapoWygEditor.vue +5 -1
- package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts.map +1 -1
- package/dist/runtime/composables/useMapoForm.d.ts.map +1 -1
- package/dist/runtime/composables/useMapoForm.js +4 -2
- package/dist/runtime/composables/useUnsavedChangesGuard.d.ts.map +1 -1
- package/dist/runtime/composables/useUnsavedChangesGuard.js +3 -2
- package/package.json +8 -6
- package/src/__tests__/useMapoForm.test.ts +5 -0
- package/src/module.ts +9 -0
- package/src/runtime/components/MapoFocusPortal.vue +3 -1
- package/src/runtime/components/MapoFormTabs.vue +4 -1
- package/src/runtime/components/fields/MapoDateField.vue +3 -1
- package/src/runtime/components/fields/MapoDateTimeField.vue +3 -1
- package/src/runtime/components/fields/MapoEnhancedMediaField.vue +8 -5
- package/src/runtime/components/fields/MapoFileField.vue +3 -1
- package/src/runtime/components/fields/MapoFksField.vue +7 -2
- package/src/runtime/components/fields/MapoMapField.vue +5 -2
- package/src/runtime/components/fields/MapoMediaField.vue +4 -1
- package/src/runtime/components/fields/MapoMediaM2mField.vue +8 -1
- package/src/runtime/components/fields/MapoRepeater.vue +33 -26
- package/src/runtime/components/fields/MapoRepeaterItem.vue +14 -8
- package/src/runtime/components/fields/MapoSeoPreview.vue +17 -13
- package/src/runtime/components/fields/MapoWygEditor.vue +8 -1
- package/src/runtime/composables/useMapoForm.ts +4 -2
- package/src/runtime/composables/useUnsavedChangesGuard.ts +3 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed, watch, watchEffect } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import { useMapoForm, injectMapoForm } from "../../composables/useMapoForm.js";
|
|
4
5
|
import { useFocusMode } from "../../composables/useFocusMode.js";
|
|
5
6
|
import MapoFormField from "../MapoFormField.vue";
|
|
7
|
+
const { t } = useI18n();
|
|
6
8
|
const props = defineProps({
|
|
7
9
|
uid: { type: String, required: false },
|
|
8
10
|
item: { type: Object, required: true },
|
|
@@ -105,7 +107,10 @@ function enterFocusMode() {
|
|
|
105
107
|
registry: props.registry,
|
|
106
108
|
languages: props.languages,
|
|
107
109
|
currentLang: props.currentLang,
|
|
108
|
-
breadcrumb: [
|
|
110
|
+
breadcrumb: [
|
|
111
|
+
props.repeaterDescriptor?.label ?? t("mapo.repeater.repeater"),
|
|
112
|
+
label.value
|
|
113
|
+
]
|
|
109
114
|
});
|
|
110
115
|
}
|
|
111
116
|
</script>
|
|
@@ -178,7 +183,7 @@ function enterFocusMode() {
|
|
|
178
183
|
color="error"
|
|
179
184
|
icon="i-lucide-trash-2"
|
|
180
185
|
:disabled="readonly"
|
|
181
|
-
title="
|
|
186
|
+
:title="t('mapo.delete')"
|
|
182
187
|
@click.stop="$emit('delete')"
|
|
183
188
|
/>
|
|
184
189
|
<UIcon name="i-lucide-chevron-down" class="h-4 w-4 text-gray-400" />
|
|
@@ -239,7 +244,7 @@ function enterFocusMode() {
|
|
|
239
244
|
:max="totalItems"
|
|
240
245
|
size="xs"
|
|
241
246
|
class="w-14 text-center"
|
|
242
|
-
title="
|
|
247
|
+
:title="t('mapo.repeater.position')"
|
|
243
248
|
@change="
|
|
244
249
|
$emit(
|
|
245
250
|
'move-to',
|
|
@@ -261,7 +266,7 @@ function enterFocusMode() {
|
|
|
261
266
|
variant="ghost"
|
|
262
267
|
color="neutral"
|
|
263
268
|
icon="i-lucide-maximize-2"
|
|
264
|
-
title="
|
|
269
|
+
:title="t('mapo.repeater.editFullScreen')"
|
|
265
270
|
@click="enterFocusMode"
|
|
266
271
|
/>
|
|
267
272
|
|
|
@@ -271,7 +276,7 @@ function enterFocusMode() {
|
|
|
271
276
|
color="neutral"
|
|
272
277
|
icon="i-lucide-chevron-up"
|
|
273
278
|
:disabled="readonly"
|
|
274
|
-
title="
|
|
279
|
+
:title="t('mapo.repeater.moveUp')"
|
|
275
280
|
@click="$emit('move-up')"
|
|
276
281
|
/>
|
|
277
282
|
<UButton
|
|
@@ -280,7 +285,7 @@ function enterFocusMode() {
|
|
|
280
285
|
color="neutral"
|
|
281
286
|
icon="i-lucide-chevron-down"
|
|
282
287
|
:disabled="readonly"
|
|
283
|
-
title="
|
|
288
|
+
:title="t('mapo.repeater.moveDown')"
|
|
284
289
|
@click="$emit('move-down')"
|
|
285
290
|
/>
|
|
286
291
|
|
|
@@ -291,7 +296,7 @@ function enterFocusMode() {
|
|
|
291
296
|
color="neutral"
|
|
292
297
|
icon="i-lucide-copy"
|
|
293
298
|
:disabled="readonly"
|
|
294
|
-
title="
|
|
299
|
+
:title="t('mapo.duplicate')"
|
|
295
300
|
@click="$emit('duplicate')"
|
|
296
301
|
/>
|
|
297
302
|
|
|
@@ -301,7 +306,7 @@ function enterFocusMode() {
|
|
|
301
306
|
color="error"
|
|
302
307
|
icon="i-lucide-trash-2"
|
|
303
308
|
:disabled="readonly"
|
|
304
|
-
title="
|
|
309
|
+
:title="t('mapo.delete')"
|
|
305
310
|
@click="$emit('delete')"
|
|
306
311
|
/>
|
|
307
312
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapoRepeaterItem.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoRepeaterItem.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MapoRepeaterItem.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoRepeaterItem.vue"],"names":[],"mappings":"AAoaA,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAO9B,KAAK,WAAW,GAAG;IACjB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAszBF,QAAA,IAAI,QAAQ,IAAY,CAAE;AAC1B,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,CAAC;AAKhD,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;kFAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import { useRequestURL } from "#app";
|
|
5
|
+
const { t } = useI18n();
|
|
4
6
|
const props = defineProps({
|
|
5
7
|
modelValue: { type: null, required: true },
|
|
6
8
|
descriptor: { type: Object, required: true },
|
|
@@ -40,7 +42,9 @@ const displayUrl = computed(() => value.value.permalink || requestUrl.host);
|
|
|
40
42
|
<div class="space-y-3">
|
|
41
43
|
<div>
|
|
42
44
|
<div class="mb-1 flex items-center justify-between">
|
|
43
|
-
<label class="text-sm font-medium text-gray-700">
|
|
45
|
+
<label class="text-sm font-medium text-gray-700">{{
|
|
46
|
+
t("mapo.url")
|
|
47
|
+
}}</label>
|
|
44
48
|
</div>
|
|
45
49
|
<UInput
|
|
46
50
|
:model-value="value.permalink"
|
|
@@ -54,14 +58,16 @@ const displayUrl = computed(() => value.value.permalink || requestUrl.host);
|
|
|
54
58
|
|
|
55
59
|
<div>
|
|
56
60
|
<div class="mb-1 flex items-center justify-between">
|
|
57
|
-
<label class="text-sm font-medium text-gray-700">
|
|
61
|
+
<label class="text-sm font-medium text-gray-700">{{
|
|
62
|
+
t("mapo.seoPreview.seoTitle")
|
|
63
|
+
}}</label>
|
|
58
64
|
<span class="text-xs" :class="titleColor">{{ titleLen }} / 60</span>
|
|
59
65
|
</div>
|
|
60
66
|
<UInput
|
|
61
67
|
:model-value="value.title"
|
|
62
68
|
:readonly="readonly"
|
|
63
69
|
:disabled="disabled"
|
|
64
|
-
placeholder="
|
|
70
|
+
:placeholder="t('mapo.seoPreview.titlePlaceholder')"
|
|
65
71
|
class="w-full"
|
|
66
72
|
@update:model-value="update('title', $event)"
|
|
67
73
|
/>
|
|
@@ -69,9 +75,9 @@ const displayUrl = computed(() => value.value.permalink || requestUrl.host);
|
|
|
69
75
|
|
|
70
76
|
<div>
|
|
71
77
|
<div class="mb-1 flex items-center justify-between">
|
|
72
|
-
<label class="text-sm font-medium text-gray-700"
|
|
73
|
-
|
|
74
|
-
>
|
|
78
|
+
<label class="text-sm font-medium text-gray-700">{{
|
|
79
|
+
t("mapo.seoPreview.metaDescription")
|
|
80
|
+
}}</label>
|
|
75
81
|
<span class="text-xs" :class="descColor">{{ descLen }} / 155</span>
|
|
76
82
|
</div>
|
|
77
83
|
<UTextarea
|
|
@@ -79,7 +85,7 @@ const displayUrl = computed(() => value.value.permalink || requestUrl.host);
|
|
|
79
85
|
:rows="3"
|
|
80
86
|
:readonly="readonly"
|
|
81
87
|
:disabled="disabled"
|
|
82
|
-
placeholder="
|
|
88
|
+
:placeholder="t('mapo.seoPreview.descriptionPlaceholder')"
|
|
83
89
|
class="w-full"
|
|
84
90
|
@update:model-value="update('description', $event)"
|
|
85
91
|
/>
|
|
@@ -89,19 +95,17 @@ const displayUrl = computed(() => value.value.permalink || requestUrl.host);
|
|
|
89
95
|
<!-- SERP preview -->
|
|
90
96
|
<div class="rounded border border-gray-200 bg-gray-50 p-4">
|
|
91
97
|
<p class="mb-1 text-xs font-medium uppercase tracking-wide text-gray-400">
|
|
92
|
-
|
|
98
|
+
{{ t("mapo.seoPreview.googlePreview") }}
|
|
93
99
|
</p>
|
|
94
100
|
<div class="max-w-lg">
|
|
95
101
|
<p class="truncate text-sm text-green-700">
|
|
96
102
|
{{ displayUrl }}
|
|
97
103
|
</p>
|
|
98
104
|
<p class="truncate text-lg text-blue-700 underline">
|
|
99
|
-
{{ value.title || "
|
|
105
|
+
{{ value.title || t("mapo.seoPreview.defaultTitle") }}
|
|
100
106
|
</p>
|
|
101
107
|
<p class="mt-1 line-clamp-2 text-sm text-gray-600">
|
|
102
|
-
{{
|
|
103
|
-
value.description || "Page description that will appear in Google search results."
|
|
104
|
-
}}
|
|
108
|
+
{{ value.description || t("mapo.seoPreview.defaultDescription") }}
|
|
105
109
|
</p>
|
|
106
110
|
</div>
|
|
107
111
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapoSeoPreview.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoSeoPreview.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MapoSeoPreview.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoSeoPreview.vue"],"names":[],"mappings":"AAmJA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,UAAU,QAAQ;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAySF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, watch, onBeforeUnmount, resolveComponent } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import { useEditor, EditorContent } from "@tiptap/vue-3";
|
|
4
5
|
import StarterKit from "@tiptap/starter-kit";
|
|
5
6
|
import Link from "@tiptap/extension-link";
|
|
@@ -13,6 +14,7 @@ const props = defineProps({
|
|
|
13
14
|
disabled: { type: Boolean, required: false }
|
|
14
15
|
});
|
|
15
16
|
const emit = defineEmits(["update:modelValue"]);
|
|
17
|
+
const { t } = useI18n();
|
|
16
18
|
const extraExtensions = props.descriptor.attrs?.extensions ?? [];
|
|
17
19
|
const ALLOWED_LINK_PROTOCOLS = ["http", "https", "mailto", "tel"];
|
|
18
20
|
function isSafeUrl(url) {
|
|
@@ -206,7 +208,9 @@ function onImageConfirm(selection) {
|
|
|
206
208
|
|
|
207
209
|
<!-- Insert Image via Media Manager (available when @mapomodule/uikit is installed) -->
|
|
208
210
|
<UTooltip
|
|
209
|
-
:text="
|
|
211
|
+
:text="
|
|
212
|
+
hasMediaManager ? t('mapo.wygEditor.insertImage') : t('mapo.wygEditor.requiresUikit')
|
|
213
|
+
"
|
|
210
214
|
:delay-open="300"
|
|
211
215
|
>
|
|
212
216
|
<UButton
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapoWygEditor.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoWygEditor.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MapoWygEditor.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoWygEditor.vue"],"names":[],"mappings":"AAqTA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA0pBF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMapoForm.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/useMapoForm.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,GAAG,EACR,KAAK,QAAQ,EAGd,MAAM,KAAK,CAAC;AAOb,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useMapoForm.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/useMapoForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { resolveFieldAccessor } from "../types/index.js";
|
|
17
17
|
import { useCurrentLang } from "./useCurrentLang.js";
|
|
18
18
|
import { useSnackStore } from "@mapomodule/store/runtime/stores/snack";
|
|
19
|
+
import { useMapoT } from "@mapomodule/i18n/runtime/composables/useMapoT";
|
|
19
20
|
import { useAuthStore } from "@mapomodule/store/runtime/stores/auth";
|
|
20
21
|
function safeClone(v) {
|
|
21
22
|
const raw = toRaw(v);
|
|
@@ -48,6 +49,7 @@ export function useMapoForm(options) {
|
|
|
48
49
|
} = options;
|
|
49
50
|
const currentLang = useCurrentLang(currentLangProp);
|
|
50
51
|
const snack = useSnackStore();
|
|
52
|
+
const t = useMapoT();
|
|
51
53
|
const authStore = useAuthStore();
|
|
52
54
|
const backup = ref(safeClone(model.value));
|
|
53
55
|
const clientErrors = ref({});
|
|
@@ -198,7 +200,7 @@ export function useMapoForm(options) {
|
|
|
198
200
|
function checkRequired(descriptor, val) {
|
|
199
201
|
if (!descriptor.required) return null;
|
|
200
202
|
if (val === null || val === void 0 || val === "")
|
|
201
|
-
return "
|
|
203
|
+
return t("mapo.form.required");
|
|
202
204
|
return null;
|
|
203
205
|
}
|
|
204
206
|
function getClientError(descriptor) {
|
|
@@ -236,7 +238,7 @@ export function useMapoForm(options) {
|
|
|
236
238
|
submitted.value = true;
|
|
237
239
|
const { valid } = validateClient();
|
|
238
240
|
if (!valid) {
|
|
239
|
-
snack.show("
|
|
241
|
+
snack.show(t("mapo.fixErrors"), "error");
|
|
240
242
|
return;
|
|
241
243
|
}
|
|
242
244
|
isLoading.value = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUnsavedChangesGuard.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/useUnsavedChangesGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"useUnsavedChangesGuard.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/useUnsavedChangesGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAIhD,uEAAuE;AACvE,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,EACrB,OAAO,GAAE,0BAA+B,QA6BzC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { onBeforeUnmount } from "vue";
|
|
2
|
-
import { onBeforeRouteLeave } from "
|
|
2
|
+
import { onBeforeRouteLeave } from "#app";
|
|
3
|
+
import { useMapoT } from "@mapomodule/i18n/runtime/composables/useMapoT";
|
|
3
4
|
export function useUnsavedChangesGuard(isDirty, options = {}) {
|
|
4
|
-
const message = options.message ?? "
|
|
5
|
+
const message = options.message ?? useMapoT()("mapo.confirmLeaveChanges");
|
|
5
6
|
const canUseWindow = typeof window !== "undefined";
|
|
6
7
|
const isEnabled = () => {
|
|
7
8
|
if (typeof options.enabled === "function") return options.enabled();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapomodule/form",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.6",
|
|
4
4
|
"description": "Mapo declarative typed form engine and field components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,15 +48,17 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@internationalized/date": "^3.7.0",
|
|
51
|
+
"@mapomodule/i18n": "workspace:*",
|
|
51
52
|
"@mapomodule/store": "2.0.0-beta.4",
|
|
52
|
-
"@mapomodule/utils": "2.0.0-beta.
|
|
53
|
+
"@mapomodule/utils": "2.0.0-beta.3",
|
|
53
54
|
"@tiptap/extension-image": "^2.11.5",
|
|
54
55
|
"@tiptap/extension-link": "^2.11.5",
|
|
55
56
|
"@tiptap/extension-underline": "^2.11.5",
|
|
56
57
|
"@tiptap/starter-kit": "^2.11.5",
|
|
57
58
|
"@tiptap/vue-3": "^2.11.5",
|
|
58
59
|
"leaflet": "^1.9.4",
|
|
59
|
-
"vue-draggable-plus": "^0.6.0"
|
|
60
|
+
"vue-draggable-plus": "^0.6.0",
|
|
61
|
+
"vue-i18n": "^11.4.0"
|
|
60
62
|
},
|
|
61
63
|
"optionalDependencies": {
|
|
62
64
|
"@types/leaflet": "^1.9.18"
|
|
@@ -69,10 +71,10 @@
|
|
|
69
71
|
"@nuxt/kit": "4.4.2",
|
|
70
72
|
"@nuxt/module-builder": "^1.0.2",
|
|
71
73
|
"@nuxt/schema": "4.4.2",
|
|
72
|
-
"nuxt": "^4.
|
|
74
|
+
"nuxt": "^4.5.1",
|
|
73
75
|
"vitest": "^3.2.0",
|
|
74
|
-
"vue": "^3.5.
|
|
75
|
-
"vue-router": "^5.0
|
|
76
|
+
"vue": "^3.5.40",
|
|
77
|
+
"vue-router": "^5.2.0",
|
|
76
78
|
"vue-tsc": "^3.3.4",
|
|
77
79
|
"zod": "^3.23.8"
|
|
78
80
|
}
|
|
@@ -10,6 +10,11 @@ vi.mock("@mapomodule/store/runtime/stores/auth", () => ({
|
|
|
10
10
|
vi.mock("@mapomodule/store/runtime/stores/snack", () => ({
|
|
11
11
|
useSnackStore: () => ({ show: vi.fn() }),
|
|
12
12
|
}));
|
|
13
|
+
// Pulls in `nuxt/app`, which cannot resolve outside a Nuxt build. Assertions
|
|
14
|
+
// below match on message keys, so echoing the key back is the useful stub.
|
|
15
|
+
vi.mock("@mapomodule/i18n/runtime/composables/useMapoT", () => ({
|
|
16
|
+
useMapoT: () => (key: string) => key,
|
|
17
|
+
}));
|
|
13
18
|
|
|
14
19
|
// provide/inject called outside setup() emits a warning; silence it in tests.
|
|
15
20
|
beforeEach(() => {
|
package/src/module.ts
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
createResolver,
|
|
7
7
|
addImports,
|
|
8
8
|
extendPages,
|
|
9
|
+
hasNuxtModule,
|
|
10
|
+
installModule,
|
|
9
11
|
} from "@nuxt/kit";
|
|
10
12
|
import type { NuxtModule } from "@nuxt/schema";
|
|
11
13
|
import type { PartialFieldRegistry } from "./runtime/types/index.js";
|
|
@@ -40,6 +42,13 @@ export default defineNuxtModule<MapoFormOptions>({
|
|
|
40
42
|
/@mapomodule[\\/]form[\\/]/,
|
|
41
43
|
);
|
|
42
44
|
|
|
45
|
+
// Form components render user-facing strings through vue-i18n
|
|
46
|
+
// (`useI18n().t("mapo...")`). @mapomodule/i18n provides the catalogs and
|
|
47
|
+
// installs @nuxtjs/i18n unless the app manages it itself.
|
|
48
|
+
if (!hasNuxtModule("@mapomodule/i18n")) {
|
|
49
|
+
await installModule(await resolver.resolvePath("@mapomodule/i18n"));
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
// Only serializable values can go into runtimeConfig.public.
|
|
44
53
|
// `mapping` and `accessor` contain functions → not SSR-serializable.
|
|
45
54
|
// To extend mapping/accessor, use an app plugin that accesses $mapoFormRegistry:
|
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
13
13
|
import { watch } from "vue";
|
|
14
|
+
import { useI18n } from "vue-i18n";
|
|
14
15
|
import { useFocusMode } from "../composables/useFocusMode.js";
|
|
15
16
|
import MapoForm from "./MapoForm.vue";
|
|
16
17
|
|
|
18
|
+
const { t } = useI18n();
|
|
17
19
|
const { focusTarget, exit } = useFocusMode();
|
|
18
20
|
|
|
19
21
|
const isOpen = computed(() => focusTarget.value !== null);
|
|
@@ -88,7 +90,7 @@ function onUpdate(val: Record<string, unknown>) {
|
|
|
88
90
|
variant="ghost"
|
|
89
91
|
color="neutral"
|
|
90
92
|
icon="i-lucide-x"
|
|
91
|
-
label="
|
|
93
|
+
:label="t('mapo.close')"
|
|
92
94
|
size="sm"
|
|
93
95
|
@click="exit"
|
|
94
96
|
/>
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import type { TabEntry } from "../types/layout.js";
|
|
4
5
|
import { injectMapoForm } from "../composables/useMapoForm.js";
|
|
5
6
|
import MapoFormGroup from "./MapoFormGroup.vue";
|
|
6
7
|
import MapoFormFlatSection from "./MapoFormFlatSection.vue";
|
|
7
8
|
|
|
9
|
+
const { t } = useI18n();
|
|
10
|
+
|
|
8
11
|
const props = defineProps<{ tabs: TabEntry[] }>();
|
|
9
12
|
|
|
10
13
|
defineSlots<Record<string, unknown>>();
|
|
@@ -54,7 +57,7 @@ const tabsWithErrors = computed(() => {
|
|
|
54
57
|
<span
|
|
55
58
|
v-if="tabsWithErrors.has(tab.name)"
|
|
56
59
|
class="inline-block h-1.5 w-1.5 rounded-full bg-red-500"
|
|
57
|
-
aria-label="
|
|
60
|
+
:aria-label="t('mapo.form.tabErrors')"
|
|
58
61
|
/>
|
|
59
62
|
</button>
|
|
60
63
|
<!-- Extra content appended to the tab bar (actions, badges, …). -->
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import { parseDate, CalendarDate } from "@internationalized/date";
|
|
4
5
|
import type { DateDescriptor } from "../../types/index.js";
|
|
5
6
|
|
|
@@ -14,6 +15,7 @@ const props = defineProps<{
|
|
|
14
15
|
|
|
15
16
|
const emit = defineEmits<{ "update:modelValue": [value: string | null] }>();
|
|
16
17
|
|
|
18
|
+
const { t } = useI18n();
|
|
17
19
|
const open = ref(false);
|
|
18
20
|
|
|
19
21
|
/** Parsed calendar value derived from the current ISO-like model string. */
|
|
@@ -30,7 +32,7 @@ const calendarValue = computed<CalendarDate | undefined>(() => {
|
|
|
30
32
|
|
|
31
33
|
const displayLabel = computed(() => {
|
|
32
34
|
const v = calendarValue.value;
|
|
33
|
-
if (!v) return "
|
|
35
|
+
if (!v) return t("mapo.dateField.selectDate");
|
|
34
36
|
return `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
|
|
35
37
|
});
|
|
36
38
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
today,
|
|
7
7
|
getLocalTimeZone,
|
|
8
8
|
} from "@internationalized/date";
|
|
9
|
+
import { useI18n } from "vue-i18n";
|
|
9
10
|
import type { DateDescriptor } from "../../types/index.js";
|
|
10
11
|
|
|
11
12
|
/** Datetime field with configurable naive/UTC serialisation semantics. */
|
|
@@ -19,6 +20,7 @@ const props = defineProps<{
|
|
|
19
20
|
|
|
20
21
|
const emit = defineEmits<{ "update:modelValue": [value: string | null] }>();
|
|
21
22
|
|
|
23
|
+
const { t } = useI18n();
|
|
22
24
|
const open = ref(false);
|
|
23
25
|
|
|
24
26
|
// Timezone strategy configurable through `descriptor.attrs.tz`:
|
|
@@ -72,7 +74,7 @@ function emitFromCalendar(v: CalendarDateTime) {
|
|
|
72
74
|
// Displayed label in the trigger button
|
|
73
75
|
const displayLabel = computed(() => {
|
|
74
76
|
const v = calendarValue.value;
|
|
75
|
-
if (!v) return "
|
|
77
|
+
if (!v) return t("mapo.dateField.selectDateTime");
|
|
76
78
|
const date = `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
|
|
77
79
|
const time = `${String(v.hour).padStart(2, "0")}:${String(v.minute).padStart(2, "0")}`;
|
|
78
80
|
return `${date} ${time}`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import type {
|
|
4
5
|
EnhancedMediaDescriptor,
|
|
5
6
|
EnhancedMediaValue,
|
|
@@ -19,6 +20,8 @@ const emit = defineEmits<{
|
|
|
19
20
|
"update:modelValue": [value: EnhancedMediaValue | null];
|
|
20
21
|
}>();
|
|
21
22
|
|
|
23
|
+
const { t } = useI18n();
|
|
24
|
+
|
|
22
25
|
const dialogOpen = ref(false);
|
|
23
26
|
|
|
24
27
|
// Picker dialog comes from @mapomodule/uikit, resolved at runtime.
|
|
@@ -83,7 +86,7 @@ function clear() {
|
|
|
83
86
|
<MapoMediaPreview v-if="current" :media="current" size="md" />
|
|
84
87
|
<div v-else class="flex flex-col items-center gap-1 text-dimmed">
|
|
85
88
|
<UIcon name="i-lucide-image-plus" class="size-7" />
|
|
86
|
-
<span class="text-[10px]">
|
|
89
|
+
<span class="text-[10px]">{{ t("mapo.select") }}</span>
|
|
87
90
|
</div>
|
|
88
91
|
</div>
|
|
89
92
|
</div>
|
|
@@ -101,7 +104,7 @@ function clear() {
|
|
|
101
104
|
size="xs"
|
|
102
105
|
variant="ghost"
|
|
103
106
|
color="neutral"
|
|
104
|
-
title="
|
|
107
|
+
:title="t('mapo.change')"
|
|
105
108
|
@click="dialogOpen = true"
|
|
106
109
|
/>
|
|
107
110
|
<UButton
|
|
@@ -110,7 +113,7 @@ function clear() {
|
|
|
110
113
|
size="xs"
|
|
111
114
|
variant="ghost"
|
|
112
115
|
color="neutral"
|
|
113
|
-
title="
|
|
116
|
+
:title="t('mapo.remove')"
|
|
114
117
|
@click="clear"
|
|
115
118
|
/>
|
|
116
119
|
</div>
|
|
@@ -119,13 +122,13 @@ function clear() {
|
|
|
119
122
|
<UInput
|
|
120
123
|
v-model="altValue"
|
|
121
124
|
size="sm"
|
|
122
|
-
placeholder="
|
|
125
|
+
:placeholder="t('mapo.enhancedMediaField.altText')"
|
|
123
126
|
:disabled="readonly || disabled || !current"
|
|
124
127
|
/>
|
|
125
128
|
<UInput
|
|
126
129
|
v-model="captionValue"
|
|
127
130
|
size="sm"
|
|
128
|
-
placeholder="
|
|
131
|
+
:placeholder="t('mapo.enhancedMediaField.caption')"
|
|
129
132
|
:disabled="readonly || disabled || !current"
|
|
130
133
|
/>
|
|
131
134
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import type { FileDescriptor } from "../../types/index.js";
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{
|
|
@@ -12,6 +13,7 @@ const props = defineProps<{
|
|
|
12
13
|
|
|
13
14
|
const emit = defineEmits<{ "update:modelValue": [value: File | null] }>();
|
|
14
15
|
|
|
16
|
+
const { t } = useI18n();
|
|
15
17
|
const inputRef = ref<HTMLInputElement | null>(null);
|
|
16
18
|
|
|
17
19
|
const currentFileName = computed<string | null>(() => {
|
|
@@ -78,7 +80,7 @@ const accept = computed(
|
|
|
78
80
|
variant="ghost"
|
|
79
81
|
color="neutral"
|
|
80
82
|
icon="i-lucide-x"
|
|
81
|
-
aria-label="
|
|
83
|
+
:aria-label="t('mapo.fileField.removeFile')"
|
|
82
84
|
@click="clear"
|
|
83
85
|
/>
|
|
84
86
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, watch, onMounted } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import { useNuxtApp } from "#app";
|
|
4
5
|
import { debounce } from "@mapomodule/utils";
|
|
5
6
|
import type { FksDescriptor } from "../../types/index.js";
|
|
@@ -14,6 +15,8 @@ const props = defineProps<{
|
|
|
14
15
|
|
|
15
16
|
const emit = defineEmits<{ "update:modelValue": [value: unknown] }>();
|
|
16
17
|
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
|
|
17
20
|
const attrs = computed(() => props.descriptor.attrs);
|
|
18
21
|
const isMultiple = computed(
|
|
19
22
|
() => props.descriptor.type === "m2m" || !!attrs.value.multiple,
|
|
@@ -70,7 +73,7 @@ const internalValue = computed({
|
|
|
70
73
|
|
|
71
74
|
// Placeholder
|
|
72
75
|
const placeholder = computed(() =>
|
|
73
|
-
isMultiple.value ? "
|
|
76
|
+
isMultiple.value ? t("mapo.fksField.searchSelect") : t("mapo.search"),
|
|
74
77
|
);
|
|
75
78
|
|
|
76
79
|
// Chip text shown in the trigger.
|
|
@@ -144,7 +147,9 @@ function getItemKey(item: unknown): unknown {
|
|
|
144
147
|
<!-- Empty state -->
|
|
145
148
|
<template #empty>
|
|
146
149
|
<span class="text-sm text-gray-500">
|
|
147
|
-
{{
|
|
150
|
+
{{
|
|
151
|
+
searchTerm ? t("mapo.fksField.noResults") : t("mapo.fksField.noItems")
|
|
152
|
+
}}
|
|
148
153
|
</span>
|
|
149
154
|
</template>
|
|
150
155
|
</USelectMenu>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, defineAsyncComponent } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import type { MapDescriptor } from "../../types/index.js";
|
|
4
5
|
|
|
5
6
|
// Leaflet is loaded only on the client because it is not SSR-safe.
|
|
@@ -7,6 +8,8 @@ const LeafletMap = defineAsyncComponent(
|
|
|
7
8
|
() => import("./MapoMapFieldClient.vue"),
|
|
8
9
|
);
|
|
9
10
|
|
|
11
|
+
const { t } = useI18n();
|
|
12
|
+
|
|
10
13
|
/** Geographic coordinates stored by the map field. */
|
|
11
14
|
interface LatLng {
|
|
12
15
|
lat: number;
|
|
@@ -63,7 +66,7 @@ function clear() {
|
|
|
63
66
|
<div class="flex gap-2">
|
|
64
67
|
<UInput
|
|
65
68
|
v-model="latInput"
|
|
66
|
-
placeholder="
|
|
69
|
+
:placeholder="t('mapo.latitude')"
|
|
67
70
|
class="flex-1"
|
|
68
71
|
:readonly="readonly"
|
|
69
72
|
:disabled="disabled"
|
|
@@ -71,7 +74,7 @@ function clear() {
|
|
|
71
74
|
/>
|
|
72
75
|
<UInput
|
|
73
76
|
v-model="lngInput"
|
|
74
|
-
placeholder="
|
|
77
|
+
:placeholder="t('mapo.longitude')"
|
|
75
78
|
class="flex-1"
|
|
76
79
|
:readonly="readonly"
|
|
77
80
|
:disabled="disabled"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
3
4
|
import type { MediaDescriptor, MediaItem } from "../../types/index.js";
|
|
4
5
|
import { useMediaManager } from "../../composables/useMediaManager.js";
|
|
5
6
|
|
|
@@ -15,6 +16,8 @@ const emit = defineEmits<{
|
|
|
15
16
|
"update:modelValue": [value: MediaItem | null];
|
|
16
17
|
}>();
|
|
17
18
|
|
|
19
|
+
const { t } = useI18n();
|
|
20
|
+
|
|
18
21
|
const dialogOpen = ref(false);
|
|
19
22
|
|
|
20
23
|
// Picker dialog comes from @mapomodule/uikit, resolved at runtime.
|
|
@@ -79,7 +82,7 @@ function clear() {
|
|
|
79
82
|
:block="!current"
|
|
80
83
|
@click="dialogOpen = true"
|
|
81
84
|
>
|
|
82
|
-
{{ current ? "
|
|
85
|
+
{{ current ? t("mapo.change") : t("mapo.mediaField.selectMedia") }}
|
|
83
86
|
</UButton>
|
|
84
87
|
|
|
85
88
|
<!-- Dialog -->
|