@ohkit/text-ellipsis 0.0.4 → 0.0.6
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 +7 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +1 -1
- package/dist/index.modern.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/package.json +2 -2
- package/src/index.tsx +17 -5
- package/src/style.scss +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ function Demo() {
|
|
|
41
41
|
| className | 自定义样式类名,会附加到根元素上 | string | - |
|
|
42
42
|
| lines | 超过几行折叠 (number > 0) | number | - |
|
|
43
43
|
| uiType | 展开按钮位置 `right`(右下侧) 或 `bottom`(底部) | string | `right` |
|
|
44
|
-
| lineHeight | 行高(
|
|
44
|
+
| lineHeight | 行高(支持px/em等单位或数字) | number/string | - |
|
|
45
45
|
| content | 文本内容 | ReactNode | - |
|
|
46
46
|
| fold | 是否折叠 | boolean | `true` |
|
|
47
47
|
| showFoldControl | 是否显示展开控制按钮 | boolean | `true` |
|
|
@@ -49,6 +49,11 @@ function Demo() {
|
|
|
49
49
|
| unfoldText | 展开状态按钮文字 | string | `展开` |
|
|
50
50
|
| maskBgColor | 展开按钮蒙层背景色(16进制) | string | `#fff` |
|
|
51
51
|
| showTitleWhenFold | 折叠状态下是否显示title属性 | boolean | `false` |
|
|
52
|
+
| titleWhenFold | 自定义折叠状态下的title内容 | string \| (title: string) => string | - |
|
|
53
|
+
| resetFoldWhenChildrenOrEllipsisChange | 当内容变化时重置折叠状态 | boolean | `false` |
|
|
54
|
+
| controlPlacement | 底部按钮对齐方式 `left`/`center`/`right` | string | `center` |
|
|
55
|
+
| whiteSpace | 是否保留换行符 | CSSProperties['whiteSpace'] | - |
|
|
56
|
+
| width | 容器宽度(默认自适应) | CSSProperties['width'] | - |
|
|
52
57
|
| renderFoldButton | 自定义渲染展开按钮 | (fold: boolean) => ReactNode | - |
|
|
53
58
|
|
|
54
59
|
### 事件
|
|
@@ -83,3 +88,4 @@ function Demo() {
|
|
|
83
88
|
1. Safari浏览器下富文本截断可能有轻微高度计算偏差
|
|
84
89
|
2. 图片和表格等复杂内容可能无法完美截断
|
|
85
90
|
3. 动态内容变化后会自动重新计算截断状态
|
|
91
|
+
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 .
|
|
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}
|
|
2
2
|
/*# sourceMappingURL=index.css.map */
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,gCACE,YAAa,CACb,iBACF,CACA,
|
|
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}"]}
|
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
|
|
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};
|
|
2
2
|
//# sourceMappingURL=index.es.js.map
|
package/dist/index.es.js.map
CHANGED
|
@@ -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 */\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 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 // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\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, whiteSpace]);\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={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} 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","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","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_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 \"./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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"}
|
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),l=t.prefixClassname("ohkit-text-ellipsis__"),
|
|
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;
|
|
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 */\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 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 // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\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, whiteSpace]);\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={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} 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","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","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","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 \"./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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"}
|
package/dist/index.modern.mjs
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
|
|
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};
|
|
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 */\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 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 // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\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, whiteSpace]);\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={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} 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","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","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","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 \"./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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"}
|
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),s=n.prefixClassname("ohkit-text-ellipsis__"),f=t.forwardRef(function(e,o){var f=e.className,
|
|
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});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -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 */\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 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 // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\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, whiteSpace]);\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={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} 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","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","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","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 \"./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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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -77,6 +77,11 @@ interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMou
|
|
|
77
77
|
* 是否保留换行
|
|
78
78
|
*/
|
|
79
79
|
whiteSpace?: React.CSSProperties['whiteSpace'];
|
|
80
|
+
/**
|
|
81
|
+
* 容器宽度(默认自适应内容)
|
|
82
|
+
* 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定
|
|
83
|
+
*/
|
|
84
|
+
width?: React.CSSProperties['width'];
|
|
80
85
|
/**
|
|
81
86
|
* 自定义渲染展开按钮
|
|
82
87
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohkit/text-ellipsis",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "text ellipsis for react",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"text ellipsis"
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"react": ">=16.8.0",
|
|
44
44
|
"react-dom": ">=16.8.0"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "aca7a8ed6a29d525bcf53091149f7a529f42fb40"
|
|
47
47
|
}
|
package/src/index.tsx
CHANGED
|
@@ -107,6 +107,11 @@ interface ITextEllipsis
|
|
|
107
107
|
* 是否保留换行
|
|
108
108
|
*/
|
|
109
109
|
whiteSpace?: React.CSSProperties['whiteSpace'];
|
|
110
|
+
/**
|
|
111
|
+
* 容器宽度(默认自适应内容)
|
|
112
|
+
* 应用场景:whiteSpace='pre*' 时,支持展示换行符,自适应内容可能导致控制按钮位置不确定
|
|
113
|
+
*/
|
|
114
|
+
width?: React.CSSProperties['width'];
|
|
110
115
|
/**
|
|
111
116
|
* 自定义渲染展开按钮
|
|
112
117
|
*/
|
|
@@ -149,6 +154,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
|
|
|
149
154
|
uiType = "right",
|
|
150
155
|
controlPlacement = 'center',
|
|
151
156
|
whiteSpace,
|
|
157
|
+
width,
|
|
152
158
|
renderFoldButton,
|
|
153
159
|
onEllipsisChange,
|
|
154
160
|
onFoldChange,
|
|
@@ -201,14 +207,20 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
|
|
|
201
207
|
};
|
|
202
208
|
}, [lineHeight, getLineHeightFail]);
|
|
203
209
|
|
|
210
|
+
const commonWrapStyle = useMemo(() => {
|
|
211
|
+
return {
|
|
212
|
+
whiteSpace,
|
|
213
|
+
width,
|
|
214
|
+
};
|
|
215
|
+
}, [whiteSpace, width])
|
|
204
216
|
// 容器样式
|
|
205
217
|
const wrapStyle = useMemo(() => {
|
|
206
218
|
const lines = innerLines;
|
|
207
219
|
if (!ellipsis || !lines || !innerLineHeight) {
|
|
208
|
-
return;
|
|
220
|
+
return commonWrapStyle;
|
|
209
221
|
}
|
|
210
222
|
return {
|
|
211
|
-
|
|
223
|
+
...commonWrapStyle,
|
|
212
224
|
// HACK: 兼容safari 15+ 富文本折叠高度丢失问题
|
|
213
225
|
minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,
|
|
214
226
|
WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案
|
|
@@ -218,7 +230,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
|
|
|
218
230
|
paddingBottom:
|
|
219
231
|
uiType === "bottom" || !fold ? `${innerLineHeight}px` : undefined,
|
|
220
232
|
};
|
|
221
|
-
}, [innerLines, innerLineHeight, ellipsis, fold, uiType,
|
|
233
|
+
}, [innerLines, innerLineHeight, ellipsis, fold, uiType, commonWrapStyle]);
|
|
222
234
|
|
|
223
235
|
// 展开|收起 按钮样式
|
|
224
236
|
const btnStyle = useMemo(() => {
|
|
@@ -437,7 +449,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
|
|
|
437
449
|
if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {
|
|
438
450
|
calcEllipsis();
|
|
439
451
|
}
|
|
440
|
-
return <div style={
|
|
452
|
+
return <div style={commonWrapStyle} className={"content-shadow-dom"} ref={(r) => {
|
|
441
453
|
assignRef(measureRef, r);
|
|
442
454
|
assignRef(wrapperRef, r);
|
|
443
455
|
updateTextContent();
|
|
@@ -446,7 +458,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
|
|
|
446
458
|
</div>
|
|
447
459
|
}}
|
|
448
460
|
</Measure>
|
|
449
|
-
{/* <div className={"
|
|
461
|
+
{/* <div className={"content-shadow-dom"} ref={wrapperRef}>
|
|
450
462
|
{finalContent}
|
|
451
463
|
</div> */}
|
|
452
464
|
{/* 主文本显示 */}
|