(function(root,factory){ const api=factory(root.BendFrameScheduler); if(typeof module==='object'&&module.exports)module.exports=factory(require('./frame-scheduler.js')); root.BendDragScheduler=api; })(typeof globalThis!=='undefined'?globalThis:this,function(FrameScheduler){ 'use strict'; const STATES=Object.freeze(['idle','armed','running','draining','settling','cancelled']); function createDragScheduler(options={}){ if(!FrameScheduler?.createFrameScheduler)throw new TypeError('BendFrameScheduler is required'); if(typeof options.onFrame!=='function')throw new TypeError('Drag onFrame callback is required'); const maxSamples=Math.max(2,Number(options.maxSamples)||12); const trim=typeof options.trim==='function'?options.trim:samples=>samples.splice(0,Math.max(0,samples.length-maxSamples)); let state='idle',pointerId=null,latest=null,logical=[],revision=0,visualRevision=0,logicalRevision=0; const frame=FrameScheduler.createFrameScheduler({ interval:options.interval, tolerance:options.tolerance, watchdogDelay:options.watchdogDelay, requestFrame:options.requestFrame, cancelFrame:options.cancelFrame, setDelay:options.setDelay, clearDelay:options.clearDelay, now:options.now, onWatchdog:options.onWatchdog, commit:(_signal,timestamp)=>options.onFrame(api,timestamp) }); const assertState=next=>{if(!STATES.includes(next))throw new TypeError(`Unknown drag state: ${next}`);state=next}; const clearData=()=>{pointerId=null;latest=null;logical.length=0;revision=0;visualRevision=0;logicalRevision=0}; const arm=id=>{ if(id==null)throw new TypeError('Drag pointerId is required'); frame.cancel({resetCadence:true});clearData();pointerId=id;assertState('armed');return api; }; const push=(sample,{schedule=true}={})=>{ if(!sample||sample.pointerId==null)return false; if(state==='idle'||state==='cancelled'||state==='settling')arm(sample.pointerId); if(sample.pointerId!==pointerId||state==='draining')return false; latest=sample;logical.push(sample);trim(logical,maxSamples);revision++; if(state==='armed')assertState('running'); if(schedule)frame.push(revision); return revision; }; const requestFrame=()=>{if(latest&&state!=='idle'&&state!=='cancelled')frame.push(revision)}; const beginDrain=sample=>{ if(sample)push(sample,{schedule:false}); if(state==='idle'||state==='cancelled')return false; frame.cancel();assertState('draining');return true; }; const beginSettling=()=>{if(state==='draining'||state==='running'||state==='armed')assertState('settling');frame.cancel();return state==='settling'}; const settle=()=>{frame.cancel({resetCadence:true});clearData();assertState('idle')}; const cancel=()=>{frame.cancel({resetCadence:true});clearData();assertState('cancelled')}; const takeLogical=max=>{ const count=Math.max(0,Math.min(logical.length,Number(max)||1)); return logical.splice(0,count); }; const drainLogical=()=>logical.splice(0); const markVisual=()=>{visualRevision=revision}; const markLogical=()=>{if(!logical.length)logicalRevision=revision}; const inspect=()=>Object.freeze({ state,pointerId,latest,logicalCount:logical.length,revision,visualRevision,logicalRevision, freshVisual:visualRevision!==revision,freshLogical:logicalRevision!==revision }); const api=Object.freeze({ arm,push,requestFrame,beginDrain,beginSettling,settle,cancel,takeLogical,drainLogical, latest:()=>latest, hasLogical:()=>logical.length>0, markVisual,markLogical,inspect }); return api; } return Object.freeze({STATES,createDragScheduler}); });