{
  "name": "SortableHandle",
  "package": "@magicblocksai/ui",
  "file": "packages/ui/src/components/SortableHandle.tsx",
  "chapterTag": "04 Forms & Inputs",
  "chapter": "05-forms-and-inputs.html",
  "sectionId": "sortable-list",
  "elName": "SortableList composed with SortableHandle",
  "demoUrl": "https://brand.magicblocks.ai/components/05-forms-and-inputs#sortable-list",
  "hasLiveDemo": false,
  "description": "Drag-grip + secondary-action handle for sortable cards / rows.",
  "useClient": true,
  "interactivity": "interactive",
  "namedExports": [
    {
      "name": "SortableHandle",
      "isPrincipal": true,
      "isType": false
    },
    {
      "name": "SortableHandleProps",
      "isPrincipal": false,
      "isType": true
    },
    {
      "name": "SortableHandleAction",
      "isPrincipal": false,
      "isType": true
    },
    {
      "name": "SortableHandleActionType",
      "isPrincipal": false,
      "isType": false
    }
  ],
  "importStatement": "import { SortableHandle } from \"@magicblocksai/ui\";",
  "props": [
    {
      "name": "id",
      "optional": false,
      "type": "string",
      "doc": "Stable identifier for the sortable item — passed to `useSortable`'s `getId` callback if you wire the drag interaction to the kit's hook."
    },
    {
      "name": "actions",
      "optional": true,
      "type": "SortableHandleAction[]",
      "doc": "Optional secondary actions rendered as icon buttons next to the drag grip. On touch the buttons are always visible; on mouse they fade in on hover. For more than 2 actions, condense into one `<DropdownMenu>` trigger and pass it as the only action."
    },
    {
      "name": "visibility",
      "optional": true,
      "type": "\"auto\" | \"always\"",
      "doc": "Visibility strategy. Default `\"auto\"` — visible at rest on touch (`@media (pointer: coarse)`) and on hover on mouse (`@media (pointer: fine)`). The kit ships this default because every consumer who builds a hover-reveal handle and forgets the touch case ends up with an invisible drag affordance on mobile. Pass `\"always\"` to keep the handle visible regardless of pointer type (Linear-style — a permanent grip on every row)."
    },
    {
      "name": "dragIcon",
      "optional": true,
      "type": "ReactNode",
      "doc": "Optional drag-glyph override. The kit's default glyph is the standard 6-dot vertical grip. Pass any 12×12 SVG to override."
    },
    {
      "name": "dragLabel",
      "optional": true,
      "type": "string",
      "doc": "Override the accessible label on the drag grip. Default `\"Drag to reorder\"`."
    },
    {
      "name": "className",
      "optional": true,
      "type": "string",
      "doc": "Class on the wrapper."
    },
    {
      "name": "style",
      "optional": true,
      "type": "CSSProperties",
      "doc": "Inline style on the wrapper."
    }
  ],
  "classesUsed": [
    "menu",
    "sortable-handle",
    "sortable-handle-action",
    "sortable-handle-actions",
    "sortable-handle-grip",
    "sortable-handle-menu",
    "sortable-handle-menu-icon",
    "sortable-handle-menu-item"
  ],
  "examples": {
    "react": "const { props } = useSortable({ items: cards, getId: (c) => c.id, onReorder: setCards });",
    "html": "<ul class=\"sortable-list\">\n  <li><div class=\"step-row\"><span class=\"step-row-title\">Alpha</span><span class=\"sortable-handle\"><span class=\"sortable-handle-grip\" aria-label=\"Drag to reorder\">⋮⋮</span></span></div></li>\n  <li><div class=\"step-row\"><span class=\"step-row-title\">Beta</span><span class=\"sortable-handle\"><span class=\"sortable-handle-grip\" aria-label=\"Drag to reorder\">⋮⋮</span></span></div></li>\n  <li><div class=\"step-row\"><span class=\"step-row-title\">Gamma</span><span class=\"sortable-handle\"><span class=\"sortable-handle-grip\" aria-label=\"Drag to reorder\">⋮⋮</span></span></div></li>\n</ul>",
    "css": ".menu {\n  background: var(--bg-paper);\n  border: 1px solid var(--hair);\n  border-radius: var(--r-lg);\n  padding: var(--s-2);\n  display: flex; flex-direction: column; gap: 2px;\n  box-shadow: var(--sh-3);\n  min-width: 260px;\n}\n\n.sortable-handle {\n  display: inline-flex;\n  align-items: center;\n  gap: 2px;\n  position: relative;\n  /* Default: hover-reveal on mouse; visible at rest on touch (rule below). */\n  opacity: 0;\n  transition: opacity var(--dur-2, 160ms) var(--ease, cubic-bezier(0.2, 0.8, 0.2, 1));\n}\n\n.sortable-handle-action {\n  appearance: none;\n  background: transparent;\n  border: 0;\n  padding: 4px;\n  border-radius: var(--r-xs);\n  color: var(--fg-dim);\n  cursor: pointer;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);\n}\n\n.sortable-handle-actions {\n  display: inline-flex;\n  align-items: center;\n  gap: 2px;\n  position: relative;\n}\n\n.sortable-handle-grip {\n  appearance: none;\n  background: transparent;\n  border: 0;\n  padding: 4px;\n  border-radius: var(--r-xs);\n  color: var(--fg-faint);\n  cursor: grab;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);\n}\n\n.sortable-handle-menu {\n  position: absolute;\n  top: calc(100% + 4px);\n  right: 0;\n  z-index: var(--z-overlay, 40);\n  list-style: none;\n  margin: 0;\n  padding: 4px;\n  min-width: 160px;\n  background: var(--bg-paper);\n  border: 1px solid var(--hair);\n  border-radius: var(--r-sm);\n  box-shadow: var(--sh-2);\n  display: flex;\n  flex-direction: column;\n  gap: 1px;\n}\n\n.sortable-handle-menu-icon {\n  display: inline-flex;\n  align-items: center;\n  color: var(--fg-dim);\n}\n\n.sortable-handle-menu-item {\n  display: flex;\n  align-items: center;\n  gap: var(--s-2);\n  width: 100%;\n  padding: 6px 10px;\n  background: transparent;\n  border: 0;\n  border-radius: var(--r-xs);\n  cursor: pointer;\n  text-align: left;\n  font: 500 13px/1.3 var(--f-body);\n  color: var(--fg);\n  transition: background var(--dur-1) var(--ease);\n}"
  }
}
