@rogieking/figui3 4.13.0 → 4.15.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
@@ -16,6 +16,15 @@ function figIsWebKitOrIOSBrowser() {
16
16
  return isIOSBrowser || isDesktopWebKit;
17
17
  }
18
18
 
19
+ /** @param {string} name @param {{ size?: string, className?: string }} [options] */
20
+ function createFigIcon(name, options = {}) {
21
+ const icon = document.createElement("fig-icon");
22
+ if (name) icon.setAttribute("name", name);
23
+ if (options.size) icon.setAttribute("size", options.size);
24
+ if (options.className) icon.className = options.className;
25
+ return icon;
26
+ }
27
+
19
28
  function figSupportsCustomizedBuiltIns() {
20
29
  if (
21
30
  typeof window === "undefined" ||
@@ -1234,10 +1243,7 @@ class FigDialog extends HTMLDialogElement {
1234
1243
  btn.setAttribute("variant", "ghost");
1235
1244
  btn.setAttribute("icon", "");
1236
1245
  btn.setAttribute("close-dialog", "");
1237
- const icon = document.createElement("span");
1238
- icon.className = "fig-mask-icon";
1239
- icon.style.setProperty("--icon", "var(--icon-close)");
1240
- btn.appendChild(icon);
1246
+ btn.appendChild(createFigIcon("close"));
1241
1247
  tooltip.appendChild(btn);
1242
1248
  header.appendChild(h3);
1243
1249
  header.appendChild(tooltip);
@@ -4907,8 +4913,10 @@ class FigField extends HTMLElement {
4907
4913
  this.#toggleable = !!(this.input && "open" in this.input);
4908
4914
 
4909
4915
  if (this.#toggleable && this.label) {
4910
- this.#chevron = document.createElement("span");
4911
- this.#chevron.className = "fig-mask-icon fig-field-chevron";
4916
+ this.#chevron = createFigIcon("chevron", {
4917
+ size: "small",
4918
+ className: "fig-field-chevron",
4919
+ });
4912
4920
  this.insertBefore(this.#chevron, this.label);
4913
4921
 
4914
4922
  this.#boundToggle = (e) => {
@@ -6423,9 +6431,10 @@ customElements.define("fig-input-fill", FigInputFill);
6423
6431
  /* Input Palette */
6424
6432
  /**
6425
6433
  * A palette of solid colors, each rendered as a fig-input-color swatch.
6426
- * Manages an internal array of colors with add support.
6434
+ * Manages an internal array of colors with optional add/remove support.
6427
6435
  * @attr {string} value - JSON array of hex strings or {color,alpha} objects, or comma-separated hex
6428
6436
  * @attr {boolean} disabled - Whether the palette is disabled
6437
+ * @attr {boolean} fixed - When set (or `fixed="true"`), palette length is locked — no add or remove
6429
6438
  * @attr {number} min - Minimum number of colors (default: 2)
6430
6439
  * @attr {number} max - Maximum number of colors (default: 8); add button hidden at max
6431
6440
  * @fires input - During color editing (detail: full color array)
@@ -6438,7 +6447,7 @@ class FigInputPalette extends HTMLElement {
6438
6447
  #renderRAF = null;
6439
6448
 
6440
6449
  static get observedAttributes() {
6441
- return ["value", "disabled", "min", "max", "open", "add"];
6450
+ return ["value", "disabled", "min", "max", "open", "fixed"];
6442
6451
  }
6443
6452
 
6444
6453
  get open() {
@@ -6462,8 +6471,10 @@ class FigInputPalette extends HTMLElement {
6462
6471
  }
6463
6472
  }
6464
6473
 
6465
- get #showAdd() {
6466
- return !this.hasAttribute("add") || this.getAttribute("add") !== "false";
6474
+ get #isFixed() {
6475
+ return (
6476
+ this.hasAttribute("fixed") && this.getAttribute("fixed") !== "false"
6477
+ );
6467
6478
  }
6468
6479
 
6469
6480
  get #min() {
@@ -6508,7 +6519,7 @@ class FigInputPalette extends HTMLElement {
6508
6519
  break;
6509
6520
  case "min":
6510
6521
  case "max":
6511
- case "add":
6522
+ case "fixed":
6512
6523
  this.#render();
6513
6524
  break;
6514
6525
  case "open":
@@ -6618,13 +6629,15 @@ class FigInputPalette extends HTMLElement {
6618
6629
  inlineWrap.appendChild(wrap);
6619
6630
  this.appendChild(inlineWrap);
6620
6631
 
6621
- if (this.#showAdd) this.#createAddButton(disabled, this);
6632
+ if (!this.#isFixed) this.#createAddButton(disabled, this);
6622
6633
 
6623
6634
  const expandedWrap = document.createElement("div");
6624
6635
  expandedWrap.className = "palette-colors-expanded";
6625
6636
  this.#colors.forEach((entry, i) => {
6626
6637
  expandedWrap.appendChild(this.#createPicker(entry, i, disabled));
6627
- expandedWrap.appendChild(this.#createRemoveButton(i, disabled));
6638
+ if (!this.#isFixed) {
6639
+ expandedWrap.appendChild(this.#createRemoveButton(i, disabled));
6640
+ }
6628
6641
  });
6629
6642
  this.appendChild(expandedWrap);
6630
6643
  }
@@ -6639,14 +6652,12 @@ class FigInputPalette extends HTMLElement {
6639
6652
  : entry.color;
6640
6653
  const ic = document.createElement("fig-input-color");
6641
6654
  ic.setAttribute("value", hexAlpha);
6642
- ic.setAttribute("picker", "figma");
6643
- ic.setAttribute("picker-anchor", "self");
6644
6655
  if (inline) {
6645
6656
  ic.setAttribute("text", "false");
6646
6657
  ic.setAttribute("alpha", "true");
6647
6658
  } else {
6648
6659
  ic.setAttribute("text", "true");
6649
- ic.setAttribute("alpha", "false");
6660
+ ic.setAttribute("alpha", this.#isFixed ? "true" : "false");
6650
6661
  ic.setAttribute("full", "");
6651
6662
  }
6652
6663
  if (disabled) ic.setAttribute("disabled", "");
@@ -6696,7 +6707,7 @@ class FigInputPalette extends HTMLElement {
6696
6707
  btn.setAttribute("aria-label", "Remove color");
6697
6708
  btn.className = "palette-remove-btn";
6698
6709
  if (disabled || this.#colors.length <= this.#min) btn.setAttribute("disabled", "");
6699
- btn.innerHTML = `<span class="fig-mask-icon" style="--icon: var(--icon-minus)"></span>`;
6710
+ btn.appendChild(createFigIcon("minus"));
6700
6711
  btn.addEventListener("click", () => {
6701
6712
  if (this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false") return;
6702
6713
  this.#removeColor(index);
@@ -6705,6 +6716,7 @@ class FigInputPalette extends HTMLElement {
6705
6716
  }
6706
6717
 
6707
6718
  #removeColor(index) {
6719
+ if (this.#isFixed) return;
6708
6720
  if (index < 0 || index >= this.#colors.length) return;
6709
6721
  if (this.#colors.length <= this.#min) return;
6710
6722
  this.#colors.splice(index, 1);
@@ -6722,7 +6734,7 @@ class FigInputPalette extends HTMLElement {
6722
6734
  addBtn.setAttribute("aria-label", "Add color");
6723
6735
  addBtn.className = "palette-add-btn";
6724
6736
  if (disabled || atMax) addBtn.setAttribute("disabled", "");
6725
- addBtn.innerHTML = `<span class="fig-mask-icon" style="--icon: var(--icon-add)"></span>`;
6737
+ addBtn.appendChild(createFigIcon("add"));
6726
6738
  addBtn.addEventListener("click", () => {
6727
6739
  if (
6728
6740
  this.hasAttribute("disabled") &&
@@ -6739,6 +6751,7 @@ class FigInputPalette extends HTMLElement {
6739
6751
  }
6740
6752
 
6741
6753
  #addColor(entry) {
6754
+ if (this.#isFixed) return;
6742
6755
  this.#colors.push(entry);
6743
6756
  const disabled =
6744
6757
  this.hasAttribute("disabled") &&
@@ -9024,10 +9037,9 @@ class FigMediaControls extends HTMLElement {
9024
9037
  btn.setAttribute("size", "small");
9025
9038
  btn.setAttribute("icon", "true");
9026
9039
  btn.setAttribute("aria-label", "Play");
9027
- const icon = document.createElement("span");
9028
- icon.className = "fig-mask-icon fig-media-controls-play-icon";
9029
- icon.style.setProperty("--icon", "var(--icon-play)");
9030
- icon.style.setProperty("--size", "1.5rem");
9040
+ const icon = createFigIcon("play", {
9041
+ className: "fig-media-controls-play-icon",
9042
+ });
9031
9043
  btn.append(icon);
9032
9044
  tooltip.append(btn);
9033
9045
  btn.addEventListener("click", (e) => {
@@ -9090,10 +9102,7 @@ class FigMediaControls extends HTMLElement {
9090
9102
  this.#playTooltip?.setAttribute("text", playing ? "Pause" : "Play");
9091
9103
  const icon = this.#playBtn.querySelector(".fig-media-controls-play-icon");
9092
9104
  if (icon) {
9093
- icon.style.setProperty(
9094
- "--icon",
9095
- playing ? "var(--icon-pause)" : "var(--icon-play)",
9096
- );
9105
+ icon.setAttribute("name", playing ? "pause" : "play");
9097
9106
  }
9098
9107
  }
9099
9108
 
@@ -9338,7 +9347,7 @@ class FigInputFile extends HTMLElement {
9338
9347
  this.#clearBtn.setAttribute("icon", "true");
9339
9348
  this.#clearBtn.className = "fig-input-file-clear";
9340
9349
  if (disabled) this.#clearBtn.setAttribute("disabled", "");
9341
- this.#clearBtn.innerHTML = `<span class="fig-mask-icon" style="--icon: var(--icon-minus);"></span>`;
9350
+ this.#clearBtn.replaceChildren(createFigIcon("minus"));
9342
9351
  this.#clearBtn.addEventListener("click", this.#onClear);
9343
9352
  clearTooltip.appendChild(this.#clearBtn);
9344
9353
  this.appendChild(clearTooltip);
@@ -11194,7 +11203,7 @@ class FigInputJoystick extends HTMLElement {
11194
11203
  </div>
11195
11204
  <fig-tooltip text="Reset">
11196
11205
  <fig-button variant="ghost" icon="true" class="fig-joystick-reset" aria-label="Reset to default">
11197
- <span class="fig-mask-icon" style="--icon: var(--icon-reset)"></span>
11206
+ <fig-icon name="reset" size="small"></fig-icon>
11198
11207
  </fig-button>
11199
11208
  </fig-tooltip>
11200
11209
  </div>
@@ -11709,8 +11718,10 @@ class FigGroup extends HTMLElement {
11709
11718
 
11710
11719
  if (isCollapsible) {
11711
11720
  if (!h3.querySelector(".fig-group-chevron")) {
11712
- const chevron = document.createElement("span");
11713
- chevron.className = "fig-mask-icon fig-group-chevron";
11721
+ const chevron = createFigIcon("chevron", {
11722
+ size: "small",
11723
+ className: "fig-group-chevron",
11724
+ });
11714
11725
  h3.prepend(chevron);
11715
11726
  }
11716
11727
  this.#chevron = h3.querySelector(".fig-group-chevron");
@@ -11752,7 +11763,71 @@ customElements.define("fig-footer", FigFooter);
11752
11763
  class FigSpinner extends HTMLElement {}
11753
11764
  customElements.define("fig-spinner", FigSpinner);
11754
11765
 
11755
- class FigIcon extends HTMLElement {}
11766
+ /** @type {Record<string, string>} */
11767
+ const FIG_ICON_TOKENS = {
11768
+ chevron: "--icon-16-chevron",
11769
+ checkmark: "--icon-16-checkmark",
11770
+ reset: "--icon-16-reset",
11771
+ "arrow-left": "--icon-16-arrow-left",
11772
+ steppers: "--icon-24-steppers",
11773
+ eyedropper: "--icon-24-eyedropper",
11774
+ add: "--icon-24-add",
11775
+ minus: "--icon-24-minus",
11776
+ back: "--icon-24-back",
11777
+ forward: "--icon-24-forward",
11778
+ close: "--icon-24-close",
11779
+ rotate: "--icon-24-rotate",
11780
+ swap: "--icon-24-swap",
11781
+ play: "--icon-24-play",
11782
+ pause: "--icon-24-pause",
11783
+ };
11784
+
11785
+ function figIconCssVar(name) {
11786
+ const token = name && FIG_ICON_TOKENS[name];
11787
+ return token ? `var(${token})` : "";
11788
+ }
11789
+
11790
+ /**
11791
+ * Masked icon using design-token SVGs from :root.
11792
+ * @attr {string} name - Icon name (chevron, add, close, …)
11793
+ * @attr {'small'|'medium'} size - Display size; medium (default) uses --spacer-4, small uses --spacer-3
11794
+ * @attr {string} color - Icon fill color (applied as background-color for the mask)
11795
+ */
11796
+ class FigIcon extends HTMLElement {
11797
+ static get observedAttributes() {
11798
+ return ["name", "size", "color"];
11799
+ }
11800
+
11801
+ connectedCallback() {
11802
+ this.#sync();
11803
+ }
11804
+
11805
+ attributeChangedCallback(name, oldValue, newValue) {
11806
+ if (oldValue !== newValue) this.#sync();
11807
+ }
11808
+
11809
+ #sync() {
11810
+ const iconName = this.getAttribute("name");
11811
+ const cssVar = figIconCssVar(iconName);
11812
+ if (cssVar) this.style.setProperty("--icon", cssVar);
11813
+ else this.style.removeProperty("--icon");
11814
+
11815
+ const size = this.getAttribute("size") || "medium";
11816
+ if (size === "small") {
11817
+ this.style.setProperty("--size", "var(--spacer-3)");
11818
+ } else {
11819
+ this.style.removeProperty("--size");
11820
+ }
11821
+
11822
+ const color = this.getAttribute("color");
11823
+ if (color) this.style.backgroundColor = color;
11824
+ else this.style.removeProperty("background-color");
11825
+
11826
+ if (!this.hasAttribute("aria-hidden")) {
11827
+ this.setAttribute("aria-hidden", "true");
11828
+ }
11829
+ }
11830
+ }
11756
11831
  customElements.define("fig-icon", FigIcon);
11757
11832
 
11758
11833
  class FigContent extends HTMLElement {}
@@ -12135,7 +12210,7 @@ class FigFillPicker extends HTMLElement {
12135
12210
  ${headerContent}
12136
12211
  ${gamutDropdown}
12137
12212
  <fig-button icon variant="ghost" class="fig-fill-picker-close">
12138
- <span class="fig-mask-icon" style="--icon: var(--icon-close)"></span>
12213
+ <fig-icon name="close"></fig-icon>
12139
12214
  </fig-button>
12140
12215
  </fig-header>
12141
12216
  <fig-content>
@@ -12305,7 +12380,7 @@ class FigFillPicker extends HTMLElement {
12305
12380
  ></fig-handle>
12306
12381
  </fig-preview>
12307
12382
  <div class="fig-fill-picker-sliders">
12308
- <fig-tooltip text="Sample color"><fig-button icon variant="ghost" class="fig-fill-picker-eyedropper"><span class="fig-mask-icon" style="--icon: var(--icon-eyedropper)"></span></fig-button></fig-tooltip>
12383
+ <fig-tooltip text="Sample color"><fig-button icon variant="ghost" class="fig-fill-picker-eyedropper"><fig-icon name="eyedropper"></fig-icon></fig-button></fig-tooltip>
12309
12384
  <fig-slider type="hue" variant="neue" min="0" max="360" value="${
12310
12385
  this.#color.h
12311
12386
  }"></fig-slider>
@@ -12821,7 +12896,7 @@ class FigFillPicker extends HTMLElement {
12821
12896
  </div>
12822
12897
  <fig-tooltip text="Flip gradient">
12823
12898
  <fig-button icon variant="ghost" class="fig-fill-picker-gradient-flip">
12824
- <span class="fig-mask-icon" style="--icon: var(--icon-swap)"></span>
12899
+ <fig-icon name="swap"></fig-icon>
12825
12900
  </fig-button>
12826
12901
  </fig-tooltip>
12827
12902
  </fig-field>
@@ -12853,7 +12928,7 @@ class FigFillPicker extends HTMLElement {
12853
12928
  <fig-header class="fig-fill-picker-gradient-stops-header" borderless>
12854
12929
  <span>Stops</span>
12855
12930
  <fig-button icon variant="ghost" class="fig-fill-picker-gradient-add" title="Add stop">
12856
- <span class="fig-mask-icon" style="--icon: var(--icon-add)"></span>
12931
+ <fig-icon name="add"></fig-icon>
12857
12932
  </fig-button>
12858
12933
  </fig-header>
12859
12934
  <div class="fig-fill-picker-gradient-stops-list"></div>
@@ -13090,7 +13165,7 @@ class FigFillPicker extends HTMLElement {
13090
13165
  <fig-button icon variant="ghost" class="fig-fill-picker-stop-remove" ${
13091
13166
  this.#gradient.stops.length <= 2 ? "disabled" : ""
13092
13167
  }>
13093
- <span class="fig-mask-icon" style="--icon: var(--icon-minus)"></span>
13168
+ <fig-icon name="minus"></fig-icon>
13094
13169
  </fig-button>
13095
13170
  </fig-field>
13096
13171
  `,
@@ -14004,8 +14079,8 @@ class FigColorTip extends HTMLElement {
14004
14079
  #render() {
14005
14080
  const mode = this.#controlMode;
14006
14081
  if (mode === "add" || mode === "remove") {
14007
- const icon = mode === "add" ? "var(--icon-add)" : "var(--icon-minus)";
14008
- this.innerHTML = `<fig-button icon variant="ghost"><span class="fig-mask-icon" style="--icon: ${icon}"></span></fig-button>`;
14082
+ const iconName = mode === "add" ? "add" : "minus";
14083
+ this.innerHTML = `<fig-button icon variant="ghost"><fig-icon name="${iconName}"></fig-icon></fig-button>`;
14009
14084
  this.#fillPicker = null;
14010
14085
  this.#chit = null;
14011
14086
  this.addEventListener("click", this.#handleControlClick);
@@ -14758,11 +14833,10 @@ class FigChooser extends HTMLElement {
14758
14833
  if (this.#navStart) return;
14759
14834
 
14760
14835
  const makeChevron = () => {
14761
- const icon = document.createElement("span");
14762
- icon.className = "fig-mask-icon fig-chooser-nav-chevron";
14763
- icon.style.setProperty("--icon", "var(--icon-chevron)");
14764
- icon.style.setProperty("--size", "1rem");
14765
- return icon;
14836
+ return createFigIcon("chevron", {
14837
+ size: "small",
14838
+ className: "fig-chooser-nav-chevron",
14839
+ });
14766
14840
  };
14767
14841
 
14768
14842
  this.#navStart = document.createElement("button");
@@ -15087,7 +15161,6 @@ class FigHandle extends HTMLElement {
15087
15161
  }
15088
15162
 
15089
15163
  connectedCallback() {
15090
- if (!this.hasAttribute("type")) this.setAttribute("type", "canvas");
15091
15164
  this.#syncDrag();
15092
15165
  this.#syncHitArea();
15093
15166
  this.addEventListener("click", this.#handleSelect);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "4.13.0",
3
+ "version": "4.15.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",