@liveblocks/react-ui 2.7.2 → 2.8.0-beta2
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.
- package/dist/_private/index.d.mts +5 -3
- package/dist/_private/index.d.ts +5 -3
- package/dist/_private/index.js +4 -2
- package/dist/_private/index.js.map +1 -1
- package/dist/_private/index.mjs +2 -1
- package/dist/_private/index.mjs.map +1 -1
- package/dist/components/Comment.js +106 -3
- package/dist/components/Comment.js.map +1 -1
- package/dist/components/Comment.mjs +107 -5
- package/dist/components/Comment.mjs.map +1 -1
- package/dist/components/Composer.js +216 -103
- package/dist/components/Composer.js.map +1 -1
- package/dist/components/Composer.mjs +220 -107
- package/dist/components/Composer.mjs.map +1 -1
- package/dist/components/HistoryVersionSummary.js +5 -0
- package/dist/components/HistoryVersionSummary.js.map +1 -1
- package/dist/components/HistoryVersionSummary.mjs +5 -0
- package/dist/components/HistoryVersionSummary.mjs.map +1 -1
- package/dist/components/InboxNotification.js +20 -4
- package/dist/components/InboxNotification.js.map +1 -1
- package/dist/components/InboxNotification.mjs +20 -4
- package/dist/components/InboxNotification.mjs.map +1 -1
- package/dist/components/Thread.js +5 -0
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/Thread.mjs +5 -0
- package/dist/components/Thread.mjs.map +1 -1
- package/dist/components/internal/Attachment.js +314 -0
- package/dist/components/internal/Attachment.js.map +1 -0
- package/dist/components/internal/Attachment.mjs +310 -0
- package/dist/components/internal/Attachment.mjs.map +1 -0
- package/dist/components/internal/InboxNotificationThread.js +12 -2
- package/dist/components/internal/InboxNotificationThread.js.map +1 -1
- package/dist/components/internal/InboxNotificationThread.mjs +13 -3
- package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
- package/dist/icons/Attachment.js +15 -0
- package/dist/icons/Attachment.js.map +1 -0
- package/dist/icons/Attachment.mjs +13 -0
- package/dist/icons/Attachment.mjs.map +1 -0
- package/dist/icons/Spinner.js +3 -9
- package/dist/icons/Spinner.js.map +1 -1
- package/dist/icons/Spinner.mjs +4 -10
- package/dist/icons/Spinner.mjs.map +1 -1
- package/dist/icons/{Missing.js → Warning.js} +3 -3
- package/dist/icons/{Missing.js.map → Warning.js.map} +1 -1
- package/dist/icons/{Missing.mjs → Warning.mjs} +3 -3
- package/dist/icons/{Missing.mjs.map → Warning.mjs.map} +1 -1
- package/dist/index.d.mts +73 -4
- package/dist/index.d.ts +73 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/overrides.js +5 -0
- package/dist/overrides.js.map +1 -1
- package/dist/overrides.mjs +5 -0
- package/dist/overrides.mjs.map +1 -1
- package/dist/primitives/Composer/contexts.js +17 -3
- package/dist/primitives/Composer/contexts.js.map +1 -1
- package/dist/primitives/Composer/contexts.mjs +15 -4
- package/dist/primitives/Composer/contexts.mjs.map +1 -1
- package/dist/primitives/Composer/index.js +207 -30
- package/dist/primitives/Composer/index.js.map +1 -1
- package/dist/primitives/Composer/index.mjs +210 -35
- package/dist/primitives/Composer/index.mjs.map +1 -1
- package/dist/primitives/Composer/utils.js +231 -0
- package/dist/primitives/Composer/utils.js.map +1 -1
- package/dist/primitives/Composer/utils.mjs +229 -1
- package/dist/primitives/Composer/utils.mjs.map +1 -1
- package/dist/primitives/EmojiPicker/utils.js +2 -2
- package/dist/primitives/EmojiPicker/utils.js.map +1 -1
- package/dist/primitives/EmojiPicker/utils.mjs +1 -1
- package/dist/primitives/EmojiPicker/utils.mjs.map +1 -1
- package/dist/primitives/FileSize.js +33 -0
- package/dist/primitives/FileSize.js.map +1 -0
- package/dist/primitives/FileSize.mjs +31 -0
- package/dist/primitives/FileSize.mjs.map +1 -0
- package/dist/primitives/index.d.mts +87 -3
- package/dist/primitives/index.d.ts +87 -3
- package/dist/primitives/index.js +4 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/index.mjs +2 -0
- package/dist/primitives/index.mjs.map +1 -1
- package/dist/slate/plugins/{paste-html.js → paste.js} +16 -5
- package/dist/slate/plugins/paste.js.map +1 -0
- package/dist/slate/plugins/{paste-html.mjs → paste.mjs} +16 -5
- package/dist/slate/plugins/paste.mjs.map +1 -0
- package/dist/utils/data-transfer.js +20 -0
- package/dist/utils/data-transfer.js.map +1 -0
- package/dist/utils/data-transfer.mjs +18 -0
- package/dist/utils/data-transfer.mjs.map +1 -0
- package/dist/utils/download.js +14 -0
- package/dist/utils/download.js.map +1 -0
- package/dist/utils/download.mjs +12 -0
- package/dist/utils/download.mjs.map +1 -0
- package/dist/utils/format-file-size.js +45 -0
- package/dist/utils/format-file-size.js.map +1 -0
- package/dist/utils/format-file-size.mjs +43 -0
- package/dist/utils/format-file-size.mjs.map +1 -0
- package/dist/utils/intl.js +6 -0
- package/dist/utils/intl.js.map +1 -1
- package/dist/utils/intl.mjs +6 -1
- package/dist/utils/intl.mjs.map +1 -1
- package/dist/utils/use-initial.js +2 -1
- package/dist/utils/use-initial.js.map +1 -1
- package/dist/utils/use-initial.mjs +3 -2
- package/dist/utils/use-initial.mjs.map +1 -1
- package/dist/utils/use-latest.js.map +1 -1
- package/dist/utils/use-latest.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/package.json +4 -4
- package/src/styles/dark/index.css +1 -0
- package/src/styles/index.css +343 -62
- package/src/styles/utils.css +44 -0
- package/styles/dark/attributes.css +1 -1
- package/styles/dark/attributes.css.map +1 -1
- package/styles/dark/media-query.css +1 -1
- package/styles/dark/media-query.css.map +1 -1
- package/styles.css +1 -1
- package/styles.css.map +1 -1
- package/dist/slate/plugins/paste-html.js.map +0 -1
- package/dist/slate/plugins/paste-html.mjs.map +0 -1
- package/dist/utils/chunk.js +0 -12
- package/dist/utils/chunk.js.map +0 -1
- package/dist/utils/chunk.mjs +0 -10
- package/dist/utils/chunk.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Comment.mjs","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n CommentData,\n CommentReaction as CommentReactionData,\n} from \"@liveblocks/core\";\nimport {\n RoomContext,\n useAddReaction,\n useDeleteComment,\n useEditComment,\n useMarkThreadAsRead,\n useRemoveReaction,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n ReactNode,\n RefObject,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\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 { EmojiAddIcon } from \"../icons/EmojiAdd\";\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,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport { MENTION_CHARACTER } from \"../slate/plugins/mentions\";\nimport { classNames } from \"../utils/class-names\";\nimport { useRefs } from \"../utils/use-refs\";\nimport { useVisibleCallback } from \"../utils/use-visible\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Composer } from \"./Composer\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { List } from \"./internal/List\";\nimport {\n ShortcutTooltip,\n ShortcutTooltipKey,\n Tooltip,\n TooltipProvider,\n} from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The comment to display.\n */\n comment: CommentData;\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 indent the comment's content.\n */\n indentContent?: boolean;\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?: (userId: string, event: MouseEvent<HTMLElement>) => void;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n autoMarkReadThreadId?: string;\n\n /**\n * @internal\n */\n additionalActions?: ReactNode;\n\n /**\n * @internal\n */\n additionalActionsClassName?: string;\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\nexport function CommentMention({\n userId,\n className,\n ...props\n}: CommentBodyMentionProps & CommentMentionProps) {\n const currentId = useCurrentUserId();\n return (\n <CommentPrimitive.Mention\n className={classNames(\"lb-comment-mention\", className)}\n data-self={userId === currentId ? \"\" : undefined}\n {...props}\n >\n {MENTION_CHARACTER}\n <User userId={userId} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <CommentPrimitive.Link\n className={classNames(\"lb-comment-link\", className)}\n href={href}\n {...props}\n >\n {children}\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={classNames(\"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 <Button\n className={classNames(\"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 </Button>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddReaction();\n const removeReaction = useRemoveReaction();\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\n// A void component (which doesn't render anything) responsible for marking a thread\n// as read when the comment it's used in becomes visible.\n// Moving this logic into a separate component allows us to use the visibility\n// and focus hooks \"conditionally\" by conditionally rendering this component.\nfunction AutoMarkReadThreadIdHandler({\n threadId,\n commentRef,\n}: {\n threadId: string;\n commentRef: RefObject<HTMLElement>;\n}) {\n const markThreadAsRead = useMarkThreadAsRead();\n const isWindowFocused = useWindowFocus();\n\n useVisibleCallback(\n commentRef,\n () => {\n markThreadAsRead(threadId);\n },\n {\n // The underlying IntersectionObserver is only enabled when the window is focused\n enabled: isWindowFocused,\n }\n );\n\n return null;\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 = forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n onAuthorClick,\n onMentionClick,\n onCommentEdit,\n onCommentDelete,\n overrides,\n className,\n additionalActions,\n additionalActionsClassName,\n autoMarkReadThreadId,\n ...props\n },\n forwardedRef\n ) => {\n const isInRoom = Boolean(useContext(RoomContext));\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteComment();\n const editComment = useEditComment();\n const addReaction = useAddReaction();\n const removeReaction = useRemoveReaction();\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\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 ({ body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\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 event.preventDefault();\n setEditing(false);\n editComment({\n commentId: comment.id,\n threadId: comment.threadId,\n body,\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 return (\n <TooltipProvider>\n {isInRoom && autoMarkReadThreadId && (\n <AutoMarkReadThreadIdHandler\n commentRef={ref}\n threadId={autoMarkReadThreadId}\n />\n )}\n <div\n id={comment.id}\n className={classNames(\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 className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n <span className=\"lb-comment-details-labels\">\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n <span className=\"lb-comment-date\">\n <Timestamp\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-date 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 </span>\n </div>\n {showActions && !isEditing && (\n <div\n className={classNames(\n \"lb-comment-actions\",\n additionalActionsClassName\n )}\n >\n {additionalActions ?? null}\n {showReactions && (\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 >\n <EmojiAddIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n )}\n {comment.userId === currentUserId && (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n <DropdownItem\n onSelect={handleEdit}\n onClick={stopPropagation}\n >\n <EditIcon className=\"lb-dropdown-item-icon\" />\n {$.COMMENT_EDIT}\n </DropdownItem>\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.COMMENT_DELETE}\n </DropdownItem>\n </>\n }\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n )}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">\n {isEditing ? (\n <Composer\n className=\"lb-comment-composer\"\n onComposerSubmit={handleEditSubmit}\n defaultValue={comment.body}\n autoFocus\n showAttribution={false}\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 >\n <CrossIcon className=\"lb-button-icon\" />\n </Button>\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut={<ShortcutTooltipKey name=\"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 >\n <CheckIcon className=\"lb-button-icon\" />\n </Button>\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n />\n ) : comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n body={comment.body}\n components={{\n Mention: ({ userId }) => (\n <CommentMention\n userId={userId}\n onClick={(event) => onMentionClick?.(userId, event)}\n />\n ),\n Link: CommentLink,\n }}\n />\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 />\n ))}\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 >\n <EmojiAddIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA;AAmFO;AAAwB;AAC7B;AACA;AAEF;AACE;AACA;AACG;AACsD;AACd;AACnC;AAGH;AAAK;AAGZ;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AACG;AACmD;AAClD;AACI;AAKV;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AACG;AAAuD;AAAO;AAInE;AAEA;AAIE;AACA;AACG;AACuD;AAC9C;AACM;AACH;AACM;AACjB;AACI;AACC;AAEJ;AAAgB;AAA4C;AAC5D;AAAe;AAGtB;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACA;AAEI;AAAgB;AAAkB;AAAe;AACnD;AACkB;AACT;AACA;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;AACG;AACU;AACA;AACC;AAET;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEJ;AAC4B;AAC3B;AACA;AACI;AAKd;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACG;AACc;AACc;AAC3B;AACA;AACI;AACC;AAGX;AAMA;AAAqC;AACnC;AAEF;AAIE;AACA;AAEA;AAAA;AACE;AAEE;AAAyB;AAC3B;AACA;AAEW;AACX;AAGF;AACF;AAYO;AAAgB;AAEnB;AACE;AACgB;AAChB;AACc;AACE;AAChB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAGrB;AAEA;AACA;AACA;AAAY;AACS;AACD;AAClB;AACD;AACH;AACoC;AAGtC;AAEE;AAEA;AAAc;AACO;AACD;AACnB;AAGH;AAA0B;AAEtB;AAAqC;AACvC;AAC8B;AAGhC;AAA6B;AAEzB;AAAwC;AACL;AAGnC;AAGyC;AACf;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;AAGO;AACa;AACF;AAGb;AACa;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACtB;AACH;AACC;AAEJ;AAAc;AACZ;AAAc;AACZ;AACW;AACM;AACP;AAEV;AAAe;AACb;AACW;AACM;AACP;AAEV;AAAe;AACb;AACW;AACI;AACJ;AAKP;AAAe;AASvB;AACY;AACT;AACA;AACF;AAIG;AACgB;AACD;AAEb;AAAmB;AACjB;AAA0B;AACxB;AACW;AACD;AACK;AAEb;AAAuB;AAO/B;AACO;AACQ;AACR;AAGD;AACW;AACD;AAER;AAAmB;AAGrB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACS;AACV;AACK;AAEb;AAAuB;AASvC;AAAc;AAEV;AACW;AACQ;AACI;AACb;AACQ;AAGZ;AACY;AACG;AAEb;AACW;AACD;AAER;AAAoB;AAGxB;AACY;AACA;AAAwB;AAAQ;AAE1C;AAAgC;AAC9B;AACS;AACE;AACD;AACK;AAEb;AAAoB;AAI7B;AAES;AACe;AAC1B;AAIC;AACW;AACI;AACF;AAEP;AACC;AACkD;AACpD;AAEI;AACR;AAGC;AAAc;AAEV;AACe;AACd;AACA;AACA;AAGH;AAA2B;AACzB;AAAmB;AACjB;AAA0B;AACxB;AACW;AACF;AACC;AACK;AAEb;AAAuB;AASrC;AAAc;AACZ;AAAY;AAKvB;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"Comment.mjs","sources":["../../src/components/Comment.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n CommentAttachment,\n CommentData,\n CommentReaction as CommentReactionData,\n} from \"@liveblocks/core\";\nimport {\n RoomContext,\n useAddReaction,\n useAttachmentUrl,\n useDeleteComment,\n useEditComment,\n useMarkThreadAsRead,\n useRemoveReaction,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n FormEvent,\n MouseEvent,\n ReactNode,\n RefObject,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\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 { EmojiAddIcon } from \"../icons/EmojiAdd\";\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,\n} from \"../primitives/Comment/types\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport { MENTION_CHARACTER } from \"../slate/plugins/mentions\";\nimport type { CommentAttachmentArgs } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { download } from \"../utils/download\";\nimport { useRefs } from \"../utils/use-refs\";\nimport { useVisibleCallback } from \"../utils/use-visible\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Composer } from \"./Composer\";\nimport {\n FileAttachment,\n MediaAttachment,\n separateMediaAttachments,\n} from \"./internal/Attachment\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport { Emoji } from \"./internal/Emoji\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { List } from \"./internal/List\";\nimport {\n ShortcutTooltip,\n ShortcutTooltipKey,\n Tooltip,\n TooltipProvider,\n} from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nconst REACTIONS_TRUNCATE = 5;\n\nexport interface CommentProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The comment to display.\n */\n comment: CommentData;\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 indent the comment's content.\n */\n indentContent?: boolean;\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?: (userId: string, event: MouseEvent<HTMLElement>) => 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 * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & CommentOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n autoMarkReadThreadId?: string;\n\n /**\n * @internal\n */\n additionalActions?: ReactNode;\n\n /**\n * @internal\n */\n additionalActionsClassName?: string;\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\nexport function CommentMention({\n userId,\n className,\n ...props\n}: CommentBodyMentionProps & CommentMentionProps) {\n const currentId = useCurrentUserId();\n return (\n <CommentPrimitive.Mention\n className={classNames(\"lb-comment-mention\", className)}\n data-self={userId === currentId ? \"\" : undefined}\n {...props}\n >\n {MENTION_CHARACTER}\n <User userId={userId} />\n </CommentPrimitive.Mention>\n );\n}\n\nexport function CommentLink({\n href,\n children,\n className,\n ...props\n}: CommentBodyLinkProps & CommentLinkProps) {\n return (\n <CommentPrimitive.Link\n className={classNames(\"lb-comment-link\", className)}\n href={href}\n {...props}\n >\n {children}\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={classNames(\"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 <Button\n className={classNames(\"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 </Button>\n );\n});\n\nexport const CommentReaction = forwardRef<\n HTMLButtonElement,\n CommentReactionProps\n>(({ comment, reaction, overrides, disabled, ...props }, forwardedRef) => {\n const addReaction = useAddReaction();\n const removeReaction = useRemoveReaction();\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 className,\n overrides,\n ...props\n}: CommentAttachmentProps) {\n const { url } = useAttachmentUrl(attachment.id);\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={classNames(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n />\n );\n}\n\nfunction CommentFileAttachment({\n attachment,\n onAttachmentClick,\n className,\n overrides,\n ...props\n}: CommentAttachmentProps) {\n const { url } = useAttachmentUrl(attachment.id);\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={classNames(\"lb-comment-attachment\", className)}\n {...props}\n attachment={attachment}\n overrides={overrides}\n onClick={url ? handleClick : undefined}\n />\n );\n}\n\nexport function CommentNonInteractiveFileAttachment({\n className,\n ...props\n}: CommentAttachmentProps) {\n return (\n <FileAttachment\n className={classNames(\"lb-comment-attachment\", className)}\n {...props}\n />\n );\n}\n\n// A void component (which doesn't render anything) responsible for marking a thread\n// as read when the comment it's used in becomes visible.\n// Moving this logic into a separate component allows us to use the visibility\n// and focus hooks \"conditionally\" by conditionally rendering this component.\nfunction AutoMarkReadThreadIdHandler({\n threadId,\n commentRef,\n}: {\n threadId: string;\n commentRef: RefObject<HTMLElement>;\n}) {\n const markThreadAsRead = useMarkThreadAsRead();\n const isWindowFocused = useWindowFocus();\n\n useVisibleCallback(\n commentRef,\n () => {\n markThreadAsRead(threadId);\n },\n {\n // The underlying IntersectionObserver is only enabled when the window is focused\n enabled: isWindowFocused,\n }\n );\n\n return null;\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 = forwardRef<HTMLDivElement, CommentProps>(\n (\n {\n comment,\n indentContent = true,\n showDeleted,\n showActions = \"hover\",\n showReactions = true,\n showAttachments = true,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onCommentEdit,\n onCommentDelete,\n overrides,\n className,\n additionalActions,\n additionalActionsClassName,\n autoMarkReadThreadId,\n ...props\n },\n forwardedRef\n ) => {\n const isInRoom = Boolean(useContext(RoomContext));\n const ref = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const currentUserId = useCurrentUserId();\n const deleteComment = useDeleteComment();\n const editComment = useEditComment();\n const addReaction = useAddReaction();\n const removeReaction = useRemoveReaction();\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 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 event.preventDefault();\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 return (\n <TooltipProvider>\n {isInRoom && autoMarkReadThreadId && (\n <AutoMarkReadThreadIdHandler\n commentRef={ref}\n threadId={autoMarkReadThreadId}\n />\n )}\n <div\n id={comment.id}\n className={classNames(\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 className=\"lb-comment-avatar\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n <span className=\"lb-comment-details-labels\">\n <User\n className=\"lb-comment-author\"\n userId={comment.userId}\n onClick={handleAuthorClick}\n />\n <span className=\"lb-comment-date\">\n <Timestamp\n locale={$.locale}\n date={comment.createdAt}\n className=\"lb-date 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 </span>\n </div>\n {showActions && !isEditing && (\n <div\n className={classNames(\n \"lb-comment-actions\",\n additionalActionsClassName\n )}\n >\n {additionalActions ?? null}\n {showReactions && (\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 >\n <EmojiAddIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n )}\n {comment.userId === currentUserId && (\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n <DropdownItem\n onSelect={handleEdit}\n onClick={stopPropagation}\n >\n <EditIcon className=\"lb-dropdown-item-icon\" />\n {$.COMMENT_EDIT}\n </DropdownItem>\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.COMMENT_DELETE}\n </DropdownItem>\n </>\n }\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n )}\n </div>\n )}\n </div>\n <div className=\"lb-comment-content\">\n {isEditing ? (\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 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 >\n <CrossIcon className=\"lb-button-icon\" />\n </Button>\n </Tooltip>\n <ShortcutTooltip\n content={$.COMMENT_EDIT_COMPOSER_SAVE}\n shortcut={<ShortcutTooltipKey name=\"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 >\n <CheckIcon className=\"lb-button-icon\" />\n </Button>\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n }\n overrides={{\n COMPOSER_PLACEHOLDER: $.COMMENT_EDIT_COMPOSER_PLACEHOLDER,\n }}\n />\n ) : comment.body ? (\n <>\n <CommentPrimitive.Body\n className=\"lb-comment-body\"\n body={comment.body}\n components={{\n Mention: ({ userId }) => (\n <CommentMention\n userId={userId}\n onClick={(event) => onMentionClick?.(userId, event)}\n />\n ),\n Link: CommentLink,\n }}\n />\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 />\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 />\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 />\n ))}\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 >\n <EmojiAddIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n </div>\n )}\n </>\n ) : (\n <div className=\"lb-comment-body\">\n <p className=\"lb-comment-deleted\">{$.COMMENT_DELETED}</p>\n </div>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFA;AAqGO;AAAwB;AAC7B;AACA;AAEF;AACE;AACA;AACG;AACsD;AACd;AACnC;AAGH;AAAK;AAGZ;AAEO;AAAqB;AAC1B;AACA;AACA;AAEF;AACE;AACG;AACmD;AAClD;AACI;AAKV;AAEO;AAAmC;AAClC;AACN;AACA;AAEF;AACE;AACG;AAAuD;AAAO;AAInE;AAEA;AAIE;AACA;AACG;AACuD;AAC9C;AACM;AACH;AACM;AACjB;AACI;AACC;AAEJ;AAAgB;AAA4C;AAC5D;AAAe;AAGtB;AAEa;AAIX;AACA;AACA;AACA;AACE;AAA4D;AAE9D;AACA;AAAuB;AAGd;AACA;AAEI;AAAgB;AAAkB;AAAe;AACnD;AACkB;AACT;AACA;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;AACG;AACU;AACA;AACC;AAET;AACQ;AACE;AACQ;AACR;AACT;AACK;AAEJ;AAC4B;AAC3B;AACA;AACI;AAKd;AAEa;AAIX;AACA;AACE;AAA4D;AAG9D;AACG;AACc;AACc;AAC3B;AACA;AACI;AACC;AAGX;AAEA;AAGE;AAME;AAAyB;AAEzB;AAA6B;AAEjC;AAEA;AAAgC;AAC9B;AACA;AACA;AACA;AAEF;AACE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACG;AACyD;AACpD;AACJ;AACA;AAC6B;AAGnC;AAEA;AAA+B;AAC7B;AACA;AACA;AACA;AAEF;AACE;AAEA;AAAoB;AAEhB;AACE;AAAA;AAGF;AAEA;AAEA;AACE;AAAA;AAGF;AAAmB;AACrB;AACmC;AAGrC;AACG;AACyD;AACpD;AACJ;AACA;AAC6B;AAGnC;AAEO;AAA6C;AAClD;AAEF;AACE;AACG;AACyD;AACpD;AAGV;AAMA;AAAqC;AACnC;AAEF;AAIE;AACA;AAEA;AAAA;AACE;AAEE;AAAyB;AAC3B;AACA;AAEW;AACX;AAGF;AACF;AAYO;AAAgB;AAEnB;AACE;AACgB;AAChB;AACc;AACE;AACE;AAClB;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;AACE;AAAsB;AAGxB;AACE;AAAe;AAGjB;AAAyB;AAErB;AACA;AAAgB;AAClB;AACC;AAGH;AAAyB;AAMrB;AAEA;AACA;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;AAGyC;AACf;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;AAGO;AACa;AACF;AAGb;AACa;AACD;AACT;AACiB;AACU;AAEzB;AACF;AACF;AACmC;AACJ;AAEF;AACtB;AACH;AACC;AAEJ;AAAc;AACZ;AAAc;AACZ;AACW;AACM;AACP;AAEV;AAAe;AACb;AACW;AACM;AACP;AAEV;AAAe;AACb;AACW;AACI;AACJ;AAKP;AAAe;AASvB;AACY;AACT;AACA;AACF;AAIG;AACgB;AACD;AAEb;AAAmB;AACjB;AAA0B;AACxB;AACW;AACD;AACK;AAEb;AAAuB;AAO/B;AACO;AACQ;AACR;AAGD;AACW;AACD;AAER;AAAmB;AAGrB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACS;AACV;AACK;AAEb;AAAuB;AASvC;AAAc;AAEV;AACW;AACQ;AACI;AACM;AACnB;AACQ;AACjB;AAGK;AACY;AACG;AAEb;AACW;AACD;AAER;AAAoB;AAGxB;AACY;AACA;AAAwB;AAAQ;AAE1C;AAAgC;AAC9B;AACS;AACE;AACD;AACK;AAEb;AAAoB;AAI7B;AAES;AACe;AAC1B;AAIC;AACW;AACI;AACF;AAEP;AACC;AACkD;AACpD;AAEI;AACR;AAIC;AAAc;AAEV;AAAc;AAEV;AACiB;AAChB;AACA;AACA;AAML;AAAc;AAEV;AACiB;AAChB;AACA;AACA;AAQT;AAAc;AAEV;AACe;AACd;AACA;AACA;AAGH;AAA2B;AACzB;AAAmB;AACjB;AAA0B;AACxB;AACW;AACF;AACC;AACK;AAEb;AAAuB;AASrC;AAAc;AACZ;AAAY;AAKvB;AAGN;;"}
|
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
var core = require('@liveblocks/core');
|
|
5
5
|
var react = require('@liveblocks/react');
|
|
6
6
|
var React = require('react');
|
|
7
|
+
var Attachment = require('../icons/Attachment.js');
|
|
7
8
|
var Emoji = require('../icons/Emoji.js');
|
|
8
9
|
var Mention = require('../icons/Mention.js');
|
|
9
10
|
var Send = require('../icons/Send.js');
|
|
10
11
|
var overrides = require('../overrides.js');
|
|
11
12
|
var index = require('../primitives/Composer/index.js');
|
|
12
13
|
var contexts = require('../primitives/Composer/contexts.js');
|
|
14
|
+
var utils = require('../primitives/Composer/utils.js');
|
|
13
15
|
var mentions = require('../slate/plugins/mentions.js');
|
|
14
16
|
var classNames = require('../utils/class-names.js');
|
|
15
17
|
var useControllableState = require('../utils/use-controllable-state.js');
|
|
18
|
+
var useLayoutEffect = require('../utils/use-layout-effect.js');
|
|
19
|
+
var Attachment$1 = require('./internal/Attachment.js');
|
|
16
20
|
var Attribution = require('./internal/Attribution.js');
|
|
17
21
|
var Avatar = require('./internal/Avatar.js');
|
|
18
22
|
var Button = require('./internal/Button.js');
|
|
@@ -24,6 +28,7 @@ var PopoverPrimitive = require('@radix-ui/react-popover');
|
|
|
24
28
|
|
|
25
29
|
function ComposerInsertMentionEditorAction({
|
|
26
30
|
label,
|
|
31
|
+
tooltipLabel,
|
|
27
32
|
className,
|
|
28
33
|
onClick,
|
|
29
34
|
...props
|
|
@@ -43,7 +48,7 @@ function ComposerInsertMentionEditorAction({
|
|
|
43
48
|
[createMention, onClick]
|
|
44
49
|
);
|
|
45
50
|
return /* @__PURE__ */ React.createElement(Tooltip.Tooltip, {
|
|
46
|
-
content: label
|
|
51
|
+
content: tooltipLabel ?? label
|
|
47
52
|
}, /* @__PURE__ */ React.createElement(Button.Button, {
|
|
48
53
|
className: classNames.classNames("lb-composer-editor-action", className),
|
|
49
54
|
onMouseDown: preventDefault,
|
|
@@ -56,6 +61,7 @@ function ComposerInsertMentionEditorAction({
|
|
|
56
61
|
}
|
|
57
62
|
function ComposerInsertEmojiEditorAction({
|
|
58
63
|
label,
|
|
64
|
+
tooltipLabel,
|
|
59
65
|
onPickerOpenChange,
|
|
60
66
|
className,
|
|
61
67
|
...props
|
|
@@ -71,7 +77,7 @@ function ComposerInsertEmojiEditorAction({
|
|
|
71
77
|
onEmojiSelect: insertText,
|
|
72
78
|
onOpenChange: onPickerOpenChange
|
|
73
79
|
}, /* @__PURE__ */ React.createElement(Tooltip.Tooltip, {
|
|
74
|
-
content: label
|
|
80
|
+
content: tooltipLabel ?? label
|
|
75
81
|
}, /* @__PURE__ */ React.createElement(PopoverPrimitive.PopoverTrigger, {
|
|
76
82
|
asChild: true
|
|
77
83
|
}, /* @__PURE__ */ React.createElement(Button.Button, {
|
|
@@ -84,6 +90,32 @@ function ComposerInsertEmojiEditorAction({
|
|
|
84
90
|
className: "lb-button-icon"
|
|
85
91
|
})))));
|
|
86
92
|
}
|
|
93
|
+
function ComposerAttachFilesEditorAction({
|
|
94
|
+
label,
|
|
95
|
+
tooltipLabel,
|
|
96
|
+
className,
|
|
97
|
+
...props
|
|
98
|
+
}) {
|
|
99
|
+
const preventDefault = React.useCallback((event) => {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
}, []);
|
|
102
|
+
const stopPropagation = React.useCallback((event) => {
|
|
103
|
+
event.stopPropagation();
|
|
104
|
+
}, []);
|
|
105
|
+
return /* @__PURE__ */ React.createElement(Tooltip.Tooltip, {
|
|
106
|
+
content: tooltipLabel ?? label
|
|
107
|
+
}, /* @__PURE__ */ React.createElement(index.AttachFiles, {
|
|
108
|
+
asChild: true
|
|
109
|
+
}, /* @__PURE__ */ React.createElement(Button.Button, {
|
|
110
|
+
className: classNames.classNames("lb-composer-editor-action", className),
|
|
111
|
+
onMouseDown: preventDefault,
|
|
112
|
+
onClick: stopPropagation,
|
|
113
|
+
"aria-label": label,
|
|
114
|
+
...props
|
|
115
|
+
}, /* @__PURE__ */ React.createElement(Attachment.AttachmentIcon, {
|
|
116
|
+
className: "lb-button-icon"
|
|
117
|
+
}))));
|
|
118
|
+
}
|
|
87
119
|
function ComposerMention({ userId }) {
|
|
88
120
|
return /* @__PURE__ */ React.createElement(index.Mention, {
|
|
89
121
|
className: "lb-composer-mention"
|
|
@@ -116,68 +148,177 @@ function ComposerLink({ href, children }) {
|
|
|
116
148
|
className: "lb-composer-link"
|
|
117
149
|
}, children);
|
|
118
150
|
}
|
|
151
|
+
function ComposerFileAttachment({
|
|
152
|
+
attachment,
|
|
153
|
+
className,
|
|
154
|
+
overrides,
|
|
155
|
+
...props
|
|
156
|
+
}) {
|
|
157
|
+
const { removeAttachment } = contexts.useComposer();
|
|
158
|
+
const handleDeleteClick = React.useCallback(() => {
|
|
159
|
+
removeAttachment(attachment.id);
|
|
160
|
+
}, [attachment.id, removeAttachment]);
|
|
161
|
+
return /* @__PURE__ */ React.createElement(Attachment$1.FileAttachment, {
|
|
162
|
+
className: classNames.classNames("lb-composer-attachment", className),
|
|
163
|
+
...props,
|
|
164
|
+
attachment,
|
|
165
|
+
onDeleteClick: handleDeleteClick,
|
|
166
|
+
preventFocusOnDelete: true,
|
|
167
|
+
overrides
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function ComposerAttachments({
|
|
171
|
+
overrides,
|
|
172
|
+
className,
|
|
173
|
+
...props
|
|
174
|
+
}) {
|
|
175
|
+
const { attachments } = contexts.useComposer();
|
|
176
|
+
if (attachments.length === 0) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
180
|
+
className: classNames.classNames("lb-composer-attachments", className),
|
|
181
|
+
...props
|
|
182
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
183
|
+
className: "lb-attachments"
|
|
184
|
+
}, attachments.map((attachment) => {
|
|
185
|
+
return /* @__PURE__ */ React.createElement(ComposerFileAttachment, {
|
|
186
|
+
key: attachment.id,
|
|
187
|
+
attachment,
|
|
188
|
+
overrides
|
|
189
|
+
});
|
|
190
|
+
})));
|
|
191
|
+
}
|
|
119
192
|
const editorComponents = {
|
|
120
193
|
Mention: ComposerMention,
|
|
121
194
|
MentionSuggestions: ComposerMentionSuggestions,
|
|
122
195
|
Link: ComposerLink
|
|
123
196
|
};
|
|
124
|
-
|
|
125
|
-
|
|
197
|
+
function ComposerEditorContainer({
|
|
198
|
+
showAttachments = true,
|
|
199
|
+
showAttribution,
|
|
200
|
+
defaultValue,
|
|
201
|
+
isCollapsed,
|
|
202
|
+
overrides: overrides$1,
|
|
203
|
+
actions,
|
|
204
|
+
autoFocus,
|
|
205
|
+
hasResolveMentionSuggestions,
|
|
206
|
+
onEmojiPickerOpenChange,
|
|
207
|
+
onEmptyChange,
|
|
208
|
+
onEditorClick
|
|
209
|
+
}) {
|
|
210
|
+
const { isEmpty } = contexts.useComposer();
|
|
211
|
+
const { hasMaxAttachments } = contexts.useComposerAttachmentsContext();
|
|
212
|
+
const $ = overrides.useOverrides(overrides$1);
|
|
213
|
+
const [isDraggingOver, dropAreaProps] = utils.useComposerAttachmentsDropArea({
|
|
214
|
+
disabled: hasMaxAttachments
|
|
215
|
+
});
|
|
216
|
+
useLayoutEffect.useLayoutEffect(() => {
|
|
217
|
+
onEmptyChange(isEmpty);
|
|
218
|
+
}, [isEmpty, onEmptyChange]);
|
|
219
|
+
const preventDefault = React.useCallback((event) => {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
}, []);
|
|
222
|
+
const stopPropagation = React.useCallback((event) => {
|
|
223
|
+
event.stopPropagation();
|
|
224
|
+
}, []);
|
|
225
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
226
|
+
className: "lb-composer-editor-container",
|
|
227
|
+
...dropAreaProps
|
|
228
|
+
}, /* @__PURE__ */ React.createElement(index.Editor, {
|
|
229
|
+
className: "lb-composer-editor",
|
|
230
|
+
onClick: onEditorClick,
|
|
231
|
+
placeholder: $.COMPOSER_PLACEHOLDER,
|
|
126
232
|
defaultValue,
|
|
127
|
-
disabled,
|
|
128
233
|
autoFocus,
|
|
234
|
+
components: editorComponents,
|
|
235
|
+
dir: $.dir
|
|
236
|
+
}), showAttachments && /* @__PURE__ */ React.createElement(ComposerAttachments, {
|
|
237
|
+
overrides: overrides$1
|
|
238
|
+
}), (!isCollapsed || isDraggingOver) && /* @__PURE__ */ React.createElement("div", {
|
|
239
|
+
className: "lb-composer-footer"
|
|
240
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
241
|
+
className: "lb-composer-editor-actions"
|
|
242
|
+
}, hasResolveMentionSuggestions && /* @__PURE__ */ React.createElement(ComposerInsertMentionEditorAction, {
|
|
243
|
+
label: $.COMPOSER_INSERT_MENTION
|
|
244
|
+
}), /* @__PURE__ */ React.createElement(ComposerInsertEmojiEditorAction, {
|
|
245
|
+
label: $.COMPOSER_INSERT_EMOJI,
|
|
246
|
+
onPickerOpenChange: onEmojiPickerOpenChange
|
|
247
|
+
}), showAttachments && /* @__PURE__ */ React.createElement(ComposerAttachFilesEditorAction, {
|
|
248
|
+
label: $.COMPOSER_ATTACH_FILES
|
|
249
|
+
})), showAttribution && /* @__PURE__ */ React.createElement(Attribution.Attribution, null), /* @__PURE__ */ React.createElement("div", {
|
|
250
|
+
className: "lb-composer-actions"
|
|
251
|
+
}, actions ?? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltip, {
|
|
252
|
+
content: $.COMPOSER_SEND,
|
|
253
|
+
shortcut: /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltipKey, {
|
|
254
|
+
name: "enter"
|
|
255
|
+
})
|
|
256
|
+
}, /* @__PURE__ */ React.createElement(index.Submit, {
|
|
257
|
+
asChild: true
|
|
258
|
+
}, /* @__PURE__ */ React.createElement(Button.Button, {
|
|
259
|
+
onMouseDown: preventDefault,
|
|
260
|
+
onClick: stopPropagation,
|
|
261
|
+
className: "lb-composer-action",
|
|
262
|
+
variant: "primary",
|
|
263
|
+
"aria-label": $.COMPOSER_SEND
|
|
264
|
+
}, /* @__PURE__ */ React.createElement(Send.SendIcon, null))))))), showAttachments && isDraggingOver && /* @__PURE__ */ React.createElement("div", {
|
|
265
|
+
className: "lb-composer-attachments-drop-area"
|
|
266
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
267
|
+
className: "lb-composer-attachments-drop-area-label"
|
|
268
|
+
}, /* @__PURE__ */ React.createElement(Attachment.AttachmentIcon, null), $.COMPOSER_ATTACH_FILES)));
|
|
269
|
+
}
|
|
270
|
+
const Composer = React.forwardRef(
|
|
271
|
+
({
|
|
272
|
+
threadId,
|
|
273
|
+
commentId,
|
|
274
|
+
metadata,
|
|
275
|
+
defaultValue,
|
|
276
|
+
defaultAttachments,
|
|
277
|
+
onComposerSubmit,
|
|
129
278
|
collapsed: controlledCollapsed,
|
|
130
279
|
defaultCollapsed,
|
|
131
280
|
onCollapsedChange: controlledOnCollapsedChange,
|
|
132
|
-
actions,
|
|
133
281
|
overrides: overrides$1,
|
|
134
|
-
|
|
135
|
-
onFocus,
|
|
282
|
+
actions,
|
|
136
283
|
onBlur,
|
|
137
284
|
className,
|
|
285
|
+
onFocus,
|
|
286
|
+
autoFocus,
|
|
287
|
+
disabled,
|
|
288
|
+
showAttachments = true,
|
|
289
|
+
showAttribution,
|
|
138
290
|
...props
|
|
139
291
|
}, forwardedRef) => {
|
|
140
292
|
const client = react.useClient();
|
|
293
|
+
const createThread = react.useCreateThread();
|
|
294
|
+
const createComment = react.useCreateComment();
|
|
295
|
+
const editComment = react.useEditComment();
|
|
141
296
|
const hasResolveMentionSuggestions = client[core.kInternal].resolveMentionSuggestions !== void 0;
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
() => disabled || (self ? !self.canComment : false),
|
|
145
|
-
[disabled, self?.canComment]
|
|
146
|
-
);
|
|
147
|
-
const { isEmpty } = contexts.useComposer();
|
|
297
|
+
const isEmptyRef = React.useRef(true);
|
|
298
|
+
const isEmojiPickerOpenRef = React.useRef(false);
|
|
148
299
|
const $ = overrides.useOverrides(overrides$1);
|
|
149
|
-
const [
|
|
150
|
-
const [collapsed, onCollapsedChange] = useControllableState.useControllableState(
|
|
300
|
+
const [isCollapsed, onCollapsedChange] = useControllableState.useControllableState(
|
|
151
301
|
controlledCollapsed === void 0 && defaultCollapsed === void 0 ? false : controlledCollapsed,
|
|
152
302
|
controlledOnCollapsedChange,
|
|
153
303
|
defaultCollapsed
|
|
154
304
|
);
|
|
155
|
-
const
|
|
156
|
-
|
|
305
|
+
const setEmptyRef = React.useCallback((isEmpty) => {
|
|
306
|
+
isEmptyRef.current = isEmpty;
|
|
157
307
|
}, []);
|
|
158
|
-
const
|
|
159
|
-
|
|
308
|
+
const setEmojiPickerOpenRef = React.useCallback((isEmojiPickerOpen) => {
|
|
309
|
+
isEmojiPickerOpenRef.current = isEmojiPickerOpen;
|
|
160
310
|
}, []);
|
|
161
|
-
const handleEditorClick = React.useCallback(
|
|
162
|
-
(event) => {
|
|
163
|
-
event.stopPropagation();
|
|
164
|
-
if (isEmpty) {
|
|
165
|
-
onCollapsedChange?.(false);
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
[isEmpty, onCollapsedChange]
|
|
169
|
-
);
|
|
170
311
|
const handleFocus = React.useCallback(
|
|
171
312
|
(event) => {
|
|
172
313
|
onFocus?.(event);
|
|
173
314
|
if (event.isDefaultPrevented()) {
|
|
174
315
|
return;
|
|
175
316
|
}
|
|
176
|
-
if (
|
|
317
|
+
if (isEmptyRef.current) {
|
|
177
318
|
onCollapsedChange?.(false);
|
|
178
319
|
}
|
|
179
320
|
},
|
|
180
|
-
[
|
|
321
|
+
[onCollapsedChange, onFocus]
|
|
181
322
|
);
|
|
182
323
|
const handleBlur = React.useCallback(
|
|
183
324
|
(event) => {
|
|
@@ -185,75 +326,24 @@ const ComposerWithContext = React.forwardRef(
|
|
|
185
326
|
if (event.isDefaultPrevented()) {
|
|
186
327
|
return;
|
|
187
328
|
}
|
|
188
|
-
const isOutside = !event.currentTarget.contains(
|
|
189
|
-
|
|
329
|
+
const isOutside = !event.currentTarget.contains(
|
|
330
|
+
event.relatedTarget ?? document.activeElement
|
|
331
|
+
);
|
|
332
|
+
if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {
|
|
190
333
|
onCollapsedChange?.(true);
|
|
191
334
|
}
|
|
192
335
|
},
|
|
193
|
-
[
|
|
336
|
+
[onBlur, onCollapsedChange]
|
|
337
|
+
);
|
|
338
|
+
const handleEditorClick = React.useCallback(
|
|
339
|
+
(event) => {
|
|
340
|
+
event.stopPropagation();
|
|
341
|
+
if (isEmptyRef.current) {
|
|
342
|
+
onCollapsedChange?.(false);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
[onCollapsedChange]
|
|
194
346
|
);
|
|
195
|
-
return /* @__PURE__ */ React.createElement("form", {
|
|
196
|
-
className: classNames.classNames(
|
|
197
|
-
"lb-root lb-composer lb-composer-form",
|
|
198
|
-
className
|
|
199
|
-
),
|
|
200
|
-
dir: $.dir,
|
|
201
|
-
...props,
|
|
202
|
-
ref: forwardedRef,
|
|
203
|
-
"data-collapsed": collapsed ? "" : void 0,
|
|
204
|
-
onFocus: handleFocus,
|
|
205
|
-
onBlur: handleBlur
|
|
206
|
-
}, /* @__PURE__ */ React.createElement(index.Editor, {
|
|
207
|
-
className: "lb-composer-editor",
|
|
208
|
-
onClick: handleEditorClick,
|
|
209
|
-
placeholder: $.COMPOSER_PLACEHOLDER,
|
|
210
|
-
defaultValue,
|
|
211
|
-
disabled: isDisabled,
|
|
212
|
-
autoFocus,
|
|
213
|
-
components: editorComponents,
|
|
214
|
-
dir: $.dir
|
|
215
|
-
}), !collapsed && /* @__PURE__ */ React.createElement("div", {
|
|
216
|
-
className: "lb-composer-footer"
|
|
217
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
218
|
-
className: "lb-composer-editor-actions"
|
|
219
|
-
}, hasResolveMentionSuggestions && /* @__PURE__ */ React.createElement(ComposerInsertMentionEditorAction, {
|
|
220
|
-
label: $.COMPOSER_INSERT_MENTION,
|
|
221
|
-
disabled: isDisabled
|
|
222
|
-
}), /* @__PURE__ */ React.createElement(ComposerInsertEmojiEditorAction, {
|
|
223
|
-
label: $.COMPOSER_INSERT_EMOJI,
|
|
224
|
-
onPickerOpenChange: setEmojiPickerOpen,
|
|
225
|
-
disabled: isDisabled
|
|
226
|
-
})), showAttribution && /* @__PURE__ */ React.createElement(Attribution.Attribution, null), /* @__PURE__ */ React.createElement("div", {
|
|
227
|
-
className: "lb-composer-actions"
|
|
228
|
-
}, actions ?? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltip, {
|
|
229
|
-
content: $.COMPOSER_SEND,
|
|
230
|
-
shortcut: /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltipKey, {
|
|
231
|
-
name: "enter"
|
|
232
|
-
})
|
|
233
|
-
}, /* @__PURE__ */ React.createElement(index.Submit, {
|
|
234
|
-
disabled: isDisabled,
|
|
235
|
-
asChild: true
|
|
236
|
-
}, /* @__PURE__ */ React.createElement(Button.Button, {
|
|
237
|
-
onMouseDown: preventDefault,
|
|
238
|
-
onClick: stopPropagation,
|
|
239
|
-
className: "lb-composer-action",
|
|
240
|
-
variant: "primary",
|
|
241
|
-
"aria-label": $.COMPOSER_SEND
|
|
242
|
-
}, /* @__PURE__ */ React.createElement(Send.SendIcon, null))))))));
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
const Composer = React.forwardRef(
|
|
246
|
-
({
|
|
247
|
-
threadId,
|
|
248
|
-
commentId,
|
|
249
|
-
metadata,
|
|
250
|
-
onComposerSubmit,
|
|
251
|
-
onFocus,
|
|
252
|
-
...props
|
|
253
|
-
}, forwardedRef) => {
|
|
254
|
-
const createThread = react.useCreateThread();
|
|
255
|
-
const createComment = react.useCreateComment();
|
|
256
|
-
const editComment = react.useEditComment();
|
|
257
347
|
const handleCommentSubmit = React.useCallback(
|
|
258
348
|
(comment, event) => {
|
|
259
349
|
onComposerSubmit?.(comment, event);
|
|
@@ -264,17 +354,20 @@ const Composer = React.forwardRef(
|
|
|
264
354
|
editComment({
|
|
265
355
|
commentId,
|
|
266
356
|
threadId,
|
|
267
|
-
body: comment.body
|
|
357
|
+
body: comment.body,
|
|
358
|
+
attachments: comment.attachments
|
|
268
359
|
});
|
|
269
360
|
} else if (threadId) {
|
|
270
361
|
createComment({
|
|
271
362
|
threadId,
|
|
272
|
-
body: comment.body
|
|
363
|
+
body: comment.body,
|
|
364
|
+
attachments: comment.attachments
|
|
273
365
|
});
|
|
274
366
|
} else {
|
|
275
367
|
createThread({
|
|
276
368
|
body: comment.body,
|
|
277
|
-
metadata: metadata ?? {}
|
|
369
|
+
metadata: metadata ?? {},
|
|
370
|
+
attachments: comment.attachments
|
|
278
371
|
});
|
|
279
372
|
}
|
|
280
373
|
},
|
|
@@ -290,11 +383,31 @@ const Composer = React.forwardRef(
|
|
|
290
383
|
);
|
|
291
384
|
return /* @__PURE__ */ React.createElement(TooltipPrimitive.TooltipProvider, null, /* @__PURE__ */ React.createElement(index.Form, {
|
|
292
385
|
onComposerSubmit: handleCommentSubmit,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
386
|
+
className: classNames.classNames(
|
|
387
|
+
"lb-root lb-composer lb-composer-form",
|
|
388
|
+
className
|
|
389
|
+
),
|
|
390
|
+
dir: $.dir,
|
|
296
391
|
...props,
|
|
297
|
-
ref: forwardedRef
|
|
392
|
+
ref: forwardedRef,
|
|
393
|
+
"data-collapsed": isCollapsed ? "" : void 0,
|
|
394
|
+
onFocus: handleFocus,
|
|
395
|
+
onBlur: handleBlur,
|
|
396
|
+
disabled,
|
|
397
|
+
defaultAttachments,
|
|
398
|
+
pasteFilesAsAttachments: showAttachments
|
|
399
|
+
}, /* @__PURE__ */ React.createElement(ComposerEditorContainer, {
|
|
400
|
+
defaultValue,
|
|
401
|
+
actions,
|
|
402
|
+
overrides: overrides$1,
|
|
403
|
+
isCollapsed,
|
|
404
|
+
showAttachments,
|
|
405
|
+
showAttribution,
|
|
406
|
+
hasResolveMentionSuggestions,
|
|
407
|
+
onEmptyChange: setEmptyRef,
|
|
408
|
+
onEmojiPickerOpenChange: setEmojiPickerOpenRef,
|
|
409
|
+
onEditorClick: handleEditorClick,
|
|
410
|
+
autoFocus
|
|
298
411
|
})));
|
|
299
412
|
}
|
|
300
413
|
);
|