UNPKG

fk-react-ui-components

Version:

Step 1 : Create a file in [ Seeds / Plants / Trees ] <br> Step 2 : It should export an Object with component name and story Component [Refer other components] <br> Step 3 : Story Component should return a react component <br> Step 3 : Created file should

324 lines (286 loc) 9.35 kB
import React from 'react'; import { StepperContainer, StepsWrapper, ContentWrapper, StepItem, DoneIconContainer, ActiveIconContainer, InactiveIconContainer } from './styles'; import { PropTypes } from 'prop-types'; /** * @class{Step} Should be a child of Stepper component */ export class Step extends React.PureComponent { render() { return React.isValidElement(this.props.children) ? this.props.children : null; } } /** * @class{Stepper} Parent class controlling the logic and rendering of steps */ export class Stepper extends React.PureComponent { constructor(props) { super(props); this.state = { stepIndex: props.stepIndex || 0, doneMapping: props.doneMapping || Array(React.Children.count(props.children)).fill(false) }; } /** * Update local state when props change * @param {object} nextProps */ componentWillReceiveProps(nextProps) { if (this.props.stepIndex !== nextProps.stepIndex) { this.setState({stepIndex: nextProps.stepIndex}); } if (this.props.doneMapping !== nextProps.doneMapping) { this.setState({doneMapping: nextProps.doneMapping}); } } /** * Returns the icon to be rendered for any step * @param {object} childProps * @param {number} index * @return {element} */ getIcon = (childProps, index) => { if (childProps.disabled) { return childProps.disabledIcon || childProps.icon || this.props.disabledIcon || this.props.icon || <InactiveIconContainer />; } else if (index === this.state.stepIndex) { return childProps.activeIcon || childProps.icon || this.props.activeIcon || this.props.icon || ( <ActiveIconContainer> <i className='fa fa-chevron-right'/> </ActiveIconContainer> ); } else if (this.state.doneMapping[index]) { return childProps.doneIcon || childProps.icon || this.props.doneIcon || this.props.icon || ( <DoneIconContainer> <i className='fa fa-check'/> </DoneIconContainer> ); } else { return childProps.inactiveIcon || childProps.icon || this.props.inactiveIcon || this.props.icon || <InactiveIconContainer/>; } } /** * Handles click on any step. * Calls onClick prop if provided else goes to that step if current * stepIndex is greater than index and step is not disabled * @param {number} index */ handleClick = (index) => { const childrenArray = React.Children.toArray(this.props.children); if (!childrenArray[index].props.disabled && this.state.doneMapping[index] ) { if (this.props.onChange && typeof this.props.stepIndex !== 'undefined') { this.props.onChange(index, 'click'); } else { this.setState({stepIndex: index}); } } } /** * Function to move to the next step */ next = () => { let index = this.state.stepIndex; const childrenArray = React.Children.toArray(this.props.children); /** * Mark the current step as done in case of un-controlled component */ if (!this.props.doneMapping) { const doneMapping = [...this.state.doneMapping]; doneMapping[index] = true; this.setState({doneMapping}); } /** * Call onComplete handler if all steps are complete */ if (index === childrenArray.length - 1) { if (this.props.onComplete) { this.props.onComplete(); } return; } /** * Calculate the next step index */ while (index < childrenArray.length && (index === this.state.stepIndex || childrenArray[index].props.disabled) ) { index += 1; } /** * Update the step index in the local state in case of un-controlled component. * For controlled component simply call the onChange handler */ if (!childrenArray[index].props.disabled) { if (this.props.onChange && this.props.stepIndex !== 'undefined') { this.props.onChange(index, 'next'); } else { this.setState({ stepIndex: index }); } } } /** * Function to move to the previous step */ previous = () => { let index = this.state.stepIndex; const childrenArray = React.Children.toArray(this.props.children); /** * Calculate the previous index */ while (index > 0 && (index === this.state.stepIndex || childrenArray[index].props.disabled) ) { index -= 1; } /** * Update the step index in the local state in case of un-controlled component. * For controlled component simply call the onChange handler */ if (!childrenArray[index].props.disabled) { if (this.props.onChange && typeof this.props.stepIndex !== 'undefined') { this.props.onChange(index, 'previous'); } else { this.setState({ stepIndex: index }); } } } render() { return ( <StepperContainer orientation={this.props.orientation} styles={this.props.styles.container || {}} > <StepsWrapper orientation={this.props.orientation} styles={this.props.styles.stepper || {}} > { React.Children.map(this.props.children, (step, index) => { return ( <StepItem orientation={this.props.orientation} styles={this.props.styles.step || {}} onClick={() => this.handleClick(index)} active={index === this.state.stepIndex} done={this.state.doneMapping[index]} > { this.getIcon(step.props, index) } { step.props.label} </StepItem> ); })} </StepsWrapper> <ContentWrapper> { this.props.header ? this.props.header : null } { React.Children.toArray(this.props.children)[this.state.stepIndex] } { this.props.footer ? this.props.footer : null } </ContentWrapper> </StepperContainer> ); } } Stepper.propTypes = { /** * Orientation of the stepper. May be horizontal or vertical */ orientation: PropTypes.oneOf(['horizontal', 'vertical']), /** * Styles of the stepper. */ styles: PropTypes.object, /** * Current step index ( to be passes for controlled component) */ stepIndex: PropTypes.number, /** * Header element */ header: PropTypes.element, /** * Footer element */ footer: PropTypes.element, /** * Mapping of steps vs there done state (true / false) * e.g. [true, false, false, false] (First step is done, rest are not) * To be passed for controlled component */ doneMapping: PropTypes.arrayOf(PropTypes.number), /** * Invoked when the step changes */ onChange: PropTypes.func, /** * Invoked when all the steps are completed */ onComplete: PropTypes.func, /** * Default icon JSX element */ icon: PropTypes.element, /** * Inactive icon. Ovverrides icon prop for inactive state */ inactiveIcon: PropTypes.element, /** * Active icon. Overrides icon prop for active state */ activeIcon: PropTypes.element, /** * Disabled icon. Overrides icon prop for disabled state */ disabledIcon: PropTypes.element }; Step.propTypes = { /** * Label for each step */ label: PropTypes.string, /** * Set disabled to true to disable a step */ disabled: PropTypes.bool, /** * Icon for a specific step. If provided, it ovverides the icon provided * in the parent Stepper Component */ icon: PropTypes.element, /** * Inactive icon. Ovverrides icon prop for inactive state */ inactiveIcon: PropTypes.element, /** * Active icon. Ovverrides icon prop for inactive state */ activeIcon: PropTypes.element, /** * Disabled icon. Ovverrides icon prop for inactive state */ disabledIcon: PropTypes.element }; Stepper.defaultProps = { orientation: 'vertical', styles: {}, disabled: false };