@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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { appendOnce } from "./owned-control.js";
|
|
2
|
+
import { attachSlots } from "./render.js";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/mirror-children.ts
|
|
5
|
+
const hostSlots = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function childHolding(host, node) {
|
|
7
|
+
let current = node;
|
|
8
|
+
while (current && current.parentNode !== host) current = current.parentNode;
|
|
9
|
+
return current ?? void 0;
|
|
10
|
+
}
|
|
11
|
+
function isCopiedChange(host, record, { control, isCopied }) {
|
|
12
|
+
if (record.target === host) return record.removedNodes.length > 0 || [...record.addedNodes].some((node) => node !== control);
|
|
13
|
+
const child = childHolding(host, record.target);
|
|
14
|
+
if (child === void 0) return false;
|
|
15
|
+
return isCopied(child) || record.type === "attributes" && record.target === child;
|
|
16
|
+
}
|
|
17
|
+
function mirrorChildren(host, { control, copy, isCopied = () => true, isPassed = () => false, place }, signal) {
|
|
18
|
+
const slots = hostSlots.get(host) ?? attachSlots(host);
|
|
19
|
+
let copies = /* @__PURE__ */ new Map();
|
|
20
|
+
hostSlots.set(host, slots);
|
|
21
|
+
const mirror = (touched) => {
|
|
22
|
+
appendOnce(host, control);
|
|
23
|
+
const children = [...host.childNodes].filter((child) => child !== control);
|
|
24
|
+
const slotted = children.filter((child) => child instanceof Element || child instanceof Text);
|
|
25
|
+
const kept = copies;
|
|
26
|
+
const originals = children.filter((child) => isCopied(child));
|
|
27
|
+
copies = new Map(originals.map((original) => [original, touched && !touched.has(original) && kept.has(original) ? kept.get(original) : copy(original)]));
|
|
28
|
+
place([...copies.values()].filter((made) => made !== void 0));
|
|
29
|
+
slots.shown.assign(...slotted.filter((child) => !isCopied(child)), control);
|
|
30
|
+
slots.kept.assign(...slotted.filter((child) => isCopied(child)));
|
|
31
|
+
};
|
|
32
|
+
const watch = () => {
|
|
33
|
+
observer.disconnect();
|
|
34
|
+
observer.observe(host, { childList: true });
|
|
35
|
+
for (const child of host.childNodes) {
|
|
36
|
+
if (child === control) continue;
|
|
37
|
+
observer.observe(child, {
|
|
38
|
+
attributes: true,
|
|
39
|
+
characterData: true,
|
|
40
|
+
childList: true,
|
|
41
|
+
subtree: true
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const observer = new MutationObserver((records) => {
|
|
46
|
+
const changes = records.filter((record) => isCopiedChange(host, record, {
|
|
47
|
+
control,
|
|
48
|
+
isCopied
|
|
49
|
+
}));
|
|
50
|
+
if (changes.length === 0) return;
|
|
51
|
+
if (changes.some((record) => record.target === host)) {
|
|
52
|
+
mirror();
|
|
53
|
+
watch();
|
|
54
|
+
} else mirror(new Set(changes.map((record) => childHolding(host, record.target))));
|
|
55
|
+
});
|
|
56
|
+
mirror();
|
|
57
|
+
watch();
|
|
58
|
+
signal.addEventListener("abort", () => {
|
|
59
|
+
observer.disconnect();
|
|
60
|
+
}, { once: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { mirrorChildren };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/lib/owned-control.ts
|
|
2
|
+
function appendOnce(host, control) {
|
|
3
|
+
const [hook] = control.classList;
|
|
4
|
+
const stale = [...host.children].filter((child) => child !== control && hook !== void 0 && child.classList.contains(hook));
|
|
5
|
+
for (const child of stale) child.remove();
|
|
6
|
+
if (control.parentNode !== host) host.append(control);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { appendOnce };
|
|
@@ -5,8 +5,8 @@ function bindDrag(target, handlers, signal) {
|
|
|
5
5
|
if (!held) return;
|
|
6
6
|
const { drag } = held;
|
|
7
7
|
held = void 0;
|
|
8
|
-
handlers.toggle(false);
|
|
9
|
-
handlers.release(drag);
|
|
8
|
+
handlers.toggle?.(false);
|
|
9
|
+
handlers.release?.(drag);
|
|
10
10
|
};
|
|
11
11
|
const whileHeld = (type, handle) => {
|
|
12
12
|
target.addEventListener(type, (event) => {
|
|
@@ -23,15 +23,18 @@ function bindDrag(target, handlers, signal) {
|
|
|
23
23
|
pointerId: event.pointerId
|
|
24
24
|
};
|
|
25
25
|
target.setPointerCapture(event.pointerId);
|
|
26
|
-
handlers.toggle(true);
|
|
26
|
+
handlers.toggle?.(true);
|
|
27
27
|
}, { signal });
|
|
28
28
|
whileHeld("pointermove", handlers.move);
|
|
29
29
|
whileHeld("pointercancel", (drag) => {
|
|
30
|
-
handlers.cancel(drag);
|
|
30
|
+
handlers.cancel?.(drag);
|
|
31
31
|
end();
|
|
32
32
|
});
|
|
33
33
|
whileHeld("lostpointercapture", end);
|
|
34
|
-
whileHeld("pointerup",
|
|
34
|
+
whileHeld("pointerup", (drag, event) => {
|
|
35
|
+
end();
|
|
36
|
+
handlers.lift?.(drag, event);
|
|
37
|
+
});
|
|
35
38
|
signal.addEventListener("abort", end, { once: true });
|
|
36
39
|
return {
|
|
37
40
|
current: () => held?.drag,
|
|
@@ -11,7 +11,17 @@ function downsample(peaks, count) {
|
|
|
11
11
|
for (let source = start; source < end; source += 1) total += peaks[source] ?? 0;
|
|
12
12
|
resampled.push(total / (end - start));
|
|
13
13
|
}
|
|
14
|
-
return resampled;
|
|
14
|
+
return rescale(resampled, peakOf(peaks));
|
|
15
|
+
}
|
|
16
|
+
function peakOf(peaks) {
|
|
17
|
+
let loudest = 0;
|
|
18
|
+
for (const peak of peaks) loudest = Math.max(loudest, peak);
|
|
19
|
+
return loudest;
|
|
20
|
+
}
|
|
21
|
+
function rescale(bars, loudest) {
|
|
22
|
+
const tallest = peakOf(bars);
|
|
23
|
+
if (tallest <= 0) return bars;
|
|
24
|
+
return bars.map((bar) => bar * loudest / tallest);
|
|
15
25
|
}
|
|
16
26
|
function upsample(peaks, count) {
|
|
17
27
|
const resampled = [];
|
|
@@ -3,6 +3,9 @@ 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 sortedRegions(regions) {
|
|
7
|
+
return regions ? readRegions(regions).toSorted((first, second) => first[0] - second[0]) : [];
|
|
8
|
+
}
|
|
6
9
|
|
|
7
10
|
//#endregion
|
|
8
|
-
export { readRegions };
|
|
11
|
+
export { readRegions, sortedRegions };
|
package/dist/dev/dat.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export interface DatHeader {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function readDatHeader(buffer: ArrayBuffer): DatHeader;
|
|
14
14
|
export declare function datByteRange(header: DatHeader, fromPair: number, toPair: number): [number, number] | undefined;
|
|
15
|
-
export declare function readDatSamples(header: DatHeader, body: ArrayBuffer): Int8Array | Int16Array;
|
|
16
|
-
export declare function readDatPeaks(header: DatHeader, body: ArrayBuffer): WaveformPeaks;
|
|
15
|
+
export declare function readDatSamples(header: DatHeader, body: ArrayBuffer, byteOffset?: number): Int8Array | Int16Array;
|
|
16
|
+
export declare function readDatPeaks(header: DatHeader, body: ArrayBuffer, byteOffset?: number): WaveformPeaks;
|
|
17
17
|
//#endregion
|
|
18
18
|
export type { WaveformPeaks };
|
|
19
19
|
//# sourceMappingURL=dat.d.ts.map
|
package/dist/dev/dat.js
CHANGED
|
@@ -16,6 +16,7 @@ function readDatHeader(buffer) {
|
|
|
16
16
|
if (channels < 1) throw new Error("A .dat header names no channels");
|
|
17
17
|
const sampleRate = view.getInt32(8, true);
|
|
18
18
|
const samplesPerPixel = view.getInt32(12, true);
|
|
19
|
+
if (samplesPerPixel < 1) throw new Error("A .dat header names no samples per pixel");
|
|
19
20
|
return {
|
|
20
21
|
bits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,
|
|
21
22
|
channels,
|
|
@@ -34,15 +35,15 @@ function datByteRange(header, fromPair, toPair) {
|
|
|
34
35
|
const pairBytes = header.channels * 2 * header.bits / 8;
|
|
35
36
|
return [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];
|
|
36
37
|
}
|
|
37
|
-
function readDatSamples(header, body) {
|
|
38
|
-
return header.bits === 8 ? new Int8Array(body) : new Int16Array(body,
|
|
38
|
+
function readDatSamples(header, body, byteOffset = 0) {
|
|
39
|
+
return header.bits === 8 ? new Int8Array(body, byteOffset) : new Int16Array(body, byteOffset, Math.floor((body.byteLength - byteOffset) / 2));
|
|
39
40
|
}
|
|
40
|
-
function readDatPeaks(header, body) {
|
|
41
|
+
function readDatPeaks(header, body, byteOffset = 0) {
|
|
41
42
|
return {
|
|
42
43
|
channels: header.channels,
|
|
43
44
|
fullScale: header.bits === 8 ? 128 : 32768,
|
|
44
45
|
pairsPerSecond: header.pairsPerSecond,
|
|
45
|
-
samples: readDatSamples(header, body)
|
|
46
|
+
samples: readDatSamples(header, body, byteOffset)
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
package/dist/dev/dat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dat.js","names":[],"sources":["../../src/dat.ts"],"sourcesContent":["import type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { clamp } from '#lib/math.ts';\n\nexport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nexport interface DatHeader {\n\tbits: 8 | 16;\n\tchannels: number;\n\tdataOffset: number;\n\tpairs: number;\n\tpairsPerSecond: number;\n\tsampleRate: number;\n\tsamplesPerPixel: number;\n\tversion: 1 | 2;\n}\n\nfunction readVersion(view: DataView): 1 | 2 {\n\tconst version = view.getInt32(0, true);\n\tif (version === 1 || version === 2) return version;\n\n\tthrow new Error(`A .dat file of version ${String(version)} is not one this reads`);\n}\n\nexport function readDatHeader(buffer: ArrayBuffer): DatHeader {\n\tif (buffer.byteLength < 20) throw new Error('A .dat header takes at least 20 bytes');\n\n\tconst view = new DataView(buffer);\n\tconst version = readVersion(view);\n\tconst dataOffset = version === 1 ? 20 : 24;\n\tif (buffer.byteLength < dataOffset) {\n\t\tthrow new Error(`A version ${String(version)} .dat header takes ${String(dataOffset)} bytes`);\n\t}\n\n\tconst channels = version === 1 ? 1 : view.getInt32(20, true);\n\tif (channels < 1) throw new Error('A .dat header names no channels');\n\n\tconst sampleRate = view.getInt32(8, true);\n\tconst samplesPerPixel = view.getInt32(12, true);\n\n\treturn {\n\t\tbits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,\n\t\tchannels,\n\t\tdataOffset,\n\t\tpairs: view.getUint32(16, true),\n\t\tpairsPerSecond: sampleRate / samplesPerPixel,\n\t\tsampleRate,\n\t\tsamplesPerPixel,\n\t\tversion,\n\t};\n}\n\nexport function datByteRange(\n\theader: DatHeader,\n\tfromPair: number,\n\ttoPair: number,\n): [number, number] | undefined {\n\tconst from = clamp(Math.floor(fromPair), 0, header.pairs);\n\tconst to = clamp(Math.ceil(toPair), from, header.pairs);\n\t// A server ignores an inverted `Range` and sends the whole file\n\tif (!(to > from)) return undefined;\n\n\tconst pairBytes = (header.channels * 2 * header.bits) / 8;\n\n\treturn [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];\n}\n\n// Typed arrays read the platform's byte order; every browser platform is little-endian, as the format is\n// A range response can end mid-sample; the odd byte is dropped\nexport function readDatSamples(
|
|
1
|
+
{"version":3,"file":"dat.js","names":[],"sources":["../../src/dat.ts"],"sourcesContent":["import type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { clamp } from '#lib/math.ts';\n\nexport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nexport interface DatHeader {\n\tbits: 8 | 16;\n\tchannels: number;\n\tdataOffset: number;\n\tpairs: number;\n\tpairsPerSecond: number;\n\tsampleRate: number;\n\tsamplesPerPixel: number;\n\tversion: 1 | 2;\n}\n\nfunction readVersion(view: DataView): 1 | 2 {\n\tconst version = view.getInt32(0, true);\n\tif (version === 1 || version === 2) return version;\n\n\tthrow new Error(`A .dat file of version ${String(version)} is not one this reads`);\n}\n\nexport function readDatHeader(buffer: ArrayBuffer): DatHeader {\n\tif (buffer.byteLength < 20) throw new Error('A .dat header takes at least 20 bytes');\n\n\tconst view = new DataView(buffer);\n\tconst version = readVersion(view);\n\tconst dataOffset = version === 1 ? 20 : 24;\n\tif (buffer.byteLength < dataOffset) {\n\t\tthrow new Error(`A version ${String(version)} .dat header takes ${String(dataOffset)} bytes`);\n\t}\n\n\tconst channels = version === 1 ? 1 : view.getInt32(20, true);\n\tif (channels < 1) throw new Error('A .dat header names no channels');\n\n\tconst sampleRate = view.getInt32(8, true);\n\tconst samplesPerPixel = view.getInt32(12, true);\n\tif (samplesPerPixel < 1) throw new Error('A .dat header names no samples per pixel');\n\n\treturn {\n\t\tbits: (view.getUint32(4, true) & 1) === 1 ? 8 : 16,\n\t\tchannels,\n\t\tdataOffset,\n\t\tpairs: view.getUint32(16, true),\n\t\tpairsPerSecond: sampleRate / samplesPerPixel,\n\t\tsampleRate,\n\t\tsamplesPerPixel,\n\t\tversion,\n\t};\n}\n\nexport function datByteRange(\n\theader: DatHeader,\n\tfromPair: number,\n\ttoPair: number,\n): [number, number] | undefined {\n\tconst from = clamp(Math.floor(fromPair), 0, header.pairs);\n\tconst to = clamp(Math.ceil(toPair), from, header.pairs);\n\t// A server ignores an inverted `Range` and sends the whole file\n\tif (!(to > from)) return undefined;\n\n\tconst pairBytes = (header.channels * 2 * header.bits) / 8;\n\n\treturn [header.dataOffset + from * pairBytes, header.dataOffset + to * pairBytes - 1];\n}\n\n// Typed arrays read the platform's byte order; every browser platform is little-endian, as the format is\n// A range response can end mid-sample; the odd byte is dropped\nexport function readDatSamples(\n\theader: DatHeader,\n\tbody: ArrayBuffer,\n\tbyteOffset = 0,\n): Int8Array | Int16Array {\n\treturn header.bits === 8\n\t\t? new Int8Array(body, byteOffset)\n\t\t: new Int16Array(body, byteOffset, Math.floor((body.byteLength - byteOffset) / 2));\n}\n\nexport function readDatPeaks(header: DatHeader, body: ArrayBuffer, byteOffset = 0): WaveformPeaks {\n\treturn {\n\t\tchannels: header.channels,\n\t\tfullScale: header.bits === 8 ? 128 : 32_768,\n\t\tpairsPerSecond: header.pairsPerSecond,\n\t\tsamples: readDatSamples(header, body, byteOffset),\n\t};\n}\n"],"mappings":";;;AAiBA,SAAS,YAAY,MAAuB;CAC3C,MAAM,UAAU,KAAK,SAAS,GAAG,IAAI;CACrC,IAAI,YAAY,KAAK,YAAY,GAAG,OAAO;CAE3C,MAAM,IAAI,MAAM,0BAA0B,OAAO,OAAO,EAAE,uBAAuB;AAClF;AAEA,SAAgB,cAAc,QAAgC;CAC7D,IAAI,OAAO,aAAa,IAAI,MAAM,IAAI,MAAM,uCAAuC;CAEnF,MAAM,OAAO,IAAI,SAAS,MAAM;CAChC,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,aAAa,YAAY,IAAI,KAAK;CACxC,IAAI,OAAO,aAAa,YACvB,MAAM,IAAI,MAAM,aAAa,OAAO,OAAO,EAAE,qBAAqB,OAAO,UAAU,EAAE,OAAO;CAG7F,MAAM,WAAW,YAAY,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI;CAC3D,IAAI,WAAW,GAAG,MAAM,IAAI,MAAM,iCAAiC;CAEnE,MAAM,aAAa,KAAK,SAAS,GAAG,IAAI;CACxC,MAAM,kBAAkB,KAAK,SAAS,IAAI,IAAI;CAC9C,IAAI,kBAAkB,GAAG,MAAM,IAAI,MAAM,0CAA0C;CAEnF,OAAO;EACN,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI,OAAO,IAAI,IAAI;EAChD;EACA;EACA,OAAO,KAAK,UAAU,IAAI,IAAI;EAC9B,gBAAgB,aAAa;EAC7B;EACA;EACA;CACD;AACD;AAEA,SAAgB,aACf,QACA,UACA,QAC+B;CAC/B,MAAM,OAAO,MAAM,KAAK,MAAM,QAAQ,GAAG,GAAG,OAAO,KAAK;CACxD,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,GAAG,MAAM,OAAO,KAAK;CAEtD,IAAI,EAAE,KAAK,OAAO,OAAO;CAEzB,MAAM,YAAa,OAAO,WAAW,IAAI,OAAO,OAAQ;CAExD,OAAO,CAAC,OAAO,aAAa,OAAO,WAAW,OAAO,aAAa,KAAK,YAAY,CAAC;AACrF;AAIA,SAAgB,eACf,QACA,MACA,aAAa,GACY;CACzB,OAAO,OAAO,SAAS,IACpB,IAAI,UAAU,MAAM,UAAU,IAC9B,IAAI,WAAW,MAAM,YAAY,KAAK,OAAO,KAAK,aAAa,cAAc,CAAC,CAAC;AACnF;AAEA,SAAgB,aAAa,QAAmB,MAAmB,aAAa,GAAkB;CACjG,OAAO;EACN,UAAU,OAAO;EACjB,WAAW,OAAO,SAAS,IAAI,MAAM;EACrC,gBAAgB,OAAO;EACvB,SAAS,eAAe,QAAQ,MAAM,UAAU;CACjD;AACD"}
|
package/dist/dev/define.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { SonicButton } from "./elements/button.js";
|
|
2
|
+
import "./define/button.js";
|
|
1
3
|
import { SonicDial } from "./elements/dial.js";
|
|
2
4
|
import "./define/dial.js";
|
|
3
5
|
import { SonicEnvelope } from "./elements/envelope.js";
|
|
4
6
|
import "./define/envelope.js";
|
|
5
|
-
import { SonicButton } from "./elements/button.js";
|
|
6
|
-
import "./define/button.js";
|
|
7
|
-
import { SonicSwitch } from "./elements/switch.js";
|
|
8
|
-
import "./define/switch.js";
|
|
9
7
|
import { SonicMeter } from "./elements/meter.js";
|
|
10
8
|
import "./define/meter.js";
|
|
11
9
|
import { SonicNumber } from "./elements/number.js";
|
|
@@ -16,6 +14,8 @@ import { SonicSlider } from "./elements/slider.js";
|
|
|
16
14
|
import "./define/slider.js";
|
|
17
15
|
import { SonicSplit } from "./elements/split.js";
|
|
18
16
|
import "./define/split.js";
|
|
17
|
+
import { SonicSwitch } from "./elements/switch.js";
|
|
18
|
+
import "./define/switch.js";
|
|
19
19
|
import { SonicWaveform } from "./elements/waveform.js";
|
|
20
20
|
import "./define/waveform.js";
|
|
21
21
|
import { SonicWavestrip } from "./elements/wavestrip.js";
|
package/dist/dev/define.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import "./define/button.js";
|
|
1
2
|
import "./define/dial.js";
|
|
2
3
|
import "./define/envelope.js";
|
|
3
|
-
import "./define/button.js";
|
|
4
|
-
import "./define/switch.js";
|
|
5
4
|
import "./define/meter.js";
|
|
6
5
|
import "./define/number.js";
|
|
7
6
|
import "./define/segmented.js";
|
|
8
7
|
import "./define/slider.js";
|
|
9
8
|
import "./define/split.js";
|
|
9
|
+
import "./define/switch.js";
|
|
10
10
|
import "./define/waveform.js";
|
|
11
11
|
import "./define/wavestrip.js";
|
|
12
12
|
import "./define/xy.js";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { placeChildren, requireChild, template } from "../lib/render.js";
|
|
2
1
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
2
|
import { SonicFormElement } from "./form-element.js";
|
|
4
3
|
import { copyNode } from "../lib/copy-node.js";
|
|
5
4
|
import { bindHold } from "../lib/hold.js";
|
|
5
|
+
import { placeChildren, requireChild, template } from "../lib/render.js";
|
|
6
|
+
import { mirrorChildren } from "../lib/mirror-children.js";
|
|
6
7
|
|
|
7
8
|
//#region src/elements/button.ts
|
|
8
9
|
const renderButton = template(`
|
|
@@ -88,7 +89,7 @@ var SonicButton = class extends SonicFormElement {
|
|
|
88
89
|
const button = this.#button;
|
|
89
90
|
const cap = requireChild(button, ".sonic-button-cap", HTMLSpanElement);
|
|
90
91
|
this.upgradeProperties("armed", "busy", "defaultPressed", "momentary", "softDisabled", "latching", "pressed", "value");
|
|
91
|
-
|
|
92
|
+
mirrorChildren(this, {
|
|
92
93
|
control: button,
|
|
93
94
|
copy: copyNode,
|
|
94
95
|
place: (copies) => {
|
|
@@ -113,12 +114,12 @@ var SonicButton = class extends SonicFormElement {
|
|
|
113
114
|
return this.#button;
|
|
114
115
|
}
|
|
115
116
|
render() {
|
|
117
|
+
if (!this.isBound()) return;
|
|
116
118
|
const button = this.#button;
|
|
117
|
-
button
|
|
119
|
+
writeAttribute(button, "disabled", this.isDisabled() ? "" : void 0);
|
|
118
120
|
writeAttribute(button, "aria-disabled", this.#isSoftDisabled() ? "true" : void 0);
|
|
119
121
|
writeAttribute(button, "aria-busy", this.busy ? "true" : void 0);
|
|
120
|
-
button
|
|
121
|
-
if (this.latching) button.setAttribute("aria-pressed", String(this.pressed));
|
|
122
|
+
writeAttribute(button, "aria-pressed", this.latching ? String(this.pressed) : void 0);
|
|
122
123
|
this.toggleState("pressed", this.pressed);
|
|
123
124
|
this.forwardNaming(button, true);
|
|
124
125
|
const submitted = this.latching && this.pressed ? this.value : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../../src/elements/button.ts"],"sourcesContent":["import type { Hold } from '#lib/hold.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { bindHold } from '#lib/hold.ts';\nimport { placeChildren, requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-button': SonicButton;\n\t}\n}\n\nconst renderButton = template(\n\t/* HTML */ `\n\t\t<button class=\"sonic-button\" type=\"button\"><span class=\"sonic-button-cap\"></span></button>\n\t`,\n\tHTMLButtonElement,\n);\n\nconst holdKeys = new Set([' ', 'Enter']);\n\nexport class SonicButton extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'busy',\n\t\t'momentary',\n\t\t'pressed',\n\t\t'soft-disabled',\n\t\t'latching',\n\t\t'value',\n\t];\n\n\tget armed(): boolean {\n\t\treturn this.hasAttribute('armed');\n\t}\n\n\tset armed(isArmed: boolean) {\n\t\tthis.reflect('armed', isArmed);\n\t}\n\n\tget busy(): boolean {\n\t\treturn this.hasAttribute('busy');\n\t}\n\n\tset busy(isBusy: boolean) {\n\t\tthis.reflect('busy', isBusy);\n\t}\n\n\tget defaultPressed(): boolean {\n\t\treturn this.hasAttribute('pressed');\n\t}\n\n\tset defaultPressed(isPressed: boolean) {\n\t\tthis.reflect('pressed', isPressed);\n\t}\n\n\tget latching(): boolean {\n\t\treturn this.hasAttribute('latching');\n\t}\n\n\tset latching(isLatching: boolean) {\n\t\tthis.reflect('latching', isLatching);\n\t}\n\n\tget momentary(): boolean {\n\t\treturn this.hasAttribute('momentary');\n\t}\n\n\tset momentary(isMomentary: boolean) {\n\t\tthis.reflect('momentary', isMomentary);\n\t}\n\n\tget pressed(): boolean {\n\t\treturn this.#isDirty ? this.#pressed : this.defaultPressed;\n\t}\n\n\tset pressed(isPressed: boolean | undefined) {\n\t\tthis.#isDirty = true;\n\t\tthis.#pressed = isPressed === true;\n\t\tthis.render();\n\t}\n\n\tget softDisabled(): boolean {\n\t\treturn this.hasAttribute('soft-disabled');\n\t}\n\n\tset softDisabled(isSoftDisabled: boolean) {\n\t\tthis.reflect('soft-disabled', isSoftDisabled);\n\t}\n\n\tget value(): string {\n\t\treturn this.getAttribute('value') ?? 'on';\n\t}\n\n\tset value(value: string | undefined) {\n\t\tthis.reflect('value', value);\n\t}\n\n\treadonly #button = renderButton();\n\n\t#holding: Hold | undefined;\n\n\t#isDirty = false;\n\n\t#pressed = false;\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'pressed') this.#isDirty = false;\n\t\tif (this.isDisabled() || this.#isSoftDisabled()) this.#holding?.release();\n\t\tthis.render();\n\t}\n\n\toverride formResetCallback(): void {\n\t\tthis.attributeChangedCallback('pressed');\n\t}\n\n\tprotected override activate(): void {\n\t\tthis.#button.click();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst button = this.#button;\n\t\tconst cap = requireChild(button, '.sonic-button-cap', HTMLSpanElement);\n\n\t\tthis.upgradeProperties(\n\t\t\t'armed',\n\t\t\t'busy',\n\t\t\t'defaultPressed',\n\t\t\t'momentary',\n\t\t\t'softDisabled',\n\t\t\t'latching',\n\t\t\t'pressed',\n\t\t\t'value',\n\t\t);\n\t\
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../../src/elements/button.ts"],"sourcesContent":["import type { Hold } from '#lib/hold.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { bindHold } from '#lib/hold.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { placeChildren, requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-button': SonicButton;\n\t}\n}\n\nconst renderButton = template(\n\t/* HTML */ `\n\t\t<button class=\"sonic-button\" type=\"button\"><span class=\"sonic-button-cap\"></span></button>\n\t`,\n\tHTMLButtonElement,\n);\n\nconst holdKeys = new Set([' ', 'Enter']);\n\nexport class SonicButton extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'busy',\n\t\t'momentary',\n\t\t'pressed',\n\t\t'soft-disabled',\n\t\t'latching',\n\t\t'value',\n\t];\n\n\tget armed(): boolean {\n\t\treturn this.hasAttribute('armed');\n\t}\n\n\tset armed(isArmed: boolean) {\n\t\tthis.reflect('armed', isArmed);\n\t}\n\n\tget busy(): boolean {\n\t\treturn this.hasAttribute('busy');\n\t}\n\n\tset busy(isBusy: boolean) {\n\t\tthis.reflect('busy', isBusy);\n\t}\n\n\tget defaultPressed(): boolean {\n\t\treturn this.hasAttribute('pressed');\n\t}\n\n\tset defaultPressed(isPressed: boolean) {\n\t\tthis.reflect('pressed', isPressed);\n\t}\n\n\tget latching(): boolean {\n\t\treturn this.hasAttribute('latching');\n\t}\n\n\tset latching(isLatching: boolean) {\n\t\tthis.reflect('latching', isLatching);\n\t}\n\n\tget momentary(): boolean {\n\t\treturn this.hasAttribute('momentary');\n\t}\n\n\tset momentary(isMomentary: boolean) {\n\t\tthis.reflect('momentary', isMomentary);\n\t}\n\n\tget pressed(): boolean {\n\t\treturn this.#isDirty ? this.#pressed : this.defaultPressed;\n\t}\n\n\tset pressed(isPressed: boolean | undefined) {\n\t\tthis.#isDirty = true;\n\t\tthis.#pressed = isPressed === true;\n\t\tthis.render();\n\t}\n\n\tget softDisabled(): boolean {\n\t\treturn this.hasAttribute('soft-disabled');\n\t}\n\n\tset softDisabled(isSoftDisabled: boolean) {\n\t\tthis.reflect('soft-disabled', isSoftDisabled);\n\t}\n\n\tget value(): string {\n\t\treturn this.getAttribute('value') ?? 'on';\n\t}\n\n\tset value(value: string | undefined) {\n\t\tthis.reflect('value', value);\n\t}\n\n\treadonly #button = renderButton();\n\n\t#holding: Hold | undefined;\n\n\t#isDirty = false;\n\n\t#pressed = false;\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'pressed') this.#isDirty = false;\n\t\tif (this.isDisabled() || this.#isSoftDisabled()) this.#holding?.release();\n\t\tthis.render();\n\t}\n\n\toverride formResetCallback(): void {\n\t\tthis.attributeChangedCallback('pressed');\n\t}\n\n\tprotected override activate(): void {\n\t\tthis.#button.click();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst button = this.#button;\n\t\tconst cap = requireChild(button, '.sonic-button-cap', HTMLSpanElement);\n\n\t\tthis.upgradeProperties(\n\t\t\t'armed',\n\t\t\t'busy',\n\t\t\t'defaultPressed',\n\t\t\t'momentary',\n\t\t\t'softDisabled',\n\t\t\t'latching',\n\t\t\t'pressed',\n\t\t\t'value',\n\t\t);\n\t\tmirrorChildren(\n\t\t\tthis,\n\t\t\t{\n\t\t\t\tcontrol: button,\n\t\t\t\tcopy: copyNode,\n\t\t\t\tplace: (copies) => {\n\t\t\t\t\tplaceChildren(cap, copies);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(button, 'button.css');\n\n\t\tbutton.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\t// A native disabled button fires no click at all\n\t\t\t\tif (this.#isSoftDisabled()) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!this.latching) return;\n\n\t\t\t\tthis.pressed = !this.pressed;\n\t\t\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindMomentary(button, signal);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#button;\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst button = this.#button;\n\n\t\twriteAttribute(button, 'disabled', this.isDisabled() ? '' : undefined);\n\t\twriteAttribute(button, 'aria-disabled', this.#isSoftDisabled() ? 'true' : undefined);\n\t\twriteAttribute(button, 'aria-busy', this.busy ? 'true' : undefined);\n\t\twriteAttribute(button, 'aria-pressed', this.latching ? String(this.pressed) : undefined);\n\t\tthis.toggleState('pressed', this.pressed);\n\t\tthis.forwardNaming(button, true);\n\n\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\tconst submitted = this.latching && this.pressed ? this.value : null;\n\n\t\tthis.writeFormValue(submitted, String(this.pressed));\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tif (this.latching) this.pressed = state === 'true';\n\t}\n\n\t#bindMomentary(button: HTMLButtonElement, signal: AbortSignal): void {\n\t\tconst holding = bindHold(\n\t\t\tbutton,\n\t\t\t{\n\t\t\t\tcanHold: () => !this.isDisabled() && !this.#isSoftDisabled(),\n\t\t\t\tleave: 'blur',\n\t\t\t\tonHold: () => {\n\t\t\t\t\tthis.#press(true);\n\t\t\t\t},\n\t\t\t\tonRelease: () => {\n\t\t\t\t\tthis.#press(false);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\n\t\tthis.#holding = holding;\n\t\tbutton.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.button !== 0 || !this.#isMomentary()) return;\n\n\t\t\t\tbutton.setPointerCapture(event.pointerId);\n\t\t\t\tholding.hold(event.pointerId);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tbutton.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.repeat || !holdKeys.has(event.key) || !this.#isMomentary()) return;\n\n\t\t\t\tholding.hold(event.key);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#isMomentary(): boolean {\n\t\treturn this.momentary && !this.latching;\n\t}\n\n\t#isSoftDisabled(): boolean {\n\t\treturn this.softDisabled && !this.isDisabled();\n\t}\n\n\t#press(isPressed: boolean): void {\n\t\tthis.pressed = isPressed;\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ export declare class SonicDial extends SonicValueElement {
|
|
|
13
13
|
get modulation(): number;
|
|
14
14
|
set modulation(amount: number | undefined);
|
|
15
15
|
get modulationValue(): number | undefined;
|
|
16
|
-
set modulationValue(value: number | undefined);
|
|
16
|
+
set modulationValue(value: null | number | undefined);
|
|
17
17
|
connectedCallback(): void;
|
|
18
18
|
protected connect(signal: AbortSignal): void;
|
|
19
19
|
protected draw(): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { requireChild, template } from "../lib/render.js";
|
|
2
2
|
import { SonicValueElement } from "./value-element.js";
|
|
3
|
+
import { bindScale } from "./scale.js";
|
|
3
4
|
|
|
4
5
|
//#region src/elements/dial.ts
|
|
5
6
|
const renderDial = template(`
|
|
@@ -54,7 +55,10 @@ var SonicDial = class extends SonicValueElement {
|
|
|
54
55
|
}
|
|
55
56
|
connect(signal) {
|
|
56
57
|
const dial = this.#dial;
|
|
57
|
-
|
|
58
|
+
bindScale(this, {
|
|
59
|
+
control: dial,
|
|
60
|
+
marks: requireChild(dial, ".sonic-dial-scale", HTMLDivElement)
|
|
61
|
+
}, signal);
|
|
58
62
|
this.render();
|
|
59
63
|
this.checkStyles(dial, "dial.css");
|
|
60
64
|
this.bindGestures(dial, signal, () => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: number | undefined) {\n\t\tthis.writeModulationValue(this.#dial, 'dial', value);\n\t}\n\n\treadonly #dial = renderDial();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tthis
|
|
1
|
+
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#dial, 'dial', value);\n\t}\n\n\treadonly #dial = renderDial();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(dial, 'dial.css');\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.writeProportions(this.#dial, 'dial', this.endless ? 0 : this.modulation);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n}\n"],"mappings":";;;;;AAUA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;CAGA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;CAGA,IAAI,kBAAsC;EACzC,OAAO,KAAK,oBAAoB;CACjC;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,qBAAqB,KAAK,OAAO,QAAQ,KAAK;CACpD;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EACZ,KAAK,YAAY,MAAM,UAAU;EACjC,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,iBAAiB,KAAK,OAAO,QAAQ,KAAK,UAAU,IAAI,KAAK,UAAU;CAC7E;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;AACD"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { requireChild, template } from "../lib/render.js";
|
|
2
1
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
2
|
import { SonicElement } from "./sonic-element.js";
|
|
3
|
+
import { requireChild, template } from "../lib/render.js";
|
|
4
4
|
import { ReadoutClaim } from "./readout-claim.js";
|
|
5
5
|
import { Readout } from "./readout.js";
|
|
6
6
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
7
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
7
8
|
import { SonicValueElement, linkValue } from "./value-element.js";
|
|
8
9
|
import { adsrPath, adsrShape, adsrStages, timeStages } from "../lib/adsr-shape.js";
|
|
9
10
|
import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
|
|
10
|
-
import { bindDrag } from "../lib/pointer-drag.js";
|
|
11
11
|
|
|
12
12
|
//#region src/elements/envelope.ts
|
|
13
13
|
const curveStages = [
|
|
@@ -31,10 +31,23 @@ function flip(point, isFlipped) {
|
|
|
31
31
|
function isParent(node) {
|
|
32
32
|
return "querySelector" in node;
|
|
33
33
|
}
|
|
34
|
+
const emptyPath = {
|
|
35
|
+
fill: "",
|
|
36
|
+
floor: {
|
|
37
|
+
from: 0,
|
|
38
|
+
to: 0
|
|
39
|
+
},
|
|
40
|
+
stroke: ""
|
|
41
|
+
};
|
|
42
|
+
function stageParts(envelope, hook, stages) {
|
|
43
|
+
return new Map(stages.map((stage) => [stage, requireChild(envelope, `.${hook}[data-sonic-stage="${stage}"]`, HTMLDivElement)]));
|
|
44
|
+
}
|
|
34
45
|
const renderEnvelope = template(`
|
|
35
46
|
<div class="sonic-envelope" aria-hidden="true">
|
|
47
|
+
<div class="sonic-envelope-bracket"></div>
|
|
36
48
|
<svg class="sonic-envelope-graph" preserveAspectRatio="none" viewBox="0 0 1 1">
|
|
37
49
|
<path class="sonic-envelope-fill" />
|
|
50
|
+
<rect class="sonic-envelope-fill sonic-envelope-floor" y="1" />
|
|
38
51
|
<path class="sonic-envelope-line" />
|
|
39
52
|
</svg>
|
|
40
53
|
<div class="sonic-envelope-handle" data-sonic-stage="delay" hidden></div>
|
|
@@ -115,12 +128,18 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
115
128
|
this.reflect("sustain", id);
|
|
116
129
|
}
|
|
117
130
|
#bindings = /* @__PURE__ */ new Map();
|
|
131
|
+
#envelope = renderEnvelope();
|
|
132
|
+
#bracket = requireChild(this.#envelope, ".sonic-envelope-bracket", HTMLDivElement);
|
|
118
133
|
#claim = new ReadoutClaim(() => {
|
|
119
134
|
this.#renderReadout();
|
|
120
135
|
});
|
|
121
|
-
#
|
|
136
|
+
#curveHandles = stageParts(this.#envelope, "sonic-envelope-curve", curveStages);
|
|
122
137
|
#fill = requireChild(this.#envelope, ".sonic-envelope-fill", SVGElement);
|
|
138
|
+
#floor = requireChild(this.#envelope, ".sonic-envelope-floor", SVGElement);
|
|
123
139
|
#graph = requireChild(this.#envelope, ".sonic-envelope-graph", SVGElement);
|
|
140
|
+
#handles = stageParts(this.#envelope, "sonic-envelope-handle", timeStages);
|
|
141
|
+
#held;
|
|
142
|
+
#isDriving = false;
|
|
124
143
|
#line = requireChild(this.#envelope, ".sonic-envelope-line", SVGElement);
|
|
125
144
|
#pointerDrag;
|
|
126
145
|
#readout = new Readout(requireChild(this.#envelope, ".sonic-envelope-readout", HTMLDivElement));
|
|
@@ -145,7 +164,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
145
164
|
connect(signal) {
|
|
146
165
|
const envelope = this.#envelope;
|
|
147
166
|
this.#signal = signal;
|
|
148
|
-
this.
|
|
167
|
+
this.keepControl(envelope, signal);
|
|
149
168
|
this.#bind();
|
|
150
169
|
this.checkStyles(envelope, "envelope.css");
|
|
151
170
|
this.#bindPointer(envelope, signal);
|
|
@@ -162,7 +181,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
162
181
|
if (!element) continue;
|
|
163
182
|
const link = linkValue(element);
|
|
164
183
|
const unwatch = link.watch(() => {
|
|
165
|
-
this.#draw();
|
|
184
|
+
if (!this.#isDriving) this.#draw();
|
|
166
185
|
});
|
|
167
186
|
this.#bindings.set(name, {
|
|
168
187
|
element,
|
|
@@ -202,32 +221,37 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
202
221
|
}
|
|
203
222
|
#draw() {
|
|
204
223
|
const shape = this.#readShape();
|
|
205
|
-
const path = this.#bindings.size === 0 ?
|
|
206
|
-
fill: "",
|
|
207
|
-
stroke: ""
|
|
208
|
-
} : adsrPath(shape);
|
|
224
|
+
const path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);
|
|
209
225
|
this.#shape = shape;
|
|
210
|
-
this.#line
|
|
211
|
-
this.#fill
|
|
212
|
-
|
|
226
|
+
writeAttribute(this.#line, "d", path.stroke);
|
|
227
|
+
writeAttribute(this.#fill, "d", path.fill);
|
|
228
|
+
writeAttribute(this.#floor, "x", String(path.floor.from));
|
|
229
|
+
writeAttribute(this.#floor, "width", String(path.floor.to - path.floor.from));
|
|
230
|
+
for (const [stage, part] of this.#handles) this.#drawPoint(part, {
|
|
213
231
|
at: shape.handles.find((point) => point.stage === stage),
|
|
214
232
|
drives: this.#drives(stage)
|
|
215
233
|
});
|
|
216
|
-
for (const stage of
|
|
234
|
+
for (const [stage, part] of this.#curveHandles) this.#drawPoint(part, {
|
|
217
235
|
at: shape.curveHandles.find((point) => point.stage === stage),
|
|
218
236
|
drives: this.#curveDrives(stage)
|
|
219
237
|
});
|
|
238
|
+
this.#drawBracket();
|
|
220
239
|
this.#renderReadout();
|
|
221
240
|
}
|
|
222
|
-
#
|
|
223
|
-
const part =
|
|
241
|
+
#drawBracket() {
|
|
242
|
+
const part = this.#held;
|
|
243
|
+
if (!part) return;
|
|
244
|
+
for (const name of ["--_sonic-envelope-x", "--_sonic-envelope-y"]) this.#bracket.style.setProperty(name, part.style.getPropertyValue(name));
|
|
245
|
+
writeAttribute(this.#bracket, "data-sonic-part", part.classList.contains("sonic-envelope-curve") ? "curve" : void 0);
|
|
246
|
+
}
|
|
247
|
+
#drawPoint(part, point) {
|
|
224
248
|
const { at, drives } = point;
|
|
225
|
-
part
|
|
249
|
+
writeAttribute(part, "hidden", at ? void 0 : "");
|
|
226
250
|
if (!at) return;
|
|
227
251
|
const isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());
|
|
228
252
|
part.style.setProperty("--_sonic-envelope-x", String(at.x));
|
|
229
253
|
part.style.setProperty("--_sonic-envelope-y", String(at.y));
|
|
230
|
-
part
|
|
254
|
+
writeAttribute(part, "data-sonic-axes", drives.map(({ axis }) => axis).join(""));
|
|
231
255
|
writeAttribute(part, "data-sonic-disabled", isStill ? "" : void 0);
|
|
232
256
|
}
|
|
233
257
|
#drives(stage) {
|
|
@@ -246,6 +270,8 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
246
270
|
if (!pressed || pressed.drives.length === 0) return void 0;
|
|
247
271
|
const { drives, isFlipped, part } = pressed;
|
|
248
272
|
this.#claim.press(true);
|
|
273
|
+
this.#held = part;
|
|
274
|
+
this.#drawBracket();
|
|
249
275
|
const box = this.#graph.getBoundingClientRect();
|
|
250
276
|
const travel = {
|
|
251
277
|
x: Math.max(1, box.width * this.#shape.share),
|
|
@@ -284,11 +310,13 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
284
310
|
const step = stepFieldDrag(mappings, drag.state, flip(pointerMove(event), drag.isFlipped));
|
|
285
311
|
drag.state = step.state;
|
|
286
312
|
if (step.state.isEngaged) this.#claim.reveal("drag");
|
|
313
|
+
this.#isDriving = true;
|
|
287
314
|
for (const { axis, binding } of drag.drives) {
|
|
288
315
|
const next = step[axis];
|
|
289
316
|
if (next !== void 0) binding.link.input(next);
|
|
290
317
|
}
|
|
291
|
-
this.#
|
|
318
|
+
this.#isDriving = false;
|
|
319
|
+
this.#draw();
|
|
292
320
|
}
|
|
293
321
|
#pressed(event) {
|
|
294
322
|
const part = event.target instanceof Element ? event.target.closest(".sonic-envelope-handle, .sonic-envelope-curve") : void 0;
|