controlState.tsx 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import Spinner from 'sentry/components/forms/spinner';
  4. import {IconCheckmark, IconWarning} from 'sentry/icons';
  5. import {fadeOut, pulse} from 'sentry/styles/animations';
  6. interface ControlStateProps {
  7. /**
  8. * Display the error indicator
  9. */
  10. error?: boolean;
  11. /**
  12. * Display the "was just saved" state
  13. */
  14. isSaved?: boolean;
  15. /**
  16. * Display the saving state
  17. */
  18. isSaving?: boolean;
  19. }
  20. /**
  21. * ControlState (i.e. loading/error icons) for form fields
  22. */
  23. const ControlState = ({isSaving, isSaved, error}: ControlStateProps) => (
  24. <Fragment>
  25. {isSaving ? (
  26. <ControlStateWrapper>
  27. <FormSpinner data-test-id="saving" />
  28. </ControlStateWrapper>
  29. ) : isSaved ? (
  30. <ControlStateWrapper>
  31. <FieldIsSaved>
  32. <IconCheckmark size="18px" />
  33. </FieldIsSaved>
  34. </ControlStateWrapper>
  35. ) : null}
  36. {error ? (
  37. <ControlStateWrapper>
  38. <FieldError>
  39. <IconWarning size="18px" />
  40. </FieldError>
  41. </ControlStateWrapper>
  42. ) : null}
  43. </Fragment>
  44. );
  45. const ControlStateWrapper = styled('div')`
  46. line-height: 0;
  47. padding: 0 8px;
  48. `;
  49. const FieldIsSaved = styled('div')`
  50. color: ${p => p.theme.green300};
  51. animation: ${fadeOut} 0.3s ease 2s 1 forwards;
  52. position: absolute;
  53. top: 0;
  54. bottom: 0;
  55. left: 0;
  56. right: 0;
  57. display: flex;
  58. align-items: center;
  59. justify-content: center;
  60. `;
  61. const FormSpinner = styled(Spinner)`
  62. margin-left: 0;
  63. `;
  64. const FieldError = styled('div')`
  65. color: ${p => p.theme.red300};
  66. animation: ${() => pulse(1.15)} 1s ease infinite;
  67. `;
  68. export default ControlState;