@xsynaptic/sonic-ui 0.5.0 → 0.6.1
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 +3 -1
- package/dist/default/dat.js +5 -4
- package/dist/default/define/button.js +1 -1
- package/dist/default/define.js +2 -2
- package/dist/default/elements/button.js +6 -5
- package/dist/default/elements/dial.js +5 -1
- package/dist/default/elements/envelope.js +46 -18
- package/dist/default/elements/meter.js +12 -8
- package/dist/default/elements/number.js +1 -1
- package/dist/default/elements/radio-group.js +46 -47
- package/dist/default/elements/readout-claim.js +0 -29
- package/dist/default/elements/readout.js +1 -1
- package/dist/default/elements/scale.js +43 -0
- package/dist/default/elements/slider.js +16 -3
- package/dist/default/elements/sonic-element.js +17 -55
- package/dist/default/elements/switch.js +33 -39
- package/dist/default/elements/value-element.js +142 -187
- package/dist/default/elements/wave-element.js +5 -2
- package/dist/default/elements/waveform.js +35 -12
- package/dist/default/elements/wavestrip.js +59 -18
- package/dist/default/elements/xy.js +34 -19
- package/dist/default/index.js +2 -2
- package/dist/default/lib/adsr-shape.js +5 -0
- package/dist/default/lib/ballistics.js +2 -2
- package/dist/default/lib/canvas-surface.js +13 -2
- package/dist/default/lib/distribute.js +1 -1
- package/dist/default/lib/drag-step.js +4 -4
- package/dist/default/lib/frame-timeline.js +2 -1
- package/dist/default/lib/marker-band.js +54 -10
- package/dist/default/lib/marker-rider.js +10 -4
- package/dist/default/lib/math.js +6 -1
- package/dist/default/lib/mirror-children.js +64 -0
- package/dist/default/lib/owned-control.js +10 -0
- package/dist/default/lib/pointer-drag.js +8 -5
- package/dist/default/lib/resample-peaks.js +11 -1
- package/dist/default/lib/time-regions.js +4 -1
- package/dist/default/lib/write-attribute.js +1 -0
- package/dist/dev/dat.d.ts +2 -2
- package/dist/dev/dat.js +5 -4
- package/dist/dev/dat.js.map +1 -1
- package/dist/dev/define/button.js +1 -1
- package/dist/dev/define.d.ts +4 -4
- package/dist/dev/define.js +2 -2
- package/dist/dev/elements/button.js +6 -5
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +1 -1
- package/dist/dev/elements/dial.js +5 -1
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.js +46 -18
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +1 -1
- package/dist/dev/elements/meter.js +12 -8
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.js +1 -1
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/radio-group.js +46 -47
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/readout-claim.js +0 -29
- package/dist/dev/elements/readout-claim.js.map +1 -1
- package/dist/dev/elements/readout.js +1 -1
- package/dist/dev/elements/readout.js.map +1 -1
- package/dist/dev/elements/scale.js +44 -0
- package/dist/dev/elements/scale.js.map +1 -0
- package/dist/dev/elements/slider.d.ts +4 -1
- package/dist/dev/elements/slider.js +16 -3
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +2 -8
- package/dist/dev/elements/sonic-element.js +18 -55
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/switch.js +33 -39
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +6 -8
- package/dist/dev/elements/value-element.js +142 -187
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +1 -0
- package/dist/dev/elements/wave-element.js +5 -2
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +1 -1
- package/dist/dev/elements/waveform.js +35 -12
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +4 -0
- package/dist/dev/elements/wavestrip.js +59 -18
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.d.ts +3 -3
- package/dist/dev/elements/xy.js +34 -19
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +2 -2
- package/dist/dev/index.js +2 -2
- package/dist/dev/lib/adsr-shape.js +5 -0
- package/dist/dev/lib/adsr-shape.js.map +1 -1
- package/dist/dev/lib/ballistics.js +2 -2
- package/dist/dev/lib/ballistics.js.map +1 -1
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +13 -2
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/distribute.js +1 -1
- package/dist/dev/lib/distribute.js.map +1 -1
- package/dist/dev/lib/drag-step.js +4 -4
- package/dist/dev/lib/drag-step.js.map +1 -1
- package/dist/dev/lib/frame-timeline.js +2 -1
- package/dist/dev/lib/frame-timeline.js.map +1 -1
- package/dist/dev/lib/marker-band.js +54 -10
- package/dist/dev/lib/marker-band.js.map +1 -1
- package/dist/dev/lib/marker-rider.js +10 -4
- package/dist/dev/lib/marker-rider.js.map +1 -1
- package/dist/dev/lib/math.js +6 -1
- package/dist/dev/lib/math.js.map +1 -1
- package/dist/dev/lib/mirror-children.js +80 -0
- package/dist/dev/lib/mirror-children.js.map +1 -0
- package/dist/dev/lib/owned-control.js +23 -0
- package/dist/dev/lib/owned-control.js.map +1 -0
- package/dist/dev/lib/pointer-drag.js +8 -5
- package/dist/dev/lib/pointer-drag.js.map +1 -1
- package/dist/dev/lib/resample-peaks.js +11 -1
- package/dist/dev/lib/resample-peaks.js.map +1 -1
- package/dist/dev/lib/time-regions.js +4 -1
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/dev/lib/write-attribute.js +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -1
- package/dist/skins/amber.css +8 -0
- package/dist/skins/flat.css +26 -0
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +2 -0
- package/dist/skins/slate.css +2 -0
- package/dist/styles/button.css +59 -10
- package/dist/styles/dial.css +24 -16
- package/dist/styles/envelope.css +58 -13
- package/dist/styles/led.css +20 -4
- package/dist/styles/material.css +143 -55
- package/dist/styles/meter.css +8 -4
- package/dist/styles/panel.css +7 -4
- package/dist/styles/segmented.css +27 -5
- package/dist/styles/slider.css +2 -1
- package/dist/styles/switch.css +65 -23
- package/dist/styles/waveform.css +28 -5
- package/dist/styles/wavestrip.css +23 -6
- package/dist/styles/xy.css +48 -27
- package/package.json +2 -5
|
@@ -1,8 +1,9 @@
|
|
|
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 { sortedRegions } from "../lib/time-regions.js";
|
|
4
5
|
import { SonicWaveElement } from "./wave-element.js";
|
|
5
|
-
import { createMarkerBand
|
|
6
|
+
import { createMarkerBand } from "../lib/marker-band.js";
|
|
6
7
|
import { stripBars, stripRegions } from "../lib/strip-scene.js";
|
|
7
8
|
|
|
8
9
|
//#region src/elements/wavestrip.ts
|
|
@@ -45,6 +46,13 @@ function fillRegion(context, path, [from, to, style]) {
|
|
|
45
46
|
context.restore();
|
|
46
47
|
}
|
|
47
48
|
var SonicWavestrip = class extends SonicWaveElement {
|
|
49
|
+
get buffered() {
|
|
50
|
+
return this.#buffered.map(([start, end]) => [start, end]);
|
|
51
|
+
}
|
|
52
|
+
set buffered(regions) {
|
|
53
|
+
this.#buffered = sortedRegions(regions);
|
|
54
|
+
this.render();
|
|
55
|
+
}
|
|
48
56
|
get cancellable() {
|
|
49
57
|
return this.hasAttribute("cancellable");
|
|
50
58
|
}
|
|
@@ -65,11 +73,15 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
65
73
|
lengths = lengths;
|
|
66
74
|
sheet = "wavestrip.css";
|
|
67
75
|
#bars;
|
|
76
|
+
#buffered = [];
|
|
77
|
+
#escapeWatch;
|
|
78
|
+
#hover;
|
|
79
|
+
#isHoverDismissed = false;
|
|
68
80
|
#markerBand = createMarkerBand(requireChild(this.control, ".sonic-wavestrip-markers", HTMLDivElement));
|
|
69
81
|
#paintedKey = "";
|
|
70
82
|
#peaks;
|
|
71
83
|
connectedCallback() {
|
|
72
|
-
this.upgradeProperties("cancellable", "peaks");
|
|
84
|
+
this.upgradeProperties("buffered", "cancellable", "peaks");
|
|
73
85
|
super.connectedCallback();
|
|
74
86
|
}
|
|
75
87
|
connect(signal) {
|
|
@@ -79,6 +91,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
79
91
|
}
|
|
80
92
|
draw() {
|
|
81
93
|
const surface = this.surface();
|
|
94
|
+
if (this.isEditing() || this.isRevealed()) this.#hover = void 0;
|
|
82
95
|
this.#placeReadout();
|
|
83
96
|
if (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) surface.requestFrame();
|
|
84
97
|
}
|
|
@@ -100,8 +113,11 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
100
113
|
look.colours[kind]
|
|
101
114
|
]);
|
|
102
115
|
}
|
|
116
|
+
previewValue() {
|
|
117
|
+
return this.#hover;
|
|
118
|
+
}
|
|
103
119
|
renderMarkers() {
|
|
104
|
-
this.#markerBand.render(this.
|
|
120
|
+
this.#markerBand.render(this.markerList(), this.markerProportions());
|
|
105
121
|
}
|
|
106
122
|
scrubsKeyRepeat() {
|
|
107
123
|
return true;
|
|
@@ -111,6 +127,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
111
127
|
return {
|
|
112
128
|
position: (event) => event.clientX,
|
|
113
129
|
startPx: box.left,
|
|
130
|
+
topPx: box.top,
|
|
114
131
|
travelPx: Math.max(1, box.width)
|
|
115
132
|
};
|
|
116
133
|
}
|
|
@@ -126,36 +143,44 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
126
143
|
#bindHover(strip, signal) {
|
|
127
144
|
strip.addEventListener("pointermove", (event) => {
|
|
128
145
|
if (event.pointerType === "touch" || event.buttons !== 0) return;
|
|
129
|
-
if (!this.readout
|
|
146
|
+
if (!this.readout) return;
|
|
130
147
|
const axis = this.#axis();
|
|
131
148
|
const at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);
|
|
132
|
-
|
|
149
|
+
const mapping = this.mapping();
|
|
150
|
+
this.#hoverAt(this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at)));
|
|
133
151
|
this.#placeReadout();
|
|
134
152
|
}, { signal });
|
|
135
153
|
for (const type of ["pointerdown", "pointerleave"]) strip.addEventListener(type, () => {
|
|
136
|
-
this
|
|
154
|
+
this.#hoverAt(void 0);
|
|
137
155
|
}, { signal });
|
|
138
156
|
}
|
|
139
157
|
#grab(event) {
|
|
140
|
-
const
|
|
141
|
-
const at = (event.clientX - startPx) / axis.travelPx;
|
|
158
|
+
const axis = this.#axis();
|
|
159
|
+
const at = (event.clientX - axis.startPx) / axis.travelPx;
|
|
142
160
|
const outside = this.#outside();
|
|
143
161
|
const grabbed = {
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
fromProportion: clampProportion(at),
|
|
163
|
+
position: axis.position,
|
|
164
|
+
travelPx: axis.travelPx
|
|
146
165
|
};
|
|
147
|
-
this.input(this.#markerAt(
|
|
166
|
+
this.input(this.#markerAt(event, axis) ?? this.mapping().valueAt(at));
|
|
148
167
|
return outside ? {
|
|
149
168
|
...grabbed,
|
|
150
169
|
outside
|
|
151
170
|
} : grabbed;
|
|
152
171
|
}
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
#markerAt(event, { startPx, topPx, travelPx }) {
|
|
179
|
+
const index = this.#markerBand.at({
|
|
180
|
+
x: event.clientX - startPx,
|
|
181
|
+
y: event.clientY - topPx
|
|
182
|
+
}, travelPx);
|
|
183
|
+
return index === void 0 ? void 0 : this.markerList()[index]?.start;
|
|
159
184
|
}
|
|
160
185
|
#outside() {
|
|
161
186
|
if (!this.cancellable) return void 0;
|
|
@@ -171,11 +196,27 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
171
196
|
const { proportionOf } = this.mapping();
|
|
172
197
|
const isScrubbing = this.heldFrom() !== void 0 && this.isRevealed();
|
|
173
198
|
return stripRegions({
|
|
174
|
-
buffered: this
|
|
199
|
+
buffered: this.#buffered.map(([start, end]) => [proportionOf(start), proportionOf(end)]),
|
|
175
200
|
played: proportionOf(this.playback()),
|
|
176
201
|
...isScrubbing ? { scrub: proportionOf(this.value) } : {}
|
|
177
202
|
}, width);
|
|
178
203
|
}
|
|
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
|
+
}
|
|
179
220
|
};
|
|
180
221
|
|
|
181
222
|
//#endregion
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { clampProportion } from "../lib/math.js";
|
|
2
|
-
import { requireChild, template } from "../lib/render.js";
|
|
1
|
+
import { clampProportion, toNumber } from "../lib/math.js";
|
|
3
2
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
4
3
|
import { SonicFormElement } from "./form-element.js";
|
|
4
|
+
import { requireChild, template } from "../lib/render.js";
|
|
5
5
|
import { ReadoutClaim } from "./readout-claim.js";
|
|
6
6
|
import { Readout } from "./readout.js";
|
|
7
7
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
8
8
|
import { focusByPointer } from "../lib/focus-by-pointer.js";
|
|
9
9
|
import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
|
|
10
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
10
11
|
import { resetKeys, valueMapping } from "../lib/value-mapping.js";
|
|
11
12
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
12
|
-
import { bindDrag } from "../lib/pointer-drag.js";
|
|
13
13
|
|
|
14
14
|
//#region src/elements/xy.ts
|
|
15
15
|
const axes = ["x", "y"];
|
|
@@ -29,6 +29,7 @@ const orientations = {
|
|
|
29
29
|
};
|
|
30
30
|
const renderXy = template(`
|
|
31
31
|
<div class="sonic-xy">
|
|
32
|
+
<div class="sonic-xy-bracket" aria-hidden="true"></div>
|
|
32
33
|
<div class="sonic-xy-field" aria-hidden="true"></div>
|
|
33
34
|
<div class="sonic-xy-puck">
|
|
34
35
|
<div class="sonic-xy-axis" data-sonic-axis="x" role="slider" tabindex="0"></div>
|
|
@@ -193,6 +194,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
193
194
|
#xy = renderXy();
|
|
194
195
|
#field = requireChild(this.#xy, ".sonic-xy-field", HTMLDivElement);
|
|
195
196
|
#formatValue;
|
|
197
|
+
#mapped;
|
|
196
198
|
#parts = {
|
|
197
199
|
x: requireChild(this.#xy, "[data-sonic-axis=\"x\"]", HTMLDivElement),
|
|
198
200
|
y: requireChild(this.#xy, "[data-sonic-axis=\"y\"]", HTMLDivElement)
|
|
@@ -202,18 +204,19 @@ var SonicXy = class extends SonicFormElement {
|
|
|
202
204
|
#readout = new Readout(requireChild(this.#xy, ".sonic-xy-readout", HTMLDivElement));
|
|
203
205
|
#values = {
|
|
204
206
|
x: {
|
|
205
|
-
|
|
207
|
+
asked: void 0,
|
|
206
208
|
value: 0
|
|
207
209
|
},
|
|
208
210
|
y: {
|
|
209
|
-
|
|
211
|
+
asked: void 0,
|
|
210
212
|
value: 0
|
|
211
213
|
}
|
|
212
214
|
};
|
|
213
215
|
attributeChangedCallback(name) {
|
|
216
|
+
this.#mapped = void 0;
|
|
214
217
|
if (name === "x" || name === "y") {
|
|
215
218
|
if (this.#pointerDrag?.current()) return;
|
|
216
|
-
this.#values[name].
|
|
219
|
+
this.#values[name].asked = void 0;
|
|
217
220
|
}
|
|
218
221
|
if (name === "disabled" && this.isDisabled()) this.#pointerDrag?.end();
|
|
219
222
|
this.#refresh();
|
|
@@ -223,12 +226,12 @@ var SonicXy = class extends SonicFormElement {
|
|
|
223
226
|
super.connectedCallback();
|
|
224
227
|
}
|
|
225
228
|
formResetCallback() {
|
|
226
|
-
for (const axis of axes) this.#values[axis].
|
|
229
|
+
for (const axis of axes) this.#values[axis].asked = void 0;
|
|
227
230
|
this.#refresh();
|
|
228
231
|
}
|
|
229
232
|
connect(signal) {
|
|
230
233
|
const xy = this.#xy;
|
|
231
|
-
this.
|
|
234
|
+
this.keepControl(xy, signal);
|
|
232
235
|
this.render();
|
|
233
236
|
this.checkStyles(xy, "xy.css");
|
|
234
237
|
this.#bindPointer(xy, signal);
|
|
@@ -238,6 +241,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
238
241
|
return this.#parts[this.#currentAxis];
|
|
239
242
|
}
|
|
240
243
|
render() {
|
|
244
|
+
if (!this.isBound()) return;
|
|
241
245
|
const { style } = this.#xy;
|
|
242
246
|
const mappings = this.#mappings();
|
|
243
247
|
for (const axis of axes) {
|
|
@@ -365,7 +369,7 @@ var SonicXy = class extends SonicFormElement {
|
|
|
365
369
|
return true;
|
|
366
370
|
}
|
|
367
371
|
#keyTo(axis, key) {
|
|
368
|
-
const next = this.#
|
|
372
|
+
const next = this.#mappings()[axis].keyTarget(key, this.#values[axis].value);
|
|
369
373
|
const hasMoved = this.#set(axis, next ?? NaN);
|
|
370
374
|
this.#currentAxis = axis;
|
|
371
375
|
this.render();
|
|
@@ -390,17 +394,19 @@ var SonicXy = class extends SonicFormElement {
|
|
|
390
394
|
});
|
|
391
395
|
}
|
|
392
396
|
#mappings() {
|
|
393
|
-
return
|
|
397
|
+
if (this.#mapped) return this.#mapped;
|
|
398
|
+
this.#mapped = {
|
|
394
399
|
x: this.#mapping("x"),
|
|
395
400
|
y: this.#mapping("y")
|
|
396
401
|
};
|
|
402
|
+
return this.#mapped;
|
|
397
403
|
}
|
|
398
404
|
#refresh() {
|
|
399
405
|
const mappings = this.#mappings();
|
|
400
406
|
for (const axis of axes) {
|
|
401
407
|
const axisValue = this.#values[axis];
|
|
402
408
|
const mapping = mappings[axis];
|
|
403
|
-
axisValue.value = mapping.snap(axisValue.
|
|
409
|
+
axisValue.value = mapping.snap(axisValue.asked ?? this.numberAttribute(axis, mapping.bounds[0]));
|
|
404
410
|
}
|
|
405
411
|
this.render();
|
|
406
412
|
}
|
|
@@ -412,11 +418,11 @@ var SonicXy = class extends SonicFormElement {
|
|
|
412
418
|
const [low, high] = mappings[axis].bounds;
|
|
413
419
|
const isCurrent = axis === this.#currentAxis;
|
|
414
420
|
const stop = isCurrent ? "0" : "-1";
|
|
415
|
-
part
|
|
416
|
-
part
|
|
417
|
-
part
|
|
418
|
-
part
|
|
419
|
-
part
|
|
421
|
+
writeAttribute(part, "aria-orientation", orientations[axis]);
|
|
422
|
+
writeAttribute(part, "aria-valuemin", String(low));
|
|
423
|
+
writeAttribute(part, "aria-valuemax", String(high));
|
|
424
|
+
writeAttribute(part, "aria-valuenow", String(this.#values[axis].value));
|
|
425
|
+
writeAttribute(part, "aria-valuetext", this.#valueText(axis));
|
|
420
426
|
this.forwardNaming(part, true);
|
|
421
427
|
writeAttribute(part, "aria-hidden", isCurrent ? void 0 : "true");
|
|
422
428
|
writeAttribute(part, "aria-disabled", isDisabled ? "true" : void 0);
|
|
@@ -451,8 +457,8 @@ var SonicXy = class extends SonicFormElement {
|
|
|
451
457
|
#set(axis, next) {
|
|
452
458
|
if (!Number.isFinite(next)) return false;
|
|
453
459
|
const axisValue = this.#values[axis];
|
|
454
|
-
const snapped = this.#
|
|
455
|
-
axisValue.
|
|
460
|
+
const snapped = this.#mappings()[axis].snap(next);
|
|
461
|
+
axisValue.asked = snapped;
|
|
456
462
|
if (snapped === axisValue.value) return false;
|
|
457
463
|
axisValue.value = snapped;
|
|
458
464
|
return true;
|
|
@@ -465,7 +471,16 @@ var SonicXy = class extends SonicFormElement {
|
|
|
465
471
|
}
|
|
466
472
|
#write(axis, next) {
|
|
467
473
|
if (this.#pointerDrag?.current()) return;
|
|
468
|
-
|
|
474
|
+
const axisValue = this.#values[axis];
|
|
475
|
+
if (next === void 0 || next === null) {
|
|
476
|
+
axisValue.asked = void 0;
|
|
477
|
+
this.#refresh();
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const asked = toNumber(next);
|
|
481
|
+
const hasMoved = this.#set(axis, asked);
|
|
482
|
+
if (Number.isFinite(asked)) axisValue.asked = asked;
|
|
483
|
+
if (hasMoved) this.render();
|
|
469
484
|
}
|
|
470
485
|
};
|
|
471
486
|
|
package/dist/default/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { SonicButton } from "./elements/button.js";
|
|
1
2
|
import { SonicDial } from "./elements/dial.js";
|
|
2
3
|
import { envelopeCurve } from "./lib/envelope-curve.js";
|
|
3
4
|
import { SonicEnvelope } from "./elements/envelope.js";
|
|
4
|
-
import { SonicButton } from "./elements/button.js";
|
|
5
|
-
import { SonicSwitch } from "./elements/switch.js";
|
|
6
5
|
import { SonicMeter } from "./elements/meter.js";
|
|
7
6
|
import { SonicNumber } from "./elements/number.js";
|
|
8
7
|
import { SonicSegmented } from "./elements/segmented.js";
|
|
9
8
|
import { SonicSlider } from "./elements/slider.js";
|
|
10
9
|
import { SonicSplit } from "./elements/split.js";
|
|
10
|
+
import { SonicSwitch } from "./elements/switch.js";
|
|
11
11
|
import { SonicWaveform } from "./elements/waveform.js";
|
|
12
12
|
import { SonicWavestrip } from "./elements/wavestrip.js";
|
|
13
13
|
import { SonicXy } from "./elements/xy.js";
|
|
@@ -98,8 +98,13 @@ function adsrShape(proportions, curves = {}) {
|
|
|
98
98
|
}
|
|
99
99
|
function adsrPath({ points }) {
|
|
100
100
|
const stroke = points.map(({ x, y }, index) => `${index === 0 ? "M" : "L"}${String(roundTo(x, 4))} ${String(roundTo(1 - y, 4))}`).join("");
|
|
101
|
+
const floor = {
|
|
102
|
+
from: roundTo(points[0]?.x ?? 0, 4),
|
|
103
|
+
to: roundTo(points.at(-1)?.x ?? 0, 4)
|
|
104
|
+
};
|
|
101
105
|
return {
|
|
102
106
|
fill: `${stroke}Z`,
|
|
107
|
+
floor,
|
|
103
108
|
stroke
|
|
104
109
|
};
|
|
105
110
|
}
|
|
@@ -3,13 +3,13 @@ const burstTimeConstants = -Math.log(1 - 10 ** (-2 / 20));
|
|
|
3
3
|
function ppmRate({ burstMs, fallDecibels, fallSeconds }) {
|
|
4
4
|
return {
|
|
5
5
|
fallDecibelsPerSecond: fallDecibels / fallSeconds,
|
|
6
|
-
|
|
6
|
+
riseTimeConstantMs: burstMs / burstTimeConstants
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
9
|
const ballisticsRates = {
|
|
10
10
|
peak: {
|
|
11
11
|
fallDecibelsPerSecond: 20,
|
|
12
|
-
|
|
12
|
+
riseTimeConstantMs: 0
|
|
13
13
|
},
|
|
14
14
|
"ppm-1": ppmRate({
|
|
15
15
|
burstMs: 5,
|
|
@@ -69,12 +69,22 @@ var CanvasSurface = class {
|
|
|
69
69
|
style.setProperty("transition-timing-function", "step-start");
|
|
70
70
|
style.setProperty("transition-behavior", "allow-discrete");
|
|
71
71
|
}
|
|
72
|
+
#current() {
|
|
73
|
+
if (this.#look && !this.#isRebuildWanted) return {
|
|
74
|
+
isRebuilt: false,
|
|
75
|
+
look: this.#look
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
isRebuilt: true,
|
|
79
|
+
look: this.#read()
|
|
80
|
+
};
|
|
81
|
+
}
|
|
72
82
|
#draw(frameMs) {
|
|
73
83
|
this.#frame = void 0;
|
|
74
84
|
const size = this.#size;
|
|
75
85
|
if (size.width === 0 || size.height === 0) return;
|
|
76
86
|
const { canvas, paint } = this.#options;
|
|
77
|
-
const
|
|
87
|
+
const { isRebuilt, look } = this.#current();
|
|
78
88
|
const isDirty = this.#isDirty;
|
|
79
89
|
this.#look = look;
|
|
80
90
|
this.#isDirty = false;
|
|
@@ -84,6 +94,7 @@ var CanvasSurface = class {
|
|
|
84
94
|
if (this.#context) paint(this.#context, {
|
|
85
95
|
frameMs,
|
|
86
96
|
isDirty,
|
|
97
|
+
isRebuilt,
|
|
87
98
|
look,
|
|
88
99
|
size
|
|
89
100
|
});
|
|
@@ -140,7 +151,7 @@ var CanvasSurface = class {
|
|
|
140
151
|
const { canvas, colours, lengths = {} } = this.#options;
|
|
141
152
|
const styles = getComputedStyle(canvas);
|
|
142
153
|
return {
|
|
143
|
-
colours: readEach(colours, (property) => styles.getPropertyValue(property).trim()),
|
|
154
|
+
colours: readEach(colours, (property) => styles.getPropertyValue(property).trim().replaceAll("currentcolor", () => styles.color)),
|
|
144
155
|
isReducedMotion: this.#reducedMotion.matches,
|
|
145
156
|
lengths: readEach(lengths, (property) => readPxProperty(styles, property, 0))
|
|
146
157
|
};
|
|
@@ -77,7 +77,7 @@ function byShortfall(values, { exact, order }, direction) {
|
|
|
77
77
|
const value = values[index] ?? NaN;
|
|
78
78
|
return trimFloat(direction * ((exact.get(index) ?? value) - value));
|
|
79
79
|
};
|
|
80
|
-
return order.toReversed().
|
|
80
|
+
return order.toReversed().toSorted((first, second) => shortfall(second) - shortfall(first));
|
|
81
81
|
}
|
|
82
82
|
function stepEach(members, values, pass) {
|
|
83
83
|
let remainder = pass.owed;
|
|
@@ -10,18 +10,18 @@ function dragThresholdPx(pointerType) {
|
|
|
10
10
|
function isAcross(from, to, proportion) {
|
|
11
11
|
return from !== proportion && (from - proportion) * (to - proportion) <= 0;
|
|
12
12
|
}
|
|
13
|
-
function passDetent(hold, from, to) {
|
|
13
|
+
function passDetent(hold, [from, to], pace) {
|
|
14
14
|
if (hold.slack === void 0 && !isAcross(from, to, hold.proportion)) return {
|
|
15
15
|
proportion: to,
|
|
16
16
|
slack: void 0
|
|
17
17
|
};
|
|
18
|
-
const slack = (hold.slack ?? 0) + to - hold.proportion;
|
|
18
|
+
const slack = (hold.slack ?? 0) + (to - hold.proportion) / pace;
|
|
19
19
|
if (Math.abs(slack) <= hold.zone) return {
|
|
20
20
|
proportion: void 0,
|
|
21
21
|
slack
|
|
22
22
|
};
|
|
23
23
|
return {
|
|
24
|
-
proportion: hold.proportion + slack - Math.sign(slack) * hold.zone,
|
|
24
|
+
proportion: hold.proportion + (slack - Math.sign(slack) * hold.zone) * pace,
|
|
25
25
|
slack: void 0
|
|
26
26
|
};
|
|
27
27
|
}
|
|
@@ -77,7 +77,7 @@ function stepDrag(mapping, state, move) {
|
|
|
77
77
|
};
|
|
78
78
|
const { detent } = state;
|
|
79
79
|
if (!detent) return land(mapping, moved, to);
|
|
80
|
-
const passed = passDetent(mapping.isWrapping ? nearestTurn(detent, state.rawProportion) : detent, state.rawProportion, to);
|
|
80
|
+
const passed = passDetent(mapping.isWrapping ? nearestTurn(detent, state.rawProportion) : detent, [state.rawProportion, to], pace);
|
|
81
81
|
const held = {
|
|
82
82
|
...moved,
|
|
83
83
|
detent: {
|
|
@@ -2,6 +2,7 @@ import { trimFloat } from "./math.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/lib/frame-timeline.ts
|
|
4
4
|
const placeholderPeriodMs = 2500;
|
|
5
|
+
const lookAheadSeconds = 30;
|
|
5
6
|
function windowStart(input, seconds, windowSeconds) {
|
|
6
7
|
if (!input.isPaged) return seconds - windowSeconds / 2;
|
|
7
8
|
if (input.held) return input.held.grabbed.startSeconds + seconds - input.held.grabbed.seconds;
|
|
@@ -31,7 +32,7 @@ function frameTimeline(input) {
|
|
|
31
32
|
const pending = pendingIn(input, [startSeconds, startSeconds + windowSeconds]);
|
|
32
33
|
const placeholder = placeholderPhase(input, pending);
|
|
33
34
|
const phase = placeholder ?? 0;
|
|
34
|
-
const wanted = overlap([startSeconds - windowSeconds, startSeconds +
|
|
35
|
+
const wanted = overlap([startSeconds - windowSeconds, startSeconds + windowSeconds + Math.max(windowSeconds, lookAheadSeconds)], range);
|
|
35
36
|
const at = (time) => (time - startSeconds) / windowSeconds;
|
|
36
37
|
return {
|
|
37
38
|
...held ? { ghostAt: at(clockSeconds) } : {},
|
|
@@ -3,28 +3,72 @@ import { readPxProperty } from "./read-px-property.js";
|
|
|
3
3
|
//#region src/lib/marker-band.ts
|
|
4
4
|
const kindPattern = /^[a-z][a-z0-9-]*$/;
|
|
5
5
|
const snapSlopPx = 4;
|
|
6
|
-
function
|
|
6
|
+
function isKind(kind) {
|
|
7
|
+
return kind !== void 0 && kindPattern.test(kind);
|
|
8
|
+
}
|
|
9
|
+
function writeKind(part, kind, fallback) {
|
|
10
|
+
if (!isKind(kind)) {
|
|
11
|
+
part.style.removeProperty("--_sonic-marker");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
part.style.setProperty("--_sonic-marker", `var(--sonic-marker-${kind}, var(${fallback}))`);
|
|
15
|
+
}
|
|
16
|
+
function renderMarker(marker, [from, to], lane) {
|
|
7
17
|
const part = document.createElement("div");
|
|
8
|
-
part.className =
|
|
18
|
+
part.className = lane === void 0 ? "sonic-wavestrip-region" : "sonic-wavestrip-marker";
|
|
19
|
+
if (lane !== void 0) part.style.setProperty("--_sonic-marker-lane", String(lane));
|
|
9
20
|
part.style.setProperty("--_sonic-marker-from", String(from));
|
|
10
21
|
part.style.setProperty("--_sonic-marker-to", String(to));
|
|
11
|
-
|
|
22
|
+
writeKind(part, marker.kind, "--_sonic-marker-default");
|
|
12
23
|
if (marker.dimmed === true) part.dataset.sonicDimmed = "";
|
|
13
24
|
return part;
|
|
14
25
|
}
|
|
15
26
|
function createMarkerBand(band) {
|
|
27
|
+
let dots = [];
|
|
28
|
+
const px = (property) => readPxProperty(getComputedStyle(band), property, 0);
|
|
16
29
|
return {
|
|
17
|
-
|
|
30
|
+
at: ({ x, y }, widthPx) => {
|
|
31
|
+
const size = px("--_sonic-marker-size");
|
|
32
|
+
const step = px("--_sonic-marker-step");
|
|
33
|
+
const nearest = nearestDot({
|
|
34
|
+
x,
|
|
35
|
+
y
|
|
36
|
+
}, dots.map(({ at, lane }) => ({
|
|
37
|
+
x: at * widthPx,
|
|
38
|
+
y: size / 2 + lane * step
|
|
39
|
+
})), size / 2 + snapSlopPx);
|
|
40
|
+
return nearest === void 0 ? void 0 : dots[nearest]?.index;
|
|
41
|
+
},
|
|
18
42
|
render: (markers, proportions) => {
|
|
19
|
-
|
|
43
|
+
const widthPx = band.getBoundingClientRect().width;
|
|
44
|
+
const points = markers.flatMap((marker, index) => marker.end === void 0 ? [{
|
|
45
|
+
at: proportions[index]?.[0] ?? 0,
|
|
46
|
+
index
|
|
47
|
+
}] : []);
|
|
48
|
+
const lanes = markerLanes(points.map(({ at }) => at * widthPx), px("--_sonic-marker-step"));
|
|
49
|
+
dots = points.map((point, order) => ({
|
|
50
|
+
...point,
|
|
51
|
+
lane: lanes[order] ?? 0
|
|
52
|
+
}));
|
|
53
|
+
const laneOf = new Map(dots.map(({ index, lane }) => [index, lane]));
|
|
54
|
+
band.replaceChildren(...markers.map((marker, index) => renderMarker(marker, proportions[index] ?? [0, 0], laneOf.get(index))));
|
|
20
55
|
}
|
|
21
56
|
};
|
|
22
57
|
}
|
|
23
|
-
function
|
|
58
|
+
function markerLanes(positions, minDistance) {
|
|
59
|
+
const lastInLane = [];
|
|
60
|
+
return positions.map((position) => {
|
|
61
|
+
const free = lastInLane.findIndex((last) => position - last >= minDistance);
|
|
62
|
+
const lane = free === -1 ? lastInLane.length : free;
|
|
63
|
+
lastInLane[lane] = position;
|
|
64
|
+
return lane;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function nearestDot(point, dots, reach) {
|
|
24
68
|
let nearest;
|
|
25
|
-
let nearestDistance = reach;
|
|
26
|
-
for (const [index,
|
|
27
|
-
const distance =
|
|
69
|
+
let nearestDistance = reach * reach;
|
|
70
|
+
for (const [index, dot] of dots.entries()) {
|
|
71
|
+
const distance = (dot.x - point.x) ** 2 + (dot.y - point.y) ** 2;
|
|
28
72
|
if (distance > nearestDistance) continue;
|
|
29
73
|
nearest = index;
|
|
30
74
|
nearestDistance = distance;
|
|
@@ -33,4 +77,4 @@ function nearestMarker(proportion, markerProportions, reach) {
|
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
//#endregion
|
|
36
|
-
export { createMarkerBand,
|
|
80
|
+
export { createMarkerBand, isKind, markerLanes, nearestDot, writeKind };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { clamp } from "./math.js";
|
|
2
2
|
import { requireChild } from "./render.js";
|
|
3
3
|
import { readPxProperty } from "./read-px-property.js";
|
|
4
|
+
import { writeKind } from "./marker-band.js";
|
|
4
5
|
|
|
5
6
|
//#region src/lib/marker-rider.ts
|
|
6
7
|
const fadeShare = .25;
|
|
@@ -43,6 +44,7 @@ function layoutRider(labels, view) {
|
|
|
43
44
|
function labelSlot(element) {
|
|
44
45
|
return {
|
|
45
46
|
element,
|
|
47
|
+
kind: void 0,
|
|
46
48
|
opacity: NaN,
|
|
47
49
|
shownPx: NaN,
|
|
48
50
|
x: NaN
|
|
@@ -63,13 +65,17 @@ function writePlacement(slot, { opacity, shownPx, x }, widthPx) {
|
|
|
63
65
|
style.setProperty("opacity", opacity < 1 ? opacity.toFixed(3) : "");
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
|
-
function writeLabel(slot, labels, placement) {
|
|
68
|
+
function writeLabel(slot, [labels, colourProperty], placement) {
|
|
67
69
|
const { element } = slot;
|
|
68
70
|
const label = placement && placement.shownPx > 0 ? labels[placement.index] : void 0;
|
|
69
71
|
if (element.hidden !== !label) element.hidden = !label;
|
|
70
72
|
if (!label || !placement) return;
|
|
71
73
|
if (element.textContent !== label.text) element.textContent = label.text;
|
|
72
74
|
element.toggleAttribute("data-sonic-dimmed", label.isDimmed);
|
|
75
|
+
if (slot.kind !== label.kind) {
|
|
76
|
+
slot.kind = label.kind;
|
|
77
|
+
writeKind(element, label.kind, colourProperty);
|
|
78
|
+
}
|
|
73
79
|
writePlacement(slot, placement, label.widthPx);
|
|
74
80
|
}
|
|
75
81
|
function measureWidths(control, className, texts) {
|
|
@@ -85,7 +91,7 @@ function measureWidths(control, className, texts) {
|
|
|
85
91
|
return widths;
|
|
86
92
|
}
|
|
87
93
|
function createLabelRider(control, options) {
|
|
88
|
-
const { className, insetProperty } = options;
|
|
94
|
+
const { className, colourProperty, insetProperty } = options;
|
|
89
95
|
const parked = labelSlot(requireChild(control, `.${className}`, HTMLElement));
|
|
90
96
|
const arriving = labelSlot(requireChild(control, `.${className} + .${className}`, HTMLElement));
|
|
91
97
|
let insetPx = 0;
|
|
@@ -104,8 +110,8 @@ function createLabelRider(control, options) {
|
|
|
104
110
|
...window,
|
|
105
111
|
insetPx
|
|
106
112
|
});
|
|
107
|
-
writeLabel(parked, measured, layout.parked);
|
|
108
|
-
writeLabel(arriving, measured, layout.arriving);
|
|
113
|
+
writeLabel(parked, [measured, colourProperty], layout.parked);
|
|
114
|
+
writeLabel(arriving, [measured, colourProperty], layout.arriving);
|
|
109
115
|
}
|
|
110
116
|
};
|
|
111
117
|
}
|
package/dist/default/lib/math.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
function clamp(value, min, max) {
|
|
3
3
|
return Math.min(max, Math.max(min, value));
|
|
4
4
|
}
|
|
5
|
+
function toNumber(value) {
|
|
6
|
+
if (typeof value === "number") return value;
|
|
7
|
+
if (typeof value !== "string" || value.trim() === "") return NaN;
|
|
8
|
+
return Number(value);
|
|
9
|
+
}
|
|
5
10
|
function clampProportion(fraction) {
|
|
6
11
|
return clamp(fraction, 0, 1);
|
|
7
12
|
}
|
|
@@ -22,4 +27,4 @@ function roundTo(value, places) {
|
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
//#endregion
|
|
25
|
-
export { clamp, clampProportion, decimalPlaces, roundTo, trimFloat, wrapProportion };
|
|
30
|
+
export { clamp, clampProportion, decimalPlaces, roundTo, toNumber, trimFloat, wrapProportion };
|