import React, { Component, createRef } from 'react' import { baseChromelessStyles } from '@monorail/helpers/baseStyles' import { BorderRadius, borderRadius, Colors, flexFlow, FontSizes, getColor, typographyFont, } from '@monorail/helpers/exports' import styled, { css } from '@monorail/helpers/styled-components' import { getThemeColor, ThemeColors } from '@monorail/helpers/theme' import { isNil } from '@monorail/sharedHelpers/typeGuards' import { CommonComponentType, CssOverridesType } from '@monorail/types' import { Icon } from '@monorail/visualComponents/icon/Icon' import { Step as StepType } from './types' /* * Styles */ const getStateStyles = ( step: StepType, styles: { isActive: CssOverridesType isCompleted: CssOverridesType isDisabled: CssOverridesType }, ) => { if (step.isActive) { return styles.isActive } else if (step.isCompleted) { return styles.isCompleted } return styles.isDisabled } const stepStateStyles = { isActive: css``, isDisabled: css` pointer-events: none; `, isCompleted: css``, } const bodyStateStyles = { isActive: css``, isDisabled: css` opacity: 0.4; `, isCompleted: css``, } const numberStateStyles = { isActive: css` background-color: ${getThemeColor(ThemeColors.ActionPrimary)}; border-color: ${getThemeColor(ThemeColors.ActionPrimary)}; color: ${getColor(Colors.White)}; `, isDisabled: css` background-color: transparent; border-color: ${getThemeColor(ThemeColors.Text200)}; color: ${getThemeColor(ThemeColors.Text200)}; `, isCompleted: css` background-color: ${getColor(Colors.White)}; border-color: ${getThemeColor(ThemeColors.ActionPrimary)}; `, } const HorizontalStepperContainer = styled.div` ${flexFlow('row')}; flex-shrink: 0; height: 100%; justify-content: flex-start; overflow-y: auto; width: 100%; ` const HorizontalStepperWrapper = styled.div` ${flexFlow('row')}; flex-shrink: 0; height: 100%; justify-content: flex-start; max-width: 100%; overflow-y: auto; ` const Step = styled.div( ({ step }) => css` ${flexFlow('row')}; ${getStateStyles(step, stepStateStyles)}; align-items: center; cursor: pointer; justify-content: center; position: relative; transition: all ease 0.25s; user-select: none; `, ) const Body = styled.div( ({ step }) => css` ${baseChromelessStyles()}; ${flexFlow('row')}; ${getStateStyles(step, bodyStateStyles)}; align-items: center; height: 100%; padding: 8px; `, ) const StyledIcon = styled(Icon)` color: ${getThemeColor(ThemeColors.ActionSecondary)}; flex-shrink: 0; margin: auto 8px; ` const Number = styled.div( ({ step }) => css` ${borderRadius(BorderRadius.Round)}; ${flexFlow('row')}; ${getStateStyles(step, numberStateStyles)}; ${typographyFont(700, FontSizes.Title4)}; align-items: center; border-style: solid; border-width: 2px; flex-shrink: 0; height: 20px; justify-content: center; margin: auto 8px; width: 20px; `, ) const TextContainer = styled.div` ${flexFlow('column')}; margin-right: 8px; ` const Title = styled.div` ${typographyFont(700, FontSizes.Title4)}; color: ${getThemeColor(ThemeColors.Text700)}; ` const Subtitle = styled.div` ${typographyFont(300, FontSizes.Title5)}; color: ${getThemeColor(ThemeColors.Text700)}; ` const Line = styled.div` background-color: ${getThemeColor(ThemeColors.Text200)}; height: 1px; margin: auto 8px; width: 32px; ` /* * Types */ type StepPropType = { step: StepType } type Props = CommonComponentType & { onStepClick: (step: StepType, index: number) => void steps: Array } type State = { activeStepIndex: number } /* * Components */ export class HorizontalStepper extends Component { state: State = { activeStepIndex: 0, } activeStepRef = createRef() scrollStepIntoView = () => { const activeStep = this.activeStepRef.current if (!isNil(activeStep)) { activeStep.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center', }) } } componentDidUpdate(prevProps: Props, prevState: State) { if (prevState.activeStepIndex !== this.state.activeStepIndex) { this.scrollStepIntoView() } } renderSection = (step: StepType, index: number) => { const { onStepClick, steps } = this.props const { activeStepIndex } = this.state if (step.isActive && activeStepIndex !== index) { this.setState({ activeStepIndex: index }) } return ( onStepClick(step, index)} data-test-id={`horizontal-stepper-step-${index}`} ref={step.isActive ? this.activeStepRef : null} > {step.isCompleted && !step.isActive ? ( ) : ( {index + 1} )} {step.label} {!isNil(step.subtitle) && {step.subtitle}} {index !== steps.length - 1 && } ) } render() { const { steps, onStepClick, ...domProps } = this.props return ( {steps.map(this.renderSection)} ) } }