"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.MenuArrowKeyNavigationProvider = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var hasEnabledItems = function hasEnabledItems(list) {
  return list.some(function (item) {
    return item.getAttribute('aria-disabled') !== 'true';
  });
};

/**
 * This component is a wrapper of vertical menus which listens to keydown events of children
 * and handles up/down arrow key navigation
 */
var MenuArrowKeyNavigationProvider = exports.MenuArrowKeyNavigationProvider = function MenuArrowKeyNavigationProvider(_ref) {
  var children = _ref.children,
    handleClose = _ref.handleClose,
    disableArrowKeyNavigation = _ref.disableArrowKeyNavigation,
    keyDownHandlerContext = _ref.keyDownHandlerContext,
    closeOnTab = _ref.closeOnTab,
    onSelection = _ref.onSelection,
    editorRef = _ref.editorRef;
  var wrapperRef = (0, _react.useRef)(null);
  var _useState = (0, _react.useState)(-1),
    _useState2 = (0, _slicedToArray2.default)(_useState, 2),
    currentSelectedItemIndex = _useState2[0],
    setCurrentSelectedItemIndex = _useState2[1];
  var _useState3 = (0, _react.useState)(editorRef.current),
    _useState4 = (0, _slicedToArray2.default)(_useState3, 1),
    listenerTargetElement = _useState4[0];
  var incrementIndex = (0, _react.useCallback)(function (list) {
    var currentIndex = currentSelectedItemIndex;
    var nextIndex = (currentIndex + 1) % list.length;
    // Skips disabled items. Previously this function relied on a list of enabled elements which caused a
    // difference between currentIndex and the item index in the menu.
    while (nextIndex !== currentIndex && list[nextIndex].getAttribute('aria-disabled') === 'true') {
      nextIndex = (nextIndex + 1) % list.length;
    }
    setCurrentSelectedItemIndex(nextIndex);
    return nextIndex;
  }, [currentSelectedItemIndex]);
  var decrementIndex = (0, _react.useCallback)(function (list) {
    var currentIndex = currentSelectedItemIndex;
    var nextIndex = (list.length + currentIndex - 1) % list.length;
    while (nextIndex !== currentIndex && list[nextIndex].getAttribute('aria-disabled') === 'true') {
      nextIndex = (list.length + nextIndex - 1) % list.length;
    }
    setCurrentSelectedItemIndex(nextIndex);
    return nextIndex;
  }, [currentSelectedItemIndex]);

  // this useEffect uses onSelection in it's dependency list which gets
  // changed as a result of the dropdown menu getting re-rendered in it's
  // parent component. Note that if onSelection gets updated to useMemo
  // this will no longer work.
  (0, _react.useEffect)(function () {
    var currentIndex = currentSelectedItemIndex;
    var list = getFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
    var currentElement = list[currentIndex];
    if (currentElement && currentElement.getAttribute('aria-disabled') === 'true') {
      var _list$focusIndex;
      var focusIndex = incrementIndex(list);
      (_list$focusIndex = list[focusIndex]) === null || _list$focusIndex === void 0 || _list$focusIndex.focus();
    }
  }, [currentSelectedItemIndex, onSelection, incrementIndex, decrementIndex]);
  (0, _react.useLayoutEffect)(function () {
    if (disableArrowKeyNavigation) {
      return;
    }

    /**
     * To handle the key events on the list
     * @param event
     */
    var handleKeyDown = function handleKeyDown(event) {
      var _wrapperRef$current;
      var targetElement = event.target;

      // Tab key on menu items can be handled in the parent components of dropdown menus with KeydownHandlerContext
      if (event.key === 'Tab' && closeOnTab) {
        handleClose(event);
        keyDownHandlerContext === null || keyDownHandlerContext === void 0 || keyDownHandlerContext.handleTab();
        return;
      }

      // To trap the focus inside the toolbar using left and right arrow keys
      var focusableElements = getFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
      if (!focusableElements || (focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements.length) === 0) {
        return;
      }
      if (!((_wrapperRef$current = wrapperRef.current) !== null && _wrapperRef$current !== void 0 && _wrapperRef$current.contains(targetElement))) {
        setCurrentSelectedItemIndex(-1);
      }
      switch (event.key) {
        case 'ArrowDown':
          {
            if (hasEnabledItems(focusableElements)) {
              var _focusableElements$fo;
              var focusIndex = incrementIndex(focusableElements);
              (_focusableElements$fo = focusableElements[focusIndex]) === null || _focusableElements$fo === void 0 || _focusableElements$fo.focus();
              event.preventDefault();
            }
            break;
          }
        case 'ArrowUp':
          {
            if (hasEnabledItems(focusableElements)) {
              var _focusableElements$_f;
              var _focusIndex = decrementIndex(focusableElements);
              (_focusableElements$_f = focusableElements[_focusIndex]) === null || _focusableElements$_f === void 0 || _focusableElements$_f.focus();
              event.preventDefault();
            }
            break;
          }

        // ArrowLeft/Right on the menu should close the menus
        // then logic to retain the focus can be handled in the parent components with KeydownHandlerContext
        case 'ArrowLeft':
          // Filter out the events from outside the menu
          if (!targetElement.closest('.custom-key-handler-wrapper')) {
            return;
          }
          handleClose(event);
          if (!targetElement.closest('[data-testid="editor-floating-toolbar"]')) {
            keyDownHandlerContext === null || keyDownHandlerContext === void 0 || keyDownHandlerContext.handleArrowLeft();
          }
          break;
        case 'ArrowRight':
          // Filter out the events from outside the menu
          if (!targetElement.closest('.custom-key-handler-wrapper')) {
            return;
          }
          handleClose(event);
          if (!targetElement.closest('[data-testid="editor-floating-toolbar"]')) {
            keyDownHandlerContext === null || keyDownHandlerContext === void 0 || keyDownHandlerContext.handleArrowRight();
          }
          break;
        case 'Escape':
          handleClose(event);
          break;
        case 'Enter':
          if (typeof onSelection === 'function') {
            onSelection(currentSelectedItemIndex);
          }
          break;
        default:
          return;
      }
    };
    listenerTargetElement && listenerTargetElement.addEventListener('keydown', handleKeyDown);
    return function () {
      listenerTargetElement && listenerTargetElement.removeEventListener('keydown', handleKeyDown);
    };
  }, [currentSelectedItemIndex, wrapperRef, handleClose, disableArrowKeyNavigation, keyDownHandlerContext, closeOnTab, onSelection, incrementIndex, decrementIndex, listenerTargetElement]);
  return /*#__PURE__*/_react.default.createElement("div", {
    className: "menu-key-handler-wrapper custom-key-handler-wrapper",
    ref: wrapperRef
  }, children);
};
function getFocusableElements(rootNode) {
  if (!rootNode) {
    return [];
  }
  var focusableModalElements = rootNode.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, div[tabindex="-1"]') || [];
  return Array.from(focusableModalElements);
}