import { useEffect, useCallback } from 'react';
import { documentIsAvailable, isInstance } from '../utils';
import useFocusWithin from './useFocusWithin';
const focusable = `
  a[href],
  button:enabled,
  input[type='checkbox']:enabled,
  input[type='radio']:enabled
`;
/**
 * @example useArrows(ref, { cycle, selector });
 * @param ref - A reference to the element that will be navigated through. [React RefObject](https://reactjs.org/docs/refs-and-the-dom.html)
 */
const useArrows = (ref, { 
/** If true, the down or up arrow key will navigate to the first or last element if the element currently focused is the last or first index of the selected elements. */
cycle = true, 
/** A query selector that will determine which elements to cycle through with the arrow keys. The selector will be used in conjunction with [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll#selectors). */
selector = focusable, 
/** Which keys are used to navigate through the list */
dir = 'up-down', allowTabFocus = true, updateTabIndex = true, 
/** When focus enters the ref, which element should receive focus first if available */
initialFocusElement } = {}, 
/** Pass this prop in the internal dependencies when list is dynamic */
dependencies = []) => {
    const [NextKey, PrevKey] = dir === 'up-down' ? ['ArrowDown', 'ArrowUp'] : ['ArrowRight', 'ArrowLeft'];
    const setTabIndexes = useCallback((el, focused) => {
        // don't set tabindex -1 for first element, or the initialFocusElement, or an element with a checked input inside of it to be able to focus it
        const focusableElements = Array.from(el.querySelectorAll(selector));
        if (focusableElements.length > 0) {
            let focusIndex = focusableElements.findIndex(focusEl => focusEl === initialFocusElement);
            if (focusIndex === -1)
                focusIndex = 0;
            focusableElements[focusIndex].tabIndex =
                allowTabFocus && !focused ? 0 : -1;
            focusableElements.splice(focusIndex, 1);
            focusableElements.forEach(item => {
                if (isInstance(item, HTMLElement)) {
                    const checked = item.checked;
                    if (!checked)
                        item.tabIndex = -1;
                    const includesChecked = item.querySelector('input[type="radio"]:checked');
                    if (includesChecked)
                        item.tabIndex = 0;
                }
            });
        }
    }, [selector, initialFocusElement, ...dependencies]);
    useFocusWithin([ref], focused => {
        if (ref.current && updateTabIndex)
            setTabIndexes(ref.current, focused);
    });
    const listener = useCallback((e) => {
        const el = ref.current;
        if (!el)
            return;
        if (['Home', 'End', NextKey, PrevKey].includes(e.key))
            e.preventDefault();
        if (!documentIsAvailable)
            return;
        const items = Array.from(el.querySelectorAll(selector)).filter((item) => isInstance(item, HTMLElement));
        if (!items.length)
            return;
        const rootNode = el.getRootNode();
        if (!isInstance(rootNode, Document) && !isInstance(rootNode, ShadowRoot))
            return;
        const focusIdx = items.indexOf(rootNode.activeElement);
        const lastIdx = items.length - 1;
        let newFocusIdx;
        if (e.key === 'Home' || (e.key === NextKey && focusIdx === -1)) {
            const focusableElements = Array.from(el.querySelectorAll(selector));
            const focusIndex = focusableElements.findIndex(focusEl => focusEl === initialFocusElement);
            newFocusIdx = focusIndex === -1 ? 0 : focusIndex;
        }
        else if (e.key === 'End' || (e.key === PrevKey && focusIdx === -1)) {
            const focusableElements = Array.from(el.querySelectorAll(selector));
            const focusIndex = focusableElements.findIndex(focusEl => focusEl === initialFocusElement);
            newFocusIdx = focusIndex === -1 ? lastIdx : focusIndex;
        }
        else if (e.key === 'Enter') {
            if (updateTabIndex) {
                setTabIndexes(el, true);
            }
            return;
        }
        else if (e.key === NextKey) {
            if (focusIdx === lastIdx) {
                if (!cycle)
                    return;
                newFocusIdx = 0;
            }
            else {
                newFocusIdx = focusIdx + 1;
            }
        }
        else if (e.key === PrevKey) {
            if (focusIdx === 0) {
                if (!cycle)
                    return;
                newFocusIdx = lastIdx;
            }
            else {
                newFocusIdx = focusIdx - 1;
            }
        }
        else {
            return;
        }
        items[newFocusIdx].focus();
    }, [ref.current, cycle, selector, initialFocusElement, ...dependencies]);
    useEffect(() => {
        const el = ref.current;
        if (!el)
            return;
        if (updateTabIndex)
            setTimeout(() => {
                setTabIndexes(el, false);
            }, 0);
        el.addEventListener('keydown', listener);
        return () => {
            el.removeEventListener('keydown', listener);
        };
    }, [ref.current, selector, listener, initialFocusElement, ...dependencies]);
};
export default useArrows;
//# sourceMappingURL=useArrows.js.map