@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.
- package/dist/assets/droppiiFontSelection.json +14521 -0
- package/dist/components/ChatBubble.d.ts +9 -1
- package/dist/components/ChatBubble.d.ts.map +1 -1
- package/dist/components/ChatBubble.js +23 -15
- package/dist/components/chat-bubble/ChatBubble.d.ts +9 -0
- package/dist/components/chat-bubble/ChatBubble.d.ts.map +1 -0
- package/dist/components/chat-bubble/ChatBubble.js +27 -0
- package/dist/components/conversation/DeskConversationList.d.ts +8 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -0
- package/dist/components/conversation/DeskConversationList.js +168 -0
- package/dist/components/icon/index.d.ts +11 -0
- package/dist/components/icon/index.d.ts.map +1 -0
- package/dist/components/icon/index.js +18 -0
- package/dist/components/message/MessageList.d.ts +10 -0
- package/dist/components/message/MessageList.d.ts.map +1 -0
- package/dist/components/message/MessageList.js +91 -0
- package/dist/components/session/AssignedSessionFilter.d.ts +7 -0
- package/dist/components/session/AssignedSessionFilter.d.ts.map +1 -0
- package/dist/components/session/AssignedSessionFilter.js +90 -0
- package/dist/context/ChatContext.d.ts +4 -71
- package/dist/context/ChatContext.d.ts.map +1 -1
- package/dist/context/ChatContext.js +33 -344
- package/dist/hooks/conversation/useConversation.d.ts +11 -0
- package/dist/hooks/conversation/useConversation.d.ts.map +1 -0
- package/dist/hooks/conversation/useConversation.js +51 -0
- package/dist/hooks/message/useMessage.d.ts +9 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -0
- package/dist/hooks/message/useMessage.js +46 -0
- package/dist/hooks/message/useSendMessage.d.ts +10 -0
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -0
- package/dist/hooks/message/useSendMessage.js +42 -0
- package/dist/index.d.ts +9 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -27
- package/dist/screens/desk-message/index.d.ts +3 -0
- package/dist/screens/desk-message/index.d.ts.map +1 -0
- package/dist/screens/desk-message/index.js +14 -0
- package/dist/types/chat.d.ts +6 -36
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -85
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/sdk.d.ts +1 -0
- package/dist/types/sdk.d.ts.map +1 -0
- package/dist/types/sdk.js +1 -0
- package/package.json +19 -3
- package/dist/components/AutoScrollAnchor.d.ts +0 -2
- package/dist/components/AutoScrollAnchor.d.ts.map +0 -1
- package/dist/components/AutoScrollAnchor.js +0 -12
- package/dist/components/AutoScrollAnchor.jsx +0 -11
- package/dist/components/ChatBubble.jsx +0 -80
- package/dist/components/ChatHeader.d.ts +0 -8
- package/dist/components/ChatHeader.d.ts.map +0 -1
- package/dist/components/ChatHeader.js +0 -32
- package/dist/components/ChatHeader.jsx +0 -72
- package/dist/components/ChatInput.d.ts +0 -3
- package/dist/components/ChatInput.d.ts.map +0 -1
- package/dist/components/ChatInput.js +0 -379
- package/dist/components/ChatInput.jsx +0 -444
- package/dist/components/ChatInputDemo.d.ts +0 -2
- package/dist/components/ChatInputDemo.d.ts.map +0 -1
- package/dist/components/ChatInputDemo.js +0 -38
- package/dist/components/ChatInputDemo.jsx +0 -53
- package/dist/components/ChatInputWithCustomIcon.d.ts +0 -16
- package/dist/components/ChatInputWithCustomIcon.d.ts.map +0 -1
- package/dist/components/ChatInputWithCustomIcon.js +0 -85
- package/dist/components/ChatInputWithCustomIcon.jsx +0 -167
- package/dist/components/ChatLayout.d.ts +0 -6
- package/dist/components/ChatLayout.d.ts.map +0 -1
- package/dist/components/ChatLayout.js +0 -48
- package/dist/components/ChatLayout.jsx +0 -122
- package/dist/components/ConversationItem.d.ts +0 -9
- package/dist/components/ConversationItem.d.ts.map +0 -1
- package/dist/components/ConversationItem.js +0 -27
- package/dist/components/ConversationItem.jsx +0 -51
- package/dist/components/ConversationList.d.ts +0 -8
- package/dist/components/ConversationList.d.ts.map +0 -1
- package/dist/components/ConversationList.js +0 -11
- package/dist/components/ConversationList.jsx +0 -22
- package/dist/components/DateDivider.d.ts +0 -7
- package/dist/components/DateDivider.d.ts.map +0 -1
- package/dist/components/DateDivider.js +0 -27
- package/dist/components/DateDivider.jsx +0 -28
- package/dist/components/EmojiPicker.d.ts +0 -4
- package/dist/components/EmojiPicker.d.ts.map +0 -1
- package/dist/components/EmojiPicker.js +0 -191
- package/dist/components/EmojiPicker.jsx +0 -229
- package/dist/components/ImageLightbox.d.ts +0 -8
- package/dist/components/ImageLightbox.d.ts.map +0 -1
- package/dist/components/ImageLightbox.js +0 -8
- package/dist/components/ImageLightbox.jsx +0 -16
- package/dist/components/ImagePreviewModal.d.ts +0 -12
- package/dist/components/ImagePreviewModal.d.ts.map +0 -1
- package/dist/components/ImagePreviewModal.js +0 -55
- package/dist/components/ImagePreviewModal.jsx +0 -84
- package/dist/components/MessageItem.d.ts +0 -3
- package/dist/components/MessageItem.d.ts.map +0 -1
- package/dist/components/MessageItem.js +0 -38
- package/dist/components/MessageItem.jsx +0 -99
- package/dist/components/MessageItemDemo.d.ts +0 -2
- package/dist/components/MessageItemDemo.d.ts.map +0 -1
- package/dist/components/MessageItemDemo.js +0 -166
- package/dist/components/MessageItemDemo.jsx +0 -179
- package/dist/components/MessageList.d.ts +0 -15
- package/dist/components/MessageList.d.ts.map +0 -1
- package/dist/components/MessageList.js +0 -243
- package/dist/components/MessageList.jsx +0 -306
- package/dist/components/MessageListDemo.d.ts +0 -2
- package/dist/components/MessageListDemo.d.ts.map +0 -1
- package/dist/components/MessageListDemo.js +0 -165
- package/dist/components/MessageListDemo.jsx +0 -183
- package/dist/components/StickerPicker.d.ts +0 -4
- package/dist/components/StickerPicker.d.ts.map +0 -1
- package/dist/components/StickerPicker.js +0 -68
- package/dist/components/StickerPicker.jsx +0 -106
- package/dist/components/SwipeIndicator.d.ts +0 -9
- package/dist/components/SwipeIndicator.d.ts.map +0 -1
- package/dist/components/SwipeIndicator.js +0 -24
- package/dist/components/SwipeIndicator.jsx +0 -28
- package/dist/components/TextFormattingToolbar.d.ts +0 -4
- package/dist/components/TextFormattingToolbar.d.ts.map +0 -1
- package/dist/components/TextFormattingToolbar.js +0 -29
- package/dist/components/TextFormattingToolbar.jsx +0 -52
- package/dist/components/TypingIndicator.d.ts +0 -6
- package/dist/components/TypingIndicator.d.ts.map +0 -1
- package/dist/components/TypingIndicator.js +0 -21
- package/dist/components/TypingIndicator.jsx +0 -27
- package/dist/components/VoiceWaveIcon.d.ts +0 -7
- package/dist/components/VoiceWaveIcon.d.ts.map +0 -1
- package/dist/components/VoiceWaveIcon.js +0 -5
- package/dist/components/VoiceWaveIcon.jsx +0 -11
- package/dist/context/ChatContext.jsx +0 -346
- package/dist/hooks/useChat.d.ts +0 -5
- package/dist/hooks/useChat.d.ts.map +0 -1
- package/dist/hooks/useChat.js +0 -73
- package/dist/hooks/useConversationList.d.ts +0 -5
- package/dist/hooks/useConversationList.d.ts.map +0 -1
- package/dist/hooks/useConversationList.js +0 -9
- package/dist/hooks/useMessages.d.ts +0 -5
- package/dist/hooks/useMessages.d.ts.map +0 -1
- package/dist/hooks/useMessages.js +0 -192
- package/dist/hooks/useSocket.d.ts +0 -7
- package/dist/hooks/useSocket.d.ts.map +0 -1
- package/dist/hooks/useSocket.js +0 -120
- package/dist/hooks/useSwipeGesture.d.ts +0 -11
- package/dist/hooks/useSwipeGesture.d.ts.map +0 -1
- package/dist/hooks/useSwipeGesture.js +0 -54
- package/dist/hooks/useTextSelection.d.ts +0 -13
- package/dist/hooks/useTextSelection.d.ts.map +0 -1
- package/dist/hooks/useTextSelection.js +0 -132
- package/dist/hooks/useTyping.d.ts +0 -7
- package/dist/hooks/useTyping.d.ts.map +0 -1
- package/dist/hooks/useTyping.js +0 -64
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { MessageList } from "./MessageList";
|
|
5
|
-
export function MessageListDemo() {
|
|
6
|
-
const [messages, setMessages] = useState([
|
|
7
|
-
// Yesterday's messages
|
|
8
|
-
{
|
|
9
|
-
id: "demo-old-1",
|
|
10
|
-
conversationId: "demo-conv",
|
|
11
|
-
senderId: "user-2",
|
|
12
|
-
content: "Hey! How was your weekend?",
|
|
13
|
-
type: "text",
|
|
14
|
-
timestamp: new Date(Date.now() - 86400000 - 3600000), // Yesterday
|
|
15
|
-
status: "read",
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
id: "demo-old-2",
|
|
19
|
-
conversationId: "demo-conv",
|
|
20
|
-
senderId: "current-user",
|
|
21
|
-
content: "It was great! Went hiking 🏔️",
|
|
22
|
-
type: "text",
|
|
23
|
-
timestamp: new Date(Date.now() - 86400000 - 3000000), // Yesterday
|
|
24
|
-
status: "read",
|
|
25
|
-
},
|
|
26
|
-
// Today's messages
|
|
27
|
-
{
|
|
28
|
-
id: "demo-1",
|
|
29
|
-
conversationId: "demo-conv",
|
|
30
|
-
senderId: "user-2",
|
|
31
|
-
content: "Good morning! How are you today? 😊",
|
|
32
|
-
type: "text",
|
|
33
|
-
timestamp: new Date(Date.now() - 3600000), // 1 hour ago
|
|
34
|
-
status: "read",
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: "demo-2",
|
|
38
|
-
conversationId: "demo-conv",
|
|
39
|
-
senderId: "current-user",
|
|
40
|
-
content: "Morning! I'm doing great, thanks for asking!",
|
|
41
|
-
type: "text",
|
|
42
|
-
timestamp: new Date(Date.now() - 3000000), // 50 minutes ago
|
|
43
|
-
status: "read",
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: "demo-3",
|
|
47
|
-
conversationId: "demo-conv",
|
|
48
|
-
senderId: "user-2",
|
|
49
|
-
content: "Check out these photos from my trip!",
|
|
50
|
-
type: "image",
|
|
51
|
-
timestamp: new Date(Date.now() - 2400000), // 40 minutes ago
|
|
52
|
-
status: "read",
|
|
53
|
-
attachments: [
|
|
54
|
-
{
|
|
55
|
-
id: "att-1",
|
|
56
|
-
name: "trip1.jpg",
|
|
57
|
-
url: "/placeholder.svg?height=300&width=400",
|
|
58
|
-
type: "image/jpeg",
|
|
59
|
-
size: 245760,
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: "att-2",
|
|
63
|
-
name: "trip2.jpg",
|
|
64
|
-
url: "/placeholder.svg?height=300&width=400",
|
|
65
|
-
type: "image/jpeg",
|
|
66
|
-
size: 198432,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: "demo-4",
|
|
72
|
-
conversationId: "demo-conv",
|
|
73
|
-
senderId: "current-user",
|
|
74
|
-
content: "Wow, those are amazing! 😍",
|
|
75
|
-
type: "text",
|
|
76
|
-
timestamp: new Date(Date.now() - 1800000), // 30 minutes ago
|
|
77
|
-
status: "read",
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: "demo-5",
|
|
81
|
-
conversationId: "demo-conv",
|
|
82
|
-
senderId: "user-2",
|
|
83
|
-
content: "Here's the document you requested",
|
|
84
|
-
type: "file",
|
|
85
|
-
timestamp: new Date(Date.now() - 1200000), // 20 minutes ago
|
|
86
|
-
status: "read",
|
|
87
|
-
attachments: [
|
|
88
|
-
{
|
|
89
|
-
id: "att-3",
|
|
90
|
-
name: "report.pdf",
|
|
91
|
-
url: "/placeholder.svg?height=200&width=200",
|
|
92
|
-
type: "application/pdf",
|
|
93
|
-
size: 1024000,
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: "demo-6",
|
|
99
|
-
conversationId: "demo-conv",
|
|
100
|
-
senderId: "current-user",
|
|
101
|
-
content: "Perfect, thanks! I'll review it this afternoon.",
|
|
102
|
-
type: "text",
|
|
103
|
-
timestamp: new Date(Date.now() - 600000), // 10 minutes ago
|
|
104
|
-
status: "read",
|
|
105
|
-
},
|
|
106
|
-
]);
|
|
107
|
-
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
108
|
-
const [hasMore, setHasMore] = useState(true);
|
|
109
|
-
const handleLoadMore = () => {
|
|
110
|
-
setIsLoadingMore(true);
|
|
111
|
-
// Simulate loading more messages
|
|
112
|
-
setTimeout(() => {
|
|
113
|
-
const olderMessages = [
|
|
114
|
-
{
|
|
115
|
-
id: `older-${Date.now()}-1`,
|
|
116
|
-
conversationId: "demo-conv",
|
|
117
|
-
senderId: "user-2",
|
|
118
|
-
content: "Hey, did you see the news today?",
|
|
119
|
-
type: "text",
|
|
120
|
-
timestamp: new Date(Date.now() - 172800000), // 2 days ago
|
|
121
|
-
status: "read",
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
id: `older-${Date.now()}-2`,
|
|
125
|
-
conversationId: "demo-conv",
|
|
126
|
-
senderId: "current-user",
|
|
127
|
-
content: "Yes! It's quite interesting.",
|
|
128
|
-
type: "text",
|
|
129
|
-
timestamp: new Date(Date.now() - 172800000 + 300000), // 2 days ago + 5 min
|
|
130
|
-
status: "read",
|
|
131
|
-
},
|
|
132
|
-
];
|
|
133
|
-
setMessages((prev) => [...olderMessages, ...prev]);
|
|
134
|
-
setIsLoadingMore(false);
|
|
135
|
-
// Simulate no more messages after a few loads
|
|
136
|
-
if (messages.length > 15) {
|
|
137
|
-
setHasMore(false);
|
|
138
|
-
}
|
|
139
|
-
}, 1500);
|
|
140
|
-
};
|
|
141
|
-
const handleSendMessage = (content) => {
|
|
142
|
-
const newMessage = {
|
|
143
|
-
id: `new-${Date.now()}`,
|
|
144
|
-
conversationId: "demo-conv",
|
|
145
|
-
senderId: "current-user",
|
|
146
|
-
content,
|
|
147
|
-
type: "text",
|
|
148
|
-
timestamp: new Date(),
|
|
149
|
-
status: "sending",
|
|
150
|
-
};
|
|
151
|
-
setMessages((prev) => [...prev, newMessage]);
|
|
152
|
-
// Simulate message status update
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
setMessages((prev) => prev.map((msg) => (msg.id === newMessage.id ? Object.assign(Object.assign({}, msg), { status: "delivered" }) : msg)));
|
|
155
|
-
}, 1000);
|
|
156
|
-
};
|
|
157
|
-
return (_jsxs("div", { className: "max-w-4xl mx-auto bg-white rounded-lg shadow-lg overflow-hidden h-[600px] flex flex-col", children: [_jsxs("div", { className: "bg-gray-50 p-4 border-b flex-shrink-0", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "MessageList Demo" }), _jsx("p", { className: "text-sm text-gray-600", children: "Features: Auto-scroll, lazy loading, date grouping, sender grouping" })] }), _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(MessageList, { messages: messages, isLoadingMore: isLoadingMore, onLoadMore: handleLoadMore, hasMore: hasMore, currentUserId: "current-user", conversationId: "demo-conv" }) }), _jsx("div", { className: "border-t p-4 flex-shrink-0", children: _jsxs("form", { onSubmit: (e) => {
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
const input = e.currentTarget.elements.namedItem("message");
|
|
160
|
-
if (input.value.trim()) {
|
|
161
|
-
handleSendMessage(input.value.trim());
|
|
162
|
-
input.value = "";
|
|
163
|
-
}
|
|
164
|
-
}, className: "flex space-x-2", children: [_jsx("input", { name: "message", type: "text", placeholder: "Nh\u1EADp tin nh\u1EAFn \u0111\u1EC3 test auto-scroll...", className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsx("button", { type: "submit", className: "px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors", children: "G\u1EEDi" })] }) })] }));
|
|
165
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { MessageList } from "./MessageList";
|
|
4
|
-
export function MessageListDemo() {
|
|
5
|
-
const [messages, setMessages] = useState([
|
|
6
|
-
// Yesterday's messages
|
|
7
|
-
{
|
|
8
|
-
id: "demo-old-1",
|
|
9
|
-
conversationId: "demo-conv",
|
|
10
|
-
senderId: "user-2",
|
|
11
|
-
content: "Hey! How was your weekend?",
|
|
12
|
-
type: "text",
|
|
13
|
-
timestamp: new Date(Date.now() - 86400000 - 3600000), // Yesterday
|
|
14
|
-
status: "read",
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
id: "demo-old-2",
|
|
18
|
-
conversationId: "demo-conv",
|
|
19
|
-
senderId: "current-user",
|
|
20
|
-
content: "It was great! Went hiking 🏔️",
|
|
21
|
-
type: "text",
|
|
22
|
-
timestamp: new Date(Date.now() - 86400000 - 3000000), // Yesterday
|
|
23
|
-
status: "read",
|
|
24
|
-
},
|
|
25
|
-
// Today's messages
|
|
26
|
-
{
|
|
27
|
-
id: "demo-1",
|
|
28
|
-
conversationId: "demo-conv",
|
|
29
|
-
senderId: "user-2",
|
|
30
|
-
content: "Good morning! How are you today? 😊",
|
|
31
|
-
type: "text",
|
|
32
|
-
timestamp: new Date(Date.now() - 3600000), // 1 hour ago
|
|
33
|
-
status: "read",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "demo-2",
|
|
37
|
-
conversationId: "demo-conv",
|
|
38
|
-
senderId: "current-user",
|
|
39
|
-
content: "Morning! I'm doing great, thanks for asking!",
|
|
40
|
-
type: "text",
|
|
41
|
-
timestamp: new Date(Date.now() - 3000000), // 50 minutes ago
|
|
42
|
-
status: "read",
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: "demo-3",
|
|
46
|
-
conversationId: "demo-conv",
|
|
47
|
-
senderId: "user-2",
|
|
48
|
-
content: "Check out these photos from my trip!",
|
|
49
|
-
type: "image",
|
|
50
|
-
timestamp: new Date(Date.now() - 2400000), // 40 minutes ago
|
|
51
|
-
status: "read",
|
|
52
|
-
attachments: [
|
|
53
|
-
{
|
|
54
|
-
id: "att-1",
|
|
55
|
-
name: "trip1.jpg",
|
|
56
|
-
url: "/placeholder.svg?height=300&width=400",
|
|
57
|
-
type: "image/jpeg",
|
|
58
|
-
size: 245760,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
id: "att-2",
|
|
62
|
-
name: "trip2.jpg",
|
|
63
|
-
url: "/placeholder.svg?height=300&width=400",
|
|
64
|
-
type: "image/jpeg",
|
|
65
|
-
size: 198432,
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: "demo-4",
|
|
71
|
-
conversationId: "demo-conv",
|
|
72
|
-
senderId: "current-user",
|
|
73
|
-
content: "Wow, those are amazing! 😍",
|
|
74
|
-
type: "text",
|
|
75
|
-
timestamp: new Date(Date.now() - 1800000), // 30 minutes ago
|
|
76
|
-
status: "read",
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "demo-5",
|
|
80
|
-
conversationId: "demo-conv",
|
|
81
|
-
senderId: "user-2",
|
|
82
|
-
content: "Here's the document you requested",
|
|
83
|
-
type: "file",
|
|
84
|
-
timestamp: new Date(Date.now() - 1200000), // 20 minutes ago
|
|
85
|
-
status: "read",
|
|
86
|
-
attachments: [
|
|
87
|
-
{
|
|
88
|
-
id: "att-3",
|
|
89
|
-
name: "report.pdf",
|
|
90
|
-
url: "/placeholder.svg?height=200&width=200",
|
|
91
|
-
type: "application/pdf",
|
|
92
|
-
size: 1024000,
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: "demo-6",
|
|
98
|
-
conversationId: "demo-conv",
|
|
99
|
-
senderId: "current-user",
|
|
100
|
-
content: "Perfect, thanks! I'll review it this afternoon.",
|
|
101
|
-
type: "text",
|
|
102
|
-
timestamp: new Date(Date.now() - 600000), // 10 minutes ago
|
|
103
|
-
status: "read",
|
|
104
|
-
},
|
|
105
|
-
]);
|
|
106
|
-
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
107
|
-
const [hasMore, setHasMore] = useState(true);
|
|
108
|
-
const handleLoadMore = () => {
|
|
109
|
-
setIsLoadingMore(true);
|
|
110
|
-
// Simulate loading more messages
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
const olderMessages = [
|
|
113
|
-
{
|
|
114
|
-
id: `older-${Date.now()}-1`,
|
|
115
|
-
conversationId: "demo-conv",
|
|
116
|
-
senderId: "user-2",
|
|
117
|
-
content: "Hey, did you see the news today?",
|
|
118
|
-
type: "text",
|
|
119
|
-
timestamp: new Date(Date.now() - 172800000), // 2 days ago
|
|
120
|
-
status: "read",
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
id: `older-${Date.now()}-2`,
|
|
124
|
-
conversationId: "demo-conv",
|
|
125
|
-
senderId: "current-user",
|
|
126
|
-
content: "Yes! It's quite interesting.",
|
|
127
|
-
type: "text",
|
|
128
|
-
timestamp: new Date(Date.now() - 172800000 + 300000), // 2 days ago + 5 min
|
|
129
|
-
status: "read",
|
|
130
|
-
},
|
|
131
|
-
];
|
|
132
|
-
setMessages((prev) => [...olderMessages, ...prev]);
|
|
133
|
-
setIsLoadingMore(false);
|
|
134
|
-
// Simulate no more messages after a few loads
|
|
135
|
-
if (messages.length > 15) {
|
|
136
|
-
setHasMore(false);
|
|
137
|
-
}
|
|
138
|
-
}, 1500);
|
|
139
|
-
};
|
|
140
|
-
const handleSendMessage = (content) => {
|
|
141
|
-
const newMessage = {
|
|
142
|
-
id: `new-${Date.now()}`,
|
|
143
|
-
conversationId: "demo-conv",
|
|
144
|
-
senderId: "current-user",
|
|
145
|
-
content,
|
|
146
|
-
type: "text",
|
|
147
|
-
timestamp: new Date(),
|
|
148
|
-
status: "sending",
|
|
149
|
-
};
|
|
150
|
-
setMessages((prev) => [...prev, newMessage]);
|
|
151
|
-
// Simulate message status update
|
|
152
|
-
setTimeout(() => {
|
|
153
|
-
setMessages((prev) => prev.map((msg) => (msg.id === newMessage.id ? Object.assign(Object.assign({}, msg), { status: "delivered" }) : msg)));
|
|
154
|
-
}, 1000);
|
|
155
|
-
};
|
|
156
|
-
return (<div className="max-w-4xl mx-auto bg-white rounded-lg shadow-lg overflow-hidden h-[600px] flex flex-col">
|
|
157
|
-
<div className="bg-gray-50 p-4 border-b flex-shrink-0">
|
|
158
|
-
<h2 className="text-lg font-semibold text-gray-900">MessageList Demo</h2>
|
|
159
|
-
<p className="text-sm text-gray-600">Features: Auto-scroll, lazy loading, date grouping, sender grouping</p>
|
|
160
|
-
</div>
|
|
161
|
-
|
|
162
|
-
<div className="flex-1 overflow-hidden">
|
|
163
|
-
<MessageList messages={messages} isLoadingMore={isLoadingMore} onLoadMore={handleLoadMore} hasMore={hasMore} currentUserId="current-user" conversationId="demo-conv"/>
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
{/* Simple input for testing */}
|
|
167
|
-
<div className="border-t p-4 flex-shrink-0">
|
|
168
|
-
<form onSubmit={(e) => {
|
|
169
|
-
e.preventDefault();
|
|
170
|
-
const input = e.currentTarget.elements.namedItem("message");
|
|
171
|
-
if (input.value.trim()) {
|
|
172
|
-
handleSendMessage(input.value.trim());
|
|
173
|
-
input.value = "";
|
|
174
|
-
}
|
|
175
|
-
}} className="flex space-x-2">
|
|
176
|
-
<input name="message" type="text" placeholder="Nhập tin nhắn để test auto-scroll..." className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"/>
|
|
177
|
-
<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
|
178
|
-
Gửi
|
|
179
|
-
</button>
|
|
180
|
-
</form>
|
|
181
|
-
</div>
|
|
182
|
-
</div>);
|
|
183
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StickerPicker.d.ts","sourceRoot":"","sources":["../../src/components/StickerPicker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAezB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AA6CvD,eAAO,MAAM,aAAa,wGA8FzB,CAAA"}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { useState } from "react";
|
|
5
|
-
import { X, Search, Heart, Smile, Star, Zap, Coffee, Music, Gamepad2Icon as GameController2, Sparkles, } from "lucide-react";
|
|
6
|
-
const stickerCategories = {
|
|
7
|
-
popular: {
|
|
8
|
-
label: "Phổ biến",
|
|
9
|
-
icon: Star,
|
|
10
|
-
stickers: ["🔥", "💯", "✨", "⭐", "🎉", "🎊", "💫", "🌟", "⚡"],
|
|
11
|
-
},
|
|
12
|
-
emotions: {
|
|
13
|
-
label: "Cảm xúc",
|
|
14
|
-
icon: Heart,
|
|
15
|
-
stickers: ["❤️", "💕", "💖", "💗", "💓", "💘", "💝", "💟", "♥️", "💔", "❣️", "💋", "👑", "💎", "🌹"],
|
|
16
|
-
},
|
|
17
|
-
reactions: {
|
|
18
|
-
label: "Phản ứng",
|
|
19
|
-
icon: Smile,
|
|
20
|
-
stickers: ["👍", "👎", "👏", "🙌", "👌", "✌️", "🤞", "🤟", "🤘", "👊", "✊", "🤛", "🤜", "👋", "🤚"],
|
|
21
|
-
},
|
|
22
|
-
fun: {
|
|
23
|
-
label: "Vui nhộn",
|
|
24
|
-
icon: Zap,
|
|
25
|
-
stickers: ["🎯", "🎪", "🎨", "🎭", "🎪", "🎡", "🎢", "🎠", "🎳", "🎮", "🕹️", "🎲", "🃏", "🎴", "🀄"],
|
|
26
|
-
},
|
|
27
|
-
food: {
|
|
28
|
-
label: "Đồ ăn",
|
|
29
|
-
icon: Coffee,
|
|
30
|
-
stickers: ["🍕", "🍔", "🍟", "🌭", "🥪", "🌮", "🌯", "🥙", "🧆", "🥚", "🍳", "🥘", "🍲", "🥗", "🍿"],
|
|
31
|
-
},
|
|
32
|
-
activities: {
|
|
33
|
-
label: "Hoạt động",
|
|
34
|
-
icon: GameController2,
|
|
35
|
-
stickers: ["⚽", "🏀", "🏈", "⚾", "🥎", "🎾", "🏐", "🏉", "🥏", "🎱", "🪀", "🏓", "🏸", "🏒", "🏑"],
|
|
36
|
-
},
|
|
37
|
-
nature: {
|
|
38
|
-
label: "Thiên nhiên",
|
|
39
|
-
icon: Sparkles,
|
|
40
|
-
stickers: ["🌸", "🌺", "🌻", "🌷", "🌹", "🥀", "🌾", "🌿", "☘️", "🍀", "🍃", "🌱", "🌲", "🌳", "🌴"],
|
|
41
|
-
},
|
|
42
|
-
music: {
|
|
43
|
-
label: "Âm nhạc",
|
|
44
|
-
icon: Music,
|
|
45
|
-
stickers: ["🎵", "🎶", "🎼", "🎹", "🥁", "🎷", "🎺", "🎸", "🪕", "🎻", "🎤", "🎧", "📻", "🎚️", "🎛️"],
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
export const StickerPicker = React.forwardRef(({ onStickerSelect, onClose, isOpen = false, style }, ref) => {
|
|
49
|
-
const [activeCategory, setActiveCategory] = useState("popular");
|
|
50
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
51
|
-
if (!isOpen)
|
|
52
|
-
return null;
|
|
53
|
-
const handleStickerClick = (sticker) => {
|
|
54
|
-
onStickerSelect === null || onStickerSelect === void 0 ? void 0 : onStickerSelect(sticker);
|
|
55
|
-
};
|
|
56
|
-
const filteredStickers = searchTerm
|
|
57
|
-
? stickerCategories[activeCategory].stickers.filter((sticker) => sticker.includes(searchTerm))
|
|
58
|
-
: stickerCategories[activeCategory].stickers;
|
|
59
|
-
return (_jsx("div", { ref: ref, className: "absolute bottom-full mb-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 w-full sm:max-w-xs md:w-80", style: style, children: _jsxs("div", { className: "p-2", children: [_jsxs("div", { className: "flex items-center justify-between mb-2 pb-1 border-b border-gray-100", children: [Object.entries(stickerCategories).map(([key, category]) => {
|
|
60
|
-
const Icon = category.icon;
|
|
61
|
-
const isActive = activeCategory === key;
|
|
62
|
-
return (_jsx("button", { onClick: () => setActiveCategory(key), className: `
|
|
63
|
-
p-1.5 rounded-full transition-all duration-200 hover:bg-gray-100
|
|
64
|
-
${isActive ? "text-purple-500 bg-purple-50" : "text-gray-500"}
|
|
65
|
-
`, title: category.label, children: _jsx(Icon, { className: "w-4 h-4" }) }, key));
|
|
66
|
-
}), _jsx("button", { onClick: onClose, className: "p-1.5 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100", children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsxs("div", { className: "relative mb-2", children: [_jsx(Search, { className: "absolute left-2 top-1/2 transform -translate-y-1/2 w-3 h-3 text-gray-400" }), _jsx("input", { type: "text", placeholder: "T\u00ECm sticker", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), className: "w-full pl-7 pr-3 py-1.5 text-xs bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent" })] }), _jsx("div", { className: "mb-2", children: _jsx("h3", { className: "text-xs font-medium text-gray-900", children: stickerCategories[activeCategory].label }) }), _jsx("div", { className: "grid grid-cols-6 gap-1 max-h-48 overflow-y-auto", children: filteredStickers.map((sticker, index) => (_jsx("button", { onClick: () => handleStickerClick(sticker), className: "p-2 text-xl hover:bg-gray-100 rounded transition-all duration-200 hover:scale-110 active:scale-95 border border-gray-100 hover:border-gray-200", title: sticker, children: sticker }, index))) }), filteredStickers.length === 0 && (_jsxs("div", { className: "text-center py-4 text-gray-500", children: [_jsx("div", { className: "text-lg mb-1", children: "\uD83D\uDD0D" }), _jsx("p", { className: "text-xs", children: "Kh\u00F4ng t\u00ECm th\u1EA5y sticker n\u00E0o" })] }))] }) }));
|
|
67
|
-
});
|
|
68
|
-
StickerPicker.displayName = "StickerPicker";
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { X, Search, Heart, Smile, Star, Zap, Coffee, Music, Gamepad2Icon as GameController2, Sparkles, } from "lucide-react";
|
|
5
|
-
const stickerCategories = {
|
|
6
|
-
popular: {
|
|
7
|
-
label: "Phổ biến",
|
|
8
|
-
icon: Star,
|
|
9
|
-
stickers: ["🔥", "💯", "✨", "⭐", "🎉", "🎊", "💫", "🌟", "⚡"],
|
|
10
|
-
},
|
|
11
|
-
emotions: {
|
|
12
|
-
label: "Cảm xúc",
|
|
13
|
-
icon: Heart,
|
|
14
|
-
stickers: ["❤️", "💕", "💖", "💗", "💓", "💘", "💝", "💟", "♥️", "💔", "❣️", "💋", "👑", "💎", "🌹"],
|
|
15
|
-
},
|
|
16
|
-
reactions: {
|
|
17
|
-
label: "Phản ứng",
|
|
18
|
-
icon: Smile,
|
|
19
|
-
stickers: ["👍", "👎", "👏", "🙌", "👌", "✌️", "🤞", "🤟", "🤘", "👊", "✊", "🤛", "🤜", "👋", "🤚"],
|
|
20
|
-
},
|
|
21
|
-
fun: {
|
|
22
|
-
label: "Vui nhộn",
|
|
23
|
-
icon: Zap,
|
|
24
|
-
stickers: ["🎯", "🎪", "🎨", "🎭", "🎪", "🎡", "🎢", "🎠", "🎳", "🎮", "🕹️", "🎲", "🃏", "🎴", "🀄"],
|
|
25
|
-
},
|
|
26
|
-
food: {
|
|
27
|
-
label: "Đồ ăn",
|
|
28
|
-
icon: Coffee,
|
|
29
|
-
stickers: ["🍕", "🍔", "🍟", "🌭", "🥪", "🌮", "🌯", "🥙", "🧆", "🥚", "🍳", "🥘", "🍲", "🥗", "🍿"],
|
|
30
|
-
},
|
|
31
|
-
activities: {
|
|
32
|
-
label: "Hoạt động",
|
|
33
|
-
icon: GameController2,
|
|
34
|
-
stickers: ["⚽", "🏀", "🏈", "⚾", "🥎", "🎾", "🏐", "🏉", "🥏", "🎱", "🪀", "🏓", "🏸", "🏒", "🏑"],
|
|
35
|
-
},
|
|
36
|
-
nature: {
|
|
37
|
-
label: "Thiên nhiên",
|
|
38
|
-
icon: Sparkles,
|
|
39
|
-
stickers: ["🌸", "🌺", "🌻", "🌷", "🌹", "🥀", "🌾", "🌿", "☘️", "🍀", "🍃", "🌱", "🌲", "🌳", "🌴"],
|
|
40
|
-
},
|
|
41
|
-
music: {
|
|
42
|
-
label: "Âm nhạc",
|
|
43
|
-
icon: Music,
|
|
44
|
-
stickers: ["🎵", "🎶", "🎼", "🎹", "🥁", "🎷", "🎺", "🎸", "🪕", "🎻", "🎤", "🎧", "📻", "🎚️", "🎛️"],
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
export const StickerPicker = React.forwardRef(({ onStickerSelect, onClose, isOpen = false, style }, ref) => {
|
|
48
|
-
const [activeCategory, setActiveCategory] = useState("popular");
|
|
49
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
50
|
-
if (!isOpen)
|
|
51
|
-
return null;
|
|
52
|
-
const handleStickerClick = (sticker) => {
|
|
53
|
-
onStickerSelect === null || onStickerSelect === void 0 ? void 0 : onStickerSelect(sticker);
|
|
54
|
-
};
|
|
55
|
-
const filteredStickers = searchTerm
|
|
56
|
-
? stickerCategories[activeCategory].stickers.filter((sticker) => sticker.includes(searchTerm))
|
|
57
|
-
: stickerCategories[activeCategory].stickers;
|
|
58
|
-
return (<div ref={ref} className="absolute bottom-full mb-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 w-full sm:max-w-xs md:w-80" style={style} // Apply dynamic style here
|
|
59
|
-
>
|
|
60
|
-
<div className="p-2">
|
|
61
|
-
{/* Category Icons Row - Compact */}
|
|
62
|
-
<div className="flex items-center justify-between mb-2 pb-1 border-b border-gray-100">
|
|
63
|
-
{Object.entries(stickerCategories).map(([key, category]) => {
|
|
64
|
-
const Icon = category.icon;
|
|
65
|
-
const isActive = activeCategory === key;
|
|
66
|
-
return (<button key={key} onClick={() => setActiveCategory(key)} className={`
|
|
67
|
-
p-1.5 rounded-full transition-all duration-200 hover:bg-gray-100
|
|
68
|
-
${isActive ? "text-purple-500 bg-purple-50" : "text-gray-500"}
|
|
69
|
-
`} title={category.label}>
|
|
70
|
-
<Icon className="w-4 h-4"/>
|
|
71
|
-
</button>);
|
|
72
|
-
})}
|
|
73
|
-
|
|
74
|
-
{/* Close button */}
|
|
75
|
-
<button onClick={onClose} className="p-1.5 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100">
|
|
76
|
-
<X className="w-4 h-4"/>
|
|
77
|
-
</button>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
{/* Search Bar - Compact */}
|
|
81
|
-
<div className="relative mb-2">
|
|
82
|
-
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 w-3 h-3 text-gray-400"/>
|
|
83
|
-
<input type="text" placeholder="Tìm sticker" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="w-full pl-7 pr-3 py-1.5 text-xs bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent"/>
|
|
84
|
-
</div>
|
|
85
|
-
|
|
86
|
-
{/* Category Title - Compact */}
|
|
87
|
-
<div className="mb-2">
|
|
88
|
-
<h3 className="text-xs font-medium text-gray-900">{stickerCategories[activeCategory].label}</h3>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
{/* Sticker Grid - Compact */}
|
|
92
|
-
<div className="grid grid-cols-6 gap-1 max-h-48 overflow-y-auto">
|
|
93
|
-
{filteredStickers.map((sticker, index) => (<button key={index} onClick={() => handleStickerClick(sticker)} className="p-2 text-xl hover:bg-gray-100 rounded transition-all duration-200 hover:scale-110 active:scale-95 border border-gray-100 hover:border-gray-200" title={sticker}>
|
|
94
|
-
{sticker}
|
|
95
|
-
</button>))}
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
{/* Empty state */}
|
|
99
|
-
{filteredStickers.length === 0 && (<div className="text-center py-4 text-gray-500">
|
|
100
|
-
<div className="text-lg mb-1">🔍</div>
|
|
101
|
-
<p className="text-xs">Không tìm thấy sticker nào</p>
|
|
102
|
-
</div>)}
|
|
103
|
-
</div>
|
|
104
|
-
</div>);
|
|
105
|
-
});
|
|
106
|
-
StickerPicker.displayName = "StickerPicker";
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
interface SwipeIndicatorProps {
|
|
2
|
-
show: boolean;
|
|
3
|
-
direction: "left" | "right";
|
|
4
|
-
text: string;
|
|
5
|
-
className?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function SwipeIndicator({ show, direction, text, className }: SwipeIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=SwipeIndicator.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeIndicator.d.ts","sourceRoot":"","sources":["../../src/components/SwipeIndicator.tsx"],"names":[],"mappings":"AAIA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAc,EAAE,EAAE,mBAAmB,kDAmC5F"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
-
export function SwipeIndicator({ show, direction, text, className = "" }) {
|
|
5
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
if (show) {
|
|
8
|
-
setIsVisible(true);
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
const timer = setTimeout(() => setIsVisible(false), 300);
|
|
12
|
-
return () => clearTimeout(timer);
|
|
13
|
-
}
|
|
14
|
-
}, [show]);
|
|
15
|
-
if (!isVisible)
|
|
16
|
-
return null;
|
|
17
|
-
return (_jsxs("div", { className: `
|
|
18
|
-
fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50
|
|
19
|
-
bg-black bg-opacity-80 text-white px-4 py-3 rounded-lg
|
|
20
|
-
flex items-center space-x-2 transition-all duration-300
|
|
21
|
-
${show ? "opacity-100 scale-100" : "opacity-0 scale-95"}
|
|
22
|
-
${className}
|
|
23
|
-
`, children: [_jsx("svg", { className: `w-5 h-5 ${direction === "left" ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }), _jsx("span", { className: "text-sm font-medium", children: text })] }));
|
|
24
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
export function SwipeIndicator({ show, direction, text, className = "" }) {
|
|
4
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
if (show) {
|
|
7
|
-
setIsVisible(true);
|
|
8
|
-
}
|
|
9
|
-
else {
|
|
10
|
-
const timer = setTimeout(() => setIsVisible(false), 300);
|
|
11
|
-
return () => clearTimeout(timer);
|
|
12
|
-
}
|
|
13
|
-
}, [show]);
|
|
14
|
-
if (!isVisible)
|
|
15
|
-
return null;
|
|
16
|
-
return (<div className={`
|
|
17
|
-
fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50
|
|
18
|
-
bg-black bg-opacity-80 text-white px-4 py-3 rounded-lg
|
|
19
|
-
flex items-center space-x-2 transition-all duration-300
|
|
20
|
-
${show ? "opacity-100 scale-100" : "opacity-0 scale-95"}
|
|
21
|
-
${className}
|
|
22
|
-
`}>
|
|
23
|
-
<svg className={`w-5 h-5 ${direction === "left" ? "rotate-180" : ""}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
24
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7"/>
|
|
25
|
-
</svg>
|
|
26
|
-
<span className="text-sm font-medium">{text}</span>
|
|
27
|
-
</div>);
|
|
28
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { TextFormattingToolbarProps } from "../types/chat";
|
|
3
|
-
export declare const TextFormattingToolbar: React.ForwardRefExoticComponent<Omit<TextFormattingToolbarProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
4
|
-
//# sourceMappingURL=TextFormattingToolbar.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TextFormattingToolbar.d.ts","sourceRoot":"","sources":["../../src/components/TextFormattingToolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAA;AAa/D,eAAO,MAAM,qBAAqB,gHAuDjC,CAAA"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { Bold, Italic, Strikethrough, List, ListOrdered, Link, Quote, Code, X } from "lucide-react";
|
|
5
|
-
const formatButtons = [
|
|
6
|
-
{ icon: Bold, label: "Đậm", format: "bold", shortcut: "Ctrl+B", symbol: "**" },
|
|
7
|
-
{ icon: Italic, label: "Nghiêng", format: "italic", shortcut: "Ctrl+I", symbol: "*" },
|
|
8
|
-
{ icon: Strikethrough, label: "Gạch ngang", format: "strikethrough", shortcut: "Ctrl+Shift+X", symbol: "~~" },
|
|
9
|
-
{ icon: ListOrdered, label: "Danh sách số", format: "numbered-list", shortcut: "Ctrl+Shift+7", symbol: "1." },
|
|
10
|
-
{ icon: List, label: "Danh sách dấu đầu dòng", format: "bullet-list", shortcut: "Ctrl+Shift+8", symbol: "•" },
|
|
11
|
-
{ icon: Link, label: "Liên kết", format: "link", shortcut: "Ctrl+K", symbol: "[]" },
|
|
12
|
-
{ icon: Quote, label: "Trích dẫn", format: "quote", shortcut: "Ctrl+Shift+>", symbol: ">" },
|
|
13
|
-
{ icon: Code, label: "Mã", format: "code", shortcut: "Ctrl+E", symbol: "`" },
|
|
14
|
-
];
|
|
15
|
-
export const TextFormattingToolbar = React.forwardRef(({ isOpen, onClose, onFormatSelect, selectedFormats = [], style }, ref) => {
|
|
16
|
-
if (!isOpen)
|
|
17
|
-
return null;
|
|
18
|
-
return (_jsx("div", { ref: ref, className: "absolute bottom-full mb-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 w-full sm:max-w-xs md:w-80", style: style, children: _jsxs("div", { className: "p-2", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("h3", { className: "text-xs font-medium text-gray-900", children: "\u0110\u1ECBnh d\u1EA1ng v\u0103n b\u1EA3n" }), _jsx("button", { onClick: onClose, className: "p-1 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100", children: _jsx(X, { className: "w-3 h-3" }) })] }), _jsx("div", { className: "grid grid-cols-4 gap-1", children: formatButtons.map((button) => {
|
|
19
|
-
const Icon = button.icon;
|
|
20
|
-
const isSelected = selectedFormats.includes(button.format);
|
|
21
|
-
return (_jsxs("button", { onClick: () => onFormatSelect(button.format), className: `
|
|
22
|
-
flex flex-col items-center p-2 rounded transition-all duration-200 hover:bg-gray-50 active:scale-95
|
|
23
|
-
${isSelected
|
|
24
|
-
? "bg-blue-50 text-blue-600 border border-blue-200"
|
|
25
|
-
: "text-gray-600 hover:text-gray-800 border border-gray-200"}
|
|
26
|
-
`, title: `${button.label} (${button.shortcut})`, "aria-label": button.label, children: [_jsx(Icon, { className: "w-4 h-4 mb-1" }), _jsx("span", { className: "text-xs font-medium", children: button.symbol })] }, button.format));
|
|
27
|
-
}) }), _jsx("div", { className: "mt-2 pt-2 border-t border-gray-100", children: _jsx("p", { className: "text-xs text-gray-500 text-center", children: "\uD83D\uDCA1 S\u1EED d\u1EE5ng ph\u00EDm t\u1EAFt \u0111\u1EC3 \u0111\u1ECBnh d\u1EA1ng nhanh" }) })] }) }));
|
|
28
|
-
});
|
|
29
|
-
TextFormattingToolbar.displayName = "TextFormattingToolbar";
|