@liveblocks/react-ui
Version:
A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.
582 lines (577 loc) • 21.1 kB
JavaScript
"use client";
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var core = require('@liveblocks/core');
var react$1 = require('@liveblocks/react');
var _private = require('@liveblocks/react/_private');
var reactSlot = require('@radix-ui/react-slot');
var TooltipPrimitive = require('@radix-ui/react-tooltip');
var react = require('react');
var components = require('../components.cjs');
var Bell = require('../icons/Bell.cjs');
var BellCrossed = require('../icons/BellCrossed.cjs');
var Check = require('../icons/Check.cjs');
var Delete = require('../icons/Delete.cjs');
var Ellipsis = require('../icons/Ellipsis.cjs');
var Warning = require('../icons/Warning.cjs');
var overrides = require('../overrides.cjs');
var Timestamp = require('../primitives/Timestamp.cjs');
var shared = require('../shared.cjs');
var cn = require('../utils/cn.cjs');
var Avatar = require('./internal/Avatar.cjs');
var Button = require('./internal/Button.cjs');
var CodeBlock = require('./internal/CodeBlock.cjs');
var Dropdown = require('./internal/Dropdown.cjs');
var InboxNotificationThread$1 = require('./internal/InboxNotificationThread.cjs');
var List = require('./internal/List.cjs');
var Room = require('./internal/Room.cjs');
var Tooltip = require('./internal/Tooltip.cjs');
var User = require('./internal/User.cjs');
var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
const InboxNotificationLayout = react.forwardRef(
({
inboxNotification,
children,
aside,
title,
date,
unread,
markAsReadOnClick,
onClick,
href,
showActions,
overrides: overrides$1,
components: components$1,
className,
asChild,
additionalDropdownItemsBefore,
additionalDropdownItemsAfter,
...props
}, forwardedRef) => {
const $ = overrides.useOverrides(overrides$1);
const { Anchor } = components.useComponents(components$1);
const Component = asChild ? reactSlot.Slot : Anchor;
const [isMoreActionOpen, setMoreActionOpen] = react.useState(false);
const markInboxNotificationAsRead = react$1.useMarkInboxNotificationAsRead();
const deleteInboxNotification = react$1.useDeleteInboxNotification();
const handleClick = react.useCallback(
(event) => {
onClick?.(event);
const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href);
if (unread && shouldMarkAsReadOnClick) {
markInboxNotificationAsRead(inboxNotification.id);
}
},
[
href,
inboxNotification.id,
markAsReadOnClick,
markInboxNotificationAsRead,
onClick,
unread
]
);
const stopPropagation = react.useCallback((event) => {
event.stopPropagation();
}, []);
const preventDefaultAndStopPropagation = react.useCallback(
(event) => {
event.preventDefault();
event.stopPropagation();
},
[]
);
const handleMoreClick = react.useCallback((event) => {
event.preventDefault();
event.stopPropagation();
setMoreActionOpen((open) => !open);
}, []);
const handleMarkAsRead = react.useCallback(() => {
markInboxNotificationAsRead(inboxNotification.id);
}, [inboxNotification.id, markInboxNotificationAsRead]);
const handleDelete = react.useCallback(() => {
deleteInboxNotification(inboxNotification.id);
}, [inboxNotification.id, deleteInboxNotification]);
return /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive.TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
Component,
{
className: cn.cn(
"lb-root lb-inbox-notification",
showActions === "hover" && "lb-inbox-notification:show-actions-hover",
isMoreActionOpen && "lb-inbox-notification:action-open",
className
),
dir: $.dir,
"data-unread": unread ? "" : void 0,
"data-kind": inboxNotification.kind,
onClick: handleClick,
href,
...props,
ref: forwardedRef,
children: [
aside && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-aside", children: aside }),
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-inbox-notification-content", children: [
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-inbox-notification-header", children: [
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-inbox-notification-title", children: title }),
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-details", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lb-inbox-notification-details-labels", children: [
/* @__PURE__ */ jsxRuntime.jsx(
Timestamp.Timestamp,
{
locale: $.locale,
date,
className: "lb-date lb-inbox-notification-date"
}
),
unread && /* @__PURE__ */ jsxRuntime.jsx(
"span",
{
className: "lb-inbox-notification-unread-indicator",
role: "presentation"
}
)
] }) }),
showActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-actions", children: /* @__PURE__ */ jsxRuntime.jsx(
Dropdown.Dropdown,
{
open: isMoreActionOpen,
onOpenChange: setMoreActionOpen,
align: "end",
content: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
additionalDropdownItemsBefore,
unread ? /* @__PURE__ */ jsxRuntime.jsx(
Dropdown.DropdownItem,
{
onSelect: handleMarkAsRead,
onClick: stopPropagation,
icon: /* @__PURE__ */ jsxRuntime.jsx(Check.CheckIcon, {}),
children: $.INBOX_NOTIFICATION_MARK_AS_READ
}
) : null,
/* @__PURE__ */ jsxRuntime.jsx(
Dropdown.DropdownItem,
{
onSelect: handleDelete,
onClick: stopPropagation,
icon: /* @__PURE__ */ jsxRuntime.jsx(Delete.DeleteIcon, {}),
children: $.INBOX_NOTIFICATION_DELETE
}
),
additionalDropdownItemsAfter
] }),
children: /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { content: $.INBOX_NOTIFICATION_MORE, children: /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
Button.Button,
{
className: "lb-inbox-notification-action",
onClick: handleMoreClick,
onPointerDown: preventDefaultAndStopPropagation,
onPointerUp: preventDefaultAndStopPropagation,
"aria-label": $.INBOX_NOTIFICATION_MORE,
icon: /* @__PURE__ */ jsxRuntime.jsx(Ellipsis.EllipsisIcon, {})
}
) }) })
}
) })
] }),
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-body", children })
] })
]
}
) });
}
);
function InboxNotificationIcon({
className,
...props
}) {
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn.cn("lb-inbox-notification-icon", className), ...props });
}
function InboxNotificationAvatar({
className,
...props
}) {
return /* @__PURE__ */ jsxRuntime.jsx(
Avatar.Avatar,
{
className: cn.cn("lb-inbox-notification-avatar", className),
...props
}
);
}
const InboxNotificationThread = react.forwardRef(
({
inboxNotification,
href,
showRoomName = true,
showReactions = true,
showAttachments = true,
showActions = "hover",
overrides: overrides$1,
...props
}, forwardedRef) => {
const $ = overrides.useOverrides(overrides$1);
const client = react$1.useClient();
const thread = react$1.useInboxNotificationThread(inboxNotification.id);
const {
status: subscriptionStatus,
subscribe,
unsubscribe
} = _private.useRoomThreadSubscription(thread.roomId, thread.id);
const currentUserId = shared.useCurrentUserId();
const { info } = react$1.useRoomInfo(inboxNotification.roomId);
const contents = react.useMemo(() => {
const contents2 = InboxNotificationThread$1.generateInboxNotificationThreadContents(
client,
inboxNotification,
thread,
currentUserId ?? ""
);
if (contents2.comments.length === 0 || contents2.userIds.length === 0) {
return null;
}
switch (contents2.type) {
case "comments": {
const reversedUserIds = [...contents2.userIds].reverse();
const firstUserId = reversedUserIds[0];
const aside2 = /* @__PURE__ */ jsxRuntime.jsx(InboxNotificationAvatar, { userId: firstUserId });
const title2 = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST(
/* @__PURE__ */ jsxRuntime.jsx(
List.List,
{
values: reversedUserIds.map((userId) => /* @__PURE__ */ jsxRuntime.jsx(User.User, { userId, replaceSelf: true }, userId)),
formatRemaining: $.LIST_REMAINING_USERS,
truncate: InboxNotificationThread$1.INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1,
locale: $.locale
}
),
showRoomName ? /* @__PURE__ */ jsxRuntime.jsx(Room.Room, { roomId: thread.roomId }) : void 0,
reversedUserIds.length
);
const content2 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-comments", children: contents2.comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationThread$1.InboxNotificationComment,
{
comment,
showHeader: contents2.comments.length > 1,
showAttachments,
showReactions,
overrides: overrides$1
},
comment.id
)) });
return {
unread: contents2.unread,
date: contents2.date,
aside: aside2,
title: title2,
content: content2,
threadId: thread.id,
commentId: contents2.comments[contents2.comments.length - 1].id
};
}
case "mention": {
const mentionCreatedBy = contents2.userIds[0];
const mentionComment = contents2.comments[0];
const aside2 = /* @__PURE__ */ jsxRuntime.jsx(InboxNotificationAvatar, { userId: mentionCreatedBy });
const title2 = $.INBOX_NOTIFICATION_THREAD_MENTION(
/* @__PURE__ */ jsxRuntime.jsx(User.User, { userId: mentionCreatedBy }, mentionCreatedBy),
showRoomName ? /* @__PURE__ */ jsxRuntime.jsx(Room.Room, { roomId: thread.roomId }) : void 0
);
const content2 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-inbox-notification-comments", children: /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationThread$1.InboxNotificationComment,
{
comment: mentionComment,
showHeader: false,
showAttachments,
showReactions,
overrides: overrides$1
},
mentionComment.id
) });
return {
unread: contents2.unread,
date: contents2.date,
aside: aside2,
title: title2,
content: content2,
threadId: thread.id,
commentId: mentionComment.id
};
}
default:
return core.assertNever(
contents2,
"Unexpected thread inbox notification type"
);
}
}, [
$,
client,
currentUserId,
inboxNotification,
overrides$1,
showRoomName,
showAttachments,
showReactions,
thread
]);
const resolvedHref = react.useMemo(() => {
const resolvedHref2 = href ?? info?.url;
return resolvedHref2 ? (
// Set the comment ID as the URL hash.
core.generateUrl(resolvedHref2, void 0, contents?.commentId)
) : void 0;
}, [contents?.commentId, href, info?.url]);
const handleSubscribeChange = react.useCallback(() => {
if (subscriptionStatus === "subscribed") {
unsubscribe();
} else {
subscribe();
}
}, [subscriptionStatus, subscribe, unsubscribe]);
const stopPropagation = react.useCallback((event) => {
event.stopPropagation();
}, []);
if (!contents) {
return null;
}
const { aside, title, content, date, unread } = contents;
return /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationLayout,
{
inboxNotification,
aside,
title,
date,
unread,
overrides: overrides$1,
href: resolvedHref,
showActions,
markAsReadOnClick: false,
additionalDropdownItemsBefore: /* @__PURE__ */ jsxRuntime.jsx(
Dropdown.DropdownItem,
{
onSelect: handleSubscribeChange,
onClick: stopPropagation,
icon: subscriptionStatus === "subscribed" ? /* @__PURE__ */ jsxRuntime.jsx(BellCrossed.BellCrossedIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(Bell.BellIcon, {}),
children: subscriptionStatus === "subscribed" ? $.THREAD_UNSUBSCRIBE : $.THREAD_SUBSCRIBE
}
),
...props,
ref: forwardedRef,
children: content
}
);
}
);
const InboxNotificationTextMention = react.forwardRef(
({
inboxNotification,
showActions = "hover",
showRoomName = true,
href,
overrides: overrides$1,
...props
}, ref) => {
const $ = overrides.useOverrides(overrides$1);
const { info } = react$1.useRoomInfo(inboxNotification.roomId);
const resolvedHref = react.useMemo(() => {
const resolvedHref2 = href ?? info?.url;
return resolvedHref2 ? core.sanitizeUrl(resolvedHref2) ?? void 0 : void 0;
}, [href, info?.url]);
const unread = react.useMemo(() => {
return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt;
}, [inboxNotification.notifiedAt, inboxNotification.readAt]);
return /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationLayout,
{
inboxNotification,
aside: /* @__PURE__ */ jsxRuntime.jsx(InboxNotificationAvatar, { userId: inboxNotification.createdBy }),
title: $.INBOX_NOTIFICATION_TEXT_MENTION(
/* @__PURE__ */ jsxRuntime.jsx(
User.User,
{
userId: inboxNotification.createdBy
},
inboxNotification.createdBy
),
showRoomName ? /* @__PURE__ */ jsxRuntime.jsx(Room.Room, { roomId: inboxNotification.roomId }) : void 0
),
date: inboxNotification.notifiedAt,
unread,
overrides: overrides$1,
showActions,
href: resolvedHref,
...props,
ref
}
);
}
);
const InboxNotificationCustom = react.forwardRef(
({
inboxNotification,
showActions = "hover",
title,
aside,
children,
overrides,
...props
}, forwardedRef) => {
const unread = react.useMemo(() => {
return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt;
}, [inboxNotification.notifiedAt, inboxNotification.readAt]);
return /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationLayout,
{
inboxNotification,
aside,
title,
date: inboxNotification.notifiedAt,
unread,
overrides,
showActions,
...props,
ref: forwardedRef,
children
}
);
}
);
const InboxNotificationInspector = react.forwardRef(
({ inboxNotification, showActions = "hover", overrides, ...props }, forwardedRef) => {
const unread = react.useMemo(() => {
return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt;
}, [inboxNotification.notifiedAt, inboxNotification.readAt]);
return /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationLayout,
{
inboxNotification,
title: /* @__PURE__ */ jsxRuntime.jsx("code", { children: inboxNotification.id }),
date: inboxNotification.notifiedAt,
unread,
overrides,
showActions,
...props,
ref: forwardedRef,
"data-inspector": "",
children: /* @__PURE__ */ jsxRuntime.jsx(
CodeBlock.CodeBlock,
{
title: "Data",
code: JSON.stringify(inboxNotification, null, 2)
}
)
}
);
}
);
const InboxNotificationCustomMissing = react.forwardRef(({ inboxNotification, ...props }, forwardedRef) => {
return /* @__PURE__ */ jsxRuntime.jsxs(
InboxNotificationCustom,
{
inboxNotification,
...props,
title: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
"Custom notification kind ",
/* @__PURE__ */ jsxRuntime.jsx("code", { children: inboxNotification.kind }),
" is not handled"
] }),
aside: /* @__PURE__ */ jsxRuntime.jsx(InboxNotificationIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(Warning.WarningIcon, {}) }),
ref: forwardedRef,
"data-missing": "",
children: [
"Notifications of this kind won\u2019t be displayed in production. Use the",
" ",
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "kinds" }),
" prop to define how they should be rendered, learn more in the console."
]
}
);
});
const InboxNotification = Object.assign(
react.forwardRef(
({ inboxNotification, kinds, ...props }, forwardedRef) => {
switch (inboxNotification.kind) {
case "thread": {
const ResolvedInboxNotificationThread = kinds?.thread ?? InboxNotificationThread;
return /* @__PURE__ */ jsxRuntime.jsx(
ResolvedInboxNotificationThread,
{
inboxNotification,
...props,
ref: forwardedRef
}
);
}
case "textMention": {
const ResolvedInboxNotificationTextMention = kinds?.textMention ?? InboxNotificationTextMention;
return /* @__PURE__ */ jsxRuntime.jsx(
ResolvedInboxNotificationTextMention,
{
inboxNotification,
...props,
ref: forwardedRef
}
);
}
default: {
const ResolvedInboxNotificationCustom = kinds?.[inboxNotification.kind];
if (!ResolvedInboxNotificationCustom) {
if (process.env.NODE_ENV !== "production") {
core.warnOnce(
`Custom notification kind "${inboxNotification.kind}" is not handled so notifications of this kind will not be displayed in production. Use the kinds prop to define how they should be rendered. Learn more: https://liveblocks.io/docs/api-reference/liveblocks-react-ui#Rendering-notification-kinds-differently.`
);
return /* @__PURE__ */ jsxRuntime.jsx(
InboxNotificationCustomMissing,
{
inboxNotification,
...props,
ref: forwardedRef
}
);
} else {
return null;
}
}
return /* @__PURE__ */ jsxRuntime.jsx(
ResolvedInboxNotificationCustom,
{
inboxNotification,
...props,
ref: forwardedRef
}
);
}
}
}
),
{
/**
* Displays a thread inbox notification kind.
*/
Thread: InboxNotificationThread,
/**
* Displays a text mention inbox notification kind.
*/
TextMention: InboxNotificationTextMention,
/**
* Displays a custom inbox notification kind.
*/
Custom: InboxNotificationCustom,
/**
* Display the inbox notification's data, which can be useful during development.
*
* @example
* <InboxNotification
* inboxNotification={inboxNotification}
* kinds={{
* $custom: InboxNotification.Inspector,
* }}
* />
*/
Inspector: InboxNotificationInspector,
Icon: InboxNotificationIcon,
Avatar: InboxNotificationAvatar
}
);
exports.InboxNotification = InboxNotification;
//# sourceMappingURL=InboxNotification.cjs.map