import { useState, useEffect, useCallback, useRef } from 'react'; import { Maximize2 } from 'lucide-react'; import { FileSources } from 'librechat-data-provider'; import { OGDialog, OGDialogContent } from '@librechat/client'; import ProgressCircle from './ProgressCircle'; import SourceIcon from './SourceIcon'; import { cn } from '~/utils'; type styleProps = { backgroundImage?: string; backgroundSize?: string; backgroundPosition?: string; backgroundRepeat?: string; }; const ImagePreview = ({ imageBase64, url, progress = 1, className = '', source, alt = 'Preview image', }: { imageBase64?: string; url?: string; progress?: number; className?: string; source?: FileSources; alt?: string; }) => { const [isModalOpen, setIsModalOpen] = useState(false); const [isHovered, setIsHovered] = useState(false); const triggerRef = useRef(null); const openModal = useCallback(() => { setIsModalOpen(true); }, []); const handleOpenChange = useCallback((open: boolean) => { setIsModalOpen(open); if (!open && triggerRef.current) { requestAnimationFrame(() => { triggerRef.current?.focus({ preventScroll: true }); }); } }, []); useEffect(() => { if (isModalOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = 'unset'; } return () => { document.body.style.overflow = 'unset'; }; }, [isModalOpen]); const baseStyle: styleProps = { backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', }; const imageUrl = imageBase64 ?? url ?? ''; const style: styleProps = imageUrl ? { ...baseStyle, backgroundImage: `url(${imageUrl})`, } : baseStyle; if (typeof style.backgroundImage !== 'string' || style.backgroundImage.length === 0) { return null; } const radius = 55; const circumference = 2 * Math.PI * radius; const offset = circumference - progress * circumference; const circleCSSProperties = { transition: 'stroke-dashoffset 0.3s linear', }; return ( <>
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} >
{alt} ); }; export default ImagePreview;