Skip to content

Commit 7fb94bc

Browse files
authored
fix: debounce search results & centralized live region (#1812)
1 parent a90ec6b commit 7fb94bc

1 file changed

Lines changed: 101 additions & 7 deletions

File tree

app/pages/search.vue

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,98 @@ defineOgImageComponent('Default', {
555555
: $t('search.meta_description_packages'),
556556
primaryColor: '#60a5fa',
557557
})
558+
559+
// -----------------------------------
560+
// Live region debouncing logic
561+
// -----------------------------------
562+
const isMobile = useIsMobile()
563+
564+
// Evaluate the text that should be announced to screen readers
565+
const rawLiveRegionMessage = computed(() => {
566+
if (isRateLimited.value) {
567+
return $t('search.rate_limited')
568+
}
569+
570+
// If status is pending, no update phrase needed yet
571+
if (status.value === 'pending') {
572+
return ''
573+
}
574+
575+
if (visibleResults.value && displayResults.value.length > 0) {
576+
if (viewMode.value === 'table' || paginationMode.value === 'paginated') {
577+
const pSize =
578+
preferredPageSize.value === 'all'
579+
? $n(effectiveTotal.value)
580+
: Math.min(preferredPageSize.value, effectiveTotal.value)
581+
return $t(
582+
'filters.count.showing_paginated',
583+
{
584+
pageSize: pSize.toString(),
585+
count: $n(effectiveTotal.value),
586+
},
587+
effectiveTotal.value,
588+
)
589+
}
590+
591+
if (isRelevanceSort.value) {
592+
return $t(
593+
'search.found_packages',
594+
{ count: $n(visibleResults.value.total) },
595+
visibleResults.value.total,
596+
)
597+
}
598+
599+
return $t(
600+
'search.found_packages_sorted',
601+
{ count: $n(effectiveTotal.value) },
602+
effectiveTotal.value,
603+
)
604+
}
605+
606+
if (status.value === 'success' || status.value === 'error') {
607+
if (displayResults.value.length === 0 && query.value) {
608+
return $t('search.no_results', { query: query.value })
609+
}
610+
}
611+
612+
return ''
613+
})
614+
615+
const debouncedLiveRegionMessage = ref('')
616+
617+
const updateLiveRegionMobile = debounce((val: string) => {
618+
debouncedLiveRegionMessage.value = val
619+
}, 700)
620+
621+
const updateLiveRegionDesktop = debounce((val: string) => {
622+
debouncedLiveRegionMessage.value = val
623+
}, 250)
624+
625+
watch(
626+
rawLiveRegionMessage,
627+
newVal => {
628+
if (!newVal) {
629+
updateLiveRegionMobile.cancel()
630+
updateLiveRegionDesktop.cancel()
631+
debouncedLiveRegionMessage.value = ''
632+
return
633+
}
634+
635+
if (isMobile.value) {
636+
updateLiveRegionDesktop.cancel()
637+
updateLiveRegionMobile(newVal)
638+
} else {
639+
updateLiveRegionMobile.cancel()
640+
updateLiveRegionDesktop(newVal)
641+
}
642+
},
643+
{ immediate: true },
644+
)
645+
646+
onBeforeUnmount(() => {
647+
updateLiveRegionMobile.cancel()
648+
updateLiveRegionDesktop.cancel()
649+
})
558650
</script>
559651

560652
<template>
@@ -615,7 +707,7 @@ defineOgImageComponent('Default', {
615707
</button>
616708
</div>
617709

618-
<div v-if="isRateLimited" role="status" class="py-12">
710+
<div v-if="isRateLimited" class="py-12">
619711
<p class="text-fg-muted font-mono mb-6 text-center">
620712
{{ $t('search.rate_limited') }}
621713
</p>
@@ -648,7 +740,6 @@ defineOgImageComponent('Default', {
648740
/>
649741
<p
650742
v-if="viewMode === 'cards' && paginationMode === 'infinite'"
651-
role="status"
652743
class="text-fg-muted text-sm mt-4 font-mono"
653744
>
654745
<template v-if="isRelevanceSort">
@@ -665,13 +756,12 @@ defineOgImageComponent('Default', {
665756
$t('search.found_packages_sorted', { count: $n(effectiveTotal) }, effectiveTotal)
666757
}}
667758
</template>
668-
<span v-if="status === 'pending'" class="text-fg-subtle">{{
669-
$t('search.updating')
670-
}}</span>
759+
<span aria-hidden="true" v-if="status === 'pending'" class="text-fg-subtle">
760+
{{ $t('search.updating') }}
761+
</span>
671762
</p>
672763
<p
673764
v-if="viewMode === 'table' || paginationMode === 'paginated'"
674-
role="status"
675765
class="text-fg-muted text-sm mt-4 font-mono"
676766
>
677767
{{
@@ -690,7 +780,7 @@ defineOgImageComponent('Default', {
690780
</p>
691781
</div>
692782

693-
<div v-else-if="status === 'success' || status === 'error'" role="status" class="py-12">
783+
<div v-else-if="status === 'success' || status === 'error'" class="py-12">
694784
<p class="text-fg-muted font-mono mb-6 text-center">
695785
{{ $t('search.no_results', { query }) }}
696786
</p>
@@ -767,6 +857,10 @@ defineOgImageComponent('Default', {
767857
:package-scope="packageScope"
768858
:can-publish-to-scope="canPublishToScope"
769859
/>
860+
861+
<div role="status" class="sr-only">
862+
{{ debouncedLiveRegionMessage }}
863+
</div>
770864
</main>
771865
</template>
772866

0 commit comments

Comments
 (0)