@rogieking/figui3 10.0.2 → 10.0.3

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
@@ -13126,6 +13126,7 @@ const FIG_MEDIA_FORWARDED_EVENTS = [
13126
13126
  * @attr {string} src - Media source URL
13127
13127
  * @attr {string} type - "image" (default) or "video" (for fig-media)
13128
13128
  * @attr {string} alt - Alt text for the generated image (default "")
13129
+ * @attr {boolean} loaded - Read-only reflected state when an image has loaded successfully
13129
13130
  * @attr {boolean} upload - Show upload overlay (generates fig-input-file)
13130
13131
  * @attr {boolean} loading-indicator - Set to `false` to disable the generated image loading spinner
13131
13132
  * @attr {string} label - Upload button label (default "Upload")
@@ -13344,6 +13345,7 @@ class FigMedia extends HTMLElement {
13344
13345
  }
13345
13346
 
13346
13347
  #syncImageLoadingState() {
13348
+ this.#syncImageLoadedState();
13347
13349
  const src = this.#currentMediaSrc();
13348
13350
  if (!src || !this.#usesDefaultImageLoadingIndicator()) {
13349
13351
  this.#finishImageLoading();
@@ -13383,6 +13385,18 @@ class FigMedia extends HTMLElement {
13383
13385
  }, 150);
13384
13386
  }
13385
13387
 
13388
+ #syncImageLoadedState() {
13389
+ const image = this.#mediaEl?.tagName === "IMG" ? this.#mediaEl : null;
13390
+ const src =
13391
+ image?.currentSrc ||
13392
+ image?.getAttribute("src") ||
13393
+ this.#currentMediaSrc();
13394
+ this.toggleAttribute(
13395
+ "loaded",
13396
+ Boolean(src && image?.complete && image.naturalWidth > 0),
13397
+ );
13398
+ }
13399
+
13386
13400
  #finishImageLoading() {
13387
13401
  if (this.#loadingTimer !== null) {
13388
13402
  clearTimeout(this.#loadingTimer);
@@ -13553,6 +13567,7 @@ class FigMedia extends HTMLElement {
13553
13567
  if (!this.#mediaEl.hasAttribute("data-generated")) return;
13554
13568
  const src = this.#currentMediaSrc();
13555
13569
  if (this.#mediaEl.getAttribute("src") !== src) {
13570
+ this.removeAttribute("loaded");
13556
13571
  if (src) {
13557
13572
  this.#mediaEl.setAttribute("src", src);
13558
13573
  } else {
@@ -13880,6 +13895,7 @@ class FigMedia extends HTMLElement {
13880
13895
  media.tagName === "IMG" &&
13881
13896
  (event.type === "load" || event.type === "error")
13882
13897
  ) {
13898
+ this.toggleAttribute("loaded", event.type === "load");
13883
13899
  this.#finishImageLoading();
13884
13900
  }
13885
13901
  const detail = {
@@ -19788,6 +19804,8 @@ figDefineElement("fig-color-tip", FigColorTip);
19788
19804
  * @attr {boolean} disabled - Whether this choice is disabled
19789
19805
  */
19790
19806
  class FigChoice extends HTMLElement {
19807
+ #childObserver = null;
19808
+
19791
19809
  static get observedAttributes() {
19792
19810
  return ["selected", "disabled"];
19793
19811
  }
@@ -19797,21 +19815,34 @@ class FigChoice extends HTMLElement {
19797
19815
  if (!this.hasAttribute("tabindex")) {
19798
19816
  this.setAttribute("tabindex", "0");
19799
19817
  }
19800
- this.setAttribute(
19801
- "aria-selected",
19802
- figBooleanAttribute(this, "selected") ? "true" : "false",
19803
- );
19818
+ this.#syncSelectedState();
19804
19819
  if (figBooleanAttribute(this, "disabled")) {
19805
19820
  this.setAttribute("aria-disabled", "true");
19806
19821
  }
19807
19822
  }
19808
19823
 
19824
+ disconnectedCallback() {
19825
+ this.#childObserver?.disconnect();
19826
+ }
19827
+
19828
+ #syncSelectedState() {
19829
+ const selected = figBooleanAttribute(this, "selected");
19830
+ this.setAttribute("aria-selected", selected ? "true" : "false");
19831
+ for (const child of this.children) {
19832
+ child.toggleAttribute("selected", selected);
19833
+ }
19834
+ this.#childObserver?.disconnect();
19835
+ if (selected && this.isConnected) {
19836
+ this.#childObserver ??= new MutationObserver(() =>
19837
+ this.#syncSelectedState(),
19838
+ );
19839
+ this.#childObserver.observe(this, { childList: true });
19840
+ }
19841
+ }
19842
+
19809
19843
  attributeChangedCallback(name) {
19810
19844
  if (name === "selected") {
19811
- this.setAttribute(
19812
- "aria-selected",
19813
- figBooleanAttribute(this, "selected") ? "true" : "false",
19814
- );
19845
+ this.#syncSelectedState();
19815
19846
  }
19816
19847
  if (name === "disabled") {
19817
19848
  const isDisabled =
@@ -19914,19 +19945,11 @@ class FigChooser extends HTMLElement {
19914
19945
  const raw = this.getAttribute("columns");
19915
19946
  const columns = raw === null ? NaN : Number(raw);
19916
19947
  const validColumns = Number.isInteger(columns) && columns > 0;
19917
- const effectiveColumns = validColumns ? columns : 2;
19918
19948
  if (validColumns) {
19919
- this.style.setProperty("--fig-chooser-grid-columns", String(effectiveColumns));
19949
+ this.style.setProperty("--fig-chooser-grid-columns", String(columns));
19920
19950
  } else {
19921
19951
  this.style.removeProperty("--fig-chooser-grid-columns");
19922
19952
  }
19923
- const isGrid = this.getAttribute("layout") === "grid";
19924
- this.choices.forEach((choice, index) => {
19925
- choice.toggleAttribute(
19926
- "data-fig-chooser-first-row",
19927
- isGrid && index < effectiveColumns,
19928
- );
19929
- });
19930
19953
  }
19931
19954
 
19932
19955
  get choices() {
@@ -20176,7 +20199,8 @@ class FigChooser extends HTMLElement {
20176
20199
  !c.hasAttribute("disabled") || c.getAttribute("disabled") === "false",
20177
20200
  );
20178
20201
  if (!choices.length) return;
20179
- const currentIndex = choices.indexOf(this.#selectedChoice);
20202
+ const focusedChoice = this.#findChoiceFromTarget(event.target);
20203
+ const currentIndex = choices.indexOf(focusedChoice);
20180
20204
  let nextIndex = currentIndex;
20181
20205
 
20182
20206
  const loop = this.hasAttribute("loop");
@@ -20211,12 +20235,9 @@ class FigChooser extends HTMLElement {
20211
20235
  case "Enter":
20212
20236
  case " ":
20213
20237
  event.preventDefault();
20214
- if (document.activeElement?.matches(this.#choiceSelector)) {
20215
- const focused = this.#findChoiceFromTarget(document.activeElement);
20216
- if (focused && focused !== this.#selectedChoice) {
20217
- this.selectedChoice = focused;
20218
- this.#emitEvents();
20219
- }
20238
+ if (focusedChoice && focusedChoice !== this.#selectedChoice) {
20239
+ this.selectedChoice = focusedChoice;
20240
+ this.#emitEvents();
20220
20241
  }
20221
20242
  return;
20222
20243
  default:
@@ -20224,9 +20245,9 @@ class FigChooser extends HTMLElement {
20224
20245
  }
20225
20246
 
20226
20247
  if (nextIndex !== currentIndex && choices[nextIndex]) {
20227
- this.selectedChoice = choices[nextIndex];
20228
- choices[nextIndex].focus({ preventScroll: !this.autoScroll });
20229
- this.#emitEvents();
20248
+ const nextChoice = choices[nextIndex];
20249
+ nextChoice.focus({ preventScroll: true });
20250
+ if (this.autoScroll) this.#scrollToChoice(nextChoice);
20230
20251
  }
20231
20252
  }
20232
20253
 
@@ -20397,12 +20418,14 @@ class FigChooser extends HTMLElement {
20397
20418
  }
20398
20419
 
20399
20420
  #applyOverflowMode() {
20400
- if (this.#overflowMode === "scrollbar") {
20421
+ const usesScrollbar = this.#overflowMode === "scrollbar";
20422
+ const isGrid = this.getAttribute("layout") === "grid";
20423
+ if (usesScrollbar || isGrid) {
20401
20424
  this.#removeNavButtons();
20402
- this.classList.add("fig-overflow-fade");
20425
+ this.classList.toggle("fig-overflow-fade", usesScrollbar);
20403
20426
  this.classList.toggle(
20404
20427
  "fig-overflow-fade-horizontal",
20405
- this.getAttribute("layout") === "horizontal",
20428
+ usesScrollbar && this.getAttribute("layout") === "horizontal",
20406
20429
  );
20407
20430
  } else {
20408
20431
  this.#createNavButtons();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "10.0.2",
3
+ "version": "10.0.3",
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",