@rogieking/figui3 6.10.0 → 6.12.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 +64 -27
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +73 -67
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +21 -21
- package/dist/fig.css +1 -1
- package/dist/fig.js +36 -48
- package/fig-editor.css +12 -13
- package/fig-editor.js +343 -356
- package/fig-lab.css +79 -3
- package/fig-lab.js +459 -11
- package/fig.js +662 -230
- 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":
|
|
@@ -9132,6 +9435,7 @@ customElements.define("fig-input-palette", FigInputPalette);
|
|
|
9132
9435
|
/**
|
|
9133
9436
|
* A gradient-only fill input built on top of fig-fill-picker.
|
|
9134
9437
|
* @attr {string} value - JSON string with gradient fill data
|
|
9438
|
+
* @attr {string} size - Passed through to the internal fig-swatch (e.g. "large")
|
|
9135
9439
|
* @attr {boolean} disabled - Whether the input is disabled
|
|
9136
9440
|
* @fires input - When the gradient value changes
|
|
9137
9441
|
* @fires change - When the gradient value is committed
|
|
@@ -9160,7 +9464,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9160
9464
|
}
|
|
9161
9465
|
|
|
9162
9466
|
static get observedAttributes() {
|
|
9163
|
-
return ["value", "disabled", "edit", "mode"];
|
|
9467
|
+
return ["value", "disabled", "edit", "mode", "size"];
|
|
9164
9468
|
}
|
|
9165
9469
|
|
|
9166
9470
|
get #editMode() {
|
|
@@ -9186,7 +9490,8 @@ class FigInputGradient extends HTMLElement {
|
|
|
9186
9490
|
}
|
|
9187
9491
|
|
|
9188
9492
|
#syncFocusTarget() {
|
|
9189
|
-
const disabled =
|
|
9493
|
+
const disabled =
|
|
9494
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9190
9495
|
if (disabled) {
|
|
9191
9496
|
this.setAttribute("tabindex", "-1");
|
|
9192
9497
|
return;
|
|
@@ -9195,10 +9500,21 @@ class FigInputGradient extends HTMLElement {
|
|
|
9195
9500
|
}
|
|
9196
9501
|
|
|
9197
9502
|
#normalizeGradient(gradient) {
|
|
9503
|
+
const normalized = normalizeGradientConfig(gradient);
|
|
9198
9504
|
return {
|
|
9199
|
-
...
|
|
9200
|
-
type: "linear",
|
|
9201
|
-
|
|
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,
|
|
9202
9518
|
};
|
|
9203
9519
|
}
|
|
9204
9520
|
|
|
@@ -9231,6 +9547,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9231
9547
|
|
|
9232
9548
|
#onKeyDown = (e) => {
|
|
9233
9549
|
const active = document.activeElement;
|
|
9550
|
+
if (!(active instanceof Node) || !this.contains(active)) return;
|
|
9234
9551
|
const isTyping =
|
|
9235
9552
|
active &&
|
|
9236
9553
|
(active.tagName === "INPUT" ||
|
|
@@ -9312,7 +9629,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9312
9629
|
if (this.#editMode !== "picker") return;
|
|
9313
9630
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
9314
9631
|
if (e.altKey || e.ctrlKey || e.metaKey) return;
|
|
9315
|
-
if (this
|
|
9632
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
9316
9633
|
const picker = this.querySelector("fig-fill-picker");
|
|
9317
9634
|
if (!picker || typeof picker.open !== "function") return;
|
|
9318
9635
|
e.preventDefault();
|
|
@@ -9357,6 +9674,12 @@ class FigInputGradient extends HTMLElement {
|
|
|
9357
9674
|
.join(", ");
|
|
9358
9675
|
const interp = gradientInterpolationClause(gradient);
|
|
9359
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
|
+
}
|
|
9360
9683
|
return `linear-gradient(${gradient.angle}deg${interpolation}, ${stops})`;
|
|
9361
9684
|
}
|
|
9362
9685
|
|
|
@@ -9367,8 +9690,21 @@ class FigInputGradient extends HTMLElement {
|
|
|
9367
9690
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
9368
9691
|
}
|
|
9369
9692
|
|
|
9693
|
+
#swatchSizeAttr() {
|
|
9694
|
+
const size = this.getAttribute("size");
|
|
9695
|
+
return size ? ` size="${size}"` : "";
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9698
|
+
#syncSwatchSize() {
|
|
9699
|
+
if (!this.#swatch) return;
|
|
9700
|
+
const size = this.getAttribute("size");
|
|
9701
|
+
if (size) this.#swatch.setAttribute("size", size);
|
|
9702
|
+
else this.#swatch.removeAttribute("size");
|
|
9703
|
+
}
|
|
9704
|
+
|
|
9370
9705
|
#buildStopHandles() {
|
|
9371
|
-
const disabled =
|
|
9706
|
+
const disabled =
|
|
9707
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9372
9708
|
const tipAttr = this.#stopHandleMode === "tip" ? ' tip="color"' : "";
|
|
9373
9709
|
return this.#gradient.stops
|
|
9374
9710
|
.map(
|
|
@@ -9382,7 +9718,9 @@ class FigInputGradient extends HTMLElement {
|
|
|
9382
9718
|
#addedOnPointerDown = false;
|
|
9383
9719
|
|
|
9384
9720
|
#render() {
|
|
9385
|
-
|
|
9721
|
+
this.#colorObserver?.disconnect();
|
|
9722
|
+
this.#colorObserver = null;
|
|
9723
|
+
const disabled = figBooleanAttribute(this, "disabled");
|
|
9386
9724
|
const mode = this.#editMode;
|
|
9387
9725
|
|
|
9388
9726
|
if (mode === "picker" && hasFigFillPicker()) {
|
|
@@ -9391,7 +9729,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9391
9729
|
const gradientValue = JSON.stringify(this.value);
|
|
9392
9730
|
this.innerHTML = `
|
|
9393
9731
|
<fig-fill-picker mode="gradient"${expAttr} value='${gradientValue}'${disabled ? " disabled" : ""}>
|
|
9394
|
-
<fig-swatch background="${this.#buildGradientCSS()}"${disabled ? " disabled" : ""}></fig-swatch>
|
|
9732
|
+
<fig-swatch background="${this.#buildGradientCSS()}"${this.#swatchSizeAttr()}${disabled ? " disabled" : ""}></fig-swatch>
|
|
9395
9733
|
</fig-fill-picker>`;
|
|
9396
9734
|
this.#swatch = this.querySelector("fig-swatch");
|
|
9397
9735
|
this.#track = null;
|
|
@@ -9401,7 +9739,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
9401
9739
|
}
|
|
9402
9740
|
|
|
9403
9741
|
this.innerHTML = `
|
|
9404
|
-
<fig-swatch background="${this.#buildGradientCSS()}"${disabled ? " disabled" : ""}></fig-swatch>
|
|
9742
|
+
<fig-swatch background="${this.#buildGradientCSS()}"${this.#swatchSizeAttr()}${disabled ? " disabled" : ""}></fig-swatch>
|
|
9405
9743
|
${mode === "true" || mode === "picker" ? `<div class="fig-input-gradient-track">${this.#buildStopHandles()}</div>` : ""}`;
|
|
9406
9744
|
this.#swatch = this.querySelector("fig-swatch");
|
|
9407
9745
|
this.#track = this.querySelector(".fig-input-gradient-track");
|
|
@@ -9450,7 +9788,11 @@ class FigInputGradient extends HTMLElement {
|
|
|
9450
9788
|
}
|
|
9451
9789
|
|
|
9452
9790
|
#setupGhostHandle() {
|
|
9453
|
-
if (
|
|
9791
|
+
if (
|
|
9792
|
+
!this.#track ||
|
|
9793
|
+
(this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false")
|
|
9794
|
+
)
|
|
9795
|
+
return;
|
|
9454
9796
|
|
|
9455
9797
|
const ghost = document.createElement("fig-handle");
|
|
9456
9798
|
ghost.classList.add("fig-input-gradient-ghost");
|
|
@@ -9711,13 +10053,14 @@ class FigInputGradient extends HTMLElement {
|
|
|
9711
10053
|
#syncSwatch() {
|
|
9712
10054
|
if (!this.#swatch) return;
|
|
9713
10055
|
this.#swatch.setAttribute("background", this.#buildGradientCSS());
|
|
10056
|
+
this.#syncSwatchSize();
|
|
9714
10057
|
}
|
|
9715
10058
|
|
|
9716
10059
|
#setupEventListeners() {
|
|
9717
10060
|
if (!this.#track) return;
|
|
9718
10061
|
|
|
9719
10062
|
this.#track.addEventListener("pointerdown", (e) => {
|
|
9720
|
-
if (this
|
|
10063
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
9721
10064
|
if (e.target.closest("fig-handle:not(.fig-input-gradient-ghost)")) return;
|
|
9722
10065
|
if (e.button !== 0) return;
|
|
9723
10066
|
e.preventDefault();
|
|
@@ -9934,7 +10277,11 @@ class FigInputGradient extends HTMLElement {
|
|
|
9934
10277
|
}
|
|
9935
10278
|
|
|
9936
10279
|
focus(options) {
|
|
9937
|
-
if (
|
|
10280
|
+
if (
|
|
10281
|
+
this.hasAttribute("disabled") &&
|
|
10282
|
+
this.getAttribute("disabled") !== "false"
|
|
10283
|
+
)
|
|
10284
|
+
return;
|
|
9938
10285
|
if (this.#isEditable) {
|
|
9939
10286
|
const firstHandle = this.#firstStopHandle();
|
|
9940
10287
|
if (firstHandle) {
|
|
@@ -9967,11 +10314,15 @@ class FigInputGradient extends HTMLElement {
|
|
|
9967
10314
|
case "mode":
|
|
9968
10315
|
this.#syncHandleMode();
|
|
9969
10316
|
break;
|
|
10317
|
+
case "size":
|
|
10318
|
+
this.#syncSwatchSize();
|
|
10319
|
+
break;
|
|
9970
10320
|
}
|
|
9971
10321
|
}
|
|
9972
10322
|
|
|
9973
10323
|
#syncDisabled() {
|
|
9974
|
-
const disabled =
|
|
10324
|
+
const disabled =
|
|
10325
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9975
10326
|
this.#syncFocusTarget();
|
|
9976
10327
|
if (this.#swatch) {
|
|
9977
10328
|
if (disabled) this.#swatch.setAttribute("disabled", "");
|
|
@@ -9999,16 +10350,24 @@ customElements.define("fig-input-gradient", FigInputGradient);
|
|
|
9999
10350
|
class FigCheckbox extends HTMLElement {
|
|
10000
10351
|
#labelElement = null;
|
|
10001
10352
|
#boundHandleInput;
|
|
10353
|
+
#boundHandleChange;
|
|
10354
|
+
#boundHandleHostClick;
|
|
10355
|
+
#labelIsGenerated = false;
|
|
10002
10356
|
|
|
10003
10357
|
constructor() {
|
|
10004
10358
|
super();
|
|
10005
10359
|
this.input = document.createElement("input");
|
|
10006
|
-
this.name = this.getAttribute("name") || "
|
|
10360
|
+
this.name = this.getAttribute("name") || "";
|
|
10007
10361
|
this.input.value = this.getAttribute("value") || "";
|
|
10008
10362
|
this.input.setAttribute("id", figUniqueId());
|
|
10009
|
-
this.input.setAttribute("name", this.name);
|
|
10363
|
+
if (this.name) this.input.setAttribute("name", this.name);
|
|
10010
10364
|
this.input.setAttribute("type", "checkbox");
|
|
10011
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
|
+
};
|
|
10012
10371
|
}
|
|
10013
10372
|
connectedCallback() {
|
|
10014
10373
|
// Reuse cloned internals when this element is duplicated via option.cloneNode(true).
|
|
@@ -10019,13 +10378,18 @@ class FigCheckbox extends HTMLElement {
|
|
|
10019
10378
|
this.append(this.input);
|
|
10020
10379
|
}
|
|
10021
10380
|
|
|
10022
|
-
this.input.removeEventListener("
|
|
10023
|
-
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);
|
|
10024
10387
|
this.#syncInputState();
|
|
10025
10388
|
|
|
10026
10389
|
const existingLabel = this.querySelector(":scope > label");
|
|
10027
10390
|
if (existingLabel) {
|
|
10028
10391
|
this.#labelElement = existingLabel;
|
|
10392
|
+
this.#labelIsGenerated = false;
|
|
10029
10393
|
this.#labelElement.setAttribute("for", this.input.id);
|
|
10030
10394
|
}
|
|
10031
10395
|
|
|
@@ -10035,6 +10399,21 @@ class FigCheckbox extends HTMLElement {
|
|
|
10035
10399
|
this.#labelElement.innerText = this.getAttribute("label");
|
|
10036
10400
|
}
|
|
10037
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
|
+
|
|
10038
10417
|
this.render();
|
|
10039
10418
|
}
|
|
10040
10419
|
static get observedAttributes() {
|
|
@@ -10044,6 +10423,7 @@ class FigCheckbox extends HTMLElement {
|
|
|
10044
10423
|
#createLabel() {
|
|
10045
10424
|
if (!this.#labelElement) {
|
|
10046
10425
|
this.#labelElement = document.createElement("label");
|
|
10426
|
+
this.#labelIsGenerated = true;
|
|
10047
10427
|
this.#labelElement.setAttribute("for", this.input.id);
|
|
10048
10428
|
}
|
|
10049
10429
|
// Add to DOM if not already there and input is in the DOM
|
|
@@ -10078,7 +10458,9 @@ class FigCheckbox extends HTMLElement {
|
|
|
10078
10458
|
this.input.indeterminate = indeterminate;
|
|
10079
10459
|
this.input.disabled = disabled;
|
|
10080
10460
|
this.input.value = this.getAttribute("value") || "";
|
|
10081
|
-
|
|
10461
|
+
const name = this.getAttribute("name") || this.name;
|
|
10462
|
+
if (name) this.input.setAttribute("name", name);
|
|
10463
|
+
else this.input.removeAttribute("name");
|
|
10082
10464
|
if (indeterminate) {
|
|
10083
10465
|
this.input.setAttribute("indeterminate", "true");
|
|
10084
10466
|
} else {
|
|
@@ -10114,8 +10496,9 @@ class FigCheckbox extends HTMLElement {
|
|
|
10114
10496
|
}
|
|
10115
10497
|
|
|
10116
10498
|
disconnectedCallback() {
|
|
10117
|
-
this.input.removeEventListener("
|
|
10118
|
-
this.input.
|
|
10499
|
+
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
10500
|
+
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
10501
|
+
this.removeEventListener("click", this.#boundHandleHostClick);
|
|
10119
10502
|
}
|
|
10120
10503
|
|
|
10121
10504
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -10124,9 +10507,10 @@ class FigCheckbox extends HTMLElement {
|
|
|
10124
10507
|
if (newValue) {
|
|
10125
10508
|
this.#createLabel();
|
|
10126
10509
|
this.#labelElement.innerText = newValue;
|
|
10127
|
-
} else if (this.#labelElement) {
|
|
10510
|
+
} else if (this.#labelElement && this.#labelIsGenerated) {
|
|
10128
10511
|
this.#labelElement.remove();
|
|
10129
10512
|
this.#labelElement = null;
|
|
10513
|
+
this.#labelIsGenerated = false;
|
|
10130
10514
|
}
|
|
10131
10515
|
break;
|
|
10132
10516
|
case "checked":
|
|
@@ -10142,7 +10526,8 @@ class FigCheckbox extends HTMLElement {
|
|
|
10142
10526
|
this.#syncInputState();
|
|
10143
10527
|
break;
|
|
10144
10528
|
case "name":
|
|
10145
|
-
this.input.setAttribute("name", newValue
|
|
10529
|
+
if (newValue) this.input.setAttribute("name", newValue);
|
|
10530
|
+
else this.input.removeAttribute("name");
|
|
10146
10531
|
break;
|
|
10147
10532
|
case "value":
|
|
10148
10533
|
this.input.value = newValue || "";
|
|
@@ -10168,7 +10553,6 @@ class FigCheckbox extends HTMLElement {
|
|
|
10168
10553
|
} else {
|
|
10169
10554
|
this.removeAttribute("checked");
|
|
10170
10555
|
}
|
|
10171
|
-
// Emit both input and change events
|
|
10172
10556
|
this.dispatchEvent(
|
|
10173
10557
|
new CustomEvent("input", {
|
|
10174
10558
|
bubbles: true,
|
|
@@ -10176,6 +10560,11 @@ class FigCheckbox extends HTMLElement {
|
|
|
10176
10560
|
detail: { checked: this.input.checked, value: this.input.value },
|
|
10177
10561
|
}),
|
|
10178
10562
|
);
|
|
10563
|
+
}
|
|
10564
|
+
|
|
10565
|
+
handleChange(e) {
|
|
10566
|
+
e.stopPropagation();
|
|
10567
|
+
this.#syncAriaChecked();
|
|
10179
10568
|
this.dispatchEvent(
|
|
10180
10569
|
new CustomEvent("change", {
|
|
10181
10570
|
bubbles: true,
|
|
@@ -10200,7 +10589,9 @@ class FigRadio extends FigCheckbox {
|
|
|
10200
10589
|
constructor() {
|
|
10201
10590
|
super();
|
|
10202
10591
|
this.input.setAttribute("type", "radio");
|
|
10203
|
-
|
|
10592
|
+
const name = this.getAttribute("name");
|
|
10593
|
+
if (name) this.input.setAttribute("name", name);
|
|
10594
|
+
else this.input.removeAttribute("name");
|
|
10204
10595
|
}
|
|
10205
10596
|
}
|
|
10206
10597
|
customElements.define("fig-radio", FigRadio);
|
|
@@ -10307,9 +10698,7 @@ class FigComboInput extends HTMLElement {
|
|
|
10307
10698
|
this.#setupListeners();
|
|
10308
10699
|
}
|
|
10309
10700
|
|
|
10310
|
-
|
|
10311
|
-
this.#applyDisabled(true);
|
|
10312
|
-
}
|
|
10701
|
+
this.#applyDisabled(figBooleanAttribute(this, "disabled"));
|
|
10313
10702
|
}
|
|
10314
10703
|
|
|
10315
10704
|
disconnectedCallback() {
|
|
@@ -10321,15 +10710,17 @@ class FigComboInput extends HTMLElement {
|
|
|
10321
10710
|
const placeholder = this.getAttribute("placeholder") || "";
|
|
10322
10711
|
const currentValue = this.value;
|
|
10323
10712
|
const experimental = this.getAttribute("experimental");
|
|
10324
|
-
const expAttr = experimental
|
|
10713
|
+
const expAttr = experimental
|
|
10714
|
+
? ` experimental="${figEscapeAttribute(experimental)}"`
|
|
10715
|
+
: "";
|
|
10325
10716
|
const dropdownLabel = this.#dropdownLabel();
|
|
10326
10717
|
|
|
10327
10718
|
const dropdownHTML = this.#usesCustomDropdown
|
|
10328
10719
|
? ""
|
|
10329
|
-
: `<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>`;
|
|
10330
10721
|
|
|
10331
10722
|
this.innerHTML = `<div class="input-combo">
|
|
10332
|
-
<fig-input-text placeholder="${placeholder}" value="${currentValue}"></fig-input-text>
|
|
10723
|
+
<fig-input-text placeholder="${figEscapeAttribute(placeholder)}" value="${figEscapeAttribute(currentValue)}"></fig-input-text>
|
|
10333
10724
|
<fig-button type="select" variant="input" icon>
|
|
10334
10725
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10335
10726
|
<path d="M5.87868 7.12132L8 9.24264L10.1213 7.12132" stroke="currentColor" stroke-opacity="0.9" stroke-linecap="round"/>
|
|
@@ -10477,9 +10868,13 @@ class FigComboInput extends HTMLElement {
|
|
|
10477
10868
|
case "options":
|
|
10478
10869
|
if (this.#dropdown && !this.#usesCustomDropdown) {
|
|
10479
10870
|
const options = this.#getOptions();
|
|
10480
|
-
this.#dropdown.
|
|
10481
|
-
.map((
|
|
10482
|
-
|
|
10871
|
+
this.#dropdown.replaceChildren(
|
|
10872
|
+
...options.map((value) => {
|
|
10873
|
+
const option = document.createElement("option");
|
|
10874
|
+
option.textContent = value;
|
|
10875
|
+
return option;
|
|
10876
|
+
}),
|
|
10877
|
+
);
|
|
10483
10878
|
}
|
|
10484
10879
|
break;
|
|
10485
10880
|
case "placeholder":
|
|
@@ -10869,6 +11264,7 @@ class FigMedia extends HTMLElement {
|
|
|
10869
11264
|
this.querySelectorAll("fig-swatch[data-generated]").forEach((el) => el.remove());
|
|
10870
11265
|
this.#ensurePreviewElement();
|
|
10871
11266
|
this.#ensureMediaElement();
|
|
11267
|
+
this.#addMediaElementListeners();
|
|
10872
11268
|
this.#syncGeneratedMediaElement();
|
|
10873
11269
|
this.#syncMediaAccessibility();
|
|
10874
11270
|
this.#syncCaption();
|
|
@@ -10877,6 +11273,9 @@ class FigMedia extends HTMLElement {
|
|
|
10877
11273
|
if (isUpload && !this.querySelector("fig-input-file[data-generated]")) {
|
|
10878
11274
|
this.#createFileInput();
|
|
10879
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);
|
|
10880
11279
|
}
|
|
10881
11280
|
|
|
10882
11281
|
disconnectedCallback() {
|
|
@@ -10899,6 +11298,14 @@ class FigMedia extends HTMLElement {
|
|
|
10899
11298
|
}
|
|
10900
11299
|
}
|
|
10901
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
|
+
|
|
10902
11309
|
#ensurePreviewElement() {
|
|
10903
11310
|
if (this.#previewEl?.isConnected) return;
|
|
10904
11311
|
const existing = this.querySelector(":scope > fig-preview");
|
|
@@ -10954,9 +11361,7 @@ class FigMedia extends HTMLElement {
|
|
|
10954
11361
|
video.preload = "auto";
|
|
10955
11362
|
this.#previewEl.append(video);
|
|
10956
11363
|
this.#mediaEl = video;
|
|
10957
|
-
this.#
|
|
10958
|
-
this.#mediaEl.addEventListener("pause", this.#boundHandleMediaPause);
|
|
10959
|
-
this.#mediaEl.addEventListener("ended", this.#boundHandleMediaEnded);
|
|
11364
|
+
this.#addMediaElementListeners();
|
|
10960
11365
|
const seekToFirstFrame = () => {
|
|
10961
11366
|
if (this.#mediaEl?.autoplay) return;
|
|
10962
11367
|
try {
|
|
@@ -11748,6 +12153,11 @@ class FigInputFile extends HTMLElement {
|
|
|
11748
12153
|
};
|
|
11749
12154
|
|
|
11750
12155
|
#onDragOver = (e) => {
|
|
12156
|
+
if (
|
|
12157
|
+
this.hasAttribute("disabled") &&
|
|
12158
|
+
this.getAttribute("disabled") !== "false"
|
|
12159
|
+
)
|
|
12160
|
+
return;
|
|
11751
12161
|
e.preventDefault();
|
|
11752
12162
|
if (!this.hasAttribute("dragover")) {
|
|
11753
12163
|
this.setAttribute("dragover", "");
|
|
@@ -11794,7 +12204,10 @@ class FigInputFile extends HTMLElement {
|
|
|
11794
12204
|
);
|
|
11795
12205
|
});
|
|
11796
12206
|
}
|
|
11797
|
-
if (
|
|
12207
|
+
if (
|
|
12208
|
+
!this.hasAttribute("multiple") ||
|
|
12209
|
+
this.getAttribute("multiple") === "false"
|
|
12210
|
+
) {
|
|
11798
12211
|
dropped = dropped.slice(0, 1);
|
|
11799
12212
|
}
|
|
11800
12213
|
if (dropped.length === 0) return;
|
|
@@ -11816,7 +12229,8 @@ class FigInputFile extends HTMLElement {
|
|
|
11816
12229
|
const disabled =
|
|
11817
12230
|
this.hasAttribute("disabled") &&
|
|
11818
12231
|
this.getAttribute("disabled") !== "false";
|
|
11819
|
-
const multiple =
|
|
12232
|
+
const multiple =
|
|
12233
|
+
this.hasAttribute("multiple") && this.getAttribute("multiple") !== "false";
|
|
11820
12234
|
const variant = this.getAttribute("variant") || "input";
|
|
11821
12235
|
const hasFile =
|
|
11822
12236
|
(this.#files && this.#files.length > 0) ||
|
|
@@ -11850,6 +12264,7 @@ class FigInputFile extends HTMLElement {
|
|
|
11850
12264
|
this.#fileInput = document.createElement("input");
|
|
11851
12265
|
this.#fileInput.type = "file";
|
|
11852
12266
|
this.#fileInput.title = "";
|
|
12267
|
+
this.#fileInput.disabled = disabled;
|
|
11853
12268
|
if (accepts) this.#fileInput.setAttribute("accept", accepts);
|
|
11854
12269
|
if (multiple) this.#fileInput.setAttribute("multiple", "");
|
|
11855
12270
|
this.#fileInput.addEventListener("change", this.#onFileChange);
|
|
@@ -11898,6 +12313,7 @@ class FigInputFile extends HTMLElement {
|
|
|
11898
12313
|
this.#fileInput = document.createElement("input");
|
|
11899
12314
|
this.#fileInput.type = "file";
|
|
11900
12315
|
this.#fileInput.title = "";
|
|
12316
|
+
this.#fileInput.disabled = disabled;
|
|
11901
12317
|
if (accepts) this.#fileInput.setAttribute("accept", accepts);
|
|
11902
12318
|
if (multiple) this.#fileInput.setAttribute("multiple", "");
|
|
11903
12319
|
this.#fileInput.addEventListener("change", this.#onFileChange);
|
|
@@ -14849,7 +15265,7 @@ class FigColorTip extends HTMLElement {
|
|
|
14849
15265
|
if (mode === "add" || mode === "remove") {
|
|
14850
15266
|
const iconName = mode === "add" ? "add" : "minus";
|
|
14851
15267
|
const label = this.getAttribute("aria-label") || (mode === "add" ? "Add color stop" : "Remove color stop");
|
|
14852
|
-
this.innerHTML = `<fig-button icon variant="ghost" aria-label="${label}"><fig-icon name="${iconName}"></fig-icon></fig-button>`;
|
|
15268
|
+
this.innerHTML = `<fig-button icon variant="ghost" aria-label="${figEscapeAttribute(label)}"><fig-icon name="${iconName}"></fig-icon></fig-button>`;
|
|
14853
15269
|
this.#fillPicker = null;
|
|
14854
15270
|
this.#swatch = null;
|
|
14855
15271
|
this.addEventListener("click", this.#handleControlClick);
|
|
@@ -14994,7 +15410,7 @@ class FigColorTip extends HTMLElement {
|
|
|
14994
15410
|
} else {
|
|
14995
15411
|
this.#fillPicker.setAttribute("alpha", "false");
|
|
14996
15412
|
}
|
|
14997
|
-
if (this
|
|
15413
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
14998
15414
|
this.#fillPicker.setAttribute("disabled", "");
|
|
14999
15415
|
} else {
|
|
15000
15416
|
this.#fillPicker.removeAttribute("disabled");
|
|
@@ -15009,7 +15425,7 @@ class FigColorTip extends HTMLElement {
|
|
|
15009
15425
|
} else {
|
|
15010
15426
|
this.#swatch.removeAttribute("alpha");
|
|
15011
15427
|
}
|
|
15012
|
-
if (this
|
|
15428
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
15013
15429
|
this.#swatch.setAttribute("disabled", "");
|
|
15014
15430
|
} else {
|
|
15015
15431
|
this.#swatch.removeAttribute("disabled");
|
|
@@ -15120,14 +15536,14 @@ class FigColorTip extends HTMLElement {
|
|
|
15120
15536
|
}
|
|
15121
15537
|
|
|
15122
15538
|
get selected() {
|
|
15123
|
-
return this
|
|
15539
|
+
return figBooleanAttribute(this, "selected");
|
|
15124
15540
|
}
|
|
15125
15541
|
set selected(value) {
|
|
15126
15542
|
this.toggleAttribute("selected", Boolean(value));
|
|
15127
15543
|
}
|
|
15128
15544
|
|
|
15129
15545
|
get disabled() {
|
|
15130
|
-
return this
|
|
15546
|
+
return figBooleanAttribute(this, "disabled");
|
|
15131
15547
|
}
|
|
15132
15548
|
set disabled(value) {
|
|
15133
15549
|
this.toggleAttribute("disabled", Boolean(value));
|
|
@@ -15154,9 +15570,9 @@ class FigChoice extends HTMLElement {
|
|
|
15154
15570
|
}
|
|
15155
15571
|
this.setAttribute(
|
|
15156
15572
|
"aria-selected",
|
|
15157
|
-
this
|
|
15573
|
+
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
15158
15574
|
);
|
|
15159
|
-
if (this
|
|
15575
|
+
if (figBooleanAttribute(this, "disabled")) {
|
|
15160
15576
|
this.setAttribute("aria-disabled", "true");
|
|
15161
15577
|
}
|
|
15162
15578
|
}
|
|
@@ -15165,7 +15581,7 @@ class FigChoice extends HTMLElement {
|
|
|
15165
15581
|
if (name === "selected") {
|
|
15166
15582
|
this.setAttribute(
|
|
15167
15583
|
"aria-selected",
|
|
15168
|
-
this
|
|
15584
|
+
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
15169
15585
|
);
|
|
15170
15586
|
}
|
|
15171
15587
|
if (name === "disabled") {
|
|
@@ -15260,7 +15676,7 @@ class FigChooser extends HTMLElement {
|
|
|
15260
15676
|
const choices = this.choices;
|
|
15261
15677
|
for (const choice of choices) {
|
|
15262
15678
|
const shouldSelect = choice === element;
|
|
15263
|
-
const isSelected = choice
|
|
15679
|
+
const isSelected = figBooleanAttribute(choice, "selected");
|
|
15264
15680
|
if (shouldSelect && !isSelected) {
|
|
15265
15681
|
choice.setAttribute("selected", "");
|
|
15266
15682
|
} else if (!shouldSelect && isSelected) {
|
|
@@ -15402,7 +15818,9 @@ class FigChooser extends HTMLElement {
|
|
|
15402
15818
|
const valueAttr = this.getAttribute("value");
|
|
15403
15819
|
if (valueAttr && this.#selectByValue(valueAttr)) return;
|
|
15404
15820
|
|
|
15405
|
-
const alreadySelected = choices.find((
|
|
15821
|
+
const alreadySelected = choices.find((choice) =>
|
|
15822
|
+
figBooleanAttribute(choice, "selected"),
|
|
15823
|
+
);
|
|
15406
15824
|
if (alreadySelected) {
|
|
15407
15825
|
this.selectedChoice = alreadySelected;
|
|
15408
15826
|
return;
|
|
@@ -16014,7 +16432,7 @@ class FigHandle extends HTMLElement {
|
|
|
16014
16432
|
}
|
|
16015
16433
|
|
|
16016
16434
|
#onHitAreaPointerDown(e) {
|
|
16017
|
-
if (this
|
|
16435
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16018
16436
|
if (e.target !== this.#hitAreaEl) return;
|
|
16019
16437
|
if (this.#hitAreaMode === "delegate") {
|
|
16020
16438
|
e.preventDefault();
|
|
@@ -16057,7 +16475,7 @@ class FigHandle extends HTMLElement {
|
|
|
16057
16475
|
}
|
|
16058
16476
|
|
|
16059
16477
|
select() {
|
|
16060
|
-
if (this
|
|
16478
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16061
16479
|
this.setAttribute("selected", "");
|
|
16062
16480
|
}
|
|
16063
16481
|
|
|
@@ -16096,12 +16514,12 @@ class FigHandle extends HTMLElement {
|
|
|
16096
16514
|
) {
|
|
16097
16515
|
if (this.#moveByKeyboard(e)) {
|
|
16098
16516
|
e.preventDefault();
|
|
16099
|
-
if (!this
|
|
16517
|
+
if (!figBooleanAttribute(this, "selected")) this.select();
|
|
16100
16518
|
}
|
|
16101
16519
|
return;
|
|
16102
16520
|
}
|
|
16103
16521
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
16104
|
-
if (e.target === this && !this
|
|
16522
|
+
if (e.target === this && !figBooleanAttribute(this, "selected")) {
|
|
16105
16523
|
e.preventDefault();
|
|
16106
16524
|
if (
|
|
16107
16525
|
this.getAttribute("type") === "color" &&
|
|
@@ -16114,7 +16532,7 @@ class FigHandle extends HTMLElement {
|
|
|
16114
16532
|
}
|
|
16115
16533
|
return;
|
|
16116
16534
|
}
|
|
16117
|
-
if (!this
|
|
16535
|
+
if (!figBooleanAttribute(this, "selected")) return;
|
|
16118
16536
|
if (this.getAttribute("type") !== "color") return;
|
|
16119
16537
|
if (!this.#canOpenColorPicker) return;
|
|
16120
16538
|
e.preventDefault();
|
|
@@ -16124,7 +16542,7 @@ class FigHandle extends HTMLElement {
|
|
|
16124
16542
|
};
|
|
16125
16543
|
|
|
16126
16544
|
#moveByKeyboard(event) {
|
|
16127
|
-
if (this
|
|
16545
|
+
if (figBooleanAttribute(this, "disabled")) return false;
|
|
16128
16546
|
const container = this.#getContainer();
|
|
16129
16547
|
if (!container) return false;
|
|
16130
16548
|
const rect = container.getBoundingClientRect();
|
|
@@ -16255,7 +16673,7 @@ class FigHandle extends HTMLElement {
|
|
|
16255
16673
|
}
|
|
16256
16674
|
|
|
16257
16675
|
#onPointerDown(e) {
|
|
16258
|
-
if (!this.#dragEnabled || this
|
|
16676
|
+
if (!this.#dragEnabled || figBooleanAttribute(this, "disabled")) return;
|
|
16259
16677
|
e.preventDefault();
|
|
16260
16678
|
const container = this.#getContainer();
|
|
16261
16679
|
if (!container) return;
|
|
@@ -16321,7 +16739,7 @@ class FigHandle extends HTMLElement {
|
|
|
16321
16739
|
this.#closeColorPickerForDrag();
|
|
16322
16740
|
this.classList.add("dragging");
|
|
16323
16741
|
this.style.cursor = "grabbing";
|
|
16324
|
-
if (!this
|
|
16742
|
+
if (!figBooleanAttribute(this, "selected")) this.select();
|
|
16325
16743
|
}
|
|
16326
16744
|
this.#didDrag = true;
|
|
16327
16745
|
clampAndApply(e.clientX, e.clientY, e.shiftKey);
|
|
@@ -16478,7 +16896,7 @@ class FigHandle extends HTMLElement {
|
|
|
16478
16896
|
}
|
|
16479
16897
|
|
|
16480
16898
|
#openDirectColorPicker() {
|
|
16481
|
-
if (this
|
|
16899
|
+
if (figBooleanAttribute(this, "disabled")) return;
|
|
16482
16900
|
const picker = this.#ensureDirectColorPicker();
|
|
16483
16901
|
if (!picker) {
|
|
16484
16902
|
this.#openNativeColorPicker();
|
|
@@ -16833,6 +17251,12 @@ class FigMenu extends HTMLElement {
|
|
|
16833
17251
|
}
|
|
16834
17252
|
if (this.#popup) {
|
|
16835
17253
|
this.#popup.removeEventListener("close", this.#boundPopupClose);
|
|
17254
|
+
const items = Array.from(
|
|
17255
|
+
this.#popup.querySelectorAll(
|
|
17256
|
+
":scope > fig-menu-item, :scope > fig-menu-separator",
|
|
17257
|
+
),
|
|
17258
|
+
);
|
|
17259
|
+
for (const item of items) this.insertBefore(item, this.#popup);
|
|
16836
17260
|
this.#popup.remove();
|
|
16837
17261
|
this.#popup = null;
|
|
16838
17262
|
}
|
|
@@ -17041,16 +17465,24 @@ class FigMenu extends HTMLElement {
|
|
|
17041
17465
|
#showAtAfterPointerRelease(x, y) {
|
|
17042
17466
|
let opened = false;
|
|
17043
17467
|
let fallbackTimer = 0;
|
|
17468
|
+
const cleanup = () => {
|
|
17469
|
+
window.clearTimeout(fallbackTimer);
|
|
17470
|
+
window.removeEventListener("pointerup", openMenu, true);
|
|
17471
|
+
window.removeEventListener("pointercancel", cancelMenu, true);
|
|
17472
|
+
};
|
|
17044
17473
|
const openMenu = () => {
|
|
17045
17474
|
if (opened) return;
|
|
17046
17475
|
opened = true;
|
|
17047
|
-
|
|
17048
|
-
window.removeEventListener("pointerup", openMenu, true);
|
|
17049
|
-
window.removeEventListener("pointercancel", openMenu, true);
|
|
17476
|
+
cleanup();
|
|
17050
17477
|
requestAnimationFrame(() => this.showAt(x, y));
|
|
17051
17478
|
};
|
|
17479
|
+
const cancelMenu = () => {
|
|
17480
|
+
if (opened) return;
|
|
17481
|
+
opened = true;
|
|
17482
|
+
cleanup();
|
|
17483
|
+
};
|
|
17052
17484
|
window.addEventListener("pointerup", openMenu, { once: true, capture: true });
|
|
17053
|
-
window.addEventListener("pointercancel",
|
|
17485
|
+
window.addEventListener("pointercancel", cancelMenu, {
|
|
17054
17486
|
once: true,
|
|
17055
17487
|
capture: true,
|
|
17056
17488
|
});
|