@ohkit/text-ellipsis 0.0.6 → 0.0.8

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
@@ -39,9 +39,12 @@ function Demo() {
39
39
  | 参数 | 说明 | 类型 | 默认值 |
40
40
  |------|------|------|--------|
41
41
  | className | 自定义样式类名,会附加到根元素上 | string | - |
42
+ | style | 自定义样式 | React.CSSProperties | - |
42
43
  | lines | 超过几行折叠 (number > 0) | number | - |
43
44
  | uiType | 展开按钮位置 `right`(右下侧) 或 `bottom`(底部) | string | `right` |
45
+ | truncateMode | 截断模式 `line`(行数) 或 `height`(高度) | string | `line` |
44
46
  | lineHeight | 行高(支持px/em等单位或数字) | number/string | - |
47
+ | maxHeight | 高度模式下的最大高度(px) | number | - |
45
48
  | content | 文本内容 | ReactNode | - |
46
49
  | fold | 是否折叠 | boolean | `true` |
47
50
  | showFoldControl | 是否显示展开控制按钮 | boolean | `true` |
@@ -85,7 +88,7 @@ function Demo() {
85
88
  ```
86
89
 
87
90
  ## 注意事项
88
- 1. Safari浏览器下富文本截断可能有轻微高度计算偏差
89
- 2. 图片和表格等复杂内容可能无法完美截断
91
+ 1. Safari浏览器下富文本截断可能有轻微高度计算偏差,可自行判断浏览器环境降级使用`height`截断模式
92
+ 2. 富文本中图片和表格等复杂内容可能无法完美截断
90
93
  3. 动态内容变化后会自动重新计算截断状态
91
94
  4. 使用`whiteSpace`保留换行符时建议设置`width`属性
package/dist/index.css CHANGED
@@ -1,2 +1,2 @@
1
- .ohkit-text-ellipsis__container{display:flex;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-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}
2
2
  /*# sourceMappingURL=index.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,gCACE,YAAa,CACb,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 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,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}"]}
package/dist/index.es.js CHANGED
@@ -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 d,useSyncPropsState as f,useRuntime as a,classNames as s,useCompatibleEffect as u,isSafari as c,assignRef as h}from"@ohkit/utils";import{Measure as v}from"@ohkit/measure";function p(){return p=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},p.apply(null,arguments)}var g=d("ohkit-text-ellipsis__"),m=t(function(t,d){var m=t.className,C=t.lineHeight,w=void 0===C?"":C,x=t.lines,F=t.maskBgColor,E=void 0===F?"#fff":F,b=t.resetFoldWhenChildrenOrEllipsisChange,y=void 0!==b&&b,H=t.showTitleWhenFold,k=t.titleWhenFold,O=t.showFoldControl,W=void 0===O||O,M=t.foldText,B=void 0===M?"收起":M,L=t.unfoldText,N=void 0===L?"展开":L,P=t.uiType,S=void 0===P?"right":P,T=t.controlPlacement,R=void 0===T?"center":T,j=t.whiteSpace,q=t.width,A=t.renderFoldButton,_=t.onEllipsisChange,z=t.onFoldChange,V=t.onStatusChange,D=t.onMouseEnter,G=t.onMouseLeave,I=t.onPointerEnter,J=t.onPointerLeave,K=t.onClick,Q=t.onFocus,U=t.content||t.children,X=n(!1),Y=X[0],Z=X[1],$=n(!1),ee=$[0],te=$[1],ne=f(t,"fold",{defaultValue:!0,onChange:z}),oe=ne[0],ie=ne[1],le=n(1),re=le[0],de=le[1],fe=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),ae=fe[0],se=fe[1],ue=n(x),ce=ue[0],he=void 0===ce?0:ce,ve=ue[1],pe=n(""),ge=pe[0],me=pe[1],Ce=a({inited:!1,contentOffsetHeight:0,ellipsis:Y,defaultFold:oe,fold:oe,foldBtnWidth:re,textContent:ge,onEllipsisChange:_,onFoldChange:z},["onEllipsisChange","fold","onFoldChange"])[0],we=o(null),xe=o(null),Fe=o(null),Ee=o(null),be=i(function(){return{lineHeight:ee?"1.4":w||void 0}},[w,ee]),ye=i(function(){return{whiteSpace:j,width:q}},[j,q]),He=i(function(){return Y&&he&&ae?p({},ye,{minHeight:oe?(he-.2)*ae+"px":void 0,WebkitLineClamp:oe?he:void 0,paddingBottom:"bottom"!==S&&oe?void 0:ae+"px"}):ye},[he,ae,Y,oe,S,ye]),ke=i(function(){if(oe){var e=ae,t="right"===S?Math.min(e/re*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+", "+E+(4===E.length?"0":"00")+", "+E+" "+t+"%, "+E+" 100%)"}}},[ae,E,oe,S,re]),Oe=l(function(){we.current&&(we.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){we.current&&(we.current.style.width="100%")}))},[]),We=l(function(e,t){void 0===t&&(t=!Ce.fold),Ce.fold=t,ie(t)},[]),Me=i(function(){/*#__PURE__*/return e.createElement("div",{className:s("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+R),style:ke,ref:Ee,onClick:We},A?A(oe):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},oe?N:B))},[ke,oe,B,We,A,R,S,N]),Be=l(function(e,t){void 0===e&&(e=Ce.ellipsis);var n=(void 0===t?{}:t).forceResetFold,o=void 0!==n&&n,i=Ce.ellipsis,l=Ce.fold,r=Ce.defaultFold;e!==i&&(Z(e),Ce.ellipsis=e,null==Ce.onEllipsisChange||Ce.onEllipsisChange(e)),y&&(o||!i&&e)&&l!==r&&We(void 0,r)},[We,U,y]),Le=l(function(){var e=xe.current,t=Fe.current;if(e&&t){Ce.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||te(!0))}if(ae===n||(se(n),n))if(x)he!==x&&ve(x),Be(Ce.contentOffsetHeight>=(x+1)*n-1);else if(Ce.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);he!==i&&ve(i),Be(!0)}else Be(!1)}},[x,ae,Be]);u(function(){Be(Ce.ellipsis,{forceResetFold:!0}),Le()},[Le,Be]),r(function(){if(Y&&Ee.current){var e=Ee.current.offsetWidth;e!==Ce.foldBtnWidth&&(Ce.foldBtnWidth=e,de(e))}},[Y,N,W]),r(function(){c&&Oe()},[oe,Oe]);var Ne=l(function(){var e,t=(null==(e=xe.current)?void 0:e.textContent)||"";t!==Ce.textContent&&(Ce.textContent=t,me(t))},[]),Pe=i(function(){return Y&&oe?"function"==typeof k?k(ge):k||ge:void 0},[k,Y,oe,ge]);return r(function(){Ce.inited&&(null==V||V({ellipsis:Y,fold:oe,title:Pe}))},[V,oe,Y,Pe]),r(function(){Ce.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:s(g("container"),m),style:be,ref:function(e){h(Fe,e),d&&h(d,e)},onMouseEnter:D,onMouseLeave:G,onPointerEnter:I,onPointerLeave:J,onClick:K,onFocus:Q},/*#__PURE__*/e.createElement(v,{offset:!0},function(t){var n=t.measureRef,o=(t.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-Ce.contentOffsetHeight)>1&&Le(),/*#__PURE__*/e.createElement("div",{style:ye,className:"content-shadow-dom",ref:function(e){h(n,e),h(xe,e),Ne()}},U)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:H?Pe:void 0,style:He,ref:we},Y&&W&&Me,U))});export{m as TextEllipsis,g as c};
1
+ import t,{forwardRef as e,useState as n,useRef as o,useMemo as i,useCallback as r,useEffect as l}from"react";import{prefixClassname as a,classNames as d}from"@ohkit/prefix-classname";import{useSyncPropsState as 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};
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 \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\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 * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\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 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 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,\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 };\n }, [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 if (!ellipsis || !lines || !innerLineHeight) {\n return commonWrapStyle;\n }\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, 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 // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\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 let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\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 if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\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 // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, 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, offsetHeight} = 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 // 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","_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$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","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","commonWrapStyle","wrapStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport {\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/react-helper\";\nimport {isSafari} from \"@ohkit/platform\";\nimport {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":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=/*#__PURE__*/i(e);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},l.apply(null,arguments)}var s=t.prefixClassname("ohkit-text-ellipsis__"),a=e.forwardRef(function(i,a){var r=i.className,f=i.lineHeight,u=void 0===f?"":f,d=i.lines,c=i.maskBgColor,h=void 0===c?"#fff":c,v=i.resetFoldWhenChildrenOrEllipsisChange,p=void 0!==v&&v,g=i.showTitleWhenFold,m=i.titleWhenFold,C=i.showFoldControl,E=void 0===C||C,b=i.foldText,w=void 0===b?"收起":b,x=i.unfoldText,F=void 0===x?"展开":x,y=i.uiType,S=void 0===y?"right":y,k=i.controlPlacement,M=void 0===k?"center":k,R=i.whiteSpace,H=i.width,O=i.renderFoldButton,W=i.onEllipsisChange,N=i.onFoldChange,P=i.onStatusChange,B=i.onMouseEnter,L=i.onMouseLeave,T=i.onPointerEnter,q=i.onPointerLeave,j=i.onClick,A=i.onFocus,_=i.content||i.children,z=e.useState(!1),V=z[0],D=z[1],G=e.useState(!1),I=G[0],J=G[1],K=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:N}),Q=K[0],U=K[1],X=e.useState(1),Y=X[0],Z=X[1],$=e.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),ee=$[0],te=$[1],ne=e.useState(d),ie=ne[0],oe=void 0===ie?0:ie,le=ne[1],se=e.useState(""),ae=se[0],re=se[1],fe=t.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:V,defaultFold:Q,fold:Q,foldBtnWidth:Y,textContent:ae,onEllipsisChange:W,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"])[0],ue=e.useRef(null),de=e.useRef(null),ce=e.useRef(null),he=e.useRef(null),ve=e.useMemo(function(){return{lineHeight:I?"1.4":u||void 0}},[u,I]),pe=e.useMemo(function(){return{whiteSpace:R,width:H}},[R,H]),ge=e.useMemo(function(){return V&&oe&&ee?l({},pe,{minHeight:Q?(oe-.2)*ee+"px":void 0,WebkitLineClamp:Q?oe:void 0,paddingBottom:"bottom"!==S&&Q?void 0:ee+"px"}):pe},[oe,ee,V,Q,S,pe]),me=e.useMemo(function(){if(Q){var e=ee,t="right"===S?Math.min(e/Y*100,80):60;return{boxSizing:"content-box",height:ee+"px",lineHeight:ee+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+h+(4===h.length?"0":"00")+", "+h+" "+t+"%, "+h+" 100%)"}}},[ee,h,Q,S,Y]),Ce=e.useCallback(function(){ue.current&&(ue.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ue.current&&(ue.current.style.width="100%")}))},[]),Ee=e.useCallback(function(e,t){void 0===t&&(t=!fe.fold),fe.fold=t,U(t)},[]),be=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+M),style:me,ref:he,onClick:Ee},O?O(Q):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},Q?F:w))},[me,Q,w,Ee,O,M,S,F]),we=e.useCallback(function(e,t){void 0===e&&(e=fe.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=fe.ellipsis,l=fe.fold,s=fe.defaultFold;e!==o&&(D(e),fe.ellipsis=e,null==fe.onEllipsisChange||fe.onEllipsisChange(e)),p&&(i||!o&&e)&&l!==s&&Ee(void 0,s)},[Ee,_,p]),xe=e.useCallback(function(){var e=de.current,t=ce.current;if(e&&t){fe.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||J(!0))}if(ee===n||(te(n),n))if(d)oe!==d&&le(d),we(fe.contentOffsetHeight>=(d+1)*n-1);else if(fe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);oe!==o&&le(o),we(!0)}else we(!1)}},[d,ee,we]);t.useCompatibleEffect(function(){we(fe.ellipsis,{forceResetFold:!0}),xe()},[xe,we]),e.useEffect(function(){if(V&&he.current){var e=he.current.offsetWidth;e!==fe.foldBtnWidth&&(fe.foldBtnWidth=e,Z(e))}},[V,F,E]),e.useEffect(function(){t.isSafari&&Ce()},[Q,Ce]);var Fe=e.useCallback(function(){var e,t=(null==(e=de.current)?void 0:e.textContent)||"";t!==fe.textContent&&(fe.textContent=t,re(t))},[]),ye=e.useMemo(function(){return V&&Q?"function"==typeof m?m(ae):m||ae:void 0},[m,V,Q,ae]);return e.useEffect(function(){fe.inited&&(null==P||P({ellipsis:V,fold:Q,title:ye}))},[P,Q,V,ye]),e.useEffect(function(){fe.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(s("container"),r),style:ve,ref:function(e){t.assignRef(ce,e),a&&t.assignRef(a,e)},onMouseEnter:B,onMouseLeave:L,onPointerEnter:T,onPointerLeave:q,onClick:j,onFocus:A},/*#__PURE__*/o.default.createElement(n.Measure,{offset:!0},function(e){var n=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-fe.contentOffsetHeight)>1&&xe(),/*#__PURE__*/o.default.createElement("div",{style:pe,className:"content-shadow-dom",ref:function(e){t.assignRef(n,e),t.assignRef(de,e),Fe()}},_)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:g?ye:void 0,style:ge,ref:ue},V&&E&&be,_))});exports.TextEllipsis=a,exports.c=s;
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;
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 \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\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 * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\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 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 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,\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 };\n }, [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 if (!ellipsis || !lines || !innerLineHeight) {\n return commonWrapStyle;\n }\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, 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 // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\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 let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\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 if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\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 // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, 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, offsetHeight} = 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 // 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","_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$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","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","commonWrapStyle","wrapStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","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 {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,2 +1,2 @@
1
- import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as s,useSyncPropsState as d,useRuntime as a,classNames as f,useCompatibleEffect as c,isSafari as h,assignRef as u}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";function g(){return g=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},g.apply(null,arguments)}const m=s("ohkit-text-ellipsis__"),v=t((t,s)=>{const{className:v,lineHeight:C="",lines:w,maskBgColor:x="#fff",content:F,children:E,resetFoldWhenChildrenOrEllipsisChange:b=!1,showTitleWhenFold:y,titleWhenFold:H,showFoldControl:$=!0,foldText:k="收起",unfoldText:O="展开",uiType:W="right",controlPlacement:M="center",whiteSpace:B,width:L,renderFoldButton:N,onEllipsisChange:P,onFoldChange:S,onStatusChange:T,onMouseEnter:R,onMouseLeave:j,onPointerEnter:q,onPointerLeave:A,onClick:_,onFocus:z}=t,V=F||E,[D,G]=n(!1),[I,J]=n(!1),[K,Q]=d(t,"fold",{defaultValue:!0,onChange:S}),[U,X]=n(1),[Y,Z]=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),[ee=0,te]=n(w),[ne,oe]=n(""),[ie]=a({inited:!1,contentOffsetHeight:0,ellipsis:D,defaultFold:K,fold:K,foldBtnWidth:U,textContent:ne,onEllipsisChange:P,onFoldChange:S},["onEllipsisChange","fold","onFoldChange"]),le=o(null),re=o(null),se=o(null),de=o(null),ae=i(()=>({lineHeight:I?"1.4":C||void 0}),[C,I]),fe=i(()=>({whiteSpace:B,width:L}),[B,L]),ce=i(()=>D&&ee&&Y?g({},fe,{minHeight:K?(ee-.2)*Y+"px":void 0,WebkitLineClamp:K?ee:void 0,paddingBottom:"bottom"!==W&&K?void 0:`${Y}px`}):fe,[ee,Y,D,K,W,fe]),he=i(()=>{if(!K)return;const e=Y,t="right"===W?Math.min(e/U*100,80):60;return{boxSizing:"content-box",height:`${Y}px`,lineHeight:`${Y}px`,paddingTop:"bottom"===W?`${e}px`:void 0,paddingLeft:"right"===W?`${e}px`:void 0,background:`linear-gradient(to ${W}, ${x}${4===x.length?"0":"00"}, ${x} ${t}%, ${x} 100%)`}},[Y,x,K,W,U]),ue=l(()=>{le.current&&(le.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{le.current&&(le.current.style.width="100%")}))},[]),pe=l((e,t=!ie.fold)=>{ie.fold=t,Q(t)},[]),ge=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${W}`,"bottom"===W&&`placement-${M}`),style:he,ref:de,onClick:pe},N?N(K):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},K?O:k)),[he,K,k,pe,N,M,W,O]),me=l((e=ie.ellipsis,{forceResetFold:t=!1}={})=>{const{ellipsis:n,fold:o,defaultFold:i}=ie;e!==n&&(G(e),ie.ellipsis=e,null==ie.onEllipsisChange||ie.onEllipsisChange(e)),b&&(t||!n&&e)&&o!==i&&pe(void 0,i)},[pe,V,b]),ve=l(()=>{const e=re.current,t=se.current;if(!e||!t)return;ie.contentOffsetHeight=e.offsetHeight;let n=0;if(!n&&e){const t=null==window.getComputedStyle?void 0:window.getComputedStyle(e),{lineHeight:o}=t||{};o&&(n=parseFloat(o),n||J(!0))}if(Y===n||(Z(n),n))if(w)ee!==w&&te(w),me(ie.contentOffsetHeight>=(w+1)*n-1);else if(ie.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);ee!==e&&te(e),me(!0)}else me(!1)},[w,Y,me]);c(()=>{me(ie.ellipsis,{forceResetFold:!0}),ve()},[ve,me]),r(()=>{if(D&&de.current){const{offsetWidth:e}=de.current;e!==ie.foldBtnWidth&&(ie.foldBtnWidth=e,X(e))}},[D,O,$]),r(()=>{h&&ue()},[K,ue]);const Ce=l(()=>{var e;const t=(null==(e=re.current)?void 0:e.textContent)||"";t!==ie.textContent&&(ie.textContent=t,oe(t))},[]),we=i(()=>D&&K?"function"==typeof H?H(ne):H||ne:void 0,[H,D,K,ne]);return r(()=>{ie.inited&&(null==T||T({ellipsis:D,fold:K,title:we}))},[T,K,D,we]),r(()=>{ie.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:f(m("container"),v),style:ae,ref:e=>{u(se,e),s&&u(s,e)},onMouseEnter:R,onMouseLeave:j,onPointerEnter:q,onPointerLeave:A,onClick:_,onFocus:z},/*#__PURE__*/e.createElement(p,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-ie.contentOffsetHeight)>1&&ve(),/*#__PURE__*/e.createElement("div",{style:fe,className:"content-shadow-dom",ref:e=>{u(t,e),u(re,e),Ce()}},V)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:y?we:void 0,style:ce,ref:le},D&&$&&ge,V))});export{v as TextEllipsis,m as c};
1
+ import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as s,classNames as a}from"@ohkit/prefix-classname";import{useSyncPropsState as d,useRuntime as c,useCompatibleEffect as 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};
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 \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\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 * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\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 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 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,\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 };\n }, [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 if (!ellipsis || !lines || !innerLineHeight) {\n return commonWrapStyle;\n }\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, 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 // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\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 let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\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 if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\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 // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, 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, offsetHeight} = 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 // 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","lineHeight","lines","maskBgColor","content","children","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","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","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","commonWrapStyle","wrapStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","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 {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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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/utils"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/utils","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.utils,e.measure)}(this,function(e,t,n,i){function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var l=/*#__PURE__*/o(t);function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},s.apply(null,arguments)}var a=n.prefixClassname("ohkit-text-ellipsis__"),f=t.forwardRef(function(e,o){var f=e.className,r=e.lineHeight,u=void 0===r?"":r,d=e.lines,c=e.maskBgColor,h=void 0===c?"#fff":c,p=e.resetFoldWhenChildrenOrEllipsisChange,v=void 0!==p&&p,g=e.showTitleWhenFold,m=e.titleWhenFold,C=e.showFoldControl,b=void 0===C||C,x=e.foldText,E=void 0===x?"收起":x,w=e.unfoldText,y=void 0===w?"展开":w,F=e.uiType,k=void 0===F?"right":F,S=e.controlPlacement,M=void 0===S?"center":S,R=e.whiteSpace,H=e.width,O=e.renderFoldButton,W=e.onEllipsisChange,N=e.onFoldChange,T=e.onStatusChange,P=e.onMouseEnter,B=e.onMouseLeave,L=e.onPointerEnter,q=e.onPointerLeave,j=e.onClick,A=e.onFocus,_=e.content||e.children,z=t.useState(!1),V=z[0],D=z[1],G=t.useState(!1),I=G[0],J=G[1],K=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:N}),Q=K[0],U=K[1],X=t.useState(1),Y=X[0],Z=X[1],$=t.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),ee=$[0],te=$[1],ne=t.useState(d),ie=ne[0],oe=void 0===ie?0:ie,le=ne[1],se=t.useState(""),ae=se[0],fe=se[1],re=n.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:V,defaultFold:Q,fold:Q,foldBtnWidth:Y,textContent:ae,onEllipsisChange:W,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"])[0],ue=t.useRef(null),de=t.useRef(null),ce=t.useRef(null),he=t.useRef(null),pe=t.useMemo(function(){return{lineHeight:I?"1.4":u||void 0}},[u,I]),ve=t.useMemo(function(){return{whiteSpace:R,width:H}},[R,H]),ge=t.useMemo(function(){return V&&oe&&ee?s({},ve,{minHeight:Q?(oe-.2)*ee+"px":void 0,WebkitLineClamp:Q?oe:void 0,paddingBottom:"bottom"!==k&&Q?void 0:ee+"px"}):ve},[oe,ee,V,Q,k,ve]),me=t.useMemo(function(){if(Q){var e=ee,t="right"===k?Math.min(e/Y*100,80):60;return{boxSizing:"content-box",height:ee+"px",lineHeight:ee+"px",paddingTop:"bottom"===k?e+"px":void 0,paddingLeft:"right"===k?e+"px":void 0,background:"linear-gradient(to "+k+", "+h+(4===h.length?"0":"00")+", "+h+" "+t+"%, "+h+" 100%)"}}},[ee,h,Q,k,Y]),Ce=t.useCallback(function(){ue.current&&(ue.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ue.current&&(ue.current.style.width="100%")}))},[]),be=t.useCallback(function(e,t){void 0===t&&(t=!re.fold),re.fold=t,U(t)},[]),xe=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+k,"bottom"===k&&"placement-"+M),style:me,ref:he,onClick:be},O?O(Q):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},Q?y:E))},[me,Q,E,be,O,M,k,y]),Ee=t.useCallback(function(e,t){void 0===e&&(e=re.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=re.ellipsis,l=re.fold,s=re.defaultFold;e!==o&&(D(e),re.ellipsis=e,null==re.onEllipsisChange||re.onEllipsisChange(e)),v&&(i||!o&&e)&&l!==s&&be(void 0,s)},[be,_,v]),we=t.useCallback(function(){var e=de.current,t=ce.current;if(e&&t){re.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||J(!0))}if(ee===n||(te(n),n))if(d)oe!==d&&le(d),Ee(re.contentOffsetHeight>=(d+1)*n-1);else if(re.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);oe!==o&&le(o),Ee(!0)}else Ee(!1)}},[d,ee,Ee]);n.useCompatibleEffect(function(){Ee(re.ellipsis,{forceResetFold:!0}),we()},[we,Ee]),t.useEffect(function(){if(V&&he.current){var e=he.current.offsetWidth;e!==re.foldBtnWidth&&(re.foldBtnWidth=e,Z(e))}},[V,y,b]),t.useEffect(function(){n.isSafari&&Ce()},[Q,Ce]);var ye=t.useCallback(function(){var e,t=(null==(e=de.current)?void 0:e.textContent)||"";t!==re.textContent&&(re.textContent=t,fe(t))},[]),Fe=t.useMemo(function(){return V&&Q?"function"==typeof m?m(ae):m||ae:void 0},[m,V,Q,ae]);return t.useEffect(function(){re.inited&&(null==T||T({ellipsis:V,fold:Q,title:Fe}))},[T,Q,V,Fe]),t.useEffect(function(){re.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(a("container"),f),style:pe,ref:function(e){n.assignRef(ce,e),o&&n.assignRef(o,e)},onMouseEnter:P,onMouseLeave:B,onPointerEnter:L,onPointerLeave:q,onClick:j,onFocus:A},/*#__PURE__*/l.default.createElement(i.Measure,{offset:!0},function(e){var t=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-re.contentOffsetHeight)>1&&we(),/*#__PURE__*/l.default.createElement("div",{style:ve,className:"content-shadow-dom",ref:function(e){n.assignRef(t,e),n.assignRef(de,e),ye()}},_)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:g?Fe:void 0,style:ge,ref:ue},V&&b&&xe,_))});e.TextEllipsis=f,e.c=a});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/prefix-classname"),require("@ohkit/react-helper"),require("@ohkit/platform"),require("@ohkit/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});
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 \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\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 * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\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 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 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,\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 };\n }, [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 if (!ellipsis || !lines || !innerLineHeight) {\n return commonWrapStyle;\n }\n return {\n ...commonWrapStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, 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 // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\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 let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\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 if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\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 // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, 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, offsetHeight} = 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 // 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","_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$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","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","commonWrapStyle","wrapStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","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 {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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"}
@@ -4,24 +4,40 @@
4
4
  * @author <wuqiuyang305@126.com>
5
5
  */
6
6
  import React, { PropsWithChildren } from "react";
7
+ import { classNames as cx } from "@ohkit/prefix-classname";
7
8
  import "./style.scss";
8
- import { classNames as cx } from "@ohkit/utils";
9
9
  export declare const c: (...arg: cx.ArgumentArray) => string;
10
10
  interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMouseEnter" | "onMouseLeave" | "onPointerEnter" | "onPointerLeave" | "onFocus" | "onClick"> {
11
+ /**
12
+ * 自定义样式类名,会附加到根元素上
13
+ */
14
+ className?: string;
15
+ /**
16
+ * 自定义样式
17
+ */
18
+ style?: React.CSSProperties;
11
19
  /**
12
20
  * right | bottom 展开按钮在右下侧还是底部
13
21
  * @default right
14
22
  */
15
23
  uiType?: "right" | "bottom";
16
24
  /**
17
- * 自定义样式类名,会附加到根元素上
25
+ * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)
26
+ * @default line
18
27
  */
19
- className?: string;
28
+ truncateMode?: "line" | "height";
29
+ /**
30
+ * truncateMode === "height" 时生效
31
+ * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度
32
+ * 单位: px
33
+ */
34
+ maxHeight?: number;
20
35
  /**
21
36
  * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高
22
37
  */
23
38
  lineHeight?: React.CSSProperties["lineHeight"];
24
39
  /**
40
+ * truncateMode === "line" 时生效
25
41
  * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度
26
42
  */
27
43
  lines?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohkit/text-ellipsis",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "text ellipsis for react",
5
5
  "keywords": [
6
6
  "text ellipsis"
@@ -32,8 +32,10 @@
32
32
  "clean": "rm -rf dist"
33
33
  },
34
34
  "dependencies": {
35
- "@ohkit/measure": "0.0.3-alpha.0",
36
- "@ohkit/utils": "0.0.3"
35
+ "@ohkit/measure": "^0.0.3",
36
+ "@ohkit/platform": "0.0.3",
37
+ "@ohkit/prefix-classname": "0.0.3",
38
+ "@ohkit/react-helper": "0.0.4"
37
39
  },
38
40
  "devDependencies": {
39
41
  "react": "^18.3.1",
@@ -43,5 +45,5 @@
43
45
  "react": ">=16.8.0",
44
46
  "react-dom": ">=16.8.0"
45
47
  },
46
- "gitHead": "aca7a8ed6a29d525bcf53091149f7a529f42fb40"
48
+ "gitHead": "3fb061e6a6bf7ccd9f26f0339080da0a4df02075"
47
49
  }
package/src/index.tsx CHANGED
@@ -14,17 +14,19 @@ import React, {
14
14
  PropsWithChildren,
15
15
  MouseEvent,
16
16
  } from "react";
17
- import "./style.scss";
18
17
  import {
19
- isSafari,
20
18
  prefixClassname as p,
21
19
  classNames as cx,
20
+ } from "@ohkit/prefix-classname";
21
+ import {
22
22
  assignRef,
23
23
  useRuntime,
24
24
  useCompatibleEffect,
25
25
  useSyncPropsState,
26
- } from "@ohkit/utils";
27
- import { Measure } from "@ohkit/measure";
26
+ } from "@ohkit/react-helper";
27
+ import {isSafari} from "@ohkit/platform";
28
+ import {Measure} from "@ohkit/measure";
29
+ import "./style.scss";
28
30
 
29
31
  export const c = p("ohkit-text-ellipsis__");
30
32
 
@@ -38,20 +40,36 @@ interface ITextEllipsis
38
40
  | "onFocus"
39
41
  | "onClick"
40
42
  > {
43
+ /**
44
+ * 自定义样式类名,会附加到根元素上
45
+ */
46
+ className?: string;
47
+ /**
48
+ * 自定义样式
49
+ */
50
+ style?: React.CSSProperties;
41
51
  /**
42
52
  * right | bottom 展开按钮在右下侧还是底部
43
53
  * @default right
44
54
  */
45
55
  uiType?: "right" | "bottom";
46
56
  /**
47
- * 自定义样式类名,会附加到根元素上
57
+ * 截断模式 (若某些浏览器不支持 webkitLineClamp,可降级切换为 height 模式)
58
+ * @default line
48
59
  */
49
- className?: string;
60
+ truncateMode?: "line" | "height";
61
+ /**
62
+ * truncateMode === "height" 时生效
63
+ * 最大高度(number > 0),没传或者传入无效值不限制,尝试取 lines * lineHeight,若仍无效 自动截断到容器的最大高度
64
+ * 单位: px
65
+ */
66
+ maxHeight?: number;
50
67
  /**
51
68
  * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高
52
69
  */
53
70
  lineHeight?: React.CSSProperties["lineHeight"];
54
71
  /**
72
+ * truncateMode === "line" 时生效
55
73
  * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度
56
74
  */
57
75
  lines?: number;
@@ -140,6 +158,7 @@ export type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;
140
158
  export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {
141
159
  const {
142
160
  className,
161
+ style,
143
162
  lineHeight = "",
144
163
  lines,
145
164
  maskBgColor = "#fff",
@@ -152,6 +171,8 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
152
171
  foldText = "收起",
153
172
  unfoldText = "展开",
154
173
  uiType = "right",
174
+ truncateMode = "line",
175
+ maxHeight,
155
176
  controlPlacement = 'center',
156
177
  whiteSpace,
157
178
  width,
@@ -184,7 +205,8 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
184
205
 
185
206
  const [runtime] = useRuntime({
186
207
  inited: false, // mounted
187
- contentOffsetHeight: 0,
208
+ contentOffsetHeight: 0, // 内容节点offsetHeight
209
+ containerContentHeight: 0, // 容器内容高度 = 容器高 - 上下padding
188
210
  ellipsis,
189
211
  defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold
190
212
  fold,
@@ -204,8 +226,9 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
204
226
  lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)
205
227
  ? "1.4" // more brower normal default lineHeight
206
228
  : lineHeight ? lineHeight : undefined,
229
+ ...style,
207
230
  };
208
- }, [lineHeight, getLineHeightFail]);
231
+ }, [style, lineHeight, getLineHeightFail]);
209
232
 
210
233
  const commonWrapStyle = useMemo(() => {
211
234
  return {
@@ -216,21 +239,24 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
216
239
  // 容器样式
217
240
  const wrapStyle = useMemo(() => {
218
241
  const lines = innerLines;
219
- if (!ellipsis || !lines || !innerLineHeight) {
242
+ const isHeightMode = truncateMode === 'height';
243
+ if (!ellipsis || !isHeightMode && (!lines || !innerLineHeight)) {
220
244
  return commonWrapStyle;
221
245
  }
246
+ const paddingBottom = showFoldControl && (uiType === "bottom" || !fold) ? `${innerLineHeight}px` : undefined;
222
247
  return {
223
248
  ...commonWrapStyle,
224
249
  // HACK: 兼容safari 15+ 富文本折叠高度丢失问题
225
- minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,
226
- WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案
227
- // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案
228
- // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案
229
- // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,
230
- paddingBottom:
231
- uiType === "bottom" || !fold ? `${innerLineHeight}px` : undefined,
250
+ minHeight: !isHeightMode && fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,
251
+ // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案?目前先交给用户去判断,自行选择truncateMode
252
+ // WebkitLineClamp: isSafari ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案
253
+ // maxHeight: isSafari && fold ? lines * innerLineHeight : undefined,
254
+ WebkitLineClamp: isHeightMode ? undefined : fold ? lines : undefined, // 利用-webkit-line-clamp截断方案
255
+ maxHeight: isHeightMode && fold ? (maxHeight || lines * innerLineHeight || runtime.containerContentHeight || 0) : undefined,
256
+ paddingBottom,
257
+ boxSizing: paddingBottom ? 'border-box' as const : undefined,
232
258
  };
233
- }, [innerLines, innerLineHeight, ellipsis, fold, uiType, commonWrapStyle]);
259
+ }, [innerLines, innerLineHeight, ellipsis, fold, showFoldControl, uiType, truncateMode, maxHeight, commonWrapStyle]);
234
260
 
235
261
  // 展开|收起 按钮样式
236
262
  const btnStyle = useMemo(() => {
@@ -257,12 +283,15 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
257
283
  }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);
258
284
 
259
285
  const reorganizeDom = useCallback(() => {
260
- // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发
286
+ // Note: safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发
261
287
  if (contentRef.current) {
262
- contentRef.current.style.width = "99.999%";
288
+ const orginStyleWidth = contentRef.current.style.width;
289
+ const orginWidth = window.getComputedStyle(contentRef.current).width;
290
+ // console.log('orginWidth, orginStyleWidth:', orginWidth, orginStyleWidth);
291
+ contentRef.current.style.width = `${parseFloat(orginWidth) - 0.1}px`;
263
292
  window.requestAnimationFrame?.(() => {
264
293
  if (contentRef.current) {
265
- contentRef.current.style.width = "100%";
294
+ contentRef.current.style.width = orginStyleWidth;
266
295
  }
267
296
  });
268
297
  }
@@ -331,12 +360,16 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
331
360
  return;
332
361
  }
333
362
  runtime.contentOffsetHeight = wrapDom.offsetHeight;
334
- let realLineHeight = 0;
363
+ const containerStyle = window.getComputedStyle(containerDom);
364
+ const paddingTop = parseFloat(containerStyle.paddingTop);
365
+ const paddingBottom = parseFloat(containerStyle.paddingBottom);
366
+ const containerContentHeight = runtime.containerContentHeight = containerDom.clientHeight - paddingTop - paddingBottom;
335
367
 
368
+ // 计算真实行高
369
+ let realLineHeight = 0;
336
370
  // 若外部未传入, 尝试读取当前文本的行高。
337
371
  if (!realLineHeight && wrapDom) {
338
- const realStyle = window.getComputedStyle?.(wrapDom);
339
- const { lineHeight } = realStyle || {};
372
+ const {lineHeight} = containerStyle || {};
340
373
  if (lineHeight) {
341
374
  // 未设置行高的为 normal
342
375
  realLineHeight = parseFloat(lineHeight);
@@ -352,9 +385,18 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
352
385
  return;
353
386
  }
354
387
  }
388
+
389
+ const isHeightMode = truncateMode === 'height';
390
+ // 高度截断模式,比较简单 直接判断是否超出容器高度
391
+ if (isHeightMode) {
392
+ resetState(runtime.contentOffsetHeight > (maxHeight || containerContentHeight));
393
+ return;
394
+ }
395
+
396
+ // 行数截断模式,需要利用行高计算是否超出容器高度
355
397
  if (!lines) {
356
- if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {
357
- const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);
398
+ if (runtime.contentOffsetHeight > containerContentHeight) {
399
+ const adjustLines = Math.floor(containerContentHeight / realLineHeight) || 1;
358
400
  if (innerLines !== adjustLines) {
359
401
  setInnerLines(adjustLines);
360
402
  }
@@ -366,14 +408,11 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
366
408
  if (innerLines !== lines) {
367
409
  setInnerLines(lines);
368
410
  }
369
- // 允许误差1px(行高为小数时)
370
- if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {
371
- resetState(true);
372
- } else {
373
- resetState(false);
374
- }
411
+ // console.log('contentOffsetHeight, realLineHeight', runtime.contentOffsetHeight, realLineHeight);
412
+ // 允许误差1px(行高为小数时, safari计算行高*行数和实践总高有差异,故将行高向下取整兼容)
413
+ resetState(runtime.contentOffsetHeight >= (lines + 1) * Math.floor(realLineHeight) - 1);
375
414
  }
376
- }, [lines, innerLineHeight, resetState]);
415
+ }, [lines, innerLineHeight, truncateMode, maxHeight, resetState]);
377
416
 
378
417
  // 监听内容高度,是否需要折叠
379
418
  // 用useLayoutEffect方式闪屏显示
@@ -387,7 +426,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
387
426
  // 监听"展开"按钮宽度变化
388
427
  useEffect(() => {
389
428
  if (ellipsis && btnWrapperRef.current) {
390
- const {offsetWidth, offsetHeight} = btnWrapperRef.current;
429
+ const {offsetWidth} = btnWrapperRef.current;
391
430
  if (offsetWidth !== runtime.foldBtnWidth) {
392
431
  runtime.foldBtnWidth = offsetWidth;
393
432
  setFoldBtnWidth(offsetWidth);
@@ -425,6 +464,10 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
425
464
  useEffect(() => {
426
465
  runtime.inited = true;
427
466
  }, []);
467
+ // 高度自适应,容器高度变化时重新计算高度(容器也需要包装Measure,TODO: 待开发 hooks -> useMeasure, 使得观测dom尺寸的方式更简洁)
468
+ // if (!maxHeight && !lines) {
469
+ // calcEllipsis();
470
+ // }
428
471
  // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);
429
472
  return (
430
473
  <div
package/src/style.scss CHANGED
@@ -12,6 +12,8 @@ $prefix: global.$comp-prefix;
12
12
  .#{$prefix}-text-ellipsis__ {
13
13
  &container {
14
14
  display: flex; // INFO: 这里必须非block, safari 13.1.3版本有bug 折叠切换时节点高度未更新
15
+ align-items: flex-start; // 配合flex食用,否则内部 display: -webkit-box 截断不生效
16
+ overflow: hidden; // 避免 .content-shadow-dom 溢出父容器导致外部滚动条视觉展示异常
15
17
  position: relative;
16
18
 
17
19
  // 保留dom用于计算高度, 不影响视图