NbDragHandle is the six-dot grip you press to move a block, a table row or a table column. It is a real button with a name, a visible focus ring and a keyboard path, and it reports a drag without performing one: where the item may land, what the drop indicator looks like and how the data reorders all belong to the host.
Use it when the reorder logic is yours, as in a document editor or a data table. For a plain vertical list, NbReorderList already owns the whole job.
Tab to a handle, press Space, move with the arrow keys, and press Space again to drop.
<template>
<div v-for="(block, index) in blocks" :key="block.id" class="block">
<NbDragHandle
:label="`Move ${block.label}`"
:announcement="announcement"
@drag-start="onStart(index, $event)"
@drag-move="onMove(index, $event)"
@drag-end="onDrop"
@drag-cancel="onCancel"
/>
{{ block.label }}
</div>
</template>The drag lifecycle
Every way of dragging reports the same four events, each with an IDragHandleEvent whose via says what drove it.
| Event | Pointer | Keyboard | Native (native) |
|---|---|---|---|
drag-start | Pointer travelled past threshold | Space or Enter | dragstart |
drag-move | Every pointer move | Each arrow key along axis | (use your drop target) |
drag-end | Pointer released | Space / Enter, or focus left | dragend with a drop |
drag-cancel | Escape or pointercancel | Escape | dragend without a drop |
For a pointer, deltaX and deltaY are pixels from where the press started, and clientX / clientY are the pointer. For the keyboard they count arrow-key steps since pick-up, and direction names the step just taken. A host that reorders live can act on direction. One that previews and commits on drop can use the deltas.
A press that never travels past threshold stays a plain click, so a handle can also open a block menu on click. A press that did become a drag does not also fire click.
Native drag and drop
Editors built on ProseMirror move blocks with the browser's own drag and drop, and need to fill dataTransfer themselves. Set native and the handle becomes draggable and forwards the DragEvent on drag-start:
<NbDragHandle
native
:label="`Move ${block.label}`"
@drag-start="({ event }) => startBlockDrag(event as DragEvent)"
/>The keyboard path is the same in both modes.
Axis
axis picks the glyph and the arrow keys. A handle for a row or a block uses vertical (the default), a column handle uses horizontal, and a free-floating item uses both.
Announcing moves
A keyboard user does not see the item travel. The handle cannot know the new position, because it does not own the reorder, so it gives you a polite live region instead: set announcement after each move ("Introduction moved to position 3 of 5") and it is read out.
Hosts that move DOM nodes
A browser drops focus from a node when that node is moved, which is exactly what a keyed list does when the host reorders on drag-move. The handle recognises that blur (focus went nowhere and nothing was clicked) and takes focus back, so the item stays held. A blur to another control, or a click elsewhere, drops the item, the same as NbReorderList.
Accessibility
- A native
<button type="button">, named by the requiredlabel. The name gains ", picked up" while held. - The keyboard instructions are the button's description (
aria-describedby), so they are heard once on focus rather than repeated as part of the name. Overrideinstructionsto translate them. grabat rest,grabbingfor the length of a pointer drag. Thegrabbingcursor is set on the document, so it holds wherever the pointer goes, and text selection is suppressed while dragging.touch-action: none, so a touch drag moves the item instead of scrolling the page.- Focus is shown with the standard focus ring. A held item is outlined in the primary colour.