Collapsible.tsx 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. import { useState } from 'react';
  2. import type { ReactNode } from 'react';
  3. import { ChevronDown } from 'lucide-react';
  4. interface CollapsibleProps {
  5. summary: ReactNode;
  6. children: ReactNode;
  7. defaultOpen?: boolean;
  8. className?: string;
  9. summaryClassName?: string;
  10. }
  11. /**
  12. * Lightweight disclosure used for densifying the Settings page.
  13. * Renders a clickable summary row and animates open/close via a simple
  14. * display swap (no height animation — keeps it snappy and layout-stable).
  15. */
  16. export function Collapsible({
  17. summary,
  18. children,
  19. defaultOpen = false,
  20. className = '',
  21. summaryClassName = '',
  22. }: CollapsibleProps) {
  23. const [open, setOpen] = useState(defaultOpen);
  24. return (
  25. <div className={className}>
  26. <button
  27. type="button"
  28. onClick={() => setOpen(o => !o)}
  29. className={`w-full flex items-center justify-between gap-2 text-left ${summaryClassName}`}
  30. aria-expanded={open}
  31. >
  32. <div className="flex-1 min-w-0">{summary}</div>
  33. <ChevronDown
  34. className={`w-4 h-4 text-bambu-gray flex-shrink-0 transition-transform ${open ? 'rotate-180' : ''}`}
  35. />
  36. </button>
  37. {open && <div className="mt-3">{children}</div>}
  38. </div>
  39. );
  40. }