mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-17 05:32:20 +02:00
82 lines
2.1 KiB
JavaScript
82 lines
2.1 KiB
JavaScript
/** Shared lifecycle guard for frame-style pointer interactions. */
|
|
|
|
export function createDirectManipulationSession({
|
|
name = 'interaction',
|
|
getContext = () => null,
|
|
isContextCurrent = () => true,
|
|
onCancel = () => {},
|
|
} = {}) {
|
|
let active = null;
|
|
let nextId = 1;
|
|
|
|
const pointerMatches = event => {
|
|
if (!active || active.pointerId == null || event?.pointerId == null) return true;
|
|
return active.pointerId === event.pointerId;
|
|
};
|
|
|
|
const releaseCapture = session => {
|
|
if (!session?.captureTarget || session.pointerId == null) return;
|
|
try { session.captureTarget.releasePointerCapture(session.pointerId); } catch {}
|
|
};
|
|
|
|
const contextIsCurrent = () => active && isContextCurrent(active.context, active.data);
|
|
|
|
function begin(event, data = {}, options = {}) {
|
|
if (active) cancel('restarted');
|
|
const pointerId = Number.isFinite(event?.pointerId) ? event.pointerId : null;
|
|
const captureTarget = options.captureTarget || null;
|
|
active = {
|
|
id: nextId++,
|
|
name,
|
|
pointerId,
|
|
captureTarget,
|
|
context: getContext(),
|
|
data,
|
|
};
|
|
if (captureTarget && pointerId != null) {
|
|
try { captureTarget.setPointerCapture(pointerId); } catch {}
|
|
}
|
|
return active;
|
|
}
|
|
|
|
function update(event, callback) {
|
|
if (!active || !pointerMatches(event)) return false;
|
|
if (!contextIsCurrent()) {
|
|
cancel('stale-context');
|
|
return false;
|
|
}
|
|
callback?.(active.data, active);
|
|
return true;
|
|
}
|
|
|
|
function commit(event, callback) {
|
|
if (!active || !pointerMatches(event)) return false;
|
|
if (!contextIsCurrent()) {
|
|
cancel('stale-context');
|
|
return false;
|
|
}
|
|
const completed = active;
|
|
active = null;
|
|
releaseCapture(completed);
|
|
callback?.(completed.data, completed);
|
|
return true;
|
|
}
|
|
|
|
function cancel(reason = 'cancelled') {
|
|
if (!active) return false;
|
|
const cancelled = active;
|
|
active = null;
|
|
releaseCapture(cancelled);
|
|
onCancel(cancelled.data, reason, cancelled);
|
|
return true;
|
|
}
|
|
|
|
return {
|
|
begin,
|
|
update,
|
|
commit,
|
|
cancel,
|
|
get active() { return active; },
|
|
};
|
|
}
|