bend_puzzle/client/input/gesture-coordinator.js
2026-07-31 12:54:46 +09:00

47 lines
2.2 KiB
JavaScript

(function(root,factory){
const api=factory();
if(typeof module==='object'&&module.exports)module.exports=api;
root.BendGestureCoordinator=api;
})(typeof globalThis!=='undefined'?globalThis:this,function(){
'use strict';
const DEFAULT_PRECEDENCE=Object.freeze({
wheel:10,
pan:20,
pinch:30,
reaction:40,
minimap:50,
draw:60,
dialog:70
});
function createGestureCoordinator(precedence=DEFAULT_PRECEDENCE){
const owners=new Map(),listeners=new Set();let revision=0;
const priority=owner=>Number(precedence[owner])||0;
const snapshot=()=>Object.freeze({revision,owners:Object.freeze(Object.fromEntries(owners))});
const notify=(change)=>{revision++;const state=snapshot();for(const listener of listeners)listener(state,change)};
const claim=(pointerId,owner,{replace=false}={})=>{
if(pointerId==null||!owner)return false;const key=String(pointerId),next=String(owner),current=owners.get(key);
if(current===next)return true;
if(current&&!replace&&priority(next)<=priority(current))return false;
owners.set(key,next);notify(Object.freeze({type:current?'transfer':'claim',pointerId:key,owner:next,previous:current||null}));return true;
};
const release=(pointerId,owner=null,reason='complete')=>{
if(pointerId==null)return false;const key=String(pointerId),current=owners.get(key);
if(!current||owner!=null&&current!==String(owner))return false;
owners.delete(key);notify(Object.freeze({type:'release',pointerId:key,owner:current,reason}));return true;
};
const cancelAll=(reason='cancelled')=>{for(const[key,owner]of[...owners]){owners.delete(key);notify(Object.freeze({type:'release',pointerId:key,owner,reason}))}};
return Object.freeze({
claim,
release,
cancelAll,
owner:pointerId=>pointerId==null?null:owners.get(String(pointerId))||null,
owns:(pointerId,owner)=>owners.get(String(pointerId))===String(owner),
snapshot,
subscribe(listener){if(typeof listener!=='function')throw new TypeError('Listener must be a function');listeners.add(listener);return()=>listeners.delete(listener)}
});
}
return Object.freeze({DEFAULT_PRECEDENCE,createGestureCoordinator});
});