Standard Callbacks
On this page
SnapSort exports five pure, framework-neutral drop-priority functions from @snap-engine/snapsort/callbacks. Pass one to a destination Container’s getDropPriority callback or call it from an application callback.
import { DROP_REJECT_PRIORITY } from "@snap-engine/snapsort";
import {
prioritizeIntersectingContainer,
prioritizeNearestContainerEdge,
prioritizePointerContainer,
prioritizeTreeDepth,
rejectDrop,
} from "@snap-engine/snapsort/callbacks"; Every helper receives a DropPriorityEvent and returns a priority without
mutating SnapSort, application state, or the DOM.
DropPriorityEvent
interface DropPriorityEvent {
session: DragSession | null;
item: Item;
itemId: ItemId;
itemMetadata: ItemMetadata;
items: Item[];
itemIds: ItemId[];
itemsMetadata: ItemMetadata[];
source: DragLocation | null;
sources: readonly (DragLocation | null)[];
container: Container;
containerMetadata: Record<string, unknown>;
index: number;
staticPriority: number;
pointer: { x: number; y: number };
dragRect: DropPriorityRect;
containerRect: DropPriorityRect;
containerContentRect: DropPriorityRect;
depth: number;
} session
Type: DragSession | null
The active drag handle. It is null when priority is evaluated without a
pointer-driven drag, so narrow it before reading session state.
if (event.session?.dropEffect === "none") return DROP_REJECT_PRIORITY; item
Type: Item
The primary Item being evaluated. For a multi-item drag it is the first Item
in the ordered run and equals items[0].
const sourceItem = event.item; itemId
Type: ItemId
The stable identity of item. Prefer it to object identity when comparing
against application state.
if (blockedIds.has(event.itemId)) return DROP_REJECT_PRIORITY; itemMetadata
Type: ItemMetadata
Read-only application metadata for item.
const kind = event.itemMetadata.kind; items
Type: Item[]
The complete dragged run in stable order. A single-item drag has one entry.
if (event.items.length > 10) return DROP_REJECT_PRIORITY; itemIds
Type: ItemId[]
Identities parallel to items: itemIds[i] identifies items[i].
const includesLockedItem = event.itemIds.some((id) => lockedIds.has(id)); itemsMetadata
Type: ItemMetadata[]
Application metadata parallel to items.
const allTasks = event.itemsMetadata.every((data) => data.kind === "task"); source
Type: DragLocation | null
The primary Item’s source location, or null when it has no source Container.
It equals sources[0] when both are present.
const isSameContainer = event.source?.container === event.container; sources
Type: readonly (DragLocation | null)[]
Source locations parallel to items. An entry can be null for an Item that
does not currently belong to a Container.
const crossesContainers = event.sources.some(
(source) => source && source.container !== event.container,
); container
Type: Container
The candidate destination whose direct positions will receive the returned priority.
if (event.container.locked) return rejectDrop(event); containerMetadata
Type: Record<string, unknown>
Read-only application metadata for the candidate destination.
const accepts = event.containerMetadata.accepts; index
Type: number
The first candidate index generated for this Container. SnapSort calls policy once per candidate Container per resolution, so this value is representative only. Do not use it to allow or reject individual slots.
console.log("First candidate index", event.index); staticPriority
Type: number
The Container’s configured dropPriority before getDropPriority overrides
it. Return this value when an application condition does not apply but the
configured priority should be preserved.
return matchesRule ? event.depth + 1 : event.staticPriority; pointer
Type: { x: number; y: number }
Frozen world-space coordinates for the pointer used by this resolution.
const pointerY = event.pointer.y; dragRect
Type: DropPriorityRect
The primary dragged Item’s virtual world-space rectangle. Its leading edge preserves the pointer-to-Item offset captured at drag start.
const leadingX = event.dragRect.x; containerRect
Type: DropPriorityRect
The destination Container’s frozen world-space border box. All five standard callbacks use this rectangle when they need destination geometry.
const right = event.containerRect.x + event.containerRect.width; containerContentRect
Type: DropPriorityRect
The destination Container’s frozen world-space content box. Standard helpers do not use it, but custom policies can use it when borders and padding should not count as the active region.
return pointInside(event.pointer, event.containerContentRect)
? 1
: event.staticPriority; depth
Type: number
The candidate Container’s zero-based depth within its independent SnapSort
tree. prioritizeTreeDepth uses it to give deeper matching Containers larger
scores.
const nestedPriority = event.depth + 1; DropPriorityRect
Type: { x: number; y: number; width: number; height: number }
A frozen world-space rectangle captured for the current target resolution.
Priority Return Values
DROP_REJECT_PRIORITY
Type: -1
The one valid rejection result. A finite nonnegative number makes the
Container eligible and ranks it; undefined preserves staticPriority.
Other negative or nonfinite results throw.
return acceptsItem(event) ? 1 : DROP_REJECT_PRIORITY; A callback result is an absolute override, not a value added to dropPriority. Returning a nonnegative number can therefore override a static -1; returning undefined preserves that static rejection.
Functions
rejectDrop
function rejectDrop(event: DropPriorityEvent): typeof DROP_REJECT_PRIORITY; Always returns DROP_REJECT_PRIORITY (-1). Use it on a structural Container
that may own nested destinations but must not directly accept Items.
const config = {
callbacks: { getDropPriority: rejectDrop },
} satisfies ContainerConfig; The rejection applies to positions owned directly by that Container. Nested Containers evaluate their own policies.
prioritizePointerContainer
function prioritizePointerContainer(
event: DropPriorityEvent,
): number | undefined; Returns 1 when event.pointer is inside or on the edge of event.containerRect; otherwise returns undefined, preserving the
Container’s configured priority.
const config = {
callbacks: { getDropPriority: prioritizePointerContainer },
} satisfies ContainerConfig; Use it when the pointer itself should select among overlapping or differently sized destinations.
prioritizeIntersectingContainer
function prioritizeIntersectingContainer(
event: DropPriorityEvent,
): number | undefined; Returns 1 when event.dragRect intersects or touches event.containerRect; otherwise returns undefined. The virtual dragged
rectangle preserves the initial grab offset.
const config = {
callbacks: { getDropPriority: prioritizeIntersectingContainer },
} satisfies ContainerConfig; Use it for bins, canvases, and drop zones that should be preferred when the dragged Item—not only the pointer—enters them.
prioritizeNearestContainerEdge
function prioritizeNearestContainerEdge(event: DropPriorityEvent): number; Returns 1 / (1 + distance), where distance is the Euclidean distance from
the pointer to the nearest point on containerRect. A pointer inside the box
scores 1; scores approach 0 as distance increases.
const config = {
callbacks: { getDropPriority: prioritizeNearestContainerEdge },
} satisfies ContainerConfig; This helper always returns a number, so it replaces staticPriority for every
candidate instead of preserving it outside the Container.
prioritizeTreeDepth
function prioritizeTreeDepth(event: DropPriorityEvent): number; Builds this virtual probe for vertically rendered trees:
const virtualProbe = {
x: event.dragRect.x,
y: event.pointer.y,
}; Returns event.depth + 1 when the probe is inside containerRect; otherwise
returns event.staticPriority. Using the virtual Item’s leading X edge
preserves the original grab offset while pointer Y selects the current row.
const config = {
callbacks: { getDropPriority: prioritizeTreeDepth },
} satisfies ContainerConfig; This helper is intentionally vertical. A horizontal tree should use an application callback with the axes exchanged.
Composing Helpers
Standard callbacks are ordinary functions and can be combined with metadata or application geometry.
function prioritizeMatchingGroup(event: DropPriorityEvent) {
const matches =
event.source?.containerMetadata.group === event.containerMetadata.group;
return matches ? prioritizeIntersectingContainer(event) : rejectDrop(event);
} Appendix
Resolution Timing
During pointer-driven target resolution SnapSort:
- Generates positions and groups them by direct Container.
- Reads
dropPriorityand callsgetDropPriorityonce per Container. - Removes effective
-1results and keeps Containers tied at the highest nonnegative priority. - Lets the active placement mode rank positions owned by those Containers.
Callbacks run synchronously and repeatedly as the pointer moves, so keep them
deterministic and inexpensive. They choose Container eligibility and
preference, not an individual slot. Programmatic moveItem calls bypass these
policies.
For the overall target-resolution model, see Drags and Ghosts. See the Svelte Container or React Container reference for callback ownership and configuration.