@supermousejs/core 2.5.0-beta.3 → 2.5.0-beta.5
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 +47 -0
- package/README.md +40 -43
- package/dist/index.d.ts +44 -36
- package/dist/index.mjs +110 -113
- package/dist/index.umd.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# @supermousejs/core
|
|
2
2
|
|
|
3
|
+
## 2.5.0-beta.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d82c62d: ---
|
|
8
|
+
|
|
9
|
+
Beta.5 — cursor policy flattening, suppression simplification, and API renames.
|
|
10
|
+
|
|
11
|
+
## Changed — breaking
|
|
12
|
+
- `cursorPolicy` option renamed to `nativeCursorSelectors` and flattened to a plain `string[]`. Migration: `cursorPolicy: { native: [".x"] }` → `nativeCursorSelectors: [".x"]`. The `hide` key is gone; cursor suppression is unconditional and no longer configurable.
|
|
13
|
+
- `CursorPolicy` and `CursorPolicyInput` types removed from the public export surface. Code importing them fails at compile time. The only remaining shape is `string[]`.
|
|
14
|
+
- `DEFAULT_CURSOR_POLICY` renamed to `DEFAULT_NATIVE_CURSOR_SELECTORS`. Value reduced to `["select", "[contenteditable]"]`.
|
|
15
|
+
- `registerHoverTarget(selector)` renamed to `addHoverSelectors(selectors)`. Same behavior — comma-separated selectors are added to the current scope's shared hover set. The new name matches `hoverSelectors` and reflects that it mutates a set rather than registering a single element.
|
|
16
|
+
- `@supermousejs/utils`: `VisualConfig.selector` renamed to `hoverSelector`. Update `definePlugin({ ..., selector: "x" })` to `definePlugin({ ..., hoverSelector: "x" })`.
|
|
17
|
+
|
|
18
|
+
## Changed — behavior
|
|
19
|
+
- `input` and `textarea` removed from the default native cursor list. Text inputs and textareas are unaffected — their UA cursor is `text`, which the probe reads and treats as a fallback trigger. Button-like inputs (`<input type="button">`, `<input type="submit">`) and checkboxes/radios change: their UA cursor is `default`, which the probe classifies as non-fallback, so the custom cursor now shows over them. This matches the pre-existing treatment of `<button>`.
|
|
20
|
+
- Cursor suppression is now a single wildcard rule per scope instead of a wildcard plus per-selector rules. Behavior is unchanged — verified in Chromium and Firefox against `<a>`, `<button>`, `<input>` (all types), `<textarea>`, `<select>`, `[contenteditable]`, `[role="button"]`, `[tabindex]`, `<label>`, and a `<canvas>` with site CSS. Range slider thumb pseudo-elements retain their explicit rules (`*` does not match pseudo-elements).
|
|
21
|
+
|
|
22
|
+
## Fixed
|
|
23
|
+
- `handleMouseOut` no longer leaves `state.isHover` and `state.isNative` stale when the pointer leaves from a descendant of the tracked element. Moving from `<span>` inside `<button>` directly outside the button previously failed to clear `hoverTarget` — the containment check was inverted.
|
|
24
|
+
|
|
25
|
+
## Internal
|
|
26
|
+
- `policy.ts` removed from `@supermousejs/core`. The default moved to `constants.ts`; `normalizePolicy` dissolved into `??` fallbacks at the call sites.
|
|
27
|
+
- `Scope.hideSelectors` field removed.
|
|
28
|
+
- Size budget re-tightened to 6.2 kB gzip / 5.5 kB brotli.
|
|
29
|
+
|
|
30
|
+
## 2.5.0-beta.4
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- cb9bf9a: Beta.4
|
|
35
|
+
|
|
36
|
+
**Added**
|
|
37
|
+
- `state.authoredCursor` — the cursor value the page intends at the element under the pointer, resolved as if Supermouse's suppression were not active. Populated on every pointerTarget change, in all cursor modes. Read this to know what the page wants (`canvas { cursor: crosshair }`, `.drag-handle { cursor: grab }`, `[disabled] { cursor: not-allowed }`) rather than just whether the OS cursor should be shown (`state.isNative`).
|
|
38
|
+
- `state.pointerTarget` — the raw element under the pointer, regardless of hover selectors. Distinct from `state.hoverTarget`, which reports the nearest ancestor matching a hover selector.
|
|
39
|
+
|
|
40
|
+
**Fixed**
|
|
41
|
+
- Cursor detection in `auto` mode no longer reads its own suppression output. The previous implementation called `getComputedStyle(target).cursor` while the scope's hide class was active, so it always saw `"none"` and never triggered native fallback for authored exotic cursors. A probe attribute (`data-sm-probe`) is now excluded from every generated suppression rule, so the read sees the page's intended value.
|
|
42
|
+
|
|
43
|
+
**Changed**
|
|
44
|
+
- `@supermousejs/labs`: `SmartIcon` now reads `state.pointerTarget` and `state.authoredCursor` instead of relying on a broad hover-selector sweep. Hovering a `<p>` no longer sets `state.isHover` on the primary scope.
|
|
45
|
+
- `@supermousejs/labs`: `SmartIconOptions.useSemanticTags` renamed to `useSemanticDetection`. The old name referred to a `registerHoverTarget` sweep that no longer runs.
|
|
46
|
+
|
|
47
|
+
**Internal**
|
|
48
|
+
- Size budget raised to 6.5 kB gzip / 5.75 kB brotli to accommodate the beta.4 surface. Will be re-tightened once the shape settles.
|
|
49
|
+
|
|
3
50
|
## 2.5.0-beta.3
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -63,23 +63,23 @@ const mouse = new Supermouse({ plugins: [redDot] });
|
|
|
63
63
|
|
|
64
64
|
## Options
|
|
65
65
|
|
|
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
|
|
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 |
|
|
83
83
|
|
|
84
84
|
## Cursor Modes
|
|
85
85
|
|
|
@@ -108,10 +108,7 @@ import { Supermouse, DEFAULT_CURSOR_POLICY } from "@supermousejs/core";
|
|
|
108
108
|
|
|
109
109
|
const mouse = new Supermouse({
|
|
110
110
|
cursorPolicy: {
|
|
111
|
-
rules: [
|
|
112
|
-
...DEFAULT_CURSOR_POLICY.rules,
|
|
113
|
-
{ selector: "[data-native]", native: true }
|
|
114
|
-
]
|
|
111
|
+
rules: [...DEFAULT_CURSOR_POLICY.rules, { selector: "[data-native]", native: true }]
|
|
115
112
|
}
|
|
116
113
|
});
|
|
117
114
|
```
|
|
@@ -183,32 +180,32 @@ Rules match ancestors as well as the hovered element. HTML `data-*` attributes o
|
|
|
183
180
|
## API
|
|
184
181
|
|
|
185
182
|
```ts
|
|
186
|
-
mouse.state;
|
|
187
|
-
mouse.options;
|
|
188
|
-
mouse.stage;
|
|
189
|
-
mouse.container;
|
|
190
|
-
mouse.isEnabled;
|
|
191
|
-
mouse.isRunning;
|
|
192
|
-
|
|
193
|
-
mouse.enable();
|
|
194
|
-
mouse.disable();
|
|
183
|
+
mouse.state; // current MouseState
|
|
184
|
+
mouse.options; // resolved options
|
|
185
|
+
mouse.stage; // DOM element plugins render into
|
|
186
|
+
mouse.container; // container element (active scope)
|
|
187
|
+
mouse.isEnabled; // input processing flag
|
|
188
|
+
mouse.isRunning; // animation loop flag
|
|
189
|
+
|
|
190
|
+
mouse.enable(); // start input, apply cursor state
|
|
191
|
+
mouse.disable(); // stop input, restore native cursor
|
|
195
192
|
mouse.disable({ reset: true }); // also reset physics and hover state
|
|
196
|
-
mouse.reset();
|
|
193
|
+
mouse.reset(); // reset state only
|
|
197
194
|
|
|
198
|
-
mouse.setCursor(mode);
|
|
195
|
+
mouse.setCursor(mode); // "auto" | "custom" | "native" | "both"
|
|
199
196
|
|
|
200
|
-
mouse.addScope(config);
|
|
197
|
+
mouse.addScope(config); // -> { name, container, setCursor, remove }
|
|
201
198
|
|
|
202
|
-
mouse.use(plugin);
|
|
203
|
-
mouse.getPlugin(name);
|
|
204
|
-
mouse.enablePlugin(name);
|
|
205
|
-
mouse.disablePlugin(name);
|
|
206
|
-
mouse.togglePlugin(name);
|
|
199
|
+
mouse.use(plugin); // install plugin
|
|
200
|
+
mouse.getPlugin(name); // retrieve plugin
|
|
201
|
+
mouse.enablePlugin(name); // enable plugin
|
|
202
|
+
mouse.disablePlugin(name); // disable plugin
|
|
203
|
+
mouse.togglePlugin(name); // toggle plugin
|
|
207
204
|
|
|
208
|
-
mouse.
|
|
209
|
-
mouse.start();
|
|
210
|
-
mouse.step(time);
|
|
211
|
-
mouse.destroy();
|
|
205
|
+
mouse.addHoverSelector(sel); // add hover selector at runtime
|
|
206
|
+
mouse.start(); // start animation loop
|
|
207
|
+
mouse.step(time); // advance one frame manually
|
|
208
|
+
mouse.destroy(); // destroy instance and plugins
|
|
212
209
|
```
|
|
213
210
|
|
|
214
211
|
## Browser Support
|
|
@@ -217,4 +214,4 @@ All modern browsers.
|
|
|
217
214
|
|
|
218
215
|
## License
|
|
219
216
|
|
|
220
|
-
MIT
|
|
217
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
/** Overall cursor mode. */
|
|
2
2
|
export declare type CursorMode = "auto" | "custom" | "native" | "both";
|
|
3
3
|
|
|
4
|
+
export declare const DEFAULT_HOVER_SELECTORS: string[];
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
7
|
+
* Selectors whose elements yield to the OS cursor in `"auto"` mode.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* The engine emits one wildcard rule per scope that suppresses the native
|
|
10
|
+
* cursor over every element (with `!important`, exclusions, and the probe
|
|
11
|
+
* attribute opt-out). This list is the exception: elements matching these
|
|
12
|
+
* selectors override the wildcard and let the OS cursor through.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
export declare interface CursorPolicy {
|
|
17
|
-
native: string[];
|
|
18
|
-
hide: string[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Shorthand accepted at the public API boundary. `{ native?: [...], hide?: [...] }`
|
|
23
|
-
* normalizes to the full `CursorPolicy` shape with missing keys defaulting to `[]`.
|
|
14
|
+
* `input` and `textarea` are not listed. Their UA cursor is `text`, which
|
|
15
|
+
* is not in `SUPERMOUSE_CURSORS`, so the probe already treats them as
|
|
16
|
+
* fallback candidates. `select` and `[contenteditable]` have UA cursors
|
|
17
|
+
* (`default`, `auto`) that the probe classifies as non-fallback, so they
|
|
18
|
+
* need explicit entries.
|
|
24
19
|
*/
|
|
25
|
-
export declare
|
|
26
|
-
native?: string[];
|
|
27
|
-
hide?: string[];
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export declare const DEFAULT_CURSOR_POLICY: CursorPolicy;
|
|
31
|
-
|
|
32
|
-
export declare const DEFAULT_HOVER_SELECTORS: string[];
|
|
20
|
+
export declare const DEFAULT_NATIVE_CURSOR_SELECTORS: string[];
|
|
33
21
|
|
|
34
22
|
/**
|
|
35
23
|
* Interaction state consumed by plugins.
|
|
@@ -74,6 +62,16 @@ export declare interface MouseState {
|
|
|
74
62
|
isNative: boolean;
|
|
75
63
|
/** Current cursor mode: auto, custom, native, or both. */
|
|
76
64
|
cursorMode: CursorMode;
|
|
65
|
+
/**
|
|
66
|
+
* The raw element currently under the pointer, regardless of whether it
|
|
67
|
+
* matches any hover selector.
|
|
68
|
+
*/
|
|
69
|
+
pointerTarget: HTMLElement | null;
|
|
70
|
+
/**
|
|
71
|
+
* The authored cursor value at `pointerTarget`, resolved as if
|
|
72
|
+
* Supermouse's suppression were not active.
|
|
73
|
+
*/
|
|
74
|
+
authoredCursor: string | null;
|
|
77
75
|
/** Currently hovered DOM element, if any. */
|
|
78
76
|
hoverTarget: HTMLElement | null;
|
|
79
77
|
/** User has `prefers-reduced-motion` enabled. */
|
|
@@ -116,8 +114,11 @@ export declare interface ScopeConfig {
|
|
|
116
114
|
cursor?: CursorMode;
|
|
117
115
|
/** Hover selectors for this scope. Inherits from top-level if omitted. */
|
|
118
116
|
hoverSelectors?: string[];
|
|
119
|
-
/**
|
|
120
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Selectors whose elements yield to the OS cursor in `"auto"` mode.
|
|
119
|
+
* Defaults to `DEFAULT_NATIVE_CURSOR_SELECTORS`.
|
|
120
|
+
*/
|
|
121
|
+
nativeCursorSelectors?: string[];
|
|
121
122
|
/** Plugins installed when this scope activates. */
|
|
122
123
|
plugins?: SupermousePlugin[];
|
|
123
124
|
/**
|
|
@@ -303,12 +304,21 @@ export declare class Supermouse {
|
|
|
303
304
|
private bindVisibilityHandling;
|
|
304
305
|
private startLoop;
|
|
305
306
|
/**
|
|
306
|
-
* Adds one or more hover selectors to the current scope
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
307
|
+
* Adds one or more hover selectors to the current scope's shared set.
|
|
308
|
+
*
|
|
309
|
+
* During a plugin's `install()`, writes to that plugin's owning scope.
|
|
310
|
+
* At runtime, writes to the currently active scope. All plugins in the
|
|
311
|
+
* scope share one selector set: `state.hoverTarget` is the innermost
|
|
312
|
+
* element matching any registered selector, so two plugins that
|
|
313
|
+
* register different granularities (e.g. `a` vs `p`) receive the same
|
|
314
|
+
* `hoverTarget` — the innermost match.
|
|
315
|
+
*
|
|
316
|
+
* Prefer the `hoverSelectors` option at construction for static cases,
|
|
317
|
+
* or a visual plugin's `hoverSelector` field for plugins that need
|
|
318
|
+
* their own contribution. Use this method for dynamic registration —
|
|
319
|
+
* elements that mount after construction.
|
|
310
320
|
*/
|
|
311
|
-
|
|
321
|
+
addHoverSelectors(selectors: string): void;
|
|
312
322
|
}
|
|
313
323
|
|
|
314
324
|
export declare type SupermouseInstance = Supermouse;
|
|
@@ -344,10 +354,8 @@ export declare interface SupermouseOptions {
|
|
|
344
354
|
* @default "auto"
|
|
345
355
|
*/
|
|
346
356
|
cursor?: CursorMode;
|
|
347
|
-
/**
|
|
348
|
-
|
|
349
|
-
*/
|
|
350
|
-
cursorPolicy?: CursorPolicyInput;
|
|
357
|
+
/** Native cursor selectors for this scope. Inherits from top-level if omitted. */
|
|
358
|
+
nativeCursorSelectors?: string[];
|
|
351
359
|
/**
|
|
352
360
|
* Additional scopes registered at construction. The primary scope is
|
|
353
361
|
* created from the top-level `container` / `hoverSelectors` / `plugins`
|
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
hide: [
|
|
4
|
-
"input",
|
|
5
|
-
"textarea",
|
|
6
|
-
"select",
|
|
7
|
-
"[contenteditable]",
|
|
8
|
-
"a",
|
|
9
|
-
"button",
|
|
10
|
-
'[role="button"]',
|
|
11
|
-
"[tabindex]",
|
|
12
|
-
"label"
|
|
13
|
-
]
|
|
14
|
-
};
|
|
15
|
-
function b(n) {
|
|
16
|
-
return n ? {
|
|
17
|
-
native: n.native ?? [],
|
|
18
|
-
hide: n.hide ?? []
|
|
19
|
-
} : S;
|
|
20
|
-
}
|
|
21
|
-
let y = 0;
|
|
22
|
-
class P {
|
|
1
|
+
let S = 0;
|
|
2
|
+
class b {
|
|
23
3
|
element;
|
|
24
4
|
scopeClass;
|
|
25
5
|
hideClass;
|
|
@@ -37,7 +17,7 @@ class P {
|
|
|
37
17
|
!t.isConnected && !s && console.warn(
|
|
38
18
|
"[Supermouse] container is not attached to the document — stage sizing/positioning will be wrong until it is."
|
|
39
19
|
);
|
|
40
|
-
const i =
|
|
20
|
+
const i = S++;
|
|
41
21
|
this.scopeClass = `supermouse-scope-${i}`, this.hideClass = `supermouse-hide-${i}`, this.element = document.createElement("div"), Object.assign(this.element.style, {
|
|
42
22
|
position: "absolute",
|
|
43
23
|
inset: "0px",
|
|
@@ -92,13 +72,11 @@ class P {
|
|
|
92
72
|
this._detach();
|
|
93
73
|
}
|
|
94
74
|
}
|
|
95
|
-
class
|
|
75
|
+
class y {
|
|
96
76
|
constructor(t, e) {
|
|
97
|
-
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, this.ruleEntries = t.rules ? Object.entries(t.rules) : e.ruleEntries;
|
|
98
|
-
|
|
99
|
-
this.
|
|
100
|
-
let i;
|
|
101
|
-
typeof t.container == "string" ? (this.containerSelector = t.container, this._bound = !1, i = document.createElement("div"), i.setAttribute("data-supermouse-placeholder", "")) : (this.containerSelector = null, this._bound = !0, i = t.container), this.stage = new P(i, t.zIndex ?? e.zIndex);
|
|
77
|
+
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, this.ruleEntries = t.rules ? Object.entries(t.rules) : e.ruleEntries, this.nativeSelectors = t.nativeCursorSelectors ?? e.nativeCursorSelectors;
|
|
78
|
+
let s;
|
|
79
|
+
typeof t.container == "string" ? (this.containerSelector = t.container, this._bound = !1, s = document.createElement("div"), s.setAttribute("data-supermouse-placeholder", "")) : (this.containerSelector = null, this._bound = !0, s = t.container), this.stage = new b(s, t.zIndex ?? e.zIndex);
|
|
102
80
|
}
|
|
103
81
|
config;
|
|
104
82
|
stage;
|
|
@@ -111,7 +89,6 @@ class C {
|
|
|
111
89
|
hoverSelectors;
|
|
112
90
|
plugins = [];
|
|
113
91
|
nativeSelectors;
|
|
114
|
-
hideSelectors;
|
|
115
92
|
inheritDataAttributes;
|
|
116
93
|
ruleEntries;
|
|
117
94
|
name;
|
|
@@ -159,20 +136,18 @@ class C {
|
|
|
159
136
|
return this.nativeSelectors.join(", ");
|
|
160
137
|
}
|
|
161
138
|
buildRules() {
|
|
162
|
-
const t = this.stage.getRulePrefix(), e = this.stage.getExclusion(), s = [
|
|
163
|
-
|
|
164
|
-
`${t}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
`${t} input[type="range"]${e}::-webkit-slider-thumb { cursor: none !important; }`
|
|
170
|
-
), s.push(
|
|
139
|
+
const t = this.stage.getRulePrefix(), e = this.stage.getExclusion(), s = ":not([data-sm-probe])";
|
|
140
|
+
return [
|
|
141
|
+
`${t}${e}${s} { cursor: none !important; }`,
|
|
142
|
+
`${t} *${e}${s} { cursor: none !important; }`,
|
|
143
|
+
`${t} *${e}${s} { cursor: none !important; }`,
|
|
144
|
+
// Pseudo-elements are not matched by `*`; they need explicit rules.
|
|
145
|
+
`${t} input[type="range"]${e}::-webkit-slider-thumb { cursor: none !important; }`,
|
|
171
146
|
`${t} input[type="range"]${e}::-moz-range-thumb { cursor: none !important; }`
|
|
172
|
-
|
|
147
|
+
];
|
|
173
148
|
}
|
|
174
149
|
}
|
|
175
|
-
const d = { x: -100, y: -100 },
|
|
150
|
+
const d = { x: -100, y: -100 }, C = /* @__PURE__ */ new Set([
|
|
176
151
|
"default",
|
|
177
152
|
"auto",
|
|
178
153
|
"pointer",
|
|
@@ -180,7 +155,7 @@ const d = { x: -100, y: -100 }, E = /* @__PURE__ */ new Set([
|
|
|
180
155
|
"inherit",
|
|
181
156
|
"grab",
|
|
182
157
|
"grabbing"
|
|
183
|
-
]),
|
|
158
|
+
]), P = ["select", "[contenteditable]"], E = [
|
|
184
159
|
"a",
|
|
185
160
|
"button",
|
|
186
161
|
"input",
|
|
@@ -188,9 +163,9 @@ const d = { x: -100, y: -100 }, E = /* @__PURE__ */ new Set([
|
|
|
188
163
|
"[data-hover]",
|
|
189
164
|
"[data-cursor]"
|
|
190
165
|
];
|
|
191
|
-
class
|
|
192
|
-
constructor(t, e, s, i, h,
|
|
193
|
-
this.state = t, this.options = e, this.onEnableChange = s, this.onActiveScopeChange = i, this.resolveScope = h, this.onHoverSettled =
|
|
166
|
+
class _ {
|
|
167
|
+
constructor(t, e, s, i, h, o) {
|
|
168
|
+
this.state = t, this.options = e, this.onEnableChange = s, this.onActiveScopeChange = i, this.resolveScope = h, this.onHoverSettled = o, this.dataPrefix = e.dataPrefix ?? "supermouse", this.normalizedDataPrefix = this.dataPrefix.toLowerCase(), this.ignoreAttribute = `data-${this.dataPrefix}-ignore`, this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
|
|
194
169
|
}
|
|
195
170
|
state;
|
|
196
171
|
options;
|
|
@@ -248,40 +223,40 @@ class w {
|
|
|
248
223
|
const e = this.activeScope.container, s = this.activeScope.inheritDataAttributes, i = this.normalizedDataPrefix, h = this.activeScope.ruleEntries;
|
|
249
224
|
if (t !== this.lastParsedTarget) {
|
|
250
225
|
this.lastParsedTarget = t, this.cachedChain = [];
|
|
251
|
-
let
|
|
252
|
-
for (;
|
|
226
|
+
let n = t;
|
|
227
|
+
for (; n; ) {
|
|
253
228
|
const p = [];
|
|
254
|
-
for (const [
|
|
255
|
-
this.matchesSelector(
|
|
256
|
-
if (this.cachedChain.push({ element:
|
|
257
|
-
const c =
|
|
229
|
+
for (const [r, l] of h)
|
|
230
|
+
this.matchesSelector(n, r) && p.push({ selector: r, rules: l });
|
|
231
|
+
if (this.cachedChain.push({ element: n, matchedRules: p }), !s || n === e) break;
|
|
232
|
+
const c = n.parentElement;
|
|
258
233
|
if (!c || !e.contains(c)) break;
|
|
259
|
-
|
|
234
|
+
n = c;
|
|
260
235
|
}
|
|
261
236
|
}
|
|
262
|
-
const
|
|
263
|
-
for (const { element:
|
|
237
|
+
const o = {};
|
|
238
|
+
for (const { element: n, matchedRules: p } of this.cachedChain) {
|
|
264
239
|
const c = {};
|
|
265
|
-
for (const { rules:
|
|
240
|
+
for (const { rules: r } of p)
|
|
266
241
|
try {
|
|
267
|
-
const l = typeof
|
|
242
|
+
const l = typeof r == "function" ? r(n) : r;
|
|
268
243
|
if (!l || typeof l != "object") continue;
|
|
269
244
|
for (const [v, u] of Object.entries(l))
|
|
270
|
-
c[v] = typeof u == "function" ? u(
|
|
245
|
+
c[v] = typeof u == "function" ? u(n) : u;
|
|
271
246
|
} catch (l) {
|
|
272
247
|
console.error("[Supermouse] Rule threw during evaluation:", l);
|
|
273
248
|
}
|
|
274
|
-
for (const
|
|
275
|
-
if (!
|
|
276
|
-
const l =
|
|
249
|
+
for (const r in n.dataset) {
|
|
250
|
+
if (!r.toLowerCase().startsWith(i)) continue;
|
|
251
|
+
const l = r.slice(i.length);
|
|
277
252
|
if (!l) continue;
|
|
278
|
-
const v = l[0].toLowerCase() + l.slice(1), u =
|
|
253
|
+
const v = l[0].toLowerCase() + l.slice(1), u = n.dataset[r];
|
|
279
254
|
c[v] = u == null || u === "" ? !0 : u;
|
|
280
255
|
}
|
|
281
|
-
for (const [
|
|
282
|
-
|
|
256
|
+
for (const [r, l] of Object.entries(c))
|
|
257
|
+
r in o || (o[r] = l);
|
|
283
258
|
}
|
|
284
|
-
this.state.interaction =
|
|
259
|
+
this.state.interaction = o;
|
|
285
260
|
}
|
|
286
261
|
matchesSelector(t, e) {
|
|
287
262
|
try {
|
|
@@ -290,8 +265,17 @@ class w {
|
|
|
290
265
|
return !1;
|
|
291
266
|
}
|
|
292
267
|
}
|
|
293
|
-
|
|
294
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Reads the element's authored cursor value without our own suppression
|
|
270
|
+
* interfering.
|
|
271
|
+
*/
|
|
272
|
+
resolveAuthoredCursor(t) {
|
|
273
|
+
t.setAttribute("data-sm-probe", "");
|
|
274
|
+
try {
|
|
275
|
+
return window.getComputedStyle(t).cursor;
|
|
276
|
+
} finally {
|
|
277
|
+
t.removeAttribute("data-sm-probe");
|
|
278
|
+
}
|
|
295
279
|
}
|
|
296
280
|
handleMove = (t) => {
|
|
297
281
|
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)));
|
|
@@ -317,23 +301,25 @@ class w {
|
|
|
317
301
|
this.clearHover(), this.state.isNative = !0, this.nativeTarget = t;
|
|
318
302
|
return;
|
|
319
303
|
}
|
|
320
|
-
this.state.isNative = !1, this.nativeTarget = null, this.currentTarget = t, this.
|
|
321
|
-
const e =
|
|
322
|
-
|
|
304
|
+
this.state.isNative = !1, this.nativeTarget = null, this.currentTarget = t, this.state.pointerTarget = t;
|
|
305
|
+
const e = this.resolveAuthoredCursor(t);
|
|
306
|
+
this.state.authoredCursor = e, this.parseDOMInteraction(t);
|
|
307
|
+
const s = t.closest(this.activeScope.hoverSelectorString);
|
|
308
|
+
if (s && (this.state.isHover = !0, this.state.hoverTarget = s), this.state.cursorMode === "auto") {
|
|
323
309
|
if (this.matchesSelector(t, this.activeScope.nativeSelectorString)) {
|
|
324
310
|
this.state.isNative = !0, this.nativeTarget = t;
|
|
325
311
|
return;
|
|
326
312
|
}
|
|
327
|
-
(t.isContentEditable || !
|
|
313
|
+
(t.isContentEditable || !C.has(e)) && (this.state.isNative = !0, this.nativeTarget = t);
|
|
328
314
|
}
|
|
329
315
|
}
|
|
330
316
|
handleMouseOut = (t) => {
|
|
331
317
|
if (!this.isEnabled) return;
|
|
332
318
|
const e = t.target, s = t.relatedTarget;
|
|
333
|
-
(e === this.state.hoverTarget ||
|
|
319
|
+
(e === this.state.hoverTarget || this.state.hoverTarget?.contains(e)) && (!s || !this.state.hoverTarget?.contains(s)) && (this.state.isHover = !1, this.state.hoverTarget = null), this.nativeTarget && (e === this.nativeTarget || this.nativeTarget.contains(e)) && (!s || !this.nativeTarget.contains(s)) && (this.state.isNative = !1, this.nativeTarget = null), e === this.currentTarget && (this.currentTarget = null, this.state.pointerTarget = null, this.state.authoredCursor = null, this.lastParsedTarget = null, this.cachedChain = []), !s && this.options.hideOnLeave && (this.state.hasReceivedInput = !1, this.state.pointer = { ...d });
|
|
334
320
|
};
|
|
335
321
|
clearHover() {
|
|
336
|
-
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 = [];
|
|
322
|
+
this.state.isHover = !1, this.state.hoverTarget = null, this.state.isNative = !1, this.nativeTarget = null, this.state.interaction = {}, this.currentTarget = null, this.state.pointerTarget = null, this.state.authoredCursor = null, this.lastParsedTarget = null, this.cachedChain = [];
|
|
337
323
|
}
|
|
338
324
|
getCurrentTarget() {
|
|
339
325
|
return this.currentTarget;
|
|
@@ -346,27 +332,27 @@ class w {
|
|
|
346
332
|
this.abortController.abort(), this.cachedChain = [];
|
|
347
333
|
}
|
|
348
334
|
}
|
|
349
|
-
let
|
|
335
|
+
let w = 0;
|
|
350
336
|
const f = /* @__PURE__ */ new Map();
|
|
351
|
-
function
|
|
352
|
-
return
|
|
337
|
+
function x() {
|
|
338
|
+
return w++;
|
|
353
339
|
}
|
|
354
|
-
function T(
|
|
355
|
-
let e = f.get(
|
|
356
|
-
e || (e = document.createElement("style"), e.id = `supermouse-styles-${
|
|
340
|
+
function T(a, t) {
|
|
341
|
+
let e = f.get(a);
|
|
342
|
+
e || (e = document.createElement("style"), e.id = `supermouse-styles-${a}`, document.head.appendChild(e), f.set(a, e)), e.textContent = t.join(`
|
|
357
343
|
`);
|
|
358
344
|
}
|
|
359
|
-
function
|
|
360
|
-
f.get(
|
|
345
|
+
function M(a) {
|
|
346
|
+
f.get(a)?.remove(), f.delete(a);
|
|
361
347
|
}
|
|
362
|
-
const g = "2.5.0-beta.
|
|
363
|
-
function
|
|
364
|
-
return
|
|
348
|
+
const g = "2.5.0-beta.5";
|
|
349
|
+
function A(a, t, e) {
|
|
350
|
+
return a + (t - a) * e;
|
|
365
351
|
}
|
|
366
|
-
function m(
|
|
367
|
-
return
|
|
352
|
+
function m(a, t, e, s) {
|
|
353
|
+
return A(a, t, 1 - Math.exp(-e * s));
|
|
368
354
|
}
|
|
369
|
-
class
|
|
355
|
+
class D {
|
|
370
356
|
static version = g;
|
|
371
357
|
version = g;
|
|
372
358
|
state;
|
|
@@ -395,7 +381,7 @@ class I {
|
|
|
395
381
|
zIndex: 9999,
|
|
396
382
|
inheritDataAttributes: !0,
|
|
397
383
|
...t
|
|
398
|
-
}, this.styleOwner =
|
|
384
|
+
}, this.styleOwner = x(), this.state = {
|
|
399
385
|
pointer: { ...d },
|
|
400
386
|
target: { ...d },
|
|
401
387
|
smooth: { ...d },
|
|
@@ -406,7 +392,9 @@ class I {
|
|
|
406
392
|
isHover: !1,
|
|
407
393
|
isNative: !1,
|
|
408
394
|
cursorMode: this.options.cursor,
|
|
395
|
+
pointerTarget: null,
|
|
409
396
|
hoverTarget: null,
|
|
397
|
+
authoredCursor: null,
|
|
410
398
|
reducedMotion: !1,
|
|
411
399
|
hasReceivedInput: !1,
|
|
412
400
|
shape: null,
|
|
@@ -417,12 +405,12 @@ class I {
|
|
|
417
405
|
container: this.options.container,
|
|
418
406
|
cursor: this.options.cursor,
|
|
419
407
|
hoverSelectors: this.options.hoverSelectors,
|
|
420
|
-
|
|
408
|
+
nativeCursorSelectors: this.options.nativeCursorSelectors,
|
|
421
409
|
plugins: this.options.plugins,
|
|
422
410
|
rules: this.options.rules,
|
|
423
411
|
zIndex: this.options.zIndex
|
|
424
412
|
});
|
|
425
|
-
if (this.input = new
|
|
413
|
+
if (this.input = new _(
|
|
426
414
|
this.state,
|
|
427
415
|
this.options,
|
|
428
416
|
(s) => {
|
|
@@ -541,7 +529,7 @@ class I {
|
|
|
541
529
|
}
|
|
542
530
|
t.stage.destroy();
|
|
543
531
|
}
|
|
544
|
-
this._scopes = [], this.scopeByName.clear(), this.scopeHandles.clear(), this._activeScope = null, this.state.scope = null,
|
|
532
|
+
this._scopes = [], this.scopeByName.clear(), this.scopeHandles.clear(), this._activeScope = null, this.state.scope = null, M(this.styleOwner);
|
|
545
533
|
}
|
|
546
534
|
// ─── Internal ───
|
|
547
535
|
_running = !1;
|
|
@@ -556,10 +544,10 @@ class I {
|
|
|
556
544
|
break;
|
|
557
545
|
}
|
|
558
546
|
}
|
|
559
|
-
const e = new
|
|
547
|
+
const e = new y(t, {
|
|
560
548
|
cursor: this.options.cursor,
|
|
561
|
-
hoverSelectors: this.options.hoverSelectors ??
|
|
562
|
-
|
|
549
|
+
hoverSelectors: this.options.hoverSelectors ?? E,
|
|
550
|
+
nativeCursorSelectors: this.options.nativeCursorSelectors ?? P,
|
|
563
551
|
zIndex: this.options.zIndex,
|
|
564
552
|
inheritDataAttributes: this.options.inheritDataAttributes,
|
|
565
553
|
ruleEntries: this.options.rules ? Object.entries(this.options.rules) : []
|
|
@@ -692,20 +680,20 @@ class I {
|
|
|
692
680
|
this._installingScope = null, t.plugins.push(e), t.plugins.sort((s, i) => (s.priority ?? 0) - (i.priority ?? 0)), (t !== this._activeScope || !t.active) && e.element && (e.element.style.display = "none");
|
|
693
681
|
}
|
|
694
682
|
removePluginFromScope(t, e) {
|
|
695
|
-
const s = t.plugins.findIndex((
|
|
683
|
+
const s = t.plugins.findIndex((n) => n.name === e);
|
|
696
684
|
if (s === -1) return;
|
|
697
685
|
const i = t.plugins[s];
|
|
698
686
|
t.plugins.splice(s, 1);
|
|
699
687
|
const h = () => {
|
|
700
688
|
try {
|
|
701
689
|
i.onDisable?.(this), i.destroy?.(this);
|
|
702
|
-
} catch (
|
|
703
|
-
console.error(`[Supermouse] Plugin '${e}' cleanup threw:`,
|
|
690
|
+
} catch (n) {
|
|
691
|
+
console.error(`[Supermouse] Plugin '${e}' cleanup threw:`, n);
|
|
704
692
|
}
|
|
705
693
|
i.element?.remove();
|
|
706
|
-
},
|
|
707
|
-
|
|
708
|
-
console.error(`[Supermouse] Plugin '${e}' onBeforeDisable threw:`,
|
|
694
|
+
}, o = i.onBeforeDisable?.(this);
|
|
695
|
+
o && typeof o.then == "function" ? Promise.resolve(o).then(h).catch((n) => {
|
|
696
|
+
console.error(`[Supermouse] Plugin '${e}' onBeforeDisable threw:`, n), h();
|
|
709
697
|
}) : h();
|
|
710
698
|
}
|
|
711
699
|
handleActiveScopeChange(t) {
|
|
@@ -798,14 +786,14 @@ class I {
|
|
|
798
786
|
const h = this._activeScope;
|
|
799
787
|
if (h) {
|
|
800
788
|
h.stage.setVisibility(this.resolveStageVisibility()), this.input.isEnabled && h.stage.setNativeCursor(this.resolveCursorState());
|
|
801
|
-
for (let
|
|
802
|
-
this.runPluginSafe(h.plugins[
|
|
789
|
+
for (let o = 0; o < h.plugins.length; o++)
|
|
790
|
+
this.runPluginSafe(h.plugins[o], e);
|
|
803
791
|
}
|
|
804
792
|
if (this.cleanupCrashedPlugins(), this.input.isEnabled) {
|
|
805
|
-
const
|
|
806
|
-
this.state.smooth.x = m(this.state.smooth.x, this.state.target.x,
|
|
807
|
-
const { x: c, y:
|
|
808
|
-
(Math.abs(c) > 0.1 || Math.abs(
|
|
793
|
+
const o = this.state.reducedMotion ? 1e3 : 1 / this.options.smoothness * 2, n = this.state.smooth.x, p = this.state.smooth.y;
|
|
794
|
+
this.state.smooth.x = m(this.state.smooth.x, this.state.target.x, o, s), this.state.smooth.y = m(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 - n) / s, this.state.velocity.y = (this.state.smooth.y - p) / s) : (this.state.velocity.x = 0, this.state.velocity.y = 0);
|
|
795
|
+
const { x: c, y: r } = this.state.velocity;
|
|
796
|
+
(Math.abs(c) > 0.1 || Math.abs(r) > 0.1) && (this.state.angle = Math.atan2(r, c) * (180 / Math.PI));
|
|
809
797
|
}
|
|
810
798
|
}
|
|
811
799
|
tick = (t) => {
|
|
@@ -824,12 +812,21 @@ class I {
|
|
|
824
812
|
this._running || (this._running = !0, !document.hidden && (this.lastTime = performance.now(), this.rafId = requestAnimationFrame(this.tick)));
|
|
825
813
|
}
|
|
826
814
|
/**
|
|
827
|
-
* Adds one or more hover selectors to the current scope
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
815
|
+
* Adds one or more hover selectors to the current scope's shared set.
|
|
816
|
+
*
|
|
817
|
+
* During a plugin's `install()`, writes to that plugin's owning scope.
|
|
818
|
+
* At runtime, writes to the currently active scope. All plugins in the
|
|
819
|
+
* scope share one selector set: `state.hoverTarget` is the innermost
|
|
820
|
+
* element matching any registered selector, so two plugins that
|
|
821
|
+
* register different granularities (e.g. `a` vs `p`) receive the same
|
|
822
|
+
* `hoverTarget` — the innermost match.
|
|
823
|
+
*
|
|
824
|
+
* Prefer the `hoverSelectors` option at construction for static cases,
|
|
825
|
+
* or a visual plugin's `hoverSelector` field for plugins that need
|
|
826
|
+
* their own contribution. Use this method for dynamic registration —
|
|
827
|
+
* elements that mount after construction.
|
|
831
828
|
*/
|
|
832
|
-
|
|
829
|
+
addHoverSelectors(t) {
|
|
833
830
|
const e = this._installingScope ?? this._activeScope ?? this._scopes[0];
|
|
834
831
|
if (e)
|
|
835
832
|
for (const s of t.split(",")) {
|
|
@@ -839,7 +836,7 @@ class I {
|
|
|
839
836
|
}
|
|
840
837
|
}
|
|
841
838
|
export {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
839
|
+
E as DEFAULT_HOVER_SELECTORS,
|
|
840
|
+
P as DEFAULT_NATIVE_CURSOR_SELECTORS,
|
|
841
|
+
D as Supermouse
|
|
845
842
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(u,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(u=typeof globalThis<"u"?globalThis:u||self,p(u.SupermouseCore={}))})(this,(function(u){"use strict";const p={native:["input","textarea","select","[contenteditable]"],hide:["input","textarea","select","[contenteditable]","a","button",'[role="button"]',"[tabindex]","label"]};function S(n){return n?{native:n.native??[],hide:n.hide??[]}:p}let P=0;class E{element;scopeClass;hideClass;container;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";_visible=!0;constructor(t,e){if(!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const s=t.hasAttribute("data-supermouse-placeholder");!t.isConnected&&!s&&console.warn("[Supermouse] container is not attached to the document — stage sizing/positioning will be wrong until it is.");const i=P++;this.scopeClass=`supermouse-scope-${i}`,this.hideClass=`supermouse-hide-${i}`,this.element=document.createElement("div"),Object.assign(this.element.style,{position:"absolute",inset:"0px",pointerEvents:"none",zIndex:String(e),opacity:"1",transition:"opacity 0.15s ease"}),this.container=t,this._attach(t)}_attach(t){const e=t===document.body;if(this.element.style.position=e?"fixed":"absolute",e)this.originalContainerPosition="";else{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),t.classList.add("supermouse-scope",this.scopeClass)}_detach(){const t=this.container;t.classList.remove("supermouse-scope",this.scopeClass,this.hideClass),t!==document.body&&this.originalContainerPosition==="static"&&(t.style.position=""),t.style.cursor=this.originalContainerCursor,this.element.remove()}attach(t){if(this.container===t)return;const e=this.currentCursorState;this._detach(),this.container=t,this._attach(t),this.currentCursorState=null,e&&this.setNativeCursor(e)}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){t!==this._visible&&(this._visible=t,this.element.style.opacity=t?"1":"0")}setNativeCursor(t){t!==this.currentCursorState&&(this.currentCursorState=t,this.container.classList.toggle(this.hideClass,t==="none"))}destroy(){this._detach()}}class _{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,this.ruleEntries=t.rules?Object.entries(t.rules):e.ruleEntries;const s=t.cursorPolicy?S(t.cursorPolicy):e.cursorPolicy;this.nativeSelectors=s.native,this.hideSelectors=s.hide;let i;typeof t.container=="string"?(this.containerSelector=t.container,this._bound=!1,i=document.createElement("div"),i.setAttribute("data-supermouse-placeholder","")):(this.containerSelector=null,this._bound=!0,i=t.container),this.stage=new E(i,t.zIndex??e.zIndex)}config;stage;containerSelector;hoverSelectors;plugins=[];nativeSelectors;hideSelectors;inheritDataAttributes;ruleEntries;name;cursorMode;active=!0;_bound;get container(){return this.stage.containerElement}get resolved(){return this._bound}match(t){if(this.containerSelector===null)return this.stage.containerElement===t;try{return t.matches(this.containerSelector)}catch{return!1}}bind(t){this.containerSelector!==null&&(this._bound&&this.stage.containerElement===t||(this.stage.attach(t),this._bound=!0))}contains(t){return this._bound&&this.stage.containerElement.contains(t)}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 i of this.hideSelectors)s.push(`${t} ${i}${e} { cursor: none !important; }`);return 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 f={x:-100,y:-100},w=new Set(["default","auto","pointer","none","inherit","grab","grabbing"]),b=["a","button","input","textarea","[data-hover]","[data-cursor]"];class x{constructor(t,e,s,i,h,r){this.state=t,this.options=e,this.onEnableChange=s,this.onActiveScopeChange=i,this.resolveScope=h,this.onHoverSettled=r,this.dataPrefix=e.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;onEnableChange;onActiveScopeChange;resolveScope;onHoverSettled;abortController=new AbortController;mediaQueryList;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;activeScope=null;nativeTarget=null;currentTarget=null;lastParsedTarget=null;hasSeenPointer=!1;isEnabled=!0;viewportX=0;viewportY=0;cachedChain=[];setActiveScope(t){t!==this.activeScope&&(this.activeScope=t,this.lastParsedTarget=null,this.cachedChain=[],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))}applyPointerToState(){const t=this.activeScope?.container??document.body;if(t===document.body){this.state.pointer.x=this.viewportX,this.state.pointer.y=this.viewportY;return}const e=t.getBoundingClientRect();this.state.pointer.x=this.viewportX-e.left,this.state.pointer.y=this.viewportY-e.top}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){if(!this.activeScope)return;const e=this.activeScope.container,s=this.activeScope.inheritDataAttributes,i=this.normalizedDataPrefix,h=this.activeScope.ruleEntries;if(t!==this.lastParsedTarget){this.lastParsedTarget=t,this.cachedChain=[];let o=t;for(;o;){const v=[];for(const[a,l]of h)this.matchesSelector(o,a)&&v.push({selector:a,rules:l});if(this.cachedChain.push({element:o,matchedRules:v}),!s||o===e)break;const c=o.parentElement;if(!c||!e.contains(c))break;o=c}}const r={};for(const{element:o,matchedRules:v}of this.cachedChain){const c={};for(const{rules:a}of v)try{const l=typeof a=="function"?a(o):a;if(!l||typeof l!="object")continue;for(const[m,d]of Object.entries(l))c[m]=typeof d=="function"?d(o):d}catch(l){console.error("[Supermouse] Rule threw during evaluation:",l)}for(const a in o.dataset){if(!a.toLowerCase().startsWith(i))continue;const l=a.slice(i.length);if(!l)continue;const m=l[0].toLowerCase()+l.slice(1),d=o.dataset[a];c[m]=d==null||d===""?!0:d}for(const[a,l]of Object.entries(c))a in r||(r[a]=l)}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.resolveScope(e);s&&s!==this.activeScope&&this.setActiveScope(s),this.activeScope&&(this.settleHoverState(e),this.onHoverSettled())};isPointerInside(t){if(!this.hasSeenPointer)return!1;const e=t.getBoundingClientRect();return this.viewportX>=e.left&&this.viewportX<=e.right&&this.viewportY>=e.top&&this.viewportY<=e.bottom}settleHoverState(t){if(this.state.cursorMode==="auto"&&t.closest(`[${this.ignoreAttribute}]`)){this.clearHover(),this.state.isNative=!0,this.nativeTarget=t;return}this.state.isNative=!1,this.nativeTarget=null,this.currentTarget=t,this.parseDOMInteraction(t);const e=t.closest(this.activeScope.hoverSelectorString);if(e&&(this.state.isHover=!0,this.state.hoverTarget=e),this.state.cursorMode==="auto"){if(this.matchesSelector(t,this.activeScope.nativeSelectorString)){this.state.isNative=!0,this.nativeTarget=t;return}(t.isContentEditable||!w.has(this.resolveComputedCursor(t)))&&(this.state.isNative=!0,this.nativeTarget=t)}}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={...f})};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.cachedChain=[]}}let T=0;const g=new Map;function M(){return T++}function A(n,t){let e=g.get(n);e||(e=document.createElement("style"),e.id=`supermouse-styles-${n}`,document.head.appendChild(e),g.set(n,e)),e.textContent=t.join(`
|
|
2
|
-
`)}function D(n){g.get(n)?.remove(),g.delete(n)}const y="2.5.0-beta.3";function I(n,t,e){return n+(t-n)*e}function C(n,t,e,s){return I(n,t,1-Math.exp(-e*s))}class O{static version=y;version=y;state;options;_scopes=[];_activeScope=null;_installingScope=null;scopeByName=new Map;scopeHandles=new Map;input;styleOwner;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.styleOwner=M(),this.state={pointer:{...f},target:{...f},smooth:{...f},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:{},scope:null};const e=this.createScope({container:this.options.container,cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors,cursorPolicy:this.options.cursorPolicy,plugins:this.options.plugins,rules:this.options.rules,zIndex:this.options.zIndex});if(this.input=new x(this.state,this.options,s=>{s||this.reset()},s=>this.handleActiveScopeChange(s),s=>this.resolveScopeForNode(s),()=>this.applyCursorNow()),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(i=>i.name===t);if(s)return s}}getScope(t){const e=this.scopeByName.get(t);return e?this.scopeHandles.get(e):void 0}enablePlugin(t){const e=this.getPlugin(t);if(!e||e.isEnabled!==!1)return;e.isEnabled=!0;const s=this.findScopeForPlugin(e);s&&s===this._activeScope&&s.active&&this.scopeActivatePlugin(e)}disablePlugin(t){const e=this.getPlugin(t);if(!e||e.isEnabled===!1)return;const s=this.findScopeForPlugin(e);s!==null&&s===this._activeScope&&s.active?this.deactivatePlugin(e):e.isEnabled=!1}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}setCursor(t){this._activeScope&&this.setScopeCursor(this._activeScope,t)}addScope(t){const e=this.createScope(t);return this.installPlugins(e),this.rebuildStylesheet(),this.scopeHandles.get(e)}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={...f},this.state.smooth={...f},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(s){console.error(s)}t.stage.destroy()}this._scopes=[],this.scopeByName.clear(),this.scopeHandles.clear(),this._activeScope=null,this.state.scope=null,D(this.styleOwner)}_running=!1;createScope(t){if(typeof t.container!="string"){const s=t.container;for(const i of this._scopes)if(i.containerSelector===null&&i.container===s){console.warn("[Supermouse] A scope is already registered for this container. The previous scope will no longer activate.");break}}const e=new _(t,{cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors??b,cursorPolicy:this.options.cursorPolicy?S(this.options.cursorPolicy):p,zIndex:this.options.zIndex,inheritDataAttributes:this.options.inheritDataAttributes,ruleEntries:this.options.rules?Object.entries(this.options.rules):[]});return this._scopes.push(e),e.name&&this.scopeByName.set(e.name,e),this.scopeHandles.set(e,this.buildScopeHandle(e)),e}buildScopeHandle(t){const e={get name(){return t.name},get container(){return t.resolved?t.container:null},get active(){return t.active},get resolved(){return t.resolved},destroy:()=>this.destroyScope(t),setCursor:s=>this.setScopeCursor(t,s),deactivate:()=>this.deactivateScope(t),activate:()=>this.activateScope(t),use:s=>(this.installPlugin(t,s),e),removePlugin:s=>this.removePluginFromScope(t,s),getPlugin:s=>t.plugins.find(i=>i.name===s)};return e}destroyScope(t){const e=this._scopes.indexOf(t);if(e!==-1){for(const s of t.plugins)try{s.destroy?.(this)}catch(i){console.error(i)}if(t.plugins.length=0,t.stage.destroy(),this._scopes.splice(e,1),t.name&&this.scopeByName.delete(t.name),this.scopeHandles.delete(t),this._activeScope===t){const s=this.resolveScopeForNode(t.container.parentElement)??this._scopes.find(i=>i.active)??null;this.input.setActiveScope(s),this.input.clearHover(),this.applyCursorNow()}this.rebuildStylesheet()}}setScopeCursor(t,e){t.cursorMode=e,t===this._activeScope&&(this.state.cursorMode=e,this.applyCursorNow())}deactivateScope(t){if(t.active&&(t.active=!1,this._activeScope===t)){const e=this.resolveScopeForNode(t.container.parentElement)??this._scopes.find(s=>s.active)??null;this.input.setActiveScope(e),this.input.clearHover(),this.applyCursorNow()}}activateScope(t){if(t.active=!0,this._activeScope!==t&&this.input.hasSeenPointer){if(t.containerSelector!==null&&!t.resolved){const e=document.querySelectorAll(t.containerSelector);for(const s of e)if(this.input.isPointerInside(s)){t.bind(s);break}}t.resolved&&this.input.isPointerInside(t.container)&&(this.input.setActiveScope(t),this.applyCursorNow())}}resolveScopeForNode(t){for(let e=t;e;e=e.parentElement)for(const s of this._scopes)if(s.active&&s.match(e))return s.bind(e),s;return null}applyCursorNow(){const t=this._activeScope;t&&(t.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&t.stage.setNativeCursor(this.resolveCursorState()))}findScopeForPlugin(t){for(const e of this._scopes)if(e.plugins.includes(t))return e;return null}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)){console.warn(`[Supermouse] Plugin "${e.name}" already installed.`);return}e.isEnabled??=!0,this._installingScope=t;try{e.install?.(this)}catch(s){console.error(`[Supermouse] Failed to install plugin '${e.name}'.`,s),this._installingScope=null;return}this._installingScope=null,t.plugins.push(e),t.plugins.sort((s,i)=>(s.priority??0)-(i.priority??0)),(t!==this._activeScope||!t.active)&&e.element&&(e.element.style.display="none")}removePluginFromScope(t,e){const s=t.plugins.findIndex(o=>o.name===e);if(s===-1)return;const i=t.plugins[s];t.plugins.splice(s,1);const h=()=>{try{i.onDisable?.(this),i.destroy?.(this)}catch(o){console.error(`[Supermouse] Plugin '${e}' cleanup threw:`,o)}i.element?.remove()},r=i.onBeforeDisable?.(this);r&&typeof r.then=="function"?Promise.resolve(r).then(h).catch(o=>{console.error(`[Supermouse] Plugin '${e}' onBeforeDisable threw:`,o),h()}):h()}handleActiveScopeChange(t){const e=this._activeScope;if(e!==t){if(e)for(const s of e.plugins)s.isEnabled!==!1&&this.scopeDeactivatePlugin(s);if(this._activeScope=t,t){this.state.cursorMode=t.cursorMode,this.state.scope={name:t.name,container:t.container};for(const s of t.plugins)s.isEnabled!==!1&&this.scopeActivatePlugin(s)}else this.state.scope=null}}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(i=>{console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`,i),e()}):e()}scopeDeactivatePlugin(t){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(i=>{console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`,i),e()}):e()}scopeActivatePlugin(t){t.element&&(t.element.style.display=""),t.onEnable?.(this)}rebuildStylesheet(){const t=[":where(.supermouse-scope .supermouse-scope) { cursor: auto }"];for(const e of this._scopes)t.push(...e.buildRules());A(this.styleOwner,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){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(e){console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`,e)}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 i=this.input.getCurrentTarget();i&&!i.isConnected?this.input.clearHover():i&&this.input.parseDOMInteraction(i),this.input.isEnabled&&this.state.hasReceivedInput&&(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y);const h=this._activeScope;if(h){h.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&h.stage.setNativeCursor(this.resolveCursorState());for(let r=0;r<h.plugins.length;r++)this.runPluginSafe(h.plugins[r],e)}if(this.cleanupCrashedPlugins(),this.input.isEnabled){const r=this.state.reducedMotion?1e3:1/this.options.smoothness*2,o=this.state.smooth.x,v=this.state.smooth.y;this.state.smooth.x=C(this.state.smooth.x,this.state.target.x,r,s),this.state.smooth.y=C(this.state.smooth.y,this.state.target.y,r,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-v)/s):(this.state.velocity.x=0,this.state.velocity.y=0);const{x:c,y:a}=this.state.velocity;(Math.abs(c)>.1||Math.abs(a)>.1)&&(this.state.angle=Math.atan2(a,c)*(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 i=s.trim();i&&e.hoverSelectors.add(i)}}}u.DEFAULT_CURSOR_POLICY=p,u.DEFAULT_HOVER_SELECTORS=b,u.Supermouse=O,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(u,v){typeof exports=="object"&&typeof module<"u"?v(exports):typeof define=="function"&&define.amd?define(["exports"],v):(u=typeof globalThis<"u"?globalThis:u||self,v(u.SupermouseCore={}))})(this,(function(u){"use strict";let v=0;class E{element;scopeClass;hideClass;container;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";_visible=!0;constructor(t,e){if(!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const s=t.hasAttribute("data-supermouse-placeholder");!t.isConnected&&!s&&console.warn("[Supermouse] container is not attached to the document — stage sizing/positioning will be wrong until it is.");const i=v++;this.scopeClass=`supermouse-scope-${i}`,this.hideClass=`supermouse-hide-${i}`,this.element=document.createElement("div"),Object.assign(this.element.style,{position:"absolute",inset:"0px",pointerEvents:"none",zIndex:String(e),opacity:"1",transition:"opacity 0.15s ease"}),this.container=t,this._attach(t)}_attach(t){const e=t===document.body;if(this.element.style.position=e?"fixed":"absolute",e)this.originalContainerPosition="";else{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),t.classList.add("supermouse-scope",this.scopeClass)}_detach(){const t=this.container;t.classList.remove("supermouse-scope",this.scopeClass,this.hideClass),t!==document.body&&this.originalContainerPosition==="static"&&(t.style.position=""),t.style.cursor=this.originalContainerCursor,this.element.remove()}attach(t){if(this.container===t)return;const e=this.currentCursorState;this._detach(),this.container=t,this._attach(t),this.currentCursorState=null,e&&this.setNativeCursor(e)}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){t!==this._visible&&(this._visible=t,this.element.style.opacity=t?"1":"0")}setNativeCursor(t){t!==this.currentCursorState&&(this.currentCursorState=t,this.container.classList.toggle(this.hideClass,t==="none"))}destroy(){this._detach()}}class P{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,this.ruleEntries=t.rules?Object.entries(t.rules):e.ruleEntries,this.nativeSelectors=t.nativeCursorSelectors??e.nativeCursorSelectors;let s;typeof t.container=="string"?(this.containerSelector=t.container,this._bound=!1,s=document.createElement("div"),s.setAttribute("data-supermouse-placeholder","")):(this.containerSelector=null,this._bound=!0,s=t.container),this.stage=new E(s,t.zIndex??e.zIndex)}config;stage;containerSelector;hoverSelectors;plugins=[];nativeSelectors;inheritDataAttributes;ruleEntries;name;cursorMode;active=!0;_bound;get container(){return this.stage.containerElement}get resolved(){return this._bound}match(t){if(this.containerSelector===null)return this.stage.containerElement===t;try{return t.matches(this.containerSelector)}catch{return!1}}bind(t){this.containerSelector!==null&&(this._bound&&this.stage.containerElement===t||(this.stage.attach(t),this._bound=!0))}contains(t){return this._bound&&this.stage.containerElement.contains(t)}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=":not([data-sm-probe])";return[`${t}${e}${s} { cursor: none !important; }`,`${t} *${e}${s} { cursor: none !important; }`,`${t} *${e}${s} { cursor: none !important; }`,`${t} input[type="range"]${e}::-webkit-slider-thumb { cursor: none !important; }`,`${t} input[type="range"]${e}::-moz-range-thumb { cursor: none !important; }`]}}const d={x:-100,y:-100},_=new Set(["default","auto","pointer","none","inherit","grab","grabbing"]),S=["select","[contenteditable]"],b=["a","button","input","textarea","[data-hover]","[data-cursor]"];class w{constructor(t,e,s,i,h,o){this.state=t,this.options=e,this.onEnableChange=s,this.onActiveScopeChange=i,this.resolveScope=h,this.onHoverSettled=o,this.dataPrefix=e.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;onEnableChange;onActiveScopeChange;resolveScope;onHoverSettled;abortController=new AbortController;mediaQueryList;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;activeScope=null;nativeTarget=null;currentTarget=null;lastParsedTarget=null;hasSeenPointer=!1;isEnabled=!0;viewportX=0;viewportY=0;cachedChain=[];setActiveScope(t){t!==this.activeScope&&(this.activeScope=t,this.lastParsedTarget=null,this.cachedChain=[],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))}applyPointerToState(){const t=this.activeScope?.container??document.body;if(t===document.body){this.state.pointer.x=this.viewportX,this.state.pointer.y=this.viewportY;return}const e=t.getBoundingClientRect();this.state.pointer.x=this.viewportX-e.left,this.state.pointer.y=this.viewportY-e.top}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){if(!this.activeScope)return;const e=this.activeScope.container,s=this.activeScope.inheritDataAttributes,i=this.normalizedDataPrefix,h=this.activeScope.ruleEntries;if(t!==this.lastParsedTarget){this.lastParsedTarget=t,this.cachedChain=[];let n=t;for(;n;){const f=[];for(const[r,l]of h)this.matchesSelector(n,r)&&f.push({selector:r,rules:l});if(this.cachedChain.push({element:n,matchedRules:f}),!s||n===e)break;const c=n.parentElement;if(!c||!e.contains(c))break;n=c}}const o={};for(const{element:n,matchedRules:f}of this.cachedChain){const c={};for(const{rules:r}of f)try{const l=typeof r=="function"?r(n):r;if(!l||typeof l!="object")continue;for(const[m,p]of Object.entries(l))c[m]=typeof p=="function"?p(n):p}catch(l){console.error("[Supermouse] Rule threw during evaluation:",l)}for(const r in n.dataset){if(!r.toLowerCase().startsWith(i))continue;const l=r.slice(i.length);if(!l)continue;const m=l[0].toLowerCase()+l.slice(1),p=n.dataset[r];c[m]=p==null||p===""?!0:p}for(const[r,l]of Object.entries(c))r in o||(o[r]=l)}this.state.interaction=o}matchesSelector(t,e){try{return t.matches(e)}catch{return!1}}resolveAuthoredCursor(t){t.setAttribute("data-sm-probe","");try{return window.getComputedStyle(t).cursor}finally{t.removeAttribute("data-sm-probe")}}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.resolveScope(e);s&&s!==this.activeScope&&this.setActiveScope(s),this.activeScope&&(this.settleHoverState(e),this.onHoverSettled())};isPointerInside(t){if(!this.hasSeenPointer)return!1;const e=t.getBoundingClientRect();return this.viewportX>=e.left&&this.viewportX<=e.right&&this.viewportY>=e.top&&this.viewportY<=e.bottom}settleHoverState(t){if(this.state.cursorMode==="auto"&&t.closest(`[${this.ignoreAttribute}]`)){this.clearHover(),this.state.isNative=!0,this.nativeTarget=t;return}this.state.isNative=!1,this.nativeTarget=null,this.currentTarget=t,this.state.pointerTarget=t;const e=this.resolveAuthoredCursor(t);this.state.authoredCursor=e,this.parseDOMInteraction(t);const s=t.closest(this.activeScope.hoverSelectorString);if(s&&(this.state.isHover=!0,this.state.hoverTarget=s),this.state.cursorMode==="auto"){if(this.matchesSelector(t,this.activeScope.nativeSelectorString)){this.state.isNative=!0,this.nativeTarget=t;return}(t.isContentEditable||!_.has(e))&&(this.state.isNative=!0,this.nativeTarget=t)}}handleMouseOut=t=>{if(!this.isEnabled)return;const e=t.target,s=t.relatedTarget;(e===this.state.hoverTarget||this.state.hoverTarget?.contains(e))&&(!s||!this.state.hoverTarget?.contains(s))&&(this.state.isHover=!1,this.state.hoverTarget=null),this.nativeTarget&&(e===this.nativeTarget||this.nativeTarget.contains(e))&&(!s||!this.nativeTarget.contains(s))&&(this.state.isNative=!1,this.nativeTarget=null),e===this.currentTarget&&(this.currentTarget=null,this.state.pointerTarget=null,this.state.authoredCursor=null,this.lastParsedTarget=null,this.cachedChain=[]),!s&&this.options.hideOnLeave&&(this.state.hasReceivedInput=!1,this.state.pointer={...d})};clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1,this.nativeTarget=null,this.state.interaction={},this.currentTarget=null,this.state.pointerTarget=null,this.state.authoredCursor=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.cachedChain=[]}}let x=0;const g=new Map;function T(){return x++}function M(a,t){let e=g.get(a);e||(e=document.createElement("style"),e.id=`supermouse-styles-${a}`,document.head.appendChild(e),g.set(a,e)),e.textContent=t.join(`
|
|
2
|
+
`)}function A(a){g.get(a)?.remove(),g.delete(a)}const y="2.5.0-beta.5";function D(a,t,e){return a+(t-a)*e}function C(a,t,e,s){return D(a,t,1-Math.exp(-e*s))}class I{static version=y;version=y;state;options;_scopes=[];_activeScope=null;_installingScope=null;scopeByName=new Map;scopeHandles=new Map;input;styleOwner;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.styleOwner=T(),this.state={pointer:{...d},target:{...d},smooth:{...d},velocity:{x:0,y:0},displacement:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,cursorMode:this.options.cursor,pointerTarget:null,hoverTarget:null,authoredCursor:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{},scope:null};const e=this.createScope({container:this.options.container,cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors,nativeCursorSelectors:this.options.nativeCursorSelectors,plugins:this.options.plugins,rules:this.options.rules,zIndex:this.options.zIndex});if(this.input=new w(this.state,this.options,s=>{s||this.reset()},s=>this.handleActiveScopeChange(s),s=>this.resolveScopeForNode(s),()=>this.applyCursorNow()),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(i=>i.name===t);if(s)return s}}getScope(t){const e=this.scopeByName.get(t);return e?this.scopeHandles.get(e):void 0}enablePlugin(t){const e=this.getPlugin(t);if(!e||e.isEnabled!==!1)return;e.isEnabled=!0;const s=this.findScopeForPlugin(e);s&&s===this._activeScope&&s.active&&this.scopeActivatePlugin(e)}disablePlugin(t){const e=this.getPlugin(t);if(!e||e.isEnabled===!1)return;const s=this.findScopeForPlugin(e);s!==null&&s===this._activeScope&&s.active?this.deactivatePlugin(e):e.isEnabled=!1}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}setCursor(t){this._activeScope&&this.setScopeCursor(this._activeScope,t)}addScope(t){const e=this.createScope(t);return this.installPlugins(e),this.rebuildStylesheet(),this.scopeHandles.get(e)}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={...d},this.state.smooth={...d},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(s){console.error(s)}t.stage.destroy()}this._scopes=[],this.scopeByName.clear(),this.scopeHandles.clear(),this._activeScope=null,this.state.scope=null,A(this.styleOwner)}_running=!1;createScope(t){if(typeof t.container!="string"){const s=t.container;for(const i of this._scopes)if(i.containerSelector===null&&i.container===s){console.warn("[Supermouse] A scope is already registered for this container. The previous scope will no longer activate.");break}}const e=new P(t,{cursor:this.options.cursor,hoverSelectors:this.options.hoverSelectors??b,nativeCursorSelectors:this.options.nativeCursorSelectors??S,zIndex:this.options.zIndex,inheritDataAttributes:this.options.inheritDataAttributes,ruleEntries:this.options.rules?Object.entries(this.options.rules):[]});return this._scopes.push(e),e.name&&this.scopeByName.set(e.name,e),this.scopeHandles.set(e,this.buildScopeHandle(e)),e}buildScopeHandle(t){const e={get name(){return t.name},get container(){return t.resolved?t.container:null},get active(){return t.active},get resolved(){return t.resolved},destroy:()=>this.destroyScope(t),setCursor:s=>this.setScopeCursor(t,s),deactivate:()=>this.deactivateScope(t),activate:()=>this.activateScope(t),use:s=>(this.installPlugin(t,s),e),removePlugin:s=>this.removePluginFromScope(t,s),getPlugin:s=>t.plugins.find(i=>i.name===s)};return e}destroyScope(t){const e=this._scopes.indexOf(t);if(e!==-1){for(const s of t.plugins)try{s.destroy?.(this)}catch(i){console.error(i)}if(t.plugins.length=0,t.stage.destroy(),this._scopes.splice(e,1),t.name&&this.scopeByName.delete(t.name),this.scopeHandles.delete(t),this._activeScope===t){const s=this.resolveScopeForNode(t.container.parentElement)??this._scopes.find(i=>i.active)??null;this.input.setActiveScope(s),this.input.clearHover(),this.applyCursorNow()}this.rebuildStylesheet()}}setScopeCursor(t,e){t.cursorMode=e,t===this._activeScope&&(this.state.cursorMode=e,this.applyCursorNow())}deactivateScope(t){if(t.active&&(t.active=!1,this._activeScope===t)){const e=this.resolveScopeForNode(t.container.parentElement)??this._scopes.find(s=>s.active)??null;this.input.setActiveScope(e),this.input.clearHover(),this.applyCursorNow()}}activateScope(t){if(t.active=!0,this._activeScope!==t&&this.input.hasSeenPointer){if(t.containerSelector!==null&&!t.resolved){const e=document.querySelectorAll(t.containerSelector);for(const s of e)if(this.input.isPointerInside(s)){t.bind(s);break}}t.resolved&&this.input.isPointerInside(t.container)&&(this.input.setActiveScope(t),this.applyCursorNow())}}resolveScopeForNode(t){for(let e=t;e;e=e.parentElement)for(const s of this._scopes)if(s.active&&s.match(e))return s.bind(e),s;return null}applyCursorNow(){const t=this._activeScope;t&&(t.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&t.stage.setNativeCursor(this.resolveCursorState()))}findScopeForPlugin(t){for(const e of this._scopes)if(e.plugins.includes(t))return e;return null}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)){console.warn(`[Supermouse] Plugin "${e.name}" already installed.`);return}e.isEnabled??=!0,this._installingScope=t;try{e.install?.(this)}catch(s){console.error(`[Supermouse] Failed to install plugin '${e.name}'.`,s),this._installingScope=null;return}this._installingScope=null,t.plugins.push(e),t.plugins.sort((s,i)=>(s.priority??0)-(i.priority??0)),(t!==this._activeScope||!t.active)&&e.element&&(e.element.style.display="none")}removePluginFromScope(t,e){const s=t.plugins.findIndex(n=>n.name===e);if(s===-1)return;const i=t.plugins[s];t.plugins.splice(s,1);const h=()=>{try{i.onDisable?.(this),i.destroy?.(this)}catch(n){console.error(`[Supermouse] Plugin '${e}' cleanup threw:`,n)}i.element?.remove()},o=i.onBeforeDisable?.(this);o&&typeof o.then=="function"?Promise.resolve(o).then(h).catch(n=>{console.error(`[Supermouse] Plugin '${e}' onBeforeDisable threw:`,n),h()}):h()}handleActiveScopeChange(t){const e=this._activeScope;if(e!==t){if(e)for(const s of e.plugins)s.isEnabled!==!1&&this.scopeDeactivatePlugin(s);if(this._activeScope=t,t){this.state.cursorMode=t.cursorMode,this.state.scope={name:t.name,container:t.container};for(const s of t.plugins)s.isEnabled!==!1&&this.scopeActivatePlugin(s)}else this.state.scope=null}}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(i=>{console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`,i),e()}):e()}scopeDeactivatePlugin(t){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(i=>{console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`,i),e()}):e()}scopeActivatePlugin(t){t.element&&(t.element.style.display=""),t.onEnable?.(this)}rebuildStylesheet(){const t=[":where(.supermouse-scope .supermouse-scope) { cursor: auto }"];for(const e of this._scopes)t.push(...e.buildRules());M(this.styleOwner,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){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(e){console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`,e)}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 i=this.input.getCurrentTarget();i&&!i.isConnected?this.input.clearHover():i&&this.input.parseDOMInteraction(i),this.input.isEnabled&&this.state.hasReceivedInput&&(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y);const h=this._activeScope;if(h){h.stage.setVisibility(this.resolveStageVisibility()),this.input.isEnabled&&h.stage.setNativeCursor(this.resolveCursorState());for(let o=0;o<h.plugins.length;o++)this.runPluginSafe(h.plugins[o],e)}if(this.cleanupCrashedPlugins(),this.input.isEnabled){const o=this.state.reducedMotion?1e3:1/this.options.smoothness*2,n=this.state.smooth.x,f=this.state.smooth.y;this.state.smooth.x=C(this.state.smooth.x,this.state.target.x,o,s),this.state.smooth.y=C(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-n)/s,this.state.velocity.y=(this.state.smooth.y-f)/s):(this.state.velocity.x=0,this.state.velocity.y=0);const{x:c,y:r}=this.state.velocity;(Math.abs(c)>.1||Math.abs(r)>.1)&&(this.state.angle=Math.atan2(r,c)*(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)))}addHoverSelectors(t){const e=this._installingScope??this._activeScope??this._scopes[0];if(e)for(const s of t.split(",")){const i=s.trim();i&&e.hoverSelectors.add(i)}}}u.DEFAULT_HOVER_SELECTORS=b,u.DEFAULT_NATIVE_CURSOR_SELECTORS=S,u.Supermouse=I,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|