122 lines
6.0 KiB
TypeScript
122 lines
6.0 KiB
TypeScript
import React, {ReactPortal, useEffect, useState} from 'react';
|
|
import {createPortal} from 'react-dom';
|
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|
import {faPaperPlane, faStop, faSync, faX} from '@fortawesome/free-solid-svg-icons';
|
|
import {useTranslations} from "next-intl";
|
|
|
|
interface QSTextGeneratedPreviewProps {
|
|
onClose: () => void;
|
|
onRefresh: () => void;
|
|
value: string;
|
|
onInsert: () => void;
|
|
isGenerating?: boolean;
|
|
onStop?: () => void;
|
|
}
|
|
|
|
export default function QSTextGeneratedPreview(
|
|
{
|
|
onClose,
|
|
onRefresh,
|
|
value,
|
|
onInsert,
|
|
isGenerating = false,
|
|
onStop,
|
|
}: QSTextGeneratedPreviewProps): ReactPortal | null {
|
|
|
|
const [mounted, setMounted] = useState(false);
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
const t = useTranslations();
|
|
|
|
useEffect((): () => void => {
|
|
setMounted(true);
|
|
const timer = setTimeout(() => setIsVisible(true), 10);
|
|
return (): void => {
|
|
setMounted(false);
|
|
setIsVisible(false);
|
|
clearTimeout(timer);
|
|
};
|
|
}, []);
|
|
|
|
const handleClose = (): void => {
|
|
setIsVisible(false);
|
|
setTimeout(onClose, 300); // Attend la fin de l'animation avant de fermer
|
|
};
|
|
|
|
if (!mounted) return null;
|
|
|
|
const modalContent = (
|
|
<div
|
|
className={`fixed inset-0 z-50 flex items-center justify-center font-['Lora'] transition-opacity duration-300 ${isVisible ? 'opacity-100' : 'opacity-0'}`}>
|
|
<div className="absolute inset-0 bg-overlay" onClick={handleClose}></div>
|
|
<div
|
|
className={`relative w-[90%] max-w-2xl h-[80%] bg-tertiary/90 backdrop-blur-sm rounded-2xl overflow-hidden border border-secondary/50 shadow-2xl flex flex-col transition-all duration-300 ${isVisible ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`}>
|
|
<div
|
|
className="flex justify-between items-center px-5 py-4 bg-secondary/30 backdrop-blur-sm border-b border-secondary/50 shadow-sm">
|
|
<div className="flex items-center">
|
|
<h2 className="text-xl font-['ADLaM_Display'] text-text-primary">{t("qsTextPreview.title")}</h2>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
{isGenerating && onStop ? (
|
|
<button
|
|
onClick={onStop}
|
|
className="w-9 h-9 rounded-xl bg-red-500 text-white hover:bg-red-600 transition-all duration-200 hover:scale-110 flex justify-center items-center shadow-sm hover:shadow-md"
|
|
>
|
|
<FontAwesomeIcon icon={faStop}/>
|
|
</button>
|
|
) : (
|
|
<button
|
|
onClick={onRefresh}
|
|
className="w-9 h-9 rounded-xl bg-secondary/50 text-primary hover:bg-secondary transition-all duration-200 hover:scale-110 flex justify-center items-center shadow-sm hover:shadow-md border border-secondary/50"
|
|
>
|
|
<FontAwesomeIcon icon={faSync}/>
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={handleClose}
|
|
className="text-muted hover:text-text-primary p-2 rounded-xl hover:bg-secondary transition-all duration-200 hover:scale-110"
|
|
>
|
|
<FontAwesomeIcon icon={faX} className={'h-5 w-5'}/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="flex-1 p-5 overflow-auto custom-scrollbar">
|
|
<div
|
|
className="w-full bg-darkest-background text-text-primary p-5 rounded-xl border border-secondary/50 shadow-inner">
|
|
{isGenerating && !value ? (
|
|
<div className="space-y-4 animate-pulse">
|
|
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-11/12"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-10/12"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-9/12"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-full"></div>
|
|
<div className="h-4 bg-secondary/30 rounded w-11/12"></div>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
<div className="text-justify leading-relaxed whitespace-pre-wrap fade-in-text">
|
|
{value}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div
|
|
className="px-5 py-4 bg-secondary/30 backdrop-blur-sm border-t border-secondary/50 flex justify-end shadow-inner">
|
|
<button
|
|
onClick={onInsert}
|
|
className="flex items-center py-2.5 px-5 rounded-xl bg-primary text-text-primary hover:bg-primary-dark transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg font-medium"
|
|
>
|
|
<FontAwesomeIcon icon={faPaperPlane} className="mr-2"/>
|
|
{t("qsTextPreview.insert")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return createPortal(modalContent, document.body);
|
|
}
|