"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ensureSafeIndex = exports.default = exports.ACTIONS = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = require("react");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
/**
 * a custom hook that handles keyboard navigation for Arrow keys based on a
 * given listSize, and a step (for up and down arrows).
 *
 * @param {number} listSize
 * @param {number} upDownStep
 *
 * Example usage:
 *    const list = ['Confluence','Jira','Atlaskit'];
 *    const {
 *      selectedItemIndex,
 *      focusedItemIndex,
 *      focusOnSearch,
 *      focusOnViewMore,
 *      setFocusedItemIndex,
 *      onKeyDown
 *    } = useSelectAndFocusOnArrowNavigation(list.length - 1, 1);
 *
 *    return (
 *      <div onKeyDown={onKeyDown}>
 *        <SearchBar onClick={() => setFocusedItemIndex(undefined)} focus={focusOnSearch} />
 *        {list.map((item, index) => (
 *           <ListItem
 *            title={item}
 *            style={{ ..., color: selected ? 'selectedStateColor' : defaultColor }}
 *            onClick={() => {
 *              setFocusedItemIndex(index);
 *            }
 *          />
 *        )}
 *      </div>
 *    );
 *
 *    const SearchBar = ({ focus }) => {
 *      const ref = useRefToFocusOrScroll(focus);
 *      return <input ref={ref} />
 *    }
 *
 */
var ACTIONS = exports.ACTIONS = /*#__PURE__*/function (ACTIONS) {
  ACTIONS["FOCUS_SEARCH"] = "focusOnSearch";
  ACTIONS["UPDATE_STATE"] = "updateState";
  ACTIONS["MOVE"] = "move";
  return ACTIONS;
}({});
var reducer = function reducer(state, action) {
  switch (action.type) {
    case ACTIONS.UPDATE_STATE:
      return _objectSpread(_objectSpread({}, state), action.payload);
    case ACTIONS.FOCUS_SEARCH:
      return _objectSpread(_objectSpread({}, state), {}, {
        focusedItemIndex: undefined,
        focusedCategoryIndex: undefined,
        focusOnSearch: true,
        focusOnViewMore: false
      });
    case ACTIONS.MOVE:
      return moveReducer(state, action);
  }
};
var moveReducer = function moveReducer(state, action) {
  var listSize = state.listSize,
    canFocusViewMore = state.canFocusViewMore;
  if (state.focusOnSearch) {
    // up arrow
    if (action.payload.positions && action.payload.positions <= -1) {
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: false,
        focusOnViewMore: !!canFocusViewMore,
        focusedItemIndex: canFocusViewMore ? undefined : listSize,
        selectedItemIndex: canFocusViewMore ? undefined : listSize
      });
    } else {
      var _action$payload$step;
      var _newIndex = action.payload.positions ? action.payload.positions - ((_action$payload$step = action.payload.step) !== null && _action$payload$step !== void 0 ? _action$payload$step : 1) : 0;
      var _safeIndex = ensureSafeIndex(_newIndex, state.listSize);
      var isLastItemFocused = _newIndex > listSize;
      var focusOnSearch = isLastItemFocused && !canFocusViewMore;
      var focusOnViewMore = isLastItemFocused && !!canFocusViewMore;
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: focusOnSearch,
        focusOnViewMore: focusOnViewMore,
        focusedItemIndex: _safeIndex,
        selectedItemIndex: _safeIndex
      });
    }
  }
  if (state.focusOnViewMore) {
    // down arrow
    if (action.payload.positions === 1) {
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: true,
        focusOnViewMore: false,
        focusedItemIndex: undefined,
        // if search is focused then select first item.
        selectedItemIndex: 0
      });
    } else {
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: false,
        focusOnViewMore: false,
        focusedItemIndex: listSize,
        selectedItemIndex: listSize
      });
    }
  }
  var newIndex = state.selectedItemIndex ?
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  state.selectedItemIndex + action.payload.positions :
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  action.payload.positions;
  var safeIndex = ensureSafeIndex(newIndex, state.listSize);
  // down arrow key is pressed or right arrow key is pressed.
  if (state.focusedItemIndex !== undefined && action.payload.positions && action.payload.positions >= 1) {
    // when multi column element browser is open and we are in last
    //  row then newIndex will be greater than listSize when
    //  down arrow key is pressed.
    // Or when last item is focused and down or right arrow key is pressed.
    var _isLastItemFocused = newIndex > listSize;
    var _focusOnSearch = _isLastItemFocused && !canFocusViewMore;
    var _focusOnViewMore = _isLastItemFocused && !!canFocusViewMore;
    // if search is focused, then select first item.
    // otherwise if view more is focused then select item should be undefined.
    var selectedItemIndex = _focusOnSearch ? 0 : _focusOnViewMore ? undefined : safeIndex;
    return _objectSpread(_objectSpread({}, state), {}, {
      focusOnSearch: _focusOnSearch,
      focusOnViewMore: _focusOnViewMore,
      selectedItemIndex: selectedItemIndex,
      focusedItemIndex: _isLastItemFocused ? undefined : safeIndex
    });
  }

  // up arrow key is pressed or left arrow key is pressed.
  if (state.focusedItemIndex !== undefined && action.payload.positions && action.payload.positions <= -1) {
    // if arrow up key is pressed when focus is in first row,
    //  or, arrow left key is pressed when first item is focused,
    //  then newIndex will become less than zero.
    // In this case, focus search, and, kept previously selected item.
    var isFirstRowFocused = newIndex < 0;
    // if focus goes to search then kept last selected item in first row.
    var _selectedItemIndex = isFirstRowFocused ? state.selectedItemIndex : safeIndex;
    return _objectSpread(_objectSpread({}, state), {}, {
      // focus search if first item is focused on up or left arrow key
      focusOnSearch: isFirstRowFocused,
      focusOnViewMore: false,
      focusedItemIndex: isFirstRowFocused ? undefined : safeIndex,
      selectedItemIndex: _selectedItemIndex
    });
  }
  if ((0, _platformFeatureFlags.fg)('jfp_a11y_fix_create_issue_no_results_link_focus')) {
    // Handle empty state navigation
    if (state.listSize === -1) {
      // If currently on search, ArrowDown and ArrowUp moves to EmptyState button
      if (state.focusOnSearch && action.payload.positions && action.payload.positions > 0) {
        return _objectSpread(_objectSpread({}, state), {}, {
          focusOnSearch: false,
          focusOnEmptyStateButton: true
        });
      }
      // If currently on EmptyState button, ArrowUp and ArrowDown moves back to search
      if (state.focusOnEmptyStateButton && action.payload.positions && action.payload.positions < 0) {
        return _objectSpread(_objectSpread({}, state), {}, {
          focusOnSearch: true,
          focusOnEmptyStateButton: false
        });
      }
      // Stay on EmptyState button for other arrows
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: false,
        focusOnEmptyStateButton: true
      });
    }
  }
  return _objectSpread(_objectSpread({}, state), {}, {
    focusOnSearch: false,
    focusOnViewMore: false,
    selectedItemIndex: safeIndex,
    focusedItemIndex: safeIndex
  });
};
var initialState = {
  focusOnSearch: true,
  focusOnViewMore: false,
  focusOnEmptyStateButton: false,
  selectedItemIndex: 0,
  focusedItemIndex: undefined,
  listSize: 0
};
var initialStateWithFocusOnSearchDisabled = _objectSpread(_objectSpread({}, initialState), {}, {
  focusOnSearch: false
});
var getInitialState = function getInitialState(listSize, canFocusViewMore) {
  return function (initialState) {
    return _objectSpread(_objectSpread({}, initialState), {}, {
      listSize: listSize,
      canFocusViewMore: canFocusViewMore
    });
  };
};

// Get the offset forwards - skip items that are disabled
var skipForwardOffsetToSafeItem = function skipForwardOffsetToSafeItem(currentIndex, listSize, stepSize, itemIsDisabled) {
  if (currentIndex === undefined) {
    return undefined;
  }
  // Iterate through the list starting from the next item
  for (var offset = 1; currentIndex + offset * stepSize <= listSize; offset++) {
    if (!itemIsDisabled(currentIndex + offset * stepSize)) {
      return offset * stepSize;
    }
  }

  // Move to the last place if possible
  return listSize - currentIndex + 1;
};

// Get the offset backwards - skip items that are disabled
var skipBackwardOffsetToSafeItem = function skipBackwardOffsetToSafeItem(currentIndex, stepSize, itemIsDisabled) {
  if (currentIndex === undefined) {
    return undefined;
  }

  // Iterate backwards starting from the previous item
  for (var offset = 1; currentIndex - offset * stepSize >= -1; offset++) {
    if (!itemIsDisabled(currentIndex - offset * stepSize) || currentIndex - offset * stepSize === -1) {
      return offset * stepSize;
    }
  }

  // Move to search if no available index
  return currentIndex + 1;
};
function useSelectAndFocusOnArrowNavigation(listSize, step, canFocusViewMore, itemIsDisabled, isFocusSearch, autoFocusSearch) {
  var _useReducer = (0, _react.useReducer)(reducer, autoFocusSearch ? initialState : initialStateWithFocusOnSearchDisabled, getInitialState(listSize, canFocusViewMore)),
    _useReducer2 = (0, _slicedToArray2.default)(_useReducer, 2),
    state = _useReducer2[0],
    dispatch = _useReducer2[1];
  (0, _react.useEffect)(function () {
    dispatch({
      type: ACTIONS.UPDATE_STATE,
      payload: {
        canFocusViewMore: canFocusViewMore
      }
    });
  }, [canFocusViewMore]);
  var selectedItemIndex = state.selectedItemIndex,
    focusedItemIndex = state.focusedItemIndex,
    focusOnSearch = state.focusOnSearch,
    focusOnViewMore = state.focusOnViewMore,
    focusedCategoryIndex = state.focusedCategoryIndex,
    focusOnEmptyStateButton = state.focusOnEmptyStateButton;

  // calls if items size changed
  var reset = (0, _react.useCallback)(function (listSize) {
    var defaultState = autoFocusSearch ? initialState : initialStateWithFocusOnSearchDisabled;
    var payload = _objectSpread(_objectSpread({}, defaultState), {}, {
      listSize: listSize
    });
    // A11Y: if categories exist ,on the initial render search element should receive focus.
    // After user pick some category the category should stay focused.
    payload = Object.assign(payload, {
      focusOnSearch: isFocusSearch !== null && isFocusSearch !== void 0 ? isFocusSearch : defaultState.focusOnSearch
    });
    dispatch({
      type: ACTIONS.UPDATE_STATE,
      payload: payload
    });
  }, [isFocusSearch, autoFocusSearch]);
  var removeFocusFromSearchAndSetOnItem = (0, _react.useCallback)(function (index) {
    var payload = {
      focusedItemIndex: index,
      selectedItemIndex: index,
      focusOnSearch: false,
      focusOnViewMore: false
    };
    payload = Object.assign(payload, {
      focusedCategoryIndex: undefined
    });
    dispatch({
      type: ACTIONS.UPDATE_STATE,
      payload: payload
    });
  }, [dispatch]);
  var setFocusedCategoryIndex = (0, _react.useCallback)(function (index) {
    var payload = {
      focusOnSearch: false,
      focusOnViewMore: false,
      focusedCategoryIndex: index,
      focusedItemIndex: undefined
    };
    dispatch({
      type: ACTIONS.UPDATE_STATE,
      payload: payload
    });
  }, [dispatch]);
  var setFocusOnSearch = (0, _react.useCallback)(function () {
    dispatch({
      type: ACTIONS.FOCUS_SEARCH,
      payload: {}
    });
  }, [dispatch]);
  var isMoving = (0, _react.useRef)(false);
  var move = (0, _react.useCallback)(function (e, positions, actualStep) {
    e.preventDefault();
    e.stopPropagation();

    // avoid firing 2 moves at the same time when holding an arrow down as this can freeze the screen
    if (!isMoving.current) {
      isMoving.current = true;
      requestAnimationFrame(function () {
        isMoving.current = false;
        dispatch({
          type: ACTIONS.MOVE,
          payload: {
            positions: positions,
            step: actualStep
          }
        });
      });
    }
  }, []);
  var onKeyDown = (0, _react.useCallback)(function (e) {
    var avoidKeysWhileSearching = ['/',
    // While already focused on search bar, let users type in.
    'ArrowRight', 'ArrowLeft'];
    if (focusOnSearch && avoidKeysWhileSearching.includes(e.key)) {
      return;
    }
    if ((0, _platformFeatureFlags.fg)('jfp_a11y_fix_create_issue_no_results_link_focus')) {
      // Handle empty state navigation and trap focus between search and CTA
      if (listSize === -1) {
        if (e.key === 'Tab') {
          e.preventDefault();
          dispatch({
            type: ACTIONS.UPDATE_STATE,
            payload: {
              focusOnSearch: focusOnEmptyStateButton,
              // cycle focus between search and button
              focusOnEmptyStateButton: !focusOnEmptyStateButton // toggle
            }
          });
          return;
        }
      }
    }
    switch (e.key) {
      case '/':
        e.preventDefault();
        e.stopPropagation();
        return setFocusOnSearch();
      case 'ArrowRight':
        {
          var _skipForwardOffsetToS;
          var itemIndex = focusOnSearch ? -1 : selectedItemIndex;
          var nextItem = (_skipForwardOffsetToS = skipForwardOffsetToSafeItem(itemIndex, listSize, 1, itemIsDisabled)) !== null && _skipForwardOffsetToS !== void 0 ? _skipForwardOffsetToS : 1;
          return move(e, nextItem);
        }
      case 'ArrowLeft':
        {
          var _skipBackwardOffsetTo;
          var _nextItem = (_skipBackwardOffsetTo = skipBackwardOffsetToSafeItem(selectedItemIndex, 1, itemIsDisabled)) !== null && _skipBackwardOffsetTo !== void 0 ? _skipBackwardOffsetTo : 1;
          return move(e, -_nextItem);
        }
      case 'ArrowDown':
        {
          var _skipForwardOffsetToS2;
          var _itemIndex = focusOnSearch ? -step : selectedItemIndex;
          var _nextItem2 = (_skipForwardOffsetToS2 = skipForwardOffsetToSafeItem(_itemIndex, listSize, step, itemIsDisabled)) !== null && _skipForwardOffsetToS2 !== void 0 ? _skipForwardOffsetToS2 : step;
          return move(e, +_nextItem2, step);
        }
      case 'ArrowUp':
        {
          var _skipBackwardOffsetTo2;
          var _nextItem3 = (_skipBackwardOffsetTo2 = skipBackwardOffsetToSafeItem(selectedItemIndex, step, itemIsDisabled)) !== null && _skipBackwardOffsetTo2 !== void 0 ? _skipBackwardOffsetTo2 : step;
          return move(e, Math.min(-_nextItem3, -step), step);
        }
    }
  }, [focusOnSearch, setFocusOnSearch, move, selectedItemIndex, itemIsDisabled, listSize, step, focusOnEmptyStateButton]);
  (0, _react.useEffect)(function () {
    // To reset selection when user filters
    reset(listSize);
  }, [listSize, reset]);
  return {
    selectedItemIndex: selectedItemIndex,
    onKeyDown: onKeyDown,
    focusOnSearch: focusOnSearch,
    focusOnViewMore: focusOnViewMore,
    focusOnEmptyStateButton: focusOnEmptyStateButton,
    setFocusOnSearch: setFocusOnSearch,
    focusedItemIndex: focusedItemIndex,
    setFocusedItemIndex: removeFocusFromSearchAndSetOnItem,
    focusedCategoryIndex: focusedCategoryIndex,
    setFocusedCategoryIndex: setFocusedCategoryIndex
  };
}
var ensureSafeIndex = exports.ensureSafeIndex = function ensureSafeIndex(index, listSize) {
  if (index < 0) {
    return 0;
  }
  if (index > listSize) {
    return listSize;
  }
  return index;
};
var _default = exports.default = useSelectAndFocusOnArrowNavigation;