import React, { createContext, UIEventHandler, useCallback, useContext, useMemo, useRef } from "react"; import { ITableContext, ITableProviderProps } from "./TableContext.types"; const TableContext = createContext(undefined); export const TableProvider = ({ value, children }: ITableProviderProps) => { const { setScrollLeft } = value; const headRef = useRef(null); const virtualizedListRef = useRef(null); const lastScrollLeft = useRef(0); const syncScroll = useCallback( (newScrollLeft: number, source: "head" | "body") => { if (newScrollLeft === lastScrollLeft.current) return; if (source === "body" && headRef.current) { headRef.current.scrollLeft = newScrollLeft; } if (source === "head" && virtualizedListRef.current) { virtualizedListRef.current.scrollLeft = newScrollLeft; } const hasScroll = newScrollLeft > 0; setScrollLeft(prevScroll => (prevScroll !== hasScroll ? hasScroll : prevScroll)); lastScrollLeft.current = newScrollLeft; }, [setScrollLeft] ); const onHeadScroll: UIEventHandler = useCallback( e => { const newLeft = (e.target as HTMLDivElement).scrollLeft; syncScroll(newLeft, "head"); }, [syncScroll] ); const onVirtualizedListScroll = useCallback>( e => { const newLeft = (e.target as HTMLDivElement).scrollLeft; syncScroll(newLeft, "body"); }, [syncScroll] ); const contextValue = useMemo( () => ({ ...value, headRef, onHeadScroll, virtualizedListRef, onVirtualizedListScroll }), [value, onHeadScroll, onVirtualizedListScroll] ); return {children}; }; export const useTable = () => { const context = useContext(TableContext); if (context === undefined) { throw new Error("useTable must be used within a TableProvider"); } return context; };