diff --git a/frontend/src/components/ui/multi-file-search-textarea.tsx b/frontend/src/components/ui/multi-file-search-textarea.tsx index fae8eb57..46b94597 100644 --- a/frontend/src/components/ui/multi-file-search-textarea.tsx +++ b/frontend/src/components/ui/multi-file-search-textarea.tsx @@ -44,6 +44,7 @@ export function MultiFileSearchTextarea({ const dropdownRef = useRef(null); const abortControllerRef = useRef(null); const searchCacheRef = useRef>(new Map()); + const itemRefs = useRef>(new Map()); // Search for files when query changes useEffect(() => { @@ -309,6 +310,16 @@ export function MultiFileSearchTextarea({ } }, [searchResults.length, showDropdown]); + // Scroll selected item into view when navigating with arrow keys + useEffect(() => { + if (selectedIndex >= 0) { + const itemEl = itemRefs.current.get(selectedIndex); + if (itemEl) { + itemEl.scrollIntoView({ block: 'nearest' }); + } + } + }, [selectedIndex]); + const dropdownPosition = getDropdownPosition(); return ( @@ -352,6 +363,10 @@ export function MultiFileSearchTextarea({ {searchResults.map((file, index) => (
{ + if (el) itemRefs.current.set(index, el); + else itemRefs.current.delete(index); + }} className={`px-3 py-2 cursor-pointer text-sm ${ index === selectedIndex ? 'bg-blue-50 text-blue-900' diff --git a/frontend/src/components/ui/wysiwyg/plugins/file-tag-typeahead-plugin.tsx b/frontend/src/components/ui/wysiwyg/plugins/file-tag-typeahead-plugin.tsx index 764b7dad..b729996c 100644 --- a/frontend/src/components/ui/wysiwyg/plugins/file-tag-typeahead-plugin.tsx +++ b/frontend/src/components/ui/wysiwyg/plugins/file-tag-typeahead-plugin.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useRef } from 'react'; import { createPortal } from 'react-dom'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { @@ -67,6 +67,8 @@ function getMenuPosition(anchorEl: HTMLElement) { export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) { const [editor] = useLexicalComposerContext(); const [options, setOptions] = useState([]); + const itemRefs = useRef>(new Map()); + const lastSelectedIndexRef = useRef(-1); const onQueryChange = useCallback( (query: string | null) => { @@ -134,6 +136,20 @@ export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) { anchorRef.current ); + // Scroll selected item into view when navigating with arrow keys + if ( + selectedIndex !== null && + selectedIndex !== lastSelectedIndexRef.current + ) { + lastSelectedIndexRef.current = selectedIndex; + setTimeout(() => { + const itemEl = itemRefs.current.get(selectedIndex); + if (itemEl) { + itemEl.scrollIntoView({ block: 'nearest' }); + } + }, 0); + } + const tagResults = options.filter((r) => r.item.type === 'tag'); const fileResults = options.filter((r) => r.item.type === 'file'); @@ -167,6 +183,10 @@ export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) { return (
{ + if (el) itemRefs.current.set(index, el); + else itemRefs.current.delete(index); + }} className={`px-3 py-2 cursor-pointer text-sm ${ index === selectedIndex ? 'bg-muted text-foreground' @@ -204,6 +224,10 @@ export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) { return (
{ + if (el) itemRefs.current.set(index, el); + else itemRefs.current.delete(index); + }} className={`px-3 py-2 cursor-pointer text-sm ${ index === selectedIndex ? 'bg-muted text-foreground'