{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reorderable-list",
  "title": "Reorderable List",
  "description": "A drag-and-drop sortable list with smooth animations.",
  "dependencies": [
    "@dnd-kit/core",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/wise-ui/components/reorderable-list.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  DndContext,\n  closestCenter,\n  KeyboardSensor,\n  PointerSensor,\n  useSensor,\n  useSensors,\n  DragOverlay,\n  type DragStartEvent,\n  type DragEndEvent,\n} from \"@dnd-kit/core\"\nimport {\n  arrayMove,\n  SortableContext,\n  sortableKeyboardCoordinates,\n  useSortable,\n  verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\"\nimport { CSS } from \"@dnd-kit/utilities\"\nimport { motion } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\nconst EASE_OUT_QUART: [number, number, number, number] = [0.25, 1, 0.5, 1]\n\ninterface ReorderableListProps<T extends { id: string | number }> {\n  items: T[]\n  onReorder: (items: T[]) => void\n  renderItem: (item: T, index: number) => React.ReactNode\n  className?: string\n  itemClassName?: string\n  gap?: number\n  dragTarget?: \"card\" | \"handle\"\n}\n\ninterface SortableItemProps {\n  id: string | number\n  children: React.ReactNode\n  className?: string\n  isDragOverlay?: boolean\n  activeId: string | number | null\n  dragTarget: \"card\" | \"handle\"\n}\n\nfunction SortableItem({\n  id,\n  children,\n  className,\n  isDragOverlay,\n  activeId,\n  dragTarget,\n}: SortableItemProps) {\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    transform,\n    transition,\n    isDragging,\n  } = useSortable({ id })\n\n  const style: React.CSSProperties = {\n    transform: CSS.Transform.toString(transform),\n    transition,\n  }\n\n  const overlayStyle: React.CSSProperties = isDragOverlay\n    ? {\n        boxShadow: \"0 8px 32px rgba(0,0,0,0.18)\",\n        transform: \"scale(1.02)\",\n      }\n    : {}\n\n  const dragProps = dragTarget === \"card\" ? { ...attributes, ...listeners } : {}\n\n  return (\n    <motion.div\n      ref={isDragOverlay ? undefined : setNodeRef}\n      style={{ ...style, ...overlayStyle }}\n      className={cn(\n        \"touch-none\",\n        dragTarget === \"card\" && \"cursor-grab active:cursor-grabbing\",\n        isDragOverlay && \"cursor-grabbing\",\n        isDragging && \"opacity-40\",\n        className\n      )}\n      layout={!isDragOverlay}\n      transition={{ duration: 0.3, ease: EASE_OUT_QUART }}\n      {...dragProps}\n    >\n      {dragTarget === \"handle\" ? (\n        <ReorderableListHandleContext.Provider\n          value={{ attributes, listeners }}\n        >\n          {children}\n        </ReorderableListHandleContext.Provider>\n      ) : (\n        children\n      )}\n    </motion.div>\n  )\n}\n\n// Context for drag handle\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst ReorderableListHandleContext = React.createContext<{\n  attributes: any\n  listeners: any\n}>({ attributes: {}, listeners: undefined })\n\nfunction ReorderableListDragHandle({\n  children,\n  className,\n}: {\n  children?: React.ReactNode\n  className?: string\n}) {\n  const { attributes, listeners } = React.useContext(\n    ReorderableListHandleContext\n  )\n  return (\n    <button\n      className={cn(\"touch-none cursor-grab active:cursor-grabbing\", className)}\n      {...attributes}\n      {...(listeners as React.HTMLAttributes<HTMLButtonElement>)}\n    >\n      {children ?? (\n        <svg\n          width=\"16\"\n          height=\"16\"\n          viewBox=\"0 0 16 16\"\n          fill=\"currentColor\"\n          className=\"text-muted-foreground\"\n        >\n          <circle cx=\"5\" cy=\"3\" r=\"1.5\" />\n          <circle cx=\"11\" cy=\"3\" r=\"1.5\" />\n          <circle cx=\"5\" cy=\"8\" r=\"1.5\" />\n          <circle cx=\"11\" cy=\"8\" r=\"1.5\" />\n          <circle cx=\"5\" cy=\"13\" r=\"1.5\" />\n          <circle cx=\"11\" cy=\"13\" r=\"1.5\" />\n        </svg>\n      )}\n    </button>\n  )\n}\n\nfunction ReorderableList<T extends { id: string | number }>({\n  items,\n  onReorder,\n  renderItem,\n  className,\n  itemClassName,\n  gap = 8,\n  dragTarget = \"card\",\n}: ReorderableListProps<T>) {\n  const [activeId, setActiveId] = React.useState<string | number | null>(null)\n\n  const sensors = useSensors(\n    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n    useSensor(KeyboardSensor, {\n      coordinateGetter: sortableKeyboardCoordinates,\n    })\n  )\n\n  const activeItem = React.useMemo(\n    () => items.find((i) => i.id === activeId),\n    [items, activeId]\n  )\n\n  function handleDragStart(event: DragStartEvent) {\n    setActiveId(event.active.id as string | number)\n  }\n\n  function handleDragEnd(event: DragEndEvent) {\n    setActiveId(null)\n    const { active, over } = event\n    if (over && active.id !== over.id) {\n      const oldIndex = items.findIndex((i) => i.id === active.id)\n      const newIndex = items.findIndex((i) => i.id === over.id)\n      onReorder(arrayMove(items, oldIndex, newIndex))\n    }\n  }\n\n  return (\n    <DndContext\n      sensors={sensors}\n      collisionDetection={closestCenter}\n      onDragStart={handleDragStart}\n      onDragEnd={handleDragEnd}\n    >\n      <SortableContext\n        items={items.map((i) => i.id)}\n        strategy={verticalListSortingStrategy}\n      >\n        <div className={cn(\"flex flex-col\", className)} style={{ gap }}>\n          {items.map((item, index) => (\n            <SortableItem\n              key={item.id}\n              id={item.id}\n              className={itemClassName}\n              activeId={activeId}\n              dragTarget={dragTarget}\n            >\n              {renderItem(item, index)}\n            </SortableItem>\n          ))}\n        </div>\n      </SortableContext>\n\n      <DragOverlay>\n        {activeItem ? (\n          <SortableItem\n            id={activeItem.id}\n            className={itemClassName}\n            isDragOverlay\n            activeId={activeId}\n            dragTarget={dragTarget}\n          >\n            {renderItem(\n              activeItem,\n              items.findIndex((i) => i.id === activeId)\n            )}\n          </SortableItem>\n        ) : null}\n      </DragOverlay>\n    </DndContext>\n  )\n}\n\nexport { ReorderableList, ReorderableListDragHandle }\nexport type { ReorderableListProps }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}