@supermousejs/core 2.4.3 → 2.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.5.0-beta.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 5d5ec21: Updated README to document cursor policy and scopes
8
+
9
+ ## 2.5.0-beta.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 0af4732: Architecture rewrite for v2.5. This is a beta, therefore expect API surface churn before the stable release.
14
+
15
+ **Scopes.** A single instance can now own multiple scopes. Pass them at construction or add them at runtime:
16
+
17
+ ```ts
18
+ const mouse = new Supermouse({
19
+ scopes: [{ name: "sidebar", container: sidebarEl, cursor: "native" }]
20
+ });
21
+
22
+ const handle = mouse.addScope({ container: modalEl, cursor: "custom" });
23
+ handle.setCursor("auto");
24
+ handle.remove();
25
+ ```
26
+
27
+ The innermost scope whose container contains the pointer is the active one. Plugin installation, cursor mode, hover selectors, and native-cursor state are all managed per scope.
28
+
29
+ **Cursor policy.** `NATIVE_TAGS` and the old `Stage.selectors` are unified into a single `CursorPolicy`. Pass a custom policy via `cursorPolicy` at the top level or per scope. `DEFAULT_CURSOR_POLICY` is exported for spreading.
30
+
31
+ **Ancestor-chain interaction.** `data-supermouse-*` attributes and `rules` now cascade from ancestors to the hovered element, ensuring the closest ancestor wins. This fixes the tag-inside-a-tag class of bugs. Disable with `inheritDataAttributes: false`.
32
+
33
+ **Lifecycle.** `onBeforeDisable` (and its `definePlugin` equivalent, `beforeDisable`) is now scope-aware: when a scope deactivates, each of its plugins runs `onBeforeDisable`, and the core waits for any returned Promise before hiding the element and calling `onDisable`.
34
+
35
+ **Changed.**
36
+ - `disable()` no longer resets physics. Call `disable({ reset: true })` for the old behavior.
37
+ - `suspend()` and `resume()` are removed. Use scopes.
38
+ - `registerHoverTarget` now mutates the active scope's selector set. It's still available for raw-object plugins, but `definePlugin`'s `selector` option is preferred.
39
+ - Stage no longer owns an individual `<style>` tag. All scopes share a single engine-level stylesheet.
40
+
41
+ **Removed.**
42
+ - `NATIVE_TAGS`, `SUPERMOUSE_CURSORS` internal constants (moved into policy).
43
+ - `Stage.addSelector` / `Stage.addSelectors` no-ops.
44
+
45
+ ### Patch Changes
46
+
47
+ - 1325a9c: Reorganized Supermouse core for better tree-shaking and module isolation
48
+
3
49
  ## 2.4.3
4
50
 
5
51
  ### Patch Changes
package/README.md CHANGED
@@ -1,158 +1,220 @@
1
- # Supermouse.js - TypeScript cursor engine
2
-
3
- [![npm version](https://img.shields.io/npm/v/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
4
- [![npm downloads](https://img.shields.io/npm/dm/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6
- ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40supermousejs%2Fcore?label=minzip)
7
-
8
- Supermouse is a headless, physics‑based cursor engine for the web written in TypeScript. It provides pointer tracking, state, physics, and a plugin lifecycle. You install plugins (or write your own) to render custom cursors and effects. The core is zero‑dependency and TypeScript‑first.
9
-
10
- **Documentation:** [supermouse.js.org](https://supermouse.js.org)
11
-
12
- ## Installation
13
-
14
- ```bash
15
- pnpm add @supermousejs/core
16
- npm install @supermousejs/core
17
- ```
18
-
19
- Install plugins you need:
20
-
21
- ```bash
22
- pnpm add @supermousejs/dot @supermousejs/ring
23
- npm install @supermousejs/dot @supermousejs/ring
24
- ```
25
-
26
- ## Quick Start
27
-
28
- ```ts
29
- import { Supermouse } from "@supermousejs/core";
30
- import { Dot } from "@supermousejs/dot";
31
-
32
- const mouse = new Supermouse({
33
- plugins: [Dot({ size: 8 })]
34
- });
35
- ```
36
-
37
- Or use the plugin interface directly:
38
-
39
- ```ts
40
- import { Supermouse } from "@supermousejs/core";
41
-
42
- const dot = document.createElement("div");
43
- // ... style dot ...
44
-
45
- const redDot = {
46
- name: "red-dot",
47
- install(app) {
48
- app.stage.appendChild(dot);
49
- },
50
- update(app) {
51
- const { smooth } = app.state;
52
- dot.style.transform = `translate3d(${smooth.x}px, ${smooth.y}px, 0)`;
53
- },
54
- destroy() {
55
- dot.remove();
56
- }
57
- };
58
-
59
- const mouse = new Supermouse({ plugins: [redDot] });
60
- ```
61
-
62
- ## Options
63
-
64
- | Option | Default | Description |
65
- | --------------------- | ----------------------------------------------------------------------- | --------------------------------------------- |
66
- | `smoothness` | `0.15` | Lower = smoother cursor follow |
67
- | `hoverSelectors` | `["a", "button", "input", "textarea", "[data-hover]", "[data-cursor]"]` | Selectors that set `state.isHover` |
68
- | `enableTouch` | `false` | Allow touch events to move cursor |
69
- | `autoDisableOnMobile` | `true` | Disable on coarse‑pointer devices |
70
- | `cursor` | `"auto"` | `"auto"`, `"custom"`, `"native"`, or `"both"` |
71
- | `hideOnLeave` | `true` | Hide when pointer leaves window |
72
- | `container` | `document.body` | Scoping container |
73
- | `zIndex` | `9999` | Stage stacking order |
74
- | `dataPrefix` | `"supermouse"` | Prefix for data attributes |
75
- | `rules` | — | Map of selectors to interaction data |
76
- | `plugins` | — | Plugins to install at creation |
77
- | `autoStart` | `true` | Set `false` and call `.start()` manually |
78
-
79
- ## Cursor Modes
80
-
81
- - `"auto"` – native cursor on interactive elements, custom elsewhere.
82
- - `"custom"` – always custom, hide native.
83
- - `"native"` – always native, hide custom.
84
- - `"both"` – show both native and custom together.
85
-
86
- Change at runtime with `mouse.setCursor("both")`.
87
-
88
- ## Containers & Multiple Instances
89
-
90
- Scope an instance to a container:
91
-
92
- ```ts
93
- const modal = document.getElementById("modal");
94
- const mouse = new Supermouse({ container: modal, cursor: "custom" });
95
- ```
96
-
97
- Multiple instances can coexist; CSS rules are scoped automatically.
98
-
99
- ## Native Cursor Fallback
100
-
101
- In `"auto"` mode, add `data-supermouse-ignore` to force the native cursor on an element and its descendants.
102
-
103
- ```html
104
- <div data-supermouse-ignore>Native cursor here</div>
105
- ```
106
-
107
- ## Data Attributes
108
-
109
- Plugins can react to `data-*` attributes, e.g. `data-supermouse-color`. The prefix is configurable via `dataPrefix`.
110
-
111
- ## Rules
112
-
113
- Define interaction data without writing many attributes:
114
-
115
- ```ts
116
- const mouse = new Supermouse({
117
- rules: {
118
- ".primary-action": { magnetic: true, color: "red" }
119
- }
120
- });
121
- ```
122
-
123
- HTML `data-*` attributes override rule values per property.
124
-
125
- ## API
126
-
127
- ```ts
128
- mouse.state; // current MouseState
129
- mouse.options; // resolved options
130
- mouse.stage; // DOM element plugins render into
131
- mouse.isEnabled; // boolean
132
-
133
- mouse.enable(); // start input, hide native cursor
134
- mouse.disable(); // stop input, show native cursor, reset
135
- mouse.suspend(); // pause non‑destructively
136
- mouse.resume(); // resume from suspend
137
-
138
- mouse.setCursor(mode); // "auto" | "custom" | "native" | "both"
139
-
140
- mouse.use(plugin); // install plugin
141
- mouse.getPlugin(name); // retrieve plugin
142
- mouse.enablePlugin(name); // enable plugin
143
- mouse.disablePlugin(name); // disable plugin
144
- mouse.togglePlugin(name); // toggle plugin
145
-
146
- mouse.registerHoverTarget(sel); // add hover selector at runtime
147
- mouse.start(); // start animation loop
148
- mouse.step(time); // advance one frame manually
149
- mouse.destroy(); // destroy instance and plugins
150
- ```
151
-
152
- ## Browser Support
153
-
154
- All modern browsers.
155
-
156
- ## License
157
-
158
- MIT
1
+ # @supermousejs/core
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@supermousejs/core.svg?style=flat-square)](https://www.npmjs.com/package/@supermousejs/core)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6
+ ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40supermousejs%2Fcore?label=minzip)
7
+
8
+ Supermouse is a headless, physics-based cursor engine for the web, written in TypeScript. It provides pointer tracking, state, physics, and a plugin lifecycle. You install plugins (or write your own) to render custom cursors and effects. Zero runtime dependencies.
9
+
10
+ **Documentation:** [supermouse.js.org](https://supermouse.js.org)
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @supermousejs/core
16
+ npm install @supermousejs/core
17
+ ```
18
+
19
+ Install plugins you need:
20
+
21
+ ```bash
22
+ pnpm add @supermousejs/dot @supermousejs/ring
23
+ npm install @supermousejs/dot @supermousejs/ring
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```ts
29
+ import { Supermouse } from "@supermousejs/core";
30
+ import { Dot } from "@supermousejs/dot";
31
+
32
+ const mouse = new Supermouse({
33
+ plugins: [Dot({ size: 8 })]
34
+ });
35
+ ```
36
+
37
+ Or write a plugin directly:
38
+
39
+ ```ts
40
+ import { Supermouse } from "@supermousejs/core";
41
+
42
+ const dot = document.createElement("div");
43
+ // ... style dot ...
44
+
45
+ const redDot = {
46
+ name: "red-dot",
47
+ install(app) {
48
+ app.stage.appendChild(dot);
49
+ },
50
+ update(app) {
51
+ const { smooth } = app.state;
52
+ dot.style.transform = `translate3d(${smooth.x}px, ${smooth.y}px, 0)`;
53
+ },
54
+ destroy() {
55
+ dot.remove();
56
+ }
57
+ };
58
+
59
+ const mouse = new Supermouse({ plugins: [redDot] });
60
+ ```
61
+
62
+ > Mount plugin elements to `app.stage`, not to `document.body`, so cursor suppression, scope switching, and cleanup work automatically.
63
+
64
+ ## Options
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 |
83
+
84
+ ## Cursor Modes
85
+
86
+ The `cursor` option decides which cursor(s) to show:
87
+
88
+ - `"auto"` — native cursor on elements that should own it (inputs, links), custom elsewhere.
89
+ - `"custom"` — always custom, hide native.
90
+ - `"native"` — always native, hide custom.
91
+ - `"both"` — show both native and custom together.
92
+
93
+ Change at runtime with `mouse.setCursor("both")`.
94
+
95
+ The [Cursor Policy](#cursor-policy) decides which elements are considered native in `"auto"` mode.
96
+
97
+ ## Cursor Policy
98
+
99
+ A single object maps selectors to two independent flags:
100
+
101
+ - `native` — in `"auto"` mode, matching elements yield to the OS cursor.
102
+ - `hide` — when the custom cursor is active, matching elements get `cursor: none !important`.
103
+
104
+ Extend the default policy:
105
+
106
+ ```ts
107
+ import { Supermouse, DEFAULT_CURSOR_POLICY } from "@supermousejs/core";
108
+
109
+ const mouse = new Supermouse({
110
+ cursorPolicy: {
111
+ rules: [
112
+ ...DEFAULT_CURSOR_POLICY.rules,
113
+ { selector: "[data-native]", native: true }
114
+ ]
115
+ }
116
+ });
117
+ ```
118
+
119
+ Or shorthand:
120
+
121
+ ```ts
122
+ const mouse = new Supermouse({
123
+ cursorPolicy: {
124
+ native: ["input", "textarea", "select", "[data-native]"],
125
+ hide: ["a", "button", "[role='button']"]
126
+ }
127
+ });
128
+ ```
129
+
130
+ ## Scopes
131
+
132
+ A single Supermouse instance can own multiple scopes. Each scope has its own container, cursor mode, hover selectors, cursor policy, and plugins. The innermost scope containing the pointer is the active one.
133
+
134
+ ```ts
135
+ const mouse = new Supermouse({
136
+ scopes: [
137
+ { name: "main", container: document.body, plugins: [Dot({ color: "red" })] },
138
+ { name: "sidebar", container: sidebarEl, cursor: "custom" }
139
+ ]
140
+ });
141
+ ```
142
+
143
+ Or add at runtime:
144
+
145
+ ```ts
146
+ const handle = mouse.addScope({ container: modalEl, cursor: "both" });
147
+ handle.setCursor("auto");
148
+ handle.remove();
149
+ ```
150
+
151
+ The single-container form is shorthand for one scope:
152
+
153
+ ```ts
154
+ const mouse = new Supermouse({ container: modalEl, cursor: "custom" });
155
+ ```
156
+
157
+ ## Native Cursor Fallback
158
+
159
+ In `"auto"` mode, add `data-supermouse-ignore` to force the native cursor on an element and its descendants:
160
+
161
+ ```html
162
+ <div data-supermouse-ignore>Native cursor here</div>
163
+ ```
164
+
165
+ ## Data Attributes
166
+
167
+ Plugins can react to `data-*` attributes (e.g. `data-supermouse-color`). Attributes cascade from ancestors to the hovered element when `inheritDataAttributes` is `true`. Change the prefix with `dataPrefix`.
168
+
169
+ ## Rules
170
+
171
+ Define interaction data without writing many attributes:
172
+
173
+ ```ts
174
+ const mouse = new Supermouse({
175
+ rules: {
176
+ ".primary-action": { magnetic: true, color: "red" }
177
+ }
178
+ });
179
+ ```
180
+
181
+ Rules match ancestors as well as the hovered element. HTML `data-*` attributes override rule values per property.
182
+
183
+ ## API
184
+
185
+ ```ts
186
+ mouse.state; // current MouseState
187
+ mouse.options; // resolved options
188
+ mouse.stage; // DOM element plugins render into
189
+ mouse.container; // container element (active scope)
190
+ mouse.isEnabled; // input processing flag
191
+ mouse.isRunning; // animation loop flag
192
+
193
+ mouse.enable(); // start input, apply cursor state
194
+ mouse.disable(); // stop input, restore native cursor
195
+ mouse.disable({ reset: true }); // also reset physics and hover state
196
+ mouse.reset(); // reset state only
197
+
198
+ mouse.setCursor(mode); // "auto" | "custom" | "native" | "both"
199
+
200
+ mouse.addScope(config); // -> { name, container, setCursor, remove }
201
+
202
+ mouse.use(plugin); // install plugin
203
+ mouse.getPlugin(name); // retrieve plugin
204
+ mouse.enablePlugin(name); // enable plugin
205
+ mouse.disablePlugin(name); // disable plugin
206
+ mouse.togglePlugin(name); // toggle plugin
207
+
208
+ mouse.registerHoverTarget(sel); // add hover selector at runtime
209
+ mouse.start(); // start animation loop
210
+ mouse.step(time); // advance one frame manually
211
+ mouse.destroy(); // destroy instance and plugins
212
+ ```
213
+
214
+ ## Browser Support
215
+
216
+ All modern browsers.
217
+
218
+ ## License
219
+
220
+ MIT