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