@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 +6 -0
- package/README.md +109 -47
- package/dist/index.mjs +1 -1
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @supermousejs/core
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@supermousejs/core)
|
|
4
4
|
[](https://www.npmjs.com/package/@supermousejs/core)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
Supermouse is a headless, physics
|
|
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
|
|
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
|
|
65
|
-
|
|
|
66
|
-
| `smoothness`
|
|
67
|
-
| `hoverSelectors`
|
|
68
|
-
| `enableTouch`
|
|
69
|
-
| `autoDisableOnMobile`
|
|
70
|
-
| `cursor`
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
- `"
|
|
84
|
-
- `"
|
|
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
|
-
|
|
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
|
-
|
|
119
|
+
Or shorthand:
|
|
91
120
|
|
|
92
121
|
```ts
|
|
93
|
-
const
|
|
94
|
-
|
|
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
|
-
|
|
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
|
|
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;
|
|
129
|
-
mouse.options;
|
|
130
|
-
mouse.stage;
|
|
131
|
-
mouse.
|
|
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.
|
|
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.
|
|
200
|
+
mouse.addScope(config); // -> { name, container, setCursor, remove }
|
|
139
201
|
|
|
140
|
-
mouse.use(plugin);
|
|
141
|
-
mouse.getPlugin(name);
|
|
142
|
-
mouse.enablePlugin(name);
|
|
143
|
-
mouse.disablePlugin(name);
|
|
144
|
-
mouse.togglePlugin(name);
|
|
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();
|
|
148
|
-
mouse.step(time);
|
|
149
|
-
mouse.destroy();
|
|
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
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.
|
|
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"})}));
|