返回 presentation-ai
media-video-node.tsx
根目录 / src / components / plate / ui / media-video-node.tsx
1 "use client";
2
3 import { useDraggable } from "@platejs/dnd";
4 import { parseTwitterUrl, parseVideoUrl } from "@platejs/media";
5 import { useMediaState } from "@platejs/media/react";
6 import { ResizableProvider, useResizableValue } from "@platejs/resizable";
7 import { type TResizableProps, type TVideoElement } from "platejs";
8 import {
9 PlateElement,
10 useEditorMounted,
11 withHOC,
12 type PlateElementProps,
13 } from "platejs/react";
14 import LiteYouTubeEmbed from "react-lite-youtube-embed";
15 import ReactPlayer from "react-player";
16
17 import { cn } from "@/lib/utils";
18 import { Caption, CaptionTextarea } from "./caption";
19 import { Resizable, ResizeHandle } from "./resize-handle";
20 import { mediaResizeHandleVariants } from "./resize-handle";
21
22 export const VideoElement = withHOC(
23 ResizableProvider,
24 function VideoElement(
25 props: PlateElementProps<TVideoElement & TResizableProps>,
26 ) {
27 const {
28 align = "center",
29 embed,
30 isUpload,
31 isYoutube,
32 readOnly,
33 unsafeUrl,
34 } = useMediaState({
35 urlParsers: [parseTwitterUrl, parseVideoUrl],
36 });
37 const width = useResizableValue("width");
38
39 const isEditorMounted = useEditorMounted();
40
41 const isTweet = true;
42
43 const { isDragging, handleRef } = useDraggable({
44 element: props.element,
45 });
46
47 return (
48 <PlateElement className="py-2.5" {...props}>
49 <figure className="relative m-0 cursor-default" contentEditable={false}>
50 <Resizable
51 className={cn(isDragging && "opacity-50")}
52 align={align}
53 options={{
54 align,
55 maxWidth: isTweet ? 550 : "100%",
56 minWidth: isTweet ? 300 : 100,
57 readOnly,
58 }}
59 >
60 <div className="group/media">
61 <ResizeHandle
62 className={mediaResizeHandleVariants({ direction: "left" })}
63 options={{ direction: "left" }}
64 />
65
66 <ResizeHandle
67 className={mediaResizeHandleVariants({ direction: "right" })}
68 options={{ direction: "right" }}
69 />
70
71 {!isUpload && isYoutube && (
72 <div ref={handleRef}>
73 <LiteYouTubeEmbed
74 id={embed!.id!}
75 title="youtube"
76 wrapperClass={cn(
77 "aspect-video rounded-sm",
78 "relative block cursor-pointer bg-black bg-cover bg-center contain-content",
79 "[&.lyt-activated]:before:absolute [&.lyt-activated]:before:top-0 [&.lyt-activated]:before:h-15 [&.lyt-activated]:before:w-full [&.lyt-activated]:before:bg-top [&.lyt-activated]:before:bg-repeat-x [&.lyt-activated]:before:pb-12.5 [&.lyt-activated]:before:[transition:all_0.2s_cubic-bezier(0,0,0.2,1)]",
80 "[&.lyt-activated]:before:bg-[url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==)]",
81 'after:block after:pb-(--aspect-ratio) after:content-[""]',
82 "[&_>_iframe]:absolute [&_>_iframe]:top-0 [&_>_iframe]:left-0 [&_>_iframe]:size-full",
83 "[&_>_.lty-playbtn]:z-1 [&_>_.lty-playbtn]:h-11.5 [&_>_.lty-playbtn]:w-17.5 [&_>_.lty-playbtn]:rounded-[14%] [&_>_.lty-playbtn]:bg-[#212121] [&_>_.lty-playbtn]:opacity-80 [&_>_.lty-playbtn]:[transition:all_0.2s_cubic-bezier(0,0,0.2,1)]",
84 "[&:hover_>_.lty-playbtn]:bg-[red] [&:hover_>_.lty-playbtn]:opacity-100",
85 '[&_>_.lty-playbtn]:before:border-y-11 [&_>_.lty-playbtn]:before:border-r-0 [&_>_.lty-playbtn]:before:border-l-19 [&_>_.lty-playbtn]:before:border-[transparent_transparent_transparent_#fff] [&_>_.lty-playbtn]:before:content-[""]',
86 "[&_>_.lty-playbtn]:absolute [&_>_.lty-playbtn]:top-1/2 [&_>_.lty-playbtn]:left-1/2 [&_>_.lty-playbtn]:transform-[translate3d(-50%,-50%,0)]",
87 "[&_>_.lty-playbtn]:before:absolute [&_>_.lty-playbtn]:before:top-1/2 [&_>_.lty-playbtn]:before:left-1/2 [&_>_.lty-playbtn]:before:transform-[translate3d(-50%,-50%,0)]",
88 "[&.lyt-activated]:cursor-[unset]",
89 "[&.lyt-activated]:before:pointer-events-none [&.lyt-activated]:before:opacity-0",
90 "[&.lyt-activated_>_.lty-playbtn]:pointer-events-none [&.lyt-activated_>_.lty-playbtn]:opacity-0!",
91 )}
92 />
93 </div>
94 )}
95
96 {isUpload && isEditorMounted && (
97 <div ref={handleRef}>
98 <ReactPlayer
99 src={unsafeUrl}
100 height="100%"
101 width="100%"
102 controls
103 />
104 </div>
105 )}
106 </div>
107 </Resizable>
108
109 <Caption style={{ width }} align={align}>
110 <CaptionTextarea
111 readOnly={readOnly}
112 placeholder="Write a caption..."
113 />
114 </Caption>
115 </figure>
116 {props.children}
117 </PlateElement>
118 );
119 },
120 );
121
121 lines Plain Text