@xsynaptic/sonic-ui 0.8.0 → 0.9.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 +0 -2
- package/dist/default/elements/dial.js +12 -3
- package/dist/default/elements/envelope.js +2 -2
- package/dist/default/elements/modulation.js +45 -0
- package/dist/default/elements/readout-claim.js +8 -3
- package/dist/default/elements/slider.js +15 -17
- package/dist/default/elements/value-element.js +89 -330
- package/dist/default/elements/value-gestures.js +301 -0
- package/dist/default/elements/waveform.js +5 -2
- package/dist/default/elements/wavestrip.js +5 -15
- package/dist/default/elements/xy.js +9 -20
- package/dist/default/lib/asked-value.js +21 -0
- package/dist/default/lib/canvas-surface.js +3 -0
- package/dist/default/lib/marker-rider.js +6 -3
- package/dist/default/lib/read-ms-property.js +10 -0
- package/dist/dev/elements/dial.js +12 -3
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.js +2 -2
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/modulation.js +46 -0
- package/dist/dev/elements/modulation.js.map +1 -0
- package/dist/dev/elements/readout-claim.js +8 -3
- package/dist/dev/elements/readout-claim.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +0 -1
- package/dist/dev/elements/slider.js +15 -17
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +8 -13
- package/dist/dev/elements/value-element.js +89 -330
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.d.ts +10 -0
- package/dist/dev/elements/value-gestures.js +302 -0
- package/dist/dev/elements/value-gestures.js.map +1 -0
- package/dist/dev/elements/waveform.js +5 -2
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +0 -1
- package/dist/dev/elements/wavestrip.js +5 -15
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +9 -20
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/lib/asked-value.js +22 -0
- package/dist/dev/lib/asked-value.js.map +1 -0
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +3 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/marker-rider.js +6 -3
- package/dist/dev/lib/marker-rider.js.map +1 -1
- package/dist/dev/lib/read-ms-property.js +11 -0
- package/dist/dev/lib/read-ms-property.js.map +1 -0
- package/dist/styles/material.css +18 -0
- package/dist/styles/waveform.css +10 -0
- package/dist/styles/wavestrip.css +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { clamp } from "../lib/math.js";
|
|
2
|
+
import { requireChild } from "../lib/render.js";
|
|
3
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
4
|
+
import { ReadoutClaim, revealDelay } from "./readout-claim.js";
|
|
5
|
+
import { ValueEntry } from "./value-entry.js";
|
|
6
|
+
import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
|
|
7
|
+
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
8
|
+
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
9
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
10
|
+
import { resetKeys } from "../lib/value-mapping.js";
|
|
11
|
+
|
|
12
|
+
//#region src/elements/value-gestures.ts
|
|
13
|
+
const detentZonePx = 8;
|
|
14
|
+
var ValueGestures = class {
|
|
15
|
+
#claim = new ReadoutClaim(() => {
|
|
16
|
+
this.#host.holdChanged();
|
|
17
|
+
});
|
|
18
|
+
#control;
|
|
19
|
+
#entry;
|
|
20
|
+
#host;
|
|
21
|
+
#isRevealed = false;
|
|
22
|
+
#keyScrub;
|
|
23
|
+
#pointerDrag;
|
|
24
|
+
constructor(host, control) {
|
|
25
|
+
this.#host = host;
|
|
26
|
+
this.#control = control;
|
|
27
|
+
}
|
|
28
|
+
bind(signal, grab) {
|
|
29
|
+
const control = this.#control;
|
|
30
|
+
const host = this.#host;
|
|
31
|
+
const entry = this.#bindEntry(signal);
|
|
32
|
+
this.#pointerDrag = bindDrag(control, {
|
|
33
|
+
cancel: (drag) => {
|
|
34
|
+
if (drag.isKeptOnCancel !== true) host.input(drag.fromValue);
|
|
35
|
+
},
|
|
36
|
+
grab: (event) => {
|
|
37
|
+
if (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;
|
|
38
|
+
host.toggleState("springing", false);
|
|
39
|
+
if (event.pointerType === "touch") event.preventDefault();
|
|
40
|
+
focusByPointer(control);
|
|
41
|
+
if (isResetPress(event)) {
|
|
42
|
+
this.#reset();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const fromValue = host.value();
|
|
46
|
+
const axis = grab(event);
|
|
47
|
+
if (!axis) return;
|
|
48
|
+
const drag = {
|
|
49
|
+
...axis,
|
|
50
|
+
fromValue,
|
|
51
|
+
isOutside: false,
|
|
52
|
+
isRevealed: false,
|
|
53
|
+
pointerType: event.pointerType,
|
|
54
|
+
revealTimer: void 0,
|
|
55
|
+
state: this.#startDrag(axis, event)
|
|
56
|
+
};
|
|
57
|
+
drag.revealTimer = setTimeout(() => {
|
|
58
|
+
this.#reveal(drag);
|
|
59
|
+
}, revealDelay(control));
|
|
60
|
+
return drag;
|
|
61
|
+
},
|
|
62
|
+
lift: (drag, event) => {
|
|
63
|
+
const tap = drag.state.isEngaged ? void 0 : host.tapTarget();
|
|
64
|
+
if (tap !== void 0) {
|
|
65
|
+
entry.forgetPress();
|
|
66
|
+
this.#commit(tap);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!entry.press(event) || host.springTarget() !== void 0) return;
|
|
70
|
+
const gesture = host.doublePress();
|
|
71
|
+
if (gesture === "reset") this.#reset();
|
|
72
|
+
else if (gesture === "entry") entry.open();
|
|
73
|
+
},
|
|
74
|
+
move: (drag, event) => {
|
|
75
|
+
this.#dragTo(drag, event);
|
|
76
|
+
},
|
|
77
|
+
release: (drag) => {
|
|
78
|
+
this.#releaseDrag(drag);
|
|
79
|
+
},
|
|
80
|
+
toggle: (isDragging) => {
|
|
81
|
+
host.toggleState("dragging", isDragging);
|
|
82
|
+
}
|
|
83
|
+
}, signal);
|
|
84
|
+
control.addEventListener("pointercancel", () => {
|
|
85
|
+
entry.forgetPress();
|
|
86
|
+
}, { signal });
|
|
87
|
+
this.#bindKeys(entry, signal);
|
|
88
|
+
signal.addEventListener("abort", () => {
|
|
89
|
+
this.#endKeyScrub();
|
|
90
|
+
this.#concealKeyReveal();
|
|
91
|
+
}, { once: true });
|
|
92
|
+
}
|
|
93
|
+
end() {
|
|
94
|
+
this.#entry?.close(false);
|
|
95
|
+
this.#pointerDrag?.end();
|
|
96
|
+
this.#endKeyScrub();
|
|
97
|
+
}
|
|
98
|
+
heldFrom() {
|
|
99
|
+
return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
|
|
100
|
+
}
|
|
101
|
+
isEditing() {
|
|
102
|
+
return this.#entry?.isOpen === true;
|
|
103
|
+
}
|
|
104
|
+
isHeld() {
|
|
105
|
+
return this.#dragging() !== void 0 || this.#keyScrub !== void 0;
|
|
106
|
+
}
|
|
107
|
+
isRevealed() {
|
|
108
|
+
return this.#claim.isRevealed;
|
|
109
|
+
}
|
|
110
|
+
pointerType() {
|
|
111
|
+
return this.#dragging()?.pointerType;
|
|
112
|
+
}
|
|
113
|
+
#bindEntry(signal) {
|
|
114
|
+
const control = this.#control;
|
|
115
|
+
const host = this.#host;
|
|
116
|
+
const input = requireChild(control, "input", HTMLInputElement);
|
|
117
|
+
input.id = host.entryId;
|
|
118
|
+
const entry = new ValueEntry(input, {
|
|
119
|
+
commit: (text) => {
|
|
120
|
+
this.#commit(host.parse(text));
|
|
121
|
+
},
|
|
122
|
+
text: host.entryText,
|
|
123
|
+
toggle: (isOpen) => {
|
|
124
|
+
if (isOpen) host.forwardNaming(input, true);
|
|
125
|
+
host.toggleState("editing", isOpen);
|
|
126
|
+
host.render();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
this.#entry = entry;
|
|
130
|
+
entry.bind(control, signal);
|
|
131
|
+
return entry;
|
|
132
|
+
}
|
|
133
|
+
#bindKeys(entry, signal) {
|
|
134
|
+
const control = this.#control;
|
|
135
|
+
const host = this.#host;
|
|
136
|
+
control.addEventListener("keydown", (event) => {
|
|
137
|
+
if (host.isDisabled() || event.defaultPrevented || event.target !== control) return;
|
|
138
|
+
host.toggleState("springing", false);
|
|
139
|
+
if (event.key === "Enter") {
|
|
140
|
+
if (host.springTarget() !== void 0) return;
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
entry.open();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const next = this.#keyTarget(event.key);
|
|
146
|
+
if (next === void 0) return;
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
this.#keyTo(event, next);
|
|
149
|
+
this.#claim.reveal("keys");
|
|
150
|
+
host.holdChanged();
|
|
151
|
+
}, { signal });
|
|
152
|
+
control.addEventListener("keyup", (event) => {
|
|
153
|
+
const isMeta = event.key === "Meta";
|
|
154
|
+
if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
|
|
155
|
+
if (isMeta && this.#dragging()) return;
|
|
156
|
+
if (isMeta || host.mapping().keyTarget(event.key, host.value()) !== void 0) this.#springBack();
|
|
157
|
+
}, { signal });
|
|
158
|
+
control.addEventListener("blur", () => {
|
|
159
|
+
this.#endKeyScrub();
|
|
160
|
+
this.#concealKeyReveal();
|
|
161
|
+
this.#springBack();
|
|
162
|
+
}, { signal });
|
|
163
|
+
}
|
|
164
|
+
#commit(next, direction = 0) {
|
|
165
|
+
const host = this.#host;
|
|
166
|
+
const landed = Number.isFinite(next) ? host.land(next, direction) : next;
|
|
167
|
+
if (host.input(landed)) host.dispatch("change");
|
|
168
|
+
}
|
|
169
|
+
#concealKeyReveal() {
|
|
170
|
+
if (this.#claim.conceal("keys")) this.#host.holdChanged();
|
|
171
|
+
}
|
|
172
|
+
#dragging() {
|
|
173
|
+
return this.#pointerDrag?.current();
|
|
174
|
+
}
|
|
175
|
+
#dragTo(drag, event) {
|
|
176
|
+
const host = this.#host;
|
|
177
|
+
const isOutside = drag.outside?.(event) === true;
|
|
178
|
+
const wasEngaged = drag.state.isEngaged;
|
|
179
|
+
const mapping = host.mapping();
|
|
180
|
+
const { state, value } = stepDrag(mapping, drag.state, {
|
|
181
|
+
isFine: event.shiftKey,
|
|
182
|
+
isOutside,
|
|
183
|
+
position: drag.position(event)
|
|
184
|
+
});
|
|
185
|
+
drag.state = this.#withinLimit(mapping, state);
|
|
186
|
+
if (value === void 0) return;
|
|
187
|
+
if (!wasEngaged) this.#reveal(drag);
|
|
188
|
+
const isCrossing = isOutside !== drag.isOutside;
|
|
189
|
+
if (isCrossing) {
|
|
190
|
+
drag.isOutside = isOutside;
|
|
191
|
+
host.toggleState("cancelling", isOutside);
|
|
192
|
+
}
|
|
193
|
+
host.input(isOutside ? drag.fromValue : value);
|
|
194
|
+
if (isCrossing) this.#renderDragReveal(drag);
|
|
195
|
+
}
|
|
196
|
+
#endKeyScrub() {
|
|
197
|
+
const host = this.#host;
|
|
198
|
+
const scrub = this.#keyScrub;
|
|
199
|
+
if (!scrub) return;
|
|
200
|
+
this.#keyScrub = void 0;
|
|
201
|
+
host.holdChanged();
|
|
202
|
+
if (host.value() !== scrub.fromValue) host.dispatch("change");
|
|
203
|
+
}
|
|
204
|
+
#keyTarget(key) {
|
|
205
|
+
const host = this.#host;
|
|
206
|
+
if (resetKeys.has(key)) return host.default();
|
|
207
|
+
return host.mapping().keyTarget(key, host.value(), host.keyStep());
|
|
208
|
+
}
|
|
209
|
+
#keyTo(event, next) {
|
|
210
|
+
const host = this.#host;
|
|
211
|
+
if (event.repeat && host.scrubsKeyRepeat()) {
|
|
212
|
+
this.#scrubKey(event.key, next);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.#endKeyScrub();
|
|
216
|
+
this.#commit(next, next > host.value() ? 1 : -1);
|
|
217
|
+
}
|
|
218
|
+
#releaseDrag(drag) {
|
|
219
|
+
const host = this.#host;
|
|
220
|
+
clearTimeout(drag.revealTimer);
|
|
221
|
+
this.#claim.conceal("drag");
|
|
222
|
+
host.toggleState("cancelling", false);
|
|
223
|
+
host.holdChanged();
|
|
224
|
+
this.#showRevealed(false);
|
|
225
|
+
if (host.value() !== drag.fromValue) host.dispatch("change");
|
|
226
|
+
this.#springBack();
|
|
227
|
+
}
|
|
228
|
+
#renderDragReveal(drag) {
|
|
229
|
+
const isRevealed = drag.isRevealed && !drag.isOutside;
|
|
230
|
+
if (isRevealed) this.#claim.reveal("drag");
|
|
231
|
+
else this.#claim.conceal("drag");
|
|
232
|
+
this.#host.holdChanged();
|
|
233
|
+
this.#showRevealed(isRevealed);
|
|
234
|
+
}
|
|
235
|
+
#reset() {
|
|
236
|
+
this.#commit(this.#host.default() ?? NaN);
|
|
237
|
+
}
|
|
238
|
+
#reveal(drag) {
|
|
239
|
+
if (drag.isRevealed || this.#dragging() !== drag) return;
|
|
240
|
+
drag.isRevealed = true;
|
|
241
|
+
this.#renderDragReveal(drag);
|
|
242
|
+
}
|
|
243
|
+
#scrubKey(key, next) {
|
|
244
|
+
const host = this.#host;
|
|
245
|
+
if (this.#keyScrub?.key !== key) {
|
|
246
|
+
this.#endKeyScrub();
|
|
247
|
+
this.#keyScrub = {
|
|
248
|
+
fromValue: host.value(),
|
|
249
|
+
key
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
host.input(next);
|
|
253
|
+
}
|
|
254
|
+
#showRevealed(isRevealed) {
|
|
255
|
+
if (isRevealed === this.#isRevealed) return;
|
|
256
|
+
this.#isRevealed = isRevealed;
|
|
257
|
+
this.#host.toggleState("revealed", isRevealed);
|
|
258
|
+
this.#host.dispatch("sonic-reveal");
|
|
259
|
+
}
|
|
260
|
+
#springBack() {
|
|
261
|
+
const host = this.#host;
|
|
262
|
+
const target = host.springTarget();
|
|
263
|
+
if (target === void 0) return;
|
|
264
|
+
if (host.mapping().snap(target) !== host.value()) host.toggleState("springing", true);
|
|
265
|
+
this.#commit(target);
|
|
266
|
+
}
|
|
267
|
+
#startDrag(axis, event) {
|
|
268
|
+
const host = this.#host;
|
|
269
|
+
const mapping = host.mapping();
|
|
270
|
+
const detent = host.detent();
|
|
271
|
+
const value = host.value();
|
|
272
|
+
const start = {
|
|
273
|
+
from: value,
|
|
274
|
+
position: axis.position(event),
|
|
275
|
+
proportion: axis.fromProportion ?? mapping.proportionOf(value),
|
|
276
|
+
thresholdPx: dragThresholdPx(event.pointerType),
|
|
277
|
+
travelPx: axis.travelPx
|
|
278
|
+
};
|
|
279
|
+
if (detent === void 0) return startDrag(mapping, start);
|
|
280
|
+
const zonePx = readPxProperty(getComputedStyle(this.#control), "--_sonic-detent-zone", detentZonePx);
|
|
281
|
+
return startDrag(mapping, {
|
|
282
|
+
...start,
|
|
283
|
+
detent: {
|
|
284
|
+
value: detent,
|
|
285
|
+
zone: zonePx / axis.travelPx
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
#withinLimit(mapping, state) {
|
|
290
|
+
const limit = this.#host.limit();
|
|
291
|
+
if (!limit || mapping.isWrapping) return state;
|
|
292
|
+
const [low, high] = limit;
|
|
293
|
+
return {
|
|
294
|
+
...state,
|
|
295
|
+
rawProportion: clamp(state.rawProportion, mapping.proportionOf(low), mapping.proportionOf(high))
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
export { ValueGestures };
|
|
@@ -209,7 +209,8 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
209
209
|
className: "sonic-waveform-label",
|
|
210
210
|
colourProperty: "--_sonic-waveform-marker",
|
|
211
211
|
fadeProperty: "--_sonic-waveform-label-fade",
|
|
212
|
-
insetProperty: "--_sonic-waveform-label-inset"
|
|
212
|
+
insetProperty: "--_sonic-waveform-label-inset",
|
|
213
|
+
parkProperty: "--_sonic-waveform-label-park"
|
|
213
214
|
});
|
|
214
215
|
connectedCallback() {
|
|
215
216
|
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "requestPeaks", "zoom");
|
|
@@ -223,7 +224,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
223
224
|
}, { signal });
|
|
224
225
|
}
|
|
225
226
|
draw() {
|
|
226
|
-
this.surface()
|
|
227
|
+
const surface = this.surface();
|
|
228
|
+
surface?.requestFrame();
|
|
229
|
+
if (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());
|
|
227
230
|
}
|
|
228
231
|
isEmpty() {
|
|
229
232
|
return !this.#peaks || this.#peaks.samples.length === 0;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { clampProportion } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
3
|
import { readPxProperty } from "../lib/read-px-property.js";
|
|
4
|
-
import { bindHoverPreview } from "../lib/hover-preview.js";
|
|
5
4
|
import { sortedRegions } from "../lib/time-regions.js";
|
|
6
5
|
import { SonicWaveElement } from "./wave-element.js";
|
|
7
6
|
import { createMarkerBand } from "../lib/marker-band.js";
|
|
@@ -77,7 +76,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
77
76
|
sizeProperty = "--_sonic-wavestrip-size";
|
|
78
77
|
#bars;
|
|
79
78
|
#buffered = [];
|
|
80
|
-
#hover;
|
|
81
79
|
#markerBand = createMarkerBand(requireChild(this.control, ".sonic-wavestrip-markers", HTMLDivElement));
|
|
82
80
|
#paintedKey = "";
|
|
83
81
|
#peaks;
|
|
@@ -86,15 +84,11 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
86
84
|
super.connectedCallback();
|
|
87
85
|
}
|
|
88
86
|
connect(signal) {
|
|
87
|
+
const { control } = this;
|
|
89
88
|
super.connect(signal);
|
|
90
|
-
this
|
|
89
|
+
this.bindHover(control, signal, {
|
|
91
90
|
canShow: () => this.readout,
|
|
92
|
-
|
|
93
|
-
this.render();
|
|
94
|
-
},
|
|
95
|
-
isTaken: () => this.isEditing() || this.isRevealed(),
|
|
96
|
-
show: () => {
|
|
97
|
-
this.renderReadout();
|
|
91
|
+
place: () => {
|
|
98
92
|
this.#placeReadout();
|
|
99
93
|
},
|
|
100
94
|
valueAt: (event) => {
|
|
@@ -103,12 +97,11 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
103
97
|
const mapping = this.mapping();
|
|
104
98
|
return this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));
|
|
105
99
|
}
|
|
106
|
-
}
|
|
107
|
-
this.bindGestures(
|
|
100
|
+
});
|
|
101
|
+
this.bindGestures(control, signal, (event) => this.#grab(event));
|
|
108
102
|
}
|
|
109
103
|
draw() {
|
|
110
104
|
const surface = this.surface();
|
|
111
|
-
if (this.isEditing() || this.isRevealed()) this.#hover?.clear();
|
|
112
105
|
this.#placeReadout();
|
|
113
106
|
this.showCurrentMarker(this.playback());
|
|
114
107
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
@@ -131,9 +124,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
131
124
|
look.colours[kind]
|
|
132
125
|
]);
|
|
133
126
|
}
|
|
134
|
-
previewValue() {
|
|
135
|
-
return this.#hover?.value();
|
|
136
|
-
}
|
|
137
127
|
renderMarkers() {
|
|
138
128
|
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
139
129
|
}
|
|
@@ -3,12 +3,13 @@ import { writeAttribute } from "../lib/write-attribute.js";
|
|
|
3
3
|
import { SonicFormElement } from "./form-element.js";
|
|
4
4
|
import { requireChild, template } from "../lib/render.js";
|
|
5
5
|
import { bindDrag } from "../lib/pointer-drag.js";
|
|
6
|
-
import { ReadoutClaim } from "./readout-claim.js";
|
|
7
6
|
import { Readout } from "./readout.js";
|
|
7
|
+
import { ReadoutClaim, revealDelay } from "./readout-claim.js";
|
|
8
8
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
9
9
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
10
10
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
11
11
|
import { resetKeys, valueMapping } from "../lib/value-mapping.js";
|
|
12
|
+
import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
|
|
12
13
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
13
14
|
|
|
14
15
|
//#region src/elements/xy.ts
|
|
@@ -337,7 +338,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
337
338
|
x: mappings.x.valueAt(pressed.x),
|
|
338
339
|
y: mappings.y.valueAt(pressed.y)
|
|
339
340
|
});
|
|
340
|
-
this.#claim.press(true);
|
|
341
|
+
this.#claim.press(true, revealDelay(this.#xy));
|
|
341
342
|
const start = (axis) => ({
|
|
342
343
|
from: this.#values[axis].value,
|
|
343
344
|
mapping: mappings[axis],
|
|
@@ -359,7 +360,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
359
360
|
}
|
|
360
361
|
#input(next) {
|
|
361
362
|
let hasMoved = false;
|
|
362
|
-
for (const axis of axes) if (this.#
|
|
363
|
+
for (const axis of axes) if (moveTo(this.#values[axis], this.#mappings()[axis], next[axis] ?? NaN)) hasMoved = true;
|
|
363
364
|
if (!hasMoved) return false;
|
|
364
365
|
this.render();
|
|
365
366
|
this.dispatchEvent(new Event("input", {
|
|
@@ -369,8 +370,9 @@ var SonicXy = class extends SonicFormElement {
|
|
|
369
370
|
return true;
|
|
370
371
|
}
|
|
371
372
|
#keyTo(axis, key) {
|
|
372
|
-
const
|
|
373
|
-
const
|
|
373
|
+
const mapping = this.#mappings()[axis];
|
|
374
|
+
const next = mapping.keyTarget(key, this.#values[axis].value);
|
|
375
|
+
const hasMoved = moveTo(this.#values[axis], mapping, next ?? NaN);
|
|
374
376
|
this.#currentAxis = axis;
|
|
375
377
|
this.render();
|
|
376
378
|
this.#parts[axis].focus();
|
|
@@ -404,9 +406,8 @@ var SonicXy = class extends SonicFormElement {
|
|
|
404
406
|
#refresh() {
|
|
405
407
|
const mappings = this.#mappings();
|
|
406
408
|
for (const axis of axes) {
|
|
407
|
-
const axisValue = this.#values[axis];
|
|
408
409
|
const mapping = mappings[axis];
|
|
409
|
-
|
|
410
|
+
resnap(this.#values[axis], mapping, this.numberAttribute(axis, mapping.bounds[0]));
|
|
410
411
|
}
|
|
411
412
|
this.render();
|
|
412
413
|
}
|
|
@@ -454,15 +455,6 @@ var SonicXy = class extends SonicFormElement {
|
|
|
454
455
|
y: this.yDefault
|
|
455
456
|
});
|
|
456
457
|
}
|
|
457
|
-
#set(axis, next) {
|
|
458
|
-
if (!Number.isFinite(next)) return false;
|
|
459
|
-
const axisValue = this.#values[axis];
|
|
460
|
-
const snapped = this.#mappings()[axis].snap(next);
|
|
461
|
-
axisValue.asked = snapped;
|
|
462
|
-
if (snapped === axisValue.value) return false;
|
|
463
|
-
axisValue.value = snapped;
|
|
464
|
-
return true;
|
|
465
|
-
}
|
|
466
458
|
#taper(axis) {
|
|
467
459
|
return this.getAttribute(`${axis}-taper`) === "log" ? "log" : "linear";
|
|
468
460
|
}
|
|
@@ -477,10 +469,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
477
469
|
this.#refresh();
|
|
478
470
|
return;
|
|
479
471
|
}
|
|
480
|
-
|
|
481
|
-
const hasMoved = this.#set(axis, asked);
|
|
482
|
-
if (Number.isFinite(asked)) axisValue.asked = asked;
|
|
483
|
-
if (hasMoved) this.render();
|
|
472
|
+
if (setAsked(axisValue, this.#mappings()[axis], toNumber(next))) this.render();
|
|
484
473
|
}
|
|
485
474
|
};
|
|
486
475
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/lib/asked-value.ts
|
|
2
|
+
function moveTo(cell, mapping, next) {
|
|
3
|
+
if (!Number.isFinite(next)) return false;
|
|
4
|
+
const snapped = mapping.snap(next);
|
|
5
|
+
cell.asked = snapped;
|
|
6
|
+
if (snapped === cell.value) return false;
|
|
7
|
+
cell.value = snapped;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function setAsked(cell, mapping, next) {
|
|
11
|
+
if (!Number.isFinite(next)) return false;
|
|
12
|
+
const hasMoved = moveTo(cell, mapping, next);
|
|
13
|
+
cell.asked = next;
|
|
14
|
+
return hasMoved;
|
|
15
|
+
}
|
|
16
|
+
function resnap(cell, mapping, fallback) {
|
|
17
|
+
cell.value = mapping.snap(cell.asked ?? fallback);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { moveTo, resnap, setAsked };
|
|
@@ -14,6 +14,9 @@ function readEach(properties, read) {
|
|
|
14
14
|
return Object.fromEntries(Object.entries(properties).flatMap(([name, property]) => property === void 0 ? [] : [[name, read(property)]]));
|
|
15
15
|
}
|
|
16
16
|
var CanvasSurface = class {
|
|
17
|
+
get isVisible() {
|
|
18
|
+
return this.#isVisible;
|
|
19
|
+
}
|
|
17
20
|
get size() {
|
|
18
21
|
return this.#size;
|
|
19
22
|
}
|
|
@@ -11,7 +11,7 @@ function parkedPlacement(labels, index, [view, arrivingX]) {
|
|
|
11
11
|
const parked = labels[index];
|
|
12
12
|
if (!parked) return void 0;
|
|
13
13
|
const next = labels[index + 1];
|
|
14
|
-
const x = Math.max(view.
|
|
14
|
+
const x = Math.max(view.parkPx, lineX(parked.start, view) + view.insetPx);
|
|
15
15
|
const approachPx = next ? lineX(next.start, view) - lineX(view.playheadSeconds, view) : Infinity;
|
|
16
16
|
return {
|
|
17
17
|
index,
|
|
@@ -90,11 +90,12 @@ function measureWidths(control, className, texts) {
|
|
|
90
90
|
return widths;
|
|
91
91
|
}
|
|
92
92
|
function createLabelRider(control, options) {
|
|
93
|
-
const { className, colourProperty, fadeProperty, insetProperty } = options;
|
|
93
|
+
const { className, colourProperty, fadeProperty, insetProperty, parkProperty } = options;
|
|
94
94
|
const parked = labelSlot(requireChild(control, `.${className}`, HTMLElement));
|
|
95
95
|
const arriving = labelSlot(requireChild(control, `.${className} + .${className}`, HTMLElement));
|
|
96
96
|
let fadeShare = .25;
|
|
97
97
|
let insetPx = 0;
|
|
98
|
+
let parkPx = 0;
|
|
98
99
|
let measured = [];
|
|
99
100
|
return {
|
|
100
101
|
measure: (labels) => {
|
|
@@ -102,6 +103,7 @@ function createLabelRider(control, options) {
|
|
|
102
103
|
const styles = getComputedStyle(control);
|
|
103
104
|
fadeShare = readPxProperty(styles, fadeProperty, .25);
|
|
104
105
|
insetPx = readPxProperty(styles, insetProperty, 0);
|
|
106
|
+
parkPx = readPxProperty(styles, parkProperty, 0);
|
|
105
107
|
measured = labels.map((label, index) => ({
|
|
106
108
|
...label,
|
|
107
109
|
widthPx: widths[index] ?? 0
|
|
@@ -111,7 +113,8 @@ function createLabelRider(control, options) {
|
|
|
111
113
|
const layout = layoutRider(measured, {
|
|
112
114
|
...window,
|
|
113
115
|
fadeShare,
|
|
114
|
-
insetPx
|
|
116
|
+
insetPx,
|
|
117
|
+
parkPx
|
|
115
118
|
});
|
|
116
119
|
writeLabel(parked, [measured, colourProperty], layout.parked);
|
|
117
120
|
writeLabel(arriving, [measured, colourProperty], layout.arriving);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/lib/read-ms-property.ts
|
|
2
|
+
function readMsProperty(styles, property, fallback) {
|
|
3
|
+
const text = styles.getPropertyValue(property).trim();
|
|
4
|
+
const parsed = Number.parseFloat(text);
|
|
5
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
6
|
+
return text.endsWith("ms") ? parsed : parsed * 1e3;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { readMsProperty };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { requireChild, template } from "../lib/render.js";
|
|
2
|
+
import { createModulation } from "./modulation.js";
|
|
2
3
|
import { SonicValueElement } from "./value-element.js";
|
|
3
4
|
import { bindScale } from "./scale.js";
|
|
4
5
|
|
|
@@ -43,12 +44,15 @@ var SonicDial = class extends SonicValueElement {
|
|
|
43
44
|
this.reflect("modulation", amount);
|
|
44
45
|
}
|
|
45
46
|
get modulationValue() {
|
|
46
|
-
return this.
|
|
47
|
+
return this.#modulation.value();
|
|
47
48
|
}
|
|
48
49
|
set modulationValue(value) {
|
|
49
|
-
this.
|
|
50
|
+
this.#modulation.write(value, this.isBound() ? this.mapping() : void 0);
|
|
50
51
|
}
|
|
51
52
|
#dial = renderDial();
|
|
53
|
+
#modulation = createModulation(this.#dial, "dial", (isModulated) => {
|
|
54
|
+
this.toggleState("modulated", isModulated);
|
|
55
|
+
});
|
|
52
56
|
connectedCallback() {
|
|
53
57
|
this.upgradeProperties("endless", "modulationValue", "modulation");
|
|
54
58
|
super.connectedCallback();
|
|
@@ -67,7 +71,12 @@ var SonicDial = class extends SonicValueElement {
|
|
|
67
71
|
}));
|
|
68
72
|
}
|
|
69
73
|
draw() {
|
|
70
|
-
this.
|
|
74
|
+
this.#modulation.draw({
|
|
75
|
+
mapping: this.mapping(),
|
|
76
|
+
modulation: this.endless ? 0 : this.modulation,
|
|
77
|
+
origin: this.originValue(),
|
|
78
|
+
value: this.value
|
|
79
|
+
});
|
|
71
80
|
}
|
|
72
81
|
focusTarget() {
|
|
73
82
|
return this.#dial;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.
|
|
1
|
+
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\treadonly #dial = renderDial();\n\n\treadonly #modulation = createModulation(this.#dial, 'dial', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(dial, 'dial.css');\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.endless ? 0 : this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n}\n"],"mappings":";;;;;;AAWA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;CAGA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;CAGA,IAAI,kBAAsC;EACzC,OAAO,KAAK,YAAY,MAAM;CAC/B;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAS;CAC1E;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,cAAc,iBAAiB,KAAK,OAAO,SAAS,gBAAgB;EAC5E,KAAK,YAAY,aAAa,WAAW;CAC1C,CAAC;CAED,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EACZ,KAAK,YAAY,MAAM,UAAU;EACjC,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,YAAY,KAAK;GACrB,SAAS,KAAK,QAAQ;GACtB,YAAY,KAAK,UAAU,IAAI,KAAK;GACpC,QAAQ,KAAK,YAAY;GACzB,OAAO,KAAK;EACb,CAAC;CACF;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;AACD"}
|
|
@@ -2,8 +2,8 @@ import { writeAttribute } from "../lib/write-attribute.js";
|
|
|
2
2
|
import { SonicElement } from "./sonic-element.js";
|
|
3
3
|
import { requireChild, template } from "../lib/render.js";
|
|
4
4
|
import { bindDrag } from "../lib/pointer-drag.js";
|
|
5
|
-
import { ReadoutClaim } from "./readout-claim.js";
|
|
6
5
|
import { Readout } from "./readout.js";
|
|
6
|
+
import { ReadoutClaim, revealDelay } from "./readout-claim.js";
|
|
7
7
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
8
8
|
import { SonicValueElement, linkValue } from "./value-element.js";
|
|
9
9
|
import { adsrPath, adsrShape, adsrStages, timeStages } from "../lib/adsr-shape.js";
|
|
@@ -269,7 +269,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
269
269
|
const pressed = this.#pressed(event);
|
|
270
270
|
if (!pressed || pressed.drives.length === 0) return void 0;
|
|
271
271
|
const { drives, isFlipped, part } = pressed;
|
|
272
|
-
this.#claim.press(true);
|
|
272
|
+
this.#claim.press(true, revealDelay(this.#envelope));
|
|
273
273
|
this.#held = part;
|
|
274
274
|
this.#drawBracket();
|
|
275
275
|
const box = this.#graph.getBoundingClientRect();
|