@rogieking/figui3 6.23.3 → 6.24.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 +18 -4
- package/components.css +38 -23
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +12 -12
- package/dist/fig.css +1 -1
- package/dist/fig.js +22 -22
- package/fig-editor.js +1 -1
- package/fig-lab.css +103 -0
- package/fig-lab.js +29 -3
- package/fig.js +89 -14
- package/package.json +1 -1
package/fig-editor.js
CHANGED
|
@@ -1521,7 +1521,7 @@ class FigFillPicker extends HTMLElement {
|
|
|
1521
1521
|
})
|
|
1522
1522
|
.join("");
|
|
1523
1523
|
const separator = group.label || group.separator
|
|
1524
|
-
? `<fig-
|
|
1524
|
+
? `<fig-separator${group.label ? ` label="${figEditorEscapeAttribute(group.label)}"` : ""}></fig-separator>`
|
|
1525
1525
|
: "";
|
|
1526
1526
|
return `${separator}${options}`;
|
|
1527
1527
|
})
|
package/fig-lab.css
CHANGED
|
@@ -8,6 +8,109 @@
|
|
|
8
8
|
* <link rel="stylesheet" href="fig-lab.css">
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
fig-ai-prompt {
|
|
12
|
+
--fig-ai-prompt-margin-inline: var(--spacer-2);
|
|
13
|
+
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-columns: 1fr auto;
|
|
16
|
+
grid-template-rows: minmax(var(--spacer-6), 1fr) auto;
|
|
17
|
+
position: relative;
|
|
18
|
+
gap: var(--spacer-2);
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
width: calc(100% - var(--fig-ai-prompt-margin-inline) * 2);
|
|
21
|
+
min-height: 8rem;
|
|
22
|
+
margin-inline: var(--fig-ai-prompt-margin-inline);
|
|
23
|
+
padding: var(--spacer-3);
|
|
24
|
+
border: 1px solid var(--figma-color-border);
|
|
25
|
+
border-radius: var(--radius-large);
|
|
26
|
+
background: var(--figma-color-bg);
|
|
27
|
+
color: var(--figma-color-text);
|
|
28
|
+
|
|
29
|
+
&:focus-within {
|
|
30
|
+
outline: var(--figma-focus-outline);
|
|
31
|
+
outline-offset: var(--figma-focus-outline-offset);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
& > fig-input-text {
|
|
35
|
+
position: absolute;
|
|
36
|
+
inset: 0;
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
border-radius: 0;
|
|
40
|
+
background: transparent;
|
|
41
|
+
box-shadow: none;
|
|
42
|
+
font-size: var(--body-large-fontSize);
|
|
43
|
+
|
|
44
|
+
&:hover,
|
|
45
|
+
&:has(textarea:focus) {
|
|
46
|
+
outline: 0;
|
|
47
|
+
box-shadow: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
& > textarea {
|
|
51
|
+
box-sizing: border-box;
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
min-height: var(--spacer-6);
|
|
55
|
+
padding: var(--spacer-3);
|
|
56
|
+
padding-bottom: calc(var(--spacer-5) + var(--spacer-3));
|
|
57
|
+
resize: none;
|
|
58
|
+
font-size: inherit;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
& > fig-button {
|
|
63
|
+
grid-row: 2;
|
|
64
|
+
align-self: end;
|
|
65
|
+
position: relative;
|
|
66
|
+
z-index: 1;
|
|
67
|
+
color: var(--figma-color-text);
|
|
68
|
+
|
|
69
|
+
&:first-of-type {
|
|
70
|
+
justify-self: start;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
&:last-of-type {
|
|
74
|
+
justify-self: end;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fig-chat-message {
|
|
80
|
+
display: block;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
width: 100%;
|
|
83
|
+
max-width: 100%;
|
|
84
|
+
color: var(--figma-color-text);
|
|
85
|
+
font-size: var(--body-large-fontSize);
|
|
86
|
+
line-height: calc(var(--line-height) + var(--spacer-1));
|
|
87
|
+
overflow-wrap: anywhere;
|
|
88
|
+
white-space: pre-wrap;
|
|
89
|
+
|
|
90
|
+
&[from="user"] {
|
|
91
|
+
width: auto;
|
|
92
|
+
margin-inline-start: var(--spacer-4);
|
|
93
|
+
padding: var(--spacer-2) var(--spacer-2-5);
|
|
94
|
+
border-radius: var(--radius-large);
|
|
95
|
+
background: var(--figma-color-bg-secondary);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&[from="user"]:has(> fig-avatar) {
|
|
99
|
+
position: relative;
|
|
100
|
+
margin-inline-end: calc(var(--spacer-4) + var(--spacer-2));
|
|
101
|
+
|
|
102
|
+
& > fig-avatar {
|
|
103
|
+
position: absolute;
|
|
104
|
+
right: calc(-1 * (var(--spacer-4) + var(--spacer-2)));
|
|
105
|
+
bottom: 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
& + fig-chat-message {
|
|
110
|
+
margin-top: var(--spacer-3);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
11
114
|
propskit-group {
|
|
12
115
|
display: block;
|
|
13
116
|
width: 100%;
|
package/fig-lab.js
CHANGED
|
@@ -25,6 +25,14 @@ function figLabUniqueId(prefix = "fig-lab") {
|
|
|
25
25
|
return `${prefix}-${figLabUniqueIdCounter}`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/* Presentation-only composition surface for AI prompt controls. */
|
|
29
|
+
class FigAiPrompt extends HTMLElement {}
|
|
30
|
+
figLabDefineElement("fig-ai-prompt", FigAiPrompt);
|
|
31
|
+
|
|
32
|
+
/* Presentation-only message surface for AI conversations. */
|
|
33
|
+
class FigChatMessage extends HTMLElement {}
|
|
34
|
+
figLabDefineElement("fig-chat-message", FigChatMessage);
|
|
35
|
+
|
|
28
36
|
/* Field + Switch wrapper */
|
|
29
37
|
class PropskitSwitch extends HTMLElement {
|
|
30
38
|
#field = null;
|
|
@@ -1685,6 +1693,10 @@ class PropskitSlider extends HTMLElement {
|
|
|
1685
1693
|
|
|
1686
1694
|
for (const mutation of mutations) {
|
|
1687
1695
|
if (mutation.type === "attributes") {
|
|
1696
|
+
if (mutation.attributeName === "value") {
|
|
1697
|
+
this.#pushExternalValueToSlider();
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1688
1700
|
if (
|
|
1689
1701
|
mutation.attributeName &&
|
|
1690
1702
|
this.#ignoredSliderAttrs.has(mutation.attributeName)
|
|
@@ -1822,7 +1834,9 @@ class PropskitSlider extends HTMLElement {
|
|
|
1822
1834
|
if (!this.#slider) return;
|
|
1823
1835
|
const hostAttrs = this.#getForwardedSliderAttrNames();
|
|
1824
1836
|
|
|
1825
|
-
const nextManaged = new Set(
|
|
1837
|
+
const nextManaged = new Set(
|
|
1838
|
+
hostAttrs.filter((name) => name !== "text" && name !== "value"),
|
|
1839
|
+
);
|
|
1826
1840
|
|
|
1827
1841
|
for (const attrName of this.#managedSliderAttrs) {
|
|
1828
1842
|
if (!nextManaged.has(attrName)) {
|
|
@@ -1831,7 +1845,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
1831
1845
|
}
|
|
1832
1846
|
|
|
1833
1847
|
for (const attrName of hostAttrs) {
|
|
1834
|
-
if (attrName === "text") continue;
|
|
1848
|
+
if (attrName === "text" || attrName === "value") continue;
|
|
1835
1849
|
const value = this.getAttribute(attrName) ?? "";
|
|
1836
1850
|
if (this.#slider.getAttribute(attrName) !== value) {
|
|
1837
1851
|
this.#slider.setAttribute(attrName, value);
|
|
@@ -1868,9 +1882,18 @@ class PropskitSlider extends HTMLElement {
|
|
|
1868
1882
|
}
|
|
1869
1883
|
|
|
1870
1884
|
this.#managedSliderAttrs = nextManaged;
|
|
1885
|
+
this.#pushExternalValueToSlider();
|
|
1871
1886
|
this.#queueSteppersSync();
|
|
1872
1887
|
}
|
|
1873
1888
|
|
|
1889
|
+
#pushExternalValueToSlider() {
|
|
1890
|
+
if (!this.#slider) return;
|
|
1891
|
+
const next = this.getAttribute("value") ?? "";
|
|
1892
|
+
if (String(this.#slider.value) !== next) {
|
|
1893
|
+
this.#slider.value = next;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1874
1897
|
#getForwardedSliderAttrNames() {
|
|
1875
1898
|
const reserved = new Set([
|
|
1876
1899
|
"label",
|
|
@@ -2259,7 +2282,10 @@ class PropskitSlider extends HTMLElement {
|
|
|
2259
2282
|
? event.detail
|
|
2260
2283
|
: this.#slider?.value;
|
|
2261
2284
|
if (this.#slider?.value !== undefined) {
|
|
2262
|
-
|
|
2285
|
+
const next = String(this.#slider.value);
|
|
2286
|
+
if (this.getAttribute("value") !== next) {
|
|
2287
|
+
this.setAttribute("value", next);
|
|
2288
|
+
}
|
|
2263
2289
|
}
|
|
2264
2290
|
this.dispatchEvent(
|
|
2265
2291
|
new CustomEvent(type, {
|
package/fig.js
CHANGED
|
@@ -5425,7 +5425,7 @@ class FigSelect extends HTMLElement {
|
|
|
5425
5425
|
return (
|
|
5426
5426
|
node?.nodeType === 1 &&
|
|
5427
5427
|
(node.tagName === "FIG-SELECT-OPTION" ||
|
|
5428
|
-
node.tagName === "FIG-
|
|
5428
|
+
node.tagName === "FIG-SEPARATOR" ||
|
|
5429
5429
|
node.tagName === "FIG-SELECT-OPTIONS")
|
|
5430
5430
|
);
|
|
5431
5431
|
}
|
|
@@ -6282,6 +6282,7 @@ figDefineElement("fig-select", FigSelect);
|
|
|
6282
6282
|
*/
|
|
6283
6283
|
class FigSlider extends HTMLElement {
|
|
6284
6284
|
#isInteracting = false;
|
|
6285
|
+
#pendingRegeneration = false;
|
|
6285
6286
|
#showEmptyTextValue = false;
|
|
6286
6287
|
#isSyncingValueAttribute = false;
|
|
6287
6288
|
#value = "";
|
|
@@ -6347,6 +6348,10 @@ class FigSlider extends HTMLElement {
|
|
|
6347
6348
|
};
|
|
6348
6349
|
this.#boundRangePointerUp = () => {
|
|
6349
6350
|
this.#isInteracting = false;
|
|
6351
|
+
if (this.#pendingRegeneration && this.isConnected) {
|
|
6352
|
+
this.#pendingRegeneration = false;
|
|
6353
|
+
this.#regenerateInnerHTML();
|
|
6354
|
+
}
|
|
6350
6355
|
};
|
|
6351
6356
|
}
|
|
6352
6357
|
|
|
@@ -6616,6 +6621,7 @@ class FigSlider extends HTMLElement {
|
|
|
6616
6621
|
}
|
|
6617
6622
|
|
|
6618
6623
|
disconnectedCallback() {
|
|
6624
|
+
this.#pendingRegeneration = false;
|
|
6619
6625
|
if (this.input) {
|
|
6620
6626
|
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
6621
6627
|
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
@@ -6828,6 +6834,62 @@ class FigSlider extends HTMLElement {
|
|
|
6828
6834
|
}
|
|
6829
6835
|
}
|
|
6830
6836
|
|
|
6837
|
+
#syncRangeConstraint(name, rawValue) {
|
|
6838
|
+
if (!this.input) return;
|
|
6839
|
+
const defaults = this.#typeDefaults[this.type] || this.#typeDefaults.range;
|
|
6840
|
+
const parsed =
|
|
6841
|
+
rawValue === null || String(rawValue).trim() === ""
|
|
6842
|
+
? defaults[name]
|
|
6843
|
+
: Number(rawValue);
|
|
6844
|
+
let next = Number.isFinite(parsed) ? parsed : defaults[name];
|
|
6845
|
+
if (name === "step" && !(next > 0)) {
|
|
6846
|
+
next = defaults.step > 0 ? defaults.step : 1;
|
|
6847
|
+
}
|
|
6848
|
+
if (this[name] === next && this.input[name] === String(next)) return;
|
|
6849
|
+
|
|
6850
|
+
this[name] = next;
|
|
6851
|
+
this.input[name] = String(next);
|
|
6852
|
+
if (name === "min") {
|
|
6853
|
+
this.input.setAttribute("aria-valuemin", String(next));
|
|
6854
|
+
} else if (name === "max") {
|
|
6855
|
+
this.input.setAttribute("aria-valuemax", String(next));
|
|
6856
|
+
}
|
|
6857
|
+
if (this.figInputNumber) {
|
|
6858
|
+
this.figInputNumber.setAttribute(name, String(next));
|
|
6859
|
+
}
|
|
6860
|
+
if (!this.hasAttribute("default") && this.type !== "delta") {
|
|
6861
|
+
this.default = this.min;
|
|
6862
|
+
}
|
|
6863
|
+
this.value = this.#normalizeSliderValue(this.value);
|
|
6864
|
+
this.#syncProperties();
|
|
6865
|
+
this.#syncStepperDatalist();
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
#syncStepperDatalist() {
|
|
6869
|
+
if (this.type !== "stepper" || !this.datalist) return;
|
|
6870
|
+
const steps = Math.min(
|
|
6871
|
+
1001,
|
|
6872
|
+
Math.max(0, Math.floor((this.max - this.min) / this.step) + 1),
|
|
6873
|
+
);
|
|
6874
|
+
const fragment = document.createDocumentFragment();
|
|
6875
|
+
for (let i = 0; i < steps; i++) {
|
|
6876
|
+
const option = document.createElement("option");
|
|
6877
|
+
option.setAttribute("value", this.min + i * this.step);
|
|
6878
|
+
fragment.append(option);
|
|
6879
|
+
}
|
|
6880
|
+
this.datalist.replaceChildren(fragment);
|
|
6881
|
+
this.input.setAttribute("list", this.datalist.id);
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6884
|
+
#regenerateWhenIdle() {
|
|
6885
|
+
if (this.#isInteracting) {
|
|
6886
|
+
this.#pendingRegeneration = true;
|
|
6887
|
+
return;
|
|
6888
|
+
}
|
|
6889
|
+
this.#pendingRegeneration = false;
|
|
6890
|
+
this.#regenerateInnerHTML();
|
|
6891
|
+
}
|
|
6892
|
+
|
|
6831
6893
|
static get observedAttributes() {
|
|
6832
6894
|
return [
|
|
6833
6895
|
"value",
|
|
@@ -6862,6 +6924,7 @@ class FigSlider extends HTMLElement {
|
|
|
6862
6924
|
}
|
|
6863
6925
|
|
|
6864
6926
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
6927
|
+
if (oldValue === newValue) return;
|
|
6865
6928
|
if (name === "value" && this.#isSyncingValueAttribute) return;
|
|
6866
6929
|
if (this.input) {
|
|
6867
6930
|
switch (name) {
|
|
@@ -6922,15 +6985,26 @@ class FigSlider extends HTMLElement {
|
|
|
6922
6985
|
case "min":
|
|
6923
6986
|
case "max":
|
|
6924
6987
|
case "step":
|
|
6988
|
+
this.#syncRangeConstraint(name, newValue);
|
|
6989
|
+
break;
|
|
6990
|
+
case "units":
|
|
6991
|
+
this.units = newValue || "";
|
|
6992
|
+
if (this.figInputNumber) {
|
|
6993
|
+
if (this.units) {
|
|
6994
|
+
this.figInputNumber.setAttribute("units", this.units);
|
|
6995
|
+
} else {
|
|
6996
|
+
this.figInputNumber.removeAttribute("units");
|
|
6997
|
+
}
|
|
6998
|
+
}
|
|
6999
|
+
break;
|
|
6925
7000
|
case "type":
|
|
6926
7001
|
case "variant":
|
|
6927
|
-
|
|
6928
|
-
this
|
|
6929
|
-
this.#regenerateInnerHTML();
|
|
7002
|
+
if (!this.#isInteracting) this[name] = newValue;
|
|
7003
|
+
this.#regenerateWhenIdle();
|
|
6930
7004
|
break;
|
|
6931
7005
|
case "text":
|
|
6932
|
-
this.text = newValue !== "false";
|
|
6933
|
-
this.#
|
|
7006
|
+
if (!this.#isInteracting) this.text = newValue !== "false";
|
|
7007
|
+
this.#regenerateWhenIdle();
|
|
6934
7008
|
break;
|
|
6935
7009
|
case "aria-label":
|
|
6936
7010
|
case "aria-labelledby":
|
|
@@ -14448,7 +14522,7 @@ class FigEasingCurve extends HTMLElement {
|
|
|
14448
14522
|
if (!this.#isEditEnabled() && !p.value && !p.spring) continue;
|
|
14449
14523
|
if (p.group !== currentGroup) {
|
|
14450
14524
|
if (p.group) {
|
|
14451
|
-
optionsHTML += `<fig-
|
|
14525
|
+
optionsHTML += `<fig-separator label="${FigEasingCurve.#escapeAttribute(p.group)}"></fig-separator>`;
|
|
14452
14526
|
}
|
|
14453
14527
|
currentGroup = p.group;
|
|
14454
14528
|
}
|
|
@@ -19276,10 +19350,10 @@ class FigMenuItem extends HTMLElement {
|
|
|
19276
19350
|
figDefineElement("fig-menu-item", FigMenuItem);
|
|
19277
19351
|
|
|
19278
19352
|
/**
|
|
19279
|
-
* Visual divider between
|
|
19353
|
+
* Visual divider between content groups.
|
|
19280
19354
|
* @attr {string} label - Optional group label shown in secondary text under the line.
|
|
19281
19355
|
*/
|
|
19282
|
-
class
|
|
19356
|
+
class FigSeparator extends HTMLElement {
|
|
19283
19357
|
static get observedAttributes() {
|
|
19284
19358
|
return ["label"];
|
|
19285
19359
|
}
|
|
@@ -19311,7 +19385,7 @@ class FigMenuSeparator extends HTMLElement {
|
|
|
19311
19385
|
else this.removeAttribute("aria-label");
|
|
19312
19386
|
}
|
|
19313
19387
|
}
|
|
19314
|
-
figDefineElement("fig-
|
|
19388
|
+
figDefineElement("fig-separator", FigSeparator);
|
|
19315
19389
|
|
|
19316
19390
|
class FigMenu extends HTMLElement {
|
|
19317
19391
|
#popup = null;
|
|
@@ -19390,7 +19464,7 @@ class FigMenu extends HTMLElement {
|
|
|
19390
19464
|
this.#popup.removeEventListener("close", this.#boundPopupClose);
|
|
19391
19465
|
const items = Array.from(
|
|
19392
19466
|
this.#panel?.querySelectorAll(
|
|
19393
|
-
":scope > fig-menu-item, :scope > fig-
|
|
19467
|
+
":scope > fig-menu-item, :scope > fig-separator",
|
|
19394
19468
|
) ?? [],
|
|
19395
19469
|
);
|
|
19396
19470
|
for (const item of items) this.insertBefore(item, this.#popup);
|
|
@@ -19433,7 +19507,7 @@ class FigMenu extends HTMLElement {
|
|
|
19433
19507
|
#detectTrigger() {
|
|
19434
19508
|
this.#trigger =
|
|
19435
19509
|
this.querySelector("[fig-menu-trigger]") ||
|
|
19436
|
-
this.querySelector(":scope > :not(fig-menu-item):not(fig-
|
|
19510
|
+
this.querySelector(":scope > :not(fig-menu-item):not(fig-separator)");
|
|
19437
19511
|
}
|
|
19438
19512
|
|
|
19439
19513
|
#usesContextMenuTrigger() {
|
|
@@ -19473,7 +19547,7 @@ class FigMenu extends HTMLElement {
|
|
|
19473
19547
|
#moveItemsToPopup() {
|
|
19474
19548
|
if (!this.#panel) return;
|
|
19475
19549
|
const items = Array.from(this.querySelectorAll(
|
|
19476
|
-
":scope > fig-menu-item, :scope > fig-
|
|
19550
|
+
":scope > fig-menu-item, :scope > fig-separator"
|
|
19477
19551
|
));
|
|
19478
19552
|
for (const item of items) {
|
|
19479
19553
|
this.#panel.appendChild(item);
|
|
@@ -19513,7 +19587,8 @@ class FigMenu extends HTMLElement {
|
|
|
19513
19587
|
for (const node of mutation.addedNodes) {
|
|
19514
19588
|
if (node.nodeType !== 1 || node === this.#popup) continue;
|
|
19515
19589
|
if (
|
|
19516
|
-
(node.tagName === "FIG-MENU-ITEM" ||
|
|
19590
|
+
(node.tagName === "FIG-MENU-ITEM" ||
|
|
19591
|
+
node.tagName === "FIG-SEPARATOR") &&
|
|
19517
19592
|
node.parentElement === this
|
|
19518
19593
|
) {
|
|
19519
19594
|
this.#panel?.appendChild(node);
|
package/package.json
CHANGED