material-ui
Version:
Material Design UI components built with React
359 lines (318 loc) • 11.8 kB
JavaScript
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var React = require('react');
var ReactDOM = require('react-dom');
var StylePropable = require('./mixins/style-propable');
var Transitions = require('./styles/transitions');
var KeyCode = require('./utils/key-code');
var DropDownArrow = require('./svg-icons/navigation/arrow-drop-down');
var Paper = require('./paper');
var Menu = require('./menu/menu');
var ClearFix = require('./clearfix');
var DefaultRawTheme = require('./styles/raw-themes/light-raw-theme');
var ThemeManager = require('./styles/theme-manager');
var DropDownMenu = React.createClass({
displayName: 'DropDownMenu',
mixins: [StylePropable],
contextTypes: {
muiTheme: React.PropTypes.object
},
//for passing default theme context to children
childContextTypes: {
muiTheme: React.PropTypes.object
},
getChildContext: function getChildContext() {
return {
muiTheme: this.state.muiTheme
};
},
// The nested styles for drop-down-menu are modified by toolbar and possibly
// other user components, so it will give full access to its js styles rather
// than just the parent.
propTypes: {
className: React.PropTypes.string,
displayMember: React.PropTypes.string,
valueMember: React.PropTypes.string,
autoWidth: React.PropTypes.bool,
disabled: React.PropTypes.bool,
onChange: React.PropTypes.func,
menuItems: React.PropTypes.array.isRequired,
menuItemStyle: React.PropTypes.object,
underlineStyle: React.PropTypes.object,
iconStyle: React.PropTypes.object,
labelStyle: React.PropTypes.object,
selectedIndex: React.PropTypes.number
},
getDefaultProps: function getDefaultProps() {
return {
autoWidth: true,
disabled: false,
valueMember: 'payload',
displayMember: 'text'
};
},
getInitialState: function getInitialState() {
return {
open: false,
selectedIndex: this._isControlled() ? null : this.props.selectedIndex || 0,
muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme)
};
},
componentDidMount: function componentDidMount() {
if (this.props.autoWidth) this._setWidth();
if (this.props.hasOwnProperty('selectedIndex')) this._setSelectedIndex(this.props);
},
componentWillReceiveProps: function componentWillReceiveProps(nextProps, nextContext) {
var newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
this.setState({ muiTheme: newMuiTheme });
if (this.props.autoWidth) this._setWidth();
if (nextProps.hasOwnProperty('value') || nextProps.hasOwnProperty('valueLink')) {
return;
} else if (nextProps.hasOwnProperty('selectedIndex')) {
this._setSelectedIndex(nextProps);
}
},
getStyles: function getStyles() {
var disabled = this.props.disabled;
var zIndex = 5; // As AppBar
var spacing = this.state.muiTheme.rawTheme.spacing;
var accentColor = this.state.muiTheme.dropDownMenu.accentColor;
var backgroundColor = this.state.muiTheme.menu.backgroundColor;
var styles = {
root: {
transition: Transitions.easeOut(),
position: 'relative',
display: 'inline-block',
height: spacing.desktopSubheaderHeight,
fontSize: spacing.desktopDropDownMenuFontSize,
outline: 'none'
},
control: {
cursor: disabled ? 'not-allowed' : 'pointer',
position: 'static',
height: '100%'
},
controlBg: {
transition: Transitions.easeOut(),
backgroundColor: backgroundColor,
height: '100%',
width: '100%',
opacity: 0
},
icon: {
position: 'absolute',
top: (spacing.desktopToolbarHeight - 24) / 2,
right: spacing.desktopGutterLess,
fill: this.state.muiTheme.dropDownMenu.accentColor
},
label: {
transition: Transitions.easeOut(),
lineHeight: spacing.desktopToolbarHeight + 'px',
position: 'absolute',
paddingLeft: spacing.desktopGutter,
top: 0,
opacity: 1,
color: disabled ? this.state.muiTheme.rawTheme.palette.disabledColor : this.state.muiTheme.rawTheme.palette.textColor
},
underline: {
borderTop: 'solid 1px ' + accentColor,
margin: '-1px ' + spacing.desktopGutter + 'px'
},
menu: {
zIndex: zIndex + 1
},
menuItem: {
paddingRight: spacing.iconSize + spacing.desktopGutterLess + spacing.desktopGutterMini,
height: spacing.desktopDropDownMenuItemHeight,
lineHeight: spacing.desktopDropDownMenuItemHeight + 'px',
whiteSpace: 'nowrap'
},
rootWhenOpen: {
opacity: 1
},
labelWhenOpen: {
opacity: 0,
top: spacing.desktopToolbarHeight / 2
},
overlay: {
height: '100%',
width: '100%',
position: 'fixed',
top: 0,
left: 0,
zIndex: zIndex
}
};
return styles;
},
getInputNode: function getInputNode() {
var root = this.refs.root;
var item = this.props.menuItems[this.state.selectedIndex];
if (item) {
root.value = item[this.props.displayMember];
}
return root;
},
render: function render() {
var _props = this.props;
var autoWidth = _props.autoWidth;
var className = _props.className;
var onFocus = _props.onFocus;
var onBlur = _props.onBlur;
var style = _props.style;
var displayMember = _props.displayMember;
var valueMember = _props.valueMember;
var valueLink = _props.valueLink;
var labelStyle = _props.labelStyle;
var iconStyle = _props.iconStyle;
var underlineStyle = _props.underlineStyle;
var menuItemStyle = _props.menuItemStyle;
var other = _objectWithoutProperties(_props, ['autoWidth', 'className', 'onFocus', 'onBlur', 'style', 'displayMember', 'valueMember', 'valueLink', 'labelStyle', 'iconStyle', 'underlineStyle', 'menuItemStyle']);
var styles = this.getStyles();
var selectedIndex = this._isControlled() ? null : this.state.selectedIndex;
var displayValue = "";
if (selectedIndex) {
if (process.env.NODE_ENV !== 'production') {
console.assert(!!this.props.menuItems[selectedIndex], 'SelectedIndex of ' + selectedIndex + ' does not exist in menuItems.');
}
} else if (valueMember && this._isControlled()) {
var value = this.props.hasOwnProperty('value') ? this.props.value : valueLink.value;
if (value !== null && value !== undefined) {
for (var i = 0; i < this.props.menuItems.length; i++) {
if (this.props.menuItems[i][valueMember] === value) {
selectedIndex = i;
}
}
}
}
var selectedItem = this.props.menuItems[selectedIndex];
if (selectedItem) {
displayValue = selectedItem[displayMember];
}
var menuItems = this.props.menuItems.map(function (item) {
item.text = item[displayMember];
item.payload = item[valueMember];
return item;
});
return React.createElement(
'div',
_extends({}, other, {
ref: 'root',
onKeyDown: this._onKeyDown,
onFocus: onFocus,
onBlur: onBlur,
className: className,
style: this.prepareStyles(styles.root, this.state.open && styles.rootWhenOpen, style) }),
React.createElement(
ClearFix,
{ style: this.mergeStyles(styles.control), onTouchTap: this._onControlClick },
React.createElement(Paper, { style: this.mergeStyles(styles.controlBg), zDepth: 0 }),
React.createElement(
'div',
{ style: this.prepareStyles(styles.label, this.state.open && styles.labelWhenOpen, labelStyle) },
displayValue
),
React.createElement(DropDownArrow, { style: this.mergeStyles(styles.icon, iconStyle) }),
React.createElement('div', { style: this.prepareStyles(styles.underline, underlineStyle) })
),
React.createElement(Menu, {
ref: 'menuItems',
autoWidth: autoWidth,
selectedIndex: selectedIndex,
menuItems: menuItems,
style: styles.menu,
menuItemStyle: this.mergeStyles(styles.menuItem, menuItemStyle),
hideable: true,
visible: this.state.open,
onRequestClose: this._onMenuRequestClose,
onItemTap: this._onMenuItemClick }),
this.state.open && React.createElement('div', { style: this.prepareStyles(styles.overlay), onTouchTap: this._handleOverlayTouchTap })
);
},
_setWidth: function _setWidth() {
var el = ReactDOM.findDOMNode(this);
var menuItemsDom = ReactDOM.findDOMNode(this.refs.menuItems);
if (!this.props.style || !this.props.style.hasOwnProperty('width')) {
el.style.width = 'auto';
el.style.width = menuItemsDom.offsetWidth + 'px';
}
},
_setSelectedIndex: function _setSelectedIndex(props) {
var selectedIndex = props.selectedIndex;
if (process.env.NODE_ENV !== 'production' && selectedIndex < 0) {
console.warn('Cannot set selectedIndex to a negative index.', selectedIndex);
}
this.setState({ selectedIndex: selectedIndex > -1 ? selectedIndex : 0 });
},
_onControlClick: function _onControlClick() {
if (!this.props.disabled) {
this.setState({ open: !this.state.open });
}
},
_onKeyDown: function _onKeyDown(e) {
switch (e.which) {
case KeyCode.UP:
if (!this.state.open) {
this._selectPreviousItem();
} else {
if (e.altKey) {
this.setState({ open: false });
}
}
break;
case KeyCode.DOWN:
if (!this.state.open) {
if (e.altKey) {
this.setState({ open: true });
} else {
this._selectNextItem();
}
}
break;
case KeyCode.ENTER:
case KeyCode.SPACE:
this.setState({ open: true });
break;
default:
return; //important
}
e.preventDefault();
},
_onMenuItemClick: function _onMenuItemClick(e, key, payload) {
if (this.props.onChange && this.state.selectedIndex !== key) {
var selectedItem = this.props.menuItems[key];
if (selectedItem) {
e.target.value = selectedItem[this.props.valueMember];
}
if (this.props.valueLink) {
this.props.valueLink.requestChange(e.target.value);
} else {
this.props.onChange(e, key, payload);
}
}
this.setState({
selectedIndex: key,
value: e.target.value,
open: false
});
},
_onMenuRequestClose: function _onMenuRequestClose() {
this.setState({ open: false });
},
_selectPreviousItem: function _selectPreviousItem() {
this.setState({ selectedIndex: Math.max(this.state.selectedIndex - 1, 0) });
},
_selectNextItem: function _selectNextItem() {
this.setState({ selectedIndex: Math.min(this.state.selectedIndex + 1, this.props.menuItems.length - 1) });
},
_handleOverlayTouchTap: function _handleOverlayTouchTap() {
this.setState({
open: false
});
},
_isControlled: function _isControlled() {
return this.props.hasOwnProperty('value') || this.props.hasOwnProperty('valueLink');
}
});
module.exports = DropDownMenu;