@liiift-studio/magnettype 1.1.5 → 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 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="field"
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 (`MagnetBlock`)
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 { MagnetBlock } from '@liiift-studio/magnettype'
48
+ import { MagnetChar } from '@liiift-studio/magnettype'
49
49
 
50
50
  // Per-character spread — each character responds to cursor distance
51
- <MagnetBlock
51
+ <MagnetChar
52
52
  spreadRadius={200}
53
53
  minWeight={300}
54
54
  maxWeight={700}
55
55
  >
56
56
  Typography that responds to presence.
57
- </MagnetBlock>
57
+ </MagnetChar>
58
58
 
59
59
  // Whole-element gate — the effect only activates when the cursor is within proximityRadius of the element edge
60
- <MagnetBlock
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
- </MagnetBlock>
66
+ </MagnetChar>
67
67
 
68
68
  // Both combined — proximity gates the spread effect
69
- <MagnetBlock
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
- </MagnetBlock>
76
+ </MagnetChar>
77
77
  ```
78
78
 
79
- **`MagnetBlock` props:**
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: 'field', axes: { wght: [300, 700] }, radius: 150 })
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: 'field', axes: { wght: [300, 700] }, radius: 150 }
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, MagnetBlockProps } from '@liiift-studio/magnettype'
166
+ import type { MagnetTypeOptions, FalloffType, MagnetModeType, MagnetCharProps } from '@liiift-studio/magnettype'
167
167
 
168
168
  const fieldOpts: MagnetTypeOptions = {
169
- mode: 'field',
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` | `'field'` | `'field'` — cursor proximity drives per-word `font-variation-settings` via a continuous rAF loop. `'legibility'` — static per-character `wdth` boost on visually confusable characters |
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 (`MagnetBlock`)
212
+ ### Block mode (`MagnetChar`)
213
213
 
214
- `MagnetBlock` 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.
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.1.5
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(s=>ot(s,n)),n}function tt(t,n,s){if(!t||t==="normal")return`"${n}" ${s}`;const M=new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`),y=`"${n}" ${s}`;return M.test(t)?t.replace(M,y):`${t}, ${y}`}function nt(t,n){let s=t;for(const[M,y]of Object.entries(n))s=tt(s,M,y);return s}function st(t,n,s){if(n.opacity!==void 0){const[M,y]=n.opacity;t.style.opacity=String(M+(y-M)*s)}n.italic===!0&&(t.style.fontStyle=s>.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),s=n.querySelectorAll(`.${z.word}, .${z.char}`);return Array.from(s).reverse().forEach(y=>{const u=y.parentNode;if(u){for(;y.firstChild;)u.insertBefore(y.firstChild,y);u.removeChild(y)}}),n.innerHTML}function pt(t,n){t.innerHTML=n}function it(t,n,s={}){var p,o;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const M=s.wdthBoost??K.wdthBoost,y=s.radius??K.radius,u=s.falloff??K.falloff,L=s.scope??K.scope,F=s.props,d=s.transitionMs??0,S=s.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,C=ot(t),g=[];for(const e of C){const E=e.textContent??"";if(!E||!E.split("").some(v=>v in rt))continue;const r=document.createDocumentFragment();for(const v of E){const l=rt[v];if(l===void 0){const f=r.lastChild;f&&f.nodeType===Node.TEXT_NODE?f.textContent+=v:r.appendChild(document.createTextNode(v))}else{const f=document.createElement("span");f.className=z.char,f.style.fontVariationSettings=tt(j,"wdth",k),f.textContent=v,r.appendChild(f),g.push({span:f,riskLevel:l})}}e.parentNode.replaceChild(r,e)}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:e})=>et(e,F));let A=[],H=!1;function _(){const e=window.scrollX,E=window.scrollY;A=g.map(({span:a})=>{const r=a.getBoundingClientRect();return{cx:(r.left+r.right)/2+e,cy:(r.top+r.bottom)/2+E}}),H=!0}let R=null;S&&(_(),R=new ResizeObserver(()=>{H=!1}),R.observe(t),(o=(p=document.fonts)==null?void 0:p.ready)==null||o.then(()=>{H=!1}));let q=-9999,B=-9999,$=!1,w=0,P=!0,T=null;function D(){if(!P)return;if(!$){g.forEach(({span:r})=>{d>0&&(r.style.transition=`font-variation-settings ${d}ms ease`),r.style.fontVariationSettings=tt(j,"wdth",k),F&&et(r,F)}),d>0&&(T!==null&&clearTimeout(T),T=setTimeout(()=>{g.forEach(({span:r})=>{r.style.transition=""}),T=null},d)),w=0;return}S&&!H&&_();const e=S?q+window.scrollX:q,E=S?B+window.scrollY:B,a=S?null:g.map(({span:r})=>r.getBoundingClientRect());g.forEach(({span:r,riskLevel:v},l)=>{r.style.transition="";let f,O;if(S)({cx:f,cy:O}=A[l]);else{const I=a[l];f=I.left+I.width/2,O=I.top+I.height/2}const x=Math.sqrt((e-f)**2+(E-O)**2),m=Math.max(0,1-x/y),b=u==="quadratic"?m*m:m,Y=M*(v/3)*b;r.style.fontVariationSettings=tt(j,"wdth",k+Y),F&&st(r,F,b)}),w=requestAnimationFrame(D)}function X(e){q=e.clientX,B=e.clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D))}function J(){$=!1,w===0&&(w=requestAnimationFrame(D))}function Q(e){e.touches.length!==0&&(q=e.touches[0].clientX,B=e.touches[0].clientY,$||($=!0),w===0&&(w=requestAnimationFrame(D)))}function i(){$=!1,w===0&&(w=requestAnimationFrame(D))}const c=L==="document"?document:t;return c.addEventListener("mousemove",X),c.addEventListener("mouseleave",J),c.addEventListener("touchmove",Q,{passive:!0}),c.addEventListener("touchend",i),()=>{P=!1,cancelAnimationFrame(w),T!==null&&clearTimeout(T),R&&R.disconnect(),c.removeEventListener("mousemove",X),c.removeEventListener("mouseleave",J),c.removeEventListener("touchmove",Q),c.removeEventListener("touchend",i),t.innerHTML=n}}function lt(t,n,s={}){var e,E;if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=n,()=>{};const M=s.axes??K.axes,y=s.radius??K.radius,u=s.falloff??K.falloff,L=s.magnetMode??K.magnetMode,F=s.scope??K.scope,d=s.props,S=s.transitionMs??0,V=s.cachePositions??!0,j=s.stabilizeLayout??!0,N=window.scrollY;t.innerHTML=n;const k=ot(t),C=[];for(const a of k){const r=a.textContent??"";if(!r.trim())continue;const v=r.split(/(\S+)/),l=document.createDocumentFragment();for(let f=0;f<v.length;f+=2){const O=v[f],x=v[f+1];if(!x)continue;const b=v[f+3]===void 0?v[f+2]??"":"",Y=document.createElement("span");Y.className=z.word,Y.textContent=O+x+b,l.appendChild(Y),C.push(Y)}a.parentNode.replaceChild(l,a)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-N)>2&&window.scrollTo({top:N,behavior:"instant"})}),C.length===0)return()=>{};const g=getComputedStyle(t).fontVariationSettings,A=nt(g,Object.fromEntries(Object.entries(M).map(([a,[r]])=>[a,r])));let H=0;if(j){const a=nt(g,Object.fromEntries(Object.entries(M).map(([m,[,b]])=>[m,b]))),r=t.style.fontVariationSettings,v=t.style.whiteSpace,l=t.style.overflow;t.style.whiteSpace="nowrap",t.style.overflow="visible",t.style.fontVariationSettings=a;const f=t.scrollWidth;t.style.fontVariationSettings=A;const O=t.scrollWidth;t.style.fontVariationSettings=r,t.style.whiteSpace=v,t.style.overflow=l;const x=C.reduce((m,b)=>{var Y;return m+(((Y=b.textContent)==null?void 0:Y.replace(/\s+/g,"").length)??0)},0);x>0&&f>O&&(H=(f-O)/x)}C.forEach(a=>{a.style.fontVariationSettings=A,d&&et(a,d)});let _=[],R=!1;function q(){const a=window.scrollX,r=window.scrollY;_=C.map(v=>{const l=v.getBoundingClientRect();return{cx:(l.left+l.right)/2+a,cy:(l.top+l.bottom)/2+r}}),R=!0}let B=null;V&&(q(),B=new ResizeObserver(()=>{R=!1}),B.observe(t),(E=(e=document.fonts)==null?void 0:e.ready)==null||E.then(()=>{R=!1}));let $=-9999,w=-9999,P=!1,T=0,D=!0,X=null;function J(){if(!D)return;if(!P){C.forEach(l=>{S>0&&(l.style.transition=`font-variation-settings ${S}ms ease`),l.style.fontVariationSettings=A,j&&(l.style.letterSpacing=""),d&&et(l,d)}),S>0&&(X!==null&&clearTimeout(X),X=setTimeout(()=>{C.forEach(l=>{l.style.transition=""}),X=null},S)),T=0;return}V&&!R&&q();const a=V?$+window.scrollX:$,r=V?w+window.scrollY:w,v=V?null:C.map(l=>l.getBoundingClientRect());C.forEach((l,f)=>{l.style.transition="";let O,x;if(V)({cx:O,cy:x}=_[f]);else{const G=v[f];O=G.left+G.width/2,x=G.top+G.height/2}const m=Math.sqrt((a-O)**2+(r-x)**2),b=Math.max(0,1-m/y),Y=u==="quadratic"?b*b:b,I=L==="repel"?1-Y:Y,Z={};for(const G of Object.keys(M)){const[U,dt]=M[G]??[300,500];Z[G]=U+(dt-U)*I}l.style.fontVariationSettings=nt(g,Z),j&&H!==0&&(l.style.letterSpacing=`${(-H*I).toFixed(3)}px`),d&&st(l,d,Y)}),T=requestAnimationFrame(J)}function Q(a){$=a.clientX,w=a.clientY,P||(P=!0),T===0&&(T=requestAnimationFrame(J))}function i(){P=!1,T===0&&(T=requestAnimationFrame(J))}function c(a){a.touches.length!==0&&($=a.touches[0].clientX,w=a.touches[0].clientY,P||(P=!0),T===0&&(T=requestAnimationFrame(J)))}function p(){P=!1,T===0&&(T=requestAnimationFrame(J))}const o=F==="document"?document:t;return o.addEventListener("mousemove",Q),o.addEventListener("mouseleave",i),o.addEventListener("touchmove",c,{passive:!0}),o.addEventListener("touchend",p),()=>{D=!1,cancelAnimationFrame(T),X!==null&&clearTimeout(X),B&&B.disconnect(),o.removeEventListener("mousemove",Q),o.removeEventListener("mouseleave",i),o.removeEventListener("touchmove",c),o.removeEventListener("touchend",p),t.innerHTML=n}}function at(t){const n=h.useRef(null),s=h.useRef(null),M=h.useRef(t);M.current=t;const y=h.useRef(null),u=t.mode??"field",{axes:L,radius:F,falloff:d,magnetMode:S,wdthBoost:V,scope:j}=t,N=L?JSON.stringify(L):void 0,k=t.props?JSON.stringify(t.props):void 0,C=h.useCallback(()=>{const g=n.current;if(!g)return;s.current===null&&(s.current=ct(g)),y.current&&(y.current(),y.current=null),(M.current.mode??"field")==="field"?y.current=lt(g,s.current,M.current):y.current=it(g,s.current,M.current)},[u,N,F,d,S,V,j,k]);return h.useLayoutEffect(()=>(C(),()=>{y.current&&(y.current(),y.current=null)}),[C]),h.useEffect(()=>{var g,A;(A=(g=document.fonts)==null?void 0:g.ready)==null||A.then(C)},[C]),n}const ut=h.forwardRef(function({children:n,as:s="p",className:M,style:y,...u},L){const F=at(u),d=h.useCallback(S=>{F.current=S,typeof L=="function"?L(S):L&&(L.current=S)},[L]);return W.jsx(s,{ref:d,className:M,style:y,children:n})});ut.displayName="MagnetTypeText";const ft=h.forwardRef(function({children:n,as:s="p",className:M,style:y,minWeight:u=300,maxWeight:L=600,proximityRadius:F,spreadRadius:d,fixedAxes:S={},cachePositions:V=!0,rafThrottle:j=!0,stabilizeLayout:N=!0},k){const C=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(i=>{C.current=i,typeof k=="function"?k(i):k&&(k.current=i)},[k]);function w(i){const c=[`'wght' ${i.toFixed(0)}`];for(const[p,o]of Object.entries(S))c.push(`'${p}' ${o}`);return c.join(", ")}function P(){const i=C.current;if(!i)return;const c=window.scrollX,p=window.scrollY,o=i.getBoundingClientRect();_.current={top:o.top+p,left:o.left+c,right:o.right+c,bottom:o.bottom+p},H.current=A.current.map(e=>{if(!e)return{cx:0,cy:0};const E=e.getBoundingClientRect();return{cx:(E.left+E.right)/2+c,cy:(E.top+E.bottom)/2+p}}),R.current=!0}function T(i,c){const p=C.current;if(!p)return;V&&!R.current&&P();let o,e,E,a,r,v;if(V&&_.current)r=i+window.scrollX,v=c+window.scrollY,{top:o,left:e,right:E,bottom:a}=_.current;else{const x=p.getBoundingClientRect();r=i,v=c,o=x.top,e=x.left,E=x.right,a=x.bottom}const l=Math.max(e-r,0,r-E),f=Math.max(o-v,0,v-a),O=Math.sqrt(l*l+f*f);if(F!==void 0&&!d){const m=1-(1-Math.max(0,1-O/F))**2;p.style.fontVariationSettings=w(u+(L-u)*m);return}if(d){if(F!==void 0&&O>F){p.style.fontVariationSettings=w(u);for(const m of A.current)m&&(m.style.fontVariationSettings=w(u),N&&(m.style.letterSpacing=""));return}if(O>d){for(const m of A.current)m&&(m.style.fontVariationSettings=w(u),N&&(m.style.letterSpacing=""));return}const x=A.current;if(V&&H.current.length===x.length)for(let m=0;m<x.length;m++){const b=x[m];if(!b)continue;const{cx:Y,cy:I}=H.current[m],Z=Math.sqrt((r-Y)**2+(v-I)**2),U=1-(1-Math.max(0,1-Z/d))**2;b.style.fontVariationSettings=w(u+(L-u)*U),N&&B.current!==0&&(b.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}else for(const m of x){if(!m)continue;const b=m.getBoundingClientRect(),Y=(b.left+b.right)/2,I=(b.top+b.bottom)/2,Z=Math.sqrt((i-Y)**2+(c-I)**2),U=1-(1-Math.max(0,1-Z/d))**2;m.style.fontVariationSettings=w(u+(L-u)*U),N&&B.current!==0&&(m.style.letterSpacing=`${(-B.current*U).toFixed(3)}px`)}}}const D=h.useCallback(i=>{g.current={x:i.clientX,y:i.clientY},j?q.current===null&&(q.current=requestAnimationFrame(()=>{q.current=null,g.current&&T(g.current.x,g.current.y)})):T(i.clientX,i.clientY)},[u,L,F,d,V,j,N,JSON.stringify(S)]),X=h.useCallback(()=>{g.current&&T(g.current.x,g.current.y)},[u,L,F,d,V,N,JSON.stringify(S)]),J=h.useCallback(()=>{g.current=null,q.current!==null&&(cancelAnimationFrame(q.current),q.current=null);const i=C.current;i&&(i.style.fontVariationSettings=w(u));for(const c of A.current)c&&(c.style.fontVariationSettings=w(u),N&&(c.style.letterSpacing=""))},[u,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,o;if(!V||!d)return;R.current=!1;const i=C.current;if(!i)return;const c=new ResizeObserver(()=>{R.current=!1});return c.observe(i),(o=(p=document.fonts)==null?void 0:p.ready)==null||o.then(()=>{R.current=!1}),()=>c.disconnect()},[V,d]),h.useEffect(()=>{R.current=!1},[n,d]),h.useEffect(()=>{if(!N||!d){B.current=0;return}const i=C.current;if(!i)return;const c=i.textContent??"",p=c.replace(/\s/g,"").length;if(p===0)return;const o=getComputedStyle(i),e=document.createElement("span");e.style.cssText="position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;",e.style.fontFamily=o.fontFamily,e.style.fontSize=o.fontSize,e.style.fontWeight=o.fontWeight,e.style.lineHeight=o.lineHeight,e.style.letterSpacing=o.letterSpacing,e.textContent=c,document.body.appendChild(e);const E=v=>{const l=[`'wght' ${v.toFixed(0)}`];for(const[f,O]of Object.entries(S))l.push(`'${f}' ${O}`);return l.join(", ")};e.style.fontVariationSettings=E(L);const a=e.scrollWidth;e.style.fontVariationSettings=E(u);const r=e.scrollWidth;document.body.removeChild(e),B.current=a>r?(a-r)/p:0},[N,d,u,L,n,JSON.stringify(S)]);const Q=h.useMemo(()=>{if(!d)return n;A.current=[];let i=0;function c(p){if(typeof p=="string")return[...p].map(o=>{if(/\s/.test(o))return o;const e=i++;return W.jsx("span",{ref:E=>{A.current[e]=E},style:{fontVariationSettings:w(u)},children:o},e)});if(Array.isArray(p))return p.map((o,e)=>W.jsx(h.Fragment,{children:c(o)},e));if(h.isValidElement(p)){const o=p;if(o.props.children!==void 0)return h.cloneElement(o,{},c(o.props.children))}return p}return c(n)},[n,d,u,JSON.stringify(S)]);return W.jsx(s,{ref:$,className:M,style:{fontVariationSettings:w(u),...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=pt;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
- export declare const MagnetBlock: default_2.ForwardRefExoticComponent<MagnetBlockProps & default_2.RefAttributes<HTMLElement>>;
41
+ /** @deprecated Use MagnetChar instead */
42
+ export declare const MagnetBlock: default_2.ForwardRefExoticComponent<MagnetCharProps & default_2.RefAttributes<HTMLElement>>;
42
43
 
43
- export declare interface MagnetBlockProps {
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 = 'field' | 'legibility';
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: 'field'
107
+ * Operating mode. Default: 'word'
102
108
  *
103
- * - **'field'** — cursor proximity drives per-word font-variation-settings.
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: 'field' — starts the cursor proximity rAF loop via startMagnetType.
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 'field' mode if mode is undefined.
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 ft, useEffect as W, forwardRef as lt, useMemo as dt } from "react";
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,
@@ -39,481 +39,487 @@ const it = {
39
39
  scope: "document"
40
40
  };
41
41
  function ct(t, n = []) {
42
- return t.nodeType === Node.TEXT_NODE ? n.push(t) : t.childNodes.forEach((s) => ct(s, n)), n;
42
+ return t.nodeType === Node.TEXT_NODE ? n.push(t) : t.childNodes.forEach((o) => ct(o, n)), n;
43
43
  }
44
- function et(t, n, s) {
45
- if (!t || t === "normal") return `"${n}" ${s}`;
46
- const S = new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`), h = `"${n}" ${s}`;
47
- return S.test(t) ? t.replace(S, h) : `${t}, ${h}`;
44
+ function et(t, n, o) {
45
+ if (!t || t === "normal") return `"${n}" ${o}`;
46
+ const M = new RegExp(`(["'])${n}\\1\\s+[\\d.eE+-]+`), h = `"${n}" ${o}`;
47
+ return M.test(t) ? t.replace(M, h) : `${t}, ${h}`;
48
48
  }
49
49
  function st(t, n) {
50
- let s = t;
51
- for (const [S, h] of Object.entries(n))
52
- s = et(s, S, h);
53
- return s;
50
+ let o = t;
51
+ for (const [M, h] of Object.entries(n))
52
+ o = et(o, M, h);
53
+ return o;
54
54
  }
55
- function at(t, n, s) {
55
+ function at(t, n, o) {
56
56
  if (n.opacity !== void 0) {
57
- const [S, h] = n.opacity;
58
- t.style.opacity = String(S + (h - S) * s);
57
+ const [M, h] = n.opacity;
58
+ t.style.opacity = String(M + (h - M) * o);
59
59
  }
60
- n.italic === !0 && (t.style.fontStyle = s > 0.5 ? "italic" : "");
60
+ n.italic === !0 && (t.style.fontStyle = o > 0.5 ? "italic" : "");
61
61
  }
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 mt(t) {
66
- const n = t.cloneNode(!0), s = n.querySelectorAll(
65
+ function pt(t) {
66
+ const n = t.cloneNode(!0), o = n.querySelectorAll(
67
67
  `.${nt.word}, .${nt.char}`
68
68
  );
69
- return Array.from(s).reverse().forEach((h) => {
70
- const u = h.parentNode;
71
- if (u) {
72
- for (; h.firstChild; ) u.insertBefore(h.firstChild, h);
73
- u.removeChild(h);
69
+ return Array.from(o).reverse().forEach((h) => {
70
+ const d = h.parentNode;
71
+ if (d) {
72
+ for (; h.firstChild; ) d.insertBefore(h.firstChild, h);
73
+ d.removeChild(h);
74
74
  }
75
75
  }), n.innerHTML;
76
76
  }
77
77
  function Mt(t, n) {
78
78
  t.innerHTML = n;
79
79
  }
80
- function pt(t, n, s = {}) {
81
- var m, o;
80
+ function ht(t, n, o = {}) {
81
+ var p, r;
82
82
  if (typeof window > "u") return () => {
83
83
  };
84
84
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
85
85
  return t.innerHTML = n, () => {
86
86
  };
87
- const S = s.wdthBoost ?? U.wdthBoost, h = s.radius ?? U.radius, u = s.falloff ?? U.falloff, b = s.scope ?? U.scope, F = s.props, d = s.transitionMs ?? 0, w = s.cachePositions ?? !0, L = window.scrollY;
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, E = ct(t), y = [];
90
- for (const e of E) {
91
- const M = e.textContent ?? "";
92
- if (!M || !M.split("").some((g) => g in it)) continue;
93
- const r = document.createDocumentFragment();
94
- for (const g of M) {
95
- const l = it[g];
96
- if (l === void 0) {
97
- const f = r.lastChild;
98
- f && f.nodeType === Node.TEXT_NODE ? f.textContent += g : r.appendChild(document.createTextNode(g));
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
+ const E = a.textContent ?? "";
92
+ if (!E || !E.split("").some((g) => g in it)) continue;
93
+ const e = document.createDocumentFragment();
94
+ for (const g of E) {
95
+ const i = it[g];
96
+ if (i === void 0) {
97
+ const u = e.lastChild;
98
+ u && u.nodeType === Node.TEXT_NODE ? u.textContent += g : e.appendChild(document.createTextNode(g));
99
99
  } else {
100
- const f = document.createElement("span");
101
- f.className = nt.char, f.style.fontVariationSettings = et(X, "wdth", j), f.textContent = g, r.appendChild(f), y.push({ span: f, riskLevel: l });
100
+ const u = document.createElement("span");
101
+ u.className = nt.char, u.style.fontVariationSettings = et(X, "wdth", j), u.textContent = g, e.appendChild(u), y.push({ span: u, riskLevel: i });
102
102
  }
103
103
  }
104
- e.parentNode.replaceChild(r, e);
104
+ a.parentNode.replaceChild(e, a);
105
105
  }
106
106
  if (requestAnimationFrame(() => {
107
107
  typeof window < "u" && Math.abs(window.scrollY - L) > 2 && window.scrollTo({ top: L, behavior: "instant" });
108
108
  }), y.length === 0) return () => {
109
109
  };
110
- F && y.forEach(({ span: e }) => ot(e, F));
110
+ F && y.forEach(({ span: a }) => ot(a, F));
111
111
  let N = [], H = !1;
112
112
  function _() {
113
- const e = window.scrollX, M = window.scrollY;
114
- N = y.map(({ span: a }) => {
115
- const r = a.getBoundingClientRect();
116
- return { cx: (r.left + r.right) / 2 + e, cy: (r.top + r.bottom) / 2 + M };
113
+ const a = window.scrollX, E = window.scrollY;
114
+ N = y.map(({ span: c }) => {
115
+ const e = c.getBoundingClientRect();
116
+ return { cx: (e.left + e.right) / 2 + a, cy: (e.top + e.bottom) / 2 + E };
117
117
  }), H = !0;
118
118
  }
119
119
  let Y = null;
120
120
  w && (_(), Y = new ResizeObserver(() => {
121
121
  H = !1;
122
- }), Y.observe(t), (o = (m = document.fonts) == null ? void 0 : m.ready) == null || o.then(() => {
122
+ }), Y.observe(t), (r = (p = document.fonts) == null ? void 0 : p.ready) == null || r.then(() => {
123
123
  H = !1;
124
124
  }));
125
- let A = -9999, $ = -9999, B = !1, v = 0, R = !0, x = null;
125
+ let A = -9999, $ = -9999, B = !1, v = 0, R = !0, T = null;
126
126
  function D() {
127
127
  if (!R) return;
128
128
  if (!B) {
129
- y.forEach(({ span: r }) => {
130
- d > 0 && (r.style.transition = `font-variation-settings ${d}ms ease`), r.style.fontVariationSettings = et(X, "wdth", j), F && ot(r, F);
131
- }), d > 0 && (x !== null && clearTimeout(x), x = setTimeout(() => {
132
- y.forEach(({ span: r }) => {
133
- r.style.transition = "";
134
- }), x = null;
135
- }, d)), v = 0;
129
+ y.forEach(({ span: e }) => {
130
+ m > 0 && (e.style.transition = `font-variation-settings ${m}ms ease`), e.style.fontVariationSettings = et(X, "wdth", j), F && ot(e, F);
131
+ }), m > 0 && (T !== null && clearTimeout(T), T = setTimeout(() => {
132
+ y.forEach(({ span: e }) => {
133
+ e.style.transition = "";
134
+ }), T = null;
135
+ }, m)), v = 0;
136
136
  return;
137
137
  }
138
138
  w && !H && _();
139
- const e = w ? A + window.scrollX : A, M = w ? $ + window.scrollY : $, a = w ? null : y.map(({ span: r }) => r.getBoundingClientRect());
140
- y.forEach(({ span: r, riskLevel: g }, l) => {
141
- r.style.transition = "";
142
- let f, O;
139
+ const a = w ? A + window.scrollX : A, E = w ? $ + window.scrollY : $, c = w ? null : y.map(({ span: e }) => e.getBoundingClientRect());
140
+ y.forEach(({ span: e, riskLevel: g }, i) => {
141
+ e.style.transition = "";
142
+ let u, O;
143
143
  if (w)
144
- ({ cx: f, cy: O } = N[l]);
144
+ ({ cx: u, cy: O } = N[i]);
145
145
  else {
146
- const P = a[l];
147
- f = P.left + P.width / 2, O = P.top + P.height / 2;
146
+ const P = c[i];
147
+ u = P.left + P.width / 2, O = P.top + P.height / 2;
148
148
  }
149
- const C = Math.sqrt((e - f) ** 2 + (M - O) ** 2), p = Math.max(0, 1 - C / h), T = u === "quadratic" ? p * p : p, q = S * (g / 3) * T;
150
- r.style.fontVariationSettings = et(X, "wdth", j + q), F && at(r, F, T);
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
- function k(e) {
154
- A = e.clientX, $ = e.clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D));
153
+ function k(a) {
154
+ A = a.clientX, $ = a.clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D));
155
155
  }
156
156
  function J() {
157
157
  B = !1, v === 0 && (v = requestAnimationFrame(D));
158
158
  }
159
- function Q(e) {
160
- e.touches.length !== 0 && (A = e.touches[0].clientX, $ = e.touches[0].clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D)));
159
+ function Q(a) {
160
+ a.touches.length !== 0 && (A = a.touches[0].clientX, $ = a.touches[0].clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D)));
161
161
  }
162
- function i() {
162
+ function l() {
163
163
  B = !1, v === 0 && (v = requestAnimationFrame(D));
164
164
  }
165
- const c = b === "document" ? document : t;
166
- return c.addEventListener("mousemove", k), c.addEventListener("mouseleave", J), c.addEventListener("touchmove", Q, { passive: !0 }), c.addEventListener("touchend", i), () => {
167
- R = !1, cancelAnimationFrame(v), x !== null && clearTimeout(x), Y && Y.disconnect(), c.removeEventListener("mousemove", k), c.removeEventListener("mouseleave", J), c.removeEventListener("touchmove", Q), c.removeEventListener("touchend", i), t.innerHTML = n;
165
+ const s = b === "document" ? document : t;
166
+ return s.addEventListener("mousemove", k), s.addEventListener("mouseleave", J), s.addEventListener("touchmove", Q, { passive: !0 }), s.addEventListener("touchend", l), () => {
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 ht(t, n, s = {}) {
171
- var e, M;
170
+ function yt(t, n, o = {}) {
171
+ var a, E;
172
172
  if (typeof window > "u") return () => {
173
173
  };
174
174
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
175
175
  return t.innerHTML = n, () => {
176
176
  };
177
- const S = s.axes ?? U.axes, h = s.radius ?? U.radius, u = s.falloff ?? U.falloff, b = s.magnetMode ?? U.magnetMode, F = s.scope ?? U.scope, d = s.props, w = s.transitionMs ?? 0, L = s.cachePositions ?? !0, X = s.stabilizeLayout ?? !0, V = window.scrollY;
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), E = [];
180
- for (const a of j) {
181
- const r = a.textContent ?? "";
182
- if (!r.trim()) continue;
183
- const g = r.split(/(\S+)/), l = document.createDocumentFragment();
184
- for (let f = 0; f < g.length; f += 2) {
185
- const O = g[f], C = g[f + 1];
186
- if (!C) continue;
187
- const T = g[f + 3] === void 0 ? g[f + 2] ?? "" : "", q = document.createElement("span");
188
- q.className = nt.word, q.textContent = O + C + T, l.appendChild(q), E.push(q);
179
+ const j = ct(t), C = [];
180
+ for (const c of j) {
181
+ const e = c.textContent ?? "";
182
+ if (!e.trim()) continue;
183
+ const g = e.split(/(\S+)/), i = document.createDocumentFragment();
184
+ for (let u = 0; u < g.length; u += 2) {
185
+ const O = g[u], S = g[u + 1];
186
+ if (!S) continue;
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
- a.parentNode.replaceChild(l, a);
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
- }), E.length === 0) return () => {
194
+ }), C.length === 0) return () => {
195
195
  };
196
196
  const y = getComputedStyle(t).fontVariationSettings, N = st(
197
197
  y,
198
- Object.fromEntries(Object.entries(S).map(([a, [r]]) => [a, r]))
198
+ Object.fromEntries(Object.entries(M).map(([c, [e]]) => [c, e]))
199
199
  );
200
200
  let H = 0;
201
201
  if (X) {
202
- const a = st(
202
+ const c = st(
203
203
  y,
204
- Object.fromEntries(Object.entries(S).map(([p, [, T]]) => [p, T]))
205
- ), r = t.style.fontVariationSettings, g = t.style.whiteSpace, l = t.style.overflow;
206
- t.style.whiteSpace = "nowrap", t.style.overflow = "visible", t.style.fontVariationSettings = a;
207
- const f = t.scrollWidth;
204
+ Object.fromEntries(Object.entries(M).map(([f, [, x]]) => [f, x]))
205
+ ), e = t.style.fontVariationSettings, g = t.style.whiteSpace, i = t.style.overflow;
206
+ t.style.whiteSpace = "nowrap", t.style.overflow = "visible", t.style.fontVariationSettings = c;
207
+ const u = t.scrollWidth;
208
208
  t.style.fontVariationSettings = N;
209
209
  const O = t.scrollWidth;
210
- t.style.fontVariationSettings = r, t.style.whiteSpace = g, t.style.overflow = l;
211
- const C = E.reduce(
212
- (p, T) => {
210
+ t.style.fontVariationSettings = e, t.style.whiteSpace = g, t.style.overflow = i;
211
+ const S = C.reduce(
212
+ (f, x) => {
213
213
  var q;
214
- return p + (((q = T.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
214
+ return f + (((q = x.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
215
215
  },
216
216
  0
217
217
  );
218
- C > 0 && f > O && (H = (f - O) / C);
218
+ S > 0 && u > O && (H = (u - O) / S);
219
219
  }
220
- E.forEach((a) => {
221
- a.style.fontVariationSettings = N, d && ot(a, d);
220
+ C.forEach((c) => {
221
+ c.style.fontVariationSettings = N, m && ot(c, m);
222
222
  });
223
223
  let _ = [], Y = !1;
224
224
  function A() {
225
- const a = window.scrollX, r = window.scrollY;
226
- _ = E.map((g) => {
227
- const l = g.getBoundingClientRect();
228
- return { cx: (l.left + l.right) / 2 + a, cy: (l.top + l.bottom) / 2 + r };
225
+ const c = window.scrollX, e = window.scrollY;
226
+ _ = C.map((g) => {
227
+ const i = g.getBoundingClientRect();
228
+ return { cx: (i.left + i.right) / 2 + c, cy: (i.top + i.bottom) / 2 + e };
229
229
  }), Y = !0;
230
230
  }
231
231
  let $ = null;
232
232
  L && (A(), $ = new ResizeObserver(() => {
233
233
  Y = !1;
234
- }), $.observe(t), (M = (e = document.fonts) == null ? void 0 : e.ready) == null || M.then(() => {
234
+ }), $.observe(t), (E = (a = document.fonts) == null ? void 0 : a.ready) == null || E.then(() => {
235
235
  Y = !1;
236
236
  }));
237
- let B = -9999, v = -9999, R = !1, x = 0, D = !0, k = null;
237
+ let B = -9999, v = -9999, R = !1, T = 0, D = !0, k = null;
238
238
  function J() {
239
239
  if (!D) return;
240
240
  if (!R) {
241
- E.forEach((l) => {
242
- w > 0 && (l.style.transition = `font-variation-settings ${w}ms ease`), l.style.fontVariationSettings = N, X && (l.style.letterSpacing = ""), d && ot(l, d);
241
+ C.forEach((i) => {
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
- E.forEach((l) => {
245
- l.style.transition = "";
244
+ C.forEach((i) => {
245
+ i.style.transition = "";
246
246
  }), k = null;
247
- }, w)), x = 0;
247
+ }, w)), T = 0;
248
248
  return;
249
249
  }
250
250
  L && !Y && A();
251
- const a = L ? B + window.scrollX : B, r = L ? v + window.scrollY : v, g = L ? null : E.map((l) => l.getBoundingClientRect());
252
- E.forEach((l, f) => {
253
- l.style.transition = "";
254
- let O, C;
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
+ i.style.transition = "";
254
+ let O, S;
255
255
  if (L)
256
- ({ cx: O, cy: C } = _[f]);
256
+ ({ cx: O, cy: S } = _[u]);
257
257
  else {
258
- const K = g[f];
259
- O = K.left + K.width / 2, C = K.top + K.height / 2;
258
+ const K = g[u];
259
+ O = K.left + K.width / 2, S = K.top + K.height / 2;
260
260
  }
261
- const p = Math.sqrt((a - O) ** 2 + (r - C) ** 2), T = Math.max(0, 1 - p / h), q = u === "quadratic" ? T * T : T, P = b === "repel" ? 1 - q : q, Z = {};
262
- for (const K of Object.keys(S)) {
263
- const [G, ut] = S[K] ?? [300, 500];
264
- Z[K] = G + (ut - G) * P;
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
+ for (const K of Object.keys(M)) {
263
+ const [G, ft] = M[K] ?? [300, 500];
264
+ Z[K] = G + (ft - G) * P;
265
265
  }
266
- l.style.fontVariationSettings = st(y, Z), X && H !== 0 && (l.style.letterSpacing = `${(-H * P).toFixed(3)}px`), d && at(l, d, q);
267
- }), x = requestAnimationFrame(J);
266
+ i.style.fontVariationSettings = st(y, Z), X && H !== 0 && (i.style.letterSpacing = `${(-H * P).toFixed(3)}px`), m && at(i, m, q);
267
+ }), T = requestAnimationFrame(J);
268
268
  }
269
- function Q(a) {
270
- B = a.clientX, v = a.clientY, R || (R = !0), x === 0 && (x = requestAnimationFrame(J));
269
+ function Q(c) {
270
+ B = c.clientX, v = c.clientY, R || (R = !0), T === 0 && (T = requestAnimationFrame(J));
271
271
  }
272
- function i() {
273
- R = !1, x === 0 && (x = requestAnimationFrame(J));
272
+ function l() {
273
+ R = !1, T === 0 && (T = requestAnimationFrame(J));
274
274
  }
275
- function c(a) {
276
- a.touches.length !== 0 && (B = a.touches[0].clientX, v = a.touches[0].clientY, R || (R = !0), x === 0 && (x = requestAnimationFrame(J)));
275
+ function s(c) {
276
+ c.touches.length !== 0 && (B = c.touches[0].clientX, v = c.touches[0].clientY, R || (R = !0), T === 0 && (T = requestAnimationFrame(J)));
277
277
  }
278
- function m() {
279
- R = !1, x === 0 && (x = requestAnimationFrame(J));
278
+ function p() {
279
+ R = !1, T === 0 && (T = requestAnimationFrame(J));
280
280
  }
281
- const o = F === "document" ? document : t;
282
- return o.addEventListener("mousemove", Q), o.addEventListener("mouseleave", i), o.addEventListener("touchmove", c, { passive: !0 }), o.addEventListener("touchend", m), () => {
283
- D = !1, cancelAnimationFrame(x), k !== null && clearTimeout(k), $ && $.disconnect(), o.removeEventListener("mousemove", Q), o.removeEventListener("mouseleave", i), o.removeEventListener("touchmove", c), o.removeEventListener("touchend", m), t.innerHTML = n;
281
+ const r = F === "document" ? document : t;
282
+ return r.addEventListener("mousemove", Q), r.addEventListener("mouseleave", l), r.addEventListener("touchmove", s, { passive: !0 }), r.addEventListener("touchend", p), () => {
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 yt(t) {
287
- const n = I(null), s = I(null), S = I(t);
288
- S.current = t;
289
- const h = I(null), u = t.mode ?? "field", { axes: b, radius: F, falloff: d, magnetMode: w, wdthBoost: L, scope: X } = t, V = b ? JSON.stringify(b) : void 0, j = t.props ? JSON.stringify(t.props) : void 0, E = z(() => {
286
+ function gt(t) {
287
+ const n = I(null), o = I(null), M = I(t);
288
+ M.current = t;
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
- s.current === null && (s.current = mt(y)), h.current && (h.current(), h.current = null), (S.current.mode ?? "field") === "field" ? h.current = ht(y, s.current, S.current) : h.current = pt(y, s.current, S.current);
293
- }, [u, V, F, d, w, L, X, j]);
294
- return ft(() => (E(), () => {
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
+ }, [d, V, F, m, w, L, X, j]);
294
+ return dt(() => (C(), () => {
295
295
  h.current && (h.current(), h.current = null);
296
- }), [E]), W(() => {
296
+ }), [C]), W(() => {
297
297
  var y, N;
298
- (N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(E);
299
- }, [E]), n;
298
+ (N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(C);
299
+ }, [C]), n;
300
300
  }
301
- const gt = lt(
302
- function({ children: n, as: s = "p", className: S, style: h, ...u }, b) {
303
- const F = yt(u), d = z(
301
+ const vt = lt(
302
+ function({ children: n, as: o = "p", className: M, style: h, ...d }, b) {
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
  },
307
307
  // eslint-disable-next-line react-hooks/exhaustive-deps
308
308
  [b]
309
309
  );
310
- return /* @__PURE__ */ tt(s, { ref: d, className: S, style: h, children: n });
310
+ return /* @__PURE__ */ tt(o, { ref: m, className: M, style: h, children: n });
311
311
  }
312
312
  );
313
- gt.displayName = "MagnetTypeText";
314
- const vt = lt(
313
+ vt.displayName = "MagnetTypeText";
314
+ const ut = lt(
315
315
  function({
316
316
  children: n,
317
- as: s = "p",
318
- className: S,
317
+ as: o = "p",
318
+ className: M,
319
319
  style: h,
320
- minWeight: u = 300,
320
+ minWeight: d = 300,
321
321
  maxWeight: b = 600,
322
322
  proximityRadius: F,
323
- spreadRadius: d,
323
+ spreadRadius: m,
324
324
  fixedAxes: w = {},
325
325
  cachePositions: L = !0,
326
326
  rafThrottle: X = !0,
327
327
  stabilizeLayout: V = !0
328
328
  }, j) {
329
- const E = I(null), y = I(null), N = I([]), H = I([]), _ = I(null), Y = I(!1), A = I(null), $ = I(0), B = z(
330
- (i) => {
331
- E.current = i, typeof j == "function" ? j(i) : j && (j.current = i);
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
+ (l) => {
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]
335
335
  );
336
- function v(i) {
337
- const c = [`'wght' ${i.toFixed(0)}`];
338
- for (const [m, o] of Object.entries(w)) c.push(`'${m}' ${o}`);
339
- return c.join(", ");
336
+ function v(l) {
337
+ const s = [`'wght' ${l.toFixed(0)}`];
338
+ for (const [p, r] of Object.entries(w)) s.push(`'${p}' ${r}`);
339
+ return s.join(", ");
340
340
  }
341
341
  function R() {
342
- const i = E.current;
343
- if (!i) return;
344
- const c = window.scrollX, m = window.scrollY, o = i.getBoundingClientRect();
342
+ const l = C.current;
343
+ if (!l) return;
344
+ const s = window.scrollX, p = window.scrollY, r = l.getBoundingClientRect();
345
345
  _.current = {
346
- top: o.top + m,
347
- left: o.left + c,
348
- right: o.right + c,
349
- bottom: o.bottom + m
350
- }, H.current = N.current.map((e) => {
351
- if (!e) return { cx: 0, cy: 0 };
352
- const M = e.getBoundingClientRect();
346
+ top: r.top + p,
347
+ left: r.left + s,
348
+ right: r.right + s,
349
+ bottom: r.bottom + p
350
+ }, H.current = N.current.map((a) => {
351
+ if (!a) return { cx: 0, cy: 0 };
352
+ const E = a.getBoundingClientRect();
353
353
  return {
354
- cx: (M.left + M.right) / 2 + c,
355
- cy: (M.top + M.bottom) / 2 + m
354
+ cx: (E.left + E.right) / 2 + s,
355
+ cy: (E.top + E.bottom) / 2 + p
356
356
  };
357
357
  }), Y.current = !0;
358
358
  }
359
- function x(i, c) {
360
- const m = E.current;
361
- if (!m) return;
359
+ function T(l, s) {
360
+ const p = C.current;
361
+ if (!p) return;
362
362
  L && !Y.current && R();
363
- let o, e, M, a, r, g;
363
+ let r, a, E, c, e, g;
364
364
  if (L && _.current)
365
- r = i + window.scrollX, g = c + window.scrollY, { top: o, left: e, right: M, bottom: a } = _.current;
365
+ e = l + window.scrollX, g = s + window.scrollY, { top: r, left: a, right: E, bottom: c } = _.current;
366
366
  else {
367
- const C = m.getBoundingClientRect();
368
- r = i, g = c, o = C.top, e = C.left, M = C.right, a = C.bottom;
367
+ const S = p.getBoundingClientRect();
368
+ e = l, g = s, r = S.top, a = S.left, E = S.right, c = S.bottom;
369
369
  }
370
- const l = Math.max(e - r, 0, r - M), f = Math.max(o - g, 0, g - a), O = Math.sqrt(l * l + f * f);
371
- if (F !== void 0 && !d) {
372
- const p = 1 - (1 - Math.max(0, 1 - O / F)) ** 2;
373
- m.style.fontVariationSettings = v(u + (b - u) * p);
370
+ const i = Math.max(a - e, 0, e - E), u = Math.max(r - g, 0, g - c), O = Math.sqrt(i * i + u * u);
371
+ if (F !== void 0 && !m) {
372
+ const f = 1 - (1 - Math.max(0, 1 - O / F)) ** 2;
373
+ p.style.fontVariationSettings = v(d + (b - d) * f);
374
374
  return;
375
375
  }
376
- if (d) {
376
+ if (m) {
377
377
  if (F !== void 0 && O > F) {
378
- m.style.fontVariationSettings = v(u);
379
- for (const p of N.current)
380
- p && (p.style.fontVariationSettings = v(u), V && (p.style.letterSpacing = ""));
378
+ p.style.fontVariationSettings = v(d);
379
+ for (const f of N.current)
380
+ f && (f.style.fontVariationSettings = v(d), V && (f.style.letterSpacing = ""));
381
381
  return;
382
382
  }
383
- if (O > d) {
384
- for (const p of N.current)
385
- p && (p.style.fontVariationSettings = v(u), V && (p.style.letterSpacing = ""));
383
+ if (O > m) {
384
+ for (const f of N.current)
385
+ f && (f.style.fontVariationSettings = v(d), V && (f.style.letterSpacing = ""));
386
386
  return;
387
387
  }
388
- const C = N.current;
389
- if (L && H.current.length === C.length)
390
- for (let p = 0; p < C.length; p++) {
391
- const T = C[p];
392
- if (!T) continue;
393
- const { cx: q, cy: P } = H.current[p], Z = Math.sqrt((r - q) ** 2 + (g - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / d)) ** 2;
394
- T.style.fontVariationSettings = v(u + (b - u) * G), V && $.current !== 0 && (T.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
388
+ const S = N.current;
389
+ if (L && H.current.length === S.length)
390
+ for (let f = 0; f < S.length; f++) {
391
+ const x = S[f];
392
+ if (!x) continue;
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
+ 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
- for (const p of C) {
398
- if (!p) continue;
399
- const T = p.getBoundingClientRect(), q = (T.left + T.right) / 2, P = (T.top + T.bottom) / 2, Z = Math.sqrt((i - q) ** 2 + (c - P) ** 2), G = 1 - (1 - Math.max(0, 1 - Z / d)) ** 2;
400
- p.style.fontVariationSettings = v(u + (b - u) * G), V && $.current !== 0 && (p.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
397
+ for (const f of S) {
398
+ if (!f) continue;
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
+ f.style.fontVariationSettings = v(d + (b - d) * G), V && $.current !== 0 && (f.style.letterSpacing = `${(-$.current * G).toFixed(3)}px`);
401
401
  }
402
402
  }
403
403
  }
404
404
  const D = z(
405
- (i) => {
406
- y.current = { x: i.clientX, y: i.clientY }, X ? A.current === null && (A.current = requestAnimationFrame(() => {
407
- A.current = null, y.current && x(y.current.x, y.current.y);
408
- })) : x(i.clientX, i.clientY);
405
+ (l) => {
406
+ y.current = { x: l.clientX, y: l.clientY }, X ? A.current === null && (A.current = requestAnimationFrame(() => {
407
+ A.current = null, y.current && T(y.current.x, y.current.y);
408
+ })) : T(l.clientX, l.clientY);
409
409
  },
410
410
  // eslint-disable-next-line react-hooks/exhaustive-deps
411
- [u, b, F, d, L, X, V, JSON.stringify(w)]
411
+ [d, b, F, m, L, X, V, JSON.stringify(w)]
412
412
  ), k = z(
413
413
  () => {
414
- y.current && x(y.current.x, y.current.y);
414
+ y.current && T(y.current.x, y.current.y);
415
415
  },
416
416
  // eslint-disable-next-line react-hooks/exhaustive-deps
417
- [u, b, F, d, L, V, JSON.stringify(w)]
417
+ [d, b, F, m, L, V, JSON.stringify(w)]
418
418
  ), J = z(
419
419
  () => {
420
420
  y.current = null, A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
421
- const i = E.current;
422
- i && (i.style.fontVariationSettings = v(u));
423
- for (const c of N.current)
424
- c && (c.style.fontVariationSettings = v(u), V && (c.style.letterSpacing = ""));
421
+ const l = C.current;
422
+ l && (l.style.fontVariationSettings = v(d));
423
+ for (const s of N.current)
424
+ s && (s.style.fontVariationSettings = v(d), V && (s.style.letterSpacing = ""));
425
425
  },
426
426
  // eslint-disable-next-line react-hooks/exhaustive-deps
427
- [u, V, JSON.stringify(w)]
427
+ [d, V, JSON.stringify(w)]
428
428
  );
429
429
  W(() => (window.addEventListener("mousemove", D, { passive: !0 }), window.addEventListener("scroll", k, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", J), () => {
430
430
  window.removeEventListener("mousemove", D), window.removeEventListener("scroll", k, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", J), A.current !== null && (cancelAnimationFrame(A.current), A.current = null);
431
431
  }), [D, k, J]), W(() => {
432
- var m, o;
433
- if (!L || !d) return;
432
+ var p, r;
433
+ if (!L || !m) return;
434
434
  Y.current = !1;
435
- const i = E.current;
436
- if (!i) return;
437
- const c = new ResizeObserver(() => {
435
+ const l = C.current;
436
+ if (!l) return;
437
+ const s = new ResizeObserver(() => {
438
438
  Y.current = !1;
439
439
  });
440
- return c.observe(i), (o = (m = document.fonts) == null ? void 0 : m.ready) == null || o.then(() => {
440
+ return s.observe(l), (r = (p = document.fonts) == null ? void 0 : p.ready) == null || r.then(() => {
441
441
  Y.current = !1;
442
- }), () => c.disconnect();
443
- }, [L, d]), W(() => {
442
+ }), () => s.disconnect();
443
+ }, [L, m]), W(() => {
444
444
  Y.current = !1;
445
- }, [n, d]), W(() => {
446
- if (!V || !d) {
445
+ }, [n, m]), W(() => {
446
+ var s, p;
447
+ if (!V || !m) {
447
448
  $.current = 0;
448
449
  return;
449
450
  }
450
- const i = E.current;
451
- if (!i) return;
452
- const c = i.textContent ?? "", m = c.replace(/\s/g, "").length;
453
- if (m === 0) return;
454
- const o = getComputedStyle(i), e = document.createElement("span");
455
- e.style.cssText = "position:fixed;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;", e.style.fontFamily = o.fontFamily, e.style.fontSize = o.fontSize, e.style.fontWeight = o.fontWeight, e.style.lineHeight = o.lineHeight, e.style.letterSpacing = o.letterSpacing, e.textContent = c, document.body.appendChild(e);
456
- const M = (g) => {
457
- const l = [`'wght' ${g.toFixed(0)}`];
458
- for (const [f, O] of Object.entries(w)) l.push(`'${f}' ${O}`);
459
- return l.join(", ");
460
- };
461
- e.style.fontVariationSettings = M(b);
462
- const a = e.scrollWidth;
463
- e.style.fontVariationSettings = M(u);
464
- const r = e.scrollWidth;
465
- document.body.removeChild(e), $.current = a > r ? (a - r) / m : 0;
466
- }, [V, d, u, b, n, JSON.stringify(w)]);
467
- const Q = dt(() => {
468
- if (!d) return n;
451
+ function l() {
452
+ const r = C.current;
453
+ if (!r) return;
454
+ const a = r.textContent ?? "", E = a.replace(/\s/g, "").length;
455
+ if (E === 0) return;
456
+ const c = getComputedStyle(r), e = document.createElement("span");
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
+ const g = (O) => {
459
+ const S = [`'wght' ${O.toFixed(0)}`];
460
+ for (const [f, x] of Object.entries(w)) S.push(`'${f}' ${x}`);
461
+ return S.join(", ");
462
+ };
463
+ e.style.fontVariationSettings = g(b);
464
+ const i = e.scrollWidth;
465
+ e.style.fontVariationSettings = g(d);
466
+ const u = e.scrollWidth;
467
+ document.body.removeChild(e), $.current = i > u ? (i - u) / E : 0;
468
+ }
469
+ l(), (p = (s = document.fonts) == null ? void 0 : s.ready) == null || p.then(l);
470
+ }, [V, m, d, b, n, JSON.stringify(w)]);
471
+ const Q = mt(() => {
472
+ if (!m) return n;
469
473
  N.current = [];
470
- let i = 0;
471
- function c(m) {
472
- if (typeof m == "string")
473
- return [...m].map((o) => {
474
- if (/\s/.test(o)) return o;
475
- const e = i++;
474
+ let l = 0;
475
+ function s(p) {
476
+ if (typeof p == "string")
477
+ return [...p].map((r) => {
478
+ if (/\s/.test(r)) return r;
479
+ const a = l++;
476
480
  return /* @__PURE__ */ tt(
477
481
  "span",
478
482
  {
479
- ref: (M) => {
480
- N.current[e] = M;
483
+ ref: (E) => {
484
+ N.current[a] = E;
481
485
  },
482
- style: { fontVariationSettings: v(u) },
483
- children: o
486
+ style: { fontVariationSettings: v(d) },
487
+ children: r
484
488
  },
485
- e
489
+ a
486
490
  );
487
491
  });
488
- if (Array.isArray(m)) return m.map((o, e) => /* @__PURE__ */ tt(rt.Fragment, { children: c(o) }, e));
489
- if (rt.isValidElement(m)) {
490
- const o = m;
491
- if (o.props.children !== void 0)
492
- return rt.cloneElement(o, {}, c(o.props.children));
492
+ if (Array.isArray(p)) return p.map((r, a) => /* @__PURE__ */ tt(rt.Fragment, { children: s(r) }, a));
493
+ if (rt.isValidElement(p)) {
494
+ const r = p;
495
+ if (r.props.children !== void 0)
496
+ return rt.cloneElement(r, {}, s(r.props.children));
493
497
  }
494
- return m;
498
+ return p;
495
499
  }
496
- return c(n);
497
- }, [n, d, u, JSON.stringify(w)]);
500
+ return s(n);
501
+ }, [n, m, d, JSON.stringify(w)]);
498
502
  return /* @__PURE__ */ tt(
499
- s,
503
+ o,
500
504
  {
501
505
  ref: B,
502
- className: S,
503
- style: { fontVariationSettings: v(u), ...h },
506
+ className: M,
507
+ style: { fontVariationSettings: v(d), ...h },
504
508
  children: Q
505
509
  }
506
510
  );
507
511
  }
508
512
  );
509
- vt.displayName = "MagnetBlock";
513
+ ut.displayName = "MagnetChar";
514
+ const Et = ut;
510
515
  export {
511
516
  nt as MAGNET_TYPE_CLASSES,
512
- vt as MagnetBlock,
513
- gt as MagnetTypeText,
514
- pt as applyMagnetType,
515
- mt as getCleanHTML,
517
+ Et as MagnetBlock,
518
+ ut as MagnetChar,
519
+ vt as MagnetTypeText,
520
+ ht as applyMagnetType,
521
+ pt as getCleanHTML,
516
522
  Mt as removeMagnetType,
517
- ht as startMagnetType,
518
- yt as useMagnetType
523
+ yt as startMagnetType,
524
+ gt as useMagnetType
519
525
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/magnettype",
3
- "version": "1.1.5",
3
+ "version": "1.2.0",
4
4
  "description": "Cursor-field per-word variable font axis variation and per-character legibility mode",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",