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