alert.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import * as React from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import classNames from 'classnames';
  5. import space from 'app/styles/space';
  6. import {Theme} from 'app/utils/theme';
  7. type Props = {
  8. type?: keyof Theme['alert'];
  9. icon?: React.ReactNode;
  10. system?: boolean;
  11. };
  12. type AlertProps = Omit<React.HTMLProps<HTMLDivElement>, keyof Props> & Props;
  13. type AlertThemeProps = {
  14. backgroundLight: string;
  15. border: string;
  16. iconColor: string;
  17. };
  18. const DEFAULT_TYPE = 'info';
  19. const IconWrapper = styled('span')`
  20. display: flex;
  21. margin-right: ${space(1)};
  22. /* Give the wrapper an explicit height so icons are line height with the
  23. * (common) line height. */
  24. height: 22px;
  25. align-items: center;
  26. `;
  27. const getAlertColorStyles = ({
  28. backgroundLight,
  29. border,
  30. iconColor,
  31. }: AlertThemeProps) => css`
  32. background: ${backgroundLight};
  33. border: 1px solid ${border};
  34. ${IconWrapper} {
  35. color: ${iconColor};
  36. }
  37. `;
  38. const getSystemAlertColorStyles = ({
  39. backgroundLight,
  40. border,
  41. iconColor,
  42. }: AlertThemeProps) => css`
  43. background: ${backgroundLight};
  44. border: 0;
  45. border-radius: 0;
  46. border-bottom: 1px solid ${border};
  47. ${IconWrapper} {
  48. color: ${iconColor};
  49. }
  50. `;
  51. const alertStyles = ({theme, type = DEFAULT_TYPE, system}: Props & {theme: Theme}) => css`
  52. display: flex;
  53. margin: 0 0 ${space(3)};
  54. padding: ${space(1.5)} ${space(2)};
  55. font-size: 15px;
  56. box-shadow: ${theme.dropShadowLight};
  57. border-radius: ${theme.borderRadius};
  58. background: ${theme.backgroundSecondary};
  59. border: 1px solid ${theme.border};
  60. a:not([role='button']) {
  61. color: ${theme.textColor};
  62. border-bottom: 1px dotted ${theme.textColor};
  63. }
  64. ${getAlertColorStyles(theme.alert[type])};
  65. ${system && getSystemAlertColorStyles(theme.alert[type])};
  66. `;
  67. const StyledTextBlock = styled('span')`
  68. line-height: 1.5;
  69. flex-grow: 1;
  70. position: relative;
  71. margin: auto;
  72. `;
  73. const Alert = styled(
  74. ({
  75. type,
  76. icon,
  77. children,
  78. className,
  79. system: _system, // don't forward to `div`
  80. ...props
  81. }: AlertProps) => {
  82. return (
  83. <div className={classNames(type ? `ref-${type}` : '', className)} {...props}>
  84. {icon && <IconWrapper>{icon}</IconWrapper>}
  85. <StyledTextBlock>{children}</StyledTextBlock>
  86. </div>
  87. );
  88. }
  89. )<AlertProps>`
  90. ${alertStyles}
  91. `;
  92. Alert.defaultProps = {
  93. type: DEFAULT_TYPE,
  94. };
  95. export {alertStyles};
  96. export default Alert;