@supermousejs/core 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1,5 +1,10 @@
1
- (function(a,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(a=typeof globalThis<"u"?globalThis:a||self,l(a.SupermouseCore={}))})(this,(function(a){"use strict";const l="2.3.0";function m(r,t,e){return r+(t-r)*e}function p(r,t,e,i){return m(r,t,1-Math.exp(-e*i))}class f{constructor(t,e,i,n){this.state=t,this.options=e,this.getHoverSelector=i,this.onEnableChange=n,this.dataPrefix=this.options.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;getHoverSelector;onEnableChange;mediaQueryList;mediaQueryHandler;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;isEnabled=!0;abortController=new AbortController;checkDeviceCapability(){this.options.autoDisableOnMobile&&(this.mediaQueryList=window.matchMedia("(pointer: fine)"),this.updateEnabledState(this.mediaQueryList.matches),this.mediaQueryHandler=t=>{this.updateEnabledState(t.matches)},this.mediaQueryList.addEventListener("change",this.mediaQueryHandler,{signal:this.abortController.signal}))}checkMotionPreference(){this.motionQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.state.reducedMotion=this.motionQuery.matches,this.motionQuery.addEventListener("change",t=>{this.state.reducedMotion=t.matches},{signal:this.abortController.signal})}updateEnabledState(t){this.isEnabled=t,this.onEnableChange(t)}parseDOMInteraction(t){if(this.options.resolveInteraction){this.state.interaction=this.options.resolveInteraction(t)||{};return}const e={};if(this.options.rules)for(const[s,o]of Object.entries(this.options.rules))t.matches(s)&&Object.assign(e,o);const i=this.normalizedDataPrefix,n=this.dataPrefix.length;for(const s in t.dataset){if(!s.toLowerCase().startsWith(i))continue;const o=s.slice(n);if(!o)continue;const h=t.dataset[s];e[o[0].toLowerCase()+o.slice(1)]=h===""?!0:h}this.state.interaction=e}handleMove(t){if(!this.isEnabled||this.options.autoDisableOnMobile&&t.pointerType==="touch"&&!this.options.enableTouch)return;let e=t.clientX,i=t.clientY;if(this.options.container&&this.options.container!==document.body){const n=this.options.container.getBoundingClientRect();e-=n.left,i-=n.top}this.state.pointer.x=e,this.state.pointer.y=i,this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=e,this.state.target.y=this.state.smooth.y=i)}handleDown(){this.isEnabled&&(this.state.isDown=!0)}handleUp(){this.isEnabled&&(this.state.isDown=!1)}handleMouseOver(t){if(!this.isEnabled)return;const e=t.target;if(e.closest(`[${this.ignoreAttribute}]`)){this.state.isNative=!0;return}const i=this.getHoverSelector(),n=e.closest(i);n&&(this.state.isHover=!0,this.state.hoverTarget=n,this.parseDOMInteraction(this.state.hoverTarget));const s=this.options.ignoreOnNative;if(s&&s!==null){const o=s==="auto"||s==="tag",h=s==="auto"||s==="css";let c=!1;if(o){const d=e.localName;(d==="input"||d==="textarea"||d==="select"||e.isContentEditable)&&(c=!0)}if(!c&&h){const d=window.getComputedStyle(e).cursor;["default","auto","pointer","none","inherit"].includes(d)||(c=!0)}c&&(this.state.isNative=!0)}}handleMouseOut(t){if(!this.isEnabled)return;const e=t.target;(e===this.state.hoverTarget||e.contains(this.state.hoverTarget))&&(!t.relatedTarget||!this.state.hoverTarget?.contains(t.relatedTarget))&&(this.state.isHover=!1,this.state.hoverTarget=null,this.state.interaction={}),this.state.isNative&&(this.state.isNative=!1)}handleWindowLeave(){this.options.hideOnLeave&&(this.state.hasReceivedInput=!1,this.state.pointer={...u})}clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1,this.state.interaction={}}bindEvents(){const{signal:t}=this.abortController;window.addEventListener("pointermove",this.handleMove.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerdown",this.handleDown.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerup",this.handleUp.bind(this),{signal:t}),document.addEventListener("mouseover",this.handleMouseOver.bind(this),{signal:t}),document.addEventListener("mouseout",this.handleMouseOut.bind(this),{signal:t}),document.addEventListener("mouseleave",this.handleWindowLeave.bind(this),{signal:t})}destroy(){this.abortController.abort()}}let b=0;class g{constructor(t=document.body,e){if(this.container=t,this.hideNativeCursor=e,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const i=b++;this.id=`supermouse-style-${i}`,this.scopeClass=`supermouse-scope-${i}`;const n=t===document.body;if(this.element=document.createElement("div"),Object.assign(this.element.style,{position:n?"fixed":"absolute",inset:"0px",pointerEvents:"none",zIndex:"9999",opacity:"1",transition:"opacity 0.15s ease"}),!n){const s=window.getComputedStyle(t);this.originalContainerPosition=s.position,s.position==="static"&&(t.style.position="relative")}this.originalContainerCursor=t.style.cursor,t.appendChild(this.element),this.styleTag=document.createElement("style"),this.styleTag.id=this.id,document.head.appendChild(this.styleTag),this.container.classList.add(this.scopeClass),this.hideNativeCursor&&this.setNativeCursor("none")}container;hideNativeCursor;element;styleTag;id;scopeClass;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";selectors=new Set(["a","button","input","textarea","select",'[role="button"]',"[tabindex]"]);addSelector(t){this.selectors.add(t),this.hideNativeCursor&&this.updateCursorCSS()}setVisibility(t){this.element.style.opacity=t?"1":"0"}setNativeCursor(t){!this.hideNativeCursor&&t==="none"||t!==this.currentCursorState&&(this.currentCursorState=t,t==="none"?(this.container.style.cursor="none",this.updateCursorCSS()):(this.container.style.cursor="",this.styleTag.innerText=""))}updateCursorCSS(){const t=Array.from(this.selectors);if(t.length===0){this.styleTag.innerText="";return}const e=t.map(i=>`.${this.scopeClass} ${i}`).join(", ");this.styleTag.innerText=`
2
- ${e} {
3
- cursor: none !important;
4
- }
5
- `}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor=this.originalContainerCursor,this.container.classList.remove(this.scopeClass),this.container!==document.body&&this.originalContainerPosition==="static"&&(this.container.style.position="")}}const u={x:-100,y:-100},v=["a","button","input","textarea","[data-hover]","[data-cursor]"];class y{static version=l;version=l;state;options;plugins=[];stage;input;rafId=0;lastTime=0;isRunning=!1;hoverSelectors;constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,ignoreOnNative:"auto",hideCursor:!0,hideOnLeave:!0,autoStart:!0,container:document.body,dataPrefix:"supermouse",...t},this.state={pointer:{x:-100,y:-100},target:{x:-100,y:-100},smooth:{x:-100,y:-100},velocity:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,forcedCursor:null,hoverTarget:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{}},this.options.hoverSelectors?this.hoverSelectors=new Set(this.options.hoverSelectors):this.hoverSelectors=new Set(v),this.stage=new g(this.options.container,!!this.options.hideCursor),this.hoverSelectors.forEach(e=>this.stage.addSelector(e)),this.input=new f(this.state,this.options,()=>Array.from(this.hoverSelectors).join(", "),e=>{e||this.reset(!0)}),this.options.plugins&&this.options.plugins.forEach(e=>this.use(e)),this.init()}getPlugin(t){return this.plugins.find(e=>e.name===t)}get isEnabled(){return this.input.isEnabled}enablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled===!1&&(e.isEnabled=!0,e.onEnable?.(this))}disablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled!==!1&&(e.isEnabled=!1,e.onDisable?.(this))}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}registerHoverTarget(t){this.hoverSelectors.has(t)||(this.hoverSelectors.add(t),this.stage.addSelector(t))}get container(){return this.stage.element}setNativeCursor(t){this.state.forcedCursor=t==="auto"?null:t==="hide"?"none":"auto"}init(){this.options.autoStart&&this.startLoop()}enable(){this.input.isEnabled=!0,this.stage.setNativeCursor("none")}disable(){this.input.isEnabled=!1,this.stage.setNativeCursor("auto"),this.reset(!0)}use(t){if(this.plugins.some(i=>i.name===t.name))return console.warn(`[Supermouse] Plugin "${t.name}" already installed.`),this;t.isEnabled===void 0&&(t.isEnabled=!0);try{t.install?.(this)}catch(i){return console.error(`[Supermouse] Failed to install plugin '${t.name}'.`,i),this}return this.plugins.push(t),this.plugins.sort((i,n)=>(i.priority||0)-(n.priority||0)),this}reset(t=!1){this.state.pointer={...u},this.state.target={...u},this.state.smooth={...u},this.state.velocity={x:0,y:0},this.state.angle=0,t&&(this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={})}startLoop(){this.isRunning||(this.isRunning=!0,this.lastTime=performance.now(),this.tick(this.lastTime))}start(){this.startLoop()}step(t){this.tick(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch(i){console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,i);const n=this.plugins.indexOf(t);n>-1&&this.plugins.splice(n,1),t.isEnabled=!1;try{t.destroy?.(this),t.onDisable?.(this)}catch(s){console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`,s)}}}tick=t=>{const e=t-this.lastTime,i=Math.min(e/1e3,.1);this.lastTime=t,this.state.hoverTarget&&!this.state.hoverTarget.isConnected&&this.input.clearHover();const n=this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput;if(this.stage.setVisibility(n),this.input.isEnabled&&this.options.hideCursor){let s="auto";this.state.forcedCursor!==null?s=this.state.forcedCursor:s=this.state.isNative||!this.state.hasReceivedInput?"auto":"none",this.stage.setNativeCursor(s)}this.input.isEnabled&&this.state.hasReceivedInput?(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y):this.state.target={...u};for(let s=0;s<this.plugins.length;s++)this.runPluginSafe(this.plugins[s],e);if(this.input.isEnabled){const s=this.state.reducedMotion?1e3:1/this.options.smoothness*2;this.state.smooth.x=p(this.state.smooth.x,this.state.target.x,s,i),this.state.smooth.y=p(this.state.smooth.y,this.state.target.y,s,i),this.state.velocity.x=this.state.target.x-this.state.smooth.x,this.state.velocity.y=this.state.target.y-this.state.smooth.y;const{x:o,y:h}=this.state.velocity;(Math.abs(o)>.1||Math.abs(h)>.1)&&(this.state.angle=Math.atan2(h,o)*(180/Math.PI))}this.isRunning&&(this.rafId=requestAnimationFrame(this.tick))};destroy(){this.isRunning=!1,cancelAnimationFrame(this.rafId),this.input.destroy(),this.stage.destroy(),this.plugins.forEach(t=>t.destroy?.(this)),this.plugins=[]}}a.DEFAULT_HOVER_SELECTORS=v,a.Input=f,a.Stage=g,a.Supermouse=y,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(r,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(r=typeof globalThis<"u"?globalThis:r||self,l(r.SupermouseCore={}))})(this,(function(r){"use strict";const l="2.4.1";function v(a,t,e){return a+(t-a)*e}function c(a,t,e,s){return v(a,t,1-Math.exp(-e*s))}const u={x:-100,y:-100},b=new Set(["input","textarea","select"]),y=new Set(["default","auto","pointer","none","inherit","grab","grabbing"]),d=["a","button","input","textarea","[data-hover]","[data-cursor]"];class p{constructor(t,e,s,i){this.state=t,this.options=e,this.getHoverSelector=s,this.onEnableChange=i,this.dataPrefix=this.options.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.ruleEntries=this.options.rules?Object.entries(this.options.rules):[],this.checkDeviceCapability(),this.checkMotionPreference(),this.setupContainerRectTracking(),this.bindEvents()}state;options;getHoverSelector;onEnableChange;mediaQueryList;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;abortController=new AbortController;nativeTarget=null;hasSeenPointer=!1;isEnabled=!0;containerRect=null;resizeObserver;matchedRules=[];lastParsedTarget=null;ruleEntries;currentTarget=null;checkDeviceCapability(){this.options.autoDisableOnMobile&&(this.mediaQueryList=window.matchMedia("(pointer: fine)"),this.updateEnabledState(this.mediaQueryList.matches),this.mediaQueryList.addEventListener("change",t=>this.updateEnabledState(t.matches),{signal:this.abortController.signal}))}checkMotionPreference(){this.motionQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.state.reducedMotion=this.motionQuery.matches,this.motionQuery.addEventListener("change",t=>{this.state.reducedMotion=t.matches},{signal:this.abortController.signal})}updateEnabledState(t){this.isEnabled=t,this.onEnableChange(t)}setupContainerRectTracking(){const t=this.options.container;if(!t||t===document.body)return;const e=()=>{this.containerRect=t.getBoundingClientRect()};e(),window.addEventListener("resize",e,{signal:this.abortController.signal}),window.addEventListener("scroll",e,{passive:!0,signal:this.abortController.signal}),typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(e),this.resizeObserver.observe(t))}parseDOMInteraction(t){if(t!==this.lastParsedTarget){this.lastParsedTarget=t,this.matchedRules=[];for(const[i,n]of this.ruleEntries)this.matchesSelector(t,i)&&this.matchedRules.push({selector:i,rules:n})}const e={};for(const{rules:i}of this.matchedRules)try{const n=typeof i=="function"?i(t):i;if(!n||typeof n!="object")continue;for(const[o,h]of Object.entries(n))e[o]=typeof h=="function"?h(t):h}catch(n){console.error("[Supermouse] Rule threw during evaluation:",n)}const s=this.normalizedDataPrefix;for(const i in t.dataset){if(!i.toLowerCase().startsWith(s))continue;const n=i.slice(s.length);if(!n)continue;const o=t.dataset[i];e[n[0].toLowerCase()+n.slice(1)]=o===""?!0:o}this.state.interaction=e}matchesSelector(t,e){try{if(!/\s/.test(e.trim()))return t.matches(e)}catch{}const s=e.trim().split(/\s+/);if(s.length<2)return!1;const i=s.pop(),n=s.join(" ");if(!i||!n)return!1;try{return t.matches(i)&&!!t.closest(n)}catch{return!1}}isOutsideContainer(t){const{container:e}=this.options;return!!e&&e!==document.body&&!e.contains(t)}resolveComputedCursor(t){return window.getComputedStyle(t).cursor}handleMove=t=>{if(this.options.autoDisableOnMobile&&t.pointerType==="touch"&&!this.options.enableTouch)return;let e=t.clientX,s=t.clientY;const i=this.options.container;i&&this.containerRect&&i!==document.body&&(e-=this.containerRect.left,s-=this.containerRect.top),this.state.pointer.x=e,this.state.pointer.y=s,this.hasSeenPointer=!0,this.isEnabled&&(this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=e,this.state.target.y=this.state.smooth.y=s))};handleDown=()=>{this.isEnabled&&(this.state.isDown=!0)};handleUp=()=>{this.isEnabled&&(this.state.isDown=!1)};handleMouseOver=t=>{if(!this.isEnabled)return;const e=t.target;if(this.isOutsideContainer(e))return;if(this.state.cursorMode==="auto"&&e.closest(`[${this.ignoreAttribute}]`)){this.state.isHover=!1,this.state.hoverTarget=null,this.state.interaction={},this.currentTarget=null,this.lastParsedTarget=null,this.matchedRules=[],this.state.isNative=!0,this.nativeTarget=e;return}if(this.state.isNative=!1,this.nativeTarget=null,this.currentTarget=e,this.parseDOMInteraction(e),this.state.cursorMode!=="auto"){const o=e.closest(this.getHoverSelector());o&&(this.state.isHover=!0,this.state.hoverTarget=o);return}const s=e.closest(this.getHoverSelector());s&&(this.state.isHover=!0,this.state.hoverTarget=s);const i=b.has(e.localName)||e.isContentEditable,n=!y.has(this.resolveComputedCursor(e));(i||n)&&(this.state.isNative=!0,this.nativeTarget=e)};handleMouseOut=t=>{if(!this.isEnabled)return;const e=t.target,s=t.relatedTarget;this.isOutsideContainer(e)||((e===this.state.hoverTarget||e.contains(this.state.hoverTarget))&&(!s||!this.state.hoverTarget?.contains(s))&&(this.state.isHover=!1,this.state.hoverTarget=null),this.nativeTarget&&(e===this.nativeTarget||e.contains(this.nativeTarget))&&(!s||!this.nativeTarget.contains(s))&&(this.state.isNative=!1,this.nativeTarget=null),e===this.currentTarget&&(this.currentTarget=null,this.lastParsedTarget=null,this.matchedRules=[]))};handleDocumentMouseOut=t=>{this.isEnabled&&t.relatedTarget===null&&this.options.hideOnLeave&&this.handleWindowLeave()};handleWindowLeave=()=>{this.options.hideOnLeave&&(this.state.hasReceivedInput=!1,this.state.pointer={...u})};clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1,this.nativeTarget=null,this.state.interaction={},this.currentTarget=null,this.lastParsedTarget=null,this.matchedRules=[]}getCurrentTarget(){return this.currentTarget}bindEvents(){const{signal:t}=this.abortController;window.addEventListener("pointermove",this.handleMove,{passive:!0,signal:t}),window.addEventListener("pointerdown",this.handleDown,{passive:!0,signal:t}),window.addEventListener("pointerup",this.handleUp,{signal:t});const s=!this.options.container||this.options.container===document.body?document:this.options.container;s.addEventListener("mouseover",this.handleMouseOver,{signal:t}),s.addEventListener("mouseout",this.handleMouseOut,{signal:t}),document.addEventListener("mouseout",this.handleDocumentMouseOut,{signal:t})}destroy(){this.abortController.abort(),this.resizeObserver?.disconnect()}}let C=0;class g{constructor(t=document.body,e=9999){if(this.container=t,this.zIndex=e,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);t.isConnected||console.warn("[Supermouse] container is not attached to the document — stage sizing/positioning will be wrong until it is.");const s=C++;this.id=`supermouse-style-${s}`,this.scopeClass=`supermouse-scope-${s}`,this.hideClass=`supermouse-hide-${s}`;const i=t===document.body;if(this.element=document.createElement("div"),Object.assign(this.element.style,{position:i?"fixed":"absolute",inset:"0px",pointerEvents:"none",zIndex:String(this.zIndex),opacity:"1",transition:"opacity 0.15s ease"}),!i){const n=window.getComputedStyle(t);this.originalContainerPosition=n.position,n.position==="static"&&(t.style.position="relative")}this.originalContainerCursor=t.style.cursor,t.appendChild(this.element),this.styleTag=document.createElement("style"),this.styleTag.id=this.id,document.head.appendChild(this.styleTag),this.container.classList.add("supermouse-scope",this.scopeClass),this.updateCursorCSS()}container;zIndex;element;styleTag;id;scopeClass;hideClass;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";selectors=new Set(["a","button","input","textarea","select",'[role="button"]',"[tabindex]"]);addSelectors(t){let e=!1;for(const s of t)s.split(",").forEach(i=>{const n=i.trim();n&&!this.selectors.has(n)&&(this.selectors.add(n),e=!0)});e&&this.updateCursorCSS()}addSelector(t){this.addSelectors([t])}setVisibility(t){this.element.style.opacity=t?"1":"0"}setNativeCursor(t){t!==this.currentCursorState&&(this.currentCursorState=t,this.container.classList.toggle(this.hideClass,t==="none"),this.container.style.cursor=t==="none"?"none":this.originalContainerCursor)}updateCursorCSS(){const t=Array.from(this.selectors);if(t.length===0){this.styleTag.innerText="";return}const e=`:not(.${this.scopeClass} .supermouse-scope):not(.${this.scopeClass} .supermouse-scope *)`,s=h=>`.${this.scopeClass}.${this.hideClass} ${h}${e} { cursor: none !important; }`,i=t.map(s).join(`
2
+ `),n=`.${this.scopeClass}.${this.hideClass} *${e} { cursor: none !important; }`,o=`.${this.scopeClass}.${this.hideClass} { cursor: none !important; }`;this.styleTag.innerText=`
3
+ ${o}
4
+ ${n}
5
+ ${i}
6
+ ${s("label")}
7
+ ${s("select")}
8
+ ${s('input[type="range"]::-webkit-slider-thumb')}
9
+ ${s('input[type="range"]::-moz-range-thumb')}
10
+ `}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor=this.originalContainerCursor,this.container.classList.remove("supermouse-scope",this.scopeClass,this.hideClass),this.container!==document.body&&this.originalContainerPosition==="static"&&(this.container.style.position="")}}class S{static version=l;version=l;state;options;plugins=[];_stage;input;rafId=0;lastTime=0;isRunning=!1;isSuspended=!1;visibilityAbortController=new AbortController;hoverSelectors;hoverSelectorString;crashedPlugins=[];constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,cursor:"auto",hideOnLeave:!0,autoStart:!0,container:document.body,dataPrefix:"supermouse",zIndex:9999,...t},this.state={pointer:{...u},target:{...u},smooth:{...u},velocity:{x:0,y:0},displacement:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,cursorMode:this.options.cursor,hoverTarget:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{}},this.hoverSelectors=new Set(this.options.hoverSelectors??d),this.hoverSelectorString=Array.from(this.hoverSelectors).join(", "),this._stage=new g(this.options.container,this.options.zIndex),this._stage.addSelectors(this.hoverSelectors),this.input=new p(this.state,this.options,()=>this.hoverSelectorString,e=>{e||this.reset(!0)}),this.options.plugins?.forEach(e=>this.use(e)),this.bindVisibilityHandling(),this.init()}getPlugin(t){return this.plugins.find(e=>e.name===t)}get isEnabled(){return this.input.isEnabled}enablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled===!1&&(e.isEnabled=!0,e.element&&(e.element.style.display=""),e.onEnable?.(this))}disablePlugin(t){const e=this.getPlugin(t);if(e&&e.isEnabled!==!1){e.isEnabled=!1;const s=()=>{e.element&&(e.element.style.display="none"),e.onDisable?.(this)},i=e.onBeforeDisable?.(this);i&&typeof i.then=="function"?Promise.resolve(i).then(s).catch(n=>{console.error(`[Supermouse] Plugin '${e.name}' onBeforeDisable threw:`,n),s()}):s()}}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}registerHoverTarget(t){this.hoverSelectors.has(t)||(this.hoverSelectors.add(t),this.hoverSelectorString=Array.from(this.hoverSelectors).join(", "),this._stage.addSelector(t))}get container(){return this.options.container}get stage(){return this._stage.element}setCursor(t){this.state.cursorMode=t}init(){this.options.autoStart&&this.startLoop()}enable(){this.input.isEnabled=!0,this.input.hasSeenPointer&&(this.state.target.x=this.state.smooth.x=this.state.pointer.x,this.state.target.y=this.state.smooth.y=this.state.pointer.y,this.resetMotion(),this.state.hasReceivedInput=!0),this._stage.setNativeCursor(this.resolveCursorState())}disable(){this.input.isEnabled=!1,this._stage.setNativeCursor("auto"),this._stage.setVisibility(!1),this.reset(!0)}suspend(){this.input.isEnabled&&(this.isSuspended=!0,this.input.isEnabled=!1,this.input.clearHover(),this._stage.setVisibility(!1))}resume(){if(this.isSuspended){this.isSuspended=!1,this.input.isEnabled=!0,this.state.hasReceivedInput&&(this.state.target.x=this.state.smooth.x=this.state.pointer.x,this.state.target.y=this.state.smooth.y=this.state.pointer.y,this.resetMotion());for(let t=this.plugins.length-1;t>=0;t--)this.runPluginSafe(this.plugins[t],0);this._stage.setVisibility(!0)}}use(t){if(this.plugins.some(e=>e.name===t.name))return console.warn(`[Supermouse] Plugin "${t.name}" already installed.`),this;t.isEnabled??=!0;try{t.install?.(this)}catch(e){return console.error(`[Supermouse] Failed to install plugin '${t.name}'.`,e),this}return this.plugins.push(t),this.plugins.sort((e,s)=>(e.priority??0)-(s.priority??0)),this}reset(t=!1){this.state.target={...u},this.state.smooth={...u},this.resetMotion(),this.state.angle=0,t&&(this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={})}startLoop(){this.isRunning||(this.isRunning=!0,!document.hidden&&(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))}start(){this.startLoop()}step(t){this.update(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch(s){console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,s),t.isEnabled=!1,this.crashedPlugins.push(t)}}cleanupCrashedPlugins(){if(this.crashedPlugins.length!==0){for(const t of this.crashedPlugins){const e=this.plugins.indexOf(t);e>-1&&this.plugins.splice(e,1);try{t.onDisable?.(this),t.destroy?.(this)}catch(s){console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`,s)}t.element?.remove()}this.crashedPlugins=[]}}resolveStageVisibility(){return this.state.cursorMode==="native"?!1:this.state.cursorMode==="both"?this.input.isEnabled&&this.state.hasReceivedInput:this.state.cursorMode==="custom"?this.input.isEnabled&&this.state.hasReceivedInput:this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput}resolveCursorState(){return!this.input.isEnabled||this.state.cursorMode==="both"||this.state.cursorMode==="native"?"auto":this.state.cursorMode==="custom"?"none":this.state.isNative||!this.state.hasReceivedInput?"auto":"none"}resetMotion(){this.state.velocity={x:0,y:0},this.state.displacement={x:0,y:0}}update(t){const e=t-this.lastTime,s=Math.min(e/1e3,.1);this.lastTime=t;const i=this.input.getCurrentTarget();i&&!i.isConnected?this.input.clearHover():i&&this.input.parseDOMInteraction(i),this._stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&this._stage.setNativeCursor(this.resolveCursorState()),this.input.isEnabled&&this.state.hasReceivedInput&&(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y);for(let n=0;n<this.plugins.length;n++)this.runPluginSafe(this.plugins[n],e);if(this.cleanupCrashedPlugins(),this.input.isEnabled){const n=this.state.reducedMotion?1e3:1/this.options.smoothness*2,o=this.state.smooth.x,h=this.state.smooth.y;this.state.smooth.x=c(this.state.smooth.x,this.state.target.x,n,s),this.state.smooth.y=c(this.state.smooth.y,this.state.target.y,n,s),this.state.displacement.x=this.state.target.x-this.state.smooth.x,this.state.displacement.y=this.state.target.y-this.state.smooth.y,s>0?(this.state.velocity.x=(this.state.smooth.x-o)/s,this.state.velocity.y=(this.state.smooth.y-h)/s):(this.state.velocity.x=0,this.state.velocity.y=0);const{x:f,y:m}=this.state.velocity;(Math.abs(f)>.1||Math.abs(m)>.1)&&(this.state.angle=Math.atan2(m,f)*(180/Math.PI))}}tick=t=>{this.update(t),this.isRunning&&(this.rafId=requestAnimationFrame(this.tick))};bindVisibilityHandling(){document.addEventListener("visibilitychange",()=>{this.isRunning&&(document.hidden?cancelAnimationFrame(this.rafId):(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))},{signal:this.visibilityAbortController.signal})}destroy(){this.isRunning=!1,cancelAnimationFrame(this.rafId),this.visibilityAbortController.abort(),this.input.destroy(),this._stage.destroy(),this.plugins.forEach(t=>t.destroy?.(this)),this.plugins=[]}}r.DEFAULT_HOVER_SELECTORS=d,r.Input=p,r.Stage=g,r.Supermouse=S,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@supermousejs/core",
3
- "version": "2.3.0",
4
- "description": "The core physics engine and state manager for Supermouse.",
3
+ "version": "2.4.1",
4
+ "description": "The core physics engine and state manager for Supermouse",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "scripts": {
46
46
  "build": "vite build",
47
- "test": "vitest"
47
+ "test": "vitest",
48
+ "test:watch": "vitest --watch"
48
49
  }
49
50
  }