import React, { createRef, useCallback, useEffect, useMemo, useState } from "react"; import { VirtualTableColumn, VirtualTableWhereFilterOp } from "./VirtualTableProps"; import { ErrorBoundary } from "../ErrorBoundary"; import { VirtualTableHeader } from "./VirtualTableHeader"; import { VirtualTableContextProps } from "./types"; import { defaultBorderMixin } from "../../styles"; import { cls } from "../../util/cls";import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; // Sortable column header wrapper const SortableColumnHeader = ({ column, columnIndex, columnRefs, isResizing, onFilterUpdate, filter, sortIndex, sortCount, onColumnSort, onClickResizeColumn, createFilterField, isDragging, isDraggable }: { column: VirtualTableColumn; columnIndex: number; columnRefs: React.Ref[]; isResizing: number; onFilterUpdate: (column: VirtualTableColumn, filterForProperty?: [VirtualTableWhereFilterOp, unknown]) => void; filter: [VirtualTableWhereFilterOp, unknown] | undefined; sortIndex: Map; sortCount: number; onColumnSort: (key: string, additive?: boolean) => void; onClickResizeColumn: (index: number) => void; createFilterField: ((props: import("./VirtualTableHeader").FilterFormFieldProps) => React.ReactNode) | undefined; isDragging: boolean; isDraggable: boolean; }) => { const [isPressing, setIsPressing] = useState(false); const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: column.key, disabled: !isDraggable || column.frozen }); const style = { // Only use translate, ignore any scale transforms transform: transform ? `translateX(${transform.x}px)` : undefined, // Don't transition the dragged item - only other items should animate transition: isDragging ? undefined : transition, minWidth: column.width, maxWidth: column.width, width: column.width }; // Combine our press handlers with dnd-kit listeners const combinedListeners = isDraggable ? { ...listeners, onPointerDown: (e: React.PointerEvent) => { setIsPressing(true); listeners?.onPointerDown?.(e); }, onPointerUp: () => setIsPressing(false), onPointerCancel: () => setIsPressing(false) } : {}; // Reset pressing state when drag ends React.useEffect(() => { if (!isDragging) { setIsPressing(false); } }, [isDragging]); return (
1 ? sortIndex.get(column.key)?.position : undefined} onColumnSort={onColumnSort} onClickResizeColumn={onClickResizeColumn} column={column} createFilterField={createFilterField} AdditionalHeaderWidget={column.AdditionalHeaderWidget} isDragging={isDragging || isPressing} isDraggable={isDraggable}/>
); }; export const VirtualTableHeaderRow = ({ columns, sortIndex, onColumnSort, onFilterUpdate, filter, onColumnResize, onColumnResizeEnd, createFilterField, AddColumnComponent, onColumnsOrderChange, data, cellRenderer: CellRenderer, rowHeight = 54, draggingColumnId, headerHeight = 48 }: VirtualTableContextProps>) => { const columnRefs = useMemo(() => columns.map(() => createRef()), [columns.length]); const [isResizing, setIsResizing] = useState(-1); const adjustWidthColumn = useCallback((index: number, width: number, end: boolean) => { const column = columns[index]; const minWidth = 100; const maxWidth = 800; const newWidth = width > maxWidth ? maxWidth : (width < minWidth ? minWidth : width); const params = { width: newWidth, key: column.key as string, column: { ...column, width: newWidth } as VirtualTableColumn }; if (!end) onColumnResize(params); else onColumnResizeEnd(params); }, [columns, onColumnResize, onColumnResizeEnd]); const getEventNewWidth = useCallback((e: MouseEvent) => { if (isResizing >= 0) { const left = columnRefs[isResizing].current?.parentElement?.getBoundingClientRect().left; if (!left) return; return e.clientX - left; } return undefined; }, [columnRefs, isResizing]); const setCursorDocument = useCallback((isResizing: boolean) => { document.body.style.cursor = isResizing ? "col-resize" : "auto"; }, []); const handleOnMouseMove = useCallback((e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); const newWidth = getEventNewWidth(e); if (newWidth) adjustWidthColumn(isResizing, newWidth, false); }, [adjustWidthColumn, getEventNewWidth, isResizing]); const handleOnMouseUp = useCallback((e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); const newWidth = getEventNewWidth(e); if (newWidth) adjustWidthColumn(isResizing, newWidth, true); setIsResizing(-1); setCursorDocument(false); }, [adjustWidthColumn, getEventNewWidth, isResizing, setCursorDocument]); const removeResizingListeners = useCallback(() => { document.removeEventListener("mousemove", handleOnMouseMove); document.removeEventListener("mouseup", handleOnMouseUp); }, [handleOnMouseMove, handleOnMouseUp]); const attachResizeListeners = useCallback(() => { document.addEventListener("mousemove", handleOnMouseMove); document.addEventListener("mouseup", handleOnMouseUp); }, [handleOnMouseMove, handleOnMouseUp]); useEffect(() => { if (isResizing >= 0) { attachResizeListeners(); } else { removeResizingListeners(); } return () => { removeResizingListeners(); }; }, [attachResizeListeners, isResizing, removeResizingListeners]); const onClickResizeColumn = useCallback((index: number) => { setIsResizing(index); setCursorDocument(true); }, [setCursorDocument]); return ( <>
{columns.map((column, columnIndex) => { const columnFilter = column && filter && filter[column.key] ? filter[column.key] : undefined; const filterForThisProperty: [VirtualTableWhereFilterOp, unknown] | undefined = columnFilter ? (Array.isArray(columnFilter[0]) ? (columnFilter as [VirtualTableWhereFilterOp, unknown][])[0] : (columnFilter as [VirtualTableWhereFilterOp, unknown])) : undefined; const isDraggable = !column.frozen && !!onColumnsOrderChange; const isDragging = draggingColumnId === column.key; return ( ); })} {AddColumnComponent && }
); };