@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/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/components/ChatWidget.tsx
2
- import { useCallback as useCallback4, useEffect as useEffect6, useState as useState6 } from "react";
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("div", { className: "prose prose-sm dark:prose-invert max-w-none", children: /* @__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: () => setIsMinimized(true),
1185
- onClose: () => setIsClosed(true)
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 useCallback5, useEffect as useEffect7, useState as useState8 } from "react";
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 useState7 } from "react";
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] = useState7(initialName);
1247
- const [email, setEmail] = useState7(initialEmail);
1248
- const [errors, setErrors] = useState7({});
1249
- const [isSubmitting, setIsSubmitting] = useState7(false);
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__ */ jsxs6("div", { className: "bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-4", children: [
1292
- /* @__PURE__ */ jsx6("h2", { className: "text-lg font-semibold", children: "Start a Conversation" }),
1293
- /* @__PURE__ */ jsx6("p", { className: "text-sm text-blue-100 mt-1", children: "Please provide your details to continue" })
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 = useCallback5(
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(ChatWidget, { config: fullConfig, className });
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,