@rogieking/figui3 6.7.0 → 6.7.2

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/fig.js CHANGED
@@ -750,6 +750,7 @@ class FigTooltip extends HTMLElement {
750
750
  static #lastHiddenAt = 0;
751
751
  static #warmupWindow = 1000;
752
752
  static #hoverOpen = null;
753
+ static #documentExitListenersReady = false;
753
754
 
754
755
  #boundHideOnChromeOpen;
755
756
  #boundHidePopupOutsideClick;
@@ -788,6 +789,7 @@ class FigTooltip extends HTMLElement {
788
789
  };
789
790
  }
790
791
  connectedCallback() {
792
+ FigTooltip.#ensureDocumentExitListeners();
791
793
  this.setup();
792
794
  this.#bindTriggerListeners();
793
795
  this.setupEventListeners();
@@ -1197,6 +1199,48 @@ class FigTooltip extends HTMLElement {
1197
1199
  }
1198
1200
  }
1199
1201
 
1202
+ static #ensureDocumentExitListeners() {
1203
+ if (FigTooltip.#documentExitListenersReady) return;
1204
+ FigTooltip.#documentExitListenersReady = true;
1205
+
1206
+ const handlePointerLeftDocument = () => {
1207
+ FigTooltip.#dismissHoverTooltipsOnDocumentExit();
1208
+ };
1209
+
1210
+ document.documentElement.addEventListener(
1211
+ "mouseleave",
1212
+ handlePointerLeftDocument,
1213
+ );
1214
+ document.addEventListener("mouseout", (event) => {
1215
+ if (event.relatedTarget) return;
1216
+ handlePointerLeftDocument();
1217
+ });
1218
+
1219
+ // Same-origin embed: leaving the iframe element (e.g. into a parent dialog).
1220
+ try {
1221
+ const frame = window.frameElement;
1222
+ if (frame) {
1223
+ frame.addEventListener("mouseleave", handlePointerLeftDocument);
1224
+ }
1225
+ } catch {}
1226
+
1227
+ window.addEventListener("message", (event) => {
1228
+ if (event?.data?.type !== "figui:dismiss-tooltips") return;
1229
+ if (window.parent !== window && event.source !== window.parent) return;
1230
+ handlePointerLeftDocument();
1231
+ });
1232
+ }
1233
+
1234
+ static #dismissHoverTooltipsOnDocumentExit() {
1235
+ for (const node of document.querySelectorAll("fig-tooltip")) {
1236
+ if (!(node instanceof FigTooltip)) continue;
1237
+ if (node.action !== "hover") continue;
1238
+ if (node.hasAttribute("show") && node.getAttribute("show") !== "false")
1239
+ continue;
1240
+ if (node.isOpen || node.timeout) node.hidePopup();
1241
+ }
1242
+ }
1243
+
1200
1244
  static #programmatic = new WeakMap();
1201
1245
 
1202
1246
  static show(anchor, text, options = {}) {
@@ -1376,6 +1420,8 @@ class FigDialog extends HTMLDialogElement {
1376
1420
  this._boundContentMutation = this._scheduleAutoResize.bind(this);
1377
1421
  this._boundContentResize = this._scheduleAutoResize.bind(this);
1378
1422
  this._boundRestoreFocus = this._restoreFocus.bind(this);
1423
+ this._boundIframeMouseLeave = this._handleIframeMouseLeave.bind(this);
1424
+ this._iframeDismissMutationObserver = null;
1379
1425
  this._previousFocus = null;
1380
1426
  }
1381
1427
 
@@ -1402,6 +1448,7 @@ class FigDialog extends HTMLDialogElement {
1402
1448
  this._setupDragListeners();
1403
1449
  this._applyPosition();
1404
1450
  this._syncAutoResize();
1451
+ this._setupIframeDismissListeners();
1405
1452
  this._syncA11y();
1406
1453
  });
1407
1454
 
@@ -1416,6 +1463,7 @@ class FigDialog extends HTMLDialogElement {
1416
1463
  });
1417
1464
  window.removeEventListener("message", this._boundIframeMessage);
1418
1465
  this._teardownAutoResize();
1466
+ this._teardownIframeDismissListeners();
1419
1467
  this.removeEventListener("close", this._boundRestoreFocus);
1420
1468
  }
1421
1469
 
@@ -1448,6 +1496,44 @@ class FigDialog extends HTMLDialogElement {
1448
1496
  return super.showModal();
1449
1497
  }
1450
1498
 
1499
+ _handleIframeMouseLeave(event) {
1500
+ const iframe = event?.currentTarget;
1501
+ if (!(iframe instanceof HTMLIFrameElement)) return;
1502
+ try {
1503
+ iframe.contentWindow?.postMessage({ type: "figui:dismiss-tooltips" }, "*");
1504
+ } catch {}
1505
+ }
1506
+
1507
+ _syncIframeDismissListeners() {
1508
+ for (const iframe of this.querySelectorAll(":scope > iframe")) {
1509
+ if (!(iframe instanceof HTMLIFrameElement)) continue;
1510
+ if (iframe.dataset.figuiDismissBound === "true") continue;
1511
+ iframe.dataset.figuiDismissBound = "true";
1512
+ iframe.addEventListener("mouseleave", this._boundIframeMouseLeave);
1513
+ }
1514
+ }
1515
+
1516
+ _setupIframeDismissListeners() {
1517
+ this._syncIframeDismissListeners();
1518
+ if (this._iframeDismissMutationObserver) return;
1519
+ this._iframeDismissMutationObserver = new MutationObserver(() => {
1520
+ this._syncIframeDismissListeners();
1521
+ });
1522
+ this._iframeDismissMutationObserver.observe(this, {
1523
+ childList: true,
1524
+ subtree: false,
1525
+ });
1526
+ }
1527
+
1528
+ _teardownIframeDismissListeners() {
1529
+ this._iframeDismissMutationObserver?.disconnect();
1530
+ this._iframeDismissMutationObserver = null;
1531
+ for (const iframe of this.querySelectorAll(":scope > iframe")) {
1532
+ iframe.removeEventListener("mouseleave", this._boundIframeMouseLeave);
1533
+ delete iframe.dataset.figuiDismissBound;
1534
+ }
1535
+ }
1536
+
1451
1537
  _handleIframeMessage(event) {
1452
1538
  if (!this.autoresize) return;
1453
1539
  const data = event?.data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.7.0",
3
+ "version": "6.7.2",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "MIT",