UNPKG

d2-ui

Version:
323 lines (273 loc) 7.8 kB
import React from 'react'; import ReactDOM from 'react-dom'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import StylePropable from '../mixins/style-propable'; import Colors from '../styles/colors'; import Popover from '../popover/popover'; import CheckIcon from '../svg-icons/navigation/check'; import ListItem from '../lists/list-item'; import getMuiTheme from '../styles/getMuiTheme'; import Menu from './menu'; const nestedMenuStyle = { position: 'relative', }; const MenuItem = React.createClass({ propTypes: { /** * If true, a left check mark will be rendered. */ checked: React.PropTypes.bool, /** * Elements passed as children to inner ListItem. */ children: React.PropTypes.node, /** * Indicates if the menu should render with compact desktop styles. */ desktop: React.PropTypes.bool, /** * Disables a menu item. */ disabled: React.PropTypes.bool, /** * Prop passed down to ListItem that tells it what kind of focus it has. */ focusState: React.PropTypes.oneOf([ 'none', 'focused', 'keyboard-focused', ]), /** * Style overrides for the inner div. */ innerDivStyle: React.PropTypes.object, /** * If true, the children will be indented. * Only needed when there is no leftIcon. */ insetChildren: React.PropTypes.bool, /** * This is the SvgIcon or FontIcon to be displayed on the left side. */ leftIcon: React.PropTypes.element, /** * Nested MenuItems for this MenuItem. Used to make nested menus. */ menuItems: React.PropTypes.node, /** * Fired when the element is touchTapped. */ onTouchTap: React.PropTypes.func, /** * This is the SvgIcon or FontIcon to be displayed on the right side. */ rightIcon: React.PropTypes.element, /** * This is the block element that contains the secondary text. * If a string is passed in, a div tag will be rendered. */ secondaryText: React.PropTypes.node, /** * Override the inline-styles of the root element. */ style: React.PropTypes.object, /** * The value of the menu item. */ value: React.PropTypes.any, }, contextTypes: { muiTheme: React.PropTypes.object, }, //for passing default theme context to children childContextTypes: { muiTheme: React.PropTypes.object, }, mixins: [ PureRenderMixin, StylePropable, ], getDefaultProps() { return { checked: false, desktop: false, disabled: false, focusState: 'none', insetChildren: false, }; }, getInitialState() { return { muiTheme: this.context.muiTheme || getMuiTheme(), open: false, }; }, getChildContext() { return { muiTheme: this.state.muiTheme, }; }, componentDidMount() { this._applyFocusState(); }, //to update theme inside state whenever a new theme is passed down //from the parent / owner using context componentWillReceiveProps(nextProps, nextContext) { let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; this.setState({muiTheme: newMuiTheme}); if (this.state.open && nextProps.focusState === 'none') { this._onRequestClose(); } }, componentDidUpdate() { this._applyFocusState(); }, componentWillUnmount() { if (this.state.open) { this.setState({ open: false, }); } }, _applyFocusState() { this.refs.listItem.applyFocusState(this.props.focusState); }, _cloneMenuItem(item) { return React.cloneElement(item, { onTouchTap: (event) => { if (!item.props.menuItems) { this._onRequestClose(); } if (item.props.onTouchTap) { item.props.onTouchTap(event); } }, onRequestClose: this._onRequestClose, }); }, _onTouchTap(event) { event.preventDefault(); this.setState({ open: true, anchorEl: ReactDOM.findDOMNode(this), }); if (this.props.onTouchTap) { this.props.onTouchTap(event); } }, _onRequestClose() { this.setState({ open: false, anchorEl: null, }); }, render() { const { checked, children, desktop, disabled, focusState, innerDivStyle, insetChildren, leftIcon, menuItems, rightIcon, secondaryText, style, value, ...other, } = this.props; const disabledColor = this.state.muiTheme.rawTheme.palette.disabledColor; const textColor = this.state.muiTheme.rawTheme.palette.textColor; const leftIndent = desktop ? 64 : 72; const sidePadding = desktop ? 24 : 16; const styles = { root: { color: disabled ? disabledColor : textColor, lineHeight: desktop ? '32px' : '48px', fontSize: desktop ? 15 : 16, whiteSpace: 'nowrap', }, innerDivStyle: { paddingLeft: leftIcon || insetChildren || checked ? leftIndent : sidePadding, paddingRight: sidePadding, paddingBottom: 0, paddingTop: 0, }, secondaryText: { float: 'right', }, leftIconDesktop: { margin: 0, left: 24, top: 4, }, rightIconDesktop: { margin: 0, right: 24, top: 4, fill: Colors.grey600, }, }; let mergedRootStyles = this.mergeStyles(styles.root, style); let mergedInnerDivStyles = this.mergeStyles(styles.innerDivStyle, innerDivStyle); //Left Icon let leftIconElement = leftIcon ? leftIcon : checked ? <CheckIcon /> : null; if (leftIconElement && desktop) { const mergedLeftIconStyles = this.mergeStyles(styles.leftIconDesktop, leftIconElement.props.style); leftIconElement = React.cloneElement(leftIconElement, {style: mergedLeftIconStyles}); } //Right Icon let rightIconElement; if (rightIcon) { const mergedRightIconStyles = desktop ? this.mergeStyles(styles.rightIconDesktop, rightIcon.props.style) : rightIcon.props.style; rightIconElement = React.cloneElement(rightIcon, {style: mergedRightIconStyles}); } //Secondary Text let secondaryTextElement; if (secondaryText) { const secondaryTextIsAnElement = React.isValidElement(secondaryText); const mergedSecondaryTextStyles = secondaryTextIsAnElement ? this.mergeStyles(styles.secondaryText, secondaryText.props.style) : null; secondaryTextElement = secondaryTextIsAnElement ? React.cloneElement(secondaryText, {style: mergedSecondaryTextStyles}) : <div style={this.prepareStyles(styles.secondaryText)}>{secondaryText}</div>; } let childMenuPopover; if (menuItems) { childMenuPopover = ( <Popover anchorOrigin={{horizontal: 'right', vertical: 'top'}} anchorEl={this.state.anchorEl} open={this.state.open} useLayerForClickAway={false} onRequestClose={this._onRequestClose} > <Menu desktop={desktop} disabled={disabled} style={nestedMenuStyle}> {React.Children.map(menuItems, this._cloneMenuItem)} </Menu> </Popover> ); other.onTouchTap = this._onTouchTap; } return ( <ListItem {...other} disabled={disabled} innerDivStyle={mergedInnerDivStyles} insetChildren={insetChildren} leftIcon={leftIconElement} ref="listItem" rightIcon={rightIconElement} style={mergedRootStyles} > {children} {secondaryTextElement} {childMenuPopover} </ListItem> ); }, }); export default MenuItem;