@rogieking/figui3 6.11.0 → 6.13.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/components.css +68 -33
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +52 -50
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +14 -14
- package/dist/fig.css +1 -1
- package/dist/fig.js +33 -45
- package/fig-editor.css +12 -1
- package/fig-editor.js +317 -334
- package/fig-lab.css +11 -10
- package/fig-lab.js +25 -26
- package/fig.js +649 -228
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -35,6 +35,10 @@ function figNextFrame(host, callback) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function figBooleanAttribute(element, name) {
|
|
39
|
+
return element.hasAttribute(name) && element.getAttribute(name) !== "false";
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
function figNormalizeTextOnlyInputSlots(host) {
|
|
39
43
|
host
|
|
40
44
|
.querySelectorAll(':scope > [slot="prepend"], :scope > [slot="append"]')
|
|
@@ -249,6 +253,15 @@ function figSyncCssVar(el, prop, value) {
|
|
|
249
253
|
}
|
|
250
254
|
}
|
|
251
255
|
|
|
256
|
+
function figEscapeAttribute(value) {
|
|
257
|
+
return String(value ?? "")
|
|
258
|
+
.replace(/&/g, "&")
|
|
259
|
+
.replace(/"/g, """)
|
|
260
|
+
.replace(/'/g, "'")
|
|
261
|
+
.replace(/</g, "<")
|
|
262
|
+
.replace(/>/g, ">");
|
|
263
|
+
}
|
|
264
|
+
|
|
252
265
|
let _figSharedCanvas = null;
|
|
253
266
|
let _figSharedCtx = null;
|
|
254
267
|
function figGetSharedCanvas(width = 1, height = 1) {
|
|
@@ -300,7 +313,10 @@ class FigButton extends HTMLElement {
|
|
|
300
313
|
if (!this.button) {
|
|
301
314
|
const isControlWrapper = this.type === "select" || this.type === "upload";
|
|
302
315
|
const controlTag = isControlWrapper ? "span" : "button";
|
|
303
|
-
|
|
316
|
+
// Custom button modes are implemented by the host. Keep the shadow
|
|
317
|
+
// control inert so invalid native types (for example "toggle") cannot
|
|
318
|
+
// normalize to "submit" inside a form.
|
|
319
|
+
const typeAttr = isControlWrapper ? "" : ' type="button"';
|
|
304
320
|
this.shadowRoot.innerHTML = `
|
|
305
321
|
<style>
|
|
306
322
|
button, button:hover, button:active, .fig-button-control {
|
|
@@ -343,9 +359,11 @@ class FigButton extends HTMLElement {
|
|
|
343
359
|
this.button.addEventListener("click", this.#boundHandleClick);
|
|
344
360
|
this.button.addEventListener("focus", this.#boundHandleFocus);
|
|
345
361
|
this.button.addEventListener("blur", this.#boundHandleBlur);
|
|
346
|
-
this.addEventListener("keydown", this.#boundHandleControlKeydown);
|
|
347
362
|
}
|
|
348
363
|
|
|
364
|
+
this.removeEventListener("keydown", this.#boundHandleControlKeydown);
|
|
365
|
+
this.addEventListener("keydown", this.#boundHandleControlKeydown);
|
|
366
|
+
|
|
349
367
|
this.#selected =
|
|
350
368
|
this.hasAttribute("selected") &&
|
|
351
369
|
this.getAttribute("selected") !== "false";
|
|
@@ -373,12 +391,14 @@ class FigButton extends HTMLElement {
|
|
|
373
391
|
#handleClick() {
|
|
374
392
|
if (this.#isDisabled()) return;
|
|
375
393
|
if (this.type === "toggle") {
|
|
376
|
-
this.
|
|
394
|
+
if (this.selected) this.removeAttribute("selected");
|
|
395
|
+
else this.setAttribute("selected", "");
|
|
377
396
|
}
|
|
378
397
|
if (this.type === "submit") {
|
|
379
398
|
let form = this.closest("form");
|
|
380
399
|
if (form) {
|
|
381
|
-
form.
|
|
400
|
+
if (typeof form.requestSubmit === "function") form.requestSubmit();
|
|
401
|
+
else form.submit();
|
|
382
402
|
}
|
|
383
403
|
}
|
|
384
404
|
if (this.type === "link") {
|
|
@@ -454,8 +474,8 @@ class FigButton extends HTMLElement {
|
|
|
454
474
|
this.disabled = disabled;
|
|
455
475
|
if (this.button instanceof HTMLButtonElement) {
|
|
456
476
|
this.button.disabled = disabled;
|
|
457
|
-
this.button.type =
|
|
458
|
-
this.button.setAttribute("type",
|
|
477
|
+
this.button.type = "button";
|
|
478
|
+
this.button.setAttribute("type", "button");
|
|
459
479
|
}
|
|
460
480
|
this.#syncA11yAttributes();
|
|
461
481
|
this.#syncPressedState();
|
|
@@ -577,7 +597,9 @@ class FigDropdown extends HTMLElement {
|
|
|
577
597
|
return;
|
|
578
598
|
}
|
|
579
599
|
// Fallback mirror for browsers that don't auto-project selectedcontent reliably.
|
|
580
|
-
this.#selectedContentEl.
|
|
600
|
+
this.#selectedContentEl.replaceChildren(
|
|
601
|
+
...Array.from(selectedOption.childNodes, (node) => node.cloneNode(true)),
|
|
602
|
+
);
|
|
581
603
|
}
|
|
582
604
|
|
|
583
605
|
#addEventListeners() {
|
|
@@ -630,6 +652,16 @@ class FigDropdown extends HTMLElement {
|
|
|
630
652
|
}
|
|
631
653
|
|
|
632
654
|
slotChange() {
|
|
655
|
+
const hasDeclaredValue = this.hasAttribute("value");
|
|
656
|
+
const hadNativeSelection =
|
|
657
|
+
this.select.options.length > 0 && this.select.selectedIndex >= 0;
|
|
658
|
+
const selectedValue = hasDeclaredValue
|
|
659
|
+
? this.getAttribute("value")
|
|
660
|
+
: this.type === "dropdown"
|
|
661
|
+
? this.#selectedValue
|
|
662
|
+
: hadNativeSelection
|
|
663
|
+
? this.select.value
|
|
664
|
+
: null;
|
|
633
665
|
while (this.select.firstChild) {
|
|
634
666
|
this.select.firstChild.remove();
|
|
635
667
|
}
|
|
@@ -648,7 +680,9 @@ class FigDropdown extends HTMLElement {
|
|
|
648
680
|
this.select.appendChild(option.cloneNode(true));
|
|
649
681
|
}
|
|
650
682
|
});
|
|
651
|
-
|
|
683
|
+
if (selectedValue !== null) {
|
|
684
|
+
this.#syncSelectedValue(selectedValue);
|
|
685
|
+
}
|
|
652
686
|
this.#syncSelectedContent();
|
|
653
687
|
if (this.type === "dropdown") {
|
|
654
688
|
this.select.selectedIndex = -1;
|
|
@@ -656,6 +690,7 @@ class FigDropdown extends HTMLElement {
|
|
|
656
690
|
}
|
|
657
691
|
|
|
658
692
|
#handleSelectInput(e) {
|
|
693
|
+
e.stopPropagation();
|
|
659
694
|
const selectedOption = e.target.selectedOptions?.[0];
|
|
660
695
|
if (this.#hasPersistentControl(selectedOption)) {
|
|
661
696
|
if (this.type === "dropdown") {
|
|
@@ -682,6 +717,7 @@ class FigDropdown extends HTMLElement {
|
|
|
682
717
|
}
|
|
683
718
|
|
|
684
719
|
#handleSelectChange(e) {
|
|
720
|
+
e.stopPropagation();
|
|
685
721
|
const selectedOption = e.target.selectedOptions?.[0];
|
|
686
722
|
if (this.#hasPersistentControl(selectedOption)) {
|
|
687
723
|
if (this.type === "dropdown") {
|
|
@@ -756,13 +792,7 @@ class FigDropdown extends HTMLElement {
|
|
|
756
792
|
if (this.type === "dropdown") {
|
|
757
793
|
return;
|
|
758
794
|
}
|
|
759
|
-
if (this.select)
|
|
760
|
-
this.select.querySelectorAll("option").forEach((o, i) => {
|
|
761
|
-
if (o.value === this.getAttribute("value")) {
|
|
762
|
-
this.select.selectedIndex = i;
|
|
763
|
-
}
|
|
764
|
-
});
|
|
765
|
-
}
|
|
795
|
+
if (this.select) this.select.value = value ?? "";
|
|
766
796
|
this.#syncSelectedContent();
|
|
767
797
|
}
|
|
768
798
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -770,13 +800,14 @@ class FigDropdown extends HTMLElement {
|
|
|
770
800
|
this.#syncSelectedValue(newValue);
|
|
771
801
|
}
|
|
772
802
|
if (name === "type") {
|
|
773
|
-
this.type = newValue;
|
|
803
|
+
this.type = newValue || "select";
|
|
804
|
+
if (this.isConnected) this.slotChange();
|
|
774
805
|
}
|
|
775
806
|
if (name === "experimental") {
|
|
776
807
|
this.slotChange();
|
|
777
808
|
}
|
|
778
809
|
if (name === "label") {
|
|
779
|
-
this.#label = newValue;
|
|
810
|
+
this.#label = newValue || "Menu";
|
|
780
811
|
this.select.setAttribute("aria-label", this.#label);
|
|
781
812
|
}
|
|
782
813
|
if (name === "disabled") {
|
|
@@ -990,9 +1021,19 @@ class FigTooltip extends HTMLElement {
|
|
|
990
1021
|
this.popup.append(content);
|
|
991
1022
|
content.innerText = this.getAttribute("text") ?? "";
|
|
992
1023
|
|
|
993
|
-
//
|
|
1024
|
+
// Preserve any descriptions supplied by the consumer and append the
|
|
1025
|
+
// transient tooltip id for as long as this popup exists.
|
|
994
1026
|
if (this.firstElementChild) {
|
|
995
|
-
|
|
1027
|
+
const describedBy = new Set(
|
|
1028
|
+
(this.firstElementChild.getAttribute("aria-describedby") || "")
|
|
1029
|
+
.split(/\s+/)
|
|
1030
|
+
.filter(Boolean),
|
|
1031
|
+
);
|
|
1032
|
+
describedBy.add(tooltipId);
|
|
1033
|
+
this.firstElementChild.setAttribute(
|
|
1034
|
+
"aria-describedby",
|
|
1035
|
+
[...describedBy].join(" "),
|
|
1036
|
+
);
|
|
996
1037
|
}
|
|
997
1038
|
|
|
998
1039
|
// Attach to DOM.
|
|
@@ -1023,13 +1064,6 @@ class FigTooltip extends HTMLElement {
|
|
|
1023
1064
|
this.popup.remove();
|
|
1024
1065
|
this.popup = null;
|
|
1025
1066
|
}
|
|
1026
|
-
// Remove the click outside listener if it was added
|
|
1027
|
-
if (this.action === "click") {
|
|
1028
|
-
document.body.removeEventListener(
|
|
1029
|
-
"click",
|
|
1030
|
-
this.#boundHidePopupOutsideClick,
|
|
1031
|
-
);
|
|
1032
|
-
}
|
|
1033
1067
|
}
|
|
1034
1068
|
#removeDescribedBy(id) {
|
|
1035
1069
|
const trigger = this.firstElementChild;
|
|
@@ -1203,21 +1237,41 @@ class FigTooltip extends HTMLElement {
|
|
|
1203
1237
|
// fig-popup observes content size changes and will reposition itself.
|
|
1204
1238
|
}
|
|
1205
1239
|
get open() {
|
|
1206
|
-
return this.hasAttribute("open") && this.getAttribute("open")
|
|
1240
|
+
return this.hasAttribute("open") && this.getAttribute("open") !== "false";
|
|
1207
1241
|
}
|
|
1208
1242
|
set open(value) {
|
|
1209
|
-
|
|
1243
|
+
if (value === false || value === "false" || value === null) {
|
|
1244
|
+
this.removeAttribute("open");
|
|
1245
|
+
} else {
|
|
1246
|
+
this.setAttribute("open", "true");
|
|
1247
|
+
}
|
|
1210
1248
|
}
|
|
1211
1249
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
1212
1250
|
if (name === "action") {
|
|
1213
|
-
this
|
|
1251
|
+
this.#unbindTriggerListeners();
|
|
1252
|
+
if (oldValue === "click") {
|
|
1253
|
+
document.body?.removeEventListener(
|
|
1254
|
+
"click",
|
|
1255
|
+
this.#boundHidePopupOutsideClick,
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
this.action = newValue || "hover";
|
|
1259
|
+
if (this.isConnected) {
|
|
1260
|
+
this.#bindTriggerListeners();
|
|
1261
|
+
if (this.action === "click") {
|
|
1262
|
+
document.body?.addEventListener(
|
|
1263
|
+
"click",
|
|
1264
|
+
this.#boundHidePopupOutsideClick,
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1214
1268
|
}
|
|
1215
1269
|
if (name === "delay") {
|
|
1216
1270
|
let delay = parseInt(newValue);
|
|
1217
1271
|
this.delay = !isNaN(delay) ? delay : 500;
|
|
1218
1272
|
}
|
|
1219
1273
|
if (name === "open") {
|
|
1220
|
-
if (newValue
|
|
1274
|
+
if (newValue !== null && newValue !== "false") {
|
|
1221
1275
|
requestAnimationFrame(() => {
|
|
1222
1276
|
this.showDelayedPopup();
|
|
1223
1277
|
});
|
|
@@ -1878,6 +1932,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
1878
1932
|
this.removeEventListener("pointerdown", this._boundPointerDown);
|
|
1879
1933
|
document.removeEventListener("pointermove", this._boundPointerMove);
|
|
1880
1934
|
document.removeEventListener("pointerup", this._boundPointerUp);
|
|
1935
|
+
document.removeEventListener("pointercancel", this._boundPointerUp);
|
|
1881
1936
|
}
|
|
1882
1937
|
|
|
1883
1938
|
_isInteractiveElement(element) {
|
|
@@ -1968,6 +2023,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
1968
2023
|
|
|
1969
2024
|
document.addEventListener("pointermove", this._boundPointerMove);
|
|
1970
2025
|
document.addEventListener("pointerup", this._boundPointerUp);
|
|
2026
|
+
document.addEventListener("pointercancel", this._boundPointerUp);
|
|
1971
2027
|
}
|
|
1972
2028
|
|
|
1973
2029
|
_handlePointerMove(e) {
|
|
@@ -2000,7 +2056,9 @@ class FigDialog extends HTMLDialogElement {
|
|
|
2000
2056
|
|
|
2001
2057
|
_handlePointerUp(e) {
|
|
2002
2058
|
if (this._isDragging) {
|
|
2003
|
-
this.
|
|
2059
|
+
if (this.hasPointerCapture?.(e.pointerId)) {
|
|
2060
|
+
this.releasePointerCapture(e.pointerId);
|
|
2061
|
+
}
|
|
2004
2062
|
this.style.cursor = "";
|
|
2005
2063
|
}
|
|
2006
2064
|
|
|
@@ -2009,6 +2067,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
2009
2067
|
|
|
2010
2068
|
document.removeEventListener("pointermove", this._boundPointerMove);
|
|
2011
2069
|
document.removeEventListener("pointerup", this._boundPointerUp);
|
|
2070
|
+
document.removeEventListener("pointercancel", this._boundPointerUp);
|
|
2012
2071
|
|
|
2013
2072
|
e.preventDefault();
|
|
2014
2073
|
}
|
|
@@ -2093,6 +2152,10 @@ class FigToast extends HTMLDialogElement {
|
|
|
2093
2152
|
this._figInitialized = true;
|
|
2094
2153
|
this._defaultOffset = 16;
|
|
2095
2154
|
this._autoCloseTimer = null;
|
|
2155
|
+
this._toastVisible = false;
|
|
2156
|
+
this._syncingOpen = false;
|
|
2157
|
+
this._managedRole = null;
|
|
2158
|
+
this._managedLive = null;
|
|
2096
2159
|
this._boundHandleClose = this.handleClose.bind(this);
|
|
2097
2160
|
}
|
|
2098
2161
|
|
|
@@ -2104,6 +2167,13 @@ class FigToast extends HTMLDialogElement {
|
|
|
2104
2167
|
this.applyPosition();
|
|
2105
2168
|
if (this.hasAttribute("open") && this.getAttribute("open") !== "false") {
|
|
2106
2169
|
this.showToast();
|
|
2170
|
+
} else {
|
|
2171
|
+
if (this.getAttribute("open") === "false") {
|
|
2172
|
+
this._syncingOpen = true;
|
|
2173
|
+
this.removeAttribute("open");
|
|
2174
|
+
this._syncingOpen = false;
|
|
2175
|
+
}
|
|
2176
|
+
this._toastVisible = false;
|
|
2107
2177
|
}
|
|
2108
2178
|
}
|
|
2109
2179
|
|
|
@@ -2128,7 +2198,11 @@ class FigToast extends HTMLDialogElement {
|
|
|
2128
2198
|
this.style.position = "fixed";
|
|
2129
2199
|
this.style.margin = "0";
|
|
2130
2200
|
this.style.top = "auto";
|
|
2131
|
-
|
|
2201
|
+
const configuredOffset = Number.parseFloat(this.getAttribute("offset"));
|
|
2202
|
+
const offset = Number.isFinite(configuredOffset)
|
|
2203
|
+
? configuredOffset
|
|
2204
|
+
: this._defaultOffset;
|
|
2205
|
+
this.style.bottom = `${offset}px`;
|
|
2132
2206
|
this.style.left = "50%";
|
|
2133
2207
|
this.style.right = "auto";
|
|
2134
2208
|
this.style.transform = "translateX(-50%)";
|
|
@@ -2138,9 +2212,22 @@ class FigToast extends HTMLDialogElement {
|
|
|
2138
2212
|
const assertive =
|
|
2139
2213
|
this.getAttribute("live") === "assertive" ||
|
|
2140
2214
|
this.getAttribute("theme") === "danger";
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
if (!this.hasAttribute("
|
|
2215
|
+
const desiredRole = assertive ? "alert" : "status";
|
|
2216
|
+
const desiredLive = assertive ? "assertive" : "polite";
|
|
2217
|
+
if (!this.hasAttribute("role") || this.getAttribute("role") === this._managedRole) {
|
|
2218
|
+
this.setAttribute("role", desiredRole);
|
|
2219
|
+
this._managedRole = desiredRole;
|
|
2220
|
+
}
|
|
2221
|
+
if (
|
|
2222
|
+
!this.hasAttribute("aria-live") ||
|
|
2223
|
+
this.getAttribute("aria-live") === this._managedLive
|
|
2224
|
+
) {
|
|
2225
|
+
this.setAttribute("aria-live", desiredLive);
|
|
2226
|
+
this._managedLive = desiredLive;
|
|
2227
|
+
}
|
|
2228
|
+
if (!this.hasAttribute("aria-atomic")) {
|
|
2229
|
+
this.setAttribute("aria-atomic", "true");
|
|
2230
|
+
}
|
|
2144
2231
|
}
|
|
2145
2232
|
|
|
2146
2233
|
startAutoClose() {
|
|
@@ -2158,18 +2245,45 @@ class FigToast extends HTMLDialogElement {
|
|
|
2158
2245
|
}
|
|
2159
2246
|
}
|
|
2160
2247
|
|
|
2248
|
+
_resolveAutoTheme() {
|
|
2249
|
+
if (this.getAttribute("theme") !== "auto") return;
|
|
2250
|
+
const colorScheme =
|
|
2251
|
+
getComputedStyle(document.documentElement).colorScheme || "";
|
|
2252
|
+
this.style.colorScheme = colorScheme.includes("dark") ? "light" : "dark";
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2161
2255
|
showToast() {
|
|
2256
|
+
const wasVisible = this._toastVisible;
|
|
2162
2257
|
this.syncLiveRegion();
|
|
2163
|
-
|
|
2258
|
+
this._resolveAutoTheme();
|
|
2259
|
+
this._syncingOpen = true;
|
|
2260
|
+
try {
|
|
2261
|
+
if (!this.open) this.show();
|
|
2262
|
+
} finally {
|
|
2263
|
+
this._syncingOpen = false;
|
|
2264
|
+
}
|
|
2265
|
+
this._toastVisible = true;
|
|
2164
2266
|
this.applyPosition();
|
|
2165
2267
|
this.startAutoClose();
|
|
2166
|
-
|
|
2268
|
+
if (!wasVisible) {
|
|
2269
|
+
this.dispatchEvent(new CustomEvent("toast-show", { bubbles: true }));
|
|
2270
|
+
}
|
|
2167
2271
|
}
|
|
2168
2272
|
|
|
2169
2273
|
hideToast() {
|
|
2274
|
+
const wasVisible = this._toastVisible || this.open;
|
|
2170
2275
|
this.clearAutoClose();
|
|
2171
|
-
|
|
2172
|
-
|
|
2276
|
+
this._syncingOpen = true;
|
|
2277
|
+
try {
|
|
2278
|
+
if (this.open) this.close();
|
|
2279
|
+
else if (this.hasAttribute("open")) this.removeAttribute("open");
|
|
2280
|
+
} finally {
|
|
2281
|
+
this._syncingOpen = false;
|
|
2282
|
+
}
|
|
2283
|
+
this._toastVisible = false;
|
|
2284
|
+
if (wasVisible) {
|
|
2285
|
+
this.dispatchEvent(new CustomEvent("toast-hide", { bubbles: true }));
|
|
2286
|
+
}
|
|
2173
2287
|
}
|
|
2174
2288
|
|
|
2175
2289
|
static get observedAttributes() {
|
|
@@ -2180,11 +2294,17 @@ class FigToast extends HTMLDialogElement {
|
|
|
2180
2294
|
this._figInit();
|
|
2181
2295
|
if (oldValue === newValue) return;
|
|
2182
2296
|
if (!this.isConnected) return;
|
|
2297
|
+
if (this._syncingOpen) return;
|
|
2183
2298
|
if (name === "offset") this.applyPosition();
|
|
2184
2299
|
if (name === "open") {
|
|
2185
2300
|
if (newValue !== null && newValue !== "false") this.showToast();
|
|
2186
2301
|
else this.hideToast();
|
|
2187
2302
|
}
|
|
2303
|
+
if (name === "duration" && this._toastVisible) this.startAutoClose();
|
|
2304
|
+
if (name === "theme") {
|
|
2305
|
+
if (newValue === "auto") this._resolveAutoTheme();
|
|
2306
|
+
else this.style.removeProperty("color-scheme");
|
|
2307
|
+
}
|
|
2188
2308
|
if (name === "theme" || name === "live") this.syncLiveRegion();
|
|
2189
2309
|
}
|
|
2190
2310
|
}
|
|
@@ -2224,6 +2344,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2224
2344
|
_wasDragged = false;
|
|
2225
2345
|
_previousFocus = null;
|
|
2226
2346
|
_boundDocumentKeydown;
|
|
2347
|
+
_boundNativeClose;
|
|
2227
2348
|
|
|
2228
2349
|
constructor() {
|
|
2229
2350
|
super();
|
|
@@ -2243,6 +2364,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2243
2364
|
this._boundPointerMove = this.handlePointerMove.bind(this);
|
|
2244
2365
|
this._boundPointerUp = this.handlePointerUp.bind(this);
|
|
2245
2366
|
this._boundDocumentKeydown = this.handleDocumentKeydown.bind(this);
|
|
2367
|
+
this._boundNativeClose = this.handleNativeClose.bind(this);
|
|
2246
2368
|
}
|
|
2247
2369
|
|
|
2248
2370
|
ensureInitialized() {
|
|
@@ -2299,6 +2421,9 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2299
2421
|
if (typeof this._boundDocumentKeydown !== "function") {
|
|
2300
2422
|
this._boundDocumentKeydown = this.handleDocumentKeydown.bind(this);
|
|
2301
2423
|
}
|
|
2424
|
+
if (typeof this._boundNativeClose !== "function") {
|
|
2425
|
+
this._boundNativeClose = this.handleNativeClose.bind(this);
|
|
2426
|
+
}
|
|
2302
2427
|
}
|
|
2303
2428
|
|
|
2304
2429
|
static get observedAttributes() {
|
|
@@ -2322,8 +2447,11 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2322
2447
|
|
|
2323
2448
|
set open(value) {
|
|
2324
2449
|
if (value === false || value === "false" || value === null) {
|
|
2325
|
-
|
|
2326
|
-
|
|
2450
|
+
const isActuallyOpen =
|
|
2451
|
+
this.matches?.(":open") || this.matches?.(":popover-open");
|
|
2452
|
+
if (!isActuallyOpen && !this.hasAttribute("open")) return;
|
|
2453
|
+
this.hidePopup();
|
|
2454
|
+
if (this.hasAttribute("open")) this.removeAttribute("open");
|
|
2327
2455
|
return;
|
|
2328
2456
|
}
|
|
2329
2457
|
if (this.open) return;
|
|
@@ -2380,12 +2508,8 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2380
2508
|
this.drag =
|
|
2381
2509
|
this.hasAttribute("drag") && this.getAttribute("drag") !== "false";
|
|
2382
2510
|
|
|
2383
|
-
this.
|
|
2384
|
-
|
|
2385
|
-
if (this.hasAttribute("open")) {
|
|
2386
|
-
this.removeAttribute("open");
|
|
2387
|
-
}
|
|
2388
|
-
});
|
|
2511
|
+
this.removeEventListener("close", this._boundNativeClose);
|
|
2512
|
+
this.addEventListener("close", this._boundNativeClose);
|
|
2389
2513
|
|
|
2390
2514
|
requestAnimationFrame(() => {
|
|
2391
2515
|
this.setupDragListeners();
|
|
@@ -2408,6 +2532,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2408
2532
|
true,
|
|
2409
2533
|
);
|
|
2410
2534
|
document.removeEventListener("keydown", this._boundDocumentKeydown, true);
|
|
2535
|
+
this.removeEventListener("close", this._boundNativeClose);
|
|
2411
2536
|
if (this._rafId !== null) {
|
|
2412
2537
|
cancelAnimationFrame(this._rafId);
|
|
2413
2538
|
this._rafId = null;
|
|
@@ -2419,7 +2544,11 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2419
2544
|
if (oldValue === newValue) return;
|
|
2420
2545
|
|
|
2421
2546
|
if (name === "open") {
|
|
2422
|
-
if (newValue ===
|
|
2547
|
+
if (newValue === "false") {
|
|
2548
|
+
this.open = false;
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
if (newValue === null) {
|
|
2423
2552
|
this.hidePopup();
|
|
2424
2553
|
return;
|
|
2425
2554
|
}
|
|
@@ -2458,7 +2587,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2458
2587
|
// When the popup opts into the native popover API, prefer showPopover()
|
|
2459
2588
|
// so the element is promoted into the browser's top layer (above any
|
|
2460
2589
|
// modal dialogs) without needing showModal().
|
|
2461
|
-
|
|
2590
|
+
let usePopover =
|
|
2462
2591
|
this.hasAttribute("popover") &&
|
|
2463
2592
|
typeof this.showPopover === "function" &&
|
|
2464
2593
|
!this.matches?.(":popover-open");
|
|
@@ -2470,7 +2599,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2470
2599
|
try {
|
|
2471
2600
|
this.showPopover();
|
|
2472
2601
|
} catch (e) {
|
|
2473
|
-
|
|
2602
|
+
usePopover = false;
|
|
2474
2603
|
}
|
|
2475
2604
|
}
|
|
2476
2605
|
if (!usePopover && !super.open) {
|
|
@@ -2580,13 +2709,20 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2580
2709
|
return val === null || val !== "false";
|
|
2581
2710
|
}
|
|
2582
2711
|
set autoresize(value) {
|
|
2583
|
-
if (value || value === "") {
|
|
2712
|
+
if (value === false || value === "false" || value === null) {
|
|
2713
|
+
this.setAttribute("autoresize", "false");
|
|
2714
|
+
} else if (value || value === "") {
|
|
2584
2715
|
this.setAttribute("autoresize", value === true ? "" : value);
|
|
2585
2716
|
} else {
|
|
2586
|
-
this.
|
|
2717
|
+
this.setAttribute("autoresize", "false");
|
|
2587
2718
|
}
|
|
2588
2719
|
}
|
|
2589
2720
|
|
|
2721
|
+
handleNativeClose() {
|
|
2722
|
+
this.teardownObservers();
|
|
2723
|
+
if (this.hasAttribute("open")) this.removeAttribute("open");
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2590
2726
|
setupObservers() {
|
|
2591
2727
|
this.teardownObservers();
|
|
2592
2728
|
|
|
@@ -2700,7 +2836,11 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2700
2836
|
}
|
|
2701
2837
|
|
|
2702
2838
|
handleOutsidePointerDown(event) {
|
|
2703
|
-
if (
|
|
2839
|
+
if (
|
|
2840
|
+
!this.open ||
|
|
2841
|
+
!(this.matches?.(":open") || this.matches?.(":popover-open"))
|
|
2842
|
+
)
|
|
2843
|
+
return;
|
|
2704
2844
|
const closedby = this.getAttribute("closedby");
|
|
2705
2845
|
if (closedby === "none" || closedby === "closerequest") return;
|
|
2706
2846
|
const target = event.target;
|
|
@@ -2764,6 +2904,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2764
2904
|
this.removeEventListener("pointerdown", this._boundPointerDown);
|
|
2765
2905
|
document.removeEventListener("pointermove", this._boundPointerMove);
|
|
2766
2906
|
document.removeEventListener("pointerup", this._boundPointerUp);
|
|
2907
|
+
document.removeEventListener("pointercancel", this._boundPointerUp);
|
|
2767
2908
|
}
|
|
2768
2909
|
|
|
2769
2910
|
isInteractiveElement(element) {
|
|
@@ -2831,6 +2972,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2831
2972
|
|
|
2832
2973
|
document.addEventListener("pointermove", this._boundPointerMove);
|
|
2833
2974
|
document.addEventListener("pointerup", this._boundPointerUp);
|
|
2975
|
+
document.addEventListener("pointercancel", this._boundPointerUp);
|
|
2834
2976
|
}
|
|
2835
2977
|
|
|
2836
2978
|
handlePointerMove(e) {
|
|
@@ -2863,7 +3005,9 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2863
3005
|
|
|
2864
3006
|
handlePointerUp(e) {
|
|
2865
3007
|
if (this._isDragging) {
|
|
2866
|
-
this.
|
|
3008
|
+
if (this.hasPointerCapture?.(e.pointerId)) {
|
|
3009
|
+
this.releasePointerCapture(e.pointerId);
|
|
3010
|
+
}
|
|
2867
3011
|
this.style.cursor = "";
|
|
2868
3012
|
}
|
|
2869
3013
|
|
|
@@ -2872,6 +3016,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2872
3016
|
|
|
2873
3017
|
document.removeEventListener("pointermove", this._boundPointerMove);
|
|
2874
3018
|
document.removeEventListener("pointerup", this._boundPointerUp);
|
|
3019
|
+
document.removeEventListener("pointercancel", this._boundPointerUp);
|
|
2875
3020
|
e.preventDefault();
|
|
2876
3021
|
}
|
|
2877
3022
|
|
|
@@ -2885,13 +3030,17 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2885
3030
|
|
|
2886
3031
|
// Local-first: nearest parent subtree.
|
|
2887
3032
|
const localScope = this.parentElement;
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
3033
|
+
try {
|
|
3034
|
+
if (localScope?.querySelector) {
|
|
3035
|
+
const localMatch = localScope.querySelector(selector);
|
|
3036
|
+
if (localMatch && !this.contains(localMatch)) return localMatch;
|
|
3037
|
+
}
|
|
2892
3038
|
|
|
2893
|
-
|
|
2894
|
-
|
|
3039
|
+
// Fallback: global document query.
|
|
3040
|
+
return document.querySelector(selector);
|
|
3041
|
+
} catch {
|
|
3042
|
+
return null;
|
|
3043
|
+
}
|
|
2895
3044
|
}
|
|
2896
3045
|
|
|
2897
3046
|
parsePosition() {
|
|
@@ -3610,7 +3759,7 @@ class FigTab extends HTMLElement {
|
|
|
3610
3759
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
3611
3760
|
}
|
|
3612
3761
|
handleClick() {
|
|
3613
|
-
if (this
|
|
3762
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
3614
3763
|
this.selected = true;
|
|
3615
3764
|
if (this.content) {
|
|
3616
3765
|
this.content.style.display = "block";
|
|
@@ -3656,6 +3805,7 @@ class FigTabs extends HTMLElement {
|
|
|
3656
3805
|
#navStart = null;
|
|
3657
3806
|
#navEnd = null;
|
|
3658
3807
|
#isUnwrapping = false;
|
|
3808
|
+
#parentDisabledTabs = new WeakSet();
|
|
3659
3809
|
|
|
3660
3810
|
constructor() {
|
|
3661
3811
|
super();
|
|
@@ -3683,7 +3833,7 @@ class FigTabs extends HTMLElement {
|
|
|
3683
3833
|
if (value) {
|
|
3684
3834
|
this.#selectByValue(value);
|
|
3685
3835
|
}
|
|
3686
|
-
if (this
|
|
3836
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
3687
3837
|
this.#applyDisabled(true);
|
|
3688
3838
|
}
|
|
3689
3839
|
this.#syncTabIndexes();
|
|
@@ -3696,10 +3846,16 @@ class FigTabs extends HTMLElement {
|
|
|
3696
3846
|
const tabs = this.querySelectorAll("fig-tab");
|
|
3697
3847
|
tabs.forEach((tab) => {
|
|
3698
3848
|
if (disabled) {
|
|
3699
|
-
|
|
3849
|
+
const alreadyDisabled =
|
|
3850
|
+
tab.hasAttribute("disabled") && tab.getAttribute("disabled") !== "false";
|
|
3851
|
+
if (!alreadyDisabled) {
|
|
3852
|
+
this.#parentDisabledTabs.add(tab);
|
|
3853
|
+
tab.setAttribute("disabled", "");
|
|
3854
|
+
}
|
|
3700
3855
|
tab.setAttribute("aria-disabled", "true");
|
|
3701
3856
|
tab.setAttribute("tabindex", "-1");
|
|
3702
|
-
} else {
|
|
3857
|
+
} else if (this.#parentDisabledTabs.has(tab)) {
|
|
3858
|
+
this.#parentDisabledTabs.delete(tab);
|
|
3703
3859
|
tab.removeAttribute("disabled");
|
|
3704
3860
|
tab.removeAttribute("aria-disabled");
|
|
3705
3861
|
}
|
|
@@ -3715,7 +3871,11 @@ class FigTabs extends HTMLElement {
|
|
|
3715
3871
|
|
|
3716
3872
|
#syncTabIndexes() {
|
|
3717
3873
|
const tabs = Array.from(this.querySelectorAll("fig-tab"));
|
|
3718
|
-
const selected =
|
|
3874
|
+
const selected =
|
|
3875
|
+
tabs.find(
|
|
3876
|
+
(tab) =>
|
|
3877
|
+
tab.hasAttribute("selected") && tab.getAttribute("selected") !== "false",
|
|
3878
|
+
) || this.#availableTabs()[0];
|
|
3719
3879
|
tabs.forEach((tab) => {
|
|
3720
3880
|
const disabled = tab.hasAttribute("disabled") && tab.getAttribute("disabled") !== "false";
|
|
3721
3881
|
tab.setAttribute("tabindex", !disabled && tab === selected ? "0" : "-1");
|
|
@@ -3832,7 +3992,9 @@ class FigTabs extends HTMLElement {
|
|
|
3832
3992
|
#handleKeyDown(event) {
|
|
3833
3993
|
const tabs = this.#availableTabs();
|
|
3834
3994
|
if (!tabs.length) return;
|
|
3835
|
-
const currentIndex = tabs.findIndex((tab) =>
|
|
3995
|
+
const currentIndex = tabs.findIndex((tab) =>
|
|
3996
|
+
figBooleanAttribute(tab, "selected"),
|
|
3997
|
+
);
|
|
3836
3998
|
let newIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
3837
3999
|
|
|
3838
4000
|
switch (event.key) {
|
|
@@ -3925,9 +4087,18 @@ class FigTabs extends HTMLElement {
|
|
|
3925
4087
|
}
|
|
3926
4088
|
|
|
3927
4089
|
handleClick(event) {
|
|
3928
|
-
if (
|
|
4090
|
+
if (
|
|
4091
|
+
this.hasAttribute("disabled") &&
|
|
4092
|
+
this.getAttribute("disabled") !== "false"
|
|
4093
|
+
)
|
|
4094
|
+
return;
|
|
3929
4095
|
const target = event.target.closest("fig-tab");
|
|
3930
4096
|
if (!target || !this.contains(target)) return;
|
|
4097
|
+
if (
|
|
4098
|
+
target.hasAttribute("disabled") &&
|
|
4099
|
+
target.getAttribute("disabled") !== "false"
|
|
4100
|
+
)
|
|
4101
|
+
return;
|
|
3931
4102
|
const previousTab = this.selectedTab;
|
|
3932
4103
|
const previousValue = this.value;
|
|
3933
4104
|
const tabs = this.querySelectorAll("fig-tab");
|
|
@@ -3975,6 +4146,12 @@ class FigSegment extends HTMLElement {
|
|
|
3975
4146
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
3976
4147
|
}
|
|
3977
4148
|
handleClick() {
|
|
4149
|
+
if (
|
|
4150
|
+
this.hasAttribute("disabled") &&
|
|
4151
|
+
this.getAttribute("disabled") !== "false"
|
|
4152
|
+
) {
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
3978
4155
|
const parentControl = this.closest("fig-segmented-control");
|
|
3979
4156
|
if (
|
|
3980
4157
|
parentControl &&
|
|
@@ -4049,6 +4226,7 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4049
4226
|
#indicatorFrame = 0;
|
|
4050
4227
|
#indicatorSyncInstant = false;
|
|
4051
4228
|
#hasRenderedIndicator = false;
|
|
4229
|
+
#parentDisabledSegments = new WeakSet();
|
|
4052
4230
|
|
|
4053
4231
|
constructor() {
|
|
4054
4232
|
super();
|
|
@@ -4101,7 +4279,7 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4101
4279
|
const segments = this.querySelectorAll("fig-segment");
|
|
4102
4280
|
for (const seg of segments) {
|
|
4103
4281
|
const shouldBeSelected = seg === segment;
|
|
4104
|
-
const isSelected = seg
|
|
4282
|
+
const isSelected = figBooleanAttribute(seg, "selected");
|
|
4105
4283
|
if (shouldBeSelected && !isSelected) {
|
|
4106
4284
|
seg.setAttribute("selected", "true");
|
|
4107
4285
|
} else if (!shouldBeSelected && isSelected) {
|
|
@@ -4156,7 +4334,11 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4156
4334
|
#getFirstSelectedSegment() {
|
|
4157
4335
|
const segments = this.querySelectorAll("fig-segment");
|
|
4158
4336
|
for (const segment of segments) {
|
|
4159
|
-
if (
|
|
4337
|
+
if (
|
|
4338
|
+
segment.hasAttribute("selected") &&
|
|
4339
|
+
segment.getAttribute("selected") !== "false"
|
|
4340
|
+
)
|
|
4341
|
+
return segment;
|
|
4160
4342
|
}
|
|
4161
4343
|
return null;
|
|
4162
4344
|
}
|
|
@@ -4171,7 +4353,11 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4171
4353
|
|
|
4172
4354
|
#syncSegmentA11y() {
|
|
4173
4355
|
const segments = Array.from(this.querySelectorAll("fig-segment"));
|
|
4174
|
-
const selected = segments.find(
|
|
4356
|
+
const selected = segments.find(
|
|
4357
|
+
(segment) =>
|
|
4358
|
+
segment.hasAttribute("selected") &&
|
|
4359
|
+
segment.getAttribute("selected") !== "false",
|
|
4360
|
+
);
|
|
4175
4361
|
segments.forEach((segment) => {
|
|
4176
4362
|
const disabled =
|
|
4177
4363
|
segment.hasAttribute("disabled") &&
|
|
@@ -4211,7 +4397,7 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4211
4397
|
const segments = this.#availableSegments();
|
|
4212
4398
|
if (!segments.length) return;
|
|
4213
4399
|
const currentIndex = segments.findIndex((segment) =>
|
|
4214
|
-
segment
|
|
4400
|
+
figBooleanAttribute(segment, "selected"),
|
|
4215
4401
|
);
|
|
4216
4402
|
let nextIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
4217
4403
|
|
|
@@ -4388,7 +4574,7 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4388
4574
|
}
|
|
4389
4575
|
|
|
4390
4576
|
if (enforceFallback) {
|
|
4391
|
-
this.selectedSegment =
|
|
4577
|
+
this.selectedSegment = this.#availableSegments()[0] || null;
|
|
4392
4578
|
}
|
|
4393
4579
|
}
|
|
4394
4580
|
|
|
@@ -4449,6 +4635,11 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4449
4635
|
}
|
|
4450
4636
|
const segment = event.target.closest("fig-segment");
|
|
4451
4637
|
if (!segment || !this.contains(segment)) return;
|
|
4638
|
+
if (
|
|
4639
|
+
segment.hasAttribute("disabled") &&
|
|
4640
|
+
segment.getAttribute("disabled") !== "false"
|
|
4641
|
+
)
|
|
4642
|
+
return;
|
|
4452
4643
|
|
|
4453
4644
|
this.#selectSegment(segment);
|
|
4454
4645
|
}
|
|
@@ -4457,9 +4648,16 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
4457
4648
|
this.setAttribute("aria-disabled", disabled ? "true" : "false");
|
|
4458
4649
|
this.querySelectorAll("fig-segment").forEach((segment) => {
|
|
4459
4650
|
if (disabled) {
|
|
4460
|
-
|
|
4651
|
+
const alreadyDisabled =
|
|
4652
|
+
segment.hasAttribute("disabled") &&
|
|
4653
|
+
segment.getAttribute("disabled") !== "false";
|
|
4654
|
+
if (!alreadyDisabled) {
|
|
4655
|
+
this.#parentDisabledSegments.add(segment);
|
|
4656
|
+
segment.setAttribute("disabled", "");
|
|
4657
|
+
}
|
|
4461
4658
|
segment.setAttribute("aria-disabled", "true");
|
|
4462
|
-
} else {
|
|
4659
|
+
} else if (this.#parentDisabledSegments.has(segment)) {
|
|
4660
|
+
this.#parentDisabledSegments.delete(segment);
|
|
4463
4661
|
segment.removeAttribute("disabled");
|
|
4464
4662
|
segment.removeAttribute("aria-disabled");
|
|
4465
4663
|
}
|
|
@@ -4652,8 +4850,8 @@ class FigOptions extends HTMLElement {
|
|
|
4652
4850
|
const sc = document.createElement("fig-segmented-control");
|
|
4653
4851
|
sc.setAttribute("sizing", this.getAttribute("sizing") || "equal");
|
|
4654
4852
|
|
|
4655
|
-
if (this
|
|
4656
|
-
if (this
|
|
4853
|
+
if (figBooleanAttribute(this, "disabled")) sc.setAttribute("disabled", "");
|
|
4854
|
+
if (figBooleanAttribute(this, "full")) sc.setAttribute("full", "");
|
|
4657
4855
|
|
|
4658
4856
|
const currentValue = this.getAttribute("value");
|
|
4659
4857
|
|
|
@@ -4709,7 +4907,7 @@ class FigOptions extends HTMLElement {
|
|
|
4709
4907
|
if (this.#parsedOptions.length === 0) return;
|
|
4710
4908
|
|
|
4711
4909
|
const dd = document.createElement("fig-dropdown");
|
|
4712
|
-
if (this
|
|
4910
|
+
if (figBooleanAttribute(this, "disabled")) dd.setAttribute("disabled", "");
|
|
4713
4911
|
|
|
4714
4912
|
const currentValue = this.getAttribute("value");
|
|
4715
4913
|
|
|
@@ -4892,7 +5090,8 @@ class FigSlider extends HTMLElement {
|
|
|
4892
5090
|
|
|
4893
5091
|
constructor() {
|
|
4894
5092
|
super();
|
|
4895
|
-
|
|
5093
|
+
// Parser-created custom elements do not have their children yet here.
|
|
5094
|
+
this.initialInnerHTML = null;
|
|
4896
5095
|
|
|
4897
5096
|
// Bind the event handlers
|
|
4898
5097
|
this.#boundHandleInput = (e) => {
|
|
@@ -4932,7 +5131,8 @@ class FigSlider extends HTMLElement {
|
|
|
4932
5131
|
this.text = this.getAttribute("text") !== "false";
|
|
4933
5132
|
this.units = this.getAttribute("units") || "";
|
|
4934
5133
|
this.transform = Number(this.getAttribute("transform") || 1);
|
|
4935
|
-
this.disabled =
|
|
5134
|
+
this.disabled =
|
|
5135
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
4936
5136
|
this.precision = this.hasAttribute("precision")
|
|
4937
5137
|
? Number(this.getAttribute("precision"))
|
|
4938
5138
|
: null;
|
|
@@ -4941,10 +5141,16 @@ class FigSlider extends HTMLElement {
|
|
|
4941
5141
|
? this.getAttribute("placeholder")
|
|
4942
5142
|
: "##";
|
|
4943
5143
|
|
|
4944
|
-
const defaults = this.#typeDefaults[this.type];
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
5144
|
+
const defaults = this.#typeDefaults[this.type] || this.#typeDefaults.range;
|
|
5145
|
+
const readNumber = (name, fallback) => {
|
|
5146
|
+
const raw = this.getAttribute(name);
|
|
5147
|
+
const parsed = raw === null || raw.trim() === "" ? fallback : Number(raw);
|
|
5148
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
5149
|
+
};
|
|
5150
|
+
this.min = readNumber("min", defaults.min);
|
|
5151
|
+
this.max = readNumber("max", defaults.max);
|
|
5152
|
+
this.step = readNumber("step", defaults.step);
|
|
5153
|
+
if (!(this.step > 0)) this.step = defaults.step > 0 ? defaults.step : 1;
|
|
4948
5154
|
this.color = this.getAttribute("color") || defaults?.color;
|
|
4949
5155
|
this.default = this.hasAttribute("default")
|
|
4950
5156
|
? this.getAttribute("default")
|
|
@@ -5022,6 +5228,8 @@ class FigSlider extends HTMLElement {
|
|
|
5022
5228
|
this.input.addEventListener("pointerdown", this.#boundRangePointerDown);
|
|
5023
5229
|
this.input.removeEventListener("pointerup", this.#boundRangePointerUp);
|
|
5024
5230
|
this.input.addEventListener("pointerup", this.#boundRangePointerUp);
|
|
5231
|
+
this.input.removeEventListener("pointercancel", this.#boundRangePointerUp);
|
|
5232
|
+
this.input.addEventListener("pointercancel", this.#boundRangePointerUp);
|
|
5025
5233
|
|
|
5026
5234
|
if (this.default) {
|
|
5027
5235
|
this.style.setProperty(
|
|
@@ -5077,13 +5285,13 @@ class FigSlider extends HTMLElement {
|
|
|
5077
5285
|
if (this.text) {
|
|
5078
5286
|
html = `${slider}
|
|
5079
5287
|
<fig-input-number
|
|
5080
|
-
placeholder="${this.placeholder}"
|
|
5288
|
+
placeholder="${figEscapeAttribute(this.placeholder)}"
|
|
5081
5289
|
min="${this.min}"
|
|
5082
5290
|
max="${this.max}"
|
|
5083
5291
|
transform="${this.transform}"
|
|
5084
5292
|
step="${this.step}"
|
|
5085
5293
|
value="${this.#showEmptyTextValue ? "" : this.value}"
|
|
5086
|
-
${this.units ? `units="${this.units}"` : ""}
|
|
5294
|
+
${this.units ? `units="${figEscapeAttribute(this.units)}"` : ""}
|
|
5087
5295
|
${this.precision !== null ? `precision="${this.precision}"` : ""}>
|
|
5088
5296
|
</fig-input-number>`;
|
|
5089
5297
|
} else {
|
|
@@ -5107,7 +5315,10 @@ class FigSlider extends HTMLElement {
|
|
|
5107
5315
|
} else if (this.type === "stepper") {
|
|
5108
5316
|
this.datalist = document.createElement("datalist");
|
|
5109
5317
|
this.datalist.setAttribute("id", figUniqueId());
|
|
5110
|
-
|
|
5318
|
+
const steps = Math.min(
|
|
5319
|
+
1001,
|
|
5320
|
+
Math.max(0, Math.floor((this.max - this.min) / this.step) + 1),
|
|
5321
|
+
);
|
|
5111
5322
|
for (let i = 0; i < steps; i++) {
|
|
5112
5323
|
let option = document.createElement("option");
|
|
5113
5324
|
option.setAttribute("value", this.min + i * this.step);
|
|
@@ -5136,6 +5347,7 @@ class FigSlider extends HTMLElement {
|
|
|
5136
5347
|
}
|
|
5137
5348
|
|
|
5138
5349
|
connectedCallback() {
|
|
5350
|
+
if (this.initialInnerHTML === null) this.initialInnerHTML = this.innerHTML;
|
|
5139
5351
|
if (this.#canReuseRenderedMarkup()) {
|
|
5140
5352
|
this.#updateRenderedMarkup();
|
|
5141
5353
|
this.#bindControlListeners();
|
|
@@ -5184,6 +5396,7 @@ class FigSlider extends HTMLElement {
|
|
|
5184
5396
|
this.input.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
5185
5397
|
this.input.removeEventListener("pointerdown", this.#boundRangePointerDown);
|
|
5186
5398
|
this.input.removeEventListener("pointerup", this.#boundRangePointerUp);
|
|
5399
|
+
this.input.removeEventListener("pointercancel", this.#boundRangePointerUp);
|
|
5187
5400
|
}
|
|
5188
5401
|
if (this.figInputNumber) {
|
|
5189
5402
|
this.figInputNumber.removeEventListener(
|
|
@@ -5536,6 +5749,8 @@ class FigInputText extends HTMLElement {
|
|
|
5536
5749
|
#boundAdornmentClick;
|
|
5537
5750
|
#mutationObserver = null;
|
|
5538
5751
|
#syncRaf = 0;
|
|
5752
|
+
#previousUserSelect = "";
|
|
5753
|
+
#previousBodyCursor = "";
|
|
5539
5754
|
#a11yAttributes = [
|
|
5540
5755
|
"aria-label",
|
|
5541
5756
|
"aria-labelledby",
|
|
@@ -5555,15 +5770,25 @@ class FigInputText extends HTMLElement {
|
|
|
5555
5770
|
e.stopPropagation();
|
|
5556
5771
|
this.#handleInputChange(e);
|
|
5557
5772
|
};
|
|
5558
|
-
this.#boundNativeInput = () => {
|
|
5773
|
+
this.#boundNativeInput = (e) => {
|
|
5774
|
+
e.stopPropagation();
|
|
5775
|
+
let value = e.target.value;
|
|
5776
|
+
if (this.type === "number") {
|
|
5777
|
+
value = Number(value) / (this.transform || 1);
|
|
5778
|
+
}
|
|
5779
|
+
this.value = value;
|
|
5559
5780
|
this.#syncSearchClearVisibility();
|
|
5781
|
+
this.dispatchEvent(
|
|
5782
|
+
new CustomEvent("input", { detail: this.value, bubbles: true }),
|
|
5783
|
+
);
|
|
5560
5784
|
};
|
|
5561
5785
|
this.#boundFocusControl = this.focus.bind(this);
|
|
5562
5786
|
this.#boundAdornmentClick = this.#handleAdornmentClick.bind(this);
|
|
5563
5787
|
}
|
|
5564
5788
|
|
|
5565
5789
|
connectedCallback() {
|
|
5566
|
-
this.multiline =
|
|
5790
|
+
this.multiline =
|
|
5791
|
+
this.hasAttribute("multiline") && this.getAttribute("multiline") !== "false";
|
|
5567
5792
|
this.value = this.getAttribute("value") || "";
|
|
5568
5793
|
this.type = this.getAttribute("type") || "text";
|
|
5569
5794
|
this.placeholder = this.getAttribute("placeholder") || "";
|
|
@@ -5590,6 +5815,9 @@ class FigInputText extends HTMLElement {
|
|
|
5590
5815
|
|
|
5591
5816
|
this.input = this.#ensureInputControl();
|
|
5592
5817
|
this.input.readOnly = this.readonly;
|
|
5818
|
+
this.disabled =
|
|
5819
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
5820
|
+
this.input.disabled = this.disabled;
|
|
5593
5821
|
this.#syncInputA11yAttributes();
|
|
5594
5822
|
this.#syncSearchPrefix();
|
|
5595
5823
|
this.#syncSearchClear();
|
|
@@ -5878,9 +6106,6 @@ class FigInputText extends HTMLElement {
|
|
|
5878
6106
|
this.value = value;
|
|
5879
6107
|
this.input.value = valueTransformed;
|
|
5880
6108
|
this.#syncSearchClearVisibility();
|
|
5881
|
-
this.dispatchEvent(
|
|
5882
|
-
new CustomEvent("input", { detail: this.value, bubbles: true }),
|
|
5883
|
-
);
|
|
5884
6109
|
this.dispatchEvent(
|
|
5885
6110
|
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
5886
6111
|
);
|
|
@@ -5903,14 +6128,13 @@ class FigInputText extends HTMLElement {
|
|
|
5903
6128
|
this.dispatchEvent(
|
|
5904
6129
|
new CustomEvent("input", { detail: this.value, bubbles: true }),
|
|
5905
6130
|
);
|
|
5906
|
-
this.dispatchEvent(
|
|
5907
|
-
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
5908
|
-
);
|
|
5909
6131
|
}
|
|
5910
6132
|
#handleMouseDown(e) {
|
|
5911
6133
|
if (this.type !== "number") return;
|
|
5912
6134
|
if (e.altKey || e.target.closest("[slot]")) {
|
|
5913
6135
|
this.#isInteracting = true;
|
|
6136
|
+
this.#previousUserSelect = this.style.userSelect;
|
|
6137
|
+
this.#previousBodyCursor = document.body.style.cursor;
|
|
5914
6138
|
this.input.style.cursor =
|
|
5915
6139
|
this.style.cursor =
|
|
5916
6140
|
document.body.style.cursor =
|
|
@@ -5918,20 +6142,27 @@ class FigInputText extends HTMLElement {
|
|
|
5918
6142
|
this.style.userSelect = "none";
|
|
5919
6143
|
window.addEventListener("pointermove", this.#boundMouseMove);
|
|
5920
6144
|
window.addEventListener("pointerup", this.#boundMouseUp);
|
|
6145
|
+
window.addEventListener("pointercancel", this.#boundMouseUp);
|
|
5921
6146
|
window.addEventListener("blur", this.#boundWindowBlur);
|
|
5922
6147
|
}
|
|
5923
6148
|
}
|
|
5924
6149
|
#handleMouseUp(e) {
|
|
5925
6150
|
if (this.type !== "number") return;
|
|
6151
|
+
const wasInteracting = this.#isInteracting;
|
|
5926
6152
|
this.#isInteracting = false;
|
|
5927
|
-
this.input.style.cursor =
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
this.style.userSelect = "all";
|
|
6153
|
+
this.input.style.cursor = "";
|
|
6154
|
+
this.style.cursor = "";
|
|
6155
|
+
document.body.style.cursor = this.#previousBodyCursor;
|
|
6156
|
+
this.style.userSelect = this.#previousUserSelect;
|
|
5932
6157
|
window.removeEventListener("pointermove", this.#boundMouseMove);
|
|
5933
6158
|
window.removeEventListener("pointerup", this.#boundMouseUp);
|
|
6159
|
+
window.removeEventListener("pointercancel", this.#boundMouseUp);
|
|
5934
6160
|
window.removeEventListener("blur", this.#boundWindowBlur);
|
|
6161
|
+
if (wasInteracting) {
|
|
6162
|
+
this.dispatchEvent(
|
|
6163
|
+
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
6164
|
+
);
|
|
6165
|
+
}
|
|
5935
6166
|
}
|
|
5936
6167
|
#sanitizeInput(value, transform = true) {
|
|
5937
6168
|
let sanitized = value;
|
|
@@ -5978,6 +6209,7 @@ class FigInputText extends HTMLElement {
|
|
|
5978
6209
|
"disabled",
|
|
5979
6210
|
"readonly",
|
|
5980
6211
|
"type",
|
|
6212
|
+
"multiline",
|
|
5981
6213
|
"step",
|
|
5982
6214
|
"min",
|
|
5983
6215
|
"max",
|
|
@@ -6024,14 +6256,18 @@ class FigInputText extends HTMLElement {
|
|
|
6024
6256
|
case "min":
|
|
6025
6257
|
case "max":
|
|
6026
6258
|
case "step":
|
|
6027
|
-
|
|
6028
|
-
|
|
6259
|
+
if (newValue === null || newValue === "") {
|
|
6260
|
+
this[name] = undefined;
|
|
6261
|
+
this.input.removeAttribute(name);
|
|
6262
|
+
} else {
|
|
6263
|
+
this[name] = this.input[name] = Number(newValue);
|
|
6029
6264
|
this.input.setAttribute(name, newValue);
|
|
6030
6265
|
}
|
|
6031
6266
|
break;
|
|
6032
6267
|
case "name":
|
|
6033
|
-
this[name] = this.input[name] = newValue;
|
|
6034
|
-
this.input.setAttribute("name", newValue);
|
|
6268
|
+
this[name] = this.input[name] = newValue || "";
|
|
6269
|
+
if (newValue) this.input.setAttribute("name", newValue);
|
|
6270
|
+
else this.input.removeAttribute("name");
|
|
6035
6271
|
break;
|
|
6036
6272
|
case "placeholder":
|
|
6037
6273
|
this.placeholder = newValue ?? "";
|
|
@@ -6040,11 +6276,42 @@ class FigInputText extends HTMLElement {
|
|
|
6040
6276
|
case "type":
|
|
6041
6277
|
this.type = newValue || "text";
|
|
6042
6278
|
this.input.type = this.type;
|
|
6279
|
+
this.removeEventListener("pointerdown", this.#boundMouseDown);
|
|
6280
|
+
if (this.type === "number") {
|
|
6281
|
+
this.step = this.hasAttribute("step")
|
|
6282
|
+
? Number(this.getAttribute("step"))
|
|
6283
|
+
: undefined;
|
|
6284
|
+
this.min = this.hasAttribute("min")
|
|
6285
|
+
? Number(this.getAttribute("min"))
|
|
6286
|
+
: undefined;
|
|
6287
|
+
this.max = this.hasAttribute("max")
|
|
6288
|
+
? Number(this.getAttribute("max"))
|
|
6289
|
+
: undefined;
|
|
6290
|
+
this.transform = Number(this.getAttribute("transform") || 1);
|
|
6291
|
+
this.addEventListener("pointerdown", this.#boundMouseDown);
|
|
6292
|
+
}
|
|
6043
6293
|
this.#syncSearchPrefix();
|
|
6044
6294
|
this.#syncSearchClear();
|
|
6045
6295
|
this.#syncSearchClearVisibility();
|
|
6046
6296
|
this.#syncPasswordToggle();
|
|
6047
6297
|
break;
|
|
6298
|
+
case "multiline": {
|
|
6299
|
+
const next = newValue !== null && newValue !== "false";
|
|
6300
|
+
if (next !== this.multiline) {
|
|
6301
|
+
this.multiline = next;
|
|
6302
|
+
const oldInput = this.input;
|
|
6303
|
+
oldInput.removeEventListener("change", this.#boundInputChange);
|
|
6304
|
+
oldInput.removeEventListener("input", this.#boundNativeInput);
|
|
6305
|
+
oldInput.remove();
|
|
6306
|
+
this.input = this.#ensureInputControl();
|
|
6307
|
+
this.input.readOnly = this.readonly;
|
|
6308
|
+
this.input.disabled = Boolean(this.disabled);
|
|
6309
|
+
this.input.addEventListener("change", this.#boundInputChange);
|
|
6310
|
+
this.input.addEventListener("input", this.#boundNativeInput);
|
|
6311
|
+
this.#syncInputA11yAttributes();
|
|
6312
|
+
}
|
|
6313
|
+
break;
|
|
6314
|
+
}
|
|
6048
6315
|
case "aria-label":
|
|
6049
6316
|
case "aria-labelledby":
|
|
6050
6317
|
case "aria-describedby":
|
|
@@ -6097,6 +6364,8 @@ class FigInputNumber extends HTMLElement {
|
|
|
6097
6364
|
#stepperEl = null;
|
|
6098
6365
|
#mutationObserver = null;
|
|
6099
6366
|
#syncRaf = 0;
|
|
6367
|
+
#previousUserSelect = "";
|
|
6368
|
+
#previousBodyCursor = "";
|
|
6100
6369
|
#a11yAttributes = [
|
|
6101
6370
|
"aria-label",
|
|
6102
6371
|
"aria-labelledby",
|
|
@@ -6234,9 +6503,9 @@ class FigInputNumber extends HTMLElement {
|
|
|
6234
6503
|
);
|
|
6235
6504
|
this.#setUnitsFromAttributes();
|
|
6236
6505
|
this.#unitPosition = this.getAttribute("unit-position") || "suffix";
|
|
6237
|
-
this.#precision = this
|
|
6238
|
-
?
|
|
6239
|
-
|
|
6506
|
+
this.#precision = this.#normalizePrecision(
|
|
6507
|
+
this.hasAttribute("precision") ? this.getAttribute("precision") : 2,
|
|
6508
|
+
);
|
|
6240
6509
|
|
|
6241
6510
|
if (this.getAttribute("step")) {
|
|
6242
6511
|
this.step = Number(this.getAttribute("step"));
|
|
@@ -6495,9 +6764,6 @@ class FigInputNumber extends HTMLElement {
|
|
|
6495
6764
|
}
|
|
6496
6765
|
this.#syncStepperState();
|
|
6497
6766
|
this.#syncSpinbuttonAria();
|
|
6498
|
-
this.dispatchEvent(
|
|
6499
|
-
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
6500
|
-
);
|
|
6501
6767
|
}
|
|
6502
6768
|
|
|
6503
6769
|
#handleKeyDown(e) {
|
|
@@ -6559,9 +6825,6 @@ class FigInputNumber extends HTMLElement {
|
|
|
6559
6825
|
}
|
|
6560
6826
|
this.#syncStepperState();
|
|
6561
6827
|
this.#syncSpinbuttonAria();
|
|
6562
|
-
this.dispatchEvent(
|
|
6563
|
-
new CustomEvent("input", { detail: this.value, bubbles: true }),
|
|
6564
|
-
);
|
|
6565
6828
|
this.dispatchEvent(
|
|
6566
6829
|
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
6567
6830
|
);
|
|
@@ -6584,15 +6847,14 @@ class FigInputNumber extends HTMLElement {
|
|
|
6584
6847
|
this.dispatchEvent(
|
|
6585
6848
|
new CustomEvent("input", { detail: this.value, bubbles: true }),
|
|
6586
6849
|
);
|
|
6587
|
-
this.dispatchEvent(
|
|
6588
|
-
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
6589
|
-
);
|
|
6590
6850
|
}
|
|
6591
6851
|
|
|
6592
6852
|
#handleMouseDown(e) {
|
|
6593
6853
|
if (this.disabled) return;
|
|
6594
6854
|
if (e.altKey || e.target.closest("[slot]")) {
|
|
6595
6855
|
this.#isInteracting = true;
|
|
6856
|
+
this.#previousUserSelect = this.style.userSelect;
|
|
6857
|
+
this.#previousBodyCursor = document.body.style.cursor;
|
|
6596
6858
|
this.input.style.cursor =
|
|
6597
6859
|
this.style.cursor =
|
|
6598
6860
|
document.body.style.cursor =
|
|
@@ -6600,20 +6862,27 @@ class FigInputNumber extends HTMLElement {
|
|
|
6600
6862
|
this.style.userSelect = "none";
|
|
6601
6863
|
window.addEventListener("pointermove", this.#boundMouseMove);
|
|
6602
6864
|
window.addEventListener("pointerup", this.#boundMouseUp);
|
|
6865
|
+
window.addEventListener("pointercancel", this.#boundMouseUp);
|
|
6603
6866
|
window.addEventListener("blur", this.#boundWindowBlur);
|
|
6604
6867
|
}
|
|
6605
6868
|
}
|
|
6606
6869
|
|
|
6607
6870
|
#handleMouseUp(e) {
|
|
6871
|
+
const wasInteracting = this.#isInteracting;
|
|
6608
6872
|
this.#isInteracting = false;
|
|
6609
|
-
this.input.style.cursor =
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
this.style.userSelect = "all";
|
|
6873
|
+
this.input.style.cursor = "";
|
|
6874
|
+
this.style.cursor = "";
|
|
6875
|
+
document.body.style.cursor = this.#previousBodyCursor;
|
|
6876
|
+
this.style.userSelect = this.#previousUserSelect;
|
|
6614
6877
|
window.removeEventListener("pointermove", this.#boundMouseMove);
|
|
6615
6878
|
window.removeEventListener("pointerup", this.#boundMouseUp);
|
|
6879
|
+
window.removeEventListener("pointercancel", this.#boundMouseUp);
|
|
6616
6880
|
window.removeEventListener("blur", this.#boundWindowBlur);
|
|
6881
|
+
if (wasInteracting) {
|
|
6882
|
+
this.dispatchEvent(
|
|
6883
|
+
new CustomEvent("change", { detail: this.value, bubbles: true }),
|
|
6884
|
+
);
|
|
6885
|
+
}
|
|
6617
6886
|
}
|
|
6618
6887
|
|
|
6619
6888
|
#sanitizeInput(value, transform = true) {
|
|
@@ -6639,6 +6908,12 @@ class FigInputNumber extends HTMLElement {
|
|
|
6639
6908
|
: parseFloat(rounded.toFixed(precision));
|
|
6640
6909
|
}
|
|
6641
6910
|
|
|
6911
|
+
#normalizePrecision(value) {
|
|
6912
|
+
const parsed = Number(value);
|
|
6913
|
+
if (!Number.isFinite(parsed)) return 2;
|
|
6914
|
+
return Math.max(0, Math.min(100, Math.trunc(parsed)));
|
|
6915
|
+
}
|
|
6916
|
+
|
|
6642
6917
|
static get observedAttributes() {
|
|
6643
6918
|
return [
|
|
6644
6919
|
"value",
|
|
@@ -6727,13 +7002,16 @@ class FigInputNumber extends HTMLElement {
|
|
|
6727
7002
|
break;
|
|
6728
7003
|
}
|
|
6729
7004
|
case "precision":
|
|
6730
|
-
this.#precision =
|
|
7005
|
+
this.#precision = this.#normalizePrecision(
|
|
7006
|
+
newValue !== null ? newValue : 2,
|
|
7007
|
+
);
|
|
6731
7008
|
this.input.value = this.#formatWithUnit(this.value);
|
|
6732
7009
|
this.#syncSpinbuttonAria();
|
|
6733
7010
|
break;
|
|
6734
7011
|
case "name":
|
|
6735
|
-
this[name] = this.input[name] = newValue;
|
|
6736
|
-
this.input.setAttribute("name", newValue);
|
|
7012
|
+
this[name] = this.input[name] = newValue || "";
|
|
7013
|
+
if (newValue) this.input.setAttribute("name", newValue);
|
|
7014
|
+
else this.input.removeAttribute("name");
|
|
6737
7015
|
break;
|
|
6738
7016
|
case "placeholder":
|
|
6739
7017
|
this.placeholder = newValue ?? "";
|
|
@@ -6770,11 +7048,16 @@ class FigAvatar extends HTMLElement {
|
|
|
6770
7048
|
}
|
|
6771
7049
|
setSrc(src) {
|
|
6772
7050
|
this.src = src;
|
|
6773
|
-
if (src) {
|
|
6774
|
-
this.
|
|
6775
|
-
|
|
6776
|
-
|
|
7051
|
+
if (!src) {
|
|
7052
|
+
this.querySelector(":scope > img")?.remove();
|
|
7053
|
+
this.img = null;
|
|
7054
|
+
return;
|
|
6777
7055
|
}
|
|
7056
|
+
const img = this.querySelector(":scope > img") || document.createElement("img");
|
|
7057
|
+
img.src = src;
|
|
7058
|
+
img.alt = this.name || "";
|
|
7059
|
+
if (!img.isConnected) this.replaceChildren(img);
|
|
7060
|
+
this.img = img;
|
|
6778
7061
|
}
|
|
6779
7062
|
getInitials(name) {
|
|
6780
7063
|
return name
|
|
@@ -6790,7 +7073,7 @@ class FigAvatar extends HTMLElement {
|
|
|
6790
7073
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
6791
7074
|
this[name] = newValue;
|
|
6792
7075
|
if (name === "name") {
|
|
6793
|
-
this.img
|
|
7076
|
+
if (this.img) this.img.alt = newValue || "";
|
|
6794
7077
|
this.name = newValue;
|
|
6795
7078
|
this.initials = this.getInitials(this.name);
|
|
6796
7079
|
this.setAttribute("initials", this.initials);
|
|
@@ -6870,7 +7153,12 @@ class FigField extends HTMLElement {
|
|
|
6870
7153
|
this.#chevron.addEventListener("click", this.#boundToggle);
|
|
6871
7154
|
this.label.addEventListener("click", this.#boundToggle);
|
|
6872
7155
|
} else if (this.input && this.label) {
|
|
6873
|
-
this.
|
|
7156
|
+
const nativeInputs = this.input.querySelectorAll("input, select, textarea");
|
|
7157
|
+
// A label with a `for` target already focuses and activates its control.
|
|
7158
|
+
// Only use the composite fallback when there is no single native target.
|
|
7159
|
+
if (nativeInputs.length !== 1) {
|
|
7160
|
+
this.label.addEventListener("click", this.#boundFocus);
|
|
7161
|
+
}
|
|
6874
7162
|
}
|
|
6875
7163
|
|
|
6876
7164
|
if (this.input && this.label && !this.#toggleable) {
|
|
@@ -6964,7 +7252,6 @@ class FigField extends HTMLElement {
|
|
|
6964
7252
|
const nativeInputs = this.input.querySelectorAll("input, select, textarea");
|
|
6965
7253
|
if (nativeInputs.length === 1) {
|
|
6966
7254
|
nativeInputs[0].focus();
|
|
6967
|
-
nativeInputs[0].click();
|
|
6968
7255
|
} else {
|
|
6969
7256
|
this.input.focus();
|
|
6970
7257
|
if (nativeInputs.length === 0) {
|
|
@@ -7096,7 +7383,7 @@ class FigInputColor extends HTMLElement {
|
|
|
7096
7383
|
|
|
7097
7384
|
#bindControlListeners() {
|
|
7098
7385
|
if (this.#swatch) {
|
|
7099
|
-
this.#swatch.disabled = this
|
|
7386
|
+
this.#swatch.disabled = figBooleanAttribute(this, "disabled");
|
|
7100
7387
|
const swatchInput = this.#swatch.querySelector('input[type="color"]');
|
|
7101
7388
|
if (this.#textInput || this.hasAttribute("swatch-disabled")) {
|
|
7102
7389
|
swatchInput?.setAttribute("tabindex", "-1");
|
|
@@ -7197,7 +7484,7 @@ class FigInputColor extends HTMLElement {
|
|
|
7197
7484
|
} else {
|
|
7198
7485
|
this.#fillPicker.setAttribute("alpha", "false");
|
|
7199
7486
|
}
|
|
7200
|
-
if (this
|
|
7487
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
7201
7488
|
this.#fillPicker.setAttribute("disabled", "");
|
|
7202
7489
|
} else {
|
|
7203
7490
|
this.#fillPicker.removeAttribute("disabled");
|
|
@@ -7231,7 +7518,11 @@ class FigInputColor extends HTMLElement {
|
|
|
7231
7518
|
}
|
|
7232
7519
|
|
|
7233
7520
|
#openFillPicker() {
|
|
7234
|
-
if (
|
|
7521
|
+
if (
|
|
7522
|
+
figBooleanAttribute(this, "disabled") ||
|
|
7523
|
+
figBooleanAttribute(this, "swatch-disabled")
|
|
7524
|
+
)
|
|
7525
|
+
return false;
|
|
7235
7526
|
const picker = this.#ensureFillPicker();
|
|
7236
7527
|
if (!picker) return false;
|
|
7237
7528
|
requestAnimationFrame(() => picker.open?.());
|
|
@@ -7246,7 +7537,11 @@ class FigInputColor extends HTMLElement {
|
|
|
7246
7537
|
|
|
7247
7538
|
#handleSwatchPointerDown(event) {
|
|
7248
7539
|
if (!hasFigFillPicker()) return;
|
|
7249
|
-
if (
|
|
7540
|
+
if (
|
|
7541
|
+
figBooleanAttribute(this, "disabled") ||
|
|
7542
|
+
figBooleanAttribute(this, "swatch-disabled")
|
|
7543
|
+
)
|
|
7544
|
+
return;
|
|
7250
7545
|
this.#pendingFillPickerPointerOpen = true;
|
|
7251
7546
|
this.#suppressNativeColorClick = true;
|
|
7252
7547
|
if (this.#nativeColorClickTimer) clearTimeout(this.#nativeColorClickTimer);
|
|
@@ -7300,8 +7595,14 @@ class FigInputColor extends HTMLElement {
|
|
|
7300
7595
|
//do not propagate to onInput handler for web component
|
|
7301
7596
|
event.stopPropagation();
|
|
7302
7597
|
// Add # prefix if not present for internal processing
|
|
7303
|
-
let inputValue = event.target.value.replace(
|
|
7598
|
+
let inputValue = event.target.value.replace(/#/g, "");
|
|
7304
7599
|
this.#setValues("#" + inputValue);
|
|
7600
|
+
if (this.#textInput) {
|
|
7601
|
+
this.#textInput.setAttribute(
|
|
7602
|
+
"value",
|
|
7603
|
+
this.hexOpaque.slice(1).toUpperCase(),
|
|
7604
|
+
);
|
|
7605
|
+
}
|
|
7305
7606
|
if (this.#alphaInput) {
|
|
7306
7607
|
this.#alphaInput.setAttribute("value", this.#alphaPercent);
|
|
7307
7608
|
}
|
|
@@ -7493,7 +7794,10 @@ class FigInputColor extends HTMLElement {
|
|
|
7493
7794
|
case "value":
|
|
7494
7795
|
this.#setValues(newValue);
|
|
7495
7796
|
if (this.#textInput) {
|
|
7496
|
-
this.#textInput.setAttribute(
|
|
7797
|
+
this.#textInput.setAttribute(
|
|
7798
|
+
"value",
|
|
7799
|
+
this.hexOpaque.slice(1).toUpperCase(),
|
|
7800
|
+
);
|
|
7497
7801
|
}
|
|
7498
7802
|
if (this.#swatch) {
|
|
7499
7803
|
this.#swatch.setAttribute("background", this.hexOpaque);
|
|
@@ -8043,7 +8347,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8043
8347
|
}
|
|
8044
8348
|
if (!attrs["dialog-position"]) attrs["dialog-position"] = "left";
|
|
8045
8349
|
return Object.entries(attrs)
|
|
8046
|
-
.map(([k, v]) => `${k}="${v}"`)
|
|
8350
|
+
.map(([k, v]) => `${k}="${figEscapeAttribute(v)}"`)
|
|
8047
8351
|
.join(" ");
|
|
8048
8352
|
}
|
|
8049
8353
|
|
|
@@ -8063,7 +8367,8 @@ class FigInputFill extends HTMLElement {
|
|
|
8063
8367
|
return `rgba(${r}, ${g}, ${b}, ${alpha}) ${stop.position}%`;
|
|
8064
8368
|
})
|
|
8065
8369
|
.join(", ");
|
|
8066
|
-
|
|
8370
|
+
const interpolation = gradientInterpolationClause(this.#gradient);
|
|
8371
|
+
return `linear-gradient(${this.#gradient.angle}deg${interpolation ? ` ${interpolation}` : ""}, ${stops})`;
|
|
8067
8372
|
}
|
|
8068
8373
|
case "image":
|
|
8069
8374
|
return this.#image.url ? `url(${this.#image.url})` : "#D9D9D9";
|
|
@@ -8088,7 +8393,8 @@ class FigInputFill extends HTMLElement {
|
|
|
8088
8393
|
}
|
|
8089
8394
|
|
|
8090
8395
|
#syncDisabled() {
|
|
8091
|
-
const disabled =
|
|
8396
|
+
const disabled =
|
|
8397
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
8092
8398
|
this.setAttribute("aria-disabled", disabled ? "true" : "false");
|
|
8093
8399
|
for (const child of [
|
|
8094
8400
|
this.#fillPicker,
|
|
@@ -8124,7 +8430,8 @@ class FigInputFill extends HTMLElement {
|
|
|
8124
8430
|
}
|
|
8125
8431
|
|
|
8126
8432
|
#render() {
|
|
8127
|
-
const disabled =
|
|
8433
|
+
const disabled =
|
|
8434
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
8128
8435
|
const fillPickerValue = JSON.stringify(this.value);
|
|
8129
8436
|
const showAlpha = this.getAttribute("alpha") !== "false";
|
|
8130
8437
|
|
|
@@ -8152,7 +8459,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8152
8459
|
type="text"
|
|
8153
8460
|
class="fig-input-fill-hex"
|
|
8154
8461
|
placeholder="000000"
|
|
8155
|
-
value="${this.#solid.color.slice(1).toUpperCase()}"
|
|
8462
|
+
value="${figEscapeAttribute(this.#solid.color.slice(1).toUpperCase())}"
|
|
8156
8463
|
${disabled ? "disabled" : ""}>
|
|
8157
8464
|
</fig-input-text>
|
|
8158
8465
|
${opacityHtml(Math.round(this.#solid.alpha * 100))}`;
|
|
@@ -8163,8 +8470,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8163
8470
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8164
8471
|
this.#gradient.type.slice(1);
|
|
8165
8472
|
controlsHtml = `
|
|
8166
|
-
<label class="fig-input-fill-label">${gradientLabel}</label
|
|
8167
|
-
${opacityHtml(100)}`;
|
|
8473
|
+
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>`;
|
|
8168
8474
|
break;
|
|
8169
8475
|
}
|
|
8170
8476
|
|
|
@@ -8190,10 +8496,10 @@ class FigInputFill extends HTMLElement {
|
|
|
8190
8496
|
const fpAttrs = this.#buildFillPickerAttrs();
|
|
8191
8497
|
this.innerHTML = `
|
|
8192
8498
|
<div class="input-combo">
|
|
8193
|
-
<fig-fill-picker ${fpAttrs} value='${fillPickerValue}' ${
|
|
8499
|
+
<fig-fill-picker ${fpAttrs} value='${figEscapeAttribute(fillPickerValue)}' ${
|
|
8194
8500
|
disabled ? "disabled" : ""
|
|
8195
8501
|
}>
|
|
8196
|
-
<fig-swatch background="${this.#fillPickerSwatchBackground()}" alpha="${this.#fillPickerSwatchAlpha()}"${disabled ? " disabled" : ""}></fig-swatch>
|
|
8502
|
+
<fig-swatch background="${figEscapeAttribute(this.#fillPickerSwatchBackground())}" alpha="${this.#fillPickerSwatchAlpha()}"${disabled ? " disabled" : ""}></fig-swatch>
|
|
8197
8503
|
</fig-fill-picker>
|
|
8198
8504
|
${controlsHtml}
|
|
8199
8505
|
</div>`;
|
|
@@ -8223,7 +8529,10 @@ class FigInputFill extends HTMLElement {
|
|
|
8223
8529
|
if (!anchor || anchor === "self") {
|
|
8224
8530
|
this.#fillPicker.anchorElement = this;
|
|
8225
8531
|
} else {
|
|
8226
|
-
|
|
8532
|
+
let el = null;
|
|
8533
|
+
try {
|
|
8534
|
+
el = document.querySelector(anchor);
|
|
8535
|
+
} catch {}
|
|
8227
8536
|
if (el) this.#fillPicker.anchorElement = el;
|
|
8228
8537
|
}
|
|
8229
8538
|
|
|
@@ -8380,7 +8689,9 @@ class FigInputFill extends HTMLElement {
|
|
|
8380
8689
|
|
|
8381
8690
|
#updateControlsForType() {
|
|
8382
8691
|
// Update only the controls (not the fill picker) when type changes
|
|
8383
|
-
const disabled =
|
|
8692
|
+
const disabled =
|
|
8693
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
8694
|
+
const showAlpha = this.getAttribute("alpha") !== "false";
|
|
8384
8695
|
const combo = this.querySelector(".input-combo");
|
|
8385
8696
|
if (!combo) return;
|
|
8386
8697
|
|
|
@@ -8403,10 +8714,10 @@ class FigInputFill extends HTMLElement {
|
|
|
8403
8714
|
type="text"
|
|
8404
8715
|
class="fig-input-fill-hex"
|
|
8405
8716
|
placeholder="000000"
|
|
8406
|
-
value="${this.#solid.color.slice(1).toUpperCase()}"
|
|
8717
|
+
value="${figEscapeAttribute(this.#solid.color.slice(1).toUpperCase())}"
|
|
8407
8718
|
${disabled ? "disabled" : ""}>
|
|
8408
8719
|
</fig-input-text>
|
|
8409
|
-
|
|
8720
|
+
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
8410
8721
|
<fig-input-number
|
|
8411
8722
|
class="fig-input-fill-opacity"
|
|
8412
8723
|
placeholder="##"
|
|
@@ -8416,31 +8727,20 @@ class FigInputFill extends HTMLElement {
|
|
|
8416
8727
|
units="%"
|
|
8417
8728
|
${disabled ? "disabled" : ""}>
|
|
8418
8729
|
</fig-input-number>
|
|
8419
|
-
</fig-tooltip
|
|
8730
|
+
</fig-tooltip>` : ""}`;
|
|
8420
8731
|
break;
|
|
8421
8732
|
case "gradient": {
|
|
8422
8733
|
const gradientLabel =
|
|
8423
8734
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8424
8735
|
this.#gradient.type.slice(1);
|
|
8425
8736
|
controlsHtml = `
|
|
8426
|
-
<label class="fig-input-fill-label">${gradientLabel}</label
|
|
8427
|
-
<fig-tooltip text="Opacity">
|
|
8428
|
-
<fig-input-number
|
|
8429
|
-
class="fig-input-fill-opacity"
|
|
8430
|
-
placeholder="##"
|
|
8431
|
-
min="0"
|
|
8432
|
-
max="100"
|
|
8433
|
-
value="100"
|
|
8434
|
-
units="%"
|
|
8435
|
-
${disabled ? "disabled" : ""}>
|
|
8436
|
-
</fig-input-number>
|
|
8437
|
-
</fig-tooltip>`;
|
|
8737
|
+
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>`;
|
|
8438
8738
|
break;
|
|
8439
8739
|
}
|
|
8440
8740
|
case "image":
|
|
8441
8741
|
controlsHtml = `
|
|
8442
8742
|
<label class="fig-input-fill-label">Image</label>
|
|
8443
|
-
|
|
8743
|
+
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
8444
8744
|
<fig-input-number
|
|
8445
8745
|
class="fig-input-fill-opacity"
|
|
8446
8746
|
placeholder="##"
|
|
@@ -8450,12 +8750,12 @@ class FigInputFill extends HTMLElement {
|
|
|
8450
8750
|
units="%"
|
|
8451
8751
|
${disabled ? "disabled" : ""}>
|
|
8452
8752
|
</fig-input-number>
|
|
8453
|
-
</fig-tooltip
|
|
8753
|
+
</fig-tooltip>` : ""}`;
|
|
8454
8754
|
break;
|
|
8455
8755
|
case "video":
|
|
8456
8756
|
controlsHtml = `
|
|
8457
8757
|
<label class="fig-input-fill-label">Video</label>
|
|
8458
|
-
|
|
8758
|
+
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
8459
8759
|
<fig-input-number
|
|
8460
8760
|
class="fig-input-fill-opacity"
|
|
8461
8761
|
placeholder="##"
|
|
@@ -8465,12 +8765,12 @@ class FigInputFill extends HTMLElement {
|
|
|
8465
8765
|
units="%"
|
|
8466
8766
|
${disabled ? "disabled" : ""}>
|
|
8467
8767
|
</fig-input-number>
|
|
8468
|
-
</fig-tooltip
|
|
8768
|
+
</fig-tooltip>` : ""}`;
|
|
8469
8769
|
break;
|
|
8470
8770
|
case "webcam":
|
|
8471
8771
|
controlsHtml = `
|
|
8472
8772
|
<label class="fig-input-fill-label">Webcam</label>
|
|
8473
|
-
|
|
8773
|
+
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
8474
8774
|
<fig-input-number
|
|
8475
8775
|
class="fig-input-fill-opacity"
|
|
8476
8776
|
placeholder="##"
|
|
@@ -8480,7 +8780,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8480
8780
|
units="%"
|
|
8481
8781
|
${disabled ? "disabled" : ""}>
|
|
8482
8782
|
</fig-input-number>
|
|
8483
|
-
</fig-tooltip
|
|
8783
|
+
</fig-tooltip>` : ""}`;
|
|
8484
8784
|
break;
|
|
8485
8785
|
}
|
|
8486
8786
|
|
|
@@ -8657,6 +8957,9 @@ class FigInputFill extends HTMLElement {
|
|
|
8657
8957
|
}
|
|
8658
8958
|
}
|
|
8659
8959
|
break;
|
|
8960
|
+
case "alpha":
|
|
8961
|
+
if (this.isConnected) this.#render();
|
|
8962
|
+
break;
|
|
8660
8963
|
case "aria-label":
|
|
8661
8964
|
case "aria-describedby":
|
|
8662
8965
|
case "aria-invalid":
|
|
@@ -9187,7 +9490,8 @@ class FigInputGradient extends HTMLElement {
|
|
|
9187
9490
|
}
|
|
9188
9491
|
|
|
9189
9492
|
#syncFocusTarget() {
|
|
9190
|
-
const disabled =
|
|
9493
|
+
const disabled =
|
|
9494
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9191
9495
|
if (disabled) {
|
|
9192
9496
|
this.setAttribute("tabindex", "-1");
|
|
9193
9497
|
return;
|
|
@@ -9196,10 +9500,21 @@ class FigInputGradient extends HTMLElement {
|
|
|
9196
9500
|
}
|
|
9197
9501
|
|
|
9198
9502
|
#normalizeGradient(gradient) {
|
|
9503
|
+
const normalized = normalizeGradientConfig(gradient);
|
|
9199
9504
|
return {
|
|
9200
|
-
...
|
|
9201
|
-
type: "linear",
|
|
9202
|
-
|
|
9505
|
+
...normalized,
|
|
9506
|
+
type: ["linear", "radial", "angular"].includes(normalized.type)
|
|
9507
|
+
? normalized.type
|
|
9508
|
+
: "linear",
|
|
9509
|
+
angle: Number.isFinite(Number(normalized.angle))
|
|
9510
|
+
? Number(normalized.angle)
|
|
9511
|
+
: 90,
|
|
9512
|
+
centerX: Number.isFinite(Number(normalized.centerX))
|
|
9513
|
+
? Number(normalized.centerX)
|
|
9514
|
+
: 50,
|
|
9515
|
+
centerY: Number.isFinite(Number(normalized.centerY))
|
|
9516
|
+
? Number(normalized.centerY)
|
|
9517
|
+
: 50,
|
|
9203
9518
|
};
|
|
9204
9519
|
}
|
|
9205
9520
|
|
|
@@ -9232,6 +9547,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9232
9547
|
|
|
9233
9548
|
#onKeyDown = (e) => {
|
|
9234
9549
|
const active = document.activeElement;
|
|
9550
|
+
if (!(active instanceof Node) || !this.contains(active)) return;
|
|
9235
9551
|
const isTyping =
|
|
9236
9552
|
active &&
|
|
9237
9553
|
(active.tagName === "INPUT" ||
|
|
@@ -9313,7 +9629,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9313
9629
|
if (this.#editMode !== "picker") return;
|
|
9314
9630
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
9315
9631
|
if (e.altKey || e.ctrlKey || e.metaKey) return;
|
|
9316
|
-
if (this
|
|
9632
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
9317
9633
|
const picker = this.querySelector("fig-fill-picker");
|
|
9318
9634
|
if (!picker || typeof picker.open !== "function") return;
|
|
9319
9635
|
e.preventDefault();
|
|
@@ -9358,6 +9674,12 @@ class FigInputGradient extends HTMLElement {
|
|
|
9358
9674
|
.join(", ");
|
|
9359
9675
|
const interp = gradientInterpolationClause(gradient);
|
|
9360
9676
|
const interpolation = interp ? ` ${interp}` : "";
|
|
9677
|
+
if (gradient.type === "radial") {
|
|
9678
|
+
return `radial-gradient(circle at ${gradient.centerX}% ${gradient.centerY}%${interpolation}, ${stops})`;
|
|
9679
|
+
}
|
|
9680
|
+
if (gradient.type === "angular") {
|
|
9681
|
+
return `conic-gradient(from ${gradient.angle}deg${interpolation}, ${stops})`;
|
|
9682
|
+
}
|
|
9361
9683
|
return `linear-gradient(${gradient.angle}deg${interpolation}, ${stops})`;
|
|
9362
9684
|
}
|
|
9363
9685
|
|
|
@@ -9381,7 +9703,8 @@ class FigInputGradient extends HTMLElement {
|
|
|
9381
9703
|
}
|
|
9382
9704
|
|
|
9383
9705
|
#buildStopHandles() {
|
|
9384
|
-
const disabled =
|
|
9706
|
+
const disabled =
|
|
9707
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9385
9708
|
const tipAttr = this.#stopHandleMode === "tip" ? ' tip="color"' : "";
|
|
9386
9709
|
return this.#gradient.stops
|
|
9387
9710
|
.map(
|
|
@@ -9395,7 +9718,9 @@ class FigInputGradient extends HTMLElement {
|
|
|
9395
9718
|
#addedOnPointerDown = false;
|
|
9396
9719
|
|
|
9397
9720
|
#render() {
|
|
9398
|
-
|
|
9721
|
+
this.#colorObserver?.disconnect();
|
|
9722
|
+
this.#colorObserver = null;
|
|
9723
|
+
const disabled = figBooleanAttribute(this, "disabled");
|
|
9399
9724
|
const mode = this.#editMode;
|
|
9400
9725
|
|
|
9401
9726
|
if (mode === "picker" && hasFigFillPicker()) {
|
|
@@ -9463,7 +9788,11 @@ class FigInputGradient extends HTMLElement {
|
|
|
9463
9788
|
}
|
|
9464
9789
|
|
|
9465
9790
|
#setupGhostHandle() {
|
|
9466
|
-
if (
|
|
9791
|
+
if (
|
|
9792
|
+
!this.#track ||
|
|
9793
|
+
(this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false")
|
|
9794
|
+
)
|
|
9795
|
+
return;
|
|
9467
9796
|
|
|
9468
9797
|
const ghost = document.createElement("fig-handle");
|
|
9469
9798
|
ghost.classList.add("fig-input-gradient-ghost");
|
|
@@ -9731,7 +10060,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9731
10060
|
if (!this.#track) return;
|
|
9732
10061
|
|
|
9733
10062
|
this.#track.addEventListener("pointerdown", (e) => {
|
|
9734
|
-
if (this
|
|
10063
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
9735
10064
|
if (e.target.closest("fig-handle:not(.fig-input-gradient-ghost)")) return;
|
|
9736
10065
|
if (e.button !== 0) return;
|
|
9737
10066
|
e.preventDefault();
|
|
@@ -9948,7 +10277,11 @@ class FigInputGradient extends HTMLElement {
|
|
|
9948
10277
|
}
|
|
9949
10278
|
|
|
9950
10279
|
focus(options) {
|
|
9951
|
-
if (
|
|
10280
|
+
if (
|
|
10281
|
+
this.hasAttribute("disabled") &&
|
|
10282
|
+
this.getAttribute("disabled") !== "false"
|
|
10283
|
+
)
|
|
10284
|
+
return;
|
|
9952
10285
|
if (this.#isEditable) {
|
|
9953
10286
|
const firstHandle = this.#firstStopHandle();
|
|
9954
10287
|
if (firstHandle) {
|
|
@@ -9988,7 +10321,8 @@ class FigInputGradient extends HTMLElement {
|
|
|
9988
10321
|
}
|
|
9989
10322
|
|
|
9990
10323
|
#syncDisabled() {
|
|
9991
|
-
const disabled =
|
|
10324
|
+
const disabled =
|
|
10325
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9992
10326
|
this.#syncFocusTarget();
|
|
9993
10327
|
if (this.#swatch) {
|
|
9994
10328
|
if (disabled) this.#swatch.setAttribute("disabled", "");
|
|
@@ -10016,16 +10350,24 @@ customElements.define("fig-input-gradient", FigInputGradient);
|
|
|
10016
10350
|
class FigCheckbox extends HTMLElement {
|
|
10017
10351
|
#labelElement = null;
|
|
10018
10352
|
#boundHandleInput;
|
|
10353
|
+
#boundHandleChange;
|
|
10354
|
+
#boundHandleHostClick;
|
|
10355
|
+
#labelIsGenerated = false;
|
|
10019
10356
|
|
|
10020
10357
|
constructor() {
|
|
10021
10358
|
super();
|
|
10022
10359
|
this.input = document.createElement("input");
|
|
10023
|
-
this.name = this.getAttribute("name") || "
|
|
10360
|
+
this.name = this.getAttribute("name") || "";
|
|
10024
10361
|
this.input.value = this.getAttribute("value") || "";
|
|
10025
10362
|
this.input.setAttribute("id", figUniqueId());
|
|
10026
|
-
this.input.setAttribute("name", this.name);
|
|
10363
|
+
if (this.name) this.input.setAttribute("name", this.name);
|
|
10027
10364
|
this.input.setAttribute("type", "checkbox");
|
|
10028
10365
|
this.#boundHandleInput = this.handleInput.bind(this);
|
|
10366
|
+
this.#boundHandleChange = this.handleChange.bind(this);
|
|
10367
|
+
this.#boundHandleHostClick = (event) => {
|
|
10368
|
+
if (event.target !== this || this.input.disabled) return;
|
|
10369
|
+
this.input.click();
|
|
10370
|
+
};
|
|
10029
10371
|
}
|
|
10030
10372
|
connectedCallback() {
|
|
10031
10373
|
// Reuse cloned internals when this element is duplicated via option.cloneNode(true).
|
|
@@ -10036,13 +10378,18 @@ class FigCheckbox extends HTMLElement {
|
|
|
10036
10378
|
this.append(this.input);
|
|
10037
10379
|
}
|
|
10038
10380
|
|
|
10039
|
-
this.input.removeEventListener("
|
|
10040
|
-
this.input.addEventListener("
|
|
10381
|
+
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
10382
|
+
this.input.addEventListener("input", this.#boundHandleInput);
|
|
10383
|
+
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
10384
|
+
this.input.addEventListener("change", this.#boundHandleChange);
|
|
10385
|
+
this.removeEventListener("click", this.#boundHandleHostClick);
|
|
10386
|
+
this.addEventListener("click", this.#boundHandleHostClick);
|
|
10041
10387
|
this.#syncInputState();
|
|
10042
10388
|
|
|
10043
10389
|
const existingLabel = this.querySelector(":scope > label");
|
|
10044
10390
|
if (existingLabel) {
|
|
10045
10391
|
this.#labelElement = existingLabel;
|
|
10392
|
+
this.#labelIsGenerated = false;
|
|
10046
10393
|
this.#labelElement.setAttribute("for", this.input.id);
|
|
10047
10394
|
}
|
|
10048
10395
|
|
|
@@ -10052,6 +10399,21 @@ class FigCheckbox extends HTMLElement {
|
|
|
10052
10399
|
this.#labelElement.innerText = this.getAttribute("label");
|
|
10053
10400
|
}
|
|
10054
10401
|
|
|
10402
|
+
if (!this.hasAttribute("label")) {
|
|
10403
|
+
const text = Array.from(this.childNodes)
|
|
10404
|
+
.filter((node) => node.nodeType === Node.TEXT_NODE)
|
|
10405
|
+
.map((node) => node.textContent || "")
|
|
10406
|
+
.join(" ")
|
|
10407
|
+
.trim();
|
|
10408
|
+
if (
|
|
10409
|
+
text &&
|
|
10410
|
+
!this.input.hasAttribute("aria-label") &&
|
|
10411
|
+
!this.input.hasAttribute("aria-labelledby")
|
|
10412
|
+
) {
|
|
10413
|
+
this.input.setAttribute("aria-label", text);
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
|
|
10055
10417
|
this.render();
|
|
10056
10418
|
}
|
|
10057
10419
|
static get observedAttributes() {
|
|
@@ -10061,6 +10423,7 @@ class FigCheckbox extends HTMLElement {
|
|
|
10061
10423
|
#createLabel() {
|
|
10062
10424
|
if (!this.#labelElement) {
|
|
10063
10425
|
this.#labelElement = document.createElement("label");
|
|
10426
|
+
this.#labelIsGenerated = true;
|
|
10064
10427
|
this.#labelElement.setAttribute("for", this.input.id);
|
|
10065
10428
|
}
|
|
10066
10429
|
// Add to DOM if not already there and input is in the DOM
|
|
@@ -10095,7 +10458,9 @@ class FigCheckbox extends HTMLElement {
|
|
|
10095
10458
|
this.input.indeterminate = indeterminate;
|
|
10096
10459
|
this.input.disabled = disabled;
|
|
10097
10460
|
this.input.value = this.getAttribute("value") || "";
|
|
10098
|
-
|
|
10461
|
+
const name = this.getAttribute("name") || this.name;
|
|
10462
|
+
if (name) this.input.setAttribute("name", name);
|
|
10463
|
+
else this.input.removeAttribute("name");
|
|
10099
10464
|
if (indeterminate) {
|
|
10100
10465
|
this.input.setAttribute("indeterminate", "true");
|
|
10101
10466
|
} else {
|
|
@@ -10131,8 +10496,9 @@ class FigCheckbox extends HTMLElement {
|
|
|
10131
10496
|
}
|
|
10132
10497
|
|
|
10133
10498
|
disconnectedCallback() {
|
|
10134
|
-
this.input.removeEventListener("
|
|
10135
|
-
this.input.
|
|
10499
|
+
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
10500
|
+
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
10501
|
+
this.removeEventListener("click", this.#boundHandleHostClick);
|
|
10136
10502
|
}
|
|
10137
10503
|
|
|
10138
10504
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -10141,9 +10507,10 @@ class FigCheckbox extends HTMLElement {
|
|
|
10141
10507
|
if (newValue) {
|
|
10142
10508
|
this.#createLabel();
|
|
10143
10509
|
this.#labelElement.innerText = newValue;
|
|
10144
|
-
} else if (this.#labelElement) {
|
|
10510
|
+
} else if (this.#labelElement && this.#labelIsGenerated) {
|
|
10145
10511
|
this.#labelElement.remove();
|
|
10146
10512
|
this.#labelElement = null;
|
|
10513
|
+
this.#labelIsGenerated = false;
|
|
10147
10514
|
}
|
|
10148
10515
|
break;
|
|
10149
10516
|
case "checked":
|
|
@@ -10159,7 +10526,8 @@ class FigCheckbox extends HTMLElement {
|
|
|
10159
10526
|
this.#syncInputState();
|
|
10160
10527
|
break;
|
|
10161
10528
|
case "name":
|
|
10162
|
-
this.input.setAttribute("name", newValue
|
|
10529
|
+
if (newValue) this.input.setAttribute("name", newValue);
|
|
10530
|
+
else this.input.removeAttribute("name");
|
|
10163
10531
|
break;
|
|
10164
10532
|
case "value":
|
|
10165
10533
|
this.input.value = newValue || "";
|
|
@@ -10185,7 +10553,6 @@ class FigCheckbox extends HTMLElement {
|
|
|
10185
10553
|
} else {
|
|
10186
10554
|
this.removeAttribute("checked");
|
|
10187
10555
|
}
|
|
10188
|
-
// Emit both input and change events
|
|
10189
10556
|
this.dispatchEvent(
|
|
10190
10557
|
new CustomEvent("input", {
|
|
10191
10558
|
bubbles: true,
|
|
@@ -10193,6 +10560,11 @@ class FigCheckbox extends HTMLElement {
|
|
|
10193
10560
|
detail: { checked: this.input.checked, value: this.input.value },
|
|
10194
10561
|
}),
|
|
10195
10562
|
);
|
|
10563
|
+
}
|
|
10564
|
+
|
|
10565
|
+
handleChange(e) {
|
|
10566
|
+
e.stopPropagation();
|
|
10567
|
+
this.#syncAriaChecked();
|
|
10196
10568
|
this.dispatchEvent(
|
|
10197
10569
|
new CustomEvent("change", {
|
|
10198
10570
|
bubbles: true,
|
|
@@ -10217,7 +10589,9 @@ class FigRadio extends FigCheckbox {
|
|
|
10217
10589
|
constructor() {
|
|
10218
10590
|
super();
|
|
10219
10591
|
this.input.setAttribute("type", "radio");
|
|
10220
|
-
|
|
10592
|
+
const name = this.getAttribute("name");
|
|
10593
|
+
if (name) this.input.setAttribute("name", name);
|
|
10594
|
+
else this.input.removeAttribute("name");
|
|
10221
10595
|
}
|
|
10222
10596
|
}
|
|
10223
10597
|
customElements.define("fig-radio", FigRadio);
|
|
@@ -10324,9 +10698,7 @@ class FigComboInput extends HTMLElement {
|
|
|
10324
10698
|
this.#setupListeners();
|
|
10325
10699
|
}
|
|
10326
10700
|
|
|
10327
|
-
|
|
10328
|
-
this.#applyDisabled(true);
|
|
10329
|
-
}
|
|
10701
|
+
this.#applyDisabled(figBooleanAttribute(this, "disabled"));
|
|
10330
10702
|
}
|
|
10331
10703
|
|
|
10332
10704
|
disconnectedCallback() {
|
|
@@ -10338,15 +10710,17 @@ class FigComboInput extends HTMLElement {
|
|
|
10338
10710
|
const placeholder = this.getAttribute("placeholder") || "";
|
|
10339
10711
|
const currentValue = this.value;
|
|
10340
10712
|
const experimental = this.getAttribute("experimental");
|
|
10341
|
-
const expAttr = experimental
|
|
10713
|
+
const expAttr = experimental
|
|
10714
|
+
? ` experimental="${figEscapeAttribute(experimental)}"`
|
|
10715
|
+
: "";
|
|
10342
10716
|
const dropdownLabel = this.#dropdownLabel();
|
|
10343
10717
|
|
|
10344
10718
|
const dropdownHTML = this.#usesCustomDropdown
|
|
10345
10719
|
? ""
|
|
10346
|
-
: `<fig-dropdown type="dropdown" label="${dropdownLabel}"${expAttr}>${options.map((o) => `<option>${o.trim()}</option>`).join("")}</fig-dropdown>`;
|
|
10720
|
+
: `<fig-dropdown type="dropdown" label="${figEscapeAttribute(dropdownLabel)}"${expAttr}>${options.map((o) => `<option>${figEscapeAttribute(o.trim())}</option>`).join("")}</fig-dropdown>`;
|
|
10347
10721
|
|
|
10348
10722
|
this.innerHTML = `<div class="input-combo">
|
|
10349
|
-
<fig-input-text placeholder="${placeholder}" value="${currentValue}"></fig-input-text>
|
|
10723
|
+
<fig-input-text placeholder="${figEscapeAttribute(placeholder)}" value="${figEscapeAttribute(currentValue)}"></fig-input-text>
|
|
10350
10724
|
<fig-button type="select" variant="input" icon>
|
|
10351
10725
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10352
10726
|
<path d="M5.87868 7.12132L8 9.24264L10.1213 7.12132" stroke="currentColor" stroke-opacity="0.9" stroke-linecap="round"/>
|
|
@@ -10494,9 +10868,13 @@ class FigComboInput extends HTMLElement {
|
|
|
10494
10868
|
case "options":
|
|
10495
10869
|
if (this.#dropdown && !this.#usesCustomDropdown) {
|
|
10496
10870
|
const options = this.#getOptions();
|
|
10497
|
-
this.#dropdown.
|
|
10498
|
-
.map((
|
|
10499
|
-
|
|
10871
|
+
this.#dropdown.replaceChildren(
|
|
10872
|
+
...options.map((value) => {
|
|
10873
|
+
const option = document.createElement("option");
|
|
10874
|
+
option.textContent = value;
|
|
10875
|
+
return option;
|
|
10876
|
+
}),
|
|
10877
|
+
);
|
|
10500
10878
|
}
|
|
10501
10879
|
break;
|
|
10502
10880
|
case "placeholder":
|
|
@@ -10886,6 +11264,7 @@ class FigMedia extends HTMLElement {
|
|
|
10886
11264
|
this.querySelectorAll("fig-swatch[data-generated]").forEach((el) => el.remove());
|
|
10887
11265
|
this.#ensurePreviewElement();
|
|
10888
11266
|
this.#ensureMediaElement();
|
|
11267
|
+
this.#addMediaElementListeners();
|
|
10889
11268
|
this.#syncGeneratedMediaElement();
|
|
10890
11269
|
this.#syncMediaAccessibility();
|
|
10891
11270
|
this.#syncCaption();
|
|
@@ -10894,6 +11273,9 @@ class FigMedia extends HTMLElement {
|
|
|
10894
11273
|
if (isUpload && !this.querySelector("fig-input-file[data-generated]")) {
|
|
10895
11274
|
this.#createFileInput();
|
|
10896
11275
|
}
|
|
11276
|
+
this.#fileInput = this.querySelector("fig-input-file[data-generated]");
|
|
11277
|
+
this.#fileInput?.removeEventListener("change", this.#boundHandleFileInput);
|
|
11278
|
+
this.#fileInput?.addEventListener("change", this.#boundHandleFileInput);
|
|
10897
11279
|
}
|
|
10898
11280
|
|
|
10899
11281
|
disconnectedCallback() {
|
|
@@ -10916,6 +11298,14 @@ class FigMedia extends HTMLElement {
|
|
|
10916
11298
|
}
|
|
10917
11299
|
}
|
|
10918
11300
|
|
|
11301
|
+
#addMediaElementListeners() {
|
|
11302
|
+
this.#removeMediaElementListeners();
|
|
11303
|
+
if (this.#mediaEl?.tagName !== "VIDEO") return;
|
|
11304
|
+
this.#mediaEl.addEventListener("play", this.#boundHandleMediaPlay);
|
|
11305
|
+
this.#mediaEl.addEventListener("pause", this.#boundHandleMediaPause);
|
|
11306
|
+
this.#mediaEl.addEventListener("ended", this.#boundHandleMediaEnded);
|
|
11307
|
+
}
|
|
11308
|
+
|
|
10919
11309
|
#ensurePreviewElement() {
|
|
10920
11310
|
if (this.#previewEl?.isConnected) return;
|
|
10921
11311
|
const existing = this.querySelector(":scope > fig-preview");
|
|
@@ -10971,9 +11361,7 @@ class FigMedia extends HTMLElement {
|
|
|
10971
11361
|
video.preload = "auto";
|
|
10972
11362
|
this.#previewEl.append(video);
|
|
10973
11363
|
this.#mediaEl = video;
|
|
10974
|
-
this.#
|
|
10975
|
-
this.#mediaEl.addEventListener("pause", this.#boundHandleMediaPause);
|
|
10976
|
-
this.#mediaEl.addEventListener("ended", this.#boundHandleMediaEnded);
|
|
11364
|
+
this.#addMediaElementListeners();
|
|
10977
11365
|
const seekToFirstFrame = () => {
|
|
10978
11366
|
if (this.#mediaEl?.autoplay) return;
|
|
10979
11367
|
try {
|
|
@@ -11765,6 +12153,11 @@ class FigInputFile extends HTMLElement {
|
|
|
11765
12153
|
};
|
|
11766
12154
|
|
|
11767
12155
|
#onDragOver = (e) => {
|
|
12156
|
+
if (
|
|
12157
|
+
this.hasAttribute("disabled") &&
|
|
12158
|
+
this.getAttribute("disabled") !== "false"
|
|
12159
|
+
)
|
|
12160
|
+
return;
|
|
11768
12161
|
e.preventDefault();
|
|
11769
12162
|
if (!this.hasAttribute("dragover")) {
|
|
11770
12163
|
this.setAttribute("dragover", "");
|
|
@@ -11811,7 +12204,10 @@ class FigInputFile extends HTMLElement {
|
|
|
11811
12204
|
);
|
|
11812
12205
|
});
|
|
11813
12206
|
}
|
|
11814
|
-
if (
|
|
12207
|
+
if (
|
|
12208
|
+
!this.hasAttribute("multiple") ||
|
|
12209
|
+
this.getAttribute("multiple") === "false"
|
|
12210
|
+
) {
|
|
11815
12211
|
dropped = dropped.slice(0, 1);
|
|
11816
12212
|
}
|
|
11817
12213
|
if (dropped.length === 0) return;
|
|
@@ -11833,7 +12229,8 @@ class FigInputFile extends HTMLElement {
|
|
|
11833
12229
|
const disabled =
|
|
11834
12230
|
this.hasAttribute("disabled") &&
|
|
11835
12231
|
this.getAttribute("disabled") !== "false";
|
|
11836
|
-
const multiple =
|
|
12232
|
+
const multiple =
|
|
12233
|
+
this.hasAttribute("multiple") && this.getAttribute("multiple") !== "false";
|
|
11837
12234
|
const variant = this.getAttribute("variant") || "input";
|
|
11838
12235
|
const hasFile =
|
|
11839
12236
|
(this.#files && this.#files.length > 0) ||
|
|
@@ -11867,6 +12264,7 @@ class FigInputFile extends HTMLElement {
|
|
|
11867
12264
|
this.#fileInput = document.createElement("input");
|
|
11868
12265
|
this.#fileInput.type = "file";
|
|
11869
12266
|
this.#fileInput.title = "";
|
|
12267
|
+
this.#fileInput.disabled = disabled;
|
|
11870
12268
|
if (accepts) this.#fileInput.setAttribute("accept", accepts);
|
|
11871
12269
|
if (multiple) this.#fileInput.setAttribute("multiple", "");
|
|
11872
12270
|
this.#fileInput.addEventListener("change", this.#onFileChange);
|
|
@@ -11915,6 +12313,7 @@ class FigInputFile extends HTMLElement {
|
|
|
11915
12313
|
this.#fileInput = document.createElement("input");
|
|
11916
12314
|
this.#fileInput.type = "file";
|
|
11917
12315
|
this.#fileInput.title = "";
|
|
12316
|
+
this.#fileInput.disabled = disabled;
|
|
11918
12317
|
if (accepts) this.#fileInput.setAttribute("accept", accepts);
|
|
11919
12318
|
if (multiple) this.#fileInput.setAttribute("multiple", "");
|
|
11920
12319
|
this.#fileInput.addEventListener("change", this.#onFileChange);
|
|
@@ -14691,7 +15090,10 @@ const FIG_ICON_TOKENS = {
|
|
|
14691
15090
|
chevron: "--icon-16-chevron",
|
|
14692
15091
|
checkmark: "--icon-16-checkmark",
|
|
14693
15092
|
reset: "--icon-16-reset",
|
|
14694
|
-
"arrow-left":
|
|
15093
|
+
"arrow-left": {
|
|
15094
|
+
medium: "--icon-24-arrow-left",
|
|
15095
|
+
small: "--icon-16-arrow-left",
|
|
15096
|
+
},
|
|
14695
15097
|
steppers: "--icon-24-steppers",
|
|
14696
15098
|
eyedropper: "--icon-24-eyedropper",
|
|
14697
15099
|
add: "--icon-24-add",
|
|
@@ -14704,6 +15106,9 @@ const FIG_ICON_TOKENS = {
|
|
|
14704
15106
|
play: "--icon-24-play",
|
|
14705
15107
|
pause: "--icon-24-pause",
|
|
14706
15108
|
search: "--icon-24-search",
|
|
15109
|
+
edit: { medium: "--icon-24-edit", small: "--icon-16-edit" },
|
|
15110
|
+
settings: { medium: "--icon-24-settings", small: "--icon-16-settings" },
|
|
15111
|
+
more: { medium: "--icon-24-more", small: "--icon-16-more" },
|
|
14707
15112
|
visible: { medium: "--icon-24-visible", small: "--icon-16-visible" },
|
|
14708
15113
|
hidden: { medium: "--icon-24-hidden", small: "--icon-16-hidden" },
|
|
14709
15114
|
};
|
|
@@ -14866,7 +15271,7 @@ class FigColorTip extends HTMLElement {
|
|
|
14866
15271
|
if (mode === "add" || mode === "remove") {
|
|
14867
15272
|
const iconName = mode === "add" ? "add" : "minus";
|
|
14868
15273
|
const label = this.getAttribute("aria-label") || (mode === "add" ? "Add color stop" : "Remove color stop");
|
|
14869
|
-
this.innerHTML = `<fig-button icon variant="ghost" aria-label="${label}"><fig-icon name="${iconName}"></fig-icon></fig-button>`;
|
|
15274
|
+
this.innerHTML = `<fig-button icon variant="ghost" aria-label="${figEscapeAttribute(label)}"><fig-icon name="${iconName}"></fig-icon></fig-button>`;
|
|
14870
15275
|
this.#fillPicker = null;
|
|
14871
15276
|
this.#swatch = null;
|
|
14872
15277
|
this.addEventListener("click", this.#handleControlClick);
|
|
@@ -15011,7 +15416,7 @@ class FigColorTip extends HTMLElement {
|
|
|
15011
15416
|
} else {
|
|
15012
15417
|
this.#fillPicker.setAttribute("alpha", "false");
|
|
15013
15418
|
}
|
|
15014
|
-
if (this
|
|
15419
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
15015
15420
|
this.#fillPicker.setAttribute("disabled", "");
|
|
15016
15421
|
} else {
|
|
15017
15422
|
this.#fillPicker.removeAttribute("disabled");
|
|
@@ -15026,7 +15431,7 @@ class FigColorTip extends HTMLElement {
|
|
|
15026
15431
|
} else {
|
|
15027
15432
|
this.#swatch.removeAttribute("alpha");
|
|
15028
15433
|
}
|
|
15029
|
-
if (this
|
|
15434
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
15030
15435
|
this.#swatch.setAttribute("disabled", "");
|
|
15031
15436
|
} else {
|
|
15032
15437
|
this.#swatch.removeAttribute("disabled");
|
|
@@ -15137,14 +15542,14 @@ class FigColorTip extends HTMLElement {
|
|
|
15137
15542
|
}
|
|
15138
15543
|
|
|
15139
15544
|
get selected() {
|
|
15140
|
-
return this
|
|
15545
|
+
return figBooleanAttribute(this, "selected");
|
|
15141
15546
|
}
|
|
15142
15547
|
set selected(value) {
|
|
15143
15548
|
this.toggleAttribute("selected", Boolean(value));
|
|
15144
15549
|
}
|
|
15145
15550
|
|
|
15146
15551
|
get disabled() {
|
|
15147
|
-
return this
|
|
15552
|
+
return figBooleanAttribute(this, "disabled");
|
|
15148
15553
|
}
|
|
15149
15554
|
set disabled(value) {
|
|
15150
15555
|
this.toggleAttribute("disabled", Boolean(value));
|
|
@@ -15171,9 +15576,9 @@ class FigChoice extends HTMLElement {
|
|
|
15171
15576
|
}
|
|
15172
15577
|
this.setAttribute(
|
|
15173
15578
|
"aria-selected",
|
|
15174
|
-
this
|
|
15579
|
+
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
15175
15580
|
);
|
|
15176
|
-
if (this
|
|
15581
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
15177
15582
|
this.setAttribute("aria-disabled", "true");
|
|
15178
15583
|
}
|
|
15179
15584
|
}
|
|
@@ -15182,7 +15587,7 @@ class FigChoice extends HTMLElement {
|
|
|
15182
15587
|
if (name === "selected") {
|
|
15183
15588
|
this.setAttribute(
|
|
15184
15589
|
"aria-selected",
|
|
15185
|
-
this
|
|
15590
|
+
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
15186
15591
|
);
|
|
15187
15592
|
}
|
|
15188
15593
|
if (name === "disabled") {
|
|
@@ -15277,7 +15682,7 @@ class FigChooser extends HTMLElement {
|
|
|
15277
15682
|
const choices = this.choices;
|
|
15278
15683
|
for (const choice of choices) {
|
|
15279
15684
|
const shouldSelect = choice === element;
|
|
15280
|
-
const isSelected = choice
|
|
15685
|
+
const isSelected = figBooleanAttribute(choice, "selected");
|
|
15281
15686
|
if (shouldSelect && !isSelected) {
|
|
15282
15687
|
choice.setAttribute("selected", "");
|
|
15283
15688
|
} else if (!shouldSelect && isSelected) {
|
|
@@ -15419,7 +15824,9 @@ class FigChooser extends HTMLElement {
|
|
|
15419
15824
|
const valueAttr = this.getAttribute("value");
|
|
15420
15825
|
if (valueAttr && this.#selectByValue(valueAttr)) return;
|
|
15421
15826
|
|
|
15422
|
-
const alreadySelected = choices.find((
|
|
15827
|
+
const alreadySelected = choices.find((choice) =>
|
|
15828
|
+
figBooleanAttribute(choice, "selected"),
|
|
15829
|
+
);
|
|
15423
15830
|
if (alreadySelected) {
|
|
15424
15831
|
this.selectedChoice = alreadySelected;
|
|
15425
15832
|
return;
|
|
@@ -16031,7 +16438,7 @@ class FigHandle extends HTMLElement {
|
|
|
16031
16438
|
}
|
|
16032
16439
|
|
|
16033
16440
|
#onHitAreaPointerDown(e) {
|
|
16034
|
-
if (this
|
|
16441
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16035
16442
|
if (e.target !== this.#hitAreaEl) return;
|
|
16036
16443
|
if (this.#hitAreaMode === "delegate") {
|
|
16037
16444
|
e.preventDefault();
|
|
@@ -16074,7 +16481,7 @@ class FigHandle extends HTMLElement {
|
|
|
16074
16481
|
}
|
|
16075
16482
|
|
|
16076
16483
|
select() {
|
|
16077
|
-
if (this
|
|
16484
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16078
16485
|
this.setAttribute("selected", "");
|
|
16079
16486
|
}
|
|
16080
16487
|
|
|
@@ -16113,12 +16520,12 @@ class FigHandle extends HTMLElement {
|
|
|
16113
16520
|
) {
|
|
16114
16521
|
if (this.#moveByKeyboard(e)) {
|
|
16115
16522
|
e.preventDefault();
|
|
16116
|
-
if (!this
|
|
16523
|
+
if (!figBooleanAttribute(this, "selected")) this.select();
|
|
16117
16524
|
}
|
|
16118
16525
|
return;
|
|
16119
16526
|
}
|
|
16120
16527
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
16121
|
-
if (e.target === this && !this
|
|
16528
|
+
if (e.target === this && !figBooleanAttribute(this, "selected")) {
|
|
16122
16529
|
e.preventDefault();
|
|
16123
16530
|
if (
|
|
16124
16531
|
this.getAttribute("type") === "color" &&
|
|
@@ -16131,7 +16538,7 @@ class FigHandle extends HTMLElement {
|
|
|
16131
16538
|
}
|
|
16132
16539
|
return;
|
|
16133
16540
|
}
|
|
16134
|
-
if (!this
|
|
16541
|
+
if (!figBooleanAttribute(this, "selected")) return;
|
|
16135
16542
|
if (this.getAttribute("type") !== "color") return;
|
|
16136
16543
|
if (!this.#canOpenColorPicker) return;
|
|
16137
16544
|
e.preventDefault();
|
|
@@ -16141,7 +16548,7 @@ class FigHandle extends HTMLElement {
|
|
|
16141
16548
|
};
|
|
16142
16549
|
|
|
16143
16550
|
#moveByKeyboard(event) {
|
|
16144
|
-
if (this
|
|
16551
|
+
if (figBooleanAttribute(this, "disabled")) return false;
|
|
16145
16552
|
const container = this.#getContainer();
|
|
16146
16553
|
if (!container) return false;
|
|
16147
16554
|
const rect = container.getBoundingClientRect();
|
|
@@ -16272,7 +16679,7 @@ class FigHandle extends HTMLElement {
|
|
|
16272
16679
|
}
|
|
16273
16680
|
|
|
16274
16681
|
#onPointerDown(e) {
|
|
16275
|
-
if (!this.#dragEnabled || this
|
|
16682
|
+
if (!this.#dragEnabled || figBooleanAttribute(this, "disabled")) return;
|
|
16276
16683
|
e.preventDefault();
|
|
16277
16684
|
const container = this.#getContainer();
|
|
16278
16685
|
if (!container) return;
|
|
@@ -16338,7 +16745,7 @@ class FigHandle extends HTMLElement {
|
|
|
16338
16745
|
this.#closeColorPickerForDrag();
|
|
16339
16746
|
this.classList.add("dragging");
|
|
16340
16747
|
this.style.cursor = "grabbing";
|
|
16341
|
-
if (!this
|
|
16748
|
+
if (!figBooleanAttribute(this, "selected")) this.select();
|
|
16342
16749
|
}
|
|
16343
16750
|
this.#didDrag = true;
|
|
16344
16751
|
clampAndApply(e.clientX, e.clientY, e.shiftKey);
|
|
@@ -16495,7 +16902,7 @@ class FigHandle extends HTMLElement {
|
|
|
16495
16902
|
}
|
|
16496
16903
|
|
|
16497
16904
|
#openDirectColorPicker() {
|
|
16498
|
-
if (this
|
|
16905
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16499
16906
|
const picker = this.#ensureDirectColorPicker();
|
|
16500
16907
|
if (!picker) {
|
|
16501
16908
|
this.#openNativeColorPicker();
|
|
@@ -16850,6 +17257,12 @@ class FigMenu extends HTMLElement {
|
|
|
16850
17257
|
}
|
|
16851
17258
|
if (this.#popup) {
|
|
16852
17259
|
this.#popup.removeEventListener("close", this.#boundPopupClose);
|
|
17260
|
+
const items = Array.from(
|
|
17261
|
+
this.#popup.querySelectorAll(
|
|
17262
|
+
":scope > fig-menu-item, :scope > fig-menu-separator",
|
|
17263
|
+
),
|
|
17264
|
+
);
|
|
17265
|
+
for (const item of items) this.insertBefore(item, this.#popup);
|
|
16853
17266
|
this.#popup.remove();
|
|
16854
17267
|
this.#popup = null;
|
|
16855
17268
|
}
|
|
@@ -17058,16 +17471,24 @@ class FigMenu extends HTMLElement {
|
|
|
17058
17471
|
#showAtAfterPointerRelease(x, y) {
|
|
17059
17472
|
let opened = false;
|
|
17060
17473
|
let fallbackTimer = 0;
|
|
17474
|
+
const cleanup = () => {
|
|
17475
|
+
window.clearTimeout(fallbackTimer);
|
|
17476
|
+
window.removeEventListener("pointerup", openMenu, true);
|
|
17477
|
+
window.removeEventListener("pointercancel", cancelMenu, true);
|
|
17478
|
+
};
|
|
17061
17479
|
const openMenu = () => {
|
|
17062
17480
|
if (opened) return;
|
|
17063
17481
|
opened = true;
|
|
17064
|
-
|
|
17065
|
-
window.removeEventListener("pointerup", openMenu, true);
|
|
17066
|
-
window.removeEventListener("pointercancel", openMenu, true);
|
|
17482
|
+
cleanup();
|
|
17067
17483
|
requestAnimationFrame(() => this.showAt(x, y));
|
|
17068
17484
|
};
|
|
17485
|
+
const cancelMenu = () => {
|
|
17486
|
+
if (opened) return;
|
|
17487
|
+
opened = true;
|
|
17488
|
+
cleanup();
|
|
17489
|
+
};
|
|
17069
17490
|
window.addEventListener("pointerup", openMenu, { once: true, capture: true });
|
|
17070
|
-
window.addEventListener("pointercancel",
|
|
17491
|
+
window.addEventListener("pointercancel", cancelMenu, {
|
|
17071
17492
|
once: true,
|
|
17072
17493
|
capture: true,
|
|
17073
17494
|
});
|