@rogieking/figui3 6.20.2 → 6.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -1
- package/components.css +63 -2
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +240 -98
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +16 -16
- package/dist/fig.css +1 -1
- package/dist/fig.js +60 -43
- package/dist/propskit.css +1 -0
- package/dist/propskit.js +591 -0
- package/fig-editor.css +157 -17
- package/fig-editor.js +457 -187
- package/fig-lab.css +51 -42
- package/fig-lab.js +125 -9
- package/fig.js +519 -16
- package/package.json +33 -4
- package/propskit-core.js +556 -0
- package/propskit.d.ts +98 -0
- package/propskit.js +15 -0
package/fig.js
CHANGED
|
@@ -228,7 +228,7 @@ function figUniqueId() {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/** Zero-size portal for fixed overlays so they never affect body layout metrics. */
|
|
231
|
-
function figGetOverlayRoot() {
|
|
231
|
+
function figGetOverlayRoot(source) {
|
|
232
232
|
if (!document.body) return null;
|
|
233
233
|
const attr = "data-figui-overlay-root";
|
|
234
234
|
let root = document.body.querySelector(`:scope > [${attr}]`);
|
|
@@ -237,9 +237,40 @@ function figGetOverlayRoot() {
|
|
|
237
237
|
root.setAttribute(attr, "");
|
|
238
238
|
document.body.append(root);
|
|
239
239
|
}
|
|
240
|
+
figSyncOverlayThemeFromSource(root, source);
|
|
240
241
|
return root;
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
/** Copy PropsKit / FigUI theme onto the overlay portal so portaled popups stay themed. */
|
|
245
|
+
function figSyncOverlayThemeFromSource(overlayRoot, source) {
|
|
246
|
+
if (!overlayRoot) return;
|
|
247
|
+
const panel =
|
|
248
|
+
(source instanceof Element
|
|
249
|
+
? source.closest(".figui-root")
|
|
250
|
+
: null) || document.querySelector(".figui-root");
|
|
251
|
+
if (!panel) return;
|
|
252
|
+
|
|
253
|
+
const themeAttr = panel.getAttribute("theme");
|
|
254
|
+
const theme =
|
|
255
|
+
themeAttr === "light" || themeAttr === "dark" || themeAttr === "system"
|
|
256
|
+
? themeAttr
|
|
257
|
+
: panel.classList.contains("figma-dark")
|
|
258
|
+
? "dark"
|
|
259
|
+
: panel.classList.contains("figma-light")
|
|
260
|
+
? "light"
|
|
261
|
+
: "system";
|
|
262
|
+
|
|
263
|
+
overlayRoot.classList.toggle("figma-light", theme === "light");
|
|
264
|
+
overlayRoot.classList.toggle("figma-dark", theme === "dark");
|
|
265
|
+
if (theme === "system") {
|
|
266
|
+
overlayRoot.style.setProperty("color-scheme", "light dark");
|
|
267
|
+
} else {
|
|
268
|
+
overlayRoot.style.setProperty("color-scheme", theme);
|
|
269
|
+
}
|
|
270
|
+
if (themeAttr) overlayRoot.setAttribute("theme", theme);
|
|
271
|
+
else overlayRoot.removeAttribute("theme");
|
|
272
|
+
}
|
|
273
|
+
|
|
243
274
|
let _figZCounter = 10000;
|
|
244
275
|
function figGetHighestZIndex() {
|
|
245
276
|
return _figZCounter++;
|
|
@@ -1045,13 +1076,13 @@ class FigTooltip extends HTMLElement {
|
|
|
1045
1076
|
// - Without popover support, fall back to today's behavior: nearest open
|
|
1046
1077
|
// <dialog> ancestor if present, else document.body.
|
|
1047
1078
|
if (supportsPopover) {
|
|
1048
|
-
(figGetOverlayRoot() ?? document.body).append(this.popup);
|
|
1079
|
+
(figGetOverlayRoot(this) ?? document.body).append(this.popup);
|
|
1049
1080
|
} else {
|
|
1050
1081
|
const parentDialog = this.closest("dialog");
|
|
1051
1082
|
if (parentDialog && parentDialog.open) {
|
|
1052
1083
|
parentDialog.append(this.popup);
|
|
1053
1084
|
} else {
|
|
1054
|
-
(figGetOverlayRoot() ?? document.body).append(this.popup);
|
|
1085
|
+
(figGetOverlayRoot(this) ?? document.body).append(this.popup);
|
|
1055
1086
|
}
|
|
1056
1087
|
}
|
|
1057
1088
|
|
|
@@ -1433,13 +1464,13 @@ class FigTooltip extends HTMLElement {
|
|
|
1433
1464
|
popup.append(content);
|
|
1434
1465
|
|
|
1435
1466
|
if (supportsPopover) {
|
|
1436
|
-
(figGetOverlayRoot() ?? document.body).append(popup);
|
|
1467
|
+
(figGetOverlayRoot(anchor) ?? document.body).append(popup);
|
|
1437
1468
|
} else {
|
|
1438
1469
|
const parentDialog = anchor.closest?.("dialog");
|
|
1439
1470
|
if (parentDialog && parentDialog.open) {
|
|
1440
1471
|
parentDialog.append(popup);
|
|
1441
1472
|
} else {
|
|
1442
|
-
(figGetOverlayRoot() ?? document.body).append(popup);
|
|
1473
|
+
(figGetOverlayRoot(anchor) ?? document.body).append(popup);
|
|
1443
1474
|
}
|
|
1444
1475
|
}
|
|
1445
1476
|
|
|
@@ -8034,6 +8065,7 @@ const GRADIENT_INTERPOLATION_SPACES = [
|
|
|
8034
8065
|
"display-p3",
|
|
8035
8066
|
"oklab",
|
|
8036
8067
|
"oklch",
|
|
8068
|
+
"hsl",
|
|
8037
8069
|
];
|
|
8038
8070
|
const GRADIENT_HUE_INTERPOLATIONS = [
|
|
8039
8071
|
"shorter",
|
|
@@ -8041,6 +8073,7 @@ const GRADIENT_HUE_INTERPOLATIONS = [
|
|
|
8041
8073
|
"increasing",
|
|
8042
8074
|
"decreasing",
|
|
8043
8075
|
];
|
|
8076
|
+
const GRADIENT_HUE_SPACES = new Set(["oklch", "hsl"]);
|
|
8044
8077
|
|
|
8045
8078
|
const GRADIENT_PICKER_SPACES = ["srgb", "srgb-linear", "oklab", "oklch"];
|
|
8046
8079
|
|
|
@@ -8069,7 +8102,7 @@ function gradientToValueShape(gradient) {
|
|
|
8069
8102
|
...normalized,
|
|
8070
8103
|
interpolationSpace: normalized.interpolationSpace,
|
|
8071
8104
|
};
|
|
8072
|
-
if (normalized.interpolationSpace
|
|
8105
|
+
if (GRADIENT_HUE_SPACES.has(normalized.interpolationSpace)) {
|
|
8073
8106
|
output.hueInterpolation = normalized.hueInterpolation;
|
|
8074
8107
|
} else {
|
|
8075
8108
|
delete output.hueInterpolation;
|
|
@@ -8082,8 +8115,8 @@ function gradientInterpolationClause(gradient) {
|
|
|
8082
8115
|
if (normalized.interpolationSpace === "srgb") {
|
|
8083
8116
|
return "";
|
|
8084
8117
|
}
|
|
8085
|
-
if (normalized.interpolationSpace
|
|
8086
|
-
return `in
|
|
8118
|
+
if (GRADIENT_HUE_SPACES.has(normalized.interpolationSpace)) {
|
|
8119
|
+
return `in ${normalized.interpolationSpace} ${normalized.hueInterpolation} hue`;
|
|
8087
8120
|
}
|
|
8088
8121
|
return `in ${normalized.interpolationSpace}`;
|
|
8089
8122
|
}
|
|
@@ -8232,6 +8265,25 @@ function figSampleGradientAt(
|
|
|
8232
8265
|
r = rgb.r;
|
|
8233
8266
|
g = rgb.g;
|
|
8234
8267
|
b = rgb.b;
|
|
8268
|
+
} else if (space === "hsl") {
|
|
8269
|
+
const hsl1 = figRgbToHsl(c1.r, c1.g, c1.b);
|
|
8270
|
+
const hsl2 = figRgbToHsl(c2.r, c2.g, c2.b);
|
|
8271
|
+
const H = figInterpolateHue(
|
|
8272
|
+
hsl1.h,
|
|
8273
|
+
hsl2.h,
|
|
8274
|
+
t,
|
|
8275
|
+
hueInterpolation || "shorter",
|
|
8276
|
+
);
|
|
8277
|
+
const S = hsl1.s + (hsl2.s - hsl1.s) * t;
|
|
8278
|
+
const L = hsl1.l + (hsl2.l - hsl1.l) * t;
|
|
8279
|
+
const rgb = hslToSRGB(H, S, L);
|
|
8280
|
+
r = rgb[0];
|
|
8281
|
+
g = rgb[1];
|
|
8282
|
+
b = rgb[2];
|
|
8283
|
+
} else if (space === "srgb") {
|
|
8284
|
+
r = Math.round(c1.r + (c2.r - c1.r) * t);
|
|
8285
|
+
g = Math.round(c1.g + (c2.g - c1.g) * t);
|
|
8286
|
+
b = Math.round(c1.b + (c2.b - c1.b) * t);
|
|
8235
8287
|
} else {
|
|
8236
8288
|
const lab1 = figRGBToOklab(c1.r, c1.g, c1.b);
|
|
8237
8289
|
const lab2 = figRGBToOklab(c2.r, c2.g, c2.b);
|
|
@@ -8247,6 +8299,33 @@ function figSampleGradientAt(
|
|
|
8247
8299
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`.toUpperCase();
|
|
8248
8300
|
}
|
|
8249
8301
|
|
|
8302
|
+
function figRgbToHsl(r, g, b) {
|
|
8303
|
+
const R = r / 255;
|
|
8304
|
+
const G = g / 255;
|
|
8305
|
+
const B = b / 255;
|
|
8306
|
+
const max = Math.max(R, G, B);
|
|
8307
|
+
const min = Math.min(R, G, B);
|
|
8308
|
+
const l = (max + min) / 2;
|
|
8309
|
+
if (max === min) {
|
|
8310
|
+
return { h: 0, s: 0, l: l * 100 };
|
|
8311
|
+
}
|
|
8312
|
+
const d = max - min;
|
|
8313
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
8314
|
+
let h;
|
|
8315
|
+
switch (max) {
|
|
8316
|
+
case R:
|
|
8317
|
+
h = ((G - B) / d + (G < B ? 6 : 0)) / 6;
|
|
8318
|
+
break;
|
|
8319
|
+
case G:
|
|
8320
|
+
h = ((B - R) / d + 2) / 6;
|
|
8321
|
+
break;
|
|
8322
|
+
default:
|
|
8323
|
+
h = ((R - G) / d + 4) / 6;
|
|
8324
|
+
break;
|
|
8325
|
+
}
|
|
8326
|
+
return { h: h * 360, s: s * 100, l: l * 100 };
|
|
8327
|
+
}
|
|
8328
|
+
|
|
8250
8329
|
function hslToP3(h, s, l) {
|
|
8251
8330
|
const sRGB = hslToSRGB(h, s, l);
|
|
8252
8331
|
return sRGB.map((c) => +(c / 255).toFixed(4));
|
|
@@ -8311,6 +8390,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8311
8390
|
angle: 180,
|
|
8312
8391
|
interpolationSpace: "srgb",
|
|
8313
8392
|
hueInterpolation: "shorter",
|
|
8393
|
+
opacity: 1,
|
|
8314
8394
|
stops: [
|
|
8315
8395
|
{ position: 0, color: "#D9D9D9", opacity: 100 },
|
|
8316
8396
|
{ position: 100, color: "#737373", opacity: 100 },
|
|
@@ -8371,6 +8451,15 @@ class FigInputFill extends HTMLElement {
|
|
|
8371
8451
|
...this.#gradient,
|
|
8372
8452
|
...parsed.gradient,
|
|
8373
8453
|
});
|
|
8454
|
+
this.#gradient.opacity = this.#normalizeFillOpacity(
|
|
8455
|
+
parsed.gradient.opacity ?? parsed.opacity ?? parsed.alpha,
|
|
8456
|
+
this.#gradient.opacity ?? 1,
|
|
8457
|
+
);
|
|
8458
|
+
} else if (parsed.opacity !== undefined || parsed.alpha !== undefined) {
|
|
8459
|
+
this.#gradient.opacity = this.#normalizeFillOpacity(
|
|
8460
|
+
parsed.opacity ?? parsed.alpha,
|
|
8461
|
+
this.#gradient.opacity ?? 1,
|
|
8462
|
+
);
|
|
8374
8463
|
}
|
|
8375
8464
|
break;
|
|
8376
8465
|
case "image":
|
|
@@ -8446,10 +8535,20 @@ class FigInputFill extends HTMLElement {
|
|
|
8446
8535
|
}
|
|
8447
8536
|
}
|
|
8448
8537
|
|
|
8538
|
+
#normalizeFillOpacity(value, fallback = 1) {
|
|
8539
|
+
if (value === undefined || value === null || value === "") return fallback;
|
|
8540
|
+
const n = Number(value);
|
|
8541
|
+
if (!Number.isFinite(n)) return fallback;
|
|
8542
|
+
if (n > 1) return Math.max(0, Math.min(1, n / 100));
|
|
8543
|
+
return Math.max(0, Math.min(1, n));
|
|
8544
|
+
}
|
|
8545
|
+
|
|
8449
8546
|
#fillPickerSwatchAlpha() {
|
|
8450
8547
|
switch (this.#fillType) {
|
|
8451
8548
|
case "solid":
|
|
8452
8549
|
return this.#solid.alpha;
|
|
8550
|
+
case "gradient":
|
|
8551
|
+
return this.#gradient.opacity ?? 1;
|
|
8453
8552
|
case "image":
|
|
8454
8553
|
return this.#image.opacity ?? 1;
|
|
8455
8554
|
case "video":
|
|
@@ -8507,9 +8606,9 @@ class FigInputFill extends HTMLElement {
|
|
|
8507
8606
|
const opacityHtml = (value) =>
|
|
8508
8607
|
showAlpha
|
|
8509
8608
|
? `<fig-tooltip text="Opacity">
|
|
8510
|
-
<fig-input-number
|
|
8609
|
+
<fig-input-number
|
|
8511
8610
|
class="fig-input-fill-opacity"
|
|
8512
|
-
placeholder="##"
|
|
8611
|
+
placeholder="##"
|
|
8513
8612
|
min="0"
|
|
8514
8613
|
max="100"
|
|
8515
8614
|
value="${value}"
|
|
@@ -8539,7 +8638,8 @@ class FigInputFill extends HTMLElement {
|
|
|
8539
8638
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8540
8639
|
this.#gradient.type.slice(1);
|
|
8541
8640
|
controlsHtml = `
|
|
8542
|
-
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label
|
|
8641
|
+
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>
|
|
8642
|
+
${opacityHtml(Math.round((this.#gradient.opacity ?? 1) * 100))}`;
|
|
8543
8643
|
break;
|
|
8544
8644
|
}
|
|
8545
8645
|
|
|
@@ -8679,6 +8779,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8679
8779
|
this.#solid.alpha = alpha;
|
|
8680
8780
|
break;
|
|
8681
8781
|
case "gradient":
|
|
8782
|
+
this.#gradient.opacity = alpha;
|
|
8682
8783
|
break;
|
|
8683
8784
|
case "image":
|
|
8684
8785
|
this.#image.opacity = alpha;
|
|
@@ -8727,6 +8828,12 @@ class FigInputFill extends HTMLElement {
|
|
|
8727
8828
|
this.#gradient.type.slice(1);
|
|
8728
8829
|
label.textContent = newLabel;
|
|
8729
8830
|
}
|
|
8831
|
+
if (this.#opacityInput) {
|
|
8832
|
+
this.#opacityInput.setAttribute(
|
|
8833
|
+
"value",
|
|
8834
|
+
Math.round((this.#gradient.opacity ?? 1) * 100),
|
|
8835
|
+
);
|
|
8836
|
+
}
|
|
8730
8837
|
break;
|
|
8731
8838
|
}
|
|
8732
8839
|
case "image":
|
|
@@ -8803,7 +8910,18 @@ class FigInputFill extends HTMLElement {
|
|
|
8803
8910
|
this.#gradient.type.charAt(0).toUpperCase() +
|
|
8804
8911
|
this.#gradient.type.slice(1);
|
|
8805
8912
|
controlsHtml = `
|
|
8806
|
-
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label
|
|
8913
|
+
<label class="fig-input-fill-label">${figEscapeAttribute(gradientLabel)}</label>
|
|
8914
|
+
${showAlpha ? `<fig-tooltip text="Opacity">
|
|
8915
|
+
<fig-input-number
|
|
8916
|
+
class="fig-input-fill-opacity"
|
|
8917
|
+
placeholder="##"
|
|
8918
|
+
min="0"
|
|
8919
|
+
max="100"
|
|
8920
|
+
value="${Math.round((this.#gradient.opacity ?? 1) * 100)}"
|
|
8921
|
+
units="%"
|
|
8922
|
+
${disabled ? "disabled" : ""}>
|
|
8923
|
+
</fig-input-number>
|
|
8924
|
+
</fig-tooltip>` : ""}`;
|
|
8807
8925
|
break;
|
|
8808
8926
|
}
|
|
8809
8927
|
case "image":
|
|
@@ -8899,6 +9017,7 @@ class FigInputFill extends HTMLElement {
|
|
|
8899
9017
|
this.#solid.alpha = alpha;
|
|
8900
9018
|
break;
|
|
8901
9019
|
case "gradient":
|
|
9020
|
+
this.#gradient.opacity = alpha;
|
|
8902
9021
|
break;
|
|
8903
9022
|
case "image":
|
|
8904
9023
|
this.#image.opacity = alpha;
|
|
@@ -9743,6 +9862,11 @@ class FigInputGradient extends HTMLElement {
|
|
|
9743
9862
|
.join(", ");
|
|
9744
9863
|
const interp = gradientInterpolationClause(gradient);
|
|
9745
9864
|
const interpolation = interp ? ` ${interp}` : "";
|
|
9865
|
+
// Inline stop editor always previews L→R so handles match the track axis,
|
|
9866
|
+
// regardless of linear angle / radial / angular type.
|
|
9867
|
+
if (this.#editMode === "true") {
|
|
9868
|
+
return `linear-gradient(to right${interpolation}, ${stops})`;
|
|
9869
|
+
}
|
|
9746
9870
|
if (gradient.type === "radial") {
|
|
9747
9871
|
return `radial-gradient(circle at ${gradient.centerX}% ${gradient.centerY}%${interpolation}, ${stops})`;
|
|
9748
9872
|
}
|
|
@@ -11213,6 +11337,9 @@ customElements.define("fig-swatch", FigSwatch);
|
|
|
11213
11337
|
* @attr {string} aria-label - Accessible label forwarded to generated video
|
|
11214
11338
|
* @attr {string} aria-labelledby - Accessible label reference forwarded to generated video
|
|
11215
11339
|
*
|
|
11340
|
+
* When `controls` are shown and no video `src` is loaded, fig-media sets
|
|
11341
|
+
* `disabled` on the associated `fig-media-controls` until media is available.
|
|
11342
|
+
*
|
|
11216
11343
|
* Sizing model:
|
|
11217
11344
|
* - Default: host shrinkwraps to its inner <img>/<video> intrinsic size.
|
|
11218
11345
|
* - `size` attribute applies a token-sized square.
|
|
@@ -11551,12 +11678,41 @@ class FigMedia extends HTMLElement {
|
|
|
11551
11678
|
this.#mediaEl.playsInline = true;
|
|
11552
11679
|
this.#syncMediaAccessibility();
|
|
11553
11680
|
this.#syncControlsVisibility();
|
|
11681
|
+
this.#syncControlsDisabled();
|
|
11554
11682
|
}
|
|
11555
11683
|
|
|
11556
11684
|
get mediaEl() {
|
|
11557
11685
|
return this.#mediaEl;
|
|
11558
11686
|
}
|
|
11559
11687
|
|
|
11688
|
+
#hasPlayableVideo() {
|
|
11689
|
+
if (this.mediaKind !== "video") return false;
|
|
11690
|
+
return Boolean(this.#currentMediaSrc());
|
|
11691
|
+
}
|
|
11692
|
+
|
|
11693
|
+
#syncControlsDisabled() {
|
|
11694
|
+
const controls =
|
|
11695
|
+
this.#controlsEl ||
|
|
11696
|
+
this.querySelector(":scope > fig-media-controls");
|
|
11697
|
+
if (!controls) return;
|
|
11698
|
+
if (this.mediaKind !== "video") return;
|
|
11699
|
+
|
|
11700
|
+
if (this.#hasPlayableVideo()) {
|
|
11701
|
+
// Only clear disabled when we set it for the empty state.
|
|
11702
|
+
if (controls.hasAttribute("data-fig-empty-disabled")) {
|
|
11703
|
+
controls.removeAttribute("disabled");
|
|
11704
|
+
controls.removeAttribute("data-fig-empty-disabled");
|
|
11705
|
+
}
|
|
11706
|
+
return;
|
|
11707
|
+
}
|
|
11708
|
+
|
|
11709
|
+
controls.setAttribute("disabled", "");
|
|
11710
|
+
controls.setAttribute("data-fig-empty-disabled", "");
|
|
11711
|
+
controls.playing = false;
|
|
11712
|
+
controls.time = 0;
|
|
11713
|
+
controls.duration = 0;
|
|
11714
|
+
}
|
|
11715
|
+
|
|
11560
11716
|
#syncControlsVisibility() {
|
|
11561
11717
|
if (this.mediaKind !== "video") {
|
|
11562
11718
|
this.#removeControls();
|
|
@@ -11571,6 +11727,7 @@ class FigMedia extends HTMLElement {
|
|
|
11571
11727
|
this.#controlsEl = userControls;
|
|
11572
11728
|
}
|
|
11573
11729
|
this.#wireControlsToMedia();
|
|
11730
|
+
this.#syncControlsDisabled();
|
|
11574
11731
|
return;
|
|
11575
11732
|
}
|
|
11576
11733
|
if (this.#isEnabledAttr("controls", false)) {
|
|
@@ -11590,6 +11747,7 @@ class FigMedia extends HTMLElement {
|
|
|
11590
11747
|
if (existingControls) {
|
|
11591
11748
|
this.#controlsEl = existingControls;
|
|
11592
11749
|
this.#wireControlsToMedia();
|
|
11750
|
+
this.#syncControlsDisabled();
|
|
11593
11751
|
return;
|
|
11594
11752
|
}
|
|
11595
11753
|
const controls = document.createElement("fig-media-controls");
|
|
@@ -11597,6 +11755,7 @@ class FigMedia extends HTMLElement {
|
|
|
11597
11755
|
this.append(controls);
|
|
11598
11756
|
this.#controlsEl = controls;
|
|
11599
11757
|
this.#wireControlsToMedia();
|
|
11758
|
+
this.#syncControlsDisabled();
|
|
11600
11759
|
}
|
|
11601
11760
|
|
|
11602
11761
|
#wireControlsToMedia() {
|
|
@@ -11605,6 +11764,7 @@ class FigMedia extends HTMLElement {
|
|
|
11605
11764
|
this.#controlsWiredFor === this.#mediaEl &&
|
|
11606
11765
|
this.#controlsWiredControls === this.#controlsEl
|
|
11607
11766
|
) {
|
|
11767
|
+
this.#syncControlsDisabled();
|
|
11608
11768
|
return;
|
|
11609
11769
|
}
|
|
11610
11770
|
this.#unwireControls();
|
|
@@ -11616,6 +11776,8 @@ class FigMedia extends HTMLElement {
|
|
|
11616
11776
|
|
|
11617
11777
|
let pendingSeekTime = null;
|
|
11618
11778
|
const syncFromVideo = () => {
|
|
11779
|
+
this.#syncControlsDisabled();
|
|
11780
|
+
if (!this.#hasPlayableVideo()) return;
|
|
11619
11781
|
controls.playing = !video.paused && !video.ended;
|
|
11620
11782
|
if (Number.isFinite(video.duration)) controls.duration = video.duration;
|
|
11621
11783
|
if (pendingSeekTime !== null) {
|
|
@@ -11628,11 +11790,13 @@ class FigMedia extends HTMLElement {
|
|
|
11628
11790
|
controls.time = video.currentTime || 0;
|
|
11629
11791
|
};
|
|
11630
11792
|
const onPlay = () => {
|
|
11793
|
+
if (!this.#hasPlayableVideo()) return;
|
|
11631
11794
|
const p = video.play?.();
|
|
11632
11795
|
if (p && typeof p.catch === "function") p.catch(() => {});
|
|
11633
11796
|
};
|
|
11634
11797
|
const onPause = () => video.pause?.();
|
|
11635
11798
|
const onSeek = (e) => {
|
|
11799
|
+
if (!this.#hasPlayableVideo()) return;
|
|
11636
11800
|
const next = Number(e?.detail?.time);
|
|
11637
11801
|
if (!Number.isFinite(next)) return;
|
|
11638
11802
|
pendingSeekTime = next;
|
|
@@ -12206,13 +12370,14 @@ customElements.define("fig-card", FigCard);
|
|
|
12206
12370
|
* - `playing` (boolean presence) — current play/pause state
|
|
12207
12371
|
* - `duration` (number, seconds) — total track length
|
|
12208
12372
|
* - `time` (number, seconds) — current playhead position
|
|
12373
|
+
* - `disabled` (boolean) — disables play/seek interaction
|
|
12209
12374
|
*
|
|
12210
12375
|
* Events:
|
|
12211
12376
|
* - `play` — emitted when the user toggles playback on (detail: { playing: true })
|
|
12212
12377
|
* - `pause` — emitted when the user toggles playback off (detail: { playing: false })
|
|
12213
12378
|
* - `seek` — emitted when the user drags the scrubber (detail: { time })
|
|
12214
12379
|
*
|
|
12215
|
-
* Properties: `playing`, `duration`, `time` mirror the attributes.
|
|
12380
|
+
* Properties: `playing`, `duration`, `time`, `disabled` mirror the attributes.
|
|
12216
12381
|
*/
|
|
12217
12382
|
class FigMediaControls extends HTMLElement {
|
|
12218
12383
|
#playBtn = null;
|
|
@@ -12223,13 +12388,14 @@ class FigMediaControls extends HTMLElement {
|
|
|
12223
12388
|
#rendered = false;
|
|
12224
12389
|
|
|
12225
12390
|
static get observedAttributes() {
|
|
12226
|
-
return ["playing", "duration", "time"];
|
|
12391
|
+
return ["playing", "duration", "time", "disabled"];
|
|
12227
12392
|
}
|
|
12228
12393
|
|
|
12229
12394
|
connectedCallback() {
|
|
12230
12395
|
this.#render();
|
|
12231
12396
|
this.#syncPlayingUi();
|
|
12232
12397
|
this.#syncTimeUi();
|
|
12398
|
+
this.#syncDisabled();
|
|
12233
12399
|
}
|
|
12234
12400
|
|
|
12235
12401
|
get playing() {
|
|
@@ -12266,10 +12432,19 @@ class FigMediaControls extends HTMLElement {
|
|
|
12266
12432
|
this.setAttribute("time", String(n));
|
|
12267
12433
|
}
|
|
12268
12434
|
|
|
12435
|
+
get disabled() {
|
|
12436
|
+
return figBooleanAttribute(this, "disabled");
|
|
12437
|
+
}
|
|
12438
|
+
set disabled(value) {
|
|
12439
|
+
if (value) this.setAttribute("disabled", "");
|
|
12440
|
+
else this.removeAttribute("disabled");
|
|
12441
|
+
}
|
|
12442
|
+
|
|
12269
12443
|
attributeChangedCallback(name) {
|
|
12270
12444
|
if (!this.#rendered) return;
|
|
12271
12445
|
if (name === "playing") this.#syncPlayingUi();
|
|
12272
12446
|
if (name === "duration" || name === "time") this.#syncTimeUi();
|
|
12447
|
+
if (name === "disabled") this.#syncDisabled();
|
|
12273
12448
|
}
|
|
12274
12449
|
|
|
12275
12450
|
#render() {
|
|
@@ -12315,6 +12490,11 @@ class FigMediaControls extends HTMLElement {
|
|
|
12315
12490
|
timeEl.textContent = this.#formatTime(this.time);
|
|
12316
12491
|
|
|
12317
12492
|
const handleSeek = (e) => {
|
|
12493
|
+
if (this.disabled) {
|
|
12494
|
+
e.preventDefault?.();
|
|
12495
|
+
e.stopPropagation();
|
|
12496
|
+
return;
|
|
12497
|
+
}
|
|
12318
12498
|
const host = e.currentTarget;
|
|
12319
12499
|
const next = Number(host?.value);
|
|
12320
12500
|
if (!Number.isFinite(next)) return;
|
|
@@ -12340,6 +12520,20 @@ class FigMediaControls extends HTMLElement {
|
|
|
12340
12520
|
this.#playTooltip = tooltip;
|
|
12341
12521
|
this.#timeSlider = slider;
|
|
12342
12522
|
this.#timeEl = timeEl;
|
|
12523
|
+
this.#syncDisabled();
|
|
12524
|
+
}
|
|
12525
|
+
|
|
12526
|
+
#syncDisabled() {
|
|
12527
|
+
const disabled = this.disabled;
|
|
12528
|
+
this.setAttribute("aria-disabled", disabled ? "true" : "false");
|
|
12529
|
+
if (this.#playBtn) {
|
|
12530
|
+
if (disabled) this.#playBtn.setAttribute("disabled", "");
|
|
12531
|
+
else this.#playBtn.removeAttribute("disabled");
|
|
12532
|
+
}
|
|
12533
|
+
if (this.#timeSlider) {
|
|
12534
|
+
if (disabled) this.#timeSlider.setAttribute("disabled", "");
|
|
12535
|
+
else this.#timeSlider.removeAttribute("disabled");
|
|
12536
|
+
}
|
|
12343
12537
|
}
|
|
12344
12538
|
|
|
12345
12539
|
#formatTime(seconds) {
|
|
@@ -12385,6 +12579,7 @@ class FigMediaControls extends HTMLElement {
|
|
|
12385
12579
|
}
|
|
12386
12580
|
|
|
12387
12581
|
toggle() {
|
|
12582
|
+
if (this.disabled) return;
|
|
12388
12583
|
const next = !this.playing;
|
|
12389
12584
|
this.playing = next;
|
|
12390
12585
|
this.dispatchEvent(
|
|
@@ -12397,12 +12592,12 @@ class FigMediaControls extends HTMLElement {
|
|
|
12397
12592
|
}
|
|
12398
12593
|
|
|
12399
12594
|
play() {
|
|
12400
|
-
if (this.playing) return;
|
|
12595
|
+
if (this.disabled || this.playing) return;
|
|
12401
12596
|
this.toggle();
|
|
12402
12597
|
}
|
|
12403
12598
|
|
|
12404
12599
|
pause() {
|
|
12405
|
-
if (!this.playing) return;
|
|
12600
|
+
if (this.disabled || !this.playing) return;
|
|
12406
12601
|
this.toggle();
|
|
12407
12602
|
}
|
|
12408
12603
|
}
|
|
@@ -15427,6 +15622,314 @@ class FigPreview extends HTMLElement {
|
|
|
15427
15622
|
}
|
|
15428
15623
|
customElements.define("fig-preview", FigPreview);
|
|
15429
15624
|
|
|
15625
|
+
/**
|
|
15626
|
+
* Compact swatch previewing gradient color-space interpolation.
|
|
15627
|
+
* Polar: CSS conic-gradient masked (SVG data-URL, round linecaps) to an arc.
|
|
15628
|
+
* Non-polar: CSS linear-gradient masked to a horizontal round-capped stroke.
|
|
15629
|
+
* Accepts the same `value` shape as fig-input-gradient / fig-fill-picker.
|
|
15630
|
+
*
|
|
15631
|
+
* @element fig-interpolation-swatch
|
|
15632
|
+
* @attr {string} value - JSON `{ type: "gradient", gradient: { … } }` (or a bare gradient object)
|
|
15633
|
+
* @attr {string} size - `small` (default, 24px) or `large` (32px)
|
|
15634
|
+
*/
|
|
15635
|
+
class FigInterpolationSwatch extends HTMLElement {
|
|
15636
|
+
static #HUE_SPACES = new Set(["oklch", "hsl"]);
|
|
15637
|
+
static #CX = 10;
|
|
15638
|
+
static #CY = 10;
|
|
15639
|
+
static #R = 8;
|
|
15640
|
+
static #STROKE = 3;
|
|
15641
|
+
// Polar endpoints ≈ 10 o'clock → 2 o'clock (SVG deg: 0 = east, CW).
|
|
15642
|
+
// CSS conic `from` is 0 = north; convert with +90.
|
|
15643
|
+
static #START_DEG = 210;
|
|
15644
|
+
static #DEFAULT_GRADIENT = {
|
|
15645
|
+
type: "linear",
|
|
15646
|
+
angle: 135,
|
|
15647
|
+
interpolationSpace: "srgb",
|
|
15648
|
+
hueInterpolation: "shorter",
|
|
15649
|
+
stops: [
|
|
15650
|
+
{ color: "#FF0000", position: 0, opacity: 100 },
|
|
15651
|
+
{ color: "#4F9EFF", position: 100, opacity: 100 },
|
|
15652
|
+
],
|
|
15653
|
+
};
|
|
15654
|
+
|
|
15655
|
+
#rendered = false;
|
|
15656
|
+
#svgEl = null;
|
|
15657
|
+
#fillEl = null;
|
|
15658
|
+
#gradient = { ...FigInterpolationSwatch.#DEFAULT_GRADIENT };
|
|
15659
|
+
|
|
15660
|
+
static get observedAttributes() {
|
|
15661
|
+
return ["value"];
|
|
15662
|
+
}
|
|
15663
|
+
|
|
15664
|
+
get value() {
|
|
15665
|
+
return {
|
|
15666
|
+
type: "gradient",
|
|
15667
|
+
gradient: { ...this.#gradient },
|
|
15668
|
+
};
|
|
15669
|
+
}
|
|
15670
|
+
|
|
15671
|
+
set value(val) {
|
|
15672
|
+
if (val == null || val === "") {
|
|
15673
|
+
this.removeAttribute("value");
|
|
15674
|
+
return;
|
|
15675
|
+
}
|
|
15676
|
+
if (typeof val === "string") {
|
|
15677
|
+
this.setAttribute("value", val);
|
|
15678
|
+
return;
|
|
15679
|
+
}
|
|
15680
|
+
this.setAttribute("value", JSON.stringify(val));
|
|
15681
|
+
}
|
|
15682
|
+
|
|
15683
|
+
connectedCallback() {
|
|
15684
|
+
this.#ensureA11y();
|
|
15685
|
+
this.#parseValue();
|
|
15686
|
+
this.#render();
|
|
15687
|
+
this.#updatePreview();
|
|
15688
|
+
}
|
|
15689
|
+
|
|
15690
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
15691
|
+
if (oldValue === newValue) return;
|
|
15692
|
+
if (name !== "value") return;
|
|
15693
|
+
this.#parseValue();
|
|
15694
|
+
if (this.#rendered) this.#updatePreview();
|
|
15695
|
+
}
|
|
15696
|
+
|
|
15697
|
+
#ensureA11y() {
|
|
15698
|
+
const named =
|
|
15699
|
+
this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby");
|
|
15700
|
+
if (!named && !this.hasAttribute("aria-hidden")) {
|
|
15701
|
+
this.setAttribute("aria-hidden", "true");
|
|
15702
|
+
}
|
|
15703
|
+
}
|
|
15704
|
+
|
|
15705
|
+
#parseValue() {
|
|
15706
|
+
const valueAttr = this.getAttribute("value");
|
|
15707
|
+
if (!valueAttr) {
|
|
15708
|
+
this.#gradient = {
|
|
15709
|
+
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
15710
|
+
stops: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({
|
|
15711
|
+
...s,
|
|
15712
|
+
})),
|
|
15713
|
+
};
|
|
15714
|
+
return;
|
|
15715
|
+
}
|
|
15716
|
+
try {
|
|
15717
|
+
const parsed = JSON.parse(valueAttr);
|
|
15718
|
+
const gradient = parsed?.type === "gradient" && parsed.gradient
|
|
15719
|
+
? parsed.gradient
|
|
15720
|
+
: parsed?.gradient
|
|
15721
|
+
? parsed.gradient
|
|
15722
|
+
: parsed;
|
|
15723
|
+
if (!gradient || typeof gradient !== "object") return;
|
|
15724
|
+
this.#gradient = this.#normalizeGradient({
|
|
15725
|
+
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
15726
|
+
...gradient,
|
|
15727
|
+
});
|
|
15728
|
+
} catch {
|
|
15729
|
+
// Keep current/default gradient on invalid JSON.
|
|
15730
|
+
}
|
|
15731
|
+
}
|
|
15732
|
+
|
|
15733
|
+
#normalizeGradient(gradient) {
|
|
15734
|
+
const next = { ...(gradient ?? {}) };
|
|
15735
|
+
const interpolationSpace = String(
|
|
15736
|
+
next.interpolationSpace ?? "srgb",
|
|
15737
|
+
).toLowerCase();
|
|
15738
|
+
const hueInterpolation = String(
|
|
15739
|
+
next.hueInterpolation ?? "shorter",
|
|
15740
|
+
).toLowerCase();
|
|
15741
|
+
const stops = Array.isArray(next.stops)
|
|
15742
|
+
? next.stops.map((stop) => ({
|
|
15743
|
+
color: String(stop?.color || "#D9D9D9").replace(
|
|
15744
|
+
/^(#(?:[0-9a-f]{6})).*/i,
|
|
15745
|
+
"$1",
|
|
15746
|
+
),
|
|
15747
|
+
position: stop?.position ?? 0,
|
|
15748
|
+
opacity: stop?.opacity ?? 100,
|
|
15749
|
+
}))
|
|
15750
|
+
: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({ ...s }));
|
|
15751
|
+
if (stops.length < 2) {
|
|
15752
|
+
return {
|
|
15753
|
+
...FigInterpolationSwatch.#DEFAULT_GRADIENT,
|
|
15754
|
+
stops: FigInterpolationSwatch.#DEFAULT_GRADIENT.stops.map((s) => ({
|
|
15755
|
+
...s,
|
|
15756
|
+
})),
|
|
15757
|
+
};
|
|
15758
|
+
}
|
|
15759
|
+
return {
|
|
15760
|
+
type: ["linear", "radial", "angular"].includes(next.type)
|
|
15761
|
+
? next.type
|
|
15762
|
+
: "linear",
|
|
15763
|
+
angle: Number.isFinite(Number(next.angle)) ? Number(next.angle) : 135,
|
|
15764
|
+
interpolationSpace,
|
|
15765
|
+
hueInterpolation,
|
|
15766
|
+
stops,
|
|
15767
|
+
};
|
|
15768
|
+
}
|
|
15769
|
+
|
|
15770
|
+
#isPolar() {
|
|
15771
|
+
return FigInterpolationSwatch.#HUE_SPACES.has(
|
|
15772
|
+
this.#gradient.interpolationSpace || "srgb",
|
|
15773
|
+
);
|
|
15774
|
+
}
|
|
15775
|
+
|
|
15776
|
+
#hueForColor(color) {
|
|
15777
|
+
const { r, g, b } = figHexToRGB(color);
|
|
15778
|
+
if (this.#gradient.interpolationSpace === "hsl") {
|
|
15779
|
+
return figRgbToHsl(r, g, b).h;
|
|
15780
|
+
}
|
|
15781
|
+
const lab = figRGBToOklab(r, g, b);
|
|
15782
|
+
const hue = figOklabToOklch(lab.l, lab.a, lab.b).h;
|
|
15783
|
+
return ((hue % 360) + 360) % 360;
|
|
15784
|
+
}
|
|
15785
|
+
|
|
15786
|
+
#polarArcGeometry() {
|
|
15787
|
+
const stops = this.#sortedStops();
|
|
15788
|
+
const startHue = this.#hueForColor(stops[0]?.color || "#FF0000");
|
|
15789
|
+
const endHue = this.#hueForColor(
|
|
15790
|
+
stops[stops.length - 1]?.color || "#4F9EFF",
|
|
15791
|
+
);
|
|
15792
|
+
const startDeg = FigInterpolationSwatch.#START_DEG - startHue;
|
|
15793
|
+
const endDeg = FigInterpolationSwatch.#START_DEG - endHue;
|
|
15794
|
+
const clockwiseSweep = ((endDeg - startDeg) % 360 + 360) % 360;
|
|
15795
|
+
const counterclockwiseSweep =
|
|
15796
|
+
clockwiseSweep === 0 ? 0 : clockwiseSweep - 360;
|
|
15797
|
+
const method = this.#gradient.hueInterpolation || "shorter";
|
|
15798
|
+
|
|
15799
|
+
let sweepDeg;
|
|
15800
|
+
if (method === "increasing") {
|
|
15801
|
+
sweepDeg = counterclockwiseSweep;
|
|
15802
|
+
} else if (method === "decreasing") {
|
|
15803
|
+
sweepDeg = clockwiseSweep;
|
|
15804
|
+
} else if (method === "longer") {
|
|
15805
|
+
sweepDeg =
|
|
15806
|
+
clockwiseSweep < 180 ? counterclockwiseSweep : clockwiseSweep;
|
|
15807
|
+
} else {
|
|
15808
|
+
sweepDeg =
|
|
15809
|
+
clockwiseSweep <= 180 ? clockwiseSweep : counterclockwiseSweep;
|
|
15810
|
+
}
|
|
15811
|
+
|
|
15812
|
+
// A round cap extends beyond the path endpoint. Inset the centerline so
|
|
15813
|
+
// the visible cap edges, rather than their centers, land on the hues.
|
|
15814
|
+
const direction = Math.sign(sweepDeg);
|
|
15815
|
+
const capAngle =
|
|
15816
|
+
(Math.asin(FigInterpolationSwatch.#STROKE / 2 / FigInterpolationSwatch.#R) *
|
|
15817
|
+
180) /
|
|
15818
|
+
Math.PI;
|
|
15819
|
+
const inset = Math.min(
|
|
15820
|
+
capAngle,
|
|
15821
|
+
Math.max(0, (Math.abs(sweepDeg) - 0.01) / 2),
|
|
15822
|
+
);
|
|
15823
|
+
return {
|
|
15824
|
+
startDeg: startDeg + direction * inset,
|
|
15825
|
+
sweepDeg: sweepDeg - direction * inset * 2,
|
|
15826
|
+
};
|
|
15827
|
+
}
|
|
15828
|
+
|
|
15829
|
+
#pointOnCircle(deg) {
|
|
15830
|
+
const rad = (deg * Math.PI) / 180;
|
|
15831
|
+
return {
|
|
15832
|
+
x: FigInterpolationSwatch.#CX + FigInterpolationSwatch.#R * Math.cos(rad),
|
|
15833
|
+
y: FigInterpolationSwatch.#CY + FigInterpolationSwatch.#R * Math.sin(rad),
|
|
15834
|
+
};
|
|
15835
|
+
}
|
|
15836
|
+
|
|
15837
|
+
#arcMaskPath(startDeg, sweepDeg) {
|
|
15838
|
+
const endDeg = startDeg + sweepDeg;
|
|
15839
|
+
const start = this.#pointOnCircle(startDeg);
|
|
15840
|
+
const end = this.#pointOnCircle(endDeg);
|
|
15841
|
+
const largeArc = Math.abs(sweepDeg) > 180 ? 1 : 0;
|
|
15842
|
+
const sweepFlag = sweepDeg >= 0 ? 1 : 0;
|
|
15843
|
+
return `M ${start.x} ${start.y} A ${FigInterpolationSwatch.#R} ${FigInterpolationSwatch.#R} 0 ${largeArc} ${sweepFlag} ${end.x} ${end.y}`;
|
|
15844
|
+
}
|
|
15845
|
+
|
|
15846
|
+
#lineMaskPath() {
|
|
15847
|
+
const start = this.#pointOnCircle(180);
|
|
15848
|
+
const end = this.#pointOnCircle(0);
|
|
15849
|
+
return `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
|
|
15850
|
+
}
|
|
15851
|
+
|
|
15852
|
+
#maskImageForPath(d) {
|
|
15853
|
+
const stroke = FigInterpolationSwatch.#STROKE;
|
|
15854
|
+
const svg =
|
|
15855
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">` +
|
|
15856
|
+
`<path d="${d}" fill="none" stroke="white" stroke-width="${stroke}" ` +
|
|
15857
|
+
`stroke-linecap="round" stroke-linejoin="round"/>` +
|
|
15858
|
+
`</svg>`;
|
|
15859
|
+
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
15860
|
+
}
|
|
15861
|
+
|
|
15862
|
+
#sortedStops() {
|
|
15863
|
+
return [...this.#gradient.stops].sort(
|
|
15864
|
+
(a, b) => (a.position ?? 0) - (b.position ?? 0),
|
|
15865
|
+
);
|
|
15866
|
+
}
|
|
15867
|
+
|
|
15868
|
+
#cssInterpolationClause() {
|
|
15869
|
+
const space = this.#gradient.interpolationSpace || "srgb";
|
|
15870
|
+
if (space === "srgb") return "";
|
|
15871
|
+
if (FigInterpolationSwatch.#HUE_SPACES.has(space)) {
|
|
15872
|
+
return ` in ${space} ${this.#gradient.hueInterpolation || "shorter"} hue`;
|
|
15873
|
+
}
|
|
15874
|
+
return ` in ${space}`;
|
|
15875
|
+
}
|
|
15876
|
+
|
|
15877
|
+
#previewBackground() {
|
|
15878
|
+
const stops = this.#sortedStops();
|
|
15879
|
+
const clause = this.#cssInterpolationClause();
|
|
15880
|
+
if (this.#isPolar()) {
|
|
15881
|
+
// Fixed hue wheel. The mask maps gradient endpoint hues onto this wheel.
|
|
15882
|
+
const cssFrom = (FigInterpolationSwatch.#START_DEG + 90) % 360;
|
|
15883
|
+
const space = this.#gradient.interpolationSpace;
|
|
15884
|
+
const wheelColor = (hue) =>
|
|
15885
|
+
space === "oklch"
|
|
15886
|
+
? `oklch(65% 0.25 ${hue})`
|
|
15887
|
+
: `hsl(${hue} 100% 50%)`;
|
|
15888
|
+
const wheelStops = [0, 300, 240, 180, 120, 60, 0]
|
|
15889
|
+
.map(wheelColor)
|
|
15890
|
+
.join(", ");
|
|
15891
|
+
return `conic-gradient(from ${cssFrom}deg in ${space} decreasing hue, ${wheelStops})`;
|
|
15892
|
+
}
|
|
15893
|
+
const stopList = stops
|
|
15894
|
+
.map((s) => `${s.color} ${s.position ?? 0}%`)
|
|
15895
|
+
.join(", ");
|
|
15896
|
+
return `linear-gradient(90deg${clause}, ${stopList})`;
|
|
15897
|
+
}
|
|
15898
|
+
|
|
15899
|
+
#render() {
|
|
15900
|
+
if (this.#rendered) return;
|
|
15901
|
+
const stroke = FigInterpolationSwatch.#STROKE;
|
|
15902
|
+
this.innerHTML = `
|
|
15903
|
+
<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">
|
|
15904
|
+
<circle class="fig-interpolation-swatch-rim" cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="${stroke}"/>
|
|
15905
|
+
</svg>
|
|
15906
|
+
<div class="fig-interpolation-swatch-fill" aria-hidden="true"></div>
|
|
15907
|
+
`;
|
|
15908
|
+
this.#svgEl = this.querySelector(".fig-interpolation-swatch-svg");
|
|
15909
|
+
this.#fillEl = this.querySelector(".fig-interpolation-swatch-fill");
|
|
15910
|
+
this.#rendered = true;
|
|
15911
|
+
}
|
|
15912
|
+
|
|
15913
|
+
#updatePreview() {
|
|
15914
|
+
if (!this.#fillEl) return;
|
|
15915
|
+
|
|
15916
|
+
const polar = this.#isPolar();
|
|
15917
|
+
if (this.#svgEl) this.#svgEl.style.display = polar ? "" : "none";
|
|
15918
|
+
|
|
15919
|
+
const d = polar
|
|
15920
|
+
? (() => {
|
|
15921
|
+
const { startDeg, sweepDeg } = this.#polarArcGeometry();
|
|
15922
|
+
return this.#arcMaskPath(startDeg, sweepDeg);
|
|
15923
|
+
})()
|
|
15924
|
+
: this.#lineMaskPath();
|
|
15925
|
+
const mask = this.#maskImageForPath(d);
|
|
15926
|
+
this.#fillEl.style.setProperty("-webkit-mask-image", mask);
|
|
15927
|
+
this.#fillEl.style.maskImage = mask;
|
|
15928
|
+
this.#fillEl.style.background = this.#previewBackground();
|
|
15929
|
+
}
|
|
15930
|
+
}
|
|
15931
|
+
customElements.define("fig-interpolation-swatch", FigInterpolationSwatch);
|
|
15932
|
+
|
|
15430
15933
|
/** @type {Record<string, string | { medium: string, small: string }>} */
|
|
15431
15934
|
const FIG_ICON_TOKENS = {
|
|
15432
15935
|
chevron: "--icon-16-chevron",
|