@ohkit/text-ellipsis 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,7 @@ function Demo() {
50
50
  | showFoldControl | 是否显示展开控制按钮 | boolean | `true` |
51
51
  | foldText | 折叠状态按钮文字 | string | `收起` |
52
52
  | unfoldText | 展开状态按钮文字 | string | `展开` |
53
- | maskBgColor | 展开按钮蒙层背景色(16进制) | string | `#fff` |
53
+ | maskBgColor | 展开按钮蒙层背景色(16进制或rgb(a)),默认自动获取计算当前组件所在层级的背景色 | string | - |
54
54
  | showTitleWhenFold | 折叠状态下是否显示title属性 | boolean | `false` |
55
55
  | titleWhenFold | 自定义折叠状态下的title内容 | string \| (title: string) => string | - |
56
56
  | resetFoldWhenChildrenOrEllipsisChange | 当内容变化时重置折叠状态 | boolean | `false` |
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 r,useEffect as l}from"react";import{prefixClassname as a,classNames as d}from"@ohkit/prefix-classname";import{useSyncPropsState as f,useRuntime as s,useCompatibleEffect as c,assignRef as u}from"@ohkit/react-helper";import{isSafari as h}from"@ohkit/platform";import{Measure as p}from"@ohkit/measure";function v(){return v=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},v.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,k=e.showTitleWhenFold,O=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],rt=ot[1],lt=f(e,"fold",{defaultValue:!0,onChange:I}),at=lt[0],dt=lt[1],ft=n(1),st=ft[0],ct=ft[1],ut=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),ht=ut[0],pt=ut[1],vt=n(F),gt=vt[0],mt=void 0===gt?0:gt,Ct=vt[1],xt=n(""),wt=xt[0],Ft=xt[1],Ht=s({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:et,defaultFold:at,fold:at,foldBtnWidth:st,textContent:wt,onEllipsisChange:G,onFoldChange:I},["onEllipsisChange","fold","onFoldChange"])[0],bt=o(null),Et=o(null),yt=o(null),kt=o(null),Ot=i(function(){return v({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 v({},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/st*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,st]),St=r(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=r(function(t,e){void 0===e&&(e=!Ht.fold),Ht.fold=e,dt(e)},[]),Nt=i(function(){/*#__PURE__*/return t.createElement("div",{className:d("btn-fold-wrapper","btn-fold-wrapper-"+T,"bottom"===T&&"placement-"+A),style:Bt,ref:kt,onClick:Lt},D?D(at):/*#__PURE__*/t.createElement("div",{className:"btn-fold"},at?N:S))},[Bt,at,S,Lt,D,A,T,N]),Pt=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&&(nt(t),Ht.ellipsis=t,null==Ht.onEllipsisChange||Ht.onEllipsisChange(t)),y&&(o||!i&&t)&&r!==l&&Lt(void 0,l)},[Lt,$,y]),Tt=r(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),r=Ht.containerContentHeight=e.clientHeight-o-i,l=0;if(!l&&t){var a=(n||{}).lineHeight;a&&((l=parseFloat(a))||rt(!0))}if(ht===l||(pt(l),l))if("height"===j)Pt(Ht.contentOffsetHeight>(q||r));else if(F)mt!==F&&Ct(F),Pt(Ht.contentOffsetHeight>=(F+1)*Math.floor(l)-1);else if(Ht.contentOffsetHeight>r){var d=Math.floor(r/l)||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]),l(function(){if(et&&kt.current){var t=kt.current.offsetWidth;t!==Ht.foldBtnWidth&&(Ht.foldBtnWidth=t,ct(t))}},[et,N,W]),l(function(){h&&St()},[at,St]);var Rt=r(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 O?O(wt):O||wt:void 0},[O,et,at,wt]);return l(function(){Ht.inited&&(null==J||J({ellipsis:et,fold:at,title:jt}))},[J,at,et,jt]),l(function(){Ht.inited=!0},[]),/*#__PURE__*/t.createElement("div",{className:d(g("container"),m),style:Ot,ref:function(t){u(yt,t),a&&u(a,t)},onMouseEnter:K,onMouseLeave:Q,onPointerEnter:U,onPointerLeave:X,onClick:Y,onFocus:Z},/*#__PURE__*/t.createElement(p,{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){u(n,t),u(Et,t),Rt()}},$)}),/*#__PURE__*/t.createElement("div",{className:"text-ellipsis-inner",title:k?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 s}from"@ohkit/prefix-classname";import{useSyncPropsState as d,useRuntime as f,useCompatibleEffect as c,assignRef as u}from"@ohkit/react-helper";import{isSafari as h}from"@ohkit/platform";import{rgbaToObj as g,findEffectiveBgColor as p}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,S=e.showTitleWhenFold,M=e.titleWhenFold,O=e.showFoldControl,W=void 0===O||O,B=e.foldText,N=void 0===B?"收起":B,L=e.unfoldText,P=void 0===L?"展开":L,T=e.uiType,R=void 0===T?"right":T,z=e.truncateMode,j=void 0===z?"line":z,q=e.maxHeight,A=e.controlPlacement,_=void 0===A?"center":A,I=e.whiteSpace,V=e.width,D=e.renderFoldButton,G=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=d(e,"fold",{defaultValue:!0,onChange:J}),st=at[0],dt=at[1],ft=n(1),ct=ft[0],ut=ft[1],ht=n("string"==typeof F&&F.endsWith("px")?parseFloat(F):0),gt=ht[0],pt=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:st,fold:st,foldBtnWidth:ct,textContent:wt,onEllipsisChange:G,onFoldChange:J,onStatusChange:K},["fold","onEllipsisChange","onFoldChange","onStatusChange"])[0],Et=o(null),yt=o(null),kt=o(null),St=o(null),Mt=i(function(){return m({lineHeight:rt?"1.4":F||void 0},b)},[b,F,rt]),Ot=i(function(){return{whiteSpace:I,width:V}},[I,V]),Wt=i(function(){var t="height"===j;if(!nt||!(t||Ct&&gt))return Ot;var e=W&&"bottom"===R&&!st?gt+"px":void 0;return m({},Ot,{minHeight:!t&&st?(Ct-.2)*gt+"px":void 0,WebkitLineClamp:t?void 0:st?Ct:void 0,maxHeight:t&&st?q||Ct*gt||Ht.containerContentHeight||0:void 0,paddingBottom:e,boxSizing:e?"border-box":void 0})},[Ct,gt,nt,st,W,R,j,q,Ot]),Bt=i(function(){if(!W||!nt)return null;var t=g(E||"")||p(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,W]),Nt=i(function(){if(st){var t=gt,e="right"===R?Math.min(t/ct*100,80):60,n=Bt||{},o=n.startColor,i=n.endColor,r=void 0===i?"rgba(255,255,255,1)":i;return{boxSizing:"content-box",height:gt+"px",lineHeight:gt+"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%)"}}},[gt,st,R,ct,Bt]),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,dt(e)},[]),Tt=i(function(){/*#__PURE__*/return t.createElement("div",{className:s("btn-fold-wrapper","btn-fold-wrapper-"+R,"bottom"===R&&"placement-"+_),style:Nt,ref:St,onClick:Pt},D?D(st):/*#__PURE__*/t.createElement("div",{className:"btn-fold"},st?P:N))},[Nt,st,N,Pt,D,_,R,P]),Rt=i(function(){return!W||"right"!==R||st?null:/*#__PURE__*/t.createElement("span",{style:Nt,className:"btn-fold-right-shadow"},D?D(st):N)},[R,W,st,Nt,N,D]),zt=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]),jt=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(gt===l||(pt(l),l))if("height"===j)zt(Ht.contentOffsetHeight>(q||r));else if(H)Ct!==H&&xt(H),zt(Ht.contentOffsetHeight>=(H+1)*Math.floor(l)-1);else if(Ht.contentOffsetHeight>r){var s=Math.floor(r/l)||1;Ct!==s&&xt(s),zt(!0)}else zt(!1)}},[H,gt,j,q,zt]);c(function(){zt(Ht.ellipsis,{forceResetFold:!0}),jt()},[jt,zt]),l(function(){if(nt&&St.current){var t=St.current.offsetWidth;t!==Ht.foldBtnWidth&&(Ht.foldBtnWidth=t,ut(t))}},[nt,P,W]),l(function(){h&&Lt()},[st,Lt]);var qt=r(function(){if(yt.current){var t=yt.current.textContent||"";t!==Ht.textContent&&(Ht.textContent=t,Ft(t))}},[]),At=r(function(t){var e=(t.offset||{}).height;void 0!==e&&Math.abs(e-Ht.contentOffsetHeight)>1&&jt()},[jt]),_t=i(function(){return nt&&st?"function"==typeof M?M(wt):M||wt:void 0},[M,nt,st,wt]);return l(function(){Ht.inited&&(null==Ht.onStatusChange||Ht.onStatusChange({ellipsis:nt,fold:st,title:_t}))},[st,nt,_t]),l(function(){Ht.inited=!0},[]),/*#__PURE__*/t.createElement("div",{className:s(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,throttleMs:100,onResize:At,triggerResizeInit:!1},function(e){var n=e.measureRef;/*#__PURE__*/return t.createElement("div",{style:Ot,className:"content-shadow-dom",ref:function(t){u(n,t),u(yt,t),qt()}},tt)}),/*#__PURE__*/t.createElement("div",{className:"text-ellipsis-inner",title:S?_t:void 0,style:Wt,ref:Et},nt&&W&&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 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 {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","transparent","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_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":"mlBA8Ba,IAAAA,EAAIC,EAAE,yBA+HNC,EAAeC,EAA8C,SAACC,EAAOC,GAChF,IACEC,EA6BEF,EA7BFE,UACAC,EA4BEH,EA5BFG,MAAKC,EA4BHJ,EA3BFK,WAAAA,OAAU,IAAAD,EAAG,GAAEA,EACfE,EA0BEN,EA1BFM,MAAKC,EA0BHP,EAzBFQ,YAAAA,OAAW,IAAAD,EAAG,OAAMA,EAEZE,EAuBNT,EAtBFU,sCAAAA,OAAwC,IAAHD,GAAQA,EAC7CE,EAqBEX,EArBFW,kBACAC,EAoBEZ,EApBFY,cAAaC,EAoBXb,EAnBFc,gBAAAA,OAAkB,IAAHD,GAAOA,EAAAE,EAmBpBf,EAlBFgB,SAAAA,WAAQD,EAAG,KAAIA,EAAAE,EAkBbjB,EAjBFkB,WAAAA,OAAU,IAAAD,EAAG,KAAIA,EAAAE,EAiBfnB,EAhBFoB,OAAAA,OAAM,IAAAD,EAAG,QAAOA,EAAAE,EAgBdrB,EAfFsB,aAAAA,OAAY,IAAAD,EAAG,OAAMA,EACrBE,EAcEvB,EAdFuB,UAASC,EAcPxB,EAbFyB,iBAAAA,OAAgB,IAAAD,EAAG,SAAQA,EAC3BE,EAYE1B,EAZF0B,WACAC,EAWE3B,EAXF2B,MACAC,EAUE5B,EAVF4B,iBACAC,EASE7B,EATF6B,iBACAC,EAQE9B,EARF8B,aACAC,EAOE/B,EAPF+B,eACAC,EAMEhC,EANFgC,aACAC,EAKEjC,EALFiC,aACAC,EAIElC,EAJFkC,eACAC,EAGEnC,EAHFmC,eACAC,EAEEpC,EAFFoC,QACAC,EACErC,EADFqC,QAEIC,EADFtC,EAxBFuC,SAwBEvC,EAvBFwC,SA0BFC,GAAgCC,GAAS,GAAlCC,GAAQF,GAAEG,GAAAA,GAAWH,GAC5B,GAAAI,GAAkDH,GAAS,GAApDI,GAAiBD,GAAEE,GAAAA,GAAoBF,GAE9C,GAAAG,GAAwBC,EAAkBjD,EAAO,OAAQ,CAACkD,cAAc,EAAMC,SAAUrB,IAAjFsB,GAAIJ,GAAA,GAAEK,GAAOL,GAAA,GACpBM,GAAwCZ,EAAS,GAA1Ca,GAAYD,GAAA,GAAEE,GAAeF,GAAA,GACpCG,GAA8Cf,EACtB,iBAAfrC,GAA2BA,EAAWqD,SAAS,MAClDC,WAAWtD,GACX,GAHCuD,GAAeH,GAAEI,GAAAA,GAAkBJ,GAK1C,GAAAK,GAAwCpB,EAASpC,GAAMyD,GAAAD,GAAhDE,GAAAA,QAAa,IAAHD,GAAG,EAACA,GAAEE,GAAaH,GAEpC,GAAAI,GAAsCxB,EAAS,IAAxCyB,GAAWD,GAAEE,GAAAA,GAAcF,GAElC,GAAOG,GAAWC,EAAW,CAC3BC,QAAQ,EACRC,oBAAqB,EACrBC,uBAAwB,EACxB9B,SAAAA,GACA+B,YAAatB,GACbA,KAAAA,GACAG,aAAAA,GACAY,YAAAA,GACAtC,iBAAAA,EACAC,aAAAA,GACC,CAAC,mBAAoB,OAAQ,iBAEhC,GAAM6C,GAAaC,EAAuB,MACpCC,GAAaD,EAAuB,MACpCE,GAAeF,EAAuB,MACtCG,GAAgBH,EAAuB,MAEvCI,GAAiBC,EAAQ,WAC7B,OAAAC,EACE7E,CAAAA,WAAYyC,GACR,MACAzC,QAA0B8E,GAC3BhF,EAEP,EAAG,CAACA,EAAOE,EAAYyC,KAEjBsC,GAAkBH,EAAQ,WAC9B,MAAO,CACLvD,WAAAA,EACAC,MAAAA,EAEJ,EAAG,CAACD,EAAYC,IAEV0D,GAAYJ,EAAQ,WACxB,IACMK,EAAgC,WAAjBhE,EACrB,IAAKqB,MAAa2C,GAFJtB,IAEgCJ,IAC5C,OAAOwB,GAET,IAAMG,GAAgBzE,GAA+B,WAAXM,GAAwBgC,QAAiC+B,EAAtBvB,GAAe,KAC5F,OAAAsB,EACKE,CAAAA,EAAAA,GAEHI,CAAAA,WAAYF,GAAgBlC,IAThBY,GASmC,IAAOJ,GAAsBuB,UAAAA,EAI5EM,gBAAiBH,OAAeH,EAAY/B,GAbhCY,QAa+CmB,EAC3D5D,UAAW+D,GAAgBlC,GAAQ7B,GAdvByC,GAc4CJ,IAAmBS,GAAQI,wBAA0B,OAAKU,EAClHI,cAAAA,EACAG,UAAWH,EAAgB,kBAAwBJ,GAEvD,EAAG,CAACnB,GAAYJ,GAAiBjB,GAAUS,GAAMtC,EAAiBM,EAAQE,EAAcC,EAAW6D,KAG7FO,GAAWV,EAAQ,WACvB,GAAK7B,GAAL,CAIA,IAAMwC,EAAUhC,GAEViC,EAAmB,UAAXzE,EAAqB0E,KAAKC,IAAKH,EAAUrC,GAAgB,IAAK,IAAM,GAKlF,MAAO,CACLmC,UAAW,cACXM,OAAWpC,GAAe,KAC1BvD,WAAeuD,GAAmB,KAClCqC,WAAuB,WAAX7E,EAAyBwE,EAAO,UAAOT,EACnDe,YAAwB,UAAX9E,EAAwBwE,EAAcT,UAAAA,EAEnDgB,WAAkC/E,sBAAAA,EAAWgF,KAVxB5F,GACE,IAAvBA,EAAY6F,OAAe,IAAM,MAS4B7F,KAAAA,EAAeqF,IAAAA,EAAWrF,MAAAA,WAhBxF,CAkBH,EAAG,CAACoD,GAAiBpD,EAAa4C,GAAMhC,EAAQmC,KAE1C+C,GAAgBC,EAAY,WAEhC,GAAI5B,GAAW6B,QAAS,CACtB,IAAMC,EAAkB9B,GAAW6B,QAAQrG,MAAMwB,MAC3C+E,EAAaC,OAAOC,iBAAiBjC,GAAW6B,SAAS7E,MAE/DgD,GAAW6B,QAAQrG,MAAMwB,MAAWgC,WAAW+C,GAAc,GAAG,KAChEC,MAAAA,OAAOE,uBAAPF,OAAOE,sBAAwB,WACzBlC,GAAW6B,UACb7B,GAAW6B,QAAQrG,MAAMwB,MAAQ8E,EAErC,EACD,CACH,EAAG,IAEGK,GAAmBP,EACvB,SAACQ,EAAkC3D,QAAI,IAAJA,IAAAA,GAAQiB,GAAQjB,MACjDiB,GAAQjB,KAAOA,EACfC,GAAQD,EACZ,EAAG,IAEG4D,GAAa/B,EAAQ,wBACzB,OACEgC,EAAAC,cAAA,MAAA,CACEhH,UAAWiH,EACT,mBAAkB,oBACE/F,EACT,WAAXA,GAAoCK,aAAAA,GAEtCtB,MAAOwF,GACP1F,IAAK8E,GACL3C,QAAS0E,IAERlF,EACCA,EAAiBwB,iBAEjB6D,EAAAC,cAAKhH,MAAAA,CAAAA,UAAW,YAAakD,GAAOlC,EAAaF,GAIzD,EAAG,CACD2E,GACAvC,GACApC,EACA8F,GACAlF,EACAH,EACAL,EACAF,IAIIkG,GAAab,EAAY,SAACc,EAAWC,QAAA,IAAXD,IAAAA,EAAchD,GAAQ1B,UAAQ,IAExD4E,QAFwD,IAAAD,EAE1D,CAAA,EAAEA,GADJE,eAAAA,OAAiB,IAAHD,GAAQA,EAEf5E,EAAwC0B,GAAxC1B,SAAgB8E,EAAwBpD,GAA9BjB,KAAesB,EAAeL,GAAfK,YAC5B2C,IAAgB1E,IAClBC,GAAYyE,GACZhD,GAAQ1B,SAAW0E,EACnBhD,MAAAA,GAAQxC,kBAARwC,GAAQxC,iBAAmBwF,IAI3B3G,IACI8G,IAAoB7E,GAAY0E,IACjCI,IAAY/C,GAEfoC,QAAiB3B,EAAWT,EAEhC,EAAG,CAACoC,GAAkBxE,EAAc5B,IAE9BgH,GAAenB,EAAY,WAC/B,IAAMoB,EAAU9C,GAAW2B,QACrBoB,EAAe9C,GAAa0B,QAClC,GAAKmB,GAAYC,EAAjB,CAGAvD,GAAQG,oBAAsBmD,EAAQE,aACtC,IAAM7C,EAAiB2B,OAAOC,iBAAiBgB,GACzC3B,EAAatC,WAAWqB,EAAeiB,YACvCV,EAAgB5B,WAAWqB,EAAeO,eAC1Cd,EAAyBJ,GAAQI,uBAAyBmD,EAAaE,aAAe7B,EAAaV,EAGrGwC,EAAiB,EAErB,IAAKA,GAAkBJ,EAAS,CAC9B,IAAOtH,GAAc2E,GAAkB,CAAA,GAAhC3E,WACHA,KAEF0H,EAAiBpE,WAAWtD,KAE1B0C,IAAqB,GAG1B,CAED,GAAIa,KAAoBmE,IACtBlE,GAAmBkE,GACdA,GAOP,GAFsC,WAAjBzG,EAGnB8F,GAAW/C,GAAQG,qBAAuBjD,GAAakD,SAKzD,GAAKnE,EAWC0D,KAAe1D,GACjB2D,GAAc3D,GAId8G,GAAW/C,GAAQG,sBAAwBlE,EAAQ,GAAKwF,KAAKkC,MAAMD,GAAkB,QAfvF,GAAI1D,GAAQG,oBAAsBC,EAAwB,CACtD,IAAMwD,EAAcnC,KAAKkC,MAAMvD,EAAyBsD,IAAmB,EACvE/D,KAAeiE,GACjBhE,GAAcgE,GAEhBb,IAAW,EACd,MACCA,IAAW,EA5Cd,CAsDH,EAAG,CAAC9G,EAAOsD,GAAiBtC,EAAcC,EAAW6F,KAIrDc,EAAoB,WAClBd,GAAW/C,GAAQ1B,SAAU,CAC3B6E,gBAAgB,IAElBE,IACF,EAAG,CAACA,GAAcN,KAGlBe,EAAU,WACR,GAAIxF,IAAYoC,GAAcyB,QAAS,CACrC,IAAO4B,EAAerD,GAAcyB,QAA7B4B,YACHA,IAAgB/D,GAAQd,eAC1Bc,GAAQd,aAAe6E,EACvB5E,GAAgB4E,GAEnB,CACH,EAAG,CAACzF,GAAUzB,EAAYJ,IAC1BqH,EAAU,WACJE,GACF/B,IAEJ,EAAG,CAAClD,GAAMkD,KACV,IAAMgC,GAAoB/B,EAAY,WAAKgC,IAAAA,EACnCC,GAAmC,OAAlBD,EAAA1D,GAAW2B,cAAO,EAAlB+B,EAAoBpE,cAAe,GACtDqE,IAAmBnE,GAAQF,cAC7BE,GAAQF,YAAcqE,EACtBpE,GAAeoE,GAEnB,EAAG,IACGC,GAAaxD,EAAQ,WACvB,OAAOtC,IAAYS,GACW,mBAAlBxC,EACNA,EAAcuD,IACdvD,GAAiBuD,QACnBgB,CACR,EAAG,CAACvE,EAAe+B,GAAUS,GAAMe,KAkBnC,OAjBAgE,EAAU,WACJ9D,GAAQE,SACI,MAAdxC,GAAAA,EAAiB,CACbY,SAAAA,GACAS,KAAAA,GACAsF,MAAOD,KAGf,EAAG,CAAC1G,EAAgBqB,GAAMT,GAAU8F,KACpCN,EAAU,WACR9D,GAAQE,QAAS,CACnB,EAAG,iBAOD0C,EAAAC,cACEhH,MAAAA,CAAAA,UAAWiH,EAAGvH,EAAE,aAAcM,GAC9BC,MAAO6E,GACP/E,IAAK,SAAC0I,GACJC,EAAU9D,GAAc6D,GACxB1I,GAAO2I,EAAU3I,EAAK0I,EACxB,EACA3G,aAAcA,EACdC,aAAcA,EACdC,eAAgBA,EAChBC,eAAgBA,EAChBC,QAASA,EACTC,QAASA,gBAGT4E,EAAAC,cAAC2B,EAAO,CAACC,QAAM,GACZ,SAAAC,GAA8B,IAA5BC,EAAUD,EAAVC,WAEMhD,GAFiB+C,EAAXE,YAEgBH,QAAU,CAAE,GAAlC9C,OAIP,YAHeb,IAAXa,GAAwBF,KAAKoD,IAAIlD,EAAS3B,GAAQG,qBAAuB,GAC3EkD,kBAEKT,EAAAC,cAAA,MAAA,CAAK/G,MAAOiF,GAAiBlF,UAAW,qBAAsBD,IAAK,SAAC0I,GACzEC,EAAUI,EAAYL,GACtBC,EAAU/D,GAAY8D,GACtBL,IACF,GACGhG,EAEL,gBAMF2E,EAAAC,cAAA,MAAA,CACEhH,UAAW,sBACXwI,MAAO/H,EAAoB8H,QAAatD,EACxChF,MAAOkF,GACPpF,IAAK0E,IAIJhC,IAAY7B,GAAmBkG,GAC/B1E,GAIT"}
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, MeasureProps} 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 onStatusChange,\n }, ['fold', 'onEllipsisChange', 'onFoldChange', 'onStatusChange']);\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 if (!wrapperRef.current) {\n return;\n }\n const newTextContent = wrapperRef.current.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const handleResize = useCallback<NonNullable<MeasureProps['onResize']>>((rect) => {\n // console.log('[handleResize] rect: ', rect, runtime.contentOffsetHeight);\n const {height} = rect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n }, [calcEllipsis]);\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 runtime.onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [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 throttleMs={100} onResize={handleResize} triggerResizeInit={false}>\n {({measureRef, /* contentRect */}) => {\n // console.log('contentRect:', 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 style={commonWrapStyle} 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$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","newTextContent","handleResize","rect","offset","abs","hoverTitle","title","assignRef","Measure","throttleMs","onResize","triggerResizeInit","_ref6","measureRef"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=require("react"),t=require("@ohkit/prefix-classname"),n=require("@ohkit/react-helper"),i=require("@ohkit/platform"),o=require("@ohkit/measure");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=/*#__PURE__*/l(e);function a(){return a=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},a.apply(null,arguments)}var s=t.prefixClassname("ohkit-text-ellipsis__"),f=e.forwardRef(function(l,f){var u=l.className,d=l.style,c=l.lineHeight,h=void 0===c?"":c,p=l.lines,g=l.maskBgColor,v=void 0===g?"#fff":g,m=l.resetFoldWhenChildrenOrEllipsisChange,C=void 0!==m&&m,x=l.showTitleWhenFold,b=l.titleWhenFold,w=l.showFoldControl,E=void 0===w||w,F=l.foldText,H=void 0===F?"收起":F,y=l.unfoldText,k=void 0===y?"展开":y,M=l.uiType,S=void 0===M?"right":M,R=l.truncateMode,O=void 0===R?"line":R,W=l.maxHeight,N=l.controlPlacement,q=void 0===N?"center":N,B=l.whiteSpace,P=l.width,T=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),ie=ne[0],oe=ne[1],le=e.useState("string"==typeof h&&h.endsWith("px")?parseFloat(h):0),re=le[0],ae=le[1],se=e.useState(p),fe=se[0],ue=void 0===fe?0:fe,de=se[1],ce=e.useState(""),he=ce[0],pe=ce[1],ge=n.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:ee,fold:ee,foldBtnWidth:ie,textContent:he,onEllipsisChange:L,onFoldChange:j},["onEllipsisChange","fold","onFoldChange"])[0],ve=e.useRef(null),me=e.useRef(null),Ce=e.useRef(null),xe=e.useRef(null),be=e.useMemo(function(){return a({lineHeight:Y?"1.4":h||void 0},d)},[d,h,Y]),we=e.useMemo(function(){return{whiteSpace:B,width:P}},[B,P]),Ee=e.useMemo(function(){var e="height"===O;if(!Q||!(e||ue&&re))return we;var t=!E||"bottom"!==S&&ee?void 0:re+"px";return a({},we,{minHeight:!e&&ee?(ue-.2)*re+"px":void 0,WebkitLineClamp:e?void 0:ee?ue:void 0,maxHeight:e&&ee?W||ue*re||ge.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[ue,re,Q,ee,E,S,O,W,we]),Fe=e.useMemo(function(){if(ee){var e=re,t="right"===S?Math.min(e/ie*100,80):60;return{boxSizing:"content-box",height:re+"px",lineHeight:re+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+v+(4===v.length?"0":"00")+", "+v+" "+t+"%, "+v+" 100%)"}}},[re,v,ee,S,ie]),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)})}},[]),ye=e.useCallback(function(e,t){void 0===t&&(t=!ge.fold),ge.fold=t,te(t)},[]),ke=e.useMemo(function(){/*#__PURE__*/return r.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+q),style:Fe,ref:xe,onClick:ye},T?T(ee):/*#__PURE__*/r.default.createElement("div",{className:"btn-fold"},ee?k:H))},[Fe,ee,H,ye,T,q,S,k]),Me=e.useCallback(function(e,t){void 0===e&&(e=ge.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=ge.ellipsis,l=ge.fold,r=ge.defaultFold;e!==o&&(U(e),ge.ellipsis=e,null==ge.onEllipsisChange||ge.onEllipsisChange(e)),C&&(i||!o&&e)&&l!==r&&ye(void 0,r)},[ye,J,C]),Se=e.useCallback(function(){var e=me.current,t=Ce.current;if(e&&t){ge.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),i=parseFloat(n.paddingTop),o=parseFloat(n.paddingBottom),l=ge.containerContentHeight=t.clientHeight-i-o,r=0;if(!r&&e){var a=(n||{}).lineHeight;a&&((r=parseFloat(a))||Z(!0))}if(re===r||(ae(r),r))if("height"===O)Me(ge.contentOffsetHeight>(W||l));else if(p)ue!==p&&de(p),Me(ge.contentOffsetHeight>=(p+1)*Math.floor(r)-1);else if(ge.contentOffsetHeight>l){var s=Math.floor(l/r)||1;ue!==s&&de(s),Me(!0)}else Me(!1)}},[p,re,O,W,Me]);n.useCompatibleEffect(function(){Me(ge.ellipsis,{forceResetFold:!0}),Se()},[Se,Me]),e.useEffect(function(){if(Q&&xe.current){var e=xe.current.offsetWidth;e!==ge.foldBtnWidth&&(ge.foldBtnWidth=e,oe(e))}},[Q,k,E]),e.useEffect(function(){i.isSafari&&He()},[ee,He]);var Re=e.useCallback(function(){var e,t=(null==(e=me.current)?void 0:e.textContent)||"";t!==ge.textContent&&(ge.textContent=t,pe(t))},[]),Oe=e.useMemo(function(){return Q&&ee?"function"==typeof b?b(he):b||he:void 0},[b,Q,ee,he]);return e.useEffect(function(){ge.inited&&(null==z||z({ellipsis:Q,fold:ee,title:Oe}))},[z,ee,Q,Oe]),e.useEffect(function(){ge.inited=!0},[]),/*#__PURE__*/r.default.createElement("div",{className:t.classNames(s("container"),u),style:be,ref:function(e){n.assignRef(Ce,e),f&&n.assignRef(f,e)},onMouseEnter:A,onMouseLeave:_,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:I},/*#__PURE__*/r.default.createElement(o.Measure,{offset:!0},function(e){var t=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-ge.contentOffsetHeight)>1&&Se(),/*#__PURE__*/r.default.createElement("div",{style:we,className:"content-shadow-dom",ref:function(e){n.assignRef(t,e),n.assignRef(me,e),Re()}},J)}),/*#__PURE__*/r.default.createElement("div",{className:"text-ellipsis-inner",title:x?Oe:void 0,style:Ee,ref:ve},Q&&E&&ke,J))});exports.TextEllipsis=f,exports.c=s;
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,S=void 0===F?"收起":F,M=l.unfoldText,k=void 0===M?"展开":M,y=l.uiType,H=void 0===y?"right":y,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,I=l.onPointerEnter,V=l.onPointerLeave,D=l.onClick,G=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,onStatusChange:z},["fold","onEllipsisChange","onFoldChange","onStatusChange"])[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"===H&&!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,H,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]),Se=e.useMemo(function(){if(ee){var e=le,t="right"===H?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"===H?e+"px":void 0,paddingLeft:"right"===H?e+"px":void 0,background:"linear-gradient(to "+H+", "+(void 0===o?"rgba(255,255,255,0.2)":o)+", "+r+" "+t+"%, "+r+" 100%)"}}},[le,ee,H,oe,Fe]),Me=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)})}},[]),ke=e.useCallback(function(e,t){void 0===t&&(t=!pe.fold),pe.fold=t,te(t)},[]),ye=e.useMemo(function(){/*#__PURE__*/return a.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+H,"bottom"===H&&"placement-"+q),style:Se,ref:be,onClick:ke},P?P(ee):/*#__PURE__*/a.default.createElement("div",{className:"btn-fold"},ee?k:S))},[Se,ee,S,ke,P,q,H,k]),He=e.useMemo(function(){return!E||"right"!==H||ee?null:/*#__PURE__*/a.default.createElement("span",{style:Se,className:"btn-fold-right-shadow"},P?P(ee):S)},[H,E,ee,Se,S,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&&ke(void 0,l)},[ke,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,k,E]),e.useEffect(function(){o.isSafari&&Me()},[ee,Me]);var Ne=e.useCallback(function(){if(me.current){var e=me.current.textContent||"";e!==pe.textContent&&(pe.textContent=e,ge(e))}},[]),We=e.useCallback(function(e){var t=(e.offset||{}).height;void 0!==t&&Math.abs(t-pe.contentOffsetHeight)>1&&Oe()},[Oe]),qe=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==pe.onStatusChange||pe.onStatusChange({ellipsis:Q,fold:ee,title:qe}))},[ee,Q,qe]),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:I,onPointerLeave:V,onClick:D,onFocus:G},/*#__PURE__*/a.default.createElement(r.Measure,{offset:!0,throttleMs:100,onResize:We,triggerResizeInit:!1},function(e){var t=e.measureRef;/*#__PURE__*/return 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?qe:void 0,style:Ee,ref:ve},Q&&E&&ye,J,He))});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 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 {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","transparent","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_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, MeasureProps} 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 onStatusChange,\n }, ['fold', 'onEllipsisChange', 'onFoldChange', 'onStatusChange']);\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 if (!wrapperRef.current) {\n return;\n }\n const newTextContent = wrapperRef.current.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const handleResize = useCallback<NonNullable<MeasureProps['onResize']>>((rect) => {\n // console.log('[handleResize] rect: ', rect, runtime.contentOffsetHeight);\n const {height} = rect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n }, [calcEllipsis]);\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 runtime.onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [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 throttleMs={100} onResize={handleResize} triggerResizeInit={false}>\n {({measureRef, /* contentRect */}) => {\n // console.log('contentRect:', 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 style={commonWrapStyle} 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$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","newTextContent","handleResize","rect","offset","abs","hoverTitle","title","assignRef","Measure","throttleMs","onResize","triggerResizeInit","_ref6","measureRef"],"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,classNames as a}from"@ohkit/prefix-classname";import{useSyncPropsState as d,useRuntime as c,useCompatibleEffect as f,assignRef as h}from"@ohkit/react-helper";import{isSafari as p}from"@ohkit/platform";import{Measure as u}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:k=!0,foldText:O="收起",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]=d(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]=c({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]),pe=i(()=>({whiteSpace:N,width:P}),[N,P]),ue=i(()=>{const e="height"===B;if(!J||!(e||oe&&te))return pe;const t=!k||"bottom"!==W&&X?void 0:`${te}px`;return g({},pe,{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,k,W,B,S,pe]),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:a("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:O)),[ge,X,O,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,k]),r(()=>{p&&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:a(m("container"),v),style:he,ref:e=>{h(ce,e),s&&h(s,e)},onMouseEnter:z,onMouseLeave:A,onPointerEnter:_,onPointerLeave:V,onClick:D,onFocus:G},/*#__PURE__*/e.createElement(u,{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:pe,className:"content-shadow-dom",ref:e=>{h(t,e),h(de,e),Fe()}},I)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:y?He:void 0,style:ue,ref:ae},J&&k&&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 r,useEffect as l}from"react";import{prefixClassname as s,classNames as a}from"@ohkit/prefix-classname";import{useSyncPropsState as d,useRuntime as h,useCompatibleEffect as c,assignRef as f}from"@ohkit/react-helper";import{isSafari as g}from"@ohkit/platform";import{rgbaToObj as u,findEffectiveBgColor as p}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:S,showFoldControl:M=!0,foldText:O="收起",unfoldText:W="展开",uiType:B="right",truncateMode:N="line",maxHeight:L,controlPlacement:P="center",whiteSpace:T,width:R,renderFoldButton:z,onEllipsisChange:j,onFoldChange:q,onStatusChange:A,onMouseEnter:_,onMouseLeave:I,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:J}=t,K=$||E,[Q,U]=n(!1),[X,Y]=n(!1),[Z,ee]=d(t,"fold",{defaultValue:!0,onChange:q}),[te,ne]=n(1),[oe,ie]=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),[re=0,le]=n(F),[se,ae]=n(""),[de]=h({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:Z,fold:Z,foldBtnWidth:te,textContent:se,onEllipsisChange:j,onFoldChange:q,onStatusChange:A},["fold","onEllipsisChange","onFoldChange","onStatusChange"]),he=o(null),ce=o(null),fe=o(null),ge=o(null),ue=i(()=>C({lineHeight:X?"1.4":w||void 0},b),[b,w,X]),pe=i(()=>({whiteSpace:T,width:R}),[T,R]),me=i(()=>{const e="height"===N;if(!Q||!(e||re&&oe))return pe;const t=M&&"bottom"===B&&!Z?`${oe}px`:void 0;return C({},pe,{minHeight:!e&&Z?(re-.2)*oe+"px":void 0,WebkitLineClamp:e?void 0:Z?re:void 0,maxHeight:e&&Z?L||re*oe||de.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[re,oe,Q,Z,M,B,N,L,pe]),Ce=i(()=>{if(!M||!Q)return null;const{r:e,g:t,b:n}=u(H||"")||p(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"===B?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"===B?`${e}px`:void 0,paddingLeft:"right"===B?`${e}px`:void 0,background:`linear-gradient(to ${B}, ${n}, ${o} ${t}%, ${o} 100%)`}},[oe,Z,B,te,Ce]),xe=r(()=>{if(he.current){const e=he.current.style.width,t=window.getComputedStyle(he.current).width;he.current.style.width=parseFloat(t)-.1+"px",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{he.current&&(he.current.style.width=e)})}},[]),be=r((e,t=!de.fold)=>{de.fold=t,ee(t)},[]),we=i(()=>/*#__PURE__*/e.createElement("div",{className:a("btn-fold-wrapper",`btn-fold-wrapper-${B}`,"bottom"===B&&`placement-${P}`),style:ve,ref:ge,onClick:be},z?z(Z):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},Z?W:O)),[ve,Z,O,be,z,P,B,W]),Fe=i(()=>!M||"right"!==B||Z?null:/*#__PURE__*/e.createElement("span",{style:ve,className:"btn-fold-right-shadow"},z?z(Z):O),[B,M,Z,ve,O,z]),He=r((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=r(()=>{const e=ce.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),r=de.containerContentHeight=t.clientHeight-o-i;let l=0;if(!l&&e){const{lineHeight:e}=n||{};e&&(l=parseFloat(e),l||Y(!0))}if(oe===l||(ie(l),l))if("height"===N)He(de.contentOffsetHeight>(L||r));else if(F)re!==F&&le(F),He(de.contentOffsetHeight>=(F+1)*Math.floor(l)-1);else if(de.contentOffsetHeight>r){const e=Math.floor(r/l)||1;re!==e&&le(e),He(!0)}else He(!1)},[F,oe,N,L,He]);c(()=>{He(de.ellipsis,{forceResetFold:!0}),$e()},[$e,He]),l(()=>{if(Q&&ge.current){const{offsetWidth:e}=ge.current;e!==de.foldBtnWidth&&(de.foldBtnWidth=e,ne(e))}},[Q,W,M]),l(()=>{g&&xe()},[Z,xe]);const Ee=r(()=>{if(!ce.current)return;const e=ce.current.textContent||"";e!==de.textContent&&(de.textContent=e,ae(e))},[]),ye=r(e=>{const{height:t}=e.offset||{};void 0!==t&&Math.abs(t-de.contentOffsetHeight)>1&&$e()},[$e]),ke=i(()=>Q&&Z?"function"==typeof S?S(se):S||se:void 0,[S,Q,Z,se]);return l(()=>{de.inited&&(null==de.onStatusChange||de.onStatusChange({ellipsis:Q,fold:Z,title:ke}))},[Z,Q,ke]),l(()=>{de.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:a(v("container"),x),style:ue,ref:e=>{f(fe,e),s&&f(s,e)},onMouseEnter:_,onMouseLeave:I,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:J},/*#__PURE__*/e.createElement(m,{offset:!0,throttleMs:100,onResize:ye,triggerResizeInit:!1},({measureRef:t})=>/*#__PURE__*/e.createElement("div",{style:pe,className:"content-shadow-dom",ref:e=>{f(t,e),f(ce,e),Ee()}},K)),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:k?ke:void 0,style:me,ref:he},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 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 {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, MeasureProps} 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 onStatusChange,\n }, ['fold', 'onEllipsisChange', 'onFoldChange', 'onStatusChange']);\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 if (!wrapperRef.current) {\n return;\n }\n const newTextContent = wrapperRef.current.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const handleResize = useCallback<NonNullable<MeasureProps['onResize']>>((rect) => {\n // console.log('[handleResize] rect: ', rect, runtime.contentOffsetHeight);\n const {height} = rect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n }, [calcEllipsis]);\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 runtime.onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [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 throttleMs={100} onResize={handleResize} triggerResizeInit={false}>\n {({measureRef, /* contentRect */}) => {\n // console.log('contentRect:', 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 style={commonWrapStyle} 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","newTextContent","handleResize","rect","offset","abs","hoverTitle","title","assignRef","Measure","throttleMs","onResize","triggerResizeInit","measureRef"],"mappings":"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"}
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/prefix-classname"),require("@ohkit/react-helper"),require("@ohkit/platform"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/prefix-classname","@ohkit/react-helper","@ohkit/platform","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.prefixClassname,e.reactHelper,e.platform,e.measure)}(this,function(e,t,n,i,o,l){function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=/*#__PURE__*/a(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 f=n.prefixClassname("ohkit-text-ellipsis__"),u=t.forwardRef(function(e,a){var u=e.className,d=e.style,c=e.lineHeight,h=void 0===c?"":c,p=e.lines,g=e.maskBgColor,v=void 0===g?"#fff":g,m=e.resetFoldWhenChildrenOrEllipsisChange,C=void 0!==m&&m,x=e.showTitleWhenFold,b=e.titleWhenFold,w=e.showFoldControl,E=void 0===w||w,y=e.foldText,F=void 0===y?"收起":y,k=e.unfoldText,H=void 0===k?"展开":k,M=e.uiType,S=void 0===M?"right":M,R=e.truncateMode,O=void 0===R?"line":R,T=e.maxHeight,W=e.controlPlacement,N=void 0===W?"center":W,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],$=i.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:j}),ee=$[0],te=$[1],ne=t.useState(1),ie=ne[0],oe=ne[1],le=t.useState("string"==typeof h&&h.endsWith("px")?parseFloat(h):0),ae=le[0],re=le[1],se=t.useState(p),fe=se[0],ue=void 0===fe?0:fe,de=se[1],ce=t.useState(""),he=ce[0],pe=ce[1],ge=i.useRuntime({inited:!1,contentOffsetHeight:0,containerContentHeight:0,ellipsis:Q,defaultFold:ee,fold:ee,foldBtnWidth:ie,textContent:he,onEllipsisChange:L,onFoldChange:j},["onEllipsisChange","fold","onFoldChange"])[0],ve=t.useRef(null),me=t.useRef(null),Ce=t.useRef(null),xe=t.useRef(null),be=t.useMemo(function(){return s({lineHeight:Y?"1.4":h||void 0},d)},[d,h,Y]),we=t.useMemo(function(){return{whiteSpace:q,width:B}},[q,B]),Ee=t.useMemo(function(){var e="height"===O;if(!Q||!(e||ue&&ae))return we;var t=!E||"bottom"!==S&&ee?void 0:ae+"px";return s({},we,{minHeight:!e&&ee?(ue-.2)*ae+"px":void 0,WebkitLineClamp:e?void 0:ee?ue:void 0,maxHeight:e&&ee?T||ue*ae||ge.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[ue,ae,Q,ee,E,S,O,T,we]),ye=t.useMemo(function(){if(ee){var e=ae,t="right"===S?Math.min(e/ie*100,80):60;return{boxSizing:"content-box",height:ae+"px",lineHeight:ae+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+v+(4===v.length?"0":"00")+", "+v+" "+t+"%, "+v+" 100%)"}}},[ae,v,ee,S,ie]),Fe=t.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)})}},[]),ke=t.useCallback(function(e,t){void 0===t&&(t=!ge.fold),ge.fold=t,te(t)},[]),He=t.useMemo(function(){/*#__PURE__*/return r.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+N),style:ye,ref:xe,onClick:ke},P?P(ee):/*#__PURE__*/r.default.createElement("div",{className:"btn-fold"},ee?H:F))},[ye,ee,F,ke,P,N,S,H]),Me=t.useCallback(function(e,t){void 0===e&&(e=ge.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=ge.ellipsis,l=ge.fold,a=ge.defaultFold;e!==o&&(U(e),ge.ellipsis=e,null==ge.onEllipsisChange||ge.onEllipsisChange(e)),C&&(i||!o&&e)&&l!==a&&ke(void 0,a)},[ke,J,C]),Se=t.useCallback(function(){var e=me.current,t=Ce.current;if(e&&t){ge.contentOffsetHeight=e.offsetHeight;var n=window.getComputedStyle(t),i=parseFloat(n.paddingTop),o=parseFloat(n.paddingBottom),l=ge.containerContentHeight=t.clientHeight-i-o,a=0;if(!a&&e){var r=(n||{}).lineHeight;r&&((a=parseFloat(r))||Z(!0))}if(ae===a||(re(a),a))if("height"===O)Me(ge.contentOffsetHeight>(T||l));else if(p)ue!==p&&de(p),Me(ge.contentOffsetHeight>=(p+1)*Math.floor(a)-1);else if(ge.contentOffsetHeight>l){var s=Math.floor(l/a)||1;ue!==s&&de(s),Me(!0)}else Me(!1)}},[p,ae,O,T,Me]);i.useCompatibleEffect(function(){Me(ge.ellipsis,{forceResetFold:!0}),Se()},[Se,Me]),t.useEffect(function(){if(Q&&xe.current){var e=xe.current.offsetWidth;e!==ge.foldBtnWidth&&(ge.foldBtnWidth=e,oe(e))}},[Q,H,E]),t.useEffect(function(){o.isSafari&&Fe()},[ee,Fe]);var Re=t.useCallback(function(){var e,t=(null==(e=me.current)?void 0:e.textContent)||"";t!==ge.textContent&&(ge.textContent=t,pe(t))},[]),Oe=t.useMemo(function(){return Q&&ee?"function"==typeof b?b(he):b||he:void 0},[b,Q,ee,he]);return t.useEffect(function(){ge.inited&&(null==z||z({ellipsis:Q,fold:ee,title:Oe}))},[z,ee,Q,Oe]),t.useEffect(function(){ge.inited=!0},[]),/*#__PURE__*/r.default.createElement("div",{className:n.classNames(f("container"),u),style:be,ref:function(e){i.assignRef(Ce,e),a&&i.assignRef(a,e)},onMouseEnter:A,onMouseLeave:_,onPointerEnter:V,onPointerLeave:D,onClick:G,onFocus:I},/*#__PURE__*/r.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&&Se(),/*#__PURE__*/r.default.createElement("div",{style:we,className:"content-shadow-dom",ref:function(e){i.assignRef(t,e),i.assignRef(me,e),Re()}},J)}),/*#__PURE__*/r.default.createElement("div",{className:"text-ellipsis-inner",title:x?Oe:void 0,style:Ee,ref:ve},Q&&E&&He,J))});e.TextEllipsis=u,e.c=f});
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 u(){return u=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},u.apply(null,arguments)}var f=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,S=e.uiType,M=void 0===S?"right":S,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,I=e.onPointerEnter,V=e.onPointerLeave,D=e.onClick,G=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),ue=se[0],fe=void 0===ue?0:ue,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,onStatusChange:z},["fold","onEllipsisChange","onFoldChange","onStatusChange"])[0],me=t.useRef(null),ve=t.useRef(null),Ce=t.useRef(null),be=t.useRef(null),xe=t.useMemo(function(){return u({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||fe&&le))return Ee;var t=w&&"bottom"===M&&!ee?le+"px":void 0;return u({},Ee,{minHeight:!e&&ee?(fe-.2)*le+"px":void 0,WebkitLineClamp:e?void 0:ee?fe:void 0,maxHeight:e&&ee?T||fe*le||ge.containerContentHeight||0:void 0,paddingBottom:t,boxSizing:t?"border-box":void 0})},[fe,le,Q,ee,w,M,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"===M?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"===M?e+"px":void 0,paddingLeft:"right"===M?e+"px":void 0,background:"linear-gradient(to "+M+", "+(void 0===o?"rgba(255,255,255,0.2)":o)+", "+r+" "+t+"%, "+r+" 100%)"}}},[le,ee,M,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)},[]),Se=t.useMemo(function(){/*#__PURE__*/return s.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+M,"bottom"===M&&"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,M,H]),Me=t.useMemo(function(){return!w||"right"!==M||ee?null:/*#__PURE__*/s.default.createElement("span",{style:ye,className:"btn-fold-right-shadow"},P?P(ee):y)},[M,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)fe!==g&&de(g),Re(ge.contentOffsetHeight>=(g+1)*Math.floor(l)-1);else if(ge.contentOffsetHeight>r){var s=Math.floor(r/l)||1;fe!==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(){if(ve.current){var e=ve.current.textContent||"";e!==ge.textContent&&(ge.textContent=e,pe(e))}},[]),Ne=t.useCallback(function(e){var t=(e.offset||{}).height;void 0!==t&&Math.abs(t-ge.contentOffsetHeight)>1&&Oe()},[Oe]),We=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==ge.onStatusChange||ge.onStatusChange({ellipsis:Q,fold:ee,title:We}))},[ee,Q,We]),t.useEffect(function(){ge.inited=!0},[]),/*#__PURE__*/s.default.createElement("div",{className:n.classNames(f("container"),d),style:xe,ref:function(e){o.assignRef(Ce,e),a&&o.assignRef(a,e)},onMouseEnter:A,onMouseLeave:_,onPointerEnter:I,onPointerLeave:V,onClick:D,onFocus:G},/*#__PURE__*/s.default.createElement(l.Measure,{offset:!0,throttleMs:100,onResize:Ne,triggerResizeInit:!1},function(e){var t=e.measureRef;/*#__PURE__*/return 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?We:void 0,style:we,ref:me},Q&&w&&Se,J,Me))});e.TextEllipsis=d,e.c=f});
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 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 {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","transparent","length","reorganizeDom","useCallback","current","orginStyleWidth","orginWidth","window","getComputedStyle","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_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, MeasureProps} 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 onStatusChange,\n }, ['fold', 'onEllipsisChange', 'onFoldChange', 'onStatusChange']);\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 if (!wrapperRef.current) {\n return;\n }\n const newTextContent = wrapperRef.current.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const handleResize = useCallback<NonNullable<MeasureProps['onResize']>>((rect) => {\n // console.log('[handleResize] rect: ', rect, runtime.contentOffsetHeight);\n const {height} = rect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n }, [calcEllipsis]);\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 runtime.onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [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 throttleMs={100} onResize={handleResize} triggerResizeInit={false}>\n {({measureRef, /* contentRect */}) => {\n // console.log('contentRect:', 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 style={commonWrapStyle} 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$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","clientHeight","realLineHeight","floor","adjustLines","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","newTextContent","handleResize","rect","offset","abs","hoverTitle","title","assignRef","Measure","throttleMs","onResize","triggerResizeInit","_ref6","measureRef"],"mappings":"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"}
@@ -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.8",
3
+ "version": "0.0.10",
4
4
  "description": "text ellipsis for react",
5
5
  "keywords": [
6
6
  "text ellipsis"
@@ -32,7 +32,8 @@
32
32
  "clean": "rm -rf dist"
33
33
  },
34
34
  "dependencies": {
35
- "@ohkit/measure": "^0.0.3",
35
+ "@ohkit/dom-helper": "0.0.1",
36
+ "@ohkit/measure": "0.0.5",
36
37
  "@ohkit/platform": "0.0.3",
37
38
  "@ohkit/prefix-classname": "0.0.3",
38
39
  "@ohkit/react-helper": "0.0.4"
@@ -45,5 +46,5 @@
45
46
  "react": ">=16.8.0",
46
47
  "react-dom": ">=16.8.0"
47
48
  },
48
- "gitHead": "3fb061e6a6bf7ccd9f26f0339080da0a4df02075"
49
+ "gitHead": "650b2c1789fdfe5f7614455fbc7e42dcee96b7ac"
49
50
  }
package/src/index.tsx CHANGED
@@ -25,7 +25,8 @@ import {
25
25
  useSyncPropsState,
26
26
  } from "@ohkit/react-helper";
27
27
  import {isSafari} from "@ohkit/platform";
28
- import {Measure} from "@ohkit/measure";
28
+ import {rgbaToObj, findEffectiveBgColor} from "@ohkit/dom-helper";
29
+ import {Measure, MeasureProps} from "@ohkit/measure";
29
30
  import "./style.scss";
30
31
 
31
32
  export const c = p("ohkit-text-ellipsis__");
@@ -103,7 +104,7 @@ interface ITextEllipsis
103
104
  */
104
105
  controlPlacement?: 'left' | 'center' | 'right';
105
106
  /**
106
- * 展开按钮文字
107
+ * 折叠按钮文字
107
108
  * @default 收起
108
109
  */
109
110
  foldText?: string;
@@ -161,7 +162,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
161
162
  style,
162
163
  lineHeight = "",
163
164
  lines,
164
- maskBgColor = "#fff",
165
+ maskBgColor,
165
166
  content,
166
167
  children,
167
168
  resetFoldWhenChildrenOrEllipsisChange = false,
@@ -214,7 +215,8 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
214
215
  textContent,
215
216
  onEllipsisChange,
216
217
  onFoldChange,
217
- }, ['onEllipsisChange', 'fold', 'onFoldChange']);
218
+ onStatusChange,
219
+ }, ['fold', 'onEllipsisChange', 'onFoldChange', 'onStatusChange']);
218
220
 
219
221
  const contentRef = useRef<HTMLDivElement>(null);
220
222
  const wrapperRef = useRef<HTMLDivElement>(null);
@@ -235,7 +237,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
235
237
  whiteSpace,
236
238
  width,
237
239
  };
238
- }, [whiteSpace, width])
240
+ }, [whiteSpace, width]);
239
241
  // 容器样式
240
242
  const wrapStyle = useMemo(() => {
241
243
  const lines = innerLines;
@@ -243,7 +245,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
243
245
  if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {
244
246
  return commonWrapStyle;
245
247
  }
246
- const paddingBottom = showFoldControl && (uiType === "bottom" || !fold) ? `${innerLineHeight}px` : undefined;
248
+ const paddingBottom = showFoldControl && (uiType === "bottom" && !fold) ? `${innerLineHeight}px` : undefined;
247
249
  return {
248
250
  ...commonWrapStyle,
249
251
  // HACK: 兼容safari 15+ 富文本折叠高度丢失问题
@@ -258,6 +260,18 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
258
260
  };
259
261
  }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);
260
262
 
263
+ // 计算折叠按钮蒙层的渐变颜色
264
+ const validMaskBgColor = useMemo(() => {
265
+ if (!showFoldControl || !ellipsis) {
266
+ return null;
267
+ }
268
+ const {r, g, b} = rgbaToObj(maskBgColor || '') || findEffectiveBgColor(containerRef.current);
269
+ return {
270
+ startColor: `rgba(${r}, ${g}, ${b}, 0.2)`,
271
+ endColor: `rgba(${r}, ${g}, ${b}, 1)`,
272
+ };
273
+ }, [maskBgColor, ellipsis, showFoldControl]);
274
+
261
275
  // 展开|收起 按钮样式
262
276
  const btnStyle = useMemo(() => {
263
277
  if (!fold) {
@@ -267,10 +281,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
267
281
  const padding = innerLineHeight;
268
282
  // 蒙层透明度所占比例
269
283
  const ratio = uiType === "right" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;
270
- // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色
271
- const transparent = `${maskBgColor}${
272
- maskBgColor.length === 4 ? "0" : "00"
273
- }`;
284
+ const {startColor = 'rgba(255,255,255,0.2)', endColor = 'rgba(255,255,255,1)'} = validMaskBgColor || {};
274
285
  return {
275
286
  boxSizing: 'content-box' as const,
276
287
  height: `${innerLineHeight}px`,
@@ -278,9 +289,9 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
278
289
  paddingTop: uiType === "bottom" ? `${padding}px` : undefined,
279
290
  paddingLeft: uiType === "right" ? `${padding}px` : undefined,
280
291
  // 渐变蒙层
281
- background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,
292
+ background: `linear-gradient(to ${uiType}, ${startColor}, ${endColor} ${ratio}%, ${endColor} 100%)`,
282
293
  };
283
- }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);
294
+ }, [innerLineHeight, fold, uiType, foldBtnWidth, validMaskBgColor]);
284
295
 
285
296
  const reorganizeDom = useCallback(() => {
286
297
  // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发
@@ -333,6 +344,16 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
333
344
  unfoldText,
334
345
  ]);
335
346
 
347
+ // 占位按钮
348
+ const ButtonShadowDom = useMemo(() => {
349
+ if (!showFoldControl || uiType !== 'right' || fold) {
350
+ return null;
351
+ }
352
+ return <span style={btnStyle} className="btn-fold-right-shadow">
353
+ {renderFoldButton ? renderFoldButton(fold) : foldText}
354
+ </span>;
355
+ }, [uiType, showFoldControl, fold, btnStyle, foldText, renderFoldButton]);
356
+
336
357
  // 重置状态
337
358
  const resetState = useCallback((newEllipsis = runtime.ellipsis, {
338
359
  forceResetFold = false, // 强制重置fold 比如child变化时
@@ -439,12 +460,22 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
439
460
  }
440
461
  }, [fold, reorganizeDom]);
441
462
  const updateTextContent = useCallback(() => {
442
- const newTextContent = wrapperRef.current?.textContent || '';
463
+ if (!wrapperRef.current) {
464
+ return;
465
+ }
466
+ const newTextContent = wrapperRef.current.textContent || '';
443
467
  if (newTextContent !== runtime.textContent) {
444
468
  runtime.textContent = newTextContent;
445
469
  setTextContent(newTextContent);
446
470
  }
447
471
  }, []);
472
+ const handleResize = useCallback<NonNullable<MeasureProps['onResize']>>((rect) => {
473
+ // console.log('[handleResize] rect: ', rect, runtime.contentOffsetHeight);
474
+ const {height} = rect.offset || {};
475
+ if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {
476
+ calcEllipsis();
477
+ }
478
+ }, [calcEllipsis]);
448
479
  const hoverTitle = useMemo(() => {
449
480
  return ellipsis && fold
450
481
  ? (typeof titleWhenFold === 'function'
@@ -454,13 +485,13 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
454
485
  }, [titleWhenFold, ellipsis, fold, textContent]);
455
486
  useEffect(() => {
456
487
  if (runtime.inited) {
457
- onStatusChange?.({
488
+ runtime.onStatusChange?.({
458
489
  ellipsis,
459
490
  fold,
460
491
  title: hoverTitle
461
492
  });
462
493
  }
463
- }, [onStatusChange, fold, ellipsis, hoverTitle]);
494
+ }, [fold, ellipsis, hoverTitle]);
464
495
  useEffect(() => {
465
496
  runtime.inited = true;
466
497
  }, []);
@@ -485,13 +516,13 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
485
516
  onFocus={onFocus}
486
517
  >
487
518
  {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}
488
- <Measure offset>
489
- {({measureRef, contentRect}) => {
490
- // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);
491
- const {height} = contentRect.offset || {};
492
- if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {
493
- calcEllipsis();
494
- }
519
+ <Measure offset throttleMs={100} onResize={handleResize} triggerResizeInit={false}>
520
+ {({measureRef, /* contentRect */}) => {
521
+ // console.log('contentRect:', contentRect, contentRect.offset?.height, runtime.contentOffsetHeight);
522
+ // const {height} = contentRect.offset || {};
523
+ // if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {
524
+ // calcEllipsis();
525
+ // }
495
526
  return <div style={commonWrapStyle} className={"content-shadow-dom"} ref={(r) => {
496
527
  assignRef(measureRef, r);
497
528
  assignRef(wrapperRef, r);
@@ -501,7 +532,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
501
532
  </div>
502
533
  }}
503
534
  </Measure>
504
- {/* <div className={"content-shadow-dom"} ref={wrapperRef}>
535
+ {/* <div style={commonWrapStyle} className={"content-shadow-dom"} ref={wrapperRef}>
505
536
  {finalContent}
506
537
  </div> */}
507
538
  {/* 主文本显示 */}
@@ -515,6 +546,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
515
546
  {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}
516
547
  {ellipsis && showFoldControl && ButtonComp}
517
548
  {finalContent}
549
+ {ButtonShadowDom}
518
550
  </div>
519
551
  </div>
520
552
  );
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 {