diff --git a/app/pages/search.vue b/app/pages/search.vue index cd3faef3e2..bb9bba7d12 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -555,6 +555,98 @@ defineOgImageComponent('Default', { : $t('search.meta_description_packages'), primaryColor: '#60a5fa', }) + +// ----------------------------------- +// Live region debouncing logic +// ----------------------------------- +const isMobile = useIsMobile() + +// Evaluate the text that should be announced to screen readers +const rawLiveRegionMessage = computed(() => { + if (isRateLimited.value) { + return $t('search.rate_limited') + } + + // If status is pending, no update phrase needed yet + if (status.value === 'pending') { + return '' + } + + if (visibleResults.value && displayResults.value.length > 0) { + if (viewMode.value === 'table' || paginationMode.value === 'paginated') { + const pSize = + preferredPageSize.value === 'all' + ? $n(effectiveTotal.value) + : Math.min(preferredPageSize.value, effectiveTotal.value) + return $t( + 'filters.count.showing_paginated', + { + pageSize: pSize.toString(), + count: $n(effectiveTotal.value), + }, + effectiveTotal.value, + ) + } + + if (isRelevanceSort.value) { + return $t( + 'search.found_packages', + { count: $n(visibleResults.value.total) }, + visibleResults.value.total, + ) + } + + return $t( + 'search.found_packages_sorted', + { count: $n(effectiveTotal.value) }, + effectiveTotal.value, + ) + } + + if (status.value === 'success' || status.value === 'error') { + if (displayResults.value.length === 0 && query.value) { + return $t('search.no_results', { query: query.value }) + } + } + + return '' +}) + +const debouncedLiveRegionMessage = ref('') + +const updateLiveRegionMobile = debounce((val: string) => { + debouncedLiveRegionMessage.value = val +}, 700) + +const updateLiveRegionDesktop = debounce((val: string) => { + debouncedLiveRegionMessage.value = val +}, 250) + +watch( + rawLiveRegionMessage, + newVal => { + if (!newVal) { + updateLiveRegionMobile.cancel() + updateLiveRegionDesktop.cancel() + debouncedLiveRegionMessage.value = '' + return + } + + if (isMobile.value) { + updateLiveRegionDesktop.cancel() + updateLiveRegionMobile(newVal) + } else { + updateLiveRegionMobile.cancel() + updateLiveRegionDesktop(newVal) + } + }, + { immediate: true }, +) + +onBeforeUnmount(() => { + updateLiveRegionMobile.cancel() + updateLiveRegionDesktop.cancel() +})