@supermousejs/core 2.5.0-beta.1 → 2.5.0-beta.3

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,46 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.5.0-beta.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 264bcbd: Beta.3 fixes and internal refactors.
8
+
9
+ **Fixed**
10
+ - Logic plugins (`priority < 0`) now correctly override `state.target` before physics. The update loop was reordering `target = pointer` after plugin execution, silently clobbering Magnetic, Stick, and any plugin that pins the cursor.
11
+ - Cursor state now applies synchronously on `mouseover`, `setCursor()`, and `handle.deactivate()`. Previously the cursor and stage visibility only updated on the next animation frame, causing a one-frame flash of the native cursor when entering a scope whose cursor mode differs from the previous one.
12
+ - Nested scope containers no longer inherit `cursor: none` from an outer scope. An engine-level `:where(.supermouse-scope .supermouse-scope) { cursor: auto }` rule cuts the inheritance without beating any authored cursor rule. Verified in Chromium and Firefox.
13
+ - `disablePlugin` on a plugin whose scope is inactive no longer re-fires `onBeforeDisable` and `onDisable`. Those hooks already ran when the scope went inactive. Semantics: `isEnabled` is user intent; lifecycle hooks fire on transitions of `(isEnabled && scopeIsActive)`.
14
+ - Deactivated eager scopes no longer orphan. The previous resolution model deleted the map entry on deactivation and never re-added it, so `handle.activate()` followed by a hover couldn't reactivate.
15
+ - Detached scope containers no longer pin stale bindings.
16
+ - `handle.activate()` is now eager when the pointer is already inside the container — fixes modals that mount under a stationary pointer.
17
+
18
+ **Changed**
19
+ - `ScopeHandle.remove()` renamed to `ScopeHandle.destroy()`.
20
+ - `ScopeHandle.disable()` / `ScopeHandle.enable()` renamed to `ScopeHandle.deactivate()` / `ScopeHandle.activate()`.
21
+ - `ScopeHandle.disabled` renamed to `ScopeHandle.active` (inverted).
22
+ - `CursorPolicy` is now `{ native: string[], hide: string[] }`. The previous `{ rules: CursorTargetRule[] }` shape is removed; `CursorTargetRule` is no longer exported.
23
+ - `ScopeConfig.container` accepts `HTMLElement | string`. A string is a CSS selector for lazy resolution. The scope stays dormant until an element matching the selector appears and the pointer enters it.
24
+ - `Stage.setNativeCursor` no longer writes inline `cursor` on the container. Cursor suppression is class-driven.
25
+
26
+ **Removed**
27
+ - `ViewportManager` module. Container rect reads are now direct `getBoundingClientRect()` calls in `Input.applyPointerToState`. Fixes stale-rect behavior after enter animations.
28
+ - Internal `scopeByContainer` map. Replaced with an ancestor walk using `Scope.match(el)`.
29
+
30
+ **Internal**
31
+ - Build target raised to `es2022`.
32
+ - `sideEffects: false` added to package.json.
33
+ - Size budget enforced at 6 kB gzip / 5.5 kB brotli via `size-limit`.
34
+
35
+ - e9431a5: Renamed `disable` and `enable` to `deactivate` and `activate` for clarify of function and implement lazy loading of containers
36
+
37
+ ## 2.5.0-beta.2
38
+
39
+ ### Patch Changes
40
+
41
+ - ff288b4: Fixed Logic Plugins not have effect from wrong ordering of target overwrite
42
+ - 18b57de: Fixed CSS stylesheets being destroyed erroneously from a lack of a primary owner and plugins being activated all at once by the `States` plugin when re-entering previously exited scope
43
+
3
44
  ## 2.5.0-beta.1
4
45
 
5
46
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,39 +1,32 @@
1
1
  /** Overall cursor mode. */
2
2
  export declare type CursorMode = "auto" | "custom" | "native" | "both";
3
3
 
4
+ /**
5
+ * A cursor policy maps two independent concerns to flat selector lists.
6
+ *
7
+ * - `native`: selectors whose elements yield to the OS cursor when the
8
+ * cursor mode is `"auto"`. Hovering them temporarily shows the native
9
+ * cursor and hides the custom one.
10
+ *
11
+ * - `hide`: selectors that receive `cursor: none !important` when the
12
+ * custom cursor is active. Needed for elements whose UA stylesheet sets
13
+ * a cursor (`a { cursor: pointer }`) that would otherwise override the
14
+ * inherited suppression.
15
+ */
4
16
  export declare interface CursorPolicy {
5
- rules: CursorTargetRule[];
17
+ native: string[];
18
+ hide: string[];
6
19
  }
7
20
 
8
- /** Shorthand form accepted at the public API boundary. */
21
+ /**
22
+ * Shorthand accepted at the public API boundary. `{ native?: [...], hide?: [...] }`
23
+ * normalizes to the full `CursorPolicy` shape with missing keys defaulting to `[]`.
24
+ */
9
25
  export declare type CursorPolicyInput = CursorPolicy | {
10
26
  native?: string[];
11
27
  hide?: string[];
12
28
  };
13
29
 
14
- /**
15
- * A single rule that ties a CSS selector to native-cursor behaviour
16
- * and/or native-cursor CSS suppression.
17
- */
18
- export declare interface CursorTargetRule {
19
- /** CSS selector to match. */
20
- selector: string;
21
- /**
22
- * In `"auto"` cursor mode, an element matching this selector yields to the
23
- * OS cursor.
24
- * @default true
25
- */
26
- native?: boolean;
27
- /**
28
- * Generate a `cursor: none !important` rule for this selector when the
29
- * custom cursor is active. Needed for elements whose UA stylesheet sets a
30
- * cursor value (e.g. `a { cursor: pointer }`) that would otherwise override
31
- * the container's inherited `cursor: none`.
32
- * @default true
33
- */
34
- hide?: boolean;
35
- }
36
-
37
30
  export declare const DEFAULT_CURSOR_POLICY: CursorPolicy;
38
31
 
39
32
  export declare const DEFAULT_HOVER_SELECTORS: string[];
@@ -91,6 +84,11 @@ export declare interface MouseState {
91
84
  shape: ShapeState | null;
92
85
  /** Centralized store for hover metadata from data attributes and rules. */
93
86
  interaction: InteractionState;
87
+ /** The currently active scope's identity, or null when none is active. */
88
+ scope: {
89
+ name: string | undefined;
90
+ container: HTMLElement;
91
+ } | null;
94
92
  }
95
93
 
96
94
  declare type ResolvedOptions = SupermouseOptions & Required<Pick<SupermouseOptions, "smoothness" | "enableTouch" | "autoDisableOnMobile" | "cursor" | "hideOnLeave" | "autoStart" | "container" | "dataPrefix" | "zIndex" | "inheritDataAttributes">>;
@@ -109,8 +107,11 @@ export declare type RuleValue = string | boolean | number;
109
107
  export declare interface ScopeConfig {
110
108
  /** Optional identifier for `getScope(name)` lookups. */
111
109
  name?: string;
112
- /** The element this scope is bound to. */
113
- container: HTMLElement;
110
+ /**
111
+ * The container this scope is bound to. Pass an element reference for
112
+ * eager binding, or a CSS selector string for lazy resolution.
113
+ */
114
+ container: HTMLElement | string;
114
115
  /** Cursor mode for this scope. Inherits from top-level if omitted. */
115
116
  cursor?: CursorMode;
116
117
  /** Hover selectors for this scope. Inherits from top-level if omitted. */
@@ -119,6 +120,12 @@ export declare interface ScopeConfig {
119
120
  cursorPolicy?: CursorPolicyInput;
120
121
  /** Plugins installed when this scope activates. */
121
122
  plugins?: SupermousePlugin[];
123
+ /**
124
+ * Rules evaluated against hovered elements inside this scope. If omitted,
125
+ * the scope inherits the primary scope's rules. If provided, the scope
126
+ * uses only its own rules.
127
+ */
128
+ rules?: Record<string, RuleDefinition>;
122
129
  /**
123
130
  * Whether data attributes and rules on ancestors cascade to the hovered
124
131
  * element. Inherits from top-level if omitted.
@@ -131,9 +138,34 @@ export declare interface ScopeConfig {
131
138
 
132
139
  export declare interface ScopeHandle {
133
140
  readonly name: string | undefined;
134
- readonly container: HTMLElement;
135
- remove(): void;
141
+ /**
142
+ * The scope's container element, or `null` if the scope is
143
+ * selector-based and has not yet been resolved to a live element.
144
+ */
145
+ readonly container: HTMLElement | null;
146
+ readonly active: boolean;
147
+ /** True for eager scopes and for selector scopes that have resolved. */
148
+ readonly resolved: boolean;
149
+ destroy(): void;
136
150
  setCursor(mode: CursorMode): void;
151
+ /**
152
+ * Marks the scope as eligible for activation.
153
+ *
154
+ * Activation is lazy: the scope becomes active on the next `mouseover`
155
+ * inside its container, not immediately. If you need to force the scope
156
+ * active while the pointer is already inside it, this will not work
157
+ * until the pointer moves. Deactivation, by contrast, is eager.
158
+ */
159
+ activate(): void;
160
+ /**
161
+ * Marks the scope as ineligible. If it was the active scope, control
162
+ * yields immediately to the nearest active ancestor scope, or to
163
+ * nothing if none exists.
164
+ */
165
+ deactivate(): void;
166
+ use(plugin: SupermousePlugin): ScopeHandle;
167
+ removePlugin(name: string): void;
168
+ getPlugin(name: string): SupermousePlugin | undefined;
137
169
  }
138
170
 
139
171
  export declare interface ShapeState {
@@ -150,8 +182,10 @@ export declare class Supermouse {
150
182
  private _scopes;
151
183
  private _activeScope;
152
184
  private _installingScope;
153
- private scopeByContainer;
185
+ private scopeByName;
186
+ private scopeHandles;
154
187
  private input;
188
+ private styleOwner;
155
189
  private rafId;
156
190
  private lastTime;
157
191
  private visibilityAbortController;
@@ -161,11 +195,42 @@ export declare class Supermouse {
161
195
  get stage(): HTMLDivElement;
162
196
  get isEnabled(): boolean;
163
197
  get isRunning(): boolean;
198
+ /**
199
+ * Installs a plugin into the primary scope.
200
+ *
201
+ * In multi-scope mode, prefer `handle.use(plugin)` to target a specific
202
+ * scope. This method always installs to the primary scope, regardless of
203
+ * which scope is currently active.
204
+ */
164
205
  use(plugin: SupermousePlugin): this;
206
+ /**
207
+ * Searches every scope and returns the first plugin matching `name`.
208
+ * Scopes are searched in registration order (primary first).
209
+ */
165
210
  getPlugin(name: string): SupermousePlugin | undefined;
211
+ getScope(name: string): ScopeHandle | undefined;
212
+ /**
213
+ * Enables a plugin by name, wherever it lives.
214
+ *
215
+ * If the plugin's scope is currently active, the activation lifecycle
216
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
217
+ * hooks do not fire until the scope next activates.
218
+ */
166
219
  enablePlugin(name: string): void;
220
+ /**
221
+ * Disables a plugin by name, wherever it lives.
222
+ *
223
+ * If the plugin's scope is currently active, the deactivation lifecycle
224
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
225
+ * hooks do not fire — they already ran when the scope went inactive.
226
+ */
167
227
  disablePlugin(name: string): void;
168
228
  togglePlugin(name: string): void;
229
+ /**
230
+ * Sets the cursor mode on the *active* scope.
231
+ *
232
+ * To set the mode on a specific scope, use `handle.setCursor(mode)`.
233
+ */
169
234
  setCursor(mode: CursorMode): void;
170
235
  addScope(config: ScopeConfig): ScopeHandle;
171
236
  enable(): void;
@@ -178,13 +243,55 @@ export declare class Supermouse {
178
243
  destroy(): void;
179
244
  private _running;
180
245
  private createScope;
181
- private removeScope;
246
+ private buildScopeHandle;
247
+ private destroyScope;
182
248
  private setScopeCursor;
249
+ /**
250
+ * Marks the scope ineligible for activation and, if it was active, yields
251
+ * immediately to the nearest active ancestor scope (or nothing).
252
+ */
253
+ private deactivateScope;
254
+ /**
255
+ * Marks the scope eligible for activation.
256
+ *
257
+ * If the pointer is already inside the scope's container, the scope
258
+ * becomes active immediately. Otherwise it activates on the next
259
+ * `mouseover` inside the container. Deactivation is always eager.
260
+ */
261
+ private activateScope;
262
+ /**
263
+ * Called by `Input` on every `mouseover`, and internally when the active
264
+ * scope is removed or deactivated. Walks from `node` up the ancestor
265
+ * chain, returning the innermost active scope whose `match` succeeds
266
+ * for an ancestor. The matched scope is bound to that ancestor before
267
+ * return.
268
+ *
269
+ * The walk order is what enforces "innermost wins": the first ancestor
270
+ * matching any active scope is, by construction, the closest one to the
271
+ * event target. Detached containers can never be ancestors of a live
272
+ * event target, so no cleanup of stale bindings is required here.
273
+ */
274
+ private resolveScopeForNode;
275
+ /**
276
+ * Synchronously writes the current cursor and stage visibility for the
277
+ * active scope. Called after hover state settles, on cursor mode changes,
278
+ * and on programmatic scope transitions. The `update()` rAF path calls
279
+ * the same writers as a backstop.
280
+ */
281
+ private applyCursorNow;
282
+ private findScopeForPlugin;
183
283
  private installPlugins;
184
284
  private installPlugin;
285
+ private removePluginFromScope;
185
286
  private handleActiveScopeChange;
186
- private activatePlugin;
187
287
  private deactivatePlugin;
288
+ /**
289
+ * Runs the deactivation lifecycle for a plugin whose owning scope just
290
+ * became inactive. Does NOT flip `plugin.isEnabled`, so user intent
291
+ * (e.g. States disabling a plugin) survives the scope round-trip.
292
+ */
293
+ private scopeDeactivatePlugin;
294
+ private scopeActivatePlugin;
188
295
  private rebuildStylesheet;
189
296
  private runPluginSafe;
190
297
  private cleanupCrashedPlugins;
@@ -196,21 +303,12 @@ export declare class Supermouse {
196
303
  private bindVisibilityHandling;
197
304
  private startLoop;
198
305
  /**
199
- * @deprecated Prefer setting `hoverSelectors` at scope creation, or use
200
- * `definePlugin`'s `selector` option. This method is kept for raw-object
201
- * plugins and runtime extension; it mutates the active scope's selector set.
306
+ * Adds one or more hover selectors to the current scope. Intended for
307
+ * raw-object plugins during `install`. Plugins written with `definePlugin`
308
+ * should use the `selector` option instead, and consumers setting up a
309
+ * scope should prefer the `hoverSelectors` option at construction.
202
310
  */
203
311
  registerHoverTarget(selector: string): void;
204
- /**
205
- * @deprecated Read from the active scope instead. Exposed for tests and
206
- * debugging only.
207
- */
208
- get hoverSelectors(): Set<string>;
209
- /**
210
- * @deprecated Read from the active scope instead. Exposed for tests and
211
- * debugging only.
212
- */
213
- get plugins(): SupermousePlugin[];
214
312
  }
215
313
 
216
314
  export declare type SupermouseInstance = Supermouse;