@liiift-studio/magnettype 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # magnetType
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/%40liiift-studio%2Fmagnettype.svg)](https://www.npmjs.com/package/@liiift-studio/magnettype) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![part of liiift type-tools](https://img.shields.io/badge/liiift-type--tools-blueviolet)](https://github.com/Liiift-Studio/type-tools)
4
+
3
5
  CSS `font-variation-settings` applies a single value to the whole element — there is no native way to drive axis values per word from cursor proximity, or to selectively widen visually confusable characters for legibility. magnetType adds both.
4
6
 
5
7
  **[magnettype.com](https://magnettype.com)** · [npm](https://www.npmjs.com/package/@liiift-studio/magnettype) · [GitHub](https://github.com/Liiift-Studio/MagnetType)
@@ -138,6 +140,7 @@ const legibilityOpts: MagnetTypeOptions = {
138
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 |
139
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` |
140
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 |
141
144
  | `as` | `'p'` | HTML element to render, e.g. `'h1'`, `'div'`, `'span'`. Accepts any valid React element type. *(React component only)* |
142
145
 
143
146
  ---
@@ -185,12 +188,10 @@ The package itself has zero runtime dependencies. Do not remove this entry.
185
188
 
186
189
  ## Future improvements
187
190
 
188
- - **Touch support** — `touchmove` events for mobile field mode; currently `mousemove`-only, so the effect is desktop-exclusive
189
- - **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
190
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
191
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
192
193
  - **SSR hydration** — pre-render legibility mode markup on the server so boosted characters are present from first paint without a client-side flash
193
194
 
194
195
  ---
195
196
 
196
- 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"},Y={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 f=new RegExp(`(["'])${o}\\1\\s+[\\d.eE+-]+`),c=`"${o}" ${r}`;return f.test(t)?t.replace(f,c):`${t}, ${c}`}function J(t,o){let r=t;for(const[f,c]of Object.entries(o))r=H(r,f,c);return r}function U(t,o,r){if(o.opacity!==void 0){const[f,c]=o.opacity;t.style.opacity=String(f+(c-f)*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 u=c.parentNode;if(u){for(;c.firstChild;)u.insertBefore(c.firstChild,c);u.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 f=r.wdthBoost??Y.wdthBoost,c=r.radius??Y.radius,u=r.falloff??Y.falloff,T=r.scope??Y.scope,v=r.props,m=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 e of A){const n=e.textContent??"";if(!n||!n.split("").some(a=>a in G))continue;const h=document.createDocumentFragment();for(const a of n){const B=G[a];if(B===void 0){const E=h.lastChild;E&&E.nodeType===Node.TEXT_NODE?E.textContent+=a:h.appendChild(document.createTextNode(a))}else{const E=document.createElement("span");E.className=R.char,E.style.fontVariationSettings=H(L,"wdth",y),E.textContent=a,h.appendChild(E),w.push({span:E,riskLevel:B})}}e.parentNode.replaceChild(h,e)}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:e})=>P(e,v));let p=-9999,q=-9999,g=!1,i=0,V=!0,s=null;function d(){if(!V)return;if(!g){w.forEach(({span:n})=>{m>0&&(n.style.transition=`font-variation-settings ${m}ms ease`),n.style.fontVariationSettings=H(L,"wdth",y),v&&P(n,v)}),m>0&&(s!==null&&clearTimeout(s),s=setTimeout(()=>{w.forEach(({span:n})=>{n.style.transition=""}),s=null},m)),i=0;return}const e=w.map(({span:n})=>n.getBoundingClientRect());w.forEach(({span:n,riskLevel:x},h)=>{n.style.transition="";const a=e[h],B=a.left+a.width/2,E=a.top+a.height/2,j=Math.sqrt((p-B)**2+(q-E)**2),O=Math.max(0,1-j/c),$=u==="quadratic"?O*O:O,X=f*(x/3)*$;n.style.fontVariationSettings=H(L,"wdth",y+X),v&&U(n,v,$)}),i=requestAnimationFrame(d)}function N(e){p=e.clientX,q=e.clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(d))}function C(){g=!1,i===0&&(i=requestAnimationFrame(d))}function F(e){e.touches.length!==0&&(p=e.touches[0].clientX,q=e.touches[0].clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(d)))}function k(){g=!1,i===0&&(i=requestAnimationFrame(d))}const l=T==="document"?document:t;return l.addEventListener("mousemove",N),l.addEventListener("mouseleave",C),l.addEventListener("touchmove",F,{passive:!0}),l.addEventListener("touchend",k),()=>{V=!1,cancelAnimationFrame(i),s!==null&&clearTimeout(s),l.removeEventListener("mousemove",N),l.removeEventListener("mouseleave",C),l.removeEventListener("touchmove",F),l.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 f=r.axes??Y.axes,c=r.radius??Y.radius,u=r.falloff??Y.falloff,T=r.magnetMode??Y.magnetMode,v=r.scope??Y.scope,m=r.props,S=r.transitionMs??0,L=window.scrollY;t.innerHTML=o;const b=_(t),y=[];for(const e of b){const n=e.textContent??"";if(!n.trim())continue;const x=n.split(/(\S+)/),h=document.createDocumentFragment();for(let a=0;a<x.length;a+=2){const B=x[a],E=x[a+1];if(!E)continue;const O=x[a+3]===void 0?x[a+2]??"":"",$=document.createElement("span");$.className=R.word,$.textContent=B+E+O,h.appendChild($),y.push($)}e.parentNode.replaceChild(h,e)}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(f).map(([e,[n]])=>[e,n])));y.forEach(e=>{e.style.fontVariationSettings=w,m&&P(e,m)});let p=-9999,q=-9999,g=!1,i=0,V=!0,s=null;function d(){if(!V)return;if(!g){y.forEach(n=>{S>0&&(n.style.transition=`font-variation-settings ${S}ms ease`),n.style.fontVariationSettings=w,m&&P(n,m)}),S>0&&(s!==null&&clearTimeout(s),s=setTimeout(()=>{y.forEach(n=>{n.style.transition=""}),s=null},S)),i=0;return}const e=y.map(n=>n.getBoundingClientRect());y.forEach((n,x)=>{n.style.transition="";const h=e[x],a=h.left+h.width/2,B=h.top+h.height/2,E=Math.sqrt((p-a)**2+(q-B)**2),j=Math.max(0,1-E/c),O=u==="quadratic"?j*j:j,$=T==="repel"?1-O:O,X={};for(const D of Object.keys(f)){const[I,nt]=f[D]??[300,500];X[D]=I+(nt-I)*$}n.style.fontVariationSettings=J(A,X),m&&U(n,m,O)}),i=requestAnimationFrame(d)}function N(e){p=e.clientX,q=e.clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(d))}function C(){g=!1,i===0&&(i=requestAnimationFrame(d))}function F(e){e.touches.length!==0&&(p=e.touches[0].clientX,q=e.touches[0].clientY,g||(g=!0),i===0&&(i=requestAnimationFrame(d)))}function k(){g=!1,i===0&&(i=requestAnimationFrame(d))}const l=v==="document"?document:t;return l.addEventListener("mousemove",N),l.addEventListener("mouseleave",C),l.addEventListener("touchmove",F,{passive:!0}),l.addEventListener("touchend",k),()=>{V=!1,cancelAnimationFrame(i),s!==null&&clearTimeout(s),l.removeEventListener("mousemove",N),l.removeEventListener("mouseleave",C),l.removeEventListener("touchmove",F),l.removeEventListener("touchend",k),t.innerHTML=o}}function W(t){const o=M.useRef(null),r=M.useRef(null),f=M.useRef(t);f.current=t;const c=M.useRef(null),u=t.mode??"field",{axes:T,radius:v,falloff:m,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),(f.current.mode??"field")==="field"?c.current=Z(p,r.current,f.current):c.current=Q(p,r.current,f.current)},[u,y,v,m,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:f,style:c,...u},T){const v=W(u),m=M.useCallback(S=>{v.current=S,typeof T=="function"?T(S):T&&(T.current=S)},[T]);return K.jsx(r,{ref:m,className:f,style:c,children:o})});tt.displayName="MagnetTypeText";const et=M.forwardRef(function({children:o,as:r="p",className:f,style:c,minWeight:u=300,maxWeight:T=600,proximityRadius:v,spreadRadius:m,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 d=[`'wght' ${s.toFixed(0)}`];for(const[N,C]of Object.entries(S))d.push(`'${N}' ${C}`);return d.join(", ")}M.useEffect(()=>{var k;if(!m)return;const s=b.current;if(!s)return;const d=[],N=document.createTreeWalker(s,NodeFilter.SHOW_TEXT),C=[];let F;for(;F=N.nextNode();)C.push(F);for(const l of C){const e=l.textContent??"";if(!/\S/.test(e))continue;const n=e.split(/(\s+)/),x=document.createDocumentFragment();for(const h of n)if(h!=="")if(/^\s+$/.test(h))x.appendChild(document.createTextNode(h));else{const a=document.createElement("span");a.style.fontVariationSettings=p(u),a.textContent=h,d.push(a),x.appendChild(a)}(k=l.parentNode)==null||k.replaceChild(x,l)}A.current=d},[]);function q(s,d){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-d,0,d-C.bottom),l=Math.sqrt(F*F+k*k);if(v!==void 0&&!m){const n=1-(1-Math.max(0,1-l/v))**2;N.style.fontVariationSettings=p(u+(T-u)*n);return}if(m){if(v!==void 0&&l>v){N.style.fontVariationSettings=p(u);for(const e of A.current)e.style.fontVariationSettings=p(u);return}for(const e of A.current){const n=e.getBoundingClientRect(),x=(n.left+n.right)/2,h=(n.top+n.bottom)/2,a=Math.sqrt((s-x)**2+(d-h)**2),E=1-(1-Math.max(0,1-a/m))**2;e.style.fontVariationSettings=p(u+(T-u)*E)}}}const g=M.useCallback(s=>{y.current={x:s.clientX,y:s.clientY},q(s.clientX,s.clientY)},[u,T,v,m]),i=M.useCallback(()=>{y.current&&q(y.current.x,y.current.y)},[u,T,v,m]),V=M.useCallback(()=>{y.current=null;const s=b.current;s&&(s.style.fontVariationSettings=p(u));for(const d of A.current)d.style.fontVariationSettings=p(u)},[u]);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:f,style:{fontVariationSettings:p(u),...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,321 @@ 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 l = new RegExp(`(["'])${o}\\1\\s+[\\d.eE+-]+`), c = `"${o}" ${r}`;
46
+ return l.test(t) ? t.replace(l, 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 [l, c] of Object.entries(o))
51
+ r = D(r, l, 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 [l, c] = o.opacity;
57
+ t.style.opacity = String(l + (c - l) * 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 u = c.parentNode;
70
+ if (u) {
71
+ for (; c.firstChild; ) u.insertBefore(c.firstChild, c);
72
+ u.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 l = r.wdthBoost ?? Y.wdthBoost, c = r.radius ?? Y.radius, u = r.falloff ?? Y.falloff, w = r.scope ?? Y.scope, v = r.props, m = 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 e of A) {
89
+ const n = e.textContent ?? "";
90
+ if (!n || !n.split("").some((a) => a in G)) continue;
91
+ const h = document.createDocumentFragment();
92
+ for (const a of n) {
93
+ const $ = G[a];
94
+ if ($ === void 0) {
95
+ const E = h.lastChild;
96
+ E && E.nodeType === Node.TEXT_NODE ? E.textContent += a : h.appendChild(document.createTextNode(a));
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 = a, h.appendChild(E), M.push({ span: E, riskLevel: $ });
100
100
  }
101
101
  }
102
- r.parentNode.replaceChild(u, r);
102
+ e.parentNode.replaceChild(h, e);
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: e }) => P(e, v));
109
+ let p = -9999, q = -9999, g = !1, i = 0, V = !0, s = null;
110
+ function d() {
111
+ if (!V) return;
112
+ if (!g) {
113
+ M.forEach(({ span: n }) => {
114
+ m > 0 && (n.style.transition = `font-variation-settings ${m}ms ease`), n.style.fontVariationSettings = D(L, "wdth", y), v && P(n, v);
115
+ }), m > 0 && (s !== null && clearTimeout(s), s = setTimeout(() => {
116
+ M.forEach(({ span: n }) => {
117
+ n.style.transition = "";
118
+ }), s = null;
119
+ }, m)), 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 e = M.map(({ span: n }) => n.getBoundingClientRect());
123
+ M.forEach(({ span: n, riskLevel: T }, h) => {
124
+ n.style.transition = "";
125
+ const a = e[h], $ = a.left + a.width / 2, E = a.top + a.height / 2, X = Math.sqrt((p - $) ** 2 + (q - E) ** 2), B = Math.max(0, 1 - X / c), O = u === "quadratic" ? B * B : B, H = l * (T / 3) * O;
126
+ n.style.fontVariationSettings = D(L, "wdth", y + H), v && Z(n, v, O);
127
+ }), i = requestAnimationFrame(d);
123
128
  }
124
- function O(r) {
125
- g = r.clientX, v = r.clientY, M || (M = !0), f === 0 && (f = requestAnimationFrame(N));
129
+ function C(e) {
130
+ p = e.clientX, q = e.clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(d));
126
131
  }
127
- function Y() {
128
- M = !1, f === 0 && (f = requestAnimationFrame(N));
132
+ function S() {
133
+ g = !1, i === 0 && (i = requestAnimationFrame(d));
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(e) {
136
+ e.touches.length !== 0 && (p = e.touches[0].clientX, q = e.touches[0].clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(d)));
137
+ }
138
+ function b() {
139
+ g = !1, i === 0 && (i = requestAnimationFrame(d));
140
+ }
141
+ const f = w === "document" ? document : t;
142
+ return f.addEventListener("mousemove", C), f.addEventListener("mouseleave", S), f.addEventListener("touchmove", F, { passive: !0 }), f.addEventListener("touchend", b), () => {
143
+ V = !1, cancelAnimationFrame(i), s !== null && clearTimeout(s), f.removeEventListener("mousemove", C), f.removeEventListener("mouseleave", S), f.removeEventListener("touchmove", F), f.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 l = r.axes ?? Y.axes, c = r.radius ?? Y.radius, u = r.falloff ?? Y.falloff, w = r.magnetMode ?? Y.magnetMode, v = r.scope ?? Y.scope, m = r.props, x = r.transitionMs ?? 0, L = window.scrollY;
153
+ t.innerHTML = o;
154
+ const N = J(t), y = [];
155
+ for (const e of N) {
156
+ const n = e.textContent ?? "";
157
+ if (!n.trim()) continue;
158
+ const T = n.split(/(\S+)/), h = document.createDocumentFragment();
159
+ for (let a = 0; a < T.length; a += 2) {
160
+ const $ = T[a], E = T[a + 1];
161
+ if (!E) continue;
162
+ const B = T[a + 3] === void 0 ? T[a + 2] ?? "" : "", O = document.createElement("span");
163
+ O.className = I.word, O.textContent = $ + E + B, h.appendChild(O), y.push(O);
153
164
  }
154
- r.parentNode.replaceChild(u, r);
165
+ e.parentNode.replaceChild(h, e);
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(l).map(([e, [n]]) => [e, n]))
163
174
  );
164
- h.forEach((r) => {
165
- r.style.fontVariationSettings = w, d && j(r, d);
175
+ y.forEach((e) => {
176
+ e.style.fontVariationSettings = M, m && P(e, m);
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 p = -9999, q = -9999, g = !1, i = 0, V = !0, s = null;
179
+ function d() {
180
+ if (!V) return;
181
+ if (!g) {
182
+ y.forEach((n) => {
183
+ x > 0 && (n.style.transition = `font-variation-settings ${x}ms ease`), n.style.fontVariationSettings = M, m && P(n, m);
184
+ }), x > 0 && (s !== null && clearTimeout(s), s = setTimeout(() => {
185
+ y.forEach((n) => {
186
+ n.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 e = y.map((n) => n.getBoundingClientRect());
192
+ y.forEach((n, T) => {
193
+ n.style.transition = "";
194
+ const h = e[T], a = h.left + h.width / 2, $ = h.top + h.height / 2, E = Math.sqrt((p - a) ** 2 + (q - $) ** 2), X = Math.max(0, 1 - E / c), B = u === "quadratic" ? X * X : X, O = w === "repel" ? 1 - B : B, H = {};
195
+ for (const K of Object.keys(l)) {
196
+ const [U, W] = l[K] ?? [300, 500];
197
+ H[K] = U + (W - U) * O;
182
198
  }
183
- s.style.fontVariationSettings = I(q, R), d && P(s, d, T);
184
- }), f = requestAnimationFrame(N);
199
+ n.style.fontVariationSettings = R(A, H), m && Z(n, m, B);
200
+ }), i = requestAnimationFrame(d);
201
+ }
202
+ function C(e) {
203
+ p = e.clientX, q = e.clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(d));
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(d));
188
207
  }
189
- function Y() {
190
- M = !1, f === 0 && (f = requestAnimationFrame(N));
208
+ function F(e) {
209
+ e.touches.length !== 0 && (p = e.touches[0].clientX, q = e.touches[0].clientY, g || (g = !0), i === 0 && (i = requestAnimationFrame(d)));
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(d));
213
+ }
214
+ const f = v === "document" ? document : t;
215
+ return f.addEventListener("mousemove", C), f.addEventListener("mouseleave", S), f.addEventListener("touchmove", F, { passive: !0 }), f.addEventListener("touchend", b), () => {
216
+ V = !1, cancelAnimationFrame(i), s !== null && clearTimeout(s), f.removeEventListener("mousemove", C), f.removeEventListener("mouseleave", S), f.removeEventListener("touchmove", F), f.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), l = k(t);
221
+ l.current = t;
222
+ const c = k(null), u = t.mode ?? "field", { axes: w, radius: v, falloff: m, 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 p = o.current;
224
+ if (!p) return;
225
+ r.current === null && (r.current = et(p)), c.current && (c.current(), c.current = null), (l.current.mode ?? "field") === "field" ? c.current = ot(p, r.current, l.current) : c.current = nt(p, r.current, l.current);
226
+ }, [u, y, v, m, 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: l, style: c, ...u }, w) {
235
+ const v = rt(u), m = 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: m, className: l, 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: l,
251
+ style: c,
252
+ minWeight: u = 300,
253
+ maxWeight: w = 600,
254
+ proximityRadius: v,
255
+ spreadRadius: m,
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 p(s) {
266
+ const d = [`'wght' ${s.toFixed(0)}`];
267
+ for (const [C, S] of Object.entries(x)) d.push(`'${C}' ${S}`);
268
+ return d.join(", ");
269
+ }
270
+ _(() => {
271
+ var b;
272
+ if (!m) return;
273
+ const s = N.current;
274
+ if (!s) return;
275
+ const d = [], C = document.createTreeWalker(s, NodeFilter.SHOW_TEXT), S = [];
276
+ let F;
277
+ for (; F = C.nextNode(); ) S.push(F);
278
+ for (const f of S) {
279
+ const e = f.textContent ?? "";
280
+ if (!/\S/.test(e)) continue;
281
+ const n = e.split(/(\s+)/), T = document.createDocumentFragment();
282
+ for (const h of n)
283
+ if (h !== "")
284
+ if (/^\s+$/.test(h))
285
+ T.appendChild(document.createTextNode(h));
286
+ else {
287
+ const a = document.createElement("span");
288
+ a.style.fontVariationSettings = p(u), a.textContent = h, d.push(a), T.appendChild(a);
289
+ }
290
+ (b = f.parentNode) == null || b.replaceChild(T, f);
291
+ }
292
+ A.current = d;
293
+ }, []);
294
+ function q(s, d) {
295
+ const C = N.current;
296
+ if (!C) return;
297
+ const S = C.getBoundingClientRect(), F = Math.max(S.left - s, 0, s - S.right), b = Math.max(S.top - d, 0, d - S.bottom), f = Math.sqrt(F * F + b * b);
298
+ if (v !== void 0 && !m) {
299
+ const n = 1 - (1 - Math.max(0, 1 - f / v)) ** 2;
300
+ C.style.fontVariationSettings = p(u + (w - u) * n);
301
+ return;
302
+ }
303
+ if (m) {
304
+ if (v !== void 0 && f > v) {
305
+ C.style.fontVariationSettings = p(u);
306
+ for (const e of A.current) e.style.fontVariationSettings = p(u);
307
+ return;
308
+ }
309
+ for (const e of A.current) {
310
+ const n = e.getBoundingClientRect(), T = (n.left + n.right) / 2, h = (n.top + n.bottom) / 2, a = Math.sqrt((s - T) ** 2 + (d - h) ** 2), E = 1 - (1 - Math.max(0, 1 - a / m)) ** 2;
311
+ e.style.fontVariationSettings = p(u + (w - u) * E);
312
+ }
313
+ }
314
+ }
315
+ const g = j(
316
+ (s) => {
317
+ y.current = { x: s.clientX, y: s.clientY }, q(s.clientX, s.clientY);
318
+ },
319
+ [u, w, v, m]
320
+ ), i = j(
321
+ () => {
322
+ y.current && q(y.current.x, y.current.y);
323
+ },
324
+ [u, w, v, m]
325
+ ), V = j(
326
+ () => {
327
+ y.current = null;
328
+ const s = N.current;
329
+ s && (s.style.fontVariationSettings = p(u));
330
+ for (const d of A.current) d.style.fontVariationSettings = p(u);
331
+ },
332
+ [u]
333
+ );
334
+ return _(() => (window.addEventListener("mousemove", g, { passive: !0 }), window.addEventListener("scroll", i, { passive: !0, capture: !0 }), document.documentElement.addEventListener("mouseleave", V), () => {
335
+ window.removeEventListener("mousemove", g), window.removeEventListener("scroll", i, { capture: !0 }), document.documentElement.removeEventListener("mouseleave", V);
336
+ }), [g, i, V]), /* @__PURE__ */ Q(
337
+ r,
338
+ {
339
+ ref: M,
340
+ className: l,
341
+ style: { fontVariationSettings: p(u), ...c },
342
+ children: o
343
+ }
219
344
  );
220
- return /* @__PURE__ */ G(n, { ref: d, className: c, style: o, children: e });
221
345
  }
222
346
  );
223
- et.displayName = "MagnetTypeText";
347
+ ct.displayName = "MagnetBlock";
224
348
  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
349
+ I as MAGNET_TYPE_CLASSES,
350
+ ct as MagnetBlock,
351
+ st as MagnetTypeText,
352
+ nt as applyMagnetType,
353
+ et as getCleanHTML,
354
+ ut as removeMagnetType,
355
+ ot as startMagnetType,
356
+ rt as useMagnetType
232
357
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/magnettype",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Cursor-field per-word variable font axis variation and per-character legibility mode",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -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
  }