56 lines
1.3 KiB
TypeScript
56 lines
1.3 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { ImageOffIcon } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface MediaImageProps {
|
|
mediaId: string | null | undefined;
|
|
alt: string;
|
|
className?: string;
|
|
fit?: "cover" | "contain";
|
|
priority?: boolean;
|
|
}
|
|
|
|
export function MediaImage({
|
|
mediaId,
|
|
alt,
|
|
className,
|
|
fit = "cover",
|
|
priority = false,
|
|
}: MediaImageProps) {
|
|
const [failedId, setFailedId] = useState<string | null>(null);
|
|
const failed = !mediaId || failedId === mediaId;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative grid min-h-32 overflow-hidden bg-muted text-muted-foreground",
|
|
className,
|
|
)}
|
|
>
|
|
{failed ? (
|
|
<div className="grid place-items-center gap-2 p-6 text-center">
|
|
<ImageOffIcon aria-hidden="true" className="size-8 opacity-60" />
|
|
<span className="text-xs">ไม่มีภาพ</span>
|
|
</div>
|
|
) : (
|
|
<Image
|
|
src={`/media/${mediaId}`}
|
|
alt={alt}
|
|
fill
|
|
priority={priority}
|
|
unoptimized
|
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
|
className={cn(
|
|
fit === "cover" ? "object-cover" : "object-contain",
|
|
)}
|
|
onError={() => setFailedId(mediaId)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|