167 lines
4.4 KiB
TypeScript
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,
|
|
)}
|
|
</>
|
|
);
|
|
};
|