@mapomodule/form 2.0.0-beta.1 → 2.0.0-beta.10
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/README.md +2 -1
- package/dist/__tests__/__mocks__/nuxt-imports.d.ts +4 -0
- package/dist/__tests__/__mocks__/nuxt-imports.d.ts.map +1 -0
- package/dist/__tests__/camomillaRoundTrip.test.d.ts +2 -0
- package/dist/__tests__/camomillaRoundTrip.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/useFormFromSchema.test.d.ts +2 -0
- package/dist/__tests__/useFormFromSchema.test.d.ts.map +1 -0
- package/dist/__tests__/useMapoForm.test.d.ts +2 -0
- package/dist/__tests__/useMapoForm.test.d.ts.map +1 -0
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +7 -34
- package/dist/module.d.mts +20 -2
- package/dist/module.d.ts +12 -2
- package/dist/module.d.ts.map +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +187 -13
- package/dist/runtime/components/MapoFocusPortal.vue +115 -0
- package/dist/runtime/components/MapoFocusPortal.vue.d.ts +4 -0
- package/dist/runtime/components/MapoFocusPortal.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoForm.vue +168 -0
- package/dist/runtime/components/MapoForm.vue.d.ts +47 -0
- package/dist/runtime/components/MapoForm.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoFormField.vue +269 -0
- package/dist/runtime/components/MapoFormField.vue.d.ts +46 -0
- package/dist/runtime/components/MapoFormField.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoFormFlatSection.vue +149 -0
- package/dist/runtime/components/MapoFormFlatSection.vue.d.ts +15 -0
- package/dist/runtime/components/MapoFormFlatSection.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoFormGroup.vue +247 -0
- package/dist/runtime/components/MapoFormGroup.vue.d.ts +44 -0
- package/dist/runtime/components/MapoFormGroup.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoFormTabs.vue +110 -0
- package/dist/runtime/components/MapoFormTabs.vue.d.ts +15 -0
- package/dist/runtime/components/MapoFormTabs.vue.d.ts.map +1 -0
- package/dist/runtime/components/MapoUnknownField.vue +62 -0
- package/dist/runtime/components/MapoUnknownField.vue.d.ts +16 -0
- package/dist/runtime/components/MapoUnknownField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoDateField.vue +88 -0
- package/dist/runtime/components/fields/MapoDateField.vue.d.ts +17 -0
- package/dist/runtime/components/fields/MapoDateField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoDateTimeField.vue +159 -0
- package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts +17 -0
- package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoEnhancedMediaField.vue +130 -0
- package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoFileField.vue +99 -0
- package/dist/runtime/components/fields/MapoFileField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoFileField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoFksField.vue +122 -0
- package/dist/runtime/components/fields/MapoFksField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoFksField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoMapField.vue +99 -0
- package/dist/runtime/components/fields/MapoMapField.vue.d.ts +22 -0
- package/dist/runtime/components/fields/MapoMapField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoMapFieldClient.vue +67 -0
- package/dist/runtime/components/fields/MapoMapFieldClient.vue.d.ts +20 -0
- package/dist/runtime/components/fields/MapoMapFieldClient.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoMediaField.vue +91 -0
- package/dist/runtime/components/fields/MapoMediaField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoMediaField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoMediaM2mField.vue +110 -0
- package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoMediaUnavailable.vue +15 -0
- package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts +4 -0
- package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoRepeater.vue +459 -0
- package/dist/runtime/components/fields/MapoRepeater.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoRepeater.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoRepeaterItem.vue +346 -0
- package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts +55 -0
- package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoSeoPreview.vue +122 -0
- package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts +22 -0
- package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoTimeField.vue +44 -0
- package/dist/runtime/components/fields/MapoTimeField.vue.d.ts +17 -0
- package/dist/runtime/components/fields/MapoTimeField.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/MapoWygEditor.vue +252 -0
- package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts +16 -0
- package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiCheckbox.vue +9 -0
- package/dist/runtime/components/fields/nui/NuiCheckbox.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiCheckbox.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiInput.vue +9 -0
- package/dist/runtime/components/fields/nui/NuiInput.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiInput.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiSelectMenu.vue +18 -0
- package/dist/runtime/components/fields/nui/NuiSelectMenu.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiSelectMenu.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiSlider.vue +9 -0
- package/dist/runtime/components/fields/nui/NuiSlider.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiSlider.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiSwitch.vue +9 -0
- package/dist/runtime/components/fields/nui/NuiSwitch.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiSwitch.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/NuiTextarea.vue +9 -0
- package/dist/runtime/components/fields/nui/NuiTextarea.vue.d.ts +4 -0
- package/dist/runtime/components/fields/nui/NuiTextarea.vue.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/useNuiFieldAttrs.d.ts +13 -0
- package/dist/runtime/components/fields/nui/useNuiFieldAttrs.d.ts.map +1 -0
- package/dist/runtime/components/fields/nui/useNuiFieldAttrs.js +19 -0
- package/dist/runtime/composables/defineFormField.d.ts +25 -0
- package/dist/runtime/composables/defineFormField.d.ts.map +1 -0
- package/dist/runtime/composables/defineFormField.js +27 -0
- package/dist/runtime/composables/useCurrentLang.d.ts +6 -0
- package/dist/runtime/composables/useCurrentLang.d.ts.map +1 -0
- package/dist/runtime/composables/useCurrentLang.js +9 -0
- package/dist/runtime/composables/useFocusMode.d.ts +40 -0
- package/dist/runtime/composables/useFocusMode.d.ts.map +1 -0
- package/dist/runtime/composables/useFocusMode.js +31 -0
- package/dist/runtime/composables/useFormDraft.d.ts +60 -0
- package/dist/runtime/composables/useFormDraft.d.ts.map +1 -0
- package/dist/runtime/composables/useFormDraft.js +52 -0
- package/dist/runtime/composables/useFormFromSchema.d.ts +21 -0
- package/dist/runtime/composables/useFormFromSchema.d.ts.map +1 -0
- package/dist/runtime/composables/useFormFromSchema.js +145 -0
- package/dist/runtime/composables/useMapoForm.d.ts +96 -0
- package/dist/runtime/composables/useMapoForm.d.ts.map +1 -0
- package/dist/runtime/composables/useMapoForm.js +325 -0
- package/dist/runtime/composables/useMediaManager.d.ts +17 -0
- package/dist/runtime/composables/useMediaManager.d.ts.map +1 -0
- package/dist/runtime/composables/useMediaManager.js +6 -0
- package/dist/runtime/composables/useProgressiveDisclosure.d.ts +44 -0
- package/dist/runtime/composables/useProgressiveDisclosure.d.ts.map +1 -0
- package/dist/runtime/composables/useProgressiveDisclosure.js +23 -0
- package/dist/runtime/composables/useUnsavedChangesGuard.d.ts +24 -0
- package/dist/runtime/composables/useUnsavedChangesGuard.d.ts.map +1 -0
- package/dist/runtime/composables/useUnsavedChangesGuard.js +28 -0
- package/dist/runtime/devtools/DevtoolsFormsPage.vue +235 -0
- package/dist/runtime/devtools/DevtoolsFormsPage.vue.d.ts +4 -0
- package/dist/runtime/devtools/DevtoolsFormsPage.vue.d.ts.map +1 -0
- package/dist/runtime/plugins/form-registry.d.ts +18 -0
- package/dist/runtime/plugins/form-registry.d.ts.map +1 -0
- package/dist/runtime/plugins/form-registry.js +19 -0
- package/dist/runtime/registry/defaults.d.ts +3 -0
- package/dist/runtime/registry/defaults.d.ts.map +1 -0
- package/dist/runtime/registry/defaults.js +93 -0
- package/dist/runtime/types/descriptor.d.ts +373 -0
- package/dist/runtime/types/descriptor.d.ts.map +1 -0
- package/dist/runtime/types/descriptor.js +26 -0
- package/dist/runtime/types/index.d.ts +12 -0
- package/dist/runtime/types/index.d.ts.map +1 -0
- package/dist/runtime/types/index.js +6 -0
- package/dist/runtime/types/layout.d.ts +21 -0
- package/dist/runtime/types/layout.d.ts.map +1 -0
- package/dist/runtime/types/layout.js +0 -0
- package/dist/runtime/types/media.d.ts +43 -0
- package/dist/runtime/types/media.d.ts.map +1 -0
- package/dist/runtime/types/media.js +0 -0
- package/dist/runtime/types/registry.d.ts +62 -0
- package/dist/runtime/types/registry.d.ts.map +1 -0
- package/dist/runtime/types/registry.js +19 -0
- package/dist/runtime/utils/flattenFieldGroups.d.ts +50 -0
- package/dist/runtime/utils/flattenFieldGroups.d.ts.map +1 -0
- package/dist/runtime/utils/flattenFieldGroups.js +24 -0
- package/dist/types.d.mts +6 -10
- package/package.json +10 -8
- package/src/__tests__/article.schema.json +193 -0
- package/src/__tests__/camomillaRoundTrip.test.ts +73 -0
- package/src/__tests__/useFormFromSchema.test.ts +24 -0
- package/src/__tests__/useMapoForm.test.ts +5 -0
- package/src/index.ts +5 -0
- package/src/module.ts +34 -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 +153 -0
- 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 +104 -0
- package/src/runtime/components/fields/MapoMediaM2mField.vue +130 -0
- package/src/runtime/components/fields/MapoMediaUnavailable.vue +19 -0
- 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 +47 -5
- package/src/runtime/composables/useFormFromSchema.ts +4 -1
- package/src/runtime/composables/useMapoForm.ts +4 -2
- package/src/runtime/composables/useMediaManager.ts +21 -0
- package/src/runtime/composables/useUnsavedChangesGuard.ts +3 -3
- package/src/runtime/registry/defaults.ts +9 -1
- package/src/runtime/types/descriptor.ts +22 -7
- package/src/runtime/types/index.ts +4 -0
- package/src/runtime/types/media.ts +44 -0
- package/dist/index.d.cts +0 -1
- package/dist/module.d.cts +0 -2
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed, defineAsyncComponent } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
const LeafletMap = defineAsyncComponent(
|
|
5
|
+
() => import("./MapoMapFieldClient.vue")
|
|
6
|
+
);
|
|
7
|
+
const { t } = useI18n();
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
modelValue: { type: null, required: true },
|
|
10
|
+
descriptor: { type: Object, required: true },
|
|
11
|
+
errors: { type: Array, required: false },
|
|
12
|
+
readonly: { type: Boolean, required: false },
|
|
13
|
+
disabled: { type: Boolean, required: false }
|
|
14
|
+
});
|
|
15
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
16
|
+
const attrs = computed(() => props.descriptor.attrs ?? {});
|
|
17
|
+
const value = computed(() => {
|
|
18
|
+
const v = props.modelValue;
|
|
19
|
+
if (!v || typeof v !== "object") return null;
|
|
20
|
+
const { lat, lng } = v;
|
|
21
|
+
return typeof lat === "number" && typeof lng === "number" ? { lat, lng } : null;
|
|
22
|
+
});
|
|
23
|
+
const latInput = ref(value.value?.lat?.toString() ?? "");
|
|
24
|
+
const lngInput = ref(value.value?.lng?.toString() ?? "");
|
|
25
|
+
function onMapClick(coords) {
|
|
26
|
+
latInput.value = coords.lat.toFixed(6);
|
|
27
|
+
lngInput.value = coords.lng.toFixed(6);
|
|
28
|
+
emit("update:modelValue", coords);
|
|
29
|
+
}
|
|
30
|
+
function onManualInput() {
|
|
31
|
+
const lat = parseFloat(latInput.value);
|
|
32
|
+
const lng = parseFloat(lngInput.value);
|
|
33
|
+
if (!isNaN(lat) && !isNaN(lng)) emit("update:modelValue", { lat, lng });
|
|
34
|
+
}
|
|
35
|
+
function clear() {
|
|
36
|
+
latInput.value = "";
|
|
37
|
+
lngInput.value = "";
|
|
38
|
+
emit("update:modelValue", null);
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<div class="mapo-map-field space-y-2">
|
|
44
|
+
<!-- Manual coordinate inputs -->
|
|
45
|
+
<div class="flex gap-2">
|
|
46
|
+
<UInput
|
|
47
|
+
v-model="latInput"
|
|
48
|
+
:placeholder="t('mapo.latitude')"
|
|
49
|
+
class="flex-1"
|
|
50
|
+
:readonly="readonly"
|
|
51
|
+
:disabled="disabled"
|
|
52
|
+
@blur="onManualInput"
|
|
53
|
+
/>
|
|
54
|
+
<UInput
|
|
55
|
+
v-model="lngInput"
|
|
56
|
+
:placeholder="t('mapo.longitude')"
|
|
57
|
+
class="flex-1"
|
|
58
|
+
:readonly="readonly"
|
|
59
|
+
:disabled="disabled"
|
|
60
|
+
@blur="onManualInput"
|
|
61
|
+
/>
|
|
62
|
+
<UButton
|
|
63
|
+
v-if="value && !readonly && !disabled"
|
|
64
|
+
variant="ghost"
|
|
65
|
+
color="neutral"
|
|
66
|
+
icon="i-lucide-x"
|
|
67
|
+
@click="clear"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Leaflet map (client only) -->
|
|
72
|
+
<ClientOnly>
|
|
73
|
+
<LeafletMap
|
|
74
|
+
:value="value"
|
|
75
|
+
:default-lat="attrs.defaultLat ?? 41.9"
|
|
76
|
+
:default-lng="attrs.defaultLng ?? 12.5"
|
|
77
|
+
:zoom="attrs.zoom ?? 6"
|
|
78
|
+
:readonly="readonly ?? false"
|
|
79
|
+
@click="onMapClick"
|
|
80
|
+
/>
|
|
81
|
+
<template #fallback>
|
|
82
|
+
<div
|
|
83
|
+
class="flex h-48 items-center justify-center rounded border border-gray-200 bg-gray-50 text-sm text-gray-400"
|
|
84
|
+
>
|
|
85
|
+
Loading map...
|
|
86
|
+
</div>
|
|
87
|
+
</template>
|
|
88
|
+
</ClientOnly>
|
|
89
|
+
|
|
90
|
+
<p
|
|
91
|
+
v-for="err in errors"
|
|
92
|
+
:key="err"
|
|
93
|
+
class="text-sm text-red-500"
|
|
94
|
+
role="alert"
|
|
95
|
+
>
|
|
96
|
+
{{ err }}
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { MapDescriptor } from "../../types/index.js";
|
|
2
|
+
/** Geographic coordinates stored by the map field. */
|
|
3
|
+
interface LatLng {
|
|
4
|
+
lat: number;
|
|
5
|
+
lng: number;
|
|
6
|
+
}
|
|
7
|
+
/** Map field with manual lat/lng inputs plus a client-only Leaflet picker. */
|
|
8
|
+
type __VLS_Props = {
|
|
9
|
+
modelValue: unknown;
|
|
10
|
+
descriptor: MapDescriptor;
|
|
11
|
+
errors?: string[];
|
|
12
|
+
readonly?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
|
+
"update:modelValue": (value: LatLng | null) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
+
"onUpdate:modelValue"?: ((value: LatLng | null) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const _default: typeof __VLS_export;
|
|
21
|
+
export default _default;
|
|
22
|
+
//# sourceMappingURL=MapoMapField.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapoMapField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMapField.vue"],"names":[],"mappings":"AA6HA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAS1D,sDAAsD;AACtD,UAAU,MAAM;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8EAA8E;AAC9E,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;AAqOF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
value: { type: [Object, null], required: true },
|
|
5
|
+
defaultLat: { type: Number, required: true },
|
|
6
|
+
defaultLng: { type: Number, required: true },
|
|
7
|
+
zoom: { type: Number, required: true },
|
|
8
|
+
readonly: { type: Boolean, required: true }
|
|
9
|
+
});
|
|
10
|
+
const emit = defineEmits(["click"]);
|
|
11
|
+
const mapEl = ref(null);
|
|
12
|
+
let L = null;
|
|
13
|
+
let map = null;
|
|
14
|
+
let marker = null;
|
|
15
|
+
onMounted(async () => {
|
|
16
|
+
L = (await import("leaflet")).default;
|
|
17
|
+
await import("leaflet/dist/leaflet.css");
|
|
18
|
+
const [iconRetinaUrl, iconUrl, shadowUrl] = await Promise.all([
|
|
19
|
+
import("leaflet/dist/images/marker-icon-2x.png?url").then((m) => m.default),
|
|
20
|
+
import("leaflet/dist/images/marker-icon.png?url").then((m) => m.default),
|
|
21
|
+
import("leaflet/dist/images/marker-shadow.png?url").then((m) => m.default)
|
|
22
|
+
]);
|
|
23
|
+
delete L.Icon.Default.prototype._getIconUrl;
|
|
24
|
+
L.Icon.Default.mergeOptions({ iconRetinaUrl, iconUrl, shadowUrl });
|
|
25
|
+
if (!mapEl.value) return;
|
|
26
|
+
map = L.map(mapEl.value).setView(
|
|
27
|
+
[
|
|
28
|
+
props.value?.lat ?? props.defaultLat,
|
|
29
|
+
props.value?.lng ?? props.defaultLng
|
|
30
|
+
],
|
|
31
|
+
props.zoom
|
|
32
|
+
);
|
|
33
|
+
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
34
|
+
attribution: '\xA9 <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
35
|
+
}).addTo(map);
|
|
36
|
+
if (props.value) {
|
|
37
|
+
marker = L.marker([props.value.lat, props.value.lng]).addTo(map);
|
|
38
|
+
}
|
|
39
|
+
if (!props.readonly) {
|
|
40
|
+
map.on("click", (e) => {
|
|
41
|
+
const { lat, lng } = e.latlng;
|
|
42
|
+
if (marker) marker.setLatLng([lat, lng]);
|
|
43
|
+
else if (L && map) marker = L.marker([lat, lng]).addTo(map);
|
|
44
|
+
emit("click", { lat, lng });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
watch(
|
|
49
|
+
() => props.value,
|
|
50
|
+
(val) => {
|
|
51
|
+
if (!map || !L) return;
|
|
52
|
+
if (val) {
|
|
53
|
+
if (marker) marker.setLatLng([val.lat, val.lng]);
|
|
54
|
+
else marker = L.marker([val.lat, val.lng]).addTo(map);
|
|
55
|
+
map.panTo([val.lat, val.lng]);
|
|
56
|
+
} else {
|
|
57
|
+
marker?.remove();
|
|
58
|
+
marker = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
onBeforeUnmount(() => map?.remove());
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<div ref="mapEl" class="h-64 w-full rounded border border-gray-200" />
|
|
67
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Geographic coordinates emitted by the interactive Leaflet client. */
|
|
2
|
+
interface LatLng {
|
|
3
|
+
lat: number;
|
|
4
|
+
lng: number;
|
|
5
|
+
}
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
value: LatLng | null;
|
|
8
|
+
defaultLat: number;
|
|
9
|
+
defaultLng: number;
|
|
10
|
+
zoom: number;
|
|
11
|
+
readonly: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
click: (coords: LatLng) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onClick?: ((coords: LatLng) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
20
|
+
//# sourceMappingURL=MapoMapFieldClient.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapoMapFieldClient.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMapFieldClient.vue"],"names":[],"mappings":"AAiGA,wEAAwE;AACxE,UAAU,MAAM;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAuGF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
import { useMediaManager } from "../../composables/useMediaManager.js";
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
modelValue: { type: null, required: true },
|
|
7
|
+
descriptor: { type: Object, required: true },
|
|
8
|
+
errors: { type: Array, required: false },
|
|
9
|
+
readonly: { type: Boolean, required: false },
|
|
10
|
+
disabled: { type: Boolean, required: false }
|
|
11
|
+
});
|
|
12
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
13
|
+
const { t } = useI18n();
|
|
14
|
+
const dialogOpen = ref(false);
|
|
15
|
+
const { MediaDialog, available } = useMediaManager();
|
|
16
|
+
const current = computed(() => {
|
|
17
|
+
if (!props.modelValue) return null;
|
|
18
|
+
if (typeof props.modelValue === "object") return props.modelValue;
|
|
19
|
+
return null;
|
|
20
|
+
});
|
|
21
|
+
const mime = computed(() => props.descriptor.attrs?.mime);
|
|
22
|
+
function onConfirm(selection) {
|
|
23
|
+
emit(
|
|
24
|
+
"update:modelValue",
|
|
25
|
+
Array.isArray(selection) ? selection[0] ?? null : selection
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function clear() {
|
|
29
|
+
emit("update:modelValue", null);
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div class="mapo-media-field space-y-2">
|
|
35
|
+
<MapoMediaUnavailable v-if="!available" />
|
|
36
|
+
|
|
37
|
+
<template v-else>
|
|
38
|
+
<!-- Selected media preview -->
|
|
39
|
+
<div
|
|
40
|
+
v-if="current"
|
|
41
|
+
class="flex items-center gap-3 rounded-lg border border-default p-2"
|
|
42
|
+
>
|
|
43
|
+
<MapoMediaPreview :media="current" size="sm" />
|
|
44
|
+
<div class="flex-1 min-w-0">
|
|
45
|
+
<p class="truncate text-xs font-medium">
|
|
46
|
+
{{ current.title || current.file?.split("/").pop() }}
|
|
47
|
+
</p>
|
|
48
|
+
<p class="text-xs text-dimmed">
|
|
49
|
+
{{ current.mime_type }}
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
<UButton
|
|
53
|
+
v-if="!readonly && !disabled"
|
|
54
|
+
icon="i-lucide-x"
|
|
55
|
+
size="xs"
|
|
56
|
+
variant="ghost"
|
|
57
|
+
color="neutral"
|
|
58
|
+
@click="clear"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Trigger button -->
|
|
63
|
+
<UButton
|
|
64
|
+
v-if="!readonly && !disabled"
|
|
65
|
+
:icon="current ? 'i-lucide-refresh-cw' : 'i-lucide-image-plus'"
|
|
66
|
+
size="sm"
|
|
67
|
+
variant="soft"
|
|
68
|
+
color="neutral"
|
|
69
|
+
:block="!current"
|
|
70
|
+
@click="dialogOpen = true"
|
|
71
|
+
>
|
|
72
|
+
{{ current ? t("mapo.change") : t("mapo.mediaField.selectMedia") }}
|
|
73
|
+
</UButton>
|
|
74
|
+
|
|
75
|
+
<!-- Dialog -->
|
|
76
|
+
<component
|
|
77
|
+
:is="MediaDialog"
|
|
78
|
+
v-model="dialogOpen"
|
|
79
|
+
selection-mode="single"
|
|
80
|
+
:mime="mime"
|
|
81
|
+
:selected="current"
|
|
82
|
+
@confirm="onConfirm"
|
|
83
|
+
/>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<!-- Errors -->
|
|
87
|
+
<p v-for="err in errors" :key="err" class="text-xs text-error" role="alert">
|
|
88
|
+
{{ err }}
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MediaDescriptor, MediaItem } from "../../types/index.js";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
modelValue: MediaItem | number | null | undefined;
|
|
4
|
+
descriptor: MediaDescriptor;
|
|
5
|
+
errors?: string[];
|
|
6
|
+
readonly?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
"update:modelValue": (value: MediaItem | null) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
"onUpdate:modelValue"?: ((value: MediaItem | null) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
16
|
+
//# sourceMappingURL=MapoMediaField.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapoMediaField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMediaField.vue"],"names":[],"mappings":"AA6GA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGvE,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,UAAU,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgOF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
import { useMediaManager } from "../../composables/useMediaManager.js";
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
modelValue: { type: null, required: true },
|
|
7
|
+
descriptor: { type: Object, required: true },
|
|
8
|
+
errors: { type: Array, required: false },
|
|
9
|
+
readonly: { type: Boolean, required: false },
|
|
10
|
+
disabled: { type: Boolean, required: false }
|
|
11
|
+
});
|
|
12
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
13
|
+
const { t } = useI18n();
|
|
14
|
+
const dialogOpen = ref(false);
|
|
15
|
+
const { MediaDialog, available } = useMediaManager();
|
|
16
|
+
const current = computed(() => {
|
|
17
|
+
if (!props.modelValue) return [];
|
|
18
|
+
return props.modelValue.filter((v) => typeof v === "object");
|
|
19
|
+
});
|
|
20
|
+
const mime = computed(() => props.descriptor.attrs?.mime);
|
|
21
|
+
function onConfirm(selection) {
|
|
22
|
+
emit("update:modelValue", Array.isArray(selection) ? selection : [selection]);
|
|
23
|
+
}
|
|
24
|
+
function removeItem(id) {
|
|
25
|
+
emit(
|
|
26
|
+
"update:modelValue",
|
|
27
|
+
current.value.filter((m) => m.id !== id)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const dragIndex = ref(null);
|
|
31
|
+
function onDragStart(index) {
|
|
32
|
+
dragIndex.value = index;
|
|
33
|
+
}
|
|
34
|
+
function onDragOverItem(index) {
|
|
35
|
+
if (dragIndex.value === null || dragIndex.value === index) return;
|
|
36
|
+
const items = [...current.value];
|
|
37
|
+
const [moved] = items.splice(dragIndex.value, 1);
|
|
38
|
+
if (!moved) return;
|
|
39
|
+
items.splice(index, 0, moved);
|
|
40
|
+
dragIndex.value = index;
|
|
41
|
+
emit("update:modelValue", items);
|
|
42
|
+
}
|
|
43
|
+
function onDragEnd() {
|
|
44
|
+
dragIndex.value = null;
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<div class="mapo-media-m2m-field space-y-2">
|
|
50
|
+
<MapoMediaUnavailable v-if="!available" />
|
|
51
|
+
|
|
52
|
+
<template v-else>
|
|
53
|
+
<!-- Selected items grid -->
|
|
54
|
+
<div v-if="current.length > 0" class="flex flex-wrap gap-2">
|
|
55
|
+
<div
|
|
56
|
+
v-for="(media, i) in current"
|
|
57
|
+
:key="media.id"
|
|
58
|
+
class="group relative"
|
|
59
|
+
:class="{
|
|
60
|
+
'opacity-50': dragIndex === i,
|
|
61
|
+
'cursor-grab': !readonly && !disabled
|
|
62
|
+
}"
|
|
63
|
+
:draggable="!readonly && !disabled"
|
|
64
|
+
@dragstart="onDragStart(i)"
|
|
65
|
+
@dragover.prevent="onDragOverItem(i)"
|
|
66
|
+
@dragend="onDragEnd"
|
|
67
|
+
>
|
|
68
|
+
<MapoMediaPreview :media="media" size="sm" />
|
|
69
|
+
<button
|
|
70
|
+
v-if="!readonly && !disabled"
|
|
71
|
+
class="absolute -right-1.5 -top-1.5 hidden size-4 items-center justify-center rounded-full bg-error text-inverted group-hover:flex"
|
|
72
|
+
type="button"
|
|
73
|
+
@click="removeItem(media.id)"
|
|
74
|
+
>
|
|
75
|
+
<UIcon name="i-lucide-x" class="size-2.5" />
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<!-- Add button -->
|
|
81
|
+
<UButton
|
|
82
|
+
v-if="!readonly && !disabled"
|
|
83
|
+
icon="i-lucide-plus"
|
|
84
|
+
size="sm"
|
|
85
|
+
variant="soft"
|
|
86
|
+
color="neutral"
|
|
87
|
+
@click="dialogOpen = true"
|
|
88
|
+
>
|
|
89
|
+
{{
|
|
90
|
+
current.length > 0 ? t("mapo.mediaM2MField.addMedia") : t("mapo.mediaField.selectMedia")
|
|
91
|
+
}}
|
|
92
|
+
</UButton>
|
|
93
|
+
|
|
94
|
+
<!-- Dialog -->
|
|
95
|
+
<component
|
|
96
|
+
:is="MediaDialog"
|
|
97
|
+
v-model="dialogOpen"
|
|
98
|
+
selection-mode="multi"
|
|
99
|
+
:mime="mime"
|
|
100
|
+
:selected="current"
|
|
101
|
+
@confirm="onConfirm"
|
|
102
|
+
/>
|
|
103
|
+
</template>
|
|
104
|
+
|
|
105
|
+
<!-- Errors -->
|
|
106
|
+
<p v-for="err in errors" :key="err" class="text-xs text-error" role="alert">
|
|
107
|
+
{{ err }}
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MediaM2mDescriptor, MediaItem } from "../../types/index.js";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
modelValue: MediaItem[] | number[] | null | undefined;
|
|
4
|
+
descriptor: MediaM2mDescriptor;
|
|
5
|
+
errors?: string[];
|
|
6
|
+
readonly?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
"update:modelValue": (value: MediaItem[]) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
"onUpdate:modelValue"?: ((value: MediaItem[]) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
16
|
+
//# sourceMappingURL=MapoMediaM2mField.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapoMediaM2mField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMediaM2mField.vue"],"names":[],"mappings":"AAuIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG1E,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IACtD,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA+QF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div
|
|
7
|
+
class="flex items-center gap-2 rounded-lg border border-dashed border-default px-3 py-2 text-xs text-muted"
|
|
8
|
+
>
|
|
9
|
+
<UIcon
|
|
10
|
+
name="i-lucide-triangle-alert"
|
|
11
|
+
class="size-4 shrink-0 text-warning"
|
|
12
|
+
/>
|
|
13
|
+
<span>Media fields require <code>@mapomodule/uikit</code>.</span>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
4
|
+
//# sourceMappingURL=MapoMediaUnavailable.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapoMediaUnavailable.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMediaUnavailable.vue"],"names":[],"mappings":"AAuEA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|