import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["stylesMemoizer", "children", "className", "onClose", "title", "onHeightChange", "transitionType", "transitionDirection", "onTransitionComplete", "onUseKeyboardToNavigate", "items", "initialFocusedItemIndex", "showNextPanel", "showPreviousPanel"];
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

import React, { cloneElement, Component } from 'react';
import classNames from 'classnames';
import { tabbable } from 'tabbable';
import { withEuiStylesMemoizer, keys, htmlIdGenerator } from '../../services';
import { EuiResizeObserver } from '../observer/resize_observer';
import { EuiContextMenuItem } from './context_menu_item';
import { euiContextMenuPanelStyles } from './context_menu_panel.styles';
import { EuiContextMenuPanelTitle } from './context_menu_panel_title';
import { jsx as ___EmotionJSX } from "@emotion/react";
export var EuiContextMenuPanelClass = /*#__PURE__*/function (_Component) {
  function EuiContextMenuPanelClass(props) {
    var _this;
    _classCallCheck(this, EuiContextMenuPanelClass);
    _this = _callSuper(this, EuiContextMenuPanelClass, [props]);
    _defineProperty(_this, "_isMounted", false);
    _defineProperty(_this, "backButton", null);
    _defineProperty(_this, "panel", null);
    _defineProperty(_this, "initialPopoverParent", null);
    // Find all tabbable menu items on both panel init and
    // whenever `menuItems` resets when `props.items` changes
    _defineProperty(_this, "findMenuItems", function () {
      var _this$props$items;
      if (!_this.panel) return;
      if (!((_this$props$items = _this.props.items) !== null && _this$props$items !== void 0 && _this$props$items.length)) return; // We only need menu items/arrow key navigation for the `items` API
      if (_this.state.menuItems.length) return; // If we already have menu items, no need to continue

      var tabbableItems = tabbable(_this.panel);
      if (tabbableItems.length) {
        _this.setState({
          menuItems: tabbableItems
        });
      }
    });
    _defineProperty(_this, "focusMenuItem", function (direction) {
      var _this$state$menuItems;
      var indexOffset = direction === 'up' ? -1 : 1;
      var nextFocusedItemIndex;
      if (_this.state.focusedItemIndex === undefined) {
        // If this is the beginning of the user's keyboard navigation of the menu, then we'll focus
        // either the first or last item.
        nextFocusedItemIndex = direction === 'up' ? _this.state.menuItems.length - 1 : 0;
      } else {
        nextFocusedItemIndex = _this.state.focusedItemIndex + indexOffset;
        if (nextFocusedItemIndex < 0) {
          nextFocusedItemIndex = _this.state.menuItems.length - 1;
        } else if (nextFocusedItemIndex === _this.state.menuItems.length) {
          nextFocusedItemIndex = 0;
        }
      }
      _this.setState({
        focusedItemIndex: nextFocusedItemIndex
      });
      (_this$state$menuItems = _this.state.menuItems[nextFocusedItemIndex]) === null || _this$state$menuItems === void 0 || _this$state$menuItems.focus();
    });
    /* Ensures that on initial focus of a menuitem the index is updated.
    Otherwise `focusMenuItem()`is not initialized when pressing a key as `onKeyDown`
    only fires on key press after the focus moved into the component */
    _defineProperty(_this, "setInitialFocusedItemIndex", function (event) {
      var _this$props$items2;
      if (!((_this$props$items2 = _this.props.items) !== null && _this$props$items2 !== void 0 && _this$props$items2.length) || !_this.state.menuItems.length || _this.state.focusedItemIndex !== undefined) return;
      var target = event.target;
      var currentIndex = _this.state.menuItems.indexOf(target);
      if (currentIndex >= 0) {
        _this.setState({
          focusedItemIndex: currentIndex
        });
      }
    });
    _defineProperty(_this, "onKeyDown", function (event) {
      // If this panel contains items you can use the left arrow key to go back at any time.
      // But if it doesn't contain items, then you have to focus on the back button specifically,
      // since there could be content inside the panel which requires use of the left arrow key,
      // e.g. text inputs.
      var _this$props = _this.props,
        items = _this$props.items,
        onClose = _this$props.onClose,
        showPreviousPanel = _this$props.showPreviousPanel;
      if (onClose && (items !== null && items !== void 0 && items.length || document.activeElement === _this.backButton || document.activeElement === _this.panel)) {
        if (event.key === keys.ARROW_LEFT) {
          if (showPreviousPanel) {
            event.preventDefault();
            event.stopPropagation();
            showPreviousPanel();
            if (_this.props.onUseKeyboardToNavigate) {
              _this.props.onUseKeyboardToNavigate();
            }
          }
        }
      }
      if (items !== null && items !== void 0 && items.length) {
        switch (event.key) {
          case keys.TAB:
            requestAnimationFrame(function () {
              // NOTE: document.activeElement is stale if not wrapped in requestAnimationFrame
              var focusedItemIndex = _this.state.menuItems.indexOf(document.activeElement);

              // We need to sync our internal state with the user tabbing through items
              _this.setState({
                focusedItemIndex: focusedItemIndex >= 0 && focusedItemIndex < _this.state.menuItems.length ? focusedItemIndex : undefined
              });
            });
            break;
          case keys.ARROW_UP:
            event.preventDefault();
            _this.focusMenuItem('up');
            if (_this.props.onUseKeyboardToNavigate) {
              _this.props.onUseKeyboardToNavigate();
            }
            break;
          case keys.ARROW_DOWN:
            event.preventDefault();
            _this.focusMenuItem('down');
            if (_this.props.onUseKeyboardToNavigate) {
              _this.props.onUseKeyboardToNavigate();
            }
            break;
          case keys.ARROW_RIGHT:
            if (_this.props.showNextPanel) {
              event.preventDefault();
              _this.props.showNextPanel(onClose && _this.state.focusedItemIndex ? _this.state.focusedItemIndex - 1 // Account for panel title back button
              : _this.state.focusedItemIndex);
              if (_this.props.onUseKeyboardToNavigate) {
                _this.props.onUseKeyboardToNavigate();
              }
            }
            break;
          default:
            break;
        }
      }
    });
    _defineProperty(_this, "reclaimPopoverFocus", function () {
      _this.setState({
        waitingForInitialPopover: false
      });
      _this.takeInitialFocus();
    });
    _defineProperty(_this, "onTransitionComplete", function () {
      if (_this.props.onTransitionComplete) {
        _this.props.onTransitionComplete();
      }
    });
    _defineProperty(_this, "panelRef", function (node) {
      _this.panel = node;
      _this.updateHeight();
      _this.getInitialPopoverParent();
      _this.findMenuItems();
    });
    _defineProperty(_this, "rootId", htmlIdGenerator());
    _this.state = {
      prevProps: {
        items: _this.props.items
      },
      menuItems: [],
      focusedItemIndex: props.onClose && props.initialFocusedItemIndex != null && props.initialFocusedItemIndex !== -1 ? props.initialFocusedItemIndex + 1 // Account for panel title back button
      : props.initialFocusedItemIndex,
      currentHeight: undefined,
      waitingForInitialPopover: false,
      tookInitialFocus: false
    };
    return _this;
  }
  _inherits(EuiContextMenuPanelClass, _Component);
  return _createClass(EuiContextMenuPanelClass, [{
    key: "takeInitialFocus",
    value: function takeInitialFocus() {
      var _this2 = this;
      // Give positioning time to render before focus is applied. Otherwise page jumps.
      requestAnimationFrame(function () {
        if (!_this2._isMounted) {
          return;
        }

        // Don't take focus yet if EuiContextMenu is in a popover
        // and the popover is initially opening/transitioning in
        if (_this2.initialPopoverParent && _this2.state.waitingForInitialPopover) {
          return;
        }

        // Setting focus while transitioning causes the animation to glitch, so we have to wait
        // until it's finished before we focus anything.
        if (_this2.props.transitionType) {
          var _this2$panel;
          // If the panel is transitioning, set focus to the panel so that users using
          // arrow keys that are fast clickers don't accidentally get stranded focus
          // or trigger keystrokes when it shouldn't
          (_this2$panel = _this2.panel) === null || _this2$panel === void 0 || _this2$panel.focus({
            preventScroll: true
          });
          return;
        }

        // Initial focus has already been handled, no need to continue and potentially hijack/focus fight
        if (_this2.state.tookInitialFocus) {
          return;
        }

        // `initialFocusedItemIndex={-1}` should only be used when preventing initial item focus is desired
        if (_this2.state.focusedItemIndex === -1) {
          // Resetting the focusedItemIndex to 0 allows keyboard up/down behavior to
          // still work correctly later if the panel is manually tabbed into
          return _this2.setState({
            tookInitialFocus: true,
            focusedItemIndex: 0
          });
        }

        // If an item should be focused, focus it (if it exists)
        if (_this2.state.focusedItemIndex != null && _this2.state.menuItems.length) {
          var focusedItem = _this2.state.menuItems[_this2.state.focusedItemIndex];
          if (focusedItem) {
            focusedItem.focus();
            return _this2.setState({
              tookInitialFocus: true
            });
          }
        }

        // Otherwise, if the back button panel title is present, focus it
        if (_this2.backButton) {
          // Focus the back button for both `items` and `children` APIs
          _this2.backButton.focus();
          // If `items`, ensure our focused item index is correct
          if (_this2.state.menuItems.length) {
            _this2.setState({
              focusedItemIndex: 0
            });
          }
          return _this2.setState({
            tookInitialFocus: true
          });
        }

        // Focus on the panel as a last resort.
        if (_this2.panel && !_this2.panel.contains(document.activeElement)) {
          _this2.panel.focus();
          _this2.setState({
            tookInitialFocus: true
          });
        }
      });
    }
  }, {
    key: "componentDidUpdate",
    value: function componentDidUpdate(prevProps) {
      if (prevProps.items !== this.props.items) {
        this.findMenuItems();
      }
      // Focus isn't always ready to be taken on mount, so we need to call it
      // on update as well just in case
      this.takeInitialFocus();
    }
  }, {
    key: "componentDidMount",
    value: function componentDidMount() {
      // If EuiContextMenu is used within an EuiPopover, we need to wait for EuiPopover to:
      // 1. Correctly set its `returnFocus` to the toggling button,
      //    so focus is correctly restored to the popover toggle on close
      // 2. Finish its react-focus-on `autoFocus` behavior after transitioning in,
      //    so the panel can handle its own focus without focus fighting
      if (this.initialPopoverParent) {
        this.initialPopoverParent.addEventListener('focus', this.reclaimPopoverFocus, {
          once: true
        });
      } else {
        this.takeInitialFocus();
      }
      this._isMounted = true;
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      var _this$initialPopoverP;
      (_this$initialPopoverP = this.initialPopoverParent) === null || _this$initialPopoverP === void 0 || _this$initialPopoverP.removeEventListener('focus', this.reclaimPopoverFocus);
      this._isMounted = false;
    }
  }, {
    key: "updateHeight",
    value: function updateHeight() {
      var currentHeight = this.panel ? this.panel.clientHeight : 0;
      if (this.state.height !== currentHeight) {
        if (this.props.onHeightChange) {
          this.props.onHeightChange(currentHeight);
          this.setState({
            height: currentHeight
          });
        }
      }
    }
  }, {
    key: "getInitialPopoverParent",
    value: function getInitialPopoverParent() {
      var _parent$parentNode;
      // If `transitionType` exists, that means we're navigating between panels
      // and the initial popover has already loaded, so we shouldn't need this logic
      if (this.props.transitionType) return;
      if (!this.panel) return;
      var parent = this.panel.parentNode;
      if (!parent) return;
      var hasEuiContextMenuParent = parent.classList.contains('euiContextMenu');

      // It's possible to use an EuiContextMenuPanel directly in a popover without
      // an EuiContextMenu, so we need to account for that when searching parent nodes
      var popoverParent = hasEuiContextMenuParent ? parent === null || parent === void 0 || (_parent$parentNode = parent.parentNode) === null || _parent$parentNode === void 0 ? void 0 : _parent$parentNode.parentNode : parent === null || parent === void 0 ? void 0 : parent.parentNode;
      if (!popoverParent) return;
      var hasPopoverParent = !!popoverParent.dataset.popoverPanel;
      if (!hasPopoverParent) return;
      this.initialPopoverParent = popoverParent;
      this.setState({
        waitingForInitialPopover: true
      });
    }
  }, {
    key: "render",
    value: function render() {
      var _this3 = this;
      var _this$props2 = this.props,
        stylesMemoizer = _this$props2.stylesMemoizer,
        children = _this$props2.children,
        className = _this$props2.className,
        onClose = _this$props2.onClose,
        title = _this$props2.title,
        onHeightChange = _this$props2.onHeightChange,
        transitionType = _this$props2.transitionType,
        transitionDirection = _this$props2.transitionDirection,
        onTransitionComplete = _this$props2.onTransitionComplete,
        onUseKeyboardToNavigate = _this$props2.onUseKeyboardToNavigate,
        items = _this$props2.items,
        initialFocusedItemIndex = _this$props2.initialFocusedItemIndex,
        showNextPanel = _this$props2.showNextPanel,
        showPreviousPanel = _this$props2.showPreviousPanel,
        rest = _objectWithoutProperties(_this$props2, _excluded);
      var titleId = this.rootId('euiContextMenuPanelTitle');
      var classes = classNames('euiContextMenuPanel', className);
      var styles = stylesMemoizer(euiContextMenuPanelStyles);
      var cssStyles = [styles.euiContextMenuPanel, transitionDirection && transitionType && styles[transitionDirection][transitionType]];
      var panelTitle = title && ___EmotionJSX(EuiContextMenuPanelTitle, {
        title: title,
        id: titleId,
        className: "euiContextMenuPanel__title",
        onClose: onClose,
        buttonRef: function buttonRef(node) {
          _this3.backButton = node;
        }
      });
      var isMenu = !!(items && items.length);
      var content = items && items.length ? items.map(function (MenuItem) {
        var _MenuItem$props$role, _MenuItem$props;
        var cloneProps = {
          role: (_MenuItem$props$role = (_MenuItem$props = MenuItem.props) === null || _MenuItem$props === void 0 ? void 0 : _MenuItem$props.role) !== null && _MenuItem$props$role !== void 0 ? _MenuItem$props$role : 'menuitem'
        };
        return MenuItem.type === EuiContextMenuItem ? /*#__PURE__*/cloneElement(MenuItem, cloneProps) : MenuItem;
      }) : children;
      return ___EmotionJSX("div", _extends({
        ref: this.panelRef,
        css: cssStyles,
        className: classes,
        onKeyDown: this.onKeyDown,
        tabIndex: -1,
        onAnimationEnd: this.onTransitionComplete
      }, rest), panelTitle, ___EmotionJSX(EuiResizeObserver, {
        onResize: function onResize() {
          return _this3.updateHeight();
        }
      }, function (resizeRef) {
        return ___EmotionJSX("div", {
          ref: resizeRef,
          role: isMenu ? 'menu' : undefined,
          className: "euiContextMenuPanel__list",
          "aria-labelledby": title ? titleId : undefined,
          onFocus: _this3.setInitialFocusedItemIndex
        }, content);
      }));
    }
  }], [{
    key: "getDerivedStateFromProps",
    value: function getDerivedStateFromProps(nextProps, prevState) {
      var needsUpdate = false;
      var nextState = {};

      // Clear refs to menuItems if we're getting new ones.
      if (nextProps.items !== prevState.prevProps.items) {
        needsUpdate = true;
        nextState.menuItems = [];
        nextState.prevProps = {
          items: nextProps.items
        };
      }
      if (needsUpdate) {
        return nextState;
      }
      return null;
    }
  }]);
}(Component);
_defineProperty(EuiContextMenuPanelClass, "defaultProps", {
  items: []
});
export var EuiContextMenuPanel = withEuiStylesMemoizer(EuiContextMenuPanelClass);