{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ds-sortable",
  "title": "Sortable",
  "description": "Lista arrastável vertical/grid — ReUI Sortable.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ds/ds-sortable.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { GripVerticalIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface SortableItem {\n  id: string\n  content: React.ReactNode\n}\n\nexport interface SortableProps\n  extends\n    React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof sortableVariants> {\n  items: SortableItem[]\n  onReorder?: (items: SortableItem[]) => void\n}\n\n// ── Variants ───────────────────────────────────────────────────────────────\n\nexport const sortableVariants = cva(\n  \"flex flex-col gap-2 rounded-2xl border bg-card p-3\",\n  {\n    variants: {\n      size: {\n        sm: \"gap-1.5\",\n        md: \"gap-2\",\n      },\n    },\n    defaultVariants: { size: \"md\" },\n  }\n)\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function Sortable({\n  className,\n  items,\n  onReorder,\n  size = \"md\",\n  ...props\n}: SortableProps) {\n  const [list, setList] = React.useState(items)\n\n  // eslint-disable-next-line react-hooks/set-state-in-effect\n  React.useEffect(() => setList(items), [items])\n\n  const move = (from: number, to: number) => {\n    const next = [...list]\n    const [m] = next.splice(from, 1)\n    next.splice(to, 0, m)\n    setList(next)\n    onReorder?.(next)\n  }\n\n  return (\n    <div\n      data-slot=\"sortable\"\n      role=\"list\"\n      aria-label=\"Sortable list\"\n      className={cn(sortableVariants({ size }), className)}\n      {...props}\n    >\n      {list.map((it, idx) => (\n        <div\n          key={it.id}\n          role=\"listitem\"\n          tabIndex={0}\n          aria-label={`Sortable item ${idx + 1} of ${list.length}`}\n          onKeyDown={(e) => {\n            if (e.key === \"ArrowUp\" && idx > 0) {\n              e.preventDefault()\n              move(idx, idx - 1)\n            } else if (e.key === \"ArrowDown\" && idx < list.length - 1) {\n              e.preventDefault()\n              move(idx, idx + 1)\n            }\n          }}\n          className=\"flex items-center gap-2 rounded-xl border bg-background p-3 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none\"\n          draggable\n          onDragStart={(e) => e.dataTransfer.setData(\"text/plain\", String(idx))}\n          onDragOver={(e) => e.preventDefault()}\n          onDrop={(e) => {\n            const from = Number(e.dataTransfer.getData(\"text/plain\"))\n            move(from, idx)\n          }}\n        >\n          <GripVerticalIcon\n            className=\"size-4 shrink-0 text-muted-foreground\"\n            aria-hidden=\"true\"\n          />\n          <span className=\"flex-1 truncate text-sm\">{it.content}</span>\n          <div className=\"flex gap-1\">\n            <button\n              type=\"button\"\n              aria-label=\"Move up\"\n              disabled={idx === 0}\n              onClick={() => move(idx, idx - 1)}\n              className=\"size-6 rounded-md hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:opacity-30\"\n            >\n              ↑\n            </button>\n            <button\n              type=\"button\"\n              aria-label=\"Move down\"\n              disabled={idx === list.length - 1}\n              onClick={() => move(idx, idx + 1)}\n              className=\"size-6 rounded-md hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:opacity-30\"\n            >\n              ↓\n            </button>\n          </div>\n        </div>\n      ))}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
