InformationBox.tsx96 lines · main
| 1 | import { ExternalLink, Maximize2, Minimize2 } from 'lucide-react' |
| 2 | import Link from 'next/link' |
| 3 | import { forwardRef, ReactNode, useState } from 'react' |
| 4 | import { Button } from 'ui' |
| 5 | |
| 6 | interface InformationBoxProps { |
| 7 | icon?: ReactNode |
| 8 | title: ReactNode | string |
| 9 | description?: ReactNode | string |
| 10 | url?: string |
| 11 | urlLabel?: string |
| 12 | defaultVisibility?: boolean |
| 13 | hideCollapse?: boolean |
| 14 | button?: React.ReactNode |
| 15 | className?: string |
| 16 | block?: boolean |
| 17 | } |
| 18 | |
| 19 | /** @deprecated Use `Admonition` from 'ui-patterns' instead. */ |
| 20 | |
| 21 | const InformationBox = forwardRef<HTMLDivElement, InformationBoxProps>( |
| 22 | ( |
| 23 | { |
| 24 | icon, |
| 25 | title, |
| 26 | description, |
| 27 | url, |
| 28 | urlLabel = 'Read more', |
| 29 | defaultVisibility = false, |
| 30 | hideCollapse = false, |
| 31 | button, |
| 32 | className = '', |
| 33 | block = false, |
| 34 | }, |
| 35 | ref |
| 36 | ) => { |
| 37 | const [isExpanded, setIsExpanded] = useState<boolean>(defaultVisibility) |
| 38 | |
| 39 | return ( |
| 40 | <div |
| 41 | ref={ref} |
| 42 | role="alert" |
| 43 | className={`${block ? 'block w-full' : ''} |
| 44 | block w-full rounded-md border bg-surface-300/25 py-3 ${className}`} |
| 45 | > |
| 46 | <div className="flex flex-col px-4"> |
| 47 | <div className="flex items-center justify-between"> |
| 48 | <div className="flex w-full space-x-3 items-center"> |
| 49 | {icon && <span className="text-foreground-lighter">{icon}</span>} |
| 50 | <div className="grow"> |
| 51 | <h5 className="text-foreground">{title}</h5> |
| 52 | </div> |
| 53 | </div> |
| 54 | {description && !hideCollapse ? ( |
| 55 | <div |
| 56 | className="cursor-pointer text-foreground-lighter" |
| 57 | onClick={() => setIsExpanded(!isExpanded)} |
| 58 | > |
| 59 | {isExpanded ? ( |
| 60 | <Minimize2 size={14} strokeWidth={1.5} /> |
| 61 | ) : ( |
| 62 | <Maximize2 size={14} strokeWidth={1.5} /> |
| 63 | )} |
| 64 | </div> |
| 65 | ) : null} |
| 66 | </div> |
| 67 | {(description || url || button) && ( |
| 68 | <div |
| 69 | className={`flex flex-col space-y-3 overflow-hidden transition-all ${ |
| 70 | isExpanded ? 'mt-3' : '' |
| 71 | }`} |
| 72 | style={{ maxHeight: isExpanded ? 500 : 0 }} |
| 73 | > |
| 74 | <div className="text-foreground-light text-sm">{description}</div> |
| 75 | |
| 76 | {url && ( |
| 77 | <div> |
| 78 | <Button asChild type="default" icon={<ExternalLink />}> |
| 79 | <Link href={url} target="_blank" rel="noreferrer"> |
| 80 | {urlLabel} |
| 81 | </Link> |
| 82 | </Button> |
| 83 | </div> |
| 84 | )} |
| 85 | |
| 86 | {button && <div>{button}</div>} |
| 87 | </div> |
| 88 | )} |
| 89 | </div> |
| 90 | </div> |
| 91 | ) |
| 92 | } |
| 93 | ) |
| 94 | |
| 95 | InformationBox.displayName = 'InformationBox' |
| 96 | export default InformationBox |