Files
tolaria/src/components/useAiPanelFocus.ts

71 lines
1.8 KiB
TypeScript
Raw Normal View History

import { useCallback, useEffect } from 'react'
interface UseAiPanelFocusArgs {
inputRef: React.RefObject<HTMLDivElement | null>
panelRef: React.RefObject<HTMLElement | null>
hasMessages: boolean
isActive: boolean
onClose: () => void
2026-05-26 16:26:51 +02:00
enabled?: boolean
}
function focusPreferredElement(
panelRef: React.RefObject<HTMLElement | null>,
inputRef: React.RefObject<HTMLDivElement | null>,
shouldFocusPanel: boolean,
) {
if (panelRef.current?.contains(document.activeElement)) return
if (shouldFocusPanel) {
panelRef.current?.focus()
return
}
inputRef.current?.focus()
}
function shouldHandleEscape(
event: KeyboardEvent,
panelRef: React.RefObject<HTMLElement | null>,
): boolean {
return event.key === 'Escape' && !!panelRef.current?.contains(document.activeElement)
}
export function useAiPanelFocus({
inputRef,
panelRef,
hasMessages,
isActive,
onClose,
2026-05-26 16:26:51 +02:00
enabled = true,
}: UseAiPanelFocusArgs) {
const shouldFocusPanel = hasMessages || isActive
useEffect(() => {
2026-05-26 16:26:51 +02:00
if (!enabled) return
const timer = setTimeout(() => {
focusPreferredElement(panelRef, inputRef, shouldFocusPanel)
}, 0)
return () => clearTimeout(timer)
2026-05-26 16:26:51 +02:00
}, [enabled, inputRef, panelRef, shouldFocusPanel])
useEffect(() => {
2026-05-26 16:26:51 +02:00
if (!enabled) return
focusPreferredElement(panelRef, inputRef, shouldFocusPanel)
2026-05-26 16:26:51 +02:00
}, [enabled, inputRef, panelRef, shouldFocusPanel])
const handleEscape = useCallback((event: KeyboardEvent) => {
if (!shouldHandleEscape(event, panelRef)) return
event.preventDefault()
onClose()
}, [onClose, panelRef])
useEffect(() => {
2026-05-26 16:26:51 +02:00
if (!enabled) return
window.addEventListener('keydown', handleEscape)
return () => window.removeEventListener('keydown', handleEscape)
2026-05-26 16:26:51 +02:00
}, [enabled, handleEscape])
}