fix and update

This commit is contained in:
lendry
2026-07-01 22:46:04 +03:00
parent cb82544905
commit 2b88e028c6
7 changed files with 385 additions and 79 deletions

View File

@@ -1,7 +1,10 @@
'use client';
import * as React from 'react';
import { createPortal } from 'react-dom';
import { ChevronLeft, ChevronRight, X, ZoomIn } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { MediaImageSkeleton } from '@/components/ui/media-image-skeleton';
import { cn } from '@/lib/utils';
interface DocumentFullscreenViewerProps {
@@ -13,6 +16,11 @@ interface DocumentFullscreenViewerProps {
export function DocumentFullscreenViewer({ images, initialIndex = 0, open, onOpenChange }: DocumentFullscreenViewerProps) {
const [index, setIndex] = React.useState(initialIndex);
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
React.useEffect(() => {
if (open) setIndex(initialIndex);
@@ -29,50 +37,101 @@ export function DocumentFullscreenViewer({ images, initialIndex = 0, open, onOpe
return () => window.removeEventListener('keydown', onKeyDown);
}, [images.length, onOpenChange, open]);
if (!open || images.length === 0) return null;
React.useEffect(() => {
if (!open) return;
const previous = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = previous;
};
}, [open]);
if (!open || images.length === 0 || !mounted) return null;
const current = images[index];
return (
<div className="fixed inset-0 z-[300] flex flex-col bg-black/95">
return createPortal(
<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">
<span className="text-sm text-white/80">
{index + 1} / {images.length}
</span>
<button type="button" onClick={() => onOpenChange(false)} className="rounded-full p-2 hover:bg-white/10" aria-label="Закрыть">
<div className="min-w-0">
<p className="truncate text-sm font-medium">Фото документа</p>
<p className="text-xs text-white/60">
{index + 1} из {images.length}
</p>
</div>
<Button
type="button"
size="icon"
variant="ghost"
className="text-white hover:bg-white/10"
aria-label="Закрыть"
onClick={() => onOpenChange(false)}
>
<X className="h-5 w-5" />
</button>
</Button>
</div>
<div className="relative flex flex-1 items-center justify-center px-4 pb-6">
<div className="relative flex flex-1 items-center justify-center px-3 pb-4">
{images.length > 1 ? (
<button
<Button
type="button"
disabled={index === 0}
onClick={() => setIndex((value) => Math.max(0, value - 1))}
className="absolute left-4 z-10 rounded-full bg-white/10 p-2 text-white disabled:opacity-30"
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={() => setIndex((value) => Math.max(0, value - 1))}
>
<ChevronLeft className="h-6 w-6" />
</button>
</Button>
) : null}
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={current.url} alt="Фото документа" className="max-h-[calc(100vh-120px)] max-w-full object-contain" />
<img
src={current.url}
alt="Фото документа"
className="max-h-[calc(100vh-140px)] max-w-full object-contain"
/>
{images.length > 1 ? (
<button
<Button
type="button"
disabled={index === images.length - 1}
onClick={() => setIndex((value) => Math.min(images.length - 1, value + 1))}
className="absolute right-4 z-10 rounded-full bg-white/10 p-2 text-white disabled:opacity-30"
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={() => setIndex((value) => Math.min(images.length - 1, value + 1))}
>
<ChevronRight className="h-6 w-6" />
</button>
</Button>
) : null}
</div>
</div>
{images.length > 1 ? (
<div className="flex gap-2 overflow-x-auto px-4 pb-4">
{images.map((image, imageIndex) => (
<button
key={image.storageKey}
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={() => setIndex(imageIndex)}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={image.url} alt="" className="h-full w-full object-cover" />
</button>
))}
</div>
) : null}
</div>,
document.body
);
}
@@ -93,7 +152,7 @@ export function DocumentPhotoThumbnail({ url, loading, className, onClick }: Doc
className
)}
>
{loading ? <div className="h-full w-full animate-pulse bg-[#eceef4]" /> : null}
{loading ? <MediaImageSkeleton className="h-full w-full min-h-0" rounded="none" /> : null}
{!loading && url ? (
<>
{/* eslint-disable-next-line @next/next/no-img-element */}