@supermousejs/core 2.5.0-beta.0 → 2.5.0-beta.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.5.0-beta.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 5d5ec21: Updated README to document cursor policy and scopes
8
+
3
9
  ## 2.5.0-beta.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # Supermouse.js - TypeScript cursor engine
1
+ # @supermousejs/core
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6
6
  ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40supermousejs%2Fcore?label=minzip)
7
7
 
8
- Supermouse is a headless, physics‑based cursor engine for the web written in TypeScript. It provides pointer tracking, state, physics, and a plugin lifecycle. You install plugins (or write your own) to render custom cursors and effects. The core is zero‑dependency and TypeScript‑first.
8
+ Supermouse is a headless, physics-based cursor engine for the web, written in TypeScript. It provides pointer tracking, state, physics, and a plugin lifecycle. You install plugins (or write your own) to render custom cursors and effects. Zero runtime dependencies.
9
9
 
10
10
  **Documentation:** [supermouse.js.org](https://supermouse.js.org)
11
11
 
@@ -34,7 +34,7 @@ const mouse = new Supermouse({
34
34
  });
35
35
  ```
36
36
 
37
- Or use the plugin interface directly:
37
+ Or write a plugin directly:
38
38
 
39
39
  ```ts
40
40
  import { Supermouse } from "@supermousejs/core";
@@ -59,46 +59,104 @@ const redDot = {
59
59
  const mouse = new Supermouse({ plugins: [redDot] });
60
60
  ```
61
61
 
62
+ > Mount plugin elements to `app.stage`, not to `document.body`, so cursor suppression, scope switching, and cleanup work automatically.
63
+
62
64
  ## Options
63
65
 
64
- | Option | Default | Description |
65
- | --------------------- | ----------------------------------------------------------------------- | --------------------------------------------- |
66
- | `smoothness` | `0.15` | Lower = smoother cursor follow |
67
- | `hoverSelectors` | `["a", "button", "input", "textarea", "[data-hover]", "[data-cursor]"]` | Selectors that set `state.isHover` |
68
- | `enableTouch` | `false` | Allow touch events to move cursor |
69
- | `autoDisableOnMobile` | `true` | Disable on coarse‑pointer devices |
70
- | `cursor` | `"auto"` | `"auto"`, `"custom"`, `"native"`, or `"both"` |
71
- | `hideOnLeave` | `true` | Hide when pointer leaves window |
72
- | `container` | `document.body` | Scoping container |
73
- | `zIndex` | `9999` | Stage stacking order |
74
- | `dataPrefix` | `"supermouse"` | Prefix for data attributes |
75
- | `rules` | — | Map of selectors to interaction data |
76
- | `plugins` | — | Plugins to install at creation |
77
- | `autoStart` | `true` | Set `false` and call `.start()` manually |
66
+ | Option | Default | Description |
67
+ | ----------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------- |
68
+ | `smoothness` | `0.15` | Lower = smoother cursor follow |
69
+ | `hoverSelectors` | `["a", "button", "input", "textarea", "[data-hover]", "[data-cursor]"]` | Selectors that set `state.isHover` |
70
+ | `enableTouch` | `false` | Allow touch events to move the cursor |
71
+ | `autoDisableOnMobile` | `true` | Disable on coarse-pointer devices |
72
+ | `cursor` | `"auto"` | `"auto"`, `"custom"`, `"native"`, or `"both"` |
73
+ | `cursorPolicy` | `DEFAULT_CURSOR_POLICY` | Rules for native-cursor fallback and CSS suppression |
74
+ | `inheritDataAttributes` | `true` | Cascade `data-*` attributes and `rules` from ancestors |
75
+ | `hideOnLeave` | `true` | Hide when the pointer leaves the window |
76
+ | `container` | `document.body` | Primary scope's container |
77
+ | `scopes` | — | Additional scopes registered at construction |
78
+ | `zIndex` | `9999` | Stage stacking order |
79
+ | `dataPrefix` | `"supermouse"` | Prefix for data attributes |
80
+ | `rules` | — | Map of selectors to interaction data |
81
+ | `plugins` | — | Plugins to install at creation |
82
+ | `autoStart` | `true` | Set `false` and call `.start()` manually |
78
83
 
79
84
  ## Cursor Modes
80
85
 
81
- - `"auto"` – native cursor on interactive elements, custom elsewhere.
82
- - `"custom"` – always custom, hide native.
83
- - `"native"` – always native, hide custom.
84
- - `"both"` – show both native and custom together.
86
+ The `cursor` option decides which cursor(s) to show:
87
+
88
+ - `"auto"` — native cursor on elements that should own it (inputs, links), custom elsewhere.
89
+ - `"custom"` — always custom, hide native.
90
+ - `"native"` — always native, hide custom.
91
+ - `"both"` — show both native and custom together.
85
92
 
86
93
  Change at runtime with `mouse.setCursor("both")`.
87
94
 
88
- ## Containers & Multiple Instances
95
+ The [Cursor Policy](#cursor-policy) decides which elements are considered native in `"auto"` mode.
96
+
97
+ ## Cursor Policy
98
+
99
+ A single object maps selectors to two independent flags:
100
+
101
+ - `native` — in `"auto"` mode, matching elements yield to the OS cursor.
102
+ - `hide` — when the custom cursor is active, matching elements get `cursor: none !important`.
103
+
104
+ Extend the default policy:
105
+
106
+ ```ts
107
+ import { Supermouse, DEFAULT_CURSOR_POLICY } from "@supermousejs/core";
108
+
109
+ const mouse = new Supermouse({
110
+ cursorPolicy: {
111
+ rules: [
112
+ ...DEFAULT_CURSOR_POLICY.rules,
113
+ { selector: "[data-native]", native: true }
114
+ ]
115
+ }
116
+ });
117
+ ```
89
118
 
90
- Scope an instance to a container:
119
+ Or shorthand:
91
120
 
92
121
  ```ts
93
- const modal = document.getElementById("modal");
94
- const mouse = new Supermouse({ container: modal, cursor: "custom" });
122
+ const mouse = new Supermouse({
123
+ cursorPolicy: {
124
+ native: ["input", "textarea", "select", "[data-native]"],
125
+ hide: ["a", "button", "[role='button']"]
126
+ }
127
+ });
95
128
  ```
96
129
 
97
- Multiple instances can coexist; CSS rules are scoped automatically.
130
+ ## Scopes
131
+
132
+ A single Supermouse instance can own multiple scopes. Each scope has its own container, cursor mode, hover selectors, cursor policy, and plugins. The innermost scope containing the pointer is the active one.
133
+
134
+ ```ts
135
+ const mouse = new Supermouse({
136
+ scopes: [
137
+ { name: "main", container: document.body, plugins: [Dot({ color: "red" })] },
138
+ { name: "sidebar", container: sidebarEl, cursor: "custom" }
139
+ ]
140
+ });
141
+ ```
142
+
143
+ Or add at runtime:
144
+
145
+ ```ts
146
+ const handle = mouse.addScope({ container: modalEl, cursor: "both" });
147
+ handle.setCursor("auto");
148
+ handle.remove();
149
+ ```
150
+
151
+ The single-container form is shorthand for one scope:
152
+
153
+ ```ts
154
+ const mouse = new Supermouse({ container: modalEl, cursor: "custom" });
155
+ ```
98
156
 
99
157
  ## Native Cursor Fallback
100
158
 
101
- In `"auto"` mode, add `data-supermouse-ignore` to force the native cursor on an element and its descendants.
159
+ In `"auto"` mode, add `data-supermouse-ignore` to force the native cursor on an element and its descendants:
102
160
 
103
161
  ```html
104
162
  <div data-supermouse-ignore>Native cursor here</div>
@@ -106,7 +164,7 @@ In `"auto"` mode, add `data-supermouse-ignore` to force the native cursor on an
106
164
 
107
165
  ## Data Attributes
108
166
 
109
- Plugins can react to `data-*` attributes, e.g. `data-supermouse-color`. The prefix is configurable via `dataPrefix`.
167
+ Plugins can react to `data-*` attributes (e.g. `data-supermouse-color`). Attributes cascade from ancestors to the hovered element when `inheritDataAttributes` is `true`. Change the prefix with `dataPrefix`.
110
168
 
111
169
  ## Rules
112
170
 
@@ -120,33 +178,37 @@ const mouse = new Supermouse({
120
178
  });
121
179
  ```
122
180
 
123
- HTML `data-*` attributes override rule values per property.
181
+ Rules match ancestors as well as the hovered element. HTML `data-*` attributes override rule values per property.
124
182
 
125
183
  ## API
126
184
 
127
185
  ```ts
128
- mouse.state; // current MouseState
129
- mouse.options; // resolved options
130
- mouse.stage; // DOM element plugins render into
131
- mouse.isEnabled; // boolean
186
+ mouse.state; // current MouseState
187
+ mouse.options; // resolved options
188
+ mouse.stage; // DOM element plugins render into
189
+ mouse.container; // container element (active scope)
190
+ mouse.isEnabled; // input processing flag
191
+ mouse.isRunning; // animation loop flag
192
+
193
+ mouse.enable(); // start input, apply cursor state
194
+ mouse.disable(); // stop input, restore native cursor
195
+ mouse.disable({ reset: true }); // also reset physics and hover state
196
+ mouse.reset(); // reset state only
132
197
 
133
- mouse.enable(); // start input, hide native cursor
134
- mouse.disable(); // stop input, show native cursor, reset
135
- mouse.suspend(); // pause non‑destructively
136
- mouse.resume(); // resume from suspend
198
+ mouse.setCursor(mode); // "auto" | "custom" | "native" | "both"
137
199
 
138
- mouse.setCursor(mode); // "auto" | "custom" | "native" | "both"
200
+ mouse.addScope(config); // -> { name, container, setCursor, remove }
139
201
 
140
- mouse.use(plugin); // install plugin
141
- mouse.getPlugin(name); // retrieve plugin
142
- mouse.enablePlugin(name); // enable plugin
143
- mouse.disablePlugin(name); // disable plugin
144
- mouse.togglePlugin(name); // toggle plugin
202
+ mouse.use(plugin); // install plugin
203
+ mouse.getPlugin(name); // retrieve plugin
204
+ mouse.enablePlugin(name); // enable plugin
205
+ mouse.disablePlugin(name); // disable plugin
206
+ mouse.togglePlugin(name); // toggle plugin
145
207
 
146
208
  mouse.registerHoverTarget(sel); // add hover selector at runtime
147
- mouse.start(); // start animation loop
148
- mouse.step(time); // advance one frame manually
149
- mouse.destroy(); // destroy instance and plugins
209
+ mouse.start(); // start animation loop
210
+ mouse.step(time); // advance one frame manually
211
+ mouse.destroy(); // destroy instance and plugins
150
212
  ```
151
213
 
152
214
  ## Browser Support
@@ -155,4 +217,4 @@ All modern browsers.
155
217
 
156
218
  ## License
157
219
 
158
- MIT
220
+ MIT
package/dist/index.mjs CHANGED
@@ -343,7 +343,7 @@ function V(i) {
343
343
  function F() {
344
344
  l?.remove(), l = null;
345
345
  }
346
- const E = "2.5.0-beta.0";
346
+ const E = "2.5.0-beta.1";
347
347
  function j(i, t, e) {
348
348
  return i + (t - i) * e;
349
349
  }
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
1
  (function(l,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(l=typeof globalThis<"u"?globalThis:l||self,p(l.SupermouseCore={}))})(this,(function(l){"use strict";const p={rules:[{selector:"input",native:!0,hide:!0},{selector:"textarea",native:!0,hide:!0},{selector:"select",native:!0,hide:!0},{selector:"[contenteditable]",native:!0,hide:!0},{selector:"a",native:!1,hide:!0},{selector:"button",native:!1,hide:!0},{selector:'[role="button"]',native:!1,hide:!0},{selector:"[tabindex]",native:!1,hide:!0}]};function w(i){if(!i)return p;if("rules"in i)return i;const t=[],e=new Set(i.native??[]),s=new Set(i.hide??[]),n=new Set([...e,...s]);for(const r of n)t.push({selector:r,native:e.has(r),hide:s.has(r)});return{rules:t}}function A(i){const t=[],e=[];for(const s of i.rules)s.native!==!1&&t.push(s.selector),s.hide!==!1&&e.push(s.selector);return{native:t,hide:e}}let R=0;class O{constructor(t,e){if(this.container=t,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);t.isConnected;const s=R++;this.scopeClass=`supermouse-scope-${s}`,this.hideClass=`supermouse-hide-${s}`;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:String(e),opacity:"1",transition:"opacity 0.15s ease"}),!n){const r=window.getComputedStyle(t);this.originalContainerPosition=r.position,r.position==="static"&&(t.style.position="relative")}this.originalContainerCursor=t.style.cursor,t.appendChild(this.element),t.classList.add("supermouse-scope",this.scopeClass)}container;element;scopeClass;hideClass;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";get containerElement(){return this.container}getRulePrefix(){return`.${this.scopeClass}.${this.hideClass}`}getExclusion(){return`:not(.${this.scopeClass} .supermouse-scope):not(.${this.scopeClass} .supermouse-scope *)`}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"),t==="none"?this.container.style.cursor="none":this.container===document.body?this.container.style.cursor=this.originalContainerCursor:this.container.style.cursor=this.originalContainerCursor||"auto")}destroy(){this.element.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 I{constructor(t,e){this.config=t,this.name=t.name,this.cursorMode=t.cursor??e.cursor,this.hoverSelectors=new Set(t.hoverSelectors??e.hoverSelectors),this.inheritDataAttributes=t.inheritDataAttributes??e.inheritDataAttributes;const s=t.cursorPolicy?w(t.cursorPolicy):e.cursorPolicy,n=A(s);this.nativeSelectors=n.native,this.hideSelectors=n.hide,this.stage=new O(t.container,t.zIndex??e.zIndex)}config;stage;hoverSelectors;plugins=[];nativeSelectors;hideSelectors;inheritDataAttributes;name;cursorMode;contains(t){return this.stage.containerElement.contains(t)}get container(){return this.stage.containerElement}get hoverSelectorString(){return Array.from(this.hoverSelectors).join(", ")}get nativeSelectorString(){return this.nativeSelectors.join(", ")}buildRules(){const t=this.stage.getRulePrefix(),e=this.stage.getExclusion(),s=[`${t}${e} { cursor: none !important; }`,`${t} *${e} { cursor: none !important; }`];for(const n of this.hideSelectors)s.push(`${t} ${n}${e} { cursor: none !important; }`);return s.push(`${t} label${e} { cursor: none !important; }`),s.push(`${t} select${e} { cursor: none !important; }`),s.push(`${t} input[type="range"]${e}::-webkit-slider-thumb { cursor: none !important; }`),s.push(`${t} input[type="range"]${e}::-moz-range-thumb { cursor: none !important; }`),s}}const v={x:-100,y:-100},D=new Set(["default","auto","pointer","none","inherit","grab","grabbing"]),x=["a","button","input","textarea","[data-hover]","[data-cursor]"],u=new Map,_=new Map;let m=null,y=!1,S=!1,C=0;function E(i){const t=i.getBoundingClientRect();return{left:t.left,top:t.top,width:t.width,height:t.height}}function T(i){u.set(i,E(i));const t=_.get(i);if(t)for(const e of t)e()}function b(){for(const i of u.keys())T(i)}function $(){m||typeof ResizeObserver>"u"||(m=new ResizeObserver(i=>{for(const t of i){const e=t.target;u.has(e)&&T(e)}}))}function z(){typeof window>"u"||(y||(window.addEventListener("scroll",b,{passive:!0,capture:!0}),y=!0),S||(window.addEventListener("resize",b,{passive:!0}),S=!0))}function k(i,t){return C++,u.has(i)||(u.set(i,E(i)),$(),m?.observe(i),z()),()=>{C--,C===0&&(m?.disconnect(),m=null,u.clear(),_.clear(),y&&(window.removeEventListener("scroll",b,{capture:!0}),y=!1),S&&(window.removeEventListener("resize",b),S=!1))}}function N(i){let t=u.get(i);return t||(t=E(i),u.set(i,t)),t}function H(i,t,e){if(i===document.body)return{x:t,y:e};const s=N(i);return{x:t-s.left,y:e-s.top}}class V{constructor(t,e,s,n){this.state=t,this.options=e,this.onEnableChange=s,this.onActiveScopeChange=n,this.dataPrefix=e.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.ruleEntries=e.rules?Object.entries(e.rules):[],this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;onEnableChange;onActiveScopeChange;abortController=new AbortController;mediaQueryList;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;activeScope=null;scopeByContainer=new Map;nativeTarget=null;currentTarget=null;lastParsedTarget=null;ruleEntries;releaseViewport=null;hasSeenPointer=!1;isEnabled=!0;viewportX=0;viewportY=0;cachedChain=[];setScopes(t){this.scopeByContainer.clear();for(const e of t)this.scopeByContainer.set(e.container,e)}setActiveScope(t){t!==this.activeScope&&(this.activeScope=t,this.releaseViewport?.(),this.releaseViewport=t&&t.container!==document.body?k(t.container):null,this.hasSeenPointer&&(this.applyPointerToState(),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.onActiveScopeChange(t))}findScope(t){let e=t;for(;e;){const s=this.scopeByContainer.get(e);if(s)return s;e=e.parentElement}return null}applyPointerToState(){const t=this.activeScope?.container??document.body,e=H(t,this.viewportX,this.viewportY);this.state.pointer.x=e.x,this.state.pointer.y=e.y}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)}parseDOMInteraction(t){const e=this.activeScope?.container??document.body,s=this.activeScope?.inheritDataAttributes??!0,n=this.normalizedDataPrefix;if(t!==this.lastParsedTarget){this.lastParsedTarget=t,this.cachedChain=[];let o=t;for(;o;){const g=[];for(const[a,h]of this.ruleEntries)this.matchesSelector(o,a)&&g.push({selector:a,rules:h});if(this.cachedChain.push({element:o,matchedRules:g}),!s||o===e)break;const c=o.parentElement;if(!c||!e.contains(c))break;o=c}}const r={};for(const{element:o,matchedRules:g}of this.cachedChain){const c={};for(const{rules:a}of g)try{const h=typeof a=="function"?a(o):a;if(!h||typeof h!="object")continue;for(const[P,f]of Object.entries(h))c[P]=typeof f=="function"?f(o):f}catch{}for(const a in o.dataset){if(!a.toLowerCase().startsWith(n))continue;const h=a.slice(n.length);if(!h)continue;const P=h[0].toLowerCase()+h.slice(1),f=o.dataset[a];c[P]=f==null||f===""?!0:f}for(const[a,h]of Object.entries(c))a in r||(r[a]=h)}this.state.interaction=r}matchesSelector(t,e){try{return t.matches(e)}catch{return!1}}resolveComputedCursor(t){return window.getComputedStyle(t).cursor}handleMove=t=>{this.options.autoDisableOnMobile&&t.pointerType==="touch"&&!this.options.enableTouch||(this.viewportX=t.clientX,this.viewportY=t.clientY,this.hasSeenPointer=!0,this.applyPointerToState(),this.isEnabled&&(this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=this.state.pointer.x,this.state.target.y=this.state.smooth.y=this.state.pointer.y)))};handleDown=()=>{this.isEnabled&&(this.state.isDown=!0)};handleUp=()=>{this.isEnabled&&(this.state.isDown=!1)};handleMouseOver=t=>{if(!this.isEnabled)return;const e=t.target,s=this.findScope(e);if(s&&s!==this.activeScope&&this.setActiveScope(s),!this.activeScope)return;if(this.state.cursorMode==="auto"&&e.closest(`[${this.ignoreAttribute}]`)){this.clearHover(),this.state.isNative=!0,this.nativeTarget=e;return}this.state.isNative=!1,this.nativeTarget=null,this.currentTarget=e,this.parseDOMInteraction(e);const n=e.closest(this.activeScope.hoverSelectorString);if(n&&(this.state.isHover=!0,this.state.hoverTarget=n),this.state.cursorMode==="auto"){if(this.matchesSelector(e,this.activeScope.nativeSelectorString)){this.state.isNative=!0,this.nativeTarget=e;return}(e.isContentEditable||!D.has(this.resolveComputedCursor(e)))&&(this.state.isNative=!0,this.nativeTarget=e)}};handleMouseOut=t=>{if(!this.isEnabled)return;const e=t.target,s=t.relatedTarget;(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.cachedChain=[]),!s&&this.options.hideOnLeave&&(this.state.hasReceivedInput=!1,this.state.pointer={...v})};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.cachedChain=[]}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}),document.addEventListener("mouseover",this.handleMouseOver,{signal:t}),document.addEventListener("mouseout",this.handleMouseOut,{signal:t})}destroy(){this.abortController.abort(),this.releaseViewport?.(),this.cachedChain=[]}}let d=null;function B(){d||typeof document>"u"||(d=document.createElement("style"),d.id="supermouse-styles",document.head.appendChild(d))}function F(i){B(),d&&(d.textContent=i.join(`
2
- `))}function U(){d?.remove(),d=null}const M="2.5.0-beta.0";function j(i,t,e){return i+(t-i)*e}function L(i,t,e,s){return j(i,t,1-Math.exp(-e*s))}class Q{static version=M;version=M;state;options;_scopes=[];_activeScope=null;_installingScope=null;scopeByContainer=new Map;input;rafId=0;lastTime=0;visibilityAbortController=new AbortController;crashedPlugins=[];constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,cursor:"auto",hideOnLeave:!0,autoStart:!0,container:document.body,dataPrefix:"supermouse",zIndex:9999,inheritDataAttributes:!0,...t},this.state={pointer:{...v},target:{...v},smooth:{...v},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:{}};const e=this.createScope({container:this.options.container,cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors,cursorPolicy:this.options.cursorPolicy,plugins:this.options.plugins,zIndex:this.options.zIndex});if(this.input=new V(this.state,this.options,s=>{s||this.reset()},s=>this.handleActiveScopeChange(s)),this.input.setScopes(this._scopes),this.input.setActiveScope(e),this.installPlugins(e),this.options.scopes)for(const s of this.options.scopes)this.addScope(s);this.rebuildStylesheet(),this.bindVisibilityHandling(),this.options.autoStart&&this.startLoop()}get container(){return(this._activeScope??this._scopes[0]).container}get stage(){return(this._installingScope??this._activeScope??this._scopes[0]).stage.element}get isEnabled(){return this.input.isEnabled}get isRunning(){return this._running}use(t){return this.installPlugin(this._scopes[0],t),this}getPlugin(t){for(const e of this._scopes){const s=e.plugins.find(n=>n.name===t);if(s)return s}}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);e&&e.isEnabled!==!1&&this.deactivatePlugin(e)}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}setCursor(t){this._activeScope&&(this._activeScope.cursorMode=t,this.state.cursorMode=t)}addScope(t){const e=this.createScope(t);return this.input.setScopes(this._scopes),this.installPlugins(e),this.rebuildStylesheet(),{name:e.name,container:e.container,remove:()=>this.removeScope(e),setCursor:s=>this.setScopeCursor(e,s)}}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._activeScope&&this._activeScope.stage.setNativeCursor(this.resolveCursorState())}disable(t){this.input.isEnabled=!1,this._activeScope&&(this._activeScope.stage.setNativeCursor("auto"),this._activeScope.stage.setVisibility(!1)),t?.reset&&this.reset()}reset(){this.state.target={...v},this.state.smooth={...v},this.resetMotion(),this.state.angle=0,this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={}}start(){this.startLoop()}step(t){this.update(t)}destroy(){this._running=!1,cancelAnimationFrame(this.rafId),this.visibilityAbortController.abort(),this.input.destroy();for(const t of this._scopes){for(const e of t.plugins)try{e.destroy?.(this)}catch{}t.stage.destroy()}this._scopes=[],this.scopeByContainer.clear(),this._activeScope=null,U()}_running=!1;createScope(t){const e=new I(t,{cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors??x,cursorPolicy:this.options.cursorPolicy?w(this.options.cursorPolicy):p,zIndex:this.options.zIndex,inheritDataAttributes:this.options.inheritDataAttributes});return this._scopes.push(e),this.scopeByContainer.set(e.container,e),e}removeScope(t){const e=this._scopes.indexOf(t);if(e!==-1){for(const s of t.plugins)try{s.destroy?.(this)}catch{}t.plugins.length=0,t.stage.destroy(),this._scopes.splice(e,1),this.scopeByContainer.delete(t.container),this.input.setScopes(this._scopes),this._activeScope===t&&this.input.setActiveScope(this._scopes[0]??null),this.rebuildStylesheet()}}setScopeCursor(t,e){t.cursorMode=e,t===this._activeScope&&(this.state.cursorMode=e)}installPlugins(t){if(t.config.plugins)for(const e of t.config.plugins)this.installPlugin(t,e)}installPlugin(t,e){if(!t.plugins.some(s=>s.name===e.name)){e.isEnabled??=!0,this._installingScope=t;try{e.install?.(this)}catch{this._installingScope=null;return}this._installingScope=null,t.plugins.push(e),t.plugins.sort((s,n)=>(s.priority??0)-(n.priority??0)),t!==this._activeScope&&(e.isEnabled=!1,e.element&&(e.element.style.display="none"))}}handleActiveScopeChange(t){const e=this._activeScope;if(e!==t){if(e)for(const s of e.plugins)this.deactivatePlugin(s);if(this._activeScope=t,t){this.state.cursorMode=t.cursorMode;for(const s of t.plugins)this.activatePlugin(s)}}}activatePlugin(t){t.isEnabled===!1&&(t.isEnabled=!0,t.element&&(t.element.style.display=""),t.onEnable?.(this))}deactivatePlugin(t){if(t.isEnabled===!1)return;t.isEnabled=!1;const e=()=>{t.element&&(t.element.style.display="none"),t.onDisable?.(this)},s=t.onBeforeDisable?.(this);s&&typeof s.then=="function"?Promise.resolve(s).then(e).catch(n=>{e()}):e()}rebuildStylesheet(){const t=[];for(const e of this._scopes)t.push(...e.buildRules());F(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch{t.isEnabled=!1,this.crashedPlugins.push(t)}}cleanupCrashedPlugins(){if(this.crashedPlugins.length!==0){for(const t of this.crashedPlugins){for(const e of this._scopes){const s=e.plugins.indexOf(t);if(s!==-1){e.plugins.splice(s,1);break}}try{t.onDisable?.(this),t.destroy?.(this)}catch{}t.element?.remove()}this.crashedPlugins=[]}}resolveStageVisibility(){return this.state.cursorMode==="native"?!1:this.state.cursorMode==="custom"||this.state.cursorMode==="both"?this.input.isEnabled&&this.state.hasReceivedInput:this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput}resolveCursorState(){return!this.input.isEnabled||this.state.cursorMode==="native"||this.state.cursorMode==="both"?"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 n=this.input.getCurrentTarget();n&&!n.isConnected?this.input.clearHover():n&&this.input.parseDOMInteraction(n);const r=this._activeScope;if(r){r.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&r.stage.setNativeCursor(this.resolveCursorState());for(let o=0;o<r.plugins.length;o++)this.runPluginSafe(r.plugins[o],e)}if(this.cleanupCrashedPlugins(),this.input.isEnabled&&this.state.hasReceivedInput&&(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y),this.input.isEnabled){const o=this.state.reducedMotion?1e3:1/this.options.smoothness*2,g=this.state.smooth.x,c=this.state.smooth.y;this.state.smooth.x=L(this.state.smooth.x,this.state.target.x,o,s),this.state.smooth.y=L(this.state.smooth.y,this.state.target.y,o,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-g)/s,this.state.velocity.y=(this.state.smooth.y-c)/s):(this.state.velocity.x=0,this.state.velocity.y=0);const{x:a,y:h}=this.state.velocity;(Math.abs(a)>.1||Math.abs(h)>.1)&&(this.state.angle=Math.atan2(h,a)*(180/Math.PI))}}tick=t=>{this.update(t),this._running&&(this.rafId=requestAnimationFrame(this.tick))};bindVisibilityHandling(){document.addEventListener("visibilitychange",()=>{this._running&&(document.hidden?cancelAnimationFrame(this.rafId):(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))},{signal:this.visibilityAbortController.signal})}startLoop(){this._running||(this._running=!0,!document.hidden&&(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))}registerHoverTarget(t){const e=this._installingScope??this._activeScope??this._scopes[0];if(e)for(const s of t.split(",")){const n=s.trim();n&&e.hoverSelectors.add(n)}}get hoverSelectors(){const t=this._activeScope??this._scopes[0];return t?t.hoverSelectors:new Set}get plugins(){const t=this._activeScope??this._scopes[0];return t?t.plugins:[]}}l.DEFAULT_CURSOR_POLICY=p,l.DEFAULT_HOVER_SELECTORS=x,l.Supermouse=Q,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
2
+ `))}function U(){d?.remove(),d=null}const M="2.5.0-beta.1";function j(i,t,e){return i+(t-i)*e}function L(i,t,e,s){return j(i,t,1-Math.exp(-e*s))}class Q{static version=M;version=M;state;options;_scopes=[];_activeScope=null;_installingScope=null;scopeByContainer=new Map;input;rafId=0;lastTime=0;visibilityAbortController=new AbortController;crashedPlugins=[];constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,cursor:"auto",hideOnLeave:!0,autoStart:!0,container:document.body,dataPrefix:"supermouse",zIndex:9999,inheritDataAttributes:!0,...t},this.state={pointer:{...v},target:{...v},smooth:{...v},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:{}};const e=this.createScope({container:this.options.container,cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors,cursorPolicy:this.options.cursorPolicy,plugins:this.options.plugins,zIndex:this.options.zIndex});if(this.input=new V(this.state,this.options,s=>{s||this.reset()},s=>this.handleActiveScopeChange(s)),this.input.setScopes(this._scopes),this.input.setActiveScope(e),this.installPlugins(e),this.options.scopes)for(const s of this.options.scopes)this.addScope(s);this.rebuildStylesheet(),this.bindVisibilityHandling(),this.options.autoStart&&this.startLoop()}get container(){return(this._activeScope??this._scopes[0]).container}get stage(){return(this._installingScope??this._activeScope??this._scopes[0]).stage.element}get isEnabled(){return this.input.isEnabled}get isRunning(){return this._running}use(t){return this.installPlugin(this._scopes[0],t),this}getPlugin(t){for(const e of this._scopes){const s=e.plugins.find(n=>n.name===t);if(s)return s}}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);e&&e.isEnabled!==!1&&this.deactivatePlugin(e)}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}setCursor(t){this._activeScope&&(this._activeScope.cursorMode=t,this.state.cursorMode=t)}addScope(t){const e=this.createScope(t);return this.input.setScopes(this._scopes),this.installPlugins(e),this.rebuildStylesheet(),{name:e.name,container:e.container,remove:()=>this.removeScope(e),setCursor:s=>this.setScopeCursor(e,s)}}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._activeScope&&this._activeScope.stage.setNativeCursor(this.resolveCursorState())}disable(t){this.input.isEnabled=!1,this._activeScope&&(this._activeScope.stage.setNativeCursor("auto"),this._activeScope.stage.setVisibility(!1)),t?.reset&&this.reset()}reset(){this.state.target={...v},this.state.smooth={...v},this.resetMotion(),this.state.angle=0,this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={}}start(){this.startLoop()}step(t){this.update(t)}destroy(){this._running=!1,cancelAnimationFrame(this.rafId),this.visibilityAbortController.abort(),this.input.destroy();for(const t of this._scopes){for(const e of t.plugins)try{e.destroy?.(this)}catch{}t.stage.destroy()}this._scopes=[],this.scopeByContainer.clear(),this._activeScope=null,U()}_running=!1;createScope(t){const e=new I(t,{cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors??x,cursorPolicy:this.options.cursorPolicy?w(this.options.cursorPolicy):p,zIndex:this.options.zIndex,inheritDataAttributes:this.options.inheritDataAttributes});return this._scopes.push(e),this.scopeByContainer.set(e.container,e),e}removeScope(t){const e=this._scopes.indexOf(t);if(e!==-1){for(const s of t.plugins)try{s.destroy?.(this)}catch{}t.plugins.length=0,t.stage.destroy(),this._scopes.splice(e,1),this.scopeByContainer.delete(t.container),this.input.setScopes(this._scopes),this._activeScope===t&&this.input.setActiveScope(this._scopes[0]??null),this.rebuildStylesheet()}}setScopeCursor(t,e){t.cursorMode=e,t===this._activeScope&&(this.state.cursorMode=e)}installPlugins(t){if(t.config.plugins)for(const e of t.config.plugins)this.installPlugin(t,e)}installPlugin(t,e){if(!t.plugins.some(s=>s.name===e.name)){e.isEnabled??=!0,this._installingScope=t;try{e.install?.(this)}catch{this._installingScope=null;return}this._installingScope=null,t.plugins.push(e),t.plugins.sort((s,n)=>(s.priority??0)-(n.priority??0)),t!==this._activeScope&&(e.isEnabled=!1,e.element&&(e.element.style.display="none"))}}handleActiveScopeChange(t){const e=this._activeScope;if(e!==t){if(e)for(const s of e.plugins)this.deactivatePlugin(s);if(this._activeScope=t,t){this.state.cursorMode=t.cursorMode;for(const s of t.plugins)this.activatePlugin(s)}}}activatePlugin(t){t.isEnabled===!1&&(t.isEnabled=!0,t.element&&(t.element.style.display=""),t.onEnable?.(this))}deactivatePlugin(t){if(t.isEnabled===!1)return;t.isEnabled=!1;const e=()=>{t.element&&(t.element.style.display="none"),t.onDisable?.(this)},s=t.onBeforeDisable?.(this);s&&typeof s.then=="function"?Promise.resolve(s).then(e).catch(n=>{e()}):e()}rebuildStylesheet(){const t=[];for(const e of this._scopes)t.push(...e.buildRules());F(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch{t.isEnabled=!1,this.crashedPlugins.push(t)}}cleanupCrashedPlugins(){if(this.crashedPlugins.length!==0){for(const t of this.crashedPlugins){for(const e of this._scopes){const s=e.plugins.indexOf(t);if(s!==-1){e.plugins.splice(s,1);break}}try{t.onDisable?.(this),t.destroy?.(this)}catch{}t.element?.remove()}this.crashedPlugins=[]}}resolveStageVisibility(){return this.state.cursorMode==="native"?!1:this.state.cursorMode==="custom"||this.state.cursorMode==="both"?this.input.isEnabled&&this.state.hasReceivedInput:this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput}resolveCursorState(){return!this.input.isEnabled||this.state.cursorMode==="native"||this.state.cursorMode==="both"?"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 n=this.input.getCurrentTarget();n&&!n.isConnected?this.input.clearHover():n&&this.input.parseDOMInteraction(n);const r=this._activeScope;if(r){r.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&r.stage.setNativeCursor(this.resolveCursorState());for(let o=0;o<r.plugins.length;o++)this.runPluginSafe(r.plugins[o],e)}if(this.cleanupCrashedPlugins(),this.input.isEnabled&&this.state.hasReceivedInput&&(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y),this.input.isEnabled){const o=this.state.reducedMotion?1e3:1/this.options.smoothness*2,g=this.state.smooth.x,c=this.state.smooth.y;this.state.smooth.x=L(this.state.smooth.x,this.state.target.x,o,s),this.state.smooth.y=L(this.state.smooth.y,this.state.target.y,o,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-g)/s,this.state.velocity.y=(this.state.smooth.y-c)/s):(this.state.velocity.x=0,this.state.velocity.y=0);const{x:a,y:h}=this.state.velocity;(Math.abs(a)>.1||Math.abs(h)>.1)&&(this.state.angle=Math.atan2(h,a)*(180/Math.PI))}}tick=t=>{this.update(t),this._running&&(this.rafId=requestAnimationFrame(this.tick))};bindVisibilityHandling(){document.addEventListener("visibilitychange",()=>{this._running&&(document.hidden?cancelAnimationFrame(this.rafId):(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))},{signal:this.visibilityAbortController.signal})}startLoop(){this._running||(this._running=!0,!document.hidden&&(this.lastTime=performance.now(),this.rafId=requestAnimationFrame(this.tick)))}registerHoverTarget(t){const e=this._installingScope??this._activeScope??this._scopes[0];if(e)for(const s of t.split(",")){const n=s.trim();n&&e.hoverSelectors.add(n)}}get hoverSelectors(){const t=this._activeScope??this._scopes[0];return t?t.hoverSelectors:new Set}get plugins(){const t=this._activeScope??this._scopes[0];return t?t.plugins:[]}}l.DEFAULT_CURSOR_POLICY=p,l.DEFAULT_HOVER_SELECTORS=x,l.Supermouse=Q,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supermousejs/core",
3
- "version": "2.5.0-beta.0",
3
+ "version": "2.5.0-beta.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "README.md",