fix(docs): restructure layout to properly center empty state relative to page
Docker Build / Build and Push Docker Image (push) Failing after 7m25s
Docker Build / Build and Push Docker Image (push) Failing after 7m25s
This commit is contained in:
+150
-103
@@ -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>{`
|
||||
|
||||
Reference in New Issue
Block a user