@weldsuite/helpdesk-widget-sdk 1.0.15 → 1.0.16

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.js CHANGED
@@ -572,14 +572,16 @@ class IframeManager {
572
572
  const container = document.createElement('div');
573
573
  container.className = 'weld-launcher-frame';
574
574
  container.setAttribute('data-state', 'visible');
575
+ // Container is larger than the button to allow hover animations (scale, shadow) without clipping
576
+ const launcherPadding = 10;
575
577
  container.style.cssText = `
576
578
  position: fixed;
577
- bottom: ${launcher.position.bottom};
578
- right: ${launcher.position.right};
579
- width: ${launcher.size};
580
- height: ${launcher.size};
579
+ bottom: calc(${launcher.position.bottom} - ${launcherPadding}px);
580
+ right: calc(${launcher.position.right} - ${launcherPadding}px);
581
+ width: calc(${launcher.size} + ${launcherPadding * 2}px);
582
+ height: calc(${launcher.size} + ${launcherPadding * 2}px);
581
583
  z-index: 2147483003;
582
- pointer-events: auto;
584
+ pointer-events: none;
583
585
  display: block;
584
586
  `;
585
587
  // Create iframe
@@ -593,6 +595,7 @@ class IframeManager {
593
595
  border: none;
594
596
  background: transparent;
595
597
  display: block;
598
+ pointer-events: auto;
596
599
  `;
597
600
  iframe.setAttribute('allow', 'clipboard-write');
598
601
  iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
@@ -713,43 +716,10 @@ class IframeManager {
713
716
  this.logger.debug('Widget iframe created');
714
717
  }
715
718
  /**
716
- * Create backdrop iframe
719
+ * Create backdrop iframe — disabled, widget stays non-modal so users can interact with the page
717
720
  */
718
721
  async createBackdropIframe() {
719
- if (!this.config.iframes.backdrop?.enabled) {
720
- this.logger.debug('Backdrop disabled, skipping creation');
721
- return;
722
- }
723
- // Create container
724
- const container = document.createElement('div');
725
- container.className = 'weld-backdrop-frame';
726
- container.setAttribute('data-state', 'hidden');
727
- container.style.cssText = `
728
- position: fixed;
729
- top: 0;
730
- left: 0;
731
- right: 0;
732
- bottom: 0;
733
- z-index: 2147483000;
734
- background: transparent;
735
- pointer-events: none;
736
- opacity: 0;
737
- transition: opacity 200ms ease;
738
- `;
739
- this.appContainer?.appendChild(container);
740
- // Store metadata (backdrop doesn't have an iframe, just a div)
741
- // We'll create a minimal "iframe" reference for consistency
742
- const dummyIframe = document.createElement('iframe');
743
- dummyIframe.style.display = 'none';
744
- this.iframes.set(IframeType.BACKDROP, {
745
- type: IframeType.BACKDROP,
746
- element: dummyIframe,
747
- container,
748
- ready: true, // Backdrop is always ready
749
- visible: false,
750
- createdAt: Date.now(),
751
- });
752
- this.logger.debug('Backdrop created');
722
+ this.logger.debug('Backdrop disabled, skipping creation');
753
723
  }
754
724
  /**
755
725
  * Build iframe URL with parameters
@@ -2250,7 +2220,7 @@ class StateCoordinator {
2250
2220
  }
2251
2221
  }
2252
2222
 
2253
- var version = "1.0.15";
2223
+ var version = "1.0.16";
2254
2224
  var packageJson = {
2255
2225
  version: version};
2256
2226
 
@@ -2333,6 +2303,242 @@ class WeldSDK {
2333
2303
  console.log('[Weld SDK] Widget close requested');
2334
2304
  this.close();
2335
2305
  }
2306
+ if (event.data?.type === 'weld:image:open' && event.data?.url) {
2307
+ this.showImageLightbox(event.data.url);
2308
+ }
2309
+ }
2310
+ /**
2311
+ * Show fullscreen image lightbox on the parent page
2312
+ */
2313
+ showImageLightbox(url) {
2314
+ // Remove existing lightbox if any
2315
+ const existing = document.getElementById('weld-image-lightbox');
2316
+ if (existing)
2317
+ existing.remove();
2318
+ // Zoom / pan state
2319
+ let scale = 1;
2320
+ let translateX = 0;
2321
+ let translateY = 0;
2322
+ let isDragging = false;
2323
+ let dragStartX = 0;
2324
+ let dragStartY = 0;
2325
+ let lastTranslateX = 0;
2326
+ let lastTranslateY = 0;
2327
+ const applyTransform = () => {
2328
+ img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
2329
+ };
2330
+ const resetTransform = () => {
2331
+ scale = 1;
2332
+ translateX = 0;
2333
+ translateY = 0;
2334
+ applyTransform();
2335
+ img.style.cursor = 'zoom-in';
2336
+ };
2337
+ const overlay = document.createElement('div');
2338
+ overlay.id = 'weld-image-lightbox';
2339
+ overlay.style.cssText = `
2340
+ position: fixed;
2341
+ inset: 0;
2342
+ z-index: 2147483647;
2343
+ background: rgba(0, 0, 0, 0.92);
2344
+ display: flex;
2345
+ align-items: center;
2346
+ justify-content: center;
2347
+ padding: 16px;
2348
+ cursor: pointer;
2349
+ overflow: hidden;
2350
+ `;
2351
+ // Close button
2352
+ const closeBtn = document.createElement('button');
2353
+ 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>`;
2354
+ closeBtn.style.cssText = `
2355
+ position: absolute;
2356
+ top: 16px;
2357
+ right: 16px;
2358
+ width: 40px;
2359
+ height: 40px;
2360
+ border-radius: 50%;
2361
+ border: none;
2362
+ background: rgba(255, 255, 255, 0.1);
2363
+ color: white;
2364
+ cursor: pointer;
2365
+ display: flex;
2366
+ align-items: center;
2367
+ justify-content: center;
2368
+ transition: background 0.15s;
2369
+ `;
2370
+ closeBtn.onmouseenter = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
2371
+ closeBtn.onmouseleave = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
2372
+ // Download button
2373
+ const downloadBtn = document.createElement('a');
2374
+ downloadBtn.href = url;
2375
+ downloadBtn.download = '';
2376
+ downloadBtn.target = '_blank';
2377
+ downloadBtn.rel = 'noopener noreferrer';
2378
+ 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>`;
2379
+ downloadBtn.style.cssText = `
2380
+ position: absolute;
2381
+ top: 16px;
2382
+ right: 64px;
2383
+ width: 40px;
2384
+ height: 40px;
2385
+ border-radius: 50%;
2386
+ border: none;
2387
+ background: rgba(255, 255, 255, 0.1);
2388
+ color: white;
2389
+ cursor: pointer;
2390
+ display: flex;
2391
+ align-items: center;
2392
+ justify-content: center;
2393
+ transition: background 0.15s;
2394
+ text-decoration: none;
2395
+ `;
2396
+ downloadBtn.onmouseenter = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
2397
+ downloadBtn.onmouseleave = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
2398
+ // Image
2399
+ const img = document.createElement('img');
2400
+ img.src = url;
2401
+ img.alt = 'Full size';
2402
+ img.draggable = false;
2403
+ img.style.cssText = `
2404
+ max-width: 100%;
2405
+ max-height: 100%;
2406
+ object-fit: contain;
2407
+ border-radius: 8px;
2408
+ cursor: zoom-in;
2409
+ transition: transform 0.2s ease;
2410
+ user-select: none;
2411
+ `;
2412
+ // Click to toggle zoom
2413
+ img.addEventListener('click', (e) => {
2414
+ e.stopPropagation();
2415
+ if (scale === 1) {
2416
+ // Zoom in to 2.5x centered on click position
2417
+ const rect = img.getBoundingClientRect();
2418
+ const clickX = e.clientX - rect.left - rect.width / 2;
2419
+ const clickY = e.clientY - rect.top - rect.height / 2;
2420
+ scale = 2.5;
2421
+ translateX = -clickX * 1.5;
2422
+ translateY = -clickY * 1.5;
2423
+ applyTransform();
2424
+ img.style.cursor = 'zoom-out';
2425
+ }
2426
+ else {
2427
+ // Zoom out - reset
2428
+ resetTransform();
2429
+ }
2430
+ });
2431
+ // Mouse wheel zoom
2432
+ overlay.addEventListener('wheel', (e) => {
2433
+ e.preventDefault();
2434
+ const delta = e.deltaY > 0 ? -0.25 : 0.25;
2435
+ const newScale = Math.min(Math.max(scale + delta, 1), 5);
2436
+ if (newScale === 1) {
2437
+ resetTransform();
2438
+ }
2439
+ else {
2440
+ scale = newScale;
2441
+ applyTransform();
2442
+ img.style.cursor = 'zoom-out';
2443
+ }
2444
+ }, { passive: false });
2445
+ // Drag to pan when zoomed
2446
+ img.addEventListener('mousedown', (e) => {
2447
+ if (scale <= 1)
2448
+ return;
2449
+ e.preventDefault();
2450
+ isDragging = true;
2451
+ dragStartX = e.clientX;
2452
+ dragStartY = e.clientY;
2453
+ lastTranslateX = translateX;
2454
+ lastTranslateY = translateY;
2455
+ img.style.cursor = 'grabbing';
2456
+ img.style.transition = 'none';
2457
+ });
2458
+ window.addEventListener('mousemove', (e) => {
2459
+ if (!isDragging)
2460
+ return;
2461
+ translateX = lastTranslateX + (e.clientX - dragStartX);
2462
+ translateY = lastTranslateY + (e.clientY - dragStartY);
2463
+ applyTransform();
2464
+ });
2465
+ window.addEventListener('mouseup', () => {
2466
+ if (!isDragging)
2467
+ return;
2468
+ isDragging = false;
2469
+ img.style.cursor = scale > 1 ? 'zoom-out' : 'zoom-in';
2470
+ img.style.transition = 'transform 0.2s ease';
2471
+ });
2472
+ // Touch: pinch to zoom + drag to pan
2473
+ let lastTouchDist = 0;
2474
+ let lastTouchScale = 1;
2475
+ overlay.addEventListener('touchstart', (e) => {
2476
+ if (e.touches.length === 2) {
2477
+ e.preventDefault();
2478
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
2479
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
2480
+ lastTouchDist = Math.hypot(dx, dy);
2481
+ lastTouchScale = scale;
2482
+ }
2483
+ else if (e.touches.length === 1 && scale > 1) {
2484
+ isDragging = true;
2485
+ dragStartX = e.touches[0].clientX;
2486
+ dragStartY = e.touches[0].clientY;
2487
+ lastTranslateX = translateX;
2488
+ lastTranslateY = translateY;
2489
+ img.style.transition = 'none';
2490
+ }
2491
+ }, { passive: false });
2492
+ overlay.addEventListener('touchmove', (e) => {
2493
+ if (e.touches.length === 2) {
2494
+ e.preventDefault();
2495
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
2496
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
2497
+ const dist = Math.hypot(dx, dy);
2498
+ scale = Math.min(Math.max(lastTouchScale * (dist / lastTouchDist), 1), 5);
2499
+ if (scale === 1) {
2500
+ translateX = 0;
2501
+ translateY = 0;
2502
+ }
2503
+ applyTransform();
2504
+ }
2505
+ else if (e.touches.length === 1 && isDragging) {
2506
+ e.preventDefault();
2507
+ translateX = lastTranslateX + (e.touches[0].clientX - dragStartX);
2508
+ translateY = lastTranslateY + (e.touches[0].clientY - dragStartY);
2509
+ applyTransform();
2510
+ }
2511
+ }, { passive: false });
2512
+ overlay.addEventListener('touchend', (e) => {
2513
+ if (e.touches.length < 2) {
2514
+ lastTouchDist = 0;
2515
+ }
2516
+ if (e.touches.length === 0) {
2517
+ isDragging = false;
2518
+ img.style.transition = 'transform 0.2s ease';
2519
+ }
2520
+ });
2521
+ const close = () => {
2522
+ document.removeEventListener('keydown', handleKeyDown);
2523
+ overlay.remove();
2524
+ };
2525
+ // Only close on backdrop click when not zoomed (prevent accidental close while panning)
2526
+ overlay.addEventListener('click', (e) => {
2527
+ if (e.target === overlay && scale <= 1)
2528
+ close();
2529
+ });
2530
+ downloadBtn.addEventListener('click', (e) => e.stopPropagation());
2531
+ closeBtn.addEventListener('click', (e) => { e.stopPropagation(); close(); });
2532
+ // Close on Escape
2533
+ const handleKeyDown = (e) => {
2534
+ if (e.key === 'Escape')
2535
+ close();
2536
+ };
2537
+ document.addEventListener('keydown', handleKeyDown);
2538
+ overlay.appendChild(closeBtn);
2539
+ overlay.appendChild(downloadBtn);
2540
+ overlay.appendChild(img);
2541
+ document.body.appendChild(overlay);
2336
2542
  }
2337
2543
  /**
2338
2544
  * Initialize the SDK and render widget
@@ -2444,8 +2650,6 @@ class WeldSDK {
2444
2650
  console.log('[Weld SDK] Opening widget...');
2445
2651
  this.stateCoordinator.openWidget();
2446
2652
  this.iframeManager.showIframe(IframeType.WIDGET);
2447
- this.iframeManager.showIframe(IframeType.BACKDROP);
2448
- // Keep launcher visible so user can click it to close the widget
2449
2653
  // Send open message to the widget iframe
2450
2654
  const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2451
2655
  if (widgetIframe?.element?.contentWindow) {
@@ -2466,8 +2670,6 @@ class WeldSDK {
2466
2670
  console.log('[Weld SDK] Closing widget...');
2467
2671
  this.stateCoordinator.closeWidget();
2468
2672
  this.iframeManager.hideIframe(IframeType.WIDGET);
2469
- this.iframeManager.hideIframe(IframeType.BACKDROP);
2470
- // Launcher stays visible
2471
2673
  // Send close message to the widget iframe
2472
2674
  const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2473
2675
  if (widgetIframe?.element?.contentWindow) {