import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"; import { ITableRowMenuContext, ITableRowMenuProviderProps } from "./TableRowMenuContext.types"; const TableRowMenuContext = createContext(undefined); export const TableRowMenuProvider = ({ value, children }: ITableRowMenuProviderProps) => { const { tableRootRef, hoveredRowRef, isMenuOpen, resetHoveredRow, setHoveredRowRef, setIsMenuOpen } = value; const rowMouseLeaveTimeoutId = useRef>(null); const [menuButtonPosition, setMenuButtonPosition] = useState(0); const hasMenuRef = useRef(null); const resetTimeout = useCallback(() => { clearTimeout(rowMouseLeaveTimeoutId.current); rowMouseLeaveTimeoutId.current = null; }, []); const onMouseOverRow = useCallback( (rowRef: React.RefObject) => { if (hasMenuRef.current === false || isMenuOpen || hoveredRowRef?.current === rowRef.current) return; resetTimeout(); setHoveredRowRef(rowRef); const tableRootTop = tableRootRef.current.getBoundingClientRect().top; const rowTop = rowRef.current.getBoundingClientRect().top; setMenuButtonPosition(rowTop - tableRootTop); if (hasMenuRef.current === null) { setTimeout(() => { hasMenuRef.current = !!document?.querySelector(`[data-row-menu-id]`); }); } }, [isMenuOpen, hoveredRowRef, resetTimeout, setHoveredRowRef, tableRootRef] ); const onMouseLeaveRow = useCallback(() => { if (isMenuOpen) return; rowMouseLeaveTimeoutId.current = setTimeout(() => { setHoveredRowRef(null); }, 400); }, [isMenuOpen, setHoveredRowRef]); const onMouseOverRowMenu = useCallback(() => { if (isMenuOpen) return; resetTimeout(); }, [isMenuOpen, resetTimeout]); const onMouseLeaveRowMenu = useCallback(() => { if (isMenuOpen) return; setHoveredRowRef(null); }, [isMenuOpen, setHoveredRowRef]); const contextValue = useMemo( () => ({ hoveredRowId: hoveredRowRef?.current?.id, resetHoveredRow, menuButtonPosition, onMouseOverRow, onMouseLeaveRow, onMouseOverRowMenu, onMouseLeaveRowMenu, setTableMenuShown: () => setIsMenuOpen(true), setTableMenuHidden: () => setIsMenuOpen(false) }), [ hoveredRowRef, resetHoveredRow, menuButtonPosition, onMouseLeaveRow, onMouseLeaveRowMenu, onMouseOverRow, onMouseOverRowMenu, setIsMenuOpen ] ); return {children}; }; export const useTableRowMenu = () => { const context = useContext(TableRowMenuContext); if (!context) { throw new Error("useTableRowMenuContext must be used within a TableRowMenuProvider"); } return context; };