@xsynaptic/sonic-ui 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +122 -19
- package/dist/default/elements/button.js +2 -5
- package/dist/default/elements/dial.js +2 -7
- package/dist/default/elements/display-element.js +9 -12
- package/dist/default/elements/envelope.js +31 -78
- package/dist/default/elements/field-gesture.js +81 -0
- package/dist/default/elements/meter.js +4 -1
- package/dist/default/elements/number.js +0 -7
- package/dist/default/elements/position-group.js +42 -26
- package/dist/default/elements/radio-group.js +59 -56
- package/dist/default/elements/scale.js +3 -3
- package/dist/default/elements/segmented.js +0 -5
- package/dist/default/elements/slider.js +11 -20
- package/dist/default/elements/sonic-element.js +3 -1
- package/dist/default/elements/split.js +14 -13
- package/dist/default/elements/switch.js +31 -47
- package/dist/default/elements/toggle.js +20 -49
- package/dist/default/elements/value-element.js +72 -94
- package/dist/default/elements/value-gestures.js +22 -22
- package/dist/default/elements/wave-element.js +16 -20
- package/dist/default/elements/waveform.js +42 -241
- package/dist/default/elements/wavestrip.js +10 -5
- package/dist/default/elements/xy.js +67 -119
- package/dist/default/index.js +2 -1
- package/dist/default/lib/canvas-surface.js +25 -0
- package/dist/default/lib/clock.js +23 -0
- package/dist/default/lib/crossfade.js +1 -0
- package/dist/default/lib/envelope-curve.js +1 -0
- package/dist/default/lib/peaks-request.js +44 -0
- package/dist/default/lib/positions.js +58 -0
- package/dist/default/lib/scrub.js +12 -0
- package/dist/default/lib/spoken-duration.js +30 -0
- package/dist/default/lib/value-model.js +62 -0
- package/dist/default/lib/waveform-buckets.js +7 -1
- package/dist/default/lib/waveform-scene.js +108 -0
- package/dist/default/lib/zoom-gesture.js +97 -1
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +14 -5
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +2 -0
- package/dist/dev/elements/dial.js +16 -7
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/display-element.d.ts +5 -3
- package/dist/dev/elements/display-element.js +11 -9
- package/dist/dev/elements/display-element.js.map +1 -1
- package/dist/dev/elements/envelope.js +48 -78
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +82 -0
- package/dist/dev/elements/field-gesture.js.map +1 -0
- package/dist/dev/elements/meter.d.ts +10 -5
- package/dist/dev/elements/meter.js +5 -1
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.js +13 -7
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/position-group.d.ts +12 -6
- package/dist/dev/elements/position-group.js +42 -26
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +11 -7
- package/dist/dev/elements/radio-group.js +59 -56
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/scale.js +3 -3
- package/dist/dev/elements/scale.js.map +1 -1
- package/dist/dev/elements/segmented.js +12 -5
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +25 -20
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +13 -2
- package/dist/dev/elements/sonic-element.js +63 -11
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.d.ts +1 -0
- package/dist/dev/elements/split.js +14 -13
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +2 -2
- package/dist/dev/elements/switch.js +38 -47
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/toggle.js +32 -49
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +11 -5
- package/dist/dev/elements/value-element.js +72 -94
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +22 -22
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +8 -5
- package/dist/dev/elements/wave-element.js +30 -20
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +20 -22
- package/dist/dev/elements/waveform.js +42 -241
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +5 -1
- package/dist/dev/elements/wavestrip.js +10 -5
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +81 -119
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +5 -3
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +25 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/clock.d.ts +5 -0
- package/dist/dev/lib/clock.js +24 -0
- package/dist/dev/lib/clock.js.map +1 -0
- package/dist/dev/lib/crossfade.d.ts +1 -0
- package/dist/dev/lib/crossfade.js +1 -0
- package/dist/dev/lib/crossfade.js.map +1 -1
- package/dist/dev/lib/envelope-curve.d.ts +1 -0
- package/dist/dev/lib/envelope-curve.js +1 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -1
- package/dist/dev/lib/mirror-children.js.map +1 -1
- package/dist/dev/lib/owned-control.js.map +1 -1
- package/dist/dev/lib/peaks-request.d.ts +5 -0
- package/dist/dev/lib/peaks-request.js +45 -0
- package/dist/dev/lib/peaks-request.js.map +1 -0
- package/dist/dev/lib/positions.d.ts +7 -0
- package/dist/dev/lib/positions.js +59 -0
- package/dist/dev/lib/positions.js.map +1 -0
- package/dist/dev/lib/scrub.d.ts +8 -0
- package/dist/dev/lib/scrub.js +13 -0
- package/dist/dev/lib/scrub.js.map +1 -0
- package/dist/dev/lib/spoken-duration.js +31 -0
- package/dist/dev/lib/spoken-duration.js.map +1 -0
- package/dist/dev/lib/time-regions.d.ts +1 -0
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/dev/lib/units.d.ts +8 -0
- package/dist/dev/lib/value-model.js +63 -0
- package/dist/dev/lib/value-model.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +2 -1
- package/dist/dev/lib/waveform-buckets.js +7 -1
- package/dist/dev/lib/waveform-buckets.js.map +1 -1
- package/dist/dev/lib/waveform-scene.d.ts +11 -0
- package/dist/dev/lib/waveform-scene.js +109 -0
- package/dist/dev/lib/waveform-scene.js.map +1 -0
- package/dist/dev/lib/zoom-gesture.js +97 -1
- package/dist/dev/lib/zoom-gesture.js.map +1 -1
- package/dist/skins/amber.css +1 -0
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +1 -0
- package/dist/skins/slate.css +1 -0
- package/dist/styles/button.css +45 -11
- package/dist/styles/dial.css +10 -9
- package/dist/styles/envelope.css +10 -4
- package/dist/styles/led.css +5 -1
- package/dist/styles/material/arc.css +3 -2
- package/dist/styles/material/bracket.css +2 -2
- package/dist/styles/material/cap.css +33 -12
- package/dist/styles/material/core.css +4 -0
- package/dist/styles/material/glass.css +1 -1
- package/dist/styles/material/groove.css +4 -3
- package/dist/styles/material/keycap.css +3 -2
- package/dist/styles/material/lens.css +1 -1
- package/dist/styles/material/pane.css +5 -4
- package/dist/styles/material/readout.css +6 -6
- package/dist/styles/material/well.css +2 -2
- package/dist/styles/meter.css +2 -7
- package/dist/styles/number.css +5 -1
- package/dist/styles/panel.css +4 -3
- package/dist/styles/ring.css +1 -1
- package/dist/styles/screen.css +5 -1
- package/dist/styles/segmented.css +17 -4
- package/dist/styles/slider.css +9 -3
- package/dist/styles/spectrum.css +16 -3
- package/dist/styles/switch.css +35 -10
- package/dist/styles/toggle.css +14 -7
- package/dist/styles/waveform.css +17 -5
- package/dist/styles/wavestrip.css +11 -3
- package/dist/styles/xy.css +9 -5
- package/package.json +1 -6
- package/dist/default/dat.js +0 -51
- package/dist/default/lib/asked-value.js +0 -21
- package/dist/default/lib/fill-watch.js +0 -29
- package/dist/dev/dat.d.ts +0 -19
- package/dist/dev/dat.js +0 -52
- package/dist/dev/dat.js.map +0 -1
- package/dist/dev/lib/asked-value.js +0 -22
- package/dist/dev/lib/asked-value.js.map +0 -1
- package/dist/dev/lib/fill-watch.js +0 -30
- package/dist/dev/lib/fill-watch.js.map +0 -1
|
@@ -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
|
};
|
|
@@ -2,15 +2,14 @@ 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";
|
|
6
5
|
import { Readout } from "./readout.js";
|
|
7
|
-
import { ReadoutClaim
|
|
8
|
-
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
6
|
+
import { ReadoutClaim } from "./readout-claim.js";
|
|
9
7
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
10
8
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
11
|
-
import { resetKeys
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
9
|
+
import { resetKeys } from "../lib/value-mapping.js";
|
|
10
|
+
import { createValueModel } from "../lib/value-model.js";
|
|
11
|
+
import { pointerPosition } from "../lib/field.js";
|
|
12
|
+
import { bindFieldGesture } from "./field-gesture.js";
|
|
14
13
|
|
|
15
14
|
//#region src/elements/xy.ts
|
|
16
15
|
const axes = ["x", "y"];
|
|
@@ -75,7 +74,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
75
74
|
this.render();
|
|
76
75
|
}
|
|
77
76
|
get pointerType() {
|
|
78
|
-
return this.#pointerType;
|
|
77
|
+
return this.#gesture?.pointerType();
|
|
79
78
|
}
|
|
80
79
|
get readout() {
|
|
81
80
|
return this.hasAttribute("readout");
|
|
@@ -84,7 +83,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
84
83
|
this.reflect("readout", isEnabled);
|
|
85
84
|
}
|
|
86
85
|
get x() {
|
|
87
|
-
return this.#
|
|
86
|
+
return this.#models.x.value;
|
|
88
87
|
}
|
|
89
88
|
set x(next) {
|
|
90
89
|
this.#write("x", next);
|
|
@@ -138,7 +137,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
138
137
|
this.reflect("x-taper", curve);
|
|
139
138
|
}
|
|
140
139
|
get y() {
|
|
141
|
-
return this.#
|
|
140
|
+
return this.#models.y.value;
|
|
142
141
|
}
|
|
143
142
|
set y(next) {
|
|
144
143
|
this.#write("y", next);
|
|
@@ -198,53 +197,33 @@ var SonicXy = class extends SonicFormElement {
|
|
|
198
197
|
#xy = renderXy();
|
|
199
198
|
#field = requireChild(this.#xy, ".sonic-xy-field", HTMLDivElement);
|
|
200
199
|
#formatValue;
|
|
201
|
-
#
|
|
200
|
+
#gesture;
|
|
201
|
+
#models = {
|
|
202
|
+
x: createValueModel(() => this.#spec("x")),
|
|
203
|
+
y: createValueModel(() => this.#spec("y"))
|
|
204
|
+
};
|
|
202
205
|
#parts = {
|
|
203
206
|
x: requireChild(this.#xy, "[data-sonic-axis=\"x\"]", HTMLDivElement),
|
|
204
207
|
y: requireChild(this.#xy, "[data-sonic-axis=\"y\"]", HTMLDivElement)
|
|
205
208
|
};
|
|
206
|
-
#pointerDrag;
|
|
207
|
-
#pointerType;
|
|
208
209
|
#puck = requireChild(this.#xy, ".sonic-xy-puck", HTMLDivElement);
|
|
209
210
|
#readout = new Readout(requireChild(this.#xy, ".sonic-xy-readout", HTMLDivElement));
|
|
210
|
-
#values = {
|
|
211
|
-
x: {
|
|
212
|
-
asked: void 0,
|
|
213
|
-
value: 0
|
|
214
|
-
},
|
|
215
|
-
y: {
|
|
216
|
-
asked: void 0,
|
|
217
|
-
value: 0
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
211
|
attributeChangedCallback(name) {
|
|
221
|
-
this.#
|
|
222
|
-
if (name === "
|
|
223
|
-
|
|
224
|
-
this.#values[name].asked = void 0;
|
|
225
|
-
}
|
|
226
|
-
if (name === "disabled" && this.isDisabled()) this.#pointerDrag?.end();
|
|
227
|
-
this.#refresh();
|
|
212
|
+
if ((name === "x" || name === "y") && this.#gesture?.current()) return;
|
|
213
|
+
if (name === "disabled" && this.isDisabled()) this.#gesture?.end();
|
|
214
|
+
this.#respec(name);
|
|
228
215
|
}
|
|
229
216
|
connectedCallback() {
|
|
230
217
|
this.upgradeProperties("readout", "xDefault", "xLabel", "xMax", "xMidpoint", "xMin", "xOrigin", "xStep", "xTaper", "yDefault", "yLabel", "yMax", "yMidpoint", "yMin", "yOrigin", "yStep", "yTaper", "formatValue", "x", "y");
|
|
231
218
|
super.connectedCallback();
|
|
232
219
|
}
|
|
233
220
|
formResetCallback() {
|
|
234
|
-
|
|
235
|
-
this.#refresh();
|
|
221
|
+
this.#respec(...axes);
|
|
236
222
|
}
|
|
237
223
|
connect(signal) {
|
|
238
224
|
const xy = this.#xy;
|
|
239
225
|
this.keepControl(xy, signal);
|
|
240
226
|
this.render();
|
|
241
|
-
this.checkStyles(xy, "xy.css", [
|
|
242
|
-
"cap",
|
|
243
|
-
"glass",
|
|
244
|
-
"pane",
|
|
245
|
-
"well",
|
|
246
|
-
"readout"
|
|
247
|
-
]);
|
|
248
227
|
this.#bindPointer(xy, signal);
|
|
249
228
|
this.#bindKeys(xy, signal);
|
|
250
229
|
}
|
|
@@ -254,11 +233,11 @@ var SonicXy = class extends SonicFormElement {
|
|
|
254
233
|
render() {
|
|
255
234
|
if (!this.isBound()) return;
|
|
256
235
|
const { style } = this.#xy;
|
|
257
|
-
const mappings = this.#mappings();
|
|
258
236
|
for (const axis of axes) {
|
|
259
|
-
const
|
|
237
|
+
const model = this.#models[axis];
|
|
238
|
+
const mapping = model.mapping();
|
|
260
239
|
const origin = this.optionalNumberAttribute(`${axis}-origin`) ?? mapping.bounds[0];
|
|
261
|
-
style.setProperty(`--_sonic-xy-${axis}`, String(mapping.proportionOf(
|
|
240
|
+
style.setProperty(`--_sonic-xy-${axis}`, String(mapping.proportionOf(model.value)));
|
|
262
241
|
style.setProperty(`--_sonic-xy-${axis}-origin`, String(mapping.proportionOf(origin)));
|
|
263
242
|
}
|
|
264
243
|
this.#renderAria();
|
|
@@ -271,7 +250,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
271
250
|
this.y = y;
|
|
272
251
|
}
|
|
273
252
|
#axisText(axis) {
|
|
274
|
-
const { value } = this.#
|
|
253
|
+
const { value } = this.#models[axis];
|
|
275
254
|
return `${this.getAttribute(`${axis}-label`) ?? axis.toUpperCase()} ${this.#formatValue?.(value, axis) ?? String(value)}`;
|
|
276
255
|
}
|
|
277
256
|
#bindKeys(xy, signal) {
|
|
@@ -297,32 +276,22 @@ var SonicXy = class extends SonicFormElement {
|
|
|
297
276
|
xy.addEventListener("mousedown", (event) => {
|
|
298
277
|
event.preventDefault();
|
|
299
278
|
}, { signal });
|
|
300
|
-
this.#
|
|
301
|
-
|
|
302
|
-
this.#input(drag.from);
|
|
303
|
-
},
|
|
279
|
+
this.#gesture = bindFieldGesture(xy, {
|
|
280
|
+
claim: this.#claim,
|
|
304
281
|
grab: (event) => {
|
|
305
282
|
if (this.isDisabled() || isMenuPress(event)) return;
|
|
306
283
|
focusByPointer(this.focusTarget());
|
|
307
|
-
this.#pointerType = event.pointerType;
|
|
308
284
|
if (!isResetPress(event)) {
|
|
309
285
|
this.toggleState("dragging", true);
|
|
310
286
|
return this.#grab(event);
|
|
311
287
|
}
|
|
312
288
|
this.#reset();
|
|
313
|
-
this.#pointerType = void 0;
|
|
314
289
|
},
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
drag.state = step.state;
|
|
318
|
-
if (step.state.isEngaged) this.#claim.reveal("drag");
|
|
319
|
-
if (!this.#input(step)) this.#renderReadout();
|
|
290
|
+
input: (_hold, next) => {
|
|
291
|
+
if (!this.#input(next)) this.#renderReadout();
|
|
320
292
|
},
|
|
321
|
-
release: (
|
|
322
|
-
|
|
323
|
-
this.#claim.press(false);
|
|
324
|
-
if (hasMoved) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
325
|
-
this.#pointerType = void 0;
|
|
293
|
+
release: (_hold, moved) => {
|
|
294
|
+
if (moved.length > 0) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
326
295
|
},
|
|
327
296
|
toggle: (isDragging) => {
|
|
328
297
|
this.toggleState("dragging", isDragging);
|
|
@@ -333,13 +302,13 @@ var SonicXy = class extends SonicFormElement {
|
|
|
333
302
|
if (this.#input(next)) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
334
303
|
}
|
|
335
304
|
#grab(event) {
|
|
336
|
-
const from = {
|
|
337
|
-
x: this.x,
|
|
338
|
-
y: this.y
|
|
339
|
-
};
|
|
340
305
|
const field = this.#field.getBoundingClientRect();
|
|
341
306
|
const puck = this.#puck.getBoundingClientRect();
|
|
342
|
-
const
|
|
307
|
+
const { x, y } = this.#models;
|
|
308
|
+
const from = {
|
|
309
|
+
x: x.value,
|
|
310
|
+
y: y.value
|
|
311
|
+
};
|
|
343
312
|
const travel = {
|
|
344
313
|
x: Math.max(1, field.width - puck.width),
|
|
345
314
|
y: Math.max(1, field.height - puck.height)
|
|
@@ -351,32 +320,26 @@ var SonicXy = class extends SonicFormElement {
|
|
|
351
320
|
};
|
|
352
321
|
const isOnPuck = event.target instanceof Node && this.#puck.contains(event.target);
|
|
353
322
|
if (!isOnPuck) this.#input({
|
|
354
|
-
x:
|
|
355
|
-
y:
|
|
323
|
+
x: x.mapping().valueAt(pressed.x),
|
|
324
|
+
y: y.mapping().valueAt(pressed.y)
|
|
356
325
|
});
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
proportion: isOnPuck ? mappings[axis].proportionOf(this.#values[axis].value) : clampProportion(pressed[axis]),
|
|
326
|
+
const hold = (axis) => ({
|
|
327
|
+
from: from[axis],
|
|
328
|
+
model: this.#models[axis],
|
|
329
|
+
...isOnPuck ? {} : { proportion: clampProportion(pressed[axis]) },
|
|
362
330
|
travelPx: travel[axis]
|
|
363
331
|
});
|
|
364
|
-
return {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
thresholdPx: dragThresholdPx(event.pointerType),
|
|
369
|
-
x: start("x"),
|
|
370
|
-
y: start("y")
|
|
371
|
-
})
|
|
372
|
-
};
|
|
332
|
+
return { axes: {
|
|
333
|
+
x: hold("x"),
|
|
334
|
+
y: hold("y")
|
|
335
|
+
} };
|
|
373
336
|
}
|
|
374
337
|
#hasDefault() {
|
|
375
338
|
return this.xDefault !== void 0 || this.yDefault !== void 0;
|
|
376
339
|
}
|
|
377
340
|
#input(next) {
|
|
378
341
|
let hasMoved = false;
|
|
379
|
-
for (const axis of axes) if (
|
|
342
|
+
for (const axis of axes) if (this.#models[axis].input(next[axis] ?? NaN)) hasMoved = true;
|
|
380
343
|
if (!hasMoved) return false;
|
|
381
344
|
this.render();
|
|
382
345
|
this.dispatchEvent(new Event("input", {
|
|
@@ -386,9 +349,8 @@ var SonicXy = class extends SonicFormElement {
|
|
|
386
349
|
return true;
|
|
387
350
|
}
|
|
388
351
|
#keyTo(axis, key) {
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
const hasMoved = moveTo(this.#values[axis], mapping, next ?? NaN);
|
|
352
|
+
const model = this.#models[axis];
|
|
353
|
+
const hasMoved = model.input(model.mapping().keyTarget(key, model.value) ?? NaN);
|
|
392
354
|
this.#currentAxis = axis;
|
|
393
355
|
this.render();
|
|
394
356
|
this.#parts[axis].focus();
|
|
@@ -399,46 +361,18 @@ var SonicXy = class extends SonicFormElement {
|
|
|
399
361
|
}));
|
|
400
362
|
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
401
363
|
}
|
|
402
|
-
#mapping(axis) {
|
|
403
|
-
const midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);
|
|
404
|
-
return valueMapping({
|
|
405
|
-
max: this.numberAttribute(`${axis}-max`, 100),
|
|
406
|
-
...midpoint === void 0 ? {} : { midpoint },
|
|
407
|
-
isNotched: false,
|
|
408
|
-
isWrapping: false,
|
|
409
|
-
min: this.numberAttribute(`${axis}-min`, 0),
|
|
410
|
-
step: this.numberAttribute(`${axis}-step`, 1),
|
|
411
|
-
taper: this.#taper(axis)
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
#mappings() {
|
|
415
|
-
if (this.#mapped) return this.#mapped;
|
|
416
|
-
this.#mapped = {
|
|
417
|
-
x: this.#mapping("x"),
|
|
418
|
-
y: this.#mapping("y")
|
|
419
|
-
};
|
|
420
|
-
return this.#mapped;
|
|
421
|
-
}
|
|
422
|
-
#refresh() {
|
|
423
|
-
const mappings = this.#mappings();
|
|
424
|
-
for (const axis of axes) {
|
|
425
|
-
const mapping = mappings[axis];
|
|
426
|
-
resnap(this.#values[axis], mapping, this.numberAttribute(axis, mapping.bounds[0]));
|
|
427
|
-
}
|
|
428
|
-
this.render();
|
|
429
|
-
}
|
|
430
364
|
#renderAria() {
|
|
431
|
-
const mappings = this.#mappings();
|
|
432
365
|
const isDisabled = this.isDisabled();
|
|
433
366
|
for (const axis of axes) {
|
|
434
367
|
const part = this.#parts[axis];
|
|
435
|
-
const
|
|
368
|
+
const model = this.#models[axis];
|
|
369
|
+
const [low, high] = model.mapping().bounds;
|
|
436
370
|
const isCurrent = axis === this.#currentAxis;
|
|
437
371
|
const stop = isCurrent ? "0" : "-1";
|
|
438
372
|
writeAttribute(part, "aria-orientation", orientations[axis]);
|
|
439
373
|
writeAttribute(part, "aria-valuemin", String(low));
|
|
440
374
|
writeAttribute(part, "aria-valuemax", String(high));
|
|
441
|
-
writeAttribute(part, "aria-valuenow", String(
|
|
375
|
+
writeAttribute(part, "aria-valuenow", String(model.value));
|
|
442
376
|
writeAttribute(part, "aria-valuetext", this.#valueText(axis));
|
|
443
377
|
this.forwardNaming(part, true);
|
|
444
378
|
writeAttribute(part, "aria-hidden", isCurrent ? void 0 : "true");
|
|
@@ -471,6 +405,22 @@ var SonicXy = class extends SonicFormElement {
|
|
|
471
405
|
y: this.yDefault
|
|
472
406
|
});
|
|
473
407
|
}
|
|
408
|
+
#respec(...forgotten) {
|
|
409
|
+
for (const axis of axes) this.#models[axis].respec(this.numberAttribute(axis, this.numberAttribute(`${axis}-min`, 0)), { forgetAsk: forgotten.includes(axis) });
|
|
410
|
+
this.render();
|
|
411
|
+
}
|
|
412
|
+
#spec(axis) {
|
|
413
|
+
const midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);
|
|
414
|
+
return {
|
|
415
|
+
max: this.numberAttribute(`${axis}-max`, 100),
|
|
416
|
+
...midpoint === void 0 ? {} : { midpoint },
|
|
417
|
+
isNotched: false,
|
|
418
|
+
isWrapping: false,
|
|
419
|
+
min: this.numberAttribute(`${axis}-min`, 0),
|
|
420
|
+
step: this.numberAttribute(`${axis}-step`, 1),
|
|
421
|
+
taper: this.#taper(axis)
|
|
422
|
+
};
|
|
423
|
+
}
|
|
474
424
|
#taper(axis) {
|
|
475
425
|
return this.getAttribute(`${axis}-taper`) === "log" ? "log" : "linear";
|
|
476
426
|
}
|
|
@@ -478,14 +428,12 @@ var SonicXy = class extends SonicFormElement {
|
|
|
478
428
|
return `${this.#axisText(axis)}, ${this.#axisText(axis === "x" ? "y" : "x")}`;
|
|
479
429
|
}
|
|
480
430
|
#write(axis, next) {
|
|
481
|
-
if (this.#
|
|
482
|
-
const axisValue = this.#values[axis];
|
|
431
|
+
if (this.#gesture?.current()) return;
|
|
483
432
|
if (next === void 0 || next === null) {
|
|
484
|
-
|
|
485
|
-
this.#refresh();
|
|
433
|
+
this.#respec(axis);
|
|
486
434
|
return;
|
|
487
435
|
}
|
|
488
|
-
if (
|
|
436
|
+
if (this.#models[axis].write(toNumber(next))) this.render();
|
|
489
437
|
}
|
|
490
438
|
};
|
|
491
439
|
|
package/dist/default/index.js
CHANGED
|
@@ -10,10 +10,11 @@ import { SonicSpectrum } from "./elements/spectrum.js";
|
|
|
10
10
|
import { SonicSplit } from "./elements/split.js";
|
|
11
11
|
import { SonicSwitch } from "./elements/switch.js";
|
|
12
12
|
import { SonicToggle } from "./elements/toggle.js";
|
|
13
|
+
import { formatClock, parseClock } from "./lib/clock.js";
|
|
13
14
|
import { SonicWaveform } from "./elements/waveform.js";
|
|
14
15
|
import { SonicWavestrip } from "./elements/wavestrip.js";
|
|
15
16
|
import { SonicXy } from "./elements/xy.js";
|
|
16
17
|
import { crossfadeGains } from "./lib/crossfade.js";
|
|
17
18
|
import { formatPercent, parsePercent } from "./lib/percent.js";
|
|
18
19
|
|
|
19
|
-
export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatPercent, parsePercent };
|
|
20
|
+
export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatClock, formatPercent, parseClock, parsePercent };
|
|
@@ -22,6 +22,7 @@ var CanvasSurface = class {
|
|
|
22
22
|
}
|
|
23
23
|
#context;
|
|
24
24
|
#cssBox;
|
|
25
|
+
#fillWatch;
|
|
25
26
|
#frame;
|
|
26
27
|
#isDirty = true;
|
|
27
28
|
#isRebuildWanted = true;
|
|
@@ -51,6 +52,7 @@ var CanvasSurface = class {
|
|
|
51
52
|
signal.addEventListener("abort", () => {
|
|
52
53
|
if (this.#frame !== void 0) cancelAnimationFrame(this.#frame);
|
|
53
54
|
this.#frame = void 0;
|
|
55
|
+
this.#stopFill();
|
|
54
56
|
}, { once: true });
|
|
55
57
|
this.#schedule();
|
|
56
58
|
}
|
|
@@ -65,6 +67,25 @@ var CanvasSurface = class {
|
|
|
65
67
|
requestFrame() {
|
|
66
68
|
this.#schedule();
|
|
67
69
|
}
|
|
70
|
+
setFill(isFilling) {
|
|
71
|
+
const { fill, signal } = this.#options;
|
|
72
|
+
if (!fill || signal.aborted || isFilling === (this.#fillWatch !== void 0)) return;
|
|
73
|
+
const { style } = fill.control;
|
|
74
|
+
this.#stopFill();
|
|
75
|
+
if (!isFilling) {
|
|
76
|
+
style.removeProperty(fill.sizeProperty);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const started = new ResizeObserver((entries) => {
|
|
80
|
+
const box = entries.at(-1)?.borderBoxSize[0];
|
|
81
|
+
if (!box) return;
|
|
82
|
+
requestAnimationFrame(() => {
|
|
83
|
+
if (this.#fillWatch === started) style.setProperty(fill.sizeProperty, `${String(box.blockSize)}px`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
this.#fillWatch = started;
|
|
87
|
+
started.observe(fill.control);
|
|
88
|
+
}
|
|
68
89
|
#armTransitions() {
|
|
69
90
|
const { canvas, colours, numbers = {} } = this.#options;
|
|
70
91
|
const { style } = canvas;
|
|
@@ -190,6 +211,10 @@ var CanvasSurface = class {
|
|
|
190
211
|
this.#draw(frameMs);
|
|
191
212
|
});
|
|
192
213
|
}
|
|
214
|
+
#stopFill() {
|
|
215
|
+
this.#fillWatch?.disconnect();
|
|
216
|
+
this.#fillWatch = void 0;
|
|
217
|
+
}
|
|
193
218
|
#watchResolution() {
|
|
194
219
|
const { signal } = this.#options;
|
|
195
220
|
matchMedia(`(resolution: ${String(deviceRatio())}dppx)`).addEventListener("change", () => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/lib/clock.ts
|
|
2
|
+
const sign = /^[-−]/;
|
|
3
|
+
function formatClock(seconds) {
|
|
4
|
+
const total = Math.floor(Math.abs(seconds));
|
|
5
|
+
const hours = Math.floor(total / 3600);
|
|
6
|
+
const minutes = Math.floor(total / 60) % 60;
|
|
7
|
+
const rest = String(total % 60).padStart(2, "0");
|
|
8
|
+
const prefix = seconds < 0 ? "−" : "";
|
|
9
|
+
if (hours === 0) return `${prefix}${String(minutes)}:${rest}`;
|
|
10
|
+
return `${prefix}${String(hours)}:${String(minutes).padStart(2, "0")}:${rest}`;
|
|
11
|
+
}
|
|
12
|
+
function parseClock(text) {
|
|
13
|
+
const clock = text.trim();
|
|
14
|
+
let seconds = 0;
|
|
15
|
+
for (const part of clock.replace(sign, "").split(":")) {
|
|
16
|
+
if (part.trim() === "") return NaN;
|
|
17
|
+
seconds = seconds * 60 + Number(part);
|
|
18
|
+
}
|
|
19
|
+
return sign.test(clock) ? -seconds : seconds;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { formatClock, parseClock };
|
|
@@ -5,6 +5,7 @@ function progress(side, span) {
|
|
|
5
5
|
if (span === 0) return side > 0 ? 1 : 0;
|
|
6
6
|
return Math.min(1, side / span);
|
|
7
7
|
}
|
|
8
|
+
/** `position` runs from 0, all a, to 1, all b; `sharpness` from 0 to 1 shortens the overlap */
|
|
8
9
|
function crossfadeGains(position, options = {}) {
|
|
9
10
|
const at = clampProportion(position);
|
|
10
11
|
const span = 1 - clampProportion(options.sharpness ?? 0);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//#region src/lib/envelope-curve.ts
|
|
2
2
|
const flatCurve = .001;
|
|
3
|
+
/** `position` is 0 to 1 along a stage; a `curve` of 0 is a straight line */
|
|
3
4
|
function envelopeCurve(position, curve) {
|
|
4
5
|
if (Math.abs(curve) < flatCurve) return position;
|
|
5
6
|
return Math.expm1(curve * position) / Math.expm1(curve);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/lib/peaks-request.ts
|
|
2
|
+
function createPeaksRequest(repaint) {
|
|
3
|
+
const awaited = /* @__PURE__ */ new WeakSet();
|
|
4
|
+
let askedKey;
|
|
5
|
+
let isAsking = false;
|
|
6
|
+
const changed = () => {
|
|
7
|
+
if (!isAsking) askedKey = void 0;
|
|
8
|
+
repaint();
|
|
9
|
+
};
|
|
10
|
+
const changedAfter = async (asked) => {
|
|
11
|
+
await Promise.allSettled([asked]);
|
|
12
|
+
changed();
|
|
13
|
+
};
|
|
14
|
+
const call = (request, wanted) => {
|
|
15
|
+
isAsking = true;
|
|
16
|
+
try {
|
|
17
|
+
return request?.(...wanted);
|
|
18
|
+
} finally {
|
|
19
|
+
isAsking = false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
ask: (request, wanted) => {
|
|
24
|
+
const key = wanted?.join(":");
|
|
25
|
+
if (!wanted || key === askedKey) return;
|
|
26
|
+
askedKey = key;
|
|
27
|
+
const asked = call(request, wanted);
|
|
28
|
+
const promises = asked instanceof Promise ? [asked] : asked ?? [];
|
|
29
|
+
for (const promise of promises) {
|
|
30
|
+
if (awaited.has(promise)) continue;
|
|
31
|
+
awaited.add(promise);
|
|
32
|
+
changedAfter(promise);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
changed,
|
|
36
|
+
reset: () => {
|
|
37
|
+
askedKey = void 0;
|
|
38
|
+
repaint();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { createPeaksRequest };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { clamp } from "./math.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/positions.ts
|
|
4
|
+
const keySteps = /* @__PURE__ */ new Map([
|
|
5
|
+
["ArrowDown", 1],
|
|
6
|
+
["ArrowLeft", -1],
|
|
7
|
+
["ArrowRight", 1],
|
|
8
|
+
["ArrowUp", -1]
|
|
9
|
+
]);
|
|
10
|
+
function indexOrUndefined(index) {
|
|
11
|
+
return index < 0 ? void 0 : index;
|
|
12
|
+
}
|
|
13
|
+
function isEnabled(position) {
|
|
14
|
+
return !position.isDisabled;
|
|
15
|
+
}
|
|
16
|
+
function enabledEnds(positions) {
|
|
17
|
+
return [positions.findIndex(isEnabled), positions.findLastIndex(isEnabled)];
|
|
18
|
+
}
|
|
19
|
+
function stepFrom(positions, from, { isWrapping, step }) {
|
|
20
|
+
const count = positions.length;
|
|
21
|
+
let index = from;
|
|
22
|
+
for (const _ of positions) {
|
|
23
|
+
const next = index + step;
|
|
24
|
+
index = isWrapping ? (next + count) % count : clamp(next, 0, count - 1);
|
|
25
|
+
if (positions[index]?.isDisabled === false) return index;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function keyTarget(positions, from, { isWrapping, key }) {
|
|
29
|
+
const [first, last] = enabledEnds(positions);
|
|
30
|
+
if (key === "Home") return indexOrUndefined(first);
|
|
31
|
+
if (key === "End") return indexOrUndefined(last);
|
|
32
|
+
const step = keySteps.get(key);
|
|
33
|
+
if (step === void 0) return void 0;
|
|
34
|
+
return stepFrom(positions, from, {
|
|
35
|
+
isWrapping,
|
|
36
|
+
step
|
|
37
|
+
}) ?? from;
|
|
38
|
+
}
|
|
39
|
+
function isMomentaryAt(positions, index) {
|
|
40
|
+
return (index === 0 || index === positions.length - 1) && positions.length > 1 && positions[index]?.isMomentary === true;
|
|
41
|
+
}
|
|
42
|
+
function pressTarget(positions, checked, pressed) {
|
|
43
|
+
return positions.length === 2 && checked >= 0 ? 1 - checked : pressed;
|
|
44
|
+
}
|
|
45
|
+
function soleStep(positions, from) {
|
|
46
|
+
const ways = [-1, 1].map((step) => stepFrom(positions, from, {
|
|
47
|
+
isWrapping: false,
|
|
48
|
+
step
|
|
49
|
+
})).filter((to) => to !== void 0 && to !== from);
|
|
50
|
+
return ways.length === 1 ? ways[0] : void 0;
|
|
51
|
+
}
|
|
52
|
+
function restOf(positions, held) {
|
|
53
|
+
if (!isMomentaryAt(positions, held)) return void 0;
|
|
54
|
+
return held === 0 ? 1 : held - 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { enabledEnds, isMomentaryAt, keyTarget, pressTarget, restOf, soleStep, stepFrom };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/lib/scrub.ts
|
|
2
|
+
function scrubRegions(state, value, mapping) {
|
|
3
|
+
const played = mapping.proportionOf(state.played);
|
|
4
|
+
const reached = mapping.proportionOf(value);
|
|
5
|
+
return {
|
|
6
|
+
played,
|
|
7
|
+
scrub: state.from !== void 0 && state.isRevealed && reached !== played ? reached : void 0
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { scrubRegions };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/lib/spoken-duration.ts
|
|
2
|
+
const formatters = /* @__PURE__ */ new Map();
|
|
3
|
+
function formatterFor(language, isZero) {
|
|
4
|
+
const key = `${language}|${String(isZero)}`;
|
|
5
|
+
const cached = formatters.get(key);
|
|
6
|
+
if (cached) return cached;
|
|
7
|
+
const options = {
|
|
8
|
+
secondsDisplay: isZero ? "always" : "auto",
|
|
9
|
+
style: "long"
|
|
10
|
+
};
|
|
11
|
+
let formatter;
|
|
12
|
+
try {
|
|
13
|
+
formatter = new Intl.DurationFormat(language || void 0, options);
|
|
14
|
+
} catch {
|
|
15
|
+
formatter = new Intl.DurationFormat(void 0, options);
|
|
16
|
+
}
|
|
17
|
+
formatters.set(key, formatter);
|
|
18
|
+
return formatter;
|
|
19
|
+
}
|
|
20
|
+
function spokenDuration(seconds, language = "") {
|
|
21
|
+
const total = Math.trunc(seconds);
|
|
22
|
+
return formatterFor(language, total === 0).format({
|
|
23
|
+
hours: Math.trunc(total / 3600),
|
|
24
|
+
minutes: Math.trunc(total / 60) % 60,
|
|
25
|
+
seconds: total % 60
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { spokenDuration };
|