@ohkit/text-ellipsis 0.0.2-alpha.0 → 0.0.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # `@ohkit/text-ellipsis`
2
+
3
+ > 多功能文本截断显示组件,支持多行截断、展开/收起功能,兼容富文本内容
4
+
5
+ ## 安装
6
+ ```bash
7
+ npm install @ohkit/text-ellipsis
8
+ ```
9
+
10
+ ## 功能特性
11
+ - 支持多行文本截断显示
12
+ - 可配置展开/收起功能按钮
13
+ - 支持富文本内容(保留文字样式)
14
+ - 提供两种UI布局模式(右侧按钮/底部按钮)
15
+ - 可自定义按钮文本和样式
16
+ - 支持动态内容高度检测
17
+ - 完善的回调机制
18
+
19
+ ## 基本用法
20
+
21
+ ```tsx
22
+ import { TextEllipsis } from '@ohkit/text-ellipsis';
23
+
24
+ function Demo() {
25
+ return (
26
+ <TextEllipsis
27
+ lines={3}
28
+ content="这是一段需要截断的长文本内容..."
29
+ />
30
+ );
31
+ }
32
+ ```
33
+
34
+ ## API 说明
35
+
36
+ ### Props
37
+
38
+ | 参数 | 说明 | 类型 | 默认值 |
39
+ |------|------|------|--------|
40
+ | className | 自定义样式类名,会附加到根元素上 | string | - |
41
+ | lines | 超过几行折叠 (number > 0) | number | - |
42
+ | uiType | 展开按钮位置 `right`(右下侧) 或 `bottom`(底部) | string | `right` |
43
+ | lineHeight | 行高(单位:px) | number/string | - |
44
+ | content | 文本内容 | ReactNode | - |
45
+ | fold | 是否折叠 | boolean | `true` |
46
+ | showFoldControl | 是否显示展开控制按钮 | boolean | `true` |
47
+ | foldText | 折叠状态按钮文字 | string | `收起` |
48
+ | unfoldText | 展开状态按钮文字 | string | `展开` |
49
+ | maskBgColor | 展开按钮蒙层背景色(16进制) | string | `#fff` |
50
+ | showTitleWhenFold | 折叠状态下是否显示title属性 | boolean | `false` |
51
+ | renderFoldButton | 自定义渲染展开按钮 | (fold: boolean) => ReactNode | - |
52
+
53
+ ### 事件
54
+
55
+ | 事件名 | 说明 | 回调参数 |
56
+ |------|------|------|
57
+ | onFoldChange | 折叠状态变化时触发 | (fold: boolean) => void |
58
+ | onEllipsisChange | 截断状态变化时触发 | (ellipsis: boolean) => void |
59
+ | onStatusChange | 关键状态变更触发 | ({fold, ellipsis, title}) => void |
60
+
61
+ ## 高级示例
62
+
63
+ ```tsx
64
+ <TextEllipsis
65
+ lines={2}
66
+ uiType="bottom"
67
+ maskBgColor="#f5f5f5"
68
+ content={
69
+ <div>
70
+ <strong>富文本内容</strong> 也可以正常截断,
71
+ <span style={{color: 'red'}}>包括样式</span>
72
+ </div>
73
+ }
74
+ renderFoldButton={(fold) => (
75
+ <button>{fold ? '显示更多' : '收起'}</button>
76
+ )}
77
+ onFoldChange={(fold) => console.log('折叠状态:', fold)}
78
+ />
79
+ ```
80
+
81
+ ## 注意事项
82
+ 1. Safari浏览器下富文本截断可能有轻微高度计算偏差
83
+ 2. 图片和表格等复杂内容可能无法完美截断
84
+ 3. 动态内容变化后会自动重新计算截断状态
package/dist/index.d.ts CHANGED
@@ -13,6 +13,9 @@ interface ITextEllipsis extends Pick<React.DOMAttributes<HTMLDivElement>, "onMou
13
13
  * @default right
14
14
  */
15
15
  uiType?: "right" | "bottom";
16
+ /**
17
+ * 自定义样式类名,会附加到根元素上
18
+ */
16
19
  className?: string;
17
20
  /**
18
21
  * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高
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 className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_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 * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_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 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/index.tsx"],"sourcesContent":["/**\n * @file 文本截断显示组件\n * @description 基于React封装一个文本截断显示组件,富文本(仅文字样式,图片和表格效果不一定好)同普通文本处理一致\n * @author <wuqiuyang305@126.com>\n */\n\nimport React, {\n forwardRef,\n useState,\n useMemo,\n useEffect,\n useCallback,\n useRef,\n PropsWithChildren,\n MouseEvent,\n} from \"react\";\nimport \"./style.scss\";\nimport {\n isSafari,\n prefixClassname as p,\n classNames as cx,\n assignRef,\n useRuntime,\n useCompatibleEffect,\n useSyncPropsState,\n} from \"@ohkit/utils\";\nimport { Measure } from \"@ohkit/measure\";\n\nexport const c = p(\"ohkit-text-ellipsis__\");\n\ninterface ITextEllipsis\n extends Pick<\n React.DOMAttributes<HTMLDivElement>,\n | \"onMouseEnter\"\n | \"onMouseLeave\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onClick\"\n > {\n /**\n * right | bottom 展开按钮在右下侧还是底部\n * @default right\n */\n uiType?: \"right\" | \"bottom\";\n className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_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.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 renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_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 +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 className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","lineHeight","lines","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","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 * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","TextEllipsis","forwardRef","props","ref","className","lineHeight","lines","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","showFoldControl","foldText","unfoldText","uiType","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","ellipsis","setEllipsis","useState","getLineHeightFail","setGetLineHeightFail","fold","setFold","useSyncPropsState","defaultValue","onChange","foldBtnWidth","setFoldBtnWidth","innerLineHeight","setInnerLineHeight","endsWith","parseFloat","innerLines","setInnerLines","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","realStyle","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","r","assignRef","Measure","offset","measureRef","contentRect","abs"],"mappings":"kTA4Ba,MAAAA,EAAIC,EAAE,yBAiGNC,EAAeC,EAA8C,CAACC,EAAOC,KAChF,MAAMC,UACJA,EAASC,WACTA,EAAa,GAAEC,MACfA,EAAKC,YACLA,EAAc,OAAMC,QACpBA,EAAOC,SACPA,EAAQC,kBACRA,EAAiBC,cACjBA,EAAaC,gBACbA,GAAkB,EAAIC,SACtBA,EAAW,KAAIC,WACfA,EAAa,KAAIC,OACjBA,EAAS,QAAOC,iBAChBA,EAAmB,SAAQC,iBAC3BA,EAAgBC,iBAChBA,EAAgBC,aAChBA,EAAYC,eACZA,EAAcC,aACdA,EAAYC,aACZA,EAAYC,eACZA,EAAcC,eACdA,EAAcC,QACdA,EAAOC,QACPA,GACExB,GAEGyB,EAAUC,GAAeC,GAAS,IAClCC,EAAmBC,GAAwBF,GAAS,IAEpDG,EAAMC,GAAWC,EAAkBhC,EAAO,OAAQ,CAACiC,cAAc,EAAMC,SAAUjB,KACjFkB,EAAcC,GAAmBT,EAAS,IAC1CU,EAAiBC,GAAsBX,EACtB,iBAAfxB,GAA2BA,EAAWoC,SAAS,MAClDC,WAAWrC,GACX,IAECsC,EAAa,EAAGC,GAAiBf,EAASvB,IAE1CuC,EAAaC,GAAkBjB,EAAS,KAExCkB,GAAWC,EAAW,CAC3BC,QAAQ,EACRC,oBAAqB,EACrBvB,WACAK,OACAK,eACAQ,cACA3B,mBACAC,gBACC,CAAC,mBAAoB,OAAQ,iBAE1BgC,GAAaC,EAAuB,MACpCC,GAAaD,EAAuB,MACpCE,GAAeF,EAAuB,MACtCG,GAAgBH,EAAuB,MAEvCI,GAAiBC,EAAQ,KACtB,CACLpD,WAAYyB,EACR,MACAzB,QAA0BqD,IAE/B,CAACrD,EAAYyB,IAGV6B,GAAYF,EAAQ,KAExB,GAAK9B,GADSgB,GACcJ,EAG5B,MAAO,CAELqB,UAAW5B,GANCW,EAMkB,IAAOJ,EAAnB,UAAyCmB,EAC3DG,gBAAiB7B,EAPLW,OAOoBe,EAIhCI,cACa,WAAX/C,GAAwBiB,OAAgC0B,EAAzB,GAAGnB,QAErC,CAACI,EAAYJ,EAAiBZ,EAAUK,EAAMjB,IAG3CgD,GAAWN,EAAQ,KACvB,IAAKzB,EACH,OAGF,MAAMgC,EAAUzB,EAEV0B,EAAmB,UAAXlD,EAAqBmD,KAAKC,IAAKH,EAAU3B,EAAgB,IAAK,IAAM,GAKlF,MAAO,CACL+B,OAAQ,GAAG7B,MACXlC,WAAY,GAAGkC,MACf8B,WAAuB,WAAXtD,EAAsB,GAAGiD,WAAcN,EACnDY,YAAwB,UAAXvD,EAAqB,GAAGiD,WAAcN,EAEnDa,WAAY,sBAAsBxD,MATbR,IACE,IAAvBA,EAAYiE,OAAe,IAAM,SAQ4BjE,KAAe0D,OAAW1D,YAExF,CAACgC,EAAiBhC,EAAayB,EAAMjB,EAAQsB,IAE1CoC,GAAgBC,EAAY,KAE5BvB,GAAWwB,UACbxB,GAAWwB,QAAQC,MAAMC,MAAQ,UACL,MAA5BC,OAAOC,uBAAPD,OAAOC,sBAAwB,KACzB5B,GAAWwB,UACbxB,GAAWwB,QAAQC,MAAMC,MAAQ,YAItC,IAEGG,GAAmBN,EACvB,CAACO,EAAkCjD,GAAQe,EAAQf,QACjDe,EAAQf,KAAOA,EACfC,EAAQD,IACT,IAEGkD,GAAazB,EAAQ,iBAEvB0B,EAAAC,cAAA,MAAA,CACEhF,UAAWiF,EACT,mBACA,oBAAoBtE,IACT,WAAXA,GAAuB,aAAaC,KAEtC4D,MAAOb,GACP5D,IAAKoD,GACL9B,QAASuD,IAER/D,EACCA,EAAiBe,gBAEjBmD,EAAAC,qBAAKhF,UAAW,YAAa4B,EAAOlB,EAAaD,IAItD,CACDkD,GACA/B,EACAnB,EACAmE,GACA/D,EACAD,EACAD,EACAD,IAIIwE,GAAaZ,EAAY,CAACa,EAAcxC,EAAQpB,YACpD,MAAMA,SAACA,EAAUK,KAAMwD,GAAWzC,EAC9BwC,IAAgB5D,IAClBC,EAAY2D,GACZxC,EAAQpB,SAAW4D,QACnBxC,EAAQ7B,kBAAR6B,EAAQ7B,iBAAmBqE,GAEvBA,IAAgBC,GAClBR,QAAiBtB,GAAW,KAG/B,CAACsB,KAEES,GAAef,EAAY,KAC/B,MAAMgB,EAAUrC,GAAWsB,QACrBgB,EAAerC,GAAaqB,QAClC,IAAKe,IAAYC,EACf,OAEF5C,EAAQG,oBAAsBwC,EAAQE,aACtC,IAAIC,EAAiB,EAGrB,IAAKA,GAAkBH,EAAS,CAC9B,MAAMI,QAAYhB,OAAOiB,wBAAPjB,OAAOiB,iBAAmBL,IACtCrF,WAAEA,GAAeyF,GAAa,GAChCzF,IAEFwF,EAAiBnD,WAAWrC,GACvBwF,GACH9D,GAAqB,GAG1B,CAKD,GAHIQ,IAAoBsD,GACtBrD,EAAmBqD,GAEhBvF,EAWCqC,IAAerC,GACjBsC,EAActC,GAGdgF,GADEvC,EAAQG,sBAAwB5C,EAAQ,GAAKuF,QAbjD,GAAI9C,EAAQG,qBAAsByC,MAAAA,OAAAA,EAAAA,EAAcC,cAAc,CAC1D,MAAMI,EAAc9B,KAAK+B,MAAMN,EAAaC,aAAeC,GACvDlD,IAAeqD,GACjBpD,EAAcoD,GAEhBV,IAAW,EACd,MACCA,IAAW,IAYd,CAAChF,EAAOiC,EAAiB+C,KAI5BY,EAAoB,KAClBZ,KACAG,MACC,CAACA,GAAcH,KAGlBa,EAAU,KACR,GAAIxE,GAAY4B,GAAcoB,QAAS,CACrC,MAAMyB,YAACA,GAA6B7C,GAAcoB,QAC9CyB,IAAgBrD,EAAQV,eAC1BU,EAAQV,aAAe+D,EACvB9D,EAAgB8D,GAEnB,GACA,CAACzE,EAAUb,EAAYF,IAC1BuF,EAAU,KACJE,GACF5B,MAED,CAACzC,EAAMyC,KACV,MAAM6B,GAAoB5B,EAAY,KAAK6B,IAAAA,EACzC,MAAMC,GAAmC,OAAlBD,EAAAlD,GAAWsB,cAAO,EAAlB4B,EAAoB1D,cAAe,GACtD2D,IAAmBzD,EAAQF,cAC7BE,EAAQF,YAAc2D,EACtB1D,EAAe0D,KAEhB,IACGC,GAAejG,GAAWC,EAC1BiG,GAAajD,EAAQ,IAChB9B,GAAYK,EACW,mBAAlBrB,EACNA,EAAckC,GACdlC,GAAiBkC,OACnBa,EACL,CAAC/C,EAAegB,EAAUK,EAAMa,IAcnC,OAbAsD,EAAU,KACJpD,EAAQE,SACV7B,MAAAA,GAAAA,EAAiB,CACbO,WACAK,OACA2E,MAAOD,OAGZ,CAACtF,EAAgBY,EAAML,EAAU+E,KACpCP,EAAU,KACRpD,EAAQE,QAAS,GAChB,iBAGDkC,EAAAC,cACEhF,MAAAA,CAAAA,UAAWiF,EAAGvF,EAAE,aAAcM,GAC9BwE,MAAOpB,GACPrD,IAAMyG,IACJC,EAAUvD,GAAcsD,GACxBzG,GAAO0G,EAAU1G,EAAKyG,IAExBvF,aAAcA,EACdC,aAAcA,EACdC,eAAgBA,EAChBC,eAAgBA,EAChBC,QAASA,EACTC,QAASA,gBAGTyD,EAAAC,cAAC0B,GAAQC,QAAM,GACZ,EAAEC,aAAYC,kBAEb,MAAM7C,OAACA,GAAU6C,EAAYF,QAAU,CAAE,EAIzC,YAHerD,IAAXU,GAAwBF,KAAKgD,IAAI9C,EAASrB,EAAQG,qBAAuB,GAC3EuC,kBAEKN,EAAAC,cAAKhF,MAAAA,CAAAA,UAAW,yBAA0BD,IAAMyG,IACrDC,EAAUG,EAAYJ,GACtBC,EAAUxD,GAAYuD,GACtBN,OAECG,mBAQPtB,EAAAC,qBACEhF,UAAW,sBACXuG,MAAOjG,EAAoBgG,QAAahD,EACxCkB,MAAOjB,GACPxD,IAAKgD,IAIJxB,GAAYf,GAAmBsE,GAC/BuB"}
@@ -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 className?: string;\n /**\n * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高\n */\n lineHeight?: React.CSSProperties[\"lineHeight\"];\n /**\n * 超过几行折叠(number > 0), 没传或者传入无效值不限制,自动截断到容器的最大高度\n */\n lines?: number;\n /**\n * 展开按钮蒙层背景色(仅支持16进制表示)\n * @default #fff\n */\n maskBgColor?: string;\n /**\n * text|ReactNode 与children任传一个\n */\n content?: React.ReactNode;\n /**\n * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_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 * 折叠状态\n * @default true\n */\n fold?: boolean;\n /**\n * 显示展开控制按钮\n * @default true\n */\n showFoldControl?: boolean;\n /**\n * 展开按钮位置 uiType='bottom'时有效\n * @default center\n */\n controlPlacement?: 'left' | 'center' | 'right';\n /**\n * 展开按钮文字\n * @default 收起\n */\n foldText?: string;\n /**\n * 展开按钮文字\n * @default 展开\n */\n unfoldText?: string;\n /**\n * 折叠状态下是否显示title属性\n * @default false\n */\n showTitleWhenFold?: boolean;\n /**\n * 折叠状态自定义title属性内容\n */\n titleWhenFold?: string | ((title: string) => string);\n /**\n * 自定义渲染展开按钮\n */\n renderFoldButton?: (fold: boolean) => React.ReactNode;\n /**\n * @param fold 折叠状态,true 折叠,false 展开\n */\n onFoldChange?: (fold: boolean) => void;\n /**\n * @param ellipsis 是否截断,true 截断,false 未截断\n */\n onEllipsisChange?: (ellipsis: boolean) => void;\n /**\n * 关键状态变更触发\n * @param status\n */\n onStatusChange?: (status: {\n fold: boolean;\n ellipsis: boolean;\n title?: string;\n }) => void;\n}\n\nexport type TextEllipsisProps = PropsWithChildren<ITextEllipsis>;\n\nexport const TextEllipsis = forwardRef<HTMLDivElement, TextEllipsisProps>((props, ref) => {\n const {\n className,\n lineHeight = \"\",\n lines,\n maskBgColor = \"#fff\",\n content,\n children,\n showTitleWhenFold,\n titleWhenFold,\n showFoldControl = true,\n foldText = \"收起\",\n unfoldText = \"展开\",\n uiType = \"right\",\n controlPlacement = 'center',\n renderFoldButton,\n onEllipsisChange,\n onFoldChange,\n onStatusChange,\n onMouseEnter,\n onMouseLeave,\n onPointerEnter,\n onPointerLeave,\n onClick,\n onFocus,\n } = props;\n // 是否截断\n const [ellipsis, setEllipsis] = useState(false);\n const [getLineHeightFail, setGetLineHeightFail] = useState(false);\n // 折叠状态\n const [fold, setFold] = useSyncPropsState(props, 'fold', {defaultValue: true, onChange: onFoldChange});\n const [foldBtnWidth, setFoldBtnWidth] = useState(1);\n const [innerLineHeight, setInnerLineHeight] = useState(\n typeof lineHeight === \"string\" && lineHeight.endsWith(\"px\")\n ? parseFloat(lineHeight)\n : 0\n );\n const [innerLines = 0, setInnerLines] = useState(lines);\n // children提取的纯文本\n const [textContent, setTextContent] = useState('');\n\n const [runtime] = useRuntime({\n 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 // HACK: 兼容safari 15+ 富文本折叠高度丢失问题\n minHeight: fold ? `${(lines - 0.5) * innerLineHeight}px` : undefined,\n WebkitLineClamp: fold ? lines : undefined, // 利用-webkit-line-clamp截断方案\n // Note: safari 对WebkitLineClamp支持太差劲 判断浏览器优雅降级为高度截断方案\n // WebkitLineClamp: isSafari ? undefined : ellipsis && fold && lines, // 利用-webkit-line-clamp截断方案\n // maxHeight: isSafari && ellipsis && fold ? lines * innerLineHeight : undefined,\n paddingBottom:\n uiType === \"bottom\" || !fold ? `${innerLineHeight}px` : undefined,\n };\n }, [innerLines, innerLineHeight, ellipsis, fold, uiType]);\n\n // 展开|收起 按钮样式\n const btnStyle = useMemo(() => {\n if (!fold) {\n return;\n }\n // 按钮padding,取行高\n const padding = innerLineHeight;\n // 蒙层透明度所占比例\n const ratio = uiType === \"right\" ? Math.min((padding / foldBtnWidth) * 100, 80) : 60;\n // 16进制透明色(考虑简写方式), 不直接使用css的transparent是因为safari的表现是灰色\n const transparent = `${maskBgColor}${\n maskBgColor.length === 4 ? \"0\" : \"00\"\n }`;\n return {\n height: `${innerLineHeight}px`,\n lineHeight: `${innerLineHeight}px`,\n paddingTop: uiType === \"bottom\" ? `${padding}px` : undefined,\n paddingLeft: uiType === \"right\" ? `${padding}px` : undefined,\n // 渐变蒙层\n background: `linear-gradient(to ${uiType}, ${transparent}, ${maskBgColor} ${ratio}%, ${maskBgColor} 100%)`,\n };\n }, [innerLineHeight, maskBgColor, fold, uiType, foldBtnWidth]);\n\n const reorganizeDom = useCallback(() => {\n // safari 中仅改变 WebkitLineClamp 没触发重排,调整微小宽度以触发\n if (contentRef.current) {\n contentRef.current.style.width = \"99.999%\";\n window.requestAnimationFrame?.(() => {\n if (contentRef.current) {\n contentRef.current.style.width = \"100%\";\n }\n });\n }\n }, []);\n\n const handleFoldChange = useCallback(\n (evt?: MouseEvent<HTMLDivElement>, fold = !runtime.fold) => {\n runtime.fold = fold;\n setFold(fold);\n }, []);\n\n const ButtonComp = useMemo(() => {\n return (\n <div\n className={cx(\n \"btn-fold-wrapper\",\n `btn-fold-wrapper-${uiType}`,\n uiType === \"bottom\" && `placement-${controlPlacement}`\n )}\n style={btnStyle}\n ref={btnWrapperRef}\n onClick={handleFoldChange}\n >\n {renderFoldButton ? (\n renderFoldButton(fold)\n ) : (\n <div className={\"btn-fold\"}>{fold ? unfoldText : foldText}</div>\n )}\n </div>\n );\n }, [\n btnStyle,\n fold,\n foldText,\n handleFoldChange,\n renderFoldButton,\n controlPlacement,\n uiType,\n unfoldText,\n ]);\n\n // 重置状态\n const resetState = useCallback((newEllipsis = runtime.ellipsis) => {\n const {ellipsis, fold: preFold} = runtime;\n if (newEllipsis !== ellipsis) {\n setEllipsis(newEllipsis);\n runtime.ellipsis = newEllipsis;\n runtime.onEllipsisChange?.(newEllipsis);\n // 从未截断状态切换为截断状态时,自动折叠(即:出现展开按钮)\n if (newEllipsis && !preFold) {\n handleFoldChange(undefined, true);\n }\n }\n }, [handleFoldChange]);\n\n const calcEllipsis = useCallback(() => {\n const wrapDom = wrapperRef.current;\n const containerDom = containerRef.current;\n if (!wrapDom || !containerDom) {\n return;\n }\n runtime.contentOffsetHeight = wrapDom.offsetHeight;\n let realLineHeight = 0;\n\n // 若外部未传入, 尝试读取当前文本的行高。\n if (!realLineHeight && wrapDom) {\n const realStyle = window.getComputedStyle?.(wrapDom);\n const { lineHeight } = realStyle || {};\n if (lineHeight) {\n // 未设置行高的为 normal\n realLineHeight = parseFloat(lineHeight);\n if (!realLineHeight) {\n setGetLineHeightFail(true);\n }\n }\n }\n // lineHeight同步到innerLineHeight\n if (innerLineHeight !== realLineHeight) {\n setInnerLineHeight(realLineHeight);\n }\n if (!lines) {\n if (runtime.contentOffsetHeight > containerDom?.offsetHeight) {\n const adjustLines = Math.floor(containerDom.offsetHeight / realLineHeight);\n if (innerLines !== adjustLines) {\n setInnerLines(adjustLines);\n }\n resetState(true);\n } else {\n resetState(false);\n }\n } else {\n if (innerLines !== lines) {\n setInnerLines(lines);\n }\n if (runtime.contentOffsetHeight >= (lines + 1) * realLineHeight) {\n resetState(true);\n } else {\n resetState(false);\n }\n }\n }, [lines, innerLineHeight, resetState]);\n\n // 监听内容高度,是否需要折叠\n // 用useLayoutEffect方式闪屏显示\n useCompatibleEffect(() => {\n resetState();\n calcEllipsis();\n }, [calcEllipsis, resetState]);\n\n // 监听\"展开\"按钮宽度变化\n useEffect(() => {\n if (ellipsis && btnWrapperRef.current) {\n const {offsetWidth, offsetHeight} = btnWrapperRef.current;\n if (offsetWidth !== runtime.foldBtnWidth) {\n runtime.foldBtnWidth = offsetWidth;\n setFoldBtnWidth(offsetWidth);\n }\n }\n }, [ellipsis, unfoldText, showFoldControl]);\n useEffect(() => {\n if (isSafari) {\n reorganizeDom();\n }\n }, [fold, reorganizeDom]);\n const updateTextContent = useCallback(() => {\n const newTextContent = wrapperRef.current?.textContent || '';\n if (newTextContent !== runtime.textContent) {\n runtime.textContent = newTextContent;\n setTextContent(newTextContent);\n }\n }, []);\n const finalContent = content || children;\n const hoverTitle = useMemo(() => {\n return ellipsis && fold\n ? (typeof titleWhenFold === 'function'\n ? titleWhenFold(textContent)\n : titleWhenFold || textContent)\n : undefined;\n }, [titleWhenFold, ellipsis, fold, textContent]);\n useEffect(() => {\n 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 className={\"offset-height-computer\"} ref={(r) => {\n assignRef(measureRef, r);\n assignRef(wrapperRef, r);\n updateTextContent();\n }}>\n {finalContent}\n </div>\n }}\n </Measure>\n {/* <div className={\"offset-height-computer\"} ref={wrapperRef}>\n {finalContent}\n </div> */}\n {/* 主文本显示 */}\n <div\n className={\"text-ellipsis-inner\"}\n title={showTitleWhenFold ? hoverTitle : undefined}\n style={wrapStyle}\n ref={contentRef}\n >\n {/* {finalContent} */}\n {/* firefox >= 133 绝对定位的按钮放文本后面也会被截断隐藏!! , 放文本前面可解决 */}\n {ellipsis && showFoldControl && ButtonComp}\n {finalContent}\n </div>\n </div>\n );\n});\n"],"names":["c","p","prefixClassname","TextEllipsis","forwardRef","props","ref","className","_props$lineHeight","lineHeight","lines","_props$maskBgColor","maskBgColor","content","children","showTitleWhenFold","titleWhenFold","_props$showFoldContro","showFoldControl","_props$foldText","foldText","_props$unfoldText","unfoldText","_props$uiType","uiType","_props$controlPlaceme","controlPlacement","renderFoldButton","onEllipsisChange","onFoldChange","onStatusChange","onMouseEnter","onMouseLeave","onPointerEnter","onPointerLeave","onClick","onFocus","_useState","useState","ellipsis","setEllipsis","_useState2","getLineHeightFail","setGetLineHeightFail","_useSyncPropsState","useSyncPropsState","defaultValue","onChange","fold","setFold","_useState3","foldBtnWidth","setFoldBtnWidth","_useState4","endsWith","parseFloat","innerLineHeight","setInnerLineHeight","_useState5","_useState5$","innerLines","setInnerLines","_useState6","textContent","setTextContent","runtime","useRuntime","inited","contentOffsetHeight","contentRef","useRef","wrapperRef","containerRef","btnWrapperRef","containerStyle","useMemo","undefined","wrapStyle","minHeight","WebkitLineClamp","paddingBottom","btnStyle","padding","ratio","Math","min","height","paddingTop","paddingLeft","background","transparent","length","reorganizeDom","useCallback","current","style","width","window","requestAnimationFrame","handleFoldChange","evt","ButtonComp","React","createElement","cx","classNames","resetState","newEllipsis","preFold","calcEllipsis","wrapDom","containerDom","offsetHeight","realLineHeight","getComputedStyle","adjustLines","floor","useCompatibleEffect","useEffect","_btnWrapperRef$curren","offsetWidth","isSafari","updateTextContent","_wrapperRef$current","newTextContent","finalContent","hoverTitle","title","r","assignRef","Measure","offset","_ref2","measureRef","contentRect","abs"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohkit/text-ellipsis",
3
- "version": "0.0.2-alpha.0",
3
+ "version": "0.0.2-alpha.1",
4
4
  "description": "text ellipsis for react",
5
5
  "keywords": [
6
6
  "text ellipsis"
@@ -47,5 +47,5 @@
47
47
  "react": ">=16.8.0",
48
48
  "react-dom": ">=16.8.0"
49
49
  },
50
- "gitHead": "bf6a397f08c860cc8ac0e61eb0ee9a594e216956"
50
+ "gitHead": "eff9b4dafbe00ec809f5da57230a1034e8df56d4"
51
51
  }
package/src/index.tsx CHANGED
@@ -43,6 +43,9 @@ interface ITextEllipsis
43
43
  * @default right
44
44
  */
45
45
  uiType?: "right" | "bottom";
46
+ /**
47
+ * 自定义样式类名,会附加到根元素上
48
+ */
46
49
  className?: string;
47
50
  /**
48
51
  * (单位:px)未传入或无效(0也视为无效)则自动取当前文本的行高