@rogieking/figui3 8.9.13 → 8.9.15
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-lab/SKILL.md +2 -2
- package/.cursor/skills/propkit/SKILL.md +1 -1
- package/README.md +5 -5
- package/components.css +2 -0
- package/dist/components.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +2 -2
- package/dist/fig.css +1 -1
- package/dist/fig.js +1 -1
- package/fig-lab.css +27 -50
- package/fig-lab.js +115 -39
- package/fig.js +7 -1
- package/package.json +1 -1
package/fig-lab.css
CHANGED
|
@@ -383,7 +383,7 @@ fig-content:has(> propskit-group[name]:last-child) {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
/* Flex-positioned numbers — margin works. Absolute numbers use right: calc(...) below. */
|
|
386
|
-
|
|
386
|
+
propskit-number[size="large"] {
|
|
387
387
|
fig-field fig-input-number {
|
|
388
388
|
margin-right: var(--spacer-1);
|
|
389
389
|
}
|
|
@@ -504,7 +504,7 @@ propskit-color {
|
|
|
504
504
|
align-items: center;
|
|
505
505
|
gap: var(--spacer-2);
|
|
506
506
|
height: 100%;
|
|
507
|
-
padding
|
|
507
|
+
padding: 0 0 0 var(--spacer-2);
|
|
508
508
|
border-radius: var(--radius-medium);
|
|
509
509
|
background-color: var(--figma-color-bg-secondary);
|
|
510
510
|
|
|
@@ -525,9 +525,8 @@ propskit-color {
|
|
|
525
525
|
z-index: 1;
|
|
526
526
|
display: flex;
|
|
527
527
|
align-items: center;
|
|
528
|
-
flex: 0
|
|
529
|
-
width:
|
|
530
|
-
max-width: 50%;
|
|
528
|
+
flex: 0 1 auto;
|
|
529
|
+
max-width: 40%;
|
|
531
530
|
min-width: 0;
|
|
532
531
|
margin: 0;
|
|
533
532
|
padding: 0;
|
|
@@ -542,55 +541,26 @@ propskit-color {
|
|
|
542
541
|
}
|
|
543
542
|
}
|
|
544
543
|
|
|
545
|
-
fig-
|
|
546
|
-
|
|
544
|
+
fig-fill-picker {
|
|
545
|
+
display: contents;
|
|
546
|
+
}
|
|
547
547
|
|
|
548
|
+
fig-swatch {
|
|
548
549
|
position: relative;
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
width: fit-content !important;
|
|
552
|
-
max-width: none;
|
|
550
|
+
flex: 0 0 33%;
|
|
551
|
+
width: 33%;
|
|
553
552
|
min-width: 0;
|
|
554
553
|
margin-left: auto;
|
|
554
|
+
--width: 100%;
|
|
555
|
+
--height: var(--propskit-color-height);
|
|
556
|
+
--size: var(--propskit-color-height);
|
|
557
|
+
--border-radius: var(--radius-medium);
|
|
555
558
|
height: var(--propskit-color-height);
|
|
556
559
|
border-radius: var(--radius-medium);
|
|
557
|
-
background-color: transparent;
|
|
558
|
-
box-shadow: none;
|
|
559
560
|
|
|
560
|
-
|
|
561
|
-
&:hover:not(:focus):not(:focus-within):not(.has-popup-open) {
|
|
561
|
+
&:hover {
|
|
562
562
|
outline: none !important;
|
|
563
563
|
}
|
|
564
|
-
|
|
565
|
-
> .input-combo {
|
|
566
|
-
flex: 0 0 auto;
|
|
567
|
-
width: auto;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
fig-input-text {
|
|
571
|
-
flex: 0 0 3.25rem;
|
|
572
|
-
width: 3.25rem;
|
|
573
|
-
|
|
574
|
-
input {
|
|
575
|
-
field-sizing: fixed;
|
|
576
|
-
box-sizing: border-box;
|
|
577
|
-
width: 100%;
|
|
578
|
-
min-width: 0;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
fig-input-number {
|
|
583
|
-
flex: 0 0 3rem;
|
|
584
|
-
width: 3rem;
|
|
585
|
-
min-width: 3rem;
|
|
586
|
-
|
|
587
|
-
input {
|
|
588
|
-
field-sizing: fixed;
|
|
589
|
-
box-sizing: border-box;
|
|
590
|
-
width: 100%;
|
|
591
|
-
min-width: 0;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
564
|
}
|
|
595
565
|
}
|
|
596
566
|
}
|
|
@@ -612,8 +582,15 @@ propskit-gradient {
|
|
|
612
582
|
&[size="large"] {
|
|
613
583
|
--propskit-gradient-height: 2rem;
|
|
614
584
|
|
|
615
|
-
fig-
|
|
616
|
-
|
|
585
|
+
fig-input-gradient {
|
|
586
|
+
--fig-input-gradient-height: var(--propskit-gradient-height);
|
|
587
|
+
height: var(--propskit-gradient-height);
|
|
588
|
+
|
|
589
|
+
fig-swatch {
|
|
590
|
+
--height: var(--propskit-gradient-height);
|
|
591
|
+
--size: var(--propskit-gradient-height);
|
|
592
|
+
height: var(--propskit-gradient-height);
|
|
593
|
+
}
|
|
617
594
|
}
|
|
618
595
|
}
|
|
619
596
|
|
|
@@ -631,7 +608,7 @@ propskit-gradient {
|
|
|
631
608
|
align-items: center;
|
|
632
609
|
gap: var(--spacer-2);
|
|
633
610
|
height: 100%;
|
|
634
|
-
padding: 0
|
|
611
|
+
padding: 0 0 0 var(--spacer-2);
|
|
635
612
|
border-radius: var(--radius-medium);
|
|
636
613
|
background-color: var(--figma-color-bg-secondary);
|
|
637
614
|
|
|
@@ -670,8 +647,8 @@ propskit-gradient {
|
|
|
670
647
|
|
|
671
648
|
fig-input-gradient {
|
|
672
649
|
position: relative;
|
|
673
|
-
flex: 0 0
|
|
674
|
-
width:
|
|
650
|
+
flex: 0 0 33%;
|
|
651
|
+
width: 33%;
|
|
675
652
|
min-width: 0;
|
|
676
653
|
margin-left: auto;
|
|
677
654
|
border-radius: var(--radius-medium);
|
package/fig-lab.js
CHANGED
|
@@ -699,11 +699,37 @@ class PropskitSwitch extends HTMLElement {
|
|
|
699
699
|
}
|
|
700
700
|
figLabDefineElement("propskit-switch", PropskitSwitch);
|
|
701
701
|
|
|
702
|
+
function figLabParseSolidColor(raw) {
|
|
703
|
+
const value = String(raw ?? "").trim();
|
|
704
|
+
if (/^#[0-9a-fA-F]{8}$/.test(value)) {
|
|
705
|
+
return {
|
|
706
|
+
color: value.slice(0, 7),
|
|
707
|
+
alpha: parseInt(value.slice(7, 9), 16) / 255,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
if (/^#[0-9a-fA-F]{6}$/.test(value)) {
|
|
711
|
+
return { color: value, alpha: 1 };
|
|
712
|
+
}
|
|
713
|
+
if (/^#[0-9a-fA-F]{3}$/.test(value)) {
|
|
714
|
+
return {
|
|
715
|
+
color: `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`,
|
|
716
|
+
alpha: 1,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
return { color: value || "#D9D9D9", alpha: 1 };
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function figLabSolidFillValue(raw) {
|
|
723
|
+
const { color, alpha } = figLabParseSolidColor(raw);
|
|
724
|
+
return JSON.stringify({ type: "solid", color, alpha });
|
|
725
|
+
}
|
|
726
|
+
|
|
702
727
|
/* Field + Color wrapper */
|
|
703
728
|
class PropskitColor extends HTMLElement {
|
|
704
729
|
#field = null;
|
|
705
730
|
#label = null;
|
|
706
731
|
#input = null;
|
|
732
|
+
#swatch = null;
|
|
707
733
|
#hasCustomLabel = false;
|
|
708
734
|
#observer = null;
|
|
709
735
|
#managedInputAttrs = new Set();
|
|
@@ -779,15 +805,18 @@ class PropskitColor extends HTMLElement {
|
|
|
779
805
|
);
|
|
780
806
|
const field = document.createElement("fig-field");
|
|
781
807
|
const label = customLabel || document.createElement("label");
|
|
782
|
-
const
|
|
783
|
-
|
|
808
|
+
const picker = document.createElement("fig-fill-picker");
|
|
809
|
+
const swatch = document.createElement("fig-swatch");
|
|
810
|
+
picker.append(swatch);
|
|
811
|
+
swatch.setAttribute("tabindex", "0");
|
|
784
812
|
for (const node of initialChildren) {
|
|
785
|
-
if (node !== customLabel)
|
|
813
|
+
if (node !== customLabel) picker.appendChild(node);
|
|
786
814
|
}
|
|
787
|
-
field.append(label,
|
|
815
|
+
field.append(label, picker);
|
|
788
816
|
this.#field = field;
|
|
789
817
|
this.#label = label;
|
|
790
|
-
this.#input =
|
|
818
|
+
this.#input = picker;
|
|
819
|
+
this.#swatch = swatch;
|
|
791
820
|
this.#hasCustomLabel = Boolean(customLabel);
|
|
792
821
|
this.replaceChildren(field);
|
|
793
822
|
}
|
|
@@ -821,6 +850,15 @@ class PropskitColor extends HTMLElement {
|
|
|
821
850
|
);
|
|
822
851
|
}
|
|
823
852
|
|
|
853
|
+
#parsedHostColor() {
|
|
854
|
+
return figLabParseSolidColor(this.getAttribute("value"));
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
#pickerValueFromHost() {
|
|
858
|
+
const { color, alpha } = this.#parsedHostColor();
|
|
859
|
+
return JSON.stringify({ type: "solid", color, alpha });
|
|
860
|
+
}
|
|
861
|
+
|
|
824
862
|
#getForwardedInputAttrNames() {
|
|
825
863
|
const reserved = new Set([
|
|
826
864
|
"label",
|
|
@@ -834,6 +872,8 @@ class PropskitColor extends HTMLElement {
|
|
|
834
872
|
"aria-label",
|
|
835
873
|
"text",
|
|
836
874
|
"default",
|
|
875
|
+
"value",
|
|
876
|
+
"mode",
|
|
837
877
|
]);
|
|
838
878
|
return this.getAttributeNames().filter(
|
|
839
879
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -849,19 +889,19 @@ class PropskitColor extends HTMLElement {
|
|
|
849
889
|
if (!nextManaged.has(attrName)) this.#input.removeAttribute(attrName);
|
|
850
890
|
}
|
|
851
891
|
for (const attrName of inputAttrs) {
|
|
852
|
-
|
|
853
|
-
// Soft sync only. Never force-clear value here — that fights live edits
|
|
854
|
-
// (fig-input-color often keeps a stale value attribute while editing).
|
|
855
|
-
if (
|
|
856
|
-
attrName === "value" &&
|
|
857
|
-
this.#input.getAttribute("value") === next
|
|
858
|
-
) {
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
861
|
-
this.#input.setAttribute(attrName, next);
|
|
892
|
+
this.#input.setAttribute(attrName, this.getAttribute(attrName) ?? "");
|
|
862
893
|
}
|
|
863
894
|
|
|
864
|
-
this.#input.setAttribute("
|
|
895
|
+
this.#input.setAttribute("mode", "solid");
|
|
896
|
+
const nextJson = this.#pickerValueFromHost();
|
|
897
|
+
if (this.#input.getAttribute("value") !== nextJson) {
|
|
898
|
+
this.#input.setAttribute("value", nextJson);
|
|
899
|
+
}
|
|
900
|
+
const { color, alpha } = this.#parsedHostColor();
|
|
901
|
+
if (this.#swatch) {
|
|
902
|
+
this.#swatch.setAttribute("background", color);
|
|
903
|
+
this.#swatch.setAttribute("alpha", String(alpha));
|
|
904
|
+
}
|
|
865
905
|
this.#managedInputAttrs = nextManaged;
|
|
866
906
|
}
|
|
867
907
|
|
|
@@ -888,31 +928,62 @@ class PropskitColor extends HTMLElement {
|
|
|
888
928
|
event instanceof CustomEvent && event.detail !== undefined
|
|
889
929
|
? event.detail
|
|
890
930
|
: undefined;
|
|
891
|
-
if (typeof detail === "string" && detail)
|
|
931
|
+
if (typeof detail === "string" && detail) {
|
|
932
|
+
return figLabParseSolidColor(detail).color;
|
|
933
|
+
}
|
|
892
934
|
if (detail && typeof detail === "object") {
|
|
893
|
-
if (typeof detail.value === "string" && detail.value) return detail.value;
|
|
894
|
-
if (typeof detail.hex === "string" && detail.hex) return detail.hex;
|
|
895
935
|
if (typeof detail.color === "string" && detail.color) return detail.color;
|
|
936
|
+
if (typeof detail.value === "string" && detail.value) {
|
|
937
|
+
return figLabParseSolidColor(detail.value).color;
|
|
938
|
+
}
|
|
939
|
+
if (typeof detail.hex === "string" && detail.hex) return detail.hex;
|
|
940
|
+
}
|
|
941
|
+
const live = this.#input?.value;
|
|
942
|
+
if (live && typeof live === "object" && typeof live.color === "string") {
|
|
943
|
+
return live.color;
|
|
896
944
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
return this.#input.value;
|
|
945
|
+
if (typeof live === "string" && live) {
|
|
946
|
+
return figLabParseSolidColor(live).color;
|
|
900
947
|
}
|
|
901
|
-
return this.#
|
|
948
|
+
return this.#parsedHostColor().color;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
#colorDetailFromEvent(event, color) {
|
|
952
|
+
const detail =
|
|
953
|
+
event instanceof CustomEvent && event.detail && typeof event.detail === "object"
|
|
954
|
+
? event.detail
|
|
955
|
+
: undefined;
|
|
956
|
+
const alpha = Number.isFinite(detail?.alpha)
|
|
957
|
+
? Math.max(0, Math.min(1, Number(detail.alpha)))
|
|
958
|
+
: this.#parsedHostColor().alpha;
|
|
959
|
+
return {
|
|
960
|
+
color,
|
|
961
|
+
alpha,
|
|
962
|
+
opacity: Math.round(alpha * 100),
|
|
963
|
+
};
|
|
902
964
|
}
|
|
903
965
|
|
|
904
966
|
#forwardInputEvent(type, event) {
|
|
905
967
|
event.stopImmediatePropagation();
|
|
906
968
|
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
907
969
|
const value = this.#valueFromColorEvent(event);
|
|
908
|
-
this
|
|
909
|
-
|
|
910
|
-
|
|
970
|
+
const detail = this.#colorDetailFromEvent(event, value);
|
|
971
|
+
const alphaHex =
|
|
972
|
+
detail.alpha < 1 - 1 / 255
|
|
973
|
+
? Math.round(detail.alpha * 255)
|
|
974
|
+
.toString(16)
|
|
975
|
+
.padStart(2, "0")
|
|
976
|
+
: "";
|
|
977
|
+
const hostValue = `${detail.color.slice(0, 7)}${alphaHex}`;
|
|
978
|
+
this.setAttribute("value", hostValue);
|
|
979
|
+
const nextJson = this.#pickerValueFromHost();
|
|
980
|
+
if (this.#input && this.#input.getAttribute("value") !== nextJson) {
|
|
981
|
+
this.#input.setAttribute("value", nextJson);
|
|
982
|
+
}
|
|
983
|
+
if (this.#swatch) {
|
|
984
|
+
this.#swatch.setAttribute("background", detail.color);
|
|
985
|
+
this.#swatch.setAttribute("alpha", String(detail.alpha));
|
|
911
986
|
}
|
|
912
|
-
const detail =
|
|
913
|
-
event instanceof CustomEvent && event.detail !== undefined
|
|
914
|
-
? event.detail
|
|
915
|
-
: value;
|
|
916
987
|
this.dispatchEvent(
|
|
917
988
|
new CustomEvent(type, {
|
|
918
989
|
detail,
|
|
@@ -927,7 +998,7 @@ class PropskitColor extends HTMLElement {
|
|
|
927
998
|
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
928
999
|
if (
|
|
929
1000
|
event.target instanceof Element &&
|
|
930
|
-
event.target.closest("fig-
|
|
1001
|
+
event.target.closest("fig-fill-picker, fig-swatch, fig-menu")
|
|
931
1002
|
) {
|
|
932
1003
|
return;
|
|
933
1004
|
}
|
|
@@ -946,18 +1017,22 @@ class PropskitColor extends HTMLElement {
|
|
|
946
1017
|
}
|
|
947
1018
|
const next = String(nextValue);
|
|
948
1019
|
this.setAttribute("value", next);
|
|
949
|
-
|
|
950
|
-
this.#input.setAttribute("value", next);
|
|
951
|
-
}
|
|
1020
|
+
this.#forceInputValue(next);
|
|
952
1021
|
}
|
|
953
1022
|
|
|
954
|
-
/** Force
|
|
1023
|
+
/** Force fill-picker refresh even when the serialized value is unchanged. */
|
|
955
1024
|
#forceInputValue(next) {
|
|
956
1025
|
if (!this.#input) return;
|
|
957
|
-
|
|
1026
|
+
const json = figLabSolidFillValue(next);
|
|
1027
|
+
if (this.#input.getAttribute("value") === json) {
|
|
958
1028
|
this.#input.removeAttribute("value");
|
|
959
1029
|
}
|
|
960
|
-
this.#input.setAttribute("value",
|
|
1030
|
+
this.#input.setAttribute("value", json);
|
|
1031
|
+
if (this.#swatch) {
|
|
1032
|
+
const parsed = figLabParseSolidColor(next);
|
|
1033
|
+
this.#swatch.setAttribute("background", parsed.color);
|
|
1034
|
+
this.#swatch.setAttribute("alpha", String(parsed.alpha));
|
|
1035
|
+
}
|
|
961
1036
|
}
|
|
962
1037
|
|
|
963
1038
|
#defaultValue() {
|
|
@@ -995,7 +1070,8 @@ class PropskitColor extends HTMLElement {
|
|
|
995
1070
|
}
|
|
996
1071
|
|
|
997
1072
|
focus(options) {
|
|
998
|
-
this
|
|
1073
|
+
const swatch = this.querySelector("fig-swatch");
|
|
1074
|
+
if (swatch instanceof HTMLElement) swatch.focus(options);
|
|
999
1075
|
}
|
|
1000
1076
|
}
|
|
1001
1077
|
figLabDefineElement("propskit-color", PropskitColor);
|
|
@@ -1158,7 +1234,7 @@ class PropskitGradient extends HTMLElement {
|
|
|
1158
1234
|
this.#input.setAttribute(attrName, next);
|
|
1159
1235
|
}
|
|
1160
1236
|
|
|
1161
|
-
if (!this.hasAttribute("edit")) this.#input.setAttribute("edit", "
|
|
1237
|
+
if (!this.hasAttribute("edit")) this.#input.setAttribute("edit", "picker");
|
|
1162
1238
|
this.#managedInputAttrs = nextManaged;
|
|
1163
1239
|
}
|
|
1164
1240
|
|
package/fig.js
CHANGED
|
@@ -7742,10 +7742,12 @@ class FigField extends HTMLElement {
|
|
|
7742
7742
|
inputTag === "fig-menu" ||
|
|
7743
7743
|
inputTag === "fig-dropdown" ||
|
|
7744
7744
|
inputTag === "fig-tooltip" ||
|
|
7745
|
+
inputTag === "fig-fill-picker" ||
|
|
7745
7746
|
(this.input instanceof HTMLDialogElement);
|
|
7746
7747
|
this.#toggleable = !!(
|
|
7747
7748
|
this.input &&
|
|
7748
7749
|
"open" in this.input &&
|
|
7750
|
+
typeof this.input.open !== "function" &&
|
|
7749
7751
|
!popupOpenHost
|
|
7750
7752
|
);
|
|
7751
7753
|
|
|
@@ -11877,7 +11879,11 @@ class FigSwatch extends HTMLElement {
|
|
|
11877
11879
|
}
|
|
11878
11880
|
|
|
11879
11881
|
focus() {
|
|
11880
|
-
this.input?.
|
|
11882
|
+
if (this.input?.isConnected) {
|
|
11883
|
+
this.input.focus();
|
|
11884
|
+
return;
|
|
11885
|
+
}
|
|
11886
|
+
HTMLElement.prototype.focus.call(this);
|
|
11881
11887
|
}
|
|
11882
11888
|
|
|
11883
11889
|
attributeChangedCallback(name, oldValue, newValue) {
|
package/package.json
CHANGED