[英]React show Material-UI Tooltip only for text that has ellipsis
尋找一種方法讓 material-ui 的工具提示僅在文本被省略號(溢出)截斷時才展開表格單元格中的文本。
目前在我的表中,我有一個這樣的單元格:
<TableCell className={classes.descriptionCell}>{row.description}</TableCell>
我對 descriptionCell 的樣式是這樣的:
descriptionCell: {
whiteSpace: 'nowrap',
maxWidth: '200px',
overflow: 'hidden',
textOverflow: 'ellipsis'
}
這使得文本按照我希望在該表中的方式運行,但我希望能夠 hover 並在工具提示中查看它的 rest,最好是 Material-UI 的內置工具提示組件。
我知道這里有一個 package https://www.npmjs.com/package/react-ellipsis-with-tooltip應該這樣做,但它使用引導工具提示,而不是材料 UI。
離開@benjamin.keen 的回答。 這是一個獨立的功能組件,它只是他使用鈎子執行比較功能的答案的擴展。
import React, { useRef, useEffect, useState } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
const OverflowTip = props => {
// Create Ref
const textElementRef = useRef();
const compareSize = () => {
const compare =
textElementRef.current.scrollWidth > textElementRef.current.clientWidth;
console.log('compare: ', compare);
setHover(compare);
};
// compare once and add resize listener on "componentDidMount"
useEffect(() => {
compareSize();
window.addEventListener('resize', compareSize);
}, []);
// remove resize listener again on "componentWillUnmount"
useEffect(() => () => {
window.removeEventListener('resize', compareSize);
}, []);
// Define state and function to update the value
const [hoverStatus, setHover] = useState(false);
return (
<Tooltip
title={props.value}
interactive
disableHoverListener={!hoverStatus}
style={{fontSize: '2em'}}
>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
>
{props.someLongText}
</div>
</Tooltip>
);
};
export default OverflowTip;
請在下面找到代碼和框 - https://codesandbox.io/s/material-demo-p2omr
我在這里使用 ref 來獲取 TableCell DOM 節點,然后比較 scrollWidth 和 clientWidth 以確定是否必須顯示 Tooltip。(這是基於此處的答案)
我已將“rowref”(具有 ref 的屬性)和“open”(禁用/啟用工具提示)添加為行的新屬性。 我不知道您的數據來自哪里,但我假設您可以將這些屬性添加到行中。
還有一件事要注意,我只是設置“disableHoverListener”道具來禁用 tooltip 。 還有其他道具 - "disableFocusListener" 和 "disableTouchListener" ,如果你想使用它們。 更多信息在這里
希望這對你有用。 如果您對代碼有任何疑問,請告訴我。
我今天遇到了同樣的問題,@vijay-menon 的回答非常有幫助。 這是一個簡單的獨立組件,用於同一件事:
import React, { Component } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
class OverflowTip extends Component {
constructor(props) {
super(props);
this.state = {
overflowed: false
};
this.textElement = React.createRef();
}
componentDidMount () {
this.setState({
isOverflowed: this.textElement.current.scrollWidth > this.textElement.current.clientWidth
});
}
render () {
const { isOverflowed } = this.state;
return (
<Tooltip
title={this.props.children}
disableHoverListener={!isOverflowed}>
<div
ref={this.textElement}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}>
{this.props.children}
</div>
</Tooltip>
);
}
}
用法示例:
<OverflowTip>
some long text here that may get truncated based on space
</OverflowTip>
一個麻煩是如果元素的空間在頁面中動態變化(例如頁面調整大小或動態 DOM 變化),它不會確認新空間並重新計算它是否溢出。
其他工具提示庫(如 Tippy)有一個方法,當嘗試打開工具提示時會觸發該方法。 這是進行溢出檢查的理想場所,因為它始終有效,無論文本元素的 DOM 寬度是否已更改。 不幸的是,使用 Material UI 提供的 API 來做到這一點比較麻煩。
基於 benjamin.keen 的回答,這是他的代碼的功能版本:
import React, { useRef, useState, useEffect } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
const OverflowTip = ({ children }) => {
const [isOverflowed, setIsOverflow] = useState(false);
const textElementRef = useRef();
useEffect(() => {
setIsOverflow(textElementRef.current.scrollWidth > textElementRef.current.clientWidth);
}, []);
return (
<Tooltip title={children} disableHoverListener={!isOverflowed}>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{children}
</div>
</Tooltip>
);
};
我不認為你需要進入任何副作用鈎子。 最上面的帖子建議在 window 上放置一個事件監聽器,它會在每次鼠標移動事件時觸發。 我們可以只定義一些回調並將它們傳遞給onMouseEnter
和onMouseLeave
import React, { useState, MouseEvent } from "react";
import Tooltip, { TooltipProps } from "@mui/material/Tooltip";
export const OverflowTooltip = ({ children, ...props }: TooltipProps) => {
const [tooltipEnabled, setTooltipEnabled] = useState(false);
const handleShouldShow = ({ currentTarget }: MouseEvent<Element>) => {
if (currentTarget.scrollWidth > currentTarget.clientWidth) {
setTooltipEnabled(true);
}
};
const hideTooltip = () => setTooltipEnabled(false);
return (
<Tooltip
onMouseEnter={handleShouldShow}
onMouseLeave={hideTooltip}
disableHoverListener={!tooltipEnabled}
{...props}
>
<div
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{children}
</div>
{children}
</Tooltip>
);
};
基於@Dheeraj 的回答——這與他的組件非常接近,但在類型腳本版本中,更有意義的道具名稱:
import React, { useRef, useEffect, useState } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
interface Props {
tooltip: string;
text: string;
}
const OverflowTooltip = (props: Props) => {
const textElementRef = useRef<HTMLInputElement | null>(null);
const compareSize = () => {
const compare =
textElementRef.current.scrollWidth > textElementRef.current.clientWidth;
setHover(compare);
};
useEffect(() => {
compareSize();
window.addEventListener('resize', compareSize);
}, []);
useEffect(() => () => {
window.removeEventListener('resize', compareSize);
}, []);
const [hoverStatus, setHover] = useState(false);
return (
<Tooltip
title={props.tooltip}
interactive
disableHoverListener={!hoverStatus}
>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{props.text}
</div>
</Tooltip>
);
};
export default OverflowTooltip;
我們像這樣使用它:
<OverflowTooltip
tooltip={'tooltip message here'}
text={'very long text here'}
/>
如果有人需要 TypScript 版本:
import { Tooltip, Typography, TypographyProps } from "@mui/material";
import { FC, ReactChild, useEffect, useRef, useState } from "react";
export interface OverflowTypograpyProps extends TypographyProps {
children: ReactChild;
}
export const OverflowTypograpy: FC<OverflowTypograpyProps> = ({
children,
...props
}) => {
const ref = useRef<HTMLSpanElement>(null);
const [tooltipEnabled, setTooltipEnabled] = useState(false);
useEffect(() => {
const compareSize = () => {
if (ref.current) {
const compare = ref.current.scrollWidth > ref.current.clientWidth;
setTooltipEnabled(compare);
}
};
compareSize();
window.addEventListener("resize", compareSize);
return () => window.removeEventListener("resize", compareSize);
}, []);
return (
<Tooltip title={children} disableHoverListener={!tooltipEnabled}>
<Typography
ref={ref}
noWrap
overflow="hidden"
textOverflow="ellipsis"
{...props}
>
{children}
</Typography>
</Tooltip>
);
};
定義文本是否溢出的方法在接受的答案中存在缺陷。 由於
scrollWidth<\/code>和
clientWidth<\/code>返回四舍五入的整數值,當它們之間的差異很小時,我們將得到相等的值,並且 tooltip 將不起作用。
問題是省略號也算作
clientWidth<\/code> ,所以當我們只有一個或 tho 個字符溢出時,我們會看到省略號,但
scrollWidth<\/code>和
clientWidth<\/code>會相等。
以下是對我
scrollWidth<\/code>的解決方案,用於確定具有分數精度的
scrollWidth<\/code>和
clientWidth<\/code>並解決了此問題:
import React, { useRef, useState, useEffect } from 'react';
import { Tooltip } from '@material-ui/core';
const OverflowTooltip = ({ children }) => {
const textElementRef = useRef();
const checkOverflow = () => {
// Using getBoundingClientRect, instead of scrollWidth and clientWidth, to get width with fractional accuracy
const clientWidth = textElementRef.current.getBoundingClientRect().width
textElementRef.current.style.overflow = 'visible';
const contentWidth = textElementRef.current.getBoundingClientRect().width
textElementRef.current.style.overflow = 'hidden';
setIsOverflow(contentWidth > clientWidth);
}
useEffect(() => {
checkOverflow();
window.addEventListener('resize', checkOverflow)
return () => {
window.removeEventListener('resize', checkOverflow)
}
}, []);
const [isOverflowed, setIsOverflow] = useState(false);
return (
<Tooltip title={children} disableHoverListener={!isOverflowed}>
<span ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{children}
</span>
</Tooltip>
);
};
export default OverflowTooltip
如果您只想在內容溢出時顯示工具提示,這將起作用。
需要
useEffect()<\/code>是因為
ref.current<\/code>最初為 null,但是當組件掛載時,它會被設置,您可以基於它獲取 html 元素。
interface MyInterface {
content: Content;
}
export const MyComponent: React.FC<MyInterface> = ({ content }) => {
const ref = useRef(null);
const [showTooltip, setShowTooltip] = useState(false);
useEffect(() => {
if (!ref.current) return;
const div = ref.current as HTMLDivElement;
const isOverflow = div.offsetWidth < div.scrollWidth;
setShowTooltip(isOverflow);
}, []);
const renderContent = () => (
<div ref={ref}>
content
</div>
);
return (
<>
{ref.current && showTooltip ? (
<Tooltip title={content.value}>
{renderContent()}
</Tooltip>
) : (
renderContent()
)}
</>
);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.