import { MutableRefObject, useCallback, useEffect, useRef } from "react"; import { observer } from "mobx-react-lite"; import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssue } from "@plane/types"; //types import { TSelectionHelper } from "@/hooks/use-multiple-select"; import { useTableKeyboardNavigation } from "@/hooks/use-table-keyboard-navigation"; //components import { TRenderQuickActions } from "../list/list-view-types"; import { SpreadsheetIssueRow } from "./issue-row"; import { SpreadsheetHeader } from "./spreadsheet-header"; type Props = { displayProperties: IIssueDisplayProperties; displayFilters: IIssueDisplayFilterOptions; handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void; issueIds: string[]; isEstimateEnabled: boolean; quickActions: TRenderQuickActions; updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined; canEditProperties: (projectId: string | undefined) => boolean; portalElement: React.MutableRefObject<HTMLDivElement | null>; containerRef: MutableRefObject<HTMLTableElement | null>; spreadsheetColumnsList: (keyof IIssueDisplayProperties)[]; selectionHelpers: TSelectionHelper; }; export const SpreadsheetTable = observer((props: Props) => { const { displayProperties, displayFilters, handleDisplayFilterUpdate, issueIds, isEstimateEnabled, portalElement, quickActions, updateIssue, canEditProperties, containerRef, spreadsheetColumnsList, selectionHelpers, } = props; // states const isScrolled = useRef(false); const handleScroll = useCallback(() => { if (!containerRef.current) return; const scrollLeft = containerRef.current.scrollLeft; const columnShadow = "8px 22px 22px 10px rgba(0, 0, 0, 0.05)"; // shadow for regular columns const headerShadow = "8px -22px 22px 10px rgba(0, 0, 0, 0.05)"; // shadow for headers //The shadow styles are added this way to avoid re-render of all the rows of table, which could be costly if (scrollLeft > 0 !== isScrolled.current) { const firstColumns = containerRef.current.querySelectorAll("table tr td:first-child, th:first-child"); for (let i = 0; i < firstColumns.length; i++) { const shadow = i === 0 ? headerShadow : columnShadow; if (scrollLeft > 0) { (firstColumns[i] as HTMLElement).style.boxShadow = shadow; } else { (firstColumns[i] as HTMLElement).style.boxShadow = "none"; } } isScrolled.current = scrollLeft > 0; } }, [containerRef]); useEffect(() => { const currentContainerRef = containerRef.current; if (currentContainerRef) currentContainerRef.addEventListener("scroll", handleScroll); return () => { if (currentContainerRef) currentContainerRef.removeEventListener("scroll", handleScroll); }; }, [handleScroll, containerRef]); const handleKeyBoardNavigation = useTableKeyboardNavigation(); return ( <table className="overflow-y-auto" onKeyDown={handleKeyBoardNavigation}> <SpreadsheetHeader displayProperties={displayProperties} displayFilters={displayFilters} handleDisplayFilterUpdate={handleDisplayFilterUpdate} canEditProperties={canEditProperties} isEstimateEnabled={isEstimateEnabled} spreadsheetColumnsList={spreadsheetColumnsList} selectionHelpers={selectionHelpers} /> <tbody> {issueIds.map((id) => ( <SpreadsheetIssueRow key={id} issueId={id} displayProperties={displayProperties} quickActions={quickActions} canEditProperties={canEditProperties} nestingLevel={0} isEstimateEnabled={isEstimateEnabled} updateIssue={updateIssue} portalElement={portalElement} containerRef={containerRef} isScrolled={isScrolled} issueIds={issueIds} spreadsheetColumnsList={spreadsheetColumnsList} selectionHelpers={selectionHelpers} /> ))} </tbody> </table> ); });