stream-chat-react 12.4.1 → 12.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/icons/stream-chat-icons.eot +0 -0
- package/dist/assets/icons/stream-chat-icons.svg +4 -0
- package/dist/assets/icons/stream-chat-icons.ttf +0 -0
- package/dist/assets/icons/stream-chat-icons.woff +0 -0
- package/dist/assets/icons/stream-chat-icons.woff2 +0 -0
- package/dist/components/Attachment/components/ProgressBar.d.ts +2 -2
- package/dist/components/Attachment/components/ProgressBar.js +2 -1
- package/dist/components/Channel/Channel.d.ts +1 -1
- package/dist/components/Channel/Channel.js +36 -15
- package/dist/components/Channel/constants.d.ts +1 -0
- package/dist/components/Channel/constants.js +1 -0
- package/dist/components/Channel/hooks/useChannelContainerClasses.d.ts +2 -0
- package/dist/components/Channel/hooks/useChannelContainerClasses.js +10 -5
- package/dist/components/ChannelPreview/utils.js +35 -0
- package/dist/components/Chat/hooks/useChat.js +2 -0
- package/dist/components/Dialog/DialogAnchor.d.ts +1 -2
- package/dist/components/Dialog/DialogMenu.d.ts +3 -0
- package/dist/components/Dialog/DialogMenu.js +5 -0
- package/dist/components/Dialog/DialogPortal.d.ts +1 -1
- package/dist/components/Dialog/DialogPortal.js +4 -12
- package/dist/components/Dialog/FormDialog.d.ts +23 -0
- package/dist/components/Dialog/FormDialog.js +72 -0
- package/dist/components/Dialog/PromptDialog.d.ts +8 -0
- package/dist/components/Dialog/PromptDialog.js +7 -0
- package/dist/components/DragAndDrop/DragAndDropContainer.d.ts +7 -0
- package/dist/components/DragAndDrop/DragAndDropContainer.js +93 -0
- package/dist/components/Form/FieldError.d.ts +6 -0
- package/dist/components/Form/FieldError.js +3 -0
- package/dist/components/Form/SwitchField.d.ts +7 -0
- package/dist/components/Form/SwitchField.js +21 -0
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.d.ts +10 -0
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +10 -0
- package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts +10 -0
- package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.js +68 -0
- package/dist/components/InfiniteScrollPaginator/hooks/useCursorPaginator.d.ts +18 -0
- package/dist/components/InfiniteScrollPaginator/hooks/useCursorPaginator.js +41 -0
- package/dist/components/Message/MessageSimple.js +5 -1
- package/dist/components/Message/QuotedMessage.js +8 -4
- package/dist/components/Message/hooks/useUserRole.js +3 -2
- package/dist/components/Message/index.d.ts +1 -0
- package/dist/components/MessageInput/AttachmentSelector.d.ts +25 -0
- package/dist/components/MessageInput/AttachmentSelector.js +125 -0
- package/dist/components/MessageInput/EditMessageForm.js +1 -1
- package/dist/components/MessageInput/MessageInput.d.ts +2 -0
- package/dist/components/MessageInput/MessageInput.js +9 -4
- package/dist/components/MessageInput/MessageInputFlat.js +4 -10
- package/dist/components/MessageInput/QuotedMessagePreview.js +7 -3
- package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +3 -1
- package/dist/components/MessageInput/hooks/useSubmitHandler.js +4 -1
- package/dist/components/MessageInput/index.d.ts +1 -0
- package/dist/components/MessageInput/index.js +1 -0
- package/dist/components/Modal/ModalHeader.d.ts +8 -0
- package/dist/components/Modal/ModalHeader.js +6 -0
- package/dist/components/Poll/Poll.d.ts +7 -0
- package/dist/components/Poll/Poll.js +8 -0
- package/dist/components/Poll/PollActions/AddCommentForm.d.ts +7 -0
- package/dist/components/Poll/PollActions/AddCommentForm.js +24 -0
- package/dist/components/Poll/PollActions/EndPollDialog.d.ts +6 -0
- package/dist/components/Poll/PollActions/EndPollDialog.js +19 -0
- package/dist/components/Poll/PollActions/PollAction.d.ts +9 -0
- package/dist/components/Poll/PollActions/PollAction.js +5 -0
- package/dist/components/Poll/PollActions/PollActions.d.ts +17 -0
- package/dist/components/Poll/PollActions/PollActions.js +46 -0
- package/dist/components/Poll/PollActions/PollAnswerList.d.ts +7 -0
- package/dist/components/Poll/PollActions/PollAnswerList.js +28 -0
- package/dist/components/Poll/PollActions/PollOptionsFullList.d.ts +6 -0
- package/dist/components/Poll/PollActions/PollOptionsFullList.js +16 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionVotesList.d.ts +7 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionVotesList.js +18 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionWithLatestVotes.d.ts +9 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionWithLatestVotes.js +19 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.d.ts +11 -0
- package/dist/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.js +18 -0
- package/dist/components/Poll/PollActions/PollResults/PollResults.d.ts +6 -0
- package/dist/components/Poll/PollActions/PollResults/PollResults.js +33 -0
- package/dist/components/Poll/PollActions/PollResults/index.d.ts +1 -0
- package/dist/components/Poll/PollActions/PollResults/index.js +1 -0
- package/dist/components/Poll/PollActions/SuggestPollOptionForm.d.ts +7 -0
- package/dist/components/Poll/PollActions/SuggestPollOptionForm.js +37 -0
- package/dist/components/Poll/PollActions/index.d.ts +7 -0
- package/dist/components/Poll/PollActions/index.js +7 -0
- package/dist/components/Poll/PollContent.d.ts +3 -0
- package/dist/components/Poll/PollContent.js +18 -0
- package/dist/components/Poll/PollCreationDialog/OptionFieldSet.d.ts +9 -0
- package/dist/components/Poll/PollCreationDialog/OptionFieldSet.js +70 -0
- package/dist/components/Poll/PollCreationDialog/PollCreationDialog.d.ts +5 -0
- package/dist/components/Poll/PollCreationDialog/PollCreationDialog.js +87 -0
- package/dist/components/Poll/PollCreationDialog/PollCreationDialogControls.d.ts +8 -0
- package/dist/components/Poll/PollCreationDialog/PollCreationDialogControls.js +44 -0
- package/dist/components/Poll/PollCreationDialog/index.d.ts +1 -0
- package/dist/components/Poll/PollCreationDialog/index.js +1 -0
- package/dist/components/Poll/PollCreationDialog/types.d.ts +21 -0
- package/dist/components/Poll/PollCreationDialog/types.js +1 -0
- package/dist/components/Poll/PollHeader.d.ts +3 -0
- package/dist/components/Poll/PollHeader.js +31 -0
- package/dist/components/Poll/PollOptionList.d.ts +6 -0
- package/dist/components/Poll/PollOptionList.js +14 -0
- package/dist/components/Poll/PollOptionSelector.d.ts +19 -0
- package/dist/components/Poll/PollOptionSelector.js +53 -0
- package/dist/components/Poll/PollVote.d.ts +12 -0
- package/dist/components/Poll/PollVote.js +31 -0
- package/dist/components/Poll/QuotedPoll.d.ts +3 -0
- package/dist/components/Poll/QuotedPoll.js +17 -0
- package/dist/components/Poll/constants.d.ts +3 -0
- package/dist/components/Poll/constants.js +3 -0
- package/dist/components/Poll/hooks/index.d.ts +2 -0
- package/dist/components/Poll/hooks/index.js +2 -0
- package/dist/components/Poll/hooks/useManagePollVotesRealtime.d.ts +4 -0
- package/dist/components/Poll/hooks/useManagePollVotesRealtime.js +36 -0
- package/dist/components/Poll/hooks/usePollAnswerPagination.d.ts +13 -0
- package/dist/components/Poll/hooks/usePollAnswerPagination.js +27 -0
- package/dist/components/Poll/hooks/usePollOptionVotesPagination.d.ts +13 -0
- package/dist/components/Poll/hooks/usePollOptionVotesPagination.js +27 -0
- package/dist/components/Poll/index.d.ts +10 -0
- package/dist/components/Poll/index.js +10 -0
- package/dist/components/Portal/Portal.d.ts +6 -0
- package/dist/components/Portal/Portal.js +14 -0
- package/dist/components/ReactFileUtilities/UploadButton.d.ts +11 -1
- package/dist/components/ReactFileUtilities/UploadButton.js +22 -4
- package/dist/components/Thread/Thread.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -0
- package/dist/context/AttachmentSelectorContext.d.ts +8 -0
- package/dist/context/AttachmentSelectorContext.js +6 -0
- package/dist/context/ComponentContext.d.ts +21 -5
- package/dist/context/PollContext.d.ts +11 -0
- package/dist/context/PollContext.js +7 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/experimental/index.browser.cjs +129 -117
- package/dist/experimental/index.browser.cjs.map +4 -4
- package/dist/experimental/index.node.cjs +129 -117
- package/dist/experimental/index.node.cjs.map +4 -4
- package/dist/i18n/Streami18n.d.ts +45 -0
- package/dist/i18n/de.json +70 -25
- package/dist/i18n/en.json +46 -1
- package/dist/i18n/es.json +74 -25
- package/dist/i18n/fr.json +83 -34
- package/dist/i18n/hi.json +54 -9
- package/dist/i18n/it.json +75 -26
- package/dist/i18n/ja.json +46 -5
- package/dist/i18n/ko.json +46 -5
- package/dist/i18n/nl.json +59 -14
- package/dist/i18n/pt.json +66 -17
- package/dist/i18n/ru.json +66 -13
- package/dist/i18n/tr.json +77 -32
- package/dist/index.browser.cjs +4226 -1857
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.node.cjs +4166 -1770
- package/dist/index.node.cjs.map +4 -4
- package/dist/scss/v2/AttachmentPreviewList/AttachmentPreviewList-layout.scss +2 -2
- package/dist/scss/v2/AudioRecorder/AudioRecorder-layout.scss +64 -14
- package/dist/scss/v2/AudioRecorder/AudioRecorder-theme.scss +11 -1
- package/dist/scss/v2/Avatar/Avatar-layout.scss +4 -0
- package/dist/scss/v2/ChannelList/ChannelList-layout.scss +15 -0
- package/dist/scss/v2/Dialog/Dialog-layout.scss +54 -0
- package/dist/scss/v2/Dialog/Dialog-theme.scss +103 -0
- package/dist/scss/v2/DragAndDropContainer/DragAmdDropContainer-layout.scss +5 -0
- package/dist/scss/v2/DragAndDropContainer/DragAndDropContainer-theme.scss +47 -0
- package/dist/scss/v2/Form/Form-layout.scss +9 -0
- package/dist/scss/v2/Form/Form-theme.scss +17 -0
- package/dist/scss/v2/Icon/Icon-layout.scss +6 -1
- package/dist/scss/v2/InfiniteScrollPaginator/InfiniteScrollPaginator-layout.scss +4 -0
- package/dist/scss/v2/MessageActionsBox/MessageActionsBox-layout.scss +0 -9
- package/dist/scss/v2/MessageInput/MessageInput-layout.scss +29 -4
- package/dist/scss/v2/MessageInput/MessageInput-theme.scss +61 -0
- package/dist/scss/v2/Modal/Modal-layout.scss +31 -0
- package/dist/scss/v2/Modal/Modal-theme.scss +6 -0
- package/dist/scss/v2/Notification/MessageNotification-layout.scss +1 -1
- package/dist/scss/v2/Poll/Poll-layout.scss +488 -0
- package/dist/scss/v2/Poll/Poll-theme.scss +206 -0
- package/dist/scss/v2/_base.scss +4 -0
- package/dist/scss/v2/_global-theme-variables.scss +1 -1
- package/dist/scss/v2/_icons.scss +7 -0
- package/dist/scss/v2/index.layout.scss +4 -0
- package/dist/scss/v2/index.scss +4 -0
- package/dist/types/types.d.ts +6 -0
- package/package.json +4 -4
|
Binary file
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
<glyph glyph-name="send" unicode="" d="M167-67l833 417-833 417 0-324 595-93-595-93 0-324z" horiz-adv-x="1000" />
|
|
42
42
|
|
|
43
43
|
<glyph glyph-name="attach" unicode="" d="M542 558h-84v-166h-166v-84h166v-166h84v166h166v84h-166v166z m-42 209c-230 0-417-187-417-417 0-230 187-417 417-417 230 0 417 187 417 417 0 230-187 417-417 417z m0-750c-184 0-333 149-333 333 0 184 149 333 333 333 184 0 333-149 333-333 0-184-149-333-333-333z" horiz-adv-x="1000" />
|
|
44
|
+
|
|
45
|
+
<glyph glyph-name="mic" unicode="" d="M350 225c83 0 150 67 150 150v300c0 83-67 150-150 150-83 0-150-67-150-150v-300c0-83 67-150 150-150z m250 150c0-138-112-250-250-250-138 0-250 112-250 250h-100c0-176 131-321 300-346v-154h100v154c170 25 300 170 300 346h-100z" horiz-adv-x="700" />
|
|
46
|
+
|
|
47
|
+
<glyph glyph-name="bin" unicode="" d="M250 58c0-45 38-83 83-83h334c46 0 83 38 83 83v417c0 46-37 83-83 83h-334c-45 0-83-37-83-83v-417z m500 625h-104l-30 30c-7 7-18 12-29 12h-174c-11 0-22-5-29-12l-30-30h-104c-23 0-42-18-42-41 0-23 19-42 42-42h500c23 0 42 19 42 42 0 23-19 41-42 41z" horiz-adv-x="1000" />
|
|
44
48
|
</font>
|
|
45
49
|
</defs>
|
|
46
50
|
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,5 +2,5 @@ import React from 'react';
|
|
|
2
2
|
export type ProgressBarProps = {
|
|
3
3
|
/** Progress expressed in fractional number value btw 0 and 100. */
|
|
4
4
|
progress: number;
|
|
5
|
-
} & Pick<React.ComponentProps<'div'>, 'onClick'>;
|
|
6
|
-
export declare const ProgressBar: ({ onClick, progress }: ProgressBarProps) => React.JSX.Element;
|
|
5
|
+
} & Pick<React.ComponentProps<'div'>, 'className' | 'onClick'>;
|
|
6
|
+
export declare const ProgressBar: ({ className, onClick, progress }: ProgressBarProps) => React.JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
export const ProgressBar = ({ onClick, progress }) => (React.createElement("div", { className: 'str-chat__message-attachment-audio-widget--progress-track', "data-progress": progress, "data-testid": 'audio-progress', onClick: onClick, role: 'progressbar', style: {
|
|
3
|
+
export const ProgressBar = ({ className, onClick, progress }) => (React.createElement("div", { className: clsx('str-chat__message-attachment-audio-widget--progress-track', className), "data-progress": progress, "data-testid": 'audio-progress', onClick: onClick, role: 'progressbar', style: {
|
|
3
4
|
'--str-chat__message-attachment-audio-widget-progress': progress + '%',
|
|
4
5
|
} },
|
|
5
6
|
React.createElement("div", { className: 'str-chat__message-attachment-audio-widget--progress-slider', style: { left: `${progress}px` } })));
|
|
@@ -6,7 +6,7 @@ import { ComponentContextValue, StreamMessage } from '../../context';
|
|
|
6
6
|
import type { MessageInputProps } from '../MessageInput';
|
|
7
7
|
import type { ChannelUnreadUiState, CustomTrigger, DefaultStreamChatGenerics, GiphyVersions, ImageAttachmentSizeHandler, SendMessageOptions, UpdateMessageOptions, VideoAttachmentSizeHandler } from '../../types/types';
|
|
8
8
|
import type { URLEnrichmentConfig } from '../MessageInput/hooks/useLinkPreviews';
|
|
9
|
-
type ChannelPropsForwardedToComponentContext<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Pick<ComponentContextValue<StreamChatGenerics>, 'Attachment' | 'AttachmentPreviewList' | 'AudioRecorder' | 'AutocompleteSuggestionItem' | 'AutocompleteSuggestionList' | 'Avatar' | 'BaseImage' | 'CooldownTimer' | 'CustomMessageActionsList' | 'DateSeparator' | 'EditMessageInput' | 'EmojiPicker' | 'emojiSearchIndex' | 'EmptyStateIndicator' | 'FileUploadIcon' | 'GiphyPreviewMessage' | 'HeaderComponent' | 'Input' | 'LinkPreviewList' | 'LoadingIndicator' | 'Message' | 'MessageActions' | 'MessageBouncePrompt' | 'MessageDeleted' | 'MessageListNotifications' | 'MessageListMainPanel' | 'MessageNotification' | 'MessageOptions' | 'MessageRepliesCountButton' | 'MessageStatus' | 'MessageSystem' | 'MessageTimestamp' | 'ModalGallery' | 'PinIndicator' | 'QuotedMessage' | 'QuotedMessagePreview' | 'reactionOptions' | 'ReactionSelector' | 'ReactionsList' | 'SendButton' | 'StartRecordingAudioButton' | 'ThreadHead' | 'ThreadHeader' | 'ThreadStart' | 'Timestamp' | 'TriggerProvider' | 'TypingIndicator' | 'UnreadMessagesNotification' | 'UnreadMessagesSeparator' | 'VirtualMessage'>;
|
|
9
|
+
type ChannelPropsForwardedToComponentContext<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Pick<ComponentContextValue<StreamChatGenerics>, 'Attachment' | 'AttachmentPreviewList' | 'AttachmentSelector' | 'AttachmentSelectorInitiationButtonContents' | 'AudioRecorder' | 'AutocompleteSuggestionItem' | 'AutocompleteSuggestionList' | 'Avatar' | 'BaseImage' | 'CooldownTimer' | 'CustomMessageActionsList' | 'DateSeparator' | 'EditMessageInput' | 'EmojiPicker' | 'emojiSearchIndex' | 'EmptyStateIndicator' | 'FileUploadIcon' | 'GiphyPreviewMessage' | 'HeaderComponent' | 'Input' | 'LinkPreviewList' | 'LoadingIndicator' | 'Message' | 'MessageActions' | 'MessageBouncePrompt' | 'MessageDeleted' | 'MessageListNotifications' | 'MessageListMainPanel' | 'MessageNotification' | 'MessageOptions' | 'MessageRepliesCountButton' | 'MessageStatus' | 'MessageSystem' | 'MessageTimestamp' | 'ModalGallery' | 'PinIndicator' | 'PollActions' | 'PollContent' | 'PollCreationDialog' | 'PollHeader' | 'PollOptionSelector' | 'QuotedMessage' | 'QuotedMessagePreview' | 'QuotedPoll' | 'reactionOptions' | 'ReactionSelector' | 'ReactionsList' | 'SendButton' | 'StartRecordingAudioButton' | 'ThreadHead' | 'ThreadHeader' | 'ThreadStart' | 'Timestamp' | 'TriggerProvider' | 'TypingIndicator' | 'UnreadMessagesNotification' | 'UnreadMessagesSeparator' | 'VirtualMessage'>;
|
|
10
10
|
export type ChannelProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger> = ChannelPropsForwardedToComponentContext<StreamChatGenerics> & {
|
|
11
11
|
/** List of accepted file types */
|
|
12
12
|
acceptedFiles?: string[];
|
|
@@ -16,30 +16,35 @@ import { DropzoneProvider } from '../MessageInput/DropzoneProvider';
|
|
|
16
16
|
import { ChannelActionProvider, ChannelStateProvider, TypingProvider, useChatContext, useTranslationContext, WithComponents, } from '../../context';
|
|
17
17
|
import { DEFAULT_HIGHLIGHT_DURATION, DEFAULT_INITIAL_CHANNEL_PAGE_SIZE, DEFAULT_JUMP_TO_PAGE_SIZE, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, DEFAULT_THREAD_PAGE_SIZE, } from '../../constants/limits';
|
|
18
18
|
import { hasMoreMessagesProbably } from '../MessageList';
|
|
19
|
-
import { useChannelContainerClasses } from './hooks/useChannelContainerClasses';
|
|
19
|
+
import { getChatContainerClass, useChannelContainerClasses, useImageFlagEmojisOnWindowsClass, } from './hooks/useChannelContainerClasses';
|
|
20
20
|
import { findInMsgSetByDate, findInMsgSetById, makeAddNotifications } from './utils';
|
|
21
21
|
import { getChannel } from '../../utils';
|
|
22
22
|
import { getImageAttachmentConfiguration, getVideoAttachmentConfiguration, } from '../Attachment/attachment-sizing';
|
|
23
23
|
import { useThreadContext } from '../Threads';
|
|
24
|
+
import { CHANNEL_CONTAINER_ID } from './constants';
|
|
24
25
|
const isUserResponseArray = (output) => output[0]?.id != null;
|
|
25
|
-
const
|
|
26
|
-
const {
|
|
27
|
-
const { channel: contextChannel, channelsQueryState, customClasses, theme, } = useChatContext('Channel');
|
|
26
|
+
const ChannelContainer = ({ children, className: additionalClassName, ...props }) => {
|
|
27
|
+
const { customClasses, theme } = useChatContext('Channel');
|
|
28
28
|
const { channelClass, chatClass } = useChannelContainerClasses({
|
|
29
29
|
customClasses,
|
|
30
30
|
});
|
|
31
|
+
const className = clsx(chatClass, theme, channelClass, additionalClassName);
|
|
32
|
+
return (React.createElement("div", { id: CHANNEL_CONTAINER_ID, ...props, className: className }, children));
|
|
33
|
+
};
|
|
34
|
+
const UnMemoizedChannel = (props) => {
|
|
35
|
+
const { channel: propsChannel, EmptyPlaceholder = null, LoadingErrorIndicator, LoadingIndicator = DefaultLoadingIndicator, } = props;
|
|
36
|
+
const { channel: contextChannel, channelsQueryState } = useChatContext('Channel');
|
|
31
37
|
const channel = propsChannel || contextChannel;
|
|
32
|
-
const className = clsx(chatClass, theme, channelClass);
|
|
33
38
|
if (channelsQueryState.queryInProgress === 'reload' && LoadingIndicator) {
|
|
34
|
-
return (React.createElement(
|
|
39
|
+
return (React.createElement(ChannelContainer, null,
|
|
35
40
|
React.createElement(LoadingIndicator, null)));
|
|
36
41
|
}
|
|
37
42
|
if (channelsQueryState.error && LoadingErrorIndicator) {
|
|
38
|
-
return (React.createElement(
|
|
43
|
+
return (React.createElement(ChannelContainer, null,
|
|
39
44
|
React.createElement(LoadingErrorIndicator, { error: channelsQueryState.error })));
|
|
40
45
|
}
|
|
41
46
|
if (!channel?.cid) {
|
|
42
|
-
return React.createElement(
|
|
47
|
+
return React.createElement(ChannelContainer, null, EmptyPlaceholder);
|
|
43
48
|
}
|
|
44
49
|
return React.createElement(ChannelInner, { ...props, channel: channel, key: channel.cid });
|
|
45
50
|
};
|
|
@@ -48,9 +53,10 @@ const ChannelInner = (props) => {
|
|
|
48
53
|
const channelQueryOptions = useMemo(() => defaultsDeep(propChannelQueryOptions, {
|
|
49
54
|
messages: { limit: DEFAULT_INITIAL_CHANNEL_PAGE_SIZE },
|
|
50
55
|
}), [propChannelQueryOptions]);
|
|
51
|
-
const { client, customClasses, latestMessageDatesByChannels, mutes,
|
|
56
|
+
const { client, customClasses, latestMessageDatesByChannels, mutes, } = useChatContext('Channel');
|
|
52
57
|
const { t } = useTranslationContext('Channel');
|
|
53
|
-
const
|
|
58
|
+
const chatContainerClass = getChatContainerClass(customClasses?.chatContainer);
|
|
59
|
+
const windowsEmojiClass = useImageFlagEmojisOnWindowsClass();
|
|
54
60
|
const thread = useThreadContext();
|
|
55
61
|
const [channelConfig, setChannelConfig] = useState(channel.getConfig());
|
|
56
62
|
const [notifications, setNotifications] = useState([]);
|
|
@@ -730,6 +736,8 @@ const ChannelInner = (props) => {
|
|
|
730
736
|
const componentContextValue = useMemo(() => ({
|
|
731
737
|
Attachment: props.Attachment,
|
|
732
738
|
AttachmentPreviewList: props.AttachmentPreviewList,
|
|
739
|
+
AttachmentSelector: props.AttachmentSelector,
|
|
740
|
+
AttachmentSelectorInitiationButtonContents: props.AttachmentSelectorInitiationButtonContents,
|
|
733
741
|
AudioRecorder: props.AudioRecorder,
|
|
734
742
|
AutocompleteSuggestionItem: props.AutocompleteSuggestionItem,
|
|
735
743
|
AutocompleteSuggestionList: props.AutocompleteSuggestionList,
|
|
@@ -761,8 +769,14 @@ const ChannelInner = (props) => {
|
|
|
761
769
|
MessageTimestamp: props.MessageTimestamp,
|
|
762
770
|
ModalGallery: props.ModalGallery,
|
|
763
771
|
PinIndicator: props.PinIndicator,
|
|
772
|
+
PollActions: props.PollActions,
|
|
773
|
+
PollContent: props.PollContent,
|
|
774
|
+
PollCreationDialog: props.PollCreationDialog,
|
|
775
|
+
PollHeader: props.PollHeader,
|
|
776
|
+
PollOptionSelector: props.PollOptionSelector,
|
|
764
777
|
QuotedMessage: props.QuotedMessage,
|
|
765
778
|
QuotedMessagePreview: props.QuotedMessagePreview,
|
|
779
|
+
QuotedPoll: props.QuotedPoll,
|
|
766
780
|
reactionOptions: props.reactionOptions,
|
|
767
781
|
ReactionSelector: props.ReactionSelector,
|
|
768
782
|
ReactionsList: props.ReactionsList,
|
|
@@ -780,6 +794,8 @@ const ChannelInner = (props) => {
|
|
|
780
794
|
}), [
|
|
781
795
|
props.Attachment,
|
|
782
796
|
props.AttachmentPreviewList,
|
|
797
|
+
props.AttachmentSelector,
|
|
798
|
+
props.AttachmentSelectorInitiationButtonContents,
|
|
783
799
|
props.AudioRecorder,
|
|
784
800
|
props.AutocompleteSuggestionItem,
|
|
785
801
|
props.AutocompleteSuggestionList,
|
|
@@ -810,8 +826,14 @@ const ChannelInner = (props) => {
|
|
|
810
826
|
props.MessageTimestamp,
|
|
811
827
|
props.ModalGallery,
|
|
812
828
|
props.PinIndicator,
|
|
829
|
+
props.PollActions,
|
|
830
|
+
props.PollContent,
|
|
831
|
+
props.PollCreationDialog,
|
|
832
|
+
props.PollHeader,
|
|
833
|
+
props.PollOptionSelector,
|
|
813
834
|
props.QuotedMessage,
|
|
814
835
|
props.QuotedMessagePreview,
|
|
836
|
+
props.QuotedPoll,
|
|
815
837
|
props.ReactionSelector,
|
|
816
838
|
props.ReactionsList,
|
|
817
839
|
props.SendButton,
|
|
@@ -831,20 +853,19 @@ const ChannelInner = (props) => {
|
|
|
831
853
|
const typingContextValue = useCreateTypingContext({
|
|
832
854
|
typing,
|
|
833
855
|
});
|
|
834
|
-
const className = clsx(chatClass, theme, channelClass);
|
|
835
856
|
if (state.error) {
|
|
836
|
-
return (React.createElement(
|
|
857
|
+
return (React.createElement(ChannelContainer, null,
|
|
837
858
|
React.createElement(LoadingErrorIndicator, { error: state.error })));
|
|
838
859
|
}
|
|
839
860
|
if (state.loading) {
|
|
840
|
-
return (React.createElement(
|
|
861
|
+
return (React.createElement(ChannelContainer, null,
|
|
841
862
|
React.createElement(LoadingIndicator, null)));
|
|
842
863
|
}
|
|
843
864
|
if (!channel.watch) {
|
|
844
|
-
return (React.createElement(
|
|
865
|
+
return (React.createElement(ChannelContainer, null,
|
|
845
866
|
React.createElement("div", null, t('Channel Missing'))));
|
|
846
867
|
}
|
|
847
|
-
return (React.createElement(
|
|
868
|
+
return (React.createElement(ChannelContainer, { className: windowsEmojiClass },
|
|
848
869
|
React.createElement(ChannelStateProvider, { value: channelStateContextValue },
|
|
849
870
|
React.createElement(ChannelActionProvider, { value: channelActionContextValue },
|
|
850
871
|
React.createElement(WithComponents, { overrides: componentContextValue },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CHANNEL_CONTAINER_ID = "str-chat__channel";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CHANNEL_CONTAINER_ID = 'str-chat__channel';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ChatContextValue } from '../../../context/ChatContext';
|
|
2
2
|
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
3
|
+
export declare const useImageFlagEmojisOnWindowsClass: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => "" | "str-chat--windows-flags";
|
|
4
|
+
export declare const getChatContainerClass: (customClass?: string) => string;
|
|
3
5
|
export declare const useChannelContainerClasses: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ customClasses, }: Pick<ChatContextValue, 'customClasses'>) => {
|
|
4
6
|
channelClass: string;
|
|
5
7
|
chatClass: string;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { useChatContext } from '../../../context/ChatContext';
|
|
2
|
-
export const
|
|
2
|
+
export const useImageFlagEmojisOnWindowsClass = () => {
|
|
3
3
|
const { useImageFlagEmojisOnWindows } = useChatContext('Channel');
|
|
4
|
+
return useImageFlagEmojisOnWindows && navigator.userAgent.match(/Win/)
|
|
5
|
+
? 'str-chat--windows-flags'
|
|
6
|
+
: '';
|
|
7
|
+
};
|
|
8
|
+
export const getChatContainerClass = (customClass) => customClass ?? 'str-chat__container';
|
|
9
|
+
export const useChannelContainerClasses = ({ customClasses, }) => {
|
|
10
|
+
const windowsEmojiClass = useImageFlagEmojisOnWindowsClass();
|
|
4
11
|
return {
|
|
5
12
|
channelClass: customClasses?.channel ?? 'str-chat__channel',
|
|
6
13
|
chatClass: customClasses?.chat ?? 'str-chat',
|
|
7
|
-
chatContainerClass: customClasses?.chatContainer
|
|
8
|
-
windowsEmojiClass
|
|
9
|
-
? 'str-chat--windows-flags'
|
|
10
|
-
: '',
|
|
14
|
+
chatContainerClass: getChatContainerClass(customClasses?.chatContainer),
|
|
15
|
+
windowsEmojiClass,
|
|
11
16
|
};
|
|
12
17
|
};
|
|
@@ -1,16 +1,51 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ReactMarkdown from 'react-markdown';
|
|
3
3
|
export const renderPreviewText = (text) => React.createElement(ReactMarkdown, { skipHtml: true }, text);
|
|
4
|
+
const getLatestPollVote = (latestVotesByOption) => {
|
|
5
|
+
let latestVote;
|
|
6
|
+
for (const optionVotes of Object.values(latestVotesByOption)) {
|
|
7
|
+
optionVotes.forEach((vote) => {
|
|
8
|
+
if (latestVote && new Date(latestVote.updated_at) >= new Date(vote.created_at))
|
|
9
|
+
return;
|
|
10
|
+
latestVote = vote;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
return latestVote;
|
|
14
|
+
};
|
|
4
15
|
export const getLatestMessagePreview = (channel, t, userLanguage = 'en') => {
|
|
5
16
|
const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
|
|
6
17
|
const previewTextToRender = latestMessage?.i18n?.[`${userLanguage}_text`] ||
|
|
7
18
|
latestMessage?.text;
|
|
19
|
+
const poll = latestMessage?.poll;
|
|
8
20
|
if (!latestMessage) {
|
|
9
21
|
return t('Nothing yet...');
|
|
10
22
|
}
|
|
11
23
|
if (latestMessage.deleted_at) {
|
|
12
24
|
return t('Message deleted');
|
|
13
25
|
}
|
|
26
|
+
if (poll) {
|
|
27
|
+
if (!poll.vote_count) {
|
|
28
|
+
const createdBy = poll.created_by?.id === channel.getClient().userID
|
|
29
|
+
? t('You')
|
|
30
|
+
: poll.created_by?.name ?? t('Poll');
|
|
31
|
+
return t('📊 {{createdBy}} created: {{ pollName}}', {
|
|
32
|
+
createdBy,
|
|
33
|
+
pollName: poll.name,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const latestVote = getLatestPollVote(poll.latest_votes_by_option);
|
|
38
|
+
const option = latestVote && poll.options.find((opt) => opt.id === latestVote.option_id);
|
|
39
|
+
if (option && latestVote) {
|
|
40
|
+
return t('📊 {{votedBy}} voted: {{pollOptionText}}', {
|
|
41
|
+
pollOptionText: option.text,
|
|
42
|
+
votedBy: latestVote?.user?.id === channel.getClient().userID
|
|
43
|
+
? t('You')
|
|
44
|
+
: latestVote.user?.name ?? t('Poll'),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
14
49
|
if (previewTextToRender) {
|
|
15
50
|
return renderPreviewText(previewTextToRender);
|
|
16
51
|
}
|
|
@@ -31,8 +31,10 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
|
|
|
31
31
|
client.setUserAgent(`stream-chat-react-${version}-${userAgent}`);
|
|
32
32
|
}
|
|
33
33
|
client.threads.registerSubscriptions();
|
|
34
|
+
client.polls.registerSubscriptions();
|
|
34
35
|
return () => {
|
|
35
36
|
client.threads.unregisterSubscriptions();
|
|
37
|
+
client.polls.unregisterSubscriptions();
|
|
36
38
|
};
|
|
37
39
|
}, [client]);
|
|
38
40
|
useEffect(() => {
|
|
@@ -16,10 +16,9 @@ export declare function useDialogAnchor<T extends HTMLElement>({ open, placement
|
|
|
16
16
|
[key: string]: React.CSSProperties;
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
|
-
type DialogAnchorProps = PropsWithChildren<Partial<DialogAnchorOptions>> & {
|
|
19
|
+
export type DialogAnchorProps = PropsWithChildren<Partial<DialogAnchorOptions>> & {
|
|
20
20
|
id: string;
|
|
21
21
|
focus?: boolean;
|
|
22
22
|
trapFocus?: boolean;
|
|
23
23
|
} & ComponentProps<'div'>;
|
|
24
24
|
export declare const DialogAnchor: ({ children, className, focus, id, placement, referenceElement, trapFocus, ...restDivProps }: DialogAnchorProps) => React.JSX.Element | null;
|
|
25
|
-
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
export const DialogMenuButton = ({ children, className, ...props }) => (React.createElement("button", { className: clsx('str-chat__dialog-menu__button', className), ...props },
|
|
4
|
+
React.createElement("div", { className: 'str-chat__dialog-menu__button-icon' }),
|
|
5
|
+
React.createElement("div", { className: 'str-chat__dialog-menu__button-text' }, children)));
|
|
@@ -3,5 +3,5 @@ export declare const DialogPortalDestination: () => React.JSX.Element;
|
|
|
3
3
|
type DialogPortalEntryProps = {
|
|
4
4
|
dialogId: string;
|
|
5
5
|
};
|
|
6
|
-
export declare const DialogPortalEntry: ({ children, dialogId, }: PropsWithChildren<DialogPortalEntryProps>) => React.
|
|
6
|
+
export declare const DialogPortalEntry: ({ children, dialogId, }: PropsWithChildren<DialogPortalEntryProps>) => React.JSX.Element;
|
|
7
7
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
3
2
|
import { useDialogIsOpen, useOpenedDialogCount } from './hooks';
|
|
3
|
+
import { Portal } from '../Portal/Portal';
|
|
4
4
|
import { useDialogManager } from '../../context';
|
|
5
5
|
export const DialogPortalDestination = () => {
|
|
6
6
|
const { dialogManager } = useDialogManager();
|
|
@@ -12,14 +12,6 @@ export const DialogPortalDestination = () => {
|
|
|
12
12
|
export const DialogPortalEntry = ({ children, dialogId, }) => {
|
|
13
13
|
const { dialogManager } = useDialogManager();
|
|
14
14
|
const dialogIsOpen = useDialogIsOpen(dialogId);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const destination = document.querySelector(`div[data-str-chat__portal-id="${dialogManager.id}"]`);
|
|
18
|
-
if (!destination)
|
|
19
|
-
return;
|
|
20
|
-
setPortalDestination(destination);
|
|
21
|
-
}, [dialogManager, dialogIsOpen]);
|
|
22
|
-
if (!portalDestination)
|
|
23
|
-
return null;
|
|
24
|
-
return createPortal(children, portalDestination);
|
|
15
|
+
const getPortalDestination = useCallback(() => document.querySelector(`div[data-str-chat__portal-id="${dialogManager.id}"]`), [dialogManager.id]);
|
|
16
|
+
return (React.createElement(Portal, { getPortalDestination: getPortalDestination, isOpen: dialogIsOpen }, children));
|
|
25
17
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { ComponentProps } from 'react';
|
|
2
|
+
type FormElements = 'input' | 'textarea';
|
|
3
|
+
type FieldId = string;
|
|
4
|
+
type Validator = (value: string | readonly string[] | number | boolean | undefined) => Error | undefined;
|
|
5
|
+
export type FieldConfig = {
|
|
6
|
+
element: FormElements;
|
|
7
|
+
props: ComponentProps<FormElements>;
|
|
8
|
+
label?: React.ReactNode;
|
|
9
|
+
validator?: Validator;
|
|
10
|
+
};
|
|
11
|
+
type TextInputFormProps<F extends FormValue<Record<FieldId, FieldConfig>>> = {
|
|
12
|
+
close: () => void;
|
|
13
|
+
fields: Record<FieldId, FieldConfig>;
|
|
14
|
+
onSubmit: (formValue: F) => Promise<void>;
|
|
15
|
+
className?: string;
|
|
16
|
+
shouldDisableSubmitButton?: (formValue: F) => boolean;
|
|
17
|
+
title?: string;
|
|
18
|
+
};
|
|
19
|
+
type FormValue<F extends Record<FieldId, FieldConfig>> = {
|
|
20
|
+
[K in keyof F]: F[K]['props']['value'];
|
|
21
|
+
};
|
|
22
|
+
export declare const FormDialog: <F extends FormValue<Record<string, FieldConfig>> = FormValue<Record<string, FieldConfig>>>({ className, close, fields, onSubmit, shouldDisableSubmitButton, title, }: TextInputFormProps<F>) => React.JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useCallback, useState, } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { FieldError } from '../Form/FieldError';
|
|
4
|
+
import { useTranslationContext } from '../../context';
|
|
5
|
+
export const FormDialog = ({ className, close, fields, onSubmit, shouldDisableSubmitButton, title, }) => {
|
|
6
|
+
const { t } = useTranslationContext();
|
|
7
|
+
const [fieldErrors, setFieldErrors] = useState({});
|
|
8
|
+
const [value, setValue] = useState(() => {
|
|
9
|
+
let acc = {};
|
|
10
|
+
for (const [id, config] of Object.entries(fields)) {
|
|
11
|
+
acc = { ...acc, [id]: config.props.value };
|
|
12
|
+
}
|
|
13
|
+
return acc;
|
|
14
|
+
});
|
|
15
|
+
const handleChange = useCallback((event) => {
|
|
16
|
+
const fieldId = event.target.id;
|
|
17
|
+
const fieldConfig = fields[fieldId];
|
|
18
|
+
if (!fieldConfig)
|
|
19
|
+
return;
|
|
20
|
+
const error = fieldConfig.validator?.(event.target.value);
|
|
21
|
+
if (error) {
|
|
22
|
+
setFieldErrors((prev) => ({ [fieldId]: error, ...prev }));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
setFieldErrors((prev) => {
|
|
26
|
+
delete prev[fieldId];
|
|
27
|
+
return prev;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
setValue((prev) => ({ ...prev, [fieldId]: event.target.value }));
|
|
31
|
+
if (!fieldConfig.props.onChange)
|
|
32
|
+
return;
|
|
33
|
+
if (fieldConfig.element === 'input') {
|
|
34
|
+
fieldConfig.props.onChange(event);
|
|
35
|
+
}
|
|
36
|
+
else if (fieldConfig.element === 'textarea') {
|
|
37
|
+
fieldConfig.props.onChange(event);
|
|
38
|
+
}
|
|
39
|
+
}, [fields]);
|
|
40
|
+
const handleSubmit = async () => {
|
|
41
|
+
if (!Object.keys(value).length)
|
|
42
|
+
return;
|
|
43
|
+
const errors = {};
|
|
44
|
+
for (const [id, fieldValue] of Object.entries(value)) {
|
|
45
|
+
const thisFieldError = fields[id].validator?.(fieldValue);
|
|
46
|
+
if (thisFieldError) {
|
|
47
|
+
errors[id] = thisFieldError;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (Object.keys(errors).length) {
|
|
51
|
+
setFieldErrors(errors);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await onSubmit(value);
|
|
55
|
+
close();
|
|
56
|
+
};
|
|
57
|
+
return (React.createElement("div", { className: clsx('str-chat__dialog str-chat__dialog--form', className) },
|
|
58
|
+
React.createElement("div", { className: 'str-chat__dialog__body' },
|
|
59
|
+
title && React.createElement("div", { className: 'str-chat__dialog__title' }, title),
|
|
60
|
+
React.createElement("form", { autoComplete: 'off' }, Object.entries(fields).map(([id, fieldConfig]) => (React.createElement("div", { className: 'str-chat__dialog__field', key: `dialog-field-${id}` },
|
|
61
|
+
fieldConfig.label && (React.createElement("label", { className: clsx(`str-chat__dialog__title str-chat__dialog__title--${id}`), htmlFor: id }, fieldConfig.label)),
|
|
62
|
+
React.createElement(fieldConfig.element, {
|
|
63
|
+
id,
|
|
64
|
+
...fieldConfig.props,
|
|
65
|
+
onChange: handleChange,
|
|
66
|
+
value: value[id],
|
|
67
|
+
}),
|
|
68
|
+
React.createElement(FieldError, { text: fieldErrors[id]?.message })))))),
|
|
69
|
+
React.createElement("div", { className: 'str-chat__dialog__controls' },
|
|
70
|
+
React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--cancel', onClick: close }, t('Cancel')),
|
|
71
|
+
React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--submit', disabled: Object.keys(fieldErrors).length > 0 || shouldDisableSubmitButton?.(value), onClick: handleSubmit, type: 'submit' }, t('Send')))));
|
|
72
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React, { ComponentProps } from 'react';
|
|
2
|
+
export type ConfirmationDialogProps = {
|
|
3
|
+
actions: ComponentProps<'button'>[];
|
|
4
|
+
prompt: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const PromptDialog: ({ actions, className, prompt, title }: ConfirmationDialogProps) => React.JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
export const PromptDialog = ({ actions, className, prompt, title }) => (React.createElement("div", { className: clsx('str-chat__dialog str-chat__dialog--prompt', className) },
|
|
4
|
+
React.createElement("div", { className: 'str-chat__dialog__body' },
|
|
5
|
+
title && React.createElement("div", { className: 'str-chat__dialog__title' }, title),
|
|
6
|
+
React.createElement("div", { className: 'str-chat__dialog__prompt' }, prompt)),
|
|
7
|
+
React.createElement("div", { className: 'str-chat__dialog__controls' }, actions.map(({ className, ...props }, i) => (React.createElement("button", { className: clsx(`str-chat__dialog__controls-button`, className), key: `prompt-dialog__controls-button--${i}`, ...props }))))));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from 'react';
|
|
2
|
+
export type DragAndDropContainerProps = PropsWithChildren<{
|
|
3
|
+
className?: string;
|
|
4
|
+
draggable?: boolean;
|
|
5
|
+
onSetNewOrder?: (newOrder: number[]) => void;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const DragAndDropContainer: ({ children, className, draggable, onSetNewOrder, }: DragAndDropContainerProps) => React.JSX.Element;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
export const DragAndDropContainer = ({ children, className, draggable, onSetNewOrder, }) => {
|
|
4
|
+
const [order, setOrder] = useState([]);
|
|
5
|
+
const [dragStartIndex, setDragStartIndex] = useState(null);
|
|
6
|
+
const [dragOverIndex, setDragOverIndex] = useState(null);
|
|
7
|
+
const [container, setContainer] = useState(null);
|
|
8
|
+
const moveDirection = dragStartIndex === null || dragOverIndex === null
|
|
9
|
+
? undefined
|
|
10
|
+
: dragStartIndex <= dragOverIndex
|
|
11
|
+
? 'down'
|
|
12
|
+
: 'up';
|
|
13
|
+
const childrenArray = React.Children.toArray(children);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setOrder(React.Children.map(children, (_, index) => index) || []);
|
|
16
|
+
}, [children]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!container)
|
|
19
|
+
return;
|
|
20
|
+
const handleDragStart = (e) => {
|
|
21
|
+
const target = e.target;
|
|
22
|
+
const draggableItem = target.closest('.str-chat__drag-and-drop-container__item');
|
|
23
|
+
if (e.dataTransfer) {
|
|
24
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
25
|
+
}
|
|
26
|
+
if (draggableItem instanceof HTMLElement) {
|
|
27
|
+
const index = Array.from(draggableItem.parentElement?.children || []).indexOf(draggableItem);
|
|
28
|
+
setDragStartIndex(index);
|
|
29
|
+
e.dataTransfer?.setData('text/plain', index.toString());
|
|
30
|
+
draggableItem.style.opacity = '0.3';
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const handleDragOver = (e) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
const target = e.target;
|
|
36
|
+
const draggableItem = target.closest('.str-chat__drag-and-drop-container__item');
|
|
37
|
+
if (draggableItem instanceof HTMLElement) {
|
|
38
|
+
const index = Array.from(draggableItem.parentElement?.children || []).indexOf(draggableItem);
|
|
39
|
+
setDragOverIndex(index);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const handleDragLeave = () => {
|
|
43
|
+
setDragOverIndex(null);
|
|
44
|
+
};
|
|
45
|
+
const handleDrop = (e) => {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
const draggedIndex = parseInt(e.dataTransfer?.getData('text/plain') || '-1', 10);
|
|
48
|
+
const target = e.target;
|
|
49
|
+
const draggableItem = target.closest('.str-chat__drag-and-drop-container__item');
|
|
50
|
+
if (draggableItem instanceof HTMLElement) {
|
|
51
|
+
const dropIndex = Array.from(draggableItem.parentElement?.children || []).indexOf(draggableItem);
|
|
52
|
+
if (draggedIndex !== -1 && draggedIndex !== dropIndex) {
|
|
53
|
+
setOrder((prevOrder) => {
|
|
54
|
+
const newOrder = [...prevOrder];
|
|
55
|
+
const [removed] = newOrder.splice(draggedIndex, 1);
|
|
56
|
+
newOrder.splice(dropIndex, 0, removed);
|
|
57
|
+
onSetNewOrder?.(newOrder);
|
|
58
|
+
return newOrder;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
setDragStartIndex(null);
|
|
63
|
+
setDragOverIndex(null);
|
|
64
|
+
};
|
|
65
|
+
const handleDragEnd = (e) => {
|
|
66
|
+
const target = e.target;
|
|
67
|
+
if (target instanceof HTMLElement) {
|
|
68
|
+
target.style.opacity = '';
|
|
69
|
+
}
|
|
70
|
+
setDragStartIndex(null);
|
|
71
|
+
setDragOverIndex(null);
|
|
72
|
+
};
|
|
73
|
+
container.addEventListener('dragstart', handleDragStart);
|
|
74
|
+
container.addEventListener('dragover', handleDragOver);
|
|
75
|
+
container.addEventListener('dragleave', handleDragLeave);
|
|
76
|
+
container.addEventListener('drop', handleDrop);
|
|
77
|
+
container.addEventListener('dragend', handleDragEnd);
|
|
78
|
+
return () => {
|
|
79
|
+
container.removeEventListener('dragstart', handleDragStart);
|
|
80
|
+
container.removeEventListener('dragover', handleDragOver);
|
|
81
|
+
container.removeEventListener('dragleave', handleDragLeave);
|
|
82
|
+
container.removeEventListener('drop', handleDrop);
|
|
83
|
+
container.removeEventListener('dragend', handleDragEnd);
|
|
84
|
+
};
|
|
85
|
+
}, [container, onSetNewOrder]);
|
|
86
|
+
return (React.createElement("div", { className: clsx('str-chat__drag-and-drop-container', className), ref: setContainer }, order.map((originalIndex, currentIndex) => {
|
|
87
|
+
const child = childrenArray[originalIndex];
|
|
88
|
+
return (React.createElement("div", { className: clsx('str-chat__drag-and-drop-container__item', {
|
|
89
|
+
'str-chat__drag-and-drop-container__item--dragged-over-from-bottom': moveDirection === 'up' && dragOverIndex === currentIndex,
|
|
90
|
+
'str-chat__drag-and-drop-container__item--dragged-over-from-top': moveDirection === 'down' && dragOverIndex === currentIndex,
|
|
91
|
+
}), draggable: draggable, key: React.isValidElement(child) ? child.key : `draggable-item-${originalIndex}` }, child));
|
|
92
|
+
})));
|
|
93
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React, { ComponentProps, PropsWithChildren } from 'react';
|
|
2
|
+
export type SwitchFieldProps = PropsWithChildren<ComponentProps<'input'>>;
|
|
3
|
+
export declare const SwitchField: ({ children, ...props }: SwitchFieldProps) => React.JSX.Element;
|
|
4
|
+
export type SimpleSwitchFieldProps = ComponentProps<'input'> & {
|
|
5
|
+
labelText: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const SimpleSwitchField: ({ labelText, ...props }: SimpleSwitchFieldProps) => React.JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import React, { useRef, } from 'react';
|
|
3
|
+
export const SwitchField = ({ children, ...props }) => {
|
|
4
|
+
const inputRef = useRef(null);
|
|
5
|
+
const handleKeyUp = (event) => {
|
|
6
|
+
if (![' ', 'Enter'].includes(event.key) || !inputRef.current)
|
|
7
|
+
return;
|
|
8
|
+
event.preventDefault();
|
|
9
|
+
inputRef.current.click();
|
|
10
|
+
};
|
|
11
|
+
return (React.createElement("div", { className: 'str-chat__form__field str-chat__form__switch-field' },
|
|
12
|
+
React.createElement("label", null,
|
|
13
|
+
React.createElement("div", { className: 'str-chat__form__field str-chat__form__switch-field-content' }, children),
|
|
14
|
+
React.createElement("input", { type: 'checkbox', ...props, ref: inputRef }),
|
|
15
|
+
React.createElement("div", { className: clsx('str-chat__form__switch-field__switch', {
|
|
16
|
+
'str-chat__form__switch-field__switch--on': props.checked,
|
|
17
|
+
}), onKeyUp: handleKeyUp, tabIndex: 0 },
|
|
18
|
+
React.createElement("div", { className: 'str-chat__form__switch-field__switch-handle' })))));
|
|
19
|
+
};
|
|
20
|
+
export const SimpleSwitchField = ({ labelText, ...props }) => (React.createElement(SwitchField, { ...props },
|
|
21
|
+
React.createElement("div", { className: 'str-chat__form__field str-chat__form__switch-field__text' }, labelText)));
|
|
@@ -31,4 +31,14 @@ export type InfiniteScrollProps = PaginatorProps & {
|
|
|
31
31
|
loadMoreNewer?: () => void;
|
|
32
32
|
useCapture?: boolean;
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* This component serves a single purpose - load more items on scroll inside the MessageList component
|
|
36
|
+
* It is not a general purpose infinite scroll controller, because:
|
|
37
|
+
* 1. It is re-rendered whenever isLoading, hasNext, hasPrev changes. This can lead to scrollListener to have stale data.
|
|
38
|
+
* 2. It pretends to invoke scrollListener on resize event even though this event is emitted only on window resize. It should
|
|
39
|
+
* rather use ResizeObserver. But then again, it ResizeObserver would invoke a stale version of scrollListener.
|
|
40
|
+
*
|
|
41
|
+
* In general, the infinite scroll controller should not aim for checking the loading state and whether there is more data to load.
|
|
42
|
+
* That should be controlled by the loading function.
|
|
43
|
+
*/
|
|
34
44
|
export declare const InfiniteScroll: (props: PropsWithChildren<InfiniteScrollProps>) => React.ReactElement<any, string | React.JSXElementConstructor<any>>;
|