All files / src/pages/flowsheet-page/economics/results-panel/navigation useResultRowFocus.ts

35.71% Statements 15/42
36.36% Branches 4/11
22.22% Functions 2/9
37.83% Lines 14/37

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,
  };
}