@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
|
@@ -4,7 +4,7 @@ import { readPxProperty } from "../lib/read-px-property.js";
|
|
|
4
4
|
import { sortedRegions } from "../lib/time-regions.js";
|
|
5
5
|
import { SonicWaveElement } from "./wave-element.js";
|
|
6
6
|
import { createMarkerBand } from "../lib/marker-band.js";
|
|
7
|
-
import { stripBars, stripRegions } from "../lib/strip-scene.js";
|
|
7
|
+
import { stripBars, stripGroove, stripRegions } from "../lib/strip-scene.js";
|
|
8
8
|
|
|
9
9
|
//#region src/elements/wavestrip.ts
|
|
10
10
|
const colours = {
|
|
@@ -15,6 +15,7 @@ const colours = {
|
|
|
15
15
|
};
|
|
16
16
|
const lengths = {
|
|
17
17
|
gapRatio: "--_sonic-wavestrip-bar-gap-ratio",
|
|
18
|
+
grooveRatio: "--_sonic-wavestrip-groove-ratio",
|
|
18
19
|
pitch: "--_sonic-wavestrip-bar-pitch",
|
|
19
20
|
radiusRatio: "--_sonic-wavestrip-bar-radius-ratio"
|
|
20
21
|
};
|
|
@@ -72,11 +73,9 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
72
73
|
colours = colours;
|
|
73
74
|
lengths = lengths;
|
|
74
75
|
sheet = "wavestrip.css";
|
|
76
|
+
sizeProperty = "--_sonic-wavestrip-size";
|
|
75
77
|
#bars;
|
|
76
78
|
#buffered = [];
|
|
77
|
-
#escapeWatch;
|
|
78
|
-
#hover;
|
|
79
|
-
#isHoverDismissed = false;
|
|
80
79
|
#markerBand = createMarkerBand(requireChild(this.control, ".sonic-wavestrip-markers", HTMLDivElement));
|
|
81
80
|
#paintedKey = "";
|
|
82
81
|
#peaks;
|
|
@@ -85,14 +84,26 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
85
84
|
super.connectedCallback();
|
|
86
85
|
}
|
|
87
86
|
connect(signal) {
|
|
87
|
+
const { control } = this;
|
|
88
88
|
super.connect(signal);
|
|
89
|
-
this
|
|
90
|
-
|
|
89
|
+
this.bindHover(control, signal, {
|
|
90
|
+
canShow: () => this.readout,
|
|
91
|
+
place: () => {
|
|
92
|
+
this.#placeReadout();
|
|
93
|
+
},
|
|
94
|
+
valueAt: (event) => {
|
|
95
|
+
const axis = this.#axis();
|
|
96
|
+
const at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);
|
|
97
|
+
const mapping = this.mapping();
|
|
98
|
+
return this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
this.bindGestures(control, signal, (event) => this.#grab(event));
|
|
91
102
|
}
|
|
92
103
|
draw() {
|
|
93
104
|
const surface = this.surface();
|
|
94
|
-
if (this.isEditing() || this.isRevealed()) this.#hover = void 0;
|
|
95
105
|
this.#placeReadout();
|
|
106
|
+
this.showCurrentMarker(this.playback());
|
|
96
107
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
97
108
|
}
|
|
98
109
|
isEmpty() {
|
|
@@ -113,9 +124,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
113
124
|
look.colours[kind]
|
|
114
125
|
]);
|
|
115
126
|
}
|
|
116
|
-
previewValue() {
|
|
117
|
-
return this.#hover;
|
|
118
|
-
}
|
|
119
127
|
renderMarkers() {
|
|
120
128
|
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
121
129
|
}
|
|
@@ -132,28 +140,18 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
132
140
|
};
|
|
133
141
|
}
|
|
134
142
|
#barsFor({ isDirty, look, size }) {
|
|
135
|
-
const peaks = this.#peaks;
|
|
136
|
-
if (!peaks || peaks.length === 0) return void 0;
|
|
137
143
|
if (!isDirty && this.#bars) return this.#bars;
|
|
144
|
+
const peaks = this.#peaks;
|
|
145
|
+
const groove = stripGroove(size, look.lengths.grooveRatio);
|
|
146
|
+
const drawn = groove ? [groove] : [];
|
|
147
|
+
const bars = peaks && peaks.length > 0 ? stripBars(peaks, size, look.lengths) : drawn;
|
|
148
|
+
this.#bars = void 0;
|
|
149
|
+
if (bars.length === 0) return void 0;
|
|
138
150
|
const path = new Path2D();
|
|
139
|
-
for (const { height, radius, width, x, y } of
|
|
151
|
+
for (const { height, radius, width, x, y } of bars) path.roundRect(x, y, width, height, radius);
|
|
140
152
|
this.#bars = path;
|
|
141
153
|
return path;
|
|
142
154
|
}
|
|
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
155
|
#grab(event) {
|
|
158
156
|
const axis = this.#axis();
|
|
159
157
|
const at = (event.clientX - axis.startPx) / axis.travelPx;
|
|
@@ -169,12 +167,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
169
167
|
outside
|
|
170
168
|
} : grabbed;
|
|
171
169
|
}
|
|
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
170
|
#markerAt(event, { startPx, topPx, travelPx }) {
|
|
179
171
|
const index = this.#markerBand.at({
|
|
180
172
|
x: event.clientX - startPx,
|
|
@@ -201,22 +193,6 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
201
193
|
...isScrubbing ? { scrub: proportionOf(this.value) } : {}
|
|
202
194
|
}, width);
|
|
203
195
|
}
|
|
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
196
|
};
|
|
221
197
|
|
|
222
198
|
//#endregion
|
|
@@ -2,13 +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 {
|
|
5
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
6
6
|
import { Readout } from "./readout.js";
|
|
7
|
+
import { ReadoutClaim, revealDelay } from "./readout-claim.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
|
+
import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
|
|
12
13
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
13
14
|
|
|
14
15
|
//#region src/elements/xy.ts
|
|
@@ -337,7 +338,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
337
338
|
x: mappings.x.valueAt(pressed.x),
|
|
338
339
|
y: mappings.y.valueAt(pressed.y)
|
|
339
340
|
});
|
|
340
|
-
this.#claim.press(true);
|
|
341
|
+
this.#claim.press(true, revealDelay(this.#xy));
|
|
341
342
|
const start = (axis) => ({
|
|
342
343
|
from: this.#values[axis].value,
|
|
343
344
|
mapping: mappings[axis],
|
|
@@ -359,7 +360,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
359
360
|
}
|
|
360
361
|
#input(next) {
|
|
361
362
|
let hasMoved = false;
|
|
362
|
-
for (const axis of axes) if (this.#
|
|
363
|
+
for (const axis of axes) if (moveTo(this.#values[axis], this.#mappings()[axis], next[axis] ?? NaN)) hasMoved = true;
|
|
363
364
|
if (!hasMoved) return false;
|
|
364
365
|
this.render();
|
|
365
366
|
this.dispatchEvent(new Event("input", {
|
|
@@ -369,8 +370,9 @@ var SonicXy = class extends SonicFormElement {
|
|
|
369
370
|
return true;
|
|
370
371
|
}
|
|
371
372
|
#keyTo(axis, key) {
|
|
372
|
-
const
|
|
373
|
-
const
|
|
373
|
+
const mapping = this.#mappings()[axis];
|
|
374
|
+
const next = mapping.keyTarget(key, this.#values[axis].value);
|
|
375
|
+
const hasMoved = moveTo(this.#values[axis], mapping, next ?? NaN);
|
|
374
376
|
this.#currentAxis = axis;
|
|
375
377
|
this.render();
|
|
376
378
|
this.#parts[axis].focus();
|
|
@@ -404,9 +406,8 @@ var SonicXy = class extends SonicFormElement {
|
|
|
404
406
|
#refresh() {
|
|
405
407
|
const mappings = this.#mappings();
|
|
406
408
|
for (const axis of axes) {
|
|
407
|
-
const axisValue = this.#values[axis];
|
|
408
409
|
const mapping = mappings[axis];
|
|
409
|
-
|
|
410
|
+
resnap(this.#values[axis], mapping, this.numberAttribute(axis, mapping.bounds[0]));
|
|
410
411
|
}
|
|
411
412
|
this.render();
|
|
412
413
|
}
|
|
@@ -454,15 +455,6 @@ var SonicXy = class extends SonicFormElement {
|
|
|
454
455
|
y: this.yDefault
|
|
455
456
|
});
|
|
456
457
|
}
|
|
457
|
-
#set(axis, next) {
|
|
458
|
-
if (!Number.isFinite(next)) return false;
|
|
459
|
-
const axisValue = this.#values[axis];
|
|
460
|
-
const snapped = this.#mappings()[axis].snap(next);
|
|
461
|
-
axisValue.asked = snapped;
|
|
462
|
-
if (snapped === axisValue.value) return false;
|
|
463
|
-
axisValue.value = snapped;
|
|
464
|
-
return true;
|
|
465
|
-
}
|
|
466
458
|
#taper(axis) {
|
|
467
459
|
return this.getAttribute(`${axis}-taper`) === "log" ? "log" : "linear";
|
|
468
460
|
}
|
|
@@ -477,10 +469,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
477
469
|
this.#refresh();
|
|
478
470
|
return;
|
|
479
471
|
}
|
|
480
|
-
|
|
481
|
-
const hasMoved = this.#set(axis, asked);
|
|
482
|
-
if (Number.isFinite(asked)) axisValue.asked = asked;
|
|
483
|
-
if (hasMoved) this.render();
|
|
472
|
+
if (setAsked(axisValue, this.#mappings()[axis], toNumber(next))) this.render();
|
|
484
473
|
}
|
|
485
474
|
};
|
|
486
475
|
|
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 };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/lib/asked-value.ts
|
|
2
|
+
function moveTo(cell, mapping, next) {
|
|
3
|
+
if (!Number.isFinite(next)) return false;
|
|
4
|
+
const snapped = mapping.snap(next);
|
|
5
|
+
cell.asked = snapped;
|
|
6
|
+
if (snapped === cell.value) return false;
|
|
7
|
+
cell.value = snapped;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function setAsked(cell, mapping, next) {
|
|
11
|
+
if (!Number.isFinite(next)) return false;
|
|
12
|
+
const hasMoved = moveTo(cell, mapping, next);
|
|
13
|
+
cell.asked = next;
|
|
14
|
+
return hasMoved;
|
|
15
|
+
}
|
|
16
|
+
function resnap(cell, mapping, fallback) {
|
|
17
|
+
cell.value = mapping.snap(cell.asked ?? fallback);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { moveTo, resnap, setAsked };
|
|
@@ -14,6 +14,9 @@ function readEach(properties, read) {
|
|
|
14
14
|
return Object.fromEntries(Object.entries(properties).flatMap(([name, property]) => property === void 0 ? [] : [[name, read(property)]]));
|
|
15
15
|
}
|
|
16
16
|
var CanvasSurface = class {
|
|
17
|
+
get isVisible() {
|
|
18
|
+
return this.#isVisible;
|
|
19
|
+
}
|
|
17
20
|
get size() {
|
|
18
21
|
return this.#size;
|
|
19
22
|
}
|
|
@@ -63,8 +66,10 @@ var CanvasSurface = class {
|
|
|
63
66
|
this.#schedule();
|
|
64
67
|
}
|
|
65
68
|
#armTransitions() {
|
|
66
|
-
const {
|
|
67
|
-
style
|
|
69
|
+
const { canvas, colours, numbers = {} } = this.#options;
|
|
70
|
+
const { style } = canvas;
|
|
71
|
+
const watched = [...Object.values(colours), ...Object.values(numbers)];
|
|
72
|
+
style.setProperty("transition-property", watched.join(", "));
|
|
68
73
|
style.setProperty("transition-duration", "0.001ms");
|
|
69
74
|
style.setProperty("transition-timing-function", "step-start");
|
|
70
75
|
style.setProperty("transition-behavior", "allow-discrete");
|
|
@@ -148,12 +153,13 @@ var CanvasSurface = class {
|
|
|
148
153
|
}, { once: true });
|
|
149
154
|
}
|
|
150
155
|
#read() {
|
|
151
|
-
const { canvas, colours, lengths = {} } = this.#options;
|
|
156
|
+
const { canvas, colours, lengths = {}, numbers = {} } = this.#options;
|
|
152
157
|
const styles = getComputedStyle(canvas);
|
|
153
158
|
return {
|
|
154
159
|
colours: readEach(colours, (property) => styles.getPropertyValue(property).trim().replaceAll("currentcolor", () => styles.color)),
|
|
155
160
|
isReducedMotion: this.#reducedMotion.matches,
|
|
156
|
-
lengths: readEach(lengths, (property) => readPxProperty(styles, property, 0))
|
|
161
|
+
lengths: readEach(lengths, (property) => readPxProperty(styles, property, 0)),
|
|
162
|
+
numbers: readEach(numbers, (property) => readPxProperty(styles, property, 0))
|
|
157
163
|
};
|
|
158
164
|
}
|
|
159
165
|
#rescale() {
|
|
@@ -20,8 +20,8 @@ function pendingIn({ pending, range }, window) {
|
|
|
20
20
|
return shown ? [shown] : [];
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
function placeholderPhase({ frameMs,
|
|
24
|
-
return !
|
|
23
|
+
function placeholderPhase({ frameMs, isStill }, pending) {
|
|
24
|
+
return !isStill && pending.length > 0 ? frameMs / placeholderPeriodMs : void 0;
|
|
25
25
|
}
|
|
26
26
|
function frameTimeline(input) {
|
|
27
27
|
const { clockSeconds, held, range, size } = input;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/lib/hover-preview.ts
|
|
2
|
+
function bindHoverPreview(target, options, signal) {
|
|
3
|
+
let hover;
|
|
4
|
+
let isDismissed = false;
|
|
5
|
+
let escapeWatch;
|
|
6
|
+
const watchEscape = (isHovered) => {
|
|
7
|
+
if (!isHovered) {
|
|
8
|
+
escapeWatch?.abort();
|
|
9
|
+
escapeWatch = void 0;
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (escapeWatch) return;
|
|
13
|
+
escapeWatch = new AbortController();
|
|
14
|
+
target.ownerDocument.addEventListener("keydown", (event) => {
|
|
15
|
+
if (hover === void 0 || event.key !== "Escape") return;
|
|
16
|
+
hover = void 0;
|
|
17
|
+
isDismissed = true;
|
|
18
|
+
options.dismiss();
|
|
19
|
+
}, { signal: escapeWatch.signal });
|
|
20
|
+
};
|
|
21
|
+
const hoverAt = (value) => {
|
|
22
|
+
if (value === void 0) isDismissed = false;
|
|
23
|
+
if (!isDismissed && !options.isTaken()) hover = value;
|
|
24
|
+
options.show();
|
|
25
|
+
watchEscape(value !== void 0);
|
|
26
|
+
};
|
|
27
|
+
target.addEventListener("pointermove", (event) => {
|
|
28
|
+
if (event.pointerType === "touch" || event.buttons !== 0 || !options.canShow()) return;
|
|
29
|
+
hoverAt(options.valueAt(event));
|
|
30
|
+
}, { signal });
|
|
31
|
+
for (const type of ["pointerdown", "pointerleave"]) target.addEventListener(type, () => {
|
|
32
|
+
hoverAt(void 0);
|
|
33
|
+
}, { signal });
|
|
34
|
+
signal.addEventListener("abort", () => {
|
|
35
|
+
watchEscape(false);
|
|
36
|
+
}, { once: true });
|
|
37
|
+
return {
|
|
38
|
+
clear: () => {
|
|
39
|
+
hover = void 0;
|
|
40
|
+
},
|
|
41
|
+
value: () => hover
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { bindHoverPreview };
|
|
@@ -4,7 +4,6 @@ import { readPxProperty } from "./read-px-property.js";
|
|
|
4
4
|
import { writeKind } from "./marker-band.js";
|
|
5
5
|
|
|
6
6
|
//#region src/lib/marker-rider.ts
|
|
7
|
-
const fadeShare = .25;
|
|
8
7
|
function lineX(seconds, view) {
|
|
9
8
|
return (seconds - view.startSeconds) / view.windowSeconds * view.widthPx;
|
|
10
9
|
}
|
|
@@ -12,11 +11,11 @@ function parkedPlacement(labels, index, [view, arrivingX]) {
|
|
|
12
11
|
const parked = labels[index];
|
|
13
12
|
if (!parked) return void 0;
|
|
14
13
|
const next = labels[index + 1];
|
|
15
|
-
const x = Math.max(view.
|
|
14
|
+
const x = Math.max(view.parkPx, lineX(parked.start, view) + view.insetPx);
|
|
16
15
|
const approachPx = next ? lineX(next.start, view) - lineX(view.playheadSeconds, view) : Infinity;
|
|
17
16
|
return {
|
|
18
17
|
index,
|
|
19
|
-
opacity: clamp(approachPx / (view.widthPx * fadeShare), 0, 1),
|
|
18
|
+
opacity: view.fadeShare > 0 ? clamp(approachPx / (view.widthPx * view.fadeShare), 0, 1) : 1,
|
|
20
19
|
shownPx: Math.min(parked.widthPx, arrivingX - x, view.widthPx - x),
|
|
21
20
|
x
|
|
22
21
|
};
|
|
@@ -91,15 +90,20 @@ function measureWidths(control, className, texts) {
|
|
|
91
90
|
return widths;
|
|
92
91
|
}
|
|
93
92
|
function createLabelRider(control, options) {
|
|
94
|
-
const { className, colourProperty, insetProperty } = options;
|
|
93
|
+
const { className, colourProperty, fadeProperty, insetProperty, parkProperty } = options;
|
|
95
94
|
const parked = labelSlot(requireChild(control, `.${className}`, HTMLElement));
|
|
96
95
|
const arriving = labelSlot(requireChild(control, `.${className} + .${className}`, HTMLElement));
|
|
96
|
+
let fadeShare = .25;
|
|
97
97
|
let insetPx = 0;
|
|
98
|
+
let parkPx = 0;
|
|
98
99
|
let measured = [];
|
|
99
100
|
return {
|
|
100
101
|
measure: (labels) => {
|
|
101
102
|
const widths = measureWidths(control, className, labels.map(({ text }) => text));
|
|
102
|
-
|
|
103
|
+
const styles = getComputedStyle(control);
|
|
104
|
+
fadeShare = readPxProperty(styles, fadeProperty, .25);
|
|
105
|
+
insetPx = readPxProperty(styles, insetProperty, 0);
|
|
106
|
+
parkPx = readPxProperty(styles, parkProperty, 0);
|
|
103
107
|
measured = labels.map((label, index) => ({
|
|
104
108
|
...label,
|
|
105
109
|
widthPx: widths[index] ?? 0
|
|
@@ -108,7 +112,9 @@ function createLabelRider(control, options) {
|
|
|
108
112
|
place: (window) => {
|
|
109
113
|
const layout = layoutRider(measured, {
|
|
110
114
|
...window,
|
|
111
|
-
|
|
115
|
+
fadeShare,
|
|
116
|
+
insetPx,
|
|
117
|
+
parkPx
|
|
112
118
|
});
|
|
113
119
|
writeLabel(parked, [measured, colourProperty], layout.parked);
|
|
114
120
|
writeLabel(arriving, [measured, colourProperty], layout.arriving);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
//#region src/lib/pointer-drag.ts
|
|
2
|
+
function capturePointer(target, pointerId) {
|
|
3
|
+
try {
|
|
4
|
+
target.setPointerCapture(pointerId);
|
|
5
|
+
} catch {}
|
|
6
|
+
}
|
|
2
7
|
function bindDrag(target, handlers, signal) {
|
|
3
8
|
let held;
|
|
4
9
|
const end = () => {
|
|
@@ -22,8 +27,8 @@ function bindDrag(target, handlers, signal) {
|
|
|
22
27
|
drag,
|
|
23
28
|
pointerId: event.pointerId
|
|
24
29
|
};
|
|
25
|
-
target.setPointerCapture(event.pointerId);
|
|
26
30
|
handlers.toggle?.(true);
|
|
31
|
+
capturePointer(target, event.pointerId);
|
|
27
32
|
}, { signal });
|
|
28
33
|
whileHeld("pointermove", handlers.move);
|
|
29
34
|
whileHeld("pointercancel", (drag) => {
|
|
@@ -43,4 +48,4 @@ function bindDrag(target, handlers, signal) {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
//#endregion
|
|
46
|
-
export { bindDrag };
|
|
51
|
+
export { bindDrag, capturePointer };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/lib/read-ms-property.ts
|
|
2
|
+
function readMsProperty(styles, property, fallback) {
|
|
3
|
+
const text = styles.getPropertyValue(property).trim();
|
|
4
|
+
const parsed = Number.parseFloat(text);
|
|
5
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
6
|
+
return text.endsWith("ms") ? parsed : parsed * 1e3;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { readMsProperty };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { linearTaper, logTaper } from "./taper.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/spectrum-columns.ts
|
|
4
|
+
const gridDecibels = 12;
|
|
5
|
+
function binHertz({ binCount, sampleRate }) {
|
|
6
|
+
return sampleRate / 2 / binCount;
|
|
7
|
+
}
|
|
8
|
+
function columnEdges(axis, width) {
|
|
9
|
+
const edges = new Float64Array(width + 1);
|
|
10
|
+
const taper = logTaper(axis.frequencyMin, axis.frequencyMax);
|
|
11
|
+
if (!taper) return edges;
|
|
12
|
+
const hertz = binHertz(axis);
|
|
13
|
+
for (let edge = 0; edge <= width; edge += 1) edges[edge] = taper.valueAt(edge / width) / hertz;
|
|
14
|
+
return edges;
|
|
15
|
+
}
|
|
16
|
+
function columnLevels(columns, levels, { edges, floor }) {
|
|
17
|
+
const levelAt = (bin) => bin < levels.length ? levels[bin] ?? floor : floor;
|
|
18
|
+
for (let column = 0; column < columns.length; column += 1) {
|
|
19
|
+
const from = edges[column] ?? 0;
|
|
20
|
+
const to = edges[column + 1] ?? from;
|
|
21
|
+
const first = Math.ceil(from);
|
|
22
|
+
const last = Math.ceil(to) - 1;
|
|
23
|
+
if (first > last) {
|
|
24
|
+
const below = levelAt(first - 1);
|
|
25
|
+
columns[column] = below + (levelAt(first) - below) * ((from + to) / 2 - (first - 1));
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
let loudest = floor;
|
|
29
|
+
for (let bin = first; bin <= last && bin < levels.length; bin += 1) loudest = Math.max(loudest, levelAt(bin));
|
|
30
|
+
columns[column] = loudest;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function levelRows({ max, min }, height) {
|
|
34
|
+
const taper = linearTaper(min, max);
|
|
35
|
+
return (level) => Math.round((1 - taper.proportionOf(level)) * height);
|
|
36
|
+
}
|
|
37
|
+
function gridColumns({ frequencyMax, frequencyMin }, width) {
|
|
38
|
+
const taper = logTaper(frequencyMin, frequencyMax);
|
|
39
|
+
const columns = [];
|
|
40
|
+
if (!taper) return columns;
|
|
41
|
+
for (let exponent = Math.floor(Math.log10(frequencyMin)) + 1; 10 ** exponent < frequencyMax; exponent += 1) columns.push(Math.round(taper.proportionOf(10 ** exponent) * width));
|
|
42
|
+
return columns;
|
|
43
|
+
}
|
|
44
|
+
function gridRows({ max, min }, rowOf) {
|
|
45
|
+
const rows = [];
|
|
46
|
+
for (let level = (Math.floor(min / gridDecibels) + 1) * gridDecibels; level < max; level += gridDecibels) rows.push(rowOf(level));
|
|
47
|
+
return rows;
|
|
48
|
+
}
|
|
49
|
+
function zoneRows({ clipFrom, hotFrom }, rowOf) {
|
|
50
|
+
const clip = rowOf(clipFrom);
|
|
51
|
+
return {
|
|
52
|
+
clip,
|
|
53
|
+
hot: Math.max(clip, rowOf(hotFrom))
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function loudestBin(frame, axis) {
|
|
57
|
+
const hertz = binHertz(axis);
|
|
58
|
+
const last = Math.min(Math.floor(axis.frequencyMax / hertz), frame.length - 1);
|
|
59
|
+
let loudest = -1;
|
|
60
|
+
let decibels = -Infinity;
|
|
61
|
+
for (let bin = Math.ceil(axis.frequencyMin / hertz); bin <= last; bin += 1) {
|
|
62
|
+
const level = frame[bin] ?? NaN;
|
|
63
|
+
if (!(Number.isFinite(level) && level > decibels)) continue;
|
|
64
|
+
loudest = bin;
|
|
65
|
+
decibels = level;
|
|
66
|
+
}
|
|
67
|
+
return loudest === -1 ? void 0 : {
|
|
68
|
+
decibels,
|
|
69
|
+
frequency: loudest * hertz
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { columnEdges, columnLevels, gridColumns, gridRows, levelRows, loudestBin, zoneRows };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ballisticsRates, fall } from "./ballistics.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/spectrum-levels.ts
|
|
4
|
+
const holdMs = 1500;
|
|
5
|
+
function createLevels(binCount) {
|
|
6
|
+
return {
|
|
7
|
+
bars: new Float64Array(binCount).fill(-Infinity),
|
|
8
|
+
heldAt: new Float64Array(binCount).fill(-Infinity),
|
|
9
|
+
holds: new Float64Array(binCount).fill(-Infinity),
|
|
10
|
+
incoming: new Float64Array(binCount).fill(-Infinity)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function receiveLevels({ incoming }, frame, floor) {
|
|
14
|
+
for (let bin = 0; bin < incoming.length; bin += 1) {
|
|
15
|
+
const level = frame[bin] ?? NaN;
|
|
16
|
+
incoming[bin] = Math.max(incoming[bin] ?? floor, Number.isFinite(level) ? level : floor);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function fallenHold(hold, bar, { elapsedMs, isHoldStepped, pastHoldMs }) {
|
|
20
|
+
if (pastHoldMs < 0) return hold;
|
|
21
|
+
if (isHoldStepped) return bar;
|
|
22
|
+
return fall(hold, {
|
|
23
|
+
fallDecibelsPerSecond: ballisticsRates.peak.fallDecibelsPerSecond,
|
|
24
|
+
floor: bar
|
|
25
|
+
}, Math.min(elapsedMs, pastHoldMs));
|
|
26
|
+
}
|
|
27
|
+
function stepLevels(levels, step) {
|
|
28
|
+
const { bars, heldAt, holds, incoming } = levels;
|
|
29
|
+
const rate = {
|
|
30
|
+
fallDecibelsPerSecond: ballisticsRates.peak.fallDecibelsPerSecond,
|
|
31
|
+
floor: step.floor
|
|
32
|
+
};
|
|
33
|
+
for (let bin = 0; bin < bars.length; bin += 1) {
|
|
34
|
+
const bar = Math.max(fall(bars[bin] ?? step.floor, rate, step.elapsedMs), incoming[bin] ?? step.floor);
|
|
35
|
+
const hold = holds[bin] ?? bar;
|
|
36
|
+
bars[bin] = bar;
|
|
37
|
+
incoming[bin] = -Infinity;
|
|
38
|
+
if (!step.isHeld || bar >= hold) {
|
|
39
|
+
holds[bin] = bar;
|
|
40
|
+
heldAt[bin] = step.nowMs;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const pastHoldMs = step.nowMs - (heldAt[bin] ?? step.nowMs) - holdMs;
|
|
44
|
+
holds[bin] = fallenHold(hold, bar, {
|
|
45
|
+
...step,
|
|
46
|
+
pastHoldMs
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function areSettled({ holds }, floor) {
|
|
51
|
+
return holds.every((hold) => hold <= floor);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { areSettled, createLevels, receiveLevels, stepLevels };
|
|
@@ -18,6 +18,17 @@ function stripBars(peaks, size, grid) {
|
|
|
18
18
|
};
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
+
function stripGroove(size, ratio) {
|
|
22
|
+
if (ratio <= 0) return void 0;
|
|
23
|
+
const height = Math.max(1, Math.round(size.height * ratio));
|
|
24
|
+
return {
|
|
25
|
+
height,
|
|
26
|
+
radius: height / 2,
|
|
27
|
+
width: size.width,
|
|
28
|
+
x: 0,
|
|
29
|
+
y: Math.round((size.height - height) / 2)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
21
32
|
function stripRegions(state, widthPx) {
|
|
22
33
|
const px = (proportion) => Math.round((Number.isFinite(proportion) ? proportion : 0) * widthPx);
|
|
23
34
|
const buffered = state.buffered.map(([start, end]) => [px(start), px(end)]).filter(([start, end]) => end > start);
|
|
@@ -45,4 +56,4 @@ function stripRegions(state, widthPx) {
|
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
//#endregion
|
|
48
|
-
export { stripBars, stripRegions };
|
|
59
|
+
export { stripBars, stripGroove, stripRegions };
|
|
@@ -3,9 +3,13 @@ function readRegions(regions) {
|
|
|
3
3
|
if ("start" in regions) return Array.from({ length: regions.length }, (_entry, index) => [regions.start(index), regions.end(index)]);
|
|
4
4
|
return [...regions].map(([start, end]) => [start, end]);
|
|
5
5
|
}
|
|
6
|
+
function dueRegions(regions, listedMs, [nowMs, delayMs]) {
|
|
7
|
+
if (delayMs <= 0) return [...regions];
|
|
8
|
+
return regions.filter((region) => nowMs - (listedMs.get(region.join(":")) ?? nowMs) >= delayMs);
|
|
9
|
+
}
|
|
6
10
|
function sortedRegions(regions) {
|
|
7
11
|
return regions ? readRegions(regions).toSorted((first, second) => first[0] - second[0]) : [];
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
//#endregion
|
|
11
|
-
export { readRegions, sortedRegions };
|
|
15
|
+
export { dueRegions, readRegions, sortedRegions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spectrum.js","names":[],"sources":["../../../src/define/spectrum.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicSpectrum } from '#elements/spectrum.ts';\n\ndefineOnce('sonic-spectrum', SonicSpectrum);\n\nexport type { SonicSpectrum } from '#elements/spectrum.ts';\n"],"mappings":";;;;AAGA,WAAW,kBAAkB,aAAa"}
|
package/dist/dev/define.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { SonicSegmented } from "./elements/segmented.js";
|
|
|
12
12
|
import "./define/segmented.js";
|
|
13
13
|
import { SonicSlider } from "./elements/slider.js";
|
|
14
14
|
import "./define/slider.js";
|
|
15
|
+
import { SonicSpectrum } from "./elements/spectrum.js";
|
|
16
|
+
import "./define/spectrum.js";
|
|
15
17
|
import { SonicSplit } from "./elements/split.js";
|
|
16
18
|
import "./define/split.js";
|
|
17
19
|
import { SonicSwitch } from "./elements/switch.js";
|
|
@@ -22,4 +24,4 @@ import { SonicWavestrip } from "./elements/wavestrip.js";
|
|
|
22
24
|
import "./define/wavestrip.js";
|
|
23
25
|
import { SonicXy } from "./elements/xy.js";
|
|
24
26
|
import "./define/xy.js";
|
|
25
|
-
export type { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSplit, SonicSwitch, SonicWaveform, SonicWavestrip, SonicXy };
|
|
27
|
+
export type { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicWaveform, SonicWavestrip, SonicXy };
|
package/dist/dev/define.js
CHANGED