@rogieking/figui3 8.9.24 → 8.9.26

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
@@ -1706,6 +1706,7 @@ class FigFillPicker extends HTMLElement {
1706
1706
  #rafIds = new Set();
1707
1707
  #ownedBlobUrls = new Set();
1708
1708
  #videoPosterCapturing = null;
1709
+ #videoPosterSrc = null;
1709
1710
 
1710
1711
  constructor() {
1711
1712
  super();
@@ -1958,7 +1959,6 @@ class FigFillPicker extends HTMLElement {
1958
1959
  }
1959
1960
  this.#webcamPosterStream = stream;
1960
1961
  this.#updateSwatch();
1961
- this.#emitInput();
1962
1962
  };
1963
1963
 
1964
1964
  const attempt = async () => {
@@ -2135,8 +2135,12 @@ class FigFillPicker extends HTMLElement {
2135
2135
  const prevUrl = this.#video.url;
2136
2136
  this.#video = { ...this.#video, ...parsed.video };
2137
2137
  this.#video.missing = !this.#video.url;
2138
- if (this.#video.url !== prevUrl && parsed.video.poster == null) {
2138
+ if (parsed.video.poster != null && parsed.video.poster !== "") {
2139
+ this.#videoPosterSrc = this.#video.url;
2140
+ } else if (this.#video.url !== prevUrl) {
2139
2141
  this.#revokeVideoPoster();
2142
+ } else {
2143
+ this.#discardStaleVideoPoster();
2140
2144
  }
2141
2145
  }
2142
2146
  this.#applyParsedWebcam(parsed);
@@ -2158,6 +2162,7 @@ class FigFillPicker extends HTMLElement {
2158
2162
  this.#video.url = valueAttr.trim();
2159
2163
  this.#video.missing = false;
2160
2164
  if (this.#video.url !== prevUrl) this.#revokeVideoPoster();
2165
+ else this.#discardStaleVideoPoster();
2161
2166
  }
2162
2167
  }
2163
2168
  }
@@ -2258,9 +2263,9 @@ class FigFillPicker extends HTMLElement {
2258
2263
  this.#dialog.anchor = this.anchorElement || this.#trigger;
2259
2264
  }
2260
2265
 
2266
+ this.#switchTab(this.#fillType, { emit: false });
2261
2267
  this.#valueAtOpen = JSON.stringify(this.value);
2262
2268
  this.#lastChangeValue = this.#valueAtOpen;
2263
- this.#switchTab(this.#fillType, { emit: false });
2264
2269
 
2265
2270
  if (this.#swatch) this.#swatch.setAttribute("selected", "true");
2266
2271
 
@@ -2609,7 +2614,7 @@ class FigFillPicker extends HTMLElement {
2609
2614
  });
2610
2615
  }
2611
2616
 
2612
- if (tabName === "video") this.#applyDefaultVideo();
2617
+ if (tabName === "video") this.#applyDefaultVideo({ emit: false });
2613
2618
  if (tabName === "webcam") {
2614
2619
  this.#webcam.live = this.#webcamMode() === "live";
2615
2620
  this.#webcamStart?.(this.#webcam.deviceId);
@@ -4299,10 +4304,17 @@ class FigFillPicker extends HTMLElement {
4299
4304
  preview.addEventListener("loaded", (e) => {
4300
4305
  const src = e.detail?.src || preview.src;
4301
4306
  if (!src) return;
4307
+ const changed = src !== this.#image.url;
4302
4308
  this.#image.url = src;
4303
4309
  this.#updateImagePreview(preview);
4304
4310
  this.#updateSwatch();
4305
- this.#emitInput();
4311
+ if (changed) this.#emitInput();
4312
+ this.dispatchEvent(
4313
+ new CustomEvent("loaded", {
4314
+ bubbles: true,
4315
+ detail: e.detail ?? { src },
4316
+ }),
4317
+ );
4306
4318
  });
4307
4319
 
4308
4320
  preview.addEventListener("change", () => {
@@ -4462,7 +4474,7 @@ class FigFillPicker extends HTMLElement {
4462
4474
  container.replaceChildren(header, preview);
4463
4475
 
4464
4476
  this.#setupVideoEvents(container);
4465
- this.#applyDefaultVideo();
4477
+ this.#applyDefaultVideo({ emit: false });
4466
4478
  }
4467
4479
 
4468
4480
  #revokeVideoPoster() {
@@ -4471,12 +4483,28 @@ class FigFillPicker extends HTMLElement {
4471
4483
  this.#ownedBlobUrls.delete(this.#video.poster);
4472
4484
  }
4473
4485
  this.#video.poster = null;
4486
+ this.#videoPosterSrc = null;
4487
+ }
4488
+
4489
+ #discardStaleVideoPoster() {
4490
+ if (!this.#video.poster) return;
4491
+ if (this.#videoPosterSrc && this.#videoPosterSrc === this.#video.url) return;
4492
+ this.#revokeVideoPoster();
4493
+ }
4494
+
4495
+ #syncVideoPreview() {
4496
+ const preview = this.#dialog?.querySelector(
4497
+ ".fig-fill-picker-video-preview",
4498
+ );
4499
+ if (preview) this.#updateVideoPreviewStyle(preview);
4474
4500
  }
4475
4501
 
4476
- #applyDefaultVideo({ emit = true } = {}) {
4502
+ #applyDefaultVideo({ emit = false } = {}) {
4477
4503
  if (this.#video.url) {
4478
4504
  this.#video.missing = false;
4505
+ this.#discardStaleVideoPoster();
4479
4506
  if (!this.#video.poster) this.#captureVideoPoster(this.#video.url);
4507
+ else this.#syncVideoPreview();
4480
4508
  return;
4481
4509
  }
4482
4510
  const fallback = this.getAttribute("default-video");
@@ -4486,10 +4514,7 @@ class FigFillPicker extends HTMLElement {
4486
4514
  }
4487
4515
  this.#video.url = fallback;
4488
4516
  this.#video.missing = false;
4489
- const preview = this.#dialog?.querySelector(
4490
- ".fig-fill-picker-video-preview",
4491
- );
4492
- if (preview) this.#updateVideoPreviewStyle(preview);
4517
+ this.#syncVideoPreview();
4493
4518
  this.#captureVideoPoster(fallback);
4494
4519
  if (emit) this.#emitInput();
4495
4520
  }
@@ -4497,7 +4522,9 @@ class FigFillPicker extends HTMLElement {
4497
4522
  #ensureVideoPoster() {
4498
4523
  if (this.#fillType !== "video") return;
4499
4524
  const src = this.#video.url;
4500
- if (!src || this.#video.poster) return;
4525
+ if (!src) return;
4526
+ this.#discardStaleVideoPoster();
4527
+ if (this.#video.poster) return;
4501
4528
  this.#captureVideoPoster(src);
4502
4529
  }
4503
4530
 
@@ -4572,8 +4599,10 @@ class FigFillPicker extends HTMLElement {
4572
4599
  if (!blob || this.#video.url !== src) return;
4573
4600
  this.#revokeVideoPoster();
4574
4601
  this.#video.poster = URL.createObjectURL(blob);
4602
+ this.#videoPosterSrc = src;
4575
4603
  this.#ownedBlobUrls.add(this.#video.poster);
4576
4604
  this.#updateSwatch();
4605
+ this.#syncVideoPreview();
4577
4606
  if (emit) this.#emitInput();
4578
4607
  } catch {
4579
4608
  // Cross-origin or decode failures leave the swatch empty until a poster exists.
@@ -4603,12 +4632,20 @@ class FigFillPicker extends HTMLElement {
4603
4632
  preview.addEventListener("loaded", (e) => {
4604
4633
  const src = e.detail?.src || preview.src;
4605
4634
  if (!src) return;
4635
+ const changed = src !== this.#video.url;
4636
+ if (changed) this.#revokeVideoPoster();
4606
4637
  this.#video.url = src;
4607
4638
  this.#video.missing = false;
4608
4639
  this.#updateVideoPreviewStyle(preview);
4609
4640
  this.#captureVideoPoster(src);
4610
4641
  this.#updateSwatch();
4611
- this.#emitInput();
4642
+ if (changed) this.#emitInput();
4643
+ this.dispatchEvent(
4644
+ new CustomEvent("loaded", {
4645
+ bubbles: true,
4646
+ detail: e.detail ?? { src },
4647
+ }),
4648
+ );
4612
4649
  });
4613
4650
 
4614
4651
  preview.addEventListener("change", () => {
package/fig.js CHANGED
@@ -9476,6 +9476,16 @@ class FigInputFill extends HTMLElement {
9476
9476
  this.#emitChange();
9477
9477
  });
9478
9478
 
9479
+ this.#fillPicker.addEventListener("loaded", (e) => {
9480
+ e.stopPropagation();
9481
+ this.dispatchEvent(
9482
+ new CustomEvent("loaded", {
9483
+ bubbles: true,
9484
+ detail: e.detail,
9485
+ }),
9486
+ );
9487
+ });
9488
+
9479
9489
  this.#fillPicker.addEventListener("webcamstream", (e) => {
9480
9490
  this.dispatchEvent(
9481
9491
  new CustomEvent("webcamstream", {
@@ -9773,7 +9783,10 @@ class FigInputFill extends HTMLElement {
9773
9783
  #webcamValue() {
9774
9784
  return {
9775
9785
  live: this.#webcam.live !== false,
9776
- snapshot: this.#webcam.snapshot ?? null,
9786
+ snapshot:
9787
+ this.#webcam.snapshot ??
9788
+ this.#fillPicker?.value?.webcam?.snapshot ??
9789
+ null,
9777
9790
  deviceId: this.#webcam.deviceId ?? null,
9778
9791
  scaleMode: this.#webcam.scaleMode || "fill",
9779
9792
  scale: this.#webcam.scale ?? 50,
@@ -12992,6 +13005,23 @@ class FigMedia extends HTMLElement {
12992
13005
  detail,
12993
13006
  }),
12994
13007
  );
13008
+ if (
13009
+ (media.tagName === "IMG" && event.type === "load") ||
13010
+ (media.tagName === "VIDEO" && event.type === "loadeddata")
13011
+ ) {
13012
+ this.dispatchEvent(
13013
+ new CustomEvent("loaded", {
13014
+ bubbles: true,
13015
+ cancelable: true,
13016
+ composed: true,
13017
+ detail: {
13018
+ src: detail.src,
13019
+ file: this.#file,
13020
+ media,
13021
+ },
13022
+ }),
13023
+ );
13024
+ }
12995
13025
  }
12996
13026
 
12997
13027
  #handleMediaPlay() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.24",
3
+ "version": "8.9.26",
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",