mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
import { Observable, Subject } from 'rxjs';
|
|
|
|
import { Field, locationUtil } from '@grafana/data';
|
|
import { locationService } from '@grafana/runtime';
|
|
|
|
import { QueryResponse } from '../service';
|
|
|
|
export function useKeyNavigationListener() {
|
|
const eventsRef = useRef(new Subject<React.KeyboardEvent>());
|
|
return {
|
|
keyboardEvents: eventsRef.current,
|
|
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
switch (e.code) {
|
|
case 'ArrowDown':
|
|
case 'ArrowUp':
|
|
case 'ArrowLeft':
|
|
case 'ArrowRight':
|
|
case 'Enter':
|
|
eventsRef.current.next(e);
|
|
default:
|
|
// ignore
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
interface ItemSelection {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
export function useSearchKeyboardNavigation(
|
|
keyboardEvents: Observable<React.KeyboardEvent>,
|
|
numColumns: number,
|
|
response: QueryResponse
|
|
): ItemSelection {
|
|
const highlightIndexRef = useRef<ItemSelection>({ x: 0, y: -1 });
|
|
const [highlightIndex, setHighlightIndex] = useState<ItemSelection>({ x: 0, y: -1 });
|
|
const urlsRef = useRef<Field>();
|
|
|
|
// Clear selection when the search results change
|
|
useEffect(() => {
|
|
urlsRef.current = response.view.fields.url;
|
|
highlightIndexRef.current.x = 0;
|
|
highlightIndexRef.current.y = -1;
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
}, [response]);
|
|
|
|
useEffect(() => {
|
|
const sub = keyboardEvents.subscribe({
|
|
next: (keyEvent) => {
|
|
switch (keyEvent?.code) {
|
|
case 'ArrowDown': {
|
|
highlightIndexRef.current.y++;
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
break;
|
|
}
|
|
case 'ArrowUp':
|
|
highlightIndexRef.current.y = Math.max(0, highlightIndexRef.current.y - 1);
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
break;
|
|
case 'ArrowRight': {
|
|
if (numColumns > 0) {
|
|
highlightIndexRef.current.x = Math.min(numColumns, highlightIndexRef.current.x + 1);
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
}
|
|
break;
|
|
}
|
|
case 'ArrowLeft': {
|
|
if (numColumns > 0) {
|
|
highlightIndexRef.current.x = Math.max(0, highlightIndexRef.current.x - 1);
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
}
|
|
break;
|
|
}
|
|
case 'Enter':
|
|
if (!urlsRef.current) {
|
|
break;
|
|
}
|
|
const idx = highlightIndexRef.current.x * numColumns + highlightIndexRef.current.y;
|
|
if (idx < 0) {
|
|
highlightIndexRef.current.x = 0;
|
|
highlightIndexRef.current.y = 0;
|
|
setHighlightIndex({ ...highlightIndexRef.current });
|
|
break;
|
|
}
|
|
const url = urlsRef.current.values?.get(idx) as string;
|
|
if (url) {
|
|
locationService.push(locationUtil.stripBaseFromUrl(url));
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
return () => sub.unsubscribe();
|
|
}, [keyboardEvents, numColumns]);
|
|
|
|
return highlightIndex;
|
|
}
|