@rogieking/figui3 5.1.1 → 6.1.0

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
@@ -1135,7 +1135,7 @@ class FigFillPicker extends HTMLElement {
1135
1135
  </fig-tooltip>
1136
1136
  </fig-field>
1137
1137
  <fig-preview class="fig-fill-picker-gradient-preview">
1138
- <fig-input-gradient class="fig-fill-picker-gradient-bar-input" edit="true" size="large" value='${JSON.stringify({ type: "gradient", gradient: gradientToValueShape(this.#gradient) })}'></fig-input-gradient>
1138
+ <fig-input-gradient class="fig-fill-picker-gradient-bar-input" edit="true" mode="tip" size="large" value='${JSON.stringify({ type: "gradient", gradient: gradientToValueShape(this.#gradient) })}'></fig-input-gradient>
1139
1139
  </fig-preview>
1140
1140
  <fig-field class="fig-fill-picker-gradient-interpolation">
1141
1141
  <label>Mixing</label>
package/fig.js CHANGED
@@ -3632,6 +3632,7 @@ customElements.define("fig-options", FigOptions);
3632
3632
  class FigSlider extends HTMLElement {
3633
3633
  #isInteracting = false;
3634
3634
  #showEmptyTextValue = false;
3635
+ #isSyncingValueAttribute = false;
3635
3636
  #value = "";
3636
3637
  // Private fields declarations
3637
3638
  #typeDefaults = {
@@ -3835,8 +3836,17 @@ class FigSlider extends HTMLElement {
3835
3836
  }
3836
3837
 
3837
3838
  set value(value) {
3838
- const normalized = String(this.#normalizeSliderValue(value));
3839
+ const rawValue = value === null || value === undefined ? "" : String(value);
3840
+ const hasParsedBounds = this.min !== undefined || this.max !== undefined;
3841
+ const normalized = hasParsedBounds
3842
+ ? String(this.#normalizeSliderValue(rawValue))
3843
+ : rawValue;
3839
3844
  this.#value = normalized;
3845
+ if (this.getAttribute("value") !== normalized) {
3846
+ this.#isSyncingValueAttribute = true;
3847
+ this.setAttribute("value", normalized);
3848
+ this.#isSyncingValueAttribute = false;
3849
+ }
3840
3850
  if (this.input && this.input.value !== normalized) {
3841
3851
  this.input.value = normalized;
3842
3852
  this.input.setAttribute("aria-valuenow", normalized);
@@ -4002,6 +4012,7 @@ class FigSlider extends HTMLElement {
4002
4012
  }
4003
4013
 
4004
4014
  attributeChangedCallback(name, oldValue, newValue) {
4015
+ if (name === "value" && this.#isSyncingValueAttribute) return;
4005
4016
  if (this.input) {
4006
4017
  switch (name) {
4007
4018
  case "color":
@@ -7263,7 +7274,7 @@ class FigInputGradient extends HTMLElement {
7263
7274
  }
7264
7275
 
7265
7276
  static get observedAttributes() {
7266
- return ["value", "disabled", "edit"];
7277
+ return ["value", "disabled", "edit", "mode"];
7267
7278
  }
7268
7279
 
7269
7280
  get #editMode() {
@@ -7277,6 +7288,10 @@ class FigInputGradient extends HTMLElement {
7277
7288
  return this.#editMode === "true";
7278
7289
  }
7279
7290
 
7291
+ get #stopHandleMode() {
7292
+ return this.getAttribute("mode") === "tip" ? "tip" : "handle";
7293
+ }
7294
+
7280
7295
  connectedCallback() {
7281
7296
  this.#parseValue();
7282
7297
  this.#render();
@@ -7428,10 +7443,11 @@ class FigInputGradient extends HTMLElement {
7428
7443
 
7429
7444
  #buildStopHandles() {
7430
7445
  const disabled = this.hasAttribute("disabled");
7446
+ const tipAttr = this.#stopHandleMode === "tip" ? ' tip="color"' : "";
7431
7447
  return this.#gradient.stops
7432
7448
  .map(
7433
7449
  (stop, i) =>
7434
- `<fig-tooltip action="manual" text="${Math.round(stop.position)}%"><fig-handle drag drag-axes="x" drag-surface=".fig-input-gradient-track" type="color" color-tip color="${this.#stopColorCSS(stop)}" value="${stop.position}% 50%" hit-area="4" data-stop-index="${i}"${disabled ? " disabled" : ""}></fig-handle></fig-tooltip>`,
7450
+ `<fig-tooltip action="manual" text="${Math.round(stop.position)}%"><fig-handle drag drag-axes="x" drag-surface=".fig-input-gradient-track" type="color"${tipAttr} color="${this.#stopColorCSS(stop)}" value="${stop.position}% 50%" hit-area="4" data-stop-index="${i}"${disabled ? " disabled" : ""}></fig-handle></fig-tooltip>`,
7435
7451
  )
7436
7452
  .join("");
7437
7453
  }
@@ -7512,8 +7528,7 @@ class FigInputGradient extends HTMLElement {
7512
7528
  const ghost = document.createElement("fig-handle");
7513
7529
  ghost.classList.add("fig-input-gradient-ghost");
7514
7530
  ghost.setAttribute("type", "color");
7515
- ghost.setAttribute("color-tip", "");
7516
- ghost.setAttribute("control", "add");
7531
+ if (this.#stopHandleMode === "tip") ghost.setAttribute("tip", "add");
7517
7532
  ghost.style.position = "absolute";
7518
7533
  ghost.style.top = "50%";
7519
7534
  ghost.style.transform = "translate(-50%, -50%)";
@@ -7669,6 +7684,7 @@ class FigInputGradient extends HTMLElement {
7669
7684
  const ghost = this.#ghostHandle;
7670
7685
  this.#track.innerHTML = this.#buildStopHandles();
7671
7686
  if (ghost) this.#track.appendChild(ghost);
7687
+ this.#syncHandleMode();
7672
7688
  this.#reobserveHandleColors();
7673
7689
  requestAnimationFrame(() => this.#repositionHandles());
7674
7690
  return;
@@ -7680,9 +7696,30 @@ class FigInputGradient extends HTMLElement {
7680
7696
  h.dataset.stopIndex = i;
7681
7697
  h.setAttribute("value", `${stop.position}% 50%`);
7682
7698
  h.setAttribute("color", this.#stopColorCSS(stop));
7699
+ if (this.#stopHandleMode === "tip") {
7700
+ h.setAttribute("tip", "color");
7701
+ } else {
7702
+ h.removeAttribute("tip");
7703
+ }
7683
7704
  const tip = h.closest("fig-tooltip");
7684
7705
  if (tip) tip.setAttribute("text", `${Math.round(stop.position)}%`);
7685
7706
  }
7707
+ this.#syncHandleMode();
7708
+ }
7709
+
7710
+ #syncHandleMode() {
7711
+ if (!this.#track) return;
7712
+ const useTip = this.#stopHandleMode === "tip";
7713
+ this.#track
7714
+ .querySelectorAll("fig-handle:not(.fig-input-gradient-ghost)")
7715
+ .forEach((handle) => {
7716
+ if (useTip) handle.setAttribute("tip", "color");
7717
+ else handle.removeAttribute("tip");
7718
+ });
7719
+ if (this.#ghostHandle) {
7720
+ if (useTip) this.#ghostHandle.setAttribute("tip", "add");
7721
+ else this.#ghostHandle.removeAttribute("tip");
7722
+ }
7686
7723
  }
7687
7724
 
7688
7725
  #reobserveHandleColors() {
@@ -7981,6 +8018,9 @@ class FigInputGradient extends HTMLElement {
7981
8018
  document.removeEventListener("keydown", this.#onKeyDown);
7982
8019
  }
7983
8020
  break;
8021
+ case "mode":
8022
+ this.#syncHandleMode();
8023
+ break;
7984
8024
  }
7985
8025
  }
7986
8026
 
@@ -12018,10 +12058,16 @@ customElements.define("fig-joystick", FigInputJoystick);
12018
12058
  // FigInputAngle moved to fig-lab.js
12019
12059
  // FigShimmer
12020
12060
  class FigShimmer extends HTMLElement {
12061
+ get durationPropertyName() {
12062
+ return this.localName === "fig-skeleton"
12063
+ ? "--fig-skeleton-duration"
12064
+ : "--fig-shimmer-duration";
12065
+ }
12066
+
12021
12067
  connectedCallback() {
12022
12068
  const duration = this.getAttribute("duration");
12023
12069
  if (duration) {
12024
- this.style.setProperty("--shimmer-duration", duration);
12070
+ this.style.setProperty(this.durationPropertyName, duration);
12025
12071
  }
12026
12072
  }
12027
12073
 
@@ -12043,7 +12089,7 @@ class FigShimmer extends HTMLElement {
12043
12089
 
12044
12090
  attributeChangedCallback(name, oldValue, newValue) {
12045
12091
  if (name === "duration") {
12046
- this.style.setProperty("--shimmer-duration", newValue || "1.5s");
12092
+ this.style.setProperty(this.durationPropertyName, newValue || "1.5s");
12047
12093
  }
12048
12094
  // playing is handled purely by CSS attribute selectors
12049
12095
  }
@@ -13377,8 +13423,7 @@ class FigHandle extends HTMLElement {
13377
13423
  "drag-snapping",
13378
13424
  "value",
13379
13425
  "type",
13380
- "control",
13381
- "color-tip",
13426
+ "tip",
13382
13427
  "hit-area",
13383
13428
  "hit-area-mode",
13384
13429
  ];
@@ -13392,24 +13437,11 @@ class FigHandle extends HTMLElement {
13392
13437
  #nativeColorInput = null;
13393
13438
  #hitAreaEl = null;
13394
13439
 
13395
- get #controlMode() {
13396
- return this.getAttribute("control") || null;
13397
- }
13398
-
13399
- get #hasControlMode() {
13400
- return this.#controlMode === "add" || this.#controlMode === "remove";
13401
- }
13402
-
13403
- get #usesColorTip() {
13404
- return (
13405
- this.#hasControlMode ||
13406
- (this.hasAttribute("color-tip") &&
13407
- this.getAttribute("color-tip") !== "false")
13408
- );
13409
- }
13410
-
13411
- get #isGhost() {
13412
- return this.classList.contains("fig-input-gradient-ghost");
13440
+ get #tipMode() {
13441
+ const mode = (this.getAttribute("tip") || "").trim().toLowerCase();
13442
+ return mode === "color" || mode === "add" || mode === "remove"
13443
+ ? mode
13444
+ : null;
13413
13445
  }
13414
13446
 
13415
13447
  get #canOpenColorPicker() {
@@ -13625,7 +13657,7 @@ class FigHandle extends HTMLElement {
13625
13657
  document.addEventListener("keydown", this.#handleKeyDown);
13626
13658
  const initial = this.getAttribute("value");
13627
13659
  if (initial) this.#applyValue(initial);
13628
- if (this.#hasControlMode) this.#showColorTip();
13660
+ if (this.#tipMode) this.#showColorTip();
13629
13661
  }
13630
13662
 
13631
13663
  disconnectedCallback() {
@@ -13645,22 +13677,13 @@ class FigHandle extends HTMLElement {
13645
13677
  select() {
13646
13678
  if (this.hasAttribute("disabled")) return;
13647
13679
  this.setAttribute("selected", "");
13648
- if (
13649
- this.getAttribute("type") === "color" &&
13650
- this.#canOpenColorPicker &&
13651
- !this.#isDragging &&
13652
- this.#usesColorTip
13653
- )
13654
- this.#showColorTip();
13655
13680
  }
13656
13681
 
13657
13682
  deselect() {
13658
13683
  this.removeAttribute("selected");
13659
- this.#hideColorTip();
13660
13684
  }
13661
13685
 
13662
13686
  #handleSelect = (e) => {
13663
- if (this.#hasControlMode) return;
13664
13687
  if (this.#didDrag) {
13665
13688
  this.#didDrag = false;
13666
13689
  return;
@@ -13668,7 +13691,7 @@ class FigHandle extends HTMLElement {
13668
13691
  if (
13669
13692
  this.getAttribute("type") === "color" &&
13670
13693
  this.#canOpenColorPicker &&
13671
- !this.#usesColorTip
13694
+ !this.#tipMode
13672
13695
  ) {
13673
13696
  this.#openDirectColorPicker();
13674
13697
  return;
@@ -13677,7 +13700,6 @@ class FigHandle extends HTMLElement {
13677
13700
  };
13678
13701
 
13679
13702
  #handleDeselect = (e) => {
13680
- if (this.#hasControlMode) return;
13681
13703
  if (this.contains(e.target)) return;
13682
13704
  if ((this.#colorTip || this.#directColorPicker) && e.target.closest?.("dialog, [popover]")) return;
13683
13705
  this.deselect();
@@ -13689,9 +13711,7 @@ class FigHandle extends HTMLElement {
13689
13711
  if (this.getAttribute("type") !== "color") return;
13690
13712
  if (!this.#canOpenColorPicker) return;
13691
13713
  e.preventDefault();
13692
- if (this.#usesColorTip) {
13693
- if (!this.#colorTip) this.#showColorTip();
13694
- } else {
13714
+ if (!this.#tipMode) {
13695
13715
  this.#openDirectColorPicker();
13696
13716
  }
13697
13717
  };
@@ -13703,30 +13723,22 @@ class FigHandle extends HTMLElement {
13703
13723
  } else {
13704
13724
  this.style.setProperty("--fill", value);
13705
13725
  }
13706
- if (this.#colorTip && value) {
13726
+ if (this.#colorTip && this.#tipMode === "color" && value) {
13707
13727
  this.#colorTip.setAttribute("value", value);
13708
13728
  }
13709
13729
  this.#syncDirectColorPickerValue();
13710
13730
  }
13711
13731
  if (name === "drag") this.#syncDrag();
13712
13732
  if (name === "hit-area") this.#syncHitArea();
13733
+ if (name === "selected") this.#syncColorTipSelected();
13713
13734
  if (name === "value" && !this.#applyingValue && !this.#isDragging) {
13714
13735
  this.#applyValue(value);
13715
13736
  }
13716
- if (name === "control") {
13717
- if (this.#hasControlMode) {
13737
+ if (name === "tip") {
13738
+ this.#hideColorTip();
13739
+ if (this.#tipMode) {
13718
13740
  this.#removeDirectColorPicker();
13719
- this.#hideColorTip();
13720
13741
  this.#showColorTip();
13721
- } else {
13722
- this.#hideColorTip();
13723
- }
13724
- }
13725
- if (name === "color-tip") {
13726
- if (this.#usesColorTip) {
13727
- this.#removeDirectColorPicker();
13728
- } else {
13729
- this.#hideColorTip();
13730
13742
  }
13731
13743
  }
13732
13744
  }
@@ -13876,6 +13888,7 @@ class FigHandle extends HTMLElement {
13876
13888
  }
13877
13889
 
13878
13890
  showColorTip() {
13891
+ if (!this.#tipMode) return;
13879
13892
  if (this.#colorTip) {
13880
13893
  this.#colorTip.style.display = "";
13881
13894
  return;
@@ -13983,7 +13996,6 @@ class FigHandle extends HTMLElement {
13983
13996
 
13984
13997
  #openDirectColorPicker() {
13985
13998
  if (this.hasAttribute("disabled")) return;
13986
- this.#hideColorTip();
13987
13999
  const picker = this.#ensureDirectColorPicker();
13988
14000
  if (!picker) {
13989
14001
  this.#openNativeColorPicker();
@@ -14041,19 +14053,19 @@ class FigHandle extends HTMLElement {
14041
14053
 
14042
14054
  #closeColorPickerForDrag() {
14043
14055
  if (this.getAttribute("type") !== "color") return;
14044
- this.#hideColorTip();
14045
14056
  this.#directColorPicker?.close();
14046
14057
  }
14047
14058
 
14048
14059
  #showColorTip() {
14060
+ const mode = this.#tipMode;
14061
+ if (!mode) return;
14049
14062
  if (this.#colorTip) return;
14050
14063
  const tip = document.createElement("fig-color-tip");
14051
- if (this.#hasControlMode) {
14052
- tip.setAttribute("control", this.#controlMode);
14064
+ if (mode === "add" || mode === "remove") {
14065
+ tip.setAttribute("control", mode);
14053
14066
  } else {
14054
14067
  tip.setAttribute("value", this.getAttribute("color") || "#D9D9D9");
14055
14068
  tip.setAttribute("alpha", "true");
14056
- tip.setAttribute("selected", "");
14057
14069
  }
14058
14070
  tip.addEventListener("input", this.#handleColorTipInput);
14059
14071
  tip.addEventListener("change", this.#handleColorTipChange);
@@ -14061,6 +14073,7 @@ class FigHandle extends HTMLElement {
14061
14073
  tip.addEventListener("remove", this.#handleColorTipControl);
14062
14074
  this.appendChild(tip);
14063
14075
  this.#colorTip = tip;
14076
+ this.#syncColorTipSelected();
14064
14077
  }
14065
14078
 
14066
14079
  #hideColorTip() {
@@ -14073,6 +14086,13 @@ class FigHandle extends HTMLElement {
14073
14086
  this.#colorTip = null;
14074
14087
  }
14075
14088
 
14089
+ #syncColorTipSelected() {
14090
+ if (!this.#colorTip || this.#tipMode !== "color") return;
14091
+ const selected =
14092
+ this.hasAttribute("selected") && this.getAttribute("selected") !== "false";
14093
+ this.#colorTip.toggleAttribute("selected", selected);
14094
+ }
14095
+
14076
14096
  #colorWithOpacity(hex, opacity) {
14077
14097
  if (opacity === undefined || opacity >= 100) return hex;
14078
14098
  const { r, g, b } = figHexToRGB(hex);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "5.1.1",
3
+ "version": "6.1.0",
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",