docs/components/interaction.md

# Interaction

Drag-and-drop primitives for sortable lists, grids, Kanban workflows, transfer lists, and hierarchical tree reordering. All 14 components use vanilla JS hooks with no npm dependencies. Components live in `PhiaUi.Components.Interaction` and related modules.

```elixir
import PhiaUi.Components.Interaction.Sortable
import PhiaUi.Components.Interaction.SortableGrid
import PhiaUi.Components.Interaction.DropZone
import PhiaUi.Components.Interaction.MultiDrag
import PhiaUi.Components.Interaction.DraggableTree
import PhiaUi.Components.Data.KanbanBoard   # for DnD kanban
```

---

## drag_handle/1

**Module**: `PhiaUi.Components.Interaction.Sortable`
**Tier**: primitive

Grip icon handle for initiating drag operations. Used inside `sortable_item/1` or `kanban_card/1` to restrict drag initiation to this handle zone.

### Attributes

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.sortable_item id="item-1" value="item-1">
  <.drag_handle />
  <span>Item content</span>
</.sortable_item>
```

---

## drop_indicator/1

**Module**: `PhiaUi.Components.Interaction.Sortable`
**Tier**: primitive

Visual line or zone indicator for valid drop targets. Appears between items during drag operations.

### Attributes

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `:string` | `"line"` | Visual style: `"line"` or `"zone"` |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.drop_indicator variant="line" />
```

---

## sortable_list/1 and sortable_item/1

**Module**: `PhiaUi.Components.Interaction.Sortable`
**Tier**: interactive
**Hook**: `PhiaSortable`

Vertical drag-to-reorder list. Items emit `phx-click` events with the reordered list on drop.

### Attributes — `sortable_list/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `on_reorder` | `:string` | required | Event fired with `%{"order" => ["id1","id2",...]}` |
| `class` | `:string` | `nil` | Additional CSS classes |

### Attributes — `sortable_item/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Unique item ID (used as drag key) |
| `value` | `:string` | required | Value emitted in reorder event |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.sortable_list id="task-list" on_reorder="reorder_tasks">
  <%= for task <- @tasks do %>
    <.sortable_item id={"task-#{task.id}"} value={to_string(task.id)}>
      <.drag_handle />
      <span>{task.title}</span>
    </.sortable_item>
  <% end %>
</.sortable_list>
```

### LiveView Example

```elixir
def handle_event("reorder_tasks", %{"order" => ids}, socket) do
  tasks = Enum.map(ids, fn id ->
    Enum.find(socket.assigns.tasks, &(to_string(&1.id) == id))
  end)
  {:noreply, assign(socket, tasks: tasks)}
end
```

---

## sortable_grid/1 and sortable_grid_item/1

**Module**: `PhiaUi.Components.Interaction.SortableGrid`
**Tier**: interactive
**Hook**: `PhiaSortableGrid`

Grid drag-to-reorder layout. Items can be dragged to any grid position.

### Attributes — `sortable_grid/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `cols` | `:integer` | `3` | Number of grid columns |
| `on_reorder` | `:string` | required | Event fired on drop |
| `class` | `:string` | `nil` | Additional CSS classes |

### Attributes — `sortable_grid_item/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Unique item ID |
| `value` | `:string` | required | Value emitted in reorder event |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.sortable_grid id="widget-grid" cols={4} on_reorder="reorder_widgets">
  <%= for widget <- @widgets do %>
    <.sortable_grid_item id={"widget-#{widget.id}"} value={to_string(widget.id)}>
      <.card class="p-4">{widget.title}</.card>
    </.sortable_grid_item>
  <% end %>
</.sortable_grid>
```

---

## kanban_board/1, kanban_column/1, kanban_card/1 (DnD)

**Module**: `PhiaUi.Components.Data.KanbanBoard`
**Tier**: widget
**Hook**: `PhiaKanban`

Full drag-and-drop Kanban board with cross-column card dragging. Cards emit server events on drop with the new column and position.

### Attributes — `kanban_board/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `on_move` | `:string` | required | Event fired: `%{"card_id" => id, "column_id" => col, "index" => idx}` |
| `class` | `:string` | `nil` | Additional CSS classes |

### Attributes — `kanban_column/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Column identifier |
| `label` | `:string` | required | Column header label |
| `count` | `:integer` | `nil` | Optional item count badge |
| `class` | `:string` | `nil` | Additional CSS classes |

### Attributes — `kanban_card/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Card identifier |
| `column_id` | `:string` | required | Parent column identifier |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.kanban_board id="project-board" on_move="move_card">
  <%= for column <- @columns do %>
    <.kanban_column id={column.id} label={column.name} count={length(column.cards)}>
      <%= for card <- column.cards do %>
        <.kanban_card id={card.id} column_id={column.id}>
          <.drag_handle />
          <p class="font-medium">{card.title}</p>
          <.badge variant="secondary">{card.priority}</.badge>
        </.kanban_card>
      <% end %>
    </.kanban_column>
  <% end %>
</.kanban_board>
```

---

## drop_zone/1

**Module**: `PhiaUi.Components.Interaction.DropZone`
**Tier**: interactive
**Hook**: `PhiaDropZone`

Generic file or item drop target with visual hover feedback.

### Attributes

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `on_drop` | `:string` | required | Event fired on drop |
| `label` | `:string` | `"Drop here"` | Label text shown in zone |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.drop_zone id="file-drop" on_drop="files_dropped" label="Drop files to upload" />
```

---

## drag_transfer_list/1

**Module**: `PhiaUi.Components.Interaction.DropZone`
**Tier**: interactive
**Hook**: `PhiaDragTransferList`

Two-column drag-between-lists transfer. Items can be dragged from "available" to "selected" and back.

### Attributes

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `available` | `:list` | required | List of available item maps `%{id, label}` |
| `selected` | `:list` | required | List of selected item maps `%{id, label}` |
| `on_change` | `:string` | required | Event fired with updated selection |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.drag_transfer_list
  id="permission-transfer"
  available={@available_roles}
  selected={@selected_roles}
  on_change="update_roles"
/>
```

---

## multi_drag_list/1

**Module**: `PhiaUi.Components.Interaction.MultiDrag`
**Tier**: interactive
**Hook**: `PhiaMultiDrag`

Multi-select drag list. Shift-click or Ctrl-click selects multiple items, then the group can be dragged together.

### Attributes

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `items` | `:list` | required | List of item maps `%{id, label}` |
| `on_reorder` | `:string` | required | Event fired on drop |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.multi_drag_list
  id="song-queue"
  items={Enum.map(@songs, &%{id: to_string(&1.id), label: &1.title})}
  on_reorder="reorder_songs"
/>
```

---

## draggable_tree/1 and draggable_tree_node/1

**Module**: `PhiaUi.Components.Interaction.DraggableTree`
**Tier**: interactive
**Hook**: `PhiaDraggableTree`

Hierarchical tree with drag-to-reorder nodes. Supports nesting, expanding/collapsing, and drag-into-folder behavior.

### Attributes — `draggable_tree/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Required for hook |
| `on_reorder` | `:string` | required | Event fired with new tree structure |
| `class` | `:string` | `nil` | Additional CSS classes |

### Attributes — `draggable_tree_node/1`

| Attr | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `:string` | required | Node identifier |
| `label` | `:string` | required | Node display label |
| `parent_id` | `:string` | `nil` | Parent node ID (nil = root) |
| `expanded` | `:boolean` | `true` | Whether children are visible |
| `class` | `:string` | `nil` | Additional CSS classes |

### Usage

```heex
<.draggable_tree id="file-tree" on_reorder="update_tree">
  <.draggable_tree_node id="root" label="Project">
    <.draggable_tree_node id="src" label="lib" parent_id="root">
      <.draggable_tree_node id="comp" label="components" parent_id="src" />
    </.draggable_tree_node>
    <.draggable_tree_node id="docs" label="docs" parent_id="root" />
  </.draggable_tree_node>
</.draggable_tree>
```

### Use Cases

- File/folder tree reordering
- Navigation item management
- Category hierarchy editing