From acb0929abe63597b7b6d82086ae44ca3650aed41 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:33:36 +0200 Subject: [PATCH] chore: migrate row sort in SupabaseGrid to DndKit (#44628) ## Problem We currently have 3 different libraries for drag & drop, two of which are not actively maintained anymore. ## Solution Migrate all usage of the two unmaintained libraries to DndKit. This PR focuses on using DndKit instead of `react-dnd` for the row sorting popover in the table editor ## Screencast https://github.com/user-attachments/assets/9d5cf43c-fbbd-4e5e-8b5a-e9e803b71421 ## Summary by CodeRabbit * **New Features** * Enhanced sorting functionality with drag-and-drop support to reorder sort rules. * **Improvements** * Updated drag handle styling for better visual clarity and usability. * Simplified sorting interface architecture. --- .../header/sort/SortPopoverPrimitive.tsx | 61 ++++++++-- .../grid/components/header/sort/SortRow.tsx | 109 ++++-------------- .../ForeignRowSelector/ForeignRowSelector.tsx | 6 +- 3 files changed, 73 insertions(+), 103 deletions(-) diff --git a/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx b/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx index a929735fe16..8066a2c5a16 100644 --- a/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx +++ b/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx @@ -1,3 +1,17 @@ +import { + closestCenter, + DndContext, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core' +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' import { THRESHOLD_COUNT } from '@supabase/pg-meta' import { keepPreviousData } from '@tanstack/react-query' import { useParams } from 'common' @@ -214,6 +228,13 @@ export const SortPopoverPrimitive = ({ } }, [sorts]) + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ) + return ( <> @@ -224,17 +245,35 @@ export const SortPopoverPrimitive = ({
- {localSorts.map((sort, index) => ( - - ))} + { + if (result.over == null) return + const activeIndex = localSorts.findIndex((sort) => sort.column === result.active.id) + const overIndex = localSorts.findIndex((sort) => sort.column === result.over!.id) + if (activeIndex === -1 || overIndex === -1) return + + setLocalSorts(arrayMove(localSorts, activeIndex, overIndex)) + }} + > + sort.column)} + strategy={verticalListSortingStrategy} + > + {localSorts.map((sort, index) => ( + + ))} + + {localSorts.length === 0 && (
No sorts applied to this view
diff --git a/apps/studio/components/grid/components/header/sort/SortRow.tsx b/apps/studio/components/grid/components/header/sort/SortRow.tsx index b941e847a69..0acf18a4725 100644 --- a/apps/studio/components/grid/components/header/sort/SortRow.tsx +++ b/apps/studio/components/grid/components/header/sort/SortRow.tsx @@ -1,10 +1,10 @@ -import type { XYCoord } from 'dnd-core' -import { Menu, X } from 'lucide-react' -import { memo, useRef } from 'react' -import { useDrag, useDrop } from 'react-dnd' +import { useSortable } from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' +import { GripVertical, X } from 'lucide-react' +import { memo } from 'react' import { Button, Switch } from 'ui' -import type { DragItem, Sort } from '@/components/grid/types' +import type { Sort } from '@/components/grid/types' import { useTableEditorTableStateSnapshot } from '@/state/table-editor-table' export interface SortRowProps { @@ -20,94 +20,29 @@ const SortRow = ({ index, columnName, sort, onDelete, onToggle, onDrag }: SortRo const snap = useTableEditorTableStateSnapshot() const column = snap.table.columns.find((x) => x.name === columnName) - const ref = useRef(null) + const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition } = + useSortable({ + id: columnName, + }) - const [{ isDragging }, drag] = useDrag({ - type: 'sort-row', - item: () => { - return { key: columnName, index } - }, - collect: (monitor: any) => ({ - isDragging: monitor.isDragging(), - }), - }) - - const [{ handlerId }, drop] = useDrop({ - accept: 'sort-row', - collect(monitor) { - return { - handlerId: monitor.getHandlerId(), - } - }, - hover(item, monitor) { - if (!ref.current) { - return - } - const dragIndex = (item as DragItem).index - const hoverIndex = index - - // Don't replace items with themselves - if (dragIndex === hoverIndex) { - return - } - - // Determine rectangle on screen - const hoverBoundingRect = ref.current?.getBoundingClientRect() - - // Get vertical middle - const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 - - // Determine mouse position - const clientOffset = monitor.getClientOffset() - - // Get pixels to the top - const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top - - // Only perform the move when the mouse has crossed half of the items height - // When dragging downwards, only move when the cursor is below 50% - // When dragging upwards, only move when the cursor is above 50% - - // Dragging downwards - if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { - return - } - - // Dragging upwards - if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { - return - } - - // Time to actually perform the action - moveSort(dragIndex, hoverIndex) - - // Note: we're mutating the monitor item here! - // Generally it's better to avoid mutations, - // but it's good here for the sake of performance - // to avoid expensive index searches. - ;(item as DragItem).index = hoverIndex - }, - }) - - const moveSort = (dragIndex: number, hoverIndex: number) => { - if (dragIndex == hoverIndex) return - onDrag(dragIndex, hoverIndex) + const style = { + transform: CSS.Transform.toString(transform), + transition, } - const opacity = isDragging ? 0 : 1 - drag(drop(ref)) - if (!column) return null return ( -
- - - +
+
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx index 12ba21b529e..eb197751169 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx @@ -2,8 +2,6 @@ import { keepPreviousData } from '@tanstack/react-query' import { useParams } from 'common' import { Loader2, X } from 'lucide-react' import { useEffect, useState } from 'react' -import { DndProvider } from 'react-dnd' -import { HTML5Backend } from 'react-dnd-html5-backend' import { Button, SidePanel } from 'ui' import { ForeignKey } from '../../ForeignKeySelector/ForeignKeySelector.types' @@ -216,9 +214,7 @@ export const ForeignRowSelector = ({
- - - +