@feedvalue/core 0.1.14 → 0.1.15

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/index.cjs CHANGED
@@ -617,6 +617,8 @@ var _FeedValue = class _FeedValue {
617
617
  __publicField(this, "modal", null);
618
618
  __publicField(this, "overlay", null);
619
619
  __publicField(this, "stylesInjected", false);
620
+ // Viewport resize compensation cleanup
621
+ __publicField(this, "viewportResizeCleanup", null);
620
622
  // Auto-close timeout reference (for cleanup on destroy)
621
623
  __publicField(this, "autoCloseTimeout", null);
622
624
  // Destroyed flag - guards async continuations (fixes React StrictMode race condition)
@@ -755,6 +757,8 @@ var _FeedValue = class _FeedValue {
755
757
  clearTimeout(this.autoCloseTimeout);
756
758
  this.autoCloseTimeout = null;
757
759
  }
760
+ this.viewportResizeCleanup?.();
761
+ this.viewportResizeCleanup = null;
758
762
  this.triggerButton?.remove();
759
763
  this.modal?.remove();
760
764
  this.overlay?.remove();
@@ -1178,6 +1182,7 @@ var _FeedValue = class _FeedValue {
1178
1182
  }
1179
1183
  this.renderTrigger();
1180
1184
  this.renderModal();
1185
+ this.setupViewportCompensation();
1181
1186
  }
1182
1187
  /**
1183
1188
  * Sanitize CSS to block potentially dangerous patterns
@@ -1390,7 +1395,7 @@ var _FeedValue = class _FeedValue {
1390
1395
  getPositionStyles(position) {
1391
1396
  switch (position) {
1392
1397
  case "bottom-left":
1393
- return "bottom: 20px; left: 20px;";
1398
+ return "bottom: calc(20px + env(safe-area-inset-bottom, 0px)); left: 20px;";
1394
1399
  case "top-right":
1395
1400
  return "top: 20px; right: 20px;";
1396
1401
  case "top-left":
@@ -1399,7 +1404,7 @@ var _FeedValue = class _FeedValue {
1399
1404
  return "top: 50%; left: 50%; transform: translate(-50%, -50%);";
1400
1405
  case "bottom-right":
1401
1406
  default:
1402
- return "bottom: 20px; right: 20px;";
1407
+ return "bottom: calc(20px + env(safe-area-inset-bottom, 0px)); right: 20px;";
1403
1408
  }
1404
1409
  }
1405
1410
  /**
@@ -1408,9 +1413,9 @@ var _FeedValue = class _FeedValue {
1408
1413
  getModalPositionStyles(position) {
1409
1414
  switch (position) {
1410
1415
  case "bottom-left":
1411
- return "bottom: 20px; left: 20px;";
1416
+ return "bottom: calc(20px + env(safe-area-inset-bottom, 0px)); left: 20px;";
1412
1417
  case "bottom-right":
1413
- return "bottom: 20px; right: 20px;";
1418
+ return "bottom: calc(20px + env(safe-area-inset-bottom, 0px)); right: 20px;";
1414
1419
  case "top-right":
1415
1420
  return "top: 20px; right: 20px;";
1416
1421
  case "top-left":
@@ -1420,6 +1425,37 @@ var _FeedValue = class _FeedValue {
1420
1425
  return "top: 50%; left: 50%; transform: translate(-50%, -50%);";
1421
1426
  }
1422
1427
  }
1428
+ /**
1429
+ * Smooth viewport resize compensation for mobile browser toolbar show/hide.
1430
+ *
1431
+ * When Chrome/Safari's bottom toolbar appears or disappears, the layout viewport
1432
+ * resizes and position:fixed elements jump. This detects toolbar-sized height
1433
+ * changes and applies an inverse CSS translate to cancel the jump, then animates
1434
+ * back to the natural position. Uses the `translate` CSS property (separate from
1435
+ * `transform`) to avoid conflicting with the hover transform on the trigger.
1436
+ */
1437
+ setupViewportCompensation() {
1438
+ if (typeof window === "undefined") return;
1439
+ let lastHeight = window.innerHeight;
1440
+ const handleResize = () => {
1441
+ const newHeight = window.innerHeight;
1442
+ const delta = newHeight - lastHeight;
1443
+ lastHeight = newHeight;
1444
+ if (Math.abs(delta) <= 5 || Math.abs(delta) >= 120) return;
1445
+ const elements = [this.triggerButton, this.modal].filter(Boolean);
1446
+ for (const el of elements) {
1447
+ el.style.transition = "none";
1448
+ el.style.translate = `0 ${delta}px`;
1449
+ void el.offsetHeight;
1450
+ el.style.transition = "translate 0.3s ease-out";
1451
+ el.style.translate = "0 0";
1452
+ }
1453
+ };
1454
+ window.addEventListener("resize", handleResize, { passive: true });
1455
+ this.viewportResizeCleanup = () => {
1456
+ window.removeEventListener("resize", handleResize);
1457
+ };
1458
+ }
1423
1459
  /**
1424
1460
  * Parse SVG string to DOM element safely using DOMParser
1425
1461
  */
@@ -1620,9 +1656,6 @@ var _FeedValue = class _FeedValue {
1620
1656
  }
1621
1657
  }
1622
1658
  };
1623
- /**
1624
- * SVG icons for trigger button (matching widget-bundle exactly)
1625
- */
1626
1659
  /**
1627
1660
  * SVG icons for trigger button - must match Lucide icons used in frontend-web
1628
1661
  * chat = MessageCircle, message = MessageSquare, feedback = MessagesSquare,