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

Compare changes

Choose any two refs to compare.

Changed files
+146 -182
src
components
+136 -121
src/components/editor/Navbar.tsx
··· 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"; 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'; 13 8 import { 14 9 Download, 15 10 ChevronDown, 16 11 Coffee, 17 12 ExternalLink, 18 13 Loader2, 14 + QrCode, 19 15 Heart, 16 + Twitter, 20 17 X, 21 18 CheckCircle, 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"; 19 + Github 20 + } from 'lucide-react'; 21 + import { toast } from 'sonner'; 22 + import { useMockupStore } from '@/contexts/MockupContext'; 23 + import html2canvas from 'html2canvas'; 31 24 32 25 export function Navbar() { 33 26 const [showExportOptions, setShowExportOptions] = useState(false); 34 - const [exportFormat, setExportFormat] = useState("PNG"); 27 + const [exportFormat, setExportFormat] = useState('PNG'); 35 28 const [quality, setQuality] = useState([2]); 36 29 const [isExporting, setIsExporting] = useState(false); 37 30 const [isDialogOpen, setIsDialogOpen] = useState(false); 38 31 39 32 const { uploadedImage, imageBorder, fixedMargin, margin } = useMockupStore(); 40 33 41 - const isMobile = typeof window !== "undefined" ? window.innerWidth < 768 : false; 34 + const isMobile = typeof window !== 'undefined' ? window.innerWidth < 768 : false; 42 35 43 36 const getQualityLabel = (value) => { 44 37 switch (value) { 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`; 38 + case 1: return 'Standard (1x)'; 39 + case 2: return 'High (2x)'; 40 + case 3: return 'Ultra (3x)'; 41 + default: return `${value}x`; 53 42 } 54 43 }; 55 44 ··· 57 46 if (!uploadedImage) return; 58 47 59 48 try { 60 - const mockupElement = document.querySelector("[data-mockup-canvas]") as HTMLDivElement; 61 - if (!mockupElement) throw new Error("Mockup canvas not found"); 49 + const mockupElement = document.querySelector('[data-mockup-canvas]') as HTMLDivElement; 50 + if (!mockupElement) throw new Error('Mockup canvas not found'); 62 51 63 - const imgElement = mockupElement.querySelector("img") as HTMLImageElement; 64 - if (!imgElement) throw new Error("Image element not found"); 52 + const imgElement = mockupElement.querySelector('img') as HTMLImageElement; 53 + if (!imgElement) throw new Error('Image element not found'); 65 54 66 55 // For fixed margin mode: capture the entire canvas (image + background margins) 67 56 const canvas = await html2canvas(mockupElement, { ··· 73 62 }); 74 63 75 64 const mimeType = `image/${format.toLowerCase()}`; 76 - const imageQuality = format === "JPEG" ? 1 : undefined; 65 + const imageQuality = format === 'JPEG' ? 1 : undefined; 77 66 78 67 canvas.toBlob( 79 68 (blob) => { 80 69 if (blob) { 81 70 const url = URL.createObjectURL(blob); 82 - const a = document.createElement("a"); 71 + const a = document.createElement('a'); 83 72 a.href = url; 84 73 a.download = `mockup-${Date.now()}.${format.toLowerCase()}`; 85 74 document.body.appendChild(a); ··· 87 76 document.body.removeChild(a); 88 77 URL.revokeObjectURL(url); 89 78 } else { 90 - throw new Error("Failed to create blob"); 79 + throw new Error('Failed to create blob'); 91 80 } 92 81 }, 93 82 mimeType, 94 - imageQuality, 83 + imageQuality 95 84 ); 85 + 86 + 96 87 } catch (error) { 97 - console.error("Export error:", error); 88 + console.error('Export error:', error); 98 89 throw error; 99 90 } 100 91 }; 101 92 102 93 const handleSingleExport = async () => { 103 94 if (!uploadedImage) { 104 - toast.error("Please upload an image first"); 95 + toast.error('Please upload an image first'); 105 96 return; 106 97 } 107 98 ··· 109 100 try { 110 101 await exportImage(exportFormat, quality[0]); 111 102 toast.success(`Successfully exported as ${exportFormat}!`, { 112 - icon: <CheckCircle className="w-4 h-4" />, 103 + icon: <CheckCircle className="w-4 h-4" /> 113 104 }); 114 105 } catch (error) { 115 - toast.error("Failed to export image. Please try again."); 106 + toast.error('Failed to export image. Please try again.'); 116 107 } finally { 117 108 setIsExporting(false); 118 109 } ··· 120 111 121 112 const handleAllFormatsExport = async () => { 122 113 if (!uploadedImage) { 123 - toast.error("Please upload an image first"); 114 + toast.error('Please upload an image first'); 124 115 return; 125 116 } 126 117 127 118 setIsExporting(true); 128 - const formats = ["PNG", "JPEG", "WebP"]; 119 + const formats = ['PNG', 'JPEG', 'WebP']; 129 120 const qualityMultiplier = quality[0]; 130 121 131 122 try { 132 - await Promise.all(formats.map((format) => exportImage(format, qualityMultiplier))); 123 + await Promise.all( 124 + formats.map(format => exportImage(format, qualityMultiplier)) 125 + ); 133 126 134 - toast.success(`Successfully exported all formats (${formats.join(", ")})!`, { 135 - icon: <CheckCircle className="w-4 h-4" />, 127 + toast.success(`Successfully exported all formats (${formats.join(', ')})!`, { 128 + icon: <CheckCircle className="w-4 h-4" /> 136 129 }); 137 130 } catch (error) { 138 - console.error("Export all formats error:", error); 139 - toast.error("Failed to export some formats. Please try again."); 131 + console.error('Export all formats error:', error); 132 + toast.error('Failed to export some formats. Please try again.'); 140 133 } finally { 141 134 setIsExporting(false); 142 135 } 143 136 }; 144 137 145 138 const ExportOptionsContent = () => ( 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"}`}> 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'}`}> 152 146 <div> 153 147 <h3 className="text-xl font-semibold text-foreground mb-6 flex items-center gap-2"> 154 148 <Download className="w-5 h-5 text-primary" /> ··· 164 158 onValueChange={(value) => value && setExportFormat(value)} 165 159 className="flex gap-1 bg-sidebar/80 rounded-full ring-2 ring-secondary p-1" 166 160 > 167 - {["PNG", "JPEG", "WebP"].map((format) => ( 161 + {['PNG', 'JPEG', 'WebP'].map((format) => ( 168 162 <ToggleGroupItem 169 163 key={format} 170 164 value={format} ··· 211 205 <Button 212 206 onClick={handleSingleExport} 213 207 className="w-full h-12 font-medium shadow-md" 214 - variant="secondary" 208 + variant='secondary' 215 209 disabled={isExporting || !uploadedImage} 216 210 > 217 211 {isExporting ? ( ··· 246 240 </Button> 247 241 </div> 248 242 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" /> 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 /> 257 249 Hey, You can also help us out at here 258 250 </a> 259 251 </Button> 260 252 </div> 261 253 262 - <Card 263 - className={`rounded-xl border-2 border-dashed border-primary/20 ${!isMobile ? "order-1" : "order-2"} group`} 264 - > 254 + <Card className={`rounded-xl border-2 border-dashed border-primary/20 ${!isMobile ? 'order-1' : 'order-2'} group`}> 265 255 <CardHeader className="pb-4"> 266 256 <div className="flex items-center gap-2"> 267 257 <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" /> ··· 281 271 <ExternalLink className="w-3 h-3" /> 282 272 </a> 283 273 </p> 284 - <p className="text-sm text-muted-foreground leading-relaxed"> 274 + <p className='text-sm text-muted-foreground leading-relaxed'> 285 275 moocup is a simple offline tool. 286 276 </p> 287 - <p className="text-sm text-muted-foreground leading-relaxed"> 277 + <p className='text-sm text-muted-foreground leading-relaxed'> 288 278 focus on your craft, we'll take care of rest. 289 279 </p> 290 - <p className="text-sm text-muted-foreground leading-relaxed"> 280 + <p className='text-sm text-muted-foreground leading-relaxed'> 291 281 you can show your support by sponsoring my work! 292 282 </p> 293 283 </div> 294 284 <div className="space-y-3"> 295 - <Button asChild className="w-full h-12 hover:primary/10 shadow-md"> 285 + <Button 286 + asChild 287 + className="w-full h-12 hover:primary/10 shadow-md" 288 + > 296 289 <a 297 290 href="https://ko-fi.com/jaydipsanghani" 298 291 target="_blank" ··· 309 302 variant="outline" 310 303 className="w-full h-12 border-primary/30 hover:bg-primary/5 hover:border-primary/50 inline-flex items-center" 311 304 > 305 + <QrCode className="w-5 h-5" /> 312 306 <span className="font-medium">UPI (India)</span> 313 307 </Button> 314 308 </DialogTrigger> 315 309 <DialogContent className="max-w-xs"> 316 310 <DialogHeader> 317 - <DialogTitle className="text-center">Thanks for contribution!</DialogTitle> 311 + <DialogTitle className="text-center">Thanks so much!</DialogTitle> 318 312 </DialogHeader> 319 313 <div className="flex flex-col items-center gap-4 py-4"> 320 314 <div className="w-48 h-48 bg-muted rounded-xl flex items-center justify-center border-2 border-dashed border-muted-foreground/20"> 321 - <img 322 - src={upiQR} 323 - className="size-full" 324 - fetchPriority="auto" 325 - alt="QR code for making payment in Indian Rupees" 326 - /> 315 + <QrCode className="w-16 h-16 text-muted-foreground/50" /> 327 316 </div> 328 - <p className="text-sm text-muted-foreground text-center">Scan with any UPI app</p> 317 + <p className="text-sm text-muted-foreground text-center"> 318 + Scan with any UPI app 319 + </p> 329 320 </div> 330 321 </DialogContent> 331 322 </Dialog> 332 323 </div> 333 324 <div className="space-y-2"> 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"> 325 + <h4 className="text-sm font-medium text-muted-foreground">Say Hi! 326 + <span className='ml-2 text-sm text-muted-foreground leading-relaxed'> 337 327 I'm always up for quick chat :) 338 328 </span> 339 329 </h4> 340 330 <div className="flex gap-2"> 341 - <Button 342 - variant="secondary" 343 - size="sm" 344 - asChild 345 - className="flex-1 border-primary/30 hover:border-primary/50" 346 - > 331 + <Button variant="secondary" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 347 332 <a 348 333 href="https://bsky.app/profile/jellydeck.bsky.social" 349 334 target="_blank" 350 335 rel="noopener noreferrer" 351 336 className="flex items-center gap-2" 352 337 > 353 - <Bluesky className="size-4" /> 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> 354 345 <span>Bluesky</span> 355 346 </a> 356 347 </Button> 357 - <Button 358 - variant="secondary" 359 - size="sm" 360 - asChild 361 - className="flex-1 border-primary/30 hover:border-primary/50" 362 - > 348 + <Button variant="secondary" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 363 349 <a 364 - href="https://x.com/JellyDeck" 350 + href="https://twitter.com/JellyDeck" 365 351 target="_blank" 366 352 rel="noopener noreferrer" 367 353 className="flex items-center gap-2" 368 354 > 369 - <XIcon className="size-4" /> 355 + <Twitter className="w-4 h-4" /> 370 356 <span>Twitter</span> 371 357 </a> 372 358 </Button> ··· 381 367 <div className="sticky top-0 z-50 bg-background/80 backdrop-blur-lg border-b"> 382 368 <div className="flex items-center justify-between px-4 h-16 lg:mx-40"> 383 369 <div className="flex items-center gap-4"> 384 - <div className="flex items-center"> 370 + <div className="flex items-center gap-2"> 385 371 <h1 className="text-xl font-bold text-primary">Moo</h1> 386 372 <a 387 373 href="https://ko-fi.com/jaydipsanghani" ··· 409 395 )} 410 396 </div> 411 397 <div className="flex items-center gap-2"> 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" /> 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> 415 428 Send us a star! 416 429 </Button> 417 430 </a> 418 431 {isMobile ? ( 419 432 <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> 420 433 <DialogTrigger asChild> 421 - <Button size="icon" disabled={!uploadedImage} className="h-10 w-10"> 434 + <Button 435 + size="icon" 436 + disabled={!uploadedImage} 437 + className="h-10 w-10" 438 + > 422 439 <Download className="w-5 h-5" /> 423 440 </Button> 424 441 </DialogTrigger> ··· 442 459 </DialogContent> 443 460 </Dialog> 444 461 ) : ( 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" 462 + <div className="relative"> 463 + <Button 464 + variant="outline" 465 + onClick={() => setShowExportOptions(!showExportOptions)} 466 + disabled={!uploadedImage} 467 + className="gap-2" 458 468 > 459 - <Card className="shadow-none border-0"> 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"> 460 475 <CardContent className="p-6"> 461 476 <ExportOptionsContent /> 462 477 </CardContent> 463 478 </Card> 464 - </PopoverContent> 465 - </Popover> 479 + )} 480 + </div> 466 481 )} 467 482 </div> 468 483 </div> 469 484 </div> 470 485 ); 471 - } 486 + }
-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=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, 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 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 }