88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { useEffect, useState } from '../../../../lib/teact/teact';
|
|
|
|
import captureKeyboardListeners from '../../../../util/captureKeyboardListeners';
|
|
import cycleRestrict from '../../../../util/cycleRestrict';
|
|
|
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
|
|
|
export function useKeyboardNavigation({
|
|
isActive,
|
|
isHorizontal,
|
|
shouldSaveSelectionOnUpdateItems,
|
|
shouldRemoveSelectionOnReset,
|
|
noArrowNavigation,
|
|
items,
|
|
shouldSelectOnTab,
|
|
onSelect,
|
|
onClose,
|
|
}: {
|
|
isActive: boolean;
|
|
isHorizontal?: boolean;
|
|
shouldSaveSelectionOnUpdateItems?: boolean;
|
|
shouldRemoveSelectionOnReset?: boolean;
|
|
noArrowNavigation?: boolean;
|
|
items?: any[];
|
|
shouldSelectOnTab?: boolean;
|
|
onSelect: (item: any) => void | boolean;
|
|
onClose: NoneToVoidFunction;
|
|
}) {
|
|
const [selectedItemIndex, setSelectedItemIndex] = useState(-1);
|
|
|
|
const getSelectedIndex = useLastCallback((newIndex: number) => {
|
|
if (!items) {
|
|
return -1;
|
|
}
|
|
|
|
return cycleRestrict(items.length, newIndex);
|
|
});
|
|
|
|
const handleArrowKey = useLastCallback((value: number, e: KeyboardEvent) => {
|
|
e.preventDefault();
|
|
setSelectedItemIndex((index) => (getSelectedIndex(index + value)));
|
|
});
|
|
|
|
const handleItemSelect = useLastCallback((e: KeyboardEvent) => {
|
|
// Prevent action on key combinations
|
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return false;
|
|
if (!isActive) return false;
|
|
|
|
if (items && items.length && selectedItemIndex > -1) {
|
|
const item = items[selectedItemIndex];
|
|
if (item) {
|
|
if (onSelect(item) === false) {
|
|
return false;
|
|
}
|
|
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!isActive) setSelectedItemIndex(shouldRemoveSelectionOnReset ? -1 : 0);
|
|
}, [isActive, shouldRemoveSelectionOnReset]);
|
|
|
|
const isSelectionOutOfRange = !items || selectedItemIndex > items.length - 1;
|
|
useEffect(() => {
|
|
if (!shouldSaveSelectionOnUpdateItems || isSelectionOutOfRange) {
|
|
setSelectedItemIndex(shouldRemoveSelectionOnReset ? -1 : 0);
|
|
}
|
|
}, [isSelectionOutOfRange, shouldRemoveSelectionOnReset, shouldSaveSelectionOnUpdateItems]);
|
|
|
|
useEffect(() => (isActive ? captureKeyboardListeners({
|
|
onEsc: onClose,
|
|
onUp: noArrowNavigation || isHorizontal ? undefined : (e: KeyboardEvent) => handleArrowKey(-1, e),
|
|
onDown: noArrowNavigation || isHorizontal ? undefined : (e: KeyboardEvent) => handleArrowKey(1, e),
|
|
onLeft: noArrowNavigation || !isHorizontal ? undefined : (e: KeyboardEvent) => handleArrowKey(-1, e),
|
|
onRight: noArrowNavigation || !isHorizontal ? undefined : (e: KeyboardEvent) => handleArrowKey(1, e),
|
|
onTab: shouldSelectOnTab ? handleItemSelect : undefined,
|
|
onEnter: handleItemSelect,
|
|
}) : undefined), [
|
|
noArrowNavigation, handleArrowKey, handleItemSelect, isActive, isHorizontal, onClose, shouldSelectOnTab,
|
|
]);
|
|
|
|
return selectedItemIndex;
|
|
}
|