fix: mobile UX — GPS permission flow, traffic toggle, overflow

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>
This commit is contained in:
Matt 2026-04-22 03:27:21 +00:00
commit 03e9780834
8 changed files with 216 additions and 72 deletions

View file

@ -26,7 +26,7 @@ export default function ModeSelector() {
role="radio"
aria-checked={active}
onClick={() => setMode(m.id)}
className="flex-1 flex items-center justify-center gap-1.5 py-2 px-2 text-xs font-medium transition-colors duration-100"
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)',