vitess.io/vitess@v0.16.2/web/vtadmin/src/components/routes/tablets/InfoDialog.tsx (about) 1 import React, { useEffect, useState } from 'react'; 2 import { Transition } from '@headlessui/react'; 3 import Dialog from '../../dialog/Dialog'; 4 import { Icon, Icons } from '../../Icon'; 5 import { UseQueryResult } from 'react-query'; 6 7 export interface BaseInfoDialogProps { 8 loadingTitle?: string; 9 loadingDescription: string; 10 successTitle?: string; 11 successDescription: string; 12 errorTitle?: string; 13 errorDescription: string; 14 useHook: () => UseQueryResult<any, Error>; 15 } 16 17 interface InfoDialogProps extends BaseInfoDialogProps { 18 isOpen: boolean; 19 onClose: () => void; 20 } 21 22 const InfoDialog: React.FC<InfoDialogProps> = ({ 23 loadingTitle, 24 loadingDescription, 25 successTitle, 26 successDescription, 27 errorTitle, 28 errorDescription, 29 useHook, 30 isOpen, 31 onClose, 32 }) => { 33 const { data, error, isLoading, refetch } = useHook(); 34 35 // Animate loading briefly in case useHook is very fast 36 // to give UX sense of work being done 37 const [animationDone, setAnimationDone] = useState(false); 38 useEffect(() => { 39 let timeout: NodeJS.Timeout; 40 if (isOpen) { 41 timeout = setTimeout(() => { 42 refetch(); 43 setAnimationDone(true); 44 }, 300); 45 } 46 return () => timeout && clearTimeout(timeout); 47 // eslint-disable-next-line react-hooks/exhaustive-deps 48 }, [isOpen]); 49 50 const loading = !animationDone || isLoading; 51 52 const SuccessState: React.FC = () => ( 53 <div className="w-full flex flex-col justify-center items-center"> 54 <span className="flex h-12 w-12 relative items-center justify-center"> 55 <Icon className="fill-current text-green-500" icon={Icons.checkSuccess} /> 56 </span> 57 <div className="text-lg mt-3 font-bold">{successTitle || 'Success!'}</div> 58 <div className="text-sm">{successDescription}</div> 59 </div> 60 ); 61 62 const FailState: React.FC = () => ( 63 <div className="w-full flex flex-col justify-center items-center"> 64 <span className="flex h-12 w-12 relative items-center justify-center"> 65 <Icon className="fill-current text-red-500" icon={Icons.alertFail} /> 66 </span> 67 <div className="text-lg mt-3 font-bold">{errorTitle || 'Error'}</div> 68 <div className="text-sm"> 69 {errorDescription}: {error?.message} 70 </div> 71 </div> 72 ); 73 74 return ( 75 <Dialog 76 isOpen={isOpen} 77 onClose={() => { 78 setAnimationDone(false); 79 onClose(); 80 }} 81 hideCancel={true} 82 confirmText="Done" 83 > 84 <div> 85 <div className="flex justify-center items-center w-full h-40"> 86 {loading && ( 87 <Transition 88 className="absolute" 89 show={loading && isOpen} 90 leave="transition-opacity duration-100" 91 enter="transition-opacity duration-75" 92 enterFrom="opacity-0" 93 enterTo="opacity-100" 94 leaveFrom="opacity-100" 95 leaveTo="opacity-0" 96 > 97 <div className="w-full flex flex-col justify-center items-center"> 98 <span className="flex h-6 w-6 relative items-center justify-center"> 99 <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-yellow-400 opacity-75"></span> 100 <span className="relative inline-flex rounded-full h-6 w-6 bg-yellow-500"></span> 101 </span> 102 <div className="text-lg mt-6 font-bold">{loadingTitle || 'Loading...'}</div> 103 <div className="text-sm">{loadingDescription}</div> 104 </div> 105 </Transition> 106 )} 107 {!loading && ( 108 <Transition 109 className="absolute" 110 show={!loading && isOpen} 111 enter="delay-100 transition-opacity duration-75" 112 enterFrom="opacity-0" 113 enterTo="opacity-100" 114 > 115 {data && <SuccessState />} 116 {error && <FailState />} 117 </Transition> 118 )} 119 </div> 120 </div> 121 </Dialog> 122 ); 123 }; 124 125 export default InfoDialog;