/** * The navigation bar at the steps for the Wizard * Represents a chain of wizard steps with the ability to switch to any available steps * Works with Wizardcontent for easy access to the state and methods of the wizard * You need to connect this component inside the Step where navigation is required * * @author: Denis Makarov * @date: 2020-02-12 */ import * as React from 'react'; import keyBy from 'lodash/keyBy'; import {WizardContext} from '../Wizard'; import {StepSchema, WizardContextProps, WizardStep} from '../../../type/WizardTypes'; import {Steps} from 'antd'; import {IStringKeyMap} from '../../../type'; import * as styles from './navigation.m.scss'; type StepSize = 'default' | 'small'; interface IProps { allowPrev?: boolean; allowNext?: boolean; beforePrevStep?: () => void; beforeNextStep?: () => void; stepSize?: StepSize; } interface IDefaultProps { allowPrev: boolean; allowNext: boolean; stepSize: StepSize; } const {Step} = Steps; export class Navigation extends React.Component { static defaultProps: IDefaultProps = { allowPrev: true, allowNext: true, stepSize: 'small' }; constructor (props: IProps) { super(props); this.moveToStep = this.moveToStep.bind(this); } moveToStep (wizardProps: WizardContextProps, nextStepKey: string, nextStepAllowed: boolean, nextStepIndex: number, activeStepIndex: number) { const moveNext = nextStepIndex > activeStepIndex; if (!nextStepAllowed) { return; } if (moveNext && this.props.beforeNextStep) { this.props.beforeNextStep(); return; } if (!moveNext && this.props.beforePrevStep) { this.props.beforePrevStep(); return; } wizardProps.moveToStep(nextStepKey); } private isStepAllowed (currentStepIndex: number, index: number) { let stepAllowed = false; if (currentStepIndex + 1 === index && this.props.allowNext) { // next step stepAllowed = true; } else if (currentStepIndex > index) { // prev steps stepAllowed = true; } return stepAllowed; } private static buildStepsChain (currentStepName: string, steps: WizardStep[], stepsSchema?: IStringKeyMap) { let array: WizardStep []; if (!stepsSchema) { array = steps; return array; } return Navigation.buildStepsChainFromSchema(currentStepName, steps, stepsSchema); } private static buildStepsChainFromSchema (currentStep: string, steps: WizardStep [], stepsSchema: IStringKeyMap) { const stepsMap = keyBy(steps, 'name'); const array: WizardStep [] = []; array.push(stepsMap[currentStep]); const currentSchemaStep: StepSchema = stepsSchema[currentStep]; let prevStep = currentSchemaStep.prev; let nextStep = currentSchemaStep.next; while (prevStep) { array.unshift(stepsMap[prevStep]); // insert the previous steps at the beginning prevStep = stepsSchema[prevStep].prev; } while (nextStep) { array.push(stepsMap[nextStep]); // the next steps are inserted at the end nextStep = stepsSchema[nextStep].next; } return array; } override render () { return ( {(wizardProps: WizardContextProps) => { const stepsChain = Navigation.buildStepsChain(wizardProps.currentStep, wizardProps.steps, wizardProps.stepsSchema); const currentStepIndex = stepsChain.findIndex((step: WizardStep) => step.name === wizardProps.currentStep); return (
{stepsChain.map((step: WizardStep, index: number) => { const stepAllowed = this.isStepAllowed(currentStepIndex, index); return ( ); })}
); }}
); } }