@getwidgets/live-chat-widget 1.1.1 → 1.1.3

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.
@@ -19752,6 +19752,7 @@
19752
19752
  };
19753
19753
  const LiveChatWidget = ({ widgetId, name: name2 = null, email = null, unique_id = null }) => {
19754
19754
  var _a, _b;
19755
+ const [showMenu, setShowMenu] = reactExports.useState(false);
19755
19756
  const [config, setConfig] = reactExports.useState(null);
19756
19757
  const [messages, setMessages] = reactExports.useState([]);
19757
19758
  const [inputMessage, setInputMessage] = reactExports.useState("");
@@ -20235,13 +20236,10 @@
20235
20236
  if (isMobile2) {
20236
20237
  return {
20237
20238
  width: "95vw",
20238
- height: "85vh",
20239
- // Changed from 60vh to 85vh for better mobile experience
20239
+ height: "85dvh",
20240
20240
  maxWidth: "95vw",
20241
- maxHeight: "95vh",
20242
- bottom: "0",
20243
- right: "0",
20244
- borderRadius: "12px 12px 0 0"
20241
+ maxHeight: "calc(100dvh - 24px)",
20242
+ borderRadius: appearanceConfig.border_radius || "12px"
20245
20243
  };
20246
20244
  }
20247
20245
  return {
@@ -20255,19 +20253,16 @@
20255
20253
  const position2 = appearanceConfig.position || "bottom-right";
20256
20254
  const isMobile2 = windowWidth < 768;
20257
20255
  const baseStyles = { position: "fixed", zIndex: 9999 };
20258
- if (isMobile2) {
20259
- return { ...baseStyles, left: 0, right: 0, bottom: 0 };
20260
- }
20261
- const desktopSpacing = "20px";
20256
+ const edgeSpacing = isMobile2 ? "12px" : "20px";
20262
20257
  switch (position2) {
20263
20258
  case "bottom-left":
20264
- return { ...baseStyles, left: desktopSpacing, bottom: desktopSpacing };
20259
+ return { ...baseStyles, left: edgeSpacing, bottom: edgeSpacing };
20265
20260
  case "top-left":
20266
- return { ...baseStyles, left: desktopSpacing, top: desktopSpacing };
20261
+ return { ...baseStyles, left: edgeSpacing, top: edgeSpacing };
20267
20262
  case "top-right":
20268
- return { ...baseStyles, right: desktopSpacing, top: desktopSpacing };
20263
+ return { ...baseStyles, right: edgeSpacing, top: edgeSpacing };
20269
20264
  default:
20270
- return { ...baseStyles, right: desktopSpacing, bottom: desktopSpacing };
20265
+ return { ...baseStyles, right: edgeSpacing, bottom: edgeSpacing };
20271
20266
  }
20272
20267
  };
20273
20268
  const responsiveDimensions = getResponsiveDimensions();
@@ -20313,7 +20308,12 @@
20313
20308
  setShowContactFields(false);
20314
20309
  }
20315
20310
  }, [inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email, authToken, isOpen]);
20316
- const isContactValid = !(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) || (!showContactFields ? true : contactName.trim() && contactEmail.trim());
20311
+ const isValidEmail = (value) => {
20312
+ if (!value) return false;
20313
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value).trim());
20314
+ };
20315
+ const emailIsInvalid = showContactFields && contactEmail.trim() && !isValidEmail(contactEmail);
20316
+ const isContactValid = !(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) || (!showContactFields ? true : contactName.trim() && contactEmail.trim() && isValidEmail(contactEmail));
20317
20317
  const createAndStoreAuthToken = () => {
20318
20318
  const payload = { name: contactName.trim(), email: contactEmail.trim(), unique_id };
20319
20319
  const token = buildUnsignedJwt(payload);
@@ -20328,7 +20328,8 @@
20328
20328
  if ((inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) && !authToken) {
20329
20329
  const missingName = !contactName.trim();
20330
20330
  const missingEmail = !contactEmail.trim();
20331
- if (missingName || missingEmail) {
20331
+ const invalidEmail = !missingEmail && !isValidEmail(contactEmail);
20332
+ if (missingName || missingEmail || invalidEmail) {
20332
20333
  setShowContactErrors(true);
20333
20334
  return;
20334
20335
  }
@@ -20337,7 +20338,45 @@
20337
20338
  }
20338
20339
  await performSend();
20339
20340
  };
20341
+ reactExports.useEffect(() => {
20342
+ if (!showMenu) return;
20343
+ const handler = (e) => {
20344
+ if (!e.target.closest(".livechat-menu-trigger") && !e.target.closest(".livechat-menu-popup")) {
20345
+ setShowMenu(false);
20346
+ }
20347
+ };
20348
+ document.addEventListener("mousedown", handler);
20349
+ return () => document.removeEventListener("mousedown", handler);
20350
+ }, [showMenu]);
20340
20351
  if (!config) return null;
20352
+ const handleStartNewChat = () => {
20353
+ try {
20354
+ const newSessionId = v4();
20355
+ if (wsRef.current) {
20356
+ try {
20357
+ wsRef.current.close();
20358
+ } catch (e) {
20359
+ }
20360
+ wsRef.current = null;
20361
+ }
20362
+ localStorage.setItem("live-chat-widget/session-id", newSessionId);
20363
+ setSessionId(newSessionId);
20364
+ setHasCheckedSession(false);
20365
+ setIsConnecting(false);
20366
+ setLocalLoading(false);
20367
+ setAuthToken(null);
20368
+ setMessages([]);
20369
+ setLocalMessages([]);
20370
+ setInputMessage("");
20371
+ setIsSessionInitialized(false);
20372
+ setShowMenu(false);
20373
+ } catch (e) {
20374
+ }
20375
+ };
20376
+ const handleEndChat = () => {
20377
+ handleStartNewChat();
20378
+ };
20379
+ const MenuDotsIcon = ({ color: color2 = "#fff", size = 22 }) => /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "5", r: "2", fill: color2 }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "2", fill: color2 }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "19", r: "2", fill: color2 }));
20341
20380
  return /* @__PURE__ */ React.createElement("div", { style: getPositionStyles() }, !isOpen && showNotificationPreview && /* @__PURE__ */ React.createElement(
20342
20381
  "div",
20343
20382
  {
@@ -20359,7 +20398,8 @@
20359
20398
  textAlign: "left",
20360
20399
  minWidth: isMobile ? "90vw" : "280px",
20361
20400
  maxWidth: isMobile ? "90vw" : "400px",
20362
- margin: isMobile ? "0 auto" : "0"
20401
+ margin: isMobile ? "0 auto" : "0",
20402
+ fontFamily: headerConfig.font_family || "'Inter', sans-serif"
20363
20403
  }
20364
20404
  },
20365
20405
  unreadCount > 0 && /* @__PURE__ */ React.createElement(
@@ -20414,7 +20454,7 @@
20414
20454
  },
20415
20455
  /* @__PURE__ */ React.createElement("path", { d: "M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z" })
20416
20456
  )
20417
- ), /* @__PURE__ */ React.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React.createElement("h3", { className: "font-semibold text-sm" }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "text-xs opacity-70" }, "Just now"))), /* @__PURE__ */ React.createElement(
20457
+ ), /* @__PURE__ */ React.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React.createElement("h3", { className: "font-semibold text-sm", style: headerConfig.font_color ? { color: headerConfig.font_color } : { color: "black" } }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "text-xs opacity-70", style: headerConfig.font_color ? { color: headerConfig.font_color } : { color: "black" } }, "Just now"))), /* @__PURE__ */ React.createElement(
20418
20458
  "button",
20419
20459
  {
20420
20460
  onClick: handleCloseNotification,
@@ -20423,7 +20463,7 @@
20423
20463
  },
20424
20464
  "×"
20425
20465
  )),
20426
- /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-relaxed text-gray-200", style: { fontSize: isMobile ? "13px" : "14px" } }, (latestMessage == null ? void 0 : latestMessage.content) || (inputAreaConfig == null ? void 0 : inputAreaConfig.first_ai_message) || headerConfig.subtitle || "Connect with visitors")
20466
+ /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-relaxed text-gray-200", style: { fontSize: isMobile ? "13px" : "14px", color: headerConfig.font_color ? headerConfig.font_color : "#000000" } }, (latestMessage == null ? void 0 : latestMessage.content) || (inputAreaConfig == null ? void 0 : inputAreaConfig.welcome_message) || (inputAreaConfig == null ? void 0 : inputAreaConfig.first_ai_message) || headerConfig.subtitle || "Connect with visitors")
20427
20467
  ), !isOpen && !showNotificationPreview && /* @__PURE__ */ React.createElement(
20428
20468
  "button",
20429
20469
  {
@@ -20445,8 +20485,8 @@
20445
20485
  src: headerConfig.logo_url,
20446
20486
  alt: "logo",
20447
20487
  style: {
20448
- width: isMobile ? 24 : 28,
20449
- height: isMobile ? 24 : 28
20488
+ width: "auto",
20489
+ height: `${headerConfig.logo_size || 32}px`
20450
20490
  }
20451
20491
  }
20452
20492
  ) : /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", style: { width: isMobile ? 18 : 20, height: isMobile ? 18 : 20 } }, /* @__PURE__ */ React.createElement("path", { d: "M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z" })),
@@ -20476,6 +20516,7 @@
20476
20516
  maxHeight: responsiveDimensions.maxHeight,
20477
20517
  backgroundColor: appearanceConfig.background_color || "#F9FAFB",
20478
20518
  borderRadius: isMobile ? responsiveDimensions.borderRadius : appearanceConfig.border_radius || "16px",
20519
+ fontFamily: headerConfig.font_family || "'Inter', sans-serif",
20479
20520
  ...isMobile ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 } : {}
20480
20521
  }
20481
20522
  },
@@ -20490,6 +20531,69 @@
20490
20531
  minHeight: "auto"
20491
20532
  }
20492
20533
  },
20534
+ /* @__PURE__ */ React.createElement(
20535
+ "button",
20536
+ {
20537
+ className: "livechat-menu-trigger absolute left-3 top-1/2 transform -translate-y-1/2 hover:opacity-80 transition-opacity",
20538
+ style: {
20539
+ background: "none",
20540
+ border: "none",
20541
+ cursor: "pointer",
20542
+ width: isMobile ? "40px" : "36px",
20543
+ height: isMobile ? "40px" : "36px",
20544
+ display: "flex",
20545
+ alignItems: "center",
20546
+ justifyContent: "center",
20547
+ padding: 0,
20548
+ zIndex: 20
20549
+ },
20550
+ onClick: (e) => {
20551
+ e.stopPropagation();
20552
+ setShowMenu((v2) => !v2);
20553
+ },
20554
+ "aria-label": "Open menu"
20555
+ },
20556
+ /* @__PURE__ */ React.createElement(MenuDotsIcon, { color: getContrastColor(headerConfig.background_color || "#111827"), size: isMobile ? 22 : 22 })
20557
+ ),
20558
+ showMenu && /* @__PURE__ */ React.createElement(
20559
+ "div",
20560
+ {
20561
+ className: "livechat-menu-popup absolute left-3 top-12 bg-white rounded shadow-lg border",
20562
+ style: {
20563
+ minWidth: isMobile ? "132px" : "120px",
20564
+ top: isMobile ? "calc(50% + 18px)" : "calc(50% + 16px)",
20565
+ left: isMobile ? "8px" : "12px",
20566
+ zIndex: 30,
20567
+ boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
20568
+ border: "1px solid #e5e7eb",
20569
+ padding: "6px 0",
20570
+ color: "#111827",
20571
+ fontSize: isMobile ? "16px" : "15px",
20572
+ fontWeight: 500
20573
+ }
20574
+ },
20575
+ /* @__PURE__ */ React.createElement(
20576
+ "button",
20577
+ {
20578
+ className: "transition-all duration-200 hover:bg-red-50",
20579
+ style: {
20580
+ background: "none",
20581
+ border: "none",
20582
+ width: "100%",
20583
+ cursor: "pointer",
20584
+ // padding: isMobile ? '8px 12px' : '6px 12px',
20585
+ minHeight: "auto",
20586
+ fontSize: isMobile ? "14px" : "13px",
20587
+ color: "#DC2626",
20588
+ fontWeight: "500",
20589
+ borderRadius: "6px"
20590
+ // margin: '4px 0'
20591
+ },
20592
+ onClick: handleEndChat
20593
+ },
20594
+ "End Chat"
20595
+ )
20596
+ ),
20493
20597
  /* @__PURE__ */ React.createElement(
20494
20598
  "button",
20495
20599
  {
@@ -20516,7 +20620,8 @@
20516
20620
  {
20517
20621
  src: headerConfig.logo_url,
20518
20622
  alt: "logo",
20519
- className: "w-auto h-8 mx-auto mb-2"
20623
+ className: "w-auto mx-auto mb-2",
20624
+ style: { height: `${headerConfig.logo_size || 32}px` }
20520
20625
  }
20521
20626
  ), !headerConfig.hide_title && /* @__PURE__ */ React.createElement("h2", { className: "font-semibold leading-tight", style: { fontSize: isMobile ? "14px" : "16px", margin: "0" } }, headerConfig.title || "Live Chat"), /* @__PURE__ */ React.createElement("p", { className: "opacity-80 leading-tight", style: { fontSize: isMobile ? "11px" : "13px", margin: "2px 0 0 0" } }, headerConfig.subtitle || "Connect with visitors"))
20522
20627
  ),
@@ -20554,7 +20659,7 @@
20554
20659
  wordBreak: "break-word"
20555
20660
  }
20556
20661
  },
20557
- inputAreaConfig.first_ai_message || "Hello! How can I assist you today?"
20662
+ inputAreaConfig.welcome_message || inputAreaConfig.first_ai_message || "Hello! How can I assist you today?"
20558
20663
  )) : combinedMessages.map((message) => {
20559
20664
  const timeText = getMessageTime(message);
20560
20665
  return /* @__PURE__ */ React.createElement("div", { key: message.id, className: `flex flex-col ${message.role === "user" ? "items-end" : "items-start"}` }, /* @__PURE__ */ React.createElement(
@@ -20663,7 +20768,7 @@
20663
20768
  })()
20664
20769
  )),
20665
20770
  showContactFields && /* @__PURE__ */ React.createElement("div", { className: "p-3 border rounded-lg mt-2", style: {
20666
- borderColor: showContactErrors && (!contactName.trim() || !contactEmail.trim()) ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20771
+ borderColor: showContactErrors && (!contactName.trim() || !contactEmail.trim() || !isValidEmail(contactEmail)) || emailIsInvalid ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20667
20772
  backgroundColor: inputAreaConfig.background_color || "#FFF"
20668
20773
  } }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold mb-2", style: { color: inputAreaConfig.text_color || "#111827" } }, "How should we reach you?"), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(
20669
20774
  "input",
@@ -20695,13 +20800,13 @@
20695
20800
  },
20696
20801
  className: "w-full px-3 py-1.5 border rounded-md",
20697
20802
  style: {
20698
- borderColor: showContactErrors && !contactEmail.trim() ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20803
+ borderColor: showContactErrors && !contactEmail.trim() || emailIsInvalid ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20699
20804
  color: inputAreaConfig.text_color || "#111827",
20700
20805
  fontSize: "13px",
20701
20806
  backgroundColor: inputAreaConfig.background_color || "#FFF"
20702
20807
  }
20703
20808
  }
20704
- ), showContactErrors && (!contactName.trim() || !contactEmail.trim()) && /* @__PURE__ */ React.createElement("div", { className: "text-xs", style: { color: "#EF4444" } }, `${!contactName.trim() ? "Name is required. " : ""}${!contactEmail.trim() ? "Email is required." : ""}`)))
20809
+ ), showContactErrors && (!contactName.trim() || !contactEmail.trim() || !isValidEmail(contactEmail)) && /* @__PURE__ */ React.createElement("div", { className: "text-xs", style: { color: "#EF4444" } }, `${!contactName.trim() ? "Name is required. " : ""}${!contactEmail.trim() ? "Email is required. " : ""}${contactEmail.trim() && !isValidEmail(contactEmail) ? "Enter a valid email address." : ""}`)))
20705
20810
  ),
20706
20811
  inputAreaConfig.show_branding !== false && /* @__PURE__ */ React.createElement(
20707
20812
  "div",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getwidgets/live-chat-widget",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "main": "dist/live-chat-widget.umd.js",
5
5
  "unpkg": "dist/live-chat-widget.umd.js",
6
6
  "type": "module",