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