@rogieking/figui3 4.15.1 → 4.15.3
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/README.md +4 -1
- package/components.css +4 -1
- package/dist/components.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +42 -42
- package/fig.js +294 -30
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -1784,6 +1784,10 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1784
1784
|
this.hasAttribute("popover") &&
|
|
1785
1785
|
typeof this.showPopover === "function" &&
|
|
1786
1786
|
!this.matches?.(":popover-open");
|
|
1787
|
+
const positionBeforeReveal = this.shouldAutoReposition();
|
|
1788
|
+
if (positionBeforeReveal) {
|
|
1789
|
+
this.style.visibility = "hidden";
|
|
1790
|
+
}
|
|
1787
1791
|
if (usePopover) {
|
|
1788
1792
|
try {
|
|
1789
1793
|
this.showPopover();
|
|
@@ -1798,6 +1802,10 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1798
1802
|
// Ignore when dialog cannot be shown yet.
|
|
1799
1803
|
}
|
|
1800
1804
|
}
|
|
1805
|
+
if (positionBeforeReveal && (this.matches?.(":open") || this.matches?.(":popover-open"))) {
|
|
1806
|
+
this.positionPopup();
|
|
1807
|
+
this.style.visibility = "";
|
|
1808
|
+
}
|
|
1801
1809
|
|
|
1802
1810
|
this.setupObservers();
|
|
1803
1811
|
document.addEventListener(
|
|
@@ -1817,6 +1825,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1817
1825
|
const anchor = this.resolveAnchor();
|
|
1818
1826
|
if (anchor?.classList) anchor.classList.remove("has-popup-open");
|
|
1819
1827
|
|
|
1828
|
+
this.style.visibility = "";
|
|
1820
1829
|
this._isPopupActive = false;
|
|
1821
1830
|
this._wasDragged = false;
|
|
1822
1831
|
this.teardownObservers();
|
|
@@ -1837,7 +1846,10 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1837
1846
|
// Ignore.
|
|
1838
1847
|
}
|
|
1839
1848
|
}
|
|
1840
|
-
|
|
1849
|
+
// Use :open, not super.open — the custom open getter reads the attribute
|
|
1850
|
+
// removed just before hidePopup(), so super.open can be false while the
|
|
1851
|
+
// native dialog is still open and no "close" event fires.
|
|
1852
|
+
if (this.matches?.(":open")) {
|
|
1841
1853
|
try {
|
|
1842
1854
|
this.close();
|
|
1843
1855
|
} catch (e) {
|
|
@@ -2484,7 +2496,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2484
2496
|
}
|
|
2485
2497
|
|
|
2486
2498
|
positionPopup() {
|
|
2487
|
-
if (!this.open || !
|
|
2499
|
+
if (!this.open || !this.matches?.(":open")) return;
|
|
2488
2500
|
|
|
2489
2501
|
const popupRect = this.getBoundingClientRect();
|
|
2490
2502
|
const offset = this.parseOffset();
|
|
@@ -5123,6 +5135,14 @@ class FigInputColor extends HTMLElement {
|
|
|
5123
5135
|
// Setup swatch (native picker)
|
|
5124
5136
|
if (this.#swatch) {
|
|
5125
5137
|
this.#swatch.disabled = this.hasAttribute("disabled");
|
|
5138
|
+
const swatchInput = this.#swatch.querySelector('input[type="color"]');
|
|
5139
|
+
if (this.#textInput || this.hasAttribute("swatch-disabled")) {
|
|
5140
|
+
swatchInput?.setAttribute("tabindex", "-1");
|
|
5141
|
+
}
|
|
5142
|
+
if (this.hasAttribute("swatch-disabled")) {
|
|
5143
|
+
swatchInput?.setAttribute("disabled", "");
|
|
5144
|
+
if (swatchInput) swatchInput.style.pointerEvents = "none";
|
|
5145
|
+
}
|
|
5126
5146
|
this.#swatch.addEventListener("input", this.#handleInput.bind(this));
|
|
5127
5147
|
}
|
|
5128
5148
|
|
|
@@ -5240,7 +5260,11 @@ class FigInputColor extends HTMLElement {
|
|
|
5240
5260
|
}
|
|
5241
5261
|
|
|
5242
5262
|
focus() {
|
|
5243
|
-
this.#
|
|
5263
|
+
if (this.#textInput) {
|
|
5264
|
+
this.#textInput.focus();
|
|
5265
|
+
return;
|
|
5266
|
+
}
|
|
5267
|
+
this.#swatch?.focus();
|
|
5244
5268
|
}
|
|
5245
5269
|
|
|
5246
5270
|
#handleInput(event) {
|
|
@@ -6618,6 +6642,14 @@ class FigInputPalette extends HTMLElement {
|
|
|
6618
6642
|
|
|
6619
6643
|
const inlineWrap = document.createElement("div");
|
|
6620
6644
|
inlineWrap.className = "palette-colors-inline";
|
|
6645
|
+
inlineWrap.addEventListener("click", () => {
|
|
6646
|
+
if (
|
|
6647
|
+
this.hasAttribute("disabled") &&
|
|
6648
|
+
this.getAttribute("disabled") !== "false"
|
|
6649
|
+
)
|
|
6650
|
+
return;
|
|
6651
|
+
this.open = true;
|
|
6652
|
+
});
|
|
6621
6653
|
|
|
6622
6654
|
const wrap = document.createElement("div");
|
|
6623
6655
|
wrap.className = "palette-colors";
|
|
@@ -6655,6 +6687,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
6655
6687
|
if (inline) {
|
|
6656
6688
|
ic.setAttribute("text", "false");
|
|
6657
6689
|
ic.setAttribute("alpha", "true");
|
|
6690
|
+
ic.setAttribute("swatch-disabled", "");
|
|
6658
6691
|
} else {
|
|
6659
6692
|
ic.setAttribute("text", "true");
|
|
6660
6693
|
ic.setAttribute("alpha", this.#isFixed ? "true" : "false");
|
|
@@ -6712,7 +6745,10 @@ class FigInputPalette extends HTMLElement {
|
|
|
6712
6745
|
if (this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false") return;
|
|
6713
6746
|
this.#removeColor(index);
|
|
6714
6747
|
});
|
|
6715
|
-
|
|
6748
|
+
const tooltip = document.createElement("fig-tooltip");
|
|
6749
|
+
tooltip.setAttribute("text", "Remove color");
|
|
6750
|
+
tooltip.appendChild(btn);
|
|
6751
|
+
return tooltip;
|
|
6716
6752
|
}
|
|
6717
6753
|
|
|
6718
6754
|
#removeColor(index) {
|
|
@@ -6742,6 +6778,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
6742
6778
|
)
|
|
6743
6779
|
return;
|
|
6744
6780
|
if (this.#colors.length >= this.#max) return;
|
|
6781
|
+
this.open = true;
|
|
6745
6782
|
this.#addColor({ color: "#D9D9D9", alpha: 1 });
|
|
6746
6783
|
});
|
|
6747
6784
|
const tooltip = document.createElement("fig-tooltip");
|
|
@@ -6775,6 +6812,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
6775
6812
|
const addBtn = this.querySelector(".palette-add-btn");
|
|
6776
6813
|
if (addBtn) addBtn.setAttribute("disabled", "");
|
|
6777
6814
|
}
|
|
6815
|
+
this.#syncRemoveButtons(disabled);
|
|
6778
6816
|
this.#emitChange();
|
|
6779
6817
|
}
|
|
6780
6818
|
|
|
@@ -6814,9 +6852,18 @@ class FigInputPalette extends HTMLElement {
|
|
|
6814
6852
|
});
|
|
6815
6853
|
const addBtn = this.querySelector(".palette-add-btn");
|
|
6816
6854
|
if (addBtn) {
|
|
6817
|
-
if (disabled) addBtn.setAttribute("disabled", "");
|
|
6855
|
+
if (disabled || this.#colors.length >= this.#max) addBtn.setAttribute("disabled", "");
|
|
6818
6856
|
else addBtn.removeAttribute("disabled");
|
|
6819
6857
|
}
|
|
6858
|
+
this.#syncRemoveButtons(disabled);
|
|
6859
|
+
}
|
|
6860
|
+
|
|
6861
|
+
#syncRemoveButtons(disabled = this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false") {
|
|
6862
|
+
const shouldDisable = disabled || this.#colors.length <= this.#min;
|
|
6863
|
+
this.querySelectorAll(".palette-remove-btn").forEach((btn) => {
|
|
6864
|
+
if (shouldDisable) btn.setAttribute("disabled", "");
|
|
6865
|
+
else btn.removeAttribute("disabled");
|
|
6866
|
+
});
|
|
6820
6867
|
}
|
|
6821
6868
|
|
|
6822
6869
|
#emitInput() {
|
|
@@ -7038,7 +7085,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
7038
7085
|
return this.#gradient.stops
|
|
7039
7086
|
.map(
|
|
7040
7087
|
(stop, i) =>
|
|
7041
|
-
`<fig-tooltip action="manual" text="${Math.round(stop.position)}%"><fig-handle drag drag-axes="x" drag-surface=".fig-input-gradient-track" type="color" color="${this.#stopColorCSS(stop)}" value="${stop.position}% 50%" hit-area="4" data-stop-index="${i}"${disabled ? " disabled" : ""}></fig-handle></fig-tooltip>`,
|
|
7088
|
+
`<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>`,
|
|
7042
7089
|
)
|
|
7043
7090
|
.join("");
|
|
7044
7091
|
}
|
|
@@ -7119,6 +7166,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
7119
7166
|
const ghost = document.createElement("fig-handle");
|
|
7120
7167
|
ghost.classList.add("fig-input-gradient-ghost");
|
|
7121
7168
|
ghost.setAttribute("type", "color");
|
|
7169
|
+
ghost.setAttribute("color-tip", "");
|
|
7122
7170
|
ghost.setAttribute("control", "add");
|
|
7123
7171
|
ghost.style.position = "absolute";
|
|
7124
7172
|
ghost.style.top = "50%";
|
|
@@ -12123,6 +12171,14 @@ class FigFillPicker extends HTMLElement {
|
|
|
12123
12171
|
});
|
|
12124
12172
|
}
|
|
12125
12173
|
|
|
12174
|
+
open() {
|
|
12175
|
+
this.#openDialog();
|
|
12176
|
+
}
|
|
12177
|
+
|
|
12178
|
+
close() {
|
|
12179
|
+
if (this.#dialog) this.#dialog.open = false;
|
|
12180
|
+
}
|
|
12181
|
+
|
|
12126
12182
|
#createDialog() {
|
|
12127
12183
|
// Collect slotted custom mode content before any DOM changes
|
|
12128
12184
|
this.#customSlots = {};
|
|
@@ -12270,6 +12326,7 @@ class FigFillPicker extends HTMLElement {
|
|
|
12270
12326
|
const onDialogClose = () => {
|
|
12271
12327
|
if (this.#chit) this.#chit.removeAttribute("selected");
|
|
12272
12328
|
this.#emitChange();
|
|
12329
|
+
this.dispatchEvent(new CustomEvent("close"));
|
|
12273
12330
|
};
|
|
12274
12331
|
this.#dialog.addEventListener("close", onDialogClose);
|
|
12275
12332
|
|
|
@@ -14936,6 +14993,7 @@ class FigHandle extends HTMLElement {
|
|
|
14936
14993
|
"value",
|
|
14937
14994
|
"type",
|
|
14938
14995
|
"control",
|
|
14996
|
+
"color-tip",
|
|
14939
14997
|
"hit-area",
|
|
14940
14998
|
"hit-area-mode",
|
|
14941
14999
|
];
|
|
@@ -14945,6 +15003,7 @@ class FigHandle extends HTMLElement {
|
|
|
14945
15003
|
#boundPointerDown = null;
|
|
14946
15004
|
#applyingValue = false;
|
|
14947
15005
|
#colorTip = null;
|
|
15006
|
+
#directColorPicker = null;
|
|
14948
15007
|
#hitAreaEl = null;
|
|
14949
15008
|
|
|
14950
15009
|
get #controlMode() {
|
|
@@ -14955,6 +15014,14 @@ class FigHandle extends HTMLElement {
|
|
|
14955
15014
|
return this.#controlMode === "add" || this.#controlMode === "remove";
|
|
14956
15015
|
}
|
|
14957
15016
|
|
|
15017
|
+
get #usesColorTip() {
|
|
15018
|
+
return (
|
|
15019
|
+
this.#hasControlMode ||
|
|
15020
|
+
(this.hasAttribute("color-tip") &&
|
|
15021
|
+
this.getAttribute("color-tip") !== "false")
|
|
15022
|
+
);
|
|
15023
|
+
}
|
|
15024
|
+
|
|
14958
15025
|
get #isGhost() {
|
|
14959
15026
|
return this.classList.contains("fig-input-gradient-ghost");
|
|
14960
15027
|
}
|
|
@@ -15174,6 +15241,7 @@ class FigHandle extends HTMLElement {
|
|
|
15174
15241
|
disconnectedCallback() {
|
|
15175
15242
|
this.#teardownDrag();
|
|
15176
15243
|
this.#hideColorTip();
|
|
15244
|
+
this.#removeDirectColorPicker();
|
|
15177
15245
|
if (this.#hitAreaEl) {
|
|
15178
15246
|
this.#hitAreaEl.remove();
|
|
15179
15247
|
this.#hitAreaEl = null;
|
|
@@ -15186,7 +15254,7 @@ class FigHandle extends HTMLElement {
|
|
|
15186
15254
|
select() {
|
|
15187
15255
|
if (this.hasAttribute("disabled")) return;
|
|
15188
15256
|
this.setAttribute("selected", "");
|
|
15189
|
-
if (this.getAttribute("type") === "color" && !this.#isDragging)
|
|
15257
|
+
if (this.getAttribute("type") === "color" && !this.#isDragging && this.#usesColorTip)
|
|
15190
15258
|
this.#showColorTip();
|
|
15191
15259
|
}
|
|
15192
15260
|
|
|
@@ -15201,23 +15269,30 @@ class FigHandle extends HTMLElement {
|
|
|
15201
15269
|
this.#didDrag = false;
|
|
15202
15270
|
return;
|
|
15203
15271
|
}
|
|
15272
|
+
if (this.getAttribute("type") === "color" && !this.#usesColorTip) {
|
|
15273
|
+
this.#openDirectColorPicker();
|
|
15274
|
+
return;
|
|
15275
|
+
}
|
|
15204
15276
|
this.select();
|
|
15205
15277
|
};
|
|
15206
15278
|
|
|
15207
15279
|
#handleDeselect = (e) => {
|
|
15208
15280
|
if (this.#hasControlMode) return;
|
|
15209
15281
|
if (this.contains(e.target)) return;
|
|
15210
|
-
if (this.#colorTip && e.target.closest?.("dialog, [popover]")) return;
|
|
15282
|
+
if ((this.#colorTip || this.#directColorPicker) && e.target.closest?.("dialog, [popover]")) return;
|
|
15211
15283
|
this.deselect();
|
|
15212
15284
|
};
|
|
15213
15285
|
|
|
15214
15286
|
#handleKeyDown = (e) => {
|
|
15215
|
-
if (e.key !== "Enter") return;
|
|
15287
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
15216
15288
|
if (!this.hasAttribute("selected")) return;
|
|
15217
15289
|
if (this.getAttribute("type") !== "color") return;
|
|
15218
|
-
if (this.#colorTip) return;
|
|
15219
15290
|
e.preventDefault();
|
|
15220
|
-
this.#
|
|
15291
|
+
if (this.#usesColorTip) {
|
|
15292
|
+
if (!this.#colorTip) this.#showColorTip();
|
|
15293
|
+
} else {
|
|
15294
|
+
this.#openDirectColorPicker();
|
|
15295
|
+
}
|
|
15221
15296
|
};
|
|
15222
15297
|
|
|
15223
15298
|
attributeChangedCallback(name, _old, value) {
|
|
@@ -15230,6 +15305,7 @@ class FigHandle extends HTMLElement {
|
|
|
15230
15305
|
if (this.#colorTip && value) {
|
|
15231
15306
|
this.#colorTip.setAttribute("value", value);
|
|
15232
15307
|
}
|
|
15308
|
+
this.#syncDirectColorPickerValue();
|
|
15233
15309
|
}
|
|
15234
15310
|
if (name === "drag") this.#syncDrag();
|
|
15235
15311
|
if (name === "hit-area") this.#syncHitArea();
|
|
@@ -15238,12 +15314,20 @@ class FigHandle extends HTMLElement {
|
|
|
15238
15314
|
}
|
|
15239
15315
|
if (name === "control") {
|
|
15240
15316
|
if (this.#hasControlMode) {
|
|
15317
|
+
this.#removeDirectColorPicker();
|
|
15241
15318
|
this.#hideColorTip();
|
|
15242
15319
|
this.#showColorTip();
|
|
15243
15320
|
} else {
|
|
15244
15321
|
this.#hideColorTip();
|
|
15245
15322
|
}
|
|
15246
15323
|
}
|
|
15324
|
+
if (name === "color-tip") {
|
|
15325
|
+
if (this.#usesColorTip) {
|
|
15326
|
+
this.#removeDirectColorPicker();
|
|
15327
|
+
} else {
|
|
15328
|
+
this.#hideColorTip();
|
|
15329
|
+
}
|
|
15330
|
+
}
|
|
15247
15331
|
}
|
|
15248
15332
|
|
|
15249
15333
|
#syncDrag() {
|
|
@@ -15400,6 +15484,117 @@ class FigHandle extends HTMLElement {
|
|
|
15400
15484
|
this.#colorTip.style.display = "none";
|
|
15401
15485
|
}
|
|
15402
15486
|
|
|
15487
|
+
#normalizeColorForPicker(rawValue = this.getAttribute("color")) {
|
|
15488
|
+
const fallback = { color: "#D9D9D9", opacity: 100 };
|
|
15489
|
+
const value = String(rawValue || "").trim();
|
|
15490
|
+
if (!value) return fallback;
|
|
15491
|
+
|
|
15492
|
+
const normalizeHex = (hex) => {
|
|
15493
|
+
const raw = hex.replace("#", "").trim();
|
|
15494
|
+
if (raw.length === 3 || raw.length === 4) {
|
|
15495
|
+
const [r, g, b, a] = raw;
|
|
15496
|
+
return {
|
|
15497
|
+
color: `#${r}${r}${g}${g}${b}${b}`.toUpperCase(),
|
|
15498
|
+
opacity: a ? Math.round((parseInt(`${a}${a}`, 16) / 255) * 100) : 100,
|
|
15499
|
+
};
|
|
15500
|
+
}
|
|
15501
|
+
if (raw.length === 6 || raw.length === 8) {
|
|
15502
|
+
return {
|
|
15503
|
+
color: `#${raw.slice(0, 6)}`.toUpperCase(),
|
|
15504
|
+
opacity:
|
|
15505
|
+
raw.length === 8
|
|
15506
|
+
? Math.round((parseInt(raw.slice(6, 8), 16) / 255) * 100)
|
|
15507
|
+
: 100,
|
|
15508
|
+
};
|
|
15509
|
+
}
|
|
15510
|
+
return fallback;
|
|
15511
|
+
};
|
|
15512
|
+
|
|
15513
|
+
const rgbToHex = (r, g, b) => {
|
|
15514
|
+
const toHex = (v) =>
|
|
15515
|
+
Math.max(0, Math.min(255, Math.round(Number(v))))
|
|
15516
|
+
.toString(16)
|
|
15517
|
+
.padStart(2, "0");
|
|
15518
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
|
|
15519
|
+
};
|
|
15520
|
+
|
|
15521
|
+
if (value.startsWith("#")) return normalizeHex(value);
|
|
15522
|
+
|
|
15523
|
+
try {
|
|
15524
|
+
const { ctx } = figGetSharedCanvas(1, 1);
|
|
15525
|
+
ctx.fillStyle = "#000000";
|
|
15526
|
+
ctx.fillStyle = value;
|
|
15527
|
+
const resolved = ctx.fillStyle;
|
|
15528
|
+
if (resolved.startsWith("#")) return normalizeHex(resolved);
|
|
15529
|
+
const rgb = resolved.match(
|
|
15530
|
+
/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?/i,
|
|
15531
|
+
);
|
|
15532
|
+
if (rgb) {
|
|
15533
|
+
return {
|
|
15534
|
+
color: rgbToHex(rgb[1], rgb[2], rgb[3]),
|
|
15535
|
+
opacity: rgb[4] !== undefined ? Math.round(parseFloat(rgb[4]) * 100) : 100,
|
|
15536
|
+
};
|
|
15537
|
+
}
|
|
15538
|
+
} catch {
|
|
15539
|
+
// Fall through to fallback.
|
|
15540
|
+
}
|
|
15541
|
+
|
|
15542
|
+
return fallback;
|
|
15543
|
+
}
|
|
15544
|
+
|
|
15545
|
+
#directColorPickerValue() {
|
|
15546
|
+
const { color, opacity } = this.#normalizeColorForPicker();
|
|
15547
|
+
return JSON.stringify(
|
|
15548
|
+
opacity < 100 ? { type: "solid", color, opacity } : { type: "solid", color },
|
|
15549
|
+
);
|
|
15550
|
+
}
|
|
15551
|
+
|
|
15552
|
+
#syncDirectColorPickerValue() {
|
|
15553
|
+
if (!this.#directColorPicker) return;
|
|
15554
|
+
this.#directColorPicker.setAttribute("value", this.#directColorPickerValue());
|
|
15555
|
+
}
|
|
15556
|
+
|
|
15557
|
+
#ensureDirectColorPicker() {
|
|
15558
|
+
if (this.#directColorPicker) return this.#directColorPicker;
|
|
15559
|
+
|
|
15560
|
+
const picker = document.createElement("fig-fill-picker");
|
|
15561
|
+
picker.setAttribute("mode", "solid");
|
|
15562
|
+
picker.setAttribute("alpha", "true");
|
|
15563
|
+
picker.setAttribute("value", this.#directColorPickerValue());
|
|
15564
|
+
picker.anchorElement = this;
|
|
15565
|
+
|
|
15566
|
+
const trigger = document.createElement("span");
|
|
15567
|
+
trigger.hidden = true;
|
|
15568
|
+
picker.appendChild(trigger);
|
|
15569
|
+
|
|
15570
|
+
picker.addEventListener("input", this.#handleDirectColorPickerInput);
|
|
15571
|
+
picker.addEventListener("change", this.#handleDirectColorPickerChange);
|
|
15572
|
+
picker.addEventListener("close", this.#handleDirectColorPickerClose);
|
|
15573
|
+
this.appendChild(picker);
|
|
15574
|
+
this.#directColorPicker = picker;
|
|
15575
|
+
return picker;
|
|
15576
|
+
}
|
|
15577
|
+
|
|
15578
|
+
#openDirectColorPicker() {
|
|
15579
|
+
if (this.hasAttribute("disabled")) return;
|
|
15580
|
+
this.#hideColorTip();
|
|
15581
|
+
const picker = this.#ensureDirectColorPicker();
|
|
15582
|
+
this.setAttribute("selected", "");
|
|
15583
|
+
this.#syncDirectColorPickerValue();
|
|
15584
|
+
picker.open();
|
|
15585
|
+
}
|
|
15586
|
+
|
|
15587
|
+
#removeDirectColorPicker() {
|
|
15588
|
+
if (!this.#directColorPicker) return;
|
|
15589
|
+
this.#directColorPicker.removeEventListener("input", this.#handleDirectColorPickerInput);
|
|
15590
|
+
this.#directColorPicker.removeEventListener("change", this.#handleDirectColorPickerChange);
|
|
15591
|
+
this.#directColorPicker.removeEventListener("close", this.#handleDirectColorPickerClose);
|
|
15592
|
+
this.#directColorPicker.close();
|
|
15593
|
+
this.#directColorPicker.remove();
|
|
15594
|
+
this.#directColorPicker = null;
|
|
15595
|
+
this.removeAttribute("selected");
|
|
15596
|
+
}
|
|
15597
|
+
|
|
15403
15598
|
#showColorTip() {
|
|
15404
15599
|
if (this.#colorTip) return;
|
|
15405
15600
|
const tip = document.createElement("fig-color-tip");
|
|
@@ -15434,6 +15629,47 @@ class FigHandle extends HTMLElement {
|
|
|
15434
15629
|
return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
|
|
15435
15630
|
}
|
|
15436
15631
|
|
|
15632
|
+
#detailFromPicker(detail) {
|
|
15633
|
+
if (!detail?.color) return null;
|
|
15634
|
+
const opacity =
|
|
15635
|
+
detail.opacity !== undefined
|
|
15636
|
+
? detail.opacity
|
|
15637
|
+
: detail.alpha !== undefined
|
|
15638
|
+
? Math.round(detail.alpha * 100)
|
|
15639
|
+
: undefined;
|
|
15640
|
+
return { color: detail.color, opacity };
|
|
15641
|
+
}
|
|
15642
|
+
|
|
15643
|
+
#handleDirectColorPickerInput = (e) => {
|
|
15644
|
+
e.stopPropagation();
|
|
15645
|
+
const detail = this.#detailFromPicker(e.detail);
|
|
15646
|
+
if (!detail) return;
|
|
15647
|
+
this.setAttribute("color", this.#colorWithOpacity(detail.color, detail.opacity));
|
|
15648
|
+
this.dispatchEvent(
|
|
15649
|
+
new CustomEvent("input", {
|
|
15650
|
+
bubbles: true,
|
|
15651
|
+
detail,
|
|
15652
|
+
}),
|
|
15653
|
+
);
|
|
15654
|
+
};
|
|
15655
|
+
|
|
15656
|
+
#handleDirectColorPickerChange = (e) => {
|
|
15657
|
+
e.stopPropagation();
|
|
15658
|
+
const detail = this.#detailFromPicker(e.detail);
|
|
15659
|
+
if (!detail) return;
|
|
15660
|
+
this.setAttribute("color", this.#colorWithOpacity(detail.color, detail.opacity));
|
|
15661
|
+
this.dispatchEvent(
|
|
15662
|
+
new CustomEvent("change", {
|
|
15663
|
+
bubbles: true,
|
|
15664
|
+
detail,
|
|
15665
|
+
}),
|
|
15666
|
+
);
|
|
15667
|
+
};
|
|
15668
|
+
|
|
15669
|
+
#handleDirectColorPickerClose = () => {
|
|
15670
|
+
this.removeAttribute("selected");
|
|
15671
|
+
};
|
|
15672
|
+
|
|
15437
15673
|
#handleColorTipInput = (e) => {
|
|
15438
15674
|
e.stopPropagation();
|
|
15439
15675
|
if (e.detail?.color) {
|
|
@@ -15528,7 +15764,7 @@ class FigMenu extends HTMLElement {
|
|
|
15528
15764
|
#observer = null;
|
|
15529
15765
|
#boundTriggerClick;
|
|
15530
15766
|
#boundPopupClick;
|
|
15531
|
-
#
|
|
15767
|
+
#boundMenuKeydown;
|
|
15532
15768
|
#boundPopupClose;
|
|
15533
15769
|
#focusedIndex = -1;
|
|
15534
15770
|
|
|
@@ -15540,7 +15776,7 @@ class FigMenu extends HTMLElement {
|
|
|
15540
15776
|
super();
|
|
15541
15777
|
this.#boundTriggerClick = this.#handleTriggerClick.bind(this);
|
|
15542
15778
|
this.#boundPopupClick = this.#handlePopupClick.bind(this);
|
|
15543
|
-
this.#
|
|
15779
|
+
this.#boundMenuKeydown = this.#handleMenuKeydown.bind(this);
|
|
15544
15780
|
this.#boundPopupClose = this.#handlePopupClose.bind(this);
|
|
15545
15781
|
}
|
|
15546
15782
|
|
|
@@ -15661,6 +15897,7 @@ class FigMenu extends HTMLElement {
|
|
|
15661
15897
|
}
|
|
15662
15898
|
|
|
15663
15899
|
#setupListeners() {
|
|
15900
|
+
this.addEventListener("keydown", this.#boundMenuKeydown);
|
|
15664
15901
|
if (this.#trigger) {
|
|
15665
15902
|
this.#trigger.addEventListener("click", this.#boundTriggerClick);
|
|
15666
15903
|
this.#trigger.setAttribute("aria-haspopup", "menu");
|
|
@@ -15668,17 +15905,16 @@ class FigMenu extends HTMLElement {
|
|
|
15668
15905
|
}
|
|
15669
15906
|
if (this.#popup) {
|
|
15670
15907
|
this.#popup.addEventListener("click", this.#boundPopupClick);
|
|
15671
|
-
this.#popup.addEventListener("keydown", this.#boundPopupKeydown);
|
|
15672
15908
|
}
|
|
15673
15909
|
}
|
|
15674
15910
|
|
|
15675
15911
|
#teardownListeners() {
|
|
15912
|
+
this.removeEventListener("keydown", this.#boundMenuKeydown);
|
|
15676
15913
|
if (this.#trigger) {
|
|
15677
15914
|
this.#trigger.removeEventListener("click", this.#boundTriggerClick);
|
|
15678
15915
|
}
|
|
15679
15916
|
if (this.#popup) {
|
|
15680
15917
|
this.#popup.removeEventListener("click", this.#boundPopupClick);
|
|
15681
|
-
this.#popup.removeEventListener("keydown", this.#boundPopupKeydown);
|
|
15682
15918
|
}
|
|
15683
15919
|
}
|
|
15684
15920
|
|
|
@@ -15713,6 +15949,27 @@ class FigMenu extends HTMLElement {
|
|
|
15713
15949
|
return Array.from(this.#popup.querySelectorAll("fig-menu-item:not([disabled]):not([disabled='true'])"));
|
|
15714
15950
|
}
|
|
15715
15951
|
|
|
15952
|
+
#syncFocusedIndex() {
|
|
15953
|
+
const items = this.#getItems();
|
|
15954
|
+
if (!items.length) {
|
|
15955
|
+
this.#focusedIndex = -1;
|
|
15956
|
+
return;
|
|
15957
|
+
}
|
|
15958
|
+
const active = document.activeElement;
|
|
15959
|
+
const idx = items.findIndex(
|
|
15960
|
+
(item) => item === active || item.contains(active),
|
|
15961
|
+
);
|
|
15962
|
+
this.#focusedIndex = idx >= 0 ? idx : -1;
|
|
15963
|
+
}
|
|
15964
|
+
|
|
15965
|
+
#focusItemAt(index) {
|
|
15966
|
+
const items = this.#getItems();
|
|
15967
|
+
if (!items.length) return;
|
|
15968
|
+
const clamped = Math.max(0, Math.min(index, items.length - 1));
|
|
15969
|
+
this.#focusedIndex = clamped;
|
|
15970
|
+
items[clamped].focus();
|
|
15971
|
+
}
|
|
15972
|
+
|
|
15716
15973
|
#syncDisabled() {
|
|
15717
15974
|
if (!this.#trigger) return;
|
|
15718
15975
|
const disabled = this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
@@ -15726,7 +15983,12 @@ class FigMenu extends HTMLElement {
|
|
|
15726
15983
|
#handleTriggerClick(e) {
|
|
15727
15984
|
if (this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false") return;
|
|
15728
15985
|
e.stopPropagation();
|
|
15729
|
-
|
|
15986
|
+
const popupShowing = this.#popup?.matches?.(":open") ?? false;
|
|
15987
|
+
if (this.open && !popupShowing) {
|
|
15988
|
+
this.removeAttribute("open");
|
|
15989
|
+
}
|
|
15990
|
+
const effectiveOpen = this.open && popupShowing;
|
|
15991
|
+
if (effectiveOpen) {
|
|
15730
15992
|
this.open = false;
|
|
15731
15993
|
} else {
|
|
15732
15994
|
this.open = true;
|
|
@@ -15741,40 +16003,42 @@ class FigMenu extends HTMLElement {
|
|
|
15741
16003
|
this.#selectItem(item);
|
|
15742
16004
|
}
|
|
15743
16005
|
|
|
15744
|
-
#
|
|
16006
|
+
#handleMenuKeydown(e) {
|
|
16007
|
+
if (!this.open || !this.#popup?.matches?.(":open")) return;
|
|
16008
|
+
|
|
15745
16009
|
const items = this.#getItems();
|
|
15746
16010
|
if (!items.length) return;
|
|
15747
16011
|
|
|
15748
16012
|
switch (e.key) {
|
|
15749
16013
|
case "ArrowDown": {
|
|
15750
16014
|
e.preventDefault();
|
|
15751
|
-
this.#
|
|
15752
|
-
|
|
16015
|
+
this.#syncFocusedIndex();
|
|
16016
|
+
this.#focusItemAt(this.#focusedIndex + 1);
|
|
15753
16017
|
break;
|
|
15754
16018
|
}
|
|
15755
16019
|
case "ArrowUp": {
|
|
15756
16020
|
e.preventDefault();
|
|
15757
|
-
this.#
|
|
15758
|
-
|
|
16021
|
+
this.#syncFocusedIndex();
|
|
16022
|
+
this.#focusItemAt(this.#focusedIndex - 1);
|
|
15759
16023
|
break;
|
|
15760
16024
|
}
|
|
15761
16025
|
case "Home": {
|
|
15762
16026
|
e.preventDefault();
|
|
15763
|
-
this.#
|
|
15764
|
-
items[0]?.focus();
|
|
16027
|
+
this.#focusItemAt(0);
|
|
15765
16028
|
break;
|
|
15766
16029
|
}
|
|
15767
16030
|
case "End": {
|
|
15768
16031
|
e.preventDefault();
|
|
15769
|
-
this.#
|
|
15770
|
-
items[this.#focusedIndex]?.focus();
|
|
16032
|
+
this.#focusItemAt(items.length - 1);
|
|
15771
16033
|
break;
|
|
15772
16034
|
}
|
|
15773
16035
|
case "Enter":
|
|
15774
16036
|
case " ": {
|
|
15775
|
-
|
|
16037
|
+
this.#syncFocusedIndex();
|
|
15776
16038
|
const focused = items[this.#focusedIndex];
|
|
15777
|
-
if (focused)
|
|
16039
|
+
if (!focused) return;
|
|
16040
|
+
e.preventDefault();
|
|
16041
|
+
this.#selectItem(focused);
|
|
15778
16042
|
break;
|
|
15779
16043
|
}
|
|
15780
16044
|
}
|
|
@@ -15809,10 +16073,10 @@ class FigMenu extends HTMLElement {
|
|
|
15809
16073
|
if (this.#trigger) {
|
|
15810
16074
|
this.#trigger.setAttribute("aria-expanded", "true");
|
|
15811
16075
|
}
|
|
15812
|
-
this.#focusedIndex =
|
|
16076
|
+
this.#focusedIndex = -1;
|
|
15813
16077
|
requestAnimationFrame(() => {
|
|
15814
|
-
|
|
15815
|
-
|
|
16078
|
+
if (!this.#trigger?.matches?.(":focus-visible")) return;
|
|
16079
|
+
this.#focusItemAt(0);
|
|
15816
16080
|
});
|
|
15817
16081
|
}
|
|
15818
16082
|
|
package/package.json
CHANGED