@supermousejs/core 2.3.0 → 2.4.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 +33 -0
- package/dist/index.d.ts +106 -139
- package/dist/index.mjs +309 -227
- package/dist/index.umd.js +10 -5
- package/package.json +4 -3
- package/src/Supermouse.ts +517 -295
- package/src/__tests__/core.spec.ts +291 -0
- package/src/__tests__/cursor-mode.spec.ts +109 -0
- package/src/__tests__/input.spec.ts +261 -0
- package/src/__tests__/integration.spec.ts +121 -0
- package/src/__tests__/lifecycle.spec.ts +199 -0
- package/src/__tests__/nested-instance.spec.ts +51 -0
- package/src/__tests__/plugin.spec.ts +130 -0
- package/src/__tests__/setup.ts +14 -6
- package/src/__tests__/stage.spec.ts +235 -0
- package/src/__tests__/suspend-resume.spec.ts +50 -0
- package/src/types.ts +165 -183
- package/vitest.config.ts +1 -1
- package/src/__tests__/supermouse.test.ts +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @supermousejs/core
|
|
2
2
|
|
|
3
|
+
## 2.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 53b7276: This release stabilises the core after experimental work, keeping the best improvements while reverting the approach that caused cursor flickering.
|
|
8
|
+
- `zIndex` option – customise the stage’s z‑index (default `9999`).
|
|
9
|
+
- `cacheCursorStyle` option – opt‑in per‑element caching of computed `cursor` values, with `clearStyleCache()` to invalidate.
|
|
10
|
+
- `suspend()` / `resume()` – temporarily yield to another Supermouse instance without tearing down the current one.
|
|
11
|
+
- `hasSeenPointer` flag – enables `enable()` to snap the cursor to the live pointer instantly, no more off‑screen sweep.
|
|
12
|
+
- Nested‑instance CSS exclusion – scoped stylesheets now automatically prevent outer instance cursor rules from leaking into nested containers.
|
|
13
|
+
- `enable()` now respects any active `forcedCursor` override.
|
|
14
|
+
- `disable()` always restores the native cursor.
|
|
15
|
+
- `clearHover()` now resets `isNative` and `nativeTarget` to prevent stale state.
|
|
16
|
+
- Plugin crash handling: crashing plugins are removed after the frame with full cleanup (`onDisable` → `destroy` → element removal).
|
|
17
|
+
- `enablePlugin` / `disablePlugin` now hide/show the plugin’s DOM element (if it exposes an `element` property) and call lifecycle hooks.
|
|
18
|
+
- `reset()` no longer wipes `state.pointer`, so `enable()` can always snap to the last known position.
|
|
19
|
+
- Native‑cursor suppression now uses a class‑toggled CSS approach with additional rules for `<label>`, `<select>`, and range slider thumbs.
|
|
20
|
+
- `handleMouseOut` clears `isNative` only when truly leaving the native element, preventing flicker when moving within native controls.
|
|
21
|
+
- Animation loop pauses when the tab is hidden and resumes on focus.
|
|
22
|
+
- `Stage` warns if the container is not attached to the DOM yet.
|
|
23
|
+
- Original container `cursor` style is restored on destroy.
|
|
24
|
+
- Various other stability and edge‑case fixes.
|
|
25
|
+
|
|
26
|
+
- 0cd6a04: - Unified cursor modes to reduce api surface by replacing cursor and hideCursor with single cursor option, decouple interaction parsing causing bugs
|
|
27
|
+
- Cache rule entries and hover selector string to reduce per-frame work.
|
|
28
|
+
- Suspend/resume no longer toggle native cursor state. this is an identified multi-scope limitation.
|
|
29
|
+
- ab3cad2: Enhanced supermouse containerization and added options for caching computed cursor styles
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 36d5366: Added proper description messages to package meta
|
|
34
|
+
- 4fc5aed: Added forward-compatible `onBeforeDisable` hook for plugins in preparation for architecture rewrite in v2.5
|
|
35
|
+
|
|
3
36
|
## 2.3.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
/** Default selectors that trigger `state.isHover`. Override with `hoverSelectors`. */
|
|
1
2
|
export declare const DEFAULT_HOVER_SELECTORS: string[];
|
|
2
3
|
|
|
3
4
|
/* Excluded from this release type: Input */
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Interaction state consumed by plugins.
|
|
8
|
+
* Extend via module augmentation for type safety.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* declare module '@supermousejs/core' {
|
|
@@ -15,10 +16,7 @@ export declare const DEFAULT_HOVER_SELECTORS: string[];
|
|
|
15
16
|
* }
|
|
16
17
|
*/
|
|
17
18
|
export declare interface InteractionState {
|
|
18
|
-
/**
|
|
19
|
-
* Allow arbitrary keys for rapid prototyping.
|
|
20
|
-
* For type safety, use module augmentation to define expected keys.
|
|
21
|
-
*/
|
|
19
|
+
/** Arbitrary keys allowed for quick prototyping. */
|
|
22
20
|
[key: string]: any;
|
|
23
21
|
}
|
|
24
22
|
|
|
@@ -28,42 +26,49 @@ export declare interface MousePosition {
|
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
export declare interface MouseState {
|
|
31
|
-
/**
|
|
29
|
+
/** Raw pointer position from the latest event (before smoothing). */
|
|
32
30
|
pointer: MousePosition;
|
|
33
|
-
/**
|
|
31
|
+
/** Goal position the core loop drives toward. */
|
|
34
32
|
target: MousePosition;
|
|
35
|
-
/**
|
|
33
|
+
/** Smoothed position used for rendering. */
|
|
36
34
|
smooth: MousePosition;
|
|
37
|
-
/**
|
|
35
|
+
/** Movement vector derived from smoothed state. */
|
|
38
36
|
velocity: MousePosition;
|
|
39
|
-
/**
|
|
37
|
+
/** Remaining distance to target. */
|
|
38
|
+
displacement: MousePosition;
|
|
39
|
+
/** Movement angle in degrees. */
|
|
40
40
|
angle: number;
|
|
41
|
-
/**
|
|
41
|
+
/** Pointer is pressed down. */
|
|
42
42
|
isDown: boolean;
|
|
43
|
-
/**
|
|
43
|
+
/** Hovering over a registered interactive element. */
|
|
44
44
|
isHover: boolean;
|
|
45
|
-
/**
|
|
45
|
+
/** Native cursor temporarily restored due to native-input heuristics. */
|
|
46
46
|
isNative: boolean;
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* 'none' = Force Custom Cursor (Hide Native)
|
|
51
|
-
* null = Let the Core decide based on isNative/isHover
|
|
52
|
-
*/
|
|
53
|
-
forcedCursor: "auto" | "none" | null;
|
|
54
|
-
/** The DOM element currently being hovered, if any. */
|
|
47
|
+
/** Current cursor mode: auto, custom, native, or both. */
|
|
48
|
+
cursorMode: "auto" | "custom" | "native" | "both";
|
|
49
|
+
/** Currently hovered DOM element, if any. */
|
|
55
50
|
hoverTarget: HTMLElement | null;
|
|
56
|
-
/**
|
|
51
|
+
/** User has `prefers-reduced-motion` enabled. */
|
|
57
52
|
reducedMotion: boolean;
|
|
58
|
-
/**
|
|
53
|
+
/** At least one valid input coordinate received. */
|
|
59
54
|
hasReceivedInput: boolean;
|
|
60
|
-
/**
|
|
55
|
+
/** Geometric shape the cursor should conform to. */
|
|
61
56
|
shape: ShapeState | null;
|
|
62
|
-
/** Centralized store for hover metadata from data attributes. */
|
|
57
|
+
/** Centralized store for hover metadata from data attributes and rules. */
|
|
63
58
|
interaction: InteractionState;
|
|
64
59
|
}
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
/**
|
|
62
|
+
* The subset of `SupermouseOptions` guaranteed to have a concrete value once
|
|
63
|
+
* the constructor has merged user input over the defaults.
|
|
64
|
+
*/
|
|
65
|
+
declare type ResolvedOptions = SupermouseOptions & Required<Pick<SupermouseOptions, "smoothness" | "enableTouch" | "autoDisableOnMobile" | "cursor" | "hideOnLeave" | "autoStart" | "container" | "dataPrefix" | "zIndex">>;
|
|
66
|
+
|
|
67
|
+
export declare type RuleDefinition = RuleSet | ((el: HTMLElement) => RuleSet);
|
|
68
|
+
|
|
69
|
+
export declare type RuleSet = Record<string, RuleValue | ((el: HTMLElement) => RuleValue)>;
|
|
70
|
+
|
|
71
|
+
export declare type RuleValue = string | boolean | number;
|
|
67
72
|
|
|
68
73
|
export declare interface ShapeState {
|
|
69
74
|
width: number;
|
|
@@ -74,104 +79,78 @@ export declare interface ShapeState {
|
|
|
74
79
|
/* Excluded from this release type: Stage */
|
|
75
80
|
|
|
76
81
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* This class orchestrates the application state, manages the animation loop,
|
|
80
|
-
* and coordinates data flow between the internal systems, and the plugins.
|
|
81
|
-
*
|
|
82
|
-
* @default
|
|
82
|
+
* Orchestrates state, animation loop, and plugin lifecycle.
|
|
83
83
|
*/
|
|
84
84
|
export declare class Supermouse {
|
|
85
85
|
static readonly version: string;
|
|
86
86
|
readonly version: string;
|
|
87
87
|
state: MouseState;
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
*/
|
|
91
|
-
options: SupermouseOptions;
|
|
88
|
+
/** Configuration options, fully resolved with defaults applied. */
|
|
89
|
+
options: ResolvedOptions;
|
|
92
90
|
private plugins;
|
|
93
|
-
private
|
|
91
|
+
private _stage;
|
|
94
92
|
private input;
|
|
95
93
|
private rafId;
|
|
96
94
|
private lastTime;
|
|
97
95
|
private isRunning;
|
|
96
|
+
private isSuspended;
|
|
97
|
+
private visibilityAbortController;
|
|
98
98
|
private hoverSelectors;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
*
|
|
102
|
-
* @param options - Global configuration options.
|
|
103
|
-
* @throws Will throw if running in a non-browser environment (window/document undefined).
|
|
104
|
-
*/
|
|
99
|
+
private hoverSelectorString;
|
|
100
|
+
private crashedPlugins;
|
|
105
101
|
constructor(options?: SupermouseOptions);
|
|
106
|
-
/**
|
|
107
|
-
* Retrieves a registered plugin instance by its unique name.
|
|
108
|
-
*/
|
|
102
|
+
/** Look up a registered plugin by name. */
|
|
109
103
|
getPlugin(name: string): SupermousePlugin | undefined;
|
|
110
|
-
/**
|
|
111
|
-
* Returns whether the cursor system is currently enabled (processing input).
|
|
112
|
-
*/
|
|
104
|
+
/** Whether the instance is not disabled/suspended and is processing input. */
|
|
113
105
|
get isEnabled(): boolean;
|
|
114
|
-
/**
|
|
115
|
-
* Enables a specific plugin by name.
|
|
116
|
-
* Triggers the `onEnable` lifecycle hook of the plugin.
|
|
117
|
-
*/
|
|
106
|
+
/** Enable a plugin by name. */
|
|
118
107
|
enablePlugin(name: string): void;
|
|
119
|
-
/**
|
|
120
|
-
* Disables a specific plugin by name.
|
|
121
|
-
* Triggers the `onDisable` lifecycle hook.
|
|
122
|
-
*/
|
|
108
|
+
/** Disable a plugin by name and hide its element. */
|
|
123
109
|
disablePlugin(name: string): void;
|
|
124
|
-
/**
|
|
125
|
-
* Toggles the enabled state of a plugin.
|
|
126
|
-
*/
|
|
110
|
+
/** Toggle a plugin's enabled state by name. */
|
|
127
111
|
togglePlugin(name: string): void;
|
|
112
|
+
/** Add a selector to hover detection and cursor suppression. */
|
|
128
113
|
registerHoverTarget(selector: string): void;
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
get
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
* @param mode
|
|
137
|
-
*/
|
|
138
|
-
setNativeCursor(mode: "hide" | "show" | "auto"): void;
|
|
114
|
+
/** The DOM element the instance is scoped to. */
|
|
115
|
+
get container(): HTMLElement;
|
|
116
|
+
/** The stage element that plugins append their visuals into. */
|
|
117
|
+
get stage(): HTMLDivElement;
|
|
118
|
+
/** Set the current cursor mode. */
|
|
119
|
+
setCursor(mode: "auto" | "custom" | "native" | "both"): void;
|
|
139
120
|
private init;
|
|
121
|
+
/** Re‑enable input processing and re‑apply cursor state. */
|
|
140
122
|
enable(): void;
|
|
123
|
+
/** Disable input processing and restore native cursor. */
|
|
141
124
|
disable(): void;
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
125
|
+
/** Temporarily yield to a scoped instance. */
|
|
126
|
+
suspend(): void;
|
|
127
|
+
/** Resume from `suspend()`. */
|
|
128
|
+
resume(): void;
|
|
129
|
+
/** Register a new plugin. */
|
|
147
130
|
use(plugin: SupermousePlugin): this;
|
|
131
|
+
/** Reset physics; optionally clear all input state. */
|
|
148
132
|
private reset;
|
|
149
133
|
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
|
-
*/
|
|
134
|
+
/** Start the animation loop. */
|
|
154
135
|
start(): void;
|
|
155
|
-
/**
|
|
156
|
-
* Manually steps the animation loop.
|
|
157
|
-
*
|
|
158
|
-
* @param time Current timestamp in milliseconds.
|
|
159
|
-
*/
|
|
136
|
+
/** Manually step the animation loop. */
|
|
160
137
|
step(time: number): void;
|
|
161
138
|
private runPluginSafe;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
139
|
+
private cleanupCrashedPlugins;
|
|
140
|
+
private resolveStageVisibility;
|
|
141
|
+
private resolveCursorState;
|
|
142
|
+
private resetMotion;
|
|
143
|
+
private update;
|
|
165
144
|
private tick;
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
145
|
+
/** Pause rAF loop when tab hidden; resume on visible. */
|
|
146
|
+
private bindVisibilityHandling;
|
|
147
|
+
/** Destroy the instance, freeing all resources. */
|
|
169
148
|
destroy(): void;
|
|
170
149
|
}
|
|
171
150
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
*/
|
|
151
|
+
export declare type SupermouseInstance = Supermouse;
|
|
152
|
+
|
|
153
|
+
/** Configuration options for the Supermouse constructor. */
|
|
175
154
|
export declare interface SupermouseOptions {
|
|
176
155
|
/**
|
|
177
156
|
* The interpolation factor (0 to 1). Lower is smoother/slower.
|
|
@@ -194,25 +173,16 @@ export declare interface SupermouseOptions {
|
|
|
194
173
|
*/
|
|
195
174
|
autoDisableOnMobile?: boolean;
|
|
196
175
|
/**
|
|
197
|
-
*
|
|
198
|
-
* - `
|
|
199
|
-
* - `
|
|
200
|
-
* - `
|
|
201
|
-
* - `
|
|
202
|
-
*
|
|
203
|
-
* @default 'auto'
|
|
176
|
+
* Overall cursor mode.
|
|
177
|
+
* - `"auto"`: use built-in heuristic to decide per element.
|
|
178
|
+
* - `"custom"`: always show custom cursor, hide native.
|
|
179
|
+
* - `"native"`: always show native cursor, hide custom.
|
|
180
|
+
* - `"both"`: always show custom cursor **and** native cursor together (no suppression).
|
|
181
|
+
* @default "auto"
|
|
204
182
|
*/
|
|
205
|
-
|
|
206
|
-
/**
|
|
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.
|
|
210
|
-
* @default true
|
|
211
|
-
*/
|
|
212
|
-
hideCursor?: boolean;
|
|
183
|
+
cursor?: "auto" | "custom" | "native" | "both";
|
|
213
184
|
/**
|
|
214
185
|
* 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.
|
|
216
186
|
* @default true
|
|
217
187
|
*/
|
|
218
188
|
hideOnLeave?: boolean;
|
|
@@ -233,51 +203,48 @@ export declare interface SupermouseOptions {
|
|
|
233
203
|
/**
|
|
234
204
|
* Semantic rules mapping CSS selectors to interaction state.
|
|
235
205
|
* 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.
|
|
237
206
|
* @example { 'button': { icon: 'pointer' } }
|
|
238
207
|
*/
|
|
239
|
-
rules?: Record<string,
|
|
240
|
-
/**
|
|
241
|
-
* Custom strategy to resolve interaction state from a hovered element.
|
|
242
|
-
* When provided, this callback bypasses the default `rules` + `data-[prefix]-*` scraping and
|
|
243
|
-
* returns the interaction payload directly for the current hover target.
|
|
244
|
-
*/
|
|
245
|
-
resolveInteraction?: (target: HTMLElement) => InteractionState;
|
|
208
|
+
rules?: Record<string, RuleDefinition>;
|
|
246
209
|
/**
|
|
247
210
|
* 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
211
|
* @default "supermouse"
|
|
251
212
|
*/
|
|
252
213
|
dataPrefix?: string;
|
|
214
|
+
/**
|
|
215
|
+
* The `z-index` applied to the cursor stage element.
|
|
216
|
+
* @default 9999
|
|
217
|
+
*/
|
|
218
|
+
zIndex?: number;
|
|
253
219
|
}
|
|
254
220
|
|
|
255
|
-
/**
|
|
256
|
-
* Interface for defining a Supermouse Plugin.
|
|
257
|
-
*/
|
|
221
|
+
/** Interface for defining a Supermouse plugin. */
|
|
258
222
|
export declare interface SupermousePlugin {
|
|
259
|
-
/** Unique
|
|
223
|
+
/** Unique plugin name. */
|
|
260
224
|
name: string;
|
|
261
|
-
/** Execution priority
|
|
225
|
+
/** Execution priority; lower runs first. */
|
|
262
226
|
priority?: number;
|
|
263
|
-
/** If false, update() will not be called. */
|
|
227
|
+
/** If false, `update()` will not be called. */
|
|
264
228
|
isEnabled?: boolean;
|
|
229
|
+
/** Root DOM element, if any. Hidden when plugin disabled. */
|
|
230
|
+
element?: HTMLElement | SVGElement;
|
|
265
231
|
/** Called when `app.use()` is executed. */
|
|
266
|
-
install?: (instance:
|
|
267
|
-
/** Called
|
|
268
|
-
update?: (instance:
|
|
269
|
-
/** Called when
|
|
270
|
-
destroy?: (instance:
|
|
271
|
-
/** Called when
|
|
272
|
-
onEnable?: (instance:
|
|
273
|
-
/** Called when
|
|
274
|
-
onDisable?: (instance:
|
|
232
|
+
install?: (instance: SupermouseInstance) => void;
|
|
233
|
+
/** Called every animation frame with delta time in ms. */
|
|
234
|
+
update?: (instance: SupermouseInstance, deltaTime: number) => void;
|
|
235
|
+
/** Called when plugin removed or instance destroyed. */
|
|
236
|
+
destroy?: (instance: SupermouseInstance) => void;
|
|
237
|
+
/** Called when plugin enabled via `.enablePlugin()`. */
|
|
238
|
+
onEnable?: (instance: SupermouseInstance) => void;
|
|
239
|
+
/** Called when plugin disabled via `.disablePlugin()`. */
|
|
240
|
+
onDisable?: (instance: SupermouseInstance) => void;
|
|
241
|
+
/**
|
|
242
|
+
* Called before the plugin is disabled.
|
|
243
|
+
*/
|
|
244
|
+
onBeforeDisable?(instance: SupermouseInstance): void | Promise<void>;
|
|
275
245
|
}
|
|
276
246
|
|
|
277
|
-
/**
|
|
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.
|
|
280
|
-
*/
|
|
247
|
+
/** Static value or function that returns a value based on state. */
|
|
281
248
|
export declare type ValueOrGetter<T> = T | ((state: MouseState) => T);
|
|
282
249
|
|
|
283
250
|
export { }
|