There are two primary components to address in this issue:
- Implementing ellipsis for text overflow based on vertical overflow to allow for multiple lines without unlimited expansion
- Detecting vertical overflow and incorporating Tooltip functionality in such cases
I am skeptical that the solution proposed by FrankerZ will effectively display the ellipsis. From what I gather, text-overflow: ellipsis
typically caters to horizontal overflow which necessitates confining the text to a single line.
I stumbled upon a method for achieving ellipsis on vertical overflow here. However, extensive adjustments might be needed when integrating this approach within Material UI components (such as ListItem) that introduce additional CSS rules, potentially leading to complexity that outweighs the benefits. This solution involves allocating space for the ellipsis at the end of each text line, which may not be ideal. While there are alternative solutions available, I have personally not experimented with any besides this one.
The second aspect (detecting overflow) appears to be more straightforward and can be managed using
divRef.current.scrollHeight > divRef.current.offsetHeight
. This method was derived from various sources suggesting a similar approach based on width. Although I have only applied this technique today, it seems functional (although I have not extensively tested it across different browsers aside from Chrome).
The example provided utilizes hooks for syntax convenience. If you are not using the alpha version, you will need to translate the state, ref, and effect mechanisms into their class-based equivalents. Additionally, the current implementation does not address window resizing, which would require a reassessment of the Tooltip's necessity. With these considerations in mind, this code snippet aims to guide you towards a viable solution.
Below is the code snippet:
import React, { useRef, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import Tooltip from "@material-ui/core/Tooltip";
import { withStyles } from "@material-ui/core/styles";
/*
CSS from http://hackingui.com/front-end/a-pure-css-solution-for-multiline-text-truncation/
Additional syntax help from https://stackoverflow.com/questions/40965977/cant-target-before-pseudo-selector-in-jss
*/
const styles = theme => ({
listItem: {
maxWidth: "20rem",
overflow: "hidden",
position: "relative",
lineHeight: "1.2em",
maxHeight: "2.4em",
textAlign: "justify",
marginRight: "-1em",
paddingRight: "1em",
borderBottom: "1px solid",
marginBottom: "0.5em",
"&&:before": {
content: '"..."',
position: "absolute",
right: 0,
bottom: 0
},
"&&:after": {
content: '""',
position: "absolute",
right: 0,
width: "1em",
height: "1em",
marginTop: "0.2em",
background: "white"
}
}
});
const data = [
"Some short text",
"Some text that is a little bit longer",
"Some text that will need to wrap but still fits on two lines",
"Some text that will overflow because it is more than just two lines worth when the maxWidth is set at 20rem.",
"A massive range of hammer drill machines and rotary hammers for SDS-plus accessory tools, designed for higher performance drilling and longer life - for easy drilling in concrete and other materials."
];
const TooltipDiv = props => {
const divRef = useRef(null);
const [allowTooltip, setAllowTooltip] = useState(false);
useEffect(() => {
if (
!allowTooltip &&
divRef.current.scrollHeight > divRef.current.offsetHeight
) {
setAllowTooltip(true);
}
}, []);
if (allowTooltip) {
return (
<Tooltip title={props.text}>
<div ref={divRef} className={props.className}>
{props.text}
</div>
</Tooltip>
);
}
return (
<div ref={divRef} className={props.className}>
{props.text}
</div>
);
};
function App(props) {
return (
<>
{data.map(text => {
return (
<>
<TooltipDiv text={text} className={props.classes.listItem} />
</>
);
})}
</>
);
}
const StyledApp = withStyles(styles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<StyledApp />, rootElement);
You can view the code in action here.