From c981fe702e285f8ec16b3253b19576e5c31913ae Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 19 Apr 2020 19:12:18 +0200 Subject: [PATCH 1/2] [ClickAwayListener] Support leading edge --- .../click-away-listener/LeadingClickAway.js | 51 ++++++++++++++++++ .../click-away-listener/LeadingClickAway.tsx | 53 +++++++++++++++++++ .../click-away-listener.md | 7 +++ .../ClickAwayListener/ClickAwayListener.js | 10 +++- 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 docs/src/pages/components/click-away-listener/LeadingClickAway.js create mode 100644 docs/src/pages/components/click-away-listener/LeadingClickAway.tsx diff --git a/docs/src/pages/components/click-away-listener/LeadingClickAway.js b/docs/src/pages/components/click-away-listener/LeadingClickAway.js new file mode 100644 index 00000000000000..c25cbc7495dbfb --- /dev/null +++ b/docs/src/pages/components/click-away-listener/LeadingClickAway.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; + +const useStyles = makeStyles((theme) => ({ + root: { + position: 'relative', + }, + dropdown: { + position: 'absolute', + top: 28, + right: 0, + left: 0, + zIndex: 1, + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, +})); + +export default function LeadingClickAway() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen((prev) => !prev); + }; + + const handleClickAway = () => { + setOpen(false); + }; + + return ( + +
+ + {open ? ( +
+ Click me, I will stay visible until you click outside. +
+ ) : null} +
+
+ ); +} diff --git a/docs/src/pages/components/click-away-listener/LeadingClickAway.tsx b/docs/src/pages/components/click-away-listener/LeadingClickAway.tsx new file mode 100644 index 00000000000000..ea92116e3419d8 --- /dev/null +++ b/docs/src/pages/components/click-away-listener/LeadingClickAway.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + position: 'relative', + }, + dropdown: { + position: 'absolute', + top: 28, + right: 0, + left: 0, + zIndex: 1, + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export default function LeadingClickAway() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen((prev) => !prev); + }; + + const handleClickAway = () => { + setOpen(false); + }; + + return ( + +
+ + {open ? ( +
+ Click me, I will stay visible until you click outside. +
+ ) : null} +
+
+ ); +} diff --git a/docs/src/pages/components/click-away-listener/click-away-listener.md b/docs/src/pages/components/click-away-listener/click-away-listener.md index a73e46fe4a7842..42a4c64a37a9dd 100644 --- a/docs/src/pages/components/click-away-listener/click-away-listener.md +++ b/docs/src/pages/components/click-away-listener/click-away-listener.md @@ -24,3 +24,10 @@ You can find a more advanced demo on the [Menu documentation section](/component The following demo uses [`Portal`](/components/portal/) to render the dropdown into a new "subtree" outside of current DOM hierarchy. {{"demo": "pages/components/click-away-listener/PortalClickAway.js"}} + +## Leading edge + +By default, the component responds to the trailing events (click + touch end). +However, you can configure it to respond to the leading events (mouse down + touch start). + +{{"demo": "pages/components/click-away-listener/LeadingClickAway.js"}} diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js index f21becace8739e..829a026e8f3ba3 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js @@ -10,6 +10,13 @@ function mapEventPropToEvent(eventProp) { return eventProp.substring(2).toLowerCase(); } +function hasClickedScrollbar(event) { + return ( + document.documentElement.clientWidth < event.clientX || + document.documentElement.clientHeight < event.clientY + ); +} + /** * Listen for click events that occur somewhere in the document, outside of the element itself. * For instance, if you need to hide a menu when people click anywhere else on your page. @@ -55,7 +62,8 @@ function ClickAwayListener(props) { // 1. IE 11 support, which trigger the handleClickAway even after the unbind // 2. The child might render null. - if (!mountedRef.current || !nodeRef.current) { + // 3. Behave like a blur listener. + if (!mountedRef.current || !nodeRef.current || hasClickedScrollbar(event)) { return; } From 3b6195de9ab0243fc7760cd0c1dc9a763dd62fa5 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 19 Apr 2020 23:45:28 +0200 Subject: [PATCH 2/2] Sebsatian review --- .../components/click-away-listener/click-away-listener.md | 2 ++ .../material-ui/src/ClickAwayListener/ClickAwayListener.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/click-away-listener/click-away-listener.md b/docs/src/pages/components/click-away-listener/click-away-listener.md index 42a4c64a37a9dd..7c7f0fe1a580b3 100644 --- a/docs/src/pages/components/click-away-listener/click-away-listener.md +++ b/docs/src/pages/components/click-away-listener/click-away-listener.md @@ -31,3 +31,5 @@ By default, the component responds to the trailing events (click + touch end). However, you can configure it to respond to the leading events (mouse down + touch start). {{"demo": "pages/components/click-away-listener/LeadingClickAway.js"}} + +> ⚠️ In this mode, only interactions on the scrollbar of the document is ignored. diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js index 829a026e8f3ba3..5b8d6b91ccf736 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js @@ -10,7 +10,7 @@ function mapEventPropToEvent(eventProp) { return eventProp.substring(2).toLowerCase(); } -function hasClickedScrollbar(event) { +function clickedRootScrollbar(event) { return ( document.documentElement.clientWidth < event.clientX || document.documentElement.clientHeight < event.clientY @@ -63,7 +63,7 @@ function ClickAwayListener(props) { // 1. IE 11 support, which trigger the handleClickAway even after the unbind // 2. The child might render null. // 3. Behave like a blur listener. - if (!mountedRef.current || !nodeRef.current || hasClickedScrollbar(event)) { + if (!mountedRef.current || !nodeRef.current || clickedRootScrollbar(event)) { return; }