@rogieking/figui3 3.16.0 → 3.18.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 +763 -642
- package/components.css +106 -27
- package/dist/base.css +1 -99
- package/dist/components.css +1 -3978
- package/dist/fig.css +1 -2
- package/dist/fig.js +83 -81
- package/fig.js +918 -153
- 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("#")) {
|
|
@@ -13286,6 +13343,640 @@ class FigChooser extends HTMLElement {
|
|
|
13286
13343
|
}
|
|
13287
13344
|
customElements.define("fig-chooser", FigChooser);
|
|
13288
13345
|
|
|
13346
|
+
/* Canvas Point */
|
|
13347
|
+
class FigCanvasPoint extends HTMLElement {
|
|
13348
|
+
static observedAttributes = [
|
|
13349
|
+
"type",
|
|
13350
|
+
"value",
|
|
13351
|
+
"color",
|
|
13352
|
+
"name",
|
|
13353
|
+
"tooltips",
|
|
13354
|
+
"disabled",
|
|
13355
|
+
"drag-surface",
|
|
13356
|
+
"snapping",
|
|
13357
|
+
];
|
|
13358
|
+
|
|
13359
|
+
#x = 50;
|
|
13360
|
+
#y = 50;
|
|
13361
|
+
#radius = 0;
|
|
13362
|
+
#radiusIsPercent = false;
|
|
13363
|
+
#angle = 0;
|
|
13364
|
+
#pointHandle = null;
|
|
13365
|
+
#angleHandle = null;
|
|
13366
|
+
#radiusSvg = null;
|
|
13367
|
+
#angleSvg = null;
|
|
13368
|
+
#pointTooltip = null;
|
|
13369
|
+
#radiusTooltip = null;
|
|
13370
|
+
#angleTooltip = null;
|
|
13371
|
+
#isDragging = false;
|
|
13372
|
+
#isRadiusDragging = false;
|
|
13373
|
+
#isAngleDragging = false;
|
|
13374
|
+
|
|
13375
|
+
get #type() {
|
|
13376
|
+
return this.getAttribute("type") || "point";
|
|
13377
|
+
}
|
|
13378
|
+
|
|
13379
|
+
get #hasRadius() {
|
|
13380
|
+
return this.#type === "point-radius" || this.#type === "point-radius-angle";
|
|
13381
|
+
}
|
|
13382
|
+
|
|
13383
|
+
get #hasAngle() {
|
|
13384
|
+
return this.#type === "point-radius-angle";
|
|
13385
|
+
}
|
|
13386
|
+
|
|
13387
|
+
get #tooltipsEnabled() {
|
|
13388
|
+
const v = this.getAttribute("tooltips");
|
|
13389
|
+
return v === null || v !== "false";
|
|
13390
|
+
}
|
|
13391
|
+
|
|
13392
|
+
get #snappingMode() {
|
|
13393
|
+
const raw = this.getAttribute("snapping");
|
|
13394
|
+
if (raw === null) return "false";
|
|
13395
|
+
const n = raw.trim().toLowerCase();
|
|
13396
|
+
if (n === "modifier") return "modifier";
|
|
13397
|
+
if (n === "" || n === "true") return "true";
|
|
13398
|
+
return "false";
|
|
13399
|
+
}
|
|
13400
|
+
|
|
13401
|
+
#shouldSnap(shiftKey) {
|
|
13402
|
+
const mode = this.#snappingMode;
|
|
13403
|
+
if (mode === "true") return true;
|
|
13404
|
+
if (mode === "modifier") return !!shiftKey;
|
|
13405
|
+
return false;
|
|
13406
|
+
}
|
|
13407
|
+
|
|
13408
|
+
get #pointTipText() {
|
|
13409
|
+
const name = this.getAttribute("name");
|
|
13410
|
+
if (name) return name;
|
|
13411
|
+
return `${Math.round(this.#x)}%, ${Math.round(this.#y)}%`;
|
|
13412
|
+
}
|
|
13413
|
+
|
|
13414
|
+
get #dragSurface() {
|
|
13415
|
+
return this.getAttribute("drag-surface") || "parent";
|
|
13416
|
+
}
|
|
13417
|
+
|
|
13418
|
+
get #container() {
|
|
13419
|
+
const surface = this.#dragSurface;
|
|
13420
|
+
if (surface === "parent") return this.parentElement;
|
|
13421
|
+
return this.closest(surface);
|
|
13422
|
+
}
|
|
13423
|
+
|
|
13424
|
+
get #handleDragSurface() {
|
|
13425
|
+
const surface = this.#dragSurface;
|
|
13426
|
+
if (surface === "parent") {
|
|
13427
|
+
const container = this.parentElement;
|
|
13428
|
+
if (container) {
|
|
13429
|
+
container.setAttribute("data-fig-canvas-point-surface", "");
|
|
13430
|
+
return "[data-fig-canvas-point-surface]";
|
|
13431
|
+
}
|
|
13432
|
+
}
|
|
13433
|
+
return surface;
|
|
13434
|
+
}
|
|
13435
|
+
|
|
13436
|
+
#resolveRadius(containerWidth) {
|
|
13437
|
+
if (this.#radiusIsPercent) return (this.#radius / 100) * containerWidth;
|
|
13438
|
+
return this.#radius;
|
|
13439
|
+
}
|
|
13440
|
+
|
|
13441
|
+
#formatRadius() {
|
|
13442
|
+
if (this.#radiusIsPercent) return `${Math.round(this.#radius)}%`;
|
|
13443
|
+
return `${Math.round(this.#radius)}px`;
|
|
13444
|
+
}
|
|
13445
|
+
|
|
13446
|
+
connectedCallback() {
|
|
13447
|
+
this.#parseValue();
|
|
13448
|
+
this.#render();
|
|
13449
|
+
}
|
|
13450
|
+
|
|
13451
|
+
disconnectedCallback() {
|
|
13452
|
+
this.#teardownRadiusDrag();
|
|
13453
|
+
}
|
|
13454
|
+
|
|
13455
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
13456
|
+
if (oldVal === newVal) return;
|
|
13457
|
+
if (name === "value" && !this.#isDragging && !this.#isRadiusDragging && !this.#isAngleDragging) {
|
|
13458
|
+
this.#parseValue();
|
|
13459
|
+
if (this.#pointHandle) this.#syncPositions();
|
|
13460
|
+
else this.#render();
|
|
13461
|
+
}
|
|
13462
|
+
if (name === "type") {
|
|
13463
|
+
this.#parseValue();
|
|
13464
|
+
this.#render();
|
|
13465
|
+
}
|
|
13466
|
+
if (name === "color" && this.#pointHandle) {
|
|
13467
|
+
if (newVal) this.#pointHandle.setAttribute("color", newVal);
|
|
13468
|
+
else this.#pointHandle.removeAttribute("color");
|
|
13469
|
+
}
|
|
13470
|
+
if (name === "disabled") {
|
|
13471
|
+
this.#render();
|
|
13472
|
+
}
|
|
13473
|
+
if (name === "tooltips") {
|
|
13474
|
+
this.#render();
|
|
13475
|
+
}
|
|
13476
|
+
if (name === "snapping" && this.#pointHandle) {
|
|
13477
|
+
this.#pointHandle.setAttribute("drag-snapping", newVal || "false");
|
|
13478
|
+
}
|
|
13479
|
+
if (name === "name" && this.#pointTooltip) {
|
|
13480
|
+
this.#pointTooltip.setAttribute("text", this.#pointTipText);
|
|
13481
|
+
}
|
|
13482
|
+
}
|
|
13483
|
+
|
|
13484
|
+
#parseValue() {
|
|
13485
|
+
const raw = this.getAttribute("value");
|
|
13486
|
+
if (!raw) return;
|
|
13487
|
+
try {
|
|
13488
|
+
const v = JSON.parse(raw);
|
|
13489
|
+
if (typeof v.x === "number") this.#x = v.x;
|
|
13490
|
+
if (typeof v.y === "number") this.#y = v.y;
|
|
13491
|
+
if (v.radius !== undefined) {
|
|
13492
|
+
const rs = String(v.radius);
|
|
13493
|
+
if (rs.endsWith("%")) {
|
|
13494
|
+
this.#radiusIsPercent = true;
|
|
13495
|
+
this.#radius = parseFloat(rs);
|
|
13496
|
+
} else {
|
|
13497
|
+
this.#radiusIsPercent = false;
|
|
13498
|
+
this.#radius = parseFloat(rs);
|
|
13499
|
+
}
|
|
13500
|
+
if (!Number.isFinite(this.#radius)) this.#radius = 0;
|
|
13501
|
+
}
|
|
13502
|
+
if (typeof v.angle === "number") this.#angle = v.angle;
|
|
13503
|
+
} catch { /* ignore */ }
|
|
13504
|
+
}
|
|
13505
|
+
|
|
13506
|
+
get value() {
|
|
13507
|
+
const v = { x: this.#x, y: this.#y };
|
|
13508
|
+
if (this.#hasRadius) {
|
|
13509
|
+
v.radius = this.#radiusIsPercent ? `${this.#radius}%` : this.#radius;
|
|
13510
|
+
}
|
|
13511
|
+
if (this.#hasAngle) v.angle = this.#angle;
|
|
13512
|
+
return v;
|
|
13513
|
+
}
|
|
13514
|
+
|
|
13515
|
+
set value(val) {
|
|
13516
|
+
if (typeof val === "object") {
|
|
13517
|
+
this.setAttribute("value", JSON.stringify(val));
|
|
13518
|
+
} else if (typeof val === "string") {
|
|
13519
|
+
this.setAttribute("value", val);
|
|
13520
|
+
}
|
|
13521
|
+
}
|
|
13522
|
+
|
|
13523
|
+
#render() {
|
|
13524
|
+
this.innerHTML = "";
|
|
13525
|
+
this.#pointHandle = null;
|
|
13526
|
+
this.#angleHandle = null;
|
|
13527
|
+
this.#radiusSvg = null;
|
|
13528
|
+
this.#angleSvg = null;
|
|
13529
|
+
this.#pointTooltip = null;
|
|
13530
|
+
this.#radiusTooltip = null;
|
|
13531
|
+
this.#angleTooltip = null;
|
|
13532
|
+
|
|
13533
|
+
const disabled = this.hasAttribute("disabled");
|
|
13534
|
+
const type = this.#type;
|
|
13535
|
+
const tooltips = this.#tooltipsEnabled;
|
|
13536
|
+
|
|
13537
|
+
const handleSurface = this.#handleDragSurface;
|
|
13538
|
+
|
|
13539
|
+
const handle = document.createElement("fig-handle");
|
|
13540
|
+
handle.setAttribute("drag", "true");
|
|
13541
|
+
handle.setAttribute("drag-surface", handleSurface);
|
|
13542
|
+
handle.setAttribute("drag-axes", "x,y");
|
|
13543
|
+
handle.setAttribute("drag-snapping", this.#snappingMode);
|
|
13544
|
+
handle.setAttribute("value", `${this.#x}% ${this.#y}%`);
|
|
13545
|
+
if (disabled) handle.setAttribute("disabled", "");
|
|
13546
|
+
if (type === "color") {
|
|
13547
|
+
handle.setAttribute("type", "color");
|
|
13548
|
+
const color = this.getAttribute("color");
|
|
13549
|
+
if (color) handle.setAttribute("color", color);
|
|
13550
|
+
}
|
|
13551
|
+
this.#pointHandle = handle;
|
|
13552
|
+
|
|
13553
|
+
if (this.#hasRadius) {
|
|
13554
|
+
this.#createRadiusSvg();
|
|
13555
|
+
}
|
|
13556
|
+
|
|
13557
|
+
if (this.#hasAngle) {
|
|
13558
|
+
this.#createAngleSvg();
|
|
13559
|
+
}
|
|
13560
|
+
|
|
13561
|
+
if (tooltips) {
|
|
13562
|
+
const tip = document.createElement("fig-tooltip");
|
|
13563
|
+
tip.setAttribute("action", "manual");
|
|
13564
|
+
tip.setAttribute("text", this.#pointTipText);
|
|
13565
|
+
tip.appendChild(handle);
|
|
13566
|
+
this.appendChild(tip);
|
|
13567
|
+
this.#pointTooltip = tip;
|
|
13568
|
+
} else {
|
|
13569
|
+
this.appendChild(handle);
|
|
13570
|
+
}
|
|
13571
|
+
|
|
13572
|
+
if (this.#hasAngle) {
|
|
13573
|
+
this.#createAngleHandle(disabled, tooltips, handleSurface);
|
|
13574
|
+
}
|
|
13575
|
+
|
|
13576
|
+
this.#setupEventListeners();
|
|
13577
|
+
requestAnimationFrame(() => this.#syncPositions());
|
|
13578
|
+
}
|
|
13579
|
+
|
|
13580
|
+
#createRadiusSvg() {
|
|
13581
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
13582
|
+
const svg = document.createElementNS(ns, "svg");
|
|
13583
|
+
svg.classList.add("fig-canvas-point-radius");
|
|
13584
|
+
svg.setAttribute("overflow", "visible");
|
|
13585
|
+
const hitCircle = document.createElementNS(ns, "circle");
|
|
13586
|
+
hitCircle.classList.add("fig-canvas-point-radius-hit");
|
|
13587
|
+
svg.appendChild(hitCircle);
|
|
13588
|
+
const circle = document.createElementNS(ns, "circle");
|
|
13589
|
+
svg.appendChild(circle);
|
|
13590
|
+
this.#radiusSvg = svg;
|
|
13591
|
+
|
|
13592
|
+
if (this.#tooltipsEnabled) {
|
|
13593
|
+
const tip = document.createElement("fig-tooltip");
|
|
13594
|
+
tip.setAttribute("action", "manual");
|
|
13595
|
+
tip.setAttribute("text", this.#formatRadius());
|
|
13596
|
+
tip.appendChild(svg);
|
|
13597
|
+
this.appendChild(tip);
|
|
13598
|
+
this.#radiusTooltip = tip;
|
|
13599
|
+
} else {
|
|
13600
|
+
this.appendChild(svg);
|
|
13601
|
+
}
|
|
13602
|
+
|
|
13603
|
+
this.#setupRadiusDrag(hitCircle);
|
|
13604
|
+
}
|
|
13605
|
+
|
|
13606
|
+
#createAngleSvg() {
|
|
13607
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
13608
|
+
const svg = document.createElementNS(ns, "svg");
|
|
13609
|
+
svg.classList.add("fig-canvas-point-angle-svg");
|
|
13610
|
+
svg.setAttribute("overflow", "visible");
|
|
13611
|
+
svg.style.position = "absolute";
|
|
13612
|
+
svg.style.pointerEvents = "none";
|
|
13613
|
+
const line = document.createElementNS(ns, "line");
|
|
13614
|
+
line.classList.add("fig-canvas-point-angle-line");
|
|
13615
|
+
svg.appendChild(line);
|
|
13616
|
+
this.#angleSvg = svg;
|
|
13617
|
+
this.appendChild(svg);
|
|
13618
|
+
}
|
|
13619
|
+
|
|
13620
|
+
#createAngleHandle(disabled, tooltips, handleSurface) {
|
|
13621
|
+
const handle = document.createElement("fig-handle");
|
|
13622
|
+
handle.setAttribute("drag", "true");
|
|
13623
|
+
handle.setAttribute("drag-surface", handleSurface);
|
|
13624
|
+
handle.setAttribute("drag-axes", "x,y");
|
|
13625
|
+
handle.setAttribute("size", "small");
|
|
13626
|
+
handle.setAttribute("hit-area", "12 circle");
|
|
13627
|
+
handle.setAttribute("hit-area-mode", "delegate");
|
|
13628
|
+
if (disabled) handle.setAttribute("disabled", "");
|
|
13629
|
+
this.#angleHandle = handle;
|
|
13630
|
+
|
|
13631
|
+
if (tooltips) {
|
|
13632
|
+
const tip = document.createElement("fig-tooltip");
|
|
13633
|
+
tip.setAttribute("action", "manual");
|
|
13634
|
+
tip.setAttribute("text", `${Math.round(this.#angle)}°`);
|
|
13635
|
+
tip.appendChild(handle);
|
|
13636
|
+
this.appendChild(tip);
|
|
13637
|
+
this.#angleTooltip = tip;
|
|
13638
|
+
} else {
|
|
13639
|
+
this.appendChild(handle);
|
|
13640
|
+
}
|
|
13641
|
+
}
|
|
13642
|
+
|
|
13643
|
+
#resizeCursorSvg(deg) {
|
|
13644
|
+
const r = Math.round(deg);
|
|
13645
|
+
return `url("data:image/svg+xml,%3Csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform='rotate(${r} 16 16)'%3E%3Cg filter='url(%23f)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.1212 16.9998L11.5607 17.4394C12.1465 18.0252 12.1464 18.975 11.5606 19.5607C10.9748 20.1465 10.0251 20.1465 9.4393 19.5606L6.4393 16.5604C5.85354 15.9746 5.85357 15.0249 6.43938 14.4391L9.43938 11.4393C10.0252 10.8535 10.9749 10.8536 11.5607 11.4394C12.1465 12.0252 12.1464 12.9749 11.5606 13.5607L11.1215 13.9998L20.8786 13.9999L20.4394 13.5607C19.8536 12.9749 19.8535 12.0252 20.4393 11.4394C21.0251 10.8536 21.9749 10.8536 22.5606 11.4394L25.5606 14.4393C25.842 14.7206 26 15.1021 26 15.4999C26 15.8978 25.842 16.2793 25.5607 16.5606L22.5607 19.5607C21.9749 20.1465 21.0251 20.1465 20.4393 19.5607C19.8536 18.9749 19.8535 18.0252 20.4393 17.4394L20.8788 16.9999L11.1212 16.9998Z' fill='white'/%3E%3C/g%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.8536 12.1465C11.0488 12.3417 11.0488 12.6583 10.8535 12.8536L8.70715 14.9998L23.2929 14.9999L21.1465 12.8536C20.9512 12.6583 20.9512 12.3417 21.1464 12.1465C21.3417 11.9512 21.6583 11.9512 21.8535 12.1465L24.8535 15.1464C24.9473 15.2402 25 15.3673 25 15.4999C25 15.6326 24.9473 15.7597 24.8536 15.8535L21.8536 18.8536C21.6583 19.0488 21.3417 19.0488 21.1465 18.8536C20.9512 18.6583 20.9512 18.3417 21.1464 18.1465L23.2929 15.9999L8.70705 15.9998L10.8536 18.1465C11.0488 18.3417 11.0488 18.6583 10.8535 18.8536C10.6583 19.0488 10.3417 19.0488 10.1464 18.8535L7.14643 15.8533C6.95118 15.658 6.95119 15.3415 7.14646 15.1462L10.1465 12.1464C10.3417 11.9512 10.6583 11.9512 10.8536 12.1465Z' fill='black'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='f' x='3' y='9' width='26' height='15' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='a'/%3E%3CfeColorMatrix in='SourceAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='b'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.35 0'/%3E%3CfeBlend in2='a' result='c'/%3E%3CfeBlend in='SourceGraphic' in2='c'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E") 16 16, nwse-resize`;
|
|
13646
|
+
}
|
|
13647
|
+
|
|
13648
|
+
#rotateCursorSvg(deg) {
|
|
13649
|
+
const r = Math.round(deg - 45);
|
|
13650
|
+
return `url("data:image/svg+xml,%3Csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform='rotate(${r} 16 16)'%3E%3Cg filter='url(%23f)'%3E%3Cpath d='M12.5607 22.4393L12.0216 21.9002C17.1558 21.2216 21.2216 17.1558 21.9002 12.0216L22.4393 12.5607C23.0251 13.1464 23.9749 13.1464 24.5607 12.5607C25.1464 11.9749 25.1464 11.0251 24.5607 10.4393L21.5607 7.43934C20.9749 6.85355 20.0251 6.85355 19.4393 7.43934L16.4393 10.4393C15.8536 11.0251 15.8536 11.9749 16.4393 12.5607C17.0251 13.1464 17.9749 13.1464 18.5607 12.5607L18.8056 12.3157C18.1013 15.5527 15.5527 18.1013 12.3157 18.8056L12.5607 18.5607C13.1464 17.9749 13.1464 17.0251 12.5607 16.4393C11.9749 15.8536 11.0251 15.8536 10.4393 16.4393L7.43934 19.4393C6.85356 20.0251 6.85356 20.9749 7.43934 21.5607L10.4393 24.5607C11.0251 25.1464 11.9749 25.1464 12.5607 24.5607C13.1464 23.9749 13.1464 23.0251 12.5607 22.4393Z' fill='white'/%3E%3C/g%3E%3Cpath d='M23.8536 11.8536C23.6583 12.0488 23.3417 12.0488 23.1464 11.8536L21 9.70711V10.5C21 16.299 16.299 21 10.5 21H9.70711L11.8536 23.1464C12.0488 23.3417 12.0488 23.6583 11.8536 23.8536C11.6583 24.0488 11.3417 24.0488 11.1464 23.8536L8.14645 20.8536C7.95119 20.6583 7.95119 20.3417 8.14645 20.1464L11.1464 17.1464C11.3417 16.9512 11.6583 16.9512 11.8536 17.1464C12.0488 17.3417 12.0488 17.6583 11.8536 17.8536L9.70711 20H10.5C15.7467 20 20 15.7467 20 10.5V9.70711L17.8536 11.8536C17.6583 12.0488 17.3417 12.0488 17.1464 11.8536C16.9512 11.6583 16.9512 11.3417 17.1464 11.1464L20.1464 8.14645C20.3417 7.95119 20.6583 7.95119 20.8536 8.14645L23.8536 11.1464C24.0488 11.3417 24.0488 11.6583 23.8536 11.8536Z' fill='black'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='f' x='4' y='5' width='24' height='24' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='a'/%3E%3CfeColorMatrix in='SourceAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='b'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.35 0'/%3E%3CfeBlend in2='a' result='c'/%3E%3CfeBlend in='SourceGraphic' in2='c'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E") 16 16, pointer`;
|
|
13651
|
+
}
|
|
13652
|
+
|
|
13653
|
+
#syncAngleCursor() {
|
|
13654
|
+
if (!this.#angleHandle || !this.#hasAngle) return;
|
|
13655
|
+
const hitArea = this.#angleHandle.querySelector(".fig-handle-hit-area");
|
|
13656
|
+
if (!hitArea) return;
|
|
13657
|
+
hitArea.style.cursor = this.#rotateCursorSvg(this.#angle);
|
|
13658
|
+
}
|
|
13659
|
+
|
|
13660
|
+
#syncPositions() {
|
|
13661
|
+
const container = this.#container;
|
|
13662
|
+
if (!container || !this.#pointHandle) return;
|
|
13663
|
+
const rect = container.getBoundingClientRect();
|
|
13664
|
+
|
|
13665
|
+
this.#pointHandle.setAttribute("value", `${this.#x}% ${this.#y}%`);
|
|
13666
|
+
|
|
13667
|
+
if (this.#radiusSvg) {
|
|
13668
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13669
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13670
|
+
const r = this.#resolveRadius(rect.width);
|
|
13671
|
+
const svg = this.#radiusSvg;
|
|
13672
|
+
const d = Math.max(r * 2, 1);
|
|
13673
|
+
svg.style.position = "absolute";
|
|
13674
|
+
svg.style.width = `${d}px`;
|
|
13675
|
+
svg.style.height = `${d}px`;
|
|
13676
|
+
svg.style.left = `${cx - r}px`;
|
|
13677
|
+
svg.style.top = `${cy - r}px`;
|
|
13678
|
+
svg.setAttribute("viewBox", `0 0 ${d} ${d}`);
|
|
13679
|
+
const circles = svg.querySelectorAll("circle");
|
|
13680
|
+
for (const c of circles) {
|
|
13681
|
+
c.setAttribute("cx", String(r));
|
|
13682
|
+
c.setAttribute("cy", String(r));
|
|
13683
|
+
c.setAttribute("r", String(Math.max(r - 1, 0)));
|
|
13684
|
+
}
|
|
13685
|
+
if (this.#radiusTooltip) {
|
|
13686
|
+
this.#radiusTooltip.setAttribute("text", this.#formatRadius());
|
|
13687
|
+
}
|
|
13688
|
+
}
|
|
13689
|
+
|
|
13690
|
+
if (this.#angleSvg && this.#hasAngle) {
|
|
13691
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13692
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13693
|
+
const r = this.#resolveRadius(rect.width);
|
|
13694
|
+
const angleRad = (this.#angle * Math.PI) / 180;
|
|
13695
|
+
const ax = cx + r * Math.cos(angleRad);
|
|
13696
|
+
const ay = cy + r * Math.sin(angleRad);
|
|
13697
|
+
|
|
13698
|
+
const svg = this.#angleSvg;
|
|
13699
|
+
svg.style.width = `${rect.width}px`;
|
|
13700
|
+
svg.style.height = `${rect.height}px`;
|
|
13701
|
+
svg.style.left = "0";
|
|
13702
|
+
svg.style.top = "0";
|
|
13703
|
+
svg.setAttribute("viewBox", `0 0 ${rect.width} ${rect.height}`);
|
|
13704
|
+
const line = svg.querySelector("line");
|
|
13705
|
+
if (line) {
|
|
13706
|
+
line.setAttribute("x1", String(cx));
|
|
13707
|
+
line.setAttribute("y1", String(cy));
|
|
13708
|
+
line.setAttribute("x2", String(ax));
|
|
13709
|
+
line.setAttribute("y2", String(ay));
|
|
13710
|
+
}
|
|
13711
|
+
}
|
|
13712
|
+
|
|
13713
|
+
if (this.#angleHandle && this.#hasAngle) {
|
|
13714
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13715
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13716
|
+
const r = this.#resolveRadius(rect.width);
|
|
13717
|
+
const angleRad = (this.#angle * Math.PI) / 180;
|
|
13718
|
+
const ax = cx + r * Math.cos(angleRad);
|
|
13719
|
+
const ay = cy + r * Math.sin(angleRad);
|
|
13720
|
+
const pxPct = rect.width > 0 ? (ax / rect.width) * 100 : 0;
|
|
13721
|
+
const pyPct = rect.height > 0 ? (ay / rect.height) * 100 : 0;
|
|
13722
|
+
this.#angleHandle.setAttribute("value", `${pxPct}% ${pyPct}%`);
|
|
13723
|
+
|
|
13724
|
+
if (this.#angleTooltip) {
|
|
13725
|
+
this.#angleTooltip.setAttribute("text", `${Math.round(this.#angle)}°`);
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13728
|
+
|
|
13729
|
+
this.#syncAngleCursor();
|
|
13730
|
+
}
|
|
13731
|
+
|
|
13732
|
+
#emitInput() {
|
|
13733
|
+
this.dispatchEvent(new CustomEvent("input", { bubbles: true, detail: this.value }));
|
|
13734
|
+
}
|
|
13735
|
+
|
|
13736
|
+
#emitChange() {
|
|
13737
|
+
this.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: this.value }));
|
|
13738
|
+
}
|
|
13739
|
+
|
|
13740
|
+
#syncValueAttribute() {
|
|
13741
|
+
this.setAttribute("value", JSON.stringify(this.value));
|
|
13742
|
+
}
|
|
13743
|
+
|
|
13744
|
+
#setupEventListeners() {
|
|
13745
|
+
if (!this.#pointHandle) return;
|
|
13746
|
+
|
|
13747
|
+
this.#pointHandle.addEventListener("input", (e) => {
|
|
13748
|
+
e.stopPropagation();
|
|
13749
|
+
this.#isDragging = true;
|
|
13750
|
+
const px = e.detail?.px ?? this.#x / 100;
|
|
13751
|
+
const py = e.detail?.py ?? this.#y / 100;
|
|
13752
|
+
this.#x = Math.round(Math.max(0, Math.min(100, px * 100)));
|
|
13753
|
+
this.#y = Math.round(Math.max(0, Math.min(100, py * 100)));
|
|
13754
|
+
if (this.#pointTooltip) {
|
|
13755
|
+
this.#pointTooltip.setAttribute("text", this.#pointTipText);
|
|
13756
|
+
this.#pointTooltip.setAttribute("show", "true");
|
|
13757
|
+
this.#pointTooltip.showPopup?.();
|
|
13758
|
+
}
|
|
13759
|
+
this.#syncPositions();
|
|
13760
|
+
this.#emitInput();
|
|
13761
|
+
});
|
|
13762
|
+
|
|
13763
|
+
this.#pointHandle.addEventListener("change", (e) => {
|
|
13764
|
+
e.stopPropagation();
|
|
13765
|
+
const px = e.detail?.px ?? this.#x / 100;
|
|
13766
|
+
const py = e.detail?.py ?? this.#y / 100;
|
|
13767
|
+
this.#x = Math.round(Math.max(0, Math.min(100, px * 100)));
|
|
13768
|
+
this.#y = Math.round(Math.max(0, Math.min(100, py * 100)));
|
|
13769
|
+
if (this.#pointTooltip) this.#pointTooltip.removeAttribute("show");
|
|
13770
|
+
this.#syncPositions();
|
|
13771
|
+
this.#syncValueAttribute();
|
|
13772
|
+
this.#emitChange();
|
|
13773
|
+
requestAnimationFrame(() => { this.#isDragging = false; });
|
|
13774
|
+
});
|
|
13775
|
+
|
|
13776
|
+
if (this.#angleHandle) {
|
|
13777
|
+
this.#angleHandle.addEventListener("input", (e) => {
|
|
13778
|
+
e.stopPropagation();
|
|
13779
|
+
this.#isAngleDragging = true;
|
|
13780
|
+
this.classList.add("fig-canvas-point-ring-active");
|
|
13781
|
+
const container = this.#container;
|
|
13782
|
+
if (!container) return;
|
|
13783
|
+
const rect = container.getBoundingClientRect();
|
|
13784
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13785
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13786
|
+
const hx = e.detail?.x ?? 0;
|
|
13787
|
+
const hy = e.detail?.y ?? 0;
|
|
13788
|
+
const hw = this.#angleHandle.offsetWidth / 2;
|
|
13789
|
+
const hh = this.#angleHandle.offsetHeight / 2;
|
|
13790
|
+
const dx = (hx + hw) - cx;
|
|
13791
|
+
const dy = (hy + hh) - cy;
|
|
13792
|
+
let angle = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
13793
|
+
if (this.#shouldSnap(e.detail?.shiftKey)) {
|
|
13794
|
+
angle = Math.round(angle / 15) * 15;
|
|
13795
|
+
}
|
|
13796
|
+
this.#angle = angle;
|
|
13797
|
+
|
|
13798
|
+
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
13799
|
+
if (this.#shouldSnap(e.detail?.shiftKey)) {
|
|
13800
|
+
const step = this.#radiusIsPercent ? 5 : 10;
|
|
13801
|
+
if (this.#radiusIsPercent) {
|
|
13802
|
+
let pct = (dist / rect.width) * 100;
|
|
13803
|
+
pct = Math.round(pct / step) * step;
|
|
13804
|
+
dist = (pct / 100) * rect.width;
|
|
13805
|
+
} else {
|
|
13806
|
+
dist = Math.round(dist / step) * step;
|
|
13807
|
+
}
|
|
13808
|
+
}
|
|
13809
|
+
if (this.#radiusIsPercent) {
|
|
13810
|
+
this.#radius = Math.max(0, (dist / rect.width) * 100);
|
|
13811
|
+
} else {
|
|
13812
|
+
this.#radius = Math.max(0, dist);
|
|
13813
|
+
}
|
|
13814
|
+
|
|
13815
|
+
this.#syncPositions();
|
|
13816
|
+
if (this.#angleTooltip) {
|
|
13817
|
+
this.#angleTooltip.setAttribute("text", `${Math.round(this.#angle)}°`);
|
|
13818
|
+
this.#angleTooltip.setAttribute("show", "true");
|
|
13819
|
+
this.#angleTooltip.showPopup?.();
|
|
13820
|
+
}
|
|
13821
|
+
if (this.#radiusTooltip) {
|
|
13822
|
+
this.#radiusTooltip.setAttribute("text", this.#formatRadius());
|
|
13823
|
+
}
|
|
13824
|
+
this.#emitInput();
|
|
13825
|
+
});
|
|
13826
|
+
|
|
13827
|
+
this.#angleHandle.addEventListener("change", (e) => {
|
|
13828
|
+
e.stopPropagation();
|
|
13829
|
+
this.classList.remove("fig-canvas-point-ring-active");
|
|
13830
|
+
if (this.#angleTooltip) this.#angleTooltip.removeAttribute("show");
|
|
13831
|
+
this.#syncPositions();
|
|
13832
|
+
this.#syncValueAttribute();
|
|
13833
|
+
this.#emitChange();
|
|
13834
|
+
requestAnimationFrame(() => { this.#isAngleDragging = false; });
|
|
13835
|
+
});
|
|
13836
|
+
|
|
13837
|
+
this.#angleHandle.addEventListener("hitareadown", (e) => {
|
|
13838
|
+
e.stopPropagation();
|
|
13839
|
+
const origEvent = e.detail?.originalEvent;
|
|
13840
|
+
if (!origEvent) return;
|
|
13841
|
+
origEvent.preventDefault();
|
|
13842
|
+
this.#isAngleDragging = true;
|
|
13843
|
+
this.classList.add("fig-canvas-point-ring-active");
|
|
13844
|
+
const container = this.#container;
|
|
13845
|
+
if (!container) return;
|
|
13846
|
+
|
|
13847
|
+
if (this.#angleTooltip) {
|
|
13848
|
+
this.#angleTooltip.setAttribute("show", "true");
|
|
13849
|
+
this.#angleTooltip.showPopup?.();
|
|
13850
|
+
}
|
|
13851
|
+
|
|
13852
|
+
const prevBodyCursor = document.body.style.cursor;
|
|
13853
|
+
document.body.style.cursor = this.#rotateCursorSvg(this.#angle);
|
|
13854
|
+
|
|
13855
|
+
const onMove = (ev) => {
|
|
13856
|
+
const rect = container.getBoundingClientRect();
|
|
13857
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13858
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13859
|
+
const dx = ev.clientX - rect.left - cx;
|
|
13860
|
+
const dy = ev.clientY - rect.top - cy;
|
|
13861
|
+
let angle = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
13862
|
+
if (this.#shouldSnap(ev.shiftKey)) {
|
|
13863
|
+
angle = Math.round(angle / 15) * 15;
|
|
13864
|
+
}
|
|
13865
|
+
this.#angle = angle;
|
|
13866
|
+
this.#syncPositions();
|
|
13867
|
+
document.body.style.cursor = this.#rotateCursorSvg(this.#angle);
|
|
13868
|
+
if (this.#angleTooltip) {
|
|
13869
|
+
this.#angleTooltip.setAttribute("text", `${Math.round(this.#angle)}°`);
|
|
13870
|
+
}
|
|
13871
|
+
this.#emitInput();
|
|
13872
|
+
};
|
|
13873
|
+
|
|
13874
|
+
const onUp = () => {
|
|
13875
|
+
this.#isAngleDragging = false;
|
|
13876
|
+
this.classList.remove("fig-canvas-point-ring-active");
|
|
13877
|
+
document.body.style.cursor = prevBodyCursor;
|
|
13878
|
+
if (this.#angleTooltip) this.#angleTooltip.removeAttribute("show");
|
|
13879
|
+
this.#syncValueAttribute();
|
|
13880
|
+
this.#emitChange();
|
|
13881
|
+
window.removeEventListener("pointermove", onMove);
|
|
13882
|
+
window.removeEventListener("pointerup", onUp);
|
|
13883
|
+
};
|
|
13884
|
+
|
|
13885
|
+
window.addEventListener("pointermove", onMove);
|
|
13886
|
+
window.addEventListener("pointerup", onUp);
|
|
13887
|
+
});
|
|
13888
|
+
}
|
|
13889
|
+
}
|
|
13890
|
+
|
|
13891
|
+
#setupRadiusDrag(circle) {
|
|
13892
|
+
if (!circle) return;
|
|
13893
|
+
circle.addEventListener("pointermove", (e) => {
|
|
13894
|
+
if (this.#isRadiusDragging) return;
|
|
13895
|
+
const container = this.#container;
|
|
13896
|
+
if (!container) return;
|
|
13897
|
+
const rect = container.getBoundingClientRect();
|
|
13898
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13899
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13900
|
+
const deg = (Math.atan2(e.clientY - rect.top - cy, e.clientX - rect.left - cx) * 180) / Math.PI;
|
|
13901
|
+
circle.style.cursor = this.#resizeCursorSvg(deg);
|
|
13902
|
+
});
|
|
13903
|
+
const onDown = (e) => {
|
|
13904
|
+
if (this.hasAttribute("disabled")) return;
|
|
13905
|
+
e.preventDefault();
|
|
13906
|
+
e.stopPropagation();
|
|
13907
|
+
this.#isRadiusDragging = true;
|
|
13908
|
+
this.classList.add("fig-canvas-point-ring-active");
|
|
13909
|
+
const container = this.#container;
|
|
13910
|
+
if (!container) return;
|
|
13911
|
+
|
|
13912
|
+
if (this.#radiusTooltip) {
|
|
13913
|
+
this.#radiusTooltip.setAttribute("show", "true");
|
|
13914
|
+
this.#radiusTooltip.showPopup?.();
|
|
13915
|
+
}
|
|
13916
|
+
|
|
13917
|
+
const prevBodyCursor = document.body.style.cursor;
|
|
13918
|
+
circle.style.pointerEvents = "none";
|
|
13919
|
+
const rect0 = container.getBoundingClientRect();
|
|
13920
|
+
const cx0 = (this.#x / 100) * rect0.width;
|
|
13921
|
+
const cy0 = (this.#y / 100) * rect0.height;
|
|
13922
|
+
const initDeg = (Math.atan2(e.clientY - rect0.top - cy0, e.clientX - rect0.left - cx0) * 180) / Math.PI;
|
|
13923
|
+
document.body.style.cursor = this.#resizeCursorSvg(initDeg);
|
|
13924
|
+
|
|
13925
|
+
const onMove = (ev) => {
|
|
13926
|
+
const rect = container.getBoundingClientRect();
|
|
13927
|
+
const cx = (this.#x / 100) * rect.width;
|
|
13928
|
+
const cy = (this.#y / 100) * rect.height;
|
|
13929
|
+
const dx = ev.clientX - rect.left - cx;
|
|
13930
|
+
const dy = ev.clientY - rect.top - cy;
|
|
13931
|
+
document.body.style.cursor = this.#resizeCursorSvg((Math.atan2(dy, dx) * 180) / Math.PI);
|
|
13932
|
+
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
13933
|
+
if (this.#shouldSnap(ev.shiftKey)) {
|
|
13934
|
+
const step = this.#radiusIsPercent ? 5 : 10;
|
|
13935
|
+
if (this.#radiusIsPercent) {
|
|
13936
|
+
let pct = (dist / rect.width) * 100;
|
|
13937
|
+
pct = Math.round(pct / step) * step;
|
|
13938
|
+
dist = (pct / 100) * rect.width;
|
|
13939
|
+
} else {
|
|
13940
|
+
dist = Math.round(dist / step) * step;
|
|
13941
|
+
}
|
|
13942
|
+
}
|
|
13943
|
+
if (this.#radiusIsPercent) {
|
|
13944
|
+
this.#radius = Math.max(0, (dist / rect.width) * 100);
|
|
13945
|
+
} else {
|
|
13946
|
+
this.#radius = Math.max(0, dist);
|
|
13947
|
+
}
|
|
13948
|
+
this.#syncPositions();
|
|
13949
|
+
this.#emitInput();
|
|
13950
|
+
};
|
|
13951
|
+
|
|
13952
|
+
const onUp = () => {
|
|
13953
|
+
this.#isRadiusDragging = false;
|
|
13954
|
+
this.classList.remove("fig-canvas-point-ring-active");
|
|
13955
|
+
circle.style.pointerEvents = "";
|
|
13956
|
+
document.body.style.cursor = prevBodyCursor;
|
|
13957
|
+
if (this.#radiusTooltip) this.#radiusTooltip.removeAttribute("show");
|
|
13958
|
+
this.#syncValueAttribute();
|
|
13959
|
+
this.#emitChange();
|
|
13960
|
+
window.removeEventListener("pointermove", onMove);
|
|
13961
|
+
window.removeEventListener("pointerup", onUp);
|
|
13962
|
+
};
|
|
13963
|
+
|
|
13964
|
+
window.addEventListener("pointermove", onMove);
|
|
13965
|
+
window.addEventListener("pointerup", onUp);
|
|
13966
|
+
};
|
|
13967
|
+
circle.addEventListener("pointerdown", onDown);
|
|
13968
|
+
this._radiusDragCleanup = () => circle.removeEventListener("pointerdown", onDown);
|
|
13969
|
+
}
|
|
13970
|
+
|
|
13971
|
+
#teardownRadiusDrag() {
|
|
13972
|
+
if (this._radiusDragCleanup) {
|
|
13973
|
+
this._radiusDragCleanup();
|
|
13974
|
+
this._radiusDragCleanup = null;
|
|
13975
|
+
}
|
|
13976
|
+
}
|
|
13977
|
+
}
|
|
13978
|
+
customElements.define("fig-canvas-point", FigCanvasPoint);
|
|
13979
|
+
|
|
13289
13980
|
/* Handle */
|
|
13290
13981
|
class FigHandle extends HTMLElement {
|
|
13291
13982
|
static observedAttributes = [
|
|
@@ -13299,6 +13990,8 @@ class FigHandle extends HTMLElement {
|
|
|
13299
13990
|
"value",
|
|
13300
13991
|
"type",
|
|
13301
13992
|
"control",
|
|
13993
|
+
"hit-area",
|
|
13994
|
+
"hit-area-mode",
|
|
13302
13995
|
];
|
|
13303
13996
|
|
|
13304
13997
|
#isDragging = false;
|
|
@@ -13306,6 +13999,7 @@ class FigHandle extends HTMLElement {
|
|
|
13306
13999
|
#boundPointerDown = null;
|
|
13307
14000
|
#applyingValue = false;
|
|
13308
14001
|
#colorTip = null;
|
|
14002
|
+
#hitAreaEl = null;
|
|
13309
14003
|
|
|
13310
14004
|
get #controlMode() {
|
|
13311
14005
|
return this.getAttribute("control") || null;
|
|
@@ -13443,8 +14137,70 @@ class FigHandle extends HTMLElement {
|
|
|
13443
14137
|
this.#applyingValue = false;
|
|
13444
14138
|
}
|
|
13445
14139
|
|
|
14140
|
+
get #hitAreaMode() {
|
|
14141
|
+
return this.getAttribute("hit-area-mode") || "handle";
|
|
14142
|
+
}
|
|
14143
|
+
|
|
14144
|
+
#parseHitArea() {
|
|
14145
|
+
const raw = this.getAttribute("hit-area");
|
|
14146
|
+
if (!raw) return null;
|
|
14147
|
+
const tokens = raw.trim().split(/\s+/);
|
|
14148
|
+
let vPad = 0, hPad = 0, circle = false;
|
|
14149
|
+
const nums = [];
|
|
14150
|
+
for (const t of tokens) {
|
|
14151
|
+
if (t === "circle") { circle = true; continue; }
|
|
14152
|
+
const n = parseFloat(t);
|
|
14153
|
+
if (Number.isFinite(n)) nums.push(n);
|
|
14154
|
+
}
|
|
14155
|
+
if (nums.length >= 2) { vPad = nums[0]; hPad = nums[1]; }
|
|
14156
|
+
else if (nums.length === 1) { vPad = nums[0]; hPad = nums[0]; }
|
|
14157
|
+
else return null;
|
|
14158
|
+
return { vPad, hPad, circle };
|
|
14159
|
+
}
|
|
14160
|
+
|
|
14161
|
+
#syncHitArea() {
|
|
14162
|
+
const parsed = this.#parseHitArea();
|
|
14163
|
+
if (!parsed) {
|
|
14164
|
+
if (this.#hitAreaEl) {
|
|
14165
|
+
this.#hitAreaEl.remove();
|
|
14166
|
+
this.#hitAreaEl = null;
|
|
14167
|
+
}
|
|
14168
|
+
this.style.removeProperty("--fig-handle-hit-area-size");
|
|
14169
|
+
return;
|
|
14170
|
+
}
|
|
14171
|
+
if (!this.#hitAreaEl) {
|
|
14172
|
+
const el = document.createElement("div");
|
|
14173
|
+
el.classList.add("fig-handle-hit-area");
|
|
14174
|
+
el.addEventListener("pointerdown", (e) => this.#onHitAreaPointerDown(e));
|
|
14175
|
+
this.prepend(el);
|
|
14176
|
+
this.#hitAreaEl = el;
|
|
14177
|
+
}
|
|
14178
|
+
this.style.setProperty("--fig-handle-hit-area-size", String(parsed.hPad * 2));
|
|
14179
|
+
if (parsed.circle) {
|
|
14180
|
+
this.#hitAreaEl.style.borderRadius = "50%";
|
|
14181
|
+
} else {
|
|
14182
|
+
this.#hitAreaEl.style.borderRadius = "inherit";
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
14185
|
+
|
|
14186
|
+
#onHitAreaPointerDown(e) {
|
|
14187
|
+
if (this.hasAttribute("disabled")) return;
|
|
14188
|
+
if (e.target !== this.#hitAreaEl) return;
|
|
14189
|
+
if (this.#hitAreaMode === "delegate") {
|
|
14190
|
+
e.preventDefault();
|
|
14191
|
+
e.stopPropagation();
|
|
14192
|
+
this.dispatchEvent(new CustomEvent("hitareadown", {
|
|
14193
|
+
bubbles: true,
|
|
14194
|
+
detail: { originalEvent: e },
|
|
14195
|
+
}));
|
|
14196
|
+
} else {
|
|
14197
|
+
this.#onPointerDown(e);
|
|
14198
|
+
}
|
|
14199
|
+
}
|
|
14200
|
+
|
|
13446
14201
|
connectedCallback() {
|
|
13447
14202
|
this.#syncDrag();
|
|
14203
|
+
this.#syncHitArea();
|
|
13448
14204
|
this.addEventListener("click", this.#handleSelect);
|
|
13449
14205
|
document.addEventListener("pointerdown", this.#handleDeselect);
|
|
13450
14206
|
document.addEventListener("keydown", this.#handleKeyDown);
|
|
@@ -13456,6 +14212,7 @@ class FigHandle extends HTMLElement {
|
|
|
13456
14212
|
disconnectedCallback() {
|
|
13457
14213
|
this.#teardownDrag();
|
|
13458
14214
|
this.#hideColorTip();
|
|
14215
|
+
if (this.#hitAreaEl) { this.#hitAreaEl.remove(); this.#hitAreaEl = null; }
|
|
13459
14216
|
this.removeEventListener("click", this.#handleSelect);
|
|
13460
14217
|
document.removeEventListener("pointerdown", this.#handleDeselect);
|
|
13461
14218
|
document.removeEventListener("keydown", this.#handleKeyDown);
|
|
@@ -13509,6 +14266,7 @@ class FigHandle extends HTMLElement {
|
|
|
13509
14266
|
}
|
|
13510
14267
|
}
|
|
13511
14268
|
if (name === "drag") this.#syncDrag();
|
|
14269
|
+
if (name === "hit-area") this.#syncHitArea();
|
|
13512
14270
|
if (name === "value" && !this.#applyingValue && !this.#isDragging) {
|
|
13513
14271
|
this.#applyValue(value);
|
|
13514
14272
|
}
|
|
@@ -13547,16 +14305,23 @@ class FigHandle extends HTMLElement {
|
|
|
13547
14305
|
|
|
13548
14306
|
this.#isDragging = true;
|
|
13549
14307
|
const axes = this.#axes;
|
|
13550
|
-
const containerRect = container.getBoundingClientRect();
|
|
13551
14308
|
const handleW = this.offsetWidth;
|
|
13552
14309
|
const handleH = this.offsetHeight;
|
|
14310
|
+
let lastRect = null;
|
|
14311
|
+
|
|
14312
|
+
const handleRect = this.getBoundingClientRect();
|
|
14313
|
+
const handleCenterX = handleRect.left + handleRect.width / 2;
|
|
14314
|
+
const handleCenterY = handleRect.top + handleRect.height / 2;
|
|
14315
|
+
const offsetX = e.clientX - handleCenterX;
|
|
14316
|
+
const offsetY = e.clientY - handleCenterY;
|
|
13553
14317
|
|
|
13554
14318
|
const clampAndApply = (clientX, clientY, shiftKey = false) => {
|
|
13555
14319
|
const rect = container.getBoundingClientRect();
|
|
14320
|
+
lastRect = rect;
|
|
13556
14321
|
const currentLeft = parseFloat(this.style.left) || 0;
|
|
13557
14322
|
const currentTop = parseFloat(this.style.top) || 0;
|
|
13558
|
-
const rawX = clientX - rect.left - handleW / 2;
|
|
13559
|
-
const rawY = clientY - rect.top - handleH / 2;
|
|
14323
|
+
const rawX = (clientX - offsetX) - rect.left - handleW / 2;
|
|
14324
|
+
const rawY = (clientY - offsetY) - rect.top - handleH / 2;
|
|
13560
14325
|
|
|
13561
14326
|
const clampedX = Math.max(
|
|
13562
14327
|
-handleW / 2,
|
|
@@ -13609,7 +14374,7 @@ class FigHandle extends HTMLElement {
|
|
|
13609
14374
|
new CustomEvent("input", {
|
|
13610
14375
|
bubbles: true,
|
|
13611
14376
|
detail: {
|
|
13612
|
-
...this.#positionDetail(
|
|
14377
|
+
...this.#positionDetail(lastRect),
|
|
13613
14378
|
shiftKey: e.shiftKey,
|
|
13614
14379
|
},
|
|
13615
14380
|
}),
|
|
@@ -13628,7 +14393,7 @@ class FigHandle extends HTMLElement {
|
|
|
13628
14393
|
this.dispatchEvent(
|
|
13629
14394
|
new CustomEvent("change", {
|
|
13630
14395
|
bubbles: true,
|
|
13631
|
-
detail: this.#positionDetail(
|
|
14396
|
+
detail: this.#positionDetail(lastRect),
|
|
13632
14397
|
}),
|
|
13633
14398
|
);
|
|
13634
14399
|
const swallowClick = (evt) => {
|