diff --git a/web/package.json b/web/package.json
index 6cdfc1596..22aa90523 100644
--- a/web/package.json
+++ b/web/package.json
@@ -144,6 +144,7 @@
"react": "^17.0.1",
"react-aspen": "^1.1.0",
"react-dom": "^17.0.1",
+ "react-draggable": "^4.4.4",
"react-select": "^4.2.1",
"react-table": "^7.6.3",
"react-virtualized-auto-sizer": "^1.0.6",
diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx
index 08fbb758d..b9f9b1860 100644
--- a/web/pgadmin/static/js/Theme/index.jsx
+++ b/web/pgadmin/static/js/Theme/index.jsx
@@ -167,7 +167,7 @@ basicSettings = createMuiTheme(basicSettings, {
top: 0,
zIndex: 9999,
}
- }
+ },
},
transitions: {
duration: {
@@ -199,6 +199,9 @@ basicSettings = createMuiTheme(basicSettings, {
},
MuiCheckbox: {
disableTouchRipple: true,
+ },
+ MuiDialogTitle: {
+ disableTypography: true,
}
},
});
@@ -390,6 +393,22 @@ function getFinalTheme(baseTheme) {
color: baseTheme.palette.text.muted,
},
},
+ MuiDialogContent: {
+ root: {
+ padding: 0,
+ userSelect: 'text',
+ }
+ },
+ MuiDialogTitle: {
+ root: {
+ fontWeight: 'bold',
+ padding: '5px 10px',
+ cursor: 'move',
+ display: 'flex',
+ alignItems: 'center',
+ ...mixins.panelBorder.bottom,
+ }
+ },
}
}, baseTheme);
}
diff --git a/web/pgadmin/static/js/components/Buttons.jsx b/web/pgadmin/static/js/components/Buttons.jsx
index 4c9c14b54..2554692dc 100644
--- a/web/pgadmin/static/js/components/Buttons.jsx
+++ b/web/pgadmin/static/js/components/Buttons.jsx
@@ -15,6 +15,12 @@ import CustomPropTypes from '../custom_prop_types';
const useStyles = makeStyles((theme)=>({
primaryButton: {
+ '&.MuiButton-outlinedSizeSmall': {
+ height: '28px',
+ '& .MuiSvgIcon-root': {
+ height: '0.8em',
+ }
+ },
'&.Mui-disabled': {
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.disabledMain,
@@ -28,6 +34,13 @@ const useStyles = makeStyles((theme)=>({
backgroundColor: theme.palette.default.main,
color: theme.palette.default.contrastText,
border: '1px solid '+theme.palette.default.borderColor,
+ whiteSpace: 'nowrap',
+ '&.MuiButton-outlinedSizeSmall': {
+ height: '28px',
+ '& .MuiSvgIcon-root': {
+ height: '0.8em',
+ }
+ },
'&.Mui-disabled': {
color: theme.palette.default.disabledContrastText,
borderColor: theme.palette.default.disabledBorderColor
@@ -52,35 +65,59 @@ const useStyles = makeStyles((theme)=>({
backgroundColor: theme.custom.icon.hoverMain,
color: theme.custom.icon.hoverContrastText,
}
+ },
+ xsButton: {
+ padding: '2px 1px',
+ height: '24px',
+ '& .MuiSvgIcon-root': {
+ height: '0.8em',
+ }
+ },
+ noBorder: {
+ border: 0,
}
}));
/* pgAdmin primary button */
export const PrimaryButton = forwardRef((props, ref)=>{
- let {children, className, ...otherProps} = props;
+ let {children, className, size, noBorder, ...otherProps} = props;
const classes = useStyles();
-
+ let allClassName = [classes.primaryButton, className];
+ if(size == 'xs') {
+ size = undefined;
+ allClassName.push(classes.xsButton);
+ }
+ noBorder && allClassName.push(classes.noBorder);
return (
-
+
);
});
PrimaryButton.displayName = 'PrimaryButton';
PrimaryButton.propTypes = {
+ size: PropTypes.string,
+ noBorder: PropTypes.bool,
children: CustomPropTypes.children,
className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
};
/* pgAdmin default button */
export const DefaultButton = forwardRef((props, ref)=>{
- let {children, className, ...otherProps} = props;
+ let {children, className, size, noBorder, ...otherProps} = props;
const classes = useStyles();
-
+ let allClassName = [classes.defaultButton, className];
+ if(size == 'xs') {
+ size = undefined;
+ allClassName.push(classes.xsButton);
+ }
+ noBorder && allClassName.push(classes.noBorder);
return (
-
+
);
});
DefaultButton.displayName = 'DefaultButton';
DefaultButton.propTypes = {
+ size: PropTypes.string,
+ noBorder: PropTypes.bool,
children: CustomPropTypes.children,
className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
};
diff --git a/web/pgadmin/static/js/helpers/ModalProvider.jsx b/web/pgadmin/static/js/helpers/ModalProvider.jsx
new file mode 100644
index 000000000..c831760b0
--- /dev/null
+++ b/web/pgadmin/static/js/helpers/ModalProvider.jsx
@@ -0,0 +1,92 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2021, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import { Box, Dialog, DialogContent, DialogTitle, Paper } from '@material-ui/core';
+import React from 'react';
+import {getEpoch} from 'sources/utils';
+import { PgIconButton } 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';
+
+const ModalContext = React.createContext({});
+
+export function useModal() {
+ return React.useContext(ModalContext);
+}
+
+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 modalContext = React.useMemo(()=>({
+ showModal: showModal,
+ closeModal: closeModal,
+ }), []);
+ return (
+
')
).set('title', promptmsg).set('closable', true);
- }
+ },
+ alert: (title, text, onCancelClick)=>{
+ if(!modalInitialized) {
+ initializeModalProvider(document.getElementById('modalContainer'));
+ }
+ modalRef.showModal(title, (closeModal)=>{
+ const onCancelClickClose = ()=>{
+ onCancelClick && onCancelClick();
+ closeModal();
+ };
+ return (
+