expandable-code.tsx 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. "use client";
  2. import { useState, useRef, useEffect } from "react";
  3. interface ExpandableCodeProps {
  4. children: React.ReactNode;
  5. maxHeight?: number;
  6. }
  7. export function ExpandableCode({
  8. children,
  9. maxHeight = 300,
  10. }: ExpandableCodeProps) {
  11. const [isExpanded, setIsExpanded] = useState(false);
  12. const [needsExpansion, setNeedsExpansion] = useState(false);
  13. const contentRef = useRef<HTMLDivElement>(null);
  14. useEffect(() => {
  15. if (contentRef.current) {
  16. setNeedsExpansion(contentRef.current.scrollHeight > maxHeight);
  17. }
  18. }, [maxHeight]);
  19. return (
  20. <div className="relative">
  21. <div
  22. ref={contentRef}
  23. className="overflow-hidden transition-[max-height] duration-300"
  24. style={{
  25. maxHeight: isExpanded || !needsExpansion ? "none" : maxHeight,
  26. }}
  27. >
  28. {children}
  29. </div>
  30. {needsExpansion && !isExpanded && (
  31. <>
  32. <div className="absolute bottom-0 left-0 right-0 h-24 bg-gradient-to-t from-neutral-100 dark:from-[#0a0a0a] to-transparent pointer-events-none" />
  33. <button
  34. onClick={() => setIsExpanded(true)}
  35. className="absolute bottom-3 left-1/2 -translate-x-1/2 px-3 py-1.5 text-xs font-medium text-muted-foreground bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700 rounded-md transition-colors"
  36. >
  37. Show all
  38. </button>
  39. </>
  40. )}
  41. </div>
  42. );
  43. }