@@ -283,6 +283,8 @@ async function loadMore() {
283283}
284284onBeforeUnmount (() => {
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// -----------------------------------
589591const 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
650619watch (
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
0 commit comments