@rogieking/figui3 8.9.49 → 8.10.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/.cursor/skills/fig-editor/SKILL.md +3 -3
- package/.cursor/skills/fig-editor/components.md +2 -2
- package/.cursor/skills/fig-editor/reference.md +3 -0
- package/.cursor/skills/fig-lab/SKILL.md +14 -5
- package/.cursor/skills/fig-lab/components.md +90 -12
- package/.cursor/skills/fig-lab/reference.md +107 -11
- package/.cursor/skills/figui3/components.md +3 -0
- package/.cursor/skills/figui3/react.md +4 -0
- package/README.md +171 -56
- package/components.css +34 -12
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +18 -5
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +1 -3
- package/dist/fig.css +1 -1
- package/dist/fig.js +7 -7
- package/fig-editor.css +10 -0
- package/fig-editor.js +100 -10
- package/fig-lab.css +1497 -450
- package/fig-lab.js +2171 -1146
- package/fig.js +73 -30
- package/package.json +1 -1
package/fig-editor.css
CHANGED
|
@@ -849,6 +849,16 @@ fig-select-option {
|
|
|
849
849
|
}
|
|
850
850
|
}
|
|
851
851
|
|
|
852
|
+
&[subtle]:not([subtle="false"]),
|
|
853
|
+
fig-select[subtle]:not([subtle="false"]) &:not([subtle="false"]) {
|
|
854
|
+
&:hover,
|
|
855
|
+
&:focus-visible {
|
|
856
|
+
&::before {
|
|
857
|
+
background-color: var(--figma-color-bg-secondary);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
852
862
|
&[selected]:not([selected="false"])::after,
|
|
853
863
|
&[aria-selected="true"]::after {
|
|
854
864
|
visibility: visible;
|
package/fig-editor.js
CHANGED
|
@@ -499,6 +499,9 @@ class FigSelect extends HTMLElement {
|
|
|
499
499
|
#button = null;
|
|
500
500
|
#popup = null;
|
|
501
501
|
#prependEl = null;
|
|
502
|
+
#prependSlot = null;
|
|
503
|
+
#prependFallback = null;
|
|
504
|
+
#customTriggerSlot = null;
|
|
502
505
|
#labelEl = null;
|
|
503
506
|
#panelSlot = null;
|
|
504
507
|
#observer = null;
|
|
@@ -534,6 +537,7 @@ class FigSelect extends HTMLElement {
|
|
|
534
537
|
"closedby",
|
|
535
538
|
"open",
|
|
536
539
|
"variant",
|
|
540
|
+
"aria-label",
|
|
537
541
|
];
|
|
538
542
|
}
|
|
539
543
|
|
|
@@ -581,7 +585,7 @@ class FigSelect extends HTMLElement {
|
|
|
581
585
|
this.#syncValue();
|
|
582
586
|
return;
|
|
583
587
|
}
|
|
584
|
-
if (name === "value" || name === "label") {
|
|
588
|
+
if (name === "value" || name === "label" || name === "aria-label") {
|
|
585
589
|
this.#syncValue();
|
|
586
590
|
return;
|
|
587
591
|
}
|
|
@@ -728,7 +732,7 @@ class FigSelect extends HTMLElement {
|
|
|
728
732
|
text-overflow: ellipsis;
|
|
729
733
|
cursor: default;
|
|
730
734
|
}
|
|
731
|
-
.fig-select-trigger:has(.fig-select-prepend
|
|
735
|
+
.fig-select-trigger:has(.fig-select-prepend.has-content) {
|
|
732
736
|
padding-left: 0;
|
|
733
737
|
}
|
|
734
738
|
.fig-select-trigger:hover,
|
|
@@ -763,7 +767,20 @@ class FigSelect extends HTMLElement {
|
|
|
763
767
|
margin-right: var(--spacer-1, 0.25rem);
|
|
764
768
|
pointer-events: none;
|
|
765
769
|
}
|
|
766
|
-
.fig-select-prepend:
|
|
770
|
+
.fig-select-prepend:not(.has-content) {
|
|
771
|
+
display: none;
|
|
772
|
+
}
|
|
773
|
+
.fig-select-prepend > slot,
|
|
774
|
+
.fig-select-prepend-fallback,
|
|
775
|
+
slot[name="trigger"] {
|
|
776
|
+
display: contents;
|
|
777
|
+
}
|
|
778
|
+
slot[name="prepend-trigger"]::slotted([data-fig-select-generated]) {
|
|
779
|
+
display: contents;
|
|
780
|
+
}
|
|
781
|
+
.fig-select-label[hidden],
|
|
782
|
+
.fig-select-prepend[hidden],
|
|
783
|
+
slot[name="trigger"][hidden] {
|
|
767
784
|
display: none;
|
|
768
785
|
}
|
|
769
786
|
/* Listbox chrome from document fig-select::part(listbox).
|
|
@@ -795,11 +812,20 @@ class FigSelect extends HTMLElement {
|
|
|
795
812
|
prependEl.className = "fig-select-prepend";
|
|
796
813
|
prependEl.setAttribute("part", "prepend");
|
|
797
814
|
prependEl.setAttribute("aria-hidden", "true");
|
|
815
|
+
const prependSlot = document.createElement("slot");
|
|
816
|
+
prependSlot.setAttribute("name", "prepend-trigger");
|
|
817
|
+
prependSlot.hidden = true;
|
|
818
|
+
const prependFallback = document.createElement("span");
|
|
819
|
+
prependFallback.className = "fig-select-prepend-fallback";
|
|
820
|
+
prependEl.append(prependSlot, prependFallback);
|
|
798
821
|
|
|
799
822
|
const labelEl = document.createElement("span");
|
|
800
823
|
labelEl.className = "fig-select-label";
|
|
801
824
|
labelEl.setAttribute("part", "label");
|
|
802
|
-
|
|
825
|
+
const customTriggerSlot = document.createElement("slot");
|
|
826
|
+
customTriggerSlot.setAttribute("name", "trigger");
|
|
827
|
+
customTriggerSlot.hidden = true;
|
|
828
|
+
button.append(prependEl, labelEl, customTriggerSlot);
|
|
803
829
|
|
|
804
830
|
const popup = document.createElement("dialog", { is: "fig-popup" });
|
|
805
831
|
popup.setAttribute("is", "fig-popup");
|
|
@@ -823,6 +849,9 @@ class FigSelect extends HTMLElement {
|
|
|
823
849
|
|
|
824
850
|
this.#button = button;
|
|
825
851
|
this.#prependEl = prependEl;
|
|
852
|
+
this.#prependSlot = prependSlot;
|
|
853
|
+
this.#prependFallback = prependFallback;
|
|
854
|
+
this.#customTriggerSlot = customTriggerSlot;
|
|
826
855
|
this.#labelEl = labelEl;
|
|
827
856
|
this.#popup = popup;
|
|
828
857
|
this.#panelSlot = panelSlot;
|
|
@@ -871,8 +900,20 @@ class FigSelect extends HTMLElement {
|
|
|
871
900
|
return { top: 8, right: 8, bottom: 8, left: 8 };
|
|
872
901
|
}
|
|
873
902
|
|
|
903
|
+
#getTriggerAlignmentRect() {
|
|
904
|
+
const labelRect = this.#labelEl?.getBoundingClientRect();
|
|
905
|
+
if (labelRect?.width && labelRect?.height) return labelRect;
|
|
906
|
+
const customTriggerRect = this.querySelector(
|
|
907
|
+
':scope > [slot="trigger"]',
|
|
908
|
+
)?.getBoundingClientRect();
|
|
909
|
+
if (customTriggerRect?.width && customTriggerRect?.height) {
|
|
910
|
+
return customTriggerRect;
|
|
911
|
+
}
|
|
912
|
+
return this.#prependEl?.getBoundingClientRect() ?? null;
|
|
913
|
+
}
|
|
914
|
+
|
|
874
915
|
#readLabelRectSnapshot() {
|
|
875
|
-
const rect = this.#
|
|
916
|
+
const rect = this.#getTriggerAlignmentRect();
|
|
876
917
|
if (!rect) return null;
|
|
877
918
|
return {
|
|
878
919
|
x: rect.x,
|
|
@@ -959,7 +1000,7 @@ class FigSelect extends HTMLElement {
|
|
|
959
1000
|
this.#originalPositionPopup?.();
|
|
960
1001
|
|
|
961
1002
|
const popupRect = popup.getBoundingClientRect();
|
|
962
|
-
const labelRect =
|
|
1003
|
+
const labelRect = this.#getTriggerAlignmentRect();
|
|
963
1004
|
const optionTextRect = this.#getOptionTextRect(selected);
|
|
964
1005
|
if (
|
|
965
1006
|
!popupRect.width ||
|
|
@@ -1146,11 +1187,57 @@ class FigSelect extends HTMLElement {
|
|
|
1146
1187
|
}
|
|
1147
1188
|
|
|
1148
1189
|
#syncPrepend(option) {
|
|
1149
|
-
if (
|
|
1190
|
+
if (
|
|
1191
|
+
!this.#prependEl ||
|
|
1192
|
+
!this.#prependSlot ||
|
|
1193
|
+
!this.#prependFallback ||
|
|
1194
|
+
!this.#customTriggerSlot
|
|
1195
|
+
) {
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const customTrigger = this.querySelector(':scope > [slot="trigger"]');
|
|
1199
|
+
const generatedTrigger = this.querySelector(
|
|
1200
|
+
':scope > [slot="prepend-trigger"][data-fig-select-generated]',
|
|
1201
|
+
);
|
|
1202
|
+
let triggerPrepend = this.querySelector(
|
|
1203
|
+
':scope > [slot="prepend-trigger"]:not([data-fig-select-generated])',
|
|
1204
|
+
);
|
|
1150
1205
|
const source = option?.querySelector?.(':scope > [slot="prepend"]');
|
|
1151
|
-
this.#
|
|
1152
|
-
|
|
1206
|
+
this.#customTriggerSlot.hidden = !customTrigger;
|
|
1207
|
+
this.#labelEl.hidden = Boolean(customTrigger);
|
|
1208
|
+
this.#prependEl.hidden = Boolean(customTrigger);
|
|
1209
|
+
if (customTrigger) {
|
|
1210
|
+
generatedTrigger?.remove();
|
|
1211
|
+
this.#prependEl.classList.remove("has-content");
|
|
1212
|
+
this.#prependSlot.hidden = true;
|
|
1213
|
+
this.#prependFallback.replaceChildren();
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
if (!triggerPrepend && source?.childNodes.length) {
|
|
1217
|
+
const generated = generatedTrigger || document.createElement("span");
|
|
1218
|
+
generated.setAttribute("slot", "prepend-trigger");
|
|
1219
|
+
generated.setAttribute("data-fig-select-generated", "");
|
|
1220
|
+
generated.replaceChildren(
|
|
1221
|
+
...Array.from(source.childNodes, (node) => node.cloneNode(true)),
|
|
1222
|
+
);
|
|
1223
|
+
if (generated.parentElement !== this) this.append(generated);
|
|
1224
|
+
triggerPrepend = generated;
|
|
1225
|
+
} else if (triggerPrepend) {
|
|
1226
|
+
generatedTrigger?.remove();
|
|
1227
|
+
} else {
|
|
1228
|
+
generatedTrigger?.remove();
|
|
1229
|
+
}
|
|
1230
|
+
const hasContent = Boolean(
|
|
1231
|
+
triggerPrepend,
|
|
1153
1232
|
);
|
|
1233
|
+
this.#prependEl.classList.toggle("has-content", hasContent);
|
|
1234
|
+
this.#prependSlot.hidden = !triggerPrepend;
|
|
1235
|
+
this.#prependFallback.hidden = Boolean(triggerPrepend);
|
|
1236
|
+
if (triggerPrepend) {
|
|
1237
|
+
this.#prependFallback.replaceChildren();
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
this.#prependFallback.replaceChildren();
|
|
1154
1241
|
}
|
|
1155
1242
|
|
|
1156
1243
|
#syncPopupAttrs() {
|
|
@@ -1266,7 +1353,10 @@ class FigSelect extends HTMLElement {
|
|
|
1266
1353
|
if (this.#labelEl) this.#labelEl.textContent = label;
|
|
1267
1354
|
this.#syncPrepend(match);
|
|
1268
1355
|
|
|
1269
|
-
const ariaLabel =
|
|
1356
|
+
const ariaLabel =
|
|
1357
|
+
this.getAttribute("aria-label") ||
|
|
1358
|
+
this.getAttribute("label") ||
|
|
1359
|
+
"Select";
|
|
1270
1360
|
this.#button?.setAttribute("aria-label", ariaLabel);
|
|
1271
1361
|
|
|
1272
1362
|
// Don't scrollToOption while open — reposition/sync would fight overflow paging.
|