| import React, { useEffect } from 'react'; |
| import { Button } from '@/components/ui/button'; |
| import { cn } from '@/lib/utils'; |
|
|
| |
| |
| |
| export default function SlideOverPanel({ |
| open, |
| onClose, |
| title, |
| subtitle, |
| headerActions = null, |
| children, |
| widthClassName = 'max-w-lg', |
| }) { |
| useEffect(() => { |
| if (!open) return; |
| const onKey = (e) => { |
| if (e.key === 'Escape') onClose(); |
| }; |
| window.addEventListener('keydown', onKey); |
| return () => window.removeEventListener('keydown', onKey); |
| }, [open, onClose]); |
|
|
| if (!open) return null; |
|
|
| return ( |
| <div className="fixed inset-0 z-50 flex justify-end bg-black/30" role="dialog" aria-modal="true"> |
| <button |
| type="button" |
| className="flex-1 cursor-default min-h-0" |
| aria-label="Close panel" |
| onClick={onClose} |
| /> |
| <div |
| className={cn( |
| 'w-full bg-white shadow-xl h-full overflow-y-auto border-l border-slate-200 p-6', |
| widthClassName |
| )} |
| > |
| <div className="flex justify-between items-start gap-4 mb-6"> |
| <div className="min-w-0 pr-2 flex-1"> |
| {typeof title === 'string' || title == null ? ( |
| <h3 className="text-lg font-bold text-slate-900 truncate">{title || ''}</h3> |
| ) : ( |
| <div className="w-full min-w-0">{title}</div> |
| )} |
| {subtitle ? ( |
| <p className="text-sm text-slate-500 mt-1 break-words">{subtitle}</p> |
| ) : null} |
| </div> |
| <div className="flex items-center gap-2 shrink-0"> |
| {headerActions} |
| <Button variant="ghost" size="sm" onClick={onClose}> |
| Close |
| </Button> |
| </div> |
| </div> |
| {children} |
| </div> |
| </div> |
| ); |
| } |
|
|