From fecd6870f0961e7e8296be88d4a352dcbfbbf2cb Mon Sep 17 00:00:00 2001 From: Zadielerick Date: Thu, 2 Jul 2015 11:14:34 -0500 Subject: [PATCH 01/16] Added support for font-icon to be able to use Google material Icons --- .../pages/components/icon-buttons.jsx | 13 ++++++++++--- src/icon-button.jsx | 17 ++++------------- src/utils/children.js | 17 +++++++++-------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/docs/src/app/components/pages/components/icon-buttons.jsx b/docs/src/app/components/pages/components/icon-buttons.jsx index bf276bf448a9e5..c984a091e3ef05 100644 --- a/docs/src/app/components/pages/components/icon-buttons.jsx +++ b/docs/src/app/components/pages/components/icon-buttons.jsx @@ -21,9 +21,9 @@ class IconButtonsPage extends React.Component { '\n' + ' \n' + '\n\n' + - '//Adding tooltipPosition to Icon Button\n' + - '\n'; + '//Method 4: Using Google material-icons\n' + + ' settings_system_daydream'; let desc = (

@@ -50,6 +50,10 @@ class IconButtonsPage extends React.Component { similiar to how the iconClassName prop from method 1 is handled. +

  • + Google Material Icons: Now also supported for iconButtons by passing "material-icons" in + iconClassName prop. +
  • ); @@ -168,6 +172,9 @@ class IconButtonsPage extends React.Component { +


    + + settings_system_daydream ); diff --git a/src/icon-button.jsx b/src/icon-button.jsx index 3eadd10a7b411a..46fa55862f7c6e 100644 --- a/src/icon-button.jsx +++ b/src/icon-button.jsx @@ -49,17 +49,6 @@ let IconButton = React.createClass({ }; }, - componentDidMount() { - if (process.env.NODE_ENV !== 'production') { - if (this.props.iconClassName && this.props.children) { - let warning = 'You have set both an iconClassName and a child icon. ' + - 'It is recommended you use only one method when adding ' + - 'icons to IconButtons.'; - console.warn(warning); - } - } - }, - getStyles() { let spacing = this.context.muiTheme.spacing; let palette = this.context.muiTheme.palette; @@ -71,7 +60,8 @@ let IconButton = React.createClass({ transition: Transitions.easeOut(), padding: spacing.iconSize / 2, width: spacing.iconSize*2, - height: spacing.iconSize*2 + height: spacing.iconSize*2, + fontSize: 0 }, tooltip: { boxSizing: 'border-box', @@ -133,7 +123,8 @@ let IconButton = React.createClass({ styles.icon, disabled ? styles.disabled : {}, iconStyleFontIcon - )}/> + )}> + {this.props.children} ); } diff --git a/src/utils/children.js b/src/utils/children.js index bc82e3cc7ed072..2b644053c64944 100644 --- a/src/utils/children.js +++ b/src/utils/children.js @@ -4,17 +4,18 @@ module.exports = { extend(children, extendedProps, extendedChildren) { - return React.Children.map(children, (child) => { + return React.isValidElement(children) ? + React.Children.map(children, (child) => { - let newProps = typeof(extendedProps) === 'function' ? - extendedProps(child) : extendedProps; + let newProps = typeof(extendedProps) === 'function' ? + extendedProps(child) : extendedProps; - let newChildren = typeof(extendedChildren) === 'function' ? - extendedChildren(child) : extendedChildren ? - extendedChildren : child.props.children; + let newChildren = typeof(extendedChildren) === 'function' ? + extendedChildren(child) : extendedChildren ? + extendedChildren : child.props.children; - return React.cloneElement(child, newProps, newChildren); - }); + return React.cloneElement(child, newProps, newChildren); + }) : children; } }; From 7a55407d221e6e3020fcec90d90638d5451352e9 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 2 Jul 2015 20:53:45 +0100 Subject: [PATCH 02/16] [eslint] enforce rule no-unused-vars --- .eslintrc | 1 - src/avatar.jsx | 1 - src/dialog-window.jsx | 2 +- src/drop-down-icon.jsx | 1 - src/left-nav.jsx | 4 ++-- src/select-field.jsx | 1 - src/text-field.jsx | 1 - src/utils/modernizr.custom.js | 6 +----- 8 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.eslintrc b/.eslintrc index c7d14a851be23d..7fcfad690d0fa7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,6 @@ "no-multi-spaces": 0, "no-underscore-dangle": 0, "no-unused-expressions": 0, - "no-unused-vars": 0, "no-sequences": 0, "no-shadow": 0, "no-shadow-restricted-names": 0, diff --git a/src/avatar.jsx b/src/avatar.jsx index e1f186892701d3..875fa2d085e193 100644 --- a/src/avatar.jsx +++ b/src/avatar.jsx @@ -1,7 +1,6 @@ let React = require('react/addons'); let StylePropable = require('./mixins/style-propable'); let Colors = require('./styles/colors'); -let Typography = require('./styles/typography'); let Avatar = React.createClass({ diff --git a/src/dialog-window.jsx b/src/dialog-window.jsx index 8d61879a3ebfad..c321031f635a35 100644 --- a/src/dialog-window.jsx +++ b/src/dialog-window.jsx @@ -58,7 +58,7 @@ let DialogWindow = React.createClass({ } }, - componentDidUpdate(prevProps, prevState) { + componentDidUpdate() { this._positionDialog(); this._focusOnAction(); }, diff --git a/src/drop-down-icon.jsx b/src/drop-down-icon.jsx index f498b2d5b201d4..8f845a038fbbf4 100644 --- a/src/drop-down-icon.jsx +++ b/src/drop-down-icon.jsx @@ -1,7 +1,6 @@ let React = require('react'); let StylePropable = require('./mixins/style-propable'); let Transitions = require('./styles/transitions'); -let Spacing = require('./styles/spacing'); let ClickAwayable = require('./mixins/click-awayable'); let FontIcon = require('./font-icon'); let Menu = require('./menu/menu'); diff --git a/src/left-nav.jsx b/src/left-nav.jsx index 226210b8a1b4b6..6d464031ad1217 100644 --- a/src/left-nav.jsx +++ b/src/left-nav.jsx @@ -55,7 +55,7 @@ let LeftNav = React.createClass({ this._enableSwipeHandling(); }, - componentDidUpdate(prevProps, prevState) { + componentDidUpdate() { this._updateMenuHeight(); this._enableSwipeHandling(); }, @@ -202,7 +202,7 @@ let LeftNav = React.createClass({ } }, - _onWindowResize(e) { + _onWindowResize() { this._updateMenuHeight(); }, diff --git a/src/select-field.jsx b/src/select-field.jsx index 471af3590f3cf1..2d2716506456ce 100644 --- a/src/select-field.jsx +++ b/src/select-field.jsx @@ -1,6 +1,5 @@ let React = require('react'); let StylePropable = require('./mixins/style-propable'); -let Transitions = require('./styles/transitions'); let TextField = require('./text-field'); let DropDownMenu = require('./drop-down-menu'); diff --git a/src/text-field.jsx b/src/text-field.jsx index 6b7d4b5e2a62b5..68df4f12a88e6f 100644 --- a/src/text-field.jsx +++ b/src/text-field.jsx @@ -59,7 +59,6 @@ let TextField = React.createClass({ }, componentWillReceiveProps(nextProps) { - let hasErrorProp = nextProps.hasOwnProperty('errorText'); let newState = {}; newState.errorText = nextProps.errorText; diff --git a/src/utils/modernizr.custom.js b/src/utils/modernizr.custom.js index 2c6972d8e91d05..71001a04f1f85e 100644 --- a/src/utils/modernizr.custom.js +++ b/src/utils/modernizr.custom.js @@ -15,10 +15,8 @@ module.exports = (function( window, document, undefined ) { modElem = document.createElement(mod), mStyle = modElem.style, - inputElem , - toString = {}.toString, prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), @@ -32,8 +30,6 @@ module.exports = (function( window, document, undefined ) { tests = {}, - inputs = {}, - attrs = {}, classes = [], @@ -221,7 +217,7 @@ module.exports = (function( window, document, undefined ) { if ( ret && 'webkitPerspective' in docElement.style ) { - injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { + injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function(node) { ret = node.offsetLeft === 9 && node.offsetHeight === 3; }); } From 1eda9c8550a568a00cb1920a07d8e117004323e7 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 2 Jul 2015 22:41:39 +0100 Subject: [PATCH 03/16] [SelectField] set font size to 16px --- .../pages/components/text-fields.jsx | 11 ++---- src/select-field.jsx | 38 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/src/app/components/pages/components/text-fields.jsx b/docs/src/app/components/pages/components/text-fields.jsx index 2c227570e2864b..2463886d9dd2e0 100644 --- a/docs/src/app/components/pages/components/text-fields.jsx +++ b/docs/src/app/components/pages/components/text-fields.jsx @@ -339,16 +339,13 @@ let TextFieldsPage = React.createClass({ value={this.state.selectValue} onChange={this._handleSelectValueChange.bind(null, 'selectValue')} hintText="Hint Text" - menuItems={menuItems} /> + menuItems={menuItems} />
    -

    - Without hintText or floatingLabelText -

    + menuItems={arbitraryArrayMenuItems} />
    Date: Thu, 2 Jul 2015 23:44:47 +0100 Subject: [PATCH 04/16] [DropDownMenu] fix warning on the text-field component page --- src/drop-down-menu.jsx | 4 +++- src/menu/menu.jsx | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/drop-down-menu.jsx b/src/drop-down-menu.jsx index c50c498ec81a42..2030d358169f78 100644 --- a/src/drop-down-menu.jsx +++ b/src/drop-down-menu.jsx @@ -159,12 +159,14 @@ let DropDownMenu = React.createClass({ if (this.props.valueMember && (this.props.valueLink || this.props.value)) { let value = this.props.value || this.props.valueLink.value; - for (let i in this.props.menuItems) + for (let i = 0; i < this.props.menuItems.length; i++) { if (this.props.menuItems[i][this.props.valueMember] === value) selectedIndex = i; + } } } + let selectedItem = this.props.menuItems[selectedIndex]; if (selectedItem) displayValue = selectedItem[this.props.displayMember]; diff --git a/src/menu/menu.jsx b/src/menu/menu.jsx index 9e73fa2505370a..3cce3726ee040e 100644 --- a/src/menu/menu.jsx +++ b/src/menu/menu.jsx @@ -308,7 +308,6 @@ var Menu = React.createClass({ _getChildren() { let menuItem, itemComponent, - isSelected, isDisabled; let styles = this.getStyles(); @@ -319,7 +318,6 @@ var Menu = React.createClass({ for (let i=0; i < this.props.menuItems.length; i++) { menuItem = this.props.menuItems[i]; - isSelected = i === this.props.selectedIndex; isDisabled = (menuItem.disabled === undefined) ? false : menuItem.disabled; let { @@ -339,7 +337,7 @@ var Menu = React.createClass({ Date: Fri, 3 Jul 2015 13:08:29 -0500 Subject: [PATCH 05/16] Revert modernizr eslint changes. --- .eslintrc | 1 + src/utils/modernizr.custom.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 7fcfad690d0fa7..c7d14a851be23d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,6 +13,7 @@ "no-multi-spaces": 0, "no-underscore-dangle": 0, "no-unused-expressions": 0, + "no-unused-vars": 0, "no-sequences": 0, "no-shadow": 0, "no-shadow-restricted-names": 0, diff --git a/src/utils/modernizr.custom.js b/src/utils/modernizr.custom.js index 71001a04f1f85e..2c6972d8e91d05 100644 --- a/src/utils/modernizr.custom.js +++ b/src/utils/modernizr.custom.js @@ -15,8 +15,10 @@ module.exports = (function( window, document, undefined ) { modElem = document.createElement(mod), mStyle = modElem.style, + inputElem , + toString = {}.toString, prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), @@ -30,6 +32,8 @@ module.exports = (function( window, document, undefined ) { tests = {}, + inputs = {}, + attrs = {}, classes = [], @@ -217,7 +221,7 @@ module.exports = (function( window, document, undefined ) { if ( ret && 'webkitPerspective' in docElement.style ) { - injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function(node) { + injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { ret = node.offsetLeft === 9 && node.offsetHeight === 3; }); } From 838bbc18c2f6a55f7540f6a57bcfc65313e0cc9a Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 4 Jul 2015 01:23:02 +0100 Subject: [PATCH 06/16] [eslint] enforce no-ununsed-vars --- .eslintrc | 1 - src/utils/modernizr.custom.js | 11 +---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.eslintrc b/.eslintrc index c7d14a851be23d..7fcfad690d0fa7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,6 @@ "no-multi-spaces": 0, "no-underscore-dangle": 0, "no-unused-expressions": 0, - "no-unused-vars": 0, "no-sequences": 0, "no-shadow": 0, "no-shadow-restricted-names": 0, diff --git a/src/utils/modernizr.custom.js b/src/utils/modernizr.custom.js index 2c6972d8e91d05..89b6604257c4bf 100644 --- a/src/utils/modernizr.custom.js +++ b/src/utils/modernizr.custom.js @@ -15,11 +15,6 @@ module.exports = (function( window, document, undefined ) { modElem = document.createElement(mod), mStyle = modElem.style, - inputElem , - - - toString = {}.toString, - prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), @@ -32,8 +27,6 @@ module.exports = (function( window, document, undefined ) { tests = {}, - inputs = {}, - attrs = {}, classes = [], @@ -221,7 +214,7 @@ module.exports = (function( window, document, undefined ) { if ( ret && 'webkitPerspective' in docElement.style ) { - injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { + injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function(node) { ret = node.offsetLeft === 9 && node.offsetHeight === 3; }); } @@ -275,8 +268,6 @@ module.exports = (function( window, document, undefined ) { setCss(''); - modElem = inputElem = null; - Modernizr._version = version; From 1c1e75b6aa71fd6498dc677cb6e5f54d3a1350fc Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 4 Jul 2015 12:31:55 +0100 Subject: [PATCH 07/16] [TextField] fix the floating label and hint text position --- docs/src/app/components/pages/components/text-fields.jsx | 8 ++++++++ src/text-field.jsx | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/src/app/components/pages/components/text-fields.jsx b/docs/src/app/components/pages/components/text-fields.jsx index 2c227570e2864b..b0d4d22eb93438 100644 --- a/docs/src/app/components/pages/components/text-fields.jsx +++ b/docs/src/app/components/pages/components/text-fields.jsx @@ -314,6 +314,14 @@ let TextFieldsPage = React.createClass({ style={styles.textfield} hintText="Hint Text (MultiLine)" multiLine={true} />
    +
    +
    Date: Sat, 4 Jul 2015 17:42:21 -0500 Subject: [PATCH 08/16] [menus] Keyboard focus fixes --- src/icon-button.jsx | 4 ++++ src/lists/list-item.jsx | 18 ++++++++++++--- src/menus/icon-menu.jsx | 19 +++++++++++++--- src/menus/menu-item.jsx | 18 ++++++++++----- src/menus/menu.jsx | 49 ++++++++++++++++++++++++++++++++--------- 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/icon-button.jsx b/src/icon-button.jsx index 46fa55862f7c6e..de061873f9b16c 100644 --- a/src/icon-button.jsx +++ b/src/icon-button.jsx @@ -152,6 +152,10 @@ let IconButton = React.createClass({ ); }, + setKeyboardFocus() { + this.refs.button.setKeyboardFocus(); + }, + _showTooltip() { if (!this.props.disabled && this.props.tooltip) { this.setState({ tooltipShown: true }); diff --git a/src/lists/list-item.jsx b/src/lists/list-item.jsx index dbe4e72fd158aa..358a20dbc5f21e 100644 --- a/src/lists/list-item.jsx +++ b/src/lists/list-item.jsx @@ -241,11 +241,23 @@ let ListItem = React.createClass({ }, - setKeyboardFocus() { + applyFocusState(focusState) { let button = this.refs.enhancedButton; + let buttonEl = React.findDOMNode(button); + if (button) { - button.setKeyboardFocus(); - React.findDOMNode(button).focus(); + switch(focusState) { + case 'none': + buttonEl.blur(); + break; + case 'focused': + buttonEl.focus(); + break; + case 'keyboard-focused': + button.setKeyboardFocus(); + buttonEl.focus(); + break; + } } }, diff --git a/src/menus/icon-menu.jsx b/src/menus/icon-menu.jsx index 055638f2f98adb..1cfe70805c3739 100644 --- a/src/menus/icon-menu.jsx +++ b/src/menus/icon-menu.jsx @@ -25,6 +25,7 @@ let IconMenu = React.createClass({ 'top-left', 'top-right' ]), + onItemKeyboardActivate: React.PropTypes.func, onItemTouchTap: React.PropTypes.func, menuListStyle: React.PropTypes.object, onKeyDown: React.PropTypes.func, @@ -34,12 +35,14 @@ let IconMenu = React.createClass({ getDefaultProps() { return { onKeyDown: () => {}, + onItemKeyboardActivate: () => {}, onItemTouchTap: () => {} }; }, getInitialState() { return { + iconButtonRef: this.props.iconButtonElement.props.ref || 'iconButton', open: false } }, @@ -81,7 +84,8 @@ let IconMenu = React.createClass({ onTouchTap: (e) => { this.open(); if (iconButtonElement.props.onTouchTap) iconButtonElement.props.onTouchTap(e); - }.bind(this) + }.bind(this), + ref: this.state.iconButtonRef }); return ( @@ -97,6 +101,7 @@ let IconMenu = React.createClass({ menuListStyle={menuListStyle} multiple={multiple} onItemTouchTap={this._handleItemTouchTap} + onItemKeyboardActivate={this._handleItemKeyboardActivate} onChange={onChange} open={open} openDirection={openDirection} @@ -112,10 +117,12 @@ let IconMenu = React.createClass({ }, close() { - if (!this.state.close) { + if (this.state.open) { this.setState({ open: false }); + //Set focus on the icon button when the menu closes + React.findDOMNode(this.refs[this.state.iconButtonRef]).focus(); } }, @@ -128,7 +135,7 @@ let IconMenu = React.createClass({ }, _handleIconButtonKeyboardActivate(e) { - this.refs.menu.setKeyboardFocusIndex(0); + this.refs.menu.setKeyboardFocused(true); }, _handleKeyDown(e) { @@ -140,6 +147,12 @@ let IconMenu = React.createClass({ this.props.onKeyDown(e); }, + _handleItemKeyboardActivate(e, child) { + //The icon button receives keyboard focus when a + //menu item is keyboard activated + this.refs[this.state.iconButtonRef].setKeyboardFocus(); + }, + _handleItemTouchTap(e, child) { setTimeout(() => { this.close(); diff --git a/src/menus/menu-item.jsx b/src/menus/menu-item.jsx index 8f642027f65cfa..528cafe414e19c 100644 --- a/src/menus/menu-item.jsx +++ b/src/menus/menu-item.jsx @@ -19,7 +19,11 @@ let MenuItem = React.createClass({ disabled: React.PropTypes.bool, innerDivStyle: React.PropTypes.object, insetChildren: React.PropTypes.bool, - keyboardFocused: React.PropTypes.bool, + focusState: React.PropTypes.oneOf([ + 'none', + 'focused', + 'keyboard-focused' + ]), leftIcon: React.PropTypes.element, rightIcon: React.PropTypes.element, secondaryText: React.PropTypes.node, @@ -28,15 +32,16 @@ let MenuItem = React.createClass({ getDefaultProps() { return { + focusState: 'none' }; }, componentDidMount() { - if (this.props.keyboardFocused) this.setKeyboardFocus(); + this._applyFocusState(); }, - componentDidUpdate: function(prevProps, prevState) { - if (this.props.keyboardFocused) this.setKeyboardFocus(); + componentDidUpdate() { + this._applyFocusState(); }, render() { @@ -45,6 +50,7 @@ let MenuItem = React.createClass({ checked, desktop, disabled, + focusState, innerDivStyle, insetChildren, leftIcon, @@ -134,8 +140,8 @@ let MenuItem = React.createClass({ ); }, - setKeyboardFocus() { - this.refs.listItem.setKeyboardFocus(); + _applyFocusState() { + this.refs.listItem.applyFocusState(this.props.focusState); } }); diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 3a591d60b54a02..3e75601ce6929c 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -22,6 +22,7 @@ let Menu = React.createClass({ desktop: React.PropTypes.bool, listStyle: React.PropTypes.object, multiple: React.PropTypes.bool, + onItemKeyboardActivate: React.PropTypes.func, onItemTouchTap: React.PropTypes.func, open: React.PropTypes.bool, openDirection: React.PropTypes.oneOf([ @@ -40,6 +41,7 @@ let Menu = React.createClass({ getDefaultProps() { return { autoWidth: true, + onItemKeyboardActivate: () => {}, onItemTouchTap: () => {}, open: true, openDirection: 'bottom-left', @@ -49,7 +51,8 @@ let Menu = React.createClass({ getInitialState() { return { - keyboardFocusIndex: -1, + focusIndex: 0, + isKeyboardFocused: false, keyWidth: this.props.desktop ? 64 : 56 }; }, @@ -58,10 +61,20 @@ let Menu = React.createClass({ if (this.props.autoWidth) this._setWidth(); }, - componentDidUpdate() { + componentDidUpdate(prevProps) { if (this.props.autoWidth) this._setWidth(); }, + componentWillUpdate(nextProps, nextState) { + let openChanged = nextProps.open !== this.props.open; + if (openChanged && !nextProps.open) { + this.setState({ + focusIndex: 0, + isKeyboardFocused: false + }); + } + }, + render() { let { @@ -156,9 +169,19 @@ let Menu = React.createClass({ selectedChildrenStyles ); + let focusState = 'none'; + if (open && childIndex === this.state.focusIndex) { + focusState = this.state.isKeyboardFocused ? + 'keyboard-focused' : 'focused'; + } + let clonedChild = React.cloneElement(child, { desktop: desktop, - keyboardFocused: open && childIndex === this.state.keyboardFocusIndex, + focusState: focusState, + onKeyboardActivate: (e) => { + this.props.onItemKeyboardActivate(e, child); + if (child.props.onKeyboardActivate) child.props.onKeyboardActivate(e); + }, onTouchTap: (e) => { this._handleMenuItemTouchTap(e, child); if (child.props.onTouchTap) child.props.onTouchTap(e); @@ -185,29 +208,36 @@ let Menu = React.createClass({ ); }, - setKeyboardFocusIndex(newIndex) { + setKeyboardFocused(keyboardFocused) { + this.setState({ + isKeyboardFocused: keyboardFocused + }); + }, + + _setFocusIndex(newIndex, isKeyboardFocused) { this.setState({ - keyboardFocusIndex: newIndex + focusIndex: newIndex, + isKeyboardFocused: isKeyboardFocused }); }, _decrementKeyboardFocusIndex() { - let index = this.state.keyboardFocusIndex; + let index = this.state.focusIndex; index--; if (index < 0) index = 0; - this.setKeyboardFocusIndex(index); + this._setFocusIndex(index, true); }, _incrementKeyboardFocusIndex() { - let index = this.state.keyboardFocusIndex; + let index = this.state.focusIndex; let maxIndex = React.Children.count(this.props.children) - 1; index++; if (index > maxIndex) index = maxIndex; - this.setKeyboardFocusIndex(index); + this._setFocusIndex(index, true); }, _handleKeyDown(e) { @@ -227,7 +257,6 @@ let Menu = React.createClass({ } break; } - e.preventDefault(); } }, From adefb080e7980d7915d70d8e88cb1fa85b51fac5 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 00:37:08 -0500 Subject: [PATCH 09/16] [menus] Fixed keyboard focus on menu when a divider is present --- .../pages/components/icon-menus.jsx | 28 +++- src/menus/icon-menu.jsx | 16 ++- src/menus/menu.jsx | 127 +++++++++++------- 3 files changed, 118 insertions(+), 53 deletions(-) diff --git a/docs/src/app/components/pages/components/icon-menus.jsx b/docs/src/app/components/pages/components/icon-menus.jsx index 7e0ba31d0a58ba..076f7c8ea0b9ec 100644 --- a/docs/src/app/components/pages/components/icon-menus.jsx +++ b/docs/src/app/components/pages/components/icon-menus.jsx @@ -2,9 +2,17 @@ let React = require('react'); let { IconButton } = require('material-ui'); let IconMenu = require('menus/icon-menu'); let MenuItem = require('menus/menu-item'); +let MenuDivider = require('menus/menu-divider'); let MoreVertIcon = require('svg-icons/navigation/more-vert'); let ComponentDoc = require('../../component-doc'); +let ContentCopy = require('svg-icons/content/content-copy'); +let ContentLink = require('svg-icons/content/link'); +let Delete = require('svg-icons/action/delete'); +let Download = require('svg-icons/file/file-download'); +let PersonAdd = require('svg-icons/social/person-add'); +let RemoveRedEye = require('svg-icons/image/remove-red-eye'); + class IconMenus extends React.Component { @@ -59,10 +67,10 @@ class IconMenus extends React.Component { desc: 'This is the placement of the menu relative to the IconButton.' }, { - name: 'menuListStyle', + name: 'menuStyle', type: 'object', header: 'optional', - desc: 'The style object to use to override underlying menu list style.' + desc: 'The style object to use to override underlying menu style.' }, { name: 'multiple', @@ -206,6 +214,22 @@ class IconMenus extends React.Component { Sign out

    + +

    Menu Item variations + + }>Preview + }>Share + }>Get link + + }>Make a copy + }>Download + + }>Remove + +

    + ); diff --git a/src/menus/icon-menu.jsx b/src/menus/icon-menu.jsx index 1cfe70805c3739..cbc10a8528d809 100644 --- a/src/menus/icon-menu.jsx +++ b/src/menus/icon-menu.jsx @@ -27,6 +27,7 @@ let IconMenu = React.createClass({ ]), onItemKeyboardActivate: React.PropTypes.func, onItemTouchTap: React.PropTypes.func, + menuStyle: React.PropTypes.object, menuListStyle: React.PropTypes.object, onKeyDown: React.PropTypes.func, width: React.PropTypes.number @@ -34,6 +35,7 @@ let IconMenu = React.createClass({ getDefaultProps() { return { + openDirection: 'bottom-left', onKeyDown: () => {}, onItemKeyboardActivate: () => {}, onItemTouchTap: () => {} @@ -60,6 +62,7 @@ let IconMenu = React.createClass({ onChange, onKeyDown, onItemTouchTap, + menuStyle, menuListStyle, style, value, @@ -69,15 +72,25 @@ let IconMenu = React.createClass({ } = this.props; let open = this.state.open; + let openDown = openDirection.split('-')[0] === 'bottom'; + let openLeft = openDirection.split('-')[1] === 'left'; let styles = { root: { display: 'inline-block', position: 'relative' + }, + + menu: { + top: openDown ? 12 : null, + bottom: !openDown ? 12 : null, + left: !openLeft ? 12 : null, + right: openLeft ? 12 : null } }; let mergedRootStyles = this.mergeAndPrefix(styles.root, style); + let mergedMenuStyles = this.mergeStyles(styles.menu, menuStyle); let iconButton = React.cloneElement(iconButtonElement, { onKeyboardActivate: this._handleIconButtonKeyboardActivate, @@ -98,7 +111,7 @@ let IconMenu = React.createClass({ diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 3e75601ce6929c..13786fe7c577b5 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -104,10 +104,10 @@ let Menu = React.createClass({ transitionDelay: open ? '0ms' : '250ms', position: 'absolute', zIndex: 10, - top: openDown ? 12 : null, - bottom: !openDown ? 12 : null, - left: !openLeft ? 12 : null, - right: openLeft ? 12 : null, + top: openDown ? 0 : null, + bottom: !openDown ? 0 : null, + left: !openLeft ? 0 : null, + right: openLeft ? 0 : null, transform: open ? 'scaleX(1)' : 'scaleX(0)', transformOrigin: openLeft ? 'right' : 'left' }, @@ -143,7 +143,8 @@ let Menu = React.createClass({ let childrenTransitionDelay = openDown ? 175 : 325; let childrenTransitionDelayIncrement = Math.ceil(150/React.Children.count(this.props.children)); - let newChildren = React.Children.map(children, (child, childIndex) => { + let menuItemIndex = 0; + let newChildren = React.Children.map(children, (child) => { if (openDown) { childrenTransitionDelay += childrenTransitionDelayIncrement; @@ -155,40 +156,11 @@ let Menu = React.createClass({ transitionDelay: open ? childrenTransitionDelay + 'ms' : '0ms' }); - let menuValue = this.getValueLink(this.props).value; - let childValue = child.props.value; - let selectedChildrenStyles = {}; + let childIsADivider = child.type.displayName === 'MenuDivider'; + let clonedChild = childIsADivider ? child : + this._cloneMenuItem(child, menuItemIndex, styles); - if ((multiple && menuValue.length && menuValue.indexOf(childValue) !== -1) || - (!multiple && menuValue && menuValue === childValue)) { - selectedChildrenStyles = this.mergeStyles(styles.selectedMenuItem, selectedMenuItemStyle); - } - - let mergedChildrenStyles = this.mergeStyles( - child.props.style || {}, - selectedChildrenStyles - ); - - let focusState = 'none'; - if (open && childIndex === this.state.focusIndex) { - focusState = this.state.isKeyboardFocused ? - 'keyboard-focused' : 'focused'; - } - - let clonedChild = React.cloneElement(child, { - desktop: desktop, - focusState: focusState, - onKeyboardActivate: (e) => { - this.props.onItemKeyboardActivate(e, child); - if (child.props.onKeyboardActivate) child.props.onKeyboardActivate(e); - }, - onTouchTap: (e) => { - this._handleMenuItemTouchTap(e, child); - if (child.props.onTouchTap) child.props.onTouchTap(e); - }, - style: mergedChildrenStyles, - tabIndex: open ? child.props.tabIndex : -1 - }); + if (!childIsADivider) menuItemIndex++; return
    {clonedChild}
    ; @@ -214,10 +186,48 @@ let Menu = React.createClass({ }); }, - _setFocusIndex(newIndex, isKeyboardFocused) { - this.setState({ - focusIndex: newIndex, - isKeyboardFocused: isKeyboardFocused + _cloneMenuItem(child, childIndex, styles) { + + let { + desktop, + multiple, + open, + selectedMenuItemStyle + } = this.props; + + let menuValue = this.getValueLink(this.props).value; + let childValue = child.props.value; + let selectedChildrenStyles = {}; + + if ((multiple && menuValue.length && menuValue.indexOf(childValue) !== -1) || + (!multiple && menuValue && menuValue === childValue)) { + selectedChildrenStyles = this.mergeStyles(styles.selectedMenuItem, selectedMenuItemStyle); + } + + let mergedChildrenStyles = this.mergeStyles( + child.props.style || {}, + selectedChildrenStyles + ); + + let focusState = 'none'; + if (open && childIndex === this.state.focusIndex) { + focusState = this.state.isKeyboardFocused ? + 'keyboard-focused' : 'focused'; + } + + return React.cloneElement(child, { + desktop: desktop, + focusState: focusState, + onKeyboardActivate: (e) => { + this.props.onItemKeyboardActivate(e, child); + if (child.props.onKeyboardActivate) child.props.onKeyboardActivate(e); + }, + onTouchTap: (e) => { + this._handleMenuItemTouchTap(e, child); + if (child.props.onTouchTap) child.props.onTouchTap(e); + }, + style: mergedChildrenStyles, + tabIndex: open ? child.props.tabIndex : -1 }); }, @@ -230,14 +240,14 @@ let Menu = React.createClass({ this._setFocusIndex(index, true); }, - _incrementKeyboardFocusIndex() { - let index = this.state.focusIndex; - let maxIndex = React.Children.count(this.props.children) - 1; - - index++; - if (index > maxIndex) index = maxIndex; - - this._setFocusIndex(index, true); + _getMenuItemCount() { + let dividerCount = 0; + React.Children.forEach(this.props.children, (child) => { + if (child.type.displayName === 'MenuDivider') { + dividerCount++; + } + }); + return React.Children.count(this.props.children) - dividerCount; }, _handleKeyDown(e) { @@ -282,6 +292,23 @@ let Menu = React.createClass({ this.props.onItemTouchTap(e, item); }, + _incrementKeyboardFocusIndex() { + let index = this.state.focusIndex; + let maxIndex = this._getMenuItemCount() - 1; + + index++; + if (index > maxIndex) index = maxIndex; + + this._setFocusIndex(index, true); + }, + + _setFocusIndex(newIndex, isKeyboardFocused) { + this.setState({ + focusIndex: newIndex, + isKeyboardFocused: isKeyboardFocused + }); + }, + _setWidth() { let el = React.findDOMNode(this); let listEl = React.findDOMNode(this.refs.list); From d4563264bdfc106617c946955426cde187d14cda Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 5 Jul 2015 12:52:54 +0100 Subject: [PATCH 10/16] [eslint] add a gulp task and continue linting --- .eslintignore | 2 ++ .eslintrc | 4 +--- docs/gulp/tasks/eslint.js | 17 +++++++++++++---- docs/package.json | 1 + gulpfile.js | 15 +++++++++++++++ package.json | 4 +++- src/before-after-wrapper.jsx | 8 ++++---- src/card/card-actions.jsx | 3 +-- src/card/card-header.jsx | 6 +++--- src/card/card-text.jsx | 4 ++-- src/card/card-title.jsx | 2 +- src/circular-progress.jsx | 2 +- src/date-picker/calendar-toolbar.jsx | 1 - src/date-picker/calendar-year.jsx | 6 +++--- src/date-picker/calendar.jsx | 4 ++-- src/date-picker/date-picker-dialog.jsx | 2 +- src/date-picker/year-button.jsx | 1 - src/enhanced-switch.jsx | 2 +- src/floating-action-button.jsx | 4 ++-- src/menu/link-menu-item.jsx | 4 ++-- src/menu/menu.jsx | 4 +--- src/menus/icon-menu.jsx | 8 +++----- src/menus/menu.jsx | 6 ++---- src/raised-button.jsx | 4 ++-- src/table/table-footer.jsx | 2 +- src/table/table-header-column.jsx | 1 - src/table/table-header.jsx | 1 - src/table/table-row.jsx | 1 - src/table/table.jsx | 4 +--- src/tabs/tabTemplate.jsx | 6 +++--- src/time-picker/clock-button.jsx | 6 +++--- src/time-picker/clock-hours.jsx | 6 +++--- src/time-picker/clock-minutes.jsx | 8 ++++---- src/time-picker/clock-number.jsx | 2 +- src/time-picker/clock-pointer.jsx | 4 ++-- src/time-picker/clock.jsx | 6 +++--- src/time-picker/time-display.jsx | 2 +- src/time-picker/time-picker.jsx | 4 ++-- src/toggle.jsx | 2 +- src/utils/color-manipulator.js | 6 +++--- 40 files changed, 94 insertions(+), 81 deletions(-) create mode 100644 .eslintignore create mode 100644 gulpfile.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000000..0934c4005a06c9 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +src/utils/modernizr.custom.js +src/svg-icons diff --git a/.eslintrc b/.eslintrc index 7fcfad690d0fa7..d4dd683173fffc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,19 +10,17 @@ "comma-spacing": 0, "eqeqeq": 0, "key-spacing": 0, - "no-multi-spaces": 0, "no-underscore-dangle": 0, "no-unused-expressions": 0, - "no-sequences": 0, "no-shadow": 0, "no-shadow-restricted-names": 0, "no-extend-native": 0, - "no-undef": 0, "new-cap": 0, "quotes": 0, "semi-spacing": 0, "space-unary-ops": 0, "space-infix-ops": 0, + "consistent-return": 0, "strict": 0 }, "parser": "babel-eslint", diff --git a/docs/gulp/tasks/eslint.js b/docs/gulp/tasks/eslint.js index 8bedaae2b51471..578346c446f87a 100644 --- a/docs/gulp/tasks/eslint.js +++ b/docs/gulp/tasks/eslint.js @@ -1,7 +1,16 @@ var gulp = require('gulp'); -var shell = require('gulp-shell'); +var eslint = require('gulp-eslint'); var handleErrors = require('../util/handleErrors'); -gulp.task('eslint', shell.task([ - '"../node_modules/.bin/eslint" ../src' -])).on('error', handleErrors); +gulp.task('eslint', function () { + return gulp.src(['../src/**']) + // eslint() attaches the lint output to the eslint property + // of the file object so it can be used by other modules. + .pipe(eslint()) + // eslint.format() outputs the lint results to the console. + // Alternatively use eslint.formatEach() (see Docs). + .pipe(eslint.format()) + // To have the process exit with an error code (1) on + // lint error, return the stream and pipe to failOnError last. + .pipe(eslint.failOnError()); +}).on('error', handleErrors); diff --git a/docs/package.json b/docs/package.json index a3a66501f66a98..7c757ce1b44786 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,6 +14,7 @@ "browser-sync": "^1.8.1", "browserify": "^10.2.0", "gulp": "^3.8.10", + "gulp-eslint": "^0.15.0", "gulp-notify": "^2.1.0", "gulp-shell": "^0.4.1", "gulp-sourcemaps": "1.5.2", diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000000000..73a0b01f610e6b --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,15 @@ +var gulp = require('gulp'); +var eslint = require('gulp-eslint'); + +gulp.task('eslint', function () { + return gulp.src(['src/**']) + // eslint() attaches the lint output to the eslint property + // of the file object so it can be used by other modules. + .pipe(eslint()) + // eslint.format() outputs the lint results to the console. + // Alternatively use eslint.formatEach() (see Docs). + .pipe(eslint.format()) + // To have the process exit with an error code (1) on + // lint error, return the stream and pipe to failOnError last. + .pipe(eslint.failOnError()); +}); diff --git a/package.json b/package.json index 3d696c6c845fcd..c3205ae678fcee 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "echo \"No test implemented\" && exit 0", "prebuild": "rm -rf lib", - "eslint": "eslint src/**", + "eslint": "gulp eslint", "build": "npm run eslint && babel --stage 1 ./src --out-dir ./lib", "prepublish": "npm run build" }, @@ -40,6 +40,8 @@ "babel-eslint": "^3.1.17", "eslint": "^0.23.0", "eslint-plugin-react": "^2.5.2", + "gulp": "^3.9.0", + "gulp-eslint": "^0.15.0", "react-router": "^0.13.3", "react-tap-event-plugin": "^0.1.6" } diff --git a/src/before-after-wrapper.jsx b/src/before-after-wrapper.jsx index 3f8d14866e0dca..16fdcf5a5f2b82 100644 --- a/src/before-after-wrapper.jsx +++ b/src/before-after-wrapper.jsx @@ -73,13 +73,13 @@ let BeforeAfterWrapper = React.createClass({ afterStyle = AutoPrefix.all({boxSizing: 'border-box'}); if (this.props.beforeStyle) beforeElement = - React.createElement( this.props.beforeElementType, + React.createElement(this.props.beforeElementType, {style: this.mergeAndPrefix(beforeStyle, this.props.beforeStyle), - key: "::before"} ); + key: "::before"}); if (this.props.afterStyle) afterElement = - React.createElement( this.props.afterElementType, + React.createElement(this.props.afterElementType, {style: this.mergeAndPrefix(afterStyle, this.props.afterStyle), - key: "::after"} ); + key: "::after"}); let children = [beforeElement, this.props.children, afterElement]; diff --git a/src/card/card-actions.jsx b/src/card/card-actions.jsx index aa945eb53fdc48..6e16e09e21e200 100644 --- a/src/card/card-actions.jsx +++ b/src/card/card-actions.jsx @@ -1,5 +1,4 @@ let React = require('react'); -let Styles = require('../styles'); let CardActions = React.createClass({ getStyles() { @@ -7,7 +6,7 @@ let CardActions = React.createClass({ root: { padding: 8 } - } + }; }, render() { diff --git a/src/card/card-header.jsx b/src/card/card-header.jsx index 41587f4922f302..1dabbbafd806d1 100644 --- a/src/card/card-header.jsx +++ b/src/card/card-header.jsx @@ -50,7 +50,7 @@ let CardHeader = React.createClass({ display: 'block', fontSize: 14 } - } + }; }, render() { @@ -63,10 +63,10 @@ let CardHeader = React.createClass({ let avatar = this.props.avatar; if (React.isValidElement(this.props.avatar)) { let avatarMergedStyle = this.mergeStyles(styles.avatar, avatar.props.style); - avatar = React.cloneElement(avatar, {style:avatarMergedStyle}) + avatar = React.cloneElement(avatar, {style:avatarMergedStyle}); } else - avatar = + avatar = ; return (
    diff --git a/src/card/card-text.jsx b/src/card/card-text.jsx index e8e74d375114b3..ce6400b53e24dd 100644 --- a/src/card/card-text.jsx +++ b/src/card/card-text.jsx @@ -15,7 +15,7 @@ let CardText = React.createClass({ getDefaultProps() { return { color: Styles.Colors.ck - } + }; }, getStyles() { @@ -25,7 +25,7 @@ let CardText = React.createClass({ fontSize: '14px', color: this.props.color } - } + }; }, render() { diff --git a/src/card/card-title.jsx b/src/card/card-title.jsx index 7f21d9d2af14f8..7da2d53614dc3f 100644 --- a/src/card/card-title.jsx +++ b/src/card/card-title.jsx @@ -38,7 +38,7 @@ let CardTitle = React.createClass({ color: this.props.subtitleColor, display: 'block' } - } + }; }, render() { let styles = this.getStyles(); diff --git a/src/circular-progress.jsx b/src/circular-progress.jsx index 2ba47978881dc0..ef4147833fccca 100644 --- a/src/circular-progress.jsx +++ b/src/circular-progress.jsx @@ -165,7 +165,7 @@ let CircularProgress = React.createClass({ let styles = this.getStyles(size || 1); return ( -
    +
    diff --git a/src/date-picker/calendar-toolbar.jsx b/src/date-picker/calendar-toolbar.jsx index 9ac3e0a36e0384..87a3abff74e720 100644 --- a/src/date-picker/calendar-toolbar.jsx +++ b/src/date-picker/calendar-toolbar.jsx @@ -3,7 +3,6 @@ let DateTime = require('../utils/date-time'); let IconButton = require('../icon-button'); let Toolbar = require('../toolbar/toolbar'); let ToolbarGroup = require('../toolbar/toolbar-group'); -let DropDownMenu = require('../drop-down-menu'); let NavigationChevronLeft = require('../svg-icons/navigation/chevron-left'); let NavigationChevronLeftDouble = require('../svg-icons/navigation-chevron-left-double'); let NavigationChevronRight = require('../svg-icons/navigation/chevron-right'); diff --git a/src/date-picker/calendar-year.jsx b/src/date-picker/calendar-year.jsx index ea205d9262f2e5..859cb873b59136 100644 --- a/src/date-picker/calendar-year.jsx +++ b/src/date-picker/calendar-year.jsx @@ -21,7 +21,7 @@ let CalendarYear = React.createClass({ this._scrollToSelectedYear(); }, - componentDidUpdate(prevProps, prevState) { + componentDidUpdate() { this._scrollToSelectedYear(); }, @@ -57,7 +57,7 @@ let CalendarYear = React.createClass({ let selected = this.props.selectedDate.getFullYear() === year; let selectedProps = {}; if (selected) { - selectedProps = {ref: 'selectedYearButton'} + selectedProps = {ref: 'selectedYearButton'}; } let yearButton = ( @@ -67,7 +67,7 @@ let CalendarYear = React.createClass({ onTouchTap={this._handleYearTouchTap} selected={selected} {...selectedProps} /> - ) + ); years.push(yearButton); } diff --git a/src/date-picker/calendar.jsx b/src/date-picker/calendar.jsx index 0ac87f7cb5c47b..3e5a448d519f04 100644 --- a/src/date-picker/calendar.jsx +++ b/src/date-picker/calendar.jsx @@ -277,7 +277,7 @@ let Calendar = React.createClass({ nextMonth: DateTime.monthDiff(this.state.selectedDate, this.props.maxDate) < 0, prevYear: DateTime.yearDiff(this.state.selectedDate, this.props.minDate) > 0, nextYear: DateTime.yearDiff(this.state.selectedDate, this.props.maxDate) < 0 - } + }; }, _handleMonthDayClick() { @@ -294,7 +294,7 @@ let Calendar = React.createClass({ switch (e.keyCode) { case KeyCode.UP: if (e.altKey && e.shiftKey) { - this._addSelectedYears(-1) + this._addSelectedYears(-1); } else if (e.shiftKey) { this._addSelectedMonths(-1); diff --git a/src/date-picker/date-picker-dialog.jsx b/src/date-picker/date-picker-dialog.jsx index 18828097ddcbae..a4278ac95dabff 100644 --- a/src/date-picker/date-picker-dialog.jsx +++ b/src/date-picker/date-picker-dialog.jsx @@ -117,7 +117,7 @@ let DatePickerDialog = React.createClass({ this.refs.dialogWindow.dismiss(); }, - _onSelectedDate(e) { + _onSelectedDate() { if (this.props.autoOk) { setTimeout(this._handleOKTouchTap, 300); } diff --git a/src/date-picker/year-button.jsx b/src/date-picker/year-button.jsx index a3fef5c79589aa..b8a713475d139e 100644 --- a/src/date-picker/year-button.jsx +++ b/src/date-picker/year-button.jsx @@ -1,6 +1,5 @@ let React = require('react'); let StylePropable = require('../mixins/style-propable'); -let DateTime = require('../utils/date-time'); let EnhancedButton = require('../enhanced-button'); diff --git a/src/enhanced-switch.jsx b/src/enhanced-switch.jsx index 6e9798d2d12351..cfc71851438c9e 100644 --- a/src/enhanced-switch.jsx +++ b/src/enhanced-switch.jsx @@ -288,7 +288,7 @@ let EnhancedSwitch = React.createClass({ ); return ( -
    +
    {inputElement} {elementsInOrder}
    diff --git a/src/floating-action-button.jsx b/src/floating-action-button.jsx index f9feed53518b41..8e4f6f251cb5ea 100644 --- a/src/floating-action-button.jsx +++ b/src/floating-action-button.jsx @@ -70,7 +70,7 @@ let FloatingActionButton = React.createClass({ }, _getBackgroundColor() { - return this.props.disabled ? this.getTheme().disabledColor : + return this.props.disabled ? this.getTheme().disabledColor : this.props.secondary ? this.getTheme().secondaryColor : this.getTheme().color; }, @@ -81,7 +81,7 @@ let FloatingActionButton = React.createClass({ }, _getIconColor() { - return this.props.disabled ? this.getTheme().disabledTextColor : + return this.props.disabled ? this.getTheme().disabledTextColor : this.props.secondary ? this.getTheme().secondaryIconColor : this.getTheme().iconColor; }, diff --git a/src/menu/link-menu-item.jsx b/src/menu/link-menu-item.jsx index 2bee50a4e0e3aa..dfd45a4b540e1e 100644 --- a/src/menu/link-menu-item.jsx +++ b/src/menu/link-menu-item.jsx @@ -30,7 +30,7 @@ let LinkMenuItem = React.createClass({ getInitialState() { return { hovered: false - } + }; }, getTheme() { @@ -66,7 +66,7 @@ let LinkMenuItem = React.createClass({ // Prevent context menu 'Open In New Tab/Window' let linkAttribute = (this.props.disabled) ? 'data-href' : 'href'; let link = {}; - link[linkAttribute] = this.props.payload + link[linkAttribute] = this.props.payload; let styles = this.getStyles(); diff --git a/src/menu/menu.jsx b/src/menu/menu.jsx index 3cce3726ee040e..2083925073c92c 100644 --- a/src/menu/menu.jsx +++ b/src/menu/menu.jsx @@ -10,7 +10,6 @@ let Paper = require('../paper'); let MenuItem = require('./menu-item'); let LinkMenuItem = require('./link-menu-item'); let SubheaderMenuItem = require('./subheader-menu-item'); -let WindowListenable = require('../mixins/window-listenable'); /*********************** @@ -306,7 +305,7 @@ var Menu = React.createClass({ }, _getChildren() { - let menuItem, + let menuItem, itemComponent, isDisabled; @@ -566,7 +565,6 @@ var Menu = React.createClass({ _tryToggleNested(index) { let item = this.refs[index]; - let toggleMenu = item.toggleNestedMenu; if (item && item.toggleNestedMenu) item.toggleNestedMenu(); } diff --git a/src/menus/icon-menu.jsx b/src/menus/icon-menu.jsx index cbc10a8528d809..beb4acf784beb6 100644 --- a/src/menus/icon-menu.jsx +++ b/src/menus/icon-menu.jsx @@ -1,8 +1,6 @@ let React = require('react/addons'); let ClickAwayable = require('../mixins/click-awayable'); -let Controllable = require('../mixins/controllable'); let StylePropable = require('../mixins/style-propable'); -let Children = require('../utils/children'); let KeyCode = require('../utils/key-code'); let Menu = require('../menus/menu'); @@ -46,7 +44,7 @@ let IconMenu = React.createClass({ return { iconButtonRef: this.props.iconButtonElement.props.ref || 'iconButton', open: false - } + }; }, componentClickAway() { @@ -148,7 +146,7 @@ let IconMenu = React.createClass({ } }, - _handleIconButtonKeyboardActivate(e) { + _handleIconButtonKeyboardActivate() { this.refs.menu.setKeyboardFocused(true); }, @@ -161,7 +159,7 @@ let IconMenu = React.createClass({ this.props.onKeyDown(e); }, - _handleItemKeyboardActivate(e, child) { + _handleItemKeyboardActivate() { //The icon button receives keyboard focus when a //menu item is keyboard activated this.refs[this.state.iconButtonRef].setKeyboardFocus(); diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 13786fe7c577b5..ad542c8ee8f99f 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -3,8 +3,6 @@ let update = React.addons.update; let Controllable = require('../mixins/controllable'); let StylePropable = require('../mixins/style-propable'); let Transitions = require('../styles/transitions'); -let Children = require('../utils/children'); -let Dom = require('../utils/dom'); let KeyCode = require('../utils/key-code'); let List = require('../lists/list'); @@ -61,11 +59,11 @@ let Menu = React.createClass({ if (this.props.autoWidth) this._setWidth(); }, - componentDidUpdate(prevProps) { + componentDidUpdate() { if (this.props.autoWidth) this._setWidth(); }, - componentWillUpdate(nextProps, nextState) { + componentWillUpdate(nextProps) { let openChanged = nextProps.open !== this.props.open; if (openChanged && !nextProps.open) { this.setState({ diff --git a/src/raised-button.jsx b/src/raised-button.jsx index 9889a13fa496cb..395c15d49dacec 100644 --- a/src/raised-button.jsx +++ b/src/raised-button.jsx @@ -64,7 +64,7 @@ let RaisedButton = React.createClass({ let disabledColor = this.props.disabledBackgroundColor ? this.props.disabledBackgroundColor : this.getTheme().disabledColor; - return this.props.disabled ? disabledColor : + return this.props.disabled ? disabledColor : this.props.backgroundColor ? this.props.backgroundColor : this.props.primary ? this.getTheme().primaryColor : this.props.secondary ? this.getTheme().secondaryColor : @@ -75,7 +75,7 @@ let RaisedButton = React.createClass({ let disabledColor = this.props.disabledLabelColor ? this.props.disabledLabelColor : this.getTheme().disabledTextColor; - return this.props.disabled ? disabledColor : + return this.props.disabled ? disabledColor : this.props.labelColor ? this.props.labelColor : this.props.primary ? this.getTheme().primaryTextColor : this.props.secondary ? this.getTheme().secondaryTextColor : diff --git a/src/table/table-footer.jsx b/src/table/table-footer.jsx index cefc453495a955..4d69c06af0a565 100644 --- a/src/table/table-footer.jsx +++ b/src/table/table-footer.jsx @@ -65,7 +65,7 @@ let TableFooter = React.createClass({ ...props } = footerData[index]; if (content === undefined) content = footerData[index]; - let key = keyPrefix + index + let key = keyPrefix + index; props.style = (props.style !== undefined) ? this.mergeAndPrefix(props.style, styles.cell) : styles.cell; footers.push( diff --git a/src/table/table-header-column.jsx b/src/table/table-header-column.jsx index c31877266fa698..843dd885fec7e7 100644 --- a/src/table/table-header-column.jsx +++ b/src/table/table-header-column.jsx @@ -1,5 +1,4 @@ let React = require('react'); -let Checkbox = require('../checkbox'); let StylePropable = require('../mixins/style-propable'); let Tooltip = require('../tooltip'); diff --git a/src/table/table-header.jsx b/src/table/table-header.jsx index 113057e969fa9a..4ca204b1afa6ae 100644 --- a/src/table/table-header.jsx +++ b/src/table/table-header.jsx @@ -86,7 +86,6 @@ let TableHeader = React.createClass({ }, _getHeaderColumns(headerData, keyPrefix) { - let styles = this.getStyles(); let headers = []; for (let index = 0; index < headerData.length; index++) { diff --git a/src/table/table-row.jsx b/src/table/table-row.jsx index fe00eeb5a49fda..55a9ce6c6c1940 100644 --- a/src/table/table-row.jsx +++ b/src/table/table-row.jsx @@ -2,7 +2,6 @@ let React = require('react'); let Checkbox = require('../checkbox'); let StylePropable = require('../mixins/style-propable'); let TableRowColumn = require('./table-row-column'); -let Tooltip = require('../tooltip'); let TableRow = React.createClass({ diff --git a/src/table/table.jsx b/src/table/table.jsx index 31cfbd35928579..f1cb6f7027c1a9 100644 --- a/src/table/table.jsx +++ b/src/table/table.jsx @@ -4,7 +4,6 @@ let ClickAwayable = require('../mixins/click-awayable'); let TableHeader = require('./table-header'); let TableRow = require('./table-row'); let TableFooter = require('./table-footer'); -let DOM = require('../utils/dom'); let Table = React.createClass({ @@ -273,7 +272,7 @@ let Table = React.createClass({ column.style = { width: this.props.defaultColumnWidth, maxWidth: this.props.defaultColumnWidth - } + }; } else { if (column.style.width === undefined) column.style.width = this.props.defaultColumnWidth; @@ -325,7 +324,6 @@ let Table = React.createClass({ if (e.shiftKey && this.props.multiSelectable && selectedRows.length) { let lastSelection = selectedRows[selectedRows.length - 1]; - let start, end, direction; if (typeof lastSelection === 'object') { lastSelection.end = rowNumber; diff --git a/src/tabs/tabTemplate.jsx b/src/tabs/tabTemplate.jsx index afbf0f7ed3f4dd..bd531b3aa0f486 100644 --- a/src/tabs/tabTemplate.jsx +++ b/src/tabs/tabTemplate.jsx @@ -5,7 +5,7 @@ let TabTemplate = React.createClass({ render() { let styles = { - 'height': '0px', + 'height': 0, 'overflow': 'hidden', 'width': '100%', 'position': 'relative', @@ -13,8 +13,8 @@ let TabTemplate = React.createClass({ }; if (this.props.selected) { - delete styles.height - delete styles.overflow + delete styles.height; + delete styles.overflow; } return ( diff --git a/src/time-picker/clock-button.jsx b/src/time-picker/clock-button.jsx index 1fdcd80a3e869d..6ebb92b336a814 100644 --- a/src/time-picker/clock-button.jsx +++ b/src/time-picker/clock-button.jsx @@ -25,7 +25,7 @@ let ClockButton = React.createClass({ _handleTouchTap() { this.setState({ selected: true - }) + }); this.props.onTouchTap(); }, @@ -87,8 +87,8 @@ let ClockButton = React.createClass({ disableFocusRipple={true} disableTouchRipple={true} onTouchTap={this._handleTouchTap}> - - {this.props.children} + + {this.props.children} ); } diff --git a/src/time-picker/clock-hours.jsx b/src/time-picker/clock-hours.jsx index 9a03592cab9560..07faeacb64b14e 100644 --- a/src/time-picker/clock-hours.jsx +++ b/src/time-picker/clock-hours.jsx @@ -5,7 +5,7 @@ let ClockPointer = require("./clock-pointer"); function rad2deg(rad) { - return rad * 57.29577951308232 + return rad * 57.29577951308232; } function getTouchEventOffsetValues(e) { @@ -105,7 +105,7 @@ let ClockHours = React.createClass({ let cx = this.basePoint.x - this.center.x; let cy = this.basePoint.y - this.center.y; - let atan = Math.atan2(cx, cy) - Math.atan2(x, y); + let atan = Math.atan2(cx, cy) - Math.atan2(x, y); let deg = rad2deg(atan); deg = Math.round(deg / step ) * step; @@ -154,7 +154,7 @@ let ClockHours = React.createClass({ return hours.map((hour) => { let isSelected = this._getSelected() == hour; - return ; + return ; }); }, diff --git a/src/time-picker/clock-minutes.jsx b/src/time-picker/clock-minutes.jsx index 21f6f791a73aab..489742dd851277 100644 --- a/src/time-picker/clock-minutes.jsx +++ b/src/time-picker/clock-minutes.jsx @@ -5,7 +5,7 @@ let ClockPointer = require("./clock-pointer"); function rad2deg(rad) { - return rad * 57.29577951308232 + return rad * 57.29577951308232; } function getTouchEventOffsetValues(e) { @@ -103,7 +103,7 @@ let ClockMinutes = React.createClass({ let cx = this.basePoint.x - this.center.x; let cy = this.basePoint.y - this.center.y; - let atan = Math.atan2(cx, cy) - Math.atan2(x, y); + let atan = Math.atan2(cx, cy) - Math.atan2(x, y); let deg = rad2deg(atan); deg = Math.round(deg / step ) * step; @@ -157,9 +157,9 @@ let ClockMinutes = React.createClass({ return (
    - + {minutes.numbers} -
    +
    ); } diff --git a/src/time-picker/clock-number.jsx b/src/time-picker/clock-number.jsx index 5a9f89153d951b..2954bdc1ce945f 100644 --- a/src/time-picker/clock-number.jsx +++ b/src/time-picker/clock-number.jsx @@ -87,7 +87,7 @@ let ClockNumber = React.createClass({ pointerEvents: "none", boxSizing: "border-box" } - } + }; if (this.props.isSelected) { styles.root.backgroundColor = this.getTheme().accentColor; diff --git a/src/time-picker/clock-pointer.jsx b/src/time-picker/clock-pointer.jsx index b50b9a3c5583a7..7ee74241bb8a7f 100644 --- a/src/time-picker/clock-pointer.jsx +++ b/src/time-picker/clock-pointer.jsx @@ -81,8 +81,8 @@ let ClockPointer = React.createClass({ transform: "rotateZ(" + angle + "deg)" }, mark: { - background: this.getTheme().selectTextColor, - border: "4px solid " + this.getTheme().accentColor, + background: this.getTheme().selectTextColor, + border: "4px solid " + this.getTheme().accentColor, width: "7px", height: "7px", position: "absolute", diff --git a/src/time-picker/clock.jsx b/src/time-picker/clock.jsx index e90f171de2a467..9d9da06665f200 100644 --- a/src/time-picker/clock.jsx +++ b/src/time-picker/clock.jsx @@ -20,7 +20,7 @@ let Clock = React.createClass({ init() { this.setState({ mode: 'hour' - }) + }); }, getDefaultProps() { @@ -46,7 +46,7 @@ let Clock = React.createClass({ setTimeout(() => { this.setState({ mode: mode - }) + }); }, 100); }, @@ -146,7 +146,7 @@ let Clock = React.createClass({ setTimeout(() => { this.setState({ mode: 'minute' - }) + }); }, 100); } }, diff --git a/src/time-picker/time-display.jsx b/src/time-picker/time-display.jsx index 507412b7481dde..6990b1eace203c 100644 --- a/src/time-picker/time-display.jsx +++ b/src/time-picker/time-display.jsx @@ -93,7 +93,7 @@ let TimeDisplay = React.createClass({ hour: {}, minute: {} - } + }; let [hour, min] = this.sanitizeTime(); diff --git a/src/time-picker/time-picker.jsx b/src/time-picker/time-picker.jsx index da46ce8714e5cf..1df45c88725c24 100644 --- a/src/time-picker/time-picker.jsx +++ b/src/time-picker/time-picker.jsx @@ -50,7 +50,7 @@ let TimePicker = React.createClass({ if (this.props.format === "ampm"){ let isAM = hours < 12; hours = hours % 12; - aditional += isAM ? " am" : " pm"; + aditional += isAM ? " am" : " pm"; hours = hours || 12; } @@ -60,7 +60,7 @@ let TimePicker = React.createClass({ if (hours.length < 2) hours = "0" + hours; if (mins.length < 2) mins = "0" + mins; - return hours + ":" + mins + aditional; + return hours + ":" + mins + aditional; }, render() { diff --git a/src/toggle.jsx b/src/toggle.jsx index 25e35abca8afe1..a2bae903eb8739 100644 --- a/src/toggle.jsx +++ b/src/toggle.jsx @@ -125,7 +125,7 @@ let Toggle = React.createClass({ left: '-10' }, this.props.rippleStyle); - let rippleColor = this.state.switched ? + let rippleColor = this.state.switched ? this.getTheme().thumbOnColor : this.context.muiTheme.component.textColor; let iconStyle = this.mergeAndPrefix( diff --git a/src/utils/color-manipulator.js b/src/utils/color-manipulator.js index 1081d9bf7e882d..fca7cb45104018 100644 --- a/src/utils/color-manipulator.js +++ b/src/utils/color-manipulator.js @@ -104,7 +104,7 @@ module.exports = { if (color.type.indexOf('hsl') > -1) { color.values[2] += amount; - return this._decomposeColor(this._convertColorToString(color)); + return this._decomposeColor(this._convertColorToString(color)); } else if (color.type.indexOf('rgb') > -1) { for (let i = 0; i < 3; i++) { @@ -115,7 +115,7 @@ module.exports = { if (color.type.indexOf('a') <= -1) color.type += 'a'; - return this._convertColorToString(color, '0.15'); + return this._convertColorToString(color, '0.15'); }, darken(color, amount) { @@ -123,7 +123,7 @@ module.exports = { if (color.type.indexOf('hsl') > -1) { color.values[2] += amount; - return this._decomposeColor(this._convertColorToString(color)); + return this._decomposeColor(this._convertColorToString(color)); } else if (color.type.indexOf('rgb') > -1) { for (let i = 0; i < 3; i++) { From d3d5be5c4de8660659be02fcdf0009db7ddb142a Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 13:02:41 -0500 Subject: [PATCH 11/16] [Menus] Make menus scrollable by giving a maxHeight --- .../pages/components/icon-menus.jsx | 75 ++++++++++++++++--- src/lists/list.jsx | 7 +- src/menus/icon-menu.jsx | 3 + src/menus/menu.jsx | 42 +++++++---- src/paper.jsx | 8 +- 5 files changed, 106 insertions(+), 29 deletions(-) diff --git a/docs/src/app/components/pages/components/icon-menus.jsx b/docs/src/app/components/pages/components/icon-menus.jsx index 076f7c8ea0b9ec..660645fcfb6dad 100644 --- a/docs/src/app/components/pages/components/icon-menus.jsx +++ b/docs/src/app/components/pages/components/icon-menus.jsx @@ -219,15 +219,72 @@ class IconMenus extends React.Component { - }>Preview - }>Share - }>Get link - - }>Make a copy - }>Download - - }>Remove - + }>Preview + }>Share + }>Get link + + }>Make a copy + }>Download + + }>Remove + + + + Alabama + Alaska + Arizona + Arkansas + California + Colorado + Connecticut + Delaware + District Of Columbia + Florida + Georgia + Hawaii + Idaho + Illinois + Indiana + Iowa + Kansas + Kentucky + Louisiana + Maine + Maryland + Massachusetts + Michigan + Minnesota + Mississippi + Missouri + Montana + Nebraska + Nevada + New Hampshire + New Jersey + New Mexico + New York + North Carolina + North Dakota + Ohio + Oklahoma + Oregon + Pennsylvania + Rhode Island + South Carolina + South Dakota + Tennessee + Texas + Utah + Vermont + Virginia + Washington + West Virginia + Wisconsin + Wyoming +

    diff --git a/src/lists/list.jsx b/src/lists/list.jsx index dfc85914ba28a7..9fcc3a34d9b8aa 100644 --- a/src/lists/list.jsx +++ b/src/lists/list.jsx @@ -15,7 +15,8 @@ let List = React.createClass({ propTypes: { insetSubheader: React.PropTypes.bool, subheader: React.PropTypes.string, - subheaderStyle: React.PropTypes.object + subheaderStyle: React.PropTypes.object, + zDepth: React.PropTypes.oneOf([0,1,2,3,4,5]) }, getDefaultProps() { @@ -31,6 +32,7 @@ let List = React.createClass({ style, subheader, subheaderStyle, + zDepth, ...other } = this.props; @@ -60,7 +62,8 @@ let List = React.createClass({ return ( + style={mergedRootStyles} + zDepth={zDepth}> {subheaderElement} {this.props.children} diff --git a/src/menus/icon-menu.jsx b/src/menus/icon-menu.jsx index beb4acf784beb6..364cb7c6a6baac 100644 --- a/src/menus/icon-menu.jsx +++ b/src/menus/icon-menu.jsx @@ -25,6 +25,7 @@ let IconMenu = React.createClass({ ]), onItemKeyboardActivate: React.PropTypes.func, onItemTouchTap: React.PropTypes.func, + maxHeight: React.PropTypes.number, menuStyle: React.PropTypes.object, menuListStyle: React.PropTypes.object, onKeyDown: React.PropTypes.func, @@ -60,6 +61,7 @@ let IconMenu = React.createClass({ onChange, onKeyDown, onItemTouchTap, + maxHeight, menuStyle, menuListStyle, style, @@ -110,6 +112,7 @@ let IconMenu = React.createClass({ {}, onItemTouchTap: () => {}, open: true, @@ -80,6 +84,7 @@ let Menu = React.createClass({ children, desktop, listStyle, + maxHeight, multiple, open, openDirection, @@ -88,6 +93,7 @@ let Menu = React.createClass({ value, valueLink, width, + zDepth, ...other } = this.props; @@ -107,7 +113,8 @@ let Menu = React.createClass({ left: !openLeft ? 0 : null, right: openLeft ? 0 : null, transform: open ? 'scaleX(1)' : 'scaleX(0)', - transformOrigin: openLeft ? 'right' : 'left' + transformOrigin: openLeft ? 'right' : 'left', + }, list: { @@ -115,12 +122,7 @@ let Menu = React.createClass({ paddingBottom: desktop ? 16 : 8, paddingTop: desktop ? 16 : 8, userSelect: 'none', - width: width, - transition: Transitions.easeOut(null, ['transform', 'opacity']), - transitionDuration: open ? '500ms' : '200ms', - transform: open ? 'scaleY(1) translate3d(0,0,0)' : 'scaleY(0) translate3d(0,-8px,0)', - transformOrigin: openDown ? 'top' : 'bottom', - opacity: open ? 1 : 0 + width: width }, menuItem: { @@ -129,6 +131,16 @@ let Menu = React.createClass({ opacity: open ? 1 : 0 }, + paper: { + transition: Transitions.easeOut(null, ['transform', 'opacity']), + transitionDuration: open ? '500ms' : '200ms', + transform: open ? 'scaleY(1) translate3d(0,0,0)' : 'scaleY(0) translate3d(0,-8px,0)', + transformOrigin: openDown ? 'top' : 'bottom', + opacity: open ? 1 : 0, + maxHeight: maxHeight, + overflowY: maxHeight ? 'scroll' : null + }, + selectedMenuItem: { color: this.context.muiTheme.palette.accent1Color } @@ -168,12 +180,14 @@ let Menu = React.createClass({
    - - {newChildren} - + + + {newChildren} + +
    ); }, diff --git a/src/paper.jsx b/src/paper.jsx index 2e0288f3998dbd..76b2e734999c0f 100644 --- a/src/paper.jsx +++ b/src/paper.jsx @@ -14,16 +14,16 @@ let Paper = React.createClass({ propTypes: { circle: React.PropTypes.bool, rounded: React.PropTypes.bool, - zDepth: React.PropTypes.oneOf([0,1,2,3,4,5]), - transitionEnabled: React.PropTypes.bool + transitionEnabled: React.PropTypes.bool, + zDepth: React.PropTypes.oneOf([0,1,2,3,4,5]) }, getDefaultProps() { return { circle: false, rounded: true, - zDepth: 1, - transitionEnabled: true + transitionEnabled: true, + zDepth: 1 }; }, From 25cb5309e95abfaaec173ca17b7d5e531ca0d2bd Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 14:09:53 -0500 Subject: [PATCH 12/16] [Menu] Fixed children cascading when menu has a MaxHeight --- src/menus/menu.jsx | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 09ee80ecf98eca..72b4a4e22a9f26 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -113,8 +113,7 @@ let Menu = React.createClass({ left: !openLeft ? 0 : null, right: openLeft ? 0 : null, transform: open ? 'scaleX(1)' : 'scaleX(0)', - transformOrigin: openLeft ? 'right' : 'left', - + transformOrigin: openLeft ? 'right' : 'left' }, list: { @@ -151,22 +150,22 @@ let Menu = React.createClass({ //Cascade children opacity let childrenTransitionDelay = openDown ? 175 : 325; - let childrenTransitionDelayIncrement = Math.ceil(150/React.Children.count(this.props.children)); + let cascadeChildrenCount = this._getCascadeChildrenCount(); + let childrenTransitionDelayIncrement = Math.ceil(150/cascadeChildrenCount); let menuItemIndex = 0; let newChildren = React.Children.map(children, (child) => { - if (openDown) { - childrenTransitionDelay += childrenTransitionDelayIncrement; - } else { - childrenTransitionDelay -= childrenTransitionDelayIncrement; - } + let childIsADivider = child.type.displayName === 'MenuDivider'; + + childrenTransitionDelay = openDown ? + childrenTransitionDelay + childrenTransitionDelayIncrement : + childrenTransitionDelay - childrenTransitionDelayIncrement; let childrenContainerStyles = this.mergeStyles(styles.menuItem, { transitionDelay: open ? childrenTransitionDelay + 'ms' : '0ms' }); - let childIsADivider = child.type.displayName === 'MenuDivider'; let clonedChild = childIsADivider ? child : this._cloneMenuItem(child, menuItemIndex, styles); @@ -252,6 +251,34 @@ let Menu = React.createClass({ this._setFocusIndex(index, true); }, + _getCascadeChildrenCount() { + let { + children, + desktop, + maxHeight + } = this.props; + let count = 1; + let currentHeight = desktop ? 16 : 8; + let menuItemHeight = desktop ? 32 : 48; + + //MaxHeight isn't set - cascade all of the children + if (!maxHeight) return React.Children.count(children); + + //Count all the children that will fit inside the + //max menu height + React.Children.forEach(children, (child) => { + if (currentHeight < maxHeight) { + let childIsADivider = child.type.displayName === 'MenuDivider'; + + currentHeight += childIsADivider ? 16 : menuItemHeight; + count++; + } + }); + + return count; + + }, + _getMenuItemCount() { let dividerCount = 0; React.Children.forEach(this.props.children, (child) => { From a7e0b08556776810739bf11f704db67afc41d6be Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 16:37:56 -0500 Subject: [PATCH 13/16] [Menu]Focus and scroll is not being set to the currently selected menu item --- .../pages/components/icon-menus.jsx | 18 +++- src/menus/menu.jsx | 97 ++++++++++++++++--- 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/docs/src/app/components/pages/components/icon-menus.jsx b/docs/src/app/components/pages/components/icon-menus.jsx index 660645fcfb6dad..1d12dabceaa924 100644 --- a/docs/src/app/components/pages/components/icon-menus.jsx +++ b/docs/src/app/components/pages/components/icon-menus.jsx @@ -22,11 +22,13 @@ class IconMenus extends React.Component { this._handleIconMenuChange = this._handleIconMenuChange.bind(this); this._handleIconMenuMultiChange = this._handleIconMenuMultiChange.bind(this); this._handleIconMenuValueLinkChange = this._handleIconMenuValueLinkChange.bind(this); + this._handleIconMenuUsStateChange = this._handleIconMenuUsStateChange.bind(this); this.state = { iconMenuValue: '1', iconMenuMultiValue: ['2', '4'], - iconMenuValueLink: '1' + iconMenuValueLink: '1', + usState: 'TX' }; } @@ -124,6 +126,11 @@ class IconMenus extends React.Component { requestChange: this._handleIconMenuValueLinkChange }; + let usStateValueLink = { + value: this.state.usState, + requestChange: this._handleIconMenuUsStateChange + }; + return ( + openDirection="bottom-right" + valueLink={usStateValueLink}> Alabama Alaska Arizona @@ -304,6 +312,12 @@ class IconMenus extends React.Component { }); } + _handleIconMenuUsStateChange(e, value) { + this.setState({ + usState: value + }); + } + _handleIconMenuValueLinkChange(e, value) { this.setState({ iconMenuValueLink: value diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 72b4a4e22a9f26..79e16857c32079 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -63,17 +63,29 @@ let Menu = React.createClass({ if (this.props.autoWidth) this._setWidth(); }, - componentDidUpdate() { + componentDidUpdate(prevProps) { + let openChanged = prevProps.open !== this.props.open; + let justOpened = openChanged && this.props.open; + if (this.props.autoWidth) this._setWidth(); + if (justOpened) this._setScollPosition(); }, - componentWillUpdate(nextProps) { + componentWillReceiveProps(nextProps) { let openChanged = nextProps.open !== this.props.open; - if (openChanged && !nextProps.open) { + let isOpening = openChanged && nextProps.open; + let isClosing = openChanged && !nextProps.open; + + if (isClosing) { this.setState({ focusIndex: 0, isKeyboardFocused: false }); + } else if (isOpening) { + let selectedIndex = this._getSelectedIndex(); + this.setState({ + focusIndex: selectedIndex >= 0 ? selectedIndex : 0 + }); } }, @@ -149,21 +161,28 @@ let Menu = React.createClass({ let mergedListStyles = this.mergeStyles(styles.list, listStyle); //Cascade children opacity - let childrenTransitionDelay = openDown ? 175 : 325; + let cumulativeDelay = openDown ? 175 : 325; let cascadeChildrenCount = this._getCascadeChildrenCount(); - let childrenTransitionDelayIncrement = Math.ceil(150/cascadeChildrenCount); + let cumulativeDelayIncrement = Math.ceil(150/cascadeChildrenCount); let menuItemIndex = 0; let newChildren = React.Children.map(children, (child) => { let childIsADivider = child.type.displayName === 'MenuDivider'; - - childrenTransitionDelay = openDown ? - childrenTransitionDelay + childrenTransitionDelayIncrement : - childrenTransitionDelay - childrenTransitionDelayIncrement; + let focusIndex = this.state.focusIndex; + let transitionDelay = 0; + + //Only cascade the visible menu items + if (open && (menuItemIndex >= focusIndex - 1) && + (menuItemIndex <= focusIndex + cascadeChildrenCount - 1)) { + cumulativeDelay = openDown ? + cumulativeDelay + cumulativeDelayIncrement : + cumulativeDelay - cumulativeDelayIncrement; + transitionDelay = cumulativeDelay; + } let childrenContainerStyles = this.mergeStyles(styles.menuItem, { - transitionDelay: open ? childrenTransitionDelay + 'ms' : '0ms' + transitionDelay: transitionDelay + 'ms' }); let clonedChild = childIsADivider ? child : @@ -179,7 +198,10 @@ let Menu = React.createClass({
    - + { + let childIsADivider = child.type.displayName === 'MenuDivider'; + + if (this._isChildSelected(child)) selectedIndex = menuItemIndex; + if (!childIsADivider) menuItemIndex++; + }.bind(this)); + + return selectedIndex; + }, + _handleKeyDown(e) { if (this.props.open) { switch (e.keyCode) { @@ -341,6 +380,15 @@ let Menu = React.createClass({ this._setFocusIndex(index, true); }, + _isChildSelected(child) { + let multiple = this.props.multiple; + let menuValue = this.getValueLink(this.props).value; + let childValue = child.props.value; + + return (multiple && menuValue.length && menuValue.indexOf(childValue) !== -1) || + (!multiple && menuValue && menuValue === childValue); + }, + _setFocusIndex(newIndex, isKeyboardFocused) { this.setState({ focusIndex: newIndex, @@ -348,6 +396,23 @@ let Menu = React.createClass({ }); }, + _setScollPosition() { + let desktop = this.props.desktop; + let focusedMenuItem = this.refs.focusedMenuItem; + let menuItemHeight = desktop ? 32 : 48; + + if (focusedMenuItem) { + let selectedOffSet = React.findDOMNode(focusedMenuItem).offsetTop; + + //Make the focused item be the 2nd item in the list the + //user sees + let scrollTop = selectedOffSet - menuItemHeight; + if (scrollTop < menuItemHeight) scrollTop = 0; + + React.findDOMNode(this.refs.scrollContainer).scrollTop = scrollTop; + } + }, + _setWidth() { let el = React.findDOMNode(this); let listEl = React.findDOMNode(this.refs.list); From c2c1ad4f569673753375ada44c34a484a7c28620 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 16:41:43 -0500 Subject: [PATCH 14/16] [Menu] eslint fix --- src/menus/menu.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 79e16857c32079..0c404e9a692938 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -318,7 +318,7 @@ let Menu = React.createClass({ let selectedIndex = -1; let menuItemIndex = 0; - React.Children.forEach(children, (child, index) => { + React.Children.forEach(children, (child) => { let childIsADivider = child.type.displayName === 'MenuDivider'; if (this._isChildSelected(child)) selectedIndex = menuItemIndex; From c3286e736903cbfb0e190fbd9ae69dde8742719d Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 17:29:49 -0500 Subject: [PATCH 15/16] [Menu] Fix keyboard access for disabled menu items --- .../pages/components/icon-menus.jsx | 49 ++++++++++++------- src/menus/menu.jsx | 15 +++--- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/docs/src/app/components/pages/components/icon-menus.jsx b/docs/src/app/components/pages/components/icon-menus.jsx index 1d12dabceaa924..6980beab2dcfc4 100644 --- a/docs/src/app/components/pages/components/icon-menus.jsx +++ b/docs/src/app/components/pages/components/icon-menus.jsx @@ -7,9 +7,11 @@ let MoreVertIcon = require('svg-icons/navigation/more-vert'); let ComponentDoc = require('../../component-doc'); let ContentCopy = require('svg-icons/content/content-copy'); +let ContentFilter = require('svg-icons/content/filter-list'); let ContentLink = require('svg-icons/content/link'); let Delete = require('svg-icons/action/delete'); let Download = require('svg-icons/file/file-download'); +let MapsPlace = require('svg-icons/maps/place'); let PersonAdd = require('svg-icons/social/person-add'); let RemoveRedEye = require('svg-icons/image/remove-red-eye'); @@ -115,11 +117,9 @@ class IconMenus extends React.Component { } ]; - let iconButtonElement = ( - - - - ); + let iconButtonElement = ; + let filterButtonElement = ; + let mapsButtonElement = ; let iconMenuValueLink = { value: this.state.iconMenuValueLink, @@ -140,6 +140,7 @@ class IconMenus extends React.Component {
    +

    Menu with various open directions Refresh Send Feedback @@ -177,10 +178,9 @@ class IconMenus extends React.Component { Help Sign out +

    -

    - -

    Menu with value +

    Menu with value, valueLink, multiple values Help Sign out -

    -

    Menu with valueLink Help Sign out -

    -

    Menu with multiple values - Refresh - Send Feedback - Settings - Help - Sign out + Blu-ray + Cassette + CD + DVD Audio + Hybrid SACD + Vinyl

    Menu Item variations + + Home + Back + Forward + + Recently closed + Google + YouTube + + @@ -235,9 +244,11 @@ class IconMenus extends React.Component { }>Remove +

    +

    Scrollable diff --git a/src/menus/menu.jsx b/src/menus/menu.jsx index 0c404e9a692938..c376bc9d62e950 100644 --- a/src/menus/menu.jsx +++ b/src/menus/menu.jsx @@ -169,6 +169,7 @@ let Menu = React.createClass({ let newChildren = React.Children.map(children, (child) => { let childIsADivider = child.type.displayName === 'MenuDivider'; + let childIsDisabled = child.props.disabled; let focusIndex = this.state.focusIndex; let transitionDelay = 0; @@ -185,10 +186,10 @@ let Menu = React.createClass({ transitionDelay: transitionDelay + 'ms' }); - let clonedChild = childIsADivider ? child : + let clonedChild = childIsADivider || childIsDisabled ? child : this._cloneMenuItem(child, menuItemIndex, styles); - if (!childIsADivider) menuItemIndex++; + if (!childIsADivider && !childIsDisabled) menuItemIndex++; return

    {clonedChild}
    ; @@ -302,13 +303,13 @@ let Menu = React.createClass({ }, _getMenuItemCount() { - let dividerCount = 0; + let menuItemCount = 0; React.Children.forEach(this.props.children, (child) => { - if (child.type.displayName === 'MenuDivider') { - dividerCount++; - } + let childIsADivider = child.type.displayName === 'MenuDivider'; + let childIsDisabled = child.props.disabled; + if (!childIsADivider && !childIsDisabled) menuItemCount++; }); - return React.Children.count(this.props.children) - dividerCount; + return menuItemCount; }, _getSelectedIndex() { From 3e0255bc85e1ffbc5912cd67eb0db832e4882af5 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 5 Jul 2015 17:37:23 -0500 Subject: [PATCH 16/16] [docs] Added more scrollable examples --- .../pages/components/icon-menus.jsx | 176 ++++++++++++++++++ src/menus/icon-menu.jsx | 2 +- 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/docs/src/app/components/pages/components/icon-menus.jsx b/docs/src/app/components/pages/components/icon-menus.jsx index 6980beab2dcfc4..84fbf8eece5fdc 100644 --- a/docs/src/app/components/pages/components/icon-menus.jsx +++ b/docs/src/app/components/pages/components/icon-menus.jsx @@ -247,6 +247,7 @@ class IconMenus extends React.Component {

    Scrollable + Wisconsin Wyoming + + + Alabama + Alaska + Arizona + Arkansas + California + Colorado + Connecticut + Delaware + District Of Columbia + Florida + Georgia + Hawaii + Idaho + Illinois + Indiana + Iowa + Kansas + Kentucky + Louisiana + Maine + Maryland + Massachusetts + Michigan + Minnesota + Mississippi + Missouri + Montana + Nebraska + Nevada + New Hampshire + New Jersey + New Mexico + New York + North Carolina + North Dakota + Ohio + Oklahoma + Oregon + Pennsylvania + Rhode Island + South Carolina + South Dakota + Tennessee + Texas + Utah + Vermont + Virginia + Washington + West Virginia + Wisconsin + Wyoming + + + + Alabama + Alaska + Arizona + Arkansas + California + Colorado + Connecticut + Delaware + District Of Columbia + Florida + Georgia + Hawaii + Idaho + Illinois + Indiana + Iowa + Kansas + Kentucky + Louisiana + Maine + Maryland + Massachusetts + Michigan + Minnesota + Mississippi + Missouri + Montana + Nebraska + Nevada + New Hampshire + New Jersey + New Mexico + New York + North Carolina + North Dakota + Ohio + Oklahoma + Oregon + Pennsylvania + Rhode Island + South Carolina + South Dakota + Tennessee + Texas + Utah + Vermont + Virginia + Washington + West Virginia + Wisconsin + Wyoming + + + + Alabama + Alaska + Arizona + Arkansas + California + Colorado + Connecticut + Delaware + District Of Columbia + Florida + Georgia + Hawaii + Idaho + Illinois + Indiana + Iowa + Kansas + Kentucky + Louisiana + Maine + Maryland + Massachusetts + Michigan + Minnesota + Mississippi + Missouri + Montana + Nebraska + Nevada + New Hampshire + New Jersey + New Mexico + New York + North Carolina + North Dakota + Ohio + Oklahoma + Oregon + Pennsylvania + Rhode Island + South Carolina + South Dakota + Tennessee + Texas + Utah + Vermont + Virginia + Washington + West Virginia + Wisconsin + Wyoming + +

    diff --git a/src/menus/icon-menu.jsx b/src/menus/icon-menu.jsx index 364cb7c6a6baac..4632e47c36a602 100644 --- a/src/menus/icon-menu.jsx +++ b/src/menus/icon-menu.jsx @@ -172,7 +172,7 @@ let IconMenu = React.createClass({ setTimeout(() => { this.close(); this.props.onItemTouchTap(e, child); - }, 200); + }, 150); } });