Fix waypoint icons and add elevation chart scrubbing

Waypoint icons:
- Replace broken Leaflet default icons with numbered blue circles
- Numbers match sidebar order (1, 2, 3...)

Elevation chart:
- Mouse hover shows crosshair, elevation, and distance at cursor
- Red dot appears on the map at the corresponding route position
- Dot disappears when mouse leaves the chart

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-03-23 07:30:00 +01:00
parent ce0cd84562
commit 6ee07f0570
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
3 changed files with 198 additions and 73 deletions

View file

@ -1,17 +1,25 @@
import { useEffect, useState, useCallback } from "react";
import { MapContainer, TileLayer, LayersControl, Marker, Polyline, useMapEvents, useMap } from "react-leaflet";
import { MapContainer, TileLayer, LayersControl, Marker, Polyline, CircleMarker, useMapEvents, useMap } from "react-leaflet";
import L from "leaflet";
import * as Y from "yjs";
import type { YjsState } from "~/lib/use-yjs";
import { baseLayers } from "@trails-cool/map";
import "leaflet/dist/leaflet.css";
// Fix Leaflet default icon paths in bundled environment
import iconUrl from "leaflet/dist/images/marker-icon.png";
import iconRetinaUrl from "leaflet/dist/images/marker-icon-2x.png";
import shadowUrl from "leaflet/dist/images/marker-shadow.png";
L.Icon.Default.mergeOptions({ iconUrl, iconRetinaUrl, shadowUrl });
function waypointIcon(index: number): L.DivIcon {
return L.divIcon({
className: "",
html: `<div style="
width:24px;height:24px;border-radius:50%;
background:#2563eb;color:white;
display:flex;align-items:center;justify-content:center;
font-size:12px;font-weight:600;
border:2px solid white;box-shadow:0 1px 4px rgba(0,0,0,0.3);
transform:translate(-12px,-12px);
">${index + 1}</div>`,
iconSize: [0, 0],
});
}
interface WaypointData {
lat: number;
@ -30,6 +38,7 @@ function getWaypointsFromYjs(waypoints: Y.Array<Y.Map<unknown>>): WaypointData[]
interface PlannerMapProps {
yjs: YjsState;
onRouteRequest?: (waypoints: WaypointData[]) => void;
highlightPosition?: [number, number] | null;
}
function MapClickHandler({ onAdd }: { onAdd: (lat: number, lng: number) => void }) {
@ -94,7 +103,7 @@ function CursorTracker({ awareness }: { awareness: YjsState["awareness"] }) {
key={clientId}
position={[cursor.lat, cursor.lng]}
icon={L.divIcon({
className: "cursor-marker",
className: "",
html: `<div style="background:${cursor.color};color:white;padding:2px 6px;border-radius:4px;font-size:11px;white-space:nowrap;transform:translate(10px,-50%)">${cursor.name}</div>`,
iconSize: [0, 0],
})}
@ -104,7 +113,7 @@ function CursorTracker({ awareness }: { awareness: YjsState["awareness"] }) {
);
}
export function PlannerMap({ yjs, onRouteRequest }: PlannerMapProps) {
export function PlannerMap({ yjs, onRouteRequest, highlightPosition }: PlannerMapProps) {
const [waypoints, setWaypoints] = useState<WaypointData[]>([]);
const [routeGeoJson, setRouteGeoJson] = useState<L.LatLngExpression[] | null>(null);
@ -201,6 +210,7 @@ export function PlannerMap({ yjs, onRouteRequest }: PlannerMapProps) {
key={i}
position={[wp.lat, wp.lon]}
draggable
icon={waypointIcon(i)}
eventHandlers={{
dragend: (e) => {
const { lat, lng } = e.target.getLatLng();
@ -215,6 +225,14 @@ export function PlannerMap({ yjs, onRouteRequest }: PlannerMapProps) {
))}
{routeGeoJson && <Polyline positions={routeGeoJson} color="#2563eb" weight={4} opacity={0.8} />}
{highlightPosition && (
<CircleMarker
center={highlightPosition}
radius={6}
pathOptions={{ color: "#ef4444", fillColor: "#ef4444", fillOpacity: 1, weight: 2 }}
/>
)}
</MapContainer>
);
}