UNPKG

@re-dev/react-truncate

Version:

Provides `Truncate`, `MiddleTruncate` and `ShowMore` React components for truncating multi-line spans and adding an ellipsis.

14 lines (13 loc) 4.93 kB
/** * name: @re-dev/react-truncate * version: v0.5.1 * description: Provides `Truncate`, `MiddleTruncate` and `ShowMore` React components for truncating multi-line spans and adding an ellipsis. * author: chengpeiquan <chengpeiquan@chengpeiquan.com> * homepage: https://truncate.js.org * license: MIT */ "use client";import P,{useCallback as j,useEffect as A,useMemo as G,useRef as O,useState as B}from"react";import $ from"react";var _=e=>e?.offsetWidth,q=e=>e.replace(/\s+$/,""),ee=(e,t,n)=>{if(t===n.length-1)return $.createElement("span",{key:t},e);{let s=$.createElement("br",{key:`${t}br`});return e?[$.createElement("span",{key:t},e),s]:s}},te=(e,t)=>{let n=document.createElement("div"),s="innerText"in window.HTMLElement.prototype?"innerText":"textContent";n.innerHTML=e?.innerHTML.replace(/\r\n|\r|\n/g,t)||"";let i=n[s],o=document.createElement("div");return o.innerHTML="foo<br/>bar",o[s]?.replace(/\r\n|\r/g,` `)!==`foo bar`&&(n.innerHTML=n.innerHTML.replace(/<br.*?[/]?>/gi,` `),i=n[s]),i||""},ne=({end:e,lastLineText:t,fullText:n,targetWidth:s,ellipsisWidth:i,measureWidth:o})=>{let l=t.length,w=Math.abs(e),h=w>l?0:l-w,p=t.slice(0,h),m=h===0?-l:e,y=n.slice(m),H=o(p)+o(y)+i;for(;H>s;)p=p.slice(0,p.length-1),H=o(p)+o(y)+i;return{startFragment:p,endFragment:y}};var J=({children:e,ellipsis:t="\u2026",lines:n=1,trimWhitespace:s=!1,width:i=0,separator:o=" ",middle:l=!1,end:w=5,onTruncate:h,...p})=>{let[m,y]=B(),[H,I]=B(),[u,z]=B(0),[N,k]=B(0),d=O(null),a=O(null),E=O(null);A(()=>{a&&a.current&&a.current.parentNode&&a.current.parentNode.removeChild(a.current)},[u]);let M=j(()=>{if(!d.current?.parentElement)return;let r=i||Math.floor(d.current.parentElement.getBoundingClientRect().width);if(!r)return window.requestAnimationFrame(()=>M());let c=window.getComputedStyle(d.current),F=[c.fontWeight,c.fontStyle,c.fontSize,c.fontFamily].join(" ");m&&(m.font=F,m.letterSpacing=c.letterSpacing),z(r)},[m,i]);A(()=>{let r=document.createElement("canvas");y(r.getContext("2d"))},[]),A(()=>(M(),window.addEventListener("resize",M),()=>{window.removeEventListener("resize",M),window.cancelAnimationFrame(N)}),[M,N]);let S=j(r=>{typeof h=="function"&&k(window.requestAnimationFrame(()=>{h(r)}))},[h]),W=j(r=>m?.measureText(r).width||0,[m]),U=G(()=>!Number.isSafeInteger(n)||n<0?0:n,[n]),v=G(()=>l?1:U,[U,l]),D=G(()=>{let r=Math.abs(w),c=Number.isFinite(r)?Math.floor(r):0;return c>0?-c:c},[w]),Q=j(()=>{let r=[],c=te(a.current,o),F=c.split(` `).map(L=>L.split(o)),Y=_(E.current)||0,Z=!0;for(let L=1;L<=v;L++){let R=F[0];if(R.length===0){r.push(),F.shift(),L--;continue}let C=R.join(o)||"";if(W(C)<=u&&F.length===1){Z=!1,r.push(C);break}if(L===v){let f=R.join(o),x=0,b=f.length-1;for(;x<=b;){let g=Math.floor((x+b)/2),V=f.slice(0,g+1);W(V)+Y<=u?x=g+1:b=g-1}let T=f.slice(0,x);if(s)for(T=q(T);!T.length&&r.length;){let g=r.pop();g&&typeof g=="string"&&(T=q(g))}if(l&&D!==0){let{startFragment:g,endFragment:V}=ne({end:D,lastLineText:T,fullText:c,targetWidth:u,ellipsisWidth:Y,measureWidth:W});C=P.createElement("span",null,g,t,V)}else C=P.createElement("span",null,T,t)}else{let f=0,x=R.length-1;for(;f<=x;){let b=Math.floor((f+x)/2),T=R.slice(0,b+1).join(o);W(T)<=u?f=b+1:x=b-1}if(f===0){L=v-1;continue}C=R.slice(0,f).join(o),F[0].splice(0,f)}r.push(C)}return S(Z),r},[o,S,v,W,u,s,D,l,t]);return A(()=>{let r=!!(d.current&&u);typeof window<"u"&&r&&(v>0?I(Q().map(ee)):(I(e),S(!1)))},[e,v,u,Q,S]),P.createElement("span",{...p,ref:d},P.createElement("span",null,H),P.createElement("span",{ref:a},e),P.createElement("span",{ref:E,style:{position:"fixed",visibility:"hidden",top:0,left:0}},t))};import re,{forwardRef as ie}from"react";var le=ie(({children:e,...t},n)=>{let{width:s,middle:i,lines:o,...l}=t;return re.createElement("div",{ref:n,style:{width:"100%"}},re.createElement(J,{...l,middle:!0},e))});le.displayName="MiddleTruncate";import X,{forwardRef as pe,useCallback as se,useImperativeHandle as ue,useRef as fe,useState as me}from"react";import oe,{isValidElement as ae,useMemo as ce}from"react";var K=({type:e,label:t,className:n,toggleLines:s})=>{let i=ce(()=>e==="more"?"\u2026 ":" ",[e]);return ae(t)?t:oe.createElement("span",null,i,oe.createElement("a",{href:"#",className:n,onClick:s,"data-testid":`${e}-button`},t))};var de=pe(({defaultExpanded:e=!1,expanded:t,lines:n=3,more:s="Expand",less:i="Collapse",anchorClass:o,onToggle:l,children:w,...h},p)=>{let{width:m,middle:y,end:H,ellipsis:I,...u}=h,[,z]=me({}),N=se(()=>z({}),[]),k=fe(e),d=typeof t=="boolean",a=d?t:k.current,E=se(M=>{M.preventDefault();let S=!a;d||(k.current=S,N()),l?.(S)},[a,d,l,N]);return ue(p,()=>({toggleLines:E})),X.createElement("div",{style:{width:"100%"}},X.createElement(J,{...u,lines:a?0:n,ellipsis:X.createElement(K,{type:"more",label:s,className:o,toggleLines:E})},w),a&&X.createElement(K,{type:"less",label:i,className:o,toggleLines:E}))});de.displayName="ShowMore";export{le as MiddleTruncate,de as ShowMore,J as Truncate};