@liveblocks/react-ui 2.25.0-aiprivatebeta9 → 3.1.0-alpha1
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/_private/package.json +2 -2
- package/dist/_private/index.cjs +8 -0
- package/dist/_private/index.cjs.map +1 -1
- package/dist/_private/index.d.cts +22 -5
- package/dist/_private/index.d.ts +22 -5
- package/dist/_private/index.js +4 -0
- package/dist/_private/index.js.map +1 -1
- package/dist/components/AiChat.cjs +17 -21
- package/dist/components/AiChat.cjs.map +1 -1
- package/dist/components/AiChat.js +18 -22
- package/dist/components/AiChat.js.map +1 -1
- package/dist/components/AiTool.cjs +61 -35
- package/dist/components/AiTool.cjs.map +1 -1
- package/dist/components/AiTool.js +62 -36
- package/dist/components/AiTool.js.map +1 -1
- package/dist/components/Comment.cjs +261 -242
- package/dist/components/Comment.cjs.map +1 -1
- package/dist/components/Comment.js +262 -243
- package/dist/components/Comment.js.map +1 -1
- package/dist/components/Composer.cjs +49 -40
- package/dist/components/Composer.cjs.map +1 -1
- package/dist/components/Composer.js +51 -42
- package/dist/components/Composer.js.map +1 -1
- package/dist/components/HistoryVersionSummary.cjs +2 -2
- package/dist/components/HistoryVersionSummary.cjs.map +1 -1
- package/dist/components/HistoryVersionSummary.js +2 -2
- package/dist/components/HistoryVersionSummary.js.map +1 -1
- package/dist/components/HistoryVersionSummaryList.cjs +2 -5
- package/dist/components/HistoryVersionSummaryList.cjs.map +1 -1
- package/dist/components/HistoryVersionSummaryList.js +2 -5
- package/dist/components/HistoryVersionSummaryList.js.map +1 -1
- package/dist/components/InboxNotification.cjs +4 -4
- package/dist/components/InboxNotification.cjs.map +1 -1
- package/dist/components/InboxNotification.js +4 -4
- package/dist/components/InboxNotification.js.map +1 -1
- package/dist/components/InboxNotificationList.cjs +2 -2
- package/dist/components/InboxNotificationList.cjs.map +1 -1
- package/dist/components/InboxNotificationList.js +2 -2
- package/dist/components/InboxNotificationList.js.map +1 -1
- package/dist/components/Thread.cjs +9 -3
- package/dist/components/Thread.cjs.map +1 -1
- package/dist/components/Thread.js +10 -4
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.cjs +47 -10
- package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.js +49 -12
- package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
- package/dist/components/internal/AiChatComposer.cjs +2 -2
- package/dist/components/internal/AiChatComposer.cjs.map +1 -1
- package/dist/components/internal/AiChatComposer.js +2 -2
- package/dist/components/internal/AiChatComposer.js.map +1 -1
- package/dist/components/internal/AiChatUserMessage.cjs +2 -2
- package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatUserMessage.js +2 -2
- package/dist/components/internal/AiChatUserMessage.js.map +1 -1
- package/dist/components/internal/Attachment.cjs +3 -3
- package/dist/components/internal/Attachment.cjs.map +1 -1
- package/dist/components/internal/Attachment.js +3 -3
- package/dist/components/internal/Attachment.js.map +1 -1
- package/dist/components/internal/Attribution.cjs +2 -2
- package/dist/components/internal/Attribution.cjs.map +1 -1
- package/dist/components/internal/Attribution.js +2 -2
- package/dist/components/internal/Attribution.js.map +1 -1
- package/dist/components/internal/Avatar.cjs +2 -2
- package/dist/components/internal/Avatar.cjs.map +1 -1
- package/dist/components/internal/Avatar.js +2 -2
- package/dist/components/internal/Avatar.js.map +1 -1
- package/dist/components/internal/Button.cjs +3 -3
- package/dist/components/internal/Button.cjs.map +1 -1
- package/dist/components/internal/Button.js +3 -3
- package/dist/components/internal/Button.js.map +1 -1
- package/dist/components/internal/CodeBlock.cjs +6 -3
- package/dist/components/internal/CodeBlock.cjs.map +1 -1
- package/dist/components/internal/CodeBlock.js +6 -3
- package/dist/components/internal/CodeBlock.js.map +1 -1
- package/dist/components/internal/Dropdown.cjs +4 -4
- package/dist/components/internal/Dropdown.cjs.map +1 -1
- package/dist/components/internal/Dropdown.js +5 -5
- package/dist/components/internal/Dropdown.js.map +1 -1
- package/dist/components/internal/Emoji.cjs +2 -2
- package/dist/components/internal/Emoji.cjs.map +1 -1
- package/dist/components/internal/Emoji.js +2 -2
- package/dist/components/internal/Emoji.js.map +1 -1
- package/dist/components/internal/EmojiPicker.cjs +6 -6
- package/dist/components/internal/EmojiPicker.cjs.map +1 -1
- package/dist/components/internal/EmojiPicker.js +7 -7
- package/dist/components/internal/EmojiPicker.js.map +1 -1
- package/dist/components/internal/Icon.cjs +2 -2
- package/dist/components/internal/Icon.cjs.map +1 -1
- package/dist/components/internal/Icon.js +2 -2
- package/dist/components/internal/Icon.js.map +1 -1
- package/dist/components/internal/InboxNotificationThread.cjs +7 -4
- package/dist/components/internal/InboxNotificationThread.cjs.map +1 -1
- package/dist/components/internal/InboxNotificationThread.js +8 -5
- package/dist/components/internal/InboxNotificationThread.js.map +1 -1
- package/dist/components/internal/List.cjs +2 -2
- package/dist/components/internal/List.cjs.map +1 -1
- package/dist/components/internal/List.js +2 -2
- package/dist/components/internal/List.js.map +1 -1
- package/dist/components/internal/Prose.cjs +2 -2
- package/dist/components/internal/Prose.cjs.map +1 -1
- package/dist/components/internal/Prose.js +2 -2
- package/dist/components/internal/Prose.js.map +1 -1
- package/dist/components/internal/Room.cjs +2 -2
- package/dist/components/internal/Room.cjs.map +1 -1
- package/dist/components/internal/Room.js +2 -2
- package/dist/components/internal/Room.js.map +1 -1
- package/dist/components/internal/Tooltip.cjs +3 -3
- package/dist/components/internal/Tooltip.cjs.map +1 -1
- package/dist/components/internal/Tooltip.js +4 -4
- package/dist/components/internal/Tooltip.js.map +1 -1
- package/dist/components/internal/User.cjs +2 -2
- package/dist/components/internal/User.cjs.map +1 -1
- package/dist/components/internal/User.js +2 -2
- package/dist/components/internal/User.js.map +1 -1
- package/dist/config.cjs +9 -9
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +8 -8
- package/dist/config.js.map +1 -1
- package/dist/icons/CrossCircleFill.cjs +25 -0
- package/dist/icons/CrossCircleFill.cjs.map +1 -0
- package/dist/icons/CrossCircleFill.js +23 -0
- package/dist/icons/CrossCircleFill.js.map +1 -0
- package/dist/icons/MinusCircle.cjs +23 -0
- package/dist/icons/MinusCircle.cjs.map +1 -0
- package/dist/icons/MinusCircle.js +21 -0
- package/dist/icons/MinusCircle.js.map +1 -0
- package/dist/icons/index.cjs +4 -0
- package/dist/icons/index.cjs.map +1 -1
- package/dist/icons/index.js +2 -0
- package/dist/icons/index.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +238 -25
- package/dist/index.d.ts +238 -25
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/overrides.cjs +5 -1
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.js +5 -1
- package/dist/overrides.js.map +1 -1
- package/dist/primitives/AiMessage/index.cjs +11 -67
- package/dist/primitives/AiMessage/index.cjs.map +1 -1
- package/dist/primitives/AiMessage/index.js +13 -69
- package/dist/primitives/AiMessage/index.js.map +1 -1
- package/dist/primitives/AiMessage/tool-invocation.cjs +70 -0
- package/dist/primitives/AiMessage/tool-invocation.cjs.map +1 -0
- package/dist/primitives/AiMessage/tool-invocation.js +68 -0
- package/dist/primitives/AiMessage/tool-invocation.js.map +1 -0
- package/dist/primitives/Collapsible/index.cjs +3 -3
- package/dist/primitives/Collapsible/index.cjs.map +1 -1
- package/dist/primitives/Collapsible/index.js +3 -3
- package/dist/primitives/Collapsible/index.js.map +1 -1
- package/dist/primitives/Comment/index.cjs +5 -4
- package/dist/primitives/Comment/index.cjs.map +1 -1
- package/dist/primitives/Comment/index.js +5 -4
- package/dist/primitives/Comment/index.js.map +1 -1
- package/dist/primitives/Composer/index.cjs +49 -41
- package/dist/primitives/Composer/index.cjs.map +1 -1
- package/dist/primitives/Composer/index.js +50 -42
- package/dist/primitives/Composer/index.js.map +1 -1
- package/dist/primitives/Composer/slate/plugins/mentions.cjs +4 -4
- package/dist/primitives/Composer/slate/plugins/mentions.cjs.map +1 -1
- package/dist/primitives/Composer/slate/plugins/mentions.js +4 -4
- package/dist/primitives/Composer/slate/plugins/mentions.js.map +1 -1
- package/dist/primitives/Composer/utils.cjs +3 -6
- package/dist/primitives/Composer/utils.cjs.map +1 -1
- package/dist/primitives/Composer/utils.js +3 -6
- package/dist/primitives/Composer/utils.js.map +1 -1
- package/dist/primitives/Markdown.cjs +6 -2
- package/dist/primitives/Markdown.cjs.map +1 -1
- package/dist/primitives/Markdown.js +6 -2
- package/dist/primitives/Markdown.js.map +1 -1
- package/dist/primitives/index.d.cts +18 -18
- package/dist/primitives/index.d.ts +18 -18
- package/dist/utils/{class-names.cjs → cn.cjs} +3 -3
- package/dist/utils/cn.cjs.map +1 -0
- package/dist/utils/{class-names.js → cn.js} +3 -3
- package/dist/utils/cn.js.map +1 -0
- package/dist/utils/use-controllable-state.cjs +25 -2
- package/dist/utils/use-controllable-state.cjs.map +1 -1
- package/dist/utils/use-controllable-state.js +25 -3
- package/dist/utils/use-controllable-state.js.map +1 -1
- package/dist/utils/use-visible.cjs +3 -1
- package/dist/utils/use-visible.cjs.map +1 -1
- package/dist/utils/use-visible.js +3 -1
- package/dist/utils/use-visible.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +16 -5
- package/primitives/package.json +2 -2
- package/src/styles/index.css +26 -7
- package/styles.css +1 -1
- package/styles.css.map +1 -1
- package/dist/utils/class-names.cjs.map +0 -1
- package/dist/utils/class-names.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { memo, forwardRef } from 'react';
|
|
3
3
|
import '../../_private/index.js';
|
|
4
4
|
import { useOverrides } from '../../overrides.js';
|
|
5
|
-
import {
|
|
5
|
+
import { cn } from '../../utils/cn.js';
|
|
6
6
|
import { Content as AiMessageContent } from '../../primitives/AiMessage/index.js';
|
|
7
7
|
|
|
8
8
|
function PlainTextPart({ part }) {
|
|
@@ -16,7 +16,7 @@ const AiChatUserMessage = memo(
|
|
|
16
16
|
const $ = useOverrides(overrides);
|
|
17
17
|
return /* @__PURE__ */ jsx("div", {
|
|
18
18
|
ref: forwardedRef,
|
|
19
|
-
className:
|
|
19
|
+
className: cn(
|
|
20
20
|
"lb-ai-chat-message lb-ai-chat-user-message",
|
|
21
21
|
className
|
|
22
22
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiChatUserMessage.js","sources":["../../../src/components/internal/AiChatUserMessage.tsx"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"AiChatUserMessage.js","sources":["../../../src/components/internal/AiChatUserMessage.tsx"],"sourcesContent":["import type {\n AiTextPart,\n AiUserMessage,\n WithNavigation,\n} from \"@liveblocks/core\";\nimport type { ComponentProps } from \"react\";\nimport { forwardRef, memo } from \"react\";\n\nimport { AiMessage } from \"../../_private\";\nimport { type GlobalOverrides, useOverrides } from \"../../overrides\";\nimport { cn } from \"../../utils/cn\";\n\ntype UiUserMessage = WithNavigation<AiUserMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * AiChatUserMessage\n * -----------------------------------------------------------------------------------------------*/\nexport interface AiChatUserMessageProps extends ComponentProps<\"div\"> {\n /**\n * The message to display.\n */\n message: UiUserMessage;\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides>;\n}\n\ntype PlainTextPartProps = {\n part: AiTextPart;\n};\n\nfunction PlainTextPart({ part }: PlainTextPartProps) {\n return <p>{part.text}</p>;\n}\n\nexport const AiChatUserMessage = memo(\n forwardRef<HTMLDivElement, AiChatUserMessageProps>(\n ({ message, className, overrides }, forwardedRef) => {\n const $ = useOverrides(overrides);\n return (\n <div\n ref={forwardedRef}\n className={cn(\n \"lb-ai-chat-message lb-ai-chat-user-message\",\n className\n )}\n >\n {message.deletedAt !== undefined ? (\n <div className=\"lb-ai-chat-message-deleted\">\n {$.AI_CHAT_MESSAGE_DELETED}\n </div>\n ) : (\n <div className=\"lb-ai-chat-message-content\">\n <AiMessage.Content\n message={message}\n components={{\n TextPart: PlainTextPart,\n }}\n className=\"lb-prose lb-ai-chat-message-text\"\n />\n </div>\n )}\n </div>\n );\n }\n )\n);\n"],"names":["AiMessage.Content"],"mappings":";;;;;;;AAgCA,SAAS,aAAA,CAAc,EAAE,IAAA,EAA4B,EAAA;AACnD,EAAA,uBAAQ,GAAA,CAAA,GAAA,EAAA;AAAA,IAAG,QAAK,EAAA,IAAA,CAAA,IAAA;AAAA,GAAK,CAAA,CAAA;AACvB,CAAA;AAEO,MAAM,iBAAoB,GAAA,IAAA;AAAA,EAC/B,UAAA;AAAA,IACE,CAAC,EAAE,OAAA,EAAS,SAAW,EAAA,SAAA,IAAa,YAAiB,KAAA;AACnD,MAAM,MAAA,CAAA,GAAI,aAAa,SAAS,CAAA,CAAA;AAChC,MAAA,uBACG,GAAA,CAAA,KAAA,EAAA;AAAA,QACC,GAAK,EAAA,YAAA;AAAA,QACL,SAAW,EAAA,EAAA;AAAA,UACT,4CAAA;AAAA,UACA,SAAA;AAAA,SACF;AAAA,QAEC,QAAA,EAAA,OAAA,CAAQ,SAAc,KAAA,KAAA,CAAA,mBACpB,GAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,4BAAA;AAAA,UACZ,QAAE,EAAA,CAAA,CAAA,uBAAA;AAAA,SACL,oBAEC,GAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,4BAAA;AAAA,UACb,QAAA,kBAAA,GAAA,CAACA,gBAAA,EAAA;AAAA,YACC,OAAA;AAAA,YACA,UAAY,EAAA;AAAA,cACV,QAAU,EAAA,aAAA;AAAA,aACZ;AAAA,YACA,SAAU,EAAA,kCAAA;AAAA,WACZ,CAAA;AAAA,SACF,CAAA;AAAA,OAEJ,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF;AACF;;;;"}
|
|
@@ -10,7 +10,7 @@ var Warning = require('../../icons/Warning.cjs');
|
|
|
10
10
|
var overrides = require('../../overrides.cjs');
|
|
11
11
|
require('../../primitives/index.cjs');
|
|
12
12
|
var contexts = require('../../primitives/Composer/contexts.cjs');
|
|
13
|
-
var
|
|
13
|
+
var cn = require('../../utils/cn.cjs');
|
|
14
14
|
var formatFileSize = require('../../utils/format-file-size.cjs');
|
|
15
15
|
var Tooltip = require('./Tooltip.cjs');
|
|
16
16
|
var utils = require('../../primitives/Composer/utils.cjs');
|
|
@@ -259,7 +259,7 @@ function MediaAttachment({
|
|
|
259
259
|
);
|
|
260
260
|
const handleKeyDown = useClickOnKeyDown(onKeyDown);
|
|
261
261
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
262
|
-
className:
|
|
262
|
+
className: cn.cn("lb-attachment lb-media-attachment", className),
|
|
263
263
|
"data-error": isError ? "" : void 0,
|
|
264
264
|
...props,
|
|
265
265
|
role: onClick ? "button" : void 0,
|
|
@@ -325,7 +325,7 @@ function FileAttachment({
|
|
|
325
325
|
);
|
|
326
326
|
const handleKeyDown = useClickOnKeyDown(onKeyDown);
|
|
327
327
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
328
|
-
className:
|
|
328
|
+
className: cn.cn("lb-attachment lb-file-attachment", className),
|
|
329
329
|
"data-error": isError ? "" : void 0,
|
|
330
330
|
...props,
|
|
331
331
|
role: onClick ? "button" : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attachment.cjs","sources":["../../../src/components/internal/Attachment.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { useRoomAttachmentUrl } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n KeyboardEvent,\n MouseEventHandler,\n PointerEvent,\n} from \"react\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\nimport { CrossIcon } from \"../../icons/Cross\";\nimport { SpinnerIcon } from \"../../icons/Spinner\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport type { Overrides } from \"../../overrides\";\nimport { useOverrides } from \"../../overrides\";\nimport { AttachmentTooLargeError } from \"../../primitives\";\nimport { useComposerAttachmentsContextOrNull } from \"../../primitives/Composer/contexts\";\nimport { classNames } from \"../../utils/class-names\";\nimport { formatFileSize } from \"../../utils/format-file-size\";\nimport { Tooltip } from \"./Tooltip\";\n\nconst MAX_DISPLAYED_MEDIA_SIZE = 60 * 1024 * 1024; // 60 MB\n\ninterface AttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n onDeleteClick?: MouseEventHandler<HTMLButtonElement>;\n preventFocusOnDelete?: boolean;\n roomId: string;\n overrides?: Partial<Overrides>;\n allowMediaPreview?: boolean;\n}\n\nconst fileExtensionRegex = /^(.+?)(\\.[^.]+)?$/;\n\nfunction splitFileName(name: string) {\n const match = name.match(fileExtensionRegex);\n\n return { base: match?.[1] ?? name, extension: match?.[2] };\n}\n\nfunction getAttachmentIconGlyph(mimeType: string) {\n if (\n mimeType === \"application/zip\" ||\n mimeType === \"application/gzip\" ||\n mimeType === \"application/vnd.rar\" ||\n mimeType === \"application/x-rar-compressed\" ||\n mimeType === \"application/x-7z-compressed\" ||\n mimeType === \"application/x-zip-compressed\" ||\n mimeType === \"application/x-tar\" ||\n mimeType === \"application/x-bzip\" ||\n mimeType === \"application/x-bzip2\"\n ) {\n return (\n <path d=\"M13 15h2v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 .5-.5V15a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2Z\" />\n );\n }\n\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType.startsWith(\"font/\") ||\n mimeType.startsWith(\"application/\")\n ) {\n return (\n <path d=\"M10 16a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1h-8a.5.5 0 0 1-.5-.5Z\" />\n );\n }\n\n if (mimeType.startsWith(\"image/\")) {\n return (\n <path d=\"M12 16h6a1 1 0 0 1 1 1v3l-1.293-1.293a1 1 0 0 0-1.414 0L14.09 20.91l-.464-.386a1 1 0 0 0-1.265-.013l-1.231.985A.995.995 0 0 1 11 21v-4a1 1 0 0 1 1-1Zm-2 1a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-6a2 2 0 0 1-2-2v-4Zm3 2a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z\" />\n );\n }\n\n if (mimeType.startsWith(\"video/\")) {\n return (\n <path d=\"M12 15.71a1 1 0 0 1 1.49-.872l4.96 2.79a1 1 0 0 1 0 1.744l-4.96 2.79A1 1 0 0 1 12 21.29v-5.58Z\" />\n );\n }\n\n if (mimeType.startsWith(\"audio/\")) {\n return (\n <path d=\"M15 15a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 1 0v-7a.5.5 0 0 0-.5-.5Zm-2.5 2.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3Zm-2 1a.5.5 0 0 1 1 0v1a.5.5 0 0 1-1 0v-1Zm6-1a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3ZM19 16a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 1 0v-5a.5.5 0 0 0-.5-.5Z\" />\n );\n }\n\n return null;\n}\n\nconst AttachmentFileIcon = memo(({ mimeType }: { mimeType: string }) => {\n const iconGlyph = useMemo(() => getAttachmentIconGlyph(mimeType), [mimeType]);\n\n return (\n <svg\n className=\"lb-attachment-icon\"\n width={30}\n height={30}\n viewBox=\"0 0 30 30\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-shadow\"\n />\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-background\"\n />\n <path\n d=\"M14.382 3.037a4 4 0 0 1 2.29 1.135l6.156 6.157a4 4 0 0 1 1.136 2.289A2 2 0 0 0 22 11h-4a2 2 0 0 1-2-2V5a2 2 0 0 0-1.618-1.963Z\"\n className=\"lb-attachment-icon-fold\"\n />\n\n {iconGlyph && <g className=\"lb-attachment-icon-glyph\">{iconGlyph}</g>}\n </svg>\n );\n});\n\nfunction AttachmentImagePreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <img\n src={url}\n loading=\"lazy\"\n onLoad={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentVideoPreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <video\n src={url}\n onLoadedData={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentPreview({\n attachment,\n allowMediaPreview = true,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n allowMediaPreview?: boolean;\n roomId: string;\n}) {\n const [isUnsupportedPreview, setUnsupportedPreview] = useState(false);\n const isUploaded =\n attachment.type === \"attachment\" || attachment.status === \"uploaded\";\n\n function markPreviewAsUnsupported() {\n setUnsupportedPreview(true);\n }\n\n if (\n !isUnsupportedPreview &&\n allowMediaPreview &&\n isUploaded &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n if (attachment.mimeType.startsWith(\"image/\")) {\n return (\n <AttachmentImagePreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n\n if (attachment.mimeType.startsWith(\"video/\")) {\n return (\n <AttachmentVideoPreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n }\n\n return <AttachmentFileIcon mimeType={attachment.mimeType} />;\n}\n\nfunction AttachmentName({\n attachment,\n}: {\n attachment: CommentMixedAttachment;\n}) {\n const { base: fileBaseName, extension: fileExtension } = useMemo(() => {\n return splitFileName(attachment.name);\n }, [attachment.name]);\n\n return (\n <span className=\"lb-attachment-name\" title={attachment.name}>\n <span className=\"lb-attachment-name-base\">{fileBaseName}</span>\n {fileExtension && (\n <span className=\"lb-attachment-name-extension\">{fileExtension}</span>\n )}\n </span>\n );\n}\n\nfunction useClickOnKeyDown(\n onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void\n) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Simulate a click event on Enter or Space because it's a div\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n\n const clickEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n event.target.dispatchEvent(clickEvent);\n }\n },\n [onKeyDown]\n );\n\n return handleKeyDown;\n}\n\nfunction useAttachmentContent(\n attachment: CommentMixedAttachment,\n overrides?: Partial<Overrides>\n) {\n const $ = useOverrides(overrides);\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n const isInComposer = Boolean(composerAttachmentsContext);\n const maxAttachmentSize = composerAttachmentsContext?.maxAttachmentSize;\n\n const status =\n attachment.type === \"localAttachment\" ? attachment.status : undefined;\n const isUploading = status === \"uploading\";\n const isError = status === \"error\";\n\n let description: string;\n\n if (attachment.type === \"localAttachment\" && attachment.status === \"error\") {\n if (attachment.error instanceof AttachmentTooLargeError) {\n if (attachment.error.origin === \"server\") {\n description = $.ATTACHMENT_TOO_LARGE();\n } else {\n description = $.ATTACHMENT_TOO_LARGE(\n maxAttachmentSize\n ? formatFileSize(maxAttachmentSize, $.locale)\n : undefined\n );\n }\n } else {\n description = $.ATTACHMENT_ERROR(attachment.error);\n }\n } else {\n description = formatFileSize(attachment.size, $.locale);\n }\n\n const deleteLabel = isInComposer\n ? $.COMPOSER_REMOVE_ATTACHMENT\n : $.COMMENT_DELETE_ATTACHMENT;\n\n return {\n isUploading,\n isError,\n description,\n deleteLabel,\n };\n}\n\nexport function MediaAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={classNames(\"lb-attachment lb-media-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function FileAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={classNames(\"lb-attachment lb-file-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function separateMediaAttachments<T extends CommentMixedAttachment>(\n attachments: T[]\n) {\n const mediaAttachments: T[] = [];\n const fileAttachments: T[] = [];\n\n for (const attachment of attachments) {\n if (\n (attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\")) &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n mediaAttachments.push(attachment);\n } else {\n fileAttachments.push(attachment);\n }\n }\n\n return {\n mediaAttachments,\n fileAttachments,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAuBA;AAWA;AAEA;AACE;AAEA;AACF;AAEA;AACE;AAWE;AACG;AAAO;AAAoP;AAIhQ;AAKE;AACG;AAAO;AAAiP;AAI7P;AACE;AACG;AAAO;AAAiQ;AAI7Q;AACE;AACG;AAAO;AAAiG;AAI7G;AACE;AACG;AAAO;AAAyP;AAIrQ;AACF;AAEA;AACE;AAEA;AACG;AACW;AACH;AACC;AACA;AACH;AACI;AACA;AACH;AAEN;AAAC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AAEe;AAAY;AAA4B;AAAU;AAAA;AAGvE;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACG;AACA;AACC;AACX;AAEA;AAAA;AAGV;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACS;AACL;AACX;AAEA;AAAA;AAGV;AAEA;AAA2B;AACzB;AACoB;AAEtB;AAKE;AACA;AAGA;AACE;AAA0B;AAG5B;AAME;AACE;AACG;AACC;AACA;AACA;AACF;AAIJ;AACE;AACG;AACC;AACA;AACA;AACF;AAEJ;AAGF;AAAQ;AAAwC;AAClD;AAEA;AAAwB;AAExB;AAGE;AACE;AAAoC;AAGtC;AACG;AAAe;AAAuC;AACrD;AAAC;AAAe;AAA2B;AAAa;AAErD;AAAe;AAAgC;AAAc;AAAA;AAItE;AAEA;AAGE;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAEA;AAA2C;AAChC;AACG;AACN;AAER;AAAqC;AACvC;AACF;AACU;AAGZ;AACF;AAEA;AAIE;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACE;AACE;AACE;AAAqC;AAErC;AAAgB;AAGV;AACN;AACF;AAEA;AAAiD;AACnD;AAEA;AAAsD;AAGxD;AAIA;AAAO;AACL;AACA;AACA;AACA;AAEJ;AAEO;AAAyB;AAC9B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AACqE;AACzC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAAwB;AAC7B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AACoE;AACxC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAGL;AACA;AAEA;AACE;AAKE;AAAgC;AAEhC;AAA+B;AACjC;AAGF;AAAO;AACL;AACA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"Attachment.cjs","sources":["../../../src/components/internal/Attachment.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { useRoomAttachmentUrl } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n KeyboardEvent,\n MouseEventHandler,\n PointerEvent,\n} from \"react\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\nimport { CrossIcon } from \"../../icons/Cross\";\nimport { SpinnerIcon } from \"../../icons/Spinner\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport type { Overrides } from \"../../overrides\";\nimport { useOverrides } from \"../../overrides\";\nimport { AttachmentTooLargeError } from \"../../primitives\";\nimport { useComposerAttachmentsContextOrNull } from \"../../primitives/Composer/contexts\";\nimport { cn } from \"../../utils/cn\";\nimport { formatFileSize } from \"../../utils/format-file-size\";\nimport { Tooltip } from \"./Tooltip\";\n\nconst MAX_DISPLAYED_MEDIA_SIZE = 60 * 1024 * 1024; // 60 MB\n\ninterface AttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n onDeleteClick?: MouseEventHandler<HTMLButtonElement>;\n preventFocusOnDelete?: boolean;\n roomId: string;\n overrides?: Partial<Overrides>;\n allowMediaPreview?: boolean;\n}\n\nconst fileExtensionRegex = /^(.+?)(\\.[^.]+)?$/;\n\nfunction splitFileName(name: string) {\n const match = name.match(fileExtensionRegex);\n\n return { base: match?.[1] ?? name, extension: match?.[2] };\n}\n\nfunction getAttachmentIconGlyph(mimeType: string) {\n if (\n mimeType === \"application/zip\" ||\n mimeType === \"application/gzip\" ||\n mimeType === \"application/vnd.rar\" ||\n mimeType === \"application/x-rar-compressed\" ||\n mimeType === \"application/x-7z-compressed\" ||\n mimeType === \"application/x-zip-compressed\" ||\n mimeType === \"application/x-tar\" ||\n mimeType === \"application/x-bzip\" ||\n mimeType === \"application/x-bzip2\"\n ) {\n return (\n <path d=\"M13 15h2v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 .5-.5V15a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2Z\" />\n );\n }\n\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType.startsWith(\"font/\") ||\n mimeType.startsWith(\"application/\")\n ) {\n return (\n <path d=\"M10 16a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1h-8a.5.5 0 0 1-.5-.5Z\" />\n );\n }\n\n if (mimeType.startsWith(\"image/\")) {\n return (\n <path d=\"M12 16h6a1 1 0 0 1 1 1v3l-1.293-1.293a1 1 0 0 0-1.414 0L14.09 20.91l-.464-.386a1 1 0 0 0-1.265-.013l-1.231.985A.995.995 0 0 1 11 21v-4a1 1 0 0 1 1-1Zm-2 1a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-6a2 2 0 0 1-2-2v-4Zm3 2a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z\" />\n );\n }\n\n if (mimeType.startsWith(\"video/\")) {\n return (\n <path d=\"M12 15.71a1 1 0 0 1 1.49-.872l4.96 2.79a1 1 0 0 1 0 1.744l-4.96 2.79A1 1 0 0 1 12 21.29v-5.58Z\" />\n );\n }\n\n if (mimeType.startsWith(\"audio/\")) {\n return (\n <path d=\"M15 15a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 1 0v-7a.5.5 0 0 0-.5-.5Zm-2.5 2.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3Zm-2 1a.5.5 0 0 1 1 0v1a.5.5 0 0 1-1 0v-1Zm6-1a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3ZM19 16a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 1 0v-5a.5.5 0 0 0-.5-.5Z\" />\n );\n }\n\n return null;\n}\n\nconst AttachmentFileIcon = memo(({ mimeType }: { mimeType: string }) => {\n const iconGlyph = useMemo(() => getAttachmentIconGlyph(mimeType), [mimeType]);\n\n return (\n <svg\n className=\"lb-attachment-icon\"\n width={30}\n height={30}\n viewBox=\"0 0 30 30\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-shadow\"\n />\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-background\"\n />\n <path\n d=\"M14.382 3.037a4 4 0 0 1 2.29 1.135l6.156 6.157a4 4 0 0 1 1.136 2.289A2 2 0 0 0 22 11h-4a2 2 0 0 1-2-2V5a2 2 0 0 0-1.618-1.963Z\"\n className=\"lb-attachment-icon-fold\"\n />\n\n {iconGlyph && <g className=\"lb-attachment-icon-glyph\">{iconGlyph}</g>}\n </svg>\n );\n});\n\nfunction AttachmentImagePreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <img\n src={url}\n loading=\"lazy\"\n onLoad={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentVideoPreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <video\n src={url}\n onLoadedData={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentPreview({\n attachment,\n allowMediaPreview = true,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n allowMediaPreview?: boolean;\n roomId: string;\n}) {\n const [isUnsupportedPreview, setUnsupportedPreview] = useState(false);\n const isUploaded =\n attachment.type === \"attachment\" || attachment.status === \"uploaded\";\n\n function markPreviewAsUnsupported() {\n setUnsupportedPreview(true);\n }\n\n if (\n !isUnsupportedPreview &&\n allowMediaPreview &&\n isUploaded &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n if (attachment.mimeType.startsWith(\"image/\")) {\n return (\n <AttachmentImagePreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n\n if (attachment.mimeType.startsWith(\"video/\")) {\n return (\n <AttachmentVideoPreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n }\n\n return <AttachmentFileIcon mimeType={attachment.mimeType} />;\n}\n\nfunction AttachmentName({\n attachment,\n}: {\n attachment: CommentMixedAttachment;\n}) {\n const { base: fileBaseName, extension: fileExtension } = useMemo(() => {\n return splitFileName(attachment.name);\n }, [attachment.name]);\n\n return (\n <span className=\"lb-attachment-name\" title={attachment.name}>\n <span className=\"lb-attachment-name-base\">{fileBaseName}</span>\n {fileExtension && (\n <span className=\"lb-attachment-name-extension\">{fileExtension}</span>\n )}\n </span>\n );\n}\n\nfunction useClickOnKeyDown(\n onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void\n) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Simulate a click event on Enter or Space because it's a div\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n\n const clickEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n event.target.dispatchEvent(clickEvent);\n }\n },\n [onKeyDown]\n );\n\n return handleKeyDown;\n}\n\nfunction useAttachmentContent(\n attachment: CommentMixedAttachment,\n overrides?: Partial<Overrides>\n) {\n const $ = useOverrides(overrides);\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n const isInComposer = Boolean(composerAttachmentsContext);\n const maxAttachmentSize = composerAttachmentsContext?.maxAttachmentSize;\n\n const status =\n attachment.type === \"localAttachment\" ? attachment.status : undefined;\n const isUploading = status === \"uploading\";\n const isError = status === \"error\";\n\n let description: string;\n\n if (attachment.type === \"localAttachment\" && attachment.status === \"error\") {\n if (attachment.error instanceof AttachmentTooLargeError) {\n if (attachment.error.origin === \"server\") {\n description = $.ATTACHMENT_TOO_LARGE();\n } else {\n description = $.ATTACHMENT_TOO_LARGE(\n maxAttachmentSize\n ? formatFileSize(maxAttachmentSize, $.locale)\n : undefined\n );\n }\n } else {\n description = $.ATTACHMENT_ERROR(attachment.error);\n }\n } else {\n description = formatFileSize(attachment.size, $.locale);\n }\n\n const deleteLabel = isInComposer\n ? $.COMPOSER_REMOVE_ATTACHMENT\n : $.COMMENT_DELETE_ATTACHMENT;\n\n return {\n isUploading,\n isError,\n description,\n deleteLabel,\n };\n}\n\nexport function MediaAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={cn(\"lb-attachment lb-media-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function FileAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={cn(\"lb-attachment lb-file-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function separateMediaAttachments<T extends CommentMixedAttachment>(\n attachments: T[]\n) {\n const mediaAttachments: T[] = [];\n const fileAttachments: T[] = [];\n\n for (const attachment of attachments) {\n if (\n (attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\")) &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n mediaAttachments.push(attachment);\n } else {\n fileAttachments.push(attachment);\n }\n }\n\n return {\n mediaAttachments,\n fileAttachments,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAuBA;AAWA;AAEA;AACE;AAEA;AACF;AAEA;AACE;AAWE;AACG;AAAO;AAAoP;AAIhQ;AAKE;AACG;AAAO;AAAiP;AAI7P;AACE;AACG;AAAO;AAAiQ;AAI7Q;AACE;AACG;AAAO;AAAiG;AAI7G;AACE;AACG;AAAO;AAAyP;AAIrQ;AACF;AAEA;AACE;AAEA;AACG;AACW;AACH;AACC;AACA;AACH;AACI;AACA;AACH;AAEN;AAAC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AAEe;AAAY;AAA4B;AAAU;AAAA;AAGvE;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACG;AACA;AACC;AACX;AAEA;AAAA;AAGV;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACS;AACL;AACX;AAEA;AAAA;AAGV;AAEA;AAA2B;AACzB;AACoB;AAEtB;AAKE;AACA;AAGA;AACE;AAA0B;AAG5B;AAME;AACE;AACG;AACC;AACA;AACA;AACF;AAIJ;AACE;AACG;AACC;AACA;AACA;AACF;AAEJ;AAGF;AAAQ;AAAwC;AAClD;AAEA;AAAwB;AAExB;AAGE;AACE;AAAoC;AAGtC;AACG;AAAe;AAAuC;AACrD;AAAC;AAAe;AAA2B;AAAa;AAErD;AAAe;AAAgC;AAAc;AAAA;AAItE;AAEA;AAGE;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAEA;AAA2C;AAChC;AACG;AACN;AAER;AAAqC;AACvC;AACF;AACU;AAGZ;AACF;AAEA;AAIE;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACE;AACE;AACE;AAAqC;AAErC;AAAgB;AAGV;AACN;AACF;AAEA;AAAiD;AACnD;AAEA;AAAsD;AAGxD;AAIA;AAAO;AACL;AACA;AACA;AACA;AAEJ;AAEO;AAAyB;AAC9B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AAC6D;AACjC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAAwB;AAC7B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AAC4D;AAChC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAGL;AACA;AAEA;AACE;AAKE;AAAgC;AAEhC;AAA+B;AACjC;AAGF;AAAO;AACL;AACA;AAEJ;;;;"}
|
|
@@ -8,7 +8,7 @@ import { WarningIcon } from '../../icons/Warning.js';
|
|
|
8
8
|
import { useOverrides } from '../../overrides.js';
|
|
9
9
|
import '../../primitives/index.js';
|
|
10
10
|
import { useComposerAttachmentsContextOrNull } from '../../primitives/Composer/contexts.js';
|
|
11
|
-
import {
|
|
11
|
+
import { cn } from '../../utils/cn.js';
|
|
12
12
|
import { formatFileSize } from '../../utils/format-file-size.js';
|
|
13
13
|
import { Tooltip } from './Tooltip.js';
|
|
14
14
|
import { AttachmentTooLargeError } from '../../primitives/Composer/utils.js';
|
|
@@ -257,7 +257,7 @@ function MediaAttachment({
|
|
|
257
257
|
);
|
|
258
258
|
const handleKeyDown = useClickOnKeyDown(onKeyDown);
|
|
259
259
|
return /* @__PURE__ */ jsxs("div", {
|
|
260
|
-
className:
|
|
260
|
+
className: cn("lb-attachment lb-media-attachment", className),
|
|
261
261
|
"data-error": isError ? "" : void 0,
|
|
262
262
|
...props,
|
|
263
263
|
role: onClick ? "button" : void 0,
|
|
@@ -323,7 +323,7 @@ function FileAttachment({
|
|
|
323
323
|
);
|
|
324
324
|
const handleKeyDown = useClickOnKeyDown(onKeyDown);
|
|
325
325
|
return /* @__PURE__ */ jsxs("div", {
|
|
326
|
-
className:
|
|
326
|
+
className: cn("lb-attachment lb-file-attachment", className),
|
|
327
327
|
"data-error": isError ? "" : void 0,
|
|
328
328
|
...props,
|
|
329
329
|
role: onClick ? "button" : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attachment.js","sources":["../../../src/components/internal/Attachment.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { useRoomAttachmentUrl } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n KeyboardEvent,\n MouseEventHandler,\n PointerEvent,\n} from \"react\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\nimport { CrossIcon } from \"../../icons/Cross\";\nimport { SpinnerIcon } from \"../../icons/Spinner\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport type { Overrides } from \"../../overrides\";\nimport { useOverrides } from \"../../overrides\";\nimport { AttachmentTooLargeError } from \"../../primitives\";\nimport { useComposerAttachmentsContextOrNull } from \"../../primitives/Composer/contexts\";\nimport { classNames } from \"../../utils/class-names\";\nimport { formatFileSize } from \"../../utils/format-file-size\";\nimport { Tooltip } from \"./Tooltip\";\n\nconst MAX_DISPLAYED_MEDIA_SIZE = 60 * 1024 * 1024; // 60 MB\n\ninterface AttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n onDeleteClick?: MouseEventHandler<HTMLButtonElement>;\n preventFocusOnDelete?: boolean;\n roomId: string;\n overrides?: Partial<Overrides>;\n allowMediaPreview?: boolean;\n}\n\nconst fileExtensionRegex = /^(.+?)(\\.[^.]+)?$/;\n\nfunction splitFileName(name: string) {\n const match = name.match(fileExtensionRegex);\n\n return { base: match?.[1] ?? name, extension: match?.[2] };\n}\n\nfunction getAttachmentIconGlyph(mimeType: string) {\n if (\n mimeType === \"application/zip\" ||\n mimeType === \"application/gzip\" ||\n mimeType === \"application/vnd.rar\" ||\n mimeType === \"application/x-rar-compressed\" ||\n mimeType === \"application/x-7z-compressed\" ||\n mimeType === \"application/x-zip-compressed\" ||\n mimeType === \"application/x-tar\" ||\n mimeType === \"application/x-bzip\" ||\n mimeType === \"application/x-bzip2\"\n ) {\n return (\n <path d=\"M13 15h2v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 .5-.5V15a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2Z\" />\n );\n }\n\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType.startsWith(\"font/\") ||\n mimeType.startsWith(\"application/\")\n ) {\n return (\n <path d=\"M10 16a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1h-8a.5.5 0 0 1-.5-.5Z\" />\n );\n }\n\n if (mimeType.startsWith(\"image/\")) {\n return (\n <path d=\"M12 16h6a1 1 0 0 1 1 1v3l-1.293-1.293a1 1 0 0 0-1.414 0L14.09 20.91l-.464-.386a1 1 0 0 0-1.265-.013l-1.231.985A.995.995 0 0 1 11 21v-4a1 1 0 0 1 1-1Zm-2 1a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-6a2 2 0 0 1-2-2v-4Zm3 2a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z\" />\n );\n }\n\n if (mimeType.startsWith(\"video/\")) {\n return (\n <path d=\"M12 15.71a1 1 0 0 1 1.49-.872l4.96 2.79a1 1 0 0 1 0 1.744l-4.96 2.79A1 1 0 0 1 12 21.29v-5.58Z\" />\n );\n }\n\n if (mimeType.startsWith(\"audio/\")) {\n return (\n <path d=\"M15 15a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 1 0v-7a.5.5 0 0 0-.5-.5Zm-2.5 2.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3Zm-2 1a.5.5 0 0 1 1 0v1a.5.5 0 0 1-1 0v-1Zm6-1a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3ZM19 16a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 1 0v-5a.5.5 0 0 0-.5-.5Z\" />\n );\n }\n\n return null;\n}\n\nconst AttachmentFileIcon = memo(({ mimeType }: { mimeType: string }) => {\n const iconGlyph = useMemo(() => getAttachmentIconGlyph(mimeType), [mimeType]);\n\n return (\n <svg\n className=\"lb-attachment-icon\"\n width={30}\n height={30}\n viewBox=\"0 0 30 30\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-shadow\"\n />\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-background\"\n />\n <path\n d=\"M14.382 3.037a4 4 0 0 1 2.29 1.135l6.156 6.157a4 4 0 0 1 1.136 2.289A2 2 0 0 0 22 11h-4a2 2 0 0 1-2-2V5a2 2 0 0 0-1.618-1.963Z\"\n className=\"lb-attachment-icon-fold\"\n />\n\n {iconGlyph && <g className=\"lb-attachment-icon-glyph\">{iconGlyph}</g>}\n </svg>\n );\n});\n\nfunction AttachmentImagePreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <img\n src={url}\n loading=\"lazy\"\n onLoad={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentVideoPreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <video\n src={url}\n onLoadedData={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentPreview({\n attachment,\n allowMediaPreview = true,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n allowMediaPreview?: boolean;\n roomId: string;\n}) {\n const [isUnsupportedPreview, setUnsupportedPreview] = useState(false);\n const isUploaded =\n attachment.type === \"attachment\" || attachment.status === \"uploaded\";\n\n function markPreviewAsUnsupported() {\n setUnsupportedPreview(true);\n }\n\n if (\n !isUnsupportedPreview &&\n allowMediaPreview &&\n isUploaded &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n if (attachment.mimeType.startsWith(\"image/\")) {\n return (\n <AttachmentImagePreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n\n if (attachment.mimeType.startsWith(\"video/\")) {\n return (\n <AttachmentVideoPreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n }\n\n return <AttachmentFileIcon mimeType={attachment.mimeType} />;\n}\n\nfunction AttachmentName({\n attachment,\n}: {\n attachment: CommentMixedAttachment;\n}) {\n const { base: fileBaseName, extension: fileExtension } = useMemo(() => {\n return splitFileName(attachment.name);\n }, [attachment.name]);\n\n return (\n <span className=\"lb-attachment-name\" title={attachment.name}>\n <span className=\"lb-attachment-name-base\">{fileBaseName}</span>\n {fileExtension && (\n <span className=\"lb-attachment-name-extension\">{fileExtension}</span>\n )}\n </span>\n );\n}\n\nfunction useClickOnKeyDown(\n onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void\n) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Simulate a click event on Enter or Space because it's a div\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n\n const clickEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n event.target.dispatchEvent(clickEvent);\n }\n },\n [onKeyDown]\n );\n\n return handleKeyDown;\n}\n\nfunction useAttachmentContent(\n attachment: CommentMixedAttachment,\n overrides?: Partial<Overrides>\n) {\n const $ = useOverrides(overrides);\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n const isInComposer = Boolean(composerAttachmentsContext);\n const maxAttachmentSize = composerAttachmentsContext?.maxAttachmentSize;\n\n const status =\n attachment.type === \"localAttachment\" ? attachment.status : undefined;\n const isUploading = status === \"uploading\";\n const isError = status === \"error\";\n\n let description: string;\n\n if (attachment.type === \"localAttachment\" && attachment.status === \"error\") {\n if (attachment.error instanceof AttachmentTooLargeError) {\n if (attachment.error.origin === \"server\") {\n description = $.ATTACHMENT_TOO_LARGE();\n } else {\n description = $.ATTACHMENT_TOO_LARGE(\n maxAttachmentSize\n ? formatFileSize(maxAttachmentSize, $.locale)\n : undefined\n );\n }\n } else {\n description = $.ATTACHMENT_ERROR(attachment.error);\n }\n } else {\n description = formatFileSize(attachment.size, $.locale);\n }\n\n const deleteLabel = isInComposer\n ? $.COMPOSER_REMOVE_ATTACHMENT\n : $.COMMENT_DELETE_ATTACHMENT;\n\n return {\n isUploading,\n isError,\n description,\n deleteLabel,\n };\n}\n\nexport function MediaAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={classNames(\"lb-attachment lb-media-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function FileAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={classNames(\"lb-attachment lb-file-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function separateMediaAttachments<T extends CommentMixedAttachment>(\n attachments: T[]\n) {\n const mediaAttachments: T[] = [];\n const fileAttachments: T[] = [];\n\n for (const attachment of attachments) {\n if (\n (attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\")) &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n mediaAttachments.push(attachment);\n } else {\n fileAttachments.push(attachment);\n }\n }\n\n return {\n mediaAttachments,\n fileAttachments,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAuBA;AAWA;AAEA;AACE;AAEA;AACF;AAEA;AACE;AAWE;AACG;AAAO;AAAoP;AAIhQ;AAKE;AACG;AAAO;AAAiP;AAI7P;AACE;AACG;AAAO;AAAiQ;AAI7Q;AACE;AACG;AAAO;AAAiG;AAI7G;AACE;AACG;AAAO;AAAyP;AAIrQ;AACF;AAEA;AACE;AAEA;AACG;AACW;AACH;AACC;AACA;AACH;AACI;AACA;AACH;AAEN;AAAC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AAEe;AAAY;AAA4B;AAAU;AAAA;AAGvE;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACG;AACA;AACC;AACX;AAEA;AAAA;AAGV;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACS;AACL;AACX;AAEA;AAAA;AAGV;AAEA;AAA2B;AACzB;AACoB;AAEtB;AAKE;AACA;AAGA;AACE;AAA0B;AAG5B;AAME;AACE;AACG;AACC;AACA;AACA;AACF;AAIJ;AACE;AACG;AACC;AACA;AACA;AACF;AAEJ;AAGF;AAAQ;AAAwC;AAClD;AAEA;AAAwB;AAExB;AAGE;AACE;AAAoC;AAGtC;AACG;AAAe;AAAuC;AACrD;AAAC;AAAe;AAA2B;AAAa;AAErD;AAAe;AAAgC;AAAc;AAAA;AAItE;AAEA;AAGE;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAEA;AAA2C;AAChC;AACG;AACN;AAER;AAAqC;AACvC;AACF;AACU;AAGZ;AACF;AAEA;AAIE;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACE;AACE;AACE;AAAqC;AAErC;AAAgB;AAGV;AACN;AACF;AAEA;AAAiD;AACnD;AAEA;AAAsD;AAGxD;AAIA;AAAO;AACL;AACA;AACA;AACA;AAEJ;AAEO;AAAyB;AAC9B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AACqE;AACzC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAAwB;AAC7B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AACoE;AACxC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAGL;AACA;AAEA;AACE;AAKE;AAAgC;AAEhC;AAA+B;AACjC;AAGF;AAAO;AACL;AACA;AAEJ;;"}
|
|
1
|
+
{"version":3,"file":"Attachment.js","sources":["../../../src/components/internal/Attachment.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { useRoomAttachmentUrl } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n KeyboardEvent,\n MouseEventHandler,\n PointerEvent,\n} from \"react\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\nimport { CrossIcon } from \"../../icons/Cross\";\nimport { SpinnerIcon } from \"../../icons/Spinner\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport type { Overrides } from \"../../overrides\";\nimport { useOverrides } from \"../../overrides\";\nimport { AttachmentTooLargeError } from \"../../primitives\";\nimport { useComposerAttachmentsContextOrNull } from \"../../primitives/Composer/contexts\";\nimport { cn } from \"../../utils/cn\";\nimport { formatFileSize } from \"../../utils/format-file-size\";\nimport { Tooltip } from \"./Tooltip\";\n\nconst MAX_DISPLAYED_MEDIA_SIZE = 60 * 1024 * 1024; // 60 MB\n\ninterface AttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n onDeleteClick?: MouseEventHandler<HTMLButtonElement>;\n preventFocusOnDelete?: boolean;\n roomId: string;\n overrides?: Partial<Overrides>;\n allowMediaPreview?: boolean;\n}\n\nconst fileExtensionRegex = /^(.+?)(\\.[^.]+)?$/;\n\nfunction splitFileName(name: string) {\n const match = name.match(fileExtensionRegex);\n\n return { base: match?.[1] ?? name, extension: match?.[2] };\n}\n\nfunction getAttachmentIconGlyph(mimeType: string) {\n if (\n mimeType === \"application/zip\" ||\n mimeType === \"application/gzip\" ||\n mimeType === \"application/vnd.rar\" ||\n mimeType === \"application/x-rar-compressed\" ||\n mimeType === \"application/x-7z-compressed\" ||\n mimeType === \"application/x-zip-compressed\" ||\n mimeType === \"application/x-tar\" ||\n mimeType === \"application/x-bzip\" ||\n mimeType === \"application/x-bzip2\"\n ) {\n return (\n <path d=\"M13 15h2v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1H15v1h-1.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 0-1H15v-1h1.5a.5.5 0 0 0 .5-.5V15a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2Z\" />\n );\n }\n\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType.startsWith(\"font/\") ||\n mimeType.startsWith(\"application/\")\n ) {\n return (\n <path d=\"M10 16a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1h-8a.5.5 0 0 1-.5-.5Z\" />\n );\n }\n\n if (mimeType.startsWith(\"image/\")) {\n return (\n <path d=\"M12 16h6a1 1 0 0 1 1 1v3l-1.293-1.293a1 1 0 0 0-1.414 0L14.09 20.91l-.464-.386a1 1 0 0 0-1.265-.013l-1.231.985A.995.995 0 0 1 11 21v-4a1 1 0 0 1 1-1Zm-2 1a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-6a2 2 0 0 1-2-2v-4Zm3 2a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z\" />\n );\n }\n\n if (mimeType.startsWith(\"video/\")) {\n return (\n <path d=\"M12 15.71a1 1 0 0 1 1.49-.872l4.96 2.79a1 1 0 0 1 0 1.744l-4.96 2.79A1 1 0 0 1 12 21.29v-5.58Z\" />\n );\n }\n\n if (mimeType.startsWith(\"audio/\")) {\n return (\n <path d=\"M15 15a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 1 0v-7a.5.5 0 0 0-.5-.5Zm-2.5 2.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3Zm-2 1a.5.5 0 0 1 1 0v1a.5.5 0 0 1-1 0v-1Zm6-1a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0v-3ZM19 16a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 1 0v-5a.5.5 0 0 0-.5-.5Z\" />\n );\n }\n\n return null;\n}\n\nconst AttachmentFileIcon = memo(({ mimeType }: { mimeType: string }) => {\n const iconGlyph = useMemo(() => getAttachmentIconGlyph(mimeType), [mimeType]);\n\n return (\n <svg\n className=\"lb-attachment-icon\"\n width={30}\n height={30}\n viewBox=\"0 0 30 30\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-shadow\"\n />\n <path\n d=\"M6 5a2 2 0 0 1 2-2h5.843a4 4 0 0 1 2.829 1.172l6.156 6.156A4 4 0 0 1 24 13.157V25a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V5Z\"\n className=\"lb-attachment-icon-background\"\n />\n <path\n d=\"M14.382 3.037a4 4 0 0 1 2.29 1.135l6.156 6.157a4 4 0 0 1 1.136 2.289A2 2 0 0 0 22 11h-4a2 2 0 0 1-2-2V5a2 2 0 0 0-1.618-1.963Z\"\n className=\"lb-attachment-icon-fold\"\n />\n\n {iconGlyph && <g className=\"lb-attachment-icon-glyph\">{iconGlyph}</g>}\n </svg>\n );\n});\n\nfunction AttachmentImagePreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <img\n src={url}\n loading=\"lazy\"\n onLoad={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentVideoPreview({\n attachment,\n markPreviewAsUnsupported,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n markPreviewAsUnsupported: () => void;\n roomId: string;\n}) {\n const { url } = useRoomAttachmentUrl(attachment.id, roomId);\n const [isLoaded, setLoaded] = useState(false);\n\n const handleLoad = useCallback(() => {\n setLoaded(true);\n }, []);\n\n return (\n <>\n {!isLoaded ? <SpinnerIcon /> : null}\n {url ? (\n <div\n className=\"lb-attachment-preview-media\"\n data-hidden={!isLoaded ? \"\" : undefined}\n >\n <video\n src={url}\n onLoadedData={handleLoad}\n onError={markPreviewAsUnsupported}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction AttachmentPreview({\n attachment,\n allowMediaPreview = true,\n roomId,\n}: {\n attachment: CommentMixedAttachment;\n allowMediaPreview?: boolean;\n roomId: string;\n}) {\n const [isUnsupportedPreview, setUnsupportedPreview] = useState(false);\n const isUploaded =\n attachment.type === \"attachment\" || attachment.status === \"uploaded\";\n\n function markPreviewAsUnsupported() {\n setUnsupportedPreview(true);\n }\n\n if (\n !isUnsupportedPreview &&\n allowMediaPreview &&\n isUploaded &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n if (attachment.mimeType.startsWith(\"image/\")) {\n return (\n <AttachmentImagePreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n\n if (attachment.mimeType.startsWith(\"video/\")) {\n return (\n <AttachmentVideoPreview\n attachment={attachment}\n markPreviewAsUnsupported={markPreviewAsUnsupported}\n roomId={roomId}\n />\n );\n }\n }\n\n return <AttachmentFileIcon mimeType={attachment.mimeType} />;\n}\n\nfunction AttachmentName({\n attachment,\n}: {\n attachment: CommentMixedAttachment;\n}) {\n const { base: fileBaseName, extension: fileExtension } = useMemo(() => {\n return splitFileName(attachment.name);\n }, [attachment.name]);\n\n return (\n <span className=\"lb-attachment-name\" title={attachment.name}>\n <span className=\"lb-attachment-name-base\">{fileBaseName}</span>\n {fileExtension && (\n <span className=\"lb-attachment-name-extension\">{fileExtension}</span>\n )}\n </span>\n );\n}\n\nfunction useClickOnKeyDown(\n onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void\n) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Simulate a click event on Enter or Space because it's a div\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n\n const clickEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n event.target.dispatchEvent(clickEvent);\n }\n },\n [onKeyDown]\n );\n\n return handleKeyDown;\n}\n\nfunction useAttachmentContent(\n attachment: CommentMixedAttachment,\n overrides?: Partial<Overrides>\n) {\n const $ = useOverrides(overrides);\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n const isInComposer = Boolean(composerAttachmentsContext);\n const maxAttachmentSize = composerAttachmentsContext?.maxAttachmentSize;\n\n const status =\n attachment.type === \"localAttachment\" ? attachment.status : undefined;\n const isUploading = status === \"uploading\";\n const isError = status === \"error\";\n\n let description: string;\n\n if (attachment.type === \"localAttachment\" && attachment.status === \"error\") {\n if (attachment.error instanceof AttachmentTooLargeError) {\n if (attachment.error.origin === \"server\") {\n description = $.ATTACHMENT_TOO_LARGE();\n } else {\n description = $.ATTACHMENT_TOO_LARGE(\n maxAttachmentSize\n ? formatFileSize(maxAttachmentSize, $.locale)\n : undefined\n );\n }\n } else {\n description = $.ATTACHMENT_ERROR(attachment.error);\n }\n } else {\n description = formatFileSize(attachment.size, $.locale);\n }\n\n const deleteLabel = isInComposer\n ? $.COMPOSER_REMOVE_ATTACHMENT\n : $.COMMENT_DELETE_ATTACHMENT;\n\n return {\n isUploading,\n isError,\n description,\n deleteLabel,\n };\n}\n\nexport function MediaAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={cn(\"lb-attachment lb-media-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function FileAttachment({\n attachment,\n overrides,\n onClick,\n onDeleteClick,\n preventFocusOnDelete,\n allowMediaPreview = true,\n roomId,\n className,\n onKeyDown,\n ...props\n}: AttachmentProps) {\n const { isUploading, isError, description, deleteLabel } =\n useAttachmentContent(attachment, overrides);\n\n const handleDeletePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n if (preventFocusOnDelete) {\n event.preventDefault();\n }\n },\n [preventFocusOnDelete]\n );\n\n const handleKeyDown = useClickOnKeyDown(onKeyDown);\n\n return (\n <div\n className={cn(\"lb-attachment lb-file-attachment\", className)}\n data-error={isError ? \"\" : undefined}\n {...props}\n role={onClick ? \"button\" : undefined}\n onClick={onClick}\n tabIndex={onClick ? 0 : -1}\n onKeyDown={onClick ? handleKeyDown : undefined}\n >\n <div className=\"lb-attachment-preview\">\n {isUploading ? (\n <SpinnerIcon />\n ) : isError ? (\n <WarningIcon />\n ) : (\n <AttachmentPreview\n attachment={attachment}\n allowMediaPreview={allowMediaPreview}\n roomId={roomId}\n />\n )}\n </div>\n <div className=\"lb-attachment-details\">\n <AttachmentName attachment={attachment} />\n <span className=\"lb-attachment-description\" title={description}>\n {description}\n </span>\n </div>\n {onDeleteClick && (\n <Tooltip content={deleteLabel}>\n <button\n type=\"button\"\n className=\"lb-attachment-delete\"\n onClick={onDeleteClick}\n onPointerDown={handleDeletePointerDown}\n aria-label={deleteLabel}\n >\n <CrossIcon />\n </button>\n </Tooltip>\n )}\n </div>\n );\n}\n\nexport function separateMediaAttachments<T extends CommentMixedAttachment>(\n attachments: T[]\n) {\n const mediaAttachments: T[] = [];\n const fileAttachments: T[] = [];\n\n for (const attachment of attachments) {\n if (\n (attachment.mimeType.startsWith(\"image/\") ||\n attachment.mimeType.startsWith(\"video/\")) &&\n attachment.size <= MAX_DISPLAYED_MEDIA_SIZE\n ) {\n mediaAttachments.push(attachment);\n } else {\n fileAttachments.push(attachment);\n }\n }\n\n return {\n mediaAttachments,\n fileAttachments,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAuBA;AAWA;AAEA;AACE;AAEA;AACF;AAEA;AACE;AAWE;AACG;AAAO;AAAoP;AAIhQ;AAKE;AACG;AAAO;AAAiP;AAI7P;AACE;AACG;AAAO;AAAiQ;AAI7Q;AACE;AACG;AAAO;AAAiG;AAI7G;AACE;AACG;AAAO;AAAyP;AAIrQ;AACF;AAEA;AACE;AAEA;AACG;AACW;AACH;AACC;AACA;AACH;AACI;AACA;AACH;AAEN;AAAC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AACC;AACG;AACQ;AACZ;AAEe;AAAY;AAA4B;AAAU;AAAA;AAGvE;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACG;AACA;AACC;AACX;AAEA;AAAA;AAGV;AAEA;AAAgC;AAC9B;AACA;AAEF;AAKE;AACA;AAEA;AACE;AAAc;AAGhB;AACE;AACG;AAA8B;AAE5B;AACW;AACoB;AAE7B;AACM;AACS;AACL;AACX;AAEA;AAAA;AAGV;AAEA;AAA2B;AACzB;AACoB;AAEtB;AAKE;AACA;AAGA;AACE;AAA0B;AAG5B;AAME;AACE;AACG;AACC;AACA;AACA;AACF;AAIJ;AACE;AACG;AACC;AACA;AACA;AACF;AAEJ;AAGF;AAAQ;AAAwC;AAClD;AAEA;AAAwB;AAExB;AAGE;AACE;AAAoC;AAGtC;AACG;AAAe;AAAuC;AACrD;AAAC;AAAe;AAA2B;AAAa;AAErD;AAAe;AAAgC;AAAc;AAAA;AAItE;AAEA;AAGE;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAEA;AAA2C;AAChC;AACG;AACN;AAER;AAAqC;AACvC;AACF;AACU;AAGZ;AACF;AAEA;AAIE;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACE;AACE;AACE;AAAqC;AAErC;AAAgB;AAGV;AACN;AACF;AAEA;AAAiD;AACnD;AAEA;AAAsD;AAGxD;AAIA;AAAO;AACL;AACA;AACA;AACA;AAEJ;AAEO;AAAyB;AAC9B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AAC6D;AACjC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAAwB;AAC7B;AACA;AACA;AACA;AACA;AACoB;AACpB;AACA;AACA;AAEF;AACE;AAGA;AAAgC;AAE5B;AACE;AAAqB;AACvB;AACF;AACqB;AAGvB;AAEA;AACG;AAC4D;AAChC;AACvB;AACuB;AAC3B;AACwB;AACa;AAErC;AAAC;AAAc;AAMV;AACC;AACA;AACA;AACF;AAEJ;AACC;AAAc;AACb;AAAC;AAAe;AAAwB;AACvC;AAAe;AAAmC;AAChD;AACH;AAAA;AACF;AAEG;AAAiB;AACf;AACM;AACK;AACD;AACM;AACH;AAED;AACb;AACF;AAAA;AAIR;AAEO;AAGL;AACA;AAEA;AACE;AAKE;AAAgC;AAEhC;AAA+B;AACjC;AAGF;AAAO;AACL;AACA;AAEJ;;"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var
|
|
4
|
+
var cn = require('../../utils/cn.cjs');
|
|
5
5
|
|
|
6
6
|
function Attribution({ className, ...props }) {
|
|
7
7
|
return /* @__PURE__ */ jsxRuntime.jsx("a", {
|
|
8
8
|
href: "https://liveblocks.io",
|
|
9
9
|
target: "_blank",
|
|
10
10
|
rel: "noopener noreferrer",
|
|
11
|
-
className:
|
|
11
|
+
className: cn.cn("lb-composer-attribution", className),
|
|
12
12
|
...props,
|
|
13
13
|
children: /* @__PURE__ */ jsxRuntime.jsx("svg", {
|
|
14
14
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attribution.cjs","sources":["../../../src/components/internal/Attribution.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\n\nimport {
|
|
1
|
+
{"version":3,"file":"Attribution.cjs","sources":["../../../src/components/internal/Attribution.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\nexport function Attribution({ className, ...props }: ComponentProps<\"a\">) {\n return (\n <a\n href=\"https://liveblocks.io\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\"lb-composer-attribution\", className)}\n {...props}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 72\"\n aria-label=\"Made with Liveblocks\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M86 10h-8v50h8V10Zm16 14h-8v36h8V24Zm0-15h-8v9h8V9Zm13 15h-9l12 36h10l13-36h-9l-9 28-8-28Zm44-1c-11 0-18 8-18 19s7 19 18 19c8 0 15-4 17-13h-8c-1 4-5 6-9 6-6 0-9-3-9-10h27c0-10-6-21-18-21Zm0 7c5 0 9 3 9 8h-18c0-5 4-8 9-8Zm42-7c-4 0-8 2-11 6V10h-8v50h8v-5c3 4 7 6 11 6 11 0 16-9 16-19s-5-19-16-19Zm-2 32c-7 0-9-7-9-13s2-13 9-13 9 7 9 13-2 13-9 13Zm32-45h-8v50h8V10Zm24 51c11 0 18-8 18-19s-8-19-18-19c-11 0-19 8-19 19s7 19 19 19Zm0-6c-8 0-10-7-10-13s2-13 10-13c7 0 9 7 9 13s-2 13-9 13Zm39 6c8 0 15-4 17-12l-8-1c-2 4-4 6-9 6-7 0-9-6-9-12s2-12 9-12c5 0 8 3 8 7l9-1c-2-8-9-13-17-13-11 0-18 9-18 19 0 11 7 19 18 19Zm30-14 4-4 10 17h10l-15-23 15-13h-11l-13 13V10h-9v50h9V47Zm39 14c8 0 16-3 16-12 0-8-8-10-15-11-2-1-7-1-7-5 0-3 3-4 6-4 4 0 7 3 7 7l8-1c-1-9-8-12-15-12s-15 3-15 11 9 10 15 11c3 1 8 2 8 5 0 4-4 5-7 5-5 0-8-3-9-7l-8 1c1 8 9 12 16 12ZM41 27H0l12 12v17l29-29ZM20 60h40L48 48V32L20 60Z\"\n fill=\"currentColor\"\n />\n </svg>\n </a>\n );\n}\n"],"names":["jsx","cn"],"mappings":";;;;;AAIO,SAAS,WAAY,CAAA,EAAE,SAAc,EAAA,GAAA,KAAA,EAA8B,EAAA;AACxE,EAAA,uBACGA,cAAA,CAAA,GAAA,EAAA;AAAA,IACC,IAAK,EAAA,uBAAA;AAAA,IACL,MAAO,EAAA,QAAA;AAAA,IACP,GAAI,EAAA,qBAAA;AAAA,IACJ,SAAA,EAAWC,KAAG,CAAA,yBAAA,EAA2B,SAAS,CAAA;AAAA,IACjD,GAAG,KAAA;AAAA,IAEJ,QAAC,kBAAAD,cAAA,CAAA,KAAA,EAAA;AAAA,MACC,KAAM,EAAA,4BAAA;AAAA,MACN,OAAQ,EAAA,YAAA;AAAA,MACR,YAAW,EAAA,sBAAA;AAAA,MAEX,QAAC,kBAAAA,cAAA,CAAA,MAAA,EAAA;AAAA,QACC,QAAS,EAAA,SAAA;AAAA,QACT,QAAS,EAAA,SAAA;AAAA,QACT,CAAE,EAAA,y3BAAA;AAAA,QACF,IAAK,EAAA,cAAA;AAAA,OACP,CAAA;AAAA,KACF,CAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
3
|
|
|
4
4
|
function Attribution({ className, ...props }) {
|
|
5
5
|
return /* @__PURE__ */ jsx("a", {
|
|
6
6
|
href: "https://liveblocks.io",
|
|
7
7
|
target: "_blank",
|
|
8
8
|
rel: "noopener noreferrer",
|
|
9
|
-
className:
|
|
9
|
+
className: cn("lb-composer-attribution", className),
|
|
10
10
|
...props,
|
|
11
11
|
children: /* @__PURE__ */ jsx("svg", {
|
|
12
12
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attribution.js","sources":["../../../src/components/internal/Attribution.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\n\nimport {
|
|
1
|
+
{"version":3,"file":"Attribution.js","sources":["../../../src/components/internal/Attribution.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\nexport function Attribution({ className, ...props }: ComponentProps<\"a\">) {\n return (\n <a\n href=\"https://liveblocks.io\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\"lb-composer-attribution\", className)}\n {...props}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 72\"\n aria-label=\"Made with Liveblocks\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M86 10h-8v50h8V10Zm16 14h-8v36h8V24Zm0-15h-8v9h8V9Zm13 15h-9l12 36h10l13-36h-9l-9 28-8-28Zm44-1c-11 0-18 8-18 19s7 19 18 19c8 0 15-4 17-13h-8c-1 4-5 6-9 6-6 0-9-3-9-10h27c0-10-6-21-18-21Zm0 7c5 0 9 3 9 8h-18c0-5 4-8 9-8Zm42-7c-4 0-8 2-11 6V10h-8v50h8v-5c3 4 7 6 11 6 11 0 16-9 16-19s-5-19-16-19Zm-2 32c-7 0-9-7-9-13s2-13 9-13 9 7 9 13-2 13-9 13Zm32-45h-8v50h8V10Zm24 51c11 0 18-8 18-19s-8-19-18-19c-11 0-19 8-19 19s7 19 19 19Zm0-6c-8 0-10-7-10-13s2-13 10-13c7 0 9 7 9 13s-2 13-9 13Zm39 6c8 0 15-4 17-12l-8-1c-2 4-4 6-9 6-7 0-9-6-9-12s2-12 9-12c5 0 8 3 8 7l9-1c-2-8-9-13-17-13-11 0-18 9-18 19 0 11 7 19 18 19Zm30-14 4-4 10 17h10l-15-23 15-13h-11l-13 13V10h-9v50h9V47Zm39 14c8 0 16-3 16-12 0-8-8-10-15-11-2-1-7-1-7-5 0-3 3-4 6-4 4 0 7 3 7 7l8-1c-1-9-8-12-15-12s-15 3-15 11 9 10 15 11c3 1 8 2 8 5 0 4-4 5-7 5-5 0-8-3-9-7l-8 1c1 8 9 12 16 12ZM41 27H0l12 12v17l29-29ZM20 60h40L48 48V32L20 60Z\"\n fill=\"currentColor\"\n />\n </svg>\n </a>\n );\n}\n"],"names":[],"mappings":";;;AAIO,SAAS,WAAY,CAAA,EAAE,SAAc,EAAA,GAAA,KAAA,EAA8B,EAAA;AACxE,EAAA,uBACG,GAAA,CAAA,GAAA,EAAA;AAAA,IACC,IAAK,EAAA,uBAAA;AAAA,IACL,MAAO,EAAA,QAAA;AAAA,IACP,GAAI,EAAA,qBAAA;AAAA,IACJ,SAAA,EAAW,EAAG,CAAA,yBAAA,EAA2B,SAAS,CAAA;AAAA,IACjD,GAAG,KAAA;AAAA,IAEJ,QAAC,kBAAA,GAAA,CAAA,KAAA,EAAA;AAAA,MACC,KAAM,EAAA,4BAAA;AAAA,MACN,OAAQ,EAAA,YAAA;AAAA,MACR,YAAW,EAAA,sBAAA;AAAA,MAEX,QAAC,kBAAA,GAAA,CAAA,MAAA,EAAA;AAAA,QACC,QAAS,EAAA,SAAA;AAAA,QACT,QAAS,EAAA,SAAA;AAAA,QACT,CAAE,EAAA,y3BAAA;AAAA,QACF,IAAK,EAAA,cAAA;AAAA,OACP,CAAA;AAAA,KACF,CAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var react = require('@liveblocks/react');
|
|
6
6
|
var react$1 = require('react');
|
|
7
|
-
var
|
|
7
|
+
var cn = require('../../utils/cn.cjs');
|
|
8
8
|
var getInitials = require('../../utils/get-initials.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ function Avatar({ userId, className, ...props }) {
|
|
|
21
21
|
[isLoading, user, userId]
|
|
22
22
|
);
|
|
23
23
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
24
|
-
className:
|
|
24
|
+
className: cn.cn("lb-avatar", className),
|
|
25
25
|
"data-loading": isLoading ? "" : void 0,
|
|
26
26
|
...props,
|
|
27
27
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.cjs","sources":["../../../src/components/internal/Avatar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useUser } from \"@liveblocks/react\";\nimport type { ComponentProps } from \"react\";\nimport { useMemo } from \"react\";\n\nimport {
|
|
1
|
+
{"version":3,"file":"Avatar.cjs","sources":["../../../src/components/internal/Avatar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useUser } from \"@liveblocks/react\";\nimport type { ComponentProps } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/get-initials\";\n\nexport interface AvatarProps extends ComponentProps<\"div\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport function Avatar({ userId, className, ...props }: AvatarProps) {\n const { user, isLoading } = useUser(userId);\n const resolvedUserName = useMemo(() => user?.name, [user]);\n const resolvedUserAvatar = useMemo(() => user?.avatar, [user]);\n const resolvedUserInitials = useMemo(\n () => (resolvedUserName ? getInitials(resolvedUserName) : undefined),\n [resolvedUserName]\n );\n const resolvedUserIdInitials = useMemo(\n () => (!isLoading && !user ? getInitials(userId) : undefined),\n [isLoading, user, userId]\n );\n\n return (\n <div\n className={cn(\"lb-avatar\", className)}\n data-loading={isLoading ? \"\" : undefined}\n {...props}\n >\n {resolvedUserAvatar && (\n <img\n className=\"lb-avatar-image\"\n src={resolvedUserAvatar}\n alt={resolvedUserName}\n />\n )}\n {resolvedUserInitials ? (\n <span className=\"lb-avatar-fallback\" aria-hidden>\n {resolvedUserInitials}\n </span>\n ) : resolvedUserIdInitials ? (\n <span className=\"lb-avatar-fallback\" aria-label={userId} title={userId}>\n {resolvedUserIdInitials}\n </span>\n ) : null}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAgBO;AACL;AACA;AACA;AACA;AAA6B;AAC+B;AACzC;AAEnB;AAA+B;AACsB;AAC3B;AAG1B;AACG;AACqC;AACL;AAC3B;AAEH;AACE;AACW;AACL;AACA;AACP;AAGC;AAAe;AAAgC;AAC7C;AAGF;AAAe;AAAiC;AAAe;AAC7D;AAED;AAAA;AAGV;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useUser } from '@liveblocks/react';
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { cn } from '../../utils/cn.js';
|
|
6
6
|
import { getInitials } from '../../utils/get-initials.js';
|
|
7
7
|
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ function Avatar({ userId, className, ...props }) {
|
|
|
19
19
|
[isLoading, user, userId]
|
|
20
20
|
);
|
|
21
21
|
return /* @__PURE__ */ jsxs("div", {
|
|
22
|
-
className:
|
|
22
|
+
className: cn("lb-avatar", className),
|
|
23
23
|
"data-loading": isLoading ? "" : void 0,
|
|
24
24
|
...props,
|
|
25
25
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.js","sources":["../../../src/components/internal/Avatar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useUser } from \"@liveblocks/react\";\nimport type { ComponentProps } from \"react\";\nimport { useMemo } from \"react\";\n\nimport {
|
|
1
|
+
{"version":3,"file":"Avatar.js","sources":["../../../src/components/internal/Avatar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useUser } from \"@liveblocks/react\";\nimport type { ComponentProps } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/get-initials\";\n\nexport interface AvatarProps extends ComponentProps<\"div\"> {\n /**\n * The user ID to display the avatar for.\n */\n userId: string;\n}\n\nexport function Avatar({ userId, className, ...props }: AvatarProps) {\n const { user, isLoading } = useUser(userId);\n const resolvedUserName = useMemo(() => user?.name, [user]);\n const resolvedUserAvatar = useMemo(() => user?.avatar, [user]);\n const resolvedUserInitials = useMemo(\n () => (resolvedUserName ? getInitials(resolvedUserName) : undefined),\n [resolvedUserName]\n );\n const resolvedUserIdInitials = useMemo(\n () => (!isLoading && !user ? getInitials(userId) : undefined),\n [isLoading, user, userId]\n );\n\n return (\n <div\n className={cn(\"lb-avatar\", className)}\n data-loading={isLoading ? \"\" : undefined}\n {...props}\n >\n {resolvedUserAvatar && (\n <img\n className=\"lb-avatar-image\"\n src={resolvedUserAvatar}\n alt={resolvedUserName}\n />\n )}\n {resolvedUserInitials ? (\n <span className=\"lb-avatar-fallback\" aria-hidden>\n {resolvedUserInitials}\n </span>\n ) : resolvedUserIdInitials ? (\n <span className=\"lb-avatar-fallback\" aria-label={userId} title={userId}>\n {resolvedUserIdInitials}\n </span>\n ) : null}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAgBO;AACL;AACA;AACA;AACA;AAA6B;AAC+B;AACzC;AAEnB;AAA+B;AACsB;AAC3B;AAG1B;AACG;AACqC;AACL;AAC3B;AAEH;AACE;AACW;AACL;AACA;AACP;AAGC;AAAe;AAAgC;AAC7C;AAGF;AAAe;AAAiC;AAAe;AAC7D;AAED;AAAA;AAGV;;"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var react = require('react');
|
|
6
6
|
var ChevronDown = require('../../icons/ChevronDown.cjs');
|
|
7
|
-
var
|
|
7
|
+
var cn = require('../../utils/cn.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
const CustomButton = react.forwardRef(
|
|
@@ -18,7 +18,7 @@ const CustomButton = react.forwardRef(
|
|
|
18
18
|
}, forwardedRef) => {
|
|
19
19
|
return /* @__PURE__ */ jsxRuntime.jsx("button", {
|
|
20
20
|
type: "button",
|
|
21
|
-
className:
|
|
21
|
+
className: cn.cn(
|
|
22
22
|
"lb-button",
|
|
23
23
|
!disableable && "lb-button:non-disableable",
|
|
24
24
|
className
|
|
@@ -54,7 +54,7 @@ const SelectButton = react.forwardRef(
|
|
|
54
54
|
return /* @__PURE__ */ jsxRuntime.jsxs(CustomButton, {
|
|
55
55
|
...props,
|
|
56
56
|
type: "button",
|
|
57
|
-
className:
|
|
57
|
+
className: cn.cn("lb-select-button", className),
|
|
58
58
|
ref: forwardedRef,
|
|
59
59
|
children: [
|
|
60
60
|
icon ? /* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.cjs","sources":["../../../src/components/internal/Button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { ChevronDownIcon } from \"../../icons/ChevronDown\";\nimport {
|
|
1
|
+
{"version":3,"file":"Button.cjs","sources":["../../../src/components/internal/Button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { ChevronDownIcon } from \"../../icons/ChevronDown\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface ButtonProps extends ComponentProps<\"button\"> {\n variant?:\n | \"default\"\n | \"toolbar\"\n | \"outline\"\n | \"primary\"\n | \"secondary\"\n | \"destructive\";\n size?: \"default\" | \"large\";\n disableable?: boolean;\n icon?: ReactNode;\n}\n\nexport const CustomButton = forwardRef<\n HTMLButtonElement,\n Omit<ButtonProps, \"icon\">\n>(\n (\n {\n variant = \"default\",\n size = \"default\",\n disableable = true,\n className,\n children,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n type=\"button\"\n className={cn(\n \"lb-button\",\n !disableable && \"lb-button:non-disableable\",\n className\n )}\n data-variant={variant}\n data-size={size}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </button>\n );\n }\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ icon, children, ...props }, forwardedRef) => {\n return (\n <CustomButton {...props} ref={forwardedRef}>\n {icon ? <span className=\"lb-icon-container\">{icon}</span> : null}\n {children ? <span className=\"lb-button-label\">{children}</span> : null}\n </CustomButton>\n );\n }\n);\n\nexport const SelectButton = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ icon, children, className, ...props }, forwardedRef) => {\n return (\n <CustomButton\n {...props}\n type=\"button\"\n className={cn(\"lb-select-button\", className)}\n ref={forwardedRef}\n >\n {icon ? <span className=\"lb-icon-container\">{icon}</span> : null}\n {children ? <span className=\"lb-button-label\">{children}</span> : null}\n <span className=\"lb-select-button-chevron\">\n <ChevronDownIcon />\n </span>\n </CustomButton>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;AAqBO;AAAqB;AAKxB;AACY;AACH;AACO;AACd;AACA;AACG;AAIL;AACG;AACM;AACM;AACT;AACgB;AAChB;AACF;AACc;AACH;AACP;AACC;AAEJ;AACH;AAGN;AAEO;AAAe;AAElB;AACG;AAAiB;AAAY;AAC3B;AAAQ;AAAe;AAAqB;AAAe;AAC/C;AAAe;AAAmB;AAAmB;AAAA;AACpE;AAGN;AAEO;AAAqB;AAExB;AACG;AACK;AACC;AACsC;AACtC;AAEJ;AAAQ;AAAe;AAAqB;AAAe;AAC/C;AAAe;AAAmB;AAAmB;AACjE;AAAe;AACG;AACnB;AAAA;AACF;AAGN;;;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { ChevronDownIcon } from '../../icons/ChevronDown.js';
|
|
5
|
-
import {
|
|
5
|
+
import { cn } from '../../utils/cn.js';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
const CustomButton = forwardRef(
|
|
@@ -16,7 +16,7 @@ const CustomButton = forwardRef(
|
|
|
16
16
|
}, forwardedRef) => {
|
|
17
17
|
return /* @__PURE__ */ jsx("button", {
|
|
18
18
|
type: "button",
|
|
19
|
-
className:
|
|
19
|
+
className: cn(
|
|
20
20
|
"lb-button",
|
|
21
21
|
!disableable && "lb-button:non-disableable",
|
|
22
22
|
className
|
|
@@ -52,7 +52,7 @@ const SelectButton = forwardRef(
|
|
|
52
52
|
return /* @__PURE__ */ jsxs(CustomButton, {
|
|
53
53
|
...props,
|
|
54
54
|
type: "button",
|
|
55
|
-
className:
|
|
55
|
+
className: cn("lb-select-button", className),
|
|
56
56
|
ref: forwardedRef,
|
|
57
57
|
children: [
|
|
58
58
|
icon ? /* @__PURE__ */ jsx("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../src/components/internal/Button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { ChevronDownIcon } from \"../../icons/ChevronDown\";\nimport {
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../../../src/components/internal/Button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { ChevronDownIcon } from \"../../icons/ChevronDown\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface ButtonProps extends ComponentProps<\"button\"> {\n variant?:\n | \"default\"\n | \"toolbar\"\n | \"outline\"\n | \"primary\"\n | \"secondary\"\n | \"destructive\";\n size?: \"default\" | \"large\";\n disableable?: boolean;\n icon?: ReactNode;\n}\n\nexport const CustomButton = forwardRef<\n HTMLButtonElement,\n Omit<ButtonProps, \"icon\">\n>(\n (\n {\n variant = \"default\",\n size = \"default\",\n disableable = true,\n className,\n children,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n type=\"button\"\n className={cn(\n \"lb-button\",\n !disableable && \"lb-button:non-disableable\",\n className\n )}\n data-variant={variant}\n data-size={size}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </button>\n );\n }\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ icon, children, ...props }, forwardedRef) => {\n return (\n <CustomButton {...props} ref={forwardedRef}>\n {icon ? <span className=\"lb-icon-container\">{icon}</span> : null}\n {children ? <span className=\"lb-button-label\">{children}</span> : null}\n </CustomButton>\n );\n }\n);\n\nexport const SelectButton = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ icon, children, className, ...props }, forwardedRef) => {\n return (\n <CustomButton\n {...props}\n type=\"button\"\n className={cn(\"lb-select-button\", className)}\n ref={forwardedRef}\n >\n {icon ? <span className=\"lb-icon-container\">{icon}</span> : null}\n {children ? <span className=\"lb-button-label\">{children}</span> : null}\n <span className=\"lb-select-button-chevron\">\n <ChevronDownIcon />\n </span>\n </CustomButton>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;AAqBO;AAAqB;AAKxB;AACY;AACH;AACO;AACd;AACA;AACG;AAIL;AACG;AACM;AACM;AACT;AACgB;AAChB;AACF;AACc;AACH;AACP;AACC;AAEJ;AACH;AAGN;AAEO;AAAe;AAElB;AACG;AAAiB;AAAY;AAC3B;AAAQ;AAAe;AAAqB;AAAe;AAC/C;AAAe;AAAmB;AAAmB;AAAA;AACpE;AAGN;AAEO;AAAqB;AAExB;AACG;AACK;AACC;AACsC;AACtC;AAEJ;AAAQ;AAAe;AAAqB;AAAe;AAC/C;AAAe;AAAmB;AAAmB;AACjE;AAAe;AACG;AACnB;AAAA;AACF;AAGN;;"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
require('../../icons/index.cjs');
|
|
6
|
+
var overrides = require('../../overrides.cjs');
|
|
6
7
|
var Button = require('./Button.cjs');
|
|
7
8
|
var Tooltip = require('./Tooltip.cjs');
|
|
8
9
|
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
@@ -10,7 +11,8 @@ var Check = require('../../icons/Check.cjs');
|
|
|
10
11
|
var Copy = require('../../icons/Copy.cjs');
|
|
11
12
|
|
|
12
13
|
const COPY_DELAY = 1500;
|
|
13
|
-
function CodeBlock({ title, code }) {
|
|
14
|
+
function CodeBlock({ title, code, overrides: overrides$1 }) {
|
|
15
|
+
const $ = overrides.useOverrides(overrides$1);
|
|
14
16
|
const [isCopied, setCopied] = react.useState(false);
|
|
15
17
|
const timeoutRef = react.useRef(null);
|
|
16
18
|
react.useEffect(() => {
|
|
@@ -47,11 +49,12 @@ function CodeBlock({ title, code }) {
|
|
|
47
49
|
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
48
50
|
className: "lb-code-block-header-actions",
|
|
49
51
|
children: /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, {
|
|
50
|
-
content: isCopied ? null :
|
|
52
|
+
content: isCopied ? null : $.COPY_TO_CLIPBOARD,
|
|
51
53
|
children: /* @__PURE__ */ jsxRuntime.jsx(Button.Button, {
|
|
52
54
|
className: "lb-code-block-header-action",
|
|
53
55
|
icon: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(Check.CheckIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(Copy.CopyIcon, {}),
|
|
54
|
-
onClick: handleCopy
|
|
56
|
+
onClick: handleCopy,
|
|
57
|
+
"aria-label": $.COPY_TO_CLIPBOARD
|
|
55
58
|
})
|
|
56
59
|
})
|
|
57
60
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.cjs","sources":["../../../src/components/internal/CodeBlock.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { CheckIcon, CopyIcon } from \"../../icons\";\nimport { Button } from \"./Button\";\nimport { Tooltip, TooltipProvider } from \"./Tooltip\";\n\nconst COPY_DELAY = 1500;\n\ninterface CodeBlockProps extends Omit<ComponentProps<\"div\">, \"title\"> {\n title: string;\n code: string;\n}\n\nexport function CodeBlock({ title, code }: CodeBlockProps) {\n const [isCopied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (isCopied) {\n timeoutRef.current = setTimeout(() => {\n setCopied(false);\n }, COPY_DELAY);\n }\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [isCopied]);\n\n const handleCopy = useCallback(() => {\n try {\n navigator.clipboard.writeText(code);\n setCopied(true);\n } catch (error) {\n console.error(error);\n }\n }, [code]);\n\n return (\n <TooltipProvider>\n <div className=\"lb-code-block\">\n <div className=\"lb-code-block-header\">\n <span className=\"lb-code-block-title\">{title}</span>\n <div className=\"lb-code-block-header-actions\">\n <Tooltip content={isCopied ? null :
|
|
1
|
+
{"version":3,"file":"CodeBlock.cjs","sources":["../../../src/components/internal/CodeBlock.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { CheckIcon, CopyIcon } from \"../../icons\";\nimport type { GlobalOverrides } from \"../../overrides\";\nimport { useOverrides } from \"../../overrides\";\nimport { Button } from \"./Button\";\nimport { Tooltip, TooltipProvider } from \"./Tooltip\";\n\nconst COPY_DELAY = 1500;\n\ninterface CodeBlockProps extends Omit<ComponentProps<\"div\">, \"title\"> {\n title: string;\n code: string;\n overrides?: Partial<GlobalOverrides>;\n}\n\nexport function CodeBlock({ title, code, overrides }: CodeBlockProps) {\n const $ = useOverrides(overrides);\n const [isCopied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (isCopied) {\n timeoutRef.current = setTimeout(() => {\n setCopied(false);\n }, COPY_DELAY);\n }\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [isCopied]);\n\n const handleCopy = useCallback(() => {\n try {\n navigator.clipboard.writeText(code);\n setCopied(true);\n } catch (error) {\n console.error(error);\n }\n }, [code]);\n\n return (\n <TooltipProvider>\n <div className=\"lb-code-block\">\n <div className=\"lb-code-block-header\">\n <span className=\"lb-code-block-title\">{title}</span>\n <div className=\"lb-code-block-header-actions\">\n <Tooltip content={isCopied ? null : $.COPY_TO_CLIPBOARD}>\n <Button\n className=\"lb-code-block-header-action\"\n icon={isCopied ? <CheckIcon /> : <CopyIcon />}\n onClick={handleCopy}\n aria-label={$.COPY_TO_CLIPBOARD}\n />\n </Tooltip>\n </div>\n </div>\n <pre className=\"lb-code-block-content\">\n <code>{code}</code>\n </pre>\n </div>\n </TooltipProvider>\n );\n}\n"],"names":["overrides","useOverrides","useState","useRef","useEffect","useCallback","jsx","TooltipProvider","jsxs","Tooltip","Button","CheckIcon","CopyIcon"],"mappings":";;;;;;;;;;;;AASA,MAAM,UAAa,GAAA,IAAA,CAAA;AAQZ,SAAS,SAAU,CAAA,EAAE,KAAO,EAAA,IAAA,aAAMA,aAA6B,EAAA;AACpE,EAAM,MAAA,CAAA,GAAIC,uBAAaD,WAAS,CAAA,CAAA;AAChC,EAAA,MAAM,CAAC,QAAA,EAAU,SAAS,CAAA,GAAIE,eAAS,KAAK,CAAA,CAAA;AAC5C,EAAM,MAAA,UAAA,GAAaC,aAA6C,IAAI,CAAA,CAAA;AAEpE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,QAAU,EAAA;AACZ,MAAW,UAAA,CAAA,OAAA,GAAU,WAAW,MAAM;AACpC,QAAA,SAAA,CAAU,KAAK,CAAA,CAAA;AAAA,SACd,UAAU,CAAA,CAAA;AAAA,KACf;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,WAAW,OAAS,EAAA;AACtB,QAAA,YAAA,CAAa,WAAW,OAAO,CAAA,CAAA;AAAA,OACjC;AAAA,KACF,CAAA;AAAA,GACF,EAAG,CAAC,QAAQ,CAAC,CAAA,CAAA;AAEb,EAAM,MAAA,UAAA,GAAaC,kBAAY,MAAM;AACnC,IAAI,IAAA;AACF,MAAU,SAAA,CAAA,SAAA,CAAU,UAAU,IAAI,CAAA,CAAA;AAClC,MAAA,SAAA,CAAU,IAAI,CAAA,CAAA;AAAA,aACP,KAAP,EAAA;AACA,MAAA,OAAA,CAAQ,MAAM,KAAK,CAAA,CAAA;AAAA,KACrB;AAAA,GACF,EAAG,CAAC,IAAI,CAAC,CAAA,CAAA;AAET,EAAA,uBACGC,cAAA,CAAAC,gCAAA,EAAA;AAAA,IACC,QAAC,kBAAAC,eAAA,CAAA,KAAA,EAAA;AAAA,MAAI,SAAU,EAAA,eAAA;AAAA,MACb,QAAA,EAAA;AAAA,wBAACA,eAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,sBAAA;AAAA,UACb,QAAA,EAAA;AAAA,4BAACF,cAAA,CAAA,MAAA,EAAA;AAAA,cAAK,SAAU,EAAA,qBAAA;AAAA,cAAuB,QAAA,EAAA,KAAA;AAAA,aAAM,CAAA;AAAA,4BAC5CA,cAAA,CAAA,KAAA,EAAA;AAAA,cAAI,SAAU,EAAA,8BAAA;AAAA,cACb,QAAC,kBAAAA,cAAA,CAAAG,eAAA,EAAA;AAAA,gBAAQ,OAAA,EAAS,QAAW,GAAA,IAAA,GAAO,CAAE,CAAA,iBAAA;AAAA,gBACpC,QAAC,kBAAAH,cAAA,CAAAI,aAAA,EAAA;AAAA,kBACC,SAAU,EAAA,6BAAA;AAAA,kBACV,MAAM,QAAW,mBAAAJ,cAAA,CAACK,eAAU,EAAA,EAAA,CAAA,kCAAMC,aAAS,EAAA,EAAA,CAAA;AAAA,kBAC3C,OAAS,EAAA,UAAA;AAAA,kBACT,cAAY,CAAE,CAAA,iBAAA;AAAA,iBAChB,CAAA;AAAA,eACF,CAAA;AAAA,aACF,CAAA;AAAA,WAAA;AAAA,SACF,CAAA;AAAA,wBACCN,cAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,uBAAA;AAAA,UACb,QAAC,kBAAAA,cAAA,CAAA,MAAA,EAAA;AAAA,YAAM,QAAA,EAAA,IAAA;AAAA,WAAK,CAAA;AAAA,SACd,CAAA;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import '../../icons/index.js';
|
|
4
|
+
import { useOverrides } from '../../overrides.js';
|
|
4
5
|
import { Button } from './Button.js';
|
|
5
6
|
import { Tooltip } from './Tooltip.js';
|
|
6
7
|
import { TooltipProvider } from '@radix-ui/react-tooltip';
|
|
@@ -8,7 +9,8 @@ import { CheckIcon } from '../../icons/Check.js';
|
|
|
8
9
|
import { CopyIcon } from '../../icons/Copy.js';
|
|
9
10
|
|
|
10
11
|
const COPY_DELAY = 1500;
|
|
11
|
-
function CodeBlock({ title, code }) {
|
|
12
|
+
function CodeBlock({ title, code, overrides }) {
|
|
13
|
+
const $ = useOverrides(overrides);
|
|
12
14
|
const [isCopied, setCopied] = useState(false);
|
|
13
15
|
const timeoutRef = useRef(null);
|
|
14
16
|
useEffect(() => {
|
|
@@ -45,11 +47,12 @@ function CodeBlock({ title, code }) {
|
|
|
45
47
|
/* @__PURE__ */ jsx("div", {
|
|
46
48
|
className: "lb-code-block-header-actions",
|
|
47
49
|
children: /* @__PURE__ */ jsx(Tooltip, {
|
|
48
|
-
content: isCopied ? null :
|
|
50
|
+
content: isCopied ? null : $.COPY_TO_CLIPBOARD,
|
|
49
51
|
children: /* @__PURE__ */ jsx(Button, {
|
|
50
52
|
className: "lb-code-block-header-action",
|
|
51
53
|
icon: isCopied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}),
|
|
52
|
-
onClick: handleCopy
|
|
54
|
+
onClick: handleCopy,
|
|
55
|
+
"aria-label": $.COPY_TO_CLIPBOARD
|
|
53
56
|
})
|
|
54
57
|
})
|
|
55
58
|
})
|