Skip to content

Commit 07e7d82

Browse files
committed
feat: implement BlockLayout and middleware for caching and static rendering
1 parent 182e281 commit 07e7d82

File tree

5 files changed

+131
-27
lines changed

5 files changed

+131
-27
lines changed

.prettierrc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
"singleQuote": true,
77
"trailingComma": "es5",
88
"jsxBracketSameLine": true,
9-
"quoteProps" : "as-needed",
10-
"jsxSingleQuote" : false,
11-
"bracketSpacing" : true,
12-
"bracketSameLine" : false,
13-
"useTabs" : false,
9+
"quoteProps": "as-needed",
10+
"jsxSingleQuote": false,
11+
"bracketSpacing": true,
12+
"bracketSameLine": false,
13+
"useTabs": false,
14+
"singleAttributePerLine": true,
1415
"plugins": [
1516
"prettier-plugin-tailwindcss"
1617
],

app/[category]/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ interface PageProps {
66
params: Promise<{ category: string }>
77
}
88

9+
export const dynamic = 'force-static'
10+
export const revalidate = 3600
11+
912
export async function generateStaticParams() {
1013
return categories.map((category) => ({
1114
category: category,
@@ -19,8 +22,6 @@ export async function generateMetadata({ params }: PageProps) {
1922
}
2023
}
2124

22-
export const revalidate = 3600
23-
2425
export default async function CategoryPage({ params }: PageProps) {
2526
const { category } = await params
2627
const categoryBlocks = blocks.filter((b) => b.category === category)
@@ -40,7 +41,10 @@ export default async function CategoryPage({ params }: PageProps) {
4041
</section>
4142

4243
{categoryBlocks.map((block, index) => (
43-
<BlockPreview {...block} key={index} />
44+
<BlockPreview
45+
{...block}
46+
key={index}
47+
/>
4448
))}
4549
</>
4650
)

app/preview/layout.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const dynamic = 'force-static'
2+
export const revalidate = 3600 // Cache for 1 hour
3+
4+
export default function BlockLayout({
5+
children,
6+
}: Readonly<{
7+
children: React.ReactNode
8+
}>) {
9+
return <>{children}</>
10+
}

components/BlockPreview.tsx

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ export const BlockPreview: React.FC<BlockPreviewProps> = ({ code, preview, title
6262
return (
6363
<section className="group mb-16 border-b [--color-border:color-mix(in_oklab,var(--color-zinc-200)_75%,transparent)] dark:[--color-border:color-mix(in_oklab,var(--color-zinc-800)_60%,transparent)]">
6464
<div className="relative border-y">
65-
<div aria-hidden className="absolute inset-x-4 -top-14 bottom-0 mx-auto max-w-7xl lg:inset-x-0">
65+
<div
66+
aria-hidden
67+
className="absolute inset-x-4 -top-14 bottom-0 mx-auto max-w-7xl lg:inset-x-0">
6668
<div className="to-(--color-border) absolute bottom-0 left-0 top-0 w-px bg-gradient-to-b from-transparent to-75%"></div>
6769
<div className="to-(--color-border) absolute bottom-0 right-0 top-0 w-px bg-gradient-to-b from-transparent to-75%"></div>
6870
</div>
@@ -72,50 +74,95 @@ export const BlockPreview: React.FC<BlockPreviewProps> = ({ code, preview, title
7274
{code && (
7375
<>
7476
<RadioGroup.Root className="flex gap-0.5">
75-
<RadioGroup.Item onClick={() => setMode('preview')} aria-label="Block preview" value="100" checked={mode == 'preview'} className={radioItem}>
77+
<RadioGroup.Item
78+
onClick={() => setMode('preview')}
79+
aria-label="Block preview"
80+
value="100"
81+
checked={mode == 'preview'}
82+
className={radioItem}>
7683
<Eye className="size-3.5 sm:opacity-50" />
7784
<span className="hidden text-[13px] sm:block">Preview</span>
7885
</RadioGroup.Item>
7986

80-
<RadioGroup.Item onClick={() => setMode('code')} aria-label="Code" value="0" checked={mode == 'code'} className={radioItem}>
87+
<RadioGroup.Item
88+
onClick={() => setMode('code')}
89+
aria-label="Code"
90+
value="0"
91+
checked={mode == 'code'}
92+
className={radioItem}>
8193
<Code2 className="size-3.5 sm:opacity-50" />
8294
<span className="hidden text-[13px] sm:block">Code</span>
8395
</RadioGroup.Item>
8496
</RadioGroup.Root>
8597

86-
<Separator orientation="vertical" className="hidden !h-4 lg:block" />
98+
<Separator
99+
orientation="vertical"
100+
className="hidden !h-4 lg:block"
101+
/>
87102
</>
88103
)}
89104
{previewOnly && (
90105
<>
91106
{' '}
92107
<span className="ml-2 text-sm capitalize">{title}</span>
93-
<Separator orientation="vertical" className="!h-4" />{' '}
108+
<Separator
109+
orientation="vertical"
110+
className="!h-4"
111+
/>{' '}
94112
</>
95113
)}
96-
<Button asChild variant="ghost" size="sm" className="size-8">
97-
<Link href={preview} passHref target="_blank">
114+
<Button
115+
asChild
116+
variant="ghost"
117+
size="sm"
118+
className="size-8">
119+
<Link
120+
href={preview}
121+
passHref
122+
target="_blank">
98123
<Maximize className="size-4" />
99124
</Link>
100125
</Button>
101-
<Separator orientation="vertical" className="hidden !h-4 lg:block" />
126+
<Separator
127+
orientation="vertical"
128+
className="hidden !h-4 lg:block"
129+
/>
102130
<span className="text-muted-foreground hidden text-sm lg:block">{width < MDSIZE ? 'Mobile' : width < LGSIZE ? 'Tablet' : 'Desktop'}</span>{' '}
103131
</div>
104132

105133
<div className="flex items-center gap-2">
106134
{code && (
107135
<>
108-
<Button onClick={cliCopy} size="sm" className="size-8 shadow-none md:w-fit" variant="outline" aria-label="copy code">
136+
<Button
137+
onClick={cliCopy}
138+
size="sm"
139+
className="size-8 shadow-none md:w-fit"
140+
variant="outline"
141+
aria-label="copy code">
109142
{cliCopied ? <Check className="size-4" /> : <Terminal className="!size-3.5" />}
110143
<span className="hidden font-mono text-xs md:block">
111144
pnpm dlx shadcn@canary add {category}-{titleToNumber(title)}
112145
</span>
113146
</Button>
114-
<Separator className="!h-4" orientation="vertical" />
115-
<OpenInV0Button {...{ title, category }} block={`${category}-${titleToNumber(title)}`} />
116-
<Separator className="!h-4" orientation="vertical" />
117-
118-
<Button onClick={copy} size="sm" variant="ghost" aria-label="copy code" className="size-8">
147+
<Separator
148+
className="!h-4"
149+
orientation="vertical"
150+
/>
151+
<OpenInV0Button
152+
{...{ title, category }}
153+
block={`${category}-${titleToNumber(title)}`}
154+
/>
155+
<Separator
156+
className="!h-4"
157+
orientation="vertical"
158+
/>
159+
160+
<Button
161+
onClick={copy}
162+
size="sm"
163+
variant="ghost"
164+
aria-label="copy code"
165+
className="size-8">
119166
{copied ? <Check className="size-4" /> : <Copy className="!size-3.5" />}
120167
</Button>
121168
</>
@@ -125,14 +172,19 @@ export const BlockPreview: React.FC<BlockPreviewProps> = ({ code, preview, title
125172
</div>
126173

127174
<div className="relative">
128-
<div aria-hidden className="absolute inset-x-4 -bottom-14 mx-auto h-14 max-w-7xl lg:inset-x-0">
175+
<div
176+
aria-hidden
177+
className="absolute inset-x-4 -bottom-14 mx-auto h-14 max-w-7xl lg:inset-x-0">
129178
<div className="from-(--color-border) absolute bottom-0 left-0 top-0 w-px bg-gradient-to-b"></div>
130179
<div className="from-(--color-border) absolute bottom-0 right-0 top-0 w-px bg-gradient-to-b"></div>
131180
</div>
132181

133182
<div className="relative z-10 mx-auto max-w-7xl px-4 lg:border-r lg:px-0">
134183
<div className={cn('bg-white dark:bg-transparent', mode == 'code' && 'hidden')}>
135-
<PanelGroup direction="horizontal" tagName="div" ref={ref}>
184+
<PanelGroup
185+
direction="horizontal"
186+
tagName="div"
187+
ref={ref}>
136188
<Panel
137189
id={`block-${title}`}
138190
order={1}
@@ -142,7 +194,20 @@ export const BlockPreview: React.FC<BlockPreviewProps> = ({ code, preview, title
142194
defaultSize={DEFAULTSIZE}
143195
minSize={SMSIZE}
144196
className="h-fit border-x">
145-
<iframe loading="lazy" allowFullScreen ref={iframeRef} title={title} height={iframeHeight} className="h-(--iframe-height) @starting:opacity-0 @starting:blur-xl block min-h-56 w-full duration-200 will-change-auto" src={preview} id={`block-${title}`} style={{ '--iframe-height': `${iframeHeight}px` } as React.CSSProperties} />
197+
<iframe
198+
key={`${category}-${title}-iframe`}
199+
loading="lazy"
200+
allowFullScreen
201+
ref={iframeRef}
202+
title={title}
203+
height={iframeHeight}
204+
className="h-(--iframe-height) @starting:opacity-0 @starting:blur-xl block min-h-56 w-full duration-200 will-change-auto"
205+
src={preview}
206+
id={`block-${title}`}
207+
style={{ '--iframe-height': `${iframeHeight}px` } as React.CSSProperties}
208+
{...{ fetchpriority: 'low' }}
209+
/>
210+
146211
{isLoading && (
147212
<div className="bg-background absolute inset-0 right-2 flex items-center justify-center border-x">
148213
<div className="border-primary size-6 animate-spin rounded-full border-2 border-t-transparent" />
@@ -153,13 +218,25 @@ export const BlockPreview: React.FC<BlockPreviewProps> = ({ code, preview, title
153218
{isLarge && (
154219
<>
155220
<PanelResizeHandle className="relative w-2 before:absolute before:inset-0 before:m-auto before:h-12 before:w-1 before:rounded-full before:bg-zinc-300 before:transition-[height,background] hover:before:h-16 hover:before:bg-zinc-400 focus:before:bg-zinc-400 dark:before:bg-zinc-600 dark:hover:before:bg-zinc-500 dark:focus:before:bg-zinc-400" />
156-
<Panel id={`code-${title}`} order={2} defaultSize={100 - DEFAULTSIZE} className="-mr-[0.5px] ml-px"></Panel>
221+
<Panel
222+
id={`code-${title}`}
223+
order={2}
224+
defaultSize={100 - DEFAULTSIZE}
225+
className="-mr-[0.5px] ml-px"></Panel>
157226
</>
158227
)}
159228
</PanelGroup>
160229
</div>
161230

162-
<div className="bg-white dark:bg-transparent">{mode == 'code' && <CodeBlock code={code as string} lang="tsx" maxHeight={iframeHeight} />}</div>
231+
<div className="bg-white dark:bg-transparent">
232+
{mode == 'code' && (
233+
<CodeBlock
234+
code={code as string}
235+
lang="tsx"
236+
maxHeight={iframeHeight}
237+
/>
238+
)}
239+
</div>
163240
</div>
164241
</div>
165242
</section>

middleware.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
3+
export function middleware(request: NextRequest) {
4+
if (request.nextUrl.pathname.startsWith('/preview/')) {
5+
const response = NextResponse.next()
6+
7+
response.headers.set('Cache-Control', 'public, max-age=3600, s-maxage=86400')
8+
response.headers.set('CDN-Cache-Control', 'public, max-age=3600')
9+
10+
return response
11+
}
12+
}

0 commit comments

Comments
 (0)