{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"data-grid-table-dnd","type":"registry:ui","title":"Data Grid Table Dnd","description":"","dependencies":["@dnd-kit/core","@dnd-kit/sortable","@dnd-kit/utilities","@tanstack/react-table"],"registryDependencies":["button","@neui/data-grid","@neui/data-grid-table"],"files":[{"path":"data-grid-table-dnd.tsx","type":"registry:ui","content":"\"use client\"\n\"use no memo\"\n\nimport {\n Fragment,\n memo,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport type { CSSProperties, ReactNode } from \"react\"\nimport { useDataGrid } from \"@/components/neui/data-grid/data-grid\"\nimport {\n DataGridTableBase,\n DataGridTableBody,\n DataGridTableBodyRow,\n DataGridTableBodyRowCell,\n DataGridTableBodyRowExpandded,\n DataGridTableBodyRowSkeleton,\n DataGridTableBodyRowSkeletonCell,\n DataGridTableEmpty,\n DataGridTableFillBodyCell,\n DataGridTableFillHeadCell,\n DataGridTableFoot,\n DataGridTableHead,\n DataGridTableHeadRow,\n DataGridTableHeadRowCell,\n DataGridTableHeadRowCellResize,\n DataGridTableRowSpacer,\n DataGridTableViewport,\n} from \"@/components/neui/data-grid/data-grid-table\"\nimport {\n closestCenter,\n DndContext,\n KeyboardSensor,\n MouseSensor,\n TouchSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n type Modifier,\n} from \"@dnd-kit/core\"\nimport {\n horizontalListSortingStrategy,\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n} from \"@dnd-kit/sortable\"\nimport { CSS } from \"@dnd-kit/utilities\"\nimport { flexRender } from \"@tanstack/react-table\"\nimport type {\n Cell,\n Header,\n HeaderGroup,\n Row,\n Table,\n} from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction DataGridTableDndHeader({\n header,\n}: {\n header: Header\n}) {\n const { props } = useDataGrid()\n const { column } = header\n\n // Check if column ordering is enabled for this column\n const canOrder =\n (column.columnDef as { enableColumnOrdering?: boolean })\n .enableColumnOrdering !== false\n\n const {\n attributes,\n isDragging,\n listeners,\n setNodeRef,\n transform,\n transition,\n } = useSortable({\n id: header.column.id,\n })\n\n const style: CSSProperties = {\n opacity: isDragging ? 0.8 : 1,\n position: \"relative\",\n transform: CSS.Translate.toString(transform),\n transition,\n cursor: isDragging ? \"grabbing\" : undefined,\n whiteSpace: \"nowrap\",\n width: props.tableLayout?.columnsResizable\n ? `calc(var(--header-${header.id}-size) * 1px)`\n : header.column.getSize(),\n zIndex: isDragging ? 1 : 0,\n }\n\n return (\n \n
\n {canOrder && (\n \n \n \n )}\n
\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n
\n {props.tableLayout?.columnsResizable && column.getCanResize() && (\n \n )}\n
\n \n )\n}\n\nfunction DataGridTableDndCell({ cell }: { cell: Cell }) {\n const { props } = useDataGrid()\n const { isDragging, setNodeRef, transform, transition } = useSortable({\n id: cell.column.id,\n })\n\n const style: CSSProperties = {\n opacity: isDragging ? 0.8 : 1,\n position: \"relative\",\n transform: CSS.Translate.toString(transform),\n transition,\n cursor: isDragging ? \"grabbing\" : undefined,\n width: props.tableLayout?.columnsResizable\n ? `calc(var(--col-${cell.column.id}-size) * 1px)`\n : cell.column.getSize(),\n zIndex: isDragging ? 1 : 0,\n }\n\n return (\n \n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n \n )\n}\n\nfunction DataGridTableDndBodyRows({ table }: { table: Table }) {\n const { isLoading, props } = useDataGrid()\n const pagination = table.getState().pagination\n\n if (props.loadingMode === \"skeleton\" && isLoading && pagination?.pageSize) {\n return (\n <>\n {Array.from({ length: pagination.pageSize }).map((_, rowIndex) => (\n \n {table.getVisibleFlatColumns().map((column, colIndex) => {\n return (\n \n {column.columnDef.meta?.skeleton}\n \n )\n })}\n \n \n ))}\n \n )\n }\n\n if (!table.getRowModel().rows.length) return \n\n return (\n <>\n {table.getRowModel().rows.map((row: Row) => {\n return (\n \n \n \n {row.getVisibleCells().map((cell: Cell) => (\n \n ))}\n \n \n \n {row.getIsExpanded() && }\n \n )\n })}\n \n )\n}\n\n/**\n * Memoized body rows: skip re-renders during active column resize.\n * Column widths update via CSS variables on the element,\n * so the browser handles width changes without React re-renders.\n */\nconst MemoizedDataGridTableDndBodyRows = memo(\n DataGridTableDndBodyRows,\n (_prev, next) => !!next.table.getState().columnSizingInfo.isResizingColumn\n) as typeof DataGridTableDndBodyRows\n\nfunction DataGridTableDnd({\n handleDragEnd,\n footerContent,\n}: {\n handleDragEnd: (event: DragEndEvent) => void\n footerContent?: ReactNode\n}) {\n const { table, props } = useDataGrid()\n const containerRef = useRef(null)\n const [isDraggingColumn, setIsDraggingColumn] = useState(false)\n\n const sensors = useSensors(\n useSensor(MouseSensor, {}),\n useSensor(TouchSensor, {}),\n // Keyboard reordering moves one sortable position per keypress instead\n // of the sensor's raw 25px default.\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n })\n )\n\n useEffect(() => {\n if (!isDraggingColumn) return\n\n const { body, documentElement } = document\n const previousBodyCursor = body.style.cursor\n const previousDocumentCursor = documentElement.style.cursor\n\n body.style.cursor = \"grabbing\"\n documentElement.style.cursor = \"grabbing\"\n\n return () => {\n body.style.cursor = previousBodyCursor\n documentElement.style.cursor = previousDocumentCursor\n }\n }, [isDraggingColumn])\n\n // Custom modifier to restrict dragging within table bounds with edge offset\n const modifiers = useMemo(() => {\n const restrictToTableBounds: Modifier = ({\n draggingNodeRect,\n transform,\n }) => {\n if (!draggingNodeRect || !containerRef.current) {\n return { ...transform, y: 0 }\n }\n\n const containerRect = containerRef.current.getBoundingClientRect()\n const edgeOffset = 0\n\n const minX = containerRect.left - draggingNodeRect.left - edgeOffset\n const maxX =\n containerRect.right -\n draggingNodeRect.left -\n draggingNodeRect.width +\n edgeOffset\n\n return {\n ...transform,\n x: Math.min(Math.max(transform.x, minX), maxX),\n y: 0, // Lock vertical movement\n }\n }\n\n return [restrictToTableBounds]\n }, [])\n\n return (\n setIsDraggingColumn(false)}\n onDragEnd={(event) => {\n setIsDraggingColumn(false)\n handleDragEnd(event)\n }}\n onDragStart={() => setIsDraggingColumn(true)}\n sensors={sensors}\n >\n \n \n \n {table\n .getHeaderGroups()\n .map((headerGroup: HeaderGroup, index) => {\n return (\n \n \n {headerGroup.headers.map((header) => (\n \n ))}\n \n \n \n )\n })}\n \n\n {(props.tableLayout?.stripped || !props.tableLayout?.rowBorder) && (\n \n )}\n\n \n \n \n\n {footerContent && (\n {footerContent}\n )}\n \n \n \n )\n}\n\nexport { DataGridTableDnd }","target":"components/neui/data-grid/data-grid-table-dnd.tsx"}]}