Builtin Library
README.md
dndList
Drag-and-drop reorderable list. Each child renders inside a vbox of
two stacked nodes — the original child plus an overlay canvas sized to
the row that captures drag and pointer-move events. Built on onDrag
onPointerMovecanvas interactions; the engine has no dedicated dnd-list arm.
Exports
dndList(id: string?, children: { any }?, opts: DndListOpts?) -> WidgetNode— build a drag-reorderable list widget node.
Types:
DndListOpts = { rowHeight?, rowWidth?, style?, app?, onReorder?, classes?, barColor?, dragStroke?, label? }
Usage
local dndList = require("@builtin::modules.zui.widget.dndList")
dndList("playlist", {
Z.lbl("Track 1"),
Z.lbl("Track 2"),
Z.lbl("Track 3"),
}, { onReorder = "playlist:reorder" })
-- Then in `onCallback`:
-- data.value = { from = 1, to = 3, value = "1,3" }
-- Caller re-renders with the children rearranged.
Notes
- Reorder events fire only on drag stop (
dragStopped = true) and only whenfrom ≠ to. The payload includes both numericfrom/toand a legacy"from,to"string that the engine test suite asserts on. - Defaults:
rowHeight = 28,rowWidth = 240,barColor = "#78b4ff",dragStroke = "#78b4ff78". - Auto-registers
<id>:drag:<index>and<id>:hover:<index>handlers on the surroundingZ.app, deduped by id. Drag state lives inapp._dndListState[id]and persists across renders. - Outside
Z.app(no app in scope), the widget renders but the overlay never draws the insertion bar — drag interaction is inert. - The outer Panel exposes
role = "list"andariaLabel = opts.label or "Reorderable list"for accessibility.