Drag & Drop
Type-safe drag and drop with typed payloads and accept predicates.
Kraft's DnD system uses two components: a DndDragHandle that makes an element draggable, and a
DndDropTarget that accepts drops. Both are generic over a payload type, so the compiler ensures you
only drop compatible items.
DnD is part of the kraft-semanticui module.
Drag handles
Wrap any element in a DndDragHandle to make it draggable. The payload parameter is the
data object that travels with the drag:
data class Task(val id: String, val title: String)
// Inside your component's render:
val task = Task("1", "Write docs")
DndDragHandle(payload = task) {
ui.segment {
icon.bars.render()
+task.title
}
} When the user starts dragging, a visual clone follows the cursor. The payload type (Task) is
inferred from the argument.
Drop targets
Define a drop zone with DndDropTarget. The type parameter must match the drag handle's payload type:
DndDropTarget<Task> {
accepts = { task -> task.id != currentTask.id } // filter what's accepted
onDrop = { task ->
// handle the dropped task
moveTask(task, targetColumn)
}
} The accepts predicate controls which payloads this target will highlight for and accept. If it
returns false, the target ignores the drag.
Visual feedback
Use the built-in highlight helpers for standard visual feedback during drag operations:
DndDropTarget<Task> {
greenHighlights() // green border on valid hover
onDrop = { task -> moveTask(task) }
}
// Or blue:
DndDropTarget<Task> {
blueHighlights()
onDrop = { task -> moveTask(task) }
} For custom feedback, use the individual callbacks:
DndDropTarget<Task> {
onDragStart = { target ->
// a compatible drag started somewhere — highlight this zone
}
onDragEnd = { target ->
// drag ended (dropped or cancelled) — remove highlight
}
onMouseOver = { target ->
// dragged item is hovering over this target
}
onMouseOut = { target ->
// dragged item left this target
}
onDrop = { task ->
// item was dropped here
}
} Full example
A Kanban-style board with draggable cards and two columns:
data class Card(val id: String, val title: String)
class KanbanBoard(ctx: NoProps) : PureComponent(ctx) {
private var todo by value(listOf(Card("1", "Design"), Card("2", "Implement")))
private var done by value(listOf<Card>())
override fun VDom.render() {
ui.two.column.grid {
noui.column {
h3 { +"To Do" }
DndDropTarget<Card> {
greenHighlights()
accepts = { card -> card !in todo }
onDrop = { card ->
done = done - card
todo = todo + card
}
}
todo.forEach { card ->
DndDragHandle(payload = card) {
ui.segment { +card.title }
}
}
}
noui.column {
h3 { +"Done" }
DndDropTarget<Card> {
blueHighlights()
accepts = { card -> card !in done }
onDrop = { card ->
todo = todo - card
done = done + card
}
}
done.forEach { card ->
DndDragHandle(payload = card) {
ui.segment { +card.title }
}
}
}
}
}
}