@liiift-studio/magnettype 1.1.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -18
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +13 -7
- package/dist/index.js +58 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Per-word cursor-proximity weight variation driven by a continuous rAF loop.
|
|
|
30
30
|
import { MagnetTypeText } from '@liiift-studio/magnettype'
|
|
31
31
|
|
|
32
32
|
<MagnetTypeText
|
|
33
|
-
mode="
|
|
33
|
+
mode="word"
|
|
34
34
|
axes={{ wght: [300, 700] }}
|
|
35
35
|
radius={150}
|
|
36
36
|
falloff="quadratic"
|
|
@@ -40,43 +40,43 @@ import { MagnetTypeText } from '@liiift-studio/magnettype'
|
|
|
40
40
|
</MagnetTypeText>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
### React — block mode (`
|
|
43
|
+
### React — block mode (`MagnetChar`)
|
|
44
44
|
|
|
45
45
|
Per-character cursor-proximity weight variation. Works with mixed content (inline elements, links, `<code>`, etc.) inside any block element. Characters are wrapped as React elements — no DOM mutation.
|
|
46
46
|
|
|
47
47
|
```tsx
|
|
48
|
-
import {
|
|
48
|
+
import { MagnetChar } from '@liiift-studio/magnettype'
|
|
49
49
|
|
|
50
50
|
// Per-character spread — each character responds to cursor distance
|
|
51
|
-
<
|
|
51
|
+
<MagnetChar
|
|
52
52
|
spreadRadius={200}
|
|
53
53
|
minWeight={300}
|
|
54
54
|
maxWeight={700}
|
|
55
55
|
>
|
|
56
56
|
Typography that responds to presence.
|
|
57
|
-
</
|
|
57
|
+
</MagnetChar>
|
|
58
58
|
|
|
59
59
|
// Whole-element gate — the effect only activates when the cursor is within proximityRadius of the element edge
|
|
60
|
-
<
|
|
60
|
+
<MagnetChar
|
|
61
61
|
proximityRadius={120}
|
|
62
62
|
minWeight={300}
|
|
63
63
|
maxWeight={700}
|
|
64
64
|
>
|
|
65
65
|
Weight rises when the cursor enters.
|
|
66
|
-
</
|
|
66
|
+
</MagnetChar>
|
|
67
67
|
|
|
68
68
|
// Both combined — proximity gates the spread effect
|
|
69
|
-
<
|
|
69
|
+
<MagnetChar
|
|
70
70
|
proximityRadius={200}
|
|
71
71
|
spreadRadius={120}
|
|
72
72
|
minWeight={300}
|
|
73
73
|
maxWeight={700}
|
|
74
74
|
>
|
|
75
75
|
Only spreads when the cursor is close.
|
|
76
|
-
</
|
|
76
|
+
</MagnetChar>
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
**`
|
|
79
|
+
**`MagnetChar` props:**
|
|
80
80
|
|
|
81
81
|
| Prop | Default | Description |
|
|
82
82
|
|------|---------|-------------|
|
|
@@ -97,7 +97,7 @@ import { MagnetBlock } from '@liiift-studio/magnettype'
|
|
|
97
97
|
```tsx
|
|
98
98
|
import { useMagnetType } from '@liiift-studio/magnettype'
|
|
99
99
|
|
|
100
|
-
const ref = useMagnetType({ mode: '
|
|
100
|
+
const ref = useMagnetType({ mode: 'word', axes: { wght: [300, 700] }, radius: 150 })
|
|
101
101
|
return <p ref={ref}>{children}</p>
|
|
102
102
|
```
|
|
103
103
|
|
|
@@ -120,7 +120,7 @@ import { startMagnetType, removeMagnetType, getCleanHTML } from '@liiift-studio/
|
|
|
120
120
|
|
|
121
121
|
const el = document.querySelector('p')
|
|
122
122
|
const original = getCleanHTML(el)
|
|
123
|
-
const opts = { mode: '
|
|
123
|
+
const opts = { mode: 'word', axes: { wght: [300, 700] }, radius: 150 }
|
|
124
124
|
|
|
125
125
|
let stop
|
|
126
126
|
|
|
@@ -163,10 +163,10 @@ ro.observe(el)
|
|
|
163
163
|
### TypeScript
|
|
164
164
|
|
|
165
165
|
```ts
|
|
166
|
-
import type { MagnetTypeOptions, FalloffType, MagnetModeType,
|
|
166
|
+
import type { MagnetTypeOptions, FalloffType, MagnetModeType, MagnetCharProps } from '@liiift-studio/magnettype'
|
|
167
167
|
|
|
168
168
|
const fieldOpts: MagnetTypeOptions = {
|
|
169
|
-
mode: '
|
|
169
|
+
mode: 'word',
|
|
170
170
|
axes: { wght: [300, 700], wdth: [90, 110] },
|
|
171
171
|
radius: 120,
|
|
172
172
|
falloff: 'quadratic' as FalloffType,
|
|
@@ -185,7 +185,7 @@ const legibilityOpts: MagnetTypeOptions = {
|
|
|
185
185
|
|
|
186
186
|
| Option | Default | Description |
|
|
187
187
|
|--------|---------|-------------|
|
|
188
|
-
| `mode` | `'
|
|
188
|
+
| `mode` | `'word'` | `'word'` — cursor proximity drives per-word `font-variation-settings` via a continuous rAF loop. `'legibility'` — static per-character `wdth` boost on visually confusable characters |
|
|
189
189
|
| `axes` | `{ wght: [300, 500] }` | *(field mode)* Map of axis tag → `[restValue, peakValue]` |
|
|
190
190
|
| `radius` | `120` | *(field mode)* Pixel radius over which the field effect fades from each word's centre |
|
|
191
191
|
| `falloff` | `'quadratic'` | *(field mode)* `'linear'` or `'quadratic'` falloff curve |
|
|
@@ -209,9 +209,9 @@ strength = normalised² (quadratic) or normalised (linear)
|
|
|
209
209
|
|
|
210
210
|
Each word's `font-variation-settings` interpolates between `restValue` and `peakValue` at that strength. Reads are batched before writes on every frame to avoid layout thrashing. When the cursor leaves, one final frame resets all words to `restValue`.
|
|
211
211
|
|
|
212
|
-
### Block mode (`
|
|
212
|
+
### Block mode (`MagnetChar`)
|
|
213
213
|
|
|
214
|
-
`
|
|
214
|
+
`MagnetChar` splits string children into per-character `<span>` elements during the React render pass using `useMemo` — no DOM mutation. Callback refs collect each span element. On `mousemove` (and on `scroll`, using the stored last position), the component reads each span's `getBoundingClientRect`, computes cursor-to-character-centre distance, and sets `font-variation-settings` directly on the span's style. This is passive and batched per frame via the event handler.
|
|
215
215
|
|
|
216
216
|
`proximityRadius` measures cursor distance to the element **edge** (not its centre) — useful as an outer gate so the effect only fires when the cursor is actually near the block. `spreadRadius` measures cursor distance to each **character centre** — controls how wide the weight gradient spreads around the cursor within the block. Both are independent and combinable.
|
|
217
217
|
|
|
@@ -247,4 +247,4 @@ The package itself has zero runtime dependencies. Do not remove this entry.
|
|
|
247
247
|
|
|
248
248
|
---
|
|
249
249
|
|
|
250
|
-
Current version: 1.
|
|
250
|
+
Current version: 1.2.0
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react"),W=require("react/jsx-runtime"),rt={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},z={word:"mt-word",char:"mt-char",probe:"mt-probe"},K={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function ot(t,n=[]){return t.nodeType===Node.TEXT_NODE?n.push(t):t.childNodes.forEach(o=>ot(o,n)),n}function tt(t,n,o){if(!t||t==="normal")return`"${n}" ${o}`;const E=new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`),y=`"${n}" ${o}`;return E.test(t)?t.replace(E,y):`${t}, ${y}`}function nt(t,n){let o=t;for(const[E,y]of Object.entries(n))o=tt(o,E,y);return o}function st(t,n,o){if(n.opacity!==void 0){const[E,y]=n.opacity;t.style.opacity=String(E+(y-E)*o)}n.italic===!0&&(t.style.fontStyle=o>.5?"italic":"")}function et(t,n){n.opacity!==void 0&&(t.style.opacity=String(n.opacity[0])),n.italic===!0&&(t.style.fontStyle="")}function ct(t){const n=t.cloneNode(!0),o=n.querySelectorAll(`.${z.word}, .${z.char}`);return Array.from(o).reverse().forEach(y=>{const d=y.parentNode;if(d){for(;y.firstChild;)d.insertBefore(y.firstChild,y);d.removeChild(y)}}),n.innerHTML}function mt(t,n){t.innerHTML=n}function it(t,n,o={}){var p,r;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.wdthBoost??K.wdthBoost,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.scope??K.scope,F=o.props,m=o.transitionMs??0,S=o.cachePositions??!0,V=window.scrollY;t.innerHTML=n;const j=getComputedStyle(t).fontVariationSettings,N=j.match(/"wdth"\s+([\d.eE+-]+)/),k=N?parseFloat(N[1]):100,T=ot(t),g=[];for(const a of T){const C=a.textContent??"";if(!C||!C.split("").some(v=>v in rt))continue;const e=document.createDocumentFragment();for(const v of C){const i=rt[v];if(i===void 0){const u=e.lastChild;u&&u.nodeType===Node.TEXT_NODE?u.textContent+=v:e.appendChild(document.createTextNode(v))}else{const u=document.createElement("span");u.className=z.char,u.style.fontVariationSettings=tt(j,"wdth",k),u.textContent=v,e.appendChild(u),g.push({span:u,riskLevel:i})}}a.parentNode.replaceChild(e,a)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-V)>2&&window.scrollTo({top:V,behavior:"instant"})}),g.length===0)return()=>{};F&&g.forEach(({span:a})=>et(a,F));let A=[],H=!1;function _(){const a=window.scrollX,C=window.scrollY;A=g.map(({span:c})=>{const e=c.getBoundingClientRect();return{cx:(e.left+e.right)/2+a,cy:(e.top+e.bottom)/2+C}}),H=!0}let R=null;S&&(_(),R=new ResizeObserver(()=>{H=!1}),R.observe(t),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{H=!1}));let q=-9999,B=-9999,$=!1,w=0,P=!0,b=null;function D(){if(!P)return;if(!$){g.forEach(({span:e})=>{m>0&&(e.style.transition=`font-variation-settings ${m}ms ease`),e.style.fontVariationSettings=tt(j,"wdth",k),F&&et(e,F)}),m>0&&(b!==null&&clearTimeout(b),b=setTimeout(()=>{g.forEach(({span:e})=>{e.style.transition=""}),b=null},m)),w=0;return}S&&!H&&_();const a=S?q+window.scrollX:q,C=S?B+window.scrollY:B,c=S?null:g.map(({span:e})=>e.getBoundingClientRect());g.forEach(({span:e,riskLevel:v},i)=>{e.style.transition="";let u,O;if(S)({cx:u,cy:O}=A[i]);else{const I=c[i];u=I.left+I.width/2,O=I.top+I.height/2}const M=Math.sqrt((a-u)**2+(C-O)**2),f=Math.max(0,1-M/y),x=d==="quadratic"?f*f:f,Y=E*(v/3)*x;e.style.fontVariationSettings=tt(j,"wdth",k+Y),F&&st(e,F,x)}),w=requestAnimationFrame(D)}function X(a){q=a.clientX,B=a.clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D))}function J(){$=!1,w===0&&(w=requestAnimationFrame(D))}function Q(a){a.touches.length!==0&&(q=a.touches[0].clientX,B=a.touches[0].clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D)))}function l(){$=!1,w===0&&(w=requestAnimationFrame(D))}const s=L==="document"?document:t;return s.addEventListener("mousemove",X),s.addEventListener("mouseleave",J),s.addEventListener("touchmove",Q,{passive:!0}),s.addEventListener("touchend",l),()=>{P=!1,cancelAnimationFrame(w),b!==null&&clearTimeout(b),R&&R.disconnect(),s.removeEventListener("mousemove",X),s.removeEventListener("mouseleave",J),s.removeEventListener("touchmove",Q),s.removeEventListener("touchend",l),t.innerHTML=n}}function lt(t,n,o={}){var a,C;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.axes??K.axes,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.magnetMode??K.magnetMode,F=o.scope??K.scope,m=o.props,S=o.transitionMs??0,V=o.cachePositions??!0,j=o.stabilizeLayout??!0,N=window.scrollY;t.innerHTML=n;const k=ot(t),T=[];for(const c of k){const e=c.textContent??"";if(!e.trim())continue;const v=e.split(/(\S+)/),i=document.createDocumentFragment();for(let u=0;u<v.length;u+=2){const O=v[u],M=v[u+1];if(!M)continue;const x=v[u+3]===void 0?v[u+2]??"":"",Y=document.createElement("span");Y.className=z.word,Y.textContent=O+M+x,i.appendChild(Y),T.push(Y)}c.parentNode.replaceChild(i,c)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-N)>2&&window.scrollTo({top:N,behavior:"instant"})}),T.length===0)return()=>{};const g=getComputedStyle(t).fontVariationSettings,A=nt(g,Object.fromEntries(Object.entries(E).map(([c,[e]])=>[c,e])));let H=0;if(j){const c=nt(g,Object.fromEntries(Object.entries(E).map(([f,[,x]])=>[f,x]))),e=t.style.fontVariationSettings,v=t.style.whiteSpace,i=t.style.overflow;t.style.whiteSpace="nowrap",t.style.overflow="visible",t.style.fontVariationSettings=c;const u=t.scrollWidth;t.style.fontVariationSettings=A;const O=t.scrollWidth;t.style.fontVariationSettings=e,t.style.whiteSpace=v,t.style.overflow=i;const M=T.reduce((f,x)=>{var Y;return f+(((Y=x.textContent)==null?void 0:Y.replace(/\s+/g,"").length)??0)},0);M>0&&u>O&&(H=(u-O)/M)}T.forEach(c=>{c.style.fontVariationSettings=A,m&&et(c,m)});let _=[],R=!1;function q(){const c=window.scrollX,e=window.scrollY;_=T.map(v=>{const i=v.getBoundingClientRect();return{cx:(i.left+i.right)/2+c,cy:(i.top+i.bottom)/2+e}}),R=!0}let B=null;V&&(q(),B=new ResizeObserver(()=>{R=!1}),B.observe(t),(C=(a=document.fonts)==null?void 0:a.ready)==null||C.then(()=>{R=!1}));let $=-9999,w=-9999,P=!1,b=0,D=!0,X=null;function J(){if(!D)return;if(!P){T.forEach(i=>{S>0&&(i.style.transition=`font-variation-settings ${S}ms ease`),i.style.fontVariationSettings=A,j&&(i.style.letterSpacing=""),m&&et(i,m)}),S>0&&(X!==null&&clearTimeout(X),X=setTimeout(()=>{T.forEach(i=>{i.style.transition=""}),X=null},S)),b=0;return}V&&!R&&q();const c=V?$+window.scrollX:$,e=V?w+window.scrollY:w,v=V?null:T.map(i=>i.getBoundingClientRect());T.forEach((i,u)=>{i.style.transition="";let O,M;if(V)({cx:O,cy:M}=_[u]);else{const G=v[u];O=G.left+G.width/2,M=G.top+G.height/2}const f=Math.sqrt((c-O)**2+(e-M)**2),x=Math.max(0,1-f/y),Y=d==="quadratic"?x*x:x,I=L==="repel"?1-Y:Y,Z={};for(const G of Object.keys(E)){const[U,dt]=E[G]??[300,500];Z[G]=U+(dt-U)*I}i.style.fontVariationSettings=nt(g,Z),j&&H!==0&&(i.style.letterSpacing=`${(-H*I).toFixed(3)}px`),m&&st(i,m,Y)}),b=requestAnimationFrame(J)}function Q(c){$=c.clientX,w=c.clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J))}function l(){P=!1,b===0&&(b=requestAnimationFrame(J))}function s(c){c.touches.length!==0&&($=c.touches[0].clientX,w=c.touches[0].clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J)))}function p(){P=!1,b===0&&(b=requestAnimationFrame(J))}const r=F==="document"?document:t;return r.addEventListener("mousemove",Q),r.addEventListener("mouseleave",l),r.addEventListener("touchmove",s,{passive:!0}),r.addEventListener("touchend",p),()=>{D=!1,cancelAnimationFrame(b),X!==null&&clearTimeout(X),B&&B.disconnect(),r.removeEventListener("mousemove",Q),r.removeEventListener("mouseleave",l),r.removeEventListener("touchmove",s),r.removeEventListener("touchend",p),t.innerHTML=n}}function at(t){const n=h.useRef(null),o=h.useRef(null),E=h.useRef(t);E.current=t;const y=h.useRef(null),d=t.mode??"field",{axes:L,radius:F,falloff:m,magnetMode:S,wdthBoost:V,scope:j}=t,N=L?JSON.stringify(L):void 0,k=t.props?JSON.stringify(t.props):void 0,T=h.useCallback(()=>{const g=n.current;if(!g)return;o.current===null&&(o.current=ct(g)),y.current&&(y.current(),y.current=null),(E.current.mode??"field")==="field"?y.current=lt(g,o.current,E.current):y.current=it(g,o.current,E.current)},[d,N,F,m,S,V,j,k]);return h.useLayoutEffect(()=>(T(),()=>{y.current&&(y.current(),y.current=null)}),[T]),h.useEffect(()=>{var g,A;(A=(g=document.fonts)==null?void 0:g.ready)==null||A.then(T)},[T]),n}const ut=h.forwardRef(function({children:n,as:o="p",className:E,style:y,...d},L){const F=at(d),m=h.useCallback(S=>{F.current=S,typeof L=="function"?L(S):L&&(L.current=S)},[L]);return W.jsx(o,{ref:m,className:E,style:y,children:n})});ut.displayName="MagnetTypeText";const ft=h.forwardRef(function({children:n,as:o="p",className:E,style:y,minWeight:d=300,maxWeight:L=600,proximityRadius:F,spreadRadius:m,fixedAxes:S={},cachePositions:V=!0,rafThrottle:j=!0,stabilizeLayout:N=!0},k){const T=h.useRef(null),g=h.useRef(null),A=h.useRef([]),H=h.useRef([]),_=h.useRef(null),R=h.useRef(!1),q=h.useRef(null),B=h.useRef(0),$=h.useCallback(l=>{T.current=l,typeof k=="function"?k(l):k&&(k.current=l)},[k]);function w(l){const s=[`'wght' ${l.toFixed(0)}`];for(const[p,r]of Object.entries(S))s.push(`'${p}' ${r}`);return s.join(", ")}function P(){const l=T.current;if(!l)return;const s=window.scrollX,p=window.scrollY,r=l.getBoundingClientRect();_.current={top:r.top+p,left:r.left+s,right:r.right+s,bottom:r.bottom+p},H.current=A.current.map(a=>{if(!a)return{cx:0,cy:0};const C=a.getBoundingClientRect();return{cx:(C.left+C.right)/2+s,cy:(C.top+C.bottom)/2+p}}),R.current=!0}function b(l,s){const p=T.current;if(!p)return;V&&!R.current&&P();let r,a,C,c,e,v;if(V&&_.current)e=l+window.scrollX,v=s+window.scrollY,{top:r,left:a,right:C,bottom:c}=_.current;else{const M=p.getBoundingClientRect();e=l,v=s,r=M.top,a=M.left,C=M.right,c=M.bottom}const i=Math.max(a-e,0,e-C),u=Math.max(r-v,0,v-c),O=Math.sqrt(i*i+u*u);if(F!==void 0&&!m){const f=1-(1-Math.max(0,1-O/F))**2;p.style.fontVariationSettings=w(d+(L-d)*f);return}if(m){if(F!==void 0&&O>F){p.style.fontVariationSettings=w(d);for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}if(O>m){for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}const M=A.current;if(V&&H.current.length===M.length)for(let f=0;f<M.length;f++){const x=M[f];if(!x)continue;const{cx:Y,cy:I}=H.current[f],Z=Math.sqrt((e-Y)**2+(v-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;x.style.fontVariationSettings=w(d+(L-d)*U),N&&B.current!==0&&(x.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}else for(const f of M){if(!f)continue;const x=f.getBoundingClientRect(),Y=(x.left+x.right)/2,I=(x.top+x.bottom)/2,Z=Math.sqrt((l-Y)**2+(s-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;f.style.fontVariationSettings=w(d+(L-d)*U),N&&B.current!==0&&(f.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}}}const D=h.useCallback(l=>{g.current={x:l.clientX,y:l.clientY},j?q.current===null&&(q.current=requestAnimationFrame(()=>{q.current=null,g.current&&b(g.current.x,g.current.y)})):b(l.clientX,l.clientY)},[d,L,F,m,V,j,N,JSON.stringify(S)]),X=h.useCallback(()=>{g.current&&b(g.current.x,g.current.y)},[d,L,F,m,V,N,JSON.stringify(S)]),J=h.useCallback(()=>{g.current=null,q.current!==null&&(cancelAnimationFrame(q.current),q.current=null);const l=T.current;l&&(l.style.fontVariationSettings=w(d));for(const s of A.current)s&&(s.style.fontVariationSettings=w(d),N&&(s.style.letterSpacing=""))},[d,N,JSON.stringify(S)]);h.useEffect(()=>(window.addEventListener("mousemove",D,{passive:!0}),window.addEventListener("scroll",X,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",J),()=>{window.removeEventListener("mousemove",D),window.removeEventListener("scroll",X,{capture:!0}),document.documentElement.removeEventListener("mouseleave",J),q.current!==null&&(cancelAnimationFrame(q.current),q.current=null)}),[D,X,J]),h.useEffect(()=>{var p,r;if(!V||!m)return;R.current=!1;const l=T.current;if(!l)return;const s=new ResizeObserver(()=>{R.current=!1});return s.observe(l),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{R.current=!1}),()=>s.disconnect()},[V,m]),h.useEffect(()=>{R.current=!1},[n,m]),h.useEffect(()=>{var s,p;if(!N||!m){B.current=0;return}function l(){const r=T.current;if(!r)return;const a=r.textContent??"",C=a.replace(/\s/g,"").length;if(C===0)return;const c=getComputedStyle(r),e=document.createElement("span");e.style.cssText="position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;",e.style.fontFamily=c.fontFamily,e.style.fontSize=c.fontSize,e.style.fontWeight=c.fontWeight,e.style.lineHeight=c.lineHeight,e.style.letterSpacing=c.letterSpacing,e.textContent=a,document.body.appendChild(e);const v=O=>{const M=[`'wght' ${O.toFixed(0)}`];for(const[f,x]of Object.entries(S))M.push(`'${f}' ${x}`);return M.join(", ")};e.style.fontVariationSettings=v(L);const i=e.scrollWidth;e.style.fontVariationSettings=v(d);const u=e.scrollWidth;document.body.removeChild(e),B.current=i>u?(i-u)/C:0}l(),(p=(s=document.fonts)==null?void 0:s.ready)==null||p.then(l)},[N,m,d,L,n,JSON.stringify(S)]);const Q=h.useMemo(()=>{if(!m)return n;A.current=[];let l=0;function s(p){if(typeof p=="string")return[...p].map(r=>{if(/\s/.test(r))return r;const a=l++;return W.jsx("span",{ref:C=>{A.current[a]=C},style:{fontVariationSettings:w(d)},children:r},a)});if(Array.isArray(p))return p.map((r,a)=>W.jsx(h.Fragment,{children:s(r)},a));if(h.isValidElement(p)){const r=p;if(r.props.children!==void 0)return h.cloneElement(r,{},s(r.props.children))}return p}return s(n)},[n,m,d,JSON.stringify(S)]);return W.jsx(o,{ref:$,className:E,style:{fontVariationSettings:w(d),...y},children:Q})});ft.displayName="MagnetBlock";exports.MAGNET_TYPE_CLASSES=z;exports.MagnetBlock=ft;exports.MagnetTypeText=ut;exports.applyMagnetType=it;exports.getCleanHTML=ct;exports.removeMagnetType=mt;exports.startMagnetType=lt;exports.useMagnetType=at;
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react"),W=require("react/jsx-runtime"),st={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},z={word:"mt-word",char:"mt-char",probe:"mt-probe"},K={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function ot(t,n=[]){return t.nodeType===Node.TEXT_NODE?n.push(t):t.childNodes.forEach(o=>ot(o,n)),n}function tt(t,n,o){if(!t||t==="normal")return`"${n}" ${o}`;const E=new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`),y=`"${n}" ${o}`;return E.test(t)?t.replace(E,y):`${t}, ${y}`}function nt(t,n){let o=t;for(const[E,y]of Object.entries(n))o=tt(o,E,y);return o}function ct(t,n,o){if(n.opacity!==void 0){const[E,y]=n.opacity;t.style.opacity=String(E+(y-E)*o)}n.italic===!0&&(t.style.fontStyle=o>.5?"italic":"")}function et(t,n){n.opacity!==void 0&&(t.style.opacity=String(n.opacity[0])),n.italic===!0&&(t.style.fontStyle="")}function it(t){const n=t.cloneNode(!0),o=n.querySelectorAll(`.${z.word}, .${z.char}`);return Array.from(o).reverse().forEach(y=>{const d=y.parentNode;if(d){for(;y.firstChild;)d.insertBefore(y.firstChild,y);d.removeChild(y)}}),n.innerHTML}function mt(t,n){t.innerHTML=n}function lt(t,n,o={}){var p,r;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.wdthBoost??K.wdthBoost,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.scope??K.scope,F=o.props,m=o.transitionMs??0,S=o.cachePositions??!0,V=window.scrollY;t.innerHTML=n;const j=getComputedStyle(t).fontVariationSettings,N=j.match(/"wdth"\s+([\d.eE+-]+)/),X=N?parseFloat(N[1]):100,T=ot(t),g=[];for(const a of T){const C=a.textContent??"";if(!C||!C.split("").some(v=>v in st))continue;const e=document.createDocumentFragment();for(const v of C){const i=st[v];if(i===void 0){const u=e.lastChild;u&&u.nodeType===Node.TEXT_NODE?u.textContent+=v:e.appendChild(document.createTextNode(v))}else{const u=document.createElement("span");u.className=z.char,u.style.fontVariationSettings=tt(j,"wdth",X),u.textContent=v,e.appendChild(u),g.push({span:u,riskLevel:i})}}a.parentNode.replaceChild(e,a)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-V)>2&&window.scrollTo({top:V,behavior:"instant"})}),g.length===0)return()=>{};F&&g.forEach(({span:a})=>et(a,F));let A=[],H=!1;function _(){const a=window.scrollX,C=window.scrollY;A=g.map(({span:c})=>{const e=c.getBoundingClientRect();return{cx:(e.left+e.right)/2+a,cy:(e.top+e.bottom)/2+C}}),H=!0}let R=null;S&&(_(),R=new ResizeObserver(()=>{H=!1}),R.observe(t),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{H=!1}));let q=-9999,$=-9999,B=!1,w=0,P=!0,b=null;function D(){if(!P)return;if(!B){g.forEach(({span:e})=>{m>0&&(e.style.transition=`font-variation-settings ${m}ms ease`),e.style.fontVariationSettings=tt(j,"wdth",X),F&&et(e,F)}),m>0&&(b!==null&&clearTimeout(b),b=setTimeout(()=>{g.forEach(({span:e})=>{e.style.transition=""}),b=null},m)),w=0;return}S&&!H&&_();const a=S?q+window.scrollX:q,C=S?$+window.scrollY:$,c=S?null:g.map(({span:e})=>e.getBoundingClientRect());g.forEach(({span:e,riskLevel:v},i)=>{e.style.transition="";let u,O;if(S)({cx:u,cy:O}=A[i]);else{const I=c[i];u=I.left+I.width/2,O=I.top+I.height/2}const M=Math.sqrt((a-u)**2+(C-O)**2),f=Math.max(0,1-M/y),x=d==="quadratic"?f*f:f,Y=E*(v/3)*x;e.style.fontVariationSettings=tt(j,"wdth",X+Y),F&&ct(e,F,x)}),w=requestAnimationFrame(D)}function k(a){q=a.clientX,$=a.clientY,B||(B=!0),w===0&&(w=requestAnimationFrame(D))}function J(){B=!1,w===0&&(w=requestAnimationFrame(D))}function Q(a){a.touches.length!==0&&(q=a.touches[0].clientX,$=a.touches[0].clientY,B||(B=!0),w===0&&(w=requestAnimationFrame(D)))}function l(){B=!1,w===0&&(w=requestAnimationFrame(D))}const s=L==="document"?document:t;return s.addEventListener("mousemove",k),s.addEventListener("mouseleave",J),s.addEventListener("touchmove",Q,{passive:!0}),s.addEventListener("touchend",l),()=>{P=!1,cancelAnimationFrame(w),b!==null&&clearTimeout(b),R&&R.disconnect(),s.removeEventListener("mousemove",k),s.removeEventListener("mouseleave",J),s.removeEventListener("touchmove",Q),s.removeEventListener("touchend",l),t.innerHTML=n}}function at(t,n,o={}){var a,C;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const E=o.axes??K.axes,y=o.radius??K.radius,d=o.falloff??K.falloff,L=o.magnetMode??K.magnetMode,F=o.scope??K.scope,m=o.props,S=o.transitionMs??0,V=o.cachePositions??!0,j=o.stabilizeLayout??!0,N=window.scrollY;t.innerHTML=n;const X=ot(t),T=[];for(const c of X){const e=c.textContent??"";if(!e.trim())continue;const v=e.split(/(\S+)/),i=document.createDocumentFragment();for(let u=0;u<v.length;u+=2){const O=v[u],M=v[u+1];if(!M)continue;const x=v[u+3]===void 0?v[u+2]??"":"",Y=document.createElement("span");Y.className=z.word,Y.textContent=O+M+x,i.appendChild(Y),T.push(Y)}c.parentNode.replaceChild(i,c)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-N)>2&&window.scrollTo({top:N,behavior:"instant"})}),T.length===0)return()=>{};const g=getComputedStyle(t).fontVariationSettings,A=nt(g,Object.fromEntries(Object.entries(E).map(([c,[e]])=>[c,e])));let H=0;if(j){const c=nt(g,Object.fromEntries(Object.entries(E).map(([f,[,x]])=>[f,x]))),e=t.style.fontVariationSettings,v=t.style.whiteSpace,i=t.style.overflow;t.style.whiteSpace="nowrap",t.style.overflow="visible",t.style.fontVariationSettings=c;const u=t.scrollWidth;t.style.fontVariationSettings=A;const O=t.scrollWidth;t.style.fontVariationSettings=e,t.style.whiteSpace=v,t.style.overflow=i;const M=T.reduce((f,x)=>{var Y;return f+(((Y=x.textContent)==null?void 0:Y.replace(/\s+/g,"").length)??0)},0);M>0&&u>O&&(H=(u-O)/M)}T.forEach(c=>{c.style.fontVariationSettings=A,m&&et(c,m)});let _=[],R=!1;function q(){const c=window.scrollX,e=window.scrollY;_=T.map(v=>{const i=v.getBoundingClientRect();return{cx:(i.left+i.right)/2+c,cy:(i.top+i.bottom)/2+e}}),R=!0}let $=null;V&&(q(),$=new ResizeObserver(()=>{R=!1}),$.observe(t),(C=(a=document.fonts)==null?void 0:a.ready)==null||C.then(()=>{R=!1}));let B=-9999,w=-9999,P=!1,b=0,D=!0,k=null;function J(){if(!D)return;if(!P){T.forEach(i=>{S>0&&(i.style.transition=`font-variation-settings ${S}ms ease`),i.style.fontVariationSettings=A,j&&(i.style.letterSpacing=""),m&&et(i,m)}),S>0&&(k!==null&&clearTimeout(k),k=setTimeout(()=>{T.forEach(i=>{i.style.transition=""}),k=null},S)),b=0;return}V&&!R&&q();const c=V?B+window.scrollX:B,e=V?w+window.scrollY:w,v=V?null:T.map(i=>i.getBoundingClientRect());T.forEach((i,u)=>{i.style.transition="";let O,M;if(V)({cx:O,cy:M}=_[u]);else{const G=v[u];O=G.left+G.width/2,M=G.top+G.height/2}const f=Math.sqrt((c-O)**2+(e-M)**2),x=Math.max(0,1-f/y),Y=d==="quadratic"?x*x:x,I=L==="repel"?1-Y:Y,Z={};for(const G of Object.keys(E)){const[U,dt]=E[G]??[300,500];Z[G]=U+(dt-U)*I}i.style.fontVariationSettings=nt(g,Z),j&&H!==0&&(i.style.letterSpacing=`${(-H*I).toFixed(3)}px`),m&&ct(i,m,Y)}),b=requestAnimationFrame(J)}function Q(c){B=c.clientX,w=c.clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J))}function l(){P=!1,b===0&&(b=requestAnimationFrame(J))}function s(c){c.touches.length!==0&&(B=c.touches[0].clientX,w=c.touches[0].clientY,P||(P=!0),b===0&&(b=requestAnimationFrame(J)))}function p(){P=!1,b===0&&(b=requestAnimationFrame(J))}const r=F==="document"?document:t;return r.addEventListener("mousemove",Q),r.addEventListener("mouseleave",l),r.addEventListener("touchmove",s,{passive:!0}),r.addEventListener("touchend",p),()=>{D=!1,cancelAnimationFrame(b),k!==null&&clearTimeout(k),$&&$.disconnect(),r.removeEventListener("mousemove",Q),r.removeEventListener("mouseleave",l),r.removeEventListener("touchmove",s),r.removeEventListener("touchend",p),t.innerHTML=n}}function ut(t){const n=h.useRef(null),o=h.useRef(null),E=h.useRef(t);E.current=t;const y=h.useRef(null),d=t.mode??"word",{axes:L,radius:F,falloff:m,magnetMode:S,wdthBoost:V,scope:j}=t,N=L?JSON.stringify(L):void 0,X=t.props?JSON.stringify(t.props):void 0,T=h.useCallback(()=>{const g=n.current;if(!g)return;o.current===null&&(o.current=it(g)),y.current&&(y.current(),y.current=null),(E.current.mode??"word")==="word"?y.current=at(g,o.current,E.current):y.current=lt(g,o.current,E.current)},[d,N,F,m,S,V,j,X]);return h.useLayoutEffect(()=>(T(),()=>{y.current&&(y.current(),y.current=null)}),[T]),h.useEffect(()=>{var g,A;(A=(g=document.fonts)==null?void 0:g.ready)==null||A.then(T)},[T]),n}const ft=h.forwardRef(function({children:n,as:o="p",className:E,style:y,...d},L){const F=ut(d),m=h.useCallback(S=>{F.current=S,typeof L=="function"?L(S):L&&(L.current=S)},[L]);return W.jsx(o,{ref:m,className:E,style:y,children:n})});ft.displayName="MagnetTypeText";const rt=h.forwardRef(function({children:n,as:o="p",className:E,style:y,minWeight:d=300,maxWeight:L=600,proximityRadius:F,spreadRadius:m,fixedAxes:S={},cachePositions:V=!0,rafThrottle:j=!0,stabilizeLayout:N=!0},X){const T=h.useRef(null),g=h.useRef(null),A=h.useRef([]),H=h.useRef([]),_=h.useRef(null),R=h.useRef(!1),q=h.useRef(null),$=h.useRef(0),B=h.useCallback(l=>{T.current=l,typeof X=="function"?X(l):X&&(X.current=l)},[X]);function w(l){const s=[`'wght' ${l.toFixed(0)}`];for(const[p,r]of Object.entries(S))s.push(`'${p}' ${r}`);return s.join(", ")}function P(){const l=T.current;if(!l)return;const s=window.scrollX,p=window.scrollY,r=l.getBoundingClientRect();_.current={top:r.top+p,left:r.left+s,right:r.right+s,bottom:r.bottom+p},H.current=A.current.map(a=>{if(!a)return{cx:0,cy:0};const C=a.getBoundingClientRect();return{cx:(C.left+C.right)/2+s,cy:(C.top+C.bottom)/2+p}}),R.current=!0}function b(l,s){const p=T.current;if(!p)return;V&&!R.current&&P();let r,a,C,c,e,v;if(V&&_.current)e=l+window.scrollX,v=s+window.scrollY,{top:r,left:a,right:C,bottom:c}=_.current;else{const M=p.getBoundingClientRect();e=l,v=s,r=M.top,a=M.left,C=M.right,c=M.bottom}const i=Math.max(a-e,0,e-C),u=Math.max(r-v,0,v-c),O=Math.sqrt(i*i+u*u);if(F!==void 0&&!m){const f=1-(1-Math.max(0,1-O/F))**2;p.style.fontVariationSettings=w(d+(L-d)*f);return}if(m){if(F!==void 0&&O>F){p.style.fontVariationSettings=w(d);for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}if(O>m){for(const f of A.current)f&&(f.style.fontVariationSettings=w(d),N&&(f.style.letterSpacing=""));return}const M=A.current;if(V&&H.current.length===M.length)for(let f=0;f<M.length;f++){const x=M[f];if(!x)continue;const{cx:Y,cy:I}=H.current[f],Z=Math.sqrt((e-Y)**2+(v-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;x.style.fontVariationSettings=w(d+(L-d)*U),N&&$.current!==0&&(x.style.letterSpacing=`${(-$.current*U).toFixed(3)}px`)}else for(const f of M){if(!f)continue;const x=f.getBoundingClientRect(),Y=(x.left+x.right)/2,I=(x.top+x.bottom)/2,Z=Math.sqrt((l-Y)**2+(s-I)**2),U=1-(1-Math.max(0,1-Z/m))**2;f.style.fontVariationSettings=w(d+(L-d)*U),N&&$.current!==0&&(f.style.letterSpacing=`${(-$.current*U).toFixed(3)}px`)}}}const D=h.useCallback(l=>{g.current={x:l.clientX,y:l.clientY},j?q.current===null&&(q.current=requestAnimationFrame(()=>{q.current=null,g.current&&b(g.current.x,g.current.y)})):b(l.clientX,l.clientY)},[d,L,F,m,V,j,N,JSON.stringify(S)]),k=h.useCallback(()=>{g.current&&b(g.current.x,g.current.y)},[d,L,F,m,V,N,JSON.stringify(S)]),J=h.useCallback(()=>{g.current=null,q.current!==null&&(cancelAnimationFrame(q.current),q.current=null);const l=T.current;l&&(l.style.fontVariationSettings=w(d));for(const s of A.current)s&&(s.style.fontVariationSettings=w(d),N&&(s.style.letterSpacing=""))},[d,N,JSON.stringify(S)]);h.useEffect(()=>(window.addEventListener("mousemove",D,{passive:!0}),window.addEventListener("scroll",k,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",J),()=>{window.removeEventListener("mousemove",D),window.removeEventListener("scroll",k,{capture:!0}),document.documentElement.removeEventListener("mouseleave",J),q.current!==null&&(cancelAnimationFrame(q.current),q.current=null)}),[D,k,J]),h.useEffect(()=>{var p,r;if(!V||!m)return;R.current=!1;const l=T.current;if(!l)return;const s=new ResizeObserver(()=>{R.current=!1});return s.observe(l),(r=(p=document.fonts)==null?void 0:p.ready)==null||r.then(()=>{R.current=!1}),()=>s.disconnect()},[V,m]),h.useEffect(()=>{R.current=!1},[n,m]),h.useEffect(()=>{var s,p;if(!N||!m){$.current=0;return}function l(){const r=T.current;if(!r)return;const a=r.textContent??"",C=a.replace(/\s/g,"").length;if(C===0)return;const c=getComputedStyle(r),e=document.createElement("span");e.style.cssText="position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;",e.style.fontFamily=c.fontFamily,e.style.fontSize=c.fontSize,e.style.fontWeight=c.fontWeight,e.style.lineHeight=c.lineHeight,e.style.letterSpacing=c.letterSpacing,e.textContent=a,document.body.appendChild(e);const v=O=>{const M=[`'wght' ${O.toFixed(0)}`];for(const[f,x]of Object.entries(S))M.push(`'${f}' ${x}`);return M.join(", ")};e.style.fontVariationSettings=v(L);const i=e.scrollWidth;e.style.fontVariationSettings=v(d);const u=e.scrollWidth;document.body.removeChild(e),$.current=i>u?(i-u)/C:0}l(),(p=(s=document.fonts)==null?void 0:s.ready)==null||p.then(l)},[N,m,d,L,n,JSON.stringify(S)]);const Q=h.useMemo(()=>{if(!m)return n;A.current=[];let l=0;function s(p){if(typeof p=="string")return[...p].map(r=>{if(/\s/.test(r))return r;const a=l++;return W.jsx("span",{ref:C=>{A.current[a]=C},style:{fontVariationSettings:w(d)},children:r},a)});if(Array.isArray(p))return p.map((r,a)=>W.jsx(h.Fragment,{children:s(r)},a));if(h.isValidElement(p)){const r=p;if(r.props.children!==void 0)return h.cloneElement(r,{},s(r.props.children))}return p}return s(n)},[n,m,d,JSON.stringify(S)]);return W.jsx(o,{ref:B,className:E,style:{fontVariationSettings:w(d),...y},children:Q})});rt.displayName="MagnetChar";const pt=rt;exports.MAGNET_TYPE_CLASSES=z;exports.MagnetBlock=pt;exports.MagnetChar=rt;exports.MagnetTypeText=ft;exports.applyMagnetType=lt;exports.getCleanHTML=it;exports.removeMagnetType=mt;exports.startMagnetType=at;exports.useMagnetType=ut;
|
package/dist/index.d.ts
CHANGED
|
@@ -38,9 +38,15 @@ export declare const MAGNET_TYPE_CLASSES: {
|
|
|
38
38
|
readonly probe: "mt-probe";
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/** @deprecated Use MagnetChar instead */
|
|
42
|
+
export declare const MagnetBlock: default_2.ForwardRefExoticComponent<MagnetCharProps & default_2.RefAttributes<HTMLElement>>;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
/** @deprecated Use MagnetCharProps instead */
|
|
45
|
+
export declare type MagnetBlockProps = MagnetCharProps;
|
|
46
|
+
|
|
47
|
+
export declare const MagnetChar: default_2.ForwardRefExoticComponent<MagnetCharProps & default_2.RefAttributes<HTMLElement>>;
|
|
48
|
+
|
|
49
|
+
export declare interface MagnetCharProps {
|
|
44
50
|
children: default_2.ReactNode;
|
|
45
51
|
/** HTML element to render. Default: 'p' */
|
|
46
52
|
as?: default_2.ElementType;
|
|
@@ -87,7 +93,7 @@ export declare interface MagnetBlockProps {
|
|
|
87
93
|
export declare type MagnetModeType = 'attract' | 'repel';
|
|
88
94
|
|
|
89
95
|
/** Which mode magnetType operates in */
|
|
90
|
-
export declare type MagnetTypeModeType = '
|
|
96
|
+
export declare type MagnetTypeModeType = 'word' | 'legibility';
|
|
91
97
|
|
|
92
98
|
/**
|
|
93
99
|
* Options for the magnetType effect.
|
|
@@ -98,9 +104,9 @@ export declare type MagnetTypeModeType = 'field' | 'legibility';
|
|
|
98
104
|
*/
|
|
99
105
|
export declare interface MagnetTypeOptions {
|
|
100
106
|
/**
|
|
101
|
-
* Operating mode. Default: '
|
|
107
|
+
* Operating mode. Default: 'word'
|
|
102
108
|
*
|
|
103
|
-
* - **'
|
|
109
|
+
* - **'word'** — cursor proximity drives per-word font-variation-settings.
|
|
104
110
|
* - **'legibility'** — cursor-driven wdth boost for confusable characters; both return a stop function.
|
|
105
111
|
*/
|
|
106
112
|
mode?: MagnetTypeModeType;
|
|
@@ -249,13 +255,13 @@ export declare function startMagnetType(element: HTMLElement, originalHTML: stri
|
|
|
249
255
|
/**
|
|
250
256
|
* React hook that applies the magnetType effect to a ref'd element.
|
|
251
257
|
*
|
|
252
|
-
* For mode: '
|
|
258
|
+
* For mode: 'word' — starts the cursor proximity rAF loop via startMagnetType.
|
|
253
259
|
* For mode: 'legibility' — starts the cursor-driven wdth boost via applyMagnetType.
|
|
254
260
|
*
|
|
255
261
|
* Both modes return a stop function on mount and restart when options change.
|
|
256
262
|
* No ResizeObserver needed — the rAF loop reads live getBoundingClientRect each frame.
|
|
257
263
|
*
|
|
258
|
-
* Defaults to '
|
|
264
|
+
* Defaults to 'word' mode if mode is undefined.
|
|
259
265
|
*/
|
|
260
266
|
export declare function useMagnetType(options: MagnetTypeOptions): RefObject<HTMLElement | null>;
|
|
261
267
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import rt, { useRef as I, useCallback as z, useLayoutEffect as
|
|
2
|
+
import rt, { useRef as I, useCallback as z, useLayoutEffect as dt, useEffect as W, forwardRef as lt, useMemo as mt } from "react";
|
|
3
3
|
import { jsx as tt } from "react/jsx-runtime";
|
|
4
4
|
const it = {
|
|
5
5
|
i: 3,
|
|
@@ -62,7 +62,7 @@ function at(t, n, o) {
|
|
|
62
62
|
function ot(t, n) {
|
|
63
63
|
n.opacity !== void 0 && (t.style.opacity = String(n.opacity[0])), n.italic === !0 && (t.style.fontStyle = "");
|
|
64
64
|
}
|
|
65
|
-
function
|
|
65
|
+
function pt(t) {
|
|
66
66
|
const n = t.cloneNode(!0), o = n.querySelectorAll(
|
|
67
67
|
`.${nt.word}, .${nt.char}`
|
|
68
68
|
);
|
|
@@ -77,7 +77,7 @@ function mt(t) {
|
|
|
77
77
|
function Mt(t, n) {
|
|
78
78
|
t.innerHTML = n;
|
|
79
79
|
}
|
|
80
|
-
function
|
|
80
|
+
function ht(t, n, o = {}) {
|
|
81
81
|
var p, r;
|
|
82
82
|
if (typeof window > "u") return () => {
|
|
83
83
|
};
|
|
@@ -86,8 +86,8 @@ function pt(t, n, o = {}) {
|
|
|
86
86
|
};
|
|
87
87
|
const M = o.wdthBoost ?? U.wdthBoost, h = o.radius ?? U.radius, d = o.falloff ?? U.falloff, b = o.scope ?? U.scope, F = o.props, m = o.transitionMs ?? 0, w = o.cachePositions ?? !0, L = window.scrollY;
|
|
88
88
|
t.innerHTML = n;
|
|
89
|
-
const X = getComputedStyle(t).fontVariationSettings, V = X.match(/"wdth"\s+([\d.eE+-]+)/), j = V ? parseFloat(V[1]) : 100,
|
|
90
|
-
for (const a of
|
|
89
|
+
const X = getComputedStyle(t).fontVariationSettings, V = X.match(/"wdth"\s+([\d.eE+-]+)/), j = V ? parseFloat(V[1]) : 100, C = ct(t), y = [];
|
|
90
|
+
for (const a of C) {
|
|
91
91
|
const E = a.textContent ?? "";
|
|
92
92
|
if (!E || !E.split("").some((g) => g in it)) continue;
|
|
93
93
|
const e = document.createDocumentFragment();
|
|
@@ -146,8 +146,8 @@ function pt(t, n, o = {}) {
|
|
|
146
146
|
const P = c[i];
|
|
147
147
|
u = P.left + P.width / 2, O = P.top + P.height / 2;
|
|
148
148
|
}
|
|
149
|
-
const S = Math.sqrt((a - u) ** 2 + (E - O) ** 2), f = Math.max(0, 1 - S / h),
|
|
150
|
-
e.style.fontVariationSettings = et(X, "wdth", j + q), F && at(e, F,
|
|
149
|
+
const S = Math.sqrt((a - u) ** 2 + (E - O) ** 2), f = Math.max(0, 1 - S / h), x = d === "quadratic" ? f * f : f, q = M * (g / 3) * x;
|
|
150
|
+
e.style.fontVariationSettings = et(X, "wdth", j + q), F && at(e, F, x);
|
|
151
151
|
}), v = requestAnimationFrame(D);
|
|
152
152
|
}
|
|
153
153
|
function k(a) {
|
|
@@ -167,7 +167,7 @@ function pt(t, n, o = {}) {
|
|
|
167
167
|
R = !1, cancelAnimationFrame(v), T !== null && clearTimeout(T), Y && Y.disconnect(), s.removeEventListener("mousemove", k), s.removeEventListener("mouseleave", J), s.removeEventListener("touchmove", Q), s.removeEventListener("touchend", l), t.innerHTML = n;
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
|
-
function
|
|
170
|
+
function yt(t, n, o = {}) {
|
|
171
171
|
var a, E;
|
|
172
172
|
if (typeof window > "u") return () => {
|
|
173
173
|
};
|
|
@@ -176,7 +176,7 @@ function ht(t, n, o = {}) {
|
|
|
176
176
|
};
|
|
177
177
|
const M = o.axes ?? U.axes, h = o.radius ?? U.radius, d = o.falloff ?? U.falloff, b = o.magnetMode ?? U.magnetMode, F = o.scope ?? U.scope, m = o.props, w = o.transitionMs ?? 0, L = o.cachePositions ?? !0, X = o.stabilizeLayout ?? !0, V = window.scrollY;
|
|
178
178
|
t.innerHTML = n;
|
|
179
|
-
const j = ct(t),
|
|
179
|
+
const j = ct(t), C = [];
|
|
180
180
|
for (const c of j) {
|
|
181
181
|
const e = c.textContent ?? "";
|
|
182
182
|
if (!e.trim()) continue;
|
|
@@ -184,14 +184,14 @@ function ht(t, n, o = {}) {
|
|
|
184
184
|
for (let u = 0; u < g.length; u += 2) {
|
|
185
185
|
const O = g[u], S = g[u + 1];
|
|
186
186
|
if (!S) continue;
|
|
187
|
-
const
|
|
188
|
-
q.className = nt.word, q.textContent = O + S +
|
|
187
|
+
const x = g[u + 3] === void 0 ? g[u + 2] ?? "" : "", q = document.createElement("span");
|
|
188
|
+
q.className = nt.word, q.textContent = O + S + x, i.appendChild(q), C.push(q);
|
|
189
189
|
}
|
|
190
190
|
c.parentNode.replaceChild(i, c);
|
|
191
191
|
}
|
|
192
192
|
if (requestAnimationFrame(() => {
|
|
193
193
|
typeof window < "u" && Math.abs(window.scrollY - V) > 2 && window.scrollTo({ top: V, behavior: "instant" });
|
|
194
|
-
}),
|
|
194
|
+
}), C.length === 0) return () => {
|
|
195
195
|
};
|
|
196
196
|
const y = getComputedStyle(t).fontVariationSettings, N = st(
|
|
197
197
|
y,
|
|
@@ -201,29 +201,29 @@ function ht(t, n, o = {}) {
|
|
|
201
201
|
if (X) {
|
|
202
202
|
const c = st(
|
|
203
203
|
y,
|
|
204
|
-
Object.fromEntries(Object.entries(M).map(([f, [,
|
|
204
|
+
Object.fromEntries(Object.entries(M).map(([f, [, x]]) => [f, x]))
|
|
205
205
|
), e = t.style.fontVariationSettings, g = t.style.whiteSpace, i = t.style.overflow;
|
|
206
206
|
t.style.whiteSpace = "nowrap", t.style.overflow = "visible", t.style.fontVariationSettings = c;
|
|
207
207
|
const u = t.scrollWidth;
|
|
208
208
|
t.style.fontVariationSettings = N;
|
|
209
209
|
const O = t.scrollWidth;
|
|
210
210
|
t.style.fontVariationSettings = e, t.style.whiteSpace = g, t.style.overflow = i;
|
|
211
|
-
const S =
|
|
212
|
-
(f,
|
|
211
|
+
const S = C.reduce(
|
|
212
|
+
(f, x) => {
|
|
213
213
|
var q;
|
|
214
|
-
return f + (((q =
|
|
214
|
+
return f + (((q = x.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
|
|
215
215
|
},
|
|
216
216
|
0
|
|
217
217
|
);
|
|
218
218
|
S > 0 && u > O && (H = (u - O) / S);
|
|
219
219
|
}
|
|
220
|
-
|
|
220
|
+
C.forEach((c) => {
|
|
221
221
|
c.style.fontVariationSettings = N, m && ot(c, m);
|
|
222
222
|
});
|
|
223
223
|
let _ = [], Y = !1;
|
|
224
224
|
function A() {
|
|
225
225
|
const c = window.scrollX, e = window.scrollY;
|
|
226
|
-
_ =
|
|
226
|
+
_ = C.map((g) => {
|
|
227
227
|
const i = g.getBoundingClientRect();
|
|
228
228
|
return { cx: (i.left + i.right) / 2 + c, cy: (i.top + i.bottom) / 2 + e };
|
|
229
229
|
}), Y = !0;
|
|
@@ -238,18 +238,18 @@ function ht(t, n, o = {}) {
|
|
|
238
238
|
function J() {
|
|
239
239
|
if (!D) return;
|
|
240
240
|
if (!R) {
|
|
241
|
-
|
|
241
|
+
C.forEach((i) => {
|
|
242
242
|
w > 0 && (i.style.transition = `font-variation-settings ${w}ms ease`), i.style.fontVariationSettings = N, X && (i.style.letterSpacing = ""), m && ot(i, m);
|
|
243
243
|
}), w > 0 && (k !== null && clearTimeout(k), k = setTimeout(() => {
|
|
244
|
-
|
|
244
|
+
C.forEach((i) => {
|
|
245
245
|
i.style.transition = "";
|
|
246
246
|
}), k = null;
|
|
247
247
|
}, w)), T = 0;
|
|
248
248
|
return;
|
|
249
249
|
}
|
|
250
250
|
L && !Y && A();
|
|
251
|
-
const c = L ? B + window.scrollX : B, e = L ? v + window.scrollY : v, g = L ? null :
|
|
252
|
-
|
|
251
|
+
const c = L ? B + window.scrollX : B, e = L ? v + window.scrollY : v, g = L ? null : C.map((i) => i.getBoundingClientRect());
|
|
252
|
+
C.forEach((i, u) => {
|
|
253
253
|
i.style.transition = "";
|
|
254
254
|
let O, S;
|
|
255
255
|
if (L)
|
|
@@ -258,10 +258,10 @@ function ht(t, n, o = {}) {
|
|
|
258
258
|
const K = g[u];
|
|
259
259
|
O = K.left + K.width / 2, S = K.top + K.height / 2;
|
|
260
260
|
}
|
|
261
|
-
const f = Math.sqrt((c - O) ** 2 + (e - S) ** 2),
|
|
261
|
+
const f = Math.sqrt((c - O) ** 2 + (e - S) ** 2), x = Math.max(0, 1 - f / h), q = d === "quadratic" ? x * x : x, P = b === "repel" ? 1 - q : q, Z = {};
|
|
262
262
|
for (const K of Object.keys(M)) {
|
|
263
|
-
const [G,
|
|
264
|
-
Z[K] = G + (
|
|
263
|
+
const [G, ft] = M[K] ?? [300, 500];
|
|
264
|
+
Z[K] = G + (ft - G) * P;
|
|
265
265
|
}
|
|
266
266
|
i.style.fontVariationSettings = st(y, Z), X && H !== 0 && (i.style.letterSpacing = `${(-H * P).toFixed(3)}px`), m && at(i, m, q);
|
|
267
267
|
}), T = requestAnimationFrame(J);
|
|
@@ -283,24 +283,24 @@ function ht(t, n, o = {}) {
|
|
|
283
283
|
D = !1, cancelAnimationFrame(T), k !== null && clearTimeout(k), $ && $.disconnect(), r.removeEventListener("mousemove", Q), r.removeEventListener("mouseleave", l), r.removeEventListener("touchmove", s), r.removeEventListener("touchend", p), t.innerHTML = n;
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
|
-
function
|
|
286
|
+
function gt(t) {
|
|
287
287
|
const n = I(null), o = I(null), M = I(t);
|
|
288
288
|
M.current = t;
|
|
289
|
-
const h = I(null), d = t.mode ?? "
|
|
289
|
+
const h = I(null), d = t.mode ?? "word", { axes: b, radius: F, falloff: m, magnetMode: w, wdthBoost: L, scope: X } = t, V = b ? JSON.stringify(b) : void 0, j = t.props ? JSON.stringify(t.props) : void 0, C = z(() => {
|
|
290
290
|
const y = n.current;
|
|
291
291
|
if (!y) return;
|
|
292
|
-
o.current === null && (o.current =
|
|
292
|
+
o.current === null && (o.current = pt(y)), h.current && (h.current(), h.current = null), (M.current.mode ?? "word") === "word" ? h.current = yt(y, o.current, M.current) : h.current = ht(y, o.current, M.current);
|
|
293
293
|
}, [d, V, F, m, w, L, X, j]);
|
|
294
|
-
return
|
|
294
|
+
return dt(() => (C(), () => {
|
|
295
295
|
h.current && (h.current(), h.current = null);
|
|
296
|
-
}), [
|
|
296
|
+
}), [C]), W(() => {
|
|
297
297
|
var y, N;
|
|
298
|
-
(N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(
|
|
299
|
-
}, [
|
|
298
|
+
(N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(C);
|
|
299
|
+
}, [C]), n;
|
|
300
300
|
}
|
|
301
|
-
const
|
|
301
|
+
const vt = lt(
|
|
302
302
|
function({ children: n, as: o = "p", className: M, style: h, ...d }, b) {
|
|
303
|
-
const F =
|
|
303
|
+
const F = gt(d), m = z(
|
|
304
304
|
(w) => {
|
|
305
305
|
F.current = w, typeof b == "function" ? b(w) : b && (b.current = w);
|
|
306
306
|
},
|
|
@@ -310,8 +310,8 @@ const gt = lt(
|
|
|
310
310
|
return /* @__PURE__ */ tt(o, { ref: m, className: M, style: h, children: n });
|
|
311
311
|
}
|
|
312
312
|
);
|
|
313
|
-
|
|
314
|
-
const
|
|
313
|
+
vt.displayName = "MagnetTypeText";
|
|
314
|
+
const ut = lt(
|
|
315
315
|
function({
|
|
316
316
|
children: n,
|
|
317
317
|
as: o = "p",
|
|
@@ -326,9 +326,9 @@ const vt = lt(
|
|
|
326
326
|
rafThrottle: X = !0,
|
|
327
327
|
stabilizeLayout: V = !0
|
|
328
328
|
}, j) {
|
|
329
|
-
const
|
|
329
|
+
const C = I(null), y = I(null), N = I([]), H = I([]), _ = I(null), Y = I(!1), A = I(null), $ = I(0), B = z(
|
|
330
330
|
(l) => {
|
|
331
|
-
|
|
331
|
+
C.current = l, typeof j == "function" ? j(l) : j && (j.current = l);
|
|
332
332
|
},
|
|
333
333
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
334
334
|
[j]
|
|
@@ -339,7 +339,7 @@ const vt = lt(
|
|
|
339
339
|
return s.join(", ");
|
|
340
340
|
}
|
|
341
341
|
function R() {
|
|
342
|
-
const l =
|
|
342
|
+
const l = C.current;
|
|
343
343
|
if (!l) return;
|
|
344
344
|
const s = window.scrollX, p = window.scrollY, r = l.getBoundingClientRect();
|
|
345
345
|
_.current = {
|
|
@@ -357,7 +357,7 @@ const vt = lt(
|
|
|
357
357
|
}), Y.current = !0;
|
|
358
358
|
}
|
|
359
359
|
function T(l, s) {
|
|
360
|
-
const p =
|
|
360
|
+
const p = C.current;
|
|
361
361
|
if (!p) return;
|
|
362
362
|
L && !Y.current && R();
|
|
363
363
|
let r, a, E, c, e, g;
|
|
@@ -388,15 +388,15 @@ const vt = lt(
|
|
|
388
388
|
const S = N.current;
|
|
389
389
|
if (L && H.current.length === S.length)
|
|
390
390
|
for (let f = 0; f < S.length; f++) {
|
|
391
|
-
const
|
|
392
|
-
if (!
|
|
391
|
+
const x = S[f];
|
|
392
|
+
if (!x) continue;
|
|
393
393
|
const { cx: q, cy: P } = H.current[f], Z = Math.sqrt((e - q) ** 2 + (g - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / m)) ** 2;
|
|
394
|
-
|
|
394
|
+
x.style.fontVariationSettings = v(d + (b - d) * G), V && $.current !== 0 && (x.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
|
|
395
395
|
}
|
|
396
396
|
else
|
|
397
397
|
for (const f of S) {
|
|
398
398
|
if (!f) continue;
|
|
399
|
-
const
|
|
399
|
+
const x = f.getBoundingClientRect(), q = (x.left + x.right) / 2, P = (x.top + x.bottom) / 2, Z = Math.sqrt((l - q) ** 2 + (s - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / m)) ** 2;
|
|
400
400
|
f.style.fontVariationSettings = v(d + (b - d) * G), V && $.current !== 0 && (f.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
|
|
401
401
|
}
|
|
402
402
|
}
|
|
@@ -418,7 +418,7 @@ const vt = lt(
|
|
|
418
418
|
), J = z(
|
|
419
419
|
() => {
|
|
420
420
|
y.current = null, A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
|
|
421
|
-
const l =
|
|
421
|
+
const l = C.current;
|
|
422
422
|
l && (l.style.fontVariationSettings = v(d));
|
|
423
423
|
for (const s of N.current)
|
|
424
424
|
s && (s.style.fontVariationSettings = v(d), V && (s.style.letterSpacing = ""));
|
|
@@ -432,7 +432,7 @@ const vt = lt(
|
|
|
432
432
|
var p, r;
|
|
433
433
|
if (!L || !m) return;
|
|
434
434
|
Y.current = !1;
|
|
435
|
-
const l =
|
|
435
|
+
const l = C.current;
|
|
436
436
|
if (!l) return;
|
|
437
437
|
const s = new ResizeObserver(() => {
|
|
438
438
|
Y.current = !1;
|
|
@@ -449,7 +449,7 @@ const vt = lt(
|
|
|
449
449
|
return;
|
|
450
450
|
}
|
|
451
451
|
function l() {
|
|
452
|
-
const r =
|
|
452
|
+
const r = C.current;
|
|
453
453
|
if (!r) return;
|
|
454
454
|
const a = r.textContent ?? "", E = a.replace(/\s/g, "").length;
|
|
455
455
|
if (E === 0) return;
|
|
@@ -457,7 +457,7 @@ const vt = lt(
|
|
|
457
457
|
e.style.cssText = "position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;", e.style.fontFamily = c.fontFamily, e.style.fontSize = c.fontSize, e.style.fontWeight = c.fontWeight, e.style.lineHeight = c.lineHeight, e.style.letterSpacing = c.letterSpacing, e.textContent = a, document.body.appendChild(e);
|
|
458
458
|
const g = (O) => {
|
|
459
459
|
const S = [`'wght' ${O.toFixed(0)}`];
|
|
460
|
-
for (const [f,
|
|
460
|
+
for (const [f, x] of Object.entries(w)) S.push(`'${f}' ${x}`);
|
|
461
461
|
return S.join(", ");
|
|
462
462
|
};
|
|
463
463
|
e.style.fontVariationSettings = g(b);
|
|
@@ -468,7 +468,7 @@ const vt = lt(
|
|
|
468
468
|
}
|
|
469
469
|
l(), (p = (s = document.fonts) == null ? void 0 : s.ready) == null || p.then(l);
|
|
470
470
|
}, [V, m, d, b, n, JSON.stringify(w)]);
|
|
471
|
-
const Q =
|
|
471
|
+
const Q = mt(() => {
|
|
472
472
|
if (!m) return n;
|
|
473
473
|
N.current = [];
|
|
474
474
|
let l = 0;
|
|
@@ -510,14 +510,16 @@ const vt = lt(
|
|
|
510
510
|
);
|
|
511
511
|
}
|
|
512
512
|
);
|
|
513
|
-
|
|
513
|
+
ut.displayName = "MagnetChar";
|
|
514
|
+
const Et = ut;
|
|
514
515
|
export {
|
|
515
516
|
nt as MAGNET_TYPE_CLASSES,
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
517
|
+
Et as MagnetBlock,
|
|
518
|
+
ut as MagnetChar,
|
|
519
|
+
vt as MagnetTypeText,
|
|
520
|
+
ht as applyMagnetType,
|
|
521
|
+
pt as getCleanHTML,
|
|
520
522
|
Mt as removeMagnetType,
|
|
521
|
-
|
|
522
|
-
|
|
523
|
+
yt as startMagnetType,
|
|
524
|
+
gt as useMagnetType
|
|
523
525
|
};
|