"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");
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,
        focusOnSearch: true,
        focusOnViewMore: false
      });
    case ACTIONS.MOVE:
      return moveReducer(state, action);
  }
  return state;
};
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 {
      return _objectSpread(_objectSpread({}, state), {}, {
        focusOnSearch: false,
        focusOnViewMore: false,
        focusedItemIndex: 0,
        selectedItemIndex: 0
      });
    }
  }
  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 ? state.selectedItemIndex + action.payload.positions : 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
    });
  }
  return _objectSpread(_objectSpread({}, state), {}, {
    focusOnSearch: false,
    focusOnViewMore: false,
    selectedItemIndex: safeIndex,
    focusedItemIndex: safeIndex
  });
};
var initialState = {
  focusOnSearch: true,
  focusOnViewMore: false,
  selectedItemIndex: 0,
  focusedItemIndex: undefined,
  listSize: 0
};
var getInitialState = function getInitialState(listSize, canFocusViewMore) {
  return function (initialState) {
    return _objectSpread(_objectSpread({}, initialState), {}, {
      listSize: listSize,
      canFocusViewMore: canFocusViewMore
    });
  };
};
function useSelectAndFocusOnArrowNavigation(listSize, step, canFocusViewMore) {
  var _useReducer = (0, _react.useReducer)(reducer, initialState, 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;
  var reset = (0, _react.useCallback)(function (listSize) {
    var payload = _objectSpread(_objectSpread({}, initialState), {}, {
      listSize: listSize
    });
    dispatch({
      type: ACTIONS.UPDATE_STATE,
      payload: payload
    });
  }, []);
  var removeFocusFromSearchAndSetOnItem = (0, _react.useCallback)(function (index) {
    var payload = {
      focusedItemIndex: index,
      selectedItemIndex: index,
      focusOnSearch: false,
      focusOnViewMore: false
    };
    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;
    }
    switch (e.key) {
      case '/':
        e.preventDefault();
        e.stopPropagation();
        return setFocusOnSearch();
      case 'ArrowRight':
        return move(e, +1);
      case 'ArrowLeft':
        return move(e, -1);
      case 'ArrowDown':
        return move(e, +step);
      case 'ArrowUp':
        return move(e, -step, step);
    }
  }, [focusOnSearch, setFocusOnSearch, move, step]);
  (0, _react.useEffect)(function () {
    // To reset selection when user filters
    reset(listSize);
  }, [listSize, reset]);
  return {
    selectedItemIndex: selectedItemIndex,
    onKeyDown: onKeyDown,
    focusOnSearch: focusOnSearch,
    focusOnViewMore: focusOnViewMore,
    setFocusOnSearch: setFocusOnSearch,
    focusedItemIndex: focusedItemIndex,
    setFocusedItemIndex: removeFocusFromSearchAndSetOnItem
  };
}
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;