UNPKG

e-lado

Version:

[![CircleCI](https://circleci.com/gh/sharetribe/sharetribe/tree/master.svg?style=svg)](https://circleci.com/gh/sharetribe/sharetribe/tree/master) [![Dependency Status](https://gemnasium.com/sharetribe/sharetribe.png)](https://gemnasium.com/sharetribe/shar

134 lines (117 loc) 4.21 kB
import { Component, PropTypes } from 'react'; import r, { div } from 'r-dom'; import classNames from 'classnames'; import { className } from '../../../utils/PropTypes'; import MenuLabel from './MenuLabel'; import MenuLabelDropdown from './MenuLabelDropdown'; import MenuContent from './MenuContent'; import css from './Menu.css'; const INITIAL_ARROW_POSITION = 25; const HOVER_TIMEOUT = 250; const MENULABEL_MAP = { menu: MenuLabel, dropdown: MenuLabelDropdown, }; class Menu extends Component { constructor(props, context) { super(props, context); this.handleMouseover = this.handleMouseover.bind(this); this.handleMouseout = this.handleMouseout.bind(this); this.handleClick = this.handleClick.bind(this); this.handleBlur = this.handleBlur.bind(this); this.calculateDropdownPosition = this.calculateDropdownPosition.bind(this); this.state = { isOpen: false, isMounted: false, arrowPosition: INITIAL_ARROW_POSITION, }; this.mouseOverTimout = null; this.mouseOutTimout = null; } componentDidMount() { this.calculateDropdownPosition(); this.setState({ isMounted: true }); // eslint-disable-line react/no-set-state } calculateDropdownPosition() { this.setState({ // eslint-disable-line react/no-did-mount-set-state, react/no-set-state arrowPosition: this.menuLabel.offsetWidth > (INITIAL_ARROW_POSITION * 2) ? Math.floor(this.menuLabel.offsetWidth / 2) : INITIAL_ARROW_POSITION, // eslint-disable-line no-magic-numbers }); } handleMouseover() { window.clearTimeout(this.mouseOutTimout); window.clearTimeout(this.mouseOverTimout); this.mouseOverTimout = setTimeout(() => ( this.setState({ isOpen: true }) // eslint-disable-line react/no-set-state ), HOVER_TIMEOUT); } handleMouseout() { window.clearTimeout(this.mouseOverTimout); this.mouseOutTimout = setTimeout(() => ( this.setState({ isOpen: false }) // eslint-disable-line react/no-set-state ), HOVER_TIMEOUT); } handleClick() { this.setState({ isOpen: !this.state.isOpen }); // eslint-disable-line react/no-set-state } handleBlur(event) { // FocusEvent is fired faster than the link elements native click handler // gets its own event. Therefore, we need to check the origin of this FocusEvent. if (!this.menu.contains(event.relatedTarget)) { this.setState({ isOpen: false });// eslint-disable-line react/no-set-state } } render() { const requestedLabel = MENULABEL_MAP[this.props.menuLabelType]; const LabelComponent = requestedLabel != null ? requestedLabel : null; const openOnHoverClass = this.state.isMounted ? '' : css.openOnHover; const transitionDelayClass = this.state.isMounted ? '' : css.transitionDelay; const openClass = this.state.isOpen ? css.openMenu : ''; return div({ className: classNames(this.props.className, 'Menu', css.menu, openOnHoverClass, openClass), onMouseOver: this.handleMouseover, onMouseOut: this.handleMouseout, onClick: this.handleClick, onBlur: this.handleBlur, tabIndex: 0, ref: (c) => { this.menu = c; }, }, [ r(LabelComponent, { key: `${this.props.identifier}_menulabel`, name: this.props.name, extraClasses: this.props.extraClassesLabel, menuLabelRef: (c) => { this.menuLabel = c; }, } ), r(MenuContent, { className: transitionDelayClass, key: `${this.props.identifier}_menucontent`, content: this.props.content, arrowPosition: this.state.arrowPosition, } ), ]); } } Menu.propTypes = { name: PropTypes.string.isRequired, extraClassesLabel: PropTypes.string, identifier: PropTypes.string.isRequired, menuLabelType: PropTypes.string, content: PropTypes.arrayOf( PropTypes.shape({ active: PropTypes.bool.isRequired, activeColor: PropTypes.string.isRequired, content: PropTypes.string.isRequired, href: PropTypes.string.isRequired, type: PropTypes.string.isRequired, }) ).isRequired, className, }; export default Menu;