fix(docs): restructure layout to properly center empty state relative to page
Docker Build / Build and Push Docker Image (push) Failing after 7m25s

This commit is contained in:
2026-06-18 18:01:16 +08:00
parent 6a7b1ecb06
commit 4c2a403bc4
+150 -103
View File
@@ -312,59 +312,121 @@ export function Docs() {
</h1>
)}
{/* Three-column layout with left sidebar always visible */}
<div className='flex gap-8'>
{/* Left sidebar - always visible */}
<aside className='hidden w-64 shrink-0 lg:block'>
<div className='sticky top-[4.5rem] max-h-[calc(100vh-5rem)] overflow-y-auto'>
{/* Search */}
<div className='relative mb-4'>
<Search className='absolute top-1/2 left-3 size-3.5 -translate-y-1/2 text-muted-foreground' />
<Input
placeholder={t('Search documents...')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className='h-8 pl-8 text-sm'
/>
</div>
{/* Categories */}
{loading ? (
<div className='space-y-3'>
{[1, 2, 3].map((i) => (
<div
key={i}
className='h-6 animate-pulse rounded bg-muted'
/>
))}
{/* Empty state - centered relative to entire page (outside three-column layout) */}
{!selectedDoc && !docLoading && (
<div className='relative min-h-[50vh]'>
{/* Left sidebar - visible for document selection */}
<aside className='hidden w-64 shrink-0 lg:block absolute left-0 top-0'>
<div className='sticky top-[4.5rem] max-h-[calc(100vh-5rem)] overflow-y-auto'>
{/* Search */}
<div className='relative mb-4'>
<Search className='absolute top-1/2 left-3 size-3.5 -translate-y-1/2 text-muted-foreground' />
<Input
placeholder={t('Search documents...')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className='h-8 pl-8 text-sm'
/>
</div>
) : categories.length === 0 ? (
<p className='text-sm text-muted-foreground'>
{t('No documents')}
</p>
) : (
<div className='space-y-1'>
{categories
.sort((a, b) => a.sort_order - b.sort_order)
.map((cat) => (
<CategorySection
key={cat.id}
category={cat}
documents={documents}
selectedSlug={selectedSlug}
onSelect={handleSelectDoc}
searchQuery={searchQuery}
{/* Categories */}
{loading ? (
<div className='space-y-3'>
{[1, 2, 3].map((i) => (
<div
key={i}
className='h-6 animate-pulse rounded bg-muted'
/>
))}
</div>
)}
</div>
</aside>
</div>
) : categories.length === 0 ? (
<p className='text-sm text-muted-foreground'>
{t('No documents')}
</p>
) : (
<div className='space-y-1'>
{categories
.sort((a, b) => a.sort_order - b.sort_order)
.map((cat) => (
<CategorySection
key={cat.id}
category={cat}
documents={documents}
selectedSlug={selectedSlug}
onSelect={handleSelectDoc}
searchQuery={searchQuery}
/>
))}
</div>
)}
</div>
</aside>
{/* Center content */}
<main className='min-w-0 flex-1'>
{!selectedDoc || docLoading ? (
docLoading ? (
{/* Centered empty state */}
<div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center text-center'>
<div className='flex size-16 items-center justify-center rounded-full bg-muted'>
<Book className='size-7 text-muted-foreground' />
</div>
<p className='mt-4 text-sm text-muted-foreground'>
{t('Select a document to start reading')}
</p>
</div>
</div>
)}
{/* Three-column layout - only when document is selected */}
{selectedDoc && (
<div className='flex gap-8'>
{/* Left sidebar */}
<aside className='hidden w-64 shrink-0 lg:block'>
<div className='sticky top-[4.5rem] max-h-[calc(100vh-5rem)] overflow-y-auto'>
{/* Search */}
<div className='relative mb-4'>
<Search className='absolute top-1/2 left-3 size-3.5 -translate-y-1/2 text-muted-foreground' />
<Input
placeholder={t('Search documents...')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className='h-8 pl-8 text-sm'
/>
</div>
{/* Categories */}
{loading ? (
<div className='space-y-3'>
{[1, 2, 3].map((i) => (
<div
key={i}
className='h-6 animate-pulse rounded bg-muted'
/>
))}
</div>
) : categories.length === 0 ? (
<p className='text-sm text-muted-foreground'>
{t('No documents')}
</p>
) : (
<div className='space-y-1'>
{categories
.sort((a, b) => a.sort_order - b.sort_order)
.map((cat) => (
<CategorySection
key={cat.id}
category={cat}
documents={documents}
selectedSlug={selectedSlug}
onSelect={handleSelectDoc}
searchQuery={searchQuery}
/>
))}
</div>
)}
</div>
</aside>
{/* Center content */}
<main className='min-w-0 flex-1'>
{docLoading ? (
<div className='animate-pulse space-y-4'>
<div className='h-8 w-2/3 rounded bg-muted' />
<div className='h-4 w-full rounded bg-muted' />
@@ -372,60 +434,45 @@ export function Docs() {
<div className='h-4 w-4/6 rounded bg-muted' />
</div>
) : (
/* Empty state - centered relative to entire page container */
/* Offset by half of sidebar width to align with page title */
<div className='relative min-h-[50vh] lg:-ml-[calc((256px+32px)/2)] lg:w-[calc(100%+(256px+32px)/2)] xl:-ml-[calc((256px+32px-192px)/2)] xl:w-[calc(100%+(256px+32px-192px)/2)]'>
<div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center text-center'>
<div className='flex size-16 items-center justify-center rounded-full bg-muted'>
<Book className='size-7 text-muted-foreground' />
<>
<div id='doc-content' className='doc-content'>
<Markdown>
{selectedDoc?.content ?? ''}
</Markdown>
</div>
{/* Prev/Next navigation */}
{(prevDoc || nextDoc) && (
<div className='mt-12 flex items-center justify-between border-t border-border pt-6'>
{prevDoc ? (
<Button
variant='outline'
size='sm'
onClick={() => handleSelectDoc(prevDoc.slug)}
>
{t('Previous')}: {prevDoc.title}
</Button>
) : (
<div />
)}
{nextDoc ? (
<Button
variant='outline'
size='sm'
onClick={() => handleSelectDoc(nextDoc.slug)}
>
{t('Next')}: {nextDoc.title}
</Button>
) : (
<div />
)}
</div>
<p className='mt-4 text-sm text-muted-foreground'>
{t('Select a document to start reading')}
</p>
</div>
</div>
)
) : (
<>
<div id='doc-content' className='doc-content'>
<Markdown>
{selectedDoc?.content ?? ''}
</Markdown>
</div>
)}
</>
)}
</main>
{/* Prev/Next navigation */}
{(prevDoc || nextDoc) && (
<div className='mt-12 flex items-center justify-between border-t border-border pt-6'>
{prevDoc ? (
<Button
variant='outline'
size='sm'
onClick={() => handleSelectDoc(prevDoc.slug)}
>
{t('Previous')}: {prevDoc.title}
</Button>
) : (
<div />
)}
{nextDoc ? (
<Button
variant='outline'
size='sm'
onClick={() => handleSelectDoc(nextDoc.slug)}
>
{t('Next')}: {nextDoc.title}
</Button>
) : (
<div />
)}
</div>
)}
</>
)}
</main>
{/* Right TOC - only visible when document is selected */}
{selectedDoc && (
{/* Right TOC */}
<aside className='hidden w-48 shrink-0 xl:block'>
{tocItems.length > 0 && (
<div className='sticky top-[4.5rem] max-h-[calc(100vh-5rem)] overflow-y-auto'>
@@ -450,8 +497,8 @@ export function Docs() {
</div>
)}
</aside>
)}
</div>
</div>
)}
</div>
<style>{`