Skip to content

Commit e74905a

Browse files
committed
fix: mobile + desktop search focus fixes
1 parent 485cc9c commit e74905a

3 files changed

Lines changed: 24 additions & 11 deletions

File tree

app/components/AppHeader.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ function expandMobileSearch() {
3030
})
3131
}
3232
33+
watch(isOnSearchPage, () => {
34+
searchBoxRef.value?.focus()
35+
nextTick(() => {
36+
searchBoxRef.value?.focus()
37+
})
38+
})
39+
3340
function handleSearchBlur() {
3441
showFullSearch.value = false
3542
// Collapse expanded search on mobile after blur (with delay for click handling)

app/pages/index.vue

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
<script setup lang="ts">
22
import { debounce } from 'perfect-debounce'
33
4-
const router = useRouter()
54
const searchQuery = shallowRef('')
65
const searchInputRef = useTemplateRef('searchInputRef')
76
const { focused: isSearchFocused } = useFocus(searchInputRef)
87
98
const isMobile = useIsMobile()
109
11-
const debouncedNavigate = debounce(() => {
12-
router.push({
10+
async function search() {
11+
const query = searchQuery.value.trim()
12+
await navigateTo({
1313
path: '/search',
14-
query: searchQuery.value.trim() ? { q: searchQuery.value.trim() } : undefined,
14+
query: query ? { q: query } : undefined,
1515
})
16-
}, 250)
17-
18-
function handleSearch() {
19-
// If input is empty, navigate immediately (no need to debounce)
20-
return searchQuery.value.trim() ? debouncedNavigate() : router.push('/search')
2116
}
2217
18+
const handleInput = isTouchDevice()
19+
? search
20+
: debounce(search, 250, { leading: true, trailing: true })
21+
2322
useSeoMeta({
2423
title: () => $t('seo.home.title'),
2524
description: () => $t('seo.home.description'),
@@ -64,7 +63,7 @@ defineOgImageComponent('Default', {
6463
class="w-full max-w-xl motion-safe:animate-slide-up motion-safe:animate-fill-both"
6564
style="animation-delay: 0.2s"
6665
>
67-
<form method="GET" action="/search" class="relative" @submit.prevent="handleSearch">
66+
<form method="GET" action="/search" class="relative" @submit.prevent.trim="search">
6867
<label for="home-search" class="sr-only">
6968
{{ $t('search.label') }}
7069
</label>
@@ -93,7 +92,7 @@ defineOgImageComponent('Default', {
9392
v-bind="noCorrect"
9493
:autofocus="!isMobile"
9594
class="w-full bg-bg-subtle border border-border rounded-lg ps-8 pe-24 py-4 font-mono text-base text-fg placeholder:text-fg-subtle transition-border-color duration-300 focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
96-
@input="handleSearch"
95+
@input="handleInput"
9796
/>
9897

9998
<button

app/utils/responsive.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @public */
2+
export function isTouchDevice() {
3+
if (import.meta.server) {
4+
return false
5+
}
6+
return 'ontouchstart' in window || navigator.maxTouchPoints > 0
7+
}

0 commit comments

Comments
 (0)