notification.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import classNames from 'classnames'
  2. import React, { ReactElement, useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import MaterialIcon from './material-icon'
  5. type NotificationType = 'info' | 'success' | 'warning' | 'error'
  6. type NotificationProps = {
  7. action?: React.ReactElement
  8. ariaLive?: 'polite' | 'off' | 'assertive'
  9. content: React.ReactElement
  10. customIcon?: React.ReactElement
  11. isDismissible?: boolean
  12. isActionBelowContent?: boolean
  13. onDismiss?: () => void
  14. title?: string
  15. type: NotificationType
  16. }
  17. function NotificationIcon({
  18. notificationType,
  19. customIcon,
  20. }: {
  21. notificationType: NotificationType
  22. customIcon?: ReactElement
  23. }) {
  24. let icon = <MaterialIcon type="info" />
  25. if (customIcon) {
  26. icon = customIcon
  27. } else if (notificationType === 'success') {
  28. icon = <MaterialIcon type="check_circle" />
  29. } else if (notificationType === 'warning') {
  30. icon = <MaterialIcon type="warning" />
  31. } else if (notificationType === 'error') {
  32. icon = <MaterialIcon type="error" />
  33. }
  34. return <div className="notification-icon">{icon}</div>
  35. }
  36. function Notification({
  37. action,
  38. ariaLive,
  39. content,
  40. customIcon,
  41. isActionBelowContent,
  42. isDismissible,
  43. onDismiss,
  44. title,
  45. type,
  46. }: NotificationProps) {
  47. type = type || 'info'
  48. const { t } = useTranslation()
  49. const [show, setShow] = useState(true)
  50. const notificationClassName = classNames(
  51. 'notification',
  52. `notification-type-${type}`,
  53. isDismissible ? 'notification-dismissible' : '',
  54. isActionBelowContent ? 'notification-cta-below-content' : ''
  55. )
  56. const handleDismiss = () => {
  57. setShow(false)
  58. if (onDismiss) onDismiss()
  59. }
  60. if (!show) {
  61. return null
  62. }
  63. return (
  64. <div
  65. className={notificationClassName}
  66. aria-live={ariaLive || 'off'}
  67. role="alert"
  68. >
  69. <NotificationIcon notificationType={type} customIcon={customIcon} />
  70. <div className="notification-content-and-cta">
  71. <div className="notification-content">
  72. {title && (
  73. <p>
  74. <b>{title}</b>
  75. </p>
  76. )}
  77. {content}
  78. </div>
  79. {action && <div className="notification-cta">{action}</div>}
  80. </div>
  81. {isDismissible && (
  82. <div className="notification-close-btn">
  83. <button aria-label={t('close')} onClick={handleDismiss}>
  84. <MaterialIcon type="close" />
  85. </button>
  86. </div>
  87. )}
  88. </div>
  89. )
  90. }
  91. export default Notification