@liiift-studio/magnettype 1.0.8 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,6 +140,7 @@ const legibilityOpts: MagnetTypeOptions = {
140
140
  | `falloff` | `'quadratic'` | *(field mode only)* Falloff curve. `'linear'` — strength decreases linearly with distance. `'quadratic'` — strength decreases as distance², giving a tighter hot zone and a sharper peak feel |
141
141
  | `magnetMode` | `'attract'` | *(field mode only)* `'attract'` — words near the cursor approach `peakValue`. `'repel'` — words near the cursor stay at `restValue`; words farther away approach `peakValue` |
142
142
  | `wdthBoost` | `6` | *(legibility mode only)* `wdth` axis units added to confusable characters, scaled by risk level. Risk 3 characters (`i l 1 I`) receive the full boost; risk 2 characters (`r 0 O`) receive ⅔; risk 1 characters (`n m o b d p q c e`) receive ⅓ |
143
+ | `transitionMs` | `0` | Duration in milliseconds for the CSS transition back to rest values when the cursor leaves (or a touch ends). `0` preserves the existing instant snap. When > 0, `font-variation-settings` animates back over the given duration using `ease` easing. The transition is cleared immediately on the next `mousemove`/`touchmove` so live tracking is not delayed |
143
144
  | `as` | `'p'` | HTML element to render, e.g. `'h1'`, `'div'`, `'span'`. Accepts any valid React element type. *(React component only)* |
144
145
 
145
146
  ---
@@ -187,12 +188,10 @@ The package itself has zero runtime dependencies. Do not remove this entry.
187
188
 
188
189
  ## Future improvements
189
190
 
190
- - **Touch support** — `touchmove` events for mobile field mode; currently `mousemove`-only, so the effect is desktop-exclusive
191
- - **Smooth transition on stop** — animate axis values back to `restValue` over a short duration when the cursor leaves, rather than snapping on the next rAF tick
192
191
  - **Custom confusable table** — allow callers to pass their own `Record<string, number>` to override or extend the built-in character risk map for language- or font-specific tuning
193
192
  - **Axis clamping** — optional per-axis min/max clamp to prevent axis values from exceeding a font's supported range, avoiding undefined browser rendering behaviour
194
193
  - **SSR hydration** — pre-render legibility mode markup on the server so boosted characters are present from first paint without a client-side flash
195
194
 
196
195
  ---
197
196
 
198
- Current version: v1.0.7
197
+ Current version: 0.1.3
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const C=require("react"),z=require("react/jsx-runtime"),_={i:3,l:3,1:3,I:3,r:2,n:1,m:1,0:2,O:2,o:1,b:1,d:1,p:1,q:1,c:1,e:1},B={word:"mt-word",char:"mt-char",probe:"mt-probe"},L={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function k(e,t=[]){return e.nodeType===Node.TEXT_NODE?t.push(e):e.childNodes.forEach(n=>k(n,t)),t}function j(e,t,n){if(!e||e==="normal")return`"${t}" ${n}`;const c=new RegExp(`(["'])${t}\\1\\s+[\\d.eE+-]+`),o=`"${t}" ${n}`;return c.test(e)?e.replace(c,o):`${e}, ${o}`}function D(e,t){let n=e;for(const[c,o]of Object.entries(t))n=j(n,c,o);return n}function I(e,t,n){if(t.opacity!==void 0){const[c,o]=t.opacity;e.style.opacity=String(c+(o-c)*n)}t.italic===!0&&(e.style.fontStyle=n>.5?"italic":"")}function V(e,t){t.opacity!==void 0&&(e.style.opacity=String(t.opacity[0])),t.italic===!0&&(e.style.fontStyle="")}function P(e){const t=e.cloneNode(!0),n=t.querySelectorAll(`.${B.word}, .${B.char}`);return Array.from(n).reverse().forEach(o=>{const y=o.parentNode;if(y){for(;o.firstChild;)y.insertBefore(o.firstChild,o);y.removeChild(o)}}),t.innerHTML}function Q(e,t){e.innerHTML=t}function W(e,t,n={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=t,()=>{};const c=n.wdthBoost??L.wdthBoost,o=n.radius??L.radius,y=n.falloff??L.falloff,m=n.scope??L.scope,p=n.props,d=window.scrollY;e.innerHTML=t;const l=getComputedStyle(e).fontVariationSettings,b=l.match(/"wdth"\s+([\d.eE+-]+)/),h=b?parseFloat(b[1]):100,q=k(e),g=[];for(const r of q){const s=r.textContent??"";if(!s||!s.split("").some(i=>i in _))continue;const u=document.createDocumentFragment();for(const i of s){const N=_[i];if(N===void 0){const a=u.lastChild;a&&a.nodeType===Node.TEXT_NODE?a.textContent+=i:u.appendChild(document.createTextNode(i))}else{const a=document.createElement("span");a.className=B.char,a.style.fontVariationSettings=j(l,"wdth",h),a.textContent=i,u.appendChild(a),g.push({span:a,riskLevel:N})}}r.parentNode.replaceChild(u,r)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-d)>2&&window.scrollTo({top:d,behavior:"instant"})}),g.length===0)return()=>{};p&&g.forEach(({span:r})=>V(r,p));let w=-9999,v=-9999,M=!1,f=0,R=!0;function A(){if(!R)return;if(!M){g.forEach(({span:s})=>{s.style.fontVariationSettings=j(l,"wdth",h),p&&V(s,p)}),f=0;return}const r=g.map(({span:s})=>s.getBoundingClientRect());g.forEach(({span:s,riskLevel:T},u)=>{const i=r[u],N=i.left+i.width/2,a=i.top+i.height/2,F=Math.sqrt((w-N)**2+(v-a)**2),x=Math.max(0,1-F/o),S=y==="quadratic"?x*x:x,$=c*(T/3)*S;s.style.fontVariationSettings=j(l,"wdth",h+$),p&&I(s,p,S)}),f=requestAnimationFrame(A)}function O(r){w=r.clientX,v=r.clientY,M||(M=!0),f===0&&(f=requestAnimationFrame(A))}function Y(){M=!1,f===0&&(f=requestAnimationFrame(A))}const E=m==="document"?document:e;return E.addEventListener("mousemove",O),E.addEventListener("mouseleave",Y),()=>{R=!1,cancelAnimationFrame(f),E.removeEventListener("mousemove",O),E.removeEventListener("mouseleave",Y),e.innerHTML=t}}function G(e,t,n={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return e.innerHTML=t,()=>{};const c=n.axes??L.axes,o=n.radius??L.radius,y=n.falloff??L.falloff,m=n.magnetMode??L.magnetMode,p=n.scope??L.scope,d=n.props,l=window.scrollY;e.innerHTML=t;const b=k(e),h=[];for(const r of b){const s=r.textContent??"";if(!s.trim())continue;const T=s.split(/(\S+)/),u=document.createDocumentFragment();for(let i=0;i<T.length;i+=2){const N=T[i],a=T[i+1];if(!a)continue;const x=T[i+3]===void 0?T[i+2]??"":"",S=document.createElement("span");S.className=B.word,S.textContent=N+a+x,u.appendChild(S),h.push(S)}r.parentNode.replaceChild(u,r)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-l)>2&&window.scrollTo({top:l,behavior:"instant"})}),h.length===0)return()=>{};const q=getComputedStyle(e).fontVariationSettings,g=D(q,Object.fromEntries(Object.entries(c).map(([r,[s]])=>[r,s])));h.forEach(r=>{r.style.fontVariationSettings=g,d&&V(r,d)});let w=-9999,v=-9999,M=!1,f=0,R=!0;function A(){if(!R)return;if(!M){h.forEach(s=>{s.style.fontVariationSettings=g,d&&V(s,d)}),f=0;return}const r=h.map(s=>s.getBoundingClientRect());h.forEach((s,T)=>{const u=r[T],i=u.left+u.width/2,N=u.top+u.height/2,a=Math.sqrt((w-i)**2+(v-N)**2),F=Math.max(0,1-a/o),x=y==="quadratic"?F*F:F,S=m==="repel"?1-x:x,$={};for(const H of Object.keys(c)){const[X,U]=c[H]??[300,500];$[H]=X+(U-X)*S}s.style.fontVariationSettings=D(q,$),d&&I(s,d,x)}),f=requestAnimationFrame(A)}function O(r){w=r.clientX,v=r.clientY,M||(M=!0),f===0&&(f=requestAnimationFrame(A))}function Y(){M=!1,f===0&&(f=requestAnimationFrame(A))}const E=p==="document"?document:e;return E.addEventListener("mousemove",O),E.addEventListener("mouseleave",Y),()=>{R=!1,cancelAnimationFrame(f),E.removeEventListener("mousemove",O),E.removeEventListener("mouseleave",Y),e.innerHTML=t}}function J(e){const t=C.useRef(null),n=C.useRef(null),c=C.useRef(e);c.current=e;const o=C.useRef(null),y=e.mode??"field",{axes:m,radius:p,falloff:d,magnetMode:l,wdthBoost:b,scope:h}=e,q=m?JSON.stringify(m):void 0,g=e.props?JSON.stringify(e.props):void 0,w=C.useCallback(()=>{const v=t.current;if(!v)return;n.current===null&&(n.current=P(v)),o.current&&(o.current(),o.current=null),(c.current.mode??"field")==="field"?o.current=G(v,n.current,c.current):o.current=W(v,n.current,c.current)},[y,q,p,d,l,b,h,g]);return C.useLayoutEffect(()=>(w(),()=>{o.current&&(o.current(),o.current=null)}),[w]),C.useEffect(()=>{document.fonts.ready.then(w)},[w]),t}const K=C.forwardRef(function({children:t,as:n="p",className:c,style:o,...y},m){const p=J(y),d=C.useCallback(l=>{p.current=l,typeof m=="function"?m(l):m&&(m.current=l)},[m]);return z.jsx(n,{ref:d,className:c,style:o,children:t})});K.displayName="MagnetTypeText";exports.MAGNET_TYPE_CLASSES=B;exports.MagnetTypeText=K;exports.applyMagnetType=W;exports.getCleanHTML=P;exports.removeMagnetType=Q;exports.startMagnetType=G;exports.useMagnetType=J;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react"),K=require("react/jsx-runtime"),G={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},R={word:"mt-word",char:"mt-char",probe:"mt-probe"},$={axes:{wght:[300,500]},radius:120,falloff:"quadratic",magnetMode:"attract",wdthBoost:6,scope:"document"};function _(t,o=[]){return t.nodeType===Node.TEXT_NODE?o.push(t):t.childNodes.forEach(r=>_(r,o)),o}function H(t,o,r){if(!t||t==="normal")return`"${o}" ${r}`;const l=new RegExp(`(["'])${o}\\1\\s+[\\d.eE+-]+`),c=`"${o}" ${r}`;return l.test(t)?t.replace(l,c):`${t}, ${c}`}function J(t,o){let r=t;for(const[l,c]of Object.entries(o))r=H(r,l,c);return r}function U(t,o,r){if(o.opacity!==void 0){const[l,c]=o.opacity;t.style.opacity=String(l+(c-l)*r)}o.italic===!0&&(t.style.fontStyle=r>.5?"italic":"")}function P(t,o){o.opacity!==void 0&&(t.style.opacity=String(o.opacity[0])),o.italic===!0&&(t.style.fontStyle="")}function z(t){const o=t.cloneNode(!0),r=o.querySelectorAll(`.${R.word}, .${R.char}`);return Array.from(r).reverse().forEach(c=>{const a=c.parentNode;if(a){for(;c.firstChild;)a.insertBefore(c.firstChild,c);a.removeChild(c)}}),o.innerHTML}function ot(t,o){t.innerHTML=o}function Q(t,o,r={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=o,()=>{};const l=r.wdthBoost??$.wdthBoost,c=r.radius??$.radius,a=r.falloff??$.falloff,T=r.scope??$.scope,v=r.props,d=r.transitionMs??0,S=window.scrollY;t.innerHTML=o;const L=getComputedStyle(t).fontVariationSettings,b=L.match(/"wdth"\s+([\d.eE+-]+)/),y=b?parseFloat(b[1]):100,A=_(t),w=[];for(const n of A){const e=n.textContent??"";if(!e||!e.split("").some(m=>m in G))continue;const h=document.createDocumentFragment();for(const m of e){const B=G[m];if(B===void 0){const E=h.lastChild;E&&E.nodeType===Node.TEXT_NODE?E.textContent+=m:h.appendChild(document.createTextNode(m))}else{const E=document.createElement("span");E.className=R.char,E.style.fontVariationSettings=H(L,"wdth",y),E.textContent=m,h.appendChild(E),w.push({span:E,riskLevel:B})}}n.parentNode.replaceChild(h,n)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-S)>2&&window.scrollTo({top:S,behavior:"instant"})}),w.length===0)return()=>{};v&&w.forEach(({span:n})=>P(n,v));let p=-9999,q=-9999,g=!1,i=0,V=!0,s=null;function f(){if(!V)return;if(!g){w.forEach(({span:e})=>{d>0&&(e.style.transition=`font-variation-settings ${d}ms ease`),e.style.fontVariationSettings=H(L,"wdth",y),v&&P(e,v)}),d>0&&(s!==null&&clearTimeout(s),s=setTimeout(()=>{w.forEach(({span:e})=>{e.style.transition=""}),s=null},d)),i=0;return}const n=w.map(({span:e})=>e.getBoundingClientRect());w.forEach(({span:e,riskLevel:x},h)=>{e.style.transition="";const m=n[h],B=m.left+m.width/2,E=m.top+m.height/2,j=Math.sqrt((p-B)**2+(q-E)**2),O=Math.max(0,1-j/c),Y=a==="quadratic"?O*O:O,X=l*(x/3)*Y;e.style.fontVariationSettings=H(L,"wdth",y+X),v&&U(e,v,Y)}),i=requestAnimationFrame(f)}function N(n){p=n.clientX,q=n.clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(f))}function C(){g=!1,i===0&&(i=requestAnimationFrame(f))}function F(n){n.touches.length!==0&&(p=n.touches[0].clientX,q=n.touches[0].clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(f)))}function k(){g=!1,i===0&&(i=requestAnimationFrame(f))}const u=T==="document"?document:t;return u.addEventListener("mousemove",N),u.addEventListener("mouseleave",C),u.addEventListener("touchmove",F,{passive:!0}),u.addEventListener("touchend",k),()=>{V=!1,cancelAnimationFrame(i),s!==null&&clearTimeout(s),u.removeEventListener("mousemove",N),u.removeEventListener("mouseleave",C),u.removeEventListener("touchmove",F),u.removeEventListener("touchend",k),t.innerHTML=o}}function Z(t,o,r={}){if(typeof window>"u")return()=>{};if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return t.innerHTML=o,()=>{};const l=r.axes??$.axes,c=r.radius??$.radius,a=r.falloff??$.falloff,T=r.magnetMode??$.magnetMode,v=r.scope??$.scope,d=r.props,S=r.transitionMs??0,L=window.scrollY;t.innerHTML=o;const b=_(t),y=[];for(const n of b){const e=n.textContent??"";if(!e.trim())continue;const x=e.split(/(\S+)/),h=document.createDocumentFragment();for(let m=0;m<x.length;m+=2){const B=x[m],E=x[m+1];if(!E)continue;const O=x[m+3]===void 0?x[m+2]??"":"",Y=document.createElement("span");Y.className=R.word,Y.textContent=B+E+O,h.appendChild(Y),y.push(Y)}n.parentNode.replaceChild(h,n)}if(requestAnimationFrame(()=>{typeof window<"u"&&Math.abs(window.scrollY-L)>2&&window.scrollTo({top:L,behavior:"instant"})}),y.length===0)return()=>{};const A=getComputedStyle(t).fontVariationSettings,w=J(A,Object.fromEntries(Object.entries(l).map(([n,[e]])=>[n,e])));y.forEach(n=>{n.style.fontVariationSettings=w,d&&P(n,d)});let p=-9999,q=-9999,g=!1,i=0,V=!0,s=null;function f(){if(!V)return;if(!g){y.forEach(e=>{S>0&&(e.style.transition=`font-variation-settings ${S}ms ease`),e.style.fontVariationSettings=w,d&&P(e,d)}),S>0&&(s!==null&&clearTimeout(s),s=setTimeout(()=>{y.forEach(e=>{e.style.transition=""}),s=null},S)),i=0;return}const n=y.map(e=>e.getBoundingClientRect());y.forEach((e,x)=>{e.style.transition="";const h=n[x],m=h.left+h.width/2,B=h.top+h.height/2,E=Math.sqrt((p-m)**2+(q-B)**2),j=Math.max(0,1-E/c),O=a==="quadratic"?j*j:j,Y=T==="repel"?1-O:O,X={};for(const D of Object.keys(l)){const[I,nt]=l[D]??[300,500];X[D]=I+(nt-I)*Y}e.style.fontVariationSettings=J(A,X),d&&U(e,d,O)}),i=requestAnimationFrame(f)}function N(n){p=n.clientX,q=n.clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(f))}function C(){g=!1,i===0&&(i=requestAnimationFrame(f))}function F(n){n.touches.length!==0&&(p=n.touches[0].clientX,q=n.touches[0].clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(f)))}function k(){g=!1,i===0&&(i=requestAnimationFrame(f))}const u=v==="document"?document:t;return u.addEventListener("mousemove",N),u.addEventListener("mouseleave",C),u.addEventListener("touchmove",F,{passive:!0}),u.addEventListener("touchend",k),()=>{V=!1,cancelAnimationFrame(i),s!==null&&clearTimeout(s),u.removeEventListener("mousemove",N),u.removeEventListener("mouseleave",C),u.removeEventListener("touchmove",F),u.removeEventListener("touchend",k),t.innerHTML=o}}function W(t){const o=M.useRef(null),r=M.useRef(null),l=M.useRef(t);l.current=t;const c=M.useRef(null),a=t.mode??"field",{axes:T,radius:v,falloff:d,magnetMode:S,wdthBoost:L,scope:b}=t,y=T?JSON.stringify(T):void 0,A=t.props?JSON.stringify(t.props):void 0,w=M.useCallback(()=>{const p=o.current;if(!p)return;r.current===null&&(r.current=z(p)),c.current&&(c.current(),c.current=null),(l.current.mode??"field")==="field"?c.current=Z(p,r.current,l.current):c.current=Q(p,r.current,l.current)},[a,y,v,d,S,L,b,A]);return M.useLayoutEffect(()=>(w(),()=>{c.current&&(c.current(),c.current=null)}),[w]),M.useEffect(()=>{document.fonts.ready.then(w)},[w]),o}const tt=M.forwardRef(function({children:o,as:r="p",className:l,style:c,...a},T){const v=W(a),d=M.useCallback(S=>{v.current=S,typeof T=="function"?T(S):T&&(T.current=S)},[T]);return K.jsx(r,{ref:d,className:l,style:c,children:o})});tt.displayName="MagnetTypeText";const et=M.forwardRef(function({children:o,as:r="p",className:l,style:c,minWeight:a=300,maxWeight:T=600,proximityRadius:v,spreadRadius:d,fixedAxes:S={}},L){const b=M.useRef(null),y=M.useRef(null),A=M.useRef([]),w=M.useCallback(s=>{b.current=s,typeof L=="function"?L(s):L&&(L.current=s)},[L]);function p(s){const f=[`'wght' ${s.toFixed(0)}`];for(const[N,C]of Object.entries(S))f.push(`'${N}' ${C}`);return f.join(", ")}M.useEffect(()=>{var k;if(!d)return;const s=b.current;if(!s)return;const f=[],N=document.createTreeWalker(s,NodeFilter.SHOW_TEXT),C=[];let F;for(;F=N.nextNode();)C.push(F);for(const u of C){const n=u.textContent??"";if(!n)continue;const e=document.createDocumentFragment();for(const x of n)if(/\s/.test(x))e.appendChild(document.createTextNode(x));else{const h=document.createElement("span");h.style.fontVariationSettings=p(a),h.textContent=x,f.push(h),e.appendChild(h)}(k=u.parentNode)==null||k.replaceChild(e,u)}A.current=f},[]);function q(s,f){const N=b.current;if(!N)return;const C=N.getBoundingClientRect(),F=Math.max(C.left-s,0,s-C.right),k=Math.max(C.top-f,0,f-C.bottom),u=Math.sqrt(F*F+k*k);if(v!==void 0&&!d){const e=1-(1-Math.max(0,1-u/v))**2;N.style.fontVariationSettings=p(a+(T-a)*e);return}if(d){if(v!==void 0&&u>v){N.style.fontVariationSettings=p(a);for(const n of A.current)n.style.fontVariationSettings=p(a);return}for(const n of A.current){const e=n.getBoundingClientRect(),x=(e.left+e.right)/2,h=(e.top+e.bottom)/2,m=Math.sqrt((s-x)**2+(f-h)**2),E=1-(1-Math.max(0,1-m/d))**2;n.style.fontVariationSettings=p(a+(T-a)*E)}}}const g=M.useCallback(s=>{y.current={x:s.clientX,y:s.clientY},q(s.clientX,s.clientY)},[a,T,v,d]),i=M.useCallback(()=>{y.current&&q(y.current.x,y.current.y)},[a,T,v,d]),V=M.useCallback(()=>{y.current=null;const s=b.current;s&&(s.style.fontVariationSettings=p(a));for(const f of A.current)f.style.fontVariationSettings=p(a)},[a]);return M.useEffect(()=>(window.addEventListener("mousemove",g,{passive:!0}),window.addEventListener("scroll",i,{passive:!0,capture:!0}),document.documentElement.addEventListener("mouseleave",V),()=>{window.removeEventListener("mousemove",g),window.removeEventListener("scroll",i,{capture:!0}),document.documentElement.removeEventListener("mouseleave",V)}),[g,i,V]),K.jsx(r,{ref:w,className:l,style:{fontVariationSettings:p(a),...c},children:o})});et.displayName="MagnetBlock";exports.MAGNET_TYPE_CLASSES=R;exports.MagnetBlock=et;exports.MagnetTypeText=tt;exports.applyMagnetType=Q;exports.getCleanHTML=z;exports.removeMagnetType=ot;exports.startMagnetType=Z;exports.useMagnetType=W;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { default as default_2 } from 'react';
2
+ import { ForwardRefExoticComponent } from 'react';
3
+ import { RefAttributes } from 'react';
2
4
  import { RefObject } from 'react';
3
5
 
4
6
  /**
@@ -38,6 +40,28 @@ export declare const MAGNET_TYPE_CLASSES: {
38
40
  readonly probe: "mt-probe";
39
41
  };
40
42
 
43
+ /**
44
+ * Drop-in block element with cursor-proximity variable font weight variation.
45
+ * Accepts any ReactNode — use spreadRadius for per-word spread, proximityRadius
46
+ * for a whole-element proximity gate, or combine both.
47
+ */
48
+ export declare const MagnetBlock: ForwardRefExoticComponent<MagnetBlockProps & RefAttributes<HTMLElement>>;
49
+
50
+ export declare interface MagnetBlockProps {
51
+ children: React.ReactNode;
52
+ /** HTML element to render. Default: 'p' */
53
+ as?: React.ElementType;
54
+ className?: string;
55
+ style?: React.CSSProperties;
56
+ minWeight?: number;
57
+ maxWeight?: number;
58
+ /** Pixel distance from the element edge within which the whole-element effect activates */
59
+ proximityRadius?: number;
60
+ /** Pixel distance from the cursor within which each word's weight rises to max */
61
+ spreadRadius?: number;
62
+ fixedAxes?: Record<string, number>;
63
+ }
64
+
41
65
  /** Whether cursor proximity attracts toward peak or repels toward rest */
42
66
  export declare type MagnetModeType = 'attract' | 'repel';
43
67
 
@@ -104,6 +128,15 @@ export declare interface MagnetTypeOptions {
104
128
  * Risk 3 characters receive wdthBoost × (3/3) = full boost at peak.
105
129
  */
106
130
  wdthBoost?: number;
131
+ /**
132
+ * Duration in milliseconds for the CSS transition back to rest values when the
133
+ * cursor leaves (mouseleave / touchend). Default: 0 (instant snap, no transition).
134
+ *
135
+ * When > 0, sets `transition: font-variation-settings <transitionMs>ms ease` on
136
+ * each span at leave time, then removes the transition property after the duration.
137
+ * On mousemove / touchmove the transition is cleared so live tracking is not delayed.
138
+ */
139
+ transitionMs?: number;
107
140
  }
108
141
 
109
142
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { useRef as B, useCallback as _, useLayoutEffect as J, useEffect as K, forwardRef as U } from "react";
2
- import { jsx as G } from "react/jsx-runtime";
3
- const D = {
1
+ import { useRef as k, useCallback as j, useLayoutEffect as tt, useEffect as _, forwardRef as z } from "react";
2
+ import { jsx as Q } from "react/jsx-runtime";
3
+ const G = {
4
4
  i: 3,
5
5
  l: 3,
6
6
  1: 3,
@@ -22,14 +22,14 @@ const D = {
22
22
  c: 1,
23
23
  e: 1
24
24
  // similar bowls
25
- }, V = {
25
+ }, I = {
26
26
  /** Applied to each word span in field mode */
27
27
  word: "mt-word",
28
28
  /** Applied to each character span in legibility mode */
29
29
  char: "mt-char",
30
30
  /** Applied to measurement probe spans (never in final output) */
31
31
  probe: "mt-probe"
32
- }, C = {
32
+ }, Y = {
33
33
  axes: { wght: [300, 500] },
34
34
  radius: 120,
35
35
  falloff: "quadratic",
@@ -37,196 +37,320 @@ const D = {
37
37
  wdthBoost: 6,
38
38
  scope: "document"
39
39
  };
40
- function k(t, e = []) {
41
- return t.nodeType === Node.TEXT_NODE ? e.push(t) : t.childNodes.forEach((n) => k(n, e)), e;
40
+ function J(t, o = []) {
41
+ return t.nodeType === Node.TEXT_NODE ? o.push(t) : t.childNodes.forEach((r) => J(r, o)), o;
42
42
  }
43
- function $(t, e, n) {
44
- if (!t || t === "normal") return `"${e}" ${n}`;
45
- const c = new RegExp(`(["'])${e}\\1\\s+[\\d.eE+-]+`), o = `"${e}" ${n}`;
46
- return c.test(t) ? t.replace(c, o) : `${t}, ${o}`;
43
+ function D(t, o, r) {
44
+ if (!t || t === "normal") return `"${o}" ${r}`;
45
+ const f = new RegExp(`(["'])${o}\\1\\s+[\\d.eE+-]+`), c = `"${o}" ${r}`;
46
+ return f.test(t) ? t.replace(f, c) : `${t}, ${c}`;
47
47
  }
48
- function I(t, e) {
49
- let n = t;
50
- for (const [c, o] of Object.entries(e))
51
- n = $(n, c, o);
52
- return n;
48
+ function R(t, o) {
49
+ let r = t;
50
+ for (const [f, c] of Object.entries(o))
51
+ r = D(r, f, c);
52
+ return r;
53
53
  }
54
- function P(t, e, n) {
55
- if (e.opacity !== void 0) {
56
- const [c, o] = e.opacity;
57
- t.style.opacity = String(c + (o - c) * n);
54
+ function Z(t, o, r) {
55
+ if (o.opacity !== void 0) {
56
+ const [f, c] = o.opacity;
57
+ t.style.opacity = String(f + (c - f) * r);
58
58
  }
59
- e.italic === !0 && (t.style.fontStyle = n > 0.5 ? "italic" : "");
59
+ o.italic === !0 && (t.style.fontStyle = r > 0.5 ? "italic" : "");
60
60
  }
61
- function j(t, e) {
62
- e.opacity !== void 0 && (t.style.opacity = String(e.opacity[0])), e.italic === !0 && (t.style.fontStyle = "");
61
+ function P(t, o) {
62
+ o.opacity !== void 0 && (t.style.opacity = String(o.opacity[0])), o.italic === !0 && (t.style.fontStyle = "");
63
63
  }
64
- function z(t) {
65
- const e = t.cloneNode(!0), n = e.querySelectorAll(
66
- `.${V.word}, .${V.char}`
64
+ function et(t) {
65
+ const o = t.cloneNode(!0), r = o.querySelectorAll(
66
+ `.${I.word}, .${I.char}`
67
67
  );
68
- return Array.from(n).reverse().forEach((o) => {
69
- const y = o.parentNode;
70
- if (y) {
71
- for (; o.firstChild; ) y.insertBefore(o.firstChild, o);
72
- y.removeChild(o);
68
+ return Array.from(r).reverse().forEach((c) => {
69
+ const a = c.parentNode;
70
+ if (a) {
71
+ for (; c.firstChild; ) a.insertBefore(c.firstChild, c);
72
+ a.removeChild(c);
73
73
  }
74
- }), e.innerHTML;
74
+ }), o.innerHTML;
75
75
  }
76
- function rt(t, e) {
77
- t.innerHTML = e;
76
+ function ut(t, o) {
77
+ t.innerHTML = o;
78
78
  }
79
- function Q(t, e, n = {}) {
79
+ function nt(t, o, r = {}) {
80
80
  if (typeof window > "u") return () => {
81
81
  };
82
82
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
83
- return t.innerHTML = e, () => {
83
+ return t.innerHTML = o, () => {
84
84
  };
85
- const c = n.wdthBoost ?? C.wdthBoost, o = n.radius ?? C.radius, y = n.falloff ?? C.falloff, m = n.scope ?? C.scope, p = n.props, d = window.scrollY;
86
- t.innerHTML = e;
87
- const l = getComputedStyle(t).fontVariationSettings, A = l.match(/"wdth"\s+([\d.eE+-]+)/), h = A ? parseFloat(A[1]) : 100, q = k(t), w = [];
88
- for (const r of q) {
89
- const s = r.textContent ?? "";
90
- if (!s || !s.split("").some((i) => i in D)) continue;
91
- const u = document.createDocumentFragment();
92
- for (const i of s) {
93
- const L = D[i];
94
- if (L === void 0) {
95
- const a = u.lastChild;
96
- a && a.nodeType === Node.TEXT_NODE ? a.textContent += i : u.appendChild(document.createTextNode(i));
85
+ const f = r.wdthBoost ?? Y.wdthBoost, c = r.radius ?? Y.radius, a = r.falloff ?? Y.falloff, w = r.scope ?? Y.scope, v = r.props, d = r.transitionMs ?? 0, x = window.scrollY;
86
+ t.innerHTML = o;
87
+ const L = getComputedStyle(t).fontVariationSettings, N = L.match(/"wdth"\s+([\d.eE+-]+)/), y = N ? parseFloat(N[1]) : 100, A = J(t), M = [];
88
+ for (const n of A) {
89
+ const e = n.textContent ?? "";
90
+ if (!e || !e.split("").some((m) => m in G)) continue;
91
+ const p = document.createDocumentFragment();
92
+ for (const m of e) {
93
+ const B = G[m];
94
+ if (B === void 0) {
95
+ const E = p.lastChild;
96
+ E && E.nodeType === Node.TEXT_NODE ? E.textContent += m : p.appendChild(document.createTextNode(m));
97
97
  } else {
98
- const a = document.createElement("span");
99
- a.className = V.char, a.style.fontVariationSettings = $(l, "wdth", h), a.textContent = i, u.appendChild(a), w.push({ span: a, riskLevel: L });
98
+ const E = document.createElement("span");
99
+ E.className = I.char, E.style.fontVariationSettings = D(L, "wdth", y), E.textContent = m, p.appendChild(E), M.push({ span: E, riskLevel: B });
100
100
  }
101
101
  }
102
- r.parentNode.replaceChild(u, r);
102
+ n.parentNode.replaceChild(p, n);
103
103
  }
104
104
  if (requestAnimationFrame(() => {
105
- typeof window < "u" && Math.abs(window.scrollY - d) > 2 && window.scrollTo({ top: d, behavior: "instant" });
106
- }), w.length === 0) return () => {
105
+ typeof window < "u" && Math.abs(window.scrollY - x) > 2 && window.scrollTo({ top: x, behavior: "instant" });
106
+ }), M.length === 0) return () => {
107
107
  };
108
- p && w.forEach(({ span: r }) => j(r, p));
109
- let g = -9999, v = -9999, M = !1, f = 0, F = !0;
110
- function N() {
111
- if (!F) return;
112
- if (!M) {
113
- w.forEach(({ span: s }) => {
114
- s.style.fontVariationSettings = $(l, "wdth", h), p && j(s, p);
115
- }), f = 0;
108
+ v && M.forEach(({ span: n }) => P(n, v));
109
+ let h = -9999, q = -9999, g = !1, i = 0, V = !0, s = null;
110
+ function l() {
111
+ if (!V) return;
112
+ if (!g) {
113
+ M.forEach(({ span: e }) => {
114
+ d > 0 && (e.style.transition = `font-variation-settings ${d}ms ease`), e.style.fontVariationSettings = D(L, "wdth", y), v && P(e, v);
115
+ }), d > 0 && (s !== null && clearTimeout(s), s = setTimeout(() => {
116
+ M.forEach(({ span: e }) => {
117
+ e.style.transition = "";
118
+ }), s = null;
119
+ }, d)), i = 0;
116
120
  return;
117
121
  }
118
- const r = w.map(({ span: s }) => s.getBoundingClientRect());
119
- w.forEach(({ span: s, riskLevel: E }, u) => {
120
- const i = r[u], L = i.left + i.width / 2, a = i.top + i.height / 2, b = Math.sqrt((g - L) ** 2 + (v - a) ** 2), T = Math.max(0, 1 - b / o), S = y === "quadratic" ? T * T : T, R = c * (E / 3) * S;
121
- s.style.fontVariationSettings = $(l, "wdth", h + R), p && P(s, p, S);
122
- }), f = requestAnimationFrame(N);
122
+ const n = M.map(({ span: e }) => e.getBoundingClientRect());
123
+ M.forEach(({ span: e, riskLevel: T }, p) => {
124
+ e.style.transition = "";
125
+ const m = n[p], B = m.left + m.width / 2, E = m.top + m.height / 2, X = Math.sqrt((h - B) ** 2 + (q - E) ** 2), O = Math.max(0, 1 - X / c), $ = a === "quadratic" ? O * O : O, H = f * (T / 3) * $;
126
+ e.style.fontVariationSettings = D(L, "wdth", y + H), v && Z(e, v, $);
127
+ }), i = requestAnimationFrame(l);
123
128
  }
124
- function O(r) {
125
- g = r.clientX, v = r.clientY, M || (M = !0), f === 0 && (f = requestAnimationFrame(N));
129
+ function C(n) {
130
+ h = n.clientX, q = n.clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(l));
126
131
  }
127
- function Y() {
128
- M = !1, f === 0 && (f = requestAnimationFrame(N));
132
+ function S() {
133
+ g = !1, i === 0 && (i = requestAnimationFrame(l));
129
134
  }
130
- const x = m === "document" ? document : t;
131
- return x.addEventListener("mousemove", O), x.addEventListener("mouseleave", Y), () => {
132
- F = !1, cancelAnimationFrame(f), x.removeEventListener("mousemove", O), x.removeEventListener("mouseleave", Y), t.innerHTML = e;
135
+ function F(n) {
136
+ n.touches.length !== 0 && (h = n.touches[0].clientX, q = n.touches[0].clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(l)));
137
+ }
138
+ function b() {
139
+ g = !1, i === 0 && (i = requestAnimationFrame(l));
140
+ }
141
+ const u = w === "document" ? document : t;
142
+ return u.addEventListener("mousemove", C), u.addEventListener("mouseleave", S), u.addEventListener("touchmove", F, { passive: !0 }), u.addEventListener("touchend", b), () => {
143
+ V = !1, cancelAnimationFrame(i), s !== null && clearTimeout(s), u.removeEventListener("mousemove", C), u.removeEventListener("mouseleave", S), u.removeEventListener("touchmove", F), u.removeEventListener("touchend", b), t.innerHTML = o;
133
144
  };
134
145
  }
135
- function Z(t, e, n = {}) {
146
+ function ot(t, o, r = {}) {
136
147
  if (typeof window > "u") return () => {
137
148
  };
138
149
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches)
139
- return t.innerHTML = e, () => {
150
+ return t.innerHTML = o, () => {
140
151
  };
141
- const c = n.axes ?? C.axes, o = n.radius ?? C.radius, y = n.falloff ?? C.falloff, m = n.magnetMode ?? C.magnetMode, p = n.scope ?? C.scope, d = n.props, l = window.scrollY;
142
- t.innerHTML = e;
143
- const A = k(t), h = [];
144
- for (const r of A) {
145
- const s = r.textContent ?? "";
146
- if (!s.trim()) continue;
147
- const E = s.split(/(\S+)/), u = document.createDocumentFragment();
148
- for (let i = 0; i < E.length; i += 2) {
149
- const L = E[i], a = E[i + 1];
150
- if (!a) continue;
151
- const T = E[i + 3] === void 0 ? E[i + 2] ?? "" : "", S = document.createElement("span");
152
- S.className = V.word, S.textContent = L + a + T, u.appendChild(S), h.push(S);
152
+ const f = r.axes ?? Y.axes, c = r.radius ?? Y.radius, a = r.falloff ?? Y.falloff, w = r.magnetMode ?? Y.magnetMode, v = r.scope ?? Y.scope, d = r.props, x = r.transitionMs ?? 0, L = window.scrollY;
153
+ t.innerHTML = o;
154
+ const N = J(t), y = [];
155
+ for (const n of N) {
156
+ const e = n.textContent ?? "";
157
+ if (!e.trim()) continue;
158
+ const T = e.split(/(\S+)/), p = document.createDocumentFragment();
159
+ for (let m = 0; m < T.length; m += 2) {
160
+ const B = T[m], E = T[m + 1];
161
+ if (!E) continue;
162
+ const O = T[m + 3] === void 0 ? T[m + 2] ?? "" : "", $ = document.createElement("span");
163
+ $.className = I.word, $.textContent = B + E + O, p.appendChild($), y.push($);
153
164
  }
154
- r.parentNode.replaceChild(u, r);
165
+ n.parentNode.replaceChild(p, n);
155
166
  }
156
167
  if (requestAnimationFrame(() => {
157
- typeof window < "u" && Math.abs(window.scrollY - l) > 2 && window.scrollTo({ top: l, behavior: "instant" });
158
- }), h.length === 0) return () => {
168
+ typeof window < "u" && Math.abs(window.scrollY - L) > 2 && window.scrollTo({ top: L, behavior: "instant" });
169
+ }), y.length === 0) return () => {
159
170
  };
160
- const q = getComputedStyle(t).fontVariationSettings, w = I(
161
- q,
162
- Object.fromEntries(Object.entries(c).map(([r, [s]]) => [r, s]))
171
+ const A = getComputedStyle(t).fontVariationSettings, M = R(
172
+ A,
173
+ Object.fromEntries(Object.entries(f).map(([n, [e]]) => [n, e]))
163
174
  );
164
- h.forEach((r) => {
165
- r.style.fontVariationSettings = w, d && j(r, d);
175
+ y.forEach((n) => {
176
+ n.style.fontVariationSettings = M, d && P(n, d);
166
177
  });
167
- let g = -9999, v = -9999, M = !1, f = 0, F = !0;
168
- function N() {
169
- if (!F) return;
170
- if (!M) {
171
- h.forEach((s) => {
172
- s.style.fontVariationSettings = w, d && j(s, d);
173
- }), f = 0;
178
+ let h = -9999, q = -9999, g = !1, i = 0, V = !0, s = null;
179
+ function l() {
180
+ if (!V) return;
181
+ if (!g) {
182
+ y.forEach((e) => {
183
+ x > 0 && (e.style.transition = `font-variation-settings ${x}ms ease`), e.style.fontVariationSettings = M, d && P(e, d);
184
+ }), x > 0 && (s !== null && clearTimeout(s), s = setTimeout(() => {
185
+ y.forEach((e) => {
186
+ e.style.transition = "";
187
+ }), s = null;
188
+ }, x)), i = 0;
174
189
  return;
175
190
  }
176
- const r = h.map((s) => s.getBoundingClientRect());
177
- h.forEach((s, E) => {
178
- const u = r[E], i = u.left + u.width / 2, L = u.top + u.height / 2, a = Math.sqrt((g - i) ** 2 + (v - L) ** 2), b = Math.max(0, 1 - a / o), T = y === "quadratic" ? b * b : b, S = m === "repel" ? 1 - T : T, R = {};
179
- for (const H of Object.keys(c)) {
180
- const [X, W] = c[H] ?? [300, 500];
181
- R[H] = X + (W - X) * S;
191
+ const n = y.map((e) => e.getBoundingClientRect());
192
+ y.forEach((e, T) => {
193
+ e.style.transition = "";
194
+ const p = n[T], m = p.left + p.width / 2, B = p.top + p.height / 2, E = Math.sqrt((h - m) ** 2 + (q - B) ** 2), X = Math.max(0, 1 - E / c), O = a === "quadratic" ? X * X : X, $ = w === "repel" ? 1 - O : O, H = {};
195
+ for (const K of Object.keys(f)) {
196
+ const [U, W] = f[K] ?? [300, 500];
197
+ H[K] = U + (W - U) * $;
182
198
  }
183
- s.style.fontVariationSettings = I(q, R), d && P(s, d, T);
184
- }), f = requestAnimationFrame(N);
199
+ e.style.fontVariationSettings = R(A, H), d && Z(e, d, O);
200
+ }), i = requestAnimationFrame(l);
201
+ }
202
+ function C(n) {
203
+ h = n.clientX, q = n.clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(l));
185
204
  }
186
- function O(r) {
187
- g = r.clientX, v = r.clientY, M || (M = !0), f === 0 && (f = requestAnimationFrame(N));
205
+ function S() {
206
+ g = !1, i === 0 && (i = requestAnimationFrame(l));
188
207
  }
189
- function Y() {
190
- M = !1, f === 0 && (f = requestAnimationFrame(N));
208
+ function F(n) {
209
+ n.touches.length !== 0 && (h = n.touches[0].clientX, q = n.touches[0].clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(l)));
191
210
  }
192
- const x = p === "document" ? document : t;
193
- return x.addEventListener("mousemove", O), x.addEventListener("mouseleave", Y), () => {
194
- F = !1, cancelAnimationFrame(f), x.removeEventListener("mousemove", O), x.removeEventListener("mouseleave", Y), t.innerHTML = e;
211
+ function b() {
212
+ g = !1, i === 0 && (i = requestAnimationFrame(l));
213
+ }
214
+ const u = v === "document" ? document : t;
215
+ return u.addEventListener("mousemove", C), u.addEventListener("mouseleave", S), u.addEventListener("touchmove", F, { passive: !0 }), u.addEventListener("touchend", b), () => {
216
+ V = !1, cancelAnimationFrame(i), s !== null && clearTimeout(s), u.removeEventListener("mousemove", C), u.removeEventListener("mouseleave", S), u.removeEventListener("touchmove", F), u.removeEventListener("touchend", b), t.innerHTML = o;
195
217
  };
196
218
  }
197
- function tt(t) {
198
- const e = B(null), n = B(null), c = B(t);
199
- c.current = t;
200
- const o = B(null), y = t.mode ?? "field", { axes: m, radius: p, falloff: d, magnetMode: l, wdthBoost: A, scope: h } = t, q = m ? JSON.stringify(m) : void 0, w = t.props ? JSON.stringify(t.props) : void 0, g = _(() => {
201
- const v = e.current;
202
- if (!v) return;
203
- n.current === null && (n.current = z(v)), o.current && (o.current(), o.current = null), (c.current.mode ?? "field") === "field" ? o.current = Z(v, n.current, c.current) : o.current = Q(v, n.current, c.current);
204
- }, [y, q, p, d, l, A, h, w]);
205
- return J(() => (g(), () => {
206
- o.current && (o.current(), o.current = null);
207
- }), [g]), K(() => {
208
- document.fonts.ready.then(g);
209
- }, [g]), e;
219
+ function rt(t) {
220
+ const o = k(null), r = k(null), f = k(t);
221
+ f.current = t;
222
+ const c = k(null), a = t.mode ?? "field", { axes: w, radius: v, falloff: d, magnetMode: x, wdthBoost: L, scope: N } = t, y = w ? JSON.stringify(w) : void 0, A = t.props ? JSON.stringify(t.props) : void 0, M = j(() => {
223
+ const h = o.current;
224
+ if (!h) return;
225
+ r.current === null && (r.current = et(h)), c.current && (c.current(), c.current = null), (f.current.mode ?? "field") === "field" ? c.current = ot(h, r.current, f.current) : c.current = nt(h, r.current, f.current);
226
+ }, [a, y, v, d, x, L, N, A]);
227
+ return tt(() => (M(), () => {
228
+ c.current && (c.current(), c.current = null);
229
+ }), [M]), _(() => {
230
+ document.fonts.ready.then(M);
231
+ }, [M]), o;
210
232
  }
211
- const et = U(
212
- function({ children: e, as: n = "p", className: c, style: o, ...y }, m) {
213
- const p = tt(y), d = _(
214
- (l) => {
215
- p.current = l, typeof m == "function" ? m(l) : m && (m.current = l);
233
+ const st = z(
234
+ function({ children: o, as: r = "p", className: f, style: c, ...a }, w) {
235
+ const v = rt(a), d = j(
236
+ (x) => {
237
+ v.current = x, typeof w == "function" ? w(x) : w && (w.current = x);
238
+ },
239
+ // eslint-disable-next-line react-hooks/exhaustive-deps
240
+ [w]
241
+ );
242
+ return /* @__PURE__ */ Q(r, { ref: d, className: f, style: c, children: o });
243
+ }
244
+ );
245
+ st.displayName = "MagnetTypeText";
246
+ const ct = z(
247
+ function({
248
+ children: o,
249
+ as: r = "p",
250
+ className: f,
251
+ style: c,
252
+ minWeight: a = 300,
253
+ maxWeight: w = 600,
254
+ proximityRadius: v,
255
+ spreadRadius: d,
256
+ fixedAxes: x = {}
257
+ }, L) {
258
+ const N = k(null), y = k(null), A = k([]), M = j(
259
+ (s) => {
260
+ N.current = s, typeof L == "function" ? L(s) : L && (L.current = s);
216
261
  },
217
262
  // eslint-disable-next-line react-hooks/exhaustive-deps
218
- [m]
263
+ [L]
264
+ );
265
+ function h(s) {
266
+ const l = [`'wght' ${s.toFixed(0)}`];
267
+ for (const [C, S] of Object.entries(x)) l.push(`'${C}' ${S}`);
268
+ return l.join(", ");
269
+ }
270
+ _(() => {
271
+ var b;
272
+ if (!d) return;
273
+ const s = N.current;
274
+ if (!s) return;
275
+ const l = [], C = document.createTreeWalker(s, NodeFilter.SHOW_TEXT), S = [];
276
+ let F;
277
+ for (; F = C.nextNode(); ) S.push(F);
278
+ for (const u of S) {
279
+ const n = u.textContent ?? "";
280
+ if (!n) continue;
281
+ const e = document.createDocumentFragment();
282
+ for (const T of n)
283
+ if (/\s/.test(T))
284
+ e.appendChild(document.createTextNode(T));
285
+ else {
286
+ const p = document.createElement("span");
287
+ p.style.fontVariationSettings = h(a), p.textContent = T, l.push(p), e.appendChild(p);
288
+ }
289
+ (b = u.parentNode) == null || b.replaceChild(e, u);
290
+ }
291
+ A.current = l;
292
+ }, []);
293
+ function q(s, l) {
294
+ const C = N.current;
295
+ if (!C) return;
296
+ const S = C.getBoundingClientRect(), F = Math.max(S.left - s, 0, s - S.right), b = Math.max(S.top - l, 0, l - S.bottom), u = Math.sqrt(F * F + b * b);
297
+ if (v !== void 0 && !d) {
298
+ const e = 1 - (1 - Math.max(0, 1 - u / v)) ** 2;
299
+ C.style.fontVariationSettings = h(a + (w - a) * e);
300
+ return;
301
+ }
302
+ if (d) {
303
+ if (v !== void 0 && u > v) {
304
+ C.style.fontVariationSettings = h(a);
305
+ for (const n of A.current) n.style.fontVariationSettings = h(a);
306
+ return;
307
+ }
308
+ for (const n of A.current) {
309
+ const e = n.getBoundingClientRect(), T = (e.left + e.right) / 2, p = (e.top + e.bottom) / 2, m = Math.sqrt((s - T) ** 2 + (l - p) ** 2), E = 1 - (1 - Math.max(0, 1 - m / d)) ** 2;
310
+ n.style.fontVariationSettings = h(a + (w - a) * E);
311
+ }
312
+ }
313
+ }
314
+ const g = j(
315
+ (s) => {
316
+ y.current = { x: s.clientX, y: s.clientY }, q(s.clientX, s.clientY);
317
+ },
318
+ [a, w, v, d]
319
+ ), i = j(
320
+ () => {
321
+ y.current && q(y.current.x, y.current.y);
322
+ },
323
+ [a, w, v, d]
324
+ ), V = j(
325
+ () => {
326
+ y.current = null;
327
+ const s = N.current;
328
+ s && (s.style.fontVariationSettings = h(a));
329
+ for (const l of A.current) l.style.fontVariationSettings = h(a);
330
+ },
331
+ [a]
332
+ );
333
+ return _(() => (window.addEventListener("mousemove", g, { passive: !0 }), window.addEventListener("scroll", i, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", V), () => {
334
+ window.removeEventListener("mousemove", g), window.removeEventListener("scroll", i, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", V);
335
+ }), [g, i, V]), /* @__PURE__ */ Q(
336
+ r,
337
+ {
338
+ ref: M,
339
+ className: f,
340
+ style: { fontVariationSettings: h(a), ...c },
341
+ children: o
342
+ }
219
343
  );
220
- return /* @__PURE__ */ G(n, { ref: d, className: c, style: o, children: e });
221
344
  }
222
345
  );
223
- et.displayName = "MagnetTypeText";
346
+ ct.displayName = "MagnetBlock";
224
347
  export {
225
- V as MAGNET_TYPE_CLASSES,
226
- et as MagnetTypeText,
227
- Q as applyMagnetType,
228
- z as getCleanHTML,
229
- rt as removeMagnetType,
230
- Z as startMagnetType,
231
- tt as useMagnetType
348
+ I as MAGNET_TYPE_CLASSES,
349
+ ct as MagnetBlock,
350
+ st as MagnetTypeText,
351
+ nt as applyMagnetType,
352
+ et as getCleanHTML,
353
+ ut as removeMagnetType,
354
+ ot as startMagnetType,
355
+ rt as useMagnetType
232
356
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/magnettype",
3
- "version": "1.0.8",
3
+ "version": "1.1.1",
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",
@@ -38,8 +38,8 @@
38
38
  "devDependencies": {
39
39
  "@types/react": "^19.0.0",
40
40
  "@vitejs/plugin-react": "^4.0.0",
41
- "happy-dom": "^12.0.0",
42
- "next": "16.2.2",
41
+ "happy-dom": "^20.9.0",
42
+ "next": "^16.2.6",
43
43
  "react": "^19.0.0",
44
44
  "typescript": "^5.0.0",
45
45
  "vite": "^6.0.0",
@@ -83,5 +83,8 @@
83
83
  "sideEffects": false,
84
84
  "publishConfig": {
85
85
  "access": "public"
86
+ },
87
+ "overrides": {
88
+ "postcss": ">=8.5.10"
86
89
  }
87
90
  }