aumera-on-screen-widget 0.0.20 → 0.0.21
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/df-btn.js +257 -66
- package/package.json +1 -1
package/df-btn.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
const transform = `w_${width},h_${height},c_fill,f_auto,q_auto`;
|
|
32
32
|
|
|
33
33
|
// Insert transformation right after `/upload/`
|
|
34
|
-
return url.replace(
|
|
34
|
+
return url.replace("/upload/", `/upload/${transform}/`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Async function to fetch chatbot settings from GraphQL
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"env:",
|
|
46
46
|
env,
|
|
47
47
|
"language:",
|
|
48
|
-
language
|
|
48
|
+
language,
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
const query = `
|
|
@@ -81,14 +81,14 @@
|
|
|
81
81
|
console.log(
|
|
82
82
|
"[OSW Widget] Response status:",
|
|
83
83
|
response.status,
|
|
84
|
-
response.statusText
|
|
84
|
+
response.statusText,
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
if (!response.ok) {
|
|
88
88
|
console.error(
|
|
89
89
|
"[OSW Widget] Failed to fetch - Status:",
|
|
90
90
|
response.status,
|
|
91
|
-
response.statusText
|
|
91
|
+
response.statusText,
|
|
92
92
|
);
|
|
93
93
|
const errorText = await response
|
|
94
94
|
.text()
|
|
@@ -103,13 +103,13 @@
|
|
|
103
103
|
if (result.data?.oswChatbotSettingsPublic?.success) {
|
|
104
104
|
console.log(
|
|
105
105
|
"[OSW Widget] Settings retrieved successfully:",
|
|
106
|
-
result.data.oswChatbotSettingsPublic.settings
|
|
106
|
+
result.data.oswChatbotSettingsPublic.settings,
|
|
107
107
|
);
|
|
108
108
|
return result.data.oswChatbotSettingsPublic.settings;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
console.warn(
|
|
112
|
-
"[OSW Widget] GraphQL query succeeded but success=false or missing data"
|
|
112
|
+
"[OSW Widget] GraphQL query succeeded but success=false or missing data",
|
|
113
113
|
);
|
|
114
114
|
return null;
|
|
115
115
|
} catch (error) {
|
|
@@ -200,10 +200,10 @@
|
|
|
200
200
|
// Fetch settings from GraphQL and merge with config
|
|
201
201
|
if (!config.orgId) {
|
|
202
202
|
console.error(
|
|
203
|
-
"[OSW Widget] No organization ID provided! Widget will not be displayed."
|
|
203
|
+
"[OSW Widget] No organization ID provided! Widget will not be displayed.",
|
|
204
204
|
);
|
|
205
205
|
console.error(
|
|
206
|
-
"[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>"
|
|
206
|
+
"[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>",
|
|
207
207
|
);
|
|
208
208
|
return; // Exit if no orgId
|
|
209
209
|
}
|
|
@@ -211,12 +211,12 @@
|
|
|
211
211
|
const settings = await fetchChatbotSettings(
|
|
212
212
|
config.orgId,
|
|
213
213
|
config.env,
|
|
214
|
-
detectedLang
|
|
214
|
+
detectedLang,
|
|
215
215
|
);
|
|
216
216
|
|
|
217
217
|
if (!settings) {
|
|
218
218
|
console.warn(
|
|
219
|
-
"[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration."
|
|
219
|
+
"[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration.",
|
|
220
220
|
);
|
|
221
221
|
console.warn("[OSW Widget] Check:");
|
|
222
222
|
console.warn("[OSW Widget] 1. Network tab for CORS errors");
|
|
@@ -235,7 +235,8 @@
|
|
|
235
235
|
config.backgroundDark =
|
|
236
236
|
settings.oswColourBackgroundDark || config.backgroundDark;
|
|
237
237
|
// Update hasDarkMode if GraphQL provides dark background
|
|
238
|
-
config.hasDarkMode =
|
|
238
|
+
config.hasDarkMode =
|
|
239
|
+
config.hasDarkMode || !!settings.oswColourBackgroundDark;
|
|
239
240
|
config.fontColor = settings.oswColourFontLight || config.fontColor;
|
|
240
241
|
config.fontColorDark = settings.oswColourFontDark || config.fontColorDark;
|
|
241
242
|
config.showNotification =
|
|
@@ -248,7 +249,7 @@
|
|
|
248
249
|
config.logoDark = cloudinaryResize(settings.chatbotMask, 64, 64);
|
|
249
250
|
console.log(
|
|
250
251
|
"[OSW Widget] Using chatbotMask as logo:",
|
|
251
|
-
settings.chatbotMask
|
|
252
|
+
settings.chatbotMask,
|
|
252
253
|
);
|
|
253
254
|
}
|
|
254
255
|
}
|
|
@@ -361,6 +362,18 @@
|
|
|
361
362
|
height: 48px;
|
|
362
363
|
}
|
|
363
364
|
|
|
365
|
+
/* Make header transparent when chat is open */
|
|
366
|
+
.df-btn:not(.df-closed) .df-btn-header {
|
|
367
|
+
background: transparent;
|
|
368
|
+
position: absolute;
|
|
369
|
+
top: 0;
|
|
370
|
+
left: 0;
|
|
371
|
+
right: 0;
|
|
372
|
+
z-index: 10001;
|
|
373
|
+
padding: 12px;
|
|
374
|
+
box-sizing: border-box;
|
|
375
|
+
}
|
|
376
|
+
|
|
364
377
|
.df-btn-text:before {
|
|
365
378
|
background-position: center;
|
|
366
379
|
background-repeat: no-repeat;
|
|
@@ -382,11 +395,12 @@
|
|
|
382
395
|
}
|
|
383
396
|
|
|
384
397
|
.df-btn:not(.df-closed){
|
|
385
|
-
border-radius: 16px
|
|
398
|
+
border-radius: 16px;
|
|
399
|
+
overflow: hidden;
|
|
386
400
|
}
|
|
387
401
|
|
|
388
|
-
.df-btn:not(.df-closed)
|
|
389
|
-
|
|
402
|
+
.df-btn:not(.df-closed) .df-btn-text:before {
|
|
403
|
+
display: none;
|
|
390
404
|
}
|
|
391
405
|
|
|
392
406
|
.df-btn-content {
|
|
@@ -403,6 +417,8 @@
|
|
|
403
417
|
${config.position === "left" ? "float: left;" : "float: right;"}
|
|
404
418
|
opacity: 1;
|
|
405
419
|
border-radius: 0 0 16px 16px;
|
|
420
|
+
/* iOS Safari keyboard fix: Explicit background to prevent transparency bleed */
|
|
421
|
+
background-color: ${config.background || "#FEFFFF"};
|
|
406
422
|
}
|
|
407
423
|
|
|
408
424
|
.df-btn:not(.df-closed) > .df-btn-content {
|
|
@@ -417,8 +433,8 @@
|
|
|
417
433
|
}
|
|
418
434
|
|
|
419
435
|
.maximize-minimize-btn {
|
|
420
|
-
width:
|
|
421
|
-
height:
|
|
436
|
+
width: auto;
|
|
437
|
+
height: auto;
|
|
422
438
|
cursor: pointer;
|
|
423
439
|
margin-right: 12px;
|
|
424
440
|
display: flex;
|
|
@@ -436,16 +452,12 @@
|
|
|
436
452
|
bottom: 0 !important;
|
|
437
453
|
margin: 0 !important;
|
|
438
454
|
border-radius: 0 !important;
|
|
439
|
-
/* Handle safe areas on mobile devices */
|
|
440
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
441
455
|
}
|
|
442
456
|
.df-btn.df-maximized .df-btn-content {
|
|
443
457
|
width: 100vw !important;
|
|
444
458
|
max-width: 100vw !important;
|
|
445
|
-
height:
|
|
446
|
-
height:
|
|
447
|
-
max-height: calc(100vh - 56px) !important;
|
|
448
|
-
max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
459
|
+
height: 100dvh !important;
|
|
460
|
+
max-height: 100dvh !important;
|
|
449
461
|
opacity: 1 !important;
|
|
450
462
|
float: none !important;
|
|
451
463
|
display: block !important;
|
|
@@ -457,8 +469,8 @@
|
|
|
457
469
|
opacity: 0 !important;
|
|
458
470
|
}
|
|
459
471
|
.close-btn {
|
|
460
|
-
width:
|
|
461
|
-
height:
|
|
472
|
+
width: auto;
|
|
473
|
+
height: auto;
|
|
462
474
|
cursor: pointer;
|
|
463
475
|
margin-left: 12px;
|
|
464
476
|
display: flex;
|
|
@@ -466,6 +478,18 @@
|
|
|
466
478
|
justify-content: center;
|
|
467
479
|
}
|
|
468
480
|
|
|
481
|
+
.df-icon-container {
|
|
482
|
+
display: flex;
|
|
483
|
+
align-items: center;
|
|
484
|
+
justify-content: center;
|
|
485
|
+
width: 36px;
|
|
486
|
+
height: 36px;
|
|
487
|
+
border-radius: 8px;
|
|
488
|
+
background-color: ${config.background || "#FFFFFF"}E6;
|
|
489
|
+
backdrop-filter: blur(4px);
|
|
490
|
+
-webkit-backdrop-filter: blur(4px);
|
|
491
|
+
}
|
|
492
|
+
|
|
469
493
|
/* Animation classes for FLIP */
|
|
470
494
|
.df-animating {
|
|
471
495
|
transition: transform .45s cubic-bezier(.4, 0, .2, 1);
|
|
@@ -485,16 +509,12 @@
|
|
|
485
509
|
.df-btn:not(.df-closed) {
|
|
486
510
|
margin: 0px;
|
|
487
511
|
border-radius: 0px;
|
|
488
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
489
512
|
}
|
|
490
513
|
|
|
491
514
|
.df-btn:not(.df-closed) > .df-btn-content {
|
|
492
515
|
width: 100vw;
|
|
493
|
-
max-height: 100vh;
|
|
494
516
|
max-height: 100dvh;
|
|
495
|
-
height:
|
|
496
|
-
height: calc(100dvh - 56px - env(safe-area-inset-bottom, 0px));
|
|
497
|
-
padding-bottom: 0px
|
|
517
|
+
height: 100dvh;
|
|
498
518
|
}
|
|
499
519
|
|
|
500
520
|
.df-btn-text {
|
|
@@ -502,14 +522,26 @@
|
|
|
502
522
|
height: 56px;
|
|
503
523
|
font-size: 16px;
|
|
504
524
|
}
|
|
525
|
+
|
|
526
|
+
/* Hide the pseudo-element on mobile when open (we use .header-logo instead) */
|
|
527
|
+
.df-btn:not(.df-closed) > .df-btn-header > .df-btn-text:before {
|
|
528
|
+
display: none;
|
|
529
|
+
}
|
|
505
530
|
}
|
|
506
531
|
|
|
507
|
-
${
|
|
532
|
+
${
|
|
533
|
+
config.hasDarkMode
|
|
534
|
+
? `
|
|
508
535
|
@media (prefers-color-scheme: dark){
|
|
509
536
|
.df-btn {
|
|
510
537
|
background-color: ${config.backgroundDark || "#171717"}
|
|
511
538
|
}
|
|
512
539
|
|
|
540
|
+
/* iOS Safari keyboard fix: Explicit background for dark mode */
|
|
541
|
+
.df-btn-content {
|
|
542
|
+
background-color: ${config.backgroundDark || "#171717"}
|
|
543
|
+
}
|
|
544
|
+
|
|
513
545
|
.df-btn-text {
|
|
514
546
|
color: ${config.fontColorDark || "#FFFFFF"}
|
|
515
547
|
}
|
|
@@ -522,10 +554,6 @@
|
|
|
522
554
|
}')
|
|
523
555
|
}
|
|
524
556
|
|
|
525
|
-
.df-btn:not(.df-closed) > .df-btn-text:before {
|
|
526
|
-
background-image: url('${origin}/assets/close${isColorDark(config.backgroundDark) ? "_dark" : ""}.svg')
|
|
527
|
-
}
|
|
528
|
-
|
|
529
557
|
.df-notification {
|
|
530
558
|
background: ${config.backgroundDark || "#171717"};
|
|
531
559
|
color: ${config.fontColorDark || "#FFFFFF"};
|
|
@@ -534,8 +562,14 @@
|
|
|
534
562
|
.df-notification:after {
|
|
535
563
|
border-top: 6px solid ${config.backgroundDark || "#171717"};
|
|
536
564
|
}
|
|
565
|
+
|
|
566
|
+
.df-icon-container {
|
|
567
|
+
background-color: ${config.backgroundDark || "#171717"}E6;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
`
|
|
571
|
+
: ""
|
|
537
572
|
}
|
|
538
|
-
` : ''}
|
|
539
573
|
|
|
540
574
|
@keyframes shake {
|
|
541
575
|
0%, 100% { transform: translateX(0); }
|
|
@@ -557,6 +591,57 @@
|
|
|
557
591
|
display: none;
|
|
558
592
|
}
|
|
559
593
|
|
|
594
|
+
/* iOS Safari keyboard background bleed fix: Override vh with dvh */
|
|
595
|
+
@supports (-webkit-touch-callout: none) {
|
|
596
|
+
.df-btn-content {
|
|
597
|
+
max-height: 100dvh !important;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.df-btn.df-maximized {
|
|
601
|
+
height: 100dvh !important;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.df-btn.df-maximized .df-btn-content {
|
|
605
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
606
|
+
max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
@media screen and (max-width: 720px) {
|
|
610
|
+
.df-btn:not(.df-closed) > .df-btn-content {
|
|
611
|
+
max-height: 100dvh !important;
|
|
612
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* iOS Safari: Force solid background painting to prevent transparency during keyboard animation */
|
|
618
|
+
@supports (-webkit-touch-callout: none) {
|
|
619
|
+
.df-btn {
|
|
620
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
621
|
+
-webkit-backface-visibility: hidden;
|
|
622
|
+
backface-visibility: hidden;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.df-btn-content {
|
|
626
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
${
|
|
630
|
+
config.hasDarkMode
|
|
631
|
+
? `
|
|
632
|
+
@media (prefers-color-scheme: dark) {
|
|
633
|
+
.df-btn {
|
|
634
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
635
|
+
}
|
|
636
|
+
.df-btn-content {
|
|
637
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
`
|
|
641
|
+
: ""
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
560
645
|
.df-btn-loading .df-btn-text:after {
|
|
561
646
|
content: '';
|
|
562
647
|
display: inline-block;
|
|
@@ -658,7 +743,7 @@
|
|
|
658
743
|
} catch (e) {
|
|
659
744
|
console.warn(
|
|
660
745
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
661
|
-
e
|
|
746
|
+
e,
|
|
662
747
|
);
|
|
663
748
|
}
|
|
664
749
|
}
|
|
@@ -711,6 +796,56 @@
|
|
|
711
796
|
}
|
|
712
797
|
};
|
|
713
798
|
|
|
799
|
+
// iOS Safari detection for keyboard background bleed fix
|
|
800
|
+
const isMobileDevice =
|
|
801
|
+
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
802
|
+
navigator.userAgent,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
// Store original body styles for scroll lock restoration
|
|
806
|
+
let savedBodyStyles = null;
|
|
807
|
+
|
|
808
|
+
// Lock body scroll on mobile to prevent background bleed when keyboard opens (iOS Safari fix)
|
|
809
|
+
function lockBodyScroll() {
|
|
810
|
+
if (!isMobileDevice) return;
|
|
811
|
+
|
|
812
|
+
savedBodyStyles = {
|
|
813
|
+
position: document.body.style.position,
|
|
814
|
+
top: document.body.style.top,
|
|
815
|
+
left: document.body.style.left,
|
|
816
|
+
right: document.body.style.right,
|
|
817
|
+
width: document.body.style.width,
|
|
818
|
+
height: document.body.style.height,
|
|
819
|
+
overflow: document.body.style.overflow,
|
|
820
|
+
scrollY: window.scrollY,
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// Lock the body to prevent background scroll during keyboard open
|
|
824
|
+
document.body.style.position = "fixed";
|
|
825
|
+
document.body.style.top = `-${savedBodyStyles.scrollY}px`;
|
|
826
|
+
document.body.style.left = "0";
|
|
827
|
+
document.body.style.right = "0";
|
|
828
|
+
document.body.style.width = "100%";
|
|
829
|
+
document.body.style.overflow = "hidden";
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Restore body scroll when chat closes
|
|
833
|
+
function unlockBodyScroll() {
|
|
834
|
+
if (!isMobileDevice || !savedBodyStyles) return;
|
|
835
|
+
|
|
836
|
+
document.body.style.position = savedBodyStyles.position;
|
|
837
|
+
document.body.style.top = savedBodyStyles.top;
|
|
838
|
+
document.body.style.left = savedBodyStyles.left;
|
|
839
|
+
document.body.style.right = savedBodyStyles.right;
|
|
840
|
+
document.body.style.width = savedBodyStyles.width;
|
|
841
|
+
document.body.style.height = savedBodyStyles.height;
|
|
842
|
+
document.body.style.overflow = savedBodyStyles.overflow;
|
|
843
|
+
|
|
844
|
+
// Restore scroll position
|
|
845
|
+
window.scrollTo(0, savedBodyStyles.scrollY);
|
|
846
|
+
savedBodyStyles = null;
|
|
847
|
+
}
|
|
848
|
+
|
|
714
849
|
// Create and inject the iframe on first open
|
|
715
850
|
function ensureIframeLoaded() {
|
|
716
851
|
return new Promise((resolve) => {
|
|
@@ -734,6 +869,29 @@
|
|
|
734
869
|
iframe.allow = "microphone *";
|
|
735
870
|
iframe.style.opacity = "0";
|
|
736
871
|
|
|
872
|
+
// iOS Safari keyboard background bleed fix: Make iframe a fixed-position repaint boundary on mobile
|
|
873
|
+
if (isMobileDevice) {
|
|
874
|
+
iframe.style.position = "fixed";
|
|
875
|
+
iframe.style.top = "0";
|
|
876
|
+
iframe.style.bottom = "0"; // Stretch from top to bottom
|
|
877
|
+
iframe.style.left = "0";
|
|
878
|
+
iframe.style.right = "0";
|
|
879
|
+
iframe.style.width = "100vw";
|
|
880
|
+
iframe.style.height = "100%"; // Fill between top and bottom
|
|
881
|
+
iframe.style.zIndex = "10000"; // Higher than widget container (999)
|
|
882
|
+
// Explicit background color to prevent transparency during iOS keyboard animation
|
|
883
|
+
const systemDarkMode =
|
|
884
|
+
config.hasDarkMode &&
|
|
885
|
+
window.matchMedia &&
|
|
886
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
887
|
+
iframe.style.backgroundColor = systemDarkMode
|
|
888
|
+
? config.backgroundDark || "#171717"
|
|
889
|
+
: config.background || "#FEFFFF";
|
|
890
|
+
// Force GPU layer for better repaint boundary
|
|
891
|
+
iframe.style.transform = "translateZ(0)";
|
|
892
|
+
iframe.style.webkitTransform = "translateZ(0)";
|
|
893
|
+
}
|
|
894
|
+
|
|
737
895
|
iframe.addEventListener("load", () => {
|
|
738
896
|
iframe.style.opacity = "";
|
|
739
897
|
iframeLoaded = true;
|
|
@@ -760,6 +918,8 @@
|
|
|
760
918
|
if (oldClose) header.removeChild(oldClose);
|
|
761
919
|
const oldMax = header.querySelector(".maximize-minimize-btn");
|
|
762
920
|
if (oldMax) header.removeChild(oldMax);
|
|
921
|
+
const oldLogo = header.querySelector(".header-logo");
|
|
922
|
+
if (oldLogo) header.removeChild(oldLogo);
|
|
763
923
|
const btnText = header.querySelector(".df-btn-text");
|
|
764
924
|
const btn = document.querySelector(".df-btn");
|
|
765
925
|
if (!btn) return;
|
|
@@ -770,41 +930,62 @@
|
|
|
770
930
|
// remove right padding from df-btn-text
|
|
771
931
|
btnText.style.paddingRight = "0";
|
|
772
932
|
|
|
773
|
-
//
|
|
774
|
-
|
|
775
|
-
// Add maximize/minimize button (right)
|
|
776
|
-
const maxBtn = document.createElement("div");
|
|
777
|
-
maxBtn.className = "maximize-minimize-btn";
|
|
778
|
-
const maximized = btn.classList.contains("df-maximized");
|
|
933
|
+
// Detect mobile view for conditional button display
|
|
934
|
+
const isMobileView = window.innerWidth <= 720;
|
|
779
935
|
const systemDarkMode =
|
|
780
936
|
config.hasDarkMode &&
|
|
781
937
|
window.matchMedia &&
|
|
782
938
|
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
783
939
|
// Use white icons only if the actual background is dark
|
|
784
|
-
const currentBg = systemDarkMode
|
|
940
|
+
const currentBg = systemDarkMode
|
|
941
|
+
? config.backgroundDark
|
|
942
|
+
: config.background;
|
|
785
943
|
const useDarkIcons = isColorDark(currentBg);
|
|
786
|
-
maxBtn.innerHTML = `<img class="maximize-minimize-icon" src="${origin}/assets/${
|
|
787
|
-
maximized ? "collapse" : "expand"
|
|
788
|
-
}${useDarkIcons ? "_dark" : ""}.svg" alt="${
|
|
789
|
-
maximized ? "Minimize" : "Maximize"
|
|
790
|
-
}" style="width:24px;height:24px;cursor:pointer;" />`;
|
|
791
|
-
header.appendChild(maxBtn);
|
|
792
|
-
maxBtn.addEventListener("click", (e) => {
|
|
793
|
-
e.stopPropagation();
|
|
794
|
-
maximizeMinimize();
|
|
795
|
-
});
|
|
796
944
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
945
|
+
if (!isMobileView) {
|
|
946
|
+
// Desktop layout: [Expand] ... [Close]
|
|
947
|
+
|
|
948
|
+
// Expand button on LEFT
|
|
949
|
+
const maxBtn = document.createElement("div");
|
|
950
|
+
maxBtn.className = "maximize-minimize-btn";
|
|
951
|
+
const maximized = btn.classList.contains("df-maximized");
|
|
952
|
+
maxBtn.innerHTML = `<div class="df-icon-container"><img class="maximize-minimize-icon" src="${origin}/assets/${
|
|
953
|
+
maximized ? "collapse" : "expand"
|
|
954
|
+
}${useDarkIcons ? "_dark" : ""}.svg" alt="${
|
|
955
|
+
maximized ? "Minimize" : "Maximize"
|
|
956
|
+
}" style="width:24px;height:24px;cursor:pointer;" /></div>`;
|
|
957
|
+
header.insertBefore(maxBtn, btnText); // LEFT
|
|
958
|
+
maxBtn.addEventListener("click", (e) => {
|
|
959
|
+
e.stopPropagation();
|
|
960
|
+
maximizeMinimize();
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// Close button on RIGHT
|
|
964
|
+
const closeBtn = document.createElement("div");
|
|
965
|
+
closeBtn.className = "close-btn";
|
|
966
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
967
|
+
useDarkIcons ? "_dark" : ""
|
|
968
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
969
|
+
header.appendChild(closeBtn); // RIGHT
|
|
970
|
+
closeBtn.addEventListener("click", (e) => {
|
|
971
|
+
e.stopPropagation();
|
|
972
|
+
dfToggle();
|
|
973
|
+
});
|
|
974
|
+
} else {
|
|
975
|
+
// Mobile layout: [Close] on right only
|
|
976
|
+
|
|
977
|
+
// Close button on RIGHT
|
|
978
|
+
const closeBtn = document.createElement("div");
|
|
979
|
+
closeBtn.className = "close-btn";
|
|
980
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
981
|
+
useDarkIcons ? "_dark" : ""
|
|
982
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
983
|
+
header.appendChild(closeBtn); // RIGHT
|
|
984
|
+
closeBtn.addEventListener("click", (e) => {
|
|
985
|
+
e.stopPropagation();
|
|
986
|
+
dfToggle();
|
|
987
|
+
});
|
|
988
|
+
}
|
|
808
989
|
} else {
|
|
809
990
|
// Set text to openText
|
|
810
991
|
if (btnText) btnText.innerText = config.openText || "Chat";
|
|
@@ -875,6 +1056,13 @@
|
|
|
875
1056
|
// Always remove maximized state when closing
|
|
876
1057
|
if (btn.classList.contains("df-closed")) {
|
|
877
1058
|
btn.classList.remove("df-maximized");
|
|
1059
|
+
// iOS Safari keyboard fix: Unlock body scroll when chat closes
|
|
1060
|
+
unlockBodyScroll();
|
|
1061
|
+
} else {
|
|
1062
|
+
// iOS Safari keyboard fix: Lock body scroll when chat opens on mobile
|
|
1063
|
+
if (isMobileDevice) {
|
|
1064
|
+
lockBodyScroll();
|
|
1065
|
+
}
|
|
878
1066
|
}
|
|
879
1067
|
updateHeaderButtons();
|
|
880
1068
|
clearInactivityAndShake();
|
|
@@ -914,7 +1102,7 @@
|
|
|
914
1102
|
} catch (e) {
|
|
915
1103
|
console.warn(
|
|
916
1104
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
917
|
-
e
|
|
1105
|
+
e,
|
|
918
1106
|
);
|
|
919
1107
|
}
|
|
920
1108
|
|
|
@@ -926,6 +1114,9 @@
|
|
|
926
1114
|
const isMobile = window.innerWidth <= 768;
|
|
927
1115
|
|
|
928
1116
|
if (isMobile) {
|
|
1117
|
+
// iOS Safari keyboard fix: Lock body scroll when opening chat on mobile
|
|
1118
|
+
lockBodyScroll();
|
|
1119
|
+
|
|
929
1120
|
// On mobile, apply FLIP animation for open+maximize
|
|
930
1121
|
const content = btn.querySelector(".df-btn-content");
|
|
931
1122
|
|