@rogieking/figui3 8.3.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.css +31 -62
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +3 -193
- package/dist/fig-lab.js +2 -59
- package/dist/fig.css +1 -1
- package/dist/fig.js +33 -272
- package/fig-editor.css +128 -27
- package/fig-editor.js +1303 -354
- package/fig-lab.js +319 -133
- package/fig.js +813 -799
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -27,6 +27,51 @@ function createFigIcon(name, options = {}) {
|
|
|
27
27
|
return icon;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
const FIG_SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
31
|
+
|
|
32
|
+
function figAppendChildren(parent, children) {
|
|
33
|
+
const append = (child) => {
|
|
34
|
+
if (child === null || child === undefined || child === false) return;
|
|
35
|
+
if (Array.isArray(child)) {
|
|
36
|
+
child.forEach(append);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
parent.append(child instanceof Node ? child : String(child));
|
|
40
|
+
};
|
|
41
|
+
append(children);
|
|
42
|
+
return parent;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function figSetAttributes(element, attributes = {}) {
|
|
46
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
47
|
+
if (value === null || value === undefined || value === false) continue;
|
|
48
|
+
if (name === "className") {
|
|
49
|
+
if (element.namespaceURI === FIG_SVG_NAMESPACE) {
|
|
50
|
+
element.setAttribute("class", String(value));
|
|
51
|
+
} else {
|
|
52
|
+
element.className = String(value);
|
|
53
|
+
}
|
|
54
|
+
} else if (value === true) {
|
|
55
|
+
element.setAttribute(name, "");
|
|
56
|
+
} else {
|
|
57
|
+
element.setAttribute(name, String(value));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return element;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function figCreateElement(tagName, attributes, children) {
|
|
64
|
+
const element = document.createElement(tagName);
|
|
65
|
+
figSetAttributes(element, attributes);
|
|
66
|
+
return figAppendChildren(element, children);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function figCreateSvgElement(tagName, attributes, children) {
|
|
70
|
+
const element = document.createElementNS(FIG_SVG_NAMESPACE, tagName);
|
|
71
|
+
figSetAttributes(element, attributes);
|
|
72
|
+
return figAppendChildren(element, children);
|
|
73
|
+
}
|
|
74
|
+
|
|
30
75
|
/** Run callback on the next frame; skip if the host disconnected first. */
|
|
31
76
|
function figNextFrame(host, callback) {
|
|
32
77
|
requestAnimationFrame(() => {
|
|
@@ -387,46 +432,50 @@ class FigButton extends HTMLElement {
|
|
|
387
432
|
// Custom button modes are implemented by the host. Keep the shadow
|
|
388
433
|
// control inert so invalid native types (for example "toggle") cannot
|
|
389
434
|
// normalize to "submit" inside a form.
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
435
|
+
const style = document.createElement("style");
|
|
436
|
+
style.textContent = `
|
|
437
|
+
button, button:hover, button:active, .fig-button-control {
|
|
438
|
+
padding: 0 var(--spacer-2);
|
|
439
|
+
appearance: none;
|
|
440
|
+
display: flex;
|
|
441
|
+
border: 0;
|
|
442
|
+
flex: 1;
|
|
443
|
+
text-align: center;
|
|
444
|
+
align-items: stretch;
|
|
445
|
+
justify-content: center;
|
|
446
|
+
font: inherit;
|
|
447
|
+
color: inherit;
|
|
448
|
+
outline: 0;
|
|
449
|
+
place-items: center;
|
|
450
|
+
background: transparent;
|
|
451
|
+
margin: calc(var(--spacer-2)*-1);
|
|
452
|
+
height: var(--spacer-4);
|
|
453
|
+
white-space: nowrap;
|
|
454
|
+
overflow: hidden;
|
|
455
|
+
text-overflow: ellipsis;
|
|
456
|
+
width: 100%;
|
|
457
|
+
min-width: 0;
|
|
458
|
+
}
|
|
459
|
+
:host([size="large"]) button,
|
|
460
|
+
:host([size="large"]) .fig-button-control {
|
|
461
|
+
height: var(--spacer-5);
|
|
462
|
+
}
|
|
463
|
+
:host([size="large"][icon]) button,
|
|
464
|
+
:host([size="large"][icon]) .fig-button-control {
|
|
465
|
+
padding: 0;
|
|
466
|
+
}
|
|
467
|
+
`;
|
|
468
|
+
const control = figCreateElement(
|
|
469
|
+
controlTag,
|
|
470
|
+
{
|
|
471
|
+
className: "fig-button-control",
|
|
472
|
+
type: isControlWrapper ? null : "button",
|
|
473
|
+
},
|
|
474
|
+
document.createElement("slot"),
|
|
475
|
+
);
|
|
476
|
+
this.shadowRoot.replaceChildren(style, control);
|
|
477
|
+
|
|
478
|
+
this.button = control;
|
|
430
479
|
this.button.addEventListener("click", this.#boundHandleClick);
|
|
431
480
|
this.button.addEventListener("focus", this.#boundHandleFocus);
|
|
432
481
|
this.button.addEventListener("blur", this.#boundHandleBlur);
|
|
@@ -1573,7 +1622,7 @@ class FigTruncate extends HTMLElement {
|
|
|
1573
1622
|
} else {
|
|
1574
1623
|
splitIndex = Math.ceil(text.length / 2);
|
|
1575
1624
|
}
|
|
1576
|
-
this.
|
|
1625
|
+
this.replaceChildren();
|
|
1577
1626
|
const startSpan = document.createElement("span");
|
|
1578
1627
|
startSpan.className = "start";
|
|
1579
1628
|
startSpan.textContent = text.slice(0, splitIndex);
|
|
@@ -4960,7 +5009,7 @@ class FigOptions extends HTMLElement {
|
|
|
4960
5009
|
}
|
|
4961
5010
|
|
|
4962
5011
|
#renderSegments() {
|
|
4963
|
-
this.
|
|
5012
|
+
this.replaceChildren();
|
|
4964
5013
|
if (this.#parsedOptions.length === 0) return;
|
|
4965
5014
|
|
|
4966
5015
|
const sc = document.createElement("fig-segmented-control");
|
|
@@ -5019,7 +5068,7 @@ class FigOptions extends HTMLElement {
|
|
|
5019
5068
|
}
|
|
5020
5069
|
|
|
5021
5070
|
#renderDropdown() {
|
|
5022
|
-
this.
|
|
5071
|
+
this.replaceChildren();
|
|
5023
5072
|
if (this.#parsedOptions.length === 0) return;
|
|
5024
5073
|
|
|
5025
5074
|
const dd = document.createElement("fig-dropdown");
|
|
@@ -5207,7 +5256,7 @@ class FigSlider extends HTMLElement {
|
|
|
5207
5256
|
constructor() {
|
|
5208
5257
|
super();
|
|
5209
5258
|
// Parser-created custom elements do not have their children yet here.
|
|
5210
|
-
this.
|
|
5259
|
+
this.initialChildren = null;
|
|
5211
5260
|
|
|
5212
5261
|
// Bind the event handlers
|
|
5213
5262
|
this.#boundHandleInput = (e) => {
|
|
@@ -5239,7 +5288,7 @@ class FigSlider extends HTMLElement {
|
|
|
5239
5288
|
this.#isInteracting = false;
|
|
5240
5289
|
if (this.#pendingRegeneration && this.isConnected) {
|
|
5241
5290
|
this.#pendingRegeneration = false;
|
|
5242
|
-
this.#
|
|
5291
|
+
this.#regenerateMarkup();
|
|
5243
5292
|
}
|
|
5244
5293
|
};
|
|
5245
5294
|
}
|
|
@@ -5372,48 +5421,57 @@ class FigSlider extends HTMLElement {
|
|
|
5372
5421
|
}
|
|
5373
5422
|
}
|
|
5374
5423
|
|
|
5375
|
-
#
|
|
5424
|
+
#regenerateMarkup() {
|
|
5376
5425
|
this.#readAttributesFromMarkup();
|
|
5377
5426
|
|
|
5378
5427
|
if (this.color) {
|
|
5379
5428
|
this.style.setProperty("--color", this.color);
|
|
5380
5429
|
}
|
|
5381
5430
|
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5431
|
+
const input = figCreateElement("input", {
|
|
5432
|
+
type: "range",
|
|
5433
|
+
tabindex: this.text ? "-1" : null,
|
|
5434
|
+
disabled: this.disabled,
|
|
5435
|
+
min: this.min,
|
|
5436
|
+
max: this.max,
|
|
5437
|
+
step: this.step,
|
|
5438
|
+
className: this.type,
|
|
5439
|
+
value: this.value,
|
|
5440
|
+
"aria-valuemin": this.min,
|
|
5441
|
+
"aria-valuemax": this.max,
|
|
5442
|
+
"aria-valuenow": this.value,
|
|
5443
|
+
});
|
|
5444
|
+
const inputContainer = figCreateElement(
|
|
5445
|
+
"div",
|
|
5446
|
+
{
|
|
5447
|
+
className: "fig-slider-input-container",
|
|
5448
|
+
role: "group",
|
|
5449
|
+
},
|
|
5450
|
+
input,
|
|
5451
|
+
);
|
|
5452
|
+
for (const child of this.initialChildren ?? []) {
|
|
5453
|
+
inputContainer.appendChild(child);
|
|
5454
|
+
}
|
|
5455
|
+
|
|
5456
|
+
const renderedChildren = [inputContainer];
|
|
5398
5457
|
if (this.text) {
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
html = slider;
|
|
5458
|
+
renderedChildren.push(
|
|
5459
|
+
figCreateElement("fig-input-number", {
|
|
5460
|
+
placeholder: this.placeholder,
|
|
5461
|
+
min: this.min,
|
|
5462
|
+
max: this.max,
|
|
5463
|
+
transform: this.transform,
|
|
5464
|
+
step: this.step,
|
|
5465
|
+
value: this.value,
|
|
5466
|
+
units: this.units || null,
|
|
5467
|
+
precision: this.precision,
|
|
5468
|
+
}),
|
|
5469
|
+
);
|
|
5412
5470
|
}
|
|
5413
|
-
this.
|
|
5471
|
+
this.replaceChildren(...renderedChildren);
|
|
5414
5472
|
|
|
5415
|
-
this.input =
|
|
5416
|
-
this.inputContainer =
|
|
5473
|
+
this.input = input;
|
|
5474
|
+
this.inputContainer = inputContainer;
|
|
5417
5475
|
this.figInputNumber = this.querySelector("fig-input-number");
|
|
5418
5476
|
this.#bindControlListeners();
|
|
5419
5477
|
|
|
@@ -5449,8 +5507,8 @@ class FigSlider extends HTMLElement {
|
|
|
5449
5507
|
this.input.setAttribute("list", this.datalist.getAttribute("id"));
|
|
5450
5508
|
}
|
|
5451
5509
|
if (this.datalist) {
|
|
5452
|
-
|
|
5453
|
-
|
|
5510
|
+
const defaultOption = [...this.datalist.querySelectorAll("option")].find(
|
|
5511
|
+
(option) => option.value === String(this.default),
|
|
5454
5512
|
);
|
|
5455
5513
|
if (defaultOption) {
|
|
5456
5514
|
defaultOption.setAttribute("default", "true");
|
|
@@ -5460,14 +5518,16 @@ class FigSlider extends HTMLElement {
|
|
|
5460
5518
|
}
|
|
5461
5519
|
|
|
5462
5520
|
connectedCallback() {
|
|
5463
|
-
if (this.
|
|
5521
|
+
if (this.initialChildren === null) {
|
|
5522
|
+
this.initialChildren = Array.from(this.childNodes);
|
|
5523
|
+
}
|
|
5464
5524
|
if (this.#canReuseRenderedMarkup()) {
|
|
5465
5525
|
this.#updateRenderedMarkup();
|
|
5466
5526
|
this.#bindControlListeners();
|
|
5467
5527
|
this.#syncValue();
|
|
5468
5528
|
return;
|
|
5469
5529
|
}
|
|
5470
|
-
this.#
|
|
5530
|
+
this.#regenerateMarkup();
|
|
5471
5531
|
}
|
|
5472
5532
|
|
|
5473
5533
|
get value() {
|
|
@@ -5752,7 +5812,7 @@ class FigSlider extends HTMLElement {
|
|
|
5752
5812
|
return;
|
|
5753
5813
|
}
|
|
5754
5814
|
this.#pendingRegeneration = false;
|
|
5755
|
-
this.#
|
|
5815
|
+
this.#regenerateMarkup();
|
|
5756
5816
|
}
|
|
5757
5817
|
|
|
5758
5818
|
static get observedAttributes() {
|
|
@@ -6581,9 +6641,18 @@ class FigInputNumber extends HTMLElement {
|
|
|
6581
6641
|
if (hasSteppers && !this.#stepperEl) {
|
|
6582
6642
|
this.#stepperEl = document.createElement("span");
|
|
6583
6643
|
this.#stepperEl.className = "fig-steppers";
|
|
6584
|
-
this.#stepperEl.
|
|
6585
|
-
|
|
6586
|
-
|
|
6644
|
+
this.#stepperEl.append(
|
|
6645
|
+
figCreateElement("button", {
|
|
6646
|
+
className: "fig-stepper-up",
|
|
6647
|
+
tabindex: "-1",
|
|
6648
|
+
"aria-label": "Increase",
|
|
6649
|
+
}),
|
|
6650
|
+
figCreateElement("button", {
|
|
6651
|
+
className: "fig-stepper-down",
|
|
6652
|
+
tabindex: "-1",
|
|
6653
|
+
"aria-label": "Decrease",
|
|
6654
|
+
}),
|
|
6655
|
+
);
|
|
6587
6656
|
this.#stepperEl.addEventListener("pointerdown", (e) => {
|
|
6588
6657
|
e.preventDefault();
|
|
6589
6658
|
e.stopPropagation();
|
|
@@ -7511,7 +7580,9 @@ class FigInputColor extends HTMLElement {
|
|
|
7511
7580
|
name !== "picker-anchor" &&
|
|
7512
7581
|
name !== "picker-experimental"
|
|
7513
7582
|
) {
|
|
7514
|
-
|
|
7583
|
+
const forwardedName = name.slice(7);
|
|
7584
|
+
if (/^on/i.test(forwardedName)) continue;
|
|
7585
|
+
attrs[forwardedName] = value;
|
|
7515
7586
|
}
|
|
7516
7587
|
}
|
|
7517
7588
|
if (!attrs["dialog-position"]) attrs["dialog-position"] = "left";
|
|
@@ -7630,41 +7701,49 @@ class FigInputColor extends HTMLElement {
|
|
|
7630
7701
|
|
|
7631
7702
|
const showAlpha = this.getAttribute("alpha") !== "false";
|
|
7632
7703
|
const disabled = this.#disabled;
|
|
7633
|
-
const disabledAttr = disabled ? " disabled" : "";
|
|
7634
|
-
|
|
7635
|
-
let html = ``;
|
|
7636
7704
|
const showText = this.getAttribute("text") !== "false";
|
|
7705
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
7706
|
+
background: this.hexOpaque,
|
|
7707
|
+
alpha: this.rgba.a,
|
|
7708
|
+
disabled,
|
|
7709
|
+
});
|
|
7710
|
+
|
|
7637
7711
|
if (showText) {
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7712
|
+
const combo = figCreateElement(
|
|
7713
|
+
"div",
|
|
7714
|
+
{ className: "input-combo" },
|
|
7715
|
+
[
|
|
7716
|
+
swatch,
|
|
7717
|
+
figCreateElement("fig-input-text", {
|
|
7718
|
+
type: "text",
|
|
7719
|
+
placeholder: "000000",
|
|
7720
|
+
value: this.hexOpaque.slice(1).toUpperCase(),
|
|
7721
|
+
disabled,
|
|
7722
|
+
}),
|
|
7723
|
+
],
|
|
7724
|
+
);
|
|
7643
7725
|
if (showAlpha) {
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
${label}
|
|
7661
|
-
</div>`;
|
|
7726
|
+
combo.appendChild(
|
|
7727
|
+
figCreateElement(
|
|
7728
|
+
"fig-tooltip",
|
|
7729
|
+
{ text: "Opacity" },
|
|
7730
|
+
figCreateElement("fig-input-number", {
|
|
7731
|
+
placeholder: "##",
|
|
7732
|
+
min: "0",
|
|
7733
|
+
max: "100",
|
|
7734
|
+
value: this.#alphaPercent,
|
|
7735
|
+
units: "%",
|
|
7736
|
+
disabled,
|
|
7737
|
+
}),
|
|
7738
|
+
),
|
|
7739
|
+
);
|
|
7740
|
+
}
|
|
7741
|
+
this.replaceChildren(combo);
|
|
7662
7742
|
} else {
|
|
7663
|
-
|
|
7743
|
+
this.replaceChildren(swatch);
|
|
7664
7744
|
}
|
|
7665
|
-
this.innerHTML = html;
|
|
7666
7745
|
|
|
7667
|
-
this.#swatch =
|
|
7746
|
+
this.#swatch = swatch;
|
|
7668
7747
|
this.#fillPicker = this.querySelector("fig-fill-picker");
|
|
7669
7748
|
this.#textInput = this.querySelector("fig-input-text:not([type=number])");
|
|
7670
7749
|
this.#alphaInput = this.querySelector("fig-input-number");
|
|
@@ -7716,7 +7795,7 @@ class FigInputColor extends HTMLElement {
|
|
|
7716
7795
|
}
|
|
7717
7796
|
|
|
7718
7797
|
const picker = document.createElement("fig-fill-picker");
|
|
7719
|
-
picker.
|
|
7798
|
+
picker.appendChild(figCreateElement("span", { hidden: true }));
|
|
7720
7799
|
picker.addEventListener("input", this.#boundFillPickerInput);
|
|
7721
7800
|
picker.addEventListener("change", this.#boundChange);
|
|
7722
7801
|
this.appendChild(picker);
|
|
@@ -8636,13 +8715,13 @@ class FigInputFill extends HTMLElement {
|
|
|
8636
8715
|
name !== "picker-anchor" &&
|
|
8637
8716
|
name !== "picker-experimental"
|
|
8638
8717
|
) {
|
|
8639
|
-
|
|
8718
|
+
const forwardedName = name.slice(7);
|
|
8719
|
+
if (/^on/i.test(forwardedName)) continue;
|
|
8720
|
+
attrs[forwardedName] = value;
|
|
8640
8721
|
}
|
|
8641
8722
|
}
|
|
8642
8723
|
if (!attrs["dialog-position"]) attrs["dialog-position"] = "left";
|
|
8643
|
-
return
|
|
8644
|
-
.map(([k, v]) => `${k}="${figEscapeAttribute(v)}"`)
|
|
8645
|
-
.join(" ");
|
|
8724
|
+
return attrs;
|
|
8646
8725
|
}
|
|
8647
8726
|
|
|
8648
8727
|
#fillPickerSwatchBackground() {
|
|
@@ -8666,6 +8745,10 @@ class FigInputFill extends HTMLElement {
|
|
|
8666
8745
|
}
|
|
8667
8746
|
case "image":
|
|
8668
8747
|
return this.#image.url ? `url(${this.#image.url})` : "#D9D9D9";
|
|
8748
|
+
case "webcam":
|
|
8749
|
+
return this.#webcam.snapshot
|
|
8750
|
+
? `url(${this.#webcam.snapshot})`
|
|
8751
|
+
: "#D9D9D9";
|
|
8669
8752
|
default:
|
|
8670
8753
|
return "#D9D9D9";
|
|
8671
8754
|
}
|
|
@@ -8733,81 +8816,97 @@ class FigInputFill extends HTMLElement {
|
|
|
8733
8816
|
syncState(this.#opacityInput, `${name} opacity`);
|
|
8734
8817
|
}
|
|
8735
8818
|
|
|
8736
|
-
#
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
${disabled ? "disabled" : ""}>
|
|
8753
|
-
</fig-input-number>
|
|
8754
|
-
</fig-tooltip>`
|
|
8755
|
-
: "";
|
|
8819
|
+
#createOpacityControl(value, disabled) {
|
|
8820
|
+
if (this.getAttribute("alpha") === "false") return null;
|
|
8821
|
+
return figCreateElement(
|
|
8822
|
+
"fig-tooltip",
|
|
8823
|
+
{ text: "Opacity" },
|
|
8824
|
+
figCreateElement("fig-input-number", {
|
|
8825
|
+
className: "fig-input-fill-opacity",
|
|
8826
|
+
placeholder: "##",
|
|
8827
|
+
min: "0",
|
|
8828
|
+
max: "100",
|
|
8829
|
+
value,
|
|
8830
|
+
units: "%",
|
|
8831
|
+
disabled,
|
|
8832
|
+
}),
|
|
8833
|
+
);
|
|
8834
|
+
}
|
|
8756
8835
|
|
|
8757
|
-
|
|
8836
|
+
#createControls(disabled) {
|
|
8837
|
+
let label = null;
|
|
8838
|
+
let opacity = 1;
|
|
8758
8839
|
|
|
8759
8840
|
switch (this.#fillType) {
|
|
8760
8841
|
case "solid":
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
type
|
|
8764
|
-
|
|
8765
|
-
placeholder
|
|
8766
|
-
value
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8842
|
+
return [
|
|
8843
|
+
figCreateElement("fig-input-text", {
|
|
8844
|
+
type: "text",
|
|
8845
|
+
className: "fig-input-fill-hex",
|
|
8846
|
+
placeholder: "000000",
|
|
8847
|
+
value: this.#solid.color.slice(1).toUpperCase(),
|
|
8848
|
+
disabled,
|
|
8849
|
+
}),
|
|
8850
|
+
this.#createOpacityControl(
|
|
8851
|
+
Math.round(this.#solid.alpha * 100),
|
|
8852
|
+
disabled,
|
|
8853
|
+
),
|
|
8854
|
+
].filter(Boolean);
|
|
8855
|
+
case "gradient":
|
|
8856
|
+
label =
|
|
8774
8857
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8775
8858
|
this.#gradient.type.slice(1);
|
|
8776
|
-
|
|
8777
|
-
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>
|
|
8778
|
-
${opacityHtml(Math.round((this.#gradient.opacity ?? 1) * 100))}`;
|
|
8859
|
+
opacity = this.#gradient.opacity ?? 1;
|
|
8779
8860
|
break;
|
|
8780
|
-
}
|
|
8781
|
-
|
|
8782
8861
|
case "image":
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
${opacityHtml(Math.round((this.#image.opacity ?? 1) * 100))}`;
|
|
8862
|
+
label = "Image";
|
|
8863
|
+
opacity = this.#image.opacity ?? 1;
|
|
8786
8864
|
break;
|
|
8787
|
-
|
|
8788
8865
|
case "video":
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
${opacityHtml(Math.round((this.#video.opacity ?? 1) * 100))}`;
|
|
8866
|
+
label = "Video";
|
|
8867
|
+
opacity = this.#video.opacity ?? 1;
|
|
8792
8868
|
break;
|
|
8793
|
-
|
|
8794
8869
|
case "webcam":
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
${opacityHtml(Math.round((this.#webcam.opacity ?? 1) * 100))}`;
|
|
8870
|
+
label = "Webcam";
|
|
8871
|
+
opacity = this.#webcam.opacity ?? 1;
|
|
8798
8872
|
break;
|
|
8799
8873
|
}
|
|
8800
8874
|
|
|
8875
|
+
return [
|
|
8876
|
+
figCreateElement(
|
|
8877
|
+
"label",
|
|
8878
|
+
{ className: "fig-input-fill-label" },
|
|
8879
|
+
label,
|
|
8880
|
+
),
|
|
8881
|
+
this.#createOpacityControl(Math.round(opacity * 100), disabled),
|
|
8882
|
+
].filter(Boolean);
|
|
8883
|
+
}
|
|
8884
|
+
|
|
8885
|
+
#render() {
|
|
8886
|
+
const disabled =
|
|
8887
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
8888
|
+
const fillPickerValue = JSON.stringify(this.value);
|
|
8801
8889
|
const fpAttrs = this.#buildFillPickerAttrs();
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8890
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
8891
|
+
background: this.#fillPickerSwatchBackground(),
|
|
8892
|
+
alpha: this.#fillPickerSwatchAlpha(),
|
|
8893
|
+
disabled,
|
|
8894
|
+
});
|
|
8895
|
+
const picker = figCreateElement(
|
|
8896
|
+
"fig-fill-picker",
|
|
8897
|
+
{
|
|
8898
|
+
value: fillPickerValue,
|
|
8899
|
+
disabled,
|
|
8900
|
+
...fpAttrs,
|
|
8901
|
+
},
|
|
8902
|
+
swatch,
|
|
8903
|
+
);
|
|
8904
|
+
const combo = figCreateElement(
|
|
8905
|
+
"div",
|
|
8906
|
+
{ className: "input-combo" },
|
|
8907
|
+
[picker, this.#createControls(disabled)],
|
|
8908
|
+
);
|
|
8909
|
+
this.replaceChildren(combo);
|
|
8811
8910
|
|
|
8812
8911
|
this.#setupEventListeners();
|
|
8813
8912
|
}
|
|
@@ -8870,6 +8969,9 @@ class FigInputFill extends HTMLElement {
|
|
|
8870
8969
|
case "video":
|
|
8871
8970
|
if (detail.video) this.#video = detail.video;
|
|
8872
8971
|
break;
|
|
8972
|
+
case "webcam":
|
|
8973
|
+
if (detail.image?.url) this.#webcam.snapshot = detail.image.url;
|
|
8974
|
+
break;
|
|
8873
8975
|
}
|
|
8874
8976
|
// Update controls (don't re-render to keep dialog open)
|
|
8875
8977
|
if (typeChanged) {
|
|
@@ -9002,7 +9104,6 @@ class FigInputFill extends HTMLElement {
|
|
|
9002
9104
|
// Update only the controls (not the fill picker) when type changes
|
|
9003
9105
|
const disabled =
|
|
9004
9106
|
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9005
|
-
const showAlpha = this.getAttribute("alpha") !== "false";
|
|
9006
9107
|
const combo = this.querySelector(".input-combo");
|
|
9007
9108
|
if (!combo) return;
|
|
9008
9109
|
|
|
@@ -9016,98 +9117,8 @@ class FigInputFill extends HTMLElement {
|
|
|
9016
9117
|
oldHex?.remove();
|
|
9017
9118
|
oldTooltips.forEach((t) => t.remove());
|
|
9018
9119
|
|
|
9019
|
-
// Generate new controls HTML
|
|
9020
|
-
let controlsHtml = "";
|
|
9021
|
-
switch (this.#fillType) {
|
|
9022
|
-
case "solid":
|
|
9023
|
-
controlsHtml = `
|
|
9024
|
-
<fig-input-text
|
|
9025
|
-
type="text"
|
|
9026
|
-
class="fig-input-fill-hex"
|
|
9027
|
-
placeholder="000000"
|
|
9028
|
-
value="${figEscapeAttribute(this.#solid.color.slice(1).toUpperCase())}"
|
|
9029
|
-
${disabled ? "disabled" : ""}>
|
|
9030
|
-
</fig-input-text>
|
|
9031
|
-
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
9032
|
-
<fig-input-number
|
|
9033
|
-
class="fig-input-fill-opacity"
|
|
9034
|
-
placeholder="##"
|
|
9035
|
-
min="0"
|
|
9036
|
-
max="100"
|
|
9037
|
-
value="${Math.round(this.#solid.alpha * 100)}"
|
|
9038
|
-
units="%"
|
|
9039
|
-
${disabled ? "disabled" : ""}>
|
|
9040
|
-
</fig-input-number>
|
|
9041
|
-
</fig-tooltip>` : ""}`;
|
|
9042
|
-
break;
|
|
9043
|
-
case "gradient": {
|
|
9044
|
-
const gradientLabel =
|
|
9045
|
-
this.#gradient.type.charAt(0).toUpperCase() +
|
|
9046
|
-
this.#gradient.type.slice(1);
|
|
9047
|
-
controlsHtml = `
|
|
9048
|
-
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>
|
|
9049
|
-
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
9050
|
-
<fig-input-number
|
|
9051
|
-
class="fig-input-fill-opacity"
|
|
9052
|
-
placeholder="##"
|
|
9053
|
-
min="0"
|
|
9054
|
-
max="100"
|
|
9055
|
-
value="${Math.round((this.#gradient.opacity ?? 1) * 100)}"
|
|
9056
|
-
units="%"
|
|
9057
|
-
${disabled ? "disabled" : ""}>
|
|
9058
|
-
</fig-input-number>
|
|
9059
|
-
</fig-tooltip>` : ""}`;
|
|
9060
|
-
break;
|
|
9061
|
-
}
|
|
9062
|
-
case "image":
|
|
9063
|
-
controlsHtml = `
|
|
9064
|
-
<label class="fig-input-fill-label">Image</label>
|
|
9065
|
-
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
9066
|
-
<fig-input-number
|
|
9067
|
-
class="fig-input-fill-opacity"
|
|
9068
|
-
placeholder="##"
|
|
9069
|
-
min="0"
|
|
9070
|
-
max="100"
|
|
9071
|
-
value="${Math.round((this.#image.opacity ?? 1) * 100)}"
|
|
9072
|
-
units="%"
|
|
9073
|
-
${disabled ? "disabled" : ""}>
|
|
9074
|
-
</fig-input-number>
|
|
9075
|
-
</fig-tooltip>` : ""}`;
|
|
9076
|
-
break;
|
|
9077
|
-
case "video":
|
|
9078
|
-
controlsHtml = `
|
|
9079
|
-
<label class="fig-input-fill-label">Video</label>
|
|
9080
|
-
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
9081
|
-
<fig-input-number
|
|
9082
|
-
class="fig-input-fill-opacity"
|
|
9083
|
-
placeholder="##"
|
|
9084
|
-
min="0"
|
|
9085
|
-
max="100"
|
|
9086
|
-
value="${Math.round((this.#video.opacity ?? 1) * 100)}"
|
|
9087
|
-
units="%"
|
|
9088
|
-
${disabled ? "disabled" : ""}>
|
|
9089
|
-
</fig-input-number>
|
|
9090
|
-
</fig-tooltip>` : ""}`;
|
|
9091
|
-
break;
|
|
9092
|
-
case "webcam":
|
|
9093
|
-
controlsHtml = `
|
|
9094
|
-
<label class="fig-input-fill-label">Webcam</label>
|
|
9095
|
-
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
9096
|
-
<fig-input-number
|
|
9097
|
-
class="fig-input-fill-opacity"
|
|
9098
|
-
placeholder="##"
|
|
9099
|
-
min="0"
|
|
9100
|
-
max="100"
|
|
9101
|
-
value="${Math.round((this.#webcam.opacity ?? 1) * 100)}"
|
|
9102
|
-
units="%"
|
|
9103
|
-
${disabled ? "disabled" : ""}>
|
|
9104
|
-
</fig-input-number>
|
|
9105
|
-
</fig-tooltip>` : ""}`;
|
|
9106
|
-
break;
|
|
9107
|
-
}
|
|
9108
|
-
|
|
9109
9120
|
// Append new controls after the fill picker
|
|
9110
|
-
combo.
|
|
9121
|
+
combo.append(...this.#createControls(disabled));
|
|
9111
9122
|
|
|
9112
9123
|
// Re-setup event listeners for the new controls
|
|
9113
9124
|
this.#opacityInput = this.querySelector(".fig-input-fill-opacity");
|
|
@@ -9496,7 +9507,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
9496
9507
|
this.hasAttribute("disabled") &&
|
|
9497
9508
|
this.getAttribute("disabled") !== "false";
|
|
9498
9509
|
|
|
9499
|
-
this.
|
|
9510
|
+
this.replaceChildren();
|
|
9500
9511
|
this.#inlinePickers = [];
|
|
9501
9512
|
this.#expandedPickers = [];
|
|
9502
9513
|
|
|
@@ -10079,11 +10090,6 @@ class FigInputGradient extends HTMLElement {
|
|
|
10079
10090
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
10080
10091
|
}
|
|
10081
10092
|
|
|
10082
|
-
#swatchSizeAttr() {
|
|
10083
|
-
const size = this.getAttribute("size");
|
|
10084
|
-
return size ? ` size="${size}"` : "";
|
|
10085
|
-
}
|
|
10086
|
-
|
|
10087
10093
|
#syncSwatchSize() {
|
|
10088
10094
|
if (!this.#swatch) return;
|
|
10089
10095
|
const size = this.getAttribute("size");
|
|
@@ -10091,16 +10097,32 @@ class FigInputGradient extends HTMLElement {
|
|
|
10091
10097
|
else this.#swatch.removeAttribute("size");
|
|
10092
10098
|
}
|
|
10093
10099
|
|
|
10094
|
-
#
|
|
10100
|
+
#createStopHandles() {
|
|
10095
10101
|
const disabled =
|
|
10096
10102
|
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
10097
|
-
const tipAttr = this.#stopHandleMode === "tip" ? ' tip="color"' : "";
|
|
10098
10103
|
return this.#gradient.stops
|
|
10099
10104
|
.map(
|
|
10100
10105
|
(stop, i) =>
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10106
|
+
figCreateElement(
|
|
10107
|
+
"fig-tooltip",
|
|
10108
|
+
{
|
|
10109
|
+
action: "manual",
|
|
10110
|
+
text: `${Math.round(stop.position)}%`,
|
|
10111
|
+
},
|
|
10112
|
+
figCreateElement("fig-handle", {
|
|
10113
|
+
drag: true,
|
|
10114
|
+
"drag-axes": "x",
|
|
10115
|
+
"drag-surface": ".fig-input-gradient-track",
|
|
10116
|
+
type: "color",
|
|
10117
|
+
tip: this.#stopHandleMode === "tip" ? "color" : null,
|
|
10118
|
+
color: this.#stopColorCSS(stop),
|
|
10119
|
+
value: `${stop.position}% 50%`,
|
|
10120
|
+
"hit-area": "4",
|
|
10121
|
+
"data-stop-index": i,
|
|
10122
|
+
disabled,
|
|
10123
|
+
}),
|
|
10124
|
+
),
|
|
10125
|
+
);
|
|
10104
10126
|
}
|
|
10105
10127
|
|
|
10106
10128
|
#ghostHandle = null;
|
|
@@ -10111,27 +10133,45 @@ class FigInputGradient extends HTMLElement {
|
|
|
10111
10133
|
this.#colorObserver = null;
|
|
10112
10134
|
const disabled = figBooleanAttribute(this, "disabled");
|
|
10113
10135
|
const mode = this.#editMode;
|
|
10136
|
+
const size = this.getAttribute("size");
|
|
10137
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
10138
|
+
background: this.#buildGradientCSS(),
|
|
10139
|
+
size: size || null,
|
|
10140
|
+
disabled,
|
|
10141
|
+
});
|
|
10114
10142
|
|
|
10115
10143
|
if (mode === "picker" && hasFigFillPicker()) {
|
|
10116
10144
|
const gradientValue = JSON.stringify(this.value);
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10145
|
+
const picker = figCreateElement(
|
|
10146
|
+
"fig-fill-picker",
|
|
10147
|
+
{
|
|
10148
|
+
mode: "gradient",
|
|
10149
|
+
value: gradientValue,
|
|
10150
|
+
disabled,
|
|
10151
|
+
},
|
|
10152
|
+
swatch,
|
|
10153
|
+
);
|
|
10154
|
+
this.replaceChildren(picker);
|
|
10155
|
+
this.#swatch = swatch;
|
|
10122
10156
|
this.#track = null;
|
|
10123
10157
|
this.#setupPickerEvents();
|
|
10124
10158
|
this.#syncFocusTarget();
|
|
10125
10159
|
return;
|
|
10126
10160
|
}
|
|
10127
10161
|
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10162
|
+
const editable = mode === "true" || mode === "picker";
|
|
10163
|
+
const track = editable
|
|
10164
|
+
? figCreateElement(
|
|
10165
|
+
"div",
|
|
10166
|
+
{ className: "fig-input-gradient-track" },
|
|
10167
|
+
this.#createStopHandles(),
|
|
10168
|
+
)
|
|
10169
|
+
: null;
|
|
10170
|
+
this.replaceChildren(...(track ? [swatch, track] : [swatch]));
|
|
10171
|
+
this.#swatch = swatch;
|
|
10172
|
+
this.#track = track;
|
|
10133
10173
|
|
|
10134
|
-
if (
|
|
10174
|
+
if (editable) {
|
|
10135
10175
|
this.#setupGhostHandle();
|
|
10136
10176
|
this.#setupEventListeners();
|
|
10137
10177
|
}
|
|
@@ -10345,7 +10385,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
10345
10385
|
|
|
10346
10386
|
if (handles.length !== stops.length) {
|
|
10347
10387
|
const ghost = this.#ghostHandle;
|
|
10348
|
-
this.#track.
|
|
10388
|
+
this.#track.replaceChildren(...this.#createStopHandles());
|
|
10349
10389
|
if (ghost) this.#track.appendChild(ghost);
|
|
10350
10390
|
this.#syncHandleMode();
|
|
10351
10391
|
this.#reobserveHandleColors();
|
|
@@ -11096,22 +11136,54 @@ class FigComboInput extends HTMLElement {
|
|
|
11096
11136
|
const currentValue = this.value;
|
|
11097
11137
|
const dropdownLabel = this.#dropdownLabel();
|
|
11098
11138
|
|
|
11099
|
-
const
|
|
11100
|
-
|
|
11101
|
-
:
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11139
|
+
const input = figCreateElement("fig-input-text", {
|
|
11140
|
+
placeholder,
|
|
11141
|
+
value: currentValue,
|
|
11142
|
+
});
|
|
11143
|
+
const icon = figCreateSvgElement(
|
|
11144
|
+
"svg",
|
|
11145
|
+
{
|
|
11146
|
+
width: "16",
|
|
11147
|
+
height: "16",
|
|
11148
|
+
viewBox: "0 0 16 16",
|
|
11149
|
+
fill: "none",
|
|
11150
|
+
},
|
|
11151
|
+
figCreateSvgElement("path", {
|
|
11152
|
+
d: "M5.87868 7.12132L8 9.24264L10.1213 7.12132",
|
|
11153
|
+
stroke: "currentColor",
|
|
11154
|
+
"stroke-opacity": "0.9",
|
|
11155
|
+
"stroke-linecap": "round",
|
|
11156
|
+
}),
|
|
11157
|
+
);
|
|
11158
|
+
const button = figCreateElement(
|
|
11159
|
+
"fig-button",
|
|
11160
|
+
{
|
|
11161
|
+
type: "select",
|
|
11162
|
+
variant: "input",
|
|
11163
|
+
icon: true,
|
|
11164
|
+
},
|
|
11165
|
+
icon,
|
|
11166
|
+
);
|
|
11167
|
+
if (!this.#usesCustomDropdown) {
|
|
11168
|
+
button.appendChild(
|
|
11169
|
+
figCreateElement(
|
|
11170
|
+
"fig-dropdown",
|
|
11171
|
+
{
|
|
11172
|
+
type: "dropdown",
|
|
11173
|
+
label: dropdownLabel,
|
|
11174
|
+
},
|
|
11175
|
+
options.map((option) =>
|
|
11176
|
+
figCreateElement("option", {}, option.trim()),
|
|
11177
|
+
),
|
|
11178
|
+
),
|
|
11179
|
+
);
|
|
11180
|
+
}
|
|
11181
|
+
this.replaceChildren(
|
|
11182
|
+
figCreateElement("div", { className: "input-combo" }, [input, button]),
|
|
11183
|
+
);
|
|
11112
11184
|
|
|
11113
|
-
this.#input =
|
|
11114
|
-
this.#button =
|
|
11185
|
+
this.#input = input;
|
|
11186
|
+
this.#button = button;
|
|
11115
11187
|
|
|
11116
11188
|
if (this.#usesCustomDropdown && this.#customDropdown && this.#button) {
|
|
11117
11189
|
if (!this.#customDropdown.hasAttribute("type")) {
|
|
@@ -11376,14 +11448,18 @@ class FigSwatch extends HTMLElement {
|
|
|
11376
11448
|
|
|
11377
11449
|
if (this.#type === "color") {
|
|
11378
11450
|
const hex = this.#toHex(bg);
|
|
11379
|
-
|
|
11380
|
-
|
|
11451
|
+
const input = figCreateElement("input", {
|
|
11452
|
+
type: "color",
|
|
11453
|
+
value: hex,
|
|
11454
|
+
});
|
|
11455
|
+
this.replaceChildren(document.createElement("div"), input);
|
|
11456
|
+
this.input = input;
|
|
11381
11457
|
if (!isVar) {
|
|
11382
11458
|
this.input.addEventListener("input", this.#boundHandleInput);
|
|
11383
11459
|
}
|
|
11384
11460
|
this.#syncA11y();
|
|
11385
11461
|
} else {
|
|
11386
|
-
this.
|
|
11462
|
+
this.replaceChildren(document.createElement("div"));
|
|
11387
11463
|
this.input = null;
|
|
11388
11464
|
this.#syncA11y();
|
|
11389
11465
|
}
|
|
@@ -13162,7 +13238,7 @@ class FigInputFile extends HTMLElement {
|
|
|
13162
13238
|
!!this.getAttribute("url") ||
|
|
13163
13239
|
!!this.getAttribute("filename");
|
|
13164
13240
|
|
|
13165
|
-
this.
|
|
13241
|
+
this.replaceChildren();
|
|
13166
13242
|
|
|
13167
13243
|
if (hasFile) {
|
|
13168
13244
|
const tooltipText = accepts
|
|
@@ -13545,10 +13621,10 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13545
13621
|
const y = pad + (1 - (pts[i].value - minVal) / range) * s;
|
|
13546
13622
|
d += (i === 0 ? "M" : "L") + x.toFixed(1) + "," + y.toFixed(1);
|
|
13547
13623
|
}
|
|
13548
|
-
return
|
|
13624
|
+
return FigEasingCurve.#createSvgIcon(d, size);
|
|
13549
13625
|
}
|
|
13550
13626
|
|
|
13551
|
-
static
|
|
13627
|
+
static #curveIconPath(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13552
13628
|
const draw = 12;
|
|
13553
13629
|
const pad = (size - draw) / 2;
|
|
13554
13630
|
const samples = 48;
|
|
@@ -13594,6 +13670,43 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13594
13670
|
const py = toY(points[i].y);
|
|
13595
13671
|
d += `${i === 0 ? "M" : "L"}${px.toFixed(1)},${py.toFixed(1)}`;
|
|
13596
13672
|
}
|
|
13673
|
+
return d;
|
|
13674
|
+
}
|
|
13675
|
+
|
|
13676
|
+
static #createSvgIcon(d, size = 24) {
|
|
13677
|
+
return figCreateSvgElement(
|
|
13678
|
+
"svg",
|
|
13679
|
+
{
|
|
13680
|
+
width: size,
|
|
13681
|
+
height: size,
|
|
13682
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
13683
|
+
fill: "none",
|
|
13684
|
+
},
|
|
13685
|
+
figCreateSvgElement("path", {
|
|
13686
|
+
d,
|
|
13687
|
+
stroke: "currentColor",
|
|
13688
|
+
"stroke-width": "1",
|
|
13689
|
+
"stroke-linecap": "round",
|
|
13690
|
+
fill: "none",
|
|
13691
|
+
}),
|
|
13692
|
+
);
|
|
13693
|
+
}
|
|
13694
|
+
|
|
13695
|
+
static #createCurveIcon(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13696
|
+
return FigEasingCurve.#createSvgIcon(
|
|
13697
|
+
FigEasingCurve.#curveIconPath(cp1x, cp1y, cp2x, cp2y, size),
|
|
13698
|
+
size,
|
|
13699
|
+
);
|
|
13700
|
+
}
|
|
13701
|
+
|
|
13702
|
+
static curveIcon(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13703
|
+
const d = FigEasingCurve.#curveIconPath(
|
|
13704
|
+
cp1x,
|
|
13705
|
+
cp1y,
|
|
13706
|
+
cp2x,
|
|
13707
|
+
cp2y,
|
|
13708
|
+
size,
|
|
13709
|
+
);
|
|
13597
13710
|
return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" fill="none"><path d="${d}" stroke="currentColor" stroke-width="1" stroke-linecap="round"/></svg>`;
|
|
13598
13711
|
}
|
|
13599
13712
|
|
|
@@ -13607,7 +13720,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13607
13720
|
this.classList.toggle("spring-mode", this.#mode === "spring");
|
|
13608
13721
|
this.classList.toggle("bezier-mode", this.#mode !== "spring");
|
|
13609
13722
|
this.#syncMetricsFromCSS();
|
|
13610
|
-
this.
|
|
13723
|
+
this.replaceChildren(...this.#createContent());
|
|
13611
13724
|
this.#cacheRefs();
|
|
13612
13725
|
if (this.#svg) {
|
|
13613
13726
|
this.#syncHandleSizes();
|
|
@@ -13618,14 +13731,6 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13618
13731
|
this.#setupEvents();
|
|
13619
13732
|
}
|
|
13620
13733
|
|
|
13621
|
-
static #escapeAttribute(value) {
|
|
13622
|
-
return String(value)
|
|
13623
|
-
.replace(/&/g, "&")
|
|
13624
|
-
.replace(/"/g, """)
|
|
13625
|
-
.replace(/</g, "<")
|
|
13626
|
-
.replace(/>/g, ">");
|
|
13627
|
-
}
|
|
13628
|
-
|
|
13629
13734
|
#canUseFigSelect() {
|
|
13630
13735
|
return ["fig-select", "fig-select-options", "fig-select-option"].every(
|
|
13631
13736
|
(tag) => {
|
|
@@ -13638,38 +13743,43 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13638
13743
|
);
|
|
13639
13744
|
}
|
|
13640
13745
|
|
|
13641
|
-
#
|
|
13642
|
-
|
|
13746
|
+
#createDropdown() {
|
|
13747
|
+
const dropdown = figCreateElement("fig-dropdown", {
|
|
13748
|
+
className: "fig-easing-curve-select",
|
|
13749
|
+
label: "Easing preset",
|
|
13750
|
+
value: this.#presetName,
|
|
13751
|
+
full: true,
|
|
13752
|
+
});
|
|
13643
13753
|
let currentGroup = undefined;
|
|
13644
|
-
let
|
|
13754
|
+
let parent = dropdown;
|
|
13645
13755
|
for (const preset of FigEasingCurve.PRESETS) {
|
|
13646
13756
|
if (!this.#isEditEnabled() && !preset.value && !preset.spring) continue;
|
|
13647
13757
|
if (preset.group !== currentGroup) {
|
|
13648
|
-
if (groupOpen) optionsHTML += "</optgroup>";
|
|
13649
|
-
groupOpen = Boolean(preset.group);
|
|
13650
|
-
if (groupOpen) {
|
|
13651
|
-
optionsHTML += `<optgroup label="${FigEasingCurve.#escapeAttribute(preset.group)}">`;
|
|
13652
|
-
}
|
|
13653
13758
|
currentGroup = preset.group;
|
|
13759
|
+
parent = currentGroup
|
|
13760
|
+
? figCreateElement("optgroup", { label: currentGroup })
|
|
13761
|
+
: dropdown;
|
|
13762
|
+
if (parent !== dropdown) dropdown.appendChild(parent);
|
|
13654
13763
|
}
|
|
13655
|
-
|
|
13656
|
-
|
|
13764
|
+
parent.appendChild(
|
|
13765
|
+
figCreateElement("option", { value: preset.name }, preset.name),
|
|
13766
|
+
);
|
|
13657
13767
|
}
|
|
13658
|
-
|
|
13659
|
-
const value = FigEasingCurve.#escapeAttribute(this.#presetName);
|
|
13660
|
-
return `<fig-dropdown class="fig-easing-curve-select" label="Easing preset" value="${value}" full>${optionsHTML}</fig-dropdown>`;
|
|
13768
|
+
return dropdown;
|
|
13661
13769
|
}
|
|
13662
13770
|
|
|
13663
|
-
#
|
|
13664
|
-
if (!this.#canUseFigSelect()) return this.#
|
|
13771
|
+
#createSelect() {
|
|
13772
|
+
if (!this.#canUseFigSelect()) return this.#createDropdown();
|
|
13665
13773
|
|
|
13666
|
-
|
|
13774
|
+
const options = document.createElement("fig-select-options");
|
|
13667
13775
|
let currentGroup = undefined;
|
|
13668
13776
|
for (const p of FigEasingCurve.PRESETS) {
|
|
13669
13777
|
if (!this.#isEditEnabled() && !p.value && !p.spring) continue;
|
|
13670
13778
|
if (p.group !== currentGroup) {
|
|
13671
13779
|
if (p.group) {
|
|
13672
|
-
|
|
13780
|
+
options.appendChild(
|
|
13781
|
+
figCreateElement("fig-separator", { label: p.group }),
|
|
13782
|
+
);
|
|
13673
13783
|
}
|
|
13674
13784
|
currentGroup = p.group;
|
|
13675
13785
|
}
|
|
@@ -13684,42 +13794,152 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13684
13794
|
this.#cp2.x,
|
|
13685
13795
|
this.#cp2.y,
|
|
13686
13796
|
];
|
|
13687
|
-
icon = FigEasingCurve
|
|
13688
|
-
}
|
|
13689
|
-
|
|
13690
|
-
|
|
13797
|
+
icon = FigEasingCurve.#createCurveIcon(...v);
|
|
13798
|
+
}
|
|
13799
|
+
options.appendChild(
|
|
13800
|
+
figCreateElement(
|
|
13801
|
+
"fig-select-option",
|
|
13802
|
+
{
|
|
13803
|
+
value: p.name,
|
|
13804
|
+
label: p.name,
|
|
13805
|
+
},
|
|
13806
|
+
[
|
|
13807
|
+
figCreateElement("span", { slot: "prepend" }, icon),
|
|
13808
|
+
figCreateElement("span", {}, p.name),
|
|
13809
|
+
],
|
|
13810
|
+
),
|
|
13811
|
+
);
|
|
13691
13812
|
}
|
|
13692
|
-
|
|
13693
|
-
|
|
13813
|
+
return figCreateElement(
|
|
13814
|
+
"fig-select",
|
|
13815
|
+
{
|
|
13816
|
+
className: "fig-easing-curve-select",
|
|
13817
|
+
label: "Easing preset",
|
|
13818
|
+
value: this.#presetName,
|
|
13819
|
+
full: true,
|
|
13820
|
+
},
|
|
13821
|
+
options,
|
|
13822
|
+
);
|
|
13823
|
+
}
|
|
13824
|
+
|
|
13825
|
+
#createHandle(className, dataHandle, label) {
|
|
13826
|
+
return figCreateSvgElement(
|
|
13827
|
+
"foreignObject",
|
|
13828
|
+
{
|
|
13829
|
+
className,
|
|
13830
|
+
"data-handle": dataHandle,
|
|
13831
|
+
width: "20",
|
|
13832
|
+
height: "20",
|
|
13833
|
+
},
|
|
13834
|
+
figCreateElement("fig-handle", {
|
|
13835
|
+
size: "small",
|
|
13836
|
+
"aria-label": label,
|
|
13837
|
+
}),
|
|
13838
|
+
);
|
|
13694
13839
|
}
|
|
13695
13840
|
|
|
13696
|
-
#
|
|
13841
|
+
#createContent() {
|
|
13697
13842
|
const size = 200;
|
|
13698
|
-
const select = this.#
|
|
13699
|
-
if (!this.#isEditEnabled()) return select;
|
|
13700
|
-
const valueInput =
|
|
13843
|
+
const select = this.#createSelect();
|
|
13844
|
+
if (!this.#isEditEnabled()) return [select];
|
|
13845
|
+
const valueInput = figCreateElement("fig-input-text", {
|
|
13846
|
+
className: "fig-easing-curve-value-input",
|
|
13847
|
+
value: this.value,
|
|
13848
|
+
full: true,
|
|
13849
|
+
});
|
|
13850
|
+
const svgChildren = [
|
|
13851
|
+
figCreateSvgElement("rect", {
|
|
13852
|
+
className: "fig-easing-curve-bounds",
|
|
13853
|
+
x: "0",
|
|
13854
|
+
y: "0",
|
|
13855
|
+
width: size,
|
|
13856
|
+
height: size,
|
|
13857
|
+
}),
|
|
13858
|
+
];
|
|
13701
13859
|
|
|
13702
13860
|
if (this.#mode === "spring") {
|
|
13703
13861
|
const targetY = 40;
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
|
-
|
|
13721
|
-
|
|
13722
|
-
|
|
13862
|
+
svgChildren.push(
|
|
13863
|
+
figCreateSvgElement("line", {
|
|
13864
|
+
className: "fig-easing-curve-target",
|
|
13865
|
+
x1: "0",
|
|
13866
|
+
y1: targetY,
|
|
13867
|
+
x2: size,
|
|
13868
|
+
y2: targetY,
|
|
13869
|
+
}),
|
|
13870
|
+
figCreateSvgElement("path", {
|
|
13871
|
+
className: "fig-easing-curve-path",
|
|
13872
|
+
}),
|
|
13873
|
+
this.#createHandle(
|
|
13874
|
+
"fig-easing-curve-handle",
|
|
13875
|
+
"bounce",
|
|
13876
|
+
"Spring bounce handle",
|
|
13877
|
+
),
|
|
13878
|
+
this.#createHandle(
|
|
13879
|
+
"fig-easing-curve-handle fig-easing-curve-duration-bar",
|
|
13880
|
+
"duration",
|
|
13881
|
+
"Spring duration handle",
|
|
13882
|
+
),
|
|
13883
|
+
);
|
|
13884
|
+
} else {
|
|
13885
|
+
svgChildren.push(
|
|
13886
|
+
figCreateSvgElement("line", {
|
|
13887
|
+
className: "fig-easing-curve-boundary",
|
|
13888
|
+
"data-boundary": "top",
|
|
13889
|
+
x1: "0",
|
|
13890
|
+
y1: "0",
|
|
13891
|
+
x2: size,
|
|
13892
|
+
y2: "0",
|
|
13893
|
+
}),
|
|
13894
|
+
figCreateSvgElement("line", {
|
|
13895
|
+
className: "fig-easing-curve-boundary",
|
|
13896
|
+
"data-boundary": "bottom",
|
|
13897
|
+
x1: "0",
|
|
13898
|
+
y1: size,
|
|
13899
|
+
x2: size,
|
|
13900
|
+
y2: size,
|
|
13901
|
+
}),
|
|
13902
|
+
figCreateSvgElement("path", {
|
|
13903
|
+
className: "fig-easing-curve-path",
|
|
13904
|
+
}),
|
|
13905
|
+
figCreateSvgElement("line", {
|
|
13906
|
+
className: "fig-easing-curve-arm",
|
|
13907
|
+
"data-arm": "1",
|
|
13908
|
+
}),
|
|
13909
|
+
figCreateSvgElement("line", {
|
|
13910
|
+
className: "fig-easing-curve-arm",
|
|
13911
|
+
"data-arm": "2",
|
|
13912
|
+
}),
|
|
13913
|
+
this.#createHandle(
|
|
13914
|
+
"fig-easing-curve-handle",
|
|
13915
|
+
"1",
|
|
13916
|
+
"First easing control point",
|
|
13917
|
+
),
|
|
13918
|
+
this.#createHandle(
|
|
13919
|
+
"fig-easing-curve-handle",
|
|
13920
|
+
"2",
|
|
13921
|
+
"Second easing control point",
|
|
13922
|
+
),
|
|
13923
|
+
);
|
|
13924
|
+
}
|
|
13925
|
+
|
|
13926
|
+
const svg = figCreateSvgElement(
|
|
13927
|
+
"svg",
|
|
13928
|
+
{
|
|
13929
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
13930
|
+
className: "fig-easing-curve-svg",
|
|
13931
|
+
},
|
|
13932
|
+
svgChildren,
|
|
13933
|
+
);
|
|
13934
|
+
return [
|
|
13935
|
+
select,
|
|
13936
|
+
figCreateElement(
|
|
13937
|
+
"div",
|
|
13938
|
+
{ className: "fig-easing-curve-svg-container" },
|
|
13939
|
+
svg,
|
|
13940
|
+
),
|
|
13941
|
+
valueInput,
|
|
13942
|
+
];
|
|
13723
13943
|
}
|
|
13724
13944
|
|
|
13725
13945
|
#readCssNumber(name, fallback) {
|
|
@@ -14054,7 +14274,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
14054
14274
|
for (const option of this.#select.querySelectorAll("fig-select-option")) {
|
|
14055
14275
|
if (option.value === optionValue) {
|
|
14056
14276
|
const prepend = option.querySelector(':scope > [slot="prepend"]');
|
|
14057
|
-
if (prepend) prepend.
|
|
14277
|
+
if (prepend) prepend.replaceChildren(icon.cloneNode(true));
|
|
14058
14278
|
}
|
|
14059
14279
|
}
|
|
14060
14280
|
}
|
|
@@ -14062,7 +14282,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
14062
14282
|
#refreshCustomPresetIcons() {
|
|
14063
14283
|
if (!this.#select) return;
|
|
14064
14284
|
if (!this.#isEditEnabled()) return;
|
|
14065
|
-
const bezierIcon = FigEasingCurve
|
|
14285
|
+
const bezierIcon = FigEasingCurve.#createCurveIcon(
|
|
14066
14286
|
this.#cp1.x,
|
|
14067
14287
|
this.#cp1.y,
|
|
14068
14288
|
this.#cp2.x,
|
|
@@ -14652,34 +14872,44 @@ class Fig3DRotate extends HTMLElement {
|
|
|
14652
14872
|
rotateY: this.#ry,
|
|
14653
14873
|
rotateZ: this.#rz,
|
|
14654
14874
|
};
|
|
14655
|
-
const
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14875
|
+
const cube = figCreateElement(
|
|
14876
|
+
"div",
|
|
14877
|
+
{ className: "fig-3d-rotate-cube" },
|
|
14878
|
+
["front", "back", "right", "left", "top", "bottom"].map((face) =>
|
|
14879
|
+
figCreateElement("div", {
|
|
14880
|
+
className: `fig-3d-rotate-face ${face}`,
|
|
14881
|
+
}),
|
|
14882
|
+
),
|
|
14883
|
+
);
|
|
14884
|
+
const container = figCreateElement(
|
|
14885
|
+
"div",
|
|
14886
|
+
{
|
|
14887
|
+
className: "fig-3d-rotate-container",
|
|
14888
|
+
tabindex: "0",
|
|
14889
|
+
},
|
|
14890
|
+
figCreateElement(
|
|
14891
|
+
"div",
|
|
14892
|
+
{ className: "fig-3d-rotate-scene" },
|
|
14893
|
+
cube,
|
|
14894
|
+
),
|
|
14895
|
+
);
|
|
14896
|
+
const fields = this.#fields.map((axis) =>
|
|
14897
|
+
figCreateElement(
|
|
14898
|
+
"fig-input-number",
|
|
14899
|
+
{
|
|
14900
|
+
name: axis,
|
|
14901
|
+
step: "1",
|
|
14902
|
+
precision: "1",
|
|
14903
|
+
value: axisValues[axis],
|
|
14904
|
+
units: "°",
|
|
14905
|
+
},
|
|
14906
|
+
figCreateElement("span", { slot: "prepend" }, axisLabels[axis]),
|
|
14907
|
+
),
|
|
14908
|
+
);
|
|
14909
|
+
|
|
14910
|
+
this.replaceChildren(container, ...fields);
|
|
14911
|
+
this.#container = container;
|
|
14912
|
+
this.#cube = cube;
|
|
14683
14913
|
this.#wireFieldInputs();
|
|
14684
14914
|
this.#updateCube();
|
|
14685
14915
|
this.#setupEvents();
|
|
@@ -14982,31 +15212,65 @@ class FigOriginGrid extends HTMLElement {
|
|
|
14982
15212
|
const cells = Array.from({ length: 9 }, (_, index) => {
|
|
14983
15213
|
const col = index % 3;
|
|
14984
15214
|
const row = Math.floor(index / 3);
|
|
14985
|
-
return
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
15215
|
+
return figCreateElement(
|
|
15216
|
+
"span",
|
|
15217
|
+
{
|
|
15218
|
+
className: "origin-grid-cell",
|
|
15219
|
+
"data-col": col,
|
|
15220
|
+
"data-row": row,
|
|
15221
|
+
},
|
|
15222
|
+
figCreateElement("span", { className: "origin-grid-dot" }),
|
|
15223
|
+
);
|
|
15224
|
+
});
|
|
14989
15225
|
|
|
14990
15226
|
const xValue = this.#x.toFixed(this.#precision);
|
|
14991
15227
|
const yValue = this.#y.toFixed(this.#precision);
|
|
14992
|
-
const
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15228
|
+
const handle = document.createElement("fig-handle");
|
|
15229
|
+
const grid = figCreateElement(
|
|
15230
|
+
"div",
|
|
15231
|
+
{
|
|
15232
|
+
className: "origin-grid",
|
|
15233
|
+
"aria-label": "Transform origin grid",
|
|
15234
|
+
},
|
|
15235
|
+
[
|
|
15236
|
+
figCreateElement(
|
|
15237
|
+
"div",
|
|
15238
|
+
{ className: "origin-grid-cells" },
|
|
15239
|
+
cells,
|
|
15240
|
+
),
|
|
15241
|
+
handle,
|
|
15242
|
+
],
|
|
15243
|
+
);
|
|
15244
|
+
const surface = figCreateElement(
|
|
15245
|
+
"div",
|
|
15246
|
+
{ className: "fig-origin-grid-surface" },
|
|
15247
|
+
grid,
|
|
15248
|
+
);
|
|
15249
|
+
const rendered = [surface];
|
|
15250
|
+
if (this.#fieldsEnabled) {
|
|
15251
|
+
const createValueInput = (name, value) =>
|
|
15252
|
+
figCreateElement(
|
|
15253
|
+
"fig-input-number",
|
|
15254
|
+
{
|
|
15255
|
+
name,
|
|
15256
|
+
value,
|
|
15257
|
+
step: "1",
|
|
15258
|
+
units: "%",
|
|
15259
|
+
},
|
|
15260
|
+
figCreateElement("span", { slot: "prepend" }, name.toUpperCase()),
|
|
15261
|
+
);
|
|
15262
|
+
rendered.push(
|
|
15263
|
+
figCreateElement("div", { className: "origin-values" }, [
|
|
15264
|
+
createValueInput("x", xValue),
|
|
15265
|
+
createValueInput("y", yValue),
|
|
15266
|
+
]),
|
|
15267
|
+
);
|
|
15268
|
+
}
|
|
15006
15269
|
|
|
15007
|
-
this
|
|
15008
|
-
this.#
|
|
15009
|
-
this.#
|
|
15270
|
+
this.replaceChildren(...rendered);
|
|
15271
|
+
this.#grid = grid;
|
|
15272
|
+
this.#cells = cells;
|
|
15273
|
+
this.#handle = handle;
|
|
15010
15274
|
this.#xInput = this.querySelector('fig-input-number[name="x"]');
|
|
15011
15275
|
this.#yInput = this.querySelector('fig-input-number[name="y"]');
|
|
15012
15276
|
this.#syncHandlePosition();
|
|
@@ -15463,7 +15727,100 @@ class FigInputJoystick extends HTMLElement {
|
|
|
15463
15727
|
}
|
|
15464
15728
|
|
|
15465
15729
|
#render() {
|
|
15466
|
-
|
|
15730
|
+
const axisLabels = this.#getAxisLabels();
|
|
15731
|
+
const planeContainer = figCreateElement("div", {
|
|
15732
|
+
className: "fig-input-joystick-plane-container",
|
|
15733
|
+
});
|
|
15734
|
+
const createAxisLabel = (position, text, noRotate = false) =>
|
|
15735
|
+
text
|
|
15736
|
+
? figCreateElement(
|
|
15737
|
+
"label",
|
|
15738
|
+
{
|
|
15739
|
+
className: [
|
|
15740
|
+
"fig-joystick-axis-label",
|
|
15741
|
+
position,
|
|
15742
|
+
noRotate ? "no-rotate" : "",
|
|
15743
|
+
]
|
|
15744
|
+
.filter(Boolean)
|
|
15745
|
+
.join(" "),
|
|
15746
|
+
"aria-hidden": "true",
|
|
15747
|
+
},
|
|
15748
|
+
text,
|
|
15749
|
+
)
|
|
15750
|
+
: null;
|
|
15751
|
+
planeContainer.append(
|
|
15752
|
+
...[
|
|
15753
|
+
createAxisLabel(
|
|
15754
|
+
"left",
|
|
15755
|
+
axisLabels.left,
|
|
15756
|
+
axisLabels.leftNoRotate,
|
|
15757
|
+
),
|
|
15758
|
+
createAxisLabel("right", axisLabels.right),
|
|
15759
|
+
createAxisLabel("top", axisLabels.top),
|
|
15760
|
+
createAxisLabel("bottom", axisLabels.bottom),
|
|
15761
|
+
].filter(Boolean),
|
|
15762
|
+
);
|
|
15763
|
+
|
|
15764
|
+
const plane = figCreateElement(
|
|
15765
|
+
"div",
|
|
15766
|
+
{ className: "fig-input-joystick-plane" },
|
|
15767
|
+
[
|
|
15768
|
+
figCreateElement("div", {
|
|
15769
|
+
className: "fig-input-joystick-guides",
|
|
15770
|
+
}),
|
|
15771
|
+
figCreateElement("fig-handle", {
|
|
15772
|
+
drag: true,
|
|
15773
|
+
"drag-surface": ".fig-input-joystick-plane",
|
|
15774
|
+
"drag-axes": "x,y",
|
|
15775
|
+
"drag-snapping": "modifier",
|
|
15776
|
+
}),
|
|
15777
|
+
],
|
|
15778
|
+
);
|
|
15779
|
+
const reset = figCreateElement(
|
|
15780
|
+
"fig-tooltip",
|
|
15781
|
+
{ text: "Reset" },
|
|
15782
|
+
figCreateElement(
|
|
15783
|
+
"fig-button",
|
|
15784
|
+
{
|
|
15785
|
+
variant: "ghost",
|
|
15786
|
+
icon: "true",
|
|
15787
|
+
className: "fig-joystick-reset",
|
|
15788
|
+
"aria-label": "Reset to default",
|
|
15789
|
+
},
|
|
15790
|
+
createFigIcon("reset", { size: "small" }),
|
|
15791
|
+
),
|
|
15792
|
+
);
|
|
15793
|
+
planeContainer.append(plane, reset);
|
|
15794
|
+
|
|
15795
|
+
const children = [planeContainer];
|
|
15796
|
+
if (this.#fieldsEnabled) {
|
|
15797
|
+
const createValueInput = (name, value) =>
|
|
15798
|
+
figCreateElement(
|
|
15799
|
+
"fig-input-number",
|
|
15800
|
+
{
|
|
15801
|
+
name,
|
|
15802
|
+
step: "1",
|
|
15803
|
+
value,
|
|
15804
|
+
min: "0",
|
|
15805
|
+
max: "100",
|
|
15806
|
+
units: "%",
|
|
15807
|
+
},
|
|
15808
|
+
figCreateElement("span", { slot: "prepend" }, name.toUpperCase()),
|
|
15809
|
+
);
|
|
15810
|
+
children.push(
|
|
15811
|
+
figCreateElement("div", { className: "joystick-values" }, [
|
|
15812
|
+
createValueInput(
|
|
15813
|
+
"x",
|
|
15814
|
+
(this.position.x * 100).toFixed(this.precision),
|
|
15815
|
+
),
|
|
15816
|
+
createValueInput(
|
|
15817
|
+
"y",
|
|
15818
|
+
(this.position.y * 100).toFixed(this.precision),
|
|
15819
|
+
),
|
|
15820
|
+
]),
|
|
15821
|
+
);
|
|
15822
|
+
}
|
|
15823
|
+
this.replaceChildren(...children);
|
|
15467
15824
|
}
|
|
15468
15825
|
|
|
15469
15826
|
#getAxisLabels() {
|
|
@@ -15492,63 +15849,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
15492
15849
|
return { left: "", right: "", top: "", bottom: "", leftNoRotate: false };
|
|
15493
15850
|
}
|
|
15494
15851
|
|
|
15495
|
-
#getInnerHTML() {
|
|
15496
|
-
const axisLabels = this.#getAxisLabels();
|
|
15497
|
-
const labelsMarkup = [
|
|
15498
|
-
axisLabels.left
|
|
15499
|
-
? `<label class="fig-joystick-axis-label left${axisLabels.leftNoRotate ? " no-rotate" : ""}" aria-hidden="true">${axisLabels.left}</label>`
|
|
15500
|
-
: "",
|
|
15501
|
-
axisLabels.right
|
|
15502
|
-
? `<label class="fig-joystick-axis-label right" aria-hidden="true">${axisLabels.right}</label>`
|
|
15503
|
-
: "",
|
|
15504
|
-
axisLabels.top
|
|
15505
|
-
? `<label class="fig-joystick-axis-label top" aria-hidden="true">${axisLabels.top}</label>`
|
|
15506
|
-
: "",
|
|
15507
|
-
axisLabels.bottom
|
|
15508
|
-
? `<label class="fig-joystick-axis-label bottom" aria-hidden="true">${axisLabels.bottom}</label>`
|
|
15509
|
-
: "",
|
|
15510
|
-
].join("");
|
|
15511
|
-
|
|
15512
|
-
return `
|
|
15513
|
-
<div class="fig-input-joystick-plane-container">
|
|
15514
|
-
${labelsMarkup}
|
|
15515
|
-
<div class="fig-input-joystick-plane">
|
|
15516
|
-
<div class="fig-input-joystick-guides"></div>
|
|
15517
|
-
<fig-handle drag drag-surface=".fig-input-joystick-plane" drag-axes="x,y" drag-snapping="modifier"></fig-handle>
|
|
15518
|
-
</div>
|
|
15519
|
-
<fig-tooltip text="Reset">
|
|
15520
|
-
<fig-button variant="ghost" icon="true" class="fig-joystick-reset" aria-label="Reset to default">
|
|
15521
|
-
<fig-icon name="reset" size="small"></fig-icon>
|
|
15522
|
-
</fig-button>
|
|
15523
|
-
</fig-tooltip>
|
|
15524
|
-
</div>
|
|
15525
|
-
${
|
|
15526
|
-
this.#fieldsEnabled
|
|
15527
|
-
? `<div class="joystick-values">
|
|
15528
|
-
<fig-input-number
|
|
15529
|
-
name="x"
|
|
15530
|
-
step="1"
|
|
15531
|
-
value="${(this.position.x * 100).toFixed(this.precision)}"
|
|
15532
|
-
min="0"
|
|
15533
|
-
max="100"
|
|
15534
|
-
units="%">
|
|
15535
|
-
<span slot="prepend">X</span>
|
|
15536
|
-
</fig-input-number>
|
|
15537
|
-
<fig-input-number
|
|
15538
|
-
name="y"
|
|
15539
|
-
step="1"
|
|
15540
|
-
min="0"
|
|
15541
|
-
max="100"
|
|
15542
|
-
value="${(this.position.y * 100).toFixed(this.precision)}"
|
|
15543
|
-
units="%">
|
|
15544
|
-
<span slot="prepend">Y</span>
|
|
15545
|
-
</fig-input-number>
|
|
15546
|
-
</div>`
|
|
15547
|
-
: ""
|
|
15548
|
-
}
|
|
15549
|
-
`;
|
|
15550
|
-
}
|
|
15551
|
-
|
|
15552
15852
|
#setupListeners() {
|
|
15553
15853
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
15554
15854
|
this.cursor = this.querySelector("fig-handle");
|
|
@@ -16078,314 +16378,6 @@ class FigPreview extends HTMLElement {
|
|
|
16078
16378
|
}
|
|
16079
16379
|
figDefineElement("fig-preview", FigPreview);
|
|
16080
16380
|
|
|
16081
|
-
/**
|
|
16082
|
-
* Compact swatch previewing gradient color-space interpolation.
|
|
16083
|
-
* Polar: CSS conic-gradient masked (SVG data-URL, round linecaps) to an arc.
|
|
16084
|
-
* Non-polar: CSS linear-gradient masked to a horizontal round-capped stroke.
|
|
16085
|
-
* Accepts the same `value` shape as fig-input-gradient / fig-fill-picker.
|
|
16086
|
-
*
|
|
16087
|
-
* @element fig-interpolation-swatch
|
|
16088
|
-
* @attr {string} value - JSON `{ type: "gradient", gradient: { … } }` (or a bare gradient object)
|
|
16089
|
-
* @attr {string} size - `small` (default, 24px) or `large` (32px)
|
|
16090
|
-
*/
|
|
16091
|
-
class FigInterpolationSwatch extends HTMLElement {
|
|
16092
|
-
static #HUE_SPACES = new Set(["oklch", "hsl"]);
|
|
16093
|
-
static #CX = 10;
|
|
16094
|
-
static #CY = 10;
|
|
16095
|
-
static #R = 8;
|
|
16096
|
-
static #STROKE = 3;
|
|
16097
|
-
// Polar endpoints ≈ 10 o'clock → 2 o'clock (SVG deg: 0 = east, CW).
|
|
16098
|
-
// CSS conic `from` is 0 = north; convert with +90.
|
|
16099
|
-
static #START_DEG = 210;
|
|
16100
|
-
static #DEFAULT_GRADIENT = {
|
|
16101
|
-
type: "linear",
|
|
16102
|
-
angle: 135,
|
|
16103
|
-
interpolationSpace: "srgb",
|
|
16104
|
-
hueInterpolation: "shorter",
|
|
16105
|
-
stops: [
|
|
16106
|
-
{ color: "#FF0000", position: 0, opacity: 100 },
|
|
16107
|
-
{ color: "#4F9EFF", position: 100, opacity: 100 },
|
|
16108
|
-
],
|
|
16109
|
-
};
|
|
16110
|
-
|
|
16111
|
-
#rendered = false;
|
|
16112
|
-
#svgEl = null;
|
|
16113
|
-
#fillEl = null;
|
|
16114
|
-
#gradient = { ...FigInterpolationSwatch.#DEFAULT_GRADIENT };
|
|
16115
|
-
|
|
16116
|
-
static get observedAttributes() {
|
|
16117
|
-
return ["value"];
|
|
16118
|
-
}
|
|
16119
|
-
|
|
16120
|
-
get value() {
|
|
16121
|
-
return {
|
|
16122
|
-
type: "gradient",
|
|
16123
|
-
gradient: { ...this.#gradient },
|
|
16124
|
-
};
|
|
16125
|
-
}
|
|
16126
|
-
|
|
16127
|
-
set value(val) {
|
|
16128
|
-
if (val == null || val === "") {
|
|
16129
|
-
this.removeAttribute("value");
|
|
16130
|
-
return;
|
|
16131
|
-
}
|
|
16132
|
-
if (typeof val === "string") {
|
|
16133
|
-
this.setAttribute("value", val);
|
|
16134
|
-
return;
|
|
16135
|
-
}
|
|
16136
|
-
this.setAttribute("value", JSON.stringify(val));
|
|
16137
|
-
}
|
|
16138
|
-
|
|
16139
|
-
connectedCallback() {
|
|
16140
|
-
this.#ensureA11y();
|
|
16141
|
-
this.#parseValue();
|
|
16142
|
-
this.#render();
|
|
16143
|
-
this.#updatePreview();
|
|
16144
|
-
}
|
|
16145
|
-
|
|
16146
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
16147
|
-
if (oldValue === newValue) return;
|
|
16148
|
-
if (name !== "value") return;
|
|
16149
|
-
this.#parseValue();
|
|
16150
|
-
if (this.#rendered) this.#updatePreview();
|
|
16151
|
-
}
|
|
16152
|
-
|
|
16153
|
-
#ensureA11y() {
|
|
16154
|
-
const named =
|
|
16155
|
-
this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby");
|
|
16156
|
-
if (!named && !this.hasAttribute("aria-hidden")) {
|
|
16157
|
-
this.setAttribute("aria-hidden", "true");
|
|
16158
|
-
}
|
|
16159
|
-
}
|
|
16160
|
-
|
|
16161
|
-
#parseValue() {
|
|
16162
|
-
const valueAttr = this.getAttribute("value");
|
|
16163
|
-
if (!valueAttr) {
|
|
16164
|
-
this.#gradient = {
|
|
16165
|
-
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
16166
|
-
stops: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({
|
|
16167
|
-
...s,
|
|
16168
|
-
})),
|
|
16169
|
-
};
|
|
16170
|
-
return;
|
|
16171
|
-
}
|
|
16172
|
-
try {
|
|
16173
|
-
const parsed = JSON.parse(valueAttr);
|
|
16174
|
-
const gradient = parsed?.type === "gradient" && parsed.gradient
|
|
16175
|
-
? parsed.gradient
|
|
16176
|
-
: parsed?.gradient
|
|
16177
|
-
? parsed.gradient
|
|
16178
|
-
: parsed;
|
|
16179
|
-
if (!gradient || typeof gradient !== "object") return;
|
|
16180
|
-
this.#gradient = this.#normalizeGradient({
|
|
16181
|
-
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
16182
|
-
...gradient,
|
|
16183
|
-
});
|
|
16184
|
-
} catch {
|
|
16185
|
-
// Keep current/default gradient on invalid JSON.
|
|
16186
|
-
}
|
|
16187
|
-
}
|
|
16188
|
-
|
|
16189
|
-
#normalizeGradient(gradient) {
|
|
16190
|
-
const next = { ...(gradient ?? {}) };
|
|
16191
|
-
const interpolationSpace = String(
|
|
16192
|
-
next.interpolationSpace ?? "srgb",
|
|
16193
|
-
).toLowerCase();
|
|
16194
|
-
const hueInterpolation = String(
|
|
16195
|
-
next.hueInterpolation ?? "shorter",
|
|
16196
|
-
).toLowerCase();
|
|
16197
|
-
const stops = Array.isArray(next.stops)
|
|
16198
|
-
? next.stops.map((stop) => ({
|
|
16199
|
-
color: String(stop?.color || "#D9D9D9").replace(
|
|
16200
|
-
/^(#(?:[0-9a-f]{6})).*/i,
|
|
16201
|
-
"$1",
|
|
16202
|
-
),
|
|
16203
|
-
position: stop?.position ?? 0,
|
|
16204
|
-
opacity: stop?.opacity ?? 100,
|
|
16205
|
-
}))
|
|
16206
|
-
: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({ ...s }));
|
|
16207
|
-
if (stops.length < 2) {
|
|
16208
|
-
return {
|
|
16209
|
-
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
16210
|
-
stops: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({
|
|
16211
|
-
...s,
|
|
16212
|
-
})),
|
|
16213
|
-
};
|
|
16214
|
-
}
|
|
16215
|
-
return {
|
|
16216
|
-
type: ["linear", "radial", "angular"].includes(next.type)
|
|
16217
|
-
? next.type
|
|
16218
|
-
: "linear",
|
|
16219
|
-
angle: Number.isFinite(Number(next.angle)) ? Number(next.angle) : 135,
|
|
16220
|
-
interpolationSpace,
|
|
16221
|
-
hueInterpolation,
|
|
16222
|
-
stops,
|
|
16223
|
-
};
|
|
16224
|
-
}
|
|
16225
|
-
|
|
16226
|
-
#isPolar() {
|
|
16227
|
-
return FigInterpolationSwatch.#HUE_SPACES.has(
|
|
16228
|
-
this.#gradient.interpolationSpace || "srgb",
|
|
16229
|
-
);
|
|
16230
|
-
}
|
|
16231
|
-
|
|
16232
|
-
#hueForColor(color) {
|
|
16233
|
-
const { r, g, b } = figHexToRGB(color);
|
|
16234
|
-
if (this.#gradient.interpolationSpace === "hsl") {
|
|
16235
|
-
return figRgbToHsl(r, g, b).h;
|
|
16236
|
-
}
|
|
16237
|
-
const lab = figRGBToOklab(r, g, b);
|
|
16238
|
-
const hue = figOklabToOklch(lab.l, lab.a, lab.b).h;
|
|
16239
|
-
return ((hue % 360) + 360) % 360;
|
|
16240
|
-
}
|
|
16241
|
-
|
|
16242
|
-
#polarArcGeometry() {
|
|
16243
|
-
const stops = this.#sortedStops();
|
|
16244
|
-
const startHue = this.#hueForColor(stops[0]?.color || "#FF0000");
|
|
16245
|
-
const endHue = this.#hueForColor(
|
|
16246
|
-
stops[stops.length - 1]?.color || "#4F9EFF",
|
|
16247
|
-
);
|
|
16248
|
-
const startDeg = FigInterpolationSwatch.#START_DEG - startHue;
|
|
16249
|
-
const endDeg = FigInterpolationSwatch.#START_DEG - endHue;
|
|
16250
|
-
const clockwiseSweep = ((endDeg - startDeg) % 360 + 360) % 360;
|
|
16251
|
-
const counterclockwiseSweep =
|
|
16252
|
-
clockwiseSweep === 0 ? 0 : clockwiseSweep - 360;
|
|
16253
|
-
const method = this.#gradient.hueInterpolation || "shorter";
|
|
16254
|
-
|
|
16255
|
-
let sweepDeg;
|
|
16256
|
-
if (method === "increasing") {
|
|
16257
|
-
sweepDeg = counterclockwiseSweep;
|
|
16258
|
-
} else if (method === "decreasing") {
|
|
16259
|
-
sweepDeg = clockwiseSweep;
|
|
16260
|
-
} else if (method === "longer") {
|
|
16261
|
-
sweepDeg =
|
|
16262
|
-
clockwiseSweep < 180 ? counterclockwiseSweep : clockwiseSweep;
|
|
16263
|
-
} else {
|
|
16264
|
-
sweepDeg =
|
|
16265
|
-
clockwiseSweep <= 180 ? clockwiseSweep : counterclockwiseSweep;
|
|
16266
|
-
}
|
|
16267
|
-
|
|
16268
|
-
// A round cap extends beyond the path endpoint. Inset the centerline so
|
|
16269
|
-
// the visible cap edges, rather than their centers, land on the hues.
|
|
16270
|
-
const direction = Math.sign(sweepDeg);
|
|
16271
|
-
const capAngle =
|
|
16272
|
-
(Math.asin(FigInterpolationSwatch.#STROKE / 2 / FigInterpolationSwatch.#R) *
|
|
16273
|
-
180) /
|
|
16274
|
-
Math.PI;
|
|
16275
|
-
const inset = Math.min(
|
|
16276
|
-
capAngle,
|
|
16277
|
-
Math.max(0, (Math.abs(sweepDeg) - 0.01) / 2),
|
|
16278
|
-
);
|
|
16279
|
-
return {
|
|
16280
|
-
startDeg: startDeg + direction * inset,
|
|
16281
|
-
sweepDeg: sweepDeg - direction * inset * 2,
|
|
16282
|
-
};
|
|
16283
|
-
}
|
|
16284
|
-
|
|
16285
|
-
#pointOnCircle(deg) {
|
|
16286
|
-
const rad = (deg * Math.PI) / 180;
|
|
16287
|
-
return {
|
|
16288
|
-
x: FigInterpolationSwatch.#CX + FigInterpolationSwatch.#R * Math.cos(rad),
|
|
16289
|
-
y: FigInterpolationSwatch.#CY + FigInterpolationSwatch.#R * Math.sin(rad),
|
|
16290
|
-
};
|
|
16291
|
-
}
|
|
16292
|
-
|
|
16293
|
-
#arcMaskPath(startDeg, sweepDeg) {
|
|
16294
|
-
const endDeg = startDeg + sweepDeg;
|
|
16295
|
-
const start = this.#pointOnCircle(startDeg);
|
|
16296
|
-
const end = this.#pointOnCircle(endDeg);
|
|
16297
|
-
const largeArc = Math.abs(sweepDeg) > 180 ? 1 : 0;
|
|
16298
|
-
const sweepFlag = sweepDeg >= 0 ? 1 : 0;
|
|
16299
|
-
return `M ${start.x} ${start.y} A ${FigInterpolationSwatch.#R} ${FigInterpolationSwatch.#R} 0 ${largeArc} ${sweepFlag} ${end.x} ${end.y}`;
|
|
16300
|
-
}
|
|
16301
|
-
|
|
16302
|
-
#lineMaskPath() {
|
|
16303
|
-
const start = this.#pointOnCircle(180);
|
|
16304
|
-
const end = this.#pointOnCircle(0);
|
|
16305
|
-
return `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
|
|
16306
|
-
}
|
|
16307
|
-
|
|
16308
|
-
#maskImageForPath(d) {
|
|
16309
|
-
const stroke = FigInterpolationSwatch.#STROKE;
|
|
16310
|
-
const svg =
|
|
16311
|
-
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">` +
|
|
16312
|
-
`<path d="${d}" fill="none" stroke="white" stroke-width="${stroke}" ` +
|
|
16313
|
-
`stroke-linecap="round" stroke-linejoin="round"/>` +
|
|
16314
|
-
`</svg>`;
|
|
16315
|
-
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
16316
|
-
}
|
|
16317
|
-
|
|
16318
|
-
#sortedStops() {
|
|
16319
|
-
return [...this.#gradient.stops].sort(
|
|
16320
|
-
(a, b) => (a.position ?? 0) - (b.position ?? 0),
|
|
16321
|
-
);
|
|
16322
|
-
}
|
|
16323
|
-
|
|
16324
|
-
#cssInterpolationClause() {
|
|
16325
|
-
const space = this.#gradient.interpolationSpace || "srgb";
|
|
16326
|
-
if (space === "srgb") return "";
|
|
16327
|
-
if (FigInterpolationSwatch.#HUE_SPACES.has(space)) {
|
|
16328
|
-
return ` in ${space} ${this.#gradient.hueInterpolation || "shorter"} hue`;
|
|
16329
|
-
}
|
|
16330
|
-
return ` in ${space}`;
|
|
16331
|
-
}
|
|
16332
|
-
|
|
16333
|
-
#previewBackground() {
|
|
16334
|
-
const stops = this.#sortedStops();
|
|
16335
|
-
const clause = this.#cssInterpolationClause();
|
|
16336
|
-
if (this.#isPolar()) {
|
|
16337
|
-
// Fixed hue wheel. The mask maps gradient endpoint hues onto this wheel.
|
|
16338
|
-
const cssFrom = (FigInterpolationSwatch.#START_DEG + 90) % 360;
|
|
16339
|
-
const space = this.#gradient.interpolationSpace;
|
|
16340
|
-
const wheelColor = (hue) =>
|
|
16341
|
-
space === "oklch"
|
|
16342
|
-
? `oklch(65% 0.25 ${hue})`
|
|
16343
|
-
: `hsl(${hue} 100% 50%)`;
|
|
16344
|
-
const wheelStops = [0, 300, 240, 180, 120, 60, 0]
|
|
16345
|
-
.map(wheelColor)
|
|
16346
|
-
.join(", ");
|
|
16347
|
-
return `conic-gradient(from ${cssFrom}deg in ${space} decreasing hue, ${wheelStops})`;
|
|
16348
|
-
}
|
|
16349
|
-
const stopList = stops
|
|
16350
|
-
.map((s) => `${s.color} ${s.position ?? 0}%`)
|
|
16351
|
-
.join(", ");
|
|
16352
|
-
return `linear-gradient(90deg${clause}, ${stopList})`;
|
|
16353
|
-
}
|
|
16354
|
-
|
|
16355
|
-
#render() {
|
|
16356
|
-
if (this.#rendered) return;
|
|
16357
|
-
const stroke = FigInterpolationSwatch.#STROKE;
|
|
16358
|
-
this.innerHTML = `
|
|
16359
|
-
<svg class="fig-interpolation-swatch-svg" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
16360
|
-
<circle class="fig-interpolation-swatch-rim" cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="${stroke}"/>
|
|
16361
|
-
</svg>
|
|
16362
|
-
<div class="fig-interpolation-swatch-fill" aria-hidden="true"></div>
|
|
16363
|
-
`;
|
|
16364
|
-
this.#svgEl = this.querySelector(".fig-interpolation-swatch-svg");
|
|
16365
|
-
this.#fillEl = this.querySelector(".fig-interpolation-swatch-fill");
|
|
16366
|
-
this.#rendered = true;
|
|
16367
|
-
}
|
|
16368
|
-
|
|
16369
|
-
#updatePreview() {
|
|
16370
|
-
if (!this.#fillEl) return;
|
|
16371
|
-
|
|
16372
|
-
const polar = this.#isPolar();
|
|
16373
|
-
if (this.#svgEl) this.#svgEl.style.display = polar ? "" : "none";
|
|
16374
|
-
|
|
16375
|
-
const d = polar
|
|
16376
|
-
? (() => {
|
|
16377
|
-
const { startDeg, sweepDeg } = this.#polarArcGeometry();
|
|
16378
|
-
return this.#arcMaskPath(startDeg, sweepDeg);
|
|
16379
|
-
})()
|
|
16380
|
-
: this.#lineMaskPath();
|
|
16381
|
-
const mask = this.#maskImageForPath(d);
|
|
16382
|
-
this.#fillEl.style.setProperty("-webkit-mask-image", mask);
|
|
16383
|
-
this.#fillEl.style.maskImage = mask;
|
|
16384
|
-
this.#fillEl.style.background = this.#previewBackground();
|
|
16385
|
-
}
|
|
16386
|
-
}
|
|
16387
|
-
figDefineElement("fig-interpolation-swatch", FigInterpolationSwatch);
|
|
16388
|
-
|
|
16389
16381
|
/** @type {Record<string, string | { medium: string, small: string }>} */
|
|
16390
16382
|
const FIG_ICON_TOKENS = {
|
|
16391
16383
|
chevron: { medium: "--icon-24-chevron", small: "--icon-16-chevron" },
|
|
@@ -16575,11 +16567,22 @@ class FigColorTip extends HTMLElement {
|
|
|
16575
16567
|
}
|
|
16576
16568
|
|
|
16577
16569
|
#render() {
|
|
16570
|
+
this.#teardownListeners();
|
|
16578
16571
|
const mode = this.#controlMode;
|
|
16579
16572
|
if (mode === "add" || mode === "remove") {
|
|
16580
16573
|
const iconName = mode === "add" ? "add" : "minus";
|
|
16581
16574
|
const label = this.getAttribute("aria-label") || (mode === "add" ? "Add color stop" : "Remove color stop");
|
|
16582
|
-
this.
|
|
16575
|
+
this.replaceChildren(
|
|
16576
|
+
figCreateElement(
|
|
16577
|
+
"fig-button",
|
|
16578
|
+
{
|
|
16579
|
+
icon: true,
|
|
16580
|
+
variant: "ghost",
|
|
16581
|
+
"aria-label": label,
|
|
16582
|
+
},
|
|
16583
|
+
createFigIcon(iconName),
|
|
16584
|
+
),
|
|
16585
|
+
);
|
|
16583
16586
|
this.#fillPicker = null;
|
|
16584
16587
|
this.#swatch = null;
|
|
16585
16588
|
this.addEventListener("click", this.#handleControlClick);
|
|
@@ -16591,7 +16594,6 @@ class FigColorTip extends HTMLElement {
|
|
|
16591
16594
|
const rawValue = (this.getAttribute("value") || "").trim();
|
|
16592
16595
|
const color = this.#normalizeColor(rawValue);
|
|
16593
16596
|
const alpha = this.#extractAlpha(rawValue);
|
|
16594
|
-
const alphaAttr = this.#alphaEnabled ? "" : 'alpha="false"';
|
|
16595
16597
|
const pickerValue =
|
|
16596
16598
|
alpha < 1
|
|
16597
16599
|
? JSON.stringify({
|
|
@@ -16600,16 +16602,28 @@ class FigColorTip extends HTMLElement {
|
|
|
16600
16602
|
opacity: Math.round(alpha * 100),
|
|
16601
16603
|
})
|
|
16602
16604
|
: JSON.stringify({ type: "solid", color });
|
|
16603
|
-
const
|
|
16604
|
-
|
|
16605
|
-
?
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16605
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
16606
|
+
background: color,
|
|
16607
|
+
alpha: alpha < 1 ? alpha : null,
|
|
16608
|
+
});
|
|
16609
|
+
let picker = null;
|
|
16610
|
+
if (hasFigFillPicker()) {
|
|
16611
|
+
picker = figCreateElement(
|
|
16612
|
+
"fig-fill-picker",
|
|
16613
|
+
{
|
|
16614
|
+
mode: "solid",
|
|
16615
|
+
alpha: this.#alphaEnabled ? null : "false",
|
|
16616
|
+
value: pickerValue,
|
|
16617
|
+
},
|
|
16618
|
+
swatch,
|
|
16619
|
+
);
|
|
16620
|
+
this.replaceChildren(picker);
|
|
16621
|
+
} else {
|
|
16622
|
+
this.replaceChildren(swatch);
|
|
16623
|
+
}
|
|
16609
16624
|
|
|
16610
|
-
this.#fillPicker =
|
|
16611
|
-
this.#swatch =
|
|
16612
|
-
this.#teardownListeners();
|
|
16625
|
+
this.#fillPicker = picker;
|
|
16626
|
+
this.#swatch = swatch;
|
|
16613
16627
|
this.#fillPicker?.addEventListener("input", this.#boundHandleInput);
|
|
16614
16628
|
this.#fillPicker?.addEventListener("change", this.#boundHandleChange);
|
|
16615
16629
|
if (!this.#fillPicker) {
|