From 3f9e7f2074df84dc6df2997e794c0335c3c25647 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 05:23:01 +0000 Subject: [PATCH 1/2] fix: disarm cell/zone selection while a draw tool is armed (tablet) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drawing extra-risk zones relied on the `.leaflet-drawing` CSS making hex polygons and markers click-through via `pointer-events: none`. That works with a mouse, but on touch tablets the tap still reaches the hex polygon's own Leaflet click handler, so a tap selected a cell instead of starting the shape — drawing was effectively desktop-only. Gate the interactive handlers on `drawMode` instead of relying on the CSS: - HexGridLayer: bind no click/hover handlers while a draw tool is armed. - ExtraRiskLayer: committed zones are interactive only when no tool is armed, so a tap inside an existing zone can't steal the gesture either. Terra Draw still receives the gesture at the map-container level, so the behaviour is now identical on mouse and touch. --- src/ui/map/HexGridLayer.tsx | 20 +++++++++++++++----- src/ui/map/extra-risk/ExtraRiskLayer.tsx | 6 +++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/ui/map/HexGridLayer.tsx b/src/ui/map/HexGridLayer.tsx index c967939..93ad244 100644 --- a/src/ui/map/HexGridLayer.tsx +++ b/src/ui/map/HexGridLayer.tsx @@ -17,6 +17,7 @@ export function HexGridLayer() { const selectedCellId = useBlockbusterStore((s) => s.selectedCellId); const hoveredCellId = useBlockbusterStore((s) => s.hoveredCellId); const waypoints = useBlockbusterStore((s) => s.waypoints); + const drawMode = useBlockbusterStore((s) => s.drawMode); const selectCell = useBlockbusterStore((s) => s.selectCell); const hoverCell = useBlockbusterStore((s) => s.hoverCell); @@ -31,6 +32,13 @@ export function HexGridLayer() { if (!grid || !showHexGrid) return null; const waypointSet = new Set(waypoints); + // While an extra-risk draw tool is armed, taps must not select or hover a + // cell — on touch devices the click-through CSS (pointer-events: none) does + // not stop the polygon's own click, so a tap would otherwise select a cell + // instead of starting the shape. Disarming the handlers makes drawing behave + // identically on mouse and touch; Terra Draw still receives the gesture at the + // map-container level. See ExtraRiskDraw / app.css `.leaflet-drawing`. + const drawing = drawMode !== null; // Own pane (below the pie + route panes) so the stack order survives toggling // the grid off and on — Leaflet otherwise paints by DOM insertion order. @@ -51,11 +59,13 @@ export function HexGridLayer() { const isHovered = cell.id === hoveredCellId; const isWaypoint = waypointSet.has(cell.id); - const handlers: LeafletEventHandlerFnMap = { - click: () => selectCell(cell.id), - mouseover: () => hoverCell(cell.id), - mouseout: () => hoverCell(null), - }; + const handlers: LeafletEventHandlerFnMap = drawing + ? {} + : { + click: () => selectCell(cell.id), + mouseover: () => hoverCell(cell.id), + mouseout: () => hoverCell(null), + }; return ( s.zones); const selectedZoneId = useBlockbusterStore((s) => s.selectedZoneId); const activeTab = useBlockbusterStore((s) => s.activeTab); + const drawMode = useBlockbusterStore((s) => s.drawMode); const selectZone = useBlockbusterStore((s) => s.selectZone); - const interactive = activeTab === 'extra'; + // Clickable to select only on the Extra-factors tab, and never while a draw + // tool is armed — on touch a tap inside a committed zone would otherwise + // select it instead of starting the new shape (see HexGridLayer). + const interactive = activeTab === 'extra' && drawMode === null; if (zones.length === 0) return null; From 111aba6b342a64e19f8b8bf1c15a9d11f27ed123 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 05:33:49 +0000 Subject: [PATCH 2/2] fix: let extra-risk zones be drawn by dragging on touch After the previous fix a tap no longer selected a cell, but dragging to draw a rectangle/circle just panned the map. Terra Draw's rectangle and circle modes default to the `click-move` interaction, where a drag is not a draw gesture at all, so Leaflet's drag handler consumed it as a pan. - Set `drawInteraction: 'click-move-or-drag'` on the rectangle and circle modes so a single drag draws the shape (Terra Draw disables map dragging for the duration of that drag via the adapter's setDraggability), while two taps still work. - While any tool is armed, also disable Leaflet's map dragging and double-click zoom so the first gesture and the two-tap path aren't hijacked by a pan or a double-tap zoom; the effect cleanup re-enables them on disarm/unmount. --- src/ui/map/extra-risk/ExtraRiskDraw.tsx | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ui/map/extra-risk/ExtraRiskDraw.tsx b/src/ui/map/extra-risk/ExtraRiskDraw.tsx index 07b157a..b5b0074 100644 --- a/src/ui/map/extra-risk/ExtraRiskDraw.tsx +++ b/src/ui/map/extra-risk/ExtraRiskDraw.tsx @@ -48,10 +48,17 @@ export function ExtraRiskDraw() { const draw = new TerraDraw({ adapter: new TerraDrawLeafletAdapter({ map, lib: L }), modes: [ - new TerraDrawRectangleMode({ styles }), + // 'click-move-or-drag' so a single drag draws the box (what touch users + // reach for) while two taps still work where dragging is awkward. + new TerraDrawRectangleMode({ styles, drawInteraction: 'click-move-or-drag' }), // CRS.Simple is not web-mercator; we pick the planar projection (never // 'globe'/haversine, which blows up here) and re-round circles on finish. - new TerraDrawCircleMode({ projection: 'web-mercator', segments: 64, styles }), + new TerraDrawCircleMode({ + projection: 'web-mercator', + segments: 64, + styles, + drawInteraction: 'click-move-or-drag', + }), new TerraDrawPolygonMode({ styles }), ], }); @@ -85,17 +92,28 @@ export function ExtraRiskDraw() { }; }, [map, addZone]); - // Arm or disarm the active tool. + // Arm or disarm the active tool. While a tool is armed we also disable + // Leaflet's own pan/zoom gestures: otherwise a drag (especially on touch, + // where there is no hover) is swallowed by the map's drag handler and pans + // the map instead of reaching Terra Draw to draw the shape. The cleanup + // re-enables them whenever the tool is disarmed or this layer unmounts (e.g. + // leaving the Extra-risk tab), so map panning is never left disabled. useEffect(() => { const draw = drawRef.current; if (!draw) return; if (drawMode) { if (!draw.enabled) draw.start(); draw.setMode(drawMode); - } else if (draw.enabled) { - draw.stop(); + map.dragging.disable(); + map.doubleClickZoom.disable(); + return () => { + map.dragging.enable(); + map.doubleClickZoom.enable(); + }; } - }, [drawMode]); + if (draw.enabled) draw.stop(); + return undefined; + }, [drawMode, map]); // While a tool is armed, flag the map container so its vector layers (hexes, // zones) and markers become click-through (see app.css). Otherwise Leaflet