@rogieking/figui3 6.8.6 → 6.8.7

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
@@ -35,6 +35,65 @@ function figNextFrame(host, callback) {
35
35
  });
36
36
  }
37
37
 
38
+ function figNormalizeTextOnlyInputSlots(host) {
39
+ host
40
+ .querySelectorAll(':scope > [slot="prepend"], :scope > [slot="append"]')
41
+ .forEach((node) => {
42
+ if (node.children.length > 0) {
43
+ figClearInputSlotTooltip(node);
44
+ return;
45
+ }
46
+ const text = node.textContent.trim();
47
+ if (!text) {
48
+ figClearInputSlotTooltip(node);
49
+ return;
50
+ }
51
+ const previousFullText = node.dataset.figInputSlotFullText || "";
52
+ const previousShortText = previousFullText
53
+ ? Array.from(previousFullText)[0]?.toUpperCase()
54
+ : "";
55
+ const fullText = text === previousShortText ? previousFullText : text;
56
+ const first = Array.from(text)[0];
57
+ const normalized = first.toUpperCase();
58
+ if (node.textContent !== normalized) node.textContent = normalized;
59
+ if (fullText && fullText !== normalized) {
60
+ node.dataset.figInputSlotFullText = fullText;
61
+ node.setAttribute("aria-label", fullText);
62
+ figBindInputSlotTooltip(node);
63
+ } else {
64
+ figClearInputSlotTooltip(node);
65
+ }
66
+ });
67
+ }
68
+
69
+ function figBindInputSlotTooltip(node) {
70
+ if (node.dataset.figInputSlotTooltipBound === "true") return;
71
+ node.dataset.figInputSlotTooltipBound = "true";
72
+ node.addEventListener("pointerenter", figShowInputSlotTooltip);
73
+ node.addEventListener("pointerleave", figHideInputSlotTooltip);
74
+ node.addEventListener("focus", figShowInputSlotTooltip);
75
+ node.addEventListener("blur", figHideInputSlotTooltip);
76
+ }
77
+
78
+ function figClearInputSlotTooltip(node) {
79
+ figHideInputSlotTooltip({ currentTarget: node });
80
+ delete node.dataset.figInputSlotFullText;
81
+ node.removeAttribute("aria-label");
82
+ }
83
+
84
+ function figShowInputSlotTooltip(event) {
85
+ const node = event.currentTarget;
86
+ const text = node?.dataset?.figInputSlotFullText;
87
+ if (!node || !text) return;
88
+ FigTooltip.show(node, text);
89
+ }
90
+
91
+ function figHideInputSlotTooltip(event) {
92
+ const node = event.currentTarget;
93
+ if (!node) return;
94
+ FigTooltip.hide(node);
95
+ }
96
+
38
97
  function createFigOverflowButtons({
39
98
  owner,
40
99
  onStart,
@@ -751,6 +810,7 @@ class FigTooltip extends HTMLElement {
751
810
  static #warmupWindow = 1000;
752
811
  static #hoverOpen = null;
753
812
  static #documentExitListenersReady = false;
813
+ static #programmaticAnchors = new Set();
754
814
 
755
815
  #boundHideOnChromeOpen;
756
816
  #boundHidePopupOutsideClick;
@@ -958,6 +1018,7 @@ class FigTooltip extends HTMLElement {
958
1018
 
959
1019
  destroy() {
960
1020
  if (this.popup) {
1021
+ this.#removeDescribedBy(this.popup.id);
961
1022
  this.popup.hidePopup?.();
962
1023
  this.popup.remove();
963
1024
  this.popup = null;
@@ -970,6 +1031,18 @@ class FigTooltip extends HTMLElement {
970
1031
  );
971
1032
  }
972
1033
  }
1034
+ #removeDescribedBy(id) {
1035
+ const trigger = this.firstElementChild;
1036
+ if (!trigger || !id) return;
1037
+ const value = trigger.getAttribute("aria-describedby");
1038
+ if (!value) return;
1039
+ const next = value
1040
+ .split(/\s+/)
1041
+ .filter((token) => token && token !== id)
1042
+ .join(" ");
1043
+ if (next) trigger.setAttribute("aria-describedby", next);
1044
+ else trigger.removeAttribute("aria-describedby");
1045
+ }
973
1046
  isTouchDevice() {
974
1047
  return (
975
1048
  "ontouchstart" in window ||
@@ -1206,6 +1279,9 @@ class FigTooltip extends HTMLElement {
1206
1279
  const handlePointerLeftDocument = () => {
1207
1280
  FigTooltip.#dismissHoverTooltipsOnDocumentExit();
1208
1281
  };
1282
+ const handleViewportChange = () => {
1283
+ FigTooltip.#dismissTooltipsOnViewportChange();
1284
+ };
1209
1285
 
1210
1286
  document.documentElement.addEventListener(
1211
1287
  "mouseleave",
@@ -1215,6 +1291,17 @@ class FigTooltip extends HTMLElement {
1215
1291
  if (event.relatedTarget) return;
1216
1292
  handlePointerLeftDocument();
1217
1293
  });
1294
+ document.addEventListener("scroll", handleViewportChange, {
1295
+ capture: true,
1296
+ passive: true,
1297
+ });
1298
+ window.addEventListener("scroll", handleViewportChange, { passive: true });
1299
+ window.visualViewport?.addEventListener("scroll", handleViewportChange, {
1300
+ passive: true,
1301
+ });
1302
+ window.visualViewport?.addEventListener("resize", handleViewportChange, {
1303
+ passive: true,
1304
+ });
1218
1305
 
1219
1306
  // Same-origin embed: leaving the iframe element (e.g. into a parent dialog).
1220
1307
  try {
@@ -1235,15 +1322,26 @@ class FigTooltip extends HTMLElement {
1235
1322
  for (const node of document.querySelectorAll("fig-tooltip")) {
1236
1323
  if (!(node instanceof FigTooltip)) continue;
1237
1324
  if (node.action !== "hover") continue;
1238
- if (node.hasAttribute("show") && node.getAttribute("show") !== "false")
1239
- continue;
1325
+ if (node.#showPersisted) continue;
1240
1326
  if (node.isOpen || node.timeout) node.hidePopup();
1241
1327
  }
1242
1328
  }
1243
1329
 
1244
1330
  static #programmatic = new WeakMap();
1245
1331
 
1332
+ static #dismissTooltipsOnViewportChange() {
1333
+ for (const node of document.querySelectorAll("fig-tooltip")) {
1334
+ if (!(node instanceof FigTooltip)) continue;
1335
+ if (node.#showPersisted) continue;
1336
+ if (node.isOpen || node.timeout) node.hidePopup();
1337
+ }
1338
+ for (const anchor of Array.from(FigTooltip.#programmaticAnchors)) {
1339
+ FigTooltip.hide(anchor);
1340
+ }
1341
+ }
1342
+
1246
1343
  static show(anchor, text, options = {}) {
1344
+ FigTooltip.#ensureDocumentExitListeners();
1247
1345
  FigTooltip.hide(anchor);
1248
1346
  const delay = options.delay ?? 500;
1249
1347
  const warm =
@@ -1252,6 +1350,7 @@ class FigTooltip extends HTMLElement {
1252
1350
 
1253
1351
  const state = { timeout: null, popup: null };
1254
1352
  FigTooltip.#programmatic.set(anchor, state);
1353
+ FigTooltip.#programmaticAnchors.add(anchor);
1255
1354
 
1256
1355
  state.timeout = setTimeout(() => {
1257
1356
  const supportsPopover =
@@ -1292,8 +1391,12 @@ class FigTooltip extends HTMLElement {
1292
1391
  const state = FigTooltip.#programmatic.get(anchor);
1293
1392
  if (!state) return;
1294
1393
  clearTimeout(state.timeout);
1295
- if (state.popup) state.popup.remove();
1394
+ if (state.popup) {
1395
+ state.popup.remove();
1396
+ FigTooltip.#lastHiddenAt = Date.now();
1397
+ }
1296
1398
  FigTooltip.#programmatic.delete(anchor);
1399
+ FigTooltip.#programmaticAnchors.delete(anchor);
1297
1400
  }
1298
1401
  }
1299
1402
 
@@ -5424,6 +5527,9 @@ class FigInputText extends HTMLElement {
5424
5527
  #boundInputChange;
5425
5528
  #boundNativeInput;
5426
5529
  #boundFocusControl;
5530
+ #boundAdornmentClick;
5531
+ #mutationObserver = null;
5532
+ #syncRaf = 0;
5427
5533
  #a11yAttributes = [
5428
5534
  "aria-label",
5429
5535
  "aria-labelledby",
@@ -5447,6 +5553,7 @@ class FigInputText extends HTMLElement {
5447
5553
  this.#syncSearchClearVisibility();
5448
5554
  };
5449
5555
  this.#boundFocusControl = this.focus.bind(this);
5556
+ this.#boundAdornmentClick = this.#handleAdornmentClick.bind(this);
5450
5557
  }
5451
5558
 
5452
5559
  connectedCallback() {
@@ -5482,6 +5589,8 @@ class FigInputText extends HTMLElement {
5482
5589
  this.#syncSearchClear();
5483
5590
  this.#syncSearchClearVisibility();
5484
5591
  this.#syncPasswordToggle();
5592
+ figNormalizeTextOnlyInputSlots(this);
5593
+ this.#startObserver();
5485
5594
 
5486
5595
  if (this.type === "number") {
5487
5596
  if (this.getAttribute("min")) {
@@ -5495,6 +5604,8 @@ class FigInputText extends HTMLElement {
5495
5604
  }
5496
5605
  this.addEventListener("pointerdown", this.#boundMouseDown);
5497
5606
  }
5607
+ this.removeEventListener("click", this.#boundAdornmentClick);
5608
+ this.addEventListener("click", this.#boundAdornmentClick);
5498
5609
  this.input.removeEventListener("change", this.#boundInputChange);
5499
5610
  this.input.addEventListener("change", this.#boundInputChange);
5500
5611
  this.input.removeEventListener("input", this.#boundNativeInput);
@@ -5506,10 +5617,17 @@ class FigInputText extends HTMLElement {
5506
5617
  this.input.removeEventListener("change", this.#boundInputChange);
5507
5618
  this.input.removeEventListener("input", this.#boundNativeInput);
5508
5619
  }
5620
+ this.removeEventListener("click", this.#boundAdornmentClick);
5509
5621
  this.removeEventListener("pointerdown", this.#boundMouseDown);
5510
5622
  window.removeEventListener("pointermove", this.#boundMouseMove);
5511
5623
  window.removeEventListener("pointerup", this.#boundMouseUp);
5512
5624
  window.removeEventListener("blur", this.#boundWindowBlur);
5625
+ this.#mutationObserver?.disconnect();
5626
+ this.#mutationObserver = null;
5627
+ if (this.#syncRaf) {
5628
+ cancelAnimationFrame(this.#syncRaf);
5629
+ this.#syncRaf = 0;
5630
+ }
5513
5631
  }
5514
5632
 
5515
5633
  focus() {
@@ -5523,37 +5641,61 @@ class FigInputText extends HTMLElement {
5523
5641
  ? existing.tagName === "TEXTAREA"
5524
5642
  : existing.tagName === "INPUT";
5525
5643
  if (matches) return existing;
5644
+ existing.remove();
5526
5645
  }
5527
5646
 
5528
- let html = `<input
5529
- type="${this.type}"
5530
- ${this.name ? `name="${this.name}"` : ""}
5531
- placeholder="${this.placeholder}"
5532
- value="${
5533
- this.type === "number" ? this.#transformNumber(this.value) : this.value
5534
- }" />`;
5647
+ let control;
5535
5648
  if (wantsTextarea) {
5536
- html = `<textarea
5537
- placeholder="${this.placeholder}">${this.value}</textarea>`;
5649
+ control = document.createElement("textarea");
5650
+ control.value = this.value;
5651
+ } else {
5652
+ control = document.createElement("input");
5653
+ control.type = this.type;
5654
+ control.value =
5655
+ this.type === "number" ? this.#transformNumber(this.value) : this.value;
5538
5656
  }
5657
+ control.setAttribute("data-generated", "input-control");
5658
+ if (this.name) control.name = this.name;
5659
+ control.placeholder = this.placeholder;
5660
+ this.insertBefore(control, this.querySelector('[slot="append"]'));
5539
5661
 
5540
- const append = this.querySelector("[slot=append]");
5541
- const prepend = this.querySelector("[slot=prepend]");
5542
-
5543
- this.innerHTML = html;
5544
-
5545
- if (prepend) {
5546
- prepend.removeEventListener("click", this.#boundFocusControl);
5547
- prepend.addEventListener("click", this.#boundFocusControl);
5548
- this.prepend(prepend);
5549
- }
5550
- if (append) {
5551
- append.removeEventListener("click", this.#boundFocusControl);
5552
- append.addEventListener("click", this.#boundFocusControl);
5553
- this.append(append);
5662
+ return control;
5663
+ }
5664
+ #handleAdornmentClick(event) {
5665
+ const adornment = event.target?.closest?.("[slot]");
5666
+ if (!adornment || adornment.parentElement !== this) return;
5667
+ this.focus();
5668
+ }
5669
+ #startObserver() {
5670
+ this.#mutationObserver?.disconnect();
5671
+ this.#mutationObserver = new MutationObserver(() => this.#scheduleLightDomSync());
5672
+ this.#mutationObserver.observe(this, {
5673
+ childList: true,
5674
+ characterData: true,
5675
+ subtree: true,
5676
+ });
5677
+ }
5678
+ #scheduleLightDomSync() {
5679
+ if (this.#syncRaf) return;
5680
+ this.#syncRaf = requestAnimationFrame(() => {
5681
+ this.#syncRaf = 0;
5682
+ if (!this.isConnected) return;
5683
+ this.#syncLightDom();
5684
+ });
5685
+ }
5686
+ #syncLightDom() {
5687
+ if (!this.input || !this.contains(this.input)) {
5688
+ this.input = this.#ensureInputControl();
5689
+ this.input.readOnly = this.readonly;
5690
+ this.input.addEventListener("change", this.#boundInputChange);
5691
+ this.input.addEventListener("input", this.#boundNativeInput);
5554
5692
  }
5555
-
5556
- return this.querySelector("input,textarea");
5693
+ this.#syncInputA11yAttributes();
5694
+ this.#syncSearchPrefix();
5695
+ this.#syncSearchClear();
5696
+ this.#syncSearchClearVisibility();
5697
+ this.#syncPasswordToggle();
5698
+ figNormalizeTextOnlyInputSlots(this);
5557
5699
  }
5558
5700
  #syncInputA11yAttributes() {
5559
5701
  if (!this.input) return;
@@ -5574,8 +5716,11 @@ class FigInputText extends HTMLElement {
5574
5716
  generated?.remove();
5575
5717
  return;
5576
5718
  }
5577
- const prepend = this.querySelector('[slot="prepend"]');
5578
- if (prepend && prepend !== generated) return;
5719
+ const prepend = this.querySelector('[slot="prepend"]:not([data-generated])');
5720
+ if (prepend) {
5721
+ generated?.remove();
5722
+ return;
5723
+ }
5579
5724
  if (generated) {
5580
5725
  const icon = generated.querySelector("fig-icon");
5581
5726
  if (icon && icon.getAttribute("name") !== "search") {
@@ -5599,8 +5744,11 @@ class FigInputText extends HTMLElement {
5599
5744
  generated?.remove();
5600
5745
  return;
5601
5746
  }
5602
- const append = this.querySelector('[slot="append"]');
5603
- if (append && append !== generated) return;
5747
+ const append = this.querySelector('[slot="append"]:not([data-generated])');
5748
+ if (append) {
5749
+ generated?.remove();
5750
+ return;
5751
+ }
5604
5752
  if (generated) {
5605
5753
  const icon = generated.querySelector("fig-icon");
5606
5754
  if (icon && icon.getAttribute("name") !== "close") {
@@ -5659,8 +5807,11 @@ class FigInputText extends HTMLElement {
5659
5807
  this.#passwordVisible = false;
5660
5808
  return;
5661
5809
  }
5662
- const append = this.querySelector('[slot="append"]');
5663
- if (append && append !== generated) return;
5810
+ const append = this.querySelector('[slot="append"]:not([data-generated])');
5811
+ if (append) {
5812
+ generated?.remove();
5813
+ return;
5814
+ }
5664
5815
  if (generated) {
5665
5816
  this.#updatePasswordToggle(generated);
5666
5817
  return;
@@ -5930,6 +6081,7 @@ class FigInputNumber extends HTMLElement {
5930
6081
  #boundBlur;
5931
6082
  #boundKeyDown;
5932
6083
  #boundFocusControl;
6084
+ #boundAdornmentClick;
5933
6085
  #units;
5934
6086
  #rawUnits;
5935
6087
  #unitsDisallow;
@@ -5937,6 +6089,8 @@ class FigInputNumber extends HTMLElement {
5937
6089
  #precision;
5938
6090
  #isInteracting = false;
5939
6091
  #stepperEl = null;
6092
+ #mutationObserver = null;
6093
+ #syncRaf = 0;
5940
6094
  #a11yAttributes = [
5941
6095
  "aria-label",
5942
6096
  "aria-labelledby",
@@ -6056,6 +6210,7 @@ class FigInputNumber extends HTMLElement {
6056
6210
  this.#handleKeyDown(e);
6057
6211
  };
6058
6212
  this.#boundFocusControl = this.focus.bind(this);
6213
+ this.#boundAdornmentClick = this.#handleAdornmentClick.bind(this);
6059
6214
  }
6060
6215
 
6061
6216
  connectedCallback() {
@@ -6114,8 +6269,12 @@ class FigInputNumber extends HTMLElement {
6114
6269
  }
6115
6270
  this.#syncStepperState();
6116
6271
  this.#syncSpinbuttonAria();
6272
+ figNormalizeTextOnlyInputSlots(this);
6117
6273
 
6118
6274
  this.addEventListener("pointerdown", this.#boundMouseDown);
6275
+ this.removeEventListener("click", this.#boundAdornmentClick);
6276
+ this.addEventListener("click", this.#boundAdornmentClick);
6277
+ this.#startObserver();
6119
6278
  this.input.removeEventListener("change", this.#boundInputChange);
6120
6279
  this.input.addEventListener("change", this.#boundInputChange);
6121
6280
  this.input.removeEventListener("input", this.#boundInput);
@@ -6136,10 +6295,17 @@ class FigInputNumber extends HTMLElement {
6136
6295
  this.input.removeEventListener("blur", this.#boundBlur);
6137
6296
  this.input.removeEventListener("keydown", this.#boundKeyDown);
6138
6297
  }
6298
+ this.removeEventListener("click", this.#boundAdornmentClick);
6139
6299
  this.removeEventListener("pointerdown", this.#boundMouseDown);
6140
6300
  window.removeEventListener("pointermove", this.#boundMouseMove);
6141
6301
  window.removeEventListener("pointerup", this.#boundMouseUp);
6142
6302
  window.removeEventListener("blur", this.#boundWindowBlur);
6303
+ this.#mutationObserver?.disconnect();
6304
+ this.#mutationObserver = null;
6305
+ if (this.#syncRaf) {
6306
+ cancelAnimationFrame(this.#syncRaf);
6307
+ this.#syncRaf = 0;
6308
+ }
6143
6309
  }
6144
6310
 
6145
6311
  focus() {
@@ -6150,30 +6316,62 @@ class FigInputNumber extends HTMLElement {
6150
6316
  const existing = this.querySelector("input");
6151
6317
  if (existing) return existing;
6152
6318
 
6153
- const html = `<input
6154
- type="text"
6155
- inputmode="decimal"
6156
- ${this.name ? `name="${this.name}"` : ""}
6157
- placeholder="${this.placeholder}"
6158
- value="${this.#formatWithUnit(this.value)}" />`;
6319
+ const input = document.createElement("input");
6320
+ input.type = "text";
6321
+ input.inputMode = "decimal";
6322
+ input.setAttribute("data-generated", "input-control");
6323
+ if (this.name) input.name = this.name;
6324
+ input.placeholder = this.placeholder;
6325
+ input.value = this.#formatWithUnit(this.value);
6326
+ this.insertBefore(input, this.querySelector('[slot="append"]'));
6159
6327
 
6160
- const append = this.querySelector("[slot=append]");
6161
- const prepend = this.querySelector("[slot=prepend]");
6328
+ return input;
6329
+ }
6162
6330
 
6163
- this.innerHTML = html;
6331
+ #handleAdornmentClick(event) {
6332
+ const adornment = event.target?.closest?.("[slot]");
6333
+ if (!adornment || adornment.parentElement !== this) return;
6334
+ this.focus();
6335
+ }
6164
6336
 
6165
- if (prepend) {
6166
- prepend.removeEventListener("click", this.#boundFocusControl);
6167
- prepend.addEventListener("click", this.#boundFocusControl);
6168
- this.prepend(prepend);
6337
+ #startObserver() {
6338
+ this.#mutationObserver?.disconnect();
6339
+ this.#mutationObserver = new MutationObserver(() => this.#scheduleLightDomSync());
6340
+ this.#mutationObserver.observe(this, {
6341
+ childList: true,
6342
+ characterData: true,
6343
+ subtree: true,
6344
+ });
6345
+ }
6346
+
6347
+ #scheduleLightDomSync() {
6348
+ if (this.#syncRaf) return;
6349
+ this.#syncRaf = requestAnimationFrame(() => {
6350
+ this.#syncRaf = 0;
6351
+ if (!this.isConnected) return;
6352
+ this.#syncLightDom();
6353
+ });
6354
+ }
6355
+
6356
+ #syncLightDom() {
6357
+ if (!this.input || !this.contains(this.input)) {
6358
+ this.input = this.#ensureInputControl();
6359
+ this.input.addEventListener("change", this.#boundInputChange);
6360
+ this.input.addEventListener("input", this.#boundInput);
6361
+ this.input.addEventListener("focus", this.#boundFocus);
6362
+ this.input.addEventListener("blur", this.#boundBlur);
6363
+ this.input.addEventListener("keydown", this.#boundKeyDown);
6169
6364
  }
6170
- if (append) {
6171
- append.removeEventListener("click", this.#boundFocusControl);
6172
- append.addEventListener("click", this.#boundFocusControl);
6173
- this.append(append);
6365
+ const hasSteppers =
6366
+ this.hasAttribute("steppers") &&
6367
+ this.getAttribute("steppers") !== "false";
6368
+ if (this.#stepperEl && !this.contains(this.#stepperEl)) {
6369
+ this.#stepperEl = null;
6174
6370
  }
6175
-
6176
- return this.querySelector("input");
6371
+ this.#syncSteppers(hasSteppers);
6372
+ this.#syncInputA11yAttributes();
6373
+ this.#syncSpinbuttonAria();
6374
+ figNormalizeTextOnlyInputSlots(this);
6177
6375
  }
6178
6376
 
6179
6377
  #syncInputA11yAttributes() {
@@ -15106,6 +15304,7 @@ class FigChooser extends HTMLElement {
15106
15304
  }
15107
15305
  if (name === "columns") {
15108
15306
  this.#syncGridColumns();
15307
+ this.#resettleSelectedChoice();
15109
15308
  }
15110
15309
  if (name === "drag") {
15111
15310
  if (this.#dragEnabled) {
@@ -15116,10 +15315,16 @@ class FigChooser extends HTMLElement {
15116
15315
  }
15117
15316
  if (name === "overflow") {
15118
15317
  this.#applyOverflowMode();
15318
+ requestAnimationFrame(() => this.#syncOverflow());
15119
15319
  }
15120
15320
  if (name === "layout") {
15121
15321
  this.#applyOverflowMode();
15122
- requestAnimationFrame(() => this.#syncOverflow());
15322
+ if (newValue === "horizontal") {
15323
+ this.scrollTop = 0;
15324
+ } else {
15325
+ this.scrollLeft = 0;
15326
+ }
15327
+ this.#resettleSelectedChoice();
15123
15328
  }
15124
15329
  }
15125
15330
 
@@ -15265,6 +15470,7 @@ class FigChooser extends HTMLElement {
15265
15470
  this.#resizeObserver?.disconnect();
15266
15471
  this.#resizeObserver = new ResizeObserver(() => {
15267
15472
  this.#syncOverflow();
15473
+ this.#resettleSelectedChoice();
15268
15474
  });
15269
15475
  this.#resizeObserver.observe(this);
15270
15476
  }
@@ -15452,6 +15658,11 @@ class FigChooser extends HTMLElement {
15452
15658
  figScrollOverflowPage(this, isHorizontal ? "x" : "y", direction);
15453
15659
  }
15454
15660
 
15661
+ #resettleSelectedChoice() {
15662
+ if (!this.#selectedChoice) return;
15663
+ this.#scrollToChoice(this.#selectedChoice, "auto");
15664
+ }
15665
+
15455
15666
  #scrollToChoice(el, behavior = "smooth") {
15456
15667
  if (!el) return;
15457
15668
  requestAnimationFrame(() => {
@@ -15464,58 +15675,25 @@ class FigChooser extends HTMLElement {
15464
15675
  const hostRect = this.getBoundingClientRect();
15465
15676
  const options = { behavior };
15466
15677
  let shouldScroll = false;
15467
- const threshold = 2;
15468
15678
 
15469
15679
  if (overflowY) {
15470
- const fullyVisible =
15471
- choiceRect.top >= hostRect.top - 1 &&
15472
- choiceRect.bottom <= hostRect.bottom + 1;
15473
- const topVisible = choiceRect.top >= hostRect.top - 1;
15474
- const bottomVisible = choiceRect.bottom <= hostRect.bottom + 1;
15475
- const atScrollStart = this.scrollTop <= threshold;
15476
- const needsScroll =
15477
- !fullyVisible &&
15478
- (!topVisible ||
15479
- (!bottomVisible && !atScrollStart) ||
15480
- choiceRect.top >= hostRect.bottom - 1);
15481
- if (needsScroll) {
15482
- const choiceTop = choiceRect.top - hostRect.top + this.scrollTop;
15483
- const maxScroll = this.scrollHeight - this.clientHeight;
15484
- options.top = Math.max(
15485
- 0,
15486
- Math.min(
15487
- choiceTop + choiceRect.height / 2 - this.clientHeight / 2,
15488
- maxScroll,
15489
- ),
15490
- );
15491
- shouldScroll = true;
15492
- }
15680
+ const choiceTop = choiceRect.top - hostRect.top + this.scrollTop;
15681
+ const maxScroll = this.scrollHeight - this.clientHeight;
15682
+ options.top = Math.max(
15683
+ 0,
15684
+ Math.min(choiceTop + choiceRect.height / 2 - this.clientHeight / 2, maxScroll),
15685
+ );
15686
+ shouldScroll = true;
15493
15687
  }
15494
15688
 
15495
15689
  if (overflowX) {
15496
- const fullyVisible =
15497
- choiceRect.left >= hostRect.left - 1 &&
15498
- choiceRect.right <= hostRect.right + 1;
15499
- const startVisible = choiceRect.left >= hostRect.left - 1;
15500
- const endVisible = choiceRect.right <= hostRect.right + 1;
15501
- const atScrollStart = this.scrollLeft <= threshold;
15502
- const needsScroll =
15503
- !fullyVisible &&
15504
- (!startVisible ||
15505
- (!endVisible && !atScrollStart) ||
15506
- choiceRect.left >= hostRect.right - 1);
15507
- if (needsScroll) {
15508
- const choiceLeft = choiceRect.left - hostRect.left + this.scrollLeft;
15509
- const maxScroll = this.scrollWidth - this.clientWidth;
15510
- options.left = Math.max(
15511
- 0,
15512
- Math.min(
15513
- choiceLeft + choiceRect.width / 2 - this.clientWidth / 2,
15514
- maxScroll,
15515
- ),
15516
- );
15517
- shouldScroll = true;
15518
- }
15690
+ const choiceLeft = choiceRect.left - hostRect.left + this.scrollLeft;
15691
+ const maxScroll = this.scrollWidth - this.clientWidth;
15692
+ options.left = Math.max(
15693
+ 0,
15694
+ Math.min(choiceLeft + choiceRect.width / 2 - this.clientWidth / 2, maxScroll),
15695
+ );
15696
+ shouldScroll = true;
15519
15697
  }
15520
15698
 
15521
15699
  if (shouldScroll) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.8.6",
3
+ "version": "6.8.7",
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",