{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dnd",
  "title": "Dnd",
  "description": "A generic drag source: makes its `children` draggable and exposes an opaque `data` payload to drop targets.",
  "registryDependencies": [
    "@engram/cn",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/dnd.tsx",
      "content": "\"use client\";\n\nimport { type HTMLAttributes, useState } from \"react\";\nimport { cn } from \"../lib/cn.js\";\n\n// HTML5 dataTransfer only carries strings, and the payload isn't readable during\n// dragover (only on drop). We keep the live payload here so a DropZone can run\n// its `accept` predicate against the real object while hovering.\nlet activePayload: unknown;\n\nexport interface DraggableProps\n  extends Omit<HTMLAttributes<HTMLDivElement>, \"onDragStart\" | \"onDragEnd\"> {\n  /** Opaque payload handed to {@link DropZone} `accept`/`onDrop`. */\n  data: unknown;\n  /** Disable dragging (e.g. locked items). */\n  disabled?: boolean;\n  /** Class applied while this element is being dragged. */\n  draggingClassName?: string;\n  onDragStart?: (data: unknown) => void;\n  onDragEnd?: (data: unknown) => void;\n}\n\n/**\n * A generic drag source: makes its `children` draggable and exposes an opaque\n * `data` payload to drop targets. Behavior only — engram carries the payload and\n * fires the callbacks; the consumer owns move rules and validation.\n */\nexport function Draggable({\n  data,\n  disabled = false,\n  draggingClassName = \"opacity-50\",\n  onDragStart,\n  onDragEnd,\n  className,\n  children,\n  ...props\n}: DraggableProps) {\n  const [dragging, setDragging] = useState(false);\n  return (\n    // biome-ignore lint/a11y/noStaticElementInteractions: a generic drag-source wrapper; the interactive item is the consumer's children, this layer only carries the payload\n    <div\n      draggable={!disabled}\n      onDragStart={(e) => {\n        activePayload = data;\n        // Some browsers require data to be set for the drag to begin.\n        e.dataTransfer.setData(\"text/plain\", \"\");\n        e.dataTransfer.effectAllowed = \"move\";\n        setDragging(true);\n        onDragStart?.(data);\n      }}\n      onDragEnd={() => {\n        activePayload = undefined;\n        setDragging(false);\n        onDragEnd?.(data);\n      }}\n      className={cn(\n        !disabled && \"cursor-grab active:cursor-grabbing\",\n        dragging && draggingClassName,\n        className,\n      )}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport interface DropZoneProps\n  extends Omit<HTMLAttributes<HTMLDivElement>, \"onDrop\"> {\n  /** Return false to reject the hovering payload (no drop, no active style). */\n  accept?: (data: unknown) => boolean;\n  /** Fired with the dropped payload when a valid item is released here. */\n  onDrop?: (data: unknown) => void;\n  /** Class applied while a valid payload is hovering. */\n  activeClassName?: string;\n}\n\n/**\n * A generic drop target: validates the hovering {@link Draggable} payload with\n * `accept` and fires `onDrop` on release. Behavior only — it highlights and\n * reports; the consumer performs the actual move.\n */\nexport function DropZone({\n  accept,\n  onDrop,\n  activeClassName = \"outline-2 outline-engram-accent\",\n  className,\n  children,\n  ...props\n}: DropZoneProps) {\n  const [over, setOver] = useState(false);\n  const allows = () => (accept ? accept(activePayload) : true);\n  return (\n    // biome-ignore lint/a11y/noStaticElementInteractions: a generic drop target; it only validates + reports the drop, the interactive content is the consumer's children\n    <div\n      onDragOver={(e) => {\n        if (!allows()) return;\n        e.preventDefault(); // signals this is a valid drop target\n        e.dataTransfer.dropEffect = \"move\";\n        if (!over) setOver(true);\n      }}\n      onDragLeave={() => setOver(false)}\n      onDrop={(e) => {\n        e.preventDefault();\n        setOver(false);\n        if (allows()) onDrop?.(activePayload);\n      }}\n      className={cn(over && activeClassName, className)}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/engram/dnd.tsx"
    }
  ],
  "meta": {
    "level": "component"
  },
  "docs": "Extend without forking: edit the copied source, use `asChild` (Radix Slot) to change the rendered element, pass the typed `annotations` prop for curated data (verdict/tags/per-plug), or use slot / render-prop props for arbitrary content. Requires the @engram/tokens theme (--engram-* CSS variables).",
  "categories": [
    "interaction"
  ],
  "type": "registry:component"
}