vitess.io/vitess@v0.16.2/web/vtadmin/src/components/dialog/Dialog.tsx (about) 1 import { Fragment, useRef } from 'react'; 2 import { Dialog as HUDialog, Transition } from '@headlessui/react'; 3 import { Icon, Icons } from '../Icon'; 4 5 interface DialogProps { 6 icon?: Icons; 7 isOpen: boolean; 8 title?: string; 9 description?: string; 10 content?: React.ReactElement; 11 onCancel?: () => void; 12 onConfirm?: Function; 13 onClose?: () => void; 14 loading?: boolean; 15 loadingText?: string; 16 confirmText?: string; 17 cancelText?: string; 18 footer?: React.ReactElement; 19 hideFooter?: boolean; 20 children?: React.ReactElement; 21 hideConfirm?: boolean; 22 hideCancel?: boolean; 23 className?: string; 24 } 25 26 /** 27 * Dialog is a controlled component, and its open state is controlled by the isOpen property. 28 * User should pass in a function to set isOpen to the onClose property. 29 */ 30 const Dialog: React.FC<DialogProps> = ({ 31 icon, 32 title, 33 description, 34 children, 35 isOpen, 36 loading, 37 loadingText, 38 confirmText, 39 cancelText, 40 footer, 41 hideFooter, 42 hideCancel, 43 hideConfirm, 44 onCancel, 45 onConfirm, 46 onClose, 47 className, 48 }) => { 49 const cancelButtonRef = useRef(null); 50 51 return ( 52 <Transition.Root show={isOpen} as={Fragment}> 53 <HUDialog 54 as="div" 55 className="fixed z-10 inset-0 overflow-y-auto" 56 initialFocus={cancelButtonRef} 57 onClose={(_) => { 58 onClose?.(); 59 }} 60 > 61 <div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 62 <Transition.Child 63 as={Fragment} 64 enter="ease-out duration-300" 65 enterFrom="opacity-0" 66 enterTo="opacity-100" 67 leave="ease-in duration-200" 68 leaveFrom="opacity-100" 69 leaveTo="opacity-0" 70 > 71 <HUDialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> 72 </Transition.Child> 73 74 {/* This element is to trick the browser into centering the modal contents. */} 75 <span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> 76 ​ 77 </span> 78 <Transition.Child 79 as={Fragment} 80 enter="ease-out duration-300" 81 enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 82 enterTo="opacity-100 translate-y-0 sm:scale-100" 83 leave="ease-in duration-200" 84 leaveFrom="opacity-100 translate-y-0 sm:scale-100" 85 leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 86 > 87 <div 88 className={`inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full ${className}`} 89 > 90 <div className="bg-white px-4 py-5 sm:py-8 sm:px-6 w-full"> 91 <div className="sm:flex sm:items-start"> 92 {icon && ( 93 <div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10"> 94 <Icon icon={icon} /> 95 </div> 96 )} 97 <div className="text-center sm:mt-0 sm:text-left w-full"> 98 {title && ( 99 <HUDialog.Title 100 as="h2" 101 className="text-xl leading-6 font-medium text-primary" 102 > 103 {title} 104 </HUDialog.Title> 105 )} 106 {description && ( 107 <div className="mt-2"> 108 <p className="text-sm text-secondary">{description}</p> 109 </div> 110 )} 111 {Boolean(children) && children} 112 </div> 113 </div> 114 </div> 115 {!footer && !hideFooter && ( 116 <div className="px-4 py-3 flex gap-2 sm:px-6 sm:flex-row-reverse"> 117 {!hideConfirm && ( 118 <button 119 disabled={loading} 120 type="button" 121 className="btn" 122 onClick={() => { 123 onConfirm?.(); 124 }} 125 > 126 {loading ? loadingText : confirmText || 'Confirm'} 127 </button> 128 )} 129 {!hideCancel && ( 130 <button 131 type="button" 132 className="btn btn-secondary" 133 onClick={() => { 134 onCancel?.(); 135 onClose?.(); 136 }} 137 ref={cancelButtonRef} 138 > 139 {cancelText || 'Cancel'} 140 </button> 141 )} 142 </div> 143 )} 144 {Boolean(footer) && footer} 145 </div> 146 </Transition.Child> 147 </div> 148 </HUDialog> 149 </Transition.Root> 150 ); 151 }; 152 153 export default Dialog;