mirror of https://github.com/grafana/grafana.git
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
|
|
import { TreeNode } from './types';
|
|
|
|
// Uses enum to enable extension in the future
|
|
export enum KeyboardAction {
|
|
SELECT = 'select',
|
|
EXPAND = 'expand',
|
|
}
|
|
|
|
// Handles keyboard interactions for the scopes tree
|
|
// onSelect is the function to call when an option is selected
|
|
// Returns the highlighted node id
|
|
export function useKeyboardInteraction(
|
|
enabled: boolean,
|
|
items: TreeNode[],
|
|
searchQuery: string,
|
|
onSelect: (nodeId: string | undefined, action: KeyboardAction) => void
|
|
) {
|
|
const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
|
|
|
|
const handleKeyDown = useCallback(
|
|
(event: KeyboardEvent): void => {
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
// If there are no options, do nothing. Also to prevent dividing by 0
|
|
if (items.length === 0) {
|
|
return;
|
|
}
|
|
|
|
switch (event.key) {
|
|
// Change highlighted index
|
|
case 'ArrowDown':
|
|
event.preventDefault();
|
|
|
|
setHighlightedIndex((prev) => (prev + 1) % items.length);
|
|
break;
|
|
case 'ArrowUp':
|
|
event.preventDefault();
|
|
|
|
setHighlightedIndex((prev) => (prev - 1 + items.length) % items.length);
|
|
break;
|
|
// Handle Select action
|
|
case 'Enter':
|
|
event.preventDefault();
|
|
|
|
if (highlightedIndex !== -1) {
|
|
onSelect(items[highlightedIndex]?.scopeNodeId, KeyboardAction.SELECT);
|
|
}
|
|
break;
|
|
// Handle Expand action
|
|
case 'ArrowRight':
|
|
// Let checking if an item actually is expandable be handled in onSelect
|
|
if (highlightedIndex !== -1) {
|
|
// Send an expand action here and let onSelect determine if the node actually is expandable
|
|
event.preventDefault();
|
|
onSelect(items[highlightedIndex]?.scopeNodeId, KeyboardAction.EXPAND);
|
|
}
|
|
|
|
break;
|
|
case 'Escape':
|
|
setHighlightedIndex(-1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
[items, onSelect, highlightedIndex, enabled]
|
|
);
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
return () => {
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
};
|
|
}, [handleKeyDown]);
|
|
|
|
// Reset highlighted index when items length changes to 0
|
|
useEffect(() => {
|
|
if (items.length === 0) {
|
|
setHighlightedIndex(-1);
|
|
}
|
|
}, [items]);
|
|
|
|
useEffect(() => {
|
|
// Reset when doing a new query
|
|
setHighlightedIndex(-1);
|
|
}, [searchQuery, enabled]);
|
|
|
|
const highlightedId = highlightedIndex === -1 ? undefined : items[highlightedIndex]?.scopeNodeId;
|
|
|
|
return { highlightedId };
|
|
}
|