Files
IdP/apps/frontend/components/chat/chat-image-lightbox.tsx
2026-06-29 12:17:25 +03:00

149 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect } from 'react';
import { ChevronLeft, ChevronRight, Download, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import type { ChatMessage } from '@/lib/api';
import { cn } from '@/lib/utils';
interface ChatImageLightboxProps {
open: boolean;
images: ChatMessage[];
index: number;
mediaUrls: Record<string, { blobUrl?: string; fileName?: string } | undefined>;
onClose: () => void;
onIndexChange: (index: number) => void;
onDownload?: (message: ChatMessage) => void;
}
export function ChatImageLightbox({
open,
images,
index,
mediaUrls,
onClose,
onIndexChange,
onDownload
}: ChatImageLightboxProps) {
const current = images[index];
useEffect(() => {
if (!open) return;
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') onClose();
if (event.key === 'ArrowLeft') onIndexChange(Math.max(0, index - 1));
if (event.key === 'ArrowRight') onIndexChange(Math.min(images.length - 1, index + 1));
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [open, index, images.length, onClose, onIndexChange]);
if (!open || !current?.storageKey) return null;
const media = mediaUrls[current.storageKey];
return (
<div className="fixed inset-0 z-[250] flex flex-col bg-[#0f1117]/96">
<div className="flex items-center justify-between px-4 py-3 text-white">
<div className="min-w-0">
<p className="truncate text-sm font-medium">{current.senderName}</p>
<p className="text-xs text-white/60">
{index + 1} из {images.length}
</p>
</div>
<div className="flex items-center gap-1">
{onDownload ? (
<Button
type="button"
size="icon"
variant="ghost"
className="text-white hover:bg-white/10"
aria-label="Скачать"
onClick={() => onDownload(current)}
>
<Download className="h-5 w-5" />
</Button>
) : null}
<Button
type="button"
size="icon"
variant="ghost"
className="text-white hover:bg-white/10"
aria-label="Закрыть"
onClick={onClose}
>
<X className="h-5 w-5" />
</Button>
</div>
</div>
<div className="relative flex flex-1 items-center justify-center px-3 pb-4">
<Button
type="button"
size="icon"
variant="ghost"
className={cn(
'absolute left-3 z-10 h-11 w-11 rounded-full text-white hover:bg-white/10',
index <= 0 && 'pointer-events-none opacity-30'
)}
aria-label="Предыдущее"
onClick={() => onIndexChange(Math.max(0, index - 1))}
>
<ChevronLeft className="h-6 w-6" />
</Button>
<div className="flex max-h-full max-w-full items-center justify-center">
{media?.blobUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={media.blobUrl} alt="" className="max-h-[calc(100vh-140px)] max-w-full object-contain" />
) : (
<div className="text-sm text-white/70">Загрузка изображения...</div>
)}
</div>
<Button
type="button"
size="icon"
variant="ghost"
className={cn(
'absolute right-3 z-10 h-11 w-11 rounded-full text-white hover:bg-white/10',
index >= images.length - 1 && 'pointer-events-none opacity-30'
)}
aria-label="Следующее"
onClick={() => onIndexChange(Math.min(images.length - 1, index + 1))}
>
<ChevronRight className="h-6 w-6" />
</Button>
</div>
{current.content ? (
<div className="border-t border-white/10 px-4 py-3 text-sm text-white/85">{current.content}</div>
) : null}
{images.length > 1 ? (
<div className="flex gap-2 overflow-x-auto px-4 pb-4">
{images.map((image, imageIndex) => {
const thumb = image.storageKey ? mediaUrls[image.storageKey] : undefined;
return (
<button
key={image.id}
type="button"
className={cn(
'h-14 w-14 shrink-0 overflow-hidden rounded-lg border-2',
imageIndex === index ? 'border-[#3390ec]' : 'border-transparent opacity-70'
)}
onClick={() => onIndexChange(imageIndex)}
>
{thumb?.blobUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={thumb.blobUrl} alt="" className="h-full w-full object-cover" />
) : null}
</button>
);
})}
</div>
) : null}
</div>
);
}