@rogieking/figui3 6.26.0 → 6.28.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 +25 -6
- package/dist/fig-editor.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +21 -18
- package/dist/fig.js +21 -21
- package/fig-lab.css +46 -13
- package/fig-lab.js +360 -138
- package/fig.js +29 -0
- 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(
|
|
@@ -565,10 +724,12 @@ class PropskitSelect extends HTMLElement {
|
|
|
565
724
|
#managedSelectAttrs = new Set();
|
|
566
725
|
#boundHandleInput = null;
|
|
567
726
|
#boundHandleChange = null;
|
|
727
|
+
#boundHandleOptionHover = null;
|
|
568
728
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
569
729
|
#boundHandlePointerDown = this.#handlePointerDown.bind(this);
|
|
570
730
|
/** True when pointerdown saw the menu open — skip click-to-open after light-dismiss. */
|
|
571
731
|
#closeGesture = false;
|
|
732
|
+
#initialValue = null;
|
|
572
733
|
|
|
573
734
|
static get observedAttributes() {
|
|
574
735
|
return ["label", "direction", "aria-label", "options", "value"];
|
|
@@ -583,6 +744,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
583
744
|
this.addEventListener("click", this.#boundHandleClick);
|
|
584
745
|
this.removeEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
585
746
|
this.addEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
747
|
+
figLabConnectPropskitResetMenu(this);
|
|
586
748
|
|
|
587
749
|
if (!this.#observer) {
|
|
588
750
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -616,6 +778,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
616
778
|
this.#unbindSelectEvents();
|
|
617
779
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
618
780
|
this.removeEventListener("pointerdown", this.#boundHandlePointerDown, true);
|
|
781
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
619
782
|
}
|
|
620
783
|
|
|
621
784
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -630,6 +793,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
630
793
|
}
|
|
631
794
|
|
|
632
795
|
#initialize() {
|
|
796
|
+
this.#initialValue = this.getAttribute("value") ?? "";
|
|
633
797
|
const customLabel = this.querySelector(":scope > label");
|
|
634
798
|
const field = document.createElement("fig-field");
|
|
635
799
|
const label = customLabel || document.createElement("label");
|
|
@@ -687,6 +851,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
687
851
|
"size",
|
|
688
852
|
"aria-label",
|
|
689
853
|
"full",
|
|
854
|
+
"default",
|
|
690
855
|
]);
|
|
691
856
|
return this.getAttributeNames().filter(
|
|
692
857
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -719,8 +884,13 @@ class PropskitSelect extends HTMLElement {
|
|
|
719
884
|
if (!this.#select) return;
|
|
720
885
|
this.#boundHandleInput ??= this.#forwardSelectEvent.bind(this, "input");
|
|
721
886
|
this.#boundHandleChange ??= this.#forwardSelectEvent.bind(this, "change");
|
|
887
|
+
this.#boundHandleOptionHover ??= this.#forwardSelectEvent.bind(
|
|
888
|
+
this,
|
|
889
|
+
"optionhover",
|
|
890
|
+
);
|
|
722
891
|
this.#select.addEventListener("input", this.#boundHandleInput);
|
|
723
892
|
this.#select.addEventListener("change", this.#boundHandleChange);
|
|
893
|
+
this.#select.addEventListener("optionhover", this.#boundHandleOptionHover);
|
|
724
894
|
}
|
|
725
895
|
|
|
726
896
|
#unbindSelectEvents() {
|
|
@@ -731,13 +901,19 @@ class PropskitSelect extends HTMLElement {
|
|
|
731
901
|
if (this.#boundHandleChange) {
|
|
732
902
|
this.#select.removeEventListener("change", this.#boundHandleChange);
|
|
733
903
|
}
|
|
904
|
+
if (this.#boundHandleOptionHover) {
|
|
905
|
+
this.#select.removeEventListener(
|
|
906
|
+
"optionhover",
|
|
907
|
+
this.#boundHandleOptionHover,
|
|
908
|
+
);
|
|
909
|
+
}
|
|
734
910
|
}
|
|
735
911
|
|
|
736
912
|
#forwardSelectEvent(type, event) {
|
|
737
913
|
if (event.target !== this.#select) return;
|
|
738
914
|
event.stopImmediatePropagation();
|
|
739
915
|
const value = this.#select?.value ?? "";
|
|
740
|
-
this.setAttribute("value", String(value));
|
|
916
|
+
if (type !== "optionhover") this.setAttribute("value", String(value));
|
|
741
917
|
const detail =
|
|
742
918
|
event instanceof CustomEvent && event.detail !== undefined
|
|
743
919
|
? event.detail
|
|
@@ -763,6 +939,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
763
939
|
|
|
764
940
|
#handlePointerDown(event) {
|
|
765
941
|
if (!(event.target instanceof Element)) return;
|
|
942
|
+
if (event.target.closest("fig-menu")) return;
|
|
766
943
|
// fig-select owns its trigger/option clicks.
|
|
767
944
|
if (event.target.closest("fig-select")) {
|
|
768
945
|
this.#closeGesture = false;
|
|
@@ -773,6 +950,9 @@ class PropskitSelect extends HTMLElement {
|
|
|
773
950
|
}
|
|
774
951
|
|
|
775
952
|
#handleClick(event) {
|
|
953
|
+
if (event.target instanceof Element && event.target.closest("fig-menu")) {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
776
956
|
if (event.target instanceof Element && event.target.closest("fig-select")) {
|
|
777
957
|
this.#closeGesture = false;
|
|
778
958
|
return;
|
|
@@ -804,6 +984,21 @@ class PropskitSelect extends HTMLElement {
|
|
|
804
984
|
}
|
|
805
985
|
}
|
|
806
986
|
|
|
987
|
+
get defaultValue() {
|
|
988
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
get isDefault() {
|
|
992
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
resetToDefault() {
|
|
996
|
+
const value = this.defaultValue;
|
|
997
|
+
this.value = value;
|
|
998
|
+
if (this.#select) this.#select.value = value;
|
|
999
|
+
figLabEmitPropskitReset(this, value);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
807
1002
|
focus(options) {
|
|
808
1003
|
this.#select?.focus(options);
|
|
809
1004
|
}
|
|
@@ -821,6 +1016,7 @@ class PropskitText extends HTMLElement {
|
|
|
821
1016
|
#boundHandleInput = null;
|
|
822
1017
|
#boundHandleChange = null;
|
|
823
1018
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
1019
|
+
#initialValue = null;
|
|
824
1020
|
|
|
825
1021
|
static get observedAttributes() {
|
|
826
1022
|
return ["label", "direction", "aria-label"];
|
|
@@ -833,6 +1029,7 @@ class PropskitText extends HTMLElement {
|
|
|
833
1029
|
this.#bindInputEvents();
|
|
834
1030
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
835
1031
|
this.addEventListener("click", this.#boundHandleClick);
|
|
1032
|
+
figLabConnectPropskitResetMenu(this);
|
|
836
1033
|
|
|
837
1034
|
if (!this.#observer) {
|
|
838
1035
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -864,6 +1061,7 @@ class PropskitText extends HTMLElement {
|
|
|
864
1061
|
this.#observer?.disconnect();
|
|
865
1062
|
this.#unbindInputEvents();
|
|
866
1063
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1064
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
867
1065
|
}
|
|
868
1066
|
|
|
869
1067
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -874,6 +1072,7 @@ class PropskitText extends HTMLElement {
|
|
|
874
1072
|
}
|
|
875
1073
|
|
|
876
1074
|
#initialize() {
|
|
1075
|
+
this.#initialValue = this.getAttribute("value") ?? "";
|
|
877
1076
|
const initialChildren = Array.from(this.childNodes).filter(
|
|
878
1077
|
(node) =>
|
|
879
1078
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
@@ -938,6 +1137,7 @@ class PropskitText extends HTMLElement {
|
|
|
938
1137
|
"aria-label",
|
|
939
1138
|
"multiline",
|
|
940
1139
|
"resizable",
|
|
1140
|
+
"default",
|
|
941
1141
|
]);
|
|
942
1142
|
return this.getAttributeNames().filter(
|
|
943
1143
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -996,7 +1196,10 @@ class PropskitText extends HTMLElement {
|
|
|
996
1196
|
}
|
|
997
1197
|
|
|
998
1198
|
#handleClick(event) {
|
|
999
|
-
if (
|
|
1199
|
+
if (
|
|
1200
|
+
event.target instanceof Element &&
|
|
1201
|
+
event.target.closest("fig-input-text, fig-menu")
|
|
1202
|
+
) {
|
|
1000
1203
|
return;
|
|
1001
1204
|
}
|
|
1002
1205
|
this.focus();
|
|
@@ -1017,6 +1220,20 @@ class PropskitText extends HTMLElement {
|
|
|
1017
1220
|
}
|
|
1018
1221
|
}
|
|
1019
1222
|
|
|
1223
|
+
get defaultValue() {
|
|
1224
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
get isDefault() {
|
|
1228
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
resetToDefault() {
|
|
1232
|
+
const value = this.defaultValue;
|
|
1233
|
+
this.value = value;
|
|
1234
|
+
figLabEmitPropskitReset(this, value);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1020
1237
|
focus(options) {
|
|
1021
1238
|
this.#input?.focus(options);
|
|
1022
1239
|
}
|
|
@@ -1034,6 +1251,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1034
1251
|
#boundHandleInput = null;
|
|
1035
1252
|
#boundHandleChange = null;
|
|
1036
1253
|
#boundHandleClick = this.#handleClick.bind(this);
|
|
1254
|
+
#initialValue = null;
|
|
1037
1255
|
|
|
1038
1256
|
static get observedAttributes() {
|
|
1039
1257
|
return ["label", "direction"];
|
|
@@ -1046,6 +1264,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1046
1264
|
this.#bindInputEvents();
|
|
1047
1265
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1048
1266
|
this.addEventListener("click", this.#boundHandleClick);
|
|
1267
|
+
figLabConnectPropskitResetMenu(this);
|
|
1049
1268
|
|
|
1050
1269
|
if (!this.#observer) {
|
|
1051
1270
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -1076,6 +1295,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1076
1295
|
this.#observer?.disconnect();
|
|
1077
1296
|
this.#unbindInputEvents();
|
|
1078
1297
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
1298
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
1079
1299
|
}
|
|
1080
1300
|
|
|
1081
1301
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -1084,6 +1304,8 @@ class PropskitNumber extends HTMLElement {
|
|
|
1084
1304
|
}
|
|
1085
1305
|
|
|
1086
1306
|
#initialize() {
|
|
1307
|
+
this.#initialValue =
|
|
1308
|
+
this.getAttribute("value") ?? this.getAttribute("min") ?? "0";
|
|
1087
1309
|
const initialChildren = Array.from(this.childNodes).filter(
|
|
1088
1310
|
(node) =>
|
|
1089
1311
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
@@ -1140,6 +1362,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
1140
1362
|
"class",
|
|
1141
1363
|
"style",
|
|
1142
1364
|
"id",
|
|
1365
|
+
"default",
|
|
1143
1366
|
]);
|
|
1144
1367
|
return this.getAttributeNames().filter(
|
|
1145
1368
|
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
@@ -1199,7 +1422,10 @@ class PropskitNumber extends HTMLElement {
|
|
|
1199
1422
|
}
|
|
1200
1423
|
|
|
1201
1424
|
#handleClick(event) {
|
|
1202
|
-
if (
|
|
1425
|
+
if (
|
|
1426
|
+
event.target instanceof Element &&
|
|
1427
|
+
event.target.closest("fig-input-number, fig-menu")
|
|
1428
|
+
) {
|
|
1203
1429
|
return;
|
|
1204
1430
|
}
|
|
1205
1431
|
this.focus();
|
|
@@ -1220,6 +1446,20 @@ class PropskitNumber extends HTMLElement {
|
|
|
1220
1446
|
}
|
|
1221
1447
|
}
|
|
1222
1448
|
|
|
1449
|
+
get defaultValue() {
|
|
1450
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "0";
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
get isDefault() {
|
|
1454
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
resetToDefault() {
|
|
1458
|
+
const value = this.defaultValue;
|
|
1459
|
+
this.value = value;
|
|
1460
|
+
figLabEmitPropskitReset(this, this.#input?.value ?? value);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1223
1463
|
focus(options) {
|
|
1224
1464
|
this.#input?.focus(options);
|
|
1225
1465
|
}
|
|
@@ -1237,12 +1477,12 @@ class PropskitGroup extends HTMLElement {
|
|
|
1237
1477
|
"propskit-slider",
|
|
1238
1478
|
"propskit-switch",
|
|
1239
1479
|
"propskit-text",
|
|
1480
|
+
"propskit-oscillator",
|
|
1240
1481
|
].join(",");
|
|
1241
1482
|
|
|
1242
1483
|
#header = null;
|
|
1243
1484
|
#chevron = null;
|
|
1244
1485
|
#resetTooltip = null;
|
|
1245
|
-
#defaults = new WeakMap();
|
|
1246
1486
|
#childObserver = null;
|
|
1247
1487
|
#dirtyFrame = 0;
|
|
1248
1488
|
#boundOnControlEvent = () => this.#queueDirtySync();
|
|
@@ -1251,7 +1491,6 @@ class PropskitGroup extends HTMLElement {
|
|
|
1251
1491
|
this.#render();
|
|
1252
1492
|
this.#bindDirtyListeners();
|
|
1253
1493
|
requestAnimationFrame(() => {
|
|
1254
|
-
this.#ensureDefaults();
|
|
1255
1494
|
this.#syncDirtyState();
|
|
1256
1495
|
});
|
|
1257
1496
|
}
|
|
@@ -1334,12 +1573,16 @@ class PropskitGroup extends HTMLElement {
|
|
|
1334
1573
|
|
|
1335
1574
|
if (!this.#childObserver) {
|
|
1336
1575
|
this.#childObserver = new MutationObserver(() => {
|
|
1337
|
-
this.#ensureDefaults();
|
|
1338
1576
|
this.#queueDirtySync();
|
|
1339
1577
|
});
|
|
1340
1578
|
}
|
|
1341
1579
|
this.#childObserver.disconnect();
|
|
1342
|
-
this.#childObserver.observe(this, {
|
|
1580
|
+
this.#childObserver.observe(this, {
|
|
1581
|
+
childList: true,
|
|
1582
|
+
subtree: true,
|
|
1583
|
+
attributes: true,
|
|
1584
|
+
attributeFilter: ["value", "checked", "default"],
|
|
1585
|
+
});
|
|
1343
1586
|
}
|
|
1344
1587
|
|
|
1345
1588
|
#unbindDirtyListeners() {
|
|
@@ -1352,7 +1595,6 @@ class PropskitGroup extends HTMLElement {
|
|
|
1352
1595
|
if (this.#dirtyFrame) return;
|
|
1353
1596
|
this.#dirtyFrame = requestAnimationFrame(() => {
|
|
1354
1597
|
this.#dirtyFrame = 0;
|
|
1355
|
-
this.#ensureDefaults();
|
|
1356
1598
|
this.#syncDirtyState();
|
|
1357
1599
|
});
|
|
1358
1600
|
}
|
|
@@ -1391,48 +1633,15 @@ class PropskitGroup extends HTMLElement {
|
|
|
1391
1633
|
#controls() {
|
|
1392
1634
|
return [
|
|
1393
1635
|
...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
|
-
}
|
|
1636
|
+
].filter(
|
|
1637
|
+
(el) =>
|
|
1638
|
+
el.closest("propskit-group") === this &&
|
|
1639
|
+
!el.parentElement?.closest(PropskitGroup.#CONTROL_SELECTOR),
|
|
1640
|
+
);
|
|
1426
1641
|
}
|
|
1427
1642
|
|
|
1428
1643
|
#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 ?? "");
|
|
1644
|
+
return el.isDefault === false;
|
|
1436
1645
|
}
|
|
1437
1646
|
|
|
1438
1647
|
#computeDirty() {
|
|
@@ -1450,67 +1659,12 @@ class PropskitGroup extends HTMLElement {
|
|
|
1450
1659
|
}
|
|
1451
1660
|
|
|
1452
1661
|
#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
1662
|
if (typeof el.resetToDefault === "function") {
|
|
1482
|
-
const prevDefault = el.getAttribute("default");
|
|
1483
|
-
el.setAttribute("default", String(snap.value ?? ""));
|
|
1484
1663
|
el.resetToDefault();
|
|
1485
|
-
if (prevDefault === null) el.removeAttribute("default");
|
|
1486
|
-
else el.setAttribute("default", prevDefault);
|
|
1487
|
-
return;
|
|
1488
1664
|
}
|
|
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
1665
|
}
|
|
1511
1666
|
|
|
1512
1667
|
#resetControls() {
|
|
1513
|
-
this.#ensureDefaults();
|
|
1514
1668
|
for (const el of this.#controls()) this.#restoreControl(el);
|
|
1515
1669
|
this.#syncDirtyState();
|
|
1516
1670
|
this.dispatchEvent(
|
|
@@ -1652,6 +1806,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
1652
1806
|
#contextMenu = null;
|
|
1653
1807
|
#pendingClickTimer = 0;
|
|
1654
1808
|
#pendingClickValue = null;
|
|
1809
|
+
#initialValue = null;
|
|
1655
1810
|
#isElasticTracking = false;
|
|
1656
1811
|
#elasticMaxPx = 0;
|
|
1657
1812
|
#elasticRangeRect = null;
|
|
@@ -1725,6 +1880,10 @@ class PropskitSlider extends HTMLElement {
|
|
|
1725
1880
|
this.#pushExternalValueToSlider();
|
|
1726
1881
|
continue;
|
|
1727
1882
|
}
|
|
1883
|
+
if (mutation.attributeName === "color") {
|
|
1884
|
+
syncSlider = true;
|
|
1885
|
+
continue;
|
|
1886
|
+
}
|
|
1728
1887
|
if (
|
|
1729
1888
|
mutation.attributeName &&
|
|
1730
1889
|
this.#ignoredSliderAttrs.has(mutation.attributeName)
|
|
@@ -1784,6 +1943,8 @@ class PropskitSlider extends HTMLElement {
|
|
|
1784
1943
|
}
|
|
1785
1944
|
|
|
1786
1945
|
#initialize() {
|
|
1946
|
+
this.#initialValue =
|
|
1947
|
+
this.getAttribute("value") ?? this.getAttribute("min") ?? "0";
|
|
1787
1948
|
const initialChildren = Array.from(this.childNodes).filter((node) => {
|
|
1788
1949
|
return (
|
|
1789
1950
|
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
|
|
@@ -1825,7 +1986,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
1825
1986
|
|
|
1826
1987
|
const resetItem = document.createElement("fig-menu-item");
|
|
1827
1988
|
resetItem.setAttribute("value", "reset-default");
|
|
1828
|
-
resetItem.textContent = "Reset
|
|
1989
|
+
resetItem.textContent = "Reset";
|
|
1829
1990
|
menu.appendChild(resetItem);
|
|
1830
1991
|
menu.addEventListener("change", this.#boundHandleContextMenuChange);
|
|
1831
1992
|
|
|
@@ -1903,17 +2064,33 @@ class PropskitSlider extends HTMLElement {
|
|
|
1903
2064
|
if (sliderType === "opacity") {
|
|
1904
2065
|
this.#slider.style.setProperty(
|
|
1905
2066
|
"--color",
|
|
1906
|
-
"
|
|
2067
|
+
this.getAttribute("color") || "var(--figma-color-bg-secondary)",
|
|
1907
2068
|
);
|
|
1908
2069
|
} else {
|
|
1909
2070
|
this.#slider.style.removeProperty("--color");
|
|
1910
2071
|
}
|
|
2072
|
+
this.#syncNumberTheme(sliderType);
|
|
1911
2073
|
|
|
1912
2074
|
this.#managedSliderAttrs = nextManaged;
|
|
1913
2075
|
this.#pushExternalValueToSlider();
|
|
1914
2076
|
this.#queueSteppersSync();
|
|
1915
2077
|
}
|
|
1916
2078
|
|
|
2079
|
+
#syncNumberTheme(sliderType) {
|
|
2080
|
+
const numberInput = this.#slider?.querySelector("fig-input-number");
|
|
2081
|
+
if (!numberInput) return;
|
|
2082
|
+
if (sliderType !== "opacity" || !this.hasAttribute("color")) {
|
|
2083
|
+
numberInput.removeAttribute("theme");
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
const theme = figLabPerceivedColorTheme(
|
|
2087
|
+
this.#slider,
|
|
2088
|
+
this.getAttribute("color"),
|
|
2089
|
+
);
|
|
2090
|
+
if (theme) numberInput.setAttribute("theme", theme);
|
|
2091
|
+
else numberInput.removeAttribute("theme");
|
|
2092
|
+
}
|
|
2093
|
+
|
|
1917
2094
|
#pushExternalValueToSlider() {
|
|
1918
2095
|
if (!this.#slider) return;
|
|
1919
2096
|
const next = this.getAttribute("value") ?? "";
|
|
@@ -1977,8 +2154,8 @@ class PropskitSlider extends HTMLElement {
|
|
|
1977
2154
|
}
|
|
1978
2155
|
}
|
|
1979
2156
|
if (numberInput) {
|
|
1980
|
-
numberInput.
|
|
1981
|
-
numberInput.
|
|
2157
|
+
numberInput.removeAttribute("tabindex");
|
|
2158
|
+
numberInput.removeAttribute("aria-hidden");
|
|
1982
2159
|
}
|
|
1983
2160
|
}
|
|
1984
2161
|
|
|
@@ -2228,8 +2405,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
2228
2405
|
return (
|
|
2229
2406
|
this.getAttribute("default") ??
|
|
2230
2407
|
this.#slider?.getAttribute("default") ??
|
|
2231
|
-
this
|
|
2232
|
-
this.#slider?.getAttribute("value") ??
|
|
2408
|
+
this.#initialValue ??
|
|
2233
2409
|
this.#rangeInput?.min ??
|
|
2234
2410
|
"0"
|
|
2235
2411
|
);
|
|
@@ -2347,6 +2523,14 @@ class PropskitSlider extends HTMLElement {
|
|
|
2347
2523
|
if (this.#slider) this.#slider.value = next;
|
|
2348
2524
|
}
|
|
2349
2525
|
|
|
2526
|
+
get defaultValue() {
|
|
2527
|
+
return this.#defaultValue();
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
get isDefault() {
|
|
2531
|
+
return figLabPropskitValuesEqual(this.value, this.defaultValue);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2350
2534
|
resetToDefault() {
|
|
2351
2535
|
this.#resetToDefault();
|
|
2352
2536
|
}
|
|
@@ -3749,6 +3933,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3749
3933
|
#activeFieldInput = null;
|
|
3750
3934
|
#dragController = null;
|
|
3751
3935
|
#dragCleanup = null;
|
|
3936
|
+
#initialValue = null;
|
|
3752
3937
|
|
|
3753
3938
|
static TYPES = [
|
|
3754
3939
|
{ name: "Wave", value: "sine" },
|
|
@@ -3772,11 +3957,18 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3772
3957
|
}
|
|
3773
3958
|
|
|
3774
3959
|
connectedCallback() {
|
|
3960
|
+
if (this.#initialValue === null) {
|
|
3961
|
+
this.#initialValue =
|
|
3962
|
+
this.getAttribute("default") ??
|
|
3963
|
+
this.getAttribute("value") ??
|
|
3964
|
+
JSON.stringify({ waves: [PropskitOscillator.#defaultWave()] });
|
|
3965
|
+
}
|
|
3775
3966
|
this.#precision = this.#readInteger("precision", 2);
|
|
3776
3967
|
this.#parseValue(this.getAttribute("value"));
|
|
3777
3968
|
this.#syncAspectRatio();
|
|
3778
3969
|
this.#render();
|
|
3779
3970
|
this.#setupResizeObserver();
|
|
3971
|
+
figLabConnectPropskitResetMenu(this);
|
|
3780
3972
|
}
|
|
3781
3973
|
|
|
3782
3974
|
disconnectedCallback() {
|
|
@@ -3786,6 +3978,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3786
3978
|
this.#resizeObserver.disconnect();
|
|
3787
3979
|
this.#resizeObserver = null;
|
|
3788
3980
|
}
|
|
3981
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
3789
3982
|
}
|
|
3790
3983
|
|
|
3791
3984
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -3825,6 +4018,27 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3825
4018
|
);
|
|
3826
4019
|
}
|
|
3827
4020
|
|
|
4021
|
+
get defaultValue() {
|
|
4022
|
+
return this.getAttribute("default") ?? this.#initialValue ?? "";
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
get isDefault() {
|
|
4026
|
+
const defaultWaves = this.#normalizedWaves(this.defaultValue);
|
|
4027
|
+
if (!defaultWaves) return false;
|
|
4028
|
+
return (
|
|
4029
|
+
JSON.stringify(this.data.waves) ===
|
|
4030
|
+
JSON.stringify(defaultWaves.map((wave) => this.#roundWave(wave)))
|
|
4031
|
+
);
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
resetToDefault() {
|
|
4035
|
+
const value = this.defaultValue;
|
|
4036
|
+
if (!value) return;
|
|
4037
|
+
this.value = value;
|
|
4038
|
+
this.#emit("input");
|
|
4039
|
+
this.#emit("change");
|
|
4040
|
+
}
|
|
4041
|
+
|
|
3828
4042
|
get data() {
|
|
3829
4043
|
return {
|
|
3830
4044
|
waves: this.#waves.map((wave) => this.#roundWave(wave)),
|
|
@@ -3866,33 +4080,35 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3866
4080
|
}
|
|
3867
4081
|
|
|
3868
4082
|
#parseValue(value) {
|
|
3869
|
-
|
|
4083
|
+
const nextWaves = this.#normalizedWaves(value);
|
|
4084
|
+
if (!nextWaves) return false;
|
|
4085
|
+
this.#waves = nextWaves;
|
|
4086
|
+
if (!this.#waves.length) {
|
|
4087
|
+
this.#waves = [PropskitOscillator.#defaultWave()];
|
|
4088
|
+
}
|
|
4089
|
+
this.#activeWaveIndex = Math.min(this.#activeWaveIndex, this.#waves.length - 1);
|
|
4090
|
+
return true;
|
|
4091
|
+
}
|
|
3870
4092
|
|
|
4093
|
+
#normalizedWaves(value) {
|
|
4094
|
+
if (!value) return null;
|
|
3871
4095
|
let parsed = null;
|
|
3872
4096
|
if (typeof value === "string") {
|
|
3873
4097
|
try {
|
|
3874
4098
|
parsed = JSON.parse(value);
|
|
3875
4099
|
} catch {
|
|
3876
|
-
|
|
4100
|
+
return null;
|
|
3877
4101
|
}
|
|
3878
4102
|
} else if (typeof value === "object") {
|
|
3879
4103
|
parsed = value;
|
|
3880
4104
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
const nextWaves = Array.isArray(parsed)
|
|
4105
|
+
if (!parsed) return null;
|
|
4106
|
+
const waves = Array.isArray(parsed)
|
|
3885
4107
|
? parsed
|
|
3886
4108
|
: Array.isArray(parsed.waves)
|
|
3887
4109
|
? parsed.waves
|
|
3888
4110
|
: [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;
|
|
4111
|
+
return waves.map((wave) => this.#normalizeWave(wave));
|
|
3896
4112
|
}
|
|
3897
4113
|
|
|
3898
4114
|
#normalizeWave(state = {}) {
|
|
@@ -3993,6 +4209,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
3993
4209
|
this.#updateWaveform();
|
|
3994
4210
|
this.#setupEvents();
|
|
3995
4211
|
this.#startPlayhead();
|
|
4212
|
+
figLabConnectPropskitResetMenu(this);
|
|
3996
4213
|
}
|
|
3997
4214
|
|
|
3998
4215
|
#getInnerHTML() {
|
|
@@ -4029,9 +4246,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4029
4246
|
<fig-tooltip text="Remove form">
|
|
4030
4247
|
<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
4248
|
</fig-tooltip>
|
|
4032
|
-
|
|
4033
|
-
${this.#getWaveTypeSelectHTML("propskit-oscillator-add-type propskit-oscillator-add-type-button", "sine", disabled, index)}
|
|
4034
|
-
</fig-tooltip>
|
|
4249
|
+
${this.#getWaveTypeMenuHTML(disabled, index)}
|
|
4035
4250
|
</fig-header>
|
|
4036
4251
|
<div class="propskit-oscillator-fields" data-wave-index="${index}"${active}>
|
|
4037
4252
|
${this.#getNumberFieldHTML(index, "frequency", "Frequency", 0.1, 16, 0.1, "")}
|
|
@@ -4042,26 +4257,33 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4042
4257
|
</fig-group>`;
|
|
4043
4258
|
}
|
|
4044
4259
|
|
|
4045
|
-
#
|
|
4046
|
-
const
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
<span slot="prepend">${PropskitOscillator.waveIcon(type.value, 24)}</span>
|
|
4260
|
+
#getWaveTypeMenuHTML(disabled, index) {
|
|
4261
|
+
const items = PropskitOscillator.TYPES.map((type) => {
|
|
4262
|
+
return `<fig-menu-item value="${type.value}">
|
|
4263
|
+
${PropskitOscillator.waveIcon(type.value, 24)}
|
|
4050
4264
|
<span>${type.name}</span>
|
|
4051
|
-
</fig-
|
|
4265
|
+
</fig-menu-item>`;
|
|
4052
4266
|
}).join("");
|
|
4053
|
-
const indexAttr =
|
|
4054
|
-
|
|
4055
|
-
|
|
4267
|
+
const indexAttr = ` data-wave-index="${PropskitOscillator.#escapeAttribute(String(index))}"`;
|
|
4268
|
+
return `<fig-menu class="propskit-oscillator-add-type" position="bottom right"${indexAttr}${disabled}>
|
|
4269
|
+
<fig-tooltip text="Add form">
|
|
4270
|
+
<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>
|
|
4271
|
+
</fig-tooltip>
|
|
4272
|
+
${items}
|
|
4273
|
+
</fig-menu>`;
|
|
4056
4274
|
}
|
|
4057
4275
|
|
|
4058
4276
|
#getNumberFieldHTML(index, name, label, min, max, step, units) {
|
|
4059
4277
|
const disabled = this.#isDisabled() ? " disabled" : "";
|
|
4278
|
+
const typeAttrs =
|
|
4279
|
+
name === "amplitude" || name === "offset"
|
|
4280
|
+
? ' type="delta" default="0"'
|
|
4281
|
+
: "";
|
|
4060
4282
|
const unitsAttr = units
|
|
4061
4283
|
? ` units="${PropskitOscillator.#escapeAttribute(units)}"`
|
|
4062
4284
|
: "";
|
|
4063
4285
|
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>`;
|
|
4286
|
+
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
4287
|
}
|
|
4066
4288
|
|
|
4067
4289
|
#cacheRefs() {
|
|
@@ -4328,7 +4550,7 @@ class PropskitOscillator extends HTMLElement {
|
|
|
4328
4550
|
if (this.#isDisabled()) return;
|
|
4329
4551
|
const insertAfter = this.#indexFromElement(control);
|
|
4330
4552
|
const insertIndex = insertAfter + 1;
|
|
4331
|
-
const type = this.#normalizeType(event.detail ?? control.value);
|
|
4553
|
+
const type = this.#normalizeType(event.detail?.value ?? control.value);
|
|
4332
4554
|
this.#waves.splice(insertIndex, 0, PropskitOscillator.#defaultWave(type));
|
|
4333
4555
|
this.#reindexExpandedWavesAfterInsert(insertIndex);
|
|
4334
4556
|
this.#activeWaveIndex = insertIndex;
|