@ohkit/text-ellipsis 0.0.3 → 0.0.5

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.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as f,useSyncPropsState as a,useRuntime as d,classNames as s,useCompatibleEffect as c,isSafari as u,assignRef as h}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";var v=f("ohkit-text-ellipsis__"),g=t(function(t,f){var g=t.className,m=t.lineHeight,C=void 0===m?"":m,x=t.lines,w=t.maskBgColor,E=void 0===w?"#fff":w,F=t.showTitleWhenFold,H=t.titleWhenFold,b=t.showFoldControl,y=void 0===b||b,k=t.foldText,W=void 0===k?"收起":k,M=t.unfoldText,S=void 0===M?"展开":M,B=t.uiType,L=void 0===B?"right":B,N=t.controlPlacement,O=void 0===N?"center":N,P=t.whiteSpace,T=t.renderFoldButton,q=t.onEllipsisChange,A=t.onFoldChange,R=t.onStatusChange,_=t.onMouseEnter,z=t.onMouseLeave,V=t.onPointerEnter,j=t.onPointerLeave,D=t.onClick,G=t.onFocus,I=t.content||t.children,J=n(!1),K=J[0],Q=J[1],U=n(!1),X=U[0],Y=U[1],Z=a(t,"fold",{defaultValue:!0,onChange:A}),$=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],fe=n(x),ae=fe[0],de=void 0===ae?0:ae,se=fe[1],ce=n(""),ue=ce[0],he=ce[1],pe=d({inited:!1,contentOffsetHeight:0,ellipsis:K,fold:$,foldBtnWidth:ne,textContent:ue,onEllipsisChange:q,onFoldChange:A},["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]),we=i(function(){if(K&&de&&le)return{whiteSpace:P,minHeight:$?(de-.2)*le+"px":void 0,WebkitLineClamp:$?de:void 0,paddingBottom:"bottom"!==L&&$?void 0:le+"px"}},[de,le,K,$,L,P]),Ee=i(function(){if($){var e=le,t="right"===L?Math.min(e/ne*100,80):60;return{boxSizing:"content-box",height:le+"px",lineHeight:le+"px",paddingTop:"bottom"===L?e+"px":void 0,paddingLeft:"right"===L?e+"px":void 0,background:"linear-gradient(to "+L+", "+E+(4===E.length?"0":"00")+", "+E+" "+t+"%, "+E+" 100%)"}}},[le,E,$,L,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)},[]),be=i(function(){/*#__PURE__*/return e.createElement("div",{className:s("btn-fold-wrapper","btn-fold-wrapper-"+L,"bottom"===L&&"placement-"+O),style:Ee,ref:Ce,onClick:He},T?T($):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},$?S:W))},[Ee,$,W,He,T,O,L,S]),ye=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),n))if(x)de!==x&&se(x),ye(pe.contentOffsetHeight>=(x+1)*n-1);else if(pe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);de!==i&&se(i),ye(!0)}else ye(!1)}},[x,le,I,ye]);c(function(){ye(),ke()},[ke,ye]),r(function(){if(K&&Ce.current){var e=Ce.current.offsetWidth;e!==pe.foldBtnWidth&&(pe.foldBtnWidth=e,oe(e))}},[K,S,y]),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=i(function(){return K&&$?"function"==typeof H?H(ue):H||ue:void 0},[H,K,$,ue]);return r(function(){pe.inited&&(null==R||R({ellipsis:K,fold:$,title:Me}))},[R,$,K,Me]),r(function(){pe.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:s(v("container"),g),style:xe,ref:function(e){h(me,e),f&&h(f,e)},onMouseEnter:_,onMouseLeave:z,onPointerEnter:V,onPointerLeave:j,onClick:D,onFocus:G},/*#__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&&ke(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:P},className:"offset-height-computer",ref:function(e){h(n,e),h(ge,e),We()}},I)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:F?Me:void 0,style:we,ref:ve},K&&y&&be,I))});export{g as TextEllipsis,v as c};
1
+ import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as f,useSyncPropsState as a,useRuntime as d,classNames as s,useCompatibleEffect as c,isSafari as u,assignRef as h}from"@ohkit/utils";import{Measure as v}from"@ohkit/measure";function p(){return p=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},p.apply(null,arguments)}var g=f("ohkit-text-ellipsis__"),m=t(function(t,f){var m=t.className,C=t.lineHeight,w=void 0===C?"":C,x=t.lines,F=t.maskBgColor,E=void 0===F?"#fff":F,b=t.resetFoldWhenChildrenOrEllipsisChange,y=void 0!==b&&b,H=t.showTitleWhenFold,k=t.titleWhenFold,O=t.showFoldControl,W=void 0===O||O,M=t.foldText,S=void 0===M?"收起":M,B=t.unfoldText,L=void 0===B?"展开":B,N=t.uiType,P=void 0===N?"right":N,T=t.controlPlacement,R=void 0===T?"center":T,j=t.whiteSpace,q=t.renderFoldButton,A=t.onEllipsisChange,_=t.onFoldChange,z=t.onStatusChange,V=t.onMouseEnter,D=t.onMouseLeave,G=t.onPointerEnter,I=t.onPointerLeave,J=t.onClick,K=t.onFocus,Q=t.content||t.children,U=n(!1),X=U[0],Y=U[1],Z=n(!1),$=Z[0],ee=Z[1],te=a(t,"fold",{defaultValue:!0,onChange:_}),ne=te[0],oe=te[1],ie=n(1),le=ie[0],re=ie[1],fe=n("string"==typeof w&&w.endsWith("px")?parseFloat(w):0),ae=fe[0],de=fe[1],se=n(x),ce=se[0],ue=void 0===ce?0:ce,he=se[1],ve=n(""),pe=ve[0],ge=ve[1],me=d({inited:!1,contentOffsetHeight:0,ellipsis:X,defaultFold:ne,fold:ne,foldBtnWidth:le,textContent:pe,onEllipsisChange:A,onFoldChange:_},["onEllipsisChange","fold","onFoldChange"])[0],Ce=o(null),we=o(null),xe=o(null),Fe=o(null),Ee=i(function(){return{lineHeight:$?"1.4":w||void 0}},[w,$]),be=i(function(){var e={whiteSpace:j};return X&&ue&&ae?p({},e,{minHeight:ne?(ue-.2)*ae+"px":void 0,WebkitLineClamp:ne?ue:void 0,paddingBottom:"bottom"!==P&&ne?void 0:ae+"px"}):e},[ue,ae,X,ne,P,j]),ye=i(function(){if(ne){var e=ae,t="right"===P?Math.min(e/le*100,80):60;return{boxSizing:"content-box",height:ae+"px",lineHeight:ae+"px",paddingTop:"bottom"===P?e+"px":void 0,paddingLeft:"right"===P?e+"px":void 0,background:"linear-gradient(to "+P+", "+E+(4===E.length?"0":"00")+", "+E+" "+t+"%, "+E+" 100%)"}}},[ae,E,ne,P,le]),He=l(function(){Ce.current&&(Ce.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){Ce.current&&(Ce.current.style.width="100%")}))},[]),ke=l(function(e,t){void 0===t&&(t=!me.fold),me.fold=t,oe(t)},[]),Oe=i(function(){/*#__PURE__*/return e.createElement("div",{className:s("btn-fold-wrapper","btn-fold-wrapper-"+P,"bottom"===P&&"placement-"+R),style:ye,ref:Fe,onClick:ke},q?q(ne):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},ne?L:S))},[ye,ne,S,ke,q,R,P,L]),We=l(function(e,t){void 0===e&&(e=me.ellipsis);var n=(void 0===t?{}:t).forceResetFold,o=void 0!==n&&n,i=me.ellipsis,l=me.fold,r=me.defaultFold;e!==i&&(Y(e),me.ellipsis=e,null==me.onEllipsisChange||me.onEllipsisChange(e)),y&&(o||!i&&e)&&l!==r&&ke(void 0,r)},[ke,Q,y]),Me=l(function(){var e=we.current,t=xe.current;if(e&&t){me.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||ee(!0))}if(ae===n||(de(n),n))if(x)ue!==x&&he(x),We(me.contentOffsetHeight>=(x+1)*n-1);else if(me.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);ue!==i&&he(i),We(!0)}else We(!1)}},[x,ae,We]);c(function(){We(me.ellipsis,{forceResetFold:!0}),Me()},[Me,We]),r(function(){if(X&&Fe.current){var e=Fe.current.offsetWidth;e!==me.foldBtnWidth&&(me.foldBtnWidth=e,re(e))}},[X,L,W]),r(function(){u&&He()},[ne,He]);var Se=l(function(){var e,t=(null==(e=we.current)?void 0:e.textContent)||"";t!==me.textContent&&(me.textContent=t,ge(t))},[]),Be=i(function(){return X&&ne?"function"==typeof k?k(pe):k||pe:void 0},[k,X,ne,pe]);return r(function(){me.inited&&(null==z||z({ellipsis:X,fold:ne,title:Be}))},[z,ne,X,Be]),r(function(){me.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:s(g("container"),m),style:Ee,ref:function(e){h(xe,e),f&&h(f,e)},onMouseEnter:V,onMouseLeave:D,onPointerEnter:G,onPointerLeave:I,onClick:J,onFocus:K},/*#__PURE__*/e.createElement(v,{offset:!0},function(t){var n=t.measureRef,o=(t.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-me.contentOffsetHeight)>1&&Me(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:j},className:"offset-height-computer",ref:function(e){h(n,e),h(we,e),Se()}},Q)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:H?Be:void 0,style:be,ref:Ce},X&&W&&Oe,Q))});export{m as TextEllipsis,g as c};
2
2
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n 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 if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, finalContent, 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 hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const commonStyle = {\n whiteSpace\n }; \n if (!ellipsis || !lines || !innerLineHeight) {\n return commonStyle;\n }\n return {\n ...commonStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","commonStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=/*#__PURE__*/i(e),l=t.prefixClassname("ohkit-text-ellipsis__"),s=e.forwardRef(function(i,s){var a=i.className,f=i.lineHeight,r=void 0===f?"":f,u=i.lines,d=i.maskBgColor,c=void 0===d?"#fff":d,h=i.showTitleWhenFold,p=i.titleWhenFold,v=i.showFoldControl,g=void 0===v||v,m=i.foldText,C=void 0===m?"收起":m,x=i.unfoldText,E=void 0===x?"展开":x,b=i.uiType,w=void 0===b?"right":b,S=i.controlPlacement,k=void 0===S?"center":S,y=i.whiteSpace,F=i.renderFoldButton,H=i.onEllipsisChange,M=i.onFoldChange,R=i.onStatusChange,N=i.onMouseEnter,W=i.onMouseLeave,B=i.onPointerEnter,L=i.onPointerLeave,P=i.onClick,T=i.onFocus,q=i.content||i.children,O=e.useState(!1),A=O[0],_=O[1],j=e.useState(!1),z=j[0],V=j[1],D=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:M}),G=D[0],I=D[1],J=e.useState(1),K=J[0],Q=J[1],U=e.useState("string"==typeof r&&r.endsWith("px")?parseFloat(r):0),X=U[0],Y=U[1],Z=e.useState(u),$=Z[0],ee=void 0===$?0:$,te=Z[1],ne=e.useState(""),ie=ne[0],oe=ne[1],le=t.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:A,fold:G,foldBtnWidth:K,textContent:ie,onEllipsisChange:H,onFoldChange:M},["onEllipsisChange","fold","onFoldChange"])[0],se=e.useRef(null),ae=e.useRef(null),fe=e.useRef(null),re=e.useRef(null),ue=e.useMemo(function(){return{lineHeight:z?"1.4":r||void 0}},[r,z]),de=e.useMemo(function(){if(A&&ee&&X)return{whiteSpace:y,minHeight:G?(ee-.2)*X+"px":void 0,WebkitLineClamp:G?ee:void 0,paddingBottom:"bottom"!==w&&G?void 0:X+"px"}},[ee,X,A,G,w,y]),ce=e.useMemo(function(){if(G){var e=X,t="right"===w?Math.min(e/K*100,80):60;return{boxSizing:"content-box",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=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)},[]),ve=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+w,"bottom"===w&&"placement-"+k),style:ce,ref:re,onClick:pe},F?F(G):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},G?E:C))},[ce,G,C,pe,F,k,w,E]),ge=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=fe.current;if(e&&t){le.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||V(!0))}if(X===n||(Y(n),n))if(u)ee!==u&&te(u),ge(le.contentOffsetHeight>=(u+1)*n-1);else if(le.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ee!==o&&te(o),ge(!0)}else ge(!1)}},[u,X,q,ge]);t.useCompatibleEffect(function(){ge(),me()},[me,ge]),e.useEffect(function(){if(A&&re.current){var e=re.current.offsetWidth;e!==le.foldBtnWidth&&(le.foldBtnWidth=e,Q(e))}},[A,E,g]),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,oe(t))},[]),xe=e.useMemo(function(){return A&&G?"function"==typeof p?p(ie):p||ie:void 0},[p,A,G,ie]);return e.useEffect(function(){le.inited&&(null==R||R({ellipsis:A,fold:G,title:xe}))},[R,G,A,xe]),e.useEffect(function(){le.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(l("container"),a),style:ue,ref:function(e){t.assignRef(fe,e),s&&t.assignRef(s,e)},onMouseEnter:N,onMouseLeave:W,onPointerEnter:B,onPointerLeave:L,onClick:P,onFocus:T},/*#__PURE__*/o.default.createElement(n.Measure,{offset:!0},function(e){var n=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-le.contentOffsetHeight)>1&&me(),/*#__PURE__*/o.default.createElement("div",{style:{whiteSpace:y},className:"offset-height-computer",ref:function(e){t.assignRef(n,e),t.assignRef(ae,e),Ce()}},q)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:h?xe:void 0,style:de,ref:se},A&&g&&ve,q))});exports.TextEllipsis=s,exports.c=l;
1
+ var e=require("react"),t=require("@ohkit/utils"),n=require("@ohkit/measure");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=/*#__PURE__*/i(e);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},l.apply(null,arguments)}var s=t.prefixClassname("ohkit-text-ellipsis__"),a=e.forwardRef(function(i,a){var r=i.className,f=i.lineHeight,u=void 0===f?"":f,d=i.lines,c=i.maskBgColor,h=void 0===c?"#fff":c,v=i.resetFoldWhenChildrenOrEllipsisChange,p=void 0!==v&&v,g=i.showTitleWhenFold,m=i.titleWhenFold,C=i.showFoldControl,E=void 0===C||C,b=i.foldText,x=void 0===b?"收起":b,w=i.unfoldText,F=void 0===w?"展开":w,S=i.uiType,y=void 0===S?"right":S,k=i.controlPlacement,R=void 0===k?"center":k,H=i.whiteSpace,M=i.renderFoldButton,O=i.onEllipsisChange,W=i.onFoldChange,N=i.onStatusChange,P=i.onMouseEnter,B=i.onMouseLeave,L=i.onPointerEnter,T=i.onPointerLeave,q=i.onClick,j=i.onFocus,A=i.content||i.children,_=e.useState(!1),z=_[0],V=_[1],D=e.useState(!1),G=D[0],I=D[1],J=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:W}),K=J[0],Q=J[1],U=e.useState(1),X=U[0],Y=U[1],Z=e.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),$=Z[0],ee=Z[1],te=e.useState(d),ne=te[0],ie=void 0===ne?0:ne,oe=te[1],le=e.useState(""),se=le[0],ae=le[1],re=t.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:z,defaultFold:K,fold:K,foldBtnWidth:X,textContent:se,onEllipsisChange:O,onFoldChange:W},["onEllipsisChange","fold","onFoldChange"])[0],fe=e.useRef(null),ue=e.useRef(null),de=e.useRef(null),ce=e.useRef(null),he=e.useMemo(function(){return{lineHeight:G?"1.4":u||void 0}},[u,G]),ve=e.useMemo(function(){var e={whiteSpace:H};return z&&ie&&$?l({},e,{minHeight:K?(ie-.2)*$+"px":void 0,WebkitLineClamp:K?ie:void 0,paddingBottom:"bottom"!==y&&K?void 0:$+"px"}):e},[ie,$,z,K,y,H]),pe=e.useMemo(function(){if(K){var e=$,t="right"===y?Math.min(e/X*100,80):60;return{boxSizing:"content-box",height:$+"px",lineHeight:$+"px",paddingTop:"bottom"===y?e+"px":void 0,paddingLeft:"right"===y?e+"px":void 0,background:"linear-gradient(to "+y+", "+h+(4===h.length?"0":"00")+", "+h+" "+t+"%, "+h+" 100%)"}}},[$,h,K,y,X]),ge=e.useCallback(function(){fe.current&&(fe.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){fe.current&&(fe.current.style.width="100%")}))},[]),me=e.useCallback(function(e,t){void 0===t&&(t=!re.fold),re.fold=t,Q(t)},[]),Ce=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+y,"bottom"===y&&"placement-"+R),style:pe,ref:ce,onClick:me},M?M(K):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},K?F:x))},[pe,K,x,me,M,R,y,F]),Ee=e.useCallback(function(e,t){void 0===e&&(e=re.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=re.ellipsis,l=re.fold,s=re.defaultFold;e!==o&&(V(e),re.ellipsis=e,null==re.onEllipsisChange||re.onEllipsisChange(e)),p&&(i||!o&&e)&&l!==s&&me(void 0,s)},[me,A,p]),be=e.useCallback(function(){var e=ue.current,t=de.current;if(e&&t){re.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||I(!0))}if($===n||(ee(n),n))if(d)ie!==d&&oe(d),Ee(re.contentOffsetHeight>=(d+1)*n-1);else if(re.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ie!==o&&oe(o),Ee(!0)}else Ee(!1)}},[d,$,Ee]);t.useCompatibleEffect(function(){Ee(re.ellipsis,{forceResetFold:!0}),be()},[be,Ee]),e.useEffect(function(){if(z&&ce.current){var e=ce.current.offsetWidth;e!==re.foldBtnWidth&&(re.foldBtnWidth=e,Y(e))}},[z,F,E]),e.useEffect(function(){t.isSafari&&ge()},[K,ge]);var xe=e.useCallback(function(){var e,t=(null==(e=ue.current)?void 0:e.textContent)||"";t!==re.textContent&&(re.textContent=t,ae(t))},[]),we=e.useMemo(function(){return z&&K?"function"==typeof m?m(se):m||se:void 0},[m,z,K,se]);return e.useEffect(function(){re.inited&&(null==N||N({ellipsis:z,fold:K,title:we}))},[N,K,z,we]),e.useEffect(function(){re.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(s("container"),r),style:he,ref:function(e){t.assignRef(de,e),a&&t.assignRef(a,e)},onMouseEnter:P,onMouseLeave:B,onPointerEnter:L,onPointerLeave:T,onClick:q,onFocus:j},/*#__PURE__*/o.default.createElement(n.Measure,{offset:!0},function(e){var n=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-re.contentOffsetHeight)>1&&be(),/*#__PURE__*/o.default.createElement("div",{style:{whiteSpace:H},className:"offset-height-computer",ref:function(e){t.assignRef(n,e),t.assignRef(ue,e),xe()}},A)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:g?we:void 0,style:ve,ref:fe},z&&E&&Ce,A))});exports.TextEllipsis=a,exports.c=s;
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n 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 if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, finalContent, 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 hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const commonStyle = {\n whiteSpace\n }; \n if (!ellipsis || !lines || !innerLineHeight) {\n return commonStyle;\n }\n return {\n ...commonStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","commonStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as s}from"react";import{prefixClassname as r,useSyncPropsState as a,useRuntime as d,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:w,children:E,showTitleWhenFold:F,titleWhenFold:H,showFoldControl:$=!0,foldText:b="收起",unfoldText:y="展开",uiType:k="right",controlPlacement:W="center",whiteSpace:M,renderFoldButton:S,onEllipsisChange:B,onFoldChange:L,onStatusChange:N,onMouseEnter:O,onMouseLeave:P,onPointerEnter:T,onPointerLeave:q,onClick:A,onFocus:R}=t,_=w||E,[z,V]=n(!1),[j,D]=n(!1),[G,I]=a(t,"fold",{defaultValue:!0,onChange:L}),[J,K]=n(1),[Q,U]=n("string"==typeof v&&v.endsWith("px")?parseFloat(v):0),[X=0,Y]=n(C),[Z,ee]=n(""),[te]=d({inited:!1,contentOffsetHeight:0,ellipsis:z,fold:G,foldBtnWidth:J,textContent:Z,onEllipsisChange:B,onFoldChange:L},["onEllipsisChange","fold","onFoldChange"]),ne=o(null),oe=o(null),ie=o(null),le=o(null),se=i(()=>({lineHeight:j?"1.4":v||void 0}),[v,j]),re=i(()=>{if(z&&X&&Q)return{whiteSpace:M,minHeight:G?(X-.2)*Q+"px":void 0,WebkitLineClamp:G?X:void 0,paddingBottom:"bottom"!==k&&G?void 0:`${Q}px`}},[X,Q,z,G,k,M]),ae=i(()=>{if(!G)return;const e=Q,t="right"===k?Math.min(e/J*100,80):60;return{boxSizing:"content-box",height:`${Q}px`,lineHeight:`${Q}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%)`}},[Q,x,G,k,J]),de=l(()=>{ne.current&&(ne.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{ne.current&&(ne.current.style.width="100%")}))},[]),fe=l((e,t=!te.fold)=>{te.fold=t,I(t)},[]),ce=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${k}`,"bottom"===k&&`placement-${W}`),style:ae,ref:le,onClick:fe},S?S(G):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},G?y:b)),[ae,G,b,fe,S,W,k,y]),he=l((e=te.ellipsis)=>{const{ellipsis:t,fold:n}=te;e!==t&&(V(e),te.ellipsis=e,null==te.onEllipsisChange||te.onEllipsisChange(e),e&&!n&&fe(void 0,!0))},[fe]),ue=l(()=>{const e=oe.current,t=ie.current;if(!e||!t)return;te.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||D(!0))}if(Q===n||(U(n),n))if(C)X!==C&&Y(C),he(te.contentOffsetHeight>=(C+1)*n-1);else if(te.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);X!==e&&Y(e),he(!0)}else he(!1)},[C,Q,_,he]);c(()=>{he(),ue()},[ue,he]),s(()=>{if(z&&le.current){const{offsetWidth:e}=le.current;e!==te.foldBtnWidth&&(te.foldBtnWidth=e,K(e))}},[z,y,$]),s(()=>{h&&de()},[G,de]);const pe=l(()=>{var e;const t=(null==(e=oe.current)?void 0:e.textContent)||"";t!==te.textContent&&(te.textContent=t,ee(t))},[]),ge=i(()=>z&&G?"function"==typeof H?H(Z):H||Z:void 0,[H,z,G,Z]);return s(()=>{te.inited&&(null==N||N({ellipsis:z,fold:G,title:ge}))},[N,G,z,ge]),s(()=>{te.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:f(g("container"),m),style:se,ref:e=>{u(ie,e),r&&u(r,e)},onMouseEnter:O,onMouseLeave:P,onPointerEnter:T,onPointerLeave:q,onClick:A,onFocus:R},/*#__PURE__*/e.createElement(p,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-te.contentOffsetHeight)>1&&ue(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:M},className:"offset-height-computer",ref:e=>{u(t,e),u(oe,e),pe()}},_)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:F?ge:void 0,style:re,ref:ne},z&&$&&ce,_))});export{m as TextEllipsis,g as c};
1
+ import e,{forwardRef as t,useState as n,useRef as o,useMemo as i,useCallback as l,useEffect as r}from"react";import{prefixClassname as s,useSyncPropsState as a,useRuntime as d,classNames as f,useCompatibleEffect as c,isSafari as h,assignRef as u}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";function g(){return g=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},g.apply(null,arguments)}const m=s("ohkit-text-ellipsis__"),v=t((t,s)=>{const{className:v,lineHeight:C="",lines:w,maskBgColor:x="#fff",content:F,children:E,resetFoldWhenChildrenOrEllipsisChange:b=!1,showTitleWhenFold:y,titleWhenFold:H,showFoldControl:$=!0,foldText:k="收起",unfoldText:O="展开",uiType:W="right",controlPlacement:M="center",whiteSpace:S,renderFoldButton:B,onEllipsisChange:L,onFoldChange:N,onStatusChange:P,onMouseEnter:T,onMouseLeave:R,onPointerEnter:j,onPointerLeave:q,onClick:A,onFocus:_}=t,z=F||E,[V,D]=n(!1),[G,I]=n(!1),[J,K]=a(t,"fold",{defaultValue:!0,onChange:N}),[Q,U]=n(1),[X,Y]=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),[Z=0,ee]=n(w),[te,ne]=n(""),[oe]=d({inited:!1,contentOffsetHeight:0,ellipsis:V,defaultFold:J,fold:J,foldBtnWidth:Q,textContent:te,onEllipsisChange:L,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"]),ie=o(null),le=o(null),re=o(null),se=o(null),ae=i(()=>({lineHeight:G?"1.4":C||void 0}),[C,G]),de=i(()=>{const e={whiteSpace:S};return V&&Z&&X?g({},e,{minHeight:J?(Z-.2)*X+"px":void 0,WebkitLineClamp:J?Z:void 0,paddingBottom:"bottom"!==W&&J?void 0:`${X}px`}):e},[Z,X,V,J,W,S]),fe=i(()=>{if(!J)return;const e=X,t="right"===W?Math.min(e/Q*100,80):60;return{boxSizing:"content-box",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}, ${x}${4===x.length?"0":"00"}, ${x} ${t}%, ${x} 100%)`}},[X,x,J,W,Q]),ce=l(()=>{ie.current&&(ie.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{ie.current&&(ie.current.style.width="100%")}))},[]),he=l((e,t=!oe.fold)=>{oe.fold=t,K(t)},[]),ue=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${W}`,"bottom"===W&&`placement-${M}`),style:fe,ref:se,onClick:he},B?B(J):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},J?O:k)),[fe,J,k,he,B,M,W,O]),pe=l((e=oe.ellipsis,{forceResetFold:t=!1}={})=>{const{ellipsis:n,fold:o,defaultFold:i}=oe;e!==n&&(D(e),oe.ellipsis=e,null==oe.onEllipsisChange||oe.onEllipsisChange(e)),b&&(t||!n&&e)&&o!==i&&he(void 0,i)},[he,z,b]),ge=l(()=>{const e=le.current,t=re.current;if(!e||!t)return;oe.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||I(!0))}if(X===n||(Y(n),n))if(w)Z!==w&&ee(w),pe(oe.contentOffsetHeight>=(w+1)*n-1);else if(oe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);Z!==e&&ee(e),pe(!0)}else pe(!1)},[w,X,pe]);c(()=>{pe(oe.ellipsis,{forceResetFold:!0}),ge()},[ge,pe]),r(()=>{if(V&&se.current){const{offsetWidth:e}=se.current;e!==oe.foldBtnWidth&&(oe.foldBtnWidth=e,U(e))}},[V,O,$]),r(()=>{h&&ce()},[J,ce]);const me=l(()=>{var e;const t=(null==(e=le.current)?void 0:e.textContent)||"";t!==oe.textContent&&(oe.textContent=t,ne(t))},[]),ve=i(()=>V&&J?"function"==typeof H?H(te):H||te:void 0,[H,V,J,te]);return r(()=>{oe.inited&&(null==P||P({ellipsis:V,fold:J,title:ve}))},[P,J,V,ve]),r(()=>{oe.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:f(m("container"),v),style:ae,ref:e=>{u(re,e),s&&u(s,e)},onMouseEnter:T,onMouseLeave:R,onPointerEnter:j,onPointerLeave:q,onClick:A,onFocus:_},/*#__PURE__*/e.createElement(p,{offset:!0},({measureRef:t,contentRect:n})=>{const{height:o}=n.offset||{};return void 0!==o&&Math.abs(o-oe.contentOffsetHeight)>1&&ge(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:S},className:"offset-height-computer",ref:e=>{u(t,e),u(le,e),me()}},z)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:y?ve:void 0,style:de,ref:ie},V&&$&&ue,z))});export{v as TextEllipsis,m as c};
2
2
  //# sourceMappingURL=index.modern.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n 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 if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, finalContent, 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 hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","lineHeight","lines","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const commonStyle = {\n whiteSpace\n }; \n if (!ellipsis || !lines || !innerLineHeight) {\n return commonStyle;\n }\n return {\n ...commonStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","lineHeight","lines","maskBgColor","content","children","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","commonStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/utils"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/utils","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.utils,e.measure)}(this,function(e,t,n,i){function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var l=/*#__PURE__*/o(t),s=n.prefixClassname("ohkit-text-ellipsis__"),f=t.forwardRef(function(e,o){var f=e.className,a=e.lineHeight,u=void 0===a?"":a,r=e.lines,d=e.maskBgColor,c=void 0===d?"#fff":d,h=e.showTitleWhenFold,p=e.titleWhenFold,g=e.showFoldControl,m=void 0===g||g,v=e.foldText,C=void 0===v?"收起":v,x=e.unfoldText,E=void 0===x?"展开":x,b=e.uiType,w=void 0===b?"right":b,y=e.controlPlacement,k=void 0===y?"center":y,S=e.whiteSpace,F=e.renderFoldButton,H=e.onEllipsisChange,M=e.onFoldChange,R=e.onStatusChange,N=e.onMouseEnter,T=e.onMouseLeave,W=e.onPointerEnter,B=e.onPointerLeave,L=e.onClick,P=e.onFocus,q=e.content||e.children,O=t.useState(!1),j=O[0],A=O[1],_=t.useState(!1),z=_[0],V=_[1],D=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:M}),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(""),ie=ne[0],oe=ne[1],le=n.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:j,fold:G,foldBtnWidth:K,textContent:ie,onEllipsisChange:H,onFoldChange:M},["onEllipsisChange","fold","onFoldChange"])[0],se=t.useRef(null),fe=t.useRef(null),ae=t.useRef(null),ue=t.useRef(null),re=t.useMemo(function(){return{lineHeight:z?"1.4":u||void 0}},[u,z]),de=t.useMemo(function(){if(j&&ee&&X)return{whiteSpace:S,minHeight:G?(ee-.2)*X+"px":void 0,WebkitLineClamp:G?ee:void 0,paddingBottom:"bottom"!==w&&G?void 0:X+"px"}},[ee,X,j,G,w,S]),ce=t.useMemo(function(){if(G){var e=X,t="right"===w?Math.min(e/K*100,80):60;return{boxSizing:"content-box",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-"+k),style:ce,ref:ue,onClick:pe},F?F(G):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},G?E:C))},[ce,G,C,pe,F,k,w,E]),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=fe.current,t=ae.current;if(e&&t){le.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||V(!0))}if(X===n||(Y(n),n))if(r)ee!==r&&te(r),me(le.contentOffsetHeight>=(r+1)*n-1);else if(le.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ee!==o&&te(o),me(!0)}else me(!1)}},[r,X,q,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,E,m]),t.useEffect(function(){n.isSafari&&he()},[G,he]);var Ce=t.useCallback(function(){var e,t=(null==(e=fe.current)?void 0:e.textContent)||"";t!==le.textContent&&(le.textContent=t,oe(t))},[]),xe=t.useMemo(function(){return j&&G?"function"==typeof p?p(ie):p||ie:void 0},[p,j,G,ie]);return t.useEffect(function(){le.inited&&(null==R||R({ellipsis:j,fold:G,title:xe}))},[R,G,j,xe]),t.useEffect(function(){le.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(s("container"),f),style:re,ref:function(e){n.assignRef(ae,e),o&&n.assignRef(o,e)},onMouseEnter:N,onMouseLeave:T,onPointerEnter:W,onPointerLeave:B,onClick:L,onFocus:P},/*#__PURE__*/l.default.createElement(i.Measure,{offset:!0},function(e){var t=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-le.contentOffsetHeight)>1&&ve(),/*#__PURE__*/l.default.createElement("div",{style:{whiteSpace:S},className:"offset-height-computer",ref:function(e){n.assignRef(t,e),n.assignRef(fe,e),Ce()}},q)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:h?xe:void 0,style:de,ref:se},j&&m&&ge,q))});e.TextEllipsis=f,e.c=s});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("@ohkit/utils"),require("@ohkit/measure")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/utils","@ohkit/measure"],t):t((e||self).textEllipsis={},e.react,e.utils,e.measure)}(this,function(e,t,n,i){function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var l=/*#__PURE__*/o(t);function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},s.apply(null,arguments)}var a=n.prefixClassname("ohkit-text-ellipsis__"),f=t.forwardRef(function(e,o){var f=e.className,r=e.lineHeight,u=void 0===r?"":r,d=e.lines,c=e.maskBgColor,h=void 0===c?"#fff":c,p=e.resetFoldWhenChildrenOrEllipsisChange,v=void 0!==p&&p,g=e.showTitleWhenFold,m=e.titleWhenFold,C=e.showFoldControl,b=void 0===C||C,x=e.foldText,E=void 0===x?"收起":x,w=e.unfoldText,y=void 0===w?"展开":w,F=e.uiType,k=void 0===F?"right":F,S=e.controlPlacement,R=void 0===S?"center":S,H=e.whiteSpace,M=e.renderFoldButton,O=e.onEllipsisChange,W=e.onFoldChange,N=e.onStatusChange,T=e.onMouseEnter,P=e.onMouseLeave,B=e.onPointerEnter,L=e.onPointerLeave,q=e.onClick,j=e.onFocus,A=e.content||e.children,_=t.useState(!1),z=_[0],V=_[1],D=t.useState(!1),G=D[0],I=D[1],J=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:W}),K=J[0],Q=J[1],U=t.useState(1),X=U[0],Y=U[1],Z=t.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),$=Z[0],ee=Z[1],te=t.useState(d),ne=te[0],ie=void 0===ne?0:ne,oe=te[1],le=t.useState(""),se=le[0],ae=le[1],fe=n.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:z,defaultFold:K,fold:K,foldBtnWidth:X,textContent:se,onEllipsisChange:O,onFoldChange:W},["onEllipsisChange","fold","onFoldChange"])[0],re=t.useRef(null),ue=t.useRef(null),de=t.useRef(null),ce=t.useRef(null),he=t.useMemo(function(){return{lineHeight:G?"1.4":u||void 0}},[u,G]),pe=t.useMemo(function(){var e={whiteSpace:H};return z&&ie&&$?s({},e,{minHeight:K?(ie-.2)*$+"px":void 0,WebkitLineClamp:K?ie:void 0,paddingBottom:"bottom"!==k&&K?void 0:$+"px"}):e},[ie,$,z,K,k,H]),ve=t.useMemo(function(){if(K){var e=$,t="right"===k?Math.min(e/X*100,80):60;return{boxSizing:"content-box",height:$+"px",lineHeight:$+"px",paddingTop:"bottom"===k?e+"px":void 0,paddingLeft:"right"===k?e+"px":void 0,background:"linear-gradient(to "+k+", "+h+(4===h.length?"0":"00")+", "+h+" "+t+"%, "+h+" 100%)"}}},[$,h,K,k,X]),ge=t.useCallback(function(){re.current&&(re.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){re.current&&(re.current.style.width="100%")}))},[]),me=t.useCallback(function(e,t){void 0===t&&(t=!fe.fold),fe.fold=t,Q(t)},[]),Ce=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+k,"bottom"===k&&"placement-"+R),style:ve,ref:ce,onClick:me},M?M(K):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},K?y:E))},[ve,K,E,me,M,R,k,y]),be=t.useCallback(function(e,t){void 0===e&&(e=fe.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=fe.ellipsis,l=fe.fold,s=fe.defaultFold;e!==o&&(V(e),fe.ellipsis=e,null==fe.onEllipsisChange||fe.onEllipsisChange(e)),v&&(i||!o&&e)&&l!==s&&me(void 0,s)},[me,A,v]),xe=t.useCallback(function(){var e=ue.current,t=de.current;if(e&&t){fe.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||I(!0))}if($===n||(ee(n),n))if(d)ie!==d&&oe(d),be(fe.contentOffsetHeight>=(d+1)*n-1);else if(fe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ie!==o&&oe(o),be(!0)}else be(!1)}},[d,$,be]);n.useCompatibleEffect(function(){be(fe.ellipsis,{forceResetFold:!0}),xe()},[xe,be]),t.useEffect(function(){if(z&&ce.current){var e=ce.current.offsetWidth;e!==fe.foldBtnWidth&&(fe.foldBtnWidth=e,Y(e))}},[z,y,b]),t.useEffect(function(){n.isSafari&&ge()},[K,ge]);var Ee=t.useCallback(function(){var e,t=(null==(e=ue.current)?void 0:e.textContent)||"";t!==fe.textContent&&(fe.textContent=t,ae(t))},[]),we=t.useMemo(function(){return z&&K?"function"==typeof m?m(se):m||se:void 0},[m,z,K,se]);return t.useEffect(function(){fe.inited&&(null==N||N({ellipsis:z,fold:K,title:we}))},[N,K,z,we]),t.useEffect(function(){fe.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(a("container"),f),style:he,ref:function(e){n.assignRef(de,e),o&&n.assignRef(o,e)},onMouseEnter:T,onMouseLeave:P,onPointerEnter:B,onPointerLeave:L,onClick:q,onFocus:j},/*#__PURE__*/l.default.createElement(i.Measure,{offset:!0},function(e){var t=e.measureRef,i=(e.contentRect.offset||{}).height;return void 0!==i&&Math.abs(i-fe.contentOffsetHeight)>1&&xe(),/*#__PURE__*/l.default.createElement("div",{style:{whiteSpace:H},className:"offset-height-computer",ref:function(e){n.assignRef(t,e),n.assignRef(ue,e),Ee()}},A)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:g?we:void 0,style:pe,ref:re},z&&b&&Ce,A))});e.TextEllipsis=f,e.c=a});
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n 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 if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, finalContent, 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 hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n /**\n * 自定义样式类名,会附加到根元素上\n */\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 当 content or children or ellipsis 变化时,重置 fold 状态 \n * @default false\n */\n resetFoldWhenChildrenOrEllipsisChange?: boolean;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 是否保留换行\n */\n whiteSpace?: React.CSSProperties['whiteSpace'];\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n resetFoldWhenChildrenOrEllipsisChange = false,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n whiteSpace,\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n const finalContent = content || children;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n inited: false, // mounted\n contentOffsetHeight: 0,\n ellipsis,\n defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold\n fold,\n foldBtnWidth,\n textContent,\n onEllipsisChange,\n onFoldChange,\n }, ['onEllipsisChange', 'fold', 'onFoldChange']);\n\n const contentRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const btnWrapperRef = useRef<HTMLDivElement>(null);\n\n const containerStyle = useMemo(() => {\n return {\n lineHeight: getLineHeightFail // 未传入且获取 lineHeight(px) 失败,则设置 default lineHeight: 1.4(em)\n ? \"1.4\" // more brower normal default lineHeight\n : lineHeight ? lineHeight : undefined,\n };\n }, [lineHeight, getLineHeightFail]);\n\n // 容器样式\n const wrapStyle = useMemo(() => {\n const lines = innerLines;\n const commonStyle = {\n whiteSpace\n }; \n if (!ellipsis || !lines || !innerLineHeight) {\n return commonStyle;\n }\n return {\n ...commonStyle,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","commonStyle","_extends","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","_temp","_ref","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
@@ -34,6 +34,11 @@ interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMou
34
34
  * text|ReactNode 与children任传一个
35
35
  */
36
36
  content?: React.ReactNode;
37
+ /**
38
+ * 当 content or children or ellipsis 变化时,重置 fold 状态
39
+ * @default false
40
+ */
41
+ resetFoldWhenChildrenOrEllipsisChange?: boolean;
37
42
  /**
38
43
  * 折叠状态
39
44
  * @default true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohkit/text-ellipsis",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "text ellipsis for react",
5
5
  "keywords": [
6
6
  "text ellipsis"
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@ohkit/measure": "0.0.3-alpha.0",
36
- "@ohkit/utils": "0.0.3-alpha.1"
36
+ "@ohkit/utils": "0.0.3"
37
37
  },
38
38
  "devDependencies": {
39
39
  "react": "^18.3.1",
@@ -43,5 +43,5 @@
43
43
  "react": ">=16.8.0",
44
44
  "react-dom": ">=16.8.0"
45
45
  },
46
- "gitHead": "6343b9ba45c8cb111bf9f55ba4b0837fa8c20b07"
46
+ "gitHead": "5994f58ae88f5d2a80c0208b16859031fb6dad3d"
47
47
  }
package/src/index.tsx CHANGED
@@ -64,6 +64,11 @@ interface ITextEllipsis
64
64
  * text|ReactNode 与children任传一个
65
65
  */
66
66
  content?: React.ReactNode;
67
+ /**
68
+ * 当 content or children or ellipsis 变化时,重置 fold 状态
69
+ * @default false
70
+ */
71
+ resetFoldWhenChildrenOrEllipsisChange?: boolean;
67
72
  /**
68
73
  * 折叠状态
69
74
  * @default true
@@ -135,6 +140,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
135
140
  maskBgColor = "#fff",
136
141
  content,
137
142
  children,
143
+ resetFoldWhenChildrenOrEllipsisChange = false,
138
144
  showTitleWhenFold,
139
145
  titleWhenFold,
140
146
  showFoldControl = true,
@@ -174,6 +180,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
174
180
  inited: false, // mounted
175
181
  contentOffsetHeight: 0,
176
182
  ellipsis,
183
+ defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold
177
184
  fold,
178
185
  foldBtnWidth,
179
186
  textContent,
@@ -197,11 +204,14 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
197
204
  // 容器样式
198
205
  const wrapStyle = useMemo(() => {
199
206
  const lines = innerLines;
207
+ const commonStyle = {
208
+ whiteSpace
209
+ };
200
210
  if (!ellipsis || !lines || !innerLineHeight) {
201
- return;
211
+ return commonStyle;
202
212
  }
203
213
  return {
204
- whiteSpace,
214
+ ...commonStyle,
205
215
  // HACK: 兼容safari 15+ 富文本折叠高度丢失问题
206
216
  minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,
207
217
  WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案
@@ -286,18 +296,24 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
286
296
  ]);
287
297
 
288
298
  // 重置状态
289
- const resetState = useCallback((newEllipsis = runtime.ellipsis) => {
290
- const {ellipsis, fold: preFold} = runtime;
299
+ const resetState = useCallback((newEllipsis = runtime.ellipsis, {
300
+ forceResetFold = false, // 强制重置fold 比如child变化时
301
+ } = {}) => {
302
+ const {ellipsis, fold: preFold, defaultFold} = runtime;
291
303
  if (newEllipsis !== ellipsis) {
292
304
  setEllipsis(newEllipsis);
293
305
  runtime.ellipsis = newEllipsis;
294
306
  runtime.onEllipsisChange?.(newEllipsis);
295
- // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)
296
- if (newEllipsis && !preFold) {
297
- handleFoldChange(undefined, true);
298
- }
299
307
  }
300
- }, [handleFoldChange]);
308
+ // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)
309
+ if (
310
+ resetFoldWhenChildrenOrEllipsisChange
311
+ && (forceResetFold || !ellipsis && newEllipsis)
312
+ && preFold !== defaultFold
313
+ ) {
314
+ handleFoldChange(undefined, defaultFold);
315
+ }
316
+ }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);
301
317
 
302
318
  const calcEllipsis = useCallback(() => {
303
319
  const wrapDom = wrapperRef.current;
@@ -348,12 +364,14 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
348
364
  resetState(false);
349
365
  }
350
366
  }
351
- }, [lines, innerLineHeight, finalContent, resetState]);
367
+ }, [lines, innerLineHeight, resetState]);
352
368
 
353
369
  // 监听内容高度,是否需要折叠
354
370
  // 用useLayoutEffect方式闪屏显示
355
371
  useCompatibleEffect(() => {
356
- resetState();
372
+ resetState(runtime.ellipsis, {
373
+ forceResetFold: true,
374
+ });
357
375
  calcEllipsis();
358
376
  }, [calcEllipsis, resetState]);
359
377