/** * Wizard Component * Displays the passed components of steps * Can organize the display of steps according to the diagram, if it is passed * Copies the wizard's props to the step component for easy access to the wizard's state and methods * * Is a provider for components * @see {Navigation} * @see {Footer} * * @author: Denis Makarov * @date: 2020-02-12 */ import * as React from 'react'; import {StepSchema, WizardContextProps, WizardStep} from '../../type/WizardTypes'; import {Content} from './content/Content'; import * as styles from './wizard.m.scss'; import {IStringKeyMap} from '../../type'; import {Navigation} from './navigation/Navigation'; import {Footer, IWizardFooterProps} from './footer/Footer'; export interface IWizardProps { steps: WizardStep[]; firstStep?: string; stepsSchema?: IStringKeyMap; onSubmit: () => void; } interface IState { activeStepKey: string; } export { IWizardFooterProps }; export const WizardContext = React.createContext(null); export class Wizard extends React.Component { static Footer = Footer; static Navigation = Navigation; static Content = Content; constructor (props: IWizardProps) { super(props); let firstStepKey = this.props.stepsSchema && this.props.firstStep; if (!firstStepKey) { firstStepKey = this.props.steps[0].name; } this.state = { activeStepKey: firstStepKey }; } get activeStep () { return this.props.steps.find((el: WizardStep) => el.name === this.state.activeStepKey); } getNextStep () { if (this.props.stepsSchema) { return this.props.stepsSchema[this.state.activeStepKey].next; } const currentStep = this.activeStep; if (!currentStep) { return null; } const currentStepKey = currentStep.name; const currentStepIndex = this.props.steps.findIndex((step: WizardStep) => step.name === currentStepKey); const nextStep = this.props.steps[currentStepIndex + 1]; return nextStep ? nextStep.name : null; } getPrevStep () { if (this.props.stepsSchema) { return this.props.stepsSchema[this.state.activeStepKey].prev; } const currentStep = this.activeStep; if (!currentStep) { return null; } const currentStepKey = currentStep.name; const currentStepIndex = this.props.steps.findIndex((step: WizardStep) => step.name === currentStepKey); const nextStep = this.props.steps[currentStepIndex - 1]; return nextStep ? nextStep.name : null; } moveNextStep () { this.move(true); } movePrevStep () { this.move(false); } moveToStep (stepKey: string) { this.setState({activeStepKey: stepKey}); } private move (moveNext: boolean) { const currentStep = this.activeStep; if (!currentStep) { return; } const currentStepKey = currentStep.name; let currentStepIndex = this.props.steps.findIndex((step: WizardStep) => step.name === currentStepKey); let nextStepKey: string | null = null; if (currentStepIndex < 0) { return; } if (this.props.stepsSchema) { nextStepKey = moveNext ? this.props.stepsSchema[currentStep.name].next : this.props.stepsSchema[currentStep.name].prev; } else { let stepToMove = moveNext ? this.props.steps[currentStepIndex + 1] : this.props.steps[currentStepIndex - 1]; if (stepToMove) { nextStepKey = stepToMove.name; } } if (!nextStepKey) { return; } this.setState({activeStepKey: nextStepKey}); } override render () { const step = this.props.steps.find((step: WizardStep) => step.name === this.state.activeStepKey); if (!step) { return null; } const StepComponent = step.component; const wizardProps: WizardContextProps = { moveToStep: this.moveToStep.bind(this), moveNextStep: this.moveNextStep.bind(this), movePrevStep: this.movePrevStep.bind(this), onSubmit: this.props.onSubmit, steps: this.props.steps, stepsSchema: this.props.stepsSchema, currentStep: this.state.activeStepKey, getNextStep: this.getNextStep.bind(this), getPrevStep: this.getPrevStep.bind(this) }; return (
{/*using clone to save ref to StepComponent*/} {React.cloneElement(StepComponent, wizardProps)}
); } }