Files
buzz-sheet/components/weapon-comparison-card.tsx
T

109 lines
4.4 KiB
TypeScript

import type { ReactNode } from "react";
import { CatalogItemImage, type CatalogItemImageData } from "@/components/catalog-item-image";
import { Card, CardContent } from "@/components/ui/card";
import { rarityGradientClass, rarityTextClass } from "@/lib/catalog/rarity";
import { cn } from "@/lib/utils";
type DamageMetric = "overallPercent" | "personalPercent";
export function weaponComparisonPercent(value: string | null, exporting = false) {
if (value === null) return null;
return Number(value) === 0
? exporting ? "-%" : "-"
: `${Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 })}%`;
}
export function WeaponComparisonCard({
weapon, rank, refinement, note, overallPercent, personalPercent,
exporting = false, compactExportGrid = false, weaponHref,
imageControl, refinementControl, noteControl, actions, renderMetric,
}: {
weapon: CatalogItemImageData & { name: string };
rank: number;
refinement: string;
note: string | null;
overallPercent: string | null;
personalPercent: string | null;
exporting?: boolean;
compactExportGrid?: boolean;
weaponHref?: string;
imageControl?: ReactNode;
refinementControl?: ReactNode;
noteControl?: ReactNode;
actions?: ReactNode;
renderMetric?: (metric: DamageMetric) => ReactNode;
}) {
const Panel = weaponHref ? "a" : "div";
const title = <p className={cn(
"truncate font-semibold",
compactExportGrid ? "text-sm" : exporting ? "text-lg" : "text-base sm:text-lg",
)}>
{rank}. {weapon.name}{refinementControl ? null : ` R${refinement}`}
</p>;
return <Card size="sm" className="bg-white py-0">
<CardContent className={cn(
"grid p-0",
compactExportGrid
? "grid-cols-[minmax(0,3fr)_minmax(0,2fr)]"
: exporting
? "grid-cols-[minmax(0,3fr)_minmax(0,1fr)]"
: renderMetric
? "md:grid-cols-[minmax(0,5fr)_minmax(0,2fr)]"
: "md:grid-cols-[minmax(0,3fr)_minmax(0,1fr)]",
)}>
<Panel
href={weaponHref}
target={weaponHref ? "_blank" : undefined}
rel={weaponHref ? "noreferrer" : undefined}
tabIndex={exporting ? -1 : undefined}
aria-label={weaponHref ? `เปิดข้อมูลอาวุธ ${weapon.name} บน Lunaris` : undefined}
className={cn(
"flex min-h-28 min-w-0 items-center gap-4 rounded-br-[4rem] p-4 pr-10 text-white",
compactExportGrid
? "rounded-br-[8rem] pr-10"
: exporting ? "rounded-br-[8rem] pr-16" : "md:rounded-br-[8rem] md:pr-16",
rarityGradientClass(weapon.rarity),
actions && "relative",
)}
>
{imageControl ?? <CatalogItemImage
item={weapon} kind="weapon" alt={weapon.name} rarityBackground={false}
className="size-20 shrink-0" loading={exporting ? "eager" : undefined}
sizes="(min-width: 768px) 5rem, 4rem"
/>}
<div className="flex min-w-0 flex-col gap-1">
{refinementControl ? <div className="flex min-w-0 flex-wrap items-center gap-2">{title}{refinementControl}</div> : title}
{noteControl ?? (note ? <p className={cn("whitespace-pre-wrap", compactExportGrid ? "text-xs" : "text-sm")}>{note}</p> : null)}
</div>
{actions}
</Panel>
<div className={cn(
"flex items-center justify-around bg-white py-5 text-black",
compactExportGrid ? "gap-1 px-2" : "gap-4 px-4",
)}>
{(["overallPercent", "personalPercent"] as const).map((metric) => {
const value = metric === "overallPercent" ? overallPercent : personalPercent;
if (!renderMetric && !value) return null;
return <div key={metric} className="min-w-0 flex-1 text-center">
<p className={cn(
"whitespace-nowrap leading-none font-bold",
compactExportGrid ? "text-2xl" : exporting ? "text-5xl" : "text-4xl sm:text-5xl",
rarityTextClass(weapon.rarity),
)}>
{renderMetric ? renderMetric(metric) : weaponComparisonPercent(value, exporting)}
</p>
<p className={cn(
"mt-1 whitespace-nowrap",
compactExportGrid ? "text-[10px]" : exporting ? "text-base" : "text-sm sm:text-base",
)}>
{metric === "overallPercent" ? "ดาเมจทั้งทีม" : "ดาเมจส่วนตัว"}
</p>
</div>;
})}
</div>
</CardContent>
</Card>;
}