@rogieking/figui3 8.3.0 → 8.4.1
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 +822 -800
- 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() {
|
|
@@ -8662,10 +8741,22 @@ class FigInputFill extends HTMLElement {
|
|
|
8662
8741
|
})
|
|
8663
8742
|
.join(", ");
|
|
8664
8743
|
const interpolation = gradientInterpolationClause(this.#gradient);
|
|
8665
|
-
|
|
8744
|
+
const interpolationSuffix = interpolation ? ` ${interpolation}` : "";
|
|
8745
|
+
switch (this.#gradient.type) {
|
|
8746
|
+
case "radial":
|
|
8747
|
+
return `radial-gradient(circle${interpolationSuffix}, ${stops})`;
|
|
8748
|
+
case "angular":
|
|
8749
|
+
return `conic-gradient(from ${this.#gradient.angle}deg${interpolationSuffix}, ${stops})`;
|
|
8750
|
+
default:
|
|
8751
|
+
return `linear-gradient(${this.#gradient.angle}deg${interpolationSuffix}, ${stops})`;
|
|
8752
|
+
}
|
|
8666
8753
|
}
|
|
8667
8754
|
case "image":
|
|
8668
8755
|
return this.#image.url ? `url(${this.#image.url})` : "#D9D9D9";
|
|
8756
|
+
case "webcam":
|
|
8757
|
+
return this.#webcam.snapshot
|
|
8758
|
+
? `url(${this.#webcam.snapshot})`
|
|
8759
|
+
: "#D9D9D9";
|
|
8669
8760
|
default:
|
|
8670
8761
|
return "#D9D9D9";
|
|
8671
8762
|
}
|
|
@@ -8733,81 +8824,97 @@ class FigInputFill extends HTMLElement {
|
|
|
8733
8824
|
syncState(this.#opacityInput, `${name} opacity`);
|
|
8734
8825
|
}
|
|
8735
8826
|
|
|
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
|
-
: "";
|
|
8827
|
+
#createOpacityControl(value, disabled) {
|
|
8828
|
+
if (this.getAttribute("alpha") === "false") return null;
|
|
8829
|
+
return figCreateElement(
|
|
8830
|
+
"fig-tooltip",
|
|
8831
|
+
{ text: "Opacity" },
|
|
8832
|
+
figCreateElement("fig-input-number", {
|
|
8833
|
+
className: "fig-input-fill-opacity",
|
|
8834
|
+
placeholder: "##",
|
|
8835
|
+
min: "0",
|
|
8836
|
+
max: "100",
|
|
8837
|
+
value,
|
|
8838
|
+
units: "%",
|
|
8839
|
+
disabled,
|
|
8840
|
+
}),
|
|
8841
|
+
);
|
|
8842
|
+
}
|
|
8756
8843
|
|
|
8757
|
-
|
|
8844
|
+
#createControls(disabled) {
|
|
8845
|
+
let label = null;
|
|
8846
|
+
let opacity = 1;
|
|
8758
8847
|
|
|
8759
8848
|
switch (this.#fillType) {
|
|
8760
8849
|
case "solid":
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
type
|
|
8764
|
-
|
|
8765
|
-
placeholder
|
|
8766
|
-
value
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8850
|
+
return [
|
|
8851
|
+
figCreateElement("fig-input-text", {
|
|
8852
|
+
type: "text",
|
|
8853
|
+
className: "fig-input-fill-hex",
|
|
8854
|
+
placeholder: "000000",
|
|
8855
|
+
value: this.#solid.color.slice(1).toUpperCase(),
|
|
8856
|
+
disabled,
|
|
8857
|
+
}),
|
|
8858
|
+
this.#createOpacityControl(
|
|
8859
|
+
Math.round(this.#solid.alpha * 100),
|
|
8860
|
+
disabled,
|
|
8861
|
+
),
|
|
8862
|
+
].filter(Boolean);
|
|
8863
|
+
case "gradient":
|
|
8864
|
+
label =
|
|
8774
8865
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8775
8866
|
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))}`;
|
|
8867
|
+
opacity = this.#gradient.opacity ?? 1;
|
|
8779
8868
|
break;
|
|
8780
|
-
}
|
|
8781
|
-
|
|
8782
8869
|
case "image":
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
${opacityHtml(Math.round((this.#image.opacity ?? 1) * 100))}`;
|
|
8870
|
+
label = "Image";
|
|
8871
|
+
opacity = this.#image.opacity ?? 1;
|
|
8786
8872
|
break;
|
|
8787
|
-
|
|
8788
8873
|
case "video":
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
${opacityHtml(Math.round((this.#video.opacity ?? 1) * 100))}`;
|
|
8874
|
+
label = "Video";
|
|
8875
|
+
opacity = this.#video.opacity ?? 1;
|
|
8792
8876
|
break;
|
|
8793
|
-
|
|
8794
8877
|
case "webcam":
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
${opacityHtml(Math.round((this.#webcam.opacity ?? 1) * 100))}`;
|
|
8878
|
+
label = "Webcam";
|
|
8879
|
+
opacity = this.#webcam.opacity ?? 1;
|
|
8798
8880
|
break;
|
|
8799
8881
|
}
|
|
8800
8882
|
|
|
8883
|
+
return [
|
|
8884
|
+
figCreateElement(
|
|
8885
|
+
"label",
|
|
8886
|
+
{ className: "fig-input-fill-label" },
|
|
8887
|
+
label,
|
|
8888
|
+
),
|
|
8889
|
+
this.#createOpacityControl(Math.round(opacity * 100), disabled),
|
|
8890
|
+
].filter(Boolean);
|
|
8891
|
+
}
|
|
8892
|
+
|
|
8893
|
+
#render() {
|
|
8894
|
+
const disabled =
|
|
8895
|
+
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
8896
|
+
const fillPickerValue = JSON.stringify(this.value);
|
|
8801
8897
|
const fpAttrs = this.#buildFillPickerAttrs();
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8898
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
8899
|
+
background: this.#fillPickerSwatchBackground(),
|
|
8900
|
+
alpha: this.#fillPickerSwatchAlpha(),
|
|
8901
|
+
disabled,
|
|
8902
|
+
});
|
|
8903
|
+
const picker = figCreateElement(
|
|
8904
|
+
"fig-fill-picker",
|
|
8905
|
+
{
|
|
8906
|
+
value: fillPickerValue,
|
|
8907
|
+
disabled,
|
|
8908
|
+
...fpAttrs,
|
|
8909
|
+
},
|
|
8910
|
+
swatch,
|
|
8911
|
+
);
|
|
8912
|
+
const combo = figCreateElement(
|
|
8913
|
+
"div",
|
|
8914
|
+
{ className: "input-combo" },
|
|
8915
|
+
[picker, this.#createControls(disabled)],
|
|
8916
|
+
);
|
|
8917
|
+
this.replaceChildren(combo);
|
|
8811
8918
|
|
|
8812
8919
|
this.#setupEventListeners();
|
|
8813
8920
|
}
|
|
@@ -8870,6 +8977,9 @@ class FigInputFill extends HTMLElement {
|
|
|
8870
8977
|
case "video":
|
|
8871
8978
|
if (detail.video) this.#video = detail.video;
|
|
8872
8979
|
break;
|
|
8980
|
+
case "webcam":
|
|
8981
|
+
if (detail.image?.url) this.#webcam.snapshot = detail.image.url;
|
|
8982
|
+
break;
|
|
8873
8983
|
}
|
|
8874
8984
|
// Update controls (don't re-render to keep dialog open)
|
|
8875
8985
|
if (typeChanged) {
|
|
@@ -9002,7 +9112,6 @@ class FigInputFill extends HTMLElement {
|
|
|
9002
9112
|
// Update only the controls (not the fill picker) when type changes
|
|
9003
9113
|
const disabled =
|
|
9004
9114
|
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
9005
|
-
const showAlpha = this.getAttribute("alpha") !== "false";
|
|
9006
9115
|
const combo = this.querySelector(".input-combo");
|
|
9007
9116
|
if (!combo) return;
|
|
9008
9117
|
|
|
@@ -9016,98 +9125,8 @@ class FigInputFill extends HTMLElement {
|
|
|
9016
9125
|
oldHex?.remove();
|
|
9017
9126
|
oldTooltips.forEach((t) => t.remove());
|
|
9018
9127
|
|
|
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
9128
|
// Append new controls after the fill picker
|
|
9110
|
-
combo.
|
|
9129
|
+
combo.append(...this.#createControls(disabled));
|
|
9111
9130
|
|
|
9112
9131
|
// Re-setup event listeners for the new controls
|
|
9113
9132
|
this.#opacityInput = this.querySelector(".fig-input-fill-opacity");
|
|
@@ -9496,7 +9515,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
9496
9515
|
this.hasAttribute("disabled") &&
|
|
9497
9516
|
this.getAttribute("disabled") !== "false";
|
|
9498
9517
|
|
|
9499
|
-
this.
|
|
9518
|
+
this.replaceChildren();
|
|
9500
9519
|
this.#inlinePickers = [];
|
|
9501
9520
|
this.#expandedPickers = [];
|
|
9502
9521
|
|
|
@@ -10079,11 +10098,6 @@ class FigInputGradient extends HTMLElement {
|
|
|
10079
10098
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
10080
10099
|
}
|
|
10081
10100
|
|
|
10082
|
-
#swatchSizeAttr() {
|
|
10083
|
-
const size = this.getAttribute("size");
|
|
10084
|
-
return size ? ` size="${size}"` : "";
|
|
10085
|
-
}
|
|
10086
|
-
|
|
10087
10101
|
#syncSwatchSize() {
|
|
10088
10102
|
if (!this.#swatch) return;
|
|
10089
10103
|
const size = this.getAttribute("size");
|
|
@@ -10091,16 +10105,32 @@ class FigInputGradient extends HTMLElement {
|
|
|
10091
10105
|
else this.#swatch.removeAttribute("size");
|
|
10092
10106
|
}
|
|
10093
10107
|
|
|
10094
|
-
#
|
|
10108
|
+
#createStopHandles() {
|
|
10095
10109
|
const disabled =
|
|
10096
10110
|
this.hasAttribute("disabled") && this.getAttribute("disabled") !== "false";
|
|
10097
|
-
const tipAttr = this.#stopHandleMode === "tip" ? ' tip="color"' : "";
|
|
10098
10111
|
return this.#gradient.stops
|
|
10099
10112
|
.map(
|
|
10100
10113
|
(stop, i) =>
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10114
|
+
figCreateElement(
|
|
10115
|
+
"fig-tooltip",
|
|
10116
|
+
{
|
|
10117
|
+
action: "manual",
|
|
10118
|
+
text: `${Math.round(stop.position)}%`,
|
|
10119
|
+
},
|
|
10120
|
+
figCreateElement("fig-handle", {
|
|
10121
|
+
drag: true,
|
|
10122
|
+
"drag-axes": "x",
|
|
10123
|
+
"drag-surface": ".fig-input-gradient-track",
|
|
10124
|
+
type: "color",
|
|
10125
|
+
tip: this.#stopHandleMode === "tip" ? "color" : null,
|
|
10126
|
+
color: this.#stopColorCSS(stop),
|
|
10127
|
+
value: `${stop.position}% 50%`,
|
|
10128
|
+
"hit-area": "4",
|
|
10129
|
+
"data-stop-index": i,
|
|
10130
|
+
disabled,
|
|
10131
|
+
}),
|
|
10132
|
+
),
|
|
10133
|
+
);
|
|
10104
10134
|
}
|
|
10105
10135
|
|
|
10106
10136
|
#ghostHandle = null;
|
|
@@ -10111,27 +10141,45 @@ class FigInputGradient extends HTMLElement {
|
|
|
10111
10141
|
this.#colorObserver = null;
|
|
10112
10142
|
const disabled = figBooleanAttribute(this, "disabled");
|
|
10113
10143
|
const mode = this.#editMode;
|
|
10144
|
+
const size = this.getAttribute("size");
|
|
10145
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
10146
|
+
background: this.#buildGradientCSS(),
|
|
10147
|
+
size: size || null,
|
|
10148
|
+
disabled,
|
|
10149
|
+
});
|
|
10114
10150
|
|
|
10115
10151
|
if (mode === "picker" && hasFigFillPicker()) {
|
|
10116
10152
|
const gradientValue = JSON.stringify(this.value);
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10153
|
+
const picker = figCreateElement(
|
|
10154
|
+
"fig-fill-picker",
|
|
10155
|
+
{
|
|
10156
|
+
mode: "gradient",
|
|
10157
|
+
value: gradientValue,
|
|
10158
|
+
disabled,
|
|
10159
|
+
},
|
|
10160
|
+
swatch,
|
|
10161
|
+
);
|
|
10162
|
+
this.replaceChildren(picker);
|
|
10163
|
+
this.#swatch = swatch;
|
|
10122
10164
|
this.#track = null;
|
|
10123
10165
|
this.#setupPickerEvents();
|
|
10124
10166
|
this.#syncFocusTarget();
|
|
10125
10167
|
return;
|
|
10126
10168
|
}
|
|
10127
10169
|
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10170
|
+
const editable = mode === "true" || mode === "picker";
|
|
10171
|
+
const track = editable
|
|
10172
|
+
? figCreateElement(
|
|
10173
|
+
"div",
|
|
10174
|
+
{ className: "fig-input-gradient-track" },
|
|
10175
|
+
this.#createStopHandles(),
|
|
10176
|
+
)
|
|
10177
|
+
: null;
|
|
10178
|
+
this.replaceChildren(...(track ? [swatch, track] : [swatch]));
|
|
10179
|
+
this.#swatch = swatch;
|
|
10180
|
+
this.#track = track;
|
|
10133
10181
|
|
|
10134
|
-
if (
|
|
10182
|
+
if (editable) {
|
|
10135
10183
|
this.#setupGhostHandle();
|
|
10136
10184
|
this.#setupEventListeners();
|
|
10137
10185
|
}
|
|
@@ -10345,7 +10393,7 @@ class FigInputGradient extends HTMLElement {
|
|
|
10345
10393
|
|
|
10346
10394
|
if (handles.length !== stops.length) {
|
|
10347
10395
|
const ghost = this.#ghostHandle;
|
|
10348
|
-
this.#track.
|
|
10396
|
+
this.#track.replaceChildren(...this.#createStopHandles());
|
|
10349
10397
|
if (ghost) this.#track.appendChild(ghost);
|
|
10350
10398
|
this.#syncHandleMode();
|
|
10351
10399
|
this.#reobserveHandleColors();
|
|
@@ -11096,22 +11144,54 @@ class FigComboInput extends HTMLElement {
|
|
|
11096
11144
|
const currentValue = this.value;
|
|
11097
11145
|
const dropdownLabel = this.#dropdownLabel();
|
|
11098
11146
|
|
|
11099
|
-
const
|
|
11100
|
-
|
|
11101
|
-
:
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11147
|
+
const input = figCreateElement("fig-input-text", {
|
|
11148
|
+
placeholder,
|
|
11149
|
+
value: currentValue,
|
|
11150
|
+
});
|
|
11151
|
+
const icon = figCreateSvgElement(
|
|
11152
|
+
"svg",
|
|
11153
|
+
{
|
|
11154
|
+
width: "16",
|
|
11155
|
+
height: "16",
|
|
11156
|
+
viewBox: "0 0 16 16",
|
|
11157
|
+
fill: "none",
|
|
11158
|
+
},
|
|
11159
|
+
figCreateSvgElement("path", {
|
|
11160
|
+
d: "M5.87868 7.12132L8 9.24264L10.1213 7.12132",
|
|
11161
|
+
stroke: "currentColor",
|
|
11162
|
+
"stroke-opacity": "0.9",
|
|
11163
|
+
"stroke-linecap": "round",
|
|
11164
|
+
}),
|
|
11165
|
+
);
|
|
11166
|
+
const button = figCreateElement(
|
|
11167
|
+
"fig-button",
|
|
11168
|
+
{
|
|
11169
|
+
type: "select",
|
|
11170
|
+
variant: "input",
|
|
11171
|
+
icon: true,
|
|
11172
|
+
},
|
|
11173
|
+
icon,
|
|
11174
|
+
);
|
|
11175
|
+
if (!this.#usesCustomDropdown) {
|
|
11176
|
+
button.appendChild(
|
|
11177
|
+
figCreateElement(
|
|
11178
|
+
"fig-dropdown",
|
|
11179
|
+
{
|
|
11180
|
+
type: "dropdown",
|
|
11181
|
+
label: dropdownLabel,
|
|
11182
|
+
},
|
|
11183
|
+
options.map((option) =>
|
|
11184
|
+
figCreateElement("option", {}, option.trim()),
|
|
11185
|
+
),
|
|
11186
|
+
),
|
|
11187
|
+
);
|
|
11188
|
+
}
|
|
11189
|
+
this.replaceChildren(
|
|
11190
|
+
figCreateElement("div", { className: "input-combo" }, [input, button]),
|
|
11191
|
+
);
|
|
11112
11192
|
|
|
11113
|
-
this.#input =
|
|
11114
|
-
this.#button =
|
|
11193
|
+
this.#input = input;
|
|
11194
|
+
this.#button = button;
|
|
11115
11195
|
|
|
11116
11196
|
if (this.#usesCustomDropdown && this.#customDropdown && this.#button) {
|
|
11117
11197
|
if (!this.#customDropdown.hasAttribute("type")) {
|
|
@@ -11376,14 +11456,18 @@ class FigSwatch extends HTMLElement {
|
|
|
11376
11456
|
|
|
11377
11457
|
if (this.#type === "color") {
|
|
11378
11458
|
const hex = this.#toHex(bg);
|
|
11379
|
-
|
|
11380
|
-
|
|
11459
|
+
const input = figCreateElement("input", {
|
|
11460
|
+
type: "color",
|
|
11461
|
+
value: hex,
|
|
11462
|
+
});
|
|
11463
|
+
this.replaceChildren(document.createElement("div"), input);
|
|
11464
|
+
this.input = input;
|
|
11381
11465
|
if (!isVar) {
|
|
11382
11466
|
this.input.addEventListener("input", this.#boundHandleInput);
|
|
11383
11467
|
}
|
|
11384
11468
|
this.#syncA11y();
|
|
11385
11469
|
} else {
|
|
11386
|
-
this.
|
|
11470
|
+
this.replaceChildren(document.createElement("div"));
|
|
11387
11471
|
this.input = null;
|
|
11388
11472
|
this.#syncA11y();
|
|
11389
11473
|
}
|
|
@@ -13162,7 +13246,7 @@ class FigInputFile extends HTMLElement {
|
|
|
13162
13246
|
!!this.getAttribute("url") ||
|
|
13163
13247
|
!!this.getAttribute("filename");
|
|
13164
13248
|
|
|
13165
|
-
this.
|
|
13249
|
+
this.replaceChildren();
|
|
13166
13250
|
|
|
13167
13251
|
if (hasFile) {
|
|
13168
13252
|
const tooltipText = accepts
|
|
@@ -13545,10 +13629,10 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13545
13629
|
const y = pad + (1 - (pts[i].value - minVal) / range) * s;
|
|
13546
13630
|
d += (i === 0 ? "M" : "L") + x.toFixed(1) + "," + y.toFixed(1);
|
|
13547
13631
|
}
|
|
13548
|
-
return
|
|
13632
|
+
return FigEasingCurve.#createSvgIcon(d, size);
|
|
13549
13633
|
}
|
|
13550
13634
|
|
|
13551
|
-
static
|
|
13635
|
+
static #curveIconPath(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13552
13636
|
const draw = 12;
|
|
13553
13637
|
const pad = (size - draw) / 2;
|
|
13554
13638
|
const samples = 48;
|
|
@@ -13594,6 +13678,43 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13594
13678
|
const py = toY(points[i].y);
|
|
13595
13679
|
d += `${i === 0 ? "M" : "L"}${px.toFixed(1)},${py.toFixed(1)}`;
|
|
13596
13680
|
}
|
|
13681
|
+
return d;
|
|
13682
|
+
}
|
|
13683
|
+
|
|
13684
|
+
static #createSvgIcon(d, size = 24) {
|
|
13685
|
+
return figCreateSvgElement(
|
|
13686
|
+
"svg",
|
|
13687
|
+
{
|
|
13688
|
+
width: size,
|
|
13689
|
+
height: size,
|
|
13690
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
13691
|
+
fill: "none",
|
|
13692
|
+
},
|
|
13693
|
+
figCreateSvgElement("path", {
|
|
13694
|
+
d,
|
|
13695
|
+
stroke: "currentColor",
|
|
13696
|
+
"stroke-width": "1",
|
|
13697
|
+
"stroke-linecap": "round",
|
|
13698
|
+
fill: "none",
|
|
13699
|
+
}),
|
|
13700
|
+
);
|
|
13701
|
+
}
|
|
13702
|
+
|
|
13703
|
+
static #createCurveIcon(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13704
|
+
return FigEasingCurve.#createSvgIcon(
|
|
13705
|
+
FigEasingCurve.#curveIconPath(cp1x, cp1y, cp2x, cp2y, size),
|
|
13706
|
+
size,
|
|
13707
|
+
);
|
|
13708
|
+
}
|
|
13709
|
+
|
|
13710
|
+
static curveIcon(cp1x, cp1y, cp2x, cp2y, size = 24) {
|
|
13711
|
+
const d = FigEasingCurve.#curveIconPath(
|
|
13712
|
+
cp1x,
|
|
13713
|
+
cp1y,
|
|
13714
|
+
cp2x,
|
|
13715
|
+
cp2y,
|
|
13716
|
+
size,
|
|
13717
|
+
);
|
|
13597
13718
|
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
13719
|
}
|
|
13599
13720
|
|
|
@@ -13607,7 +13728,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13607
13728
|
this.classList.toggle("spring-mode", this.#mode === "spring");
|
|
13608
13729
|
this.classList.toggle("bezier-mode", this.#mode !== "spring");
|
|
13609
13730
|
this.#syncMetricsFromCSS();
|
|
13610
|
-
this.
|
|
13731
|
+
this.replaceChildren(...this.#createContent());
|
|
13611
13732
|
this.#cacheRefs();
|
|
13612
13733
|
if (this.#svg) {
|
|
13613
13734
|
this.#syncHandleSizes();
|
|
@@ -13618,14 +13739,6 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13618
13739
|
this.#setupEvents();
|
|
13619
13740
|
}
|
|
13620
13741
|
|
|
13621
|
-
static #escapeAttribute(value) {
|
|
13622
|
-
return String(value)
|
|
13623
|
-
.replace(/&/g, "&")
|
|
13624
|
-
.replace(/"/g, """)
|
|
13625
|
-
.replace(/</g, "<")
|
|
13626
|
-
.replace(/>/g, ">");
|
|
13627
|
-
}
|
|
13628
|
-
|
|
13629
13742
|
#canUseFigSelect() {
|
|
13630
13743
|
return ["fig-select", "fig-select-options", "fig-select-option"].every(
|
|
13631
13744
|
(tag) => {
|
|
@@ -13638,38 +13751,43 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13638
13751
|
);
|
|
13639
13752
|
}
|
|
13640
13753
|
|
|
13641
|
-
#
|
|
13642
|
-
|
|
13754
|
+
#createDropdown() {
|
|
13755
|
+
const dropdown = figCreateElement("fig-dropdown", {
|
|
13756
|
+
className: "fig-easing-curve-select",
|
|
13757
|
+
label: "Easing preset",
|
|
13758
|
+
value: this.#presetName,
|
|
13759
|
+
full: true,
|
|
13760
|
+
});
|
|
13643
13761
|
let currentGroup = undefined;
|
|
13644
|
-
let
|
|
13762
|
+
let parent = dropdown;
|
|
13645
13763
|
for (const preset of FigEasingCurve.PRESETS) {
|
|
13646
13764
|
if (!this.#isEditEnabled() && !preset.value && !preset.spring) continue;
|
|
13647
13765
|
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
13766
|
currentGroup = preset.group;
|
|
13767
|
+
parent = currentGroup
|
|
13768
|
+
? figCreateElement("optgroup", { label: currentGroup })
|
|
13769
|
+
: dropdown;
|
|
13770
|
+
if (parent !== dropdown) dropdown.appendChild(parent);
|
|
13654
13771
|
}
|
|
13655
|
-
|
|
13656
|
-
|
|
13772
|
+
parent.appendChild(
|
|
13773
|
+
figCreateElement("option", { value: preset.name }, preset.name),
|
|
13774
|
+
);
|
|
13657
13775
|
}
|
|
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>`;
|
|
13776
|
+
return dropdown;
|
|
13661
13777
|
}
|
|
13662
13778
|
|
|
13663
|
-
#
|
|
13664
|
-
if (!this.#canUseFigSelect()) return this.#
|
|
13779
|
+
#createSelect() {
|
|
13780
|
+
if (!this.#canUseFigSelect()) return this.#createDropdown();
|
|
13665
13781
|
|
|
13666
|
-
|
|
13782
|
+
const options = document.createElement("fig-select-options");
|
|
13667
13783
|
let currentGroup = undefined;
|
|
13668
13784
|
for (const p of FigEasingCurve.PRESETS) {
|
|
13669
13785
|
if (!this.#isEditEnabled() && !p.value && !p.spring) continue;
|
|
13670
13786
|
if (p.group !== currentGroup) {
|
|
13671
13787
|
if (p.group) {
|
|
13672
|
-
|
|
13788
|
+
options.appendChild(
|
|
13789
|
+
figCreateElement("fig-separator", { label: p.group }),
|
|
13790
|
+
);
|
|
13673
13791
|
}
|
|
13674
13792
|
currentGroup = p.group;
|
|
13675
13793
|
}
|
|
@@ -13684,42 +13802,152 @@ class FigEasingCurve extends HTMLElement {
|
|
|
13684
13802
|
this.#cp2.x,
|
|
13685
13803
|
this.#cp2.y,
|
|
13686
13804
|
];
|
|
13687
|
-
icon = FigEasingCurve
|
|
13688
|
-
}
|
|
13689
|
-
|
|
13690
|
-
|
|
13805
|
+
icon = FigEasingCurve.#createCurveIcon(...v);
|
|
13806
|
+
}
|
|
13807
|
+
options.appendChild(
|
|
13808
|
+
figCreateElement(
|
|
13809
|
+
"fig-select-option",
|
|
13810
|
+
{
|
|
13811
|
+
value: p.name,
|
|
13812
|
+
label: p.name,
|
|
13813
|
+
},
|
|
13814
|
+
[
|
|
13815
|
+
figCreateElement("span", { slot: "prepend" }, icon),
|
|
13816
|
+
figCreateElement("span", {}, p.name),
|
|
13817
|
+
],
|
|
13818
|
+
),
|
|
13819
|
+
);
|
|
13691
13820
|
}
|
|
13692
|
-
|
|
13693
|
-
|
|
13821
|
+
return figCreateElement(
|
|
13822
|
+
"fig-select",
|
|
13823
|
+
{
|
|
13824
|
+
className: "fig-easing-curve-select",
|
|
13825
|
+
label: "Easing preset",
|
|
13826
|
+
value: this.#presetName,
|
|
13827
|
+
full: true,
|
|
13828
|
+
},
|
|
13829
|
+
options,
|
|
13830
|
+
);
|
|
13831
|
+
}
|
|
13832
|
+
|
|
13833
|
+
#createHandle(className, dataHandle, label) {
|
|
13834
|
+
return figCreateSvgElement(
|
|
13835
|
+
"foreignObject",
|
|
13836
|
+
{
|
|
13837
|
+
className,
|
|
13838
|
+
"data-handle": dataHandle,
|
|
13839
|
+
width: "20",
|
|
13840
|
+
height: "20",
|
|
13841
|
+
},
|
|
13842
|
+
figCreateElement("fig-handle", {
|
|
13843
|
+
size: "small",
|
|
13844
|
+
"aria-label": label,
|
|
13845
|
+
}),
|
|
13846
|
+
);
|
|
13694
13847
|
}
|
|
13695
13848
|
|
|
13696
|
-
#
|
|
13849
|
+
#createContent() {
|
|
13697
13850
|
const size = 200;
|
|
13698
|
-
const select = this.#
|
|
13699
|
-
if (!this.#isEditEnabled()) return select;
|
|
13700
|
-
const valueInput =
|
|
13851
|
+
const select = this.#createSelect();
|
|
13852
|
+
if (!this.#isEditEnabled()) return [select];
|
|
13853
|
+
const valueInput = figCreateElement("fig-input-text", {
|
|
13854
|
+
className: "fig-easing-curve-value-input",
|
|
13855
|
+
value: this.value,
|
|
13856
|
+
full: true,
|
|
13857
|
+
});
|
|
13858
|
+
const svgChildren = [
|
|
13859
|
+
figCreateSvgElement("rect", {
|
|
13860
|
+
className: "fig-easing-curve-bounds",
|
|
13861
|
+
x: "0",
|
|
13862
|
+
y: "0",
|
|
13863
|
+
width: size,
|
|
13864
|
+
height: size,
|
|
13865
|
+
}),
|
|
13866
|
+
];
|
|
13701
13867
|
|
|
13702
13868
|
if (this.#mode === "spring") {
|
|
13703
13869
|
const targetY = 40;
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
|
-
|
|
13721
|
-
|
|
13722
|
-
|
|
13870
|
+
svgChildren.push(
|
|
13871
|
+
figCreateSvgElement("line", {
|
|
13872
|
+
className: "fig-easing-curve-target",
|
|
13873
|
+
x1: "0",
|
|
13874
|
+
y1: targetY,
|
|
13875
|
+
x2: size,
|
|
13876
|
+
y2: targetY,
|
|
13877
|
+
}),
|
|
13878
|
+
figCreateSvgElement("path", {
|
|
13879
|
+
className: "fig-easing-curve-path",
|
|
13880
|
+
}),
|
|
13881
|
+
this.#createHandle(
|
|
13882
|
+
"fig-easing-curve-handle",
|
|
13883
|
+
"bounce",
|
|
13884
|
+
"Spring bounce handle",
|
|
13885
|
+
),
|
|
13886
|
+
this.#createHandle(
|
|
13887
|
+
"fig-easing-curve-handle fig-easing-curve-duration-bar",
|
|
13888
|
+
"duration",
|
|
13889
|
+
"Spring duration handle",
|
|
13890
|
+
),
|
|
13891
|
+
);
|
|
13892
|
+
} else {
|
|
13893
|
+
svgChildren.push(
|
|
13894
|
+
figCreateSvgElement("line", {
|
|
13895
|
+
className: "fig-easing-curve-boundary",
|
|
13896
|
+
"data-boundary": "top",
|
|
13897
|
+
x1: "0",
|
|
13898
|
+
y1: "0",
|
|
13899
|
+
x2: size,
|
|
13900
|
+
y2: "0",
|
|
13901
|
+
}),
|
|
13902
|
+
figCreateSvgElement("line", {
|
|
13903
|
+
className: "fig-easing-curve-boundary",
|
|
13904
|
+
"data-boundary": "bottom",
|
|
13905
|
+
x1: "0",
|
|
13906
|
+
y1: size,
|
|
13907
|
+
x2: size,
|
|
13908
|
+
y2: size,
|
|
13909
|
+
}),
|
|
13910
|
+
figCreateSvgElement("path", {
|
|
13911
|
+
className: "fig-easing-curve-path",
|
|
13912
|
+
}),
|
|
13913
|
+
figCreateSvgElement("line", {
|
|
13914
|
+
className: "fig-easing-curve-arm",
|
|
13915
|
+
"data-arm": "1",
|
|
13916
|
+
}),
|
|
13917
|
+
figCreateSvgElement("line", {
|
|
13918
|
+
className: "fig-easing-curve-arm",
|
|
13919
|
+
"data-arm": "2",
|
|
13920
|
+
}),
|
|
13921
|
+
this.#createHandle(
|
|
13922
|
+
"fig-easing-curve-handle",
|
|
13923
|
+
"1",
|
|
13924
|
+
"First easing control point",
|
|
13925
|
+
),
|
|
13926
|
+
this.#createHandle(
|
|
13927
|
+
"fig-easing-curve-handle",
|
|
13928
|
+
"2",
|
|
13929
|
+
"Second easing control point",
|
|
13930
|
+
),
|
|
13931
|
+
);
|
|
13932
|
+
}
|
|
13933
|
+
|
|
13934
|
+
const svg = figCreateSvgElement(
|
|
13935
|
+
"svg",
|
|
13936
|
+
{
|
|
13937
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
13938
|
+
className: "fig-easing-curve-svg",
|
|
13939
|
+
},
|
|
13940
|
+
svgChildren,
|
|
13941
|
+
);
|
|
13942
|
+
return [
|
|
13943
|
+
select,
|
|
13944
|
+
figCreateElement(
|
|
13945
|
+
"div",
|
|
13946
|
+
{ className: "fig-easing-curve-svg-container" },
|
|
13947
|
+
svg,
|
|
13948
|
+
),
|
|
13949
|
+
valueInput,
|
|
13950
|
+
];
|
|
13723
13951
|
}
|
|
13724
13952
|
|
|
13725
13953
|
#readCssNumber(name, fallback) {
|
|
@@ -14054,7 +14282,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
14054
14282
|
for (const option of this.#select.querySelectorAll("fig-select-option")) {
|
|
14055
14283
|
if (option.value === optionValue) {
|
|
14056
14284
|
const prepend = option.querySelector(':scope > [slot="prepend"]');
|
|
14057
|
-
if (prepend) prepend.
|
|
14285
|
+
if (prepend) prepend.replaceChildren(icon.cloneNode(true));
|
|
14058
14286
|
}
|
|
14059
14287
|
}
|
|
14060
14288
|
}
|
|
@@ -14062,7 +14290,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
14062
14290
|
#refreshCustomPresetIcons() {
|
|
14063
14291
|
if (!this.#select) return;
|
|
14064
14292
|
if (!this.#isEditEnabled()) return;
|
|
14065
|
-
const bezierIcon = FigEasingCurve
|
|
14293
|
+
const bezierIcon = FigEasingCurve.#createCurveIcon(
|
|
14066
14294
|
this.#cp1.x,
|
|
14067
14295
|
this.#cp1.y,
|
|
14068
14296
|
this.#cp2.x,
|
|
@@ -14652,34 +14880,44 @@ class Fig3DRotate extends HTMLElement {
|
|
|
14652
14880
|
rotateY: this.#ry,
|
|
14653
14881
|
rotateZ: this.#rz,
|
|
14654
14882
|
};
|
|
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
|
-
|
|
14883
|
+
const cube = figCreateElement(
|
|
14884
|
+
"div",
|
|
14885
|
+
{ className: "fig-3d-rotate-cube" },
|
|
14886
|
+
["front", "back", "right", "left", "top", "bottom"].map((face) =>
|
|
14887
|
+
figCreateElement("div", {
|
|
14888
|
+
className: `fig-3d-rotate-face ${face}`,
|
|
14889
|
+
}),
|
|
14890
|
+
),
|
|
14891
|
+
);
|
|
14892
|
+
const container = figCreateElement(
|
|
14893
|
+
"div",
|
|
14894
|
+
{
|
|
14895
|
+
className: "fig-3d-rotate-container",
|
|
14896
|
+
tabindex: "0",
|
|
14897
|
+
},
|
|
14898
|
+
figCreateElement(
|
|
14899
|
+
"div",
|
|
14900
|
+
{ className: "fig-3d-rotate-scene" },
|
|
14901
|
+
cube,
|
|
14902
|
+
),
|
|
14903
|
+
);
|
|
14904
|
+
const fields = this.#fields.map((axis) =>
|
|
14905
|
+
figCreateElement(
|
|
14906
|
+
"fig-input-number",
|
|
14907
|
+
{
|
|
14908
|
+
name: axis,
|
|
14909
|
+
step: "1",
|
|
14910
|
+
precision: "1",
|
|
14911
|
+
value: axisValues[axis],
|
|
14912
|
+
units: "°",
|
|
14913
|
+
},
|
|
14914
|
+
figCreateElement("span", { slot: "prepend" }, axisLabels[axis]),
|
|
14915
|
+
),
|
|
14916
|
+
);
|
|
14917
|
+
|
|
14918
|
+
this.replaceChildren(container, ...fields);
|
|
14919
|
+
this.#container = container;
|
|
14920
|
+
this.#cube = cube;
|
|
14683
14921
|
this.#wireFieldInputs();
|
|
14684
14922
|
this.#updateCube();
|
|
14685
14923
|
this.#setupEvents();
|
|
@@ -14982,31 +15220,65 @@ class FigOriginGrid extends HTMLElement {
|
|
|
14982
15220
|
const cells = Array.from({ length: 9 }, (_, index) => {
|
|
14983
15221
|
const col = index % 3;
|
|
14984
15222
|
const row = Math.floor(index / 3);
|
|
14985
|
-
return
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
15223
|
+
return figCreateElement(
|
|
15224
|
+
"span",
|
|
15225
|
+
{
|
|
15226
|
+
className: "origin-grid-cell",
|
|
15227
|
+
"data-col": col,
|
|
15228
|
+
"data-row": row,
|
|
15229
|
+
},
|
|
15230
|
+
figCreateElement("span", { className: "origin-grid-dot" }),
|
|
15231
|
+
);
|
|
15232
|
+
});
|
|
14989
15233
|
|
|
14990
15234
|
const xValue = this.#x.toFixed(this.#precision);
|
|
14991
15235
|
const yValue = this.#y.toFixed(this.#precision);
|
|
14992
|
-
const
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15236
|
+
const handle = document.createElement("fig-handle");
|
|
15237
|
+
const grid = figCreateElement(
|
|
15238
|
+
"div",
|
|
15239
|
+
{
|
|
15240
|
+
className: "origin-grid",
|
|
15241
|
+
"aria-label": "Transform origin grid",
|
|
15242
|
+
},
|
|
15243
|
+
[
|
|
15244
|
+
figCreateElement(
|
|
15245
|
+
"div",
|
|
15246
|
+
{ className: "origin-grid-cells" },
|
|
15247
|
+
cells,
|
|
15248
|
+
),
|
|
15249
|
+
handle,
|
|
15250
|
+
],
|
|
15251
|
+
);
|
|
15252
|
+
const surface = figCreateElement(
|
|
15253
|
+
"div",
|
|
15254
|
+
{ className: "fig-origin-grid-surface" },
|
|
15255
|
+
grid,
|
|
15256
|
+
);
|
|
15257
|
+
const rendered = [surface];
|
|
15258
|
+
if (this.#fieldsEnabled) {
|
|
15259
|
+
const createValueInput = (name, value) =>
|
|
15260
|
+
figCreateElement(
|
|
15261
|
+
"fig-input-number",
|
|
15262
|
+
{
|
|
15263
|
+
name,
|
|
15264
|
+
value,
|
|
15265
|
+
step: "1",
|
|
15266
|
+
units: "%",
|
|
15267
|
+
},
|
|
15268
|
+
figCreateElement("span", { slot: "prepend" }, name.toUpperCase()),
|
|
15269
|
+
);
|
|
15270
|
+
rendered.push(
|
|
15271
|
+
figCreateElement("div", { className: "origin-values" }, [
|
|
15272
|
+
createValueInput("x", xValue),
|
|
15273
|
+
createValueInput("y", yValue),
|
|
15274
|
+
]),
|
|
15275
|
+
);
|
|
15276
|
+
}
|
|
15006
15277
|
|
|
15007
|
-
this
|
|
15008
|
-
this.#
|
|
15009
|
-
this.#
|
|
15278
|
+
this.replaceChildren(...rendered);
|
|
15279
|
+
this.#grid = grid;
|
|
15280
|
+
this.#cells = cells;
|
|
15281
|
+
this.#handle = handle;
|
|
15010
15282
|
this.#xInput = this.querySelector('fig-input-number[name="x"]');
|
|
15011
15283
|
this.#yInput = this.querySelector('fig-input-number[name="y"]');
|
|
15012
15284
|
this.#syncHandlePosition();
|
|
@@ -15463,7 +15735,100 @@ class FigInputJoystick extends HTMLElement {
|
|
|
15463
15735
|
}
|
|
15464
15736
|
|
|
15465
15737
|
#render() {
|
|
15466
|
-
|
|
15738
|
+
const axisLabels = this.#getAxisLabels();
|
|
15739
|
+
const planeContainer = figCreateElement("div", {
|
|
15740
|
+
className: "fig-input-joystick-plane-container",
|
|
15741
|
+
});
|
|
15742
|
+
const createAxisLabel = (position, text, noRotate = false) =>
|
|
15743
|
+
text
|
|
15744
|
+
? figCreateElement(
|
|
15745
|
+
"label",
|
|
15746
|
+
{
|
|
15747
|
+
className: [
|
|
15748
|
+
"fig-joystick-axis-label",
|
|
15749
|
+
position,
|
|
15750
|
+
noRotate ? "no-rotate" : "",
|
|
15751
|
+
]
|
|
15752
|
+
.filter(Boolean)
|
|
15753
|
+
.join(" "),
|
|
15754
|
+
"aria-hidden": "true",
|
|
15755
|
+
},
|
|
15756
|
+
text,
|
|
15757
|
+
)
|
|
15758
|
+
: null;
|
|
15759
|
+
planeContainer.append(
|
|
15760
|
+
...[
|
|
15761
|
+
createAxisLabel(
|
|
15762
|
+
"left",
|
|
15763
|
+
axisLabels.left,
|
|
15764
|
+
axisLabels.leftNoRotate,
|
|
15765
|
+
),
|
|
15766
|
+
createAxisLabel("right", axisLabels.right),
|
|
15767
|
+
createAxisLabel("top", axisLabels.top),
|
|
15768
|
+
createAxisLabel("bottom", axisLabels.bottom),
|
|
15769
|
+
].filter(Boolean),
|
|
15770
|
+
);
|
|
15771
|
+
|
|
15772
|
+
const plane = figCreateElement(
|
|
15773
|
+
"div",
|
|
15774
|
+
{ className: "fig-input-joystick-plane" },
|
|
15775
|
+
[
|
|
15776
|
+
figCreateElement("div", {
|
|
15777
|
+
className: "fig-input-joystick-guides",
|
|
15778
|
+
}),
|
|
15779
|
+
figCreateElement("fig-handle", {
|
|
15780
|
+
drag: true,
|
|
15781
|
+
"drag-surface": ".fig-input-joystick-plane",
|
|
15782
|
+
"drag-axes": "x,y",
|
|
15783
|
+
"drag-snapping": "modifier",
|
|
15784
|
+
}),
|
|
15785
|
+
],
|
|
15786
|
+
);
|
|
15787
|
+
const reset = figCreateElement(
|
|
15788
|
+
"fig-tooltip",
|
|
15789
|
+
{ text: "Reset" },
|
|
15790
|
+
figCreateElement(
|
|
15791
|
+
"fig-button",
|
|
15792
|
+
{
|
|
15793
|
+
variant: "ghost",
|
|
15794
|
+
icon: "true",
|
|
15795
|
+
className: "fig-joystick-reset",
|
|
15796
|
+
"aria-label": "Reset to default",
|
|
15797
|
+
},
|
|
15798
|
+
createFigIcon("reset", { size: "small" }),
|
|
15799
|
+
),
|
|
15800
|
+
);
|
|
15801
|
+
planeContainer.append(plane, reset);
|
|
15802
|
+
|
|
15803
|
+
const children = [planeContainer];
|
|
15804
|
+
if (this.#fieldsEnabled) {
|
|
15805
|
+
const createValueInput = (name, value) =>
|
|
15806
|
+
figCreateElement(
|
|
15807
|
+
"fig-input-number",
|
|
15808
|
+
{
|
|
15809
|
+
name,
|
|
15810
|
+
step: "1",
|
|
15811
|
+
value,
|
|
15812
|
+
min: "0",
|
|
15813
|
+
max: "100",
|
|
15814
|
+
units: "%",
|
|
15815
|
+
},
|
|
15816
|
+
figCreateElement("span", { slot: "prepend" }, name.toUpperCase()),
|
|
15817
|
+
);
|
|
15818
|
+
children.push(
|
|
15819
|
+
figCreateElement("div", { className: "joystick-values" }, [
|
|
15820
|
+
createValueInput(
|
|
15821
|
+
"x",
|
|
15822
|
+
(this.position.x * 100).toFixed(this.precision),
|
|
15823
|
+
),
|
|
15824
|
+
createValueInput(
|
|
15825
|
+
"y",
|
|
15826
|
+
(this.position.y * 100).toFixed(this.precision),
|
|
15827
|
+
),
|
|
15828
|
+
]),
|
|
15829
|
+
);
|
|
15830
|
+
}
|
|
15831
|
+
this.replaceChildren(...children);
|
|
15467
15832
|
}
|
|
15468
15833
|
|
|
15469
15834
|
#getAxisLabels() {
|
|
@@ -15492,63 +15857,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
15492
15857
|
return { left: "", right: "", top: "", bottom: "", leftNoRotate: false };
|
|
15493
15858
|
}
|
|
15494
15859
|
|
|
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
15860
|
#setupListeners() {
|
|
15553
15861
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
15554
15862
|
this.cursor = this.querySelector("fig-handle");
|
|
@@ -16078,314 +16386,6 @@ class FigPreview extends HTMLElement {
|
|
|
16078
16386
|
}
|
|
16079
16387
|
figDefineElement("fig-preview", FigPreview);
|
|
16080
16388
|
|
|
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
16389
|
/** @type {Record<string, string | { medium: string, small: string }>} */
|
|
16390
16390
|
const FIG_ICON_TOKENS = {
|
|
16391
16391
|
chevron: { medium: "--icon-24-chevron", small: "--icon-16-chevron" },
|
|
@@ -16575,11 +16575,22 @@ class FigColorTip extends HTMLElement {
|
|
|
16575
16575
|
}
|
|
16576
16576
|
|
|
16577
16577
|
#render() {
|
|
16578
|
+
this.#teardownListeners();
|
|
16578
16579
|
const mode = this.#controlMode;
|
|
16579
16580
|
if (mode === "add" || mode === "remove") {
|
|
16580
16581
|
const iconName = mode === "add" ? "add" : "minus";
|
|
16581
16582
|
const label = this.getAttribute("aria-label") || (mode === "add" ? "Add color stop" : "Remove color stop");
|
|
16582
|
-
this.
|
|
16583
|
+
this.replaceChildren(
|
|
16584
|
+
figCreateElement(
|
|
16585
|
+
"fig-button",
|
|
16586
|
+
{
|
|
16587
|
+
icon: true,
|
|
16588
|
+
variant: "ghost",
|
|
16589
|
+
"aria-label": label,
|
|
16590
|
+
},
|
|
16591
|
+
createFigIcon(iconName),
|
|
16592
|
+
),
|
|
16593
|
+
);
|
|
16583
16594
|
this.#fillPicker = null;
|
|
16584
16595
|
this.#swatch = null;
|
|
16585
16596
|
this.addEventListener("click", this.#handleControlClick);
|
|
@@ -16591,7 +16602,6 @@ class FigColorTip extends HTMLElement {
|
|
|
16591
16602
|
const rawValue = (this.getAttribute("value") || "").trim();
|
|
16592
16603
|
const color = this.#normalizeColor(rawValue);
|
|
16593
16604
|
const alpha = this.#extractAlpha(rawValue);
|
|
16594
|
-
const alphaAttr = this.#alphaEnabled ? "" : 'alpha="false"';
|
|
16595
16605
|
const pickerValue =
|
|
16596
16606
|
alpha < 1
|
|
16597
16607
|
? JSON.stringify({
|
|
@@ -16600,16 +16610,28 @@ class FigColorTip extends HTMLElement {
|
|
|
16600
16610
|
opacity: Math.round(alpha * 100),
|
|
16601
16611
|
})
|
|
16602
16612
|
: JSON.stringify({ type: "solid", color });
|
|
16603
|
-
const
|
|
16604
|
-
|
|
16605
|
-
?
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16613
|
+
const swatch = figCreateElement("fig-swatch", {
|
|
16614
|
+
background: color,
|
|
16615
|
+
alpha: alpha < 1 ? alpha : null,
|
|
16616
|
+
});
|
|
16617
|
+
let picker = null;
|
|
16618
|
+
if (hasFigFillPicker()) {
|
|
16619
|
+
picker = figCreateElement(
|
|
16620
|
+
"fig-fill-picker",
|
|
16621
|
+
{
|
|
16622
|
+
mode: "solid",
|
|
16623
|
+
alpha: this.#alphaEnabled ? null : "false",
|
|
16624
|
+
value: pickerValue,
|
|
16625
|
+
},
|
|
16626
|
+
swatch,
|
|
16627
|
+
);
|
|
16628
|
+
this.replaceChildren(picker);
|
|
16629
|
+
} else {
|
|
16630
|
+
this.replaceChildren(swatch);
|
|
16631
|
+
}
|
|
16609
16632
|
|
|
16610
|
-
this.#fillPicker =
|
|
16611
|
-
this.#swatch =
|
|
16612
|
-
this.#teardownListeners();
|
|
16633
|
+
this.#fillPicker = picker;
|
|
16634
|
+
this.#swatch = swatch;
|
|
16613
16635
|
this.#fillPicker?.addEventListener("input", this.#boundHandleInput);
|
|
16614
16636
|
this.#fillPicker?.addEventListener("change", this.#boundHandleChange);
|
|
16615
16637
|
if (!this.#fillPicker) {
|