@xcelsior/ui-chat 1.0.4 → 1.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.
@@ -0,0 +1,5 @@
1
+
2
+ > @xcelsior/ui-chat@1.0.0 lint /Users/tuannguyen/Work/xcelsior-packages/packages/ui/ui-chat
3
+ > biome check .
4
+
5
+ Checked 24 files in 17ms. No fixes applied.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - improve minimize
8
+
3
9
  ## 1.0.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -62,6 +62,88 @@ function App() {
62
62
  }
63
63
  ```
64
64
 
65
+ ### Chat Widget States (Bubble Mode)
66
+
67
+ The chat widget supports three states in popover mode:
68
+
69
+ - **`open`**: Fully open with chat interface visible
70
+ - **`minimized`**: Show bubble button only (default)
71
+ - **`closed`**: Fully hidden, no bubble visible
72
+
73
+ #### Uncontrolled Mode (Default)
74
+
75
+ By default, the widget starts in `minimized` state and manages its own state:
76
+
77
+ ```tsx
78
+ import { ChatWidget } from '@xcelsior/ui-chat';
79
+
80
+ function App() {
81
+ return (
82
+ <ChatWidget
83
+ config={chatConfig}
84
+ // Starts minimized by default
85
+ />
86
+ );
87
+ }
88
+ ```
89
+
90
+ You can also set a different default state:
91
+
92
+ ```tsx
93
+ <ChatWidget
94
+ config={chatConfig}
95
+ defaultState="open" // or "minimized" or "closed"
96
+ />
97
+ ```
98
+
99
+ #### Controlled Mode
100
+
101
+ Control the widget state externally:
102
+
103
+ ```tsx
104
+ import { ChatWidget, ChatWidgetState } from '@xcelsior/ui-chat';
105
+ import { useState } from 'react';
106
+
107
+ function App() {
108
+ const [chatState, setChatState] = useState<ChatWidgetState>('minimized');
109
+
110
+ return (
111
+ <>
112
+ <button onClick={() => setChatState('open')}>
113
+ Open Chat
114
+ </button>
115
+ <button onClick={() => setChatState('minimized')}>
116
+ Minimize Chat
117
+ </button>
118
+ <button onClick={() => setChatState('closed')}>
119
+ Close Chat
120
+ </button>
121
+
122
+ <ChatWidget
123
+ config={chatConfig}
124
+ state={chatState}
125
+ onStateChange={setChatState}
126
+ />
127
+ </>
128
+ );
129
+ }
130
+ ```
131
+
132
+ #### Listen to State Changes (Semi-controlled)
133
+
134
+ You can listen to state changes while keeping the widget uncontrolled:
135
+
136
+ ```tsx
137
+ <ChatWidget
138
+ config={chatConfig}
139
+ defaultState="minimized"
140
+ onStateChange={(state) => {
141
+ console.log('Chat state changed to:', state);
142
+ // Save to localStorage, analytics, etc.
143
+ }}
144
+ />
145
+ ```
146
+
65
147
  ### With File Upload
66
148
 
67
149
  The file upload feature uses a presigned URL approach for secure, direct-to-S3 uploads:
@@ -0,0 +1,337 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface IUser {
4
+ name: string;
5
+ email: string;
6
+ avatar?: string;
7
+ type: 'customer' | 'agent';
8
+ status?: 'online' | 'offline' | 'away' | 'busy';
9
+ }
10
+ type MessageType = 'text' | 'image' | 'file' | 'system';
11
+ type MessageStatus = 'sent' | 'delivered' | 'read';
12
+ interface IMessage {
13
+ id: string;
14
+ conversationId: string;
15
+ senderId: string;
16
+ senderType: 'customer' | 'agent' | 'system';
17
+ content: string;
18
+ messageType: MessageType;
19
+ createdAt: string;
20
+ status: MessageStatus;
21
+ metadata?: Record<string, unknown>;
22
+ }
23
+ type ConversationStatus = 'open' | 'pending' | 'closed' | 'archived';
24
+ type ConversationPriority = 'low' | 'medium' | 'high' | 'urgent';
25
+ type ConversationChannel = 'web' | 'mobile' | 'email';
26
+ interface IConversation {
27
+ id: string;
28
+ customerId: string;
29
+ assignedAgentId?: string;
30
+ status: ConversationStatus;
31
+ priority: ConversationPriority;
32
+ subject?: string;
33
+ channel: ConversationChannel;
34
+ tags?: string[];
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ closedAt?: string;
38
+ lastMessageAt?: string;
39
+ messageCount?: number;
40
+ unreadCount?: number;
41
+ satisfaction?: 1 | 2 | 3 | 4 | 5;
42
+ metadata?: Record<string, unknown>;
43
+ }
44
+ interface IWebSocketMessage {
45
+ type: 'message' | 'typing' | 'read' | 'connected' | 'error' | 'system';
46
+ data: any;
47
+ }
48
+ interface ISendMessageData {
49
+ conversationId: string;
50
+ content: string;
51
+ messageType?: MessageType;
52
+ }
53
+ interface ITypingData {
54
+ conversationId: string;
55
+ isTyping: boolean;
56
+ }
57
+ interface IReadMessageData {
58
+ messageId: string;
59
+ conversationId: string;
60
+ }
61
+ interface IFileUploadConfig {
62
+ uploadUrl: string;
63
+ maxFileSize?: number;
64
+ allowedTypes?: string[];
65
+ headers?: Record<string, string>;
66
+ }
67
+ interface IUploadedFile {
68
+ url: string;
69
+ name: string;
70
+ size: number;
71
+ type: string;
72
+ markdown?: string;
73
+ }
74
+ interface IChatConfig {
75
+ websocketUrl: string;
76
+ conversationId?: string;
77
+ apiKey: string;
78
+ currentUser: IUser;
79
+ fileUpload?: IFileUploadConfig;
80
+ httpApiUrl?: string;
81
+ headers?: Record<string, string>;
82
+ enableEmoji?: boolean;
83
+ enableFileUpload?: boolean;
84
+ enableTypingIndicator?: boolean;
85
+ enableReadReceipts?: boolean;
86
+ /**
87
+ * For agents, set this to true to disable creating a WebSocket connection in the chat widget.
88
+ * Agents should have a global WebSocket connection instead.
89
+ * @default false
90
+ */
91
+ disableWebSocket?: boolean;
92
+ onMessageSent?: (message: IMessage) => void;
93
+ onMessageReceived?: (message: IMessage) => void;
94
+ onConversationChange?: (conversation: IConversation) => void;
95
+ onConnectionChange?: (connected: boolean) => void;
96
+ onError?: (error: Error) => void;
97
+ toast?: {
98
+ success: (message: string) => void;
99
+ error: (message: string) => void;
100
+ info: (message: string) => void;
101
+ };
102
+ }
103
+ interface IApiResponse<T> {
104
+ success: boolean;
105
+ data?: T;
106
+ error?: {
107
+ code: string;
108
+ message: string;
109
+ };
110
+ pagination?: {
111
+ nextPageToken?: string;
112
+ };
113
+ }
114
+
115
+ interface ChatWidgetProps {
116
+ config: IChatConfig;
117
+ className?: string;
118
+ /**
119
+ * Variant of the chat widget:
120
+ * - 'popover': Fixed positioned floating widget (default)
121
+ * - 'fullPage': Full page layout that fills the container
122
+ */
123
+ variant?: 'popover' | 'fullPage';
124
+ /**
125
+ * External WebSocket connection (for agents with global connection)
126
+ */
127
+ externalWebSocket?: WebSocket | null;
128
+ /**
129
+ * Callback when user wants to minimize the widget
130
+ */
131
+ onMinimize?: () => void;
132
+ /**
133
+ * Callback when user wants to close the widget
134
+ */
135
+ onClose?: () => void;
136
+ }
137
+ declare function ChatWidget({ config, className, variant, externalWebSocket, onMinimize, onClose, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
138
+
139
+ type ChatWidgetState = 'open' | 'minimized' | 'closed' | 'undefined';
140
+ interface UseChatWidgetStateOptions {
141
+ /**
142
+ * Controlled state. When provided, the component is controlled.
143
+ */
144
+ state?: ChatWidgetState;
145
+ /**
146
+ * Default state for uncontrolled mode
147
+ * @default 'minimized'
148
+ */
149
+ defaultState?: ChatWidgetState;
150
+ /**
151
+ * Callback when state changes
152
+ */
153
+ onStateChange?: (state: ChatWidgetState) => void;
154
+ }
155
+ interface UseChatWidgetStateReturn {
156
+ /**
157
+ * Current state of the widget
158
+ */
159
+ currentState: ChatWidgetState;
160
+ /**
161
+ * Function to update the state
162
+ */
163
+ setState: (newState: ChatWidgetState) => void;
164
+ /**
165
+ * Whether the state is controlled
166
+ */
167
+ isControlled: boolean;
168
+ }
169
+ /**
170
+ * Hook to manage chat widget state (controlled vs uncontrolled)
171
+ * Encapsulates the logic for handling both controlled and uncontrolled state patterns
172
+ */
173
+ declare function useChatWidgetState({ state: controlledState, defaultState, onStateChange, }: UseChatWidgetStateOptions): UseChatWidgetStateReturn;
174
+
175
+ interface ChatWidgetWrapperProps {
176
+ /**
177
+ * Base configuration for the chat widget (without user info and conversationId)
178
+ */
179
+ config: Omit<IChatConfig, 'currentUser' | 'conversationId' | 'userId'> & {
180
+ currentUser?: Partial<IUser>;
181
+ conversationId?: string;
182
+ };
183
+ /**
184
+ * Custom className for the wrapper
185
+ */
186
+ className?: string;
187
+ /**
188
+ * Storage key prefix for persisting user data
189
+ * Defaults to 'xcelsior_chat'
190
+ */
191
+ storageKeyPrefix?: string;
192
+ /**
193
+ * Callback when user submits the pre-chat form
194
+ */
195
+ onPreChatSubmit?: (user: IUser) => void;
196
+ /**
197
+ * Controlled state. When provided, the component is controlled.
198
+ * - 'open': Fully open with chat interface
199
+ * - 'minimized': Show bubble button only
200
+ * - 'closed': Fully hidden
201
+ */
202
+ state?: ChatWidgetState;
203
+ /**
204
+ * Default state for uncontrolled mode
205
+ * @default 'minimized'
206
+ */
207
+ defaultState?: ChatWidgetState;
208
+ /**
209
+ * Callback when state changes
210
+ */
211
+ onStateChange?: (state: ChatWidgetState) => void;
212
+ }
213
+ /**
214
+ * Chat component that handles:
215
+ * - Automatic conversation ID generation
216
+ * - Pre-chat form for collecting user information
217
+ * - Session persistence in localStorage
218
+ */
219
+ declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, state, defaultState, onStateChange, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
220
+
221
+ interface ChatHeaderProps {
222
+ agent?: IUser;
223
+ onClose?: () => void;
224
+ onMinimize?: () => void;
225
+ }
226
+ declare function ChatHeader({ agent, onClose, onMinimize }: ChatHeaderProps): react_jsx_runtime.JSX.Element;
227
+
228
+ interface UseFileUploadReturn {
229
+ uploadFile: (file: File) => Promise<IUploadedFile | null>;
230
+ isUploading: boolean;
231
+ uploadProgress: number;
232
+ error: Error | null;
233
+ canUpload: boolean;
234
+ }
235
+ declare function useFileUpload(apiKey: string, config?: IFileUploadConfig): UseFileUploadReturn;
236
+
237
+ interface ChatInputProps {
238
+ onSend: (message: string) => void;
239
+ onTyping?: (isTyping: boolean) => void;
240
+ config: IChatConfig;
241
+ fileUpload: UseFileUploadReturn;
242
+ disabled?: boolean;
243
+ }
244
+ declare function ChatInput({ onSend, onTyping, config, fileUpload, disabled, }: ChatInputProps): react_jsx_runtime.JSX.Element;
245
+
246
+ interface MessageItemProps {
247
+ message: IMessage;
248
+ currentUser: IUser;
249
+ showAvatar?: boolean;
250
+ showTimestamp?: boolean;
251
+ }
252
+ declare function MessageItem({ message, currentUser, showAvatar, showTimestamp, }: MessageItemProps): react_jsx_runtime.JSX.Element;
253
+
254
+ interface MessageListProps {
255
+ messages: IMessage[];
256
+ currentUser: IUser;
257
+ isLoading?: boolean;
258
+ isTyping?: boolean;
259
+ typingUser?: string;
260
+ autoScroll?: boolean;
261
+ onLoadMore?: () => void;
262
+ hasMore?: boolean;
263
+ isLoadingMore?: boolean;
264
+ }
265
+ declare function MessageList({ messages, currentUser, isLoading, isTyping, typingUser, autoScroll, onLoadMore, hasMore, isLoadingMore, }: MessageListProps): react_jsx_runtime.JSX.Element;
266
+
267
+ interface TypingIndicatorProps {
268
+ isTyping: boolean;
269
+ userName?: string;
270
+ }
271
+ declare function TypingIndicator({ isTyping, userName }: TypingIndicatorProps): react_jsx_runtime.JSX.Element | null;
272
+
273
+ interface PreChatFormProps {
274
+ onSubmit: (name: string, email: string) => void;
275
+ className?: string;
276
+ initialName?: string;
277
+ initialEmail?: string;
278
+ /**
279
+ * Callback when user wants to minimize the form
280
+ */
281
+ onMinimize: () => void;
282
+ /**
283
+ * Callback when user wants to close the form
284
+ */
285
+ onClose: () => void;
286
+ }
287
+ /**
288
+ * PreChatForm component for collecting user information before starting chat
289
+ */
290
+ declare function PreChatForm({ onSubmit, className, initialName, initialEmail, onMinimize, onClose, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
291
+
292
+ interface UseWebSocketReturn {
293
+ isConnected: boolean;
294
+ sendMessage: (action: string, data: any) => void;
295
+ lastMessage: IWebSocketMessage | null;
296
+ error: Error | null;
297
+ reconnect: () => void;
298
+ }
299
+ /**
300
+ * Hook for WebSocket connection in chat widget.
301
+ * Can use an external WebSocket connection (for agents) via the externalWebSocket prop.
302
+ */
303
+ declare function useWebSocket(config: IChatConfig, externalWebSocket?: WebSocket | null): UseWebSocketReturn;
304
+
305
+ interface UseMessagesReturn {
306
+ messages: IMessage[];
307
+ addMessage: (message: IMessage) => void;
308
+ updateMessageStatus: (messageId: string, status: IMessage['status']) => void;
309
+ clearMessages: () => void;
310
+ isLoading: boolean;
311
+ error: Error | null;
312
+ loadMore: () => Promise<void>;
313
+ hasMore: boolean;
314
+ isLoadingMore: boolean;
315
+ }
316
+ declare function useMessages(websocket: UseWebSocketReturn, config: IChatConfig): UseMessagesReturn;
317
+
318
+ interface UseTypingIndicatorReturn {
319
+ isTyping: boolean;
320
+ typingUsers: string[];
321
+ }
322
+ declare function useTypingIndicator(websocket: UseWebSocketReturn): UseTypingIndicatorReturn;
323
+
324
+ interface FetchMessagesParams {
325
+ conversationId: string;
326
+ limit?: number;
327
+ pageToken?: string;
328
+ }
329
+ /**
330
+ * Fetch messages for a conversation from the REST API
331
+ */
332
+ declare function fetchMessages(baseUrl: string, params: FetchMessagesParams, headers?: Record<string, string>): Promise<{
333
+ data: IMessage[];
334
+ nextPageToken: string | undefined;
335
+ }>;
336
+
337
+ export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, type ChatWidgetState, type ConversationChannel, type ConversationPriority, type ConversationStatus, type IApiResponse, type IChatConfig, type IConversation, type IFileUploadConfig, type IMessage, type IReadMessageData, type ISendMessageData, type ITypingData, type IUploadedFile, type IUser, type IWebSocketMessage, MessageItem, MessageList, type MessageStatus, type MessageType, PreChatForm, TypingIndicator, type UseChatWidgetStateOptions, type UseChatWidgetStateReturn, fetchMessages, useChatWidgetState, useFileUpload, useMessages, useTypingIndicator, useWebSocket };