components.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import styled from '@emotion/styled';
  2. import Button, {ButtonPropsWithAriaLabel} from 'sentry/components/button';
  3. import {IconClose} from 'sentry/icons/iconClose';
  4. import {t} from 'sentry/locale';
  5. import space from 'sentry/styles/space';
  6. const ModalHeader = styled('header')`
  7. position: relative;
  8. border-bottom: 1px solid ${p => p.theme.border};
  9. padding: ${space(3)} ${space(3)};
  10. margin: -${space(4)} -${space(2)} ${space(3)} -${space(3)};
  11. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  12. padding: ${space(3)} ${space(4)};
  13. margin: -${space(4)} -${space(4)} ${space(3)} -${space(4)};
  14. }
  15. h1,
  16. h2,
  17. h3,
  18. h4,
  19. h5,
  20. h6 {
  21. font-size: 20px;
  22. font-weight: 600;
  23. margin-bottom: 0;
  24. line-height: 1.1;
  25. }
  26. `;
  27. const CloseButton = styled((p: ButtonPropsWithAriaLabel) => (
  28. <Button size="zero" icon={<IconClose size="10px" />} {...p} />
  29. ))`
  30. position: absolute;
  31. top: 0;
  32. right: 0;
  33. transform: translate(50%, -50%);
  34. border-radius: 50%;
  35. border: none;
  36. box-shadow: 0 0 0 1px ${p => p.theme.translucentBorder};
  37. background: ${p => p.theme.background};
  38. height: 24px;
  39. width: 24px;
  40. `;
  41. const ModalBody = styled('section')`
  42. font-size: ${p => p.theme.fontSizeMedium};
  43. p:last-child {
  44. margin-bottom: 0;
  45. }
  46. img {
  47. max-width: 100%;
  48. }
  49. `;
  50. const ModalFooter = styled('footer')`
  51. border-top: 1px solid ${p => p.theme.border};
  52. display: flex;
  53. justify-content: flex-end;
  54. padding: ${space(3)} ${space(2)};
  55. margin: ${space(3)} -${space(3)} -${space(4)};
  56. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  57. padding: ${space(3)} ${space(4)};
  58. margin: ${space(3)} -${space(4)} -${space(4)};
  59. }
  60. `;
  61. interface ClosableHeaderProps extends React.HTMLAttributes<HTMLHeadingElement> {
  62. /**
  63. * Show a close button in the header
  64. */
  65. closeButton?: boolean;
  66. }
  67. /**
  68. * Creates a ModalHeader that includes props to enable the close button
  69. */
  70. const makeClosableHeader = (closeModal: () => void) => {
  71. const ClosableHeader: React.FC<ClosableHeaderProps> = ({
  72. closeButton,
  73. children,
  74. ...props
  75. }) => (
  76. <ModalHeader {...props}>
  77. {children}
  78. {closeButton ? (
  79. <CloseButton aria-label={t('Close Modal')} onClick={closeModal} />
  80. ) : null}
  81. </ModalHeader>
  82. );
  83. ClosableHeader.displayName = 'Header';
  84. return ClosableHeader;
  85. };
  86. /**
  87. * Creates a CloseButton component that is connected to the provided closeModal trigger
  88. */
  89. const makeCloseButton =
  90. (closeModal: () => void): React.FC<Omit<ButtonPropsWithAriaLabel, 'aria-label'>> =>
  91. props =>
  92. <CloseButton {...props} aria-label={t('Close Modal')} onClick={closeModal} />;
  93. export {makeClosableHeader, makeCloseButton, ModalBody, ModalFooter};