stream-chat-react 14.10.0 → 14.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +20 -1
  2. package/dist/cjs/{useNotificationApi.bf76a16b.js → NotificationList.6abbee1c.js} +4935 -1232
  3. package/dist/cjs/NotificationList.6abbee1c.js.map +1 -0
  4. package/dist/cjs/{ReactPlayerWrapper.60a93742.js → ReactPlayerWrapper.a94a0cd1.js} +3 -3
  5. package/dist/cjs/{ReactPlayerWrapper.60a93742.js.map → ReactPlayerWrapper.a94a0cd1.js.map} +1 -1
  6. package/dist/cjs/channel-detail.js +263 -200
  7. package/dist/cjs/channel-detail.js.map +1 -1
  8. package/dist/cjs/emojis.js +31 -15
  9. package/dist/cjs/emojis.js.map +1 -1
  10. package/dist/cjs/index.js +2105 -7646
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/useChannelHeaderOnlineStatus.7e17671e.js +961 -0
  13. package/dist/cjs/useChannelHeaderOnlineStatus.7e17671e.js.map +1 -0
  14. package/dist/cjs/useInertWhenHidden.22151beb.js +6426 -0
  15. package/dist/cjs/useInertWhenHidden.22151beb.js.map +1 -0
  16. package/dist/es/{useNotificationApi.ed6bff88.mjs → NotificationList.db01a56d.mjs} +4556 -1258
  17. package/dist/es/NotificationList.db01a56d.mjs.map +1 -0
  18. package/dist/es/channel-detail.mjs +86 -23
  19. package/dist/es/channel-detail.mjs.map +1 -1
  20. package/dist/es/emojis.mjs +25 -8
  21. package/dist/es/emojis.mjs.map +1 -1
  22. package/dist/es/index.mjs +1045 -6586
  23. package/dist/es/index.mjs.map +1 -1
  24. package/dist/es/useChannelHeaderOnlineStatus.a9357343.mjs +771 -0
  25. package/dist/es/useChannelHeaderOnlineStatus.a9357343.mjs.map +1 -0
  26. package/dist/es/useInertWhenHidden.3d5fbdba.mjs +5765 -0
  27. package/dist/es/useInertWhenHidden.3d5fbdba.mjs.map +1 -0
  28. package/dist/types/components/Attachment/Geolocation.d.ts.map +1 -1
  29. package/dist/types/components/Attachment/LinkPreview/Card.d.ts.map +1 -1
  30. package/dist/types/components/Attachment/LinkPreview/CardAudio.d.ts.map +1 -1
  31. package/dist/types/components/Attachment/ModalGallery.d.ts.map +1 -1
  32. package/dist/types/components/Attachment/UnsupportedAttachment.d.ts.map +1 -1
  33. package/dist/types/components/Attachment/VisibilityDisclaimer.d.ts.map +1 -1
  34. package/dist/types/components/Attachment/components/DownloadButton.d.ts.map +1 -1
  35. package/dist/types/components/Avatar/Avatar.d.ts +5 -1
  36. package/dist/types/components/Avatar/Avatar.d.ts.map +1 -1
  37. package/dist/types/components/Badge/Badge.d.ts.map +1 -1
  38. package/dist/types/components/Badge/MediaBadge.d.ts.map +1 -1
  39. package/dist/types/components/BaseImage/ImagePlaceholder.d.ts.map +1 -1
  40. package/dist/types/components/Button/PlayButton.d.ts.map +1 -1
  41. package/dist/types/components/Channel/Channel.d.ts.map +1 -1
  42. package/dist/types/components/ChannelListItem/ChannelListItem.d.ts.map +1 -1
  43. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.defaults.d.ts.map +1 -1
  44. package/dist/types/components/ChannelListItem/ChannelListItemUI.d.ts.map +1 -1
  45. package/dist/types/components/ChannelListItem/hooks/useMessageDeliveryStatus.d.ts.map +1 -1
  46. package/dist/types/components/ChatView/ChatView.d.ts.map +1 -1
  47. package/dist/types/components/Dialog/components/Callout.d.ts.map +1 -1
  48. package/dist/types/components/Dialog/components/ContextMenu.d.ts +4 -0
  49. package/dist/types/components/Dialog/components/ContextMenu.d.ts.map +1 -1
  50. package/dist/types/components/Dialog/components/Prompt.d.ts.map +1 -1
  51. package/dist/types/components/Dialog/components/Viewer.d.ts.map +1 -1
  52. package/dist/types/components/EmptyStateIndicator/EmptyStateIndicator.d.ts.map +1 -1
  53. package/dist/types/components/Form/NumericInput.d.ts.map +1 -1
  54. package/dist/types/components/Form/TextInput.d.ts.map +1 -1
  55. package/dist/types/components/Gallery/GalleryHeader.d.ts.map +1 -1
  56. package/dist/types/components/Gallery/GalleryUI.d.ts.map +1 -1
  57. package/dist/types/components/Icons/index.d.ts +1 -0
  58. package/dist/types/components/Icons/index.d.ts.map +1 -1
  59. package/dist/types/components/Icons/slots.d.ts +10 -0
  60. package/dist/types/components/Icons/slots.d.ts.map +1 -0
  61. package/dist/types/components/Loading/LoadingIndicator.d.ts +2 -2
  62. package/dist/types/components/Loading/LoadingIndicator.d.ts.map +1 -1
  63. package/dist/types/components/Location/ShareLocationDialog.d.ts.map +1 -1
  64. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.d.ts.map +1 -1
  65. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.d.ts.map +1 -1
  66. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingPlayback.d.ts.map +1 -1
  67. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
  68. package/dist/types/components/Message/MessageAlsoSentInChannelIndicator.d.ts.map +1 -1
  69. package/dist/types/components/Message/MessageDeletedBubble.d.ts.map +1 -1
  70. package/dist/types/components/Message/MessageStatus.d.ts.map +1 -1
  71. package/dist/types/components/Message/MessageTranslationIndicator.d.ts.map +1 -1
  72. package/dist/types/components/Message/PinIndicator.d.ts.map +1 -1
  73. package/dist/types/components/Message/ReminderNotification.d.ts.map +1 -1
  74. package/dist/types/components/MessageActions/DownloadSubmenu.d.ts.map +1 -1
  75. package/dist/types/components/MessageActions/MessageActions.defaults.d.ts.map +1 -1
  76. package/dist/types/components/MessageActions/RemindMeSubmenu.d.ts.map +1 -1
  77. package/dist/types/components/MessageBounce/MessageBouncePrompt.d.ts.map +1 -1
  78. package/dist/types/components/MessageComposer/AttachmentPreviewList/AudioAttachmentPreview.d.ts.map +1 -1
  79. package/dist/types/components/MessageComposer/AttachmentPreviewList/FileAttachmentPreview.d.ts.map +1 -1
  80. package/dist/types/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.d.ts.map +1 -1
  81. package/dist/types/components/MessageComposer/AttachmentPreviewList/MediaAttachmentPreview.d.ts.map +1 -1
  82. package/dist/types/components/MessageComposer/AttachmentPreviewList/UnsupportedAttachmentPreview.d.ts.map +1 -1
  83. package/dist/types/components/MessageComposer/AttachmentSelector/AttachmentSelector.d.ts.map +1 -1
  84. package/dist/types/components/MessageComposer/AttachmentSelector/CommandsMenu.d.ts.map +1 -1
  85. package/dist/types/components/MessageComposer/CommandChip.d.ts.map +1 -1
  86. package/dist/types/components/MessageComposer/LinkPreviewList.d.ts.map +1 -1
  87. package/dist/types/components/MessageComposer/MessageComposer.d.ts +10 -1
  88. package/dist/types/components/MessageComposer/MessageComposer.d.ts.map +1 -1
  89. package/dist/types/components/MessageComposer/MessageComposerActions.d.ts.map +1 -1
  90. package/dist/types/components/MessageComposer/QuotedMessagePreview.d.ts.map +1 -1
  91. package/dist/types/components/MessageComposer/RemoveAttachmentPreviewButton.d.ts.map +1 -1
  92. package/dist/types/components/MessageComposer/SendButton.d.ts.map +1 -1
  93. package/dist/types/components/MessageComposer/SendToChannelCheckbox.d.ts.map +1 -1
  94. package/dist/types/components/MessageComposer/WithDragAndDropUpload.d.ts.map +1 -1
  95. package/dist/types/components/MessageComposer/hooks/useMessageComposerCommands.d.ts.map +1 -1
  96. package/dist/types/components/MessageComposer/hooks/useMessageComposerController.d.ts.map +1 -1
  97. package/dist/types/components/MessageList/ScrollToLatestMessageButton.d.ts.map +1 -1
  98. package/dist/types/components/MessageList/UnreadMessagesNotification.d.ts.map +1 -1
  99. package/dist/types/components/MessageList/UnreadMessagesSeparator.d.ts.map +1 -1
  100. package/dist/types/components/MessageList/hooks/useMarkRead.d.ts.map +1 -1
  101. package/dist/types/components/Modal/CloseButtonOnModalOverlay.d.ts.map +1 -1
  102. package/dist/types/components/Notifications/Notification.d.ts.map +1 -1
  103. package/dist/types/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.d.ts.map +1 -1
  104. package/dist/types/components/Poll/PollCreationDialog/OptionFieldSet.d.ts.map +1 -1
  105. package/dist/types/components/Poll/PollCreationDialog/PollCreationDialogControls.d.ts.map +1 -1
  106. package/dist/types/components/Poll/PollCreationDialog/PollOptionReorderHandle.d.ts.map +1 -1
  107. package/dist/types/components/Reactions/MessageReactionsDetail.d.ts.map +1 -1
  108. package/dist/types/components/Reactions/ReactionSelector.d.ts.map +1 -1
  109. package/dist/types/components/Reactions/ReactionSelectorWithButton.d.ts +6 -1
  110. package/dist/types/components/Reactions/ReactionSelectorWithButton.d.ts.map +1 -1
  111. package/dist/types/components/SummarizedMessagePreview/SummarizedMessagePreview.d.ts.map +1 -1
  112. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/BroadcastMentionItem.d.ts.map +1 -1
  113. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/RoleItem.d.ts.map +1 -1
  114. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.d.ts.map +1 -1
  115. package/dist/types/components/Thread/ThreadHeader.d.ts.map +1 -1
  116. package/dist/types/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts.map +1 -1
  117. package/dist/types/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts.map +1 -1
  118. package/dist/types/components/VideoPlayer/VideoThumbnail.d.ts.map +1 -1
  119. package/dist/types/context/ComponentContext.d.ts +3 -0
  120. package/dist/types/context/ComponentContext.d.ts.map +1 -1
  121. package/dist/types/context/WithComponents.d.ts.map +1 -1
  122. package/dist/types/context/index.d.ts +1 -0
  123. package/dist/types/context/index.d.ts.map +1 -1
  124. package/dist/types/context/useComponentContextIcons.d.ts +16 -0
  125. package/dist/types/context/useComponentContextIcons.d.ts.map +1 -0
  126. package/dist/types/plugins/ChannelDetail/ChannelDetail.d.ts.map +1 -1
  127. package/dist/types/plugins/ChannelDetail/ChannelDetailEmptyList.d.ts.map +1 -1
  128. package/dist/types/plugins/ChannelDetail/ChannelDetailSearchInput.d.ts.map +1 -1
  129. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigatorHeader.d.ts.map +1 -1
  130. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesEmptyList.d.ts.map +1 -1
  131. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementActions.defaults.d.ts.map +1 -1
  132. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.d.ts.map +1 -1
  133. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaEmptyList.d.ts.map +1 -1
  134. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.d.ts.map +1 -1
  135. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberActions.defaults.d.ts.map +1 -1
  136. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.d.ts.map +1 -1
  137. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.d.ts.map +1 -1
  138. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersHeaderActions.defaults.d.ts.map +1 -1
  139. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesEmptyList.d.ts.map +1 -1
  140. package/dist/types/plugins/Emojis/EmojiPicker.d.ts +6 -1
  141. package/dist/types/plugins/Emojis/EmojiPicker.d.ts.map +1 -1
  142. package/dist/types/plugins/Emojis/Picker.d.ts +7 -0
  143. package/dist/types/plugins/Emojis/Picker.d.ts.map +1 -0
  144. package/dist/types/utils/getChannelConfig.d.ts +8 -0
  145. package/dist/types/utils/getChannelConfig.d.ts.map +1 -0
  146. package/package.json +3 -9
  147. package/AI.md +0 -423
  148. package/dist/cjs/useChannelHeaderOnlineStatus.6ace991c.js +0 -4485
  149. package/dist/cjs/useChannelHeaderOnlineStatus.6ace991c.js.map +0 -1
  150. package/dist/cjs/useMessageComposerController.f8f20e3d.js +0 -99
  151. package/dist/cjs/useMessageComposerController.f8f20e3d.js.map +0 -1
  152. package/dist/cjs/useNotificationApi.bf76a16b.js.map +0 -1
  153. package/dist/es/useChannelHeaderOnlineStatus.d6065c22.mjs +0 -3899
  154. package/dist/es/useChannelHeaderOnlineStatus.d6065c22.mjs.map +0 -1
  155. package/dist/es/useMessageComposerController.d50f6168.mjs +0 -69
  156. package/dist/es/useMessageComposerController.d50f6168.mjs.map +0 -1
  157. package/dist/es/useNotificationApi.ed6bff88.mjs.map +0 -1
@@ -1,3899 +0,0 @@
1
- import { $n as useStableId, Ar as DialogPortalEntry, Bn as IconUser, Cr as DialogManagerProvider, En as IconRefresh, Gr as useComponentContext, Ht as IconDownload, Ir as useDialogOnNearestManager, It as IconChevronLeft, Jr as useChatContext, Jt as IconExclamationMarkFill, Lr as useModalDialog, Lt as IconChevronRight, Mt as IconCheckmark, Nr as useDialog, Pr as useDialogIsOpen, Q as isMessageDeleted, Qn as Button, Rr as useModalDialogIsOpen, Tr as modalDialogManagerId, Vr as useStateStore, Yn as IconXmark, Yt as IconExclamationTriangleFill, Zr as useChannelStateContext, _r as getDateString, an as IconLoading, bt as IconArrowDownCircle, ir as isNotificationForPanel, jr as Portal, mr as useTranslationContext, n as hasSystemNotificationTag, qt as IconExclamationMark, r as useNotificationApi, sr as AriaLiveAnnouncerContext, tn as IconImage, vr as isDate, xn as IconPlayFill, xt as IconArrowLeft, z as usePopoverPosition, zr as useModalDialogIsTopmost } from "./useNotificationApi.ed6bff88.mjs";
2
- import React, { createContext, createElement, forwardRef, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
3
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
- import { isGiphyAttachment, isImageAttachment, isLocalImageAttachment, isLocalVideoAttachment, isScrapedContent, isVideoAttachment } from "stream-chat";
5
- import clsx from "clsx";
6
- import { useSyncExternalStore } from "use-sync-external-store/shim";
7
- import { FocusScope } from "@react-aria/focus";
8
- import { SKIP, visit } from "unist-util-visit";
9
- import ReactMarkdown from "react-markdown";
10
- import remarkGfm from "remark-gfm";
11
- import { sanitizeUrl } from "@braintree/sanitize-url";
12
- import { toString } from "mdast-util-to-string";
13
- import remarkParse from "remark-parse";
14
- import { unified } from "unified";
15
- //#region src/context/MessageContext.tsx
16
- var MessageContext = React.createContext(void 0);
17
- var MessageProvider = ({ children, value }) => /* @__PURE__ */ jsx(MessageContext.Provider, {
18
- value,
19
- children
20
- });
21
- var useMessageContext = (_componentName) => {
22
- const contextValue = useContext(MessageContext);
23
- if (!contextValue) return {};
24
- return contextValue;
25
- };
26
- //#endregion
27
- //#region src/context/MessageTranslationViewContext.tsx
28
- /**
29
- * Message translation view context: user-specific state for whether each message
30
- * shows original text or a translation.
31
- *
32
- * ## Spec
33
- *
34
- * - **State**: Per message list (channel vs thread), we store a map
35
- * `messageId → 'original' | 'translated'`. Default for messages with `message.i18n`
36
- * is `'translated'`; otherwise `'original'`.
37
- *
38
- * - **Provider placement**: The provider is tied to the **message list**, not the channel.
39
- * It is rendered inside `MessageList` and `VirtualizedMessageList`. That way the
40
- * main channel list and the thread list each have their own translation view state
41
- * (e.g. Thread.tsx gets correct behavior without sharing channel state).
42
- *
43
- * - **Multiple translations**: `message.i18n` can contain multiple languages, e.g.:
44
- * `{ en_text: "Good morning", fr_text: "Bonjour", it_text: "Buongiorno", language: "en" }`.
45
- * Which translation is shown is determined by the app’s **user language**
46
- * (`useTranslationContext().userLanguage`). We use `message.i18n[userLanguage + '_text']`;
47
- * if missing, we fall back to `message.text`. Only one translation is shown at a time.
48
- *
49
- * - **Source language**: When present, `message.i18n.language` is the original/source
50
- * language of `message.text`. It can be used for the indicator label, e.g.
51
- * "Translated from English · View original".
52
- *
53
- * - **Invariants**: The original message content, layout, grouping, and order stay
54
- * unchanged. Removing or toggling translation only changes the annotation and which
55
- * text is displayed. "View original" / "View translation" switch the displayed
56
- * text and update the annotation (e.g. "Original · View translation" when showing
57
- * original).
58
- *
59
- * - **Translation indicator visibility**: The translation indicator (e.g. "Translated ·
60
- * View original") is **not** shown when the currently viewed text already corresponds
61
- * to `userLanguage` — for example when viewing original and the original text is the
62
- * user-language version (i.e. `getTranslatedMessageText({ language: userLanguage, message })`
63
- * equals `message.text`). In that case there is no meaningful original/translated choice,
64
- * so the indicator is hidden.
65
- */
66
- /**
67
- * Returns the translated message text for a given language from `message.i18n`, or
68
- * undefined if not present. Used to resolve which of the multiple translations to show.
69
- */
70
- var getTranslatedMessageText = ({ language, message }) => message?.i18n?.[`${language}_text`];
71
- var defaultContextValue = {
72
- getTranslationView: (_messageId, hasI18n) => hasI18n ? "translated" : "original",
73
- setTranslationView: () => {}
74
- };
75
- var MessageTranslationViewContext = createContext(defaultContextValue);
76
- var MessageTranslationViewProvider = ({ children }) => {
77
- const [viewByMessageId, setViewByMessageId] = useState({});
78
- const setTranslationView = useCallback((messageId, view) => {
79
- setViewByMessageId((prev) => ({
80
- ...prev,
81
- [messageId]: view
82
- }));
83
- }, []);
84
- const getTranslationView = useCallback((messageId, hasI18n) => viewByMessageId[messageId] ?? (hasI18n ? "translated" : "original"), [viewByMessageId]);
85
- const stableValue = React.useMemo(() => ({
86
- getTranslationView,
87
- setTranslationView
88
- }), [getTranslationView, setTranslationView]);
89
- return /* @__PURE__ */ jsx(MessageTranslationViewContext.Provider, {
90
- value: stableValue,
91
- children
92
- });
93
- };
94
- var useMessageTranslationViewContext = () => {
95
- return useContext(MessageTranslationViewContext) ?? defaultContextValue;
96
- };
97
- //#endregion
98
- //#region src/context/ModalContext.tsx
99
- var ModalContext = React.createContext(void 0);
100
- var ModalContextProvider = ({ children, value }) => /* @__PURE__ */ jsx(ModalContext.Provider, {
101
- value,
102
- children
103
- });
104
- var useModalContext = () => {
105
- const contextValue = useContext(ModalContext);
106
- if (!contextValue) {
107
- console.warn(`The useModalContext hook was called outside of the ModalContext provider. Make sure this hook is called within a child of the GlobalModal.`);
108
- return { close: () => null };
109
- }
110
- return contextValue;
111
- };
112
- //#endregion
113
- //#region src/components/Accessibility/AriaLiveOutletContext.ts
114
- var AriaLiveOutletContext = createContext(void 0);
115
- var useAriaLiveOutletContext = () => useContext(AriaLiveOutletContext);
116
- //#endregion
117
- //#region src/components/VisuallyHidden/VisuallyHidden.tsx
118
- var visuallyHiddenStyle = {
119
- border: 0,
120
- clip: "rect(0, 0, 0, 0)",
121
- height: "1px",
122
- margin: "-1px",
123
- overflow: "hidden",
124
- padding: 0,
125
- position: "absolute",
126
- whiteSpace: "nowrap",
127
- width: "1px"
128
- };
129
- /**
130
- * Hides content visually while preserving it for assistive technologies.
131
- */
132
- var VisuallyHidden = ({ children, style, ...rest }) => /* @__PURE__ */ jsx("span", {
133
- ...rest,
134
- style: {
135
- ...visuallyHiddenStyle,
136
- ...style
137
- },
138
- children
139
- });
140
- //#endregion
141
- //#region src/components/Accessibility/AriaLiveOutlet.tsx
142
- /**
143
- * Dumb render target for the unified announcer. Registers itself with the nearest
144
- * {@link AriaLiveAnnouncerProvider} and renders the polite/assertive live regions ONLY when
145
- * it is the active (innermost) outlet, so exactly one region is live at a time — always in
146
- * the active accessibility-tree scope. Mount one at the `Chat` root and one inside each
147
- * `aria-modal` dialog (with a higher `layer`).
148
- */
149
- var AriaLiveOutlet = ({ layer = 0, portal = false }) => {
150
- const context = useAriaLiveOutletContext();
151
- const idRef = useRef(null);
152
- if (idRef.current === null) idRef.current = Symbol("aria-live-outlet");
153
- const registerOutlet = context?.registerOutlet;
154
- const unregisterOutlet = context?.unregisterOutlet;
155
- useEffect(() => {
156
- const id = idRef.current;
157
- if (!id || !registerOutlet || !unregisterOutlet) return;
158
- registerOutlet(id, layer);
159
- return () => unregisterOutlet(id);
160
- }, [
161
- layer,
162
- registerOutlet,
163
- unregisterOutlet
164
- ]);
165
- const getPortalDestination = useCallback(() => document.body, []);
166
- if (!context || context.activeOutletId !== idRef.current) return null;
167
- const { announcementsByPriority } = context;
168
- const liveRegions = /* @__PURE__ */ jsxs(VisuallyHidden, { children: [/* @__PURE__ */ jsx("div", {
169
- "aria-atomic": "false",
170
- "aria-live": "polite",
171
- "aria-relevant": "additions",
172
- "data-testid": "str-chat__aria-live-region--polite",
173
- role: "status",
174
- children: announcementsByPriority.polite.map((announcement) => /* @__PURE__ */ jsx("div", { children: announcement.message }, announcement.id))
175
- }), /* @__PURE__ */ jsx("div", {
176
- "aria-atomic": "false",
177
- "aria-live": "assertive",
178
- "aria-relevant": "additions",
179
- "data-testid": "str-chat__aria-live-region--assertive",
180
- role: "alert",
181
- children: announcementsByPriority.assertive.map((announcement) => /* @__PURE__ */ jsx("div", { children: announcement.message }, announcement.id))
182
- })] });
183
- if (!portal) return liveRegions;
184
- return /* @__PURE__ */ jsx(Portal, {
185
- getPortalDestination,
186
- isOpen: true,
187
- children: liveRegions
188
- });
189
- };
190
- //#endregion
191
- //#region src/components/Notifications/NotificationConfigurationContext.tsx
192
- var defaultNotificationDisplayFilter = () => true;
193
- var defaultNotificationConfigurationContextValue = { displayFilter: defaultNotificationDisplayFilter };
194
- var NotificationConfigurationContext = React.createContext(defaultNotificationConfigurationContextValue);
195
- var NotificationConfigurationProvider = ({ children, displayFilter }) => {
196
- const parentConfiguration = useContext(NotificationConfigurationContext);
197
- const value = useMemo(() => ({ displayFilter: displayFilter ?? parentConfiguration.displayFilter }), [displayFilter, parentConfiguration.displayFilter]);
198
- return /* @__PURE__ */ jsx(NotificationConfigurationContext.Provider, {
199
- value,
200
- children
201
- });
202
- };
203
- var useNotificationConfigurationContext = () => useContext(NotificationConfigurationContext);
204
- //#endregion
205
- //#region src/components/Notifications/hooks/useNotifications.ts
206
- /**
207
- * Subscribes to client.notifications.store and returns the list of notifications.
208
- * Optionally pass a filter so only notifications that match are returned (e.g. for a specific NotificationList).
209
- */
210
- var useNotifications = (options) => {
211
- const { client } = useChatContext();
212
- const { displayFilter } = useNotificationConfigurationContext();
213
- const { applyDisplayFilter, fallbackPanel, filter, panel } = options ?? {};
214
- const selector = useCallback((state) => {
215
- return { notifications: state.notifications.filter((notification) => {
216
- if (panel && !isNotificationForPanel(notification, panel, { fallbackPanel })) return false;
217
- if (applyDisplayFilter && !displayFilter({
218
- fallbackPanel,
219
- filter,
220
- notification,
221
- panel
222
- })) return false;
223
- return filter ? filter(notification) : true;
224
- }) };
225
- }, [
226
- applyDisplayFilter,
227
- displayFilter,
228
- fallbackPanel,
229
- filter,
230
- panel
231
- ]);
232
- const { notifications } = useStateStore(client.notifications.store, selector);
233
- return notifications;
234
- };
235
- //#endregion
236
- //#region src/components/Loading/LoadingIndicator.tsx
237
- var LoadingIndicator = (props) => /* @__PURE__ */ jsx(IconLoading, {
238
- ...props,
239
- className: "str-chat__loading-indicator"
240
- });
241
- //#endregion
242
- //#region src/components/MessageComposer/hooks/utils.ts
243
- function prettifyFileSize(bytes, precision = 3) {
244
- const units = [
245
- "B",
246
- "kB",
247
- "MB",
248
- "GB"
249
- ];
250
- const exponent = bytes === 0 ? 0 : Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
251
- const mantissa = bytes / 1024 ** exponent;
252
- return `${precision === 0 ? Math.round(mantissa).toString() : mantissa.toPrecision(precision)} ${units[exponent]}`;
253
- }
254
- //#endregion
255
- //#region src/components/Attachment/components/FileSizeIndicator.tsx
256
- var FileSizeIndicator = ({ fileSize, maximumFractionDigits }) => {
257
- const actualFileSize = typeof fileSize === "string" ? parseFloat(fileSize) : fileSize;
258
- if (typeof actualFileSize === "undefined" || !Number.isFinite(Number(actualFileSize))) return null;
259
- return /* @__PURE__ */ jsx("span", {
260
- className: "str-chat__message-attachment-file--item-size",
261
- "data-testid": "file-size-indicator",
262
- children: prettifyFileSize(actualFileSize, maximumFractionDigits)
263
- });
264
- };
265
- //#endregion
266
- //#region src/utils/useStableCallback.ts
267
- /**
268
- * A utility hook implementing a stable callback. It takes in an unstable method that
269
- * is supposed to be invoked somewhere deeper in the DOM tree without making it
270
- * change its reference every time the parent component rerenders. It will also return
271
- * the value of the callback if it does return one.
272
- * A common use-case would be having a function whose invocation depends on state
273
- * somewhere high up in the DOM tree and wanting to use the same function deeper
274
- * down, for example in a leaf node and simply using useCallback results in
275
- * cascading dependency hell. If we wrap it in useStableCallback, we would be able
276
- * to:
277
- * - Use the same function as a dependency of another hook (since it is stable)
278
- * - Still invoke it and get the latest state
279
- *
280
- * **Caveats:**
281
- * - Never wrap a function that is supposed to return a React.ReactElement in
282
- * useStableCallback, since React will not know that the DOM needs to be updated
283
- * whenever the callback value changes (for example, renderItem from FlatList must
284
- * never be wrapped in this hook)
285
- * - Always prefer using a standard useCallback/stable function wherever possible
286
- * (the purpose of useStableCallback is to bridge the gap between top level contexts
287
- * and cascading rereders in downstream components - **not** as an escape hatch)
288
- * @param callback - the callback we want to stabilize
289
- */
290
- var useStableCallback = (callback) => {
291
- const ref = useRef(callback);
292
- ref.current = callback;
293
- return useCallback((...args) => ref.current(...args), []);
294
- };
295
- //#endregion
296
- //#region src/a11y/hooks/useAriaIdentifiers.ts
297
- var sanitizeAriaRootId = (rootId) => rootId?.trim().replace(/[^A-Za-z0-9:_-]/g, "-") ?? "";
298
- var buildAriaIdentifier = (sanitizedRootId, descriptor) => sanitizedRootId ? `${sanitizedRootId}-${descriptor}` : void 0;
299
- /**
300
- * Derives stable ARIA identifier IDs from a single root ID.
301
- *
302
- * Use this to keep dialog/component labeling conventions consistent without
303
- * manually building `*-title` and `*-description` IDs at each call site.
304
- *
305
- * Behavior:
306
- * - Root ID is trimmed and sanitized to `[A-Za-z0-9:_-]` before use.
307
- * - Returns `undefined` IDs when root ID is missing/empty after sanitization.
308
- */
309
- var useAriaIdentifiers = (rootId) => {
310
- const sanitizedRootId = sanitizeAriaRootId(rootId);
311
- return useMemo(() => ({
312
- descriptionId: buildAriaIdentifier(sanitizedRootId, "description"),
313
- titleId: buildAriaIdentifier(sanitizedRootId, "title")
314
- }), [sanitizedRootId]);
315
- };
316
- //#endregion
317
- //#region src/components/Dialog/components/Alert.tsx
318
- var Root = forwardRef(function AlertRoot({ children, className, ...props }, ref) {
319
- return /* @__PURE__ */ jsx("div", {
320
- ...props,
321
- className: clsx("str-chat__alert-root", className),
322
- ref,
323
- children
324
- });
325
- });
326
- var Header = forwardRef(function AlertHeader({ children, className, description, descriptionId, Icon, title, titleId, ...props }, ref) {
327
- const { dialogId } = useModalContext();
328
- const { descriptionId: derivedDescriptionId, titleId: derivedTitleId } = useAriaIdentifiers(dialogId);
329
- const resolvedTitleId = titleId ?? derivedTitleId;
330
- const resolvedDescriptionId = descriptionId ?? derivedDescriptionId;
331
- return /* @__PURE__ */ jsx("div", {
332
- ...props,
333
- className: clsx("str-chat__alert-header", className),
334
- ref,
335
- children: title ? /* @__PURE__ */ jsxs(Fragment$1, { children: [Icon && /* @__PURE__ */ jsx(Icon, {}), /* @__PURE__ */ jsxs("div", {
336
- className: "str-chat__alert-header__copy",
337
- children: [/* @__PURE__ */ jsx("h2", {
338
- className: "str-chat__alert-header__title",
339
- id: resolvedTitleId,
340
- children: title
341
- }), description && /* @__PURE__ */ jsx("p", {
342
- className: "str-chat__alert-header__description",
343
- id: resolvedDescriptionId,
344
- children: description
345
- })]
346
- })] }) : children
347
- });
348
- });
349
- var Alert = {
350
- Actions: forwardRef(function AlertActions({ children, className, ...props }, ref) {
351
- return /* @__PURE__ */ jsx("div", {
352
- ...props,
353
- className: clsx("str-chat__alert-actions", className),
354
- ref,
355
- children
356
- });
357
- }),
358
- Header,
359
- Root
360
- };
361
- //#endregion
362
- //#region src/components/Dialog/service/DialogAnchor.tsx
363
- function useDialogAnchor({ allowFlip, offset, open, placement, referenceElement, updateKey, updatePositionOnContentResize = false }) {
364
- const [popperElement, setPopperElement] = useState(null);
365
- const [stabilisedChosenPlacement, setStabilisedChosenPlacement] = useState(null);
366
- const { placement: chosenPlacement, refs, strategy, update, x, y } = usePopoverPosition({
367
- allowFlip,
368
- freeze: true,
369
- offset,
370
- placement: stabilisedChosenPlacement ?? placement
371
- });
372
- if (!stabilisedChosenPlacement && popperElement && placement !== chosenPlacement) setStabilisedChosenPlacement(chosenPlacement);
373
- else if (stabilisedChosenPlacement && !popperElement) setStabilisedChosenPlacement(null);
374
- const frozenReferenceRef = useRef(null);
375
- if (open && referenceElement && !frozenReferenceRef.current) frozenReferenceRef.current = referenceElement;
376
- if (!open) frozenReferenceRef.current = null;
377
- const effectiveReference = open ? frozenReferenceRef.current : referenceElement;
378
- useEffect(() => {
379
- refs.setReference(effectiveReference);
380
- }, [effectiveReference, refs]);
381
- useEffect(() => {
382
- refs.setFloating(popperElement);
383
- }, [popperElement, refs]);
384
- useEffect(() => {
385
- if (open && popperElement && effectiveReference) update?.();
386
- }, [
387
- open,
388
- placement,
389
- popperElement,
390
- update,
391
- updateKey,
392
- effectiveReference
393
- ]);
394
- useEffect(() => {
395
- if (!popperElement || !updatePositionOnContentResize) return;
396
- const resizeObserver = new ResizeObserver(update);
397
- resizeObserver.observe(popperElement);
398
- return () => {
399
- resizeObserver.disconnect();
400
- };
401
- }, [
402
- popperElement,
403
- update,
404
- updatePositionOnContentResize
405
- ]);
406
- if (popperElement && !open) setPopperElement(null);
407
- return {
408
- placement: stabilisedChosenPlacement ?? chosenPlacement,
409
- setPopperElement,
410
- styles: {
411
- left: x ?? 0,
412
- position: strategy,
413
- top: y ?? 0
414
- }
415
- };
416
- }
417
- var DialogAnchor = ({ allowFlip = true, children, className, closeOnClickOutside, closeTransitionMs = 0, dialogManagerId, focus = true, id, offset, placement = "auto", referenceElement = null, tabIndex, trapFocus, updateKey, updatePositionOnContentResize, ...restDivProps }) => {
418
- /**
419
- * Rendering lifecycle notes:
420
- * - `open=true` renders dialog contents immediately.
421
- * - `open=false` can keep contents mounted for `closeTransitionMs` to allow CSS exit
422
- * animations before unmount.
423
- *
424
- * State exposed to CSS:
425
- * - `data-str-chat-dialog-state="open"` while actively open.
426
- * - `data-str-chat-dialog-state="closing"` during delayed unmount window.
427
- *
428
- * Consumers like `ContextMenu` combine:
429
- * - `data-str-chat-dialog-state` from this component, and
430
- * - `data-str-chat-placement` (e.g. `top-start`, `right-end`)
431
- * to select the correct closing keyframe in CSS (horizontal vs vertical roll direction).
432
- * In practice, JS only toggles state and timing (`closeTransitionMs`); CSS owns the
433
- * visual motion details (transform/easing), so animation behavior stays declarative.
434
- */
435
- const dialog = useDialog({
436
- closeOnClickOutside,
437
- dialogManagerId,
438
- id
439
- });
440
- const open = useDialogIsOpen(id, dialogManagerId);
441
- const [shouldRender, setShouldRender] = useState(open);
442
- const closeTimeoutRef = useRef(null);
443
- const isClosing = !open && shouldRender;
444
- useEffect(() => {
445
- if (open) {
446
- setShouldRender(true);
447
- if (closeTimeoutRef.current) {
448
- clearTimeout(closeTimeoutRef.current);
449
- closeTimeoutRef.current = null;
450
- }
451
- return;
452
- }
453
- if (!shouldRender) return;
454
- if (!closeTransitionMs) {
455
- setShouldRender(false);
456
- return;
457
- }
458
- closeTimeoutRef.current = setTimeout(() => {
459
- setShouldRender(false);
460
- closeTimeoutRef.current = null;
461
- }, closeTransitionMs);
462
- }, [
463
- closeTransitionMs,
464
- open,
465
- shouldRender
466
- ]);
467
- useEffect(() => () => {
468
- if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
469
- }, []);
470
- const { placement: chosenPlacement, setPopperElement, styles } = useDialogAnchor({
471
- allowFlip,
472
- offset,
473
- open: shouldRender,
474
- placement,
475
- referenceElement,
476
- updateKey,
477
- updatePositionOnContentResize
478
- });
479
- useEffect(() => {
480
- if (!open) return;
481
- const hideOnEscape = (event) => {
482
- if (event.key !== "Escape" || event.defaultPrevented) return;
483
- dialog?.close();
484
- };
485
- document.addEventListener("keyup", hideOnEscape);
486
- return () => {
487
- document.removeEventListener("keyup", hideOnEscape);
488
- };
489
- }, [dialog, open]);
490
- if (!shouldRender) return null;
491
- const { ["aria-describedby"]: ariaDescribedBy, ["aria-label"]: ariaLabel, ["aria-labelledby"]: ariaLabelledBy, ["aria-modal"]: ariaModal, role, ...anchorDivProps } = restDivProps;
492
- const resolvedRole = trapFocus ? role ?? "dialog" : role;
493
- const resolvedAriaModal = trapFocus ? true : ariaModal;
494
- const resolvedAriaLabel = ariaLabelledBy ? void 0 : ariaLabel;
495
- return /* @__PURE__ */ jsx(DialogPortalEntry, {
496
- dialogId: id,
497
- dialogManagerId,
498
- children: /* @__PURE__ */ jsx(FocusScope, {
499
- autoFocus: focus,
500
- contain: trapFocus,
501
- restoreFocus: true,
502
- children: /* @__PURE__ */ jsxs("div", {
503
- ...anchorDivProps,
504
- "aria-describedby": ariaDescribedBy,
505
- "aria-label": resolvedAriaLabel,
506
- "aria-labelledby": ariaLabelledBy,
507
- "aria-modal": resolvedAriaModal,
508
- className: clsx("str-chat__dialog-contents", className),
509
- "data-str-chat-dialog-state": isClosing ? "closing" : "open",
510
- "data-str-chat-placement": chosenPlacement,
511
- "data-testid": "str-chat__dialog-contents",
512
- ref: setPopperElement,
513
- role: resolvedRole,
514
- style: styles,
515
- tabIndex: typeof tabIndex !== "undefined" ? tabIndex : 0,
516
- children: [children, resolvedAriaModal && /* @__PURE__ */ jsx(AriaLiveOutlet, { layer: 1 })]
517
- })
518
- })
519
- });
520
- };
521
- //#endregion
522
- //#region src/components/Avatar/Avatar.tsx
523
- var getInitials = (name) => {
524
- const regex = /(\p{L}{1})\p{L}+/gu;
525
- if (!name || name.trim().length === 0) return "";
526
- const initials = Array.from(name?.matchAll(regex) || []);
527
- if (!initials.length) return "";
528
- return `${initials.at(0)[1]}${initials.length > 1 ? initials.at(-1)[1] : ""}`;
529
- };
530
- /**
531
- * A round avatar image with fallback to username's first letter
532
- */
533
- var Avatar = ({ className, FallbackIcon = IconUser, imageUrl, initials: customInitials, isOnline, size, userName, ...rest }) => {
534
- const [error, setError] = useState(false);
535
- useEffect(() => () => setError(false), [imageUrl]);
536
- const nameString = userName?.toString().trim() || "";
537
- const sizeAwareInitials = useMemo(() => {
538
- const initials = customInitials || getInitials(nameString);
539
- if (size === "sm" || size === "xs") return initials.charAt(0);
540
- return initials;
541
- }, [
542
- nameString,
543
- size,
544
- customInitials
545
- ]);
546
- const showImage = typeof imageUrl === "string" && imageUrl && !error;
547
- return /* @__PURE__ */ jsxs("div", {
548
- className: clsx(`str-chat__avatar`, className, {
549
- "str-chat__avatar--multiple-letters": sizeAwareInitials.length > 1,
550
- "str-chat__avatar--no-letters": !sizeAwareInitials.length,
551
- "str-chat__avatar--one-letter": sizeAwareInitials.length === 1,
552
- [`str-chat__avatar--size-${size}`]: typeof size === "string"
553
- }),
554
- "data-testid": "avatar",
555
- role: "button",
556
- title: userName,
557
- ...rest,
558
- children: [typeof isOnline === "boolean" && /* @__PURE__ */ jsx("div", { className: clsx("str-chat__avatar-status-badge", {
559
- "str-chat__avatar-status-badge--offline": !isOnline,
560
- "str-chat__avatar-status-badge--online": isOnline
561
- }) }), showImage ? /* @__PURE__ */ jsx("img", {
562
- alt: nameString,
563
- className: "str-chat__avatar-image",
564
- "data-testid": "avatar-img",
565
- onError: () => setError(true),
566
- src: imageUrl
567
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!!sizeAwareInitials.length && /* @__PURE__ */ jsx("div", {
568
- className: "str-chat__avatar-initials",
569
- "data-testid": "avatar-fallback",
570
- children: sizeAwareInitials
571
- }), !sizeAwareInitials.length && /* @__PURE__ */ jsx(FallbackIcon, {})] })]
572
- });
573
- };
574
- //#endregion
575
- //#region src/components/Badge/Badge.tsx
576
- /**
577
- * Compact pill/circle badge for counts and labels.
578
- * Uses design tokens: --badge-bg-*, --badge-text-*, --badge-border.
579
- */
580
- var Badge = ({ children, className, size = "md", variant = "default", ...spanProps }) => /* @__PURE__ */ jsx("div", {
581
- ...spanProps,
582
- className: clsx("str-chat__badge", `str-chat__badge--variant-${variant}`, { [`str-chat__badge--size-${size}`]: size }, className),
583
- children
584
- });
585
- var ErrorBadge = ({ className, size = "sm", ...rest }) => /* @__PURE__ */ jsx(Badge, {
586
- ...rest,
587
- className,
588
- size,
589
- variant: "error",
590
- children: /* @__PURE__ */ jsx(IconExclamationMarkFill, {})
591
- });
592
- //#endregion
593
- //#region src/components/Avatar/ChannelAvatar.tsx
594
- var ChannelAvatar = ({ displayMembers, imageUrl, size, userName, ...sharedProps }) => {
595
- return /* @__PURE__ */ jsx(GroupAvatar, {
596
- displayMembers: useMemo(() => {
597
- if (!imageUrl && displayMembers && displayMembers.length > 0) return displayMembers;
598
- return [{
599
- imageUrl,
600
- userName
601
- }];
602
- }, [
603
- displayMembers,
604
- imageUrl,
605
- userName
606
- ]),
607
- size,
608
- ...sharedProps
609
- });
610
- };
611
- //#endregion
612
- //#region src/components/Avatar/GroupAvatar.tsx
613
- /**
614
- * Avatar component to display multiple users' avatars in a group.
615
- * Renders a single Avatar if fewer than 2 members. Otherwise, renders up to 2 avatars (when overflowCount is set) or 4, plus an optional +N badge.
616
- */
617
- var GroupAvatar = ({ badgeSize, className, displayMembers = [], isOnline, size, ...rest }) => {
618
- const displayMembersToRender = useMemo(() => displayMembers.length > 4 ? displayMembers.slice(0, 2) : displayMembers, [displayMembers]);
619
- const overflowCount = displayMembers.length - displayMembersToRender.length;
620
- if (displayMembers.length < 2) {
621
- const firstUser = displayMembers[0];
622
- return /* @__PURE__ */ jsx(Avatar, {
623
- imageUrl: firstUser?.imageUrl,
624
- isOnline,
625
- size,
626
- userName: firstUser?.userName,
627
- ...rest
628
- });
629
- }
630
- let avatarSize = null;
631
- if (size === "2xl") avatarSize = "lg";
632
- else if (size === "xl") avatarSize = "md";
633
- else if (size === "lg") avatarSize = "sm";
634
- return /* @__PURE__ */ jsxs("div", {
635
- className: clsx("str-chat__avatar-group", {
636
- "str-chat__avatar-group--offline": typeof isOnline === "boolean" && !isOnline,
637
- "str-chat__avatar-group--online": typeof isOnline === "boolean" && isOnline,
638
- [`str-chat__avatar-group--size-${size}`]: typeof size === "string"
639
- }, className),
640
- "data-testid": "group-avatar",
641
- role: "button",
642
- ...rest,
643
- children: [displayMembersToRender.map(({ id, imageUrl, userName }, index) => /* @__PURE__ */ jsx(Avatar, {
644
- imageUrl,
645
- size: avatarSize,
646
- userName
647
- }, id || `${userName}-${imageUrl}-${index}`)), typeof overflowCount === "number" && overflowCount > 0 && /* @__PURE__ */ jsxs(Badge, {
648
- className: "str-chat__avatar-group__count-badge",
649
- "data-testid": "group-avatar-count-badge",
650
- size: badgeSize,
651
- variant: "counter",
652
- children: ["+", overflowCount]
653
- })]
654
- });
655
- };
656
- //#endregion
657
- //#region src/a11y/a11yUtils.ts
658
- var MENU_KEYBOARD_NAVIGATION_KEYS = [
659
- "ArrowDown",
660
- "ArrowUp",
661
- "End",
662
- "Home"
663
- ];
664
- var isMenuKeyboardNavigationKey = (key) => MENU_KEYBOARD_NAVIGATION_KEYS.includes(key);
665
- var getNextRovingFocusIndex = ({ activeIndex, itemCount, key }) => {
666
- if (itemCount <= 0 || !isMenuKeyboardNavigationKey(key)) return null;
667
- const lastIndex = itemCount - 1;
668
- if (key === "Home") return 0;
669
- if (key === "End") return lastIndex;
670
- if (activeIndex === -1) return key === "ArrowUp" ? lastIndex : 0;
671
- if (key === "ArrowUp") return activeIndex <= 0 ? lastIndex : activeIndex - 1;
672
- return activeIndex >= lastIndex ? 0 : activeIndex + 1;
673
- };
674
- var getDefaultActiveIndex = (items) => {
675
- const activeElement = document.activeElement;
676
- if (!(activeElement instanceof Element)) return -1;
677
- return items.findIndex((item) => item instanceof Element && item === activeElement);
678
- };
679
- var createRovingFocusKeyDownHandler = ({ focusItem = (item) => item.focus(), getActiveIndex = (items) => getDefaultActiveIndex(items), getItems }) => {
680
- const handleKeyDown = (event) => {
681
- const items = getItems(event);
682
- const nextIndex = getNextRovingFocusIndex({
683
- activeIndex: getActiveIndex(items, event),
684
- itemCount: items.length,
685
- key: event.key
686
- });
687
- if (nextIndex === null) return;
688
- event.preventDefault();
689
- const nextItem = items[nextIndex];
690
- if (!nextItem) return;
691
- focusItem(nextItem, event);
692
- };
693
- return handleKeyDown;
694
- };
695
- //#endregion
696
- //#region src/components/Dialog/components/ContextMenu.tsx
697
- var BaseContextMenuButton = ({ children, className, details, hasSubMenu, Icon, label, role = "menuitem", SubmenuIcon = IconChevronRight, variant, ...props }) => /* @__PURE__ */ jsxs("button", {
698
- ...props,
699
- className: clsx("str-chat__context-menu__button", {
700
- "str-chat__context-menu__button--with-submenu": hasSubMenu,
701
- [`str-chat__context-menu__button--${variant}`]: typeof variant === "string"
702
- }, className),
703
- role,
704
- type: "button",
705
- children: [
706
- Icon && /* @__PURE__ */ jsx(Icon, { className: "str-chat__context-menu__button__icon" }),
707
- label ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
708
- className: "str-chat__context-menu__button__label",
709
- children: label
710
- }), /* @__PURE__ */ jsx("div", {
711
- className: "str-chat__context-menu__button__details",
712
- children: details
713
- })] }) : /* @__PURE__ */ jsx("div", {
714
- className: "str-chat__context-menu__button__label",
715
- children
716
- }),
717
- !!hasSubMenu && /* @__PURE__ */ jsx(SubmenuIcon, { className: "str-chat__context-menu__button__submenu-icon" })
718
- ]
719
- });
720
- var UserContextMenuButton = ({ children, className, imageUrl, role = "menuitem", userName, ...props }) => {
721
- const { Avatar: Avatar$1 = Avatar } = useComponentContext();
722
- return /* @__PURE__ */ jsxs("button", {
723
- ...props,
724
- className: clsx("str-chat__context-menu__button str-chat__user-context-menu__button", className),
725
- role,
726
- type: "button",
727
- children: [/* @__PURE__ */ jsx(Avatar$1, {
728
- "aria-hidden": true,
729
- imageUrl,
730
- size: "sm",
731
- userName
732
- }), /* @__PURE__ */ jsx("div", {
733
- className: "str-chat__context-menu__button__label",
734
- children: children ?? userName
735
- })]
736
- });
737
- };
738
- var EmojiContextMenuButton = ({ children, className, emoji, label, role = "menuitem", ...props }) => /* @__PURE__ */ jsxs("button", {
739
- ...props,
740
- className: clsx("str-chat__context-menu__button str-chat__emoji-context-menu__button", className),
741
- role,
742
- type: "button",
743
- children: [/* @__PURE__ */ jsx("span", {
744
- className: "str-chat__context-menu__button__emoji str-chat__emoji-item--entity",
745
- children: emoji
746
- }), /* @__PURE__ */ jsx("div", {
747
- className: "str-chat__context-menu__button__label",
748
- children: children ?? label
749
- })]
750
- });
751
- var ContextMenuButtonWithSubmenu = ({ children, className, Submenu, submenuContainerProps, submenuPlacement = "right-start", submenuRollAxis = "x", ...buttonProps }) => {
752
- const { className: submenuClassName, ...submenuContainerRestProps } = submenuContainerProps ?? {};
753
- const { registerDialogSubmenu, unregisterDialogSubmenu } = useContextMenuContext();
754
- const buttonRef = useRef(null);
755
- const [dialogContainer, setDialogContainer] = useState(null);
756
- const keepSubmenuOpenFlag = useRef(false);
757
- const dialogCloseTimeout = useRef(null);
758
- const dialogId = useMemo(() => `submenu-${Math.random().toString(36).slice(2)}`, []);
759
- const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId });
760
- const dialogIsOpen = useDialogIsOpen(dialogId, dialogManager?.id);
761
- useEffect(() => {
762
- if (!dialogIsOpen) return;
763
- registerDialogSubmenu();
764
- return () => unregisterDialogSubmenu();
765
- }, [
766
- dialogIsOpen,
767
- registerDialogSubmenu,
768
- unregisterDialogSubmenu
769
- ]);
770
- const { placement: chosenPlacement, setPopperElement, styles } = useDialogAnchor({
771
- offset: 8,
772
- open: dialogIsOpen,
773
- placement: submenuPlacement,
774
- referenceElement: buttonRef.current
775
- });
776
- const closeDialogLazily = useCallback(() => {
777
- if (dialogCloseTimeout.current) clearTimeout(dialogCloseTimeout.current);
778
- dialogCloseTimeout.current = setTimeout(() => {
779
- if (keepSubmenuOpenFlag.current) return;
780
- dialog.close();
781
- }, 100);
782
- }, [dialog]);
783
- const keepSubmenuOpen = useCallback(() => {
784
- keepSubmenuOpenFlag.current = true;
785
- }, []);
786
- const allowToCloseSubmenu = useCallback(() => {
787
- keepSubmenuOpenFlag.current = false;
788
- }, []);
789
- const closeSubmenu = useCallback(() => {
790
- allowToCloseSubmenu();
791
- closeDialogLazily();
792
- }, [allowToCloseSubmenu, closeDialogLazily]);
793
- const handleClose = useCallback((event) => {
794
- const parentButton = buttonRef.current;
795
- if (!dialogIsOpen || !parentButton) return;
796
- event.stopPropagation();
797
- closeDialogLazily();
798
- parentButton.focus();
799
- }, [
800
- closeDialogLazily,
801
- dialogIsOpen,
802
- buttonRef
803
- ]);
804
- const handleFocusParentButton = () => {
805
- if (dialogIsOpen) return;
806
- dialog.open();
807
- keepSubmenuOpen();
808
- };
809
- useEffect(() => {
810
- const parentButton = buttonRef.current;
811
- if (!dialogIsOpen || !parentButton) return;
812
- const hideOnEscape = (event) => {
813
- if (event.key !== "Escape") return;
814
- handleClose(event);
815
- closeSubmenu();
816
- };
817
- document.addEventListener("keyup", hideOnEscape, { capture: true });
818
- return () => {
819
- document.removeEventListener("keyup", hideOnEscape, { capture: true });
820
- };
821
- }, [
822
- dialogIsOpen,
823
- handleClose,
824
- closeSubmenu
825
- ]);
826
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BaseContextMenuButton, {
827
- "aria-expanded": dialogIsOpen,
828
- "aria-haspopup": "menu",
829
- className: clsx(className, "str_chat__button-with-submenu", { "str_chat__button-with-submenu--submenu-open": dialogIsOpen }),
830
- hasSubMenu: true,
831
- onBlur: closeSubmenu,
832
- onClick: (event) => {
833
- event.stopPropagation();
834
- dialog.toggle();
835
- },
836
- onFocus: handleFocusParentButton,
837
- onMouseEnter: handleFocusParentButton,
838
- onMouseLeave: closeSubmenu,
839
- ...buttonProps,
840
- ref: buttonRef,
841
- children
842
- }), dialogIsOpen && /* @__PURE__ */ jsx("div", {
843
- className: clsx("str-chat__context-menu__submenu-container", submenuClassName),
844
- "data-str-chat-placement": chosenPlacement,
845
- "data-str-chat-roll-axis": submenuRollAxis,
846
- onBlur: (event) => {
847
- if (event.relatedTarget instanceof Node && dialogContainer?.contains(event.relatedTarget)) return;
848
- closeSubmenu();
849
- },
850
- onFocus: keepSubmenuOpen,
851
- onMouseEnter: keepSubmenuOpen,
852
- onMouseLeave: closeSubmenu,
853
- ref: (element) => {
854
- setPopperElement(element);
855
- setDialogContainer(element);
856
- },
857
- style: styles,
858
- tabIndex: -1,
859
- ...submenuContainerRestProps,
860
- children: /* @__PURE__ */ jsx(Submenu, {})
861
- })] });
862
- };
863
- var ContextMenuButton = (props) => {
864
- const { Submenu, submenuContainerProps, submenuPlacement, submenuRollAxis, ...buttonProps } = props;
865
- if (Submenu) return /* @__PURE__ */ jsx(ContextMenuButtonWithSubmenu, {
866
- ...buttonProps,
867
- Submenu,
868
- submenuContainerProps,
869
- submenuPlacement,
870
- submenuRollAxis
871
- });
872
- return /* @__PURE__ */ jsx(BaseContextMenuButton, { ...buttonProps });
873
- };
874
- var ContextMenuBackButton = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, className, role = "menuitem", ...props }) => {
875
- const { t } = useTranslationContext();
876
- const generatedBackNavigationLabelId = useStableId();
877
- const generatedVisibleLabelId = useStableId();
878
- const resolvedAriaLabel = ariaLabel ?? t("aria/Back to parent menu button");
879
- const resolvedAriaLabelledBy = ariaLabelledBy ?? `${generatedVisibleLabelId} ${generatedBackNavigationLabelId}`;
880
- return /* @__PURE__ */ jsxs("button", {
881
- ...props,
882
- "aria-labelledby": resolvedAriaLabelledBy,
883
- className: clsx("str-chat__context-menu__back-button", className),
884
- role,
885
- type: "button",
886
- children: [!ariaLabelledBy && /* @__PURE__ */ jsx(VisuallyHidden, {
887
- id: generatedBackNavigationLabelId,
888
- children: resolvedAriaLabel
889
- }), /* @__PURE__ */ jsx("span", {
890
- className: "str-chat__context-menu__back-button__content",
891
- id: generatedVisibleLabelId,
892
- children
893
- })]
894
- });
895
- };
896
- var ContextMenuHeader = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
897
- ...props,
898
- className: clsx("str-chat__context-menu__header", className),
899
- children
900
- });
901
- var ContextMenuBody = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
902
- ...props,
903
- className: clsx("str-chat__context-menu__body", className),
904
- children
905
- });
906
- var ContextMenuRoot = React.forwardRef(function ContextMenuRoot({ className, role = "menu", ...props }, ref) {
907
- return /* @__PURE__ */ jsx("div", {
908
- ...props,
909
- className: clsx("str-chat__context-menu", className),
910
- ref,
911
- role
912
- });
913
- });
914
- var DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR = [
915
- "[role=\"menuitem\"]:not(:disabled)",
916
- "[role=\"menuitemradio\"]:not(:disabled)",
917
- "[role=\"menuitemcheckbox\"]:not(:disabled)"
918
- ].join(",");
919
- var isVisibleContextMenuKeyboardNavigationItem = (item) => {
920
- if (item.offsetParent !== null || item.offsetHeight > 0) return true;
921
- if (item.hidden) return false;
922
- if (item.style.display === "none") return false;
923
- return true;
924
- };
925
- var getVisibleContextMenuKeyboardNavigationItems = (contextMenuRoot, itemSelector = DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR) => Array.from(contextMenuRoot?.querySelectorAll(itemSelector) ?? []).filter(isVisibleContextMenuKeyboardNavigationItem);
926
- var createContextMenuFocusRestoreRequest = ({ contextMenuRoot, focusReturnTarget }) => {
927
- const target = focusReturnTarget ?? (document.activeElement instanceof HTMLElement ? document.activeElement : null);
928
- if (!target) return {
929
- index: -1,
930
- target: null
931
- };
932
- return {
933
- index: getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot).findIndex((menuItem) => menuItem === target),
934
- target
935
- };
936
- };
937
- var resolveContextMenuFocusRestoreTarget = ({ contextMenuRoot, request }) => {
938
- if (!request) return null;
939
- if (request.target?.isConnected) return request.target;
940
- if (request.index >= 0) return getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot)[request.index] ?? null;
941
- return null;
942
- };
943
- var CONTEXT_MENU_HEADER_SELECTOR = ".str-chat__context-menu__header";
944
- var resolveSubmenuInitialFocusTarget = ({ contextMenuRoot, initialFocus, itemSelector }) => {
945
- if (initialFocus === "none") return null;
946
- const items = getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot, itemSelector);
947
- if (initialFocus === "first") return items[0] ?? null;
948
- return items.find((item) => !item.closest(CONTEXT_MENU_HEADER_SELECTOR)) ?? items[0] ?? null;
949
- };
950
- var ContextMenuContext = React.createContext(void 0);
951
- var useContextMenuContext = () => useContext(ContextMenuContext);
952
- /**
953
- * Internal/default content renderer for {@link ContextMenu}.
954
- *
955
- * Override this through `ComponentContext.ContextMenuContent` when you need to
956
- * customize submenu/back navigation behavior while keeping the same anchor/focus
957
- * handling from `ContextMenu`.
958
- */
959
- function ContextMenuContent({ anchorReferenceElement, backButtonAriaLabel, backLabel, children, className, enableAnimations = true, Header, items, ItemsWrapper, keyboardNavigation, menuClassName, onClose, onMenuLevelChange, transitionDirection, ...props }) {
960
- const { t } = useTranslationContext();
961
- const resolvedBackLabel = backLabel ?? t("Back");
962
- const { ["aria-describedby"]: rootAriaDescribedBy, ["aria-label"]: rootAriaLabel, ["aria-labelledby"]: rootAriaLabelledBy, ...contextMenuRootProps } = props;
963
- const rootLevel = useMemo(() => ({
964
- Header,
965
- items,
966
- ItemsWrapper,
967
- menuClassName
968
- }), [
969
- Header,
970
- items,
971
- ItemsWrapper,
972
- menuClassName
973
- ]);
974
- const [menuStack, setMenuStack] = useState(() => [rootLevel]);
975
- const [menuBodyAnimationKey, setMenuBodyAnimationKey] = useState(0);
976
- const contextMenuRootRef = useRef(null);
977
- const focusRestoreRequestRef = useRef(null);
978
- const pendingForwardFocusRef = useRef(null);
979
- const activeMenu = menuStack[menuStack.length - 1];
980
- const keyboardNavigationItemSelector = keyboardNavigation?.itemSelector ?? DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR;
981
- const ActiveMenuItemsWrapper = activeMenu.ItemsWrapper ?? React.Fragment;
982
- const closeMenu = useCallback(() => {
983
- onClose?.();
984
- }, [onClose]);
985
- const openSubmenu = useCallback(({ focusReturnTarget, Header, initialFocus = "first-item", ItemsWrapper: SubmenuItemsWrapper, menuClassName, Submenu }) => {
986
- pendingForwardFocusRef.current = initialFocus;
987
- focusRestoreRequestRef.current = null;
988
- const nextLevel = {
989
- focusRestoreRequest: createContextMenuFocusRestoreRequest({
990
- contextMenuRoot: contextMenuRootRef.current,
991
- focusReturnTarget
992
- }),
993
- Header,
994
- initialFocus,
995
- ItemsWrapper: SubmenuItemsWrapper ?? ItemsWrapper,
996
- menuClassName,
997
- Submenu
998
- };
999
- setMenuStack((current) => [...current, nextLevel]);
1000
- }, [ItemsWrapper]);
1001
- const returnToParentMenu = useCallback(() => {
1002
- setMenuStack((current) => {
1003
- if (current.length <= 1) return current;
1004
- focusRestoreRequestRef.current = current[current.length - 1]?.focusRestoreRequest ?? null;
1005
- pendingForwardFocusRef.current = null;
1006
- return current.slice(0, -1);
1007
- });
1008
- }, []);
1009
- useEffect(() => {
1010
- setMenuStack((current) => {
1011
- if (current.length === 1 && current[0] === rootLevel) return current;
1012
- return [rootLevel];
1013
- });
1014
- }, [rootLevel]);
1015
- useEffect(() => {
1016
- onMenuLevelChange?.(menuStack.length);
1017
- }, [menuStack.length, onMenuLevelChange]);
1018
- useLayoutEffect(() => {
1019
- const focusRestoreRequest = focusRestoreRequestRef.current;
1020
- const pendingForwardFocus = pendingForwardFocusRef.current;
1021
- if (!focusRestoreRequest && !pendingForwardFocus) return;
1022
- const contextMenuRoot = contextMenuRootRef.current;
1023
- (focusRestoreRequest ? resolveContextMenuFocusRestoreTarget({
1024
- contextMenuRoot,
1025
- request: focusRestoreRequest
1026
- }) : resolveSubmenuInitialFocusTarget({
1027
- contextMenuRoot,
1028
- initialFocus: pendingForwardFocus,
1029
- itemSelector: keyboardNavigationItemSelector
1030
- }))?.focus();
1031
- }, [
1032
- keyboardNavigationItemSelector,
1033
- menuBodyAnimationKey,
1034
- menuStack.length
1035
- ]);
1036
- useEffect(() => {
1037
- if (!transitionDirection) return;
1038
- setMenuBodyAnimationKey((value) => value + 1);
1039
- }, [transitionDirection, menuStack.length]);
1040
- const dialogSubmenuOpenCountRef = useRef(0);
1041
- const registerDialogSubmenu = useCallback(() => {
1042
- dialogSubmenuOpenCountRef.current += 1;
1043
- }, []);
1044
- const unregisterDialogSubmenu = useCallback(() => {
1045
- dialogSubmenuOpenCountRef.current = Math.max(0, dialogSubmenuOpenCountRef.current - 1);
1046
- }, []);
1047
- const rovingFocusKeyDownHandler = useMemo(() => createRovingFocusKeyDownHandler({ getItems: (event) => getVisibleContextMenuKeyboardNavigationItems(event.currentTarget, keyboardNavigationItemSelector) }), [keyboardNavigationItemSelector]);
1048
- const escapeConsumedRef = useRef(false);
1049
- const keyboardNavigationHandler = useCallback((event) => {
1050
- if (event.key === "Escape") {
1051
- if (dialogSubmenuOpenCountRef.current > 0) return;
1052
- event.preventDefault();
1053
- event.stopPropagation();
1054
- if (menuStack.length > 1) {
1055
- escapeConsumedRef.current = true;
1056
- returnToParentMenu();
1057
- } else closeMenu();
1058
- return;
1059
- }
1060
- rovingFocusKeyDownHandler(event);
1061
- }, [
1062
- closeMenu,
1063
- menuStack.length,
1064
- returnToParentMenu,
1065
- rovingFocusKeyDownHandler
1066
- ]);
1067
- const suppressEscapeKeyUp = useCallback((event) => {
1068
- if (event.key === "Escape" && escapeConsumedRef.current) {
1069
- escapeConsumedRef.current = false;
1070
- event.stopPropagation();
1071
- }
1072
- }, []);
1073
- const isSubmenuLevel = menuStack.length > 1;
1074
- return /* @__PURE__ */ jsx(ContextMenuContext.Provider, {
1075
- value: {
1076
- anchorReferenceElement,
1077
- closeMenu,
1078
- openSubmenu,
1079
- registerDialogSubmenu,
1080
- returnToParentMenu,
1081
- unregisterDialogSubmenu
1082
- },
1083
- children: /* @__PURE__ */ jsxs(ContextMenuRoot, {
1084
- "aria-describedby": isSubmenuLevel ? void 0 : rootAriaDescribedBy,
1085
- "aria-label": isSubmenuLevel ? t("aria/Submenu") : rootAriaLabel,
1086
- "aria-labelledby": isSubmenuLevel ? void 0 : rootAriaLabelledBy,
1087
- className: clsx(className, activeMenu.menuClassName),
1088
- "data-str-chat-enable-animations": enableAnimations,
1089
- onKeyDownCapture: keyboardNavigationHandler,
1090
- onKeyUpCapture: suppressEscapeKeyUp,
1091
- ref: contextMenuRootRef,
1092
- ...contextMenuRootProps,
1093
- children: [activeMenu.Header ? /* @__PURE__ */ jsx(activeMenu.Header, {}) : menuStack.length > 1 ? /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, {
1094
- "aria-label": backButtonAriaLabel,
1095
- onClick: returnToParentMenu,
1096
- children: [/* @__PURE__ */ jsx(IconChevronLeft, {}), /* @__PURE__ */ jsx("span", { children: resolvedBackLabel })]
1097
- }) }) : null, /* @__PURE__ */ jsx(ContextMenuBody, {
1098
- className: clsx({
1099
- "str-chat__context-menu__body--submenu-backward": transitionDirection === "backward",
1100
- "str-chat__context-menu__body--submenu-forward": transitionDirection === "forward"
1101
- }),
1102
- children: activeMenu.Submenu ? /* @__PURE__ */ jsx(activeMenu.Submenu, {}) : /* @__PURE__ */ jsx(ActiveMenuItemsWrapper, { children: typeof children !== "undefined" ? children : activeMenu.items?.map((Item, index) => /* @__PURE__ */ jsx(Item, {}, `context-menu-item-${index}`)) })
1103
- }, `context-menu-body-${menuStack.length}-${menuBodyAnimationKey}`)]
1104
- })
1105
- });
1106
- }
1107
- /**
1108
- * Contextual actions menu that can be used in two modes:
1109
- *
1110
- * - Anchored dialog mode: pass `id` + `referenceElement` (submenu-aware positioning).
1111
- * - Inline mode: omit `id` and render a plain contextual list.
1112
- *
1113
- * Customization via `ComponentContext`:
1114
- *
1115
- * - `ContextMenu`: replace the whole menu container/behavior.
1116
- * - `ContextMenuContent`: keep default container behavior but customize menu content
1117
- * rendering (items, submenu transitions, back navigation UI).
1118
- * - To customize outside-click dismissal via `ComponentContext`, provide a custom
1119
- * `ContextMenu` that sets `closeOnClickOutside`:
1120
- *
1121
- * ```tsx
1122
- * const CustomContextMenu = (props: ContextMenuProps) => (
1123
- * <ContextMenu {...props} closeOnClickOutside={false} />
1124
- * );
1125
- *
1126
- * <ComponentProvider value={{ ContextMenu: CustomContextMenu }}>
1127
- * <Chat client={client}>{children}</Chat>
1128
- * </ComponentProvider>
1129
- * ```
1130
- *
1131
- * Example:
1132
- * ```tsx
1133
- * <ComponentProvider
1134
- * value={{
1135
- * ContextMenu: MyContextMenu,
1136
- * ContextMenuContent: MyContextMenuContent,
1137
- * }}
1138
- * >
1139
- * <Chat client={client}>{children}</Chat>
1140
- * </ComponentProvider>
1141
- * ```
1142
- */
1143
- var ContextMenu = (props) => {
1144
- const { ContextMenuContent: ContextMenuContentComponent = ContextMenuContent } = useComponentContext();
1145
- const { allowFlip, closeOnClickOutside, closeTransitionMs = 130, dialogManagerId, focus, id, offset = 8, onMenuLevelChange: onMenuLevelChangeProp, placement, referenceElement, submenuTransitionDurationMs, tabIndex, trapFocus, ...menuProps } = props;
1146
- const resolvedSubmenuTransitionDurationMs = submenuTransitionDurationMs ?? 460;
1147
- const isAnchored = id != null;
1148
- const [menuLevel, setMenuLevel] = useState(1);
1149
- const [transitionDirection, setTransitionDirection] = useState(void 0);
1150
- const [contentResetToken, setContentResetToken] = useState(0);
1151
- const transitionTimeoutRef = useRef(null);
1152
- const previousMenuLevelRef = useRef(1);
1153
- const open = useDialogIsOpen(id ?? "", dialogManagerId);
1154
- const previousOpenRef = useRef(open);
1155
- useEffect(() => {
1156
- if (!isAnchored) return;
1157
- if (previousOpenRef.current && !open) {
1158
- setMenuLevel(1);
1159
- setTransitionDirection(void 0);
1160
- setContentResetToken((value) => value + 1);
1161
- previousMenuLevelRef.current = 1;
1162
- if (transitionTimeoutRef.current) {
1163
- clearTimeout(transitionTimeoutRef.current);
1164
- transitionTimeoutRef.current = null;
1165
- }
1166
- }
1167
- previousOpenRef.current = open;
1168
- }, [isAnchored, open]);
1169
- useEffect(() => () => {
1170
- if (transitionTimeoutRef.current) clearTimeout(transitionTimeoutRef.current);
1171
- }, []);
1172
- const handleMenuLevelChange = useCallback((level) => {
1173
- if (isAnchored) {
1174
- const previousLevel = previousMenuLevelRef.current;
1175
- if (level !== previousLevel) {
1176
- setTransitionDirection(level > previousLevel ? "forward" : "backward");
1177
- if (transitionTimeoutRef.current) clearTimeout(transitionTimeoutRef.current);
1178
- transitionTimeoutRef.current = setTimeout(() => {
1179
- setTransitionDirection(void 0);
1180
- transitionTimeoutRef.current = null;
1181
- }, resolvedSubmenuTransitionDurationMs);
1182
- }
1183
- previousMenuLevelRef.current = level;
1184
- setMenuLevel(level);
1185
- return;
1186
- }
1187
- onMenuLevelChangeProp?.(level);
1188
- }, [
1189
- isAnchored,
1190
- onMenuLevelChangeProp,
1191
- resolvedSubmenuTransitionDurationMs
1192
- ]);
1193
- const content = /* @__PURE__ */ createElement(ContextMenuContentComponent, {
1194
- anchorReferenceElement: isAnchored ? referenceElement : void 0,
1195
- ...menuProps,
1196
- key: `context-menu-content-${contentResetToken}`,
1197
- onMenuLevelChange: handleMenuLevelChange,
1198
- transitionDirection
1199
- });
1200
- if (isAnchored) {
1201
- const { ["aria-describedby"]: ariaDescribedBy, ["aria-label"]: ariaLabel, ["aria-labelledby"]: ariaLabelledBy, backButtonAriaLabel: _bbal, backLabel: _b, enableAnimations: _ea, Header: _h, items: _i, ItemsWrapper: _w, keyboardNavigation: _kn, menuClassName: _m, onClose: _c, role: _r, ...anchorDivProps } = menuProps;
1202
- const isSubmenuLevel = menuLevel > 1;
1203
- return /* @__PURE__ */ jsx(DialogAnchor, {
1204
- allowFlip,
1205
- "aria-describedby": isSubmenuLevel ? void 0 : ariaDescribedBy,
1206
- "aria-label": isSubmenuLevel ? void 0 : ariaLabel,
1207
- "aria-labelledby": isSubmenuLevel ? void 0 : ariaLabelledBy,
1208
- closeOnClickOutside,
1209
- closeTransitionMs,
1210
- dialogManagerId,
1211
- focus,
1212
- id,
1213
- offset,
1214
- placement,
1215
- referenceElement,
1216
- tabIndex,
1217
- trapFocus,
1218
- updateKey: menuLevel,
1219
- ...anchorDivProps,
1220
- children: content
1221
- });
1222
- }
1223
- return content;
1224
- };
1225
- //#endregion
1226
- //#region src/components/Dialog/components/Prompt.tsx
1227
- var PromptRoot = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
1228
- ...props,
1229
- className: clsx("str-chat__prompt", className),
1230
- children
1231
- });
1232
- var PromptHeader = ({ className, close, description, descriptionId, goBack, LeadingContent, title, titleId, TrailingContent }) => {
1233
- const { t } = useTranslationContext();
1234
- const { dialogId } = useModalContext();
1235
- const { descriptionId: derivedDescriptionId, titleId: derivedTitleId } = useAriaIdentifiers(dialogId);
1236
- const resolvedTitleId = titleId ?? derivedTitleId;
1237
- const resolvedDescriptionId = descriptionId ?? derivedDescriptionId;
1238
- const hasDescription = description != null && description !== "";
1239
- return /* @__PURE__ */ jsxs("div", {
1240
- className: clsx("str-chat__prompt__header", className, {
1241
- "str-chat__prompt__header--withDescription": hasDescription,
1242
- "str-chat__prompt__header--withGoBack": goBack
1243
- }),
1244
- children: [
1245
- LeadingContent && /* @__PURE__ */ jsx("div", {
1246
- className: "str-chat__prompt__header__leading-content",
1247
- children: /* @__PURE__ */ jsx(LeadingContent, {})
1248
- }),
1249
- /* @__PURE__ */ jsxs("div", {
1250
- className: clsx("str-chat__prompt__header__title-group"),
1251
- children: [
1252
- goBack && /* @__PURE__ */ jsx(Button, {
1253
- appearance: "ghost",
1254
- "aria-label": t("Go back"),
1255
- circular: true,
1256
- className: "str-chat__prompt__header__go-back-button",
1257
- onClick: goBack,
1258
- size: "md",
1259
- variant: "secondary",
1260
- children: /* @__PURE__ */ jsx(IconArrowLeft, {})
1261
- }),
1262
- /* @__PURE__ */ jsx("h2", {
1263
- className: "str-chat__prompt__header__title",
1264
- id: resolvedTitleId,
1265
- children: title
1266
- }),
1267
- hasDescription && /* @__PURE__ */ jsx("p", {
1268
- className: "str-chat__prompt__header__description",
1269
- id: resolvedDescriptionId,
1270
- children: description
1271
- })
1272
- ]
1273
- }),
1274
- (close || TrailingContent) && /* @__PURE__ */ jsxs("div", {
1275
- className: "str-chat__prompt__header__trailing-content",
1276
- children: [TrailingContent && /* @__PURE__ */ jsx(TrailingContent, {}), close && /* @__PURE__ */ jsx(Button, {
1277
- appearance: "ghost",
1278
- "aria-label": t("Close"),
1279
- circular: true,
1280
- className: "str-chat__prompt__header__close-button",
1281
- onClick: close,
1282
- size: "md",
1283
- variant: "secondary",
1284
- children: /* @__PURE__ */ jsx(IconXmark, {})
1285
- })]
1286
- })
1287
- ]
1288
- });
1289
- };
1290
- var PromptBody = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1291
- className: clsx("str-chat__prompt__body", className),
1292
- children
1293
- });
1294
- var PromptFooter = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1295
- className: clsx("str-chat__prompt__footer", className),
1296
- children
1297
- });
1298
- var PromptFooterControls = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1299
- className: clsx("str-chat__prompt__footer__controls", className),
1300
- children
1301
- });
1302
- var PromptFooterControlsButtonSecondary = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
1303
- appearance: "ghost",
1304
- className: clsx("str-chat__prompt__footer__controls-button", className),
1305
- size: "md",
1306
- variant: "secondary",
1307
- ...props
1308
- });
1309
- var PromptFooterControlsButtonPrimary = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
1310
- appearance: "solid",
1311
- className: clsx("str-chat__prompt__footer__controls-button", className),
1312
- size: "md",
1313
- variant: "primary",
1314
- ...props
1315
- });
1316
- var Prompt = {
1317
- Body: PromptBody,
1318
- Footer: PromptFooter,
1319
- FooterControls: PromptFooterControls,
1320
- FooterControlsButtonPrimary: PromptFooterControlsButtonPrimary,
1321
- FooterControlsButtonSecondary: PromptFooterControlsButtonSecondary,
1322
- Header: PromptHeader,
1323
- Root: PromptRoot
1324
- };
1325
- //#endregion
1326
- //#region src/a11y/hooks/useResolvedModalAriaProps.ts
1327
- /**
1328
- * Resolves modal labeling/description attributes from explicit props first,
1329
- * then from the modal dialog id convention (`${dialogId}-title|description`).
1330
- *
1331
- * Rules:
1332
- * - `aria-labelledby` wins over `aria-label`.
1333
- * - `aria-describedby` defaults to inferred id when explicit value is absent.
1334
- */
1335
- var useResolvedModalAriaProps = ({ ariaDescribedby, ariaLabel, ariaLabelledby, dialogId }) => {
1336
- const { descriptionId, titleId } = useAriaIdentifiers(dialogId);
1337
- return useMemo(() => {
1338
- const resolvedAriaLabelledby = ariaLabel ? ariaLabelledby : ariaLabelledby ?? titleId;
1339
- return {
1340
- "aria-describedby": ariaDescribedby ?? descriptionId,
1341
- "aria-label": resolvedAriaLabelledby ? void 0 : ariaLabel,
1342
- "aria-labelledby": resolvedAriaLabelledby
1343
- };
1344
- }, [
1345
- ariaDescribedby,
1346
- ariaLabel,
1347
- ariaLabelledby,
1348
- descriptionId,
1349
- titleId
1350
- ]);
1351
- };
1352
- //#endregion
1353
- //#region src/components/Modal/GlobalModal.tsx
1354
- var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className, CloseButtonOnOverlay, dialogId, dialogRootProps, getInitialFocusElement, initialFocusStrategy, onClose, onCloseAttempt, open, role = "dialog" }) => {
1355
- const generatedDialogId = useStableId();
1356
- const resolvedDialogId = dialogId ?? `modal-dialog-${generatedDialogId}`;
1357
- const dialog = useModalDialog(resolvedDialogId);
1358
- const isOpen = useModalDialogIsOpen(resolvedDialogId);
1359
- const isTopmost = useModalDialogIsTopmost(resolvedDialogId);
1360
- const overlayRef = useRef(null);
1361
- const dialogRef = useRef(null);
1362
- const closeButtonRef = useRef(null);
1363
- const closingRef = useRef(false);
1364
- const { theme } = useChatContext();
1365
- const { NotificationList: NotificationList$1 = NotificationList } = useComponentContext();
1366
- const { className: dialogRootClassName, onKeyDown: dialogRootOnKeyDown, ...dialogRootPropsRest } = dialogRootProps ?? {};
1367
- const dialogLabelingBaseId = resolvedDialogId;
1368
- const resolvedInitialFocusStrategy = initialFocusStrategy ?? "firstElement";
1369
- const resolvedModalAriaProps = useResolvedModalAriaProps({
1370
- ariaDescribedby,
1371
- ariaLabel,
1372
- ariaLabelledby,
1373
- dialogId: dialogLabelingBaseId
1374
- });
1375
- const maybeClose = useCallback((source, event) => {
1376
- if (onCloseAttempt?.(source, event) !== false) {
1377
- dialog.close();
1378
- closingRef.current = true;
1379
- onClose?.(event);
1380
- }
1381
- }, [
1382
- dialog,
1383
- onClose,
1384
- onCloseAttempt
1385
- ]);
1386
- const modalContextValue = useMemo(() => ({
1387
- close: () => maybeClose("button", {}),
1388
- dialogId: dialogLabelingBaseId
1389
- }), [dialogLabelingBaseId, maybeClose]);
1390
- const handleOverlayClick = (event) => {
1391
- if (!isTopmost) return;
1392
- const target = event.target;
1393
- if (overlayRef.current === target) maybeClose("overlay", event);
1394
- };
1395
- const handleCloseButtonClick = (event) => {
1396
- if (!isTopmost) return;
1397
- maybeClose("button", event);
1398
- };
1399
- const handleDialogKeyDown = (event) => {
1400
- dialogRootOnKeyDown?.(event);
1401
- if (event.defaultPrevented || !isTopmost) return;
1402
- if (event.key === "Escape") {
1403
- maybeClose("escape", event);
1404
- return;
1405
- }
1406
- if (event.key === "Enter" && event.target === dialogRef.current) {
1407
- const target = dialogRef.current?.querySelector("[data-autofocus]");
1408
- if (target) {
1409
- event.preventDefault();
1410
- target.focus();
1411
- }
1412
- }
1413
- };
1414
- useEffect(() => {
1415
- if (!open) {
1416
- closingRef.current = false;
1417
- if (isOpen) dialog.close();
1418
- return;
1419
- }
1420
- if (open && !isOpen && !closingRef.current) dialog.open();
1421
- }, [
1422
- dialog,
1423
- isOpen,
1424
- open
1425
- ]);
1426
- useEffect(() => {
1427
- if (!open || !isOpen || !isTopmost || !getInitialFocusElement && resolvedInitialFocusStrategy === "firstElement") return;
1428
- const frame = requestAnimationFrame(() => {
1429
- const dialogElement = dialogRef.current;
1430
- if (!dialogElement) return;
1431
- (getInitialFocusElement?.(dialogElement) ?? dialogElement).focus();
1432
- });
1433
- return () => cancelAnimationFrame(frame);
1434
- }, [
1435
- getInitialFocusElement,
1436
- isOpen,
1437
- isTopmost,
1438
- open,
1439
- resolvedInitialFocusStrategy
1440
- ]);
1441
- if (!open || !isOpen) return null;
1442
- return /* @__PURE__ */ jsx(DialogPortalEntry, {
1443
- dialogId: resolvedDialogId,
1444
- dialogManagerId: modalDialogManagerId,
1445
- children: /* @__PURE__ */ jsx(ModalContextProvider, {
1446
- value: modalContextValue,
1447
- children: /* @__PURE__ */ jsxs("div", {
1448
- className: clsx("str-chat", theme, "str-chat__modal str-chat-react__modal str-chat__modal--open", className),
1449
- onClick: handleOverlayClick,
1450
- ref: overlayRef,
1451
- children: [
1452
- /* @__PURE__ */ jsx(FocusScope, {
1453
- autoFocus: isTopmost && !getInitialFocusElement && resolvedInitialFocusStrategy === "firstElement",
1454
- contain: isTopmost,
1455
- restoreFocus: true,
1456
- children: /* @__PURE__ */ jsx("div", {
1457
- ...dialogRootPropsRest,
1458
- "aria-describedby": resolvedModalAriaProps["aria-describedby"],
1459
- "aria-label": resolvedModalAriaProps["aria-label"],
1460
- "aria-labelledby": resolvedModalAriaProps["aria-labelledby"],
1461
- "aria-modal": isTopmost ? "true" : void 0,
1462
- className: clsx("str-chat__modal__dialog", dialogRootClassName),
1463
- inert: isTopmost ? void 0 : true,
1464
- onKeyDown: handleDialogKeyDown,
1465
- ref: dialogRef,
1466
- role,
1467
- tabIndex: isTopmost ? 0 : -1,
1468
- children: /* @__PURE__ */ jsxs(DialogManagerProvider, {
1469
- id: `${resolvedDialogId}-floating-dialog-manager`,
1470
- portalDestinationProps: {
1471
- captureOutsideClicks: true,
1472
- className: "str-chat__modal__floating-dialog-overlay"
1473
- },
1474
- children: [children, isTopmost && /* @__PURE__ */ jsx(AriaLiveOutlet, { layer: 1 })]
1475
- })
1476
- })
1477
- }),
1478
- /* @__PURE__ */ jsx(NotificationList$1, {
1479
- className: "str-chat__modal__notification-list",
1480
- panel: "modal",
1481
- verticalAlignment: "top"
1482
- }),
1483
- CloseButtonOnOverlay && /* @__PURE__ */ jsx(CloseButtonOnOverlay, {
1484
- onClick: handleCloseButtonClick,
1485
- ref: closeButtonRef
1486
- })
1487
- ]
1488
- })
1489
- })
1490
- });
1491
- };
1492
- //#endregion
1493
- //#region src/components/Avatar/utils.ts
1494
- var extractDisplayInfo = ({ user }) => ({
1495
- id: user?.id,
1496
- imageUrl: user?.image,
1497
- userName: user?.name
1498
- });
1499
- //#endregion
1500
- //#region src/components/Message/renderText/remarkPlugins/htmlToTextPlugin.ts
1501
- var visitor = (node) => {
1502
- if (node.type !== "html") return;
1503
- node.type = "text";
1504
- };
1505
- var transform = (tree) => {
1506
- visit(tree, visitor);
1507
- };
1508
- var htmlToTextPlugin = () => transform;
1509
- //#endregion
1510
- //#region src/components/Message/renderText/remarkPlugins/imageToLink.ts
1511
- var text = (value) => ({
1512
- type: "text",
1513
- value
1514
- });
1515
- /**
1516
- * Converts image Markdown links (![Minion](https://octodex.github.com/images/minion.png))
1517
- * to HTML <a href={url}>{url | title | alt}</a>
1518
- *
1519
- * By default, the anchor text content is the image url so that image preview can be generated / enriched on the server.
1520
- * @param getTextLabelFrom
1521
- */
1522
- function imageToLink({ getTextLabelFrom = "url" } = {}) {
1523
- return (tree) => {
1524
- const visitor = (node, index, parent) => {
1525
- if (parent == null || index == null) return;
1526
- const link = {
1527
- children: [text(node[getTextLabelFrom] ?? node.url)],
1528
- title: node.title ?? node.alt ?? node.url,
1529
- type: "link",
1530
- url: node.url
1531
- };
1532
- parent.children.splice(index, 1, link);
1533
- return [SKIP, index + 1];
1534
- };
1535
- visit(tree, "image", visitor);
1536
- };
1537
- }
1538
- //#endregion
1539
- //#region src/components/Message/renderText/remarkPlugins/plusPlusToEmphasis.ts
1540
- /**
1541
- * \S → first char must be non-whitespace
1542
- * (?:...)?→ optional middle+closing when length > 1
1543
- * [\s\S]*?→ anything (including newlines), lazy
1544
- * final \S→ last char non-whitespace (only required when there’s more than 1)
1545
- *
1546
- * Matches:
1547
- * ++a++
1548
- * Does not match:
1549
- * ++++
1550
- * ++ ++
1551
- */
1552
- var INS_REGEX = /\+\+(\S(?:[\s\S]*?\S)?)\+\+/g;
1553
- var IGNORE_NODE_TYPES = /* @__PURE__ */ new Set([
1554
- "code",
1555
- "inlineCode",
1556
- "link",
1557
- "linkReference",
1558
- "definition",
1559
- "math",
1560
- "inlineMath"
1561
- ]);
1562
- /**
1563
- * Converts MD "++Some text++" to inserted text element rendered in HTML as <ins>Some text</ins>
1564
- * https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ins
1565
- */
1566
- var plusPlusToEmphasis = () => {
1567
- const visitor = (node, index, parent) => {
1568
- if (IGNORE_NODE_TYPES.has(node.type)) return SKIP;
1569
- if (node.type !== "text" || parent == null || typeof index !== "number") return;
1570
- const value = node.value;
1571
- INS_REGEX.lastIndex = 0;
1572
- let match;
1573
- let last = 0;
1574
- const out = [];
1575
- while (match = INS_REGEX.exec(value)) {
1576
- const [full, inner] = match;
1577
- const start = match.index;
1578
- if (start > last) out.push({
1579
- type: "text",
1580
- value: value.slice(last, start)
1581
- });
1582
- out.push({
1583
- children: [{
1584
- type: "text",
1585
- value: inner
1586
- }],
1587
- data: { hName: "ins" },
1588
- type: "emphasis"
1589
- });
1590
- last = start + full.length;
1591
- }
1592
- if (out.length === 0) return;
1593
- if (last < value.length) out.push({
1594
- type: "text",
1595
- value: value.slice(last)
1596
- });
1597
- parent.children.splice(index, 1, ...out);
1598
- return [SKIP, index + out.length];
1599
- };
1600
- return (tree) => visit(tree, visitor);
1601
- };
1602
- //#endregion
1603
- //#region src/components/Message/Timestamp.tsx
1604
- function Timestamp(props) {
1605
- const { calendar, calendarFormats, customClass, format, timestamp } = props;
1606
- const { formatDate } = useMessageContext("MessageTimestamp");
1607
- const { t, tDateTimeParser } = useTranslationContext("MessageTimestamp");
1608
- const normalizedTimestamp = timestamp && isDate(timestamp) ? timestamp.toISOString() : timestamp;
1609
- const when = useMemo(() => getDateString({
1610
- calendar,
1611
- calendarFormats,
1612
- format,
1613
- formatDate,
1614
- messageCreatedAt: normalizedTimestamp,
1615
- t,
1616
- tDateTimeParser,
1617
- timestampTranslationKey: "timestamp/MessageTimestamp"
1618
- }), [
1619
- calendar,
1620
- calendarFormats,
1621
- format,
1622
- formatDate,
1623
- normalizedTimestamp,
1624
- t,
1625
- tDateTimeParser
1626
- ]);
1627
- if (!when) return null;
1628
- return /* @__PURE__ */ jsx("time", {
1629
- className: customClass,
1630
- dateTime: normalizedTimestamp,
1631
- title: normalizedTimestamp,
1632
- children: when
1633
- });
1634
- }
1635
- //#endregion
1636
- //#region src/components/Message/MessageTimestamp.tsx
1637
- var UnMemoizedMessageTimestamp = (props) => {
1638
- const { message: propMessage, ...timestampProps } = props;
1639
- const { message: contextMessage } = useMessageContext("MessageTimestamp");
1640
- const { Timestamp: Timestamp$1 = Timestamp } = useComponentContext("MessageTimestamp");
1641
- return /* @__PURE__ */ jsx(Timestamp$1, {
1642
- timestamp: (propMessage || contextMessage).created_at,
1643
- ...timestampProps
1644
- });
1645
- };
1646
- var MessageTimestamp = React.memo(UnMemoizedMessageTimestamp);
1647
- //#endregion
1648
- //#region src/components/Attachment/components/DownloadButton.tsx
1649
- /**
1650
- * Icon download control for {@link Audio} and {@link FileAttachment} rows.
1651
- * (BaseImage defines its own small download link when `showDownloadButtonOnError` is used.)
1652
- */
1653
- var DownloadButton = ({ assetUrl, className, suggestedFileName, tooltipTitle }) => {
1654
- const { t } = useTranslationContext();
1655
- if (!assetUrl) return null;
1656
- const href = sanitizeUrl(assetUrl);
1657
- if (!href) return null;
1658
- return /* @__PURE__ */ jsx("a", {
1659
- "aria-label": t("aria/Download attachment"),
1660
- className: clsx("str-chat__button", "str-chat__button--secondary", "str-chat__button--outline", "str-chat__button--circular", "str-chat__button--size-sm", "str-chat__audio-attachment-download-button", className),
1661
- download: suggestedFileName ?? "",
1662
- href,
1663
- rel: "noopener noreferrer",
1664
- target: "_blank",
1665
- title: tooltipTitle ?? t("Download Attachment"),
1666
- children: /* @__PURE__ */ jsx("div", {
1667
- className: "str-chat__button__content",
1668
- children: /* @__PURE__ */ jsx(IconDownload, { className: "str-chat__icon str-chat__audio-attachment-download-button__icon" })
1669
- })
1670
- });
1671
- };
1672
- //#endregion
1673
- //#region src/components/FileIcon/FileIconSet.tsx
1674
- var BASE_FILE_ICON_CLASSNAME = "str-chat__file-icon";
1675
- var FILE_ICON_GRAPHIC_CLASSNAME = "str-chat__file-icon__graphic";
1676
- /** Add this class (e.g. via className) when hiding the label with CSS to center the icon graphic. */
1677
- var FILE_ICON_NO_LABEL_CLASSNAME = "str-chat__file-icon--no-label";
1678
- /** Rendered dimensions (px) and label position in viewBox coords for consistent spacing. */
1679
- var FILE_ICON_SIZE_CONFIG = {
1680
- lg: {
1681
- height: 40,
1682
- labelX: 16,
1683
- labelY: 36,
1684
- width: 32
1685
- },
1686
- md: {
1687
- height: 32,
1688
- labelX: 16,
1689
- labelY: 35,
1690
- width: 26
1691
- },
1692
- sm: {
1693
- height: 24,
1694
- labelX: 16,
1695
- labelY: 31.5,
1696
- width: 19
1697
- },
1698
- xl: {
1699
- height: 48,
1700
- labelX: 16,
1701
- labelY: 36,
1702
- width: 40
1703
- }
1704
- };
1705
- /** Merge partial overrides with default config. Use for Chat-level fileIconSizeConfig. */
1706
- var mergeFileIconSizeConfig = (overrides) => {
1707
- if (!overrides) return FILE_ICON_SIZE_CONFIG;
1708
- return [
1709
- "sm",
1710
- "md",
1711
- "lg",
1712
- "xl"
1713
- ].reduce((acc, size) => ({
1714
- ...acc,
1715
- [size]: {
1716
- ...FILE_ICON_SIZE_CONFIG[size],
1717
- ...overrides[size]
1718
- }
1719
- }), {});
1720
- };
1721
- var FILE_ICON_VIEWBOX = {
1722
- height: 40,
1723
- width: 32
1724
- };
1725
- var FILE_ICON_PAPER_PATH = "M0 4C0 1.79086 1.79086 0 4 0H22.4L32 10V36C32 38.2091 30.2091 40 28 40H4C1.79086 40 0 38.2091 0 36V4Z";
1726
- var FILE_ICON_FOLD_PATH = "M32 10H25.4C23.7431 10 22.4 8.65685 22.4 7V0L32 10Z";
1727
- var FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X = 32 / 26;
1728
- var FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y = 40 / 32;
1729
- var Svg = ({ className, size, sizeConfig, ...props }) => {
1730
- const dimensions = size ? (sizeConfig ?? FILE_ICON_SIZE_CONFIG)[size] : void 0;
1731
- const dimensionsStyle = dimensions ? {
1732
- flexShrink: 0,
1733
- height: `${dimensions.height}px`,
1734
- width: `${dimensions.width}px`
1735
- } : void 0;
1736
- return /* @__PURE__ */ jsx("svg", {
1737
- height: dimensions?.height,
1738
- viewBox: `0 0 ${FILE_ICON_VIEWBOX.width} ${FILE_ICON_VIEWBOX.height}`,
1739
- width: dimensions?.width,
1740
- xmlns: "http://www.w3.org/2000/svg",
1741
- ...props,
1742
- className: clsx(BASE_FILE_ICON_CLASSNAME, { [`${BASE_FILE_ICON_CLASSNAME}--size-${size}`]: size }, className),
1743
- style: {
1744
- ...dimensionsStyle,
1745
- ...props.style
1746
- }
1747
- });
1748
- };
1749
- var FileIconLabel = ({ label, size, sizeConfig }) => {
1750
- const config = size ? (sizeConfig ?? FILE_ICON_SIZE_CONFIG)[size] : {
1751
- labelX: 16,
1752
- labelY: 33
1753
- };
1754
- return /* @__PURE__ */ jsx("text", {
1755
- className: "str-chat__file-icon__label",
1756
- x: config.labelX,
1757
- y: config.labelY,
1758
- children: label
1759
- });
1760
- };
1761
- var TEXT_SYMBOLS = {
1762
- withCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1763
- /* @__PURE__ */ jsx("rect", {
1764
- fill: "white",
1765
- height: "1.6",
1766
- rx: "0.8",
1767
- width: "14.4",
1768
- x: "8",
1769
- y: "12.2"
1770
- }),
1771
- /* @__PURE__ */ jsx("rect", {
1772
- fill: "white",
1773
- height: "1.6",
1774
- rx: "0.8",
1775
- width: "14.4",
1776
- x: "8",
1777
- y: "20.2"
1778
- }),
1779
- /* @__PURE__ */ jsx("rect", {
1780
- fill: "white",
1781
- height: "1.6",
1782
- rx: "0.8",
1783
- width: "9.6",
1784
- x: "8",
1785
- y: "16.2"
1786
- })
1787
- ] }),
1788
- withoutCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1789
- /* @__PURE__ */ jsx("rect", {
1790
- fill: "white",
1791
- height: "1.4",
1792
- rx: "0.7",
1793
- width: "12.6",
1794
- x: "6",
1795
- y: "14.8"
1796
- }),
1797
- /* @__PURE__ */ jsx("rect", {
1798
- fill: "white",
1799
- height: "1.4",
1800
- rx: "0.7",
1801
- width: "12.6",
1802
- x: "6",
1803
- y: "21.8"
1804
- }),
1805
- /* @__PURE__ */ jsx("rect", {
1806
- fill: "white",
1807
- height: "1.4",
1808
- rx: "0.7",
1809
- width: "8.4",
1810
- x: "6",
1811
- y: "18.3"
1812
- })
1813
- ] })
1814
- };
1815
- var OTHER_SYMBOLS = {
1816
- withCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1817
- /* @__PURE__ */ jsx("rect", {
1818
- fill: "white",
1819
- height: "1.6",
1820
- rx: "0.8",
1821
- width: "14.4",
1822
- x: "8",
1823
- y: "13.2"
1824
- }),
1825
- /* @__PURE__ */ jsx("rect", {
1826
- fill: "white",
1827
- height: "1.6",
1828
- rx: "0.8",
1829
- width: "14.4",
1830
- x: "8",
1831
- y: "21.2"
1832
- }),
1833
- /* @__PURE__ */ jsx("rect", {
1834
- fill: "white",
1835
- height: "1.6",
1836
- rx: "0.8",
1837
- width: "9.6",
1838
- x: "8",
1839
- y: "17.2"
1840
- })
1841
- ] }),
1842
- withoutCaption: TEXT_SYMBOLS.withoutCaption
1843
- };
1844
- var CODE_SYMBOLS = {
1845
- withCaption: /* @__PURE__ */ jsx("path", {
1846
- d: "M14.5 22.5L17.5 11.5M20.1666 14.1667L21.9732 16.0862C22.4564 16.5996 22.4564 17.4004 21.9732 17.9138L20.1666 19.8333M11.8333 19.8333L10.0267 17.9138C9.54351 17.4004 9.54351 16.5996 10.0267 16.0862L11.8333 14.1667",
1847
- stroke: "white",
1848
- strokeLinecap: "round",
1849
- strokeLinejoin: "round",
1850
- strokeWidth: "1.2"
1851
- }),
1852
- withoutCaption: /* @__PURE__ */ jsx("path", {
1853
- d: "M11.6876 23.8125L14.3126 14.1875M16.646 16.5208L18.2267 18.2004C18.6495 18.6496 18.6495 19.3503 18.2267 19.7996L16.646 21.4792M9.3543 21.4792L7.77352 19.7996C7.35072 19.3503 7.35072 18.6496 7.77352 18.2004L9.3543 16.5208",
1854
- stroke: "white",
1855
- strokeLinecap: "round",
1856
- strokeLinejoin: "round",
1857
- strokeWidth: "1.33333"
1858
- })
1859
- };
1860
- var AUDIO_SYMBOLS = {
1861
- withCaption: /* @__PURE__ */ jsx("path", {
1862
- d: "M20.5 15.5V18.5M22.5 14.5V19.5M13.5 19.5H10.5C10.3674 19.5 10.2402 19.4473 10.1464 19.3536C10.0527 19.2598 10 19.1326 10 19V15C10 14.8674 10.0527 14.7402 10.1464 14.6464C10.2402 14.5527 10.3674 14.5 10.5 14.5H13.5L18 11V23L13.5 19.5Z",
1863
- stroke: "white",
1864
- strokeLinecap: "round",
1865
- strokeLinejoin: "round",
1866
- strokeWidth: "1.2"
1867
- }),
1868
- withoutCaption: /* @__PURE__ */ jsx("path", {
1869
- d: "M17.5 17.5V20.5M19.5 16.5V21.5M10.5 21.5H7.5C7.36739 21.5 7.24021 21.4473 7.14645 21.3536C7.05268 21.2598 7 21.1326 7 21V17C7 16.8674 7.05268 16.7402 7.14645 16.6464C7.24021 16.5527 7.36739 16.5 7.5 16.5H10.5L15 13V25L10.5 21.5Z",
1870
- stroke: "white",
1871
- strokeLinecap: "round",
1872
- strokeLinejoin: "round",
1873
- strokeWidth: "1.33333"
1874
- })
1875
- };
1876
- var PRESENTATION_SYMBOLS = {
1877
- withCaption: /* @__PURE__ */ jsx("path", {
1878
- d: "M12.5 15.5H19.5M16 17.5V22.5M14 22.5H18M9.49999 17.5C9.41472 17.5001 9.33085 17.4783 9.25635 17.4368C9.18185 17.3953 9.1192 17.3355 9.07436 17.263C9.02952 17.1904 9.00397 17.1076 9.00014 17.0224C8.99631 16.9373 9.01433 16.8525 9.05249 16.7762L11.5525 11.7763C11.5941 11.6932 11.6579 11.6233 11.737 11.5745C11.816 11.5257 11.9071 11.4999 12 11.5H20C20.0929 11.4999 20.184 11.5257 20.263 11.5745C20.342 11.6233 20.4059 11.6932 20.4475 11.7763L22.9475 16.7762C22.9857 16.8525 23.0037 16.9373 22.9998 17.0224C22.996 17.1076 22.9705 17.1904 22.9256 17.263C22.8808 17.3355 22.8181 17.3953 22.7436 17.4368C22.6691 17.4783 22.5853 17.5001 22.5 17.5H9.49999Z",
1879
- stroke: "white",
1880
- strokeLinecap: "round",
1881
- strokeLinejoin: "round",
1882
- strokeWidth: "1.2"
1883
- }),
1884
- withoutCaption: /* @__PURE__ */ jsx("path", {
1885
- d: "M9.49999 17.5H16.5M13 19.5V24.5M11 24.5H15M6.49999 19.5C6.41472 19.5001 6.33085 19.4783 6.25635 19.4368C6.18185 19.3953 6.11921 19.3355 6.07436 19.263C6.02952 19.1904 6.00397 19.1076 6.00014 19.0224C5.99631 18.9373 6.01433 18.8525 6.05249 18.7763L8.55249 13.7763C8.59406 13.6932 8.65795 13.6233 8.73699 13.5745C8.81603 13.5257 8.9071 13.4999 8.99999 13.5H17C17.0929 13.4999 17.184 13.5257 17.263 13.5745C17.342 13.6233 17.4059 13.6932 17.4475 13.7763L19.9475 18.7763C19.9857 18.8525 20.0037 18.9373 19.9998 19.0224C19.996 19.1076 19.9705 19.1904 19.9256 19.263C19.8808 19.3355 19.8181 19.3953 19.7436 19.4368C19.6691 19.4783 19.5853 19.5001 19.5 19.5H6.49999Z",
1886
- stroke: "white",
1887
- strokeLinecap: "round",
1888
- strokeLinejoin: "round",
1889
- strokeWidth: "1.33333"
1890
- })
1891
- };
1892
- var SPREADSHEET_SYMBOLS = {
1893
- withCaption: /* @__PURE__ */ jsx("path", {
1894
- d: "M10 15.5H22M10 18.5H22M13.5 15.5V21.5M10 12.5H22V21C22 21.1326 21.9473 21.2598 21.8536 21.3536C21.7598 21.4473 21.6326 21.5 21.5 21.5H10.5C10.3674 21.5 10.2402 21.4473 10.1464 21.3536C10.0527 21.2598 10 21.1326 10 21V12.5Z",
1895
- stroke: "white",
1896
- strokeLinecap: "round",
1897
- strokeLinejoin: "round",
1898
- strokeWidth: "1.5"
1899
- }),
1900
- withoutCaption: /* @__PURE__ */ jsx("path", {
1901
- d: "M7 17.5H19M7 20.5H19M10.5 17.5V23.5M7 14.5H19V23C19 23.1326 18.9473 23.2598 18.8536 23.3536C18.7598 23.4473 18.6326 23.5 18.5 23.5H7.5C7.36739 23.5 7.24021 23.4473 7.14645 23.3536C7.05268 23.2598 7 23.1326 7 23V14.5Z",
1902
- stroke: "white",
1903
- strokeLinecap: "round",
1904
- strokeLinejoin: "round",
1905
- strokeWidth: "1.2"
1906
- })
1907
- };
1908
- var COMPRESSION_SYMBOLS = {
1909
- withCaption: /* @__PURE__ */ jsx("path", {
1910
- clipRule: "evenodd",
1911
- d: "M9.41177 0H7.52942V2H9.41177V4H7.52942V6H9.41177V8H7.52942V10H9.41177V12H7.52942V14H9.41177V12H11.2941V10H9.41177V8H11.2941V6H9.41177V4H11.2941V2H9.41177V0ZM7.52942 17C7.52942 16.4477 7.9508 16 8.4706 16H10.3529C10.8727 16 11.2941 16.4477 11.2941 17V23C11.2941 23.5523 10.8727 24 10.3529 24H8.4706C7.9508 24 7.52942 23.5523 7.52942 23V17ZM8.4706 23V20H10.3529V23H8.4706Z",
1912
- fill: "white",
1913
- fillRule: "evenodd"
1914
- }),
1915
- withoutCaption: /* @__PURE__ */ jsx("path", {
1916
- clipRule: "evenodd",
1917
- d: "M8.17031 0H6.11768V2.14737H8.17031V4.29474H6.11768V6.4421H8.17031V8.58947H6.11768V10.7368H8.17031V12.8842H6.11768V15.0316H8.17031V12.8842H10.2229V10.7368H8.17031V8.58947H10.2229V6.4421H8.17031V4.29474H10.2229V2.14737H8.17031V0ZM6.11768 18.2526C6.11768 17.6597 6.57717 17.1789 7.14399 17.1789H9.19662C9.76344 17.1789 10.2229 17.6597 10.2229 18.2526V24.6947C10.2229 25.2877 9.76344 25.7684 9.19662 25.7684H7.14399C6.57717 25.7684 6.11768 25.2877 6.11768 24.6947V18.2526ZM7.14399 24.6947V21.4737H9.19662V24.6947H7.14399Z",
1918
- fill: "white",
1919
- fillRule: "evenodd"
1920
- })
1921
- };
1922
- var VIDEO_SYMBOLS = {
1923
- withCaption: /* @__PURE__ */ jsx("path", {
1924
- d: "M20.5 16L23.5 14V20L20.5 18M10 13H20C20.2761 13 20.5 13.2239 20.5 13.5V20.5C20.5 20.7761 20.2761 21 20 21H10C9.72386 21 9.5 20.7761 9.5 20.5V13.5C9.5 13.2239 9.72386 13 10 13Z",
1925
- stroke: "white",
1926
- strokeLinecap: "round",
1927
- strokeLinejoin: "round",
1928
- strokeWidth: "1.2"
1929
- }),
1930
- withoutCaption: /* @__PURE__ */ jsx("path", {
1931
- d: "M17.5001 18L20.5001 16V22L17.5001 20M7.00012 15H17.0001C17.2763 15 17.5001 15.2239 17.5001 15.5V22.5C17.5001 22.7761 17.2763 23 17.0001 23H7.00012C6.72398 23 6.50012 22.7761 6.50012 22.5V15.5C6.50012 15.2239 6.72398 15 7.00012 15Z",
1932
- stroke: "white",
1933
- strokeLinecap: "round",
1934
- strokeLinejoin: "round",
1935
- strokeWidth: "1.33333"
1936
- })
1937
- };
1938
- var StandardFileTypeIcon = ({ className, color, fileTypeClassName, label, size, sizeConfig, symbols, ...props }) => {
1939
- const renderLabel = !!label;
1940
- const resolvedLabel = renderLabel ? label : void 0;
1941
- const symbolVariant = renderLabel ? "withCaption" : "withoutCaption";
1942
- return /* @__PURE__ */ jsxs(Svg, {
1943
- ...props,
1944
- className: clsx(fileTypeClassName, className),
1945
- size,
1946
- sizeConfig,
1947
- children: [/* @__PURE__ */ jsxs("g", {
1948
- className: FILE_ICON_GRAPHIC_CLASSNAME,
1949
- children: [
1950
- /* @__PURE__ */ jsx("path", {
1951
- d: FILE_ICON_PAPER_PATH,
1952
- fill: color
1953
- }),
1954
- renderLabel ? symbols[symbolVariant] : /* @__PURE__ */ jsx("g", {
1955
- transform: `scale(${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X} ${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y})`,
1956
- children: symbols[symbolVariant]
1957
- }),
1958
- /* @__PURE__ */ jsx("path", {
1959
- d: FILE_ICON_FOLD_PATH,
1960
- fill: "white",
1961
- opacity: "0.5"
1962
- })
1963
- ]
1964
- }), resolvedLabel && /* @__PURE__ */ jsx(FileIconLabel, {
1965
- label: resolvedLabel,
1966
- size,
1967
- sizeConfig
1968
- })]
1969
- });
1970
- };
1971
- var PDF_SMALL_SYMBOL = /* @__PURE__ */ jsx("path", {
1972
- d: "M20.7533 19.5337C20.28 19.037 19.3093 18.7537 17.9373 18.7537C17.204 18.7537 16.3526 18.8244 15.43 18.9897C14.8647 18.4461 14.3499 17.8523 13.892 17.2157C13.5373 16.7424 13.2293 16.2224 12.9453 15.725C13.49 14.069 13.75 12.7204 13.75 11.7504C13.75 10.6624 13.348 9.52637 12.1886 9.52637C11.834 9.52637 11.4786 9.7397 11.2893 10.047C10.7693 10.9697 11.006 12.9804 11.9046 14.9677C11.5664 15.984 11.1876 16.9863 10.7693 17.9724C10.3906 18.8717 9.96465 19.7944 9.49131 20.6457C6.88931 21.687 5.20931 22.8937 5.01998 23.839C4.94931 24.1944 5.06731 24.5257 5.32798 24.7857C5.42265 24.857 5.75398 25.141 6.32131 25.141C8.04798 25.141 9.86998 22.349 10.7926 20.6697C11.5026 20.433 12.2126 20.1964 12.922 20.007C13.6704 19.8038 14.4284 19.638 15.1933 19.5104C17.0146 21.1424 18.6233 21.403 19.428 21.403C20.4213 21.403 20.7766 21.0004 20.8946 20.6697C21.108 20.243 20.966 19.7704 20.7533 19.5337ZM19.8066 20.2204C19.7353 20.575 19.38 20.8117 18.884 20.8117C18.742 20.8117 18.624 20.7877 18.4813 20.7644C17.5826 20.551 16.7306 20.1017 15.8793 19.3917C16.5357 19.2807 17.2003 19.2254 17.866 19.2264C18.3633 19.2264 18.7893 19.2497 19.0726 19.321C19.404 19.3917 19.9246 19.605 19.806 20.2197L19.8066 20.2204ZM14.7906 19.1084C14.1305 19.2321 13.4755 19.382 12.8273 19.5577C12.262 19.7047 11.7017 19.8703 11.1473 20.0544C11.4355 19.4962 11.7039 18.9281 11.952 18.351C12.236 17.6884 12.472 17.0024 12.7093 16.3637C12.9453 16.7657 13.206 17.1684 13.466 17.523C13.8911 18.065 14.3329 18.5937 14.7906 19.1084ZM11.692 10.307C11.7338 10.2232 11.7978 10.1525 11.877 10.1025C11.9563 10.0526 12.0477 10.0253 12.1413 10.0237C12.638 10.0237 12.7326 10.591 12.7326 11.041C12.7326 11.7977 12.496 12.957 12.094 14.2817C11.4073 12.4364 11.3606 10.899 11.692 10.307ZM9.08931 21.3317C7.88265 23.319 6.72331 24.549 6.01398 24.549C5.88599 24.5498 5.76132 24.5083 5.65931 24.431C5.51731 24.289 5.44598 24.1237 5.49331 23.9344C5.63531 23.2244 7.00731 22.231 9.08931 21.3317Z",
1973
- fill: "white"
1974
- });
1975
- var PDF_LEGACY_SYMBOL = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("path", {
1976
- d: "M23.7533 19.2C23.28 18.7033 22.3093 18.42 20.9373 18.42C20.204 18.42 19.3526 18.4906 18.43 18.656C17.8647 18.1124 17.3499 17.5186 16.892 16.882C16.5373 16.4086 16.2293 15.8886 15.9453 15.3913C16.49 13.7353 16.75 12.3866 16.75 11.4166C16.75 10.3286 16.348 9.19263 15.1886 9.19263C14.834 9.19263 14.4786 9.40596 14.2893 9.71329C13.7693 10.636 14.006 12.6466 14.9046 14.634C14.5664 15.6502 14.1877 16.6526 13.7693 17.6386C13.3906 18.538 12.9646 19.4606 12.4913 20.312C9.88931 21.3533 8.20931 22.56 8.01998 23.5053C7.94931 23.8606 8.06731 24.192 8.32798 24.452C8.42265 24.5233 8.75398 24.8073 9.32131 24.8073C11.048 24.8073 12.87 22.0153 13.7926 20.336C14.5026 20.0993 15.2126 19.8626 15.922 19.6733C16.6704 19.4701 17.4284 19.3043 18.1933 19.1766C20.0146 20.8086 21.6233 21.0693 22.428 21.0693C23.4213 21.0693 23.7766 20.6666 23.8946 20.336C24.108 19.9093 23.966 19.4366 23.7533 19.2ZM22.8066 19.8866C22.7353 20.2413 22.38 20.478 21.884 20.478C21.742 20.478 21.624 20.454 21.4813 20.4306C20.5826 20.2173 19.7306 19.768 18.8793 19.058C19.5357 18.947 20.2003 18.8917 20.866 18.8926C21.3633 18.8926 21.7893 18.916 22.0726 18.9873C22.404 19.058 22.9246 19.2713 22.806 19.886L22.8066 19.8866ZM17.7906 18.7746C17.1305 18.8983 16.4755 19.0482 15.8273 19.224C15.262 19.3709 14.7017 19.5366 14.1473 19.7206C14.4355 19.1625 14.7039 18.5944 14.952 18.0173C15.236 17.3546 15.472 16.6686 15.7093 16.03C15.9453 16.432 16.206 16.8346 16.466 17.1893C16.8911 17.7313 17.3329 18.26 17.7906 18.7746ZM14.692 9.97329C14.7338 9.88949 14.7978 9.81875 14.877 9.7688C14.9563 9.71884 15.0477 9.69157 15.1413 9.68996C15.638 9.68996 15.7326 10.2573 15.7326 10.7073C15.7326 11.464 15.496 12.6233 15.094 13.948C14.4073 12.1026 14.3606 10.5653 14.692 9.97329ZM12.0893 20.998C10.8826 22.9853 9.72331 24.2153 9.01398 24.2153C8.88599 24.2161 8.76132 24.1746 8.65931 24.0973C8.51731 23.9553 8.44598 23.79 8.49331 23.6006C8.63531 22.8906 10.0073 21.8973 12.0893 20.998Z",
1977
- fill: "white"
1978
- }), /* @__PURE__ */ jsx("path", {
1979
- d: "M9.74219 34.4258V28.6992H10.8828V29.3633H10.9531C11.0286 29.2096 11.1276 29.0781 11.25 28.9688C11.375 28.8594 11.5208 28.776 11.6875 28.7188C11.8542 28.6589 12.0391 28.6289 12.2422 28.6289C12.6016 28.6289 12.9115 28.7188 13.1719 28.8984C13.4323 29.0781 13.6328 29.3333 13.7734 29.6641C13.9167 29.9922 13.9883 30.3854 13.9883 30.8438V30.8516C13.9883 31.3125 13.918 31.7083 13.7773 32.0391C13.6367 32.3698 13.4362 32.6237 13.1758 32.8008C12.9154 32.9779 12.6042 33.0664 12.2422 33.0664C12.0443 33.0664 11.8607 33.0365 11.6914 32.9766C11.5247 32.9141 11.3776 32.8268 11.25 32.7148C11.125 32.6029 11.026 32.4688 10.9531 32.3125H10.8828V34.4258H9.74219ZM11.8516 32.1211C12.0547 32.1211 12.2279 32.0703 12.3711 31.9688C12.5169 31.8672 12.6289 31.7214 12.707 31.5312C12.7878 31.3411 12.8281 31.1146 12.8281 30.8516V30.8438C12.8281 30.5807 12.7878 30.3542 12.707 30.1641C12.6289 29.974 12.5169 29.8281 12.3711 29.7266C12.2279 29.625 12.0547 29.5742 11.8516 29.5742C11.651 29.5742 11.4766 29.625 11.3281 29.7266C11.1823 29.8281 11.069 29.974 10.9883 30.1641C10.9102 30.3516 10.8711 30.5781 10.8711 30.8438V30.8516C10.8711 31.112 10.9115 31.3385 10.9922 31.5312C11.0729 31.7214 11.1862 31.8672 11.332 31.9688C11.4805 32.0703 11.6536 32.1211 11.8516 32.1211ZM16.457 33.0664C16.1003 33.0664 15.7904 32.9779 15.5273 32.8008C15.2669 32.6211 15.0651 32.3659 14.9219 32.0352C14.7812 31.7044 14.7109 31.3099 14.7109 30.8516V30.8438C14.7109 30.3828 14.7799 29.987 14.918 29.6562C15.0586 29.3255 15.2591 29.0716 15.5195 28.8945C15.7799 28.7174 16.0924 28.6289 16.457 28.6289C16.6523 28.6289 16.8333 28.6602 17 28.7227C17.1693 28.7826 17.3177 28.8685 17.4453 28.9805C17.5729 29.0924 17.6719 29.2279 17.7422 29.3867H17.8125V27.0547H18.9531V33H17.8125V32.332H17.7422C17.6693 32.4857 17.5703 32.6172 17.4453 32.7266C17.3229 32.8359 17.1784 32.9206 17.0117 32.9805C16.8451 33.0378 16.6602 33.0664 16.457 33.0664ZM16.8438 32.1211C17.0469 32.1211 17.2214 32.0703 17.3672 31.9688C17.513 31.8672 17.625 31.7214 17.7031 31.5312C17.7839 31.3411 17.8242 31.1159 17.8242 30.8555V30.8477C17.8242 30.582 17.7839 30.3555 17.7031 30.168C17.625 29.9779 17.5117 29.832 17.3633 29.7305C17.2174 29.6263 17.0443 29.5742 16.8438 29.5742C16.6458 29.5742 16.4727 29.6263 16.3242 29.7305C16.1784 29.832 16.0664 29.9779 15.9883 30.168C15.9102 30.3555 15.8711 30.5807 15.8711 30.8438V30.8516C15.8711 31.1146 15.9102 31.3411 15.9883 31.5312C16.0664 31.7214 16.1784 31.8672 16.3242 31.9688C16.4701 32.0703 16.6432 32.1211 16.8438 32.1211ZM20.4648 33V29.5586H19.7695V28.6992H20.4648V28.2969C20.4648 28.0104 20.5156 27.7721 20.6172 27.582C20.7188 27.3919 20.8776 27.25 21.0938 27.1562C21.3125 27.0599 21.5951 27.0117 21.9414 27.0117C22.0586 27.0117 22.1641 27.0156 22.2578 27.0234C22.3542 27.0312 22.444 27.0417 22.5273 27.0547V27.8164C22.4909 27.8086 22.4427 27.8034 22.3828 27.8008C22.3255 27.7956 22.2604 27.793 22.1875 27.793C21.9661 27.793 21.8099 27.8438 21.7188 27.9453C21.6276 28.0443 21.582 28.1875 21.582 28.375V28.6992H22.4922V29.5586H21.6055V33H20.4648Z",
1980
- fill: "white"
1981
- })] });
1982
- var FilePdfIcon = ({ className, label, size, sizeConfig, ...props }) => {
1983
- const useLegacyPdfMarkup = !!label;
1984
- return /* @__PURE__ */ jsx(Svg, {
1985
- ...props,
1986
- className: clsx("str-chat__file-icon--pdf", useLegacyPdfMarkup && "str-chat__file-icon--no-label", className),
1987
- size,
1988
- sizeConfig,
1989
- children: /* @__PURE__ */ jsxs("g", {
1990
- className: FILE_ICON_GRAPHIC_CLASSNAME,
1991
- children: [
1992
- /* @__PURE__ */ jsx("path", {
1993
- d: FILE_ICON_PAPER_PATH,
1994
- fill: "#E71A01"
1995
- }),
1996
- useLegacyPdfMarkup ? PDF_LEGACY_SYMBOL : /* @__PURE__ */ jsx("g", {
1997
- transform: `scale(${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X} ${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y})`,
1998
- children: PDF_SMALL_SYMBOL
1999
- }),
2000
- /* @__PURE__ */ jsx("path", {
2001
- d: FILE_ICON_FOLD_PATH,
2002
- fill: "white",
2003
- opacity: "0.5"
2004
- })
2005
- ]
2006
- })
2007
- });
2008
- };
2009
- var FileWordIcon = ({ className, label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2010
- ...props,
2011
- className,
2012
- color: "#3375E2",
2013
- fileTypeClassName: "str-chat__file-icon--doc",
2014
- label,
2015
- symbols: TEXT_SYMBOLS
2016
- });
2017
- var FilePowerPointIcon = ({ className, label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2018
- ...props,
2019
- className,
2020
- color: "#D14423",
2021
- fileTypeClassName: "str-chat__file-icon--ppt",
2022
- label,
2023
- symbols: PRESENTATION_SYMBOLS
2024
- });
2025
- var FileExcelIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2026
- ...props,
2027
- className,
2028
- color: "#0C864B",
2029
- fileTypeClassName: "str-chat__file-icon--xls",
2030
- label,
2031
- symbols: SPREADSHEET_SYMBOLS
2032
- });
2033
- var FileArchiveIcon = ({ className = "", label = "", ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2034
- ...props,
2035
- className,
2036
- color: "#E59E34",
2037
- fileTypeClassName: "str-chat__file-icon--compressed",
2038
- label,
2039
- symbols: COMPRESSION_SYMBOLS
2040
- });
2041
- var FileCodeIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2042
- ...props,
2043
- className,
2044
- color: "#00ACA1",
2045
- fileTypeClassName: "str-chat__file-icon--code",
2046
- label,
2047
- symbols: CODE_SYMBOLS
2048
- });
2049
- var FileAudioIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2050
- ...props,
2051
- className,
2052
- color: "#2727B0",
2053
- fileTypeClassName: "str-chat__file-icon--audio",
2054
- label,
2055
- symbols: AUDIO_SYMBOLS
2056
- });
2057
- var FileVideoIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2058
- ...props,
2059
- className,
2060
- color: "#A847B7",
2061
- fileTypeClassName: "str-chat__file-icon--video",
2062
- label,
2063
- symbols: VIDEO_SYMBOLS
2064
- });
2065
- var FileFallbackIcon = ({ className = "", label = "", ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
2066
- ...props,
2067
- className,
2068
- color: "#888888",
2069
- fileTypeClassName: "str-chat__file-icon--other",
2070
- label,
2071
- symbols: OTHER_SYMBOLS
2072
- });
2073
- //#endregion
2074
- //#region src/components/FileIcon/mimeTypes.ts
2075
- var wordMimeTypes = [
2076
- "application/msword",
2077
- "application/msword-template",
2078
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2079
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
2080
- "application/vnd.ms-word.document.macroEnabled.12",
2081
- "application/vnd.ms-word.template.macroEnabled.12",
2082
- "application/vnd.oasis.opendocument.text",
2083
- "application/vnd.oasis.opendocument.text-template",
2084
- "application/vnd.oasis.opendocument.text-flat-xml"
2085
- ];
2086
- var excelMimeTypes = [
2087
- "text/csv",
2088
- "application/vnd.ms-excel",
2089
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
2090
- "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
2091
- "application/vnd.ms-excel.sheet.macroEnabled.12",
2092
- "application/vnd.ms-excel.template.macroEnabled.12",
2093
- "application/vnd.ms-excel.addin.macroEnabled.12",
2094
- "application/vnd.ms-excel.addin.macroEnabled.12",
2095
- "application/vnd.oasis.opendocument.spreadsheet",
2096
- "application/vnd.oasis.opendocument.spreadsheet-template",
2097
- "application/vnd.oasis.opendocument.spreadsheet-flat-xml"
2098
- ];
2099
- var powerpointMimeTypes = [
2100
- "application/vnd.ms-powerpoint",
2101
- "application/vnd.openxmlformats-officedocument.presentationml.presentation",
2102
- "application/vnd.openxmlformats-officedocument.presentationml.template",
2103
- "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
2104
- "application/vnd.ms-powerpoint.addin.macroEnabled.12",
2105
- "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
2106
- "application/vnd.ms-powerpoint.template.macroEnabled.12",
2107
- "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
2108
- "application/vnd.oasis.opendocument.presentation",
2109
- "application/vnd.oasis.opendocument.presentation-template",
2110
- "application/vnd.oasis.opendocument.presentation-flat-xml"
2111
- ];
2112
- var archiveFileTypes = [
2113
- "application/zip",
2114
- "application/x-7z-compressed",
2115
- "application/x-archive",
2116
- "application/x-tar",
2117
- "application/gzip",
2118
- "application/x-compress",
2119
- "application/x-bzip",
2120
- "application/x-lzip",
2121
- "application/x-lz4",
2122
- "application/x-lzma",
2123
- "application/x-lzop",
2124
- "application/x-xz",
2125
- "application/x-webarchive",
2126
- "application/vnd.rar"
2127
- ];
2128
- var codeFileTypes = [
2129
- "text/html",
2130
- "text/css",
2131
- "application/x-javascript",
2132
- "text/javascript",
2133
- "application/json",
2134
- "text/x-python",
2135
- "text/x-go",
2136
- "text/x-csrc",
2137
- "text/x-c++src",
2138
- "application/x-ruby",
2139
- "text/rust",
2140
- "text/x-java",
2141
- "application/x-php",
2142
- "text/x-csharp",
2143
- "text/x-scala",
2144
- "text/x-erlang",
2145
- "application/x-shellscript"
2146
- ];
2147
- var mimeTypeToExtensionMap = {
2148
- "application/epub+zip": "epub",
2149
- "application/gzip": "gz",
2150
- "application/java-archive": "jar",
2151
- "application/json": "json",
2152
- "application/ld+json": "jsonld",
2153
- "application/msword": "doc",
2154
- "application/msword-template": "dot",
2155
- "application/octet-stream": "bin",
2156
- "application/ogg": "ogx",
2157
- "application/pdf": "pdf",
2158
- "application/postscript": "ps",
2159
- "application/rtf": "rtf",
2160
- "application/vnd.amazon.ebook": "azw",
2161
- "application/vnd.apple.installer+xml": "mpkg",
2162
- "application/vnd.mozilla.xul+xml": "xul",
2163
- "application/vnd.ms-excel": "xls",
2164
- "application/vnd.ms-excel.addin.macroEnabled.12": "xlam",
2165
- "application/vnd.ms-excel.sheet.macroEnabled.12": "xlsm",
2166
- "application/vnd.ms-excel.template.macroEnabled.12": "xltm",
2167
- "application/vnd.ms-fontobject": "eot",
2168
- "application/vnd.ms-powerpoint": "ppt",
2169
- "application/vnd.ms-powerpoint.addin.macroEnabled.12": "ppam",
2170
- "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "pptm",
2171
- "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "ppsm",
2172
- "application/vnd.ms-powerpoint.template.macroEnabled.12": "potm",
2173
- "application/vnd.ms-word.document.macroEnabled.12": "docm",
2174
- "application/vnd.ms-word.template.macroEnabled.12": "dotm",
2175
- "application/vnd.oasis.opendocument.presentation": "odp",
2176
- "application/vnd.oasis.opendocument.presentation-flat-xml": "fodp",
2177
- "application/vnd.oasis.opendocument.presentation-template": "otp",
2178
- "application/vnd.oasis.opendocument.spreadsheet": "ods",
2179
- "application/vnd.oasis.opendocument.spreadsheet-flat-xml": "fods",
2180
- "application/vnd.oasis.opendocument.spreadsheet-template": "ots",
2181
- "application/vnd.oasis.opendocument.text": "odt",
2182
- "application/vnd.oasis.opendocument.text-flat-xml": "fodt",
2183
- "application/vnd.oasis.opendocument.text-template": "ott",
2184
- "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
2185
- "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "ppsx",
2186
- "application/vnd.openxmlformats-officedocument.presentationml.template": "potx",
2187
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
2188
- "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "xltx",
2189
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
2190
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "dotx",
2191
- "application/vnd.rar": "rar",
2192
- "application/vnd.visio": "vsd",
2193
- "application/wasm": "wasm",
2194
- "application/x-7z-compressed": "7z",
2195
- "application/x-abiword": "abw",
2196
- "application/x-archive": "ar",
2197
- "application/x-bzip": "bz",
2198
- "application/x-bzip2": "bz2",
2199
- "application/x-cdf": "cda",
2200
- "application/x-compress": "Z",
2201
- "application/x-csh": "csh",
2202
- "application/x-dosexec": "exe",
2203
- "application/x-freearc": "arc",
2204
- "application/x-httpd-php": "php",
2205
- "application/x-iso9660-image": "iso",
2206
- "application/x-javascript": "js",
2207
- "application/x-lz4": "lz4",
2208
- "application/x-lzip": "lz",
2209
- "application/x-lzma": "lzma",
2210
- "application/x-lzop": "lzo",
2211
- "application/x-mobipocket-ebook": "mobi",
2212
- "application/x-msdownload": "exe",
2213
- "application/x-perl": "pl",
2214
- "application/x-php": "php",
2215
- "application/x-rar-compressed": "rar",
2216
- "application/x-ruby": "rb",
2217
- "application/x-sh": "sh",
2218
- "application/x-shellscript": "sh",
2219
- "application/x-shockwave-flash": "swf",
2220
- "application/x-sql": "sql",
2221
- "application/x-stuffit": "sit",
2222
- "application/x-tar": "tar",
2223
- "application/x-webarchive": "war",
2224
- "application/x-xz": "xz",
2225
- "application/x-yaml": "yaml",
2226
- "application/xhtml+xml": "xhtml",
2227
- "application/xml": "xml",
2228
- "application/zip": "zip",
2229
- "audio/aac": "aac",
2230
- "audio/flac": "flac",
2231
- "audio/midi": "midi",
2232
- "audio/mp4": "m4a",
2233
- "audio/mpeg": "mp3",
2234
- "audio/ogg": "oga",
2235
- "audio/opus": "opus",
2236
- "audio/wav": "wav",
2237
- "audio/webm": "weba",
2238
- "audio/x-aiff": "aiff",
2239
- "audio/x-m4a": "m4a",
2240
- "audio/x-midi": "midi",
2241
- "audio/x-ms-wma": "wma",
2242
- "audio/x-wav": "wav",
2243
- "font/otf": "otf",
2244
- "font/ttf": "ttf",
2245
- "font/woff": "woff",
2246
- "font/woff2": "woff2",
2247
- "image/apng": "apng",
2248
- "image/avif": "avif",
2249
- "image/bmp": "bmp",
2250
- "image/gif": "gif",
2251
- "image/heic": "heic",
2252
- "image/heif": "heif",
2253
- "image/jpeg": "jpg",
2254
- "image/png": "png",
2255
- "image/svg+xml": "svg",
2256
- "image/tiff": "tiff",
2257
- "image/vnd.microsoft.icon": "ico",
2258
- "image/webp": "webp",
2259
- "image/x-icon": "ico",
2260
- "text/calendar": "ics",
2261
- "text/css": "css",
2262
- "text/csv": "csv",
2263
- "text/html": "html",
2264
- "text/javascript": "js",
2265
- "text/markdown": "md",
2266
- "text/plain": "txt",
2267
- "text/rtf": "rtf",
2268
- "text/rust": "rs",
2269
- "text/tab-separated-values": "tsv",
2270
- "text/vcard": "vcf",
2271
- "text/x-c": "c",
2272
- "text/x-c++src": "cpp",
2273
- "text/x-csharp": "cs",
2274
- "text/x-csrc": "c",
2275
- "text/x-diff": "diff",
2276
- "text/x-erlang": "erl",
2277
- "text/x-go": "go",
2278
- "text/x-java": "java",
2279
- "text/x-java-source": "java",
2280
- "text/x-kotlin": "kt",
2281
- "text/x-lua": "lua",
2282
- "text/x-markdown": "md",
2283
- "text/x-objectivec": "m",
2284
- "text/x-pascal": "pas",
2285
- "text/x-perl": "pl",
2286
- "text/x-python": "py",
2287
- "text/x-ruby": "rb",
2288
- "text/x-rust": "rs",
2289
- "text/x-scala": "scala",
2290
- "text/x-sh": "sh",
2291
- "text/x-shellscript": "sh",
2292
- "text/x-sql": "sql",
2293
- "text/x-swift": "swift",
2294
- "text/x-typescript": "ts",
2295
- "text/x-yaml": "yaml",
2296
- "text/xml": "xml",
2297
- "text/yaml": "yaml",
2298
- "video/3gpp": "3gp",
2299
- "video/3gpp2": "3g2",
2300
- "video/mp2t": "ts",
2301
- "video/mp4": "mp4",
2302
- "video/mpeg": "mpeg",
2303
- "video/ogg": "ogv",
2304
- "video/quicktime": "mov",
2305
- "video/webm": "webm",
2306
- "video/x-flv": "flv",
2307
- "video/x-m4v": "m4v",
2308
- "video/x-matroska": "mkv",
2309
- "video/x-ms-wmv": "wmv",
2310
- "video/x-msvideo": "avi"
2311
- };
2312
- //#endregion
2313
- //#region src/components/FileIcon/iconMap.ts
2314
- function generateMimeTypeToIconMap({ FileArchiveIcon, FileCodeIcon, FileExcelIcon, FilePdfIcon, FilePowerPointIcon, FileWordIcon }) {
2315
- const mimeTypeToIconMap = { "application/pdf": FilePdfIcon };
2316
- for (const type of wordMimeTypes) mimeTypeToIconMap[type] = FileWordIcon;
2317
- for (const type of excelMimeTypes) mimeTypeToIconMap[type] = FileExcelIcon;
2318
- for (const type of powerpointMimeTypes) mimeTypeToIconMap[type] = FilePowerPointIcon;
2319
- for (const type of archiveFileTypes) mimeTypeToIconMap[type] = FileArchiveIcon;
2320
- for (const type of codeFileTypes) mimeTypeToIconMap[type] = FileCodeIcon;
2321
- return mimeTypeToIconMap;
2322
- }
2323
- function generateGeneralTypeToIconMap({ FileAltIcon, FileAudioIcon, FileFallbackIcon, FileVideoIcon }) {
2324
- return {
2325
- "audio/": FileAudioIcon,
2326
- "image/": FileFallbackIcon,
2327
- "text/": FileAltIcon,
2328
- "video/": FileVideoIcon
2329
- };
2330
- }
2331
- var iconMap = { standard: {
2332
- ...generateMimeTypeToIconMap({
2333
- FileArchiveIcon,
2334
- FileCodeIcon,
2335
- FileExcelIcon,
2336
- FilePdfIcon,
2337
- FilePowerPointIcon,
2338
- FileWordIcon
2339
- }),
2340
- ...generateGeneralTypeToIconMap({
2341
- FileAltIcon: FileFallbackIcon,
2342
- FileAudioIcon,
2343
- FileFallbackIcon,
2344
- FileVideoIcon
2345
- }),
2346
- fallback: FileFallbackIcon
2347
- } };
2348
- //#endregion
2349
- //#region src/components/FileIcon/FileIcon.tsx
2350
- function mimeTypeToIcon(mimeType) {
2351
- const theMap = iconMap["standard"];
2352
- if (!mimeType) return theMap.fallback;
2353
- const icon = theMap[mimeType];
2354
- if (icon) return icon;
2355
- if (mimeType.startsWith("audio/")) return theMap["audio/"] ?? theMap.fallback;
2356
- if (mimeType.startsWith("video/")) return theMap["video/"] ?? theMap.fallback;
2357
- if (mimeType.startsWith("image/")) return theMap["image/"] ?? theMap.fallback;
2358
- if (mimeType.startsWith("text/")) return theMap["text/"] ?? theMap.fallback;
2359
- return theMap.fallback;
2360
- }
2361
- var labelFromMimeType = ({ fileName, mimeType }) => {
2362
- let label;
2363
- if (mimeType) label = mimeTypeToExtensionMap[mimeType];
2364
- if (!label && fileName) label = fileName.split(".").slice(-1)[0];
2365
- return label;
2366
- };
2367
- var FileIcon = (props) => {
2368
- const { className, fileName, mimeType, size = "md", sizeConfig: sizeConfigOverride, ...rest } = props;
2369
- const sizeConfig = useMemo(() => mergeFileIconSizeConfig(sizeConfigOverride), [sizeConfigOverride]);
2370
- const Icon = mimeTypeToIcon(mimeType);
2371
- const label = fileName ? labelFromMimeType({
2372
- fileName,
2373
- mimeType
2374
- }) : void 0;
2375
- return /* @__PURE__ */ jsx(Icon, {
2376
- ...rest,
2377
- className,
2378
- label,
2379
- size,
2380
- sizeConfig
2381
- });
2382
- };
2383
- //#endregion
2384
- //#region src/components/AudioPlayback/components/formatTime.ts
2385
- var formatTime = (totalSeconds, rounding = "ceil") => {
2386
- if (totalSeconds == null || Number.isNaN(totalSeconds) || totalSeconds < 0) return null;
2387
- const roundedSeconds = rounding === "floor" ? Math.floor(totalSeconds) : Math.ceil(totalSeconds);
2388
- const hours = Math.floor(roundedSeconds / 3600);
2389
- const minutes = Math.floor(roundedSeconds % 3600 / 60);
2390
- const seconds = roundedSeconds % 60;
2391
- const minSec = `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
2392
- return hours ? `${String(hours).padStart(2, "0")}:${minSec}` : minSec;
2393
- };
2394
- //#endregion
2395
- //#region src/components/BaseImage/ImagePlaceholder.tsx
2396
- var ImagePlaceholder = ({ className }) => {
2397
- const { t } = useTranslationContext();
2398
- return /* @__PURE__ */ jsx("div", {
2399
- "aria-label": t("aria/Image failed to load"),
2400
- className: clsx("str-chat__image-placeholder", className),
2401
- "data-testid": "str-chat__base-image-placeholder",
2402
- role: "img",
2403
- children: /* @__PURE__ */ jsx(IconImage, {})
2404
- });
2405
- };
2406
- //#endregion
2407
- //#region src/components/BaseImage/BaseImage.tsx
2408
- var BaseImage = forwardRef(function BaseImage({ src, ...props }, ref) {
2409
- const { alt: propsAlt, className: propsClassName, onError: propsOnError, showDownloadButtonOnError = false, ...imgProps } = props;
2410
- const [failedSrc, setFailedSrc] = useState(null);
2411
- const { ImagePlaceholder: ImagePlaceholderComponent = ImagePlaceholder } = useComponentContext();
2412
- const sanitizedUrl = useMemo(() => sanitizeUrl(src), [src]);
2413
- const error = failedSrc === sanitizedUrl;
2414
- useEffect(() => () => {
2415
- setFailedSrc(null);
2416
- }, [sanitizedUrl]);
2417
- if (error) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ImagePlaceholderComponent, { className: clsx(propsClassName, "str-chat__base-image--load-failed") }), showDownloadButtonOnError && /* @__PURE__ */ jsx(DownloadButton, { assetUrl: sanitizedUrl })] });
2418
- return /* @__PURE__ */ jsx("img", {
2419
- "data-testid": "str-chat__base-image",
2420
- ...imgProps,
2421
- alt: propsAlt ?? "",
2422
- className: clsx(propsClassName, "str-chat__base-image"),
2423
- onError: (e) => {
2424
- setFailedSrc(sanitizedUrl);
2425
- propsOnError?.(e);
2426
- },
2427
- ref,
2428
- src: sanitizedUrl
2429
- });
2430
- });
2431
- //#endregion
2432
- //#region src/components/BaseImage/toBaseImageDescriptors.ts
2433
- /**
2434
- * Maps an attachment (or link preview) to image/video URLs and metadata for {@link BaseImage} or the gallery.
2435
- */
2436
- var toBaseImageDescriptors = (attachment, options = {}) => {
2437
- if (isGiphyAttachment(attachment)) {
2438
- const giphyVersion = options?.giphyVersionName && attachment.giphy ? attachment.giphy[options.giphyVersionName] : void 0;
2439
- return {
2440
- alt: giphyVersion?.url || attachment.thumb_url,
2441
- dimensions: giphyVersion ? {
2442
- height: giphyVersion.height,
2443
- width: giphyVersion.width
2444
- } : void 0,
2445
- imageUrl: attachment.thumb_url,
2446
- title: attachment.title || attachment.thumb_url
2447
- };
2448
- }
2449
- if (isScrapedContent(attachment)) {
2450
- const imageUrl = attachment.image_url || attachment.thumb_url;
2451
- return {
2452
- alt: attachment.title || imageUrl,
2453
- imageUrl,
2454
- title: attachment.title
2455
- };
2456
- }
2457
- if (isLocalVideoAttachment(attachment)) return {
2458
- title: attachment.title,
2459
- videoThumbnailUrl: attachment.thumb_url ?? attachment.localMetadata.previewUri,
2460
- videoUrl: attachment.asset_url ?? attachment.localMetadata.previewUri
2461
- };
2462
- if (isVideoAttachment(attachment)) return {
2463
- title: attachment.title,
2464
- videoThumbnailUrl: attachment.thumb_url,
2465
- videoUrl: attachment.asset_url
2466
- };
2467
- if (isLocalImageAttachment(attachment)) {
2468
- const imageUrl = attachment.image_url || attachment.localMetadata.previewUri;
2469
- return {
2470
- alt: attachment.title || imageUrl,
2471
- imageUrl,
2472
- title: attachment.title
2473
- };
2474
- }
2475
- if (isImageAttachment(attachment)) {
2476
- const imageUrl = attachment.image_url;
2477
- return {
2478
- alt: attachment.title || imageUrl,
2479
- imageUrl,
2480
- title: attachment.title
2481
- };
2482
- }
2483
- };
2484
- //#endregion
2485
- //#region src/components/Gallery/GalleryContext.tsx
2486
- /**
2487
- * Maps an attachment (or link preview) to gallery item fields.
2488
- * Delegates to {@link toBaseImageDescriptors}.
2489
- */
2490
- var toGalleryItemDescriptors = (...args) => toBaseImageDescriptors(...args);
2491
- var GalleryContext = createContext(void 0);
2492
- var useGalleryContext = () => {
2493
- const contextValue = useContext(GalleryContext);
2494
- if (!contextValue) {
2495
- console.warn(`The useGalleryContext hook was called outside of the GalleryContext provider. Make sure this hook is called within a child of the Gallery component.`);
2496
- return {};
2497
- }
2498
- return contextValue;
2499
- };
2500
- //#endregion
2501
- //#region src/components/Gallery/GalleryHeader.tsx
2502
- var GalleryHeader = ({ currentItem }) => {
2503
- const { t } = useTranslationContext();
2504
- const { MessageTimestamp: MessageTimestamp$1 = MessageTimestamp } = useComponentContext("GalleryUI");
2505
- const { isMyMessage, message } = useMessageContext("GalleryUI");
2506
- const modalContext = useContext(ModalContext);
2507
- const headerTitle = isMyMessage?.() && t("You") || message?.user?.name || message?.user?.id || currentItem.title || t("User uploaded content");
2508
- const downloadUrl = useMemo(() => {
2509
- const rawDownloadUrl = currentItem.videoUrl ?? currentItem.imageUrl;
2510
- if (!rawDownloadUrl) return void 0;
2511
- const sanitizedUrl = sanitizeUrl(rawDownloadUrl);
2512
- return sanitizedUrl === "about:blank" ? void 0 : sanitizedUrl;
2513
- }, [currentItem.imageUrl, currentItem.videoUrl]);
2514
- const downloadLabel = t("aria/Download attachment");
2515
- return /* @__PURE__ */ jsxs("div", {
2516
- className: "str-chat__gallery__header",
2517
- children: [
2518
- /* @__PURE__ */ jsx("div", {
2519
- "aria-hidden": "true",
2520
- className: "str-chat__gallery__header-spacer"
2521
- }),
2522
- /* @__PURE__ */ jsxs("div", {
2523
- className: "str-chat__gallery__header-meta",
2524
- children: [/* @__PURE__ */ jsx("div", {
2525
- className: "str-chat__gallery__title",
2526
- children: headerTitle
2527
- }), message?.created_at ? /* @__PURE__ */ jsx(MessageTimestamp$1, { customClass: "str-chat__gallery__timestamp" }) : null]
2528
- }),
2529
- /* @__PURE__ */ jsxs("div", {
2530
- className: "str-chat__gallery__header-actions",
2531
- children: [downloadUrl ? /* @__PURE__ */ jsx("a", {
2532
- "aria-label": downloadLabel,
2533
- className: "str-chat__gallery__action-button str-chat__gallery__action-button--download",
2534
- download: true,
2535
- href: downloadUrl,
2536
- rel: "noreferrer",
2537
- target: "_blank",
2538
- title: downloadLabel,
2539
- children: /* @__PURE__ */ jsx(IconArrowDownCircle, {})
2540
- }) : null, modalContext?.close ? /* @__PURE__ */ jsx(Button, {
2541
- "aria-label": t("Close"),
2542
- className: "str-chat__gallery__action-button str-chat__gallery__action-button--close",
2543
- onClick: modalContext.close,
2544
- title: t("Close"),
2545
- children: /* @__PURE__ */ jsx(IconXmark, {})
2546
- }) : null]
2547
- })
2548
- ]
2549
- });
2550
- };
2551
- //#endregion
2552
- //#region src/components/VideoPlayer/VideoPlayer.tsx
2553
- var ReactPlayer = React.lazy(() => import("./ReactPlayerWrapper.d2ae5941.mjs"));
2554
- var VideoPlayer = ({ isPlaying, thumbnailUrl, videoUrl }) => {
2555
- const { LoadingIndicator: LoadingIndicator$1 = LoadingIndicator, VideoPlayer: VideoPlayerContext } = useComponentContext();
2556
- return VideoPlayerContext ? /* @__PURE__ */ jsx(VideoPlayerContext, {
2557
- thumbnailUrl,
2558
- videoUrl
2559
- }) : /* @__PURE__ */ jsx(React.Suspense, {
2560
- fallback: /* @__PURE__ */ jsx("div", {
2561
- className: "str-chat__video-player-loading",
2562
- children: /* @__PURE__ */ jsx(LoadingIndicator$1, {})
2563
- }),
2564
- children: /* @__PURE__ */ jsx(ReactPlayer, {
2565
- isPlaying,
2566
- thumbnailUrl,
2567
- videoUrl
2568
- })
2569
- });
2570
- };
2571
- //#endregion
2572
- //#region src/components/VideoPlayer/VideoThumbnail.tsx
2573
- var VideoThumbnail = ({ className, onPlay, ...imageProps }) => {
2574
- const { t } = useTranslationContext();
2575
- return /* @__PURE__ */ jsxs("div", {
2576
- className: "str-chat__message-attachment__video-thumbnail",
2577
- children: [/* @__PURE__ */ jsx(BaseImage, {
2578
- className: clsx("str-chat__message-attachment__video-thumbnail-image", className),
2579
- ...imageProps
2580
- }), onPlay ? /* @__PURE__ */ jsx(Button, {
2581
- appearance: "solid",
2582
- "aria-label": t("Play video"),
2583
- circular: true,
2584
- className: clsx("str-chat__message-attachment__video-thumbnail__play-indicator"),
2585
- onClick: onPlay,
2586
- size: "lg",
2587
- variant: "secondary",
2588
- children: /* @__PURE__ */ jsx(IconPlayFill, {})
2589
- }) : /* @__PURE__ */ jsx("div", {
2590
- className: "str-chat__message-attachment__video-thumbnail__play-indicator",
2591
- children: /* @__PURE__ */ jsx(IconPlayFill, {})
2592
- })]
2593
- });
2594
- };
2595
- //#endregion
2596
- //#region src/components/Gallery/GalleryUI.tsx
2597
- var SWIPE_THRESHOLD = 50;
2598
- var TRANSITION_DURATION = 300;
2599
- var GalleryUI = () => {
2600
- const { t } = useTranslationContext();
2601
- const { closeOnBackgroundClick, currentIndex, currentItem, goToNext, goToPrevious, hasNext, hasPrevious, itemCount, onRequestClose } = useGalleryContext();
2602
- const modalContext = useContext(ModalContext);
2603
- const [showVideo, setShowVideo] = useState(false);
2604
- const isTransitioningRef = useRef(false);
2605
- const [slideOffset, setSlideOffset] = useState(0);
2606
- const [isDragging, setIsDragging] = useState(false);
2607
- const [slideDirection, setSlideDirection] = useState(null);
2608
- const ignoreNextClickRef = useRef(false);
2609
- const touchStartRef = useRef(null);
2610
- const isVerticalSwipeRef = useRef(false);
2611
- const containerRef = useRef(null);
2612
- useEffect(() => {
2613
- setShowVideo(false);
2614
- }, [currentIndex]);
2615
- const prevIndexRef = useRef(currentIndex);
2616
- useEffect(() => {
2617
- if (prevIndexRef.current === currentIndex) return;
2618
- setSlideDirection(currentIndex > prevIndexRef.current ? "forward" : "backward");
2619
- setSlideOffset(0);
2620
- setIsDragging(false);
2621
- isTransitioningRef.current = true;
2622
- const timer = setTimeout(() => {
2623
- setSlideDirection(null);
2624
- isTransitioningRef.current = false;
2625
- }, TRANSITION_DURATION);
2626
- prevIndexRef.current = currentIndex;
2627
- return () => clearTimeout(timer);
2628
- }, [currentIndex]);
2629
- const handleGoToNext = useCallback(() => {
2630
- if (isTransitioningRef.current) return;
2631
- goToNext();
2632
- }, [goToNext]);
2633
- const handleGoToPrevious = useCallback(() => {
2634
- if (isTransitioningRef.current) return;
2635
- goToPrevious();
2636
- }, [goToPrevious]);
2637
- const handleKeyDown = useCallback((event) => {
2638
- if (event.key === "ArrowLeft") {
2639
- event.preventDefault();
2640
- handleGoToPrevious();
2641
- } else if (event.key === "ArrowRight") {
2642
- event.preventDefault();
2643
- handleGoToNext();
2644
- }
2645
- }, [handleGoToNext, handleGoToPrevious]);
2646
- useEffect(() => {
2647
- document.addEventListener("keydown", handleKeyDown);
2648
- return () => document.removeEventListener("keydown", handleKeyDown);
2649
- }, [handleKeyDown]);
2650
- const handleTouchStart = useCallback((event) => {
2651
- if (isTransitioningRef.current) return;
2652
- const touch = event.touches[0];
2653
- ignoreNextClickRef.current = false;
2654
- touchStartRef.current = {
2655
- x: touch.clientX,
2656
- y: touch.clientY
2657
- };
2658
- isVerticalSwipeRef.current = false;
2659
- }, []);
2660
- const handleTouchMove = useCallback((event) => {
2661
- if (!touchStartRef.current || isTransitioningRef.current) return;
2662
- const touch = event.touches[0];
2663
- const deltaX = touch.clientX - touchStartRef.current.x;
2664
- const deltaY = touch.clientY - touchStartRef.current.y;
2665
- if (!isDragging && !isVerticalSwipeRef.current) {
2666
- if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 10) {
2667
- ignoreNextClickRef.current = true;
2668
- isVerticalSwipeRef.current = true;
2669
- return;
2670
- }
2671
- if (Math.abs(deltaX) > 10) {
2672
- ignoreNextClickRef.current = true;
2673
- setIsDragging(true);
2674
- }
2675
- }
2676
- if (isVerticalSwipeRef.current) return;
2677
- if (!hasNext && deltaX < 0 || !hasPrevious && deltaX > 0) setSlideOffset(deltaX * .3);
2678
- else setSlideOffset(deltaX);
2679
- }, [
2680
- isDragging,
2681
- hasNext,
2682
- hasPrevious
2683
- ]);
2684
- const handleTouchEnd = useCallback(() => {
2685
- if (!touchStartRef.current || isVerticalSwipeRef.current) {
2686
- if (isVerticalSwipeRef.current) ignoreNextClickRef.current = true;
2687
- touchStartRef.current = null;
2688
- return;
2689
- }
2690
- const offset = slideOffset;
2691
- if (isDragging || Math.abs(offset) > 10) ignoreNextClickRef.current = true;
2692
- touchStartRef.current = null;
2693
- if (Math.abs(offset) >= SWIPE_THRESHOLD) if (offset < 0 && hasNext) goToNext();
2694
- else if (offset > 0 && hasPrevious) goToPrevious();
2695
- else setSlideOffset(0);
2696
- else setSlideOffset(0);
2697
- setIsDragging(false);
2698
- }, [
2699
- slideOffset,
2700
- hasNext,
2701
- hasPrevious,
2702
- goToNext,
2703
- goToPrevious,
2704
- isDragging
2705
- ]);
2706
- const requestClose = modalContext?.close ?? onRequestClose;
2707
- const handleBackgroundClick = useCallback((event) => {
2708
- if (event.target !== event.currentTarget) return;
2709
- if (ignoreNextClickRef.current) {
2710
- ignoreNextClickRef.current = false;
2711
- return;
2712
- }
2713
- if (!closeOnBackgroundClick) return;
2714
- requestClose?.();
2715
- }, [closeOnBackgroundClick, requestClose]);
2716
- const mediaStyle = isDragging || slideOffset !== 0 && slideDirection === null ? { transform: `translateX(${slideOffset}px)` } : {};
2717
- return /* @__PURE__ */ jsxs("div", {
2718
- className: "str-chat__gallery",
2719
- children: [/* @__PURE__ */ jsxs("div", {
2720
- className: "str-chat__gallery__main",
2721
- children: [
2722
- /* @__PURE__ */ jsx(GalleryHeader, { currentItem }),
2723
- /* @__PURE__ */ jsx(NavButton, {
2724
- "aria-label": t("Previous image"),
2725
- className: clsx("str-chat__gallery__nav-button--prev", !hasPrevious && "str-chat__gallery__nav-button--hidden"),
2726
- disabled: !hasPrevious,
2727
- onClick: handleGoToPrevious,
2728
- children: /* @__PURE__ */ jsx(IconChevronLeft, {})
2729
- }),
2730
- /* @__PURE__ */ jsx("div", {
2731
- className: "str-chat__gallery__slide-container",
2732
- onClick: handleBackgroundClick,
2733
- onTouchEnd: handleTouchEnd,
2734
- onTouchMove: handleTouchMove,
2735
- onTouchStart: handleTouchStart,
2736
- ref: containerRef,
2737
- children: /* @__PURE__ */ jsx("div", {
2738
- className: clsx("str-chat__gallery__media-container", {
2739
- "str-chat__gallery__media--dragging": isDragging,
2740
- "str-chat__gallery__media--slide-backward": !isDragging && slideDirection === "backward",
2741
- "str-chat__gallery__media--slide-forward": !isDragging && slideDirection === "forward"
2742
- }),
2743
- style: mediaStyle,
2744
- children: currentItem.videoUrl && currentItem.videoThumbnailUrl ? /* @__PURE__ */ jsx("div", {
2745
- className: "str-chat__gallery__media str-chat__gallery__media--video",
2746
- children: showVideo ? /* @__PURE__ */ jsx(VideoPlayer, {
2747
- isPlaying: true,
2748
- videoUrl: currentItem.videoUrl
2749
- }) : /* @__PURE__ */ jsx(VideoThumbnail, {
2750
- alt: currentItem.title ?? "",
2751
- onPlay: () => setShowVideo(true),
2752
- src: currentItem.videoThumbnailUrl
2753
- })
2754
- }) : /* @__PURE__ */ jsx("div", {
2755
- className: "str-chat__gallery__media str-chat__gallery__media--image",
2756
- children: /* @__PURE__ */ jsx(BaseImage, {
2757
- alt: currentItem.alt,
2758
- src: currentItem.imageUrl
2759
- })
2760
- })
2761
- })
2762
- }),
2763
- /* @__PURE__ */ jsx(NavButton, {
2764
- "aria-label": t("Next image"),
2765
- className: clsx("str-chat__gallery__nav-button--next", !hasNext && "str-chat__gallery__nav-button--hidden"),
2766
- disabled: !hasNext,
2767
- onClick: handleGoToNext,
2768
- children: /* @__PURE__ */ jsx(IconChevronRight, {})
2769
- })
2770
- ]
2771
- }), itemCount > 1 && /* @__PURE__ */ jsxs("div", {
2772
- className: "str-chat__gallery__position-indicator",
2773
- children: [
2774
- currentIndex + 1,
2775
- " of ",
2776
- itemCount
2777
- ]
2778
- })]
2779
- });
2780
- };
2781
- var NavButton = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
2782
- ...props,
2783
- className: clsx("str-chat__gallery__nav-button", className)
2784
- });
2785
- //#endregion
2786
- //#region src/components/Gallery/Gallery.tsx
2787
- var Gallery = ({ closeOnBackgroundClick = true, GalleryUI: GalleryUI$1, initialIndex = 0, items, onIndexChange, onRequestClose }) => {
2788
- const { GalleryUI: ContextGalleryUI } = useComponentContext();
2789
- const ResolvedGalleryUI = GalleryUI$1 ?? ContextGalleryUI ?? GalleryUI;
2790
- const [currentIndex, setCurrentIndex] = useState(initialIndex);
2791
- const itemCount = items.length;
2792
- const goToIndex = useCallback((index) => {
2793
- if (index >= 0 && index < itemCount) setCurrentIndex(index);
2794
- }, [itemCount]);
2795
- const goToNext = useCallback(() => {
2796
- setCurrentIndex((prev) => prev < itemCount - 1 ? prev + 1 : prev);
2797
- }, [itemCount]);
2798
- const goToPrevious = useCallback(() => {
2799
- setCurrentIndex((prev) => prev > 0 ? prev - 1 : prev);
2800
- }, []);
2801
- useEffect(() => {
2802
- onIndexChange?.(currentIndex);
2803
- }, [currentIndex, onIndexChange]);
2804
- const hasNext = currentIndex < itemCount - 1;
2805
- const hasPrevious = currentIndex > 0;
2806
- const currentItem = items[currentIndex];
2807
- const contextValue = useMemo(() => ({
2808
- closeOnBackgroundClick,
2809
- currentIndex,
2810
- currentItem,
2811
- goToIndex,
2812
- goToNext,
2813
- goToPrevious,
2814
- hasNext,
2815
- hasPrevious,
2816
- itemCount,
2817
- items,
2818
- onRequestClose
2819
- }), [
2820
- closeOnBackgroundClick,
2821
- currentIndex,
2822
- currentItem,
2823
- goToIndex,
2824
- goToNext,
2825
- goToPrevious,
2826
- hasNext,
2827
- hasPrevious,
2828
- itemCount,
2829
- items,
2830
- onRequestClose
2831
- ]);
2832
- return /* @__PURE__ */ jsx(GalleryContext.Provider, {
2833
- value: contextValue,
2834
- children: /* @__PURE__ */ jsx(ResolvedGalleryUI, {})
2835
- });
2836
- };
2837
- //#endregion
2838
- //#region src/components/Form/SwitchField.tsx
2839
- var SwitchField = ({ children, description, fieldClassName, title, ...props }) => {
2840
- const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, checked, defaultChecked, disabled, id, onChange, onKeyDown, ...rest } = props;
2841
- const generatedSwitchId = useStableId();
2842
- const switchId = id ?? `str-chat__switch-field-${generatedSwitchId}`;
2843
- const switchLabelId = `${switchId}-label`;
2844
- const inputRef = useRef(null);
2845
- const announce = useContext(AriaLiveAnnouncerContext)?.announce;
2846
- const { t } = useTranslationContext("SwitchField");
2847
- const [uncontrolledChecked, setUncontrolledChecked] = useState(Boolean(defaultChecked));
2848
- const isControlled = checked !== void 0;
2849
- const isOn = isControlled ? checked : uncontrolledChecked;
2850
- const isReadOnly = isControlled && onChange === void 0;
2851
- const childLabelId = (isValidElement(children) ? children : void 0)?.props.id;
2852
- const resolvedAriaLabelledBy = ariaLabelledBy ?? (!ariaLabel ? title ? switchLabelId : childLabelId : void 0);
2853
- const resolveAnnouncementName = () => {
2854
- if (title) return title;
2855
- if (ariaLabel) return ariaLabel;
2856
- if (resolvedAriaLabelledBy) {
2857
- const labelEl = document.getElementById(resolvedAriaLabelledBy);
2858
- return ((labelEl?.querySelector(".str-chat__form__switch-field__label__text"))?.textContent || labelEl?.textContent || "").trim() || void 0;
2859
- }
2860
- };
2861
- const handleChange = (event) => {
2862
- if (!isControlled) setUncontrolledChecked(event.target.checked);
2863
- onChange?.(event);
2864
- const setting = resolveAnnouncementName();
2865
- if (!announce || !setting) return;
2866
- announce(event.target.checked ? t("aria/{{ setting }} enabled", { setting }) : t("aria/{{ setting }} disabled", { setting }), { priority: "polite" });
2867
- };
2868
- const handleKeyDown = (event) => {
2869
- onKeyDown?.(event);
2870
- if (event.defaultPrevented || event.key !== " ") return;
2871
- event.preventDefault();
2872
- event.currentTarget.click();
2873
- };
2874
- const handleSwitchClick = (event) => {
2875
- if (disabled || event.target === inputRef.current) return;
2876
- inputRef.current?.click();
2877
- };
2878
- return /* @__PURE__ */ jsxs("div", {
2879
- className: clsx("str-chat__form__switch-field", fieldClassName, disabled && "str-chat__form__switch-field--disabled"),
2880
- children: [title ? /* @__PURE__ */ jsx(SwitchFieldLabel, {
2881
- description,
2882
- htmlFor: switchId,
2883
- id: switchLabelId,
2884
- title
2885
- }) : children, /* @__PURE__ */ jsx(Switch, {
2886
- ...rest,
2887
- "aria-label": ariaLabel,
2888
- "aria-labelledby": resolvedAriaLabelledBy,
2889
- checked: isOn,
2890
- disabled,
2891
- id: switchId,
2892
- on: isOn,
2893
- onChange: handleChange,
2894
- onKeyDown: handleKeyDown,
2895
- onSwitchClick: handleSwitchClick,
2896
- readOnly: isReadOnly,
2897
- switchRef: inputRef
2898
- })]
2899
- });
2900
- };
2901
- var Switch = ({ className, on, onSwitchClick, presentation, switchRef, ...props }) => /* @__PURE__ */ jsxs("div", {
2902
- "aria-hidden": presentation ? true : void 0,
2903
- className: clsx("str-chat__form__switch-field__switch", { "str-chat__form__switch-field__switch--on": on }),
2904
- onClick: presentation ? void 0 : onSwitchClick,
2905
- children: [!presentation && /* @__PURE__ */ jsx("input", {
2906
- ...props,
2907
- className: clsx("str-chat__form__switch-field__input", className),
2908
- ref: switchRef,
2909
- role: "switch",
2910
- type: "checkbox"
2911
- }), /* @__PURE__ */ jsx("span", { className: "str-chat__form__switch-field__switch-handle" })]
2912
- });
2913
- var SwitchFieldLabel = ({ asError, children, className, description, title, ...props }) => /* @__PURE__ */ jsx("label", {
2914
- className: clsx("str-chat__form__switch-field__label", { "str-chat__form__switch-field__label--as-error": asError }, className),
2915
- ...props,
2916
- children: /* @__PURE__ */ jsx("div", {
2917
- className: "str-chat__form__switch-field__label__content",
2918
- children: title ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SwitchFieldTitle, { children: title }), description != null && description !== "" && /* @__PURE__ */ jsx(SwitchFieldDescription, { children: description })] }) : children
2919
- })
2920
- });
2921
- var SwitchFieldTitle = ({ children, className, title, ...props }) => /* @__PURE__ */ jsx("div", {
2922
- className: clsx("str-chat__form__switch-field__label__text", className),
2923
- ...props,
2924
- children: children ?? title
2925
- });
2926
- var SwitchFieldDescription = ({ children, className, description, ...props }) => /* @__PURE__ */ jsx("div", {
2927
- className: clsx("str-chat__form__switch-field__label__description", className),
2928
- ...props,
2929
- children: children ?? description
2930
- });
2931
- //#endregion
2932
- //#region src/components/Form/Checkbox.tsx
2933
- var Checkbox = ({ checked, ...props }) => /* @__PURE__ */ jsx("div", {
2934
- ...props,
2935
- className: clsx("str-chat__checkmark str-chat__checkbox", {
2936
- "str-chat__checkbox--checked": checked,
2937
- "str-chat__checkmark--checked": checked
2938
- })
2939
- });
2940
- var Checkmark = Checkbox;
2941
- //#endregion
2942
- //#region src/components/Form/TextInput.tsx
2943
- var TextInputIconMessageLine = ({ icon, text }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
2944
- "aria-hidden": true,
2945
- className: "str-chat__form-text-input__message-icon",
2946
- children: icon
2947
- }), /* @__PURE__ */ jsx("span", {
2948
- className: "str-chat__form-text-input__message-text",
2949
- children: text
2950
- })] });
2951
- var TextInputFieldMessage = (props) => {
2952
- if (props.kind === "neutral") return /* @__PURE__ */ jsx("div", {
2953
- className: clsx("str-chat__form-text-input__message", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2954
- id: props.id,
2955
- children: props.text
2956
- });
2957
- else if (props.kind === "success") return /* @__PURE__ */ jsx("div", {
2958
- className: clsx("str-chat__form-text-input__message", "str-chat__form-text-input__message--success", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2959
- id: props.id,
2960
- children: /* @__PURE__ */ jsx(TextInputIconMessageLine, {
2961
- icon: props.successMessageIcon ?? /* @__PURE__ */ jsx(IconCheckmark, {}),
2962
- text: props.text
2963
- })
2964
- });
2965
- else if (props.kind === "error") return /* @__PURE__ */ jsx("div", {
2966
- className: clsx("str-chat__form-text-input__message", "str-chat__form-field-error", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2967
- id: props.id,
2968
- role: "alert",
2969
- children: /* @__PURE__ */ jsx(TextInputIconMessageLine, {
2970
- icon: props.errorMessageIcon ?? /* @__PURE__ */ jsx(IconExclamationMark, {}),
2971
- text: props.text
2972
- })
2973
- });
2974
- return null;
2975
- };
2976
- var TextInput = forwardRef(function TextInput({ className, disabled, error = false, errorMessage, errorMessageIcon, fieldMessagePlacement = "outside", id: idProp, label, leading, message, successMessage, successMessageIcon, trailing, trailingText, variant = "outline", ...inputProps }, ref) {
2977
- const autoId = useStableId();
2978
- const id = idProp ?? autoId;
2979
- const hasError = error && (errorMessage != null || message != null);
2980
- const showSuccess = !hasError && successMessage != null;
2981
- const showNeutral = !hasError && !showSuccess && message != null;
2982
- const messageInside = fieldMessagePlacement === "inside" && (hasError || showSuccess || showNeutral);
2983
- const messageId = hasError ? `${id}-field-error` : showSuccess || showNeutral ? `${id}-message` : void 0;
2984
- const describedBy = [inputProps["aria-describedby"], messageId].filter((value) => !!value).join(" ");
2985
- const fieldMessage = hasError ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2986
- errorMessageIcon,
2987
- id: messageId,
2988
- insidePlacement: messageInside,
2989
- kind: "error",
2990
- text: errorMessage ?? message
2991
- }) : showSuccess ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2992
- id: messageId,
2993
- insidePlacement: messageInside,
2994
- kind: "success",
2995
- successMessageIcon,
2996
- text: successMessage
2997
- }) : showNeutral ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2998
- id: messageId,
2999
- insidePlacement: messageInside,
3000
- kind: "neutral",
3001
- text: message
3002
- }) : null;
3003
- return /* @__PURE__ */ jsxs("div", {
3004
- className: clsx("str-chat__form-text-input", error && "str-chat__form-text-input--error", showSuccess && "str-chat__form-text-input--success", disabled && "str-chat__form-text-input--disabled", messageInside && "str-chat__form-text-input--field-message-inside", className),
3005
- children: [
3006
- label ? /* @__PURE__ */ jsx("label", {
3007
- className: "str-chat__form-text-input__label",
3008
- htmlFor: id,
3009
- children: label
3010
- }) : null,
3011
- /* @__PURE__ */ jsxs("div", {
3012
- className: clsx("str-chat__form-text-input__wrapper", `str-chat__form-text-input__wrapper--${variant}`, messageInside && "str-chat__form-text-input__wrapper--field-message-inside"),
3013
- children: [/* @__PURE__ */ jsxs("div", {
3014
- className: "str-chat__form-text-input__control-row",
3015
- children: [
3016
- leading ? /* @__PURE__ */ jsx("span", {
3017
- className: "str-chat__form-text-input__leading",
3018
- children: leading
3019
- }) : null,
3020
- /* @__PURE__ */ jsx("input", {
3021
- "aria-describedby": describedBy,
3022
- "aria-invalid": error,
3023
- className: "str-chat__form-text-input__input",
3024
- disabled,
3025
- id,
3026
- ref,
3027
- ...inputProps
3028
- }),
3029
- trailingText != null ? /* @__PURE__ */ jsx("span", {
3030
- "aria-hidden": true,
3031
- className: "str-chat__form-text-input__suffix",
3032
- children: trailingText
3033
- }) : null,
3034
- trailing ? /* @__PURE__ */ jsx("span", {
3035
- className: "str-chat__form-text-input__trailing",
3036
- children: trailing
3037
- }) : null
3038
- ]
3039
- }), messageInside ? fieldMessage : null]
3040
- }),
3041
- messageInside ? null : fieldMessage
3042
- ]
3043
- });
3044
- });
3045
- //#endregion
3046
- //#region src/components/ListItemLayout/ListItemLayout.tsx
3047
- var ListItemLayout = ({ ContentSlot = ListItemLayoutContent, contentClassName, description, descriptionClassName, destructive, LeadingIcon, LeadingSlot, RootElement, rootProps, selected, subtitle, subtitleClassName, title, titleClassName, TrailingIcon, TrailingSlot }) => {
3048
- const ContainerComponent = RootElement ?? "div";
3049
- return /* @__PURE__ */ jsx("div", {
3050
- className: "str-chat__list-item-layout",
3051
- children: /* @__PURE__ */ jsxs(ContainerComponent, {
3052
- ...ContainerComponent === "button" ? { type: "button" } : void 0,
3053
- ...rootProps,
3054
- className: clsx("str-chat__list-item-layout__container", rootProps?.className, destructive && "str-chat__list-item-layout__container--destructive", selected && "str-chat__list-item-layout__container--selected"),
3055
- children: [
3056
- LeadingIcon && /* @__PURE__ */ jsx("div", {
3057
- className: "str-chat__list-item-layout__leading-icon",
3058
- children: /* @__PURE__ */ jsx(LeadingIcon, {})
3059
- }),
3060
- LeadingSlot && /* @__PURE__ */ jsx(LeadingSlot, {}),
3061
- /* @__PURE__ */ jsx(ContentSlot, {
3062
- className: contentClassName,
3063
- description,
3064
- descriptionClassName,
3065
- subtitle,
3066
- subtitleClassName,
3067
- title,
3068
- titleClassName
3069
- }),
3070
- TrailingIcon && /* @__PURE__ */ jsx("div", {
3071
- className: "str-chat__list-item-layout__trailing-icon",
3072
- children: /* @__PURE__ */ jsx(TrailingIcon, {})
3073
- }),
3074
- TrailingSlot && /* @__PURE__ */ jsx(TrailingSlot, {})
3075
- ]
3076
- })
3077
- });
3078
- };
3079
- var ListItemLayoutContent = ({ className, description, descriptionClassName, subtitle, subtitleClassName, title, titleClassName, ...props }) => /* @__PURE__ */ jsxs("div", {
3080
- ...props,
3081
- className: clsx("str-chat__list-item-layout__content", className, {
3082
- "str-chat__list-item-layout__content--withDescription": description,
3083
- "str-chat__list-item-layout__content--withSubtitle": subtitle,
3084
- "str-chat__list-item-layout__content--withTitle": title
3085
- }),
3086
- children: [
3087
- title && /* @__PURE__ */ jsx("div", {
3088
- className: clsx("str-chat__list-item-layout__title", titleClassName),
3089
- children: title
3090
- }),
3091
- subtitle && /* @__PURE__ */ jsx("div", {
3092
- className: clsx("str-chat__list-item-layout__subtitle", subtitleClassName),
3093
- children: subtitle
3094
- }),
3095
- description && /* @__PURE__ */ jsx("div", {
3096
- className: clsx("str-chat__list-item-layout__description", descriptionClassName),
3097
- children: description
3098
- })
3099
- ]
3100
- });
3101
- //#endregion
3102
- //#region src/components/ChannelListItem/utils.tsx
3103
- var remarkPlugins = [
3104
- htmlToTextPlugin,
3105
- [remarkGfm, { singleTilde: false }],
3106
- plusPlusToEmphasis,
3107
- imageToLink
3108
- ];
3109
- var renderPreviewText = (text) => /* @__PURE__ */ jsx(ReactMarkdown, {
3110
- remarkPlugins,
3111
- skipHtml: true,
3112
- children: text
3113
- });
3114
- var createPlainTextProcessor = () => unified().use(remarkParse).use(remarkPlugins);
3115
- var plainTextProcessor;
3116
- var stripMarkdownToText = (text) => {
3117
- try {
3118
- const processor = plainTextProcessor ?? (plainTextProcessor = createPlainTextProcessor());
3119
- return toString(processor.runSync(processor.parse(text))) || text;
3120
- } catch {
3121
- return text;
3122
- }
3123
- };
3124
- var getLatestPollVote = (latestVotesByOption) => {
3125
- let latestVote;
3126
- for (const optionVotes of Object.values(latestVotesByOption)) optionVotes.forEach((vote) => {
3127
- if (latestVote && new Date(latestVote.updated_at) >= new Date(vote.created_at)) return;
3128
- latestVote = vote;
3129
- });
3130
- return latestVote;
3131
- };
3132
- /**
3133
- * Resolves the channel's latest-message preview once: the localized DISPLAY string, a `kind`
3134
- * discriminant, and the bits announcements need (poll question, attachment type). Single source of
3135
- * truth for the display variant ({@link getLatestMessagePreview}) and the announcement variant
3136
- * ({@link getLatestMessagePreviewText}), so the translation lookup and branching run once per call.
3137
- */
3138
- var getLatestMessagePreviewParts = (channel, t, userLanguage = "en", latestMessageArg) => {
3139
- const latestMessage = latestMessageArg ?? channel.state.latestMessages[channel.state.latestMessages.length - 1];
3140
- const previewTextToRender = getTranslatedMessageText({
3141
- language: userLanguage,
3142
- message: latestMessage
3143
- }) || latestMessage?.text;
3144
- const poll = latestMessage?.poll;
3145
- if (!latestMessage) return {
3146
- isUserMessageText: false,
3147
- kind: "empty",
3148
- latestMessage,
3149
- text: t("Nothing yet...")
3150
- };
3151
- if (isMessageDeleted(latestMessage)) return {
3152
- isUserMessageText: false,
3153
- kind: "deleted",
3154
- latestMessage,
3155
- text: t("Message deleted")
3156
- };
3157
- if (poll) if (!poll.vote_count) {
3158
- const createdBy = poll.created_by?.id === channel.getClient().userID ? t("You") : poll.created_by?.name ?? t("Poll");
3159
- return {
3160
- isUserMessageText: false,
3161
- kind: "poll",
3162
- latestMessage,
3163
- pollName: poll.name,
3164
- text: t("📊 {{createdBy}} created: {{ pollName}}", {
3165
- createdBy,
3166
- pollName: poll.name
3167
- })
3168
- };
3169
- } else {
3170
- const latestVote = getLatestPollVote(poll.latest_votes_by_option);
3171
- const option = latestVote && poll.options.find((opt) => opt.id === latestVote.option_id);
3172
- if (option && latestVote) return {
3173
- isUserMessageText: false,
3174
- kind: "poll",
3175
- latestMessage,
3176
- pollName: poll.name,
3177
- text: t("📊 {{votedBy}} voted: {{pollOptionText}}", {
3178
- pollOptionText: option.text,
3179
- votedBy: latestVote?.user?.id === channel.getClient().userID ? t("You") : latestVote.user?.name ?? t("Poll")
3180
- })
3181
- };
3182
- }
3183
- if (previewTextToRender) return {
3184
- isUserMessageText: true,
3185
- kind: "text",
3186
- latestMessage,
3187
- text: previewTextToRender
3188
- };
3189
- if (latestMessage.command) return {
3190
- isUserMessageText: false,
3191
- kind: "command",
3192
- latestMessage,
3193
- text: `/${latestMessage.command}`
3194
- };
3195
- if (latestMessage.attachments?.length) return {
3196
- isUserMessageText: false,
3197
- kind: "attachment",
3198
- latestMessage,
3199
- text: t("🏙 Attachment...")
3200
- };
3201
- if (latestMessage.shared_location) return {
3202
- isUserMessageText: false,
3203
- kind: "location",
3204
- latestMessage,
3205
- text: t("📍Shared location")
3206
- };
3207
- return {
3208
- isUserMessageText: false,
3209
- kind: "text",
3210
- latestMessage,
3211
- text: t("Empty message...")
3212
- };
3213
- };
3214
- /**
3215
- * Maps a known attachment `type` to a localized, human-readable word (e.g. "image" → "Image"). The
3216
- * cases are literal `t('aria/…')` calls so `i18next-cli` extracts them. Unknown/custom types return
3217
- * `undefined`, so the announcement falls back to a generic "Attachment".
3218
- */
3219
- var getAttachmentTypeLabel = (type, t) => {
3220
- switch (type) {
3221
- case "audio": return t("aria/audio");
3222
- case "file": return t("aria/file");
3223
- case "giphy": return t("aria/GIF");
3224
- case "image": return t("aria/image");
3225
- case "video": return t("aria/video");
3226
- case "voiceRecording": return t("aria/voice message");
3227
- default: return;
3228
- }
3229
- };
3230
- /**
3231
- * The text announced for the channel's latest message (for an accessible name / live region).
3232
- * Non-text content gets a concise, screen-reader-friendly phrasing that may differ from the visible
3233
- * preview: a poll announces its question, an attachment its type, a shared location and a deleted
3234
- * message a plain phrase. User-message text is stripped of markdown so the words are read, not the
3235
- * syntax (AI-generated text is returned verbatim, matching the display path).
3236
- */
3237
- var getLatestMessagePreviewText = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
3238
- const { isUserMessageText, kind, latestMessage: resolvedLatestMessage, pollName, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
3239
- switch (kind) {
3240
- case "poll": return t("aria/Poll: {{ pollName }}", { pollName: pollName ?? "" });
3241
- case "attachment": {
3242
- const realAttachments = resolvedLatestMessage?.attachments?.filter((attachment) => !attachment.og_scrape_url) ?? [];
3243
- if (realAttachments.length > 1) return t("aria/Message with attachments");
3244
- const typeLabel = getAttachmentTypeLabel(realAttachments[0]?.type, t);
3245
- return typeLabel ? t("aria/Attachment {{ attachmentType }}", { attachmentType: typeLabel }) : t("aria/Attachment");
3246
- }
3247
- case "location": return t("aria/Shared location");
3248
- case "empty": return t("aria/There are no messages in this chat.");
3249
- case "text": return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? stripMarkdownToText(text) : text;
3250
- default: return text;
3251
- }
3252
- };
3253
- var getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
3254
- const { isUserMessageText, latestMessage: resolvedLatestMessage, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
3255
- return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? renderPreviewText(text) : text;
3256
- };
3257
- /**
3258
- * Channel display image: channel.data.image, or for DM (2 members) the other member's user.image.
3259
- */
3260
- var getChannelDisplayImage = (channel, currentUserId) => {
3261
- const data = channel.data;
3262
- if (data?.image && typeof data.image === "string") return data.image;
3263
- const memberList = Object.values(channel.state.members);
3264
- if (memberList.length === 2) {
3265
- const image = memberList.find((m) => m.user?.id !== currentUserId)?.user?.image;
3266
- if (image && typeof image === "string") return image;
3267
- }
3268
- };
3269
- var getGroupChannelDisplayInfo = (channel) => {
3270
- const members = Object.values(channel.state.members);
3271
- if (members.length <= 2) return;
3272
- const memberList = [];
3273
- for (const member of members) {
3274
- const { user } = member;
3275
- if (!user?.name && !user?.image) continue;
3276
- memberList.push(extractDisplayInfo(member));
3277
- }
3278
- return { members: memberList };
3279
- };
3280
- //#endregion
3281
- //#region src/components/ChannelListItem/hooks/useChannelDisplayName.ts
3282
- /**
3283
- * 1. channel.data.name
3284
- * 2. DM (exactly 2 members): other member's name, then directMessageLabel
3285
- * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
3286
- * 4. undefined otherwise
3287
- */
3288
- function computeChannelDisplayName(channel, directMessageLabel, currentUserId) {
3289
- const data = channel.data;
3290
- if (data?.name && typeof data.name === "string") return data.name;
3291
- const memberList = Object.values(channel.state.members);
3292
- const otherMembers = memberList.filter((m) => m.user?.id !== currentUserId);
3293
- if (memberList.length === 2 && otherMembers.length === 1) return otherMembers[0].user?.name || directMessageLabel;
3294
- if (otherMembers.length >= 2) {
3295
- const names = otherMembers.map((m) => m.user?.name).filter(Boolean).slice(0, 2);
3296
- if (names.length > 0) return names.join(", ");
3297
- }
3298
- }
3299
- /**
3300
- * Channel display name with translation context.
3301
- * 1. channel.data.name
3302
- * 2. DM (exactly 2 members): other member's name, then translated "Direct message"
3303
- * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
3304
- * 4. undefined otherwise
3305
- */
3306
- var useChannelDisplayName = (channel) => {
3307
- const { client } = useChatContext("useChannelDisplayName");
3308
- const { t } = useTranslationContext("useChannelDisplayName");
3309
- const directMessageLabel = t("Direct message");
3310
- const [displayName, setDisplayName] = useState(() => channel ? computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0) : void 0);
3311
- useEffect(() => {
3312
- if (!channel) {
3313
- setDisplayName(void 0);
3314
- return;
3315
- }
3316
- const updateDisplayName = () => setDisplayName(computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0));
3317
- updateDisplayName();
3318
- client.on("user.updated", updateDisplayName);
3319
- return () => {
3320
- client.off("user.updated", updateDisplayName);
3321
- };
3322
- }, [
3323
- channel,
3324
- channel?.data,
3325
- client,
3326
- directMessageLabel
3327
- ]);
3328
- return displayName;
3329
- };
3330
- //#endregion
3331
- //#region src/components/ChannelListItem/hooks/useChannelPreviewInfo.ts
3332
- var emptyGroupInfo = {
3333
- members: [],
3334
- overflowCount: void 0
3335
- };
3336
- var useChannelPreviewInfo = (props) => {
3337
- const { channel, overrideImage, overrideTitle } = props;
3338
- const { client } = useChatContext();
3339
- const channelDisplayName = useChannelDisplayName(channel);
3340
- const displayTitle = overrideTitle ?? channelDisplayName;
3341
- const [displayImage, setDisplayImage] = useState(() => channel ? overrideImage ?? getChannelDisplayImage(channel, client.userID ?? void 0) : void 0);
3342
- const [groupChannelDisplayInfo, setGroupChannelDisplayInfo] = useState(() => channel ? getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo : emptyGroupInfo);
3343
- useEffect(() => {
3344
- if (!channel) return;
3345
- if (overrideImage) return;
3346
- const updateInfo = () => {
3347
- setDisplayImage(getChannelDisplayImage(channel, client.userID ?? void 0));
3348
- setGroupChannelDisplayInfo(getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo);
3349
- };
3350
- updateInfo();
3351
- const { unsubscribe: unsubscribeChannelUpdated } = channel.on("channel.updated", updateInfo);
3352
- const { unsubscribe: unsubscribeUserUpdated } = client.on("user.updated", updateInfo);
3353
- return () => {
3354
- unsubscribeChannelUpdated();
3355
- unsubscribeUserUpdated();
3356
- };
3357
- }, [
3358
- channel,
3359
- channel?.data,
3360
- client,
3361
- overrideImage
3362
- ]);
3363
- return useMemo(() => ({
3364
- displayImage: overrideImage ?? displayImage,
3365
- displayTitle,
3366
- groupChannelDisplayInfo
3367
- }), [
3368
- displayImage,
3369
- displayTitle,
3370
- groupChannelDisplayInfo,
3371
- overrideImage
3372
- ]);
3373
- };
3374
- //#endregion
3375
- //#region src/utils/isDmChannel.ts
3376
- var isDmChannel = ({ channel, ownUserId }) => {
3377
- const memberCount = channel.data?.member_count ?? 0;
3378
- return memberCount === 1 || memberCount === 2 && !!ownUserId && Object.values(channel.state?.members ?? {}).some(({ user }) => user?.id === ownUserId);
3379
- };
3380
- //#endregion
3381
- //#region src/components/ChannelList/hooks/useSelectedChannelState.ts
3382
- var noop = () => {};
3383
- function useSelectedChannelState({ channel, selector, stateChangeEventKeys = ["all"] }) {
3384
- return useSyncExternalStore(useCallback((onStoreChange) => {
3385
- if (!channel) return noop;
3386
- const subscriptions = stateChangeEventKeys.map((et) => channel.on(et, () => {
3387
- onStoreChange(selector(channel));
3388
- }));
3389
- return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
3390
- }, [
3391
- channel,
3392
- selector,
3393
- stateChangeEventKeys
3394
- ]), useCallback(() => {
3395
- if (!channel) return void 0;
3396
- return selector(channel);
3397
- }, [channel, selector]));
3398
- }
3399
- //#endregion
3400
- //#region src/components/ChannelList/hooks/useChannelMembershipState.ts
3401
- var selector = (c) => c.state.membership;
3402
- var keys = ["member.updated"];
3403
- function useChannelMembershipState(channel) {
3404
- return useSelectedChannelState({
3405
- channel,
3406
- selector,
3407
- stateChangeEventKeys: keys
3408
- });
3409
- }
3410
- //#endregion
3411
- //#region src/components/ChannelListItem/hooks/useIsChannelMuted.ts
3412
- /**
3413
- * `channel.muteStatus()` throws in two cases:
3414
- * - the channel has not been initialized yet (i.e. `.watch()`/`.query()` has not
3415
- * resolved) - the ChannelList can briefly hold such channels, e.g. a
3416
- * `message.new` for a channel that has not been watched yet (#2474);
3417
- * - the channel is disconnected (e.g. after `client.disconnectUser()`), in which
3418
- * case `channel.getClient()` throws even though the channel stays initialized
3419
- * (#2393 failure class).
3420
- * Guard against both to avoid crashing the whole app.
3421
- */
3422
- var getMuteStatus = (channel) => channel.initialized && !channel.disconnected ? channel.muteStatus() : {
3423
- createdAt: null,
3424
- expiresAt: null,
3425
- muted: false
3426
- };
3427
- var useIsChannelMuted = (channel) => {
3428
- const { client } = useChatContext("useIsChannelMuted");
3429
- const [muted, setMuted] = useState(() => getMuteStatus(channel));
3430
- useEffect(() => {
3431
- const handleEvent = () => {
3432
- setMuted(getMuteStatus(channel));
3433
- };
3434
- return client.on("notification.channel_mutes_updated", handleEvent).unsubscribe;
3435
- }, [channel, client]);
3436
- return muted;
3437
- };
3438
- //#endregion
3439
- //#region src/components/ChannelListItem/hooks/useIsUserMuted.ts
3440
- var useIsUserMuted = (targetUserId) => {
3441
- const { mutes } = useChatContext();
3442
- return useMemo(() => !!targetUserId && mutes.some((mute) => mute.target.id === targetUserId), [mutes, targetUserId]);
3443
- };
3444
- //#endregion
3445
- //#region src/components/Notifications/Notification.tsx
3446
- var IconsBySeverity = {
3447
- error: IconExclamationMark,
3448
- info: null,
3449
- loading: IconRefresh,
3450
- success: IconCheckmark,
3451
- warning: IconExclamationTriangleFill
3452
- };
3453
- var DefaultNotificationIcon = ({ notification }) => {
3454
- if (!notification.severity) return null;
3455
- const Icon = IconsBySeverity[notification.severity] ?? null;
3456
- return Icon && /* @__PURE__ */ jsx("div", {
3457
- className: "str-chat__notification-icon",
3458
- children: /* @__PURE__ */ jsx(Icon, {})
3459
- });
3460
- };
3461
- var Notification = forwardRef(({ className, entryDirection, Icon = DefaultNotificationIcon, notification, onDismiss, showClose = false, transitionState }, ref) => {
3462
- const { removeNotification } = useNotificationApi();
3463
- const { t } = useTranslationContext();
3464
- const displayMessage = t("translationBuilderTopic/notification", {
3465
- notification,
3466
- value: notification.message
3467
- });
3468
- const handleDismiss = () => {
3469
- if (onDismiss) {
3470
- onDismiss();
3471
- return;
3472
- }
3473
- removeNotification(notification.id);
3474
- };
3475
- const isPersistent = !notification.duration;
3476
- const severity = notification.severity;
3477
- const ariaLiveOverride = notification.metadata?.ariaLive;
3478
- const livePriority = ariaLiveOverride === "assertive" || ariaLiveOverride === "polite" ? ariaLiveOverride : severity === "error" ? "assertive" : "polite";
3479
- return /* @__PURE__ */ jsxs("div", {
3480
- className: clsx("str-chat__notification", entryDirection && `str-chat__notification--enter-from-${entryDirection}`, transitionState === "enter" && "str-chat__notification--is-entering", transitionState === "exit" && "str-chat__notification--is-exiting", severity && `str-chat__notification--${severity}`, severity === "loading" && "str-chat__notification--loading", className),
3481
- "data-testid": "notification",
3482
- ref,
3483
- children: [
3484
- /* @__PURE__ */ jsxs("div", {
3485
- className: "str-chat__notification-content",
3486
- children: [Icon && /* @__PURE__ */ jsx(Icon, { notification }), /* @__PURE__ */ jsx("div", {
3487
- "aria-atomic": "true",
3488
- "aria-live": livePriority,
3489
- className: "str-chat__notification-message",
3490
- role: livePriority === "assertive" ? "alert" : "status",
3491
- children: displayMessage
3492
- })]
3493
- }),
3494
- notification.actions && notification.actions.length > 0 && /* @__PURE__ */ jsx("div", {
3495
- className: "str-chat__notification-actions",
3496
- children: notification.actions.map((action, index) => /* @__PURE__ */ jsx(Button, {
3497
- appearance: "outline",
3498
- className: "str-chat__notification-action",
3499
- inverseTheme: true,
3500
- onClick: () => {
3501
- action.handler();
3502
- },
3503
- size: "sm",
3504
- variant: "secondary",
3505
- children: action.label
3506
- }, index))
3507
- }),
3508
- (showClose || isPersistent) && /* @__PURE__ */ jsx(Button, {
3509
- appearance: "ghost",
3510
- "aria-label": t("aria/Dismiss notification"),
3511
- circular: true,
3512
- className: "str-chat__notification-close-button",
3513
- inverseTheme: true,
3514
- onClick: handleDismiss,
3515
- size: "sm",
3516
- variant: "secondary",
3517
- children: /* @__PURE__ */ jsx(IconXmark, {})
3518
- })
3519
- ]
3520
- });
3521
- });
3522
- Notification.displayName = "Notification";
3523
- //#endregion
3524
- //#region src/components/Notifications/NotificationList.tsx
3525
- var ENTER_TRANSLATION = {
3526
- bottom: {
3527
- x: "0%",
3528
- y: "100%"
3529
- },
3530
- left: {
3531
- x: "-100%",
3532
- y: "0%"
3533
- },
3534
- right: {
3535
- x: "100%",
3536
- y: "0%"
3537
- },
3538
- top: {
3539
- x: "0%",
3540
- y: "-100%"
3541
- }
3542
- };
3543
- var EXIT_ANIMATION_MS = 340;
3544
- var DEFAULT_MIN_DISPLAY_MS = 1e3;
3545
- var isEnterFrom = (value) => value === "bottom" || value === "left" || value === "right" || value === "top";
3546
- var getNotificationEnterFrom = (notification, fallbackEnterFrom) => {
3547
- if (!notification) return fallbackEnterFrom;
3548
- const metadataEnterFrom = notification.metadata?.entryDirection;
3549
- if (isEnterFrom(metadataEnterFrom)) return metadataEnterFrom;
3550
- const originEnterFrom = notification.origin.context?.entryDirection;
3551
- if (isEnterFrom(originEnterFrom)) return originEnterFrom;
3552
- return fallbackEnterFrom;
3553
- };
3554
- var isPersistent = (notification) => !notification.duration;
3555
- var haveSameType = (a, b) => !!a?.type && !!b?.type && a.type === b.type;
3556
- /** FIFO queue selector — oldest `createdAt` other than `displayed` wins. */
3557
- var pickOldest = (notifications, displayed) => {
3558
- const excludeId = displayed?.id ?? null;
3559
- let oldest = null;
3560
- for (const notification of notifications) {
3561
- if (notification.id === excludeId) continue;
3562
- if (!oldest || notification.createdAt < oldest.createdAt) oldest = notification;
3563
- }
3564
- return oldest;
3565
- };
3566
- /** LIFO queue selector — newest `createdAt` other than `displayed` wins. */
3567
- var pickNewest = (notifications, displayed) => {
3568
- const excludeId = displayed?.id ?? null;
3569
- let newest = null;
3570
- for (const notification of notifications) {
3571
- if (notification.id === excludeId) continue;
3572
- if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3573
- }
3574
- return newest;
3575
- };
3576
- var pickNewestPersistent = (notifications, excludeId) => {
3577
- let newest = null;
3578
- for (const notification of notifications) {
3579
- if (notification.id === excludeId) continue;
3580
- if (!isPersistent(notification)) continue;
3581
- if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3582
- }
3583
- return newest;
3584
- };
3585
- var pickNewestOfType = (notifications, type, excludeId) => {
3586
- if (!type) return null;
3587
- let newest = null;
3588
- for (const notification of notifications) {
3589
- if (notification.id === excludeId) continue;
3590
- if (notification.type !== type) continue;
3591
- if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3592
- }
3593
- return newest;
3594
- };
3595
- /**
3596
- * Builds the default `PickNextNotification` selector with a configurable queue fallback.
3597
- * Encodes the snackbar concurrency rules from the design spec — the scheduling effect
3598
- * only decides *when* to swap, the returned function decides *what* to swap to.
3599
- *
3600
- * Rules, in order of precedence:
3601
- * 1. **Persistent wins.** Persistent variants (no `duration`) jump ahead of any
3602
- * queued transient because they carry an action the user must acknowledge.
3603
- * 2. **Persistent ↛ replaced by transient.** While a persistent is displayed it stays
3604
- * put until dismissed externally or a *newer* persistent arrives.
3605
- * 3. **Same-type refresh.** While a transient is displayed, a repeated trigger of the
3606
- * same `type` collapses to its latest occurrence. (The scheduling effect detects
3607
- * this via `haveSameType` and bypasses the dwell window so the refresh feels
3608
- * instant; this function just makes sure the latest same-type is returned.)
3609
- * 4. **Queue fallback.** Otherwise, `pickFromQueue` selects the next notification
3610
- * (default: `pickOldest` / FIFO).
3611
- *
3612
- * Exported so callers that want to layer behavior on top of the design rules can wrap
3613
- * the result instead of rewriting it.
3614
- */
3615
- var createDefaultPickNext = (pickFromQueue = pickOldest) => (notifications, displayed) => {
3616
- if (notifications.length === 0) return null;
3617
- const newestPersistent = pickNewestPersistent(notifications, null);
3618
- if (!displayed) return newestPersistent ?? pickFromQueue(notifications, null);
3619
- if (!notifications.some(({ id }) => id === displayed.id)) return newestPersistent ?? pickFromQueue(notifications, null);
3620
- if (isPersistent(displayed)) {
3621
- const newerPersistent = newestPersistent && newestPersistent.id !== displayed.id ? newestPersistent : pickNewestPersistent(notifications, displayed.id);
3622
- if (newerPersistent && newerPersistent.createdAt > displayed.createdAt) return newerPersistent;
3623
- return displayed;
3624
- }
3625
- const sameTypeNewest = pickNewestOfType(notifications, displayed.type, displayed.id);
3626
- if (sameTypeNewest) return sameTypeNewest;
3627
- if (newestPersistent) return newestPersistent;
3628
- return pickFromQueue(notifications, displayed) ?? displayed;
3629
- };
3630
- /** Default selector — design-spec rules with FIFO queue fallback (`pickOldest`). */
3631
- var defaultPickNext = createDefaultPickNext(pickOldest);
3632
- var NotificationList = ({ className, enterFrom = "bottom", fallbackPanel, filter, minDisplayMs = DEFAULT_MIN_DISPLAY_MS, panel, pickNext = defaultPickNext, verticalAlignment = "bottom" }) => {
3633
- const { Notification: NotificationComponent = Notification } = useComponentContext();
3634
- const { t } = useTranslationContext();
3635
- const { removeNotification, startNotificationTimeout } = useNotificationApi();
3636
- const exitTimeoutRef = useRef(null);
3637
- const replacementTimeoutRef = useRef(null);
3638
- const candidateRef = useRef(null);
3639
- const displayedAtRef = useRef(null);
3640
- const listRef = useRef(null);
3641
- const observedElementRef = useRef(null);
3642
- const startedTimeoutIdsRef = useRef(null);
3643
- if (!startedTimeoutIdsRef.current) startedTimeoutIdsRef.current = /* @__PURE__ */ new Set();
3644
- const [displayedNotification, setDisplayedNotification] = useState(null);
3645
- const [transitionState, setTransitionState] = useState("enter");
3646
- const notifications = useNotifications({
3647
- applyDisplayFilter: true,
3648
- fallbackPanel,
3649
- filter: useCallback((notification) => {
3650
- if (hasSystemNotificationTag(notification)) return false;
3651
- return filter ? filter(notification) : true;
3652
- }, [filter]),
3653
- panel
3654
- });
3655
- const dismiss = useCallback((id) => {
3656
- startedTimeoutIdsRef.current?.delete(id);
3657
- removeNotification(id);
3658
- }, [removeNotification]);
3659
- useEffect(() => {
3660
- const notificationIds = new Set(notifications.map(({ id }) => id));
3661
- startedTimeoutIdsRef.current?.forEach((id) => {
3662
- if (!notificationIds.has(id)) startedTimeoutIdsRef.current?.delete(id);
3663
- });
3664
- }, [notifications]);
3665
- const clearReplacementTimeout = useCallback(() => {
3666
- if (replacementTimeoutRef.current !== null) {
3667
- window.clearTimeout(replacementTimeoutRef.current);
3668
- replacementTimeoutRef.current = null;
3669
- }
3670
- }, []);
3671
- useEffect(() => () => {
3672
- clearReplacementTimeout();
3673
- if (exitTimeoutRef.current !== null) {
3674
- window.clearTimeout(exitTimeoutRef.current);
3675
- exitTimeoutRef.current = null;
3676
- }
3677
- }, [clearReplacementTimeout]);
3678
- useEffect(() => {
3679
- candidateRef.current = pickNext(notifications, displayedNotification);
3680
- }, [
3681
- displayedNotification,
3682
- notifications,
3683
- pickNext
3684
- ]);
3685
- useEffect(() => {
3686
- if (transitionState === "exit") return;
3687
- if (!displayedNotification) {
3688
- const candidate = pickNext(notifications, null);
3689
- if (candidate) {
3690
- displayedAtRef.current = Date.now();
3691
- setDisplayedNotification(candidate);
3692
- setTransitionState("enter");
3693
- }
3694
- return;
3695
- }
3696
- const candidate = pickNext(notifications, displayedNotification);
3697
- if (!candidate) {
3698
- clearReplacementTimeout();
3699
- setTransitionState("exit");
3700
- exitTimeoutRef.current = window.setTimeout(() => {
3701
- exitTimeoutRef.current = null;
3702
- displayedAtRef.current = null;
3703
- setDisplayedNotification(null);
3704
- setTransitionState("enter");
3705
- }, EXIT_ANIMATION_MS);
3706
- return;
3707
- }
3708
- if (candidate.id === displayedNotification.id) {
3709
- clearReplacementTimeout();
3710
- return;
3711
- }
3712
- const displayedInStore = notifications.some(({ id }) => id === displayedNotification.id);
3713
- const startSwap = () => {
3714
- replacementTimeoutRef.current = null;
3715
- setTransitionState("exit");
3716
- const previousId = displayedNotification.id;
3717
- const wasInStore = displayedInStore;
3718
- exitTimeoutRef.current = window.setTimeout(() => {
3719
- exitTimeoutRef.current = null;
3720
- if (wasInStore) {
3721
- startedTimeoutIdsRef.current?.delete(previousId);
3722
- removeNotification(previousId);
3723
- }
3724
- const next = candidateRef.current;
3725
- if (next && next.id !== previousId) {
3726
- displayedAtRef.current = Date.now();
3727
- setDisplayedNotification(next);
3728
- setTransitionState("enter");
3729
- } else {
3730
- displayedAtRef.current = null;
3731
- setDisplayedNotification(null);
3732
- setTransitionState("enter");
3733
- }
3734
- }, EXIT_ANIMATION_MS);
3735
- };
3736
- if (!displayedInStore) {
3737
- clearReplacementTimeout();
3738
- startSwap();
3739
- return;
3740
- }
3741
- if (haveSameType(displayedNotification, candidate)) {
3742
- clearReplacementTimeout();
3743
- startSwap();
3744
- return;
3745
- }
3746
- const elapsed = displayedAtRef.current ? Date.now() - displayedAtRef.current : 0;
3747
- const remaining = Math.max(0, minDisplayMs - elapsed);
3748
- if (remaining === 0) {
3749
- clearReplacementTimeout();
3750
- startSwap();
3751
- } else if (replacementTimeoutRef.current === null) replacementTimeoutRef.current = window.setTimeout(startSwap, remaining);
3752
- }, [
3753
- clearReplacementTimeout,
3754
- displayedNotification,
3755
- minDisplayMs,
3756
- notifications,
3757
- pickNext,
3758
- removeNotification,
3759
- transitionState
3760
- ]);
3761
- const notification = displayedNotification;
3762
- const notificationEnterFrom = getNotificationEnterFrom(notification, enterFrom);
3763
- useEffect(() => {
3764
- const element = observedElementRef.current;
3765
- if (!element || !notification || transitionState === "exit") return;
3766
- const startTimeout = () => {
3767
- if (!startedTimeoutIdsRef.current || startedTimeoutIdsRef.current.has(notification.id)) return;
3768
- startedTimeoutIdsRef.current.add(notification.id);
3769
- startNotificationTimeout(notification.id);
3770
- };
3771
- if (typeof IntersectionObserver === "undefined") {
3772
- startTimeout();
3773
- return;
3774
- }
3775
- const observer = new IntersectionObserver((entries) => {
3776
- const [entry] = entries;
3777
- if (!entry?.isIntersecting) return;
3778
- startTimeout();
3779
- observer.disconnect();
3780
- }, {
3781
- root: listRef.current,
3782
- threshold: .5
3783
- });
3784
- observer.observe(element);
3785
- return () => {
3786
- observer.disconnect();
3787
- };
3788
- }, [
3789
- notification,
3790
- startNotificationTimeout,
3791
- transitionState
3792
- ]);
3793
- if (!notification) return null;
3794
- return /* @__PURE__ */ jsxs("div", {
3795
- "aria-label": t("aria/Notifications"),
3796
- className: clsx("str-chat__notification-list", `str-chat__notification-list--enter-from-${notificationEnterFrom}`, `str-chat__notification-list--position-${verticalAlignment}`, panel && `str-chat__notification-list--${panel}`, className),
3797
- "data-testid": "notification-list",
3798
- ref: listRef,
3799
- role: "region",
3800
- style: {
3801
- "--str-chat__notification-list-enter-x": ENTER_TRANSLATION[notificationEnterFrom].x,
3802
- "--str-chat__notification-list-enter-y": ENTER_TRANSLATION[notificationEnterFrom].y
3803
- },
3804
- children: [
3805
- /* @__PURE__ */ jsx("div", {
3806
- "aria-hidden": true,
3807
- className: "str-chat__notification-list__edge str-chat__notification-list__edge--top"
3808
- }),
3809
- /* @__PURE__ */ jsx(NotificationComponent, {
3810
- entryDirection: notificationEnterFrom,
3811
- notification,
3812
- onDismiss: () => dismiss(notification.id),
3813
- ref: (element) => {
3814
- observedElementRef.current = element;
3815
- },
3816
- showClose: !notification.duration,
3817
- transitionState
3818
- }, notification.id),
3819
- /* @__PURE__ */ jsx("div", {
3820
- "aria-hidden": true,
3821
- className: "str-chat__notification-list__edge str-chat__notification-list__edge--bottom"
3822
- })
3823
- ]
3824
- });
3825
- };
3826
- //#endregion
3827
- //#region src/components/ChannelHeader/hooks/useChannelHasMembersOnline.ts
3828
- var getOtherWatchers = (watchers, ownUserId) => {
3829
- const next = Object.assign({}, watchers ?? {});
3830
- if (ownUserId) delete next[ownUserId];
3831
- return next;
3832
- };
3833
- var useChannelHasMembersOnline = ({ channel: channelOverride, enabled = true } = {}) => {
3834
- const { channel: contextChannel } = useChannelStateContext();
3835
- const { client } = useChatContext();
3836
- const channel = channelOverride ?? contextChannel;
3837
- const ownUserId = client.user?.id;
3838
- const [watchers, setWatchers] = useState(() => getOtherWatchers(channel?.state?.watchers, ownUserId));
3839
- useEffect(() => {
3840
- setWatchers(getOtherWatchers(channel?.state?.watchers, ownUserId));
3841
- }, [channel, ownUserId]);
3842
- useEffect(() => {
3843
- if (!enabled || !channel) return;
3844
- const startSubscription = channel.on("user.watching.start", (event) => {
3845
- setWatchers((prev) => {
3846
- if (!event.user?.id || event.user.id === ownUserId) return prev;
3847
- if (prev[event.user.id]) return prev;
3848
- return Object.assign({ [event.user.id]: event.user }, prev);
3849
- });
3850
- });
3851
- const stopSubscription = channel.on("user.watching.stop", (event) => {
3852
- setWatchers((prev) => {
3853
- if (!event.user?.id || !prev[event.user.id]) return prev;
3854
- const next = Object.assign({}, prev);
3855
- delete next[event.user.id];
3856
- return next;
3857
- });
3858
- });
3859
- return () => {
3860
- startSubscription.unsubscribe();
3861
- stopSubscription.unsubscribe();
3862
- };
3863
- }, [
3864
- channel,
3865
- enabled,
3866
- ownUserId
3867
- ]);
3868
- if (!enabled) return false;
3869
- return Object.keys(watchers).length > 0;
3870
- };
3871
- //#endregion
3872
- //#region src/components/ChannelHeader/hooks/useChannelHeaderOnlineStatus.ts
3873
- /**
3874
- * Returns the channel header online status text (e.g. "Online", "Offline", or "X members, Y online").
3875
- * Returns null when the channel has no members (nothing to show).
3876
- */
3877
- function useChannelHeaderOnlineStatus({ channel: channelOverride, watcherCount: watcherCountOverride } = {}) {
3878
- const { t } = useTranslationContext();
3879
- const { client } = useChatContext();
3880
- const { channel: contextChannel, watcherCount: contextWatcherCount = 0 } = useChannelStateContext();
3881
- const channel = channelOverride ?? contextChannel;
3882
- const watcherCount = watcherCountOverride ?? contextWatcherCount;
3883
- const { member_count: memberCount = 0 } = channel?.data || {};
3884
- const isDirectMessagingChannel = isDmChannel({
3885
- channel,
3886
- ownUserId: client.user?.id
3887
- });
3888
- const hasMembersOnline = useChannelHasMembersOnline({
3889
- channel,
3890
- enabled: isDirectMessagingChannel
3891
- });
3892
- if (!memberCount) return null;
3893
- if (isDirectMessagingChannel) return hasMembersOnline ? t("Online") : t("Offline");
3894
- return `${t("{{ memberCount }} members", { memberCount })} · ${t("{{ watcherCount }} online", { watcherCount })}`;
3895
- }
3896
- //#endregion
3897
- export { BaseContextMenuButton as $, Gallery as A, AriaLiveOutlet as At, formatTime as B, useMessageTranslationViewContext as Bt, Checkbox as C, useAriaIdentifiers as Ct, SwitchFieldDescription as D, useNotifications as Dt, SwitchField as E, LoadingIndicator as Et, toGalleryItemDescriptors as F, ModalContextProvider as Ft, MessageTimestamp as G, FILE_ICON_GRAPHIC_CLASSNAME as H, MessageProvider as Ht, useGalleryContext as I, useModalContext as It, imageToLink as J, Timestamp as K, toBaseImageDescriptors as L, MessageTranslationViewContext as Lt, VideoThumbnail as M, AriaLiveOutletContext as Mt, VideoPlayer as N, useAriaLiveOutletContext as Nt, SwitchFieldLabel as O, NotificationConfigurationProvider as Ot, GalleryContext as P, ModalContext as Pt, Prompt as Q, BaseImage as R, MessageTranslationViewProvider as Rt, TextInput as S, Root as St, Switch as T, FileSizeIndicator as Tt, FILE_ICON_NO_LABEL_CLASSNAME as U, useMessageContext as Ut, FileIcon as V, MessageContext as Vt, DownloadButton as W, extractDisplayInfo as X, htmlToTextPlugin as Y, GlobalModal as Z, getLatestMessagePreview as _, Avatar as _t, pickNewest as a, ContextMenuHeader as at, ListItemLayout as b, Alert as bt, useIsUserMuted as c, EmojiContextMenuButton as ct, useSelectedChannelState as d, createRovingFocusKeyDownHandler as dt, ContextMenu as et, isDmChannel as f, getNextRovingFocusIndex as ft, getGroupChannelDisplayInfo as g, ErrorBadge as gt, getChannelDisplayImage as h, Badge as ht, createDefaultPickNext as i, ContextMenuContent as it, GalleryUI as j, VisuallyHidden as jt, SwitchFieldTitle as k, useNotificationConfigurationContext as kt, useIsChannelMuted as l, UserContextMenuButton as lt, useChannelDisplayName as m, ChannelAvatar as mt, useChannelHasMembersOnline as n, ContextMenuBody as nt, pickOldest as o, ContextMenuRoot as ot, useChannelPreviewInfo as p, GroupAvatar as pt, plusPlusToEmphasis as q, NotificationList as r, ContextMenuButton as rt, Notification as s, DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR as st, useChannelHeaderOnlineStatus as t, ContextMenuBackButton as tt, useChannelMembershipState as u, useContextMenuContext as ut, getLatestMessagePreviewText as v, DialogAnchor as vt, Checkmark as w, useStableCallback as wt, ListItemLayoutContent as x, Header as xt, renderPreviewText as y, useDialogAnchor as yt, ImagePlaceholder as z, getTranslatedMessageText as zt };
3898
-
3899
- //# sourceMappingURL=useChannelHeaderOnlineStatus.d6065c22.mjs.map