Panel.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { Megaphone } from 'lucide-react'
  2. import { forwardRef, type PropsWithChildren, type ReactNode } from 'react'
  3. import { Badge, Button, cn, Loading } from 'ui'
  4. interface PanelProps {
  5. className?: string
  6. id?: string
  7. footer?: ReactNode
  8. loading?: boolean
  9. noMargin?: boolean
  10. title?: ReactNode | false
  11. wrapWithLoading?: boolean
  12. noHideOverflow?: boolean
  13. titleClasses?: string
  14. footerClasses?: string
  15. }
  16. /**
  17. * @deprecated Use Card component from ui package instead
  18. */
  19. function Panel(props: PropsWithChildren<PanelProps>) {
  20. const content = (
  21. <div
  22. className={cn(
  23. 'bg-surface-100',
  24. 'rounded-md border shadow-xs',
  25. props.noHideOverflow ? '' : 'overflow-hidden',
  26. props.noMargin ? '' : 'mb-4 md:mb-8',
  27. props.className
  28. )}
  29. id={props.id}
  30. >
  31. {props.title && (
  32. <div
  33. className={cn(
  34. 'bg-surface-100 border-b border-default flex items-center px-card py-4',
  35. props.titleClasses
  36. )}
  37. >
  38. {props.title}
  39. </div>
  40. )}
  41. {props.children}
  42. {props.footer && <Footer className={props.footerClasses}>{props.footer}</Footer>}
  43. </div>
  44. )
  45. if (props.wrapWithLoading === false) {
  46. return content
  47. }
  48. return <Loading active={Boolean(props.loading)}>{content}</Loading>
  49. }
  50. function Content({ children, className }: { children: ReactNode; className?: string | false }) {
  51. return <div className={cn('px-card py-4', className)}>{children}</div>
  52. }
  53. function Footer({ children, className }: { children: ReactNode; className?: string }) {
  54. return (
  55. <div className={cn('bg-surface-100 border-t border-default', className)}>
  56. <div className="flex h-12 items-center px-card">{children}</div>
  57. </div>
  58. )
  59. }
  60. const PanelNotice = forwardRef<
  61. HTMLDivElement,
  62. {
  63. className?: string | false
  64. title?: string
  65. description?: ReactNode
  66. href?: string
  67. buttonText?: string
  68. layout?: 'horizontal' | 'vertical'
  69. badgeLabel?: string
  70. }
  71. >(
  72. (
  73. {
  74. className,
  75. title,
  76. description,
  77. href,
  78. buttonText,
  79. layout = 'horizontal',
  80. badgeLabel,
  81. ...props
  82. },
  83. ref
  84. ) => {
  85. return (
  86. <div
  87. ref={ref}
  88. {...props}
  89. className={cn(
  90. 'relative px-card py-5 bg-studio flex flex-col lg:flex-row lg:justify-between gap-6 overflow-hidden lg:items-center',
  91. layout === 'vertical' && 'flex-col! items-start! gap-y-2',
  92. className
  93. )}
  94. >
  95. <div
  96. className="absolute inset-0 mt-[-5px]"
  97. style={{
  98. backgroundImage: `
  99. linear-gradient(to right, hsl(var(--background-200)/1) 0%, hsl(var(--background-200)/1) 30%, hsl(var(--background-200)/0) 100%),
  100. linear-gradient(to right, hsl(var(--border-default)/0.33) 1px, transparent 1px),
  101. linear-gradient(to bottom, hsl(var(--border-default)/0.33) 1px, transparent 1px)
  102. `,
  103. backgroundSize: '100% 100%, 15px 15px, 15px 15px',
  104. backgroundPosition: '0 0, 0 0, 0 0',
  105. }}
  106. />
  107. <div className="relative flex flex-col gap-y-2">
  108. <div className="flex flex-row items-center -space-x-px">
  109. <Badge
  110. variant={'default'}
  111. className="rounded-r-none pr-2 shrink-0 gap-1.5 border-dashed bg-surface-400/0 text-foreground-lighter"
  112. >
  113. <Megaphone size={16} strokeWidth={1.2} />
  114. <span className="text-foreground-lighter">{badgeLabel ?? 'Upcoming change'}</span>
  115. </Badge>
  116. <Badge
  117. variant="default"
  118. className="rounded-l-none shrink-0 gap-1.5 bg-surface-400/0 text-foreground-lighter border-l-0"
  119. >
  120. <span className="text-foreground text-xs">{title}</span>
  121. </Badge>
  122. </div>
  123. {description && (
  124. <div className="text-foreground-light text-sm flex flex-col gap-0">
  125. <div className="prose text-xs max-w-none [&_p]:mt-2 [&_p]:mb-0">{description}</div>
  126. </div>
  127. )}
  128. </div>
  129. {href && (
  130. <Button size="tiny" type="default" className="text-xs" asChild>
  131. <a href={href} target="_blank" rel="noreferrer noopener">
  132. {buttonText ?? 'Read the accouncement'}
  133. </a>
  134. </Button>
  135. )}
  136. </div>
  137. )
  138. }
  139. )
  140. PanelNotice.displayName = 'PanelNotice'
  141. Panel.Content = Content
  142. Panel.Footer = Footer
  143. Panel.Notice = PanelNotice
  144. export default Panel