dstpabuaran.com/resources/js/components/image-preview-modal.tsx
Yoga Pangestu 10db7bc5a2 Refactor product variant photo handling to support multiple images
- Updated ProductVariantService to handle multiple photo keys and URLs.
- Introduced FileUploadMultiple component for uploading multiple images.
- Modified product creation and editing forms to accommodate multiple photos.
- Adjusted data structures in product drafts and tests to reflect changes in photo handling.
- Enhanced image preview modal to navigate through multiple images.
2026-08-01 15:49:38 +07:00

99 lines
3.2 KiB
TypeScript

import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
type ImagePreviewModalProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
src: string | null;
title?: string;
alt?: string;
sources?: string[];
};
export function ImagePreviewModal({
open,
onOpenChange,
src,
title,
alt = 'Preview',
sources,
}: ImagePreviewModalProps) {
const allImages = sources && sources.length > 0 ? sources : src ? [src] : [];
const [currentIndex, setCurrentIndex] = useState(0);
const currentSrc = allImages[currentIndex] ?? src;
const hasMultiple = allImages.length > 1;
function handlePrev() {
setCurrentIndex((prev) =>
prev === 0 ? allImages.length - 1 : prev - 1,
);
}
function handleNext() {
setCurrentIndex((prev) =>
prev === allImages.length - 1 ? 0 : prev + 1,
);
}
return (
<Dialog
open={open}
onOpenChange={(v) => {
if (!v) {
setCurrentIndex(0);
}
onOpenChange(v);
}}
>
<DialogContent showCloseButton>
{title && (
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
)}
{currentSrc && (
<div className="relative">
<img
src={currentSrc}
alt={alt}
className="max-h-[80vh] w-full rounded-lg object-contain"
/>
{hasMultiple && (
<>
<Button
variant="secondary"
size="icon"
className="absolute left-2 top-1/2 h-8 w-8 -translate-y-1/2 rounded-full bg-black/50 text-white hover:bg-black/70"
onClick={handlePrev}
>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="secondary"
size="icon"
className="absolute right-2 top-1/2 h-8 w-8 -translate-y-1/2 rounded-full bg-black/50 text-white hover:bg-black/70"
onClick={handleNext}
>
<ChevronRight className="h-4 w-4" />
</Button>
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-2 py-1 text-xs text-white">
{currentIndex + 1} / {allImages.length}
</div>
</>
)}
</div>
)}
</DialogContent>
</Dialog>
);
}