stunning screenshots in seconds https://moocup.jaydip.me

Compare changes

Choose any two refs to compare.

Changed files
+182 -146
src
components
+121 -136
src/components/editor/Navbar.tsx
··· 1 - import { useState } from 'react'; 2 - import { Button } from '@/components/ui/button'; 3 - import { Slider } from '@/components/ui/slider'; 4 - import { Card, CardContent, CardHeader } from '@/components/ui/card'; 5 - import { Dialog, DialogContent, DialogTrigger, DialogHeader, DialogTitle } from '@/components/ui/dialog'; 6 - import { Badge } from '@/components/ui/badge'; 7 - import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; 1 + import { useState } from "react"; 2 + import { Button } from "@/components/ui/button"; 3 + import { Card, CardContent, CardHeader } from "@/components/ui/card"; 4 + import { 5 + Dialog, 6 + DialogContent, 7 + DialogTrigger, 8 + DialogHeader, 9 + DialogTitle, 10 + } from "@/components/ui/dialog"; 11 + import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; 12 + import upiQR from "/assets/upiQR.png"; 8 13 import { 9 14 Download, 10 15 ChevronDown, 11 16 Coffee, 12 17 ExternalLink, 13 18 Loader2, 14 - QrCode, 15 19 Heart, 16 - Twitter, 17 20 X, 18 21 CheckCircle, 19 - Github 20 - } from 'lucide-react'; 21 - import { toast } from 'sonner'; 22 - import { useMockupStore } from '@/contexts/MockupContext'; 23 - import html2canvas from 'html2canvas'; 22 + } from "lucide-react"; 23 + import { Github } from "@/components/icons/Github"; 24 + import { Bluesky } from "@/components/icons/Bluesky"; 25 + import { X as XIcon } from "@/components/icons/X"; 26 + 27 + import { toast } from "sonner"; 28 + import { useMockupStore } from "@/contexts/MockupContext"; 29 + import html2canvas from "html2canvas"; 30 + import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; 24 31 25 32 export function Navbar() { 26 33 const [showExportOptions, setShowExportOptions] = useState(false); 27 - const [exportFormat, setExportFormat] = useState('PNG'); 34 + const [exportFormat, setExportFormat] = useState("PNG"); 28 35 const [quality, setQuality] = useState([2]); 29 36 const [isExporting, setIsExporting] = useState(false); 30 37 const [isDialogOpen, setIsDialogOpen] = useState(false); 31 38 32 39 const { uploadedImage, imageBorder, fixedMargin, margin } = useMockupStore(); 33 40 34 - const isMobile = typeof window !== 'undefined' ? window.innerWidth < 768 : false; 41 + const isMobile = typeof window !== "undefined" ? window.innerWidth < 768 : false; 35 42 36 43 const getQualityLabel = (value) => { 37 44 switch (value) { 38 - case 1: return 'Standard (1x)'; 39 - case 2: return 'High (2x)'; 40 - case 3: return 'Ultra (3x)'; 41 - default: return `${value}x`; 45 + case 1: 46 + return "Standard (1x)"; 47 + case 2: 48 + return "High (2x)"; 49 + case 3: 50 + return "Ultra (3x)"; 51 + default: 52 + return `${value}x`; 42 53 } 43 54 }; 44 55 ··· 46 57 if (!uploadedImage) return; 47 58 48 59 try { 49 - const mockupElement = document.querySelector('[data-mockup-canvas]') as HTMLDivElement; 50 - if (!mockupElement) throw new Error('Mockup canvas not found'); 60 + const mockupElement = document.querySelector("[data-mockup-canvas]") as HTMLDivElement; 61 + if (!mockupElement) throw new Error("Mockup canvas not found"); 51 62 52 - const imgElement = mockupElement.querySelector('img') as HTMLImageElement; 53 - if (!imgElement) throw new Error('Image element not found'); 63 + const imgElement = mockupElement.querySelector("img") as HTMLImageElement; 64 + if (!imgElement) throw new Error("Image element not found"); 54 65 55 66 // For fixed margin mode: capture the entire canvas (image + background margins) 56 67 const canvas = await html2canvas(mockupElement, { ··· 62 73 }); 63 74 64 75 const mimeType = `image/${format.toLowerCase()}`; 65 - const imageQuality = format === 'JPEG' ? 1 : undefined; 76 + const imageQuality = format === "JPEG" ? 1 : undefined; 66 77 67 78 canvas.toBlob( 68 79 (blob) => { 69 80 if (blob) { 70 81 const url = URL.createObjectURL(blob); 71 - const a = document.createElement('a'); 82 + const a = document.createElement("a"); 72 83 a.href = url; 73 84 a.download = `mockup-${Date.now()}.${format.toLowerCase()}`; 74 85 document.body.appendChild(a); ··· 76 87 document.body.removeChild(a); 77 88 URL.revokeObjectURL(url); 78 89 } else { 79 - throw new Error('Failed to create blob'); 90 + throw new Error("Failed to create blob"); 80 91 } 81 92 }, 82 93 mimeType, 83 - imageQuality 94 + imageQuality, 84 95 ); 85 - 86 - 87 96 } catch (error) { 88 - console.error('Export error:', error); 97 + console.error("Export error:", error); 89 98 throw error; 90 99 } 91 100 }; 92 101 93 102 const handleSingleExport = async () => { 94 103 if (!uploadedImage) { 95 - toast.error('Please upload an image first'); 104 + toast.error("Please upload an image first"); 96 105 return; 97 106 } 98 107 ··· 100 109 try { 101 110 await exportImage(exportFormat, quality[0]); 102 111 toast.success(`Successfully exported as ${exportFormat}!`, { 103 - icon: <CheckCircle className="w-4 h-4" /> 112 + icon: <CheckCircle className="w-4 h-4" />, 104 113 }); 105 114 } catch (error) { 106 - toast.error('Failed to export image. Please try again.'); 115 + toast.error("Failed to export image. Please try again."); 107 116 } finally { 108 117 setIsExporting(false); 109 118 } ··· 111 120 112 121 const handleAllFormatsExport = async () => { 113 122 if (!uploadedImage) { 114 - toast.error('Please upload an image first'); 123 + toast.error("Please upload an image first"); 115 124 return; 116 125 } 117 126 118 127 setIsExporting(true); 119 - const formats = ['PNG', 'JPEG', 'WebP']; 128 + const formats = ["PNG", "JPEG", "WebP"]; 120 129 const qualityMultiplier = quality[0]; 121 130 122 131 try { 123 - await Promise.all( 124 - formats.map(format => exportImage(format, qualityMultiplier)) 125 - ); 132 + await Promise.all(formats.map((format) => exportImage(format, qualityMultiplier))); 126 133 127 - toast.success(`Successfully exported all formats (${formats.join(', ')})!`, { 128 - icon: <CheckCircle className="w-4 h-4" /> 134 + toast.success(`Successfully exported all formats (${formats.join(", ")})!`, { 135 + icon: <CheckCircle className="w-4 h-4" />, 129 136 }); 130 137 } catch (error) { 131 - console.error('Export all formats error:', error); 132 - toast.error('Failed to export some formats. Please try again.'); 138 + console.error("Export all formats error:", error); 139 + toast.error("Failed to export some formats. Please try again."); 133 140 } finally { 134 141 setIsExporting(false); 135 142 } 136 143 }; 137 144 138 145 const ExportOptionsContent = () => ( 139 - <div className={` 140 - ${isMobile 141 - ? 'flex flex-col gap-2' 142 - : 'grid grid-cols-2 gap-6' 143 - } 144 - `}> 145 - <div className={`${!isMobile ? 'order-2' : 'order-1'}`}> 146 + <div 147 + className={` 148 + ${isMobile ? "flex flex-col gap-2" : "grid grid-cols-2 gap-6"} 149 + `} 150 + > 151 + <div className={`${!isMobile ? "order-2" : "order-1"}`}> 146 152 <div> 147 153 <h3 className="text-xl font-semibold text-foreground mb-6 flex items-center gap-2"> 148 154 <Download className="w-5 h-5 text-primary" /> ··· 158 164 onValueChange={(value) => value && setExportFormat(value)} 159 165 className="flex gap-1 bg-sidebar/80 rounded-full ring-2 ring-secondary p-1" 160 166 > 161 - {['PNG', 'JPEG', 'WebP'].map((format) => ( 167 + {["PNG", "JPEG", "WebP"].map((format) => ( 162 168 <ToggleGroupItem 163 169 key={format} 164 170 value={format} ··· 205 211 <Button 206 212 onClick={handleSingleExport} 207 213 className="w-full h-12 font-medium shadow-md" 208 - variant='secondary' 214 + variant="secondary" 209 215 disabled={isExporting || !uploadedImage} 210 216 > 211 217 {isExporting ? ( ··· 240 246 </Button> 241 247 </div> 242 248 243 - <Button 244 - variant="link" 245 - className="w-full" 246 - > 247 - <a href='https://github.com/jellydeck/moocup' target="_blank" rel="noopener noreferrer" className='inline-flex gap-2 w-full grid-cols-2'> 248 - <Github /> 249 + <Button variant="link" className="w-full"> 250 + <a 251 + href="https://github.com/jellydeck/moocup" 252 + target="_blank" 253 + rel="noopener noreferrer" 254 + className="inline-flex gap-2 w-full grid-cols-2" 255 + > 256 + <Github className="size-5" /> 249 257 Hey, You can also help us out at here 250 258 </a> 251 259 </Button> 252 260 </div> 253 261 254 - <Card className={`rounded-xl border-2 border-dashed border-primary/20 ${!isMobile ? 'order-1' : 'order-2'} group`}> 262 + <Card 263 + className={`rounded-xl border-2 border-dashed border-primary/20 ${!isMobile ? "order-1" : "order-2"} group`} 264 + > 255 265 <CardHeader className="pb-4"> 256 266 <div className="flex items-center gap-2"> 257 267 <Heart className="w-5 h-5 text-primary fill-primary/20 group-hover:fill-primary/50 transition-colors group-hover:animate-pulse group-hover:scale-110" /> ··· 271 281 <ExternalLink className="w-3 h-3" /> 272 282 </a> 273 283 </p> 274 - <p className='text-sm text-muted-foreground leading-relaxed'> 284 + <p className="text-sm text-muted-foreground leading-relaxed"> 275 285 moocup is a simple offline tool. 276 286 </p> 277 - <p className='text-sm text-muted-foreground leading-relaxed'> 287 + <p className="text-sm text-muted-foreground leading-relaxed"> 278 288 focus on your craft, we'll take care of rest. 279 289 </p> 280 - <p className='text-sm text-muted-foreground leading-relaxed'> 290 + <p className="text-sm text-muted-foreground leading-relaxed"> 281 291 you can show your support by sponsoring my work! 282 292 </p> 283 293 </div> 284 294 <div className="space-y-3"> 285 - <Button 286 - asChild 287 - className="w-full h-12 hover:primary/10 shadow-md" 288 - > 295 + <Button asChild className="w-full h-12 hover:primary/10 shadow-md"> 289 296 <a 290 297 href="https://ko-fi.com/jaydipsanghani" 291 298 target="_blank" ··· 302 309 variant="outline" 303 310 className="w-full h-12 border-primary/30 hover:bg-primary/5 hover:border-primary/50 inline-flex items-center" 304 311 > 305 - <QrCode className="w-5 h-5" /> 306 312 <span className="font-medium">UPI (India)</span> 307 313 </Button> 308 314 </DialogTrigger> 309 315 <DialogContent className="max-w-xs"> 310 316 <DialogHeader> 311 - <DialogTitle className="text-center">Thanks so much!</DialogTitle> 317 + <DialogTitle className="text-center">Thanks for contribution!</DialogTitle> 312 318 </DialogHeader> 313 319 <div className="flex flex-col items-center gap-4 py-4"> 314 320 <div className="w-48 h-48 bg-muted rounded-xl flex items-center justify-center border-2 border-dashed border-muted-foreground/20"> 315 - <QrCode className="w-16 h-16 text-muted-foreground/50" /> 321 + <img 322 + src={upiQR} 323 + className="size-full" 324 + fetchPriority="auto" 325 + alt="QR code for making payment in Indian Rupees" 326 + /> 316 327 </div> 317 - <p className="text-sm text-muted-foreground text-center"> 318 - Scan with any UPI app 319 - </p> 328 + <p className="text-sm text-muted-foreground text-center">Scan with any UPI app</p> 320 329 </div> 321 330 </DialogContent> 322 331 </Dialog> 323 332 </div> 324 333 <div className="space-y-2"> 325 - <h4 className="text-sm font-medium text-muted-foreground">Say Hi! 326 - <span className='ml-2 text-sm text-muted-foreground leading-relaxed'> 334 + <h4 className="text-sm font-medium text-muted-foreground"> 335 + Say Hi! 336 + <span className="ml-2 text-sm text-muted-foreground leading-relaxed"> 327 337 I'm always up for quick chat :) 328 338 </span> 329 339 </h4> 330 340 <div className="flex gap-2"> 331 - <Button variant="secondary" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 341 + <Button 342 + variant="secondary" 343 + size="sm" 344 + asChild 345 + className="flex-1 border-primary/30 hover:border-primary/50" 346 + > 332 347 <a 333 348 href="https://bsky.app/profile/jellydeck.bsky.social" 334 349 target="_blank" 335 350 rel="noopener noreferrer" 336 351 className="flex items-center gap-2" 337 352 > 338 - <svg 339 - xmlns="http://www.w3.org/2000/svg" 340 - viewBox="0 0 24 24" 341 - className='w-4 h-4' 342 - > 343 - <path fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10Q2-2 2 6t5 8q-5 3-1 6t6-3q2 6 6 3t-1-6q5 0 5-8t-10 4" /> 344 - </svg> 353 + <Bluesky className="size-4" /> 345 354 <span>Bluesky</span> 346 355 </a> 347 356 </Button> 348 - <Button variant="secondary" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 357 + <Button 358 + variant="secondary" 359 + size="sm" 360 + asChild 361 + className="flex-1 border-primary/30 hover:border-primary/50" 362 + > 349 363 <a 350 - href="https://twitter.com/JellyDeck" 364 + href="https://x.com/JellyDeck" 351 365 target="_blank" 352 366 rel="noopener noreferrer" 353 367 className="flex items-center gap-2" 354 368 > 355 - <Twitter className="w-4 h-4" /> 369 + <XIcon className="size-4" /> 356 370 <span>Twitter</span> 357 371 </a> 358 372 </Button> ··· 367 381 <div className="sticky top-0 z-50 bg-background/80 backdrop-blur-lg border-b"> 368 382 <div className="flex items-center justify-between px-4 h-16 lg:mx-40"> 369 383 <div className="flex items-center gap-4"> 370 - <div className="flex items-center gap-2"> 384 + <div className="flex items-center"> 371 385 <h1 className="text-xl font-bold text-primary">Moo</h1> 372 386 <a 373 387 href="https://ko-fi.com/jaydipsanghani" ··· 395 409 )} 396 410 </div> 397 411 <div className="flex items-center gap-2"> 398 - <a href='https://github.com/jellydeck/moocup' target="_blank" rel="noopener noreferrer" 399 - > 400 - <Button 401 - variant="outline" 402 - className="gap-2 mr-2" 403 - > 404 - <svg xmlns="http://www.w3.org/2000/svg" stroke="#000" viewBox="0 0 20 20"> 405 - <g id="SVGRepo_iconCarrier"> 406 - <g 407 - id="Page-1" 408 - fill="none" 409 - fillRule="evenodd" 410 - stroke="none" 411 - strokeWidth="1" 412 - > 413 - <g 414 - id="Dribbble-Light-Preview" 415 - fill="#99d28e" 416 - transform="translate(-140 -7559)" 417 - > 418 - <g id="icons" transform="translate(56 160)"> 419 - <path 420 - id="github-[#99d28e142]" 421 - d="M94 7399c5.523 0 10 4.59 10 10.253 0 4.529-2.862 8.371-6.833 9.728-.507.101-.687-.219-.687-.492 0-.338.012-1.442.012-2.814 0-.956-.32-1.58-.679-1.898 2.227-.254 4.567-1.121 4.567-5.059 0-1.12-.388-2.034-1.03-2.752.104-.259.447-1.302-.098-2.714 0 0-.838-.275-2.747 1.051a9.4 9.4 0 0 0-2.505-.345 9.4 9.4 0 0 0-2.503.345c-1.911-1.326-2.751-1.051-2.751-1.051-.543 1.412-.2 2.455-.097 2.714-.639.718-1.03 1.632-1.03 2.752 0 3.928 2.335 4.808 4.556 5.067-.286.256-.545.708-.635 1.371-.57.262-2.018.715-2.91-.852 0 0-.529-.985-1.533-1.057 0 0-.975-.013-.068.623 0 0 .655.315 1.11 1.5 0 0 .587 1.83 3.369 1.21.005.857.014 1.665.014 1.909 0 .271-.184.588-.683.493-3.974-1.355-6.839-5.199-6.839-9.729 0-5.663 4.478-10.253 10-10.253" 422 - ></path> 423 - </g> 424 - </g> 425 - </g> 426 - </g> 427 - </svg> 412 + <a href="https://github.com/jellydeck/moocup" target="_blank" rel="noopener noreferrer"> 413 + <Button variant="outline" className="gap-2 mr-2"> 414 + <Github className="size-5" /> 428 415 Send us a star! 429 416 </Button> 430 417 </a> 431 418 {isMobile ? ( 432 419 <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> 433 420 <DialogTrigger asChild> 434 - <Button 435 - size="icon" 436 - disabled={!uploadedImage} 437 - className="h-10 w-10" 438 - > 421 + <Button size="icon" disabled={!uploadedImage} className="h-10 w-10"> 439 422 <Download className="w-5 h-5" /> 440 423 </Button> 441 424 </DialogTrigger> ··· 459 442 </DialogContent> 460 443 </Dialog> 461 444 ) : ( 462 - <div className="relative"> 463 - <Button 464 - variant="outline" 465 - onClick={() => setShowExportOptions(!showExportOptions)} 466 - disabled={!uploadedImage} 467 - className="gap-2" 445 + <Popover> 446 + <PopoverTrigger asChild> 447 + <Button variant="outline" disabled={!uploadedImage} className="gap-2 group"> 448 + <Download className="w-4 h-4" /> 449 + Export 450 + <ChevronDown className="size-4 transition-transform group-data-[state=open]:rotate-180" /> 451 + </Button> 452 + </PopoverTrigger> 453 + 454 + <PopoverContent 455 + align="end" 456 + side="bottom" 457 + className="w-[900px] p-0.5 border rounded-2xl mt-1.5" 468 458 > 469 - <Download className="w-4 h-4" /> 470 - Export 471 - <ChevronDown className={`w-4 h-4 transition-transform ${showExportOptions ? 'rotate-180' : ''}`} /> 472 - </Button> 473 - {showExportOptions && ( 474 - <Card className="absolute right-0 top-full mt-2 w-[900px] shadow-lg z-50 bg-background border rounded-2xl"> 459 + <Card className="shadow-none border-0"> 475 460 <CardContent className="p-6"> 476 461 <ExportOptionsContent /> 477 462 </CardContent> 478 463 </Card> 479 - )} 480 - </div> 464 + </PopoverContent> 465 + </Popover> 481 466 )} 482 467 </div> 483 468 </div> 484 469 </div> 485 470 ); 486 - } 471 + }
+14
src/components/icons/Bluesky.tsx
··· 1 + export function Bluesky(props: React.SVGProps<SVGSVGElement>) { 2 + return ( 3 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}> 4 + <path 5 + fill="none" 6 + stroke="currentColor" 7 + strokeLinecap="round" 8 + strokeLinejoin="round" 9 + strokeWidth="2" 10 + d="M12 10Q2-2 2 6t5 8q-5 3-1 6t6-3q2 6 6 3t-1-6q5 0 5-8t-10 4" 11 + /> 12 + </svg> 13 + ); 14 + }
+21
src/components/icons/Github.tsx
··· 1 + export function Github(props: React.SVGProps<SVGSVGElement>) { 2 + return ( 3 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" {...props}> 4 + <path 5 + fill="#99d28e" 6 + stroke="none" 7 + d="M10 0c5.523 0 10 4.59 10 10.253 0 4.529-2.862 8.371-6.833 9.728-.507.101-.687-.219-.687-.492 8 + 0-.338.012-1.442.012-2.814 0-.956-.32-1.58-.679-1.898 9 + 2.227-.254 4.567-1.121 4.567-5.059 0-1.12-.388-2.034-1.03-2.752 10 + .104-.259.447-1.302-.098-2.714 0 0-.838-.275-2.747 1.051A9.4 9.4 11 + 0 0 0 10 4.958a9.4 9.4 0 0 0-2.503.345C5.586 3.977 4.746 4.252 12 + 4.746 4.252c-.543 1.412-.2 2.455-.097 2.714-.639.718-1.03 1.632-1.03 13 + 2.752 0 3.928 2.335 4.808 4.556 5.067-.286.256-.545.708-.635 14 + 1.371-.57.262-2.018.715-2.91-.852 0 0-.529-.985-1.533-1.057 15 + 0 0-.975-.013-.068.623 0 0 .655.315 1.11 1.5 0 0 .587 1.83 16 + 3.369 1.21.005.857.014 1.665.014 1.909 0 .271-.184.588-.683.493C2.865 17 + 18.627 0 14.783 0 10.253 0 4.59 4.478 0 10 0" 18 + /> 19 + </svg> 20 + ); 21 + }
+16
src/components/icons/X.tsx
··· 1 + export function X(props: React.SVGProps<SVGSVGElement>) { 2 + return ( 3 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}> 4 + <path 5 + fill="currentColor" 6 + fillRule="evenodd" 7 + d="m13.458 9.122l7.516-7.965h2.491l-.01.011l-8.89 9.424l8.139 8 + 10.802a.906.906 0 0 1-.658 1.45H16.95a.9.9 0 0 1-.659-.359l-5.727-7.601l-7.472 9 + 7.96H.535l8.922-9.43L1.318 2.612a.906.906 0 0 1 .724-1.452h4.965c.285 0 10 + .553.134.724.361zm-.763 2a1 1 0 0 1-.07-.093l-6.07-8.056H3.86l13.607 11 + 18.06h2.696z" 12 + clipRule="evenodd" 13 + /> 14 + </svg> 15 + ); 16 + }
+10 -10
src/components/ui/popover.tsx
··· 1 - import * as React from "react" 2 - import * as PopoverPrimitive from "@radix-ui/react-popover" 1 + import * as React from "react"; 2 + import * as PopoverPrimitive from "@radix-ui/react-popover"; 3 3 4 - import { cn } from "@/lib/utils" 4 + import { cn } from "@/lib/utils"; 5 5 6 - const Popover = PopoverPrimitive.Root 6 + const Popover = PopoverPrimitive.Root; 7 7 8 - const PopoverTrigger = PopoverPrimitive.Trigger 8 + const PopoverTrigger = PopoverPrimitive.Trigger; 9 9 10 10 const PopoverContent = React.forwardRef< 11 11 React.ElementRef<typeof PopoverPrimitive.Content>, ··· 17 17 align={align} 18 18 sideOffset={sideOffset} 19 19 className={cn( 20 - "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 21 - className 20 + "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 21 + className, 22 22 )} 23 23 {...props} 24 24 /> 25 25 </PopoverPrimitive.Portal> 26 - )) 27 - PopoverContent.displayName = PopoverPrimitive.Content.displayName 26 + )); 27 + PopoverContent.displayName = PopoverPrimitive.Content.displayName; 28 28 29 - export { Popover, PopoverTrigger, PopoverContent } 29 + export { Popover, PopoverTrigger, PopoverContent };