Make the locator model a camera, not just a location

That means we can track zoom
This commit is contained in:
Eli Ribble 2026-04-03 22:42:50 +00:00
parent 10e368c403
commit e08f614d11
No known key found for this signature in database
2 changed files with 52 additions and 15 deletions

View file

@ -23,19 +23,19 @@ import type { LngLatBoundsLike, Map as MapLibreMap } from "maplibre-gl";
import { onMounted, onUnmounted, ref, type Ref, shallowRef, watch } from "vue";
import { boundsMarkers, boundsDefault } from "@/map-utils";
import type { Location, Marker } from "@/types";
import type { Camera, MoveEndEventInternal } from "@/type/map";
// Emits interface
interface Emits {
(e: "update:modelValue", location: Location): void;
(e: "update:modelValue", value: Camera): void;
(e: "click", location: Location): void;
(e: "load"): void;
(e: "zoomend"): void;
(e: "markerDragEnd", location: Location): void;
}
// Props
interface Props {
modelValue: Location | null;
modelValue: Camera | null;
apiKey?: string;
markers?: Marker[];
}
@ -61,13 +61,13 @@ const initializeMap = () => {
if (props.markers.length > 0) {
bounds = boundsMarkers(props.markers);
}
map.value = new maplibregl.Map({
const _map = new maplibregl.Map({
bounds: bounds,
container: mapContainer.value,
style: "https://tiles.stadiamaps.com/styles/alidade_smooth.json",
});
map.value.on("click", (e: maplibregl.MapLayerMouseEvent) => {
map.value = _map;
_map.on("click", (e: maplibregl.MapLayerMouseEvent) => {
e.preventDefault();
console.log("internal click", e);
emit("click", {
@ -76,13 +76,32 @@ const initializeMap = () => {
});
});
map.value.on("load", () => {
_map.on("load", () => {
console.log("map loaded");
emit("load");
});
map.value.on("zoomend", () => {
emit("zoomend");
_map.on("zoomend", (evt: MoveEndEventInternal) => {
console.log("zoomend", evt);
if (_map && !evt.isInternalUpdate) {
const center = _map.getCenter();
const newCamera: Camera = {
location: center,
zoom: _map.getZoom(),
};
emit("update:modelValue", newCamera);
}
});
_map.on("moveend", (evt: MoveEndEventInternal) => {
console.log("moveend", evt);
if (_map && !evt.isInternalUpdate) {
const center = _map.getCenter();
emit("update:modelValue", {
location: center,
zoom: _map.getZoom(),
});
}
});
};
@ -128,14 +147,22 @@ const frameMarkers = () => {
if (props.markers.length === 1) {
// Single marker: pan to it
map.value.panTo(props.markers[0].location, { duration: 1000 });
map.value.panTo(
props.markers[0].location,
{ duration: 1000 },
{ isInternalUpdate: true },
);
} else {
// Multiple markers: fit bounds
const bounds = new maplibregl.LngLatBounds();
props.markers.forEach((marker) => {
bounds.extend([marker.location.lng, marker.location.lat]);
});
map.value.fitBounds(bounds, { padding: 10, duration: 1000 });
map.value.fitBounds(
bounds,
{ padding: 10, duration: 1000 },
{ isInternalUpdate: true },
);
}
};
@ -144,7 +171,11 @@ watch(
() => props.modelValue,
(newLocation) => {
if (map.value && newLocation) {
map.value.panTo(newLocation, { duration: 1000 });
map.value.panTo(
newLocation.location,
{ duration: 1000 },
{ isInternalUpdate: true },
);
}
},
{ deep: true },

View file

@ -155,7 +155,7 @@ select.tall {
</p>
<div class="map-container">
<MapLocator
v-model="currentLocation"
v-model="currentCamera"
:markers="markers"
:initial-zoom="15"
@click="doMapClick"
@ -163,7 +163,12 @@ select.tall {
@marker-drag-end="doMapMarkerDragEnd"
/>
</div>
<input type="hidden" id="map-zoom" name="map-zoom" />
<input
type="hidden"
id="map-zoom"
name="map-zoom"
:value="currentCamera?.zoom ?? 0"
/>
<input type="hidden" id="address-country" name="address-country" />
<input type="hidden" id="address-locality" name="address-locality" />
<input type="hidden" id="address-number" name="address-number" />
@ -527,9 +532,10 @@ import AddressSuggestion from "@/components/AddressSuggestion.vue";
import ImageUpload, { Image } from "@/components/ImageUpload.vue";
import MapLocator from "@/components/MapLocator.vue";
import type { Location, Marker } from "@/types";
import type { Camera } from "@/type/map";
import type { Address } from "@/type/stadia";
const currentLocation = ref<Location | null>(null);
const currentCamera = ref<Camera | null>(null);
const errorMessage = ref("");
const formElement = ref<HTMLFormElement | null>(null);
const images = ref<Image[]>([]);