Skip to main content

Module components

Module components 

Source
Expand description

§Drag-and-drop API reference

Ready-made components over the shared drag context: DndProvider, Draggable, DropZone and DragOverlay, plus DragHandle, NoDrag, SettleSlot for seamless drop-settle handoffs, and ParentZone, the context marker behind automatic nesting.

Concept guide: docs/concepts/drag-and-drop.md. The two-world BridgeDropZone and the N-world bridge_drop_zone! macro also live in this module; their reference is docs/api/mixing-payload-types.md.

rsx! {
    DndProvider::<Card> {
        Draggable::<Card> { payload: card.clone(), "Drag me" }
        DropZone::<Card> {
            on_drop: move |outcome: DropOutcome<Card>| { /* ... */ },
            "Drop here"
        }
    }
}

All components are generic over the payload type T: Clone + PartialEq + 'static. Except for DndProvider, each renders a wrapper div and forwards arbitrary attributes (class, style, id, …) to it. Every forwarded style fragment is merged: configurable defaults are user-overridable, while behavior-critical declarations such as touch ownership and drag transforms are emitted last. Attributes that implement the component’s contract—its event listeners, ARIA role, focusability, mounted hook, and data-* state—remain component-owned and cannot be replaced through the forwarded attribute list; use the component’s typed callbacks for supported user events.

§DndProvider

Provides a DndContext<T> (the drag world) to its children. Renders no DOM of its own.

PropTypeDefaultWhat it does
dirDirectionLtrRtl mirrors keyboard navigation and spatial zone ordering to follow a right-to-left layout. Live prop changes propagate reactively.
releaseReleasePolicy<T>pointer-within, 48px recovery, not stickyCollision detector, release recovery radius, and sticky-hover behavior for this provider. See core.md.

§Draggable

A focusable pointer and keyboard drag source. On drag start it pushes payload into the shared context.

PropTypeDefaultWhat it does
payloadTrequiredThe value delivered to whichever zone receives this drag.
drag_idOption<DragId>stable auto idStable source identity. Set it when a domain id should survive remounts; otherwise one is generated per mounted draggable. The ordinary HTML id remains available through forwarded attributes.
zoneOption<ZoneId>NoneThe zone this item currently lives in, reported in DropOutcome::from.
effectDropEffectMoveBase drop effect; modifier keys can override it at release.
disabledboolfalseDisable dragging without unmounting. Renders data-disabled.
thresholdf648.0Movement in CSS px before a pointer press becomes a drag.
activationOption<ActivationPolicy>NoneComposable surface, handle, delay, distance, either, or manual activation. When omitted, threshold preserves the 3.x behavior.
touchTouchSenseAutoHow a finger shares the element with native scrolling. Auto keeps vertical swipes scrolling and picks up on a short hold or sideways pull; Immediate owns every touch from the first pixel. A mouse is identical under both; pens follow the finger rules.
labelOption<String>NoneHuman name used in screen-reader announcements (“Picked up {label}”).
on_drag_startOption<EventHandler<()>>NoneFired when a drag begins.
on_drag_endOption<EventHandler<bool>>NoneFired when the drag ends; true if a zone consumed the payload, false if cancelled.

Data attributes, present while true and absent otherwise:

AttributePresent while
data-draggingthis element’s payload is in flight (also correct when a custom source started the drag)
data-disableddisabled is set

Keyboard, on the focused element: Space or Enter picks up and drops, Up and Down cycle zones in spatial order, Right descends into nested zones and Left ascends (mirrored under Direction::Rtl), Escape cancels. Keyboard drops deliver the same DropOutcome with mode: DragMode::Keyboard.

§Activation policies and handles

ActivationPolicy combines an Activator (Surface, Handle, or Manual) with an ActivationConstraint:

  • Distance(px) promotes after pointer travel.
  • Delay { duration_ms, tolerance } promotes after a hold while movement remains within the tolerance.
  • Either(vec![...]) promotes when any contained constraint succeeds.
  • Manual leaves pickup to a custom DndContext source.

Use DragHandle for editable cards and other mixed-interaction surfaces. It renders an accessible button, is keyboard-operable, and activates only the nearest handle-configured draggable. Its functional style claims touch on the handle while the rest of the card remains available for native scrolling:

Draggable::<Card> {
    payload: card,
    activation: ActivationPolicy::handle(ActivationConstraint::Distance(6.0)),
    DragHandle { label: "Move card", "Move" }
    CardEditor {}
}

For a surface-configured draggable, wrap controls that must never start a drag in NoDrag. It stops pointer and keyboard activation while preserving the control’s normal behavior and exposes data-no-drag for inspection.

§DropZone

A region that accepts drags carrying T. Registers itself (id, label, callbacks, element handle) in the provider’s zone registry, which powers pointer hit-testing and keyboard navigation, and measures itself the moment it mounts, so a zone appearing mid-drag (a virtualized row) is immediately hit-testable.

PropTypeDefaultWhat it does
idOption<ZoneId>autoStable identity. Auto-generated ids start at 2^32; explicit ids in u32 range never collide with them.
labelOption<String>NoneHuman name for announcements (“Over {label}”). Kept in sync if the prop changes.
acceptsOption<Callback<T, bool>>accept allReturn false to reject a payload: the zone will not highlight, keyboard navigation skips it, and drops fall through it. Keep it cheap; registry queries snapshot candidates before invoking application callbacks.
accepts_queryOption<Callback<DropQuery<T>, bool>>accept allRich acceptance using payload, source, proposed effect, input mode, pointer kind, and drag id. Evaluated after accepts.
allowed_effectsDropEffectsDropEffects::ALLEffects this target supports. An unsupported proposal falls back deterministically to Move, Copy, then Link; an empty set rejects the drag.
edgeOption<EdgeSet>NoneOpt into the closest-edge signal: renders data-edge live while an acceptable pointer drag hovers, and fills DropOutcome::edge at release.
on_dropEventHandler<DropOutcome<T>>requiredFired on a successful drop.

Data attributes:

AttributePresent while
data-activean acceptable drag is in flight anywhere (reveal your targets)
data-overthat drag hovers this zone (highlight it)
data-edgehovered with edge set; valued "top" | "right" | "bottom" | "left"

All three follow pointer, touch and keyboard drags alike, because they read the shared context rather than DOM events.

Nesting is automatic: a DropZone inside another discovers its parent through ParentZone and provides itself to zones deeper down, which is what hierarchical keyboard traversal walks.

Overlap precedence follows registry order, not browser paint order. Among overlapping acceptable zones, the later record receives the drop; a rejecting one is skipped at release. CSS z-index, stacking contexts and portals are not inspected, so keep registry and visual order aligned when targets overlap, or avoid the overlap. Replacing a same-id record retains its slot.

§Drag monitor

use_dnd_monitor::<T> subscribes to the nearest provider’s complete lifecycle without placing callbacks on every source or target:

use_dnd_monitor::<Card>(move |event| match event {
    DndEvent::Dropped(receipt) => save_undo(receipt),
    DndEvent::Cancelled { reason, .. } => log_cancel(reason),
    _ => {}
});

Events are Started, Moved, TargetChanged, Dropped, and Cancelled. Each carries a DragSnapshot<T> with the stable DragId; dropped events carry a parallel DropReceipt<T> containing both snapshot and the unchanged 3.x DropOutcome<T>. This adds metadata without extending the constructible, exhaustive DropOutcome field set.

§DragOverlay

Renders its children pinned to the pointer while a drag is in flight: a custom ghost that follows the cursor.

PropTypeDefaultWhat it does
settleboolfalseOn a successful pointer drop, glide the ghost into the receiving zone instead of vanishing. Cancelled drags and keyboard drops never settle.
durationf64200.0Settle transition duration in milliseconds.
easingString"ease"CSS easing function for the settle glide.
match_sourceboolfalseSize the ghost to the grabbed element’s measured rect, so it appears exactly over what was picked up. Custom drag sources must call set_source_rect or the ghost stays hidden.
on_settledOption<EventHandler<()>>NoneFired when the drop-settle finishes, including the degenerate no-glide cases. Never fires for cancelled drags.

During the glide the context is settling: dragging() is already false (zones have unlit) but payload() stays readable, so the ghost keeps its content. The glide honors prefers-reduced-motion by snapping near instantly; cleanup still runs because transitionend still fires.

Keyboard drags carry no pointer position, so during one the ghost sits at the viewport origin. Check dnd.mode() and skip rendering it if that matters.

§SettleSlot

Wraps the element a drop just created so the drop-settle reads as one object: while the ghost glides, the wrapper holds the element’s space but keeps it invisible (no second copy next to the ghost), re-aims the glide at its own measured rect, and reveals the element the instant the ghost unmounts.

PropTypeDefaultWhat it does
activeboolrequiredTrue on the just-landed element only, typically by remembering the dropped payload’s id in on_drop and comparing.

Inert while nothing is settling (keyboard drops, cancelled drags, overlays without settle), so it is always safe to render:

on_drop: move |o: DropOutcome<Card>| { landed.set(Some(o.payload.id)); /* model */ },
// ...
SettleSlot::<Card> { active: landed() == Some(card.id),
    Draggable::<Card> { payload: card.clone(), CardFace { card } }
}

§ParentZone

The context marker a DropZone provides so zones nested inside it discover their parent. Read it (try_use_context::<ParentZone>()) when building a custom zone that should participate in hierarchical keyboard traversal; provide it if custom zones can nest inside yours.

§DropOutcome

Everything a consumer learns from a completed drop, delivered to on_drop:

FieldTypeMeaning
payloadTThe value that was dragged.
fromOption<ZoneId>The zone the Draggable declared via its zone prop, if any.
toZoneIdThe zone that received the drop.
effectDropEffectThe resolved effect, modifier keys applied. See docs/api/drop-effects.md.
modeDragModePointer or Keyboard. Non-exhaustive; match the variants you handle.
clientPointPointer position in viewport coordinates at drop time.
elementPointPointer position relative to the zone’s element.
grabPointWhere inside the dragged element the pointer grabbed it. element - grab is where the element’s top-left should land. Zero for keyboard drops.
edgeOption<Edge>The zone edge nearest the release point. Some only for pointer drops on zones that set edge. Treat None as your neutral intent.

§Closest-edge primitives

Edge names one side of a zone (Top, Right, Bottom, Left), with as_str() matching the data-edge attribute values. EdgeSet names which edges compete, by stacking direction: Vertical (top/bottom), Horizontal (left/right), All. edge_of(point, rect, edges) is the pure function behind both: it clamps the point into the rect and returns the nearest allowed edge, preferring Top, then Bottom, Left, Right on ties. Call it directly for custom zones.

§Where the rest lives

Ids, geometry (Point, Rect), the context and registry: docs/api/core.md. DropEffect and the modifier convention: docs/api/drop-effects.md. TouchSense details: docs/concepts/touch-and-input.md.

Re-exports§

pub use draggable::Draggable;
pub use draggable::Draggable;
pub use drop_zone::BridgeDropZone;
pub use drop_zone::BridgeDropZone;
pub use drop_zone::BridgeParentZoneBoundary;
pub use drop_zone::DropZone;
pub use drop_zone::DropZone;
pub use handle::DragHandle;
pub use handle::DragHandle;
pub use handle::NoDrag;
pub use handle::NoDrag;
pub use overlay::DragOverlay;
pub use overlay::DragOverlay;
pub use overlay::SettleSlot;
pub use overlay::SettleSlot;
pub use provider::DndProvider;
pub use provider::DndProvider;

Structs§

ParentZone
Context marker a DropZone provides so zones nested inside it can discover their parent - powering hierarchical keyboard traversal with no configuration.

Functions§

BridgeDropZone
A drop target registered in two payload worlds at once - the bridge between two coexisting providers (DndProvider<A> and DndProvider<B>).
DndProvider
Provides a DndContext<T> to its children.
DragHandle
An accessible button that activates the nearest handle-only super::Draggable.
DragOverlay
Renders its children pinned to the pointer while a drag is in flight - a custom “ghost” that follows the cursor.
Draggable
Wraps its children in a focusable pointer/keyboard drag source and pushes payload into the shared context on drag start.
DropZone
A region that accepts drags carrying T.
NoDrag
Stop pointer and keyboard events in an interactive subtree from activating a surface-driven draggable.
SettleSlot
Wraps the element a drop just created so the drop-settle reads as ONE object: while the ghost glides, the wrapper holds the element’s space but keeps it invisible (no “second copy” next to the ghost), re-aims the glide at its own measured rect (the ghost lands exactly where the element is, not at the zone’s center), and reveals the element the instant the ghost unmounts.