dioxus-dnd 3.1.0

Modular, accessible drag-and-drop for Dioxus: sortable lists, kanban boards, trees, grids, file drops, multi-select, touch support and more
Documentation
# Touch and input

One input model covers every device: in-app drags run on pointer events,
which unify mouse, touch and pen into a single stream, and every
provider-backed drag source is keyboard-operable on top. Nothing here uses
the browser's native HTML5 drag, so there is no native drag image to fight
and no touch shim to install.

API reference: the `touch` and `threshold` props in
[api/drag-and-drop.md](../api/drag-and-drop.md); this guide is the model
behind them. Live demo: the
[Podcast queue](https://kindintelligence.github.io/dioxus-dnd/podcast-queue)
page runs touch grips inside a scrollable, auto-scrolling list.

## The mental model

A press is not a drag. Every drag surface feeds the same small gesture
machine, and the machine promotes a press to a drag by device-specific
rules:

- **A mouse promotes on plain travel**: 8 CSS px in any direction (the
  `threshold` prop). Below that, a release is a click, so clicks stay
  clicks - no accidental drags from a twitchy press.
- **A finger or a pen under `TouchSense::Auto`** (the default) promotes on
  a short hold - 250ms with the finger still - or on a sideways-dominant
  pull (more horizontal than vertical) past the threshold. A
  vertical-dominant pull resolves the press as scroll intent: the machine
  steps aside and the browser pans the page. An exact diagonal counts as
  scroll.

Keyboard is the third path, present on every typed source: Space or Enter
picks up, arrows choose a zone, Space drops, Escape cancels, and the same
`on_drop` fires with `mode: DragMode::Keyboard`. See
[Accessibility](accessibility.md).

## Sharing the screen with scrolling

`Auto` works because of what the element declares *before* any gesture:
it carries `touch-action: pan-y pinch-zoom`, so the browser knows vertical
pans are its to take, and pinch-zoom stays allowed (two fingers were never
a drag, and zooming is an accessibility floor). Text selection and the iOS
callout are pinned down too, so a long-press cannot start selecting instead
of dragging.

The moment a drag begins, the item owns the touch. `touch-action` is only
consulted at gesture start, so the crate also cancels every further touch
move for the drag's duration - the page stays put under the drag, and a
pan can never start mid-flight. Nothing to configure: scrolling a list and
dragging its rows stop fighting by default.

## `TouchSense::Immediate`

The opt-out restores `touch-action: none`: the surface owns every touch
from the first pixel, and any travel past the threshold drags - no hold, no
sideways rule. The cost is that finger-scrolling *across* the element is
disabled.

Right for surfaces that never sit in a scrollable view - a full-screen
canvas, a game board - or when a vertical pull must begin a drag instantly:

```rust,ignore
Draggable::<Piece> { payload: piece, touch: TouchSense::Immediate, PieceFace {} }
```

Mouse drags are identical under both settings; `TouchSense` decides what
fingers and pens mean, and under `Immediate` every device promotes on plain
travel. `SortableGrid` tiles are always immediate (grids rarely scroll by
dragging across their own tiles); `Draggable` and whole-row `SortableList`
take the `touch` prop.

## Touch handles on sortables

`touch_handle: true` on `SortableList` confines pointer drags to a leading
grip while the rows themselves keep finger-scrolling. The grip is always
immediate - a grip *is* an explicit statement of intent, so there is
nothing to disambiguate - and it is exposed as `[data-sort-handle]` for
styling. The default grip renders a braille-dots glyph; pass the `handle`
callback for your own content.

```rust,ignore
SortableList { len, render, on_sort, touch_handle: true,
    class: "[&_[data-sort-handle]]:w-6 [&_[data-sort-handle]]:cursor-grab",
}
```

Use it when you want the explicit affordance, or when rows are dense enough
that even hold-or-sideways feels risky next to scrolling.

## Pointer capture

Where do move events go once the cursor leaves the element mid-drag? Dioxus
0.7 exposes no pointer-capture API, so the answer depends on platform and
features (this summarizes the README's "Platform notes"):

- **Web with the `web` feature** (recommended for web): the crate grabs
  real pointer capture on press. The drag stays glued to the source no
  matter where the cursor goes, and a release anywhere commits the drop.
- **Web without it** (the dependency-free default): nothing retargets, so
  the crate compensates. Straying off the surface does not cancel the drag,
  and a mouse released outside is reconciled when the cursor returns, via
  held-button state. Best-effort: a release that never returns will not
  commit.
- **Desktop (dioxus-desktop)**: the capture API does not exist there, so
  `Draggable`, `SortableList` and `SortableGrid` render a full-viewport
  capture substitute while a drag is in flight - an invisible fixed layer
  that keeps pointer events flowing to the drag's handlers anywhere in the
  window. Verified on Linux (WebKitGTK).
- **Touch and pen are unaffected everywhere**: the browser implicitly
  captures them to the source element, whole gesture included.

## `PointerKind`

At pickup the crate records which device started the drag - `Mouse`,
`Touch` or `Pen`, mapped from the DOM `pointerType`. Host-side glue (the
multi-window bridge above all) reads it to decide which drags need input
bridging: a touch contact is implicitly captured, so the source element
keeps receiving the whole gesture and bridging it again would double-drive
the drag; mouse and pen go blind at the viewport edge whenever native
capture is unavailable, so they must be bridged. The enum is
non-exhaustive, and `implicitly_captured()` encodes the safe default for
kinds it has never heard of. See [Multi-window](multi-window.md).

## Gotchas

- **`Immediate` inside a scrollable list is a scroll trap.** Rows that own
  every touch cannot be finger-scrolled across; keep `Auto` there, or use
  `touch_handle`.
- **The hold-or-sideways rule covers pens too.** Under `Auto`, a pen
  promotes like a finger (hold or sideways pull); only a mouse promotes on
  plain travel.
- **Two fingers never drag.** Pinch-zoom stays allowed under `Auto` by
  design.
- **Self-contained sortables have no keyboard drag.** `SortableList` /
  `SortableGrid` run without a provider; pair them with `ReorderButtons`
  for the no-gesture path. See [Accessibility]accessibility.md.
- **The capture substitute is `position: fixed`.** A transformed ancestor
  becomes its containing block and clips it - the standard caveat, shared
  with the overlay.

## Related

- [Drag and drop]drag-and-drop.md: the components these gestures drive.
- [Accessibility]accessibility.md: the keyboard path and
  `ReorderButtons`.
- [Sortable lists]sortable-lists.md: whole-row versus handle dragging.
- [Multi-window]multi-window.md: what host glue does with `PointerKind`.