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.
Files changed (180) hide show
  1. package/dist/assets/icons/stream-chat-icons.eot +0 -0
  2. package/dist/assets/icons/stream-chat-icons.svg +4 -0
  3. package/dist/assets/icons/stream-chat-icons.ttf +0 -0
  4. package/dist/assets/icons/stream-chat-icons.woff +0 -0
  5. package/dist/assets/icons/stream-chat-icons.woff2 +0 -0
  6. package/dist/components/Attachment/components/ProgressBar.d.ts +2 -2
  7. package/dist/components/Attachment/components/ProgressBar.js +2 -1
  8. package/dist/components/Channel/Channel.d.ts +1 -1
  9. package/dist/components/Channel/Channel.js +36 -15
  10. package/dist/components/Channel/constants.d.ts +1 -0
  11. package/dist/components/Channel/constants.js +1 -0
  12. package/dist/components/Channel/hooks/useChannelContainerClasses.d.ts +2 -0
  13. package/dist/components/Channel/hooks/useChannelContainerClasses.js +10 -5
  14. package/dist/components/ChannelPreview/utils.js +35 -0
  15. package/dist/components/Chat/hooks/useChat.js +2 -0
  16. package/dist/components/Dialog/DialogAnchor.d.ts +1 -2
  17. package/dist/components/Dialog/DialogMenu.d.ts +3 -0
  18. package/dist/components/Dialog/DialogMenu.js +5 -0
  19. package/dist/components/Dialog/DialogPortal.d.ts +1 -1
  20. package/dist/components/Dialog/DialogPortal.js +4 -12
  21. package/dist/components/Dialog/FormDialog.d.ts +23 -0
  22. package/dist/components/Dialog/FormDialog.js +72 -0
  23. package/dist/components/Dialog/PromptDialog.d.ts +8 -0
  24. package/dist/components/Dialog/PromptDialog.js +7 -0
  25. package/dist/components/DragAndDrop/DragAndDropContainer.d.ts +7 -0
  26. package/dist/components/DragAndDrop/DragAndDropContainer.js +93 -0
  27. package/dist/components/Form/FieldError.d.ts +6 -0
  28. package/dist/components/Form/FieldError.js +3 -0
  29. package/dist/components/Form/SwitchField.d.ts +7 -0
  30. package/dist/components/Form/SwitchField.js +21 -0
  31. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.d.ts +10 -0
  32. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +10 -0
  33. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts +10 -0
  34. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.js +68 -0
  35. package/dist/components/InfiniteScrollPaginator/hooks/useCursorPaginator.d.ts +18 -0
  36. package/dist/components/InfiniteScrollPaginator/hooks/useCursorPaginator.js +41 -0
  37. package/dist/components/Message/MessageSimple.js +5 -1
  38. package/dist/components/Message/QuotedMessage.js +8 -4
  39. package/dist/components/Message/hooks/useUserRole.js +3 -2
  40. package/dist/components/Message/index.d.ts +1 -0
  41. package/dist/components/MessageInput/AttachmentSelector.d.ts +25 -0
  42. package/dist/components/MessageInput/AttachmentSelector.js +125 -0
  43. package/dist/components/MessageInput/EditMessageForm.js +1 -1
  44. package/dist/components/MessageInput/MessageInput.d.ts +2 -0
  45. package/dist/components/MessageInput/MessageInput.js +9 -4
  46. package/dist/components/MessageInput/MessageInputFlat.js +4 -10
  47. package/dist/components/MessageInput/QuotedMessagePreview.js +7 -3
  48. package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +3 -1
  49. package/dist/components/MessageInput/hooks/useSubmitHandler.js +4 -1
  50. package/dist/components/MessageInput/index.d.ts +1 -0
  51. package/dist/components/MessageInput/index.js +1 -0
  52. package/dist/components/Modal/ModalHeader.d.ts +8 -0
  53. package/dist/components/Modal/ModalHeader.js +6 -0
  54. package/dist/components/Poll/Poll.d.ts +7 -0
  55. package/dist/components/Poll/Poll.js +8 -0
  56. package/dist/components/Poll/PollActions/AddCommentForm.d.ts +7 -0
  57. package/dist/components/Poll/PollActions/AddCommentForm.js +24 -0
  58. package/dist/components/Poll/PollActions/EndPollDialog.d.ts +6 -0
  59. package/dist/components/Poll/PollActions/EndPollDialog.js +19 -0
  60. package/dist/components/Poll/PollActions/PollAction.d.ts +9 -0
  61. package/dist/components/Poll/PollActions/PollAction.js +5 -0
  62. package/dist/components/Poll/PollActions/PollActions.d.ts +17 -0
  63. package/dist/components/Poll/PollActions/PollActions.js +46 -0
  64. package/dist/components/Poll/PollActions/PollAnswerList.d.ts +7 -0
  65. package/dist/components/Poll/PollActions/PollAnswerList.js +28 -0
  66. package/dist/components/Poll/PollActions/PollOptionsFullList.d.ts +6 -0
  67. package/dist/components/Poll/PollActions/PollOptionsFullList.js +16 -0
  68. package/dist/components/Poll/PollActions/PollResults/PollOptionVotesList.d.ts +7 -0
  69. package/dist/components/Poll/PollActions/PollResults/PollOptionVotesList.js +18 -0
  70. package/dist/components/Poll/PollActions/PollResults/PollOptionWithLatestVotes.d.ts +9 -0
  71. package/dist/components/Poll/PollActions/PollResults/PollOptionWithLatestVotes.js +19 -0
  72. package/dist/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.d.ts +11 -0
  73. package/dist/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.js +18 -0
  74. package/dist/components/Poll/PollActions/PollResults/PollResults.d.ts +6 -0
  75. package/dist/components/Poll/PollActions/PollResults/PollResults.js +33 -0
  76. package/dist/components/Poll/PollActions/PollResults/index.d.ts +1 -0
  77. package/dist/components/Poll/PollActions/PollResults/index.js +1 -0
  78. package/dist/components/Poll/PollActions/SuggestPollOptionForm.d.ts +7 -0
  79. package/dist/components/Poll/PollActions/SuggestPollOptionForm.js +37 -0
  80. package/dist/components/Poll/PollActions/index.d.ts +7 -0
  81. package/dist/components/Poll/PollActions/index.js +7 -0
  82. package/dist/components/Poll/PollContent.d.ts +3 -0
  83. package/dist/components/Poll/PollContent.js +18 -0
  84. package/dist/components/Poll/PollCreationDialog/OptionFieldSet.d.ts +9 -0
  85. package/dist/components/Poll/PollCreationDialog/OptionFieldSet.js +70 -0
  86. package/dist/components/Poll/PollCreationDialog/PollCreationDialog.d.ts +5 -0
  87. package/dist/components/Poll/PollCreationDialog/PollCreationDialog.js +87 -0
  88. package/dist/components/Poll/PollCreationDialog/PollCreationDialogControls.d.ts +8 -0
  89. package/dist/components/Poll/PollCreationDialog/PollCreationDialogControls.js +44 -0
  90. package/dist/components/Poll/PollCreationDialog/index.d.ts +1 -0
  91. package/dist/components/Poll/PollCreationDialog/index.js +1 -0
  92. package/dist/components/Poll/PollCreationDialog/types.d.ts +21 -0
  93. package/dist/components/Poll/PollCreationDialog/types.js +1 -0
  94. package/dist/components/Poll/PollHeader.d.ts +3 -0
  95. package/dist/components/Poll/PollHeader.js +31 -0
  96. package/dist/components/Poll/PollOptionList.d.ts +6 -0
  97. package/dist/components/Poll/PollOptionList.js +14 -0
  98. package/dist/components/Poll/PollOptionSelector.d.ts +19 -0
  99. package/dist/components/Poll/PollOptionSelector.js +53 -0
  100. package/dist/components/Poll/PollVote.d.ts +12 -0
  101. package/dist/components/Poll/PollVote.js +31 -0
  102. package/dist/components/Poll/QuotedPoll.d.ts +3 -0
  103. package/dist/components/Poll/QuotedPoll.js +17 -0
  104. package/dist/components/Poll/constants.d.ts +3 -0
  105. package/dist/components/Poll/constants.js +3 -0
  106. package/dist/components/Poll/hooks/index.d.ts +2 -0
  107. package/dist/components/Poll/hooks/index.js +2 -0
  108. package/dist/components/Poll/hooks/useManagePollVotesRealtime.d.ts +4 -0
  109. package/dist/components/Poll/hooks/useManagePollVotesRealtime.js +36 -0
  110. package/dist/components/Poll/hooks/usePollAnswerPagination.d.ts +13 -0
  111. package/dist/components/Poll/hooks/usePollAnswerPagination.js +27 -0
  112. package/dist/components/Poll/hooks/usePollOptionVotesPagination.d.ts +13 -0
  113. package/dist/components/Poll/hooks/usePollOptionVotesPagination.js +27 -0
  114. package/dist/components/Poll/index.d.ts +10 -0
  115. package/dist/components/Poll/index.js +10 -0
  116. package/dist/components/Portal/Portal.d.ts +6 -0
  117. package/dist/components/Portal/Portal.js +14 -0
  118. package/dist/components/ReactFileUtilities/UploadButton.d.ts +11 -1
  119. package/dist/components/ReactFileUtilities/UploadButton.js +22 -4
  120. package/dist/components/Thread/Thread.js +1 -1
  121. package/dist/components/index.d.ts +2 -0
  122. package/dist/components/index.js +1 -0
  123. package/dist/context/AttachmentSelectorContext.d.ts +8 -0
  124. package/dist/context/AttachmentSelectorContext.js +6 -0
  125. package/dist/context/ComponentContext.d.ts +21 -5
  126. package/dist/context/PollContext.d.ts +11 -0
  127. package/dist/context/PollContext.js +7 -0
  128. package/dist/context/index.d.ts +1 -0
  129. package/dist/context/index.js +1 -0
  130. package/dist/css/v2/index.css +2 -2
  131. package/dist/css/v2/index.layout.css +2 -2
  132. package/dist/experimental/index.browser.cjs +129 -117
  133. package/dist/experimental/index.browser.cjs.map +4 -4
  134. package/dist/experimental/index.node.cjs +129 -117
  135. package/dist/experimental/index.node.cjs.map +4 -4
  136. package/dist/i18n/Streami18n.d.ts +45 -0
  137. package/dist/i18n/de.json +70 -25
  138. package/dist/i18n/en.json +46 -1
  139. package/dist/i18n/es.json +74 -25
  140. package/dist/i18n/fr.json +83 -34
  141. package/dist/i18n/hi.json +54 -9
  142. package/dist/i18n/it.json +75 -26
  143. package/dist/i18n/ja.json +46 -5
  144. package/dist/i18n/ko.json +46 -5
  145. package/dist/i18n/nl.json +59 -14
  146. package/dist/i18n/pt.json +66 -17
  147. package/dist/i18n/ru.json +66 -13
  148. package/dist/i18n/tr.json +77 -32
  149. package/dist/index.browser.cjs +4226 -1857
  150. package/dist/index.browser.cjs.map +4 -4
  151. package/dist/index.node.cjs +4166 -1770
  152. package/dist/index.node.cjs.map +4 -4
  153. package/dist/scss/v2/AttachmentPreviewList/AttachmentPreviewList-layout.scss +2 -2
  154. package/dist/scss/v2/AudioRecorder/AudioRecorder-layout.scss +64 -14
  155. package/dist/scss/v2/AudioRecorder/AudioRecorder-theme.scss +11 -1
  156. package/dist/scss/v2/Avatar/Avatar-layout.scss +4 -0
  157. package/dist/scss/v2/ChannelList/ChannelList-layout.scss +15 -0
  158. package/dist/scss/v2/Dialog/Dialog-layout.scss +54 -0
  159. package/dist/scss/v2/Dialog/Dialog-theme.scss +103 -0
  160. package/dist/scss/v2/DragAndDropContainer/DragAmdDropContainer-layout.scss +5 -0
  161. package/dist/scss/v2/DragAndDropContainer/DragAndDropContainer-theme.scss +47 -0
  162. package/dist/scss/v2/Form/Form-layout.scss +9 -0
  163. package/dist/scss/v2/Form/Form-theme.scss +17 -0
  164. package/dist/scss/v2/Icon/Icon-layout.scss +6 -1
  165. package/dist/scss/v2/InfiniteScrollPaginator/InfiniteScrollPaginator-layout.scss +4 -0
  166. package/dist/scss/v2/MessageActionsBox/MessageActionsBox-layout.scss +0 -9
  167. package/dist/scss/v2/MessageInput/MessageInput-layout.scss +29 -4
  168. package/dist/scss/v2/MessageInput/MessageInput-theme.scss +61 -0
  169. package/dist/scss/v2/Modal/Modal-layout.scss +31 -0
  170. package/dist/scss/v2/Modal/Modal-theme.scss +6 -0
  171. package/dist/scss/v2/Notification/MessageNotification-layout.scss +1 -1
  172. package/dist/scss/v2/Poll/Poll-layout.scss +488 -0
  173. package/dist/scss/v2/Poll/Poll-theme.scss +206 -0
  174. package/dist/scss/v2/_base.scss +4 -0
  175. package/dist/scss/v2/_global-theme-variables.scss +1 -1
  176. package/dist/scss/v2/_icons.scss +7 -0
  177. package/dist/scss/v2/index.layout.scss +4 -0
  178. package/dist/scss/v2/index.scss +4 -0
  179. package/dist/types/types.d.ts +6 -0
  180. package/package.json +4 -4
@@ -0,0 +1,87 @@
1
+ import clsx from 'clsx';
2
+ import { nanoid } from 'nanoid';
3
+ import React, { useState } from 'react';
4
+ import { FieldError } from '../../Form/FieldError';
5
+ import { OptionFieldSet } from './OptionFieldSet';
6
+ import { PollCreationDialogControls } from './PollCreationDialogControls';
7
+ import { VALID_MAX_VOTES_VALUE_REGEX } from '../constants';
8
+ import { ModalHeader } from '../../Modal/ModalHeader';
9
+ import { SimpleSwitchField } from '../../Form/SwitchField';
10
+ import { useChatContext, useTranslationContext } from '../../../context';
11
+ export const PollCreationDialog = ({ close }) => {
12
+ const { client } = useChatContext();
13
+ const { t } = useTranslationContext();
14
+ const [nameError, setNameError] = useState();
15
+ const [optionsErrors, setOptionsErrors] = useState({});
16
+ const [multipleAnswerCountError, setMultipleAnswerCountError] = useState();
17
+ const [state, setState] = useState(() => ({
18
+ allow_answers: false,
19
+ allow_user_suggested_options: false,
20
+ description: '',
21
+ enforce_unique_vote: true,
22
+ id: nanoid(),
23
+ max_votes_allowed: '',
24
+ name: '',
25
+ options: [{ id: nanoid(), text: '' }],
26
+ user_id: client.user?.id,
27
+ voting_visibility: 'public',
28
+ }));
29
+ return (React.createElement("div", { className: 'str-chat__dialog str-chat__poll-creation-dialog', "data-testid": 'poll-creation-dialog' },
30
+ React.createElement(ModalHeader, { close: close, title: t('Create poll') }),
31
+ React.createElement("div", { className: 'str-chat__dialog__body' },
32
+ React.createElement("form", { autoComplete: 'off' },
33
+ React.createElement("div", { className: clsx('str-chat__form__field str-chat__form__input-field str-chat__form__input-field--with-label', {
34
+ 'str-chat__form__input-field--has-error': nameError,
35
+ }) },
36
+ React.createElement("label", { className: 'str-chat__form__field-label', htmlFor: 'name' }, t('Question')),
37
+ React.createElement("div", { className: clsx('str-chat__form__input-field__value') },
38
+ React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-name-input-field-error', text: nameError }),
39
+ React.createElement("input", { id: 'name', onBlur: (e) => {
40
+ if (!e.target.value) {
41
+ setNameError('The field is required');
42
+ }
43
+ }, onChange: (e) => {
44
+ setState((prev) => ({ ...prev, name: e.target.value }));
45
+ if (nameError && e.target.value) {
46
+ setNameError(undefined);
47
+ }
48
+ }, placeholder: t('Ask a question'), type: 'text', value: state.name }))),
49
+ React.createElement(OptionFieldSet, { errors: optionsErrors, options: state.options, setErrors: setOptionsErrors, setState: setState }),
50
+ React.createElement("div", { className: clsx('str-chat__form__expandable-field', {
51
+ 'str-chat__form__expandable-field--expanded': !state.enforce_unique_vote,
52
+ }) },
53
+ React.createElement(SimpleSwitchField, { checked: !state.enforce_unique_vote, id: 'enforce_unique_vote', labelText: t('Multiple answers'), onChange: (e) => {
54
+ setState((prev) => ({
55
+ ...prev,
56
+ enforce_unique_vote: !e.target.checked,
57
+ max_votes_allowed: '',
58
+ }));
59
+ setMultipleAnswerCountError(undefined);
60
+ } }),
61
+ !state.enforce_unique_vote && (React.createElement("div", { className: clsx('str-chat__form__input-field', {
62
+ 'str-chat__form__input-field--has-error': multipleAnswerCountError,
63
+ }) },
64
+ React.createElement("div", { className: clsx('str-chat__form__input-field__value') },
65
+ React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-max-votes-allowed-input-field-error', text: multipleAnswerCountError }),
66
+ React.createElement("input", { id: 'max_votes_allowed', onChange: (e) => {
67
+ const isValidValue = !e.target.value || e.target.value.match(VALID_MAX_VOTES_VALUE_REGEX);
68
+ if (!isValidValue) {
69
+ setMultipleAnswerCountError(t('Type a number from 2 to 10'));
70
+ }
71
+ else if (multipleAnswerCountError) {
72
+ setMultipleAnswerCountError(undefined);
73
+ }
74
+ setState((prev) => ({ ...prev, max_votes_allowed: e.target.value }));
75
+ }, placeholder: t('Maximum number of votes (from 2 to 10)'), type: 'number', value: state.max_votes_allowed }))))),
76
+ React.createElement(SimpleSwitchField, { checked: state.voting_visibility === 'anonymous', id: 'voting_visibility', labelText: t('Anonymous poll'), onChange: (e) => setState((prev) => ({
77
+ ...prev,
78
+ voting_visibility: (e.target.checked ? 'anonymous' : 'public'),
79
+ })) }),
80
+ React.createElement(SimpleSwitchField, { checked: state.allow_user_suggested_options, id: 'allow_user_suggested_options', labelText: t('Allow option suggestion'), onChange: (e) => setState((prev) => ({ ...prev, allow_user_suggested_options: e.target.checked })) }),
81
+ React.createElement(SimpleSwitchField, { checked: state.allow_answers, id: 'allow_answers', labelText: t('Allow comments'), onChange: (e) => setState((prev) => ({ ...prev, allow_answers: e.target.checked })) }))),
82
+ React.createElement(PollCreationDialogControls, { close: close, errors: [
83
+ ...(nameError ?? []),
84
+ ...(multipleAnswerCountError ?? []),
85
+ ...Object.keys(optionsErrors),
86
+ ], state: state })));
87
+ };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { PollFormState } from './types';
3
+ export type PollCreationDialogControlsProps = {
4
+ close: () => void;
5
+ errors: string[];
6
+ state: PollFormState;
7
+ };
8
+ export declare const PollCreationDialogControls: ({ close, errors, state, }: PollCreationDialogControlsProps) => React.JSX.Element;
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { VALID_MAX_VOTES_VALUE_REGEX } from '../constants';
3
+ import { useChatContext, useMessageInputContext, useTranslationContext } from '../../../context';
4
+ export const PollCreationDialogControls = ({ close, errors, state, }) => {
5
+ const { client } = useChatContext();
6
+ const { t } = useTranslationContext('PollCreationDialogControls');
7
+ const { handleSubmit: handleSubmitMessage } = useMessageInputContext('PollCreationDialogControls');
8
+ const canSubmit = () => {
9
+ const hasAtLeastOneOption = state.options.filter((o) => !!o.text).length > 0;
10
+ const hasName = !!state.name;
11
+ const maxVotesAllowedNumber = parseInt(state.max_votes_allowed?.match(VALID_MAX_VOTES_VALUE_REGEX)?.[0] || '');
12
+ const validMaxVotesAllowed = state.max_votes_allowed === '' ||
13
+ (!!maxVotesAllowedNumber && (2 <= maxVotesAllowedNumber || maxVotesAllowedNumber <= 10));
14
+ const noErrors = errors.length === 0;
15
+ return hasAtLeastOneOption && hasName && validMaxVotesAllowed && noErrors;
16
+ };
17
+ return (React.createElement("div", { className: 'str-chat__dialog__controls' },
18
+ React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--cancel', onClick: close }, t('Cancel')),
19
+ React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--submit', disabled: !canSubmit(), onClick: async (e) => {
20
+ let pollId;
21
+ try {
22
+ const { poll } = await client.createPoll({
23
+ ...state,
24
+ max_votes_allowed: state.max_votes_allowed
25
+ ? parseInt(state.max_votes_allowed)
26
+ : undefined,
27
+ options: state.options?.filter((o) => o.text).map((o) => ({ text: o.text })),
28
+ });
29
+ pollId = poll.id;
30
+ }
31
+ catch (e) {
32
+ // todo: add notification
33
+ return;
34
+ }
35
+ try {
36
+ await handleSubmitMessage(e, { poll_id: pollId });
37
+ }
38
+ catch (e) {
39
+ // todo: add notification
40
+ return;
41
+ }
42
+ close();
43
+ }, type: 'submit' }, t('Create'))));
44
+ };
@@ -0,0 +1 @@
1
+ export * from './PollCreationDialog';
@@ -0,0 +1 @@
1
+ export * from './PollCreationDialog';
@@ -0,0 +1,21 @@
1
+ import type { VotingVisibility } from 'stream-chat';
2
+ type Id = string;
3
+ export type PollOptionFormData = {
4
+ id: Id;
5
+ text: string;
6
+ };
7
+ export type PollFormState = {
8
+ id: Id;
9
+ max_votes_allowed: string;
10
+ name: string;
11
+ options: PollOptionFormData[];
12
+ allow_answers?: boolean;
13
+ allow_user_suggested_options?: boolean;
14
+ description?: string;
15
+ enforce_unique_vote?: boolean;
16
+ is_closed?: boolean;
17
+ user_id?: string;
18
+ voting_visibility?: VotingVisibility;
19
+ };
20
+ export type OptionErrors = Record<Id, string>;
21
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../types';
3
+ export declare const PollHeader: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element | undefined;
@@ -0,0 +1,31 @@
1
+ import React, { useMemo } from 'react';
2
+ import { usePollContext, useTranslationContext } from '../../context';
3
+ import { useStateStore } from '../../store';
4
+ const pollStateSelector = (nextValue) => ({
5
+ enforce_unique_vote: nextValue.enforce_unique_vote,
6
+ is_closed: nextValue.is_closed,
7
+ max_votes_allowed: nextValue.max_votes_allowed,
8
+ name: nextValue.name,
9
+ options: nextValue.options,
10
+ });
11
+ export const PollHeader = () => {
12
+ const { t } = useTranslationContext('PollHeader');
13
+ const { poll } = usePollContext();
14
+ const { enforce_unique_vote, is_closed, max_votes_allowed, name, options } = useStateStore(poll.state, pollStateSelector);
15
+ const selectionInstructions = useMemo(() => {
16
+ if (is_closed)
17
+ return t('Vote ended');
18
+ if (enforce_unique_vote)
19
+ return t('Select one');
20
+ if (max_votes_allowed)
21
+ return t('Select up to {{count}}', {
22
+ count: max_votes_allowed > options.length ? options.length : max_votes_allowed,
23
+ });
24
+ return t('Select one or more');
25
+ }, [is_closed, enforce_unique_vote, max_votes_allowed, options.length, t]);
26
+ if (!name)
27
+ return;
28
+ return (React.createElement("div", { className: 'str-chat__poll-header' },
29
+ React.createElement("div", { className: 'str-chat__poll-title' }, name),
30
+ React.createElement("div", { className: 'str-chat__poll-subtitle' }, selectionInstructions)));
31
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../types';
3
+ export type PollOptionListProps = {
4
+ optionsDisplayCount?: number;
5
+ };
6
+ export declare const PollOptionList: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ optionsDisplayCount, }: PollOptionListProps) => React.JSX.Element;
@@ -0,0 +1,14 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import { PollOptionSelector as DefaultPollOptionSelector } from './PollOptionSelector';
4
+ import { useStateStore } from '../../store';
5
+ import { useComponentContext, usePollContext } from '../../context';
6
+ const pollStateSelector = (nextValue) => ({ options: nextValue.options });
7
+ export const PollOptionList = ({ optionsDisplayCount, }) => {
8
+ const { PollOptionSelector = DefaultPollOptionSelector, } = useComponentContext();
9
+ const { poll } = usePollContext();
10
+ const { options } = useStateStore(poll.state, pollStateSelector);
11
+ return (React.createElement("div", { className: clsx('str-chat__poll-option-list', {
12
+ 'str-chat__poll-option-list--full': typeof optionsDisplayCount === 'undefined',
13
+ }) }, options.slice(0, optionsDisplayCount ?? options.length).map((option) => (React.createElement(PollOptionSelector, { displayAvatarCount: 3, key: `poll-option-${option.id}`, option: option })))));
14
+ };
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import type { PollOption } from 'stream-chat';
3
+ import type { DefaultStreamChatGenerics } from '../../types';
4
+ type AmountBarProps = {
5
+ amount: number;
6
+ className?: string;
7
+ };
8
+ export declare const AmountBar: ({ amount, className }: AmountBarProps) => React.JSX.Element;
9
+ export type CheckmarkProps = {
10
+ checked?: boolean;
11
+ };
12
+ export declare const Checkmark: ({ checked }: CheckmarkProps) => React.JSX.Element;
13
+ export type PollOptionSelectorProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
14
+ option: PollOption<StreamChatGenerics>;
15
+ displayAvatarCount?: number;
16
+ voteCountVerbose?: boolean;
17
+ };
18
+ export declare const PollOptionSelector: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ displayAvatarCount, option, voteCountVerbose, }: PollOptionSelectorProps<StreamChatGenerics>) => React.JSX.Element;
19
+ export {};
@@ -0,0 +1,53 @@
1
+ import clsx from 'clsx';
2
+ import debounce from 'lodash.debounce';
3
+ import React, { useMemo } from 'react';
4
+ import { isVoteAnswer } from 'stream-chat';
5
+ import { Avatar } from '../Avatar';
6
+ import { useChannelStateContext, useMessageContext, usePollContext, useTranslationContext, } from '../../context';
7
+ import { useStateStore } from '../../store';
8
+ export const AmountBar = ({ amount, className }) => (React.createElement("div", { className: clsx('str-chat__amount-bar', className), "data-testid": 'amount-bar', role: 'progressbar', style: {
9
+ '--str-chat__amount-bar-fulfillment': amount + '%',
10
+ } }));
11
+ export const Checkmark = ({ checked }) => (React.createElement("div", { className: clsx('str-chat__checkmark', { 'str-chat__checkmark--checked': checked }) }));
12
+ const pollStateSelector = (nextValue) => ({
13
+ is_closed: nextValue.is_closed,
14
+ latest_votes_by_option: nextValue.latest_votes_by_option,
15
+ maxVotedOptionIds: nextValue.maxVotedOptionIds,
16
+ ownVotesByOptionId: nextValue.ownVotesByOptionId,
17
+ vote_counts_by_option: nextValue.vote_counts_by_option,
18
+ voting_visibility: nextValue.voting_visibility,
19
+ });
20
+ export const PollOptionSelector = ({ displayAvatarCount, option, voteCountVerbose, }) => {
21
+ const { t } = useTranslationContext();
22
+ const { channelCapabilities = {} } = useChannelStateContext('PollOptionsShortlist');
23
+ const { message } = useMessageContext();
24
+ const { poll } = usePollContext();
25
+ const { is_closed, latest_votes_by_option, maxVotedOptionIds, ownVotesByOptionId, vote_counts_by_option, voting_visibility, } = useStateStore(poll.state, pollStateSelector);
26
+ const canCastVote = channelCapabilities['cast-poll-vote'] && !is_closed;
27
+ const winningOptionCount = maxVotedOptionIds[0] ? vote_counts_by_option[maxVotedOptionIds[0]] : 0;
28
+ const toggleVote = useMemo(() => debounce(() => {
29
+ if (!canCastVote)
30
+ return;
31
+ const haveVotedForTheOption = !!ownVotesByOptionId[option.id];
32
+ return haveVotedForTheOption
33
+ ? poll.removeVote(ownVotesByOptionId[option.id].id, message.id)
34
+ : poll.castVote(option.id, message.id);
35
+ }, 100), [canCastVote, message.id, option.id, ownVotesByOptionId, poll]);
36
+ return (React.createElement("div", { className: clsx('str-chat__poll-option', {
37
+ 'str-chat__poll-option--votable': canCastVote,
38
+ }), key: `base-poll-option-${option.id}`, onClick: toggleVote },
39
+ canCastVote && React.createElement(Checkmark, { checked: !!ownVotesByOptionId[option.id] }),
40
+ React.createElement("div", { className: 'str-chat__poll-option-data' },
41
+ React.createElement("p", { className: 'str-chat__poll-option-text' }, option.text),
42
+ displayAvatarCount && voting_visibility === 'public' && (React.createElement("div", { className: 'str-chat__poll-option-voters' }, latest_votes_by_option?.[option.id] &&
43
+ latest_votes_by_option[option.id]
44
+ .filter((vote) => !!vote.user && !isVoteAnswer(vote))
45
+ .slice(0, displayAvatarCount)
46
+ .map(({ user }) => (React.createElement(Avatar, { image: user?.image, key: `poll-option-${option.id}-avatar-${user?.id}`, name: user?.name }))))),
47
+ React.createElement("div", { className: 'str-chat__poll-option-vote-count' }, voteCountVerbose
48
+ ? t('{{count}} votes', { count: vote_counts_by_option[option.id] ?? 0 })
49
+ : vote_counts_by_option[option.id] ?? 0)),
50
+ React.createElement(AmountBar, { amount: (winningOptionCount && (vote_counts_by_option[option.id] ?? 0) / winningOptionCount) * 100, className: clsx('str-chat__poll-option__votes-bar', {
51
+ 'str-chat__poll-option__votes-bar--winner': is_closed && maxVotedOptionIds.length === 1 && maxVotedOptionIds[0] === option.id,
52
+ }) })));
53
+ };
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { PollVote as PollVoteType } from 'stream-chat';
3
+ import type { DefaultStreamChatGenerics } from '../../types';
4
+ type PollVoteProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
5
+ vote: PollVoteType<StreamChatGenerics>;
6
+ };
7
+ export declare const PollVote: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ vote, }: PollVoteProps<StreamChatGenerics>) => React.JSX.Element;
8
+ export type PollVoteListingProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
9
+ votes: PollVoteType<StreamChatGenerics>[];
10
+ };
11
+ export declare const PollVoteListing: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ votes, }: PollVoteListingProps<StreamChatGenerics>) => React.JSX.Element;
12
+ export {};
@@ -0,0 +1,31 @@
1
+ import React, { useState } from 'react';
2
+ import { Avatar } from '../Avatar';
3
+ import { PopperTooltip } from '../Tooltip';
4
+ import { useEnterLeaveHandlers } from '../Tooltip/hooks';
5
+ import { useChatContext, useTranslationContext } from '../../context';
6
+ const PollVoteTimestamp = ({ timestamp }) => {
7
+ const { t } = useTranslationContext();
8
+ const { handleEnter, handleLeave, tooltipVisible } = useEnterLeaveHandlers();
9
+ const [referenceElement, setReferenceElement] = useState(null);
10
+ const timestampDate = new Date(timestamp);
11
+ return (React.createElement("div", { className: 'str-chat__poll-vote__timestamp', onMouseEnter: handleEnter, onMouseLeave: handleLeave, ref: setReferenceElement },
12
+ t('timestamp/PollVote', { timestamp: timestampDate }),
13
+ React.createElement(PopperTooltip, { offset: [0, 5], placement: 'bottom', referenceElement: referenceElement, visible: tooltipVisible }, t('timestamp/PollVoteTooltip', { timestamp: timestampDate }))));
14
+ };
15
+ const PollVoteAuthor = ({ vote, }) => {
16
+ const { t } = useTranslationContext();
17
+ const { client } = useChatContext();
18
+ const { handleEnter, handleLeave, tooltipVisible } = useEnterLeaveHandlers();
19
+ const [referenceElement, setReferenceElement] = useState(null);
20
+ const displayName = client.user?.id && client.user.id === vote.user?.id
21
+ ? t('You')
22
+ : vote.user?.name || t('Anonymous');
23
+ return (React.createElement("div", { className: 'str-chat__poll-vote__author', onMouseEnter: handleEnter, onMouseLeave: handleLeave, ref: setReferenceElement },
24
+ vote.user && (React.createElement(Avatar, { className: 'str-chat__avatar--poll-vote-author', image: vote.user.image, key: `poll-vote-${vote.id}-avatar-${vote.user.id}`, name: vote.user.name })),
25
+ React.createElement("div", { className: 'str-chat__poll-vote__author__name' }, displayName),
26
+ React.createElement(PopperTooltip, { offset: [0, 5], placement: 'bottom', referenceElement: referenceElement, visible: tooltipVisible }, displayName)));
27
+ };
28
+ export const PollVote = ({ vote, }) => (React.createElement("div", { className: 'str-chat__poll-vote' },
29
+ React.createElement(PollVoteAuthor, { vote: vote }),
30
+ React.createElement(PollVoteTimestamp, { timestamp: vote.created_at })));
31
+ export const PollVoteListing = ({ votes, }) => (React.createElement("div", { className: 'str-chat__poll-vote-listing' }, votes.map((vote) => (React.createElement(PollVote, { key: `poll-vote-${vote.id}`, vote: vote })))));
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../types';
3
+ export declare const QuotedPoll: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import { usePollContext } from '../../context';
4
+ import { useStateStore } from '../../store';
5
+ const pollStateSelectorQuotedPoll = (nextValue) => ({
6
+ is_closed: nextValue.is_closed,
7
+ name: nextValue.name,
8
+ });
9
+ export const QuotedPoll = () => {
10
+ const { poll } = usePollContext();
11
+ const { is_closed, name } = useStateStore(poll.state, pollStateSelectorQuotedPoll);
12
+ return (React.createElement("div", { className: clsx('str-chat__quoted-poll-preview', {
13
+ 'str-chat__quoted-poll-preview--closed': is_closed,
14
+ }) },
15
+ React.createElement("div", { className: 'str-chat__quoted-poll-preview__icon' }, "\uD83D\uDCCA"),
16
+ React.createElement("div", { className: 'str-chat__quoted-poll-preview__name' }, name)));
17
+ };
@@ -0,0 +1,3 @@
1
+ export declare const MAX_POLL_OPTIONS: 100;
2
+ export declare const VALID_MAX_VOTES_VALUE_REGEX: RegExp;
3
+ export declare const MAX_OPTIONS_DISPLAYED: 10;
@@ -0,0 +1,3 @@
1
+ export const MAX_POLL_OPTIONS = 100;
2
+ export const VALID_MAX_VOTES_VALUE_REGEX = /^([2-9]|10)$/;
3
+ export const MAX_OPTIONS_DISPLAYED = 10;
@@ -0,0 +1,2 @@
1
+ export * from './usePollAnswerPagination';
2
+ export * from './usePollOptionVotesPagination';
@@ -0,0 +1,2 @@
1
+ export * from './usePollAnswerPagination';
2
+ export * from './usePollOptionVotesPagination';
@@ -0,0 +1,4 @@
1
+ import type { PollAnswer, PollVote } from 'stream-chat';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ import { CursorPaginatorStateStore } from '../../InfiniteScrollPaginator/hooks/useCursorPaginator';
4
+ export declare function useManagePollVotesRealtime<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, T extends PollVote<StreamChatGenerics> | PollAnswer<StreamChatGenerics> = PollVote<StreamChatGenerics>>(managedVoteType: 'answer' | 'vote', cursorPaginatorState?: CursorPaginatorStateStore<T>, optionId?: string): T[];
@@ -0,0 +1,36 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { isVoteAnswer } from 'stream-chat';
3
+ import { useChatContext } from '../../../context';
4
+ export function useManagePollVotesRealtime(managedVoteType, cursorPaginatorState, optionId) {
5
+ const { client } = useChatContext();
6
+ const [votesInRealtime, setVotesInRealtime] = useState(cursorPaginatorState?.getLatestValue().items ?? []);
7
+ useEffect(() => cursorPaginatorState?.subscribeWithSelector((state) => [state.latestPageItems], ([latestPageItems]) => setVotesInRealtime((prev) => [...prev, ...latestPageItems])), [cursorPaginatorState]);
8
+ useEffect(() => {
9
+ const handleVoteEvent = (event) => {
10
+ if (!event.poll_vote)
11
+ return;
12
+ const isAnswer = isVoteAnswer(event.poll_vote);
13
+ if ((managedVoteType === 'answer' && !isAnswer) ||
14
+ (managedVoteType === 'vote' && (isAnswer || event.poll_vote.option_id !== optionId)))
15
+ return;
16
+ if (event.type === 'poll.vote_removed') {
17
+ setVotesInRealtime((prev) => event.poll_vote ? prev.filter((vote) => vote.id !== event.poll_vote.id) : prev);
18
+ }
19
+ if (event.type === 'poll.vote_changed') {
20
+ setVotesInRealtime((prev) => event.poll_vote ? prev.filter((vote) => vote.id !== event.poll_vote.id) : prev);
21
+ }
22
+ if (['poll.vote_casted', 'poll.vote_changed'].includes(event.type)) {
23
+ setVotesInRealtime((prev) => (event.poll_vote ? [event.poll_vote, ...prev] : prev));
24
+ }
25
+ };
26
+ const voteCastedSubscription = client.on('poll.vote_casted', handleVoteEvent);
27
+ const voteRemovedSubscription = client.on('poll.vote_removed', handleVoteEvent);
28
+ const voteChangedSubscription = client.on('poll.vote_changed', handleVoteEvent);
29
+ return () => {
30
+ voteCastedSubscription.unsubscribe();
31
+ voteRemovedSubscription.unsubscribe();
32
+ voteChangedSubscription.unsubscribe();
33
+ };
34
+ }, [client, optionId, managedVoteType]);
35
+ return votesInRealtime;
36
+ }
@@ -0,0 +1,13 @@
1
+ import type { PollAnswer, PollAnswersQueryParams } from 'stream-chat';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ type UsePollAnswerPaginationParams = {
4
+ paginationParams?: PollAnswersQueryParams;
5
+ };
6
+ export declare const usePollAnswerPagination: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ paginationParams }?: UsePollAnswerPaginationParams) => {
7
+ answers: PollAnswer<StreamChatGenerics>[];
8
+ error: Error | undefined;
9
+ hasNextPage: boolean;
10
+ loading: boolean;
11
+ loadMore: () => Promise<void>;
12
+ };
13
+ export {};
@@ -0,0 +1,27 @@
1
+ import { useCallback } from 'react';
2
+ import { useManagePollVotesRealtime } from './useManagePollVotesRealtime';
3
+ import { useCursorPaginator, } from '../../InfiniteScrollPaginator/hooks/useCursorPaginator';
4
+ import { usePollContext } from '../../../context';
5
+ import { useStateStore } from '../../../store';
6
+ const paginationStateSelector = (state) => [state.error, state.hasNextPage, state.loading];
7
+ export const usePollAnswerPagination = ({ paginationParams } = {}) => {
8
+ const { poll } = usePollContext();
9
+ const paginationFn = useCallback(async (next) => {
10
+ const { next: newNext, votes } = await poll.queryAnswers({
11
+ filter: paginationParams?.filter,
12
+ options: !next ? paginationParams?.options : { ...paginationParams?.options, next },
13
+ sort: { created_at: -1, ...paginationParams?.sort },
14
+ });
15
+ return { items: votes, next: newNext };
16
+ }, [paginationParams, poll]);
17
+ const { cursorPaginatorState, loadMore } = useCursorPaginator(paginationFn, true);
18
+ const answers = useManagePollVotesRealtime('answer', cursorPaginatorState);
19
+ const [error, hasNextPage, loading] = useStateStore(cursorPaginatorState, paginationStateSelector);
20
+ return {
21
+ answers,
22
+ error,
23
+ hasNextPage,
24
+ loading,
25
+ loadMore,
26
+ };
27
+ };
@@ -0,0 +1,13 @@
1
+ import type { DefaultStreamChatGenerics } from '../../../types';
2
+ import type { PollOptionVotesQueryParams, PollVote } from 'stream-chat';
3
+ type UsePollOptionVotesPaginationParams = {
4
+ paginationParams: PollOptionVotesQueryParams;
5
+ };
6
+ export declare const usePollOptionVotesPagination: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ paginationParams, }: UsePollOptionVotesPaginationParams) => {
7
+ error: Error | undefined;
8
+ hasNextPage: boolean;
9
+ loading: boolean;
10
+ loadMore: () => Promise<void>;
11
+ votes: PollVote<StreamChatGenerics>[];
12
+ };
13
+ export {};
@@ -0,0 +1,27 @@
1
+ import { useCallback } from 'react';
2
+ import { useManagePollVotesRealtime } from './useManagePollVotesRealtime';
3
+ import { useCursorPaginator, } from '../../InfiniteScrollPaginator/hooks/useCursorPaginator';
4
+ import { useStateStore } from '../../../store';
5
+ import { usePollContext } from '../../../context';
6
+ const paginationStateSelector = (state) => [state.error, state.hasNextPage, state.loading];
7
+ export const usePollOptionVotesPagination = ({ paginationParams, }) => {
8
+ const { poll } = usePollContext();
9
+ const paginationFn = useCallback(async (next) => {
10
+ const { next: newNext, votes } = await poll.queryOptionVotes({
11
+ filter: paginationParams.filter,
12
+ options: !next ? paginationParams?.options : { ...paginationParams?.options, next },
13
+ sort: { created_at: -1, ...paginationParams?.sort },
14
+ });
15
+ return { items: votes, next: newNext };
16
+ }, [paginationParams, poll]);
17
+ const { cursorPaginatorState, loadMore } = useCursorPaginator(paginationFn, true);
18
+ const votes = useManagePollVotesRealtime('vote', cursorPaginatorState, paginationParams.filter.option_id);
19
+ const [error, hasNextPage, loading] = useStateStore(cursorPaginatorState, paginationStateSelector);
20
+ return {
21
+ error,
22
+ hasNextPage,
23
+ loading,
24
+ loadMore,
25
+ votes,
26
+ };
27
+ };
@@ -0,0 +1,10 @@
1
+ export * from './Poll';
2
+ export * from './PollActions';
3
+ export * from './PollContent';
4
+ export * from './PollCreationDialog';
5
+ export * from './PollHeader';
6
+ export * from './PollOptionList';
7
+ export * from './PollOptionSelector';
8
+ export * from './PollVote';
9
+ export * from './QuotedPoll';
10
+ export * from './hooks';
@@ -0,0 +1,10 @@
1
+ export * from './Poll';
2
+ export * from './PollActions';
3
+ export * from './PollContent';
4
+ export * from './PollCreationDialog';
5
+ export * from './PollHeader';
6
+ export * from './PollOptionList';
7
+ export * from './PollOptionSelector';
8
+ export * from './PollVote';
9
+ export * from './QuotedPoll';
10
+ export * from './hooks';
@@ -0,0 +1,6 @@
1
+ import { PropsWithChildren, ReactPortal } from 'react';
2
+ export type PortalProps = {
3
+ getPortalDestination: () => Element | null;
4
+ isOpen?: boolean;
5
+ };
6
+ export declare const Portal: ({ children, getPortalDestination, isOpen, }: PropsWithChildren<PortalProps>) => ReactPortal | null;
@@ -0,0 +1,14 @@
1
+ import { useLayoutEffect, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ export const Portal = ({ children, getPortalDestination, isOpen, }) => {
4
+ const [portalDestination, setPortalDestination] = useState(null);
5
+ useLayoutEffect(() => {
6
+ const destination = getPortalDestination();
7
+ if (!destination || !isOpen)
8
+ return;
9
+ setPortalDestination(destination);
10
+ }, [getPortalDestination, isOpen]);
11
+ if (!portalDestination)
12
+ return null;
13
+ return createPortal(children, portalDestination);
14
+ };
@@ -1,6 +1,16 @@
1
1
  import React, { ComponentProps } from 'react';
2
+ import { PartialSelected } from '../../types/types';
3
+ /**
4
+ * @deprecated Use FileInputProps instead.
5
+ */
2
6
  export type UploadButtonProps = {
3
7
  onFileChange: (files: Array<File>) => void;
4
8
  resetOnChange?: boolean;
5
9
  } & Omit<ComponentProps<'input'>, 'type' | 'onChange'>;
6
- export declare const UploadButton: ({ onFileChange, resetOnChange, ...rest }: UploadButtonProps) => React.JSX.Element;
10
+ /**
11
+ * @deprecated Use FileInput instead
12
+ */
13
+ export declare const UploadButton: React.ForwardRefExoticComponent<Omit<UploadButtonProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
14
+ export type FileInputProps = UploadButtonProps;
15
+ export declare const FileInput: React.ForwardRefExoticComponent<Omit<UploadButtonProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
16
+ export declare const UploadFileInput: React.ForwardRefExoticComponent<Omit<PartialSelected<UploadButtonProps, "onFileChange">, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -1,6 +1,24 @@
1
- import React from 'react';
1
+ import clsx from 'clsx';
2
+ import { nanoid } from 'nanoid';
3
+ import React, { forwardRef, useCallback, useMemo } from 'react';
2
4
  import { useHandleFileChangeWrapper } from './utils';
3
- export const UploadButton = ({ onFileChange, resetOnChange = true, ...rest }) => {
5
+ import { useChannelStateContext, useMessageInputContext, useTranslationContext, } from '../../context';
6
+ /**
7
+ * @deprecated Use FileInput instead
8
+ */
9
+ export const UploadButton = forwardRef(function UploadButton({ onFileChange, resetOnChange = true, ...rest }, ref) {
4
10
  const handleInputChange = useHandleFileChangeWrapper(resetOnChange, onFileChange);
5
- return React.createElement("input", { onChange: handleInputChange, type: 'file', ...rest });
6
- };
11
+ return React.createElement("input", { onChange: handleInputChange, ref: ref, type: 'file', ...rest });
12
+ });
13
+ export const FileInput = UploadButton;
14
+ export const UploadFileInput = forwardRef(function UploadFileInput({ className, onFileChange: onFileChangeCustom, ...props }, ref) {
15
+ const { t } = useTranslationContext('UploadFileInput');
16
+ const { acceptedFiles = [], multipleUploads } = useChannelStateContext('UploadFileInput');
17
+ const { isUploadEnabled, maxFilesLeft, uploadNewFiles, } = useMessageInputContext('UploadFileInput');
18
+ const id = useMemo(() => nanoid(), []);
19
+ const onFileChange = useCallback((files) => {
20
+ uploadNewFiles(files);
21
+ onFileChangeCustom?.(files);
22
+ }, [onFileChangeCustom, uploadNewFiles]);
23
+ return (React.createElement(FileInput, { accept: acceptedFiles?.join(','), "aria-label": t('aria/File upload'), "data-testid": 'file-input', disabled: !isUploadEnabled || maxFilesLeft === 0, id: id, multiple: multipleUploads, ...props, className: clsx('str-chat__file-input', className), onFileChange: onFileChange, ref: ref }));
24
+ });