????
Current Path : C:/opt/pgsql/pgAdmin 4/web/pgadmin/static/js/helpers/wizard/ |
Current File : C:/opt/pgsql/pgAdmin 4/web/pgadmin/static/js/helpers/wizard/Wizard.jsx |
///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2024, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import React from 'react'; import { makeStyles } from '@mui/styles'; import clsx from 'clsx'; import FastForwardIcon from '@mui/icons-material/FastForward'; import FastRewindIcon from '@mui/icons-material/FastRewind'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import DoneIcon from '@mui/icons-material/Done'; import HelpIcon from '@mui/icons-material/HelpRounded'; import CheckIcon from '@mui/icons-material/Check'; import { DefaultButton, PrimaryButton, PgIconButton } from '../../../../static/js/components/Buttons'; import PropTypes from 'prop-types'; import { Box } from '@mui/material'; import gettext from 'sources/gettext'; import Loader from 'sources/components/Loader'; const useStyles = makeStyles((theme) => ({ wizardBase: { height: '100%', display: 'flex', flexDirection: 'column', backgroundColor: theme.palette.background.default }, root: { display: 'flex', flexDirection: 'column', flexGrow: 1, minHeight: 0 }, rightPanel: { position: 'relative', display: 'flex', flexBasis: '75%', overflow: 'auto', height: '100%', minHeight: '100px' }, leftPanel: { display: 'flex', flexBasis: '25%', flexDirection: 'column', alignItems: 'flex-start', borderRight: '1px solid', ...theme.mixins.panelBorder.right, }, label: { display: 'inline-block', position: 'relative', paddingLeft: '0.5rem', flexBasis: '70%' }, labelArrow: { display: 'inline-block', position: 'relative', flexBasis: '30%' }, labelDone: { display: 'inline-block', position: 'relative', flexBasis: '30%', color: theme.otherVars.activeStepBg + ' !important', padding: '4px' }, stepLabel: { padding: '1em', paddingRight: 0 }, active: { fontWeight: 600 }, activeIndex: { backgroundColor: theme.otherVars.activeStepBg + ' !important', color: theme.otherVars.activeStepFg + ' !important' }, stepIndex: { padding: '0.5em 1em ', height: '2.5em', borderRadius: '2em', backgroundColor: theme.otherVars.stepBg, color: theme.otherVars.stepFg, display: 'inline-block', flex: 0.5, }, wizard: { width: '100%', height: '100%', minHeight: 100, display: 'flex', flexWrap: 'wrap', }, wizardFooter: { borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`, padding: '0.5rem', display: 'flex', width: '100%', background: theme.otherVars.headerBg, zIndex: 999, }, backButton: { marginRight: theme.spacing(1), }, instructions: { marginTop: theme.spacing(1), marginBottom: theme.spacing(1), }, actionBtn: { alignItems: 'flex-start', }, buttonMargin: { marginLeft: '0.5em' }, stepDefaultStyle: { width: '100%', height: '100%', padding: '8px', display: 'flex', flexDirection: 'column', }, hidden: { display: 'none', } }), ); function Wizard({ stepList, onStepChange, onSave, className, ...props }) { const classes = useStyles(); const [activeStep, setActiveStep] = React.useState(0); const steps = stepList && stepList.length > 0 ? stepList : []; const [disableNext, setDisableNext] = React.useState(false); const handleNext = () => { // beforeNext should always return a promise if(props.beforeNext) { props.beforeNext(activeStep).then((skipCurrentStep=false)=>{ if (skipCurrentStep) { setActiveStep((prevActiveStep) => prevActiveStep + 2); } else { setActiveStep((prevActiveStep) => prevActiveStep + 1); } }).catch(()=>{/*This is intentional (SonarQube)*/}); } else { setActiveStep((prevActiveStep) => prevActiveStep + 1); } }; const handleBack = () => { // beforeBack should always return a promise if(props.beforeBack) { props.beforeBack(activeStep).then((skipCurrentStep=false)=>{ if (skipCurrentStep) { setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 2); } else { setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1); } }).catch(()=>{/*This is intentional (SonarQube)*/}); } else { setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1); } }; React.useEffect(() => { if (onStepChange) { onStepChange({ currentStep: activeStep }); } }, [activeStep]); React.useEffect(() => { if (props.disableNextStep) { setDisableNext(props.disableNextStep(activeStep)); } }); return ( <Box className={classes.wizardBase}> <div className={clsx(classes.root, props?.rootClass)}> <div className={clsx(classes.wizard, className)}> <Box className={classes.leftPanel}> {steps.map((label, index) => ( <Box key={label} className={clsx(classes.stepLabel, index === activeStep ? classes.active : '')}> <Box className={clsx(classes.stepIndex, index === activeStep ? classes.activeIndex : '')}>{index + 1}</Box> <Box className={classes.label}>{label} </Box> <Box className={classes.labelArrow}>{index === activeStep ? <ChevronRightIcon /> : null}</Box> <Box className={classes.labelDone}>{index < activeStep ? <DoneIcon />: null}</Box> </Box> ))} </Box> <div className={clsx(classes.rightPanel, props.stepPanelCss)}> <Loader message={props?.loaderText} /> { React.Children.map(props.children, (child) => { return ( <div className={clsx(classes.stepDefaultStyle, child.props.className, (child.props.stepId !== activeStep ? classes.hidden : ''))}> {child} </div> ); }) } </div> </div> </div> <div className={classes.wizardFooter}> <Box> <PgIconButton data-test="dialog-help" onClick={() => props.onHelp()} icon={<HelpIcon />} title="Help for this dialog." disabled={props.disableDialogHelp} /> </Box> <Box className={classes.actionBtn} marginLeft="auto"> <DefaultButton onClick={handleBack} disabled={activeStep === 0} className={classes.buttonMargin} startIcon={<FastRewindIcon />}> {gettext('Back')} </DefaultButton> <DefaultButton onClick={() => handleNext()} className={classes.buttonMargin} startIcon={<FastForwardIcon />} disabled={activeStep == steps.length - 1 || disableNext}> {gettext('Next')} </DefaultButton> <PrimaryButton className={classes.buttonMargin} startIcon={<CheckIcon />} disabled={activeStep !== (steps.length - 1) } onClick={onSave}> {gettext('Finish')} </PrimaryButton> </Box> </div> </Box> ); } export default Wizard; Wizard.propTypes = { props: PropTypes.object, title: PropTypes.string, stepList: PropTypes.array, onSave: PropTypes.func, onHelp: PropTypes.func, onStepChange: PropTypes.func, className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), disableNextStep: PropTypes.func, stepPanelCss: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), rootClass: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]), disableDialogHelp: PropTypes.bool, beforeNext: PropTypes.func, beforeBack: PropTypes.func, loaderText: PropTypes.string };