@xcelsior/ui-chat 1.0.1
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/.storybook/main.ts +27 -0
- package/.storybook/preview.tsx +39 -0
- package/CHANGELOG.md +32 -0
- package/README.md +526 -0
- package/biome.json +3 -0
- package/package.json +61 -0
- package/postcss.config.js +5 -0
- package/src/components/Chat.stories.tsx +54 -0
- package/src/components/Chat.tsx +194 -0
- package/src/components/ChatHeader.tsx +93 -0
- package/src/components/ChatInput.tsx +363 -0
- package/src/components/ChatWidget.tsx +234 -0
- package/src/components/MessageItem.stories.tsx +232 -0
- package/src/components/MessageItem.tsx +143 -0
- package/src/components/MessageList.tsx +189 -0
- package/src/components/PreChatForm.tsx +202 -0
- package/src/components/TypingIndicator.tsx +31 -0
- package/src/hooks/useFileUpload.ts +134 -0
- package/src/hooks/useMessages.ts +165 -0
- package/src/hooks/useTypingIndicator.ts +33 -0
- package/src/hooks/useWebSocket.ts +209 -0
- package/src/index.tsx +46 -0
- package/src/types.ts +145 -0
- package/src/utils/api.ts +43 -0
- package/tsconfig.json +5 -0
- package/tsup.config.ts +12 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { StorybookConfig } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
const config: StorybookConfig = {
|
|
4
|
+
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
|
5
|
+
addons: [
|
|
6
|
+
'@storybook/addon-links',
|
|
7
|
+
'@storybook/addon-essentials',
|
|
8
|
+
'@storybook/addon-interactions',
|
|
9
|
+
],
|
|
10
|
+
framework: {
|
|
11
|
+
name: '@storybook/react-vite',
|
|
12
|
+
options: {},
|
|
13
|
+
},
|
|
14
|
+
docs: {
|
|
15
|
+
autodocs: 'tag',
|
|
16
|
+
},
|
|
17
|
+
typescript: {
|
|
18
|
+
check: false,
|
|
19
|
+
reactDocgen: 'react-docgen-typescript',
|
|
20
|
+
reactDocgenTypescriptOptions: {
|
|
21
|
+
shouldExtractLiteralValuesFromEnum: true,
|
|
22
|
+
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default config;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import '@xcelsior/design-system/styles';
|
|
2
|
+
import type { Preview } from '@storybook/react';
|
|
3
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
4
|
+
import { ToastContainer } from '@xcelsior/design-system';
|
|
5
|
+
|
|
6
|
+
const queryClient = new QueryClient({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
queries: {
|
|
9
|
+
retry: false,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const preview: Preview = {
|
|
15
|
+
parameters: {
|
|
16
|
+
actions: { argTypesRegex: '^on[A-Z].*' },
|
|
17
|
+
controls: {
|
|
18
|
+
matchers: {
|
|
19
|
+
color: /(background|color)$/i,
|
|
20
|
+
date: /Date$/,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
docs: {
|
|
24
|
+
toc: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
tags: ['autodocs'],
|
|
28
|
+
decorators: [
|
|
29
|
+
(Story) => (
|
|
30
|
+
<ToastContainer>
|
|
31
|
+
<QueryClientProvider client={queryClient}>
|
|
32
|
+
<Story />
|
|
33
|
+
</QueryClientProvider>
|
|
34
|
+
</ToastContainer>
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default preview;
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 631caeb: initial version
|
|
8
|
+
|
|
9
|
+
All notable changes to this project will be documented in this file.
|
|
10
|
+
|
|
11
|
+
## [1.0.0] - 2025-10-28
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Initial release of @xcelsior/ui-chat
|
|
16
|
+
- Real-time WebSocket-based chat functionality
|
|
17
|
+
- Emoji picker support with @emoji-mart/react
|
|
18
|
+
- File upload support with progress tracking
|
|
19
|
+
- Typing indicators
|
|
20
|
+
- Read receipts
|
|
21
|
+
- Message status tracking (sent, delivered, read)
|
|
22
|
+
- Support for text, image, and file messages
|
|
23
|
+
- Markdown rendering in messages
|
|
24
|
+
- Dark mode support
|
|
25
|
+
- Responsive design
|
|
26
|
+
- Accessibility features
|
|
27
|
+
- Comprehensive Storybook documentation
|
|
28
|
+
- TypeScript type definitions
|
|
29
|
+
- Custom hooks for WebSocket, messages, file uploads, and typing indicators
|
|
30
|
+
- Individual components for custom implementations
|
|
31
|
+
- Integration with @xcelsior/design-system and @xcelsior/ui-fields
|
|
32
|
+
- Mock WebSocket for Storybook testing
|
package/README.md
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
# @xcelsior/ui-chat
|
|
2
|
+
|
|
3
|
+
A comprehensive, real-time chat widget component for React applications with WebSocket support, emoji picker, file uploads, and more.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 **Real-time Communication**: WebSocket-based instant messaging
|
|
8
|
+
- 💬 **Rich Messages**: Support for text, markdown, images, and files
|
|
9
|
+
- 🤖 **AI Assistant**: Visual differentiation for AI-generated messages with robot icon
|
|
10
|
+
- 😊 **Emoji Support**: Built-in emoji picker
|
|
11
|
+
- 📎 **File Uploads**: Upload and share files with progress tracking
|
|
12
|
+
- ⌨️ **Typing Indicators**: Show when users are typing
|
|
13
|
+
- ✓✓ **Read Receipts**: Track message delivery and read status
|
|
14
|
+
- 🎨 **Customizable**: Fully configurable appearance and behavior
|
|
15
|
+
- 📱 **Responsive**: Works great on all screen sizes
|
|
16
|
+
- 🌓 **Dark Mode**: Automatic dark mode support
|
|
17
|
+
- ♿ **Accessible**: Built with accessibility in mind
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @xcelsior/ui-chat @xcelsior/design-system @xcelsior/ui-fields
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Peer Dependencies
|
|
26
|
+
|
|
27
|
+
Make sure you have the following peer dependencies installed:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add react react-dom @tanstack/react-query react-hook-form flowbite-react
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Basic Example
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { ChatWidget } from '@xcelsior/ui-chat';
|
|
39
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
40
|
+
|
|
41
|
+
const queryClient = new QueryClient();
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
const chatConfig = {
|
|
45
|
+
websocketUrl: 'wss://your-api.com/chat',
|
|
46
|
+
userId: 'user-123',
|
|
47
|
+
userType: 'customer',
|
|
48
|
+
conversationId: 'conv-456',
|
|
49
|
+
currentUser: {
|
|
50
|
+
id: 'user-123',
|
|
51
|
+
name: 'John Doe',
|
|
52
|
+
email: 'john@example.com',
|
|
53
|
+
type: 'customer',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<QueryClientProvider client={queryClient}>
|
|
59
|
+
<ChatWidget config={chatConfig} />
|
|
60
|
+
</QueryClientProvider>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### With File Upload
|
|
66
|
+
|
|
67
|
+
The file upload feature uses a presigned URL approach for secure, direct-to-S3 uploads:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { ChatWidget } from '@xcelsior/ui-chat';
|
|
71
|
+
|
|
72
|
+
const chatConfig = {
|
|
73
|
+
// ... other config
|
|
74
|
+
fileUpload: {
|
|
75
|
+
uploadUrl: 'https://your-api.com/attachments/upload-url',
|
|
76
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB (default)
|
|
77
|
+
allowedTypes: [
|
|
78
|
+
'image/jpeg',
|
|
79
|
+
'image/png',
|
|
80
|
+
'image/gif',
|
|
81
|
+
'image/webp',
|
|
82
|
+
'application/pdf',
|
|
83
|
+
'application/msword',
|
|
84
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
85
|
+
'text/plain',
|
|
86
|
+
'text/csv',
|
|
87
|
+
],
|
|
88
|
+
headers: {
|
|
89
|
+
'Authorization': 'Bearer your-token',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
<ChatWidget config={chatConfig} />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**How it works:**
|
|
98
|
+
|
|
99
|
+
1. User selects a file
|
|
100
|
+
2. Widget requests a presigned upload URL from your API (`POST /attachments/upload-url`)
|
|
101
|
+
3. Widget uploads file directly to S3 using the presigned URL
|
|
102
|
+
4. File becomes accessible via CloudFront CDN
|
|
103
|
+
5. The CloudFront URL is inserted into the message as markdown
|
|
104
|
+
|
|
105
|
+
**Backend API Contract:**
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Request
|
|
109
|
+
POST /attachments/upload-url
|
|
110
|
+
{
|
|
111
|
+
"fileName": "document.pdf",
|
|
112
|
+
"contentType": "application/pdf",
|
|
113
|
+
"fileSize": 1234567
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Response
|
|
117
|
+
{
|
|
118
|
+
"data": {
|
|
119
|
+
"uploadUrl": "https://s3.amazonaws.com/...", // Presigned URL for upload
|
|
120
|
+
"attachmentUrl": "https://cdn.example.com/...", // Final public URL
|
|
121
|
+
"key": "attachments/uuid.pdf", // S3 key
|
|
122
|
+
"expiresIn": 300 // URL expiry (seconds)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### With Event Callbacks
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { ChatWidget } from '@xcelsior/ui-chat';
|
|
131
|
+
|
|
132
|
+
const chatConfig = {
|
|
133
|
+
// ... other config
|
|
134
|
+
onMessageSent: (message) => {
|
|
135
|
+
console.log('Message sent:', message);
|
|
136
|
+
},
|
|
137
|
+
onMessageReceived: (message) => {
|
|
138
|
+
console.log('Message received:', message);
|
|
139
|
+
},
|
|
140
|
+
onConnectionChange: (connected) => {
|
|
141
|
+
console.log('Connection status:', connected);
|
|
142
|
+
},
|
|
143
|
+
onError: (error) => {
|
|
144
|
+
console.error('Chat error:', error);
|
|
145
|
+
},
|
|
146
|
+
toast: {
|
|
147
|
+
success: (message) => console.log('Success:', message),
|
|
148
|
+
error: (message) => console.error('Error:', message),
|
|
149
|
+
info: (message) => console.log('Info:', message),
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
<ChatWidget config={chatConfig} />
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Configuration
|
|
157
|
+
|
|
158
|
+
### IChatConfig
|
|
159
|
+
|
|
160
|
+
The main configuration object for the chat widget.
|
|
161
|
+
|
|
162
|
+
| Property | Type | Required | Description |
|
|
163
|
+
|----------|------|----------|-------------|
|
|
164
|
+
| `websocketUrl` | `string` | ✓ | WebSocket server URL |
|
|
165
|
+
| `userId` | `string` | ✓ | Current user's ID |
|
|
166
|
+
| `userType` | `'customer' \| 'agent'` | ✓ | Type of user |
|
|
167
|
+
| `currentUser` | `IUser` | ✓ | Current user information |
|
|
168
|
+
| `conversationId` | `string` | | Conversation ID (auto-generated if not provided) |
|
|
169
|
+
| `httpApiUrl` | `string` | | REST API URL for fetching data |
|
|
170
|
+
| `headers` | `Record<string, string>` | | HTTP headers for API requests |
|
|
171
|
+
| `fileUpload` | `IFileUploadConfig` | | File upload configuration |
|
|
172
|
+
| `enableEmoji` | `boolean` | | Enable emoji picker (default: true) |
|
|
173
|
+
| `enableFileUpload` | `boolean` | | Enable file uploads (default: true) |
|
|
174
|
+
| `enableTypingIndicator` | `boolean` | | Enable typing indicators (default: true) |
|
|
175
|
+
| `enableReadReceipts` | `boolean` | | Enable read receipts (default: true) |
|
|
176
|
+
| `onMessageSent` | `(message: IMessage) => void` | | Callback when message is sent |
|
|
177
|
+
| `onMessageReceived` | `(message: IMessage) => void` | | Callback when message is received |
|
|
178
|
+
| `onConnectionChange` | `(connected: boolean) => void` | | Callback when connection status changes |
|
|
179
|
+
| `onError` | `(error: Error) => void` | | Callback when error occurs |
|
|
180
|
+
| `toast` | `object` | | Toast notification handlers |
|
|
181
|
+
|
|
182
|
+
### IFileUploadConfig
|
|
183
|
+
|
|
184
|
+
Configuration for file uploads.
|
|
185
|
+
|
|
186
|
+
| Property | Type | Required | Description |
|
|
187
|
+
|----------|------|----------|-------------|
|
|
188
|
+
| `uploadUrl` | `string` | ✓ | Upload endpoint URL |
|
|
189
|
+
| `maxFileSize` | `number` | | Maximum file size in bytes (default: 5MB) |
|
|
190
|
+
| `allowedTypes` | `string[]` | | Allowed MIME types (default: images and PDFs) |
|
|
191
|
+
| `headers` | `Record<string, string>` | | Additional headers for upload request |
|
|
192
|
+
|
|
193
|
+
## WebSocket Integration
|
|
194
|
+
|
|
195
|
+
The chat widget connects to your WebSocket API using the following protocol:
|
|
196
|
+
|
|
197
|
+
### Connection
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
wss://your-api.com/chat?userId=user-123&userType=customer&conversationId=conv-456
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Sending Messages
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"action": "sendMessage",
|
|
208
|
+
"data": {
|
|
209
|
+
"conversationId": "conv-456",
|
|
210
|
+
"content": "Hello!",
|
|
211
|
+
"messageType": "text"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Receiving Messages
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"type": "message",
|
|
221
|
+
"data": {
|
|
222
|
+
"id": "msg-789",
|
|
223
|
+
"conversationId": "conv-456",
|
|
224
|
+
"senderId": "agent-123",
|
|
225
|
+
"senderType": "agent",
|
|
226
|
+
"content": "Hi! How can I help?",
|
|
227
|
+
"messageType": "text",
|
|
228
|
+
"createdAt": "2025-10-28T12:00:00Z",
|
|
229
|
+
"status": "delivered"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Typing Indicator
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"action": "typing",
|
|
239
|
+
"data": {
|
|
240
|
+
"conversationId": "conv-456",
|
|
241
|
+
"isTyping": true
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## REST API Integration
|
|
247
|
+
|
|
248
|
+
The chat widget can fetch existing messages from your REST API when `httpApiUrl` is provided in the config.
|
|
249
|
+
|
|
250
|
+
### Fetching Messages
|
|
251
|
+
|
|
252
|
+
When the widget loads or the `conversationId` changes, it will automatically fetch existing messages:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
GET /messages?conversationId=conv-456&limit=50
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Expected response format:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"success": true,
|
|
263
|
+
"data": {
|
|
264
|
+
"items": [...messages],
|
|
265
|
+
"nextPageToken": "optional-pagination-token"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
or alternatively:
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"success": true,
|
|
275
|
+
"data": [...messages],
|
|
276
|
+
"nextToken": "optional-pagination-token"
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Example with HTTP API
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
const chatConfig = {
|
|
284
|
+
websocketUrl: 'wss://your-api.com/chat',
|
|
285
|
+
httpApiUrl: 'https://your-api.com',
|
|
286
|
+
conversationId: 'conv-456',
|
|
287
|
+
headers: {
|
|
288
|
+
'Authorization': 'Bearer your-token',
|
|
289
|
+
},
|
|
290
|
+
// ... other config
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
<ChatWidget config={chatConfig} />
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Performance Optimizations
|
|
297
|
+
|
|
298
|
+
### Debounced Typing Indicator
|
|
299
|
+
|
|
300
|
+
The typing indicator is now debounced to reduce unnecessary WebSocket messages:
|
|
301
|
+
- **Start typing**: Sent after 300ms of typing activity
|
|
302
|
+
- **Stop typing**: Sent after 1.5s of inactivity
|
|
303
|
+
- This reduces WebSocket traffic while maintaining responsive user feedback
|
|
304
|
+
|
|
305
|
+
## Components
|
|
306
|
+
|
|
307
|
+
### ChatWidget
|
|
308
|
+
|
|
309
|
+
The main chat widget component that includes all features.
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
import { ChatWidget } from '@xcelsior/ui-chat';
|
|
313
|
+
|
|
314
|
+
<ChatWidget config={chatConfig} className="custom-class" />
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Individual Components
|
|
318
|
+
|
|
319
|
+
For custom implementations, you can use individual components:
|
|
320
|
+
|
|
321
|
+
#### MessageList
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { MessageList } from '@xcelsior/ui-chat';
|
|
325
|
+
|
|
326
|
+
<MessageList
|
|
327
|
+
messages={messages}
|
|
328
|
+
currentUser={currentUser}
|
|
329
|
+
isTyping={false}
|
|
330
|
+
autoScroll={true}
|
|
331
|
+
/>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### MessageItem
|
|
335
|
+
|
|
336
|
+
Displays individual messages with automatic icon detection:
|
|
337
|
+
- 🤖 **Robot icon** for AI-generated messages (when `metadata.isAI === true`)
|
|
338
|
+
- 🎧 **Headset icon** for human agent messages
|
|
339
|
+
- 👤 **User icon** for customer messages
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
import { MessageItem } from '@xcelsior/ui-chat';
|
|
343
|
+
|
|
344
|
+
// Regular agent message
|
|
345
|
+
<MessageItem
|
|
346
|
+
message={message}
|
|
347
|
+
currentUser={currentUser}
|
|
348
|
+
showAvatar={true}
|
|
349
|
+
showTimestamp={true}
|
|
350
|
+
/>
|
|
351
|
+
|
|
352
|
+
// AI-generated message (automatically shows robot icon)
|
|
353
|
+
<MessageItem
|
|
354
|
+
message={{
|
|
355
|
+
...message,
|
|
356
|
+
senderType: 'agent',
|
|
357
|
+
metadata: { isAI: true }
|
|
358
|
+
}}
|
|
359
|
+
currentUser={currentUser}
|
|
360
|
+
showAvatar={true}
|
|
361
|
+
showTimestamp={true}
|
|
362
|
+
/>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### ChatInput
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
import { ChatInput } from '@xcelsior/ui-chat';
|
|
369
|
+
|
|
370
|
+
<ChatInput
|
|
371
|
+
onSend={handleSend}
|
|
372
|
+
onTyping={handleTyping}
|
|
373
|
+
config={chatConfig}
|
|
374
|
+
fileUpload={fileUploadHook}
|
|
375
|
+
/>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### ChatHeader
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
import { ChatHeader } from '@xcelsior/ui-chat';
|
|
382
|
+
|
|
383
|
+
<ChatHeader
|
|
384
|
+
agent={agent}
|
|
385
|
+
onClose={handleClose}
|
|
386
|
+
onMinimize={handleMinimize}
|
|
387
|
+
/>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Hooks
|
|
391
|
+
|
|
392
|
+
### useWebSocket
|
|
393
|
+
|
|
394
|
+
Custom hook for WebSocket connection management.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
import { useWebSocket } from '@xcelsior/ui-chat';
|
|
398
|
+
|
|
399
|
+
const { isConnected, sendMessage, lastMessage, error, reconnect } = useWebSocket(config);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### useMessages
|
|
403
|
+
|
|
404
|
+
Custom hook for message management.
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
import { useMessages } from '@xcelsior/ui-chat';
|
|
408
|
+
|
|
409
|
+
const { messages, addMessage, updateMessageStatus, clearMessages } = useMessages(websocket, config);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### useFileUpload
|
|
413
|
+
|
|
414
|
+
Custom hook for file uploads.
|
|
415
|
+
|
|
416
|
+
```tsx
|
|
417
|
+
import { useFileUpload } from '@xcelsior/ui-chat';
|
|
418
|
+
|
|
419
|
+
const { uploadFile, isUploading, uploadProgress, error, canUpload } = useFileUpload(config);
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### useTypingIndicator
|
|
423
|
+
|
|
424
|
+
Custom hook for typing indicators.
|
|
425
|
+
|
|
426
|
+
```tsx
|
|
427
|
+
import { useTypingIndicator } from '@xcelsior/ui-chat';
|
|
428
|
+
|
|
429
|
+
const { isTyping, typingUsers } = useTypingIndicator(websocket);
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Styling
|
|
433
|
+
|
|
434
|
+
The component uses Tailwind CSS for styling and supports dark mode automatically. You can customize the appearance by:
|
|
435
|
+
|
|
436
|
+
1. **Using the className prop**: Pass custom classes to the ChatWidget component
|
|
437
|
+
2. **Overriding CSS variables**: Define custom CSS variables for colors and sizes
|
|
438
|
+
3. **Using Tailwind configuration**: Extend your Tailwind config to customize the design system
|
|
439
|
+
|
|
440
|
+
## Backend Integration
|
|
441
|
+
|
|
442
|
+
This package is designed to work with the `@xcelsior/chat-api` backend service. For more information on setting up the backend, see the [chat-api documentation](../../services/chat-api/README.md).
|
|
443
|
+
|
|
444
|
+
### Required Backend Endpoints
|
|
445
|
+
|
|
446
|
+
- **WebSocket**: For real-time communication
|
|
447
|
+
- `$connect`: Handle new connections
|
|
448
|
+
- `$disconnect`: Handle disconnections
|
|
449
|
+
- `sendMessage`: Handle message sending
|
|
450
|
+
- `typing`: Handle typing indicators
|
|
451
|
+
|
|
452
|
+
- **REST API** (optional): For fetching historical data
|
|
453
|
+
- `GET /messages?conversationId=xxx`: Fetch message history
|
|
454
|
+
- `GET /conversations/:id`: Fetch conversation details
|
|
455
|
+
|
|
456
|
+
- **File Upload API** (optional): For file attachment support
|
|
457
|
+
- `POST /attachments/upload-url`: Generate presigned S3 upload URL
|
|
458
|
+
- Request: `{ fileName, contentType, fileSize }`
|
|
459
|
+
- Response: `{ uploadUrl, attachmentUrl, key, expiresIn }`
|
|
460
|
+
|
|
461
|
+
## Examples
|
|
462
|
+
|
|
463
|
+
Check out the Storybook for live examples and interactive demos:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
pnpm storybook
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Development
|
|
470
|
+
|
|
471
|
+
### Build
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
pnpm build
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Development Mode
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
pnpm dev
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Storybook
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
pnpm storybook
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Lint
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
pnpm lint
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## TypeScript
|
|
496
|
+
|
|
497
|
+
This package is written in TypeScript and includes full type definitions. All types are exported from the main package:
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
import type {
|
|
501
|
+
IChatConfig,
|
|
502
|
+
IMessage,
|
|
503
|
+
IUser,
|
|
504
|
+
IConversation,
|
|
505
|
+
MessageType,
|
|
506
|
+
MessageStatus,
|
|
507
|
+
} from '@xcelsior/ui-chat';
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Browser Support
|
|
511
|
+
|
|
512
|
+
- Chrome (latest)
|
|
513
|
+
- Firefox (latest)
|
|
514
|
+
- Safari (latest)
|
|
515
|
+
- Edge (latest)
|
|
516
|
+
|
|
517
|
+
WebSocket support is required.
|
|
518
|
+
|
|
519
|
+
## License
|
|
520
|
+
|
|
521
|
+
MIT
|
|
522
|
+
|
|
523
|
+
## Support
|
|
524
|
+
|
|
525
|
+
For issues and questions, please contact the development team or create an issue in the repository.
|
|
526
|
+
|
package/biome.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xcelsior/ui-chat",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
10
|
+
"@storybook/addon-interactions": "^8.6.14",
|
|
11
|
+
"@storybook/addon-links": "^8.6.14",
|
|
12
|
+
"@storybook/addon-onboarding": "^8.6.14",
|
|
13
|
+
"@storybook/blocks": "^8.6.14",
|
|
14
|
+
"@storybook/nextjs": "^8.6.14",
|
|
15
|
+
"@storybook/react": "^8.6.14",
|
|
16
|
+
"@storybook/react-vite": "^8.6.14",
|
|
17
|
+
"@storybook/testing-library": "^0.2.2",
|
|
18
|
+
"@types/react": "^18.2.0",
|
|
19
|
+
"@types/react-dom": "^18.2.0",
|
|
20
|
+
"@types/node": "^24.9.2",
|
|
21
|
+
"react": "^18.2.0",
|
|
22
|
+
"storybook": "^8.6.14",
|
|
23
|
+
"@tailwindcss/postcss": "^4.1.13",
|
|
24
|
+
"tsup": "^8.0.2",
|
|
25
|
+
"typescript": "^5.3.3",
|
|
26
|
+
"@xcelsior/design-system": "1.0.4",
|
|
27
|
+
"@xcelsior/ui-fields": "1.0.3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@xcelsior/design-system": "^1",
|
|
31
|
+
"@xcelsior/ui-fields": "^1",
|
|
32
|
+
"flowbite-react": "^0.12",
|
|
33
|
+
"react": "^18.2.0",
|
|
34
|
+
"react-dom": "^18.2.0",
|
|
35
|
+
"react-hook-form": "^7",
|
|
36
|
+
"@tanstack/react-query": "^5.24.1"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@emoji-mart/react": "^1.1.1",
|
|
40
|
+
"axios": "^1.6.7",
|
|
41
|
+
"date-fns": "^3.3.1",
|
|
42
|
+
"react-markdown": "^9.0.1"
|
|
43
|
+
},
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"import": "./src/index.tsx",
|
|
47
|
+
"require": "./dist/index.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup src/index.tsx --format esm,cjs --dts --external react",
|
|
52
|
+
"dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react",
|
|
53
|
+
"prepublish": "npm run build",
|
|
54
|
+
"lint": "biome check .",
|
|
55
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
56
|
+
"storybook": "storybook dev -p 6007",
|
|
57
|
+
"build-storybook": "storybook build",
|
|
58
|
+
"predeploy": "npm run build-storybook",
|
|
59
|
+
"deploy": "aws s3 sync storybook-static/ s3://excelsior-ui-chat --delete"
|
|
60
|
+
}
|
|
61
|
+
}
|