109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import Image from "next/image";
|
||
|
||
import { GuideExportBranding } from "@/components/public/guide-export-branding";
|
||
import { ExportText } from "@/components/public/export-language";
|
||
import type { PublicGuide } from "@/lib/guides/queries";
|
||
import { constellationChartRows } from "@/lib/guides/export-image";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
export type ConstellationImageGuide = Pick<PublicGuide, "coverUrl"> & {
|
||
character: Pick<PublicGuide["character"], "name" | "splashImageUrl">;
|
||
};
|
||
|
||
export interface ConstellationImageGroup {
|
||
note: string | null;
|
||
rows: Array<{
|
||
level: number;
|
||
cumulativePercent: string;
|
||
isGreat: boolean;
|
||
}>;
|
||
}
|
||
|
||
function percentage(value: number): string {
|
||
return `${value.toLocaleString("en-US", { maximumFractionDigits: 2 })}%`;
|
||
}
|
||
|
||
export function ConstellationImage({
|
||
guide,
|
||
group,
|
||
}: {
|
||
guide: ConstellationImageGuide;
|
||
group: ConstellationImageGroup;
|
||
}) {
|
||
const rows = constellationChartRows(group.rows);
|
||
const background = guide.character.splashImageUrl ?? guide.coverUrl;
|
||
const footer = group.note;
|
||
|
||
return (
|
||
<div className="relative isolate size-375 overflow-hidden bg-[#070b20] font-heading text-white">
|
||
<GuideExportBranding backgroundUrl={background} variant="constellation" />
|
||
|
||
<div className="absolute inset-x-20 top-7 z-10 flex items-start justify-center gap-5">
|
||
<Image
|
||
src="/icon/nav/Constellations.webp"
|
||
alt=""
|
||
width={112}
|
||
height={112}
|
||
loading="eager"
|
||
className="size-28 shrink-0 object-contain drop-shadow-[0_5px_4px_rgba(0,0,0,0.8)]"
|
||
/>
|
||
<div className="flex flex-col items-center text-center drop-shadow-[0_5px_4px_rgba(0,0,0,0.8)]">
|
||
<h2
|
||
className={cn(
|
||
"whitespace-nowrap leading-none font-bold",
|
||
guide.character.name.length > 16
|
||
? "text-[60px]"
|
||
: "text-[76px]",
|
||
)}
|
||
>
|
||
<ExportText
|
||
th={`กลุ่มดาว ${guide.character.name}`}
|
||
en={`${guide.character.name}’s Constellations`}
|
||
/>
|
||
</h2>
|
||
<p className="mt-3 line-clamp-2 max-w-275 whitespace-pre-line text-[42px] leading-tight font-bold text-[#f1badd]">
|
||
<ExportText th="ดาเมจทั้งทีม" en="Team Damage" />
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="absolute top-40 bottom-28.75 left-38.5 z-20 w-1 bg-black/90" />
|
||
<div className="absolute inset-x-14 top-51.25 bottom-36.25 z-20 flex flex-col justify-between">
|
||
{rows.map((row) => (
|
||
<div
|
||
key={row.level}
|
||
className="grid grid-cols-[82px_1fr] items-center gap-4"
|
||
>
|
||
<div className="text-right text-[58px] leading-none font-bold drop-shadow-[0_5px_4px_rgba(0,0,0,0.9)]">
|
||
C{row.level}
|
||
</div>
|
||
<div className="flex h-25 items-center">
|
||
<div
|
||
className={cn(
|
||
"h-full shrink-0 rounded-r-xl",
|
||
row.isGreat ? "bg-warning" : "bg-white",
|
||
)}
|
||
style={{ width: `${row.widthPercent * 8.5}px` }}
|
||
/>
|
||
<span
|
||
className={cn(
|
||
"ml-3 text-[42px] leading-none font-bold drop-shadow-[0_4px_3px_rgba(0,0,0,0.9)]",
|
||
row.isGreat ? "text-[#f0c76a]" : "text-[#f1badd]",
|
||
)}
|
||
>
|
||
{percentage(row.percent)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{footer ? (
|
||
<p data-export-note className="absolute bottom-10 left-38.5 z-30 max-w-275 truncate text-[28px] font-semibold drop-shadow-[0_4px_3px_rgba(0,0,0,0.9)]">
|
||
{footer}
|
||
</p>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|