diff --git a/packages/ra-ui-materialui/src/list/BulkActions.js b/packages/ra-ui-materialui/src/list/BulkActions.js index d6db1903602..01c2a4bbe21 100644 --- a/packages/ra-ui-materialui/src/list/BulkActions.js +++ b/packages/ra-ui-materialui/src/list/BulkActions.js @@ -1,47 +1,47 @@ import React, { cloneElement, Children, - Component, isValidElement, + useState, + useCallback, + useEffect, } from 'react'; import PropTypes from 'prop-types'; import { CSSTransition } from 'react-transition-group'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; -import { withStyles, createStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import FilterNoneIcon from '@material-ui/icons/FilterNone'; -import compose from 'recompose/compose'; import classnames from 'classnames'; -import { translate } from 'ra-core'; +import { useTranslate } from 'ra-core'; import Button from '../button/Button'; import BulkDeleteAction from './BulkDeleteAction'; -const styles = theme => - createStyles({ - bulkActionsButton: { +const useStyles = makeStyles(theme => ({ + bulkActionsButton: { + opacity: 1, + transition: theme.transitions.create('opacity', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + '&.fade-enter': { + opacity: 0, + }, + '&.fade-enter-done': { opacity: 1, - transition: theme.transitions.create('opacity', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - '&.fade-enter': { - opacity: 0, - }, - '&.fade-enter-done': { - opacity: 1, - }, - '&.fade-exit': { - opacity: 0, - }, - '&.fade-exit-done': { - opacity: 0, - }, }, - icon: { - marginRight: theme.spacing(1), + '&.fade-exit': { + opacity: 0, }, - }); + '&.fade-exit-done': { + opacity: 0, + }, + }, + icon: { + marginRight: theme.spacing(1), + }, +})); const timeoutDurations = { enter: 0, @@ -50,7 +50,6 @@ const timeoutDurations = { const sanitizeRestProps = ({ basePath, - classes, filterValues, resource, onUnselectItems, @@ -60,132 +59,122 @@ const sanitizeRestProps = ({ /** * @deprecated pass a Fragment with button children as bulkActionButtons props instead */ -class BulkActions extends Component { - state = { - isOpen: false, - activeAction: null, - }; - - componentDidMount() { +const BulkActions = ({ + basePath, + children, + className, + filterValues, + label, + resource, + selectedIds, + ...rest +}) => { + const [isOpen, setOpen] = useState(false); + const [activeAction, setActiveAction] = useState(null); + const [anchorElement, setAnchorElement] = useState(null); + const styles = useStyles({}); + const translate = useTranslate(); + + useEffect(() => { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.warn( ' is deprecated. Use the bulkActionButtons prop instead.' ); } - } - - storeButtonRef = node => { - this.anchorElement = node; - }; - - handleClick = () => { - this.setState({ isOpen: true }); - }; - - handleClose = () => { - this.setState({ isOpen: false }); - }; - - handleLaunchAction = action => { - this.setState({ activeAction: action, isOpen: false }); - }; - - handleExitAction = () => { - this.setState({ activeAction: null }); - }; - - render() { - const { - basePath, - classes, - children, - className, - filterValues, - label, - resource, - selectedIds, - translate, - ...rest - } = this.props; - const { isOpen } = this.state; + }); - return ( - 0} - timeout={timeoutDurations} - mountOnEnter - unmountOnExit - classNames="fade" - > -
- - - {Children.map(children, (child, index) => - isValidElement(child) ? ( - - this.handleLaunchAction(index) - } - {...sanitizeRestProps(rest)} - > - {translate(child.props.label)} - - ) : null - )} - + const storeButtonRef = useCallback(node => { + setAnchorElement(node); + }, []); + + const handleClick = useCallback(() => { + setOpen(true); + }, []); + + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + const handleLaunchAction = useCallback(action => { + setActiveAction(action); + setOpen(false); + }, []); + + const handleExitAction = useCallback(() => { + setActiveAction(null); + }, []); + + return ( + 0} + timeout={timeoutDurations} + mountOnEnter + unmountOnExit + classNames="fade" + > +
+ + {Children.map(children, (child, index) => - isValidElement(child) && - this.state.activeAction === index - ? cloneElement(child, { - basePath, - filterValues, - onExit: this.handleExitAction, - resource, - selectedIds, - }) - : null + isValidElement(child) ? ( + handleLaunchAction(index)} + {...sanitizeRestProps(rest)} + > + {translate(child.props.label)} + + ) : null )} -
-
- ); - } -} + + {Children.map(children, (child, index) => + isValidElement(child) && activeAction === index + ? cloneElement(child, { + basePath, + filterValues, + onExit: handleExitAction, + resource, + selectedIds, + }) + : null + )} +
+
+ ); +}; BulkActions.propTypes = { basePath: PropTypes.string, - classes: PropTypes.object, className: PropTypes.string, children: PropTypes.node, filterValues: PropTypes.object, // eslint-disable-line react/forbid-prop-types label: PropTypes.string, resource: PropTypes.string, selectedIds: PropTypes.arrayOf(PropTypes.any), - translate: PropTypes.func.isRequired, }; BulkActions.defaultProps = { @@ -194,9 +183,4 @@ BulkActions.defaultProps = { selectedIds: [], }; -const EnhancedButton = compose( - withStyles(styles), - translate -)(BulkActions); - -export default EnhancedButton; +export default BulkActions;