@xcelsior/ui-chat 1.0.5 → 1.0.7
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 +13 -0
- package/README.md +82 -0
- package/dist/index.d.mts +72 -4
- package/dist/index.d.ts +72 -4
- package/dist/index.js +148 -43
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +149 -45
- 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/MessageItem.tsx +0 -2
- package/src/components/PreChatForm.tsx +65 -4
- package/src/hooks/useChatWidgetState.ts +68 -0
- package/src/index.tsx +8 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/ChatWidget.tsx
|
|
2
|
-
import { useCallback as useCallback4, useEffect as useEffect6
|
|
2
|
+
import { useCallback as useCallback4, useEffect as useEffect6 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/hooks/useWebSocket.ts
|
|
5
5
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
@@ -571,7 +571,7 @@ function MessageItem({
|
|
|
571
571
|
{
|
|
572
572
|
className: `rounded-2xl px-4 py-2 ${isOwnMessage ? "bg-blue-600 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"}`,
|
|
573
573
|
children: [
|
|
574
|
-
message.messageType === "text" && /* @__PURE__ */ jsx2(
|
|
574
|
+
message.messageType === "text" && /* @__PURE__ */ jsx2(
|
|
575
575
|
ReactMarkdown,
|
|
576
576
|
{
|
|
577
577
|
components: {
|
|
@@ -600,7 +600,7 @@ function MessageItem({
|
|
|
600
600
|
},
|
|
601
601
|
children: message.content
|
|
602
602
|
}
|
|
603
|
-
)
|
|
603
|
+
),
|
|
604
604
|
message.messageType === "image" && /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(
|
|
605
605
|
"img",
|
|
606
606
|
{
|
|
@@ -1088,10 +1088,10 @@ function ChatWidget({
|
|
|
1088
1088
|
config,
|
|
1089
1089
|
className = "",
|
|
1090
1090
|
variant = "popover",
|
|
1091
|
-
externalWebSocket
|
|
1091
|
+
externalWebSocket,
|
|
1092
|
+
onMinimize,
|
|
1093
|
+
onClose
|
|
1092
1094
|
}) {
|
|
1093
|
-
const [isMinimized, setIsMinimized] = useState6(false);
|
|
1094
|
-
const [isClosed, setIsClosed] = useState6(false);
|
|
1095
1095
|
const isFullPage = variant === "fullPage";
|
|
1096
1096
|
const websocket = useWebSocket(config, externalWebSocket);
|
|
1097
1097
|
const { messages, addMessage, isLoading, loadMore, hasMore, isLoadingMore } = useMessages(
|
|
@@ -1143,28 +1143,6 @@ function ChatWidget({
|
|
|
1143
1143
|
config.toast?.error(websocket.error.message || "An error occurred");
|
|
1144
1144
|
}
|
|
1145
1145
|
}, [websocket.error, config]);
|
|
1146
|
-
if (!isFullPage) {
|
|
1147
|
-
if (isClosed) {
|
|
1148
|
-
return null;
|
|
1149
|
-
}
|
|
1150
|
-
if (isMinimized) {
|
|
1151
|
-
return /* @__PURE__ */ jsx5("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ jsxs5(
|
|
1152
|
-
"button",
|
|
1153
|
-
{
|
|
1154
|
-
type: "button",
|
|
1155
|
-
onClick: () => setIsMinimized(false),
|
|
1156
|
-
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",
|
|
1157
|
-
"aria-label": "Open chat",
|
|
1158
|
-
children: [
|
|
1159
|
-
/* @__PURE__ */ jsx5("span", { className: "text-2xl", children: "\u{1F4AC}" }),
|
|
1160
|
-
messages.some(
|
|
1161
|
-
(msg) => msg.senderId !== config.currentUser.email && msg.status !== "read"
|
|
1162
|
-
) && /* @__PURE__ */ jsx5("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: "!" })
|
|
1163
|
-
]
|
|
1164
|
-
}
|
|
1165
|
-
) });
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
1146
|
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}`;
|
|
1169
1147
|
const containerStyle = isFullPage ? void 0 : {
|
|
1170
1148
|
width: "400px",
|
|
@@ -1181,8 +1159,8 @@ function ChatWidget({
|
|
|
1181
1159
|
type: "agent",
|
|
1182
1160
|
status: websocket.isConnected ? "online" : "offline"
|
|
1183
1161
|
} : void 0,
|
|
1184
|
-
onMinimize
|
|
1185
|
-
onClose
|
|
1162
|
+
onMinimize,
|
|
1163
|
+
onClose
|
|
1186
1164
|
}
|
|
1187
1165
|
),
|
|
1188
1166
|
!websocket.isConnected && /* @__PURE__ */ jsx5("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__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
@@ -1232,21 +1210,23 @@ function ChatWidget({
|
|
|
1232
1210
|
}
|
|
1233
1211
|
|
|
1234
1212
|
// src/components/Chat.tsx
|
|
1235
|
-
import { useCallback as
|
|
1213
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useState as useState8 } from "react";
|
|
1236
1214
|
|
|
1237
1215
|
// src/components/PreChatForm.tsx
|
|
1238
|
-
import { useState as
|
|
1216
|
+
import { useState as useState6 } from "react";
|
|
1239
1217
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1240
1218
|
function PreChatForm({
|
|
1241
1219
|
onSubmit,
|
|
1242
1220
|
className = "",
|
|
1243
1221
|
initialName = "",
|
|
1244
|
-
initialEmail = ""
|
|
1222
|
+
initialEmail = "",
|
|
1223
|
+
onMinimize,
|
|
1224
|
+
onClose
|
|
1245
1225
|
}) {
|
|
1246
|
-
const [name, setName] =
|
|
1247
|
-
const [email, setEmail] =
|
|
1248
|
-
const [errors, setErrors] =
|
|
1249
|
-
const [isSubmitting, setIsSubmitting] =
|
|
1226
|
+
const [name, setName] = useState6(initialName);
|
|
1227
|
+
const [email, setEmail] = useState6(initialEmail);
|
|
1228
|
+
const [errors, setErrors] = useState6({});
|
|
1229
|
+
const [isSubmitting, setIsSubmitting] = useState6(false);
|
|
1250
1230
|
const validateForm = () => {
|
|
1251
1231
|
const newErrors = {};
|
|
1252
1232
|
if (!name.trim()) {
|
|
@@ -1288,10 +1268,74 @@ function PreChatForm({
|
|
|
1288
1268
|
maxHeight: "calc(100vh - 2rem)"
|
|
1289
1269
|
},
|
|
1290
1270
|
children: [
|
|
1291
|
-
/* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
1293
|
-
|
|
1294
|
-
|
|
1271
|
+
/* @__PURE__ */ jsx6("div", { className: "bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-4", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-start justify-between", children: [
|
|
1272
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex-1", children: [
|
|
1273
|
+
/* @__PURE__ */ jsx6("h2", { className: "text-lg font-semibold", children: "Start a Conversation" }),
|
|
1274
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm text-blue-100 mt-1", children: "Please provide your details to continue" })
|
|
1275
|
+
] }),
|
|
1276
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex gap-2 ml-2", children: [
|
|
1277
|
+
/* @__PURE__ */ jsx6(
|
|
1278
|
+
"button",
|
|
1279
|
+
{
|
|
1280
|
+
type: "button",
|
|
1281
|
+
onClick: onMinimize,
|
|
1282
|
+
className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
|
|
1283
|
+
"aria-label": "Minimize chat",
|
|
1284
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1285
|
+
"svg",
|
|
1286
|
+
{
|
|
1287
|
+
className: "w-5 h-5",
|
|
1288
|
+
fill: "none",
|
|
1289
|
+
stroke: "currentColor",
|
|
1290
|
+
viewBox: "0 0 24 24",
|
|
1291
|
+
children: [
|
|
1292
|
+
/* @__PURE__ */ jsx6("title", { children: "Minimize" }),
|
|
1293
|
+
/* @__PURE__ */ jsx6(
|
|
1294
|
+
"path",
|
|
1295
|
+
{
|
|
1296
|
+
strokeLinecap: "round",
|
|
1297
|
+
strokeLinejoin: "round",
|
|
1298
|
+
strokeWidth: 2,
|
|
1299
|
+
d: "M20 12H4"
|
|
1300
|
+
}
|
|
1301
|
+
)
|
|
1302
|
+
]
|
|
1303
|
+
}
|
|
1304
|
+
)
|
|
1305
|
+
}
|
|
1306
|
+
),
|
|
1307
|
+
/* @__PURE__ */ jsx6(
|
|
1308
|
+
"button",
|
|
1309
|
+
{
|
|
1310
|
+
type: "button",
|
|
1311
|
+
onClick: onClose,
|
|
1312
|
+
className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
|
|
1313
|
+
"aria-label": "Close chat",
|
|
1314
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1315
|
+
"svg",
|
|
1316
|
+
{
|
|
1317
|
+
className: "w-5 h-5",
|
|
1318
|
+
fill: "none",
|
|
1319
|
+
stroke: "currentColor",
|
|
1320
|
+
viewBox: "0 0 24 24",
|
|
1321
|
+
children: [
|
|
1322
|
+
/* @__PURE__ */ jsx6("title", { children: "Close" }),
|
|
1323
|
+
/* @__PURE__ */ jsx6(
|
|
1324
|
+
"path",
|
|
1325
|
+
{
|
|
1326
|
+
strokeLinecap: "round",
|
|
1327
|
+
strokeLinejoin: "round",
|
|
1328
|
+
strokeWidth: 2,
|
|
1329
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
1330
|
+
}
|
|
1331
|
+
)
|
|
1332
|
+
]
|
|
1333
|
+
}
|
|
1334
|
+
)
|
|
1335
|
+
}
|
|
1336
|
+
)
|
|
1337
|
+
] })
|
|
1338
|
+
] }) }),
|
|
1295
1339
|
/* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "p-6 space-y-5", children: [
|
|
1296
1340
|
/* @__PURE__ */ jsxs6("div", { children: [
|
|
1297
1341
|
/* @__PURE__ */ jsxs6(
|
|
@@ -1411,6 +1455,32 @@ function PreChatForm({
|
|
|
1411
1455
|
);
|
|
1412
1456
|
}
|
|
1413
1457
|
|
|
1458
|
+
// src/hooks/useChatWidgetState.ts
|
|
1459
|
+
import { useCallback as useCallback5, useState as useState7 } from "react";
|
|
1460
|
+
function useChatWidgetState({
|
|
1461
|
+
state: controlledState,
|
|
1462
|
+
defaultState = "minimized",
|
|
1463
|
+
onStateChange
|
|
1464
|
+
}) {
|
|
1465
|
+
const [uncontrolledState, setUncontrolledState] = useState7(defaultState);
|
|
1466
|
+
const isControlled = controlledState !== void 0 && controlledState !== "undefined";
|
|
1467
|
+
const currentState = isControlled ? controlledState : uncontrolledState;
|
|
1468
|
+
const setState = useCallback5(
|
|
1469
|
+
(newValue) => {
|
|
1470
|
+
if (!isControlled) {
|
|
1471
|
+
setUncontrolledState(newValue);
|
|
1472
|
+
}
|
|
1473
|
+
onStateChange?.(newValue);
|
|
1474
|
+
},
|
|
1475
|
+
[isControlled, onStateChange]
|
|
1476
|
+
);
|
|
1477
|
+
return {
|
|
1478
|
+
currentState,
|
|
1479
|
+
setState,
|
|
1480
|
+
isControlled
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1414
1484
|
// src/components/Chat.tsx
|
|
1415
1485
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1416
1486
|
function generateSessionId() {
|
|
@@ -1423,11 +1493,19 @@ function Chat({
|
|
|
1423
1493
|
config,
|
|
1424
1494
|
className = "",
|
|
1425
1495
|
storageKeyPrefix = "xcelsior_chat",
|
|
1426
|
-
onPreChatSubmit
|
|
1496
|
+
onPreChatSubmit,
|
|
1497
|
+
state,
|
|
1498
|
+
defaultState = "minimized",
|
|
1499
|
+
onStateChange
|
|
1427
1500
|
}) {
|
|
1428
1501
|
const [userInfo, setUserInfo] = useState8(null);
|
|
1429
1502
|
const [conversationId, setConversationId] = useState8("");
|
|
1430
1503
|
const [isLoading, setIsLoading] = useState8(true);
|
|
1504
|
+
const { currentState, setState } = useChatWidgetState({
|
|
1505
|
+
state,
|
|
1506
|
+
defaultState,
|
|
1507
|
+
onStateChange
|
|
1508
|
+
});
|
|
1431
1509
|
useEffect7(() => {
|
|
1432
1510
|
const initializeSession = () => {
|
|
1433
1511
|
try {
|
|
@@ -1483,7 +1561,7 @@ function Chat({
|
|
|
1483
1561
|
};
|
|
1484
1562
|
initializeSession();
|
|
1485
1563
|
}, [config, storageKeyPrefix]);
|
|
1486
|
-
const handlePreChatSubmit =
|
|
1564
|
+
const handlePreChatSubmit = useCallback6(
|
|
1487
1565
|
(name, email) => {
|
|
1488
1566
|
const convId = conversationId || generateSessionId();
|
|
1489
1567
|
const user = {
|
|
@@ -1512,6 +1590,21 @@ function Chat({
|
|
|
1512
1590
|
if (isLoading) {
|
|
1513
1591
|
return null;
|
|
1514
1592
|
}
|
|
1593
|
+
if (currentState === "closed") {
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
if (currentState === "minimized") {
|
|
1597
|
+
return /* @__PURE__ */ jsx7("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ jsx7(
|
|
1598
|
+
"button",
|
|
1599
|
+
{
|
|
1600
|
+
type: "button",
|
|
1601
|
+
onClick: () => setState("open"),
|
|
1602
|
+
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",
|
|
1603
|
+
"aria-label": "Open chat",
|
|
1604
|
+
children: /* @__PURE__ */ jsx7("span", { className: "text-2xl", children: "\u{1F4AC}" })
|
|
1605
|
+
}
|
|
1606
|
+
) });
|
|
1607
|
+
}
|
|
1515
1608
|
if (!userInfo || !userInfo.email || !userInfo.name) {
|
|
1516
1609
|
return /* @__PURE__ */ jsx7(
|
|
1517
1610
|
PreChatForm,
|
|
@@ -1519,7 +1612,9 @@ function Chat({
|
|
|
1519
1612
|
onSubmit: handlePreChatSubmit,
|
|
1520
1613
|
className,
|
|
1521
1614
|
initialName: config.currentUser?.name,
|
|
1522
|
-
initialEmail: config.currentUser?.email
|
|
1615
|
+
initialEmail: config.currentUser?.email,
|
|
1616
|
+
onClose: () => setState("closed"),
|
|
1617
|
+
onMinimize: () => setState("minimized")
|
|
1523
1618
|
}
|
|
1524
1619
|
);
|
|
1525
1620
|
}
|
|
@@ -1528,7 +1623,15 @@ function Chat({
|
|
|
1528
1623
|
conversationId,
|
|
1529
1624
|
currentUser: userInfo
|
|
1530
1625
|
};
|
|
1531
|
-
return /* @__PURE__ */ jsx7(
|
|
1626
|
+
return /* @__PURE__ */ jsx7(
|
|
1627
|
+
ChatWidget,
|
|
1628
|
+
{
|
|
1629
|
+
config: fullConfig,
|
|
1630
|
+
className,
|
|
1631
|
+
onClose: () => setState("closed"),
|
|
1632
|
+
onMinimize: () => setState("minimized")
|
|
1633
|
+
}
|
|
1634
|
+
);
|
|
1532
1635
|
}
|
|
1533
1636
|
|
|
1534
1637
|
// src/components/TypingIndicator.tsx
|
|
@@ -1568,6 +1671,7 @@ export {
|
|
|
1568
1671
|
PreChatForm,
|
|
1569
1672
|
TypingIndicator,
|
|
1570
1673
|
fetchMessages,
|
|
1674
|
+
useChatWidgetState,
|
|
1571
1675
|
useFileUpload,
|
|
1572
1676
|
useMessages,
|
|
1573
1677
|
useTypingIndicator,
|