github.com/replicatedhq/ship@v0.55.0/web/init/src/components/shared/StepNumbers.jsx (about) 1 import * as React from "react"; 2 import { withRouter } from "react-router-dom"; 3 import isEmpty from "lodash/isEmpty"; 4 import find from "lodash/find"; 5 import indexOf from "lodash/indexOf"; 6 7 class StepNumbers extends React.Component { 8 constructor() { 9 super(); 10 this.state = { 11 currentStep: 0, 12 progressLength: 0, 13 steps: [], 14 } 15 } 16 17 getPositionAtCenter = (element) => { 18 const data = element.getBoundingClientRect(); 19 return { 20 x: data.left + data.width / 2, 21 y: data.top + data.height / 2 22 }; 23 } 24 25 getDistanceBetweenElements = (a, b) => { 26 var aPosition = this.getPositionAtCenter(a); 27 var bPosition = this.getPositionAtCenter(b); 28 return Math.sqrt(Math.pow(aPosition.x - bPosition.x, 2) + Math.pow(aPosition.y - bPosition.y, 2)); 29 } 30 31 setStepsToState = () => { 32 const { steps } = this.props; 33 const stateSteps = steps.map((step) => { 34 const cleanedPath = this.props.location.pathname.split("/").pop(); 35 const newStep = { 36 ...step, 37 isComplete: false, 38 isActive: cleanedPath === step.id, 39 }; 40 return newStep; 41 }); 42 const currIdx = find(stateSteps, ["isActive", true]); 43 const currStep = indexOf(stateSteps, currIdx); 44 this.setState({ steps: stateSteps, currentStep: currStep }); 45 this.setCompleteSteps(currStep, stateSteps); 46 } 47 48 setCompleteSteps = (currentStep, steps) => { 49 for (let i = 0; i < currentStep; i++) { 50 let currStep = steps[i]; 51 currStep.isComplete = true; 52 } 53 this.setState({ steps }); 54 } 55 56 determineCurrentStep = (id) => { 57 let stateStep = find(this.state.steps, ["id", id]); 58 const stateStepIndex = indexOf(this.state.steps, stateStep); 59 const { currentStep } = this.state; 60 stateStep.isActive = currentStep === stateStepIndex ? true : false; 61 } 62 63 goToStep = (idx) => { 64 const { basePath } = this.props; 65 const step = this.state.steps[idx]; 66 this.props.history.push(`${basePath}/${step.id}`); 67 } 68 69 componentDidUpdate(lastProps, lastState) { 70 if ( 71 (this.props.location.pathname !== lastProps.location.pathname) || 72 (this.props.steps !== lastProps.steps && !isEmpty(this.props.steps)) 73 ) { 74 this.setStepsToState(); 75 } 76 if (this.state.currentStep !== lastState.currentStep) { 77 const elOne = find(this.state.steps, ["isComplete", true]); 78 const elTwo = find(this.state.steps, ["isActive", true]); 79 if (elOne && elTwo) { 80 const length = this.getDistanceBetweenElements(document.getElementById(elOne.id), document.getElementById(elTwo.id)); 81 this.setState({ progressLength: length }); 82 } 83 } 84 } 85 86 componentDidMount() { 87 if (!isEmpty(this.props.steps)) { 88 this.setStepsToState(); 89 } 90 } 91 92 renderSteps = () => { 93 const { steps } = this.state; 94 if (!steps.length) return; 95 const renderedSteps = this.state.steps.map((step, i) => { 96 this.determineCurrentStep(step.id); // Is this the current step, if so set to active 97 return ( 98 <div key={`${step.id}-${i}`} id={step.id} className={`flex-auto u-cursor--pointer flex step-number ${step.isActive ? "is-active" : ""} ${step.isComplete ? "is-complete" : ""}`} onClick={() => this.goToStep(i)}> 99 <span className="number flex-column flex-verticalCenter alignItems--center">{step.isComplete ? <span className="icon clickable u-smallCheckWhite"></span> : i + 1}</span> 100 </div> 101 ) 102 }); 103 return renderedSteps; 104 } 105 106 render() { 107 const { inNav } = this.props; 108 const { currentStep, progressLength } = this.state; 109 return ( 110 <div className={`flex-column justifyContent--center ${inNav ? "navbar-steps flex1" : "flex-auto"}`}> 111 {!isEmpty(this.state.steps) ? <div className="steps-numbers-wrapper"> 112 <div className="numbers-wrapper flex flex1 justifyContent--spaceBetween"> 113 {this.renderSteps()} 114 {currentStep > 0 && <span className="completed-progress-bar" style={{ width: `${progressLength}px` }}></span>} 115 <span className="progress-base"></span> 116 </div> 117 </div> 118 : null} 119 </div> 120 ); 121 } 122 } 123 124 export default withRouter(StepNumbers);