Contract 2.0.0 · package sha256
a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e· generated fromexamples/section-5/5.07-input-and-drag.tsbyservices/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.
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.
import { has } from '@holostep-configurators/runtime-client';
import type { DragMove, DragOptions, Part, Point3, Pose, PoseInput, ScreenPoint, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const armedIds: readonly string[];
declare const ruler: Part; // a 'lines' item of a helper collection
declare const machine: { step(move: DragMove): { pose: PoseInput; rulerPoints: readonly Point3[] } };
// moveGlide returns false when the store keeps the node where it is (an equal sync)
declare const store: { canDrop(part: Part, pose: Pose): boolean; moveGlide(node: string, pose: Pose): boolean };
declare const longPress: { onFire(cb: (e: { pointerId: number; at: ScreenPoint }, part: Part) => void): void };
declare function isWallUnit(part: Part): boolean;
declare function wallKeyOf(part: Part): string;
const dragOptions: DragOptions = {
on: (part) => (isWallUnit(part) ? { collection: 'room', key: wallKeyOf(part) } : 'floor'),
commit: 'app',
constrain: (move, frame) => {
// App code: collision, snap, spring. It aims with move.ray, never move.pose after a re-yaw
// (the grab offset doesn't turn); the pressed part comes from its own pointerdown input.pick.
const step = machine.step(move);
frame.lines(ruler, step.rulerPoints); // shows in this frame
return step.pose; // { position, quaternion, scale }: one rotation
},
signal,
};
let stopDrag = session.input.drag(armedIds.map((node) => ({ node })), dragOptions); // arming changes: stopDrag(), register again
session.on('drag:end', ({ part, pose, moved, reason }) => {
if (!moved || reason !== 'released' || !store.canDrop(part, pose)) {
if (has(session, 'input.revert')) session.input.revert(part);
return;
}
// the store sync changes the node's pose and ends the held pose; an equal sync releases nothing,
// unless it superseded the drop's pending sync (a store undo): then it shows the store's pose
if (!store.moveGlide(part.node!, pose) && has(session, 'input.revert')) session.input.revert(part);
}, { signal });
longPress.onFire((e, part) => {
if (!has(session, 'input.startDrag') || !part.node) return;
stopDrag(); // the pressed cabinet may be unarmed: register it first
stopDrag = session.input.drag([...armedIds, part.node].map((node) => ({ node })), dragOptions);
session.input.startDrag(part, { pointerId: e.pointerId, at: e.at });
});
Back to the examples · llms.txt