Skip to content

Drag Lifecycle

MosaicJS implements a deterministic, state-driven drag lifecycle.
Pointer events (pointerdown, pointermove, pointerup) act as inputs that advance the lifecycle, but they are not the lifecycle itself.

All drag behavior is governed by explicit state transitions that are observable, testable, and guaranteed.

Drag State Machine

Diagram generated from src/state.ts and rendered via MermaidJS

Lifecycle Hooks

MosaicJS exposes optional lifecycle hooks that allow external systems to observe drag behavior without mutating engine state.

Hooks are invoked synchronously at specific, guaranteed points in the lifecycle.

HookGuaranteed State
onDragStartPointerDown
onDragMoveDragging
onPreDropDropping
onDropConfirmedMutated
onDropRejectedRollingBack
onDragEndIdle

Example Usage

html
<div id="root">
  <div class="item" data-mosaic-id="a" id="item-a">A</div>
  <div class="item" data-mosaic-id="b" id="item-b">B</div>
</div>

<script>
  const mosaic = new Mosaic({
    root: document.getElementById("root"),
    selectors: { node: ".item" },
    dragLifecycleHooks: {
      onDragEnd: () => {
        console.log("The drag operation has completed.");
      }
    }
  });

  mosaic.initialize();
</script>

Lifecycle hooks are intended for observation and side effects such as analytics, logging, or UI feedback.
Hooks must not manipulate the state machine or trigger additional drag operations.

PointerDown Phase

Triggered by a valid pointerdown event on a draggable node.

ts
pointerDown(e) {
  this.activeNode = node;
  this.mosaic.snapshot = createSnapshot(root);
  this.mosaic.setState(MosaicState.PointerDown);
}

Responsibilities:

  • Identify and lock the active node
  • Capture a snapshot of the current DOM order
  • Transition the engine to PointerDown

Dragging Phase

  • The active node follows pointer movement
  • Potential drop targets are computed internally (v0.2)
  • The lifecycle transitions to and remains in Dragging

This phase may repeat many times during a single drag operation.

Dropping Phase

Triggered by pointerup.

ts
const result = checkConstraints(dragged, target, options);
  • If constraints pass, Mosaic commits the mutation and clears the snapshot
  • If constraints fail, Mosaic restores the snapshot via restoreSnapshot()

In both cases, cleanup is guaranteed and the lifecycle proceeds toward Idle.

Guarantees

MosaicJS guarantees:

  • Deterministic, explicit drag lifecycle transitions
  • Snapshot rollback for all rejected drops
  • Read-only lifecycle context for external observers
  • Guaranteed cleanup and return to Idle
  • External hooks cannot mutate engine state

MosaicJS intentionally does not guarantee:

  • Styling, animation, or visual transitions
  • Framework-specific integration behavior
  • Accessibility semantics
  • Business logic, persistence, or side effects