@xsynaptic/sonic-ui 0.6.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -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/dial.js +12 -3
- package/dist/default/elements/display-element.js +77 -0
- package/dist/default/elements/envelope.js +3 -3
- package/dist/default/elements/modulation.js +45 -0
- package/dist/default/elements/readout-claim.js +8 -3
- package/dist/default/elements/slider.js +68 -4
- package/dist/default/elements/spectrum.js +230 -0
- package/dist/default/elements/value-element.js +114 -317
- package/dist/default/elements/value-gestures.js +301 -0
- package/dist/default/elements/wave-element.js +59 -9
- package/dist/default/elements/waveform.js +61 -12
- package/dist/default/elements/wavestrip.js +25 -49
- package/dist/default/elements/xy.js +10 -21
- package/dist/default/index.js +2 -1
- package/dist/default/lib/asked-value.js +21 -0
- package/dist/default/lib/canvas-surface.js +10 -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 +12 -6
- package/dist/default/lib/pointer-drag.js +7 -2
- package/dist/default/lib/read-ms-property.js +10 -0
- 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/dial.js +12 -3
- package/dist/dev/elements/dial.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 +3 -3
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/modulation.js +46 -0
- package/dist/dev/elements/modulation.js.map +1 -0
- package/dist/dev/elements/readout-claim.js +8 -3
- package/dist/dev/elements/readout-claim.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +5 -0
- package/dist/dev/elements/slider.js +68 -4
- 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 +13 -14
- package/dist/dev/elements/value-element.js +114 -317
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.d.ts +10 -0
- package/dist/dev/elements/value-gestures.js +302 -0
- package/dist/dev/elements/value-gestures.js.map +1 -0
- package/dist/dev/elements/wave-element.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 +61 -12
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +3 -1
- package/dist/dev/elements/wavestrip.js +25 -49
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +10 -21
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +4 -1
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/asked-value.js +22 -0
- package/dist/dev/lib/asked-value.js.map +1 -0
- package/dist/dev/lib/canvas-surface.d.ts +5 -3
- package/dist/dev/lib/canvas-surface.js +10 -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 +12 -6
- 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/read-ms-property.js +11 -0
- package/dist/dev/lib/read-ms-property.js.map +1 -0
- 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 +36 -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 +48 -16
- package/dist/styles/wavestrip.css +26 -5
- package/package.json +2 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { clamp } from "../lib/math.js";
|
|
2
|
+
import { requireChild } from "../lib/render.js";
|
|
3
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
4
|
+
import { ReadoutClaim, revealDelay } from "./readout-claim.js";
|
|
5
|
+
import { ValueEntry } from "./value-entry.js";
|
|
6
|
+
import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
|
|
7
|
+
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
8
|
+
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
9
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
10
|
+
import { resetKeys } from "../lib/value-mapping.js";
|
|
11
|
+
|
|
12
|
+
//#region src/elements/value-gestures.ts
|
|
13
|
+
const detentZonePx = 8;
|
|
14
|
+
var ValueGestures = class {
|
|
15
|
+
#claim = new ReadoutClaim(() => {
|
|
16
|
+
this.#host.holdChanged();
|
|
17
|
+
});
|
|
18
|
+
#control;
|
|
19
|
+
#entry;
|
|
20
|
+
#host;
|
|
21
|
+
#isRevealed = false;
|
|
22
|
+
#keyScrub;
|
|
23
|
+
#pointerDrag;
|
|
24
|
+
constructor(host, control) {
|
|
25
|
+
this.#host = host;
|
|
26
|
+
this.#control = control;
|
|
27
|
+
}
|
|
28
|
+
bind(signal, grab) {
|
|
29
|
+
const control = this.#control;
|
|
30
|
+
const host = this.#host;
|
|
31
|
+
const entry = this.#bindEntry(signal);
|
|
32
|
+
this.#pointerDrag = bindDrag(control, {
|
|
33
|
+
cancel: (drag) => {
|
|
34
|
+
if (drag.isKeptOnCancel !== true) host.input(drag.fromValue);
|
|
35
|
+
},
|
|
36
|
+
grab: (event) => {
|
|
37
|
+
if (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;
|
|
38
|
+
host.toggleState("springing", false);
|
|
39
|
+
if (event.pointerType === "touch") event.preventDefault();
|
|
40
|
+
focusByPointer(control);
|
|
41
|
+
if (isResetPress(event)) {
|
|
42
|
+
this.#reset();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const fromValue = host.value();
|
|
46
|
+
const axis = grab(event);
|
|
47
|
+
if (!axis) return;
|
|
48
|
+
const drag = {
|
|
49
|
+
...axis,
|
|
50
|
+
fromValue,
|
|
51
|
+
isOutside: false,
|
|
52
|
+
isRevealed: false,
|
|
53
|
+
pointerType: event.pointerType,
|
|
54
|
+
revealTimer: void 0,
|
|
55
|
+
state: this.#startDrag(axis, event)
|
|
56
|
+
};
|
|
57
|
+
drag.revealTimer = setTimeout(() => {
|
|
58
|
+
this.#reveal(drag);
|
|
59
|
+
}, revealDelay(control));
|
|
60
|
+
return drag;
|
|
61
|
+
},
|
|
62
|
+
lift: (drag, event) => {
|
|
63
|
+
const tap = drag.state.isEngaged ? void 0 : host.tapTarget();
|
|
64
|
+
if (tap !== void 0) {
|
|
65
|
+
entry.forgetPress();
|
|
66
|
+
this.#commit(tap);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!entry.press(event) || host.springTarget() !== void 0) return;
|
|
70
|
+
const gesture = host.doublePress();
|
|
71
|
+
if (gesture === "reset") this.#reset();
|
|
72
|
+
else if (gesture === "entry") entry.open();
|
|
73
|
+
},
|
|
74
|
+
move: (drag, event) => {
|
|
75
|
+
this.#dragTo(drag, event);
|
|
76
|
+
},
|
|
77
|
+
release: (drag) => {
|
|
78
|
+
this.#releaseDrag(drag);
|
|
79
|
+
},
|
|
80
|
+
toggle: (isDragging) => {
|
|
81
|
+
host.toggleState("dragging", isDragging);
|
|
82
|
+
}
|
|
83
|
+
}, signal);
|
|
84
|
+
control.addEventListener("pointercancel", () => {
|
|
85
|
+
entry.forgetPress();
|
|
86
|
+
}, { signal });
|
|
87
|
+
this.#bindKeys(entry, signal);
|
|
88
|
+
signal.addEventListener("abort", () => {
|
|
89
|
+
this.#endKeyScrub();
|
|
90
|
+
this.#concealKeyReveal();
|
|
91
|
+
}, { once: true });
|
|
92
|
+
}
|
|
93
|
+
end() {
|
|
94
|
+
this.#entry?.close(false);
|
|
95
|
+
this.#pointerDrag?.end();
|
|
96
|
+
this.#endKeyScrub();
|
|
97
|
+
}
|
|
98
|
+
heldFrom() {
|
|
99
|
+
return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
|
|
100
|
+
}
|
|
101
|
+
isEditing() {
|
|
102
|
+
return this.#entry?.isOpen === true;
|
|
103
|
+
}
|
|
104
|
+
isHeld() {
|
|
105
|
+
return this.#dragging() !== void 0 || this.#keyScrub !== void 0;
|
|
106
|
+
}
|
|
107
|
+
isRevealed() {
|
|
108
|
+
return this.#claim.isRevealed;
|
|
109
|
+
}
|
|
110
|
+
pointerType() {
|
|
111
|
+
return this.#dragging()?.pointerType;
|
|
112
|
+
}
|
|
113
|
+
#bindEntry(signal) {
|
|
114
|
+
const control = this.#control;
|
|
115
|
+
const host = this.#host;
|
|
116
|
+
const input = requireChild(control, "input", HTMLInputElement);
|
|
117
|
+
input.id = host.entryId;
|
|
118
|
+
const entry = new ValueEntry(input, {
|
|
119
|
+
commit: (text) => {
|
|
120
|
+
this.#commit(host.parse(text));
|
|
121
|
+
},
|
|
122
|
+
text: host.entryText,
|
|
123
|
+
toggle: (isOpen) => {
|
|
124
|
+
if (isOpen) host.forwardNaming(input, true);
|
|
125
|
+
host.toggleState("editing", isOpen);
|
|
126
|
+
host.render();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
this.#entry = entry;
|
|
130
|
+
entry.bind(control, signal);
|
|
131
|
+
return entry;
|
|
132
|
+
}
|
|
133
|
+
#bindKeys(entry, signal) {
|
|
134
|
+
const control = this.#control;
|
|
135
|
+
const host = this.#host;
|
|
136
|
+
control.addEventListener("keydown", (event) => {
|
|
137
|
+
if (host.isDisabled() || event.defaultPrevented || event.target !== control) return;
|
|
138
|
+
host.toggleState("springing", false);
|
|
139
|
+
if (event.key === "Enter") {
|
|
140
|
+
if (host.springTarget() !== void 0) return;
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
entry.open();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const next = this.#keyTarget(event.key);
|
|
146
|
+
if (next === void 0) return;
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
this.#keyTo(event, next);
|
|
149
|
+
this.#claim.reveal("keys");
|
|
150
|
+
host.holdChanged();
|
|
151
|
+
}, { signal });
|
|
152
|
+
control.addEventListener("keyup", (event) => {
|
|
153
|
+
const isMeta = event.key === "Meta";
|
|
154
|
+
if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
|
|
155
|
+
if (isMeta && this.#dragging()) return;
|
|
156
|
+
if (isMeta || host.mapping().keyTarget(event.key, host.value()) !== void 0) this.#springBack();
|
|
157
|
+
}, { signal });
|
|
158
|
+
control.addEventListener("blur", () => {
|
|
159
|
+
this.#endKeyScrub();
|
|
160
|
+
this.#concealKeyReveal();
|
|
161
|
+
this.#springBack();
|
|
162
|
+
}, { signal });
|
|
163
|
+
}
|
|
164
|
+
#commit(next, direction = 0) {
|
|
165
|
+
const host = this.#host;
|
|
166
|
+
const landed = Number.isFinite(next) ? host.land(next, direction) : next;
|
|
167
|
+
if (host.input(landed)) host.dispatch("change");
|
|
168
|
+
}
|
|
169
|
+
#concealKeyReveal() {
|
|
170
|
+
if (this.#claim.conceal("keys")) this.#host.holdChanged();
|
|
171
|
+
}
|
|
172
|
+
#dragging() {
|
|
173
|
+
return this.#pointerDrag?.current();
|
|
174
|
+
}
|
|
175
|
+
#dragTo(drag, event) {
|
|
176
|
+
const host = this.#host;
|
|
177
|
+
const isOutside = drag.outside?.(event) === true;
|
|
178
|
+
const wasEngaged = drag.state.isEngaged;
|
|
179
|
+
const mapping = host.mapping();
|
|
180
|
+
const { state, value } = stepDrag(mapping, drag.state, {
|
|
181
|
+
isFine: event.shiftKey,
|
|
182
|
+
isOutside,
|
|
183
|
+
position: drag.position(event)
|
|
184
|
+
});
|
|
185
|
+
drag.state = this.#withinLimit(mapping, state);
|
|
186
|
+
if (value === void 0) return;
|
|
187
|
+
if (!wasEngaged) this.#reveal(drag);
|
|
188
|
+
const isCrossing = isOutside !== drag.isOutside;
|
|
189
|
+
if (isCrossing) {
|
|
190
|
+
drag.isOutside = isOutside;
|
|
191
|
+
host.toggleState("cancelling", isOutside);
|
|
192
|
+
}
|
|
193
|
+
host.input(isOutside ? drag.fromValue : value);
|
|
194
|
+
if (isCrossing) this.#renderDragReveal(drag);
|
|
195
|
+
}
|
|
196
|
+
#endKeyScrub() {
|
|
197
|
+
const host = this.#host;
|
|
198
|
+
const scrub = this.#keyScrub;
|
|
199
|
+
if (!scrub) return;
|
|
200
|
+
this.#keyScrub = void 0;
|
|
201
|
+
host.holdChanged();
|
|
202
|
+
if (host.value() !== scrub.fromValue) host.dispatch("change");
|
|
203
|
+
}
|
|
204
|
+
#keyTarget(key) {
|
|
205
|
+
const host = this.#host;
|
|
206
|
+
if (resetKeys.has(key)) return host.default();
|
|
207
|
+
return host.mapping().keyTarget(key, host.value(), host.keyStep());
|
|
208
|
+
}
|
|
209
|
+
#keyTo(event, next) {
|
|
210
|
+
const host = this.#host;
|
|
211
|
+
if (event.repeat && host.scrubsKeyRepeat()) {
|
|
212
|
+
this.#scrubKey(event.key, next);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.#endKeyScrub();
|
|
216
|
+
this.#commit(next, next > host.value() ? 1 : -1);
|
|
217
|
+
}
|
|
218
|
+
#releaseDrag(drag) {
|
|
219
|
+
const host = this.#host;
|
|
220
|
+
clearTimeout(drag.revealTimer);
|
|
221
|
+
this.#claim.conceal("drag");
|
|
222
|
+
host.toggleState("cancelling", false);
|
|
223
|
+
host.holdChanged();
|
|
224
|
+
this.#showRevealed(false);
|
|
225
|
+
if (host.value() !== drag.fromValue) host.dispatch("change");
|
|
226
|
+
this.#springBack();
|
|
227
|
+
}
|
|
228
|
+
#renderDragReveal(drag) {
|
|
229
|
+
const isRevealed = drag.isRevealed && !drag.isOutside;
|
|
230
|
+
if (isRevealed) this.#claim.reveal("drag");
|
|
231
|
+
else this.#claim.conceal("drag");
|
|
232
|
+
this.#host.holdChanged();
|
|
233
|
+
this.#showRevealed(isRevealed);
|
|
234
|
+
}
|
|
235
|
+
#reset() {
|
|
236
|
+
this.#commit(this.#host.default() ?? NaN);
|
|
237
|
+
}
|
|
238
|
+
#reveal(drag) {
|
|
239
|
+
if (drag.isRevealed || this.#dragging() !== drag) return;
|
|
240
|
+
drag.isRevealed = true;
|
|
241
|
+
this.#renderDragReveal(drag);
|
|
242
|
+
}
|
|
243
|
+
#scrubKey(key, next) {
|
|
244
|
+
const host = this.#host;
|
|
245
|
+
if (this.#keyScrub?.key !== key) {
|
|
246
|
+
this.#endKeyScrub();
|
|
247
|
+
this.#keyScrub = {
|
|
248
|
+
fromValue: host.value(),
|
|
249
|
+
key
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
host.input(next);
|
|
253
|
+
}
|
|
254
|
+
#showRevealed(isRevealed) {
|
|
255
|
+
if (isRevealed === this.#isRevealed) return;
|
|
256
|
+
this.#isRevealed = isRevealed;
|
|
257
|
+
this.#host.toggleState("revealed", isRevealed);
|
|
258
|
+
this.#host.dispatch("sonic-reveal");
|
|
259
|
+
}
|
|
260
|
+
#springBack() {
|
|
261
|
+
const host = this.#host;
|
|
262
|
+
const target = host.springTarget();
|
|
263
|
+
if (target === void 0) return;
|
|
264
|
+
if (host.mapping().snap(target) !== host.value()) host.toggleState("springing", true);
|
|
265
|
+
this.#commit(target);
|
|
266
|
+
}
|
|
267
|
+
#startDrag(axis, event) {
|
|
268
|
+
const host = this.#host;
|
|
269
|
+
const mapping = host.mapping();
|
|
270
|
+
const detent = host.detent();
|
|
271
|
+
const value = host.value();
|
|
272
|
+
const start = {
|
|
273
|
+
from: value,
|
|
274
|
+
position: axis.position(event),
|
|
275
|
+
proportion: axis.fromProportion ?? mapping.proportionOf(value),
|
|
276
|
+
thresholdPx: dragThresholdPx(event.pointerType),
|
|
277
|
+
travelPx: axis.travelPx
|
|
278
|
+
};
|
|
279
|
+
if (detent === void 0) return startDrag(mapping, start);
|
|
280
|
+
const zonePx = readPxProperty(getComputedStyle(this.#control), "--_sonic-detent-zone", detentZonePx);
|
|
281
|
+
return startDrag(mapping, {
|
|
282
|
+
...start,
|
|
283
|
+
detent: {
|
|
284
|
+
value: detent,
|
|
285
|
+
zone: zonePx / axis.travelPx
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
#withinLimit(mapping, state) {
|
|
290
|
+
const limit = this.#host.limit();
|
|
291
|
+
if (!limit || mapping.isWrapping) return state;
|
|
292
|
+
const [low, high] = limit;
|
|
293
|
+
return {
|
|
294
|
+
...state,
|
|
295
|
+
rawProportion: clamp(state.rawProportion, mapping.proportionOf(low), mapping.proportionOf(high))
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
export { ValueGestures };
|
|
@@ -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,19 @@ 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",
|
|
186
|
-
|
|
211
|
+
fadeProperty: "--_sonic-waveform-label-fade",
|
|
212
|
+
insetProperty: "--_sonic-waveform-label-inset",
|
|
213
|
+
parkProperty: "--_sonic-waveform-label-park"
|
|
187
214
|
});
|
|
188
215
|
connectedCallback() {
|
|
189
|
-
this.upgradeProperties("peaks", "pending", "playing", "playbackRate", "readTime", "requestPeaks", "zoom");
|
|
216
|
+
this.upgradeProperties("peaks", "pending", "pendingDelay", "playing", "playbackRate", "readTime", "reducedMotion", "requestPeaks", "zoom");
|
|
190
217
|
super.connectedCallback();
|
|
191
218
|
}
|
|
192
219
|
connect(signal) {
|
|
@@ -197,13 +224,16 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
197
224
|
}, { signal });
|
|
198
225
|
}
|
|
199
226
|
draw() {
|
|
200
|
-
this.surface()
|
|
227
|
+
const surface = this.surface();
|
|
228
|
+
surface?.requestFrame();
|
|
229
|
+
if (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());
|
|
201
230
|
}
|
|
202
231
|
isEmpty() {
|
|
203
232
|
return !this.#peaks || this.#peaks.samples.length === 0;
|
|
204
233
|
}
|
|
205
234
|
paint(context, { frameMs, isDirty, isRebuilt, look, size }) {
|
|
206
235
|
const held = this.#held();
|
|
236
|
+
const pending = this.#duePending();
|
|
207
237
|
const timeline = frameTimeline({
|
|
208
238
|
clockSeconds: this.#clock.read(frameMs, {
|
|
209
239
|
isPlaying: this.playing,
|
|
@@ -212,9 +242,10 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
212
242
|
}),
|
|
213
243
|
frameMs,
|
|
214
244
|
...held ? { held } : {},
|
|
215
|
-
isPaged: look.isReducedMotion,
|
|
245
|
+
isPaged: look.isReducedMotion && this.reducedMotion === "page",
|
|
216
246
|
isPlaying: this.playing,
|
|
217
|
-
|
|
247
|
+
isStill: look.isReducedMotion,
|
|
248
|
+
pending,
|
|
218
249
|
range: [this.min, Math.max(this.min, this.max)],
|
|
219
250
|
size,
|
|
220
251
|
zoom: this.zoom
|
|
@@ -226,6 +257,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
226
257
|
};
|
|
227
258
|
placeLine(this.#playhead, timeline.playheadAt);
|
|
228
259
|
placeLine(this.#ghost, timeline.ghostAt);
|
|
260
|
+
this.showCurrentMarker(seconds);
|
|
229
261
|
this.toggleState("pending", view.pending.length > 0);
|
|
230
262
|
this.#rider.place({
|
|
231
263
|
playheadSeconds: seconds,
|
|
@@ -239,7 +271,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
239
271
|
...view,
|
|
240
272
|
colours: look.colours
|
|
241
273
|
}, isRebuilt);
|
|
242
|
-
if (timeline.wanted) this.#
|
|
274
|
+
if (timeline.wanted) this.#ask(timeline.wanted);
|
|
243
275
|
}
|
|
244
276
|
if (timeline.isMoving) this.surface()?.requestFrame();
|
|
245
277
|
}
|
|
@@ -253,6 +285,17 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
253
285
|
}]));
|
|
254
286
|
this.surface()?.rebuild();
|
|
255
287
|
}
|
|
288
|
+
#ask(wanted) {
|
|
289
|
+
const asked = this.#requestPeaks?.(...wanted);
|
|
290
|
+
if (!(asked instanceof Promise)) return;
|
|
291
|
+
this.#asked = asked;
|
|
292
|
+
this.#repaintAfter(asked);
|
|
293
|
+
}
|
|
294
|
+
#duePending() {
|
|
295
|
+
const due = dueRegions(this.#pending, this.#pendingListedMs, [performance.now(), this.pendingDelay]);
|
|
296
|
+
if (due.length < this.#pending.length) this.surface()?.requestFrame();
|
|
297
|
+
return due;
|
|
298
|
+
}
|
|
256
299
|
#grab() {
|
|
257
300
|
const drawn = this.#drawn ?? {
|
|
258
301
|
seconds: this.value,
|
|
@@ -291,7 +334,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
291
334
|
#paintScene(context, scene, isRebuilt) {
|
|
292
335
|
const { dpr, height, pixelsPerSecond, startSeconds, width } = scene;
|
|
293
336
|
const x = (seconds) => (seconds - startSeconds) * pixelsPerSecond;
|
|
294
|
-
const { grid } = scene.colours;
|
|
337
|
+
const { ends, grid } = scene.colours;
|
|
295
338
|
const line = Math.max(1, Math.round(dpr));
|
|
296
339
|
const [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];
|
|
297
340
|
const [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];
|
|
@@ -302,7 +345,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
302
345
|
if (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);
|
|
303
346
|
paintPlaceholders(context, scene);
|
|
304
347
|
if (this.#peaks) paintWave(context, scene, this.#peaks);
|
|
305
|
-
context.fillStyle =
|
|
348
|
+
context.fillStyle = ends;
|
|
306
349
|
for (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);
|
|
307
350
|
this.#paintMarkers(context, scene, isRebuilt);
|
|
308
351
|
}
|
|
@@ -319,6 +362,12 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
319
362
|
for (const probe of probes) probe.remove();
|
|
320
363
|
return new Map(kinds.map((kind, index) => [kind, read[index] ?? ""]));
|
|
321
364
|
}
|
|
365
|
+
async #repaintAfter(asked) {
|
|
366
|
+
await Promise.allSettled([asked]);
|
|
367
|
+
if (this.#asked !== asked) return;
|
|
368
|
+
this.#asked = void 0;
|
|
369
|
+
this.surface()?.invalidate();
|
|
370
|
+
}
|
|
322
371
|
#sourceSeconds() {
|
|
323
372
|
return this.#readTime?.() ?? this.playback();
|
|
324
373
|
}
|