Some checks failed
Build Frontend / build (push) Failing after 7s
Replaces imports from 'components/ui' with 'components/common' across the app and dashboard pages, and updates model and API imports to use new paths under 'lib'. Removes redundant authentication checks from several dashboard pages. Adds new dashboard components and utility files, and reorganizes hooks and services into the 'lib' directory for improved structure.
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { Dialog, DialogContent } from "@/components/common/dialog";
|
|
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
import { Button } from "@/components/common/button";
|
|
import { useEffect } from "react";
|
|
|
|
interface ImageViewerModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
imageUrl: string;
|
|
onNavigate?: (direction: 'prev' | 'next') => void;
|
|
}
|
|
|
|
export function ImageViewerModal({ isOpen, onClose, imageUrl, onNavigate }: ImageViewerModalProps) {
|
|
const handleNavigation = (direction: 'prev' | 'next') => (e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
onNavigate?.(direction);
|
|
};
|
|
|
|
// Add keyboard navigation
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (!onNavigate) return;
|
|
|
|
if (e.key === 'ArrowLeft') {
|
|
e.preventDefault();
|
|
onNavigate('prev');
|
|
} else if (e.key === 'ArrowRight') {
|
|
e.preventDefault();
|
|
onNavigate('next');
|
|
} else if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
if (isOpen) {
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
}
|
|
|
|
return () => {
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
};
|
|
}, [isOpen, onNavigate, onClose]);
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
<DialogContent className="p-0 border-none bg-black/90 max-w-[90vw] max-h-[90vh] overflow-hidden [&>button:not([id='custom-close-btn'])]:hidden">
|
|
{/* Custom close button */}
|
|
<Button
|
|
id="custom-close-btn"
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={onClose}
|
|
className="absolute top-4 right-4 z-50 bg-black/40 hover:bg-black/60 text-white rounded-full border border-white/20"
|
|
>
|
|
<X size={18} />
|
|
</Button>
|
|
|
|
<div className="relative w-[90vw] h-[90vh] mx-auto">
|
|
{/* Navigation buttons */}
|
|
{onNavigate && (
|
|
<>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="absolute left-4 top-1/2 -translate-y-1/2 z-50 bg-black/40 hover:bg-black/60 text-white rounded-full w-10 h-10 border border-white/20"
|
|
onClick={handleNavigation('prev')}
|
|
>
|
|
<ChevronLeft size={24} />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 z-50 bg-black/40 hover:bg-black/60 text-white rounded-full w-10 h-10 border border-white/20"
|
|
onClick={handleNavigation('next')}
|
|
>
|
|
<ChevronRight size={24} />
|
|
</Button>
|
|
</>
|
|
)}
|
|
|
|
{/* Image container - add onClick handler to close modal */}
|
|
<div
|
|
className="w-full h-full flex items-center justify-center p-4 cursor-pointer"
|
|
onClick={onClose}
|
|
>
|
|
<img
|
|
src={imageUrl}
|
|
alt="Full size image"
|
|
className="max-w-full max-h-full object-contain cursor-pointer"
|
|
loading="eager"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|