@xsynaptic/sonic-ui 0.17.0 → 0.19.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/CHANGELOG.md +28 -0
- package/README.md +122 -19
- package/dist/default/elements/button.js +2 -5
- package/dist/default/elements/dial.js +2 -7
- package/dist/default/elements/display-element.js +9 -12
- package/dist/default/elements/envelope.js +31 -78
- package/dist/default/elements/field-gesture.js +81 -0
- package/dist/default/elements/meter.js +4 -1
- package/dist/default/elements/number.js +0 -7
- package/dist/default/elements/position-group.js +42 -26
- package/dist/default/elements/radio-group.js +59 -56
- package/dist/default/elements/scale.js +3 -3
- package/dist/default/elements/segmented.js +0 -5
- package/dist/default/elements/slider.js +11 -20
- package/dist/default/elements/sonic-element.js +3 -1
- package/dist/default/elements/split.js +14 -13
- package/dist/default/elements/switch.js +31 -47
- package/dist/default/elements/toggle.js +20 -49
- package/dist/default/elements/value-element.js +72 -94
- package/dist/default/elements/value-gestures.js +22 -22
- package/dist/default/elements/wave-element.js +16 -20
- package/dist/default/elements/waveform.js +42 -241
- package/dist/default/elements/wavestrip.js +10 -5
- package/dist/default/elements/xy.js +67 -119
- package/dist/default/index.js +2 -1
- package/dist/default/lib/canvas-surface.js +25 -0
- package/dist/default/lib/clock.js +23 -0
- package/dist/default/lib/crossfade.js +1 -0
- package/dist/default/lib/envelope-curve.js +1 -0
- package/dist/default/lib/peaks-request.js +44 -0
- package/dist/default/lib/positions.js +58 -0
- package/dist/default/lib/scrub.js +12 -0
- package/dist/default/lib/spoken-duration.js +30 -0
- package/dist/default/lib/value-model.js +62 -0
- package/dist/default/lib/waveform-buckets.js +7 -1
- package/dist/default/lib/waveform-scene.js +108 -0
- package/dist/default/lib/zoom-gesture.js +97 -1
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +14 -5
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +2 -0
- package/dist/dev/elements/dial.js +16 -7
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/display-element.d.ts +5 -3
- package/dist/dev/elements/display-element.js +11 -9
- package/dist/dev/elements/display-element.js.map +1 -1
- package/dist/dev/elements/envelope.js +48 -78
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +82 -0
- package/dist/dev/elements/field-gesture.js.map +1 -0
- package/dist/dev/elements/meter.d.ts +10 -5
- package/dist/dev/elements/meter.js +5 -1
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.js +13 -7
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/position-group.d.ts +12 -6
- package/dist/dev/elements/position-group.js +42 -26
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +11 -7
- package/dist/dev/elements/radio-group.js +59 -56
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/scale.js +3 -3
- package/dist/dev/elements/scale.js.map +1 -1
- package/dist/dev/elements/segmented.js +12 -5
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +25 -20
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +13 -2
- package/dist/dev/elements/sonic-element.js +63 -11
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.d.ts +1 -0
- package/dist/dev/elements/split.js +14 -13
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +2 -2
- package/dist/dev/elements/switch.js +38 -47
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/toggle.js +32 -49
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +11 -5
- package/dist/dev/elements/value-element.js +72 -94
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +22 -22
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +8 -5
- package/dist/dev/elements/wave-element.js +30 -20
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +20 -22
- package/dist/dev/elements/waveform.js +42 -241
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +5 -1
- package/dist/dev/elements/wavestrip.js +10 -5
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +81 -119
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +5 -3
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +25 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/clock.d.ts +5 -0
- package/dist/dev/lib/clock.js +24 -0
- package/dist/dev/lib/clock.js.map +1 -0
- package/dist/dev/lib/crossfade.d.ts +1 -0
- package/dist/dev/lib/crossfade.js +1 -0
- package/dist/dev/lib/crossfade.js.map +1 -1
- package/dist/dev/lib/envelope-curve.d.ts +1 -0
- package/dist/dev/lib/envelope-curve.js +1 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -1
- package/dist/dev/lib/mirror-children.js.map +1 -1
- package/dist/dev/lib/owned-control.js.map +1 -1
- package/dist/dev/lib/peaks-request.d.ts +5 -0
- package/dist/dev/lib/peaks-request.js +45 -0
- package/dist/dev/lib/peaks-request.js.map +1 -0
- package/dist/dev/lib/positions.d.ts +7 -0
- package/dist/dev/lib/positions.js +59 -0
- package/dist/dev/lib/positions.js.map +1 -0
- package/dist/dev/lib/scrub.d.ts +8 -0
- package/dist/dev/lib/scrub.js +13 -0
- package/dist/dev/lib/scrub.js.map +1 -0
- package/dist/dev/lib/spoken-duration.js +31 -0
- package/dist/dev/lib/spoken-duration.js.map +1 -0
- package/dist/dev/lib/time-regions.d.ts +1 -0
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/dev/lib/units.d.ts +8 -0
- package/dist/dev/lib/value-model.js +63 -0
- package/dist/dev/lib/value-model.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +2 -1
- package/dist/dev/lib/waveform-buckets.js +7 -1
- package/dist/dev/lib/waveform-buckets.js.map +1 -1
- package/dist/dev/lib/waveform-scene.d.ts +11 -0
- package/dist/dev/lib/waveform-scene.js +109 -0
- package/dist/dev/lib/waveform-scene.js.map +1 -0
- package/dist/dev/lib/zoom-gesture.js +97 -1
- package/dist/dev/lib/zoom-gesture.js.map +1 -1
- package/dist/skins/amber.css +1 -0
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +1 -0
- package/dist/skins/slate.css +1 -0
- package/dist/styles/button.css +45 -11
- package/dist/styles/dial.css +10 -9
- package/dist/styles/envelope.css +10 -4
- package/dist/styles/led.css +5 -1
- package/dist/styles/material/arc.css +3 -2
- package/dist/styles/material/bracket.css +2 -2
- package/dist/styles/material/cap.css +33 -12
- package/dist/styles/material/core.css +4 -0
- package/dist/styles/material/glass.css +1 -1
- package/dist/styles/material/groove.css +4 -3
- package/dist/styles/material/keycap.css +3 -2
- package/dist/styles/material/lens.css +1 -1
- package/dist/styles/material/pane.css +5 -4
- package/dist/styles/material/readout.css +6 -6
- package/dist/styles/material/well.css +2 -2
- package/dist/styles/meter.css +2 -7
- package/dist/styles/number.css +5 -1
- package/dist/styles/panel.css +4 -3
- package/dist/styles/ring.css +1 -1
- package/dist/styles/screen.css +5 -1
- package/dist/styles/segmented.css +17 -4
- package/dist/styles/slider.css +9 -3
- package/dist/styles/spectrum.css +16 -3
- package/dist/styles/switch.css +35 -10
- package/dist/styles/toggle.css +14 -7
- package/dist/styles/waveform.css +17 -5
- package/dist/styles/wavestrip.css +11 -3
- package/dist/styles/xy.css +9 -5
- package/package.json +1 -6
- package/dist/default/dat.js +0 -51
- package/dist/default/lib/asked-value.js +0 -21
- package/dist/default/lib/fill-watch.js +0 -29
- package/dist/dev/dat.d.ts +0 -19
- package/dist/dev/dat.js +0 -52
- package/dist/dev/dat.js.map +0 -1
- package/dist/dev/lib/asked-value.js +0 -22
- package/dist/dev/lib/asked-value.js.map +0 -1
- package/dist/dev/lib/fill-watch.js +0 -30
- package/dist/dev/lib/fill-watch.js.map +0 -1
|
@@ -14,7 +14,7 @@ const detentZonePx = 8;
|
|
|
14
14
|
var ValueGestures = class {
|
|
15
15
|
#claim = new ReadoutClaim(() => {
|
|
16
16
|
this.#showRevealed();
|
|
17
|
-
this.#host.
|
|
17
|
+
this.#host.scrubChanged();
|
|
18
18
|
});
|
|
19
19
|
#control;
|
|
20
20
|
#entry;
|
|
@@ -53,7 +53,7 @@ var ValueGestures = class {
|
|
|
53
53
|
this.#pointerType = void 0;
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
const fromValue = host.value
|
|
56
|
+
const fromValue = host.model.value;
|
|
57
57
|
host.toggleState("dragging", true);
|
|
58
58
|
const axis = grab(event);
|
|
59
59
|
if (!axis) {
|
|
@@ -103,9 +103,6 @@ var ValueGestures = class {
|
|
|
103
103
|
this.#pointerDrag?.end();
|
|
104
104
|
this.#endKeyScrub();
|
|
105
105
|
}
|
|
106
|
-
heldFrom() {
|
|
107
|
-
return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
|
|
108
|
-
}
|
|
109
106
|
isEditing() {
|
|
110
107
|
return this.#entry?.isOpen === true;
|
|
111
108
|
}
|
|
@@ -118,6 +115,9 @@ var ValueGestures = class {
|
|
|
118
115
|
pointerType() {
|
|
119
116
|
return this.#pointerType;
|
|
120
117
|
}
|
|
118
|
+
scrubFrom() {
|
|
119
|
+
return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
|
|
120
|
+
}
|
|
121
121
|
#bindEntry(signal) {
|
|
122
122
|
const control = this.#control;
|
|
123
123
|
const host = this.#host;
|
|
@@ -156,13 +156,13 @@ var ValueGestures = class {
|
|
|
156
156
|
this.#claim.reveal("keys");
|
|
157
157
|
this.#showRevealed();
|
|
158
158
|
this.#keyTo(event, next);
|
|
159
|
-
host.
|
|
159
|
+
host.scrubChanged();
|
|
160
160
|
}, { signal });
|
|
161
161
|
control.addEventListener("keyup", (event) => {
|
|
162
162
|
const isMeta = event.key === "Meta";
|
|
163
163
|
if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
|
|
164
164
|
if (isMeta && this.#dragging()) return;
|
|
165
|
-
if (isMeta || host.mapping().keyTarget(event.key, host.value
|
|
165
|
+
if (isMeta || host.model.mapping().keyTarget(event.key, host.model.value) !== void 0) this.#springBack();
|
|
166
166
|
}, { signal });
|
|
167
167
|
control.addEventListener("blur", () => {
|
|
168
168
|
this.#endKeyScrub();
|
|
@@ -172,13 +172,13 @@ var ValueGestures = class {
|
|
|
172
172
|
}
|
|
173
173
|
#commit(next, direction = 0) {
|
|
174
174
|
const host = this.#host;
|
|
175
|
-
const landed = Number.isFinite(next) ? host.land(next, direction) : next;
|
|
175
|
+
const landed = Number.isFinite(next) ? host.model.land(next, direction) : next;
|
|
176
176
|
if (host.input(landed)) host.dispatch("change");
|
|
177
177
|
}
|
|
178
178
|
#concealKeyReveal() {
|
|
179
179
|
if (!this.#claim.conceal("keys")) return;
|
|
180
180
|
this.#showRevealed();
|
|
181
|
-
this.#host.
|
|
181
|
+
this.#host.scrubChanged();
|
|
182
182
|
}
|
|
183
183
|
#crossCancelZone(drag, value) {
|
|
184
184
|
const host = this.#host;
|
|
@@ -196,7 +196,7 @@ var ValueGestures = class {
|
|
|
196
196
|
const host = this.#host;
|
|
197
197
|
const isOutside = drag.outside?.(event) === true;
|
|
198
198
|
const wasEngaged = drag.state.isEngaged;
|
|
199
|
-
const mapping = host.mapping();
|
|
199
|
+
const mapping = host.model.mapping();
|
|
200
200
|
const { state, value } = stepDrag(mapping, drag.state, {
|
|
201
201
|
isFine: event.shiftKey,
|
|
202
202
|
isOutside,
|
|
@@ -213,13 +213,13 @@ var ValueGestures = class {
|
|
|
213
213
|
const scrub = this.#keyScrub;
|
|
214
214
|
if (!scrub) return;
|
|
215
215
|
this.#keyScrub = void 0;
|
|
216
|
-
host.
|
|
217
|
-
if (host.value
|
|
216
|
+
host.scrubChanged();
|
|
217
|
+
if (host.model.value !== scrub.fromValue) host.dispatch("change");
|
|
218
218
|
}
|
|
219
219
|
#keyTarget(key) {
|
|
220
220
|
const host = this.#host;
|
|
221
221
|
if (resetKeys.has(key)) return host.default();
|
|
222
|
-
return host.mapping().keyTarget(key, host.value
|
|
222
|
+
return host.model.mapping().keyTarget(key, host.model.value, host.keyStep());
|
|
223
223
|
}
|
|
224
224
|
#keyTo(event, next) {
|
|
225
225
|
const host = this.#host;
|
|
@@ -228,7 +228,7 @@ var ValueGestures = class {
|
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
230
|
this.#endKeyScrub();
|
|
231
|
-
this.#commit(next, next > host.value
|
|
231
|
+
this.#commit(next, next > host.model.value ? 1 : -1);
|
|
232
232
|
}
|
|
233
233
|
#lift(drag, event, entry) {
|
|
234
234
|
const host = this.#host;
|
|
@@ -248,16 +248,16 @@ var ValueGestures = class {
|
|
|
248
248
|
clearTimeout(drag.revealTimer);
|
|
249
249
|
this.#claim.conceal("drag");
|
|
250
250
|
host.toggleState("cancelling", false);
|
|
251
|
-
host.
|
|
251
|
+
host.scrubChanged();
|
|
252
252
|
this.#showRevealed();
|
|
253
|
-
if (host.value
|
|
253
|
+
if (host.model.value !== drag.fromValue) host.dispatch("change");
|
|
254
254
|
this.#springBack();
|
|
255
255
|
this.#pointerType = void 0;
|
|
256
256
|
}
|
|
257
257
|
#renderDragReveal(drag) {
|
|
258
258
|
if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
|
|
259
259
|
else this.#claim.conceal("drag");
|
|
260
|
-
this.#host.
|
|
260
|
+
this.#host.scrubChanged();
|
|
261
261
|
this.#showRevealed();
|
|
262
262
|
}
|
|
263
263
|
#reset() {
|
|
@@ -273,7 +273,7 @@ var ValueGestures = class {
|
|
|
273
273
|
if (this.#keyScrub?.key !== key) {
|
|
274
274
|
this.#endKeyScrub();
|
|
275
275
|
this.#keyScrub = {
|
|
276
|
-
fromValue: host.value
|
|
276
|
+
fromValue: host.model.value,
|
|
277
277
|
key
|
|
278
278
|
};
|
|
279
279
|
}
|
|
@@ -290,14 +290,14 @@ var ValueGestures = class {
|
|
|
290
290
|
const host = this.#host;
|
|
291
291
|
const target = host.springTarget();
|
|
292
292
|
if (target === void 0) return;
|
|
293
|
-
if (host.mapping().snap(target) !== host.value
|
|
293
|
+
if (host.model.mapping().snap(target) !== host.model.value) host.toggleState("springing", true);
|
|
294
294
|
this.#commit(target);
|
|
295
295
|
}
|
|
296
296
|
#startDrag(axis, event) {
|
|
297
297
|
const host = this.#host;
|
|
298
|
-
const mapping = host.mapping();
|
|
298
|
+
const mapping = host.model.mapping();
|
|
299
299
|
const detent = host.detent();
|
|
300
|
-
const value = host.value
|
|
300
|
+
const value = host.model.value;
|
|
301
301
|
const start = {
|
|
302
302
|
from: value,
|
|
303
303
|
position: axis.position(event),
|
|
@@ -316,7 +316,7 @@ var ValueGestures = class {
|
|
|
316
316
|
});
|
|
317
317
|
}
|
|
318
318
|
#withinLimit(mapping, state) {
|
|
319
|
-
const limit = this.#host.limit();
|
|
319
|
+
const limit = this.#host.model.limit();
|
|
320
320
|
if (!limit || mapping.isWrapping) return state;
|
|
321
321
|
const [low, high] = limit;
|
|
322
322
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SonicValueElement } from "./value-element.js";
|
|
2
2
|
import { bindSurface } from "../lib/canvas-surface.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatClock, parseClock } from "../lib/clock.js";
|
|
4
|
+
import { spokenDuration } from "../lib/spoken-duration.js";
|
|
4
5
|
|
|
5
6
|
//#region src/elements/wave-element.ts
|
|
6
7
|
function markerKey(marker) {
|
|
@@ -31,13 +32,17 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
31
32
|
this.renderMarkers();
|
|
32
33
|
this.render();
|
|
33
34
|
}
|
|
35
|
+
notation = {
|
|
36
|
+
format: formatClock,
|
|
37
|
+
parse: parseClock,
|
|
38
|
+
speak: (seconds) => spokenDuration(seconds, this.closest("[lang]")?.lang)
|
|
39
|
+
};
|
|
34
40
|
#current;
|
|
35
41
|
#markers = [];
|
|
36
42
|
#surface;
|
|
37
|
-
#syncFill;
|
|
38
43
|
attributeChangedCallback(name) {
|
|
39
44
|
super.attributeChangedCallback(name);
|
|
40
|
-
if (name === "fill") this.#
|
|
45
|
+
if (name === "fill") this.#surface?.setFill(this.fill);
|
|
41
46
|
}
|
|
42
47
|
connectedCallback() {
|
|
43
48
|
this.upgradeProperties("fill", "markers");
|
|
@@ -53,6 +58,10 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
53
58
|
this.#surface = bindSurface({
|
|
54
59
|
canvas: this.canvas,
|
|
55
60
|
colours: this.colours,
|
|
61
|
+
fill: {
|
|
62
|
+
control,
|
|
63
|
+
sizeProperty: this.sizeProperty
|
|
64
|
+
},
|
|
56
65
|
lengths: this.lengths,
|
|
57
66
|
paint: (context, frame) => {
|
|
58
67
|
this.paint(context, frame);
|
|
@@ -63,27 +72,11 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
63
72
|
signal
|
|
64
73
|
});
|
|
65
74
|
this.render();
|
|
66
|
-
this.
|
|
67
|
-
"cap",
|
|
68
|
-
"glass",
|
|
69
|
-
"pane",
|
|
70
|
-
"well",
|
|
71
|
-
"readout",
|
|
72
|
-
"value"
|
|
73
|
-
]);
|
|
74
|
-
this.#syncFill = bindFill(control, this.sizeProperty, signal);
|
|
75
|
-
this.#syncFill(this.fill);
|
|
75
|
+
this.#surface.setFill(this.fill);
|
|
76
76
|
}
|
|
77
77
|
focusTarget() {
|
|
78
78
|
return this.control;
|
|
79
79
|
}
|
|
80
|
-
heldWrite(next) {
|
|
81
|
-
super.heldWrite(next);
|
|
82
|
-
this.render();
|
|
83
|
-
}
|
|
84
|
-
holdChanged() {
|
|
85
|
-
this.render();
|
|
86
|
-
}
|
|
87
80
|
markerList() {
|
|
88
81
|
return this.#markers;
|
|
89
82
|
}
|
|
@@ -101,6 +94,9 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
101
94
|
renderEmpty() {
|
|
102
95
|
this.toggleState("empty", this.isEmpty());
|
|
103
96
|
}
|
|
97
|
+
scrubChanged() {
|
|
98
|
+
this.render();
|
|
99
|
+
}
|
|
104
100
|
showCurrentMarker(seconds) {
|
|
105
101
|
const current = this.#markers.findLast((marker) => marker.start <= seconds);
|
|
106
102
|
if (markerKey(current) === markerKey(this.#current)) return;
|
|
@@ -1,36 +1,19 @@
|
|
|
1
1
|
import { clamp, roundTo } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
|
-
import { capturePointer } from "../lib/pointer-drag.js";
|
|
4
3
|
import { dueRegions, readRegions } from "../lib/time-regions.js";
|
|
5
4
|
import { SonicWaveElement } from "./wave-element.js";
|
|
6
5
|
import { frameTimeline } from "../lib/frame-timeline.js";
|
|
7
6
|
import { isKind, writeKind } from "../lib/marker-band.js";
|
|
8
7
|
import { createLabelRider } from "../lib/marker-rider.js";
|
|
8
|
+
import { createPeaksRequest } from "../lib/peaks-request.js";
|
|
9
9
|
import { createTrackingClock } from "../lib/tracking-clock.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { paintWaveform, waveformColours } from "../lib/waveform-scene.js";
|
|
11
|
+
import { bindZoom, bindZoomKeys } from "../lib/zoom-gesture.js";
|
|
12
12
|
|
|
13
13
|
//#region src/elements/waveform.ts
|
|
14
|
-
const colours = {
|
|
15
|
-
ends: "--_sonic-waveform-ends",
|
|
16
|
-
grid: "--_sonic-waveform-grid",
|
|
17
|
-
marker: "--_sonic-waveform-marker",
|
|
18
|
-
placeholder: "--_sonic-waveform-placeholder",
|
|
19
|
-
wave: "--_sonic-waveform-wave",
|
|
20
|
-
waveEdge: "--_sonic-waveform-wave-edge"
|
|
21
|
-
};
|
|
22
14
|
const defaultZoom = 70;
|
|
23
15
|
const defaultZoomMin = 20;
|
|
24
16
|
const defaultZoomMax = 280;
|
|
25
|
-
const zoomKeys = /* @__PURE__ */ new Map([
|
|
26
|
-
["+", 1],
|
|
27
|
-
["-", -1],
|
|
28
|
-
["=", 1]
|
|
29
|
-
]);
|
|
30
|
-
const amplitudeMargin = .94;
|
|
31
|
-
const hatchPitchPx = 8;
|
|
32
|
-
const placeholderWavelengthPx = 48;
|
|
33
|
-
const placeholderAmplitude = .2;
|
|
34
17
|
const renderWaveform = template(`
|
|
35
18
|
<div class="sonic-waveform" role="slider" tabindex="0">
|
|
36
19
|
<canvas class="sonic-waveform-canvas" aria-hidden="true"></canvas>
|
|
@@ -59,66 +42,6 @@ function placeLine(line, at) {
|
|
|
59
42
|
const translate = `${String(at * 100)}cqi`;
|
|
60
43
|
if (line.style.getPropertyValue("translate") !== translate) line.style.setProperty("translate", translate);
|
|
61
44
|
}
|
|
62
|
-
function paintHatch(context, scene, [from, to]) {
|
|
63
|
-
if (to <= from) return;
|
|
64
|
-
const { dpr, height } = scene;
|
|
65
|
-
const pitch = Math.max(2, Math.round(hatchPitchPx * dpr));
|
|
66
|
-
const offset = scene.startSeconds * scene.pixelsPerSecond;
|
|
67
|
-
context.save();
|
|
68
|
-
context.beginPath();
|
|
69
|
-
context.rect(from, 0, to - from, height);
|
|
70
|
-
context.clip();
|
|
71
|
-
context.beginPath();
|
|
72
|
-
for (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {
|
|
73
|
-
context.moveTo(x, height);
|
|
74
|
-
context.lineTo(x + height, 0);
|
|
75
|
-
}
|
|
76
|
-
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
77
|
-
context.strokeStyle = scene.colours.ends;
|
|
78
|
-
context.stroke();
|
|
79
|
-
context.restore();
|
|
80
|
-
}
|
|
81
|
-
function paintPlaceholders(context, scene) {
|
|
82
|
-
if (scene.pending.length === 0) return;
|
|
83
|
-
const { dpr, height, pixelsPerSecond, startSeconds } = scene;
|
|
84
|
-
const wavelength = placeholderWavelengthPx * dpr;
|
|
85
|
-
const center = height / 2;
|
|
86
|
-
const origin = startSeconds * pixelsPerSecond;
|
|
87
|
-
const waveY = (x) => center - Math.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) * center * placeholderAmplitude;
|
|
88
|
-
context.beginPath();
|
|
89
|
-
for (const [from, to] of scene.pending) {
|
|
90
|
-
const opening = (from - startSeconds) * pixelsPerSecond;
|
|
91
|
-
const closing = (to - startSeconds) * pixelsPerSecond;
|
|
92
|
-
context.moveTo(opening, waveY(opening));
|
|
93
|
-
for (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));
|
|
94
|
-
context.lineTo(closing, waveY(closing));
|
|
95
|
-
}
|
|
96
|
-
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
97
|
-
context.strokeStyle = scene.colours.placeholder;
|
|
98
|
-
context.stroke();
|
|
99
|
-
}
|
|
100
|
-
function waveFill(context, scene) {
|
|
101
|
-
const { wave, waveEdge } = scene.colours;
|
|
102
|
-
const gradient = context.createLinearGradient(0, 0, 0, scene.height);
|
|
103
|
-
gradient.addColorStop(0, waveEdge);
|
|
104
|
-
gradient.addColorStop(.5, wave);
|
|
105
|
-
gradient.addColorStop(1, waveEdge);
|
|
106
|
-
return gradient;
|
|
107
|
-
}
|
|
108
|
-
function paintWave(context, scene, peaks) {
|
|
109
|
-
const buckets = waveformBuckets(peaks, scene);
|
|
110
|
-
const [first] = buckets;
|
|
111
|
-
if (!first) return;
|
|
112
|
-
const center = scene.height / 2;
|
|
113
|
-
const amplitudePx = center * amplitudeMargin;
|
|
114
|
-
context.fillStyle = waveFill(context, scene);
|
|
115
|
-
context.beginPath();
|
|
116
|
-
context.moveTo(first.x, center);
|
|
117
|
-
for (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);
|
|
118
|
-
for (const bucket of buckets.toReversed()) context.lineTo(bucket.x, center - bucket.low * amplitudePx);
|
|
119
|
-
context.closePath();
|
|
120
|
-
context.fill();
|
|
121
|
-
}
|
|
122
45
|
var SonicWaveform = class extends SonicWaveElement {
|
|
123
46
|
static observedAttributes = [
|
|
124
47
|
...SonicWaveElement.observedAttributes,
|
|
@@ -137,8 +60,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
137
60
|
set peaks(peaks) {
|
|
138
61
|
this.#peaks = peaks;
|
|
139
62
|
this.renderEmpty();
|
|
140
|
-
this.#
|
|
63
|
+
this.#peaksRequest.changed();
|
|
141
64
|
}
|
|
65
|
+
/** Spans being fetched; drawn as placeholders until cleared */
|
|
142
66
|
get pending() {
|
|
143
67
|
return this.#pending.map(([start, end]) => [start, end]);
|
|
144
68
|
}
|
|
@@ -152,8 +76,9 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
152
76
|
const key = region.join(":");
|
|
153
77
|
return [key, listedMs.get(key) ?? nowMs];
|
|
154
78
|
}));
|
|
155
|
-
this.#
|
|
79
|
+
this.#peaksRequest.changed();
|
|
156
80
|
}
|
|
81
|
+
/** How long a span stays listed in `pending` before its placeholder draws */
|
|
157
82
|
get pendingDelay() {
|
|
158
83
|
return Math.max(0, this.numberAttribute("pending-delay", 0));
|
|
159
84
|
}
|
|
@@ -173,6 +98,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
173
98
|
set playing(isPlaying) {
|
|
174
99
|
this.reflect("playing", isPlaying);
|
|
175
100
|
}
|
|
101
|
+
/** Read every frame for the playback time, so the waveform scrolls smoother than writes to `value` arrive */
|
|
176
102
|
get readTime() {
|
|
177
103
|
return this.#readTime;
|
|
178
104
|
}
|
|
@@ -198,8 +124,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
198
124
|
}
|
|
199
125
|
set requestPeaks(request) {
|
|
200
126
|
this.#requestPeaks = request;
|
|
201
|
-
this.#
|
|
202
|
-
this.surface()?.invalidate();
|
|
127
|
+
this.#peaksRequest.reset();
|
|
203
128
|
}
|
|
204
129
|
get zoom() {
|
|
205
130
|
const zoom = this.numberAttribute("zoom", defaultZoom);
|
|
@@ -230,25 +155,22 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
230
155
|
}
|
|
231
156
|
control = renderWaveform();
|
|
232
157
|
canvas = requireChild(this.control, ".sonic-waveform-canvas", HTMLCanvasElement);
|
|
233
|
-
colours =
|
|
158
|
+
colours = waveformColours;
|
|
234
159
|
lengths = {};
|
|
235
160
|
sheet = "waveform.css";
|
|
236
161
|
sizeProperty = "--_sonic-waveform-size";
|
|
237
|
-
#asked = /* @__PURE__ */ new WeakSet();
|
|
238
|
-
#askedKey;
|
|
239
162
|
#clock = createTrackingClock();
|
|
240
|
-
#connection;
|
|
241
163
|
#drawn;
|
|
242
164
|
#ghost = requireChild(this.control, ".sonic-waveform-ghost", HTMLDivElement);
|
|
243
165
|
#grabbed;
|
|
244
|
-
#isAsking = false;
|
|
245
|
-
#isPinchSpent = false;
|
|
246
166
|
#kindColours = /* @__PURE__ */ new Map();
|
|
247
167
|
#paintedKey = "";
|
|
248
168
|
#peaks;
|
|
169
|
+
#peaksRequest = createPeaksRequest(() => {
|
|
170
|
+
this.surface()?.invalidate();
|
|
171
|
+
});
|
|
249
172
|
#pending = [];
|
|
250
173
|
#pendingListedMs = /* @__PURE__ */ new Map();
|
|
251
|
-
#pinch;
|
|
252
174
|
#playhead = requireChild(this.control, ".sonic-waveform-playhead", HTMLDivElement);
|
|
253
175
|
#readTime;
|
|
254
176
|
#renderLabel;
|
|
@@ -260,11 +182,10 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
260
182
|
insetProperty: "--_sonic-waveform-label-inset",
|
|
261
183
|
parkProperty: "--_sonic-waveform-label-park"
|
|
262
184
|
});
|
|
263
|
-
#
|
|
264
|
-
#wheel;
|
|
185
|
+
#zoomGesture;
|
|
265
186
|
attributeChangedCallback(name) {
|
|
266
187
|
super.attributeChangedCallback(name);
|
|
267
|
-
if (name === "zoomable") this.#
|
|
188
|
+
if (name === "zoomable") this.#zoomGesture?.sync();
|
|
268
189
|
}
|
|
269
190
|
connectedCallback() {
|
|
270
191
|
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "renderLabel", "requestPeaks", "zoom", "zoomable", "zoomMax", "zoomMin");
|
|
@@ -272,15 +193,20 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
272
193
|
}
|
|
273
194
|
connect(signal) {
|
|
274
195
|
super.connect(signal);
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
196
|
+
const zoomTarget = {
|
|
197
|
+
claim: () => {
|
|
198
|
+
this.abandonHold();
|
|
199
|
+
},
|
|
200
|
+
isDisabled: () => this.isDisabled(),
|
|
201
|
+
isZoomable: () => this.zoomable,
|
|
202
|
+
zoom: () => this.zoom,
|
|
203
|
+
zoomTo: (target) => {
|
|
204
|
+
this.#zoomTo(target);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
this.#zoomGesture = bindZoom(this.control, zoomTarget, signal);
|
|
282
208
|
this.bindGestures(this.control, signal, () => this.#grab());
|
|
283
|
-
|
|
209
|
+
bindZoomKeys(this.control, zoomTarget, signal);
|
|
284
210
|
if (!("fonts" in document)) return;
|
|
285
211
|
document.fonts.addEventListener("loadingdone", () => {
|
|
286
212
|
this.renderMarkers();
|
|
@@ -298,6 +224,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
298
224
|
const held = this.#held();
|
|
299
225
|
const isPaged = look.isReducedMotion && this.reducedMotion === "page";
|
|
300
226
|
const pending = this.#duePending();
|
|
227
|
+
const range = [this.min, Math.max(this.min, this.max)];
|
|
301
228
|
const timeline = frameTimeline({
|
|
302
229
|
clockSeconds: this.#clock.read(frameMs, {
|
|
303
230
|
isPlaying: this.playing,
|
|
@@ -310,7 +237,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
310
237
|
isPlaying: this.playing,
|
|
311
238
|
isStill: look.isReducedMotion,
|
|
312
239
|
pending,
|
|
313
|
-
range
|
|
240
|
+
range,
|
|
314
241
|
size,
|
|
315
242
|
zoom: this.zoom
|
|
316
243
|
});
|
|
@@ -332,11 +259,17 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
332
259
|
});
|
|
333
260
|
if (isDirty || timeline.paintKey !== this.#paintedKey) {
|
|
334
261
|
this.#paintedKey = timeline.paintKey;
|
|
335
|
-
this.#
|
|
262
|
+
if (isRebuilt) this.#kindColours = this.#readKindColours();
|
|
263
|
+
paintWaveform(context, {
|
|
336
264
|
...view,
|
|
337
265
|
colours: look.colours
|
|
338
|
-
},
|
|
339
|
-
|
|
266
|
+
}, {
|
|
267
|
+
kindColours: this.#kindColours,
|
|
268
|
+
markers: this.markerList(),
|
|
269
|
+
peaks: this.#peaks,
|
|
270
|
+
range
|
|
271
|
+
});
|
|
272
|
+
this.#peaksRequest.ask(this.#requestPeaks, timeline.wanted);
|
|
340
273
|
}
|
|
341
274
|
if (timeline.isMoving) this.surface()?.requestFrame();
|
|
342
275
|
}
|
|
@@ -354,95 +287,13 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
354
287
|
}]));
|
|
355
288
|
this.surface()?.rebuild();
|
|
356
289
|
}
|
|
357
|
-
#ask(wanted) {
|
|
358
|
-
const key = wanted.join(":");
|
|
359
|
-
if (key === this.#askedKey) return;
|
|
360
|
-
let asked;
|
|
361
|
-
this.#askedKey = key;
|
|
362
|
-
this.#isAsking = true;
|
|
363
|
-
try {
|
|
364
|
-
asked = this.#requestPeaks?.(...wanted);
|
|
365
|
-
} finally {
|
|
366
|
-
this.#isAsking = false;
|
|
367
|
-
}
|
|
368
|
-
const promises = asked instanceof Promise ? [asked] : asked ?? [];
|
|
369
|
-
for (const promise of promises) {
|
|
370
|
-
if (this.#asked.has(promise)) continue;
|
|
371
|
-
this.#asked.add(promise);
|
|
372
|
-
this.#repaintAfter(promise);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
#bindPinch(signal) {
|
|
376
|
-
const control = this.control;
|
|
377
|
-
this.#touches.clear();
|
|
378
|
-
this.#pinch = void 0;
|
|
379
|
-
this.#isPinchSpent = false;
|
|
380
|
-
control.addEventListener("pointerdown", (event) => {
|
|
381
|
-
if (event.pointerType !== "touch" || !this.zoomable || this.isDisabled()) return;
|
|
382
|
-
this.#touches.set(event.pointerId, event.clientX);
|
|
383
|
-
if (this.#pinch || this.#isPinchSpent) return;
|
|
384
|
-
const pointerIds = this.#touchPair();
|
|
385
|
-
if (!pointerIds) return;
|
|
386
|
-
this.abandonHold();
|
|
387
|
-
capturePointer(control, event.pointerId);
|
|
388
|
-
this.#pinch = {
|
|
389
|
-
pointerIds,
|
|
390
|
-
startSpanPx: this.#spanPx(pointerIds),
|
|
391
|
-
startZoom: this.zoom
|
|
392
|
-
};
|
|
393
|
-
}, { signal });
|
|
394
|
-
control.addEventListener("pointermove", (event) => {
|
|
395
|
-
if (!this.#touches.has(event.pointerId)) return;
|
|
396
|
-
this.#touches.set(event.pointerId, event.clientX);
|
|
397
|
-
const pinch = this.#pinch;
|
|
398
|
-
if (!pinch?.pointerIds.includes(event.pointerId)) return;
|
|
399
|
-
this.#zoomTo(pinch.startZoom * pinchFactor(pinch.startSpanPx, this.#spanPx(pinch.pointerIds)));
|
|
400
|
-
}, { signal });
|
|
401
|
-
for (const type of ["pointerup", "pointercancel"]) control.addEventListener(type, (event) => {
|
|
402
|
-
if (!this.#touches.delete(event.pointerId)) return;
|
|
403
|
-
if (this.#pinch?.pointerIds.includes(event.pointerId)) {
|
|
404
|
-
this.#pinch = void 0;
|
|
405
|
-
this.#isPinchSpent = true;
|
|
406
|
-
}
|
|
407
|
-
if (this.#touches.size === 0) this.#isPinchSpent = false;
|
|
408
|
-
}, { signal });
|
|
409
|
-
}
|
|
410
|
-
#bindWheel() {
|
|
411
|
-
this.#wheel?.abort();
|
|
412
|
-
this.#wheel = void 0;
|
|
413
|
-
if (!this.zoomable || !this.#connection) return;
|
|
414
|
-
this.#wheel = new AbortController();
|
|
415
|
-
this.control.addEventListener("wheel", (event) => {
|
|
416
|
-
if (!event.ctrlKey && !event.metaKey || this.isDisabled()) return;
|
|
417
|
-
event.preventDefault();
|
|
418
|
-
this.#zoomTo(this.zoom * wheelFactor(event.deltaY, event.deltaMode));
|
|
419
|
-
}, {
|
|
420
|
-
passive: false,
|
|
421
|
-
signal: this.#wheel.signal
|
|
422
|
-
});
|
|
423
|
-
let startZoom = this.zoom;
|
|
424
|
-
for (const type of ["gesturestart", "gesturechange"]) this.control.addEventListener(type, (event) => {
|
|
425
|
-
if (this.isDisabled() || this.#touches.size > 0) return;
|
|
426
|
-
event.preventDefault();
|
|
427
|
-
if (type === "gesturestart") startZoom = this.zoom;
|
|
428
|
-
else if ("scale" in event && typeof event.scale === "number") this.#zoomTo(startZoom * event.scale);
|
|
429
|
-
}, { signal: this.#wheel.signal });
|
|
430
|
-
}
|
|
431
|
-
#bindZoomKeys(signal) {
|
|
432
|
-
this.control.addEventListener("keydown", (event) => {
|
|
433
|
-
const direction = this.#zoomKey(event);
|
|
434
|
-
if (direction === void 0) return;
|
|
435
|
-
event.preventDefault();
|
|
436
|
-
this.#zoomTo(this.zoom * zoomKeyFactor ** direction);
|
|
437
|
-
}, { signal });
|
|
438
|
-
}
|
|
439
290
|
#duePending() {
|
|
440
291
|
const due = dueRegions(this.#pending, this.#pendingListedMs, [performance.now(), this.pendingDelay]);
|
|
441
292
|
if (due.length < this.#pending.length) this.surface()?.requestFrame();
|
|
442
293
|
return due;
|
|
443
294
|
}
|
|
444
295
|
#grab() {
|
|
445
|
-
if (this.#
|
|
296
|
+
if (this.#zoomGesture?.isPinching() === true) return void 0;
|
|
446
297
|
const drawn = this.#drawn ?? {
|
|
447
298
|
seconds: this.value,
|
|
448
299
|
startSeconds: this.value
|
|
@@ -459,42 +310,13 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
459
310
|
}
|
|
460
311
|
#held() {
|
|
461
312
|
const grabbed = this.#grabbed;
|
|
462
|
-
const from = this.
|
|
313
|
+
const { from } = this.scrubState();
|
|
463
314
|
if (!grabbed || from === void 0) return void 0;
|
|
464
315
|
return {
|
|
465
316
|
grabbed,
|
|
466
317
|
seconds: this.value === from ? grabbed.seconds : this.value
|
|
467
318
|
};
|
|
468
319
|
}
|
|
469
|
-
#paintMarkers(context, scene, isRebuilt) {
|
|
470
|
-
if (isRebuilt) this.#kindColours = this.#readKindColours();
|
|
471
|
-
const line = Math.max(1, Math.round(scene.dpr));
|
|
472
|
-
for (const { dimmed, kind, start } of this.markers) {
|
|
473
|
-
const x = (start - scene.startSeconds) * scene.pixelsPerSecond;
|
|
474
|
-
context.fillStyle = (isKind(kind) ? this.#kindColours.get(kind) : void 0) ?? scene.colours.marker;
|
|
475
|
-
context.globalAlpha = dimmed === true ? .5 : 1;
|
|
476
|
-
context.fillRect(x - line / 2, 0, line, scene.height);
|
|
477
|
-
}
|
|
478
|
-
context.globalAlpha = 1;
|
|
479
|
-
}
|
|
480
|
-
#paintScene(context, scene, isRebuilt) {
|
|
481
|
-
const { dpr, height, pixelsPerSecond, startSeconds, width } = scene;
|
|
482
|
-
const x = (seconds) => (seconds - startSeconds) * pixelsPerSecond;
|
|
483
|
-
const { ends, grid } = scene.colours;
|
|
484
|
-
const line = Math.max(1, Math.round(dpr));
|
|
485
|
-
const [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];
|
|
486
|
-
const [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];
|
|
487
|
-
context.clearRect(0, 0, width, height);
|
|
488
|
-
paintHatch(context, scene, [0, from]);
|
|
489
|
-
paintHatch(context, scene, [to, width]);
|
|
490
|
-
context.fillStyle = grid;
|
|
491
|
-
if (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);
|
|
492
|
-
paintPlaceholders(context, scene);
|
|
493
|
-
if (this.#peaks) paintWave(context, scene, this.#peaks);
|
|
494
|
-
context.fillStyle = ends;
|
|
495
|
-
for (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);
|
|
496
|
-
this.#paintMarkers(context, scene, isRebuilt);
|
|
497
|
-
}
|
|
498
320
|
#readKindColours() {
|
|
499
321
|
const kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];
|
|
500
322
|
const probes = kinds.map((kind) => {
|
|
@@ -508,29 +330,8 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
508
330
|
for (const probe of probes) probe.remove();
|
|
509
331
|
return new Map(kinds.map((kind, index) => [kind, read[index] ?? ""]));
|
|
510
332
|
}
|
|
511
|
-
#repaint() {
|
|
512
|
-
if (!this.#isAsking) this.#askedKey = void 0;
|
|
513
|
-
this.surface()?.invalidate();
|
|
514
|
-
}
|
|
515
|
-
async #repaintAfter(asked) {
|
|
516
|
-
await Promise.allSettled([asked]);
|
|
517
|
-
this.#repaint();
|
|
518
|
-
}
|
|
519
333
|
#sourceSeconds() {
|
|
520
|
-
return this.#readTime?.() ?? this.
|
|
521
|
-
}
|
|
522
|
-
#spanPx([first, second]) {
|
|
523
|
-
return (this.#touches.get(second) ?? 0) - (this.#touches.get(first) ?? 0);
|
|
524
|
-
}
|
|
525
|
-
#touchPair() {
|
|
526
|
-
const [first, second, third] = this.#touches.keys();
|
|
527
|
-
if (first === void 0 || second === void 0 || third !== void 0) return void 0;
|
|
528
|
-
return [first, second];
|
|
529
|
-
}
|
|
530
|
-
#zoomKey(event) {
|
|
531
|
-
if (!this.zoomable || this.isDisabled() || event.target !== this.control) return void 0;
|
|
532
|
-
if (event.ctrlKey || event.metaKey || event.altKey) return void 0;
|
|
533
|
-
return zoomKeys.get(event.key);
|
|
334
|
+
return this.#readTime?.() ?? this.scrubState().played;
|
|
534
335
|
}
|
|
535
336
|
#zoomTo(target) {
|
|
536
337
|
const next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);
|