@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
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { clampProportion } from "../lib/math.js";
|
|
1
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
|
+
import { createModulation } from "./modulation.js";
|
|
2
4
|
import { SonicValueElement } from "./value-element.js";
|
|
3
5
|
import { bindScale } from "./scale.js";
|
|
4
6
|
import { sortedRegions } from "../lib/time-regions.js";
|
|
@@ -34,6 +36,7 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
34
36
|
...SonicValueElement.observedAttributes,
|
|
35
37
|
"modulation",
|
|
36
38
|
"orientation",
|
|
39
|
+
"scrub",
|
|
37
40
|
"spring"
|
|
38
41
|
];
|
|
39
42
|
get buffered() {
|
|
@@ -56,10 +59,10 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
56
59
|
this.reflect("modulation", amount);
|
|
57
60
|
}
|
|
58
61
|
get modulationValue() {
|
|
59
|
-
return this.
|
|
62
|
+
return this.#modulation.value();
|
|
60
63
|
}
|
|
61
64
|
set modulationValue(value) {
|
|
62
|
-
this.
|
|
65
|
+
this.#modulation.write(value, this.isBound() ? this.mapping() : void 0);
|
|
63
66
|
}
|
|
64
67
|
get orientation() {
|
|
65
68
|
return this.getAttribute("orientation") === "vertical" ? "vertical" : "horizontal";
|
|
@@ -67,6 +70,12 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
67
70
|
set orientation(direction) {
|
|
68
71
|
this.reflect("orientation", direction);
|
|
69
72
|
}
|
|
73
|
+
get scrub() {
|
|
74
|
+
return this.hasAttribute("scrub");
|
|
75
|
+
}
|
|
76
|
+
set scrub(isScrubbing) {
|
|
77
|
+
this.reflect("scrub", isScrubbing);
|
|
78
|
+
}
|
|
70
79
|
get spring() {
|
|
71
80
|
return this.hasAttribute("spring");
|
|
72
81
|
}
|
|
@@ -75,8 +84,11 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
75
84
|
}
|
|
76
85
|
#buffered = [];
|
|
77
86
|
#slider = renderSlider();
|
|
87
|
+
#modulation = createModulation(this.#slider, "slider", (isModulated) => {
|
|
88
|
+
this.toggleState("modulated", isModulated);
|
|
89
|
+
});
|
|
78
90
|
connectedCallback() {
|
|
79
|
-
this.upgradeProperties("buffered", "groovePress", "modulationValue", "modulation", "orientation", "spring");
|
|
91
|
+
this.upgradeProperties("buffered", "groovePress", "modulationValue", "modulation", "orientation", "scrub", "spring");
|
|
80
92
|
super.connectedCallback();
|
|
81
93
|
}
|
|
82
94
|
connect(signal) {
|
|
@@ -89,6 +101,18 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
89
101
|
slider.addEventListener("transitionend", (event) => {
|
|
90
102
|
if (event.propertyName === "--_sonic-slider-value") this.toggleState("springing", false);
|
|
91
103
|
}, { signal });
|
|
104
|
+
this.bindHover(slider, signal, {
|
|
105
|
+
canShow: () => this.scrub && this.readout && this.orientation === "horizontal",
|
|
106
|
+
place: () => {
|
|
107
|
+
this.#placeReadout(slider);
|
|
108
|
+
},
|
|
109
|
+
valueAt: (event) => {
|
|
110
|
+
const axis = this.#axis(slider, cap);
|
|
111
|
+
const at = clampProportion((axis.position(event) - axis.startPx) / axis.travelPx);
|
|
112
|
+
const mapping = this.mapping();
|
|
113
|
+
return mapping.snap(mapping.valueAt(at));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
92
116
|
this.render();
|
|
93
117
|
this.checkStyles(slider, "slider.css");
|
|
94
118
|
this.bindGestures(slider, signal, (event) => {
|
|
@@ -103,12 +127,29 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
103
127
|
return this.orientation;
|
|
104
128
|
}
|
|
105
129
|
draw() {
|
|
106
|
-
this.
|
|
130
|
+
this.#modulation.draw({
|
|
131
|
+
mapping: this.mapping(),
|
|
132
|
+
modulation: this.modulation,
|
|
133
|
+
origin: this.originValue(),
|
|
134
|
+
value: this.value
|
|
135
|
+
});
|
|
107
136
|
this.#renderBuffered(this.#slider);
|
|
137
|
+
this.#renderScrub(this.#slider);
|
|
138
|
+
this.#placeReadout(this.#slider);
|
|
108
139
|
}
|
|
109
140
|
focusTarget() {
|
|
110
141
|
return this.#slider;
|
|
111
142
|
}
|
|
143
|
+
heldWrite(next) {
|
|
144
|
+
super.heldWrite(next);
|
|
145
|
+
if (this.scrub) this.render();
|
|
146
|
+
}
|
|
147
|
+
holdChanged() {
|
|
148
|
+
if (this.scrub) this.render();
|
|
149
|
+
}
|
|
150
|
+
scrubsKeyRepeat() {
|
|
151
|
+
return this.scrub;
|
|
152
|
+
}
|
|
112
153
|
springTarget() {
|
|
113
154
|
return this.spring ? this.originValue() : void 0;
|
|
114
155
|
}
|
|
@@ -125,6 +166,13 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
125
166
|
travelPx: Math.max(1, box.width - capBox.width)
|
|
126
167
|
};
|
|
127
168
|
}
|
|
169
|
+
#placeReadout(slider) {
|
|
170
|
+
if (!this.scrub) {
|
|
171
|
+
slider.style.removeProperty("--_sonic-slider-readout-value");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
slider.style.setProperty("--_sonic-slider-readout-value", String(this.mapping().proportionOf(this.readoutValue())));
|
|
175
|
+
}
|
|
128
176
|
#renderBuffered(slider) {
|
|
129
177
|
const buffered = this.#buffered;
|
|
130
178
|
if (buffered.length === 0) {
|
|
@@ -143,6 +191,22 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
143
191
|
});
|
|
144
192
|
slider.style.setProperty("--_sonic-slider-buffered-regions", `linear-gradient(var(--_sonic-slider-toward), ${stops.join(", ")})`);
|
|
145
193
|
}
|
|
194
|
+
#renderScrub(slider) {
|
|
195
|
+
const { style } = slider;
|
|
196
|
+
const { proportionOf } = this.mapping();
|
|
197
|
+
const isHeld = this.scrub && this.heldFrom() !== void 0;
|
|
198
|
+
const played = proportionOf(this.playback());
|
|
199
|
+
const scrubbed = proportionOf(this.value);
|
|
200
|
+
if (isHeld) style.setProperty("--_sonic-slider-played", String(played));
|
|
201
|
+
else style.removeProperty("--_sonic-slider-played");
|
|
202
|
+
if (played === scrubbed || !isHeld || !this.isRevealed()) {
|
|
203
|
+
style.removeProperty("--_sonic-slider-scrub-region");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const from = grooveStop(Math.min(played, scrubbed));
|
|
207
|
+
const to = grooveStop(Math.max(played, scrubbed));
|
|
208
|
+
style.setProperty("--_sonic-slider-scrub-region", `linear-gradient(var(--_sonic-slider-toward), transparent ${from}, var(--_sonic-slider-scrub) ${from} ${to}, transparent ${to})`);
|
|
209
|
+
}
|
|
146
210
|
};
|
|
147
211
|
|
|
148
212
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-element.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-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-slider-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 SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'spring',\n\t];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget buffered(): Array<[number, number]> {\n\t\treturn this.#buffered.map(([start, end]) => [start, end]);\n\t}\n\n\tset buffered(regions: TimeRegions | undefined) {\n\t\tthis.#buffered = sortedRegions(regions);\n\t\tthis.render();\n\t}\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\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\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#slider, 'slider', value);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #slider = renderSlider();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'buffered',\n\t\t\t'groovePress',\n\t\t\t'modulationValue',\n\t\t\t'modulation',\n\t\t\t'orientation',\n\t\t\t'spring',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: slider, marks: requireChild(slider, '.sonic-slider-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tthis.writeProportions(this.#slider, 'slider', this.modulation);\n\t\tthis.#renderBuffered(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.#buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { clampProportion } from '#lib/math.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-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-slider-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 SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'scrub',\n\t\t'spring',\n\t];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget buffered(): Array<[number, number]> {\n\t\treturn this.#buffered.map(([start, end]) => [start, end]);\n\t}\n\n\tset buffered(regions: TimeRegions | undefined) {\n\t\tthis.#buffered = sortedRegions(regions);\n\t\tthis.render();\n\t}\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\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\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget scrub(): boolean {\n\t\treturn this.hasAttribute('scrub');\n\t}\n\n\tset scrub(isScrubbing: boolean) {\n\t\tthis.reflect('scrub', isScrubbing);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #slider = renderSlider();\n\n\treadonly #modulation = createModulation(this.#slider, 'slider', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'buffered',\n\t\t\t'groovePress',\n\t\t\t'modulationValue',\n\t\t\t'modulation',\n\t\t\t'orientation',\n\t\t\t'scrub',\n\t\t\t'spring',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: slider, marks: requireChild(slider, '.sonic-slider-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.bindHover(slider, signal, {\n\t\t\tcanShow: () => this.scrub && this.readout && this.orientation === 'horizontal',\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout(slider);\n\t\t\t},\n\t\t\tvalueAt: (event) => {\n\t\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\t\tconst at = clampProportion((axis.position(event) - axis.startPx) / axis.travelPx);\n\t\t\t\tconst mapping = this.mapping();\n\n\t\t\t\treturn mapping.snap(mapping.valueAt(at));\n\t\t\t},\n\t\t});\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t\tthis.#renderBuffered(this.#slider);\n\t\tthis.#renderScrub(this.#slider);\n\t\tthis.#placeReadout(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override heldWrite(next: number): void {\n\t\tsuper.heldWrite(next);\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override holdChanged(): void {\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn this.scrub;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#placeReadout(slider: HTMLElement): void {\n\t\tif (!this.scrub) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-readout-value');\n\t\t\treturn;\n\t\t}\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-readout-value',\n\t\t\tString(this.mapping().proportionOf(this.readoutValue())),\n\t\t);\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.#buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n\n\t#renderScrub(slider: HTMLElement): void {\n\t\tconst { style } = slider;\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst isHeld = this.scrub && this.heldFrom() !== undefined;\n\t\tconst played = proportionOf(this.playback());\n\t\tconst scrubbed = proportionOf(this.value);\n\n\t\tif (isHeld) style.setProperty('--_sonic-slider-played', String(played));\n\t\telse style.removeProperty('--_sonic-slider-played');\n\n\t\tif (played === scrubbed || !isHeld || !this.isRevealed()) {\n\t\t\tstyle.removeProperty('--_sonic-slider-scrub-region');\n\t\t\treturn;\n\t\t}\n\n\t\tconst from = grooveStop(Math.min(played, scrubbed));\n\t\tconst to = grooveStop(Math.max(played, scrubbed));\n\n\t\tstyle.setProperty(\n\t\t\t'--_sonic-slider-scrub-region',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), transparent ${from}, var(--_sonic-slider-scrub) ${from} ${to}, transparent ${to})`,\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;AAoBA,SAAS,WAAW,YAA4B;CAC/C,IAAI,cAAc,GAAG,OAAO;CAC5B,IAAI,cAAc,GAAG,OAAO;CAE5B,OAAO,QAAQ,OAAO,UAAU,EAAE;AACnC;AAEA,MAAM,eAAe,SACT;;;;;;;;;;;;;;;;;;;IAoBX,cACD;AAEA,IAAa,cAAb,cAAiC,kBAAkB;CAClD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;EACA;EACA;CACD;CAGA,IAAI,WAAoC;EACvC,OAAO,KAAK,UAAU,KAAK,CAAC,OAAO,SAAS,CAAC,OAAO,GAAG,CAAC;CACzD;CAEA,IAAI,SAAS,SAAkC;EAC9C,KAAK,YAAY,cAAc,OAAO;EACtC,KAAK,OAAO;CACb;CAEA,IAAI,cAA+B;EAClC,OAAO,KAAK,aAAa,cAAc,MAAM,SAAS,SAAS;CAChE;CAEA,IAAI,YAAY,SAAsC;EACrD,KAAK,QAAQ,gBAAgB,OAAO;CACrC;CAEA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;CAEA,IAAI,kBAAsC;EACzC,OAAO,KAAK,YAAY,MAAM;CAC/B;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAS;CAC1E;CAEA,IAAI,cAAyC;EAC5C,OAAO,KAAK,aAAa,aAAa,MAAM,aAAa,aAAa;CACvE;CAEA,IAAI,YAAY,WAAkD;EACjE,KAAK,QAAQ,eAAe,SAAS;CACtC;CAEA,IAAI,QAAiB;EACpB,OAAO,KAAK,aAAa,OAAO;CACjC;CAEA,IAAI,MAAM,aAAsB;EAC/B,KAAK,QAAQ,SAAS,WAAW;CAClC;CAEA,IAAI,SAAkB;EACrB,OAAO,KAAK,aAAa,QAAQ;CAClC;CAEA,IAAI,OAAO,UAAmB;EAC7B,KAAK,QAAQ,UAAU,QAAQ;CAChC;CAEA,YAAqC,CAAC;CAEtC,AAAS,UAAU,aAAa;CAEhC,AAAS,cAAc,iBAAiB,KAAK,SAAS,WAAW,gBAAgB;EAChF,KAAK,YAAY,aAAa,WAAW;CAC1C,CAAC;CAED,AAAS,oBAA0B;EAClC,KAAK,kBACJ,YACA,eACA,mBACA,cACA,eACA,SACA,QACD;EACA,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,SAAS,KAAK;EACpB,MAAM,MAAM,aAAa,QAAQ,qBAAqB,cAAc;EAEpE,UACC,MACA;GAAE,SAAS;GAAQ,OAAO,aAAa,QAAQ,uBAAuB,cAAc;EAAE,GACtF,MACD;EACA,OAAO,iBACN,kBACC,UAAU;GACV,IAAI,MAAM,iBAAiB,yBAAyB,KAAK,YAAY,aAAa,KAAK;EACxF,GACA,EAAE,OAAO,CACV;EACA,KAAK,UAAU,QAAQ,QAAQ;GAC9B,eAAe,KAAK,SAAS,KAAK,WAAW,KAAK,gBAAgB;GAClE,aAAa;IACZ,KAAK,cAAc,MAAM;GAC1B;GACA,UAAU,UAAU;IACnB,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAG;IACnC,MAAM,KAAK,iBAAiB,KAAK,SAAS,KAAK,IAAI,KAAK,WAAW,KAAK,QAAQ;IAChF,MAAM,UAAU,KAAK,QAAQ;IAE7B,OAAO,QAAQ,KAAK,QAAQ,QAAQ,EAAE,CAAC;GACxC;EACD,CAAC;EACD,KAAK,OAAO;EACZ,KAAK,YAAY,QAAQ,YAAY;EACrC,KAAK,aAAa,QAAQ,SAAS,UAAU;GAC5C,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAG;GACnC,IAAI,MAAM,kBAAkB,QAAQ,IAAI,SAAS,MAAM,MAAM,GAAG,OAAO;GAEvE,IAAI,KAAK,gBAAgB,QAAQ;GAEjC,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC,SAAS,KAAK,SAAS,KAAK,IAAI,KAAK,WAAW,KAAK,QAAQ,CAAC;GAExF,OAAO;EACR,CAAC;CACF;CAEA,AAAmB,qBAAgD;EAClE,OAAO,KAAK;CACb;CAEA,AAAU,OAAa;EACtB,KAAK,YAAY,KAAK;GACrB,SAAS,KAAK,QAAQ;GACtB,YAAY,KAAK;GACjB,QAAQ,KAAK,YAAY;GACzB,OAAO,KAAK;EACb,CAAC;EACD,KAAK,gBAAgB,KAAK,OAAO;EACjC,KAAK,aAAa,KAAK,OAAO;EAC9B,KAAK,cAAc,KAAK,OAAO;CAChC;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,UAAU,MAAoB;EAChD,MAAM,UAAU,IAAI;EACpB,IAAI,KAAK,OAAO,KAAK,OAAO;CAC7B;CAEA,AAAmB,cAAoB;EACtC,IAAI,KAAK,OAAO,KAAK,OAAO;CAC7B;CAEA,AAAmB,kBAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,eAAmC;EACrD,OAAO,KAAK,SAAS,KAAK,YAAY,IAAI;CAC3C;CAEA,MAAM,QAAqB,KAA8B;EACxD,MAAM,MAAM,OAAO,sBAAsB;EACzC,MAAM,SAAS,IAAI,sBAAsB;EAEzC,OAAO,KAAK,gBAAgB,aACzB;GACA,WAAW,UAAU,CAAC,MAAM;GAC5B,SAAS,OAAO,SAAS,IAAI,IAAI;GACjC,UAAU,KAAK,IAAI,GAAG,IAAI,SAAS,OAAO,MAAM;EACjD,IACC;GACA,WAAW,UAAU,MAAM;GAC3B,SAAS,IAAI,OAAO,OAAO,QAAQ;GACnC,UAAU,KAAK,IAAI,GAAG,IAAI,QAAQ,OAAO,KAAK;EAC/C;CACH;CAEA,cAAc,QAA2B;EACxC,IAAI,CAAC,KAAK,OAAO;GAChB,OAAO,MAAM,eAAe,+BAA+B;GAC3D;EACD;EAEA,OAAO,MAAM,YACZ,iCACA,OAAO,KAAK,QAAQ,CAAC,CAAC,aAAa,KAAK,aAAa,CAAC,CAAC,CACxD;CACD;CAEA,gBAAgB,QAA2B;EAC1C,MAAM,WAAW,KAAK;EAEtB,IAAI,SAAS,WAAW,GAAG;GAC1B,OAAO,MAAM,eAAe,kCAAkC;GAC9D;EACD;EAEA,MAAM,EAAE,iBAAiB,KAAK,QAAQ;EACtC,MAAM,QAAQ,SAAS,SAAS,CAAC,OAAO,SAAS;GAChD,MAAM,OAAO,WAAW,aAAa,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC;GAC1D,MAAM,KAAK,WAAW,aAAa,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC;GAExD,OAAO;IACN,eAAe;IACf,iCAAiC,KAAK,GAAG;IACzC,eAAe;GAChB;EACD,CAAC;EAED,OAAO,MAAM,YACZ,oCACA,gDAAgD,MAAM,KAAK,IAAI,EAAE,EAClE;CACD;CAEA,aAAa,QAA2B;EACvC,MAAM,EAAE,UAAU;EAClB,MAAM,EAAE,iBAAiB,KAAK,QAAQ;EACtC,MAAM,SAAS,KAAK,SAAS,KAAK,SAAS,MAAM;EACjD,MAAM,SAAS,aAAa,KAAK,SAAS,CAAC;EAC3C,MAAM,WAAW,aAAa,KAAK,KAAK;EAExC,IAAI,QAAQ,MAAM,YAAY,0BAA0B,OAAO,MAAM,CAAC;OACjE,MAAM,eAAe,wBAAwB;EAElD,IAAI,WAAW,YAAY,CAAC,UAAU,CAAC,KAAK,WAAW,GAAG;GACzD,MAAM,eAAe,8BAA8B;GACnD;EACD;EAEA,MAAM,OAAO,WAAW,KAAK,IAAI,QAAQ,QAAQ,CAAC;EAClD,MAAM,KAAK,WAAW,KAAK,IAAI,QAAQ,QAAQ,CAAC;EAEhD,MAAM,YACL,gCACA,4DAA4D,KAAK,+BAA+B,KAAK,GAAG,GAAG,gBAAgB,GAAG,EAC/H;CACD;AACD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { SurfaceFrame } from "../lib/canvas-surface.js";
|
|
2
|
+
import { SonicDisplayElement } from "./display-element.js";
|
|
3
|
+
//#region src/elements/spectrum.d.ts
|
|
4
|
+
declare global {
|
|
5
|
+
interface HTMLElementTagNameMap {
|
|
6
|
+
'sonic-spectrum': SonicSpectrum;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
type Colour = keyof typeof colours;
|
|
10
|
+
type Numeric = keyof typeof numbers;
|
|
11
|
+
interface SpectrumPeak {
|
|
12
|
+
decibels: number;
|
|
13
|
+
frequency: number;
|
|
14
|
+
}
|
|
15
|
+
declare const colours: {
|
|
16
|
+
readonly clip: "--_sonic-spectrum-clip";
|
|
17
|
+
readonly grid: "--_sonic-spectrum-grid";
|
|
18
|
+
readonly hot: "--_sonic-spectrum-hot";
|
|
19
|
+
readonly lit: "--_sonic-spectrum-lit";
|
|
20
|
+
};
|
|
21
|
+
declare const numbers: {
|
|
22
|
+
readonly clipFrom: "--_sonic-spectrum-clip-from";
|
|
23
|
+
readonly hotFrom: "--_sonic-spectrum-hot-from";
|
|
24
|
+
};
|
|
25
|
+
export declare class SonicSpectrum extends SonicDisplayElement<Colour, Numeric> {
|
|
26
|
+
#private;
|
|
27
|
+
static readonly observedAttributes: string[];
|
|
28
|
+
get frequencyMax(): number;
|
|
29
|
+
set frequencyMax(hertz: number | undefined);
|
|
30
|
+
get frequencyMin(): number;
|
|
31
|
+
set frequencyMin(hertz: number | undefined);
|
|
32
|
+
get grid(): 'none' | 'on';
|
|
33
|
+
set grid(grid: 'none' | 'on' | undefined);
|
|
34
|
+
get peak(): SpectrumPeak | undefined;
|
|
35
|
+
get peakHold(): boolean;
|
|
36
|
+
set peakHold(isHeld: boolean);
|
|
37
|
+
get sampleRate(): number;
|
|
38
|
+
set sampleRate(hertz: number | undefined);
|
|
39
|
+
protected readonly control: HTMLDivElement;
|
|
40
|
+
protected readonly canvas: HTMLCanvasElement;
|
|
41
|
+
protected readonly colours: {
|
|
42
|
+
readonly clip: "--_sonic-spectrum-clip";
|
|
43
|
+
readonly grid: "--_sonic-spectrum-grid";
|
|
44
|
+
readonly hot: "--_sonic-spectrum-hot";
|
|
45
|
+
readonly lit: "--_sonic-spectrum-lit";
|
|
46
|
+
};
|
|
47
|
+
protected readonly numbers: {
|
|
48
|
+
readonly clipFrom: "--_sonic-spectrum-clip-from";
|
|
49
|
+
readonly hotFrom: "--_sonic-spectrum-hot-from";
|
|
50
|
+
};
|
|
51
|
+
protected readonly sheet = "spectrum.css";
|
|
52
|
+
connectedCallback(): void;
|
|
53
|
+
resetPeak(): void;
|
|
54
|
+
protected paint(context: CanvasRenderingContext2D, { frameMs, isDirty, look, size }: SurfaceFrame<Colour, never, Numeric>): void;
|
|
55
|
+
protected pull(analyser: AnalyserNode, buffer: Float32Array<ArrayBuffer>): void;
|
|
56
|
+
protected receive(frame: Float32Array): void;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//# sourceMappingURL=spectrum.d.ts.map
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { requireChild, template } from "../lib/render.js";
|
|
2
|
+
import { SonicDisplayElement } from "./display-element.js";
|
|
3
|
+
import { columnEdges, columnLevels, gridColumns, gridRows, levelRows, loudestBin, zoneRows } from "../lib/spectrum-columns.js";
|
|
4
|
+
import { areSettled, createLevels, receiveLevels, stepLevels } from "../lib/spectrum-levels.js";
|
|
5
|
+
|
|
6
|
+
//#region src/elements/spectrum.ts
|
|
7
|
+
const colours = {
|
|
8
|
+
clip: "--_sonic-spectrum-clip",
|
|
9
|
+
grid: "--_sonic-spectrum-grid",
|
|
10
|
+
hot: "--_sonic-spectrum-hot",
|
|
11
|
+
lit: "--_sonic-spectrum-lit"
|
|
12
|
+
};
|
|
13
|
+
const numbers = {
|
|
14
|
+
clipFrom: "--_sonic-spectrum-clip-from",
|
|
15
|
+
hotFrom: "--_sonic-spectrum-hot-from"
|
|
16
|
+
};
|
|
17
|
+
const defaultFrequencies = [20, 2e4];
|
|
18
|
+
const defaultSampleRate = 48e3;
|
|
19
|
+
const renderSpectrum = template(`
|
|
20
|
+
<div aria-hidden="true" class="sonic-spectrum">
|
|
21
|
+
<canvas class="sonic-spectrum-canvas"></canvas>
|
|
22
|
+
</div>
|
|
23
|
+
`, HTMLDivElement);
|
|
24
|
+
function paintGrid(context, { dpr, height, look, range, rowOf, width }, [frequencyMin, frequencyMax]) {
|
|
25
|
+
const line = Math.max(1, Math.round(dpr));
|
|
26
|
+
const columns = gridColumns({
|
|
27
|
+
frequencyMax,
|
|
28
|
+
frequencyMin
|
|
29
|
+
}, width);
|
|
30
|
+
const rows = gridRows(range, rowOf);
|
|
31
|
+
context.beginPath();
|
|
32
|
+
for (const column of columns) context.rect(column, 0, line, height);
|
|
33
|
+
for (const row of rows) context.rect(0, row, width, line);
|
|
34
|
+
context.fillStyle = look.colours.grid;
|
|
35
|
+
context.fill();
|
|
36
|
+
}
|
|
37
|
+
function zoneFill(context, { height, look, rowOf }) {
|
|
38
|
+
const gradient = context.createLinearGradient(0, 0, 0, height);
|
|
39
|
+
const { clip, hot } = zoneRows(look.numbers, rowOf);
|
|
40
|
+
gradient.addColorStop(clip / height, look.colours.clip);
|
|
41
|
+
gradient.addColorStop(clip / height, look.colours.hot);
|
|
42
|
+
gradient.addColorStop(hot / height, look.colours.hot);
|
|
43
|
+
gradient.addColorStop(hot / height, look.colours.lit);
|
|
44
|
+
return gradient;
|
|
45
|
+
}
|
|
46
|
+
function paintBars(context, scene, bars) {
|
|
47
|
+
const { height, rowOf } = scene;
|
|
48
|
+
let previous = height;
|
|
49
|
+
context.beginPath();
|
|
50
|
+
context.moveTo(0, height);
|
|
51
|
+
for (const [column, level] of bars.entries()) {
|
|
52
|
+
const row = rowOf(level);
|
|
53
|
+
bars[column] = row;
|
|
54
|
+
if (row === previous) continue;
|
|
55
|
+
context.lineTo(column, previous);
|
|
56
|
+
context.lineTo(column, row);
|
|
57
|
+
previous = row;
|
|
58
|
+
}
|
|
59
|
+
context.lineTo(bars.length, previous);
|
|
60
|
+
context.lineTo(bars.length, height);
|
|
61
|
+
context.closePath();
|
|
62
|
+
context.fillStyle = zoneFill(context, scene);
|
|
63
|
+
context.fill();
|
|
64
|
+
}
|
|
65
|
+
function paintHolds(context, { look, rowOf }, { bars, holds }) {
|
|
66
|
+
context.beginPath();
|
|
67
|
+
for (const [column, level] of holds.entries()) {
|
|
68
|
+
const row = rowOf(level);
|
|
69
|
+
if (row < (bars[column] ?? 0)) context.rect(column, row, 1, 1);
|
|
70
|
+
}
|
|
71
|
+
context.fillStyle = look.colours.lit;
|
|
72
|
+
context.fill();
|
|
73
|
+
}
|
|
74
|
+
var SonicSpectrum = class extends SonicDisplayElement {
|
|
75
|
+
static observedAttributes = [
|
|
76
|
+
...SonicDisplayElement.observedAttributes,
|
|
77
|
+
"frequency-max",
|
|
78
|
+
"frequency-min",
|
|
79
|
+
"grid",
|
|
80
|
+
"peak-hold",
|
|
81
|
+
"sample-rate"
|
|
82
|
+
];
|
|
83
|
+
get frequencyMax() {
|
|
84
|
+
return this.numberAttribute("frequency-max", defaultFrequencies[1]);
|
|
85
|
+
}
|
|
86
|
+
set frequencyMax(hertz) {
|
|
87
|
+
this.reflect("frequency-max", hertz);
|
|
88
|
+
}
|
|
89
|
+
get frequencyMin() {
|
|
90
|
+
return this.numberAttribute("frequency-min", defaultFrequencies[0]);
|
|
91
|
+
}
|
|
92
|
+
set frequencyMin(hertz) {
|
|
93
|
+
this.reflect("frequency-min", hertz);
|
|
94
|
+
}
|
|
95
|
+
get grid() {
|
|
96
|
+
return this.getAttribute("grid") === "none" ? "none" : "on";
|
|
97
|
+
}
|
|
98
|
+
set grid(grid) {
|
|
99
|
+
this.reflect("grid", grid);
|
|
100
|
+
}
|
|
101
|
+
get peak() {
|
|
102
|
+
return this.#peak && { ...this.#peak };
|
|
103
|
+
}
|
|
104
|
+
get peakHold() {
|
|
105
|
+
return this.hasAttribute("peak-hold");
|
|
106
|
+
}
|
|
107
|
+
set peakHold(isHeld) {
|
|
108
|
+
this.reflect("peak-hold", isHeld);
|
|
109
|
+
}
|
|
110
|
+
get sampleRate() {
|
|
111
|
+
const sampleRate = this.numberAttribute("sample-rate", defaultSampleRate);
|
|
112
|
+
return sampleRate > 0 ? sampleRate : defaultSampleRate;
|
|
113
|
+
}
|
|
114
|
+
set sampleRate(hertz) {
|
|
115
|
+
this.reflect("sample-rate", hertz);
|
|
116
|
+
}
|
|
117
|
+
control = renderSpectrum();
|
|
118
|
+
canvas = requireChild(this.control, ".sonic-spectrum-canvas", HTMLCanvasElement);
|
|
119
|
+
colours = colours;
|
|
120
|
+
numbers = numbers;
|
|
121
|
+
sheet = "spectrum.css";
|
|
122
|
+
#columns;
|
|
123
|
+
#isSettled = true;
|
|
124
|
+
#lastFrameMs;
|
|
125
|
+
#levels = createLevels(0);
|
|
126
|
+
#peak;
|
|
127
|
+
#receivedRate;
|
|
128
|
+
connectedCallback() {
|
|
129
|
+
this.upgradeProperties("frequencyMax", "frequencyMin", "grid", "peakHold", "sampleRate");
|
|
130
|
+
super.connectedCallback();
|
|
131
|
+
}
|
|
132
|
+
resetPeak() {
|
|
133
|
+
this.#peak = void 0;
|
|
134
|
+
}
|
|
135
|
+
paint(context, { frameMs, isDirty, look, size }) {
|
|
136
|
+
if (this.isDisabled()) {
|
|
137
|
+
if (isDirty) this.#draw(context, look, size);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const wasSettled = this.#isSettled;
|
|
141
|
+
this.#step(frameMs, look.isReducedMotion);
|
|
142
|
+
if (isDirty || !wasSettled || !this.#isSettled) this.#draw(context, look, size);
|
|
143
|
+
if (!this.#isSettled) this.surface()?.requestFrame();
|
|
144
|
+
}
|
|
145
|
+
pull(analyser, buffer) {
|
|
146
|
+
analyser.getFloatFrequencyData(buffer);
|
|
147
|
+
}
|
|
148
|
+
receive(frame) {
|
|
149
|
+
if (frame.length !== this.#levels.bars.length) this.#levels = createLevels(frame.length);
|
|
150
|
+
this.#receivedRate = this.analyser?.context.sampleRate ?? this.sampleRate;
|
|
151
|
+
receiveLevels(this.#levels, frame, this.min);
|
|
152
|
+
const loudest = loudestBin(frame, this.#axis());
|
|
153
|
+
if (loudest && loudest.decibels > (this.#peak?.decibels ?? -Infinity)) this.#peak = loudest;
|
|
154
|
+
}
|
|
155
|
+
#axis() {
|
|
156
|
+
const [frequencyMin, frequencyMax] = this.#frequencies();
|
|
157
|
+
return {
|
|
158
|
+
binCount: this.#levels.bars.length,
|
|
159
|
+
frequencyMax,
|
|
160
|
+
frequencyMin,
|
|
161
|
+
sampleRate: this.#receivedRate ?? this.sampleRate
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
#columnsFor(width) {
|
|
165
|
+
const axis = this.#axis();
|
|
166
|
+
const key = [
|
|
167
|
+
width,
|
|
168
|
+
axis.binCount,
|
|
169
|
+
axis.sampleRate,
|
|
170
|
+
axis.frequencyMin,
|
|
171
|
+
axis.frequencyMax
|
|
172
|
+
].join(" ");
|
|
173
|
+
if (this.#columns?.key !== key) this.#columns = {
|
|
174
|
+
bars: new Float64Array(width),
|
|
175
|
+
edges: columnEdges(axis, width),
|
|
176
|
+
holds: new Float64Array(width),
|
|
177
|
+
key
|
|
178
|
+
};
|
|
179
|
+
return this.#columns;
|
|
180
|
+
}
|
|
181
|
+
#draw(context, look, size) {
|
|
182
|
+
const scene = {
|
|
183
|
+
...size,
|
|
184
|
+
look,
|
|
185
|
+
range: {
|
|
186
|
+
max: this.max,
|
|
187
|
+
min: this.min
|
|
188
|
+
},
|
|
189
|
+
rowOf: levelRows({
|
|
190
|
+
max: this.max,
|
|
191
|
+
min: this.min
|
|
192
|
+
}, size.height)
|
|
193
|
+
};
|
|
194
|
+
context.clearRect(0, 0, size.width, size.height);
|
|
195
|
+
if (this.grid === "on") paintGrid(context, scene, this.#frequencies());
|
|
196
|
+
if (this.isDisabled()) return;
|
|
197
|
+
const { bars, edges, holds } = this.#columnsFor(size.width);
|
|
198
|
+
const spread = {
|
|
199
|
+
edges,
|
|
200
|
+
floor: scene.range.min
|
|
201
|
+
};
|
|
202
|
+
columnLevels(bars, this.#levels.bars, spread);
|
|
203
|
+
paintBars(context, scene, bars);
|
|
204
|
+
if (!this.peakHold) return;
|
|
205
|
+
columnLevels(holds, this.#levels.holds, spread);
|
|
206
|
+
paintHolds(context, scene, {
|
|
207
|
+
bars,
|
|
208
|
+
holds
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
#frequencies() {
|
|
212
|
+
const { frequencyMax, frequencyMin } = this;
|
|
213
|
+
return frequencyMin > 0 && frequencyMax > frequencyMin ? [frequencyMin, frequencyMax] : defaultFrequencies;
|
|
214
|
+
}
|
|
215
|
+
#step(frameMs, isReducedMotion) {
|
|
216
|
+
const floor = this.min;
|
|
217
|
+
stepLevels(this.#levels, {
|
|
218
|
+
elapsedMs: Math.max(0, frameMs - (this.#lastFrameMs ?? frameMs)),
|
|
219
|
+
floor,
|
|
220
|
+
isHeld: this.peakHold,
|
|
221
|
+
isHoldStepped: isReducedMotion,
|
|
222
|
+
nowMs: frameMs
|
|
223
|
+
});
|
|
224
|
+
this.#isSettled = areSettled(this.#levels, floor);
|
|
225
|
+
this.#lastFrameMs = this.#isSettled ? void 0 : frameMs;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
//#endregion
|
|
230
|
+
export { SonicSpectrum };
|
|
231
|
+
//# sourceMappingURL=spectrum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spectrum.js","names":[],"sources":["../../../src/elements/spectrum.ts"],"sourcesContent":["import type { SurfaceFrame, SurfaceLook, SurfaceSize } from '#lib/canvas-surface.ts';\nimport type { FrequencyAxis } from '#lib/spectrum-columns.ts';\n\nimport { SonicDisplayElement } from '#elements/display-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport {\n\tcolumnEdges,\n\tcolumnLevels,\n\tgridColumns,\n\tgridRows,\n\tlevelRows,\n\tloudestBin,\n\tzoneRows,\n} from '#lib/spectrum-columns.ts';\nimport { areSettled, createLevels, receiveLevels, stepLevels } from '#lib/spectrum-levels.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-spectrum': SonicSpectrum;\n\t}\n}\n\ntype Colour = keyof typeof colours;\n\ntype Numeric = keyof typeof numbers;\n\ntype Look = SurfaceLook<Colour, never, Numeric>;\n\ninterface SpectrumPeak {\n\tdecibels: number;\n\tfrequency: number;\n}\n\ninterface Scene extends SurfaceSize {\n\tlook: Look;\n\trange: { max: number; min: number };\n\trowOf: (level: number) => number;\n}\n\ninterface Columns {\n\tbars: Float64Array;\n\tedges: Float64Array;\n\tholds: Float64Array;\n\tkey: string;\n}\n\nconst colours = {\n\tclip: '--_sonic-spectrum-clip',\n\tgrid: '--_sonic-spectrum-grid',\n\thot: '--_sonic-spectrum-hot',\n\tlit: '--_sonic-spectrum-lit',\n} as const;\n\nconst numbers = {\n\tclipFrom: '--_sonic-spectrum-clip-from',\n\thotFrom: '--_sonic-spectrum-hot-from',\n} as const;\n\nconst defaultFrequencies: [number, number] = [20, 20_000];\nconst defaultSampleRate = 48_000;\n\nconst renderSpectrum = template(\n\t/* HTML */ `\n\t\t<div aria-hidden=\"true\" class=\"sonic-spectrum\">\n\t\t\t<canvas class=\"sonic-spectrum-canvas\"></canvas>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\n// One path, since a crossing filled twice would show brighter than its lines\nfunction paintGrid(\n\tcontext: CanvasRenderingContext2D,\n\t{ dpr, height, look, range, rowOf, width }: Scene,\n\t[frequencyMin, frequencyMax]: [number, number],\n): void {\n\tconst line = Math.max(1, Math.round(dpr));\n\tconst columns = gridColumns({ frequencyMax, frequencyMin }, width);\n\tconst rows = gridRows(range, rowOf);\n\n\tcontext.beginPath();\n\tfor (const column of columns) context.rect(column, 0, line, height);\n\tfor (const row of rows) context.rect(0, row, width, line);\n\tcontext.fillStyle = look.colours.grid;\n\tcontext.fill();\n}\n\nfunction zoneFill(\n\tcontext: CanvasRenderingContext2D,\n\t{ height, look, rowOf }: Scene,\n): CanvasGradient {\n\tconst gradient = context.createLinearGradient(0, 0, 0, height);\n\tconst { clip, hot } = zoneRows(look.numbers, rowOf);\n\n\tgradient.addColorStop(clip / height, look.colours.clip);\n\tgradient.addColorStop(clip / height, look.colours.hot);\n\tgradient.addColorStop(hot / height, look.colours.hot);\n\tgradient.addColorStop(hot / height, look.colours.lit);\n\n\treturn gradient;\n}\n\n// Whole device pixels, so nothing is antialiased\nfunction paintBars(context: CanvasRenderingContext2D, scene: Scene, bars: Float64Array): void {\n\tconst { height, rowOf } = scene;\n\tlet previous = height;\n\n\tcontext.beginPath();\n\tcontext.moveTo(0, height);\n\tfor (const [column, level] of bars.entries()) {\n\t\tconst row = rowOf(level);\n\n\t\tbars[column] = row;\n\t\tif (row === previous) continue;\n\n\t\tcontext.lineTo(column, previous);\n\t\tcontext.lineTo(column, row);\n\t\tprevious = row;\n\t}\n\tcontext.lineTo(bars.length, previous);\n\tcontext.lineTo(bars.length, height);\n\tcontext.closePath();\n\tcontext.fillStyle = zoneFill(context, scene);\n\tcontext.fill();\n}\n\nfunction paintHolds(\n\tcontext: CanvasRenderingContext2D,\n\t{ look, rowOf }: Scene,\n\t{ bars, holds }: { bars: Float64Array; holds: Float64Array },\n): void {\n\tcontext.beginPath();\n\tfor (const [column, level] of holds.entries()) {\n\t\tconst row = rowOf(level);\n\n\t\tif (row < (bars[column] ?? 0)) context.rect(column, row, 1, 1);\n\t}\n\tcontext.fillStyle = look.colours.lit;\n\tcontext.fill();\n}\n\nexport class SonicSpectrum extends SonicDisplayElement<Colour, Numeric> {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicDisplayElement.observedAttributes,\n\t\t'frequency-max',\n\t\t'frequency-min',\n\t\t'grid',\n\t\t'peak-hold',\n\t\t'sample-rate',\n\t];\n\n\tget frequencyMax(): number {\n\t\treturn this.numberAttribute('frequency-max', defaultFrequencies[1]);\n\t}\n\n\tset frequencyMax(hertz: number | undefined) {\n\t\tthis.reflect('frequency-max', hertz);\n\t}\n\n\tget frequencyMin(): number {\n\t\treturn this.numberAttribute('frequency-min', defaultFrequencies[0]);\n\t}\n\n\tset frequencyMin(hertz: number | undefined) {\n\t\tthis.reflect('frequency-min', hertz);\n\t}\n\n\tget grid(): 'none' | 'on' {\n\t\treturn this.getAttribute('grid') === 'none' ? 'none' : 'on';\n\t}\n\n\tset grid(grid: 'none' | 'on' | undefined) {\n\t\tthis.reflect('grid', grid);\n\t}\n\n\tget peak(): SpectrumPeak | undefined {\n\t\treturn this.#peak && { ...this.#peak };\n\t}\n\n\tget peakHold(): boolean {\n\t\treturn this.hasAttribute('peak-hold');\n\t}\n\n\tset peakHold(isHeld: boolean) {\n\t\tthis.reflect('peak-hold', isHeld);\n\t}\n\n\tget sampleRate(): number {\n\t\tconst sampleRate = this.numberAttribute('sample-rate', defaultSampleRate);\n\n\t\treturn sampleRate > 0 ? sampleRate : defaultSampleRate;\n\t}\n\n\tset sampleRate(hertz: number | undefined) {\n\t\tthis.reflect('sample-rate', hertz);\n\t}\n\n\tprotected readonly control = renderSpectrum();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-spectrum-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = colours;\n\n\tprotected readonly numbers = numbers;\n\n\tprotected readonly sheet = 'spectrum.css';\n\n\t#columns: Columns | undefined;\n\n\t#isSettled = true;\n\n\t#lastFrameMs: number | undefined;\n\n\t#levels = createLevels(0);\n\n\t#peak: SpectrumPeak | undefined;\n\n\t#receivedRate: number | undefined;\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('frequencyMax', 'frequencyMin', 'grid', 'peakHold', 'sampleRate');\n\t\tsuper.connectedCallback();\n\t}\n\n\tresetPeak(): void {\n\t\tthis.#peak = undefined;\n\t}\n\n\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, look, size }: SurfaceFrame<Colour, never, Numeric>,\n\t): void {\n\t\tif (this.isDisabled()) {\n\t\t\tif (isDirty) this.#draw(context, look, size);\n\t\t\treturn;\n\t\t}\n\n\t\tconst wasSettled = this.#isSettled;\n\n\t\tthis.#step(frameMs, look.isReducedMotion);\n\t\tif (isDirty || !wasSettled || !this.#isSettled) this.#draw(context, look, size);\n\t\tif (!this.#isSettled) this.surface()?.requestFrame();\n\t}\n\n\tprotected pull(analyser: AnalyserNode, buffer: Float32Array<ArrayBuffer>): void {\n\t\tanalyser.getFloatFrequencyData(buffer);\n\t}\n\n\tprotected receive(frame: Float32Array): void {\n\t\tif (frame.length !== this.#levels.bars.length) this.#levels = createLevels(frame.length);\n\t\tthis.#receivedRate = this.analyser?.context.sampleRate ?? this.sampleRate;\n\t\treceiveLevels(this.#levels, frame, this.min);\n\n\t\tconst loudest = loudestBin(frame, this.#axis());\n\n\t\tif (loudest && loudest.decibels > (this.#peak?.decibels ?? -Infinity)) this.#peak = loudest;\n\t}\n\n\t#axis(): FrequencyAxis {\n\t\tconst [frequencyMin, frequencyMax] = this.#frequencies();\n\n\t\treturn {\n\t\t\tbinCount: this.#levels.bars.length,\n\t\t\tfrequencyMax,\n\t\t\tfrequencyMin,\n\t\t\t// The bars on show keep the rate they arrived at once the analyser is unset\n\t\t\tsampleRate: this.#receivedRate ?? this.sampleRate,\n\t\t};\n\t}\n\n\t#columnsFor(width: number): Columns {\n\t\tconst axis = this.#axis();\n\t\tconst key = [width, axis.binCount, axis.sampleRate, axis.frequencyMin, axis.frequencyMax].join(\n\t\t\t' ',\n\t\t);\n\n\t\tif (this.#columns?.key !== key) {\n\t\t\tthis.#columns = {\n\t\t\t\tbars: new Float64Array(width),\n\t\t\t\tedges: columnEdges(axis, width),\n\t\t\t\tholds: new Float64Array(width),\n\t\t\t\tkey,\n\t\t\t};\n\t\t}\n\n\t\treturn this.#columns;\n\t}\n\n\t#draw(context: CanvasRenderingContext2D, look: Look, size: SurfaceSize): void {\n\t\tconst scene = {\n\t\t\t...size,\n\t\t\tlook,\n\t\t\trange: { max: this.max, min: this.min },\n\t\t\trowOf: levelRows({ max: this.max, min: this.min }, size.height),\n\t\t};\n\n\t\tcontext.clearRect(0, 0, size.width, size.height);\n\t\tif (this.grid === 'on') paintGrid(context, scene, this.#frequencies());\n\t\tif (this.isDisabled()) return;\n\n\t\tconst { bars, edges, holds } = this.#columnsFor(size.width);\n\t\tconst spread = { edges, floor: scene.range.min };\n\n\t\tcolumnLevels(bars, this.#levels.bars, spread);\n\t\tpaintBars(context, scene, bars);\n\t\tif (!this.peakHold) return;\n\n\t\tcolumnLevels(holds, this.#levels.holds, spread);\n\t\tpaintHolds(context, scene, { bars, holds });\n\t}\n\n\t#frequencies(): [number, number] {\n\t\tconst { frequencyMax, frequencyMin } = this;\n\n\t\treturn frequencyMin > 0 && frequencyMax > frequencyMin\n\t\t\t? [frequencyMin, frequencyMax]\n\t\t\t: defaultFrequencies;\n\t}\n\n\t#step(frameMs: number, isReducedMotion: boolean): void {\n\t\tconst floor = this.min;\n\n\t\tstepLevels(this.#levels, {\n\t\t\telapsedMs: Math.max(0, frameMs - (this.#lastFrameMs ?? frameMs)),\n\t\t\tfloor,\n\t\t\tisHeld: this.peakHold,\n\t\t\tisHoldStepped: isReducedMotion,\n\t\t\tnowMs: frameMs,\n\t\t});\n\t\tthis.#isSettled = areSettled(this.#levels, floor);\n\t\tthis.#lastFrameMs = this.#isSettled ? undefined : frameMs;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { SonicFormElement } from "./form-element.js";
|
|
2
2
|
import { ValueMapping } from "../lib/value-mapping.js";
|
|
3
|
+
import { ValueAxis } from "./value-gestures.js";
|
|
3
4
|
//#region src/elements/value-element.d.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
position: (event: PointerEvent) => number;
|
|
9
|
-
travelPx: number;
|
|
5
|
+
interface HoverBinding {
|
|
6
|
+
canShow: () => boolean;
|
|
7
|
+
place: () => void;
|
|
8
|
+
valueAt: (event: PointerEvent) => number | undefined;
|
|
10
9
|
}
|
|
11
10
|
export declare abstract class SonicValueElement extends SonicFormElement {
|
|
12
11
|
#private;
|
|
@@ -17,8 +16,8 @@ export declare abstract class SonicValueElement extends SonicFormElement {
|
|
|
17
16
|
set detent(value: number | undefined);
|
|
18
17
|
get dimmed(): boolean;
|
|
19
18
|
set dimmed(isDimmed: boolean);
|
|
20
|
-
get doublePress(): 'entry' | 'reset';
|
|
21
|
-
set doublePress(gesture: 'entry' | 'reset' | undefined);
|
|
19
|
+
get doublePress(): 'entry' | 'none' | 'reset';
|
|
20
|
+
set doublePress(gesture: 'entry' | 'none' | 'reset' | undefined);
|
|
22
21
|
get formatSpokenValue(): ((value: number) => string) | undefined;
|
|
23
22
|
set formatSpokenValue(format: ((value: number) => string) | undefined);
|
|
24
23
|
get formatValue(): ((value: number) => string) | undefined;
|
|
@@ -37,10 +36,13 @@ export declare abstract class SonicValueElement extends SonicFormElement {
|
|
|
37
36
|
set origin(value: number | undefined);
|
|
38
37
|
get parseValue(): ((text: string) => number) | undefined;
|
|
39
38
|
set parseValue(parse: ((text: string) => number) | undefined);
|
|
39
|
+
get pointerType(): string | undefined;
|
|
40
40
|
get positions(): Array<number> | undefined;
|
|
41
41
|
set positions(positions: Array<number> | undefined);
|
|
42
42
|
get readout(): boolean;
|
|
43
43
|
set readout(isShown: boolean);
|
|
44
|
+
get spokenStep(): number | undefined;
|
|
45
|
+
set spokenStep(value: number | undefined);
|
|
44
46
|
get step(): number;
|
|
45
47
|
set step(value: number | undefined);
|
|
46
48
|
get taper(): 'linear' | 'log';
|
|
@@ -51,22 +53,21 @@ export declare abstract class SonicValueElement extends SonicFormElement {
|
|
|
51
53
|
attributeChangedCallback(name: string): void;
|
|
52
54
|
connectedCallback(): void;
|
|
53
55
|
protected bindGestures(control: HTMLElement, signal: AbortSignal, grab: (event: PointerEvent) => undefined | ValueAxis): void;
|
|
56
|
+
protected bindHover(control: HTMLElement, signal: AbortSignal, { canShow, place, valueAt }: HoverBinding): void;
|
|
54
57
|
protected controlOrientation(): 'horizontal' | 'vertical' | undefined;
|
|
55
58
|
protected controlRole(): 'slider' | 'spinbutton';
|
|
56
59
|
protected abstract draw(): void;
|
|
57
60
|
protected abstract focusTarget(): HTMLElement;
|
|
58
61
|
protected heldFrom(): number | undefined;
|
|
59
|
-
protected heldWrite(
|
|
62
|
+
protected heldWrite(next: number): void;
|
|
60
63
|
protected holdChanged(): void;
|
|
61
64
|
protected input(next: number): boolean;
|
|
62
|
-
protected isEditing(): boolean;
|
|
63
65
|
protected isRevealed(): boolean;
|
|
64
66
|
protected isWrapping(): boolean;
|
|
65
67
|
protected mapping(): ValueMapping;
|
|
66
68
|
protected originValue(): number;
|
|
67
|
-
protected
|
|
69
|
+
protected playback(): number;
|
|
68
70
|
protected proportionsChanged(): void;
|
|
69
|
-
protected readModulationValue(): number | undefined;
|
|
70
71
|
protected readoutValue(): number;
|
|
71
72
|
protected render(): void;
|
|
72
73
|
protected renderReadout(): void;
|
|
@@ -75,8 +76,6 @@ export declare abstract class SonicValueElement extends SonicFormElement {
|
|
|
75
76
|
protected springTarget(): number | undefined;
|
|
76
77
|
protected tapTarget(): number | undefined;
|
|
77
78
|
protected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number;
|
|
78
|
-
protected writeModulationValue(control: HTMLElement, prefix: 'dial' | 'slider', next: null | number | undefined): void;
|
|
79
|
-
protected writeProportions(control: HTMLElement, prefix: 'dial' | 'slider', modulation: number): void;
|
|
80
79
|
}
|
|
81
80
|
//#endregion
|
|
82
81
|
//# sourceMappingURL=value-element.d.ts.map
|