Skip to content

Commit b29c0c7

Browse files
mavdotsoitsarghyadas
authored andcommitted
init new video modal component
1 parent 2a95656 commit b29c0c7

File tree

6 files changed

+335
-0
lines changed

6 files changed

+335
-0
lines changed

config/docs.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ export const docsConfig: DocsConfig = {
210210
items: [],
211211
label: "",
212212
},
213+
{
214+
title: "Video Modal",
215+
href: `/docs/components/video-modal`,
216+
items: [],
217+
label: "",
218+
},
213219
],
214220
},
215221
{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: Video Modal
3+
date: 2024-06-01
4+
description: Example component for demonstrating Video Modal component
5+
author: mavdotso
6+
published: true
7+
---
8+
9+
<ComponentPreview name="video-modal-demo" />
10+
11+
<Steps>
12+
13+
### Installation
14+
15+
Copy and paste the following code into your project.
16+
17+
```text
18+
components/magicui/video-modal.tsx
19+
```
20+
21+
<ComponentSource name="video-modal" />
22+
23+
Install @radix-ui/react-dialog.
24+
25+
```text
26+
npm install @radix-ui/react-dialog
27+
```
28+
29+
</Steps>
30+
31+
## Props
32+
33+
## Credits
34+
35+
- Credit to [Wope](https://wope.com/)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { PlayCircle } from "lucide-react";
2+
import {
3+
VideoModal,
4+
VideoModalTrigger,
5+
VideoModalContent,
6+
VideoModalTitle,
7+
VideoModalDescription,
8+
VideoModalVideo,
9+
VideoPreview,
10+
VideoPlayButton,
11+
VideoPlayer,
12+
} from "../magicui/video-modal";
13+
import { Button } from "@/components/ui/button";
14+
15+
const VideoModalDemo = () => {
16+
return (
17+
<div className="relative justify-center">
18+
<VideoModal>
19+
<VideoModalTrigger>
20+
<Button>Open modal</Button>
21+
</VideoModalTrigger>
22+
<VideoModalContent>
23+
<VideoModalTitle>Modal Video Demo</VideoModalTitle>
24+
<VideoModalDescription>
25+
Your subtitle or description here
26+
</VideoModalDescription>
27+
<VideoModalVideo>
28+
<VideoPlayer>
29+
<VideoPreview>
30+
<img
31+
src="https://cdn.dribbble.com/userupload/4145843/file/original-c7a2c9a768450460259f232259d103d2.png?resize=1600x1200"
32+
alt="Video preview"
33+
/>
34+
</VideoPreview>
35+
<VideoPlayButton>
36+
<button className="flex h-32 w-32 items-center justify-center rounded-full border border-white border-opacity-10 bg-white bg-opacity-5 transition duration-300 hover:bg-opacity-10">
37+
<PlayCircle className="h-20 w-20 stroke-1 text-white" />
38+
</button>
39+
</VideoPlayButton>
40+
<iframe
41+
className="h-full w-full"
42+
src="https://cdn.magicui.design/globe.mp4"
43+
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
44+
allowFullScreen
45+
/>
46+
</VideoPlayer>
47+
</VideoModalVideo>
48+
</VideoModalContent>
49+
</VideoModal>
50+
</div>
51+
);
52+
};
53+
54+
export default VideoModalDemo;
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import * as DialogPrimitive from "@radix-ui/react-dialog";
5+
import { motion } from "framer-motion";
6+
import { cn } from "@/lib/utils";
7+
8+
const VideoModal = DialogPrimitive.Root;
9+
10+
const VideoModalTrigger = DialogPrimitive.Trigger;
11+
12+
const VideoModalPortal = ({
13+
children,
14+
...props
15+
}: DialogPrimitive.DialogPortalProps) => (
16+
<DialogPrimitive.Portal {...props}>
17+
<div className="min-w-screen fixed inset-0 z-50 flex min-h-screen items-center justify-center p-2">
18+
{children}
19+
</div>
20+
</DialogPrimitive.Portal>
21+
);
22+
VideoModalPortal.displayName = DialogPrimitive.Portal.displayName;
23+
24+
const VideoModalOverlay = React.forwardRef<
25+
React.ElementRef<typeof DialogPrimitive.Overlay>,
26+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
27+
>(({ className, ...props }, ref) => (
28+
<DialogPrimitive.Overlay
29+
ref={ref}
30+
className={cn("fixed inset-0 z-50 backdrop-blur-xl", className)}
31+
{...props}
32+
asChild
33+
>
34+
<motion.div
35+
initial={{ opacity: 0 }}
36+
animate={{ opacity: 1 }}
37+
exit={{ opacity: 0 }}
38+
/>
39+
</DialogPrimitive.Overlay>
40+
));
41+
VideoModalOverlay.displayName = DialogPrimitive.Overlay.displayName;
42+
43+
const VideoModalContent = React.forwardRef<
44+
React.ElementRef<typeof DialogPrimitive.Content>,
45+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
46+
>(({ className, children, ...props }, ref) => (
47+
<VideoModalPortal>
48+
<VideoModalOverlay />
49+
<DialogPrimitive.Content ref={ref} {...props} asChild>
50+
<motion.div
51+
className={cn(
52+
"fixed z-50 flex h-screen w-screen items-center justify-center p-3",
53+
className,
54+
)}
55+
initial={{ opacity: 0 }}
56+
animate={{ opacity: 1 }}
57+
exit={{ opacity: 0 }}
58+
>
59+
<motion.div
60+
className="relative mx-auto flex h-full w-full items-center justify-center rounded-2xl border border-gray-950/[.1] bg-gray-50/[.2] dark:border-gray-50/[.1] dark:bg-gray-950/[.5]"
61+
initial={{ scale: 0.9, opacity: 0 }}
62+
animate={{ scale: 1, opacity: 1 }}
63+
exit={{ scale: 0.9, opacity: 0 }}
64+
transition={{ type: "spring", damping: 20, stiffness: 300 }}
65+
>
66+
{/* Mobile close button */}
67+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-full border border-gray-950/[.1] bg-gray-950/[.01] p-2 transition duration-300 hover:bg-gray-950/[.05] dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15] lg:hidden">
68+
<svg
69+
fill="none"
70+
height="12"
71+
viewBox="0 0 12 12"
72+
width="12"
73+
xmlns="http://www.w3.org/2000/svg"
74+
>
75+
<path
76+
d="M1 1L11 11M11 1L1 11"
77+
className="stroke-current"
78+
strokeLinecap="round"
79+
strokeLinejoin="round"
80+
strokeWidth="1.5"
81+
></path>
82+
</svg>
83+
<span className="sr-only">Close</span>
84+
</DialogPrimitive.Close>
85+
86+
<div className="flex h-[80%] w-full max-w-5xl gap-6">
87+
<DialogPrimitive.Close className="hidden self-start rounded-full border border-gray-950/[.1] bg-gray-950/[.01] p-2 transition duration-300 hover:bg-gray-950/[.05] dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15] lg:block">
88+
<svg
89+
fill="none"
90+
height="12"
91+
viewBox="0 0 12 12"
92+
width="12"
93+
xmlns="http://www.w3.org/2000/svg"
94+
>
95+
<path
96+
d="M1 1L11 11M11 1L1 11"
97+
className="stroke-current"
98+
strokeLinecap="round"
99+
strokeLinejoin="round"
100+
strokeWidth="1.5"
101+
></path>
102+
</svg>
103+
<span className="sr-only">Close</span>
104+
</DialogPrimitive.Close>
105+
<div className="flex w-full flex-col max-lg:p-4 max-lg:text-center">
106+
{children}
107+
</div>
108+
</div>
109+
</motion.div>
110+
</motion.div>
111+
</DialogPrimitive.Content>
112+
</VideoModalPortal>
113+
));
114+
VideoModalContent.displayName = DialogPrimitive.Content.displayName;
115+
116+
const VideoModalTitle = React.forwardRef<
117+
React.ElementRef<typeof DialogPrimitive.Title>,
118+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
119+
>(({ className, ...props }, ref) => (
120+
<DialogPrimitive.Title
121+
ref={ref}
122+
className={cn(
123+
"mb-4 text-4xl font-bold text-gray-950 dark:text-gray-50",
124+
className,
125+
)}
126+
{...props}
127+
/>
128+
));
129+
VideoModalTitle.displayName = DialogPrimitive.Title.displayName;
130+
131+
const VideoModalDescription = React.forwardRef<
132+
React.ElementRef<typeof DialogPrimitive.Description>,
133+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
134+
>(({ className, ...props }, ref) => (
135+
<DialogPrimitive.Description
136+
ref={ref}
137+
className={cn(
138+
"mb-6 text-xl text-gray-950/80 dark:text-gray-50/70",
139+
className,
140+
)}
141+
{...props}
142+
/>
143+
));
144+
VideoModalDescription.displayName = DialogPrimitive.Description.displayName;
145+
146+
const VideoPreview = React.forwardRef<
147+
HTMLDivElement,
148+
React.HTMLAttributes<HTMLDivElement>
149+
>(({ className, children, ...props }, ref) => (
150+
<div
151+
ref={ref}
152+
className={cn(
153+
"absolute inset-0 z-10 transition-opacity duration-300 group-[.playing]:pointer-events-none group-[.playing]:opacity-0",
154+
className,
155+
)}
156+
{...props}
157+
>
158+
{children}
159+
</div>
160+
));
161+
VideoPreview.displayName = "VideoPreview";
162+
163+
const VideoPlayButton = React.forwardRef<
164+
HTMLDivElement,
165+
React.HTMLAttributes<HTMLDivElement>
166+
>(({ className, children, ...props }, ref) => (
167+
<div
168+
ref={ref}
169+
className={cn(
170+
"absolute inset-0 z-20 flex items-center justify-center transition-opacity duration-300 group-[.playing]:pointer-events-none group-[.playing]:opacity-0",
171+
className,
172+
)}
173+
{...props}
174+
>
175+
{children}
176+
</div>
177+
));
178+
VideoPlayButton.displayName = "VideoPlayButton";
179+
180+
const VideoPlayer = React.forwardRef<
181+
HTMLDivElement,
182+
React.HTMLAttributes<HTMLDivElement>
183+
>(({ className, children, ...props }, ref) => {
184+
const [isPlaying, setIsPlaying] = React.useState(false);
185+
186+
return (
187+
<div
188+
ref={ref}
189+
className={cn(
190+
"group relative aspect-video max-w-4xl overflow-hidden rounded-xl border border-gray-950/[.1] object-cover dark:border-gray-50/[.1]",
191+
isPlaying && "playing",
192+
className,
193+
)}
194+
onClick={() => setIsPlaying(true)}
195+
{...props}
196+
>
197+
{children}
198+
</div>
199+
);
200+
});
201+
VideoPlayer.displayName = "VideoPlayer";
202+
203+
const VideoModalVideo = React.forwardRef<
204+
HTMLDivElement,
205+
React.HTMLAttributes<HTMLDivElement>
206+
>(({ className, children, ...props }, ref) => (
207+
<div
208+
ref={ref}
209+
className={cn(
210+
"aspect-video max-w-4xl overflow-hidden rounded-xl border border-gray-950/[.1] object-cover shadow-xl dark:border-gray-50/[.1]",
211+
className,
212+
)}
213+
{...props}
214+
>
215+
{children}
216+
</div>
217+
));
218+
VideoModalVideo.displayName = "VideoModalVideo";
219+
220+
export {
221+
VideoModal,
222+
VideoModalTrigger,
223+
VideoModalContent,
224+
VideoModalTitle,
225+
VideoModalDescription,
226+
VideoModalVideo,
227+
VideoPreview,
228+
VideoPlayButton,
229+
VideoPlayer,
230+
};

registry/registry-examples.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,9 @@ export const examples: Registry = [
477477
type: "registry:example",
478478
files: ["example/rainbow-button-demo.tsx"],
479479
},
480+
{
481+
name: "video-modal-demo",
482+
type: "registry:example",
483+
files: ["example/video-modal-demo.tsx"],
484+
},
480485
];

registry/registry-ui.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,4 +603,9 @@ export const ui: Registry = [
603603
},
604604
},
605605
},
606+
{
607+
name: "video-modal",
608+
type: "registry:ui",
609+
files: ["magicui/video-modal.tsx"],
610+
},
606611
];

0 commit comments

Comments
 (0)