mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
310 lines
9.3 KiB
JavaScript
310 lines
9.3 KiB
JavaScript
/////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin 4 - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
import { Box, Dialog, DialogContent, DialogTitle, makeStyles, Paper } from '@material-ui/core';
|
|
import React, { useState } from 'react';
|
|
import clsx from 'clsx';
|
|
import { getEpoch } from 'sources/utils';
|
|
import { DefaultButton, PgIconButton, PrimaryButton } from '../components/Buttons';
|
|
import Draggable from 'react-draggable';
|
|
import CloseIcon from '@material-ui/icons/CloseRounded';
|
|
import CustomPropTypes from '../custom_prop_types';
|
|
import PropTypes from 'prop-types';
|
|
import gettext from 'sources/gettext';
|
|
import Theme from '../Theme';
|
|
import HTMLReactParser from 'html-react-parser';
|
|
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
|
|
import { Rnd } from 'react-rnd';
|
|
import { ExpandDialogIcon, MinimizeDialogIcon } from '../components/ExternalIcon';
|
|
|
|
const ModalContext = React.createContext({});
|
|
|
|
export function useModal() {
|
|
return React.useContext(ModalContext);
|
|
}
|
|
const useAlertStyles = makeStyles((theme) => ({
|
|
footer: {
|
|
display: 'flex',
|
|
justifyContent: 'flex-end',
|
|
padding: '0.5rem',
|
|
...theme.mixins.panelBorder.top,
|
|
},
|
|
margin: {
|
|
marginLeft: '0.25rem',
|
|
}
|
|
}));
|
|
|
|
function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLabel = gettext('Cancel'), onOkClick, onCancelClick }) {
|
|
const classes = useAlertStyles();
|
|
return (
|
|
<Box display="flex" flexDirection="column" height="100%">
|
|
<Box flexGrow="1" p={2}>{typeof (text) == 'string' ? HTMLReactParser(text) : text}</Box>
|
|
<Box className={classes.footer}>
|
|
{confirm &&
|
|
<DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} >{cancelLabel}</DefaultButton>
|
|
}
|
|
<PrimaryButton className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|
|
AlertContent.propTypes = {
|
|
text: PropTypes.string,
|
|
confirm: PropTypes.bool,
|
|
onOkClick: PropTypes.func,
|
|
onCancelClick: PropTypes.func,
|
|
okLabel: PropTypes.string,
|
|
cancelLabel: PropTypes.string,
|
|
};
|
|
|
|
function alert(title, text, onOkClick, okLabel = gettext('OK')) {
|
|
// bind the modal provider before calling
|
|
this.showModal(title, (closeModal) => {
|
|
const onOkClickClose = () => {
|
|
onOkClick && onOkClick();
|
|
closeModal();
|
|
};
|
|
return (
|
|
<AlertContent text={text} onOkClick={onOkClickClose} okLabel={okLabel} />
|
|
);
|
|
});
|
|
}
|
|
|
|
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No')) {
|
|
// bind the modal provider before calling
|
|
this.showModal(title, (closeModal) => {
|
|
const onCancelClickClose = () => {
|
|
onCancelClick && onCancelClick();
|
|
closeModal();
|
|
};
|
|
const onOkClickClose = () => {
|
|
onOkClick && onOkClick();
|
|
closeModal();
|
|
};
|
|
return (
|
|
<AlertContent text={text} confirm onOkClick={onOkClickClose} onCancelClick={onCancelClickClose} okLabel={okLabel} cancelLabel={cancelLabel} />
|
|
);
|
|
});
|
|
}
|
|
|
|
export default function ModalProvider({ children }) {
|
|
const [modals, setModals] = React.useState([]);
|
|
|
|
const showModal = (title, content, modalOptions) => {
|
|
let id = getEpoch().toString() + Math.random();
|
|
setModals((prev) => [...prev, {
|
|
id: id,
|
|
title: title,
|
|
content: content,
|
|
...modalOptions,
|
|
}]);
|
|
};
|
|
const closeModal = (id) => {
|
|
setModals((prev) => {
|
|
return prev.filter((o) => o.id != id);
|
|
});
|
|
};
|
|
|
|
const fullScreenModal = (fullScreen) => {
|
|
setModals((prev) => [...prev, {
|
|
fullScreen: fullScreen,
|
|
}]);
|
|
};
|
|
|
|
const modalContextBase = {
|
|
showModal: showModal,
|
|
closeModal: closeModal,
|
|
fullScreenModal: fullScreenModal
|
|
};
|
|
const modalContext = React.useMemo(() => ({
|
|
...modalContextBase,
|
|
confirm: confirm.bind(modalContextBase),
|
|
alert: alert.bind(modalContextBase)
|
|
}), []);
|
|
return (
|
|
<ModalContext.Provider value={modalContext}>
|
|
{children}
|
|
{modals.map((modalOptions, i) => (
|
|
<ModalContainer key={i} {...modalOptions} />
|
|
))}
|
|
</ModalContext.Provider>
|
|
);
|
|
}
|
|
|
|
ModalProvider.propTypes = {
|
|
children: CustomPropTypes.children,
|
|
};
|
|
|
|
const dialogStyle = makeStyles((theme) => ({
|
|
dialog: {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
border: '1px solid ' + theme.otherVars.inputBorderColor,
|
|
borderRadius: theme.shape.borderRadius,
|
|
},
|
|
fullScreen: {
|
|
transform: 'none !important'
|
|
}
|
|
}));
|
|
|
|
function PaperComponent(props) {
|
|
let classes = dialogStyle();
|
|
let [dialogPosition, setDialogPosition] = useState(null);
|
|
let resizeable = props.isresizeable == 'true' ? true : false;
|
|
|
|
const setEnableResizing = () => {
|
|
return props.isfullscreen == 'true' ? false : resizeable;
|
|
};
|
|
|
|
const setConditionalPosition = () => {
|
|
return props.isfullscreen == 'true' ? { x: 0, y: 0 } : dialogPosition && { x: dialogPosition.x, y: dialogPosition.y };
|
|
};
|
|
|
|
return (
|
|
props.isresizeable == 'true' ?
|
|
<Rnd
|
|
size={props.isfullscreen == 'true' && { width: '100%', height: '100%' }}
|
|
className={clsx(classes.dialog, props.isfullscreen == 'true' ? classes.fullScreen : '')}
|
|
default={{
|
|
x: 300,
|
|
y: 100,
|
|
...(props.width && { width: props.width }),
|
|
...(props.height && { height: props.height }),
|
|
}}
|
|
{...(props.width && { minWidth: 500 })}
|
|
{...(props.width && { minHeight: 190 })}
|
|
bounds="window"
|
|
enableResizing={setEnableResizing()}
|
|
position={setConditionalPosition()}
|
|
onDragStop={(e, position) => {
|
|
if (props.isfullscreen !== 'true') {
|
|
setDialogPosition({
|
|
...position,
|
|
});
|
|
}
|
|
}}
|
|
onResize={(e, direction, ref, delta, position) => {
|
|
setDialogPosition({
|
|
...position,
|
|
});
|
|
}}
|
|
dragHandleClassName="modal-drag-area"
|
|
>
|
|
<Paper {...props} style={{ width: '100%', height: '100%', maxHeight: '100%', maxWidth: '100%' }} />
|
|
</Rnd>
|
|
:
|
|
<Draggable cancel={'[class*="MuiDialogContent-root"]'}>
|
|
<Paper {...props} style={{ minWidth: '600px' }} />
|
|
</Draggable>
|
|
);
|
|
}
|
|
|
|
PaperComponent.propTypes = {
|
|
isfullscreen: PropTypes.string,
|
|
isresizeable: PropTypes.string,
|
|
width: PropTypes.number,
|
|
height: PropTypes.number,
|
|
};
|
|
|
|
export const useModalStyles = makeStyles((theme) => ({
|
|
container: {
|
|
backgroundColor: theme.palette.background.default
|
|
},
|
|
|
|
titleBar: {
|
|
display: 'flex',
|
|
flexGrow: 1
|
|
},
|
|
title: {
|
|
flexGrow: 1
|
|
},
|
|
icon: {
|
|
fill: 'currentColor',
|
|
width: '1em',
|
|
height: '1em',
|
|
display: 'inline-block',
|
|
fontSize: '1.5rem',
|
|
transition: 'none',
|
|
flexShrink: 0,
|
|
userSelect: 'none',
|
|
},
|
|
footer: {
|
|
display: 'flex',
|
|
justifyContent: 'flex-end',
|
|
padding: '0.5rem',
|
|
...theme.mixins.panelBorder?.top,
|
|
},
|
|
margin: {
|
|
marginLeft: '0.25rem',
|
|
},
|
|
iconButtonStyle: {
|
|
marginLeft: 'auto',
|
|
marginRight: '4px'
|
|
}
|
|
}));
|
|
|
|
function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose, fullScreen = false, isFullWidth = false, showFullScreen = false, isResizeable = false }) {
|
|
let useModalRef = useModal();
|
|
const classes = useModalStyles();
|
|
let closeModal = (_e, reason) => {
|
|
useModalRef.closeModal(id);
|
|
if(reason == 'escapeKeyDown') {
|
|
onClose?.();
|
|
}
|
|
};
|
|
const [isfullScreen, setIsFullScreen] = useState(fullScreen);
|
|
|
|
return (
|
|
<Theme>
|
|
<Dialog
|
|
open={true}
|
|
onClose={closeModal}
|
|
PaperComponent={PaperComponent}
|
|
PaperProps={{ 'isfullscreen': isfullScreen.toString(), 'isresizeable': isResizeable.toString(), width: dialogWidth, height: dialogHeight }}
|
|
fullScreen={isfullScreen}
|
|
fullWidth={isFullWidth}
|
|
disableBackdropClick
|
|
>
|
|
<DialogTitle className='modal-drag-area'>
|
|
<Box className={classes.titleBar}>
|
|
<Box className={classes.title} marginRight="0.25rem" >{title}</Box>
|
|
{
|
|
showFullScreen && !isfullScreen &&
|
|
<Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Maximize')} icon={<ExpandDialogIcon className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isfullScreen); }} /></Box>
|
|
}
|
|
{
|
|
showFullScreen && isfullScreen &&
|
|
<Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Minimize')} icon={<MinimizeDialogIcon className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isfullScreen); }} /></Box>
|
|
}
|
|
|
|
<Box marginLeft="auto"><PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={closeModal} /></Box>
|
|
</Box>
|
|
</DialogTitle>
|
|
<DialogContent height="100%">
|
|
{content(closeModal)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</Theme>
|
|
);
|
|
}
|
|
ModalContainer.propTypes = {
|
|
id: PropTypes.string,
|
|
title: CustomPropTypes.children,
|
|
content: PropTypes.func,
|
|
fullScreen: PropTypes.bool,
|
|
maxWidth: PropTypes.string,
|
|
isFullWidth: PropTypes.bool,
|
|
showFullScreen: PropTypes.bool,
|
|
isResizeable: PropTypes.bool,
|
|
dialogHeight: PropTypes.number,
|
|
dialogWidth: PropTypes.number,
|
|
onClose: PropTypes.func,
|
|
};
|