carpa_turborepo/apps/cms/src/components/YouTubePreview.tsx

88 lines
2.5 KiB
TypeScript

"use client";
import React from "react";
import { useFormFields } from "@payloadcms/ui";
/** Extract an 11-char YouTube video id from common URL shapes (or a bare id). */
function extractYouTubeId(input: unknown): string | null {
if (typeof input !== "string") return null;
const url = input.trim();
if (!url) return null;
if (/^[\w-]{11}$/.test(url)) return url; // already just an id
const patterns = [
/(?:youtube\.com\/watch\?[^#]*\bv=)([\w-]{11})/i,
/(?:youtu\.be\/)([\w-]{11})/i,
/(?:youtube\.com\/embed\/)([\w-]{11})/i,
/(?:youtube\.com\/shorts\/)([\w-]{11})/i,
/(?:youtube\.com\/live\/)([\w-]{11})/i,
];
for (const p of patterns) {
const m = url.match(p);
if (m?.[1]) return m[1];
}
return null;
}
export interface YouTubePreviewProps {
/** Form path of the field holding the YouTube URL (e.g. "media.youtube"). */
sourcePath?: string;
}
/**
* Renders a live, embedded YouTube preview from the sibling URL field. Virtual
* (a `ui` field) — it stores nothing; the URL lives on the `youtube` text
* field, which is what syncs to Typesense.
*/
export function YouTubePreview({
sourcePath = "media.youtube",
}: YouTubePreviewProps) {
const value = useFormFields(([fields]) => fields?.[sourcePath]?.value);
const id = extractYouTubeId(value);
return (
<div className="field-type" style={{ marginBlock: "calc(var(--base) / 2)" }}>
<label className="field-label">YouTube preview</label>
{id ? (
<div
style={{
position: "relative",
width: "100%",
maxWidth: 560,
paddingBottom: "min(56.25%, 315px)",
height: 0,
overflow: "hidden",
borderRadius: "var(--style-radius-m)",
border: "1px solid var(--theme-elevation-150)",
}}
>
<iframe
src={`https://www.youtube-nocookie.com/embed/${id}`}
title="YouTube preview"
style={{
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
border: 0,
}}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>
</div>
) : (
<p
style={{
margin: 0,
color: "var(--theme-elevation-400)",
fontSize: "0.8rem",
}}
>
Paste a YouTube URL above to preview it here.
</p>
)}
</div>
);
}
export default YouTubePreview;