{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table-draggable-group-header",
  "title": "Data Table Draggable Group Header",
  "description": "Drag-to-reorder column group header that moves an entire group of columns at once.",
  "dependencies": [
    "@virtuoso.dev/data-table"
  ],
  "registryDependencies": [
    "petyosi/react-virtuoso/data-table",
    "petyosi/react-virtuoso/data-table-reorder-grip",
    "petyosi/react-virtuoso/data-table-reorder-drop-zone"
  ],
  "files": [
    {
      "path": "registry/new-york/data-table/column-reorder/draggable-group-header.tsx",
      "content": "'use client'\n\nimport { useCellValue, usePublisher } from '@virtuoso.dev/data-table'\nimport {\n  beginColumnDrag$,\n  columnDragState$,\n  endColumnDrag$,\n  reorderColumnGroup$,\n  setColumnDropTarget$,\n} from '@virtuoso.dev/data-table/column-reorder'\n\nimport { cn } from '@/lib/utils'\n\ninterface DraggableGroupHeaderProps {\n  label: string\n  columnKeys: string[]\n  stickyGroup?: 'left' | 'right'\n}\n\nfunction DraggableGroupHeader({ label, columnKeys, stickyGroup }: DraggableGroupHeaderProps) {\n  const dragState = useCellValue(columnDragState$)\n  const beginColumnDrag = usePublisher(beginColumnDrag$)\n  const endColumnDrag = usePublisher(endColumnDrag$)\n  const setColumnDropTarget = usePublisher(setColumnDropTarget$)\n  const reorderColumnGroup = usePublisher(reorderColumnGroup$)\n\n  const isGroupDrag = (dragState.sourceKeys?.length ?? 0) > 1\n  const isSelfDrag = isGroupDrag && dragState.sourceKeys?.[0] === columnKeys[0]\n  const groupAnchorKey = dragState.dropTarget?.key\n  const dropPosition =\n    groupAnchorKey && (groupAnchorKey === columnKeys[0] || groupAnchorKey === columnKeys.at(-1)) ? dragState.dropTarget!.position : null\n\n  return (\n    <div\n      className={cn(\n        'relative flex items-center border-b border-border bg-muted px-2 py-1 text-xs font-semibold whitespace-nowrap',\n        stickyGroup && 'bg-muted/80'\n      )}\n      onDragOver={(ev) => {\n        if (!isGroupDrag) {\n          return\n        }\n        if (isSelfDrag) {\n          return\n        }\n        if (dragState.sourceSticky !== stickyGroup) {\n          return\n        }\n        ev.preventDefault()\n        ev.dataTransfer.dropEffect = 'move'\n        const rect = ev.currentTarget.getBoundingClientRect()\n        const position = ev.clientX < rect.left + rect.width / 2 ? 'before' : 'after'\n        const anchorKey = position === 'before' ? columnKeys[0]! : columnKeys.at(-1)!\n        if (dragState.dropTarget?.key === anchorKey && dragState.dropTarget.position === position) {\n          return\n        }\n        setColumnDropTarget({ key: anchorKey, position })\n      }}\n      onDragLeave={(ev) => {\n        const next = ev.relatedTarget as Node | null\n        if (next && ev.currentTarget.contains(next)) {\n          return\n        }\n        setColumnDropTarget(null)\n      }}\n      onDrop={(ev) => {\n        ev.preventDefault()\n        if (!isGroupDrag || isSelfDrag || !dragState.sourceKeys) {\n          setColumnDropTarget(null)\n          return\n        }\n        const rect = ev.currentTarget.getBoundingClientRect()\n        const position = ev.clientX < rect.left + rect.width / 2 ? 'before' : 'after'\n        const anchorKey = position === 'before' ? columnKeys[0]! : columnKeys.at(-1)!\n        setColumnDropTarget(null)\n        reorderColumnGroup({ sourceKeys: dragState.sourceKeys, targetKey: anchorKey, position })\n        endColumnDrag()\n      }}\n    >\n      {dropPosition === 'before' && (\n        <div className=\"pointer-events-none absolute top-1/2 left-0 z-[1] h-4 w-[2px] -translate-x-1/2 -translate-y-1/2 bg-primary\" />\n      )}\n      {dropPosition === 'after' && (\n        <div className=\"pointer-events-none absolute top-1/2 right-0 z-[1] h-4 w-[2px] translate-x-1/2 -translate-y-1/2 bg-primary\" />\n      )}\n      <span\n        draggable\n        onDragStart={(ev) => {\n          beginColumnDrag({ sourceKeys: columnKeys, sourceSticky: stickyGroup })\n          ev.dataTransfer.effectAllowed = 'move'\n          ev.dataTransfer.setData('text/plain', 'group')\n          const header = ev.currentTarget.parentElement!\n          const rect = header.getBoundingClientRect()\n          ev.dataTransfer.setDragImage(header, ev.clientX - rect.left, ev.clientY - rect.top)\n        }}\n        onDragEnd={() => {\n          endColumnDrag()\n        }}\n        className=\"mr-2 cursor-grab text-sm opacity-50 select-none\"\n      >\n        ⠿\n      </span>\n      {label}\n    </div>\n  )\n}\n\nexport { DraggableGroupHeader }\nexport type { DraggableGroupHeaderProps }\n",
      "type": "registry:component",
      "target": "@ui/data-table/column-reorder/draggable-group-header.tsx"
    }
  ],
  "type": "registry:component"
}