improved Popup handleing
This commit is contained in:
@@ -5,26 +5,39 @@ import { Popup, useMap } from "react-leaflet";
|
||||
|
||||
export const ContextMenu = () => {
|
||||
const map = useMap();
|
||||
const { popup, setSearchElements, setPopup, setSearchPopup } = useMapStore();
|
||||
const {
|
||||
contextMenu,
|
||||
setContextMenu,
|
||||
setSearchElements,
|
||||
setSearchPopup,
|
||||
setOpenMissionMarker,
|
||||
openMissionMarker,
|
||||
} = useMapStore();
|
||||
|
||||
useEffect(() => {
|
||||
map.on("contextmenu", (e) => {
|
||||
setPopup({ isOpen: true, lat: e.latlng.lat, lng: e.latlng.lng });
|
||||
setSearchPopup(undefined);
|
||||
// setOpenMissionMarker({ open: [], close: openMissionMarker });
|
||||
setContextMenu({ lat: e.latlng.lat, lng: e.latlng.lng });
|
||||
// setSearchPopup(null);
|
||||
});
|
||||
}, [popup]);
|
||||
}, [contextMenu]);
|
||||
|
||||
if (!popup) return null;
|
||||
if (!contextMenu) return null;
|
||||
|
||||
return (
|
||||
<Popup position={[popup.lat, popup.lng]}>
|
||||
<Popup
|
||||
position={[contextMenu.lat, contextMenu.lng]}
|
||||
autoClose={false}
|
||||
closeOnClick={false}
|
||||
autoPan={false}
|
||||
>
|
||||
{/* // TODO: maske: */}
|
||||
<div className="absolute transform -translate-y-1/2 z-1000 opacity-100 pointer-events-auto p-3">
|
||||
<button
|
||||
className="btn btn-sm rounded-full bg-amber-600 hover:bg-amber-700 aspect-square"
|
||||
onClick={async () => {
|
||||
const address = await fetch(
|
||||
`https://nominatim.openstreetmap.org/reverse?lat=${popup.lat}&lon=${popup.lng}&format=json`,
|
||||
`https://nominatim.openstreetmap.org/reverse?lat=${contextMenu.lat}&lon=${contextMenu.lng}&format=json`,
|
||||
);
|
||||
const data = (await address.json()) as {
|
||||
address: {
|
||||
@@ -62,8 +75,8 @@ export const ContextMenu = () => {
|
||||
`https://overpass-api.de/api/interpreter?data=${encodeURIComponent(`
|
||||
[out:json];
|
||||
(
|
||||
way["building"](around:100, ${popup.lat}, ${popup.lng});
|
||||
relation["building"](around:100, ${popup.lat}, ${popup.lng});
|
||||
way["building"](around:100, ${contextMenu.lat}, ${contextMenu.lng});
|
||||
relation["building"](around:100, ${contextMenu.lat}, ${contextMenu.lng});
|
||||
);
|
||||
out body;
|
||||
>;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MissionOptionalDefaults } from "@repo/db/zod";
|
||||
import { useMapStore } from "_store/mapStore";
|
||||
import { useMissionsStore } from "_store/missionsStore";
|
||||
import { Icon, Marker as LMarker } from "leaflet";
|
||||
import { House, Route } from "lucide-react";
|
||||
@@ -10,6 +11,8 @@ export const MissionMarker = ({
|
||||
}: {
|
||||
mission: MissionOptionalDefaults;
|
||||
}) => {
|
||||
const { openMissionMarker, setOpenMissionMarker, setSearchPopup } =
|
||||
useMapStore();
|
||||
const [zoom, setZoom] = useState(0);
|
||||
const map = useMap();
|
||||
const markerRef = useRef<LMarker<any>>(null);
|
||||
@@ -26,8 +29,24 @@ export const MissionMarker = ({
|
||||
};
|
||||
}, [map]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = () => {
|
||||
if (mission.id) {
|
||||
setOpenMissionMarker({
|
||||
open: [mission.id],
|
||||
close: [],
|
||||
});
|
||||
// setSearchPopup(null);
|
||||
}
|
||||
};
|
||||
markerRef.current?.on("click", handleClick);
|
||||
return () => {
|
||||
markerRef.current?.off("click", handleClick);
|
||||
};
|
||||
}, [markerRef.current, mission.id, setOpenMissionMarker]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<Marker
|
||||
ref={markerRef}
|
||||
position={[mission.addressLat, mission.addressLng]}
|
||||
@@ -38,8 +57,14 @@ export const MissionMarker = ({
|
||||
popupAnchor: [0, 0],
|
||||
})
|
||||
}
|
||||
>
|
||||
<Popup>
|
||||
></Marker>
|
||||
{mission.id && openMissionMarker.includes(mission.id) && (
|
||||
<Popup
|
||||
position={[mission.addressLat, mission.addressLng]}
|
||||
autoClose={false}
|
||||
closeOnClick={false}
|
||||
autoPan={false}
|
||||
>
|
||||
<div className="absolute z-1000 opacity-100 pointer-events-auto w-[500px] text-white">
|
||||
<div className="bg-amber-600 pt-2 flex gap-1">
|
||||
<div className="p-2 bg-amber-700">
|
||||
@@ -54,8 +79,8 @@ export const MissionMarker = ({
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</Marker>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Marker, Polygon, Polyline, Popup } from "react-leaflet";
|
||||
import L from "leaflet";
|
||||
|
||||
export const SearchElements = () => {
|
||||
const { searchElements, searchPopup, setSearchPopup, setPopup } =
|
||||
const { searchElements, searchPopup, setSearchPopup, setContextMenu } =
|
||||
useMapStore();
|
||||
const poppupRef = useRef<LMarker>(null);
|
||||
const intervalRef = useRef<NodeJS.Timeout>(null);
|
||||
@@ -13,27 +13,6 @@ export const SearchElements = () => {
|
||||
(element) => element.id === searchPopup?.elementId,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
intervalRef.current = setInterval(() => {
|
||||
if (searchPopup?.isOpen) {
|
||||
poppupRef.current?.openPopup();
|
||||
} else {
|
||||
poppupRef.current?.closePopup();
|
||||
}
|
||||
}, 100);
|
||||
poppupRef.current?.on("popupclose", () => {
|
||||
setSearchPopup(undefined);
|
||||
});
|
||||
return () => {
|
||||
if (poppupRef.current) {
|
||||
poppupRef.current.off("popupclose");
|
||||
}
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
};
|
||||
}, [searchPopup, searchPopupElement]);
|
||||
|
||||
const SearchElement = ({
|
||||
element,
|
||||
}: {
|
||||
@@ -45,13 +24,16 @@ export const SearchElements = () => {
|
||||
if (ref.current) {
|
||||
ref.current.on("click", () => {
|
||||
const center = ref.current.getBounds().getCenter();
|
||||
setSearchPopup({
|
||||
isOpen: true,
|
||||
lat: center.lat,
|
||||
lng: center.lng,
|
||||
elementId: element.id,
|
||||
});
|
||||
setPopup(null);
|
||||
if (searchPopup?.elementId !== element.id) {
|
||||
setSearchPopup({
|
||||
lat: center.lat,
|
||||
lng: center.lng,
|
||||
elementId: element.id,
|
||||
});
|
||||
} else {
|
||||
setSearchPopup(null);
|
||||
}
|
||||
setContextMenu(null);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
@@ -71,13 +53,20 @@ export const SearchElements = () => {
|
||||
}: {
|
||||
element: (typeof searchElements)[1];
|
||||
}) => {
|
||||
if (!searchPopup) return null;
|
||||
return (
|
||||
<Popup>
|
||||
<Popup
|
||||
autoPan={false}
|
||||
position={[searchPopup.lat, searchPopup.lng]}
|
||||
autoClose={false}
|
||||
closeOnClick={false}
|
||||
>
|
||||
<div className="bg-base-100/70 border border-rescuetrack w-[250px] text-white pointer-events-auto p-2">
|
||||
<h3 className="text-lg font-bold">
|
||||
{element.tags?.building === "yes"
|
||||
? "Gebäude"
|
||||
: element.tags?.building}
|
||||
{!element.tags?.building && "unbekannt"}
|
||||
</h3>
|
||||
<p className="">
|
||||
{element.tags?.["addr:street"]} {element.tags?.["addr:housenumber"]}
|
||||
@@ -108,14 +97,14 @@ export const SearchElements = () => {
|
||||
icon={new L.DivIcon()}
|
||||
opacity={0}
|
||||
>
|
||||
{searchPopupElement && (
|
||||
<SearchElementPopup element={searchPopupElement} />
|
||||
)}
|
||||
{!searchPopupElement && (
|
||||
<div className="w-20 border border-rescuetrack"></div>
|
||||
)}
|
||||
</Marker>
|
||||
)}
|
||||
{searchPopupElement && (
|
||||
<SearchElementPopup element={searchPopupElement} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ export const Chat = () => {
|
||||
|
||||
timeout.current = setInterval(() => {
|
||||
fetchDispatcher();
|
||||
}, 1000);
|
||||
}, 1000000);
|
||||
fetchDispatcher();
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -2,8 +2,7 @@ import { popup } from "leaflet";
|
||||
import { create } from "zustand";
|
||||
|
||||
interface MapStore {
|
||||
popup: {
|
||||
isOpen: boolean;
|
||||
contextMenu: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
} | null;
|
||||
@@ -11,6 +10,8 @@ interface MapStore {
|
||||
center: L.LatLngExpression;
|
||||
zoom: number;
|
||||
};
|
||||
openMissionMarker: string[];
|
||||
setOpenMissionMarker: (mission: { open: string[]; close: string[] }) => void;
|
||||
searchElements: {
|
||||
id: number;
|
||||
nodes: {
|
||||
@@ -29,30 +30,38 @@ interface MapStore {
|
||||
type: string;
|
||||
}[];
|
||||
setSearchElements: (elements: MapStore["searchElements"]) => void;
|
||||
setPopup: (popup: MapStore["popup"]) => void;
|
||||
searchPopup?: {
|
||||
isOpen: boolean;
|
||||
setContextMenu: (popup: MapStore["contextMenu"]) => void;
|
||||
searchPopup: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
elementId: number;
|
||||
};
|
||||
} | null;
|
||||
setSearchPopup: (popup: MapStore["searchPopup"]) => void;
|
||||
}
|
||||
|
||||
export const useMapStore = create<MapStore>((set, get) => ({
|
||||
openMissionMarker: [],
|
||||
setOpenMissionMarker: ({ open, close }) => {
|
||||
set((state) => ({
|
||||
openMissionMarker: [...state.openMissionMarker, ...open].filter(
|
||||
(id) => !close.includes(id),
|
||||
),
|
||||
}));
|
||||
},
|
||||
map: {
|
||||
center: [51.5, 10.5],
|
||||
zoom: 6,
|
||||
},
|
||||
searchPopup: null,
|
||||
searchElements: [],
|
||||
setSearchPopup: (popup) =>
|
||||
set((state) => ({
|
||||
searchPopup: popup,
|
||||
})),
|
||||
popup: null,
|
||||
setPopup: (popup) =>
|
||||
contextMenu: null,
|
||||
setContextMenu: (contextMenu) =>
|
||||
set((state) => ({
|
||||
popup: popup,
|
||||
contextMenu,
|
||||
})),
|
||||
setSearchElements: (elements) =>
|
||||
set((state) => ({
|
||||
|
||||
Reference in New Issue
Block a user