A DragSession is the temporary state SnapSort creates for one drag gesture, or the gesture starting with the pointer press, moving the item, and finally ending with the pointer release and placing the item in its final position. Sessions, for the duration of a gesture, manage various states like the item/ghost position, callbacks, etc.

Application Boundary

Before we move on, we must cover how SnapSort separates the application from SnapSort library. SnapSort is strictly designed to manage the logic necessary to move and animate items, containers, and ghosts; it is prohibited from making any permanent changes to the DOM or application state without first invoking callbacks to commit a request to change the DOM.

Subsequently, the frontend framework is responsible for applying these callbacks to update the DOM and application state as needed.

onDragStart

Immediately upon starting a drag, the root container creates a pending DragSession and dispatches onDragStart. At this point, the drag has not officially started yet. The callback receives a DragStartEvent:

interface DragStartEvent {
  session: DragSession;
  item: Item;
  itemId: ItemId;
  itemMetadata: ItemMetadata;
  items: Item[];
  itemIds: ItemId[];
  itemsMetadata: ItemMetadata[];
  element: HTMLElement | null;
  source: DragLocation;
  sources: DragLocation[];
}

Return false from onDragStart to veto a gesture before the session is officially active.

import type { DragStartEvent } from "@snap-engine/snapsort";

function onDragStart(event: DragStartEvent) {
  if (event.itemsMetadata.some((metadata) => metadata.disabled === true)) {
    return false;
  }

  event.session.dragVisual = "preview";
}

A vetoed startup clears the pending session. In simple terms, it cancels the drag before it even begins.

What’s in a Session

Once created, the interface for a DragSession looks like this.

type DragSessionStatus = "pending" | "active" | "dropping" | "ended";

interface DragSession {
  readonly root: Container;
  readonly pointerId: number;
  readonly items: readonly Item[];
  readonly sources: readonly DragLocation[];
  readonly pressedItem: Item;
  readonly primaryItem: Item;
  readonly start: Readonly<{ x: number; y: number }>;
  readonly pointer: Readonly<{ x: number; y: number }>;
  readonly status: DragSessionStatus;
  dragVisual: "item" | "preview" | "none";
  dropEffect: "move" | "none";
}

For now, the key fields are:

  • items: the ordered run of Items being dragged.
  • pressedItem: the Item where pointer input began.
  • primaryItem: items[0], used by singular event fields and as swap mode’s one participant.
  • dragVisual: how the dragged run is represented at the pointer.
  • dropEffect: whether SnapSort commits its ordinary drop mutation.

DragVisual

dragVisual can only be set before the dragged item begins moving, usually in onDragStart. It can take one of the following values:

  • "item" moves the real Items and creates source-spacer ghosts where their original layout slots need to remain occupied.
  • "preview" leaves the real Items in their original locations and creates one pointer-preview ghost for the complete run.
  • "none" leaves the Items in layout without a pointer-following visual.

This field is set automatically by the placement mode, so unless you are masking custom modifications, there is no need to override it.

DropEffect

dropEffect can change any time before the drop commits. The two options are:

  • "move" commits the item upon drop, in other words actually moves the item to its destination.
  • "none" suppresses that persistent mutation, effectively canceling the drop gesture at the last minute.

All four placement modes default to "move". Application-specific behavior can temporarily select "none" and apply its own logic as needed.

Multi-Item Drag

To drag multiple Items together, set each Item’s selected flag. When an Item starts a drag, SnapSort collects all selected items and treats them as one large item, but still exposes the list of all items. Conceptually, A regular drag of one item is treated as a special case where only one item is selected; this is why fields like items in DragSession are arrays.