stepper.tsx 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  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. type Props = React.HTMLAttributes<HTMLDivElement> & {
  26. currentStepIndex: number;
  27. numSteps: number;
  28. onClick: (stepIndex: number) => void;
  29. };
  30. function Stepper({currentStepIndex, numSteps, onClick, ...props}: Props) {
  31. return (
  32. <StepperContainer {...props}>
  33. {Array(numSteps)
  34. .fill(0)
  35. .map((_, i) => (
  36. <StepperIndicator
  37. key={i}
  38. onClick={() => i < currentStepIndex && onClick(i)}
  39. clickable={i < currentStepIndex}
  40. >
  41. {currentStepIndex === i && (
  42. <StepperTransitionIndicator
  43. layout
  44. transition={testableTransition({
  45. type: 'spring',
  46. stiffness: 175,
  47. damping: 18,
  48. })}
  49. initial={false}
  50. layoutId="animation"
  51. />
  52. )}
  53. </StepperIndicator>
  54. ))}
  55. </StepperContainer>
  56. );
  57. }
  58. export default Stepper;