77
src/components/CustomWorldCoverArtwork.tsx
Normal file
77
src/components/CustomWorldCoverArtwork.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import type { CustomWorldCoverRenderMode } from '../services/customWorldCover';
|
||||
|
||||
const COVER_PORTRAIT_CLASS_NAMES = [
|
||||
'h-[54%] w-[24%] translate-y-[8%]',
|
||||
'h-[68%] w-[30%]',
|
||||
'h-[56%] w-[24%] translate-y-[10%]',
|
||||
] as const;
|
||||
|
||||
type CustomWorldCoverArtworkProps = {
|
||||
imageSrc?: string | null;
|
||||
title: string;
|
||||
fallbackLabel: string;
|
||||
renderMode?: CustomWorldCoverRenderMode;
|
||||
characterImageSrcs?: string[];
|
||||
className?: string;
|
||||
overlay?: ReactNode;
|
||||
};
|
||||
|
||||
export function CustomWorldCoverArtwork({
|
||||
imageSrc,
|
||||
title,
|
||||
fallbackLabel,
|
||||
renderMode = 'image',
|
||||
characterImageSrcs = [],
|
||||
className = '',
|
||||
overlay,
|
||||
}: CustomWorldCoverArtworkProps) {
|
||||
const coverCharacterImageSrcs = characterImageSrcs.slice(0, 3);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative overflow-hidden bg-[radial-gradient(circle_at_top,rgba(255,244,214,0.3),transparent_38%),linear-gradient(180deg,rgba(34,40,55,0.92),rgba(10,12,18,0.96))] ${className}`}
|
||||
>
|
||||
{imageSrc ? (
|
||||
<img
|
||||
src={imageSrc}
|
||||
alt={title}
|
||||
loading="lazy"
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
) : null}
|
||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(8,10,14,0.04),rgba(8,10,14,0.26)_46%,rgba(8,10,14,0.82)_100%)]" />
|
||||
{!imageSrc ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center px-4 text-center text-sm font-semibold tracking-[0.18em] text-zinc-300">
|
||||
{fallbackLabel}
|
||||
</div>
|
||||
) : null}
|
||||
{renderMode === 'scene_with_roles' && coverCharacterImageSrcs.length > 0 ? (
|
||||
<>
|
||||
<div className="absolute inset-x-0 bottom-0 h-[42%] bg-[linear-gradient(180deg,rgba(8,10,14,0)_0%,rgba(8,10,14,0.88)_100%)]" />
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 flex items-end justify-center gap-2 px-3 pb-2 sm:pb-3">
|
||||
{coverCharacterImageSrcs.map((characterImageSrc, index) => (
|
||||
<div
|
||||
key={`${title}-cover-character-${index}-${characterImageSrc}`}
|
||||
className={`overflow-hidden rounded-[1rem] border border-white/16 bg-[linear-gradient(180deg,rgba(255,255,255,0.14),rgba(255,255,255,0.04))] shadow-[0_12px_28px_rgba(0,0,0,0.4)] ${COVER_PORTRAIT_CLASS_NAMES[index] ?? COVER_PORTRAIT_CLASS_NAMES[1]}`}
|
||||
>
|
||||
<img
|
||||
src={characterImageSrc}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
className="h-full w-full object-cover object-top"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{overlay ? (
|
||||
<div className="pointer-events-none absolute inset-0">{overlay}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomWorldCoverArtwork;
|
||||
Reference in New Issue
Block a user