@rogieking/figui3 6.5.1 → 6.6.1

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
@@ -3319,7 +3319,14 @@ class FigTabs extends HTMLElement {
3319
3319
  #ensureScroller() {
3320
3320
  if (this.#isStructuring) return;
3321
3321
  const existing = this.querySelector(":scope > [data-fig-tabs-scroll]");
3322
- if (existing && existing === this.#scroller) return;
3322
+ const directNodes = existing
3323
+ ? Array.from(this.childNodes).filter((node) => {
3324
+ if (node === existing) return false;
3325
+ if (node.nodeType !== Node.ELEMENT_NODE) return true;
3326
+ return !node.hasAttribute("data-fig-tabs-nav");
3327
+ })
3328
+ : null;
3329
+ if (existing && existing === this.#scroller && !directNodes?.length) return;
3323
3330
 
3324
3331
  this.#isStructuring = true;
3325
3332
  try {
@@ -3332,11 +3339,13 @@ class FigTabs extends HTMLElement {
3332
3339
  scroller.className = "fig-tabs-scroll";
3333
3340
  scroller.dataset.figTabsScroll = "";
3334
3341
 
3335
- const nodes = Array.from(this.childNodes).filter((node) => {
3336
- if (node === scroller) return false;
3337
- if (node.nodeType !== Node.ELEMENT_NODE) return true;
3338
- return !node.hasAttribute("data-fig-tabs-nav");
3339
- });
3342
+ const nodes =
3343
+ directNodes ??
3344
+ Array.from(this.childNodes).filter((node) => {
3345
+ if (node === scroller) return false;
3346
+ if (node.nodeType !== Node.ELEMENT_NODE) return true;
3347
+ return !node.hasAttribute("data-fig-tabs-nav");
3348
+ });
3340
3349
 
3341
3350
  for (const node of nodes) {
3342
3351
  scroller.appendChild(node);
@@ -3444,26 +3453,45 @@ class FigTabs extends HTMLElement {
3444
3453
  this.querySelectorAll("fig-tab").forEach((tab) => tab.removeAttribute("selected"));
3445
3454
  this.selectedTab = tabs[newIndex];
3446
3455
  tabs[newIndex].setAttribute("selected", "true");
3447
- const val = tabs[newIndex].getAttribute("value");
3456
+ const val = this.#resolveTabValue(tabs[newIndex]);
3448
3457
  if (val) this.setAttribute("value", val);
3458
+ else this.removeAttribute("value");
3449
3459
  tabs[newIndex].focus();
3450
3460
  this.#syncTabIndexes();
3451
3461
  this.#scrollSelectedTabIntoView(tabs[newIndex]);
3462
+ this.#emitSelectionEvents();
3452
3463
  }
3453
3464
  }
3454
3465
 
3455
3466
  get value() {
3456
- return this.selectedTab?.getAttribute("value") || "";
3467
+ return this.#resolveTabValue(this.selectedTab);
3457
3468
  }
3458
3469
 
3459
3470
  set value(val) {
3460
3471
  this.setAttribute("value", val);
3461
3472
  }
3462
3473
 
3474
+ #emitSelectionEvents() {
3475
+ const val = this.value;
3476
+ this.dispatchEvent(
3477
+ new CustomEvent("input", { detail: val, bubbles: true }),
3478
+ );
3479
+ this.dispatchEvent(
3480
+ new CustomEvent("change", { detail: val, bubbles: true }),
3481
+ );
3482
+ }
3483
+
3484
+ #resolveTabValue(tab) {
3485
+ if (!tab) return "";
3486
+ const attrValue = tab.getAttribute("value");
3487
+ if (attrValue !== null) return attrValue;
3488
+ return tab.textContent?.trim() || "";
3489
+ }
3490
+
3463
3491
  #selectByValue(value) {
3464
3492
  const tabs = this.querySelectorAll("fig-tab");
3465
3493
  for (const tab of tabs) {
3466
- if (tab.getAttribute("value") === value) {
3494
+ if (this.#resolveTabValue(tab) === value) {
3467
3495
  this.selectedTab = tab;
3468
3496
  tab.setAttribute("selected", "true");
3469
3497
  } else {
@@ -3491,6 +3519,8 @@ class FigTabs extends HTMLElement {
3491
3519
  if (this.hasAttribute("disabled")) return;
3492
3520
  const target = event.target.closest("fig-tab");
3493
3521
  if (!target || !this.contains(target)) return;
3522
+ const previousTab = this.selectedTab;
3523
+ const previousValue = this.value;
3494
3524
  const tabs = this.querySelectorAll("fig-tab");
3495
3525
  for (const tab of tabs) {
3496
3526
  if (tab === target) {
@@ -3500,10 +3530,14 @@ class FigTabs extends HTMLElement {
3500
3530
  tab.removeAttribute("selected");
3501
3531
  }
3502
3532
  }
3503
- const val = target.getAttribute("value");
3533
+ const val = this.#resolveTabValue(target);
3504
3534
  if (val) this.setAttribute("value", val);
3535
+ else this.removeAttribute("value");
3505
3536
  this.#syncTabIndexes();
3506
3537
  this.#scrollSelectedTabIntoView(target);
3538
+ if (previousTab !== target || previousValue !== this.value) {
3539
+ this.#emitSelectionEvents();
3540
+ }
3507
3541
  }
3508
3542
  }
3509
3543
  customElements.define("fig-tabs", FigTabs);
@@ -14237,6 +14271,7 @@ customElements.define("fig-choice", FigChoice);
14237
14271
  * A selection controller for a list of choice elements.
14238
14272
  * @attr {string} choice-element - CSS selector for child choices (default: "fig-choice")
14239
14273
  * @attr {string} layout - Layout mode: "vertical" (default), "horizontal", "grid"
14274
+ * @attr {number} columns - Number of columns when layout="grid" (default: 2)
14240
14275
  * @attr {string} value - Value of the currently selected choice
14241
14276
  * @attr {boolean} disabled - Whether the chooser is disabled
14242
14277
  */
@@ -14262,7 +14297,9 @@ class FigChooser extends HTMLElement {
14262
14297
  "value",
14263
14298
  "disabled",
14264
14299
  "choice-element",
14300
+ "columns",
14265
14301
  "drag",
14302
+ "layout",
14266
14303
  "overflow",
14267
14304
  "loop",
14268
14305
  ];
@@ -14283,6 +14320,16 @@ class FigChooser extends HTMLElement {
14283
14320
  return this.getAttribute("choice-element") || "fig-choice";
14284
14321
  }
14285
14322
 
14323
+ #syncGridColumns() {
14324
+ const raw = this.getAttribute("columns");
14325
+ const columns = raw === null ? NaN : Number(raw);
14326
+ if (Number.isInteger(columns) && columns > 0) {
14327
+ this.style.setProperty("--fig-chooser-grid-columns", String(columns));
14328
+ } else {
14329
+ this.style.removeProperty("--fig-chooser-grid-columns");
14330
+ }
14331
+ }
14332
+
14286
14333
  get choices() {
14287
14334
  return Array.from((this.#scroller || this).querySelectorAll(this.#choiceSelector));
14288
14335
  }
@@ -14360,6 +14407,7 @@ class FigChooser extends HTMLElement {
14360
14407
 
14361
14408
  connectedCallback() {
14362
14409
  this.setAttribute("role", "listbox");
14410
+ this.#syncGridColumns();
14363
14411
  this.#ensureScroller();
14364
14412
  this.addEventListener("click", this.#boundHandleClick);
14365
14413
  this.addEventListener("keydown", this.#boundHandleKeyDown);
@@ -14436,6 +14484,9 @@ class FigChooser extends HTMLElement {
14436
14484
  if (name === "choice-element") {
14437
14485
  requestAnimationFrame(() => this.#syncSelection());
14438
14486
  }
14487
+ if (name === "columns") {
14488
+ this.#syncGridColumns();
14489
+ }
14439
14490
  if (name === "drag") {
14440
14491
  if (this.#dragEnabled) {
14441
14492
  this.#setupDrag();
@@ -14446,6 +14497,10 @@ class FigChooser extends HTMLElement {
14446
14497
  if (name === "overflow") {
14447
14498
  this.#applyOverflowMode();
14448
14499
  }
14500
+ if (name === "layout") {
14501
+ this.#applyOverflowMode();
14502
+ requestAnimationFrame(() => this.#syncOverflow());
14503
+ }
14449
14504
  }
14450
14505
 
14451
14506
  #syncSelection() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.5.1",
3
+ "version": "6.6.1",
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",