@@ -48,6 +48,7 @@ const isSearchFocused = ref(false)
4848const searchInputRef = ref <HTMLInputElement >()
4949
5050const selectedIndex = ref (0 )
51+ const packageListRef = useTemplateRef (' packageListRef' )
5152
5253const resultCount = computed (() => visibleResults .value ?.objects .length ?? 0 )
5354
@@ -56,25 +57,46 @@ function clampIndex(next: number) {
5657 return Math .max (0 , Math .min (resultCount .value - 1 , next ))
5758}
5859
60+ function scrollToSelectedResult() {
61+ // Use virtualizer's scrollToIndex to ensure the item is rendered and visible
62+ packageListRef .value ?.scrollToIndex (selectedIndex .value )
63+ }
64+
5965function focusSelectedResult() {
60- const el = document .querySelector <HTMLElement >(` [data-result-index="${selectedIndex .value }"] ` )
61- el ?.focus ()
66+ // First ensure the item is rendered by scrolling to it
67+ scrollToSelectedResult ()
68+ // Then focus it after a tick to allow rendering
69+ nextTick (() => {
70+ const el = document .querySelector <HTMLElement >(` [data-result-index="${selectedIndex .value }"] ` )
71+ el ?.focus ()
72+ })
6273}
6374
6475function handleResultsKeydown(e : KeyboardEvent ) {
6576 if (resultCount .value <= 0 ) return
6677
78+ const isFromInput = (e .target as HTMLElement ).tagName === ' INPUT'
79+
6780 if (e .key === ' ArrowDown' ) {
6881 e .preventDefault ()
6982 selectedIndex .value = clampIndex (selectedIndex .value + 1 )
70- focusSelectedResult ()
83+ // Only move focus if already in results, not when typing in search input
84+ if (isFromInput ) {
85+ scrollToSelectedResult ()
86+ } else {
87+ focusSelectedResult ()
88+ }
7189 return
7290 }
7391
7492 if (e .key === ' ArrowUp' ) {
7593 e .preventDefault ()
7694 selectedIndex .value = clampIndex (selectedIndex .value - 1 )
77- focusSelectedResult ()
95+ if (isFromInput ) {
96+ scrollToSelectedResult ()
97+ } else {
98+ focusSelectedResult ()
99+ }
78100 return
79101 }
80102
@@ -198,13 +220,10 @@ watch(query, () => {
198220 hasInteracted .value = true
199221})
200222
201- watch (
202- () => visibleResults .value ?.objects ,
203- objects => {
204- if (! objects ?.length ) return
205- selectedIndex .value = 0
206- },
207- )
223+ // Reset selection when query changes (new search)
224+ watch (query , () => {
225+ selectedIndex .value = 0
226+ })
208227
209228// Check if current query could be a valid package name
210229const isValidPackageName = computed (() => isValidNewPackageName (query .value .trim ()))
@@ -423,6 +442,7 @@ defineOgImageComponent('Default', {
423442
424443 <PackageList
425444 v-if =" visibleResults.objects.length > 0"
445+ ref =" packageListRef"
426446 :results =" visibleResults.objects"
427447 :selected-index =" selectedIndex"
428448 heading-level =" h2"
0 commit comments