@liiift-studio/magnettype 1.0.0 → 1.0.2
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/dist/index.cjs +1 -1
- package/dist/index.d.ts +66 -22
- package/dist/index.js +161 -137
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const C=require("react"),z=require("react/jsx-runtime"),_={i:3,l:3,1:3,I:3,r:2,n:1,m:1,0:2,O:2,o:1,b:1,d:1,p:1,q:1,c:1,e:1},B={word:"mt-word",char:"mt-char",probe:"mt-probe"},L={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function k(e,t=[]){return e.nodeType===Node.TEXT_NODE?t.push(e):e.childNodes.forEach(n=>k(n,t)),t}function j(e,t,n){if(!e||e==="normal")return`"${t}" ${n}`;const c=new RegExp(`(["'])${t}\\1\\s+[\\d.eE+-]+`),o=`"${t}" ${n}`;return c.test(e)?e.replace(c,o):`${e}, ${o}`}function D(e,t){let n=e;for(const[c,o]of Object.entries(t))n=j(n,c,o);return n}function I(e,t,n){if(t.opacity!==void 0){const[c,o]=t.opacity;e.style.opacity=String(c+(o-c)*n)}t.italic===!0&&(e.style.fontStyle=n>.5?"italic":"")}function V(e,t){t.opacity!==void 0&&(e.style.opacity=String(t.opacity[0])),t.italic===!0&&(e.style.fontStyle="")}function P(e){const t=e.cloneNode(!0),n=t.querySelectorAll(`.${B.word}, .${B.char}`);return Array.from(n).reverse().forEach(o=>{const y=o.parentNode;if(y){for(;o.firstChild;)y.insertBefore(o.firstChild,o);y.removeChild(o)}}),t.innerHTML}function Q(e,t){e.innerHTML=t}function W(e,t,n={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=t,()=>{};const c=n.wdthBoost??L.wdthBoost,o=n.radius??L.radius,y=n.falloff??L.falloff,m=n.scope??L.scope,p=n.props,d=window.scrollY;e.innerHTML=t;const l=getComputedStyle(e).fontVariationSettings,b=l.match(/"wdth"\s+([\d.eE+-]+)/),h=b?parseFloat(b[1]):100,q=k(e),g=[];for(const r of q){const s=r.textContent??"";if(!s||!s.split("").some(i=>i in _))continue;const u=document.createDocumentFragment();for(const i of s){const N=_[i];if(N===void 0){const a=u.lastChild;a&&a.nodeType===Node.TEXT_NODE?a.textContent+=i:u.appendChild(document.createTextNode(i))}else{const a=document.createElement("span");a.className=B.char,a.style.fontVariationSettings=j(l,"wdth",h),a.textContent=i,u.appendChild(a),g.push({span:a,riskLevel:N})}}r.parentNode.replaceChild(u,r)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-d)>2&&window.scrollTo({top:d,behavior:"instant"})}),g.length===0)return()=>{};p&&g.forEach(({span:r})=>V(r,p));let w=-9999,v=-9999,M=!1,f=0,R=!0;function A(){if(!R)return;if(!M){g.forEach(({span:s})=>{s.style.fontVariationSettings=j(l,"wdth",h),p&&V(s,p)}),f=0;return}const r=g.map(({span:s})=>s.getBoundingClientRect());g.forEach(({span:s,riskLevel:T},u)=>{const i=r[u],N=i.left+i.width/2,a=i.top+i.height/2,F=Math.sqrt((w-N)**2+(v-a)**2),x=Math.max(0,1-F/o),S=y==="quadratic"?x*x:x,$=c*(T/3)*S;s.style.fontVariationSettings=j(l,"wdth",h+$),p&&I(s,p,S)}),f=requestAnimationFrame(A)}function O(r){w=r.clientX,v=r.clientY,M||(M=!0),f===0&&(f=requestAnimationFrame(A))}function Y(){M=!1,f===0&&(f=requestAnimationFrame(A))}const E=m==="document"?document:e;return E.addEventListener("mousemove",O),E.addEventListener("mouseleave",Y),()=>{R=!1,cancelAnimationFrame(f),E.removeEventListener("mousemove",O),E.removeEventListener("mouseleave",Y),e.innerHTML=t}}function G(e,t,n={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=t,()=>{};const c=n.axes??L.axes,o=n.radius??L.radius,y=n.falloff??L.falloff,m=n.magnetMode??L.magnetMode,p=n.scope??L.scope,d=n.props,l=window.scrollY;e.innerHTML=t;const b=k(e),h=[];for(const r of b){const s=r.textContent??"";if(!s.trim())continue;const T=s.split(/(\S+)/),u=document.createDocumentFragment();for(let i=0;i<T.length;i+=2){const N=T[i],a=T[i+1];if(!a)continue;const x=T[i+3]===void 0?T[i+2]??"":"",S=document.createElement("span");S.className=B.word,S.textContent=N+a+x,u.appendChild(S),h.push(S)}r.parentNode.replaceChild(u,r)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-l)>2&&window.scrollTo({top:l,behavior:"instant"})}),h.length===0)return()=>{};const q=getComputedStyle(e).fontVariationSettings,g=D(q,Object.fromEntries(Object.entries(c).map(([r,[s]])=>[r,s])));h.forEach(r=>{r.style.fontVariationSettings=g,d&&V(r,d)});let w=-9999,v=-9999,M=!1,f=0,R=!0;function A(){if(!R)return;if(!M){h.forEach(s=>{s.style.fontVariationSettings=g,d&&V(s,d)}),f=0;return}const r=h.map(s=>s.getBoundingClientRect());h.forEach((s,T)=>{const u=r[T],i=u.left+u.width/2,N=u.top+u.height/2,a=Math.sqrt((w-i)**2+(v-N)**2),F=Math.max(0,1-a/o),x=y==="quadratic"?F*F:F,S=m==="repel"?1-x:x,$={};for(const H of Object.keys(c)){const[X,U]=c[H]??[300,500];$[H]=X+(U-X)*S}s.style.fontVariationSettings=D(q,$),d&&I(s,d,x)}),f=requestAnimationFrame(A)}function O(r){w=r.clientX,v=r.clientY,M||(M=!0),f===0&&(f=requestAnimationFrame(A))}function Y(){M=!1,f===0&&(f=requestAnimationFrame(A))}const E=p==="document"?document:e;return E.addEventListener("mousemove",O),E.addEventListener("mouseleave",Y),()=>{R=!1,cancelAnimationFrame(f),E.removeEventListener("mousemove",O),E.removeEventListener("mouseleave",Y),e.innerHTML=t}}function J(e){const t=C.useRef(null),n=C.useRef(null),c=C.useRef(e);c.current=e;const o=C.useRef(null),y=e.mode??"field",{axes:m,radius:p,falloff:d,magnetMode:l,wdthBoost:b,scope:h}=e,q=m?JSON.stringify(m):void 0,g=e.props?JSON.stringify(e.props):void 0,w=C.useCallback(()=>{const v=t.current;if(!v)return;n.current===null&&(n.current=P(v)),o.current&&(o.current(),o.current=null),(c.current.mode??"field")==="field"?o.current=G(v,n.current,c.current):o.current=W(v,n.current,c.current)},[y,q,p,d,l,b,h,g]);return C.useLayoutEffect(()=>(w(),()=>{o.current&&(o.current(),o.current=null)}),[w]),C.useEffect(()=>{document.fonts.ready.then(w)},[w]),t}const K=C.forwardRef(function({children:t,as:n="p",className:c,style:o,...y},m){const p=J(y),d=C.useCallback(l=>{p.current=l,typeof m=="function"?m(l):m&&(m.current=l)},[m]);return z.jsx(n,{ref:d,className:c,style:o,children:t})});K.displayName="MagnetTypeText";exports.MAGNET_TYPE_CLASSES=B;exports.MagnetTypeText=K;exports.applyMagnetType=W;exports.getCleanHTML=P;exports.removeMagnetType=Q;exports.startMagnetType=G;exports.useMagnetType=J;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,16 +2,22 @@ import { default as default_2 } from 'react';
|
|
|
2
2
|
import { RefObject } from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Start the legibility effect on an element.
|
|
6
6
|
*
|
|
7
|
-
* Wraps
|
|
8
|
-
*
|
|
7
|
+
* Wraps visually confusable characters in spans, then listens for mousemove events
|
|
8
|
+
* to drive per-character wdth boost based on cursor distance. Characters near the
|
|
9
|
+
* cursor receive a wdth boost proportional to their confusion risk level.
|
|
10
|
+
* Resets to base wdth on mouseleave.
|
|
11
|
+
*
|
|
12
|
+
* Defaults to listening on document (scope: 'document'), enabling cross-paragraph
|
|
13
|
+
* effects when multiple elements are independently targeted.
|
|
9
14
|
*
|
|
10
15
|
* @param element - Target element (must be in the live DOM and visible)
|
|
11
16
|
* @param originalHTML - Clean HTML snapshot from getCleanHTML()
|
|
12
|
-
* @param options - MagnetTypeOptions;
|
|
17
|
+
* @param options - MagnetTypeOptions; wdthBoost, radius, falloff, scope, props used
|
|
18
|
+
* @returns - A stop function. Call it to cancel listeners and restore markup.
|
|
13
19
|
*/
|
|
14
|
-
export declare function applyMagnetType(element: HTMLElement, originalHTML: string, options?: MagnetTypeOptions): void;
|
|
20
|
+
export declare function applyMagnetType(element: HTMLElement, originalHTML: string, options?: MagnetTypeOptions): () => void;
|
|
15
21
|
|
|
16
22
|
/** Falloff curve for the cursor proximity field */
|
|
17
23
|
export declare type FalloffType = 'linear' | 'quadratic';
|
|
@@ -43,25 +49,26 @@ export declare type MagnetTypeModeType = 'field' | 'legibility';
|
|
|
43
49
|
*
|
|
44
50
|
* Two modes share this options type:
|
|
45
51
|
* - 'field' — cursor proximity drives per-word variable font axis values
|
|
46
|
-
* - 'legibility' — per-character wdth boost for
|
|
52
|
+
* - 'legibility' — cursor proximity drives per-character wdth boost for confusable characters
|
|
47
53
|
*/
|
|
48
54
|
export declare interface MagnetTypeOptions {
|
|
49
55
|
/**
|
|
50
56
|
* Operating mode. Default: 'field'
|
|
51
57
|
*
|
|
52
58
|
* - **'field'** — cursor proximity drives per-word font-variation-settings.
|
|
53
|
-
* - **'legibility'** —
|
|
59
|
+
* - **'legibility'** — cursor-driven wdth boost for confusable characters; both return a stop function.
|
|
54
60
|
*/
|
|
55
61
|
mode?: MagnetTypeModeType;
|
|
56
62
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
63
|
+
* Scope for cursor event listeners. Default: 'document'
|
|
64
|
+
*
|
|
65
|
+
* - **'document'** — listens on document; works across multiple elements and paragraphs.
|
|
66
|
+
* - **'element'** — listens only on the target element.
|
|
60
67
|
*/
|
|
61
|
-
|
|
68
|
+
scope?: ScopeType;
|
|
62
69
|
/**
|
|
63
70
|
* Pixel radius over which the field effect fades.
|
|
64
|
-
* Words beyond this distance receive restValue. Default: 120
|
|
71
|
+
* Words/chars beyond this distance receive restValue. Default: 120
|
|
65
72
|
*/
|
|
66
73
|
radius?: number;
|
|
67
74
|
/**
|
|
@@ -71,6 +78,17 @@ export declare interface MagnetTypeOptions {
|
|
|
71
78
|
* - **'quadratic'** — strength decreases as distance², giving a tighter hot zone
|
|
72
79
|
*/
|
|
73
80
|
falloff?: FalloffType;
|
|
81
|
+
/**
|
|
82
|
+
* Additional CSS property effects driven by cursor proximity.
|
|
83
|
+
* Supports opacity [rest, peak] and italic toggle.
|
|
84
|
+
*/
|
|
85
|
+
props?: MagnetTypeProps;
|
|
86
|
+
/**
|
|
87
|
+
* Map of axis tag → [restValue, peakValue].
|
|
88
|
+
* restValue is applied at full distance; peakValue when cursor is directly over the word.
|
|
89
|
+
* Default: { wght: [300, 500] }
|
|
90
|
+
*/
|
|
91
|
+
axes?: Record<string, [number, number]>;
|
|
74
92
|
/**
|
|
75
93
|
* Attraction or repulsion mode. Default: 'attract'
|
|
76
94
|
*
|
|
@@ -79,15 +97,32 @@ export declare interface MagnetTypeOptions {
|
|
|
79
97
|
*/
|
|
80
98
|
magnetMode?: MagnetModeType;
|
|
81
99
|
/**
|
|
82
|
-
* wdth axis units to add to confusable characters. Default: 6
|
|
100
|
+
* wdth axis units to add to confusable characters at full cursor strength. Default: 6
|
|
83
101
|
*
|
|
84
|
-
* Risk 1 characters receive wdthBoost × (1/3).
|
|
85
|
-
* Risk 2 characters receive wdthBoost × (2/3).
|
|
86
|
-
* Risk 3 characters receive wdthBoost × (3/3) = full boost.
|
|
102
|
+
* Risk 1 characters receive wdthBoost × (1/3) at peak.
|
|
103
|
+
* Risk 2 characters receive wdthBoost × (2/3) at peak.
|
|
104
|
+
* Risk 3 characters receive wdthBoost × (3/3) = full boost at peak.
|
|
87
105
|
*/
|
|
88
106
|
wdthBoost?: number;
|
|
89
107
|
}
|
|
90
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Additional CSS property effects driven by cursor proximity.
|
|
111
|
+
* Applied to word spans in field mode, or char spans in legibility mode.
|
|
112
|
+
*/
|
|
113
|
+
declare interface MagnetTypeProps {
|
|
114
|
+
/**
|
|
115
|
+
* Opacity range [restValue, peakValue].
|
|
116
|
+
* e.g. [1, 0.6] fades words/chars near the cursor.
|
|
117
|
+
*/
|
|
118
|
+
opacity?: [number, number];
|
|
119
|
+
/**
|
|
120
|
+
* When true, applies font-style:italic to spans where cursor strength > 0.5.
|
|
121
|
+
* For variable fonts with an 'ital' axis, use axes: { ital: [0, 1] } instead.
|
|
122
|
+
*/
|
|
123
|
+
italic?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
91
126
|
/**
|
|
92
127
|
* Drop-in component that applies the magnetType effect to its children.
|
|
93
128
|
* Forwards the ref to the root element while also attaching the internal hook ref.
|
|
@@ -107,10 +142,17 @@ declare interface MagnetTypeTextProps extends MagnetTypeOptions {
|
|
|
107
142
|
* Remove magnetType markup and restore the element to its original HTML.
|
|
108
143
|
*
|
|
109
144
|
* @param element - The element that was previously modified
|
|
110
|
-
* @param originalHTML - The snapshot passed to applyMagnetType
|
|
145
|
+
* @param originalHTML - The snapshot passed to applyMagnetType / startMagnetType
|
|
111
146
|
*/
|
|
112
147
|
export declare function removeMagnetType(element: HTMLElement, originalHTML: string): void;
|
|
113
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Scope for cursor event listeners.
|
|
151
|
+
* 'document' enables cross-element / cross-paragraph effects (default).
|
|
152
|
+
* 'element' restricts the field to the target element only.
|
|
153
|
+
*/
|
|
154
|
+
declare type ScopeType = 'element' | 'document';
|
|
155
|
+
|
|
114
156
|
/**
|
|
115
157
|
* Start the cursor-field effect on an element.
|
|
116
158
|
*
|
|
@@ -118,9 +160,12 @@ export declare function removeMagnetType(element: HTMLElement, originalHTML: str
|
|
|
118
160
|
* font-variation-settings based on cursor distance. Uses a requestAnimationFrame
|
|
119
161
|
* loop for smooth axis interpolation. Resets all words to restValue on mouseleave.
|
|
120
162
|
*
|
|
163
|
+
* Defaults to listening on document (scope: 'document'), enabling cross-paragraph
|
|
164
|
+
* effects when multiple elements are independently targeted.
|
|
165
|
+
*
|
|
121
166
|
* @param element - Target element (must be in the live DOM and visible)
|
|
122
167
|
* @param originalHTML - Clean HTML snapshot from getCleanHTML()
|
|
123
|
-
* @param options - MagnetTypeOptions; axes, radius, falloff, magnetMode
|
|
168
|
+
* @param options - MagnetTypeOptions; axes, radius, falloff, magnetMode, scope, props used
|
|
124
169
|
* @returns - A stop function. Call it to cancel the rAF loop and restore markup.
|
|
125
170
|
*/
|
|
126
171
|
export declare function startMagnetType(element: HTMLElement, originalHTML: string, options?: MagnetTypeOptions): () => void;
|
|
@@ -129,11 +174,10 @@ export declare function startMagnetType(element: HTMLElement, originalHTML: stri
|
|
|
129
174
|
* React hook that applies the magnetType effect to a ref'd element.
|
|
130
175
|
*
|
|
131
176
|
* For mode: 'field' — starts the cursor proximity rAF loop via startMagnetType.
|
|
132
|
-
*
|
|
133
|
-
* No ResizeObserver needed — the rAF loop reads live getBoundingClientRect each frame.
|
|
177
|
+
* For mode: 'legibility' — starts the cursor-driven wdth boost via applyMagnetType.
|
|
134
178
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
179
|
+
* Both modes return a stop function on mount and restart when options change.
|
|
180
|
+
* No ResizeObserver needed — the rAF loop reads live getBoundingClientRect each frame.
|
|
137
181
|
*
|
|
138
182
|
* Defaults to 'field' mode if mode is undefined.
|
|
139
183
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useRef as
|
|
2
|
-
import { jsx as
|
|
3
|
-
const
|
|
1
|
+
import { useRef as B, useCallback as _, useLayoutEffect as J, useEffect as K, forwardRef as U } from "react";
|
|
2
|
+
import { jsx as G } from "react/jsx-runtime";
|
|
3
|
+
const D = {
|
|
4
4
|
i: 3,
|
|
5
5
|
l: 3,
|
|
6
6
|
1: 3,
|
|
@@ -22,7 +22,7 @@ const B = {
|
|
|
22
22
|
c: 1,
|
|
23
23
|
e: 1
|
|
24
24
|
// similar bowls
|
|
25
|
-
},
|
|
25
|
+
}, V = {
|
|
26
26
|
/** Applied to each word span in field mode */
|
|
27
27
|
word: "mt-word",
|
|
28
28
|
/** Applied to each character span in legibility mode */
|
|
@@ -34,175 +34,199 @@ const B = {
|
|
|
34
34
|
radius: 120,
|
|
35
35
|
falloff: "quadratic",
|
|
36
36
|
magnetMode: "attract",
|
|
37
|
-
wdthBoost: 6
|
|
37
|
+
wdthBoost: 6,
|
|
38
|
+
scope: "document"
|
|
38
39
|
};
|
|
39
|
-
function
|
|
40
|
-
return
|
|
40
|
+
function k(t, e = []) {
|
|
41
|
+
return t.nodeType === Node.TEXT_NODE ? e.push(t) : t.childNodes.forEach((n) => k(n, e)), e;
|
|
41
42
|
}
|
|
42
|
-
function
|
|
43
|
-
if (!
|
|
44
|
-
const
|
|
45
|
-
return
|
|
43
|
+
function $(t, e, n) {
|
|
44
|
+
if (!t || t === "normal") return `"${e}" ${n}`;
|
|
45
|
+
const c = new RegExp(`(["'])${e}\\1\\s+[\\d.eE+-]+`), o = `"${e}" ${n}`;
|
|
46
|
+
return c.test(t) ? t.replace(c, o) : `${t}, ${o}`;
|
|
46
47
|
}
|
|
47
|
-
function
|
|
48
|
-
let
|
|
49
|
-
for (const [
|
|
50
|
-
|
|
51
|
-
return
|
|
48
|
+
function I(t, e) {
|
|
49
|
+
let n = t;
|
|
50
|
+
for (const [c, o] of Object.entries(e))
|
|
51
|
+
n = $(n, c, o);
|
|
52
|
+
return n;
|
|
52
53
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
function P(t, e, n) {
|
|
55
|
+
if (e.opacity !== void 0) {
|
|
56
|
+
const [c, o] = e.opacity;
|
|
57
|
+
t.style.opacity = String(c + (o - c) * n);
|
|
58
|
+
}
|
|
59
|
+
e.italic === !0 && (t.style.fontStyle = n > 0.5 ? "italic" : "");
|
|
60
|
+
}
|
|
61
|
+
function j(t, e) {
|
|
62
|
+
e.opacity !== void 0 && (t.style.opacity = String(e.opacity[0])), e.italic === !0 && (t.style.fontStyle = "");
|
|
63
|
+
}
|
|
64
|
+
function z(t) {
|
|
65
|
+
const e = t.cloneNode(!0), n = e.querySelectorAll(
|
|
66
|
+
`.${V.word}, .${V.char}`
|
|
56
67
|
);
|
|
57
|
-
return Array.from(
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
for (;
|
|
61
|
-
|
|
68
|
+
return Array.from(n).reverse().forEach((o) => {
|
|
69
|
+
const y = o.parentNode;
|
|
70
|
+
if (y) {
|
|
71
|
+
for (; o.firstChild; ) y.insertBefore(o.firstChild, o);
|
|
72
|
+
y.removeChild(o);
|
|
62
73
|
}
|
|
63
|
-
}),
|
|
74
|
+
}), e.innerHTML;
|
|
64
75
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
76
|
+
function rt(t, e) {
|
|
77
|
+
t.innerHTML = e;
|
|
67
78
|
}
|
|
68
|
-
function
|
|
69
|
-
if (typeof window > "u") return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
function Q(t, e, n = {}) {
|
|
80
|
+
if (typeof window > "u") return () => {
|
|
81
|
+
};
|
|
82
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
|
|
83
|
+
return t.innerHTML = e, () => {
|
|
84
|
+
};
|
|
85
|
+
const c = n.wdthBoost ?? C.wdthBoost, o = n.radius ?? C.radius, y = n.falloff ?? C.falloff, m = n.scope ?? C.scope, p = n.props, d = window.scrollY;
|
|
86
|
+
t.innerHTML = e;
|
|
87
|
+
const l = getComputedStyle(t).fontVariationSettings, A = l.match(/"wdth"\s+([\d.eE+-]+)/), h = A ? parseFloat(A[1]) : 100, q = k(t), w = [];
|
|
88
|
+
for (const r of q) {
|
|
89
|
+
const s = r.textContent ?? "";
|
|
90
|
+
if (!s || !s.split("").some((i) => i in D)) continue;
|
|
91
|
+
const u = document.createDocumentFragment();
|
|
92
|
+
for (const i of s) {
|
|
93
|
+
const L = D[i];
|
|
94
|
+
if (L === void 0) {
|
|
95
|
+
const a = u.lastChild;
|
|
96
|
+
a && a.nodeType === Node.TEXT_NODE ? a.textContent += i : u.appendChild(document.createTextNode(i));
|
|
82
97
|
} else {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const u = a.match(/"wdth"\s+([\d.eE+-]+)/), v = (u ? parseFloat(u[1]) : 100) + s;
|
|
86
|
-
c.style.fontVariationSettings = H(a, "wdth", v), c.textContent = m, M.appendChild(c);
|
|
98
|
+
const a = document.createElement("span");
|
|
99
|
+
a.className = V.char, a.style.fontVariationSettings = $(l, "wdth", h), a.textContent = i, u.appendChild(a), w.push({ span: a, riskLevel: L });
|
|
87
100
|
}
|
|
88
101
|
}
|
|
89
|
-
|
|
102
|
+
r.parentNode.replaceChild(u, r);
|
|
90
103
|
}
|
|
91
|
-
requestAnimationFrame(() => {
|
|
92
|
-
typeof window < "u" && Math.abs(window.scrollY -
|
|
93
|
-
})
|
|
104
|
+
if (requestAnimationFrame(() => {
|
|
105
|
+
typeof window < "u" && Math.abs(window.scrollY - d) > 2 && window.scrollTo({ top: d, behavior: "instant" });
|
|
106
|
+
}), w.length === 0) return () => {
|
|
107
|
+
};
|
|
108
|
+
p && w.forEach(({ span: r }) => j(r, p));
|
|
109
|
+
let g = -9999, v = -9999, M = !1, f = 0, F = !0;
|
|
110
|
+
function N() {
|
|
111
|
+
if (!F) return;
|
|
112
|
+
if (!M) {
|
|
113
|
+
w.forEach(({ span: s }) => {
|
|
114
|
+
s.style.fontVariationSettings = $(l, "wdth", h), p && j(s, p);
|
|
115
|
+
}), f = 0;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const r = w.map(({ span: s }) => s.getBoundingClientRect());
|
|
119
|
+
w.forEach(({ span: s, riskLevel: E }, u) => {
|
|
120
|
+
const i = r[u], L = i.left + i.width / 2, a = i.top + i.height / 2, b = Math.sqrt((g - L) ** 2 + (v - a) ** 2), T = Math.max(0, 1 - b / o), S = y === "quadratic" ? T * T : T, R = c * (E / 3) * S;
|
|
121
|
+
s.style.fontVariationSettings = $(l, "wdth", h + R), p && P(s, p, S);
|
|
122
|
+
}), f = requestAnimationFrame(N);
|
|
123
|
+
}
|
|
124
|
+
function O(r) {
|
|
125
|
+
g = r.clientX, v = r.clientY, M || (M = !0), f === 0 && (f = requestAnimationFrame(N));
|
|
126
|
+
}
|
|
127
|
+
function Y() {
|
|
128
|
+
M = !1, f === 0 && (f = requestAnimationFrame(N));
|
|
129
|
+
}
|
|
130
|
+
const x = m === "document" ? document : t;
|
|
131
|
+
return x.addEventListener("mousemove", O), x.addEventListener("mouseleave", Y), () => {
|
|
132
|
+
F = !1, cancelAnimationFrame(f), x.removeEventListener("mousemove", O), x.removeEventListener("mouseleave", Y), t.innerHTML = e;
|
|
133
|
+
};
|
|
94
134
|
}
|
|
95
|
-
function
|
|
135
|
+
function Z(t, e, n = {}) {
|
|
96
136
|
if (typeof window > "u") return () => {
|
|
97
137
|
};
|
|
98
138
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
|
|
99
|
-
return
|
|
139
|
+
return t.innerHTML = e, () => {
|
|
100
140
|
};
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
for (const
|
|
105
|
-
const
|
|
106
|
-
if (!
|
|
107
|
-
const
|
|
108
|
-
for (let
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
const
|
|
112
|
-
|
|
141
|
+
const c = n.axes ?? C.axes, o = n.radius ?? C.radius, y = n.falloff ?? C.falloff, m = n.magnetMode ?? C.magnetMode, p = n.scope ?? C.scope, d = n.props, l = window.scrollY;
|
|
142
|
+
t.innerHTML = e;
|
|
143
|
+
const A = k(t), h = [];
|
|
144
|
+
for (const r of A) {
|
|
145
|
+
const s = r.textContent ?? "";
|
|
146
|
+
if (!s.trim()) continue;
|
|
147
|
+
const E = s.split(/(\S+)/), u = document.createDocumentFragment();
|
|
148
|
+
for (let i = 0; i < E.length; i += 2) {
|
|
149
|
+
const L = E[i], a = E[i + 1];
|
|
150
|
+
if (!a) continue;
|
|
151
|
+
const T = E[i + 3] === void 0 ? E[i + 2] ?? "" : "", S = document.createElement("span");
|
|
152
|
+
S.className = V.word, S.textContent = L + a + T, u.appendChild(S), h.push(S);
|
|
113
153
|
}
|
|
114
|
-
|
|
154
|
+
r.parentNode.replaceChild(u, r);
|
|
115
155
|
}
|
|
116
156
|
if (requestAnimationFrame(() => {
|
|
117
|
-
typeof window < "u" && Math.abs(window.scrollY -
|
|
118
|
-
}),
|
|
157
|
+
typeof window < "u" && Math.abs(window.scrollY - l) > 2 && window.scrollTo({ top: l, behavior: "instant" });
|
|
158
|
+
}), h.length === 0) return () => {
|
|
119
159
|
};
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
Object.fromEntries(Object.entries(
|
|
160
|
+
const q = getComputedStyle(t).fontVariationSettings, w = I(
|
|
161
|
+
q,
|
|
162
|
+
Object.fromEntries(Object.entries(c).map(([r, [s]]) => [r, s]))
|
|
123
163
|
);
|
|
124
|
-
|
|
125
|
-
|
|
164
|
+
h.forEach((r) => {
|
|
165
|
+
r.style.fontVariationSettings = w, d && j(r, d);
|
|
126
166
|
});
|
|
127
|
-
let
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (!c) {
|
|
135
|
-
f.forEach((d) => {
|
|
136
|
-
d.style.fontVariationSettings = m;
|
|
137
|
-
}), u = 0;
|
|
167
|
+
let g = -9999, v = -9999, M = !1, f = 0, F = !0;
|
|
168
|
+
function N() {
|
|
169
|
+
if (!F) return;
|
|
170
|
+
if (!M) {
|
|
171
|
+
h.forEach((s) => {
|
|
172
|
+
s.style.fontVariationSettings = w, d && j(s, d);
|
|
173
|
+
}), f = 0;
|
|
138
174
|
return;
|
|
139
175
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
for (const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
176
|
+
const r = h.map((s) => s.getBoundingClientRect());
|
|
177
|
+
h.forEach((s, E) => {
|
|
178
|
+
const u = r[E], i = u.left + u.width / 2, L = u.top + u.height / 2, a = Math.sqrt((g - i) ** 2 + (v - L) ** 2), b = Math.max(0, 1 - a / o), T = y === "quadratic" ? b * b : b, S = m === "repel" ? 1 - T : T, R = {};
|
|
179
|
+
for (const H of Object.keys(c)) {
|
|
180
|
+
const [X, W] = c[H] ?? [300, 500];
|
|
181
|
+
R[H] = X + (W - X) * S;
|
|
182
|
+
}
|
|
183
|
+
s.style.fontVariationSettings = I(q, R), d && P(s, d, T);
|
|
184
|
+
}), f = requestAnimationFrame(N);
|
|
147
185
|
}
|
|
148
|
-
function O(
|
|
149
|
-
|
|
186
|
+
function O(r) {
|
|
187
|
+
g = r.clientX, v = r.clientY, M || (M = !0), f === 0 && (f = requestAnimationFrame(N));
|
|
150
188
|
}
|
|
151
189
|
function Y() {
|
|
152
|
-
|
|
190
|
+
M = !1, f === 0 && (f = requestAnimationFrame(N));
|
|
153
191
|
}
|
|
154
|
-
|
|
155
|
-
|
|
192
|
+
const x = p === "document" ? document : t;
|
|
193
|
+
return x.addEventListener("mousemove", O), x.addEventListener("mouseleave", Y), () => {
|
|
194
|
+
F = !1, cancelAnimationFrame(f), x.removeEventListener("mousemove", O), x.removeEventListener("mouseleave", Y), t.innerHTML = e;
|
|
156
195
|
};
|
|
157
196
|
}
|
|
158
|
-
function
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
if (!
|
|
164
|
-
|
|
165
|
-
}, [
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if ((r.current.mode ?? "field") === "field")
|
|
172
|
-
return () => {
|
|
173
|
-
t.current && (t.current(), t.current = null);
|
|
174
|
-
};
|
|
175
|
-
let s = 0, c = 0;
|
|
176
|
-
const u = new ResizeObserver((E) => {
|
|
177
|
-
const v = Math.round(E[0].contentRect.width);
|
|
178
|
-
v !== s && (s = v, cancelAnimationFrame(c), c = requestAnimationFrame(p));
|
|
179
|
-
});
|
|
180
|
-
return u.observe(n.current), () => {
|
|
181
|
-
u.disconnect(), cancelAnimationFrame(c), t.current && (t.current(), t.current = null);
|
|
182
|
-
};
|
|
183
|
-
}, [p]), k(() => {
|
|
184
|
-
document.fonts.ready.then(p);
|
|
185
|
-
}, [p]), n;
|
|
197
|
+
function tt(t) {
|
|
198
|
+
const e = B(null), n = B(null), c = B(t);
|
|
199
|
+
c.current = t;
|
|
200
|
+
const o = B(null), y = t.mode ?? "field", { axes: m, radius: p, falloff: d, magnetMode: l, wdthBoost: A, scope: h } = t, q = m ? JSON.stringify(m) : void 0, w = t.props ? JSON.stringify(t.props) : void 0, g = _(() => {
|
|
201
|
+
const v = e.current;
|
|
202
|
+
if (!v) return;
|
|
203
|
+
n.current === null && (n.current = z(v)), o.current && (o.current(), o.current = null), (c.current.mode ?? "field") === "field" ? o.current = Z(v, n.current, c.current) : o.current = Q(v, n.current, c.current);
|
|
204
|
+
}, [y, q, p, d, l, A, h, w]);
|
|
205
|
+
return J(() => (g(), () => {
|
|
206
|
+
o.current && (o.current(), o.current = null);
|
|
207
|
+
}), [g]), K(() => {
|
|
208
|
+
document.fonts.ready.then(g);
|
|
209
|
+
}, [g]), e;
|
|
186
210
|
}
|
|
187
|
-
const
|
|
188
|
-
function({ children:
|
|
189
|
-
const
|
|
190
|
-
(
|
|
191
|
-
|
|
211
|
+
const et = U(
|
|
212
|
+
function({ children: e, as: n = "p", className: c, style: o, ...y }, m) {
|
|
213
|
+
const p = tt(y), d = _(
|
|
214
|
+
(l) => {
|
|
215
|
+
p.current = l, typeof m == "function" ? m(l) : m && (m.current = l);
|
|
192
216
|
},
|
|
193
217
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
194
|
-
[
|
|
218
|
+
[m]
|
|
195
219
|
);
|
|
196
|
-
return /* @__PURE__ */
|
|
220
|
+
return /* @__PURE__ */ G(n, { ref: d, className: c, style: o, children: e });
|
|
197
221
|
}
|
|
198
222
|
);
|
|
199
|
-
|
|
223
|
+
et.displayName = "MagnetTypeText";
|
|
200
224
|
export {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
225
|
+
V as MAGNET_TYPE_CLASSES,
|
|
226
|
+
et as MagnetTypeText,
|
|
227
|
+
Q as applyMagnetType,
|
|
228
|
+
z as getCleanHTML,
|
|
229
|
+
rt as removeMagnetType,
|
|
230
|
+
Z as startMagnetType,
|
|
231
|
+
tt as useMagnetType
|
|
208
232
|
};
|