releasebird-javascript-sdk 1.0.73 → 1.0.76
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/build/index.js +1 -1
- package/package.json +1 -1
- package/published/1.0.74/index.js +1 -0
- package/published/1.0.75/index.js +1 -0
- package/published/1.0.76/index.js +1 -0
- package/published/latest/index.js +1 -1
- package/src/RbirdFormManager.js +567 -0
- package/src/RbirdWebsiteWidget.js +361 -9
- package/src/ReleasebirdFormViewer.js +268 -0
- package/src/Styles.js +268 -23
- package/src/index.js +22 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import RbirdUtils from "./RbirdUtils";
|
|
2
2
|
import ReleasebirdImageViewer from "./ReleasebirdImageViewer";
|
|
3
|
+
import ReleasebirdFormViewer from "./ReleasebirdFormViewer";
|
|
3
4
|
import {
|
|
4
5
|
hideWidgetImage,
|
|
5
6
|
launcher0,
|
|
@@ -44,6 +45,15 @@ export default class RbirdWebsiteWidget {
|
|
|
44
45
|
messageBubblesContainer = null;
|
|
45
46
|
dismissedBubbles = new Set(); // Track dismissed bubbles by chatId
|
|
46
47
|
|
|
48
|
+
// Drag functionality
|
|
49
|
+
widgetWrapper = null;
|
|
50
|
+
dragHandle = null;
|
|
51
|
+
isDragging = false;
|
|
52
|
+
dragStartX = 0;
|
|
53
|
+
dragStartY = 0;
|
|
54
|
+
initialX = 0;
|
|
55
|
+
initialY = 0;
|
|
56
|
+
|
|
47
57
|
static getInstance() {
|
|
48
58
|
if (!this.instance) {
|
|
49
59
|
this.instance = new RbirdWebsiteWidget();
|
|
@@ -72,11 +82,23 @@ export default class RbirdWebsiteWidget {
|
|
|
72
82
|
this.websiteWidgetVisible = true;
|
|
73
83
|
|
|
74
84
|
try {
|
|
85
|
+
// Create wrapper for drag handle and button
|
|
86
|
+
this.widgetWrapper = document.createElement("div");
|
|
87
|
+
this.widgetWrapper.className = "rbird-widget-wrapper";
|
|
88
|
+
document.body.appendChild(this.widgetWrapper);
|
|
89
|
+
|
|
90
|
+
// Create drag handle
|
|
91
|
+
this.dragHandle = this.createDragHandle();
|
|
92
|
+
this.widgetWrapper.appendChild(this.dragHandle);
|
|
93
|
+
|
|
94
|
+
// Create launcher button
|
|
75
95
|
let elem = document.createElement("div");
|
|
76
|
-
elem.onclick = () => {
|
|
77
|
-
this.
|
|
96
|
+
elem.onclick = (e) => {
|
|
97
|
+
if (!this.isDragging) {
|
|
98
|
+
this.openWebsiteWidget();
|
|
99
|
+
}
|
|
78
100
|
};
|
|
79
|
-
|
|
101
|
+
this.widgetWrapper.appendChild(elem);
|
|
80
102
|
this.websiteWidget = elem;
|
|
81
103
|
|
|
82
104
|
if (RbirdSessionManager.getInstance().widgetSettings?.allowClose && !this.noButton) {
|
|
@@ -94,6 +116,7 @@ export default class RbirdWebsiteWidget {
|
|
|
94
116
|
document.body.appendChild(this.countBadge);
|
|
95
117
|
|
|
96
118
|
this.styleWidget();
|
|
119
|
+
this.initDragListeners();
|
|
97
120
|
|
|
98
121
|
resolve();
|
|
99
122
|
} catch (error) {
|
|
@@ -108,6 +131,10 @@ export default class RbirdWebsiteWidget {
|
|
|
108
131
|
|
|
109
132
|
openWebsiteWidget() {
|
|
110
133
|
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
134
|
+
|
|
135
|
+
// Position the modal relative to the bubble position
|
|
136
|
+
this.positionWidgetContent();
|
|
137
|
+
|
|
111
138
|
RbirdUtils.addClass(this.widgetContent, "cta__modal--visible");
|
|
112
139
|
RbirdUtils.addClass(this.backdrop, "cta__modal-dimmer--visible");
|
|
113
140
|
|
|
@@ -127,6 +154,9 @@ export default class RbirdWebsiteWidget {
|
|
|
127
154
|
this.hideWidgetButton.style.display = 'none';
|
|
128
155
|
}
|
|
129
156
|
this.websiteWidget.style.display = 'none';
|
|
157
|
+
if (this.widgetWrapper) {
|
|
158
|
+
this.widgetWrapper.style.display = 'none';
|
|
159
|
+
}
|
|
130
160
|
}
|
|
131
161
|
|
|
132
162
|
if (RbirdSessionManager.getInstance().widgetSettings.launcher === 5) {
|
|
@@ -164,6 +194,9 @@ export default class RbirdWebsiteWidget {
|
|
|
164
194
|
if (this.hideWidgetButton) {
|
|
165
195
|
this.hideWidgetButton.style.display = 'none';
|
|
166
196
|
this.websiteWidget.style.display = 'none';
|
|
197
|
+
if (this.widgetWrapper) {
|
|
198
|
+
this.widgetWrapper.style.display = 'none';
|
|
199
|
+
}
|
|
167
200
|
}
|
|
168
201
|
}
|
|
169
202
|
|
|
@@ -236,11 +269,17 @@ export default class RbirdWebsiteWidget {
|
|
|
236
269
|
showButton(showButton) {
|
|
237
270
|
if (typeof window === 'undefined') return;
|
|
238
271
|
if (showButton && !this.noButton) {
|
|
272
|
+
if (this.widgetWrapper) {
|
|
273
|
+
this.widgetWrapper.style.display = 'flex';
|
|
274
|
+
}
|
|
239
275
|
this.websiteWidget.style.display = 'block';
|
|
240
276
|
if (this.hideWidgetButton && !this.noButton) {
|
|
241
277
|
this.hideWidgetButton.style.display = "block";
|
|
242
278
|
}
|
|
243
279
|
} else {
|
|
280
|
+
if (this.widgetWrapper) {
|
|
281
|
+
this.widgetWrapper.style.display = 'none';
|
|
282
|
+
}
|
|
244
283
|
this.websiteWidget.style.display = 'none';
|
|
245
284
|
if (this.hideWidgetButton) {
|
|
246
285
|
this.hideWidgetButton.style.display = "none";
|
|
@@ -267,11 +306,19 @@ export default class RbirdWebsiteWidget {
|
|
|
267
306
|
this.initButton();
|
|
268
307
|
if (!this.noButton) {
|
|
269
308
|
this.websiteWidget.style.display = 'block';
|
|
309
|
+
if (this.widgetWrapper) {
|
|
310
|
+
this.widgetWrapper.style.display = 'flex';
|
|
311
|
+
}
|
|
270
312
|
}
|
|
271
313
|
|
|
272
314
|
this.websiteWidget.onclick = () => this.openWebsiteWidget();
|
|
273
315
|
this.unregisterListeners();
|
|
274
316
|
|
|
317
|
+
// Update positions of related elements after a short delay to ensure DOM is updated
|
|
318
|
+
setTimeout(() => {
|
|
319
|
+
this.updateRelatedElementsPosition();
|
|
320
|
+
}, 50);
|
|
321
|
+
|
|
275
322
|
// Reload message bubbles when widget is closed
|
|
276
323
|
this.dismissedBubbles.clear(); // Reset dismissed bubbles
|
|
277
324
|
this.fetchUnreadMessages();
|
|
@@ -310,6 +357,16 @@ export default class RbirdWebsiteWidget {
|
|
|
310
357
|
ReleasebirdImageViewer.showImage(e.data?.url);
|
|
311
358
|
}
|
|
312
359
|
|
|
360
|
+
if (e.data?.key === 'showForm') {
|
|
361
|
+
ReleasebirdFormViewer.showForm(
|
|
362
|
+
e.data?.form,
|
|
363
|
+
e.data?.formId,
|
|
364
|
+
e.data?.chatId,
|
|
365
|
+
e.data?.apiKey,
|
|
366
|
+
that.iframe
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
313
370
|
if (e.data === 'newMessageArrived') {
|
|
314
371
|
this.countNotifications();
|
|
315
372
|
// Also refresh message bubbles when new message arrives
|
|
@@ -337,6 +394,29 @@ export default class RbirdWebsiteWidget {
|
|
|
337
394
|
this.websiteWidget.className=RbirdSessionManager.getInstance().widgetSettings.launcher === 5 ? 'launcherButton5' : 'launcherButton' ;
|
|
338
395
|
this.initButton();
|
|
339
396
|
|
|
397
|
+
// Position the wrapper based on launcher settings or saved position
|
|
398
|
+
const settings = RbirdSessionManager.getInstance().widgetSettings;
|
|
399
|
+
const launcherPosition = settings.launcherPosition || 'right';
|
|
400
|
+
const spaceLeftRight = settings.spaceLeftRight || 20;
|
|
401
|
+
const spaceBottom = settings.spaceBottom || 20;
|
|
402
|
+
|
|
403
|
+
// Set initial wrapper position
|
|
404
|
+
this.widgetWrapper.style.bottom = `${spaceBottom}px`;
|
|
405
|
+
if (launcherPosition === 'right') {
|
|
406
|
+
this.widgetWrapper.style.right = `${spaceLeftRight}px`;
|
|
407
|
+
this.widgetWrapper.style.left = 'unset';
|
|
408
|
+
this.widgetWrapper.style.flexDirection = 'row';
|
|
409
|
+
} else {
|
|
410
|
+
this.widgetWrapper.style.left = `${spaceLeftRight}px`;
|
|
411
|
+
this.widgetWrapper.style.right = 'unset';
|
|
412
|
+
this.widgetWrapper.style.flexDirection = 'row-reverse';
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Try to apply saved position after a short delay (to ensure elements are rendered)
|
|
416
|
+
setTimeout(() => {
|
|
417
|
+
this.applyWidgetPosition();
|
|
418
|
+
}, 100);
|
|
419
|
+
|
|
340
420
|
// Create backdrop
|
|
341
421
|
this.backdrop = document.createElement("div");
|
|
342
422
|
this.backdrop.className = "cta__modal-dimmer";
|
|
@@ -418,11 +498,9 @@ export default class RbirdWebsiteWidget {
|
|
|
418
498
|
};
|
|
419
499
|
http.onreadystatechange = function () {
|
|
420
500
|
if (http.readyState === XMLHttpRequest.DONE) {
|
|
421
|
-
console.log('[RbirdWidget] Unread messages response:', http.status, http.responseText);
|
|
422
501
|
if (http.status === 200 || http.status === 201) {
|
|
423
502
|
try {
|
|
424
503
|
const messages = JSON.parse(http.responseText);
|
|
425
|
-
console.log('[RbirdWidget] Parsed messages:', messages);
|
|
426
504
|
that.renderMessageBubbles(messages);
|
|
427
505
|
} catch (e) {
|
|
428
506
|
console.error('[RbirdWidget] Error parsing unread messages:', e);
|
|
@@ -438,12 +516,10 @@ export default class RbirdWebsiteWidget {
|
|
|
438
516
|
* @param {Array} messages - Array of unread messages (one per chat, max 5)
|
|
439
517
|
*/
|
|
440
518
|
renderMessageBubbles(messages) {
|
|
441
|
-
console.log('[RbirdWidget] renderMessageBubbles called with:', messages);
|
|
442
519
|
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
443
520
|
|
|
444
521
|
// Don't show bubbles if widget is open
|
|
445
522
|
if (this.isOpen()) {
|
|
446
|
-
console.log('[RbirdWidget] Widget is open, hiding bubbles');
|
|
447
523
|
this.hideMessageBubbles();
|
|
448
524
|
return;
|
|
449
525
|
}
|
|
@@ -453,10 +529,8 @@ export default class RbirdWebsiteWidget {
|
|
|
453
529
|
.filter(msg => !this.dismissedBubbles.has(msg.chatId))
|
|
454
530
|
.slice(0, 5);
|
|
455
531
|
|
|
456
|
-
console.log('[RbirdWidget] Filtered messages:', filteredMessages);
|
|
457
532
|
|
|
458
533
|
if (filteredMessages.length === 0) {
|
|
459
|
-
console.log('[RbirdWidget] No messages to display');
|
|
460
534
|
this.hideMessageBubbles();
|
|
461
535
|
return;
|
|
462
536
|
}
|
|
@@ -618,4 +692,282 @@ export default class RbirdWebsiteWidget {
|
|
|
618
692
|
}
|
|
619
693
|
}
|
|
620
694
|
|
|
695
|
+
/**
|
|
696
|
+
* Create the drag handle element (6-dot grip pattern)
|
|
697
|
+
*/
|
|
698
|
+
createDragHandle() {
|
|
699
|
+
const handle = document.createElement('div');
|
|
700
|
+
handle.className = 'rbird-drag-handle';
|
|
701
|
+
|
|
702
|
+
// Create 3 rows of 2 dots each
|
|
703
|
+
for (let i = 0; i < 3; i++) {
|
|
704
|
+
const row = document.createElement('div');
|
|
705
|
+
row.className = 'rbird-drag-handle-row';
|
|
706
|
+
for (let j = 0; j < 2; j++) {
|
|
707
|
+
const dot = document.createElement('div');
|
|
708
|
+
dot.className = 'rbird-drag-handle-dot';
|
|
709
|
+
row.appendChild(dot);
|
|
710
|
+
}
|
|
711
|
+
handle.appendChild(row);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return handle;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Initialize drag event listeners
|
|
719
|
+
*/
|
|
720
|
+
initDragListeners() {
|
|
721
|
+
if (!this.dragHandle) return;
|
|
722
|
+
|
|
723
|
+
// Mouse events
|
|
724
|
+
this.dragHandle.addEventListener('mousedown', (e) => this.startDrag(e));
|
|
725
|
+
document.addEventListener('mousemove', (e) => this.onDrag(e));
|
|
726
|
+
document.addEventListener('mouseup', (e) => this.endDrag(e));
|
|
727
|
+
|
|
728
|
+
// Touch events for mobile
|
|
729
|
+
this.dragHandle.addEventListener('touchstart', (e) => this.startDrag(e), { passive: false });
|
|
730
|
+
document.addEventListener('touchmove', (e) => this.onDrag(e), { passive: false });
|
|
731
|
+
document.addEventListener('touchend', (e) => this.endDrag(e));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Start dragging
|
|
736
|
+
*/
|
|
737
|
+
startDrag(e) {
|
|
738
|
+
if (this.isMobileDevice()) return; // Disable dragging on mobile for now
|
|
739
|
+
|
|
740
|
+
e.preventDefault();
|
|
741
|
+
this.isDragging = true;
|
|
742
|
+
this.widgetWrapper.classList.add('rbird-dragging');
|
|
743
|
+
|
|
744
|
+
const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
|
|
745
|
+
const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
|
|
746
|
+
|
|
747
|
+
this.dragStartX = clientX;
|
|
748
|
+
this.dragStartY = clientY;
|
|
749
|
+
|
|
750
|
+
const rect = this.widgetWrapper.getBoundingClientRect();
|
|
751
|
+
this.initialX = rect.left;
|
|
752
|
+
this.initialY = rect.top;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Handle drag movement
|
|
757
|
+
*/
|
|
758
|
+
onDrag(e) {
|
|
759
|
+
if (!this.isDragging) return;
|
|
760
|
+
|
|
761
|
+
e.preventDefault();
|
|
762
|
+
|
|
763
|
+
const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
|
|
764
|
+
const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
|
|
765
|
+
|
|
766
|
+
const deltaX = clientX - this.dragStartX;
|
|
767
|
+
const deltaY = clientY - this.dragStartY;
|
|
768
|
+
|
|
769
|
+
let newX = this.initialX + deltaX;
|
|
770
|
+
let newY = this.initialY + deltaY;
|
|
771
|
+
|
|
772
|
+
// Constrain to viewport
|
|
773
|
+
const wrapperRect = this.widgetWrapper.getBoundingClientRect();
|
|
774
|
+
const maxX = window.innerWidth - wrapperRect.width;
|
|
775
|
+
const maxY = window.innerHeight - wrapperRect.height;
|
|
776
|
+
|
|
777
|
+
newX = Math.max(0, Math.min(newX, maxX));
|
|
778
|
+
newY = Math.max(0, Math.min(newY, maxY));
|
|
779
|
+
|
|
780
|
+
this.updateWidgetPosition(newX, newY);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* End dragging and save position
|
|
785
|
+
*/
|
|
786
|
+
endDrag(e) {
|
|
787
|
+
if (!this.isDragging) return;
|
|
788
|
+
|
|
789
|
+
this.isDragging = false;
|
|
790
|
+
this.widgetWrapper.classList.remove('rbird-dragging');
|
|
791
|
+
|
|
792
|
+
// Save position to localStorage
|
|
793
|
+
const rect = this.widgetWrapper.getBoundingClientRect();
|
|
794
|
+
this.saveWidgetPosition(rect.left, rect.top);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Update widget and related elements position
|
|
799
|
+
*/
|
|
800
|
+
updateWidgetPosition(x, y) {
|
|
801
|
+
if (!this.widgetWrapper) return;
|
|
802
|
+
|
|
803
|
+
// Update wrapper position
|
|
804
|
+
this.widgetWrapper.style.left = `${x}px`;
|
|
805
|
+
this.widgetWrapper.style.right = 'unset';
|
|
806
|
+
this.widgetWrapper.style.top = 'unset';
|
|
807
|
+
this.widgetWrapper.style.bottom = `${window.innerHeight - y - this.widgetWrapper.offsetHeight}px`;
|
|
808
|
+
|
|
809
|
+
// Update badge position
|
|
810
|
+
if (this.countBadge) {
|
|
811
|
+
const buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
812
|
+
this.countBadge.style.left = `${buttonRect.right - 15}px`;
|
|
813
|
+
this.countBadge.style.right = 'unset';
|
|
814
|
+
this.countBadge.style.bottom = `${window.innerHeight - buttonRect.top + 5}px`;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Update hide button position (top right of bubble)
|
|
818
|
+
if (this.hideWidgetButton) {
|
|
819
|
+
const buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
820
|
+
this.hideWidgetButton.style.left = `${buttonRect.right - 5}px`;
|
|
821
|
+
this.hideWidgetButton.style.right = 'unset';
|
|
822
|
+
this.hideWidgetButton.style.bottom = `${window.innerHeight - buttonRect.top + 5}px`;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Update message bubbles container position
|
|
826
|
+
if (this.messageBubblesContainer) {
|
|
827
|
+
const buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
828
|
+
this.messageBubblesContainer.style.left = `${buttonRect.left}px`;
|
|
829
|
+
this.messageBubblesContainer.style.right = 'unset';
|
|
830
|
+
this.messageBubblesContainer.style.bottom = `${window.innerHeight - buttonRect.top + 20}px`;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Save widget position to localStorage
|
|
836
|
+
*/
|
|
837
|
+
saveWidgetPosition(x, y) {
|
|
838
|
+
try {
|
|
839
|
+
const position = { x, y, timestamp: Date.now() };
|
|
840
|
+
window.localStorage.setItem('rbird_widget_position', JSON.stringify(position));
|
|
841
|
+
} catch (e) {
|
|
842
|
+
console.warn('[RbirdWidget] Could not save position to localStorage:', e);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Load widget position from localStorage
|
|
848
|
+
*/
|
|
849
|
+
loadWidgetPosition() {
|
|
850
|
+
try {
|
|
851
|
+
const saved = window.localStorage.getItem('rbird_widget_position');
|
|
852
|
+
if (saved) {
|
|
853
|
+
const position = JSON.parse(saved);
|
|
854
|
+
// Validate position is still within viewport
|
|
855
|
+
if (position.x >= 0 && position.x < window.innerWidth &&
|
|
856
|
+
position.y >= 0 && position.y < window.innerHeight) {
|
|
857
|
+
return position;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
} catch (e) {
|
|
861
|
+
console.warn('[RbirdWidget] Could not load position from localStorage:', e);
|
|
862
|
+
}
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Apply saved position or use default
|
|
868
|
+
*/
|
|
869
|
+
applyWidgetPosition() {
|
|
870
|
+
const savedPosition = this.loadWidgetPosition();
|
|
871
|
+
|
|
872
|
+
if (savedPosition && this.widgetWrapper) {
|
|
873
|
+
// Apply saved position
|
|
874
|
+
this.updateWidgetPosition(savedPosition.x, savedPosition.y);
|
|
875
|
+
// Update related elements after a short delay to ensure DOM is ready
|
|
876
|
+
setTimeout(() => {
|
|
877
|
+
this.updateRelatedElementsPosition();
|
|
878
|
+
}, 50);
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Update positions of badge, hide button, and message bubbles based on current button position
|
|
886
|
+
*/
|
|
887
|
+
updateRelatedElementsPosition() {
|
|
888
|
+
if (!this.websiteWidget) return;
|
|
889
|
+
|
|
890
|
+
const buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
891
|
+
|
|
892
|
+
// Check if we have a custom position (not default)
|
|
893
|
+
const savedPosition = this.loadWidgetPosition();
|
|
894
|
+
if (!savedPosition) return; // Use default CSS positioning
|
|
895
|
+
|
|
896
|
+
// Update badge position
|
|
897
|
+
if (this.countBadge) {
|
|
898
|
+
this.countBadge.style.left = `${buttonRect.right - 15}px`;
|
|
899
|
+
this.countBadge.style.right = 'unset';
|
|
900
|
+
this.countBadge.style.bottom = `${window.innerHeight - buttonRect.top + 5}px`;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Update hide button position (top right of bubble)
|
|
904
|
+
if (this.hideWidgetButton) {
|
|
905
|
+
this.hideWidgetButton.style.left = `${buttonRect.right - 5}px`;
|
|
906
|
+
this.hideWidgetButton.style.right = 'unset';
|
|
907
|
+
this.hideWidgetButton.style.bottom = `${window.innerHeight - buttonRect.top + 5}px`;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Update message bubbles container position
|
|
911
|
+
if (this.messageBubblesContainer) {
|
|
912
|
+
this.messageBubblesContainer.style.left = `${buttonRect.left}px`;
|
|
913
|
+
this.messageBubblesContainer.style.right = 'unset';
|
|
914
|
+
this.messageBubblesContainer.style.bottom = `${window.innerHeight - buttonRect.top + 20}px`;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Position the widget content (modal) relative to the bubble position
|
|
920
|
+
*/
|
|
921
|
+
positionWidgetContent() {
|
|
922
|
+
if (!this.widgetContent || !this.websiteWidget || this.isMobileDevice()) return;
|
|
923
|
+
|
|
924
|
+
let buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
925
|
+
const modalWidth = 450; // Default modal width
|
|
926
|
+
const modalHeight = 725; // Default modal height
|
|
927
|
+
const padding = 10;
|
|
928
|
+
const gap = 5; // Small gap between bubble and modal
|
|
929
|
+
|
|
930
|
+
// Check if there's enough space above the bubble for the modal
|
|
931
|
+
const spaceAbove = buttonRect.top - gap - padding;
|
|
932
|
+
const effectiveModalHeight = Math.min(modalHeight, window.innerHeight - 100); // Leave some space
|
|
933
|
+
|
|
934
|
+
if (spaceAbove < effectiveModalHeight) {
|
|
935
|
+
// Not enough space - move bubble down
|
|
936
|
+
const neededSpace = effectiveModalHeight + gap + padding;
|
|
937
|
+
const newBottomFromViewport = window.innerHeight - neededSpace - buttonRect.height;
|
|
938
|
+
|
|
939
|
+
// Update bubble position
|
|
940
|
+
if (this.widgetWrapper) {
|
|
941
|
+
this.widgetWrapper.style.bottom = `${newBottomFromViewport}px`;
|
|
942
|
+
// Save the new position
|
|
943
|
+
const wrapperRect = this.widgetWrapper.getBoundingClientRect();
|
|
944
|
+
this.saveWidgetPosition(wrapperRect.left, wrapperRect.top);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Get updated button position
|
|
948
|
+
buttonRect = this.websiteWidget.getBoundingClientRect();
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// Horizontal position - align modal's right edge with bubble's right edge
|
|
952
|
+
const rightFromViewport = window.innerWidth - buttonRect.right;
|
|
953
|
+
this.widgetContent.style.right = `${rightFromViewport}px`;
|
|
954
|
+
this.widgetContent.style.left = 'unset';
|
|
955
|
+
|
|
956
|
+
// Check if modal would go off-screen on the left
|
|
957
|
+
const modalLeft = buttonRect.right - modalWidth;
|
|
958
|
+
if (modalLeft < padding) {
|
|
959
|
+
// Shift modal to the right to keep it on screen
|
|
960
|
+
this.widgetContent.style.left = `${padding}px`;
|
|
961
|
+
this.widgetContent.style.right = 'unset';
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Vertical position - directly above the button with small gap
|
|
965
|
+
const bottomFromViewport = window.innerHeight - buttonRect.top + gap;
|
|
966
|
+
this.widgetContent.style.bottom = `${bottomFromViewport}px`;
|
|
967
|
+
this.widgetContent.style.top = 'unset';
|
|
968
|
+
|
|
969
|
+
// Reset max-height to default
|
|
970
|
+
this.widgetContent.style.maxHeight = '';
|
|
971
|
+
}
|
|
972
|
+
|
|
621
973
|
}
|