'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

var _Menu = require('../../styles/cspace-input/Menu.css');

var _Menu2 = _interopRequireDefault(_Menu);

var _MenuItem = require('../../styles/cspace-input/MenuItem.css');

var _MenuItem2 = _interopRequireDefault(_MenuItem);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var propTypes = {
  options: _react.PropTypes.arrayOf(_react.PropTypes.shape({
    value: _react.PropTypes.string
  })),
  tabIndex: _react.PropTypes.string,
  renderItemLabel: _react.PropTypes.func,
  value: _react.PropTypes.string,
  onSelect: _react.PropTypes.func
};

var defaultProps = {
  options: [],
  // Display a no break space if the label is blank, so height is preserved.
  renderItemLabel: function renderItemLabel(label) {
    return label || ' ';
  },
  tabIndex: '0'
};

var stopPropagation = function stopPropagation(event) {
  event.stopPropagation();
};

var Menu = function (_Component) {
  _inherits(Menu, _Component);

  function Menu(props) {
    _classCallCheck(this, Menu);

    var _this = _possibleConstructorReturn(this, (Menu.__proto__ || Object.getPrototypeOf(Menu)).call(this, props));

    _this.handleBlur = _this.handleBlur.bind(_this);
    _this.handleFocus = _this.handleFocus.bind(_this);
    _this.handleItemClick = _this.handleItemClick.bind(_this);
    _this.handleItemMouseDownCapture = _this.handleItemMouseDownCapture.bind(_this);
    _this.handleKeyDown = _this.handleKeyDown.bind(_this);
    _this.handleKeyPress = _this.handleKeyPress.bind(_this);
    _this.handleRef = _this.handleRef.bind(_this);
    _this.handleSelectedItemRef = _this.handleSelectedItemRef.bind(_this);

    _this.state = {
      focusedIndex: null,
      value: props.value
    };
    return _this;
  }

  _createClass(Menu, [{
    key: 'componentWillReceiveProps',
    value: function componentWillReceiveProps(nextProps) {
      this.setState({
        value: nextProps.value
      });
    }
  }, {
    key: 'componentDidUpdate',
    value: function componentDidUpdate(prevProps, prevState) {
      if (prevState.focusedIndex !== this.state.focusedIndex && !this.isItemMouseDown) {
        this.scrollFocusedItemIntoView();
      }
    }
  }, {
    key: 'scrollFocusedItemIntoView',
    value: function scrollFocusedItemIntoView() {
      var focusedIndex = this.state.focusedIndex;


      if (focusedIndex !== null) {
        var focusedItem = this.domNode.querySelector('li:nth-of-type(' + (focusedIndex + 1) + ')');

        var focusedItemRect = focusedItem.getBoundingClientRect();
        var focusedItemTop = focusedItemRect.top;
        var focusedItemBottom = focusedItemRect.bottom;

        var menuRect = this.domNode.getBoundingClientRect();
        var menuTop = menuRect.top;
        var menuBottom = menuRect.bottom;

        if (focusedItemBottom >= menuBottom) {
          // Scroll the bottom of the item into view.

          var delta = Math.ceil(focusedItemBottom - menuBottom);

          this.domNode.scrollTop = this.domNode.scrollTop + delta;
        } else if (focusedItemTop <= menuTop) {
          // Scroll the top of the item into view.

          var _delta = Math.ceil(menuTop - focusedItemTop);

          this.domNode.scrollTop = this.domNode.scrollTop - _delta;
        }
      }
    }
  }, {
    key: 'focus',
    value: function focus() {
      if (this.domNode) {
        this.domNode.focus();
      }
    }
  }, {
    key: 'handleRef',
    value: function handleRef(ref) {
      this.domNode = ref;
    }
  }, {
    key: 'handleSelectedItemRef',
    value: function handleSelectedItemRef(ref) {
      this.selectedIndex = ref === null ? null : parseInt(ref.dataset.index, 10);
    }
  }, {
    key: 'handleBlur',
    value: function handleBlur() {
      this.setState({
        focusedIndex: null
      });
    }
  }, {
    key: 'handleFocus',
    value: function handleFocus() {
      this.setState({
        focusedIndex: this.selectedIndex || 0
      });
    }
  }, {
    key: 'handleItemClick',
    value: function handleItemClick(event) {
      var index = parseInt(event.currentTarget.dataset.index, 10);

      this.isItemMouseDown = false;
      this.selectItem(index);
    }
  }, {
    key: 'handleItemMouseDownCapture',
    value: function handleItemMouseDownCapture() {
      this.isItemMouseDown = true;
    }
  }, {
    key: 'handleKeyDown',
    value: function handleKeyDown(event) {
      if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
        // Prevent the page from scrolling.
        event.preventDefault();

        var focusedIndex = this.state.focusedIndex;
        var options = this.props.options;


        if (event.key === 'ArrowDown') {
          focusedIndex += 1;

          if (focusedIndex >= options.length) {
            focusedIndex = 0;
          }
        } else {
          focusedIndex -= 1;

          if (focusedIndex < 0) {
            focusedIndex = options.length - 1;
          }
        }

        this.setState({
          focusedIndex: focusedIndex
        });
      }
    }
  }, {
    key: 'handleKeyPress',
    value: function handleKeyPress(event) {
      if (event.key === 'Enter') {
        this.selectItem(this.state.focusedIndex);
      }
    }
  }, {
    key: 'selectItem',
    value: function selectItem(index) {
      var selectedOption = this.props.options[index];
      var value = selectedOption.value;

      this.setState({
        value: value,
        focusedIndex: index
      });

      var onSelect = this.props.onSelect;


      if (onSelect) {
        onSelect(selectedOption);
      }
    }
  }, {
    key: 'renderItems',
    value: function renderItems() {
      var _this2 = this;

      var _state = this.state,
          focusedIndex = _state.focusedIndex,
          value = _state.value;
      var _props = this.props,
          options = _props.options,
          renderItemLabel = _props.renderItemLabel;


      return options.map(function (option, index) {
        var _classNames;

        var optionValue = option.value,
            optionLabel = option.label,
            optionIndent = option.indent,
            optionStartGroup = option.startGroup;


        var className = (0, _classnames2.default)(optionIndent ? _MenuItem2.default['indent' + optionIndent] : null, (_classNames = {}, _defineProperty(_classNames, _MenuItem2.default.common, true), _defineProperty(_classNames, _MenuItem2.default.startGroup, !!optionStartGroup), _defineProperty(_classNames, _MenuItem2.default.selected, optionValue === value), _defineProperty(_classNames, _MenuItem2.default.focused, focusedIndex === index), _classNames));

        var ref = optionValue === value ? _this2.handleSelectedItemRef : null;

        // TODO: ARIA
        // https://www.w3.org/WAI/GL/wiki/index.php?title=Using_aria-activedescendant&oldid=2629

        return (
          /* eslint-disable jsx-a11y/no-static-element-interactions */
          _react2.default.createElement(
            'li',
            {
              className: className,
              'data-index': index,
              key: index,
              ref: ref,
              role: 'option',
              onClick: _this2.handleItemClick

              // Prevent flash of incorrectly focused item when clicking on an item in an unfocused
              // menu.
              , onMouseDown: stopPropagation

              // Track if the mouse is down on an item, using the capture phase so that the state
              // of the mouse may be updated before onFocus fires.
              , onMouseDownCapture: _this2.handleItemMouseDownCapture
            },
            renderItemLabel(optionLabel)
          )
          /* eslint-enable jsx-a11y/no-static-element-interactions */

        );
      });
    }
  }, {
    key: 'render',
    value: function render() {
      var tabIndex = this.props.tabIndex;


      var items = this.renderItems();

      if (items.length === 0) {
        return null;
      }

      // TODO: ARIA
      // https://www.w3.org/WAI/GL/wiki/index.php?title=Using_aria-activedescendant&oldid=2629

      return (
        /* eslint-disable jsx-a11y/no-static-element-interactions */
        _react2.default.createElement(
          'ul',
          {
            className: _Menu2.default.common,
            ref: this.handleRef,
            role: 'listbox',
            tabIndex: tabIndex,
            onBlur: this.handleBlur,
            onFocus: this.handleFocus,
            onKeyDown: this.handleKeyDown,
            onKeyPress: this.handleKeyPress
          },
          items
        )
        /* eslint-enable jsx-a11y/no-static-element-interactions */

      );
    }
  }]);

  return Menu;
}(_react.Component);

exports.default = Menu;


Menu.propTypes = propTypes;
Menu.defaultProps = defaultProps;