@@ -60,7 +60,7 @@ export class Button extends ButtonBase {
6060
6161 private _mountedButton : HTMLButtonElement | null = null ;
6262 private _lastMouseDownEvent : Types . SyntheticEvent | undefined ;
63- private _ignoreMouseUp = false ;
63+ private _ignoreTouchEnd = false ;
6464 private _ignoreClick = false ;
6565 private _longPressTimer : number | undefined ;
6666 private _isMouseOver = false ;
@@ -104,7 +104,8 @@ export class Button extends ButtonBase {
104104 aria-checked = { ariaChecked }
105105 onClick = { this . onClick }
106106 onTouchStart = { this . _onMouseDown }
107- onTouchEnd = { this . _onMouseUp }
107+ onTouchMove = { this . _onTouchMove }
108+ onTouchEnd = { this . _onTouchEnd }
108109 onContextMenu = { this . _onContextMenu }
109110 onMouseDown = { this . _onMouseDown }
110111 onMouseUp = { this . _onMouseUp }
@@ -194,6 +195,7 @@ export class Button extends ButtonBase {
194195 }
195196
196197 private _onMouseDown = ( e : React . SyntheticEvent < any > ) => {
198+ this . _isMouseOver = true ;
197199 if ( this . props . onPressIn ) {
198200 this . props . onPressIn ( e ) ;
199201 }
@@ -211,21 +213,32 @@ export class Button extends ButtonBase {
211213 if ( this . props . onLongPress ) {
212214 // lastMouseDownEvent can never be undefined at this point
213215 this . props . onLongPress ( this . _lastMouseDownEvent ! ) ;
214- this . _ignoreMouseUp = true ;
215- this . _ignoreClick = true ;
216+
217+ if ( 'touches' in e ) {
218+ this . _ignoreTouchEnd = true ;
219+ } else {
220+ this . _ignoreClick = true ;
221+ }
216222 }
217223 } , this . props . delayLongPress || _longPressTime ) ;
218224 }
219225 }
220226
221- private _onMouseUp = ( e : Types . SyntheticEvent | Types . TouchEvent ) => {
222- if ( this . _ignoreMouseUp ) {
223- e . stopPropagation ( ) ;
224- // Touch event won't trigger onClick when a long press is released. So we reset the ignore flag here.
225- if ( 'touches' in e ) {
226- this . _ignoreClick = false ;
227- }
227+ private _onTouchMove = ( e : React . SyntheticEvent < HTMLButtonElement , TouchEvent > ) => {
228+ const buttonRect = ( e . target as HTMLButtonElement ) . getBoundingClientRect ( ) ;
229+ this . _isMouseOver =
230+ e . nativeEvent . touches [ 0 ] . clientX > buttonRect . left &&
231+ e . nativeEvent . touches [ 0 ] . clientX < buttonRect . right &&
232+ e . nativeEvent . touches [ 0 ] . clientY > buttonRect . top &&
233+ e . nativeEvent . touches [ 0 ] . clientY < buttonRect . bottom ;
234+
235+ // Touch has left the button, cancel the longpress handler.
236+ if ( this . _isMouseOver && this . _longPressTimer ) {
237+ clearTimeout ( this . _longPressTimer ) ;
228238 }
239+ }
240+
241+ private _onMouseUp = ( e : Types . SyntheticEvent | Types . TouchEvent ) => {
229242 if ( this . props . onPressOut ) {
230243 this . props . onPressOut ( e ) ;
231244 }
@@ -235,6 +248,40 @@ export class Button extends ButtonBase {
235248 }
236249 }
237250
251+ /**
252+ * Case where onPressOut is not triggered and the bubbling is canceled:
253+ * 1- Long press > release
254+ *
255+ * Cases where onPressOut is triggered:
256+ * 2- Long press > leave button > release touch
257+ * 3- Tap
258+ *
259+ * Case where onPressOut is not triggered and the bubbling is NOT canceled:
260+ * 4- Press in > leave button > release touch
261+ */
262+ private _onTouchEnd = ( e : Types . SyntheticEvent | Types . TouchEvent ) => {
263+ if ( this . _isMouseOver && this . _ignoreTouchEnd ) {
264+ /* 1 */
265+ e . stopPropagation ( ) ;
266+ } else if (
267+ /* 2 */
268+ ! this . _isMouseOver && this . _ignoreTouchEnd ||
269+ /* 3 */
270+ this . _isMouseOver && ! this . _ignoreTouchEnd
271+ ) {
272+ if ( this . props . onPressOut ) {
273+ this . props . onPressOut ( e ) ;
274+ }
275+ } else {
276+ /* 4 */
277+ this . _ignoreTouchEnd = false ;
278+ }
279+
280+ if ( this . _longPressTimer ) {
281+ clearTimeout ( this . _longPressTimer ) ;
282+ }
283+ }
284+
238285 private _onMouseEnter = ( e : Types . SyntheticEvent ) => {
239286 this . _isMouseOver = true ;
240287 this . _onHoverStart ( e ) ;
@@ -243,6 +290,14 @@ export class Button extends ButtonBase {
243290 private _onMouseLeave = ( e : Types . SyntheticEvent ) => {
244291 this . _isMouseOver = false ;
245292 this . _onHoverEnd ( e ) ;
293+
294+ // The mouse is still down. A long press may be just happened. Re-enable the next click.
295+ this . _ignoreClick = false ;
296+
297+ // Cancel longpress if mouse has left.
298+ if ( this . _longPressTimer ) {
299+ clearTimeout ( this . _longPressTimer ) ;
300+ }
246301 }
247302
248303 // When we get focus on an element, show the hover effect on the element.
0 commit comments