| 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 |