53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
import Image from "next/image";
|
|
import type { PortableTextComponents } from "@portabletext/react";
|
|
import { urlFor } from "@/lib/sanityImage";
|
|
|
|
function extractYouTubeId(url: string): string | null {
|
|
const match = url.match(
|
|
/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/,
|
|
);
|
|
return match?.[1] ?? null;
|
|
}
|
|
|
|
export const portableTextComponents: PortableTextComponents = {
|
|
types: {
|
|
image: ({ value }) => {
|
|
if (!value?.asset) return null;
|
|
const src = urlFor(value).url();
|
|
return (
|
|
<figure className="my-8">
|
|
<div className="relative aspect-video w-full overflow-hidden rounded-xl">
|
|
<Image
|
|
src={src}
|
|
alt={value.alt || ""}
|
|
fill
|
|
className="rounded-xl object-cover"
|
|
sizes="(max-width: 800px) 100vw, 800px"
|
|
/>
|
|
</div>
|
|
{value.caption && (
|
|
<figcaption className="mt-2 text-center text-sm text-lightsec dark:text-darksec">
|
|
{value.caption}
|
|
</figcaption>
|
|
)}
|
|
</figure>
|
|
);
|
|
},
|
|
youtube: ({ value }) => {
|
|
if (!value?.url) return null;
|
|
const videoId = extractYouTubeId(value.url);
|
|
if (!videoId) return null;
|
|
return (
|
|
<div className="my-8 aspect-video w-full overflow-hidden rounded-xl">
|
|
<iframe
|
|
src={`https://www.youtube-nocookie.com/embed/${videoId}`}
|
|
title="YouTube video"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
className="h-full w-full"
|
|
/>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
};
|