@xapp/chat-widget 1.104.0 → 1.105.0
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.css +1 -1
- package/dist/index.es.js +111 -26
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +111 -26
- package/dist/index.js.map +1 -1
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +5 -5
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -329,7 +329,7 @@ function buildChatWidgetChannel(props) {
|
|
|
329
329
|
// and the text on it are always a matched pair rather than one scraped
|
|
330
330
|
// value and one guess.
|
|
331
331
|
const primaryColor = (0, color_1.usableColorOr)(props.primaryColor, DEFAULT_PRIMARY);
|
|
332
|
-
return Object.assign({ type: "chat-widget",
|
|
332
|
+
return Object.assign(Object.assign({ type: "chat-widget",
|
|
333
333
|
// MCP by default. The widget runs on a public page, so homeowner mode
|
|
334
334
|
// — which is unauthenticated and carries the appId in the url — is the
|
|
335
335
|
// only one that can work there; business_owner needs a token, and a
|
|
@@ -340,7 +340,7 @@ function buildChatWidgetChannel(props) {
|
|
|
340
340
|
mcp: {
|
|
341
341
|
mode: "homeowner"
|
|
342
342
|
}
|
|
343
|
-
}, theme: buildChatTheme(primaryColor, props.secondaryColor), actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber }) }, (businessName ? { botName: businessName } : {}));
|
|
343
|
+
}, theme: buildChatTheme(primaryColor, props.secondaryColor), actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber }) }, (formWidgetKey ? { formWidgetKey } : {})), (businessName ? { botName: businessName } : {}));
|
|
344
344
|
}
|
|
345
345
|
/**
|
|
346
346
|
* The dock of buttons that replaces the plain chat bubble.
|
|
@@ -3871,6 +3871,31 @@ function useVisualViewportProperties(enabled) {
|
|
|
3871
3871
|
return trackVisualViewport();
|
|
3872
3872
|
}, [enabled]);
|
|
3873
3873
|
}
|
|
3874
|
+
/** The sessionStorage key the booking page writes the flag to. */
|
|
3875
|
+
var TEST_LEAD_STORAGE_KEY = "xa_test";
|
|
3876
|
+
/** The attribute sent with a lead submission. */
|
|
3877
|
+
var TEST_LEAD_ATTRIBUTE = "xaTest";
|
|
3878
|
+
/** True when the current browser session is a test-lead page view. */
|
|
3879
|
+
function isTestLeadSession() {
|
|
3880
|
+
try {
|
|
3881
|
+
return sessionStorage.getItem(TEST_LEAD_STORAGE_KEY) === "1";
|
|
3882
|
+
}
|
|
3883
|
+
catch (_a) {
|
|
3884
|
+
// Storage can be blocked by the browser; treat that as a real visit.
|
|
3885
|
+
return false;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
/**
|
|
3889
|
+
* Tags the attributes as a test lead when the session flag is set. A test must
|
|
3890
|
+
* never be counted as Google traffic, so any `rwg_token` is dropped as well.
|
|
3891
|
+
*/
|
|
3892
|
+
function addTestLeadAttribute(attributes) {
|
|
3893
|
+
if (isTestLeadSession()) {
|
|
3894
|
+
attributes[TEST_LEAD_ATTRIBUTE] = true;
|
|
3895
|
+
delete attributes["rwg_token"];
|
|
3896
|
+
}
|
|
3897
|
+
return attributes;
|
|
3898
|
+
}
|
|
3874
3899
|
|
|
3875
3900
|
/**
|
|
3876
3901
|
* Quicker configurable uuid
|
|
@@ -3894,6 +3919,7 @@ function uuid() {
|
|
|
3894
3919
|
var IconButton_1 = IconButton;
|
|
3895
3920
|
var RichInput_1 = RichInput;
|
|
3896
3921
|
var SuggestionsList_1 = SuggestionsList;
|
|
3922
|
+
var addTestLeadAttribute_1 = addTestLeadAttribute;
|
|
3897
3923
|
var findItemByIndex_1 = findItemByIndex;
|
|
3898
3924
|
var getAttribution_1 = getAttribution;
|
|
3899
3925
|
var getItemsLength_1 = getItemsLength;
|
|
@@ -6363,6 +6389,8 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6363
6389
|
if (origin) {
|
|
6364
6390
|
attributes["origin"] = origin;
|
|
6365
6391
|
}
|
|
6392
|
+
// A test lead from Studio's "send yourself a test lead" booking page
|
|
6393
|
+
addTestLeadAttribute_1(attributes);
|
|
6366
6394
|
// Add current time for accurate date/time responses
|
|
6367
6395
|
attributes["currentTime"] = new Date().toISOString();
|
|
6368
6396
|
now = new Date().getTime();
|
|
@@ -7591,6 +7619,8 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7591
7619
|
if (origin) {
|
|
7592
7620
|
attributes["origin"] = origin;
|
|
7593
7621
|
}
|
|
7622
|
+
// A test lead from Studio's "send yourself a test lead" booking page
|
|
7623
|
+
addTestLeadAttribute_1(attributes);
|
|
7594
7624
|
// Add current time for accurate date/time responses
|
|
7595
7625
|
attributes["currentTime"] = new Date().toISOString();
|
|
7596
7626
|
request = requestFromMessage(message, userId, this.isNewSession, sessionId, accessToken, attributes, this.visitorInfo);
|
|
@@ -37293,12 +37323,25 @@ var ChatWidgetConnected = function (props) {
|
|
|
37293
37323
|
}
|
|
37294
37324
|
return (require$$0.jsx(ChatConfigContext.Provider, { value: config, children: require$$0.jsx(ChatServerContext.Provider, { value: chatServer, children: require$$0.jsx(ChatWidgetUI, __assign$1({}, props, { config: rawConfig })) }) }));
|
|
37295
37325
|
};
|
|
37326
|
+
var HOST_FORM_WIDGET_CHECK_MS = 500;
|
|
37327
|
+
/** About 15s: long enough for a slow host script, short enough not to poll forever. */
|
|
37328
|
+
var HOST_FORM_WIDGET_MAX_CHECKS = 30;
|
|
37329
|
+
/** The form widget key a Form/Book button carries on its own formConfig, if any. */
|
|
37330
|
+
function formButtonKey(button) {
|
|
37331
|
+
var _a, _b;
|
|
37332
|
+
return opensFormWidget(button)
|
|
37333
|
+
? ((_b = (_a = button.formConfig) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.key) || undefined
|
|
37334
|
+
: undefined;
|
|
37335
|
+
}
|
|
37336
|
+
function isHostFormWidgetOnPage() {
|
|
37337
|
+
return isFormWidgetScriptLoaded() || isFormWidgetReady();
|
|
37338
|
+
}
|
|
37296
37339
|
/**
|
|
37297
37340
|
* Inner ChatWidget component that renders the actual UI.
|
|
37298
37341
|
* Exported for use by StaticChatWidgetContainer to avoid infinite loops.
|
|
37299
37342
|
*/
|
|
37300
37343
|
var ChatWidgetUI = function (props) {
|
|
37301
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20;
|
|
37344
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23;
|
|
37302
37345
|
var innerDispatch = useChatDispatch();
|
|
37303
37346
|
var dispatch = useChatServerDispatch();
|
|
37304
37347
|
// Publish the visual viewport's geometry so the panel can size around the soft keyboard.
|
|
@@ -37329,21 +37372,21 @@ var ChatWidgetUI = function (props) {
|
|
|
37329
37372
|
var popOutEnabled = dockedMode && ((_c = props.config.popOut) === null || _c === void 0 ? void 0 : _c.enabled) === true;
|
|
37330
37373
|
var popOutMinWidth = (_e = (_d = props.config.popOut) === null || _d === void 0 ? void 0 : _d.minWidth) !== null && _e !== void 0 ? _e : DEFAULT_POP_OUT_MIN_WIDTH;
|
|
37331
37374
|
var anchorRef = React.useRef(null);
|
|
37332
|
-
var
|
|
37375
|
+
var _24 = React.useState(false), anchorOffScreen = _24[0], setAnchorOffScreen = _24[1];
|
|
37333
37376
|
// A returning visitor's chat often sits below the fold at load. Following them before they
|
|
37334
37377
|
// have even reached it would put a floating window in front of someone who never scrolled
|
|
37335
37378
|
// past anything, so the chat has to have been on screen once first.
|
|
37336
|
-
var
|
|
37379
|
+
var _25 = React.useState(false), anchorSeen = _25[0], setAnchorSeen = _25[1];
|
|
37337
37380
|
// Set when the visitor puts the floating window away: it stays put until their own
|
|
37338
37381
|
// scrolling brings the chat's place on the page back into view.
|
|
37339
|
-
var
|
|
37382
|
+
var _26 = React.useState(false), popOutDismissed = _26[0], setPopOutDismissed = _26[1];
|
|
37340
37383
|
// On a phone the chat offers its entry point rather than covering the page; this is set when
|
|
37341
37384
|
// the visitor taps that and asks for the conversation.
|
|
37342
|
-
var
|
|
37385
|
+
var _27 = React.useState(false), mobileOpened = _27[0], setMobileOpened = _27[1];
|
|
37343
37386
|
// Tracked rather than read at render: a visitor who narrows the window or rotates a tablet
|
|
37344
37387
|
// while the chat is popped out would otherwise keep a floating window over most of a phone
|
|
37345
37388
|
// screen until some unrelated re-render happened along.
|
|
37346
|
-
var
|
|
37389
|
+
var _28 = React.useState(typeof window === "undefined" ? 0 : window.innerWidth), viewportWidth = _28[0], setViewportWidth = _28[1];
|
|
37347
37390
|
React.useEffect(function () {
|
|
37348
37391
|
if (!popOutEnabled || typeof window === "undefined") {
|
|
37349
37392
|
return undefined;
|
|
@@ -37509,10 +37552,10 @@ var ChatWidgetUI = function (props) {
|
|
|
37509
37552
|
var autoOpenOnWidthMatch = !isNaN(autoOpenOnWidthValue) && window.innerWidth >= autoOpenOnWidthValue;
|
|
37510
37553
|
// When autoOpenOnWidth is configured, use width check instead of persisted visible state
|
|
37511
37554
|
// to prevent greeting requests on viewports below the threshold
|
|
37512
|
-
var
|
|
37555
|
+
var _29 = React.useState((!canMinimize && !canCancel) ||
|
|
37513
37556
|
// !!get("visible") ||
|
|
37514
|
-
(!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible =
|
|
37515
|
-
var
|
|
37557
|
+
(!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible = _29[0], setVisibleState = _29[1];
|
|
37558
|
+
var _30 = React.useState(false); _30[0]; var setTypingState = _30[1]; // false initially - state kept for potential external observers
|
|
37516
37559
|
// Ref to track typing state for use in timeout callbacks
|
|
37517
37560
|
var typingRef = React.useRef(false);
|
|
37518
37561
|
// Timeout ref for debouncing "stop typing" events
|
|
@@ -37559,7 +37602,7 @@ var ChatWidgetUI = function (props) {
|
|
|
37559
37602
|
};
|
|
37560
37603
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37561
37604
|
}, []);
|
|
37562
|
-
var
|
|
37605
|
+
var _31 = React.useState(!document.hidden), isTabVisible = _31[0], setIsTabVisible = _31[1];
|
|
37563
37606
|
React.useEffect(function () {
|
|
37564
37607
|
var handleVisibilityChange = function () {
|
|
37565
37608
|
setIsTabVisible(!document.hidden);
|
|
@@ -37783,8 +37826,8 @@ var ChatWidgetUI = function (props) {
|
|
|
37783
37826
|
}));
|
|
37784
37827
|
}
|
|
37785
37828
|
// Action Bar state and handlers
|
|
37786
|
-
var
|
|
37787
|
-
var
|
|
37829
|
+
var _32 = React.useState(false), formModalOpen = _32[0], setFormModalOpen = _32[1];
|
|
37830
|
+
var _33 = React.useState(null), activeFormConfig = _33[0], setActiveFormConfig = _33[1];
|
|
37788
37831
|
var handleFormButtonClick = React.useCallback(function (formButton) {
|
|
37789
37832
|
// If form-widget is configured, use its openForm API
|
|
37790
37833
|
// The form-widget script should already be pre-loaded
|
|
@@ -37856,14 +37899,51 @@ var ChatWidgetUI = function (props) {
|
|
|
37856
37899
|
// contact/booking page the form already *is* the page, which makes every button that
|
|
37857
37900
|
// only opens one redundant. XappMedia/chat-widget#1626.
|
|
37858
37901
|
var isFormPage = useIsFormPage();
|
|
37902
|
+
// Studio can save Form/Book buttons with no form widget key anywhere. They can only open the
|
|
37903
|
+
// FormModal fallback, which then says "Form widget key not configured" - so drop them, unless
|
|
37904
|
+
// the host page installed the form widget itself. One key anywhere is enough: a page has one
|
|
37905
|
+
// form widget and every Form/Book button opens it through the same global.
|
|
37906
|
+
var configuredButtons = (_2 = (_1 = config === null || config === void 0 ? void 0 : config.actionBar) === null || _1 === void 0 ? void 0 : _1.buttons) !== null && _2 !== void 0 ? _2 : [];
|
|
37907
|
+
var hasKeylessFormButton = !(config === null || config === void 0 ? void 0 : config.formWidgetKey) &&
|
|
37908
|
+
configuredButtons.some(opensFormWidget) &&
|
|
37909
|
+
!configuredButtons.some(formButtonKey);
|
|
37910
|
+
var _34 = React.useState(function () { return hasKeylessFormButton && isHostFormWidgetOnPage(); }), hostFormWidget = _34[0], setHostFormWidget = _34[1];
|
|
37911
|
+
// The host's form-widget script loads independently of ours and can arrive after the first
|
|
37912
|
+
// render, so keep looking for a while rather than deciding once. This only runs when there is
|
|
37913
|
+
// no key anywhere, and with no key we never preload - so the script this finds by its fixed id
|
|
37914
|
+
// cannot be our own.
|
|
37915
|
+
React.useEffect(function () {
|
|
37916
|
+
if (!hasKeylessFormButton || hostFormWidget) {
|
|
37917
|
+
return undefined;
|
|
37918
|
+
}
|
|
37919
|
+
var checks = 0;
|
|
37920
|
+
var interval = setInterval(function () {
|
|
37921
|
+
checks++;
|
|
37922
|
+
if (isHostFormWidgetOnPage()) {
|
|
37923
|
+
setHostFormWidget(true);
|
|
37924
|
+
clearInterval(interval);
|
|
37925
|
+
}
|
|
37926
|
+
else if (checks >= HOST_FORM_WIDGET_MAX_CHECKS) {
|
|
37927
|
+
clearInterval(interval);
|
|
37928
|
+
}
|
|
37929
|
+
}, HOST_FORM_WIDGET_CHECK_MS);
|
|
37930
|
+
return function () { return clearInterval(interval); };
|
|
37931
|
+
}, [hasKeylessFormButton, hostFormWidget]);
|
|
37932
|
+
var actionBarConfig = React.useMemo(function () {
|
|
37933
|
+
var actionBar = config === null || config === void 0 ? void 0 : config.actionBar;
|
|
37934
|
+
if (!actionBar || !hasKeylessFormButton || hostFormWidget) {
|
|
37935
|
+
return actionBar;
|
|
37936
|
+
}
|
|
37937
|
+
return __assign$1(__assign$1({}, actionBar), { buttons: actionBar.buttons.filter(function (button) { return !opensFormWidget(button); }) });
|
|
37938
|
+
}, [config === null || config === void 0 ? void 0 : config.actionBar, hasKeylessFormButton, hostFormWidget]);
|
|
37859
37939
|
var actionBarButtons = React.useMemo(function () {
|
|
37860
|
-
return actionBarEnabled &&
|
|
37861
|
-
? visibleActionBarButtons(
|
|
37940
|
+
return actionBarEnabled && actionBarConfig
|
|
37941
|
+
? visibleActionBarButtons(actionBarConfig.buttons, {
|
|
37862
37942
|
chatDisabled: config.disabled,
|
|
37863
37943
|
formDisabled: isFormPage,
|
|
37864
37944
|
})
|
|
37865
37945
|
: [];
|
|
37866
|
-
}, [actionBarEnabled,
|
|
37946
|
+
}, [actionBarEnabled, actionBarConfig, config === null || config === void 0 ? void 0 : config.disabled, isFormPage]);
|
|
37867
37947
|
// A bar that filters down to nothing is a bare strip of chrome. Treat it as if the bar
|
|
37868
37948
|
// were off, so the visitor still gets the plain chat button rather than an empty shell.
|
|
37869
37949
|
var actionBarVisible = actionBarEnabled && actionBarButtons.length > 0;
|
|
@@ -37875,7 +37955,7 @@ var ChatWidgetUI = function (props) {
|
|
|
37875
37955
|
onConnectionStatusChange(connectionStatus);
|
|
37876
37956
|
}
|
|
37877
37957
|
}, [connectionStatus, onConnectionStatusChange]);
|
|
37878
|
-
useExternalScript((
|
|
37958
|
+
useExternalScript((_3 = props.config) === null || _3 === void 0 ? void 0 : _3.middlewareUrl);
|
|
37879
37959
|
// Only reach for form.xapp.ai when something on this page can still open the form.
|
|
37880
37960
|
//
|
|
37881
37961
|
// Loading it is not free: the script mounts a second form-widget instance whose wrapper
|
|
@@ -37885,11 +37965,16 @@ var ChatWidgetUI = function (props) {
|
|
|
37885
37965
|
//
|
|
37886
37966
|
// With no bar configured the side button IS the entry point, so that case still loads.
|
|
37887
37967
|
var formWidgetNeeded = !isFormPage && (!actionBarEnabled || actionBarButtons.some(opensFormWidget));
|
|
37968
|
+
// The top-level key is the documented place for it, but an App can carry the key only on
|
|
37969
|
+
// a Form/Book button's formConfig - buildChatWidgetChannel did exactly that. Reading only
|
|
37970
|
+
// the top-level key left the script unloaded, so every click waited out
|
|
37971
|
+
// openFormWidgetWhenReady before the modal fallback found the button's key.
|
|
37972
|
+
var formWidgetKey = (_4 = config === null || config === void 0 ? void 0 : config.formWidgetKey) !== null && _4 !== void 0 ? _4 : actionBarButtons.map(formButtonKey).find(Boolean);
|
|
37888
37973
|
React.useEffect(function () {
|
|
37889
|
-
if (
|
|
37890
|
-
preloadFormWidgetScript(
|
|
37974
|
+
if (formWidgetKey && formWidgetNeeded) {
|
|
37975
|
+
preloadFormWidgetScript(formWidgetKey);
|
|
37891
37976
|
}
|
|
37892
|
-
}, [
|
|
37977
|
+
}, [formWidgetKey, formWidgetNeeded]);
|
|
37893
37978
|
// Set body attribute when actionBar is enabled (used by CSS to hide form-widget side button on mobile)
|
|
37894
37979
|
// Uses reference counting to handle multiple widget instances
|
|
37895
37980
|
React.useEffect(function () {
|
|
@@ -37915,23 +38000,23 @@ var ChatWidgetUI = function (props) {
|
|
|
37915
38000
|
};
|
|
37916
38001
|
}, [actionBarVisible]);
|
|
37917
38002
|
// This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
|
|
37918
|
-
var widgetAgent = ((
|
|
38003
|
+
var widgetAgent = ((_5 = chatState.agents["agent:robot"]) === null || _5 === void 0 ? void 0 : _5.user) ||
|
|
37919
38004
|
(config === null || config === void 0 ? void 0 : config.agent) || {
|
|
37920
38005
|
nick: "agent:robot",
|
|
37921
38006
|
avatarPath: config.avatarUrl,
|
|
37922
38007
|
display_name: "Agent",
|
|
37923
38008
|
};
|
|
37924
|
-
var widgetContainer = (require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : "").concat(poppedOut ? " widget-container--popped" : "").concat(collapsedOnMobile ? " widget-container--collapsed" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), !headerHidden && (require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: poppedOut ? handlePopOutDismiss : handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: poppedOut || canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (
|
|
38009
|
+
var widgetContainer = (require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : "").concat(poppedOut ? " widget-container--popped" : "").concat(collapsedOnMobile ? " widget-container--collapsed" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), !headerHidden && (require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: poppedOut ? handlePopOutDismiss : handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: poppedOut || canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_8 = (_7 = (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.connection) === null || _7 === void 0 ? void 0 : _7.mcp) === null || _8 === void 0 ? void 0 : _8.debugMode, onSubmit: handleOnSubmit })), require$$0.jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_10 = (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.typingStatus) === null || _10 === void 0 ? void 0 : _10.textTypingStatusEnabled, disableAutoScroll: props.disableAutoScroll, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), require$$0.jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: require$$0.jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && require$$0.jsx(ServerOffline, {}), chatState.wsButton && visible && (require$$0.jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), require$$0.jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_11 = config === null || config === void 0 ? void 0 : config.input) === null || _11 === void 0 ? void 0 : _11.placeholder, sendButtonIcon: (_13 = (_12 = config === null || config === void 0 ? void 0 : config.footer) === null || _12 === void 0 ? void 0 : _12.sendButton) === null || _13 === void 0 ? void 0 : _13.icon, sendButtonIconHover: (_15 = (_14 = config === null || config === void 0 ? void 0 : config.footer) === null || _14 === void 0 ? void 0 : _14.sendButton) === null || _15 === void 0 ? void 0 : _15.iconHover, sendButtonIconDisabled: (_17 = (_16 = config === null || config === void 0 ? void 0 : config.footer) === null || _16 === void 0 ? void 0 : _16.sendButton) === null || _17 === void 0 ? void 0 : _17.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: menuConfig, footerConfig: (_18 = props.config) === null || _18 === void 0 ? void 0 : _18.footer, inputConfig: (_19 = props.config) === null || _19 === void 0 ? void 0 : _19.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), require$$0.jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: require$$0.jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }));
|
|
37925
38010
|
return (require$$0.jsxs(require$$0.Fragment, { children: [popOutEnabled ? (
|
|
37926
38011
|
// The anchor keeps the chat's place in the page, so the layout does not jump when
|
|
37927
38012
|
// the chat pops out. The chat itself lives in popOutHost, which the effect above
|
|
37928
38013
|
// moves between this anchor and the body.
|
|
37929
|
-
require$$0.jsx("div", { className: "widget-anchor", ref: anchorRef, children: popOutHost && reactDom.createPortal(widgetContainer, popOutHost) })) : (widgetContainer), actionBarVisible &&
|
|
38014
|
+
require$$0.jsx("div", { className: "widget-anchor", ref: anchorRef, children: popOutHost && reactDom.createPortal(widgetContainer, popOutHost) })) : (widgetContainer), actionBarVisible && actionBarConfig && !(collapsedOnMobile && mobileEntry === "button") ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign$1(__assign$1({}, actionBarConfig), {
|
|
37930
38015
|
// Use actionBar.cta if set, otherwise fall back to widget-level cta
|
|
37931
|
-
cta: resolveCta((
|
|
38016
|
+
cta: resolveCta((_20 = actionBarConfig.cta) !== null && _20 !== void 0 ? _20 : config.cta, {
|
|
37932
38017
|
hasConversation: conversationOnScreen,
|
|
37933
38018
|
arrivedViaChatLink: arrivedViaChatLink,
|
|
37934
|
-
}) }), visible: collapsedOnMobile ? false : visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (
|
|
38019
|
+
}) }), visible: collapsedOnMobile ? false : visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (_21 = chatState.visuals) === null || _21 === void 0 ? void 0 : _21.hasInteracted, onChatClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick, onCtaDismiss: handleCtaDismiss }), formModalOpen && activeFormConfig && (require$$0.jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (require$$0.jsx(ChatButton, { addClass: visible && !collapsedOnMobile ? "visible" : "", onClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, config: chatButtonCta, imageUrl: (_22 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _22 === void 0 ? void 0 : _22.imageUrl, visible: collapsedOnMobile ? false : visible, hasInteracted: (_23 = chatState.visuals) === null || _23 === void 0 ? void 0 : _23.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
|
|
37935
38020
|
};
|
|
37936
38021
|
/**
|
|
37937
38022
|
* Top-level wrapper that dispatches between preview mode and connected mode.
|