feat(public): render responsive character guides
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import Link from "next/link";
|
||||
import { ChevronLeftIcon, ScrollTextIcon } from "lucide-react";
|
||||
|
||||
import {
|
||||
collectGuideSections,
|
||||
PublicBlockRenderer,
|
||||
} from "@/components/public/block-renderer";
|
||||
import { SectionDrawer } from "@/components/public/section-drawer";
|
||||
import { SiteHeader } from "@/components/public/site-header";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import type { PageSnapshot } from "@/lib/content/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function GuideShell({ snapshot }: { snapshot: PageSnapshot }) {
|
||||
const sections = collectGuideSections(snapshot.blocks);
|
||||
|
||||
return (
|
||||
<div className="min-h-svh bg-background">
|
||||
<SiteHeader />
|
||||
<div className="sticky top-0 z-30 border-b bg-background/95 supports-backdrop-filter:backdrop-blur">
|
||||
<div className="mx-auto w-full max-w-7xl px-4 sm:px-6">
|
||||
<div className="flex min-h-14 items-center gap-3 py-2">
|
||||
<Link
|
||||
href="/"
|
||||
className="grid size-10 shrink-0 place-items-center rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
aria-label="กลับไปหน้ารวมตัวละคร"
|
||||
>
|
||||
<ChevronLeftIcon aria-hidden="true" />
|
||||
</Link>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-heading font-semibold">{snapshot.character.nameTh}</p>
|
||||
{snapshot.character.nameEn ? (
|
||||
<p className="truncate text-xs text-muted-foreground">{snapshot.character.nameEn}</p>
|
||||
) : null}
|
||||
</div>
|
||||
{snapshot.character.element ? <Badge>{snapshot.character.element}</Badge> : null}
|
||||
{snapshot.character.role ? (
|
||||
<Badge variant="outline" className="hidden sm:inline-flex">
|
||||
{snapshot.character.role}
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
<nav
|
||||
aria-label={`หน้าคู่มือ ${snapshot.character.nameTh}`}
|
||||
className="flex min-h-11 gap-1 overflow-x-auto border-t [scrollbar-width:none]"
|
||||
>
|
||||
{snapshot.pages.map((page) => (
|
||||
<Link
|
||||
key={page.id}
|
||||
href={`/${snapshot.character.slug}/${page.slug}`}
|
||||
aria-current={page.id === snapshot.page.id ? "page" : undefined}
|
||||
className={cn(
|
||||
"relative flex min-h-11 shrink-0 items-center rounded-md px-3 text-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring",
|
||||
page.id === snapshot.page.id &&
|
||||
"font-medium text-foreground after:absolute after:inset-x-3 after:bottom-0 after:h-0.5 after:bg-primary",
|
||||
)}
|
||||
>
|
||||
{page.title}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto grid w-full max-w-7xl gap-8 px-4 py-6 sm:px-6 lg:grid-cols-[13rem_minmax(0,1fr)] lg:py-10">
|
||||
<aside className="hidden lg:block">
|
||||
<div className="sticky top-32 space-y-3">
|
||||
<p className="flex items-center gap-2 text-sm font-medium">
|
||||
<ScrollTextIcon aria-hidden="true" className="size-4 text-primary" />
|
||||
สารบัญหน้านี้
|
||||
</p>
|
||||
<Separator />
|
||||
{sections.length > 0 ? (
|
||||
<nav aria-label="สารบัญหน้านี้" className="grid gap-1">
|
||||
{sections.map((section) => (
|
||||
<a
|
||||
key={section.id}
|
||||
href={`#${section.id}`}
|
||||
className="rounded-md px-2 py-2 text-sm leading-6 text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
{section.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
) : (
|
||||
<p className="text-sm leading-6 text-muted-foreground">หน้านี้ยังไม่มีหัวข้อย่อย</p>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="min-w-0 space-y-6">
|
||||
{sections.length > 0 ? <SectionDrawer sections={sections} /> : null}
|
||||
{snapshot.page.publicNote ? (
|
||||
<Alert>
|
||||
<ScrollTextIcon aria-hidden="true" />
|
||||
<AlertTitle>หมายเหตุจากผู้เขียน</AlertTitle>
|
||||
<AlertDescription>{snapshot.page.publicNote}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
<PublicBlockRenderer snapshot={snapshot} />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user