@product7/product7-js 0.5.5 → 0.5.7
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/README.md +10 -10
- package/dist/README.md +10 -10
- package/dist/product7-js.js +7261 -7100
- package/dist/product7-js.js.map +1 -1
- package/dist/product7-js.min.js +1 -1
- package/dist/product7-js.min.js.map +1 -1
- package/package.json +1 -1
- package/src/api/services/{WebChatService.js → LiveChatService.js} +14 -14
- package/src/core/APIService.js +15 -15
- package/src/core/Product7.js +9 -4
- package/src/core/WebSocketService.js +1 -1
- package/src/docs/api.md +8 -8
- package/src/docs/example.md +9 -9
- package/src/docs/framework-integrations.md +3 -3
- package/src/index.js +38 -37
- package/src/styles/base.js +8 -8
- package/src/styles/{web-chat-components.js → liveChat-components.js} +114 -114
- package/src/styles/{web-chat-core.js → liveChat-core.js} +96 -31
- package/src/styles/{web-chat-features.js → liveChat-features.js} +20 -20
- package/src/styles/{web-chat-views.js → liveChat-views.js} +137 -137
- package/src/styles/liveChat.js +17 -0
- package/src/styles/{webChatCustomStyles.js → liveChatCustomStyles.js} +16 -16
- package/src/styles/styles.js +3 -3
- package/src/widgets/BaseWidget.js +2 -2
- package/src/widgets/ChangelogWidget.js +3 -3
- package/src/widgets/{WebChatWidget.js → LiveChatWidget.js} +169 -165
- package/src/widgets/SurveyWidget.js +7 -7
- package/src/widgets/WidgetFactory.js +2 -2
- package/src/widgets/{web-chat/WebChatState.js → liveChat/LiveChatState.js} +1 -1
- package/src/widgets/{web-chat/components/WebChatLauncher.js → liveChat/components/LiveChatLauncher.js} +16 -16
- package/src/widgets/{web-chat/components/WebChatPanel.js → liveChat/components/LiveChatPanel.js} +41 -10
- package/src/widgets/{web-chat → liveChat}/components/NavigationTabs.js +16 -16
- package/src/widgets/{web-chat → liveChat}/views/ChangelogView.js +17 -17
- package/src/widgets/{web-chat → liveChat}/views/ChatView.js +153 -95
- package/src/widgets/{web-chat → liveChat}/views/ConversationsView.js +24 -24
- package/src/widgets/{web-chat → liveChat}/views/HelpView.js +32 -32
- package/src/widgets/{web-chat → liveChat}/views/HomeView.js +52 -52
- package/src/widgets/{web-chat → liveChat}/views/PreChatFormView.js +15 -18
- package/types/index.d.ts +8 -9
- package/src/styles/web-chat.js +0 -17
|
@@ -11,18 +11,19 @@
|
|
|
11
11
|
this._pendingAttachments = [];
|
|
12
12
|
this._emojiPickerOpen = false;
|
|
13
13
|
this._emojiOutsideHandler = null;
|
|
14
|
+
this._menuOutsideHandler = null;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
render() {
|
|
17
18
|
this.element = document.createElement('div');
|
|
18
|
-
this.element.className = '
|
|
19
|
+
this.element.className = 'liveChat-view liveChat-chat-view';
|
|
19
20
|
|
|
20
21
|
this._updateContent();
|
|
21
22
|
|
|
22
23
|
this._unsubscribe = this.state.subscribe((type, data) => {
|
|
23
24
|
if (type === 'connectionChange') {
|
|
24
25
|
const banner = this.element?.querySelector(
|
|
25
|
-
'.
|
|
26
|
+
'.liveChat-connection-banner'
|
|
26
27
|
);
|
|
27
28
|
if (banner) {
|
|
28
29
|
banner.style.display = data.connected ? 'none' : 'flex';
|
|
@@ -85,53 +86,68 @@
|
|
|
85
86
|
const teamName = this.state.teamName || 'Support';
|
|
86
87
|
const headerAvatarHtml = logoUrl
|
|
87
88
|
? `<img src="${this._escapeHtml(logoUrl)}" alt="${this._escapeHtml(teamName)}" />`
|
|
88
|
-
: `<iconify-icon icon="ph:chats-circle
|
|
89
|
+
: `<iconify-icon icon="ph:chats-circle" width="20" height="20"></iconify-icon>`;
|
|
90
|
+
|
|
91
|
+
const isExpanded = this.options.isExpanded?.() ?? false;
|
|
92
|
+
const expandIcon = isExpanded ? 'ph:arrows-in' : 'ph:arrows-out';
|
|
93
|
+
const expandLabel = isExpanded ? 'Collapse window' : 'Expand window';
|
|
89
94
|
|
|
90
95
|
this.element.innerHTML = `
|
|
91
|
-
<div class="
|
|
92
|
-
<button class="sdk-btn-icon
|
|
96
|
+
<div class="liveChat-chat-header">
|
|
97
|
+
<button class="sdk-btn-icon liveChat-back-btn" aria-label="Back">
|
|
93
98
|
<iconify-icon icon="ph:arrow-left" width="20" height="20"></iconify-icon>
|
|
94
99
|
</button>
|
|
95
|
-
<div class="
|
|
100
|
+
<div class="liveChat-chat-header-avatar">
|
|
96
101
|
${headerAvatarHtml}
|
|
97
102
|
</div>
|
|
98
|
-
<div class="
|
|
99
|
-
<span class="
|
|
100
|
-
<span class="
|
|
103
|
+
<div class="liveChat-chat-header-info">
|
|
104
|
+
<span class="liveChat-chat-title">${this._escapeHtml(teamName)}</span>
|
|
105
|
+
<span class="liveChat-chat-subtitle">${isClosed ? 'Conversation resolved' : this.state.responseTime || 'Typically replies within minutes'}</span>
|
|
101
106
|
</div>
|
|
102
|
-
<div class="
|
|
103
|
-
<
|
|
107
|
+
<div class="liveChat-chat-header-actions">
|
|
108
|
+
<div class="liveChat-chat-menu-wrapper">
|
|
109
|
+
<button class="sdk-btn-icon liveChat-chat-menu-btn" aria-label="Options">
|
|
110
|
+
<iconify-icon icon="ph:dots-three" width="20" height="20"></iconify-icon>
|
|
111
|
+
</button>
|
|
112
|
+
<div class="liveChat-chat-menu-dropdown">
|
|
113
|
+
<button class="liveChat-chat-menu-item liveChat-expand-window-btn">
|
|
114
|
+
<iconify-icon icon="${expandIcon}" width="16" height="16"></iconify-icon>
|
|
115
|
+
<span>${expandLabel}</span>
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
<button class="sdk-btn-icon sdk-close-btn liveChat-mobile-close-btn" aria-label="Close">
|
|
104
120
|
<iconify-icon icon="ph:x" width="18" height="18"></iconify-icon>
|
|
105
121
|
</button>
|
|
106
122
|
</div>
|
|
107
123
|
</div>
|
|
108
124
|
|
|
109
|
-
<div class="
|
|
125
|
+
<div class="liveChat-connection-banner" style="display:none;">
|
|
110
126
|
<iconify-icon icon="ph:wifi-slash" width="14" height="14"></iconify-icon>
|
|
111
127
|
<span>Reconnecting…</span>
|
|
112
128
|
</div>
|
|
113
129
|
|
|
114
|
-
<div class="
|
|
130
|
+
<div class="liveChat-chat-messages">
|
|
115
131
|
${messagesHtml}
|
|
116
132
|
${
|
|
117
133
|
isClosed
|
|
118
134
|
? `
|
|
119
|
-
<div class="
|
|
120
|
-
<iconify-icon icon="ph:check-circle
|
|
135
|
+
<div class="liveChat-closed-banner">
|
|
136
|
+
<iconify-icon icon="ph:check-circle" width="18" height="18"></iconify-icon>
|
|
121
137
|
<span>This conversation has been resolved</span>
|
|
122
138
|
</div>
|
|
123
139
|
`
|
|
124
140
|
: ''
|
|
125
141
|
}
|
|
126
|
-
<div class="
|
|
127
|
-
<div class="
|
|
142
|
+
<div class="liveChat-typing-indicator">
|
|
143
|
+
<div class="liveChat-typing-dots">
|
|
128
144
|
<span></span><span></span><span></span>
|
|
129
145
|
</div>
|
|
130
|
-
<span class="
|
|
146
|
+
<span class="liveChat-typing-text"></span>
|
|
131
147
|
</div>
|
|
132
148
|
</div>
|
|
133
149
|
|
|
134
|
-
<div class="
|
|
150
|
+
<div class="liveChat-scroll-pill" style="display:none;">
|
|
135
151
|
<iconify-icon icon="ph:arrow-down" width="14" height="14"></iconify-icon>
|
|
136
152
|
<span>New message</span>
|
|
137
153
|
</div>
|
|
@@ -140,33 +156,33 @@
|
|
|
140
156
|
isClosed
|
|
141
157
|
? ''
|
|
142
158
|
: `
|
|
143
|
-
<div class="
|
|
159
|
+
<div class="liveChat-compose-attachments-preview"></div>
|
|
144
160
|
|
|
145
|
-
<div class="
|
|
146
|
-
<div class="
|
|
147
|
-
<textarea class="
|
|
161
|
+
<div class="liveChat-chat-compose">
|
|
162
|
+
<div class="liveChat-compose-input-wrapper">
|
|
163
|
+
<textarea class="liveChat-compose-input" placeholder="${placeholder}" rows="1"></textarea>
|
|
148
164
|
</div>
|
|
149
|
-
<div class="
|
|
150
|
-
<div class="
|
|
151
|
-
<button class="sdk-btn-icon
|
|
152
|
-
<iconify-icon icon="ph:paperclip
|
|
165
|
+
<div class="liveChat-compose-bottom">
|
|
166
|
+
<div class="liveChat-compose-actions">
|
|
167
|
+
<button class="sdk-btn-icon liveChat-compose-attach" aria-label="Attach file">
|
|
168
|
+
<iconify-icon icon="ph:paperclip" width="20" height="20"></iconify-icon>
|
|
153
169
|
</button>
|
|
154
|
-
<button class="sdk-btn-icon
|
|
155
|
-
<iconify-icon icon="ph:smiley
|
|
170
|
+
<button class="sdk-btn-icon liveChat-emoji-btn" aria-label="Emoji">
|
|
171
|
+
<iconify-icon icon="ph:smiley" width="20" height="20"></iconify-icon>
|
|
156
172
|
</button>
|
|
157
173
|
</div>
|
|
158
|
-
<button class="
|
|
174
|
+
<button class="liveChat-compose-send" aria-label="Send" disabled>
|
|
159
175
|
<iconify-icon icon="ph:paper-plane-right" width="20" height="20"></iconify-icon>
|
|
160
176
|
</button>
|
|
161
177
|
</div>
|
|
162
|
-
<input type="file" class="
|
|
178
|
+
<input type="file" class="liveChat-compose-file-input" multiple accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.txt,.zip" />
|
|
163
179
|
</div>
|
|
164
180
|
`
|
|
165
181
|
}
|
|
166
182
|
`;
|
|
167
183
|
|
|
168
184
|
this._typingIndicator = this.element.querySelector(
|
|
169
|
-
'.
|
|
185
|
+
'.liveChat-typing-indicator'
|
|
170
186
|
);
|
|
171
187
|
this._attachEvents();
|
|
172
188
|
this._scrollToBottom();
|
|
@@ -178,11 +194,11 @@
|
|
|
178
194
|
const logoUrl = this.options.logoUrl;
|
|
179
195
|
|
|
180
196
|
const logoHtml = logoUrl
|
|
181
|
-
? `<div class="
|
|
197
|
+
? `<div class="liveChat-chat-empty-logo"><img src="${this._escapeHtml(logoUrl)}" alt="${this._escapeHtml(this.state.teamName)}" /></div>`
|
|
182
198
|
: '';
|
|
183
199
|
|
|
184
200
|
return `
|
|
185
|
-
<div class="
|
|
201
|
+
<div class="liveChat-chat-empty">
|
|
186
202
|
${logoHtml}
|
|
187
203
|
<h3>${isNewConversation ? 'Start a new conversation' : 'Start the conversation'}</h3>
|
|
188
204
|
</div>
|
|
@@ -194,12 +210,12 @@
|
|
|
194
210
|
return attachments
|
|
195
211
|
.map((att) => {
|
|
196
212
|
if (att.type === 'image') {
|
|
197
|
-
return `<img class="
|
|
213
|
+
return `<img class="liveChat-message-image" src="${this._escapeHtml(att.url)}" alt="${this._escapeHtml(att.name || 'image')}" data-url="${this._escapeHtml(att.url)}" />`;
|
|
198
214
|
}
|
|
199
|
-
return `<a class="
|
|
200
|
-
<iconify-icon icon="ph:file
|
|
215
|
+
return `<a class="liveChat-message-file" href="${this._escapeHtml(att.url)}" data-url="${this._escapeHtml(att.url)}" data-name="${this._escapeHtml(att.name || 'file')}">
|
|
216
|
+
<iconify-icon icon="ph:file" width="18" height="18"></iconify-icon>
|
|
201
217
|
<span>${this._escapeHtml(att.name || 'file')}</span>
|
|
202
|
-
<iconify-icon icon="ph:download-simple" width="16" height="16" class="
|
|
218
|
+
<iconify-icon icon="ph:download-simple" width="16" height="16" class="liveChat-file-download-icon"></iconify-icon>
|
|
203
219
|
</a>`;
|
|
204
220
|
})
|
|
205
221
|
.join('');
|
|
@@ -212,8 +228,8 @@
|
|
|
212
228
|
|
|
213
229
|
const isOwn = message.isOwn;
|
|
214
230
|
const messageClass = isOwn
|
|
215
|
-
? '
|
|
216
|
-
: '
|
|
231
|
+
? 'liveChat-message-own'
|
|
232
|
+
: 'liveChat-message-received';
|
|
217
233
|
const timeStr = isLastInGroup
|
|
218
234
|
? this._formatMessageTime(message.timestamp)
|
|
219
235
|
: '';
|
|
@@ -221,25 +237,25 @@
|
|
|
221
237
|
const isOptimistic = message.isOptimistic;
|
|
222
238
|
|
|
223
239
|
const contentHtml = message.content
|
|
224
|
-
? `<div class="
|
|
240
|
+
? `<div class="liveChat-message-content">${this._formatMessageContent(message.content)}</div>`
|
|
225
241
|
: '';
|
|
226
242
|
const bubbleHtml = contentHtml
|
|
227
|
-
? `<div class="
|
|
243
|
+
? `<div class="liveChat-message-bubble">${contentHtml}</div>`
|
|
228
244
|
: '';
|
|
229
245
|
|
|
230
246
|
if (isOwn) {
|
|
231
247
|
const sentIndicator = isLastInGroup
|
|
232
|
-
? `<div class="
|
|
248
|
+
? `<div class="liveChat-message-meta liveChat-message-meta-own">
|
|
233
249
|
${
|
|
234
250
|
isOptimistic
|
|
235
|
-
? `<span class="
|
|
236
|
-
: `<span class="
|
|
251
|
+
? `<span class="liveChat-message-sent-status">Sending…</span>`
|
|
252
|
+
: `<span class="liveChat-message-sent-status">Sent</span>`
|
|
237
253
|
}
|
|
238
254
|
${timeStr ? `<span>·</span><span>${timeStr}</span>` : ''}
|
|
239
255
|
</div>`
|
|
240
256
|
: '';
|
|
241
257
|
return `
|
|
242
|
-
<div class="
|
|
258
|
+
<div class="liveChat-message ${messageClass}${isOptimistic ? ' liveChat-message-optimistic' : ''}">
|
|
243
259
|
${bubbleHtml}
|
|
244
260
|
${attachmentsHtml}
|
|
245
261
|
${sentIndicator}
|
|
@@ -249,15 +265,15 @@
|
|
|
249
265
|
|
|
250
266
|
const avatarHtml = this._renderSenderAvatar(message.sender);
|
|
251
267
|
return `
|
|
252
|
-
<div class="
|
|
253
|
-
<div class="
|
|
254
|
-
<div class="
|
|
255
|
-
<div class="
|
|
268
|
+
<div class="liveChat-message ${messageClass}">
|
|
269
|
+
<div class="liveChat-message-row">
|
|
270
|
+
<div class="liveChat-message-avatar">${avatarHtml}</div>
|
|
271
|
+
<div class="liveChat-message-wrapper">
|
|
256
272
|
${bubbleHtml}
|
|
257
273
|
${attachmentsHtml}
|
|
258
274
|
</div>
|
|
259
275
|
</div>
|
|
260
|
-
${timeStr ? `<div class="
|
|
276
|
+
${timeStr ? `<div class="liveChat-message-meta"><span>${timeStr}</span></div>` : ''}
|
|
261
277
|
</div>
|
|
262
278
|
`;
|
|
263
279
|
}
|
|
@@ -286,11 +302,11 @@
|
|
|
286
302
|
const action = rawAction.charAt(0).toUpperCase() + rawAction.slice(1);
|
|
287
303
|
|
|
288
304
|
return `
|
|
289
|
-
<div class="
|
|
290
|
-
<div class="
|
|
291
|
-
<span class="
|
|
292
|
-
<span class="
|
|
293
|
-
${timeStr ? `<span class="
|
|
305
|
+
<div class="liveChat-message-system-event">
|
|
306
|
+
<div class="liveChat-message-system-event-avatar">${logoHtml}</div>
|
|
307
|
+
<span class="liveChat-message-system-event-name">${this._escapeHtml(name)}</span>
|
|
308
|
+
<span class="liveChat-message-system-event-action">${this._escapeHtml(action)}</span>
|
|
309
|
+
${timeStr ? `<span class="liveChat-message-system-event-time">${timeStr}</span>` : ''}
|
|
294
310
|
</div>
|
|
295
311
|
`;
|
|
296
312
|
}
|
|
@@ -304,16 +320,16 @@
|
|
|
304
320
|
const timeStr = this._formatMessageTime(message.timestamp);
|
|
305
321
|
|
|
306
322
|
return `
|
|
307
|
-
<div class="
|
|
308
|
-
<div class="
|
|
309
|
-
<div class="
|
|
310
|
-
<div class="
|
|
311
|
-
<div class="
|
|
312
|
-
<div class="
|
|
323
|
+
<div class="liveChat-message liveChat-message-received">
|
|
324
|
+
<div class="liveChat-message-row">
|
|
325
|
+
<div class="liveChat-message-avatar">${avatarHtml}</div>
|
|
326
|
+
<div class="liveChat-message-wrapper">
|
|
327
|
+
<div class="liveChat-message-bubble">
|
|
328
|
+
<div class="liveChat-message-content">${this._formatMessageContent(content)}</div>
|
|
313
329
|
</div>
|
|
314
330
|
</div>
|
|
315
331
|
</div>
|
|
316
|
-
${timeStr ? `<div class="
|
|
332
|
+
${timeStr ? `<div class="liveChat-message-meta"><span>${timeStr}</span></div>` : ''}
|
|
317
333
|
</div>
|
|
318
334
|
`;
|
|
319
335
|
}
|
|
@@ -342,7 +358,7 @@
|
|
|
342
358
|
})
|
|
343
359
|
.join('');
|
|
344
360
|
|
|
345
|
-
return `<div class="
|
|
361
|
+
return `<div class="liveChat-avatar-stack">${avatarItems}</div>`;
|
|
346
362
|
}
|
|
347
363
|
|
|
348
364
|
_formatMessageTime(timestamp) {
|
|
@@ -372,9 +388,9 @@
|
|
|
372
388
|
|
|
373
389
|
_appendMessage(message) {
|
|
374
390
|
const messagesContainer = this.element.querySelector(
|
|
375
|
-
'.
|
|
391
|
+
'.liveChat-chat-messages'
|
|
376
392
|
);
|
|
377
|
-
const emptyState = messagesContainer.querySelector('.
|
|
393
|
+
const emptyState = messagesContainer.querySelector('.liveChat-chat-empty');
|
|
378
394
|
if (emptyState) {
|
|
379
395
|
emptyState.remove();
|
|
380
396
|
}
|
|
@@ -394,14 +410,14 @@
|
|
|
394
410
|
if (isNearBottom) {
|
|
395
411
|
this._scrollToBottom();
|
|
396
412
|
} else if (!message.isOwn) {
|
|
397
|
-
const pill = this.element.querySelector('.
|
|
413
|
+
const pill = this.element.querySelector('.liveChat-scroll-pill');
|
|
398
414
|
if (pill) pill.style.display = 'flex';
|
|
399
415
|
}
|
|
400
416
|
}
|
|
401
417
|
|
|
402
418
|
_scrollToBottom() {
|
|
403
419
|
const messagesContainer = this.element.querySelector(
|
|
404
|
-
'.
|
|
420
|
+
'.liveChat-chat-messages'
|
|
405
421
|
);
|
|
406
422
|
if (messagesContainer) {
|
|
407
423
|
setTimeout(() => {
|
|
@@ -412,9 +428,9 @@
|
|
|
412
428
|
|
|
413
429
|
_setupScrollPill() {
|
|
414
430
|
const messagesContainer = this.element.querySelector(
|
|
415
|
-
'.
|
|
431
|
+
'.liveChat-chat-messages'
|
|
416
432
|
);
|
|
417
|
-
const pill = this.element.querySelector('.
|
|
433
|
+
const pill = this.element.querySelector('.liveChat-scroll-pill');
|
|
418
434
|
if (!messagesContainer || !pill) return;
|
|
419
435
|
|
|
420
436
|
pill.addEventListener('click', () => {
|
|
@@ -469,8 +485,8 @@
|
|
|
469
485
|
}
|
|
470
486
|
|
|
471
487
|
_updateSendButtonState() {
|
|
472
|
-
const input = this.element.querySelector('.
|
|
473
|
-
const sendBtn = this.element.querySelector('.
|
|
488
|
+
const input = this.element.querySelector('.liveChat-compose-input');
|
|
489
|
+
const sendBtn = this.element.querySelector('.liveChat-compose-send');
|
|
474
490
|
if (input && sendBtn) {
|
|
475
491
|
sendBtn.disabled =
|
|
476
492
|
!input.value.trim() && this._pendingAttachments.length === 0;
|
|
@@ -479,7 +495,7 @@
|
|
|
479
495
|
|
|
480
496
|
_renderAttachmentPreviews() {
|
|
481
497
|
const container = this.element.querySelector(
|
|
482
|
-
'.
|
|
498
|
+
'.liveChat-compose-attachments-preview'
|
|
483
499
|
);
|
|
484
500
|
if (!container) return;
|
|
485
501
|
|
|
@@ -494,18 +510,18 @@
|
|
|
494
510
|
.map((att, i) => {
|
|
495
511
|
const isImage = att.type.startsWith('image');
|
|
496
512
|
const thumb = isImage
|
|
497
|
-
? `<img class="
|
|
498
|
-
: `<div class="
|
|
513
|
+
? `<img class="liveChat-attachment-thumb" src="${att.preview}" alt="${this._escapeHtml(att.file.name)}" />`
|
|
514
|
+
: `<div class="liveChat-attachment-thumb liveChat-attachment-file-icon"><iconify-icon icon="ph:file" width="24" height="24"></iconify-icon></div>`;
|
|
499
515
|
return `
|
|
500
|
-
<div class="
|
|
516
|
+
<div class="liveChat-attachment-preview" data-index="${i}">
|
|
501
517
|
${thumb}
|
|
502
|
-
<button class="
|
|
518
|
+
<button class="liveChat-attachment-remove" data-index="${i}" aria-label="Remove">×</button>
|
|
503
519
|
</div>
|
|
504
520
|
`;
|
|
505
521
|
})
|
|
506
522
|
.join('');
|
|
507
523
|
|
|
508
|
-
container.querySelectorAll('.
|
|
524
|
+
container.querySelectorAll('.liveChat-attachment-remove').forEach((btn) => {
|
|
509
525
|
btn.addEventListener('click', (e) => {
|
|
510
526
|
const idx = parseInt(e.currentTarget.dataset.index, 10);
|
|
511
527
|
this._pendingAttachments.splice(idx, 1);
|
|
@@ -517,13 +533,13 @@
|
|
|
517
533
|
|
|
518
534
|
_attachEvents() {
|
|
519
535
|
this.element
|
|
520
|
-
.querySelector('.
|
|
536
|
+
.querySelector('.liveChat-back-btn')
|
|
521
537
|
.addEventListener('click', () => {
|
|
522
538
|
this.state.setView('messages');
|
|
523
539
|
});
|
|
524
540
|
|
|
525
541
|
const mobileCloseBtn = this.element.querySelector(
|
|
526
|
-
'.
|
|
542
|
+
'.liveChat-mobile-close-btn'
|
|
527
543
|
);
|
|
528
544
|
if (mobileCloseBtn) {
|
|
529
545
|
mobileCloseBtn.addEventListener('click', () => {
|
|
@@ -531,8 +547,47 @@
|
|
|
531
547
|
});
|
|
532
548
|
}
|
|
533
549
|
|
|
534
|
-
const
|
|
535
|
-
const
|
|
550
|
+
const menuBtn = this.element.querySelector('.liveChat-chat-menu-btn');
|
|
551
|
+
const dropdown = this.element.querySelector('.liveChat-chat-menu-dropdown');
|
|
552
|
+
if (menuBtn && dropdown) {
|
|
553
|
+
menuBtn.addEventListener('click', (e) => {
|
|
554
|
+
e.stopPropagation();
|
|
555
|
+
dropdown.classList.toggle('open');
|
|
556
|
+
if (
|
|
557
|
+
dropdown.classList.contains('open') &&
|
|
558
|
+
this._menuOutsideHandler === null
|
|
559
|
+
) {
|
|
560
|
+
this._menuOutsideHandler = (ev) => {
|
|
561
|
+
if (!dropdown.contains(ev.target) && !menuBtn.contains(ev.target)) {
|
|
562
|
+
dropdown.classList.remove('open');
|
|
563
|
+
document.removeEventListener('click', this._menuOutsideHandler);
|
|
564
|
+
this._menuOutsideHandler = null;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
setTimeout(
|
|
568
|
+
() => document.addEventListener('click', this._menuOutsideHandler),
|
|
569
|
+
0
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const expandWindowBtn = this.element.querySelector(
|
|
576
|
+
'.liveChat-expand-window-btn'
|
|
577
|
+
);
|
|
578
|
+
if (expandWindowBtn && dropdown) {
|
|
579
|
+
expandWindowBtn.addEventListener('click', () => {
|
|
580
|
+
dropdown.classList.remove('open');
|
|
581
|
+
if (this._menuOutsideHandler) {
|
|
582
|
+
document.removeEventListener('click', this._menuOutsideHandler);
|
|
583
|
+
this._menuOutsideHandler = null;
|
|
584
|
+
}
|
|
585
|
+
this.options.onToggleExpand?.();
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const input = this.element.querySelector('.liveChat-compose-input');
|
|
590
|
+
const sendBtn = this.element.querySelector('.liveChat-compose-send');
|
|
536
591
|
|
|
537
592
|
if (input && sendBtn) {
|
|
538
593
|
input.addEventListener('input', () => {
|
|
@@ -557,7 +612,7 @@
|
|
|
557
612
|
});
|
|
558
613
|
}
|
|
559
614
|
|
|
560
|
-
const emojiBtn = this.element.querySelector('.
|
|
615
|
+
const emojiBtn = this.element.querySelector('.liveChat-emoji-btn');
|
|
561
616
|
if (emojiBtn) {
|
|
562
617
|
emojiBtn.addEventListener('click', (e) => {
|
|
563
618
|
e.stopPropagation();
|
|
@@ -565,9 +620,9 @@
|
|
|
565
620
|
});
|
|
566
621
|
}
|
|
567
622
|
|
|
568
|
-
const attachBtn = this.element.querySelector('.
|
|
623
|
+
const attachBtn = this.element.querySelector('.liveChat-compose-attach');
|
|
569
624
|
const fileInput = this.element.querySelector(
|
|
570
|
-
'.
|
|
625
|
+
'.liveChat-compose-file-input'
|
|
571
626
|
);
|
|
572
627
|
|
|
573
628
|
if (attachBtn && fileInput) {
|
|
@@ -596,11 +651,11 @@
|
|
|
596
651
|
}
|
|
597
652
|
|
|
598
653
|
const messagesContainer = this.element.querySelector(
|
|
599
|
-
'.
|
|
654
|
+
'.liveChat-chat-messages'
|
|
600
655
|
);
|
|
601
656
|
if (messagesContainer) {
|
|
602
657
|
messagesContainer.addEventListener('click', (e) => {
|
|
603
|
-
const fileLink = e.target.closest('.
|
|
658
|
+
const fileLink = e.target.closest('.liveChat-message-file');
|
|
604
659
|
if (fileLink) {
|
|
605
660
|
e.preventDefault();
|
|
606
661
|
const url = fileLink.dataset.url;
|
|
@@ -609,7 +664,7 @@
|
|
|
609
664
|
return;
|
|
610
665
|
}
|
|
611
666
|
|
|
612
|
-
const img = e.target.closest('.
|
|
667
|
+
const img = e.target.closest('.liveChat-message-image');
|
|
613
668
|
if (img) {
|
|
614
669
|
const url = img.dataset.url || img.src;
|
|
615
670
|
window.open(url, '_blank');
|
|
@@ -648,7 +703,7 @@
|
|
|
648
703
|
async _sendMessage() {
|
|
649
704
|
if (this._isConversationClosed) return;
|
|
650
705
|
|
|
651
|
-
const input = this.element.querySelector('.
|
|
706
|
+
const input = this.element.querySelector('.liveChat-compose-input');
|
|
652
707
|
const content = input.value.trim();
|
|
653
708
|
const hasAttachments = this._pendingAttachments.length > 0;
|
|
654
709
|
|
|
@@ -713,7 +768,7 @@
|
|
|
713
768
|
|
|
714
769
|
async _toggleEmojiPicker() {
|
|
715
770
|
const existing = this.element.querySelector(
|
|
716
|
-
'.
|
|
771
|
+
'.liveChat-emoji-picker-container'
|
|
717
772
|
);
|
|
718
773
|
if (existing) {
|
|
719
774
|
existing.remove();
|
|
@@ -732,12 +787,12 @@
|
|
|
732
787
|
}
|
|
733
788
|
|
|
734
789
|
const container = document.createElement('div');
|
|
735
|
-
container.className = '
|
|
790
|
+
container.className = 'liveChat-emoji-picker-container';
|
|
736
791
|
|
|
737
792
|
const picker = document.createElement('emoji-picker');
|
|
738
793
|
container.appendChild(picker);
|
|
739
794
|
|
|
740
|
-
const compose = this.element.querySelector('.
|
|
795
|
+
const compose = this.element.querySelector('.liveChat-chat-compose');
|
|
741
796
|
compose.parentNode.insertBefore(container, compose);
|
|
742
797
|
this._emojiPickerOpen = true;
|
|
743
798
|
|
|
@@ -754,7 +809,7 @@
|
|
|
754
809
|
this._emojiOutsideHandler = (e) => {
|
|
755
810
|
if (
|
|
756
811
|
!container.contains(e.target) &&
|
|
757
|
-
!e.target.closest('.
|
|
812
|
+
!e.target.closest('.liveChat-emoji-btn')
|
|
758
813
|
) {
|
|
759
814
|
container.remove();
|
|
760
815
|
this._emojiPickerOpen = false;
|
|
@@ -769,7 +824,7 @@
|
|
|
769
824
|
}
|
|
770
825
|
|
|
771
826
|
_insertEmoji(emoji) {
|
|
772
|
-
const input = this.element.querySelector('.
|
|
827
|
+
const input = this.element.querySelector('.liveChat-compose-input');
|
|
773
828
|
if (!input) return;
|
|
774
829
|
const start = input.selectionStart;
|
|
775
830
|
const end = input.selectionEnd;
|
|
@@ -813,7 +868,7 @@
|
|
|
813
868
|
if (this._typingIndicator) {
|
|
814
869
|
this._typingIndicator.style.display = 'flex';
|
|
815
870
|
const textEl = this._typingIndicator.querySelector(
|
|
816
|
-
'.
|
|
871
|
+
'.liveChat-typing-text'
|
|
817
872
|
);
|
|
818
873
|
if (textEl) {
|
|
819
874
|
textEl.textContent = `${userName || 'Support'} is typing...`;
|
|
@@ -839,6 +894,9 @@
|
|
|
839
894
|
if (this._emojiOutsideHandler) {
|
|
840
895
|
document.removeEventListener('click', this._emojiOutsideHandler);
|
|
841
896
|
}
|
|
897
|
+
if (this._menuOutsideHandler) {
|
|
898
|
+
document.removeEventListener('click', this._menuOutsideHandler);
|
|
899
|
+
}
|
|
842
900
|
if (this.element && this.element.parentNode) {
|
|
843
901
|
this.element.parentNode.removeChild(this.element);
|
|
844
902
|
}
|