@droppii-org/chat-sdk 0.0.4 → 0.0.6

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 (153) hide show
  1. package/dist/assets/droppiiFontSelection.json +14521 -0
  2. package/dist/components/ChatBubble.d.ts +9 -1
  3. package/dist/components/ChatBubble.d.ts.map +1 -1
  4. package/dist/components/ChatBubble.js +23 -15
  5. package/dist/components/chat-bubble/ChatBubble.d.ts +9 -0
  6. package/dist/components/chat-bubble/ChatBubble.d.ts.map +1 -0
  7. package/dist/components/chat-bubble/ChatBubble.js +27 -0
  8. package/dist/components/conversation/DeskConversationList.d.ts +8 -0
  9. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -0
  10. package/dist/components/conversation/DeskConversationList.js +168 -0
  11. package/dist/components/icon/index.d.ts +11 -0
  12. package/dist/components/icon/index.d.ts.map +1 -0
  13. package/dist/components/icon/index.js +18 -0
  14. package/dist/components/message/MessageList.d.ts +10 -0
  15. package/dist/components/message/MessageList.d.ts.map +1 -0
  16. package/dist/components/message/MessageList.js +91 -0
  17. package/dist/components/session/AssignedSessionFilter.d.ts +7 -0
  18. package/dist/components/session/AssignedSessionFilter.d.ts.map +1 -0
  19. package/dist/components/session/AssignedSessionFilter.js +90 -0
  20. package/dist/context/ChatContext.d.ts +4 -71
  21. package/dist/context/ChatContext.d.ts.map +1 -1
  22. package/dist/context/ChatContext.js +33 -344
  23. package/dist/hooks/conversation/useConversation.d.ts +11 -0
  24. package/dist/hooks/conversation/useConversation.d.ts.map +1 -0
  25. package/dist/hooks/conversation/useConversation.js +51 -0
  26. package/dist/hooks/message/useMessage.d.ts +9 -0
  27. package/dist/hooks/message/useMessage.d.ts.map +1 -0
  28. package/dist/hooks/message/useMessage.js +46 -0
  29. package/dist/hooks/message/useSendMessage.d.ts +10 -0
  30. package/dist/hooks/message/useSendMessage.d.ts.map +1 -0
  31. package/dist/hooks/message/useSendMessage.js +42 -0
  32. package/dist/index.d.ts +9 -26
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +10 -27
  35. package/dist/screens/desk-message/index.d.ts +3 -0
  36. package/dist/screens/desk-message/index.d.ts.map +1 -0
  37. package/dist/screens/desk-message/index.js +14 -0
  38. package/dist/types/chat.d.ts +6 -36
  39. package/dist/types/chat.d.ts.map +1 -1
  40. package/dist/types/index.d.ts +0 -85
  41. package/dist/types/index.d.ts.map +1 -1
  42. package/dist/types/index.js +1 -1
  43. package/dist/types/sdk.d.ts +1 -0
  44. package/dist/types/sdk.d.ts.map +1 -0
  45. package/dist/types/sdk.js +1 -0
  46. package/package.json +19 -3
  47. package/dist/components/AutoScrollAnchor.d.ts +0 -2
  48. package/dist/components/AutoScrollAnchor.d.ts.map +0 -1
  49. package/dist/components/AutoScrollAnchor.js +0 -12
  50. package/dist/components/AutoScrollAnchor.jsx +0 -11
  51. package/dist/components/ChatBubble.jsx +0 -80
  52. package/dist/components/ChatHeader.d.ts +0 -8
  53. package/dist/components/ChatHeader.d.ts.map +0 -1
  54. package/dist/components/ChatHeader.js +0 -32
  55. package/dist/components/ChatHeader.jsx +0 -72
  56. package/dist/components/ChatInput.d.ts +0 -3
  57. package/dist/components/ChatInput.d.ts.map +0 -1
  58. package/dist/components/ChatInput.js +0 -379
  59. package/dist/components/ChatInput.jsx +0 -444
  60. package/dist/components/ChatInputDemo.d.ts +0 -2
  61. package/dist/components/ChatInputDemo.d.ts.map +0 -1
  62. package/dist/components/ChatInputDemo.js +0 -38
  63. package/dist/components/ChatInputDemo.jsx +0 -53
  64. package/dist/components/ChatInputWithCustomIcon.d.ts +0 -16
  65. package/dist/components/ChatInputWithCustomIcon.d.ts.map +0 -1
  66. package/dist/components/ChatInputWithCustomIcon.js +0 -85
  67. package/dist/components/ChatInputWithCustomIcon.jsx +0 -167
  68. package/dist/components/ChatLayout.d.ts +0 -6
  69. package/dist/components/ChatLayout.d.ts.map +0 -1
  70. package/dist/components/ChatLayout.js +0 -48
  71. package/dist/components/ChatLayout.jsx +0 -122
  72. package/dist/components/ConversationItem.d.ts +0 -9
  73. package/dist/components/ConversationItem.d.ts.map +0 -1
  74. package/dist/components/ConversationItem.js +0 -27
  75. package/dist/components/ConversationItem.jsx +0 -51
  76. package/dist/components/ConversationList.d.ts +0 -8
  77. package/dist/components/ConversationList.d.ts.map +0 -1
  78. package/dist/components/ConversationList.js +0 -11
  79. package/dist/components/ConversationList.jsx +0 -22
  80. package/dist/components/DateDivider.d.ts +0 -7
  81. package/dist/components/DateDivider.d.ts.map +0 -1
  82. package/dist/components/DateDivider.js +0 -27
  83. package/dist/components/DateDivider.jsx +0 -28
  84. package/dist/components/EmojiPicker.d.ts +0 -4
  85. package/dist/components/EmojiPicker.d.ts.map +0 -1
  86. package/dist/components/EmojiPicker.js +0 -191
  87. package/dist/components/EmojiPicker.jsx +0 -229
  88. package/dist/components/ImageLightbox.d.ts +0 -8
  89. package/dist/components/ImageLightbox.d.ts.map +0 -1
  90. package/dist/components/ImageLightbox.js +0 -8
  91. package/dist/components/ImageLightbox.jsx +0 -16
  92. package/dist/components/ImagePreviewModal.d.ts +0 -12
  93. package/dist/components/ImagePreviewModal.d.ts.map +0 -1
  94. package/dist/components/ImagePreviewModal.js +0 -55
  95. package/dist/components/ImagePreviewModal.jsx +0 -84
  96. package/dist/components/MessageItem.d.ts +0 -3
  97. package/dist/components/MessageItem.d.ts.map +0 -1
  98. package/dist/components/MessageItem.js +0 -38
  99. package/dist/components/MessageItem.jsx +0 -99
  100. package/dist/components/MessageItemDemo.d.ts +0 -2
  101. package/dist/components/MessageItemDemo.d.ts.map +0 -1
  102. package/dist/components/MessageItemDemo.js +0 -166
  103. package/dist/components/MessageItemDemo.jsx +0 -179
  104. package/dist/components/MessageList.d.ts +0 -15
  105. package/dist/components/MessageList.d.ts.map +0 -1
  106. package/dist/components/MessageList.js +0 -243
  107. package/dist/components/MessageList.jsx +0 -306
  108. package/dist/components/MessageListDemo.d.ts +0 -2
  109. package/dist/components/MessageListDemo.d.ts.map +0 -1
  110. package/dist/components/MessageListDemo.js +0 -165
  111. package/dist/components/MessageListDemo.jsx +0 -183
  112. package/dist/components/StickerPicker.d.ts +0 -4
  113. package/dist/components/StickerPicker.d.ts.map +0 -1
  114. package/dist/components/StickerPicker.js +0 -68
  115. package/dist/components/StickerPicker.jsx +0 -106
  116. package/dist/components/SwipeIndicator.d.ts +0 -9
  117. package/dist/components/SwipeIndicator.d.ts.map +0 -1
  118. package/dist/components/SwipeIndicator.js +0 -24
  119. package/dist/components/SwipeIndicator.jsx +0 -28
  120. package/dist/components/TextFormattingToolbar.d.ts +0 -4
  121. package/dist/components/TextFormattingToolbar.d.ts.map +0 -1
  122. package/dist/components/TextFormattingToolbar.js +0 -29
  123. package/dist/components/TextFormattingToolbar.jsx +0 -52
  124. package/dist/components/TypingIndicator.d.ts +0 -6
  125. package/dist/components/TypingIndicator.d.ts.map +0 -1
  126. package/dist/components/TypingIndicator.js +0 -21
  127. package/dist/components/TypingIndicator.jsx +0 -27
  128. package/dist/components/VoiceWaveIcon.d.ts +0 -7
  129. package/dist/components/VoiceWaveIcon.d.ts.map +0 -1
  130. package/dist/components/VoiceWaveIcon.js +0 -5
  131. package/dist/components/VoiceWaveIcon.jsx +0 -11
  132. package/dist/context/ChatContext.jsx +0 -346
  133. package/dist/hooks/useChat.d.ts +0 -5
  134. package/dist/hooks/useChat.d.ts.map +0 -1
  135. package/dist/hooks/useChat.js +0 -73
  136. package/dist/hooks/useConversationList.d.ts +0 -5
  137. package/dist/hooks/useConversationList.d.ts.map +0 -1
  138. package/dist/hooks/useConversationList.js +0 -9
  139. package/dist/hooks/useMessages.d.ts +0 -5
  140. package/dist/hooks/useMessages.d.ts.map +0 -1
  141. package/dist/hooks/useMessages.js +0 -192
  142. package/dist/hooks/useSocket.d.ts +0 -7
  143. package/dist/hooks/useSocket.d.ts.map +0 -1
  144. package/dist/hooks/useSocket.js +0 -120
  145. package/dist/hooks/useSwipeGesture.d.ts +0 -11
  146. package/dist/hooks/useSwipeGesture.d.ts.map +0 -1
  147. package/dist/hooks/useSwipeGesture.js +0 -54
  148. package/dist/hooks/useTextSelection.d.ts +0 -13
  149. package/dist/hooks/useTextSelection.d.ts.map +0 -1
  150. package/dist/hooks/useTextSelection.js +0 -132
  151. package/dist/hooks/useTyping.d.ts +0 -7
  152. package/dist/hooks/useTyping.d.ts.map +0 -1
  153. package/dist/hooks/useTyping.js +0 -64
@@ -1,444 +0,0 @@
1
- "use client";
2
- import { useState, useCallback, useEffect, useRef } from "react";
3
- import { Smile, ImageIcon, Paperclip, Mic, Send, Type, Sticker, User, X, Loader2, FileText } from "lucide-react"; // Import FileText icon
4
- import { useChat } from "../hooks/useChat";
5
- import { useTyping } from "../hooks/useTyping";
6
- import { useTextSelection } from "../hooks/useTextSelection";
7
- import { TextFormattingToolbar } from "./TextFormattingToolbar";
8
- import { EmojiPicker } from "./EmojiPicker";
9
- import { StickerPicker } from "./StickerPicker";
10
- export function ChatInput({ conversationId, onSendMessage, onEmojiClick, onStickerClick, onFileUpload, onImageUpload, onContactShare, onVoiceRecord, onVoiceMessage, onQuickReact, placeholder = "Nhập tin nhắn", disabled = false, className = "", }) {
11
- const [message, setMessage] = useState("");
12
- const [isRecording, setIsRecording] = useState(false);
13
- const [showVoiceWave, setShowVoiceWave] = useState(false);
14
- const [showTextToolbar, setShowTextToolbar] = useState(false);
15
- const [showEmojiPicker, setShowEmojiPicker] = useState(false);
16
- const [showStickerPicker, setShowStickerPicker] = useState(false);
17
- const [selectedFormats, setSelectedFormats] = useState([]);
18
- const [selectedFiles, setSelectedFiles] = useState([]); // State for selected files
19
- const [isUploading, setIsUploading] = useState(false); // State for uploading status
20
- const fileInputRef = useRef(null);
21
- const imageInputRef = useRef(null);
22
- // Refs for popups
23
- const textToolbarRef = useRef(null);
24
- const emojiPickerRef = useRef(null);
25
- const stickerPickerRef = useRef(null);
26
- // Refs for toggle buttons
27
- const textButtonRef = useRef(null);
28
- const emojiButtonRef = useRef(null);
29
- const stickerButtonRef = useRef(null);
30
- // Ref for the relative container that wraps the popups
31
- const popupWrapperRef = useRef(null);
32
- // State for dynamic popup styles
33
- const [textToolbarStyle, setTextToolbarStyle] = useState({});
34
- const [emojiPickerStyle, setEmojiPickerStyle] = useState({});
35
- const [stickerPickerStyle, setStickerPickerStyle] = useState({});
36
- const { sendMessage: sendChatMessage } = useChat(conversationId || "");
37
- const { startTyping, stopTyping } = useTyping(conversationId || "");
38
- const { textareaRef, applyFormat } = useTextSelection();
39
- const handleSubmit = useCallback(async (e) => {
40
- e.preventDefault();
41
- if (disabled || isUploading)
42
- return;
43
- if (selectedFiles.length > 0) {
44
- setIsUploading(true);
45
- // Simulate file upload
46
- await Promise.all(selectedFiles.map(async (file) => {
47
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate network delay
48
- if (file.type.startsWith("image/")) {
49
- onImageUpload === null || onImageUpload === void 0 ? void 0 : onImageUpload(file);
50
- }
51
- else {
52
- onFileUpload === null || onFileUpload === void 0 ? void 0 : onFileUpload(file);
53
- }
54
- }));
55
- setSelectedFiles([]);
56
- setIsUploading(false);
57
- }
58
- if (message.trim()) {
59
- // Use integrated chat system if available, otherwise use callback
60
- if (sendChatMessage) {
61
- sendChatMessage(message.trim());
62
- }
63
- else {
64
- onSendMessage === null || onSendMessage === void 0 ? void 0 : onSendMessage(message.trim());
65
- }
66
- setMessage("");
67
- setSelectedFormats([]);
68
- stopTyping();
69
- // Reset textarea height
70
- if (textareaRef.current) {
71
- textareaRef.current.style.height = "auto";
72
- }
73
- }
74
- }, [
75
- message,
76
- disabled,
77
- isUploading,
78
- selectedFiles,
79
- sendChatMessage,
80
- onSendMessage,
81
- onFileUpload,
82
- onImageUpload,
83
- stopTyping,
84
- textareaRef,
85
- ]);
86
- const handleInputChange = useCallback((e) => {
87
- const value = e.target.value;
88
- setMessage(value);
89
- // Trigger typing indicator
90
- if (value.trim()) {
91
- startTyping();
92
- }
93
- else {
94
- stopTyping();
95
- }
96
- // Auto-resize textarea
97
- const textarea = e.target;
98
- textarea.style.height = "auto";
99
- textarea.style.height = Math.min(textarea.scrollHeight, 120) + "px";
100
- }, [startTyping, stopTyping]);
101
- const handleKeyPress = useCallback((e) => {
102
- if (e.key === "Enter" && !e.shiftKey) {
103
- e.preventDefault();
104
- handleSubmit(e);
105
- }
106
- // Handle keyboard shortcuts
107
- if (e.ctrlKey || e.metaKey) {
108
- switch (e.key) {
109
- case "b":
110
- e.preventDefault();
111
- applyFormat("bold");
112
- break;
113
- case "i":
114
- e.preventDefault();
115
- applyFormat("italic");
116
- break;
117
- case "e":
118
- e.preventDefault();
119
- applyFormat("code");
120
- break;
121
- case "k":
122
- e.preventDefault();
123
- applyFormat("link");
124
- break;
125
- }
126
- }
127
- }, [handleSubmit, applyFormat]);
128
- const handleFileClick = useCallback(() => {
129
- var _a;
130
- (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
131
- }, []);
132
- const handleImageClick = useCallback(() => {
133
- var _a;
134
- (_a = imageInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
135
- }, []);
136
- const handleFileChange = useCallback((e) => {
137
- const files = Array.from(e.target.files || []);
138
- if (files.length > 0) {
139
- setSelectedFiles((prev) => [...prev, ...files]);
140
- e.target.value = ""; // Reset input to allow selecting same file again
141
- }
142
- }, []);
143
- const handleImageChange = useCallback((e) => {
144
- const files = Array.from(e.target.files || []);
145
- if (files.length > 0) {
146
- setSelectedFiles((prev) => [...prev, ...files]);
147
- e.target.value = ""; // Reset input to allow selecting same file again
148
- }
149
- }, []);
150
- const handleRemoveFile = useCallback((fileToRemove) => {
151
- setSelectedFiles((prev) => prev.filter((file) => file !== fileToRemove));
152
- }, []);
153
- const handleVoiceRecord = useCallback(() => {
154
- setIsRecording(!isRecording);
155
- onVoiceRecord === null || onVoiceRecord === void 0 ? void 0 : onVoiceRecord();
156
- }, [isRecording, onVoiceRecord]);
157
- const handleTextToolbarToggle = useCallback(() => {
158
- setShowTextToolbar((prev) => !prev);
159
- setShowEmojiPicker(false);
160
- setShowStickerPicker(false);
161
- }, []);
162
- const handleEmojiToggle = useCallback(() => {
163
- setShowEmojiPicker((prev) => !prev);
164
- setShowTextToolbar(false);
165
- setShowStickerPicker(false);
166
- }, []);
167
- const handleStickerToggle = useCallback(() => {
168
- setShowStickerPicker((prev) => !prev);
169
- setShowTextToolbar(false);
170
- setShowEmojiPicker(false);
171
- }, []);
172
- const handleFormatSelect = useCallback((format) => {
173
- applyFormat(format);
174
- setSelectedFormats((prev) => {
175
- if (prev.includes(format)) {
176
- return prev.filter((f) => f !== format);
177
- }
178
- else {
179
- return [...prev, format];
180
- }
181
- });
182
- }, [applyFormat]);
183
- const handleEmojiSelect = useCallback((emoji) => {
184
- const textarea = textareaRef.current;
185
- if (textarea) {
186
- const start = textarea.selectionStart;
187
- const end = textarea.selectionEnd;
188
- const newMessage = message.slice(0, start) + emoji + message.slice(end);
189
- setMessage(newMessage);
190
- setTimeout(() => {
191
- textarea.selectionStart = textarea.selectionEnd = start + emoji.length;
192
- textarea.focus();
193
- }, 0);
194
- }
195
- onEmojiClick === null || onEmojiClick === void 0 ? void 0 : onEmojiClick(emoji);
196
- }, [message, onEmojiClick, textareaRef]);
197
- const handleStickerSelect = useCallback((sticker) => {
198
- // Send sticker immediately (like a message)
199
- if (sendChatMessage) {
200
- sendChatMessage(sticker);
201
- }
202
- else {
203
- onSendMessage === null || onSendMessage === void 0 ? void 0 : onSendMessage(sticker);
204
- }
205
- onStickerClick === null || onStickerClick === void 0 ? void 0 : onStickerClick(sticker);
206
- setShowStickerPicker(false); // Close picker after selection
207
- }, [sendChatMessage, onSendMessage, onStickerClick]);
208
- // Click outside logic
209
- useEffect(() => {
210
- const handleClickOutside = (event) => {
211
- const target = event.target;
212
- // Text Formatting Toolbar
213
- if (showTextToolbar &&
214
- textToolbarRef.current &&
215
- !textToolbarRef.current.contains(target) &&
216
- textButtonRef.current &&
217
- !textButtonRef.current.contains(target)) {
218
- setShowTextToolbar(false);
219
- }
220
- // Emoji Picker
221
- if (showEmojiPicker &&
222
- emojiPickerRef.current &&
223
- !emojiPickerRef.current.contains(target) &&
224
- emojiButtonRef.current &&
225
- !emojiButtonRef.current.contains(target)) {
226
- setShowEmojiPicker(false);
227
- }
228
- // Sticker Picker
229
- if (showStickerPicker &&
230
- stickerPickerRef.current &&
231
- !stickerPickerRef.current.contains(target) &&
232
- stickerButtonRef.current &&
233
- !stickerButtonRef.current.contains(target)) {
234
- setShowStickerPicker(false);
235
- }
236
- };
237
- document.addEventListener("mousedown", handleClickOutside);
238
- return () => {
239
- document.removeEventListener("mousedown", handleClickOutside);
240
- };
241
- }, [showTextToolbar, showEmojiPicker, showStickerPicker]);
242
- // Dynamic positioning logic for popups
243
- const calculatePopupPosition = useCallback((buttonRef, popupRef, setPopupStyle) => {
244
- const wrapper = popupWrapperRef.current;
245
- const button = buttonRef.current;
246
- const popup = popupRef.current;
247
- if (!wrapper || !button || !popup)
248
- return;
249
- const wrapperRect = wrapper.getBoundingClientRect();
250
- const buttonRect = button.getBoundingClientRect();
251
- const popupRect = popup.getBoundingClientRect();
252
- const viewportWidth = window.innerWidth;
253
- let desiredLeft = buttonRect.left - wrapperRect.left;
254
- // Add a small margin (e.g., 8px) from the right edge of the viewport
255
- const rightViewportMargin = 8;
256
- if (wrapperRect.left + desiredLeft + popupRect.width > viewportWidth - rightViewportMargin) {
257
- desiredLeft = viewportWidth - wrapperRect.left - popupRect.width - rightViewportMargin;
258
- // Ensure it doesn't go off the left edge (e.g., 8px margin from left)
259
- desiredLeft = Math.max(8, desiredLeft);
260
- }
261
- setPopupStyle({ left: `${desiredLeft}px` });
262
- }, []);
263
- useEffect(() => {
264
- if (showTextToolbar) {
265
- calculatePopupPosition(textButtonRef, textToolbarRef, setTextToolbarStyle);
266
- window.addEventListener("resize", () => calculatePopupPosition(textButtonRef, textToolbarRef, setTextToolbarStyle));
267
- }
268
- else {
269
- setTextToolbarStyle({}); // Reset style when closed
270
- }
271
- return () => window.removeEventListener("resize", () => calculatePopupPosition(textButtonRef, textToolbarRef, setTextToolbarStyle));
272
- }, [showTextToolbar, calculatePopupPosition]);
273
- useEffect(() => {
274
- if (showEmojiPicker) {
275
- calculatePopupPosition(emojiButtonRef, emojiPickerRef, setEmojiPickerStyle);
276
- window.addEventListener("resize", () => calculatePopupPosition(emojiButtonRef, emojiPickerRef, setEmojiPickerStyle));
277
- }
278
- else {
279
- setEmojiPickerStyle({}); // Reset style when closed
280
- }
281
- return () => window.removeEventListener("resize", () => calculatePopupPosition(emojiButtonRef, emojiPickerRef, setEmojiPickerStyle));
282
- }, [showEmojiPicker, calculatePopupPosition]);
283
- useEffect(() => {
284
- if (showStickerPicker) {
285
- calculatePopupPosition(stickerButtonRef, stickerPickerRef, setStickerPickerStyle);
286
- window.addEventListener("resize", () => calculatePopupPosition(stickerButtonRef, stickerPickerRef, setStickerPickerStyle));
287
- }
288
- else {
289
- setStickerPickerStyle({}); // Reset style when closed
290
- }
291
- return () => window.removeEventListener("resize", () => calculatePopupPosition(stickerButtonRef, stickerPickerRef, setStickerPickerStyle));
292
- }, [showStickerPicker, calculatePopupPosition]);
293
- // Cleanup object URLs when selectedFiles change or component unmounts
294
- useEffect(() => {
295
- const objectUrls = [];
296
- selectedFiles.forEach((file) => {
297
- if (file.type.startsWith("image/")) {
298
- objectUrls.push(URL.createObjectURL(file));
299
- }
300
- });
301
- return () => {
302
- objectUrls.forEach((url) => URL.revokeObjectURL(url));
303
- };
304
- }, [selectedFiles]);
305
- // Small, left-aligned action buttons
306
- const actionButtons = [
307
- {
308
- icon: Type,
309
- label: "Định dạng văn bản",
310
- onClick: handleTextToolbarToggle,
311
- color: showTextToolbar ? "text-blue-500 bg-blue-50" : "text-gray-500 hover:text-blue-500",
312
- active: showTextToolbar,
313
- ref: textButtonRef,
314
- },
315
- {
316
- icon: Smile,
317
- label: "Emoji",
318
- onClick: handleEmojiToggle,
319
- color: showEmojiPicker ? "text-yellow-500 bg-yellow-50" : "text-gray-500 hover:text-yellow-500",
320
- active: showEmojiPicker,
321
- ref: emojiButtonRef,
322
- },
323
- {
324
- icon: Sticker,
325
- label: "Sticker",
326
- onClick: handleStickerToggle,
327
- color: showStickerPicker ? "text-purple-500 bg-purple-50" : "text-gray-500 hover:text-purple-500",
328
- active: showStickerPicker,
329
- ref: stickerButtonRef,
330
- },
331
- {
332
- icon: ImageIcon,
333
- label: "Hình ảnh",
334
- onClick: handleImageClick,
335
- color: "text-gray-500 hover:text-green-500",
336
- },
337
- {
338
- icon: Paperclip,
339
- label: "Tệp đính kèm",
340
- onClick: handleFileClick,
341
- color: "text-gray-500 hover:text-blue-500",
342
- },
343
- {
344
- icon: User,
345
- label: "Chia sẻ liên hệ",
346
- onClick: onContactShare,
347
- color: "text-gray-500 hover:text-indigo-500",
348
- },
349
- {
350
- icon: Mic,
351
- label: "Ghi âm",
352
- onClick: handleVoiceRecord,
353
- color: isRecording ? "text-red-500 bg-red-50" : "text-gray-500 hover:text-red-500",
354
- active: isRecording,
355
- },
356
- ];
357
- const isSendButtonDisabled = disabled || isUploading || (message.trim() === "" && selectedFiles.length === 0);
358
- return (<div className={`bg-white border-t border-gray-200 p-3 sm:p-4 ${className}`}>
359
- {/* Toolbars */}
360
- <div className="relative" ref={popupWrapperRef}>
361
- <TextFormattingToolbar ref={textToolbarRef} isOpen={showTextToolbar} onClose={() => setShowTextToolbar(false)} onFormatSelect={handleFormatSelect} selectedFormats={selectedFormats} style={textToolbarStyle}/>
362
- <EmojiPicker ref={emojiPickerRef} isOpen={showEmojiPicker} onEmojiSelect={handleEmojiSelect} onClose={() => setShowEmojiPicker(false)} style={emojiPickerStyle}/>
363
- <StickerPicker ref={stickerPickerRef} isOpen={showStickerPicker} onStickerSelect={handleStickerSelect} onClose={() => setShowStickerPicker(false)} style={stickerPickerStyle}/>
364
- </div>
365
-
366
- {/* Main Input Container */}
367
- <div className="bg-gray-50 rounded-2xl border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200">
368
- {/* Selected Files Preview */}
369
- {selectedFiles.length > 0 && (<div className="px-4 pt-3 pb-2 border-b border-gray-100 flex flex-wrap gap-2">
370
- {selectedFiles.map((file, index) => {
371
- const isImage = file.type.startsWith("image/");
372
- const fileUrl = isImage ? URL.createObjectURL(file) : null;
373
- return (<div key={index} className="flex items-center bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-1 rounded-lg relative overflow-hidden">
374
- {isImage ? (<img src={fileUrl || ""} alt={file.name} className="h-12 w-12 object-cover rounded-md mr-2"/>) : (<div className="flex items-center space-x-1">
375
- <FileText className="w-4 h-4 text-blue-600"/> {/* File icon */}
376
- <span className="truncate max-w-[100px] sm:max-w-[150px]">{file.name}</span>
377
- </div>)}
378
- <button type="button" onClick={() => handleRemoveFile(file)} className="ml-1 p-0.5 rounded-full hover:bg-blue-200 text-blue-600 absolute top-0.5 right-0.5 bg-white/70 backdrop-blur-sm" aria-label={`Remove ${file.name}`}>
379
- <X className="w-3 h-3"/>
380
- </button>
381
- </div>);
382
- })}
383
- </div>)}
384
-
385
- {/* Input Field */}
386
- <div className="px-4 py-3">
387
- <form onSubmit={handleSubmit} className="flex items-end space-x-3">
388
- <div className="flex-1 relative">
389
- <textarea ref={textareaRef} value={message} onChange={handleInputChange} onKeyDown={handleKeyPress} placeholder={placeholder} disabled={disabled || isUploading} className="w-full bg-transparent border-none outline-none resize-none text-sm sm:text-base text-gray-900 placeholder-gray-500 leading-5" rows={1} style={{
390
- minHeight: "20px",
391
- maxHeight: "120px",
392
- }}/>
393
- </div>
394
-
395
- {/* Send Button or Quick React */}
396
- <button type="submit" disabled={isSendButtonDisabled} className="flex-shrink-0 p-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200" aria-label={isUploading ? "Đang tải lên" : "Gửi tin nhắn"}>
397
- {isUploading ? <Loader2 className="w-4 h-4 animate-spin"/> : <Send className="w-4 h-4"/>}
398
- </button>
399
- </form>
400
- </div>
401
-
402
- {/* Action Icons Row - Small, Left Aligned */}
403
- <div className="px-4 pb-3 border-t border-gray-100">
404
- <div className="flex items-center space-x-2 justify-start">
405
- {actionButtons.map((button, index) => {
406
- const Icon = button.icon;
407
- return (<button key={index} ref={button.ref} // Assign ref here
408
- onClick={button.onClick} disabled={disabled || isUploading} className={`
409
- p-1.5 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed
410
- hover:bg-gray-100 active:scale-95
411
- ${button.color}
412
- `} aria-label={button.label} title={button.label}>
413
- <Icon className="w-4 h-4"/>
414
- </button>);
415
- })}
416
- </div>
417
- </div>
418
-
419
- {/* Recording Indicator */}
420
- {isRecording && (<div className="px-4 pb-2">
421
- <div className="flex items-center space-x-2 text-red-500 text-sm">
422
- <div className="w-2 h-2 bg-red-500 rounded-full animate-pulse"></div>
423
- <span>Đang ghi âm...</span>
424
- </div>
425
- </div>)}
426
-
427
- {/* Voice Wave Indicator */}
428
- {showVoiceWave && (<div className="px-4 pb-2">
429
- <div className="flex items-center space-x-1">
430
- {[...Array(8)].map((_, i) => (<div key={i} className="w-1 bg-blue-500 rounded-full animate-pulse" style={{
431
- height: `${Math.random() * 20 + 10}px`,
432
- animationDelay: `${i * 0.1}s`,
433
- }}/>))}
434
- </div>
435
- </div>)}
436
- </div>
437
-
438
- {/* Hidden File Inputs */}
439
- <input ref={fileInputRef} type="file" onChange={handleFileChange} className="hidden" accept=".pdf,.doc,.docx,.txt,.zip,.rar" multiple // Allow multiple file selection
440
- />
441
- <input ref={imageInputRef} type="file" onChange={handleImageChange} className="hidden" accept="image/*" multiple // Allow multiple image selection
442
- />
443
- </div>);
444
- }
@@ -1,2 +0,0 @@
1
- export declare function ChatInputDemo(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=ChatInputDemo.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ChatInputDemo.d.ts","sourceRoot":"","sources":["../../src/components/ChatInputDemo.tsx"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,4CA0E5B"}
@@ -1,38 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { ChatInput } from "./ChatInput";
5
- export function ChatInputDemo() {
6
- const [messages, setMessages] = useState([]);
7
- const handleSendMessage = (message) => {
8
- setMessages((prev) => [...prev, message]);
9
- console.log("Message sent:", message);
10
- };
11
- const handleEmojiClick = (emoji) => {
12
- console.log("Emoji clicked:", emoji);
13
- };
14
- const handleStickerClick = (sticker) => {
15
- console.log("Sticker clicked:", sticker);
16
- setMessages((prev) => [...prev, `Sticker: ${sticker}`]);
17
- };
18
- const handleFileUpload = (file) => {
19
- console.log("File uploaded:", file.name);
20
- setMessages((prev) => [...prev, `📎 File: ${file.name}`]);
21
- };
22
- const handleImageUpload = (file) => {
23
- console.log("Image uploaded:", file.name);
24
- setMessages((prev) => [...prev, `🖼️ Image: ${file.name}`]);
25
- };
26
- const handleContactShare = () => {
27
- console.log("Contact share clicked");
28
- setMessages((prev) => [...prev, "👤 Contact shared"]);
29
- };
30
- const handleVoiceRecord = () => {
31
- console.log("Voice record clicked");
32
- };
33
- const handleQuickReact = () => {
34
- console.log("Quick react clicked");
35
- setMessages((prev) => [...prev, "👍"]);
36
- };
37
- return (_jsxs("div", { className: "max-w-2xl mx-auto bg-white rounded-lg shadow-lg overflow-hidden", children: [_jsx("div", { className: "h-96 p-4 bg-gray-50 overflow-y-auto", children: _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "text-center text-gray-500 text-sm mb-4", children: "Demo Chat Interface" }), messages.map((message, index) => (_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "bg-blue-500 text-white px-4 py-2 rounded-2xl rounded-tr-md max-w-xs", children: message }) }, index))), messages.length === 0 && (_jsx("div", { className: "text-center text-gray-400 text-sm", children: "Type a message, select emoji, or send sticker to test the ChatInput component" }))] }) }), _jsx(ChatInput, { onSendMessage: handleSendMessage, onEmojiClick: handleEmojiClick, onStickerClick: handleStickerClick, onFileUpload: handleFileUpload, onImageUpload: handleImageUpload, onContactShare: handleContactShare, onVoiceRecord: handleVoiceRecord, onQuickReact: handleQuickReact, placeholder: "Nh\u1EADp tin nh\u1EAFn" })] }));
38
- }
@@ -1,53 +0,0 @@
1
- "use client";
2
- import { useState } from "react";
3
- import { ChatInput } from "./ChatInput";
4
- export function ChatInputDemo() {
5
- const [messages, setMessages] = useState([]);
6
- const handleSendMessage = (message) => {
7
- setMessages((prev) => [...prev, message]);
8
- console.log("Message sent:", message);
9
- };
10
- const handleEmojiClick = (emoji) => {
11
- console.log("Emoji clicked:", emoji);
12
- };
13
- const handleStickerClick = (sticker) => {
14
- console.log("Sticker clicked:", sticker);
15
- setMessages((prev) => [...prev, `Sticker: ${sticker}`]);
16
- };
17
- const handleFileUpload = (file) => {
18
- console.log("File uploaded:", file.name);
19
- setMessages((prev) => [...prev, `📎 File: ${file.name}`]);
20
- };
21
- const handleImageUpload = (file) => {
22
- console.log("Image uploaded:", file.name);
23
- setMessages((prev) => [...prev, `🖼️ Image: ${file.name}`]);
24
- };
25
- const handleContactShare = () => {
26
- console.log("Contact share clicked");
27
- setMessages((prev) => [...prev, "👤 Contact shared"]);
28
- };
29
- const handleVoiceRecord = () => {
30
- console.log("Voice record clicked");
31
- };
32
- const handleQuickReact = () => {
33
- console.log("Quick react clicked");
34
- setMessages((prev) => [...prev, "👍"]);
35
- };
36
- return (<div className="max-w-2xl mx-auto bg-white rounded-lg shadow-lg overflow-hidden">
37
- {/* Mock Chat Messages */}
38
- <div className="h-96 p-4 bg-gray-50 overflow-y-auto">
39
- <div className="space-y-3">
40
- <div className="text-center text-gray-500 text-sm mb-4">Demo Chat Interface</div>
41
- {messages.map((message, index) => (<div key={index} className="flex justify-end">
42
- <div className="bg-blue-500 text-white px-4 py-2 rounded-2xl rounded-tr-md max-w-xs">{message}</div>
43
- </div>))}
44
- {messages.length === 0 && (<div className="text-center text-gray-400 text-sm">
45
- Type a message, select emoji, or send sticker to test the ChatInput component
46
- </div>)}
47
- </div>
48
- </div>
49
-
50
- {/* Chat Input */}
51
- <ChatInput onSendMessage={handleSendMessage} onEmojiClick={handleEmojiClick} onStickerClick={handleStickerClick} onFileUpload={handleFileUpload} onImageUpload={handleImageUpload} onContactShare={handleContactShare} onVoiceRecord={handleVoiceRecord} onQuickReact={handleQuickReact} placeholder="Nhập tin nhắn"/>
52
- </div>);
53
- }
@@ -1,16 +0,0 @@
1
- interface ChatInputProps {
2
- onSendMessage?: (message: string) => void;
3
- onEmojiClick?: (emoji: string) => void;
4
- onFileUpload?: (file: File) => void;
5
- onImageUpload?: (file: File) => void;
6
- onContactShare?: () => void;
7
- onVoiceRecord?: () => void;
8
- onVoiceMessage?: () => void;
9
- onQuickReact?: () => void;
10
- placeholder?: string;
11
- disabled?: boolean;
12
- className?: string;
13
- }
14
- export declare function ChatInputWithCustomIcon({ onSendMessage, onEmojiClick, onFileUpload, onImageUpload, onContactShare, onVoiceRecord, onVoiceMessage, onQuickReact, placeholder, disabled, className, }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
15
- export {};
16
- //# sourceMappingURL=ChatInputWithCustomIcon.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ChatInputWithCustomIcon.d.ts","sourceRoot":"","sources":["../../src/components/ChatInputWithCustomIcon.tsx"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACnC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,uBAAuB,CAAC,EACtC,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAA6B,EAC7B,QAAgB,EAChB,SAAc,GACf,EAAE,cAAc,2CAgRhB"}
@@ -1,85 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useRef, useCallback } from "react";
4
- import { Smile, ImageIcon, Paperclip, Mic, ThumbsUp, Send, Type, Sticker, User } from "lucide-react";
5
- import { VoiceWaveIcon } from "./VoiceWaveIcon";
6
- export function ChatInputWithCustomIcon({ onSendMessage, onEmojiClick, onFileUpload, onImageUpload, onContactShare, onVoiceRecord, onVoiceMessage, onQuickReact, placeholder = "Nhập tin nhắn", disabled = false, className = "", }) {
7
- const [message, setMessage] = useState("");
8
- const [isRecording, setIsRecording] = useState(false);
9
- const [showVoiceWave, setShowVoiceWave] = useState(false);
10
- const textareaRef = useRef(null);
11
- const fileInputRef = useRef(null);
12
- const imageInputRef = useRef(null);
13
- const handleSubmit = useCallback((e) => {
14
- e.preventDefault();
15
- if (!message.trim() || disabled)
16
- return;
17
- onSendMessage === null || onSendMessage === void 0 ? void 0 : onSendMessage(message.trim());
18
- setMessage("");
19
- // Reset textarea height
20
- if (textareaRef.current) {
21
- textareaRef.current.style.height = "auto";
22
- }
23
- }, [message, disabled, onSendMessage]);
24
- const handleInputChange = useCallback((e) => {
25
- const value = e.target.value;
26
- setMessage(value);
27
- // Auto-resize textarea
28
- const textarea = e.target;
29
- textarea.style.height = "auto";
30
- textarea.style.height = Math.min(textarea.scrollHeight, 120) + "px";
31
- }, []);
32
- const handleKeyPress = useCallback((e) => {
33
- if (e.key === "Enter" && !e.shiftKey) {
34
- e.preventDefault();
35
- handleSubmit(e);
36
- }
37
- }, [handleSubmit]);
38
- const handleFileClick = useCallback(() => {
39
- var _a;
40
- (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
41
- }, []);
42
- const handleImageClick = useCallback(() => {
43
- var _a;
44
- (_a = imageInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
45
- }, []);
46
- const handleFileChange = useCallback((e) => {
47
- var _a;
48
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
49
- if (file) {
50
- onFileUpload === null || onFileUpload === void 0 ? void 0 : onFileUpload(file);
51
- e.target.value = ""; // Reset input
52
- }
53
- }, [onFileUpload]);
54
- const handleImageChange = useCallback((e) => {
55
- var _a;
56
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
57
- if (file) {
58
- onImageUpload === null || onImageUpload === void 0 ? void 0 : onImageUpload(file);
59
- e.target.value = ""; // Reset input
60
- }
61
- }, [onImageUpload]);
62
- const handleVoiceRecord = useCallback(() => {
63
- setIsRecording(!isRecording);
64
- onVoiceRecord === null || onVoiceRecord === void 0 ? void 0 : onVoiceRecord();
65
- }, [isRecording, onVoiceRecord]);
66
- const handleVoiceMessage = useCallback(() => {
67
- setShowVoiceWave(!showVoiceWave);
68
- onVoiceMessage === null || onVoiceMessage === void 0 ? void 0 : onVoiceMessage();
69
- }, [showVoiceWave, onVoiceMessage]);
70
- return (_jsxs("div", { className: `bg-white border-t border-gray-200 p-3 sm:p-4 ${className}`, children: [_jsxs("div", { className: "bg-gray-50 rounded-2xl border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200", children: [_jsx("div", { className: "px-4 py-3", children: _jsxs("form", { onSubmit: handleSubmit, className: "flex items-end space-x-3", children: [_jsx("div", { className: "flex-1 relative", children: _jsx("textarea", { ref: textareaRef, value: message, onChange: handleInputChange, onKeyPress: handleKeyPress, placeholder: placeholder, disabled: disabled, className: "w-full bg-transparent border-none outline-none resize-none text-sm sm:text-base text-gray-900 placeholder-gray-500 leading-5", rows: 1, style: {
71
- minHeight: "20px",
72
- maxHeight: "120px",
73
- } }) }), message.trim() ? (_jsx("button", { type: "submit", disabled: disabled, className: "flex-shrink-0 p-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200", "aria-label": "Send message", children: _jsx(Send, { className: "w-4 h-4" }) })) : (_jsx("button", { type: "button", onClick: onQuickReact, className: "flex-shrink-0 p-2 text-blue-500 hover:bg-blue-50 rounded-full transition-colors duration-200", "aria-label": "Quick react", children: _jsx(ThumbsUp, { className: "w-4 h-4" }) }))] }) }), _jsx("div", { className: "px-4 pb-3 border-t border-gray-100", children: _jsxs("div", { className: "flex items-center justify-between space-x-1", children: [_jsx("button", { onClick: () => console.log("Text style clicked"), disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-blue-500", "aria-label": "Text style", title: "Text style", children: _jsx(Type, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: () => onEmojiClick === null || onEmojiClick === void 0 ? void 0 : onEmojiClick("😊"), disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-yellow-500", "aria-label": "Emoji", title: "Emoji", children: _jsx(Smile, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: () => console.log("Sticker clicked"), disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-purple-500", "aria-label": "Sticker", title: "Sticker", children: _jsx(Sticker, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: handleImageClick, disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-green-500", "aria-label": "Image upload", title: "Image upload", children: _jsx(ImageIcon, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: handleFileClick, disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-blue-500", "aria-label": "File attachment", title: "File attachment", children: _jsx(Paperclip, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: onContactShare, disabled: disabled, className: "p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 active:scale-95 text-gray-500 hover:text-indigo-500", "aria-label": "Contact sharing", title: "Contact sharing", children: _jsx(User, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: handleVoiceRecord, disabled: disabled, className: `
74
- p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed
75
- hover:bg-gray-100 active:scale-95
76
- ${isRecording ? "text-red-500 bg-red-50" : "text-gray-500 hover:text-red-500"}
77
- `, "aria-label": "Record audio", title: "Record audio", children: _jsx(Mic, { className: "w-4 h-4 sm:w-5 sm:h-5" }) }), _jsx("button", { onClick: handleVoiceMessage, disabled: disabled, className: `
78
- p-2 rounded-full transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed
79
- hover:bg-gray-100 active:scale-95
80
- ${showVoiceWave ? "text-blue-500 bg-blue-50" : "text-gray-500 hover:text-blue-500"}
81
- `, "aria-label": "Voice message", title: "Voice message", children: _jsx(VoiceWaveIcon, { className: "w-4 h-4 sm:w-5 sm:h-5", animated: showVoiceWave }) })] }) }), isRecording && (_jsx("div", { className: "px-4 pb-2", children: _jsxs("div", { className: "flex items-center space-x-2 text-red-500 text-sm", children: [_jsx("div", { className: "w-2 h-2 bg-red-500 rounded-full animate-pulse" }), _jsx("span", { children: "Recording..." })] }) })), showVoiceWave && (_jsx("div", { className: "px-4 pb-2", children: _jsx("div", { className: "flex items-center space-x-1", children: [...Array(8)].map((_, i) => (_jsx("div", { className: "w-1 bg-blue-500 rounded-full animate-pulse", style: {
82
- height: `${Math.random() * 20 + 10}px`,
83
- animationDelay: `${i * 0.1}s`,
84
- } }, i))) }) }))] }), _jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileChange, className: "hidden", accept: ".pdf,.doc,.docx,.txt,.zip,.rar" }), _jsx("input", { ref: imageInputRef, type: "file", onChange: handleImageChange, className: "hidden", accept: "image/*" })] }));
85
- }