@ohkit/text-ellipsis 0.0.1-beta.1 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +32 -26
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.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/package.json +6 -5
- package/src/Readme.mdx +33 -0
- package/src/__test__/data.tsx +42 -0
- package/src/index.stories.tsx +149 -0
- package/src/index.tsx +432 -0
- package/src/style.scss +77 -0
package/dist/index.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.ohkit-text-ellipsis__container{display:flex;position:relative}.ohkit-text-ellipsis__container .offset-height-computer{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 .btn-fold{display:inline-block}
|
|
1
|
+
.ohkit-text-ellipsis__container{display:flex;position:relative}.ohkit-text-ellipsis__container .offset-height-computer{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,wDAKE,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,iFACE,oBACF","file":"index.css","sourcesContent":[".ohkit-text-ellipsis__container {\n display: flex;\n position: relative;\n}\n.ohkit-text-ellipsis__container .offset-height-computer {\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 .btn-fold {\n display: inline-block;\n}"]}
|
|
1
|
+
{"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,gCACE,YAAa,CACb,iBACF,CACA,wDAKE,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 .offset-height-computer {\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.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import React, { PropsWithChildren } from "react";
|
|
|
7
7
|
import "./style.scss";
|
|
8
8
|
import { classNames as cx } from "@ohkit/utils";
|
|
9
9
|
export declare const c: (...arg: cx.ArgumentArray) => string;
|
|
10
|
-
interface ITextEllipsis {
|
|
10
|
+
interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMouseEnter" | "onMouseLeave" | "onPointerEnter" | "onPointerLeave" | "onFocus" | "onClick"> {
|
|
11
11
|
/**
|
|
12
12
|
* right | bottom 展开按钮在右下侧还是底部
|
|
13
13
|
* @default right
|
|
@@ -31,11 +31,21 @@ interface ITextEllipsis {
|
|
|
31
31
|
* text|ReactNode 与children任传一个
|
|
32
32
|
*/
|
|
33
33
|
content?: React.ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* 折叠状态
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
fold?: boolean;
|
|
34
39
|
/**
|
|
35
40
|
* 显示展开控制按钮
|
|
36
41
|
* @default true
|
|
37
42
|
*/
|
|
38
43
|
showFoldControl?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 展开按钮位置 uiType='bottom'时有效
|
|
46
|
+
* @default center
|
|
47
|
+
*/
|
|
48
|
+
controlPlacement?: 'left' | 'center' | 'right';
|
|
39
49
|
/**
|
|
40
50
|
* 展开按钮文字
|
|
41
51
|
* @default 收起
|
|
@@ -46,6 +56,15 @@ interface ITextEllipsis {
|
|
|
46
56
|
* @default 展开
|
|
47
57
|
*/
|
|
48
58
|
unfoldText?: string;
|
|
59
|
+
/**
|
|
60
|
+
* 折叠状态下是否显示title属性
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
showTitleWhenFold?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* 折叠状态自定义title属性内容
|
|
66
|
+
*/
|
|
67
|
+
titleWhenFold?: string | ((title: string) => string);
|
|
49
68
|
/**
|
|
50
69
|
* 自定义渲染展开按钮
|
|
51
70
|
*/
|
|
@@ -58,31 +77,18 @@ interface ITextEllipsis {
|
|
|
58
77
|
* @param ellipsis 是否截断,true 截断,false 未截断
|
|
59
78
|
*/
|
|
60
79
|
onEllipsisChange?: (ellipsis: boolean) => void;
|
|
80
|
+
/**
|
|
81
|
+
* 关键状态变更触发
|
|
82
|
+
* @param status
|
|
83
|
+
*/
|
|
84
|
+
onStatusChange?: (status: {
|
|
85
|
+
fold: boolean;
|
|
86
|
+
ellipsis: boolean;
|
|
87
|
+
title?: string;
|
|
88
|
+
}) => void;
|
|
61
89
|
}
|
|
62
90
|
export type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;
|
|
63
|
-
export declare
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*/
|
|
67
|
-
lineHeight,
|
|
68
|
-
/**
|
|
69
|
-
* 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度
|
|
70
|
-
*/
|
|
71
|
-
lines,
|
|
72
|
-
/**
|
|
73
|
-
* 展开按钮蒙层背景色(仅支持16进制表示)
|
|
74
|
-
*/
|
|
75
|
-
maskBgColor,
|
|
76
|
-
/**
|
|
77
|
-
* text|ReactNode 与children任传一个
|
|
78
|
-
*/
|
|
79
|
-
content, children,
|
|
80
|
-
/**
|
|
81
|
-
* 显示展开控制按钮
|
|
82
|
-
*/
|
|
83
|
-
showFoldControl, foldText, unfoldText,
|
|
84
|
-
/**
|
|
85
|
-
* right | bottom 展开按钮在右下侧还是底部
|
|
86
|
-
*/
|
|
87
|
-
uiType, renderFoldButton, onEllipsisChange, onFoldChange, }: TextEllipsisProps): React.JSX.Element;
|
|
91
|
+
export declare const TextEllipsis: React.ForwardRefExoticComponent<ITextEllipsis & {
|
|
92
|
+
children?: React.ReactNode;
|
|
93
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
88
94
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function
|
|
1
|
+
var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i=/*#__PURE__*/o(e),l=t.prefixClassname("ohkit-text-ellipsis__"),s=e.forwardRef(function(o,s){var a=o.className,r=o.lineHeight,f=void 0===r?"":r,u=o.lines,d=o.maskBgColor,c=void 0===d?"#fff":d,h=o.content,p=o.children,g=o.showTitleWhenFold,v=o.titleWhenFold,m=o.showFoldControl,C=void 0===m||m,E=o.foldText,x=void 0===E?"收起":E,b=o.unfoldText,w=void 0===b?"展开":b,k=o.uiType,F=void 0===k?"right":k,H=o.controlPlacement,M=void 0===H?"center":H,y=o.renderFoldButton,R=o.onEllipsisChange,S=o.onFoldChange,N=o.onStatusChange,W=o.onMouseEnter,T=o.onMouseLeave,B=o.onPointerEnter,L=o.onPointerLeave,P=o.onClick,q=o.onFocus,O=e.useState(!1),A=O[0],_=O[1],j=e.useState(!1),V=j[0],z=j[1],D=t.useSyncPropsState(o,"fold",{defaultValue:!0,onChange:S}),G=D[0],I=D[1],J=e.useState(1),K=J[0],Q=J[1],U=e.useState("string"==typeof f&&f.endsWith("px")?parseFloat(f):0),X=U[0],Y=U[1],Z=e.useState(u),$=Z[0],ee=void 0===$?0:$,te=Z[1],ne=e.useState(""),oe=ne[0],ie=ne[1],le=t.useRuntime({contentOffsetHeight:0,ellipsis:A,fold:G,foldBtnWidth:K,textContent:oe,onEllipsisChange:R,onFoldChange:S},["onEllipsisChange","fold","onFoldChange"])[0],se=e.useRef(null),ae=e.useRef(null),re=e.useRef(null),fe=e.useRef(null),ue=e.useMemo(function(){return{lineHeight:V?"1.4":f||void 0}},[f,V]),de=e.useMemo(function(){if(A&&ee&&X)return{minHeight:G?(ee-.5)*X+"px":void 0,WebkitLineClamp:G?ee:void 0,paddingBottom:"bottom"!==F&&G?void 0:X+"px"}},[ee,X,A,G,F]),ce=e.useMemo(function(){if(G){var e=X,t="right"===F?Math.min(e/K*100,80):60;return{height:X+"px",lineHeight:X+"px",paddingTop:"bottom"===F?e+"px":void 0,paddingLeft:"right"===F?e+"px":void 0,background:"linear-gradient(to "+F+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[X,c,G,F,K]),he=e.useCallback(function(){se.current&&(se.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){se.current&&(se.current.style.width="100%")}))},[]),pe=e.useCallback(function(e,t){void 0===t&&(t=!le.fold),le.fold=t,I(t)},[]),ge=e.useMemo(function(){/*#__PURE__*/return i.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+F,"bottom"===F&&"placement-"+M),style:ce,ref:fe,onClick:pe},y?y(G):/*#__PURE__*/i.default.createElement("div",{className:"btn-fold"},G?w:x))},[ce,G,x,pe,y,M,F,w]),ve=e.useCallback(function(e){void 0===e&&(e=le.ellipsis);var t=le.fold;e!==le.ellipsis&&(_(e),le.ellipsis=e,null==le.onEllipsisChange||le.onEllipsisChange(e),e&&!t&&pe(void 0,!0))},[pe]),me=e.useCallback(function(){var e=ae.current,t=re.current;if(e&&t){le.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||z(!0))}if(X!==n&&Y(n),u)ee!==u&&te(u),ve(le.contentOffsetHeight>=(u+1)*n);else if(le.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);ee!==i&&te(i),ve(!0)}else ve(!1)}},[u,X,ve]);t.useCompatibleEffect(function(){ve(),me()},[me,ve]),e.useEffect(function(){if(A&&fe.current){var e=fe.current.offsetWidth;e!==le.foldBtnWidth&&(le.foldBtnWidth=e,Q(e))}},[A,w,C]),e.useEffect(function(){t.isSafari&&he()},[G,he]);var Ce=e.useCallback(function(){var e,t=(null==(e=ae.current)?void 0:e.textContent)||"";t!==le.textContent&&(le.textContent=t,ie(t))},[]),Ee=h||p,xe=e.useMemo(function(){return A&&G?"function"==typeof v?v(oe):v||oe:void 0},[v,A,G,oe]);return e.useEffect(function(){null==N||N({ellipsis:A,fold:G,title:xe})},[N,G,A,xe]),console.log("[render TextEllipsis]: ellipsis fold: ",A,G),/*#__PURE__*/i.default.createElement("div",{className:t.classNames(l("container"),a),style:ue,ref:function(e){t.assignRef(re,e),s&&t.assignRef(s,e)},onMouseEnter:W,onMouseLeave:T,onPointerEnter:B,onPointerLeave:L,onClick:P,onFocus:q},/*#__PURE__*/i.default.createElement(n.Measure,{offset:!0},function(e){var n=e.measureRef,o=(e.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-le.contentOffsetHeight)>1&&(console.log("calcEllipsis"),me()),/*#__PURE__*/i.default.createElement("div",{className:"offset-height-computer",ref:function(e){t.assignRef(n,e),t.assignRef(ae,e),Ce()}},Ee)}),/*#__PURE__*/i.default.createElement("div",{className:"text-ellipsis-inner",title:g?xe:void 0,style:de,ref:se},A&&C&&ge,Ee))});exports.TextEllipsis=s,exports.c=l;
|
|
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 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} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\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 * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\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\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport function TextEllipsis({\n className,\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight = \"\",\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines,\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n */\n maskBgColor = \"#fff\",\n /**\n * text|ReactNode 与children任传一个\n */\n content,\n children,\n /**\n * 显示展开控制按钮\n */\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n /**\n * right | bottom 展开按钮在右下侧还是底部\n */\n uiType = \"right\",\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n}: TextEllipsisProps) {\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useState(true);\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(0);\n const [innerLines = 0, setInnerLines] = useState(lines);\n\n const [runtime] = useRuntime({\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', '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: lineHeight\n ? lineHeight\n : // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.5(em)\n getLineHeightFail\n ? \"1.5\"\n : 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: ellipsis && fold && lines * innerLineHeight, // TODO: 上面方案不支持 则需优雅降级为高度截断方案\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? (padding / foldBtnWidth) * 100 : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\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((evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n runtime.onFoldChange?.(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\"btn-fold-wrapper\", `btn-fold-wrapper-${uiType}`)}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>\n {fold ? unfoldText : foldText}\n </div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n // renderIcon,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((ellipsis = false) => {\n const {ellipsis: preEllipsis, fold: preFold} = runtime;\n if (ellipsis !== preEllipsis) {\n runtime.ellipsis = ellipsis;\n setEllipsis(ellipsis);\n runtime.onEllipsisChange?.(ellipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (ellipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n setInnerLines(adjustLines);\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n setInnerLines(lines);\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold ? wrapperRef.current?.textContent || '' : undefined;\n }, [ellipsis, fold]);\n // console.log('[render TextEllipsis]: ellipsis, fold: ', ellipsis, fold);\n return (\n <div className={cx(c(\"container\"), className)} style={containerStyle} ref={containerRef}>\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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div className={\"text-ellipsis-inner\"} title={hoverTitle} style={wrapStyle} ref={contentRef}>\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n}\n\n"],"names":["c","p","_ref","className","_ref$lineHeight","lineHeight","lines","_ref$maskBgColor","maskBgColor","content","children","_ref$showFoldControl","showFoldControl","_ref$foldText","foldText","_ref$unfoldText","unfoldText","_ref$uiType","uiType","renderFoldButton","onEllipsisChange","onFoldChange","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useState3","fold","setFold","_useState4","foldBtnWidth","setFoldBtnWidth","_useState5","innerLineHeight","setInnerLineHeight","_useState6","_useState6$","innerLines","setInnerLines","runtime","useRuntime","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ref","onClick","resetState","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","parseFloat","adjustLines","Math","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","finalContent","hoverTitle","_wrapperRef$current","textContent","Measure","offset","_ref3","measureRef","contentRect","abs","r","assignRef","title"],"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 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 * 折叠状态\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 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 showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\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 contentOffsetHeight: 0,\n ellipsis,\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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * 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]);\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 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 const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\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 if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\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 finalContent = content || children;\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 onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n console.log('[render TextEllipsis]: ellipsis fold: ', ellipsis, fold);\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 console.log('calcEllipsis');\n calcEllipsis();\n }\n return <div 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","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_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","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","console","log","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{
|
|
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 f,useRuntime as a,classNames as d,useCompatibleEffect as c,isSafari as u,assignRef as h}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";var v=s("ohkit-text-ellipsis__"),g=t(function(t,s){var g=t.className,m=t.lineHeight,C=void 0===m?"":m,x=t.lines,E=t.maskBgColor,w=void 0===E?"#fff":E,F=t.content,H=t.children,y=t.showTitleWhenFold,b=t.titleWhenFold,k=t.showFoldControl,W=void 0===k||k,M=t.foldText,B=void 0===M?"收起":M,L=t.unfoldText,N=void 0===L?"展开":L,T=t.uiType,O=void 0===T?"right":T,P=t.controlPlacement,S=void 0===P?"center":P,q=t.renderFoldButton,A=t.onEllipsisChange,R=t.onFoldChange,_=t.onStatusChange,V=t.onMouseEnter,j=t.onMouseLeave,z=t.onPointerEnter,D=t.onPointerLeave,G=t.onClick,I=t.onFocus,J=n(!1),K=J[0],Q=J[1],U=n(!1),X=U[0],Y=U[1],Z=f(t,"fold",{defaultValue:!0,onChange:R}),$=Z[0],ee=Z[1],te=n(1),ne=te[0],oe=te[1],ie=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),le=ie[0],re=ie[1],se=n(x),fe=se[0],ae=void 0===fe?0:fe,de=se[1],ce=n(""),ue=ce[0],he=ce[1],pe=a({contentOffsetHeight:0,ellipsis:K,fold:$,foldBtnWidth:ne,textContent:ue,onEllipsisChange:A,onFoldChange:R},["onEllipsisChange","fold","onFoldChange"])[0],ve=o(null),ge=o(null),me=o(null),Ce=o(null),xe=i(function(){return{lineHeight:X?"1.4":C||void 0}},[C,X]),Ee=i(function(){if(K&&ae&&le)return{minHeight:$?(ae-.5)*le+"px":void 0,WebkitLineClamp:$?ae:void 0,paddingBottom:"bottom"!==O&&$?void 0:le+"px"}},[ae,le,K,$,O]),we=i(function(){if($){var e=le,t="right"===O?Math.min(e/ne*100,80):60;return{height:le+"px",lineHeight:le+"px",paddingTop:"bottom"===O?e+"px":void 0,paddingLeft:"right"===O?e+"px":void 0,background:"linear-gradient(to "+O+", "+w+(4===w.length?"0":"00")+", "+w+" "+t+"%, "+w+" 100%)"}}},[le,w,$,O,ne]),Fe=l(function(){ve.current&&(ve.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ve.current&&(ve.current.style.width="100%")}))},[]),He=l(function(e,t){void 0===t&&(t=!pe.fold),pe.fold=t,ee(t)},[]),ye=i(function(){/*#__PURE__*/return e.createElement("div",{className:d("btn-fold-wrapper","btn-fold-wrapper-"+O,"bottom"===O&&"placement-"+S),style:we,ref:Ce,onClick:He},q?q($):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},$?N:B))},[we,$,B,He,q,S,O,N]),be=l(function(e){void 0===e&&(e=pe.ellipsis);var t=pe.fold;e!==pe.ellipsis&&(Q(e),pe.ellipsis=e,null==pe.onEllipsisChange||pe.onEllipsisChange(e),e&&!t&&He(void 0,!0))},[He]),ke=l(function(){var e=ge.current,t=me.current;if(e&&t){pe.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||Y(!0))}if(le!==n&&re(n),x)ae!==x&&de(x),be(pe.contentOffsetHeight>=(x+1)*n);else if(pe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);ae!==i&&de(i),be(!0)}else be(!1)}},[x,le,be]);c(function(){be(),ke()},[ke,be]),r(function(){if(K&&Ce.current){var e=Ce.current.offsetWidth;e!==pe.foldBtnWidth&&(pe.foldBtnWidth=e,oe(e))}},[K,N,W]),r(function(){u&&Fe()},[$,Fe]);var We=l(function(){var e,t=(null==(e=ge.current)?void 0:e.textContent)||"";t!==pe.textContent&&(pe.textContent=t,he(t))},[]),Me=F||H,Be=i(function(){return K&&$?"function"==typeof b?b(ue):b||ue:void 0},[b,K,$,ue]);return r(function(){null==_||_({ellipsis:K,fold:$,title:Be})},[_,$,K,Be]),console.log("[render TextEllipsis]: ellipsis fold: ",K,$),/*#__PURE__*/e.createElement("div",{className:d(v("container"),g),style:xe,ref:function(e){h(me,e),s&&h(s,e)},onMouseEnter:V,onMouseLeave:j,onPointerEnter:z,onPointerLeave:D,onClick:G,onFocus:I},/*#__PURE__*/e.createElement(p,{offset:!0},function(t){var n=t.measureRef,o=(t.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-pe.contentOffsetHeight)>1&&(console.log("calcEllipsis"),ke()),/*#__PURE__*/e.createElement("div",{className:"offset-height-computer",ref:function(e){h(n,e),h(ge,e),We()}},Me)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:y?Be:void 0,style:Ee,ref:ve},K&&W&&ye,Me))});export{g as TextEllipsis,v as c};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\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} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\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 * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\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\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport function TextEllipsis({\n className,\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight = \"\",\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines,\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n */\n maskBgColor = \"#fff\",\n /**\n * text|ReactNode 与children任传一个\n */\n content,\n children,\n /**\n * 显示展开控制按钮\n */\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n /**\n * right | bottom 展开按钮在右下侧还是底部\n */\n uiType = \"right\",\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n}: TextEllipsisProps) {\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useState(true);\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(0);\n const [innerLines = 0, setInnerLines] = useState(lines);\n\n const [runtime] = useRuntime({\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', '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: lineHeight\n ? lineHeight\n : // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.5(em)\n getLineHeightFail\n ? \"1.5\"\n : 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: ellipsis && fold && lines * innerLineHeight, // TODO: 上面方案不支持 则需优雅降级为高度截断方案\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? (padding / foldBtnWidth) * 100 : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\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((evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n runtime.onFoldChange?.(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\"btn-fold-wrapper\", `btn-fold-wrapper-${uiType}`)}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>\n {fold ? unfoldText : foldText}\n </div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n // renderIcon,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((ellipsis = false) => {\n const {ellipsis: preEllipsis, fold: preFold} = runtime;\n if (ellipsis !== preEllipsis) {\n runtime.ellipsis = ellipsis;\n setEllipsis(ellipsis);\n runtime.onEllipsisChange?.(ellipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (ellipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n setInnerLines(adjustLines);\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n setInnerLines(lines);\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold ? wrapperRef.current?.textContent || '' : undefined;\n }, [ellipsis, fold]);\n // console.log('[render TextEllipsis]: ellipsis, fold: ', ellipsis, fold);\n return (\n <div className={cx(c(\"container\"), className)} style={containerStyle} ref={containerRef}>\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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div className={\"text-ellipsis-inner\"} title={hoverTitle} style={wrapStyle} ref={contentRef}>\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n}\n\n"],"names":["c","p","TextEllipsis","_ref","className","_ref$lineHeight","lineHeight","lines","_ref$maskBgColor","maskBgColor","content","children","_ref$showFoldControl","showFoldControl","_ref$foldText","foldText","_ref$unfoldText","unfoldText","_ref$uiType","uiType","renderFoldButton","onEllipsisChange","onFoldChange","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useState3","fold","setFold","_useState4","foldBtnWidth","setFoldBtnWidth","_useState5","innerLineHeight","setInnerLineHeight","_useState6","_useState6$","innerLines","setInnerLines","runtime","useRuntime","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ref","onClick","resetState","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","parseFloat","adjustLines","Math","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","finalContent","hoverTitle","_wrapperRef$current","textContent","Measure","offset","_ref3","measureRef","contentRect","abs","r","assignRef","title"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.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 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 * 折叠状态\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 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 showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\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 contentOffsetHeight: 0,\n ellipsis,\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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * 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]);\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 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 const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\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 if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\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 finalContent = content || children;\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 onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n console.log('[render TextEllipsis]: ellipsis fold: ', ellipsis, fold);\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 console.log('calcEllipsis');\n calcEllipsis();\n }\n return <div 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","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_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","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","console","log","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
|
package/dist/index.modern.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{
|
|
1
|
+
import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as s}from"react";import{prefixClassname as r,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";const g=r("ohkit-text-ellipsis__"),m=t((t,r)=>{const{className:m,lineHeight:v="",lines:C,maskBgColor:x="#fff",content:E,children:w,showTitleWhenFold:F,titleWhenFold:H,showFoldControl:$=!0,foldText:y="收起",unfoldText:b="展开",uiType:k="right",controlPlacement:W="center",renderFoldButton:M,onEllipsisChange:B,onFoldChange:L,onStatusChange:N,onMouseEnter:T,onMouseLeave:O,onPointerEnter:P,onPointerLeave:S,onClick:q,onFocus:A}=t,[R,_]=n(!1),[V,j]=n(!1),[z,D]=d(t,"fold",{defaultValue:!0,onChange:L}),[G,I]=n(1),[J,K]=n("string"==typeof v&&v.endsWith("px")?parseFloat(v):0),[Q=0,U]=n(C),[X,Y]=n(""),[Z]=a({contentOffsetHeight:0,ellipsis:R,fold:z,foldBtnWidth:G,textContent:X,onEllipsisChange:B,onFoldChange:L},["onEllipsisChange","fold","onFoldChange"]),ee=o(null),te=o(null),ne=o(null),oe=o(null),ie=i(()=>({lineHeight:V?"1.4":v||void 0}),[v,V]),le=i(()=>{if(R&&Q&&J)return{minHeight:z?(Q-.5)*J+"px":void 0,WebkitLineClamp:z?Q:void 0,paddingBottom:"bottom"!==k&&z?void 0:`${J}px`}},[Q,J,R,z,k]),se=i(()=>{if(!z)return;const e=J,t="right"===k?Math.min(e/G*100,80):60;return{height:`${J}px`,lineHeight:`${J}px`,paddingTop:"bottom"===k?`${e}px`:void 0,paddingLeft:"right"===k?`${e}px`:void 0,background:`linear-gradient(to ${k}, ${x}${4===x.length?"0":"00"}, ${x} ${t}%, ${x} 100%)`}},[J,x,z,k,G]),re=l(()=>{ee.current&&(ee.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{ee.current&&(ee.current.style.width="100%")}))},[]),de=l((e,t=!Z.fold)=>{Z.fold=t,D(t)},[]),ae=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${k}`,"bottom"===k&&`placement-${W}`),style:se,ref:oe,onClick:de},M?M(z):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},z?b:y)),[se,z,y,de,M,W,k,b]),fe=l((e=Z.ellipsis)=>{const{ellipsis:t,fold:n}=Z;e!==t&&(_(e),Z.ellipsis=e,null==Z.onEllipsisChange||Z.onEllipsisChange(e),e&&!n&&de(void 0,!0))},[de]),ce=l(()=>{const e=te.current,t=ne.current;if(!e||!t)return;Z.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(J!==n&&K(n),C)Q!==C&&U(C),fe(Z.contentOffsetHeight>=(C+1)*n);else if(Z.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);Q!==e&&U(e),fe(!0)}else fe(!1)},[C,J,fe]);c(()=>{fe(),ce()},[ce,fe]),s(()=>{if(R&&oe.current){const{offsetWidth:e}=oe.current;e!==Z.foldBtnWidth&&(Z.foldBtnWidth=e,I(e))}},[R,b,$]),s(()=>{h&&re()},[z,re]);const he=l(()=>{var e;const t=(null==(e=te.current)?void 0:e.textContent)||"";t!==Z.textContent&&(Z.textContent=t,Y(t))},[]),ue=E||w,pe=i(()=>R&&z?"function"==typeof H?H(X):H||X:void 0,[H,R,z,X]);return s(()=>{null==N||N({ellipsis:R,fold:z,title:pe})},[N,z,R,pe]),console.log("[render TextEllipsis]: ellipsis fold: ",R,z),/*#__PURE__*/e.createElement("div",{className:f(g("container"),m),style:ie,ref:e=>{u(ne,e),r&&u(r,e)},onMouseEnter:T,onMouseLeave:O,onPointerEnter:P,onPointerLeave:S,onClick:q,onFocus:A},/*#__PURE__*/e.createElement(p,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-Z.contentOffsetHeight)>1&&(console.log("calcEllipsis"),ce()),/*#__PURE__*/e.createElement("div",{className:"offset-height-computer",ref:e=>{u(t,e),u(te,e),he()}},ue)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:F?pe:void 0,style:le,ref:ee},R&&$&&ae,ue))});export{m as TextEllipsis,g 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 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} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\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 * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\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\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport function TextEllipsis({\n className,\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight = \"\",\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines,\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n */\n maskBgColor = \"#fff\",\n /**\n * text|ReactNode 与children任传一个\n */\n content,\n children,\n /**\n * 显示展开控制按钮\n */\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n /**\n * right | bottom 展开按钮在右下侧还是底部\n */\n uiType = \"right\",\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n}: TextEllipsisProps) {\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useState(true);\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(0);\n const [innerLines = 0, setInnerLines] = useState(lines);\n\n const [runtime] = useRuntime({\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', '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: lineHeight\n ? lineHeight\n : // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.5(em)\n getLineHeightFail\n ? \"1.5\"\n : 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // maxHeight: ellipsis && fold && lines * innerLineHeight, // TODO: 上面方案不支持 则需优雅降级为高度截断方案\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? (padding / foldBtnWidth) * 100 : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\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((evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n runtime.onFoldChange?.(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\"btn-fold-wrapper\", `btn-fold-wrapper-${uiType}`)}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>\n {fold ? unfoldText : foldText}\n </div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n // renderIcon,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((ellipsis = false) => {\n const {ellipsis: preEllipsis, fold: preFold} = runtime;\n if (ellipsis !== preEllipsis) {\n runtime.ellipsis = ellipsis;\n setEllipsis(ellipsis);\n runtime.onEllipsisChange?.(ellipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (ellipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n setInnerLines(adjustLines);\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n setInnerLines(lines);\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold ? wrapperRef.current?.textContent || '' : undefined;\n }, [ellipsis, fold]);\n // console.log('[render TextEllipsis]: ellipsis, fold: ', ellipsis, fold);\n return (\n <div className={cx(c(\"container\"), className)} style={containerStyle} ref={containerRef}>\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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div className={\"text-ellipsis-inner\"} title={hoverTitle} style={wrapStyle} ref={contentRef}>\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n}\n\n"],"names":["c","p","TextEllipsis","className","lineHeight","lines","maskBgColor","content","children","showFoldControl","foldText","unfoldText","uiType","renderFoldButton","onEllipsisChange","onFoldChange","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","innerLines","setInnerLines","runtime","useRuntime","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","ref","onClick","resetState","preEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","parseFloat","adjustLines","Math","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","finalContent","hoverTitle","_wrapperRef$current","textContent","Measure","offset","measureRef","contentRect","abs","r","assignRef","title"],"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 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 * 折叠状态\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 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 showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\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 contentOffsetHeight: 0,\n ellipsis,\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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * 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]);\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 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 const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\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 }\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 if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\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 finalContent = content || children;\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 onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n console.log('[render TextEllipsis]: ellipsis fold: ', ellipsis, fold);\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 console.log('calcEllipsis');\n calcEllipsis();\n }\n return <div 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","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","console","log","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,
|
|
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,o){function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var l=/*#__PURE__*/i(t),s=n.prefixClassname("ohkit-text-ellipsis__"),a=t.forwardRef(function(e,i){var a=e.className,f=e.lineHeight,u=void 0===f?"":f,r=e.lines,d=e.maskBgColor,c=void 0===d?"#fff":d,h=e.content,p=e.children,g=e.showTitleWhenFold,m=e.titleWhenFold,v=e.showFoldControl,C=void 0===v||v,E=e.foldText,x=void 0===E?"收起":E,b=e.unfoldText,k=void 0===b?"展开":b,y=e.uiType,w=void 0===y?"right":y,F=e.controlPlacement,H=void 0===F?"center":F,M=e.renderFoldButton,R=e.onEllipsisChange,S=e.onFoldChange,T=e.onStatusChange,N=e.onMouseEnter,W=e.onMouseLeave,B=e.onPointerEnter,L=e.onPointerLeave,P=e.onClick,q=e.onFocus,O=t.useState(!1),j=O[0],A=O[1],_=t.useState(!1),V=_[0],z=_[1],D=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:S}),G=D[0],I=D[1],J=t.useState(1),K=J[0],Q=J[1],U=t.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),X=U[0],Y=U[1],Z=t.useState(r),$=Z[0],ee=void 0===$?0:$,te=Z[1],ne=t.useState(""),oe=ne[0],ie=ne[1],le=n.useRuntime({contentOffsetHeight:0,ellipsis:j,fold:G,foldBtnWidth:K,textContent:oe,onEllipsisChange:R,onFoldChange:S},["onEllipsisChange","fold","onFoldChange"])[0],se=t.useRef(null),ae=t.useRef(null),fe=t.useRef(null),ue=t.useRef(null),re=t.useMemo(function(){return{lineHeight:V?"1.4":u||void 0}},[u,V]),de=t.useMemo(function(){if(j&&ee&&X)return{minHeight:G?(ee-.5)*X+"px":void 0,WebkitLineClamp:G?ee:void 0,paddingBottom:"bottom"!==w&&G?void 0:X+"px"}},[ee,X,j,G,w]),ce=t.useMemo(function(){if(G){var e=X,t="right"===w?Math.min(e/K*100,80):60;return{height:X+"px",lineHeight:X+"px",paddingTop:"bottom"===w?e+"px":void 0,paddingLeft:"right"===w?e+"px":void 0,background:"linear-gradient(to "+w+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[X,c,G,w,K]),he=t.useCallback(function(){se.current&&(se.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){se.current&&(se.current.style.width="100%")}))},[]),pe=t.useCallback(function(e,t){void 0===t&&(t=!le.fold),le.fold=t,I(t)},[]),ge=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+w,"bottom"===w&&"placement-"+H),style:ce,ref:ue,onClick:pe},M?M(G):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},G?k:x))},[ce,G,x,pe,M,H,w,k]),me=t.useCallback(function(e){void 0===e&&(e=le.ellipsis);var t=le.fold;e!==le.ellipsis&&(A(e),le.ellipsis=e,null==le.onEllipsisChange||le.onEllipsisChange(e),e&&!t&&pe(void 0,!0))},[pe]),ve=t.useCallback(function(){var e=ae.current,t=fe.current;if(e&&t){le.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||z(!0))}if(X!==n&&Y(n),r)ee!==r&&te(r),me(le.contentOffsetHeight>=(r+1)*n);else if(le.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);ee!==i&&te(i),me(!0)}else me(!1)}},[r,X,me]);n.useCompatibleEffect(function(){me(),ve()},[ve,me]),t.useEffect(function(){if(j&&ue.current){var e=ue.current.offsetWidth;e!==le.foldBtnWidth&&(le.foldBtnWidth=e,Q(e))}},[j,k,C]),t.useEffect(function(){n.isSafari&&he()},[G,he]);var Ce=t.useCallback(function(){var e,t=(null==(e=ae.current)?void 0:e.textContent)||"";t!==le.textContent&&(le.textContent=t,ie(t))},[]),Ee=h||p,xe=t.useMemo(function(){return j&&G?"function"==typeof m?m(oe):m||oe:void 0},[m,j,G,oe]);return t.useEffect(function(){null==T||T({ellipsis:j,fold:G,title:xe})},[T,G,j,xe]),console.log("[render TextEllipsis]: ellipsis fold: ",j,G),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(s("container"),a),style:re,ref:function(e){n.assignRef(fe,e),i&&n.assignRef(i,e)},onMouseEnter:N,onMouseLeave:W,onPointerEnter:B,onPointerLeave:L,onClick:P,onFocus:q},/*#__PURE__*/l.default.createElement(o.Measure,{offset:!0},function(e){var t=e.measureRef,o=(e.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-le.contentOffsetHeight)>1&&(console.log("calcEllipsis"),ve()),/*#__PURE__*/l.default.createElement("div",{className:"offset-height-computer",ref:function(e){n.assignRef(t,e),n.assignRef(ae,e),Ce()}},Ee)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:g?xe:void 0,style:de,ref:se},j&&C&&ge,Ee))});e.TextEllipsis=a,e.c=s});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|