resolves #91
This commit is contained in:
@@ -113,6 +113,7 @@ export const SmartPopup = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleConflict = useCallback(() => {
|
const handleConflict = useCallback(() => {
|
||||||
|
console.log("handleConflict in smartMarker", id, options);
|
||||||
const newAnchor = calculateAnchor(id, "popup", options);
|
const newAnchor = calculateAnchor(id, "popup", options);
|
||||||
setAnchor(newAnchor);
|
setAnchor(newAnchor);
|
||||||
}, [id, options]);
|
}, [id, options]);
|
||||||
@@ -139,10 +140,10 @@ export const SmartPopup = (
|
|||||||
<Popup {...props} className={cn("relative", wrapperClassName)}>
|
<Popup {...props} className={cn("relative", wrapperClassName)}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"pointer-events-auto bg-base-100 relative",
|
"bg-base-100 pointer-events-auto relative",
|
||||||
anchor.includes("right") && "-translate-x-full",
|
anchor.includes("right") && "-translate-x-full",
|
||||||
anchor.includes("bottom") && "-translate-y-full",
|
anchor.includes("bottom") && "-translate-y-full",
|
||||||
!showContent && "opacity-0 pointer-events-none",
|
!showContent && "pointer-events-none opacity-0",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -150,7 +151,7 @@ export const SmartPopup = (
|
|||||||
data-id={id}
|
data-id={id}
|
||||||
id={`popup-domain-${id}`}
|
id={`popup-domain-${id}`}
|
||||||
className={cn(
|
className={cn(
|
||||||
"map-collision absolute w-[200%] h-[200%] top-0 left-0 transform pointer-events-none",
|
"map-collision pointer-events-none absolute left-0 top-0 h-[200%] w-[200%] transform",
|
||||||
anchor.includes("left") && "-translate-x-1/2",
|
anchor.includes("left") && "-translate-x-1/2",
|
||||||
anchor.includes("top") && "-translate-y-1/2",
|
anchor.includes("top") && "-translate-y-1/2",
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -272,7 +272,9 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleConflict = useCallback(() => {
|
const handleConflict = useCallback(() => {
|
||||||
const newAnchor = calculateAnchor(`aircraft-${aircraft.id}`, "marker");
|
const newAnchor = calculateAnchor(`aircraft-${aircraft.id}`, "marker", {
|
||||||
|
ignoreCluster: true,
|
||||||
|
});
|
||||||
setAnchor(newAnchor);
|
setAnchor(newAnchor);
|
||||||
}, [aircraft.id]);
|
}, [aircraft.id]);
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ export const ContextMenu = () => {
|
|||||||
setSearchPopup,
|
setSearchPopup,
|
||||||
toggleSearchElementSelection,
|
toggleSearchElementSelection,
|
||||||
} = useMapStore();
|
} = useMapStore();
|
||||||
const { missionFormValues, setMissionFormValues, setOpen, isOpen } = usePannelStore(
|
const {
|
||||||
(state) => state,
|
missionFormValues,
|
||||||
);
|
setMissionFormValues,
|
||||||
|
setOpen,
|
||||||
|
isOpen: isPannelOpen,
|
||||||
|
} = usePannelStore((state) => state);
|
||||||
const [showRulerOptions, setShowRulerOptions] = useState(false);
|
const [showRulerOptions, setShowRulerOptions] = useState(false);
|
||||||
const [rulerHover, setRulerHover] = useState(false);
|
const [rulerHover, setRulerHover] = useState(false);
|
||||||
const [rulerOptionsHover, setRulerOptionsHover] = useState(false);
|
const [rulerOptionsHover, setRulerOptionsHover] = useState(false);
|
||||||
@@ -53,7 +56,8 @@ export const ContextMenu = () => {
|
|||||||
|
|
||||||
if (!contextMenu || !dispatcherConnected) return null;
|
if (!contextMenu || !dispatcherConnected) return null;
|
||||||
|
|
||||||
const missionBtnText = missionFormValues && isOpen ? "Position übernehmen" : "Einsatz erstellen";
|
const missionBtnText =
|
||||||
|
missionFormValues && isPannelOpen ? "Position übernehmen" : "Einsatz erstellen";
|
||||||
|
|
||||||
const addOSMobjects = async (ignorePreviosSelected?: boolean) => {
|
const addOSMobjects = async (ignorePreviosSelected?: boolean) => {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -101,13 +105,13 @@ export const ContextMenu = () => {
|
|||||||
autoPan={false}
|
autoPan={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="absolute opacity-100 pointer-events-none p-3 flex items-center justify-center"
|
className="pointer-events-none absolute flex items-center justify-center p-3 opacity-100"
|
||||||
style={{ left: "-13px", top: "-13px" }}
|
style={{ left: "-13px", top: "-13px" }}
|
||||||
>
|
>
|
||||||
<div className="relative w-38 h-38 flex items-center justify-center -translate-x-1/2 -translate-y-1/2 pointer-events-none">
|
<div className="w-38 h-38 pointer-events-none relative flex -translate-x-1/2 -translate-y-1/2 items-center justify-center">
|
||||||
{/* Top Button */}
|
{/* Top Button */}
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute left-1/2 top-0 pointer-events-auto opacity-80 tooltip tooltip-top tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-top tooltip-accent pointer-events-auto absolute left-1/2 top-0 h-10 w-10 opacity-80"
|
||||||
data-tip={missionBtnText}
|
data-tip={missionBtnText}
|
||||||
style={{ transform: "translateX(-50%)" }}
|
style={{ transform: "translateX(-50%)" }}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -131,17 +135,18 @@ export const ContextMenu = () => {
|
|||||||
if (closestObject) {
|
if (closestObject) {
|
||||||
toggleSearchElementSelection(closestObject.wayID, true);
|
toggleSearchElementSelection(closestObject.wayID, true);
|
||||||
}
|
}
|
||||||
|
if (isPannelOpen) {
|
||||||
map.setView([contextMenu.lat, contextMenu.lng], 18, {
|
map.setView([contextMenu.lat, contextMenu.lng], 18, {
|
||||||
animate: true,
|
animate: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MapPinned size={20} />
|
<MapPinned size={20} />
|
||||||
</button>
|
</button>
|
||||||
{/* Left Button */}
|
{/* Left Button */}
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute top-1/2 left-0 pointer-events-auto opacity-80"
|
className="btn btn-circle bg-rescuetrack pointer-events-auto absolute left-0 top-1/2 h-10 w-10 opacity-80"
|
||||||
style={{ transform: "translateY(-50%)" }}
|
style={{ transform: "translateY(-50%)" }}
|
||||||
onMouseEnter={() => setRulerHover(true)}
|
onMouseEnter={() => setRulerHover(true)}
|
||||||
onMouseLeave={() => setRulerHover(false)}
|
onMouseLeave={() => setRulerHover(false)}
|
||||||
@@ -151,7 +156,7 @@ export const ContextMenu = () => {
|
|||||||
</button>
|
</button>
|
||||||
{/* Bottom Button */}
|
{/* Bottom Button */}
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute left-1/2 bottom-0 pointer-events-auto opacity-80 tooltip tooltip-bottom tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-bottom tooltip-accent pointer-events-auto absolute bottom-0 left-1/2 h-10 w-10 opacity-80"
|
||||||
data-tip="Koordinaten kopieren"
|
data-tip="Koordinaten kopieren"
|
||||||
style={{ transform: "translateX(-50%)" }}
|
style={{ transform: "translateX(-50%)" }}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -164,7 +169,7 @@ export const ContextMenu = () => {
|
|||||||
</button>
|
</button>
|
||||||
{/* Right Button (original Search button) */}
|
{/* Right Button (original Search button) */}
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute top-1/2 right-0 pointer-events-auto opacity-80 tooltip tooltip-right tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-right tooltip-accent pointer-events-auto absolute right-0 top-1/2 h-10 w-10 opacity-80"
|
||||||
data-tip="Gebäude suchen"
|
data-tip="Gebäude suchen"
|
||||||
style={{ transform: "translateY(-50%)" }}
|
style={{ transform: "translateY(-50%)" }}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -176,7 +181,7 @@ export const ContextMenu = () => {
|
|||||||
{/* Ruler Options - shown when Ruler button is hovered or options are hovered */}
|
{/* Ruler Options - shown when Ruler button is hovered or options are hovered */}
|
||||||
{showRulerOptions && (
|
{showRulerOptions && (
|
||||||
<div
|
<div
|
||||||
className="absolute flex flex-col items-center pointer-events-auto"
|
className="pointer-events-auto absolute flex flex-col items-center"
|
||||||
style={{
|
style={{
|
||||||
left: "-100px", // position to the right of the left button
|
left: "-100px", // position to the right of the left button
|
||||||
top: "50%",
|
top: "50%",
|
||||||
@@ -200,7 +205,7 @@ export const ContextMenu = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 mb-2 opacity-80 tooltip tooltip-left tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent mb-2 h-10 w-10 opacity-80"
|
||||||
data-tip="Strecke Messen"
|
data-tip="Strecke Messen"
|
||||||
style={{
|
style={{
|
||||||
transform: "translateX(100%)",
|
transform: "translateX(100%)",
|
||||||
@@ -212,7 +217,7 @@ export const ContextMenu = () => {
|
|||||||
<RulerDimensionLine size={20} />
|
<RulerDimensionLine size={20} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 mb-2 opacity-80 tooltip tooltip-left tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent mb-2 h-10 w-10 opacity-80"
|
||||||
data-tip="Radius Messen"
|
data-tip="Radius Messen"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
/* ... */
|
/* ... */
|
||||||
@@ -221,7 +226,7 @@ export const ContextMenu = () => {
|
|||||||
<Radius size={20} />
|
<Radius size={20} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle bg-rescuetrack w-10 h-10 opacity-80 tooltip tooltip-left tooltip-accent"
|
className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent h-10 w-10 opacity-80"
|
||||||
data-tip="Fläche Messen"
|
data-tip="Fläche Messen"
|
||||||
style={{
|
style={{
|
||||||
transform: "translateX(100%)",
|
transform: "translateX(100%)",
|
||||||
|
|||||||
@@ -209,7 +209,15 @@ const MissionPopupContent = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const MissionMarker = ({ mission }: { mission: Mission }) => {
|
const MissionMarker = ({
|
||||||
|
mission,
|
||||||
|
options,
|
||||||
|
}: {
|
||||||
|
mission: Mission;
|
||||||
|
options: {
|
||||||
|
hideDetailedKeyword?: boolean;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
const [hideMarker, setHideMarker] = useState(false);
|
const [hideMarker, setHideMarker] = useState(false);
|
||||||
const { editingMissionId, missionFormValues } = usePannelStore((state) => state);
|
const { editingMissionId, missionFormValues } = usePannelStore((state) => state);
|
||||||
@@ -261,7 +269,9 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleConflict = useCallback(() => {
|
const handleConflict = useCallback(() => {
|
||||||
const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker");
|
const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker", {
|
||||||
|
ignoreCluster: true,
|
||||||
|
});
|
||||||
setAnchor(newAnchor);
|
setAnchor(newAnchor);
|
||||||
}, [mission.id]);
|
}, [mission.id]);
|
||||||
|
|
||||||
@@ -318,7 +328,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
"
|
"
|
||||||
></div>
|
></div>
|
||||||
<span class="text-white text-[15px] text-nowrap">
|
<span class="text-white text-[15px] text-nowrap">
|
||||||
${mission.missionKeywordAbbreviation} ${mission.missionKeywordName}
|
${mission.missionKeywordAbbreviation} ${options.hideDetailedKeyword ? "" : mission.missionKeywordName}
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
data-anchor-lat="${mission.addressLat}"
|
data-anchor-lat="${mission.addressLat}"
|
||||||
@@ -396,6 +406,11 @@ export const MissionLayer = () => {
|
|||||||
selectedStation,
|
selectedStation,
|
||||||
} = usePilotConnectionStore((state) => state);
|
} = usePilotConnectionStore((state) => state);
|
||||||
|
|
||||||
|
const { data: aircrafts = [] } = useQuery({
|
||||||
|
queryKey: ["aircrafts"],
|
||||||
|
queryFn: () => getConnectedAircraftsAPI(),
|
||||||
|
refetchInterval: 10_000,
|
||||||
|
});
|
||||||
const { data: missions = [] } = useQuery({
|
const { data: missions = [] } = useQuery({
|
||||||
queryKey: ["missions"],
|
queryKey: ["missions"],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
@@ -426,7 +441,15 @@ export const MissionLayer = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filteredMissions.map((mission) => {
|
{filteredMissions.map((mission) => {
|
||||||
return <MissionMarker key={mission.id} mission={mission as Mission} />;
|
return (
|
||||||
|
<MissionMarker
|
||||||
|
key={mission.id}
|
||||||
|
mission={mission as Mission}
|
||||||
|
options={{
|
||||||
|
hideDetailedKeyword: missions.length + aircrafts.length > 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ const PopupContent = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="relative flex flex-col text-white min-w-[200px]">
|
<div className="relative flex min-w-fit flex-col text-white">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute w-[calc(100%+2px)] h-4 z-99 pointer-events-none",
|
"z-99 pointer-events-none absolute h-4 w-[calc(100%+2px)]",
|
||||||
anchor.includes("left") ? "-left-[2px]" : "-right-[2px]",
|
anchor.includes("left") ? "-left-[2px]" : "-right-[2px]",
|
||||||
anchor.includes("top") ? "-top-[2px]" : "-bottom-[2px]",
|
anchor.includes("top") ? "-top-[2px]" : "-bottom-[2px]",
|
||||||
)}
|
)}
|
||||||
@@ -68,7 +68,7 @@ const PopupContent = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={mission.id}
|
key={mission.id}
|
||||||
className={cn("relative inline-flex items-center gap-2 text-nowrap w-full")}
|
className={cn("relative inline-flex w-full items-center gap-2 text-nowrap")}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: markerColor,
|
backgroundColor: markerColor,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
@@ -99,7 +99,7 @@ const PopupContent = ({
|
|||||||
{aircrafts.map((aircraft) => (
|
{aircrafts.map((aircraft) => (
|
||||||
<div
|
<div
|
||||||
key={aircraft.id}
|
key={aircraft.id}
|
||||||
className="relative w-auto inline-flex items-center gap-2 text-nowrap cursor-pointer"
|
className="relative inline-flex w-auto cursor-pointer items-center gap-2 text-nowrap px-2"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus],
|
backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus],
|
||||||
}}
|
}}
|
||||||
@@ -119,14 +119,18 @@ const PopupContent = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="mx-2 my-0.5 text-gt font-bold"
|
className="text-gt my-0.5 font-bold"
|
||||||
style={{
|
style={{
|
||||||
color: FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus],
|
color: FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{aircraft.fmsStatus}
|
{aircraft.fmsStatus}
|
||||||
</span>
|
</span>
|
||||||
<span>{aircraft.Station.bosCallsign}</span>
|
<span>
|
||||||
|
{aircraft.Station.bosCallsign.length > 15
|
||||||
|
? aircraft.Station.locationStateShort
|
||||||
|
: aircraft.Station.bosCallsign}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -299,7 +303,7 @@ export const MarkerCluster = () => {
|
|||||||
position={[c.lat, c.lng]}
|
position={[c.lat, c.lng]}
|
||||||
autoPan={false}
|
autoPan={false}
|
||||||
autoClose={false}
|
autoClose={false}
|
||||||
className="w-[202px]"
|
className="min-w-fit"
|
||||||
>
|
>
|
||||||
<PopupContent aircrafts={c.aircrafts} missions={c.missions} />
|
<PopupContent aircrafts={c.aircrafts} missions={c.missions} />
|
||||||
</SmartPopup>
|
</SmartPopup>
|
||||||
|
|||||||
Reference in New Issue
Block a user