Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/resources/scripts/components/elements/dialog/Dialog.tsx
10260 views
1
import React, { useRef, useState } from 'react';
2
import { Dialog as HDialog } from '@headlessui/react';
3
import { Button } from '@/components/elements/button/index';
4
import { XIcon } from '@heroicons/react/solid';
5
import { AnimatePresence, motion } from 'framer-motion';
6
import { DialogContext, IconPosition, RenderDialogProps, styles } from './';
7
8
const variants = {
9
open: {
10
scale: 1,
11
opacity: 1,
12
transition: {
13
type: 'spring',
14
damping: 15,
15
stiffness: 300,
16
duration: 0.15,
17
},
18
},
19
closed: {
20
scale: 0.75,
21
opacity: 0,
22
transition: {
23
type: 'easeIn',
24
duration: 0.15,
25
},
26
},
27
bounce: {
28
scale: 0.95,
29
opacity: 1,
30
transition: { type: 'linear', duration: 0.075 },
31
},
32
};
33
34
export default ({
35
open,
36
title,
37
description,
38
onClose,
39
hideCloseIcon,
40
preventExternalClose,
41
children,
42
}: RenderDialogProps) => {
43
const container = useRef<HTMLDivElement>(null);
44
const [icon, setIcon] = useState<React.ReactNode>();
45
const [footer, setFooter] = useState<React.ReactNode>();
46
const [iconPosition, setIconPosition] = useState<IconPosition>('title');
47
const [down, setDown] = useState(false);
48
49
const onContainerClick = (down: boolean, e: React.MouseEvent<HTMLDivElement>): void => {
50
if (e.target instanceof HTMLElement && container.current?.isSameNode(e.target)) {
51
setDown(down);
52
}
53
};
54
55
const onDialogClose = (): void => {
56
if (!preventExternalClose) {
57
return onClose();
58
}
59
};
60
61
return (
62
<AnimatePresence>
63
{open && (
64
<DialogContext.Provider value={{ setIcon, setFooter, setIconPosition }}>
65
<HDialog
66
static
67
as={motion.div}
68
initial={{ opacity: 0 }}
69
animate={{ opacity: 1 }}
70
exit={{ opacity: 0 }}
71
transition={{ duration: 0.15 }}
72
open={open}
73
onClose={onDialogClose}
74
>
75
<div className={'fixed inset-0 bg-gray-900/50 z-40'} />
76
<div className={'fixed inset-0 overflow-y-auto z-50'}>
77
<div
78
ref={container}
79
className={styles.container}
80
onMouseDown={onContainerClick.bind(this, true)}
81
onMouseUp={onContainerClick.bind(this, false)}
82
>
83
<HDialog.Panel
84
as={motion.div}
85
initial={'closed'}
86
animate={down ? 'bounce' : 'open'}
87
exit={'closed'}
88
variants={variants}
89
className={styles.panel}
90
>
91
<div className={'flex p-6 pb-0 overflow-y-auto'}>
92
{iconPosition === 'container' && icon}
93
<div className={'flex-1 max-h-[70vh] min-w-0'}>
94
<div className={'flex items-center'}>
95
{iconPosition !== 'container' && icon}
96
<div>
97
{title && (
98
<HDialog.Title className={styles.title}>{title}</HDialog.Title>
99
)}
100
{description && (
101
<HDialog.Description>{description}</HDialog.Description>
102
)}
103
</div>
104
</div>
105
{children}
106
<div className={'invisible h-6'} />
107
</div>
108
</div>
109
{footer}
110
{/* Keep this below the other buttons so that it isn't the default focus if they're present. */}
111
{!hideCloseIcon && (
112
<div className={'absolute right-0 top-0 m-4'}>
113
<Button.Text
114
size={Button.Sizes.Small}
115
shape={Button.Shapes.IconSquare}
116
onClick={onClose}
117
className={'group'}
118
>
119
<XIcon className={styles.close_icon} />
120
</Button.Text>
121
</div>
122
)}
123
</HDialog.Panel>
124
</div>
125
</div>
126
</HDialog>
127
</DialogContext.Provider>
128
)}
129
</AnimatePresence>
130
);
131
};
132
133