@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.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,160 +56,400 @@ 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
|
-
// Calculate
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
: "52px"
|
|
70
|
-
: isMobile
|
|
71
|
-
? "min(calc(100vw - 2rem), 370px)"
|
|
72
|
-
: widthValue;
|
|
73
|
-
const initialHeight = shouldStartMinimized
|
|
74
|
-
? "52px"
|
|
75
|
-
: isMobile
|
|
76
|
-
? "min(calc(100vh - 2rem), 580px)"
|
|
77
|
-
: heightValue;
|
|
78
|
-
const initialMinWidth = shouldStartMinimized && showTriggerText && hasTriggerText ? "auto" : "";
|
|
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
|
|
79
72
|
container.style.cssText = `
|
|
80
73
|
position: fixed;
|
|
81
74
|
pointer-events: none;
|
|
82
75
|
z-index: 999999;
|
|
83
|
-
width: ${
|
|
84
|
-
height: ${
|
|
85
|
-
${
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
${this.getPositionStyles(config.position || "bottom-right")}
|
|
76
|
+
width: ${triggerWidth};
|
|
77
|
+
height: ${triggerHeight};
|
|
78
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
79
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
80
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
89
81
|
display: ${config.hideDefaultTrigger ? "none" : "block"};
|
|
90
|
-
overflow:
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
box-sizing: border-box;
|
|
91
84
|
`;
|
|
92
|
-
// Create iframe
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
+
};
|
|
195
203
|
this.widgetElement = container;
|
|
196
|
-
this.
|
|
197
|
-
this.
|
|
198
|
-
this.
|
|
204
|
+
this.triggerIframe = triggerIframe;
|
|
205
|
+
this.chatIframe = chatIframe;
|
|
206
|
+
this.state.isMinimized = true;
|
|
207
|
+
this.state.isLoaded = true;
|
|
199
208
|
resolve();
|
|
200
209
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
// Trigger the load event
|
|
211
|
+
triggerIframe.src = "about:blank";
|
|
212
|
+
// Set up communication with trigger iframe
|
|
213
|
+
window.addEventListener("message", (event) => {
|
|
214
|
+
// Check if message is from trigger iframe
|
|
215
|
+
if (event.data.type === "trigger_clicked" &&
|
|
216
|
+
event.source === triggerIframe.contentWindow) {
|
|
217
|
+
this.openChat();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
206
220
|
});
|
|
207
221
|
}
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
buildTriggerHTML(primaryColor, text, showText, customAvatar, avatarType, avatarText) {
|
|
223
|
+
const avatarContent = customAvatar
|
|
224
|
+
? `<img src="${customAvatar}" alt="Avatar" style="width: 32px; height: 32px; border-radius: 50%;" />`
|
|
225
|
+
: avatarType === "text" && avatarText
|
|
226
|
+
? `<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>`
|
|
227
|
+
: `<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>`;
|
|
228
|
+
return `<!DOCTYPE html>
|
|
229
|
+
<html>
|
|
230
|
+
<head>
|
|
231
|
+
<meta charset="UTF-8">
|
|
232
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
233
|
+
<style>
|
|
234
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
235
|
+
body {
|
|
236
|
+
width: 100%;
|
|
237
|
+
height: 100%;
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
242
|
+
overflow: hidden;
|
|
243
|
+
}
|
|
244
|
+
.trigger {
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
gap: ${showText ? "12px" : "0"};
|
|
249
|
+
padding: ${showText ? "10px 20px" : "10px"};
|
|
250
|
+
background: ${primaryColor};
|
|
251
|
+
color: white;
|
|
252
|
+
border-radius: ${showText ? "26px" : "50%"};
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
transition: all 0.3s ease;
|
|
255
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
256
|
+
animation: pulse 2s infinite;
|
|
257
|
+
${showText ? "" : "width: 52px; height: 52px;"}
|
|
258
|
+
}
|
|
259
|
+
.trigger:hover {
|
|
260
|
+
transform: translateY(-2px);
|
|
261
|
+
filter: brightness(1.1);
|
|
262
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
263
|
+
}
|
|
264
|
+
.trigger:active {
|
|
265
|
+
transform: translateY(0);
|
|
266
|
+
}
|
|
267
|
+
.avatar {
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
flex-shrink: 0;
|
|
272
|
+
}
|
|
273
|
+
.text {
|
|
274
|
+
font-size: 14px;
|
|
275
|
+
font-weight: 600;
|
|
276
|
+
white-space: nowrap;
|
|
277
|
+
}
|
|
278
|
+
.badge {
|
|
279
|
+
position: absolute;
|
|
280
|
+
top: -4px;
|
|
281
|
+
right: -4px;
|
|
282
|
+
background: #f44336;
|
|
283
|
+
color: white;
|
|
284
|
+
border-radius: 10px;
|
|
285
|
+
padding: 2px 6px;
|
|
286
|
+
font-size: 11px;
|
|
287
|
+
font-weight: 600;
|
|
288
|
+
min-width: 18px;
|
|
289
|
+
text-align: center;
|
|
290
|
+
display: none;
|
|
291
|
+
animation: pulse 2s infinite;
|
|
292
|
+
}
|
|
293
|
+
.badge.show {
|
|
294
|
+
display: block;
|
|
295
|
+
}
|
|
296
|
+
@keyframes pulse {
|
|
297
|
+
0%, 100% {
|
|
298
|
+
transform: scale(1);
|
|
299
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
300
|
+
}
|
|
301
|
+
50% {
|
|
302
|
+
transform: scale(1.02);
|
|
303
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
</style>
|
|
307
|
+
</head>
|
|
308
|
+
<body>
|
|
309
|
+
<div style="position: relative;">
|
|
310
|
+
<div class="trigger" onclick="handleClick()">
|
|
311
|
+
<div class="avatar">${avatarContent}</div>
|
|
312
|
+
${showText ? `<span class="text">${text}</span>` : ""}
|
|
313
|
+
</div>
|
|
314
|
+
<div class="badge" id="badge">0</div>
|
|
315
|
+
</div>
|
|
316
|
+
<script>
|
|
317
|
+
function handleClick() {
|
|
318
|
+
window.parent.postMessage({ type: 'trigger_clicked' }, '*');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Listen for unread count updates
|
|
322
|
+
window.addEventListener('message', function(event) {
|
|
323
|
+
if (event.data.type === 'updateUnreadCount') {
|
|
324
|
+
const badge = document.getElementById('badge');
|
|
325
|
+
const count = event.data.count || 0;
|
|
326
|
+
badge.textContent = count;
|
|
327
|
+
if (count > 0) {
|
|
328
|
+
badge.classList.add('show');
|
|
329
|
+
} else {
|
|
330
|
+
badge.classList.remove('show');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
</script>
|
|
335
|
+
</body>
|
|
336
|
+
</html>`;
|
|
337
|
+
}
|
|
338
|
+
async openChat() {
|
|
339
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
340
|
+
return;
|
|
341
|
+
if (this.state.isOpen)
|
|
342
|
+
return; // Already open
|
|
343
|
+
const config = this.options.config || {};
|
|
210
344
|
const isMobile = window.innerWidth <= 768;
|
|
211
|
-
const
|
|
345
|
+
const isTablet = window.innerWidth > 768 && window.innerWidth <= 1024;
|
|
346
|
+
const ensureUnits = (value, defaultValue) => {
|
|
347
|
+
if (!value)
|
|
348
|
+
return defaultValue;
|
|
349
|
+
const str = String(value);
|
|
350
|
+
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
351
|
+
return str;
|
|
352
|
+
if (str.match(/^[\d.]+$/))
|
|
353
|
+
return `${str}px`;
|
|
354
|
+
return str;
|
|
355
|
+
};
|
|
356
|
+
const widthValue = ensureUnits(config.width, "350px");
|
|
357
|
+
const heightValue = ensureUnits(config.height, "650px");
|
|
358
|
+
// Calculate chat dimensions
|
|
359
|
+
const chatWidth = isMobile
|
|
360
|
+
? "100vw"
|
|
361
|
+
: isTablet
|
|
362
|
+
? "min(calc(100vw - 3rem), 370px)"
|
|
363
|
+
: widthValue;
|
|
364
|
+
const chatHeight = isMobile
|
|
365
|
+
? "100vh"
|
|
366
|
+
: isTablet
|
|
367
|
+
? "min(calc(100vh - 3rem), 620px)"
|
|
368
|
+
: heightValue;
|
|
369
|
+
// Resize container for chat
|
|
370
|
+
this.widgetElement.style.cssText = `
|
|
371
|
+
position: fixed;
|
|
372
|
+
pointer-events: none;
|
|
373
|
+
z-index: 999999;
|
|
374
|
+
width: ${chatWidth};
|
|
375
|
+
height: ${chatHeight};
|
|
376
|
+
${isMobile ? "max-width: 100vw; max-height: 100vh;" : ""}
|
|
377
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
378
|
+
${this.getPositionStyles(config.position || "bottom-right", false)}
|
|
379
|
+
display: block;
|
|
380
|
+
overflow: visible;
|
|
381
|
+
box-sizing: border-box;
|
|
382
|
+
`;
|
|
383
|
+
// Hide trigger, show chat
|
|
384
|
+
this.triggerIframe.style.display = "none";
|
|
385
|
+
this.chatIframe.style.display = "block";
|
|
386
|
+
this.chatIframe.style.visibility = "visible";
|
|
387
|
+
this.state.isMinimized = false;
|
|
388
|
+
this.state.isOpen = true;
|
|
389
|
+
this.emit("maximized", { timestamp: Date.now() });
|
|
390
|
+
}
|
|
391
|
+
closeChat() {
|
|
392
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
393
|
+
return;
|
|
394
|
+
const config = this.options.config || {};
|
|
395
|
+
const showTriggerText = config.showTriggerText !== false;
|
|
396
|
+
const hasTriggerText = !!config.triggerText;
|
|
397
|
+
// Hide chat iframe
|
|
398
|
+
this.chatIframe.style.display = "none";
|
|
399
|
+
this.chatIframe.style.visibility = "hidden";
|
|
400
|
+
// Resize container back to trigger size
|
|
401
|
+
const triggerWidth = showTriggerText && hasTriggerText ? "auto" : "52px";
|
|
402
|
+
const triggerHeight = "52px";
|
|
403
|
+
const triggerMinWidth = showTriggerText && hasTriggerText ? "auto" : "";
|
|
404
|
+
this.widgetElement.style.cssText = `
|
|
405
|
+
position: fixed;
|
|
406
|
+
pointer-events: none;
|
|
407
|
+
z-index: 999999;
|
|
408
|
+
width: ${triggerWidth};
|
|
409
|
+
height: ${triggerHeight};
|
|
410
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
411
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
412
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
413
|
+
display: block;
|
|
414
|
+
overflow: hidden;
|
|
415
|
+
box-sizing: border-box;
|
|
416
|
+
`;
|
|
417
|
+
// Show trigger again
|
|
418
|
+
this.triggerIframe.style.display = "block";
|
|
419
|
+
this.state.isMinimized = true;
|
|
420
|
+
this.state.isOpen = false;
|
|
421
|
+
this.emit("minimized", { timestamp: Date.now() });
|
|
422
|
+
}
|
|
423
|
+
getPositionStyles(position, isMinimized = false) {
|
|
424
|
+
// Responsive positioning following mobile-first best practices
|
|
425
|
+
const viewportWidth = window.innerWidth;
|
|
426
|
+
const isMobile = viewportWidth <= 768;
|
|
427
|
+
const isTablet = viewportWidth > 768 && viewportWidth <= 1024;
|
|
428
|
+
// For extra small devices (Galaxy S8+, iPhone SE, etc.), use minimal or no margins
|
|
429
|
+
// For mobile, use small margins for breathing room
|
|
430
|
+
// For tablet/desktop, use larger margins for floating appearance
|
|
431
|
+
let margin;
|
|
432
|
+
if (isMinimized) {
|
|
433
|
+
// Minimized button always needs some space from edges
|
|
434
|
+
margin = isMobile ? "12px" : "20px";
|
|
435
|
+
}
|
|
436
|
+
else if (isMobile) {
|
|
437
|
+
// Full screen on all mobile devices (no margins)
|
|
438
|
+
margin = "0";
|
|
439
|
+
}
|
|
440
|
+
else if (isTablet) {
|
|
441
|
+
// Medium margins on tablets
|
|
442
|
+
margin = "16px";
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
// Larger margins on desktop for floating effect
|
|
446
|
+
margin = "24px";
|
|
447
|
+
}
|
|
448
|
+
// On mobile screens when not minimized, position at edges for full coverage
|
|
449
|
+
if (isMobile && !isMinimized) {
|
|
450
|
+
return `top: 0; left: 0; right: 0; bottom: 0;`;
|
|
451
|
+
}
|
|
452
|
+
// Standard positioning for larger screens or minimized state
|
|
212
453
|
switch (position) {
|
|
213
454
|
case "bottom-right":
|
|
214
455
|
return `bottom: ${margin}; right: ${margin};`;
|
|
@@ -225,7 +466,7 @@ class BuniChatWidget {
|
|
|
225
466
|
setupPostMessageAPI(iframe) {
|
|
226
467
|
// Listen for messages from the iframe
|
|
227
468
|
window.addEventListener("message", (event) => {
|
|
228
|
-
var _a
|
|
469
|
+
var _a;
|
|
229
470
|
// Verify origin for security
|
|
230
471
|
if (event.origin !== this.getBaseUrl()) {
|
|
231
472
|
return;
|
|
@@ -256,60 +497,15 @@ class BuniChatWidget {
|
|
|
256
497
|
}
|
|
257
498
|
break;
|
|
258
499
|
case "minimized":
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Resize container to trigger button size when minimized
|
|
262
|
-
this.widgetElement.style.width = data.dimensions.width;
|
|
263
|
-
this.widgetElement.style.height = data.dimensions.height;
|
|
264
|
-
this.widgetElement.style.overflow = "hidden";
|
|
265
|
-
if (data.dimensions.minWidth) {
|
|
266
|
-
this.widgetElement.style.minWidth = data.dimensions.minWidth;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (this.iframe) {
|
|
270
|
-
// Use circular border for icon-only, rounded for text button
|
|
271
|
-
this.iframe.style.borderRadius =
|
|
272
|
-
((_a = data.dimensions) === null || _a === void 0 ? void 0 : _a.width) === ((_b = data.dimensions) === null || _b === void 0 ? void 0 : _b.height)
|
|
273
|
-
? "50%"
|
|
274
|
-
: "26px";
|
|
275
|
-
}
|
|
276
|
-
this.emit("minimized", data);
|
|
500
|
+
// User clicked minimize in chat - close chat and show trigger
|
|
501
|
+
this.closeChat();
|
|
277
502
|
break;
|
|
278
|
-
case "
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const config = this.options.config || {};
|
|
284
|
-
if (isMobile) {
|
|
285
|
-
// On mobile, enforce responsive sizing
|
|
286
|
-
this.widgetElement.style.width = "min(calc(100vw - 2rem), 370px)";
|
|
287
|
-
this.widgetElement.style.height =
|
|
288
|
-
"min(calc(100vh - 2rem), 680px)";
|
|
289
|
-
this.widgetElement.style.maxWidth = "370px";
|
|
290
|
-
this.widgetElement.style.maxHeight = "680px";
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
// On desktop, respect custom dimensions
|
|
294
|
-
const ensureUnits = (value, defaultValue) => {
|
|
295
|
-
if (!value)
|
|
296
|
-
return defaultValue;
|
|
297
|
-
const str = String(value);
|
|
298
|
-
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
299
|
-
return str;
|
|
300
|
-
if (str.match(/^[\d.]+$/))
|
|
301
|
-
return `${str}px`;
|
|
302
|
-
return str;
|
|
303
|
-
};
|
|
304
|
-
this.widgetElement.style.width = ensureUnits(config.width, "350px");
|
|
305
|
-
this.widgetElement.style.height = ensureUnits(config.height, "650px");
|
|
306
|
-
}
|
|
307
|
-
this.widgetElement.style.overflow = "visible";
|
|
308
|
-
}
|
|
309
|
-
if (this.iframe) {
|
|
310
|
-
this.iframe.style.borderRadius = "12px";
|
|
503
|
+
case "new_unread_message":
|
|
504
|
+
// Update unread count in trigger
|
|
505
|
+
this.state.unreadCount++;
|
|
506
|
+
if ((_a = this.triggerIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
507
|
+
this.triggerIframe.contentWindow.postMessage({ type: "updateUnreadCount", count: this.state.unreadCount }, "*");
|
|
311
508
|
}
|
|
312
|
-
this.emit("maximized", data);
|
|
313
509
|
break;
|
|
314
510
|
case "customer_data_updated":
|
|
315
511
|
this.customerData = data;
|
|
@@ -330,8 +526,8 @@ class BuniChatWidget {
|
|
|
330
526
|
}
|
|
331
527
|
postMessageToWidget(type, data) {
|
|
332
528
|
var _a;
|
|
333
|
-
if (
|
|
334
|
-
|
|
529
|
+
if ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
530
|
+
this.chatIframe.contentWindow.postMessage({ type, data }, this.getBaseUrl());
|
|
335
531
|
}
|
|
336
532
|
}
|
|
337
533
|
getBaseUrl() {
|
|
@@ -344,44 +540,49 @@ class BuniChatWidget {
|
|
|
344
540
|
this.widgetElement.remove();
|
|
345
541
|
this.widgetElement = null;
|
|
346
542
|
}
|
|
543
|
+
this.triggerIframe = null;
|
|
544
|
+
this.chatIframe = null;
|
|
347
545
|
this.eventListeners.clear();
|
|
348
546
|
this.state.isLoaded = false;
|
|
349
547
|
this.customerData = null;
|
|
350
548
|
this.sessionVariables = null;
|
|
351
549
|
}
|
|
352
550
|
show() {
|
|
353
|
-
// Show the
|
|
354
|
-
if (this.widgetElement
|
|
551
|
+
// Show the widget container if it was hidden (hideDefaultTrigger mode)
|
|
552
|
+
if (this.widgetElement) {
|
|
355
553
|
this.widgetElement.style.display = "block";
|
|
356
554
|
}
|
|
357
|
-
|
|
358
|
-
this.state.isOpen
|
|
359
|
-
|
|
360
|
-
|
|
555
|
+
// Open chat if not already open
|
|
556
|
+
if (!this.chatIframe && !this.state.isOpen) {
|
|
557
|
+
this.openChat();
|
|
558
|
+
}
|
|
361
559
|
}
|
|
362
560
|
hide() {
|
|
363
561
|
var _a;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
562
|
+
// Close chat if open
|
|
563
|
+
if (this.chatIframe) {
|
|
564
|
+
this.closeChat();
|
|
565
|
+
}
|
|
566
|
+
// If hideDefaultTrigger is enabled, completely hide the container
|
|
567
|
+
if (((_a = this.options.config) === null || _a === void 0 ? void 0 : _a.hideDefaultTrigger) && this.widgetElement) {
|
|
368
568
|
this.widgetElement.style.display = "none";
|
|
369
569
|
}
|
|
370
570
|
this.state.isOpen = false;
|
|
371
571
|
this.emit("visibility_changed", { visibility: "hidden" });
|
|
372
572
|
}
|
|
373
573
|
toggle() {
|
|
374
|
-
this.
|
|
574
|
+
if (this.chatIframe || this.state.isOpen) {
|
|
575
|
+
this.closeChat();
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
this.openChat();
|
|
579
|
+
}
|
|
375
580
|
}
|
|
376
581
|
minimize() {
|
|
377
|
-
this.
|
|
378
|
-
this.state.isMinimized = true;
|
|
379
|
-
this.emit("minimized", { timestamp: Date.now() });
|
|
582
|
+
this.closeChat();
|
|
380
583
|
}
|
|
381
584
|
maximize() {
|
|
382
|
-
this.
|
|
383
|
-
this.state.isMinimized = false;
|
|
384
|
-
this.emit("maximized", { timestamp: Date.now() });
|
|
585
|
+
this.openChat();
|
|
385
586
|
}
|
|
386
587
|
setCustomerData(data) {
|
|
387
588
|
this.customerData = { ...this.customerData, ...data };
|