Skip to content

fix(web): keep the long-press pin card on release and on every touch screen - #760

Merged
Medformatik merged 2 commits into
mainfrom
fix/mobile-long-press-context-menu
Nov 4, 2026
Merged

Medformatik merged 2 commits into
mainfrom
fix/mobile-long-press-context-menu

Conversation

@Medformatik

@Medformatik Medformatik commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

What changed and why

Follow-up to #740. On a phone, a long-press on the map opened the desktop right-click menu under the finger, and the menu closed again as soon as the finger lifted.

Cause

  • MapLibre 6.16 makes up its own contextmenu event once a finger has rested on the map for 500 ms. It is a plain MouseEvent with no pointerType or sourceCapabilities, so MapContextMenu's touch check let it through and opened the menu.
  • The menu's ClickAwayListener reacts to touchend. The touchend of the very finger that opened the menu counted as a tap outside it, so lifting the finger closed it.
  • Our own long-press fires right after MapLibre's, so the dropped-pin card appeared behind the menu and was easy to miss.

Fix

  • Touch never opens the context menu. As designed in feat(web): map gestures, dropped-pin actions and swipe-away place cards on mobile #740, a long-press drops the gray pin, and its card carries the same actions.
  • cameFromTouch (in lib/mapTouch, replacing isTouchClick and the menu's own copy) also counts an event without pointer information as touch while a finger is down or has just lifted. Touches are tracked page-wide, so the answer doesn't depend on which component is mounted.
  • The click some browsers send when a long-press lifts (iOS Safari, Chromium touch emulation) is now swallowed counting from the lift, not from when the pin dropped. Before, a hold longer than about 1.5 s let that click through as a tap, which cleared the pin it had just dropped. A new touch ends that window, so the next tap still clears the pin.

The card on every touch screen (second commit)

With touch never opening the menu, the pin card is where its actions live on a touch screen. The card also had gaps of its own:

  • Tablets and phones held sideways. The card chose its layout by width, so above 600 px a touch screen got the compact desktop card with only Directions. Now any touch-first screen ((pointer: coarse)) gets the action row. Wider than a phone, the card keeps the desktop card's place, 480 px wide, and the row wraps onto a second line instead of scrolling.
  • Copy offers the coordinates, the address and a place's name, like the menu's Copy location. With only one of them, it copies straight away.
  • Long-press on a map label recognises that place, as right-click does: the card shows its name, routes under it, and Details opens the place.
  • The menu and the card now describe a location through one helper (components/map/mapLocation), so they can't drift apart.
  • The places help page describes this.

Desktop with a mouse is unchanged: right-click opens the menu, left click drops the pin and shows the compact card.

Verification

  • Reproduced on the dev server at 390×844 with real touch input (CDP touch events), logging every event. On main, every hold opened the menu at about 550 ms and closed it on release.

  • With the fix, holds of 0.7 s, 1.6 s, 2 s and 4 s never open the menu, and the pin and card stay. The release click now reaches the canvas and is swallowed. A tap afterwards clears the pin, and a second long-press drops a new one.

  • Long-press on a phone held sideways (844×390) and a tablet (820×1180): the card shows all six actions in two rows, centred above the footer. On a phone (390×844) it keeps the scrolling row.

  • On desktop (1280×800, mouse), right-click opens the context menu with all six actions, and left click drops the pin with the compact card.

  • Copy's choice and map-label recognition need the geocoding API and map tiles, which aren't available in this environment, so tests cover them rather than a browser check.

  • New tests:

    • MapLibre's long-press contextmenu doesn't open the menu and leaves the pin.
    • A 3 s hold keeps the pin on release, and the next tap clears it.
    • A plain tap's click isn't swallowed.
    • A touch outside the map counts.
    • On a touch screen wider than a phone, the card has the action row.
    • Copy offers coordinates and address, and a map label's name.
    • Details opens the map label's place, and From here routes under its name.
    • The shared location helper's fallbacks and copy rows.

    The component tests for the two bugs fail on main, and the five new card tests fail on the previous card.

  • Web test suite: 5855 passed, 8 failed. The 8 (useNavJunctions ×7, CategoryResultMarkers ×1) fail the same way on an untouched main in this environment. The web type check, Biome on the changed files, Prettier on the docs page and pnpm check-dead-code pass.

  • Not verified: a real Android or iOS device. In particular, I couldn't check whether Android Chrome sends a click after a long-press; the fix covers both cases.

Release and upgrade impact

None.

…lease

On a phone, holding a finger on the map opened the desktop right-click
menu under it, and the menu closed again as soon as the finger lifted.

MapLibre 6.16 makes up its own contextmenu event when a finger rests on
the map for 500 ms. It is a plain MouseEvent with no pointer type, so
the context menu's touch check let it through. The menu then closed on
the lift: its click-away listener reacts to touchend, and the touchend
of the finger that opened it counted as a tap outside.

Touch never opens that menu now; the long-press drops the gray pin,
whose card carries the same actions. A shared check in lib/mapTouch
treats an event with no pointer information as touch while a finger is
down or has just lifted, wherever on the page it touched, so MapLibre's
event is recognised whichever component asks.

The click some browsers send when a long-press lifts is now swallowed
counting from the lift, not from when the pin dropped. Before, a hold
longer than about 1.5 s let that click through as a tap, which cleared
the pin it had just dropped. A new touch ends that window, so the next
tap still clears the pin.
…oice

Touch never opens the right-click menu, so the dropped-pin card is where
its actions live on a touch screen. The card chose its layout by width,
though: a tablet or a phone held sideways got the compact desktop card
with only Directions, leaving From here, Share, Save parking and Copy
out of reach.

- The action row shows on any touch-first screen ((pointer: coarse)).
  Wider than a phone, the card keeps the desktop card's place, 480 px
  wide, and the row wraps instead of scrolling. A mouse-driven desktop
  keeps its compact card and right-click menu.
- Copy offers the coordinates, the address and a place's name, as the
  menu's Copy location does; with only one of them it copies directly.
- A long-press on a basemap label recognises that place, as right-click
  does: the card shows its name, routes under it, and Details opens it.

The menu and the card now describe a location through one helper
(components/map/mapLocation), so the two can't drift apart.
@Medformatik Medformatik changed the title fix(web): keep the long-press pin instead of a menu that closes on release fix(web): keep the long-press pin card on release and on every touch screen Nov 3, 2026
@Medformatik
Medformatik merged commit 31f640e into main Nov 4, 2026
38 of 42 checks passed
@Medformatik
Medformatik deleted the fix/mobile-long-press-context-menu branch November 4, 2026 00:00
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant