@weldsuite/helpdesk-widget-sdk 1.0.15 → 1.0.17
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/angular.d.ts +36 -41
- package/dist/angular.esm.js +483 -102
- package/dist/angular.esm.js.map +1 -1
- package/dist/angular.js +483 -102
- package/dist/angular.js.map +1 -1
- package/dist/index.d.ts +60 -56
- package/dist/index.esm.js +505 -104
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +506 -103
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +506 -103
- package/dist/index.umd.js.map +1 -1
- package/dist/react.d.ts +36 -41
- package/dist/react.esm.js +483 -102
- package/dist/react.esm.js.map +1 -1
- package/dist/react.js +483 -102
- package/dist/react.js.map +1 -1
- package/dist/vue-composables.esm.js +483 -102
- package/dist/vue-composables.esm.js.map +1 -1
- package/dist/vue-composables.js +483 -102
- package/dist/vue-composables.js.map +1 -1
- package/package.json +4 -4
package/dist/index.umd.js
CHANGED
|
@@ -37,32 +37,6 @@
|
|
|
37
37
|
closeOnClick: true,
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
|
-
customization: {
|
|
41
|
-
primaryColor: '#000000',
|
|
42
|
-
accentColor: '#3b82f6',
|
|
43
|
-
backgroundColor: '#ffffff',
|
|
44
|
-
textColor: '#111827',
|
|
45
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
46
|
-
fontSize: '14px',
|
|
47
|
-
borderRadius: '12px',
|
|
48
|
-
},
|
|
49
|
-
features: {
|
|
50
|
-
attachments: true,
|
|
51
|
-
reactions: true,
|
|
52
|
-
typing: true,
|
|
53
|
-
readReceipts: true,
|
|
54
|
-
offlineMode: false,
|
|
55
|
-
fileUpload: true,
|
|
56
|
-
imageUpload: true,
|
|
57
|
-
voiceMessages: false,
|
|
58
|
-
videoMessages: false,
|
|
59
|
-
},
|
|
60
|
-
mobile: {
|
|
61
|
-
fullScreen: true,
|
|
62
|
-
scrollLock: true,
|
|
63
|
-
keyboardHandling: 'auto',
|
|
64
|
-
safeAreaInsets: true,
|
|
65
|
-
},
|
|
66
40
|
auth: {
|
|
67
41
|
enabled: true,
|
|
68
42
|
mode: 'anonymous',
|
|
@@ -106,6 +80,7 @@
|
|
|
106
80
|
validateConfig(config);
|
|
107
81
|
return {
|
|
108
82
|
widgetId: config.widgetId,
|
|
83
|
+
testMode: config.testMode,
|
|
109
84
|
api: {
|
|
110
85
|
...DEFAULT_CONFIG.api,
|
|
111
86
|
widgetId: config.widgetId,
|
|
@@ -135,18 +110,6 @@
|
|
|
135
110
|
...config.iframes?.backdrop,
|
|
136
111
|
},
|
|
137
112
|
},
|
|
138
|
-
customization: {
|
|
139
|
-
...DEFAULT_CONFIG.customization,
|
|
140
|
-
...config.customization,
|
|
141
|
-
},
|
|
142
|
-
features: {
|
|
143
|
-
...DEFAULT_CONFIG.features,
|
|
144
|
-
...config.features,
|
|
145
|
-
},
|
|
146
|
-
mobile: {
|
|
147
|
-
...DEFAULT_CONFIG.mobile,
|
|
148
|
-
...config.mobile,
|
|
149
|
-
},
|
|
150
113
|
auth: {
|
|
151
114
|
...DEFAULT_CONFIG.auth,
|
|
152
115
|
...config.auth,
|
|
@@ -389,6 +352,8 @@
|
|
|
389
352
|
this.modalContainer = null;
|
|
390
353
|
this.styleElement = null;
|
|
391
354
|
this.messageBroker = null;
|
|
355
|
+
// Guard flag to prevent double-binding event listeners
|
|
356
|
+
this.eventListenersBound = false;
|
|
392
357
|
this.config = config;
|
|
393
358
|
this.logger = new Logger(config.logging);
|
|
394
359
|
this.deviceInfo = detectDevice();
|
|
@@ -428,18 +393,27 @@
|
|
|
428
393
|
}
|
|
429
394
|
/**
|
|
430
395
|
* Create root container structure
|
|
396
|
+
* Reuses existing container if it has the same widgetId (singleton behavior)
|
|
431
397
|
*/
|
|
432
398
|
createRootContainer() {
|
|
433
|
-
|
|
434
|
-
let existingContainer = document.getElementById('weld-container');
|
|
399
|
+
const existingContainer = document.getElementById('weld-container');
|
|
435
400
|
if (existingContainer) {
|
|
436
|
-
|
|
401
|
+
// Reuse if same widgetId
|
|
402
|
+
if (existingContainer.getAttribute('data-widget-id') === this.config.widgetId) {
|
|
403
|
+
this.logger.debug('Reusing existing root container');
|
|
404
|
+
this.rootContainer = existingContainer;
|
|
405
|
+
this.appContainer = existingContainer.querySelector('.weld-app');
|
|
406
|
+
this.modalContainer = document.getElementById('weld-modal-container');
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
this.logger.warn('Weld container already exists with different widgetId, removing old instance');
|
|
437
410
|
existingContainer.remove();
|
|
438
411
|
}
|
|
439
412
|
// Create root container
|
|
440
413
|
this.rootContainer = document.createElement('div');
|
|
441
414
|
this.rootContainer.id = 'weld-container';
|
|
442
415
|
this.rootContainer.className = 'weld-namespace';
|
|
416
|
+
this.rootContainer.setAttribute('data-widget-id', this.config.widgetId);
|
|
443
417
|
// Create app container
|
|
444
418
|
this.appContainer = document.createElement('div');
|
|
445
419
|
this.appContainer.className = 'weld-app';
|
|
@@ -474,17 +448,7 @@
|
|
|
474
448
|
* Generate CSS for containers
|
|
475
449
|
*/
|
|
476
450
|
generateCSS() {
|
|
477
|
-
const { customization } = this.config;
|
|
478
451
|
return `
|
|
479
|
-
/* Weld Container */
|
|
480
|
-
#weld-container {
|
|
481
|
-
--weld-color-primary: ${customization.primaryColor};
|
|
482
|
-
--weld-color-accent: ${customization.accentColor};
|
|
483
|
-
--weld-font-family: ${customization.fontFamily};
|
|
484
|
-
--weld-font-size-base: ${customization.fontSize};
|
|
485
|
-
--weld-radius-xl: ${customization.borderRadius};
|
|
486
|
-
}
|
|
487
|
-
|
|
488
452
|
/* Import main stylesheet */
|
|
489
453
|
@import url('/styles/index.css');
|
|
490
454
|
|
|
@@ -508,20 +472,27 @@
|
|
|
508
472
|
* Create launcher iframe
|
|
509
473
|
*/
|
|
510
474
|
async createLauncherIframe() {
|
|
475
|
+
// Guard: skip if launcher iframe already exists
|
|
476
|
+
if (this.iframes.has(exports.IframeType.LAUNCHER)) {
|
|
477
|
+
this.logger.debug('Launcher iframe already exists, skipping creation');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
511
480
|
const { iframes } = this.config;
|
|
512
481
|
const { launcher } = iframes;
|
|
513
482
|
// Create container
|
|
514
483
|
const container = document.createElement('div');
|
|
515
484
|
container.className = 'weld-launcher-frame';
|
|
516
485
|
container.setAttribute('data-state', 'visible');
|
|
486
|
+
// Container is larger than the button to allow hover animations (scale, shadow) without clipping
|
|
487
|
+
const launcherPadding = 10;
|
|
517
488
|
container.style.cssText = `
|
|
518
489
|
position: fixed;
|
|
519
|
-
bottom: ${launcher.position.bottom};
|
|
520
|
-
right: ${launcher.position.right};
|
|
521
|
-
width: ${launcher.size};
|
|
522
|
-
height: ${launcher.size};
|
|
490
|
+
bottom: calc(${launcher.position.bottom} - ${launcherPadding}px);
|
|
491
|
+
right: calc(${launcher.position.right} - ${launcherPadding}px);
|
|
492
|
+
width: calc(${launcher.size} + ${launcherPadding * 2}px);
|
|
493
|
+
height: calc(${launcher.size} + ${launcherPadding * 2}px);
|
|
523
494
|
z-index: 2147483003;
|
|
524
|
-
pointer-events:
|
|
495
|
+
pointer-events: none;
|
|
525
496
|
display: block;
|
|
526
497
|
`;
|
|
527
498
|
// Create iframe
|
|
@@ -533,8 +504,12 @@
|
|
|
533
504
|
width: 100%;
|
|
534
505
|
height: 100%;
|
|
535
506
|
border: none;
|
|
536
|
-
background:
|
|
507
|
+
background: none;
|
|
508
|
+
color-scheme: none;
|
|
537
509
|
display: block;
|
|
510
|
+
pointer-events: auto;
|
|
511
|
+
border-radius: 50%;
|
|
512
|
+
filter: drop-shadow(rgba(9, 14, 21, 0.54) 0px 1px 6px) drop-shadow(rgba(9, 14, 21, 0.9) 0px 2px 32px);
|
|
538
513
|
`;
|
|
539
514
|
iframe.setAttribute('allow', 'clipboard-write');
|
|
540
515
|
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
|
|
@@ -550,20 +525,36 @@
|
|
|
550
525
|
createdAt: Date.now(),
|
|
551
526
|
});
|
|
552
527
|
// When DOM loads, notify MessageBroker to send weld:init
|
|
528
|
+
let launcherRetried = false;
|
|
553
529
|
iframe.onload = () => {
|
|
554
530
|
const metadata = this.iframes.get(exports.IframeType.LAUNCHER);
|
|
555
531
|
if (metadata) {
|
|
556
532
|
this.logger.debug('Launcher iframe DOM loaded');
|
|
557
|
-
// Notify MessageBroker that DOM is loaded (triggers weld:init)
|
|
558
533
|
this.messageBroker?.setIframeDomLoaded(exports.IframeType.LAUNCHER);
|
|
559
534
|
}
|
|
560
535
|
};
|
|
536
|
+
iframe.onerror = () => {
|
|
537
|
+
this.logger.error('Launcher iframe failed to load');
|
|
538
|
+
if (!launcherRetried) {
|
|
539
|
+
launcherRetried = true;
|
|
540
|
+
this.logger.info('Retrying launcher iframe load...');
|
|
541
|
+
setTimeout(() => { iframe.src = this.buildIframeUrl(launcher.url); }, 3000);
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
this.config.onError?.(new Error('Failed to load widget launcher'));
|
|
545
|
+
}
|
|
546
|
+
};
|
|
561
547
|
this.logger.debug('Launcher iframe created');
|
|
562
548
|
}
|
|
563
549
|
/**
|
|
564
550
|
* Create widget iframe
|
|
565
551
|
*/
|
|
566
552
|
async createWidgetIframe() {
|
|
553
|
+
// Guard: skip if widget iframe already exists
|
|
554
|
+
if (this.iframes.has(exports.IframeType.WIDGET)) {
|
|
555
|
+
this.logger.debug('Widget iframe already exists, skipping creation');
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
567
558
|
const { iframes } = this.config;
|
|
568
559
|
const { widget } = iframes;
|
|
569
560
|
// Create container
|
|
@@ -644,54 +635,32 @@
|
|
|
644
635
|
createdAt: Date.now(),
|
|
645
636
|
});
|
|
646
637
|
// When DOM loads, notify MessageBroker to send weld:init
|
|
638
|
+
let widgetRetried = false;
|
|
647
639
|
iframe.onload = () => {
|
|
648
640
|
const metadata = this.iframes.get(exports.IframeType.WIDGET);
|
|
649
641
|
if (metadata) {
|
|
650
642
|
this.logger.debug('Widget iframe DOM loaded');
|
|
651
|
-
// Notify MessageBroker that DOM is loaded (triggers weld:init)
|
|
652
643
|
this.messageBroker?.setIframeDomLoaded(exports.IframeType.WIDGET);
|
|
653
644
|
}
|
|
654
645
|
};
|
|
646
|
+
iframe.onerror = () => {
|
|
647
|
+
this.logger.error('Widget iframe failed to load');
|
|
648
|
+
if (!widgetRetried) {
|
|
649
|
+
widgetRetried = true;
|
|
650
|
+
this.logger.info('Retrying widget iframe load...');
|
|
651
|
+
setTimeout(() => { iframe.src = this.buildIframeUrl(widget.url); }, 3000);
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
this.config.onError?.(new Error('Failed to load widget'));
|
|
655
|
+
}
|
|
656
|
+
};
|
|
655
657
|
this.logger.debug('Widget iframe created');
|
|
656
658
|
}
|
|
657
659
|
/**
|
|
658
|
-
* Create backdrop iframe
|
|
660
|
+
* Create backdrop iframe — disabled, widget stays non-modal so users can interact with the page
|
|
659
661
|
*/
|
|
660
662
|
async createBackdropIframe() {
|
|
661
|
-
|
|
662
|
-
this.logger.debug('Backdrop disabled, skipping creation');
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
// Create container
|
|
666
|
-
const container = document.createElement('div');
|
|
667
|
-
container.className = 'weld-backdrop-frame';
|
|
668
|
-
container.setAttribute('data-state', 'hidden');
|
|
669
|
-
container.style.cssText = `
|
|
670
|
-
position: fixed;
|
|
671
|
-
top: 0;
|
|
672
|
-
left: 0;
|
|
673
|
-
right: 0;
|
|
674
|
-
bottom: 0;
|
|
675
|
-
z-index: 2147483000;
|
|
676
|
-
background: transparent;
|
|
677
|
-
pointer-events: none;
|
|
678
|
-
opacity: 0;
|
|
679
|
-
transition: opacity 200ms ease;
|
|
680
|
-
`;
|
|
681
|
-
this.appContainer?.appendChild(container);
|
|
682
|
-
// Store metadata (backdrop doesn't have an iframe, just a div)
|
|
683
|
-
// We'll create a minimal "iframe" reference for consistency
|
|
684
|
-
const dummyIframe = document.createElement('iframe');
|
|
685
|
-
dummyIframe.style.display = 'none';
|
|
686
|
-
this.iframes.set(exports.IframeType.BACKDROP, {
|
|
687
|
-
type: exports.IframeType.BACKDROP,
|
|
688
|
-
element: dummyIframe,
|
|
689
|
-
container,
|
|
690
|
-
ready: true, // Backdrop is always ready
|
|
691
|
-
visible: false,
|
|
692
|
-
createdAt: Date.now(),
|
|
693
|
-
});
|
|
694
|
-
this.logger.debug('Backdrop created');
|
|
663
|
+
this.logger.debug('Backdrop disabled, skipping creation');
|
|
695
664
|
}
|
|
696
665
|
/**
|
|
697
666
|
* Build iframe URL with parameters
|
|
@@ -705,12 +674,21 @@
|
|
|
705
674
|
url.searchParams.set('device', this.deviceInfo.type);
|
|
706
675
|
url.searchParams.set('mobile', String(this.deviceInfo.isMobile));
|
|
707
676
|
url.searchParams.set('parentOrigin', window.location.origin);
|
|
677
|
+
if (this.config.testMode) {
|
|
678
|
+
url.searchParams.set('testMode', 'true');
|
|
679
|
+
}
|
|
708
680
|
return url.toString();
|
|
709
681
|
}
|
|
710
682
|
/**
|
|
711
683
|
* Setup event listeners
|
|
712
684
|
*/
|
|
713
685
|
setupEventListeners() {
|
|
686
|
+
// Guard: prevent double-binding
|
|
687
|
+
if (this.eventListenersBound) {
|
|
688
|
+
this.logger.debug('Event listeners already bound, skipping');
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
this.eventListenersBound = true;
|
|
714
692
|
// Window resize - use bound handler for proper cleanup
|
|
715
693
|
window.addEventListener('resize', this.boundHandleResize);
|
|
716
694
|
// Orientation change - use bound handler for proper cleanup
|
|
@@ -813,7 +791,7 @@
|
|
|
813
791
|
iframe.container.style.transform = 'scale(1) translateY(0)';
|
|
814
792
|
}
|
|
815
793
|
// Handle mobile scroll lock
|
|
816
|
-
if (this.deviceInfo.isMobile && type === exports.IframeType.WIDGET
|
|
794
|
+
if (this.deviceInfo.isMobile && type === exports.IframeType.WIDGET) {
|
|
817
795
|
document.body.classList.add('weld-mobile-open');
|
|
818
796
|
}
|
|
819
797
|
// Hide launcher on mobile when widget is open (full-screen mode)
|
|
@@ -912,6 +890,8 @@
|
|
|
912
890
|
this.iframes.clear();
|
|
913
891
|
// Clear messageBroker reference
|
|
914
892
|
this.messageBroker = null;
|
|
893
|
+
// Reset guard flag
|
|
894
|
+
this.eventListenersBound = false;
|
|
915
895
|
this.logger.info('IframeManager destroyed');
|
|
916
896
|
}
|
|
917
897
|
}
|
|
@@ -979,6 +959,8 @@
|
|
|
979
959
|
// Events
|
|
980
960
|
MessageType["EVENT_TRACK"] = "weld:event:track";
|
|
981
961
|
MessageType["ERROR_REPORT"] = "weld:error:report";
|
|
962
|
+
// Page tracking
|
|
963
|
+
MessageType["PAGE_CHANGE"] = "weld:page:change";
|
|
982
964
|
// API responses
|
|
983
965
|
MessageType["API_SUCCESS"] = "weld:api:success";
|
|
984
966
|
MessageType["API_ERROR"] = "weld:api:error";
|
|
@@ -1557,8 +1539,6 @@
|
|
|
1557
1539
|
iframeType,
|
|
1558
1540
|
config: {
|
|
1559
1541
|
api: this.config.api,
|
|
1560
|
-
customization: this.config.customization,
|
|
1561
|
-
features: this.config.features,
|
|
1562
1542
|
},
|
|
1563
1543
|
};
|
|
1564
1544
|
const message = createMessage('weld:init', MessageOrigin.PARENT, initPayload);
|
|
@@ -2401,7 +2381,7 @@
|
|
|
2401
2381
|
}
|
|
2402
2382
|
}
|
|
2403
2383
|
|
|
2404
|
-
var version = "1.0.
|
|
2384
|
+
var version = "1.0.17";
|
|
2405
2385
|
var packageJson = {
|
|
2406
2386
|
version: version};
|
|
2407
2387
|
|
|
@@ -2409,6 +2389,16 @@
|
|
|
2409
2389
|
* Weld SDK - Main Entry Point
|
|
2410
2390
|
* Public API for the Weld helpdesk widget
|
|
2411
2391
|
*/
|
|
2392
|
+
/**
|
|
2393
|
+
* Module-level singleton registry keyed by widgetId
|
|
2394
|
+
*/
|
|
2395
|
+
const sdkRegistry = new Map();
|
|
2396
|
+
/**
|
|
2397
|
+
* SessionStorage key helpers
|
|
2398
|
+
*/
|
|
2399
|
+
function openStateKey(widgetId) {
|
|
2400
|
+
return `weld-widget-open-${widgetId}`;
|
|
2401
|
+
}
|
|
2412
2402
|
/**
|
|
2413
2403
|
* SDK initialization status
|
|
2414
2404
|
*/
|
|
@@ -2431,6 +2421,8 @@
|
|
|
2431
2421
|
this.readyResolve = null;
|
|
2432
2422
|
// Subscription IDs for cleanup
|
|
2433
2423
|
this.subscriptionIds = [];
|
|
2424
|
+
// Page tracking cleanup
|
|
2425
|
+
this.pageTrackingCleanup = null;
|
|
2434
2426
|
/**
|
|
2435
2427
|
* Update user attributes (Intercom-style, with rate limiting)
|
|
2436
2428
|
* Limited to 20 calls per page load to prevent abuse
|
|
@@ -2469,6 +2461,13 @@
|
|
|
2469
2461
|
console.log('[Weld SDK] Received message:', event.data.type);
|
|
2470
2462
|
}
|
|
2471
2463
|
if (event.data?.type === 'launcher:clicked') {
|
|
2464
|
+
if (this.status !== SDKStatus.READY) {
|
|
2465
|
+
console.log('[Weld SDK] Launcher clicked but SDK not ready yet — waiting...');
|
|
2466
|
+
this.readyPromise?.then(() => {
|
|
2467
|
+
this.handleLauncherClickMessage(event);
|
|
2468
|
+
});
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2472
2471
|
// Toggle behavior - if widget is open, close it; if closed, open it
|
|
2473
2472
|
const state = this.stateCoordinator.getState();
|
|
2474
2473
|
if (state.widget.isOpen) {
|
|
@@ -2481,9 +2480,260 @@
|
|
|
2481
2480
|
}
|
|
2482
2481
|
}
|
|
2483
2482
|
if (event.data?.type === 'weld:close') {
|
|
2483
|
+
if (this.status !== SDKStatus.READY)
|
|
2484
|
+
return;
|
|
2484
2485
|
console.log('[Weld SDK] Widget close requested');
|
|
2485
2486
|
this.close();
|
|
2486
2487
|
}
|
|
2488
|
+
if (event.data?.type === 'weld:unread-count') {
|
|
2489
|
+
const count = event.data.count ?? 0;
|
|
2490
|
+
// Forward to launcher iframe
|
|
2491
|
+
const launcherIframe = this.iframeManager.getIframe(exports.IframeType.LAUNCHER);
|
|
2492
|
+
if (launcherIframe?.element?.contentWindow) {
|
|
2493
|
+
launcherIframe.element.contentWindow.postMessage({
|
|
2494
|
+
type: 'weld:unread-count',
|
|
2495
|
+
count
|
|
2496
|
+
}, '*');
|
|
2497
|
+
}
|
|
2498
|
+
// Update state coordinator for external API consumers
|
|
2499
|
+
this.stateCoordinator.setBadgeCount(count);
|
|
2500
|
+
}
|
|
2501
|
+
if (event.data?.type === 'weld:image:open' && event.data?.url) {
|
|
2502
|
+
this.showImageLightbox(event.data.url);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* Show fullscreen image lightbox on the parent page
|
|
2507
|
+
*/
|
|
2508
|
+
showImageLightbox(url) {
|
|
2509
|
+
// Remove existing lightbox if any
|
|
2510
|
+
const existing = document.getElementById('weld-image-lightbox');
|
|
2511
|
+
if (existing)
|
|
2512
|
+
existing.remove();
|
|
2513
|
+
// Zoom / pan state
|
|
2514
|
+
let scale = 1;
|
|
2515
|
+
let translateX = 0;
|
|
2516
|
+
let translateY = 0;
|
|
2517
|
+
let isDragging = false;
|
|
2518
|
+
let dragStartX = 0;
|
|
2519
|
+
let dragStartY = 0;
|
|
2520
|
+
let lastTranslateX = 0;
|
|
2521
|
+
let lastTranslateY = 0;
|
|
2522
|
+
const applyTransform = () => {
|
|
2523
|
+
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
|
|
2524
|
+
};
|
|
2525
|
+
const resetTransform = () => {
|
|
2526
|
+
scale = 1;
|
|
2527
|
+
translateX = 0;
|
|
2528
|
+
translateY = 0;
|
|
2529
|
+
applyTransform();
|
|
2530
|
+
img.style.cursor = 'zoom-in';
|
|
2531
|
+
};
|
|
2532
|
+
const overlay = document.createElement('div');
|
|
2533
|
+
overlay.id = 'weld-image-lightbox';
|
|
2534
|
+
overlay.style.cssText = `
|
|
2535
|
+
position: fixed;
|
|
2536
|
+
inset: 0;
|
|
2537
|
+
z-index: 2147483647;
|
|
2538
|
+
background: rgba(0, 0, 0, 0.92);
|
|
2539
|
+
display: flex;
|
|
2540
|
+
align-items: center;
|
|
2541
|
+
justify-content: center;
|
|
2542
|
+
padding: 16px;
|
|
2543
|
+
cursor: pointer;
|
|
2544
|
+
overflow: hidden;
|
|
2545
|
+
`;
|
|
2546
|
+
// Close button
|
|
2547
|
+
const closeBtn = document.createElement('button');
|
|
2548
|
+
closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
|
|
2549
|
+
closeBtn.style.cssText = `
|
|
2550
|
+
position: absolute;
|
|
2551
|
+
top: 16px;
|
|
2552
|
+
right: 16px;
|
|
2553
|
+
width: 40px;
|
|
2554
|
+
height: 40px;
|
|
2555
|
+
border-radius: 50%;
|
|
2556
|
+
border: none;
|
|
2557
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2558
|
+
color: white;
|
|
2559
|
+
cursor: pointer;
|
|
2560
|
+
display: flex;
|
|
2561
|
+
align-items: center;
|
|
2562
|
+
justify-content: center;
|
|
2563
|
+
transition: background 0.15s;
|
|
2564
|
+
`;
|
|
2565
|
+
closeBtn.onmouseenter = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
|
|
2566
|
+
closeBtn.onmouseleave = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
|
|
2567
|
+
// Download button
|
|
2568
|
+
const downloadBtn = document.createElement('a');
|
|
2569
|
+
downloadBtn.href = url;
|
|
2570
|
+
downloadBtn.download = '';
|
|
2571
|
+
downloadBtn.target = '_blank';
|
|
2572
|
+
downloadBtn.rel = 'noopener noreferrer';
|
|
2573
|
+
downloadBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;
|
|
2574
|
+
downloadBtn.style.cssText = `
|
|
2575
|
+
position: absolute;
|
|
2576
|
+
top: 16px;
|
|
2577
|
+
right: 64px;
|
|
2578
|
+
width: 40px;
|
|
2579
|
+
height: 40px;
|
|
2580
|
+
border-radius: 50%;
|
|
2581
|
+
border: none;
|
|
2582
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2583
|
+
color: white;
|
|
2584
|
+
cursor: pointer;
|
|
2585
|
+
display: flex;
|
|
2586
|
+
align-items: center;
|
|
2587
|
+
justify-content: center;
|
|
2588
|
+
transition: background 0.15s;
|
|
2589
|
+
text-decoration: none;
|
|
2590
|
+
`;
|
|
2591
|
+
downloadBtn.onmouseenter = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
|
|
2592
|
+
downloadBtn.onmouseleave = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
|
|
2593
|
+
// Image
|
|
2594
|
+
const img = document.createElement('img');
|
|
2595
|
+
img.src = url;
|
|
2596
|
+
img.alt = 'Full size';
|
|
2597
|
+
img.draggable = false;
|
|
2598
|
+
img.style.cssText = `
|
|
2599
|
+
max-width: 100%;
|
|
2600
|
+
max-height: 100%;
|
|
2601
|
+
object-fit: contain;
|
|
2602
|
+
border-radius: 8px;
|
|
2603
|
+
cursor: zoom-in;
|
|
2604
|
+
transition: transform 0.2s ease;
|
|
2605
|
+
user-select: none;
|
|
2606
|
+
`;
|
|
2607
|
+
// Click to toggle zoom
|
|
2608
|
+
img.addEventListener('click', (e) => {
|
|
2609
|
+
e.stopPropagation();
|
|
2610
|
+
if (scale === 1) {
|
|
2611
|
+
// Zoom in to 2.5x centered on click position
|
|
2612
|
+
const rect = img.getBoundingClientRect();
|
|
2613
|
+
const clickX = e.clientX - rect.left - rect.width / 2;
|
|
2614
|
+
const clickY = e.clientY - rect.top - rect.height / 2;
|
|
2615
|
+
scale = 2.5;
|
|
2616
|
+
translateX = -clickX * 1.5;
|
|
2617
|
+
translateY = -clickY * 1.5;
|
|
2618
|
+
applyTransform();
|
|
2619
|
+
img.style.cursor = 'zoom-out';
|
|
2620
|
+
}
|
|
2621
|
+
else {
|
|
2622
|
+
// Zoom out - reset
|
|
2623
|
+
resetTransform();
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
// Mouse wheel zoom
|
|
2627
|
+
overlay.addEventListener('wheel', (e) => {
|
|
2628
|
+
e.preventDefault();
|
|
2629
|
+
const delta = e.deltaY > 0 ? -0.25 : 0.25;
|
|
2630
|
+
const newScale = Math.min(Math.max(scale + delta, 1), 5);
|
|
2631
|
+
if (newScale === 1) {
|
|
2632
|
+
resetTransform();
|
|
2633
|
+
}
|
|
2634
|
+
else {
|
|
2635
|
+
scale = newScale;
|
|
2636
|
+
applyTransform();
|
|
2637
|
+
img.style.cursor = 'zoom-out';
|
|
2638
|
+
}
|
|
2639
|
+
}, { passive: false });
|
|
2640
|
+
// Drag to pan when zoomed
|
|
2641
|
+
img.addEventListener('mousedown', (e) => {
|
|
2642
|
+
if (scale <= 1)
|
|
2643
|
+
return;
|
|
2644
|
+
e.preventDefault();
|
|
2645
|
+
isDragging = true;
|
|
2646
|
+
dragStartX = e.clientX;
|
|
2647
|
+
dragStartY = e.clientY;
|
|
2648
|
+
lastTranslateX = translateX;
|
|
2649
|
+
lastTranslateY = translateY;
|
|
2650
|
+
img.style.cursor = 'grabbing';
|
|
2651
|
+
img.style.transition = 'none';
|
|
2652
|
+
});
|
|
2653
|
+
window.addEventListener('mousemove', (e) => {
|
|
2654
|
+
if (!isDragging)
|
|
2655
|
+
return;
|
|
2656
|
+
translateX = lastTranslateX + (e.clientX - dragStartX);
|
|
2657
|
+
translateY = lastTranslateY + (e.clientY - dragStartY);
|
|
2658
|
+
applyTransform();
|
|
2659
|
+
});
|
|
2660
|
+
window.addEventListener('mouseup', () => {
|
|
2661
|
+
if (!isDragging)
|
|
2662
|
+
return;
|
|
2663
|
+
isDragging = false;
|
|
2664
|
+
img.style.cursor = scale > 1 ? 'zoom-out' : 'zoom-in';
|
|
2665
|
+
img.style.transition = 'transform 0.2s ease';
|
|
2666
|
+
});
|
|
2667
|
+
// Touch: pinch to zoom + drag to pan
|
|
2668
|
+
let lastTouchDist = 0;
|
|
2669
|
+
let lastTouchScale = 1;
|
|
2670
|
+
overlay.addEventListener('touchstart', (e) => {
|
|
2671
|
+
if (e.touches.length === 2) {
|
|
2672
|
+
e.preventDefault();
|
|
2673
|
+
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
|
2674
|
+
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
|
2675
|
+
lastTouchDist = Math.hypot(dx, dy);
|
|
2676
|
+
lastTouchScale = scale;
|
|
2677
|
+
}
|
|
2678
|
+
else if (e.touches.length === 1 && scale > 1) {
|
|
2679
|
+
isDragging = true;
|
|
2680
|
+
dragStartX = e.touches[0].clientX;
|
|
2681
|
+
dragStartY = e.touches[0].clientY;
|
|
2682
|
+
lastTranslateX = translateX;
|
|
2683
|
+
lastTranslateY = translateY;
|
|
2684
|
+
img.style.transition = 'none';
|
|
2685
|
+
}
|
|
2686
|
+
}, { passive: false });
|
|
2687
|
+
overlay.addEventListener('touchmove', (e) => {
|
|
2688
|
+
if (e.touches.length === 2) {
|
|
2689
|
+
e.preventDefault();
|
|
2690
|
+
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
|
2691
|
+
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
|
2692
|
+
const dist = Math.hypot(dx, dy);
|
|
2693
|
+
scale = Math.min(Math.max(lastTouchScale * (dist / lastTouchDist), 1), 5);
|
|
2694
|
+
if (scale === 1) {
|
|
2695
|
+
translateX = 0;
|
|
2696
|
+
translateY = 0;
|
|
2697
|
+
}
|
|
2698
|
+
applyTransform();
|
|
2699
|
+
}
|
|
2700
|
+
else if (e.touches.length === 1 && isDragging) {
|
|
2701
|
+
e.preventDefault();
|
|
2702
|
+
translateX = lastTranslateX + (e.touches[0].clientX - dragStartX);
|
|
2703
|
+
translateY = lastTranslateY + (e.touches[0].clientY - dragStartY);
|
|
2704
|
+
applyTransform();
|
|
2705
|
+
}
|
|
2706
|
+
}, { passive: false });
|
|
2707
|
+
overlay.addEventListener('touchend', (e) => {
|
|
2708
|
+
if (e.touches.length < 2) {
|
|
2709
|
+
lastTouchDist = 0;
|
|
2710
|
+
}
|
|
2711
|
+
if (e.touches.length === 0) {
|
|
2712
|
+
isDragging = false;
|
|
2713
|
+
img.style.transition = 'transform 0.2s ease';
|
|
2714
|
+
}
|
|
2715
|
+
});
|
|
2716
|
+
const close = () => {
|
|
2717
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
2718
|
+
overlay.remove();
|
|
2719
|
+
};
|
|
2720
|
+
// Only close on backdrop click when not zoomed (prevent accidental close while panning)
|
|
2721
|
+
overlay.addEventListener('click', (e) => {
|
|
2722
|
+
if (e.target === overlay && scale <= 1)
|
|
2723
|
+
close();
|
|
2724
|
+
});
|
|
2725
|
+
downloadBtn.addEventListener('click', (e) => e.stopPropagation());
|
|
2726
|
+
closeBtn.addEventListener('click', (e) => { e.stopPropagation(); close(); });
|
|
2727
|
+
// Close on Escape
|
|
2728
|
+
const handleKeyDown = (e) => {
|
|
2729
|
+
if (e.key === 'Escape')
|
|
2730
|
+
close();
|
|
2731
|
+
};
|
|
2732
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
2733
|
+
overlay.appendChild(closeBtn);
|
|
2734
|
+
overlay.appendChild(downloadBtn);
|
|
2735
|
+
overlay.appendChild(img);
|
|
2736
|
+
document.body.appendChild(overlay);
|
|
2487
2737
|
}
|
|
2488
2738
|
/**
|
|
2489
2739
|
* Initialize the SDK and render widget
|
|
@@ -2509,6 +2759,13 @@
|
|
|
2509
2759
|
this.logger.info('WeldSDK ready');
|
|
2510
2760
|
// Call onReady callback
|
|
2511
2761
|
this.config.onReady?.();
|
|
2762
|
+
// Start tracking page URL changes
|
|
2763
|
+
this.startPageTracking();
|
|
2764
|
+
// Auto-open if widget was previously open (persisted in sessionStorage)
|
|
2765
|
+
if (this.wasOpen()) {
|
|
2766
|
+
this.logger.info('Restoring previously open widget from sessionStorage');
|
|
2767
|
+
this.open();
|
|
2768
|
+
}
|
|
2512
2769
|
}
|
|
2513
2770
|
catch (error) {
|
|
2514
2771
|
this.status = SDKStatus.ERROR;
|
|
@@ -2587,6 +2844,58 @@
|
|
|
2587
2844
|
isReady() {
|
|
2588
2845
|
return this.status === SDKStatus.READY;
|
|
2589
2846
|
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Update callbacks on an existing instance (used by singleton reuse)
|
|
2849
|
+
*/
|
|
2850
|
+
updateCallbacks(config) {
|
|
2851
|
+
if (config.onReady !== undefined)
|
|
2852
|
+
this.config.onReady = config.onReady;
|
|
2853
|
+
if (config.onOpen !== undefined)
|
|
2854
|
+
this.config.onOpen = config.onOpen;
|
|
2855
|
+
if (config.onClose !== undefined)
|
|
2856
|
+
this.config.onClose = config.onClose;
|
|
2857
|
+
if (config.onError !== undefined)
|
|
2858
|
+
this.config.onError = config.onError;
|
|
2859
|
+
if (config.onDestroy !== undefined)
|
|
2860
|
+
this.config.onDestroy = config.onDestroy;
|
|
2861
|
+
if (config.onMinimize !== undefined)
|
|
2862
|
+
this.config.onMinimize = config.onMinimize;
|
|
2863
|
+
if (config.onMaximize !== undefined)
|
|
2864
|
+
this.config.onMaximize = config.onMaximize;
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Persist open/closed state to sessionStorage
|
|
2868
|
+
*/
|
|
2869
|
+
persistOpenState(isOpen) {
|
|
2870
|
+
try {
|
|
2871
|
+
sessionStorage.setItem(openStateKey(this.config.widgetId), isOpen ? 'true' : 'false');
|
|
2872
|
+
}
|
|
2873
|
+
catch {
|
|
2874
|
+
// sessionStorage might not be available
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Clear persisted state from sessionStorage
|
|
2879
|
+
*/
|
|
2880
|
+
clearPersistedState() {
|
|
2881
|
+
try {
|
|
2882
|
+
sessionStorage.removeItem(openStateKey(this.config.widgetId));
|
|
2883
|
+
}
|
|
2884
|
+
catch {
|
|
2885
|
+
// sessionStorage might not be available
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
/**
|
|
2889
|
+
* Check if widget was previously open (from sessionStorage)
|
|
2890
|
+
*/
|
|
2891
|
+
wasOpen() {
|
|
2892
|
+
try {
|
|
2893
|
+
return sessionStorage.getItem(openStateKey(this.config.widgetId)) === 'true';
|
|
2894
|
+
}
|
|
2895
|
+
catch {
|
|
2896
|
+
return false;
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2590
2899
|
/**
|
|
2591
2900
|
* Open the widget
|
|
2592
2901
|
*/
|
|
@@ -2595,8 +2904,6 @@
|
|
|
2595
2904
|
console.log('[Weld SDK] Opening widget...');
|
|
2596
2905
|
this.stateCoordinator.openWidget();
|
|
2597
2906
|
this.iframeManager.showIframe(exports.IframeType.WIDGET);
|
|
2598
|
-
this.iframeManager.showIframe(exports.IframeType.BACKDROP);
|
|
2599
|
-
// Keep launcher visible so user can click it to close the widget
|
|
2600
2907
|
// Send open message to the widget iframe
|
|
2601
2908
|
const widgetIframe = this.iframeManager.getIframe(exports.IframeType.WIDGET);
|
|
2602
2909
|
if (widgetIframe?.element?.contentWindow) {
|
|
@@ -2607,6 +2914,7 @@
|
|
|
2607
2914
|
if (launcherIframe?.element?.contentWindow) {
|
|
2608
2915
|
launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-opened' }, '*');
|
|
2609
2916
|
}
|
|
2917
|
+
this.persistOpenState(true);
|
|
2610
2918
|
this.config.onOpen?.();
|
|
2611
2919
|
}
|
|
2612
2920
|
/**
|
|
@@ -2617,8 +2925,6 @@
|
|
|
2617
2925
|
console.log('[Weld SDK] Closing widget...');
|
|
2618
2926
|
this.stateCoordinator.closeWidget();
|
|
2619
2927
|
this.iframeManager.hideIframe(exports.IframeType.WIDGET);
|
|
2620
|
-
this.iframeManager.hideIframe(exports.IframeType.BACKDROP);
|
|
2621
|
-
// Launcher stays visible
|
|
2622
2928
|
// Send close message to the widget iframe
|
|
2623
2929
|
const widgetIframe = this.iframeManager.getIframe(exports.IframeType.WIDGET);
|
|
2624
2930
|
if (widgetIframe?.element?.contentWindow) {
|
|
@@ -2629,6 +2935,7 @@
|
|
|
2629
2935
|
if (launcherIframe?.element?.contentWindow) {
|
|
2630
2936
|
launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-closed' }, '*');
|
|
2631
2937
|
}
|
|
2938
|
+
this.persistOpenState(false);
|
|
2632
2939
|
this.config.onClose?.();
|
|
2633
2940
|
}
|
|
2634
2941
|
/**
|
|
@@ -2817,6 +3124,8 @@
|
|
|
2817
3124
|
});
|
|
2818
3125
|
// Broadcast logout to iframes
|
|
2819
3126
|
this.messageBroker.broadcast('weld:auth:logout', {});
|
|
3127
|
+
// Clear persisted widget state
|
|
3128
|
+
this.clearPersistedState();
|
|
2820
3129
|
// Clear any stored session data
|
|
2821
3130
|
try {
|
|
2822
3131
|
const prefix = 'weld-';
|
|
@@ -2958,6 +3267,63 @@
|
|
|
2958
3267
|
this.logger.setLevel('warn');
|
|
2959
3268
|
this.logger.info('Debug mode disabled');
|
|
2960
3269
|
}
|
|
3270
|
+
/**
|
|
3271
|
+
* Send a page change message to the widget iframe
|
|
3272
|
+
*/
|
|
3273
|
+
sendPageChange(url, title) {
|
|
3274
|
+
const widgetIframe = this.iframeManager.getIframe(exports.IframeType.WIDGET);
|
|
3275
|
+
if (widgetIframe?.element?.contentWindow) {
|
|
3276
|
+
widgetIframe.element.contentWindow.postMessage({
|
|
3277
|
+
type: 'weld:page:change',
|
|
3278
|
+
url,
|
|
3279
|
+
title,
|
|
3280
|
+
timestamp: Date.now(),
|
|
3281
|
+
}, '*');
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Start tracking page URL changes (SPA navigations + popstate)
|
|
3286
|
+
*/
|
|
3287
|
+
startPageTracking() {
|
|
3288
|
+
let lastUrl = window.location.href;
|
|
3289
|
+
let debounceTimer = null;
|
|
3290
|
+
const notifyChange = () => {
|
|
3291
|
+
const currentUrl = window.location.href;
|
|
3292
|
+
if (currentUrl !== lastUrl) {
|
|
3293
|
+
lastUrl = currentUrl;
|
|
3294
|
+
this.sendPageChange(currentUrl, document.title);
|
|
3295
|
+
}
|
|
3296
|
+
};
|
|
3297
|
+
const debouncedNotify = () => {
|
|
3298
|
+
if (debounceTimer)
|
|
3299
|
+
clearTimeout(debounceTimer);
|
|
3300
|
+
debounceTimer = setTimeout(notifyChange, 300);
|
|
3301
|
+
};
|
|
3302
|
+
// Send initial page
|
|
3303
|
+
this.sendPageChange(window.location.href, document.title);
|
|
3304
|
+
// Monkey-patch history.pushState and history.replaceState
|
|
3305
|
+
const origPushState = history.pushState.bind(history);
|
|
3306
|
+
const origReplaceState = history.replaceState.bind(history);
|
|
3307
|
+
history.pushState = function (...args) {
|
|
3308
|
+
origPushState(...args);
|
|
3309
|
+
debouncedNotify();
|
|
3310
|
+
};
|
|
3311
|
+
history.replaceState = function (...args) {
|
|
3312
|
+
origReplaceState(...args);
|
|
3313
|
+
debouncedNotify();
|
|
3314
|
+
};
|
|
3315
|
+
// Listen for popstate (browser back/forward)
|
|
3316
|
+
const handlePopstate = () => debouncedNotify();
|
|
3317
|
+
window.addEventListener('popstate', handlePopstate);
|
|
3318
|
+
// Store cleanup
|
|
3319
|
+
this.pageTrackingCleanup = () => {
|
|
3320
|
+
if (debounceTimer)
|
|
3321
|
+
clearTimeout(debounceTimer);
|
|
3322
|
+
window.removeEventListener('popstate', handlePopstate);
|
|
3323
|
+
history.pushState = origPushState;
|
|
3324
|
+
history.replaceState = origReplaceState;
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
2961
3327
|
/**
|
|
2962
3328
|
* Ensure SDK is ready before operation
|
|
2963
3329
|
*/
|
|
@@ -2966,11 +3332,26 @@
|
|
|
2966
3332
|
throw new Error('SDK not ready. Call init() first.');
|
|
2967
3333
|
}
|
|
2968
3334
|
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Detach from the current component lifecycle without destroying the widget.
|
|
3337
|
+
* Use this as a React useEffect cleanup — the widget stays alive across navigations.
|
|
3338
|
+
*/
|
|
3339
|
+
detach() {
|
|
3340
|
+
// No-op: widget stays alive in the singleton registry
|
|
3341
|
+
this.logger.debug('WeldSDK detached (no-op, widget stays alive)');
|
|
3342
|
+
}
|
|
2969
3343
|
/**
|
|
2970
3344
|
* Destroy SDK and cleanup
|
|
2971
3345
|
*/
|
|
2972
3346
|
destroy() {
|
|
2973
3347
|
this.logger.info('Destroying WeldSDK');
|
|
3348
|
+
// Remove from singleton registry
|
|
3349
|
+
sdkRegistry.delete(this.config.widgetId);
|
|
3350
|
+
// Clear persisted state
|
|
3351
|
+
this.clearPersistedState();
|
|
3352
|
+
// Stop page tracking
|
|
3353
|
+
this.pageTrackingCleanup?.();
|
|
3354
|
+
this.pageTrackingCleanup = null;
|
|
2974
3355
|
// Remove event listener using bound handler
|
|
2975
3356
|
window.removeEventListener('message', this.boundHandleLauncherClick);
|
|
2976
3357
|
// Unsubscribe from all message broker subscriptions
|
|
@@ -2990,13 +3371,33 @@
|
|
|
2990
3371
|
}
|
|
2991
3372
|
}
|
|
2992
3373
|
/**
|
|
2993
|
-
* Create and initialize WeldSDK instance
|
|
3374
|
+
* Create and initialize WeldSDK instance.
|
|
3375
|
+
* Uses singleton pattern — if an instance for the same widgetId already exists
|
|
3376
|
+
* and is not destroyed, updates callbacks and returns the existing instance.
|
|
2994
3377
|
*/
|
|
2995
3378
|
async function createWeldSDK(config) {
|
|
3379
|
+
const widgetId = config.widgetId;
|
|
3380
|
+
// Check for existing, non-destroyed instance
|
|
3381
|
+
const existing = sdkRegistry.get(widgetId);
|
|
3382
|
+
if (existing && existing.getStatus() !== 'destroyed') {
|
|
3383
|
+
existing.updateCallbacks(config);
|
|
3384
|
+
return existing;
|
|
3385
|
+
}
|
|
2996
3386
|
const sdk = new WeldSDK(config);
|
|
3387
|
+
sdkRegistry.set(widgetId, sdk);
|
|
2997
3388
|
await sdk.init();
|
|
2998
3389
|
return sdk;
|
|
2999
3390
|
}
|
|
3391
|
+
/**
|
|
3392
|
+
* Explicitly destroy a WeldSDK instance by widgetId.
|
|
3393
|
+
* Use this for logout or when you need to fully remove the widget.
|
|
3394
|
+
*/
|
|
3395
|
+
function destroyWeldSDK(widgetId) {
|
|
3396
|
+
const sdk = sdkRegistry.get(widgetId);
|
|
3397
|
+
if (sdk) {
|
|
3398
|
+
sdk.destroy();
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3000
3401
|
|
|
3001
3402
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
3002
3403
|
exports.HelpdeskWidget = WeldSDK;
|
|
@@ -3015,6 +3416,8 @@
|
|
|
3015
3416
|
exports.deepMerge = deepMerge;
|
|
3016
3417
|
exports.default = WeldSDK;
|
|
3017
3418
|
exports.defaultLogger = defaultLogger;
|
|
3419
|
+
exports.destroyHelpdeskWidget = destroyWeldSDK;
|
|
3420
|
+
exports.destroyWeldSDK = destroyWeldSDK;
|
|
3018
3421
|
exports.formatFileSize = formatFileSize;
|
|
3019
3422
|
exports.getStateValue = getStateValue;
|
|
3020
3423
|
exports.hasRequiredProperties = hasRequiredProperties;
|