UNPKG

@magicbell/magicbell-react

Version:

React components for building a notification inbox for your app

73 lines 3.73 kB
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime"; /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; import { useNotification } from '@magicbell/react-headless'; import { useTheme } from '../../context/MagicBellThemeContext.js'; import NotificationContent from '../NotificationContent/index.js'; import NotificationMenu from '../NotificationMenu/index.js'; import NotificationState from '../NotificationState/index.js'; import Timestamp from '../Timestamp/index.js'; import { openActionUrl } from './eventHandlers.js'; import NotificationTitle from './NotificationTitle.js'; import StyledContainer from './StyledContainer.js'; const actionableTags = new Set(['A', 'BUTTON', 'INPUT', 'TEXTAREA', 'SELECT']); function isActionableElement(event) { let el = event.target; while (el && el !== event.currentTarget) { if (actionableTags.has(el.tagName.toUpperCase())) { const isMenu = el.dataset.magicbellTarget === 'notification-menu'; return [true, isMenu]; } el = el.parentElement; } return [false, false]; } /** * Component that renders a notification. When the notification is clicked the * `onClick` callback is called and the notification is marked as read. * * @example * <ClickableNotification notification={notification} onClick={openActionUrl} /> */ export default function ClickableNotification({ notification: rawNotification, onClick, prose }) { const { notification: { default: theme }, } = useTheme(); const notification = useNotification(rawNotification); const handleClick = (event) => { // ignore event when user clicks inside the menu if (event.isDefaultPrevented()) return; const [isAction, isMenu] = isActionableElement(event); let markAsReadPromise; // don't mark as read when the user clicks the menu if (!isMenu) { markAsReadPromise = notification.markAsRead(); } // We don't want to invoke the action url when the user clicks a link or button inside the notification. // Notification content should take precedence. if (isAction) return; const onClickResult = onClick?.(notification); if (onClickResult === false) return; if (notification.actionUrl) { // We wait for the markAsRead before navigating to the new url, to prevent race conditions // between mark and read, and fetching new data on page reload. But let's not wait forever. const timeout = new Promise((resolve) => setTimeout(resolve, 1_000)); Promise.race([markAsReadPromise, timeout]).then(() => openActionUrl(notification)); } }; const content = css ` margin: 0 8px !important; width: 100%; `; const actions = css ` display: flex; padding: 0 5px !important; flex-direction: column; align-items: flex-end; margin-left: auto !important; font-size: ${theme.fontSize} !important; `; return (_jsxs(StyledContainer, { role: "button", "aria-labelledby": `magicbell-notification-title-${notification.id}`, notification: notification, onClick: handleClick, children: [_jsx(NotificationState, { notification: notification }), _jsxs("div", { css: content, children: [_jsx(NotificationTitle, { notification: notification }), _jsx(NotificationContent, { notification: notification, prose: prose })] }), _jsxs("div", { css: actions, children: [notification.sentAt ? _jsx(Timestamp, { date: notification.sentAt, tooltipPlacement: "left" }) : _jsx("div", {}), _jsx(NotificationMenu, { notification: notification })] })] })); } //# sourceMappingURL=ClickableNotification.js.map