Input.drag ​

Contract 2.0.0 · package sha256 a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e · generated from configurator-runtime.d.ts by services/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.

Method of Input · stable · since 2.0.0 · called as session.input.drag

ts
drag(query: Query, options: DragOptions): () => void;

Makes parts draggable. Lifecycle call: immediate. Each call adds a registration, and a press uses the newest one whose query matches the part; the returned function or signal removes it. Neither a new registration nor a removal ends a drag in progress, which completes under the options it started with. During a drag: 'drag:start', 'drag:move', 'drag:end'; no 'click' follows a drag that moved. A lost pointer capture ends it with reason 'cancelled'. A transaction requested during a drag opens after 'drag:end'. Returns the unregister function. Errors: throws HoloInputError on invalid options. A slow constrain records 'frame-budget'.

Options ​

Through Session:

Option Type Default What it does
commit 'runtime' | 'app' 'runtime' 'runtime': at release the runtime writes the pose to the scene model (one recorded commit, actor 'shopper'), which enters the write queue then, behind any…
constrain (move: DragMove, frame: Frame) => PoseInput | null undefined (the unit follows move.pose) Synchronous.
on 'floor' | Query | ((part: Part) => 'floor' | Query) — The surface the proposed pose lies on; a function picks it per part at press time.
signal AbortSignal undefined Aborting removes this registration; a drag in progress completes.
undo CommitOptions['undo'] 'Move' Undo label of a runtime commit, as CommitOptions.undo ('rebase' erases every earlier step).

Abort (signal) ​

This call is a creator (SignalOption.signal, the kinds of call).

A creator (collection for a new name, materials.create, on, onFrame, beforeCommit, follow, input.drag, labs.lightLinking) given a signal already aborted returns at once, already ended: a Collection or Material reading disposed: true that holds no content and no live name, or a registration that never runs and whose disposer does nothing.

It records no Problem for the abort and throws only what it would throw anyway, as the DOM's addEventListener with an aborted signal adds nothing (spec section 6.5).

Example ​

Imports: values (has, validate, the Holo*Error classes, the pure helpers) from @holostep-configurators/runtime-client, types from @holostep-configurators/runtime-client/contract, /math and /testing names from those entries; boot from the runtime module the root's loadRuntimeAtFloor resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (examples/declaration/11-Input.drag.ts):

ts
import { has } from '@holostep-configurators/runtime-client';
import type { Pose, Ray, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
// The app owns placement: the runtime shows the constrained pose, the store decides on release.
declare const planner: {
  snap(pose: Pose, ray: Ray): Pose;
  canDrop(node: string, pose: Pose): boolean;
};
// movePlaced returns false when the store keeps the node where it is (an equal sync)
declare const store: { movePlaced(node: string, pose: Pose): boolean };
declare const editing: AbortSignal;                        // aborts when editing ends
// commit: 'app' and constrain are experimental; this has() also brings input.revert
if (has(session, 'input.drag.commit.app') && session.supports('input.drag.constrain')) {
  session.input.drag({ role: 'product' }, {
    on: 'floor',
    commit: 'app',                                         // the runtime writes nothing at release
    constrain: (move) => {
      const p = planner.snap(move.pose, move.ray);
      // one rotation: a Pose is not a PoseInput
      return { position: p.position, quaternion: p.quaternion, scale: p.scale };
    },
    signal: editing,
  });
  // the page's signal, not editing: a drag in progress completes after the removal
  session.on('drag:end', ({ part, pose, moved, reason }) => {
    if (!moved || !part.node) return;
    if (reason !== 'released' || !planner.canDrop(part.node, pose)) {
      session.input.revert(part);                          // back to the scene model's pose
      return;
    }
    // the store's sync writes the scene model; the held pose ends. An equal sync commits
    // nothing and releases nothing, so revert when the store keeps the node where it is.
    if (!store.movePlaced(part.node, pose)) session.input.revert(part);
  }, { signal });
}

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: drag names no product
2. A precedent pass: Babylon.js PointerDragBehavior (startDrag, onDragObservable, validateDrag) (row G1); jQuery UI draggable (drag, revert) (row G1)
3. Two apps, or labs pass: 15 apps (ledger: app-04c2774145, app-37356bf09f, app-4762caacdc, app-fc07850558, app-fc306ec3dc; fleet cross-check: app-04c2774145, app-37356bf09f, app-4762caacdc, app-510d750508, app-59c5b5751e, app-b362a81c85, app-bcb7ef21da, app-c1384707fc and 7 more)
4. The cheaper fix first decided per need in Appendix B: rows G1, K47 at tier labs, stable (a member only where no rule, data, asset or app code serves the need)