@rogieking/figui3 3.16.0 → 3.17.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/README.md +725 -643
- package/components.css +1 -21
- package/dist/base.css +1 -99
- package/dist/components.css +1 -3978
- package/dist/fig.css +1 -2
- package/dist/fig.js +80 -78
- package/fig.js +209 -151
- package/package.json +14 -9
package/fig.js
CHANGED
|
@@ -75,24 +75,31 @@ function figUniqueId() {
|
|
|
75
75
|
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
* Gets the highest z-index currently in use on the page
|
|
80
|
-
* @returns {number} The highest z-index found, minimum of 1000
|
|
81
|
-
*/
|
|
78
|
+
let _figZCounter = 10000;
|
|
82
79
|
function figGetHighestZIndex() {
|
|
83
|
-
|
|
80
|
+
return _figZCounter++;
|
|
81
|
+
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
highest = zIndex;
|
|
91
|
-
}
|
|
83
|
+
function figSyncCssVar(el, prop, value) {
|
|
84
|
+
if (value && value.trim()) {
|
|
85
|
+
el.style.setProperty(prop, value.trim());
|
|
86
|
+
} else {
|
|
87
|
+
el.style.removeProperty(prop);
|
|
92
88
|
}
|
|
89
|
+
}
|
|
93
90
|
|
|
94
|
-
|
|
91
|
+
let _figSharedCanvas = null;
|
|
92
|
+
let _figSharedCtx = null;
|
|
93
|
+
function figGetSharedCanvas(width = 1, height = 1) {
|
|
94
|
+
if (!_figSharedCanvas) {
|
|
95
|
+
_figSharedCanvas = document.createElement("canvas");
|
|
96
|
+
_figSharedCtx = _figSharedCanvas.getContext("2d");
|
|
97
|
+
}
|
|
98
|
+
if (_figSharedCanvas.width !== width) _figSharedCanvas.width = width;
|
|
99
|
+
if (_figSharedCanvas.height !== height) _figSharedCanvas.height = height;
|
|
100
|
+
return { canvas: _figSharedCanvas, ctx: _figSharedCtx };
|
|
95
101
|
}
|
|
102
|
+
|
|
96
103
|
/**
|
|
97
104
|
* Checks if the browser supports the native popover API
|
|
98
105
|
* @returns {boolean} True if popover is supported
|
|
@@ -253,6 +260,7 @@ class FigDropdown extends HTMLElement {
|
|
|
253
260
|
set label(value) {
|
|
254
261
|
this.#label = value;
|
|
255
262
|
}
|
|
263
|
+
#boundSlotChange;
|
|
256
264
|
constructor() {
|
|
257
265
|
super();
|
|
258
266
|
this.select = document.createElement("select");
|
|
@@ -260,6 +268,7 @@ class FigDropdown extends HTMLElement {
|
|
|
260
268
|
this.attachShadow({ mode: "open" });
|
|
261
269
|
this.#boundHandleSelectInput = this.#handleSelectInput.bind(this);
|
|
262
270
|
this.#boundHandleSelectChange = this.#handleSelectChange.bind(this);
|
|
271
|
+
this.#boundSlotChange = this.slotChange.bind(this);
|
|
263
272
|
}
|
|
264
273
|
|
|
265
274
|
#supportsSelectedContent() {
|
|
@@ -342,7 +351,7 @@ class FigDropdown extends HTMLElement {
|
|
|
342
351
|
this.appendChild(this.select);
|
|
343
352
|
this.shadowRoot.appendChild(this.optionsSlot);
|
|
344
353
|
|
|
345
|
-
this.optionsSlot.addEventListener("slotchange", this
|
|
354
|
+
this.optionsSlot.addEventListener("slotchange", this.#boundSlotChange);
|
|
346
355
|
|
|
347
356
|
this.#addEventListeners();
|
|
348
357
|
}
|
|
@@ -478,6 +487,12 @@ class FigDropdown extends HTMLElement {
|
|
|
478
487
|
this.select.setAttribute("aria-label", this.#label);
|
|
479
488
|
}
|
|
480
489
|
}
|
|
490
|
+
|
|
491
|
+
disconnectedCallback() {
|
|
492
|
+
this.optionsSlot.removeEventListener("slotchange", this.#boundSlotChange);
|
|
493
|
+
this.select.removeEventListener("input", this.#boundHandleSelectInput);
|
|
494
|
+
this.select.removeEventListener("change", this.#boundHandleSelectChange);
|
|
495
|
+
}
|
|
481
496
|
}
|
|
482
497
|
|
|
483
498
|
customElements.define("fig-dropdown", FigDropdown);
|
|
@@ -496,6 +511,12 @@ class FigTooltip extends HTMLElement {
|
|
|
496
511
|
|
|
497
512
|
#boundHideOnChromeOpen;
|
|
498
513
|
#boundHidePopupOutsideClick;
|
|
514
|
+
#boundShowDelayedPopup;
|
|
515
|
+
#boundHandlePointerLeave;
|
|
516
|
+
#boundHandleTouchStart;
|
|
517
|
+
#boundHandleTouchMove;
|
|
518
|
+
#boundHandleTouchEnd;
|
|
519
|
+
#boundHandleTouchCancel;
|
|
499
520
|
#touchTimeout;
|
|
500
521
|
#isTouching = false;
|
|
501
522
|
#observer = null;
|
|
@@ -506,9 +527,14 @@ class FigTooltip extends HTMLElement {
|
|
|
506
527
|
let delay = parseInt(this.getAttribute("delay"));
|
|
507
528
|
this.delay = !isNaN(delay) ? delay : 500;
|
|
508
529
|
|
|
509
|
-
// Bind methods that will be used as event listeners
|
|
510
530
|
this.#boundHideOnChromeOpen = this.#hideOnChromeOpen.bind(this);
|
|
511
531
|
this.#boundHidePopupOutsideClick = this.hidePopupOutsideClick.bind(this);
|
|
532
|
+
this.#boundShowDelayedPopup = this.showDelayedPopup.bind(this);
|
|
533
|
+
this.#boundHandlePointerLeave = this.#handlePointerLeave.bind(this);
|
|
534
|
+
this.#boundHandleTouchStart = this.#handleTouchStart.bind(this);
|
|
535
|
+
this.#boundHandleTouchMove = this.#handleTouchMove.bind(this);
|
|
536
|
+
this.#boundHandleTouchEnd = this.#handleTouchEnd.bind(this);
|
|
537
|
+
this.#boundHandleTouchCancel = this.#handleTouchCancel.bind(this);
|
|
512
538
|
}
|
|
513
539
|
connectedCallback() {
|
|
514
540
|
this.setup();
|
|
@@ -517,16 +543,13 @@ class FigTooltip extends HTMLElement {
|
|
|
517
543
|
|
|
518
544
|
disconnectedCallback() {
|
|
519
545
|
this.destroy();
|
|
520
|
-
// Remove global listeners
|
|
521
546
|
document.removeEventListener(
|
|
522
547
|
"mousedown",
|
|
523
548
|
this.#boundHideOnChromeOpen,
|
|
524
549
|
true,
|
|
525
550
|
);
|
|
526
|
-
// Disconnect mutation observer
|
|
527
551
|
this.#stopObserving();
|
|
528
552
|
|
|
529
|
-
// Remove click outside listener for click action
|
|
530
553
|
if (this.action === "click") {
|
|
531
554
|
document.body.removeEventListener(
|
|
532
555
|
"click",
|
|
@@ -534,15 +557,17 @@ class FigTooltip extends HTMLElement {
|
|
|
534
557
|
);
|
|
535
558
|
}
|
|
536
559
|
|
|
537
|
-
// Clean up touch-related timers and listeners
|
|
538
560
|
clearTimeout(this.#touchTimeout);
|
|
539
561
|
if (this.action === "hover") {
|
|
540
|
-
this.removeEventListener("
|
|
541
|
-
this.removeEventListener("
|
|
542
|
-
this.removeEventListener("
|
|
543
|
-
this.removeEventListener("
|
|
562
|
+
this.removeEventListener("pointerenter", this.#boundShowDelayedPopup);
|
|
563
|
+
this.removeEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
564
|
+
this.removeEventListener("touchstart", this.#boundHandleTouchStart);
|
|
565
|
+
this.removeEventListener("touchmove", this.#boundHandleTouchMove);
|
|
566
|
+
this.removeEventListener("touchend", this.#boundHandleTouchEnd);
|
|
567
|
+
this.removeEventListener("touchcancel", this.#boundHandleTouchCancel);
|
|
544
568
|
} else if (this.action === "click") {
|
|
545
|
-
this.removeEventListener("
|
|
569
|
+
this.removeEventListener("click", this.#boundShowDelayedPopup);
|
|
570
|
+
this.removeEventListener("touchstart", this.#boundShowDelayedPopup);
|
|
546
571
|
}
|
|
547
572
|
}
|
|
548
573
|
|
|
@@ -613,36 +638,29 @@ class FigTooltip extends HTMLElement {
|
|
|
613
638
|
if (this.action === "manual") return;
|
|
614
639
|
if (this.action === "hover") {
|
|
615
640
|
if (!this.isTouchDevice()) {
|
|
616
|
-
this.addEventListener("pointerenter", this
|
|
617
|
-
this.addEventListener(
|
|
618
|
-
"pointerleave",
|
|
619
|
-
this.#handlePointerLeave.bind(this),
|
|
620
|
-
);
|
|
641
|
+
this.addEventListener("pointerenter", this.#boundShowDelayedPopup);
|
|
642
|
+
this.addEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
621
643
|
}
|
|
622
|
-
|
|
623
|
-
this.addEventListener("touchstart", this.#handleTouchStart.bind(this), {
|
|
644
|
+
this.addEventListener("touchstart", this.#boundHandleTouchStart, {
|
|
624
645
|
passive: true,
|
|
625
646
|
});
|
|
626
|
-
this.addEventListener("touchmove", this.#
|
|
647
|
+
this.addEventListener("touchmove", this.#boundHandleTouchMove, {
|
|
627
648
|
passive: true,
|
|
628
649
|
});
|
|
629
|
-
this.addEventListener("touchend", this.#
|
|
650
|
+
this.addEventListener("touchend", this.#boundHandleTouchEnd, {
|
|
630
651
|
passive: true,
|
|
631
652
|
});
|
|
632
|
-
this.addEventListener("touchcancel", this.#
|
|
653
|
+
this.addEventListener("touchcancel", this.#boundHandleTouchCancel, {
|
|
633
654
|
passive: true,
|
|
634
655
|
});
|
|
635
656
|
} else if (this.action === "click") {
|
|
636
|
-
this.addEventListener("click", this
|
|
657
|
+
this.addEventListener("click", this.#boundShowDelayedPopup);
|
|
637
658
|
document.body.addEventListener("click", this.#boundHidePopupOutsideClick);
|
|
638
|
-
|
|
639
|
-
// Touch support for better mobile responsiveness
|
|
640
|
-
this.addEventListener("touchstart", this.showDelayedPopup.bind(this), {
|
|
659
|
+
this.addEventListener("touchstart", this.#boundShowDelayedPopup, {
|
|
641
660
|
passive: true,
|
|
642
661
|
});
|
|
643
662
|
}
|
|
644
663
|
|
|
645
|
-
// Add listener for chrome interactions
|
|
646
664
|
document.addEventListener("mousedown", this.#boundHideOnChromeOpen, true);
|
|
647
665
|
}
|
|
648
666
|
|
|
@@ -934,6 +952,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
934
952
|
#boundPointerDown;
|
|
935
953
|
#boundPointerMove;
|
|
936
954
|
#boundPointerUp;
|
|
955
|
+
#boundClose;
|
|
937
956
|
#offset = 16; // 1rem in pixels
|
|
938
957
|
#positionInitialized = false;
|
|
939
958
|
#dragThreshold = 3; // pixels before drag starts
|
|
@@ -943,6 +962,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
943
962
|
this.#boundPointerDown = this.#handlePointerDown.bind(this);
|
|
944
963
|
this.#boundPointerMove = this.#handlePointerMove.bind(this);
|
|
945
964
|
this.#boundPointerUp = this.#handlePointerUp.bind(this);
|
|
965
|
+
this.#boundClose = this.close.bind(this);
|
|
946
966
|
}
|
|
947
967
|
|
|
948
968
|
connectedCallback() {
|
|
@@ -962,12 +982,15 @@ class FigDialog extends HTMLDialogElement {
|
|
|
962
982
|
|
|
963
983
|
disconnectedCallback() {
|
|
964
984
|
this.#removeDragListeners();
|
|
985
|
+
this.querySelectorAll("fig-button[close-dialog]").forEach((button) => {
|
|
986
|
+
button.removeEventListener("click", this.#boundClose);
|
|
987
|
+
});
|
|
965
988
|
}
|
|
966
989
|
|
|
967
990
|
#addCloseListeners() {
|
|
968
991
|
this.querySelectorAll("fig-button[close-dialog]").forEach((button) => {
|
|
969
|
-
button.removeEventListener("click", this
|
|
970
|
-
button.addEventListener("click", this
|
|
992
|
+
button.removeEventListener("click", this.#boundClose);
|
|
993
|
+
button.addEventListener("click", this.#boundClose);
|
|
971
994
|
});
|
|
972
995
|
}
|
|
973
996
|
|
|
@@ -2242,16 +2265,18 @@ figDefineCustomizedBuiltIn("fig-popup", FigPopup, { extends: "dialog" });
|
|
|
2242
2265
|
*/
|
|
2243
2266
|
class FigTab extends HTMLElement {
|
|
2244
2267
|
#selected;
|
|
2268
|
+
#boundHandleClick;
|
|
2245
2269
|
constructor() {
|
|
2246
2270
|
super();
|
|
2247
2271
|
this.content = null;
|
|
2248
2272
|
this.#selected = false;
|
|
2273
|
+
this.#boundHandleClick = this.handleClick.bind(this);
|
|
2249
2274
|
}
|
|
2250
2275
|
connectedCallback() {
|
|
2251
2276
|
this.setAttribute("label", this.innerText);
|
|
2252
2277
|
this.setAttribute("role", "tab");
|
|
2253
2278
|
this.setAttribute("tabindex", "0");
|
|
2254
|
-
this.addEventListener("click", this
|
|
2279
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
2255
2280
|
|
|
2256
2281
|
requestAnimationFrame(() => {
|
|
2257
2282
|
if (typeof this.getAttribute("content") === "string") {
|
|
@@ -2276,7 +2301,7 @@ class FigTab extends HTMLElement {
|
|
|
2276
2301
|
this.setAttribute("selected", value ? "true" : "false");
|
|
2277
2302
|
}
|
|
2278
2303
|
disconnectedCallback() {
|
|
2279
|
-
this.removeEventListener("click", this
|
|
2304
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
2280
2305
|
}
|
|
2281
2306
|
handleClick() {
|
|
2282
2307
|
this.selected = true;
|
|
@@ -2305,8 +2330,12 @@ customElements.define("fig-tab", FigTab);
|
|
|
2305
2330
|
* @attr {string} name - Identifier for the tabs group
|
|
2306
2331
|
*/
|
|
2307
2332
|
class FigTabs extends HTMLElement {
|
|
2333
|
+
#boundHandleClick;
|
|
2334
|
+
#boundHandleKeyDown;
|
|
2308
2335
|
constructor() {
|
|
2309
2336
|
super();
|
|
2337
|
+
this.#boundHandleClick = this.handleClick.bind(this);
|
|
2338
|
+
this.#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
2310
2339
|
}
|
|
2311
2340
|
|
|
2312
2341
|
static get observedAttributes() {
|
|
@@ -2316,8 +2345,8 @@ class FigTabs extends HTMLElement {
|
|
|
2316
2345
|
connectedCallback() {
|
|
2317
2346
|
this.name = this.getAttribute("name") || "tabs";
|
|
2318
2347
|
this.setAttribute("role", "tablist");
|
|
2319
|
-
this.addEventListener("click", this
|
|
2320
|
-
this.addEventListener("keydown", this.#
|
|
2348
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
2349
|
+
this.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
2321
2350
|
requestAnimationFrame(() => {
|
|
2322
2351
|
const value = this.getAttribute("value");
|
|
2323
2352
|
if (value) {
|
|
@@ -2345,8 +2374,8 @@ class FigTabs extends HTMLElement {
|
|
|
2345
2374
|
}
|
|
2346
2375
|
|
|
2347
2376
|
disconnectedCallback() {
|
|
2348
|
-
this.removeEventListener("click", this
|
|
2349
|
-
this.removeEventListener("keydown", this.#
|
|
2377
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
2378
|
+
this.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
2350
2379
|
}
|
|
2351
2380
|
|
|
2352
2381
|
#handleKeyDown(event) {
|
|
@@ -2446,14 +2475,16 @@ customElements.define("fig-tabs", FigTabs);
|
|
|
2446
2475
|
class FigSegment extends HTMLElement {
|
|
2447
2476
|
#value;
|
|
2448
2477
|
#selected;
|
|
2478
|
+
#boundHandleClick;
|
|
2449
2479
|
constructor() {
|
|
2450
2480
|
super();
|
|
2481
|
+
this.#boundHandleClick = this.handleClick.bind(this);
|
|
2451
2482
|
}
|
|
2452
2483
|
connectedCallback() {
|
|
2453
|
-
this.addEventListener("click", this
|
|
2484
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
2454
2485
|
}
|
|
2455
2486
|
disconnectedCallback() {
|
|
2456
|
-
this.removeEventListener("click", this
|
|
2487
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
2457
2488
|
}
|
|
2458
2489
|
handleClick() {
|
|
2459
2490
|
const parentControl = this.closest("fig-segmented-control");
|
|
@@ -5084,6 +5115,12 @@ class FigInputFill extends HTMLElement {
|
|
|
5084
5115
|
this.#render();
|
|
5085
5116
|
}
|
|
5086
5117
|
|
|
5118
|
+
disconnectedCallback() {
|
|
5119
|
+
this.#fillPicker = null;
|
|
5120
|
+
this.#opacityInput = null;
|
|
5121
|
+
this.#hexInput = null;
|
|
5122
|
+
}
|
|
5123
|
+
|
|
5087
5124
|
#parseValue() {
|
|
5088
5125
|
const valueAttr = this.getAttribute("value");
|
|
5089
5126
|
if (!valueAttr) return;
|
|
@@ -6074,6 +6111,16 @@ class FigInputGradient extends HTMLElement {
|
|
|
6074
6111
|
|
|
6075
6112
|
disconnectedCallback() {
|
|
6076
6113
|
document.removeEventListener("keydown", this.#onKeyDown);
|
|
6114
|
+
if (this.#colorObserver) {
|
|
6115
|
+
this.#colorObserver.disconnect();
|
|
6116
|
+
this.#colorObserver = null;
|
|
6117
|
+
}
|
|
6118
|
+
clearTimeout(this.#arrowTooltipTimer);
|
|
6119
|
+
this.removeEventListener("pointerenter", this.#onTrackEnter);
|
|
6120
|
+
this.removeEventListener("pointermove", this.#onTrackMove);
|
|
6121
|
+
this.removeEventListener("pointerleave", this.#onTrackLeave);
|
|
6122
|
+
this.removeEventListener("click", this.#onTrackClick);
|
|
6123
|
+
this.removeEventListener("dblclick", this.#onTrackDblClick);
|
|
6077
6124
|
}
|
|
6078
6125
|
|
|
6079
6126
|
#onKeyDown = (e) => {
|
|
@@ -6208,10 +6255,8 @@ class FigInputGradient extends HTMLElement {
|
|
|
6208
6255
|
}
|
|
6209
6256
|
|
|
6210
6257
|
#sampleGradientColor(position) {
|
|
6211
|
-
const
|
|
6212
|
-
|
|
6213
|
-
canvas.height = 1;
|
|
6214
|
-
const ctx = canvas.getContext("2d");
|
|
6258
|
+
const { ctx } = figGetSharedCanvas(256, 1);
|
|
6259
|
+
ctx.clearRect(0, 0, 256, 1);
|
|
6215
6260
|
const grad = ctx.createLinearGradient(0, 0, 256, 0);
|
|
6216
6261
|
for (const stop of this.#gradient.stops) {
|
|
6217
6262
|
try {
|
|
@@ -6450,6 +6495,44 @@ class FigInputGradient extends HTMLElement {
|
|
|
6450
6495
|
#setupEventListeners() {
|
|
6451
6496
|
if (!this.#track) return;
|
|
6452
6497
|
|
|
6498
|
+
this.#track.addEventListener("pointerdown", (e) => {
|
|
6499
|
+
if (this.hasAttribute("disabled")) return;
|
|
6500
|
+
if (e.target.closest("fig-handle:not(.fig-input-gradient-ghost)")) return;
|
|
6501
|
+
if (e.button !== 0) return;
|
|
6502
|
+
e.preventDefault();
|
|
6503
|
+
|
|
6504
|
+
const trackRect = this.#track.getBoundingClientRect();
|
|
6505
|
+
const pct = Math.max(0, Math.min(1, (e.clientX - trackRect.left) / trackRect.width));
|
|
6506
|
+
const position = Math.round(pct * 100);
|
|
6507
|
+
const color = this.#sampleGradientColor(pct);
|
|
6508
|
+
this.#gradient.stops.push({ position, color, opacity: 100 });
|
|
6509
|
+
this.#gradient.stops.sort((a, b) => a.position - b.position);
|
|
6510
|
+
const newIndex = this.#gradient.stops.findIndex(
|
|
6511
|
+
(s) => s.position === position && s.color === color,
|
|
6512
|
+
);
|
|
6513
|
+
this.#syncHandles();
|
|
6514
|
+
this.#syncChit();
|
|
6515
|
+
this.#emitInput();
|
|
6516
|
+
this.#hideGhost();
|
|
6517
|
+
|
|
6518
|
+
const handles = this.#track.querySelectorAll(
|
|
6519
|
+
"fig-handle:not(.fig-input-gradient-ghost)",
|
|
6520
|
+
);
|
|
6521
|
+
const newHandle = handles[newIndex];
|
|
6522
|
+
if (newHandle) {
|
|
6523
|
+
newHandle.select();
|
|
6524
|
+
newHandle.dispatchEvent(new PointerEvent("pointerdown", {
|
|
6525
|
+
bubbles: true,
|
|
6526
|
+
clientX: e.clientX,
|
|
6527
|
+
clientY: e.clientY,
|
|
6528
|
+
pointerId: e.pointerId,
|
|
6529
|
+
pointerType: e.pointerType,
|
|
6530
|
+
button: e.button,
|
|
6531
|
+
buttons: e.buttons,
|
|
6532
|
+
}));
|
|
6533
|
+
}
|
|
6534
|
+
});
|
|
6535
|
+
|
|
6453
6536
|
this.#track.addEventListener("input", (e) => {
|
|
6454
6537
|
const handle = e.target.closest("fig-handle");
|
|
6455
6538
|
if (!handle) return;
|
|
@@ -6851,9 +6934,11 @@ customElements.define("fig-switch", FigSwitch);
|
|
|
6851
6934
|
class FigToast extends HTMLDialogElement {
|
|
6852
6935
|
_defaultOffset = 16; // 1rem in pixels
|
|
6853
6936
|
_autoCloseTimer = null;
|
|
6937
|
+
#boundHandleClose;
|
|
6854
6938
|
|
|
6855
6939
|
constructor() {
|
|
6856
6940
|
super();
|
|
6941
|
+
this.#boundHandleClose = this.handleClose.bind(this);
|
|
6857
6942
|
}
|
|
6858
6943
|
|
|
6859
6944
|
getOffset() {
|
|
@@ -6903,8 +6988,8 @@ class FigToast extends HTMLDialogElement {
|
|
|
6903
6988
|
|
|
6904
6989
|
addCloseListeners() {
|
|
6905
6990
|
this.querySelectorAll("[close-toast]").forEach((button) => {
|
|
6906
|
-
button.removeEventListener("click", this
|
|
6907
|
-
button.addEventListener("click", this
|
|
6991
|
+
button.removeEventListener("click", this.#boundHandleClose);
|
|
6992
|
+
button.addEventListener("click", this.#boundHandleClose);
|
|
6908
6993
|
});
|
|
6909
6994
|
}
|
|
6910
6995
|
|
|
@@ -7191,12 +7276,10 @@ class FigChit extends HTMLElement {
|
|
|
7191
7276
|
}
|
|
7192
7277
|
|
|
7193
7278
|
#toHex(color) {
|
|
7194
|
-
// Convert color to hex for the native input
|
|
7195
7279
|
if (!color) return "#D9D9D9";
|
|
7196
7280
|
if (color.startsWith("#")) return color.slice(0, 7);
|
|
7197
|
-
// Use canvas to convert rgba/named colors to hex
|
|
7198
7281
|
try {
|
|
7199
|
-
const ctx =
|
|
7282
|
+
const { ctx } = figGetSharedCanvas(1, 1);
|
|
7200
7283
|
ctx.fillStyle = color;
|
|
7201
7284
|
return ctx.fillStyle;
|
|
7202
7285
|
} catch {
|
|
@@ -7665,7 +7748,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
7665
7748
|
|
|
7666
7749
|
connectedCallback() {
|
|
7667
7750
|
this.#precision = parseInt(this.getAttribute("precision") || "2");
|
|
7668
|
-
|
|
7751
|
+
figSyncCssVar(this, "--aspect-ratio", this.getAttribute("aspect-ratio"));
|
|
7669
7752
|
const val = this.getAttribute("value");
|
|
7670
7753
|
if (val) this.#parseValue(val);
|
|
7671
7754
|
this.#presetName = this.#matchPreset();
|
|
@@ -7681,17 +7764,9 @@ class FigEasingCurve extends HTMLElement {
|
|
|
7681
7764
|
}
|
|
7682
7765
|
}
|
|
7683
7766
|
|
|
7684
|
-
#syncAspectRatioVar(value) {
|
|
7685
|
-
if (value && value.trim()) {
|
|
7686
|
-
this.style.setProperty("--aspect-ratio", value.trim());
|
|
7687
|
-
} else {
|
|
7688
|
-
this.style.removeProperty("--aspect-ratio");
|
|
7689
|
-
}
|
|
7690
|
-
}
|
|
7691
|
-
|
|
7692
7767
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
7693
7768
|
if (name === "aspect-ratio") {
|
|
7694
|
-
this
|
|
7769
|
+
figSyncCssVar(this, "--aspect-ratio", newValue);
|
|
7695
7770
|
if (this.#svg) {
|
|
7696
7771
|
this.#syncViewportSize();
|
|
7697
7772
|
this.#updatePaths();
|
|
@@ -8486,12 +8561,9 @@ class Fig3DRotate extends HTMLElement {
|
|
|
8486
8561
|
|
|
8487
8562
|
connectedCallback() {
|
|
8488
8563
|
this.#precision = parseInt(this.getAttribute("precision") || "1");
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
this
|
|
8492
|
-
"--perspective-origin",
|
|
8493
|
-
this.getAttribute("perspective-origin"),
|
|
8494
|
-
);
|
|
8564
|
+
figSyncCssVar(this, "--aspect-ratio", this.getAttribute("aspect-ratio"));
|
|
8565
|
+
figSyncCssVar(this, "--perspective", this.getAttribute("perspective"));
|
|
8566
|
+
figSyncCssVar(this, "--perspective-origin", this.getAttribute("perspective-origin"));
|
|
8495
8567
|
this.#syncTransformOrigin(this.getAttribute("transform-origin"));
|
|
8496
8568
|
this.#parseFields(this.getAttribute("fields"));
|
|
8497
8569
|
const val = this.getAttribute("value");
|
|
@@ -8509,29 +8581,7 @@ class Fig3DRotate extends HTMLElement {
|
|
|
8509
8581
|
}
|
|
8510
8582
|
}
|
|
8511
8583
|
|
|
8512
|
-
|
|
8513
|
-
if (value && value.trim()) {
|
|
8514
|
-
this.style.setProperty("--aspect-ratio", value.trim());
|
|
8515
|
-
} else {
|
|
8516
|
-
this.style.removeProperty("--aspect-ratio");
|
|
8517
|
-
}
|
|
8518
|
-
}
|
|
8519
|
-
|
|
8520
|
-
#syncPerspectiveVar(value) {
|
|
8521
|
-
if (value && value.trim()) {
|
|
8522
|
-
this.style.setProperty("--perspective", value.trim());
|
|
8523
|
-
} else {
|
|
8524
|
-
this.style.removeProperty("--perspective");
|
|
8525
|
-
}
|
|
8526
|
-
}
|
|
8527
|
-
|
|
8528
|
-
#syncCSSVar(prop, value) {
|
|
8529
|
-
if (value && value.trim()) {
|
|
8530
|
-
this.style.setProperty(prop, value.trim());
|
|
8531
|
-
} else {
|
|
8532
|
-
this.style.removeProperty(prop);
|
|
8533
|
-
}
|
|
8534
|
-
}
|
|
8584
|
+
|
|
8535
8585
|
|
|
8536
8586
|
#syncTransformOrigin(value) {
|
|
8537
8587
|
if (!value || !value.trim()) {
|
|
@@ -8580,15 +8630,15 @@ class Fig3DRotate extends HTMLElement {
|
|
|
8580
8630
|
|
|
8581
8631
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
8582
8632
|
if (name === "aspect-ratio") {
|
|
8583
|
-
this
|
|
8633
|
+
figSyncCssVar(this, "--aspect-ratio", newValue);
|
|
8584
8634
|
return;
|
|
8585
8635
|
}
|
|
8586
8636
|
if (name === "perspective") {
|
|
8587
|
-
this
|
|
8637
|
+
figSyncCssVar(this, "--perspective", newValue);
|
|
8588
8638
|
return;
|
|
8589
8639
|
}
|
|
8590
8640
|
if (name === "perspective-origin") {
|
|
8591
|
-
this
|
|
8641
|
+
figSyncCssVar(this, "--perspective-origin", newValue);
|
|
8592
8642
|
return;
|
|
8593
8643
|
}
|
|
8594
8644
|
if (name === "transform-origin") {
|
|
@@ -8838,7 +8888,7 @@ class FigOriginGrid extends HTMLElement {
|
|
|
8838
8888
|
|
|
8839
8889
|
connectedCallback() {
|
|
8840
8890
|
this.#precision = parseInt(this.getAttribute("precision") || "0");
|
|
8841
|
-
|
|
8891
|
+
figSyncCssVar(this, "--aspect-ratio", this.getAttribute("aspect-ratio"));
|
|
8842
8892
|
this.#applyIncomingValue(this.getAttribute("value"));
|
|
8843
8893
|
|
|
8844
8894
|
this.#render();
|
|
@@ -8862,7 +8912,7 @@ class FigOriginGrid extends HTMLElement {
|
|
|
8862
8912
|
|
|
8863
8913
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
8864
8914
|
if (name === "aspect-ratio") {
|
|
8865
|
-
this
|
|
8915
|
+
figSyncCssVar(this, "--aspect-ratio", newValue);
|
|
8866
8916
|
return;
|
|
8867
8917
|
}
|
|
8868
8918
|
if (name === "drag") {
|
|
@@ -8901,14 +8951,6 @@ class FigOriginGrid extends HTMLElement {
|
|
|
8901
8951
|
return attr.toLowerCase() !== "false";
|
|
8902
8952
|
}
|
|
8903
8953
|
|
|
8904
|
-
#syncAspectRatioVar(value) {
|
|
8905
|
-
if (value && value.trim()) {
|
|
8906
|
-
this.style.setProperty("--aspect-ratio", value.trim());
|
|
8907
|
-
} else {
|
|
8908
|
-
this.style.removeProperty("--aspect-ratio");
|
|
8909
|
-
}
|
|
8910
|
-
}
|
|
8911
|
-
|
|
8912
8954
|
#syncDragState() {
|
|
8913
8955
|
if (!this.#grid) return;
|
|
8914
8956
|
this.#grid.classList.toggle("drag-disabled", !this.#dragEnabled);
|
|
@@ -9292,6 +9334,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9292
9334
|
#boundYFocusOut = null;
|
|
9293
9335
|
#isSyncingValueAttr = false;
|
|
9294
9336
|
#defaultPosition = { x: 0.5, y: 0.5 };
|
|
9337
|
+
#initialized = false;
|
|
9295
9338
|
|
|
9296
9339
|
constructor() {
|
|
9297
9340
|
super();
|
|
@@ -9303,7 +9346,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9303
9346
|
this.xInput = null;
|
|
9304
9347
|
this.yInput = null;
|
|
9305
9348
|
this.coordinates = "screen";
|
|
9306
|
-
this.#initialized = false;
|
|
9307
9349
|
this.#boundPlanePointerDown = (e) => this.#handlePlanePointerDown(e);
|
|
9308
9350
|
this.#boundHandlePointerDown = () => {
|
|
9309
9351
|
this.isDragging = true;
|
|
@@ -9317,8 +9359,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9317
9359
|
this.#boundYFocusOut = () => this.#handleFieldFocusOut();
|
|
9318
9360
|
}
|
|
9319
9361
|
|
|
9320
|
-
#initialized = false;
|
|
9321
|
-
|
|
9322
9362
|
connectedCallback() {
|
|
9323
9363
|
// Initialize position
|
|
9324
9364
|
requestAnimationFrame(() => {
|
|
@@ -9327,7 +9367,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9327
9367
|
this.transform = this.getAttribute("transform") || 1;
|
|
9328
9368
|
this.transform = Number(this.transform);
|
|
9329
9369
|
this.coordinates = this.getAttribute("coordinates") || "screen";
|
|
9330
|
-
|
|
9370
|
+
figSyncCssVar(this, "--aspect-ratio", this.getAttribute("aspect-ratio"));
|
|
9331
9371
|
if (!this.hasAttribute("value")) {
|
|
9332
9372
|
this.setAttribute("value", "50% 50%");
|
|
9333
9373
|
}
|
|
@@ -9346,14 +9386,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9346
9386
|
return this.coordinates === "math" ? 1 - y : y;
|
|
9347
9387
|
}
|
|
9348
9388
|
|
|
9349
|
-
#syncAspectRatioVar(value) {
|
|
9350
|
-
if (value && value.trim()) {
|
|
9351
|
-
this.style.setProperty("--aspect-ratio", value.trim());
|
|
9352
|
-
} else {
|
|
9353
|
-
this.style.removeProperty("--aspect-ratio");
|
|
9354
|
-
}
|
|
9355
|
-
}
|
|
9356
|
-
|
|
9357
9389
|
disconnectedCallback() {
|
|
9358
9390
|
this.#cleanupListeners();
|
|
9359
9391
|
}
|
|
@@ -9655,7 +9687,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
9655
9687
|
}
|
|
9656
9688
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
9657
9689
|
if (name === "aspect-ratio") {
|
|
9658
|
-
this
|
|
9690
|
+
figSyncCssVar(this, "--aspect-ratio", newValue);
|
|
9659
9691
|
return;
|
|
9660
9692
|
}
|
|
9661
9693
|
if (name === "value") {
|
|
@@ -9705,6 +9737,11 @@ class FigInputAngle extends HTMLElement {
|
|
|
9705
9737
|
#opposite;
|
|
9706
9738
|
#prevRawAngle = null;
|
|
9707
9739
|
#boundHandleRawChange;
|
|
9740
|
+
#boundHandleMouseDown;
|
|
9741
|
+
#boundHandleTouchStart;
|
|
9742
|
+
#boundHandleKeyDown;
|
|
9743
|
+
#boundHandleKeyUp;
|
|
9744
|
+
#boundHandleAngleInput;
|
|
9708
9745
|
|
|
9709
9746
|
constructor() {
|
|
9710
9747
|
super();
|
|
@@ -9725,6 +9762,11 @@ class FigInputAngle extends HTMLElement {
|
|
|
9725
9762
|
this.rotationSpan = null;
|
|
9726
9763
|
|
|
9727
9764
|
this.#boundHandleRawChange = this.#handleRawChange.bind(this);
|
|
9765
|
+
this.#boundHandleMouseDown = this.#handleMouseDown.bind(this);
|
|
9766
|
+
this.#boundHandleTouchStart = this.#handleTouchStart.bind(this);
|
|
9767
|
+
this.#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
9768
|
+
this.#boundHandleKeyUp = this.#handleKeyUp.bind(this);
|
|
9769
|
+
this.#boundHandleAngleInput = this.#handleAngleInput.bind(this);
|
|
9728
9770
|
}
|
|
9729
9771
|
|
|
9730
9772
|
connectedCallback() {
|
|
@@ -9899,30 +9941,23 @@ class FigInputAngle extends HTMLElement {
|
|
|
9899
9941
|
this.angleInput = this.querySelector("fig-input-number[name='angle']");
|
|
9900
9942
|
this.rotationSpan = this.querySelector(".fig-input-angle-rotations");
|
|
9901
9943
|
this.#updateRotationDisplay();
|
|
9902
|
-
this.plane?.addEventListener("mousedown", this.#
|
|
9903
|
-
this.plane?.addEventListener(
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
);
|
|
9907
|
-
window.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
9908
|
-
window.addEventListener("keyup", this.#handleKeyUp.bind(this));
|
|
9944
|
+
this.plane?.addEventListener("mousedown", this.#boundHandleMouseDown);
|
|
9945
|
+
this.plane?.addEventListener("touchstart", this.#boundHandleTouchStart);
|
|
9946
|
+
window.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
9947
|
+
window.addEventListener("keyup", this.#boundHandleKeyUp);
|
|
9909
9948
|
if (this.text && this.angleInput) {
|
|
9910
|
-
this.angleInput.addEventListener(
|
|
9911
|
-
"input",
|
|
9912
|
-
this.#handleAngleInput.bind(this),
|
|
9913
|
-
);
|
|
9949
|
+
this.angleInput.addEventListener("input", this.#boundHandleAngleInput);
|
|
9914
9950
|
}
|
|
9915
|
-
// Capture-phase listener for unit suffix parsing
|
|
9916
9951
|
this.addEventListener("change", this.#boundHandleRawChange, true);
|
|
9917
9952
|
}
|
|
9918
9953
|
|
|
9919
9954
|
#cleanupListeners() {
|
|
9920
|
-
this.plane?.removeEventListener("mousedown", this.#
|
|
9921
|
-
this.plane?.removeEventListener("touchstart", this.#
|
|
9922
|
-
window.removeEventListener("keydown", this.#
|
|
9923
|
-
window.removeEventListener("keyup", this.#
|
|
9955
|
+
this.plane?.removeEventListener("mousedown", this.#boundHandleMouseDown);
|
|
9956
|
+
this.plane?.removeEventListener("touchstart", this.#boundHandleTouchStart);
|
|
9957
|
+
window.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
9958
|
+
window.removeEventListener("keyup", this.#boundHandleKeyUp);
|
|
9924
9959
|
if (this.text && this.angleInput) {
|
|
9925
|
-
this.angleInput.removeEventListener("input", this.#
|
|
9960
|
+
this.angleInput.removeEventListener("input", this.#boundHandleAngleInput);
|
|
9926
9961
|
}
|
|
9927
9962
|
this.removeEventListener("change", this.#boundHandleRawChange, true);
|
|
9928
9963
|
}
|
|
@@ -10427,6 +10462,8 @@ class FigFillPicker extends HTMLElement {
|
|
|
10427
10462
|
#opacitySlider = null;
|
|
10428
10463
|
#isDraggingColor = false;
|
|
10429
10464
|
#teardownColorAreaEvents = null;
|
|
10465
|
+
#dialogOpenObserver = null;
|
|
10466
|
+
#webcamTabObserver = null;
|
|
10430
10467
|
|
|
10431
10468
|
constructor() {
|
|
10432
10469
|
super();
|
|
@@ -10452,10 +10489,26 @@ class FigFillPicker extends HTMLElement {
|
|
|
10452
10489
|
this.#teardownColorAreaEvents();
|
|
10453
10490
|
this.#teardownColorAreaEvents = null;
|
|
10454
10491
|
}
|
|
10492
|
+
if (this.#dialogOpenObserver) {
|
|
10493
|
+
this.#dialogOpenObserver.disconnect();
|
|
10494
|
+
this.#dialogOpenObserver = null;
|
|
10495
|
+
}
|
|
10496
|
+
if (this.#webcamTabObserver) {
|
|
10497
|
+
this.#webcamTabObserver.disconnect();
|
|
10498
|
+
this.#webcamTabObserver = null;
|
|
10499
|
+
}
|
|
10500
|
+
if (this.#webcam.stream) {
|
|
10501
|
+
this.#webcam.stream.getTracks().forEach((track) => track.stop());
|
|
10502
|
+
this.#webcam.stream = null;
|
|
10503
|
+
}
|
|
10504
|
+
if (this.#video.url && this.#video.url.startsWith("blob:")) {
|
|
10505
|
+
URL.revokeObjectURL(this.#video.url);
|
|
10506
|
+
}
|
|
10455
10507
|
if (this.#chit) this.#chit.removeAttribute("selected");
|
|
10456
10508
|
if (this.#dialog) {
|
|
10457
10509
|
this.#dialog.close();
|
|
10458
10510
|
this.#dialog.remove();
|
|
10511
|
+
this.#dialog = null;
|
|
10459
10512
|
}
|
|
10460
10513
|
}
|
|
10461
10514
|
|
|
@@ -10792,11 +10845,11 @@ class FigFillPicker extends HTMLElement {
|
|
|
10792
10845
|
};
|
|
10793
10846
|
this.#dialog.addEventListener("close", onDialogClose);
|
|
10794
10847
|
|
|
10795
|
-
|
|
10848
|
+
this.#dialogOpenObserver = new MutationObserver(() => {
|
|
10796
10849
|
const isOpen = this.#dialog.hasAttribute("open") && this.#dialog.getAttribute("open") !== "false";
|
|
10797
10850
|
if (!isOpen) onDialogClose();
|
|
10798
10851
|
});
|
|
10799
|
-
|
|
10852
|
+
this.#dialogOpenObserver.observe(this.#dialog, { attributes: true, attributeFilter: ["open"] });
|
|
10800
10853
|
|
|
10801
10854
|
// Initialize built-in tabs (skip any overridden by custom slots)
|
|
10802
10855
|
const builtinInits = {
|
|
@@ -11718,10 +11771,15 @@ class FigFillPicker extends HTMLElement {
|
|
|
11718
11771
|
}
|
|
11719
11772
|
}
|
|
11720
11773
|
|
|
11774
|
+
static #gradientSupportCache = new Map();
|
|
11721
11775
|
#testGradientSupport(css) {
|
|
11776
|
+
const cached = FigFillPicker.#gradientSupportCache.get(css);
|
|
11777
|
+
if (cached !== undefined) return cached;
|
|
11722
11778
|
const el = document.createElement("div");
|
|
11723
11779
|
el.style.background = css;
|
|
11724
|
-
|
|
11780
|
+
const result = !!el.style.background;
|
|
11781
|
+
FigFillPicker.#gradientSupportCache.set(css, result);
|
|
11782
|
+
return result;
|
|
11725
11783
|
}
|
|
11726
11784
|
|
|
11727
11785
|
#getGradientCSS() {
|
|
@@ -12066,13 +12124,12 @@ class FigFillPicker extends HTMLElement {
|
|
|
12066
12124
|
}
|
|
12067
12125
|
};
|
|
12068
12126
|
|
|
12069
|
-
|
|
12070
|
-
const observer = new MutationObserver(() => {
|
|
12127
|
+
this.#webcamTabObserver = new MutationObserver(() => {
|
|
12071
12128
|
if (container.style.display !== "none" && !this.#webcam.stream) {
|
|
12072
12129
|
startWebcam();
|
|
12073
12130
|
}
|
|
12074
12131
|
});
|
|
12075
|
-
|
|
12132
|
+
this.#webcamTabObserver.observe(container, {
|
|
12076
12133
|
attributes: true,
|
|
12077
12134
|
attributeFilter: ["style"],
|
|
12078
12135
|
});
|
|
@@ -12591,8 +12648,8 @@ class FigColorTip extends HTMLElement {
|
|
|
12591
12648
|
}
|
|
12592
12649
|
|
|
12593
12650
|
try {
|
|
12594
|
-
const ctx =
|
|
12595
|
-
|
|
12651
|
+
const { ctx } = figGetSharedCanvas(1, 1);
|
|
12652
|
+
ctx.fillStyle = "#000000";
|
|
12596
12653
|
ctx.fillStyle = value;
|
|
12597
12654
|
const resolved = ctx.fillStyle;
|
|
12598
12655
|
if (resolved.startsWith("#")) {
|
|
@@ -13547,12 +13604,13 @@ class FigHandle extends HTMLElement {
|
|
|
13547
13604
|
|
|
13548
13605
|
this.#isDragging = true;
|
|
13549
13606
|
const axes = this.#axes;
|
|
13550
|
-
const containerRect = container.getBoundingClientRect();
|
|
13551
13607
|
const handleW = this.offsetWidth;
|
|
13552
13608
|
const handleH = this.offsetHeight;
|
|
13609
|
+
let lastRect = null;
|
|
13553
13610
|
|
|
13554
13611
|
const clampAndApply = (clientX, clientY, shiftKey = false) => {
|
|
13555
13612
|
const rect = container.getBoundingClientRect();
|
|
13613
|
+
lastRect = rect;
|
|
13556
13614
|
const currentLeft = parseFloat(this.style.left) || 0;
|
|
13557
13615
|
const currentTop = parseFloat(this.style.top) || 0;
|
|
13558
13616
|
const rawX = clientX - rect.left - handleW / 2;
|
|
@@ -13609,7 +13667,7 @@ class FigHandle extends HTMLElement {
|
|
|
13609
13667
|
new CustomEvent("input", {
|
|
13610
13668
|
bubbles: true,
|
|
13611
13669
|
detail: {
|
|
13612
|
-
...this.#positionDetail(
|
|
13670
|
+
...this.#positionDetail(lastRect),
|
|
13613
13671
|
shiftKey: e.shiftKey,
|
|
13614
13672
|
},
|
|
13615
13673
|
}),
|
|
@@ -13628,7 +13686,7 @@ class FigHandle extends HTMLElement {
|
|
|
13628
13686
|
this.dispatchEvent(
|
|
13629
13687
|
new CustomEvent("change", {
|
|
13630
13688
|
bubbles: true,
|
|
13631
|
-
detail: this.#positionDetail(
|
|
13689
|
+
detail: this.#positionDetail(lastRect),
|
|
13632
13690
|
}),
|
|
13633
13691
|
);
|
|
13634
13692
|
const swallowClick = (evt) => {
|