106 lines
2.7 KiB
Vue
106 lines
2.7 KiB
Vue
<template>
|
|
<div class="map-location-display">
|
|
<div
|
|
ref="mapContainer"
|
|
class="map-container rounded shadow-sm"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
icon,
|
|
map as leafMap, marker as leafMarker, Marker, tileLayer
|
|
} from "leaflet";
|
|
import markerIcon2x from "leaflet/dist/images/marker-icon-2x.png";
|
|
import markerIcon from "leaflet/dist/images/marker-icon.png";
|
|
import markerShadow from "leaflet/dist/images/marker-shadow.png";
|
|
import "leaflet/dist/leaflet.css";
|
|
import { onBeforeMount, onMounted, ref, shallowRef, useAttrs } from "vue";
|
|
|
|
defineOptions({ name: "MapLocationDisplay" });
|
|
|
|
const attrs = useAttrs();
|
|
const mapContainer = ref<HTMLElement | null>(null);
|
|
const map = shallowRef<L.Map | null>(null);
|
|
|
|
const lat = ref<number | null>(null);
|
|
const lng = ref<number | null>(null);
|
|
const storeName = ref<string>("");
|
|
const storeAddress = ref<string>("");
|
|
const phone = ref<string>("");
|
|
|
|
|
|
const defaultIcon = icon({
|
|
iconUrl: markerIcon,
|
|
iconRetinaUrl: markerIcon2x,
|
|
shadowUrl: markerShadow,
|
|
iconSize: [25, 41],
|
|
iconAnchor: [12, 41],
|
|
popupAnchor: [1, -34],
|
|
shadowSize: [41, 41]
|
|
});
|
|
Marker.prototype.options.icon = defaultIcon;
|
|
|
|
onBeforeMount(() => {
|
|
if (!attrs.json) {
|
|
throw new Error("this component requires json");
|
|
}
|
|
});
|
|
|
|
onMounted(() => {
|
|
const json = String(attrs.json ?? "{}");
|
|
const data = JSON.parse(json);
|
|
|
|
lat.value = data.lat ? Number.parseFloat(data.lat) : null;
|
|
lng.value = data.lng ? Number.parseFloat(data.lng) : null;
|
|
storeName.value = data.name || "";
|
|
storeAddress.value = data.address || "";
|
|
phone.value = data.phone || "";
|
|
|
|
if (lat.value && lng.value) {
|
|
initMap();
|
|
}
|
|
});
|
|
|
|
const initMap = () => {
|
|
if (!mapContainer.value || !lat.value || !lng.value) return;
|
|
|
|
const position: [number, number] = [lat.value, lng.value];
|
|
|
|
map.value = leafMap(
|
|
mapContainer.value,
|
|
{
|
|
// * Prevent scroll issues
|
|
scrollWheelZoom: false
|
|
}).setView(position, 15);
|
|
|
|
tileLayer(
|
|
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
{attribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>",}
|
|
).addTo(map.value);
|
|
|
|
const marker = leafMarker(position).addTo(map.value);
|
|
|
|
if (storeName.value || storeAddress.value) {
|
|
const popupContent = `
|
|
<div class="text-center">
|
|
<strong class="leaflet-popup-title">${storeName.value}</strong>
|
|
<span>${storeAddress.value}</span><br>
|
|
<a href="tel:${phone.value}">${phone.value}</a>
|
|
</div>
|
|
`;
|
|
marker.bindPopup(popupContent).openPopup();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.map-location-display {
|
|
.map-container {
|
|
height: 350px;
|
|
width: 100%;
|
|
z-index: 1;
|
|
}
|
|
}
|
|
</style>
|