@@ -283,6 +283,7 @@ async function loadMore() {
283283}
284284onBeforeUnmount (() => {
285285 updateUrlPage .cancel ()
286+ cancelPendingAnnouncements ()
286287})
287288
288289// Update URL when page changes from scrolling
@@ -584,94 +585,92 @@ defineOgImageComponent('Default', {
584585})
585586
586587// -----------------------------------
587- // Live region debouncing logic
588+ // Live region announcements
588589// -----------------------------------
589590const isMobile = useIsMobile ()
591+ const { polite } = useAnnouncer ()
590592
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- }
593+ const announcePoliteDesktop = debounce ((message : string ) => {
594+ polite (message )
595+ }, 250 )
623596
624- return $t (
625- ' search.found_packages_sorted' ,
626- { count: $n (effectiveTotal .value ) },
627- effectiveTotal .value ,
628- )
629- }
597+ const announcePoliteMobile = debounce ((message : string ) => {
598+ polite (message )
599+ }, 700 )
630600
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- }
601+ function announcePolite(message : string ) {
602+ if (isMobile .value ) {
603+ announcePoliteDesktop .cancel ()
604+ announcePoliteMobile (message )
605+ return
635606 }
636607
637- return ' '
638- })
639-
640- const debouncedLiveRegionMessage = ref (' ' )
641-
642- const updateLiveRegionMobile = debounce ((val : string ) => {
643- debouncedLiveRegionMessage .value = val
644- }, 700 )
608+ announcePoliteMobile .cancel ()
609+ announcePoliteDesktop (message )
610+ }
645611
646- const updateLiveRegionDesktop = debounce ((val : string ) => {
647- debouncedLiveRegionMessage .value = val
648- }, 250 )
612+ function cancelPendingAnnouncements() {
613+ announcePoliteDesktop .cancel ()
614+ announcePoliteMobile .cancel ()
615+ }
649616
617+ // Announce search results changes to screen readers
650618watch (
651- rawLiveRegionMessage ,
652- newVal => {
653- if (! newVal ) {
654- updateLiveRegionMobile .cancel ()
655- updateLiveRegionDesktop .cancel ()
656- debouncedLiveRegionMessage .value = ' '
619+ () => ({
620+ rateLimited: isRateLimited .value ,
621+ searchStatus: status .value ,
622+ count: displayResults .value .length ,
623+ searchQuery: query .value ,
624+ mode: viewMode .value ,
625+ pagMode: paginationMode .value ,
626+ total: effectiveTotal .value ,
627+ }),
628+ ({ rateLimited , searchStatus , count , searchQuery , mode , pagMode , total }) => {
629+ if (rateLimited ) {
630+ announcePolite ($t (' search.rate_limited' ))
657631 return
658632 }
659633
660- if (isMobile .value ) {
661- updateLiveRegionDesktop .cancel ()
662- updateLiveRegionMobile (newVal )
663- } else {
664- updateLiveRegionMobile .cancel ()
665- updateLiveRegionDesktop (newVal )
634+ // Don't announce while searching
635+ if (searchStatus === ' pending' ) {
636+ cancelPendingAnnouncements ()
637+ return
638+ }
639+
640+ if (count > 0 ) {
641+ if (mode === ' table' || pagMode === ' paginated' ) {
642+ const pSize = Math .min (preferredPageSize .value , total )
643+
644+ announcePolite (
645+ $t (
646+ ' filters.count.showing_paginated' ,
647+ {
648+ pageSize: pSize .toString (),
649+ count: $n (total ),
650+ },
651+ total ,
652+ ),
653+ )
654+ } else if (isRelevanceSort .value ) {
655+ announcePolite (
656+ $t (
657+ ' search.found_packages' ,
658+ { count: $n (visibleResults .value ?.total ?? 0 ) },
659+ visibleResults .value ?.total ?? 0 ,
660+ ),
661+ )
662+ } else {
663+ announcePolite ($t (' search.found_packages_sorted' , { count: $n (total ) }, total ))
664+ }
665+ } else if (searchStatus === ' success' || searchStatus === ' error' ) {
666+ if (searchQuery ) {
667+ announcePolite ($t (' search.no_results' , { query: searchQuery }))
668+ } else {
669+ cancelPendingAnnouncements ()
670+ }
666671 }
667672 },
668- { immediate: true },
669673)
670-
671- onBeforeUnmount (() => {
672- updateLiveRegionMobile .cancel ()
673- updateLiveRegionDesktop .cancel ()
674- })
675674 </script >
676675
677676<template >
@@ -910,10 +909,6 @@ onBeforeUnmount(() => {
910909 :package-scope =" packageScope"
911910 :can-publish-to-scope =" canPublishToScope"
912911 />
913-
914- <div role =" status" class =" sr-only" >
915- {{ debouncedLiveRegionMessage }}
916- </div >
917912 </main >
918913</template >
919914
0 commit comments