@rogieking/figui3 9.4.4 → 9.4.6

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-lab.js CHANGED
@@ -3065,8 +3065,10 @@ class FigReorder extends HTMLElement {
3065
3065
  this.#clearHandleMarks(allChildren);
3066
3066
  this.#clearReorderItemMarks(allChildren);
3067
3067
  this.#markNonItems(allChildren.filter((child) => !childSet.has(child)));
3068
+ const reorderable = !this.#disabled && children.length >= 2;
3069
+ this.#markReorderItems(children, reorderable);
3068
3070
 
3069
- if (this.#disabled || children.length < 2) {
3071
+ if (!reorderable) {
3070
3072
  this.#unbindAll();
3071
3073
  return;
3072
3074
  }
@@ -3088,7 +3090,6 @@ class FigReorder extends HTMLElement {
3088
3090
  }
3089
3091
 
3090
3092
  this.#markHandles(children);
3091
- this.#markReorderItems(children);
3092
3093
  }
3093
3094
 
3094
3095
  #clearReorderItemMarks(children) {
@@ -3101,9 +3102,9 @@ class FigReorder extends HTMLElement {
3101
3102
  }
3102
3103
  }
3103
3104
 
3104
- #markReorderItems(children) {
3105
+ #markReorderItems(children, reorderable = true) {
3105
3106
  for (const child of children) {
3106
- child.setAttribute("data-reorder-item", "");
3107
+ if (reorderable) child.setAttribute("data-reorder-item", "");
3107
3108
  if (!child.hasAttribute("role")) {
3108
3109
  child.setAttribute("role", "listitem");
3109
3110
  child.setAttribute("data-reorder-generated-role", "");
package/fig.js CHANGED
@@ -4912,14 +4912,20 @@ figDefineElement("fig-toolbelt-item", FigToolbeltItem);
4912
4912
  * @attr {boolean} disabled - Disables all items
4913
4913
  * @attr {"horizontal"|"vertical"} layout - Item direction (default: horizontal)
4914
4914
  * @attr {"buttons"|"scrollbar"} overflow - Overflow controls (default: scrollbar)
4915
+ * @slot prepend - Fixed content before the scrolling viewport
4916
+ * @slot append - Fixed content after the scrolling viewport
4915
4917
  * @fires input - Selected value
4916
4918
  * @fires change - Selected value
4917
4919
  */
4918
4920
  class FigToolbelt extends HTMLElement {
4919
4921
  #boundHandleClick = this.#handleClick.bind(this);
4920
4922
  #boundHandleKeyDown = this.#handleKeyDown.bind(this);
4923
+ #boundHandleSlotChange = this.#handleSlotChange.bind(this);
4921
4924
  #boundSyncOverflow = this.#syncOverflow.bind(this);
4922
4925
  #viewport = null;
4926
+ #itemSlot = null;
4927
+ #prependSlot = null;
4928
+ #appendSlot = null;
4923
4929
  #mutationObserver = null;
4924
4930
  #resizeObserver = null;
4925
4931
  #navStart = null;
@@ -4933,6 +4939,15 @@ class FigToolbelt extends HTMLElement {
4933
4939
  const frame = document.createElement("div");
4934
4940
  frame.setAttribute("part", "frame");
4935
4941
 
4942
+ this.#prependSlot = document.createElement("slot");
4943
+ this.#prependSlot.name = "prepend";
4944
+ this.#prependSlot.setAttribute("part", "prepend");
4945
+ this.#prependSlot.hidden = true;
4946
+ this.#prependSlot.style.display = "none";
4947
+
4948
+ const scrollRegion = document.createElement("div");
4949
+ scrollRegion.setAttribute("part", "scroll-region");
4950
+
4936
4951
  const startSlot = document.createElement("slot");
4937
4952
  startSlot.name = "start";
4938
4953
  startSlot.style.display = "contents";
@@ -4940,16 +4955,31 @@ class FigToolbelt extends HTMLElement {
4940
4955
  this.#viewport = document.createElement("div");
4941
4956
  this.#viewport.className = "fig-overflow-fade";
4942
4957
  this.#viewport.setAttribute("part", "viewport");
4943
- const itemSlot = document.createElement("slot");
4944
- itemSlot.style.display = "contents";
4945
- this.#viewport.append(itemSlot);
4958
+ this.#itemSlot = document.createElement("slot");
4959
+ this.#itemSlot.style.display = "contents";
4960
+ this.#viewport.append(this.#itemSlot);
4946
4961
 
4947
4962
  const endSlot = document.createElement("slot");
4948
4963
  endSlot.name = "end";
4949
4964
  endSlot.style.display = "contents";
4950
4965
 
4951
- frame.append(startSlot, this.#viewport, endSlot);
4966
+ this.#appendSlot = document.createElement("slot");
4967
+ this.#appendSlot.name = "append";
4968
+ this.#appendSlot.setAttribute("part", "append");
4969
+ this.#appendSlot.hidden = true;
4970
+ this.#appendSlot.style.display = "none";
4971
+
4972
+ scrollRegion.append(startSlot, this.#viewport, endSlot);
4973
+ frame.append(this.#prependSlot, scrollRegion, this.#appendSlot);
4952
4974
  shadow.append(frame);
4975
+
4976
+ for (const slot of [
4977
+ this.#prependSlot,
4978
+ this.#itemSlot,
4979
+ this.#appendSlot,
4980
+ ]) {
4981
+ slot.addEventListener("slotchange", this.#boundHandleSlotChange);
4982
+ }
4953
4983
  }
4954
4984
 
4955
4985
  static get observedAttributes() {
@@ -4968,6 +4998,7 @@ class FigToolbelt extends HTMLElement {
4968
4998
 
4969
4999
  connectedCallback() {
4970
5000
  if (!this.hasAttribute("role")) this.setAttribute("role", "toolbar");
5001
+ this.#syncFixedSlots();
4971
5002
  this.#syncOrientation();
4972
5003
  this.addEventListener("click", this.#boundHandleClick);
4973
5004
  this.addEventListener("keydown", this.#boundHandleKeyDown);
@@ -5036,9 +5067,32 @@ class FigToolbelt extends HTMLElement {
5036
5067
  }
5037
5068
 
5038
5069
  #items() {
5039
- return Array.from(this.querySelectorAll("fig-toolbelt-item")).filter(
5040
- (item) => item.closest("fig-toolbelt") === this,
5041
- );
5070
+ const itemsByRegion = {
5071
+ prepend: [],
5072
+ viewport: [],
5073
+ append: [],
5074
+ };
5075
+ for (const item of this.querySelectorAll("fig-toolbelt-item")) {
5076
+ if (item.closest("fig-toolbelt") !== this) continue;
5077
+ const region = this.#itemRegion(item);
5078
+ if (region) itemsByRegion[region].push(item);
5079
+ }
5080
+ return [
5081
+ ...itemsByRegion.prepend,
5082
+ ...itemsByRegion.viewport,
5083
+ ...itemsByRegion.append,
5084
+ ];
5085
+ }
5086
+
5087
+ #itemRegion(item) {
5088
+ let topLevelChild = item;
5089
+ while (topLevelChild.parentElement && topLevelChild.parentElement !== this) {
5090
+ topLevelChild = topLevelChild.parentElement;
5091
+ }
5092
+ if (topLevelChild.parentElement !== this) return null;
5093
+ const slot = topLevelChild.getAttribute("slot") || "";
5094
+ if (slot === "prepend" || slot === "append") return slot;
5095
+ return slot ? null : "viewport";
5042
5096
  }
5043
5097
 
5044
5098
  #availableItems() {
@@ -5188,6 +5242,24 @@ class FigToolbelt extends HTMLElement {
5188
5242
  this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true }));
5189
5243
  }
5190
5244
 
5245
+ #handleSlotChange() {
5246
+ this.#syncFixedSlots();
5247
+ this.#syncSelection();
5248
+ this.#syncTabIndexes();
5249
+ requestAnimationFrame(() => {
5250
+ this.#syncOverflow();
5251
+ this.#scrollSelectedItemIntoView();
5252
+ });
5253
+ }
5254
+
5255
+ #syncFixedSlots() {
5256
+ for (const slot of [this.#prependSlot, this.#appendSlot]) {
5257
+ const hidden = slot.assignedElements({ flatten: true }).length === 0;
5258
+ slot.hidden = hidden;
5259
+ slot.style.display = hidden ? "none" : "";
5260
+ }
5261
+ }
5262
+
5191
5263
  #startObserver() {
5192
5264
  this.#mutationObserver?.disconnect();
5193
5265
  this.#mutationObserver = new MutationObserver((records) => {
@@ -5305,7 +5377,7 @@ class FigToolbelt extends HTMLElement {
5305
5377
  }
5306
5378
 
5307
5379
  #scrollItemIntoView(item, behavior = "smooth") {
5308
- if (!item) return;
5380
+ if (!item || this.#itemRegion(item) !== "viewport") return;
5309
5381
  requestAnimationFrame(() => {
5310
5382
  if (!this.isConnected || !item.isConnected) return;
5311
5383
  const isVertical = this.#isVertical;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "9.4.4",
3
+ "version": "9.4.6",
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",