@xsynaptic/sonic-ui 0.6.1 → 0.8.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 +4 -0
- package/dist/default/define/spectrum.js +7 -0
- package/dist/default/define.js +1 -0
- package/dist/default/elements/button.js +30 -2
- package/dist/default/elements/display-element.js +77 -0
- package/dist/default/elements/envelope.js +1 -1
- package/dist/default/elements/slider.js +67 -1
- package/dist/default/elements/spectrum.js +230 -0
- package/dist/default/elements/value-element.js +49 -11
- package/dist/default/elements/wave-element.js +59 -9
- package/dist/default/elements/waveform.js +56 -10
- package/dist/default/elements/wavestrip.js +31 -45
- package/dist/default/elements/xy.js +1 -1
- package/dist/default/index.js +2 -1
- package/dist/default/lib/canvas-surface.js +7 -4
- package/dist/default/lib/frame-timeline.js +2 -2
- package/dist/default/lib/hover-preview.js +46 -0
- package/dist/default/lib/marker-rider.js +7 -4
- package/dist/default/lib/pointer-drag.js +7 -2
- package/dist/default/lib/spectrum-columns.js +74 -0
- package/dist/default/lib/spectrum-levels.js +55 -0
- package/dist/default/lib/strip-scene.js +12 -1
- package/dist/default/lib/time-regions.js +5 -1
- package/dist/dev/define/spectrum.d.ts +2 -0
- package/dist/dev/define/spectrum.js +8 -0
- package/dist/dev/define/spectrum.js.map +1 -0
- package/dist/dev/define.d.ts +3 -1
- package/dist/dev/define.js +1 -0
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +30 -2
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/display-element.d.ts +29 -0
- package/dist/dev/elements/display-element.js +78 -0
- package/dist/dev/elements/display-element.js.map +1 -0
- package/dist/dev/elements/envelope.js +1 -1
- package/dist/dev/elements/slider.d.ts +6 -0
- package/dist/dev/elements/slider.js +67 -1
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/spectrum.d.ts +59 -0
- package/dist/dev/elements/spectrum.js +231 -0
- package/dist/dev/elements/spectrum.js.map +1 -0
- package/dist/dev/elements/value-element.d.ts +5 -1
- package/dist/dev/elements/value-element.js +49 -11
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +8 -2
- package/dist/dev/elements/wave-element.js +59 -9
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +12 -4
- package/dist/dev/elements/waveform.js +56 -10
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +3 -0
- package/dist/dev/elements/wavestrip.js +31 -45
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +1 -1
- package/dist/dev/index.d.ts +4 -1
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/canvas-surface.d.ts +4 -3
- package/dist/dev/lib/canvas-surface.js +7 -4
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/frame-timeline.js +2 -2
- package/dist/dev/lib/frame-timeline.js.map +1 -1
- package/dist/dev/lib/hover-preview.js +47 -0
- package/dist/dev/lib/hover-preview.js.map +1 -0
- package/dist/dev/lib/marker-rider.js +7 -4
- package/dist/dev/lib/marker-rider.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +7 -2
- package/dist/dev/lib/pointer-drag.js.map +1 -1
- package/dist/dev/lib/spectrum-columns.js +75 -0
- package/dist/dev/lib/spectrum-columns.js.map +1 -0
- package/dist/dev/lib/spectrum-levels.js +56 -0
- package/dist/dev/lib/spectrum-levels.js.map +1 -0
- package/dist/dev/lib/strip-scene.js +12 -1
- package/dist/dev/lib/strip-scene.js.map +1 -1
- package/dist/dev/lib/time-regions.js +5 -1
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/styles/button.css +7 -1
- package/dist/styles/controls.css +1 -0
- package/dist/styles/material.css +18 -3
- package/dist/styles/meter.css +22 -18
- package/dist/styles/slider.css +42 -5
- package/dist/styles/spectrum.css +105 -0
- package/dist/styles/waveform.css +38 -16
- package/dist/styles/wavestrip.css +22 -4
- package/package.json +2 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { clamp, toNumber } from "../lib/math.js";
|
|
1
|
+
import { clamp, toNumber, trimFloat } from "../lib/math.js";
|
|
2
2
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
3
|
import { SonicFormElement } from "./form-element.js";
|
|
4
4
|
import { requireChild } from "../lib/render.js";
|
|
5
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
5
6
|
import { ReadoutClaim } from "./readout-claim.js";
|
|
6
7
|
import { Readout } from "./readout.js";
|
|
7
8
|
import { ValueEntry } from "./value-entry.js";
|
|
@@ -9,7 +10,6 @@ import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
|
|
|
9
10
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
10
11
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
11
12
|
import { parseNumberList } from "../lib/number-list.js";
|
|
12
|
-
import { bindDrag } from "../lib/pointer-drag.js";
|
|
13
13
|
import { readPxProperty } from "../lib/read-px-property.js";
|
|
14
14
|
import { resetKeys, valueMapping } from "../lib/value-mapping.js";
|
|
15
15
|
|
|
@@ -49,6 +49,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
49
49
|
"notched",
|
|
50
50
|
"origin",
|
|
51
51
|
"readout",
|
|
52
|
+
"spoken-step",
|
|
52
53
|
"step",
|
|
53
54
|
"tabindex",
|
|
54
55
|
"taper",
|
|
@@ -156,6 +157,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
156
157
|
set parseValue(parse) {
|
|
157
158
|
this.#parseValue = parse;
|
|
158
159
|
}
|
|
160
|
+
get pointerType() {
|
|
161
|
+
return this.#dragging()?.pointerType;
|
|
162
|
+
}
|
|
159
163
|
get positions() {
|
|
160
164
|
return parseNumberList(this.getAttribute("positions"));
|
|
161
165
|
}
|
|
@@ -168,6 +172,12 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
168
172
|
set readout(isShown) {
|
|
169
173
|
this.reflect("readout", isShown);
|
|
170
174
|
}
|
|
175
|
+
get spokenStep() {
|
|
176
|
+
return this.optionalNumberAttribute("spoken-step");
|
|
177
|
+
}
|
|
178
|
+
set spokenStep(value) {
|
|
179
|
+
this.reflect("spoken-step", value);
|
|
180
|
+
}
|
|
171
181
|
get step() {
|
|
172
182
|
return this.numberAttribute("step", 1);
|
|
173
183
|
}
|
|
@@ -206,8 +216,10 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
206
216
|
#entry;
|
|
207
217
|
#formatSpokenValue;
|
|
208
218
|
#formatValue;
|
|
219
|
+
#heldAt;
|
|
209
220
|
#instance = String(instanceCount += 1);
|
|
210
221
|
#isModulated = false;
|
|
222
|
+
#isRevealed = false;
|
|
211
223
|
#keyScrub;
|
|
212
224
|
#land;
|
|
213
225
|
#limit;
|
|
@@ -235,7 +247,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
235
247
|
this.render();
|
|
236
248
|
}
|
|
237
249
|
connectedCallback() {
|
|
238
|
-
this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
|
|
250
|
+
this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "spokenStep", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
|
|
239
251
|
super.connectedCallback();
|
|
240
252
|
}
|
|
241
253
|
bindGestures(control, signal, grab) {
|
|
@@ -261,6 +273,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
261
273
|
fromValue,
|
|
262
274
|
isOutside: false,
|
|
263
275
|
isRevealed: false,
|
|
276
|
+
pointerType: event.pointerType,
|
|
264
277
|
revealTimer: void 0,
|
|
265
278
|
state: this.#startDrag(control, axis, event)
|
|
266
279
|
};
|
|
@@ -306,7 +319,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
306
319
|
heldFrom() {
|
|
307
320
|
return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
|
|
308
321
|
}
|
|
309
|
-
heldWrite(
|
|
322
|
+
heldWrite(next) {
|
|
323
|
+
this.#heldAt = next;
|
|
324
|
+
}
|
|
310
325
|
holdChanged() {}
|
|
311
326
|
input(next) {
|
|
312
327
|
const previous = this.#value;
|
|
@@ -346,6 +361,10 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
346
361
|
originValue() {
|
|
347
362
|
return this.origin ?? this.mapping().bounds[0];
|
|
348
363
|
}
|
|
364
|
+
playback() {
|
|
365
|
+
const from = this.heldFrom();
|
|
366
|
+
return from === void 0 ? this.#value : this.#heldAt ?? from;
|
|
367
|
+
}
|
|
349
368
|
previewValue() {}
|
|
350
369
|
proportionsChanged() {}
|
|
351
370
|
readModulationValue() {
|
|
@@ -362,10 +381,11 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
362
381
|
const readout = this.#readout;
|
|
363
382
|
if (!readout) return;
|
|
364
383
|
const { isOpen, value } = this.#shown();
|
|
384
|
+
const text = value === void 0 ? void 0 : this.#textFor(value);
|
|
365
385
|
readout.bubble.show({
|
|
366
386
|
anchor: readout.anchor,
|
|
367
|
-
isOpen,
|
|
368
|
-
text
|
|
387
|
+
isOpen: isOpen && text !== "",
|
|
388
|
+
text
|
|
369
389
|
});
|
|
370
390
|
}
|
|
371
391
|
restoreState(state) {
|
|
@@ -473,10 +493,13 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
473
493
|
drag.state = this.#withinLimit(mapping, state);
|
|
474
494
|
if (value === void 0) return;
|
|
475
495
|
if (!wasEngaged) this.#reveal(drag);
|
|
496
|
+
const isCrossing = isOutside !== drag.isOutside;
|
|
497
|
+
if (isCrossing) {
|
|
498
|
+
drag.isOutside = isOutside;
|
|
499
|
+
this.toggleState("cancelling", isOutside);
|
|
500
|
+
}
|
|
476
501
|
this.input(isOutside ? drag.fromValue : value);
|
|
477
|
-
if (
|
|
478
|
-
drag.isOutside = isOutside;
|
|
479
|
-
this.#renderDragReveal(drag);
|
|
502
|
+
if (isCrossing) this.#renderDragReveal(drag);
|
|
480
503
|
}
|
|
481
504
|
#endHolds() {
|
|
482
505
|
this.#entry?.close(false);
|
|
@@ -508,7 +531,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
508
531
|
#releaseDrag(drag) {
|
|
509
532
|
clearTimeout(drag.revealTimer);
|
|
510
533
|
this.#claim.conceal("drag");
|
|
534
|
+
this.toggleState("cancelling", false);
|
|
511
535
|
this.#renderHold();
|
|
536
|
+
this.#showRevealed(false);
|
|
512
537
|
if (this.#value !== drag.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
513
538
|
this.#springBack();
|
|
514
539
|
}
|
|
@@ -524,7 +549,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
524
549
|
writeAttribute(control, "role", this.controlRole());
|
|
525
550
|
writeAttribute(control, "aria-valuemin", String(low));
|
|
526
551
|
writeAttribute(control, "aria-valuemax", String(high));
|
|
527
|
-
writeAttribute(control, "aria-valuenow", String(this.#
|
|
552
|
+
writeAttribute(control, "aria-valuenow", String(this.#spokenNow()));
|
|
528
553
|
if (orientation) writeAttribute(control, "aria-orientation", orientation);
|
|
529
554
|
writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#value));
|
|
530
555
|
this.forwardNaming(control, true);
|
|
@@ -543,11 +568,14 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
543
568
|
writeAttribute(control, "tabindex", isDisabled ? void 0 : stop);
|
|
544
569
|
}
|
|
545
570
|
#renderDragReveal(drag) {
|
|
546
|
-
|
|
571
|
+
const isRevealed = drag.isRevealed && !drag.isOutside;
|
|
572
|
+
if (isRevealed) this.#claim.reveal("drag");
|
|
547
573
|
else this.#claim.conceal("drag");
|
|
548
574
|
this.#renderHold();
|
|
575
|
+
this.#showRevealed(isRevealed);
|
|
549
576
|
}
|
|
550
577
|
#renderHold() {
|
|
578
|
+
if (this.heldFrom() === void 0) this.#heldAt = void 0;
|
|
551
579
|
this.renderReadout();
|
|
552
580
|
this.holdChanged();
|
|
553
581
|
}
|
|
@@ -581,6 +609,16 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
581
609
|
value: preview ?? this.#value
|
|
582
610
|
};
|
|
583
611
|
}
|
|
612
|
+
#showRevealed(isRevealed) {
|
|
613
|
+
if (isRevealed === this.#isRevealed) return;
|
|
614
|
+
this.#isRevealed = isRevealed;
|
|
615
|
+
this.toggleState("revealed", isRevealed);
|
|
616
|
+
this.dispatchEvent(new Event("sonic-reveal", { bubbles: true }));
|
|
617
|
+
}
|
|
618
|
+
#spokenNow() {
|
|
619
|
+
const step = this.spokenStep;
|
|
620
|
+
return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#value / step) * step) : this.#value;
|
|
621
|
+
}
|
|
584
622
|
#springBack() {
|
|
585
623
|
const target = this.springTarget();
|
|
586
624
|
if (target === void 0) return;
|
|
@@ -2,19 +2,44 @@ import { SonicValueElement } from "./value-element.js";
|
|
|
2
2
|
import { bindSurface } from "../lib/canvas-surface.js";
|
|
3
3
|
|
|
4
4
|
//#region src/elements/wave-element.ts
|
|
5
|
+
function markerKey(marker) {
|
|
6
|
+
return marker ? [
|
|
7
|
+
marker.start,
|
|
8
|
+
marker.end,
|
|
9
|
+
marker.kind,
|
|
10
|
+
marker.label
|
|
11
|
+
].join("|") : "";
|
|
12
|
+
}
|
|
5
13
|
var SonicWaveElement = class extends SonicValueElement {
|
|
14
|
+
static observedAttributes = [...SonicValueElement.observedAttributes, "fill"];
|
|
15
|
+
get currentMarker() {
|
|
16
|
+
return this.#current ? { ...this.#current } : void 0;
|
|
17
|
+
}
|
|
18
|
+
get fill() {
|
|
19
|
+
return this.hasAttribute("fill");
|
|
20
|
+
}
|
|
21
|
+
set fill(isFilling) {
|
|
22
|
+
this.reflect("fill", isFilling);
|
|
23
|
+
}
|
|
6
24
|
get markers() {
|
|
7
25
|
return this.#markers.map((marker) => ({ ...marker }));
|
|
8
26
|
}
|
|
9
27
|
set markers(list) {
|
|
10
28
|
this.#markers = [...list ?? []].filter((marker) => Number.isFinite(marker.start)).map((marker) => ({ ...marker })).toSorted((first, second) => first.start - second.start);
|
|
11
|
-
if (this.isBound())
|
|
29
|
+
if (!this.isBound()) return;
|
|
30
|
+
this.renderMarkers();
|
|
31
|
+
this.render();
|
|
12
32
|
}
|
|
13
|
-
#
|
|
33
|
+
#current;
|
|
34
|
+
#fillWatch;
|
|
14
35
|
#markers = [];
|
|
15
36
|
#surface;
|
|
37
|
+
attributeChangedCallback(name) {
|
|
38
|
+
super.attributeChangedCallback(name);
|
|
39
|
+
if (name === "fill") this.#watchFill();
|
|
40
|
+
}
|
|
16
41
|
connectedCallback() {
|
|
17
|
-
this.upgradeProperties("markers");
|
|
42
|
+
this.upgradeProperties("fill", "markers");
|
|
18
43
|
super.connectedCallback();
|
|
19
44
|
}
|
|
20
45
|
repaint() {
|
|
@@ -38,16 +63,20 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
38
63
|
});
|
|
39
64
|
this.render();
|
|
40
65
|
this.checkStyles(control, this.sheet);
|
|
66
|
+
this.#watchFill();
|
|
67
|
+
signal.addEventListener("abort", () => {
|
|
68
|
+
this.#fillWatch?.disconnect();
|
|
69
|
+
this.#fillWatch = void 0;
|
|
70
|
+
}, { once: true });
|
|
41
71
|
}
|
|
42
72
|
focusTarget() {
|
|
43
73
|
return this.control;
|
|
44
74
|
}
|
|
45
75
|
heldWrite(next) {
|
|
46
|
-
|
|
76
|
+
super.heldWrite(next);
|
|
47
77
|
this.render();
|
|
48
78
|
}
|
|
49
79
|
holdChanged() {
|
|
50
|
-
if (this.heldFrom() === void 0) this.#heldAt = void 0;
|
|
51
80
|
this.render();
|
|
52
81
|
}
|
|
53
82
|
markerList() {
|
|
@@ -61,19 +90,40 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
61
90
|
return [Math.min(at, to), Math.max(at, to)];
|
|
62
91
|
});
|
|
63
92
|
}
|
|
64
|
-
playback() {
|
|
65
|
-
const from = this.heldFrom();
|
|
66
|
-
return from === void 0 ? this.value : this.#heldAt ?? from;
|
|
67
|
-
}
|
|
68
93
|
proportionsChanged() {
|
|
69
94
|
this.renderMarkers();
|
|
70
95
|
}
|
|
71
96
|
renderEmpty() {
|
|
72
97
|
this.toggleState("empty", this.isEmpty());
|
|
73
98
|
}
|
|
99
|
+
showCurrentMarker(seconds) {
|
|
100
|
+
const current = this.#markers.findLast((marker) => marker.start <= seconds);
|
|
101
|
+
if (markerKey(current) === markerKey(this.#current)) return;
|
|
102
|
+
this.#current = current;
|
|
103
|
+
this.dispatchEvent(new Event("sonic-marker", { bubbles: true }));
|
|
104
|
+
}
|
|
74
105
|
surface() {
|
|
75
106
|
return this.#surface;
|
|
76
107
|
}
|
|
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
|
+
}
|
|
77
127
|
};
|
|
78
128
|
|
|
79
129
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clamp } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
|
-
import { readRegions } from "../lib/time-regions.js";
|
|
3
|
+
import { dueRegions, readRegions } from "../lib/time-regions.js";
|
|
4
4
|
import { SonicWaveElement } from "./wave-element.js";
|
|
5
5
|
import { frameTimeline } from "../lib/frame-timeline.js";
|
|
6
6
|
import { isKind, writeKind } from "../lib/marker-band.js";
|
|
@@ -10,9 +10,10 @@ import { waveformBuckets } from "../lib/waveform-buckets.js";
|
|
|
10
10
|
|
|
11
11
|
//#region src/elements/waveform.ts
|
|
12
12
|
const colours = {
|
|
13
|
+
ends: "--_sonic-waveform-ends",
|
|
13
14
|
grid: "--_sonic-waveform-grid",
|
|
14
|
-
hatch: "--_sonic-waveform-hatch",
|
|
15
15
|
marker: "--_sonic-waveform-marker",
|
|
16
|
+
placeholder: "--_sonic-waveform-placeholder",
|
|
16
17
|
wave: "--_sonic-waveform-wave",
|
|
17
18
|
waveEdge: "--_sonic-waveform-wave-edge"
|
|
18
19
|
};
|
|
@@ -63,7 +64,7 @@ function paintHatch(context, scene, [from, to]) {
|
|
|
63
64
|
context.lineTo(x + height, 0);
|
|
64
65
|
}
|
|
65
66
|
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
66
|
-
context.strokeStyle = scene.colours.
|
|
67
|
+
context.strokeStyle = scene.colours.ends;
|
|
67
68
|
context.stroke();
|
|
68
69
|
context.restore();
|
|
69
70
|
}
|
|
@@ -83,7 +84,7 @@ function paintPlaceholders(context, scene) {
|
|
|
83
84
|
context.lineTo(closing, waveY(closing));
|
|
84
85
|
}
|
|
85
86
|
context.lineWidth = Math.max(1, Math.round(dpr));
|
|
86
|
-
context.strokeStyle = scene.colours.
|
|
87
|
+
context.strokeStyle = scene.colours.placeholder;
|
|
87
88
|
context.stroke();
|
|
88
89
|
}
|
|
89
90
|
function waveFill(context, scene) {
|
|
@@ -111,8 +112,10 @@ function paintWave(context, scene, peaks) {
|
|
|
111
112
|
var SonicWaveform = class extends SonicWaveElement {
|
|
112
113
|
static observedAttributes = [
|
|
113
114
|
...SonicWaveElement.observedAttributes,
|
|
115
|
+
"pending-delay",
|
|
114
116
|
"playing",
|
|
115
117
|
"playback-rate",
|
|
118
|
+
"reduced-motion",
|
|
116
119
|
"zoom"
|
|
117
120
|
];
|
|
118
121
|
get peaks() {
|
|
@@ -127,9 +130,21 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
127
130
|
return this.#pending.map(([start, end]) => [start, end]);
|
|
128
131
|
}
|
|
129
132
|
set pending(regions) {
|
|
133
|
+
const listedMs = this.#pendingListedMs;
|
|
134
|
+
const nowMs = performance.now();
|
|
130
135
|
this.#pending = regions ? readRegions(regions) : [];
|
|
136
|
+
this.#pendingListedMs = new Map(this.#pending.map((region) => {
|
|
137
|
+
const key = region.join(":");
|
|
138
|
+
return [key, listedMs.get(key) ?? nowMs];
|
|
139
|
+
}));
|
|
131
140
|
this.surface()?.invalidate();
|
|
132
141
|
}
|
|
142
|
+
get pendingDelay() {
|
|
143
|
+
return Math.max(0, this.numberAttribute("pending-delay", 0));
|
|
144
|
+
}
|
|
145
|
+
set pendingDelay(value) {
|
|
146
|
+
this.reflect("pending-delay", value);
|
|
147
|
+
}
|
|
133
148
|
get playbackRate() {
|
|
134
149
|
const playbackRate = this.numberAttribute("playback-rate", 1);
|
|
135
150
|
return playbackRate > 0 ? playbackRate : 1;
|
|
@@ -150,11 +165,18 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
150
165
|
this.#readTime = read;
|
|
151
166
|
this.render();
|
|
152
167
|
}
|
|
168
|
+
get reducedMotion() {
|
|
169
|
+
return this.getAttribute("reduced-motion") === "scroll" ? "scroll" : "page";
|
|
170
|
+
}
|
|
171
|
+
set reducedMotion(motion) {
|
|
172
|
+
this.reflect("reduced-motion", motion);
|
|
173
|
+
}
|
|
153
174
|
get requestPeaks() {
|
|
154
175
|
return this.#requestPeaks;
|
|
155
176
|
}
|
|
156
177
|
set requestPeaks(request) {
|
|
157
178
|
this.#requestPeaks = request;
|
|
179
|
+
this.#asked = void 0;
|
|
158
180
|
this.surface()?.invalidate();
|
|
159
181
|
}
|
|
160
182
|
get zoom() {
|
|
@@ -169,6 +191,8 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
169
191
|
colours = colours;
|
|
170
192
|
lengths = {};
|
|
171
193
|
sheet = "waveform.css";
|
|
194
|
+
sizeProperty = "--_sonic-waveform-size";
|
|
195
|
+
#asked;
|
|
172
196
|
#clock = createTrackingClock();
|
|
173
197
|
#drawn;
|
|
174
198
|
#ghost = requireChild(this.control, ".sonic-waveform-ghost", HTMLDivElement);
|
|
@@ -177,16 +201,18 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
177
201
|
#paintedKey = "";
|
|
178
202
|
#peaks;
|
|
179
203
|
#pending = [];
|
|
204
|
+
#pendingListedMs = /* @__PURE__ */ new Map();
|
|
180
205
|
#playhead = requireChild(this.control, ".sonic-waveform-playhead", HTMLDivElement);
|
|
181
206
|
#readTime;
|
|
182
207
|
#requestPeaks;
|
|
183
208
|
#rider = createLabelRider(this.control, {
|
|
184
209
|
className: "sonic-waveform-label",
|
|
185
210
|
colourProperty: "--_sonic-waveform-marker",
|
|
211
|
+
fadeProperty: "--_sonic-waveform-label-fade",
|
|
186
212
|
insetProperty: "--_sonic-waveform-label-inset"
|
|
187
213
|
});
|
|
188
214
|
connectedCallback() {
|
|
189
|
-
this.upgradeProperties("peaks", "pending", "playing", "playbackRate", "readTime", "requestPeaks", "zoom");
|
|
215
|
+
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "requestPeaks", "zoom");
|
|
190
216
|
super.connectedCallback();
|
|
191
217
|
}
|
|
192
218
|
connect(signal) {
|
|
@@ -204,6 +230,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
204
230
|
}
|
|
205
231
|
paint(context, { frameMs, isDirty, isRebuilt, look, size }) {
|
|
206
232
|
const held = this.#held();
|
|
233
|
+
const pending = this.#duePending();
|
|
207
234
|
const timeline = frameTimeline({
|
|
208
235
|
clockSeconds: this.#clock.read(frameMs, {
|
|
209
236
|
isPlaying: this.playing,
|
|
@@ -212,9 +239,10 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
212
239
|
}),
|
|
213
240
|
frameMs,
|
|
214
241
|
...held ? { held } : {},
|
|
215
|
-
isPaged: look.isReducedMotion,
|
|
242
|
+
isPaged: look.isReducedMotion && this.reducedMotion === "page",
|
|
216
243
|
isPlaying: this.playing,
|
|
217
|
-
|
|
244
|
+
isStill: look.isReducedMotion,
|
|
245
|
+
pending,
|
|
218
246
|
range: [this.min, Math.max(this.min, this.max)],
|
|
219
247
|
size,
|
|
220
248
|
zoom: this.zoom
|
|
@@ -226,6 +254,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
226
254
|
};
|
|
227
255
|
placeLine(this.#playhead, timeline.playheadAt);
|
|
228
256
|
placeLine(this.#ghost, timeline.ghostAt);
|
|
257
|
+
this.showCurrentMarker(seconds);
|
|
229
258
|
this.toggleState("pending", view.pending.length > 0);
|
|
230
259
|
this.#rider.place({
|
|
231
260
|
playheadSeconds: seconds,
|
|
@@ -239,7 +268,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
239
268
|
...view,
|
|
240
269
|
colours: look.colours
|
|
241
270
|
}, isRebuilt);
|
|
242
|
-
if (timeline.wanted) this.#
|
|
271
|
+
if (timeline.wanted) this.#ask(timeline.wanted);
|
|
243
272
|
}
|
|
244
273
|
if (timeline.isMoving) this.surface()?.requestFrame();
|
|
245
274
|
}
|
|
@@ -253,6 +282,17 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
253
282
|
}]));
|
|
254
283
|
this.surface()?.rebuild();
|
|
255
284
|
}
|
|
285
|
+
#ask(wanted) {
|
|
286
|
+
const asked = this.#requestPeaks?.(...wanted);
|
|
287
|
+
if (!(asked instanceof Promise)) return;
|
|
288
|
+
this.#asked = asked;
|
|
289
|
+
this.#repaintAfter(asked);
|
|
290
|
+
}
|
|
291
|
+
#duePending() {
|
|
292
|
+
const due = dueRegions(this.#pending, this.#pendingListedMs, [performance.now(), this.pendingDelay]);
|
|
293
|
+
if (due.length < this.#pending.length) this.surface()?.requestFrame();
|
|
294
|
+
return due;
|
|
295
|
+
}
|
|
256
296
|
#grab() {
|
|
257
297
|
const drawn = this.#drawn ?? {
|
|
258
298
|
seconds: this.value,
|
|
@@ -291,7 +331,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
291
331
|
#paintScene(context, scene, isRebuilt) {
|
|
292
332
|
const { dpr, height, pixelsPerSecond, startSeconds, width } = scene;
|
|
293
333
|
const x = (seconds) => (seconds - startSeconds) * pixelsPerSecond;
|
|
294
|
-
const { grid } = scene.colours;
|
|
334
|
+
const { ends, grid } = scene.colours;
|
|
295
335
|
const line = Math.max(1, Math.round(dpr));
|
|
296
336
|
const [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];
|
|
297
337
|
const [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];
|
|
@@ -302,7 +342,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
302
342
|
if (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);
|
|
303
343
|
paintPlaceholders(context, scene);
|
|
304
344
|
if (this.#peaks) paintWave(context, scene, this.#peaks);
|
|
305
|
-
context.fillStyle =
|
|
345
|
+
context.fillStyle = ends;
|
|
306
346
|
for (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);
|
|
307
347
|
this.#paintMarkers(context, scene, isRebuilt);
|
|
308
348
|
}
|
|
@@ -319,6 +359,12 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
319
359
|
for (const probe of probes) probe.remove();
|
|
320
360
|
return new Map(kinds.map((kind, index) => [kind, read[index] ?? ""]));
|
|
321
361
|
}
|
|
362
|
+
async #repaintAfter(asked) {
|
|
363
|
+
await Promise.allSettled([asked]);
|
|
364
|
+
if (this.#asked !== asked) return;
|
|
365
|
+
this.#asked = void 0;
|
|
366
|
+
this.surface()?.invalidate();
|
|
367
|
+
}
|
|
322
368
|
#sourceSeconds() {
|
|
323
369
|
return this.#readTime?.() ?? this.playback();
|
|
324
370
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
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";
|
|
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";
|
|
7
|
-
import { stripBars, stripRegions } from "../lib/strip-scene.js";
|
|
8
|
+
import { stripBars, stripGroove, stripRegions } from "../lib/strip-scene.js";
|
|
8
9
|
|
|
9
10
|
//#region src/elements/wavestrip.ts
|
|
10
11
|
const colours = {
|
|
@@ -15,6 +16,7 @@ const colours = {
|
|
|
15
16
|
};
|
|
16
17
|
const lengths = {
|
|
17
18
|
gapRatio: "--_sonic-wavestrip-bar-gap-ratio",
|
|
19
|
+
grooveRatio: "--_sonic-wavestrip-groove-ratio",
|
|
18
20
|
pitch: "--_sonic-wavestrip-bar-pitch",
|
|
19
21
|
radiusRatio: "--_sonic-wavestrip-bar-radius-ratio"
|
|
20
22
|
};
|
|
@@ -72,11 +74,10 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
72
74
|
colours = colours;
|
|
73
75
|
lengths = lengths;
|
|
74
76
|
sheet = "wavestrip.css";
|
|
77
|
+
sizeProperty = "--_sonic-wavestrip-size";
|
|
75
78
|
#bars;
|
|
76
79
|
#buffered = [];
|
|
77
|
-
#escapeWatch;
|
|
78
80
|
#hover;
|
|
79
|
-
#isHoverDismissed = false;
|
|
80
81
|
#markerBand = createMarkerBand(requireChild(this.control, ".sonic-wavestrip-markers", HTMLDivElement));
|
|
81
82
|
#paintedKey = "";
|
|
82
83
|
#peaks;
|
|
@@ -86,13 +87,30 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
86
87
|
}
|
|
87
88
|
connect(signal) {
|
|
88
89
|
super.connect(signal);
|
|
89
|
-
this.#
|
|
90
|
+
this.#hover = bindHoverPreview(this.control, {
|
|
91
|
+
canShow: () => this.readout,
|
|
92
|
+
dismiss: () => {
|
|
93
|
+
this.render();
|
|
94
|
+
},
|
|
95
|
+
isTaken: () => this.isEditing() || this.isRevealed(),
|
|
96
|
+
show: () => {
|
|
97
|
+
this.renderReadout();
|
|
98
|
+
this.#placeReadout();
|
|
99
|
+
},
|
|
100
|
+
valueAt: (event) => {
|
|
101
|
+
const axis = this.#axis();
|
|
102
|
+
const at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);
|
|
103
|
+
const mapping = this.mapping();
|
|
104
|
+
return this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));
|
|
105
|
+
}
|
|
106
|
+
}, signal);
|
|
90
107
|
this.bindGestures(this.control, signal, (event) => this.#grab(event));
|
|
91
108
|
}
|
|
92
109
|
draw() {
|
|
93
110
|
const surface = this.surface();
|
|
94
|
-
if (this.isEditing() || this.isRevealed()) this.#hover
|
|
111
|
+
if (this.isEditing() || this.isRevealed()) this.#hover?.clear();
|
|
95
112
|
this.#placeReadout();
|
|
113
|
+
this.showCurrentMarker(this.playback());
|
|
96
114
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
97
115
|
}
|
|
98
116
|
isEmpty() {
|
|
@@ -114,7 +132,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
114
132
|
]);
|
|
115
133
|
}
|
|
116
134
|
previewValue() {
|
|
117
|
-
return this.#hover;
|
|
135
|
+
return this.#hover?.value();
|
|
118
136
|
}
|
|
119
137
|
renderMarkers() {
|
|
120
138
|
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
@@ -132,28 +150,18 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
132
150
|
};
|
|
133
151
|
}
|
|
134
152
|
#barsFor({ isDirty, look, size }) {
|
|
135
|
-
const peaks = this.#peaks;
|
|
136
|
-
if (!peaks || peaks.length === 0) return void 0;
|
|
137
153
|
if (!isDirty && this.#bars) return this.#bars;
|
|
154
|
+
const peaks = this.#peaks;
|
|
155
|
+
const groove = stripGroove(size, look.lengths.grooveRatio);
|
|
156
|
+
const drawn = groove ? [groove] : [];
|
|
157
|
+
const bars = peaks && peaks.length > 0 ? stripBars(peaks, size, look.lengths) : drawn;
|
|
158
|
+
this.#bars = void 0;
|
|
159
|
+
if (bars.length === 0) return void 0;
|
|
138
160
|
const path = new Path2D();
|
|
139
|
-
for (const { height, radius, width, x, y } of
|
|
161
|
+
for (const { height, radius, width, x, y } of bars) path.roundRect(x, y, width, height, radius);
|
|
140
162
|
this.#bars = path;
|
|
141
163
|
return path;
|
|
142
164
|
}
|
|
143
|
-
#bindHover(strip, signal) {
|
|
144
|
-
strip.addEventListener("pointermove", (event) => {
|
|
145
|
-
if (event.pointerType === "touch" || event.buttons !== 0) return;
|
|
146
|
-
if (!this.readout) return;
|
|
147
|
-
const axis = this.#axis();
|
|
148
|
-
const at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);
|
|
149
|
-
const mapping = this.mapping();
|
|
150
|
-
this.#hoverAt(this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at)));
|
|
151
|
-
this.#placeReadout();
|
|
152
|
-
}, { signal });
|
|
153
|
-
for (const type of ["pointerdown", "pointerleave"]) strip.addEventListener(type, () => {
|
|
154
|
-
this.#hoverAt(void 0);
|
|
155
|
-
}, { signal });
|
|
156
|
-
}
|
|
157
165
|
#grab(event) {
|
|
158
166
|
const axis = this.#axis();
|
|
159
167
|
const at = (event.clientX - axis.startPx) / axis.travelPx;
|
|
@@ -169,12 +177,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
169
177
|
outside
|
|
170
178
|
} : grabbed;
|
|
171
179
|
}
|
|
172
|
-
#hoverAt(value) {
|
|
173
|
-
if (value === void 0) this.#isHoverDismissed = false;
|
|
174
|
-
if (!this.#isHoverDismissed && !this.isEditing() && !this.isRevealed()) this.#hover = value;
|
|
175
|
-
this.renderReadout();
|
|
176
|
-
this.#watchEscape(value !== void 0);
|
|
177
|
-
}
|
|
178
180
|
#markerAt(event, { startPx, topPx, travelPx }) {
|
|
179
181
|
const index = this.#markerBand.at({
|
|
180
182
|
x: event.clientX - startPx,
|
|
@@ -201,22 +203,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
201
203
|
...isScrubbing ? { scrub: proportionOf(this.value) } : {}
|
|
202
204
|
}, width);
|
|
203
205
|
}
|
|
204
|
-
#watchEscape(isHovered) {
|
|
205
|
-
if (!isHovered) {
|
|
206
|
-
this.#escapeWatch?.abort();
|
|
207
|
-
this.#escapeWatch = void 0;
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (this.#escapeWatch) return;
|
|
211
|
-
const watch = new AbortController();
|
|
212
|
-
this.#escapeWatch = watch;
|
|
213
|
-
this.ownerDocument.addEventListener("keydown", (event) => {
|
|
214
|
-
if (event.key !== "Escape" || this.#hover === void 0) return;
|
|
215
|
-
this.#hover = void 0;
|
|
216
|
-
this.#isHoverDismissed = true;
|
|
217
|
-
this.render();
|
|
218
|
-
}, { signal: watch.signal });
|
|
219
|
-
}
|
|
220
206
|
};
|
|
221
207
|
|
|
222
208
|
//#endregion
|
|
@@ -2,12 +2,12 @@ import { clampProportion, toNumber } from "../lib/math.js";
|
|
|
2
2
|
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
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
5
6
|
import { ReadoutClaim } from "./readout-claim.js";
|
|
6
7
|
import { Readout } from "./readout.js";
|
|
7
8
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
8
9
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
9
10
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
10
|
-
import { bindDrag } from "../lib/pointer-drag.js";
|
|
11
11
|
import { resetKeys, valueMapping } from "../lib/value-mapping.js";
|
|
12
12
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
13
13
|
|
package/dist/default/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SonicMeter } from "./elements/meter.js";
|
|
|
6
6
|
import { SonicNumber } from "./elements/number.js";
|
|
7
7
|
import { SonicSegmented } from "./elements/segmented.js";
|
|
8
8
|
import { SonicSlider } from "./elements/slider.js";
|
|
9
|
+
import { SonicSpectrum } from "./elements/spectrum.js";
|
|
9
10
|
import { SonicSplit } from "./elements/split.js";
|
|
10
11
|
import { SonicSwitch } from "./elements/switch.js";
|
|
11
12
|
import { SonicWaveform } from "./elements/waveform.js";
|
|
@@ -14,4 +15,4 @@ import { SonicXy } from "./elements/xy.js";
|
|
|
14
15
|
import { crossfadeGains } from "./lib/crossfade.js";
|
|
15
16
|
import { formatPercent, parsePercent } from "./lib/percent.js";
|
|
16
17
|
|
|
17
|
-
export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSplit, SonicSwitch, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatPercent, parsePercent };
|
|
18
|
+
export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatPercent, parsePercent };
|