feat(mobile): Handy-Optimierung – Tap-to-place, Bottom-Nav, kompakte Banner (v1.4.12)

- Tap-to-place: Symbol antippen → auf Karte tippen platziert es (Alternative zu Drag&Drop, mobil-tauglich); schwebender Hinweis mit Abbrechen
- Mobile Bottom-Navigation: Karte / Journal(Auswertung) / Symbole ohne Menü erreichbar
- FAB entfernt; Karten-Steuerung (Zoom/Standort/Massstab) hebt sich mobil über die Bottom-Nav
- Touch-Ziele in der Werkzeugleiste auf 44px vergrössert
- "Einsatz/Übung beenden" jetzt auch im Mobile-Overflow-Menü
- Banner (Übung/Offline/E-Mail/Live) mobil kompakt: kleinere Höhe, gekürzte Texte, truncate
- Einsatz-beenden-Bestätigung als In-App-Dialog statt Browser-confirm()

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-19 15:20:27 +02:00
parent 947a757d9b
commit 6e3663cf7f
7 changed files with 161 additions and 55 deletions

View File

@@ -85,6 +85,9 @@ interface MapViewProps {
mapRef?: React.MutableRefObject<maplibregl.Map | null>
onDrawModeChange?: (mode: DrawMode) => void
undoDrawPointRef?: React.MutableRefObject<(() => boolean) | null>
/** Tap-to-place: wenn gesetzt, platziert der nächste Karten-Tipp dieses Symbol (Mobil-freundlich). */
pendingSymbol?: { iconId: string; imageUrl?: string } | null
onSymbolPlaced?: () => void
}
export function MapView({
@@ -100,6 +103,8 @@ export function MapView({
mapRef: externalMapRef,
onDrawModeChange,
undoDrawPointRef,
pendingSymbol,
onSymbolPlaced,
}: MapViewProps) {
const mapContainer = useRef<HTMLDivElement | null>(null)
const map = useRef<maplibregl.Map | null>(null)
@@ -165,6 +170,8 @@ export function MapView({
const onSymbolDropRef = useRef(onSymbolDrop)
const onTextPlaceRef = useRef(onTextPlace)
const onDrawModeChangeRef = useRef(onDrawModeChange)
const pendingSymbolRef = useRef(pendingSymbol)
const onSymbolPlacedRef = useRef(onSymbolPlaced)
// Lock/unlock map panning based on draw mode
// Apple-style: touchZoomRotate (two-finger pinch/zoom) ALWAYS enabled
@@ -191,6 +198,8 @@ export function MapView({
useEffect(() => { onSymbolDropRef.current = onSymbolDrop }, [onSymbolDrop])
useEffect(() => { onTextPlaceRef.current = onTextPlace }, [onTextPlace])
useEffect(() => { onDrawModeChangeRef.current = onDrawModeChange }, [onDrawModeChange])
useEffect(() => { pendingSymbolRef.current = pendingSymbol }, [pendingSymbol])
useEffect(() => { onSymbolPlacedRef.current = onSymbolPlaced }, [onSymbolPlaced])
// Expose undo-draw-point function to parent via ref
useEffect(() => {
@@ -891,6 +900,15 @@ export function MapView({
const width = selectedWidthRef.current
const coords: [number, number] = [e.lngLat.lng, e.lngLat.lat]
// Tap-to-place: ein zuvor angetipptes Symbol wird hier platziert (Mobil-freundlich,
// Alternative zum Drag&Drop). Hat Vorrang vor allen Zeichen-Modi.
if (pendingSymbolRef.current) {
const p = pendingSymbolRef.current
onSymbolDropRef.current(p.iconId, coords, p.imageUrl)
onSymbolPlacedRef.current?.()
return
}
if (mode === 'select') {
// Detect click near a line/polygon for vertex editing
const pixel = e.point