@xsynaptic/sonic-ui 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/default/elements/dial.js +12 -3
- package/dist/default/elements/display-element.js +14 -2
- 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 +16 -18
- package/dist/default/elements/spectrum.js +1 -0
- package/dist/default/elements/value-element.js +108 -330
- package/dist/default/elements/value-gestures.js +301 -0
- package/dist/default/elements/wave-element.js +5 -26
- package/dist/default/elements/waveform.js +6 -2
- package/dist/default/elements/wavestrip.js +5 -16
- 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/fill-watch.js +29 -0
- package/dist/default/lib/hover-preview.js +8 -3
- 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/display-element.d.ts +4 -1
- package/dist/dev/elements/display-element.js +14 -2
- package/dist/dev/elements/display-element.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 +16 -18
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/spectrum.d.ts +1 -0
- package/dist/dev/elements/spectrum.js +1 -0
- package/dist/dev/elements/spectrum.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +11 -13
- package/dist/dev/elements/value-element.js +108 -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/wave-element.js +5 -26
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.js +6 -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 -16
- 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/fill-watch.js +30 -0
- package/dist/dev/lib/fill-watch.js.map +1 -0
- package/dist/dev/lib/hover-preview.js +8 -3
- package/dist/dev/lib/hover-preview.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/envelope.css +1 -1
- package/dist/styles/led.css +1 -1
- package/dist/styles/material.css +42 -8
- package/dist/styles/spectrum.css +5 -0
- package/dist/styles/switch.css +42 -33
- package/dist/styles/waveform.css +31 -2
- package/dist/styles/wavestrip.css +13 -7
- package/dist/styles/xy.css +4 -4
- 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 };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SonicValueElement } from "./value-element.js";
|
|
2
2
|
import { bindSurface } from "../lib/canvas-surface.js";
|
|
3
|
+
import { bindFill } from "../lib/fill-watch.js";
|
|
3
4
|
|
|
4
5
|
//#region src/elements/wave-element.ts
|
|
5
6
|
function markerKey(marker) {
|
|
@@ -31,12 +32,12 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
31
32
|
this.render();
|
|
32
33
|
}
|
|
33
34
|
#current;
|
|
34
|
-
#fillWatch;
|
|
35
35
|
#markers = [];
|
|
36
36
|
#surface;
|
|
37
|
+
#syncFill;
|
|
37
38
|
attributeChangedCallback(name) {
|
|
38
39
|
super.attributeChangedCallback(name);
|
|
39
|
-
if (name === "fill") this.#
|
|
40
|
+
if (name === "fill") this.#syncFill?.(this.fill);
|
|
40
41
|
}
|
|
41
42
|
connectedCallback() {
|
|
42
43
|
this.upgradeProperties("fill", "markers");
|
|
@@ -63,11 +64,8 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
63
64
|
});
|
|
64
65
|
this.render();
|
|
65
66
|
this.checkStyles(control, this.sheet);
|
|
66
|
-
this.#
|
|
67
|
-
|
|
68
|
-
this.#fillWatch?.disconnect();
|
|
69
|
-
this.#fillWatch = void 0;
|
|
70
|
-
}, { once: true });
|
|
67
|
+
this.#syncFill = bindFill(control, this.sizeProperty, signal);
|
|
68
|
+
this.#syncFill(this.fill);
|
|
71
69
|
}
|
|
72
70
|
focusTarget() {
|
|
73
71
|
return this.control;
|
|
@@ -105,25 +103,6 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
105
103
|
surface() {
|
|
106
104
|
return this.#surface;
|
|
107
105
|
}
|
|
108
|
-
#watchFill() {
|
|
109
|
-
const control = this.control;
|
|
110
|
-
const isFilling = this.fill && this.isBound();
|
|
111
|
-
if (isFilling === (this.#fillWatch !== void 0)) return;
|
|
112
|
-
this.#fillWatch?.disconnect();
|
|
113
|
-
this.#fillWatch = void 0;
|
|
114
|
-
if (!isFilling) {
|
|
115
|
-
control.style.removeProperty(this.sizeProperty);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
this.#fillWatch = new ResizeObserver((entries) => {
|
|
119
|
-
const box = entries.at(-1)?.borderBoxSize[0];
|
|
120
|
-
if (!box) return;
|
|
121
|
-
requestAnimationFrame(() => {
|
|
122
|
-
if (this.#fillWatch) control.style.setProperty(this.sizeProperty, `${String(box.blockSize)}px`);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
this.#fillWatch.observe(control);
|
|
126
|
-
}
|
|
127
106
|
};
|
|
128
107
|
|
|
129
108
|
//#endregion
|
|
@@ -25,6 +25,7 @@ const placeholderAmplitude = .2;
|
|
|
25
25
|
const renderWaveform = template(`
|
|
26
26
|
<div class="sonic-waveform" role="slider" tabindex="0">
|
|
27
27
|
<canvas class="sonic-waveform-canvas" aria-hidden="true"></canvas>
|
|
28
|
+
<div class="sonic-waveform-scrim" aria-hidden="true"></div>
|
|
28
29
|
<div class="sonic-waveform-ghost" aria-hidden="true" hidden></div>
|
|
29
30
|
<div class="sonic-waveform-playhead" aria-hidden="true"></div>
|
|
30
31
|
<div class="sonic-waveform-label" aria-hidden="true" hidden></div>
|
|
@@ -209,7 +210,8 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
209
210
|
className: "sonic-waveform-label",
|
|
210
211
|
colourProperty: "--_sonic-waveform-marker",
|
|
211
212
|
fadeProperty: "--_sonic-waveform-label-fade",
|
|
212
|
-
insetProperty: "--_sonic-waveform-label-inset"
|
|
213
|
+
insetProperty: "--_sonic-waveform-label-inset",
|
|
214
|
+
parkProperty: "--_sonic-waveform-label-park"
|
|
213
215
|
});
|
|
214
216
|
connectedCallback() {
|
|
215
217
|
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "requestPeaks", "zoom");
|
|
@@ -223,7 +225,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
223
225
|
}, { signal });
|
|
224
226
|
}
|
|
225
227
|
draw() {
|
|
226
|
-
this.surface()
|
|
228
|
+
const surface = this.surface();
|
|
229
|
+
surface?.requestFrame();
|
|
230
|
+
if (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());
|
|
227
231
|
}
|
|
228
232
|
isEmpty() {
|
|
229
233
|
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,10 @@ 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
|
|
91
|
-
|
|
92
|
-
dismiss: () => {
|
|
93
|
-
this.render();
|
|
94
|
-
},
|
|
95
|
-
isTaken: () => this.isEditing() || this.isRevealed(),
|
|
96
|
-
show: () => {
|
|
97
|
-
this.renderReadout();
|
|
89
|
+
this.bindHover(control, signal, {
|
|
90
|
+
place: () => {
|
|
98
91
|
this.#placeReadout();
|
|
99
92
|
},
|
|
100
93
|
valueAt: (event) => {
|
|
@@ -103,12 +96,11 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
103
96
|
const mapping = this.mapping();
|
|
104
97
|
return this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));
|
|
105
98
|
}
|
|
106
|
-
}
|
|
107
|
-
this.bindGestures(
|
|
99
|
+
});
|
|
100
|
+
this.bindGestures(control, signal, (event) => this.#grab(event));
|
|
108
101
|
}
|
|
109
102
|
draw() {
|
|
110
103
|
const surface = this.surface();
|
|
111
|
-
if (this.isEditing() || this.isRevealed()) this.#hover?.clear();
|
|
112
104
|
this.#placeReadout();
|
|
113
105
|
this.showCurrentMarker(this.playback());
|
|
114
106
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
@@ -131,9 +123,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
131
123
|
look.colours[kind]
|
|
132
124
|
]);
|
|
133
125
|
}
|
|
134
|
-
previewValue() {
|
|
135
|
-
return this.#hover?.value();
|
|
136
|
-
}
|
|
137
126
|
renderMarkers() {
|
|
138
127
|
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
139
128
|
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/lib/fill-watch.ts
|
|
2
|
+
function bindFill(control, sizeProperty, signal) {
|
|
3
|
+
let watch;
|
|
4
|
+
const stop = () => {
|
|
5
|
+
watch?.disconnect();
|
|
6
|
+
watch = void 0;
|
|
7
|
+
};
|
|
8
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
9
|
+
return (isFilling) => {
|
|
10
|
+
if (signal.aborted || isFilling === (watch !== void 0)) return;
|
|
11
|
+
stop();
|
|
12
|
+
if (!isFilling) {
|
|
13
|
+
control.style.removeProperty(sizeProperty);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const started = new ResizeObserver((entries) => {
|
|
17
|
+
const box = entries.at(-1)?.borderBoxSize[0];
|
|
18
|
+
if (!box) return;
|
|
19
|
+
requestAnimationFrame(() => {
|
|
20
|
+
if (watch === started) control.style.setProperty(sizeProperty, `${String(box.blockSize)}px`);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
watch = started;
|
|
24
|
+
started.observe(control);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { bindFill };
|
|
@@ -3,6 +3,11 @@ function bindHoverPreview(target, options, signal) {
|
|
|
3
3
|
let hover;
|
|
4
4
|
let isDismissed = false;
|
|
5
5
|
let escapeWatch;
|
|
6
|
+
const hold = (value) => {
|
|
7
|
+
if (value === hover) return;
|
|
8
|
+
hover = value;
|
|
9
|
+
options.changed();
|
|
10
|
+
};
|
|
6
11
|
const watchEscape = (isHovered) => {
|
|
7
12
|
if (!isHovered) {
|
|
8
13
|
escapeWatch?.abort();
|
|
@@ -13,14 +18,14 @@ function bindHoverPreview(target, options, signal) {
|
|
|
13
18
|
escapeWatch = new AbortController();
|
|
14
19
|
target.ownerDocument.addEventListener("keydown", (event) => {
|
|
15
20
|
if (hover === void 0 || event.key !== "Escape") return;
|
|
16
|
-
hover = void 0;
|
|
17
21
|
isDismissed = true;
|
|
22
|
+
hold(void 0);
|
|
18
23
|
options.dismiss();
|
|
19
24
|
}, { signal: escapeWatch.signal });
|
|
20
25
|
};
|
|
21
26
|
const hoverAt = (value) => {
|
|
22
27
|
if (value === void 0) isDismissed = false;
|
|
23
|
-
if (!isDismissed && !options.isTaken())
|
|
28
|
+
if (!isDismissed && !options.isTaken()) hold(value);
|
|
24
29
|
options.show();
|
|
25
30
|
watchEscape(value !== void 0);
|
|
26
31
|
};
|
|
@@ -36,7 +41,7 @@ function bindHoverPreview(target, options, signal) {
|
|
|
36
41
|
}, { once: true });
|
|
37
42
|
return {
|
|
38
43
|
clear: () => {
|
|
39
|
-
|
|
44
|
+
hold(void 0);
|
|
40
45
|
},
|
|
41
46
|
value: () => hover
|
|
42
47
|
};
|
|
@@ -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 };
|