From 8b20c1e1800d8efbe844293226c73b999ee92db6 Mon Sep 17 00:00:00 2001 From: Niklas Wagner Date: Wed, 20 Dec 2017 10:38:52 +0100 Subject: [PATCH] [Menu] Better focus, hover, select logic --- docs/src/pages/demos/menus/FadeMenu.js | 47 +++++++++++++++++ docs/src/pages/demos/menus/LongMenu.js | 6 +-- .../pages/demos/menus/MenuListComposition.js | 2 +- docs/src/pages/demos/menus/SimpleListMenu.js | 13 ++--- docs/src/pages/demos/menus/SimpleMenu.js | 13 ++--- docs/src/pages/demos/menus/menus.md | 6 +++ pages/api/popover.md | 2 +- pages/api/snackbar.md | 2 +- pages/demos/menus.js | 7 +++ src/Drawer/Drawer.js | 2 +- src/Menu/Menu.js | 16 ++---- src/Popover/Popover.d.ts | 5 +- src/Popover/Popover.js | 31 ++++++------ src/Snackbar/Snackbar.js | 50 ++++++++----------- src/Table/TablePagination.js | 2 +- src/Tabs/Tabs.js | 2 +- 16 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 docs/src/pages/demos/menus/FadeMenu.js diff --git a/docs/src/pages/demos/menus/FadeMenu.js b/docs/src/pages/demos/menus/FadeMenu.js new file mode 100644 index 00000000000000..f2ef9908ff825c --- /dev/null +++ b/docs/src/pages/demos/menus/FadeMenu.js @@ -0,0 +1,47 @@ +import React from 'react'; +import Button from 'material-ui/Button'; +import Menu, { MenuItem } from 'material-ui/Menu'; +import Fade from 'material-ui/transitions/Fade'; + +class FadeMenu extends React.Component { + state = { + anchorEl: null, + }; + + handleClick = event => { + this.setState({ anchorEl: event.currentTarget }); + }; + + handleClose = () => { + this.setState({ anchorEl: null }); + }; + + render() { + const { anchorEl } = this.state; + + return ( +
+ + + Profile + My account + Logout + +
+ ); + } +} + +export default FadeMenu; diff --git a/docs/src/pages/demos/menus/LongMenu.js b/docs/src/pages/demos/menus/LongMenu.js index 3fbd97b54331d6..9418614a532660 100644 --- a/docs/src/pages/demos/menus/LongMenu.js +++ b/docs/src/pages/demos/menus/LongMenu.js @@ -36,13 +36,13 @@ class LongMenu extends React.Component { }; render() { - const open = Boolean(this.state.anchorEl); + const { anchorEl } = this.state; return (
@@ -51,7 +51,7 @@ class LongMenu extends React.Component { Profile diff --git a/docs/src/pages/demos/menus/menus.md b/docs/src/pages/demos/menus/menus.md index 7bad24312dc8ad..a0b2ecae9bec77 100644 --- a/docs/src/pages/demos/menus/menus.md +++ b/docs/src/pages/demos/menus/menus.md @@ -52,3 +52,9 @@ The `MenuItem` is a wrapper around `ListItem` with some additional styles. You can use the same list composition features with the `MenuItem` component: {{"demo": "pages/demos/menus/ListItemComposition.js"}} + +## Change Transition + +Use a different transition all together. + +{{"demo": "pages/demos/menus/FadeMenu.js"}} diff --git a/pages/api/popover.md b/pages/api/popover.md index 2eadf739f3b3cd..04c43001ebc687 100644 --- a/pages/api/popover.md +++ b/pages/api/popover.md @@ -32,7 +32,7 @@ filename: /src/Popover/Popover.js | open * | bool | | If `true`, the popover is visible. | | PaperProps | object | | Properties applied to the `Paper` element. | | transformOrigin | shape | { vertical: 'top', horizontal: 'left',} | This is the point on the popover which will attach to the anchor's origin.
Options: vertical: [top, center, bottom, x(px)]; horizontal: [left, center, right, x(px)]. | -| transitionClasses | shape | | The animation classNames applied to the component as it enters or exits. This property is a direct binding to [`CSSTransition.classNames`](https://reactcommunity.org/react-transition-group/#CSSTransition-prop-classNames). | +| transition | union: string |
 func
| Grow | Transition component. | | transitionDuration | union: number |
 {enter?: number, exit?: number} |
 {0?: undefined}
| 'auto' | Set to 'auto' to automatically calculate transition time based on height. | Any other properties supplied will be [spread to the root element](/guides/api#spread). diff --git a/pages/api/snackbar.md b/pages/api/snackbar.md index 7ecaeea47a72a7..2db6e5bc63e9f4 100644 --- a/pages/api/snackbar.md +++ b/pages/api/snackbar.md @@ -29,7 +29,7 @@ filename: /src/Snackbar/Snackbar.js | open | bool | | If true, `Snackbar` is open. | | resumeHideDuration | number | | The number of milliseconds to wait before dismissing after user interaction. If `autoHideDuration` property isn't specified, it does nothing. If `autoHideDuration` property is specified but `resumeHideDuration` isn't, we default to `autoHideDuration / 2` ms. | | SnackbarContentProps | object | | Properties applied to the `SnackbarContent` element. | -| transition | union: string |
 func
| | Transition component. | +| transition | union: string |
 func
| Slide | Transition component. | | transitionDuration | union: number |
 {enter?: number, exit?: number}
| { enter: duration.enteringScreen, exit: duration.leavingScreen,} | The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object. | Any other properties supplied will be [spread to the root element](/guides/api#spread). diff --git a/pages/demos/menus.js b/pages/demos/menus.js index e622e607449898..64d81ce3a5a214 100644 --- a/pages/demos/menus.js +++ b/pages/demos/menus.js @@ -41,6 +41,13 @@ module.exports = require('fs') raw: preval` module.exports = require('fs') .readFileSync(require.resolve('docs/src/pages/demos/menus/ListItemComposition'), 'utf8') +`, + }, + 'pages/demos/menus/FadeMenu.js': { + js: require('docs/src/pages/demos/menus/FadeMenu').default, + raw: preval` +module.exports = require('fs') + .readFileSync(require.resolve('docs/src/pages/demos/menus/FadeMenu'), 'utf8') `, }, }} diff --git a/src/Drawer/Drawer.js b/src/Drawer/Drawer.js index eaa92b77f7993a..1a678c46b0a746 100644 --- a/src/Drawer/Drawer.js +++ b/src/Drawer/Drawer.js @@ -248,4 +248,4 @@ Drawer.defaultProps = { type: 'temporary', // Mobile first. }; -export default withStyles(styles, { flip: false, withTheme: true, name: 'MuiDrawer' })(Drawer); +export default withStyles(styles, { name: 'MuiDrawer', flip: false, withTheme: true })(Drawer); diff --git a/src/Menu/Menu.js b/src/Menu/Menu.js index d60086b677e785..828beb726d011a 100644 --- a/src/Menu/Menu.js +++ b/src/Menu/Menu.js @@ -36,14 +36,6 @@ class Menu extends React.Component { } } - componentDidUpdate(prevProps) { - if (!prevProps.open && this.props.open) { - // Needs to refocus as when a menu is rendered into another Modal, - // the first modal might change the focus to prevent any leak. - this.focus(); - } - } - getContentAnchorEl = () => { if (!this.menuList || !this.menuList.selectedItem) { return findDOMNode(this.menuList).firstChild; @@ -66,7 +58,7 @@ class Menu extends React.Component { } }; - handleEnter = (element: HTMLElement) => { + handleEnter = element => { const { theme } = this.props; const menuList = findDOMNode(this.menuList); @@ -90,7 +82,7 @@ class Menu extends React.Component { } }; - handleListKeyDown = (event: SyntheticUIEvent<>, key: string) => { + handleListKeyDown = (event, key) => { if (key === 'tab') { event.preventDefault(); @@ -106,7 +98,6 @@ class Menu extends React.Component { classes, MenuListProps, onEnter, - open, PaperProps = {}, PopoverClasses, theme, @@ -119,7 +110,6 @@ class Menu extends React.Component { getContentAnchorEl={this.getContentAnchorEl} classes={PopoverClasses} onEnter={this.handleEnter} - open={open} anchorOrigin={themeDirection === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN} transformOrigin={themeDirection === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN} PaperProps={{ @@ -224,4 +214,4 @@ Menu.defaultProps = { transitionDuration: 'auto', }; -export default withStyles(styles, { withTheme: true, name: 'MuiMenu' })(Menu); +export default withStyles(styles, { name: 'MuiMenu', withTheme: true })(Menu); diff --git a/src/Popover/Popover.d.ts b/src/Popover/Popover.d.ts index d133e33083b04a..fbd36f9c3961d5 100644 --- a/src/Popover/Popover.d.ts +++ b/src/Popover/Popover.d.ts @@ -24,10 +24,6 @@ export interface PopoverProps anchorReference?: PopoverReference; children?: React.ReactNode; elevation?: number; - enteredClassName?: string; - enteringClassName?: string; - exitedClassName?: string; - exitingClassName?: string; getContentAnchorEl?: Function; marginThreshold?: number; modal?: boolean; @@ -35,6 +31,7 @@ export interface PopoverProps role?: string; theme?: Object; transformOrigin?: PopoverOrigin; + transition?: React.ReactType; transitionDuration?: TransitionDuration; } diff --git a/src/Popover/Popover.js b/src/Popover/Popover.js index fc1fa4f3acc9ba..ff618446fee9e2 100644 --- a/src/Popover/Popover.js +++ b/src/Popover/Popover.js @@ -260,15 +260,22 @@ class Popover extends React.Component { PaperProps, role, transformOrigin, - transitionClasses, + transition: TransitionProp, transitionDuration, action, ...other } = this.props; + const transitionProps = {}; + + // The provided transition might not support the auto timeout value. + if (TransitionProp === Grow) { + transitionProps.timeout = transitionDuration; + } + return ( - { + ref={node => { this.transitionEl = node; }} - timeout={transitionDuration} - transitionClasses={transitionClasses} + {...transitionProps} > {children} - + ); } @@ -428,17 +434,9 @@ Popover.propTypes = { vertical: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['top', 'center', 'bottom'])]), }), /** - * The animation classNames applied to the component as it enters or exits. - * This property is a direct binding to [`CSSTransition.classNames`](https://reactcommunity.org/react-transition-group/#CSSTransition-prop-classNames). + * Transition component. */ - transitionClasses: PropTypes.shape({ - appear: PropTypes.string, - appearActive: PropTypes.string, - enter: PropTypes.string, - enterActive: PropTypes.string, - exit: PropTypes.string, - exitActive: PropTypes.string, - }), + transition: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), /** * Set to 'auto' to automatically calculate transition time based on height. */ @@ -461,6 +459,7 @@ Popover.defaultProps = { vertical: 'top', horizontal: 'left', }, + transition: Grow, transitionDuration: 'auto', }; diff --git a/src/Snackbar/Snackbar.js b/src/Snackbar/Snackbar.js index 7d8ddf3926e452..729a443e7f8492 100644 --- a/src/Snackbar/Snackbar.js +++ b/src/Snackbar/Snackbar.js @@ -86,11 +86,6 @@ export const styles = theme => { }; }; -export type Origin = { - horizontal?: 'left' | 'center' | 'right' | number, - vertical?: 'top' | 'center' | 'bottom' | number, -}; - class Snackbar extends React.Component { state = { // Used to only render active snackbars. @@ -219,30 +214,11 @@ class Snackbar extends React.Component { return null; } - const transitionProps = { - in: open, - appear: true, - timeout: transitionDuration, - onEnter, - onEntering, - onEntered, - onExit, - onExiting, - onExited: createChainedFunction(this.handleExited, onExited), - }; - const transitionContent = children || ( - - ); + const transitionProps = {}; - let transition; - if (TransitionProp) { - transition = {transitionContent}; - } else { - transition = ( - - {transitionContent} - - ); + // The provided transition might not support the direction property. + if (transitionProps === Slide) { + transitionProps.direction = vertical === 'top' ? 'down' : 'up'; } return ( @@ -260,7 +236,22 @@ class Snackbar extends React.Component { onMouseLeave={this.handleMouseLeave} {...other} > - {transition} + + {children || ( + + )} +
@@ -391,6 +382,7 @@ Snackbar.defaultProps = { vertical: 'bottom', horizontal: 'center', }, + transition: Slide, transitionDuration: { enter: duration.enteringScreen, exit: duration.leavingScreen, diff --git a/src/Table/TablePagination.js b/src/Table/TablePagination.js index 558580a9553ba8..1f85a50fe51f39 100644 --- a/src/Table/TablePagination.js +++ b/src/Table/TablePagination.js @@ -235,4 +235,4 @@ TablePagination.defaultProps = { rowsPerPageOptions: [5, 10, 25], }; -export default withStyles(styles, { withTheme: true, name: 'MuiTablePagination' })(TablePagination); +export default withStyles(styles, { name: 'MuiTablePagination', withTheme: true })(TablePagination); diff --git a/src/Tabs/Tabs.js b/src/Tabs/Tabs.js index 5e34d46eaefb50..7a66d5a01ab6dd 100644 --- a/src/Tabs/Tabs.js +++ b/src/Tabs/Tabs.js @@ -445,4 +445,4 @@ Tabs.defaultProps = { textColor: 'inherit', }; -export default withStyles(styles, { withTheme: true, name: 'MuiTabs' })(Tabs); +export default withStyles(styles, { name: 'MuiTabs', withTheme: true })(Tabs);