alert.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import 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 getAlertColorStyles = ({
  20. backgroundLight,
  21. border,
  22. iconColor,
  23. }: AlertThemeProps) => css`
  24. background: ${backgroundLight};
  25. border: 1px solid ${border};
  26. svg {
  27. color: ${iconColor};
  28. }
  29. `;
  30. const getSystemAlertColorStyles = ({
  31. backgroundLight,
  32. border,
  33. iconColor,
  34. }: AlertThemeProps) => css`
  35. background: ${backgroundLight};
  36. border: 0;
  37. border-radius: 0;
  38. border-bottom: 1px solid ${border};
  39. svg {
  40. color: ${iconColor};
  41. }
  42. `;
  43. const alertStyles = ({theme, type = DEFAULT_TYPE, system}: Props & {theme: Theme}) => css`
  44. display: flex;
  45. margin: 0 0 ${space(3)};
  46. padding: ${space(1.5)} ${space(2)};
  47. font-size: 15px;
  48. box-shadow: ${theme.dropShadowLight};
  49. border-radius: ${theme.borderRadius};
  50. background: ${theme.backgroundSecondary};
  51. border: 1px solid ${theme.border};
  52. a:not([role='button']) {
  53. color: ${theme.textColor};
  54. border-bottom: 1px dotted ${theme.textColor};
  55. }
  56. ${getAlertColorStyles(theme.alert[type])};
  57. ${system && getSystemAlertColorStyles(theme.alert[type])};
  58. `;
  59. const IconWrapper = styled('span')`
  60. display: flex;
  61. margin-right: ${space(1)};
  62. /* Give the wrapper an explicit height so icons are line height with the
  63. * (common) line height. */
  64. height: 22px;
  65. align-items: center;
  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;