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