@rogieking/figui3 4.15.4 → 4.15.9
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 +24 -0
- package/base.css +11 -0
- package/components.css +119 -60
- package/dist/base.css +1 -1
- package/dist/components.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +28 -41
- package/fig.js +167 -144
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -7308,6 +7308,10 @@ class FigInputGradient extends HTMLElement {
|
|
|
7308
7308
|
});
|
|
7309
7309
|
}
|
|
7310
7310
|
|
|
7311
|
+
refreshLayout() {
|
|
7312
|
+
this.#repositionHandles();
|
|
7313
|
+
}
|
|
7314
|
+
|
|
7311
7315
|
#syncHandles() {
|
|
7312
7316
|
if (!this.#track) return;
|
|
7313
7317
|
const handles = this.#track.querySelectorAll(
|
|
@@ -11811,6 +11815,13 @@ customElements.define("fig-footer", FigFooter);
|
|
|
11811
11815
|
class FigSpinner extends HTMLElement {}
|
|
11812
11816
|
customElements.define("fig-spinner", FigSpinner);
|
|
11813
11817
|
|
|
11818
|
+
/**
|
|
11819
|
+
* A styled visual preview layer for arbitrary content such as images, canvas,
|
|
11820
|
+
* video, SVG, or custom rendered surfaces.
|
|
11821
|
+
*/
|
|
11822
|
+
class FigPreview extends HTMLElement {}
|
|
11823
|
+
customElements.define("fig-preview", FigPreview);
|
|
11824
|
+
|
|
11814
11825
|
/** @type {Record<string, string>} */
|
|
11815
11826
|
const FIG_ICON_TOKENS = {
|
|
11816
11827
|
chevron: "--icon-16-chevron",
|
|
@@ -12202,6 +12213,7 @@ class FigFillPicker extends HTMLElement {
|
|
|
12202
12213
|
this.#dialog.anchor = this.anchorElement || this.#trigger;
|
|
12203
12214
|
const dialogPosition = this.getAttribute("dialog-position") || "left";
|
|
12204
12215
|
this.#dialog.setAttribute("position", dialogPosition);
|
|
12216
|
+
this.#dialog.setAttribute("offset", this.getAttribute("dialog-offset") || "8 8");
|
|
12205
12217
|
|
|
12206
12218
|
const builtinModes = ["solid", "gradient", "image", "video", "webcam"];
|
|
12207
12219
|
const builtinLabels = {
|
|
@@ -12410,6 +12422,11 @@ class FigFillPicker extends HTMLElement {
|
|
|
12410
12422
|
// Use RAF to ensure layout is complete before updating angle input
|
|
12411
12423
|
requestAnimationFrame(() => {
|
|
12412
12424
|
this.#updateGradientUI();
|
|
12425
|
+
const barInput = tab.querySelector(".fig-fill-picker-gradient-bar-input");
|
|
12426
|
+
barInput?.refreshLayout?.();
|
|
12427
|
+
requestAnimationFrame(() => {
|
|
12428
|
+
barInput?.refreshLayout?.();
|
|
12429
|
+
});
|
|
12413
12430
|
});
|
|
12414
12431
|
}
|
|
12415
12432
|
|
|
@@ -12430,6 +12447,7 @@ class FigFillPicker extends HTMLElement {
|
|
|
12430
12447
|
<fig-handle
|
|
12431
12448
|
type="color"
|
|
12432
12449
|
color="${this.#hsvToHex({ ...this.#color, a: 1 })}"
|
|
12450
|
+
data-no-color-picker
|
|
12433
12451
|
drag
|
|
12434
12452
|
drag-surface=".fig-fill-picker-color-area"
|
|
12435
12453
|
drag-axes="x,y"
|
|
@@ -13347,16 +13365,11 @@ class FigFillPicker extends HTMLElement {
|
|
|
13347
13365
|
</fig-dropdown>
|
|
13348
13366
|
<fig-input-number class="fig-fill-picker-scale" min="1" max="200" value="${
|
|
13349
13367
|
this.#image.scale
|
|
13350
|
-
}" units="%"
|
|
13368
|
+
}" units="%" ${
|
|
13369
|
+
this.#image.scaleMode === "tile" ? "" : 'style="display: none;"'
|
|
13370
|
+
}></fig-input-number>
|
|
13351
13371
|
</fig-field>
|
|
13352
|
-
<
|
|
13353
|
-
<div class="fig-fill-picker-checkerboard"></div>
|
|
13354
|
-
<div class="fig-fill-picker-image-preview"></div>
|
|
13355
|
-
<fig-button variant="overlay" class="fig-fill-picker-upload">
|
|
13356
|
-
Upload from computer
|
|
13357
|
-
<input type="file" accept="image/*" style="display: none;" />
|
|
13358
|
-
</fig-button>
|
|
13359
|
-
</div>
|
|
13372
|
+
<fig-image class="fig-fill-picker-media-preview fig-fill-picker-image-preview" upload="true" label="Upload from computer" size="auto" aspect-ratio="1/1" fit="cover" checkerboard="true"></fig-image>
|
|
13360
13373
|
`;
|
|
13361
13374
|
|
|
13362
13375
|
this.#setupImageEvents(container);
|
|
@@ -13367,8 +13380,6 @@ class FigFillPicker extends HTMLElement {
|
|
|
13367
13380
|
".fig-fill-picker-scale-mode",
|
|
13368
13381
|
);
|
|
13369
13382
|
const scaleInput = container.querySelector(".fig-fill-picker-scale");
|
|
13370
|
-
const uploadBtn = container.querySelector(".fig-fill-picker-upload");
|
|
13371
|
-
const fileInput = container.querySelector('input[type="file"]');
|
|
13372
13383
|
const preview = container.querySelector(".fig-fill-picker-image-preview");
|
|
13373
13384
|
|
|
13374
13385
|
scaleModeDropdown.addEventListener("change", (e) => {
|
|
@@ -13386,88 +13397,106 @@ class FigFillPicker extends HTMLElement {
|
|
|
13386
13397
|
this.#emitInput();
|
|
13387
13398
|
});
|
|
13388
13399
|
|
|
13389
|
-
|
|
13390
|
-
|
|
13400
|
+
preview.addEventListener("loaded", (e) => {
|
|
13401
|
+
const src = e.detail?.src || preview.src;
|
|
13402
|
+
if (!src) return;
|
|
13403
|
+
this.#image.url = src;
|
|
13404
|
+
this.#updateImagePreview(preview);
|
|
13405
|
+
this.#updateChit();
|
|
13406
|
+
this.#emitInput();
|
|
13391
13407
|
});
|
|
13392
13408
|
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
this.#updateImagePreview(preview);
|
|
13400
|
-
this.#updateChit();
|
|
13401
|
-
this.#emitInput();
|
|
13402
|
-
};
|
|
13403
|
-
reader.readAsDataURL(file);
|
|
13404
|
-
}
|
|
13409
|
+
preview.addEventListener("change", () => {
|
|
13410
|
+
if (preview.src) return;
|
|
13411
|
+
this.#image.url = null;
|
|
13412
|
+
this.#updateImagePreview(preview);
|
|
13413
|
+
this.#updateChit();
|
|
13414
|
+
this.#emitInput();
|
|
13405
13415
|
});
|
|
13406
13416
|
|
|
13407
|
-
|
|
13408
|
-
const previewArea = container.querySelector(
|
|
13409
|
-
".fig-fill-picker-media-preview",
|
|
13410
|
-
);
|
|
13411
|
-
previewArea.addEventListener("dragover", (e) => {
|
|
13412
|
-
e.preventDefault();
|
|
13413
|
-
previewArea.classList.add("dragover");
|
|
13414
|
-
});
|
|
13415
|
-
previewArea.addEventListener("dragleave", () => {
|
|
13416
|
-
previewArea.classList.remove("dragover");
|
|
13417
|
-
});
|
|
13418
|
-
previewArea.addEventListener("drop", (e) => {
|
|
13419
|
-
e.preventDefault();
|
|
13420
|
-
previewArea.classList.remove("dragover");
|
|
13421
|
-
const file = e.dataTransfer.files[0];
|
|
13422
|
-
if (file && file.type.startsWith("image/")) {
|
|
13423
|
-
const reader = new FileReader();
|
|
13424
|
-
reader.onload = (e) => {
|
|
13425
|
-
this.#image.url = e.target.result;
|
|
13426
|
-
this.#updateImagePreview(preview);
|
|
13427
|
-
this.#updateChit();
|
|
13428
|
-
this.#emitInput();
|
|
13429
|
-
};
|
|
13430
|
-
reader.readAsDataURL(file);
|
|
13431
|
-
}
|
|
13432
|
-
});
|
|
13417
|
+
this.#updateImagePreview(preview);
|
|
13433
13418
|
}
|
|
13434
13419
|
|
|
13435
13420
|
#updateImagePreview(element) {
|
|
13436
|
-
const container = element.closest(".fig-fill-picker-media-preview");
|
|
13437
13421
|
if (!this.#image.url) {
|
|
13438
|
-
element.
|
|
13439
|
-
|
|
13422
|
+
element.removeAttribute("src");
|
|
13423
|
+
element.classList.remove("has-media", "is-tiled");
|
|
13424
|
+
element.style.backgroundImage = "";
|
|
13425
|
+
element.style.backgroundPosition = "";
|
|
13426
|
+
element.style.backgroundRepeat = "";
|
|
13427
|
+
element.style.backgroundSize = "";
|
|
13440
13428
|
return;
|
|
13441
13429
|
}
|
|
13442
13430
|
|
|
13443
|
-
element.
|
|
13444
|
-
|
|
13445
|
-
element.style.backgroundImage =
|
|
13446
|
-
element.style.backgroundPosition = "
|
|
13431
|
+
element.setAttribute("src", this.#image.url);
|
|
13432
|
+
element.classList.add("has-media");
|
|
13433
|
+
element.style.backgroundImage = "";
|
|
13434
|
+
element.style.backgroundPosition = "";
|
|
13435
|
+
element.style.backgroundRepeat = "";
|
|
13436
|
+
element.style.backgroundSize = "";
|
|
13437
|
+
element.mediaEl?.style.removeProperty("opacity");
|
|
13438
|
+
|
|
13439
|
+
const fileInput = element.querySelector("fig-input-file[data-generated]");
|
|
13440
|
+
if (fileInput) {
|
|
13441
|
+
fileInput.setAttribute("label", "Replace");
|
|
13442
|
+
fileInput.removeAttribute("url");
|
|
13443
|
+
}
|
|
13447
13444
|
|
|
13448
13445
|
switch (this.#image.scaleMode) {
|
|
13449
13446
|
case "fill":
|
|
13450
|
-
element.
|
|
13451
|
-
element.
|
|
13452
|
-
break;
|
|
13453
|
-
case "fit":
|
|
13454
|
-
element.style.backgroundSize = "contain";
|
|
13455
|
-
element.style.backgroundRepeat = "no-repeat";
|
|
13447
|
+
element.classList.remove("is-tiled");
|
|
13448
|
+
element.setAttribute("fit", "cover");
|
|
13456
13449
|
break;
|
|
13457
13450
|
case "crop":
|
|
13458
|
-
element.
|
|
13459
|
-
element.
|
|
13451
|
+
element.classList.remove("is-tiled");
|
|
13452
|
+
element.setAttribute("fit", "cover");
|
|
13453
|
+
break;
|
|
13454
|
+
case "fit":
|
|
13455
|
+
element.classList.remove("is-tiled");
|
|
13456
|
+
element.setAttribute("fit", "contain");
|
|
13460
13457
|
break;
|
|
13461
13458
|
case "tile":
|
|
13459
|
+
element.classList.add("is-tiled");
|
|
13460
|
+
element.setAttribute("fit", "none");
|
|
13461
|
+
element.style.backgroundImage = `url(${this.#image.url})`;
|
|
13462
|
+
element.style.backgroundPosition = "top left";
|
|
13462
13463
|
element.style.backgroundSize = `${this.#image.scale}%`;
|
|
13463
13464
|
element.style.backgroundRepeat = "repeat";
|
|
13464
|
-
element.style.
|
|
13465
|
+
if (element.mediaEl) element.mediaEl.style.opacity = "0";
|
|
13465
13466
|
break;
|
|
13466
13467
|
}
|
|
13467
13468
|
}
|
|
13468
13469
|
|
|
13469
13470
|
// For video elements (still uses object-fit)
|
|
13470
13471
|
#updateVideoPreviewStyle(element) {
|
|
13472
|
+
if (element.tagName === "FIG-MEDIA") {
|
|
13473
|
+
if (!this.#video.url) {
|
|
13474
|
+
element.removeAttribute("src");
|
|
13475
|
+
element.classList.remove("has-media");
|
|
13476
|
+
return;
|
|
13477
|
+
}
|
|
13478
|
+
|
|
13479
|
+
element.setAttribute("src", this.#video.url);
|
|
13480
|
+
element.classList.add("has-media");
|
|
13481
|
+
|
|
13482
|
+
const fileInput = element.querySelector("fig-input-file[data-generated]");
|
|
13483
|
+
if (fileInput) {
|
|
13484
|
+
fileInput.setAttribute("label", "Replace");
|
|
13485
|
+
fileInput.removeAttribute("url");
|
|
13486
|
+
}
|
|
13487
|
+
|
|
13488
|
+
switch (this.#video.scaleMode) {
|
|
13489
|
+
case "fill":
|
|
13490
|
+
case "crop":
|
|
13491
|
+
element.setAttribute("fit", "cover");
|
|
13492
|
+
break;
|
|
13493
|
+
case "fit":
|
|
13494
|
+
element.setAttribute("fit", "contain");
|
|
13495
|
+
break;
|
|
13496
|
+
}
|
|
13497
|
+
return;
|
|
13498
|
+
}
|
|
13499
|
+
|
|
13471
13500
|
element.style.objectPosition = "center";
|
|
13472
13501
|
element.style.width = "100%";
|
|
13473
13502
|
element.style.height = "100%";
|
|
@@ -13499,14 +13528,7 @@ class FigFillPicker extends HTMLElement {
|
|
|
13499
13528
|
<option value="crop">Crop</option>
|
|
13500
13529
|
</fig-dropdown>
|
|
13501
13530
|
</fig-field>
|
|
13502
|
-
<
|
|
13503
|
-
<div class="fig-fill-picker-checkerboard"></div>
|
|
13504
|
-
<video class="fig-fill-picker-video-preview" style="display: none;" muted loop></video>
|
|
13505
|
-
<fig-button variant="overlay" class="fig-fill-picker-upload">
|
|
13506
|
-
Upload from computer
|
|
13507
|
-
<input type="file" accept="video/*" style="display: none;" />
|
|
13508
|
-
</fig-button>
|
|
13509
|
-
</div>
|
|
13531
|
+
<fig-media class="fig-fill-picker-media-preview fig-fill-picker-video-preview" type="video" upload="true" label="Upload from computer" size="auto" aspect-ratio="1/1" fit="cover" checkerboard="true" autoplay="true" muted="true" loop="true"></fig-media>
|
|
13510
13532
|
`;
|
|
13511
13533
|
|
|
13512
13534
|
this.#setupVideoEvents(container);
|
|
@@ -13516,8 +13538,6 @@ class FigFillPicker extends HTMLElement {
|
|
|
13516
13538
|
const scaleModeDropdown = container.querySelector(
|
|
13517
13539
|
".fig-fill-picker-scale-mode",
|
|
13518
13540
|
);
|
|
13519
|
-
const uploadBtn = container.querySelector(".fig-fill-picker-upload");
|
|
13520
|
-
const fileInput = container.querySelector('input[type="file"]');
|
|
13521
13541
|
const preview = container.querySelector(".fig-fill-picker-video-preview");
|
|
13522
13542
|
|
|
13523
13543
|
scaleModeDropdown.addEventListener("change", (e) => {
|
|
@@ -13527,51 +13547,25 @@ class FigFillPicker extends HTMLElement {
|
|
|
13527
13547
|
this.#emitInput();
|
|
13528
13548
|
});
|
|
13529
13549
|
|
|
13530
|
-
|
|
13531
|
-
|
|
13550
|
+
preview.addEventListener("loaded", (e) => {
|
|
13551
|
+
const src = e.detail?.src || preview.src;
|
|
13552
|
+
if (!src) return;
|
|
13553
|
+
this.#video.url = src;
|
|
13554
|
+
this.#updateVideoPreviewStyle(preview);
|
|
13555
|
+
preview.play?.();
|
|
13556
|
+
this.#updateChit();
|
|
13557
|
+
this.#emitInput();
|
|
13532
13558
|
});
|
|
13533
13559
|
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
const file = e.target.files[0];
|
|
13541
|
-
if (file) {
|
|
13542
|
-
this.#video.url = URL.createObjectURL(file);
|
|
13543
|
-
preview.src = this.#video.url;
|
|
13544
|
-
preview.style.display = "block";
|
|
13545
|
-
preview.play();
|
|
13546
|
-
previewArea.classList.add("has-media");
|
|
13547
|
-
this.#updateVideoPreviewStyle(preview);
|
|
13548
|
-
this.#updateChit();
|
|
13549
|
-
this.#emitInput();
|
|
13550
|
-
}
|
|
13560
|
+
preview.addEventListener("change", () => {
|
|
13561
|
+
if (preview.src) return;
|
|
13562
|
+
this.#video.url = null;
|
|
13563
|
+
this.#updateVideoPreviewStyle(preview);
|
|
13564
|
+
this.#updateChit();
|
|
13565
|
+
this.#emitInput();
|
|
13551
13566
|
});
|
|
13552
13567
|
|
|
13553
|
-
|
|
13554
|
-
e.preventDefault();
|
|
13555
|
-
previewArea.classList.add("dragover");
|
|
13556
|
-
});
|
|
13557
|
-
previewArea.addEventListener("dragleave", () => {
|
|
13558
|
-
previewArea.classList.remove("dragover");
|
|
13559
|
-
});
|
|
13560
|
-
previewArea.addEventListener("drop", (e) => {
|
|
13561
|
-
e.preventDefault();
|
|
13562
|
-
previewArea.classList.remove("dragover");
|
|
13563
|
-
const file = e.dataTransfer.files[0];
|
|
13564
|
-
if (file && file.type.startsWith("video/")) {
|
|
13565
|
-
this.#video.url = URL.createObjectURL(file);
|
|
13566
|
-
preview.src = this.#video.url;
|
|
13567
|
-
preview.style.display = "block";
|
|
13568
|
-
preview.play();
|
|
13569
|
-
previewArea.classList.add("has-media");
|
|
13570
|
-
this.#updateVideoPreviewStyle(preview);
|
|
13571
|
-
this.#updateChit();
|
|
13572
|
-
this.#emitInput();
|
|
13573
|
-
}
|
|
13574
|
-
});
|
|
13568
|
+
this.#updateVideoPreviewStyle(preview);
|
|
13575
13569
|
}
|
|
13576
13570
|
|
|
13577
13571
|
// ============ WEBCAM TAB ============
|
|
@@ -15026,6 +15020,10 @@ class FigHandle extends HTMLElement {
|
|
|
15026
15020
|
return this.classList.contains("fig-input-gradient-ghost");
|
|
15027
15021
|
}
|
|
15028
15022
|
|
|
15023
|
+
get #canOpenColorPicker() {
|
|
15024
|
+
return !this.hasAttribute("data-no-color-picker");
|
|
15025
|
+
}
|
|
15026
|
+
|
|
15029
15027
|
get #dragEnabled() {
|
|
15030
15028
|
const v = this.getAttribute("drag");
|
|
15031
15029
|
return v !== null && v !== "false";
|
|
@@ -15078,14 +15076,8 @@ class FigHandle extends HTMLElement {
|
|
|
15078
15076
|
get value() {
|
|
15079
15077
|
const container = this.#getContainer();
|
|
15080
15078
|
if (!container) return "0% 0%";
|
|
15081
|
-
const
|
|
15082
|
-
|
|
15083
|
-
const hh = this.offsetHeight / 2;
|
|
15084
|
-
const x = parseFloat(this.style.left) || 0;
|
|
15085
|
-
const y = parseFloat(this.style.top) || 0;
|
|
15086
|
-
const px = rect.width > 0 ? ((x + hw) / rect.width) * 100 : 0;
|
|
15087
|
-
const py = rect.height > 0 ? ((y + hh) / rect.height) * 100 : 0;
|
|
15088
|
-
return `${Math.round(px)}% ${Math.round(py)}%`;
|
|
15079
|
+
const { px, py } = this.#positionDetail(container.getBoundingClientRect());
|
|
15080
|
+
return `${Math.round(px * 100)}% ${Math.round(py * 100)}%`;
|
|
15089
15081
|
}
|
|
15090
15082
|
|
|
15091
15083
|
set value(v) {
|
|
@@ -15124,26 +15116,32 @@ class FigHandle extends HTMLElement {
|
|
|
15124
15116
|
const hw = this.offsetWidth / 2;
|
|
15125
15117
|
const hh = this.offsetHeight / 2;
|
|
15126
15118
|
|
|
15127
|
-
const
|
|
15119
|
+
const resolvePx = (token, containerDim, halfHandle) => {
|
|
15128
15120
|
if (token && typeof token === "object" && "px" in token) {
|
|
15129
15121
|
return Math.max(
|
|
15130
15122
|
-halfHandle,
|
|
15131
15123
|
Math.min(containerDim - halfHandle, token.px - halfHandle),
|
|
15132
15124
|
);
|
|
15133
15125
|
}
|
|
15126
|
+
return null;
|
|
15127
|
+
};
|
|
15128
|
+
|
|
15129
|
+
const resolveResponsive = (token, halfHandle) => {
|
|
15134
15130
|
const pct = typeof token === "number" ? token : 0;
|
|
15135
|
-
|
|
15136
|
-
return Math.max(
|
|
15137
|
-
-halfHandle,
|
|
15138
|
-
Math.min(containerDim - halfHandle, center - halfHandle),
|
|
15139
|
-
);
|
|
15131
|
+
return `calc(${pct}% - ${halfHandle}px)`;
|
|
15140
15132
|
};
|
|
15141
15133
|
|
|
15142
15134
|
const axes = this.#axes;
|
|
15143
|
-
if (axes.x)
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15135
|
+
if (axes.x) {
|
|
15136
|
+
const xPx = resolvePx(xToken, rect.width, hw);
|
|
15137
|
+
this.style.left =
|
|
15138
|
+
xPx === null ? resolveResponsive(xToken, hw) : `${Math.round(xPx)}px`;
|
|
15139
|
+
}
|
|
15140
|
+
if (axes.y) {
|
|
15141
|
+
const yPx = resolvePx(yToken, rect.height, hh);
|
|
15142
|
+
this.style.top =
|
|
15143
|
+
yPx === null ? resolveResponsive(yToken, hh) : `${Math.round(yPx)}px`;
|
|
15144
|
+
}
|
|
15147
15145
|
}
|
|
15148
15146
|
|
|
15149
15147
|
#syncValueAttribute() {
|
|
@@ -15254,7 +15252,12 @@ class FigHandle extends HTMLElement {
|
|
|
15254
15252
|
select() {
|
|
15255
15253
|
if (this.hasAttribute("disabled")) return;
|
|
15256
15254
|
this.setAttribute("selected", "");
|
|
15257
|
-
if (
|
|
15255
|
+
if (
|
|
15256
|
+
this.getAttribute("type") === "color" &&
|
|
15257
|
+
this.#canOpenColorPicker &&
|
|
15258
|
+
!this.#isDragging &&
|
|
15259
|
+
this.#usesColorTip
|
|
15260
|
+
)
|
|
15258
15261
|
this.#showColorTip();
|
|
15259
15262
|
}
|
|
15260
15263
|
|
|
@@ -15269,7 +15272,11 @@ class FigHandle extends HTMLElement {
|
|
|
15269
15272
|
this.#didDrag = false;
|
|
15270
15273
|
return;
|
|
15271
15274
|
}
|
|
15272
|
-
if (
|
|
15275
|
+
if (
|
|
15276
|
+
this.getAttribute("type") === "color" &&
|
|
15277
|
+
this.#canOpenColorPicker &&
|
|
15278
|
+
!this.#usesColorTip
|
|
15279
|
+
) {
|
|
15273
15280
|
this.#openDirectColorPicker();
|
|
15274
15281
|
return;
|
|
15275
15282
|
}
|
|
@@ -15287,6 +15294,7 @@ class FigHandle extends HTMLElement {
|
|
|
15287
15294
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
15288
15295
|
if (!this.hasAttribute("selected")) return;
|
|
15289
15296
|
if (this.getAttribute("type") !== "color") return;
|
|
15297
|
+
if (!this.#canOpenColorPicker) return;
|
|
15290
15298
|
e.preventDefault();
|
|
15291
15299
|
if (this.#usesColorTip) {
|
|
15292
15300
|
if (!this.#colorTip) this.#showColorTip();
|
|
@@ -15372,8 +15380,9 @@ class FigHandle extends HTMLElement {
|
|
|
15372
15380
|
const clampAndApply = (clientX, clientY, shiftKey = false) => {
|
|
15373
15381
|
const rect = container.getBoundingClientRect();
|
|
15374
15382
|
lastRect = rect;
|
|
15375
|
-
const
|
|
15376
|
-
const
|
|
15383
|
+
const currentPosition = this.#positionDetail(rect);
|
|
15384
|
+
const currentLeft = currentPosition.x;
|
|
15385
|
+
const currentTop = currentPosition.y;
|
|
15377
15386
|
const rawX = clientX - offsetX - rect.left - handleW / 2;
|
|
15378
15387
|
const rawY = clientY - offsetY - rect.top - handleH / 2;
|
|
15379
15388
|
|
|
@@ -15421,6 +15430,7 @@ class FigHandle extends HTMLElement {
|
|
|
15421
15430
|
const dx = e.clientX - startX;
|
|
15422
15431
|
const dy = e.clientY - startY;
|
|
15423
15432
|
if (dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) return;
|
|
15433
|
+
this.#closeColorPickerForDrag();
|
|
15424
15434
|
this.classList.add("dragging");
|
|
15425
15435
|
this.style.cursor = "grabbing";
|
|
15426
15436
|
if (!this.hasAttribute("selected")) this.select();
|
|
@@ -15447,6 +15457,7 @@ class FigHandle extends HTMLElement {
|
|
|
15447
15457
|
if (this.#didDrag) {
|
|
15448
15458
|
clampAndApply(e.clientX, e.clientY, e.shiftKey);
|
|
15449
15459
|
this.#syncValueAttribute();
|
|
15460
|
+
this.#applyValue(this.getAttribute("value"));
|
|
15450
15461
|
this.dispatchEvent(
|
|
15451
15462
|
new CustomEvent("change", {
|
|
15452
15463
|
bubbles: true,
|
|
@@ -15560,6 +15571,7 @@ class FigHandle extends HTMLElement {
|
|
|
15560
15571
|
const picker = document.createElement("fig-fill-picker");
|
|
15561
15572
|
picker.setAttribute("mode", "solid");
|
|
15562
15573
|
picker.setAttribute("alpha", "true");
|
|
15574
|
+
picker.setAttribute("dialog-offset", "8 8");
|
|
15563
15575
|
picker.setAttribute("value", this.#directColorPickerValue());
|
|
15564
15576
|
picker.anchorElement = this;
|
|
15565
15577
|
|
|
@@ -15595,6 +15607,12 @@ class FigHandle extends HTMLElement {
|
|
|
15595
15607
|
this.removeAttribute("selected");
|
|
15596
15608
|
}
|
|
15597
15609
|
|
|
15610
|
+
#closeColorPickerForDrag() {
|
|
15611
|
+
if (this.getAttribute("type") !== "color") return;
|
|
15612
|
+
this.#hideColorTip();
|
|
15613
|
+
this.#directColorPicker?.close();
|
|
15614
|
+
}
|
|
15615
|
+
|
|
15598
15616
|
#showColorTip() {
|
|
15599
15617
|
if (this.#colorTip) return;
|
|
15600
15618
|
const tip = document.createElement("fig-color-tip");
|
|
@@ -15710,12 +15728,17 @@ class FigHandle extends HTMLElement {
|
|
|
15710
15728
|
};
|
|
15711
15729
|
|
|
15712
15730
|
#positionDetail(containerRect) {
|
|
15731
|
+
const rect = containerRect || this.#getContainer()?.getBoundingClientRect();
|
|
15732
|
+
if (!rect) return { x: 0, y: 0, px: 0, py: 0 };
|
|
15733
|
+
const handleRect = this.getBoundingClientRect();
|
|
15713
15734
|
const hw = this.offsetWidth / 2;
|
|
15714
15735
|
const hh = this.offsetHeight / 2;
|
|
15715
|
-
const x =
|
|
15716
|
-
const y =
|
|
15717
|
-
const
|
|
15718
|
-
const
|
|
15736
|
+
const x = handleRect.left - rect.left;
|
|
15737
|
+
const y = handleRect.top - rect.top;
|
|
15738
|
+
const centerX = x + hw;
|
|
15739
|
+
const centerY = y + hh;
|
|
15740
|
+
const px = rect.width > 0 ? centerX / rect.width : 0;
|
|
15741
|
+
const py = rect.height > 0 ? centerY / rect.height : 0;
|
|
15719
15742
|
return { x, y, px, py };
|
|
15720
15743
|
}
|
|
15721
15744
|
}
|
package/package.json
CHANGED