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