UNPKG

material-ui

Version:

Material Design UI components built with React

480 lines (408 loc) 17 kB
'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 PureRenderMixin = require('react-addons-pure-render-mixin'); var ColorManipulator = require('../utils/color-manipulator'); var StylePropable = require('../mixins/style-propable'); var Colors = require('../styles/colors'); var Transitions = require('../styles/transitions'); var Typography = require('../styles/typography'); var EnhancedButton = require('../enhanced-button'); var IconButton = require('../icon-button'); var OpenIcon = require('../svg-icons/navigation/arrow-drop-up'); var CloseIcon = require('../svg-icons/navigation/arrow-drop-down'); var NestedList = require('./nested-list'); var DefaultRawTheme = require('../styles/raw-themes/light-raw-theme'); var ThemeManager = require('../styles/theme-manager'); var ListItem = React.createClass({ displayName: 'ListItem', mixins: [PureRenderMixin, StylePropable], contextTypes: { muiTheme: React.PropTypes.object }, propTypes: { autoGenerateNestedIndicator: React.PropTypes.bool, disabled: React.PropTypes.bool, disableKeyboardFocus: React.PropTypes.bool, initiallyOpen: React.PropTypes.bool, innerDivStyle: React.PropTypes.object, insetChildren: React.PropTypes.bool, innerStyle: React.PropTypes.object, leftAvatar: React.PropTypes.element, leftCheckbox: React.PropTypes.element, leftIcon: React.PropTypes.element, nestedLevel: React.PropTypes.number, nestedItems: React.PropTypes.arrayOf(React.PropTypes.element), onKeyboardFocus: React.PropTypes.func, onMouseEnter: React.PropTypes.func, onMouseLeave: React.PropTypes.func, onNestedListToggle: React.PropTypes.func, onTouchStart: React.PropTypes.func, onTouchTap: React.PropTypes.func, rightAvatar: React.PropTypes.element, rightIcon: React.PropTypes.element, rightIconButton: React.PropTypes.element, rightToggle: React.PropTypes.element, primaryText: React.PropTypes.node, secondaryText: React.PropTypes.node, secondaryTextLines: React.PropTypes.oneOf([1, 2]) }, //for passing default theme context to children childContextTypes: { muiTheme: React.PropTypes.object }, getChildContext: function getChildContext() { return { muiTheme: this.state.muiTheme }; }, getDefaultProps: function getDefaultProps() { return { autoGenerateNestedIndicator: true, initiallyOpen: false, nestedItems: [], nestedLevel: 0, onKeyboardFocus: function onKeyboardFocus() {}, onMouseEnter: function onMouseEnter() {}, onMouseLeave: function onMouseLeave() {}, onNestedListToggle: function onNestedListToggle() {}, onTouchStart: function onTouchStart() {}, secondaryTextLines: 1 }; }, getInitialState: function getInitialState() { return { hovered: false, isKeyboardFocused: false, open: this.props.initiallyOpen, rightIconButtonHovered: false, rightIconButtonKeyboardFocused: false, touch: false, muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme) }; }, //to update theme inside state whenever a new theme is passed down //from the parent / owner using context componentWillReceiveProps: function componentWillReceiveProps(nextProps, nextContext) { var newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; this.setState({ muiTheme: newMuiTheme }); }, render: function render() { var _props = this.props; var autoGenerateNestedIndicator = _props.autoGenerateNestedIndicator; var children = _props.children; var disabled = _props.disabled; var disableKeyboardFocus = _props.disableKeyboardFocus; var innerDivStyle = _props.innerDivStyle; var insetChildren = _props.insetChildren; var leftAvatar = _props.leftAvatar; var leftCheckbox = _props.leftCheckbox; var leftIcon = _props.leftIcon; var nestedItems = _props.nestedItems; var nestedLevel = _props.nestedLevel; var onKeyboardFocus = _props.onKeyboardFocus; var onMouseLeave = _props.onMouseLeave; var onMouseEnter = _props.onMouseEnter; var onTouchStart = _props.onTouchStart; var onTouchTap = _props.onTouchTap; var rightAvatar = _props.rightAvatar; var rightIcon = _props.rightIcon; var rightIconButton = _props.rightIconButton; var rightToggle = _props.rightToggle; var primaryText = _props.primaryText; var secondaryText = _props.secondaryText; var secondaryTextLines = _props.secondaryTextLines; var style = _props.style; var other = _objectWithoutProperties(_props, ['autoGenerateNestedIndicator', 'children', 'disabled', 'disableKeyboardFocus', 'innerDivStyle', 'insetChildren', 'leftAvatar', 'leftCheckbox', 'leftIcon', 'nestedItems', 'nestedLevel', 'onKeyboardFocus', 'onMouseLeave', 'onMouseEnter', 'onTouchStart', 'onTouchTap', 'rightAvatar', 'rightIcon', 'rightIconButton', 'rightToggle', 'primaryText', 'secondaryText', 'secondaryTextLines', 'style']); var textColor = this.state.muiTheme.rawTheme.palette.textColor; var hoverColor = ColorManipulator.fade(textColor, 0.1); var singleAvatar = !secondaryText && (leftAvatar || rightAvatar); var singleNoAvatar = !secondaryText && !(leftAvatar || rightAvatar); var twoLine = secondaryText && secondaryTextLines === 1; var threeLine = secondaryText && secondaryTextLines > 1; var hasCheckbox = leftCheckbox || rightToggle; var styles = { root: { backgroundColor: (this.state.isKeyboardFocused || this.state.hovered) && !this.state.rightIconButtonHovered && !this.state.rightIconButtonKeyboardFocused ? hoverColor : null, color: textColor, display: 'block', fontSize: 16, lineHeight: '16px', position: 'relative', transition: Transitions.easeOut() }, //This inner div is needed so that ripples will span the entire container innerDiv: { marginLeft: nestedLevel * this.state.muiTheme.listItem.nestedLevelDepth, paddingLeft: leftIcon || leftAvatar || leftCheckbox || insetChildren ? 72 : 16, paddingRight: rightIcon || rightAvatar || rightIconButton ? 56 : rightToggle ? 72 : 16, paddingBottom: singleAvatar ? 20 : 16, paddingTop: singleNoAvatar || threeLine ? 16 : 20, position: 'relative' }, icons: { height: 24, width: 24, display: 'block', position: 'absolute', top: twoLine ? 12 : singleAvatar ? 4 : 0, padding: 12 }, leftIcon: { color: Colors.grey600, fill: Colors.grey600, left: 4 }, rightIcon: { color: Colors.grey400, fill: Colors.grey400, right: 4 }, avatars: { position: 'absolute', top: singleAvatar ? 8 : 16 }, label: { cursor: 'pointer' }, leftAvatar: { left: 16 }, rightAvatar: { right: 16 }, leftCheckbox: { position: 'absolute', display: 'block', width: 24, top: twoLine ? 24 : singleAvatar ? 16 : 12, left: 16 }, primaryText: {}, rightIconButton: { position: 'absolute', display: 'block', top: twoLine ? 12 : singleAvatar ? 4 : 0, right: 4 }, rightToggle: { position: 'absolute', display: 'block', width: 54, top: twoLine ? 25 : singleAvatar ? 17 : 13, right: 8 }, secondaryText: { fontSize: 14, lineHeight: threeLine ? '18px' : '16px', height: threeLine ? 36 : 16, margin: 0, marginTop: 4, color: Typography.textLightBlack, //needed for 2 and 3 line ellipsis overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: threeLine ? null : 'nowrap', display: threeLine ? '-webkit-box' : null, WebkitLineClamp: threeLine ? 2 : null, WebkitBoxOrient: threeLine ? 'vertical' : null } }; var contentChildren = [children]; if (leftIcon) { this._pushElement(contentChildren, leftIcon, this.mergeStyles(styles.icons, styles.leftIcon)); } if (rightIcon) { this._pushElement(contentChildren, rightIcon, this.mergeStyles(styles.icons, styles.rightIcon)); } if (leftAvatar) { this._pushElement(contentChildren, leftAvatar, this.mergeStyles(styles.avatars, styles.leftAvatar)); } if (rightAvatar) { this._pushElement(contentChildren, rightAvatar, this.mergeStyles(styles.avatars, styles.rightAvatar)); } if (leftCheckbox) { this._pushElement(contentChildren, leftCheckbox, this.mergeStyles(styles.leftCheckbox)); } //RightIconButtonElement var hasNestListItems = nestedItems.length; var hasRightElement = rightAvatar || rightIcon || rightIconButton || rightToggle; var needsNestedIndicator = hasNestListItems && autoGenerateNestedIndicator && !hasRightElement; if (rightIconButton || needsNestedIndicator) { var rightIconButtonElement = rightIconButton; var rightIconButtonHandlers = { onKeyboardFocus: this._handleRightIconButtonKeyboardFocus, onMouseEnter: this._handleRightIconButtonMouseEnter, onMouseLeave: this._handleRightIconButtonMouseLeave, onTouchTap: this._handleRightIconButtonTouchTap, onMouseDown: this._handleRightIconButtonMouseUp, onMouseUp: this._handleRightIconButtonMouseUp }; // Create a nested list indicator icon if we don't have an icon on the right if (needsNestedIndicator) { rightIconButtonElement = this.state.open ? React.createElement( IconButton, null, React.createElement(OpenIcon, null) ) : React.createElement( IconButton, null, React.createElement(CloseIcon, null) ); rightIconButtonHandlers.onTouchTap = this._handleNestedListToggle; } this._pushElement(contentChildren, rightIconButtonElement, this.mergeStyles(styles.rightIconButton), rightIconButtonHandlers); } if (rightToggle) { this._pushElement(contentChildren, rightToggle, this.mergeStyles(styles.rightToggle)); } if (primaryText) { var secondaryTextElement = this._createTextElement(styles.primaryText, primaryText, 'primaryText'); contentChildren.push(secondaryTextElement); } if (secondaryText) { var secondaryTextElement = this._createTextElement(styles.secondaryText, secondaryText, 'secondaryText'); contentChildren.push(secondaryTextElement); } var nestedList = nestedItems.length ? React.createElement( NestedList, { nestedLevel: nestedLevel + 1, open: this.state.open }, nestedItems ) : undefined; return hasCheckbox ? this._createLabelElement(styles, contentChildren) : disabled ? this._createDisabledElement(styles, contentChildren) : React.createElement( 'div', null, React.createElement( EnhancedButton, _extends({}, other, { disabled: disabled, disableKeyboardFocus: disableKeyboardFocus || this.state.rightIconButtonKeyboardFocused, linkButton: true, onKeyboardFocus: this._handleKeyboardFocus, onMouseLeave: this._handleMouseLeave, onMouseEnter: this._handleMouseEnter, onTouchStart: this._handleTouchStart, onTouchTap: onTouchTap, ref: 'enhancedButton', style: this.mergeStyles(styles.root, style) }), React.createElement( 'div', { style: this.prepareStyles(styles.innerDiv, innerDivStyle) }, contentChildren ) ), nestedList ); }, applyFocusState: function applyFocusState(focusState) { var button = this.refs.enhancedButton; var buttonEl = ReactDOM.findDOMNode(button); if (button) { switch (focusState) { case 'none': buttonEl.blur(); break; case 'focused': buttonEl.focus(); break; case 'keyboard-focused': button.setKeyboardFocus(); buttonEl.focus(); break; } } }, _createDisabledElement: function _createDisabledElement(styles, contentChildren) { var _props2 = this.props; var innerDivStyle = _props2.innerDivStyle; var style = _props2.style; var mergedDivStyles = this.prepareStyles(styles.root, styles.innerDiv, innerDivStyle, style); return React.createElement('div', { style: mergedDivStyles }, contentChildren); }, _createLabelElement: function _createLabelElement(styles, contentChildren) { var _props3 = this.props; var innerDivStyle = _props3.innerDivStyle; var style = _props3.style; var mergedLabelStyles = this.prepareStyles(styles.root, styles.innerDiv, innerDivStyle, styles.label, style); return React.createElement('label', { style: mergedLabelStyles }, contentChildren); }, _createTextElement: function _createTextElement(styles, data, key) { var isAnElement = React.isValidElement(data); var mergedStyles = isAnElement ? this.prepareStyles(styles, data.props.style) : null; return isAnElement ? React.cloneElement(data, { key: key, style: mergedStyles }) : React.createElement( 'div', { key: key, style: this.prepareStyles(styles) }, data ); }, _handleKeyboardFocus: function _handleKeyboardFocus(e, isKeyboardFocused) { this.setState({ isKeyboardFocused: isKeyboardFocused }); this.props.onKeyboardFocus(e, isKeyboardFocused); }, _handleMouseEnter: function _handleMouseEnter(e) { if (!this.state.touch) this.setState({ hovered: true }); this.props.onMouseEnter(e); }, _handleMouseLeave: function _handleMouseLeave(e) { this.setState({ hovered: false }); this.props.onMouseLeave(e); }, _handleNestedListToggle: function _handleNestedListToggle(e) { e.stopPropagation(); this.setState({ open: !this.state.open }); this.props.onNestedListToggle(this); }, _handleRightIconButtonKeyboardFocus: function _handleRightIconButtonKeyboardFocus(e, isKeyboardFocused) { var iconButton = this.props.rightIconButton; var newState = {}; newState.rightIconButtonKeyboardFocused = isKeyboardFocused; if (isKeyboardFocused) newState.isKeyboardFocused = false; this.setState(newState); if (iconButton && iconButton.props.onKeyboardFocus) iconButton.props.onKeyboardFocus(e, isKeyboardFocused); }, _handleRightIconButtonMouseDown: function _handleRightIconButtonMouseDown(e) { var iconButton = this.props.rightIconButton; e.stopPropagation(); if (iconButton && iconButton.props.onMouseDown) iconButton.props.onMouseDown(e); }, _handleRightIconButtonMouseLeave: function _handleRightIconButtonMouseLeave(e) { var iconButton = this.props.rightIconButton; this.setState({ rightIconButtonHovered: false }); if (iconButton && iconButton.props.onMouseLeave) iconButton.props.onMouseLeave(e); }, _handleRightIconButtonMouseEnter: function _handleRightIconButtonMouseEnter(e) { var iconButton = this.props.rightIconButton; this.setState({ rightIconButtonHovered: true }); if (iconButton && iconButton.props.onMouseEnter) iconButton.props.onMouseEnter(e); }, _handleRightIconButtonMouseUp: function _handleRightIconButtonMouseUp(e) { var iconButton = this.props.rightIconButton; e.stopPropagation(); if (iconButton && iconButton.props.onMouseUp) iconButton.props.onMouseUp(e); }, _handleRightIconButtonTouchTap: function _handleRightIconButtonTouchTap(e) { var iconButton = this.props.rightIconButton; //Stop the event from bubbling up to the list-item e.stopPropagation(); if (iconButton && iconButton.props.onTouchTap) iconButton.props.onTouchTap(e); }, _handleTouchStart: function _handleTouchStart(e) { this.setState({ touch: true }); this.props.onTouchStart(e); }, _pushElement: function _pushElement(children, element, baseStyles, additionalProps) { if (element) { var styles = this.mergeStyles(baseStyles, element.props.style); children.push(React.cloneElement(element, _extends({ key: children.length, style: styles }, additionalProps))); } } }); module.exports = ListItem;