Files
flood/client/source/scripts/components/ui/ContextMenu.js
2016-06-17 22:03:57 -07:00

194 lines
4.5 KiB
JavaScript

import classnames from 'classnames';
import React from 'react';
import ReactDOM from 'react-dom';
import UIActions from '../../actions/UIActions';
export default class ContextMenu extends React.Component {
constructor() {
super();
this.state = {
menuPosition: {},
isMenuPositionIdeal: false
};
}
componentDidMount() {
document.addEventListener('click', UIActions.dismissContextMenu);
if (this.props.onMenuOpen) {
this.props.onMenuOpen();
}
this.checkMenuPosition();
}
componentDidUpdate() {
this.checkMenuPosition();
}
componentWillUnmount() {
document.removeEventListener('click', UIActions.dismissContextMenu);
if (this.props.onMenuClose) {
this.props.onMenuClose();
}
}
componentWillUpdate(nextProps) {
if (nextProps.clickPosition.x !== this.props.clickPosition.x
|| nextProps.clickPosition.y !== this.props.clickPosition.y) {
this.setState({
isMenuPositionIdeal: false,
menuPosition: this.getMenuPosition()
});
}
}
checkMenuPosition() {
if (!this.state.isMenuPositionIdeal) {
this.setState({
isMenuPositionIdeal: true,
menuPosition: this.getMenuPosition()
});
}
}
getMenuPosition() {
let menuBorderBox = this.getRenderedMenuBorderBox();
let viewportDimensions = this.getViewportDimensions();
let menuPosition = {};
if (menuBorderBox.left + menuBorderBox.width > viewportDimensions.width) {
menuPosition.right = viewportDimensions.width - this.props.clickPosition.x;
} else {
menuPosition.left = menuBorderBox.left;
}
if (menuBorderBox.height + this.props.clickPosition.y > viewportDimensions.height) {
menuPosition.bottom = viewportDimensions.height - this.props.clickPosition.y;
} else {
menuPosition.top = menuBorderBox.top;
}
return menuPosition;
}
getViewportDimensions() {
let height = global.window.innerHeight;
let width = global.window.innerWidth;
return {height, width};
}
getRenderedMenuBorderBox() {
let menuDOMNode = ReactDOM.findDOMNode(this);
if (menuDOMNode) {
return menuDOMNode.getBoundingClientRect();
}
return null;
}
getMenuItems(items) {
return items.map((item, index) => {
let labelAction, labelSecondary, menuItemContent;
let menuItemClasses = classnames('menu__item', {
'is-selectable': item.clickHandler,
'menu__item--separator': item.type === 'separator'
});
let primaryLabelClasses = classnames('menu__item__label--primary', {
'has-action': item.labelAction
});
if (item.labelSecondary) {
labelSecondary = (
<span className="menu__item__label--secondary">
{item.labelSecondary}
</span>
);
}
if (item.labelAction) {
labelAction = (
<span className="menu__item__label__action">
{item.labelAction}
</span>
);
}
if (item.type !== 'separator') {
menuItemContent = (
<span>
<span className={primaryLabelClasses}>
<span className="menu__item__label">
{item.label}
</span>
{labelAction}
</span>
{labelSecondary}
</span>
);
}
return (
<li className={menuItemClasses} key={index} onClick={this.handleMenuItemClick.bind(this, item)}>
{menuItemContent}
</li>
);
});
}
handleMenuItemClick(item, event) {
if (item.dismissMenu === false) {
event.nativeEvent.stopImmediatePropagation();
}
if (item.clickHandler) {
item.clickHandler(item.action, event);
}
return false;
}
render() {
let className = 'context-menu menu';
let menuPosition = {
left: this.props.clickPosition.x,
top: this.props.clickPosition.y
};
let visibility = 'hidden';
if (this.state.isMenuPositionIdeal) {
visibility = 'visible';
menuPosition = this.state.menuPosition;
}
let styles = {
width: `${this.props.width}px`,
...menuPosition,
visibility
};
return (
<div className={className} style={styles}>
{this.getMenuItems(this.props.items)}
</div>
);
}
}
ContextMenu.defaultProps = {
width: 200
};
ContextMenu.propTypes = {
clickPosition: React.PropTypes.object,
onMenuClose: React.PropTypes.func,
onMenuOpen: React.PropTypes.func,
items: React.PropTypes.array,
width: React.PropTypes.number
};