Scaffold.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { forwardRef, HTMLAttributes } from 'react'
  2. import { cn } from 'ui'
  3. export const MAX_WIDTH_CLASSES = 'mx-auto w-full max-w-[1200px]'
  4. export const PADDING_CLASSES = 'px-4 @lg:px-6 @xl:px-10'
  5. export const MAX_WIDTH_CLASSES_COLUMN = 'min-w-[420px]'
  6. /**
  7. * Controls the width and padding of the UI contents. Typically used as the top level child immediately after a layout.
  8. *
  9. * e.g:```
  10. * <Layout>
  11. * <ScaffoldContainer>
  12. * {children}
  13. * </ScaffoldContainer>
  14. * </Layout>```
  15. */
  16. export const ScaffoldContainer = forwardRef<
  17. HTMLDivElement,
  18. HTMLAttributes<HTMLDivElement> & {
  19. bottomPadding?: boolean
  20. size?: 'small' | 'default' | 'large' | 'full'
  21. }
  22. >(({ className, bottomPadding, size = 'default', ...props }, ref) => {
  23. const maxWidthClass = {
  24. small: 'max-w-[768px]',
  25. default: 'max-w-[1200px]',
  26. large: 'max-w-[1600px]',
  27. full: 'max-w-none',
  28. }[size]
  29. return (
  30. <div
  31. ref={ref}
  32. {...props}
  33. className={cn(
  34. 'mx-auto w-full @container',
  35. maxWidthClass,
  36. PADDING_CLASSES,
  37. bottomPadding && 'pb-16',
  38. className
  39. )}
  40. />
  41. )
  42. })
  43. /**
  44. * Top most header for a page
  45. *
  46. * Note: In most cases, we won't use this directly in pages. Header title and description can be controlled via
  47. * the PageLayout component, which uses the PageHeader component, and in turn uses this ScaffoldHeader component
  48. */
  49. export const ScaffoldHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  50. ({ className, ...props }, ref) => {
  51. return (
  52. <header {...props} ref={ref} className={cn('w-full', 'flex-col gap-3 py-6', className)} />
  53. )
  54. }
  55. )
  56. /**
  57. * Title for the top most header for a page
  58. *
  59. * Note: In most cases, we won't use this directly in pages. Header title and description can be controlled via
  60. * the PageLayout component, which uses the PageHeader component, and in turn uses this ScaffoldTitle component
  61. */
  62. export const ScaffoldTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
  63. ({ className, ...props }, ref) => {
  64. return <h1 ref={ref} {...props} className={cn(className)} />
  65. }
  66. )
  67. /**
  68. * Description for the top most header for a page
  69. *
  70. * Note: In most cases, we won't use this directly in pages. Header title and description can be controlled via
  71. * the PageLayout component, which uses the PageHeader component, and in turn uses this ScaffoldDescription component
  72. */
  73. export const ScaffoldDescription = forwardRef<
  74. HTMLHeadingElement,
  75. HTMLAttributes<HTMLHeadingElement>
  76. >(({ className, ...props }, ref) => {
  77. return <p ref={ref} {...props} className={cn('text-sm text-foreground-light', className)} />
  78. })
  79. export const ScaffoldSection = forwardRef<
  80. HTMLDivElement,
  81. HTMLAttributes<HTMLDivElement> & { isFullWidth?: boolean; topPadding?: boolean }
  82. >(({ className, isFullWidth, topPadding, ...props }, ref) => {
  83. return (
  84. <div
  85. ref={ref}
  86. {...props}
  87. className={cn(
  88. 'flex flex-col first:pt-12 py-6',
  89. isFullWidth ? 'w-full' : 'gap-3 @md:grid-cols-12 @lg:grid',
  90. className
  91. )}
  92. />
  93. )
  94. })
  95. /**
  96. * Horizontal divider between ScaffoldSections
  97. */
  98. export const ScaffoldDivider = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  99. ({ className, ...props }, ref) => {
  100. return <div ref={ref} {...props} className={cn('w-full h-px bg-border shrink-0', className)} />
  101. }
  102. )
  103. /**
  104. * Title for a page section
  105. */
  106. export const ScaffoldSectionTitle = forwardRef<
  107. HTMLHeadingElement,
  108. HTMLAttributes<HTMLHeadingElement>
  109. >(({ className, ...props }, ref) => {
  110. return <h3 ref={ref} {...props} className={cn('text-foreground text-xl', className)} />
  111. })
  112. /**
  113. * Description for a page section
  114. */
  115. export const ScaffoldSectionDescription = forwardRef<
  116. HTMLParagraphElement,
  117. HTMLAttributes<HTMLParagraphElement>
  118. >(({ className, ...props }, ref) => {
  119. return <p ref={ref} {...props} className={cn('text-sm text-foreground-light', className)} />
  120. })
  121. /**
  122. * Child of ScaffoldSection - Left hand column for a section
  123. */
  124. export const ScaffoldSectionDetail = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  125. ({ className, children, title, ...props }, ref) => {
  126. return (
  127. <div ref={ref} {...props} className={cn('col-span-4 xl:col-span-5 prose text-sm', className)}>
  128. {title && <h2>{title}</h2>}
  129. {children}
  130. </div>
  131. )
  132. }
  133. )
  134. /**
  135. * Child of ScaffoldSection - Right hand column for a section
  136. */
  137. export const ScaffoldSectionContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  138. ({ className, ...props }, ref) => {
  139. return (
  140. <div
  141. ref={ref}
  142. {...props}
  143. className={cn('col-span-8 xl:col-span-7', 'flex flex-col gap-6', className)}
  144. />
  145. )
  146. }
  147. )
  148. /**
  149. * TBD: Provide better JSDocs on how to use this component
  150. *
  151. * Table and filters
  152. */
  153. export const ScaffoldFilterAndContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  154. ({ className, ...props }, ref) => {
  155. return (
  156. <div ref={ref} {...props} className={cn('flex flex-col gap-3 items-center', className)} />
  157. )
  158. }
  159. )
  160. /**
  161. * TBD: Provide better JSDocs on how to use this component
  162. *
  163. * Actions Group
  164. */
  165. export const ScaffoldActionsContainer = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  166. ({ className, ...props }, ref) => {
  167. return <div ref={ref} {...props} className={cn('flex w-full items-center', className)} />
  168. }
  169. )
  170. /**
  171. * TBD: Provide better JSDocs on how to use this component
  172. *
  173. * Actions Group
  174. */
  175. export const ScaffoldActionsGroup = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  176. ({ className, ...props }, ref) => {
  177. return <div ref={ref} {...props} className={cn('flex flex-row gap-3', className)} />
  178. }
  179. )
  180. /**
  181. * For a single column section - currently only used for vercel integration
  182. */
  183. export const ScaffoldColumn = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  184. ({ className, ...props }, ref) => {
  185. return (
  186. <div
  187. ref={ref}
  188. {...props}
  189. className={cn('flex flex-col gap-3', MAX_WIDTH_CLASSES_COLUMN, className)}
  190. />
  191. )
  192. }
  193. )
  194. /**
  195. * For older layouts - eventually to be replaced with ScaffoldContainer component
  196. * @deprecated
  197. */
  198. export const ScaffoldContainerLegacy = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  199. ({ className, ...props }, ref) => {
  200. return (
  201. <div
  202. ref={ref}
  203. {...props}
  204. className={cn(MAX_WIDTH_CLASSES, PADDING_CLASSES, 'my-8 flex flex-col gap-8', className)}
  205. />
  206. )
  207. }
  208. )
  209. ScaffoldHeader.displayName = 'ScaffoldHeader'
  210. ScaffoldTitle.displayName = 'ScaffoldTitle'
  211. ScaffoldDescription.displayName = 'ScaffoldDescription'
  212. ScaffoldContainer.displayName = 'ScaffoldContainer'
  213. ScaffoldDivider.displayName = 'ScaffoldDivider'
  214. ScaffoldSection.displayName = 'ScaffoldSection'
  215. ScaffoldColumn.displayName = 'ScaffoldColumn'
  216. ScaffoldSectionDetail.displayName = 'ScaffoldSectionDetail'
  217. ScaffoldSectionContent.displayName = 'ScaffoldSectionContent'
  218. ScaffoldFilterAndContent.displayName = 'ScaffoldFilterAndContent'
  219. ScaffoldActionsContainer.displayName = 'ScaffoldActionsContainer'
  220. ScaffoldActionsGroup.displayName = 'ScaffoldActionsGroup'
  221. ScaffoldContainerLegacy.displayName = 'ScaffoldContainerLegacy'
  222. ScaffoldSectionTitle.displayName = 'ScaffoldSectionTitle'
  223. ScaffoldSectionDescription.displayName = 'ScaffoldSectionDescription'