diff --git a/apps/compliance-portal/src/pages/documents/_components/DocumentAccessAction.tsx b/apps/compliance-portal/src/pages/documents/_components/DocumentAccessAction.tsx index 7f01a6285..f936d2c6e 100644 --- a/apps/compliance-portal/src/pages/documents/_components/DocumentAccessAction.tsx +++ b/apps/compliance-portal/src/pages/documents/_components/DocumentAccessAction.tsx @@ -74,6 +74,19 @@ export function DocumentAccessAction({ const { t } = useTranslation("documents"); if (!interactive) { + // Pending rows have no mobile hit overlay — expose status for assistive tech. + if (requested) { + return ( + + + + ); + } + return ( - {/* Mobile: status icon only; the row overlay handles activation. */} -
+ {/* Mobile: status icon only; the row overlay handles activation. + Pending (`requested`) rows keep the icon in the a11y tree as status. */} +
, +): number | null { + const middle = wrapper.getBoundingClientRect().top + wrapper.clientHeight / 2; + for (let index = 0; index < pages.length; index += 1) { + const page = pages[index]; + if (page == null) { + continue; + } + const rect = page.getBoundingClientRect(); + if (rect.top <= middle && rect.bottom >= middle) { + return index + 1; + } + } + return null; +} + export interface PdfPreviewHandle { scrollToPage: (page: number) => void; } @@ -73,6 +91,18 @@ export function PdfPreview({ file, scale, ref, onNumPages, onVisiblePageChange } }, }), []); + const resolveVisiblePage = () => { + const wrapper = wrapperRef.current; + const pages = documentRef.current?.pages.current; + if (!wrapper || !pages?.length) { + return; + } + const page = findCenteredPage(wrapper, pages); + if (page != null) { + onVisiblePageChange(page); + } + }; + useEffect(() => { const wrapper = wrapperRef.current; if (!wrapper) { @@ -89,21 +119,33 @@ export function PdfPreview({ file, scale, ref, onNumPages, onVisiblePageChange } return () => observer.disconnect(); }, []); - const resolveVisiblePage = () => { - const wrapper = wrapperRef.current; - const pages = documentRef.current?.pages.current; - if (!wrapper || !pages?.length) { + // After fit-to-width / zoom reflow, the same scroll offset can center a + // different page — recompute once the resized pages have painted. + useEffect(() => { + if (pageWidth == null || numPages === 0) { return; } - const middle = wrapper.getBoundingClientRect().top + wrapper.clientHeight / 2; - for (let index = 0; index < pages.length; index += 1) { - const rect = pages[index].getBoundingClientRect(); - if (rect.top <= middle && rect.bottom >= middle) { - onVisiblePageChange(index + 1); - return; - } - } - }; + + let secondFrame = 0; + const firstFrame = requestAnimationFrame(() => { + secondFrame = requestAnimationFrame(() => { + const wrapper = wrapperRef.current; + const pages = documentRef.current?.pages.current; + if (!wrapper || !pages?.length) { + return; + } + const page = findCenteredPage(wrapper, pages); + if (page != null) { + onVisiblePageChange(page); + } + }); + }); + + return () => { + cancelAnimationFrame(firstFrame); + cancelAnimationFrame(secondFrame); + }; + }, [pageWidth, scale, numPages, onVisiblePageChange]); const slots = pdfPreview();