@ohkit/text-ellipsis 0.0.7 → 0.0.9

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/dist/index.css CHANGED
@@ -1,2 +1,2 @@
1
- .ohkit-text-ellipsis__container{align-items:flex-start;display:flex;overflow:hidden;position:relative}.ohkit-text-ellipsis__container .content-shadow-dom{overflow-wrap:break-word;pointer-events:none;position:absolute;visibility:hidden;word-break:break-word;z-index:-1}.ohkit-text-ellipsis__container .text-ellipsis-inner{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;overflow-wrap:break-word;position:relative;text-overflow:ellipsis;word-break:break-word}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper{align-items:center;bottom:0;color:#4c84ff;cursor:pointer;display:flex;justify-content:center;position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper:hover{color:#709bff}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-right{padding-left:24px;right:0}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom{width:100%}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-left{justify-content:flex-start}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-center{justify-content:center}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-right{justify-content:flex-end}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper .btn-fold{display:inline-block}
1
+ .ohkit-text-ellipsis__container{align-items:flex-start;display:flex;overflow:hidden;position:relative}.ohkit-text-ellipsis__container .content-shadow-dom{overflow-wrap:break-word;pointer-events:none;position:absolute;visibility:hidden;word-break:break-word;z-index:-1}.ohkit-text-ellipsis__container .text-ellipsis-inner{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;overflow-wrap:break-word;position:relative;text-overflow:ellipsis;word-break:break-word}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-right-shadow{padding-left:12px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;visibility:hidden}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper{align-items:center;bottom:0;color:#4c84ff;cursor:pointer;display:flex;justify-content:center;position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper:hover{color:#709bff}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-right{padding-left:12px;right:0}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom{width:100%}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-left{justify-content:flex-start}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-center{justify-content:center}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-right{justify-content:flex-end}.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper .btn-fold{display:inline-block}
2
2
  /*# sourceMappingURL=index.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,gCAEE,sBAAuB,CADvB,YAAa,CAEb,eAAgB,CAChB,iBACF,CACA,oDAKE,wBAAyB,CAFzB,mBAAoB,CADpB,iBAAkB,CADlB,iBAAkB,CAKlB,qBAAsB,CAFtB,UAGF,CACA,qDAKE,2BAA4B,CAF5B,mBAAoB,CAFpB,eAAgB,CAKhB,wBAAyB,CAJzB,iBAAkB,CAElB,sBAAuB,CAGvB,qBACF,CACA,uEAKE,kBAAmB,CAHnB,QAAS,CAKT,aAAc,CAEd,cAAe,CALf,YAAa,CAEb,sBAAuB,CALvB,iBAAkB,CAOlB,wBAAiB,CAAjB,qBAAiB,CAAjB,gBAAiB,CALjB,SAOF,CACA,6EACE,aACF,CACA,6EAEE,iBAAkB,CADlB,OAEF,CACA,8EACE,UACF,CACA,6FACE,0BACF,CACA,+FACE,sBACF,CACA,8FACE,wBACF,CACA,iFACE,oBACF","file":"index.css","sourcesContent":[".ohkit-text-ellipsis__container {\n display: flex;\n align-items: flex-start;\n overflow: hidden;\n position: relative;\n}\n.ohkit-text-ellipsis__container .content-shadow-dom {\n visibility: hidden;\n position: absolute;\n pointer-events: none;\n z-index: -1;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner {\n overflow: hidden;\n position: relative;\n display: -webkit-box;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper {\n position: absolute;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #4c84ff;\n user-select: none;\n cursor: pointer;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper:hover {\n color: #709bff;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-right {\n right: 0;\n padding-left: 24px;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom {\n width: 100%;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-left {\n justify-content: flex-start;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-center {\n justify-content: center;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-right {\n justify-content: flex-end;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper .btn-fold {\n display: inline-block;\n}"]}
1
+ {"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,gCAEE,sBAAuB,CADvB,YAAa,CAEb,eAAgB,CAChB,iBACF,CACA,oDAKE,wBAAyB,CAFzB,mBAAoB,CADpB,iBAAkB,CADlB,iBAAkB,CAKlB,qBAAsB,CAFtB,UAGF,CACA,qDAKE,2BAA4B,CAF5B,mBAAoB,CAFpB,eAAgB,CAKhB,wBAAyB,CAJzB,iBAAkB,CAElB,sBAAuB,CAGvB,qBACF,CACA,4EAIE,iBAAkB,CAFlB,mBAAoB,CACpB,wBAAiB,CAAjB,qBAAiB,CAAjB,gBAAiB,CAFjB,iBAIF,CACA,uEAKE,kBAAmB,CAHnB,QAAS,CAKT,aAAc,CAEd,cAAe,CALf,YAAa,CAEb,sBAAuB,CALvB,iBAAkB,CAOlB,wBAAiB,CAAjB,qBAAiB,CAAjB,gBAAiB,CALjB,SAOF,CACA,6EACE,aACF,CACA,6EAEE,iBAAkB,CADlB,OAEF,CACA,8EACE,UACF,CACA,6FACE,0BACF,CACA,+FACE,sBACF,CACA,8FACE,wBACF,CACA,iFACE,oBACF","file":"index.css","sourcesContent":[".ohkit-text-ellipsis__container {\n display: flex;\n align-items: flex-start;\n overflow: hidden;\n position: relative;\n}\n.ohkit-text-ellipsis__container .content-shadow-dom {\n visibility: hidden;\n position: absolute;\n pointer-events: none;\n z-index: -1;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner {\n overflow: hidden;\n position: relative;\n display: -webkit-box;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-right-shadow {\n visibility: hidden;\n pointer-events: none;\n user-select: none;\n padding-left: 12px;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper {\n position: absolute;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #4c84ff;\n user-select: none;\n cursor: pointer;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper:hover {\n color: #709bff;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-right {\n right: 0;\n padding-left: 12px;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom {\n width: 100%;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-left {\n justify-content: flex-start;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-center {\n justify-content: center;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper-bottom.placement-right {\n justify-content: flex-end;\n}\n.ohkit-text-ellipsis__container .text-ellipsis-inner .btn-fold-wrapper .btn-fold {\n display: inline-block;\n}"]}
package/dist/index.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import t,{forwardRef as e,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as a,useSyncPropsState as d,useRuntime as s,classNames as f,useCompatibleEffect as c,isSafari as u,assignRef as h}from"@ohkit/utils";import{Measure as v}from"@ohkit/measure";function p(){return p=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)({}).hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},p.apply(null,arguments)}var g=a("ohkit-text-ellipsis__"),m=e(function(e,a){var m=e.className,C=e.style,x=e.lineHeight,w=void 0===x?"":x,F=e.lines,H=e.maskBgColor,b=void 0===H?"#fff":H,E=e.resetFoldWhenChildrenOrEllipsisChange,y=void 0!==E&&E,O=e.showTitleWhenFold,k=e.titleWhenFold,M=e.showFoldControl,W=void 0===M||M,B=e.foldText,S=void 0===B?"收起":B,L=e.unfoldText,N=void 0===L?"展开":L,P=e.uiType,T=void 0===P?"right":P,R=e.truncateMode,j=void 0===R?"line":R,q=e.maxHeight,z=e.controlPlacement,A=void 0===z?"center":z,_=e.whiteSpace,V=e.width,D=e.renderFoldButton,G=e.onEllipsisChange,I=e.onFoldChange,J=e.onStatusChange,K=e.onMouseEnter,Q=e.onMouseLeave,U=e.onPointerEnter,X=e.onPointerLeave,Y=e.onClick,Z=e.onFocus,$=e.content||e.children,tt=n(!1),et=tt[0],nt=tt[1],ot=n(!1),it=ot[0],lt=ot[1],rt=d(e,"fold",{defaultValue:!0,onChange:I}),at=rt[0],dt=rt[1],st=n(1),ft=st[0],ct=st[1],ut=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),ht=ut[0],vt=ut[1],pt=n(F),gt=pt[0],mt=void 0===gt?0:gt,Ct=pt[1],xt=n(""),wt=xt[0],Ft=xt[1],Ht=s({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:et,defaultFold:at,fold:at,foldBtnWidth:ft,textContent:wt,onEllipsisChange:G,onFoldChange:I},["onEllipsisChange","fold","onFoldChange"])[0],bt=o(null),Et=o(null),yt=o(null),Ot=o(null),kt=i(function(){return p({lineHeight:it?"1.4":w||void 0},C)},[C,w,it]),Mt=i(function(){return{whiteSpace:_,width:V}},[_,V]),Wt=i(function(){var t="height"===j;if(!et||!(t||mt&&ht))return Mt;var e=!W||"bottom"!==T&&at?void 0:ht+"px";return p({},Mt,{minHeight:!t&&at?(mt-.2)*ht+"px":void 0,WebkitLineClamp:t?void 0:at?mt:void 0,maxHeight:t&&at?q||mt*ht||Ht.containerContentHeight||0:void 0,paddingBottom:e,boxSizing:e?"border-box":void 0})},[mt,ht,et,at,W,T,j,q,Mt]),Bt=i(function(){if(at){var t=ht,e="right"===T?Math.min(t/ft*100,80):60;return{boxSizing:"content-box",height:ht+"px",lineHeight:ht+"px",paddingTop:"bottom"===T?t+"px":void 0,paddingLeft:"right"===T?t+"px":void 0,background:"linear-gradient(to "+T+", "+b+(4===b.length?"0":"00")+", "+b+" "+e+"%, "+b+" 100%)"}}},[ht,b,at,T,ft]),St=l(function(){if(bt.current){var t=bt.current.style.width,e=window.getComputedStyle(bt.current).width;bt.current.style.width=parseFloat(e)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){bt.current&&(bt.current.style.width=t)})}},[]),Lt=l(function(t,e){void 0===e&&(e=!Ht.fold),Ht.fold=e,dt(e)},[]),Nt=i(function(){/*#__PURE__*/return t.createElement("div",{className:f("btn-fold-wrapper","btn-fold-wrapper-"+T,"bottom"===T&&"placement-"+A),style:Bt,ref:Ot,onClick:Lt},D?D(at):/*#__PURE__*/t.createElement("div",{className:"btn-fold"},at?N:S))},[Bt,at,S,Lt,D,A,T,N]),Pt=l(function(t,e){void 0===t&&(t=Ht.ellipsis);var n=(void 0===e?{}:e).forceResetFold,o=void 0!==n&&n,i=Ht.ellipsis,l=Ht.fold,r=Ht.defaultFold;t!==i&&(nt(t),Ht.ellipsis=t,null==Ht.onEllipsisChange||Ht.onEllipsisChange(t)),y&&(o||!i&&t)&&l!==r&&Lt(void 0,r)},[Lt,$,y]),Tt=l(function(){var t=Et.current,e=yt.current;if(t&&e){Ht.contentOffsetHeight=t.offsetHeight;var n=window.getComputedStyle(e),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),l=Ht.containerContentHeight=e.clientHeight-o-i,r=0;if(!r&&t){var a=(n||{}).lineHeight;a&&((r=parseFloat(a))||lt(!0))}if(ht===r||(vt(r),r))if("height"===j)Pt(Ht.contentOffsetHeight>(q||l));else if(F)mt!==F&&Ct(F),Pt(Ht.contentOffsetHeight>=(F+1)*Math.floor(r)-1);else if(Ht.contentOffsetHeight>l){var d=Math.floor(l/r)||1;mt!==d&&Ct(d),Pt(!0)}else Pt(!1)}},[F,ht,j,q,Pt]);c(function(){Pt(Ht.ellipsis,{forceResetFold:!0}),Tt()},[Tt,Pt]),r(function(){if(et&&Ot.current){var t=Ot.current.offsetWidth;t!==Ht.foldBtnWidth&&(Ht.foldBtnWidth=t,ct(t))}},[et,N,W]),r(function(){u&&St()},[at,St]);var Rt=l(function(){var t,e=(null==(t=Et.current)?void 0:t.textContent)||"";e!==Ht.textContent&&(Ht.textContent=e,Ft(e))},[]),jt=i(function(){return et&&at?"function"==typeof k?k(wt):k||wt:void 0},[k,et,at,wt]);return r(function(){Ht.inited&&(null==J||J({ellipsis:et,fold:at,title:jt}))},[J,at,et,jt]),r(function(){Ht.inited=!0},[]),/*#__PURE__*/t.createElement("div",{className:f(g("container"),m),style:kt,ref:function(t){h(yt,t),a&&h(a,t)},onMouseEnter:K,onMouseLeave:Q,onPointerEnter:U,onPointerLeave:X,onClick:Y,onFocus:Z},/*#__PURE__*/t.createElement(v,{offset:!0},function(e){var n=e.measureRef,o=(e.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-Ht.contentOffsetHeight)>1&&Tt(),/*#__PURE__*/t.createElement("div",{style:Mt,className:"content-shadow-dom",ref:function(t){h(n,t),h(Et,t),Rt()}},$)}),/*#__PURE__*/t.createElement("div",{className:"text-ellipsis-inner",title:O?jt:void 0,style:Wt,ref:bt},et&&W&&Nt,$))});export{m as TextEllipsis,g as c};
1
+ import t,{forwardRef as e,useState as n,useRef as o,useMemo as i,useCallback as r,useEffect as l}from"react";import{prefixClassname as a,classNames as d}from"@ohkit/prefix-classname";import{useSyncPropsState as s,useRuntime as f,useCompatibleEffect as c,assignRef as u}from"@ohkit/react-helper";import{isSafari as h}from"@ohkit/platform";import{rgbaToObj as p,findEffectiveBgColor as g}from"@ohkit/dom-helper";import{Measure as v}from"@ohkit/measure";function m(){return m=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)({}).hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},m.apply(null,arguments)}var C=a("ohkit-text-ellipsis__"),x=e(function(e,a){var x=e.className,b=e.style,w=e.lineHeight,F=void 0===w?"":w,H=e.lines,E=e.maskBgColor,y=e.resetFoldWhenChildrenOrEllipsisChange,k=void 0!==y&&y,O=e.showTitleWhenFold,M=e.titleWhenFold,W=e.showFoldControl,B=void 0===W||W,N=e.foldText,S=void 0===N?"收起":N,L=e.unfoldText,P=void 0===L?"展开":L,T=e.uiType,R=void 0===T?"right":T,j=e.truncateMode,q=void 0===j?"line":j,z=e.maxHeight,A=e.controlPlacement,_=void 0===A?"center":A,V=e.whiteSpace,D=e.width,G=e.renderFoldButton,I=e.onEllipsisChange,J=e.onFoldChange,K=e.onStatusChange,Q=e.onMouseEnter,U=e.onMouseLeave,X=e.onPointerEnter,Y=e.onPointerLeave,Z=e.onClick,$=e.onFocus,tt=e.content||e.children,et=n(!1),nt=et[0],ot=et[1],it=n(!1),rt=it[0],lt=it[1],at=s(e,"fold",{defaultValue:!0,onChange:J}),dt=at[0],st=at[1],ft=n(1),ct=ft[0],ut=ft[1],ht=n("string"==typeof F&&F.endsWith("px")?parseFloat(F):0),pt=ht[0],gt=ht[1],vt=n(H),mt=vt[0],Ct=void 0===mt?0:mt,xt=vt[1],bt=n(""),wt=bt[0],Ft=bt[1],Ht=f({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:nt,defaultFold:dt,fold:dt,foldBtnWidth:ct,textContent:wt,onEllipsisChange:I,onFoldChange:J},["onEllipsisChange","fold","onFoldChange"])[0],Et=o(null),yt=o(null),kt=o(null),Ot=o(null),Mt=i(function(){return m({lineHeight:rt?"1.4":F||void 0},b)},[b,F,rt]),Wt=i(function(){return{whiteSpace:V,width:D}},[V,D]),Bt=i(function(){var t="height"===q;if(!nt||!(t||Ct&&pt))return Wt;var e=B&&"bottom"===R&&!dt?pt+"px":void 0;return m({},Wt,{minHeight:!t&&dt?(Ct-.2)*pt+"px":void 0,WebkitLineClamp:t?void 0:dt?Ct:void 0,maxHeight:t&&dt?z||Ct*pt||Ht.containerContentHeight||0:void 0,paddingBottom:e,boxSizing:e?"border-box":void 0})},[Ct,pt,nt,dt,B,R,q,z,Wt]),Nt=i(function(){if(!B||!nt)return null;var t=p(E||"")||g(kt.current),e=t.r,n=t.g,o=t.b;return{startColor:"rgba("+e+", "+n+", "+o+", 0.2)",endColor:"rgba("+e+", "+n+", "+o+", 1)"}},[E,nt,B]),St=i(function(){if(dt){var t=pt,e="right"===R?Math.min(t/ct*100,80):60,n=Nt||{},o=n.startColor,i=n.endColor,r=void 0===i?"rgba(255,255,255,1)":i;return{boxSizing:"content-box",height:pt+"px",lineHeight:pt+"px",paddingTop:"bottom"===R?t+"px":void 0,paddingLeft:"right"===R?t+"px":void 0,background:"linear-gradient(to "+R+", "+(void 0===o?"rgba(255,255,255,0.2)":o)+", "+r+" "+e+"%, "+r+" 100%)"}}},[pt,dt,R,ct,Nt]),Lt=r(function(){if(Et.current){var t=Et.current.style.width,e=window.getComputedStyle(Et.current).width;Et.current.style.width=parseFloat(e)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){Et.current&&(Et.current.style.width=t)})}},[]),Pt=r(function(t,e){void 0===e&&(e=!Ht.fold),Ht.fold=e,st(e)},[]),Tt=i(function(){/*#__PURE__*/return t.createElement("div",{className:d("btn-fold-wrapper","btn-fold-wrapper-"+R,"bottom"===R&&"placement-"+_),style:St,ref:Ot,onClick:Pt},G?G(dt):/*#__PURE__*/t.createElement("div",{className:"btn-fold"},dt?P:S))},[St,dt,S,Pt,G,_,R,P]),Rt=i(function(){return!B||"right"!==R||dt?null:/*#__PURE__*/t.createElement("span",{style:St,className:"btn-fold-right-shadow"},G?G(dt):S)},[R,B,dt,St,S,G]),jt=r(function(t,e){void 0===t&&(t=Ht.ellipsis);var n=(void 0===e?{}:e).forceResetFold,o=void 0!==n&&n,i=Ht.ellipsis,r=Ht.fold,l=Ht.defaultFold;t!==i&&(ot(t),Ht.ellipsis=t,null==Ht.onEllipsisChange||Ht.onEllipsisChange(t)),k&&(o||!i&&t)&&r!==l&&Pt(void 0,l)},[Pt,tt,k]),qt=r(function(){var t=yt.current,e=kt.current;if(t&&e){Ht.contentOffsetHeight=t.offsetHeight;var n=window.getComputedStyle(e),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),r=Ht.containerContentHeight=e.clientHeight-o-i,l=0;if(!l&&t){var a=(n||{}).lineHeight;a&&((l=parseFloat(a))||lt(!0))}if(pt===l||(gt(l),l))if("height"===q)jt(Ht.contentOffsetHeight>(z||r));else if(H)Ct!==H&&xt(H),jt(Ht.contentOffsetHeight>=(H+1)*Math.floor(l)-1);else if(Ht.contentOffsetHeight>r){var d=Math.floor(r/l)||1;Ct!==d&&xt(d),jt(!0)}else jt(!1)}},[H,pt,q,z,jt]);c(function(){jt(Ht.ellipsis,{forceResetFold:!0}),qt()},[qt,jt]),l(function(){if(nt&&Ot.current){var t=Ot.current.offsetWidth;t!==Ht.foldBtnWidth&&(Ht.foldBtnWidth=t,ut(t))}},[nt,P,B]),l(function(){h&&Lt()},[dt,Lt]);var zt=r(function(){var t,e=(null==(t=yt.current)?void 0:t.textContent)||"";e!==Ht.textContent&&(Ht.textContent=e,Ft(e))},[]),At=i(function(){return nt&&dt?"function"==typeof M?M(wt):M||wt:void 0},[M,nt,dt,wt]);return l(function(){Ht.inited&&(null==K||K({ellipsis:nt,fold:dt,title:At}))},[K,dt,nt,At]),l(function(){Ht.inited=!0},[]),/*#__PURE__*/t.createElement("div",{className:d(C("container"),x),style:Mt,ref:function(t){u(kt,t),a&&u(a,t)},onMouseEnter:Q,onMouseLeave:U,onPointerEnter:X,onPointerLeave:Y,onClick:Z,onFocus:$},/*#__PURE__*/t.createElement(v,{offset:!0},function(e){var n=e.measureRef,o=(e.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-Ht.contentOffsetHeight)>1&&qt(),/*#__PURE__*/t.createElement("div",{style:Wt,className:"content-shadow-dom",ref:function(t){u(n,t),u(yt,t),zt()}},tt)}),/*#__PURE__*/t.createElement("div",{className:"text-ellipsis-inner",title:O?At:void 0,style:Bt,ref:Et},nt&&B&&Tt,tt,Rt))});export{x as TextEllipsis,C as c};
2
2
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width])\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" || !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport {\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/react-helper\";\nimport {isSafari} from \"@ohkit/platform\";\nimport {rgbaToObj, findEffectiveBgColor} from \"@ohkit/dom-helper\";\nimport {Measure} from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 折叠按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor,\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width]);\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" && !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 计算折叠按钮蒙层的渐变颜色\n const validMaskBgColor = useMemo(() => {\n if (!showFoldControl || !ellipsis) {\n return null;\n }\n const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);\n return {\n startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,\n endColor: `rgba(${r}, ${g}, ${b}, 1)`,\n };\n }, [maskBgColor, ellipsis, showFoldControl]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,\n };\n }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 占位按钮\n const ButtonShadowDom = useMemo(() => {\n if (!showFoldControl || uiType !== 'right' || fold) {\n return null;\n }\n return <span style={btnStyle} className=\"btn-fold-right-shadow\">\n {renderFoldButton ? renderFoldButton(fold) : foldText}\n </span>;\n }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n {ButtonShadowDom}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","validMaskBgColor","_ref","rgbaToObj","findEffectiveBgColor","current","r","g","b","startColor","endColor","btnStyle","padding","ratio","Math","min","_ref2","_ref2$startColor","_ref2$endColor","height","paddingTop","paddingLeft","background","reorganizeDom","useCallback","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ButtonShadowDom","resetState","newEllipsis","_temp","_ref3","_ref3$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","assignRef","Measure","offset","_ref5","measureRef","contentRect","abs"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=/*#__PURE__*/i(e);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},l.apply(null,arguments)}var a=t.prefixClassname("ohkit-text-ellipsis__"),r=e.forwardRef(function(i,r){var s=i.className,f=i.style,u=i.lineHeight,d=void 0===u?"":u,c=i.lines,h=i.maskBgColor,g=void 0===h?"#fff":h,p=i.resetFoldWhenChildrenOrEllipsisChange,v=void 0!==p&&p,m=i.showTitleWhenFold,C=i.titleWhenFold,x=i.showFoldControl,b=void 0===x||x,w=i.foldText,E=void 0===w?"收起":w,F=i.unfoldText,H=void 0===F?"展开":F,y=i.uiType,M=void 0===y?"right":y,S=i.truncateMode,k=void 0===S?"line":S,R=i.maxHeight,O=i.controlPlacement,W=void 0===O?"center":O,N=i.whiteSpace,B=i.width,P=i.renderFoldButton,T=i.onEllipsisChange,L=i.onFoldChange,q=i.onStatusChange,j=i.onMouseEnter,z=i.onMouseLeave,A=i.onPointerEnter,_=i.onPointerLeave,V=i.onClick,D=i.onFocus,G=i.content||i.children,I=e.useState(!1),J=I[0],K=I[1],Q=e.useState(!1),U=Q[0],X=Q[1],Y=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:L}),Z=Y[0],$=Y[1],ee=e.useState(1),te=ee[0],ne=ee[1],ie=e.useState("string"==typeof d&&d.endsWith("px")?parseFloat(d):0),oe=ie[0],le=ie[1],ae=e.useState(c),re=ae[0],se=void 0===re?0:re,fe=ae[1],ue=e.useState(""),de=ue[0],ce=ue[1],he=t.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:J,defaultFold:Z,fold:Z,foldBtnWidth:te,textContent:de,onEllipsisChange:T,onFoldChange:L},["onEllipsisChange","fold","onFoldChange"])[0],ge=e.useRef(null),pe=e.useRef(null),ve=e.useRef(null),me=e.useRef(null),Ce=e.useMemo(function(){return l({lineHeight:U?"1.4":d||void 0},f)},[f,d,U]),xe=e.useMemo(function(){return{whiteSpace:N,width:B}},[N,B]),be=e.useMemo(function(){var e="height"===k;if(!J||!(e||se&&oe))return xe;var t=!b||"bottom"!==M&&Z?void 0:oe+"px";return l({},xe,{minHeight:!e&&Z?(se-.2)*oe+"px":void 0,WebkitLineClamp:e?void 0:Z?se:void 0,maxHeight:e&&Z?R||se*oe||he.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[se,oe,J,Z,b,M,k,R,xe]),we=e.useMemo(function(){if(Z){var e=oe,t="right"===M?Math.min(e/te*100,80):60;return{boxSizing:"content-box",height:oe+"px",lineHeight:oe+"px",paddingTop:"bottom"===M?e+"px":void 0,paddingLeft:"right"===M?e+"px":void 0,background:"linear-gradient(to "+M+", "+g+(4===g.length?"0":"00")+", "+g+" "+t+"%, "+g+" 100%)"}}},[oe,g,Z,M,te]),Ee=e.useCallback(function(){if(ge.current){var e=ge.current.style.width,t=window.getComputedStyle(ge.current).width;ge.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ge.current&&(ge.current.style.width=e)})}},[]),Fe=e.useCallback(function(e,t){void 0===t&&(t=!he.fold),he.fold=t,$(t)},[]),He=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+M,"bottom"===M&&"placement-"+W),style:we,ref:me,onClick:Fe},P?P(Z):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},Z?H:E))},[we,Z,E,Fe,P,W,M,H]),ye=e.useCallback(function(e,t){void 0===e&&(e=he.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=he.ellipsis,l=he.fold,a=he.defaultFold;e!==o&&(K(e),he.ellipsis=e,null==he.onEllipsisChange||he.onEllipsisChange(e)),v&&(i||!o&&e)&&l!==a&&Fe(void 0,a)},[Fe,G,v]),Me=e.useCallback(function(){var e=pe.current,t=ve.current;if(e&&t){he.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),i=parseFloat(n.paddingTop),o=parseFloat(n.paddingBottom),l=he.containerContentHeight=t.clientHeight-i-o,a=0;if(!a&&e){var r=(n||{}).lineHeight;r&&((a=parseFloat(r))||X(!0))}if(oe===a||(le(a),a))if("height"===k)ye(he.contentOffsetHeight>(R||l));else if(c)se!==c&&fe(c),ye(he.contentOffsetHeight>=(c+1)*Math.floor(a)-1);else if(he.contentOffsetHeight>l){var s=Math.floor(l/a)||1;se!==s&&fe(s),ye(!0)}else ye(!1)}},[c,oe,k,R,ye]);t.useCompatibleEffect(function(){ye(he.ellipsis,{forceResetFold:!0}),Me()},[Me,ye]),e.useEffect(function(){if(J&&me.current){var e=me.current.offsetWidth;e!==he.foldBtnWidth&&(he.foldBtnWidth=e,ne(e))}},[J,H,b]),e.useEffect(function(){t.isSafari&&Ee()},[Z,Ee]);var Se=e.useCallback(function(){var e,t=(null==(e=pe.current)?void 0:e.textContent)||"";t!==he.textContent&&(he.textContent=t,ce(t))},[]),ke=e.useMemo(function(){return J&&Z?"function"==typeof C?C(de):C||de:void 0},[C,J,Z,de]);return e.useEffect(function(){he.inited&&(null==q||q({ellipsis:J,fold:Z,title:ke}))},[q,Z,J,ke]),e.useEffect(function(){he.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(a("container"),s),style:Ce,ref:function(e){t.assignRef(ve,e),r&&t.assignRef(r,e)},onMouseEnter:j,onMouseLeave:z,onPointerEnter:A,onPointerLeave:_,onClick:V,onFocus:D},/*#__PURE__*/o.default.createElement(n.Measure,{offset:!0},function(e){var n=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-he.contentOffsetHeight)>1&&Me(),/*#__PURE__*/o.default.createElement("div",{style:xe,className:"content-shadow-dom",ref:function(e){t.assignRef(n,e),t.assignRef(pe,e),Se()}},G)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:m?ke:void 0,style:be,ref:ge},J&&b&&He,G))});exports.TextEllipsis=r,exports.c=a;
1
+ var e=require("react"),t=require("@ohkit/prefix-classname"),n=require("@ohkit/react-helper"),o=require("@ohkit/platform"),i=require("@ohkit/dom-helper"),r=require("@ohkit/measure");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=/*#__PURE__*/l(e);function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s.apply(null,arguments)}var u=t.prefixClassname("ohkit-text-ellipsis__"),f=e.forwardRef(function(l,f){var d=l.className,c=l.style,h=l.lineHeight,g=void 0===h?"":h,p=l.lines,v=l.maskBgColor,m=l.resetFoldWhenChildrenOrEllipsisChange,C=void 0!==m&&m,b=l.showTitleWhenFold,x=l.titleWhenFold,w=l.showFoldControl,E=void 0===w||w,F=l.foldText,y=void 0===F?"收起":F,H=l.unfoldText,M=void 0===H?"展开":H,k=l.uiType,S=void 0===k?"right":k,R=l.truncateMode,O=void 0===R?"line":R,N=l.maxHeight,W=l.controlPlacement,q=void 0===W?"center":W,B=l.whiteSpace,T=l.width,P=l.renderFoldButton,L=l.onEllipsisChange,j=l.onFoldChange,z=l.onStatusChange,A=l.onMouseEnter,_=l.onMouseLeave,V=l.onPointerEnter,D=l.onPointerLeave,G=l.onClick,I=l.onFocus,J=l.content||l.children,K=e.useState(!1),Q=K[0],U=K[1],X=e.useState(!1),Y=X[0],Z=X[1],$=n.useSyncPropsState(l,"fold",{defaultValue:!0,onChange:j}),ee=$[0],te=$[1],ne=e.useState(1),oe=ne[0],ie=ne[1],re=e.useState("string"==typeof g&&g.endsWith("px")?parseFloat(g):0),le=re[0],ae=re[1],se=e.useState(p),ue=se[0],fe=void 0===ue?0:ue,de=se[1],ce=e.useState(""),he=ce[0],ge=ce[1],pe=n.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:ee,fold:ee,foldBtnWidth:oe,textContent:he,onEllipsisChange:L,onFoldChange:j},["onEllipsisChange","fold","onFoldChange"])[0],ve=e.useRef(null),me=e.useRef(null),Ce=e.useRef(null),be=e.useRef(null),xe=e.useMemo(function(){return s({lineHeight:Y?"1.4":g||void 0},c)},[c,g,Y]),we=e.useMemo(function(){return{whiteSpace:B,width:T}},[B,T]),Ee=e.useMemo(function(){var e="height"===O;if(!Q||!(e||fe&&le))return we;var t=E&&"bottom"===S&&!ee?le+"px":void 0;return s({},we,{minHeight:!e&&ee?(fe-.2)*le+"px":void 0,WebkitLineClamp:e?void 0:ee?fe:void 0,maxHeight:e&&ee?N||fe*le||pe.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[fe,le,Q,ee,E,S,O,N,we]),Fe=e.useMemo(function(){if(!E||!Q)return null;var e=i.rgbaToObj(v||"")||i.findEffectiveBgColor(Ce.current),t=e.r,n=e.g,o=e.b;return{startColor:"rgba("+t+", "+n+", "+o+", 0.2)",endColor:"rgba("+t+", "+n+", "+o+", 1)"}},[v,Q,E]),ye=e.useMemo(function(){if(ee){var e=le,t="right"===S?Math.min(e/oe*100,80):60,n=Fe||{},o=n.startColor,i=n.endColor,r=void 0===i?"rgba(255,255,255,1)":i;return{boxSizing:"content-box",height:le+"px",lineHeight:le+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+(void 0===o?"rgba(255,255,255,0.2)":o)+", "+r+" "+t+"%, "+r+" 100%)"}}},[le,ee,S,oe,Fe]),He=e.useCallback(function(){if(ve.current){var e=ve.current.style.width,t=window.getComputedStyle(ve.current).width;ve.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ve.current&&(ve.current.style.width=e)})}},[]),Me=e.useCallback(function(e,t){void 0===t&&(t=!pe.fold),pe.fold=t,te(t)},[]),ke=e.useMemo(function(){/*#__PURE__*/return a.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+q),style:ye,ref:be,onClick:Me},P?P(ee):/*#__PURE__*/a.default.createElement("div",{className:"btn-fold"},ee?M:y))},[ye,ee,y,Me,P,q,S,M]),Se=e.useMemo(function(){return!E||"right"!==S||ee?null:/*#__PURE__*/a.default.createElement("span",{style:ye,className:"btn-fold-right-shadow"},P?P(ee):y)},[S,E,ee,ye,y,P]),Re=e.useCallback(function(e,t){void 0===e&&(e=pe.ellipsis);var n=(void 0===t?{}:t).forceResetFold,o=void 0!==n&&n,i=pe.ellipsis,r=pe.fold,l=pe.defaultFold;e!==i&&(U(e),pe.ellipsis=e,null==pe.onEllipsisChange||pe.onEllipsisChange(e)),C&&(o||!i&&e)&&r!==l&&Me(void 0,l)},[Me,J,C]),Oe=e.useCallback(function(){var e=me.current,t=Ce.current;if(e&&t){pe.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),r=pe.containerContentHeight=t.clientHeight-o-i,l=0;if(!l&&e){var a=(n||{}).lineHeight;a&&((l=parseFloat(a))||Z(!0))}if(le===l||(ae(l),l))if("height"===O)Re(pe.contentOffsetHeight>(N||r));else if(p)fe!==p&&de(p),Re(pe.contentOffsetHeight>=(p+1)*Math.floor(l)-1);else if(pe.contentOffsetHeight>r){var s=Math.floor(r/l)||1;fe!==s&&de(s),Re(!0)}else Re(!1)}},[p,le,O,N,Re]);n.useCompatibleEffect(function(){Re(pe.ellipsis,{forceResetFold:!0}),Oe()},[Oe,Re]),e.useEffect(function(){if(Q&&be.current){var e=be.current.offsetWidth;e!==pe.foldBtnWidth&&(pe.foldBtnWidth=e,ie(e))}},[Q,M,E]),e.useEffect(function(){o.isSafari&&He()},[ee,He]);var Ne=e.useCallback(function(){var e,t=(null==(e=me.current)?void 0:e.textContent)||"";t!==pe.textContent&&(pe.textContent=t,ge(t))},[]),We=e.useMemo(function(){return Q&&ee?"function"==typeof x?x(he):x||he:void 0},[x,Q,ee,he]);return e.useEffect(function(){pe.inited&&(null==z||z({ellipsis:Q,fold:ee,title:We}))},[z,ee,Q,We]),e.useEffect(function(){pe.inited=!0},[]),/*#__PURE__*/a.default.createElement("div",{className:t.classNames(u("container"),d),style:xe,ref:function(e){n.assignRef(Ce,e),f&&n.assignRef(f,e)},onMouseEnter:A,onMouseLeave:_,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:I},/*#__PURE__*/a.default.createElement(r.Measure,{offset:!0},function(e){var t=e.measureRef,o=(e.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-pe.contentOffsetHeight)>1&&Oe(),/*#__PURE__*/a.default.createElement("div",{style:we,className:"content-shadow-dom",ref:function(e){n.assignRef(t,e),n.assignRef(me,e),Ne()}},J)}),/*#__PURE__*/a.default.createElement("div",{className:"text-ellipsis-inner",title:b?We:void 0,style:Ee,ref:ve},Q&&E&&ke,J,Se))});exports.TextEllipsis=f,exports.c=u;
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width])\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" || !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport {\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/react-helper\";\nimport {isSafari} from \"@ohkit/platform\";\nimport {rgbaToObj, findEffectiveBgColor} from \"@ohkit/dom-helper\";\nimport {Measure} from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 折叠按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor,\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width]);\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" && !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 计算折叠按钮蒙层的渐变颜色\n const validMaskBgColor = useMemo(() => {\n if (!showFoldControl || !ellipsis) {\n return null;\n }\n const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);\n return {\n startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,\n endColor: `rgba(${r}, ${g}, ${b}, 1)`,\n };\n }, [maskBgColor, ellipsis, showFoldControl]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,\n };\n }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 占位按钮\n const ButtonShadowDom = useMemo(() => {\n if (!showFoldControl || uiType !== 'right' || fold) {\n return null;\n }\n return <span style={btnStyle} className=\"btn-fold-right-shadow\">\n {renderFoldButton ? renderFoldButton(fold) : foldText}\n </span>;\n }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n {ButtonShadowDom}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","validMaskBgColor","_ref","rgbaToObj","findEffectiveBgColor","current","r","g","b","startColor","endColor","btnStyle","padding","ratio","Math","min","_ref2","_ref2$startColor","_ref2$endColor","height","paddingTop","paddingLeft","background","reorganizeDom","useCallback","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ButtonShadowDom","resetState","newEllipsis","_temp","_ref3","_ref3$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","assignRef","Measure","offset","_ref5","measureRef","contentRect","abs"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as s,useSyncPropsState as a,useRuntime as d,classNames as c,useCompatibleEffect as f,isSafari as h,assignRef as u}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";function g(){return g=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},g.apply(null,arguments)}const m=s("ohkit-text-ellipsis__"),v=t((t,s)=>{const{className:v,style:C,lineHeight:x="",lines:w,maskBgColor:F="#fff",content:H,children:b,resetFoldWhenChildrenOrEllipsisChange:E=!1,showTitleWhenFold:y,titleWhenFold:$,showFoldControl:O=!0,foldText:k="收起",unfoldText:M="展开",uiType:W="right",truncateMode:B="line",maxHeight:S,controlPlacement:L="center",whiteSpace:N,width:P,renderFoldButton:T,onEllipsisChange:R,onFoldChange:j,onStatusChange:q,onMouseEnter:z,onMouseLeave:A,onPointerEnter:_,onPointerLeave:V,onClick:D,onFocus:G}=t,I=H||b,[J,K]=n(!1),[Q,U]=n(!1),[X,Y]=a(t,"fold",{defaultValue:!0,onChange:j}),[Z,ee]=n(1),[te,ne]=n("string"==typeof x&&x.endsWith("px")?parseFloat(x):0),[oe=0,ie]=n(w),[le,re]=n(""),[se]=d({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:J,defaultFold:X,fold:X,foldBtnWidth:Z,textContent:le,onEllipsisChange:R,onFoldChange:j},["onEllipsisChange","fold","onFoldChange"]),ae=o(null),de=o(null),ce=o(null),fe=o(null),he=i(()=>g({lineHeight:Q?"1.4":x||void 0},C),[C,x,Q]),ue=i(()=>({whiteSpace:N,width:P}),[N,P]),pe=i(()=>{const e="height"===B;if(!J||!(e||oe&&te))return ue;const t=!O||"bottom"!==W&&X?void 0:`${te}px`;return g({},ue,{minHeight:!e&&X?(oe-.2)*te+"px":void 0,WebkitLineClamp:e?void 0:X?oe:void 0,maxHeight:e&&X?S||oe*te||se.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[oe,te,J,X,O,W,B,S,ue]),ge=i(()=>{if(!X)return;const e=te,t="right"===W?Math.min(e/Z*100,80):60;return{boxSizing:"content-box",height:`${te}px`,lineHeight:`${te}px`,paddingTop:"bottom"===W?`${e}px`:void 0,paddingLeft:"right"===W?`${e}px`:void 0,background:`linear-gradient(to ${W}, ${F}${4===F.length?"0":"00"}, ${F} ${t}%, ${F} 100%)`}},[te,F,X,W,Z]),me=l(()=>{if(ae.current){const e=ae.current.style.width,t=window.getComputedStyle(ae.current).width;ae.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{ae.current&&(ae.current.style.width=e)})}},[]),ve=l((e,t=!se.fold)=>{se.fold=t,Y(t)},[]),Ce=i(()=>/*#__PURE__*/e.createElement("div",{className:c("btn-fold-wrapper",`btn-fold-wrapper-${W}`,"bottom"===W&&`placement-${L}`),style:ge,ref:fe,onClick:ve},T?T(X):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},X?M:k)),[ge,X,k,ve,T,L,W,M]),xe=l((e=se.ellipsis,{forceResetFold:t=!1}={})=>{const{ellipsis:n,fold:o,defaultFold:i}=se;e!==n&&(K(e),se.ellipsis=e,null==se.onEllipsisChange||se.onEllipsisChange(e)),E&&(t||!n&&e)&&o!==i&&ve(void 0,i)},[ve,I,E]),we=l(()=>{const e=de.current,t=ce.current;if(!e||!t)return;se.contentOffsetHeight=e.offsetHeight;const n=window.getComputedStyle(t),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),l=se.containerContentHeight=t.clientHeight-o-i;let r=0;if(!r&&e){const{lineHeight:e}=n||{};e&&(r=parseFloat(e),r||U(!0))}if(te===r||(ne(r),r))if("height"===B)xe(se.contentOffsetHeight>(S||l));else if(w)oe!==w&&ie(w),xe(se.contentOffsetHeight>=(w+1)*Math.floor(r)-1);else if(se.contentOffsetHeight>l){const e=Math.floor(l/r)||1;oe!==e&&ie(e),xe(!0)}else xe(!1)},[w,te,B,S,xe]);f(()=>{xe(se.ellipsis,{forceResetFold:!0}),we()},[we,xe]),r(()=>{if(J&&fe.current){const{offsetWidth:e}=fe.current;e!==se.foldBtnWidth&&(se.foldBtnWidth=e,ee(e))}},[J,M,O]),r(()=>{h&&me()},[X,me]);const Fe=l(()=>{var e;const t=(null==(e=de.current)?void 0:e.textContent)||"";t!==se.textContent&&(se.textContent=t,re(t))},[]),He=i(()=>J&&X?"function"==typeof $?$(le):$||le:void 0,[$,J,X,le]);return r(()=>{se.inited&&(null==q||q({ellipsis:J,fold:X,title:He}))},[q,X,J,He]),r(()=>{se.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:c(m("container"),v),style:he,ref:e=>{u(ce,e),s&&u(s,e)},onMouseEnter:z,onMouseLeave:A,onPointerEnter:_,onPointerLeave:V,onClick:D,onFocus:G},/*#__PURE__*/e.createElement(p,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-se.contentOffsetHeight)>1&&we(),/*#__PURE__*/e.createElement("div",{style:ue,className:"content-shadow-dom",ref:e=>{u(t,e),u(de,e),Fe()}},I)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:y?He:void 0,style:pe,ref:ae},J&&O&&Ce,I))});export{v as TextEllipsis,m as c};
1
+ import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as s,classNames as a}from"@ohkit/prefix-classname";import{useSyncPropsState as d,useRuntime as c,useCompatibleEffect as h,assignRef as f}from"@ohkit/react-helper";import{isSafari as p}from"@ohkit/platform";import{rgbaToObj as g,findEffectiveBgColor as u}from"@ohkit/dom-helper";import{Measure as m}from"@ohkit/measure";function C(){return C=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},C.apply(null,arguments)}const v=s("ohkit-text-ellipsis__"),x=t((t,s)=>{const{className:x,style:b,lineHeight:w="",lines:F,maskBgColor:H,content:$,children:E,resetFoldWhenChildrenOrEllipsisChange:y=!1,showTitleWhenFold:k,titleWhenFold:O,showFoldControl:M=!0,foldText:W="收起",unfoldText:B="展开",uiType:N="right",truncateMode:S="line",maxHeight:L,controlPlacement:P="center",whiteSpace:T,width:R,renderFoldButton:j,onEllipsisChange:q,onFoldChange:z,onStatusChange:A,onMouseEnter:_,onMouseLeave:V,onPointerEnter:D,onPointerLeave:G,onClick:I,onFocus:J}=t,K=$||E,[Q,U]=n(!1),[X,Y]=n(!1),[Z,ee]=d(t,"fold",{defaultValue:!0,onChange:z}),[te,ne]=n(1),[oe,ie]=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),[le=0,re]=n(F),[se,ae]=n(""),[de]=c({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:Z,fold:Z,foldBtnWidth:te,textContent:se,onEllipsisChange:q,onFoldChange:z},["onEllipsisChange","fold","onFoldChange"]),ce=o(null),he=o(null),fe=o(null),pe=o(null),ge=i(()=>C({lineHeight:X?"1.4":w||void 0},b),[b,w,X]),ue=i(()=>({whiteSpace:T,width:R}),[T,R]),me=i(()=>{const e="height"===S;if(!Q||!(e||le&&oe))return ue;const t=M&&"bottom"===N&&!Z?`${oe}px`:void 0;return C({},ue,{minHeight:!e&&Z?(le-.2)*oe+"px":void 0,WebkitLineClamp:e?void 0:Z?le:void 0,maxHeight:e&&Z?L||le*oe||de.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[le,oe,Q,Z,M,N,S,L,ue]),Ce=i(()=>{if(!M||!Q)return null;const{r:e,g:t,b:n}=g(H||"")||u(fe.current);return{startColor:`rgba(${e}, ${t}, ${n}, 0.2)`,endColor:`rgba(${e}, ${t}, ${n}, 1)`}},[H,Q,M]),ve=i(()=>{if(!Z)return;const e=oe,t="right"===N?Math.min(e/te*100,80):60,{startColor:n="rgba(255,255,255,0.2)",endColor:o="rgba(255,255,255,1)"}=Ce||{};return{boxSizing:"content-box",height:`${oe}px`,lineHeight:`${oe}px`,paddingTop:"bottom"===N?`${e}px`:void 0,paddingLeft:"right"===N?`${e}px`:void 0,background:`linear-gradient(to ${N}, ${n}, ${o} ${t}%, ${o} 100%)`}},[oe,Z,N,te,Ce]),xe=l(()=>{if(ce.current){const e=ce.current.style.width,t=window.getComputedStyle(ce.current).width;ce.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{ce.current&&(ce.current.style.width=e)})}},[]),be=l((e,t=!de.fold)=>{de.fold=t,ee(t)},[]),we=i(()=>/*#__PURE__*/e.createElement("div",{className:a("btn-fold-wrapper",`btn-fold-wrapper-${N}`,"bottom"===N&&`placement-${P}`),style:ve,ref:pe,onClick:be},j?j(Z):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},Z?B:W)),[ve,Z,W,be,j,P,N,B]),Fe=i(()=>!M||"right"!==N||Z?null:/*#__PURE__*/e.createElement("span",{style:ve,className:"btn-fold-right-shadow"},j?j(Z):W),[N,M,Z,ve,W,j]),He=l((e=de.ellipsis,{forceResetFold:t=!1}={})=>{const{ellipsis:n,fold:o,defaultFold:i}=de;e!==n&&(U(e),de.ellipsis=e,null==de.onEllipsisChange||de.onEllipsisChange(e)),y&&(t||!n&&e)&&o!==i&&be(void 0,i)},[be,K,y]),$e=l(()=>{const e=he.current,t=fe.current;if(!e||!t)return;de.contentOffsetHeight=e.offsetHeight;const n=window.getComputedStyle(t),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),l=de.containerContentHeight=t.clientHeight-o-i;let r=0;if(!r&&e){const{lineHeight:e}=n||{};e&&(r=parseFloat(e),r||Y(!0))}if(oe===r||(ie(r),r))if("height"===S)He(de.contentOffsetHeight>(L||l));else if(F)le!==F&&re(F),He(de.contentOffsetHeight>=(F+1)*Math.floor(r)-1);else if(de.contentOffsetHeight>l){const e=Math.floor(l/r)||1;le!==e&&re(e),He(!0)}else He(!1)},[F,oe,S,L,He]);h(()=>{He(de.ellipsis,{forceResetFold:!0}),$e()},[$e,He]),r(()=>{if(Q&&pe.current){const{offsetWidth:e}=pe.current;e!==de.foldBtnWidth&&(de.foldBtnWidth=e,ne(e))}},[Q,B,M]),r(()=>{p&&xe()},[Z,xe]);const Ee=l(()=>{var e;const t=(null==(e=he.current)?void 0:e.textContent)||"";t!==de.textContent&&(de.textContent=t,ae(t))},[]),ye=i(()=>Q&&Z?"function"==typeof O?O(se):O||se:void 0,[O,Q,Z,se]);return r(()=>{de.inited&&(null==A||A({ellipsis:Q,fold:Z,title:ye}))},[A,Z,Q,ye]),r(()=>{de.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:a(v("container"),x),style:ge,ref:e=>{f(fe,e),s&&f(s,e)},onMouseEnter:_,onMouseLeave:V,onPointerEnter:D,onPointerLeave:G,onClick:I,onFocus:J},/*#__PURE__*/e.createElement(m,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-de.contentOffsetHeight)>1&&$e(),/*#__PURE__*/e.createElement("div",{style:ue,className:"content-shadow-dom",ref:e=>{f(t,e),f(he,e),Ee()}},K)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:k?ye:void 0,style:me,ref:ce},Q&&M&&we,K,Fe))});export{x as TextEllipsis,v as c};
2
2
  //# sourceMappingURL=index.modern.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width])\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" || !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","style","lineHeight","lines","maskBgColor","content","children","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","truncateMode","maxHeight","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport {\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/react-helper\";\nimport {isSafari} from \"@ohkit/platform\";\nimport {rgbaToObj, findEffectiveBgColor} from \"@ohkit/dom-helper\";\nimport {Measure} from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 折叠按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor,\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width]);\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" && !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 计算折叠按钮蒙层的渐变颜色\n const validMaskBgColor = useMemo(() => {\n if (!showFoldControl || !ellipsis) {\n return null;\n }\n const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);\n return {\n startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,\n endColor: `rgba(${r}, ${g}, ${b}, 1)`,\n };\n }, [maskBgColor, ellipsis, showFoldControl]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,\n };\n }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 占位按钮\n const ButtonShadowDom = useMemo(() => {\n if (!showFoldControl || uiType !== 'right' || fold) {\n return null;\n }\n return <span style={btnStyle} className=\"btn-fold-right-shadow\">\n {renderFoldButton ? renderFoldButton(fold) : foldText}\n </span>;\n }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n {ButtonShadowDom}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","style","lineHeight","lines","maskBgColor","content","children","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","truncateMode","maxHeight","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","validMaskBgColor","r","g","b","rgbaToObj","findEffectiveBgColor","current","startColor","endColor","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","reorganizeDom","useCallback","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ButtonShadowDom","resetState","newEllipsis","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"2pBA+Ba,MAAAA,EAAIC,EAAE,yBA+HNC,EAAeC,EAA8C,CAACC,EAAOC,KAChF,MAAMC,UACJA,EAASC,MACTA,EAAKC,WACLA,EAAa,GAAEC,MACfA,EAAKC,YACLA,EAAWC,QACXA,EAAOC,SACPA,EAAQC,sCACRA,GAAwC,EAAKC,kBAC7CA,EAAiBC,cACjBA,EAAaC,gBACbA,GAAkB,EAAIC,SACtBA,EAAW,KAAIC,WACfA,EAAa,KAAIC,OACjBA,EAAS,QAAOC,aAChBA,EAAe,OAAMC,UACrBA,EAASC,iBACTA,EAAmB,SAAQC,WAC3BA,EAAUC,MACVA,EAAKC,iBACLA,EAAgBC,iBAChBA,EAAgBC,aAChBA,EAAYC,eACZA,EAAcC,aACdA,EAAYC,aACZA,EAAYC,eACZA,EAAcC,eACdA,EAAcC,QACdA,EAAOC,QACPA,GACE9B,EACE+B,EAAexB,GAAWC,GAEzBwB,EAAUC,GAAeC,GAAS,IAClCC,EAAmBC,GAAwBF,GAAS,IAEpDG,EAAMC,IAAWC,EAAkBvC,EAAO,OAAQ,CAACwC,cAAc,EAAMC,SAAUlB,KACjFmB,GAAcC,IAAmBT,EAAS,IAC1CU,GAAiBC,IAAsBX,EACtB,iBAAf9B,GAA2BA,EAAW0C,SAAS,MAClDC,WAAW3C,GACX,IAEC4C,GAAa,EAAGC,IAAiBf,EAAS7B,IAE1C6C,GAAaC,IAAkBjB,EAAS,KAExCkB,IAAWC,EAAW,CAC3BC,QAAQ,EACRC,oBAAqB,EACrBC,uBAAwB,EACxBxB,WACAyB,YAAapB,EACbA,OACAK,gBACAQ,eACA5B,mBACAC,gBACC,CAAC,mBAAoB,OAAQ,iBAE1BmC,GAAaC,EAAuB,MACpCC,GAAaD,EAAuB,MACpCE,GAAeF,EAAuB,MACtCG,GAAgBH,EAAuB,MAEvCI,GAAiBC,EAAQ,IAC7BC,EAAA,CACE7D,WAAY+B,EACR,MACA/B,QAA0B8D,GAC3B/D,GAEJ,CAACA,EAAOC,EAAY+B,IAEjBgC,GAAkBH,EAAQ,KACvB,CACL7C,aACAC,UAED,CAACD,EAAYC,IAEVgD,GAAYJ,EAAQ,KACxB,MACMK,EAAgC,WAAjBrD,EACrB,IAAKgB,KAAaqC,GAFJrB,IAEgCJ,IAC5C,OAAOuB,GAET,MAAMG,EAAgB1D,GAA+B,WAAXG,IAAwBsB,EAAQ,GAAGO,YAAsBsB,EACnG,OAAAD,EACKE,CAAAA,EAAAA,GAEHI,CAAAA,WAAYF,GAAgBhC,GAThBW,GASmC,IAAOJ,GAAnB,UAAyCsB,EAI5EM,gBAAiBH,OAAeH,EAAY7B,EAbhCW,QAa+CkB,EAC3DjD,UAAWoD,GAAgBhC,EAAQpB,GAdvB+B,GAc4CJ,IAAmBQ,GAAQI,wBAA0B,OAAKU,EAClHI,gBACAG,UAAWH,EAAgB,kBAAwBJ,KAEpD,CAAClB,GAAYJ,GAAiBZ,EAAUK,EAAMzB,EAAiBG,EAAQC,EAAcC,EAAWkD,KAG7FO,GAAmBV,EAAQ,KAC/B,IAAKpD,IAAoBoB,EACvB,YAEF,MAAM2C,EAACA,EAACC,EAAEA,EAACC,EAAEA,GAAKC,EAAUxE,GAAe,KAAOyE,EAAqBlB,GAAamB,SACpF,MAAO,CACLC,WAAY,QAAQN,MAAMC,MAAMC,UAChCK,SAAU,QAAQP,MAAMC,MAAMC,UAE/B,CAACvE,EAAa0B,EAAUpB,IAGrBuE,GAAWnB,EAAQ,KACvB,IAAK3B,EACH,OAGF,MAAM+C,EAAUxC,GAEVyC,EAAmB,UAAXtE,EAAqBuE,KAAKC,IAAKH,EAAU1C,GAAgB,IAAK,IAAM,IAC5EuC,WAACA,EAAa,wBAAuBC,SAAEA,EAAW,uBAAyBR,IAAoB,CAAA,EACrG,MAAO,CACLD,UAAW,cACXe,OAAQ,GAAG5C,OACXxC,WAAY,GAAGwC,OACf6C,WAAuB,WAAX1E,EAAsB,GAAGqE,WAAclB,EACnDwB,YAAwB,UAAX3E,EAAqB,GAAGqE,WAAclB,EAEnDyB,WAAY,sBAAsB5E,MAAWkE,MAAeC,KAAYG,OAAWH,YAEpF,CAACtC,GAAiBP,EAAMtB,EAAQ2B,GAAcgC,KAE3CkB,GAAgBC,EAAY,KAEhC,GAAInC,GAAWsB,QAAS,CACtB,MAAMc,EAAkBpC,GAAWsB,QAAQ7E,MAAMiB,MAC3C2E,EAAaC,OAAOC,iBAAiBvC,GAAWsB,SAAS5D,MAE/DsC,GAAWsB,QAAQ7E,MAAMiB,MAAW2B,WAAWgD,GAAc,GAA5B,KACjCC,MAAAA,OAAOE,uBAAPF,OAAOE,sBAAwB,KACzBxC,GAAWsB,UACbtB,GAAWsB,QAAQ7E,MAAMiB,MAAQ0E,IAGtC,GACA,IAEGK,GAAmBN,EACvB,CAACO,EAAkC/D,GAAQe,GAAQf,QACjDe,GAAQf,KAAOA,EACfC,GAAQD,IACT,IAEGgE,GAAarC,EAAQ,iBAEvBsC,EAAAC,cAAA,MAAA,CACErG,UAAWsG,EACT,mBACA,oBAAoBzF,IACT,WAAXA,GAAuB,aAAaG,KAEtCf,MAAOgF,GACPlF,IAAK6D,GACLjC,QAASsE,IAER9E,EACCA,EAAiBgB,gBAEjBiE,EAAAC,cAAA,MAAA,CAAKrG,UAAW,YAAamC,EAAOvB,EAAaD,IAItD,CACDsE,GACA9C,EACAxB,EACAsF,GACA9E,EACAH,EACAH,EACAD,IAII2F,GAAkBzC,EAAQ,KACzBpD,GAA8B,UAAXG,GAAsBsB,EAE7C,kBACMiE,EAAAC,cAAMpG,OAAAA,CAAAA,MAAOgF,GAAUjF,UAAU,yBACrCmB,EAAmBA,EAAiBgB,GAAQxB,GAE9C,CAACE,EAAQH,EAAiByB,EAAM8C,GAAUtE,EAAUQ,IAGjDqF,GAAab,EAAY,CAACc,EAAcvD,GAAQpB,UACpD4E,eAAAA,GAAiB,GACf,MACF,MAAM5E,SAACA,EAAUK,KAAMwE,EAAOpD,YAAEA,GAAeL,GAC3CuD,IAAgB3E,IAClBC,EAAY0E,GACZvD,GAAQpB,SAAW2E,EACnBvD,MAAAA,GAAQ9B,kBAAR8B,GAAQ9B,iBAAmBqF,IAI3BlG,IACImG,IAAoB5E,GAAY2E,IACjCE,IAAYpD,GAEf0C,QAAiBjC,EAAWT,IAE7B,CAAC0C,GAAkBpE,EAActB,IAE9BqG,GAAejB,EAAY,KAC/B,MAAMkB,EAAUnD,GAAWoB,QACrBgC,EAAenD,GAAamB,QAClC,IAAK+B,IAAYC,EACf,OAEF5D,GAAQG,oBAAsBwD,EAAQE,aACtC,MAAMlD,EAAiBiC,OAAOC,iBAAiBe,GACzCvB,EAAa1C,WAAWgB,EAAe0B,YACvCnB,EAAgBvB,WAAWgB,EAAeO,eAC1Cd,EAAyBJ,GAAQI,uBAAyBwD,EAAaE,aAAezB,EAAanB,EAGzG,IAAI6C,EAAiB,EAErB,IAAKA,GAAkBJ,EAAS,CAC9B,MAAM3G,WAACA,GAAc2D,GAAkB,CAAE,EACrC3D,IAEF+G,EAAiBpE,WAAW3C,GACvB+G,GACH/E,GAAqB,GAG1B,CAED,GAAIQ,KAAoBuE,IACtBtE,GAAmBsE,GACdA,GAOP,GAFsC,WAAjBnG,EAGnB0F,GAAWtD,GAAQG,qBAAuBtC,GAAauC,SAKzD,GAAKnD,EAWC2C,KAAe3C,GACjB4C,GAAc5C,GAIdqG,GAAWtD,GAAQG,sBAAwBlD,EAAQ,GAAKiF,KAAK8B,MAAMD,GAAkB,QAfvF,GAAI/D,GAAQG,oBAAsBC,EAAwB,CACtD,MAAM6D,EAAc/B,KAAK8B,MAAM5D,EAAyB2D,IAAmB,EACvEnE,KAAeqE,GACjBpE,GAAcoE,GAEhBX,IAAW,EACd,MACCA,IAAW,IAUd,CAACrG,EAAOuC,GAAiB5B,EAAcC,EAAWyF,KAIrDY,EAAoB,KAClBZ,GAAWtD,GAAQpB,SAAU,CAC3B4E,gBAAgB,IAElBE,MACC,CAACA,GAAcJ,KAGlBa,EAAU,KACR,GAAIvF,GAAY8B,GAAckB,QAAS,CACrC,MAAMwC,YAACA,GAAe1D,GAAckB,QAChCwC,IAAgBpE,GAAQV,eAC1BU,GAAQV,aAAe8E,EACvB7E,GAAgB6E,GAEnB,GACA,CAACxF,EAAUlB,EAAYF,IAC1B2G,EAAU,KACJE,GACF7B,MAED,CAACvD,EAAMuD,KACV,MAAM8B,GAAoB7B,EAAY,KAAK8B,IAAAA,EACzC,MAAMC,GAAiBD,OAAAA,EAAA/D,GAAWoB,cAAX2C,EAAAA,EAAoBzE,cAAe,GACtD0E,IAAmBxE,GAAQF,cAC7BE,GAAQF,YAAc0E,EACtBzE,GAAeyE,KAEhB,IACGC,GAAa7D,EAAQ,IAChBhC,GAAYK,EACW,mBAAlB1B,EACNA,EAAcuC,IACdvC,GAAiBuC,QACnBgB,EACL,CAACvD,EAAeqB,EAAUK,EAAMa,KAkBnC,OAjBAqE,EAAU,KACJnE,GAAQE,SACI,MAAd9B,GAAAA,EAAiB,CACbQ,WACAK,OACAyF,MAAOD,OAGZ,CAACrG,EAAgBa,EAAML,EAAU6F,KACpCN,EAAU,KACRnE,GAAQE,QAAS,GAChB,iBAODgD,EAAAC,cACErG,MAAAA,CAAAA,UAAWsG,EAAG5G,EAAE,aAAcM,GAC9BC,MAAO4D,GACP9D,IAAM0E,IACJoD,EAAUlE,GAAcc,GACxB1E,GAAO8H,EAAU9H,EAAK0E,IAExBlD,aAAcA,EACdC,aAAcA,EACdC,eAAgBA,EAChBC,eAAgBA,EAChBC,QAASA,EACTC,QAASA,gBAGTwE,EAAAC,cAACyB,EAAQC,CAAAA,QACN,GAAA,EAAEC,aAAYC,kBAEb,MAAM3C,OAACA,GAAU2C,EAAYF,QAAU,GAIvC,YAHe/D,IAAXsB,GAAwBF,KAAK8C,IAAI5C,EAASpC,GAAQG,qBAAuB,GAC3EuD,kBAEKR,EAAAC,cAAKpG,MAAAA,CAAAA,MAAOgE,GAAiBjE,UAAW,qBAAsBD,IAAM0E,IACzEoD,EAAUG,EAAYvD,GACtBoD,EAAUnE,GAAYe,GACtB+C,OAEC3F,kBAQPuE,EAAAC,cAAA,MAAA,CACErG,UAAW,sBACX4H,MAAOpH,EAAoBmH,QAAa3D,EACxC/D,MAAOiE,GACPnE,IAAKyD,IAIJ1B,GAAYpB,GAAmByF,GAC/BtE,EACA0E"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/utils"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/utils","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.utils,e.measure)}(this,function(e,t,n,i){function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var l=/*#__PURE__*/o(t);function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},s.apply(null,arguments)}var a=n.prefixClassname("ohkit-text-ellipsis__"),r=t.forwardRef(function(e,o){var r=e.className,f=e.style,u=e.lineHeight,d=void 0===u?"":u,c=e.lines,h=e.maskBgColor,p=void 0===h?"#fff":h,g=e.resetFoldWhenChildrenOrEllipsisChange,v=void 0!==g&&g,m=e.showTitleWhenFold,C=e.titleWhenFold,x=e.showFoldControl,b=void 0===x||x,w=e.foldText,E=void 0===w?"收起":w,y=e.unfoldText,F=void 0===y?"展开":y,H=e.uiType,k=void 0===H?"right":H,M=e.truncateMode,S=void 0===M?"line":M,R=e.maxHeight,O=e.controlPlacement,T=void 0===O?"center":O,W=e.whiteSpace,N=e.width,B=e.renderFoldButton,P=e.onEllipsisChange,L=e.onFoldChange,q=e.onStatusChange,j=e.onMouseEnter,z=e.onMouseLeave,A=e.onPointerEnter,_=e.onPointerLeave,V=e.onClick,D=e.onFocus,G=e.content||e.children,I=t.useState(!1),J=I[0],K=I[1],Q=t.useState(!1),U=Q[0],X=Q[1],Y=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:L}),Z=Y[0],$=Y[1],ee=t.useState(1),te=ee[0],ne=ee[1],ie=t.useState("string"==typeof d&&d.endsWith("px")?parseFloat(d):0),oe=ie[0],le=ie[1],se=t.useState(c),ae=se[0],re=void 0===ae?0:ae,fe=se[1],ue=t.useState(""),de=ue[0],ce=ue[1],he=n.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:J,defaultFold:Z,fold:Z,foldBtnWidth:te,textContent:de,onEllipsisChange:P,onFoldChange:L},["onEllipsisChange","fold","onFoldChange"])[0],pe=t.useRef(null),ge=t.useRef(null),ve=t.useRef(null),me=t.useRef(null),Ce=t.useMemo(function(){return s({lineHeight:U?"1.4":d||void 0},f)},[f,d,U]),xe=t.useMemo(function(){return{whiteSpace:W,width:N}},[W,N]),be=t.useMemo(function(){var e="height"===S;if(!J||!(e||re&&oe))return xe;var t=!b||"bottom"!==k&&Z?void 0:oe+"px";return s({},xe,{minHeight:!e&&Z?(re-.2)*oe+"px":void 0,WebkitLineClamp:e?void 0:Z?re:void 0,maxHeight:e&&Z?R||re*oe||he.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[re,oe,J,Z,b,k,S,R,xe]),we=t.useMemo(function(){if(Z){var e=oe,t="right"===k?Math.min(e/te*100,80):60;return{boxSizing:"content-box",height:oe+"px",lineHeight:oe+"px",paddingTop:"bottom"===k?e+"px":void 0,paddingLeft:"right"===k?e+"px":void 0,background:"linear-gradient(to "+k+", "+p+(4===p.length?"0":"00")+", "+p+" "+t+"%, "+p+" 100%)"}}},[oe,p,Z,k,te]),Ee=t.useCallback(function(){if(pe.current){var e=pe.current.style.width,t=window.getComputedStyle(pe.current).width;pe.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){pe.current&&(pe.current.style.width=e)})}},[]),ye=t.useCallback(function(e,t){void 0===t&&(t=!he.fold),he.fold=t,$(t)},[]),Fe=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+k,"bottom"===k&&"placement-"+T),style:we,ref:me,onClick:ye},B?B(Z):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},Z?F:E))},[we,Z,E,ye,B,T,k,F]),He=t.useCallback(function(e,t){void 0===e&&(e=he.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=he.ellipsis,l=he.fold,s=he.defaultFold;e!==o&&(K(e),he.ellipsis=e,null==he.onEllipsisChange||he.onEllipsisChange(e)),v&&(i||!o&&e)&&l!==s&&ye(void 0,s)},[ye,G,v]),ke=t.useCallback(function(){var e=ge.current,t=ve.current;if(e&&t){he.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),i=parseFloat(n.paddingTop),o=parseFloat(n.paddingBottom),l=he.containerContentHeight=t.clientHeight-i-o,s=0;if(!s&&e){var a=(n||{}).lineHeight;a&&((s=parseFloat(a))||X(!0))}if(oe===s||(le(s),s))if("height"===S)He(he.contentOffsetHeight>(R||l));else if(c)re!==c&&fe(c),He(he.contentOffsetHeight>=(c+1)*Math.floor(s)-1);else if(he.contentOffsetHeight>l){var r=Math.floor(l/s)||1;re!==r&&fe(r),He(!0)}else He(!1)}},[c,oe,S,R,He]);n.useCompatibleEffect(function(){He(he.ellipsis,{forceResetFold:!0}),ke()},[ke,He]),t.useEffect(function(){if(J&&me.current){var e=me.current.offsetWidth;e!==he.foldBtnWidth&&(he.foldBtnWidth=e,ne(e))}},[J,F,b]),t.useEffect(function(){n.isSafari&&Ee()},[Z,Ee]);var Me=t.useCallback(function(){var e,t=(null==(e=ge.current)?void 0:e.textContent)||"";t!==he.textContent&&(he.textContent=t,ce(t))},[]),Se=t.useMemo(function(){return J&&Z?"function"==typeof C?C(de):C||de:void 0},[C,J,Z,de]);return t.useEffect(function(){he.inited&&(null==q||q({ellipsis:J,fold:Z,title:Se}))},[q,Z,J,Se]),t.useEffect(function(){he.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(a("container"),r),style:Ce,ref:function(e){n.assignRef(ve,e),o&&n.assignRef(o,e)},onMouseEnter:j,onMouseLeave:z,onPointerEnter:A,onPointerLeave:_,onClick:V,onFocus:D},/*#__PURE__*/l.default.createElement(i.Measure,{offset:!0},function(e){var t=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-he.contentOffsetHeight)>1&&ke(),/*#__PURE__*/l.default.createElement("div",{style:xe,className:"content-shadow-dom",ref:function(e){n.assignRef(t,e),n.assignRef(ge,e),Me()}},G)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:m?Se:void 0,style:be,ref:pe},J&&b&&Fe,G))});e.TextEllipsis=r,e.c=a});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/prefix-classname"),require("@ohkit/react-helper"),require("@ohkit/platform"),require("@ohkit/dom-helper"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/prefix-classname","@ohkit/react-helper","@ohkit/platform","@ohkit/dom-helper","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.prefixClassname,e.reactHelper,e.platform,e.domHelper,e.measure)}(this,function(e,t,n,o,i,r,l){function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var s=/*#__PURE__*/a(t);function f(){return f=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},f.apply(null,arguments)}var u=n.prefixClassname("ohkit-text-ellipsis__"),d=t.forwardRef(function(e,a){var d=e.className,c=e.style,h=e.lineHeight,p=void 0===h?"":h,g=e.lines,m=e.maskBgColor,v=e.resetFoldWhenChildrenOrEllipsisChange,C=void 0!==v&&v,b=e.showTitleWhenFold,x=e.titleWhenFold,E=e.showFoldControl,w=void 0===E||E,k=e.foldText,y=void 0===k?"收起":k,F=e.unfoldText,H=void 0===F?"展开":F,M=e.uiType,S=void 0===M?"right":M,R=e.truncateMode,O=void 0===R?"line":R,T=e.maxHeight,N=e.controlPlacement,W=void 0===N?"center":N,q=e.whiteSpace,B=e.width,P=e.renderFoldButton,L=e.onEllipsisChange,j=e.onFoldChange,z=e.onStatusChange,A=e.onMouseEnter,_=e.onMouseLeave,V=e.onPointerEnter,D=e.onPointerLeave,G=e.onClick,I=e.onFocus,J=e.content||e.children,K=t.useState(!1),Q=K[0],U=K[1],X=t.useState(!1),Y=X[0],Z=X[1],$=o.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:j}),ee=$[0],te=$[1],ne=t.useState(1),oe=ne[0],ie=ne[1],re=t.useState("string"==typeof p&&p.endsWith("px")?parseFloat(p):0),le=re[0],ae=re[1],se=t.useState(g),fe=se[0],ue=void 0===fe?0:fe,de=se[1],ce=t.useState(""),he=ce[0],pe=ce[1],ge=o.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:ee,fold:ee,foldBtnWidth:oe,textContent:he,onEllipsisChange:L,onFoldChange:j},["onEllipsisChange","fold","onFoldChange"])[0],me=t.useRef(null),ve=t.useRef(null),Ce=t.useRef(null),be=t.useRef(null),xe=t.useMemo(function(){return f({lineHeight:Y?"1.4":p||void 0},c)},[c,p,Y]),Ee=t.useMemo(function(){return{whiteSpace:q,width:B}},[q,B]),we=t.useMemo(function(){var e="height"===O;if(!Q||!(e||ue&&le))return Ee;var t=w&&"bottom"===S&&!ee?le+"px":void 0;return f({},Ee,{minHeight:!e&&ee?(ue-.2)*le+"px":void 0,WebkitLineClamp:e?void 0:ee?ue:void 0,maxHeight:e&&ee?T||ue*le||ge.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[ue,le,Q,ee,w,S,O,T,Ee]),ke=t.useMemo(function(){if(!w||!Q)return null;var e=r.rgbaToObj(m||"")||r.findEffectiveBgColor(Ce.current),t=e.r,n=e.g,o=e.b;return{startColor:"rgba("+t+", "+n+", "+o+", 0.2)",endColor:"rgba("+t+", "+n+", "+o+", 1)"}},[m,Q,w]),ye=t.useMemo(function(){if(ee){var e=le,t="right"===S?Math.min(e/oe*100,80):60,n=ke||{},o=n.startColor,i=n.endColor,r=void 0===i?"rgba(255,255,255,1)":i;return{boxSizing:"content-box",height:le+"px",lineHeight:le+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+(void 0===o?"rgba(255,255,255,0.2)":o)+", "+r+" "+t+"%, "+r+" 100%)"}}},[le,ee,S,oe,ke]),Fe=t.useCallback(function(){if(me.current){var e=me.current.style.width,t=window.getComputedStyle(me.current).width;me.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){me.current&&(me.current.style.width=e)})}},[]),He=t.useCallback(function(e,t){void 0===t&&(t=!ge.fold),ge.fold=t,te(t)},[]),Me=t.useMemo(function(){/*#__PURE__*/return s.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+W),style:ye,ref:be,onClick:He},P?P(ee):/*#__PURE__*/s.default.createElement("div",{className:"btn-fold"},ee?H:y))},[ye,ee,y,He,P,W,S,H]),Se=t.useMemo(function(){return!w||"right"!==S||ee?null:/*#__PURE__*/s.default.createElement("span",{style:ye,className:"btn-fold-right-shadow"},P?P(ee):y)},[S,w,ee,ye,y,P]),Re=t.useCallback(function(e,t){void 0===e&&(e=ge.ellipsis);var n=(void 0===t?{}:t).forceResetFold,o=void 0!==n&&n,i=ge.ellipsis,r=ge.fold,l=ge.defaultFold;e!==i&&(U(e),ge.ellipsis=e,null==ge.onEllipsisChange||ge.onEllipsisChange(e)),C&&(o||!i&&e)&&r!==l&&He(void 0,l)},[He,J,C]),Oe=t.useCallback(function(){var e=ve.current,t=Ce.current;if(e&&t){ge.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),o=parseFloat(n.paddingTop),i=parseFloat(n.paddingBottom),r=ge.containerContentHeight=t.clientHeight-o-i,l=0;if(!l&&e){var a=(n||{}).lineHeight;a&&((l=parseFloat(a))||Z(!0))}if(le===l||(ae(l),l))if("height"===O)Re(ge.contentOffsetHeight>(T||r));else if(g)ue!==g&&de(g),Re(ge.contentOffsetHeight>=(g+1)*Math.floor(l)-1);else if(ge.contentOffsetHeight>r){var s=Math.floor(r/l)||1;ue!==s&&de(s),Re(!0)}else Re(!1)}},[g,le,O,T,Re]);o.useCompatibleEffect(function(){Re(ge.ellipsis,{forceResetFold:!0}),Oe()},[Oe,Re]),t.useEffect(function(){if(Q&&be.current){var e=be.current.offsetWidth;e!==ge.foldBtnWidth&&(ge.foldBtnWidth=e,ie(e))}},[Q,H,w]),t.useEffect(function(){i.isSafari&&Fe()},[ee,Fe]);var Te=t.useCallback(function(){var e,t=(null==(e=ve.current)?void 0:e.textContent)||"";t!==ge.textContent&&(ge.textContent=t,pe(t))},[]),Ne=t.useMemo(function(){return Q&&ee?"function"==typeof x?x(he):x||he:void 0},[x,Q,ee,he]);return t.useEffect(function(){ge.inited&&(null==z||z({ellipsis:Q,fold:ee,title:Ne}))},[z,ee,Q,Ne]),t.useEffect(function(){ge.inited=!0},[]),/*#__PURE__*/s.default.createElement("div",{className:n.classNames(u("container"),d),style:xe,ref:function(e){o.assignRef(Ce,e),a&&o.assignRef(a,e)},onMouseEnter:A,onMouseLeave:_,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:I},/*#__PURE__*/s.default.createElement(l.Measure,{offset:!0},function(e){var t=e.measureRef,n=(e.contentRect.offset||{}).height;return void 0!==n&&Math.abs(n-ge.contentOffsetHeight)>1&&Oe(),/*#__PURE__*/s.default.createElement("div",{style:Ee,className:"content-shadow-dom",ref:function(e){o.assignRef(t,e),o.assignRef(ve,e),Te()}},J)}),/*#__PURE__*/s.default.createElement("div",{className:"text-ellipsis-inner",title:b?Ne:void 0,style:we,ref:me},Q&&w&&Me,J,Se))});e.TextEllipsis=d,e.c=u});
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width])\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" || !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport {\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/react-helper\";\nimport {isSafari} from \"@ohkit/platform\";\nimport {rgbaToObj, findEffectiveBgColor} from \"@ohkit/dom-helper\";\nimport {Measure} from \"@ohkit/measure\";\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * 自定义样式\n */\n style?: React.CSSProperties;\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)\n * @default line\n */\n truncateMode?: \"line\" | \"height\";\n /**\n * truncateMode === \"height\" 时生效\n * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度\n * 单位: px\n */\n maxHeight?: number;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * truncateMode === \"line\" 时生效\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 折叠按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 容器宽度(默认自适应内容)\n * 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定\n */\n width?: React.CSSProperties['width'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n style,\n lineHeight = \"\",\n lines,\n maskBgColor,\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n truncateMode = \"line\",\n maxHeight,\n controlPlacement = 'center',\n whiteSpace,\n width,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0, // 内容节点offsetHeight\n containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n ...style,\n };\n }, [style, lineHeight, getLineHeightFail]);\n\n const commonWrapStyle = useMemo(() => {\n return {\n whiteSpace,\n width,\n };\n }, [whiteSpace, width]);\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const isHeightMode = truncateMode === 'height';\n if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {\n return commonWrapStyle;\n }\n const paddingBottom = showFoldControl && (uiType === \"bottom\" && !fold) ? `${innerLineHeight}px` : undefined;\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode\n // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,\n WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,\n paddingBottom,\n boxSizing: paddingBottom ? 'border-box' as const : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);\n\n // 计算折叠按钮蒙层的渐变颜色\n const validMaskBgColor = useMemo(() => {\n if (!showFoldControl || !ellipsis) {\n return null;\n }\n const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);\n return {\n startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,\n endColor: `rgba(${r}, ${g}, ${b}, 1)`,\n };\n }, [maskBgColor, ellipsis, showFoldControl]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,\n };\n }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);\n\n const reorganizeDom = useCallback(() => {\n // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n const orginStyleWidth = contentRef.current.style.width;\n const orginWidth = window.getComputedStyle(contentRef.current).width;\n // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);\n contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = orginStyleWidth;\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 占位按钮\n const ButtonShadowDom = useMemo(() => {\n if (!showFoldControl || uiType !== 'right' || fold) {\n return null;\n }\n return <span style={btnStyle} className=\"btn-fold-right-shadow\">\n {renderFoldButton ? renderFoldButton(fold) : foldText}\n </span>;\n }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n const containerStyle = window.getComputedStyle(containerDom);\n const paddingTop = parseFloat(containerStyle.paddingTop);\n const paddingBottom = parseFloat(containerStyle.paddingBottom);\n const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;\n\n // 计算真实行高\n let realLineHeight = 0;\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const {lineHeight} = containerStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n\n const isHeightMode = truncateMode === 'height';\n // 高度截断模式,比较简单 直接判断是否超出容器高度\n if (isHeightMode) {\n resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));\n return;\n }\n\n // 行数截断模式,需要利用行高计算是否超出容器高度\n if (!lines) {\n if (runtime.contentOffsetHeight > containerContentHeight) {\n const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);\n // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)\n resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);\n }\n }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)\n // if (!maxHeight && !lines) {\n // calcEllipsis();\n // }\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={commonWrapStyle} className={\"content-shadow-dom\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"content-shadow-dom\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n {ButtonShadowDom}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","style","_props$lineHeight","lineHeight","lines","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$truncateMode","truncateMode","maxHeight","_props$controlPlaceme","controlPlacement","whiteSpace","width","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","containerContentHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","_extends","undefined","commonWrapStyle","wrapStyle","isHeightMode","paddingBottom","minHeight","WebkitLineClamp","boxSizing","validMaskBgColor","_ref","rgbaToObj","findEffectiveBgColor","current","r","g","b","startColor","endColor","btnStyle","padding","ratio","Math","min","_ref2","_ref2$startColor","_ref2$endColor","height","paddingTop","paddingLeft","background","reorganizeDom","useCallback","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ButtonShadowDom","resetState","newEllipsis","_temp","_ref3","_ref3$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","assignRef","Measure","offset","_ref5","measureRef","contentRect","abs"],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * @author <wuqiuyang305@126.com>
5
5
  */
6
6
  import React, { PropsWithChildren } from "react";
7
- import { classNames as cx } from "@ohkit/utils";
7
+ import { classNames as cx } from "@ohkit/prefix-classname";
8
8
  import "./style.scss";
9
9
  export declare const c: (...arg: cx.ArgumentArray) => string;
10
10
  interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMouseEnter" | "onMouseLeave" | "onPointerEnter" | "onPointerLeave" | "onFocus" | "onClick"> {
@@ -71,7 +71,7 @@ interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMou
71
71
  */
72
72
  controlPlacement?: 'left' | 'center' | 'right';
73
73
  /**
74
- * 展开按钮文字
74
+ * 折叠按钮文字
75
75
  * @default 收起
76
76
  */
77
77
  foldText?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohkit/text-ellipsis",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "text ellipsis for react",
5
5
  "keywords": [
6
6
  "text ellipsis"
@@ -32,8 +32,11 @@
32
32
  "clean": "rm -rf dist"
33
33
  },
34
34
  "dependencies": {
35
- "@ohkit/measure": "0.0.3",
36
- "@ohkit/utils": "0.0.3"
35
+ "@ohkit/dom-helper": "0.0.1",
36
+ "@ohkit/measure": "0.0.4",
37
+ "@ohkit/platform": "0.0.3",
38
+ "@ohkit/prefix-classname": "0.0.3",
39
+ "@ohkit/react-helper": "0.0.4"
37
40
  },
38
41
  "devDependencies": {
39
42
  "react": "^18.3.1",
@@ -43,5 +46,5 @@
43
46
  "react": ">=16.8.0",
44
47
  "react-dom": ">=16.8.0"
45
48
  },
46
- "gitHead": "0b9cb51d650f932fca8d4365dad030951ee84cdf"
49
+ "gitHead": "ab0bb5983bfc0a7c434dd19a7c6e274726b604f7"
47
50
  }
package/src/index.tsx CHANGED
@@ -15,15 +15,18 @@ import React, {
15
15
  MouseEvent,
16
16
  } from "react";
17
17
  import {
18
- isSafari,
19
18
  prefixClassname as p,
20
19
  classNames as cx,
20
+ } from "@ohkit/prefix-classname";
21
+ import {
21
22
  assignRef,
22
23
  useRuntime,
23
24
  useCompatibleEffect,
24
25
  useSyncPropsState,
25
- } from "@ohkit/utils";
26
- import { Measure } from "@ohkit/measure";
26
+ } from "@ohkit/react-helper";
27
+ import {isSafari} from "@ohkit/platform";
28
+ import {rgbaToObj, findEffectiveBgColor} from "@ohkit/dom-helper";
29
+ import {Measure} from "@ohkit/measure";
27
30
  import "./style.scss";
28
31
 
29
32
  export const c = p("ohkit-text-ellipsis__");
@@ -101,7 +104,7 @@ interface ITextEllipsis
101
104
  */
102
105
  controlPlacement?: 'left' | 'center' | 'right';
103
106
  /**
104
- * 展开按钮文字
107
+ * 折叠按钮文字
105
108
  * @default 收起
106
109
  */
107
110
  foldText?: string;
@@ -159,7 +162,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
159
162
  style,
160
163
  lineHeight = "",
161
164
  lines,
162
- maskBgColor = "#fff",
165
+ maskBgColor,
163
166
  content,
164
167
  children,
165
168
  resetFoldWhenChildrenOrEllipsisChange = false,
@@ -233,7 +236,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
233
236
  whiteSpace,
234
237
  width,
235
238
  };
236
- }, [whiteSpace, width])
239
+ }, [whiteSpace, width]);
237
240
  // 容器样式
238
241
  const wrapStyle = useMemo(() => {
239
242
  const lines = innerLines;
@@ -241,7 +244,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
241
244
  if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {
242
245
  return commonWrapStyle;
243
246
  }
244
- const paddingBottom = showFoldControl && (uiType === "bottom" || !fold) ? `${innerLineHeight}px` : undefined;
247
+ const paddingBottom = showFoldControl && (uiType === "bottom" && !fold) ? `${innerLineHeight}px` : undefined;
245
248
  return {
246
249
  ...commonWrapStyle,
247
250
  // HACK: 兼容safari 15+ 富文本折叠高度丢失问题
@@ -256,6 +259,18 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
256
259
  };
257
260
  }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);
258
261
 
262
+ // 计算折叠按钮蒙层的渐变颜色
263
+ const validMaskBgColor = useMemo(() => {
264
+ if (!showFoldControl || !ellipsis) {
265
+ return null;
266
+ }
267
+ const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);
268
+ return {
269
+ startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,
270
+ endColor: `rgba(${r}, ${g}, ${b}, 1)`,
271
+ };
272
+ }, [maskBgColor, ellipsis, showFoldControl]);
273
+
259
274
  // 展开|收起 按钮样式
260
275
  const btnStyle = useMemo(() => {
261
276
  if (!fold) {
@@ -265,10 +280,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
265
280
  const padding = innerLineHeight;
266
281
  // 蒙层透明度所占比例
267
282
  const ratio = uiType === "right" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;
268
- // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色
269
- const transparent = `${maskBgColor}${
270
- maskBgColor.length === 4 ? "0" : "00"
271
- }`;
283
+ const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};
272
284
  return {
273
285
  boxSizing: 'content-box' as const,
274
286
  height: `${innerLineHeight}px`,
@@ -276,9 +288,9 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
276
288
  paddingTop: uiType === "bottom" ? `${padding}px` : undefined,
277
289
  paddingLeft: uiType === "right" ? `${padding}px` : undefined,
278
290
  // 渐变蒙层
279
- background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,
291
+ background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,
280
292
  };
281
- }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);
293
+ }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);
282
294
 
283
295
  const reorganizeDom = useCallback(() => {
284
296
  // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发
@@ -331,6 +343,16 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
331
343
  unfoldText,
332
344
  ]);
333
345
 
346
+ // 占位按钮
347
+ const ButtonShadowDom = useMemo(() => {
348
+ if (!showFoldControl || uiType !== 'right' || fold) {
349
+ return null;
350
+ }
351
+ return <span style={btnStyle} className="btn-fold-right-shadow">
352
+ {renderFoldButton ? renderFoldButton(fold) : foldText}
353
+ </span>;
354
+ }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);
355
+
334
356
  // 重置状态
335
357
  const resetState = useCallback((newEllipsis = runtime.ellipsis, {
336
358
  forceResetFold = false, // 强制重置fold 比如child变化时
@@ -513,6 +535,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
513
535
  {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}
514
536
  {ellipsis && showFoldControl && ButtonComp}
515
537
  {finalContent}
538
+ {ButtonShadowDom}
516
539
  </div>
517
540
  </div>
518
541
  );
package/src/style.scss CHANGED
@@ -32,6 +32,13 @@ $prefix: global.$comp-prefix;
32
32
  -webkit-box-orient: vertical;
33
33
  @include text-ellipsis-break();
34
34
 
35
+ // 右侧折叠按钮占位
36
+ .btn-fold-right-shadow {
37
+ visibility: hidden;
38
+ pointer-events: none;
39
+ user-select: none;
40
+ padding-left: 12px;
41
+ }
35
42
 
36
43
  .btn-fold-wrapper {
37
44
  position: absolute;
@@ -49,7 +56,7 @@ $prefix: global.$comp-prefix;
49
56
  // 展开按钮在右侧
50
57
  &-right {
51
58
  right: 0;
52
- padding-left: 24px;
59
+ padding-left: 12px;
53
60
  }
54
61
  // 展开按钮在底部(蒙层模式)
55
62
  &-bottom {