long-text.tsx 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { useRef, useState } from 'react'
  2. import { cn } from '@/lib/utils'
  3. import {
  4. Popover,
  5. PopoverContent,
  6. PopoverTrigger,
  7. } from '@/components/ui/popover'
  8. import {
  9. Tooltip,
  10. TooltipContent,
  11. TooltipProvider,
  12. TooltipTrigger,
  13. } from '@/components/ui/tooltip'
  14. type LongTextProps = {
  15. children: React.ReactNode
  16. className?: string
  17. contentClassName?: string
  18. }
  19. export function LongText({
  20. children,
  21. className = '',
  22. contentClassName = '',
  23. }: LongTextProps) {
  24. const ref = useRef<HTMLDivElement>(null)
  25. const [isOverflown, setIsOverflown] = useState(false)
  26. // Use ref callback to check overflow when element is mounted
  27. const refCallback = (node: HTMLDivElement | null) => {
  28. ref.current = node
  29. if (node && checkOverflow(node)) {
  30. queueMicrotask(() => setIsOverflown(true))
  31. }
  32. }
  33. if (!isOverflown)
  34. return (
  35. <div ref={refCallback} className={cn('truncate', className)}>
  36. {children}
  37. </div>
  38. )
  39. return (
  40. <>
  41. <div className='hidden sm:block'>
  42. <TooltipProvider delayDuration={0}>
  43. <Tooltip>
  44. <TooltipTrigger asChild>
  45. <div ref={refCallback} className={cn('truncate', className)}>
  46. {children}
  47. </div>
  48. </TooltipTrigger>
  49. <TooltipContent>
  50. <p className={contentClassName}>{children}</p>
  51. </TooltipContent>
  52. </Tooltip>
  53. </TooltipProvider>
  54. </div>
  55. <div className='sm:hidden'>
  56. <Popover>
  57. <PopoverTrigger asChild>
  58. <div ref={refCallback} className={cn('truncate', className)}>
  59. {children}
  60. </div>
  61. </PopoverTrigger>
  62. <PopoverContent className={cn('w-fit', contentClassName)}>
  63. <p>{children}</p>
  64. </PopoverContent>
  65. </Popover>
  66. </div>
  67. </>
  68. )
  69. }
  70. const checkOverflow = (textContainer: HTMLDivElement | null) => {
  71. if (textContainer) {
  72. return (
  73. textContainer.offsetHeight < textContainer.scrollHeight ||
  74. textContainer.offsetWidth < textContainer.scrollWidth
  75. )
  76. }
  77. return false
  78. }