@rogieking/figui3 8.9.18 → 8.9.19

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.css CHANGED
@@ -174,8 +174,10 @@ fig-fill-picker {
174
174
  display: block;
175
175
  }
176
176
  }
177
- fig-preview{
178
- overflow: visible;
177
+ .fig-fill-picker-tab:is([data-tab="solid"], [data-tab="gradient"]) {
178
+ fig-preview {
179
+ overflow: visible;
180
+ }
179
181
  }
180
182
 
181
183
  .fig-fill-picker-color-area {
package/fig-editor.js CHANGED
@@ -2078,9 +2078,13 @@ class FigFillPicker extends HTMLElement {
2078
2078
  bg = "";
2079
2079
  }
2080
2080
  break;
2081
- default:
2082
- const slot = this.#customSlots[this.#fillType];
2083
- bg = slot?.element?.getAttribute("swatch-background") || "#D9D9D9";
2081
+ default: {
2082
+ const slot =
2083
+ this.#customSlots[this.#fillType]?.element ||
2084
+ this.querySelector(`[slot="mode-${this.#fillType}"]`);
2085
+ bg = slot?.getAttribute("swatch-background") || "";
2086
+ break;
2087
+ }
2084
2088
  }
2085
2089
 
2086
2090
  this.#swatch.setAttribute("background", bg);
@@ -4573,11 +4577,16 @@ class FigFillPicker extends HTMLElement {
4573
4577
  existing?.getTracks?.().filter((track) => track.readyState !== "ended") ??
4574
4578
  [];
4575
4579
  if (existing && liveTracks.length) {
4576
- const currentId = this.#webcam.deviceId;
4577
- if (!requested || !currentId || requested === currentId) {
4580
+ const liveId =
4581
+ existing.getVideoTracks?.()?.[0]?.getSettings?.()?.deviceId ||
4582
+ this.#webcam.deviceId ||
4583
+ null;
4584
+ // Reuse the live stream only when it is already the requested device.
4585
+ // An unknown liveId is not a match — otherwise the camera select cannot switch.
4586
+ if (!requested || requested === liveId) {
4578
4587
  attachPreview(existing);
4579
4588
  this.#emitWebcamStream();
4580
- populateCameras(null, requested || currentId);
4589
+ populateCameras(null, requested || liveId);
4581
4590
  return;
4582
4591
  }
4583
4592
  }
@@ -4638,8 +4647,7 @@ class FigFillPicker extends HTMLElement {
4638
4647
  cameraSelect.addEventListener("change", (e) => {
4639
4648
  const next =
4640
4649
  typeof e.detail === "string" ? e.detail : e.target?.value;
4641
- if (!next) return;
4642
- this.#webcam.deviceId = next;
4650
+ if (!next || next === this.#webcam.deviceId) return;
4643
4651
  startWebcam(next);
4644
4652
  });
4645
4653
 
package/fig.js CHANGED
@@ -8979,6 +8979,7 @@ class FigInputFill extends HTMLElement {
8979
8979
  scale: 50,
8980
8980
  opacity: 1,
8981
8981
  };
8982
+ #custom = {};
8982
8983
 
8983
8984
  constructor() {
8984
8985
  super();
@@ -9066,6 +9067,11 @@ class FigInputFill extends HTMLElement {
9066
9067
  if (parsed.opacity !== undefined)
9067
9068
  this.#webcam.opacity = parsed.opacity;
9068
9069
  break;
9070
+ default: {
9071
+ const { type: _type, ...rest } = parsed;
9072
+ this.#custom = rest;
9073
+ break;
9074
+ }
9069
9075
  }
9070
9076
  } catch (e) {
9071
9077
  // If not JSON, treat as hex color
@@ -9142,9 +9148,9 @@ class FigInputFill extends HTMLElement {
9142
9148
  case "webcam":
9143
9149
  return this.#webcam.snapshot
9144
9150
  ? figCssUrl(this.#webcam.snapshot)
9145
- : "#D9D9D9";
9151
+ : "";
9146
9152
  default:
9147
- return "#D9D9D9";
9153
+ return this.#customSwatchBackground();
9148
9154
  }
9149
9155
  }
9150
9156
 
@@ -9173,7 +9179,38 @@ class FigInputFill extends HTMLElement {
9173
9179
  case "webcam":
9174
9180
  return this.#webcam.opacity ?? 1;
9175
9181
  default:
9176
- return 1;
9182
+ return this.#normalizeFillOpacity(
9183
+ this.#custom.opacity ?? this.#custom.alpha,
9184
+ 1,
9185
+ );
9186
+ }
9187
+ }
9188
+
9189
+ #customModeSlot() {
9190
+ return this.querySelector(`[slot="mode-${this.#fillType}"]`);
9191
+ }
9192
+
9193
+ #customTypeLabel() {
9194
+ const slotLabel = this.#customModeSlot()?.getAttribute("label");
9195
+ if (slotLabel) return slotLabel;
9196
+ if (!this.#fillType) return "Fill";
9197
+ return this.#fillType.charAt(0).toUpperCase() + this.#fillType.slice(1);
9198
+ }
9199
+
9200
+ #customSwatchBackground() {
9201
+ return (
9202
+ this.#custom.swatchBackground ||
9203
+ this.#custom.background ||
9204
+ this.#custom.css ||
9205
+ this.#customModeSlot()?.getAttribute("swatch-background") ||
9206
+ ""
9207
+ );
9208
+ }
9209
+
9210
+ #adoptModeSlots() {
9211
+ if (!this.#fillPicker) return;
9212
+ for (const slot of [...this.querySelectorAll(":scope > [slot^='mode-']")]) {
9213
+ this.#fillPicker.append(slot);
9177
9214
  }
9178
9215
  }
9179
9216
 
@@ -9268,6 +9305,13 @@ class FigInputFill extends HTMLElement {
9268
9305
  label = "Webcam";
9269
9306
  opacity = this.#webcam.opacity ?? 1;
9270
9307
  break;
9308
+ default:
9309
+ label = this.#customTypeLabel();
9310
+ opacity = this.#normalizeFillOpacity(
9311
+ this.#custom.opacity ?? this.#custom.alpha,
9312
+ 1,
9313
+ );
9314
+ break;
9271
9315
  }
9272
9316
 
9273
9317
  return [
@@ -9290,6 +9334,7 @@ class FigInputFill extends HTMLElement {
9290
9334
  alpha: this.#fillPickerSwatchAlpha(),
9291
9335
  disabled,
9292
9336
  });
9337
+ const modeSlots = [...this.querySelectorAll("[slot^='mode-']")];
9293
9338
  const picker = figCreateElement(
9294
9339
  "fig-fill-picker",
9295
9340
  {
@@ -9297,7 +9342,7 @@ class FigInputFill extends HTMLElement {
9297
9342
  disabled,
9298
9343
  ...fpAttrs,
9299
9344
  },
9300
- swatch,
9345
+ [swatch, ...modeSlots],
9301
9346
  );
9302
9347
  const combo = figCreateElement(
9303
9348
  "div",
@@ -9307,6 +9352,11 @@ class FigInputFill extends HTMLElement {
9307
9352
  this.replaceChildren(combo);
9308
9353
 
9309
9354
  this.#setupEventListeners();
9355
+ queueMicrotask(() => {
9356
+ this.#adoptModeSlots();
9357
+ this.#syncSwatch();
9358
+ this.#updateControls();
9359
+ });
9310
9360
  }
9311
9361
 
9312
9362
  #setupEventListeners() {
@@ -9375,6 +9425,11 @@ class FigInputFill extends HTMLElement {
9375
9425
  this.#webcam.snapshot = detail.image.url;
9376
9426
  }
9377
9427
  break;
9428
+ default: {
9429
+ const { type: _type, colorSpace: _colorSpace, ...rest } = detail;
9430
+ this.#custom = rest;
9431
+ break;
9432
+ }
9378
9433
  }
9379
9434
  // Update controls (don't re-render to keep dialog open)
9380
9435
  if (typeChanged) {
@@ -9441,6 +9496,9 @@ class FigInputFill extends HTMLElement {
9441
9496
  case "webcam":
9442
9497
  this.#webcam.opacity = alpha;
9443
9498
  break;
9499
+ default:
9500
+ this.#custom.opacity = alpha;
9501
+ break;
9444
9502
  }
9445
9503
  this.#updateFillPicker();
9446
9504
  this.#syncSwatch();
@@ -9510,6 +9568,22 @@ class FigInputFill extends HTMLElement {
9510
9568
  );
9511
9569
  }
9512
9570
  break;
9571
+ default: {
9572
+ const label = this.querySelector(".fig-input-fill-label");
9573
+ if (label) label.textContent = this.#customTypeLabel();
9574
+ if (this.#opacityInput) {
9575
+ this.#opacityInput.setAttribute(
9576
+ "value",
9577
+ Math.round(
9578
+ this.#normalizeFillOpacity(
9579
+ this.#custom.opacity ?? this.#custom.alpha,
9580
+ 1,
9581
+ ) * 100,
9582
+ ),
9583
+ );
9584
+ }
9585
+ break;
9586
+ }
9513
9587
  }
9514
9588
  }
9515
9589
 
@@ -9587,6 +9661,9 @@ class FigInputFill extends HTMLElement {
9587
9661
  case "webcam":
9588
9662
  this.#webcam.opacity = alpha;
9589
9663
  break;
9664
+ default:
9665
+ this.#custom.opacity = alpha;
9666
+ break;
9590
9667
  }
9591
9668
  this.#updateFillPicker();
9592
9669
  this.#updateSwatchAlpha(alpha);
@@ -9703,7 +9780,7 @@ class FigInputFill extends HTMLElement {
9703
9780
  webcam: this.#webcamValue(),
9704
9781
  };
9705
9782
  default:
9706
- return { type: this.#fillType };
9783
+ return { type: this.#fillType, ...this.#custom };
9707
9784
  }
9708
9785
  }
9709
9786
 
@@ -11886,7 +11963,24 @@ class FigSwatch extends HTMLElement {
11886
11963
  }
11887
11964
 
11888
11965
  #render() {
11889
- const rawBg = this.getAttribute("background") || "#D9D9D9";
11966
+ const rawAttr = this.getAttribute("background");
11967
+ const emptyMedia = this.hasAttribute("background") && !rawAttr?.trim();
11968
+ if (emptyMedia) {
11969
+ if (this.#type !== "image" || this.input) {
11970
+ this.#type = "image";
11971
+ this.setAttribute("data-type", "image");
11972
+ if (this.input) {
11973
+ this.input.removeEventListener("input", this.#boundHandleInput);
11974
+ }
11975
+ this.replaceChildren(document.createElement("div"));
11976
+ this.input = null;
11977
+ this.#syncA11y();
11978
+ }
11979
+ this.style.setProperty("--swatch-background", "none");
11980
+ return;
11981
+ }
11982
+
11983
+ const rawBg = rawAttr || "#D9D9D9";
11890
11984
  const isVar = rawBg.includes("var(");
11891
11985
  const bg = isVar ? this.#resolveBackground(rawBg) : rawBg;
11892
11986
  const newType = this.#detectType(bg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.18",
3
+ "version": "8.9.19",
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",