plane/web/components/issues/issue-layouts/spreadsheet/spreadsheet-table.tsx

137 lines
4.7 KiB
TypeScript

import { MutableRefObject, useCallback, useEffect, useRef } from "react";
import { observer } from "mobx-react-lite";
//types
import { useTableKeyboardNavigation } from "hooks/use-table-keyboard-navigation";
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssue } from "@plane/types";
//components
import { SpreadsheetIssueRow } from "./issue-row";
import { SpreadsheetHeader } from "./spreadsheet-header";
import { useIntersectionObserver } from "hooks/use-intersection-observer";
type Props = {
displayProperties: IIssueDisplayProperties;
displayFilters: IIssueDisplayFilterOptions;
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
issueIds: string[];
isEstimateEnabled: boolean;
quickActions: (
issue: TIssue,
customActionButton?: React.ReactElement,
portalElement?: HTMLDivElement | null
) => React.ReactNode;
updateIssue:
| ((projectId: string | undefined | null, issueId: string, data: Partial<TIssue>) => Promise<void>)
| undefined;
canEditProperties: (projectId: string | undefined) => boolean;
portalElement: React.MutableRefObject<HTMLDivElement | null>;
containerRef: MutableRefObject<HTMLTableElement | null>;
onEndOfListTrigger: () => void;
};
export const SpreadsheetTable = observer((props: Props) => {
const {
displayProperties,
displayFilters,
handleDisplayFilterUpdate,
issueIds,
isEstimateEnabled,
portalElement,
quickActions,
updateIssue,
canEditProperties,
containerRef,
onEndOfListTrigger,
} = props;
// states
const isScrolled = useRef(false);
const intersectionRef = useRef<HTMLTableSectionElement | null>(null);
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]);
// useEffect(() => {
// if (intersectionRef.current) {
// const observer = new IntersectionObserver(
// (entries) => {
// if (entries[0].isIntersecting) onEndOfListTrigger();
// },
// {
// root: containerRef?.current,
// rootMargin: `50% 0% 50% 0%`,
// }
// );
// observer.observe(intersectionRef.current);
// return () => {
// if (intersectionRef.current) {
// // eslint-disable-next-line react-hooks/exhaustive-deps
// observer.unobserve(intersectionRef.current);
// }
// };
// }
// }, [intersectionRef, containerRef]);
useIntersectionObserver(containerRef, intersectionRef, onEndOfListTrigger, `50% 0% 50% 0%`);
const handleKeyBoardNavigation = useTableKeyboardNavigation();
return (
<table className="overflow-y-auto" onKeyDown={handleKeyBoardNavigation}>
<SpreadsheetHeader
displayProperties={displayProperties}
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
isEstimateEnabled={isEstimateEnabled}
/>
<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}
/>
))}
</tbody>
<tfoot ref={intersectionRef}>Loading...</tfoot>
</table>
);
});