import { CSSProperties, Fragment, ReactNode, useEffect, useId, useMemo, useRef, useState, } from "react" import { useDataGrid } from "@/components/reui/data-grid/data-grid" import { DataGridTableBase, DataGridTableBody, DataGridTableBodyRow, DataGridTableBodyRowCell, DataGridTableBodyRowExpandded, DataGridTableBodyRowSkeleton, DataGridTableBodyRowSkeletonCell, DataGridTableEmpty, DataGridTableFoot, DataGridTableHead, DataGridTableHeadRow, DataGridTableHeadRowCell, DataGridTableHeadRowCellResize, DataGridTableRowSpacer, DataGridTableViewport, } from "@/components/reui/data-grid/data-grid-table" import { closestCenter, DndContext, KeyboardSensor, Modifier, MouseSensor, TouchSensor, useSensor, useSensors, type DragEndEvent, } from "@dnd-kit/core" import { horizontalListSortingStrategy, SortableContext, useSortable, } from "@dnd-kit/sortable" import { CSS } from "@dnd-kit/utilities" import { Cell, flexRender, Header, HeaderGroup, Row, } from "@tanstack/react-table" import { Button } from "@evobgp/ui/components/button" import { GripVerticalIcon } from "lucide-react" function DataGridTableDndHeader({ header, }: { header: Header }) { const { props } = useDataGrid() const { column } = header // Check if column ordering is enabled for this column const canOrder = (column.columnDef as { enableColumnOrdering?: boolean }) .enableColumnOrdering !== false const { attributes, isDragging, listeners, setNodeRef, transform, transition, } = useSortable({ id: header.column.id, }) const style: CSSProperties = { opacity: isDragging ? 0.8 : 1, position: "relative", transform: CSS.Translate.toString(transform), transition, cursor: isDragging ? "grabbing" : undefined, whiteSpace: "nowrap", width: props.tableLayout?.columnsResizable ? `calc(var(--header-${header.id}-size) * 1px)` : header.column.getSize(), zIndex: isDragging ? 1 : 0, } return (
{canOrder && ( )}
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
{props.tableLayout?.columnsResizable && column.getCanResize() && ( )}
) } function DataGridTableDndCell({ cell }: { cell: Cell }) { const { props } = useDataGrid() const { isDragging, setNodeRef, transform, transition } = useSortable({ id: cell.column.id, }) const style: CSSProperties = { opacity: isDragging ? 0.8 : 1, position: "relative", transform: CSS.Translate.toString(transform), transition, cursor: isDragging ? "grabbing" : undefined, width: props.tableLayout?.columnsResizable ? `calc(var(--col-${cell.column.id}-size) * 1px)` : cell.column.getSize(), zIndex: isDragging ? 1 : 0, } return ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ) } function DataGridTableDnd({ handleDragEnd, footerContent, }: { handleDragEnd: (event: DragEndEvent) => void footerContent?: ReactNode }) { const { table, isLoading, props } = useDataGrid() const pagination = table.getState().pagination const containerRef = useRef(null) const [isDraggingColumn, setIsDraggingColumn] = useState(false) const sensors = useSensors( useSensor(MouseSensor, {}), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {}) ) useEffect(() => { if (!isDraggingColumn) return const { body, documentElement } = document const previousBodyCursor = body.style.cursor const previousDocumentCursor = documentElement.style.cursor body.style.cursor = "grabbing" documentElement.style.cursor = "grabbing" return () => { body.style.cursor = previousBodyCursor documentElement.style.cursor = previousDocumentCursor } }, [isDraggingColumn]) // Custom modifier to restrict dragging within table bounds with edge offset const modifiers = useMemo(() => { const restrictToTableBounds: Modifier = ({ draggingNodeRect, transform, }) => { if (!draggingNodeRect || !containerRef.current) { return { ...transform, y: 0 } } const containerRect = containerRef.current.getBoundingClientRect() const edgeOffset = 0 const minX = containerRect.left - draggingNodeRect.left - edgeOffset const maxX = containerRect.right - draggingNodeRect.left - draggingNodeRect.width + edgeOffset return { ...transform, x: Math.min(Math.max(transform.x, minX), maxX), y: 0, // Lock vertical movement } } return [restrictToTableBounds] }, []) return ( setIsDraggingColumn(false)} onDragEnd={(event) => { setIsDraggingColumn(false) handleDragEnd(event) }} onDragStart={() => setIsDraggingColumn(true)} sensors={sensors} > {table .getHeaderGroups() .map((headerGroup: HeaderGroup, index) => { return ( {headerGroup.headers.map((header) => ( ))} ) })} {(props.tableLayout?.stripped || !props.tableLayout?.rowBorder) && ( )} {props.loadingMode === "skeleton" && isLoading && pagination?.pageSize ? ( Array.from({ length: pagination.pageSize }).map((_, rowIndex) => ( {table.getVisibleFlatColumns().map((column, colIndex) => { return ( {column.columnDef.meta?.skeleton} ) })} )) ) : table.getRowModel().rows.length ? ( table.getRowModel().rows.map((row: Row) => { return ( {row .getVisibleCells() .map((cell: Cell) => ( ))} {row.getIsExpanded() && ( )} ) }) ) : ( )} {footerContent && ( {footerContent} )} ) } export { DataGridTableDnd }