48 lines · 1.3 KB · TypeScript
RawDownload
import { useId, useState } from "react"; import { cn } from "../../lib/utils"; /** * Long error text clamped to a few lines with a toggle to reveal the rest. * Short single-line text renders as-is without the toggle. */export function ExpandableText({  text,  className,  collapsedClassName = "line-clamp-3",  expandLabel = "Show full error",}: {  text: string;  className?: string;  collapsedClassName?: string;  expandLabel?: string;}) {  const textId = useId();  const [expanded, setExpanded] = useState(false);  const canExpand = text.length > 180 || text.includes("\n");   return (    <div className={cn("min-w-0", className)}>      <div        id={textId}        className={cn(          "whitespace-pre-wrap break-words",          !expanded && canExpand ? collapsedClassName : null,        )}      >        {text}      </div>      {canExpand ? (        <button          type="button"          aria-expanded={expanded}          aria-controls={textId}          className="cursor-pointer mt-1 text-[11px] font-medium text-foreground/70 underline-offset-2 hover:text-foreground hover:underline"          onClick={() => setExpanded((value) => !value)}        >          {expanded ? "Show less" : expandLabel}        </button>      ) : null}    </div>  );}