182 lines
4.5 KiB
TypeScript
182 lines
4.5 KiB
TypeScript
/* eslint-disable @next/next/no-img-element -- Share card rendering requires direct img tags for html-to-image output. */
|
|
import { QRCodeSVG } from "qrcode.react";
|
|
import type { ReactNode } from "react";
|
|
|
|
export interface ShareCardTheme {
|
|
emoji: string;
|
|
tagline: string;
|
|
bgColor: string;
|
|
gradientBorder: string;
|
|
accentLine: string;
|
|
glows: { top: number; right: number; width: number; height: number; color: string }[];
|
|
qrFgColor: string;
|
|
}
|
|
|
|
export default function ShareCardShell({
|
|
theme,
|
|
cardRef,
|
|
bgDataUrl,
|
|
children,
|
|
}: {
|
|
theme: ShareCardTheme;
|
|
cardRef: React.RefObject<HTMLDivElement | null>;
|
|
bgDataUrl?: string | null;
|
|
children: ReactNode;
|
|
}) {
|
|
const shareUrl =
|
|
typeof window !== "undefined" ? window.location.origin : "nowhatever.app";
|
|
|
|
return (
|
|
<div
|
|
ref={cardRef}
|
|
style={{
|
|
width: 340,
|
|
padding: 1.5,
|
|
borderRadius: 20,
|
|
background: theme.gradientBorder,
|
|
fontFamily:
|
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
borderRadius: 18.5,
|
|
background: theme.bgColor,
|
|
position: "relative",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
{/* Background image */}
|
|
{bgDataUrl && (
|
|
<img
|
|
src={bgDataUrl}
|
|
alt=""
|
|
style={{
|
|
position: "absolute",
|
|
inset: 0,
|
|
width: "100%",
|
|
height: "100%",
|
|
objectFit: "cover",
|
|
opacity: 0.12,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* Decorative glows */}
|
|
{theme.glows.map((g, i) => (
|
|
<div
|
|
key={i}
|
|
style={{
|
|
position: "absolute",
|
|
top: g.top,
|
|
right: g.right,
|
|
width: g.width,
|
|
height: g.height,
|
|
borderRadius: "50%",
|
|
background: `radial-gradient(circle, ${g.color}, transparent 70%)`,
|
|
}}
|
|
/>
|
|
))}
|
|
|
|
{/* Brand header */}
|
|
<div
|
|
style={{
|
|
padding: "14px 20px 12px",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
position: "relative",
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
<span style={{ fontSize: 18 }}>{theme.emoji}</span>
|
|
<div>
|
|
<div
|
|
style={{
|
|
fontSize: 13,
|
|
fontWeight: 800,
|
|
color: "#ffffff",
|
|
letterSpacing: "0.02em",
|
|
}}
|
|
>
|
|
NoWhatever
|
|
</div>
|
|
<div
|
|
style={{
|
|
fontSize: 9,
|
|
fontWeight: 600,
|
|
color: "rgba(255,255,255,0.35)",
|
|
letterSpacing: "0.15em",
|
|
marginTop: 1,
|
|
}}
|
|
>
|
|
{theme.tagline}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Thin accent line */}
|
|
<div
|
|
style={{
|
|
height: 1,
|
|
margin: "0 20px",
|
|
background: theme.accentLine,
|
|
}}
|
|
/>
|
|
|
|
{/* Card-specific content */}
|
|
{children}
|
|
|
|
{/* QR footer */}
|
|
<div
|
|
style={{
|
|
padding: "14px 20px 16px",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 14,
|
|
borderTop: "1px solid rgba(255,255,255,0.04)",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
padding: 5,
|
|
borderRadius: 8,
|
|
background: "#ffffff",
|
|
flexShrink: 0,
|
|
}}
|
|
>
|
|
<QRCodeSVG
|
|
value={shareUrl}
|
|
size={52}
|
|
level="M"
|
|
bgColor="#ffffff"
|
|
fgColor={theme.qrFgColor}
|
|
/>
|
|
</div>
|
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
<div
|
|
style={{
|
|
fontSize: 12,
|
|
fontWeight: 700,
|
|
color: "rgba(255,255,255,0.7)",
|
|
}}
|
|
>
|
|
扫码一起「别说随便」
|
|
</div>
|
|
<div
|
|
style={{
|
|
fontSize: 10,
|
|
color: "rgba(255,255,255,0.25)",
|
|
marginTop: 3,
|
|
}}
|
|
>
|
|
{shareUrl.replace(/^https?:\/\//, "")}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|