Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 76x 76x 78x 78x 78x 78x 78x 78x 78x 78x 78x 5x 78x 78x | import { useCallback, useEffect, useRef, useState } from "react";
import type { EconomicsResultLineRead } from "@/api/apiStore.gen";
import { scrollElementIntoEconomicsContext } from "../../shared/model/economicsScroll";
import {
detailSectionsForLineId,
mergeDetailSections,
} from "../model/resultLineSelectors";
import type { DetailSectionValue } from "../model/types";
const RESULT_ROW_HIGHLIGHT_MS = 1400;
const RESULT_ROW_SCROLL_ATTEMPTS = 8;
export function useResultRowFocus({
lines,
focusResultLineId,
focusRequestKey,
}: {
lines?: EconomicsResultLineRead[];
focusResultLineId?: number;
focusRequestKey: number;
}) {
const [selectedSourceRowId, setSelectedSourceRowId] = useState<number>();
const [openDetailSections, setOpenDetailSections] = useState<
DetailSectionValue[]
>([]);
const rowRefs = useRef(new Map<number, HTMLElement>());
const handledFocusRequestRef = useRef<string | null>(null);
const highlightClearTimeoutRef = useRef<ReturnType<
typeof window.setTimeout
> | null>(null);
const scrollToSourceRow = useCallback((sourceRowId: number, attempt = 0) => {
window.requestAnimationFrame(() => {
const row = rowRefs.current.get(sourceRowId);
if (row) {
scrollElementIntoEconomicsContext(row);
row.focus();
return;
}
if (attempt < RESULT_ROW_SCROLL_ATTEMPTS) {
scrollToSourceRow(sourceRowId, attempt + 1);
}
});
}, []);
const selectSourceRow = useCallback(
(sourceRowId: number | undefined) => {
Iif (!sourceRowId) return;
if (lines) {
const sourceSections = detailSectionsForLineId(lines, sourceRowId);
setOpenDetailSections((current) =>
mergeDetailSections(current, sourceSections),
);
}
if (highlightClearTimeoutRef.current !== null) {
window.clearTimeout(highlightClearTimeoutRef.current);
}
setSelectedSourceRowId(sourceRowId);
highlightClearTimeoutRef.current = window.setTimeout(() => {
setSelectedSourceRowId((current) =>
current === sourceRowId ? undefined : current,
);
highlightClearTimeoutRef.current = null;
}, RESULT_ROW_HIGHLIGHT_MS);
scrollToSourceRow(sourceRowId);
},
[lines, scrollToSourceRow],
);
useEffect(() => {
return () => {
if (highlightClearTimeoutRef.current !== null) {
window.clearTimeout(highlightClearTimeoutRef.current);
}
};
}, []);
useEffect(() => {
if (!focusResultLineId) return;
const focusRequestId =
String(focusRequestKey) + ":" + String(focusResultLineId);
Iif (handledFocusRequestRef.current === focusRequestId) return;
Iif (!lines?.some((line) => line.id === focusResultLineId)) return;
handledFocusRequestRef.current = focusRequestId;
selectSourceRow(focusResultLineId);
}, [focusRequestKey, focusResultLineId, lines, selectSourceRow]);
const detailSectionValues = mergeDetailSections(
openDetailSections,
lines && selectedSourceRowId
? detailSectionsForLineId(lines, selectedSourceRowId)
: [],
);
return {
selectedSourceRowId,
rowRefs: rowRefs.current,
detailSectionValues,
setOpenDetailSections,
selectSourceRow,
};
}
|