mirror of
https://github.com/zvx-echo6/navi.git
synced 2026-06-10 17:04:50 +02:00
GPS permission:
- Remove silent mount-time getCurrentPosition calls that cause iOS Safari
to cache a "denied" state without ever prompting the user
- LocateButton always retries getCurrentPosition on tap (user gesture)
- Only show "denied" toast on PERMISSION_DENIED (code 1), not timeout
- MapView watchPosition now starts only after confirmed grant, not unconditionally
Traffic toggle:
- Fix isStyleLoaded() race in LayerControl — if style not loaded when
toggle fires, defer to map.once("style.load") instead of silently bailing
- Change outside-click handler from mousedown to pointerdown for mobile
Mobile UX (from prior session):
- Add LocateButton component (crosshair GPS locate/re-center)
- Reposition layer control + locate button to top-right on mobile
(below MapLibre nav controls, above bottom sheet)
- ModeSelector: add min-w-0 to prevent flex overflow at 390px
- StopItem: remove button visible on touch (60% opacity vs hover-only)
- Panel: overflow-x-hidden + safe-area-inset-bottom on mobile sheet
- Body overflow-x guard on mobile viewports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
43 lines
1.3 KiB
JavaScript
43 lines
1.3 KiB
JavaScript
import { Car, Footprints, Bike } from 'lucide-react'
|
|
import { useStore } from '../store'
|
|
|
|
const MODES = [
|
|
{ id: 'auto', label: 'Drive', Icon: Car },
|
|
{ id: 'pedestrian', label: 'Walk', Icon: Footprints },
|
|
{ id: 'bicycle', label: 'Bike', Icon: Bike },
|
|
]
|
|
|
|
export default function ModeSelector() {
|
|
const mode = useStore((s) => s.mode)
|
|
const setMode = useStore((s) => s.setMode)
|
|
|
|
return (
|
|
<div
|
|
className="flex rounded-lg overflow-hidden"
|
|
style={{ border: '1px solid var(--border)' }}
|
|
role="radiogroup"
|
|
aria-label="Travel mode"
|
|
>
|
|
{MODES.map((m) => {
|
|
const active = mode === m.id
|
|
return (
|
|
<button
|
|
key={m.id}
|
|
role="radio"
|
|
aria-checked={active}
|
|
onClick={() => setMode(m.id)}
|
|
className="flex-1 min-w-0 flex items-center justify-center gap-1.5 py-2 px-2 text-xs font-medium transition-colors duration-100"
|
|
style={{
|
|
background: active ? 'var(--accent-muted)' : 'transparent',
|
|
color: active ? 'var(--accent)' : 'var(--text-secondary)',
|
|
borderRight: m.id !== 'bicycle' ? '1px solid var(--border)' : 'none',
|
|
}}
|
|
>
|
|
<m.Icon size={14} />
|
|
{m.label}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|