stepper.tsx 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import styled from '@emotion/styled';
  2. import {motion} from 'framer-motion';
  3. import {space} from 'sentry/styles/space';
  4. import testableTransition from 'sentry/utils/testableTransition';
  5. const StepperContainer = styled('div')`
  6. display: flex;
  7. flex-direction: row;
  8. gap: ${space(1)};
  9. border-radius: 4px;
  10. position: relative;
  11. overflow: hidden;
  12. `;
  13. const StepperIndicator = styled('span')<{clickable?: boolean}>`
  14. height: 8px;
  15. width: 80px;
  16. background-color: ${p => p.theme.progressBackground};
  17. cursor: ${p => (p.clickable ? 'pointer' : 'default')};
  18. `;
  19. const StepperTransitionIndicator = styled(motion.span)`
  20. height: 8px;
  21. width: 80px;
  22. background-color: ${p => p.theme.progressBar};
  23. position: absolute;
  24. `;
  25. StepperTransitionIndicator.defaultProps = {
  26. layout: true,
  27. transition: testableTransition({
  28. type: 'spring',
  29. stiffness: 175,
  30. damping: 18,
  31. }),
  32. };
  33. type Props = React.HTMLAttributes<HTMLDivElement> & {
  34. currentStepIndex: number;
  35. numSteps: number;
  36. onClick: (stepIndex: number) => void;
  37. };
  38. function Stepper({currentStepIndex, numSteps, onClick, ...props}: Props) {
  39. return (
  40. <StepperContainer {...props}>
  41. {Array(numSteps)
  42. .fill(0)
  43. .map((_, i) => (
  44. <StepperIndicator
  45. key={i}
  46. onClick={() => i < currentStepIndex && onClick(i)}
  47. clickable={i < currentStepIndex}
  48. >
  49. {currentStepIndex === i && (
  50. <StepperTransitionIndicator initial={false} layoutId="animation" />
  51. )}
  52. </StepperIndicator>
  53. ))}
  54. </StepperContainer>
  55. );
  56. }
  57. export default Stepper;