@buni.ai/chatbot-core 1.0.12 → 1.0.14
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.esm.js +397 -260
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +397 -260
- package/dist/index.js.map +1 -1
- package/dist/widget.d.ts +5 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -9,7 +9,8 @@ class BuniChatWidget {
|
|
|
9
9
|
};
|
|
10
10
|
this.eventListeners = new Map();
|
|
11
11
|
this.widgetElement = null;
|
|
12
|
-
this.
|
|
12
|
+
this.triggerIframe = null;
|
|
13
|
+
this.chatIframe = null;
|
|
13
14
|
this.customerData = null;
|
|
14
15
|
this.sessionVariables = null;
|
|
15
16
|
}
|
|
@@ -55,193 +56,382 @@ class BuniChatWidget {
|
|
|
55
56
|
};
|
|
56
57
|
const widthValue = ensureUnits(config.width, "350px");
|
|
57
58
|
const heightValue = ensureUnits(config.height, "650px");
|
|
58
|
-
//
|
|
59
|
-
const shouldStartMinimized = config.initialMinimized === true;
|
|
59
|
+
// Configuration
|
|
60
60
|
const showTriggerText = config.showTriggerText !== false;
|
|
61
61
|
const hasTriggerText = !!config.triggerText;
|
|
62
|
-
|
|
62
|
+
const primaryColor = config.primaryColor || "#795548";
|
|
63
|
+
const triggerText = config.triggerText || "";
|
|
64
|
+
const companyName = config.companyName || "Chat Support";
|
|
65
|
+
// Responsive breakpoints
|
|
63
66
|
const isMobile = window.innerWidth <= 768;
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
? showTriggerText && hasTriggerText
|
|
70
|
-
? "auto"
|
|
71
|
-
: "52px"
|
|
72
|
-
: isExtraSmall
|
|
73
|
-
? "100vw" // Full width on very small devices
|
|
74
|
-
: isMobile
|
|
75
|
-
? "min(100vw, 370px)" // Constrained width on mobile
|
|
76
|
-
: isTablet
|
|
77
|
-
? "min(calc(100vw - 3rem), 370px)" // Slightly smaller on tablets
|
|
78
|
-
: widthValue; // Custom width on desktop
|
|
79
|
-
const initialHeight = shouldStartMinimized
|
|
80
|
-
? "52px"
|
|
81
|
-
: isExtraSmall
|
|
82
|
-
? "100vh" // Full height on very small devices
|
|
83
|
-
: isMobile
|
|
84
|
-
? "min(100vh, 600px)" // Constrained height on mobile
|
|
85
|
-
: isTablet
|
|
86
|
-
? "min(calc(100vh - 3rem), 620px)" // Slightly smaller on tablets
|
|
87
|
-
: heightValue; // Custom height on desktop
|
|
88
|
-
const initialMinWidth = shouldStartMinimized && showTriggerText && hasTriggerText ? "auto" : "";
|
|
89
|
-
// Use CSS custom properties for dynamic styling
|
|
67
|
+
// Calculate trigger dimensions
|
|
68
|
+
const triggerWidth = showTriggerText && hasTriggerText ? "auto" : "52px";
|
|
69
|
+
const triggerHeight = "52px";
|
|
70
|
+
const triggerMinWidth = showTriggerText && hasTriggerText ? "auto" : "";
|
|
71
|
+
// Container starts as trigger size, initially hidden until connection is ready
|
|
90
72
|
container.style.cssText = `
|
|
91
73
|
position: fixed;
|
|
92
74
|
pointer-events: none;
|
|
93
75
|
z-index: 999999;
|
|
94
|
-
width: ${
|
|
95
|
-
height: ${
|
|
96
|
-
${
|
|
97
|
-
${isMobile && !shouldStartMinimized ? "max-width: 100vw; max-height: 100vh;" : ""}
|
|
76
|
+
width: ${triggerWidth};
|
|
77
|
+
height: ${triggerHeight};
|
|
78
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
98
79
|
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
99
|
-
${this.getPositionStyles(config.position || "bottom-right",
|
|
100
|
-
display:
|
|
101
|
-
overflow:
|
|
80
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
81
|
+
display: none;
|
|
82
|
+
overflow: hidden;
|
|
102
83
|
box-sizing: border-box;
|
|
103
84
|
`;
|
|
104
|
-
// Create iframe
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
else {
|
|
223
|
-
iframe.style.boxShadow = "0 8px 32px rgba(0,0,0,0.15)";
|
|
224
|
-
}
|
|
225
|
-
iframe.setAttribute("allow", "clipboard-write");
|
|
226
|
-
iframe.setAttribute("title", "BuniAI Chat Widget");
|
|
227
|
-
iframe.onload = () => {
|
|
85
|
+
// Create inline trigger iframe (no src)
|
|
86
|
+
const triggerIframe = document.createElement("iframe");
|
|
87
|
+
triggerIframe.id = "buni-trigger-iframe";
|
|
88
|
+
triggerIframe.style.cssText = `
|
|
89
|
+
position: absolute;
|
|
90
|
+
top: 0;
|
|
91
|
+
left: 0;
|
|
92
|
+
border: none;
|
|
93
|
+
width: 100%;
|
|
94
|
+
height: 100%;
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
border-radius: ${showTriggerText && hasTriggerText ? "26px" : "50%"};
|
|
97
|
+
transition: border-radius 0.3s ease, box-shadow 0.3s ease;
|
|
98
|
+
pointer-events: auto;
|
|
99
|
+
`;
|
|
100
|
+
triggerIframe.setAttribute("title", "BuniAI Chat Trigger");
|
|
101
|
+
// Inject trigger HTML content directly (no src)
|
|
102
|
+
container.appendChild(triggerIframe);
|
|
103
|
+
document.body.appendChild(container);
|
|
104
|
+
triggerIframe.onload = () => {
|
|
105
|
+
var _a;
|
|
106
|
+
const triggerDoc = triggerIframe.contentDocument ||
|
|
107
|
+
((_a = triggerIframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document);
|
|
108
|
+
if (!triggerDoc) {
|
|
109
|
+
reject(new Error("Failed to access trigger iframe document"));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Build inline HTML for trigger
|
|
113
|
+
const triggerHTML = this.buildTriggerHTML(primaryColor, triggerText || companyName, showTriggerText && hasTriggerText, config.customAvatar, config.avatarType, config.avatarText);
|
|
114
|
+
triggerDoc.open();
|
|
115
|
+
triggerDoc.write(triggerHTML);
|
|
116
|
+
triggerDoc.close();
|
|
117
|
+
// Create chat iframe (initially hidden)
|
|
118
|
+
const chatIframe = document.createElement("iframe");
|
|
119
|
+
chatIframe.id = "buni-chat-iframe";
|
|
120
|
+
// Build URL with configuration parameters
|
|
121
|
+
const params = new URLSearchParams({
|
|
122
|
+
token: this.options.token,
|
|
123
|
+
embedded: "true",
|
|
124
|
+
source: "package",
|
|
125
|
+
framework: this.options.framework || "vanilla",
|
|
126
|
+
});
|
|
127
|
+
// Add all configuration parameters
|
|
128
|
+
if (config.theme)
|
|
129
|
+
params.set("theme", config.theme);
|
|
130
|
+
if (config.primaryColor)
|
|
131
|
+
params.set("primaryColor", config.primaryColor);
|
|
132
|
+
if (config.secondaryColor)
|
|
133
|
+
params.set("secondaryColor", config.secondaryColor);
|
|
134
|
+
if (config.position)
|
|
135
|
+
params.set("position", config.position);
|
|
136
|
+
if (widthValue && config.width !== undefined)
|
|
137
|
+
params.set("width", widthValue);
|
|
138
|
+
if (heightValue && config.height !== undefined)
|
|
139
|
+
params.set("height", heightValue);
|
|
140
|
+
if (config.customAvatar)
|
|
141
|
+
params.set("customAvatar", config.customAvatar);
|
|
142
|
+
if (config.companyName)
|
|
143
|
+
params.set("companyName", config.companyName);
|
|
144
|
+
if (config.welcomeMessage)
|
|
145
|
+
params.set("welcomeMessage", config.welcomeMessage);
|
|
146
|
+
if (config.triggerText)
|
|
147
|
+
params.set("triggerText", config.triggerText);
|
|
148
|
+
if (config.borderRadius)
|
|
149
|
+
params.set("borderRadius", config.borderRadius);
|
|
150
|
+
if (config.avatarType)
|
|
151
|
+
params.set("avatarType", config.avatarType);
|
|
152
|
+
if (config.avatarText)
|
|
153
|
+
params.set("avatarText", config.avatarText);
|
|
154
|
+
// Boolean options
|
|
155
|
+
if (config.showBranding !== undefined)
|
|
156
|
+
params.set("showBranding", String(config.showBranding));
|
|
157
|
+
if (config.autoOpen !== undefined)
|
|
158
|
+
params.set("autoOpen", String(config.autoOpen));
|
|
159
|
+
if (config.allowMinimize !== undefined)
|
|
160
|
+
params.set("allowMinimize", String(config.allowMinimize));
|
|
161
|
+
if (config.showMinimize !== undefined)
|
|
162
|
+
params.set("showMinimize", String(config.showMinimize));
|
|
163
|
+
if (config.allowClose !== undefined)
|
|
164
|
+
params.set("allowClose", String(config.allowClose));
|
|
165
|
+
if (config.enableFileUpload !== undefined)
|
|
166
|
+
params.set("enableFileUpload", String(config.enableFileUpload));
|
|
167
|
+
if (config.showTimestamps !== undefined)
|
|
168
|
+
params.set("showTimestamps", String(config.showTimestamps));
|
|
169
|
+
if (config.enableMobile !== undefined)
|
|
170
|
+
params.set("enableMobile", String(config.enableMobile));
|
|
171
|
+
if (config.showPreChatForm !== undefined)
|
|
172
|
+
params.set("showPreChatForm", String(config.showPreChatForm));
|
|
173
|
+
if (config.showStartButton !== undefined)
|
|
174
|
+
params.set("showStartButton", String(config.showStartButton));
|
|
175
|
+
if (config.startButtonText)
|
|
176
|
+
params.set("startButtonText", config.startButtonText);
|
|
177
|
+
if (config.preChatFormFields)
|
|
178
|
+
params.set("preChatFormFields", JSON.stringify(config.preChatFormFields));
|
|
179
|
+
chatIframe.src = `${this.getBaseUrl()}/embed/chat?${params.toString()}`;
|
|
180
|
+
// Chat iframe styling - initially hidden
|
|
181
|
+
chatIframe.style.cssText = `
|
|
182
|
+
position: absolute;
|
|
183
|
+
top: 0;
|
|
184
|
+
left: 0;
|
|
185
|
+
border: none;
|
|
186
|
+
width: 100%;
|
|
187
|
+
height: 100%;
|
|
188
|
+
box-sizing: border-box;
|
|
189
|
+
border-radius: ${isMobile ? "0" : "16px"};
|
|
190
|
+
transition: border-radius 0.3s ease, box-shadow 0.3s ease;
|
|
191
|
+
pointer-events: auto;
|
|
192
|
+
box-shadow: ${isMobile ? "none" : "0 8px 32px rgba(0,0,0,0.15)"};
|
|
193
|
+
display: none;
|
|
194
|
+
`;
|
|
195
|
+
chatIframe.setAttribute("allow", "clipboard-write");
|
|
196
|
+
chatIframe.setAttribute("title", "BuniAI Chat Widget");
|
|
197
|
+
container.appendChild(chatIframe);
|
|
198
|
+
chatIframe.onload = () => {
|
|
199
|
+
// Set visibility hidden after iframe loads to allow content initialization
|
|
200
|
+
chatIframe.style.visibility = "hidden";
|
|
201
|
+
this.setupPostMessageAPI(chatIframe);
|
|
202
|
+
};
|
|
228
203
|
this.widgetElement = container;
|
|
229
|
-
this.
|
|
230
|
-
this.
|
|
231
|
-
this.
|
|
204
|
+
this.triggerIframe = triggerIframe;
|
|
205
|
+
this.chatIframe = chatIframe;
|
|
206
|
+
this.state.isMinimized = true;
|
|
207
|
+
this.state.isLoaded = true;
|
|
232
208
|
resolve();
|
|
233
209
|
};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
210
|
+
// Trigger the load event
|
|
211
|
+
triggerIframe.src = "about:blank";
|
|
212
|
+
// Set up communication with trigger iframe
|
|
213
|
+
window.addEventListener("message", (event) => {
|
|
214
|
+
var _a;
|
|
215
|
+
// Check if message is from trigger iframe
|
|
216
|
+
if (event.data.type === "trigger_clicked" &&
|
|
217
|
+
event.source === triggerIframe.contentWindow) {
|
|
218
|
+
this.openChat();
|
|
219
|
+
}
|
|
220
|
+
// Check if message is connection_ready from chat iframe
|
|
221
|
+
if (event.data.type === "connection_ready" &&
|
|
222
|
+
event.source === ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow)) {
|
|
223
|
+
// Connection is ready, show the trigger button
|
|
224
|
+
if (container && !config.hideDefaultTrigger) {
|
|
225
|
+
container.style.display = "block";
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
239
229
|
});
|
|
240
230
|
}
|
|
231
|
+
buildTriggerHTML(primaryColor, text, showText, customAvatar, avatarType, avatarText) {
|
|
232
|
+
const avatarContent = customAvatar
|
|
233
|
+
? `<img src="${customAvatar}" alt="Avatar" style="width: 32px; height: 32px; border-radius: 50%;" />`
|
|
234
|
+
: avatarType === "text" && avatarText
|
|
235
|
+
? `<div style="width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,0.3); display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; color: white;">${avatarText.substring(0, 2).toUpperCase()}</div>`
|
|
236
|
+
: `<svg width="28" height="28" viewBox="0 0 24 24" fill="white"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1 0-.9-2-2zm0 14H6l-2 2V4h16v12z"/></svg>`;
|
|
237
|
+
return `<!DOCTYPE html>
|
|
238
|
+
<html>
|
|
239
|
+
<head>
|
|
240
|
+
<meta charset="UTF-8">
|
|
241
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
242
|
+
<style>
|
|
243
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
244
|
+
body {
|
|
245
|
+
width: 100%;
|
|
246
|
+
height: 100%;
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
}
|
|
253
|
+
.trigger {
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
gap: ${showText ? "12px" : "0"};
|
|
258
|
+
padding: ${showText ? "10px 20px" : "10px"};
|
|
259
|
+
background: ${primaryColor};
|
|
260
|
+
color: white;
|
|
261
|
+
border-radius: ${showText ? "26px" : "50%"};
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
transition: all 0.3s ease;
|
|
264
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
265
|
+
animation: pulse 2s infinite;
|
|
266
|
+
${showText ? "" : "width: 52px; height: 52px;"}
|
|
267
|
+
}
|
|
268
|
+
.trigger:hover {
|
|
269
|
+
transform: translateY(-2px);
|
|
270
|
+
filter: brightness(1.1);
|
|
271
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
272
|
+
}
|
|
273
|
+
.trigger:active {
|
|
274
|
+
transform: translateY(0);
|
|
275
|
+
}
|
|
276
|
+
.avatar {
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
justify-content: center;
|
|
280
|
+
flex-shrink: 0;
|
|
281
|
+
}
|
|
282
|
+
.text {
|
|
283
|
+
font-size: 14px;
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
white-space: nowrap;
|
|
286
|
+
}
|
|
287
|
+
.badge {
|
|
288
|
+
position: absolute;
|
|
289
|
+
top: -4px;
|
|
290
|
+
right: -4px;
|
|
291
|
+
background: #f44336;
|
|
292
|
+
color: white;
|
|
293
|
+
border-radius: 10px;
|
|
294
|
+
padding: 2px 6px;
|
|
295
|
+
font-size: 11px;
|
|
296
|
+
font-weight: 600;
|
|
297
|
+
min-width: 18px;
|
|
298
|
+
text-align: center;
|
|
299
|
+
display: none;
|
|
300
|
+
animation: pulse 2s infinite;
|
|
301
|
+
}
|
|
302
|
+
.badge.show {
|
|
303
|
+
display: block;
|
|
304
|
+
}
|
|
305
|
+
@keyframes pulse {
|
|
306
|
+
0%, 100% {
|
|
307
|
+
transform: scale(1);
|
|
308
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
309
|
+
}
|
|
310
|
+
50% {
|
|
311
|
+
transform: scale(1.02);
|
|
312
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
</style>
|
|
316
|
+
</head>
|
|
317
|
+
<body>
|
|
318
|
+
<div style="position: relative;">
|
|
319
|
+
<div class="trigger" onclick="handleClick()">
|
|
320
|
+
<div class="avatar">${avatarContent}</div>
|
|
321
|
+
${showText ? `<span class="text">${text}</span>` : ""}
|
|
322
|
+
</div>
|
|
323
|
+
<div class="badge" id="badge">0</div>
|
|
324
|
+
</div>
|
|
325
|
+
<script>
|
|
326
|
+
function handleClick() {
|
|
327
|
+
window.parent.postMessage({ type: 'trigger_clicked' }, '*');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Listen for unread count updates
|
|
331
|
+
window.addEventListener('message', function(event) {
|
|
332
|
+
if (event.data.type === 'updateUnreadCount') {
|
|
333
|
+
const badge = document.getElementById('badge');
|
|
334
|
+
const count = event.data.count || 0;
|
|
335
|
+
badge.textContent = count;
|
|
336
|
+
if (count > 0) {
|
|
337
|
+
badge.classList.add('show');
|
|
338
|
+
} else {
|
|
339
|
+
badge.classList.remove('show');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
</script>
|
|
344
|
+
</body>
|
|
345
|
+
</html>`;
|
|
346
|
+
}
|
|
347
|
+
async openChat() {
|
|
348
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
349
|
+
return;
|
|
350
|
+
if (this.state.isOpen)
|
|
351
|
+
return; // Already open
|
|
352
|
+
const config = this.options.config || {};
|
|
353
|
+
const isMobile = window.innerWidth <= 768;
|
|
354
|
+
const isTablet = window.innerWidth > 768 && window.innerWidth <= 1024;
|
|
355
|
+
const ensureUnits = (value, defaultValue) => {
|
|
356
|
+
if (!value)
|
|
357
|
+
return defaultValue;
|
|
358
|
+
const str = String(value);
|
|
359
|
+
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
360
|
+
return str;
|
|
361
|
+
if (str.match(/^[\d.]+$/))
|
|
362
|
+
return `${str}px`;
|
|
363
|
+
return str;
|
|
364
|
+
};
|
|
365
|
+
const widthValue = ensureUnits(config.width, "350px");
|
|
366
|
+
const heightValue = ensureUnits(config.height, "650px");
|
|
367
|
+
// Calculate chat dimensions
|
|
368
|
+
const chatWidth = isMobile
|
|
369
|
+
? "100vw"
|
|
370
|
+
: isTablet
|
|
371
|
+
? "min(calc(100vw - 3rem), 370px)"
|
|
372
|
+
: widthValue;
|
|
373
|
+
const chatHeight = isMobile
|
|
374
|
+
? "100vh"
|
|
375
|
+
: isTablet
|
|
376
|
+
? "min(calc(100vh - 3rem), 620px)"
|
|
377
|
+
: heightValue;
|
|
378
|
+
// Resize container for chat
|
|
379
|
+
this.widgetElement.style.cssText = `
|
|
380
|
+
position: fixed;
|
|
381
|
+
pointer-events: none;
|
|
382
|
+
z-index: 999999;
|
|
383
|
+
width: ${chatWidth};
|
|
384
|
+
height: ${chatHeight};
|
|
385
|
+
${isMobile ? "max-width: 100vw; max-height: 100vh;" : ""}
|
|
386
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
387
|
+
${this.getPositionStyles(config.position || "bottom-right", false)}
|
|
388
|
+
display: block;
|
|
389
|
+
overflow: visible;
|
|
390
|
+
box-sizing: border-box;
|
|
391
|
+
`;
|
|
392
|
+
// Hide trigger, show chat
|
|
393
|
+
this.triggerIframe.style.display = "none";
|
|
394
|
+
this.chatIframe.style.display = "block";
|
|
395
|
+
this.chatIframe.style.visibility = "visible";
|
|
396
|
+
this.state.isMinimized = false;
|
|
397
|
+
this.state.isOpen = true;
|
|
398
|
+
this.emit("maximized", { timestamp: Date.now() });
|
|
399
|
+
}
|
|
400
|
+
closeChat() {
|
|
401
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
402
|
+
return;
|
|
403
|
+
const config = this.options.config || {};
|
|
404
|
+
const showTriggerText = config.showTriggerText !== false;
|
|
405
|
+
const hasTriggerText = !!config.triggerText;
|
|
406
|
+
// Hide chat iframe
|
|
407
|
+
this.chatIframe.style.display = "none";
|
|
408
|
+
this.chatIframe.style.visibility = "hidden";
|
|
409
|
+
// Resize container back to trigger size
|
|
410
|
+
const triggerWidth = showTriggerText && hasTriggerText ? "auto" : "52px";
|
|
411
|
+
const triggerHeight = "52px";
|
|
412
|
+
const triggerMinWidth = showTriggerText && hasTriggerText ? "auto" : "";
|
|
413
|
+
this.widgetElement.style.cssText = `
|
|
414
|
+
position: fixed;
|
|
415
|
+
pointer-events: none;
|
|
416
|
+
z-index: 999999;
|
|
417
|
+
width: ${triggerWidth};
|
|
418
|
+
height: ${triggerHeight};
|
|
419
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
420
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
421
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
422
|
+
display: block;
|
|
423
|
+
overflow: hidden;
|
|
424
|
+
box-sizing: border-box;
|
|
425
|
+
`;
|
|
426
|
+
// Show trigger again
|
|
427
|
+
this.triggerIframe.style.display = "block";
|
|
428
|
+
this.state.isMinimized = true;
|
|
429
|
+
this.state.isOpen = false;
|
|
430
|
+
this.emit("minimized", { timestamp: Date.now() });
|
|
431
|
+
}
|
|
241
432
|
getPositionStyles(position, isMinimized = false) {
|
|
242
433
|
// Responsive positioning following mobile-first best practices
|
|
243
434
|
const viewportWidth = window.innerWidth;
|
|
244
|
-
const isExtraSmall = viewportWidth <= 375;
|
|
245
435
|
const isMobile = viewportWidth <= 768;
|
|
246
436
|
const isTablet = viewportWidth > 768 && viewportWidth <= 1024;
|
|
247
437
|
// For extra small devices (Galaxy S8+, iPhone SE, etc.), use minimal or no margins
|
|
@@ -252,13 +442,9 @@ class BuniChatWidget {
|
|
|
252
442
|
// Minimized button always needs some space from edges
|
|
253
443
|
margin = isMobile ? "12px" : "20px";
|
|
254
444
|
}
|
|
255
|
-
else if (isExtraSmall) {
|
|
256
|
-
// Full screen on extra small devices (no margins)
|
|
257
|
-
margin = "0";
|
|
258
|
-
}
|
|
259
445
|
else if (isMobile) {
|
|
260
|
-
//
|
|
261
|
-
margin = "
|
|
446
|
+
// Full screen on all mobile devices (no margins)
|
|
447
|
+
margin = "0";
|
|
262
448
|
}
|
|
263
449
|
else if (isTablet) {
|
|
264
450
|
// Medium margins on tablets
|
|
@@ -268,18 +454,9 @@ class BuniChatWidget {
|
|
|
268
454
|
// Larger margins on desktop for floating effect
|
|
269
455
|
margin = "24px";
|
|
270
456
|
}
|
|
271
|
-
// On
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
case "bottom-right":
|
|
275
|
-
case "bottom-left":
|
|
276
|
-
return `bottom: 0; left: 0; right: 0;`;
|
|
277
|
-
case "top-right":
|
|
278
|
-
case "top-left":
|
|
279
|
-
return `top: 0; left: 0; right: 0;`;
|
|
280
|
-
default:
|
|
281
|
-
return `bottom: 0; left: 0; right: 0;`;
|
|
282
|
-
}
|
|
457
|
+
// On mobile screens when not minimized, position at edges for full coverage
|
|
458
|
+
if (isMobile && !isMinimized) {
|
|
459
|
+
return `top: 0; left: 0; right: 0; bottom: 0;`;
|
|
283
460
|
}
|
|
284
461
|
// Standard positioning for larger screens or minimized state
|
|
285
462
|
switch (position) {
|
|
@@ -298,7 +475,7 @@ class BuniChatWidget {
|
|
|
298
475
|
setupPostMessageAPI(iframe) {
|
|
299
476
|
// Listen for messages from the iframe
|
|
300
477
|
window.addEventListener("message", (event) => {
|
|
301
|
-
var _a
|
|
478
|
+
var _a;
|
|
302
479
|
// Verify origin for security
|
|
303
480
|
if (event.origin !== this.getBaseUrl()) {
|
|
304
481
|
return;
|
|
@@ -329,60 +506,15 @@ class BuniChatWidget {
|
|
|
329
506
|
}
|
|
330
507
|
break;
|
|
331
508
|
case "minimized":
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
// Resize container to trigger button size when minimized
|
|
335
|
-
this.widgetElement.style.width = data.dimensions.width;
|
|
336
|
-
this.widgetElement.style.height = data.dimensions.height;
|
|
337
|
-
this.widgetElement.style.overflow = "hidden";
|
|
338
|
-
if (data.dimensions.minWidth) {
|
|
339
|
-
this.widgetElement.style.minWidth = data.dimensions.minWidth;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
if (this.iframe) {
|
|
343
|
-
// Use circular border for icon-only, rounded for text button
|
|
344
|
-
this.iframe.style.borderRadius =
|
|
345
|
-
((_a = data.dimensions) === null || _a === void 0 ? void 0 : _a.width) === ((_b = data.dimensions) === null || _b === void 0 ? void 0 : _b.height)
|
|
346
|
-
? "50%"
|
|
347
|
-
: "26px";
|
|
348
|
-
}
|
|
349
|
-
this.emit("minimized", data);
|
|
509
|
+
// User clicked minimize in chat - close chat and show trigger
|
|
510
|
+
this.closeChat();
|
|
350
511
|
break;
|
|
351
|
-
case "
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const config = this.options.config || {};
|
|
357
|
-
if (isMobile) {
|
|
358
|
-
// On mobile, enforce responsive sizing
|
|
359
|
-
this.widgetElement.style.width = "min(calc(100vw - 2rem), 370px)";
|
|
360
|
-
this.widgetElement.style.height =
|
|
361
|
-
"min(calc(100vh - 2rem), 680px)";
|
|
362
|
-
this.widgetElement.style.maxWidth = "370px";
|
|
363
|
-
this.widgetElement.style.maxHeight = "680px";
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
// On desktop, respect custom dimensions
|
|
367
|
-
const ensureUnits = (value, defaultValue) => {
|
|
368
|
-
if (!value)
|
|
369
|
-
return defaultValue;
|
|
370
|
-
const str = String(value);
|
|
371
|
-
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
372
|
-
return str;
|
|
373
|
-
if (str.match(/^[\d.]+$/))
|
|
374
|
-
return `${str}px`;
|
|
375
|
-
return str;
|
|
376
|
-
};
|
|
377
|
-
this.widgetElement.style.width = ensureUnits(config.width, "350px");
|
|
378
|
-
this.widgetElement.style.height = ensureUnits(config.height, "650px");
|
|
379
|
-
}
|
|
380
|
-
this.widgetElement.style.overflow = "visible";
|
|
381
|
-
}
|
|
382
|
-
if (this.iframe) {
|
|
383
|
-
this.iframe.style.borderRadius = "12px";
|
|
512
|
+
case "new_unread_message":
|
|
513
|
+
// Update unread count in trigger
|
|
514
|
+
this.state.unreadCount++;
|
|
515
|
+
if ((_a = this.triggerIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
516
|
+
this.triggerIframe.contentWindow.postMessage({ type: "updateUnreadCount", count: this.state.unreadCount }, "*");
|
|
384
517
|
}
|
|
385
|
-
this.emit("maximized", data);
|
|
386
518
|
break;
|
|
387
519
|
case "customer_data_updated":
|
|
388
520
|
this.customerData = data;
|
|
@@ -403,8 +535,8 @@ class BuniChatWidget {
|
|
|
403
535
|
}
|
|
404
536
|
postMessageToWidget(type, data) {
|
|
405
537
|
var _a;
|
|
406
|
-
if (
|
|
407
|
-
|
|
538
|
+
if ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
539
|
+
this.chatIframe.contentWindow.postMessage({ type, data }, this.getBaseUrl());
|
|
408
540
|
}
|
|
409
541
|
}
|
|
410
542
|
getBaseUrl() {
|
|
@@ -417,44 +549,49 @@ class BuniChatWidget {
|
|
|
417
549
|
this.widgetElement.remove();
|
|
418
550
|
this.widgetElement = null;
|
|
419
551
|
}
|
|
552
|
+
this.triggerIframe = null;
|
|
553
|
+
this.chatIframe = null;
|
|
420
554
|
this.eventListeners.clear();
|
|
421
555
|
this.state.isLoaded = false;
|
|
422
556
|
this.customerData = null;
|
|
423
557
|
this.sessionVariables = null;
|
|
424
558
|
}
|
|
425
559
|
show() {
|
|
426
|
-
// Show the
|
|
427
|
-
if (this.widgetElement
|
|
560
|
+
// Show the widget container if it was hidden (hideDefaultTrigger mode)
|
|
561
|
+
if (this.widgetElement) {
|
|
428
562
|
this.widgetElement.style.display = "block";
|
|
429
563
|
}
|
|
430
|
-
|
|
431
|
-
this.state.isOpen
|
|
432
|
-
|
|
433
|
-
|
|
564
|
+
// Open chat if not already open
|
|
565
|
+
if (!this.chatIframe && !this.state.isOpen) {
|
|
566
|
+
this.openChat();
|
|
567
|
+
}
|
|
434
568
|
}
|
|
435
569
|
hide() {
|
|
436
570
|
var _a;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
571
|
+
// Close chat if open
|
|
572
|
+
if (this.chatIframe) {
|
|
573
|
+
this.closeChat();
|
|
574
|
+
}
|
|
575
|
+
// If hideDefaultTrigger is enabled, completely hide the container
|
|
576
|
+
if (((_a = this.options.config) === null || _a === void 0 ? void 0 : _a.hideDefaultTrigger) && this.widgetElement) {
|
|
441
577
|
this.widgetElement.style.display = "none";
|
|
442
578
|
}
|
|
443
579
|
this.state.isOpen = false;
|
|
444
580
|
this.emit("visibility_changed", { visibility: "hidden" });
|
|
445
581
|
}
|
|
446
582
|
toggle() {
|
|
447
|
-
this.
|
|
583
|
+
if (this.chatIframe || this.state.isOpen) {
|
|
584
|
+
this.closeChat();
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
this.openChat();
|
|
588
|
+
}
|
|
448
589
|
}
|
|
449
590
|
minimize() {
|
|
450
|
-
this.
|
|
451
|
-
this.state.isMinimized = true;
|
|
452
|
-
this.emit("minimized", { timestamp: Date.now() });
|
|
591
|
+
this.closeChat();
|
|
453
592
|
}
|
|
454
593
|
maximize() {
|
|
455
|
-
this.
|
|
456
|
-
this.state.isMinimized = false;
|
|
457
|
-
this.emit("maximized", { timestamp: Date.now() });
|
|
594
|
+
this.openChat();
|
|
458
595
|
}
|
|
459
596
|
setCustomerData(data) {
|
|
460
597
|
this.customerData = { ...this.customerData, ...data };
|