Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ModalProvider.jsx329 linesDownload Raw Back to helpers
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9 10import { Box, Dialog, DialogContent, DialogTitle, Paper } from '@mui/material';11import { makeStyles } from '@mui/styles';12import React, { useState, useMemo } from 'react';13import clsx from 'clsx';14import { getEpoch } from 'sources/utils';15import { DefaultButton, PgIconButton, PrimaryButton } from '../components/Buttons';16import Draggable from 'react-draggable';17import CloseIcon from '@mui/icons-material/CloseRounded';18import CustomPropTypes from '../custom_prop_types';19import PropTypes from 'prop-types';20import gettext from 'sources/gettext';21import HTMLReactParser from 'html-react-parser';22import CheckRoundedIcon from '@mui/icons-material/CheckRounded';23import { Rnd } from 'react-rnd';24import { ExpandDialogIcon, MinimizeDialogIcon } from '../components/ExternalIcon';25 26export const ModalContext = React.createContext({});27const MIN_HEIGHT = 190;28const MIN_WIDTH = 500;29 30export function useModal() {31  return React.useContext(ModalContext);32}33const useAlertStyles = makeStyles((theme) => ({34  footer: {35    display: 'flex',36    justifyContent: 'flex-end',37    padding: '0.5rem',38    ...theme.mixins.panelBorder.top,39  },40  margin: {41    marginLeft: '0.25rem',42  }43}));44 45function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLabel = gettext('Cancel'), onOkClick, onCancelClick }) {46  const classes = useAlertStyles();47  return (48    <Box display="flex" flexDirection="column" height="100%">49      <Box flexGrow="1" p={2}>{typeof (text) == 'string' ? HTMLReactParser(text) : text}</Box>50      <Box className={classes.footer}>51        {confirm &&52          <DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} >{cancelLabel}</DefaultButton>53        }54        <PrimaryButton className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>55      </Box>56    </Box>57  );58}59AlertContent.propTypes = {60  text: PropTypes.string,61  confirm: PropTypes.bool,62  onOkClick: PropTypes.func,63  onCancelClick: PropTypes.func,64  okLabel: PropTypes.string,65  cancelLabel: PropTypes.string,66};67 68function alert(title, text, onOkClick, okLabel = gettext('OK')) {69  // bind the modal provider before calling70  this.showModal(title, (closeModal) => {71    const onOkClickClose = () => {72      onOkClick?.();73      closeModal();74    };75    return (76      <AlertContent text={text} onOkClick={onOkClickClose} okLabel={okLabel} />77    );78  });79}80 81function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No')) {82  // bind the modal provider before calling83  this.showModal(title, (closeModal) => {84    const onCancelClickClose = () => {85      onCancelClick?.();86      closeModal();87    };88    const onOkClickClose = () => {89      onOkClick?.();90      closeModal();91    };92    return (93      <AlertContent text={text} confirm onOkClick={onOkClickClose} onCancelClick={onCancelClickClose} okLabel={okLabel} cancelLabel={cancelLabel} />94    );95  });96}97 98export default function ModalProvider({ children }) {99  const [modals, setModals] = React.useState([]);100 101  const showModal = (title, content, modalOptions) => {102    let id = getEpoch().toString() + crypto.getRandomValues(new Uint8Array(4));103    setModals((prev) => [...prev, {104      id: id,105      title: title,106      content: content,107      ...modalOptions,108    }]);109  };110  const closeModal = (id) => {111    setModals((prev) => {112      return prev.filter((o) => o.id != id);113    });114  };115 116  const fullScreenModal = (fullScreen) => {117    setModals((prev) => [...prev, {118      fullScreen: fullScreen,119    }]);120  };121 122  const modalContextBase = {123    showModal: showModal,124    closeModal: closeModal,125    fullScreenModal: fullScreenModal126  };127  const modalContext = React.useMemo(() => ({128    ...modalContextBase,129    confirm: confirm.bind(modalContextBase),130    alert: alert.bind(modalContextBase)131  }), []);132  return (133    <ModalContext.Provider value={modalContext}>134      {children}135      {modals.map((modalOptions) => (136        <ModalContainer key={modalOptions.id} {...modalOptions} />137      ))}138    </ModalContext.Provider>139  );140}141 142ModalProvider.propTypes = {143  children: CustomPropTypes.children,144};145 146const dialogStyle = makeStyles((theme) => ({147  dialog: {148    display: 'flex',149    alignItems: 'center',150    justifyContent: 'center',151    border: '1px solid ' + theme.otherVars.inputBorderColor,152    borderRadius: theme.shape.borderRadius,153  },154  fullScreen: {155    transform: 'none !important'156  }157}));158 159 160function checkIsResizable(props) {161  return props.isresizeable == 'true';162}163 164function setEnableResizing(props, resizeable) {165  return props.isfullscreen == 'true' ? false : resizeable;166}167 168function PaperComponent({minHeight, minWidth, ...props}) {169  let classes = dialogStyle();170  let [dialogPosition, setDialogPosition] = useState(null);171  let resizeable = checkIsResizable(props);172 173  const setConditionalPosition = () => {174    return props.isfullscreen == 'true' ? { x: 0, y: 0 } : dialogPosition && { x: dialogPosition.x, y: dialogPosition.y };175  };176 177  const y_position = window.innerHeight*0.02; // 2% of total height178  const x_position =  props.width ? (window.innerWidth/2) - (props.width/2) : (window.innerWidth/2) - (MIN_WIDTH/2);179 180  return (181    props.isresizeable == 'true' ?182      <Rnd183        size={props.isfullscreen == 'true' && { width: '100%', height: '100%' }}184        className={clsx(classes.dialog, props.isfullscreen == 'true' ? classes.fullScreen : '')}185        default={{186          x: x_position,187          y: y_position,188          ...(props.width && { width: props.width }),189          ...(props.height && { height: props.height }),190        }}191        minWidth = { minWidth || MIN_WIDTH }192        minHeight = { minHeight || MIN_HEIGHT }193        // {...(props.width && { minWidth: MIN_WIDTH })}194        // {...(props.height && { minHeight: MIN_HEIGHT })}195        bounds="window"196        enableResizing={setEnableResizing(props, resizeable)}197        position={setConditionalPosition()}198        onDragStop={(e, position) => {199          if (props.isfullscreen !== 'true') {200            setDialogPosition({201              ...position,202            });203          }204        }}205        onResize={(e, direction, ref, delta, position) => {206          setDialogPosition({207            ...position,208          });209        }}210        dragHandleClassName="modal-drag-area"211      >212        <Paper {...props} style={{ width: '100%', height: '100%', maxHeight: '100%', maxWidth: '100%' }} />213      </Rnd>214      :215      <Draggable cancel={'[class*="MuiDialogContent-root"]'}>216        <Paper {...props} style={{ minWidth: '600px' }} />217      </Draggable>218  );219}220 221PaperComponent.propTypes = {222  isfullscreen: PropTypes.string,223  isresizeable: PropTypes.string,224  width: PropTypes.number,225  height: PropTypes.number,226  minWidth: PropTypes.number,227  minHeight: PropTypes.number,228};229 230export const useModalStyles = makeStyles((theme) => ({231  container: {232    backgroundColor: theme.palette.background.default233  },234 235  titleBar: {236    display: 'flex',237    flexGrow: 1238  },239  title: {240    flexGrow: 1241  },242  icon: {243    fill: 'currentColor',244    width: '1em',245    height: '1em',246    display: 'inline-block',247    fontSize: '1.5rem',248    transition: 'none',249    flexShrink: 0,250    userSelect: 'none',251  },252  footer: {253    display: 'flex',254    justifyContent: 'flex-end',255    padding: '0.5rem',256    ...theme.mixins.panelBorder?.top,257  },258  margin: {259    marginLeft: '0.25rem',260  },261  iconButtonStyle: {262    marginLeft: 'auto',263    marginRight: '4px'264  },265}));266 267function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose, fullScreen = false, isFullWidth = false, showFullScreen = false, isResizeable = false, minHeight = MIN_HEIGHT, minWidth = MIN_WIDTH, showTitle=true }) {268  let useModalRef = useModal();269  const classes = useModalStyles();270  let closeModal = (_e, reason) => {271    if(reason == 'backdropClick' && showTitle) {272      return;273    }274    useModalRef.closeModal(id);275    if(reason == 'escapeKeyDown' || reason == undefined) {276      onClose?.();277    }278  };279  const [isFullScreen, setIsFullScreen] = useState(fullScreen);280 281  return (282    <Dialog283      open={true}284      onClose={closeModal}285      PaperComponent={PaperComponent}286      PaperProps={{ 'isfullscreen': isFullScreen.toString(), 'isresizeable': isResizeable.toString(), width: dialogWidth, height: dialogHeight, minHeight: minHeight, minWidth: minWidth }}287      fullScreen={isFullScreen}288      fullWidth={isFullWidth}289      disablePortal290    >291      { showTitle &&292        <DialogTitle className='modal-drag-area'>293          <Box className={classes.titleBar}>294            <Box className={classes.title} marginRight="0.25rem" >{title}</Box>295            {296              showFullScreen && !isFullScreen &&297                <Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Maximize')} icon={<ExpandDialogIcon className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>298            }299            {300              showFullScreen && isFullScreen &&301                <Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Minimize')} icon={<MinimizeDialogIcon  className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>302            }303 304            <Box marginLeft="auto"><PgIconButton title={gettext('Close')} icon={<CloseIcon  />} size="xs" noBorder onClick={closeModal} /></Box>305          </Box>306        </DialogTitle>307      }308      <DialogContent height="100%">309        {useMemo(()=>{ return content(closeModal); }, [])}310      </DialogContent>311    </Dialog>312  );313}314ModalContainer.propTypes = {315  id: PropTypes.string,316  title: CustomPropTypes.children,317  content: PropTypes.func,318  fullScreen: PropTypes.bool,319  isFullWidth: PropTypes.bool,320  showFullScreen: PropTypes.bool,321  isResizeable: PropTypes.bool,322  dialogHeight: PropTypes.number,323  dialogWidth: PropTypes.number,324  onClose: PropTypes.func,325  minWidth: PropTypes.number,326  minHeight: PropTypes.number,327  showTitle: PropTypes.bool,328};329 
codekingpro/portable-devtools · Team Ai