@rogieking/figui3 9.3.0 → 9.4.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/.cursor/skills/fig-lab/SKILL.md +3 -3
- package/.cursor/skills/fig-lab/components.md +22 -2
- package/.cursor/skills/fig-lab/reference.md +22 -2
- package/.cursor/skills/figui3/SKILL.md +2 -2
- package/.cursor/skills/figui3/components.md +27 -0
- package/.cursor/skills/figui3/react.md +4 -1
- package/.cursor/skills/figui3/reference.md +10 -0
- package/CHANGELOG.md +3951 -0
- package/README.md +65 -0
- package/components.css +247 -2
- package/dist/components.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +5 -5
- package/fig-lab.css +115 -0
- package/fig-lab.js +447 -5
- package/fig.js +507 -0
- package/package.json +2 -4
package/fig.js
CHANGED
|
@@ -4798,6 +4798,513 @@ class FigTabs extends HTMLElement {
|
|
|
4798
4798
|
}
|
|
4799
4799
|
figDefineElement("fig-tabs", FigTabs);
|
|
4800
4800
|
|
|
4801
|
+
/* Toolbelt */
|
|
4802
|
+
/**
|
|
4803
|
+
* A semantic item group for use within FigToolbelt.
|
|
4804
|
+
*/
|
|
4805
|
+
class FigToolbeltGroup extends HTMLElement {
|
|
4806
|
+
connectedCallback() {
|
|
4807
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "group");
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
figDefineElement("fig-toolbelt-group", FigToolbeltGroup);
|
|
4811
|
+
|
|
4812
|
+
/**
|
|
4813
|
+
* A selectable item for use within FigToolbelt.
|
|
4814
|
+
* @attr {string} value - The value associated with the item
|
|
4815
|
+
* @attr {boolean} selected - Whether the item is currently selected
|
|
4816
|
+
* @attr {boolean} disabled - Whether the item is unavailable
|
|
4817
|
+
*/
|
|
4818
|
+
class FigToolbeltItem extends HTMLElement {
|
|
4819
|
+
static get observedAttributes() {
|
|
4820
|
+
return ["selected", "disabled"];
|
|
4821
|
+
}
|
|
4822
|
+
|
|
4823
|
+
connectedCallback() {
|
|
4824
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "button");
|
|
4825
|
+
if (!this.hasAttribute("tabindex")) this.setAttribute("tabindex", "-1");
|
|
4826
|
+
this.#syncA11yState();
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4829
|
+
get value() {
|
|
4830
|
+
const value = this.getAttribute("value");
|
|
4831
|
+
return value !== null ? value : this.textContent?.trim() || "";
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4834
|
+
set value(value) {
|
|
4835
|
+
this.setAttribute("value", value ?? "");
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4838
|
+
get selected() {
|
|
4839
|
+
return figBooleanAttribute(this, "selected");
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
set selected(value) {
|
|
4843
|
+
this.toggleAttribute("selected", Boolean(value));
|
|
4844
|
+
}
|
|
4845
|
+
|
|
4846
|
+
get disabled() {
|
|
4847
|
+
return figBooleanAttribute(this, "disabled");
|
|
4848
|
+
}
|
|
4849
|
+
|
|
4850
|
+
set disabled(value) {
|
|
4851
|
+
this.toggleAttribute("disabled", Boolean(value));
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
attributeChangedCallback() {
|
|
4855
|
+
this.#syncA11yState();
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4858
|
+
#syncA11yState() {
|
|
4859
|
+
const selected = figBooleanAttribute(this, "selected");
|
|
4860
|
+
const disabled = figBooleanAttribute(this, "disabled");
|
|
4861
|
+
this.setAttribute("aria-pressed", selected ? "true" : "false");
|
|
4862
|
+
if (disabled) {
|
|
4863
|
+
this.setAttribute("aria-disabled", "true");
|
|
4864
|
+
this.setAttribute("tabindex", "-1");
|
|
4865
|
+
} else {
|
|
4866
|
+
this.removeAttribute("aria-disabled");
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
figDefineElement("fig-toolbelt-item", FigToolbeltItem);
|
|
4871
|
+
|
|
4872
|
+
/**
|
|
4873
|
+
* A selectable toolbar with configurable layout and overflow controls.
|
|
4874
|
+
* @attr {string} value - The selected item value
|
|
4875
|
+
* @attr {boolean} disabled - Disables all items
|
|
4876
|
+
* @attr {"horizontal"|"vertical"} layout - Item direction (default: horizontal)
|
|
4877
|
+
* @attr {"buttons"|"scrollbar"} overflow - Overflow controls (default: scrollbar)
|
|
4878
|
+
* @fires input - Selected value
|
|
4879
|
+
* @fires change - Selected value
|
|
4880
|
+
*/
|
|
4881
|
+
class FigToolbelt extends HTMLElement {
|
|
4882
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
4883
|
+
#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
4884
|
+
#boundSyncOverflow = this.#syncOverflow.bind(this);
|
|
4885
|
+
#viewport = null;
|
|
4886
|
+
#mutationObserver = null;
|
|
4887
|
+
#resizeObserver = null;
|
|
4888
|
+
#navStart = null;
|
|
4889
|
+
#navEnd = null;
|
|
4890
|
+
#parentDisabledItems = new WeakSet();
|
|
4891
|
+
#syncingValue = false;
|
|
4892
|
+
|
|
4893
|
+
constructor() {
|
|
4894
|
+
super();
|
|
4895
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
4896
|
+
const frame = document.createElement("div");
|
|
4897
|
+
frame.setAttribute("part", "frame");
|
|
4898
|
+
|
|
4899
|
+
const startSlot = document.createElement("slot");
|
|
4900
|
+
startSlot.name = "start";
|
|
4901
|
+
startSlot.style.display = "contents";
|
|
4902
|
+
|
|
4903
|
+
this.#viewport = document.createElement("div");
|
|
4904
|
+
this.#viewport.className = "fig-overflow-fade";
|
|
4905
|
+
this.#viewport.setAttribute("part", "viewport");
|
|
4906
|
+
const itemSlot = document.createElement("slot");
|
|
4907
|
+
itemSlot.style.display = "contents";
|
|
4908
|
+
this.#viewport.append(itemSlot);
|
|
4909
|
+
|
|
4910
|
+
const endSlot = document.createElement("slot");
|
|
4911
|
+
endSlot.name = "end";
|
|
4912
|
+
endSlot.style.display = "contents";
|
|
4913
|
+
|
|
4914
|
+
frame.append(startSlot, this.#viewport, endSlot);
|
|
4915
|
+
shadow.append(frame);
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4918
|
+
static get observedAttributes() {
|
|
4919
|
+
return ["value", "disabled", "layout", "overflow"];
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4922
|
+
get #isVertical() {
|
|
4923
|
+
return this.getAttribute("layout") === "vertical";
|
|
4924
|
+
}
|
|
4925
|
+
|
|
4926
|
+
get #overflowMode() {
|
|
4927
|
+
return this.getAttribute("overflow") === "buttons"
|
|
4928
|
+
? "buttons"
|
|
4929
|
+
: "scrollbar";
|
|
4930
|
+
}
|
|
4931
|
+
|
|
4932
|
+
connectedCallback() {
|
|
4933
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "toolbar");
|
|
4934
|
+
this.#syncOrientation();
|
|
4935
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
4936
|
+
this.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
4937
|
+
this.#viewport.addEventListener("scroll", this.#boundSyncOverflow);
|
|
4938
|
+
this.#applyOverflowMode();
|
|
4939
|
+
this.#startObserver();
|
|
4940
|
+
this.#startResizeObserver();
|
|
4941
|
+
figNextFrame(this, () => {
|
|
4942
|
+
this.#syncSelection();
|
|
4943
|
+
this.#applyDisabled(figBooleanAttribute(this, "disabled"));
|
|
4944
|
+
this.#syncTabIndexes();
|
|
4945
|
+
this.#syncOverflow();
|
|
4946
|
+
this.#scrollSelectedItemIntoView(undefined, "auto");
|
|
4947
|
+
});
|
|
4948
|
+
}
|
|
4949
|
+
|
|
4950
|
+
disconnectedCallback() {
|
|
4951
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
4952
|
+
this.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
4953
|
+
this.#viewport.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
4954
|
+
this.#mutationObserver?.disconnect();
|
|
4955
|
+
this.#mutationObserver = null;
|
|
4956
|
+
this.#resizeObserver?.disconnect();
|
|
4957
|
+
this.#resizeObserver = null;
|
|
4958
|
+
this.#removeNavButtons();
|
|
4959
|
+
}
|
|
4960
|
+
|
|
4961
|
+
get value() {
|
|
4962
|
+
return this.selectedItem?.value || "";
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
set value(value) {
|
|
4966
|
+
if (value === null || value === undefined) {
|
|
4967
|
+
this.removeAttribute("value");
|
|
4968
|
+
} else {
|
|
4969
|
+
this.setAttribute("value", String(value));
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4973
|
+
get selectedItem() {
|
|
4974
|
+
return this.#items().find((item) => figBooleanAttribute(item, "selected")) || null;
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4977
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
4978
|
+
if (oldValue === newValue) return;
|
|
4979
|
+
if (name === "value" && !this.#syncingValue) {
|
|
4980
|
+
this.#selectByValue(newValue);
|
|
4981
|
+
}
|
|
4982
|
+
if (name === "disabled") {
|
|
4983
|
+
this.#applyDisabled(newValue !== null && newValue !== "false");
|
|
4984
|
+
}
|
|
4985
|
+
if (name === "overflow") {
|
|
4986
|
+
this.#applyOverflowMode();
|
|
4987
|
+
requestAnimationFrame(() => this.#syncOverflow());
|
|
4988
|
+
}
|
|
4989
|
+
if (name === "layout") {
|
|
4990
|
+
this.#syncOrientation();
|
|
4991
|
+
if (this.#isVertical) this.#viewport.scrollLeft = 0;
|
|
4992
|
+
else this.#viewport.scrollTop = 0;
|
|
4993
|
+
this.#applyOverflowMode();
|
|
4994
|
+
requestAnimationFrame(() => {
|
|
4995
|
+
this.#syncOverflow();
|
|
4996
|
+
this.#scrollSelectedItemIntoView(undefined, "auto");
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
#items() {
|
|
5002
|
+
return Array.from(this.querySelectorAll("fig-toolbelt-item")).filter(
|
|
5003
|
+
(item) => item.closest("fig-toolbelt") === this,
|
|
5004
|
+
);
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
#availableItems() {
|
|
5008
|
+
return this.#items().filter((item) => !figBooleanAttribute(item, "disabled"));
|
|
5009
|
+
}
|
|
5010
|
+
|
|
5011
|
+
#applyDisabled(disabled) {
|
|
5012
|
+
if (disabled) this.setAttribute("aria-disabled", "true");
|
|
5013
|
+
else this.removeAttribute("aria-disabled");
|
|
5014
|
+
for (const item of this.#items()) {
|
|
5015
|
+
if (disabled) {
|
|
5016
|
+
if (!figBooleanAttribute(item, "disabled")) {
|
|
5017
|
+
this.#parentDisabledItems.add(item);
|
|
5018
|
+
item.setAttribute("disabled", "");
|
|
5019
|
+
}
|
|
5020
|
+
} else if (this.#parentDisabledItems.has(item)) {
|
|
5021
|
+
this.#parentDisabledItems.delete(item);
|
|
5022
|
+
item.removeAttribute("disabled");
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
this.#syncTabIndexes();
|
|
5026
|
+
}
|
|
5027
|
+
|
|
5028
|
+
#syncSelection() {
|
|
5029
|
+
const declaredValue = this.getAttribute("value");
|
|
5030
|
+
if (declaredValue !== null) {
|
|
5031
|
+
this.#selectByValue(declaredValue);
|
|
5032
|
+
return;
|
|
5033
|
+
}
|
|
5034
|
+
|
|
5035
|
+
const selectedItems = this.#items().filter((item) =>
|
|
5036
|
+
figBooleanAttribute(item, "selected"),
|
|
5037
|
+
);
|
|
5038
|
+
const selected = selectedItems[0] || null;
|
|
5039
|
+
for (const item of selectedItems.slice(1)) {
|
|
5040
|
+
item.removeAttribute("selected");
|
|
5041
|
+
}
|
|
5042
|
+
if (selected) this.#reflectValue(selected.value);
|
|
5043
|
+
}
|
|
5044
|
+
|
|
5045
|
+
#selectByValue(value) {
|
|
5046
|
+
const target =
|
|
5047
|
+
value === null
|
|
5048
|
+
? null
|
|
5049
|
+
: this.#items().find((item) => item.value === value) || null;
|
|
5050
|
+
for (const item of this.#items()) {
|
|
5051
|
+
item.toggleAttribute("selected", item === target);
|
|
5052
|
+
}
|
|
5053
|
+
this.#syncTabIndexes(target);
|
|
5054
|
+
if (target) this.#scrollSelectedItemIntoView(target);
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
#selectItem(target, { emit = false, focus = false } = {}) {
|
|
5058
|
+
if (!target || figBooleanAttribute(target, "disabled")) return false;
|
|
5059
|
+
const previousItem = this.selectedItem;
|
|
5060
|
+
const previousValue = this.value;
|
|
5061
|
+
for (const item of this.#items()) {
|
|
5062
|
+
item.toggleAttribute("selected", item === target);
|
|
5063
|
+
}
|
|
5064
|
+
this.#reflectValue(target.value);
|
|
5065
|
+
this.#syncTabIndexes(target);
|
|
5066
|
+
if (focus) target.focus();
|
|
5067
|
+
this.#scrollSelectedItemIntoView(target);
|
|
5068
|
+
if (emit && (previousItem !== target || previousValue !== target.value)) {
|
|
5069
|
+
this.#emitSelectionEvents();
|
|
5070
|
+
}
|
|
5071
|
+
return true;
|
|
5072
|
+
}
|
|
5073
|
+
|
|
5074
|
+
#reflectValue(value) {
|
|
5075
|
+
this.#syncingValue = true;
|
|
5076
|
+
this.setAttribute("value", value);
|
|
5077
|
+
this.#syncingValue = false;
|
|
5078
|
+
}
|
|
5079
|
+
|
|
5080
|
+
#syncTabIndexes(preferredItem) {
|
|
5081
|
+
const items = this.#items();
|
|
5082
|
+
const availableItems = this.#availableItems();
|
|
5083
|
+
const active =
|
|
5084
|
+
(preferredItem && availableItems.includes(preferredItem) && preferredItem) ||
|
|
5085
|
+
(availableItems.includes(document.activeElement) && document.activeElement) ||
|
|
5086
|
+
(availableItems.includes(this.selectedItem) && this.selectedItem) ||
|
|
5087
|
+
availableItems[0] ||
|
|
5088
|
+
null;
|
|
5089
|
+
for (const item of items) {
|
|
5090
|
+
item.setAttribute("tabindex", item === active ? "0" : "-1");
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
|
|
5094
|
+
#handleClick(event) {
|
|
5095
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
5096
|
+
const target = event.target.closest?.("fig-toolbelt-item");
|
|
5097
|
+
if (!target || target.closest("fig-toolbelt") !== this) return;
|
|
5098
|
+
this.#selectItem(target, { emit: true });
|
|
5099
|
+
}
|
|
5100
|
+
|
|
5101
|
+
#handleKeyDown(event) {
|
|
5102
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
5103
|
+
const target = event.target.closest?.("fig-toolbelt-item");
|
|
5104
|
+
if (!target || target.closest("fig-toolbelt") !== this) return;
|
|
5105
|
+
|
|
5106
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
5107
|
+
event.preventDefault();
|
|
5108
|
+
this.#selectItem(target, { emit: true });
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5112
|
+
const items = this.#availableItems();
|
|
5113
|
+
if (!items.length) return;
|
|
5114
|
+
const currentIndex = Math.max(0, items.indexOf(target));
|
|
5115
|
+
let nextIndex = currentIndex;
|
|
5116
|
+
switch (event.key) {
|
|
5117
|
+
case "ArrowLeft":
|
|
5118
|
+
if (this.#isVertical) return;
|
|
5119
|
+
nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
5120
|
+
break;
|
|
5121
|
+
case "ArrowUp":
|
|
5122
|
+
if (!this.#isVertical) return;
|
|
5123
|
+
nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
5124
|
+
break;
|
|
5125
|
+
case "ArrowRight":
|
|
5126
|
+
if (this.#isVertical) return;
|
|
5127
|
+
nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
5128
|
+
break;
|
|
5129
|
+
case "ArrowDown":
|
|
5130
|
+
if (!this.#isVertical) return;
|
|
5131
|
+
nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
5132
|
+
break;
|
|
5133
|
+
case "Home":
|
|
5134
|
+
nextIndex = 0;
|
|
5135
|
+
break;
|
|
5136
|
+
case "End":
|
|
5137
|
+
nextIndex = items.length - 1;
|
|
5138
|
+
break;
|
|
5139
|
+
default:
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
event.preventDefault();
|
|
5143
|
+
this.#syncTabIndexes(items[nextIndex]);
|
|
5144
|
+
items[nextIndex].focus();
|
|
5145
|
+
this.#scrollItemIntoView(items[nextIndex]);
|
|
5146
|
+
}
|
|
5147
|
+
|
|
5148
|
+
#emitSelectionEvents() {
|
|
5149
|
+
const detail = this.value;
|
|
5150
|
+
this.dispatchEvent(new CustomEvent("input", { detail, bubbles: true }));
|
|
5151
|
+
this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true }));
|
|
5152
|
+
}
|
|
5153
|
+
|
|
5154
|
+
#startObserver() {
|
|
5155
|
+
this.#mutationObserver?.disconnect();
|
|
5156
|
+
this.#mutationObserver = new MutationObserver((records) => {
|
|
5157
|
+
const authoredChange = records.some(
|
|
5158
|
+
(record) =>
|
|
5159
|
+
record.type === "childList" ||
|
|
5160
|
+
(record.target instanceof Element &&
|
|
5161
|
+
record.target.matches("fig-toolbelt-item") &&
|
|
5162
|
+
record.target.closest("fig-toolbelt") === this),
|
|
5163
|
+
);
|
|
5164
|
+
if (!authoredChange) return;
|
|
5165
|
+
this.#applyOverflowMode();
|
|
5166
|
+
this.#syncSelection();
|
|
5167
|
+
this.#applyDisabled(figBooleanAttribute(this, "disabled"));
|
|
5168
|
+
this.#syncTabIndexes();
|
|
5169
|
+
requestAnimationFrame(() => {
|
|
5170
|
+
this.#syncOverflow();
|
|
5171
|
+
this.#scrollSelectedItemIntoView();
|
|
5172
|
+
});
|
|
5173
|
+
});
|
|
5174
|
+
this.#mutationObserver.observe(this, {
|
|
5175
|
+
childList: true,
|
|
5176
|
+
attributes: true,
|
|
5177
|
+
subtree: true,
|
|
5178
|
+
attributeFilter: ["value", "selected", "disabled"],
|
|
5179
|
+
});
|
|
5180
|
+
}
|
|
5181
|
+
|
|
5182
|
+
#startResizeObserver() {
|
|
5183
|
+
this.#resizeObserver?.disconnect();
|
|
5184
|
+
this.#resizeObserver = new ResizeObserver(() => this.#syncOverflow());
|
|
5185
|
+
this.#resizeObserver.observe(this);
|
|
5186
|
+
this.#resizeObserver.observe(this.#viewport);
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5189
|
+
#syncOverflow() {
|
|
5190
|
+
if (this.#overflowMode === "scrollbar") return;
|
|
5191
|
+
figSyncOverflowState(
|
|
5192
|
+
this,
|
|
5193
|
+
this.#viewport,
|
|
5194
|
+
this.#isVertical ? "y" : "x",
|
|
5195
|
+
);
|
|
5196
|
+
}
|
|
5197
|
+
|
|
5198
|
+
#syncOrientation() {
|
|
5199
|
+
this.setAttribute(
|
|
5200
|
+
"aria-orientation",
|
|
5201
|
+
this.#isVertical ? "vertical" : "horizontal",
|
|
5202
|
+
);
|
|
5203
|
+
this.#viewport.classList.add("fig-overflow-fade");
|
|
5204
|
+
this.#viewport.classList.toggle(
|
|
5205
|
+
"fig-overflow-fade-horizontal",
|
|
5206
|
+
!this.#isVertical,
|
|
5207
|
+
);
|
|
5208
|
+
}
|
|
5209
|
+
|
|
5210
|
+
#applyOverflowMode() {
|
|
5211
|
+
if (this.#overflowMode === "scrollbar") {
|
|
5212
|
+
this.#removeNavButtons();
|
|
5213
|
+
} else {
|
|
5214
|
+
this.#createNavButtons();
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
|
|
5218
|
+
#createNavButtons() {
|
|
5219
|
+
if (
|
|
5220
|
+
this.#navStart &&
|
|
5221
|
+
this.#navEnd &&
|
|
5222
|
+
this.contains(this.#navStart) &&
|
|
5223
|
+
this.contains(this.#navEnd)
|
|
5224
|
+
) {
|
|
5225
|
+
this.#positionNavButtons();
|
|
5226
|
+
return;
|
|
5227
|
+
}
|
|
5228
|
+
this.#navStart?.remove();
|
|
5229
|
+
this.#navEnd?.remove();
|
|
5230
|
+
const buttons = createFigOverflowButtons({
|
|
5231
|
+
owner: "toolbelt",
|
|
5232
|
+
onStart: () => this.#scrollByPage(-1),
|
|
5233
|
+
onEnd: () => this.#scrollByPage(1),
|
|
5234
|
+
});
|
|
5235
|
+
this.#navStart = buttons.start;
|
|
5236
|
+
this.#navEnd = buttons.end;
|
|
5237
|
+
this.#navStart.setAttribute("slot", "start");
|
|
5238
|
+
this.#navEnd.setAttribute("slot", "end");
|
|
5239
|
+
this.#positionNavButtons();
|
|
5240
|
+
}
|
|
5241
|
+
|
|
5242
|
+
#positionNavButtons() {
|
|
5243
|
+
if (this.firstElementChild !== this.#navStart) this.prepend(this.#navStart);
|
|
5244
|
+
if (this.lastElementChild !== this.#navEnd) this.append(this.#navEnd);
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
#removeNavButtons() {
|
|
5248
|
+
this.#navStart?.remove();
|
|
5249
|
+
this.#navEnd?.remove();
|
|
5250
|
+
this.#navStart = null;
|
|
5251
|
+
this.#navEnd = null;
|
|
5252
|
+
this.classList.remove("overflow-start", "overflow-end");
|
|
5253
|
+
}
|
|
5254
|
+
|
|
5255
|
+
#scrollByPage(direction) {
|
|
5256
|
+
figScrollOverflowPage(
|
|
5257
|
+
this.#viewport,
|
|
5258
|
+
this.#isVertical ? "y" : "x",
|
|
5259
|
+
direction,
|
|
5260
|
+
);
|
|
5261
|
+
}
|
|
5262
|
+
|
|
5263
|
+
#scrollSelectedItemIntoView(
|
|
5264
|
+
item = this.selectedItem,
|
|
5265
|
+
behavior = "smooth",
|
|
5266
|
+
) {
|
|
5267
|
+
if (item) this.#scrollItemIntoView(item, behavior);
|
|
5268
|
+
}
|
|
5269
|
+
|
|
5270
|
+
#scrollItemIntoView(item, behavior = "smooth") {
|
|
5271
|
+
if (!item) return;
|
|
5272
|
+
requestAnimationFrame(() => {
|
|
5273
|
+
if (!this.isConnected || !item.isConnected) return;
|
|
5274
|
+
const isVertical = this.#isVertical;
|
|
5275
|
+
const hasOverflow = isVertical
|
|
5276
|
+
? this.#viewport.scrollHeight > this.#viewport.clientHeight
|
|
5277
|
+
: this.#viewport.scrollWidth > this.#viewport.clientWidth;
|
|
5278
|
+
if (!hasOverflow) return;
|
|
5279
|
+
const containerRect = this.#viewport.getBoundingClientRect();
|
|
5280
|
+
const itemRect = item.getBoundingClientRect();
|
|
5281
|
+
if (isVertical) {
|
|
5282
|
+
const itemCenter =
|
|
5283
|
+
itemRect.top -
|
|
5284
|
+
containerRect.top +
|
|
5285
|
+
this.#viewport.scrollTop +
|
|
5286
|
+
itemRect.height / 2;
|
|
5287
|
+
this.#viewport.scrollTo({
|
|
5288
|
+
top: itemCenter - this.#viewport.clientHeight / 2,
|
|
5289
|
+
behavior,
|
|
5290
|
+
});
|
|
5291
|
+
} else {
|
|
5292
|
+
const itemCenter =
|
|
5293
|
+
itemRect.left -
|
|
5294
|
+
containerRect.left +
|
|
5295
|
+
this.#viewport.scrollLeft +
|
|
5296
|
+
itemRect.width / 2;
|
|
5297
|
+
this.#viewport.scrollTo({
|
|
5298
|
+
left: itemCenter - this.#viewport.clientWidth / 2,
|
|
5299
|
+
behavior,
|
|
5300
|
+
});
|
|
5301
|
+
}
|
|
5302
|
+
this.#syncOverflow();
|
|
5303
|
+
});
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
figDefineElement("fig-toolbelt", FigToolbelt);
|
|
5307
|
+
|
|
4801
5308
|
/* Segmented Control */
|
|
4802
5309
|
/**
|
|
4803
5310
|
* A custom segment element for use within FigSegmentedControl.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rogieking/figui3",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.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": "SEE LICENSE IN LICENSE",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"dist/",
|
|
46
46
|
".cursor/skills/",
|
|
47
47
|
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
48
49
|
"LICENSE"
|
|
49
50
|
],
|
|
50
51
|
"sideEffects": [
|
|
@@ -108,8 +109,5 @@
|
|
|
108
109
|
"lightningcss": "^1.32.0",
|
|
109
110
|
"lightningcss-cli": "^1.32.0",
|
|
110
111
|
"playwright": "^1.58.2"
|
|
111
|
-
},
|
|
112
|
-
"peerDependencies": {
|
|
113
|
-
"typescript": "^5.0.0"
|
|
114
112
|
}
|
|
115
113
|
}
|