Environment
- reshaped: 3.9.0
- react / react-dom: 19.x
- Affected: iOS Safari and iOS Chrome (both WebKit). Works on desktop (Chrome/Safari/Firefox) and Android.
Description
An open Flyout / Popover / DropdownMenu does not close when the user taps outside of it on iOS. The user gets stuck with the menu open and cannot interact with the page underneath. Selecting a menu item or re-tapping the trigger still closes it — only outside-tap dismissal is broken, and only on iOS.
Steps to reproduce
- On an iOS device (Safari or Chrome), render a
DropdownMenu (or Popover) whose trigger sits among plain, non-interactive elements (e.g. a View/div without cursor: pointer).
- Tap the trigger to open the menu.
- Tap an empty / non-interactive area outside the menu.
Expected: the flyout closes (as it does on desktop / Android).
Actual: the flyout stays open.
Root cause
useOnClickOutside records the press position on mousedown / touchstart, but fires the actual close handler on a document-level click event:
// reshaped/dist/hooks/useOnClickOutside.js
document.addEventListener("mousedown", handleMouseDown, { passive: true });
document.addEventListener("touchstart", handleMouseDown, { passive: true });
// ...
const handleClick = (event) => {
if ("button" in event && event.button === 2) return;
if (isMouseDownInsideRef.current) return;
handlerRef.current?.(event); // -> handleClose({ reason: "outside-click" })
};
document.addEventListener("click", handleClick);
Suggested fix
Trigger outside-dismissal from a pointer/touch event that iOS delivers regardless of target interactivity, instead of (or in addition to) click:
- Add a touchend listener alongside click. isMouseDownInsideRef is already set on touchstart, so the inside/outside bookkeeping keeps working:
document.addEventListener("click", handleClick);
document.addEventListener("touchend", handleClick); // iOS delivers this for any target
// ...remove both in cleanup
- Or switch outside detection to pointerdown (fires on iOS for all targets) and dismiss from there.
Environment
Description
An open
Flyout/Popover/DropdownMenudoes not close when the user taps outside of it on iOS. The user gets stuck with the menu open and cannot interact with the page underneath. Selecting a menu item or re-tapping the trigger still closes it — only outside-tap dismissal is broken, and only on iOS.Steps to reproduce
DropdownMenu(orPopover) whose trigger sits among plain, non-interactive elements (e.g. aView/divwithoutcursor: pointer).Expected: the flyout closes (as it does on desktop / Android).
Actual: the flyout stays open.
Root cause
useOnClickOutsiderecords the press position onmousedown/touchstart, but fires the actual close handler on a document-levelclickevent:Suggested fix
Trigger outside-dismissal from a pointer/touch event that iOS delivers regardless of target interactivity, instead of (or in addition to) click: