@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/react.js CHANGED
@@ -514,14 +514,16 @@ class IframeManager {
514
514
  const container = document.createElement('div');
515
515
  container.className = 'weld-launcher-frame';
516
516
  container.setAttribute('data-state', 'visible');
517
+ // Container is larger than the button to allow hover animations (scale, shadow) without clipping
518
+ const launcherPadding = 10;
517
519
  container.style.cssText = `
518
520
  position: fixed;
519
- bottom: ${launcher.position.bottom};
520
- right: ${launcher.position.right};
521
- width: ${launcher.size};
522
- height: ${launcher.size};
521
+ bottom: calc(${launcher.position.bottom} - ${launcherPadding}px);
522
+ right: calc(${launcher.position.right} - ${launcherPadding}px);
523
+ width: calc(${launcher.size} + ${launcherPadding * 2}px);
524
+ height: calc(${launcher.size} + ${launcherPadding * 2}px);
523
525
  z-index: 2147483003;
524
- pointer-events: auto;
526
+ pointer-events: none;
525
527
  display: block;
526
528
  `;
527
529
  // Create iframe
@@ -535,6 +537,7 @@ class IframeManager {
535
537
  border: none;
536
538
  background: transparent;
537
539
  display: block;
540
+ pointer-events: auto;
538
541
  `;
539
542
  iframe.setAttribute('allow', 'clipboard-write');
540
543
  iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
@@ -655,43 +658,10 @@ class IframeManager {
655
658
  this.logger.debug('Widget iframe created');
656
659
  }
657
660
  /**
658
- * Create backdrop iframe
661
+ * Create backdrop iframe — disabled, widget stays non-modal so users can interact with the page
659
662
  */
660
663
  async createBackdropIframe() {
661
- if (!this.config.iframes.backdrop?.enabled) {
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(IframeType.BACKDROP, {
687
- type: 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');
664
+ this.logger.debug('Backdrop disabled, skipping creation');
695
665
  }
696
666
  /**
697
667
  * Build iframe URL with parameters
@@ -2192,7 +2162,7 @@ class StateCoordinator {
2192
2162
  }
2193
2163
  }
2194
2164
 
2195
- var version = "1.0.15";
2165
+ var version = "1.0.16";
2196
2166
  var packageJson = {
2197
2167
  version: version};
2198
2168
 
@@ -2275,6 +2245,242 @@ class WeldSDK {
2275
2245
  console.log('[Weld SDK] Widget close requested');
2276
2246
  this.close();
2277
2247
  }
2248
+ if (event.data?.type === 'weld:image:open' && event.data?.url) {
2249
+ this.showImageLightbox(event.data.url);
2250
+ }
2251
+ }
2252
+ /**
2253
+ * Show fullscreen image lightbox on the parent page
2254
+ */
2255
+ showImageLightbox(url) {
2256
+ // Remove existing lightbox if any
2257
+ const existing = document.getElementById('weld-image-lightbox');
2258
+ if (existing)
2259
+ existing.remove();
2260
+ // Zoom / pan state
2261
+ let scale = 1;
2262
+ let translateX = 0;
2263
+ let translateY = 0;
2264
+ let isDragging = false;
2265
+ let dragStartX = 0;
2266
+ let dragStartY = 0;
2267
+ let lastTranslateX = 0;
2268
+ let lastTranslateY = 0;
2269
+ const applyTransform = () => {
2270
+ img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
2271
+ };
2272
+ const resetTransform = () => {
2273
+ scale = 1;
2274
+ translateX = 0;
2275
+ translateY = 0;
2276
+ applyTransform();
2277
+ img.style.cursor = 'zoom-in';
2278
+ };
2279
+ const overlay = document.createElement('div');
2280
+ overlay.id = 'weld-image-lightbox';
2281
+ overlay.style.cssText = `
2282
+ position: fixed;
2283
+ inset: 0;
2284
+ z-index: 2147483647;
2285
+ background: rgba(0, 0, 0, 0.92);
2286
+ display: flex;
2287
+ align-items: center;
2288
+ justify-content: center;
2289
+ padding: 16px;
2290
+ cursor: pointer;
2291
+ overflow: hidden;
2292
+ `;
2293
+ // Close button
2294
+ const closeBtn = document.createElement('button');
2295
+ 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>`;
2296
+ closeBtn.style.cssText = `
2297
+ position: absolute;
2298
+ top: 16px;
2299
+ right: 16px;
2300
+ width: 40px;
2301
+ height: 40px;
2302
+ border-radius: 50%;
2303
+ border: none;
2304
+ background: rgba(255, 255, 255, 0.1);
2305
+ color: white;
2306
+ cursor: pointer;
2307
+ display: flex;
2308
+ align-items: center;
2309
+ justify-content: center;
2310
+ transition: background 0.15s;
2311
+ `;
2312
+ closeBtn.onmouseenter = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
2313
+ closeBtn.onmouseleave = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
2314
+ // Download button
2315
+ const downloadBtn = document.createElement('a');
2316
+ downloadBtn.href = url;
2317
+ downloadBtn.download = '';
2318
+ downloadBtn.target = '_blank';
2319
+ downloadBtn.rel = 'noopener noreferrer';
2320
+ 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>`;
2321
+ downloadBtn.style.cssText = `
2322
+ position: absolute;
2323
+ top: 16px;
2324
+ right: 64px;
2325
+ width: 40px;
2326
+ height: 40px;
2327
+ border-radius: 50%;
2328
+ border: none;
2329
+ background: rgba(255, 255, 255, 0.1);
2330
+ color: white;
2331
+ cursor: pointer;
2332
+ display: flex;
2333
+ align-items: center;
2334
+ justify-content: center;
2335
+ transition: background 0.15s;
2336
+ text-decoration: none;
2337
+ `;
2338
+ downloadBtn.onmouseenter = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.2)'; };
2339
+ downloadBtn.onmouseleave = () => { downloadBtn.style.background = 'rgba(255, 255, 255, 0.1)'; };
2340
+ // Image
2341
+ const img = document.createElement('img');
2342
+ img.src = url;
2343
+ img.alt = 'Full size';
2344
+ img.draggable = false;
2345
+ img.style.cssText = `
2346
+ max-width: 100%;
2347
+ max-height: 100%;
2348
+ object-fit: contain;
2349
+ border-radius: 8px;
2350
+ cursor: zoom-in;
2351
+ transition: transform 0.2s ease;
2352
+ user-select: none;
2353
+ `;
2354
+ // Click to toggle zoom
2355
+ img.addEventListener('click', (e) => {
2356
+ e.stopPropagation();
2357
+ if (scale === 1) {
2358
+ // Zoom in to 2.5x centered on click position
2359
+ const rect = img.getBoundingClientRect();
2360
+ const clickX = e.clientX - rect.left - rect.width / 2;
2361
+ const clickY = e.clientY - rect.top - rect.height / 2;
2362
+ scale = 2.5;
2363
+ translateX = -clickX * 1.5;
2364
+ translateY = -clickY * 1.5;
2365
+ applyTransform();
2366
+ img.style.cursor = 'zoom-out';
2367
+ }
2368
+ else {
2369
+ // Zoom out - reset
2370
+ resetTransform();
2371
+ }
2372
+ });
2373
+ // Mouse wheel zoom
2374
+ overlay.addEventListener('wheel', (e) => {
2375
+ e.preventDefault();
2376
+ const delta = e.deltaY > 0 ? -0.25 : 0.25;
2377
+ const newScale = Math.min(Math.max(scale + delta, 1), 5);
2378
+ if (newScale === 1) {
2379
+ resetTransform();
2380
+ }
2381
+ else {
2382
+ scale = newScale;
2383
+ applyTransform();
2384
+ img.style.cursor = 'zoom-out';
2385
+ }
2386
+ }, { passive: false });
2387
+ // Drag to pan when zoomed
2388
+ img.addEventListener('mousedown', (e) => {
2389
+ if (scale <= 1)
2390
+ return;
2391
+ e.preventDefault();
2392
+ isDragging = true;
2393
+ dragStartX = e.clientX;
2394
+ dragStartY = e.clientY;
2395
+ lastTranslateX = translateX;
2396
+ lastTranslateY = translateY;
2397
+ img.style.cursor = 'grabbing';
2398
+ img.style.transition = 'none';
2399
+ });
2400
+ window.addEventListener('mousemove', (e) => {
2401
+ if (!isDragging)
2402
+ return;
2403
+ translateX = lastTranslateX + (e.clientX - dragStartX);
2404
+ translateY = lastTranslateY + (e.clientY - dragStartY);
2405
+ applyTransform();
2406
+ });
2407
+ window.addEventListener('mouseup', () => {
2408
+ if (!isDragging)
2409
+ return;
2410
+ isDragging = false;
2411
+ img.style.cursor = scale > 1 ? 'zoom-out' : 'zoom-in';
2412
+ img.style.transition = 'transform 0.2s ease';
2413
+ });
2414
+ // Touch: pinch to zoom + drag to pan
2415
+ let lastTouchDist = 0;
2416
+ let lastTouchScale = 1;
2417
+ overlay.addEventListener('touchstart', (e) => {
2418
+ if (e.touches.length === 2) {
2419
+ e.preventDefault();
2420
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
2421
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
2422
+ lastTouchDist = Math.hypot(dx, dy);
2423
+ lastTouchScale = scale;
2424
+ }
2425
+ else if (e.touches.length === 1 && scale > 1) {
2426
+ isDragging = true;
2427
+ dragStartX = e.touches[0].clientX;
2428
+ dragStartY = e.touches[0].clientY;
2429
+ lastTranslateX = translateX;
2430
+ lastTranslateY = translateY;
2431
+ img.style.transition = 'none';
2432
+ }
2433
+ }, { passive: false });
2434
+ overlay.addEventListener('touchmove', (e) => {
2435
+ if (e.touches.length === 2) {
2436
+ e.preventDefault();
2437
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
2438
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
2439
+ const dist = Math.hypot(dx, dy);
2440
+ scale = Math.min(Math.max(lastTouchScale * (dist / lastTouchDist), 1), 5);
2441
+ if (scale === 1) {
2442
+ translateX = 0;
2443
+ translateY = 0;
2444
+ }
2445
+ applyTransform();
2446
+ }
2447
+ else if (e.touches.length === 1 && isDragging) {
2448
+ e.preventDefault();
2449
+ translateX = lastTranslateX + (e.touches[0].clientX - dragStartX);
2450
+ translateY = lastTranslateY + (e.touches[0].clientY - dragStartY);
2451
+ applyTransform();
2452
+ }
2453
+ }, { passive: false });
2454
+ overlay.addEventListener('touchend', (e) => {
2455
+ if (e.touches.length < 2) {
2456
+ lastTouchDist = 0;
2457
+ }
2458
+ if (e.touches.length === 0) {
2459
+ isDragging = false;
2460
+ img.style.transition = 'transform 0.2s ease';
2461
+ }
2462
+ });
2463
+ const close = () => {
2464
+ document.removeEventListener('keydown', handleKeyDown);
2465
+ overlay.remove();
2466
+ };
2467
+ // Only close on backdrop click when not zoomed (prevent accidental close while panning)
2468
+ overlay.addEventListener('click', (e) => {
2469
+ if (e.target === overlay && scale <= 1)
2470
+ close();
2471
+ });
2472
+ downloadBtn.addEventListener('click', (e) => e.stopPropagation());
2473
+ closeBtn.addEventListener('click', (e) => { e.stopPropagation(); close(); });
2474
+ // Close on Escape
2475
+ const handleKeyDown = (e) => {
2476
+ if (e.key === 'Escape')
2477
+ close();
2478
+ };
2479
+ document.addEventListener('keydown', handleKeyDown);
2480
+ overlay.appendChild(closeBtn);
2481
+ overlay.appendChild(downloadBtn);
2482
+ overlay.appendChild(img);
2483
+ document.body.appendChild(overlay);
2278
2484
  }
2279
2485
  /**
2280
2486
  * Initialize the SDK and render widget
@@ -2386,8 +2592,6 @@ class WeldSDK {
2386
2592
  console.log('[Weld SDK] Opening widget...');
2387
2593
  this.stateCoordinator.openWidget();
2388
2594
  this.iframeManager.showIframe(IframeType.WIDGET);
2389
- this.iframeManager.showIframe(IframeType.BACKDROP);
2390
- // Keep launcher visible so user can click it to close the widget
2391
2595
  // Send open message to the widget iframe
2392
2596
  const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2393
2597
  if (widgetIframe?.element?.contentWindow) {
@@ -2408,8 +2612,6 @@ class WeldSDK {
2408
2612
  console.log('[Weld SDK] Closing widget...');
2409
2613
  this.stateCoordinator.closeWidget();
2410
2614
  this.iframeManager.hideIframe(IframeType.WIDGET);
2411
- this.iframeManager.hideIframe(IframeType.BACKDROP);
2412
- // Launcher stays visible
2413
2615
  // Send close message to the widget iframe
2414
2616
  const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2415
2617
  if (widgetIframe?.element?.contentWindow) {