@rogieking/figui3 8.9.30 → 8.9.32

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
@@ -407,14 +407,19 @@ function figSyncOverflowState(host, scrollEl, axis = "x", threshold = 2) {
407
407
  return scrollable;
408
408
  }
409
409
 
410
- function figScrollOverflowPage(scrollEl, axis = "x", direction = 1) {
410
+ function figScrollOverflowPage(
411
+ scrollEl,
412
+ axis = "x",
413
+ direction = 1,
414
+ behavior = "smooth",
415
+ ) {
411
416
  if (!scrollEl) return;
412
417
  const isHorizontal = axis === "x";
413
418
  const pageSize = isHorizontal ? scrollEl.clientWidth : scrollEl.clientHeight;
414
419
  const scrollAmount = pageSize * 0.8 * direction;
415
420
  scrollEl.scrollBy({
416
421
  [isHorizontal ? "left" : "top"]: scrollAmount,
417
- behavior: "smooth",
422
+ behavior,
418
423
  });
419
424
  }
420
425
 
@@ -17687,6 +17692,8 @@ figDefineElement("fig-choice", FigChoice);
17687
17692
  * @attr {number} columns - Number of columns when layout="grid" (default: 2)
17688
17693
  * @attr {string} value - Selected choice value. Omit to select the first choice; `value=""` means none.
17689
17694
  * @attr {boolean} disabled - Whether the chooser is disabled
17695
+ * @attr {boolean} auto-scroll - Whether selection changes automatically scroll into view (default: true)
17696
+ * @attr {"auto"|"smooth"} scroll-behavior - Scrolling animation behavior (default: "smooth")
17690
17697
  */
17691
17698
  class FigChooser extends HTMLElement {
17692
17699
  #selectedChoice = null;
@@ -17714,6 +17721,7 @@ class FigChooser extends HTMLElement {
17714
17721
  "layout",
17715
17722
  "overflow",
17716
17723
  "loop",
17724
+ "auto-scroll",
17717
17725
  ];
17718
17726
  }
17719
17727
 
@@ -17728,6 +17736,31 @@ class FigChooser extends HTMLElement {
17728
17736
  return attr === null || attr !== "false";
17729
17737
  }
17730
17738
 
17739
+ get autoScroll() {
17740
+ const attr = this.getAttribute("auto-scroll");
17741
+ return attr === null || attr !== "false";
17742
+ }
17743
+
17744
+ set autoScroll(value) {
17745
+ if (value === false || value === "false" || value === null) {
17746
+ this.setAttribute("auto-scroll", "false");
17747
+ } else {
17748
+ this.removeAttribute("auto-scroll");
17749
+ }
17750
+ }
17751
+
17752
+ get scrollBehavior() {
17753
+ return this.getAttribute("scroll-behavior") === "auto" ? "auto" : "smooth";
17754
+ }
17755
+
17756
+ set scrollBehavior(value) {
17757
+ if (value === "auto" || value === "smooth") {
17758
+ this.setAttribute("scroll-behavior", value);
17759
+ } else {
17760
+ this.removeAttribute("scroll-behavior");
17761
+ }
17762
+ }
17763
+
17731
17764
  get #choiceSelector() {
17732
17765
  return this.getAttribute("choice-element") || "fig-choice";
17733
17766
  }
@@ -17767,7 +17800,7 @@ class FigChooser extends HTMLElement {
17767
17800
  if (this.getAttribute("value") !== val) {
17768
17801
  this.setAttribute("value", val);
17769
17802
  }
17770
- this.#scrollToChoice(element);
17803
+ if (this.autoScroll) this.#scrollToChoice(element);
17771
17804
  }
17772
17805
 
17773
17806
  get value() {
@@ -17782,6 +17815,11 @@ class FigChooser extends HTMLElement {
17782
17815
  this.setAttribute("value", String(val));
17783
17816
  }
17784
17817
 
17818
+ scrollSelectionIntoView(options = {}) {
17819
+ if (!this.#selectedChoice) return;
17820
+ this.#scrollToChoice(this.#selectedChoice, options);
17821
+ }
17822
+
17785
17823
  connectedCallback() {
17786
17824
  this.setAttribute("role", "listbox");
17787
17825
  if (this.shadowRoot) this.shadowRoot.replaceChildren();
@@ -17805,9 +17843,9 @@ class FigChooser extends HTMLElement {
17805
17843
 
17806
17844
  #scheduleInitialScrollSettle() {
17807
17845
  const resettle = () => {
17808
- if (!this.isConnected) return;
17846
+ if (!this.isConnected || !this.autoScroll) return;
17809
17847
  if (this.#selectedChoice) {
17810
- this.#scrollToChoice(this.#selectedChoice, "auto");
17848
+ this.#scrollToChoice(this.#selectedChoice);
17811
17849
  }
17812
17850
  };
17813
17851
  const wireImages = () => {
@@ -17883,6 +17921,9 @@ class FigChooser extends HTMLElement {
17883
17921
  }
17884
17922
  this.#resettleSelectedChoice();
17885
17923
  }
17924
+ if (name === "auto-scroll" && this.autoScroll) {
17925
+ this.#resettleSelectedChoice();
17926
+ }
17886
17927
  }
17887
17928
 
17888
17929
  #syncSelection() {
@@ -18026,7 +18067,7 @@ class FigChooser extends HTMLElement {
18026
18067
 
18027
18068
  if (nextIndex !== currentIndex && choices[nextIndex]) {
18028
18069
  this.selectedChoice = choices[nextIndex];
18029
- choices[nextIndex].focus();
18070
+ choices[nextIndex].focus({ preventScroll: !this.autoScroll });
18030
18071
  this.#emitEvents();
18031
18072
  }
18032
18073
  }
@@ -18236,16 +18277,25 @@ class FigChooser extends HTMLElement {
18236
18277
 
18237
18278
  #scrollByPage(direction) {
18238
18279
  const isHorizontal = this.getAttribute("layout") === "horizontal";
18239
- figScrollOverflowPage(this, isHorizontal ? "x" : "y", direction);
18280
+ figScrollOverflowPage(this, isHorizontal ? "x" : "y", direction, "auto");
18240
18281
  }
18241
18282
 
18242
18283
  #resettleSelectedChoice() {
18243
- if (!this.#selectedChoice) return;
18244
- this.#scrollToChoice(this.#selectedChoice, "auto");
18284
+ if (!this.autoScroll || !this.#selectedChoice) return;
18285
+ this.#scrollToChoice(this.#selectedChoice);
18245
18286
  }
18246
18287
 
18247
- #scrollToChoice(el, behavior = "smooth") {
18288
+ #scrollToChoice(el, options = {}) {
18248
18289
  if (!el) return;
18290
+ const scrollOptions =
18291
+ typeof options === "object" && options !== null ? options : {};
18292
+ const behavior =
18293
+ scrollOptions.behavior === "smooth" ||
18294
+ scrollOptions.behavior === "instant"
18295
+ ? scrollOptions.behavior
18296
+ : "auto";
18297
+ const block = scrollOptions.block || "center";
18298
+ const inline = scrollOptions.inline || "center";
18249
18299
  requestAnimationFrame(() => {
18250
18300
  if (!el.isConnected) return;
18251
18301
  const overflowY = this.scrollHeight > this.clientHeight;
@@ -18260,9 +18310,13 @@ class FigChooser extends HTMLElement {
18260
18310
  if (overflowY) {
18261
18311
  const choiceTop = choiceRect.top - hostRect.top + this.scrollTop;
18262
18312
  const maxScroll = this.scrollHeight - this.clientHeight;
18263
- options.top = Math.max(
18264
- 0,
18265
- Math.min(choiceTop + choiceRect.height / 2 - this.clientHeight / 2, maxScroll),
18313
+ options.top = this.#alignedScrollPosition(
18314
+ choiceTop,
18315
+ choiceRect.height,
18316
+ this.clientHeight,
18317
+ this.scrollTop,
18318
+ maxScroll,
18319
+ block,
18266
18320
  );
18267
18321
  shouldScroll = true;
18268
18322
  }
@@ -18270,9 +18324,13 @@ class FigChooser extends HTMLElement {
18270
18324
  if (overflowX) {
18271
18325
  const choiceLeft = choiceRect.left - hostRect.left + this.scrollLeft;
18272
18326
  const maxScroll = this.scrollWidth - this.clientWidth;
18273
- options.left = Math.max(
18274
- 0,
18275
- Math.min(choiceLeft + choiceRect.width / 2 - this.clientWidth / 2, maxScroll),
18327
+ options.left = this.#alignedScrollPosition(
18328
+ choiceLeft,
18329
+ choiceRect.width,
18330
+ this.clientWidth,
18331
+ this.scrollLeft,
18332
+ maxScroll,
18333
+ inline,
18276
18334
  );
18277
18335
  shouldScroll = true;
18278
18336
  }
@@ -18284,6 +18342,36 @@ class FigChooser extends HTMLElement {
18284
18342
  });
18285
18343
  }
18286
18344
 
18345
+ #alignedScrollPosition(
18346
+ elementStart,
18347
+ elementSize,
18348
+ viewportSize,
18349
+ currentScroll,
18350
+ maxScroll,
18351
+ alignment,
18352
+ ) {
18353
+ let nextScroll;
18354
+ if (alignment === "start") {
18355
+ nextScroll = elementStart;
18356
+ } else if (alignment === "end") {
18357
+ nextScroll = elementStart + elementSize - viewportSize;
18358
+ } else if (alignment === "nearest") {
18359
+ const viewportStart = currentScroll;
18360
+ const viewportEnd = currentScroll + viewportSize;
18361
+ const elementEnd = elementStart + elementSize;
18362
+ if (elementStart >= viewportStart && elementEnd <= viewportEnd) {
18363
+ nextScroll = currentScroll;
18364
+ } else if (elementStart < viewportStart) {
18365
+ nextScroll = elementStart;
18366
+ } else {
18367
+ nextScroll = elementEnd - viewportSize;
18368
+ }
18369
+ } else {
18370
+ nextScroll = elementStart + elementSize / 2 - viewportSize / 2;
18371
+ }
18372
+ return Math.max(0, Math.min(nextScroll, maxScroll));
18373
+ }
18374
+
18287
18375
  #startObserver() {
18288
18376
  this.#mutationObserver?.disconnect();
18289
18377
  this.#mutationObserver = new MutationObserver(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.30",
3
+ "version": "8.9.32",
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",