aumera-on-screen-widget 0.0.20 → 0.0.22
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 +259 -67
- 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) {
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
showNotification: false,
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
-
// Track if dark mode was explicitly requested
|
|
153
|
+
// Track if dark mode was explicitly requested
|
|
154
154
|
const hasExplicitDarkBackground = !!wrapper.getAttribute("backgroundDark");
|
|
155
155
|
|
|
156
156
|
// Initial config from HTML attributes with fallback to defaults
|
|
@@ -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,13 @@
|
|
|
436
452
|
bottom: 0 !important;
|
|
437
453
|
margin: 0 !important;
|
|
438
454
|
border-radius: 0 !important;
|
|
439
|
-
|
|
440
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
455
|
+
z-index: 10000;
|
|
441
456
|
}
|
|
442
457
|
.df-btn.df-maximized .df-btn-content {
|
|
443
458
|
width: 100vw !important;
|
|
444
459
|
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;
|
|
460
|
+
height: 100dvh !important;
|
|
461
|
+
max-height: 100dvh !important;
|
|
449
462
|
opacity: 1 !important;
|
|
450
463
|
float: none !important;
|
|
451
464
|
display: block !important;
|
|
@@ -457,8 +470,8 @@
|
|
|
457
470
|
opacity: 0 !important;
|
|
458
471
|
}
|
|
459
472
|
.close-btn {
|
|
460
|
-
width:
|
|
461
|
-
height:
|
|
473
|
+
width: auto;
|
|
474
|
+
height: auto;
|
|
462
475
|
cursor: pointer;
|
|
463
476
|
margin-left: 12px;
|
|
464
477
|
display: flex;
|
|
@@ -466,6 +479,18 @@
|
|
|
466
479
|
justify-content: center;
|
|
467
480
|
}
|
|
468
481
|
|
|
482
|
+
.df-icon-container {
|
|
483
|
+
display: flex;
|
|
484
|
+
align-items: center;
|
|
485
|
+
justify-content: center;
|
|
486
|
+
width: 36px;
|
|
487
|
+
height: 36px;
|
|
488
|
+
border-radius: 8px;
|
|
489
|
+
background-color: ${config.background || "#FFFFFF"}E6;
|
|
490
|
+
backdrop-filter: blur(4px);
|
|
491
|
+
-webkit-backdrop-filter: blur(4px);
|
|
492
|
+
}
|
|
493
|
+
|
|
469
494
|
/* Animation classes for FLIP */
|
|
470
495
|
.df-animating {
|
|
471
496
|
transition: transform .45s cubic-bezier(.4, 0, .2, 1);
|
|
@@ -485,16 +510,12 @@
|
|
|
485
510
|
.df-btn:not(.df-closed) {
|
|
486
511
|
margin: 0px;
|
|
487
512
|
border-radius: 0px;
|
|
488
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
489
513
|
}
|
|
490
514
|
|
|
491
515
|
.df-btn:not(.df-closed) > .df-btn-content {
|
|
492
516
|
width: 100vw;
|
|
493
|
-
max-height: 100vh;
|
|
494
517
|
max-height: 100dvh;
|
|
495
|
-
height:
|
|
496
|
-
height: calc(100dvh - 56px - env(safe-area-inset-bottom, 0px));
|
|
497
|
-
padding-bottom: 0px
|
|
518
|
+
height: 100dvh;
|
|
498
519
|
}
|
|
499
520
|
|
|
500
521
|
.df-btn-text {
|
|
@@ -502,14 +523,26 @@
|
|
|
502
523
|
height: 56px;
|
|
503
524
|
font-size: 16px;
|
|
504
525
|
}
|
|
526
|
+
|
|
527
|
+
/* Hide the pseudo-element on mobile when open (we use .header-logo instead) */
|
|
528
|
+
.df-btn:not(.df-closed) > .df-btn-header > .df-btn-text:before {
|
|
529
|
+
display: none;
|
|
530
|
+
}
|
|
505
531
|
}
|
|
506
532
|
|
|
507
|
-
${
|
|
533
|
+
${
|
|
534
|
+
config.hasDarkMode
|
|
535
|
+
? `
|
|
508
536
|
@media (prefers-color-scheme: dark){
|
|
509
537
|
.df-btn {
|
|
510
538
|
background-color: ${config.backgroundDark || "#171717"}
|
|
511
539
|
}
|
|
512
540
|
|
|
541
|
+
/* iOS Safari keyboard fix: Explicit background for dark mode */
|
|
542
|
+
.df-btn-content {
|
|
543
|
+
background-color: ${config.backgroundDark || "#171717"}
|
|
544
|
+
}
|
|
545
|
+
|
|
513
546
|
.df-btn-text {
|
|
514
547
|
color: ${config.fontColorDark || "#FFFFFF"}
|
|
515
548
|
}
|
|
@@ -522,10 +555,6 @@
|
|
|
522
555
|
}')
|
|
523
556
|
}
|
|
524
557
|
|
|
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
558
|
.df-notification {
|
|
530
559
|
background: ${config.backgroundDark || "#171717"};
|
|
531
560
|
color: ${config.fontColorDark || "#FFFFFF"};
|
|
@@ -534,8 +563,14 @@
|
|
|
534
563
|
.df-notification:after {
|
|
535
564
|
border-top: 6px solid ${config.backgroundDark || "#171717"};
|
|
536
565
|
}
|
|
566
|
+
|
|
567
|
+
.df-icon-container {
|
|
568
|
+
background-color: ${config.backgroundDark || "#171717"}E6;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
`
|
|
572
|
+
: ""
|
|
537
573
|
}
|
|
538
|
-
` : ''}
|
|
539
574
|
|
|
540
575
|
@keyframes shake {
|
|
541
576
|
0%, 100% { transform: translateX(0); }
|
|
@@ -557,6 +592,57 @@
|
|
|
557
592
|
display: none;
|
|
558
593
|
}
|
|
559
594
|
|
|
595
|
+
/* iOS Safari keyboard background bleed fix: Override vh with dvh */
|
|
596
|
+
@supports (-webkit-touch-callout: none) {
|
|
597
|
+
.df-btn-content {
|
|
598
|
+
max-height: 100dvh !important;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.df-btn.df-maximized {
|
|
602
|
+
height: 100dvh !important;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.df-btn.df-maximized .df-btn-content {
|
|
606
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
607
|
+
max-height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
@media screen and (max-width: 720px) {
|
|
611
|
+
.df-btn:not(.df-closed) > .df-btn-content {
|
|
612
|
+
max-height: 100dvh !important;
|
|
613
|
+
height: calc(100dvh - env(safe-area-inset-bottom, 0px)) !important;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* iOS Safari: Force solid background painting to prevent transparency during keyboard animation */
|
|
619
|
+
@supports (-webkit-touch-callout: none) {
|
|
620
|
+
.df-btn {
|
|
621
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
622
|
+
-webkit-backface-visibility: hidden;
|
|
623
|
+
backface-visibility: hidden;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.df-btn-content {
|
|
627
|
+
background-color: ${config.background || "#FEFFFF"} !important;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
${
|
|
631
|
+
config.hasDarkMode
|
|
632
|
+
? `
|
|
633
|
+
@media (prefers-color-scheme: dark) {
|
|
634
|
+
.df-btn {
|
|
635
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
636
|
+
}
|
|
637
|
+
.df-btn-content {
|
|
638
|
+
background-color: ${config.backgroundDark || "#171717"} !important;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
`
|
|
642
|
+
: ""
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
560
646
|
.df-btn-loading .df-btn-text:after {
|
|
561
647
|
content: '';
|
|
562
648
|
display: inline-block;
|
|
@@ -658,7 +744,7 @@
|
|
|
658
744
|
} catch (e) {
|
|
659
745
|
console.warn(
|
|
660
746
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
661
|
-
e
|
|
747
|
+
e,
|
|
662
748
|
);
|
|
663
749
|
}
|
|
664
750
|
}
|
|
@@ -711,6 +797,56 @@
|
|
|
711
797
|
}
|
|
712
798
|
};
|
|
713
799
|
|
|
800
|
+
// iOS Safari detection for keyboard background bleed fix
|
|
801
|
+
const isMobileDevice =
|
|
802
|
+
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
803
|
+
navigator.userAgent,
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
// Store original body styles for scroll lock restoration
|
|
807
|
+
let savedBodyStyles = null;
|
|
808
|
+
|
|
809
|
+
// Lock body scroll on mobile to prevent background bleed when keyboard opens (iOS Safari fix)
|
|
810
|
+
function lockBodyScroll() {
|
|
811
|
+
if (!isMobileDevice) return;
|
|
812
|
+
|
|
813
|
+
savedBodyStyles = {
|
|
814
|
+
position: document.body.style.position,
|
|
815
|
+
top: document.body.style.top,
|
|
816
|
+
left: document.body.style.left,
|
|
817
|
+
right: document.body.style.right,
|
|
818
|
+
width: document.body.style.width,
|
|
819
|
+
height: document.body.style.height,
|
|
820
|
+
overflow: document.body.style.overflow,
|
|
821
|
+
scrollY: window.scrollY,
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// Lock the body to prevent background scroll during keyboard open
|
|
825
|
+
document.body.style.position = "fixed";
|
|
826
|
+
document.body.style.top = `-${savedBodyStyles.scrollY}px`;
|
|
827
|
+
document.body.style.left = "0";
|
|
828
|
+
document.body.style.right = "0";
|
|
829
|
+
document.body.style.width = "100%";
|
|
830
|
+
document.body.style.overflow = "hidden";
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Restore body scroll when chat closes
|
|
834
|
+
function unlockBodyScroll() {
|
|
835
|
+
if (!isMobileDevice || !savedBodyStyles) return;
|
|
836
|
+
|
|
837
|
+
document.body.style.position = savedBodyStyles.position;
|
|
838
|
+
document.body.style.top = savedBodyStyles.top;
|
|
839
|
+
document.body.style.left = savedBodyStyles.left;
|
|
840
|
+
document.body.style.right = savedBodyStyles.right;
|
|
841
|
+
document.body.style.width = savedBodyStyles.width;
|
|
842
|
+
document.body.style.height = savedBodyStyles.height;
|
|
843
|
+
document.body.style.overflow = savedBodyStyles.overflow;
|
|
844
|
+
|
|
845
|
+
// Restore scroll position
|
|
846
|
+
window.scrollTo(0, savedBodyStyles.scrollY);
|
|
847
|
+
savedBodyStyles = null;
|
|
848
|
+
}
|
|
849
|
+
|
|
714
850
|
// Create and inject the iframe on first open
|
|
715
851
|
function ensureIframeLoaded() {
|
|
716
852
|
return new Promise((resolve) => {
|
|
@@ -734,6 +870,29 @@
|
|
|
734
870
|
iframe.allow = "microphone *";
|
|
735
871
|
iframe.style.opacity = "0";
|
|
736
872
|
|
|
873
|
+
// iOS Safari keyboard background bleed fix: Make iframe a fixed-position repaint boundary on mobile
|
|
874
|
+
if (isMobileDevice) {
|
|
875
|
+
iframe.style.position = "fixed";
|
|
876
|
+
iframe.style.top = "0";
|
|
877
|
+
iframe.style.bottom = "0"; // Stretch from top to bottom
|
|
878
|
+
iframe.style.left = "0";
|
|
879
|
+
iframe.style.right = "0";
|
|
880
|
+
iframe.style.width = "100vw";
|
|
881
|
+
iframe.style.height = "100%"; // Fill between top and bottom
|
|
882
|
+
iframe.style.zIndex = "10000"; // Higher than widget container (999)
|
|
883
|
+
// Explicit background color to prevent transparency during iOS keyboard animation
|
|
884
|
+
const systemDarkMode =
|
|
885
|
+
config.hasDarkMode &&
|
|
886
|
+
window.matchMedia &&
|
|
887
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
888
|
+
iframe.style.backgroundColor = systemDarkMode
|
|
889
|
+
? config.backgroundDark || "#171717"
|
|
890
|
+
: config.background || "#FEFFFF";
|
|
891
|
+
// Force GPU layer for better repaint boundary
|
|
892
|
+
iframe.style.transform = "translateZ(0)";
|
|
893
|
+
iframe.style.webkitTransform = "translateZ(0)";
|
|
894
|
+
}
|
|
895
|
+
|
|
737
896
|
iframe.addEventListener("load", () => {
|
|
738
897
|
iframe.style.opacity = "";
|
|
739
898
|
iframeLoaded = true;
|
|
@@ -760,6 +919,8 @@
|
|
|
760
919
|
if (oldClose) header.removeChild(oldClose);
|
|
761
920
|
const oldMax = header.querySelector(".maximize-minimize-btn");
|
|
762
921
|
if (oldMax) header.removeChild(oldMax);
|
|
922
|
+
const oldLogo = header.querySelector(".header-logo");
|
|
923
|
+
if (oldLogo) header.removeChild(oldLogo);
|
|
763
924
|
const btnText = header.querySelector(".df-btn-text");
|
|
764
925
|
const btn = document.querySelector(".df-btn");
|
|
765
926
|
if (!btn) return;
|
|
@@ -770,41 +931,62 @@
|
|
|
770
931
|
// remove right padding from df-btn-text
|
|
771
932
|
btnText.style.paddingRight = "0";
|
|
772
933
|
|
|
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");
|
|
934
|
+
// Detect mobile view for conditional button display
|
|
935
|
+
const isMobileView = window.innerWidth <= 720;
|
|
779
936
|
const systemDarkMode =
|
|
780
937
|
config.hasDarkMode &&
|
|
781
938
|
window.matchMedia &&
|
|
782
939
|
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
783
940
|
// Use white icons only if the actual background is dark
|
|
784
|
-
const currentBg = systemDarkMode
|
|
941
|
+
const currentBg = systemDarkMode
|
|
942
|
+
? config.backgroundDark
|
|
943
|
+
: config.background;
|
|
785
944
|
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
945
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
946
|
+
if (!isMobileView) {
|
|
947
|
+
// Desktop layout: [Expand] ... [Close]
|
|
948
|
+
|
|
949
|
+
// Expand button on LEFT
|
|
950
|
+
const maxBtn = document.createElement("div");
|
|
951
|
+
maxBtn.className = "maximize-minimize-btn";
|
|
952
|
+
const maximized = btn.classList.contains("df-maximized");
|
|
953
|
+
maxBtn.innerHTML = `<div class="df-icon-container"><img class="maximize-minimize-icon" src="${origin}/assets/${
|
|
954
|
+
maximized ? "collapse" : "expand"
|
|
955
|
+
}${useDarkIcons ? "_dark" : ""}.svg" alt="${
|
|
956
|
+
maximized ? "Minimize" : "Maximize"
|
|
957
|
+
}" style="width:24px;height:24px;cursor:pointer;" /></div>`;
|
|
958
|
+
header.insertBefore(maxBtn, btnText); // LEFT
|
|
959
|
+
maxBtn.addEventListener("click", (e) => {
|
|
960
|
+
e.stopPropagation();
|
|
961
|
+
maximizeMinimize();
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// Close button on RIGHT
|
|
965
|
+
const closeBtn = document.createElement("div");
|
|
966
|
+
closeBtn.className = "close-btn";
|
|
967
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
968
|
+
useDarkIcons ? "_dark" : ""
|
|
969
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
970
|
+
header.appendChild(closeBtn); // RIGHT
|
|
971
|
+
closeBtn.addEventListener("click", (e) => {
|
|
972
|
+
e.stopPropagation();
|
|
973
|
+
dfToggle();
|
|
974
|
+
});
|
|
975
|
+
} else {
|
|
976
|
+
// Mobile layout: [Close] on right only
|
|
977
|
+
|
|
978
|
+
// Close button on RIGHT
|
|
979
|
+
const closeBtn = document.createElement("div");
|
|
980
|
+
closeBtn.className = "close-btn";
|
|
981
|
+
closeBtn.innerHTML = `<div class="df-icon-container"><img src="${origin}/assets/close${
|
|
982
|
+
useDarkIcons ? "_dark" : ""
|
|
983
|
+
}.svg" alt="Close" style="width:20px;height:20px;cursor:pointer;" /></div>`;
|
|
984
|
+
header.appendChild(closeBtn); // RIGHT
|
|
985
|
+
closeBtn.addEventListener("click", (e) => {
|
|
986
|
+
e.stopPropagation();
|
|
987
|
+
dfToggle();
|
|
988
|
+
});
|
|
989
|
+
}
|
|
808
990
|
} else {
|
|
809
991
|
// Set text to openText
|
|
810
992
|
if (btnText) btnText.innerText = config.openText || "Chat";
|
|
@@ -875,6 +1057,13 @@
|
|
|
875
1057
|
// Always remove maximized state when closing
|
|
876
1058
|
if (btn.classList.contains("df-closed")) {
|
|
877
1059
|
btn.classList.remove("df-maximized");
|
|
1060
|
+
// iOS Safari keyboard fix: Unlock body scroll when chat closes
|
|
1061
|
+
unlockBodyScroll();
|
|
1062
|
+
} else {
|
|
1063
|
+
// iOS Safari keyboard fix: Lock body scroll when chat opens on mobile
|
|
1064
|
+
if (isMobileDevice) {
|
|
1065
|
+
lockBodyScroll();
|
|
1066
|
+
}
|
|
878
1067
|
}
|
|
879
1068
|
updateHeaderButtons();
|
|
880
1069
|
clearInactivityAndShake();
|
|
@@ -914,7 +1103,7 @@
|
|
|
914
1103
|
} catch (e) {
|
|
915
1104
|
console.warn(
|
|
916
1105
|
"[OSW Widget] Could not save notification dismissed state:",
|
|
917
|
-
e
|
|
1106
|
+
e,
|
|
918
1107
|
);
|
|
919
1108
|
}
|
|
920
1109
|
|
|
@@ -926,6 +1115,9 @@
|
|
|
926
1115
|
const isMobile = window.innerWidth <= 768;
|
|
927
1116
|
|
|
928
1117
|
if (isMobile) {
|
|
1118
|
+
// iOS Safari keyboard fix: Lock body scroll when opening chat on mobile
|
|
1119
|
+
lockBodyScroll();
|
|
1120
|
+
|
|
929
1121
|
// On mobile, apply FLIP animation for open+maximize
|
|
930
1122
|
const content = btn.querySelector(".df-btn-content");
|
|
931
1123
|
|