mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-29 21:51:59 +02:00
Refactor: Consolidate event viewers into unified EventViewer component
Migrate EventStreamViewer, HttpResponseTimeline, GrpcResponsePane, and WebsocketResponsePane to use a shared EventViewer component with generic event type support, render props for rows and details, and keyboard navigation (↑/↓/j/k/Escape). This reduces duplication and provides a consistent event viewing experience across all response types.
This commit is contained in:
70
src-web/hooks/useEventViewerKeyboard.ts
Normal file
70
src-web/hooks/useEventViewerKeyboard.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { Virtualizer } from '@tanstack/react-virtual';
|
||||
import { useCallback } from 'react';
|
||||
import { useKey } from 'react-use';
|
||||
|
||||
interface UseEventViewerKeyboardProps {
|
||||
totalCount: number;
|
||||
activeIndex: number | null;
|
||||
setActiveIndex: (index: number | null) => void;
|
||||
virtualizer?: Virtualizer<HTMLDivElement, Element> | null;
|
||||
isContainerFocused: () => boolean;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export function useEventViewerKeyboard({
|
||||
totalCount,
|
||||
activeIndex,
|
||||
setActiveIndex,
|
||||
virtualizer,
|
||||
isContainerFocused,
|
||||
enabled = true,
|
||||
}: UseEventViewerKeyboardProps) {
|
||||
const selectPrev = useCallback(() => {
|
||||
if (totalCount === 0) return;
|
||||
|
||||
const newIndex = activeIndex == null ? 0 : Math.max(0, activeIndex - 1);
|
||||
setActiveIndex(newIndex);
|
||||
virtualizer?.scrollToIndex(newIndex, { align: 'auto' });
|
||||
}, [activeIndex, setActiveIndex, totalCount, virtualizer]);
|
||||
|
||||
const selectNext = useCallback(() => {
|
||||
if (totalCount === 0) return;
|
||||
|
||||
const newIndex = activeIndex == null ? 0 : Math.min(totalCount - 1, activeIndex + 1);
|
||||
setActiveIndex(newIndex);
|
||||
virtualizer?.scrollToIndex(newIndex, { align: 'auto' });
|
||||
}, [activeIndex, setActiveIndex, totalCount, virtualizer]);
|
||||
|
||||
useKey(
|
||||
(e) => e.key === 'ArrowUp' || e.key === 'k',
|
||||
(e) => {
|
||||
if (!enabled || !isContainerFocused()) return;
|
||||
e.preventDefault();
|
||||
selectPrev();
|
||||
},
|
||||
undefined,
|
||||
[enabled, isContainerFocused, selectPrev],
|
||||
);
|
||||
|
||||
useKey(
|
||||
(e) => e.key === 'ArrowDown' || e.key === 'j',
|
||||
(e) => {
|
||||
if (!enabled || !isContainerFocused()) return;
|
||||
e.preventDefault();
|
||||
selectNext();
|
||||
},
|
||||
undefined,
|
||||
[enabled, isContainerFocused, selectNext],
|
||||
);
|
||||
|
||||
useKey(
|
||||
(e) => e.key === 'Escape',
|
||||
(e) => {
|
||||
if (!enabled || !isContainerFocused()) return;
|
||||
e.preventDefault();
|
||||
setActiveIndex(null);
|
||||
},
|
||||
undefined,
|
||||
[enabled, isContainerFocused, setActiveIndex],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user