@rogieking/figui3 11.6.1 → 11.6.3
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/.cursor/skills/fig-editor/components.md +10 -5
- package/.cursor/skills/fig-editor/reference.md +10 -4
- package/.cursor/skills/figui3/reference.md +8 -0
- package/CHANGELOG.md +32 -0
- package/README.md +28 -7
- package/dist/fig-color.js +1 -0
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +3 -3
- package/dist/fig-property.css +1 -1
- package/dist/fig.js +5 -5
- package/fig-color.js +406 -0
- package/fig-editor.css +1 -16
- package/fig-editor.js +39 -594
- package/fig-gradient-editor.css +6 -1
- package/fig-gradient-editor.js +622 -237
- package/fig-palette-editor.css +20 -0
- package/fig-palette-editor.js +815 -0
- package/fig-popup-gradient.js +9 -1
- package/fig-popup-palette.js +13 -2
- package/fig-property.css +4 -0
- package/fig.js +16 -165
- package/package.json +7 -2
package/fig-editor.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
/* Copyright (c) 2026 Rogie King. PolyForm Shield 1.0.0. See LICENSE. */
|
|
2
2
|
import "./fig.js";
|
|
3
3
|
import "./fig-property.js";
|
|
4
|
+
import {
|
|
5
|
+
figConvertRGB,
|
|
6
|
+
figHexToRGB as figEditorHexToRgb,
|
|
7
|
+
figHslToRGB,
|
|
8
|
+
figHsvToRGB,
|
|
9
|
+
figOklabToOklch as figEditorOklabToOklch,
|
|
10
|
+
figOklabToRGB,
|
|
11
|
+
figOklchToRGB,
|
|
12
|
+
figRGBToHsl as figEditorRgbToHsl,
|
|
13
|
+
figRGBToHsv,
|
|
14
|
+
figRGBToOklab as figEditorRgbToOklab,
|
|
15
|
+
figRGBToOklch,
|
|
16
|
+
} from "./fig-color.js";
|
|
4
17
|
import { figGradientCSS } from "./fig-gradient-editor.js";
|
|
18
|
+
import "./fig-palette-editor.js";
|
|
5
19
|
import "./fig-popup-easing.js";
|
|
6
20
|
import "./fig-popup-gradient.js";
|
|
7
21
|
import "./fig-popup-palette.js";
|
|
@@ -195,73 +209,6 @@ class FigInputPoint extends HTMLElement {
|
|
|
195
209
|
}
|
|
196
210
|
figEditorDefineElement("fig-input-point", FigInputPoint);
|
|
197
211
|
|
|
198
|
-
function figEditorNormalizePaletteValue(value) {
|
|
199
|
-
let entries = value;
|
|
200
|
-
if (typeof entries === "string") {
|
|
201
|
-
const raw = entries.trim();
|
|
202
|
-
if (!raw) return [];
|
|
203
|
-
try {
|
|
204
|
-
entries = JSON.parse(raw);
|
|
205
|
-
} catch {
|
|
206
|
-
entries = raw.includes(",") ? raw.split(",") : [raw];
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (!Array.isArray(entries)) return [];
|
|
210
|
-
return entries.map((entry) => {
|
|
211
|
-
if (typeof entry === "string") {
|
|
212
|
-
const color = entry.trim();
|
|
213
|
-
return {
|
|
214
|
-
color: color.slice(0, 7) || "#D9D9D9",
|
|
215
|
-
alpha:
|
|
216
|
-
color.length > 7
|
|
217
|
-
? Math.max(0, Math.min(1, parseInt(color.slice(7, 9), 16) / 255))
|
|
218
|
-
: 1,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
if (entry && typeof entry === "object") {
|
|
222
|
-
const alpha =
|
|
223
|
-
entry.alpha !== undefined
|
|
224
|
-
? Number(entry.alpha)
|
|
225
|
-
: entry.opacity !== undefined
|
|
226
|
-
? Number(entry.opacity) / 100
|
|
227
|
-
: 1;
|
|
228
|
-
return {
|
|
229
|
-
color: String(entry.color || "#D9D9D9").slice(0, 7),
|
|
230
|
-
alpha: Number.isFinite(alpha) ? Math.max(0, Math.min(1, alpha)) : 1,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
return { color: "#D9D9D9", alpha: 1 };
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function figEditorPaletteEntryValue(entry) {
|
|
238
|
-
const color = String(entry?.color || "#D9D9D9").slice(0, 7);
|
|
239
|
-
const alpha = Math.max(0, Math.min(1, Number(entry?.alpha ?? 1)));
|
|
240
|
-
if (alpha >= 1) return color;
|
|
241
|
-
return `${color}${Math.round(alpha * 255)
|
|
242
|
-
.toString(16)
|
|
243
|
-
.padStart(2, "0")}`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function figEditorAveragePaletteEntries(first, second) {
|
|
247
|
-
if (!first && !second) return { color: "#D9D9D9", alpha: 1 };
|
|
248
|
-
if (!first || !second) return { ...(first || second) };
|
|
249
|
-
const firstRgb = figEditorHexToRgb(first.color);
|
|
250
|
-
const secondRgb = figEditorHexToRgb(second.color);
|
|
251
|
-
const color = `#${["r", "g", "b"]
|
|
252
|
-
.map((channel) =>
|
|
253
|
-
Math.round((firstRgb[channel] + secondRgb[channel]) / 2)
|
|
254
|
-
.toString(16)
|
|
255
|
-
.padStart(2, "0"),
|
|
256
|
-
)
|
|
257
|
-
.join("")
|
|
258
|
-
.toUpperCase()}`;
|
|
259
|
-
return {
|
|
260
|
-
color,
|
|
261
|
-
alpha: (Number(first.alpha ?? 1) + Number(second.alpha ?? 1)) / 2,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
212
|
const FIG_EDITOR_SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
266
213
|
|
|
267
214
|
function figEditorCreateSvgElement(tagName, attributes, children) {
|
|
@@ -279,73 +226,6 @@ function figEditorCreateSvgElement(tagName, attributes, children) {
|
|
|
279
226
|
return figEditorAppendChildren(element, children);
|
|
280
227
|
}
|
|
281
228
|
|
|
282
|
-
function figEditorHexToRgb(hex) {
|
|
283
|
-
const h = String(hex || "").replace(/^#/, "");
|
|
284
|
-
return {
|
|
285
|
-
r: parseInt(h.substring(0, 2), 16) || 0,
|
|
286
|
-
g: parseInt(h.substring(2, 4), 16) || 0,
|
|
287
|
-
b: parseInt(h.substring(4, 6), 16) || 0,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function figEditorRgbToHsl(r, g, b) {
|
|
292
|
-
const R = r / 255;
|
|
293
|
-
const G = g / 255;
|
|
294
|
-
const B = b / 255;
|
|
295
|
-
const max = Math.max(R, G, B);
|
|
296
|
-
const min = Math.min(R, G, B);
|
|
297
|
-
const l = (max + min) / 2;
|
|
298
|
-
if (max === min) return { h: 0, s: 0, l: l * 100 };
|
|
299
|
-
const d = max - min;
|
|
300
|
-
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
301
|
-
let h;
|
|
302
|
-
switch (max) {
|
|
303
|
-
case R:
|
|
304
|
-
h = ((G - B) / d + (G < B ? 6 : 0)) / 6;
|
|
305
|
-
break;
|
|
306
|
-
case G:
|
|
307
|
-
h = ((B - R) / d + 2) / 6;
|
|
308
|
-
break;
|
|
309
|
-
default:
|
|
310
|
-
h = ((R - G) / d + 4) / 6;
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
return { h: h * 360, s: s * 100, l: l * 100 };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function figEditorRgbToLinear(c) {
|
|
317
|
-
const s = c / 255;
|
|
318
|
-
return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function figEditorRgbToOklab(r, g, b) {
|
|
322
|
-
const lr = figEditorRgbToLinear(r);
|
|
323
|
-
const lg = figEditorRgbToLinear(g);
|
|
324
|
-
const lb = figEditorRgbToLinear(b);
|
|
325
|
-
const l_ = Math.cbrt(
|
|
326
|
-
0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb,
|
|
327
|
-
);
|
|
328
|
-
const m_ = Math.cbrt(
|
|
329
|
-
0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb,
|
|
330
|
-
);
|
|
331
|
-
const s_ = Math.cbrt(
|
|
332
|
-
0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb,
|
|
333
|
-
);
|
|
334
|
-
return {
|
|
335
|
-
l: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
|
|
336
|
-
a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
|
|
337
|
-
b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_,
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function figEditorOklabToOklch(L, a, b) {
|
|
342
|
-
return {
|
|
343
|
-
l: L,
|
|
344
|
-
c: Math.sqrt(a * a + b * b),
|
|
345
|
-
h: (Math.atan2(b, a) * 180) / Math.PI,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
229
|
function figEditorCreateOverflowButtons({
|
|
350
230
|
owner,
|
|
351
231
|
onStart,
|
|
@@ -460,262 +340,6 @@ function figEditorScrollElementToCenter(
|
|
|
460
340
|
});
|
|
461
341
|
}
|
|
462
342
|
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* A reusable editor for an ordered palette of solid colors.
|
|
466
|
-
* @attr {string} value - JSON array of hex strings or {color,alpha} objects, or comma-separated hex
|
|
467
|
-
* @attr {number} min - Minimum number of colors (default: 2)
|
|
468
|
-
* @attr {number} max - Maximum number of colors (unbounded when omitted)
|
|
469
|
-
* @attr {boolean} fixed - Locks palette length while preserving color editing and reordering
|
|
470
|
-
* @attr {boolean} disabled - Disables editing and reordering
|
|
471
|
-
* @fires input - During color editing or reordering (detail: normalized color array)
|
|
472
|
-
* @fires change - On committed color edits, add, remove, or reorder
|
|
473
|
-
*/
|
|
474
|
-
class FigPaletteEditor extends HTMLElement {
|
|
475
|
-
#colors = [];
|
|
476
|
-
#syncingValueAttribute = false;
|
|
477
|
-
|
|
478
|
-
static get observedAttributes() {
|
|
479
|
-
return ["value", "min", "max", "fixed", "disabled"];
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
get #min() {
|
|
483
|
-
const value = Number.parseInt(this.getAttribute("min"), 10);
|
|
484
|
-
return Number.isFinite(value) ? Math.max(0, value) : 2;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
get #max() {
|
|
488
|
-
const value = Number.parseInt(this.getAttribute("max"), 10);
|
|
489
|
-
return Number.isFinite(value) ? Math.max(this.#min, value) : Infinity;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
get #fixed() {
|
|
493
|
-
return figEditorBooleanAttribute(this, "fixed");
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
get #disabled() {
|
|
497
|
-
return figEditorBooleanAttribute(this, "disabled");
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
get value() {
|
|
501
|
-
return this.#colors.map((entry) => ({ ...entry }));
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
set value(value) {
|
|
505
|
-
this.setAttribute(
|
|
506
|
-
"value",
|
|
507
|
-
typeof value === "string" ? value : JSON.stringify(value ?? []),
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
connectedCallback() {
|
|
512
|
-
this.#colors = figEditorNormalizePaletteValue(this.getAttribute("value"));
|
|
513
|
-
this.#render();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
517
|
-
if (oldValue === newValue || this.#syncingValueAttribute) return;
|
|
518
|
-
if (name === "value") {
|
|
519
|
-
this.#colors = figEditorNormalizePaletteValue(newValue);
|
|
520
|
-
}
|
|
521
|
-
if (this.isConnected) this.#render();
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
#reflectValue() {
|
|
525
|
-
this.#syncingValueAttribute = true;
|
|
526
|
-
this.setAttribute("value", JSON.stringify(this.#colors));
|
|
527
|
-
this.#syncingValueAttribute = false;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
#emit(type) {
|
|
531
|
-
this.dispatchEvent(
|
|
532
|
-
new CustomEvent(type, {
|
|
533
|
-
bubbles: true,
|
|
534
|
-
detail: this.value,
|
|
535
|
-
}),
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
#commit(type, { rerender = false } = {}) {
|
|
540
|
-
this.#reflectValue();
|
|
541
|
-
if (rerender) this.#render();
|
|
542
|
-
this.#emit(type);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
#removeRow(row) {
|
|
546
|
-
const currentIndex = Number.parseInt(row?.dataset.index ?? "", 10);
|
|
547
|
-
if (
|
|
548
|
-
this.#disabled ||
|
|
549
|
-
this.#fixed ||
|
|
550
|
-
this.#colors.length <= this.#min ||
|
|
551
|
-
!Number.isInteger(currentIndex)
|
|
552
|
-
) {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
this.#colors.splice(currentIndex, 1);
|
|
556
|
-
this.#reflectValue();
|
|
557
|
-
this.#render();
|
|
558
|
-
this.#emit("input");
|
|
559
|
-
this.#emit("change");
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
#createRow(entry, index) {
|
|
563
|
-
const colorInput = figEditorCreateElement("fig-input-color", {
|
|
564
|
-
className: "fig-palette-editor-color",
|
|
565
|
-
slot: "input",
|
|
566
|
-
value: figEditorPaletteEntryValue(entry),
|
|
567
|
-
text: true,
|
|
568
|
-
alpha: true,
|
|
569
|
-
full: true,
|
|
570
|
-
disabled: this.#disabled || null,
|
|
571
|
-
"aria-label": `Palette color ${index + 1}`,
|
|
572
|
-
});
|
|
573
|
-
const syncColor = (event, type) => {
|
|
574
|
-
event.stopPropagation();
|
|
575
|
-
const row = colorInput.closest(".fig-palette-editor-color-row");
|
|
576
|
-
const currentIndex = Number.parseInt(row?.dataset.index ?? "", 10);
|
|
577
|
-
if (!Number.isInteger(currentIndex) || !this.#colors[currentIndex]) return;
|
|
578
|
-
this.#colors[currentIndex] = {
|
|
579
|
-
color:
|
|
580
|
-
colorInput.hexOpaque ||
|
|
581
|
-
this.#colors[currentIndex]?.color ||
|
|
582
|
-
"#D9D9D9",
|
|
583
|
-
alpha:
|
|
584
|
-
colorInput.rgba?.a ?? this.#colors[currentIndex]?.alpha ?? 1,
|
|
585
|
-
};
|
|
586
|
-
this.#commit(type);
|
|
587
|
-
};
|
|
588
|
-
colorInput.addEventListener("input", (event) =>
|
|
589
|
-
syncColor(event, "input"),
|
|
590
|
-
);
|
|
591
|
-
colorInput.addEventListener("change", (event) =>
|
|
592
|
-
syncColor(event, "change"),
|
|
593
|
-
);
|
|
594
|
-
|
|
595
|
-
const row = figEditorCreateElement(
|
|
596
|
-
"fig-property",
|
|
597
|
-
{
|
|
598
|
-
className: "fig-palette-editor-color-row",
|
|
599
|
-
"data-index": index,
|
|
600
|
-
leading: "drag",
|
|
601
|
-
trailing: this.#fixed ? null : "remove",
|
|
602
|
-
"show-trailing": this.#fixed ? null : true,
|
|
603
|
-
},
|
|
604
|
-
colorInput,
|
|
605
|
-
);
|
|
606
|
-
row.addEventListener("remove", (event) => {
|
|
607
|
-
event.stopPropagation();
|
|
608
|
-
this.#removeRow(row);
|
|
609
|
-
});
|
|
610
|
-
return row;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
#handleReorder = (event) => {
|
|
614
|
-
const oldIndex = Number(event.detail?.oldIndex);
|
|
615
|
-
const newIndex = Number(event.detail?.newIndex);
|
|
616
|
-
if (
|
|
617
|
-
!Number.isInteger(oldIndex) ||
|
|
618
|
-
!Number.isInteger(newIndex) ||
|
|
619
|
-
oldIndex === newIndex ||
|
|
620
|
-
oldIndex < 0 ||
|
|
621
|
-
newIndex < 0 ||
|
|
622
|
-
oldIndex >= this.#colors.length ||
|
|
623
|
-
newIndex >= this.#colors.length
|
|
624
|
-
) {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
const [entry] = this.#colors.splice(oldIndex, 1);
|
|
628
|
-
this.#colors.splice(newIndex, 0, entry);
|
|
629
|
-
this.querySelectorAll(".fig-palette-editor-color-row").forEach(
|
|
630
|
-
(row, index) => {
|
|
631
|
-
row.dataset.index = String(index);
|
|
632
|
-
row
|
|
633
|
-
.querySelector(".fig-palette-editor-color")
|
|
634
|
-
?.setAttribute("aria-label", `Palette color ${index + 1}`);
|
|
635
|
-
row
|
|
636
|
-
.querySelector(".fig-palette-editor-remove")
|
|
637
|
-
?.setAttribute("aria-label", `Remove color ${index + 1}`);
|
|
638
|
-
},
|
|
639
|
-
);
|
|
640
|
-
this.#reflectValue();
|
|
641
|
-
this.#emit("input");
|
|
642
|
-
this.#emit("change");
|
|
643
|
-
};
|
|
644
|
-
|
|
645
|
-
#focusColorValue(index) {
|
|
646
|
-
requestAnimationFrame(() => {
|
|
647
|
-
if (!this.isConnected) return;
|
|
648
|
-
const input = this.querySelector(
|
|
649
|
-
`.fig-palette-editor-color-row[data-index="${index}"] .fig-palette-editor-color fig-input-text input`,
|
|
650
|
-
);
|
|
651
|
-
input?.focus();
|
|
652
|
-
input?.select();
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
#render() {
|
|
657
|
-
const reorder = figEditorCreateElement(
|
|
658
|
-
"fig-reorder",
|
|
659
|
-
{
|
|
660
|
-
axis: "vertical",
|
|
661
|
-
items: ".fig-palette-editor-color-row",
|
|
662
|
-
"aria-label": "Palette colors",
|
|
663
|
-
disabled: this.#disabled || null,
|
|
664
|
-
},
|
|
665
|
-
this.#colors.map((entry, index) => this.#createRow(entry, index)),
|
|
666
|
-
);
|
|
667
|
-
reorder.addEventListener("reorder", this.#handleReorder);
|
|
668
|
-
|
|
669
|
-
const children = [reorder];
|
|
670
|
-
if (!this.#fixed) {
|
|
671
|
-
const addButton = figEditorCreateElement(
|
|
672
|
-
"fig-button",
|
|
673
|
-
{
|
|
674
|
-
className: "fig-palette-editor-add",
|
|
675
|
-
variant: "secondary",
|
|
676
|
-
full: true,
|
|
677
|
-
disabled:
|
|
678
|
-
this.#disabled || this.#colors.length >= this.#max || null,
|
|
679
|
-
},
|
|
680
|
-
"Add color",
|
|
681
|
-
);
|
|
682
|
-
addButton.addEventListener("click", () => {
|
|
683
|
-
if (this.#disabled || this.#colors.length >= this.#max) return;
|
|
684
|
-
const addedIndex = this.#colors.length;
|
|
685
|
-
this.#colors.push(
|
|
686
|
-
figEditorAveragePaletteEntries(
|
|
687
|
-
this.#colors.at(-2),
|
|
688
|
-
this.#colors.at(-1),
|
|
689
|
-
),
|
|
690
|
-
);
|
|
691
|
-
this.#reflectValue();
|
|
692
|
-
this.#render();
|
|
693
|
-
this.#emit("input");
|
|
694
|
-
this.#emit("change");
|
|
695
|
-
this.#focusColorValue(addedIndex);
|
|
696
|
-
});
|
|
697
|
-
children.push(figEditorCreateElement("fig-field", {}, addButton));
|
|
698
|
-
}
|
|
699
|
-
this.replaceChildren(...children);
|
|
700
|
-
this.querySelectorAll(".fig-palette-editor-color-row").forEach(
|
|
701
|
-
(row, index) => {
|
|
702
|
-
const remove = row.querySelector(
|
|
703
|
-
':scope > [data-fig-property-action="remove"]',
|
|
704
|
-
);
|
|
705
|
-
if (!remove) return;
|
|
706
|
-
remove.classList.add("fig-palette-editor-remove");
|
|
707
|
-
remove.setAttribute("aria-label", `Remove color ${index + 1}`);
|
|
708
|
-
remove.toggleAttribute(
|
|
709
|
-
"disabled",
|
|
710
|
-
this.#disabled || this.#colors.length <= this.#min,
|
|
711
|
-
);
|
|
712
|
-
},
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
figEditorDefineElement("fig-palette-editor", FigPaletteEditor);
|
|
717
|
-
|
|
718
|
-
|
|
719
343
|
/* Select — dropdown-styled trigger + fig-popup listbox */
|
|
720
344
|
/** Parse options attr — same formats as fig-options. */
|
|
721
345
|
function figSelectParseOptionsAttribute(raw) {
|
|
@@ -3745,8 +3369,15 @@ class FigFillPicker extends HTMLElement {
|
|
|
3745
3369
|
const gradH = ctx.createLinearGradient(0, 0, width, 0);
|
|
3746
3370
|
if (isP3) {
|
|
3747
3371
|
gradH.addColorStop(0, "color(display-p3 1 1 1)");
|
|
3748
|
-
const
|
|
3749
|
-
|
|
3372
|
+
const srgb = figHslToRGB(hue, 100, 50);
|
|
3373
|
+
const p3 = figConvertRGB(srgb.r, srgb.g, srgb.b, {
|
|
3374
|
+
from: "srgb",
|
|
3375
|
+
to: "display-p3",
|
|
3376
|
+
});
|
|
3377
|
+
gradH.addColorStop(
|
|
3378
|
+
1,
|
|
3379
|
+
`color(display-p3 ${p3.r / 255} ${p3.g / 255} ${p3.b / 255})`,
|
|
3380
|
+
);
|
|
3750
3381
|
} else {
|
|
3751
3382
|
gradH.addColorStop(0, "#FFFFFF");
|
|
3752
3383
|
gradH.addColorStop(1, `hsl(${hue}, 100%, 50%)`);
|
|
@@ -5838,91 +5469,11 @@ class FigFillPicker extends HTMLElement {
|
|
|
5838
5469
|
|
|
5839
5470
|
// ============ COLOR CONVERSION UTILITIES ============
|
|
5840
5471
|
#hsvToRGB(hsv) {
|
|
5841
|
-
|
|
5842
|
-
const s = hsv.s / 100;
|
|
5843
|
-
const v = hsv.v / 100;
|
|
5844
|
-
|
|
5845
|
-
let r, g, b;
|
|
5846
|
-
const i = Math.floor(h * 6);
|
|
5847
|
-
const f = h * 6 - i;
|
|
5848
|
-
const p = v * (1 - s);
|
|
5849
|
-
const q = v * (1 - f * s);
|
|
5850
|
-
const t = v * (1 - (1 - f) * s);
|
|
5851
|
-
|
|
5852
|
-
switch (i % 6) {
|
|
5853
|
-
case 0:
|
|
5854
|
-
r = v;
|
|
5855
|
-
g = t;
|
|
5856
|
-
b = p;
|
|
5857
|
-
break;
|
|
5858
|
-
case 1:
|
|
5859
|
-
r = q;
|
|
5860
|
-
g = v;
|
|
5861
|
-
b = p;
|
|
5862
|
-
break;
|
|
5863
|
-
case 2:
|
|
5864
|
-
r = p;
|
|
5865
|
-
g = v;
|
|
5866
|
-
b = t;
|
|
5867
|
-
break;
|
|
5868
|
-
case 3:
|
|
5869
|
-
r = p;
|
|
5870
|
-
g = q;
|
|
5871
|
-
b = v;
|
|
5872
|
-
break;
|
|
5873
|
-
case 4:
|
|
5874
|
-
r = t;
|
|
5875
|
-
g = p;
|
|
5876
|
-
b = v;
|
|
5877
|
-
break;
|
|
5878
|
-
case 5:
|
|
5879
|
-
r = v;
|
|
5880
|
-
g = p;
|
|
5881
|
-
b = q;
|
|
5882
|
-
break;
|
|
5883
|
-
}
|
|
5884
|
-
|
|
5885
|
-
return {
|
|
5886
|
-
r: Math.round(r * 255),
|
|
5887
|
-
g: Math.round(g * 255),
|
|
5888
|
-
b: Math.round(b * 255),
|
|
5889
|
-
};
|
|
5472
|
+
return figHsvToRGB(hsv.h, hsv.s, hsv.v);
|
|
5890
5473
|
}
|
|
5891
5474
|
|
|
5892
5475
|
#rgbToHSV(rgb) {
|
|
5893
|
-
|
|
5894
|
-
const g = rgb.g / 255;
|
|
5895
|
-
const b = rgb.b / 255;
|
|
5896
|
-
|
|
5897
|
-
const max = Math.max(r, g, b);
|
|
5898
|
-
const min = Math.min(r, g, b);
|
|
5899
|
-
const d = max - min;
|
|
5900
|
-
|
|
5901
|
-
let h = 0;
|
|
5902
|
-
const s = max === 0 ? 0 : d / max;
|
|
5903
|
-
const v = max;
|
|
5904
|
-
|
|
5905
|
-
if (max !== min) {
|
|
5906
|
-
switch (max) {
|
|
5907
|
-
case r:
|
|
5908
|
-
h = (g - b) / d + (g < b ? 6 : 0);
|
|
5909
|
-
break;
|
|
5910
|
-
case g:
|
|
5911
|
-
h = (b - r) / d + 2;
|
|
5912
|
-
break;
|
|
5913
|
-
case b:
|
|
5914
|
-
h = (r - g) / d + 4;
|
|
5915
|
-
break;
|
|
5916
|
-
}
|
|
5917
|
-
h /= 6;
|
|
5918
|
-
}
|
|
5919
|
-
|
|
5920
|
-
return {
|
|
5921
|
-
h: h * 360,
|
|
5922
|
-
s: s * 100,
|
|
5923
|
-
v: v * 100,
|
|
5924
|
-
a: 1,
|
|
5925
|
-
};
|
|
5476
|
+
return { ...figRGBToHsv(rgb.r, rgb.g, rgb.b), a: 1 };
|
|
5926
5477
|
}
|
|
5927
5478
|
|
|
5928
5479
|
#hsvToHex(hsv) {
|
|
@@ -6007,145 +5558,39 @@ class FigFillPicker extends HTMLElement {
|
|
|
6007
5558
|
}
|
|
6008
5559
|
|
|
6009
5560
|
#hexToP3(hex, alpha = 1) {
|
|
6010
|
-
const
|
|
6011
|
-
const
|
|
6012
|
-
|
|
5561
|
+
const srgb = figEditorHexToRgb(hex);
|
|
5562
|
+
const p3 = figConvertRGB(srgb.r, srgb.g, srgb.b, {
|
|
5563
|
+
from: "srgb",
|
|
5564
|
+
to: "display-p3",
|
|
5565
|
+
});
|
|
5566
|
+
const r = +(p3.r / 255).toFixed(4);
|
|
5567
|
+
const g = +(p3.g / 255).toFixed(4);
|
|
5568
|
+
const b = +(p3.b / 255).toFixed(4);
|
|
6013
5569
|
return `color(display-p3 ${r} ${g} ${b} / ${alpha})`;
|
|
6014
5570
|
}
|
|
6015
5571
|
|
|
6016
5572
|
#rgbToHSL(rgb) {
|
|
6017
|
-
|
|
6018
|
-
const g = rgb.g / 255;
|
|
6019
|
-
const b = rgb.b / 255;
|
|
6020
|
-
|
|
6021
|
-
const max = Math.max(r, g, b);
|
|
6022
|
-
const min = Math.min(r, g, b);
|
|
6023
|
-
let h, s;
|
|
6024
|
-
const l = (max + min) / 2;
|
|
6025
|
-
|
|
6026
|
-
if (max === min) {
|
|
6027
|
-
h = s = 0;
|
|
6028
|
-
} else {
|
|
6029
|
-
const d = max - min;
|
|
6030
|
-
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
6031
|
-
|
|
6032
|
-
switch (max) {
|
|
6033
|
-
case r:
|
|
6034
|
-
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
6035
|
-
break;
|
|
6036
|
-
case g:
|
|
6037
|
-
h = ((b - r) / d + 2) / 6;
|
|
6038
|
-
break;
|
|
6039
|
-
case b:
|
|
6040
|
-
h = ((r - g) / d + 4) / 6;
|
|
6041
|
-
break;
|
|
6042
|
-
}
|
|
6043
|
-
}
|
|
6044
|
-
|
|
6045
|
-
return { h: h * 360, s: s * 100, l: l * 100 };
|
|
5573
|
+
return figEditorRgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
6046
5574
|
}
|
|
6047
5575
|
|
|
6048
5576
|
#hslToRGB(hsl) {
|
|
6049
|
-
|
|
6050
|
-
const s = hsl.s / 100;
|
|
6051
|
-
const l = hsl.l / 100;
|
|
6052
|
-
|
|
6053
|
-
let r, g, b;
|
|
6054
|
-
|
|
6055
|
-
if (s === 0) {
|
|
6056
|
-
r = g = b = l;
|
|
6057
|
-
} else {
|
|
6058
|
-
const hue2rgb = (p, q, t) => {
|
|
6059
|
-
if (t < 0) t += 1;
|
|
6060
|
-
if (t > 1) t -= 1;
|
|
6061
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
6062
|
-
if (t < 1 / 2) return q;
|
|
6063
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
6064
|
-
return p;
|
|
6065
|
-
};
|
|
6066
|
-
|
|
6067
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
6068
|
-
const p = 2 * l - q;
|
|
6069
|
-
|
|
6070
|
-
r = hue2rgb(p, q, h + 1 / 3);
|
|
6071
|
-
g = hue2rgb(p, q, h);
|
|
6072
|
-
b = hue2rgb(p, q, h - 1 / 3);
|
|
6073
|
-
}
|
|
6074
|
-
|
|
6075
|
-
return {
|
|
6076
|
-
r: Math.round(r * 255),
|
|
6077
|
-
g: Math.round(g * 255),
|
|
6078
|
-
b: Math.round(b * 255),
|
|
6079
|
-
};
|
|
5577
|
+
return figHslToRGB(hsl.h, hsl.s, hsl.l);
|
|
6080
5578
|
}
|
|
6081
5579
|
|
|
6082
|
-
// OKLAB/OKLCH conversions (simplified)
|
|
6083
5580
|
#rgbToOKLAB(rgb) {
|
|
6084
|
-
|
|
6085
|
-
const toLinear = (c) => {
|
|
6086
|
-
c = c / 255;
|
|
6087
|
-
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
6088
|
-
};
|
|
6089
|
-
|
|
6090
|
-
const r = toLinear(rgb.r);
|
|
6091
|
-
const g = toLinear(rgb.g);
|
|
6092
|
-
const b = toLinear(rgb.b);
|
|
6093
|
-
|
|
6094
|
-
// Convert to LMS
|
|
6095
|
-
const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
|
6096
|
-
const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
|
|
6097
|
-
const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
|
|
6098
|
-
|
|
6099
|
-
// Convert to Oklab
|
|
6100
|
-
const l_ = Math.cbrt(l);
|
|
6101
|
-
const m_ = Math.cbrt(m);
|
|
6102
|
-
const s_ = Math.cbrt(s);
|
|
6103
|
-
|
|
6104
|
-
return {
|
|
6105
|
-
l: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
|
|
6106
|
-
a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
|
|
6107
|
-
b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_,
|
|
6108
|
-
};
|
|
5581
|
+
return figEditorRgbToOklab(rgb.r, rgb.g, rgb.b);
|
|
6109
5582
|
}
|
|
6110
5583
|
|
|
6111
5584
|
#rgbToOKLCH(rgb) {
|
|
6112
|
-
|
|
6113
|
-
return {
|
|
6114
|
-
l: lab.l,
|
|
6115
|
-
c: Math.sqrt(lab.a * lab.a + lab.b * lab.b),
|
|
6116
|
-
h: ((Math.atan2(lab.b, lab.a) * 180) / Math.PI + 360) % 360,
|
|
6117
|
-
};
|
|
5585
|
+
return figRGBToOklch(rgb.r, rgb.g, rgb.b);
|
|
6118
5586
|
}
|
|
6119
5587
|
|
|
6120
5588
|
#oklabToRGB(lab) {
|
|
6121
|
-
|
|
6122
|
-
const m_ = lab.l - 0.1055613458 * lab.a - 0.0638541728 * lab.b;
|
|
6123
|
-
const s_ = lab.l - 0.0894841775 * lab.a - 1.291485548 * lab.b;
|
|
6124
|
-
|
|
6125
|
-
const l = l_ * l_ * l_;
|
|
6126
|
-
const m = m_ * m_ * m_;
|
|
6127
|
-
const s = s_ * s_ * s_;
|
|
6128
|
-
|
|
6129
|
-
const toSRGB = (c) => {
|
|
6130
|
-
const v =
|
|
6131
|
-
c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
6132
|
-
return Math.round(Math.max(0, Math.min(1, v)) * 255);
|
|
6133
|
-
};
|
|
6134
|
-
|
|
6135
|
-
return {
|
|
6136
|
-
r: toSRGB(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
6137
|
-
g: toSRGB(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
6138
|
-
b: toSRGB(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),
|
|
6139
|
-
};
|
|
5589
|
+
return figOklabToRGB(lab.l, lab.a, lab.b);
|
|
6140
5590
|
}
|
|
6141
5591
|
|
|
6142
5592
|
#oklchToRGB(lch) {
|
|
6143
|
-
|
|
6144
|
-
return this.#oklabToRGB({
|
|
6145
|
-
l: lch.l,
|
|
6146
|
-
a: lch.c * Math.cos(hRad),
|
|
6147
|
-
b: lch.c * Math.sin(hRad),
|
|
6148
|
-
});
|
|
5593
|
+
return figOklchToRGB(lch.l, lch.c, lch.h);
|
|
6149
5594
|
}
|
|
6150
5595
|
|
|
6151
5596
|
// ============ EVENT EMITTERS ============
|