@rogieking/figui3 6.26.0 → 6.27.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/propkit/SKILL.md +2 -0
- package/README.md +24 -5
- package/dist/fig-editor.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +21 -18
- package/fig-lab.css +46 -13
- package/fig-lab.js +347 -137
- package/package.json +1 -1
package/fig-lab.js
CHANGED
|
@@ -32,6 +32,125 @@ function figLabColorEventAliases(color, alpha, opacity) {
|
|
|
32
32
|
return { color, alpha: normalizedAlpha, opacity: normalizedOpacity };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const figLabPropskitResetMenus = new WeakMap();
|
|
36
|
+
|
|
37
|
+
function figLabConnectPropskitResetMenu(host) {
|
|
38
|
+
let state = figLabPropskitResetMenus.get(host);
|
|
39
|
+
if (!state) {
|
|
40
|
+
const menu = document.createElement("fig-menu");
|
|
41
|
+
menu.setAttribute("position", "bottom left");
|
|
42
|
+
menu.setAttribute("offset", "0 0");
|
|
43
|
+
const resetItem = document.createElement("fig-menu-item");
|
|
44
|
+
resetItem.setAttribute("value", "reset-default");
|
|
45
|
+
resetItem.textContent = "Reset";
|
|
46
|
+
menu.appendChild(resetItem);
|
|
47
|
+
|
|
48
|
+
let fallbackTimer = 0;
|
|
49
|
+
const clearPendingOpen = () => {
|
|
50
|
+
if (fallbackTimer) {
|
|
51
|
+
clearTimeout(fallbackTimer);
|
|
52
|
+
fallbackTimer = 0;
|
|
53
|
+
}
|
|
54
|
+
window.removeEventListener("pointerup", openMenu, true);
|
|
55
|
+
window.removeEventListener("pointercancel", openMenu, true);
|
|
56
|
+
};
|
|
57
|
+
const openMenu = () => {
|
|
58
|
+
clearPendingOpen();
|
|
59
|
+
const point = state?.point;
|
|
60
|
+
if (point) menu.showAt?.(point.x, point.y);
|
|
61
|
+
};
|
|
62
|
+
const handleContextMenu = (event) => {
|
|
63
|
+
if (
|
|
64
|
+
figLabBooleanAttribute(host, "disabled") ||
|
|
65
|
+
event.target?.closest?.("fig-menu")
|
|
66
|
+
) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
event.stopImmediatePropagation?.();
|
|
72
|
+
state.point = { x: event.clientX, y: event.clientY };
|
|
73
|
+
clearPendingOpen();
|
|
74
|
+
window.addEventListener("pointerup", openMenu, {
|
|
75
|
+
once: true,
|
|
76
|
+
capture: true,
|
|
77
|
+
});
|
|
78
|
+
window.addEventListener("pointercancel", openMenu, {
|
|
79
|
+
once: true,
|
|
80
|
+
capture: true,
|
|
81
|
+
});
|
|
82
|
+
fallbackTimer = window.setTimeout(openMenu, 180);
|
|
83
|
+
};
|
|
84
|
+
const handleChange = (event) => {
|
|
85
|
+
event.stopPropagation();
|
|
86
|
+
if (event.detail?.value === "reset-default") host.resetToDefault?.();
|
|
87
|
+
};
|
|
88
|
+
state = {
|
|
89
|
+
menu,
|
|
90
|
+
point: null,
|
|
91
|
+
clearPendingOpen,
|
|
92
|
+
handleContextMenu,
|
|
93
|
+
handleChange,
|
|
94
|
+
};
|
|
95
|
+
figLabPropskitResetMenus.set(host, state);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (state.menu.parentElement !== host) host.appendChild(state.menu);
|
|
99
|
+
host.removeEventListener("contextmenu", state.handleContextMenu);
|
|
100
|
+
host.addEventListener("contextmenu", state.handleContextMenu);
|
|
101
|
+
state.menu.removeEventListener("change", state.handleChange);
|
|
102
|
+
state.menu.addEventListener("change", state.handleChange);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function figLabDisconnectPropskitResetMenu(host) {
|
|
106
|
+
const state = figLabPropskitResetMenus.get(host);
|
|
107
|
+
if (!state) return;
|
|
108
|
+
state.clearPendingOpen();
|
|
109
|
+
host.removeEventListener("contextmenu", state.handleContextMenu);
|
|
110
|
+
state.menu.removeEventListener("change", state.handleChange);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function figLabEmitPropskitReset(host, detail) {
|
|
114
|
+
for (const type of ["input", "change"]) {
|
|
115
|
+
host.dispatchEvent(
|
|
116
|
+
new CustomEvent(type, {
|
|
117
|
+
detail,
|
|
118
|
+
bubbles: true,
|
|
119
|
+
cancelable: true,
|
|
120
|
+
composed: true,
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function figLabPropskitValuesEqual(value, defaultValue) {
|
|
127
|
+
if (typeof value === "boolean" || typeof defaultValue === "boolean") {
|
|
128
|
+
return Boolean(value) === Boolean(defaultValue);
|
|
129
|
+
}
|
|
130
|
+
return String(value ?? "") === String(defaultValue ?? "");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function figLabPerceivedColorTheme(context, color) {
|
|
134
|
+
if (!context || !color) return null;
|
|
135
|
+
const probe = document.createElement("span");
|
|
136
|
+
probe.style.cssText =
|
|
137
|
+
"position:absolute;visibility:hidden;pointer-events:none;color:" + color;
|
|
138
|
+
context.appendChild(probe);
|
|
139
|
+
const resolved = getComputedStyle(probe).color;
|
|
140
|
+
probe.remove();
|
|
141
|
+
if (!resolved.startsWith("rgb")) return null;
|
|
142
|
+
const channels = resolved.match(/[\d.]+/g)?.slice(0, 3).map(Number);
|
|
143
|
+
if (!channels || channels.length < 3 || channels.some(Number.isNaN)) return null;
|
|
144
|
+
const [r, g, b] = channels.map((channel) => {
|
|
145
|
+
const value = channel / 255;
|
|
146
|
+
return value <= 0.04045
|
|
147
|
+
? value / 12.92
|
|
148
|
+
: ((value + 0.055) / 1.055) ** 2.4;
|
|
149
|
+
});
|
|
150
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
151
|
+
return luminance <= 0.179 ? "light" : "dark";
|
|
152
|
+
}
|
|
153
|
+
|
|
35
154
|
/* Unique IDs for lab components such as propskit-group. */
|
|
36
155
|
let figLabUniqueIdCounter = 0;
|
|
37
156
|
function figLabUniqueId(prefix = "fig-lab") {
|
|
@@ -58,6 +177,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
58
177
|
#boundHandleInput = null;
|
|
59
178
|
#boundHandleChange = null;
|
|
60
179
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
180
|
+
#initialChecked = false;
|
|
61
181
|
|
|
62
182
|
static get observedAttributes() {
|
|
63
183
|
return ["label", "direction"];
|
|
@@ -70,6 +190,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
70
190
|
this.#bindSwitchEvents();
|
|
71
191
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
72
192
|
this.addEventListener("click", this.#boundHandleClick);
|
|
193
|
+
figLabConnectPropskitResetMenu(this);
|
|
73
194
|
|
|
74
195
|
if (!this.#observer) {
|
|
75
196
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -100,6 +221,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
100
221
|
this.#observer?.disconnect();
|
|
101
222
|
this.#unbindSwitchEvents();
|
|
102
223
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
224
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
103
225
|
}
|
|
104
226
|
|
|
105
227
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -108,6 +230,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
108
230
|
}
|
|
109
231
|
|
|
110
232
|
#initialize() {
|
|
233
|
+
this.#initialChecked = figLabBooleanAttribute(this, "checked");
|
|
111
234
|
const initialChildren = Array.from(this.childNodes).filter(
|
|
112
235
|
(node) =>
|
|
113
236
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
@@ -171,6 +294,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
171
294
|
"id",
|
|
172
295
|
"checked",
|
|
173
296
|
"value",
|
|
297
|
+
"default",
|
|
174
298
|
]);
|
|
175
299
|
return this.getAttributeNames().filter(
|
|
176
300
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -233,7 +357,10 @@ class PropskitSwitch extends HTMLElement {
|
|
|
233
357
|
}
|
|
234
358
|
|
|
235
359
|
#handleClick(event) {
|
|
236
|
-
if (
|
|
360
|
+
if (
|
|
361
|
+
event.target instanceof Element &&
|
|
362
|
+
event.target.closest("fig-segmented-control, fig-menu")
|
|
363
|
+
) {
|
|
237
364
|
return;
|
|
238
365
|
}
|
|
239
366
|
const value = this.#switch?.value === "on" ? "off" : "on";
|
|
@@ -260,6 +387,25 @@ class PropskitSwitch extends HTMLElement {
|
|
|
260
387
|
this.setAttribute("value", nextValue ?? "");
|
|
261
388
|
}
|
|
262
389
|
|
|
390
|
+
get defaultValue() {
|
|
391
|
+
return this.hasAttribute("default")
|
|
392
|
+
? figLabBooleanAttribute(this, "default")
|
|
393
|
+
: this.#initialChecked;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
get isDefault() {
|
|
397
|
+
return this.checked === this.defaultValue;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
resetToDefault() {
|
|
401
|
+
const checked = this.defaultValue;
|
|
402
|
+
this.checked = checked;
|
|
403
|
+
figLabEmitPropskitReset(this, {
|
|
404
|
+
checked,
|
|
405
|
+
value: this.getAttribute("value") ?? "",
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
263
409
|
focus(options) {
|
|
264
410
|
const selected =
|
|
265
411
|
this.#switch?.querySelector("fig-segment[selected]") ||
|
|
@@ -297,6 +443,7 @@ class PropskitColor extends HTMLElement {
|
|
|
297
443
|
}
|
|
298
444
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
299
445
|
this.addEventListener("click", this.#boundHandleClick);
|
|
446
|
+
figLabConnectPropskitResetMenu(this);
|
|
300
447
|
|
|
301
448
|
if (!this.#observer) {
|
|
302
449
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -328,6 +475,7 @@ class PropskitColor extends HTMLElement {
|
|
|
328
475
|
this.#observer?.disconnect();
|
|
329
476
|
this.#unbindInputEvents();
|
|
330
477
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
478
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
331
479
|
}
|
|
332
480
|
|
|
333
481
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -491,7 +639,10 @@ class PropskitColor extends HTMLElement {
|
|
|
491
639
|
}
|
|
492
640
|
|
|
493
641
|
#handleClick(event) {
|
|
494
|
-
if (
|
|
642
|
+
if (
|
|
643
|
+
event.target instanceof Element &&
|
|
644
|
+
event.target.closest("fig-input-color, fig-menu")
|
|
645
|
+
) {
|
|
495
646
|
return;
|
|
496
647
|
}
|
|
497
648
|
this.focus();
|
|
@@ -527,8 +678,16 @@ class PropskitColor extends HTMLElement {
|
|
|
527
678
|
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
528
679
|
}
|
|
529
680
|
|
|
681
|
+
get defaultValue() {
|
|
682
|
+
return String(this.#defaultValue());
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
get isDefault() {
|
|
686
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
687
|
+
}
|
|
688
|
+
|
|
530
689
|
resetToDefault() {
|
|
531
|
-
const next =
|
|
690
|
+
const next = this.defaultValue;
|
|
532
691
|
this.setAttribute("value", next);
|
|
533
692
|
this.#forceInputValue(next);
|
|
534
693
|
this.dispatchEvent(
|
|
@@ -569,6 +728,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
569
728
|
#boundHandlePointerDown = this.#handlePointerDown.bind(this);
|
|
570
729
|
/** True when pointerdown saw the menu open — skip click-to-open after light-dismiss. */
|
|
571
730
|
#closeGesture = false;
|
|
731
|
+
#initialValue = null;
|
|
572
732
|
|
|
573
733
|
static get observedAttributes() {
|
|
574
734
|
return ["label", "direction", "aria-label", "options", "value"];
|
|
@@ -583,6 +743,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
583
743
|
this.addEventListener("click", this.#boundHandleClick);
|
|
584
744
|
this.removeEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
585
745
|
this.addEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
746
|
+
figLabConnectPropskitResetMenu(this);
|
|
586
747
|
|
|
587
748
|
if (!this.#observer) {
|
|
588
749
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -616,6 +777,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
616
777
|
this.#unbindSelectEvents();
|
|
617
778
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
618
779
|
this.removeEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
780
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
619
781
|
}
|
|
620
782
|
|
|
621
783
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -630,6 +792,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
630
792
|
}
|
|
631
793
|
|
|
632
794
|
#initialize() {
|
|
795
|
+
this.#initialValue = this.getAttribute("value") ?? "";
|
|
633
796
|
const customLabel = this.querySelector(":scope > label");
|
|
634
797
|
const field = document.createElement("fig-field");
|
|
635
798
|
const label = customLabel || document.createElement("label");
|
|
@@ -687,6 +850,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
687
850
|
"size",
|
|
688
851
|
"aria-label",
|
|
689
852
|
"full",
|
|
853
|
+
"default",
|
|
690
854
|
]);
|
|
691
855
|
return this.getAttributeNames().filter(
|
|
692
856
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -763,6 +927,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
763
927
|
|
|
764
928
|
#handlePointerDown(event) {
|
|
765
929
|
if (!(event.target instanceof Element)) return;
|
|
930
|
+
if (event.target.closest("fig-menu")) return;
|
|
766
931
|
// fig-select owns its trigger/option clicks.
|
|
767
932
|
if (event.target.closest("fig-select")) {
|
|
768
933
|
this.#closeGesture = false;
|
|
@@ -773,6 +938,9 @@ class PropskitSelect extends HTMLElement {
|
|
|
773
938
|
}
|
|
774
939
|
|
|
775
940
|
#handleClick(event) {
|
|
941
|
+
if (event.target instanceof Element && event.target.closest("fig-menu")) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
776
944
|
if (event.target instanceof Element && event.target.closest("fig-select")) {
|
|
777
945
|
this.#closeGesture = false;
|
|
778
946
|
return;
|
|
@@ -804,6 +972,21 @@ class PropskitSelect extends HTMLElement {
|
|
|
804
972
|
}
|
|
805
973
|
}
|
|
806
974
|
|
|
975
|
+
get defaultValue() {
|
|
976
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
get isDefault() {
|
|
980
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
resetToDefault() {
|
|
984
|
+
const value = this.defaultValue;
|
|
985
|
+
this.value = value;
|
|
986
|
+
if (this.#select) this.#select.value = value;
|
|
987
|
+
figLabEmitPropskitReset(this, value);
|
|
988
|
+
}
|
|
989
|
+
|
|
807
990
|
focus(options) {
|
|
808
991
|
this.#select?.focus(options);
|
|
809
992
|
}
|
|
@@ -821,6 +1004,7 @@ class PropskitText extends HTMLElement {
|
|
|
821
1004
|
#boundHandleInput = null;
|
|
822
1005
|
#boundHandleChange = null;
|
|
823
1006
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
1007
|
+
#initialValue = null;
|
|
824
1008
|
|
|
825
1009
|
static get observedAttributes() {
|
|
826
1010
|
return ["label", "direction", "aria-label"];
|
|
@@ -833,6 +1017,7 @@ class PropskitText extends HTMLElement {
|
|
|
833
1017
|
this.#bindInputEvents();
|
|
834
1018
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
835
1019
|
this.addEventListener("click", this.#boundHandleClick);
|
|
1020
|
+
figLabConnectPropskitResetMenu(this);
|
|
836
1021
|
|
|
837
1022
|
if (!this.#observer) {
|
|
838
1023
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -864,6 +1049,7 @@ class PropskitText extends HTMLElement {
|
|
|
864
1049
|
this.#observer?.disconnect();
|
|
865
1050
|
this.#unbindInputEvents();
|
|
866
1051
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1052
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
867
1053
|
}
|
|
868
1054
|
|
|
869
1055
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -874,6 +1060,7 @@ class PropskitText extends HTMLElement {
|
|
|
874
1060
|
}
|
|
875
1061
|
|
|
876
1062
|
#initialize() {
|
|
1063
|
+
this.#initialValue = this.getAttribute("value") ?? "";
|
|
877
1064
|
const initialChildren = Array.from(this.childNodes).filter(
|
|
878
1065
|
(node) =>
|
|
879
1066
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
@@ -938,6 +1125,7 @@ class PropskitText extends HTMLElement {
|
|
|
938
1125
|
"aria-label",
|
|
939
1126
|
"multiline",
|
|
940
1127
|
"resizable",
|
|
1128
|
+
"default",
|
|
941
1129
|
]);
|
|
942
1130
|
return this.getAttributeNames().filter(
|
|
943
1131
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -996,7 +1184,10 @@ class PropskitText extends HTMLElement {
|
|
|
996
1184
|
}
|
|
997
1185
|
|
|
998
1186
|
#handleClick(event) {
|
|
999
|
-
if (
|
|
1187
|
+
if (
|
|
1188
|
+
event.target instanceof Element &&
|
|
1189
|
+
event.target.closest("fig-input-text, fig-menu")
|
|
1190
|
+
) {
|
|
1000
1191
|
return;
|
|
1001
1192
|
}
|
|
1002
1193
|
this.focus();
|
|
@@ -1017,6 +1208,20 @@ class PropskitText extends HTMLElement {
|
|
|
1017
1208
|
}
|
|
1018
1209
|
}
|
|
1019
1210
|
|
|
1211
|
+
get defaultValue() {
|
|
1212
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
get isDefault() {
|
|
1216
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
resetToDefault() {
|
|
1220
|
+
const value = this.defaultValue;
|
|
1221
|
+
this.value = value;
|
|
1222
|
+
figLabEmitPropskitReset(this, value);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1020
1225
|
focus(options) {
|
|
1021
1226
|
this.#input?.focus(options);
|
|
1022
1227
|
}
|
|
@@ -1034,6 +1239,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1034
1239
|
#boundHandleInput = null;
|
|
1035
1240
|
#boundHandleChange = null;
|
|
1036
1241
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
1242
|
+
#initialValue = null;
|
|
1037
1243
|
|
|
1038
1244
|
static get observedAttributes() {
|
|
1039
1245
|
return ["label", "direction"];
|
|
@@ -1046,6 +1252,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1046
1252
|
this.#bindInputEvents();
|
|
1047
1253
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1048
1254
|
this.addEventListener("click", this.#boundHandleClick);
|
|
1255
|
+
figLabConnectPropskitResetMenu(this);
|
|
1049
1256
|
|
|
1050
1257
|
if (!this.#observer) {
|
|
1051
1258
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -1076,6 +1283,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1076
1283
|
this.#observer?.disconnect();
|
|
1077
1284
|
this.#unbindInputEvents();
|
|
1078
1285
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1286
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
1079
1287
|
}
|
|
1080
1288
|
|
|
1081
1289
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -1084,6 +1292,8 @@ class PropskitNumber extends HTMLElement {
|
|
|
1084
1292
|
}
|
|
1085
1293
|
|
|
1086
1294
|
#initialize() {
|
|
1295
|
+
this.#initialValue =
|
|
1296
|
+
this.getAttribute("value") ?? this.getAttribute("min") ?? "0";
|
|
1087
1297
|
const initialChildren = Array.from(this.childNodes).filter(
|
|
1088
1298
|
(node) =>
|
|
1089
1299
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
@@ -1140,6 +1350,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1140
1350
|
"class",
|
|
1141
1351
|
"style",
|
|
1142
1352
|
"id",
|
|
1353
|
+
"default",
|
|
1143
1354
|
]);
|
|
1144
1355
|
return this.getAttributeNames().filter(
|
|
1145
1356
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -1199,7 +1410,10 @@ class PropskitNumber extends HTMLElement {
|
|
|
1199
1410
|
}
|
|
1200
1411
|
|
|
1201
1412
|
#handleClick(event) {
|
|
1202
|
-
if (
|
|
1413
|
+
if (
|
|
1414
|
+
event.target instanceof Element &&
|
|
1415
|
+
event.target.closest("fig-input-number, fig-menu")
|
|
1416
|
+
) {
|
|
1203
1417
|
return;
|
|
1204
1418
|
}
|
|
1205
1419
|
this.focus();
|
|
@@ -1220,6 +1434,20 @@ class PropskitNumber extends HTMLElement {
|
|
|
1220
1434
|
}
|
|
1221
1435
|
}
|
|
1222
1436
|
|
|
1437
|
+
get defaultValue() {
|
|
1438
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "0";
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
get isDefault() {
|
|
1442
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
resetToDefault() {
|
|
1446
|
+
const value = this.defaultValue;
|
|
1447
|
+
this.value = value;
|
|
1448
|
+
figLabEmitPropskitReset(this, this.#input?.value ?? value);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1223
1451
|
focus(options) {
|
|
1224
1452
|
this.#input?.focus(options);
|
|
1225
1453
|
}
|
|
@@ -1237,12 +1465,12 @@ class PropskitGroup extends HTMLElement {
|
|
|
1237
1465
|
"propskit-slider",
|
|
1238
1466
|
"propskit-switch",
|
|
1239
1467
|
"propskit-text",
|
|
1468
|
+
"propskit-oscillator",
|
|
1240
1469
|
].join(",");
|
|
1241
1470
|
|
|
1242
1471
|
#header = null;
|
|
1243
1472
|
#chevron = null;
|
|
1244
1473
|
#resetTooltip = null;
|
|
1245
|
-
#defaults = new WeakMap();
|
|
1246
1474
|
#childObserver = null;
|
|
1247
1475
|
#dirtyFrame = 0;
|
|
1248
1476
|
#boundOnControlEvent = () => this.#queueDirtySync();
|
|
@@ -1251,7 +1479,6 @@ class PropskitGroup extends HTMLElement {
|
|
|
1251
1479
|
this.#render();
|
|
1252
1480
|
this.#bindDirtyListeners();
|
|
1253
1481
|
requestAnimationFrame(() => {
|
|
1254
|
-
this.#ensureDefaults();
|
|
1255
1482
|
this.#syncDirtyState();
|
|
1256
1483
|
});
|
|
1257
1484
|
}
|
|
@@ -1334,12 +1561,16 @@ class PropskitGroup extends HTMLElement {
|
|
|
1334
1561
|
|
|
1335
1562
|
if (!this.#childObserver) {
|
|
1336
1563
|
this.#childObserver = new MutationObserver(() => {
|
|
1337
|
-
this.#ensureDefaults();
|
|
1338
1564
|
this.#queueDirtySync();
|
|
1339
1565
|
});
|
|
1340
1566
|
}
|
|
1341
1567
|
this.#childObserver.disconnect();
|
|
1342
|
-
this.#childObserver.observe(this, {
|
|
1568
|
+
this.#childObserver.observe(this, {
|
|
1569
|
+
childList: true,
|
|
1570
|
+
subtree: true,
|
|
1571
|
+
attributes: true,
|
|
1572
|
+
attributeFilter: ["value", "checked", "default"],
|
|
1573
|
+
});
|
|
1343
1574
|
}
|
|
1344
1575
|
|
|
1345
1576
|
#unbindDirtyListeners() {
|
|
@@ -1352,7 +1583,6 @@ class PropskitGroup extends HTMLElement {
|
|
|
1352
1583
|
if (this.#dirtyFrame) return;
|
|
1353
1584
|
this.#dirtyFrame = requestAnimationFrame(() => {
|
|
1354
1585
|
this.#dirtyFrame = 0;
|
|
1355
|
-
this.#ensureDefaults();
|
|
1356
1586
|
this.#syncDirtyState();
|
|
1357
1587
|
});
|
|
1358
1588
|
}
|
|
@@ -1391,48 +1621,15 @@ class PropskitGroup extends HTMLElement {
|
|
|
1391
1621
|
#controls() {
|
|
1392
1622
|
return [
|
|
1393
1623
|
...this.querySelectorAll(PropskitGroup.#CONTROL_SELECTOR),
|
|
1394
|
-
].filter(
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
return { kind: "checked", value: Boolean(el.checked) };
|
|
1400
|
-
}
|
|
1401
|
-
if ("value" in el) {
|
|
1402
|
-
return { kind: "value", value: el.value };
|
|
1403
|
-
}
|
|
1404
|
-
return { kind: "value", value: el.getAttribute("value") ?? "" };
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
#ensureDefaults() {
|
|
1408
|
-
for (const el of this.#controls()) {
|
|
1409
|
-
if (this.#defaults.has(el)) continue;
|
|
1410
|
-
if (el.hasAttribute("default")) {
|
|
1411
|
-
if (el.localName === "propskit-switch") {
|
|
1412
|
-
this.#defaults.set(el, {
|
|
1413
|
-
kind: "checked",
|
|
1414
|
-
value: figLabBooleanAttribute(el, "default"),
|
|
1415
|
-
});
|
|
1416
|
-
continue;
|
|
1417
|
-
}
|
|
1418
|
-
this.#defaults.set(el, {
|
|
1419
|
-
kind: "value",
|
|
1420
|
-
value: el.getAttribute("default") ?? "",
|
|
1421
|
-
});
|
|
1422
|
-
continue;
|
|
1423
|
-
}
|
|
1424
|
-
this.#defaults.set(el, this.#snapshotControl(el));
|
|
1425
|
-
}
|
|
1624
|
+
].filter(
|
|
1625
|
+
(el) =>
|
|
1626
|
+
el.closest("propskit-group") === this &&
|
|
1627
|
+
!el.parentElement?.closest(PropskitGroup.#CONTROL_SELECTOR),
|
|
1628
|
+
);
|
|
1426
1629
|
}
|
|
1427
1630
|
|
|
1428
1631
|
#controlIsDirty(el) {
|
|
1429
|
-
|
|
1430
|
-
if (!snap) return false;
|
|
1431
|
-
const cur = this.#snapshotControl(el);
|
|
1432
|
-
if (snap.kind === "checked") {
|
|
1433
|
-
return Boolean(cur.value) !== Boolean(snap.value);
|
|
1434
|
-
}
|
|
1435
|
-
return String(cur.value ?? "") !== String(snap.value ?? "");
|
|
1632
|
+
return el.isDefault === false;
|
|
1436
1633
|
}
|
|
1437
1634
|
|
|
1438
1635
|
#computeDirty() {
|
|
@@ -1450,67 +1647,12 @@ class PropskitGroup extends HTMLElement {
|
|
|
1450
1647
|
}
|
|
1451
1648
|
|
|
1452
1649
|
#restoreControl(el) {
|
|
1453
|
-
const snap = this.#defaults.get(el);
|
|
1454
|
-
if (!snap) return;
|
|
1455
|
-
|
|
1456
|
-
if (snap.kind === "checked") {
|
|
1457
|
-
el.checked = snap.value;
|
|
1458
|
-
const detail = {
|
|
1459
|
-
checked: Boolean(snap.value),
|
|
1460
|
-
value: el.getAttribute("value") ?? "",
|
|
1461
|
-
};
|
|
1462
|
-
el.dispatchEvent(
|
|
1463
|
-
new CustomEvent("input", {
|
|
1464
|
-
detail,
|
|
1465
|
-
bubbles: true,
|
|
1466
|
-
cancelable: true,
|
|
1467
|
-
composed: true,
|
|
1468
|
-
}),
|
|
1469
|
-
);
|
|
1470
|
-
el.dispatchEvent(
|
|
1471
|
-
new CustomEvent("change", {
|
|
1472
|
-
detail,
|
|
1473
|
-
bubbles: true,
|
|
1474
|
-
cancelable: true,
|
|
1475
|
-
composed: true,
|
|
1476
|
-
}),
|
|
1477
|
-
);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
1650
|
if (typeof el.resetToDefault === "function") {
|
|
1482
|
-
const prevDefault = el.getAttribute("default");
|
|
1483
|
-
el.setAttribute("default", String(snap.value ?? ""));
|
|
1484
1651
|
el.resetToDefault();
|
|
1485
|
-
if (prevDefault === null) el.removeAttribute("default");
|
|
1486
|
-
else el.setAttribute("default", prevDefault);
|
|
1487
|
-
return;
|
|
1488
1652
|
}
|
|
1489
|
-
|
|
1490
|
-
el.value = snap.value;
|
|
1491
|
-
const detail =
|
|
1492
|
-
el.getAttribute?.("value") ??
|
|
1493
|
-
("value" in el ? el.value : snap.value);
|
|
1494
|
-
el.dispatchEvent(
|
|
1495
|
-
new CustomEvent("input", {
|
|
1496
|
-
detail,
|
|
1497
|
-
bubbles: true,
|
|
1498
|
-
cancelable: true,
|
|
1499
|
-
composed: true,
|
|
1500
|
-
}),
|
|
1501
|
-
);
|
|
1502
|
-
el.dispatchEvent(
|
|
1503
|
-
new CustomEvent("change", {
|
|
1504
|
-
detail,
|
|
1505
|
-
bubbles: true,
|
|
1506
|
-
cancelable: true,
|
|
1507
|
-
composed: true,
|
|
1508
|
-
}),
|
|
1509
|
-
);
|
|
1510
1653
|
}
|
|
1511
1654
|
|
|
1512
1655
|
#resetControls() {
|
|
1513
|
-
this.#ensureDefaults();
|
|
1514
1656
|
for (const el of this.#controls()) this.#restoreControl(el);
|
|
1515
1657
|
this.#syncDirtyState();
|
|
1516
1658
|
this.dispatchEvent(
|
|
@@ -1652,6 +1794,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
1652
1794
|
#contextMenu = null;
|
|
1653
1795
|
#pendingClickTimer = 0;
|
|
1654
1796
|
#pendingClickValue = null;
|
|
1797
|
+
#initialValue = null;
|
|
1655
1798
|
#isElasticTracking = false;
|
|
1656
1799
|
#elasticMaxPx = 0;
|
|
1657
1800
|
#elasticRangeRect = null;
|
|
@@ -1725,6 +1868,10 @@ class PropskitSlider extends HTMLElement {
|
|
|
1725
1868
|
this.#pushExternalValueToSlider();
|
|
1726
1869
|
continue;
|
|
1727
1870
|
}
|
|
1871
|
+
if (mutation.attributeName === "color") {
|
|
1872
|
+
syncSlider = true;
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1728
1875
|
if (
|
|
1729
1876
|
mutation.attributeName &&
|
|
1730
1877
|
this.#ignoredSliderAttrs.has(mutation.attributeName)
|
|
@@ -1784,6 +1931,8 @@ class PropskitSlider extends HTMLElement {
|
|
|
1784
1931
|
}
|
|
1785
1932
|
|
|
1786
1933
|
#initialize() {
|
|
1934
|
+
this.#initialValue =
|
|
1935
|
+
this.getAttribute("value") ?? this.getAttribute("min") ?? "0";
|
|
1787
1936
|
const initialChildren = Array.from(this.childNodes).filter((node) => {
|
|
1788
1937
|
return (
|
|
1789
1938
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
|
|
@@ -1825,7 +1974,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
1825
1974
|
|
|
1826
1975
|
const resetItem = document.createElement("fig-menu-item");
|
|
1827
1976
|
resetItem.setAttribute("value", "reset-default");
|
|
1828
|
-
resetItem.textContent = "Reset
|
|
1977
|
+
resetItem.textContent = "Reset";
|
|
1829
1978
|
menu.appendChild(resetItem);
|
|
1830
1979
|
menu.addEventListener("change", this.#boundHandleContextMenuChange);
|
|
1831
1980
|
|
|
@@ -1903,17 +2052,33 @@ class PropskitSlider extends HTMLElement {
|
|
|
1903
2052
|
if (sliderType === "opacity") {
|
|
1904
2053
|
this.#slider.style.setProperty(
|
|
1905
2054
|
"--color",
|
|
1906
|
-
"
|
|
2055
|
+
this.getAttribute("color") || "var(--figma-color-bg-secondary)",
|
|
1907
2056
|
);
|
|
1908
2057
|
} else {
|
|
1909
2058
|
this.#slider.style.removeProperty("--color");
|
|
1910
2059
|
}
|
|
2060
|
+
this.#syncNumberTheme(sliderType);
|
|
1911
2061
|
|
|
1912
2062
|
this.#managedSliderAttrs = nextManaged;
|
|
1913
2063
|
this.#pushExternalValueToSlider();
|
|
1914
2064
|
this.#queueSteppersSync();
|
|
1915
2065
|
}
|
|
1916
2066
|
|
|
2067
|
+
#syncNumberTheme(sliderType) {
|
|
2068
|
+
const numberInput = this.#slider?.querySelector("fig-input-number");
|
|
2069
|
+
if (!numberInput) return;
|
|
2070
|
+
if (sliderType !== "opacity" || !this.hasAttribute("color")) {
|
|
2071
|
+
numberInput.removeAttribute("theme");
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
const theme = figLabPerceivedColorTheme(
|
|
2075
|
+
this.#slider,
|
|
2076
|
+
this.getAttribute("color"),
|
|
2077
|
+
);
|
|
2078
|
+
if (theme) numberInput.setAttribute("theme", theme);
|
|
2079
|
+
else numberInput.removeAttribute("theme");
|
|
2080
|
+
}
|
|
2081
|
+
|
|
1917
2082
|
#pushExternalValueToSlider() {
|
|
1918
2083
|
if (!this.#slider) return;
|
|
1919
2084
|
const next = this.getAttribute("value") ?? "";
|
|
@@ -1977,8 +2142,8 @@ class PropskitSlider extends HTMLElement {
|
|
|
1977
2142
|
}
|
|
1978
2143
|
}
|
|
1979
2144
|
if (numberInput) {
|
|
1980
|
-
numberInput.
|
|
1981
|
-
numberInput.
|
|
2145
|
+
numberInput.removeAttribute("tabindex");
|
|
2146
|
+
numberInput.removeAttribute("aria-hidden");
|
|
1982
2147
|
}
|
|
1983
2148
|
}
|
|
1984
2149
|
|
|
@@ -2228,8 +2393,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
2228
2393
|
return (
|
|
2229
2394
|
this.getAttribute("default") ??
|
|
2230
2395
|
this.#slider?.getAttribute("default") ??
|
|
2231
|
-
this
|
|
2232
|
-
this.#slider?.getAttribute("value") ??
|
|
2396
|
+
this.#initialValue ??
|
|
2233
2397
|
this.#rangeInput?.min ??
|
|
2234
2398
|
"0"
|
|
2235
2399
|
);
|
|
@@ -2347,6 +2511,14 @@ class PropskitSlider extends HTMLElement {
|
|
|
2347
2511
|
if (this.#slider) this.#slider.value = next;
|
|
2348
2512
|
}
|
|
2349
2513
|
|
|
2514
|
+
get defaultValue() {
|
|
2515
|
+
return this.#defaultValue();
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
get isDefault() {
|
|
2519
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2350
2522
|
resetToDefault() {
|
|
2351
2523
|
this.#resetToDefault();
|
|
2352
2524
|
}
|
|
@@ -3749,6 +3921,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3749
3921
|
#activeFieldInput = null;
|
|
3750
3922
|
#dragController = null;
|
|
3751
3923
|
#dragCleanup = null;
|
|
3924
|
+
#initialValue = null;
|
|
3752
3925
|
|
|
3753
3926
|
static TYPES = [
|
|
3754
3927
|
{ name: "Wave", value: "sine" },
|
|
@@ -3772,11 +3945,18 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3772
3945
|
}
|
|
3773
3946
|
|
|
3774
3947
|
connectedCallback() {
|
|
3948
|
+
if (this.#initialValue === null) {
|
|
3949
|
+
this.#initialValue =
|
|
3950
|
+
this.getAttribute("default") ??
|
|
3951
|
+
this.getAttribute("value") ??
|
|
3952
|
+
JSON.stringify({ waves: [PropskitOscillator.#defaultWave()] });
|
|
3953
|
+
}
|
|
3775
3954
|
this.#precision = this.#readInteger("precision", 2);
|
|
3776
3955
|
this.#parseValue(this.getAttribute("value"));
|
|
3777
3956
|
this.#syncAspectRatio();
|
|
3778
3957
|
this.#render();
|
|
3779
3958
|
this.#setupResizeObserver();
|
|
3959
|
+
figLabConnectPropskitResetMenu(this);
|
|
3780
3960
|
}
|
|
3781
3961
|
|
|
3782
3962
|
disconnectedCallback() {
|
|
@@ -3786,6 +3966,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3786
3966
|
this.#resizeObserver.disconnect();
|
|
3787
3967
|
this.#resizeObserver = null;
|
|
3788
3968
|
}
|
|
3969
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
3789
3970
|
}
|
|
3790
3971
|
|
|
3791
3972
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -3825,6 +4006,27 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3825
4006
|
);
|
|
3826
4007
|
}
|
|
3827
4008
|
|
|
4009
|
+
get defaultValue() {
|
|
4010
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
get isDefault() {
|
|
4014
|
+
const defaultWaves = this.#normalizedWaves(this.defaultValue);
|
|
4015
|
+
if (!defaultWaves) return false;
|
|
4016
|
+
return (
|
|
4017
|
+
JSON.stringify(this.data.waves) ===
|
|
4018
|
+
JSON.stringify(defaultWaves.map((wave) => this.#roundWave(wave)))
|
|
4019
|
+
);
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
resetToDefault() {
|
|
4023
|
+
const value = this.defaultValue;
|
|
4024
|
+
if (!value) return;
|
|
4025
|
+
this.value = value;
|
|
4026
|
+
this.#emit("input");
|
|
4027
|
+
this.#emit("change");
|
|
4028
|
+
}
|
|
4029
|
+
|
|
3828
4030
|
get data() {
|
|
3829
4031
|
return {
|
|
3830
4032
|
waves: this.#waves.map((wave) => this.#roundWave(wave)),
|
|
@@ -3866,33 +4068,35 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3866
4068
|
}
|
|
3867
4069
|
|
|
3868
4070
|
#parseValue(value) {
|
|
3869
|
-
|
|
4071
|
+
const nextWaves = this.#normalizedWaves(value);
|
|
4072
|
+
if (!nextWaves) return false;
|
|
4073
|
+
this.#waves = nextWaves;
|
|
4074
|
+
if (!this.#waves.length) {
|
|
4075
|
+
this.#waves = [PropskitOscillator.#defaultWave()];
|
|
4076
|
+
}
|
|
4077
|
+
this.#activeWaveIndex = Math.min(this.#activeWaveIndex, this.#waves.length - 1);
|
|
4078
|
+
return true;
|
|
4079
|
+
}
|
|
3870
4080
|
|
|
4081
|
+
#normalizedWaves(value) {
|
|
4082
|
+
if (!value) return null;
|
|
3871
4083
|
let parsed = null;
|
|
3872
4084
|
if (typeof value === "string") {
|
|
3873
4085
|
try {
|
|
3874
4086
|
parsed = JSON.parse(value);
|
|
3875
4087
|
} catch {
|
|
3876
|
-
|
|
4088
|
+
return null;
|
|
3877
4089
|
}
|
|
3878
4090
|
} else if (typeof value === "object") {
|
|
3879
4091
|
parsed = value;
|
|
3880
4092
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
const nextWaves = Array.isArray(parsed)
|
|
4093
|
+
if (!parsed) return null;
|
|
4094
|
+
const waves = Array.isArray(parsed)
|
|
3885
4095
|
? parsed
|
|
3886
4096
|
: Array.isArray(parsed.waves)
|
|
3887
4097
|
? parsed.waves
|
|
3888
4098
|
: [parsed];
|
|
3889
|
-
|
|
3890
|
-
this.#waves = nextWaves.map((wave) => this.#normalizeWave(wave));
|
|
3891
|
-
if (!this.#waves.length) {
|
|
3892
|
-
this.#waves = [PropskitOscillator.#defaultWave()];
|
|
3893
|
-
}
|
|
3894
|
-
this.#activeWaveIndex = Math.min(this.#activeWaveIndex, this.#waves.length - 1);
|
|
3895
|
-
return true;
|
|
4099
|
+
return waves.map((wave) => this.#normalizeWave(wave));
|
|
3896
4100
|
}
|
|
3897
4101
|
|
|
3898
4102
|
#normalizeWave(state = {}) {
|
|
@@ -3993,6 +4197,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3993
4197
|
this.#updateWaveform();
|
|
3994
4198
|
this.#setupEvents();
|
|
3995
4199
|
this.#startPlayhead();
|
|
4200
|
+
figLabConnectPropskitResetMenu(this);
|
|
3996
4201
|
}
|
|
3997
4202
|
|
|
3998
4203
|
#getInnerHTML() {
|
|
@@ -4029,9 +4234,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4029
4234
|
<fig-tooltip text="Remove form">
|
|
4030
4235
|
<fig-button class="propskit-oscillator-remove-button" variant="ghost" icon data-wave-index="${index}" aria-label="Remove form"${removeDisabled}><fig-icon name="minus"></fig-icon></fig-button>
|
|
4031
4236
|
</fig-tooltip>
|
|
4032
|
-
|
|
4033
|
-
${this.#getWaveTypeSelectHTML("propskit-oscillator-add-type propskit-oscillator-add-type-button", "sine", disabled, index)}
|
|
4034
|
-
</fig-tooltip>
|
|
4237
|
+
${this.#getWaveTypeMenuHTML(disabled, index)}
|
|
4035
4238
|
</fig-header>
|
|
4036
4239
|
<div class="propskit-oscillator-fields" data-wave-index="${index}"${active}>
|
|
4037
4240
|
${this.#getNumberFieldHTML(index, "frequency", "Frequency", 0.1, 16, 0.1, "")}
|
|
@@ -4042,26 +4245,33 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4042
4245
|
</fig-group>`;
|
|
4043
4246
|
}
|
|
4044
4247
|
|
|
4045
|
-
#
|
|
4046
|
-
const
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
<span slot="prepend">${PropskitOscillator.waveIcon(type.value, 24)}</span>
|
|
4248
|
+
#getWaveTypeMenuHTML(disabled, index) {
|
|
4249
|
+
const items = PropskitOscillator.TYPES.map((type) => {
|
|
4250
|
+
return `<fig-menu-item value="${type.value}">
|
|
4251
|
+
${PropskitOscillator.waveIcon(type.value, 24)}
|
|
4050
4252
|
<span>${type.name}</span>
|
|
4051
|
-
</fig-
|
|
4253
|
+
</fig-menu-item>`;
|
|
4052
4254
|
}).join("");
|
|
4053
|
-
const indexAttr =
|
|
4054
|
-
|
|
4055
|
-
|
|
4255
|
+
const indexAttr = ` data-wave-index="${PropskitOscillator.#escapeAttribute(String(index))}"`;
|
|
4256
|
+
return `<fig-menu class="propskit-oscillator-add-type" position="bottom right"${indexAttr}${disabled}>
|
|
4257
|
+
<fig-tooltip text="Add form">
|
|
4258
|
+
<fig-button class="propskit-oscillator-add-type-button" variant="ghost" icon fig-menu-trigger aria-label="Add form"><fig-icon name="plus"></fig-icon></fig-button>
|
|
4259
|
+
</fig-tooltip>
|
|
4260
|
+
${items}
|
|
4261
|
+
</fig-menu>`;
|
|
4056
4262
|
}
|
|
4057
4263
|
|
|
4058
4264
|
#getNumberFieldHTML(index, name, label, min, max, step, units) {
|
|
4059
4265
|
const disabled = this.#isDisabled() ? " disabled" : "";
|
|
4266
|
+
const typeAttrs =
|
|
4267
|
+
name === "amplitude" || name === "offset"
|
|
4268
|
+
? ' type="delta" default="0"'
|
|
4269
|
+
: "";
|
|
4060
4270
|
const unitsAttr = units
|
|
4061
4271
|
? ` units="${PropskitOscillator.#escapeAttribute(units)}"`
|
|
4062
4272
|
: "";
|
|
4063
4273
|
const wave = this.#waves[index] || PropskitOscillator.#defaultWave();
|
|
4064
|
-
return `<propskit-slider class="propskit-oscillator-field" label="${label}" direction="horizontal" name="${name}" data-wave-index="${index}" value="${this.#round(wave[name])}" min="${min}" max="${max}" step="${step}" precision="${this.#precision}" elastic="false"${unitsAttr}${disabled}></propskit-slider>`;
|
|
4274
|
+
return `<propskit-slider class="propskit-oscillator-field" label="${label}" direction="horizontal" name="${name}" data-wave-index="${index}" value="${this.#round(wave[name])}" min="${min}" max="${max}" step="${step}" precision="${this.#precision}" elastic="false"${typeAttrs}${unitsAttr}${disabled}></propskit-slider>`;
|
|
4065
4275
|
}
|
|
4066
4276
|
|
|
4067
4277
|
#cacheRefs() {
|
|
@@ -4328,7 +4538,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4328
4538
|
if (this.#isDisabled()) return;
|
|
4329
4539
|
const insertAfter = this.#indexFromElement(control);
|
|
4330
4540
|
const insertIndex = insertAfter + 1;
|
|
4331
|
-
const type = this.#normalizeType(event.detail ?? control.value);
|
|
4541
|
+
const type = this.#normalizeType(event.detail?.value ?? control.value);
|
|
4332
4542
|
this.#waves.splice(insertIndex, 0, PropskitOscillator.#defaultWave(type));
|
|
4333
4543
|
this.#reindexExpandedWavesAfterInsert(insertIndex);
|
|
4334
4544
|
this.#activeWaveIndex = insertIndex;
|