@supermousejs/labs 2.3.1 → 2.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @supermousejs/labs
2
2
 
3
- Advanced state machine that morphs SVGs based on semantic context.
3
+ Experimentental state machines and effects for supermouse
4
4
 
5
5
  ## Installation
6
6
 
@@ -31,10 +31,10 @@ app.use(SmartIcon({ icons: { ... } }));
31
31
  | transitionDuration | number | 200 | Morph transition time in ms. |
32
32
  | switchDelay | number | 80 | Minimum ms a state must be requested before committing to the next icon. |
33
33
  | size | number | 24 | Size of the icon container. |
34
- | color | string | 'black' | Icon fill color (currentColor). |
35
- | offset | [number, number] | [0, 0] | Fixed offset. |
36
- | anchor | string | 'center' | Alignment (center, top-left, etc). |
37
- | followStrategy | 'smooth' | 'raw' | 'smooth' | Whether the icon should smoothly follow the pointer or snap to it. |
34
+ | color | string | 'currentColor' | Color applied to the icon container. SVGs that use `currentColor` will inherit it. If empty, the icon keeps its original fill. |
35
+ | offset | [number, number] | [0, 0] | Fixed offset from the pointer position. |
36
+ | anchor | string | 'center' | Alignment (center, top-left, top-right, bottom-left, bottom-right). |
37
+ | followStrategy | 'smooth' | 'raw' | 'smooth' | Whether the icon smoothly follows the pointer or snaps to it. |
38
38
  | rotateWithVelocity | boolean | false | Rotate the icon based on movement velocity. |
39
39
 
40
40
  ## SmartRing
@@ -55,12 +55,12 @@ app.use(SmartRing({ size: 24, color: '#f59e0b', enableSkew: true }));
55
55
  | Name | Type | Default | Description |
56
56
  | --- | --- | --- | --- |
57
57
  | size | number | 20 | Base diameter. |
58
- | hoverSize | number | 40 | Diameter on hover. |
59
- | color | string | '#ffffff' | Border color used for the ring. |
60
- | fill | string | 'transparent' | Background fill color applied to the ring. |
58
+ | hoverSize | number | 40 | Diameter when hovering interactive elements. |
59
+ | color | string | '#ffffff' | Border color. |
60
+ | fill | string | 'transparent' | Background fill color. |
61
61
  | borderWidth | number | 2 | Border thickness in pixels. |
62
62
  | mixBlendMode | string | 'difference' | CSS mix-blend-mode applied to the ring element. |
63
- | enableSkew | boolean | false | Distort shape based on velocity. |
63
+ | enableSkew | boolean | false | Distort the ring based on velocity (stretch and rotate). |
64
64
 
65
65
  ## Sparkles
66
66
 
@@ -80,9 +80,9 @@ app.use(Sparkles({ color: '#f59e0b', frequency: 10 }));
80
80
  | Name | Type | Default | Description |
81
81
  | --- | --- | --- | --- |
82
82
  | color | string | '#ff00ff' | Particle color. |
83
- | count | number | 30 | Number of particles to keep in the pool. |
84
- | frequency | number | 10 | Pixels per spawn. |
85
- | decay | number | 0.05 | Fade rate. |
83
+ | count | number | 30 | Number of particles in the pool. |
84
+ | frequency | number | 10 | Pixels of movement required to spawn a particle. Lower values for a denser trail. |
85
+ | decay | number | 2.5 | Fade rate (0-1 per second). Higher values make for faster fading. |
86
86
  | scatter | number | 5 | Random position offset for particle spawning. |
87
87
 
88
88
  ## TextRing
@@ -103,13 +103,13 @@ app.use(TextRing({ text: 'LOADING • ', radius: 40 }));
103
103
  | Name | Type | Default | Description |
104
104
  | --- | --- | --- | --- |
105
105
  | text | string | 'SUPERMOUSE • SUPERMOUSE • ' | Text content. |
106
- | radius | number | 60 | Ring radius. |
106
+ | radius | number | 60 | Ring radius in pixels. |
107
107
  | fontSize | number | 12 | Text size in pixels. |
108
- | speed | number | 0.5 | Rotation speed deg/frame. |
109
- | color | string | 'currentColor' | Text color used by the SVG ring. |
108
+ | speed | number | 30 | Rotation speed in degrees per second. |
109
+ | color | string | 'currentColor' | Text color. |
110
110
  | opacity | number | 1 | Opacity of the rendered ring container. |
111
111
  | className | string | '' | Optional CSS class added to the container. |
112
- | spread | boolean | false | Stretch the text to fit around the full loop. |
112
+ | spread | boolean | true | Stretch the text to fit the entire circle using cross-browser letter-spacing. |
113
113
 
114
114
  ## Documentation
115
115
 
package/dist/index.d.ts CHANGED
@@ -3,9 +3,9 @@ import { ValueOrGetter } from '../packages/core/src';
3
3
 
4
4
  export declare const SmartIcon: (options: SmartIconOptions) => SupermousePlugin;
5
5
 
6
- export declare type SmartIconAnchor = "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
6
+ declare type SmartIconAnchor = "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
7
7
 
8
- export declare interface SmartIconMap {
8
+ declare interface SmartIconMap {
9
9
  [key: string]: string;
10
10
  }
11
11
 
@@ -19,6 +19,7 @@ export declare interface SmartIconOptions {
19
19
  /** Minimum ms a state must be requested before committing. Default 80. */
20
20
  switchDelay?: number;
21
21
  size?: ValueOrGetter<number>;
22
+ /** Color applied to icons that use `currentColor` in their SVG. */
22
23
  color?: ValueOrGetter<string>;
23
24
  offset?: [number, number];
24
25
  anchor?: ValueOrGetter<SmartIconAnchor>;
@@ -46,9 +47,9 @@ export declare interface SparklesOptions {
46
47
  name?: string;
47
48
  isEnabled?: boolean;
48
49
  color?: ValueOrGetter<string>;
49
- /** Number of particles to keep in the pool. Default 30. */
50
+ /** Number of particles in the pool. Default 30. */
50
51
  count?: number;
51
- /** How fast particles fade out (0.01 - 0.1). Default 0.05. */
52
+ /** How fast particles fade out (0–1 per second). Default 2.5 (matches old 0.05 per frame at 50fps). */
52
53
  decay?: number;
53
54
  /** Pixels of movement required to spawn a particle. Lower = denser trail. Default 10. */
54
55
  frequency?: number;
@@ -56,6 +57,10 @@ export declare interface SparklesOptions {
56
57
  scatter?: number;
57
58
  }
58
59
 
60
+ /**
61
+ * The SVG `textlength`/`lengthAdjust` attributes don't work/work reliably on Firefox-based browsers.
62
+ * So this implementation uses a workaround. Report: https://bugzilla.mozilla.org/show_bug.cgi?id=569722
63
+ */
59
64
  export declare const TextRing: (options?: TextRingOptions) => SupermousePlugin;
60
65
 
61
66
  export declare interface TextRingOptions {
package/dist/index.mjs CHANGED
@@ -1,36 +1,48 @@
1
- import { normalize as p, definePlugin as W, dom as l, math as b, Layers as D, effects as Y } from "@supermousejs/utils";
2
- import { getCirclePath as j, getCircumference as U, formatLoopText as B } from "@supermousejs/zoetrope";
3
- function q(t, a) {
4
- const o = t.tagName.toLowerCase();
5
- if (o === "input" || o === "textarea" || t.isContentEditable) {
6
- const i = t.type;
7
- if (["button", "submit", "checkbox", "radio", "range", "color"].includes(i)) {
8
- if (a.pointer)
1
+ import { normalizeAll as X, definePlugin as N, css as x, math as M, createActor as j, Layers as U, injectStyles as tt, damp as V, effects as et, lerpAngle as rt, setTransform as D, createCircle as _, normalize as nt, circlePath as st, circumference as ot, createSVGElement as G, path as it, text as at, textPath as lt } from "@supermousejs/utils";
2
+ function ct(t, e) {
3
+ const f = t.tagName.toLowerCase();
4
+ if (f === "input" || f === "textarea" || t.isContentEditable) {
5
+ const a = t.type;
6
+ if (["button", "submit", "checkbox", "radio", "range", "color"].includes(a)) {
7
+ if (e.pointer)
9
8
  return "pointer";
10
- } else if (a.text) return "text";
11
- } else if ((o === "a" || o === "button" || t.closest("a") || t.closest("button")) && a.pointer)
9
+ } else if (e.text) return "text";
10
+ } else if ((f === "a" || f === "button" || t.closest("a") || t.closest("button")) && e.pointer)
12
11
  return "pointer";
12
+ if (e.text && ["p", "span", "h1", "h2", "h3", "h4", "h5", "h6", "li", "blockquote", "code", "pre"].includes(
13
+ f
14
+ ))
15
+ return "text";
16
+ if (e.text || e.grab) {
17
+ const a = window.getComputedStyle(t).cursor;
18
+ if (e.text && a === "text") return "text";
19
+ if (e.grab && a === "grab") return "grab";
20
+ }
13
21
  return null;
14
22
  }
15
- const Q = (t) => {
16
- let a, o = t.defaultState || "default", i = t.defaultState || "default", f = null, T = null, C = !1, h, u = null, m = 0;
17
- const M = t.switchDelay ?? 80;
18
- let e = 0, y = 0;
19
- const x = p(t.size, 24), d = p(t.followStrategy, "smooth"), c = p(t.anchor, "center"), r = p(t.rotateWithVelocity, !1), R = t.useSemanticTags ?? !0, A = t.transitionDuration ?? 200, S = t.offset ? t.offset[0] : 0, n = t.offset ? t.offset[1] : 0;
23
+ const xt = (t) => {
24
+ let e, f = t.defaultState ?? "default", a = null, i, g = "idle", c = 0, C = f, u = null, n = 0, o = 0, z = 0;
25
+ const d = X(t, {
26
+ size: 24,
27
+ color: "",
28
+ followStrategy: "smooth",
29
+ anchor: "center",
30
+ rotateWithVelocity: !1
31
+ }), p = t.useSemanticTags ?? !0, m = t.transitionDuration ?? 200, r = t.switchDelay ?? 80, h = t.offset?.[0] ?? 0, y = t.offset?.[1] ?? 0, A = t.defaultState ?? "default", b = m / 2;
20
32
  function v(s) {
21
- i = s, C = !0, clearTimeout(h), a.style.transform = "scale(0)", h = setTimeout(() => {
22
- o = i, a.innerHTML = t.icons[o] || "", a.style.transform = "scale(1)", h = setTimeout(() => {
23
- C = !1;
24
- }, A / 2);
25
- }, A / 2);
33
+ x(e, { transform: `scale(${s})` });
34
+ }
35
+ function S(s) {
36
+ f = s, e.innerHTML = t.icons[s] ?? "";
26
37
  }
27
- return W(
38
+ let l;
39
+ return N(
28
40
  {
29
- name: t.name || "smart-icon",
41
+ name: t.name ?? "smart-icon",
30
42
  selector: "[data-supermouse-icon]",
31
- create: () => {
32
- const s = l.createActor("div");
33
- return s.style.zIndex = D.CURSOR, a = l.createActor("div"), l.applyStyles(a, {
43
+ create: (s) => {
44
+ const w = j("div");
45
+ return x(w, { zIndex: U.CURSOR }), p && s.registerHoverTarget("p, span, h1, h2, h3, h4, h5, h6, li, blockquote, code, pre"), e = j("div"), x(e, {
34
46
  width: "100%",
35
47
  height: "100%",
36
48
  display: "flex",
@@ -38,116 +50,169 @@ const Q = (t) => {
38
50
  justifyContent: "center",
39
51
  transformOrigin: "center center",
40
52
  transform: "scale(1)",
41
- transition: `transform ${A / 2}ms cubic-bezier(0.16, 1, 0.3, 1)`
42
- }), a.innerHTML = t.icons[o] || "", l.injectStyles(
53
+ transition: `transform ${b}ms cubic-bezier(0.16, 1, 0.3, 1)`
54
+ }), e.innerHTML = t.icons[f] ?? "", tt(
43
55
  "supermouse-smart-icon-styles",
44
56
  `
45
- @keyframes sm {
46
- 0% { transform: rotate(0deg); }
47
- 100% { transform: rotate(360deg); }
48
- }
49
- .supermouse-spin {
50
- animation: sm 1s linear infinite;
51
- }
52
- `
53
- ), s.appendChild(a), s;
54
- },
55
- styles: {
56
- color: "color"
57
+ @keyframes sm {
58
+ 0% { transform: rotate(0deg); }
59
+ 100% { transform: rotate(360deg); }
60
+ }
61
+ .supermouse-spin {
62
+ animation: sm 1s linear infinite;
63
+ }
64
+ `
65
+ ), l = s.state.cursorMode, s.setCursor("custom"), w.appendChild(e), w;
57
66
  },
58
- update: (s, w, g) => {
59
- const z = t.icons, L = s.state.hoverTarget;
60
- let E = t.defaultState || "default";
61
- if (L) {
62
- L !== f && (f = L, T = R ? q(L, z) : null);
63
- const N = s.state.interaction?.icon;
64
- N && z[N] ? E = N : T && (E = T);
65
- } else
66
- f = null, T = null;
67
- E !== o && !C ? !z[E] && E !== (t.defaultState || "default") ? (u = null, m = 0) : E !== u ? (u = E, m = 0) : (m += g, m >= M && (v(E), u = null, m = 0)) : E === o && (u = null, m = 0);
68
- const k = x(s.state);
69
- l.setStyle(w, "width", `${k}px`), l.setStyle(w, "height", `${k}px`);
70
- let $ = 0, P = 0;
71
- const I = k / 2, O = c(s.state);
72
- O !== "center" && (O.includes("left") && ($ = I), O.includes("right") && ($ = -I), O.includes("top") && (P = I), O.includes("bottom") && (P = -I));
73
- const X = o === "pointer" || o === "text";
74
- if (r(s.state) && !X && !s.state.reducedMotion) {
75
- const { x: N, y: H } = s.state.velocity;
76
- b.dist(N, H) > 1 && (y = b.angle(N, H)), e = b.lerpAngle(e, y, 0.15);
67
+ update: (s, w, $) => {
68
+ const T = performance.now(), F = $ / 1e3, O = t.icons, k = s.state.hoverTarget, H = !!k, I = H && k !== a;
69
+ let R = A;
70
+ if (H) {
71
+ I && (a = k, i = p ? ct(k, O) : null);
72
+ const L = s.state.interaction?.icon;
73
+ L && O[L] ? R = L : i && (R = i);
77
74
  } else
78
- e = b.lerpAngle(e, 0, 0.15);
79
- const F = d(s.state) === "raw" ? s.state.pointer : s.state.smooth;
80
- l.setTransform(
81
- w,
82
- F.x + S + $,
83
- F.y + n + P,
84
- e
85
- );
75
+ a && (a = null, i = null);
76
+ R !== f && g === "idle" ? !!O[R] || R === A ? I ? (S(R), v(1), u = null, n = 0) : R !== u ? (u = R, n = 0) : (n += $, n >= r && (C = R, g = "out", c = T, v(0), u = null, n = 0)) : (u = null, n = 0) : R === f && (u = null, n = 0), g === "out" && T - c >= b && (S(C), g = "in", c = T, v(0)), g === "in" && T - c >= b && (g = "idle", v(1));
77
+ const E = d.size(s.state), J = d.color(s.state);
78
+ x(w, {
79
+ width: `${E}px`,
80
+ height: `${E}px`,
81
+ color: J || "inherit"
82
+ });
83
+ let P = 0, W = 0;
84
+ const Y = E / 2;
85
+ switch (d.anchor(s.state)) {
86
+ case "center":
87
+ P = -Y, W = -Y;
88
+ break;
89
+ case "top-left":
90
+ P = 0, W = 0;
91
+ break;
92
+ case "top-right":
93
+ P = -E, W = 0;
94
+ break;
95
+ case "bottom-left":
96
+ P = 0, W = -E;
97
+ break;
98
+ case "bottom-right":
99
+ P = -E, W = -E;
100
+ break;
101
+ }
102
+ const K = f === "pointer" || f === "text";
103
+ if (d.rotateWithVelocity(s.state) && !K && !s.state.reducedMotion) {
104
+ const { x: L, y: B } = s.state.velocity;
105
+ M.dist(L, B) > 1 && (z = M.angle(L, B));
106
+ const Z = 1 - Math.exp(-20 * F);
107
+ o = M.lerpAngle(o, z, Z);
108
+ } else {
109
+ const L = 1 - Math.exp(-20 * F);
110
+ o = M.lerpAngle(o, 0, L);
111
+ }
112
+ const q = d.followStrategy(s.state) === "raw" ? s.state.pointer : s.state.smooth, Q = `translate3d(${q.x + h + P}px, ${q.y + y + W}px, 0) rotate(${o}deg)`;
113
+ w.style.transform = Q;
86
114
  },
87
- cleanup() {
88
- clearTimeout(h);
115
+ cleanup(s) {
116
+ s.setCursor(l);
89
117
  }
90
118
  },
91
119
  t
92
120
  );
93
- }, Z = (t = {}) => {
94
- const a = p(t.size, 20), o = p(t.hoverSize, 40), i = p(t.color, "#ffffff"), f = p(t.borderWidth, 2), T = p(t.fill, "transparent");
95
- let C = 20, h = 20, u = 0, m = 1, M = 1;
96
- return W(
121
+ }, gt = (t = {}) => {
122
+ const e = X(t, {
123
+ size: 20,
124
+ hoverSize: 40,
125
+ color: "#ffffff",
126
+ fill: "transparent",
127
+ borderWidth: 2
128
+ });
129
+ let f = 20, a = 20, i = 0, g = 1, c = 1;
130
+ const C = 150;
131
+ let u = null;
132
+ return N(
97
133
  {
98
- name: t.name || "smart-ring",
134
+ name: t.name ?? "smart-ring",
99
135
  selector: "[data-supermouse-color]",
100
- create: (e) => {
101
- const y = l.createCircle(a(e.state), T(e.state));
102
- return l.applyStyles(y, {
103
- zIndex: D.FOLLOWER,
104
- mixBlendMode: t.mixBlendMode || "difference",
105
- transition: "opacity 0.2s ease, border-radius 0.2s ease",
106
- borderStyle: "solid"
107
- }), y;
136
+ create: (n) => {
137
+ const o = _(e.size(n.state), e.fill(n.state));
138
+ return x(o, {
139
+ zIndex: U.FOLLOWER,
140
+ mixBlendMode: t.mixBlendMode ?? "difference",
141
+ transition: "border-radius 0.2s ease",
142
+ borderStyle: "solid",
143
+ opacity: "0"
144
+ }), o;
145
+ },
146
+ onEnable(n, o) {
147
+ const z = e.size(n.state);
148
+ f = z * 0.5, a = z * 0.5, i = 0, g = 1, c = 1, clearTimeout(u ?? void 0), o.style.display = "block", o.style.opacity = "1", o.style.transition = "border-radius 0.2s ease";
108
149
  },
109
- update: (e, y) => {
110
- const x = a(e.state), d = e.state.shape;
111
- let c = x, r = x, R = "50%", A = i(e.state);
112
- d ? (c = d.width, r = d.height, R = `${d.borderRadius}px`) : (e.state.isHover && (c = o(e.state), r = o(e.state)), e.state.isDown && (c *= 0.9, r *= 0.9)), e.state.interaction.color && (A = e.state.interaction.color), C = b.lerp(C, c, 0.2), h = b.lerp(h, r, 0.2), l.setStyle(y, "width", `${C}px`), l.setStyle(y, "height", `${h}px`), l.setStyle(y, "borderRadius", R), l.setStyle(y, "borderColor", A), l.setStyle(y, "backgroundColor", T(e.state)), l.setStyle(y, "borderWidth", `${f(e.state)}px`);
113
- let S = 0, n = 1, v = 1;
114
- if (!d && t.enableSkew && !e.state.reducedMotion) {
115
- const { velocity: g } = e.state, z = Y.getVelocityDistortion(g.x, g.y);
116
- S = z.rotation, n = z.scaleX, v = z.scaleY, u = b.lerpAngle(u, S, 0.15);
150
+ beforeDisable(n, o) {
151
+ const { x: z, y: d } = n.state.smooth;
152
+ return o.style.transition = `opacity ${C}ms ease, transform ${C}ms ease`, o.style.opacity = "0", D(o, z, d, 0, 0.2, 0.2), new Promise((p) => {
153
+ u = setTimeout(() => {
154
+ o.style.transition = "border-radius 0.2s ease", p();
155
+ }, C);
156
+ });
157
+ },
158
+ update: (n, o, z) => {
159
+ if (o.style.display === "none") return;
160
+ const d = z / 1e3, p = e.size(n.state), m = n.state.shape;
161
+ let r = p, h = p, y = "50%", A = e.color(n.state);
162
+ m ? (r = m.width, h = m.height, y = `${m.borderRadius}px`) : (n.state.isHover && (r = e.hoverSize(n.state), h = e.hoverSize(n.state)), n.state.isDown && (r *= 0.9, h *= 0.9)), n.state.interaction.color && (A = n.state.interaction.color), f = V(f, r, 20, d), a = V(a, h, 20, d), x(o, {
163
+ width: `${f}px`,
164
+ height: `${a}px`,
165
+ borderRadius: y,
166
+ borderColor: A,
167
+ backgroundColor: e.fill(n.state),
168
+ borderWidth: `${e.borderWidth(n.state)}px`,
169
+ opacity: "1"
170
+ });
171
+ let b = 0, v = 1, S = 1;
172
+ if (!m && t.enableSkew && !n.state.reducedMotion) {
173
+ const { displacement: w } = n.state, $ = et.getVelocityDistortion(w.x, w.y);
174
+ b = $.rotation, v = $.scaleX, S = $.scaleY;
175
+ const T = 1 - Math.exp(-20 * d);
176
+ i = rt(i, b, T);
117
177
  } else
118
- u = 0;
119
- m = b.lerp(m, n, 0.15), M = b.lerp(M, v, 0.15);
120
- const { x: s, y: w } = e.state.smooth;
121
- l.setTransform(y, s, w, u, m, M);
178
+ i = 0;
179
+ g = V(g, v, 20, d), c = V(c, S, 20, d);
180
+ const { x: l, y: s } = n.state.smooth;
181
+ D(o, l, s, i, g, c);
122
182
  }
123
183
  },
124
184
  t
125
185
  );
126
- }, _ = (t = {}) => {
127
- const a = t.count || 30, o = t.decay || 0.05, i = t.frequency || 10, f = t.scatter || 5, C = p(t.color, "#ff00ff"), h = [];
128
- let u = 0, m = 0, M = !1, e = 0;
129
- const y = (x, d, c) => {
130
- const r = h.find((n) => !n.isActive);
186
+ }, yt = (t = {}) => {
187
+ const e = t.count ?? 30, f = t.decay ?? 2.5, a = t.frequency ?? 10, i = t.scatter ?? 5, g = nt(t.color, "#ff00ff"), c = [];
188
+ let C = 0, u = 0, n = !1, o = 0;
189
+ const z = (d, p, m) => {
190
+ const r = c.find((b) => !b.isActive);
131
191
  if (!r) return;
132
- r.isActive = !0, r.life = 1, r.x = x + b.random(-f, f), r.y = d + b.random(-f, f);
133
- const R = b.random(0, Math.PI * 2), A = b.random(0.5, 1.5);
134
- r.vx = Math.cos(R) * A, r.vy = Math.sin(R) * A, r.scale = b.random(0.5, 1.2), r.color = c;
135
- const S = b.random(2, 5);
136
- l.setStyle(r.el, "width", `${S}px`), l.setStyle(r.el, "height", `${S}px`), l.setStyle(r.el, "backgroundColor", c), l.setStyle(r.el, "opacity", "1"), l.setTransform(r.el, r.x, r.y, 0, r.scale, r.scale);
192
+ r.isActive = !0, r.life = 1, r.x = d + M.random(-i, i), r.y = p + M.random(-i, i);
193
+ const h = M.random(0, Math.PI * 2), y = M.random(0.5, 1.5);
194
+ r.vx = Math.cos(h) * y, r.vy = Math.sin(h) * y, r.scale = M.random(0.5, 1.2), r.color = m;
195
+ const A = M.random(2, 5);
196
+ x(r.el, {
197
+ width: `${A}px`,
198
+ height: `${A}px`,
199
+ backgroundColor: m,
200
+ opacity: "1"
201
+ }), D(r.el, r.x, r.y, 0, r.scale, r.scale);
137
202
  };
138
- return W(
203
+ return N(
139
204
  {
140
- name: t.name || "sparkles",
141
- install(x) {
142
- for (let d = 0; d < a; d++) {
143
- const c = l.createCircle(0, "transparent");
144
- l.applyStyles(c, {
145
- zIndex: D.TRACE,
205
+ name: t.name ?? "sparkles",
206
+ create: () => {
207
+ const d = j("div");
208
+ x(d, { zIndex: U.TRACE });
209
+ for (let p = 0; p < e; p++) {
210
+ const m = _(0, "transparent");
211
+ x(m, {
146
212
  opacity: "0",
147
- willChange: "transform, opacity",
148
- transition: "none"
149
- }), x.container.appendChild(c), h.push({
150
- el: c,
213
+ willChange: "transform, opacity"
214
+ }), d.appendChild(m), c.push({
215
+ el: m,
151
216
  isActive: !1,
152
217
  x: 0,
153
218
  y: 0,
@@ -158,52 +223,62 @@ const Q = (t) => {
158
223
  color: ""
159
224
  });
160
225
  }
226
+ return d;
161
227
  },
162
- update(x) {
163
- const { x: d, y: c } = x.state.pointer;
164
- M || (u = d, m = c, M = !0);
165
- const r = d - u, R = c - m, A = Math.hypot(r, R);
166
- if (e += A, e > i) {
167
- const S = C(x.state), n = Math.floor(e / i);
168
- for (let v = 0; v < n; v++) {
169
- const s = v / n, w = u + r * s, g = m + R * s;
170
- y(w, g, S);
228
+ update: (d, p, m) => {
229
+ const r = m / 1e3, { x: h, y } = d.state.pointer;
230
+ n || (C = h, u = y, n = !0);
231
+ const A = h - C, b = y - u, v = Math.hypot(A, b);
232
+ if (o += v, o > a) {
233
+ const S = g(d.state), l = Math.floor(o / a);
234
+ for (let s = 0; s < l; s++) {
235
+ const w = s / l;
236
+ z(C + A * w, u + b * w, S);
171
237
  }
172
- e = e % i;
238
+ o = o % a;
173
239
  }
174
- u = d, m = c;
175
- for (let S = 0; S < a; S++) {
176
- const n = h[S];
177
- if (n.isActive)
178
- if (n.x += n.vx, n.y += n.vy, n.life -= o, n.life <= 0)
179
- n.isActive = !1, n.el.style.opacity = "0";
240
+ C = h, u = y;
241
+ for (let S = 0; S < e; S++) {
242
+ const l = c[S];
243
+ if (l.isActive)
244
+ if (l.x += l.vx * r * 60, l.y += l.vy * r * 60, l.life -= f * r, l.life <= 0)
245
+ l.isActive = !1, x(l.el, { opacity: "0" });
180
246
  else {
181
- n.el.style.opacity = String(n.life);
182
- const v = n.scale * n.life;
183
- l.setTransform(n.el, n.x, n.y, 0, v, v);
247
+ const s = l.scale * l.life;
248
+ x(l.el, { opacity: String(l.life) }), D(l.el, l.x, l.y, 0, s, s);
184
249
  }
185
250
  }
186
251
  },
187
- destroy() {
188
- h.forEach((x) => x.el.remove()), h.length = 0;
252
+ destroy: () => {
253
+ c.length = 0;
189
254
  }
190
255
  },
191
256
  t
192
257
  );
193
258
  };
194
- let V = 0;
195
- const tt = (t = {}) => {
196
- let a, o, i, f, T;
197
- const C = `supermouse-text-ring-path-${V++}`, h = "SUPERMOUSE • SUPERMOUSE • ", u = 60, m = 12, M = 0.5, e = t.className || "", y = t.spread ?? !1, x = p(t.text, h), d = p(t.radius, u), c = p(t.fontSize, m), r = p(t.speed, M), R = p(t.opacity, 1);
198
- let A = 0, S = "", n = 0, v = 0;
199
- return W(
259
+ function dt(t, e) {
260
+ return e ? t + " " : t;
261
+ }
262
+ let ft = 0;
263
+ const pt = (t = {}) => {
264
+ let e, f, a, i, g, c;
265
+ const C = `supermouse-text-ring-path-${ft++}`, u = X(t, {
266
+ text: "SUPERMOUSE • SUPERMOUSE • ",
267
+ radius: 60,
268
+ fontSize: 12,
269
+ speed: 30,
270
+ opacity: 1,
271
+ color: "currentColor"
272
+ }), n = t.className ?? "", o = t.spread ?? !0;
273
+ let z = 0, d = "", p = 0, m = 0;
274
+ return N(
200
275
  {
201
- name: "text-ring",
276
+ name: t.name ?? "text-ring",
202
277
  selector: "[data-supermouse-text-ring]",
203
- create: (s) => {
204
- const w = l.createActor("div");
205
- l.applyStyles(w, {
206
- zIndex: D.FOLLOWER,
278
+ create: (r) => {
279
+ const h = j("div");
280
+ x(h, {
281
+ zIndex: U.FOLLOWER,
207
282
  transition: "opacity 0.2s ease",
208
283
  opacity: "1",
209
284
  width: "0px",
@@ -212,34 +287,61 @@ const tt = (t = {}) => {
212
287
  display: "flex",
213
288
  alignItems: "center",
214
289
  justifyContent: "center"
215
- }), e && w.classList.add(...e.split(" ").filter(Boolean)), a = document.createElementNS("http://www.w3.org/2000/svg", "svg"), a.style.overflow = "visible", a.style.position = "absolute", a.style.left = "0", a.style.top = "0", o = document.createElementNS("http://www.w3.org/2000/svg", "path"), o.setAttribute("id", C), o.setAttribute("fill", "none"), f = document.createElementNS("http://www.w3.org/2000/svg", "text");
216
- const g = c(s.state);
217
- return f.setAttribute("font-size", `${g}px`), v = g, f.setAttribute("fill", "currentColor"), f.style.textTransform = "uppercase", f.style.letterSpacing = "2px", i = document.createElementNS("http://www.w3.org/2000/svg", "textPath"), i.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", `#${C}`), i.setAttribute("href", `#${C}`), i.setAttribute("startOffset", "0%"), T = document.createTextNode(""), i.appendChild(T), f.appendChild(i), a.appendChild(o), a.appendChild(f), w.appendChild(a), w;
218
- },
219
- styles: {
220
- color: "color"
290
+ }), n && h.classList.add(...n.split(" ").filter(Boolean)), e = G("svg"), x(e, {
291
+ overflow: "visible",
292
+ position: "absolute",
293
+ left: "0",
294
+ top: "0"
295
+ }), f = it(void 0, { id: C, fill: "none" });
296
+ const y = u.fontSize(r.state);
297
+ return m = y, i = at({ "font-size": `${y}px`, fill: "currentColor" }), x(i, {
298
+ textTransform: "uppercase",
299
+ letterSpacing: "2px"
300
+ }), a = lt(`#${C}`, { startOffset: "0%" }), g = document.createTextNode(""), a.appendChild(g), i.appendChild(a), e.appendChild(f), e.appendChild(i), c = G("text"), x(c, {
301
+ visibility: "hidden",
302
+ position: "absolute",
303
+ pointerEvents: "none",
304
+ whiteSpace: "nowrap"
305
+ }), c.setAttribute("font-size", `${y}px`), c.setAttribute("font-family", getComputedStyle(i).fontFamily || "sans-serif"), c.setAttribute("font-weight", getComputedStyle(i).fontWeight || "normal"), c.setAttribute("font-style", getComputedStyle(i).fontStyle || "normal"), e.appendChild(c), h.appendChild(e), h;
221
306
  },
222
- update: (s, w) => {
223
- let g = x(s.state);
224
- const z = d(s.state), L = c(s.state), E = r(s.state), k = R(s.state);
225
- l.setStyle(w, "opacity", String(k));
226
- const $ = s.state.interaction;
227
- if ($.textRing && typeof $.textRing == "string" ? g = $.textRing : $.text && typeof $.text == "string" && (g = $.text), z !== n && (o.setAttribute("d", j(z)), n = z), y) {
228
- const O = U(z);
229
- i.setAttribute("textLength", String(O)), i.setAttribute("lengthAdjust", "spacing"), g = B(g, !0);
307
+ update: (r, h, y) => {
308
+ const A = y / 1e3;
309
+ let b = u.text(r.state);
310
+ const v = u.radius(r.state), S = u.fontSize(r.state), l = u.speed(r.state), s = u.opacity(r.state), w = u.color(r.state);
311
+ x(h, { color: w, opacity: String(s) });
312
+ const $ = r.state.interaction;
313
+ if ($.textRing && typeof $.textRing == "string" ? b = $.textRing : $.text && typeof $.text == "string" && (b = $.text), v !== p) {
314
+ f.setAttribute("d", st(v));
315
+ const k = v * 2;
316
+ x(e, {
317
+ width: `${k}px`,
318
+ height: `${k}px`
319
+ }), p = v;
320
+ }
321
+ const T = dt(b, o);
322
+ if (S !== m && (i.setAttribute("font-size", `${S}px`), c.setAttribute("font-size", `${S}px`), m = S), T !== d && (g.textContent = T, d = T), o) {
323
+ const k = ot(v);
324
+ c.textContent = T;
325
+ const H = c.getComputedTextLength(), I = T.length;
326
+ if (I > 1) {
327
+ const E = (k - H) / (I - 1);
328
+ x(i, { letterSpacing: `${E}px` });
329
+ } else
330
+ x(i, { letterSpacing: "0px" });
331
+ i.removeAttribute("textLength"), a.removeAttribute("textLength"), a.removeAttribute("lengthAdjust");
230
332
  } else
231
- i.removeAttribute("textLength"), i.removeAttribute("lengthAdjust");
232
- L !== v && (f.setAttribute("font-size", `${L}px`), v = L), g !== S && (T.textContent = g, S = g), A += E;
233
- const { x: P, y: I } = s.state.smooth;
234
- l.setTransform(w, P, I, A);
333
+ x(i, { letterSpacing: "2px" }), i.removeAttribute("textLength"), a.removeAttribute("textLength"), a.removeAttribute("lengthAdjust");
334
+ z += l * A;
335
+ const { x: F, y: O } = r.state.smooth;
336
+ D(h, F, O, z);
235
337
  }
236
338
  },
237
339
  t
238
340
  );
239
341
  };
240
342
  export {
241
- Q as SmartIcon,
242
- Z as SmartRing,
243
- _ as Sparkles,
244
- tt as TextRing
343
+ xt as SmartIcon,
344
+ gt as SmartRing,
345
+ yt as Sparkles,
346
+ pt as TextRing
245
347
  };
package/dist/index.umd.js CHANGED
@@ -1,9 +1,9 @@
1
- (function(C,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("@supermousejs/utils"),require("@supermousejs/zoetrope")):typeof define=="function"&&define.amd?define(["exports","@supermousejs/utils","@supermousejs/zoetrope"],e):(C=typeof globalThis<"u"?globalThis:C||self,e(C.SupermouseLabs={},C.SupermouseUtils,C.SupermouseZoetrope))})(this,(function(C,e,k){"use strict";function D(t,s){const c=t.tagName.toLowerCase();if(c==="input"||c==="textarea"||t.isContentEditable){const l=t.type;if(["button","submit","checkbox","radio","range","color"].includes(l)){if(s.pointer)return"pointer"}else if(s.text)return"text"}else if((c==="a"||c==="button"||t.closest("a")||t.closest("button"))&&s.pointer)return"pointer";return null}const F=t=>{let s,c=t.defaultState||"default",l=t.defaultState||"default",d=null,z=null,w=!1,g,m=null,u=0;const E=t.switchDelay??80;let n=0,y=0;const x=e.normalize(t.size,24),f=e.normalize(t.followStrategy,"smooth"),i=e.normalize(t.anchor,"center"),o=e.normalize(t.rotateWithVelocity,!1),T=t.useSemanticTags??!0,b=t.transitionDuration??200,S=t.offset?t.offset[0]:0,a=t.offset?t.offset[1]:0;function p(r){l=r,w=!0,clearTimeout(g),s.style.transform="scale(0)",g=setTimeout(()=>{c=l,s.innerHTML=t.icons[c]||"",s.style.transform="scale(1)",g=setTimeout(()=>{w=!1},b/2)},b/2)}return e.definePlugin({name:t.name||"smart-icon",selector:"[data-supermouse-icon]",create:()=>{const r=e.dom.createActor("div");return r.style.zIndex=e.Layers.CURSOR,s=e.dom.createActor("div"),e.dom.applyStyles(s,{width:"100%",height:"100%",display:"flex",alignItems:"center",justifyContent:"center",transformOrigin:"center center",transform:"scale(1)",transition:`transform ${b/2}ms cubic-bezier(0.16, 1, 0.3, 1)`}),s.innerHTML=t.icons[c]||"",e.dom.injectStyles("supermouse-smart-icon-styles",`
2
- @keyframes sm {
3
- 0% { transform: rotate(0deg); }
4
- 100% { transform: rotate(360deg); }
5
- }
6
- .supermouse-spin {
7
- animation: sm 1s linear infinite;
8
- }
9
- `),r.appendChild(s),r},styles:{color:"color"},update:(r,v,h)=>{const A=t.icons,M=r.state.hoverTarget;let R=t.defaultState||"default";if(M){M!==d&&(d=M,z=T?D(M,A):null);const P=r.state.interaction?.icon;P&&A[P]?R=P:z&&(R=z)}else d=null,z=null;R!==c&&!w?!A[R]&&R!==(t.defaultState||"default")?(m=null,u=0):R!==m?(m=R,u=0):(u+=h,u>=E&&(p(R),m=null,u=0)):R===c&&(m=null,u=0);const j=x(r.state);e.dom.setStyle(v,"width",`${j}px`),e.dom.setStyle(v,"height",`${j}px`);let L=0,I=0;const O=j/2,$=i(r.state);$!=="center"&&($.includes("left")&&(L=O),$.includes("right")&&(L=-O),$.includes("top")&&(I=O),$.includes("bottom")&&(I=-O));const q=c==="pointer"||c==="text";if(o(r.state)&&!q&&!r.state.reducedMotion){const{x:P,y:W}=r.state.velocity;e.math.dist(P,W)>1&&(y=e.math.angle(P,W)),n=e.math.lerpAngle(n,y,.15)}else n=e.math.lerpAngle(n,0,.15);const N=f(r.state)==="raw"?r.state.pointer:r.state.smooth;e.dom.setTransform(v,N.x+S+L,N.y+a+I,n)},cleanup(){clearTimeout(g)}},t)},H=(t={})=>{const s=e.normalize(t.size,20),c=e.normalize(t.hoverSize,40),l=e.normalize(t.color,"#ffffff"),d=e.normalize(t.borderWidth,2),z=e.normalize(t.fill,"transparent");let w=20,g=20,m=0,u=1,E=1;return e.definePlugin({name:t.name||"smart-ring",selector:"[data-supermouse-color]",create:n=>{const y=e.dom.createCircle(s(n.state),z(n.state));return e.dom.applyStyles(y,{zIndex:e.Layers.FOLLOWER,mixBlendMode:t.mixBlendMode||"difference",transition:"opacity 0.2s ease, border-radius 0.2s ease",borderStyle:"solid"}),y},update:(n,y)=>{const x=s(n.state),f=n.state.shape;let i=x,o=x,T="50%",b=l(n.state);f?(i=f.width,o=f.height,T=`${f.borderRadius}px`):(n.state.isHover&&(i=c(n.state),o=c(n.state)),n.state.isDown&&(i*=.9,o*=.9)),n.state.interaction.color&&(b=n.state.interaction.color),w=e.math.lerp(w,i,.2),g=e.math.lerp(g,o,.2),e.dom.setStyle(y,"width",`${w}px`),e.dom.setStyle(y,"height",`${g}px`),e.dom.setStyle(y,"borderRadius",T),e.dom.setStyle(y,"borderColor",b),e.dom.setStyle(y,"backgroundColor",z(n.state)),e.dom.setStyle(y,"borderWidth",`${d(n.state)}px`);let S=0,a=1,p=1;if(!f&&t.enableSkew&&!n.state.reducedMotion){const{velocity:h}=n.state,A=e.effects.getVelocityDistortion(h.x,h.y);S=A.rotation,a=A.scaleX,p=A.scaleY,m=e.math.lerpAngle(m,S,.15)}else m=0;u=e.math.lerp(u,a,.15),E=e.math.lerp(E,p,.15);const{x:r,y:v}=n.state.smooth;e.dom.setTransform(y,r,v,m,u,E)}},t)},U=(t={})=>{const s=t.count||30,c=t.decay||.05,l=t.frequency||10,d=t.scatter||5,w=e.normalize(t.color,"#ff00ff"),g=[];let m=0,u=0,E=!1,n=0;const y=(x,f,i)=>{const o=g.find(a=>!a.isActive);if(!o)return;o.isActive=!0,o.life=1,o.x=x+e.math.random(-d,d),o.y=f+e.math.random(-d,d);const T=e.math.random(0,Math.PI*2),b=e.math.random(.5,1.5);o.vx=Math.cos(T)*b,o.vy=Math.sin(T)*b,o.scale=e.math.random(.5,1.2),o.color=i;const S=e.math.random(2,5);e.dom.setStyle(o.el,"width",`${S}px`),e.dom.setStyle(o.el,"height",`${S}px`),e.dom.setStyle(o.el,"backgroundColor",i),e.dom.setStyle(o.el,"opacity","1"),e.dom.setTransform(o.el,o.x,o.y,0,o.scale,o.scale)};return e.definePlugin({name:t.name||"sparkles",install(x){for(let f=0;f<s;f++){const i=e.dom.createCircle(0,"transparent");e.dom.applyStyles(i,{zIndex:e.Layers.TRACE,opacity:"0",willChange:"transform, opacity",transition:"none"}),x.container.appendChild(i),g.push({el:i,isActive:!1,x:0,y:0,vx:0,vy:0,life:0,scale:1,color:""})}},update(x){const{x:f,y:i}=x.state.pointer;E||(m=f,u=i,E=!0);const o=f-m,T=i-u,b=Math.hypot(o,T);if(n+=b,n>l){const S=w(x.state),a=Math.floor(n/l);for(let p=0;p<a;p++){const r=p/a,v=m+o*r,h=u+T*r;y(v,h,S)}n=n%l}m=f,u=i;for(let S=0;S<s;S++){const a=g[S];if(a.isActive)if(a.x+=a.vx,a.y+=a.vy,a.life-=c,a.life<=0)a.isActive=!1,a.el.style.opacity="0";else{a.el.style.opacity=String(a.life);const p=a.scale*a.life;e.dom.setTransform(a.el,a.x,a.y,0,p,p)}}},destroy(){g.forEach(x=>x.el.remove()),g.length=0}},t)};let X=0;const Y=(t={})=>{let s,c,l,d,z;const w=`supermouse-text-ring-path-${X++}`,g="SUPERMOUSE • SUPERMOUSE • ",m=60,u=12,E=.5,n=t.className||"",y=t.spread??!1,x=e.normalize(t.text,g),f=e.normalize(t.radius,m),i=e.normalize(t.fontSize,u),o=e.normalize(t.speed,E),T=e.normalize(t.opacity,1);let b=0,S="",a=0,p=0;return e.definePlugin({name:"text-ring",selector:"[data-supermouse-text-ring]",create:r=>{const v=e.dom.createActor("div");e.dom.applyStyles(v,{zIndex:e.Layers.FOLLOWER,transition:"opacity 0.2s ease",opacity:"1",width:"0px",height:"0px",overflow:"visible",display:"flex",alignItems:"center",justifyContent:"center"}),n&&v.classList.add(...n.split(" ").filter(Boolean)),s=document.createElementNS("http://www.w3.org/2000/svg","svg"),s.style.overflow="visible",s.style.position="absolute",s.style.left="0",s.style.top="0",c=document.createElementNS("http://www.w3.org/2000/svg","path"),c.setAttribute("id",w),c.setAttribute("fill","none"),d=document.createElementNS("http://www.w3.org/2000/svg","text");const h=i(r.state);return d.setAttribute("font-size",`${h}px`),p=h,d.setAttribute("fill","currentColor"),d.style.textTransform="uppercase",d.style.letterSpacing="2px",l=document.createElementNS("http://www.w3.org/2000/svg","textPath"),l.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",`#${w}`),l.setAttribute("href",`#${w}`),l.setAttribute("startOffset","0%"),z=document.createTextNode(""),l.appendChild(z),d.appendChild(l),s.appendChild(c),s.appendChild(d),v.appendChild(s),v},styles:{color:"color"},update:(r,v)=>{let h=x(r.state);const A=f(r.state),M=i(r.state),R=o(r.state),j=T(r.state);e.dom.setStyle(v,"opacity",String(j));const L=r.state.interaction;if(L.textRing&&typeof L.textRing=="string"?h=L.textRing:L.text&&typeof L.text=="string"&&(h=L.text),A!==a&&(c.setAttribute("d",k.getCirclePath(A)),a=A),y){const $=k.getCircumference(A);l.setAttribute("textLength",String($)),l.setAttribute("lengthAdjust","spacing"),h=k.formatLoopText(h,!0)}else l.removeAttribute("textLength"),l.removeAttribute("lengthAdjust");M!==p&&(d.setAttribute("font-size",`${M}px`),p=M),h!==S&&(z.textContent=h,S=h),b+=R;const{x:I,y:O}=r.state.smooth;e.dom.setTransform(v,I,O,b)}},t)};C.SmartIcon=F,C.SmartRing=H,C.Sparkles=U,C.TextRing=Y,Object.defineProperty(C,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(L,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("@supermousejs/utils")):typeof define=="function"&&define.amd?define(["exports","@supermousejs/utils"],e):(L=typeof globalThis<"u"?globalThis:L||self,e(L.SupermouseLabs={},L.SupermouseUtils))})(this,(function(L,e){"use strict";function U(t,r){const m=t.tagName.toLowerCase();if(m==="input"||m==="textarea"||t.isContentEditable){const i=t.type;if(["button","submit","checkbox","radio","range","color"].includes(i)){if(r.pointer)return"pointer"}else if(r.text)return"text"}else if((m==="a"||m==="button"||t.closest("a")||t.closest("button"))&&r.pointer)return"pointer";if(r.text&&["p","span","h1","h2","h3","h4","h5","h6","li","blockquote","code","pre"].includes(m))return"text";if(r.text||r.grab){const i=window.getComputedStyle(t).cursor;if(r.text&&i==="text")return"text";if(r.grab&&i==="grab")return"grab"}return null}const q=t=>{let r,m=t.defaultState??"default",i=null,c,x="idle",d=0,C=m,h=null,o=0,a=0,z=0;const f=e.normalizeAll(t,{size:24,color:"",followStrategy:"smooth",anchor:"center",rotateWithVelocity:!1}),p=t.useSemanticTags??!0,g=t.transitionDuration??200,n=t.switchDelay??80,u=t.offset?.[0]??0,y=t.offset?.[1]??0,T=t.defaultState??"default",b=g/2;function v(s){e.css(r,{transform:`scale(${s})`})}function S(s){m=s,r.innerHTML=t.icons[s]??""}let l;return e.definePlugin({name:t.name??"smart-icon",selector:"[data-supermouse-icon]",create:s=>{const A=e.createActor("div");return e.css(A,{zIndex:e.Layers.CURSOR}),p&&s.registerHoverTarget("p, span, h1, h2, h3, h4, h5, h6, li, blockquote, code, pre"),r=e.createActor("div"),e.css(r,{width:"100%",height:"100%",display:"flex",alignItems:"center",justifyContent:"center",transformOrigin:"center center",transform:"scale(1)",transition:`transform ${b}ms cubic-bezier(0.16, 1, 0.3, 1)`}),r.innerHTML=t.icons[m]??"",e.injectStyles("supermouse-smart-icon-styles",`
2
+ @keyframes sm {
3
+ 0% { transform: rotate(0deg); }
4
+ 100% { transform: rotate(360deg); }
5
+ }
6
+ .supermouse-spin {
7
+ animation: sm 1s linear infinite;
8
+ }
9
+ `),l=s.state.cursorMode,s.setCursor("custom"),A.appendChild(r),A},update:(s,A,w)=>{const $=performance.now(),j=w/1e3,W=t.icons,M=s.state.hoverTarget,D=!!M,I=D&&M!==i;let R=T;if(D){I&&(i=M,c=p?U(M,W):null);const E=s.state.interaction?.icon;E&&W[E]?R=E:c&&(R=c)}else i&&(i=null,c=null);R!==m&&x==="idle"?!!W[R]||R===T?I?(S(R),v(1),h=null,o=0):R!==h?(h=R,o=0):(o+=w,o>=n&&(C=R,x="out",d=$,v(0),h=null,o=0)):(h=null,o=0):R===m&&(h=null,o=0),x==="out"&&$-d>=b&&(S(C),x="in",d=$,v(0)),x==="in"&&$-d>=b&&(x="idle",v(1));const k=f.size(s.state),_=f.color(s.state);e.css(A,{width:`${k}px`,height:`${k}px`,color:_||"inherit"});let P=0,O=0;const F=k/2;switch(f.anchor(s.state)){case"center":P=-F,O=-F;break;case"top-left":P=0,O=0;break;case"top-right":P=-k,O=0;break;case"bottom-left":P=0,O=-k;break;case"bottom-right":P=-k,O=-k;break}const J=m==="pointer"||m==="text";if(f.rotateWithVelocity(s.state)&&!J&&!s.state.reducedMotion){const{x:E,y:H}=s.state.velocity;e.math.dist(E,H)>1&&(z=e.math.angle(E,H));const Q=1-Math.exp(-20*j);a=e.math.lerpAngle(a,z,Q)}else{const E=1-Math.exp(-20*j);a=e.math.lerpAngle(a,0,E)}const V=f.followStrategy(s.state)==="raw"?s.state.pointer:s.state.smooth,K=`translate3d(${V.x+u+P}px, ${V.y+y+O}px, 0) rotate(${a}deg)`;A.style.transform=K},cleanup(s){s.setCursor(l)}},t)},N=(t={})=>{const r=e.normalizeAll(t,{size:20,hoverSize:40,color:"#ffffff",fill:"transparent",borderWidth:2});let m=20,i=20,c=0,x=1,d=1;const C=150;let h=null;return e.definePlugin({name:t.name??"smart-ring",selector:"[data-supermouse-color]",create:o=>{const a=e.createCircle(r.size(o.state),r.fill(o.state));return e.css(a,{zIndex:e.Layers.FOLLOWER,mixBlendMode:t.mixBlendMode??"difference",transition:"border-radius 0.2s ease",borderStyle:"solid",opacity:"0"}),a},onEnable(o,a){const z=r.size(o.state);m=z*.5,i=z*.5,c=0,x=1,d=1,clearTimeout(h??void 0),a.style.display="block",a.style.opacity="1",a.style.transition="border-radius 0.2s ease"},beforeDisable(o,a){const{x:z,y:f}=o.state.smooth;return a.style.transition=`opacity ${C}ms ease, transform ${C}ms ease`,a.style.opacity="0",e.setTransform(a,z,f,0,.2,.2),new Promise(p=>{h=setTimeout(()=>{a.style.transition="border-radius 0.2s ease",p()},C)})},update:(o,a,z)=>{if(a.style.display==="none")return;const f=z/1e3,p=r.size(o.state),g=o.state.shape;let n=p,u=p,y="50%",T=r.color(o.state);g?(n=g.width,u=g.height,y=`${g.borderRadius}px`):(o.state.isHover&&(n=r.hoverSize(o.state),u=r.hoverSize(o.state)),o.state.isDown&&(n*=.9,u*=.9)),o.state.interaction.color&&(T=o.state.interaction.color),m=e.damp(m,n,20,f),i=e.damp(i,u,20,f),e.css(a,{width:`${m}px`,height:`${i}px`,borderRadius:y,borderColor:T,backgroundColor:r.fill(o.state),borderWidth:`${r.borderWidth(o.state)}px`,opacity:"1"});let b=0,v=1,S=1;if(!g&&t.enableSkew&&!o.state.reducedMotion){const{displacement:A}=o.state,w=e.effects.getVelocityDistortion(A.x,A.y);b=w.rotation,v=w.scaleX,S=w.scaleY;const $=1-Math.exp(-20*f);c=e.lerpAngle(c,b,$)}else c=0;x=e.damp(x,v,20,f),d=e.damp(d,S,20,f);const{x:l,y:s}=o.state.smooth;e.setTransform(a,l,s,c,x,d)}},t)},X=(t={})=>{const r=t.count??30,m=t.decay??2.5,i=t.frequency??10,c=t.scatter??5,x=e.normalize(t.color,"#ff00ff"),d=[];let C=0,h=0,o=!1,a=0;const z=(f,p,g)=>{const n=d.find(b=>!b.isActive);if(!n)return;n.isActive=!0,n.life=1,n.x=f+e.math.random(-c,c),n.y=p+e.math.random(-c,c);const u=e.math.random(0,Math.PI*2),y=e.math.random(.5,1.5);n.vx=Math.cos(u)*y,n.vy=Math.sin(u)*y,n.scale=e.math.random(.5,1.2),n.color=g;const T=e.math.random(2,5);e.css(n.el,{width:`${T}px`,height:`${T}px`,backgroundColor:g,opacity:"1"}),e.setTransform(n.el,n.x,n.y,0,n.scale,n.scale)};return e.definePlugin({name:t.name??"sparkles",create:()=>{const f=e.createActor("div");e.css(f,{zIndex:e.Layers.TRACE});for(let p=0;p<r;p++){const g=e.createCircle(0,"transparent");e.css(g,{opacity:"0",willChange:"transform, opacity"}),f.appendChild(g),d.push({el:g,isActive:!1,x:0,y:0,vx:0,vy:0,life:0,scale:1,color:""})}return f},update:(f,p,g)=>{const n=g/1e3,{x:u,y}=f.state.pointer;o||(C=u,h=y,o=!0);const T=u-C,b=y-h,v=Math.hypot(T,b);if(a+=v,a>i){const S=x(f.state),l=Math.floor(a/i);for(let s=0;s<l;s++){const A=s/l;z(C+T*A,h+b*A,S)}a=a%i}C=u,h=y;for(let S=0;S<r;S++){const l=d[S];if(l.isActive)if(l.x+=l.vx*n*60,l.y+=l.vy*n*60,l.life-=m*n,l.life<=0)l.isActive=!1,e.css(l.el,{opacity:"0"});else{const s=l.scale*l.life;e.css(l.el,{opacity:String(l.life)}),e.setTransform(l.el,l.x,l.y,0,s,s)}}},destroy:()=>{d.length=0}},t)};function Y(t,r){return r?t+" ":t}let B=0;const G=(t={})=>{let r,m,i,c,x,d;const C=`supermouse-text-ring-path-${B++}`,h=e.normalizeAll(t,{text:"SUPERMOUSE • SUPERMOUSE • ",radius:60,fontSize:12,speed:30,opacity:1,color:"currentColor"}),o=t.className??"",a=t.spread??!0;let z=0,f="",p=0,g=0;return e.definePlugin({name:t.name??"text-ring",selector:"[data-supermouse-text-ring]",create:n=>{const u=e.createActor("div");e.css(u,{zIndex:e.Layers.FOLLOWER,transition:"opacity 0.2s ease",opacity:"1",width:"0px",height:"0px",overflow:"visible",display:"flex",alignItems:"center",justifyContent:"center"}),o&&u.classList.add(...o.split(" ").filter(Boolean)),r=e.createSVGElement("svg"),e.css(r,{overflow:"visible",position:"absolute",left:"0",top:"0"}),m=e.path(void 0,{id:C,fill:"none"});const y=h.fontSize(n.state);return g=y,c=e.text({"font-size":`${y}px`,fill:"currentColor"}),e.css(c,{textTransform:"uppercase",letterSpacing:"2px"}),i=e.textPath(`#${C}`,{startOffset:"0%"}),x=document.createTextNode(""),i.appendChild(x),c.appendChild(i),r.appendChild(m),r.appendChild(c),d=e.createSVGElement("text"),e.css(d,{visibility:"hidden",position:"absolute",pointerEvents:"none",whiteSpace:"nowrap"}),d.setAttribute("font-size",`${y}px`),d.setAttribute("font-family",getComputedStyle(c).fontFamily||"sans-serif"),d.setAttribute("font-weight",getComputedStyle(c).fontWeight||"normal"),d.setAttribute("font-style",getComputedStyle(c).fontStyle||"normal"),r.appendChild(d),u.appendChild(r),u},update:(n,u,y)=>{const T=y/1e3;let b=h.text(n.state);const v=h.radius(n.state),S=h.fontSize(n.state),l=h.speed(n.state),s=h.opacity(n.state),A=h.color(n.state);e.css(u,{color:A,opacity:String(s)});const w=n.state.interaction;if(w.textRing&&typeof w.textRing=="string"?b=w.textRing:w.text&&typeof w.text=="string"&&(b=w.text),v!==p){m.setAttribute("d",e.circlePath(v));const M=v*2;e.css(r,{width:`${M}px`,height:`${M}px`}),p=v}const $=Y(b,a);if(S!==g&&(c.setAttribute("font-size",`${S}px`),d.setAttribute("font-size",`${S}px`),g=S),$!==f&&(x.textContent=$,f=$),a){const M=e.circumference(v);d.textContent=$;const D=d.getComputedTextLength(),I=$.length;if(I>1){const k=(M-D)/(I-1);e.css(c,{letterSpacing:`${k}px`})}else e.css(c,{letterSpacing:"0px"});c.removeAttribute("textLength"),i.removeAttribute("textLength"),i.removeAttribute("lengthAdjust")}else e.css(c,{letterSpacing:"2px"}),c.removeAttribute("textLength"),i.removeAttribute("textLength"),i.removeAttribute("lengthAdjust");z+=l*T;const{x:j,y:W}=n.state.smooth;e.setTransform(u,j,W,z)}},t)};L.SmartIcon=q,L.SmartRing=N,L.Sparkles=X,L.TextRing=G,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@supermousejs/labs",
3
- "version": "2.3.1",
3
+ "version": "2.4.1",
4
4
  "main": "dist/index.umd.js",
5
5
  "module": "dist/index.mjs",
6
+ "description": "Experimentental state machines and effects for supermouse",
6
7
  "types": "dist/index.d.ts",
7
8
  "files": [
8
9
  "dist",
@@ -12,19 +13,18 @@
12
13
  "url": "https://github.com/Whitestar14/supermouse-js",
13
14
  "license": "MIT",
14
15
  "dependencies": {
15
- "@supermousejs/zoetrope": "2.3.1",
16
- "@supermousejs/utils": "2.3.1"
16
+ "@supermousejs/utils": "2.4.1"
17
17
  },
18
18
  "peerDependencies": {
19
- "@supermousejs/core": "2.3.0"
19
+ "@supermousejs/core": "2.4.1"
20
20
  },
21
21
  "devDependencies": {
22
- "@supermousejs/core": "2.3.0"
22
+ "@supermousejs/core": "2.4.1"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
26
26
  "url": "git+https://github.com/Whitestar14/supermouse-js.git",
27
- "directory": "packages/core"
27
+ "directory": "packages/labs"
28
28
  },
29
29
  "bugs": {
30
30
  "url": "https://github.com/Whitestar14/supermouse-js/issues"