-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Slider] Touch in SwipeableDrawer #16565
Comments
Similar issue with a scrolling content: https://codesandbox.io/s/keen-cache-c7pzc. |
@daniel-rabe What do you think of this diff? diff --git a/packages/material-ui/src/Slider/Slider.js b/packages/material-ui/src/Slider/Slider.js
index f96acaf3a..aa6fefc3a 100644
--- a/packages/material-ui/src/Slider/Slider.js
+++ b/packages/material-ui/src/Slider/Slider.js
@@ -552,6 +552,10 @@ const Slider = React.forwardRef(function Slider(props, ref) {
const handleTouchStart = useEventCallback(event => {
// Workaround as Safari has partial support for touchAction: 'none'.
event.preventDefault();
+
+ // The slider is going to take care of this interaction. Dibs!
+ event.stopPropagation();
+
const touch = event.changedTouches[0];
if (touch != null) {
// A number that uniquely identifies the current finger in the touch session. |
However, reading https://css-tricks.com/dangers-stopping-event-propagation/, I'm wondering if we shouldn't rely on something else cc @eps1lon. |
i tested this in our project with mui-core 3.9.3 and lab 3.0.0-alpha30 and it does not solve the problem. i think preventDefault should already fix the drawer-sliding, but the touchEvent is passive by default and preventDefault has no effect |
Only works with v4. |
Alternatively: diff --git a/packages/material-ui/src/Slider/Slider.js b/packages/material-ui/src/Slider/Slider.js
index f96acaf3a..f7db34fcf 100644
--- a/packages/material-ui/src/Slider/Slider.js
+++ b/packages/material-ui/src/Slider/Slider.js
@@ -552,6 +552,10 @@ const Slider = React.forwardRef(function Slider(props, ref) {
const handleTouchStart = useEventCallback(event => {
// Workaround as Safari has partial support for touchAction: 'none'.
event.preventDefault();
+
+ // The slider is going to take care of this interaction. Dibs!
+ event.__MUI_HANDLED__ = true;
+
const touch = event.changedTouches[0];
if (touch != null) {
// A number that uniquely identifies the current finger in the touch session.
diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
index b04e4b021..3991c1184 100644
--- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
+++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
@@ -293,7 +293,10 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(props, ref) {
const handleBodyTouchStart = React.useCallback(
event => {
// We are not supposed to handle this touch move.
- if (nodeThatClaimedTheSwipe !== null && nodeThatClaimedTheSwipe !== swipeInstance.current) {
+ if (
+ (nodeThatClaimedTheSwipe !== null && nodeThatClaimedTheSwipe !== swipeInstance.current) ||
+ event.__MUI_HANDLED__
+ ) {
return;
} |
@oliviertassinari is this resolved? Can i work on this? |
@jayesh-kaza Thanks for giving a start at this issue. I want to make is clear that the diff I have showcased isn't meant to be the actual solution to the problem, rather something we could explore. I believe the related issue is part of a broader problem:
I think that we should consider a solution that closes these 4 issues at the same time. It would ensure we have something that is globally optimum. I have created a simple reproduction for issues 1, 2, and 3: import React from 'react';
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
import Slider from '@material-ui/core/Slider';
import Dialog from '@material-ui/core/Dialog';
export default function App() {
const [dialogOpen, setDialogOpen] = React.useState(false);
const [drawerOpen, setDrawerOpen] = React.useState(false);
return (
<div>
<button
onClick={() => {
setDrawerOpen(true);
}}
>
open drawer
</button>
<SwipeableDrawer
open={drawerOpen}
onClose={() => {
setDrawerOpen(false);
}}
onOpen={() => {
setDrawerOpen(true);
}}
anchor="right"
variant="temporary"
>
<div style={{ width: 200, padding: '50px 20px' }}>
slider issue #16565
<Slider defaultValue={20} aria-labelledby="continuous-slider" />
<button
onClick={event => {
setDialogOpen(true);
}}
>
Dialog issue #17408 open dialog
</button>
<div style={{ marginTop: 20, overflow: 'auto' }}>
<div style={{ width: 400, backgroundColor: 'red' }}>
scroll container scroll containerscroll container scroll container scroll container
scroll containerscroll container scroll container scroll container scroll
containerscroll container scroll container
</div>
</div>
</div>
</SwipeableDrawer>
<Dialog
open={dialogOpen}
onClose={() => {
setDialogOpen(false);
}}
>
<div>dialog body dialog body dialog body dialog body</div>
</Dialog>
</div>
);
} |
I propose the following solution to problem n°1 (this issue). It's based on https://css-tricks.com/dangers-stopping-event-propagation/ recommandations. I like how it reduces the bundle size and give developers some control with diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
index 862aaf47e..a1d63ee4e 100644
--- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
+++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
@@ -13,16 +13,6 @@ import SwipeArea from './SwipeArea';
// trigger a native scroll.
const UNCERTAINTY_THRESHOLD = 3; // px
-// We can only have one node at the time claiming ownership for handling the swipe.
-// Otherwise, the UX would be confusing.
-// That's why we use a singleton here.
-let nodeThatClaimedTheSwipe = null;
-
-// Exported for test purposes.
-export function reset() {
- nodeThatClaimedTheSwipe = null;
-}
-
function calculateCurrentX(anchor, touches) {
return anchor === 'right' ? document.body.offsetWidth - touches[0].pageX : touches[0].pageX;
}
@@ -147,7 +137,6 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(props, ref) {
if (!touchDetected.current) {
return;
}
- nodeThatClaimedTheSwipe = null;
touchDetected.current = false;
setMaybeSwiping(false);
@@ -302,8 +291,13 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(props, ref) {
const handleBodyTouchStart = React.useCallback(
event => {
- // We are not supposed to handle this touch move.
- if (nodeThatClaimedTheSwipe !== null && nodeThatClaimedTheSwipe !== swipeInstance.current) {
+ // Example of use case: ignore the event if there is a Slider.
+ if (event.defaultPrevented) {
+ return;
+ }
+
+ // We can only have one node at the time claiming ownership for handling the swipe.
+ if (event.muiHandled) {
return;
}
@@ -326,7 +320,7 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(props, ref) {
}
}
- nodeThatClaimedTheSwipe = swipeInstance.current;
+ event.muiHandled = true;
swipeInstance.current.startX = currentX;
swipeInstance.current.startY = currentY;
@@ -367,16 +361,6 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(props, ref) {
return undefined;
}, [variant, handleBodyTouchStart, handleBodyTouchMove, handleBodyTouchEnd]);
- React.useEffect(
- () => () => {
- // We need to release the lock.
- if (nodeThatClaimedTheSwipe === swipeInstance.current) {
- nodeThatClaimedTheSwipe = null;
- }
- },
- [],
- );
-
React.useEffect(() => {
if (!open) {
setMaybeSwiping(false); |
When the Slider is in a SwipeableDrawer the drawer moves on slider-movement on touch devices.
https://codesandbox.io/s/great-tesla-0kjx1?fontsize=14
The text was updated successfully, but these errors were encountered: