@xsynaptic/sonic-ui 0.1.0 → 0.3.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 +8 -1
- package/dist/default/define/dial.js +7 -0
- package/dist/default/define/key.js +7 -0
- package/dist/default/define/meter.js +7 -0
- package/dist/default/define/number.js +7 -0
- package/dist/default/define/segmented.js +7 -0
- package/dist/default/define/slider.js +7 -0
- package/dist/{define.js → default/define.js} +1 -0
- package/dist/default/elements/define-once.js +7 -0
- package/dist/default/elements/dial.js +81 -0
- package/dist/default/elements/form-element.js +56 -0
- package/dist/default/elements/key.js +144 -0
- package/dist/{elements → default/elements}/meter.js +27 -16
- package/dist/default/elements/number.js +46 -0
- package/dist/default/elements/range-element.js +433 -0
- package/dist/{elements → default/elements}/range-entry.js +5 -4
- package/dist/default/elements/segmented.js +173 -0
- package/dist/default/elements/slider.js +99 -0
- package/dist/default/elements/sonic-element.js +153 -0
- package/dist/{index.js → default/index.js} +3 -1
- package/dist/default/lib/copy-node.js +30 -0
- package/dist/default/lib/percent.js +12 -0
- package/dist/default/lib/read-px-property.js +8 -0
- package/dist/{lib → default/lib}/render.js +9 -2
- package/dist/{lib → default/lib}/taper.js +4 -2
- package/dist/default/lib/write-attribute.js +11 -0
- package/dist/dev/define/dial.js.map +1 -0
- package/dist/dev/define/key.js.map +1 -0
- package/dist/dev/define/meter.js.map +1 -0
- package/dist/dev/define/number.d.ts +2 -0
- package/dist/dev/define/number.js +8 -0
- package/dist/dev/define/number.js.map +1 -0
- package/dist/dev/define/segmented.js.map +1 -0
- package/dist/dev/define/slider.js.map +1 -0
- package/dist/{define.d.ts → dev/define.d.ts} +3 -1
- package/dist/dev/define.js +6 -0
- package/dist/dev/elements/define-once.js.map +1 -0
- package/dist/{elements → dev/elements}/dial.d.ts +7 -0
- package/dist/{elements → dev/elements}/dial.js +31 -6
- package/dist/dev/elements/dial.js.map +1 -0
- package/dist/dev/elements/form-element.d.ts +22 -0
- package/dist/dev/elements/form-element.js +57 -0
- package/dist/dev/elements/form-element.js.map +1 -0
- package/dist/dev/elements/key.d.ts +30 -0
- package/dist/{elements → dev/elements}/key.js +71 -25
- package/dist/dev/elements/key.js.map +1 -0
- package/dist/{elements → dev/elements}/meter.d.ts +6 -0
- package/dist/dev/elements/meter.js +131 -0
- package/dist/dev/elements/meter.js.map +1 -0
- package/dist/dev/elements/number.d.ts +16 -0
- package/dist/dev/elements/number.js +47 -0
- package/dist/dev/elements/number.js.map +1 -0
- package/dist/dev/elements/range-element.d.ts +56 -0
- package/dist/{elements → dev/elements}/range-element.js +153 -67
- package/dist/dev/elements/range-element.js.map +1 -0
- package/dist/dev/elements/range-entry.js +75 -0
- package/dist/dev/elements/range-entry.js.map +1 -0
- package/dist/{elements → dev/elements}/segmented.d.ts +5 -2
- package/dist/dev/elements/segmented.js +174 -0
- package/dist/dev/elements/segmented.js.map +1 -0
- package/dist/{elements → dev/elements}/slider.d.ts +8 -0
- package/dist/{elements → dev/elements}/slider.js +37 -7
- package/dist/dev/elements/slider.js.map +1 -0
- package/dist/dev/elements/sonic-element.d.ts +33 -0
- package/dist/dev/elements/sonic-element.js +169 -0
- package/dist/dev/elements/sonic-element.js.map +1 -0
- package/dist/{index.d.ts → dev/index.d.ts} +3 -1
- package/dist/dev/index.js +9 -0
- package/dist/dev/lib/copy-node.js +31 -0
- package/dist/dev/lib/copy-node.js.map +1 -0
- package/dist/dev/lib/percent.d.ts +5 -0
- package/dist/dev/lib/percent.js +13 -0
- package/dist/dev/lib/percent.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -0
- package/dist/dev/lib/render.js +27 -0
- package/dist/dev/lib/render.js.map +1 -0
- package/dist/dev/lib/taper.js +36 -0
- package/dist/dev/lib/taper.js.map +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -0
- package/dist/skins/ivory.css +3 -3
- package/dist/skins/slate.css +4 -4
- package/dist/styles/controls.css +2 -1
- package/dist/styles/dial.css +70 -16
- package/dist/styles/key.css +71 -11
- package/dist/styles/led.css +75 -0
- package/dist/styles/material.css +154 -25
- package/dist/styles/meter.css +44 -8
- package/dist/styles/number.css +81 -0
- package/dist/styles/screen.css +6 -33
- package/dist/styles/segmented.css +45 -7
- package/dist/styles/slider.css +68 -4
- package/package.json +16 -13
- package/dist/define/dial.js.map +0 -1
- package/dist/define/key.js.map +0 -1
- package/dist/define/meter.js.map +0 -1
- package/dist/define/segmented.js.map +0 -1
- package/dist/define/slider.js.map +0 -1
- package/dist/elements/define-once.js.map +0 -1
- package/dist/elements/dial.js.map +0 -1
- package/dist/elements/key.d.ts +0 -17
- package/dist/elements/key.js.map +0 -1
- package/dist/elements/meter.js.map +0 -1
- package/dist/elements/range-element.d.ts +0 -32
- package/dist/elements/range-element.js.map +0 -1
- package/dist/elements/range-entry.js.map +0 -1
- package/dist/elements/segmented.js +0 -104
- package/dist/elements/segmented.js.map +0 -1
- package/dist/elements/slider.js.map +0 -1
- package/dist/elements/sonic-element.d.ts +0 -20
- package/dist/elements/sonic-element.js +0 -94
- package/dist/elements/sonic-element.js.map +0 -1
- package/dist/lib/read-px-property.js.map +0 -1
- package/dist/lib/render.js.map +0 -1
- package/dist/lib/taper.js.map +0 -1
- package/dist/lib/write-attribute.js.map +0 -1
- /package/dist/{define → dev/define}/dial.d.ts +0 -0
- /package/dist/{define → dev/define}/dial.js +0 -0
- /package/dist/{define → dev/define}/key.d.ts +0 -0
- /package/dist/{define → dev/define}/key.js +0 -0
- /package/dist/{define → dev/define}/meter.d.ts +0 -0
- /package/dist/{define → dev/define}/meter.js +0 -0
- /package/dist/{define → dev/define}/segmented.d.ts +0 -0
- /package/dist/{define → dev/define}/segmented.js +0 -0
- /package/dist/{define → dev/define}/slider.d.ts +0 -0
- /package/dist/{define → dev/define}/slider.js +0 -0
- /package/dist/{elements → dev/elements}/define-once.js +0 -0
- /package/dist/{lib → dev/lib}/read-px-property.js +0 -0
- /package/dist/{lib → dev/lib}/write-attribute.js +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @xsynaptic/sonic-ui
|
|
2
2
|
|
|
3
|
-
Native custom elements for audio interfaces
|
|
3
|
+
Native custom elements for audio interfaces. Each control renders in the light DOM, drawn in CSS, skinned with `--sonic-*` custom properties. No framework, no runtime dependencies.
|
|
4
4
|
|
|
5
5
|
Early and unstable: any 0.x release may break the API.
|
|
6
6
|
|
|
@@ -29,6 +29,13 @@ import '@xsynaptic/sonic-ui/define';
|
|
|
29
29
|
|
|
30
30
|
The styles sit in `@layer sonic`, so list it after your resets. A skin (`slate`, `lime` or `ivory`) applies under `.sonic-skin-<name>` on any ancestor. The package root exports the classes without registering them, for your own tag names.
|
|
31
31
|
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
- **Attributes and properties** reflect each other: `dial.max = 200` writes `max="200"`. `value` and the key's `pressed` are the live state and never write their attribute, as on native inputs.
|
|
35
|
+
- **Events:** `change` when a control settles, and `input` while a dial or slider moves; both bubble.
|
|
36
|
+
- **Formatting:** `formatValue` sets the text the readout and assistive technology read, and `parseValue` reads typed entry back. `formatPercent` and `parsePercent` from the package root show 0 to 1 as a percentage.
|
|
37
|
+
- **Forms:** controls that hold a value submit under their `name`, reset, take a `<label>`, and follow a disabled `<fieldset>`, as native inputs do.
|
|
38
|
+
|
|
32
39
|
## Browsers
|
|
33
40
|
|
|
34
41
|
Current Chrome and Edge, Safari 26 and Firefox 147.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { template } from "../lib/render.js";
|
|
2
|
+
import { SonicRangeElement } from "./range-element.js";
|
|
3
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
4
|
+
|
|
5
|
+
//#region src/elements/dial.ts
|
|
6
|
+
const renderDial = template(`
|
|
7
|
+
<div class="sonic-dial" role="slider" tabindex="0">
|
|
8
|
+
<div class="sonic-dial-notches"></div>
|
|
9
|
+
<div class="sonic-dial-ring"></div>
|
|
10
|
+
<div class="sonic-dial-modulation"></div>
|
|
11
|
+
<div class="sonic-dial-cap"></div>
|
|
12
|
+
<div class="sonic-dial-pointer"></div>
|
|
13
|
+
<div class="sonic-dial-readout" popover="manual">
|
|
14
|
+
<span></span>
|
|
15
|
+
<input
|
|
16
|
+
autocomplete="off"
|
|
17
|
+
class="sonic-dial-entry"
|
|
18
|
+
enterkeyhint="done"
|
|
19
|
+
hidden
|
|
20
|
+
spellcheck="false"
|
|
21
|
+
type="text"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
`, HTMLDivElement);
|
|
26
|
+
const fallbackTravelPx = 160;
|
|
27
|
+
var SonicDial = class extends SonicRangeElement {
|
|
28
|
+
static observedAttributes = [
|
|
29
|
+
...SonicRangeElement.observedAttributes,
|
|
30
|
+
"endless",
|
|
31
|
+
"modulation"
|
|
32
|
+
];
|
|
33
|
+
get endless() {
|
|
34
|
+
return this.hasAttribute("endless");
|
|
35
|
+
}
|
|
36
|
+
set endless(isEndless) {
|
|
37
|
+
this.reflect("endless", isEndless);
|
|
38
|
+
}
|
|
39
|
+
get modulation() {
|
|
40
|
+
return this.numberAttribute("modulation", 0);
|
|
41
|
+
}
|
|
42
|
+
set modulation(amount) {
|
|
43
|
+
this.reflect("modulation", amount);
|
|
44
|
+
}
|
|
45
|
+
#dial = renderDial();
|
|
46
|
+
connectedCallback() {
|
|
47
|
+
this.upgradeProperties("endless", "modulation");
|
|
48
|
+
super.connectedCallback();
|
|
49
|
+
}
|
|
50
|
+
connect(signal) {
|
|
51
|
+
const dial = this.#dial;
|
|
52
|
+
this.appendOnce(dial);
|
|
53
|
+
this.render();
|
|
54
|
+
this.checkStyles(dial, "dial.css");
|
|
55
|
+
this.bindGestures(dial, signal, () => ({
|
|
56
|
+
position: (event) => -event.clientY,
|
|
57
|
+
travelPx: readPxProperty(getComputedStyle(dial), "--_sonic-dial-travel", fallbackTravelPx)
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
focusTarget() {
|
|
61
|
+
return this.#dial;
|
|
62
|
+
}
|
|
63
|
+
render() {
|
|
64
|
+
const dial = this.#dial;
|
|
65
|
+
const positions = this.positions();
|
|
66
|
+
const [modulationFrom, modulationTo] = this.modulationFractions(this.endless ? 0 : this.modulation);
|
|
67
|
+
dial.style.setProperty("--_sonic-dial-value", String(this.fraction(this.value)));
|
|
68
|
+
dial.style.setProperty("--_sonic-dial-origin", String(this.originFraction()));
|
|
69
|
+
dial.style.setProperty("--_sonic-dial-modulation-from", String(modulationFrom));
|
|
70
|
+
dial.style.setProperty("--_sonic-dial-modulation-to", String(modulationTo));
|
|
71
|
+
if (positions === void 0) dial.style.removeProperty("--_sonic-dial-positions");
|
|
72
|
+
else dial.style.setProperty("--_sonic-dial-positions", String(positions));
|
|
73
|
+
this.renderAria(dial);
|
|
74
|
+
}
|
|
75
|
+
wraps() {
|
|
76
|
+
return this.endless;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { SonicDial };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { SonicElement } from "./sonic-element.js";
|
|
2
|
+
|
|
3
|
+
//#region src/elements/form-element.ts
|
|
4
|
+
var SonicFormElement = class extends SonicElement {
|
|
5
|
+
static formAssociated = true;
|
|
6
|
+
get form() {
|
|
7
|
+
return this.internals?.form ?? null;
|
|
8
|
+
}
|
|
9
|
+
get labels() {
|
|
10
|
+
return this.internals?.labels ?? null;
|
|
11
|
+
}
|
|
12
|
+
get name() {
|
|
13
|
+
return this.getAttribute("name") ?? "";
|
|
14
|
+
}
|
|
15
|
+
set name(value) {
|
|
16
|
+
this.reflect("name", value);
|
|
17
|
+
}
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
if (this.internals) this.internals.role = "none";
|
|
21
|
+
this.addEventListener("click", (event) => {
|
|
22
|
+
if (event.target === this) this.activate();
|
|
23
|
+
});
|
|
24
|
+
this.addEventListener("focusin", () => {
|
|
25
|
+
this.render();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
connectedCallback() {
|
|
29
|
+
this.upgradeProperties("name");
|
|
30
|
+
super.connectedCallback();
|
|
31
|
+
}
|
|
32
|
+
formDisabledCallback() {
|
|
33
|
+
this.attributeChangedCallback("disabled");
|
|
34
|
+
}
|
|
35
|
+
formResetCallback() {
|
|
36
|
+
this.attributeChangedCallback("value");
|
|
37
|
+
}
|
|
38
|
+
formStateRestoreCallback(state) {
|
|
39
|
+
const prefix = `${this.localName}:`;
|
|
40
|
+
if (typeof state === "string" && state.startsWith(prefix)) this.restoreState(state.slice(prefix.length));
|
|
41
|
+
}
|
|
42
|
+
activate() {
|
|
43
|
+
this.focus();
|
|
44
|
+
}
|
|
45
|
+
namingLabels() {
|
|
46
|
+
const internals = this.internals;
|
|
47
|
+
if (!internals || this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby")) return [];
|
|
48
|
+
return [...internals.labels].filter((label) => label instanceof Element);
|
|
49
|
+
}
|
|
50
|
+
writeFormValue(value, state) {
|
|
51
|
+
this.internals?.setFormValue(value, `${this.localName}:${state}`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { SonicFormElement };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { requireChild, template } from "../lib/render.js";
|
|
2
|
+
import { SonicFormElement } from "./form-element.js";
|
|
3
|
+
import { copyNode } from "../lib/copy-node.js";
|
|
4
|
+
|
|
5
|
+
//#region src/elements/key.ts
|
|
6
|
+
const renderKey = template(`
|
|
7
|
+
<button class="sonic-key" type="button"><span class="sonic-key-cap"></span></button>
|
|
8
|
+
`, HTMLButtonElement);
|
|
9
|
+
const holdKeys = /* @__PURE__ */ new Set([" ", "Enter"]);
|
|
10
|
+
var SonicKey = class extends SonicFormElement {
|
|
11
|
+
static observedAttributes = [
|
|
12
|
+
...SonicFormElement.observedAttributes,
|
|
13
|
+
"momentary",
|
|
14
|
+
"pressed",
|
|
15
|
+
"toggle",
|
|
16
|
+
"value"
|
|
17
|
+
];
|
|
18
|
+
get defaultPressed() {
|
|
19
|
+
return this.hasAttribute("pressed");
|
|
20
|
+
}
|
|
21
|
+
set defaultPressed(isPressed) {
|
|
22
|
+
this.reflect("pressed", isPressed);
|
|
23
|
+
}
|
|
24
|
+
get momentary() {
|
|
25
|
+
return this.hasAttribute("momentary");
|
|
26
|
+
}
|
|
27
|
+
set momentary(isMomentary) {
|
|
28
|
+
this.reflect("momentary", isMomentary);
|
|
29
|
+
}
|
|
30
|
+
get pressed() {
|
|
31
|
+
return this.#isDirty ? this.#pressed : this.defaultPressed;
|
|
32
|
+
}
|
|
33
|
+
set pressed(isPressed) {
|
|
34
|
+
this.#isDirty = true;
|
|
35
|
+
this.#pressed = isPressed === true;
|
|
36
|
+
this.render();
|
|
37
|
+
}
|
|
38
|
+
get toggle() {
|
|
39
|
+
return this.hasAttribute("toggle");
|
|
40
|
+
}
|
|
41
|
+
set toggle(isToggle) {
|
|
42
|
+
this.reflect("toggle", isToggle);
|
|
43
|
+
}
|
|
44
|
+
get value() {
|
|
45
|
+
return this.getAttribute("value") ?? "on";
|
|
46
|
+
}
|
|
47
|
+
set value(value) {
|
|
48
|
+
this.reflect("value", value);
|
|
49
|
+
}
|
|
50
|
+
#holder;
|
|
51
|
+
#isDirty = false;
|
|
52
|
+
#key = renderKey();
|
|
53
|
+
#pressed = false;
|
|
54
|
+
attributeChangedCallback(name) {
|
|
55
|
+
if (name === "pressed") this.#isDirty = false;
|
|
56
|
+
if (name === "disabled" && this.isDisabled()) this.#release();
|
|
57
|
+
this.render();
|
|
58
|
+
}
|
|
59
|
+
formResetCallback() {
|
|
60
|
+
this.attributeChangedCallback("pressed");
|
|
61
|
+
}
|
|
62
|
+
activate() {
|
|
63
|
+
this.#key.click();
|
|
64
|
+
}
|
|
65
|
+
connect(signal) {
|
|
66
|
+
const key = this.#key;
|
|
67
|
+
const cap = requireChild(key, ".sonic-key-cap", HTMLSpanElement);
|
|
68
|
+
this.upgradeProperties("defaultPressed", "momentary", "toggle", "pressed", "value");
|
|
69
|
+
this.mirrorChildren({
|
|
70
|
+
control: key,
|
|
71
|
+
copy: (originals) => {
|
|
72
|
+
cap.replaceChildren(...originals.map((original) => copyNode(original)));
|
|
73
|
+
}
|
|
74
|
+
}, signal);
|
|
75
|
+
this.render();
|
|
76
|
+
this.checkStyles(key, "key.css");
|
|
77
|
+
signal.addEventListener("abort", () => {
|
|
78
|
+
this.#release();
|
|
79
|
+
}, { once: true });
|
|
80
|
+
key.addEventListener("click", () => {
|
|
81
|
+
if (!this.toggle) return;
|
|
82
|
+
this.pressed = !this.pressed;
|
|
83
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
84
|
+
}, { signal });
|
|
85
|
+
this.#bindMomentary(key, signal);
|
|
86
|
+
}
|
|
87
|
+
focusTarget() {
|
|
88
|
+
return this.#key;
|
|
89
|
+
}
|
|
90
|
+
render() {
|
|
91
|
+
const key = this.#key;
|
|
92
|
+
key.disabled = this.isDisabled();
|
|
93
|
+
key.removeAttribute("aria-pressed");
|
|
94
|
+
if (this.toggle) key.setAttribute("aria-pressed", String(this.pressed));
|
|
95
|
+
this.toggleState("pressed", this.pressed);
|
|
96
|
+
this.forwardNaming(key, true);
|
|
97
|
+
const submitted = this.toggle && this.pressed ? this.value : null;
|
|
98
|
+
this.writeFormValue(submitted, String(this.pressed));
|
|
99
|
+
}
|
|
100
|
+
restoreState(state) {
|
|
101
|
+
if (this.toggle) this.pressed = state === "true";
|
|
102
|
+
}
|
|
103
|
+
#bindMomentary(key, signal) {
|
|
104
|
+
const releasePointer = (event) => {
|
|
105
|
+
if (event.pointerId === this.#holder) this.#release();
|
|
106
|
+
};
|
|
107
|
+
key.addEventListener("pointerdown", (event) => {
|
|
108
|
+
if (event.button !== 0 || !this.#isMomentary()) return;
|
|
109
|
+
key.setPointerCapture(event.pointerId);
|
|
110
|
+
this.#hold(event.pointerId);
|
|
111
|
+
}, { signal });
|
|
112
|
+
key.addEventListener("lostpointercapture", releasePointer, { signal });
|
|
113
|
+
key.addEventListener("pointercancel", releasePointer, { signal });
|
|
114
|
+
key.addEventListener("pointerup", releasePointer, { signal });
|
|
115
|
+
key.addEventListener("keydown", (event) => {
|
|
116
|
+
if (event.repeat || !holdKeys.has(event.key) || !this.#isMomentary()) return;
|
|
117
|
+
this.#hold(event.key);
|
|
118
|
+
}, { signal });
|
|
119
|
+
key.addEventListener("keyup", (event) => {
|
|
120
|
+
if (event.key === this.#holder) this.#release();
|
|
121
|
+
}, { signal });
|
|
122
|
+
key.addEventListener("blur", () => {
|
|
123
|
+
if (typeof this.#holder === "string") this.#release();
|
|
124
|
+
}, { signal });
|
|
125
|
+
}
|
|
126
|
+
#hold(holder) {
|
|
127
|
+
if (this.isDisabled() || this.#holder !== void 0) return;
|
|
128
|
+
this.#holder = holder;
|
|
129
|
+
this.pressed = true;
|
|
130
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
131
|
+
}
|
|
132
|
+
#isMomentary() {
|
|
133
|
+
return this.momentary && !this.toggle;
|
|
134
|
+
}
|
|
135
|
+
#release() {
|
|
136
|
+
if (this.#holder === void 0) return;
|
|
137
|
+
this.#holder = void 0;
|
|
138
|
+
this.pressed = false;
|
|
139
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { SonicKey };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SonicElement } from "./sonic-element.js";
|
|
2
1
|
import { template } from "../lib/render.js";
|
|
2
|
+
import { SonicElement } from "./sonic-element.js";
|
|
3
3
|
|
|
4
4
|
//#region src/elements/meter.ts
|
|
5
5
|
const renderMeter = template(`
|
|
@@ -38,6 +38,24 @@ var SonicMeter = class extends SonicElement {
|
|
|
38
38
|
this.#render(now);
|
|
39
39
|
this.#schedule(now);
|
|
40
40
|
}
|
|
41
|
+
get max() {
|
|
42
|
+
return this.numberAttribute("max", 0);
|
|
43
|
+
}
|
|
44
|
+
set max(decibels) {
|
|
45
|
+
this.reflect("max", decibels);
|
|
46
|
+
}
|
|
47
|
+
get min() {
|
|
48
|
+
return this.numberAttribute("min", -60);
|
|
49
|
+
}
|
|
50
|
+
set min(decibels) {
|
|
51
|
+
this.reflect("min", decibels);
|
|
52
|
+
}
|
|
53
|
+
get orientation() {
|
|
54
|
+
return this.getAttribute("orientation") === "horizontal" ? "horizontal" : "vertical";
|
|
55
|
+
}
|
|
56
|
+
set orientation(direction) {
|
|
57
|
+
this.reflect("orientation", direction);
|
|
58
|
+
}
|
|
41
59
|
#bar = -Infinity;
|
|
42
60
|
#clipAt = -Infinity;
|
|
43
61
|
#frame;
|
|
@@ -54,7 +72,7 @@ var SonicMeter = class extends SonicElement {
|
|
|
54
72
|
}
|
|
55
73
|
connect(signal) {
|
|
56
74
|
const now = performance.now();
|
|
57
|
-
this.
|
|
75
|
+
this.upgradeProperties("max", "min", "orientation", "level");
|
|
58
76
|
this.appendOnce(this.#meter);
|
|
59
77
|
this.#render(now);
|
|
60
78
|
this.#schedule(now);
|
|
@@ -66,24 +84,18 @@ var SonicMeter = class extends SonicElement {
|
|
|
66
84
|
}, { once: true });
|
|
67
85
|
}
|
|
68
86
|
#fraction(decibels) {
|
|
69
|
-
const min = this
|
|
70
|
-
const range = this
|
|
87
|
+
const min = this.min;
|
|
88
|
+
const range = this.max - min;
|
|
71
89
|
return range > 0 ? Math.min(1, Math.max(0, (decibels - min) / range)) : 0;
|
|
72
90
|
}
|
|
73
91
|
#isSettled(now) {
|
|
74
|
-
const floor = this
|
|
92
|
+
const floor = this.min;
|
|
75
93
|
return this.#bar <= Math.max(this.#target, floor) && this.#peak <= Math.max(this.#bar, floor) && now - this.#clipAt >= holdMs;
|
|
76
94
|
}
|
|
77
|
-
#max() {
|
|
78
|
-
return this.numberAttribute("max", 0);
|
|
79
|
-
}
|
|
80
|
-
#min() {
|
|
81
|
-
return this.numberAttribute("min", -60);
|
|
82
|
-
}
|
|
83
95
|
#render(now) {
|
|
84
96
|
const meter = this.#meter;
|
|
85
|
-
meter.style.setProperty("--_sonic-meter-min", String(this
|
|
86
|
-
meter.style.setProperty("--_sonic-meter-max", String(this
|
|
97
|
+
meter.style.setProperty("--_sonic-meter-min", String(this.min));
|
|
98
|
+
meter.style.setProperty("--_sonic-meter-max", String(this.max));
|
|
87
99
|
if (this.disabled) {
|
|
88
100
|
meter.style.setProperty("--_sonic-meter-level", "0");
|
|
89
101
|
meter.style.setProperty("--_sonic-meter-peak", "0");
|
|
@@ -103,7 +115,7 @@ var SonicMeter = class extends SonicElement {
|
|
|
103
115
|
const fall = releaseDecibelsPerSecond * Math.max(0, time - (this.#lastFrame ?? time)) / 1e3;
|
|
104
116
|
this.#frame = void 0;
|
|
105
117
|
this.#lastFrame = time;
|
|
106
|
-
this.#bar = Math.max(this.#target, this
|
|
118
|
+
this.#bar = Math.max(this.#target, this.min, this.#bar - fall);
|
|
107
119
|
if (time - this.#peakAt >= holdMs) this.#peak = Math.max(this.#bar, this.#peak - fall);
|
|
108
120
|
this.#render(time);
|
|
109
121
|
if (this.#isSettled(time)) {
|
|
@@ -115,5 +127,4 @@ var SonicMeter = class extends SonicElement {
|
|
|
115
127
|
};
|
|
116
128
|
|
|
117
129
|
//#endregion
|
|
118
|
-
export { SonicMeter };
|
|
119
|
-
//# sourceMappingURL=meter.js.map
|
|
130
|
+
export { SonicMeter };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { requireChild, template } from "../lib/render.js";
|
|
2
|
+
import { SonicRangeElement } from "./range-element.js";
|
|
3
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
4
|
+
|
|
5
|
+
//#region src/elements/number.ts
|
|
6
|
+
const renderNumber = template(`
|
|
7
|
+
<div class="sonic-number" role="spinbutton" tabindex="0">
|
|
8
|
+
<span aria-hidden="true" class="sonic-number-value"></span>
|
|
9
|
+
<input
|
|
10
|
+
autocomplete="off"
|
|
11
|
+
class="sonic-number-entry"
|
|
12
|
+
enterkeyhint="done"
|
|
13
|
+
hidden
|
|
14
|
+
spellcheck="false"
|
|
15
|
+
type="text"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
`, HTMLDivElement);
|
|
19
|
+
const fallbackTravelPx = 160;
|
|
20
|
+
var SonicNumber = class extends SonicRangeElement {
|
|
21
|
+
#number = renderNumber();
|
|
22
|
+
#digits = requireChild(this.#number, ".sonic-number-value", HTMLSpanElement);
|
|
23
|
+
connect(signal) {
|
|
24
|
+
const number = this.#number;
|
|
25
|
+
this.appendOnce(number);
|
|
26
|
+
this.render();
|
|
27
|
+
this.checkStyles(number, "number.css");
|
|
28
|
+
this.bindGestures(number, signal, () => ({
|
|
29
|
+
position: (event) => -event.clientY,
|
|
30
|
+
travelPx: readPxProperty(getComputedStyle(number), "--_sonic-number-travel", fallbackTravelPx)
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
controlRole() {
|
|
34
|
+
return "spinbutton";
|
|
35
|
+
}
|
|
36
|
+
focusTarget() {
|
|
37
|
+
return this.#number;
|
|
38
|
+
}
|
|
39
|
+
render() {
|
|
40
|
+
this.#digits.textContent = this.valueText();
|
|
41
|
+
this.renderAria(this.#number);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { SonicNumber };
|