full rework in vite

This commit is contained in:
2026-08-14 23:43:49 +02:00
parent c64f94f96b
commit a99d913420
34 changed files with 2032 additions and 6076 deletions
+46
View File
@@ -0,0 +1,46 @@
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
import type { Topic } from "@/data/topics"
export default function TopicContent({ topic }: { topic: Topic }) {
return (
<section className="py-12 md:py-24 w-full">
<div className="container mx-auto px-4 md:px-6">
<h2 className="text-3xl font-bold mb-8">{topic.title}</h2>
<p className="text-lg mb-8">{topic.description}</p>
<div className="grid md:grid-cols-2 gap-16">
{topic.items.map((item, itemIndex) => (
<div key={itemIndex}>
<h3 className="text-2xl font-bold mb-4">{item.title}</h3>
<p className="text-muted-foreground mb-6">{item.description}</p>
<div className="bg-background rounded-lg p-6 shadow-2xl my-4">
<Carousel className="max-w px-8">
<CarouselContent>
<CarouselItem>
<pre className="text-sm font-mono">
<code className="text-pretty">{item.code}</code>
</pre>
</CarouselItem>
<CarouselItem>
<pre className="text-sm font-mono">
<code className="text-pretty">{item.result}</code>
</pre>
</CarouselItem>
</CarouselContent>
<CarouselPrevious className="mx-6"/>
<CarouselNext className="mx-6"/>
</Carousel>
</div>
<p className="text-pretty">{item.explanation}</p>
</div>
))}
</div>
</div>
</section>
)
}
+110
View File
@@ -0,0 +1,110 @@
import { JSX, SVGProps } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
export default function Footer() {
return (
<footer className="bg-muted text-muted-foreground py-6">
<div className="container mx-auto px-4 md:px-6 flex items-center justify-between">
<p className="text-sm">&copy; 2024 Quintavalle Pietro. All rights reserved.</p>
<nav className="flex items-center gap-4">
<Dialog>
<DialogTrigger>Licence</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>MIT License</DialogTitle>
<DialogDescription>
<p className="font-bold">Copyright (c) 2024 Quintavalle Pietro</p>
<p className="mt-2">
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
</p>
<p>
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
</p>
<p className="mt-2">
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</p>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
<a href="https://github.com/Quinta0" target="_blank" rel="noopener noreferrer"
className="hover:underline">
<GitHubIcon className="w-6 h-6"/>
</a>
<a href="mailto:0pietroquintavalle0@gmail.com" className="hover:underline">
<GmailIcon className="w-6 h-6"/>
</a>
<a href="https://www.linkedin.com/in/pietro-quintavalle-996b96267/" target="_blank"
rel="noopener noreferrer" className="hover:underline">
<LinkedInIcon className="w-6 h-6"/>
</a>
</nav>
</div>
</footer>
)
}
function GitHubIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
strokeWidth="0"
>
<path d="M12 .5C5.48.5.5 5.48.5 12c0 5.08 3.29 9.35 7.85 10.86.58.1.79-.24.79-.54 0-.27-.01-.99-.01-1.94-3.19.69-3.86-1.54-3.86-1.54-.53-1.36-1.29-1.72-1.29-1.72-1.05-.72.08-.71.08-.71 1.16.08 1.77 1.2 1.77 1.2 1.03 1.77 2.69 1.26 3.34.96.1-.75.4-1.26.73-1.55-2.55-.29-5.23-1.28-5.23-5.72 0-1.26.45-2.29 1.2-3.1-.12-.29-.52-1.45.12-3.02 0 0 .97-.31 3.17 1.18.92-.26 1.91-.39 2.89-.39.98 0 1.97.13 2.89.39 2.2-1.49 3.17-1.18 3.17-1.18.64 1.57.24 2.73.12 3.02.75.81 1.2 1.84 1.2 3.1 0 4.46-2.69 5.43-5.25 5.71.41.35.78 1.03.78 2.08 0 1.5-.01 2.72-.01 3.08 0 .3.21.65.8.54C20.71 21.35 24 17.08 24 12c0-6.52-4.98-11.5-12-11.5z"/>
</svg>
)
}
function GmailIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
strokeWidth="0"
>
<path d="M12 12.713l11.985-8.677a.868.868 0 0 0-.491-.148H.506c-.177 0-.344.055-.491.148L12 12.713zm0 1.431L.035 5.596A.875.875 0 0 0 0 6.125v11.75c0 .478.387.875.875.875h22.25c.478 0 .875-.387.875-.875V6.125a.875.875 0 0 0-.035-.529L12 14.144z"/>
</svg>
)
}
function LinkedInIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
strokeWidth="0"
>
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.761 0 5-2.239 5-5v-14c0-2.761-2.239-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.5c-.966 0-1.75-.784-1.75-1.75s.784-1.75 1.75-1.75 1.75.784 1.75 1.75-.784 1.75-1.75 1.75zm13.5 12.5h-3v-5.5c0-1.379-1.121-2.5-2.5-2.5s-2.5 1.121-2.5 2.5v5.5h-3v-11h3v1.474c.809-1.161 2.201-1.974 3.5-1.974 2.481 0 4.5 2.019 4.5 4.5v7z"/>
</svg>
)
}
+113
View File
@@ -0,0 +1,113 @@
import { Link } from "react-router-dom";
import { topics } from "@/data/topics";
import { JSX, SVGProps } from "react";
export default function Navigation() {
return (
<header className="bg-primary text-primary-foreground py-4 px-6 shadow">
<div className="container mx-auto flex items-center justify-between">
<Link to="/" className="flex items-center gap-2 font-bold text-xl">
<CodeIcon className="w-6 h-6"/>
Dot Java
</Link>
<nav className="hidden md:flex items-center gap-6">
{topics.map((topic) => (
<Link key={topic.id} to={`/topic/${topic.id}`} className="hover:underline">
{topic.title}
</Link>
))}
</nav>
</div>
</header>
);
}
function CodeIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
width="24"
height="24"
viewBox="0 0 512 512"
enableBackground="23 23 23"
xmlSpace="preserve"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path fill="#171717" opacity="1.000000" stroke="none"
d="
M240.000000,513.000000
C160.019394,513.000000 80.538788,513.000000 1.029092,513.000000
C1.029092,342.394379 1.029092,171.788727 1.029092,1.091547
C171.563965,1.091547 342.127991,1.091547 512.846008,1.091547
C512.846008,171.666565 512.846008,342.333252 512.846008,513.000000
C422.122040,513.000000 331.311005,513.000000 240.000000,513.000000
M410.993988,312.497375
C410.656464,320.474792 410.875092,328.522644 409.875427,336.416199
C407.066986,358.591736 395.828674,374.636627 374.031616,381.696930
C360.300110,386.144745 346.144257,386.188507 332.139099,382.561066
C314.212372,377.917847 301.829712,366.828979 296.220825,349.172028
C293.632660,341.024353 292.780731,332.325165 291.074066,323.528076
C278.871552,323.528076 265.968353,323.528076 252.056076,323.528076
C253.064514,332.063293 253.489624,340.370422 255.100037,348.441040
C260.761566,376.813599 275.974670,398.381897 302.717468,410.285675
C331.966187,423.304871 362.237671,423.536865 392.339081,413.506226
C415.206604,405.886169 431.804352,390.516083 441.814331,368.480042
C448.760773,353.188080 450.558044,336.881256 450.544250,320.242584
C450.483337,246.767456 450.518280,173.292236 450.518524,99.817055
C450.518524,98.201042 450.518524,96.585030 450.518524,94.734009
C411.417328,94.734009 372.851379,94.734009 334.226685,94.734009
C334.226685,106.749191 334.226685,118.510406 334.226685,130.714798
C359.846375,130.714798 385.238434,130.714798 410.481018,130.714798
C410.747650,131.456650 410.950775,131.758057 410.950897,132.059509
C410.974640,191.872437 410.984070,251.685364 410.993988,312.497375
M87.691093,348.998718
C64.397789,356.446655 60.637661,382.525238 66.682755,398.797058
C73.876472,418.160767 99.387474,426.453217 119.434303,416.107788
C133.284363,408.960266 139.973038,393.206451 136.584061,375.933624
C133.839737,361.946350 126.024956,352.428772 112.140968,348.535126
C104.306267,346.337982 96.337631,346.484802 87.691093,348.998718
z"/>
<path fill="#FCFCFC" opacity="1.000000" stroke="none"
d="
M410.993958,311.997833
C410.984070,251.685364 410.974640,191.872437 410.950897,132.059509
C410.950775,131.758057 410.747650,131.456650 410.481018,130.714798
C385.238434,130.714798 359.846375,130.714798 334.226685,130.714798
C334.226685,118.510406 334.226685,106.749191 334.226685,94.734009
C372.851379,94.734009 411.417328,94.734009 450.518524,94.734009
C450.518524,96.585030 450.518524,98.201042 450.518524,99.817055
C450.518280,173.292236 450.483337,246.767456 450.544250,320.242584
C450.558044,336.881256 448.760773,353.188080 441.814331,368.480042
C431.804352,390.516083 415.206604,405.886169 392.339081,413.506226
C362.237671,423.536865 331.966187,423.304871 302.717468,410.285675
C275.974670,398.381897 260.761566,376.813599 255.100037,348.441040
C253.489624,340.370422 253.064514,332.063293 252.056076,323.528076
C265.968353,323.528076 278.871552,323.528076 291.074066,323.528076
C292.780731,332.325165 293.632660,341.024353 296.220825,349.172028
C301.829712,366.828979 314.212372,377.917847 332.139099,382.561066
C346.144257,386.188507 360.300110,386.144745 374.031616,381.696930
C395.828674,374.636627 407.066986,358.591736 409.875427,336.416199
C410.875092,328.522644 410.656464,320.474792 410.993958,311.997833
z"/>
<path fill="#FDFDFD" opacity="1.000000" stroke="none"
d="
M88.082108,348.899780
C96.337631,346.484802 104.306267,346.337982 112.140968,348.535126
C126.024956,352.428772 133.839737,361.946350 136.584061,375.933624
C139.973038,393.206451 133.284363,408.960266 119.434303,416.107788
C99.387474,426.453217 73.876472,418.160767 66.682755,398.797058
C60.637661,382.525238 64.397789,356.446655 88.082108,348.899780
z"/>
</svg>
);
}
+56
View File
@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
+79
View File
@@ -0,0 +1,79 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+262
View File
@@ -0,0 +1,262 @@
"use client"
import * as React from "react"
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react"
import { ArrowLeft, ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = {
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
function useCarousel() {
const context = React.useContext(CarouselContext)
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />")
}
return context
}
const Carousel = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(
(
{
orientation = "horizontal",
opts,
setApi,
plugins,
className,
children,
...props
},
ref
) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
return
}
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
const scrollPrev = React.useCallback(() => {
api?.scrollPrev()
}, [api])
const scrollNext = React.useCallback(() => {
api?.scrollNext()
}, [api])
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault()
scrollPrev()
} else if (event.key === "ArrowRight") {
event.preventDefault()
scrollNext()
}
},
[scrollPrev, scrollNext]
)
React.useEffect(() => {
if (!api || !setApi) {
return
}
setApi(api)
}, [api, setApi])
React.useEffect(() => {
if (!api) {
return
}
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
return () => {
api?.off("select", onSelect)
}
}, [api, onSelect])
return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
ref={ref}
onKeyDownCapture={handleKeyDown}
className={cn("relative", className)}
role="region"
aria-roledescription="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
)
}
)
Carousel.displayName = "Carousel"
const CarouselContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel()
return (
<div ref={carouselRef} className="overflow-hidden">
<div
ref={ref}
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className
)}
{...props}
/>
</div>
)
})
CarouselContent.displayName = "CarouselContent"
const CarouselItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { orientation } = useCarousel()
return (
<div
ref={ref}
role="group"
aria-roledescription="slide"
className={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
className
)}
{...props}
/>
)
})
CarouselItem.displayName = "CarouselItem"
const CarouselPrevious = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Previous slide</span>
</Button>
)
})
CarouselPrevious.displayName = "CarouselPrevious"
const CarouselNext = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">Next slide</span>
</Button>
)
})
CarouselNext.displayName = "CarouselNext"
export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
}
+122
View File
@@ -0,0 +1,122 @@
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}