Skip to content

Commit c551e62

Browse files
committed
fix: switch to useAnnouncer + <NuxtAnnouncer>
1 parent 781b407 commit c551e62

File tree

4 files changed

+1521
-222
lines changed

4 files changed

+1521
-222
lines changed

app/app.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ if (import.meta.client) {
140140
{{ route.name === 'search' ? `${$t('search.title_packages')} - npmx` : message }}
141141
</NuxtRouteAnnouncer>
142142

143+
<NuxtAnnouncer />
144+
143145
<div id="main-content" class="flex-1 flex flex-col" tabindex="-1">
144146
<NuxtPage />
145147
</div>

app/pages/search.vue

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ async function loadMore() {
283283
}
284284
onBeforeUnmount(() => {
285285
updateUrlPage.cancel()
286+
announcePoliteDesktop.cancel()
287+
announcePoliteMobile.cancel()
286288
})
287289
288290
// Update URL when page changes from scrolling
@@ -584,94 +586,92 @@ defineOgImageComponent('Default', {
584586
})
585587
586588
// -----------------------------------
587-
// Live region debouncing logic
589+
// Live region announcements
588590
// -----------------------------------
589591
const isMobile = useIsMobile()
592+
const { polite } = useAnnouncer()
590593
591-
// Evaluate the text that should be announced to screen readers
592-
const rawLiveRegionMessage = computed(() => {
593-
if (isRateLimited.value) {
594-
return $t('search.rate_limited')
595-
}
596-
597-
// If status is pending, no update phrase needed yet
598-
if (status.value === 'pending') {
599-
return ''
600-
}
601-
602-
if (visibleResults.value && displayResults.value.length > 0) {
603-
if (viewMode.value === 'table' || paginationMode.value === 'paginated') {
604-
const pSize = Math.min(preferredPageSize.value, effectiveTotal.value)
605-
606-
return $t(
607-
'filters.count.showing_paginated',
608-
{
609-
pageSize: pSize.toString(),
610-
count: $n(effectiveTotal.value),
611-
},
612-
effectiveTotal.value,
613-
)
614-
}
615-
616-
if (isRelevanceSort.value) {
617-
return $t(
618-
'search.found_packages',
619-
{ count: $n(visibleResults.value.total) },
620-
visibleResults.value.total,
621-
)
622-
}
594+
const announcePoliteDesktop = debounce((message: string) => {
595+
polite(message)
596+
}, 250)
623597
624-
return $t(
625-
'search.found_packages_sorted',
626-
{ count: $n(effectiveTotal.value) },
627-
effectiveTotal.value,
628-
)
629-
}
598+
const announcePoliteMobile = debounce((message: string) => {
599+
polite(message)
600+
}, 700)
630601
631-
if (status.value === 'success' || status.value === 'error') {
632-
if (displayResults.value.length === 0 && query.value) {
633-
return $t('search.no_results', { query: query.value })
634-
}
602+
function announcePolite(message: string) {
603+
if (isMobile.value) {
604+
announcePoliteDesktop.cancel()
605+
announcePoliteMobile(message)
606+
return
635607
}
636608
637-
return ''
638-
})
639-
640-
const debouncedLiveRegionMessage = ref('')
641-
642-
const updateLiveRegionMobile = debounce((val: string) => {
643-
debouncedLiveRegionMessage.value = val
644-
}, 700)
609+
announcePoliteMobile.cancel()
610+
announcePoliteDesktop(message)
611+
}
645612
646-
const updateLiveRegionDesktop = debounce((val: string) => {
647-
debouncedLiveRegionMessage.value = val
648-
}, 250)
613+
function cancelPendingAnnouncements() {
614+
announcePoliteDesktop.cancel()
615+
announcePoliteMobile.cancel()
616+
}
649617
618+
// Announce search results changes to screen readers
650619
watch(
651-
rawLiveRegionMessage,
652-
newVal => {
653-
if (!newVal) {
654-
updateLiveRegionMobile.cancel()
655-
updateLiveRegionDesktop.cancel()
656-
debouncedLiveRegionMessage.value = ''
620+
() => ({
621+
rateLimited: isRateLimited.value,
622+
searchStatus: status.value,
623+
count: displayResults.value.length,
624+
searchQuery: query.value,
625+
mode: viewMode.value,
626+
pagMode: paginationMode.value,
627+
total: effectiveTotal.value,
628+
}),
629+
({ rateLimited, searchStatus, count, searchQuery, mode, pagMode, total }) => {
630+
if (rateLimited) {
631+
announcePolite($t('search.rate_limited'))
657632
return
658633
}
659634
660-
if (isMobile.value) {
661-
updateLiveRegionDesktop.cancel()
662-
updateLiveRegionMobile(newVal)
663-
} else {
664-
updateLiveRegionMobile.cancel()
665-
updateLiveRegionDesktop(newVal)
635+
// Don't announce while searching
636+
if (searchStatus === 'pending') {
637+
cancelPendingAnnouncements()
638+
return
639+
}
640+
641+
if (count > 0) {
642+
if (mode === 'table' || pagMode === 'paginated') {
643+
const pSize = Math.min(preferredPageSize.value, total)
644+
645+
announcePolite(
646+
$t(
647+
'filters.count.showing_paginated',
648+
{
649+
pageSize: pSize.toString(),
650+
count: $n(total),
651+
},
652+
total,
653+
),
654+
)
655+
} else if (isRelevanceSort.value) {
656+
announcePolite(
657+
$t(
658+
'search.found_packages',
659+
{ count: $n(visibleResults.value?.total ?? 0) },
660+
visibleResults.value?.total ?? 0,
661+
),
662+
)
663+
} else {
664+
announcePolite($t('search.found_packages_sorted', { count: $n(total) }, total))
665+
}
666+
} else if (searchStatus === 'success' || searchStatus === 'error') {
667+
if (searchQuery) {
668+
announcePolite($t('search.no_results', { query: searchQuery }))
669+
} else {
670+
cancelPendingAnnouncements()
671+
}
666672
}
667673
},
668-
{ immediate: true },
669674
)
670-
671-
onBeforeUnmount(() => {
672-
updateLiveRegionMobile.cancel()
673-
updateLiveRegionDesktop.cancel()
674-
})
675675
</script>
676676

677677
<template>
@@ -910,10 +910,6 @@ onBeforeUnmount(() => {
910910
:package-scope="packageScope"
911911
:can-publish-to-scope="canPublishToScope"
912912
/>
913-
914-
<div role="status" class="sr-only">
915-
{{ debouncedLiveRegionMessage }}
916-
</div>
917913
</main>
918914
</template>
919915

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"ipaddr.js": "2.3.0",
8989
"marked": "17.0.4",
9090
"module-replacements": "2.11.0",
91-
"nuxt": "4.3.1",
91+
"nuxt": "4.4.2",
9292
"nuxt-og-image": "5.1.13",
9393
"ofetch": "1.5.1",
9494
"ohash": "2.0.11",

0 commit comments

Comments
 (0)