@liiift-studio/magnettype 1.1.4 → 1.1.5

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
@@ -86,6 +86,9 @@ import { MagnetBlock } from '@liiift-studio/magnettype'
86
86
  | `spreadRadius` | — | Pixel distance from the cursor within which each **character's** weight rises to `maxWeight`. When omitted, per-character splitting is skipped |
87
87
  | `proximityRadius` | — | Pixel distance from the element **edge** that gates the effect. Without `spreadRadius`, drives a whole-element weight ramp. With `spreadRadius`, acts as an outer gate — the spread only fires while the cursor is within this distance |
88
88
  | `fixedAxes` | `{}` | Additional axis values to hold constant in every `font-variation-settings` string (e.g. `{ opsz: 144 }`) |
89
+ | `stabilizeLayout` | `true` | Apply compensating letter-spacing to prevent text reflow as weight rises. Measures the element's width at rest and peak weight via an off-screen probe and applies proportional negative letter-spacing per character. Disable if you want natural bold spacing, or if your font expands glyphs very unevenly (compensation is a per-element average) |
90
+ | `cachePositions` | `true` | Cache character centre positions in page-relative coordinates, eliminating `getBoundingClientRect` calls on every `mousemove`. Rebuilt on resize and after fonts load. Disable if the element is inside a custom scroll container (`overflow: scroll` on a non-window element) |
91
+ | `rafThrottle` | `true` | Throttle proximity updates to one per animation frame (≈ 60 fps on most displays). Disable for lowest input latency on 120 Hz displays or very fast-moving effects |
89
92
  | `className` | — | Forwarded to the root element |
90
93
  | `style` | — | Merged with the root element's style; `fontVariationSettings` at `minWeight` is set as the base |
91
94
 
@@ -244,4 +247,4 @@ The package itself has zero runtime dependencies. Do not remove this entry.
244
247
 
245
248
  ---
246
249
 
247
- Current version: 1.1.3
250
+ Current version: 1.1.5
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react"),G=require("react/jsx-runtime"),tt={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},D={word:"mt-word",char:"mt-char",probe:"mt-probe"},j={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function W(e,r=[]){return e.nodeType===Node.TEXT_NODE?r.push(e):e.childNodes.forEach(o=>W(o,r)),r}function K(e,r,o){if(!e||e==="normal")return`"${r}" ${o}`;const y=new RegExp(`(["'])${r}\\1\\s+[\\d.eE+-]+`),a=`"${r}" ${o}`;return y.test(e)?e.replace(y,a):`${e}, ${a}`}function et(e,r){let o=e;for(const[y,a]of Object.entries(r))o=K(o,y,a);return o}function nt(e,r,o){if(r.opacity!==void 0){const[y,a]=r.opacity;e.style.opacity=String(y+(a-y)*o)}r.italic===!0&&(e.style.fontStyle=o>.5?"italic":"")}function U(e,r){r.opacity!==void 0&&(e.style.opacity=String(r.opacity[0])),r.italic===!0&&(e.style.fontStyle="")}function rt(e){const r=e.cloneNode(!0),o=r.querySelectorAll(`.${D.word}, .${D.char}`);return Array.from(o).reverse().forEach(a=>{const l=a.parentNode;if(l){for(;a.firstChild;)l.insertBefore(a.firstChild,a);l.removeChild(a)}}),r.innerHTML}function lt(e,r){e.innerHTML=r}function ot(e,r,o={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=r,()=>{};const y=o.wdthBoost??j.wdthBoost,a=o.radius??j.radius,l=o.falloff??j.falloff,x=o.scope??j.scope,E=o.props,f=o.transitionMs??0,w=window.scrollY;e.innerHTML=r;const L=getComputedStyle(e).fontVariationSettings,Y=L.match(/"wdth"\s+([\d.eE+-]+)/),T=Y?parseFloat(Y[1]):100,F=W(e),p=[];for(const s of F){const t=s.textContent??"";if(!t||!t.split("").some(n=>n in tt))continue;const c=document.createDocumentFragment();for(const n of t){const M=tt[n];if(M===void 0){const m=c.lastChild;m&&m.nodeType===Node.TEXT_NODE?m.textContent+=n:c.appendChild(document.createTextNode(n))}else{const m=document.createElement("span");m.className=D.char,m.style.fontVariationSettings=K(L,"wdth",T),m.textContent=n,c.appendChild(m),p.push({span:m,riskLevel:M})}}s.parentNode.replaceChild(c,s)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-w)>2&&window.scrollTo({top:w,behavior:"instant"})}),p.length===0)return()=>{};E&&p.forEach(({span:s})=>U(s,E));let g=-9999,V=-9999,S=!1,u=0,b=!0,N=null;function h(){if(!b)return;if(!S){p.forEach(({span:t})=>{f>0&&(t.style.transition=`font-variation-settings ${f}ms ease`),t.style.fontVariationSettings=K(L,"wdth",T),E&&U(t,E)}),f>0&&(N!==null&&clearTimeout(N),N=setTimeout(()=>{p.forEach(({span:t})=>{t.style.transition=""}),N=null},f)),u=0;return}const s=p.map(({span:t})=>t.getBoundingClientRect());p.forEach(({span:t,riskLevel:i},c)=>{t.style.transition="";const n=s[c],M=n.left+n.width/2,m=n.top+n.height/2,B=Math.sqrt((g-M)**2+(V-m)**2),A=Math.max(0,1-B/a),q=l==="quadratic"?A*A:A,H=y*(i/3)*q;t.style.fontVariationSettings=K(L,"wdth",T+H),E&&nt(t,E,q)}),u=requestAnimationFrame(h)}function X(s){g=s.clientX,V=s.clientY,S||(S=!0),u===0&&(u=requestAnimationFrame(h))}function R(){S=!1,u===0&&(u=requestAnimationFrame(h))}function k(s){s.touches.length!==0&&(g=s.touches[0].clientX,V=s.touches[0].clientY,S||(S=!0),u===0&&(u=requestAnimationFrame(h)))}function $(){S=!1,u===0&&(u=requestAnimationFrame(h))}const v=x==="document"?document:e;return v.addEventListener("mousemove",X),v.addEventListener("mouseleave",R),v.addEventListener("touchmove",k,{passive:!0}),v.addEventListener("touchend",$),()=>{b=!1,cancelAnimationFrame(u),N!==null&&clearTimeout(N),v.removeEventListener("mousemove",X),v.removeEventListener("mouseleave",R),v.removeEventListener("touchmove",k),v.removeEventListener("touchend",$),e.innerHTML=r}}function st(e,r,o={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=r,()=>{};const y=o.axes??j.axes,a=o.radius??j.radius,l=o.falloff??j.falloff,x=o.magnetMode??j.magnetMode,E=o.scope??j.scope,f=o.props,w=o.transitionMs??0,L=window.scrollY;e.innerHTML=r;const Y=W(e),T=[];for(const s of Y){const t=s.textContent??"";if(!t.trim())continue;const i=t.split(/(\S+)/),c=document.createDocumentFragment();for(let n=0;n<i.length;n+=2){const M=i[n],m=i[n+1];if(!m)continue;const A=i[n+3]===void 0?i[n+2]??"":"",q=document.createElement("span");q.className=D.word,q.textContent=M+m+A,c.appendChild(q),T.push(q)}s.parentNode.replaceChild(c,s)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-L)>2&&window.scrollTo({top:L,behavior:"instant"})}),T.length===0)return()=>{};const F=getComputedStyle(e).fontVariationSettings,p=et(F,Object.fromEntries(Object.entries(y).map(([s,[t]])=>[s,t])));T.forEach(s=>{s.style.fontVariationSettings=p,f&&U(s,f)});let g=-9999,V=-9999,S=!1,u=0,b=!0,N=null;function h(){if(!b)return;if(!S){T.forEach(t=>{w>0&&(t.style.transition=`font-variation-settings ${w}ms ease`),t.style.fontVariationSettings=p,f&&U(t,f)}),w>0&&(N!==null&&clearTimeout(N),N=setTimeout(()=>{T.forEach(t=>{t.style.transition=""}),N=null},w)),u=0;return}const s=T.map(t=>t.getBoundingClientRect());T.forEach((t,i)=>{t.style.transition="";const c=s[i],n=c.left+c.width/2,M=c.top+c.height/2,m=Math.sqrt((g-n)**2+(V-M)**2),B=Math.max(0,1-m/a),A=l==="quadratic"?B*B:B,q=x==="repel"?1-A:A,H={};for(const _ of Object.keys(y)){const[J,O]=y[_]??[300,500];H[_]=J+(O-J)*q}t.style.fontVariationSettings=et(F,H),f&&nt(t,f,A)}),u=requestAnimationFrame(h)}function X(s){g=s.clientX,V=s.clientY,S||(S=!0),u===0&&(u=requestAnimationFrame(h))}function R(){S=!1,u===0&&(u=requestAnimationFrame(h))}function k(s){s.touches.length!==0&&(g=s.touches[0].clientX,V=s.touches[0].clientY,S||(S=!0),u===0&&(u=requestAnimationFrame(h)))}function $(){S=!1,u===0&&(u=requestAnimationFrame(h))}const v=E==="document"?document:e;return v.addEventListener("mousemove",X),v.addEventListener("mouseleave",R),v.addEventListener("touchmove",k,{passive:!0}),v.addEventListener("touchend",$),()=>{b=!1,cancelAnimationFrame(u),N!==null&&clearTimeout(N),v.removeEventListener("mousemove",X),v.removeEventListener("mouseleave",R),v.removeEventListener("touchmove",k),v.removeEventListener("touchend",$),e.innerHTML=r}}function ct(e){const r=d.useRef(null),o=d.useRef(null),y=d.useRef(e);y.current=e;const a=d.useRef(null),l=e.mode??"field",{axes:x,radius:E,falloff:f,magnetMode:w,wdthBoost:L,scope:Y}=e,T=x?JSON.stringify(x):void 0,F=e.props?JSON.stringify(e.props):void 0,p=d.useCallback(()=>{const g=r.current;if(!g)return;o.current===null&&(o.current=rt(g)),a.current&&(a.current(),a.current=null),(y.current.mode??"field")==="field"?a.current=st(g,o.current,y.current):a.current=ot(g,o.current,y.current)},[l,T,E,f,w,L,Y,F]);return d.useLayoutEffect(()=>(p(),()=>{a.current&&(a.current(),a.current=null)}),[p]),d.useEffect(()=>{document.fonts.ready.then(p)},[p]),r}const it=d.forwardRef(function({children:r,as:o="p",className:y,style:a,...l},x){const E=ct(l),f=d.useCallback(w=>{E.current=w,typeof x=="function"?x(w):x&&(x.current=w)},[x]);return G.jsx(o,{ref:f,className:y,style:a,children:r})});it.displayName="MagnetTypeText";const ut=d.forwardRef(function({children:r,as:o="p",className:y,style:a,minWeight:l=300,maxWeight:x=600,proximityRadius:E,spreadRadius:f,fixedAxes:w={},cachePositions:L=!0,rafThrottle:Y=!0},T){const F=d.useRef(null),p=d.useRef(null),g=d.useRef([]),V=d.useRef([]),S=d.useRef(null),u=d.useRef(!1),b=d.useRef(null),N=d.useCallback(t=>{F.current=t,typeof T=="function"?T(t):T&&(T.current=t)},[T]);function h(t){const i=[`'wght' ${t.toFixed(0)}`];for(const[c,n]of Object.entries(w))i.push(`'${c}' ${n}`);return i.join(", ")}function X(){const t=F.current;if(!t)return;const i=window.scrollX,c=window.scrollY,n=t.getBoundingClientRect();S.current={top:n.top+c,left:n.left+i,right:n.right+i,bottom:n.bottom+c},V.current=g.current.map(M=>{if(!M)return{cx:0,cy:0};const m=M.getBoundingClientRect();return{cx:(m.left+m.right)/2+i,cy:(m.top+m.bottom)/2+c}}),u.current=!0}function R(t,i){const c=F.current;if(!c)return;L&&!u.current&&X();let n,M,m,B,A,q;if(L&&S.current)A=t+window.scrollX,q=i+window.scrollY,{top:n,left:M,right:m,bottom:B}=S.current;else{const O=c.getBoundingClientRect();A=t,q=i,n=O.top,M=O.left,m=O.right,B=O.bottom}const H=Math.max(M-A,0,A-m),_=Math.max(n-q,0,q-B),J=Math.sqrt(H*H+_*_);if(E!==void 0&&!f){const C=1-(1-Math.max(0,1-J/E))**2;c.style.fontVariationSettings=h(l+(x-l)*C);return}if(f){if(E!==void 0&&J>E){c.style.fontVariationSettings=h(l);for(const C of g.current)C&&(C.style.fontVariationSettings=h(l));return}if(J>f){for(const C of g.current)C&&(C.style.fontVariationSettings=h(l));return}const O=g.current;if(L&&V.current.length===O.length)for(let C=0;C<O.length;C++){const I=O[C];if(!I)continue;const{cx:z,cy:P}=V.current[C],Q=Math.sqrt((A-z)**2+(q-P)**2),Z=1-(1-Math.max(0,1-Q/f))**2;I.style.fontVariationSettings=h(l+(x-l)*Z)}else for(const C of O){if(!C)continue;const I=C.getBoundingClientRect(),z=(I.left+I.right)/2,P=(I.top+I.bottom)/2,Q=Math.sqrt((t-z)**2+(i-P)**2),Z=1-(1-Math.max(0,1-Q/f))**2;C.style.fontVariationSettings=h(l+(x-l)*Z)}}}const k=d.useCallback(t=>{p.current={x:t.clientX,y:t.clientY},Y?b.current===null&&(b.current=requestAnimationFrame(()=>{b.current=null,p.current&&R(p.current.x,p.current.y)})):R(t.clientX,t.clientY)},[l,x,E,f,L,Y,JSON.stringify(w)]),$=d.useCallback(()=>{p.current&&R(p.current.x,p.current.y)},[l,x,E,f,L,JSON.stringify(w)]),v=d.useCallback(()=>{p.current=null,b.current!==null&&(cancelAnimationFrame(b.current),b.current=null);const t=F.current;t&&(t.style.fontVariationSettings=h(l));for(const i of g.current)i&&(i.style.fontVariationSettings=h(l))},[l,JSON.stringify(w)]);d.useEffect(()=>(window.addEventListener("mousemove",k,{passive:!0}),window.addEventListener("scroll",$,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",v),()=>{window.removeEventListener("mousemove",k),window.removeEventListener("scroll",$,{capture:!0}),document.documentElement.removeEventListener("mouseleave",v),b.current!==null&&(cancelAnimationFrame(b.current),b.current=null)}),[k,$,v]),d.useEffect(()=>{if(!L||!f)return;u.current=!1;const t=F.current;if(!t)return;const i=new ResizeObserver(()=>{u.current=!1});return i.observe(t),document.fonts.ready.then(()=>{u.current=!1}),()=>i.disconnect()},[L,f]),d.useEffect(()=>{u.current=!1},[r,f]);const s=d.useMemo(()=>{if(!f)return r;g.current=[];let t=0;function i(c){if(typeof c=="string")return[...c].map(n=>{if(/\s/.test(n))return n;const M=t++;return G.jsx("span",{ref:m=>{g.current[M]=m},style:{fontVariationSettings:h(l)},children:n},M)});if(Array.isArray(c))return c.map((n,M)=>G.jsx(d.Fragment,{children:i(n)},M));if(d.isValidElement(c)){const n=c;if(n.props.children!==void 0)return d.cloneElement(n,{},i(n.props.children))}return c}return i(r)},[r,f,l,JSON.stringify(w)]);return G.jsx(o,{ref:N,className:y,style:{fontVariationSettings:h(l),...a},children:s})});ut.displayName="MagnetBlock";exports.MAGNET_TYPE_CLASSES=D;exports.MagnetBlock=ut;exports.MagnetTypeText=it;exports.applyMagnetType=ot;exports.getCleanHTML=rt;exports.removeMagnetType=lt;exports.startMagnetType=st;exports.useMagnetType=ct;
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;
package/dist/index.d.ts CHANGED
@@ -69,6 +69,18 @@ export declare interface MagnetBlockProps {
69
69
  * @default true
70
70
  */
71
71
  rafThrottle?: boolean;
72
+ /**
73
+ * Apply compensating letter-spacing to keep line lengths stable as font weight changes.
74
+ * Measures the element's text width at rest and peak weight via an off-screen probe span,
75
+ * then applies proportional negative letter-spacing per character as weight rises,
76
+ * cancelling the advance-width increase that would otherwise cause text to reflow.
77
+ *
78
+ * Disable if you prefer natural bold letter-spacing, or if the font expands characters
79
+ * very unevenly across the weight axis (the compensation is a per-element average and
80
+ * may not perfectly cancel highly variable per-character expansion).
81
+ * @default true
82
+ */
83
+ stabilizeLayout?: boolean;
72
84
  }
73
85
 
74
86
  /** Whether cursor proximity attracts toward peak or repels toward rest */
@@ -137,6 +149,28 @@ export declare interface MagnetTypeOptions {
137
149
  * Risk 3 characters receive wdthBoost × (3/3) = full boost at peak.
138
150
  */
139
151
  wdthBoost?: number;
152
+ /**
153
+ * Cache word/character centre positions in page-relative coordinates after setup,
154
+ * eliminating getBoundingClientRect calls during the active interaction loop.
155
+ * The cache is rebuilt on resize and after fonts load. Disable if the element lives
156
+ * inside a custom scroll container (overflow: scroll on a non-window element) —
157
+ * page coordinates are computed using window.scrollX/Y and will be incorrect when
158
+ * a nested element is the scroll parent.
159
+ * @default true
160
+ */
161
+ cachePositions?: boolean;
162
+ /**
163
+ * Apply compensating letter-spacing to keep line lengths stable as font weight
164
+ * changes. Measures the element's text width at rest and peak weight, then applies
165
+ * proportional negative letter-spacing per word/character as weight rises, cancelling
166
+ * the advance-width increase that would otherwise cause text to reflow.
167
+ *
168
+ * Disable if you prefer natural bold letter-spacing, or if the font expands characters
169
+ * very unevenly across the weight axis (the compensation is a per-element average and
170
+ * may not perfectly cancel highly variable per-character expansion).
171
+ * @default true
172
+ */
173
+ stabilizeLayout?: boolean;
140
174
  /**
141
175
  * Duration in milliseconds for the CSS transition back to rest values when the
142
176
  * cursor leaves (mouseleave / touchend). Default: 0 (instant snap, no transition).
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import et, { useRef as O, useCallback as R, useLayoutEffect as ut, useEffect as K, forwardRef as ct, useMemo as at } from "react";
3
- import { jsx as U } from "react/jsx-runtime";
4
- const rt = {
2
+ import rt, { useRef as I, useCallback as z, useLayoutEffect as ft, useEffect as W, forwardRef as lt, useMemo as dt } from "react";
3
+ import { jsx as tt } from "react/jsx-runtime";
4
+ const it = {
5
5
  i: 3,
6
6
  l: 3,
7
7
  1: 3,
@@ -23,14 +23,14 @@ const rt = {
23
23
  c: 1,
24
24
  e: 1
25
25
  // similar bowls
26
- }, G = {
26
+ }, nt = {
27
27
  /** Applied to each word span in field mode */
28
28
  word: "mt-word",
29
29
  /** Applied to each character span in legibility mode */
30
30
  char: "mt-char",
31
31
  /** Applied to measurement probe spans (never in final output) */
32
32
  probe: "mt-probe"
33
- }, j = {
33
+ }, U = {
34
34
  axes: { wght: [300, 500] },
35
35
  radius: 120,
36
36
  falloff: "quadratic",
@@ -38,389 +38,482 @@ const rt = {
38
38
  wdthBoost: 6,
39
39
  scope: "document"
40
40
  };
41
- function nt(e, r = []) {
42
- return e.nodeType === Node.TEXT_NODE ? r.push(e) : e.childNodes.forEach((o) => nt(o, r)), r;
41
+ function ct(t, n = []) {
42
+ return t.nodeType === Node.TEXT_NODE ? n.push(t) : t.childNodes.forEach((s) => ct(s, n)), n;
43
43
  }
44
- function z(e, r, o) {
45
- if (!e || e === "normal") return `"${r}" ${o}`;
46
- const p = new RegExp(`(["'])${r}\\1\\s+[\\d.eE+-]+`), a = `"${r}" ${o}`;
47
- return p.test(e) ? e.replace(p, a) : `${e}, ${a}`;
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}`;
48
48
  }
49
- function ot(e, r) {
50
- let o = e;
51
- for (const [p, a] of Object.entries(r))
52
- o = z(o, p, a);
53
- return o;
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;
54
54
  }
55
- function st(e, r, o) {
56
- if (r.opacity !== void 0) {
57
- const [p, a] = r.opacity;
58
- e.style.opacity = String(p + (a - p) * o);
55
+ function at(t, n, s) {
56
+ if (n.opacity !== void 0) {
57
+ const [S, h] = n.opacity;
58
+ t.style.opacity = String(S + (h - S) * s);
59
59
  }
60
- r.italic === !0 && (e.style.fontStyle = o > 0.5 ? "italic" : "");
60
+ n.italic === !0 && (t.style.fontStyle = s > 0.5 ? "italic" : "");
61
61
  }
62
- function Q(e, r) {
63
- r.opacity !== void 0 && (e.style.opacity = String(r.opacity[0])), r.italic === !0 && (e.style.fontStyle = "");
62
+ function ot(t, n) {
63
+ n.opacity !== void 0 && (t.style.opacity = String(n.opacity[0])), n.italic === !0 && (t.style.fontStyle = "");
64
64
  }
65
- function lt(e) {
66
- const r = e.cloneNode(!0), o = r.querySelectorAll(
67
- `.${G.word}, .${G.char}`
65
+ function mt(t) {
66
+ const n = t.cloneNode(!0), s = n.querySelectorAll(
67
+ `.${nt.word}, .${nt.char}`
68
68
  );
69
- return Array.from(o).reverse().forEach((a) => {
70
- const l = a.parentNode;
71
- if (l) {
72
- for (; a.firstChild; ) l.insertBefore(a.firstChild, a);
73
- l.removeChild(a);
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);
74
74
  }
75
- }), r.innerHTML;
75
+ }), n.innerHTML;
76
76
  }
77
- function gt(e, r) {
78
- e.innerHTML = r;
77
+ function Mt(t, n) {
78
+ t.innerHTML = n;
79
79
  }
80
- function ft(e, r, o = {}) {
80
+ function pt(t, n, s = {}) {
81
+ var m, o;
81
82
  if (typeof window > "u") return () => {
82
83
  };
83
84
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
84
- return e.innerHTML = r, () => {
85
+ return t.innerHTML = n, () => {
85
86
  };
86
- const p = o.wdthBoost ?? j.wdthBoost, a = o.radius ?? j.radius, l = o.falloff ?? j.falloff, S = o.scope ?? j.scope, w = o.props, f = o.transitionMs ?? 0, M = window.scrollY;
87
- e.innerHTML = r;
88
- const L = getComputedStyle(e).fontVariationSettings, Y = L.match(/"wdth"\s+([\d.eE+-]+)/), E = Y ? parseFloat(Y[1]) : 100, q = nt(e), m = [];
89
- for (const c of q) {
90
- const t = c.textContent ?? "";
91
- if (!t || !t.split("").some((n) => n in rt)) continue;
92
- const s = document.createDocumentFragment();
93
- for (const n of t) {
94
- const g = rt[n];
95
- if (g === void 0) {
96
- const d = s.lastChild;
97
- d && d.nodeType === Node.TEXT_NODE ? d.textContent += n : s.appendChild(document.createTextNode(n));
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;
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));
98
99
  } else {
99
- const d = document.createElement("span");
100
- d.className = G.char, d.style.fontVariationSettings = z(L, "wdth", E), d.textContent = n, s.appendChild(d), m.push({ span: d, riskLevel: g });
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 });
101
102
  }
102
103
  }
103
- c.parentNode.replaceChild(s, c);
104
+ e.parentNode.replaceChild(r, e);
104
105
  }
105
106
  if (requestAnimationFrame(() => {
106
- typeof window < "u" && Math.abs(window.scrollY - M) > 2 && window.scrollTo({ top: M, behavior: "instant" });
107
- }), m.length === 0) return () => {
107
+ typeof window < "u" && Math.abs(window.scrollY - L) > 2 && window.scrollTo({ top: L, behavior: "instant" });
108
+ }), y.length === 0) return () => {
108
109
  };
109
- w && m.forEach(({ span: c }) => Q(c, w));
110
- let y = -9999, F = -9999, T = !1, u = 0, C = !0, N = null;
111
- function h() {
112
- if (!C) return;
113
- if (!T) {
114
- m.forEach(({ span: t }) => {
115
- f > 0 && (t.style.transition = `font-variation-settings ${f}ms ease`), t.style.fontVariationSettings = z(L, "wdth", E), w && Q(t, w);
116
- }), f > 0 && (N !== null && clearTimeout(N), N = setTimeout(() => {
117
- m.forEach(({ span: t }) => {
118
- t.style.transition = "";
119
- }), N = null;
120
- }, f)), u = 0;
110
+ F && y.forEach(({ span: e }) => ot(e, F));
111
+ let N = [], H = !1;
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 };
117
+ }), H = !0;
118
+ }
119
+ let Y = null;
120
+ w && (_(), Y = new ResizeObserver(() => {
121
+ H = !1;
122
+ }), Y.observe(t), (o = (m = document.fonts) == null ? void 0 : m.ready) == null || o.then(() => {
123
+ H = !1;
124
+ }));
125
+ let A = -9999, $ = -9999, B = !1, v = 0, R = !0, x = null;
126
+ function D() {
127
+ if (!R) return;
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;
121
136
  return;
122
137
  }
123
- const c = m.map(({ span: t }) => t.getBoundingClientRect());
124
- m.forEach(({ span: t, riskLevel: i }, s) => {
125
- t.style.transition = "";
126
- const n = c[s], g = n.left + n.width / 2, d = n.top + n.height / 2, B = Math.sqrt((y - g) ** 2 + (F - d) ** 2), b = Math.max(0, 1 - B / a), A = l === "quadratic" ? b * b : b, I = p * (i / 3) * A;
127
- t.style.fontVariationSettings = z(L, "wdth", E + I), w && st(t, w, A);
128
- }), u = requestAnimationFrame(h);
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;
143
+ if (w)
144
+ ({ cx: f, cy: O } = N[l]);
145
+ else {
146
+ const P = a[l];
147
+ f = P.left + P.width / 2, O = P.top + P.height / 2;
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);
151
+ }), v = requestAnimationFrame(D);
129
152
  }
130
- function H(c) {
131
- y = c.clientX, F = c.clientY, T || (T = !0), u === 0 && (u = requestAnimationFrame(h));
153
+ function k(e) {
154
+ A = e.clientX, $ = e.clientY, B || (B = !0), v === 0 && (v = requestAnimationFrame(D));
132
155
  }
133
- function $() {
134
- T = !1, u === 0 && (u = requestAnimationFrame(h));
156
+ function J() {
157
+ B = !1, v === 0 && (v = requestAnimationFrame(D));
135
158
  }
136
- function X(c) {
137
- c.touches.length !== 0 && (y = c.touches[0].clientX, F = c.touches[0].clientY, T || (T = !0), u === 0 && (u = requestAnimationFrame(h)));
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)));
138
161
  }
139
- function k() {
140
- T = !1, u === 0 && (u = requestAnimationFrame(h));
162
+ function i() {
163
+ B = !1, v === 0 && (v = requestAnimationFrame(D));
141
164
  }
142
- const v = S === "document" ? document : e;
143
- return v.addEventListener("mousemove", H), v.addEventListener("mouseleave", $), v.addEventListener("touchmove", X, { passive: !0 }), v.addEventListener("touchend", k), () => {
144
- C = !1, cancelAnimationFrame(u), N !== null && clearTimeout(N), v.removeEventListener("mousemove", H), v.removeEventListener("mouseleave", $), v.removeEventListener("touchmove", X), v.removeEventListener("touchend", k), e.innerHTML = r;
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;
145
168
  };
146
169
  }
147
- function dt(e, r, o = {}) {
170
+ function ht(t, n, s = {}) {
171
+ var e, M;
148
172
  if (typeof window > "u") return () => {
149
173
  };
150
174
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
151
- return e.innerHTML = r, () => {
175
+ return t.innerHTML = n, () => {
152
176
  };
153
- const p = o.axes ?? j.axes, a = o.radius ?? j.radius, l = o.falloff ?? j.falloff, S = o.magnetMode ?? j.magnetMode, w = o.scope ?? j.scope, f = o.props, M = o.transitionMs ?? 0, L = window.scrollY;
154
- e.innerHTML = r;
155
- const Y = nt(e), E = [];
156
- for (const c of Y) {
157
- const t = c.textContent ?? "";
158
- if (!t.trim()) continue;
159
- const i = t.split(/(\S+)/), s = document.createDocumentFragment();
160
- for (let n = 0; n < i.length; n += 2) {
161
- const g = i[n], d = i[n + 1];
162
- if (!d) continue;
163
- const b = i[n + 3] === void 0 ? i[n + 2] ?? "" : "", A = document.createElement("span");
164
- A.className = G.word, A.textContent = g + d + b, s.appendChild(A), E.push(A);
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;
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);
165
189
  }
166
- c.parentNode.replaceChild(s, c);
190
+ a.parentNode.replaceChild(l, a);
167
191
  }
168
192
  if (requestAnimationFrame(() => {
169
- typeof window < "u" && Math.abs(window.scrollY - L) > 2 && window.scrollTo({ top: L, behavior: "instant" });
193
+ typeof window < "u" && Math.abs(window.scrollY - V) > 2 && window.scrollTo({ top: V, behavior: "instant" });
170
194
  }), E.length === 0) return () => {
171
195
  };
172
- const q = getComputedStyle(e).fontVariationSettings, m = ot(
173
- q,
174
- Object.fromEntries(Object.entries(p).map(([c, [t]]) => [c, t]))
196
+ const y = getComputedStyle(t).fontVariationSettings, N = st(
197
+ y,
198
+ Object.fromEntries(Object.entries(S).map(([a, [r]]) => [a, r]))
175
199
  );
176
- E.forEach((c) => {
177
- c.style.fontVariationSettings = m, f && Q(c, f);
200
+ let H = 0;
201
+ if (X) {
202
+ const a = st(
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;
208
+ t.style.fontVariationSettings = N;
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) => {
213
+ var q;
214
+ return p + (((q = T.textContent) == null ? void 0 : q.replace(/\s+/g, "").length) ?? 0);
215
+ },
216
+ 0
217
+ );
218
+ C > 0 && f > O && (H = (f - O) / C);
219
+ }
220
+ E.forEach((a) => {
221
+ a.style.fontVariationSettings = N, d && ot(a, d);
178
222
  });
179
- let y = -9999, F = -9999, T = !1, u = 0, C = !0, N = null;
180
- function h() {
181
- if (!C) return;
182
- if (!T) {
183
- E.forEach((t) => {
184
- M > 0 && (t.style.transition = `font-variation-settings ${M}ms ease`), t.style.fontVariationSettings = m, f && Q(t, f);
185
- }), M > 0 && (N !== null && clearTimeout(N), N = setTimeout(() => {
186
- E.forEach((t) => {
187
- t.style.transition = "";
188
- }), N = null;
189
- }, M)), u = 0;
223
+ let _ = [], Y = !1;
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 };
229
+ }), Y = !0;
230
+ }
231
+ let $ = null;
232
+ L && (A(), $ = new ResizeObserver(() => {
233
+ Y = !1;
234
+ }), $.observe(t), (M = (e = document.fonts) == null ? void 0 : e.ready) == null || M.then(() => {
235
+ Y = !1;
236
+ }));
237
+ let B = -9999, v = -9999, R = !1, x = 0, D = !0, k = null;
238
+ function J() {
239
+ if (!D) return;
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);
243
+ }), w > 0 && (k !== null && clearTimeout(k), k = setTimeout(() => {
244
+ E.forEach((l) => {
245
+ l.style.transition = "";
246
+ }), k = null;
247
+ }, w)), x = 0;
190
248
  return;
191
249
  }
192
- const c = E.map((t) => t.getBoundingClientRect());
193
- E.forEach((t, i) => {
194
- t.style.transition = "";
195
- const s = c[i], n = s.left + s.width / 2, g = s.top + s.height / 2, d = Math.sqrt((y - n) ** 2 + (F - g) ** 2), B = Math.max(0, 1 - d / a), b = l === "quadratic" ? B * B : B, A = S === "repel" ? 1 - b : b, I = {};
196
- for (const _ of Object.keys(p)) {
197
- const [D, V] = p[_] ?? [300, 500];
198
- I[_] = D + (V - D) * A;
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;
255
+ if (L)
256
+ ({ cx: O, cy: C } = _[f]);
257
+ else {
258
+ const K = g[f];
259
+ O = K.left + K.width / 2, C = K.top + K.height / 2;
199
260
  }
200
- t.style.fontVariationSettings = ot(q, I), f && st(t, f, b);
201
- }), u = requestAnimationFrame(h);
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;
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);
202
268
  }
203
- function H(c) {
204
- y = c.clientX, F = c.clientY, T || (T = !0), u === 0 && (u = requestAnimationFrame(h));
269
+ function Q(a) {
270
+ B = a.clientX, v = a.clientY, R || (R = !0), x === 0 && (x = requestAnimationFrame(J));
205
271
  }
206
- function $() {
207
- T = !1, u === 0 && (u = requestAnimationFrame(h));
272
+ function i() {
273
+ R = !1, x === 0 && (x = requestAnimationFrame(J));
208
274
  }
209
- function X(c) {
210
- c.touches.length !== 0 && (y = c.touches[0].clientX, F = c.touches[0].clientY, T || (T = !0), u === 0 && (u = requestAnimationFrame(h)));
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)));
211
277
  }
212
- function k() {
213
- T = !1, u === 0 && (u = requestAnimationFrame(h));
278
+ function m() {
279
+ R = !1, x === 0 && (x = requestAnimationFrame(J));
214
280
  }
215
- const v = w === "document" ? document : e;
216
- return v.addEventListener("mousemove", H), v.addEventListener("mouseleave", $), v.addEventListener("touchmove", X, { passive: !0 }), v.addEventListener("touchend", k), () => {
217
- C = !1, cancelAnimationFrame(u), N !== null && clearTimeout(N), v.removeEventListener("mousemove", H), v.removeEventListener("mouseleave", $), v.removeEventListener("touchmove", X), v.removeEventListener("touchend", k), e.innerHTML = r;
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;
218
284
  };
219
285
  }
220
- function mt(e) {
221
- const r = O(null), o = O(null), p = O(e);
222
- p.current = e;
223
- const a = O(null), l = e.mode ?? "field", { axes: S, radius: w, falloff: f, magnetMode: M, wdthBoost: L, scope: Y } = e, E = S ? JSON.stringify(S) : void 0, q = e.props ? JSON.stringify(e.props) : void 0, m = R(() => {
224
- const y = r.current;
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(() => {
290
+ const y = n.current;
225
291
  if (!y) return;
226
- o.current === null && (o.current = lt(y)), a.current && (a.current(), a.current = null), (p.current.mode ?? "field") === "field" ? a.current = dt(y, o.current, p.current) : a.current = ft(y, o.current, p.current);
227
- }, [l, E, w, f, M, L, Y, q]);
228
- return ut(() => (m(), () => {
229
- a.current && (a.current(), a.current = null);
230
- }), [m]), K(() => {
231
- document.fonts.ready.then(m);
232
- }, [m]), r;
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(), () => {
295
+ h.current && (h.current(), h.current = null);
296
+ }), [E]), W(() => {
297
+ var y, N;
298
+ (N = (y = document.fonts) == null ? void 0 : y.ready) == null || N.then(E);
299
+ }, [E]), n;
233
300
  }
234
- const pt = ct(
235
- function({ children: r, as: o = "p", className: p, style: a, ...l }, S) {
236
- const w = mt(l), f = R(
237
- (M) => {
238
- w.current = M, typeof S == "function" ? S(M) : S && (S.current = M);
301
+ const gt = lt(
302
+ function({ children: n, as: s = "p", className: S, style: h, ...u }, b) {
303
+ const F = yt(u), d = z(
304
+ (w) => {
305
+ F.current = w, typeof b == "function" ? b(w) : b && (b.current = w);
239
306
  },
240
307
  // eslint-disable-next-line react-hooks/exhaustive-deps
241
- [S]
308
+ [b]
242
309
  );
243
- return /* @__PURE__ */ U(o, { ref: f, className: p, style: a, children: r });
310
+ return /* @__PURE__ */ tt(s, { ref: d, className: S, style: h, children: n });
244
311
  }
245
312
  );
246
- pt.displayName = "MagnetTypeText";
247
- const ht = ct(
313
+ gt.displayName = "MagnetTypeText";
314
+ const vt = lt(
248
315
  function({
249
- children: r,
250
- as: o = "p",
251
- className: p,
252
- style: a,
253
- minWeight: l = 300,
254
- maxWeight: S = 600,
255
- proximityRadius: w,
256
- spreadRadius: f,
257
- fixedAxes: M = {},
316
+ children: n,
317
+ as: s = "p",
318
+ className: S,
319
+ style: h,
320
+ minWeight: u = 300,
321
+ maxWeight: b = 600,
322
+ proximityRadius: F,
323
+ spreadRadius: d,
324
+ fixedAxes: w = {},
258
325
  cachePositions: L = !0,
259
- rafThrottle: Y = !0
260
- }, E) {
261
- const q = O(null), m = O(null), y = O([]), F = O([]), T = O(null), u = O(!1), C = O(null), N = R(
262
- (t) => {
263
- q.current = t, typeof E == "function" ? E(t) : E && (E.current = t);
326
+ rafThrottle: X = !0,
327
+ stabilizeLayout: V = !0
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);
264
332
  },
265
333
  // eslint-disable-next-line react-hooks/exhaustive-deps
266
- [E]
334
+ [j]
267
335
  );
268
- function h(t) {
269
- const i = [`'wght' ${t.toFixed(0)}`];
270
- for (const [s, n] of Object.entries(M)) i.push(`'${s}' ${n}`);
271
- return i.join(", ");
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(", ");
272
340
  }
273
- function H() {
274
- const t = q.current;
275
- if (!t) return;
276
- const i = window.scrollX, s = window.scrollY, n = t.getBoundingClientRect();
277
- T.current = {
278
- top: n.top + s,
279
- left: n.left + i,
280
- right: n.right + i,
281
- bottom: n.bottom + s
282
- }, F.current = y.current.map((g) => {
283
- if (!g) return { cx: 0, cy: 0 };
284
- const d = g.getBoundingClientRect();
341
+ function R() {
342
+ const i = E.current;
343
+ if (!i) return;
344
+ const c = window.scrollX, m = window.scrollY, o = i.getBoundingClientRect();
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();
285
353
  return {
286
- cx: (d.left + d.right) / 2 + i,
287
- cy: (d.top + d.bottom) / 2 + s
354
+ cx: (M.left + M.right) / 2 + c,
355
+ cy: (M.top + M.bottom) / 2 + m
288
356
  };
289
- }), u.current = !0;
357
+ }), Y.current = !0;
290
358
  }
291
- function $(t, i) {
292
- const s = q.current;
293
- if (!s) return;
294
- L && !u.current && H();
295
- let n, g, d, B, b, A;
296
- if (L && T.current)
297
- b = t + window.scrollX, A = i + window.scrollY, { top: n, left: g, right: d, bottom: B } = T.current;
359
+ function x(i, c) {
360
+ const m = E.current;
361
+ if (!m) return;
362
+ L && !Y.current && R();
363
+ let o, e, M, a, r, g;
364
+ if (L && _.current)
365
+ r = i + window.scrollX, g = c + window.scrollY, { top: o, left: e, right: M, bottom: a } = _.current;
298
366
  else {
299
- const V = s.getBoundingClientRect();
300
- b = t, A = i, n = V.top, g = V.left, d = V.right, B = V.bottom;
367
+ const C = m.getBoundingClientRect();
368
+ r = i, g = c, o = C.top, e = C.left, M = C.right, a = C.bottom;
301
369
  }
302
- const I = Math.max(g - b, 0, b - d), _ = Math.max(n - A, 0, A - B), D = Math.sqrt(I * I + _ * _);
303
- if (w !== void 0 && !f) {
304
- const x = 1 - (1 - Math.max(0, 1 - D / w)) ** 2;
305
- s.style.fontVariationSettings = h(l + (S - l) * x);
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);
306
374
  return;
307
375
  }
308
- if (f) {
309
- if (w !== void 0 && D > w) {
310
- s.style.fontVariationSettings = h(l);
311
- for (const x of y.current) x && (x.style.fontVariationSettings = h(l));
376
+ if (d) {
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 = ""));
312
381
  return;
313
382
  }
314
- if (D > f) {
315
- for (const x of y.current) x && (x.style.fontVariationSettings = h(l));
383
+ if (O > d) {
384
+ for (const p of N.current)
385
+ p && (p.style.fontVariationSettings = v(u), V && (p.style.letterSpacing = ""));
316
386
  return;
317
387
  }
318
- const V = y.current;
319
- if (L && F.current.length === V.length)
320
- for (let x = 0; x < V.length; x++) {
321
- const J = V[x];
322
- if (!J) continue;
323
- const { cx: Z, cy: P } = F.current[x], W = Math.sqrt((b - Z) ** 2 + (A - P) ** 2), tt = 1 - (1 - Math.max(0, 1 - W / f)) ** 2;
324
- J.style.fontVariationSettings = h(l + (S - l) * tt);
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`);
325
395
  }
326
396
  else
327
- for (const x of V) {
328
- if (!x) continue;
329
- const J = x.getBoundingClientRect(), Z = (J.left + J.right) / 2, P = (J.top + J.bottom) / 2, W = Math.sqrt((t - Z) ** 2 + (i - P) ** 2), tt = 1 - (1 - Math.max(0, 1 - W / f)) ** 2;
330
- x.style.fontVariationSettings = h(l + (S - l) * tt);
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`);
331
401
  }
332
402
  }
333
403
  }
334
- const X = R(
335
- (t) => {
336
- m.current = { x: t.clientX, y: t.clientY }, Y ? C.current === null && (C.current = requestAnimationFrame(() => {
337
- C.current = null, m.current && $(m.current.x, m.current.y);
338
- })) : $(t.clientX, t.clientY);
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);
339
409
  },
340
410
  // eslint-disable-next-line react-hooks/exhaustive-deps
341
- [l, S, w, f, L, Y, JSON.stringify(M)]
342
- ), k = R(
411
+ [u, b, F, d, L, X, V, JSON.stringify(w)]
412
+ ), k = z(
343
413
  () => {
344
- m.current && $(m.current.x, m.current.y);
414
+ y.current && x(y.current.x, y.current.y);
345
415
  },
346
416
  // eslint-disable-next-line react-hooks/exhaustive-deps
347
- [l, S, w, f, L, JSON.stringify(M)]
348
- ), v = R(
417
+ [u, b, F, d, L, V, JSON.stringify(w)]
418
+ ), J = z(
349
419
  () => {
350
- m.current = null, C.current !== null && (cancelAnimationFrame(C.current), C.current = null);
351
- const t = q.current;
352
- t && (t.style.fontVariationSettings = h(l));
353
- for (const i of y.current) i && (i.style.fontVariationSettings = h(l));
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 = ""));
354
425
  },
355
426
  // eslint-disable-next-line react-hooks/exhaustive-deps
356
- [l, JSON.stringify(M)]
427
+ [u, V, JSON.stringify(w)]
357
428
  );
358
- K(() => (window.addEventListener("mousemove", X, { passive: !0 }), window.addEventListener("scroll", k, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", v), () => {
359
- window.removeEventListener("mousemove", X), window.removeEventListener("scroll", k, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", v), C.current !== null && (cancelAnimationFrame(C.current), C.current = null);
360
- }), [X, k, v]), K(() => {
361
- if (!L || !f) return;
362
- u.current = !1;
363
- const t = q.current;
364
- if (!t) return;
365
- const i = new ResizeObserver(() => {
366
- u.current = !1;
429
+ W(() => (window.addEventListener("mousemove", D, { passive: !0 }), window.addEventListener("scroll", k, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", J), () => {
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
+ }), [D, k, J]), W(() => {
432
+ var m, o;
433
+ if (!L || !d) return;
434
+ Y.current = !1;
435
+ const i = E.current;
436
+ if (!i) return;
437
+ const c = new ResizeObserver(() => {
438
+ Y.current = !1;
367
439
  });
368
- return i.observe(t), document.fonts.ready.then(() => {
369
- u.current = !1;
370
- }), () => i.disconnect();
371
- }, [L, f]), K(() => {
372
- u.current = !1;
373
- }, [r, f]);
374
- const c = at(() => {
375
- if (!f) return r;
376
- y.current = [];
377
- let t = 0;
378
- function i(s) {
379
- if (typeof s == "string")
380
- return [...s].map((n) => {
381
- if (/\s/.test(n)) return n;
382
- const g = t++;
383
- return /* @__PURE__ */ U(
440
+ return c.observe(i), (o = (m = document.fonts) == null ? void 0 : m.ready) == null || o.then(() => {
441
+ Y.current = !1;
442
+ }), () => c.disconnect();
443
+ }, [L, d]), W(() => {
444
+ Y.current = !1;
445
+ }, [n, d]), W(() => {
446
+ if (!V || !d) {
447
+ $.current = 0;
448
+ return;
449
+ }
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;
469
+ 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++;
476
+ return /* @__PURE__ */ tt(
384
477
  "span",
385
478
  {
386
- ref: (d) => {
387
- y.current[g] = d;
479
+ ref: (M) => {
480
+ N.current[e] = M;
388
481
  },
389
- style: { fontVariationSettings: h(l) },
390
- children: n
482
+ style: { fontVariationSettings: v(u) },
483
+ children: o
391
484
  },
392
- g
485
+ e
393
486
  );
394
487
  });
395
- if (Array.isArray(s)) return s.map((n, g) => /* @__PURE__ */ U(et.Fragment, { children: i(n) }, g));
396
- if (et.isValidElement(s)) {
397
- const n = s;
398
- if (n.props.children !== void 0)
399
- return et.cloneElement(n, {}, i(n.props.children));
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));
400
493
  }
401
- return s;
494
+ return m;
402
495
  }
403
- return i(r);
404
- }, [r, f, l, JSON.stringify(M)]);
405
- return /* @__PURE__ */ U(
406
- o,
496
+ return c(n);
497
+ }, [n, d, u, JSON.stringify(w)]);
498
+ return /* @__PURE__ */ tt(
499
+ s,
407
500
  {
408
- ref: N,
409
- className: p,
410
- style: { fontVariationSettings: h(l), ...a },
411
- children: c
501
+ ref: B,
502
+ className: S,
503
+ style: { fontVariationSettings: v(u), ...h },
504
+ children: Q
412
505
  }
413
506
  );
414
507
  }
415
508
  );
416
- ht.displayName = "MagnetBlock";
509
+ vt.displayName = "MagnetBlock";
417
510
  export {
418
- G as MAGNET_TYPE_CLASSES,
419
- ht as MagnetBlock,
420
- pt as MagnetTypeText,
421
- ft as applyMagnetType,
422
- lt as getCleanHTML,
423
- gt as removeMagnetType,
424
- dt as startMagnetType,
425
- mt as useMagnetType
511
+ nt as MAGNET_TYPE_CLASSES,
512
+ vt as MagnetBlock,
513
+ gt as MagnetTypeText,
514
+ pt as applyMagnetType,
515
+ mt as getCleanHTML,
516
+ Mt as removeMagnetType,
517
+ ht as startMagnetType,
518
+ yt as useMagnetType
426
519
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/magnettype",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
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",