@rogieking/figui3 9.0.11 → 9.0.13

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-editor.js CHANGED
@@ -495,6 +495,8 @@ figEditorDefineElement("fig-select-options", FigSelectOptions);
495
495
  * A dropdown-styled select.
496
496
  * @attr {string} variant - Visual style. Use `ghost` for a borderless control.
497
497
  * @attr {string} size - Control size. Use `large` for a 32px-tall control.
498
+ * @attr {string} menu-anchor - CSS selector for an alternate menu anchor.
499
+ * @property {Element|string|null} menuAnchor - Alternate menu anchor element or selector.
498
500
  * @event optionfocus - Fires with the focused option value during open-menu keyboard navigation.
499
501
  */
500
502
  class FigSelect extends HTMLElement {
@@ -521,6 +523,7 @@ class FigSelect extends HTMLElement {
521
523
  #frozenLabelRect = null;
522
524
  #frozenViewport = null;
523
525
  #syncingOptions = false;
526
+ #menuAnchorRef = null;
524
527
  #boundTriggerClick = this.#handleTriggerClick.bind(this);
525
528
  #boundOptionClick = this.#handleOptionClick.bind(this);
526
529
  #boundOptionPointerOver = this.#handleOptionPointerOver.bind(this);
@@ -540,6 +543,7 @@ class FigSelect extends HTMLElement {
540
543
  "open",
541
544
  "variant",
542
545
  "size",
546
+ "menu-anchor",
543
547
  "aria-label",
544
548
  ];
545
549
  }
@@ -562,6 +566,26 @@ class FigSelect extends HTMLElement {
562
566
  else this.removeAttribute("open");
563
567
  }
564
568
 
569
+ get menuAnchor() {
570
+ return this.#menuAnchorRef ?? this.getAttribute("menu-anchor");
571
+ }
572
+
573
+ set menuAnchor(value) {
574
+ if (value instanceof Element) {
575
+ if (this.hasAttribute("menu-anchor")) {
576
+ this.removeAttribute("menu-anchor");
577
+ }
578
+ this.#menuAnchorRef = value;
579
+ } else if (typeof value === "string" && value.trim()) {
580
+ this.#menuAnchorRef = null;
581
+ this.setAttribute("menu-anchor", value);
582
+ } else {
583
+ this.#menuAnchorRef = null;
584
+ this.removeAttribute("menu-anchor");
585
+ }
586
+ this.#syncMenuAnchor();
587
+ }
588
+
565
589
  connectedCallback() {
566
590
  if (!this.#initialized) this.#initialize();
567
591
  this.#ensurePanelSlotAttrs();
@@ -606,8 +630,14 @@ class FigSelect extends HTMLElement {
606
630
  else this.#openList();
607
631
  return;
608
632
  }
633
+ if (name === "menu-anchor") {
634
+ this.#menuAnchorRef = null;
635
+ this.#syncMenuAnchor();
636
+ return;
637
+ }
609
638
  if (name === "position" || name === "offset" || name === "closedby") {
610
639
  this.#syncPopupAttrs();
640
+ if (this.open) this.#placeOpenMenu();
611
641
  }
612
642
  }
613
643
 
@@ -875,7 +905,7 @@ class FigSelect extends HTMLElement {
875
905
  this.#labelEl = labelEl;
876
906
  this.#popup = popup;
877
907
  this.#panelSlot = panelSlot;
878
- popup.anchor = button;
908
+ this.#syncMenuAnchor();
879
909
 
880
910
  this.#ensurePanelSlotAttrs();
881
911
  this.#installPopupPositioning();
@@ -897,7 +927,7 @@ class FigSelect extends HTMLElement {
897
927
  this.#originalPositionPopup?.();
898
928
  return;
899
929
  }
900
- this.#positionPopupOverSelected();
930
+ this.#placeOpenMenu();
901
931
  };
902
932
  this.#popupPositionPatched = true;
903
933
  }
@@ -920,6 +950,60 @@ class FigSelect extends HTMLElement {
920
950
  return { top: 8, right: 8, bottom: 8, left: 8 };
921
951
  }
922
952
 
953
+ #resolveExplicitMenuAnchor() {
954
+ if (this.#menuAnchorRef?.isConnected) return this.#menuAnchorRef;
955
+
956
+ const selector = this.getAttribute("menu-anchor");
957
+ if (selector) {
958
+ try {
959
+ const localMatch = this.parentElement?.querySelector(selector);
960
+ if (
961
+ localMatch &&
962
+ localMatch !== this &&
963
+ !this.contains(localMatch)
964
+ ) {
965
+ return localMatch;
966
+ }
967
+
968
+ const root = this.getRootNode();
969
+ const rootMatch = root?.querySelector?.(selector);
970
+ if (
971
+ rootMatch &&
972
+ rootMatch !== this &&
973
+ !this.contains(rootMatch)
974
+ ) {
975
+ return rootMatch;
976
+ }
977
+ } catch {}
978
+ }
979
+
980
+ return null;
981
+ }
982
+
983
+ #resolveMenuAnchor() {
984
+ return this.#resolveExplicitMenuAnchor() || this.#button;
985
+ }
986
+
987
+ #hasMenuAnchor() {
988
+ return Boolean(this.#resolveExplicitMenuAnchor());
989
+ }
990
+
991
+ #usesMenuAnchorOverlayGeometry() {
992
+ return !this.#usesExplicitPosition() && this.#hasMenuAnchor();
993
+ }
994
+
995
+ #syncMenuAnchor() {
996
+ if (!this.#popup) return;
997
+ this.#popup.anchor = this.#resolveMenuAnchor();
998
+ this.#syncPopupWidth();
999
+ if (!this.open) return;
1000
+ this.#freezeMenuPosition = false;
1001
+ this.#frozenLabelRect = null;
1002
+ this.#frozenViewport = null;
1003
+ this.#popup.setupObservers?.();
1004
+ this.#popup.queueReposition?.();
1005
+ }
1006
+
923
1007
  #getTriggerAlignmentRect() {
924
1008
  const labelRect = this.#labelEl?.getBoundingClientRect();
925
1009
  if (labelRect?.width && labelRect?.height) return labelRect;
@@ -933,7 +1017,11 @@ class FigSelect extends HTMLElement {
933
1017
  }
934
1018
 
935
1019
  #readLabelRectSnapshot() {
936
- const rect = this.#getTriggerAlignmentRect();
1020
+ const anchor = this.#usesMenuAnchorOverlayGeometry()
1021
+ ? this.#resolveExplicitMenuAnchor()
1022
+ : null;
1023
+ const rect = anchor?.getBoundingClientRect() ??
1024
+ this.#getTriggerAlignmentRect();
937
1025
  if (!rect) return null;
938
1026
  return {
939
1027
  x: rect.x,
@@ -995,10 +1083,99 @@ class FigSelect extends HTMLElement {
995
1083
  this.#frozenViewport = this.#readViewportSnapshot();
996
1084
  }
997
1085
 
1086
+ #usesExplicitPosition() {
1087
+ const value = this.getAttribute("position");
1088
+ return Boolean(value && value.trim());
1089
+ }
1090
+
1091
+ #clearOverlayPositionStyles() {
1092
+ if (!this.#popup) return;
1093
+ this.#popup.style.removeProperty("left");
1094
+ this.#popup.style.removeProperty("top");
1095
+ this.#popup.style.removeProperty("right");
1096
+ this.#popup.style.removeProperty("bottom");
1097
+ }
1098
+
1099
+ #placeOpenMenu() {
1100
+ if (this.#usesExplicitPosition()) {
1101
+ this.#freezeMenuPosition = false;
1102
+ this.#frozenLabelRect = null;
1103
+ this.#frozenViewport = null;
1104
+ this.#syncPopupWidth();
1105
+ this.#clearOverlayPositionStyles();
1106
+ if (this.#positionExplicitMenuAnchor()) return;
1107
+ this.#originalPositionPopup?.();
1108
+ this.#clampMenuToViewport();
1109
+ return;
1110
+ }
1111
+ this.#positionPopupOverSelected();
1112
+ }
1113
+
1114
+ #positionExplicitMenuAnchor() {
1115
+ const popup = this.#popup;
1116
+ const menuAnchor = this.#resolveExplicitMenuAnchor();
1117
+ if (
1118
+ !popup ||
1119
+ !menuAnchor ||
1120
+ typeof popup.parsePosition !== "function" ||
1121
+ typeof popup.parseOffset !== "function" ||
1122
+ typeof popup.computeCoords !== "function"
1123
+ ) {
1124
+ return false;
1125
+ }
1126
+
1127
+ const popupRect = popup.getBoundingClientRect();
1128
+ const anchorRect = menuAnchor.getBoundingClientRect();
1129
+ if (!popupRect.width || !popupRect.height) return false;
1130
+ const { vertical, horizontal, shorthand } = popup.parsePosition();
1131
+ const offset = popup.parseOffset();
1132
+ let { left, top } = popup.computeCoords(
1133
+ anchorRect,
1134
+ popupRect,
1135
+ vertical,
1136
+ horizontal,
1137
+ offset,
1138
+ shorthand,
1139
+ );
1140
+ const margins = this.#getViewportMargins();
1141
+ if (typeof popup.clampToViewport === "function") {
1142
+ ({ left, top } = popup.clampToViewport(
1143
+ { left, top },
1144
+ popupRect,
1145
+ margins,
1146
+ ));
1147
+ }
1148
+ popup.style.setProperty("right", "auto", "important");
1149
+ popup.style.setProperty("bottom", "auto", "important");
1150
+ popup.style.setProperty("left", `${Math.round(left)}px`, "important");
1151
+ popup.style.setProperty("top", `${Math.round(top)}px`, "important");
1152
+ return true;
1153
+ }
1154
+
1155
+ #clampMenuToViewport() {
1156
+ const popup = this.#popup;
1157
+ if (!popup || typeof popup.clampToViewport !== "function") return;
1158
+ const popupRect = popup.getBoundingClientRect();
1159
+ if (!popupRect.width || !popupRect.height) return;
1160
+ const margins = this.#getViewportMargins();
1161
+ const { left, top } = popup.clampToViewport(
1162
+ { left: popupRect.left, top: popupRect.top },
1163
+ popupRect,
1164
+ margins,
1165
+ );
1166
+ popup.style.setProperty("right", "auto", "important");
1167
+ popup.style.setProperty("bottom", "auto", "important");
1168
+ popup.style.setProperty("left", `${Math.round(left)}px`, "important");
1169
+ popup.style.setProperty("top", `${Math.round(top)}px`, "important");
1170
+ }
1171
+
998
1172
  #positionPopupOverSelected() {
999
1173
  // Content ResizeObserver / overflow scroll re-enter here; keep the
1000
1174
  // open-time alignment unless the trigger or viewport actually changed.
1001
- if (this.#shouldSkipFrozenPositionPass()) return;
1175
+ if (this.#shouldSkipFrozenPositionPass()) {
1176
+ this.#clampMenuToViewport();
1177
+ return;
1178
+ }
1002
1179
 
1003
1180
  const popup = this.#popup;
1004
1181
  const label = this.#labelEl;
@@ -1016,30 +1193,46 @@ class FigSelect extends HTMLElement {
1016
1193
  return;
1017
1194
  }
1018
1195
 
1196
+ const menuAnchor = this.#usesMenuAnchorOverlayGeometry()
1197
+ ? this.#resolveExplicitMenuAnchor()
1198
+ : null;
1199
+ if (menuAnchor) this.#syncPopupWidth();
1200
+
1019
1201
  // Lay out with the default positioning first so option metrics are valid.
1020
1202
  this.#originalPositionPopup?.();
1021
1203
 
1022
1204
  const popupRect = popup.getBoundingClientRect();
1023
- const labelRect = this.#getTriggerAlignmentRect();
1024
- const optionTextRect = this.#getOptionTextRect(selected);
1025
- if (
1026
- !popupRect.width ||
1027
- !popupRect.height ||
1028
- !labelRect.width ||
1029
- !optionTextRect
1030
- ) {
1205
+ const selectedOptionRect = selected.getBoundingClientRect();
1206
+ if (!popupRect.width || !popupRect.height || !selectedOptionRect.height) {
1031
1207
  return;
1032
1208
  }
1033
1209
 
1034
- const selectedOffsetX = optionTextRect.left - popupRect.left;
1035
- const selectedOffsetY = optionTextRect.top - popupRect.top;
1036
- const full = figEditorBooleanAttribute(this, "full");
1037
- // [full]: pin menu to host width/edges. Otherwise overlay selected
1038
- // option text on the trigger label (blend-mode style).
1039
- let left = full
1040
- ? this.getBoundingClientRect().left
1041
- : labelRect.left - selectedOffsetX;
1042
- let top = labelRect.top - selectedOffsetY;
1210
+ let left;
1211
+ let top;
1212
+ let alignmentRect;
1213
+ if (menuAnchor) {
1214
+ const anchorRect = menuAnchor.getBoundingClientRect();
1215
+ alignmentRect = anchorRect;
1216
+ left = anchorRect.left + (anchorRect.width - popupRect.width) / 2;
1217
+ top =
1218
+ anchorRect.top +
1219
+ (anchorRect.height - selectedOptionRect.height) / 2 -
1220
+ (selectedOptionRect.top - popupRect.top);
1221
+ } else {
1222
+ const labelRect = this.#getTriggerAlignmentRect();
1223
+ const optionTextRect = this.#getOptionTextRect(selected);
1224
+ if (!labelRect?.width || !optionTextRect) return;
1225
+ alignmentRect = labelRect;
1226
+ const selectedOffsetX = optionTextRect.left - popupRect.left;
1227
+ const selectedOffsetY = optionTextRect.top - popupRect.top;
1228
+ const full = figEditorBooleanAttribute(this, "full");
1229
+ // [full]: pin menu to host width/edges. Otherwise overlay selected
1230
+ // option text on the trigger label (blend-mode style).
1231
+ left = full
1232
+ ? this.getBoundingClientRect().left
1233
+ : labelRect.left - selectedOffsetX;
1234
+ top = labelRect.top - selectedOffsetY;
1235
+ }
1043
1236
 
1044
1237
  // Keep the whole menu in-view when aligning over the selected option
1045
1238
  // would otherwise push it past a viewport edge (corners / far sides).
@@ -1062,15 +1255,24 @@ class FigSelect extends HTMLElement {
1062
1255
  popup.style.setProperty("left", `${Math.round(left)}px`, "important");
1063
1256
  popup.style.setProperty("top", `${Math.round(top)}px`, "important");
1064
1257
 
1065
- // Nudge the panel scroller so the selected label stays over the trigger.
1258
+ // If the panel scrolls, keep the selected row centered over a menu anchor.
1259
+ // Standard selects retain text-to-trigger alignment.
1066
1260
  const panel = this.#getPanel();
1067
- const alignedTextRect = this.#getOptionTextRect(selected);
1261
+ const alignedOptionRect = selected.getBoundingClientRect();
1262
+ const alignedTextRect = menuAnchor
1263
+ ? null
1264
+ : this.#getOptionTextRect(selected);
1068
1265
  if (
1069
- alignedTextRect &&
1070
1266
  panel &&
1071
1267
  panel.scrollHeight > panel.clientHeight + 1
1072
1268
  ) {
1073
- const deltaY = alignedTextRect.top - labelRect.top;
1269
+ const deltaY = menuAnchor
1270
+ ? alignedOptionRect.top +
1271
+ alignedOptionRect.height / 2 -
1272
+ (alignmentRect.top + alignmentRect.height / 2)
1273
+ : alignedTextRect
1274
+ ? alignedTextRect.top - alignmentRect.top
1275
+ : 0;
1074
1276
  if (Math.abs(deltaY) > 0.5) {
1075
1277
  panel.scrollTop += deltaY;
1076
1278
  }
@@ -1395,9 +1597,7 @@ class FigSelect extends HTMLElement {
1395
1597
  e.preventDefault();
1396
1598
  e.stopPropagation();
1397
1599
  const nextOpen = !this.open;
1398
- if (nextOpen && this.#popup && this.#button) {
1399
- this.#popup.anchor = this.#button;
1400
- }
1600
+ if (nextOpen) this.#syncMenuAnchor();
1401
1601
  this.open = nextOpen;
1402
1602
  }
1403
1603
 
@@ -1448,7 +1648,7 @@ class FigSelect extends HTMLElement {
1448
1648
  (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ")
1449
1649
  ) {
1450
1650
  e.preventDefault();
1451
- if (this.#popup && this.#button) this.#popup.anchor = this.#button;
1651
+ this.#syncMenuAnchor();
1452
1652
  this.open = true;
1453
1653
  requestAnimationFrame(() => {
1454
1654
  const options = this.#getOptions({ enabledOnly: true });
@@ -1554,21 +1754,66 @@ class FigSelect extends HTMLElement {
1554
1754
  const hostWidth = Math.ceil(this.getBoundingClientRect().width);
1555
1755
  const triggerWidth = Math.ceil(this.#button.getBoundingClientRect().width);
1556
1756
  const anchorWidth = Math.max(hostWidth, triggerWidth, 96);
1757
+ const menuAnchor = this.#hasMenuAnchor()
1758
+ ? this.#resolveExplicitMenuAnchor()
1759
+ : null;
1557
1760
 
1558
1761
  // Use !important — fig-select::part(listbox) width rules beat element.style.
1559
1762
  // Menus size to their options while remaining at least as wide as the trigger.
1560
- this.#popup.style.setProperty("width", "max-content", "important");
1561
- this.#popup.style.setProperty("min-width", `${anchorWidth}px`, "important");
1562
- this.#popup.style.setProperty(
1563
- "max-width",
1564
- "min(20rem, calc(100vw - 1rem))",
1565
- "important",
1763
+ const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
1764
+ const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
1765
+ const rem =
1766
+ parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
1767
+ const maxWidth = Math.max(
1768
+ 0,
1769
+ Math.min(Math.floor(20 * rem), Math.floor(viewportWidth - 16)),
1566
1770
  );
1771
+ const maxHeight = Math.max(0, Math.floor(viewportHeight - 16));
1772
+ this.#popup.style.setProperty("box-sizing", "border-box", "important");
1773
+ this.#popup.style.setProperty("padding", "0", "important");
1774
+ this.#popup.style.setProperty("border", "0", "important");
1775
+ this.#popup.style.setProperty("margin", "0", "important");
1776
+ if (menuAnchor) {
1777
+ const requestedWidth = menuAnchor.getBoundingClientRect().width;
1778
+ // Match a normal anchor exactly. If that would make the menu narrower
1779
+ // than its usable trigger/content width, keep the wider menu. Default
1780
+ // overlay centers it; explicit position applies its requested alignment.
1781
+ this.#popup.style.setProperty("width", "max-content", "important");
1782
+ this.#popup.style.setProperty("min-width", "0", "important");
1783
+ this.#popup.style.setProperty("max-width", `${maxWidth}px`, "important");
1784
+ const intrinsicWidth = this.#popup.getBoundingClientRect().width;
1785
+ const minimumWidth = Math.min(
1786
+ maxWidth,
1787
+ Math.max(anchorWidth, intrinsicWidth),
1788
+ );
1789
+ const width = Math.max(requestedWidth, minimumWidth);
1790
+ this.#popup.style.setProperty("width", `${width}px`, "important");
1791
+ this.#popup.style.setProperty("min-width", `${width}px`, "important");
1792
+ this.#popup.style.setProperty("max-width", `${width}px`, "important");
1793
+ } else {
1794
+ this.#popup.style.setProperty(
1795
+ "width",
1796
+ "max-content",
1797
+ "important",
1798
+ );
1799
+ this.#popup.style.setProperty("min-width", `${anchorWidth}px`, "important");
1800
+ this.#popup.style.setProperty("max-width", `${maxWidth}px`, "important");
1801
+ }
1802
+ this.#popup.style.setProperty("max-height", `${maxHeight}px`, "important");
1803
+ this.#popup.style.setProperty("height", "max-content", "important");
1804
+ this.#popup.style.setProperty("overflow", "hidden", "important");
1805
+ const used = this.#popup.getBoundingClientRect();
1806
+ if (used.height > maxHeight) {
1807
+ this.#popup.style.setProperty("height", `${maxHeight}px`, "important");
1808
+ }
1809
+ if (!menuAnchor && used.width > maxWidth) {
1810
+ this.#popup.style.setProperty("width", `${maxWidth}px`, "important");
1811
+ }
1567
1812
  }
1568
1813
 
1569
1814
  #openList() {
1570
1815
  if (!this.#popup || figEditorBooleanAttribute(this, "disabled")) return;
1571
- if (this.#button) this.#popup.anchor = this.#button;
1816
+ this.#syncMenuAnchor();
1572
1817
  this.#installPopupPositioning();
1573
1818
  this.#freezeMenuPosition = false;
1574
1819
  this.#frozenLabelRect = null;
@@ -1580,26 +1825,33 @@ class FigSelect extends HTMLElement {
1580
1825
  this.#button?.setAttribute("aria-expanded", "true");
1581
1826
  this.#focusedIndex = -1;
1582
1827
  requestAnimationFrame(() => {
1583
- this.#syncPopupWidth();
1584
- this.#positionPopupOverSelected();
1585
- const panel = this.#getPanel();
1586
- const options = this.#getEnabledOptions();
1587
- const selectedIndex = options.findIndex(
1588
- (opt) => this.#optionValue(opt) === this.value,
1589
- );
1590
- if (selectedIndex >= 0) {
1591
- this.#focusOptionAt(selectedIndex);
1592
- } else if (
1593
- this.#button?.hasAttribute("data-focus-visible") ||
1594
- this.#button?.matches?.(":focus-visible")
1595
- ) {
1596
- this.#focusOptionAt(0);
1597
- }
1598
- panel?.syncOverflow?.();
1599
- // Freeze after open align so later positionPopup passes don't undo scroll.
1600
- // Window resize / trigger movement still realigns via geometry checks.
1601
- this.#freezeMenuPosition = true;
1602
- this.#rememberFrozenGeometry();
1828
+ requestAnimationFrame(() => {
1829
+ this.#syncPopupWidth();
1830
+ this.#placeOpenMenu();
1831
+ this.#syncPopupWidth();
1832
+ this.#clampMenuToViewport();
1833
+ const panel = this.#getPanel();
1834
+ const options = this.#getEnabledOptions();
1835
+ const selectedIndex = options.findIndex(
1836
+ (opt) => this.#optionValue(opt) === this.value,
1837
+ );
1838
+ if (selectedIndex >= 0) {
1839
+ this.#focusOptionAt(selectedIndex);
1840
+ } else if (
1841
+ this.#button?.hasAttribute("data-focus-visible") ||
1842
+ this.#button?.matches?.(":focus-visible")
1843
+ ) {
1844
+ this.#focusOptionAt(0);
1845
+ }
1846
+ panel?.syncOverflow?.();
1847
+ // Freeze overlay after layout settles so later positionPopup passes
1848
+ // don't undo scroll. Still clamp if content grows past the viewport.
1849
+ // Explicit position stays live so the popup can follow the anchor.
1850
+ if (!this.#usesExplicitPosition()) {
1851
+ this.#freezeMenuPosition = true;
1852
+ this.#rememberFrozenGeometry();
1853
+ }
1854
+ });
1603
1855
  });
1604
1856
  }
1605
1857
 
package/fig.js CHANGED
@@ -318,6 +318,10 @@ fig-select-options > .fig-overflow-end::before,
318
318
  .fig-menu-options > .fig-overflow-end::before {
319
319
  border-radius: 0 0 var(--radius-large) var(--radius-large);
320
320
  }
321
+ fig-select-options.overflow-start > :is(fig-separator, fig-menu-separator)[sticky]:not([sticky="false"]),
322
+ .fig-menu-options.overflow-start > :is(fig-separator, fig-menu-separator)[sticky]:not([sticky="false"]) {
323
+ top: var(--fig-vertical-overflow-size, var(--spacer-4)) !important;
324
+ }
321
325
  `;
322
326
 
323
327
  const FIG_SHADOW_ICON_CSS = `
@@ -3933,10 +3937,12 @@ class FigPopup extends HTMLDialogElement {
3933
3937
  const bounds = this.getViewportBounds(m);
3934
3938
  const maxLeft = bounds.maxRight - popupRect.width;
3935
3939
  const maxTop = bounds.maxBottom - popupRect.height;
3940
+ const clampLeftMax = Math.max(bounds.minLeft, maxLeft);
3941
+ const clampTopMax = Math.max(bounds.minTop, maxTop);
3936
3942
 
3937
3943
  return {
3938
- left: Math.min(maxLeft, Math.max(bounds.minLeft, coords.left)),
3939
- top: Math.min(maxTop, Math.max(bounds.minTop, coords.top)),
3944
+ left: Math.min(clampLeftMax, Math.max(bounds.minLeft, coords.left)),
3945
+ top: Math.min(clampTopMax, Math.max(bounds.minTop, coords.top)),
3940
3946
  };
3941
3947
  }
3942
3948
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "9.0.11",
3
+ "version": "9.0.13",
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": "SEE LICENSE IN LICENSE",