Bug 1493659 - Skip the click event handler if touch event has occurred. r=birtles

Tapping the menu button during the panel has shown make the menu button to be unopenable. This cause reason is removing the pointer-events property before the mouse event happens.
The tapping event will happen many the event like the following:
1) touchstart
2) touchmove (Long tap only)
3) touchend
4) mousedown
5) mouseup
6) click

This patch will detect the touchend event to eat the click event.

Differential Revision: https://phabricator.services.mozilla.com/D7827

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mantaroh Yoshinaga 2018-10-07 17:52:11 +00:00
Родитель 4865990e1a
Коммит 4c95ba0b3d
1 изменённых файлов: 50 добавлений и 0 удалений

Просмотреть файл

@ -70,6 +70,7 @@ class MenuButton extends PureComponent {
this.onHidden = this.onHidden.bind(this); this.onHidden = this.onHidden.bind(this);
this.onClick = this.onClick.bind(this); this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this); this.onKeyDown = this.onKeyDown.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
this.buttonRef = createRef(); this.buttonRef = createRef();
@ -79,6 +80,7 @@ class MenuButton extends PureComponent {
isMenuInitialized: flags.testing || false, isMenuInitialized: flags.testing || false,
win: props.doc.defaultView.top, win: props.doc.defaultView.top,
}; };
this.ignoreNextClick = false;
this.initializeTooltip(); this.initializeTooltip();
} }
@ -196,6 +198,44 @@ class MenuButton extends PureComponent {
}); });
} }
// When we are closing the menu we will get a 'hidden' event before we get
// a 'click' event. We want to re-enable the pointer-events: auto setting we
// use on the button while the menu is visible, but we don't want to do it
// until after the subsequent click event since otherwise we will end up
// re-opening the menu.
//
// For mouse events, we achieve this by using setTimeout(..., 0) to schedule
// a separate task to run after the click event, but in the case of touch
// events the event order differs and the setTimeout callback will run before
// the click event.
//
// In order to prevent that we detect touch events and set a flag to ignore
// the next click event. However, we need to differentiate between touch drag
// events and long press events (which don't generate a 'click') and "taps"
// (which do). We do that by looking for a 'touchmove' event and clearing the
// flag if we get one.
onTouchStart(evt) {
const touchend = () => {
const anchorRect = this.buttonRef.current.getClientRects()[0];
const { clientX, clientY } = evt.changedTouches[0];
// We need to check that the click is inside the bounds since when the
// menu is being closed the button will currently have
// pointer-events: none (and if we don't check the bounds we will end up
// ignoring unrelated clicks).
if (anchorRect.x <= clientX && clientX <= anchorRect.x + anchorRect.width &&
anchorRect.y <= clientY && clientY <= anchorRect.y + anchorRect.height) {
this.ignoreNextClick = true;
}
};
const touchmove = () => {
this.state.win.removeEventListener("touchend", touchend);
};
this.state.win.addEventListener("touchend", touchend, { once: true });
this.state.win.addEventListener("touchmove", touchmove, { once: true });
}
onHidden() { onHidden() {
this.setState({ expanded: false }); this.setState({ expanded: false });
// While the menu is open, if we click _anywhere_ outside the menu, it will // While the menu is open, if we click _anywhere_ outside the menu, it will
@ -213,14 +253,24 @@ class MenuButton extends PureComponent {
if (this.buttonRef.current) { if (this.buttonRef.current) {
this.buttonRef.current.style.pointerEvents = "auto"; this.buttonRef.current.style.pointerEvents = "auto";
} }
this.state.win.removeEventListener("touchstart",
this.onTouchStart,
true);
}, 0); }, 0);
this.state.win.addEventListener("touchstart", this.onTouchStart, true);
if (this.props.onCloseButton) { if (this.props.onCloseButton) {
this.props.onCloseButton(); this.props.onCloseButton();
} }
} }
async onClick(e) { async onClick(e) {
if (this.ignoreNextClick) {
this.ignoreNextClick = false;
return;
}
if (e.target === this.buttonRef.current) { if (e.target === this.buttonRef.current) {
// On Mac, even after clicking the button it doesn't get focus. // On Mac, even after clicking the button it doesn't get focus.
// Force focus to the button so that our keydown handlers get called. // Force focus to the button so that our keydown handlers get called.