@liveblocks/react-ui 3.15.0-thread2 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +6 -16
  2. package/dist/_private/index.cjs +1 -5
  3. package/dist/_private/index.cjs.map +1 -1
  4. package/dist/_private/index.d.cts +4 -4
  5. package/dist/_private/index.d.ts +4 -4
  6. package/dist/_private/index.js +1 -2
  7. package/dist/_private/index.js.map +1 -1
  8. package/dist/components/AvatarStack.cjs +117 -0
  9. package/dist/components/AvatarStack.cjs.map +1 -0
  10. package/dist/components/AvatarStack.js +115 -0
  11. package/dist/components/AvatarStack.js.map +1 -0
  12. package/dist/components/Comment.cjs +10 -28
  13. package/dist/components/Comment.cjs.map +1 -1
  14. package/dist/components/Comment.js +12 -11
  15. package/dist/components/Comment.js.map +1 -1
  16. package/dist/components/CommentPin.cjs +36 -0
  17. package/dist/components/CommentPin.cjs.map +1 -0
  18. package/dist/components/CommentPin.js +34 -0
  19. package/dist/components/CommentPin.js.map +1 -0
  20. package/dist/components/Composer.cjs +2 -4
  21. package/dist/components/Composer.cjs.map +1 -1
  22. package/dist/components/Composer.js +3 -5
  23. package/dist/components/Composer.js.map +1 -1
  24. package/dist/components/Cursor.cjs +40 -0
  25. package/dist/components/Cursor.cjs.map +1 -0
  26. package/dist/components/Cursor.js +38 -0
  27. package/dist/components/Cursor.js.map +1 -0
  28. package/dist/components/Cursors.cjs +256 -0
  29. package/dist/components/Cursors.cjs.map +1 -0
  30. package/dist/components/Cursors.js +254 -0
  31. package/dist/components/Cursors.js.map +1 -0
  32. package/dist/components/FloatingComposer.cjs +82 -0
  33. package/dist/components/FloatingComposer.cjs.map +1 -0
  34. package/dist/components/FloatingComposer.js +80 -0
  35. package/dist/components/FloatingComposer.js.map +1 -0
  36. package/dist/components/FloatingThread.cjs +82 -0
  37. package/dist/components/FloatingThread.cjs.map +1 -0
  38. package/dist/components/FloatingThread.js +80 -0
  39. package/dist/components/FloatingThread.js.map +1 -0
  40. package/dist/components/InboxNotification.cjs +4 -6
  41. package/dist/components/InboxNotification.cjs.map +1 -1
  42. package/dist/components/InboxNotification.js +5 -7
  43. package/dist/components/InboxNotification.js.map +1 -1
  44. package/dist/components/Thread.cjs +19 -28
  45. package/dist/components/Thread.cjs.map +1 -1
  46. package/dist/components/Thread.js +19 -9
  47. package/dist/components/Thread.js.map +1 -1
  48. package/dist/components/internal/AiComposer.cjs +1 -2
  49. package/dist/components/internal/AiComposer.cjs.map +1 -1
  50. package/dist/components/internal/AiComposer.js +1 -2
  51. package/dist/components/internal/AiComposer.js.map +1 -1
  52. package/dist/components/internal/Avatar.cjs +10 -13
  53. package/dist/components/internal/Avatar.cjs.map +1 -1
  54. package/dist/components/internal/Avatar.js +11 -14
  55. package/dist/components/internal/Avatar.js.map +1 -1
  56. package/dist/components/internal/CodeBlock.cjs +1 -2
  57. package/dist/components/internal/CodeBlock.cjs.map +1 -1
  58. package/dist/components/internal/CodeBlock.js +1 -2
  59. package/dist/components/internal/CodeBlock.js.map +1 -1
  60. package/dist/components/internal/Dropdown.cjs +7 -28
  61. package/dist/components/internal/Dropdown.cjs.map +1 -1
  62. package/dist/components/internal/Dropdown.js +7 -7
  63. package/dist/components/internal/Dropdown.js.map +1 -1
  64. package/dist/components/internal/EmojiPicker.cjs +6 -27
  65. package/dist/components/internal/EmojiPicker.cjs.map +1 -1
  66. package/dist/components/internal/EmojiPicker.js +6 -6
  67. package/dist/components/internal/EmojiPicker.js.map +1 -1
  68. package/dist/components/internal/List.cjs +2 -2
  69. package/dist/components/internal/List.cjs.map +1 -1
  70. package/dist/components/internal/List.js +2 -2
  71. package/dist/components/internal/List.js.map +1 -1
  72. package/dist/components/internal/Tooltip.cjs +7 -28
  73. package/dist/components/internal/Tooltip.cjs.map +1 -1
  74. package/dist/components/internal/Tooltip.js +7 -7
  75. package/dist/components/internal/Tooltip.js.map +1 -1
  76. package/dist/index.cjs +12 -0
  77. package/dist/index.cjs.map +1 -1
  78. package/dist/index.d.cts +232 -137
  79. package/dist/index.d.ts +232 -137
  80. package/dist/index.js +6 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/primitives/AiComposer/index.cjs +5 -4
  83. package/dist/primitives/AiComposer/index.cjs.map +1 -1
  84. package/dist/primitives/AiComposer/index.js +5 -4
  85. package/dist/primitives/AiComposer/index.js.map +1 -1
  86. package/dist/primitives/AiMessage/index.cjs +2 -2
  87. package/dist/primitives/AiMessage/index.cjs.map +1 -1
  88. package/dist/primitives/AiMessage/index.js +2 -2
  89. package/dist/primitives/AiMessage/index.js.map +1 -1
  90. package/dist/primitives/Collapsible/index.cjs +4 -4
  91. package/dist/primitives/Collapsible/index.cjs.map +1 -1
  92. package/dist/primitives/Collapsible/index.js +4 -4
  93. package/dist/primitives/Collapsible/index.js.map +1 -1
  94. package/dist/primitives/Comment/index.cjs +4 -4
  95. package/dist/primitives/Comment/index.cjs.map +1 -1
  96. package/dist/primitives/Comment/index.js +4 -4
  97. package/dist/primitives/Comment/index.js.map +1 -1
  98. package/dist/primitives/Composer/index.cjs +23 -35
  99. package/dist/primitives/Composer/index.cjs.map +1 -1
  100. package/dist/primitives/Composer/index.js +23 -16
  101. package/dist/primitives/Composer/index.js.map +1 -1
  102. package/dist/primitives/Duration.cjs +2 -2
  103. package/dist/primitives/Duration.cjs.map +1 -1
  104. package/dist/primitives/Duration.js +2 -2
  105. package/dist/primitives/Duration.js.map +1 -1
  106. package/dist/primitives/FileSize.cjs +2 -2
  107. package/dist/primitives/FileSize.cjs.map +1 -1
  108. package/dist/primitives/FileSize.js +2 -2
  109. package/dist/primitives/FileSize.js.map +1 -1
  110. package/dist/primitives/Markdown.cjs +2 -2
  111. package/dist/primitives/Markdown.cjs.map +1 -1
  112. package/dist/primitives/Markdown.js +2 -2
  113. package/dist/primitives/Markdown.js.map +1 -1
  114. package/dist/primitives/Timestamp.cjs +2 -2
  115. package/dist/primitives/Timestamp.cjs.map +1 -1
  116. package/dist/primitives/Timestamp.js +2 -2
  117. package/dist/primitives/Timestamp.js.map +1 -1
  118. package/dist/utils/Portal.cjs +2 -2
  119. package/dist/utils/Portal.cjs.map +1 -1
  120. package/dist/utils/Portal.js +2 -2
  121. package/dist/utils/Portal.js.map +1 -1
  122. package/dist/utils/animation-loop.cjs +44 -0
  123. package/dist/utils/animation-loop.cjs.map +1 -0
  124. package/dist/utils/animation-loop.js +42 -0
  125. package/dist/utils/animation-loop.js.map +1 -0
  126. package/dist/utils/use-pre-resolve-user.cjs +18 -0
  127. package/dist/utils/use-pre-resolve-user.cjs.map +1 -0
  128. package/dist/utils/use-pre-resolve-user.js +16 -0
  129. package/dist/utils/use-pre-resolve-user.js.map +1 -0
  130. package/dist/version.cjs +1 -1
  131. package/dist/version.cjs.map +1 -1
  132. package/dist/version.js +1 -1
  133. package/dist/version.js.map +1 -1
  134. package/package.json +7 -10
  135. package/src/styles/dark/index.css +1 -1
  136. package/src/styles/index.css +252 -4
  137. package/styles/dark/attributes.css +1 -1
  138. package/styles/dark/attributes.css.map +1 -1
  139. package/styles/dark/media-query.css +1 -1
  140. package/styles/dark/media-query.css.map +1 -1
  141. package/styles.css +1 -1
  142. package/styles.css.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Comment.cjs","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n assertNever,\n type BaseMetadata,\n type CommentAttachment,\n type CommentData,\n type CommentReaction as CommentReactionData,\n type DCM,\n type GroupMentionData,\n MENTION_CHARACTER,\n type MentionData,\n Permission,\n} from \"@liveblocks/core\";\nimport {\n useAddRoomCommentReaction,\n useDeleteRoomComment,\n useEditRoomComment,\n useRemoveRoomCommentReaction,\n useRoomAttachmentUrl,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n ComponentsProvider,\n type GlobalComponents,\n useComponents,\n} from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { CrossIcon } from \"../icons/Cross\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EditIcon } from \"../icons/Edit\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { EmojiPlusIcon } from \"../icons/EmojiPlus\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport type { ComposerSubmitComment } from \"../primitives\";\nimport * as CommentPrimitive from \"../primitives/Comment\";\nimport type {\n CommentBodyLinkProps,\n CommentBodyMentionProps,\n CommentLinkProps,\n CommentMentionProps as CommentPrimitiveMentionProps,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { CommentAttachmentArgs } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { download } from \"../utils/download\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { useRefs } from \"../utils/use-refs\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport {\n FileAttachment,\n MediaAttachment,\n separateMediaAttachments,\n} from \"./internal/Attachment\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button, CustomButton } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { List } from \"./internal/List\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps<CM extends BaseMetadata = DCM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The comment to display.\n */\n comment: CommentData<CM>;\n\n /**\n * The comment's avatar.\n * Can be combined with `Comment.Avatar` to easily follow default styles.\n */\n avatar?: ReactNode;\n\n /**\n * The comment's author.\n * Can be combined with `Comment.Author` to easily follow default styles.\n */\n author?: ReactNode;\n\n /**\n * The comment's date.\n * Can be combined with `Comment.Date` to easily follow default styles,\n * or the `Timestamp` primitive for more control.\n */\n date?: ReactNode;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n\n /**\n * Whether to show the comment if it was deleted. If set to `false`, it will render deleted comments as `null`.\n */\n showDeleted?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show the composer's formatting controls when editing the comment.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comment's content.\n */\n indentContent?: boolean;\n\n /**\n * Additional content to display below the comment's body.\n */\n additionalContent?: ReactNode;\n\n /**\n * The event handler called when the comment is edited.\n */\n onCommentEdit?: (comment: CommentData) => void;\n\n /**\n * The event handler called when the comment is deleted.\n */\n onCommentDelete?: (comment: CommentData) => void;\n\n /**\n * The event handler called when clicking on the author.\n */\n onAuthorClick?: (userId: string, event: MouseEvent<HTMLElement>) => void;\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: (\n mention: MentionData,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: (\n args: CommentAttachmentArgs,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * Add (or change) items to display in the comment's dropdown.\n */\n dropdownItems?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the comment's content.\n */\n children?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n actionsClassName?: string;\n\n /**\n * @internal\n */\n internalDropdownItems?: ReactNode;\n}\n\nexport interface CommentAvatarProps\n extends Omit<ComponentProps<\"div\">, \"children\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport interface CommentAuthorProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The user ID to display the author for.\n */\n userId: string;\n}\n\nexport interface CommentDateProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nfunction CommentAvatar(props: CommentAvatarProps) {\n return <Avatar {...props} />;\n}\n\nfunction CommentAuthor(props: CommentAuthorProps) {\n return <User {...props} />;\n}\n\nfunction CommentDate({ locale, date, className, ...props }: CommentDateProps) {\n return (\n <Timestamp\n locale={locale}\n date={date}\n className={cn(\"lb-date\", className)}\n {...(props as Omit<ComponentProps<\"time\">, \"children\" | \"title\">)}\n />\n );\n}\n\nexport interface CommentDropdownItemProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\"> {\n /**\n * An optional icon displayed in this dropdown item.\n */\n icon?: ReactNode;\n\n /**\n * The event handler called when the dropdown item is selected.\n */\n onSelect?: (event: Event) => void;\n}\n\ninterface CommentReactionButtonProps\n extends ComponentPropsWithoutRef<typeof Button> {\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ninterface CommentReactionProps extends ComponentPropsWithoutRef<\"button\"> {\n comment: CommentData;\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ntype CommentNonInteractiveReactionProps = Omit<CommentReactionProps, \"comment\">;\n\ninterface CommentAttachmentProps extends ComponentProps<typeof FileAttachment> {\n attachment: CommentAttachment;\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n}\n\ninterface CommentMentionProps\n extends CommentBodyMentionProps,\n CommentPrimitiveMentionProps {\n overrides?: CommentProps[\"overrides\"];\n}\n\nfunction CommentUserMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const currentId = useCurrentUserId();\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={mention.id === currentId ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nfunction CommentGroupMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={isMember ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentMention({ mention, ...props }: CommentMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <CommentUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <CommentGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n const { Anchor } = useComponents();\n\n return (\n <CommentPrimitive.Link\n className={cn(\"lb-comment-link\", className)}\n href={href}\n {...props}\n asChild\n >\n <Anchor {...props}>{children}</Anchor>\n </CommentPrimitive.Link>\n );\n}\n\nexport function CommentNonInteractiveLink({\n href: _href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <span className={cn(\"lb-comment-link\", className)} {...props}>\n {children}\n </span>\n );\n}\n\nconst CommentReactionButton = forwardRef<\n HTMLButtonElement,\n CommentReactionButtonProps\n>(({ reaction, overrides, className, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n return (\n <CustomButton\n className={cn(\"lb-comment-reaction\", className)}\n variant=\"outline\"\n aria-label={$.COMMENT_REACTION_DESCRIPTION(\n reaction.emoji,\n reaction.users.length\n )}\n {...props}\n ref={forwardedRef}\n >\n <Emoji className=\"lb-comment-reaction-emoji\" emoji={reaction.emoji} />\n <span className=\"lb-comment-reaction-count\">{reaction.users.length}</span>\n </CustomButton>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n const $ = useOverrides(overrides);\n const tooltipContent = useMemo(\n () => (\n <span>\n {$.COMMENT_REACTION_LIST(\n <List\n values={reaction.users.map((users) => (\n <User key={users.id} userId={users.id} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={REACTIONS_TRUNCATE}\n locale={$.locale}\n />,\n reaction.emoji,\n reaction.users.length\n )}\n </span>\n ),\n [$, reaction]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handlePressedChange = useCallback(\n (isPressed: boolean) => {\n if (isPressed) {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n } else {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n }\n },\n [addReaction, comment.threadId, comment.id, reaction.emoji, removeReaction]\n );\n\n return (\n <Tooltip\n content={tooltipContent}\n multiline\n className=\"lb-comment-reaction-tooltip\"\n >\n <TogglePrimitive.Root\n asChild\n pressed={isActive}\n onPressedChange={handlePressedChange}\n onClick={stopPropagation}\n disabled={disabled}\n ref={forwardedRef}\n >\n <CommentReactionButton\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n );\n});\n\nexport const CommentNonInteractiveReaction = forwardRef<\n HTMLButtonElement,\n CommentNonInteractiveReactionProps\n>(({ reaction, overrides, ...props }, forwardedRef) => {\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n\n return (\n <CommentReactionButton\n disableable={false}\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n ref={forwardedRef}\n />\n );\n});\n\nfunction openAttachment({ attachment, url }: CommentAttachmentArgs) {\n // Open the attachment in a new tab if the attachment is a PDF,\n // an image, a video, or audio. Otherwise, download it.\n if (\n attachment.mimeType === \"application/pdf\" ||\n attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\") ||\n attachment.mimeType.startsWith(\"audio/\")\n ) {\n window.open(url, \"_blank\");\n } else {\n download(url, attachment.name);\n }\n}\n\nfunction CommentMediaAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <MediaAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nfunction CommentFileAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nexport function CommentNonInteractiveFileAttachment({\n className,\n ...props\n}: CommentAttachmentProps) {\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n allowMediaPreview={false}\n {...props}\n />\n );\n}\n\nconst CommentDropdownItem = forwardRef<\n HTMLDivElement,\n CommentDropdownItemProps\n>(({ children, icon, onSelect, onClick, ...props }, forwardedRef) => {\n const handleClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n }\n },\n [onClick]\n );\n\n return (\n <DropdownItem\n icon={icon}\n onSelect={onSelect}\n onClick={handleClick}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </DropdownItem>\n );\n});\n\n/**\n * Displays a single comment.\n *\n * @example\n * <>\n * {thread.comments.map((comment) => (\n * <Comment key={comment.id} comment={comment} />\n * ))}\n * </>\n */\nexport const Comment = Object.assign(\n forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n showAttachments = true,\n showComposerFormattingControls = true,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onCommentEdit,\n onCommentDelete,\n dropdownItems,\n overrides,\n components,\n additionalContent,\n avatar,\n author,\n date,\n className,\n actions,\n actionsClassName,\n internalDropdownItems,\n children,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteRoomComment(comment.roomId);\n const editComment = useEditRoomComment(comment.roomId);\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const $ = useOverrides(overrides);\n const [isEditing, setEditing] = useState(false);\n const [isTarget, setTarget] = useState(false);\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const [isReactionActionOpen, setReactionActionOpen] = useState(false);\n const { mediaAttachments, fileAttachments } = useMemo(() => {\n return separateMediaAttachments(comment.attachments);\n }, [comment.attachments]);\n\n const permissions = useRoomPermissions(comment.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleEdit = useCallback(() => {\n setEditing(true);\n }, []);\n\n const handleEditCancel = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n setEditing(false);\n },\n []\n );\n\n const handleEditSubmit = useCallback(\n (\n { body, attachments }: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentEdit?.(comment);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n event.preventDefault();\n\n setEditing(false);\n editComment({\n commentId: comment.id,\n threadId: comment.threadId,\n body,\n attachments,\n });\n },\n [comment, editComment, onCommentEdit]\n );\n\n const handleDelete = useCallback(() => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentDelete?.(comment);\n\n deleteComment({\n commentId: comment.id,\n threadId: comment.threadId,\n });\n }, [comment, deleteComment, onCommentDelete]);\n\n const handleAuthorClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n onAuthorClick?.(comment.userId, event);\n },\n [comment.userId, onAuthorClick]\n );\n\n const handleReactionSelect = useCallback(\n (emoji: string) => {\n const reactionIndex = comment.reactions.findIndex(\n (reaction) => reaction.emoji === emoji\n );\n\n if (\n reactionIndex >= 0 &&\n currentUserId &&\n comment.reactions[reactionIndex]?.users.some(\n (user) => user.id === currentUserId\n )\n ) {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n } else {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n }\n },\n [\n addReaction,\n comment.id,\n comment.reactions,\n comment.threadId,\n removeReaction,\n currentUserId,\n ]\n );\n\n useEffect(() => {\n const isWindowDefined = typeof window !== \"undefined\";\n if (!isWindowDefined) return;\n\n const hash = window.location.hash;\n const commentId = hash.slice(1);\n\n if (commentId === comment.id) {\n setTarget(true);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!showDeleted && !comment.body) {\n return null;\n }\n\n const commentDropdownItems =\n comment.userId === currentUserId ? (\n <>\n <CommentDropdownItem onSelect={handleEdit} icon={<EditIcon />}>\n {$.COMMENT_EDIT}\n </CommentDropdownItem>\n <CommentDropdownItem onSelect={handleDelete} icon={<DeleteIcon />}>\n {$.COMMENT_DELETE}\n </CommentDropdownItem>\n </>\n ) : null;\n const defaultDropdownItems =\n internalDropdownItems || commentDropdownItems ? (\n <>\n {internalDropdownItems}\n {commentDropdownItems}\n </>\n ) : null;\n\n const dropdownContent =\n typeof dropdownItems === \"function\" ? (\n dropdownItems({ children: defaultDropdownItems, comment })\n ) : defaultDropdownItems || dropdownItems ? (\n <>\n {defaultDropdownItems}\n {dropdownItems}\n </>\n ) : null;\n\n let content: ReactNode;\n\n if (isEditing) {\n content = (\n <Composer\n className=\"lb-comment-composer\"\n onComposerSubmit={handleEditSubmit}\n defaultValue={comment.body}\n defaultAttachments={comment.attachments}\n autoFocus\n showAttribution={false}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n actions={\n <>\n <Tooltip\n content={$.COMMENT_EDIT_COMPOSER_CANCEL}\n aria-label={$.COMMENT_EDIT_COMPOSER_CANCEL}\n >\n <Button\n className=\"lb-composer-action\"\n onClick={handleEditCancel}\n icon={<CrossIcon />}\n />\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut=\"Enter\"\n >\n <ComposerPrimitive.Submit asChild>\n <Button\n variant=\"primary\"\n className=\"lb-composer-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_EDIT_COMPOSER_SAVE}\n icon={<CheckIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n roomId={comment.roomId}\n />\n );\n } else {\n content = comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n body={comment.body}\n components={{\n Mention: ({ mention }) => (\n <CommentMention\n mention={mention}\n onClick={(event) => onMentionClick?.(mention, event)}\n overrides={overrides}\n />\n ),\n Link: CommentLink,\n }}\n />\n {additionalContent}\n {showAttachments &&\n (mediaAttachments.length > 0 || fileAttachments.length > 0) ? (\n <div className=\"lb-comment-attachments\">\n {mediaAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {mediaAttachments.map((attachment) => (\n <CommentMediaAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n {fileAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {fileAttachments.map((attachment) => (\n <CommentFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n {showReactions && comment.reactions.length > 0 && (\n <div className=\"lb-comment-reactions\">\n {comment.reactions.map((reaction) => (\n <CommentReaction\n key={reaction.emoji}\n comment={comment}\n reaction={reaction}\n overrides={overrides}\n disabled={!canComment}\n />\n ))}\n {canComment ? (\n <EmojiPicker onEmojiSelect={handleReactionSelect}>\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-reaction lb-comment-reaction-add\"\n variant=\"outline\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n );\n\n content =\n typeof children === \"function\"\n ? children({ comment, children: content })\n : (children ?? content);\n }\n\n return (\n <TooltipProvider>\n <ComponentsProvider components={components}>\n <div\n id={comment.id}\n className={cn(\n \"lb-root lb-comment\",\n indentContent && \"lb-comment:indent-content\",\n showActions === \"hover\" && \"lb-comment:show-actions-hover\",\n (isMoreActionOpen || isReactionActionOpen) &&\n \"lb-comment:action-open\",\n className\n )}\n data-deleted={!comment.body ? \"\" : undefined}\n data-editing={isEditing ? \"\" : undefined}\n // In some cases, `:target` doesn't work as expected so we also define it manually.\n data-target={isTarget ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={mergedRefs}\n >\n <div className=\"lb-comment-header\">\n <div className=\"lb-comment-details\">\n {avatar ? (\n <div\n className=\"lb-comment-avatar\"\n onClick={handleAuthorClick}\n >\n {avatar}\n </div>\n ) : (\n <Avatar\n className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n <span className=\"lb-comment-details-labels\">\n {author ? (\n <span\n className=\"lb-comment-author\"\n onClick={handleAuthorClick}\n >\n {author}\n </span>\n ) : (\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n {date ? (\n <span className=\"lb-comment-date\">{date}</span>\n ) : (\n <span className=\"lb-comment-date\">\n <CommentDate\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-comment-date-created\"\n />\n {comment.editedAt && comment.body && (\n <>\n {\" \"}\n <span className=\"lb-comment-date-edited\">\n {$.COMMENT_EDITED}\n </span>\n </>\n )}\n </span>\n )}\n </span>\n </div>\n {showActions && !isEditing && (\n <div className={cn(\"lb-comment-actions\", actionsClassName)}>\n {actions ?? null}\n {showReactions && canComment ? (\n <EmojiPicker\n onEmojiSelect={handleReactionSelect}\n onOpenChange={setReactionActionOpen}\n >\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n {dropdownContent ? (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={dropdownContent}\n >\n <Tooltip content={$.COMMENT_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n disabled={!comment.body}\n onClick={stopPropagation}\n aria-label={$.COMMENT_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n ) : null}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">{content}</div>\n </div>\n </ComponentsProvider>\n </TooltipProvider>\n );\n }\n ),\n {\n /**\n * Displays a dropdown item in the comment's dropdown.\n */\n DropdownItem: CommentDropdownItem,\n\n /**\n * Displays a comment's avatar.\n */\n Avatar: CommentAvatar,\n\n /**\n * Displays a comment's author.\n */\n Author: CommentAuthor,\n\n /**\n * Displays a comment's date.\n */\n Date: CommentDate,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA;AAmKA;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACE;AAAC;AAAA;AACC;AACA;AACkC;AAC7B;AAAA;AAGX;AAwCA;AAA4B;AAC1B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AACb;AACvC;AAEJ;AAAuD;AAC7B;AAAA;AAAA;AAGhC;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AAC7B;AACvB;AAEJ;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAwD;AAGxD;AAAyD;AAGzD;AAAoD;AAE1D;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AAC2C;AAC1C;AACI;AACG;AAEsB;AAAA;AAGnC;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AAKF;AAEA;AAIE;AACA;AACE;AAAC;AAAA;AAC+C;AACtC;AACM;AACH;AACM;AACjB;AACI;AACC;AAEL;AAAoE;AACD;AAAA;AAAA;AAGzE;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACD;AAAC;AAAA;AAGE;AACkB;AACT;AACA;AAAA;AACZ;AACS;AACM;AAEnB;AAEU;AAGd;AACE;AAAsB;AAGxB;AAA4B;AAExB;AACE;AAAY;AACQ;AACC;AACH;AACjB;AAED;AAAe;AACK;AACC;AACH;AACjB;AACH;AACF;AAC0E;AAG5E;AACE;AAAC;AAAA;AACU;AACA;AACC;AAEV;AAAiB;AAAhB;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEL;AAAC;AAAA;AAC4B;AAC3B;AACA;AACI;AAAA;AACN;AAAA;AACF;AAAA;AAGN;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACE;AAAC;AAAA;AACc;AACc;AAC3B;AACA;AACI;AACC;AAAA;AAGX;AAEA;AAGE;AAME;AAAyB;AAEzB;AAA6B;AAEjC;AAEA;AAAgC;AAC9B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEA;AAA+B;AAC7B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEO;AAA6C;AAClD;AAEF;AACE;AACE;AAAC;AAAA;AACiD;AAC7B;AACf;AAAA;AAGV;AAEA;AAIE;AAAoB;AAEhB;AAEA;AACE;AAAsB;AACxB;AACF;AACQ;AAGV;AACE;AAAC;AAAA;AACC;AACA;AACS;AACL;AACC;AAEJ;AAAA;AAGP;AAYO;AAAuB;AAC5B;AAEI;AACE;AACgB;AAChB;AACc;AACE;AACE;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAmD;AAGrD;AACA;AAMA;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAMrB;AAEA;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAAY;AACS;AACD;AAClB;AACA;AACD;AACH;AACoC;AAGtC;AAEE;AAEA;AAAc;AACO;AACD;AACnB;AAGH;AAA0B;AAEtB;AAAqC;AACvC;AAC8B;AAGhC;AAA6B;AAEzB;AAAwC;AACL;AAGnC;AAG0C;AAChB;AAGxB;AAAe;AACK;AACC;AACnB;AACD;AAED;AAAY;AACQ;AACC;AACnB;AACD;AACH;AACF;AACA;AACE;AACQ;AACA;AACA;AACR;AACA;AACF;AAGF;AACE;AACA;AAAsB;AAEtB;AACA;AAEA;AACE;AAAc;AAChB;AAGF;AACE;AAAO;AAGT;AAGM;AAEA;AAGA;AAGN;AAGO;AAAA;AACA;AAIP;AAKO;AAAA;AACA;AAIP;AAEA;AACE;AACE;AAAC;AAAA;AACW;AACQ;AACI;AACM;AACnB;AACQ;AACjB;AACwB;AAGpB;AAAA;AAAC;AAAA;AACY;AACG;AAEd;AAAC;AAAA;AACW;AACD;AACQ;AAAA;AACnB;AAAA;AACF;AACA;AAAC;AAAA;AACY;AACF;AAGP;AAAC;AAAA;AACS;AACE;AACD;AACK;AACG;AAAA;AAErB;AAAA;AACF;AACF;AAES;AACe;AAC1B;AACgB;AAAA;AAClB;AAGF;AAEI;AAAA;AAAkB;AAAjB;AACW;AACI;AACF;AAER;AAAC;AAAA;AACC;AACmD;AACnD;AAAA;AACF;AAEI;AACR;AAAA;AACF;AACC;AAII;AAGK;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAIE;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAEJ;AAGC;AACC;AAAC;AAAA;AAEC;AACA;AACA;AACW;AAAA;AAJG;AAMjB;AAKO;AAAC;AAAA;AACW;AACF;AACC;AACK;AACO;AAAA;AAK3B;AACN;AASN;AAGmB;AAGrB;AAGM;AAAC;AAAA;AACa;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACtB;AACH;AACC;AAEL;AACE;AACG;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAGC;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAME;AAAA;AAAC;AAAA;AACW;AACI;AACJ;AAAA;AACZ;AAGK;AAAA;AAGD;AACF;AAEJ;AAEJ;AACF;AAGK;AAAW;AAEV;AAAC;AAAA;AACgB;AACD;AAIV;AAAC;AAAA;AACW;AACD;AACK;AACO;AAAA;AAG3B;AAAA;AAEA;AAEF;AAAC;AAAA;AACO;AACQ;AACR;AACG;AAIL;AAAC;AAAA;AACW;AACS;AACV;AACK;AACM;AAAA;AAG1B;AAAA;AAEA;AACN;AAEJ;AAC6C;AAAA;AAAA;AAGnD;AAEJ;AACF;AACA;AAAA;AAAA;AAAA;AAIgB;AAAA;AAAA;AAAA;AAKN;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;AAKF;AAEV;;;;;;;;"}
1
+ {"version":3,"file":"Comment.cjs","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n assertNever,\n type BaseMetadata,\n type CommentAttachment,\n type CommentData,\n type CommentReaction as CommentReactionData,\n type DCM,\n type GroupMentionData,\n MENTION_CHARACTER,\n type MentionData,\n Permission,\n} from \"@liveblocks/core\";\nimport {\n useAddRoomCommentReaction,\n useDeleteRoomComment,\n useEditRoomComment,\n useRemoveRoomCommentReaction,\n useRoomAttachmentUrl,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport { Toggle as TogglePrimitive } from \"radix-ui\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n ComponentsProvider,\n type GlobalComponents,\n useComponents,\n} from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { CrossIcon } from \"../icons/Cross\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EditIcon } from \"../icons/Edit\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { EmojiPlusIcon } from \"../icons/EmojiPlus\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport type { ComposerSubmitComment } from \"../primitives\";\nimport * as CommentPrimitive from \"../primitives/Comment\";\nimport type {\n CommentBodyLinkProps,\n CommentBodyMentionProps,\n CommentLinkProps,\n CommentMentionProps as CommentPrimitiveMentionProps,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { CommentAttachmentArgs } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { download } from \"../utils/download\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { useRefs } from \"../utils/use-refs\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport {\n FileAttachment,\n MediaAttachment,\n separateMediaAttachments,\n} from \"./internal/Attachment\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button, CustomButton } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { List } from \"./internal/List\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps<CM extends BaseMetadata = DCM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The comment to display.\n */\n comment: CommentData<CM>;\n\n /**\n * The comment's avatar.\n * Can be combined with `Comment.Avatar` to easily follow default styles.\n */\n avatar?: ReactNode;\n\n /**\n * The comment's author.\n * Can be combined with `Comment.Author` to easily follow default styles.\n */\n author?: ReactNode;\n\n /**\n * The comment's date.\n * Can be combined with `Comment.Date` to easily follow default styles,\n * or the `Timestamp` primitive for more control.\n */\n date?: ReactNode;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n\n /**\n * Whether to show the comment if it was deleted. If set to `false`, it will render deleted comments as `null`.\n */\n showDeleted?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show the composer's formatting controls when editing the comment.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comment's content.\n */\n indentContent?: boolean;\n\n /**\n * Additional content to display below the comment's body.\n */\n additionalContent?: ReactNode;\n\n /**\n * The event handler called when the comment is edited.\n */\n onCommentEdit?: (comment: CommentData) => void;\n\n /**\n * The event handler called when the comment is deleted.\n */\n onCommentDelete?: (comment: CommentData) => void;\n\n /**\n * The event handler called when clicking on the author.\n */\n onAuthorClick?: (userId: string, event: MouseEvent<HTMLElement>) => void;\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: (\n mention: MentionData,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: (\n args: CommentAttachmentArgs,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * Add (or change) items to display in the comment's dropdown.\n */\n dropdownItems?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the comment's content.\n */\n children?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n actionsClassName?: string;\n\n /**\n * @internal\n */\n internalDropdownItems?: ReactNode;\n}\n\nexport interface CommentAvatarProps\n extends Omit<ComponentProps<\"div\">, \"children\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport interface CommentAuthorProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The user ID to display the author for.\n */\n userId: string;\n}\n\nexport interface CommentDateProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nfunction CommentAvatar(props: CommentAvatarProps) {\n return <Avatar {...props} />;\n}\n\nfunction CommentAuthor(props: CommentAuthorProps) {\n return <User {...props} />;\n}\n\nfunction CommentDate({ locale, date, className, ...props }: CommentDateProps) {\n return (\n <Timestamp\n locale={locale}\n date={date}\n className={cn(\"lb-date\", className)}\n {...(props as Omit<ComponentProps<\"time\">, \"children\" | \"title\">)}\n />\n );\n}\n\nexport interface CommentDropdownItemProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\"> {\n /**\n * An optional icon displayed in this dropdown item.\n */\n icon?: ReactNode;\n\n /**\n * The event handler called when the dropdown item is selected.\n */\n onSelect?: (event: Event) => void;\n}\n\ninterface CommentReactionButtonProps\n extends ComponentPropsWithoutRef<typeof Button> {\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ninterface CommentReactionProps extends ComponentPropsWithoutRef<\"button\"> {\n comment: CommentData;\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ntype CommentNonInteractiveReactionProps = Omit<CommentReactionProps, \"comment\">;\n\ninterface CommentAttachmentProps extends ComponentProps<typeof FileAttachment> {\n attachment: CommentAttachment;\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n}\n\ninterface CommentMentionProps\n extends CommentBodyMentionProps,\n CommentPrimitiveMentionProps {\n overrides?: CommentProps[\"overrides\"];\n}\n\nfunction CommentUserMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const currentId = useCurrentUserId();\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={mention.id === currentId ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nfunction CommentGroupMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={isMember ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentMention({ mention, ...props }: CommentMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <CommentUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <CommentGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n const { Anchor } = useComponents();\n\n return (\n <CommentPrimitive.Link\n className={cn(\"lb-comment-link\", className)}\n href={href}\n {...props}\n asChild\n >\n <Anchor {...props}>{children}</Anchor>\n </CommentPrimitive.Link>\n );\n}\n\nexport function CommentNonInteractiveLink({\n href: _href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <span className={cn(\"lb-comment-link\", className)} {...props}>\n {children}\n </span>\n );\n}\n\nconst CommentReactionButton = forwardRef<\n HTMLButtonElement,\n CommentReactionButtonProps\n>(({ reaction, overrides, className, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n return (\n <CustomButton\n className={cn(\"lb-comment-reaction\", className)}\n variant=\"outline\"\n aria-label={$.COMMENT_REACTION_DESCRIPTION(\n reaction.emoji,\n reaction.users.length\n )}\n {...props}\n ref={forwardedRef}\n >\n <Emoji className=\"lb-comment-reaction-emoji\" emoji={reaction.emoji} />\n <span className=\"lb-comment-reaction-count\">{reaction.users.length}</span>\n </CustomButton>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n const $ = useOverrides(overrides);\n const tooltipContent = useMemo(\n () => (\n <span>\n {$.COMMENT_REACTION_LIST(\n <List\n values={reaction.users.map((users) => (\n <User key={users.id} userId={users.id} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={REACTIONS_TRUNCATE}\n locale={$.locale}\n />,\n reaction.emoji,\n reaction.users.length\n )}\n </span>\n ),\n [$, reaction]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handlePressedChange = useCallback(\n (isPressed: boolean) => {\n if (isPressed) {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n } else {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n }\n },\n [addReaction, comment.threadId, comment.id, reaction.emoji, removeReaction]\n );\n\n return (\n <Tooltip\n content={tooltipContent}\n multiline\n className=\"lb-comment-reaction-tooltip\"\n >\n <TogglePrimitive.Root\n asChild\n pressed={isActive}\n onPressedChange={handlePressedChange}\n onClick={stopPropagation}\n disabled={disabled}\n ref={forwardedRef}\n >\n <CommentReactionButton\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n );\n});\n\nexport const CommentNonInteractiveReaction = forwardRef<\n HTMLButtonElement,\n CommentNonInteractiveReactionProps\n>(({ reaction, overrides, ...props }, forwardedRef) => {\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n\n return (\n <CommentReactionButton\n disableable={false}\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n ref={forwardedRef}\n />\n );\n});\n\nfunction openAttachment({ attachment, url }: CommentAttachmentArgs) {\n // Open the attachment in a new tab if the attachment is a PDF,\n // an image, a video, or audio. Otherwise, download it.\n if (\n attachment.mimeType === \"application/pdf\" ||\n attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\") ||\n attachment.mimeType.startsWith(\"audio/\")\n ) {\n window.open(url, \"_blank\");\n } else {\n download(url, attachment.name);\n }\n}\n\nfunction CommentMediaAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <MediaAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nfunction CommentFileAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nexport function CommentNonInteractiveFileAttachment({\n className,\n ...props\n}: CommentAttachmentProps) {\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n allowMediaPreview={false}\n {...props}\n />\n );\n}\n\nconst CommentDropdownItem = forwardRef<\n HTMLDivElement,\n CommentDropdownItemProps\n>(({ children, icon, onSelect, onClick, ...props }, forwardedRef) => {\n const handleClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n }\n },\n [onClick]\n );\n\n return (\n <DropdownItem\n icon={icon}\n onSelect={onSelect}\n onClick={handleClick}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </DropdownItem>\n );\n});\n\n/**\n * Displays a single comment.\n *\n * @example\n * <>\n * {thread.comments.map((comment) => (\n * <Comment key={comment.id} comment={comment} />\n * ))}\n * </>\n */\nexport const Comment = Object.assign(\n forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n showAttachments = true,\n showComposerFormattingControls = true,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onCommentEdit,\n onCommentDelete,\n dropdownItems,\n overrides,\n components,\n additionalContent,\n avatar,\n author,\n date,\n className,\n actions,\n actionsClassName,\n internalDropdownItems,\n children,\n ...props\n },\n forwardedRef\n ) => {\n const bodyId = `${comment.id}:body`;\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteRoomComment(comment.roomId);\n const editComment = useEditRoomComment(comment.roomId);\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const $ = useOverrides(overrides);\n const [isEditing, setEditing] = useState(false);\n const [isTarget, setTarget] = useState(false);\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const [isReactionActionOpen, setReactionActionOpen] = useState(false);\n const { mediaAttachments, fileAttachments } = useMemo(() => {\n return separateMediaAttachments(comment.attachments);\n }, [comment.attachments]);\n\n const permissions = useRoomPermissions(comment.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleEdit = useCallback(() => {\n setEditing(true);\n }, []);\n\n const handleEditCancel = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n setEditing(false);\n },\n []\n );\n\n const handleEditSubmit = useCallback(\n (\n { body, attachments }: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentEdit?.(comment);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n event.preventDefault();\n\n setEditing(false);\n editComment({\n commentId: comment.id,\n threadId: comment.threadId,\n body,\n attachments,\n });\n },\n [comment, editComment, onCommentEdit]\n );\n\n const handleDelete = useCallback(() => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentDelete?.(comment);\n\n deleteComment({\n commentId: comment.id,\n threadId: comment.threadId,\n });\n }, [comment, deleteComment, onCommentDelete]);\n\n const handleAuthorClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n onAuthorClick?.(comment.userId, event);\n },\n [comment.userId, onAuthorClick]\n );\n\n const handleReactionSelect = useCallback(\n (emoji: string) => {\n const reactionIndex = comment.reactions.findIndex(\n (reaction) => reaction.emoji === emoji\n );\n\n if (\n reactionIndex >= 0 &&\n currentUserId &&\n comment.reactions[reactionIndex]?.users.some(\n (user) => user.id === currentUserId\n )\n ) {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n } else {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n }\n },\n [\n addReaction,\n comment.id,\n comment.reactions,\n comment.threadId,\n removeReaction,\n currentUserId,\n ]\n );\n\n useEffect(() => {\n const isWindowDefined = typeof window !== \"undefined\";\n if (!isWindowDefined) return;\n\n const hash = window.location.hash;\n const commentId = hash.slice(1);\n\n if (commentId === comment.id) {\n setTarget(true);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!showDeleted && !comment.body) {\n return null;\n }\n\n const commentDropdownItems =\n comment.userId === currentUserId ? (\n <>\n <CommentDropdownItem onSelect={handleEdit} icon={<EditIcon />}>\n {$.COMMENT_EDIT}\n </CommentDropdownItem>\n <CommentDropdownItem onSelect={handleDelete} icon={<DeleteIcon />}>\n {$.COMMENT_DELETE}\n </CommentDropdownItem>\n </>\n ) : null;\n const defaultDropdownItems =\n internalDropdownItems || commentDropdownItems ? (\n <>\n {internalDropdownItems}\n {commentDropdownItems}\n </>\n ) : null;\n\n const dropdownContent =\n typeof dropdownItems === \"function\" ? (\n dropdownItems({ children: defaultDropdownItems, comment })\n ) : defaultDropdownItems || dropdownItems ? (\n <>\n {defaultDropdownItems}\n {dropdownItems}\n </>\n ) : null;\n\n let content: ReactNode;\n\n if (isEditing) {\n content = (\n <Composer\n className=\"lb-comment-composer\"\n onComposerSubmit={handleEditSubmit}\n defaultValue={comment.body}\n defaultAttachments={comment.attachments}\n autoFocus\n showAttribution={false}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n actions={\n <>\n <Tooltip\n content={$.COMMENT_EDIT_COMPOSER_CANCEL}\n aria-label={$.COMMENT_EDIT_COMPOSER_CANCEL}\n >\n <Button\n className=\"lb-composer-action\"\n onClick={handleEditCancel}\n icon={<CrossIcon />}\n />\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut=\"Enter\"\n >\n <ComposerPrimitive.Submit asChild>\n <Button\n variant=\"primary\"\n className=\"lb-composer-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_EDIT_COMPOSER_SAVE}\n icon={<CheckIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n roomId={comment.roomId}\n />\n );\n } else {\n content = comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n id={bodyId}\n body={comment.body}\n components={{\n Mention: ({ mention }) => (\n <CommentMention\n mention={mention}\n onClick={(event) => onMentionClick?.(mention, event)}\n overrides={overrides}\n />\n ),\n Link: CommentLink,\n }}\n />\n {additionalContent}\n {showAttachments &&\n (mediaAttachments.length > 0 || fileAttachments.length > 0) ? (\n <div className=\"lb-comment-attachments\">\n {mediaAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {mediaAttachments.map((attachment) => (\n <CommentMediaAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n {fileAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {fileAttachments.map((attachment) => (\n <CommentFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n {showReactions && comment.reactions.length > 0 && (\n <div className=\"lb-comment-reactions\">\n {comment.reactions.map((reaction) => (\n <CommentReaction\n key={reaction.emoji}\n comment={comment}\n reaction={reaction}\n overrides={overrides}\n disabled={!canComment}\n />\n ))}\n {canComment ? (\n <EmojiPicker onEmojiSelect={handleReactionSelect}>\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-reaction lb-comment-reaction-add\"\n variant=\"outline\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n );\n\n content =\n typeof children === \"function\"\n ? children({ comment, children: content })\n : (children ?? content);\n }\n\n return (\n <TooltipProvider>\n <ComponentsProvider components={components}>\n <div\n role=\"article\"\n id={comment.id}\n className={cn(\n \"lb-root lb-comment\",\n indentContent && \"lb-comment:indent-content\",\n showActions === \"hover\" && \"lb-comment:show-actions-hover\",\n (isMoreActionOpen || isReactionActionOpen) &&\n \"lb-comment:action-open\",\n className\n )}\n data-deleted={!comment.body ? \"\" : undefined}\n data-editing={isEditing ? \"\" : undefined}\n // In some cases, `:target` doesn't work as expected so we also define it manually.\n data-target={isTarget ? \"\" : undefined}\n aria-labelledby={bodyId}\n dir={$.dir}\n {...props}\n ref={mergedRefs}\n >\n <div className=\"lb-comment-header\">\n <div className=\"lb-comment-details\">\n {avatar ? (\n <div\n className=\"lb-comment-avatar\"\n onClick={handleAuthorClick}\n >\n {avatar}\n </div>\n ) : (\n <Avatar\n className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n <span className=\"lb-comment-details-labels\">\n {author ? (\n <span\n className=\"lb-comment-author\"\n onClick={handleAuthorClick}\n >\n {author}\n </span>\n ) : (\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n {date ? (\n <span className=\"lb-comment-date\">{date}</span>\n ) : (\n <span className=\"lb-comment-date\">\n <CommentDate\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-comment-date-created\"\n />\n {comment.editedAt && comment.body && (\n <>\n {\" \"}\n <span className=\"lb-comment-date-edited\">\n {$.COMMENT_EDITED}\n </span>\n </>\n )}\n </span>\n )}\n </span>\n </div>\n {showActions && !isEditing && (\n <div className={cn(\"lb-comment-actions\", actionsClassName)}>\n {actions ?? null}\n {showReactions && canComment ? (\n <EmojiPicker\n onEmojiSelect={handleReactionSelect}\n onOpenChange={setReactionActionOpen}\n >\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n {dropdownContent ? (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={dropdownContent}\n >\n <Tooltip content={$.COMMENT_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n disabled={!comment.body}\n onClick={stopPropagation}\n aria-label={$.COMMENT_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n ) : null}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">{content}</div>\n </div>\n </ComponentsProvider>\n </TooltipProvider>\n );\n }\n ) as <CM extends BaseMetadata = DCM>(\n props: CommentProps<CM> & RefAttributes<HTMLDivElement>\n ) => JSX.Element,\n {\n /**\n * Displays a dropdown item in the comment's dropdown.\n */\n DropdownItem: CommentDropdownItem,\n\n /**\n * Displays a comment's avatar.\n */\n Avatar: CommentAvatar,\n\n /**\n * Displays a comment's author.\n */\n Author: CommentAuthor,\n\n /**\n * Displays a comment's date.\n */\n Date: CommentDate,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4FA;AAmKA;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACE;AAAC;AAAA;AACC;AACA;AACkC;AAC7B;AAAA;AAGX;AAwCA;AAA4B;AAC1B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AACb;AACvC;AAEJ;AAAuD;AAC7B;AAAA;AAAA;AAGhC;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AAC7B;AACvB;AAEJ;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAwD;AAGxD;AAAyD;AAGzD;AAAoD;AAE1D;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AAC2C;AAC1C;AACI;AACG;AAEsB;AAAA;AAGnC;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AAKF;AAEA;AAIE;AACA;AACE;AAAC;AAAA;AAC+C;AACtC;AACM;AACH;AACM;AACjB;AACI;AACC;AAEL;AAAoE;AACD;AAAA;AAAA;AAGzE;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACD;AAAC;AAAA;AAGE;AACkB;AACT;AACA;AAAA;AACZ;AACS;AACM;AAEnB;AAEU;AAGd;AACE;AAAsB;AAGxB;AAA4B;AAExB;AACE;AAAY;AACQ;AACC;AACH;AACjB;AAED;AAAe;AACK;AACC;AACH;AACjB;AACH;AACF;AAC0E;AAG5E;AACE;AAAC;AAAA;AACU;AACA;AACC;AAEV;AAAiB;AAAhB;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEL;AAAC;AAAA;AAC4B;AAC3B;AACA;AACI;AAAA;AACN;AAAA;AACF;AAAA;AAGN;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACE;AAAC;AAAA;AACc;AACc;AAC3B;AACA;AACI;AACC;AAAA;AAGX;AAEA;AAGE;AAME;AAAyB;AAEzB;AAA6B;AAEjC;AAEA;AAAgC;AAC9B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEA;AAA+B;AAC7B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEO;AAA6C;AAClD;AAEF;AACE;AACE;AAAC;AAAA;AACiD;AAC7B;AACf;AAAA;AAGV;AAEA;AAIE;AAAoB;AAEhB;AAEA;AACE;AAAsB;AACxB;AACF;AACQ;AAGV;AACE;AAAC;AAAA;AACC;AACA;AACS;AACL;AACC;AAEJ;AAAA;AAGP;AAYO;AAAuB;AAC5B;AAEI;AACE;AACgB;AAChB;AACc;AACE;AACE;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAmD;AAGrD;AACA;AAMA;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAMrB;AAEA;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAAY;AACS;AACD;AAClB;AACA;AACD;AACH;AACoC;AAGtC;AAEE;AAEA;AAAc;AACO;AACD;AACnB;AAGH;AAA0B;AAEtB;AAAqC;AACvC;AAC8B;AAGhC;AAA6B;AAEzB;AAAwC;AACL;AAGnC;AAG0C;AAChB;AAGxB;AAAe;AACK;AACC;AACnB;AACD;AAED;AAAY;AACQ;AACC;AACnB;AACD;AACH;AACF;AACA;AACE;AACQ;AACA;AACA;AACR;AACA;AACF;AAGF;AACE;AACA;AAAsB;AAEtB;AACA;AAEA;AACE;AAAc;AAChB;AAGF;AACE;AAAO;AAGT;AAGM;AAEA;AAGA;AAGN;AAGO;AAAA;AACA;AAIP;AAKO;AAAA;AACA;AAIP;AAEA;AACE;AACE;AAAC;AAAA;AACW;AACQ;AACI;AACM;AACnB;AACQ;AACjB;AACwB;AAGpB;AAAA;AAAC;AAAA;AACY;AACG;AAEd;AAAC;AAAA;AACW;AACD;AACQ;AAAA;AACnB;AAAA;AACF;AACA;AAAC;AAAA;AACY;AACF;AAGP;AAAC;AAAA;AACS;AACE;AACD;AACK;AACG;AAAA;AAErB;AAAA;AACF;AACF;AAES;AACe;AAC1B;AACgB;AAAA;AAClB;AAGF;AAEI;AAAA;AAAkB;AAAjB;AACW;AACN;AACU;AACF;AAER;AAAC;AAAA;AACC;AACmD;AACnD;AAAA;AACF;AAEI;AACR;AAAA;AACF;AACC;AAII;AAGK;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAIE;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAEJ;AAGC;AACC;AAAC;AAAA;AAEC;AACA;AACA;AACW;AAAA;AAJG;AAMjB;AAKO;AAAC;AAAA;AACW;AACF;AACC;AACK;AACO;AAAA;AAK3B;AACN;AASN;AAGmB;AAGrB;AAGM;AAAC;AAAA;AACM;AACO;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACZ;AACV;AACH;AACC;AAEL;AACE;AACG;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAGC;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAME;AAAA;AAAC;AAAA;AACW;AACI;AACJ;AAAA;AACZ;AAGK;AAAA;AAGD;AACF;AAEJ;AAEJ;AACF;AAGK;AAAW;AAEV;AAAC;AAAA;AACgB;AACD;AAIV;AAAC;AAAA;AACW;AACD;AACK;AACO;AAAA;AAG3B;AAAA;AAEA;AAEF;AAAC;AAAA;AACO;AACQ;AACR;AACG;AAIL;AAAC;AAAA;AACW;AACS;AACV;AACK;AACM;AAAA;AAG1B;AAAA;AAEA;AACN;AAEJ;AAC6C;AAAA;AAAA;AAGnD;AAEJ;AACF;AAGA;AAAA;AAAA;AAAA;AAIgB;AAAA;AAAA;AAAA;AAKN;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;AAKF;AAEV;;;;;;;;"}
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { MENTION_CHARACTER, assertNever, Permission } from '@liveblocks/core';
4
4
  import { useAddRoomCommentReaction, useRemoveRoomCommentReaction, useRoomAttachmentUrl, useDeleteRoomComment, useEditRoomComment, useRoomPermissions } from '@liveblocks/react/_private';
5
- import * as TogglePrimitive from '@radix-ui/react-toggle';
5
+ import { Toggle } from 'radix-ui';
6
6
  import { forwardRef, useMemo, useCallback, useRef, useState, useEffect } from 'react';
7
7
  import { useComponents, ComponentsProvider } from '../components.js';
8
8
  import { CheckIcon } from '../icons/Check.js';
@@ -24,16 +24,13 @@ import { Composer } from './Composer.js';
24
24
  import { MediaAttachment, FileAttachment, separateMediaAttachments } from './internal/Attachment.js';
25
25
  import { Avatar } from './internal/Avatar.js';
26
26
  import { CustomButton, Button } from './internal/Button.js';
27
- import { DropdownItem, Dropdown } from './internal/Dropdown.js';
27
+ import { DropdownItem, Dropdown, DropdownTrigger } from './internal/Dropdown.js';
28
28
  import { Emoji } from './internal/Emoji.js';
29
- import { EmojiPicker } from './internal/EmojiPicker.js';
29
+ import { EmojiPicker, EmojiPickerTrigger } from './internal/EmojiPicker.js';
30
30
  import { Group } from './internal/Group.js';
31
31
  import { List } from './internal/List.js';
32
- import { Tooltip, ShortcutTooltip } from './internal/Tooltip.js';
32
+ import { Tooltip, ShortcutTooltip, TooltipProvider } from './internal/Tooltip.js';
33
33
  import { User } from './internal/User.js';
34
- import { PopoverTrigger } from '@radix-ui/react-popover';
35
- import { TooltipProvider } from '@radix-ui/react-tooltip';
36
- import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu';
37
34
 
38
35
 
39
36
  const REACTIONS_TRUNCATE = 5;
@@ -200,7 +197,7 @@ const CommentReaction = forwardRef(({ comment, reaction, overrides, disabled, ..
200
197
  multiline: true,
201
198
  className: "lb-comment-reaction-tooltip",
202
199
  children: /* @__PURE__ */ jsx(
203
- TogglePrimitive.Root,
200
+ Toggle.Root,
204
201
  {
205
202
  asChild: true,
206
203
  pressed: isActive,
@@ -380,6 +377,7 @@ const Comment = Object.assign(
380
377
  children,
381
378
  ...props
382
379
  }, forwardedRef) => {
380
+ const bodyId = `${comment.id}:body`;
383
381
  const ref = useRef(null);
384
382
  const mergedRefs = useRefs(forwardedRef, ref);
385
383
  const currentUserId = useCurrentUserId();
@@ -555,6 +553,7 @@ const Comment = Object.assign(
555
553
  CommentBody,
556
554
  {
557
555
  className: "lb-comment-body",
556
+ id: bodyId,
558
557
  body: comment.body,
559
558
  components: {
560
559
  Mention: ({ mention }) => /* @__PURE__ */ jsx(
@@ -603,7 +602,7 @@ const Comment = Object.assign(
603
602
  },
604
603
  reaction.emoji
605
604
  )),
606
- canComment ? /* @__PURE__ */ jsx(EmojiPicker, { onEmojiSelect: handleReactionSelect, children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_ADD_REACTION, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
605
+ canComment ? /* @__PURE__ */ jsx(EmojiPicker, { onEmojiSelect: handleReactionSelect, children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_ADD_REACTION, children: /* @__PURE__ */ jsx(EmojiPickerTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
607
606
  Button,
608
607
  {
609
608
  className: "lb-comment-reaction lb-comment-reaction-add",
@@ -620,6 +619,7 @@ const Comment = Object.assign(
620
619
  return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(ComponentsProvider, { components, children: /* @__PURE__ */ jsxs(
621
620
  "div",
622
621
  {
622
+ role: "article",
623
623
  id: comment.id,
624
624
  className: cn(
625
625
  "lb-root lb-comment",
@@ -631,6 +631,7 @@ const Comment = Object.assign(
631
631
  "data-deleted": !comment.body ? "" : void 0,
632
632
  "data-editing": isEditing ? "" : void 0,
633
633
  "data-target": isTarget ? "" : void 0,
634
+ "aria-labelledby": bodyId,
634
635
  dir: $.dir,
635
636
  ...props,
636
637
  ref: mergedRefs,
@@ -691,7 +692,7 @@ const Comment = Object.assign(
691
692
  {
692
693
  onEmojiSelect: handleReactionSelect,
693
694
  onOpenChange: setReactionActionOpen,
694
- children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_ADD_REACTION, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
695
+ children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_ADD_REACTION, children: /* @__PURE__ */ jsx(EmojiPickerTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
695
696
  Button,
696
697
  {
697
698
  className: "lb-comment-action",
@@ -709,7 +710,7 @@ const Comment = Object.assign(
709
710
  onOpenChange: setMoreActionOpen,
710
711
  align: "end",
711
712
  content: dropdownContent,
712
- children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_MORE, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
713
+ children: /* @__PURE__ */ jsx(Tooltip, { content: $.COMMENT_MORE, children: /* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
713
714
  Button,
714
715
  {
715
716
  className: "lb-comment-action",
@@ -1 +1 @@
1
- {"version":3,"file":"Comment.js","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n assertNever,\n type BaseMetadata,\n type CommentAttachment,\n type CommentData,\n type CommentReaction as CommentReactionData,\n type DCM,\n type GroupMentionData,\n MENTION_CHARACTER,\n type MentionData,\n Permission,\n} from \"@liveblocks/core\";\nimport {\n useAddRoomCommentReaction,\n useDeleteRoomComment,\n useEditRoomComment,\n useRemoveRoomCommentReaction,\n useRoomAttachmentUrl,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n ComponentsProvider,\n type GlobalComponents,\n useComponents,\n} from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { CrossIcon } from \"../icons/Cross\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EditIcon } from \"../icons/Edit\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { EmojiPlusIcon } from \"../icons/EmojiPlus\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport type { ComposerSubmitComment } from \"../primitives\";\nimport * as CommentPrimitive from \"../primitives/Comment\";\nimport type {\n CommentBodyLinkProps,\n CommentBodyMentionProps,\n CommentLinkProps,\n CommentMentionProps as CommentPrimitiveMentionProps,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { CommentAttachmentArgs } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { download } from \"../utils/download\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { useRefs } from \"../utils/use-refs\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport {\n FileAttachment,\n MediaAttachment,\n separateMediaAttachments,\n} from \"./internal/Attachment\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button, CustomButton } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { List } from \"./internal/List\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps<CM extends BaseMetadata = DCM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The comment to display.\n */\n comment: CommentData<CM>;\n\n /**\n * The comment's avatar.\n * Can be combined with `Comment.Avatar` to easily follow default styles.\n */\n avatar?: ReactNode;\n\n /**\n * The comment's author.\n * Can be combined with `Comment.Author` to easily follow default styles.\n */\n author?: ReactNode;\n\n /**\n * The comment's date.\n * Can be combined with `Comment.Date` to easily follow default styles,\n * or the `Timestamp` primitive for more control.\n */\n date?: ReactNode;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n\n /**\n * Whether to show the comment if it was deleted. If set to `false`, it will render deleted comments as `null`.\n */\n showDeleted?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show the composer's formatting controls when editing the comment.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comment's content.\n */\n indentContent?: boolean;\n\n /**\n * Additional content to display below the comment's body.\n */\n additionalContent?: ReactNode;\n\n /**\n * The event handler called when the comment is edited.\n */\n onCommentEdit?: (comment: CommentData) => void;\n\n /**\n * The event handler called when the comment is deleted.\n */\n onCommentDelete?: (comment: CommentData) => void;\n\n /**\n * The event handler called when clicking on the author.\n */\n onAuthorClick?: (userId: string, event: MouseEvent<HTMLElement>) => void;\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: (\n mention: MentionData,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: (\n args: CommentAttachmentArgs,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * Add (or change) items to display in the comment's dropdown.\n */\n dropdownItems?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the comment's content.\n */\n children?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n actionsClassName?: string;\n\n /**\n * @internal\n */\n internalDropdownItems?: ReactNode;\n}\n\nexport interface CommentAvatarProps\n extends Omit<ComponentProps<\"div\">, \"children\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport interface CommentAuthorProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The user ID to display the author for.\n */\n userId: string;\n}\n\nexport interface CommentDateProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nfunction CommentAvatar(props: CommentAvatarProps) {\n return <Avatar {...props} />;\n}\n\nfunction CommentAuthor(props: CommentAuthorProps) {\n return <User {...props} />;\n}\n\nfunction CommentDate({ locale, date, className, ...props }: CommentDateProps) {\n return (\n <Timestamp\n locale={locale}\n date={date}\n className={cn(\"lb-date\", className)}\n {...(props as Omit<ComponentProps<\"time\">, \"children\" | \"title\">)}\n />\n );\n}\n\nexport interface CommentDropdownItemProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\"> {\n /**\n * An optional icon displayed in this dropdown item.\n */\n icon?: ReactNode;\n\n /**\n * The event handler called when the dropdown item is selected.\n */\n onSelect?: (event: Event) => void;\n}\n\ninterface CommentReactionButtonProps\n extends ComponentPropsWithoutRef<typeof Button> {\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ninterface CommentReactionProps extends ComponentPropsWithoutRef<\"button\"> {\n comment: CommentData;\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ntype CommentNonInteractiveReactionProps = Omit<CommentReactionProps, \"comment\">;\n\ninterface CommentAttachmentProps extends ComponentProps<typeof FileAttachment> {\n attachment: CommentAttachment;\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n}\n\ninterface CommentMentionProps\n extends CommentBodyMentionProps,\n CommentPrimitiveMentionProps {\n overrides?: CommentProps[\"overrides\"];\n}\n\nfunction CommentUserMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const currentId = useCurrentUserId();\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={mention.id === currentId ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nfunction CommentGroupMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={isMember ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentMention({ mention, ...props }: CommentMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <CommentUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <CommentGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n const { Anchor } = useComponents();\n\n return (\n <CommentPrimitive.Link\n className={cn(\"lb-comment-link\", className)}\n href={href}\n {...props}\n asChild\n >\n <Anchor {...props}>{children}</Anchor>\n </CommentPrimitive.Link>\n );\n}\n\nexport function CommentNonInteractiveLink({\n href: _href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <span className={cn(\"lb-comment-link\", className)} {...props}>\n {children}\n </span>\n );\n}\n\nconst CommentReactionButton = forwardRef<\n HTMLButtonElement,\n CommentReactionButtonProps\n>(({ reaction, overrides, className, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n return (\n <CustomButton\n className={cn(\"lb-comment-reaction\", className)}\n variant=\"outline\"\n aria-label={$.COMMENT_REACTION_DESCRIPTION(\n reaction.emoji,\n reaction.users.length\n )}\n {...props}\n ref={forwardedRef}\n >\n <Emoji className=\"lb-comment-reaction-emoji\" emoji={reaction.emoji} />\n <span className=\"lb-comment-reaction-count\">{reaction.users.length}</span>\n </CustomButton>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n const $ = useOverrides(overrides);\n const tooltipContent = useMemo(\n () => (\n <span>\n {$.COMMENT_REACTION_LIST(\n <List\n values={reaction.users.map((users) => (\n <User key={users.id} userId={users.id} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={REACTIONS_TRUNCATE}\n locale={$.locale}\n />,\n reaction.emoji,\n reaction.users.length\n )}\n </span>\n ),\n [$, reaction]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handlePressedChange = useCallback(\n (isPressed: boolean) => {\n if (isPressed) {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n } else {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n }\n },\n [addReaction, comment.threadId, comment.id, reaction.emoji, removeReaction]\n );\n\n return (\n <Tooltip\n content={tooltipContent}\n multiline\n className=\"lb-comment-reaction-tooltip\"\n >\n <TogglePrimitive.Root\n asChild\n pressed={isActive}\n onPressedChange={handlePressedChange}\n onClick={stopPropagation}\n disabled={disabled}\n ref={forwardedRef}\n >\n <CommentReactionButton\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n );\n});\n\nexport const CommentNonInteractiveReaction = forwardRef<\n HTMLButtonElement,\n CommentNonInteractiveReactionProps\n>(({ reaction, overrides, ...props }, forwardedRef) => {\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n\n return (\n <CommentReactionButton\n disableable={false}\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n ref={forwardedRef}\n />\n );\n});\n\nfunction openAttachment({ attachment, url }: CommentAttachmentArgs) {\n // Open the attachment in a new tab if the attachment is a PDF,\n // an image, a video, or audio. Otherwise, download it.\n if (\n attachment.mimeType === \"application/pdf\" ||\n attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\") ||\n attachment.mimeType.startsWith(\"audio/\")\n ) {\n window.open(url, \"_blank\");\n } else {\n download(url, attachment.name);\n }\n}\n\nfunction CommentMediaAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <MediaAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nfunction CommentFileAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nexport function CommentNonInteractiveFileAttachment({\n className,\n ...props\n}: CommentAttachmentProps) {\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n allowMediaPreview={false}\n {...props}\n />\n );\n}\n\nconst CommentDropdownItem = forwardRef<\n HTMLDivElement,\n CommentDropdownItemProps\n>(({ children, icon, onSelect, onClick, ...props }, forwardedRef) => {\n const handleClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n }\n },\n [onClick]\n );\n\n return (\n <DropdownItem\n icon={icon}\n onSelect={onSelect}\n onClick={handleClick}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </DropdownItem>\n );\n});\n\n/**\n * Displays a single comment.\n *\n * @example\n * <>\n * {thread.comments.map((comment) => (\n * <Comment key={comment.id} comment={comment} />\n * ))}\n * </>\n */\nexport const Comment = Object.assign(\n forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n showAttachments = true,\n showComposerFormattingControls = true,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onCommentEdit,\n onCommentDelete,\n dropdownItems,\n overrides,\n components,\n additionalContent,\n avatar,\n author,\n date,\n className,\n actions,\n actionsClassName,\n internalDropdownItems,\n children,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteRoomComment(comment.roomId);\n const editComment = useEditRoomComment(comment.roomId);\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const $ = useOverrides(overrides);\n const [isEditing, setEditing] = useState(false);\n const [isTarget, setTarget] = useState(false);\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const [isReactionActionOpen, setReactionActionOpen] = useState(false);\n const { mediaAttachments, fileAttachments } = useMemo(() => {\n return separateMediaAttachments(comment.attachments);\n }, [comment.attachments]);\n\n const permissions = useRoomPermissions(comment.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleEdit = useCallback(() => {\n setEditing(true);\n }, []);\n\n const handleEditCancel = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n setEditing(false);\n },\n []\n );\n\n const handleEditSubmit = useCallback(\n (\n { body, attachments }: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentEdit?.(comment);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n event.preventDefault();\n\n setEditing(false);\n editComment({\n commentId: comment.id,\n threadId: comment.threadId,\n body,\n attachments,\n });\n },\n [comment, editComment, onCommentEdit]\n );\n\n const handleDelete = useCallback(() => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentDelete?.(comment);\n\n deleteComment({\n commentId: comment.id,\n threadId: comment.threadId,\n });\n }, [comment, deleteComment, onCommentDelete]);\n\n const handleAuthorClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n onAuthorClick?.(comment.userId, event);\n },\n [comment.userId, onAuthorClick]\n );\n\n const handleReactionSelect = useCallback(\n (emoji: string) => {\n const reactionIndex = comment.reactions.findIndex(\n (reaction) => reaction.emoji === emoji\n );\n\n if (\n reactionIndex >= 0 &&\n currentUserId &&\n comment.reactions[reactionIndex]?.users.some(\n (user) => user.id === currentUserId\n )\n ) {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n } else {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n }\n },\n [\n addReaction,\n comment.id,\n comment.reactions,\n comment.threadId,\n removeReaction,\n currentUserId,\n ]\n );\n\n useEffect(() => {\n const isWindowDefined = typeof window !== \"undefined\";\n if (!isWindowDefined) return;\n\n const hash = window.location.hash;\n const commentId = hash.slice(1);\n\n if (commentId === comment.id) {\n setTarget(true);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!showDeleted && !comment.body) {\n return null;\n }\n\n const commentDropdownItems =\n comment.userId === currentUserId ? (\n <>\n <CommentDropdownItem onSelect={handleEdit} icon={<EditIcon />}>\n {$.COMMENT_EDIT}\n </CommentDropdownItem>\n <CommentDropdownItem onSelect={handleDelete} icon={<DeleteIcon />}>\n {$.COMMENT_DELETE}\n </CommentDropdownItem>\n </>\n ) : null;\n const defaultDropdownItems =\n internalDropdownItems || commentDropdownItems ? (\n <>\n {internalDropdownItems}\n {commentDropdownItems}\n </>\n ) : null;\n\n const dropdownContent =\n typeof dropdownItems === \"function\" ? (\n dropdownItems({ children: defaultDropdownItems, comment })\n ) : defaultDropdownItems || dropdownItems ? (\n <>\n {defaultDropdownItems}\n {dropdownItems}\n </>\n ) : null;\n\n let content: ReactNode;\n\n if (isEditing) {\n content = (\n <Composer\n className=\"lb-comment-composer\"\n onComposerSubmit={handleEditSubmit}\n defaultValue={comment.body}\n defaultAttachments={comment.attachments}\n autoFocus\n showAttribution={false}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n actions={\n <>\n <Tooltip\n content={$.COMMENT_EDIT_COMPOSER_CANCEL}\n aria-label={$.COMMENT_EDIT_COMPOSER_CANCEL}\n >\n <Button\n className=\"lb-composer-action\"\n onClick={handleEditCancel}\n icon={<CrossIcon />}\n />\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut=\"Enter\"\n >\n <ComposerPrimitive.Submit asChild>\n <Button\n variant=\"primary\"\n className=\"lb-composer-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_EDIT_COMPOSER_SAVE}\n icon={<CheckIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n roomId={comment.roomId}\n />\n );\n } else {\n content = comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n body={comment.body}\n components={{\n Mention: ({ mention }) => (\n <CommentMention\n mention={mention}\n onClick={(event) => onMentionClick?.(mention, event)}\n overrides={overrides}\n />\n ),\n Link: CommentLink,\n }}\n />\n {additionalContent}\n {showAttachments &&\n (mediaAttachments.length > 0 || fileAttachments.length > 0) ? (\n <div className=\"lb-comment-attachments\">\n {mediaAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {mediaAttachments.map((attachment) => (\n <CommentMediaAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n {fileAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {fileAttachments.map((attachment) => (\n <CommentFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n {showReactions && comment.reactions.length > 0 && (\n <div className=\"lb-comment-reactions\">\n {comment.reactions.map((reaction) => (\n <CommentReaction\n key={reaction.emoji}\n comment={comment}\n reaction={reaction}\n overrides={overrides}\n disabled={!canComment}\n />\n ))}\n {canComment ? (\n <EmojiPicker onEmojiSelect={handleReactionSelect}>\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-reaction lb-comment-reaction-add\"\n variant=\"outline\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n );\n\n content =\n typeof children === \"function\"\n ? children({ comment, children: content })\n : (children ?? content);\n }\n\n return (\n <TooltipProvider>\n <ComponentsProvider components={components}>\n <div\n id={comment.id}\n className={cn(\n \"lb-root lb-comment\",\n indentContent && \"lb-comment:indent-content\",\n showActions === \"hover\" && \"lb-comment:show-actions-hover\",\n (isMoreActionOpen || isReactionActionOpen) &&\n \"lb-comment:action-open\",\n className\n )}\n data-deleted={!comment.body ? \"\" : undefined}\n data-editing={isEditing ? \"\" : undefined}\n // In some cases, `:target` doesn't work as expected so we also define it manually.\n data-target={isTarget ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={mergedRefs}\n >\n <div className=\"lb-comment-header\">\n <div className=\"lb-comment-details\">\n {avatar ? (\n <div\n className=\"lb-comment-avatar\"\n onClick={handleAuthorClick}\n >\n {avatar}\n </div>\n ) : (\n <Avatar\n className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n <span className=\"lb-comment-details-labels\">\n {author ? (\n <span\n className=\"lb-comment-author\"\n onClick={handleAuthorClick}\n >\n {author}\n </span>\n ) : (\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n {date ? (\n <span className=\"lb-comment-date\">{date}</span>\n ) : (\n <span className=\"lb-comment-date\">\n <CommentDate\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-comment-date-created\"\n />\n {comment.editedAt && comment.body && (\n <>\n {\" \"}\n <span className=\"lb-comment-date-edited\">\n {$.COMMENT_EDITED}\n </span>\n </>\n )}\n </span>\n )}\n </span>\n </div>\n {showActions && !isEditing && (\n <div className={cn(\"lb-comment-actions\", actionsClassName)}>\n {actions ?? null}\n {showReactions && canComment ? (\n <EmojiPicker\n onEmojiSelect={handleReactionSelect}\n onOpenChange={setReactionActionOpen}\n >\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n {dropdownContent ? (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={dropdownContent}\n >\n <Tooltip content={$.COMMENT_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n disabled={!comment.body}\n onClick={stopPropagation}\n aria-label={$.COMMENT_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n ) : null}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">{content}</div>\n </div>\n </ComponentsProvider>\n </TooltipProvider>\n );\n }\n ),\n {\n /**\n * Displays a dropdown item in the comment's dropdown.\n */\n DropdownItem: CommentDropdownItem,\n\n /**\n * Displays a comment's avatar.\n */\n Avatar: CommentAvatar,\n\n /**\n * Displays a comment's author.\n */\n Author: CommentAuthor,\n\n /**\n * Displays a comment's date.\n */\n Date: CommentDate,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA;AAmKA;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACE;AAAC;AAAA;AACC;AACA;AACkC;AAC7B;AAAA;AAGX;AAwCA;AAA4B;AAC1B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AACb;AACvC;AAEJ;AAAuD;AAC7B;AAAA;AAAA;AAGhC;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AAC7B;AACvB;AAEJ;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAwD;AAGxD;AAAyD;AAGzD;AAAoD;AAE1D;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AAC2C;AAC1C;AACI;AACG;AAEsB;AAAA;AAGnC;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AAKF;AAEA;AAIE;AACA;AACE;AAAC;AAAA;AAC+C;AACtC;AACM;AACH;AACM;AACjB;AACI;AACC;AAEL;AAAoE;AACD;AAAA;AAAA;AAGzE;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACD;AAAC;AAAA;AAGE;AACkB;AACT;AACA;AAAA;AACZ;AACS;AACM;AAEnB;AAEU;AAGd;AACE;AAAsB;AAGxB;AAA4B;AAExB;AACE;AAAY;AACQ;AACC;AACH;AACjB;AAED;AAAe;AACK;AACC;AACH;AACjB;AACH;AACF;AAC0E;AAG5E;AACE;AAAC;AAAA;AACU;AACA;AACC;AAEV;AAAiB;AAAhB;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEL;AAAC;AAAA;AAC4B;AAC3B;AACA;AACI;AAAA;AACN;AAAA;AACF;AAAA;AAGN;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACE;AAAC;AAAA;AACc;AACc;AAC3B;AACA;AACI;AACC;AAAA;AAGX;AAEA;AAGE;AAME;AAAyB;AAEzB;AAA6B;AAEjC;AAEA;AAAgC;AAC9B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEA;AAA+B;AAC7B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEO;AAA6C;AAClD;AAEF;AACE;AACE;AAAC;AAAA;AACiD;AAC7B;AACf;AAAA;AAGV;AAEA;AAIE;AAAoB;AAEhB;AAEA;AACE;AAAsB;AACxB;AACF;AACQ;AAGV;AACE;AAAC;AAAA;AACC;AACA;AACS;AACL;AACC;AAEJ;AAAA;AAGP;AAYO;AAAuB;AAC5B;AAEI;AACE;AACgB;AAChB;AACc;AACE;AACE;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAmD;AAGrD;AACA;AAMA;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAMrB;AAEA;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAAY;AACS;AACD;AAClB;AACA;AACD;AACH;AACoC;AAGtC;AAEE;AAEA;AAAc;AACO;AACD;AACnB;AAGH;AAA0B;AAEtB;AAAqC;AACvC;AAC8B;AAGhC;AAA6B;AAEzB;AAAwC;AACL;AAGnC;AAG0C;AAChB;AAGxB;AAAe;AACK;AACC;AACnB;AACD;AAED;AAAY;AACQ;AACC;AACnB;AACD;AACH;AACF;AACA;AACE;AACQ;AACA;AACA;AACR;AACA;AACF;AAGF;AACE;AACA;AAAsB;AAEtB;AACA;AAEA;AACE;AAAc;AAChB;AAGF;AACE;AAAO;AAGT;AAGM;AAEA;AAGA;AAGN;AAGO;AAAA;AACA;AAIP;AAKO;AAAA;AACA;AAIP;AAEA;AACE;AACE;AAAC;AAAA;AACW;AACQ;AACI;AACM;AACnB;AACQ;AACjB;AACwB;AAGpB;AAAA;AAAC;AAAA;AACY;AACG;AAEd;AAAC;AAAA;AACW;AACD;AACQ;AAAA;AACnB;AAAA;AACF;AACA;AAAC;AAAA;AACY;AACF;AAGP;AAAC;AAAA;AACS;AACE;AACD;AACK;AACG;AAAA;AAErB;AAAA;AACF;AACF;AAES;AACe;AAC1B;AACgB;AAAA;AAClB;AAGF;AAEI;AAAA;AAAkB;AAAjB;AACW;AACI;AACF;AAER;AAAC;AAAA;AACC;AACmD;AACnD;AAAA;AACF;AAEI;AACR;AAAA;AACF;AACC;AAII;AAGK;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAIE;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAEJ;AAGC;AACC;AAAC;AAAA;AAEC;AACA;AACA;AACW;AAAA;AAJG;AAMjB;AAKO;AAAC;AAAA;AACW;AACF;AACC;AACK;AACO;AAAA;AAK3B;AACN;AASN;AAGmB;AAGrB;AAGM;AAAC;AAAA;AACa;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACtB;AACH;AACC;AAEL;AACE;AACG;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAGC;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAME;AAAA;AAAC;AAAA;AACW;AACI;AACJ;AAAA;AACZ;AAGK;AAAA;AAGD;AACF;AAEJ;AAEJ;AACF;AAGK;AAAW;AAEV;AAAC;AAAA;AACgB;AACD;AAIV;AAAC;AAAA;AACW;AACD;AACK;AACO;AAAA;AAG3B;AAAA;AAEA;AAEF;AAAC;AAAA;AACO;AACQ;AACR;AACG;AAIL;AAAC;AAAA;AACW;AACS;AACV;AACK;AACM;AAAA;AAG1B;AAAA;AAEA;AACN;AAEJ;AAC6C;AAAA;AAAA;AAGnD;AAEJ;AACF;AACA;AAAA;AAAA;AAAA;AAIgB;AAAA;AAAA;AAAA;AAKN;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;AAKF;AAEV;;"}
1
+ {"version":3,"file":"Comment.js","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n assertNever,\n type BaseMetadata,\n type CommentAttachment,\n type CommentData,\n type CommentReaction as CommentReactionData,\n type DCM,\n type GroupMentionData,\n MENTION_CHARACTER,\n type MentionData,\n Permission,\n} from \"@liveblocks/core\";\nimport {\n useAddRoomCommentReaction,\n useDeleteRoomComment,\n useEditRoomComment,\n useRemoveRoomCommentReaction,\n useRoomAttachmentUrl,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport { Toggle as TogglePrimitive } from \"radix-ui\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n ComponentsProvider,\n type GlobalComponents,\n useComponents,\n} from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { CrossIcon } from \"../icons/Cross\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EditIcon } from \"../icons/Edit\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { EmojiPlusIcon } from \"../icons/EmojiPlus\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport type { ComposerSubmitComment } from \"../primitives\";\nimport * as CommentPrimitive from \"../primitives/Comment\";\nimport type {\n CommentBodyLinkProps,\n CommentBodyMentionProps,\n CommentLinkProps,\n CommentMentionProps as CommentPrimitiveMentionProps,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { CommentAttachmentArgs } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { download } from \"../utils/download\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { useRefs } from \"../utils/use-refs\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport {\n FileAttachment,\n MediaAttachment,\n separateMediaAttachments,\n} from \"./internal/Attachment\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button, CustomButton } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { List } from \"./internal/List\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps<CM extends BaseMetadata = DCM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The comment to display.\n */\n comment: CommentData<CM>;\n\n /**\n * The comment's avatar.\n * Can be combined with `Comment.Avatar` to easily follow default styles.\n */\n avatar?: ReactNode;\n\n /**\n * The comment's author.\n * Can be combined with `Comment.Author` to easily follow default styles.\n */\n author?: ReactNode;\n\n /**\n * The comment's date.\n * Can be combined with `Comment.Date` to easily follow default styles,\n * or the `Timestamp` primitive for more control.\n */\n date?: ReactNode;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n\n /**\n * Whether to show the comment if it was deleted. If set to `false`, it will render deleted comments as `null`.\n */\n showDeleted?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show the composer's formatting controls when editing the comment.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comment's content.\n */\n indentContent?: boolean;\n\n /**\n * Additional content to display below the comment's body.\n */\n additionalContent?: ReactNode;\n\n /**\n * The event handler called when the comment is edited.\n */\n onCommentEdit?: (comment: CommentData) => void;\n\n /**\n * The event handler called when the comment is deleted.\n */\n onCommentDelete?: (comment: CommentData) => void;\n\n /**\n * The event handler called when clicking on the author.\n */\n onAuthorClick?: (userId: string, event: MouseEvent<HTMLElement>) => void;\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: (\n mention: MentionData,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: (\n args: CommentAttachmentArgs,\n event: MouseEvent<HTMLElement>\n ) => void;\n\n /**\n * Add (or change) items to display in the comment's dropdown.\n */\n dropdownItems?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the comment's content.\n */\n children?:\n | ReactNode\n | ((props: PropsWithChildren<{ comment: CommentData<CM> }>) => ReactNode);\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n actionsClassName?: string;\n\n /**\n * @internal\n */\n internalDropdownItems?: ReactNode;\n}\n\nexport interface CommentAvatarProps\n extends Omit<ComponentProps<\"div\">, \"children\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport interface CommentAuthorProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The user ID to display the author for.\n */\n userId: string;\n}\n\nexport interface CommentDateProps\n extends Omit<ComponentProps<\"span\">, \"children\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nfunction CommentAvatar(props: CommentAvatarProps) {\n return <Avatar {...props} />;\n}\n\nfunction CommentAuthor(props: CommentAuthorProps) {\n return <User {...props} />;\n}\n\nfunction CommentDate({ locale, date, className, ...props }: CommentDateProps) {\n return (\n <Timestamp\n locale={locale}\n date={date}\n className={cn(\"lb-date\", className)}\n {...(props as Omit<ComponentProps<\"time\">, \"children\" | \"title\">)}\n />\n );\n}\n\nexport interface CommentDropdownItemProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\"> {\n /**\n * An optional icon displayed in this dropdown item.\n */\n icon?: ReactNode;\n\n /**\n * The event handler called when the dropdown item is selected.\n */\n onSelect?: (event: Event) => void;\n}\n\ninterface CommentReactionButtonProps\n extends ComponentPropsWithoutRef<typeof Button> {\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ninterface CommentReactionProps extends ComponentPropsWithoutRef<\"button\"> {\n comment: CommentData;\n reaction: CommentReactionData;\n overrides?: Partial<GlobalOverrides & CommentOverrides>;\n}\n\ntype CommentNonInteractiveReactionProps = Omit<CommentReactionProps, \"comment\">;\n\ninterface CommentAttachmentProps extends ComponentProps<typeof FileAttachment> {\n attachment: CommentAttachment;\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n}\n\ninterface CommentMentionProps\n extends CommentBodyMentionProps,\n CommentPrimitiveMentionProps {\n overrides?: CommentProps[\"overrides\"];\n}\n\nfunction CommentUserMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const currentId = useCurrentUserId();\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={mention.id === currentId ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nfunction CommentGroupMention({\n mention,\n className,\n ...props\n}: CommentMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <CommentPrimitive.Mention\n className={cn(\"lb-mention lb-comment-mention\", className)}\n data-self={isMember ? \"\" : undefined}\n {...props}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentMention({ mention, ...props }: CommentMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <CommentUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <CommentGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n const { Anchor } = useComponents();\n\n return (\n <CommentPrimitive.Link\n className={cn(\"lb-comment-link\", className)}\n href={href}\n {...props}\n asChild\n >\n <Anchor {...props}>{children}</Anchor>\n </CommentPrimitive.Link>\n );\n}\n\nexport function CommentNonInteractiveLink({\n href: _href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <span className={cn(\"lb-comment-link\", className)} {...props}>\n {children}\n </span>\n );\n}\n\nconst CommentReactionButton = forwardRef<\n HTMLButtonElement,\n CommentReactionButtonProps\n>(({ reaction, overrides, className, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n return (\n <CustomButton\n className={cn(\"lb-comment-reaction\", className)}\n variant=\"outline\"\n aria-label={$.COMMENT_REACTION_DESCRIPTION(\n reaction.emoji,\n reaction.users.length\n )}\n {...props}\n ref={forwardedRef}\n >\n <Emoji className=\"lb-comment-reaction-emoji\" emoji={reaction.emoji} />\n <span className=\"lb-comment-reaction-count\">{reaction.users.length}</span>\n </CustomButton>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n const $ = useOverrides(overrides);\n const tooltipContent = useMemo(\n () => (\n <span>\n {$.COMMENT_REACTION_LIST(\n <List\n values={reaction.users.map((users) => (\n <User key={users.id} userId={users.id} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={REACTIONS_TRUNCATE}\n locale={$.locale}\n />,\n reaction.emoji,\n reaction.users.length\n )}\n </span>\n ),\n [$, reaction]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handlePressedChange = useCallback(\n (isPressed: boolean) => {\n if (isPressed) {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n } else {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji: reaction.emoji,\n });\n }\n },\n [addReaction, comment.threadId, comment.id, reaction.emoji, removeReaction]\n );\n\n return (\n <Tooltip\n content={tooltipContent}\n multiline\n className=\"lb-comment-reaction-tooltip\"\n >\n <TogglePrimitive.Root\n asChild\n pressed={isActive}\n onPressedChange={handlePressedChange}\n onClick={stopPropagation}\n disabled={disabled}\n ref={forwardedRef}\n >\n <CommentReactionButton\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n );\n});\n\nexport const CommentNonInteractiveReaction = forwardRef<\n HTMLButtonElement,\n CommentNonInteractiveReactionProps\n>(({ reaction, overrides, ...props }, forwardedRef) => {\n const currentId = useCurrentUserId();\n const isActive = useMemo(() => {\n return reaction.users.some((users) => users.id === currentId);\n }, [currentId, reaction]);\n\n return (\n <CommentReactionButton\n disableable={false}\n data-self={isActive ? \"\" : undefined}\n reaction={reaction}\n overrides={overrides}\n {...props}\n ref={forwardedRef}\n />\n );\n});\n\nfunction openAttachment({ attachment, url }: CommentAttachmentArgs) {\n // Open the attachment in a new tab if the attachment is a PDF,\n // an image, a video, or audio. Otherwise, download it.\n if (\n attachment.mimeType === \"application/pdf\" ||\n attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\") ||\n attachment.mimeType.startsWith(\"audio/\")\n ) {\n window.open(url, \"_blank\");\n } else {\n download(url, attachment.name);\n }\n}\n\nfunction CommentMediaAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <MediaAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nfunction CommentFileAttachment({\n attachment,\n onAttachmentClick,\n roomId,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps & {\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n if (!url) {\n return;\n }\n\n const args: CommentAttachmentArgs = { attachment, url };\n\n onAttachmentClick?.(args, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n openAttachment(args);\n },\n [attachment, onAttachmentClick, url]\n );\n\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n roomId={roomId}\n />\n );\n}\n\nexport function CommentNonInteractiveFileAttachment({\n className,\n ...props\n}: CommentAttachmentProps) {\n return (\n <FileAttachment\n className={cn(\"lb-comment-attachment\", className)}\n allowMediaPreview={false}\n {...props}\n />\n );\n}\n\nconst CommentDropdownItem = forwardRef<\n HTMLDivElement,\n CommentDropdownItemProps\n>(({ children, icon, onSelect, onClick, ...props }, forwardedRef) => {\n const handleClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n }\n },\n [onClick]\n );\n\n return (\n <DropdownItem\n icon={icon}\n onSelect={onSelect}\n onClick={handleClick}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </DropdownItem>\n );\n});\n\n/**\n * Displays a single comment.\n *\n * @example\n * <>\n * {thread.comments.map((comment) => (\n * <Comment key={comment.id} comment={comment} />\n * ))}\n * </>\n */\nexport const Comment = Object.assign(\n forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n showAttachments = true,\n showComposerFormattingControls = true,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onCommentEdit,\n onCommentDelete,\n dropdownItems,\n overrides,\n components,\n additionalContent,\n avatar,\n author,\n date,\n className,\n actions,\n actionsClassName,\n internalDropdownItems,\n children,\n ...props\n },\n forwardedRef\n ) => {\n const bodyId = `${comment.id}:body`;\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteRoomComment(comment.roomId);\n const editComment = useEditRoomComment(comment.roomId);\n const addReaction = useAddRoomCommentReaction(comment.roomId);\n const removeReaction = useRemoveRoomCommentReaction(comment.roomId);\n const $ = useOverrides(overrides);\n const [isEditing, setEditing] = useState(false);\n const [isTarget, setTarget] = useState(false);\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const [isReactionActionOpen, setReactionActionOpen] = useState(false);\n const { mediaAttachments, fileAttachments } = useMemo(() => {\n return separateMediaAttachments(comment.attachments);\n }, [comment.attachments]);\n\n const permissions = useRoomPermissions(comment.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleEdit = useCallback(() => {\n setEditing(true);\n }, []);\n\n const handleEditCancel = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n setEditing(false);\n },\n []\n );\n\n const handleEditSubmit = useCallback(\n (\n { body, attachments }: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentEdit?.(comment);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n event.preventDefault();\n\n setEditing(false);\n editComment({\n commentId: comment.id,\n threadId: comment.threadId,\n body,\n attachments,\n });\n },\n [comment, editComment, onCommentEdit]\n );\n\n const handleDelete = useCallback(() => {\n // TODO: Add a way to preventDefault from within this callback, to override the default behavior (e.g. showing a confirmation dialog)\n onCommentDelete?.(comment);\n\n deleteComment({\n commentId: comment.id,\n threadId: comment.threadId,\n });\n }, [comment, deleteComment, onCommentDelete]);\n\n const handleAuthorClick = useCallback(\n (event: MouseEvent<HTMLElement>) => {\n onAuthorClick?.(comment.userId, event);\n },\n [comment.userId, onAuthorClick]\n );\n\n const handleReactionSelect = useCallback(\n (emoji: string) => {\n const reactionIndex = comment.reactions.findIndex(\n (reaction) => reaction.emoji === emoji\n );\n\n if (\n reactionIndex >= 0 &&\n currentUserId &&\n comment.reactions[reactionIndex]?.users.some(\n (user) => user.id === currentUserId\n )\n ) {\n removeReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n } else {\n addReaction({\n threadId: comment.threadId,\n commentId: comment.id,\n emoji,\n });\n }\n },\n [\n addReaction,\n comment.id,\n comment.reactions,\n comment.threadId,\n removeReaction,\n currentUserId,\n ]\n );\n\n useEffect(() => {\n const isWindowDefined = typeof window !== \"undefined\";\n if (!isWindowDefined) return;\n\n const hash = window.location.hash;\n const commentId = hash.slice(1);\n\n if (commentId === comment.id) {\n setTarget(true);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!showDeleted && !comment.body) {\n return null;\n }\n\n const commentDropdownItems =\n comment.userId === currentUserId ? (\n <>\n <CommentDropdownItem onSelect={handleEdit} icon={<EditIcon />}>\n {$.COMMENT_EDIT}\n </CommentDropdownItem>\n <CommentDropdownItem onSelect={handleDelete} icon={<DeleteIcon />}>\n {$.COMMENT_DELETE}\n </CommentDropdownItem>\n </>\n ) : null;\n const defaultDropdownItems =\n internalDropdownItems || commentDropdownItems ? (\n <>\n {internalDropdownItems}\n {commentDropdownItems}\n </>\n ) : null;\n\n const dropdownContent =\n typeof dropdownItems === \"function\" ? (\n dropdownItems({ children: defaultDropdownItems, comment })\n ) : defaultDropdownItems || dropdownItems ? (\n <>\n {defaultDropdownItems}\n {dropdownItems}\n </>\n ) : null;\n\n let content: ReactNode;\n\n if (isEditing) {\n content = (\n <Composer\n className=\"lb-comment-composer\"\n onComposerSubmit={handleEditSubmit}\n defaultValue={comment.body}\n defaultAttachments={comment.attachments}\n autoFocus\n showAttribution={false}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n actions={\n <>\n <Tooltip\n content={$.COMMENT_EDIT_COMPOSER_CANCEL}\n aria-label={$.COMMENT_EDIT_COMPOSER_CANCEL}\n >\n <Button\n className=\"lb-composer-action\"\n onClick={handleEditCancel}\n icon={<CrossIcon />}\n />\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut=\"Enter\"\n >\n <ComposerPrimitive.Submit asChild>\n <Button\n variant=\"primary\"\n className=\"lb-composer-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_EDIT_COMPOSER_SAVE}\n icon={<CheckIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n roomId={comment.roomId}\n />\n );\n } else {\n content = comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n id={bodyId}\n body={comment.body}\n components={{\n Mention: ({ mention }) => (\n <CommentMention\n mention={mention}\n onClick={(event) => onMentionClick?.(mention, event)}\n overrides={overrides}\n />\n ),\n Link: CommentLink,\n }}\n />\n {additionalContent}\n {showAttachments &&\n (mediaAttachments.length > 0 || fileAttachments.length > 0) ? (\n <div className=\"lb-comment-attachments\">\n {mediaAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {mediaAttachments.map((attachment) => (\n <CommentMediaAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n {fileAttachments.length > 0 ? (\n <div className=\"lb-attachments\">\n {fileAttachments.map((attachment) => (\n <CommentFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n onAttachmentClick={onAttachmentClick}\n roomId={comment.roomId}\n />\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n {showReactions && comment.reactions.length > 0 && (\n <div className=\"lb-comment-reactions\">\n {comment.reactions.map((reaction) => (\n <CommentReaction\n key={reaction.emoji}\n comment={comment}\n reaction={reaction}\n overrides={overrides}\n disabled={!canComment}\n />\n ))}\n {canComment ? (\n <EmojiPicker onEmojiSelect={handleReactionSelect}>\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-reaction lb-comment-reaction-add\"\n variant=\"outline\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n );\n\n content =\n typeof children === \"function\"\n ? children({ comment, children: content })\n : (children ?? content);\n }\n\n return (\n <TooltipProvider>\n <ComponentsProvider components={components}>\n <div\n role=\"article\"\n id={comment.id}\n className={cn(\n \"lb-root lb-comment\",\n indentContent && \"lb-comment:indent-content\",\n showActions === \"hover\" && \"lb-comment:show-actions-hover\",\n (isMoreActionOpen || isReactionActionOpen) &&\n \"lb-comment:action-open\",\n className\n )}\n data-deleted={!comment.body ? \"\" : undefined}\n data-editing={isEditing ? \"\" : undefined}\n // In some cases, `:target` doesn't work as expected so we also define it manually.\n data-target={isTarget ? \"\" : undefined}\n aria-labelledby={bodyId}\n dir={$.dir}\n {...props}\n ref={mergedRefs}\n >\n <div className=\"lb-comment-header\">\n <div className=\"lb-comment-details\">\n {avatar ? (\n <div\n className=\"lb-comment-avatar\"\n onClick={handleAuthorClick}\n >\n {avatar}\n </div>\n ) : (\n <Avatar\n className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n <span className=\"lb-comment-details-labels\">\n {author ? (\n <span\n className=\"lb-comment-author\"\n onClick={handleAuthorClick}\n >\n {author}\n </span>\n ) : (\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n )}\n {date ? (\n <span className=\"lb-comment-date\">{date}</span>\n ) : (\n <span className=\"lb-comment-date\">\n <CommentDate\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-comment-date-created\"\n />\n {comment.editedAt && comment.body && (\n <>\n {\" \"}\n <span className=\"lb-comment-date-edited\">\n {$.COMMENT_EDITED}\n </span>\n </>\n )}\n </span>\n )}\n </span>\n </div>\n {showActions && !isEditing && (\n <div className={cn(\"lb-comment-actions\", actionsClassName)}>\n {actions ?? null}\n {showReactions && canComment ? (\n <EmojiPicker\n onEmojiSelect={handleReactionSelect}\n onOpenChange={setReactionActionOpen}\n >\n <Tooltip content={$.COMMENT_ADD_REACTION}>\n <EmojiPickerTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={$.COMMENT_ADD_REACTION}\n icon={<EmojiPlusIcon />}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n ) : null}\n {dropdownContent ? (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={dropdownContent}\n >\n <Tooltip content={$.COMMENT_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-comment-action\"\n disabled={!comment.body}\n onClick={stopPropagation}\n aria-label={$.COMMENT_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n ) : null}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">{content}</div>\n </div>\n </ComponentsProvider>\n </TooltipProvider>\n );\n }\n ) as <CM extends BaseMetadata = DCM>(\n props: CommentProps<CM> & RefAttributes<HTMLDivElement>\n ) => JSX.Element,\n {\n /**\n * Displays a dropdown item in the comment's dropdown.\n */\n DropdownItem: CommentDropdownItem,\n\n /**\n * Displays a comment's avatar.\n */\n Avatar: CommentAvatar,\n\n /**\n * Displays a comment's author.\n */\n Author: CommentAuthor,\n\n /**\n * Displays a comment's date.\n */\n Date: CommentDate,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4FA;AAmKA;AACE;AACF;AAEA;AACE;AACF;AAEA;AACE;AACE;AAAC;AAAA;AACC;AACA;AACkC;AAC7B;AAAA;AAGX;AAwCA;AAA4B;AAC1B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AACb;AACvC;AAEJ;AAAuD;AAC7B;AAAA;AAAA;AAGhC;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AACyD;AAC7B;AACvB;AAEJ;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAwD;AAGxD;AAAyD;AAGzD;AAAoD;AAE1D;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAkB;AAAjB;AAC2C;AAC1C;AACI;AACG;AAEsB;AAAA;AAGnC;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AAKF;AAEA;AAIE;AACA;AACE;AAAC;AAAA;AAC+C;AACtC;AACM;AACH;AACM;AACjB;AACI;AACC;AAEL;AAAoE;AACD;AAAA;AAAA;AAGzE;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACD;AAAC;AAAA;AAGE;AACkB;AACT;AACA;AAAA;AACZ;AACS;AACM;AAEnB;AAEU;AAGd;AACE;AAAsB;AAGxB;AAA4B;AAExB;AACE;AAAY;AACQ;AACC;AACH;AACjB;AAED;AAAe;AACK;AACC;AACH;AACjB;AACH;AACF;AAC0E;AAG5E;AACE;AAAC;AAAA;AACU;AACA;AACC;AAEV;AAAiB;AAAhB;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEL;AAAC;AAAA;AAC4B;AAC3B;AACA;AACI;AAAA;AACN;AAAA;AACF;AAAA;AAGN;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACE;AAAC;AAAA;AACc;AACc;AAC3B;AACA;AACI;AACC;AAAA;AAGX;AAEA;AAGE;AAME;AAAyB;AAEzB;AAA6B;AAEjC;AAEA;AAAgC;AAC9B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEA;AAA+B;AAC7B;AACA;AACA;AACA;AACA;AAEF;AAGE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACE;AAAC;AAAA;AACiD;AAC5C;AACJ;AACA;AAC6B;AAC7B;AAAA;AAGN;AAEO;AAA6C;AAClD;AAEF;AACE;AACE;AAAC;AAAA;AACiD;AAC7B;AACf;AAAA;AAGV;AAEA;AAIE;AAAoB;AAEhB;AAEA;AACE;AAAsB;AACxB;AACF;AACQ;AAGV;AACE;AAAC;AAAA;AACC;AACA;AACS;AACL;AACC;AAEJ;AAAA;AAGP;AAYO;AAAuB;AAC5B;AAEI;AACE;AACgB;AAChB;AACc;AACE;AACE;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAmD;AAGrD;AACA;AAMA;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAMrB;AAEA;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAAY;AACS;AACD;AAClB;AACA;AACD;AACH;AACoC;AAGtC;AAEE;AAEA;AAAc;AACO;AACD;AACnB;AAGH;AAA0B;AAEtB;AAAqC;AACvC;AAC8B;AAGhC;AAA6B;AAEzB;AAAwC;AACL;AAGnC;AAG0C;AAChB;AAGxB;AAAe;AACK;AACC;AACnB;AACD;AAED;AAAY;AACQ;AACC;AACnB;AACD;AACH;AACF;AACA;AACE;AACQ;AACA;AACA;AACR;AACA;AACF;AAGF;AACE;AACA;AAAsB;AAEtB;AACA;AAEA;AACE;AAAc;AAChB;AAGF;AACE;AAAO;AAGT;AAGM;AAEA;AAGA;AAGN;AAGO;AAAA;AACA;AAIP;AAKO;AAAA;AACA;AAIP;AAEA;AACE;AACE;AAAC;AAAA;AACW;AACQ;AACI;AACM;AACnB;AACQ;AACjB;AACwB;AAGpB;AAAA;AAAC;AAAA;AACY;AACG;AAEd;AAAC;AAAA;AACW;AACD;AACQ;AAAA;AACnB;AAAA;AACF;AACA;AAAC;AAAA;AACY;AACF;AAGP;AAAC;AAAA;AACS;AACE;AACD;AACK;AACG;AAAA;AAErB;AAAA;AACF;AACF;AAES;AACe;AAC1B;AACgB;AAAA;AAClB;AAGF;AAEI;AAAA;AAAkB;AAAjB;AACW;AACN;AACU;AACF;AAER;AAAC;AAAA;AACC;AACmD;AACnD;AAAA;AACF;AAEI;AACR;AAAA;AACF;AACC;AAII;AAGK;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAIE;AAAC;AAAA;AAEC;AACA;AACA;AACgB;AAAA;AAJA;AAQpB;AAEJ;AAGC;AACC;AAAC;AAAA;AAEC;AACA;AACA;AACW;AAAA;AAJG;AAMjB;AAKO;AAAC;AAAA;AACW;AACF;AACC;AACK;AACO;AAAA;AAK3B;AACN;AASN;AAGmB;AAGrB;AAGM;AAAC;AAAA;AACM;AACO;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACZ;AACV;AACH;AACC;AAEL;AACE;AACG;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAGC;AACC;AAAC;AAAA;AACW;AACD;AAER;AAAA;AAGH;AAAC;AAAA;AACW;AACM;AACP;AAAA;AACX;AAME;AAAA;AAAC;AAAA;AACW;AACI;AACJ;AAAA;AACZ;AAGK;AAAA;AAGD;AACF;AAEJ;AAEJ;AACF;AAGK;AAAW;AAEV;AAAC;AAAA;AACgB;AACD;AAIV;AAAC;AAAA;AACW;AACD;AACK;AACO;AAAA;AAG3B;AAAA;AAEA;AAEF;AAAC;AAAA;AACO;AACQ;AACR;AACG;AAIL;AAAC;AAAA;AACW;AACS;AACV;AACK;AACM;AAAA;AAG1B;AAAA;AAEA;AACN;AAEJ;AAC6C;AAAA;AAAA;AAGnD;AAEJ;AACF;AAGA;AAAA;AAAA;AAAA;AAIgB;AAAA;AAAA;AAAA;AAKN;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;AAKF;AAEV;;"}
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var react = require('react');
6
+ var cn = require('../utils/cn.cjs');
7
+ var Avatar = require('./internal/Avatar.cjs');
8
+
9
+
10
+ const CommentPin = react.forwardRef(
11
+ ({
12
+ corner = "bottom-left",
13
+ userId,
14
+ size,
15
+ type = "button",
16
+ className,
17
+ style,
18
+ ...props
19
+ }, forwardedRef) => {
20
+ return /* @__PURE__ */ jsxRuntime.jsx(
21
+ "button",
22
+ {
23
+ className: cn.cn("lb-root lb-comment-pin", className),
24
+ "data-corner": corner,
25
+ style: { "--lb-comment-pin-size": size, ...style },
26
+ type,
27
+ ...props,
28
+ ref: forwardedRef,
29
+ children: userId ? /* @__PURE__ */ jsxRuntime.jsx(Avatar.Avatar, { className: "lb-comment-pin-avatar", userId }) : null
30
+ }
31
+ );
32
+ }
33
+ );
34
+
35
+ exports.CommentPin = CommentPin;
36
+ //# sourceMappingURL=CommentPin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentPin.cjs","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": size, ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;AA8BO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACoC;AACjD;AACI;AACC;AAID;AAAA;AACN;AAGN;;"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { forwardRef } from 'react';
4
+ import { cn } from '../utils/cn.js';
5
+ import { Avatar } from './internal/Avatar.js';
6
+
7
+
8
+ const CommentPin = forwardRef(
9
+ ({
10
+ corner = "bottom-left",
11
+ userId,
12
+ size,
13
+ type = "button",
14
+ className,
15
+ style,
16
+ ...props
17
+ }, forwardedRef) => {
18
+ return /* @__PURE__ */ jsx(
19
+ "button",
20
+ {
21
+ className: cn("lb-root lb-comment-pin", className),
22
+ "data-corner": corner,
23
+ style: { "--lb-comment-pin-size": size, ...style },
24
+ type,
25
+ ...props,
26
+ ref: forwardedRef,
27
+ children: userId ? /* @__PURE__ */ jsx(Avatar, { className: "lb-comment-pin-avatar", userId }) : null
28
+ }
29
+ );
30
+ }
31
+ );
32
+
33
+ export { CommentPin };
34
+ //# sourceMappingURL=CommentPin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentPin.js","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": size, ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;AA8BO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACoC;AACjD;AACI;AACC;AAID;AAAA;AACN;AAGN;;"}
@@ -33,9 +33,7 @@ var Group = require('./internal/Group.cjs');
33
33
  var GroupDescription = require('./internal/GroupDescription.cjs');
34
34
  var Tooltip = require('./internal/Tooltip.cjs');
35
35
  var User = require('./internal/User.cjs');
36
- var PopoverPrimitive = require('@radix-ui/react-popover');
37
36
  var Users = require('../icons/Users.cjs');
38
- var TooltipPrimitive = require('@radix-ui/react-tooltip');
39
37
 
40
38
 
41
39
  function ComposerInsertMentionEditorAction({
@@ -85,7 +83,7 @@ function ComposerInsertEmojiEditorAction({
85
83
  const stopPropagation = react.useCallback((event) => {
86
84
  event.stopPropagation();
87
85
  }, []);
88
- return /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker.EmojiPicker, { onEmojiSelect: insertText, onOpenChange: onPickerOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { content: tooltipLabel ?? label, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
86
+ return /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker.EmojiPicker, { onEmojiSelect: insertText, onOpenChange: onPickerOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { content: tooltipLabel ?? label, children: /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker.EmojiPickerTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
89
87
  Button.Button,
90
88
  {
91
89
  className: cn.cn("lb-composer-editor-action", className),
@@ -537,7 +535,7 @@ const Composer = react.forwardRef(
537
535
  threadId
538
536
  ]
539
537
  );
540
- return /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive.TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
538
+ return /* @__PURE__ */ jsxRuntime.jsx(Tooltip.TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
541
539
  index.Form,
542
540
  {
543
541
  onComposerSubmit: handleComposerSubmit,