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
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PollContent as DefaultPollContent } from './PollContent';
|
|
3
|
+
import { QuotedPoll as DefaultQuotedPoll } from './QuotedPoll';
|
|
4
|
+
import { PollProvider, useComponentContext } from '../../context';
|
|
5
|
+
export const Poll = ({ isQuoted, poll, }) => {
|
|
6
|
+
const { PollContent = DefaultPollContent, QuotedPoll = DefaultQuotedPoll, } = useComponentContext();
|
|
7
|
+
return poll ? (React.createElement(PollProvider, { poll: poll }, isQuoted ? React.createElement(QuotedPoll, null) : React.createElement(PollContent, null))) : null;
|
|
8
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
3
|
+
export type AddCommentFormProps = {
|
|
4
|
+
close: () => void;
|
|
5
|
+
messageId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const AddCommentForm: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, messageId, }: AddCommentFormProps) => React.JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormDialog } from '../../Dialog/FormDialog';
|
|
3
|
+
import { useStateStore } from '../../../store';
|
|
4
|
+
import { usePollContext, useTranslationContext } from '../../../context';
|
|
5
|
+
const pollStateSelector = (nextValue) => ({ ownAnswer: nextValue.ownAnswer });
|
|
6
|
+
export const AddCommentForm = ({ close, messageId, }) => {
|
|
7
|
+
const { t } = useTranslationContext('AddCommentForm');
|
|
8
|
+
const { poll } = usePollContext();
|
|
9
|
+
const { ownAnswer } = useStateStore(poll.state, pollStateSelector);
|
|
10
|
+
return (React.createElement(FormDialog, { className: 'str-chat__prompt-dialog str-chat__modal__poll-add-comment', close: close, fields: {
|
|
11
|
+
comment: {
|
|
12
|
+
element: 'input',
|
|
13
|
+
props: {
|
|
14
|
+
id: 'comment',
|
|
15
|
+
name: 'comment',
|
|
16
|
+
required: true,
|
|
17
|
+
type: 'text',
|
|
18
|
+
value: ownAnswer?.answer_text ?? '',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}, onSubmit: async (value) => {
|
|
22
|
+
await poll.addAnswer(value.comment, messageId);
|
|
23
|
+
}, shouldDisableSubmitButton: (value) => !value.comment || value.comment === ownAnswer?.answer_text, title: ownAnswer ? t('Update your comment') : t('Add a comment') }));
|
|
24
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
3
|
+
export type EndPollDialogProps = {
|
|
4
|
+
close: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const EndPollDialog: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, }: EndPollDialogProps) => React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PromptDialog } from '../../Dialog/PromptDialog';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { usePollContext, useTranslationContext } from '../../../context';
|
|
4
|
+
export const EndPollDialog = ({ close, }) => {
|
|
5
|
+
const { t } = useTranslationContext('SuggestPollOptionForm');
|
|
6
|
+
const { poll } = usePollContext();
|
|
7
|
+
return (React.createElement(PromptDialog, { actions: [
|
|
8
|
+
{
|
|
9
|
+
children: t('Cancel'),
|
|
10
|
+
className: 'str-chat__dialog__controls-button--cancel',
|
|
11
|
+
onClick: close,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
children: t('End'),
|
|
15
|
+
className: '.str-chat__dialog__controls-button--submit str-chat__dialog__controls-button--end-poll',
|
|
16
|
+
onClick: poll.close,
|
|
17
|
+
},
|
|
18
|
+
], className: 'str-chat__modal__end-vote', prompt: t('Nobody will be able to vote in this poll anymore.'), title: t('End vote') }));
|
|
19
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from 'react';
|
|
2
|
+
export type PollActionProps = {
|
|
3
|
+
buttonText: string;
|
|
4
|
+
closeModal: () => void;
|
|
5
|
+
modalIsOpen: boolean;
|
|
6
|
+
openModal: () => void;
|
|
7
|
+
modalClassName?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const PollAction: ({ buttonText, children, closeModal, modalClassName, modalIsOpen, openModal, }: PropsWithChildren<PollActionProps>) => React.JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal } from '../../Modal';
|
|
3
|
+
export const PollAction = ({ buttonText, children, closeModal, modalClassName, modalIsOpen, openModal, }) => (React.createElement(React.Fragment, null,
|
|
4
|
+
React.createElement("button", { className: 'str-chat__poll-action', onClick: openModal }, buttonText),
|
|
5
|
+
React.createElement(Modal, { className: modalClassName, onClose: closeModal, open: modalIsOpen }, children)));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AddCommentFormProps } from './AddCommentForm';
|
|
3
|
+
import { SuggestPollOptionFormProps } from './SuggestPollOptionForm';
|
|
4
|
+
import { EndPollDialogProps } from './EndPollDialog';
|
|
5
|
+
import { PollAnswerListProps } from './PollAnswerList';
|
|
6
|
+
import { FullPollOptionsListingProps } from './PollOptionsFullList';
|
|
7
|
+
import { PollResultsProps } from './PollResults';
|
|
8
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
9
|
+
export type PollActionsProps = {
|
|
10
|
+
AddCommentForm?: React.ComponentType<AddCommentFormProps>;
|
|
11
|
+
EndPollDialog?: React.ComponentType<EndPollDialogProps>;
|
|
12
|
+
PollAnswerList?: React.ComponentType<PollAnswerListProps>;
|
|
13
|
+
PollOptionsFullList?: React.ComponentType<FullPollOptionsListingProps>;
|
|
14
|
+
PollResults?: React.ComponentType<PollResultsProps>;
|
|
15
|
+
SuggestPollOptionForm?: React.ComponentType<SuggestPollOptionFormProps>;
|
|
16
|
+
};
|
|
17
|
+
export declare const PollActions: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ AddCommentForm, EndPollDialog, PollAnswerList, PollOptionsFullList, PollResults, SuggestPollOptionForm, }: PollActionsProps) => React.JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { PollAction } from './PollAction';
|
|
3
|
+
import { AddCommentForm as DefaultAddCommentForm } from './AddCommentForm';
|
|
4
|
+
import { SuggestPollOptionForm as DefaultSuggestPollOptionForm, } from './SuggestPollOptionForm';
|
|
5
|
+
import { EndPollDialog as DefaultEndPollDialog } from './EndPollDialog';
|
|
6
|
+
import { PollAnswerList as DefaultPollAnswerList } from './PollAnswerList';
|
|
7
|
+
import { PollOptionsFullList as DefaultPollOptionsFullList, } from './PollOptionsFullList';
|
|
8
|
+
import { PollResults as DefaultPollResults } from './PollResults';
|
|
9
|
+
import { MAX_OPTIONS_DISPLAYED, MAX_POLL_OPTIONS } from '../constants';
|
|
10
|
+
import { useChannelStateContext, useChatContext, useMessageContext, usePollContext, useTranslationContext, } from '../../../context';
|
|
11
|
+
import { useStateStore } from '../../../store';
|
|
12
|
+
const pollStateSelector = (nextValue) => ({
|
|
13
|
+
allow_answers: nextValue.allow_answers,
|
|
14
|
+
allow_user_suggested_options: nextValue.allow_user_suggested_options,
|
|
15
|
+
answers_count: nextValue.answers_count,
|
|
16
|
+
created_by_id: nextValue.created_by_id,
|
|
17
|
+
is_closed: nextValue.is_closed,
|
|
18
|
+
options: nextValue.options,
|
|
19
|
+
ownAnswer: nextValue.ownAnswer,
|
|
20
|
+
});
|
|
21
|
+
export const PollActions = ({ AddCommentForm = DefaultAddCommentForm, EndPollDialog = DefaultEndPollDialog, PollAnswerList = DefaultPollAnswerList, PollOptionsFullList = DefaultPollOptionsFullList, PollResults = DefaultPollResults, SuggestPollOptionForm = DefaultSuggestPollOptionForm, }) => {
|
|
22
|
+
const { client } = useChatContext();
|
|
23
|
+
const { t } = useTranslationContext('PollActions');
|
|
24
|
+
const { channelCapabilities = {} } = useChannelStateContext('PollActions');
|
|
25
|
+
const { message } = useMessageContext('PollActions');
|
|
26
|
+
const { poll } = usePollContext();
|
|
27
|
+
const { allow_answers, allow_user_suggested_options, answers_count, created_by_id, is_closed, options, ownAnswer, } = useStateStore(poll.state, pollStateSelector);
|
|
28
|
+
const [modalOpen, setModalOpen] = useState();
|
|
29
|
+
const closeModal = useCallback(() => setModalOpen(undefined), []);
|
|
30
|
+
const onUpdateAnswerClick = useCallback(() => setModalOpen('add-comment'), []);
|
|
31
|
+
return (React.createElement("div", { className: 'str-chat__poll-actions' },
|
|
32
|
+
options.length > MAX_OPTIONS_DISPLAYED && (React.createElement(PollAction, { buttonText: t('See all options ({{count}})', {
|
|
33
|
+
count: options.length,
|
|
34
|
+
}), closeModal: closeModal, modalIsOpen: modalOpen === 'view-all-options', openModal: () => setModalOpen('view-all-options') },
|
|
35
|
+
React.createElement(PollOptionsFullList, { close: closeModal }))),
|
|
36
|
+
!is_closed && allow_user_suggested_options && options.length < MAX_POLL_OPTIONS && (React.createElement(PollAction, { buttonText: t('Suggest an option'), closeModal: closeModal, modalClassName: 'str-chat__suggest-poll-option-modal', modalIsOpen: modalOpen === 'suggest-option', openModal: () => setModalOpen('suggest-option') },
|
|
37
|
+
React.createElement(SuggestPollOptionForm, { close: closeModal, messageId: message.id }))),
|
|
38
|
+
!is_closed && allow_answers && (React.createElement(PollAction, { buttonText: ownAnswer ? t('Update your comment') : t('Add a comment'), closeModal: closeModal, modalClassName: 'str-chat__add-poll-answer-modal', modalIsOpen: modalOpen === 'add-comment', openModal: () => setModalOpen('add-comment') },
|
|
39
|
+
React.createElement(AddCommentForm, { close: closeModal, messageId: message.id }))),
|
|
40
|
+
answers_count > 0 && channelCapabilities['query-poll-votes'] && (React.createElement(PollAction, { buttonText: t('View {{count}} comments', { count: answers_count }), closeModal: closeModal, modalClassName: 'str-chat__poll-answer-list-modal', modalIsOpen: modalOpen === 'view-comments', openModal: () => setModalOpen('view-comments') },
|
|
41
|
+
React.createElement(PollAnswerList, { close: closeModal, onUpdateOwnAnswerClick: onUpdateAnswerClick }))),
|
|
42
|
+
React.createElement(PollAction, { buttonText: t('View results'), closeModal: closeModal, modalClassName: 'str-chat__poll-results-modal', modalIsOpen: modalOpen === 'view-results', openModal: () => setModalOpen('view-results') },
|
|
43
|
+
React.createElement(PollResults, { close: closeModal })),
|
|
44
|
+
!is_closed && created_by_id === client.user?.id && (React.createElement(PollAction, { buttonText: t('End vote'), closeModal: closeModal, modalClassName: 'str-chat__end-poll-modal', modalIsOpen: modalOpen === 'end-vote', openModal: () => setModalOpen('end-vote') },
|
|
45
|
+
React.createElement(EndPollDialog, { close: closeModal })))));
|
|
46
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
3
|
+
export type PollAnswerListProps = {
|
|
4
|
+
onUpdateOwnAnswerClick: () => void;
|
|
5
|
+
close?: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const PollAnswerList: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, onUpdateOwnAnswerClick, }: PollAnswerListProps) => React.JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ModalHeader } from '../../Modal/ModalHeader';
|
|
3
|
+
import { PollVote } from '../PollVote';
|
|
4
|
+
import { usePollAnswerPagination } from '../hooks';
|
|
5
|
+
import { InfiniteScrollPaginator } from '../../InfiniteScrollPaginator/InfiniteScrollPaginator';
|
|
6
|
+
import { LoadingIndicator } from '../../Loading';
|
|
7
|
+
import { useStateStore } from '../../../store';
|
|
8
|
+
import { usePollContext, useTranslationContext } from '../../../context';
|
|
9
|
+
const pollStateSelector = (nextValue) => ({
|
|
10
|
+
is_closed: nextValue.is_closed,
|
|
11
|
+
ownAnswer: nextValue.ownAnswer,
|
|
12
|
+
});
|
|
13
|
+
export const PollAnswerList = ({ close, onUpdateOwnAnswerClick, }) => {
|
|
14
|
+
const { t } = useTranslationContext();
|
|
15
|
+
const { poll } = usePollContext();
|
|
16
|
+
const { is_closed, ownAnswer } = useStateStore(poll.state, pollStateSelector);
|
|
17
|
+
const { answers, error, hasNextPage, loading, loadMore, } = usePollAnswerPagination();
|
|
18
|
+
return (React.createElement("div", { className: 'str-chat__modal__poll-answer-list' },
|
|
19
|
+
React.createElement(ModalHeader, { close: close, title: t('Poll comments') }),
|
|
20
|
+
React.createElement("div", { className: 'str-chat__modal__poll-answer-list__body' },
|
|
21
|
+
React.createElement(InfiniteScrollPaginator, { loadNextOnScrollToBottom: loadMore, threshold: 40 },
|
|
22
|
+
React.createElement("div", { className: 'str-chat__poll-answer-list' }, answers.map((answer) => (React.createElement("div", { className: 'str-chat__poll-answer', key: `comment-${answer.id}` },
|
|
23
|
+
answer.answer_text && (React.createElement("p", { className: 'str-chat__poll-answer__text' }, answer.answer_text)),
|
|
24
|
+
React.createElement(PollVote, { key: `poll-vote-${answer.id}`, vote: answer }))))),
|
|
25
|
+
hasNextPage && (React.createElement("div", { className: 'str-chat__loading-indicator-placeholder' }, loading && React.createElement(LoadingIndicator, null)))),
|
|
26
|
+
error?.message && React.createElement("div", null, error?.message)),
|
|
27
|
+
answers.length > 0 && !is_closed && (React.createElement("button", { className: 'str-chat__poll-action', onClick: onUpdateOwnAnswerClick }, ownAnswer ? t('Update your comment') : t('Add a comment')))));
|
|
28
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
3
|
+
export type FullPollOptionsListingProps = {
|
|
4
|
+
close?: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const PollOptionsFullList: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, }: FullPollOptionsListingProps) => React.JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ModalHeader } from '../../Modal/ModalHeader';
|
|
3
|
+
import { PollOptionList } from '../PollOptionList';
|
|
4
|
+
import { useStateStore } from '../../../store';
|
|
5
|
+
import { usePollContext, useTranslationContext } from '../../../context';
|
|
6
|
+
const pollStateSelector = (nextValue) => ({ name: nextValue.name });
|
|
7
|
+
export const PollOptionsFullList = ({ close, }) => {
|
|
8
|
+
const { t } = useTranslationContext();
|
|
9
|
+
const { poll } = usePollContext();
|
|
10
|
+
const { name } = useStateStore(poll.state, pollStateSelector);
|
|
11
|
+
return (React.createElement("div", { className: 'str-chat__modal__poll-option-list' },
|
|
12
|
+
React.createElement(ModalHeader, { close: close, title: t('Poll options') }),
|
|
13
|
+
React.createElement("div", { className: 'str-chat__modal__poll-option-list__body' },
|
|
14
|
+
React.createElement("div", { className: 'str-chat__modal__poll-option-list__title' }, name),
|
|
15
|
+
React.createElement(PollOptionList, null))));
|
|
16
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PollOption } from 'stream-chat';
|
|
3
|
+
import type { DefaultStreamChatGenerics } from '../../../../types';
|
|
4
|
+
export type PollOptionVotesListingProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
5
|
+
option: PollOption<StreamChatGenerics>;
|
|
6
|
+
};
|
|
7
|
+
export declare const PollOptionVotesList: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ option, }: PollOptionVotesListingProps<StreamChatGenerics>) => React.JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { PollVoteListing } from '../../PollVote';
|
|
3
|
+
import { usePollOptionVotesPagination } from '../../hooks';
|
|
4
|
+
import { LoadingIndicator } from '../../../Loading';
|
|
5
|
+
import { InfiniteScrollPaginator } from '../../../InfiniteScrollPaginator/InfiniteScrollPaginator';
|
|
6
|
+
import { PollOptionWithVotesHeader } from './PollOptionWithVotesHeader';
|
|
7
|
+
export const PollOptionVotesList = ({ option, }) => {
|
|
8
|
+
const paginationParams = useMemo(() => ({ filter: { option_id: option.id } }), [option.id]);
|
|
9
|
+
const { error, hasNextPage, loading, loadMore, votes, } = usePollOptionVotesPagination({
|
|
10
|
+
paginationParams,
|
|
11
|
+
});
|
|
12
|
+
return (React.createElement("div", { className: 'str-chat__poll-option str-chat__poll-option--full-vote-list' },
|
|
13
|
+
React.createElement(PollOptionWithVotesHeader, { option: option }),
|
|
14
|
+
React.createElement(InfiniteScrollPaginator, { loadNextOnScrollToBottom: loadMore, threshold: 40 },
|
|
15
|
+
React.createElement(PollVoteListing, { votes: votes }),
|
|
16
|
+
hasNextPage && (React.createElement("div", { className: 'str-chat__loading-indicator-placeholder' }, loading && React.createElement(LoadingIndicator, null)))),
|
|
17
|
+
error && error.message));
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PollOption } from 'stream-chat';
|
|
3
|
+
import type { DefaultStreamChatGenerics } from '../../../../types';
|
|
4
|
+
export type PollOptionWithVotesProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
5
|
+
option: PollOption<StreamChatGenerics>;
|
|
6
|
+
countVotesPreview?: number;
|
|
7
|
+
showAllVotes?: () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const PollOptionWithLatestVotes: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ countVotesPreview, option, showAllVotes, }: PollOptionWithVotesProps<StreamChatGenerics>) => React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PollOptionWithVotesHeader } from './PollOptionWithVotesHeader';
|
|
3
|
+
import { PollVoteListing } from '../../PollVote';
|
|
4
|
+
import { useStateStore } from '../../../../store';
|
|
5
|
+
import { useChannelStateContext, usePollContext, useTranslationContext } from '../../../../context';
|
|
6
|
+
const pollStateSelector = (nextValue) => ({ latest_votes_by_option: nextValue.latest_votes_by_option });
|
|
7
|
+
export const PollOptionWithLatestVotes = ({ countVotesPreview = 5, option, showAllVotes, }) => {
|
|
8
|
+
const { t } = useTranslationContext();
|
|
9
|
+
const { channelCapabilities = {} } = useChannelStateContext('PollOptionWithLatestVotes');
|
|
10
|
+
const { poll } = usePollContext();
|
|
11
|
+
const { latest_votes_by_option } = useStateStore(poll.state, pollStateSelector);
|
|
12
|
+
const votes = latest_votes_by_option && latest_votes_by_option[option.id];
|
|
13
|
+
return (React.createElement("div", { className: 'str-chat__poll-option' },
|
|
14
|
+
React.createElement(PollOptionWithVotesHeader, { option: option }),
|
|
15
|
+
votes && React.createElement(PollVoteListing, { votes: votes.slice(0, countVotesPreview) }),
|
|
16
|
+
channelCapabilities['query-poll-votes'] &&
|
|
17
|
+
showAllVotes &&
|
|
18
|
+
votes?.length > countVotesPreview && (React.createElement("button", { className: 'str-chat__poll-option__show-all-votes-button', onClick: showAllVotes }, t('Show all')))));
|
|
19
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PollOption } from 'stream-chat';
|
|
3
|
+
import type { DefaultStreamChatGenerics } from '../../../../types';
|
|
4
|
+
export type PollResultOptionVoteCounterProps = {
|
|
5
|
+
optionId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const PollResultOptionVoteCounter: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ optionId, }: PollResultOptionVoteCounterProps) => React.JSX.Element;
|
|
8
|
+
export type PollOptionWithVotesHeaderProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
9
|
+
option: PollOption<StreamChatGenerics>;
|
|
10
|
+
};
|
|
11
|
+
export declare const PollOptionWithVotesHeader: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ option, }: PollOptionWithVotesHeaderProps<StreamChatGenerics>) => React.JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useStateStore } from '../../../../store';
|
|
3
|
+
import { usePollContext, useTranslationContext } from '../../../../context';
|
|
4
|
+
const pollStateSelector = (nextValue) => ({
|
|
5
|
+
maxVotedOptionIds: nextValue.maxVotedOptionIds,
|
|
6
|
+
vote_counts_by_option: nextValue.vote_counts_by_option,
|
|
7
|
+
});
|
|
8
|
+
export const PollResultOptionVoteCounter = ({ optionId, }) => {
|
|
9
|
+
const { t } = useTranslationContext();
|
|
10
|
+
const { poll } = usePollContext();
|
|
11
|
+
const { maxVotedOptionIds, vote_counts_by_option } = useStateStore(poll.state, pollStateSelector);
|
|
12
|
+
return (React.createElement("div", { className: 'str-chat__poll-result-option-vote-counter' },
|
|
13
|
+
maxVotedOptionIds.length === 1 && maxVotedOptionIds[0] === optionId && (React.createElement("div", { className: 'str-chat__poll-result-winning-option-icon' })),
|
|
14
|
+
React.createElement("span", { className: 'str-chat__poll-result-option-vote-count' }, t('{{count}} votes', { count: vote_counts_by_option[optionId] ?? 0 }))));
|
|
15
|
+
};
|
|
16
|
+
export const PollOptionWithVotesHeader = ({ option, }) => (React.createElement("div", { className: 'str-chat__poll-option__header' },
|
|
17
|
+
React.createElement("div", { className: 'str-chat__poll-option__option-text' }, option.text),
|
|
18
|
+
React.createElement(PollResultOptionVoteCounter, { optionId: option.id })));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../../types';
|
|
3
|
+
export type PollResultsProps = {
|
|
4
|
+
close?: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const PollResults: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, }: PollResultsProps) => React.JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import React, { useCallback, useState } from 'react';
|
|
3
|
+
import { PollOptionVotesList } from './PollOptionVotesList';
|
|
4
|
+
import { PollOptionWithLatestVotes } from './PollOptionWithLatestVotes';
|
|
5
|
+
import { ModalHeader } from '../../../Modal/ModalHeader';
|
|
6
|
+
import { useStateStore } from '../../../../store';
|
|
7
|
+
import { usePollContext, useTranslationContext } from '../../../../context';
|
|
8
|
+
const pollStateSelector = (nextValue) => ({
|
|
9
|
+
name: nextValue.name,
|
|
10
|
+
options: nextValue.options,
|
|
11
|
+
vote_counts_by_option: nextValue.vote_counts_by_option,
|
|
12
|
+
});
|
|
13
|
+
export const PollResults = ({ close, }) => {
|
|
14
|
+
const { t } = useTranslationContext();
|
|
15
|
+
const { poll } = usePollContext();
|
|
16
|
+
const { name, options, vote_counts_by_option } = useStateStore(poll.state, pollStateSelector);
|
|
17
|
+
const [optionToView, setOptionToView] = useState();
|
|
18
|
+
const goBack = useCallback(() => setOptionToView(undefined), []);
|
|
19
|
+
return (React.createElement("div", { className: clsx('str-chat__modal__poll-results', {
|
|
20
|
+
'str-chat__modal__poll-results--option-detail': optionToView,
|
|
21
|
+
}) }, optionToView ? (React.createElement(React.Fragment, null,
|
|
22
|
+
React.createElement(ModalHeader, { close: close, goBack: goBack, title: optionToView.text }),
|
|
23
|
+
React.createElement("div", { className: 'str-chat__modal__poll-results__body' },
|
|
24
|
+
React.createElement(PollOptionVotesList, { key: `poll-option-detail-${optionToView.id}`, option: optionToView })))) : (React.createElement(React.Fragment, null,
|
|
25
|
+
React.createElement(ModalHeader, { close: close, title: t('Poll results') }),
|
|
26
|
+
React.createElement("div", { className: 'str-chat__modal__poll-results__body' },
|
|
27
|
+
React.createElement("div", { className: 'str-chat__modal__poll-results__title' }, name),
|
|
28
|
+
React.createElement("div", { className: 'str-chat__modal__poll-results__option-list' }, options
|
|
29
|
+
.sort((next, current) => (vote_counts_by_option[current.id] ?? 0) >= (vote_counts_by_option[next.id] ?? 0)
|
|
30
|
+
? 1
|
|
31
|
+
: -1)
|
|
32
|
+
.map((option) => (React.createElement(PollOptionWithLatestVotes, { key: `poll-option-${option.id}`, option: option, showAllVotes: () => setOptionToView(option) })))))))));
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PollResults';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PollResults';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
3
|
+
export type SuggestPollOptionFormProps = {
|
|
4
|
+
close: () => void;
|
|
5
|
+
messageId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const SuggestPollOptionForm: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ close, messageId, }: SuggestPollOptionFormProps) => React.JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormDialog } from '../../Dialog/FormDialog';
|
|
3
|
+
import { useChatContext, usePollContext, useTranslationContext } from '../../../context';
|
|
4
|
+
import { useStateStore } from '../../../store';
|
|
5
|
+
const pollStateSelector = (nextValue) => ({ options: nextValue.options });
|
|
6
|
+
export const SuggestPollOptionForm = ({ close, messageId, }) => {
|
|
7
|
+
const { client } = useChatContext('SuggestPollOptionForm');
|
|
8
|
+
const { t } = useTranslationContext('SuggestPollOptionForm');
|
|
9
|
+
const { poll } = usePollContext();
|
|
10
|
+
const { options } = useStateStore(poll.state, pollStateSelector);
|
|
11
|
+
return (React.createElement(FormDialog, { className: 'str-chat__prompt-dialog str-chat__modal__suggest-poll-option', close: close, fields: {
|
|
12
|
+
optionText: {
|
|
13
|
+
element: 'input',
|
|
14
|
+
props: {
|
|
15
|
+
id: 'optionText',
|
|
16
|
+
name: 'optionText',
|
|
17
|
+
required: true,
|
|
18
|
+
type: 'text',
|
|
19
|
+
value: '',
|
|
20
|
+
},
|
|
21
|
+
validator: (value) => {
|
|
22
|
+
if (!value)
|
|
23
|
+
return;
|
|
24
|
+
const existingOption = options.find((option) => option.text === value.trim());
|
|
25
|
+
if (existingOption) {
|
|
26
|
+
return new Error(t('Option already exists'));
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}, onSubmit: async (value) => {
|
|
32
|
+
const { poll_option } = await client.createPollOption(poll.id, {
|
|
33
|
+
text: value.optionText,
|
|
34
|
+
});
|
|
35
|
+
poll.castVote(poll_option.id, messageId);
|
|
36
|
+
}, shouldDisableSubmitButton: (value) => !value.optionText, title: t('Suggest an option') }));
|
|
37
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { PollHeader as DefaultPollHeader } from './PollHeader';
|
|
4
|
+
import { PollActions as DefaultPollActions } from './PollActions';
|
|
5
|
+
import { PollOptionList } from './PollOptionList';
|
|
6
|
+
import { MAX_OPTIONS_DISPLAYED } from './constants';
|
|
7
|
+
import { useComponentContext, usePollContext } from '../../context';
|
|
8
|
+
import { useStateStore } from '../../store';
|
|
9
|
+
const pollStateSelectorPollContent = (nextValue) => ({ is_closed: nextValue.is_closed });
|
|
10
|
+
export const PollContent = () => {
|
|
11
|
+
const { PollHeader = DefaultPollHeader, PollActions = DefaultPollActions, } = useComponentContext();
|
|
12
|
+
const { poll } = usePollContext();
|
|
13
|
+
const { is_closed } = useStateStore(poll.state, pollStateSelectorPollContent);
|
|
14
|
+
return (React.createElement("div", { className: clsx('str-chat__poll', { 'str-chat__poll--closed': is_closed }) },
|
|
15
|
+
React.createElement(PollHeader, null),
|
|
16
|
+
React.createElement(PollOptionList, { optionsDisplayCount: MAX_OPTIONS_DISPLAYED }),
|
|
17
|
+
React.createElement(PollActions, null)));
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { OptionErrors, PollFormState } from './types';
|
|
3
|
+
export type OptionFieldSetProps = {
|
|
4
|
+
errors: OptionErrors;
|
|
5
|
+
options: PollFormState['options'];
|
|
6
|
+
setErrors: (fn: (prev: OptionErrors) => OptionErrors) => void;
|
|
7
|
+
setState: (fn: (prev: PollFormState) => PollFormState) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const OptionFieldSet: ({ errors, options, setErrors, setState }: OptionFieldSetProps) => React.JSX.Element;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { MAX_POLL_OPTIONS } from '../constants';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import React, { useCallback } from 'react';
|
|
5
|
+
import { FieldError } from '../../Form/FieldError';
|
|
6
|
+
import { DragAndDropContainer } from '../../DragAndDrop/DragAndDropContainer';
|
|
7
|
+
import { useTranslationContext } from '../../../context';
|
|
8
|
+
const VALIDATION_ERRORS = { 'Option already exists': true };
|
|
9
|
+
export const OptionFieldSet = ({ errors, options, setErrors, setState }) => {
|
|
10
|
+
const { t } = useTranslationContext('OptionFieldSet');
|
|
11
|
+
const findOptionDuplicate = (sourceOption) => {
|
|
12
|
+
const isDuplicateFilter = (option) => !!sourceOption.text.trim() && // do not include empty options into consideration
|
|
13
|
+
option.id !== sourceOption.id &&
|
|
14
|
+
option.text.trim() === sourceOption.text.trim();
|
|
15
|
+
return options.find(isDuplicateFilter);
|
|
16
|
+
};
|
|
17
|
+
const onSetNewOrder = useCallback((newOrder) => {
|
|
18
|
+
setState((prev) => ({ ...prev, options: newOrder.map((index) => prev.options[index]) }));
|
|
19
|
+
}, [setState]);
|
|
20
|
+
const draggable = options.length > 1;
|
|
21
|
+
return (React.createElement("fieldset", { className: 'str-chat__form__field str-chat__form__input-fieldset' },
|
|
22
|
+
React.createElement("legend", { className: 'str-chat__form__field-label' }, t('Options')),
|
|
23
|
+
React.createElement(DragAndDropContainer, { className: 'str-chat__form__input-fieldset__values', draggable: draggable, onSetNewOrder: onSetNewOrder }, options.map((option, i) => (React.createElement("div", { className: clsx('str-chat__form__input-field', {
|
|
24
|
+
'str-chat__form__input-field--draggable': draggable,
|
|
25
|
+
'str-chat__form__input-field--has-error': errors[option.id],
|
|
26
|
+
}), key: `new-poll-option-${i}` },
|
|
27
|
+
React.createElement("div", { className: 'str-chat__form__input-field__value' },
|
|
28
|
+
React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-option-input-field-error', text: errors[option.id] }),
|
|
29
|
+
React.createElement("input", { id: option.id, onBlur: (e) => {
|
|
30
|
+
if (findOptionDuplicate({ id: e.target.id, text: e.target.value })) {
|
|
31
|
+
setErrors((prev) => ({
|
|
32
|
+
...prev,
|
|
33
|
+
[e.target.id]: t('Option already exists'),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
}, onChange: (e) => {
|
|
37
|
+
setState((prev) => {
|
|
38
|
+
const shouldAddEmptyOption = prev.options.length < MAX_POLL_OPTIONS &&
|
|
39
|
+
(!prev.options ||
|
|
40
|
+
(prev.options.slice(i + 1).length === 0 && !!e.target.value));
|
|
41
|
+
const shouldRemoveOption = prev.options && prev.options.slice(i + 1).length > 0 && !e.target.value;
|
|
42
|
+
const optionListHead = prev.options ? prev.options.slice(0, i) : [];
|
|
43
|
+
const optionListTail = shouldAddEmptyOption
|
|
44
|
+
? [{ id: nanoid(), text: '' }]
|
|
45
|
+
: (prev.options || []).slice(i + 1);
|
|
46
|
+
if ((errors[option.id] && !e.target.value) ||
|
|
47
|
+
(VALIDATION_ERRORS[errors[option.id]] &&
|
|
48
|
+
!findOptionDuplicate({ id: e.target.id, text: e.target.value }))) {
|
|
49
|
+
setErrors((prev) => {
|
|
50
|
+
delete prev[option.id];
|
|
51
|
+
return prev;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...prev,
|
|
56
|
+
options: [
|
|
57
|
+
...optionListHead,
|
|
58
|
+
...(shouldRemoveOption ? [] : [{ ...option, text: e.target.value }]),
|
|
59
|
+
...optionListTail,
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}, onKeyUp: (event) => {
|
|
64
|
+
if (event.key === 'Enter') {
|
|
65
|
+
const nextInputId = options[i + 1].id;
|
|
66
|
+
document.getElementById(nextInputId)?.focus();
|
|
67
|
+
}
|
|
68
|
+
}, placeholder: t('Add an option'), type: 'text', value: option.text })),
|
|
69
|
+
draggable && React.createElement("div", { className: 'str-chat__drag-handle' })))))));
|
|
70
|
+
};
|