@buni.ai/chatbot-angular 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +397 -260
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +397 -260
- package/dist/index.js.map +1 -1
- package/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,193 +94,382 @@ 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
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
? showTriggerText && hasTriggerText
|
|
108
|
-
? "auto"
|
|
109
|
-
: "52px"
|
|
110
|
-
: isExtraSmall
|
|
111
|
-
? "100vw" // Full width on very small devices
|
|
112
|
-
: isMobile
|
|
113
|
-
? "min(100vw, 370px)" // Constrained width on mobile
|
|
114
|
-
: isTablet
|
|
115
|
-
? "min(calc(100vw - 3rem), 370px)" // Slightly smaller on tablets
|
|
116
|
-
: widthValue; // Custom width on desktop
|
|
117
|
-
const initialHeight = shouldStartMinimized
|
|
118
|
-
? "52px"
|
|
119
|
-
: isExtraSmall
|
|
120
|
-
? "100vh" // Full height on very small devices
|
|
121
|
-
: isMobile
|
|
122
|
-
? "min(100vh, 600px)" // Constrained height on mobile
|
|
123
|
-
: isTablet
|
|
124
|
-
? "min(calc(100vh - 3rem), 620px)" // Slightly smaller on tablets
|
|
125
|
-
: heightValue; // Custom height on desktop
|
|
126
|
-
const initialMinWidth = shouldStartMinimized && showTriggerText && hasTriggerText ? "auto" : "";
|
|
127
|
-
// Use CSS custom properties for dynamic styling
|
|
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, initially hidden until connection is ready
|
|
128
110
|
container.style.cssText = `
|
|
129
111
|
position: fixed;
|
|
130
112
|
pointer-events: none;
|
|
131
113
|
z-index: 999999;
|
|
132
|
-
width: ${
|
|
133
|
-
height: ${
|
|
134
|
-
${
|
|
135
|
-
${isMobile && !shouldStartMinimized ? "max-width: 100vw; max-height: 100vh;" : ""}
|
|
114
|
+
width: ${triggerWidth};
|
|
115
|
+
height: ${triggerHeight};
|
|
116
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
136
117
|
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
137
|
-
${this.getPositionStyles(config.position || "bottom-right",
|
|
138
|
-
display:
|
|
139
|
-
overflow:
|
|
118
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
119
|
+
display: none;
|
|
120
|
+
overflow: hidden;
|
|
140
121
|
box-sizing: border-box;
|
|
141
122
|
`;
|
|
142
|
-
// Create iframe
|
|
143
|
-
const
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
else {
|
|
261
|
-
iframe.style.boxShadow = "0 8px 32px rgba(0,0,0,0.15)";
|
|
262
|
-
}
|
|
263
|
-
iframe.setAttribute("allow", "clipboard-write");
|
|
264
|
-
iframe.setAttribute("title", "BuniAI Chat Widget");
|
|
265
|
-
iframe.onload = () => {
|
|
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
|
+
};
|
|
266
241
|
this.widgetElement = container;
|
|
267
|
-
this.
|
|
268
|
-
this.
|
|
269
|
-
this.
|
|
242
|
+
this.triggerIframe = triggerIframe;
|
|
243
|
+
this.chatIframe = chatIframe;
|
|
244
|
+
this.state.isMinimized = true;
|
|
245
|
+
this.state.isLoaded = true;
|
|
270
246
|
resolve();
|
|
271
247
|
};
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
248
|
+
// Trigger the load event
|
|
249
|
+
triggerIframe.src = "about:blank";
|
|
250
|
+
// Set up communication with trigger iframe
|
|
251
|
+
window.addEventListener("message", (event) => {
|
|
252
|
+
var _a;
|
|
253
|
+
// Check if message is from trigger iframe
|
|
254
|
+
if (event.data.type === "trigger_clicked" &&
|
|
255
|
+
event.source === triggerIframe.contentWindow) {
|
|
256
|
+
this.openChat();
|
|
257
|
+
}
|
|
258
|
+
// Check if message is connection_ready from chat iframe
|
|
259
|
+
if (event.data.type === "connection_ready" &&
|
|
260
|
+
event.source === ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow)) {
|
|
261
|
+
// Connection is ready, show the trigger button
|
|
262
|
+
if (container && !config.hideDefaultTrigger) {
|
|
263
|
+
container.style.display = "block";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
277
267
|
});
|
|
278
268
|
}
|
|
269
|
+
buildTriggerHTML(primaryColor, text, showText, customAvatar, avatarType, avatarText) {
|
|
270
|
+
const avatarContent = customAvatar
|
|
271
|
+
? `<img src="${customAvatar}" alt="Avatar" style="width: 32px; height: 32px; border-radius: 50%;" />`
|
|
272
|
+
: avatarType === "text" && avatarText
|
|
273
|
+
? `<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>`
|
|
274
|
+
: `<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>`;
|
|
275
|
+
return `<!DOCTYPE html>
|
|
276
|
+
<html>
|
|
277
|
+
<head>
|
|
278
|
+
<meta charset="UTF-8">
|
|
279
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
280
|
+
<style>
|
|
281
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
282
|
+
body {
|
|
283
|
+
width: 100%;
|
|
284
|
+
height: 100%;
|
|
285
|
+
display: flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
justify-content: center;
|
|
288
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
}
|
|
291
|
+
.trigger {
|
|
292
|
+
display: flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
gap: ${showText ? "12px" : "0"};
|
|
296
|
+
padding: ${showText ? "10px 20px" : "10px"};
|
|
297
|
+
background: ${primaryColor};
|
|
298
|
+
color: white;
|
|
299
|
+
border-radius: ${showText ? "26px" : "50%"};
|
|
300
|
+
cursor: pointer;
|
|
301
|
+
transition: all 0.3s ease;
|
|
302
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
303
|
+
animation: pulse 2s infinite;
|
|
304
|
+
${showText ? "" : "width: 52px; height: 52px;"}
|
|
305
|
+
}
|
|
306
|
+
.trigger:hover {
|
|
307
|
+
transform: translateY(-2px);
|
|
308
|
+
filter: brightness(1.1);
|
|
309
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
310
|
+
}
|
|
311
|
+
.trigger:active {
|
|
312
|
+
transform: translateY(0);
|
|
313
|
+
}
|
|
314
|
+
.avatar {
|
|
315
|
+
display: flex;
|
|
316
|
+
align-items: center;
|
|
317
|
+
justify-content: center;
|
|
318
|
+
flex-shrink: 0;
|
|
319
|
+
}
|
|
320
|
+
.text {
|
|
321
|
+
font-size: 14px;
|
|
322
|
+
font-weight: 600;
|
|
323
|
+
white-space: nowrap;
|
|
324
|
+
}
|
|
325
|
+
.badge {
|
|
326
|
+
position: absolute;
|
|
327
|
+
top: -4px;
|
|
328
|
+
right: -4px;
|
|
329
|
+
background: #f44336;
|
|
330
|
+
color: white;
|
|
331
|
+
border-radius: 10px;
|
|
332
|
+
padding: 2px 6px;
|
|
333
|
+
font-size: 11px;
|
|
334
|
+
font-weight: 600;
|
|
335
|
+
min-width: 18px;
|
|
336
|
+
text-align: center;
|
|
337
|
+
display: none;
|
|
338
|
+
animation: pulse 2s infinite;
|
|
339
|
+
}
|
|
340
|
+
.badge.show {
|
|
341
|
+
display: block;
|
|
342
|
+
}
|
|
343
|
+
@keyframes pulse {
|
|
344
|
+
0%, 100% {
|
|
345
|
+
transform: scale(1);
|
|
346
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
347
|
+
}
|
|
348
|
+
50% {
|
|
349
|
+
transform: scale(1.02);
|
|
350
|
+
box-shadow: 0 6px 24px ${primaryColor}99;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
</style>
|
|
354
|
+
</head>
|
|
355
|
+
<body>
|
|
356
|
+
<div style="position: relative;">
|
|
357
|
+
<div class="trigger" onclick="handleClick()">
|
|
358
|
+
<div class="avatar">${avatarContent}</div>
|
|
359
|
+
${showText ? `<span class="text">${text}</span>` : ""}
|
|
360
|
+
</div>
|
|
361
|
+
<div class="badge" id="badge">0</div>
|
|
362
|
+
</div>
|
|
363
|
+
<script>
|
|
364
|
+
function handleClick() {
|
|
365
|
+
window.parent.postMessage({ type: 'trigger_clicked' }, '*');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Listen for unread count updates
|
|
369
|
+
window.addEventListener('message', function(event) {
|
|
370
|
+
if (event.data.type === 'updateUnreadCount') {
|
|
371
|
+
const badge = document.getElementById('badge');
|
|
372
|
+
const count = event.data.count || 0;
|
|
373
|
+
badge.textContent = count;
|
|
374
|
+
if (count > 0) {
|
|
375
|
+
badge.classList.add('show');
|
|
376
|
+
} else {
|
|
377
|
+
badge.classList.remove('show');
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
</script>
|
|
382
|
+
</body>
|
|
383
|
+
</html>`;
|
|
384
|
+
}
|
|
385
|
+
async openChat() {
|
|
386
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
387
|
+
return;
|
|
388
|
+
if (this.state.isOpen)
|
|
389
|
+
return; // Already open
|
|
390
|
+
const config = this.options.config || {};
|
|
391
|
+
const isMobile = window.innerWidth <= 768;
|
|
392
|
+
const isTablet = window.innerWidth > 768 && window.innerWidth <= 1024;
|
|
393
|
+
const ensureUnits = (value, defaultValue) => {
|
|
394
|
+
if (!value)
|
|
395
|
+
return defaultValue;
|
|
396
|
+
const str = String(value);
|
|
397
|
+
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
398
|
+
return str;
|
|
399
|
+
if (str.match(/^[\d.]+$/))
|
|
400
|
+
return `${str}px`;
|
|
401
|
+
return str;
|
|
402
|
+
};
|
|
403
|
+
const widthValue = ensureUnits(config.width, "350px");
|
|
404
|
+
const heightValue = ensureUnits(config.height, "650px");
|
|
405
|
+
// Calculate chat dimensions
|
|
406
|
+
const chatWidth = isMobile
|
|
407
|
+
? "100vw"
|
|
408
|
+
: isTablet
|
|
409
|
+
? "min(calc(100vw - 3rem), 370px)"
|
|
410
|
+
: widthValue;
|
|
411
|
+
const chatHeight = isMobile
|
|
412
|
+
? "100vh"
|
|
413
|
+
: isTablet
|
|
414
|
+
? "min(calc(100vh - 3rem), 620px)"
|
|
415
|
+
: heightValue;
|
|
416
|
+
// Resize container for chat
|
|
417
|
+
this.widgetElement.style.cssText = `
|
|
418
|
+
position: fixed;
|
|
419
|
+
pointer-events: none;
|
|
420
|
+
z-index: 999999;
|
|
421
|
+
width: ${chatWidth};
|
|
422
|
+
height: ${chatHeight};
|
|
423
|
+
${isMobile ? "max-width: 100vw; max-height: 100vh;" : ""}
|
|
424
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
425
|
+
${this.getPositionStyles(config.position || "bottom-right", false)}
|
|
426
|
+
display: block;
|
|
427
|
+
overflow: visible;
|
|
428
|
+
box-sizing: border-box;
|
|
429
|
+
`;
|
|
430
|
+
// Hide trigger, show chat
|
|
431
|
+
this.triggerIframe.style.display = "none";
|
|
432
|
+
this.chatIframe.style.display = "block";
|
|
433
|
+
this.chatIframe.style.visibility = "visible";
|
|
434
|
+
this.state.isMinimized = false;
|
|
435
|
+
this.state.isOpen = true;
|
|
436
|
+
this.emit("maximized", { timestamp: Date.now() });
|
|
437
|
+
}
|
|
438
|
+
closeChat() {
|
|
439
|
+
if (!this.chatIframe || !this.widgetElement || !this.triggerIframe)
|
|
440
|
+
return;
|
|
441
|
+
const config = this.options.config || {};
|
|
442
|
+
const showTriggerText = config.showTriggerText !== false;
|
|
443
|
+
const hasTriggerText = !!config.triggerText;
|
|
444
|
+
// Hide chat iframe
|
|
445
|
+
this.chatIframe.style.display = "none";
|
|
446
|
+
this.chatIframe.style.visibility = "hidden";
|
|
447
|
+
// Resize container back to trigger size
|
|
448
|
+
const triggerWidth = showTriggerText && hasTriggerText ? "auto" : "52px";
|
|
449
|
+
const triggerHeight = "52px";
|
|
450
|
+
const triggerMinWidth = showTriggerText && hasTriggerText ? "auto" : "";
|
|
451
|
+
this.widgetElement.style.cssText = `
|
|
452
|
+
position: fixed;
|
|
453
|
+
pointer-events: none;
|
|
454
|
+
z-index: 999999;
|
|
455
|
+
width: ${triggerWidth};
|
|
456
|
+
height: ${triggerHeight};
|
|
457
|
+
${triggerMinWidth ? `min-width: ${triggerMinWidth};` : ""}
|
|
458
|
+
transition: width 0.3s ease, height 0.3s ease, border-radius 0.3s ease;
|
|
459
|
+
${this.getPositionStyles(config.position || "bottom-right", true)}
|
|
460
|
+
display: block;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
box-sizing: border-box;
|
|
463
|
+
`;
|
|
464
|
+
// Show trigger again
|
|
465
|
+
this.triggerIframe.style.display = "block";
|
|
466
|
+
this.state.isMinimized = true;
|
|
467
|
+
this.state.isOpen = false;
|
|
468
|
+
this.emit("minimized", { timestamp: Date.now() });
|
|
469
|
+
}
|
|
279
470
|
getPositionStyles(position, isMinimized = false) {
|
|
280
471
|
// Responsive positioning following mobile-first best practices
|
|
281
472
|
const viewportWidth = window.innerWidth;
|
|
282
|
-
const isExtraSmall = viewportWidth <= 375;
|
|
283
473
|
const isMobile = viewportWidth <= 768;
|
|
284
474
|
const isTablet = viewportWidth > 768 && viewportWidth <= 1024;
|
|
285
475
|
// For extra small devices (Galaxy S8+, iPhone SE, etc.), use minimal or no margins
|
|
@@ -290,13 +480,9 @@ class BuniChatWidget {
|
|
|
290
480
|
// Minimized button always needs some space from edges
|
|
291
481
|
margin = isMobile ? "12px" : "20px";
|
|
292
482
|
}
|
|
293
|
-
else if (isExtraSmall) {
|
|
294
|
-
// Full screen on extra small devices (no margins)
|
|
295
|
-
margin = "0";
|
|
296
|
-
}
|
|
297
483
|
else if (isMobile) {
|
|
298
|
-
//
|
|
299
|
-
margin = "
|
|
484
|
+
// Full screen on all mobile devices (no margins)
|
|
485
|
+
margin = "0";
|
|
300
486
|
}
|
|
301
487
|
else if (isTablet) {
|
|
302
488
|
// Medium margins on tablets
|
|
@@ -306,18 +492,9 @@ class BuniChatWidget {
|
|
|
306
492
|
// Larger margins on desktop for floating effect
|
|
307
493
|
margin = "24px";
|
|
308
494
|
}
|
|
309
|
-
// On
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
case "bottom-right":
|
|
313
|
-
case "bottom-left":
|
|
314
|
-
return `bottom: 0; left: 0; right: 0;`;
|
|
315
|
-
case "top-right":
|
|
316
|
-
case "top-left":
|
|
317
|
-
return `top: 0; left: 0; right: 0;`;
|
|
318
|
-
default:
|
|
319
|
-
return `bottom: 0; left: 0; right: 0;`;
|
|
320
|
-
}
|
|
495
|
+
// On mobile screens when not minimized, position at edges for full coverage
|
|
496
|
+
if (isMobile && !isMinimized) {
|
|
497
|
+
return `top: 0; left: 0; right: 0; bottom: 0;`;
|
|
321
498
|
}
|
|
322
499
|
// Standard positioning for larger screens or minimized state
|
|
323
500
|
switch (position) {
|
|
@@ -336,7 +513,7 @@ class BuniChatWidget {
|
|
|
336
513
|
setupPostMessageAPI(iframe) {
|
|
337
514
|
// Listen for messages from the iframe
|
|
338
515
|
window.addEventListener("message", (event) => {
|
|
339
|
-
var _a
|
|
516
|
+
var _a;
|
|
340
517
|
// Verify origin for security
|
|
341
518
|
if (event.origin !== this.getBaseUrl()) {
|
|
342
519
|
return;
|
|
@@ -367,60 +544,15 @@ class BuniChatWidget {
|
|
|
367
544
|
}
|
|
368
545
|
break;
|
|
369
546
|
case "minimized":
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
// Resize container to trigger button size when minimized
|
|
373
|
-
this.widgetElement.style.width = data.dimensions.width;
|
|
374
|
-
this.widgetElement.style.height = data.dimensions.height;
|
|
375
|
-
this.widgetElement.style.overflow = "hidden";
|
|
376
|
-
if (data.dimensions.minWidth) {
|
|
377
|
-
this.widgetElement.style.minWidth = data.dimensions.minWidth;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (this.iframe) {
|
|
381
|
-
// Use circular border for icon-only, rounded for text button
|
|
382
|
-
this.iframe.style.borderRadius =
|
|
383
|
-
((_a = data.dimensions) === null || _a === void 0 ? void 0 : _a.width) === ((_b = data.dimensions) === null || _b === void 0 ? void 0 : _b.height)
|
|
384
|
-
? "50%"
|
|
385
|
-
: "26px";
|
|
386
|
-
}
|
|
387
|
-
this.emit("minimized", data);
|
|
547
|
+
// User clicked minimize in chat - close chat and show trigger
|
|
548
|
+
this.closeChat();
|
|
388
549
|
break;
|
|
389
|
-
case "
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const config = this.options.config || {};
|
|
395
|
-
if (isMobile) {
|
|
396
|
-
// On mobile, enforce responsive sizing
|
|
397
|
-
this.widgetElement.style.width = "min(calc(100vw - 2rem), 370px)";
|
|
398
|
-
this.widgetElement.style.height =
|
|
399
|
-
"min(calc(100vh - 2rem), 680px)";
|
|
400
|
-
this.widgetElement.style.maxWidth = "370px";
|
|
401
|
-
this.widgetElement.style.maxHeight = "680px";
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
// On desktop, respect custom dimensions
|
|
405
|
-
const ensureUnits = (value, defaultValue) => {
|
|
406
|
-
if (!value)
|
|
407
|
-
return defaultValue;
|
|
408
|
-
const str = String(value);
|
|
409
|
-
if (str.match(/^[\d.]+\s*(px|em|rem|%|vh|vw)$/))
|
|
410
|
-
return str;
|
|
411
|
-
if (str.match(/^[\d.]+$/))
|
|
412
|
-
return `${str}px`;
|
|
413
|
-
return str;
|
|
414
|
-
};
|
|
415
|
-
this.widgetElement.style.width = ensureUnits(config.width, "350px");
|
|
416
|
-
this.widgetElement.style.height = ensureUnits(config.height, "650px");
|
|
417
|
-
}
|
|
418
|
-
this.widgetElement.style.overflow = "visible";
|
|
419
|
-
}
|
|
420
|
-
if (this.iframe) {
|
|
421
|
-
this.iframe.style.borderRadius = "12px";
|
|
550
|
+
case "new_unread_message":
|
|
551
|
+
// Update unread count in trigger
|
|
552
|
+
this.state.unreadCount++;
|
|
553
|
+
if ((_a = this.triggerIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
554
|
+
this.triggerIframe.contentWindow.postMessage({ type: "updateUnreadCount", count: this.state.unreadCount }, "*");
|
|
422
555
|
}
|
|
423
|
-
this.emit("maximized", data);
|
|
424
556
|
break;
|
|
425
557
|
case "customer_data_updated":
|
|
426
558
|
this.customerData = data;
|
|
@@ -441,8 +573,8 @@ class BuniChatWidget {
|
|
|
441
573
|
}
|
|
442
574
|
postMessageToWidget(type, data) {
|
|
443
575
|
var _a;
|
|
444
|
-
if (
|
|
445
|
-
|
|
576
|
+
if ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
|
|
577
|
+
this.chatIframe.contentWindow.postMessage({ type, data }, this.getBaseUrl());
|
|
446
578
|
}
|
|
447
579
|
}
|
|
448
580
|
getBaseUrl() {
|
|
@@ -455,44 +587,49 @@ class BuniChatWidget {
|
|
|
455
587
|
this.widgetElement.remove();
|
|
456
588
|
this.widgetElement = null;
|
|
457
589
|
}
|
|
590
|
+
this.triggerIframe = null;
|
|
591
|
+
this.chatIframe = null;
|
|
458
592
|
this.eventListeners.clear();
|
|
459
593
|
this.state.isLoaded = false;
|
|
460
594
|
this.customerData = null;
|
|
461
595
|
this.sessionVariables = null;
|
|
462
596
|
}
|
|
463
597
|
show() {
|
|
464
|
-
// Show the
|
|
465
|
-
if (this.widgetElement
|
|
598
|
+
// Show the widget container if it was hidden (hideDefaultTrigger mode)
|
|
599
|
+
if (this.widgetElement) {
|
|
466
600
|
this.widgetElement.style.display = "block";
|
|
467
601
|
}
|
|
468
|
-
|
|
469
|
-
this.state.isOpen
|
|
470
|
-
|
|
471
|
-
|
|
602
|
+
// Open chat if not already open
|
|
603
|
+
if (!this.chatIframe && !this.state.isOpen) {
|
|
604
|
+
this.openChat();
|
|
605
|
+
}
|
|
472
606
|
}
|
|
473
607
|
hide() {
|
|
474
608
|
var _a;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
609
|
+
// Close chat if open
|
|
610
|
+
if (this.chatIframe) {
|
|
611
|
+
this.closeChat();
|
|
612
|
+
}
|
|
613
|
+
// If hideDefaultTrigger is enabled, completely hide the container
|
|
614
|
+
if (((_a = this.options.config) === null || _a === void 0 ? void 0 : _a.hideDefaultTrigger) && this.widgetElement) {
|
|
479
615
|
this.widgetElement.style.display = "none";
|
|
480
616
|
}
|
|
481
617
|
this.state.isOpen = false;
|
|
482
618
|
this.emit("visibility_changed", { visibility: "hidden" });
|
|
483
619
|
}
|
|
484
620
|
toggle() {
|
|
485
|
-
this.
|
|
621
|
+
if (this.chatIframe || this.state.isOpen) {
|
|
622
|
+
this.closeChat();
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
this.openChat();
|
|
626
|
+
}
|
|
486
627
|
}
|
|
487
628
|
minimize() {
|
|
488
|
-
this.
|
|
489
|
-
this.state.isMinimized = true;
|
|
490
|
-
this.emit("minimized", { timestamp: Date.now() });
|
|
629
|
+
this.closeChat();
|
|
491
630
|
}
|
|
492
631
|
maximize() {
|
|
493
|
-
this.
|
|
494
|
-
this.state.isMinimized = false;
|
|
495
|
-
this.emit("maximized", { timestamp: Date.now() });
|
|
632
|
+
this.openChat();
|
|
496
633
|
}
|
|
497
634
|
setCustomerData(data) {
|
|
498
635
|
this.customerData = { ...this.customerData, ...data };
|