Unmanaged Row Dragging
Introduction
Section titled “Introduction”Unmanaged row dragging gives you the drag interaction without the automatic
reorder. RowDragManager still owns the pointer gesture and paints the drop
indicator, but on drop it leaves your data untouched — it just tells you what
the user did and steps back. You decide what happens: apply the move, reject it,
route the row somewhere else, or fold it into your own model.
That’s the distinction from Managed Row Dragging, where the grid reorders its own data so the change sticks with no code from you. Managed is the fast path; unmanaged is the control path — reach for it when the reorder has rules, lives server-side, or has to land somewhere other than the grid’s own array.
Opt in with { managed: false } and subscribe with onDrag:
import { RowDragManager } from '@zengrid/enterprise/grid';
const drag = new RowDragManager({ managed: false }); // unmanagedconst grid = new Zengrid(mount, { columns, /* … */ });grid.setData(rows);drag.attach(grid);
drag.onDrag((event) => { // Fires on every phase — nothing moves until you make it move. if (event.phase === 'drop' && event.plan) { applyMyReorder(event.plan.fromSource, event.plan.toSource); }});The drag lifecycle
Section titled “The drag lifecycle”onDrag(fn) fires on every phase of the gesture, so you can preview, log, or
gate the move as it happens. It returns an unsubscribe function.
| Phase | When | plan |
|---|---|---|
start |
The user picks a row up. | null |
move |
The pointer crosses rows during the drag. | null |
drop |
The row is released. | the concrete reorder, or null for a no-op |
Every event carries the same shape:
| Field | Meaning |
|---|---|
phase |
'start' · 'move' · 'drop'. |
fromRow |
Display index the drag started on. |
fromSource |
Source (dataset) index the drag started on. |
overRow |
Display index under the pointer (-1 on start). |
insertBeforeRow |
Display index the row would land before (-1 on start). |
plan |
On drop, the source-index reorder { fromSource, toSource, toRow }, or null when the drop is a no-op. null on start/move. |
The plan on a drop is the same decision managed mode would apply — so the
simplest unmanaged handler reproduces managed behaviour by feeding it to
moveRow yourself. moveRow is exported for exactly that:
import { RowDragManager, moveRow } from '@zengrid/enterprise/grid';
drag.onDrag((e) => { if (e.phase !== 'drop' || !e.plan) return; const data = readRowsFromGrid(grid); grid.setData(moveRow(data, e.plan.fromSource, e.plan.toSource)); grid.refresh();});Managed vs. unmanaged
Section titled “Managed vs. unmanaged”Managed (managed: true, default) |
Unmanaged (managed: false) |
|
|---|---|---|
| Drop reorders the grid’s data | Yes, automatically | No — you do it |
| Notifies via | onChange (after commit) |
onDrag (every phase) |
| Best for | plain reordering of the grid’s own array | rules, vetoes, server-side order, external targets |
Everything else is shared: handle still reserves dragging for a
rowDragColumn() grip, the drop indicator
still renders (import @zengrid/enterprise/styles.css), and detach() still tears
it all down. Unmanaged dragging works against the natural (unsorted) row order, so
use it on a grid without an active sort.
Try it live
Section titled “Try it live”Drag a row in each tab. The first leaves the data alone (watch the console); the second hands the reorder to you; the third layers a rule on top — the top row is locked and nothing may drop above it.
managed: false — the drop indicator tracks the pointer but the grid never reorders itself. Open the console to watch start/move/drop fire; the rows snap back because nothing is wired to move them.
- Managed Row Dragging — let the grid reorder its own data for you.
- Accessing Rows — the
grid.rowsAPI the handlers above read the order back from. - Row Data — supply the rows you reorder.