@xsynaptic/sonic-ui 0.17.0 → 0.18.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 +13 -0
- package/README.md +112 -19
- package/dist/default/dat.js +1 -0
- package/dist/default/elements/button.js +2 -0
- package/dist/default/elements/dial.js +2 -0
- package/dist/default/elements/display-element.js +9 -7
- package/dist/default/elements/envelope.js +31 -72
- package/dist/default/elements/field-gesture.js +81 -0
- package/dist/default/elements/meter.js +4 -0
- 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/slider.js +11 -13
- package/dist/default/elements/split.js +14 -13
- package/dist/default/elements/switch.js +31 -46
- package/dist/default/elements/toggle.js +20 -44
- package/dist/default/elements/value-element.js +65 -91
- package/dist/default/elements/value-gestures.js +22 -22
- package/dist/default/elements/wave-element.js +9 -12
- package/dist/default/elements/waveform.js +42 -241
- package/dist/default/elements/wavestrip.js +10 -5
- package/dist/default/elements/xy.js +67 -112
- package/dist/default/lib/canvas-surface.js +25 -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/value-model.js +62 -0
- package/dist/default/lib/waveform-scene.js +108 -0
- package/dist/default/lib/zoom-gesture.js +97 -1
- package/dist/dev/dat.d.ts +1 -0
- package/dist/dev/dat.js +1 -0
- package/dist/dev/dat.js.map +1 -1
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +2 -0
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +2 -0
- package/dist/dev/elements/dial.js +2 -0
- 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 +9 -7
- package/dist/dev/elements/display-element.js.map +1 -1
- package/dist/dev/elements/envelope.js +31 -72
- 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 +4 -0
- package/dist/dev/elements/meter.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/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +11 -13
- package/dist/dev/elements/slider.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 +31 -46
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/toggle.js +20 -44
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +5 -5
- package/dist/dev/elements/value-element.js +65 -91
- 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 +6 -4
- package/dist/dev/elements/wave-element.js +9 -12
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +19 -21
- 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 +67 -112
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +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/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/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/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 +1 -0
- 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/styles/button.css +16 -6
- package/dist/styles/waveform.css +2 -1
- package/package.json +1 -1
- package/dist/default/lib/asked-value.js +0 -21
- package/dist/default/lib/fill-watch.js +0 -29
- 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
|
@@ -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);
|
|
@@ -1,6 +1,7 @@
|
|
|
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 { scrubRegions } from "../lib/scrub.js";
|
|
4
5
|
import { sortedRegions } from "../lib/time-regions.js";
|
|
5
6
|
import { SonicWaveElement } from "./wave-element.js";
|
|
6
7
|
import { createMarkerBand } from "../lib/marker-band.js";
|
|
@@ -48,6 +49,7 @@ function fillRegion(context, path, [from, to, style]) {
|
|
|
48
49
|
context.restore();
|
|
49
50
|
}
|
|
50
51
|
var SonicWavestrip = class extends SonicWaveElement {
|
|
52
|
+
/** Loaded spans, in seconds */
|
|
51
53
|
get buffered() {
|
|
52
54
|
return this.#buffered.map(([start, end]) => [start, end]);
|
|
53
55
|
}
|
|
@@ -61,6 +63,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
61
63
|
set cancellable(isCancellable) {
|
|
62
64
|
this.reflect("cancellable", isCancellable);
|
|
63
65
|
}
|
|
66
|
+
/** One amplitude from 0 to 1 per slice of the whole track, any length; resampled to the bars and never normalised */
|
|
64
67
|
get peaks() {
|
|
65
68
|
return this.#peaks;
|
|
66
69
|
}
|
|
@@ -80,6 +83,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
80
83
|
#markerBand = createMarkerBand(requireChild(this.control, ".sonic-wavestrip-markers", HTMLDivElement));
|
|
81
84
|
#paintedKey = "";
|
|
82
85
|
#peaks;
|
|
86
|
+
/** Viewport x of a time, clamped to the strip */
|
|
83
87
|
clientXOf(value) {
|
|
84
88
|
const { startPx, travelPx } = this.#axis();
|
|
85
89
|
return startPx + this.mapping().proportionOf(value) * travelPx;
|
|
@@ -107,7 +111,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
107
111
|
draw() {
|
|
108
112
|
const surface = this.surface();
|
|
109
113
|
this.#placeReadout();
|
|
110
|
-
this.showCurrentMarker(this.
|
|
114
|
+
this.showCurrentMarker(this.scrubState().played);
|
|
111
115
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
112
116
|
}
|
|
113
117
|
isEmpty() {
|
|
@@ -189,12 +193,13 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
189
193
|
this.control.style.setProperty("--_sonic-wavestrip-readout-at", String(at));
|
|
190
194
|
}
|
|
191
195
|
#stripRegions(width) {
|
|
192
|
-
const
|
|
193
|
-
const
|
|
196
|
+
const mapping = this.mapping();
|
|
197
|
+
const { proportionOf } = mapping;
|
|
198
|
+
const { played, scrub } = scrubRegions(this.scrubState(), this.value, mapping);
|
|
194
199
|
return stripRegions({
|
|
195
200
|
buffered: this.#buffered.map(([start, end]) => [proportionOf(start), proportionOf(end)]),
|
|
196
|
-
played
|
|
197
|
-
...
|
|
201
|
+
played,
|
|
202
|
+
...scrub === void 0 ? {} : { scrub }
|
|
198
203
|
}, width);
|
|
199
204
|
}
|
|
200
205
|
};
|