Files
twenty/packages/twenty-ui/src/display/tooltip/OverflowingTextWithTooltip.tsx
Baptiste Devessier 6c2d64dcb2 Fix e2e tests (#11577)
2025-04-15 12:16:38 +02:00

167 lines
4.4 KiB
TypeScript

import { styled } from '@linaria/react';
import { useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { THEME_COMMON } from '@ui/theme';
import { isDefined } from 'twenty-shared/utils';
import { AppTooltip, TooltipDelay } from './AppTooltip';
const spacing4 = THEME_COMMON.spacing(4);
const StyledOverflowingMultilineText = styled.div<{
cursorPointer: boolean;
size: 'large' | 'small';
displayedMaxRows: number;
}>`
cursor: ${({ cursorPointer }) => (cursorPointer ? 'pointer' : 'inherit')};
font-family: inherit;
font-size: inherit;
font-weight: inherit;
max-width: 100%;
overflow: hidden;
text-decoration: inherit;
text-overflow: ellipsis;
height: ${({ size }) => (size === 'large' ? spacing4 : 'auto')};
-webkit-line-clamp: ${({ displayedMaxRows }) =>
displayedMaxRows ? displayedMaxRows.toString() : '1'};
display: -webkit-box;
-webkit-box-orient: vertical;
white-space: pre-wrap;
& :hover {
text-overflow: ${({ cursorPointer }) =>
cursorPointer ? 'clip' : 'ellipsis'};
white-space: ${({ cursorPointer }) =>
cursorPointer ? 'normal' : 'nowrap'};
}
`;
const StyledOverflowingText = styled.div<{
cursorPointer: boolean;
size: 'large' | 'small';
}>`
cursor: ${({ cursorPointer }) => (cursorPointer ? 'pointer' : 'inherit')};
font-family: inherit;
font-size: inherit;
font-weight: inherit;
max-width: 100%;
text-decoration: inherit;
text-overflow: ellipsis;
overflow: hidden;
height: ${({ size }) => (size === 'large' ? spacing4 : 'auto')};
white-space: nowrap;
& :hover {
text-overflow: ${({ cursorPointer }) =>
cursorPointer ? 'clip' : 'ellipsis'};
white-space: ${({ cursorPointer }) =>
cursorPointer ? 'normal' : 'nowrap'};
}
`;
const Styledpre = styled.pre`
font-family: inherit;
white-space: pre-wrap;
`;
export const OverflowingTextWithTooltip = ({
size = 'small',
text,
isTooltipMultiline,
displayedMaxRows,
hideTooltip,
}: {
size?: 'large' | 'small';
text: string | null | undefined;
isTooltipMultiline?: boolean;
displayedMaxRows?: number;
hideTooltip?: boolean;
}) => {
const textElementId = `title-id-${+new Date()}`;
const textRef = useRef<HTMLDivElement>(null);
const [isTitleOverflowing, setIsTitleOverflowing] = useState(false);
const [shouldRenderTooltip, setShouldRenderTooltip] = useState(false);
const handleMouseEnter = () => {
const isOverflowing =
(text?.length ?? 0) > 0 && textRef.current
? textRef.current?.scrollHeight > textRef.current?.clientHeight ||
textRef.current.scrollWidth > textRef.current.clientWidth
: false;
setIsTitleOverflowing(isOverflowing);
setShouldRenderTooltip(true);
};
const handleMouseLeave = () => {
setIsTitleOverflowing(false);
setShouldRenderTooltip(false);
};
const handleTooltipClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
event.preventDefault();
};
return (
<>
{isDefined(displayedMaxRows) ? (
<StyledOverflowingMultilineText
data-testid="tooltip"
cursorPointer={isTitleOverflowing}
size={size}
displayedMaxRows={displayedMaxRows}
ref={textRef}
id={textElementId}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{text}
</StyledOverflowingMultilineText>
) : (
<StyledOverflowingText
data-testid="tooltip"
cursorPointer={isTitleOverflowing}
size={size}
ref={textRef}
id={textElementId}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{text}
</StyledOverflowingText>
)}
{shouldRenderTooltip &&
isTitleOverflowing &&
createPortal(
<div onClick={handleTooltipClick}>
<AppTooltip
anchorSelect={`#${textElementId}`}
offset={5}
hidden={!isTitleOverflowing || hideTooltip}
noArrow
place="bottom"
positionStrategy="absolute"
delay={TooltipDelay.mediumDelay}
isOpen={true}
>
{isTooltipMultiline ? (
<Styledpre>{text}</Styledpre>
) : (
`${text || ''}`
)}
</AppTooltip>
</div>,
document.body,
)}
</>
);
};