notification.tsx 2.6 KB

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