@supermousejs/core 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8dc8e06: cleanup and robustness pass: better case-insensitive data-\* handling, safer plugin lifecycle behavior, more reliable cursor restoration, and a more consistent reset/update flow
8
+ - 600de13: added `dataPrefix` option for customizable data attribute handling, added a `start()` public method to manually start the raf loop and enhanced supermouse's resistance to bugs
9
+
10
+ ### Patch Changes
11
+
12
+ - b72e264: Renamed `setCursor` to `setNativeCursor` for simplicity and removed redundant checks on `ignoreOnNative`
13
+
3
14
  ## 2.2.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -28,21 +28,21 @@ export declare interface MousePosition {
28
28
  }
29
29
 
30
30
  export declare interface MouseState {
31
- /** The raw position of the input pointer (mouse/touch). */
31
+ /** The raw position from the latest pointer event, before smoothing is applied. */
32
32
  pointer: MousePosition;
33
- /** The target position the cursor logic wants to reach. */
33
+ /** The current goal position that the core loop is driving toward. */
34
34
  target: MousePosition;
35
35
  /** The smoothed/interpolated position used for rendering. */
36
36
  smooth: MousePosition;
37
- /** The current velocity vector of the smooth position. */
37
+ /** The current movement vector derived from the smoothed state. */
38
38
  velocity: MousePosition;
39
- /** The angle of movement in degrees. Calculated from velocity. */
39
+ /** The current movement angle in degrees, derived from velocity. */
40
40
  angle: number;
41
41
  /** Whether the pointer is currently pressed down. */
42
42
  isDown: boolean;
43
43
  /** Whether the pointer is currently hovering over a registered interactive element. */
44
44
  isHover: boolean;
45
- /** Whether the native cursor is currently forced visible by internal logic (e.g. input elements). */
45
+ /** Whether the runtime has temporarily restored the native cursor due to native-input heuristics. */
46
46
  isNative: boolean;
47
47
  /**
48
48
  * If set, this overrides all auto-detection logic.
@@ -131,11 +131,11 @@ export declare class Supermouse {
131
131
  */
132
132
  get container(): HTMLDivElement;
133
133
  /**
134
- * Manually override the native cursor visibility.
134
+ * Sets the native cursor visibility.
135
135
  *
136
- * @param type 'auto' (Show Native), 'none' (Hide Native), or null (Resume Auto-detection)
136
+ * @param mode
137
137
  */
138
- setCursor(type: "auto" | "none" | null): void;
138
+ setNativeCursor(mode: "hide" | "show" | "auto"): void;
139
139
  private init;
140
140
  enable(): void;
141
141
  disable(): void;
@@ -145,9 +145,13 @@ export declare class Supermouse {
145
145
  * @param plugin - The plugin object to install.
146
146
  */
147
147
  use(plugin: SupermousePlugin): this;
148
- private resetCoords;
149
- private resetPosition;
148
+ private reset;
150
149
  private startLoop;
150
+ /**
151
+ * Starts the animation loop. This is automatically called if `autoStart` is true.
152
+ * Plugins can call this method to resume the loop if it has been stopped.
153
+ */
154
+ start(): void;
151
155
  /**
152
156
  * Manually steps the animation loop.
153
157
  *
@@ -191,20 +195,24 @@ export declare interface SupermouseOptions {
191
195
  autoDisableOnMobile?: boolean;
192
196
  /**
193
197
  * Strategy for detecting when to fallback to the native cursor.
194
- * - `true` / `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
198
+ * - `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
195
199
  * - `'tag'`: Checks only semantic tags like <input>, <textarea> (Fastest, prevents layout thrashing).
196
200
  * - `'css'`: Checks only computed CSS cursor styles (Slow, triggers reflow).
197
- * - `false`: Never fallback to native cursor.
201
+ * - `null`: Never fallback to native cursor.
202
+ *
198
203
  * @default 'auto'
199
204
  */
200
- ignoreOnNative?: boolean | NativeIgnoreStrategy;
205
+ ignoreOnNative?: NativeIgnoreStrategy | null;
201
206
  /**
202
- * Whether to hide the native cursor via global CSS injection.
207
+ * Whether to hide the native cursor via scoped CSS injection on the container.
208
+ * When enabled, the stage toggles `cursor: none` on the configured container and
209
+ * on registered hover targets.
203
210
  * @default true
204
211
  */
205
212
  hideCursor?: boolean;
206
213
  /**
207
- * Whether to hide the custom cursor when the mouse leaves the browser window.
214
+ * Whether to hide the custom cursor when the pointer leaves the browser viewport.
215
+ * When enabled the runtime clears the cursor back to an off-screen position to avoid stale hover state.
208
216
  * @default true
209
217
  */
210
218
  hideOnLeave?: boolean;
@@ -224,14 +232,24 @@ export declare interface SupermouseOptions {
224
232
  autoStart?: boolean;
225
233
  /**
226
234
  * Semantic rules mapping CSS selectors to interaction state.
235
+ * Rules are evaluated against hovered elements and merged into `state.interaction`.
236
+ * Matching data attributes on the same element are also read and can override or enrich the final object.
227
237
  * @example { 'button': { icon: 'pointer' } }
228
238
  */
229
239
  rules?: Record<string, InteractionState>;
230
240
  /**
231
241
  * Custom strategy to resolve interaction state from a hovered element.
232
- * Overrides the default data-attribute scraping.
242
+ * When provided, this callback bypasses the default `rules` + `data-[prefix]-*` scraping and
243
+ * returns the interaction payload directly for the current hover target.
233
244
  */
234
245
  resolveInteraction?: (target: HTMLElement) => InteractionState;
246
+ /**
247
+ * The prefix used for data attributes to store hover metadata.
248
+ * For example, if dataPrefix is "supermouse", then the attribute would be "data-supermouse-*".
249
+ * This allows for multiple instances of Supermouse to coexist without conflicting data attributes.
250
+ * @default "supermouse"
251
+ */
252
+ dataPrefix?: string;
235
253
  }
236
254
 
237
255
  /**
@@ -246,7 +264,7 @@ export declare interface SupermousePlugin {
246
264
  isEnabled?: boolean;
247
265
  /** Called when `app.use()` is executed. */
248
266
  install?: (instance: Supermouse) => void;
249
- /** Called on every animation frame. */
267
+ /** Called on every animation frame with the frame delta time in milliseconds. */
250
268
  update?: (instance: Supermouse, deltaTime: number) => void;
251
269
  /** Called when the plugin is removed or the app is destroyed. */
252
270
  destroy?: (instance: Supermouse) => void;
@@ -258,6 +276,7 @@ export declare interface SupermousePlugin {
258
276
 
259
277
  /**
260
278
  * Allows a property to be a static value or a function that returns the value based on state.
279
+ * This is primarily useful for plugin option definitions that should react to the current runtime state.
261
280
  */
262
281
  export declare type ValueOrGetter<T> = T | ((state: MouseState) => T);
263
282
 
package/dist/index.mjs CHANGED
@@ -1,15 +1,13 @@
1
- function c(n, t, e) {
1
+ const d = "2.3.0";
2
+ function p(n, t, e) {
2
3
  return n + (t - n) * e;
3
4
  }
4
- function d(n, t, e, i) {
5
- return c(n, t, 1 - Math.exp(-e * i));
6
- }
7
- function p(n, t) {
8
- return Math.atan2(t, n) * (180 / Math.PI);
5
+ function c(n, t, e, i) {
6
+ return p(n, t, 1 - Math.exp(-e * i));
9
7
  }
10
8
  class f {
11
- constructor(t, e, i, o) {
12
- this.state = t, this.options = e, this.getHoverSelector = i, this.onEnableChange = o, this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
9
+ constructor(t, e, i, a) {
10
+ this.state = t, this.options = e, this.getHoverSelector = i, this.onEnableChange = a, this.dataPrefix = this.options.dataPrefix ?? "supermouse", this.normalizedDataPrefix = this.dataPrefix.toLowerCase(), this.ignoreAttribute = `data-${this.dataPrefix}-ignore`, this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
13
11
  }
14
12
  state;
15
13
  options;
@@ -18,6 +16,9 @@ class f {
18
16
  mediaQueryList;
19
17
  mediaQueryHandler;
20
18
  motionQuery;
19
+ dataPrefix;
20
+ normalizedDataPrefix;
21
+ ignoreAttribute;
21
22
  /**
22
23
  * Master switch for input processing.
23
24
  * Toggled by `Supermouse.enable()`/`disable()` or automatically by device capability checks.
@@ -52,30 +53,30 @@ class f {
52
53
  }
53
54
  parseDOMInteraction(t) {
54
55
  if (this.options.resolveInteraction) {
55
- this.state.interaction = this.options.resolveInteraction(t);
56
+ this.state.interaction = this.options.resolveInteraction(t) || {};
56
57
  return;
57
58
  }
58
59
  const e = {};
59
60
  if (this.options.rules)
60
- for (const [o, s] of Object.entries(this.options.rules))
61
- t.matches(o) && Object.assign(e, s);
62
- const i = t.dataset;
63
- for (const o in i)
64
- if (o.startsWith("supermouse")) {
65
- const s = o.slice(10);
66
- if (s) {
67
- const r = s.charAt(0).toLowerCase() + s.slice(1), a = i[o];
68
- a !== void 0 && (e[r] = a === "" ? !0 : a);
69
- }
70
- }
61
+ for (const [s, o] of Object.entries(this.options.rules))
62
+ t.matches(s) && Object.assign(e, o);
63
+ const i = this.normalizedDataPrefix, a = this.dataPrefix.length;
64
+ for (const s in t.dataset) {
65
+ if (!s.toLowerCase().startsWith(i)) continue;
66
+ const o = s.slice(a);
67
+ if (!o) continue;
68
+ const r = t.dataset[s];
69
+ e[o[0].toLowerCase() + o.slice(1)] = r === "" ? !0 : r;
70
+ }
71
71
  this.state.interaction = e;
72
72
  }
73
73
  handleMove(t) {
74
- if (!this.isEnabled || this.options.autoDisableOnMobile && t.pointerType === "touch") return;
74
+ if (!this.isEnabled || this.options.autoDisableOnMobile && t.pointerType === "touch" && !this.options.enableTouch)
75
+ return;
75
76
  let e = t.clientX, i = t.clientY;
76
77
  if (this.options.container && this.options.container !== document.body) {
77
- const o = this.options.container.getBoundingClientRect();
78
- e -= o.left, i -= o.top;
78
+ const a = this.options.container.getBoundingClientRect();
79
+ e -= a.left, i -= a.top;
79
80
  }
80
81
  this.state.pointer.x = e, this.state.pointer.y = i, this.state.hasReceivedInput || (this.state.hasReceivedInput = !0, this.state.target.x = this.state.smooth.x = e, this.state.target.y = this.state.smooth.y = i);
81
82
  }
@@ -88,25 +89,25 @@ class f {
88
89
  handleMouseOver(t) {
89
90
  if (!this.isEnabled) return;
90
91
  const e = t.target;
91
- if (e.closest("[data-supermouse-ignore]")) {
92
+ if (e.closest(`[${this.ignoreAttribute}]`)) {
92
93
  this.state.isNative = !0;
93
94
  return;
94
95
  }
95
- const i = this.getHoverSelector(), o = e.closest(i);
96
- o && (this.state.isHover = !0, this.state.hoverTarget = o, this.parseDOMInteraction(this.state.hoverTarget));
96
+ const i = this.getHoverSelector(), a = e.closest(i);
97
+ a && (this.state.isHover = !0, this.state.hoverTarget = a, this.parseDOMInteraction(this.state.hoverTarget));
97
98
  const s = this.options.ignoreOnNative;
98
- if (s) {
99
- const r = s === !0 || s === "auto" || s === "tag", a = s === !0 || s === "auto" || s === "css";
100
- let l = !1;
101
- if (r) {
99
+ if (s && s !== null) {
100
+ const o = s === "auto" || s === "tag", r = s === "auto" || s === "css";
101
+ let u = !1;
102
+ if (o) {
102
103
  const h = e.localName;
103
- (h === "input" || h === "textarea" || h === "select" || e.isContentEditable) && (l = !0);
104
+ (h === "input" || h === "textarea" || h === "select" || e.isContentEditable) && (u = !0);
104
105
  }
105
- if (!l && a) {
106
+ if (!u && r) {
106
107
  const h = window.getComputedStyle(e).cursor;
107
- ["default", "auto", "pointer", "none", "inherit"].includes(h) || (l = !0);
108
+ ["default", "auto", "pointer", "none", "inherit"].includes(h) || (u = !0);
108
109
  }
109
- l && (this.state.isNative = !0);
110
+ u && (this.state.isNative = !0);
110
111
  }
111
112
  }
112
113
  handleMouseOut(t) {
@@ -115,10 +116,10 @@ class f {
115
116
  (e === this.state.hoverTarget || e.contains(this.state.hoverTarget)) && (!t.relatedTarget || !this.state.hoverTarget?.contains(t.relatedTarget)) && (this.state.isHover = !1, this.state.hoverTarget = null, this.state.interaction = {}), this.state.isNative && (this.state.isNative = !1);
116
117
  }
117
118
  handleWindowLeave() {
118
- this.options.hideOnLeave && (this.state.hasReceivedInput = !1);
119
+ this.options.hideOnLeave && (this.state.hasReceivedInput = !1, this.state.pointer = { ...l });
119
120
  }
120
121
  clearHover() {
121
- this.state.isHover = !1, this.state.hoverTarget = null, this.state.isNative = !1;
122
+ this.state.isHover = !1, this.state.hoverTarget = null, this.state.isNative = !1, this.state.interaction = {};
122
123
  }
123
124
  bindEvents() {
124
125
  const { signal: t } = this.abortController;
@@ -135,18 +136,19 @@ class v {
135
136
  throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);
136
137
  const i = g++;
137
138
  this.id = `supermouse-style-${i}`, this.scopeClass = `supermouse-scope-${i}`;
138
- const o = t === document.body;
139
- this.element = document.createElement("div"), Object.assign(this.element.style, {
140
- position: o ? "fixed" : "absolute",
141
- top: "0",
142
- left: "0",
143
- width: "100%",
144
- height: "100%",
139
+ const a = t === document.body;
140
+ if (this.element = document.createElement("div"), Object.assign(this.element.style, {
141
+ position: a ? "fixed" : "absolute",
142
+ inset: "0px",
145
143
  pointerEvents: "none",
146
144
  zIndex: "9999",
147
145
  opacity: "1",
148
146
  transition: "opacity 0.15s ease"
149
- }), o || window.getComputedStyle(t).position === "static" && (t.style.position = "relative"), t.appendChild(this.element), this.styleTag = document.createElement("style"), this.styleTag.id = this.id, document.head.appendChild(this.styleTag), this.container.classList.add(this.scopeClass), this.hideNativeCursor && this.setNativeCursor("none");
147
+ }), !a) {
148
+ const s = window.getComputedStyle(t);
149
+ this.originalContainerPosition = s.position, s.position === "static" && (t.style.position = "relative");
150
+ }
151
+ this.originalContainerCursor = t.style.cursor, t.appendChild(this.element), this.styleTag = document.createElement("style"), this.styleTag.id = this.id, document.head.appendChild(this.styleTag), this.container.classList.add(this.scopeClass), this.hideNativeCursor && this.setNativeCursor("none");
150
152
  }
151
153
  container;
152
154
  hideNativeCursor;
@@ -156,6 +158,8 @@ class v {
156
158
  id;
157
159
  scopeClass;
158
160
  currentCursorState = null;
161
+ originalContainerPosition = "";
162
+ originalContainerCursor = "";
159
163
  selectors = /* @__PURE__ */ new Set([
160
164
  "a",
161
165
  "button",
@@ -197,10 +201,10 @@ class v {
197
201
  `;
198
202
  }
199
203
  destroy() {
200
- this.element.remove(), this.styleTag.remove(), this.container.style.cursor = "", this.container.classList.remove(this.scopeClass);
204
+ this.element.remove(), this.styleTag.remove(), this.container.style.cursor = this.originalContainerCursor, this.container.classList.remove(this.scopeClass), this.container !== document.body && this.originalContainerPosition === "static" && (this.container.style.position = "");
201
205
  }
202
206
  }
203
- const u = { x: -100, y: -100 }, m = [
207
+ const l = { x: -100, y: -100 }, m = [
204
208
  "a",
205
209
  "button",
206
210
  "input",
@@ -209,8 +213,8 @@ const u = { x: -100, y: -100 }, m = [
209
213
  "[data-cursor]"
210
214
  ];
211
215
  class y {
212
- static version = "2.2.0";
213
- version = "2.2.0";
216
+ static version = d;
217
+ version = d;
214
218
  state;
215
219
  /**
216
220
  * Configuration options.
@@ -239,8 +243,9 @@ class y {
239
243
  hideOnLeave: !0,
240
244
  autoStart: !0,
241
245
  container: document.body,
246
+ dataPrefix: "supermouse",
242
247
  ...t
243
- }, this.options.container || (this.options.container = document.body), this.state = {
248
+ }, this.state = {
244
249
  pointer: { x: -100, y: -100 },
245
250
  target: { x: -100, y: -100 },
246
251
  smooth: { x: -100, y: -100 },
@@ -260,7 +265,7 @@ class y {
260
265
  this.options,
261
266
  () => Array.from(this.hoverSelectors).join(", "),
262
267
  (e) => {
263
- e || this.resetPosition();
268
+ e || this.reset(!0);
264
269
  }
265
270
  ), this.options.plugins && this.options.plugins.forEach((e) => this.use(e)), this.init();
266
271
  }
@@ -309,12 +314,12 @@ class y {
309
314
  return this.stage.element;
310
315
  }
311
316
  /**
312
- * Manually override the native cursor visibility.
317
+ * Sets the native cursor visibility.
313
318
  *
314
- * @param type 'auto' (Show Native), 'none' (Hide Native), or null (Resume Auto-detection)
319
+ * @param mode
315
320
  */
316
- setCursor(t) {
317
- this.state.forcedCursor = t;
321
+ setNativeCursor(t) {
322
+ this.state.forcedCursor = t === "auto" ? null : t === "hide" ? "none" : "auto";
318
323
  }
319
324
  init() {
320
325
  this.options.autoStart && this.startLoop();
@@ -323,7 +328,7 @@ class y {
323
328
  this.input.isEnabled = !0, this.stage.setNativeCursor("none");
324
329
  }
325
330
  disable() {
326
- this.input.isEnabled = !1, this.stage.setNativeCursor("auto"), this.resetPosition();
331
+ this.input.isEnabled = !1, this.stage.setNativeCursor("auto"), this.reset(!0);
327
332
  }
328
333
  /**
329
334
  * Registers a new plugin.
@@ -331,17 +336,29 @@ class y {
331
336
  * @param plugin - The plugin object to install.
332
337
  */
333
338
  use(t) {
334
- return this.plugins.find((e) => e.name === t.name) ? (console.warn(`[Supermouse] Plugin "${t.name}" already installed.`), this) : (t.isEnabled === void 0 && (t.isEnabled = !0), this.plugins.push(t), this.plugins.sort((e, i) => (e.priority || 0) - (i.priority || 0)), t.install?.(this), this);
335
- }
336
- resetCoords() {
337
- this.state.pointer = { ...u }, this.state.target = { ...u }, this.state.smooth = { ...u }, this.state.velocity = { x: 0, y: 0 }, this.state.angle = 0;
339
+ if (this.plugins.some((i) => i.name === t.name))
340
+ return console.warn(`[Supermouse] Plugin "${t.name}" already installed.`), this;
341
+ t.isEnabled === void 0 && (t.isEnabled = !0);
342
+ try {
343
+ t.install?.(this);
344
+ } catch (i) {
345
+ return console.error(`[Supermouse] Failed to install plugin '${t.name}'.`, i), this;
346
+ }
347
+ return this.plugins.push(t), this.plugins.sort((i, a) => (i.priority || 0) - (a.priority || 0)), this;
338
348
  }
339
- resetPosition() {
340
- this.resetCoords(), this.state.hasReceivedInput = !1, this.state.shape = null, this.state.interaction = {};
349
+ reset(t = !1) {
350
+ this.state.pointer = { ...l }, this.state.target = { ...l }, this.state.smooth = { ...l }, this.state.velocity = { x: 0, y: 0 }, this.state.angle = 0, t && (this.state.hasReceivedInput = !1, this.state.shape = null, this.state.interaction = {});
341
351
  }
342
352
  startLoop() {
343
353
  this.isRunning || (this.isRunning = !0, this.lastTime = performance.now(), this.tick(this.lastTime));
344
354
  }
355
+ /**
356
+ * Starts the animation loop. This is automatically called if `autoStart` is true.
357
+ * Plugins can call this method to resume the loop if it has been stopped.
358
+ */
359
+ start() {
360
+ this.startLoop();
361
+ }
345
362
  /**
346
363
  * Manually steps the animation loop.
347
364
  *
@@ -355,11 +372,13 @@ class y {
355
372
  try {
356
373
  t.update?.(this, e);
357
374
  } catch (i) {
358
- console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`, i), t.isEnabled = !1;
375
+ console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`, i);
376
+ const a = this.plugins.indexOf(t);
377
+ a > -1 && this.plugins.splice(a, 1), t.isEnabled = !1;
359
378
  try {
360
- t.onDisable?.(this);
361
- } catch (o) {
362
- console.error(`[Supermouse] Failed to execute onDisable for plugin '${t.name}'.`, o);
379
+ t.destroy?.(this), t.onDisable?.(this);
380
+ } catch (s) {
381
+ console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`, s);
363
382
  }
364
383
  }
365
384
  }
@@ -369,21 +388,21 @@ class y {
369
388
  tick = (t) => {
370
389
  const e = t - this.lastTime, i = Math.min(e / 1e3, 0.1);
371
390
  this.lastTime = t, this.state.hoverTarget && !this.state.hoverTarget.isConnected && this.input.clearHover();
372
- const o = this.input.isEnabled && !this.state.isNative && this.state.hasReceivedInput;
373
- if (this.stage.setVisibility(o), this.input.isEnabled && this.options.hideCursor) {
391
+ const a = this.input.isEnabled && !this.state.isNative && this.state.hasReceivedInput;
392
+ if (this.stage.setVisibility(a), this.input.isEnabled && this.options.hideCursor) {
374
393
  let s = "auto";
375
394
  this.state.forcedCursor !== null ? s = this.state.forcedCursor : s = this.state.isNative || !this.state.hasReceivedInput ? "auto" : "none", this.stage.setNativeCursor(s);
376
395
  }
377
- this.input.isEnabled ? (this.state.target.x = this.state.pointer.x, this.state.target.y = this.state.pointer.y) : this.resetCoords();
396
+ this.input.isEnabled && this.state.hasReceivedInput ? (this.state.target.x = this.state.pointer.x, this.state.target.y = this.state.pointer.y) : this.state.target = { ...l };
378
397
  for (let s = 0; s < this.plugins.length; s++)
379
398
  this.runPluginSafe(this.plugins[s], e);
380
399
  if (this.input.isEnabled) {
381
400
  const s = this.state.reducedMotion ? 1e3 : 1 / this.options.smoothness * 2;
382
- this.state.smooth.x = d(this.state.smooth.x, this.state.target.x, s, i), this.state.smooth.y = d(this.state.smooth.y, this.state.target.y, s, i);
383
- const r = this.state.target.x - this.state.smooth.x, a = this.state.target.y - this.state.smooth.y;
384
- this.state.velocity.x = r, this.state.velocity.y = a, (Math.abs(r) > 0.1 || Math.abs(a) > 0.1) && (this.state.angle = p(r, a));
401
+ this.state.smooth.x = c(this.state.smooth.x, this.state.target.x, s, i), this.state.smooth.y = c(this.state.smooth.y, this.state.target.y, s, i), this.state.velocity.x = this.state.target.x - this.state.smooth.x, this.state.velocity.y = this.state.target.y - this.state.smooth.y;
402
+ const { x: o, y: r } = this.state.velocity;
403
+ (Math.abs(o) > 0.1 || Math.abs(r) > 0.1) && (this.state.angle = Math.atan2(r, o) * (180 / Math.PI));
385
404
  }
386
- this.options.autoStart && this.isRunning && (this.rafId = requestAnimationFrame(this.tick));
405
+ this.isRunning && (this.rafId = requestAnimationFrame(this.tick));
387
406
  };
388
407
  /**
389
408
  * Destroys the instance.
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
- (function(n,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(n=typeof globalThis<"u"?globalThis:n||self,l(n.SupermouseCore={}))})(this,(function(n){"use strict";function l(a,t,e){return a+(t-a)*e}function p(a,t,e,i){return l(a,t,1-Math.exp(-e*i))}function m(a,t){return Math.atan2(t,a)*(180/Math.PI)}class f{constructor(t,e,i,o){this.state=t,this.options=e,this.getHoverSelector=i,this.onEnableChange=o,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;getHoverSelector;onEnableChange;mediaQueryList;mediaQueryHandler;motionQuery;isEnabled=!0;abortController=new AbortController;checkDeviceCapability(){this.options.autoDisableOnMobile&&(this.mediaQueryList=window.matchMedia("(pointer: fine)"),this.updateEnabledState(this.mediaQueryList.matches),this.mediaQueryHandler=t=>{this.updateEnabledState(t.matches)},this.mediaQueryList.addEventListener("change",this.mediaQueryHandler,{signal:this.abortController.signal}))}checkMotionPreference(){this.motionQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.state.reducedMotion=this.motionQuery.matches,this.motionQuery.addEventListener("change",t=>{this.state.reducedMotion=t.matches},{signal:this.abortController.signal})}updateEnabledState(t){this.isEnabled=t,this.onEnableChange(t)}parseDOMInteraction(t){if(this.options.resolveInteraction){this.state.interaction=this.options.resolveInteraction(t);return}const e={};if(this.options.rules)for(const[o,s]of Object.entries(this.options.rules))t.matches(o)&&Object.assign(e,s);const i=t.dataset;for(const o in i)if(o.startsWith("supermouse")){const s=o.slice(10);if(s){const h=s.charAt(0).toLowerCase()+s.slice(1),r=i[o];r!==void 0&&(e[h]=r===""?!0:r)}}this.state.interaction=e}handleMove(t){if(!this.isEnabled||this.options.autoDisableOnMobile&&t.pointerType==="touch")return;let e=t.clientX,i=t.clientY;if(this.options.container&&this.options.container!==document.body){const o=this.options.container.getBoundingClientRect();e-=o.left,i-=o.top}this.state.pointer.x=e,this.state.pointer.y=i,this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=e,this.state.target.y=this.state.smooth.y=i)}handleDown(){this.isEnabled&&(this.state.isDown=!0)}handleUp(){this.isEnabled&&(this.state.isDown=!1)}handleMouseOver(t){if(!this.isEnabled)return;const e=t.target;if(e.closest("[data-supermouse-ignore]")){this.state.isNative=!0;return}const i=this.getHoverSelector(),o=e.closest(i);o&&(this.state.isHover=!0,this.state.hoverTarget=o,this.parseDOMInteraction(this.state.hoverTarget));const s=this.options.ignoreOnNative;if(s){const h=s===!0||s==="auto"||s==="tag",r=s===!0||s==="auto"||s==="css";let d=!1;if(h){const u=e.localName;(u==="input"||u==="textarea"||u==="select"||e.isContentEditable)&&(d=!0)}if(!d&&r){const u=window.getComputedStyle(e).cursor;["default","auto","pointer","none","inherit"].includes(u)||(d=!0)}d&&(this.state.isNative=!0)}}handleMouseOut(t){if(!this.isEnabled)return;const e=t.target;(e===this.state.hoverTarget||e.contains(this.state.hoverTarget))&&(!t.relatedTarget||!this.state.hoverTarget?.contains(t.relatedTarget))&&(this.state.isHover=!1,this.state.hoverTarget=null,this.state.interaction={}),this.state.isNative&&(this.state.isNative=!1)}handleWindowLeave(){this.options.hideOnLeave&&(this.state.hasReceivedInput=!1)}clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1}bindEvents(){const{signal:t}=this.abortController;window.addEventListener("pointermove",this.handleMove.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerdown",this.handleDown.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerup",this.handleUp.bind(this),{signal:t}),document.addEventListener("mouseover",this.handleMouseOver.bind(this),{signal:t}),document.addEventListener("mouseout",this.handleMouseOut.bind(this),{signal:t}),document.addEventListener("mouseleave",this.handleWindowLeave.bind(this),{signal:t})}destroy(){this.abortController.abort()}}let b=0;class g{constructor(t=document.body,e){if(this.container=t,this.hideNativeCursor=e,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const i=b++;this.id=`supermouse-style-${i}`,this.scopeClass=`supermouse-scope-${i}`;const o=t===document.body;this.element=document.createElement("div"),Object.assign(this.element.style,{position:o?"fixed":"absolute",top:"0",left:"0",width:"100%",height:"100%",pointerEvents:"none",zIndex:"9999",opacity:"1",transition:"opacity 0.15s ease"}),o||window.getComputedStyle(t).position==="static"&&(t.style.position="relative"),t.appendChild(this.element),this.styleTag=document.createElement("style"),this.styleTag.id=this.id,document.head.appendChild(this.styleTag),this.container.classList.add(this.scopeClass),this.hideNativeCursor&&this.setNativeCursor("none")}container;hideNativeCursor;element;styleTag;id;scopeClass;currentCursorState=null;selectors=new Set(["a","button","input","textarea","select",'[role="button"]',"[tabindex]"]);addSelector(t){this.selectors.add(t),this.hideNativeCursor&&this.updateCursorCSS()}setVisibility(t){this.element.style.opacity=t?"1":"0"}setNativeCursor(t){!this.hideNativeCursor&&t==="none"||t!==this.currentCursorState&&(this.currentCursorState=t,t==="none"?(this.container.style.cursor="none",this.updateCursorCSS()):(this.container.style.cursor="",this.styleTag.innerText=""))}updateCursorCSS(){const t=Array.from(this.selectors);if(t.length===0){this.styleTag.innerText="";return}const e=t.map(i=>`.${this.scopeClass} ${i}`).join(", ");this.styleTag.innerText=`
1
+ (function(a,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(a=typeof globalThis<"u"?globalThis:a||self,l(a.SupermouseCore={}))})(this,(function(a){"use strict";const l="2.3.0";function m(r,t,e){return r+(t-r)*e}function p(r,t,e,i){return m(r,t,1-Math.exp(-e*i))}class f{constructor(t,e,i,n){this.state=t,this.options=e,this.getHoverSelector=i,this.onEnableChange=n,this.dataPrefix=this.options.dataPrefix??"supermouse",this.normalizedDataPrefix=this.dataPrefix.toLowerCase(),this.ignoreAttribute=`data-${this.dataPrefix}-ignore`,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;getHoverSelector;onEnableChange;mediaQueryList;mediaQueryHandler;motionQuery;dataPrefix;normalizedDataPrefix;ignoreAttribute;isEnabled=!0;abortController=new AbortController;checkDeviceCapability(){this.options.autoDisableOnMobile&&(this.mediaQueryList=window.matchMedia("(pointer: fine)"),this.updateEnabledState(this.mediaQueryList.matches),this.mediaQueryHandler=t=>{this.updateEnabledState(t.matches)},this.mediaQueryList.addEventListener("change",this.mediaQueryHandler,{signal:this.abortController.signal}))}checkMotionPreference(){this.motionQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.state.reducedMotion=this.motionQuery.matches,this.motionQuery.addEventListener("change",t=>{this.state.reducedMotion=t.matches},{signal:this.abortController.signal})}updateEnabledState(t){this.isEnabled=t,this.onEnableChange(t)}parseDOMInteraction(t){if(this.options.resolveInteraction){this.state.interaction=this.options.resolveInteraction(t)||{};return}const e={};if(this.options.rules)for(const[s,o]of Object.entries(this.options.rules))t.matches(s)&&Object.assign(e,o);const i=this.normalizedDataPrefix,n=this.dataPrefix.length;for(const s in t.dataset){if(!s.toLowerCase().startsWith(i))continue;const o=s.slice(n);if(!o)continue;const h=t.dataset[s];e[o[0].toLowerCase()+o.slice(1)]=h===""?!0:h}this.state.interaction=e}handleMove(t){if(!this.isEnabled||this.options.autoDisableOnMobile&&t.pointerType==="touch"&&!this.options.enableTouch)return;let e=t.clientX,i=t.clientY;if(this.options.container&&this.options.container!==document.body){const n=this.options.container.getBoundingClientRect();e-=n.left,i-=n.top}this.state.pointer.x=e,this.state.pointer.y=i,this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=e,this.state.target.y=this.state.smooth.y=i)}handleDown(){this.isEnabled&&(this.state.isDown=!0)}handleUp(){this.isEnabled&&(this.state.isDown=!1)}handleMouseOver(t){if(!this.isEnabled)return;const e=t.target;if(e.closest(`[${this.ignoreAttribute}]`)){this.state.isNative=!0;return}const i=this.getHoverSelector(),n=e.closest(i);n&&(this.state.isHover=!0,this.state.hoverTarget=n,this.parseDOMInteraction(this.state.hoverTarget));const s=this.options.ignoreOnNative;if(s&&s!==null){const o=s==="auto"||s==="tag",h=s==="auto"||s==="css";let c=!1;if(o){const d=e.localName;(d==="input"||d==="textarea"||d==="select"||e.isContentEditable)&&(c=!0)}if(!c&&h){const d=window.getComputedStyle(e).cursor;["default","auto","pointer","none","inherit"].includes(d)||(c=!0)}c&&(this.state.isNative=!0)}}handleMouseOut(t){if(!this.isEnabled)return;const e=t.target;(e===this.state.hoverTarget||e.contains(this.state.hoverTarget))&&(!t.relatedTarget||!this.state.hoverTarget?.contains(t.relatedTarget))&&(this.state.isHover=!1,this.state.hoverTarget=null,this.state.interaction={}),this.state.isNative&&(this.state.isNative=!1)}handleWindowLeave(){this.options.hideOnLeave&&(this.state.hasReceivedInput=!1,this.state.pointer={...u})}clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1,this.state.interaction={}}bindEvents(){const{signal:t}=this.abortController;window.addEventListener("pointermove",this.handleMove.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerdown",this.handleDown.bind(this),{passive:!0,signal:t}),window.addEventListener("pointerup",this.handleUp.bind(this),{signal:t}),document.addEventListener("mouseover",this.handleMouseOver.bind(this),{signal:t}),document.addEventListener("mouseout",this.handleMouseOut.bind(this),{signal:t}),document.addEventListener("mouseleave",this.handleWindowLeave.bind(this),{signal:t})}destroy(){this.abortController.abort()}}let b=0;class g{constructor(t=document.body,e){if(this.container=t,this.hideNativeCursor=e,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const i=b++;this.id=`supermouse-style-${i}`,this.scopeClass=`supermouse-scope-${i}`;const n=t===document.body;if(this.element=document.createElement("div"),Object.assign(this.element.style,{position:n?"fixed":"absolute",inset:"0px",pointerEvents:"none",zIndex:"9999",opacity:"1",transition:"opacity 0.15s ease"}),!n){const s=window.getComputedStyle(t);this.originalContainerPosition=s.position,s.position==="static"&&(t.style.position="relative")}this.originalContainerCursor=t.style.cursor,t.appendChild(this.element),this.styleTag=document.createElement("style"),this.styleTag.id=this.id,document.head.appendChild(this.styleTag),this.container.classList.add(this.scopeClass),this.hideNativeCursor&&this.setNativeCursor("none")}container;hideNativeCursor;element;styleTag;id;scopeClass;currentCursorState=null;originalContainerPosition="";originalContainerCursor="";selectors=new Set(["a","button","input","textarea","select",'[role="button"]',"[tabindex]"]);addSelector(t){this.selectors.add(t),this.hideNativeCursor&&this.updateCursorCSS()}setVisibility(t){this.element.style.opacity=t?"1":"0"}setNativeCursor(t){!this.hideNativeCursor&&t==="none"||t!==this.currentCursorState&&(this.currentCursorState=t,t==="none"?(this.container.style.cursor="none",this.updateCursorCSS()):(this.container.style.cursor="",this.styleTag.innerText=""))}updateCursorCSS(){const t=Array.from(this.selectors);if(t.length===0){this.styleTag.innerText="";return}const e=t.map(i=>`.${this.scopeClass} ${i}`).join(", ");this.styleTag.innerText=`
2
2
  ${e} {
3
3
  cursor: none !important;
4
4
  }
5
- `}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor="",this.container.classList.remove(this.scopeClass)}}const c={x:-100,y:-100},v=["a","button","input","textarea","[data-hover]","[data-cursor]"];class y{static version="2.2.0";version="2.2.0";state;options;plugins=[];stage;input;rafId=0;lastTime=0;isRunning=!1;hoverSelectors;constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,ignoreOnNative:"auto",hideCursor:!0,hideOnLeave:!0,autoStart:!0,container:document.body,...t},this.options.container||(this.options.container=document.body),this.state={pointer:{x:-100,y:-100},target:{x:-100,y:-100},smooth:{x:-100,y:-100},velocity:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,forcedCursor:null,hoverTarget:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{}},this.options.hoverSelectors?this.hoverSelectors=new Set(this.options.hoverSelectors):this.hoverSelectors=new Set(v),this.stage=new g(this.options.container,!!this.options.hideCursor),this.hoverSelectors.forEach(e=>this.stage.addSelector(e)),this.input=new f(this.state,this.options,()=>Array.from(this.hoverSelectors).join(", "),e=>{e||this.resetPosition()}),this.options.plugins&&this.options.plugins.forEach(e=>this.use(e)),this.init()}getPlugin(t){return this.plugins.find(e=>e.name===t)}get isEnabled(){return this.input.isEnabled}enablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled===!1&&(e.isEnabled=!0,e.onEnable?.(this))}disablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled!==!1&&(e.isEnabled=!1,e.onDisable?.(this))}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}registerHoverTarget(t){this.hoverSelectors.has(t)||(this.hoverSelectors.add(t),this.stage.addSelector(t))}get container(){return this.stage.element}setCursor(t){this.state.forcedCursor=t}init(){this.options.autoStart&&this.startLoop()}enable(){this.input.isEnabled=!0,this.stage.setNativeCursor("none")}disable(){this.input.isEnabled=!1,this.stage.setNativeCursor("auto"),this.resetPosition()}use(t){return this.plugins.find(e=>e.name===t.name)?(console.warn(`[Supermouse] Plugin "${t.name}" already installed.`),this):(t.isEnabled===void 0&&(t.isEnabled=!0),this.plugins.push(t),this.plugins.sort((e,i)=>(e.priority||0)-(i.priority||0)),t.install?.(this),this)}resetCoords(){this.state.pointer={...c},this.state.target={...c},this.state.smooth={...c},this.state.velocity={x:0,y:0},this.state.angle=0}resetPosition(){this.resetCoords(),this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={}}startLoop(){this.isRunning||(this.isRunning=!0,this.lastTime=performance.now(),this.tick(this.lastTime))}step(t){this.tick(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch(i){console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,i),t.isEnabled=!1;try{t.onDisable?.(this)}catch(o){console.error(`[Supermouse] Failed to execute onDisable for plugin '${t.name}'.`,o)}}}tick=t=>{const e=t-this.lastTime,i=Math.min(e/1e3,.1);this.lastTime=t,this.state.hoverTarget&&!this.state.hoverTarget.isConnected&&this.input.clearHover();const o=this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput;if(this.stage.setVisibility(o),this.input.isEnabled&&this.options.hideCursor){let s="auto";this.state.forcedCursor!==null?s=this.state.forcedCursor:s=this.state.isNative||!this.state.hasReceivedInput?"auto":"none",this.stage.setNativeCursor(s)}this.input.isEnabled?(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y):this.resetCoords();for(let s=0;s<this.plugins.length;s++)this.runPluginSafe(this.plugins[s],e);if(this.input.isEnabled){const s=this.state.reducedMotion?1e3:1/this.options.smoothness*2;this.state.smooth.x=p(this.state.smooth.x,this.state.target.x,s,i),this.state.smooth.y=p(this.state.smooth.y,this.state.target.y,s,i);const h=this.state.target.x-this.state.smooth.x,r=this.state.target.y-this.state.smooth.y;this.state.velocity.x=h,this.state.velocity.y=r,(Math.abs(h)>.1||Math.abs(r)>.1)&&(this.state.angle=m(h,r))}this.options.autoStart&&this.isRunning&&(this.rafId=requestAnimationFrame(this.tick))};destroy(){this.isRunning=!1,cancelAnimationFrame(this.rafId),this.input.destroy(),this.stage.destroy(),this.plugins.forEach(t=>t.destroy?.(this)),this.plugins=[]}}n.DEFAULT_HOVER_SELECTORS=v,n.Input=f,n.Stage=g,n.Supermouse=y,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
5
+ `}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor=this.originalContainerCursor,this.container.classList.remove(this.scopeClass),this.container!==document.body&&this.originalContainerPosition==="static"&&(this.container.style.position="")}}const u={x:-100,y:-100},v=["a","button","input","textarea","[data-hover]","[data-cursor]"];class y{static version=l;version=l;state;options;plugins=[];stage;input;rafId=0;lastTime=0;isRunning=!1;hoverSelectors;constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,ignoreOnNative:"auto",hideCursor:!0,hideOnLeave:!0,autoStart:!0,container:document.body,dataPrefix:"supermouse",...t},this.state={pointer:{x:-100,y:-100},target:{x:-100,y:-100},smooth:{x:-100,y:-100},velocity:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,forcedCursor:null,hoverTarget:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{}},this.options.hoverSelectors?this.hoverSelectors=new Set(this.options.hoverSelectors):this.hoverSelectors=new Set(v),this.stage=new g(this.options.container,!!this.options.hideCursor),this.hoverSelectors.forEach(e=>this.stage.addSelector(e)),this.input=new f(this.state,this.options,()=>Array.from(this.hoverSelectors).join(", "),e=>{e||this.reset(!0)}),this.options.plugins&&this.options.plugins.forEach(e=>this.use(e)),this.init()}getPlugin(t){return this.plugins.find(e=>e.name===t)}get isEnabled(){return this.input.isEnabled}enablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled===!1&&(e.isEnabled=!0,e.onEnable?.(this))}disablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled!==!1&&(e.isEnabled=!1,e.onDisable?.(this))}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}registerHoverTarget(t){this.hoverSelectors.has(t)||(this.hoverSelectors.add(t),this.stage.addSelector(t))}get container(){return this.stage.element}setNativeCursor(t){this.state.forcedCursor=t==="auto"?null:t==="hide"?"none":"auto"}init(){this.options.autoStart&&this.startLoop()}enable(){this.input.isEnabled=!0,this.stage.setNativeCursor("none")}disable(){this.input.isEnabled=!1,this.stage.setNativeCursor("auto"),this.reset(!0)}use(t){if(this.plugins.some(i=>i.name===t.name))return console.warn(`[Supermouse] Plugin "${t.name}" already installed.`),this;t.isEnabled===void 0&&(t.isEnabled=!0);try{t.install?.(this)}catch(i){return console.error(`[Supermouse] Failed to install plugin '${t.name}'.`,i),this}return this.plugins.push(t),this.plugins.sort((i,n)=>(i.priority||0)-(n.priority||0)),this}reset(t=!1){this.state.pointer={...u},this.state.target={...u},this.state.smooth={...u},this.state.velocity={x:0,y:0},this.state.angle=0,t&&(this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={})}startLoop(){this.isRunning||(this.isRunning=!0,this.lastTime=performance.now(),this.tick(this.lastTime))}start(){this.startLoop()}step(t){this.tick(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch(i){console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,i);const n=this.plugins.indexOf(t);n>-1&&this.plugins.splice(n,1),t.isEnabled=!1;try{t.destroy?.(this),t.onDisable?.(this)}catch(s){console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`,s)}}}tick=t=>{const e=t-this.lastTime,i=Math.min(e/1e3,.1);this.lastTime=t,this.state.hoverTarget&&!this.state.hoverTarget.isConnected&&this.input.clearHover();const n=this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput;if(this.stage.setVisibility(n),this.input.isEnabled&&this.options.hideCursor){let s="auto";this.state.forcedCursor!==null?s=this.state.forcedCursor:s=this.state.isNative||!this.state.hasReceivedInput?"auto":"none",this.stage.setNativeCursor(s)}this.input.isEnabled&&this.state.hasReceivedInput?(this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y):this.state.target={...u};for(let s=0;s<this.plugins.length;s++)this.runPluginSafe(this.plugins[s],e);if(this.input.isEnabled){const s=this.state.reducedMotion?1e3:1/this.options.smoothness*2;this.state.smooth.x=p(this.state.smooth.x,this.state.target.x,s,i),this.state.smooth.y=p(this.state.smooth.y,this.state.target.y,s,i),this.state.velocity.x=this.state.target.x-this.state.smooth.x,this.state.velocity.y=this.state.target.y-this.state.smooth.y;const{x:o,y:h}=this.state.velocity;(Math.abs(o)>.1||Math.abs(h)>.1)&&(this.state.angle=Math.atan2(h,o)*(180/Math.PI))}this.isRunning&&(this.rafId=requestAnimationFrame(this.tick))};destroy(){this.isRunning=!1,cancelAnimationFrame(this.rafId),this.input.destroy(),this.stage.destroy(),this.plugins.forEach(t=>t.destroy?.(this)),this.plugins=[]}}a.DEFAULT_HOVER_SELECTORS=v,a.Input=f,a.Stage=g,a.Supermouse=y,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supermousejs/core",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "The core physics engine and state manager for Supermouse.",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.mjs",