mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-30 06:05:30 -05:00
7b47acc486
[web] BS5 notifications in projects and welcome pages GitOrigin-RevId: 25780bb64660ef41c41c007f94f70df273cac716
124 lines
2.8 KiB
TypeScript
124 lines
2.8 KiB
TypeScript
import classNames from 'classnames'
|
|
import React, { ReactElement, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import MaterialIcon from './material-icon'
|
|
|
|
export type NotificationType =
|
|
| 'info'
|
|
| 'success'
|
|
| 'warning'
|
|
| 'error'
|
|
| 'offer'
|
|
|
|
export type NotificationProps = {
|
|
action?: React.ReactElement
|
|
ariaLive?: 'polite' | 'off' | 'assertive'
|
|
className?: string
|
|
content: React.ReactNode
|
|
customIcon?: React.ReactElement | null
|
|
disclaimer?: React.ReactElement | string
|
|
isDismissible?: boolean
|
|
isActionBelowContent?: boolean
|
|
onDismiss?: () => void
|
|
title?: string
|
|
type: NotificationType
|
|
id?: string
|
|
}
|
|
|
|
function NotificationIcon({
|
|
notificationType,
|
|
customIcon,
|
|
}: {
|
|
notificationType: NotificationType
|
|
customIcon?: ReactElement
|
|
}) {
|
|
let icon = <MaterialIcon type="info" />
|
|
|
|
if (customIcon) {
|
|
icon = customIcon
|
|
} else if (notificationType === 'success') {
|
|
icon = <MaterialIcon type="check_circle" />
|
|
} else if (notificationType === 'warning') {
|
|
icon = <MaterialIcon type="warning" />
|
|
} else if (notificationType === 'error') {
|
|
icon = <MaterialIcon type="error" />
|
|
} else if (notificationType === 'offer') {
|
|
icon = <MaterialIcon type="campaign" />
|
|
}
|
|
return <div className="notification-icon">{icon}</div>
|
|
}
|
|
|
|
function Notification({
|
|
action,
|
|
ariaLive,
|
|
className = '',
|
|
content,
|
|
customIcon,
|
|
disclaimer,
|
|
isActionBelowContent,
|
|
isDismissible,
|
|
onDismiss,
|
|
title,
|
|
type,
|
|
id,
|
|
}: NotificationProps) {
|
|
type = type || 'info'
|
|
const { t } = useTranslation()
|
|
const [show, setShow] = useState(true)
|
|
|
|
const notificationClassName = classNames(
|
|
'notification',
|
|
`notification-type-${type}`,
|
|
isActionBelowContent ? 'notification-cta-below-content' : '',
|
|
className
|
|
)
|
|
|
|
const handleDismiss = () => {
|
|
setShow(false)
|
|
if (onDismiss) onDismiss()
|
|
}
|
|
|
|
// return null
|
|
|
|
if (!show) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={notificationClassName}
|
|
aria-live={ariaLive || 'off'}
|
|
role="alert"
|
|
id={id}
|
|
>
|
|
{customIcon !== null && (
|
|
<NotificationIcon notificationType={type} customIcon={customIcon} />
|
|
)}
|
|
|
|
<div className="notification-content-and-cta">
|
|
<div className="notification-content">
|
|
{title && (
|
|
<p>
|
|
<b>{title}</b>
|
|
</p>
|
|
)}
|
|
{content}
|
|
</div>
|
|
{action && <div className="notification-cta">{action}</div>}
|
|
{disclaimer && (
|
|
<div className="notification-disclaimer">{disclaimer}</div>
|
|
)}
|
|
</div>
|
|
|
|
{isDismissible && (
|
|
<div className="notification-close-btn">
|
|
<button aria-label={t('close')} onClick={handleDismiss}>
|
|
<MaterialIcon type="close" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Notification
|