:doc
  @name UIFlyout
  recommended skins:
    %ui-kit/skins/flyout/index.ess
    %ui-kit/skins/flyout/position/bottom.ess
  @prop {Boolean}  [hideOnClick] Always hide flyout after anything is clicked
  @prop {String}   [position] applies state classes for position
  @prop {Boolean}  [showCloseButton=false] Shows close button that calls hideMenu
  @prop {Boolean}  [lazyYield] Do not yield the menu unless menu is active
  @prop {Boolean}  [manualHide] Only hide flyout by calling toggle or hide
  @prop {Boolean}  [showMenuOnLoad] Mounts with flyout open
  @prop {Number}   [animationTimeout=0] number in ms to wait before removing menu, or sending false value for isActive
  @prop {Boolean}  [scrollToMenu=true] body will scroll to fully show menu when active
  @prop {Object}   [scrollToMenuOptions={}] custom options for scroll-to-element (incl. top/bottomOffset for offset when above/below the fold)
  @prop {String}   [hideMenuKeybinding] key to listen for that will hide flyout
  @prop {String}   [showMenuKeybinding] key to listen for that will show flyout
  @prop {String}   [toggleMenuKeybinding] key to listen for that will toggle flyout
  @prop {Function} [onShow] called after flyout shows
  @prop {Function} [onHide] called after flyout hides
  @prop {Function} [onWindowClickHide] this is called when flyout will hide because of clicking outside the menu (onHide will also be called)
  @prop {Function} [onComponentWillHide] called when component will hide
  @prop {Funciton} [onComponentWillShow] called when component will show
  @prop {Function} [onKeybindingPress] called when a keybinding is pressed
    @arg {String} action - the keybinding action called (show, hide, toggle)

// import * as ReactDOM from 'react-dom'
import * as scrollToElement from 'scroll-to-element'
import elContains from '../../utils/element-contains-el'
import {attachKeybindings, removeKeybindings, hasKeybinding} from '../../utils/keybinding.js'

:js
  var classFn = this.composeClasses('UIFlyout', {
    'active': state.isActive,
    'has-trigger': !!props.trigger
  })

  var fns= {
    classFn: classFn,
    hide: this.startHidingMenu,
    show: this.startShowingMenu,
    toggle: this.toggleMenu }

  var menuStates = {
    'showing': state.isActive,
    'position-@': props.position
  }

div(className=classFn())
  yield trigger(fns, state.isActive)
  if props.lazyYield
    if state.isActive || state.showMenuStarted || state.hideMenuStarted
      div(className=classFn('&-menu', menuStates) ref='menu')
        yield menu(fns, state.isActive)
        if props.showCloseButton
          div(className=classFn('&-menu-close') onClick=this.startHidingMenu)
  else
    div(className=classFn('&-menu', menuStates) ref='menu')
      yield menu(fns, state.isActive)
      if props.showCloseButton
        div(className=classFn('&-menu-close') onClick=this.startHidingMenu)

:module
  export var mixins = [require('../../mixins/compose-classes')]

  export function getDefaultProps () {
    var noop = () => {};
    return {
      position: 'bottom-middle',
      animationTimeout: 0,
      scrollToMenu: true,
      scrollToMenuOptions: {},
      onShow: noop,
      onHide: noop,
      onWindowClickHide: noop,
      onComponentWillHide: noop,
      onComponentWillShow: noop,
      onKeybindingPress: noop
    };
  }

  export function componentDidMount () {
    this.setKeybindings();
    if (this.keybindings.show)   attachKeybindings(this.keybindings.show);
    if (this.keybindings.toggle) attachKeybindings(this.keybindings.toggle);
    if (this.props.showMenuOnLoad) this.startShowingMenu();
  }

  export function componentWillReceiveProps (nextProps) {
    if (!this.equalPairs(nextProps.showMenuKeybinding, this.props.showMenuKeybinding) ||
        !this.equalPairs(nextProps.hideMenuKeybinding, this.props.hideMenuKeybinding) ||
        !this.equalPairs(nextProps.toggleMenuKeybinding, this.props.toggleMenuKeybinding)) {
      removeKeybindings(this.getKeybindings());
      this.setKeybindings(nextProps);
      if (this.state.isActive) attachKeybindings(this.keybindings.hide)
      else attachKeybindings(Object.assign({},
        this.keybindings.show || {},
        this.keybindings.toggle || {}))
    }

    if (nextProps.showMenuOnLoad && !this.state.hasShownMenu) {
      this.startShowingMenu();
    }
  }

  export function componentDidUpdate (prevProps, prevState) {
    if (this.state.isActive && !prevState.isActive) {
      this.scrollToMenu();
      this.props.onShow();
      if (!this.props.manualHide) window.addEventListener('click', this.onWindowClick);
    }

    if (!this.state.isActive && prevState.isActive) {
      this.props.onHide();
    }

    if (this.state.hideMenuStarted && !prevState.hideMenuStarted) {
      setTimeout(this.hideMenu, this.props.animationTimeout);
    }

    if (this.state.showMenuStarted && !prevState.showMenuStarted) {
      setTimeout(this.showMenu, 0);
    }
  }

  export function componentWillUnmount () {
    window.removeEventListener('click', this.onWindowClick);
    removeKeybindings(this.getKeybindings());
    if (this.state.isActive && this.props.onHide) this.props.onHide();
  }

  export function setKeybindings (nextProps) {
    var props = nextProps || this.props;
    var kb = {};
    if (props.showMenuKeybinding)   kb.show   = {[props.showMenuKeybinding]  : this.onKeybindingPress.bind(null, 'show')};
    if (props.hideMenuKeybinding)   kb.hide   = {[props.hideMenuKeybinding]  : this.onKeybindingPress.bind(null, 'hide')};
    if (props.toggleMenuKeybinding) kb.toggle = {[props.toggleMenuKeybinding]: this.onKeybindingPress.bind(null, 'toggle')};
    this.keybindings = kb;
  }

  export function getKeybindings () {
    return Object.assign({},
        this.keybindings.show   || {},
        this.keybindings.hide   || {},
        this.keybindings.toggle || {});
  }

  export function onKeybindingPress (action) {
    this.props.onKeybindingPress(action);
    switch (action) {
      case 'toggle':
        this.toggleMenu();
        break;
      case 'show':
        this.startShowingMenu();
        break;
      case 'hide':
        this.startHidingMenu();
        break;
    }
  }

  export function toggleMenu (evt) {
    this.state.isActive ?
      this.startHidingMenu(evt) :
      this.startShowingMenu(evt);
  }

  export function startShowingMenu (evt) {
    if (this.state.isActive) return;
    this.props.onComponentWillShow();
    if (this.keybindings.show) removeKeybindings(this.keybindings.show);
    if (this.keybindings.hide) attachKeybindings(this.keybindings.hide);

    this.setState({showMenuStarted: true, hasShownMenu: true});
  }

  export function startHidingMenu (evt) {
    if (!this.state.isActive) return;
    if (evt) evt.stopPropagation();
    this.props.onComponentWillHide();
    if (this.keybindings.hide) removeKeybindings(this.keybindings.hide);
    if (this.keybindings.show) attachKeybindings(this.keybindings.show);
    this.setState({hideMenuStarted: true});
    window.removeEventListener('click', this.onWindowClick);
  }

  export function hideMenu () {
    this.setState({hideMenuStarted: false, isActive: false});
  }

  export function showMenu () {
    this.setState({showMenuStarted: false, isActive: true});
  }

  export function scrollToMenu () {
    var menu = this.getDOMNode().getElementsByClassName('UIFlyout-menu')[0];
    if (!menu || !this.props.scrollToMenu) return;
    var menuIsBelowFold = menu.getBoundingClientRect().bottom > window.innerHeight;
    var menuIsAboveFold = menu.getBoundingClientRect().top < 0;
    var align = menuIsAboveFold ? 'top' : 'bottom'
    var offset = menuIsAboveFold ? this.props.scrollToMenuOptions.topOffset : this.props.scrollToMenuOptions.bottomOffset

    var defaultOptions = {
      align,
      ease: 'inOutSine',
      duration: 150,
      offset: offset || 0
    };

    var scrollToMenuOptions = Object.assign(defaultOptions, this.props.scrollToMenuOptions);

    if ((menuIsBelowFold || menuIsAboveFold) && scrollToElement) {
      scrollToElement(menu, scrollToMenuOptions);
    }
  }

  export function onWindowClick (evt) {
    if (!this.isMounted()) return;
    // var el = this.getDOMNode ? this.getDOMNode() : ReactDOM.findDOMNode(this);
    var el = this.getDOMNode();
    if (this.props.hideOnClick || !elContains(el, evt.target)) {
      if (!elContains(el, evt.target)) this.props.onWindowClickHide();
      this.startHidingMenu();
    }
  }