Skip to main content

Module tree

Module tree 

Source
Expand description

§Trees API reference

Hierarchical drops for file explorers, nested menus and outliners: a drop on a node can mean three different things, so DropIntent captures the trichotomy, TreeNodeTarget resolves it live from where inside the row the pointer sits, intent_from_offset is the public band math behind it, and would_create_cycle guards against dropping a node into its own subtree.

Concept guide: docs/concepts/trees.md.

TreeNodeTarget::<u64> {
    node: NodeId(n.id),
    accepts: move |(dragged, intent): (u64, DropIntent)| {
        if intent == DropIntent::Into && !n.folder { return false; }
        !would_create_cycle(parent_of, NodeId(dragged), NodeId(target))
    },
    on_drop: move |ev: TreeDropEvent<u64>| reparent(ev.payload, ev.target, ev.intent),
    Draggable::<u64> { payload: n.id, RowFace {} }
}

§TreeNodeTarget

A single tree row acting as a drop target with intent detection. The row is target-only; drags start from a core Draggable placed inside it, and the payload travels through the shared DndContext<T>. Renders a wrapper div and forwards arbitrary attributes (class, style, id, …) to it.

PropTypeDefaultWhat it does
nodeNodeIdrequiredThe node this row represents, handed back as TreeDropEvent::target.
row_heightf6428.0Height in px used for the before/into/after bands. Keep it close to the rendered height: keyboard drops resolve their intent from the measured row center against this value, so a large mismatch can bias a keyboard drop toward After or Before instead of Into.
acceptsOption<Callback<(T, DropIntent), bool>>accept allReceives payload and intent together; return false to refuse the combination (cycle prevention, leaves refusing Into).
on_dropEventHandler<TreeDropEvent<T>>requiredFired on an accepted drop with payload, target and intent.
labelOption<String>NoneHuman name announced during keyboard navigation. Kept in sync if the prop changes.

Data attributes:

AttributePresent while
data-intentan acceptable pointer drag hovers this row; valued "before" | "after" | "into" per the live band

The attribute is absent when not hovered, so both value selectors (Tailwind data-[intent=into]:bg-blue-50) and presence selectors (data-intent:outline) work. The band derives from the shared pointer position rather than DOM hover events, so mouse, touch and pen see the same feedback; keyboard drags render no live band.

Behavior:

  • Every target registers itself (id, parent, label, callbacks) in the shared zone registry, which is what makes it reachable by pointer hit-testing and keyboard navigation. It measures itself at mount, unregisters on unmount, and discovers a parent zone through ParentZone.
  • At the registry level a target accepts a payload if accepts passes for any of the three intents, because hover cannot know the final band yet. The exact intent is re-checked at drop time; a release in a band whose exact combination is refused delivers nothing, so on_drop never sees a refused pair.
  • Keyboard drops resolve from the measured row center against row_height, which lands in the Into band when the prop is honest.
  • In a multi-window desktop drag world, hover and the pointer read through the joined window, so the live band tracks correctly in windows the drag did not start in.
  • All props are re-synced when they change across renders, so rows rendered in loops stay current as the model reorders.

§TreeDropEvent

A completed tree drop, delivered to on_drop:

FieldTypeMeaning
payloadTThe value that was dragged.
targetNodeIdThe node whose row received the drop.
intentDropIntentWhere, relative to target, the payload should land.

Non-exhaustive, so drop context can be added without a major release: destructure with .., and synthesize events for tests or programmatic moves through TreeDropEvent::new(payload, target, intent).

§DropIntent

Where, relative to the target node, the payload should land:

VariantMeaning
BeforeInsert as the target’s previous sibling.
AfterInsert as the target’s next sibling.
IntoInsert as the target’s child.

Copy and Eq, so intent rules are plain comparisons.

§NodeId

Identifies a tree node: a newtype over a public u64 (NodeId(pub u64)) with From<u64>. Copy, Eq, Hash and Ord, so it works as a map key and sorts.

§intent_from_offset

pub fn intent_from_offset(y: f64, row_height: f64) -> DropIntent

Derives a DropIntent from the pointer’s Y offset within a row of the given height: the top 25% is Before, the bottom 25% is After, the middle half is Into. The quarter boundaries themselves land Into. Offsets outside the row clamp to the nearest end band, and heights are floored at 1.0 so a degenerate row never divides by zero.

This is the exact math TreeNodeTarget runs, public for custom tree interactions. If your rows cannot receive children (a flat outline), map Into to whichever sibling intent you prefer.

§would_create_cycle

pub fn would_create_cycle(
    parent_of: impl Fn(NodeId) -> Option<NodeId>,
    dragged: NodeId,
    target: NodeId,
) -> bool

Would attaching dragged under target create a cycle? Walks target’s ancestry through the parent_of lookup you provide, returning true when dragged is target itself or any of its ancestors. The crate never holds your tree, so parent_of is any closure over your own model.

The walk is bounded at 10,000 steps; a parent map that never terminates (a cycle in your own data) reads as unsafe (true) rather than looping forever.

§Where the rest lives

The Draggable that starts row drags, and DropOutcome: docs/api/drag-and-drop.md. Zone ids, the registry and ParentZone: docs/api/core.md. What keyboard drags announce: docs/api/accessibility.md.

Re-exports§

pub use TreeNodeTarget_completions::Component::TreeNodeTarget;

Structs§

NodeId
Identifies a tree node.
TreeDropEvent
A completed tree drop.
TreeNodeTargetProps
Properties for the TreeNodeTarget component.

Enums§

DropIntent
Where, relative to the target node, the payload should land.

Functions§

TreeNodeTarget
A single tree row that acts as a drop target with intent detection.
intent_from_offset
Derive a DropIntent from the pointer’s Y offset within a row of the given height. Top 25% → Before, bottom 25% → After, middle → Into.
would_create_cycle
Would attaching dragged under target create a cycle? Walks target’s ancestry via the parent_of lookup you provide.