Data Table - Drag and Drop

With the draggableRows property, the user can pick up a table row and drop it on another element of your page, for example to move an item into a folder.

The table only starts the drag. It emits (rowDragStart) with the row's item and (rowDragEnd) when the drag ends, whether the row was dropped or not. What happens on drop is up to your drop target.

Example

Drag a hero row onto the Team box.

Team
Drag a hero here.
IdName
1 Superman
2 Batman
3 Wonder Woman
4 Aquaman
5 The Flash

HTML:

<div>
    <div class="border rounded p-3 mb-3" class.border-primary="{isDragging()}" (dragover)="onDragOver($event)" (drop)="onDrop($event)">
        <div class="fw-semibold">Team</div>
        <div if="{count($team) === 0}" class="small text-muted">Drag a hero here.</div>
        <div else class="small">{teamNames()}</div>
    </div>
    <DataTable columns="$columns" items="$heroes" draggableRows (rowDragStart)="onRowDragStart" (rowDragEnd)="onRowDragEnd" />
</div>

PHP:

<?php

namespace ExamplesUi\Tables;

use ExamplesUi\HeroModel;
use Viewi\Components\BaseComponent;
use Viewi\Components\DOM\DomEvent;
use Viewi\UI\Components\Tables\TableColumn;

class TableDragExample extends BaseComponent
{
    public array $columns = [];
    public array $heroes = [];
    // heroes dropped onto the team box
    public array $team = [];
    // the row being dragged
    public ?HeroModel $dragged = null;

    // ...

    public function onRowDragStart(HeroModel $hero)
    {
        $this->dragged = $hero;
    }

    public function onRowDragEnd()
    {
        $this->dragged = null;
    }

    public function onDragOver(DomEvent $event)
    {
        // allow dropping here
        $event->preventDefault();
    }

    public function onDrop(DomEvent $event)
    {
        $event->preventDefault();
        if ($this->dragged === null) {
            return;
        }
        $hero = $this->dragged;
        $this->dragged = null;
        $this->team = [...$this->team, $hero];
        $this->heroes = array_values(array_filter($this->heroes, fn(HeroModel $h) => $h !== $hero));
    }

    public function isDragging(): bool
    {
        return $this->dragged !== null;
    }

    public function teamNames(): string
    {
        return implode(', ', array_map(fn(HeroModel $h) => $h->Name, $this->team));
    }
}

The drop target must call preventDefault() in its (dragover) handler, otherwise the browser does not allow the drop.

The example resets $dragged in onDrop too. The dropped row is removed from the table, and a removed row may not emit (rowDragEnd).

Drag Data

The table also puts the row key into the drag data as text/plain, and sets the allowed effect to move. The key is the item property set by selectKey (default: Id, see Selection). It is useful when the drop target is outside your component and cannot receive the item from (rowDragStart).

Draggable rows show a grab cursor.

Properties

draggableRows - (optional) makes the rows draggable. Default: false.

selectKey - (optional) the item property put into the drag data. Default: Id.

Events

(rowDragStart) - (T $item): void. Emitted when the user starts dragging a row.

(rowDragEnd) - (bool $ended): void. Emitted when the drag ends. The value is always true.