improved OSM Element selection

This commit is contained in:
PxlLoewe
2025-06-03 15:50:20 -07:00
parent 56856664a7
commit 0f50bf1db8
10 changed files with 118 additions and 154 deletions

View File

@@ -1,18 +1,15 @@
import { useMapStore } from "_store/mapStore";
import { Marker as LMarker } from "leaflet";
import { Fragment, useEffect, useRef } from "react";
import { Marker, Polygon, Popup } from "react-leaflet";
import L from "leaflet";
import { useQuery } from "@tanstack/react-query";
import { getMissionsAPI } from "_querys/missions";
import { OSMWay } from "@repo/db";
import { usePannelStore } from "_store/pannelStore";
import { OSMWay } from "@repo/db";
import { useEffect } from "react";
export const SearchElements = () => {
const { searchElements, searchPopup, setSearchPopup, setContextMenu, openMissionMarker } =
useMapStore();
const { missionFormValues, setMissionFormValues } = usePannelStore((state) => state);
const missions = useQuery({
const { searchElements, openMissionMarker, setSearchElements } = useMapStore();
const { isOpen: pannelOpen, editingMissionId } = usePannelStore((state) => state);
const { data: missions } = useQuery({
queryKey: ["missions"],
queryFn: () =>
getMissionsAPI({
@@ -26,131 +23,59 @@ export const SearchElements = () => {
],
}),
});
const poppupRef = useRef<LMarker>(null);
const searchPopupElement = searchElements.find(
(element) => element.wayID === searchPopup?.elementId,
);
const SearchElement = ({
element,
isActive = false,
}: {
element: (typeof searchElements)[1];
isActive?: boolean;
}) => {
const ref = useRef<L.Polygon>(null);
useEffect(() => {
if (ref.current) {
ref.current.on("click", () => {
const center = ref.current?.getBounds().getCenter();
if (center && searchPopup?.elementId !== element.wayID) {
setSearchPopup({
lat: center.lat,
lng: center.lng,
elementId: element.wayID,
});
} else {
setSearchPopup(null);
}
setContextMenu(null);
});
useEffect(() => {
if (pannelOpen) {
const missionEdited = missions?.find((m) => m.id === editingMissionId);
console.log("Mission Edited", missionEdited, editingMissionId, missions);
if (missionEdited) {
const elements = missionEdited.addressOSMways.map((e) => ({
...(e as unknown as OSMWay),
isSelected: true,
}));
setSearchElements(elements);
} else {
setSearchElements([]);
}
}, [element.wayID]);
} else {
const openMissions = openMissionMarker.map((m) => {
const mission = missions?.find((mi) => mi.id === m.id);
return mission;
});
const elements = openMissions
.filter((m) => m?.addressOSMways)
.flatMap((m) =>
m?.addressOSMways.map((e) => ({
...(e as unknown as OSMWay),
isSelected: true,
})),
);
setSearchElements(elements.filter((e) => !!e));
}
}, [openMissionMarker, pannelOpen, missions]);
const SearchElement = ({ element }: { element: OSMWay }) => {
const { toggleSearchElementSelection } = useMapStore();
if (!element.nodes) return null;
return (
<Polygon
positions={element.nodes.map((node) => [node.lat, node.lon])}
color={searchPopup?.elementId === element.wayID || isActive ? "#ff4500" : "#46b7a3"}
color={element.isSelected ? "#ff4500" : "#46b7a3"}
eventHandlers={{
click: () => {
const addressOSMways = missionFormValues?.addressOSMways || [];
addressOSMways.push(JSON.parse(JSON.stringify(element)));
setMissionFormValues({
...missionFormValues,
addressOSMways,
});
if (!pannelOpen) return;
toggleSearchElementSelection(element.wayID);
},
}}
ref={ref}
/>
);
};
const SearchElementPopup = ({ element }: { element: (typeof searchElements)[1] }) => {
if (!searchPopup) return null;
return (
<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"]}
</p>
<p className="">
{element.tags?.["addr:suburb"]} {element.tags?.["addr:postcode"]}
</p>
<div className="flex flex-col gap-2 mt-2">
<button className="btn bg-rescuetrack-highlight">Zum Einsatz Hinzufügen</button>
</div>
</div>
</Popup>
);
};
return (
<>
{openMissionMarker.map(({ id }) => {
const mission = missions.data?.find((m) => m.id === id);
if (!mission) return null;
return (
<Fragment key={`mission-osm-${mission.id}`}>
{(mission.addressOSMways as (OSMWay | null)[])
.filter((element): element is OSMWay => element !== null)
.map((element: OSMWay, i) => (
<SearchElement
key={`mission-elem-${element.wayID}-${i}`}
element={element}
isActive
/>
))}
</Fragment>
);
})}
{searchElements.map((element, i) => {
if (
missions.data?.some(
(mission) =>
(mission.addressOSMways as (OSMWay | null)[])
.filter((e): e is OSMWay => e !== null)
.some((e) => e.wayID === element.wayID) &&
openMissionMarker.some((m) => m.id === mission.id),
)
)
return null;
return <SearchElement key={`mission-elem-${element.wayID}-${i}`} element={element} />;
})}
{searchPopup && (
<Marker
position={[searchPopup.lat, searchPopup.lng]}
ref={poppupRef}
icon={new L.DivIcon()}
opacity={0}
>
{!searchPopupElement && <div className="w-20 border border-rescuetrack"></div>}
</Marker>
)}
{searchPopupElement && <SearchElementPopup element={searchPopupElement} />}
</>
);
};