{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reorderable-table",
  "title": "Reorderable Table",
  "description": "Drag rows to reorder them, built on @dnd-kit/sortable.",
  "dependencies": [
    "@tanstack/react-table",
    "@dnd-kit/core",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities",
    "@dnd-kit/modifiers"
  ],
  "registryDependencies": [
    "table"
  ],
  "files": [
    {
      "path": "src/components/reorder/columns.tsx",
      "content": "'use client'\n\nimport type { ColumnDef } from '@tanstack/react-table'\n\nexport type Task = {\n  id: string\n  title: string\n  priority: string\n  status: string\n}\n\nexport const columns: ColumnDef<Task>[] = [\n  {\n    id: 'title',\n    accessorKey: 'title',\n    header: 'Title',\n  },\n  {\n    id: 'priority',\n    accessorKey: 'priority',\n    header: 'Priority',\n    sortingFn: 'basic',\n  },\n  {\n    id: 'status',\n    accessorKey: 'status',\n    header: 'Status',\n    sortingFn: 'basic',\n  },\n]\n",
      "type": "registry:component",
      "target": "@components/tables/reorderable-table/columns.tsx"
    },
    {
      "path": "src/components/reorder/data-table.tsx",
      "content": "'use client'\n\nimport { useMemo, useState } from 'react'\nimport {\n  DndContext,\n  KeyboardSensor,\n  PointerSensor,\n  closestCenter,\n  useSensor,\n  useSensors,\n} from '@dnd-kit/core'\nimport type { DragEndEvent } from '@dnd-kit/core'\nimport { restrictToVerticalAxis } from '@dnd-kit/modifiers'\nimport {\n  SortableContext,\n  arrayMove,\n  sortableKeyboardCoordinates,\n  useSortable,\n  verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport {\n  flexRender,\n  getCoreRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from '@tanstack/react-table'\nimport type { Cell, ColumnDef, Row, SortingState } from '@tanstack/react-table'\nimport { ArrowDown, ArrowUp, ArrowUpDown, GripVertical } from 'lucide-react'\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '#/components/ui/table'\n\ninterface ReorderableTableProps<TData extends { id: string }> {\n  columns: ColumnDef<TData, any>[]\n  data: TData[]\n  onDataChange: (data: TData[]) => void\n}\n\nfunction DraggableRow<TData extends { id: string }>({\n  row,\n}: {\n  row: Row<TData>\n}) {\n  const { transform, transition, setNodeRef, isDragging, attributes, listeners } =\n    useSortable({ id: row.original.id })\n\n  return (\n    <TableRow\n      ref={setNodeRef}\n      style={{\n        transform: CSS.Translate.toString(transform),\n        transition,\n        opacity: isDragging ? 0.6 : 1,\n        position: 'relative',\n        zIndex: isDragging ? 1 : 0,\n      }}\n    >\n      <TableCell className=\"w-8\">\n        <button\n          type=\"button\"\n          {...attributes}\n          {...listeners}\n          className=\"cursor-grab text-muted-foreground active:cursor-grabbing\"\n          aria-label=\"Reorder row\"\n        >\n          <GripVertical className=\"h-3.5 w-3.5\" />\n        </button>\n      </TableCell>\n      {row.getVisibleCells().map((cell: Cell<TData, unknown>) => (\n        <TableCell key={cell.id}>\n          {flexRender(cell.column.columnDef.cell, cell.getContext())}\n        </TableCell>\n      ))}\n    </TableRow>\n  )\n}\n\nexport function ReorderableTable<TData extends { id: string }>({\n  columns,\n  data,\n  onDataChange,\n}: ReorderableTableProps<TData>) {\n  const [sorting, setSorting] = useState<SortingState>([])\n\n  const dataIds = useMemo(() => data.map((row) => row.id), [data])\n\n  const table = useReactTable({\n    data,\n    columns,\n    getRowId: (row) => row.id,\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    onSortingChange: setSorting,\n    state: { sorting },\n  })\n\n  const sensors = useSensors(\n    useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n    useSensor(KeyboardSensor, {\n      coordinateGetter: sortableKeyboardCoordinates,\n    }),\n  )\n\n  function handleRowDragEnd(event: DragEndEvent) {\n    const { active, over } = event\n    if (!over || active.id === over.id) return\n    const oldIndex = dataIds.indexOf(active.id as string)\n    const newIndex = dataIds.indexOf(over.id as string)\n    onDataChange(arrayMove(data, oldIndex, newIndex))\n  }\n\n  return (\n    <div className=\"overflow-hidden rounded-md border\">\n      <DndContext\n        sensors={sensors}\n        collisionDetection={closestCenter}\n        modifiers={[restrictToVerticalAxis]}\n        onDragEnd={handleRowDragEnd}\n      >\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                <TableHead className=\"w-8\" />\n                {headerGroup.headers.map((header) => {\n                  const canSort = header.column.getCanSort()\n                  return (\n                    <TableHead key={header.id}>\n                      <span\n                        onClick={header.column.getToggleSortingHandler()}\n                        className={\n                          canSort\n                            ? 'flex cursor-pointer select-none items-center gap-2'\n                            : 'flex items-center gap-2'\n                        }\n                      >\n                        {header.isPlaceholder\n                          ? null\n                          : flexRender(\n                              header.column.columnDef.header,\n                              header.getContext(),\n                            )}\n                        {canSort ? (\n                          header.column.getIsSorted() === 'asc' ? (\n                            <ArrowUp className=\"h-3 w-3\" />\n                          ) : header.column.getIsSorted() === 'desc' ? (\n                            <ArrowDown className=\"h-3 w-3\" />\n                          ) : (\n                            <ArrowUpDown className=\"h-3 w-3 opacity-50\" />\n                          )\n                        ) : null}\n                      </span>\n                    </TableHead>\n                  )\n                })}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              <SortableContext\n                items={dataIds}\n                strategy={verticalListSortingStrategy}\n              >\n                {table.getRowModel().rows.map((row) => (\n                  <DraggableRow key={row.id} row={row} />\n                ))}\n              </SortableContext>\n            ) : (\n              <TableRow>\n                <TableCell\n                  colSpan={columns.length + 1}\n                  className=\"h-24 text-center\"\n                >\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </DndContext>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/reorderable-table/data-table.tsx"
    },
    {
      "path": "src/components/reorder/index.tsx",
      "content": "import { useState } from 'react'\n\nimport { columns } from './columns'\nimport type { Task } from './columns'\nimport { ReorderableTable } from './data-table'\n\nconst initialData: Task[] = [\n  { id: 'task-1', title: 'Design landing page', priority: 'High', status: 'In progress' },\n  { id: 'task-2', title: 'Set up CI pipeline', priority: 'Medium', status: 'Todo' },\n  { id: 'task-3', title: 'Write onboarding docs', priority: 'Low', status: 'Todo' },\n  { id: 'task-4', title: 'Fix pagination bug', priority: 'High', status: 'In progress' },\n  { id: 'task-5', title: 'Add dark mode toggle', priority: 'Medium', status: 'Done' },\n  { id: 'task-6', title: 'Audit accessibility', priority: 'Low', status: 'Todo' },\n]\n\nexport function ReorderableTableDemo() {\n  const [data, setData] = useState(initialData)\n\n  return <ReorderableTable columns={columns} data={data} onDataChange={setData} />\n}\n",
      "type": "registry:component",
      "target": "@components/tables/reorderable-table/index.tsx"
    }
  ],
  "type": "registry:block"
}