@xcelsior/ui-chat 1.0.5 → 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.
- package/CHANGELOG.md +6 -0
- package/README.md +82 -0
- package/dist/index.d.mts +72 -4
- package/dist/index.d.ts +72 -4
- package/dist/index.js +146 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -43
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Chat.tsx +60 -1
- package/src/components/ChatWidget.tsx +13 -37
- package/src/components/PreChatForm.tsx +65 -4
- package/src/hooks/useChatWidgetState.ts +68 -0
- package/src/index.tsx +8 -3
package/CHANGELOG.md
CHANGED
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:
|
package/dist/index.d.mts
CHANGED
|
@@ -125,8 +125,52 @@ interface ChatWidgetProps {
|
|
|
125
125
|
* External WebSocket connection (for agents with global connection)
|
|
126
126
|
*/
|
|
127
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;
|
|
128
136
|
}
|
|
129
|
-
declare function ChatWidget({ config, className, variant, externalWebSocket, }: ChatWidgetProps): react_jsx_runtime.JSX.Element
|
|
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;
|
|
130
174
|
|
|
131
175
|
interface ChatWidgetWrapperProps {
|
|
132
176
|
/**
|
|
@@ -149,6 +193,22 @@ interface ChatWidgetWrapperProps {
|
|
|
149
193
|
* Callback when user submits the pre-chat form
|
|
150
194
|
*/
|
|
151
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;
|
|
152
212
|
}
|
|
153
213
|
/**
|
|
154
214
|
* Chat component that handles:
|
|
@@ -156,7 +216,7 @@ interface ChatWidgetWrapperProps {
|
|
|
156
216
|
* - Pre-chat form for collecting user information
|
|
157
217
|
* - Session persistence in localStorage
|
|
158
218
|
*/
|
|
159
|
-
declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
|
|
219
|
+
declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, state, defaultState, onStateChange, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
|
|
160
220
|
|
|
161
221
|
interface ChatHeaderProps {
|
|
162
222
|
agent?: IUser;
|
|
@@ -215,11 +275,19 @@ interface PreChatFormProps {
|
|
|
215
275
|
className?: string;
|
|
216
276
|
initialName?: string;
|
|
217
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;
|
|
218
286
|
}
|
|
219
287
|
/**
|
|
220
288
|
* PreChatForm component for collecting user information before starting chat
|
|
221
289
|
*/
|
|
222
|
-
declare function PreChatForm({ onSubmit, className, initialName, initialEmail, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
|
|
290
|
+
declare function PreChatForm({ onSubmit, className, initialName, initialEmail, onMinimize, onClose, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
|
|
223
291
|
|
|
224
292
|
interface UseWebSocketReturn {
|
|
225
293
|
isConnected: boolean;
|
|
@@ -266,4 +334,4 @@ declare function fetchMessages(baseUrl: string, params: FetchMessagesParams, hea
|
|
|
266
334
|
nextPageToken: string | undefined;
|
|
267
335
|
}>;
|
|
268
336
|
|
|
269
|
-
export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, 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, fetchMessages, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -125,8 +125,52 @@ interface ChatWidgetProps {
|
|
|
125
125
|
* External WebSocket connection (for agents with global connection)
|
|
126
126
|
*/
|
|
127
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;
|
|
128
136
|
}
|
|
129
|
-
declare function ChatWidget({ config, className, variant, externalWebSocket, }: ChatWidgetProps): react_jsx_runtime.JSX.Element
|
|
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;
|
|
130
174
|
|
|
131
175
|
interface ChatWidgetWrapperProps {
|
|
132
176
|
/**
|
|
@@ -149,6 +193,22 @@ interface ChatWidgetWrapperProps {
|
|
|
149
193
|
* Callback when user submits the pre-chat form
|
|
150
194
|
*/
|
|
151
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;
|
|
152
212
|
}
|
|
153
213
|
/**
|
|
154
214
|
* Chat component that handles:
|
|
@@ -156,7 +216,7 @@ interface ChatWidgetWrapperProps {
|
|
|
156
216
|
* - Pre-chat form for collecting user information
|
|
157
217
|
* - Session persistence in localStorage
|
|
158
218
|
*/
|
|
159
|
-
declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
|
|
219
|
+
declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, state, defaultState, onStateChange, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
|
|
160
220
|
|
|
161
221
|
interface ChatHeaderProps {
|
|
162
222
|
agent?: IUser;
|
|
@@ -215,11 +275,19 @@ interface PreChatFormProps {
|
|
|
215
275
|
className?: string;
|
|
216
276
|
initialName?: string;
|
|
217
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;
|
|
218
286
|
}
|
|
219
287
|
/**
|
|
220
288
|
* PreChatForm component for collecting user information before starting chat
|
|
221
289
|
*/
|
|
222
|
-
declare function PreChatForm({ onSubmit, className, initialName, initialEmail, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
|
|
290
|
+
declare function PreChatForm({ onSubmit, className, initialName, initialEmail, onMinimize, onClose, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
|
|
223
291
|
|
|
224
292
|
interface UseWebSocketReturn {
|
|
225
293
|
isConnected: boolean;
|
|
@@ -266,4 +334,4 @@ declare function fetchMessages(baseUrl: string, params: FetchMessagesParams, hea
|
|
|
266
334
|
nextPageToken: string | undefined;
|
|
267
335
|
}>;
|
|
268
336
|
|
|
269
|
-
export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, 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, fetchMessages, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
PreChatForm: () => PreChatForm,
|
|
40
40
|
TypingIndicator: () => TypingIndicator,
|
|
41
41
|
fetchMessages: () => fetchMessages,
|
|
42
|
+
useChatWidgetState: () => useChatWidgetState,
|
|
42
43
|
useFileUpload: () => useFileUpload,
|
|
43
44
|
useMessages: () => useMessages,
|
|
44
45
|
useTypingIndicator: () => useTypingIndicator,
|
|
@@ -1136,10 +1137,10 @@ function ChatWidget({
|
|
|
1136
1137
|
config,
|
|
1137
1138
|
className = "",
|
|
1138
1139
|
variant = "popover",
|
|
1139
|
-
externalWebSocket
|
|
1140
|
+
externalWebSocket,
|
|
1141
|
+
onMinimize,
|
|
1142
|
+
onClose
|
|
1140
1143
|
}) {
|
|
1141
|
-
const [isMinimized, setIsMinimized] = (0, import_react8.useState)(false);
|
|
1142
|
-
const [isClosed, setIsClosed] = (0, import_react8.useState)(false);
|
|
1143
1144
|
const isFullPage = variant === "fullPage";
|
|
1144
1145
|
const websocket = useWebSocket(config, externalWebSocket);
|
|
1145
1146
|
const { messages, addMessage, isLoading, loadMore, hasMore, isLoadingMore } = useMessages(
|
|
@@ -1191,28 +1192,6 @@ function ChatWidget({
|
|
|
1191
1192
|
config.toast?.error(websocket.error.message || "An error occurred");
|
|
1192
1193
|
}
|
|
1193
1194
|
}, [websocket.error, config]);
|
|
1194
|
-
if (!isFullPage) {
|
|
1195
|
-
if (isClosed) {
|
|
1196
|
-
return null;
|
|
1197
|
-
}
|
|
1198
|
-
if (isMinimized) {
|
|
1199
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1200
|
-
"button",
|
|
1201
|
-
{
|
|
1202
|
-
type: "button",
|
|
1203
|
-
onClick: () => setIsMinimized(false),
|
|
1204
|
-
className: "h-14 w-14 rounded-full bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg hover:shadow-xl transition-all flex items-center justify-center relative",
|
|
1205
|
-
"aria-label": "Open chat",
|
|
1206
|
-
children: [
|
|
1207
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-2xl", children: "\u{1F4AC}" }),
|
|
1208
|
-
messages.some(
|
|
1209
|
-
(msg) => msg.senderId !== config.currentUser.email && msg.status !== "read"
|
|
1210
|
-
) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center", children: "!" })
|
|
1211
|
-
]
|
|
1212
|
-
}
|
|
1213
|
-
) });
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
1195
|
const containerClasses = isFullPage ? `flex flex-col bg-white dark:bg-gray-900 h-full ${className}` : `fixed bottom-4 right-4 z-50 flex flex-col bg-white dark:bg-gray-900 rounded-lg shadow-2xl overflow-hidden ${className}`;
|
|
1217
1196
|
const containerStyle = isFullPage ? void 0 : {
|
|
1218
1197
|
width: "400px",
|
|
@@ -1229,8 +1208,8 @@ function ChatWidget({
|
|
|
1229
1208
|
type: "agent",
|
|
1230
1209
|
status: websocket.isConnected ? "online" : "offline"
|
|
1231
1210
|
} : void 0,
|
|
1232
|
-
onMinimize
|
|
1233
|
-
onClose
|
|
1211
|
+
onMinimize,
|
|
1212
|
+
onClose
|
|
1234
1213
|
}
|
|
1235
1214
|
),
|
|
1236
1215
|
!websocket.isConnected && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "bg-yellow-50 dark:bg-yellow-900/30 border-b border-yellow-200 dark:border-yellow-800 px-4 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
@@ -1280,7 +1259,7 @@ function ChatWidget({
|
|
|
1280
1259
|
}
|
|
1281
1260
|
|
|
1282
1261
|
// src/components/Chat.tsx
|
|
1283
|
-
var
|
|
1262
|
+
var import_react11 = require("react");
|
|
1284
1263
|
|
|
1285
1264
|
// src/components/PreChatForm.tsx
|
|
1286
1265
|
var import_react9 = require("react");
|
|
@@ -1289,7 +1268,9 @@ function PreChatForm({
|
|
|
1289
1268
|
onSubmit,
|
|
1290
1269
|
className = "",
|
|
1291
1270
|
initialName = "",
|
|
1292
|
-
initialEmail = ""
|
|
1271
|
+
initialEmail = "",
|
|
1272
|
+
onMinimize,
|
|
1273
|
+
onClose
|
|
1293
1274
|
}) {
|
|
1294
1275
|
const [name, setName] = (0, import_react9.useState)(initialName);
|
|
1295
1276
|
const [email, setEmail] = (0, import_react9.useState)(initialEmail);
|
|
@@ -1336,10 +1317,74 @@ function PreChatForm({
|
|
|
1336
1317
|
maxHeight: "calc(100vh - 2rem)"
|
|
1337
1318
|
},
|
|
1338
1319
|
children: [
|
|
1339
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1340
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1341
|
-
|
|
1342
|
-
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between", children: [
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1", children: [
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-lg font-semibold", children: "Start a Conversation" }),
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-blue-100 mt-1", children: "Please provide your details to continue" })
|
|
1324
|
+
] }),
|
|
1325
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-2 ml-2", children: [
|
|
1326
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1327
|
+
"button",
|
|
1328
|
+
{
|
|
1329
|
+
type: "button",
|
|
1330
|
+
onClick: onMinimize,
|
|
1331
|
+
className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
|
|
1332
|
+
"aria-label": "Minimize chat",
|
|
1333
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1334
|
+
"svg",
|
|
1335
|
+
{
|
|
1336
|
+
className: "w-5 h-5",
|
|
1337
|
+
fill: "none",
|
|
1338
|
+
stroke: "currentColor",
|
|
1339
|
+
viewBox: "0 0 24 24",
|
|
1340
|
+
children: [
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("title", { children: "Minimize" }),
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1343
|
+
"path",
|
|
1344
|
+
{
|
|
1345
|
+
strokeLinecap: "round",
|
|
1346
|
+
strokeLinejoin: "round",
|
|
1347
|
+
strokeWidth: 2,
|
|
1348
|
+
d: "M20 12H4"
|
|
1349
|
+
}
|
|
1350
|
+
)
|
|
1351
|
+
]
|
|
1352
|
+
}
|
|
1353
|
+
)
|
|
1354
|
+
}
|
|
1355
|
+
),
|
|
1356
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1357
|
+
"button",
|
|
1358
|
+
{
|
|
1359
|
+
type: "button",
|
|
1360
|
+
onClick: onClose,
|
|
1361
|
+
className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
|
|
1362
|
+
"aria-label": "Close chat",
|
|
1363
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1364
|
+
"svg",
|
|
1365
|
+
{
|
|
1366
|
+
className: "w-5 h-5",
|
|
1367
|
+
fill: "none",
|
|
1368
|
+
stroke: "currentColor",
|
|
1369
|
+
viewBox: "0 0 24 24",
|
|
1370
|
+
children: [
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("title", { children: "Close" }),
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1373
|
+
"path",
|
|
1374
|
+
{
|
|
1375
|
+
strokeLinecap: "round",
|
|
1376
|
+
strokeLinejoin: "round",
|
|
1377
|
+
strokeWidth: 2,
|
|
1378
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
1379
|
+
}
|
|
1380
|
+
)
|
|
1381
|
+
]
|
|
1382
|
+
}
|
|
1383
|
+
)
|
|
1384
|
+
}
|
|
1385
|
+
)
|
|
1386
|
+
] })
|
|
1387
|
+
] }) }),
|
|
1343
1388
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "p-6 space-y-5", children: [
|
|
1344
1389
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1345
1390
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
@@ -1459,6 +1504,32 @@ function PreChatForm({
|
|
|
1459
1504
|
);
|
|
1460
1505
|
}
|
|
1461
1506
|
|
|
1507
|
+
// src/hooks/useChatWidgetState.ts
|
|
1508
|
+
var import_react10 = require("react");
|
|
1509
|
+
function useChatWidgetState({
|
|
1510
|
+
state: controlledState,
|
|
1511
|
+
defaultState = "minimized",
|
|
1512
|
+
onStateChange
|
|
1513
|
+
}) {
|
|
1514
|
+
const [uncontrolledState, setUncontrolledState] = (0, import_react10.useState)(defaultState);
|
|
1515
|
+
const isControlled = controlledState !== void 0 && controlledState !== "undefined";
|
|
1516
|
+
const currentState = isControlled ? controlledState : uncontrolledState;
|
|
1517
|
+
const setState = (0, import_react10.useCallback)(
|
|
1518
|
+
(newValue) => {
|
|
1519
|
+
if (!isControlled) {
|
|
1520
|
+
setUncontrolledState(newValue);
|
|
1521
|
+
}
|
|
1522
|
+
onStateChange?.(newValue);
|
|
1523
|
+
},
|
|
1524
|
+
[isControlled, onStateChange]
|
|
1525
|
+
);
|
|
1526
|
+
return {
|
|
1527
|
+
currentState,
|
|
1528
|
+
setState,
|
|
1529
|
+
isControlled
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1462
1533
|
// src/components/Chat.tsx
|
|
1463
1534
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1464
1535
|
function generateSessionId() {
|
|
@@ -1471,12 +1542,20 @@ function Chat({
|
|
|
1471
1542
|
config,
|
|
1472
1543
|
className = "",
|
|
1473
1544
|
storageKeyPrefix = "xcelsior_chat",
|
|
1474
|
-
onPreChatSubmit
|
|
1545
|
+
onPreChatSubmit,
|
|
1546
|
+
state,
|
|
1547
|
+
defaultState = "minimized",
|
|
1548
|
+
onStateChange
|
|
1475
1549
|
}) {
|
|
1476
|
-
const [userInfo, setUserInfo] = (0,
|
|
1477
|
-
const [conversationId, setConversationId] = (0,
|
|
1478
|
-
const [isLoading, setIsLoading] = (0,
|
|
1479
|
-
|
|
1550
|
+
const [userInfo, setUserInfo] = (0, import_react11.useState)(null);
|
|
1551
|
+
const [conversationId, setConversationId] = (0, import_react11.useState)("");
|
|
1552
|
+
const [isLoading, setIsLoading] = (0, import_react11.useState)(true);
|
|
1553
|
+
const { currentState, setState } = useChatWidgetState({
|
|
1554
|
+
state,
|
|
1555
|
+
defaultState,
|
|
1556
|
+
onStateChange
|
|
1557
|
+
});
|
|
1558
|
+
(0, import_react11.useEffect)(() => {
|
|
1480
1559
|
const initializeSession = () => {
|
|
1481
1560
|
try {
|
|
1482
1561
|
if (config.currentUser?.email && config.currentUser?.name) {
|
|
@@ -1531,7 +1610,7 @@ function Chat({
|
|
|
1531
1610
|
};
|
|
1532
1611
|
initializeSession();
|
|
1533
1612
|
}, [config, storageKeyPrefix]);
|
|
1534
|
-
const handlePreChatSubmit = (0,
|
|
1613
|
+
const handlePreChatSubmit = (0, import_react11.useCallback)(
|
|
1535
1614
|
(name, email) => {
|
|
1536
1615
|
const convId = conversationId || generateSessionId();
|
|
1537
1616
|
const user = {
|
|
@@ -1560,6 +1639,21 @@ function Chat({
|
|
|
1560
1639
|
if (isLoading) {
|
|
1561
1640
|
return null;
|
|
1562
1641
|
}
|
|
1642
|
+
if (currentState === "closed") {
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
if (currentState === "minimized") {
|
|
1646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1647
|
+
"button",
|
|
1648
|
+
{
|
|
1649
|
+
type: "button",
|
|
1650
|
+
onClick: () => setState("open"),
|
|
1651
|
+
className: "h-14 w-14 rounded-full bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg hover:shadow-xl transition-all flex items-center justify-center relative",
|
|
1652
|
+
"aria-label": "Open chat",
|
|
1653
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-2xl", children: "\u{1F4AC}" })
|
|
1654
|
+
}
|
|
1655
|
+
) });
|
|
1656
|
+
}
|
|
1563
1657
|
if (!userInfo || !userInfo.email || !userInfo.name) {
|
|
1564
1658
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1565
1659
|
PreChatForm,
|
|
@@ -1567,7 +1661,9 @@ function Chat({
|
|
|
1567
1661
|
onSubmit: handlePreChatSubmit,
|
|
1568
1662
|
className,
|
|
1569
1663
|
initialName: config.currentUser?.name,
|
|
1570
|
-
initialEmail: config.currentUser?.email
|
|
1664
|
+
initialEmail: config.currentUser?.email,
|
|
1665
|
+
onClose: () => setState("closed"),
|
|
1666
|
+
onMinimize: () => setState("minimized")
|
|
1571
1667
|
}
|
|
1572
1668
|
);
|
|
1573
1669
|
}
|
|
@@ -1576,7 +1672,15 @@ function Chat({
|
|
|
1576
1672
|
conversationId,
|
|
1577
1673
|
currentUser: userInfo
|
|
1578
1674
|
};
|
|
1579
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1675
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1676
|
+
ChatWidget,
|
|
1677
|
+
{
|
|
1678
|
+
config: fullConfig,
|
|
1679
|
+
className,
|
|
1680
|
+
onClose: () => setState("closed"),
|
|
1681
|
+
onMinimize: () => setState("minimized")
|
|
1682
|
+
}
|
|
1683
|
+
);
|
|
1580
1684
|
}
|
|
1581
1685
|
|
|
1582
1686
|
// src/components/TypingIndicator.tsx
|
|
@@ -1617,6 +1721,7 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
1617
1721
|
PreChatForm,
|
|
1618
1722
|
TypingIndicator,
|
|
1619
1723
|
fetchMessages,
|
|
1724
|
+
useChatWidgetState,
|
|
1620
1725
|
useFileUpload,
|
|
1621
1726
|
useMessages,
|
|
1622
1727
|
useTypingIndicator,
|