Enhance WizardView component by adding support for controlled current step management and optional sub-step descriptions using Timeline. Introduce new props for sidebar width, button visibility, and step skipping functionality, improving user interaction and flexibility in the wizard flow.

This commit is contained in:
Tom Butcher 2026-07-20 02:05:58 +01:00
parent 0c6fe5cbac
commit 9442c9105b

View File

@ -9,13 +9,15 @@ import {
Divider, Divider,
Progress, Progress,
Button, Button,
Dropdown Dropdown,
Timeline
} from 'antd' } from 'antd'
import NewObjectButtons from './NewObjectButtons' import NewObjectButtons from './NewObjectButtons'
const { Title } = Typography const { Title } = Typography
const WizardView = ({ const WizardView = ({
sizeBarWidth = '160px',
showSteps = true, showSteps = true,
steps, steps,
title = 'Wizard View', title = 'Wizard View',
@ -27,22 +29,76 @@ const WizardView = ({
progress = 0, progress = 0,
actions = [], actions = [],
sideBarGrow = false, sideBarGrow = false,
disabled = false disabled = false,
showButtons = true,
showTitle = true,
currentStep: controlledCurrentStep,
currentSubStep = 0,
onStepChange,
showSkipButton = false,
onSkip = () => {}
}) => { }) => {
const [currentStep, setCurrentStep] = useState(0) const [internalCurrentStep, setInternalCurrentStep] = useState(0)
const currentStep = controlledCurrentStep ?? internalCurrentStep
const isMobile = useMediaQuery({ maxWidth: 768 }) const isMobile = useMediaQuery({ maxWidth: 768 })
const setCurrentStep = (step) => {
if (controlledCurrentStep === undefined) {
setInternalCurrentStep(step)
}
onStepChange?.(step)
}
return ( return (
<Flex gap='middle' style={{ width: '100%' }}> <Flex gap='middle' style={{ width: '100%' }}>
{!isMobile && showSteps == true ? ( {!isMobile && showSteps == true ? (
sideBar != null ? ( sideBar != null ? (
sideBar sideBar
) : ( ) : (
<div style={{ minWidth: sideBarGrow == true ? '100%' : '160px' }}> <div
style={{ minWidth: sideBarGrow == true ? '100%' : sizeBarWidth }}
>
<Steps <Steps
current={currentStep} current={currentStep}
items={steps.map((step, index) => ({ items={steps.map(({ subSteps, ...step }, index) => ({
...step, ...step,
description: subSteps ? (
<Timeline
className='subSteps'
items={subSteps.map((subStep, subStepIndex) => {
const complete =
index < currentStep ||
(index === currentStep && subStepIndex < currentSubStep)
const current =
index === currentStep && subStepIndex === currentSubStep
const disabled = index < currentStep
return {
...subStep,
children: subStep.title,
dot: complete ? (
<div
className={`subSteps-dot ${disabled ? 'subSteps-dot-disabled' : ''}`}
>
<CheckIcon
style={{
fontSize: '5px',
marginTop: '0px',
marginRight: '0px',
color: disabled
? 'var(--color-primary)'
: 'var(--layout-modal-bg)'
}}
/>
</div>
) : undefined,
color: complete ? 'blue' : current ? 'blue' : 'gray'
}
})}
/>
) : (
step.description
),
icon: icon:
index < currentStep ? ( index < currentStep ? (
<div className='ant-steps-item-icon'> <div className='ant-steps-item-icon'>
@ -76,62 +132,83 @@ const WizardView = ({
} }
> >
<Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}> <Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}>
<Title level={2} style={{ margin: '0 0 8px 0', lineHeight: 0.7 }}> {showTitle ? (
{title} <Title level={2} style={{ margin: '0 0 8px 0', lineHeight: 0.7 }}>
</Title> {title}
</Title>
) : null}
<div style={{ minHeight: '260px', marginBottom: 4, width: '100%' }}> <div style={{ minHeight: '260px', marginBottom: 4, width: '100%' }}>
{steps[currentStep].content} {steps[currentStep].content}
</div> </div>
</Flex> </Flex>
<Flex gap={'middle'} align='center' justify='end'> {showButtons || progress > 0 || actions.length > 0 ? (
{progress > 0 ? ( <Flex
<Progress gap={'middle'}
style={{ maxWidth: '160px' }} align='center'
showInfo={false} justify={showSkipButton ? 'space-between' : 'end'}
percent={progress} >
/> {showSkipButton && showButtons && (
) : null} <Button onClick={onSkip} disabled={disabled}>
{(actions || []).map((action) => { Skip
if (action.steps.includes(steps[currentStep].key)) { </Button>
if (action.children) { )}
return (
<Dropdown menu={{ items: action.children }} key={action.key}> <Flex gap={'middle'} align='center' justify='end'>
{progress > 0 ? (
<Progress
style={{ maxWidth: '160px' }}
showInfo={false}
percent={progress}
/>
) : null}
{(actions || []).map((action) => {
if (action.steps.includes(steps[currentStep].key)) {
if (action.children) {
return (
<Dropdown
menu={{ items: action.children }}
key={action.key}
>
<Button
onClick={action?.onClick}
disabled={action?.disabled || disabled}
loading={action?.loading || false}
>
{action.label}
</Button>
</Dropdown>
)
}
return (
<Button <Button
key={action.key}
onClick={action?.onClick} onClick={action?.onClick}
disabled={action?.disabled || disabled} disabled={action?.disabled || disabled}
loading={action?.loading || false} loading={action?.loading || false}
> >
{action.label} {action.label}
</Button> </Button>
</Dropdown> )
) }
} return null
return ( })}
<Button
key={action.key}
onClick={action?.onClick}
disabled={action?.disabled || disabled}
loading={action?.loading || false}
>
{action.label}
</Button>
)
}
return null
})}
<NewObjectButtons {showButtons && (
disabled={disabled} <NewObjectButtons
currentStep={currentStep} disabled={disabled}
totalSteps={steps.length} currentStep={currentStep}
onPrevious={() => setCurrentStep((prev) => prev - 1)} totalSteps={steps.length}
onNext={() => setCurrentStep((prev) => prev + 1)} onPrevious={() => setCurrentStep(currentStep - 1)}
onSubmit={onSubmit} onNext={() => setCurrentStep(currentStep + 1)}
formValid={formValid} onSubmit={onSubmit}
submitLoading={loading} formValid={formValid}
submitText={submitText} submitLoading={loading}
/> submitText={submitText}
</Flex> />
)}
</Flex>
</Flex>
) : null}
</Flex> </Flex>
</Flex> </Flex>
) )
@ -149,7 +226,15 @@ WizardView.propTypes = {
submitText: PropTypes.string, submitText: PropTypes.string,
progress: PropTypes.number, progress: PropTypes.number,
actions: PropTypes.array, actions: PropTypes.array,
sideBarGrow: PropTypes.bool sideBarGrow: PropTypes.bool,
showButtons: PropTypes.bool,
showTitle: PropTypes.bool,
currentStep: PropTypes.number,
currentSubStep: PropTypes.number,
onStepChange: PropTypes.func,
showSkipButton: PropTypes.bool,
onSkip: PropTypes.func,
sizeBarWidth: PropTypes.string
} }
export default WizardView export default WizardView