88 lines
2.5 KiB
TypeScript
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;
|