73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
RIBBON_DEVICE_INFO,
|
|
RIBBON_CONSTANTS,
|
|
RibbonDesign,
|
|
DEFAULT_RIBBON,
|
|
} from "@/components/admin/awards/RibbonDesigner";
|
|
import { useState } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export const RibbonViewer = ({ ribbonDesign, showRibbonOnly }: { ribbonDesign: RibbonDesign, showRibbonOnly?: boolean }) => {
|
|
const initial: RibbonDesign =
|
|
ribbonDesign && typeof ribbonDesign === "object" && Array.isArray((ribbonDesign as RibbonDesign).stripes)
|
|
? (ribbonDesign as RibbonDesign)
|
|
: DEFAULT_RIBBON;
|
|
|
|
const [design] = useState<RibbonDesign>(initial);
|
|
|
|
const { stripes, devices, mirrored } = design;
|
|
if (stripes.length === 0) return null;
|
|
|
|
const displayStripes = mirrored ? [...stripes, ...stripes.slice(0, -1).reverse()] : stripes;
|
|
|
|
const totalWeight = displayStripes.reduce((sum, s) => sum + s.width, 0);
|
|
const viewWidth = RIBBON_CONSTANTS.RIBBON_WIDTH;
|
|
const viewHeight = RIBBON_CONSTANTS.PREVIEW_HEIGHT;
|
|
|
|
return (
|
|
<svg
|
|
viewBox={`0 0 ${viewWidth} ${viewHeight}`}
|
|
className={cn("h-auto max-h-20", {
|
|
"w-full rounded border border-zinc-700 bg-zinc-950": !showRibbonOnly
|
|
} )}
|
|
>
|
|
<rect x={0} y={0} width={viewWidth} height={viewHeight} rx={2} fill="#18181b" />
|
|
{(() => {
|
|
let x = 0;
|
|
return displayStripes.map((stripe, i) => {
|
|
const w = (stripe.width / totalWeight) * viewWidth;
|
|
const el = <rect key={i} x={x} y={0} width={w} height={viewHeight} fill={stripe.color} />;
|
|
x += w;
|
|
return el;
|
|
});
|
|
})()}
|
|
|
|
<image href="/images/ribbon-overlay.png" className="mix-blend-multiply w-full" />
|
|
|
|
{devices.map((device) => {
|
|
const info = RIBBON_DEVICE_INFO[device.type];
|
|
const cx = (device.position / 100) * viewWidth;
|
|
const cy = (device.positionY / 100) * viewHeight;
|
|
const fontSize = Math.min(viewHeight * 0.45, 24);
|
|
return (
|
|
<text
|
|
key={device.id}
|
|
x={cx}
|
|
y={cy}
|
|
textAnchor="middle"
|
|
dominantBaseline="central"
|
|
fill="#facc15"
|
|
fontSize={fontSize}
|
|
fontFamily="serif"
|
|
style={{ pointerEvents: "none" }}
|
|
className="text-shadow-sm text-shadow-black"
|
|
>
|
|
{info.symbol}
|
|
</text>
|
|
);
|
|
})}
|
|
</svg>
|
|
);
|
|
};
|