@ohkit/text-ellipsis 0.0.3-alpha.1 → 0.0.4

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.content,H=t.children,b=t.showTitleWhenFold,y=t.titleWhenFold,k=t.showFoldControl,W=void 0===k||k,M=t.foldText,S=void 0===M?"收起":M,B=t.unfoldText,L=void 0===B?"展开":B,N=t.uiType,O=void 0===N?"right":N,P=t.controlPlacement,T=void 0===P?"center":P,q=t.whiteSpace,A=t.renderFoldButton,R=t.onEllipsisChange,_=t.onFoldChange,z=t.onStatusChange,V=t.onMouseEnter,j=t.onMouseLeave,D=t.onPointerEnter,G=t.onPointerLeave,I=t.onClick,J=t.onFocus,K=n(!1),Q=K[0],U=K[1],X=n(!1),Y=X[0],Z=X[1],$=a(t,"fold",{defaultValue:!0,onChange:_}),ee=$[0],te=$[1],ne=n(1),oe=ne[0],ie=ne[1],le=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),re=le[0],fe=le[1],ae=n(x),de=ae[0],se=void 0===de?0:de,ce=ae[1],ue=n(""),he=ue[0],pe=ue[1],ve=d({inited:!1,contentOffsetHeight:0,ellipsis:Q,fold:ee,foldBtnWidth:oe,textContent:he,onEllipsisChange:R,onFoldChange:_},["onEllipsisChange","fold","onFoldChange"])[0],ge=o(null),me=o(null),Ce=o(null),xe=o(null),we=i(function(){return{lineHeight:Y?"1.4":C||void 0}},[C,Y]),Ee=i(function(){if(Q&&se&&re)return{whiteSpace:q,minHeight:ee?(se-.2)*re+"px":void 0,WebkitLineClamp:ee?se:void 0,paddingBottom:"bottom"!==O&&ee?void 0:re+"px"}},[se,re,Q,ee,O,q]),Fe=i(function(){if(ee){var e=re,t="right"===O?Math.min(e/oe*100,80):60;return{boxSizing:"content-box",height:re+"px",lineHeight:re+"px",paddingTop:"bottom"===O?e+"px":void 0,paddingLeft:"right"===O?e+"px":void 0,background:"linear-gradient(to "+O+", "+E+(4===E.length?"0":"00")+", "+E+" "+t+"%, "+E+" 100%)"}}},[re,E,ee,O,oe]),He=l(function(){ge.current&&(ge.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ge.current&&(ge.current.style.width="100%")}))},[]),be=l(function(e,t){void 0===t&&(t=!ve.fold),ve.fold=t,te(t)},[]),ye=i(function(){/*#__PURE__*/return e.createElement("div",{className:s("btn-fold-wrapper","btn-fold-wrapper-"+O,"bottom"===O&&"placement-"+T),style:Fe,ref:xe,onClick:be},A?A(ee):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},ee?L:S))},[Fe,ee,S,be,A,T,O,L]),ke=l(function(e){void 0===e&&(e=ve.ellipsis);var t=ve.fold;e!==ve.ellipsis&&(U(e),ve.ellipsis=e,null==ve.onEllipsisChange||ve.onEllipsisChange(e),e&&!t&&be(void 0,!0))},[be]),We=l(function(){var e=me.current,t=Ce.current;if(e&&t){ve.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||Z(!0))}if(re===n||(fe(n),n))if(x)se!==x&&ce(x),ke(ve.contentOffsetHeight>=(x+1)*n-1);else if(ve.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);se!==i&&ce(i),ke(!0)}else ke(!1)}},[x,re,ke]);c(function(){ke(),We()},[We,ke]),r(function(){if(Q&&xe.current){var e=xe.current.offsetWidth;e!==ve.foldBtnWidth&&(ve.foldBtnWidth=e,ie(e))}},[Q,L,W]),r(function(){u&&He()},[ee,He]);var Me=l(function(){var e,t=(null==(e=me.current)?void 0:e.textContent)||"";t!==ve.textContent&&(ve.textContent=t,pe(t))},[]),Se=F||H,Be=i(function(){return Q&&ee?"function"==typeof y?y(he):y||he:void 0},[y,Q,ee,he]);return r(function(){ve.inited&&(null==z||z({ellipsis:Q,fold:ee,title:Be}))},[z,ee,Q,Be]),r(function(){ve.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:s(v("container"),g),style:we,ref:function(e){h(Ce,e),f&&h(f,e)},onMouseEnter:V,onMouseLeave:j,onPointerEnter:D,onPointerLeave:G,onClick:I,onFocus:J},/*#__PURE__*/e.createElement(p,{offset:!0},function(t){var n=t.measureRef,o=(t.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-ve.contentOffsetHeight)>1&&We(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:q},className:"offset-height-computer",ref:function(e){h(n,e),h(me,e),Me()}},Se)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:b?Be:void 0,style:Ee,ref:ge},Q&&W&&ye,Se))});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 d,useSyncPropsState as f,useRuntime as s,classNames as a,useCompatibleEffect as c,isSafari as u,assignRef as h}from"@ohkit/utils";import{Measure as v}from"@ohkit/measure";var p=d("ohkit-text-ellipsis__"),g=t(function(t,d){var g=t.className,m=t.lineHeight,C=void 0===m?"":m,x=t.lines,F=t.maskBgColor,w=void 0===F?"#fff":F,E=t.resetFoldWhenChildrenOrEllipsisChange,H=void 0!==E&&E,b=t.showTitleWhenFold,y=t.titleWhenFold,k=t.showFoldControl,W=void 0===k||k,M=t.foldText,S=void 0===M?"收起":M,B=t.unfoldText,L=void 0===B?"展开":B,N=t.uiType,O=void 0===N?"right":N,P=t.controlPlacement,T=void 0===P?"center":P,R=t.whiteSpace,q=t.renderFoldButton,A=t.onEllipsisChange,_=t.onFoldChange,z=t.onStatusChange,V=t.onMouseEnter,j=t.onMouseLeave,D=t.onPointerEnter,G=t.onPointerLeave,I=t.onClick,J=t.onFocus,K=t.content||t.children,Q=n(!1),U=Q[0],X=Q[1],Y=n(!1),Z=Y[0],$=Y[1],ee=f(t,"fold",{defaultValue:!0,onChange:_}),te=ee[0],ne=ee[1],oe=n(1),ie=oe[0],le=oe[1],re=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),de=re[0],fe=re[1],se=n(x),ae=se[0],ce=void 0===ae?0:ae,ue=se[1],he=n(""),ve=he[0],pe=he[1],ge=s({inited:!1,contentOffsetHeight:0,ellipsis:U,defaultFold:te,fold:te,foldBtnWidth:ie,textContent:ve,onEllipsisChange:A,onFoldChange:_},["onEllipsisChange","fold","onFoldChange"])[0],me=o(null),Ce=o(null),xe=o(null),Fe=o(null),we=i(function(){return{lineHeight:Z?"1.4":C||void 0}},[C,Z]),Ee=i(function(){if(U&&ce&&de)return{whiteSpace:R,minHeight:te?(ce-.2)*de+"px":void 0,WebkitLineClamp:te?ce:void 0,paddingBottom:"bottom"!==O&&te?void 0:de+"px"}},[ce,de,U,te,O,R]),He=i(function(){if(te){var e=de,t="right"===O?Math.min(e/ie*100,80):60;return{boxSizing:"content-box",height:de+"px",lineHeight:de+"px",paddingTop:"bottom"===O?e+"px":void 0,paddingLeft:"right"===O?e+"px":void 0,background:"linear-gradient(to "+O+", "+w+(4===w.length?"0":"00")+", "+w+" "+t+"%, "+w+" 100%)"}}},[de,w,te,O,ie]),be=l(function(){me.current&&(me.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){me.current&&(me.current.style.width="100%")}))},[]),ye=l(function(e,t){void 0===t&&(t=!ge.fold),ge.fold=t,ne(t)},[]),ke=i(function(){/*#__PURE__*/return e.createElement("div",{className:a("btn-fold-wrapper","btn-fold-wrapper-"+O,"bottom"===O&&"placement-"+T),style:He,ref:Fe,onClick:ye},q?q(te):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},te?L:S))},[He,te,S,ye,q,T,O,L]),We=l(function(e,t){void 0===e&&(e=ge.ellipsis);var n=(void 0===t?{}:t).forceResetFold,o=void 0!==n&&n,i=ge.ellipsis,l=ge.fold,r=ge.defaultFold;e!==i&&(X(e),ge.ellipsis=e,null==ge.onEllipsisChange||ge.onEllipsisChange(e)),H&&(o||!i&&e)&&l!==r&&ye(void 0,r)},[ye,K,H]),Me=l(function(){var e=Ce.current,t=xe.current;if(e&&t){ge.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var o=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;o&&((n=parseFloat(o))||$(!0))}if(de===n||(fe(n),n))if(x)ce!==x&&ue(x),We(ge.contentOffsetHeight>=(x+1)*n-1);else if(ge.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var i=Math.floor(t.offsetHeight/n);ce!==i&&ue(i),We(!0)}else We(!1)}},[x,de,We]);c(function(){We(ge.ellipsis,{forceResetFold:!0}),Me()},[Me,We]),r(function(){if(U&&Fe.current){var e=Fe.current.offsetWidth;e!==ge.foldBtnWidth&&(ge.foldBtnWidth=e,le(e))}},[U,L,W]),r(function(){u&&be()},[te,be]);var Se=l(function(){var e,t=(null==(e=Ce.current)?void 0:e.textContent)||"";t!==ge.textContent&&(ge.textContent=t,pe(t))},[]),Be=i(function(){return U&&te?"function"==typeof y?y(ve):y||ve:void 0},[y,U,te,ve]);return r(function(){ge.inited&&(null==z||z({ellipsis:U,fold:te,title:Be}))},[z,te,U,Be]),r(function(){ge.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:a(p("container"),g),style:we,ref:function(e){h(xe,e),d&&h(d,e)},onMouseEnter:V,onMouseLeave:j,onPointerEnter:D,onPointerLeave:G,onClick:I,onFocus:J},/*#__PURE__*/e.createElement(v,{offset:!0},function(t){var n=t.measureRef,o=(t.contentRect.offset||{}).height;return void 0!==o&&Math.abs(o-ge.contentOffsetHeight)>1&&Me(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:R},className:"offset-height-computer",ref:function(e){h(n,e),h(Ce,e),Se()}},K)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:b?Be:void 0,style:Ee,ref:me},U&&W&&ke,K))});export{g as TextEllipsis,p 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 // 是否截断\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, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 runtime.inited: ', ellipsis, fold, runtime.inited);\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","content","children","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","_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","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","finalContent","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 if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"kTA4Ba,IAAAA,EAAIC,EAAE,yBA0GNC,EAAeC,EAA8C,SAACC,EAAOC,GAChF,IACEC,EAyBEF,EAzBFE,UAASC,EAyBPH,EAxBFI,WAAAA,OAAU,IAAAD,EAAG,GAAEA,EACfE,EAuBEL,EAvBFK,MAAKC,EAuBHN,EAtBFO,YAAAA,OAAc,IAAHD,EAAG,OAAMA,EAEZE,EAoBNR,EAnBFS,sCAAAA,OAAwC,IAAHD,GAAQA,EAC7CE,EAkBEV,EAlBFU,kBACAC,EAiBEX,EAjBFW,cAAaC,EAiBXZ,EAhBFa,gBAAAA,OAAkB,IAAHD,GAAOA,EAAAE,EAgBpBd,EAfFe,SAAAA,OAAW,IAAHD,EAAG,KAAIA,EAAAE,EAebhB,EAdFiB,WAAAA,OAAa,IAAHD,EAAG,KAAIA,EAAAE,EAcflB,EAbFmB,OAAAA,OAAS,IAAHD,EAAG,QAAOA,EAAAE,EAadpB,EAZFqB,iBAAAA,OAAmB,IAAHD,EAAG,SAAQA,EAC3BE,EAWEtB,EAXFsB,WACAC,EAUEvB,EAVFuB,iBACAC,EASExB,EATFwB,iBACAC,EAQEzB,EARFyB,aACAC,EAOE1B,EAPF0B,eACAC,EAME3B,EANF2B,aACAC,EAKE5B,EALF4B,aACAC,EAIE7B,EAJF6B,eACAC,EAGE9B,EAHF8B,eACAC,EAEE/B,EAFF+B,QACAC,EACEhC,EADFgC,QAEIC,EADFjC,EArBFkC,SAqBElC,EApBFmC,SAuBFC,EAAgCC,GAAS,GAAlCC,EAAQF,KAAEG,EAAWH,EAAA,GAC5BI,EAAkDH,GAAS,GAApDI,EAAiBD,EAAA,GAAEE,EAAoBF,EAAA,GAE9CG,GAAwBC,EAAkB5C,EAAO,OAAQ,CAAC6C,cAAc,EAAMC,SAAUrB,IAAjFsB,GAAIJ,GAAA,GAAEK,GAAOL,GAAA,GACpBM,GAAwCZ,EAAS,GAA1Ca,GAAYD,GAAA,GAAEE,GAAeF,GACpC,GAAAG,GAA8Cf,EACtB,iBAAfjC,GAA2BA,EAAWiD,SAAS,MAClDC,WAAWlD,GACX,GAHCmD,GAAeH,GAAEI,GAAAA,GAAkBJ,MAK1CK,GAAwCpB,EAAShC,GAAMqD,GAAAD,GAAhDE,GAAAA,QAAU,IAAAD,GAAG,EAACA,GAAEE,GAAaH,GAEpC,GAAAI,GAAsCxB,EAAS,IAAxCyB,GAAWD,GAAA,GAAEE,GAAcF,GAAA,GAE3BG,GAAWC,EAAW,CAC3BC,QAAQ,EACRC,oBAAqB,EACrB7B,SAAAA,EACA8B,YAAarB,GACbA,KAAAA,GACAG,aAAAA,GACAY,YAAAA,GACAtC,iBAAAA,EACAC,aAAAA,GACC,CAAC,mBAAoB,OAAQ,iBAVlB,GAYR4C,GAAaC,EAAuB,MACpCC,GAAaD,EAAuB,MACpCE,GAAeF,EAAuB,MACtCG,GAAgBH,EAAuB,MAEvCI,GAAiBC,EAAQ,WAC7B,MAAO,CACLvE,WAAYqC,EACR,MACArC,QAA0BwE,EAElC,EAAG,CAACxE,EAAYqC,IAGVoC,GAAYF,EAAQ,WAExB,GAAKrC,GADSqB,IACcJ,GAG5B,MAAO,CACLjC,WAAAA,EAEAwD,UAAW/B,IAPCY,GAOkB,IAAOJ,GAAsBqB,UAAAA,EAC3DG,gBAAiBhC,GARLY,QAQoBiB,EAIhCI,cACa,WAAX7D,GAAwB4B,QAAgC6B,EAAtBrB,GAAsBqB,KAE9D,EAAG,CAACjB,GAAYJ,GAAiBjB,EAAUS,GAAM5B,EAAQG,IAGnD2D,GAAWN,EAAQ,WACvB,GAAK5B,GAAL,CAIA,IAAMmC,EAAU3B,GAEV4B,EAAmB,UAAXhE,EAAqBiE,KAAKC,IAAKH,EAAUhC,GAAgB,IAAK,IAAM,GAKlF,MAAO,CACLoC,UAAW,cACXC,OAAWhC,GAAmB,KAC9BnD,WAAemD,GAAe,KAC9BiC,WAAuB,WAAXrE,EAAyB+D,EAAcN,UAAAA,EACnDa,YAAwB,UAAXtE,EAAwB+D,EAAO,UAAON,EAEnDc,WAAkCvE,sBAAAA,EAAWwE,KAVxBpF,GACE,IAAvBA,EAAYqF,OAAe,IAAM,MAS4BrF,KAAAA,MAAe4E,EAAK,MAAM5E,EAAW,SAhBnG,CAkBH,EAAG,CAACgD,GAAiBhD,EAAawC,GAAM5B,EAAQ+B,KAE1C2C,GAAgBC,EAAY,WAE5BzB,GAAW0B,UACb1B,GAAW0B,QAAQC,MAAMC,MAAQ,UACL,MAA5BC,OAAOC,uBAAPD,OAAOC,sBAAwB,WACzB9B,GAAW0B,UACb1B,GAAW0B,QAAQC,MAAMC,MAAQ,OAErC,GAEJ,EAAG,IAEGG,GAAmBN,EACvB,SAACO,EAAkCtD,QAAAA,IAAAA,IAAAA,GAAQiB,GAAQjB,MACjDiB,GAAQjB,KAAOA,EACfC,GAAQD,EACZ,EAAG,IAEGuD,GAAa3B,EAAQ,wBACzB,OACE4B,EAAAC,cACEtG,MAAAA,CAAAA,UAAWuG,EACT,mBACoBtF,oBAAAA,EACT,WAAXA,GAAoCE,aAAAA,GAEtC2E,MAAOf,GACPhF,IAAKwE,GACL1C,QAASqE,IAER7E,EACCA,EAAiBwB,iBAEjBwD,EAAAC,cAAKtG,MAAAA,CAAAA,UAAW,YAAa6C,GAAO9B,EAAaF,GAIzD,EAAG,CACDkE,GACAlC,GACAhC,EACAqF,GACA7E,EACAF,EACAF,EACAF,IAIIyF,GAAaZ,EAAY,SAACa,EAAWC,YAAXD,IAAAA,EAAc3C,GAAQ1B,UAAQ,IAExDuE,QAAF,IAF0DD,EAE1D,CAAA,EAAEA,GADJE,eAAAA,OAAc,IAAAD,GAAQA,EAEfvE,EAAwC0B,GAAxC1B,SAAgByE,EAAwB/C,GAA9BjB,KAAeqB,EAAeJ,GAAfI,YAC5BuC,IAAgBrE,IAClBC,EAAYoE,GACZ3C,GAAQ1B,SAAWqE,QACnB3C,GAAQxC,kBAARwC,GAAQxC,iBAAmBmF,IAI3BlG,IACIqG,IAAoBxE,GAAYqE,IACjCI,IAAY3C,GAEfgC,QAAiBxB,EAAWR,EAEhC,EAAG,CAACgC,GAAkBnE,EAAcxB,IAE9BuG,GAAelB,EAAY,WAC/B,IAAMmB,EAAU1C,GAAWwB,QACrBmB,EAAe1C,GAAauB,QAClC,GAAKkB,GAAYC,EAAjB,CAGAlD,GAAQG,oBAAsB8C,EAAQE,aACtC,IAAIC,EAAiB,EAGrB,IAAKA,GAAkBH,EAAS,CAC9B,IACQ7G,IADiC,MAAvB8F,OAAOmB,sBAAgB,EAAvBnB,OAAOmB,iBAAmBJ,KACR,CAAA,GAA5B7G,WACJA,KAEFgH,EAAiB9D,WAAWlD,KAE1BsC,GAAqB,GAG1B,CAED,GAAIa,KAAoB6D,IACtB5D,GAAmB4D,GACdA,GAIP,GAAK/G,EAWCsD,KAAetD,GACjBuD,GAAcvD,GAIdqG,GADE1C,GAAQG,sBAAwB9D,EAAQ,GAAK+G,EAAiB,QAdlE,GAAIpD,GAAQG,qBAAkC,MAAZ+C,OAAY,EAAZA,EAAcC,cAAc,CAC1D,IAAMG,EAAclC,KAAKmC,MAAML,EAAaC,aAAeC,GACvDzD,KAAe2D,GACjB1D,GAAc0D,GAEhBZ,IAAW,EACd,MACCA,IAAW,EA/Bd,CA4CH,EAAG,CAACrG,EAAOkD,GAAiBmD,KAI5Bc,EAAoB,WAClBd,GAAW1C,GAAQ1B,SAAU,CAC3BwE,gBAAgB,IAElBE,IACF,EAAG,CAACA,GAAcN,KAGlBe,EAAU,WACR,GAAInF,GAAYmC,GAAcsB,QAAS,CACrC2B,IAAOC,EAA6BlD,GAAcsB,QAA3C4B,YACHA,IAAgB3D,GAAQd,eAC1Bc,GAAQd,aAAeyE,EACvBxE,GAAgBwE,GAEnB,CACH,EAAG,CAACrF,EAAUrB,EAAYJ,IAC1B4G,EAAU,WACJG,GACF/B,IAEJ,EAAG,CAAC9C,GAAM8C,KACV,IAAMgC,GAAoB/B,EAAY,WAAK,IAAAgC,EACnCC,GAAiBD,OAAAA,EAAAvD,GAAWwB,cAAX+B,EAAAA,EAAoBhE,cAAe,GACtDiE,IAAmB/D,GAAQF,cAC7BE,GAAQF,YAAciE,EACtBhE,GAAegE,GAEnB,EAAG,IACGC,GAAarD,EAAQ,WACvB,OAAOrC,GAAYS,GACW,mBAAlBpC,EACNA,EAAcmD,IACdnD,GAAiBmD,QACnBc,CACR,EAAG,CAACjE,EAAe2B,EAAUS,GAAMe,KAcnC,OAbA2D,EAAU,WACJzD,GAAQE,SACI,MAAdxC,GAAAA,EAAiB,CACbY,SAAAA,EACAS,KAAAA,GACAkF,MAAOD,KAGf,EAAG,CAACtG,EAAgBqB,GAAMT,EAAU0F,KACpCP,EAAU,WACRzD,GAAQE,QAAS,CACnB,EAAG,iBAGDqC,EAAAC,cACEtG,MAAAA,CAAAA,UAAWuG,EAAG7G,EAAE,aAAcM,GAC9B8F,MAAOtB,GACPzE,IAAK,SAACiI,GACJC,EAAU3D,GAAc0D,GACxBjI,GAAOkI,EAAUlI,EAAKiI,EACxB,EACAvG,aAAcA,EACdC,aAAcA,EACdC,eAAgBA,EAChBC,eAAgBA,EAChBC,QAASA,EACTC,QAASA,gBAGTuE,EAAAC,cAAC4B,EAAQC,CAAAA,QACN,GAAA,SAAAC,GAA8B,IAA5BC,EAAUD,EAAVC,WAEMhD,GAFiB+C,EAAXE,YAEgBH,QAAU,CAAE,GAAlC9C,OAIP,YAHeX,IAAXW,GAAwBH,KAAKqD,IAAIlD,EAASvB,GAAQG,qBAAuB,GAC3E6C,kBAEKT,EAAAC,cAAKR,MAAAA,CAAAA,MAAO,CAAC1E,WAAAA,GAAapB,UAAW,yBAA0BD,IAAK,SAACiI,GAC1EC,EAAUI,EAAYL,GACtBC,EAAU5D,GAAY2D,GACtBL,IACF,GACG5F,EAEL,gBAMFsE,EAAAC,cACEtG,MAAAA,CAAAA,UAAW,sBACX+H,MAAOvH,EAAoBsH,QAAapD,EACxCoB,MAAOnB,GACP5E,IAAKoE,IAIJ/B,GAAYzB,GAAmByF,GAC/BrE,GAIT"}
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.content,p=i.children,v=i.showTitleWhenFold,g=i.titleWhenFold,m=i.showFoldControl,C=void 0===m||m,x=i.foldText,E=void 0===x?"收起":x,b=i.unfoldText,w=void 0===b?"展开":b,S=i.uiType,k=void 0===S?"right":S,y=i.controlPlacement,F=void 0===y?"center":y,H=i.whiteSpace,M=i.renderFoldButton,R=i.onEllipsisChange,N=i.onFoldChange,W=i.onStatusChange,B=i.onMouseEnter,L=i.onMouseLeave,P=i.onPointerEnter,T=i.onPointerLeave,q=i.onClick,O=i.onFocus,A=e.useState(!1),_=A[0],j=A[1],z=e.useState(!1),V=z[0],D=z[1],G=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:N}),I=G[0],J=G[1],K=e.useState(1),Q=K[0],U=K[1],X=e.useState("string"==typeof r&&r.endsWith("px")?parseFloat(r):0),Y=X[0],Z=X[1],$=e.useState(u),ee=$[0],te=void 0===ee?0:ee,ne=$[1],ie=e.useState(""),oe=ie[0],le=ie[1],se=t.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:_,fold:I,foldBtnWidth:Q,textContent:oe,onEllipsisChange:R,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"])[0],ae=e.useRef(null),fe=e.useRef(null),re=e.useRef(null),ue=e.useRef(null),de=e.useMemo(function(){return{lineHeight:V?"1.4":r||void 0}},[r,V]),ce=e.useMemo(function(){if(_&&te&&Y)return{whiteSpace:H,minHeight:I?(te-.2)*Y+"px":void 0,WebkitLineClamp:I?te:void 0,paddingBottom:"bottom"!==k&&I?void 0:Y+"px"}},[te,Y,_,I,k,H]),he=e.useMemo(function(){if(I){var e=Y,t="right"===k?Math.min(e/Q*100,80):60;return{boxSizing:"content-box",height:Y+"px",lineHeight:Y+"px",paddingTop:"bottom"===k?e+"px":void 0,paddingLeft:"right"===k?e+"px":void 0,background:"linear-gradient(to "+k+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[Y,c,I,k,Q]),pe=e.useCallback(function(){ae.current&&(ae.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ae.current&&(ae.current.style.width="100%")}))},[]),ve=e.useCallback(function(e,t){void 0===t&&(t=!se.fold),se.fold=t,J(t)},[]),ge=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+k,"bottom"===k&&"placement-"+F),style:he,ref:ue,onClick:ve},M?M(I):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},I?w:E))},[he,I,E,ve,M,F,k,w]),me=e.useCallback(function(e){void 0===e&&(e=se.ellipsis);var t=se.fold;e!==se.ellipsis&&(j(e),se.ellipsis=e,null==se.onEllipsisChange||se.onEllipsisChange(e),e&&!t&&ve(void 0,!0))},[ve]),Ce=e.useCallback(function(){var e=fe.current,t=re.current;if(e&&t){se.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||D(!0))}if(Y===n||(Z(n),n))if(u)te!==u&&ne(u),me(se.contentOffsetHeight>=(u+1)*n-1);else if(se.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);te!==o&&ne(o),me(!0)}else me(!1)}},[u,Y,me]);t.useCompatibleEffect(function(){me(),Ce()},[Ce,me]),e.useEffect(function(){if(_&&ue.current){var e=ue.current.offsetWidth;e!==se.foldBtnWidth&&(se.foldBtnWidth=e,U(e))}},[_,w,C]),e.useEffect(function(){t.isSafari&&pe()},[I,pe]);var xe=e.useCallback(function(){var e,t=(null==(e=fe.current)?void 0:e.textContent)||"";t!==se.textContent&&(se.textContent=t,le(t))},[]),Ee=h||p,be=e.useMemo(function(){return _&&I?"function"==typeof g?g(oe):g||oe:void 0},[g,_,I,oe]);return e.useEffect(function(){se.inited&&(null==W||W({ellipsis:_,fold:I,title:be}))},[W,I,_,be]),e.useEffect(function(){se.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(l("container"),a),style:de,ref:function(e){t.assignRef(re,e),s&&t.assignRef(s,e)},onMouseEnter:B,onMouseLeave:L,onPointerEnter:P,onPointerLeave:T,onClick:q,onFocus:O},/*#__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-se.contentOffsetHeight)>1&&Ce(),/*#__PURE__*/o.default.createElement("div",{style:{whiteSpace:H},className:"offset-height-computer",ref:function(e){t.assignRef(n,e),t.assignRef(fe,e),xe()}},Ee)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:v?be:void 0,style:ce,ref:ae},_&&C&&ge,Ee))});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),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.resetFoldWhenChildrenOrEllipsisChange,p=void 0!==h&&h,v=i.showTitleWhenFold,g=i.titleWhenFold,m=i.showFoldControl,C=void 0===m||m,E=i.foldText,x=void 0===E?"收起":E,b=i.unfoldText,w=void 0===b?"展开":b,F=i.uiType,S=void 0===F?"right":F,k=i.controlPlacement,R=void 0===k?"center":k,y=i.whiteSpace,H=i.renderFoldButton,M=i.onEllipsisChange,W=i.onFoldChange,N=i.onStatusChange,B=i.onMouseEnter,L=i.onMouseLeave,O=i.onPointerEnter,P=i.onPointerLeave,T=i.onClick,q=i.onFocus,A=i.content||i.children,_=e.useState(!1),j=_[0],z=_[1],V=e.useState(!1),D=V[0],G=V[1],I=t.useSyncPropsState(i,"fold",{defaultValue:!0,onChange:W}),J=I[0],K=I[1],Q=e.useState(1),U=Q[0],X=Q[1],Y=e.useState("string"==typeof r&&r.endsWith("px")?parseFloat(r):0),Z=Y[0],$=Y[1],ee=e.useState(u),te=ee[0],ne=void 0===te?0:te,ie=ee[1],oe=e.useState(""),le=oe[0],se=oe[1],ae=t.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:j,defaultFold:J,fold:J,foldBtnWidth:U,textContent:le,onEllipsisChange:M,onFoldChange:W},["onEllipsisChange","fold","onFoldChange"])[0],fe=e.useRef(null),re=e.useRef(null),ue=e.useRef(null),de=e.useRef(null),ce=e.useMemo(function(){return{lineHeight:D?"1.4":r||void 0}},[r,D]),he=e.useMemo(function(){if(j&&ne&&Z)return{whiteSpace:y,minHeight:J?(ne-.2)*Z+"px":void 0,WebkitLineClamp:J?ne:void 0,paddingBottom:"bottom"!==S&&J?void 0:Z+"px"}},[ne,Z,j,J,S,y]),pe=e.useMemo(function(){if(J){var e=Z,t="right"===S?Math.min(e/U*100,80):60;return{boxSizing:"content-box",height:Z+"px",lineHeight:Z+"px",paddingTop:"bottom"===S?e+"px":void 0,paddingLeft:"right"===S?e+"px":void 0,background:"linear-gradient(to "+S+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[Z,c,J,S,U]),ve=e.useCallback(function(){fe.current&&(fe.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){fe.current&&(fe.current.style.width="100%")}))},[]),ge=e.useCallback(function(e,t){void 0===t&&(t=!ae.fold),ae.fold=t,K(t)},[]),me=e.useMemo(function(){/*#__PURE__*/return o.default.createElement("div",{className:t.classNames("btn-fold-wrapper","btn-fold-wrapper-"+S,"bottom"===S&&"placement-"+R),style:pe,ref:de,onClick:ge},H?H(J):/*#__PURE__*/o.default.createElement("div",{className:"btn-fold"},J?w:x))},[pe,J,x,ge,H,R,S,w]),Ce=e.useCallback(function(e,t){void 0===e&&(e=ae.ellipsis);var n=(void 0===t?{}:t).forceResetFold,i=void 0!==n&&n,o=ae.ellipsis,l=ae.fold,s=ae.defaultFold;e!==o&&(z(e),ae.ellipsis=e,null==ae.onEllipsisChange||ae.onEllipsisChange(e)),p&&(i||!o&&e)&&l!==s&&ge(void 0,s)},[ge,A,p]),Ee=e.useCallback(function(){var e=re.current,t=ue.current;if(e&&t){ae.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||G(!0))}if(Z===n||($(n),n))if(u)ne!==u&&ie(u),Ce(ae.contentOffsetHeight>=(u+1)*n-1);else if(ae.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ne!==o&&ie(o),Ce(!0)}else Ce(!1)}},[u,Z,Ce]);t.useCompatibleEffect(function(){Ce(ae.ellipsis,{forceResetFold:!0}),Ee()},[Ee,Ce]),e.useEffect(function(){if(j&&de.current){var e=de.current.offsetWidth;e!==ae.foldBtnWidth&&(ae.foldBtnWidth=e,X(e))}},[j,w,C]),e.useEffect(function(){t.isSafari&&ve()},[J,ve]);var xe=e.useCallback(function(){var e,t=(null==(e=re.current)?void 0:e.textContent)||"";t!==ae.textContent&&(ae.textContent=t,se(t))},[]),be=e.useMemo(function(){return j&&J?"function"==typeof g?g(le):g||le:void 0},[g,j,J,le]);return e.useEffect(function(){ae.inited&&(null==N||N({ellipsis:j,fold:J,title:be}))},[N,J,j,be]),e.useEffect(function(){ae.inited=!0},[]),/*#__PURE__*/o.default.createElement("div",{className:t.classNames(l("container"),a),style:ce,ref:function(e){t.assignRef(ue,e),s&&t.assignRef(s,e)},onMouseEnter:B,onMouseLeave:L,onPointerEnter:O,onPointerLeave:P,onClick:T,onFocus:q},/*#__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-ae.contentOffsetHeight)>1&&Ee(),/*#__PURE__*/o.default.createElement("div",{style:{whiteSpace:y},className:"offset-height-computer",ref:function(e){t.assignRef(n,e),t.assignRef(re,e),xe()}},A)}),/*#__PURE__*/o.default.createElement("div",{className:"text-ellipsis-inner",title:v?be:void 0,style:he,ref:fe},j&&C&&me,A))});exports.TextEllipsis=s,exports.c=l;
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n 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 // 是否截断\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, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 runtime.inited: ', ellipsis, fold, runtime.inited);\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","content","children","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","_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","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","finalContent","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 if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"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"}
@@ -1,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,[_,z]=n(!1),[V,j]=n(!1),[D,G]=a(t,"fold",{defaultValue:!0,onChange:L}),[I,J]=n(1),[K,Q]=n("string"==typeof v&&v.endsWith("px")?parseFloat(v):0),[U=0,X]=n(C),[Y,Z]=n(""),[ee]=d({inited:!1,contentOffsetHeight:0,ellipsis:_,fold:D,foldBtnWidth:I,textContent:Y,onEllipsisChange:B,onFoldChange:L},["onEllipsisChange","fold","onFoldChange"]),te=o(null),ne=o(null),oe=o(null),ie=o(null),le=i(()=>({lineHeight:V?"1.4":v||void 0}),[v,V]),se=i(()=>{if(_&&U&&K)return{whiteSpace:M,minHeight:D?(U-.2)*K+"px":void 0,WebkitLineClamp:D?U:void 0,paddingBottom:"bottom"!==k&&D?void 0:`${K}px`}},[U,K,_,D,k,M]),re=i(()=>{if(!D)return;const e=K,t="right"===k?Math.min(e/I*100,80):60;return{boxSizing:"content-box",height:`${K}px`,lineHeight:`${K}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%)`}},[K,x,D,k,I]),ae=l(()=>{te.current&&(te.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{te.current&&(te.current.style.width="100%")}))},[]),de=l((e,t=!ee.fold)=>{ee.fold=t,G(t)},[]),fe=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${k}`,"bottom"===k&&`placement-${W}`),style:re,ref:ie,onClick:de},S?S(D):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},D?y:b)),[re,D,b,de,S,W,k,y]),ce=l((e=ee.ellipsis)=>{const{ellipsis:t,fold:n}=ee;e!==t&&(z(e),ee.ellipsis=e,null==ee.onEllipsisChange||ee.onEllipsisChange(e),e&&!n&&de(void 0,!0))},[de]),he=l(()=>{const e=ne.current,t=oe.current;if(!e||!t)return;ee.contentOffsetHeight=e.offsetHeight;let n=0;if(!n&&e){const t=null==window.getComputedStyle?void 0:window.getComputedStyle(e),{lineHeight:o}=t||{};o&&(n=parseFloat(o),n||j(!0))}if(K===n||(Q(n),n))if(C)U!==C&&X(C),ce(ee.contentOffsetHeight>=(C+1)*n-1);else if(ee.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);U!==e&&X(e),ce(!0)}else ce(!1)},[C,K,ce]);c(()=>{ce(),he()},[he,ce]),s(()=>{if(_&&ie.current){const{offsetWidth:e}=ie.current;e!==ee.foldBtnWidth&&(ee.foldBtnWidth=e,J(e))}},[_,y,$]),s(()=>{h&&ae()},[D,ae]);const ue=l(()=>{var e;const t=(null==(e=ne.current)?void 0:e.textContent)||"";t!==ee.textContent&&(ee.textContent=t,Z(t))},[]),pe=w||E,ge=i(()=>_&&D?"function"==typeof H?H(Y):H||Y:void 0,[H,_,D,Y]);return s(()=>{ee.inited&&(null==N||N({ellipsis:_,fold:D,title:ge}))},[N,D,_,ge]),s(()=>{ee.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:f(g("container"),m),style:le,ref:e=>{u(oe,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-ee.contentOffsetHeight)>1&&he(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:M},className:"offset-height-computer",ref:e=>{u(t,e),u(ne,e),ue()}},pe)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:F?ge:void 0,style:se,ref:te},_&&$&&fe,pe))});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 s}from"react";import{prefixClassname as r,useSyncPropsState as d,useRuntime as a,classNames as f,useCompatibleEffect as c,isSafari as h,assignRef as u}from"@ohkit/utils";import{Measure as p}from"@ohkit/measure";const g=r("ohkit-text-ellipsis__"),m=t((t,r)=>{const{className:m,lineHeight:C="",lines:v,maskBgColor:x="#fff",content:F,children:w,resetFoldWhenChildrenOrEllipsisChange:E=!1,showTitleWhenFold:H,titleWhenFold:$,showFoldControl:b=!0,foldText:y="收起",unfoldText:k="展开",uiType:W="right",controlPlacement:M="center",whiteSpace:S,renderFoldButton:B,onEllipsisChange:L,onFoldChange:N,onStatusChange:O,onMouseEnter:P,onMouseLeave:T,onPointerEnter:R,onPointerLeave:q,onClick:A,onFocus:_}=t,z=F||w,[V,j]=n(!1),[D,G]=n(!1),[I,J]=d(t,"fold",{defaultValue:!0,onChange:N}),[K,Q]=n(1),[U,X]=n("string"==typeof C&&C.endsWith("px")?parseFloat(C):0),[Y=0,Z]=n(v),[ee,te]=n(""),[ne]=a({inited:!1,contentOffsetHeight:0,ellipsis:V,defaultFold:I,fold:I,foldBtnWidth:K,textContent:ee,onEllipsisChange:L,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"]),oe=o(null),ie=o(null),le=o(null),se=o(null),re=i(()=>({lineHeight:D?"1.4":C||void 0}),[C,D]),de=i(()=>{if(V&&Y&&U)return{whiteSpace:S,minHeight:I?(Y-.2)*U+"px":void 0,WebkitLineClamp:I?Y:void 0,paddingBottom:"bottom"!==W&&I?void 0:`${U}px`}},[Y,U,V,I,W,S]),ae=i(()=>{if(!I)return;const e=U,t="right"===W?Math.min(e/K*100,80):60;return{boxSizing:"content-box",height:`${U}px`,lineHeight:`${U}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%)`}},[U,x,I,W,K]),fe=l(()=>{oe.current&&(oe.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(()=>{oe.current&&(oe.current.style.width="100%")}))},[]),ce=l((e,t=!ne.fold)=>{ne.fold=t,J(t)},[]),he=i(()=>/*#__PURE__*/e.createElement("div",{className:f("btn-fold-wrapper",`btn-fold-wrapper-${W}`,"bottom"===W&&`placement-${M}`),style:ae,ref:se,onClick:ce},B?B(I):/*#__PURE__*/e.createElement("div",{className:"btn-fold"},I?k:y)),[ae,I,y,ce,B,M,W,k]),ue=l((e=ne.ellipsis,{forceResetFold:t=!1}={})=>{const{ellipsis:n,fold:o,defaultFold:i}=ne;e!==n&&(j(e),ne.ellipsis=e,null==ne.onEllipsisChange||ne.onEllipsisChange(e)),E&&(t||!n&&e)&&o!==i&&ce(void 0,i)},[ce,z,E]),pe=l(()=>{const e=ie.current,t=le.current;if(!e||!t)return;ne.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||G(!0))}if(U===n||(X(n),n))if(v)Y!==v&&Z(v),ue(ne.contentOffsetHeight>=(v+1)*n-1);else if(ne.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){const e=Math.floor(t.offsetHeight/n);Y!==e&&Z(e),ue(!0)}else ue(!1)},[v,U,ue]);c(()=>{ue(ne.ellipsis,{forceResetFold:!0}),pe()},[pe,ue]),s(()=>{if(V&&se.current){const{offsetWidth:e}=se.current;e!==ne.foldBtnWidth&&(ne.foldBtnWidth=e,Q(e))}},[V,k,b]),s(()=>{h&&fe()},[I,fe]);const ge=l(()=>{var e;const t=(null==(e=ie.current)?void 0:e.textContent)||"";t!==ne.textContent&&(ne.textContent=t,te(t))},[]),me=i(()=>V&&I?"function"==typeof $?$(ee):$||ee:void 0,[$,V,I,ee]);return s(()=>{ne.inited&&(null==O||O({ellipsis:V,fold:I,title:me}))},[O,I,V,me]),s(()=>{ne.inited=!0},[]),/*#__PURE__*/e.createElement("div",{className:f(g("container"),m),style:re,ref:e=>{u(le,e),r&&u(r,e)},onMouseEnter:P,onMouseLeave:T,onPointerEnter:R,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-ne.contentOffsetHeight)>1&&pe(),/*#__PURE__*/e.createElement("div",{style:{whiteSpace:S},className:"offset-height-computer",ref:e=>{u(t,e),u(ie,e),ge()}},z)}),/*#__PURE__*/e.createElement("div",{className:"text-ellipsis-inner",title:H?me:void 0,style:de,ref:oe},V&&b&&he,z))});export{m as TextEllipsis,g as c};
2
2
  //# sourceMappingURL=index.modern.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n 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 // 是否截断\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, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 runtime.inited: ', ellipsis, fold, runtime.inited);\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","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","finalContent","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 if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","lineHeight","lines","maskBgColor","content","children","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","r","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"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"}
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.content,p=e.children,g=e.showTitleWhenFold,m=e.titleWhenFold,v=e.showFoldControl,C=void 0===v||v,x=e.foldText,E=void 0===x?"收起":x,b=e.unfoldText,w=void 0===b?"展开":b,y=e.uiType,k=void 0===y?"right":y,S=e.controlPlacement,F=void 0===S?"center":S,H=e.whiteSpace,M=e.renderFoldButton,R=e.onEllipsisChange,N=e.onFoldChange,T=e.onStatusChange,W=e.onMouseEnter,B=e.onMouseLeave,L=e.onPointerEnter,P=e.onPointerLeave,q=e.onClick,O=e.onFocus,j=t.useState(!1),A=j[0],_=j[1],z=t.useState(!1),V=z[0],D=z[1],G=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:N}),I=G[0],J=G[1],K=t.useState(1),Q=K[0],U=K[1],X=t.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),Y=X[0],Z=X[1],$=t.useState(r),ee=$[0],te=void 0===ee?0:ee,ne=$[1],ie=t.useState(""),oe=ie[0],le=ie[1],se=n.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:A,fold:I,foldBtnWidth:Q,textContent:oe,onEllipsisChange:R,onFoldChange:N},["onEllipsisChange","fold","onFoldChange"])[0],fe=t.useRef(null),ae=t.useRef(null),ue=t.useRef(null),re=t.useRef(null),de=t.useMemo(function(){return{lineHeight:V?"1.4":u||void 0}},[u,V]),ce=t.useMemo(function(){if(A&&te&&Y)return{whiteSpace:H,minHeight:I?(te-.2)*Y+"px":void 0,WebkitLineClamp:I?te:void 0,paddingBottom:"bottom"!==k&&I?void 0:Y+"px"}},[te,Y,A,I,k,H]),he=t.useMemo(function(){if(I){var e=Y,t="right"===k?Math.min(e/Q*100,80):60;return{boxSizing:"content-box",height:Y+"px",lineHeight:Y+"px",paddingTop:"bottom"===k?e+"px":void 0,paddingLeft:"right"===k?e+"px":void 0,background:"linear-gradient(to "+k+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[Y,c,I,k,Q]),pe=t.useCallback(function(){fe.current&&(fe.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){fe.current&&(fe.current.style.width="100%")}))},[]),ge=t.useCallback(function(e,t){void 0===t&&(t=!se.fold),se.fold=t,J(t)},[]),me=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+k,"bottom"===k&&"placement-"+F),style:he,ref:re,onClick:ge},M?M(I):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},I?w:E))},[he,I,E,ge,M,F,k,w]),ve=t.useCallback(function(e){void 0===e&&(e=se.ellipsis);var t=se.fold;e!==se.ellipsis&&(_(e),se.ellipsis=e,null==se.onEllipsisChange||se.onEllipsisChange(e),e&&!t&&ge(void 0,!0))},[ge]),Ce=t.useCallback(function(){var e=ae.current,t=ue.current;if(e&&t){se.contentOffsetHeight=e.offsetHeight;var n=0;if(!n&&e){var i=((null==window.getComputedStyle?void 0:window.getComputedStyle(e))||{}).lineHeight;i&&((n=parseFloat(i))||D(!0))}if(Y===n||(Z(n),n))if(r)te!==r&&ne(r),ve(se.contentOffsetHeight>=(r+1)*n-1);else if(se.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);te!==o&&ne(o),ve(!0)}else ve(!1)}},[r,Y,ve]);n.useCompatibleEffect(function(){ve(),Ce()},[Ce,ve]),t.useEffect(function(){if(A&&re.current){var e=re.current.offsetWidth;e!==se.foldBtnWidth&&(se.foldBtnWidth=e,U(e))}},[A,w,C]),t.useEffect(function(){n.isSafari&&pe()},[I,pe]);var xe=t.useCallback(function(){var e,t=(null==(e=ae.current)?void 0:e.textContent)||"";t!==se.textContent&&(se.textContent=t,le(t))},[]),Ee=h||p,be=t.useMemo(function(){return A&&I?"function"==typeof m?m(oe):m||oe:void 0},[m,A,I,oe]);return t.useEffect(function(){se.inited&&(null==T||T({ellipsis:A,fold:I,title:be}))},[T,I,A,be]),t.useEffect(function(){se.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(s("container"),f),style:de,ref:function(e){n.assignRef(ue,e),o&&n.assignRef(o,e)},onMouseEnter:W,onMouseLeave:B,onPointerEnter:L,onPointerLeave:P,onClick:q,onFocus:O},/*#__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-se.contentOffsetHeight)>1&&Ce(),/*#__PURE__*/l.default.createElement("div",{style:{whiteSpace:H},className:"offset-height-computer",ref:function(e){n.assignRef(t,e),n.assignRef(ae,e),xe()}},Ee)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:g?be:void 0,style:ce,ref:fe},A&&C&&me,Ee))});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),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.resetFoldWhenChildrenOrEllipsisChange,p=void 0!==h&&h,g=e.showTitleWhenFold,v=e.titleWhenFold,m=e.showFoldControl,C=void 0===m||m,x=e.foldText,E=void 0===x?"收起":x,b=e.unfoldText,w=void 0===b?"展开":b,F=e.uiType,y=void 0===F?"right":F,k=e.controlPlacement,S=void 0===k?"center":k,R=e.whiteSpace,H=e.renderFoldButton,M=e.onEllipsisChange,W=e.onFoldChange,N=e.onStatusChange,T=e.onMouseEnter,B=e.onMouseLeave,L=e.onPointerEnter,O=e.onPointerLeave,P=e.onClick,q=e.onFocus,j=e.content||e.children,A=t.useState(!1),_=A[0],z=A[1],V=t.useState(!1),D=V[0],G=V[1],I=n.useSyncPropsState(e,"fold",{defaultValue:!0,onChange:W}),J=I[0],K=I[1],Q=t.useState(1),U=Q[0],X=Q[1],Y=t.useState("string"==typeof u&&u.endsWith("px")?parseFloat(u):0),Z=Y[0],$=Y[1],ee=t.useState(r),te=ee[0],ne=void 0===te?0:te,ie=ee[1],oe=t.useState(""),le=oe[0],se=oe[1],fe=n.useRuntime({inited:!1,contentOffsetHeight:0,ellipsis:_,defaultFold:J,fold:J,foldBtnWidth:U,textContent:le,onEllipsisChange:M,onFoldChange:W},["onEllipsisChange","fold","onFoldChange"])[0],ae=t.useRef(null),ue=t.useRef(null),re=t.useRef(null),de=t.useRef(null),ce=t.useMemo(function(){return{lineHeight:D?"1.4":u||void 0}},[u,D]),he=t.useMemo(function(){if(_&&ne&&Z)return{whiteSpace:R,minHeight:J?(ne-.2)*Z+"px":void 0,WebkitLineClamp:J?ne:void 0,paddingBottom:"bottom"!==y&&J?void 0:Z+"px"}},[ne,Z,_,J,y,R]),pe=t.useMemo(function(){if(J){var e=Z,t="right"===y?Math.min(e/U*100,80):60;return{boxSizing:"content-box",height:Z+"px",lineHeight:Z+"px",paddingTop:"bottom"===y?e+"px":void 0,paddingLeft:"right"===y?e+"px":void 0,background:"linear-gradient(to "+y+", "+c+(4===c.length?"0":"00")+", "+c+" "+t+"%, "+c+" 100%)"}}},[Z,c,J,y,U]),ge=t.useCallback(function(){ae.current&&(ae.current.style.width="99.999%",null==window.requestAnimationFrame||window.requestAnimationFrame(function(){ae.current&&(ae.current.style.width="100%")}))},[]),ve=t.useCallback(function(e,t){void 0===t&&(t=!fe.fold),fe.fold=t,K(t)},[]),me=t.useMemo(function(){/*#__PURE__*/return l.default.createElement("div",{className:n.classNames("btn-fold-wrapper","btn-fold-wrapper-"+y,"bottom"===y&&"placement-"+S),style:pe,ref:de,onClick:ve},H?H(J):/*#__PURE__*/l.default.createElement("div",{className:"btn-fold"},J?w:E))},[pe,J,E,ve,H,S,y,w]),Ce=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&&(z(e),fe.ellipsis=e,null==fe.onEllipsisChange||fe.onEllipsisChange(e)),p&&(i||!o&&e)&&l!==s&&ve(void 0,s)},[ve,j,p]),xe=t.useCallback(function(){var e=ue.current,t=re.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))||G(!0))}if(Z===n||($(n),n))if(r)ne!==r&&ie(r),Ce(fe.contentOffsetHeight>=(r+1)*n-1);else if(fe.contentOffsetHeight>(null==t?void 0:t.offsetHeight)){var o=Math.floor(t.offsetHeight/n);ne!==o&&ie(o),Ce(!0)}else Ce(!1)}},[r,Z,Ce]);n.useCompatibleEffect(function(){Ce(fe.ellipsis,{forceResetFold:!0}),xe()},[xe,Ce]),t.useEffect(function(){if(_&&de.current){var e=de.current.offsetWidth;e!==fe.foldBtnWidth&&(fe.foldBtnWidth=e,X(e))}},[_,w,C]),t.useEffect(function(){n.isSafari&&ge()},[J,ge]);var Ee=t.useCallback(function(){var e,t=(null==(e=ue.current)?void 0:e.textContent)||"";t!==fe.textContent&&(fe.textContent=t,se(t))},[]),be=t.useMemo(function(){return _&&J?"function"==typeof v?v(le):v||le:void 0},[v,_,J,le]);return t.useEffect(function(){fe.inited&&(null==N||N({ellipsis:_,fold:J,title:be}))},[N,J,_,be]),t.useEffect(function(){fe.inited=!0},[]),/*#__PURE__*/l.default.createElement("div",{className:n.classNames(s("container"),f),style:ce,ref:function(e){n.assignRef(re,e),o&&n.assignRef(o,e)},onMouseEnter:T,onMouseLeave:B,onPointerEnter:L,onPointerLeave:O,onClick:P,onFocus:q},/*#__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:R},className:"offset-height-computer",ref:function(e){n.assignRef(t,e),n.assignRef(ue,e),Ee()}},j)}),/*#__PURE__*/l.default.createElement("div",{className:"text-ellipsis-inner",title:g?be:void 0,style:he,ref:ae},_&&C&&me,j))});e.TextEllipsis=f,e.c=s});
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 // 是否截断\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, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 runtime.inited: ', ellipsis, fold, runtime.inited);\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","content","children","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","_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","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","finalContent","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 if (!ellipsis || !lines || !innerLineHeight) {\n return;\n }\n return {\n whiteSpace,\n // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.2) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType, whiteSpace]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n boxSizing: 'content-box' as const,\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis, {\n forceResetFold = false, // 强制重置fold 比如child变化时\n } = {}) => {\n const {ellipsis, fold: preFold, defaultFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n }\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (\n resetFoldWhenChildrenOrEllipsisChange\n && (forceResetFold || !ellipsis && newEllipsis)\n && preFold !== defaultFold\n ) {\n handleFoldChange(undefined, defaultFold);\n }\n }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n if (!realLineHeight) {\n return;\n }\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n // 允许误差1px(行高为小数时)\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight - 1) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState(runtime.ellipsis, {\n forceResetFold: true,\n });\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n if (runtime.inited) { \n onStatusChange?.({\n ellipsis,\n fold,\n title: hoverTitle\n });\n }\n }, [onStatusChange, fold, ellipsis, hoverTitle]);\n useEffect(() => {\n runtime.inited = true;\n }, []);\n // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);\n return (\n <div\n className={cx(c(\"container\"), className)}\n style={containerStyle}\n ref={(r) => {\n assignRef(containerRef, r);\n ref && assignRef(ref, r);\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n onClick={onClick}\n onFocus={onFocus}\n >\n {/* 此dom仅用于计算高度 用.text-ellipsis-inner计算 在不重新初始化情况下切换文本时高度计算有问题 */}\n <Measure offset>\n {({measureRef, contentRect}) => {\n // console.log('contentRect:', contentRect.offset?.height, runtime.contentOffsetHeight);\n const {height} = contentRect.offset || {};\n if (height !== undefined && Math.abs(height - runtime.contentOffsetHeight) > 1) {\n calcEllipsis();\n }\n return <div style={{whiteSpace}} className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","_props$resetFoldWhenC","resetFoldWhenChildrenOrEllipsisChange","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","whiteSpace","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","finalContent","content","children","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","defaultFold","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","boxSizing","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","_temp","_ref$forceResetFold","forceResetFold","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","hoverTitle","title","classNames","r","assignRef","Measure","offset","_ref3","measureRef","contentRect","abs"],"mappings":"idA4BaA,EAAIC,EAAAA,gBAAE,yBA0GNC,EAAeC,EAAAA,WAA8C,SAACC,EAAOC,GAChF,IACEC,EAyBEF,EAzBFE,UAASC,EAyBPH,EAxBFI,WAAAA,OAAU,IAAAD,EAAG,GAAEA,EACfE,EAuBEL,EAvBFK,MAAKC,EAuBHN,EAtBFO,YAAAA,OAAc,IAAHD,EAAG,OAAMA,EAEZE,EAoBNR,EAnBFS,sCAAAA,OAAwC,IAAHD,GAAQA,EAC7CE,EAkBEV,EAlBFU,kBACAC,EAiBEX,EAjBFW,cAAaC,EAiBXZ,EAhBFa,gBAAAA,OAAkB,IAAHD,GAAOA,EAAAE,EAgBpBd,EAfFe,SAAAA,OAAW,IAAHD,EAAG,KAAIA,EAAAE,EAebhB,EAdFiB,WAAAA,OAAa,IAAHD,EAAG,KAAIA,EAAAE,EAcflB,EAbFmB,OAAAA,OAAS,IAAHD,EAAG,QAAOA,EAAAE,EAadpB,EAZFqB,iBAAAA,OAAmB,IAAHD,EAAG,SAAQA,EAC3BE,EAWEtB,EAXFsB,WACAC,EAUEvB,EAVFuB,iBACAC,EASExB,EATFwB,iBACAC,EAQEzB,EARFyB,aACAC,EAOE1B,EAPF0B,eACAC,EAME3B,EANF2B,aACAC,EAKE5B,EALF4B,aACAC,EAIE7B,EAJF6B,eACAC,EAGE9B,EAHF8B,eACAC,EAEE/B,EAFF+B,QACAC,EACEhC,EADFgC,QAEIC,EADFjC,EArBFkC,SAqBElC,EApBFmC,SAuBFC,EAAgCC,YAAS,GAAlCC,EAAQF,KAAEG,EAAWH,EAAA,GAC5BI,EAAkDH,YAAS,GAApDI,EAAiBD,EAAA,GAAEE,EAAoBF,EAAA,GAE9CG,EAAwBC,EAAAA,kBAAkB5C,EAAO,OAAQ,CAAC6C,cAAc,EAAMC,SAAUrB,IAAjFsB,EAAIJ,EAAA,GAAEK,EAAOL,EAAA,GACpBM,EAAwCZ,EAAQA,SAAC,GAA1Ca,EAAYD,EAAA,GAAEE,EAAeF,EACpC,GAAAG,EAA8Cf,EAAAA,SACtB,iBAAfjC,GAA2BA,EAAWiD,SAAS,MAClDC,WAAWlD,GACX,GAHCmD,EAAeH,EAAEI,GAAAA,EAAkBJ,KAK1CK,GAAwCpB,EAAQA,SAAChC,GAAMqD,GAAAD,GAAhDE,GAAAA,QAAU,IAAAD,GAAG,EAACA,GAAEE,GAAaH,GAEpC,GAAAI,GAAsCxB,EAAQA,SAAC,IAAxCyB,GAAWD,GAAA,GAAEE,GAAcF,GAAA,GAE3BG,GAAWC,EAAAA,WAAW,CAC3BC,QAAQ,EACRC,oBAAqB,EACrB7B,SAAAA,EACA8B,YAAarB,EACbA,KAAAA,EACAG,aAAAA,EACAY,YAAAA,GACAtC,iBAAAA,EACAC,aAAAA,GACC,CAAC,mBAAoB,OAAQ,iBAVlB,GAYR4C,GAAaC,EAAMA,OAAiB,MACpCC,GAAaD,EAAAA,OAAuB,MACpCE,GAAeF,SAAuB,MACtCG,GAAgBH,EAAAA,OAAuB,MAEvCI,GAAiBC,EAAOA,QAAC,WAC7B,MAAO,CACLvE,WAAYqC,EACR,MACArC,QAA0BwE,EAElC,EAAG,CAACxE,EAAYqC,IAGVoC,GAAYF,EAAAA,QAAQ,WAExB,GAAKrC,GADSqB,IACcJ,EAG5B,MAAO,CACLjC,WAAAA,EAEAwD,UAAW/B,GAPCY,GAOkB,IAAOJ,EAAsBqB,UAAAA,EAC3DG,gBAAiBhC,EARLY,QAQoBiB,EAIhCI,cACa,WAAX7D,GAAwB4B,OAAgC6B,EAAtBrB,EAAsBqB,KAE9D,EAAG,CAACjB,GAAYJ,EAAiBjB,EAAUS,EAAM5B,EAAQG,IAGnD2D,GAAWN,EAAOA,QAAC,WACvB,GAAK5B,EAAL,CAIA,IAAMmC,EAAU3B,EAEV4B,EAAmB,UAAXhE,EAAqBiE,KAAKC,IAAKH,EAAUhC,EAAgB,IAAK,IAAM,GAKlF,MAAO,CACLoC,UAAW,cACXC,OAAWhC,EAAmB,KAC9BnD,WAAemD,EAAe,KAC9BiC,WAAuB,WAAXrE,EAAyB+D,EAAcN,UAAAA,EACnDa,YAAwB,UAAXtE,EAAwB+D,EAAO,UAAON,EAEnDc,WAAkCvE,sBAAAA,EAAWwE,KAVxBpF,GACE,IAAvBA,EAAYqF,OAAe,IAAM,MAS4BrF,KAAAA,MAAe4E,EAAK,MAAM5E,EAAW,SAhBnG,CAkBH,EAAG,CAACgD,EAAiBhD,EAAawC,EAAM5B,EAAQ+B,IAE1C2C,GAAgBC,EAAAA,YAAY,WAE5BzB,GAAW0B,UACb1B,GAAW0B,QAAQC,MAAMC,MAAQ,UACL,MAA5BC,OAAOC,uBAAPD,OAAOC,sBAAwB,WACzB9B,GAAW0B,UACb1B,GAAW0B,QAAQC,MAAMC,MAAQ,OAErC,GAEJ,EAAG,IAEGG,GAAmBN,EAAWA,YAClC,SAACO,EAAkCtD,QAAAA,IAAAA,IAAAA,GAAQiB,GAAQjB,MACjDiB,GAAQjB,KAAOA,EACfC,EAAQD,EACZ,EAAG,IAEGuD,GAAa3B,EAAOA,QAAC,wBACzB,OACE4B,EAAAA,QAAAC,cACEtG,MAAAA,CAAAA,UAAWuG,EAAAA,WACT,mBACoBtF,oBAAAA,EACT,WAAXA,GAAoCE,aAAAA,GAEtC2E,MAAOf,GACPhF,IAAKwE,GACL1C,QAASqE,IAER7E,EACCA,EAAiBwB,gBAEjBwD,EAAAA,QAAAC,cAAKtG,MAAAA,CAAAA,UAAW,YAAa6C,EAAO9B,EAAaF,GAIzD,EAAG,CACDkE,GACAlC,EACAhC,EACAqF,GACA7E,EACAF,EACAF,EACAF,IAIIyF,GAAaZ,EAAAA,YAAY,SAACa,EAAWC,YAAXD,IAAAA,EAAc3C,GAAQ1B,UAAQ,IAExDuE,QAAF,IAF0DD,EAE1D,CAAA,EAAEA,GADJE,eAAAA,OAAc,IAAAD,GAAQA,EAEfvE,EAAwC0B,GAAxC1B,SAAgByE,EAAwB/C,GAA9BjB,KAAeqB,EAAeJ,GAAfI,YAC5BuC,IAAgBrE,IAClBC,EAAYoE,GACZ3C,GAAQ1B,SAAWqE,QACnB3C,GAAQxC,kBAARwC,GAAQxC,iBAAmBmF,IAI3BlG,IACIqG,IAAoBxE,GAAYqE,IACjCI,IAAY3C,GAEfgC,QAAiBxB,EAAWR,EAEhC,EAAG,CAACgC,GAAkBnE,EAAcxB,IAE9BuG,GAAelB,EAAAA,YAAY,WAC/B,IAAMmB,EAAU1C,GAAWwB,QACrBmB,EAAe1C,GAAauB,QAClC,GAAKkB,GAAYC,EAAjB,CAGAlD,GAAQG,oBAAsB8C,EAAQE,aACtC,IAAIC,EAAiB,EAGrB,IAAKA,GAAkBH,EAAS,CAC9B,IACQ7G,IADiC,MAAvB8F,OAAOmB,sBAAgB,EAAvBnB,OAAOmB,iBAAmBJ,KACR,CAAA,GAA5B7G,WACJA,KAEFgH,EAAiB9D,WAAWlD,KAE1BsC,GAAqB,GAG1B,CAED,GAAIa,IAAoB6D,IACtB5D,EAAmB4D,GACdA,GAIP,GAAK/G,EAWCsD,KAAetD,GACjBuD,GAAcvD,GAIdqG,GADE1C,GAAQG,sBAAwB9D,EAAQ,GAAK+G,EAAiB,QAdlE,GAAIpD,GAAQG,qBAAkC,MAAZ+C,OAAY,EAAZA,EAAcC,cAAc,CAC1D,IAAMG,EAAclC,KAAKmC,MAAML,EAAaC,aAAeC,GACvDzD,KAAe2D,GACjB1D,GAAc0D,GAEhBZ,IAAW,EACd,MACCA,IAAW,EA/Bd,CA4CH,EAAG,CAACrG,EAAOkD,EAAiBmD,KAI5Bc,EAAmBA,oBAAC,WAClBd,GAAW1C,GAAQ1B,SAAU,CAC3BwE,gBAAgB,IAElBE,IACF,EAAG,CAACA,GAAcN,KAGlBe,EAASA,UAAC,WACR,GAAInF,GAAYmC,GAAcsB,QAAS,CACrC2B,IAAOC,EAA6BlD,GAAcsB,QAA3C4B,YACHA,IAAgB3D,GAAQd,eAC1Bc,GAAQd,aAAeyE,EACvBxE,EAAgBwE,GAEnB,CACH,EAAG,CAACrF,EAAUrB,EAAYJ,IAC1B4G,EAASA,UAAC,WACJG,EAAQA,UACV/B,IAEJ,EAAG,CAAC9C,EAAM8C,KACV,IAAMgC,GAAoB/B,EAAAA,YAAY,WAAK,IAAAgC,EACnCC,GAAiBD,OAAAA,EAAAvD,GAAWwB,cAAX+B,EAAAA,EAAoBhE,cAAe,GACtDiE,IAAmB/D,GAAQF,cAC7BE,GAAQF,YAAciE,EACtBhE,GAAegE,GAEnB,EAAG,IACGC,GAAarD,EAAAA,QAAQ,WACvB,OAAOrC,GAAYS,EACW,mBAAlBpC,EACNA,EAAcmD,IACdnD,GAAiBmD,QACnBc,CACR,EAAG,CAACjE,EAAe2B,EAAUS,EAAMe,KAcnC,OAbA2D,EAASA,UAAC,WACJzD,GAAQE,SACI,MAAdxC,GAAAA,EAAiB,CACbY,SAAAA,EACAS,KAAAA,EACAkF,MAAOD,KAGf,EAAG,CAACtG,EAAgBqB,EAAMT,EAAU0F,KACpCP,EAASA,UAAC,WACRzD,GAAQE,QAAS,CACnB,EAAG,iBAGDqC,EAAA,QAAAC,cACEtG,MAAAA,CAAAA,UAAWuG,EAAEyB,WAACtI,EAAE,aAAcM,GAC9B8F,MAAOtB,GACPzE,IAAK,SAACkI,GACJC,EAASA,UAAC5D,GAAc2D,GACxBlI,GAAOmI,YAAUnI,EAAKkI,EACxB,EACAxG,aAAcA,EACdC,aAAcA,EACdC,eAAgBA,EAChBC,eAAgBA,EAChBC,QAASA,EACTC,QAASA,gBAGTuE,EAAAA,QAAAC,cAAC6B,EAAAA,QAAQC,CAAAA,QACN,GAAA,SAAAC,GAA8B,IAA5BC,EAAUD,EAAVC,WAEMjD,GAFiBgD,EAAXE,YAEgBH,QAAU,CAAE,GAAlC/C,OAIP,YAHeX,IAAXW,GAAwBH,KAAKsD,IAAInD,EAASvB,GAAQG,qBAAuB,GAC3E6C,kBAEKT,EAAA,QAAAC,cAAKR,MAAAA,CAAAA,MAAO,CAAC1E,WAAAA,GAAapB,UAAW,yBAA0BD,IAAK,SAACkI,GAC1EC,EAASA,UAACI,EAAYL,GACtBC,EAASA,UAAC7D,GAAY4D,GACtBN,IACF,GACG5F,EAEL,gBAMFsE,EAAAA,QAAAC,cACEtG,MAAAA,CAAAA,UAAW,sBACX+H,MAAOvH,EAAoBsH,QAAapD,EACxCoB,MAAOnB,GACP5E,IAAKoE,IAIJ/B,GAAYzB,GAAmByF,GAC/BrE,GAIT"}
@@ -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-alpha.1",
3
+ "version": "0.0.4",
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": "b99b9c045114435c96547ad13c30f21e6a49f4c0"
46
+ "gitHead": "d10717e4226ca123acdd87fe420363f52a07cabd"
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,
@@ -154,6 +160,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
154
160
  onClick,
155
161
  onFocus,
156
162
  } = props;
163
+ const finalContent = content || children;
157
164
  // 是否截断
158
165
  const [ellipsis, setEllipsis] = useState(false);
159
166
  const [getLineHeightFail, setGetLineHeightFail] = useState(false);
@@ -173,6 +180,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
173
180
  inited: false, // mounted
174
181
  contentOffsetHeight: 0,
175
182
  ellipsis,
183
+ defaultFold: fold, // 记录一下默认的折叠状态,用于 reset fold
176
184
  fold,
177
185
  foldBtnWidth,
178
186
  textContent,
@@ -285,18 +293,24 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
285
293
  ]);
286
294
 
287
295
  // 重置状态
288
- const resetState = useCallback((newEllipsis = runtime.ellipsis) => {
289
- const {ellipsis, fold: preFold} = runtime;
296
+ const resetState = useCallback((newEllipsis = runtime.ellipsis, {
297
+ forceResetFold = false, // 强制重置fold 比如child变化时
298
+ } = {}) => {
299
+ const {ellipsis, fold: preFold, defaultFold} = runtime;
290
300
  if (newEllipsis !== ellipsis) {
291
301
  setEllipsis(newEllipsis);
292
302
  runtime.ellipsis = newEllipsis;
293
303
  runtime.onEllipsisChange?.(newEllipsis);
294
- // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)
295
- if (newEllipsis && !preFold) {
296
- handleFoldChange(undefined, true);
297
- }
298
304
  }
299
- }, [handleFoldChange]);
305
+ // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)
306
+ if (
307
+ resetFoldWhenChildrenOrEllipsisChange
308
+ && (forceResetFold || !ellipsis && newEllipsis)
309
+ && preFold !== defaultFold
310
+ ) {
311
+ handleFoldChange(undefined, defaultFold);
312
+ }
313
+ }, [handleFoldChange, finalContent, resetFoldWhenChildrenOrEllipsisChange]);
300
314
 
301
315
  const calcEllipsis = useCallback(() => {
302
316
  const wrapDom = wrapperRef.current;
@@ -352,7 +366,9 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
352
366
  // 监听内容高度,是否需要折叠
353
367
  // 用useLayoutEffect方式闪屏显示
354
368
  useCompatibleEffect(() => {
355
- resetState();
369
+ resetState(runtime.ellipsis, {
370
+ forceResetFold: true,
371
+ });
356
372
  calcEllipsis();
357
373
  }, [calcEllipsis, resetState]);
358
374
 
@@ -378,7 +394,6 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
378
394
  setTextContent(newTextContent);
379
395
  }
380
396
  }, []);
381
- const finalContent = content || children;
382
397
  const hoverTitle = useMemo(() => {
383
398
  return ellipsis && fold
384
399
  ? (typeof titleWhenFold === 'function'
@@ -398,7 +413,7 @@ export const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props
398
413
  useEffect(() => {
399
414
  runtime.inited = true;
400
415
  }, []);
401
- // console.log('[render TextEllipsis]: ellipsis fold runtime.inited: ', ellipsis, fold, runtime.inited);
416
+ // console.log('[render TextEllipsis]: ellipsis fold wrapStyle: ', ellipsis, fold, wrapStyle);
402
417
  return (
403
418
  <div
404
419
  className={cx(c("container"), className)}