Files

120 lines
3.8 KiB
TypeScript

import { debounce } from 'es-toolkit';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import { cn } from 'lib/utils/cn';
import { Heading } from '@luxfi/ui/heading';
import { Skeleton } from '@luxfi/ui/skeleton';
import { Tooltip } from '@luxfi/ui/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import TextAd from 'ui/shared/ad/TextAd';
type Props = {
title: string;
className?: string;
beforeTitle?: React.ReactNode;
afterTitle?: React.ReactNode;
contentAfter?: React.ReactNode;
secondRow?: React.ReactNode;
isLoading?: boolean;
withTextAd?: boolean;
};
const TEXT_MAX_LINES = 1;
const PageTitle = ({ title, contentAfter, withTextAd, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => {
const tooltip = useDisclosure();
const isMobile = useIsMobile();
const [ isTextTruncated, setIsTextTruncated ] = React.useState(false);
const headingRef = React.useRef<HTMLHeadingElement>(null);
const textRef = React.useRef<HTMLSpanElement>(null);
const updatedTruncateState = React.useCallback(() => {
if (!headingRef.current || !textRef.current) {
return;
}
const headingRect = headingRef.current.getBoundingClientRect();
const textRect = textRef.current.getBoundingClientRect();
if ((TEXT_MAX_LINES + 1) * headingRect.height <= textRect.height) {
setIsTextTruncated(true);
} else {
setIsTextTruncated(false);
}
}, []);
React.useEffect(() => {
if (!isLoading) {
updatedTruncateState();
}
}, [ isLoading, updatedTruncateState ]);
React.useEffect(() => {
const handleResize = debounce(updatedTruncateState, 1000);
window.addEventListener('resize', handleResize);
return function cleanup() {
window.removeEventListener('resize', handleResize);
};
}, [ updatedTruncateState ]);
const handleTooltipOpenChange = React.useCallback((details: { open: boolean }) => {
if (details.open) {
tooltip.onOpen();
} else {
tooltip.onClose();
}
}, [ tooltip ]);
return (
<div className={ cn('flex flex-col gap-y-3 mb-6', className) }>
<div className="flex items-center flex-row flex-wrap gap-x-3 gap-y-3">
<div className={ cn('flex items-center max-w-full', isLoading ? 'h-auto lg:h-10' : 'h-auto') }>
{ beforeTitle }
<Skeleton
loading={ isLoading }
className="overflow-hidden"
>
<Tooltip
content={ title }
open={ tooltip.open }
onOpenChange={ handleTooltipOpenChange }
contentProps={{ className: 'max-w-[calc(100vw-32px)] lg:max-w-[500px]' }}
disabled={ !isTextTruncated }
>
<Heading
ref={ headingRef }
level="1"
className="whitespace-normal break-all overflow-hidden text-ellipsis"
style={{
WebkitLineClamp: TEXT_MAX_LINES,
WebkitBoxOrient: 'vertical',
display: '-webkit-box',
}}
onMouseEnter={ tooltip.onOpen }
onMouseLeave={ tooltip.onClose }
onClick={ isMobile ? tooltip.onToggle : undefined }
>
<span ref={ textRef }>
{ title }
</span>
</Heading>
</Tooltip>
</Skeleton>
{ afterTitle }
</div>
{ contentAfter }
{ withTextAd && <TextAd className="order-[-1] lg:order-[100] mb-6 lg:mb-0 ml-auto w-full lg:w-auto"/> }
</div>
{ secondRow && (
<Skeleton loading={ isLoading } alignItems="center" display="flex" className="min-h-10 overflow-hidden empty:hidden">
{ secondRow }
</Skeleton>
) }
</div>
);
};
export default PageTitle;