@rogieking/figui3 11.6.6 → 11.6.8

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
@@ -942,6 +942,13 @@ class FigDropdown extends HTMLElement {
942
942
  #boundHandleSelectInput;
943
943
  #boundHandleSelectChange;
944
944
  #boundHandleSelectKeydown;
945
+ #boundHandleSelectPointerDown;
946
+ #boundHandleSelectBlur;
947
+ #openButton = null;
948
+ #openButtonWasSelected = false;
949
+ #openSelectWasSelected = false;
950
+ #openStateActive = false;
951
+ #openStateFrame = 0;
945
952
 
946
953
  get label() {
947
954
  return this.#label;
@@ -958,6 +965,9 @@ class FigDropdown extends HTMLElement {
958
965
  this.#boundHandleSelectInput = this.#handleSelectInput.bind(this);
959
966
  this.#boundHandleSelectChange = this.#handleSelectChange.bind(this);
960
967
  this.#boundHandleSelectKeydown = this.#handleSelectKeydown.bind(this);
968
+ this.#boundHandleSelectPointerDown =
969
+ this.#handleSelectPointerDown.bind(this);
970
+ this.#boundHandleSelectBlur = this.#clearOpenState.bind(this);
961
971
  this.#boundSlotChange = this.slotChange.bind(this);
962
972
  }
963
973
 
@@ -965,6 +975,87 @@ class FigDropdown extends HTMLElement {
965
975
  this.select.addEventListener("input", this.#boundHandleSelectInput);
966
976
  this.select.addEventListener("change", this.#boundHandleSelectChange);
967
977
  this.select.addEventListener("keydown", this.#boundHandleSelectKeydown);
978
+ this.select.addEventListener(
979
+ "pointerdown",
980
+ this.#boundHandleSelectPointerDown,
981
+ );
982
+ this.select.addEventListener("blur", this.#boundHandleSelectBlur);
983
+ }
984
+
985
+ #markOpenState() {
986
+ const button = this.closest('fig-button[type="select"]');
987
+ if (this.#openStateActive && this.#openButton !== button) {
988
+ this.#clearOpenState();
989
+ }
990
+ if (!this.#openStateActive) {
991
+ this.#openSelectWasSelected =
992
+ this.select.hasAttribute("selected") &&
993
+ this.select.getAttribute("selected") !== "false";
994
+ this.#openStateActive = true;
995
+ }
996
+ this.select.setAttribute("selected", "");
997
+ if (!button) return;
998
+ if (this.#openButton !== button) {
999
+ this.#openButton = button;
1000
+ this.#openButtonWasSelected =
1001
+ button.hasAttribute("selected") &&
1002
+ button.getAttribute("selected") !== "false";
1003
+ }
1004
+ button.setAttribute("selected", "");
1005
+ }
1006
+
1007
+ #clearOpenState() {
1008
+ if (this.#openStateFrame) {
1009
+ cancelAnimationFrame(this.#openStateFrame);
1010
+ this.#openStateFrame = 0;
1011
+ }
1012
+ if (this.#openButton && !this.#openButtonWasSelected) {
1013
+ this.#openButton.removeAttribute("selected");
1014
+ }
1015
+ if (this.#openStateActive && !this.#openSelectWasSelected) {
1016
+ this.select.removeAttribute("selected");
1017
+ }
1018
+ this.#openButton = null;
1019
+ this.#openButtonWasSelected = false;
1020
+ this.#openSelectWasSelected = false;
1021
+ this.#openStateActive = false;
1022
+ }
1023
+
1024
+ #watchOpenState() {
1025
+ if (this.#openStateFrame) cancelAnimationFrame(this.#openStateFrame);
1026
+ let attempts = 0;
1027
+ let sawOpen = false;
1028
+ const check = () => {
1029
+ this.#openStateFrame = 0;
1030
+ if (!this.isConnected) {
1031
+ this.#clearOpenState();
1032
+ return;
1033
+ }
1034
+ let open = false;
1035
+ try {
1036
+ open = this.select.matches(":open");
1037
+ } catch {
1038
+ return;
1039
+ }
1040
+ if (open) sawOpen = true;
1041
+ if (sawOpen && !open) {
1042
+ this.#clearOpenState();
1043
+ return;
1044
+ }
1045
+ attempts += 1;
1046
+ if (sawOpen || attempts < 4) {
1047
+ this.#openStateFrame = requestAnimationFrame(check);
1048
+ } else {
1049
+ this.#clearOpenState();
1050
+ }
1051
+ };
1052
+ this.#openStateFrame = requestAnimationFrame(check);
1053
+ }
1054
+
1055
+ #handleSelectPointerDown(e) {
1056
+ if (e.button !== 0 || this.select.disabled) return;
1057
+ this.#markOpenState();
1058
+ this.#watchOpenState();
968
1059
  }
969
1060
 
970
1061
  #hasPersistentControl(optionEl) {
@@ -1081,6 +1172,7 @@ class FigDropdown extends HTMLElement {
1081
1172
  this.#keepPickerOpen();
1082
1173
  return;
1083
1174
  }
1175
+ this.#clearOpenState();
1084
1176
 
1085
1177
  // Get the value before resetting (use stored value for dropdown type)
1086
1178
  const selectedValue =
@@ -1099,16 +1191,19 @@ class FigDropdown extends HTMLElement {
1099
1191
  }
1100
1192
 
1101
1193
  #handleSelectKeydown(e) {
1102
- if (this.closest('fig-button[type="select"]')) return;
1103
1194
  if (e.key !== "Enter" || e.defaultPrevented) return;
1104
1195
  if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
1105
1196
  if (this.select.disabled || this.select.multiple) return;
1197
+ this.#markOpenState();
1198
+ this.#watchOpenState();
1199
+ if (this.closest('fig-button[type="select"]')) return;
1106
1200
  if (typeof this.select.showPicker !== "function") return;
1107
1201
  e.preventDefault();
1108
1202
  try {
1109
1203
  this.select.showPicker();
1110
1204
  } catch {
1111
1205
  // showPicker can be unavailable during non-user-initiated key events.
1206
+ this.#clearOpenState();
1112
1207
  }
1113
1208
  }
1114
1209
 
@@ -1169,6 +1264,12 @@ class FigDropdown extends HTMLElement {
1169
1264
  this.select.removeEventListener("input", this.#boundHandleSelectInput);
1170
1265
  this.select.removeEventListener("change", this.#boundHandleSelectChange);
1171
1266
  this.select.removeEventListener("keydown", this.#boundHandleSelectKeydown);
1267
+ this.select.removeEventListener(
1268
+ "pointerdown",
1269
+ this.#boundHandleSelectPointerDown,
1270
+ );
1271
+ this.select.removeEventListener("blur", this.#boundHandleSelectBlur);
1272
+ this.#clearOpenState();
1172
1273
  }
1173
1274
  }
1174
1275
 
@@ -14977,6 +15078,7 @@ class FigMediaControls extends HTMLElement {
14977
15078
  if (!this.#playBtn) return;
14978
15079
  const playing = this.playing;
14979
15080
  this.#playBtn.setAttribute("aria-label", playing ? "Pause" : "Play");
15081
+ this.#playBtn.toggleAttribute("selected", playing);
14980
15082
  this.#playTooltip?.setAttribute("text", playing ? "Pause" : "Play");
14981
15083
  const icon = this.#playBtn.querySelector("fig-icon");
14982
15084
  if (icon) {
@@ -15728,9 +15830,10 @@ const FIG_INPUT_FILE_VIDEO_EXTENSIONS = [
15728
15830
  * @attr {string} accepts - Native file chooser filter.
15729
15831
  * @attr {boolean} preview - Preview supported files or show their media-specific icon.
15730
15832
  * @attr {string} label - Upload button label. Defaults by known file type.
15833
+ * @attr {boolean} remove - Show the remove button for a selected file. Defaults to true.
15731
15834
  */
15732
15835
  class FigInputFile extends HTMLElement {
15733
- static observedAttributes = ["accepts", "preview", "label", "disabled", "multiple", "variant", "url", "filename"];
15836
+ static observedAttributes = ["accepts", "preview", "label", "disabled", "multiple", "variant", "url", "filename", "remove"];
15734
15837
 
15735
15838
  #fileInput = null;
15736
15839
  #filenameEl = null;
@@ -15783,6 +15886,10 @@ class FigInputFile extends HTMLElement {
15783
15886
  );
15784
15887
  }
15785
15888
 
15889
+ get #removeEnabled() {
15890
+ return this.getAttribute("remove") !== "false";
15891
+ }
15892
+
15786
15893
  get #largePreviewEnabled() {
15787
15894
  return this.getAttribute("preview")?.toLowerCase() === "large";
15788
15895
  }
@@ -16442,18 +16549,20 @@ class FigInputFile extends HTMLElement {
16442
16549
  this.appendChild(this.#uploadBtn);
16443
16550
  }
16444
16551
 
16445
- const clearTooltip = document.createElement("fig-tooltip");
16446
- clearTooltip.setAttribute("text", "Remove");
16447
- this.#clearBtn = document.createElement("fig-button");
16448
- this.#clearBtn.setAttribute("variant", variant === "overlay" ? "overlay" : "ghost");
16449
- this.#clearBtn.setAttribute("icon", "true");
16450
- this.#clearBtn.setAttribute("aria-label", "Remove");
16451
- this.#clearBtn.className = "fig-input-file-clear";
16452
- if (disabled) this.#clearBtn.setAttribute("disabled", "");
16453
- this.#clearBtn.replaceChildren(createFigIcon("minus"));
16454
- this.#clearBtn.addEventListener("click", this.#onClear);
16455
- clearTooltip.appendChild(this.#clearBtn);
16456
- this.appendChild(clearTooltip);
16552
+ if (this.#removeEnabled) {
16553
+ const clearTooltip = document.createElement("fig-tooltip");
16554
+ clearTooltip.setAttribute("text", "Remove");
16555
+ this.#clearBtn = document.createElement("fig-button");
16556
+ this.#clearBtn.setAttribute("variant", variant === "overlay" ? "overlay" : "ghost");
16557
+ this.#clearBtn.setAttribute("icon", "true");
16558
+ this.#clearBtn.setAttribute("aria-label", "Remove");
16559
+ this.#clearBtn.className = "fig-input-file-clear";
16560
+ if (disabled) this.#clearBtn.setAttribute("disabled", "");
16561
+ this.#clearBtn.replaceChildren(createFigIcon("minus"));
16562
+ this.#clearBtn.addEventListener("click", this.#onClear);
16563
+ clearTooltip.appendChild(this.#clearBtn);
16564
+ this.appendChild(clearTooltip);
16565
+ }
16457
16566
  if (showsAudioPreview) this.#renderAudioPreview();
16458
16567
  } else {
16459
16568
  const tooltipText = accepts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "11.6.6",
3
+ "version": "11.6.8",
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",