pmtool / src /components /ui /custom-sheet.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
3.13 kB
import React, { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
interface CustomSheetProps {
open: boolean;
onOpenChange: (open: boolean) => void;
children: React.ReactNode;
className?: string;
side?: "right" | "left";
title?: string;
description?: string;
}
export const CustomSheet: React.FC<CustomSheetProps> = ({
open,
onOpenChange,
children,
className,
side = "right",
title,
description
}) => {
const [isRendered, setIsRendered] = useState(false);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (open) {
setIsRendered(true);
// Small delay to allow render before transition
setTimeout(() => setIsVisible(true), 10);
// Prevent body scroll
document.body.style.overflow = 'hidden';
} else {
setIsVisible(false);
// Wait for transition to finish before unmounting
setTimeout(() => setIsRendered(false), 300);
// Restore body scroll
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [open]);
if (!isRendered) return null;
return createPortal(
<div className="fixed inset-0 z-[9999] flex justify-end">
{/* Backdrop */}
<div
className={cn(
"fixed inset-0 bg-black/50 transition-opacity duration-300",
isVisible ? "opacity-100" : "opacity-0"
)}
onClick={() => onOpenChange(false)}
/>
{/* Sheet Content */}
<div
className={cn(
"relative h-full w-full max-w-md bg-background shadow-xl transition-transform duration-300 ease-in-out flex flex-col",
side === "right" ? (isVisible ? "translate-x-0" : "translate-x-full") : (isVisible ? "translate-x-0" : "-translate-x-full"),
className
)}
>
{/* Header */}
<div className="flex items-center justify-between p-4 border-b">
<div>
{title && <h2 className="text-lg font-semibold">{title}</h2>}
{description && <p className="text-sm text-muted-foreground">{description}</p>}
</div>
<Button variant="ghost" size="icon" onClick={() => onOpenChange(false)}>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button>
</div>
{/* Body */}
<div className="flex-1 overflow-y-auto p-4">
{children}
</div>
</div>
</div>,
document.body
);
};