157 | {this.props.toggle}
158 |
159 | {this.props.isOpen &&
160 |
161 | }
162 |
163 |
164 | );
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/js/NestedDropdownMenu.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import ReactDOM from 'react-dom';
4 | import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
5 | import classnames from 'classnames';
6 |
7 | export default class NestedDropdownMenu extends PureComponent {
8 | constructor(props) {
9 | super(props);
10 |
11 | this.toggleComponent = null;
12 | this.closeCallback = null;
13 | this.state = {
14 | isHoverOpen: false,
15 | isClickOpen: false,
16 | };
17 | }
18 |
19 | static propTypes = {
20 | toggle: PropTypes.node.isRequired,
21 | children: PropTypes.node,
22 | nested: PropTypes.oneOf(['inherit', 'reverse', 'left', 'right']),
23 | animate: PropTypes.bool,
24 | direction: PropTypes.oneOf(['left', 'right']),
25 | upwards: PropTypes.bool,
26 | delay: PropTypes.number,
27 | enterTimeout: PropTypes.number,
28 | leaveTimeout: PropTypes.number,
29 | openOnMouseover: PropTypes.bool,
30 | };
31 |
32 | static defaultProps = {
33 | nested: 'reverse',
34 | animate: false,
35 | direction: 'right',
36 | upwards: false,
37 | delay: 500,
38 | enterTimeout: 150,
39 | leaveTimeout: 150,
40 | openOnMouseover: true,
41 | };
42 |
43 | componentDidMount() {
44 | this.toggleComponent = ReactDOM.findDOMNode(this).querySelector('*');
45 | this.toggleComponent.addEventListener('click', this.handleToggleComponentClick);
46 | }
47 |
48 | componentWillUnmount() {
49 | this.closeCallback && clearTimeout(this.closeCallback);
50 | this.toggleComponent.removeEventListener('click', this.handleToggleComponentClick);
51 | }
52 |
53 | handleToggleComponentClick = (e) => {
54 | e.stopPropagation();
55 | this.setState({ isClickOpen: !this.state.isClickOpen });
56 | };
57 |
58 | handleMouseOver = () => {
59 | if(this.closeCallback) {
60 | clearTimeout(this.closeCallback);
61 | this.closeCallback = null;
62 | }
63 | this.setState({ isHoverOpen: true });
64 | };
65 |
66 | handleMouseLeave = () => {
67 | this.closeCallback = setTimeout(() => {
68 | this.setState({ isHoverOpen: false });
69 | }, this.props.delay);
70 | };
71 |
72 | render() {
73 | const { toggle, children, nested, animate, direction, upwards, enterTimeout, leaveTimeout } = this.props;
74 | const isOpen = this.state.isHoverOpen || this.state.isClickOpen;
75 |
76 | let itemProps = {
77 | className: classnames('nested-dd-menu', `nested-${nested}`),
78 | };
79 | if(this.props.openOnMouseover) {
80 | itemProps.onMouseOver = this.handleMouseOver;
81 | itemProps.onMouseLeave = this.handleMouseLeave;
82 | }
83 |
84 | const prefix = upwards ? 'up-' : '';
85 | const transitionProps = {
86 | className: 'dd-item-ignore',
87 | transitionEnter: animate,
88 | transitionLeave: animate,
89 | transitionName: `grow-from-${prefix}${direction}`,
90 | transitionEnterTimeout: enterTimeout,
91 | transitionLeaveTimeout: leaveTimeout,
92 | };
93 |
94 | return (
95 |