@xsynaptic/sonic-ui 0.17.0 → 0.19.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/CHANGELOG.md +28 -0
- package/README.md +122 -19
- package/dist/default/elements/button.js +2 -5
- package/dist/default/elements/dial.js +2 -7
- package/dist/default/elements/display-element.js +9 -12
- package/dist/default/elements/envelope.js +31 -78
- package/dist/default/elements/field-gesture.js +81 -0
- package/dist/default/elements/meter.js +4 -1
- package/dist/default/elements/number.js +0 -7
- package/dist/default/elements/position-group.js +42 -26
- package/dist/default/elements/radio-group.js +59 -56
- package/dist/default/elements/scale.js +3 -3
- package/dist/default/elements/segmented.js +0 -5
- package/dist/default/elements/slider.js +11 -20
- package/dist/default/elements/sonic-element.js +3 -1
- package/dist/default/elements/split.js +14 -13
- package/dist/default/elements/switch.js +31 -47
- package/dist/default/elements/toggle.js +20 -49
- package/dist/default/elements/value-element.js +72 -94
- package/dist/default/elements/value-gestures.js +22 -22
- package/dist/default/elements/wave-element.js +16 -20
- package/dist/default/elements/waveform.js +42 -241
- package/dist/default/elements/wavestrip.js +10 -5
- package/dist/default/elements/xy.js +67 -119
- package/dist/default/index.js +2 -1
- package/dist/default/lib/canvas-surface.js +25 -0
- package/dist/default/lib/clock.js +23 -0
- package/dist/default/lib/crossfade.js +1 -0
- package/dist/default/lib/envelope-curve.js +1 -0
- package/dist/default/lib/peaks-request.js +44 -0
- package/dist/default/lib/positions.js +58 -0
- package/dist/default/lib/scrub.js +12 -0
- package/dist/default/lib/spoken-duration.js +30 -0
- package/dist/default/lib/value-model.js +62 -0
- package/dist/default/lib/waveform-buckets.js +7 -1
- package/dist/default/lib/waveform-scene.js +108 -0
- package/dist/default/lib/zoom-gesture.js +97 -1
- package/dist/dev/elements/button.d.ts +2 -0
- package/dist/dev/elements/button.js +14 -5
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +2 -0
- package/dist/dev/elements/dial.js +16 -7
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/display-element.d.ts +5 -3
- package/dist/dev/elements/display-element.js +11 -9
- package/dist/dev/elements/display-element.js.map +1 -1
- package/dist/dev/elements/envelope.js +48 -78
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +82 -0
- package/dist/dev/elements/field-gesture.js.map +1 -0
- package/dist/dev/elements/meter.d.ts +10 -5
- package/dist/dev/elements/meter.js +5 -1
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.js +13 -7
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/position-group.d.ts +12 -6
- package/dist/dev/elements/position-group.js +42 -26
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +11 -7
- package/dist/dev/elements/radio-group.js +59 -56
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/scale.js +3 -3
- package/dist/dev/elements/scale.js.map +1 -1
- package/dist/dev/elements/segmented.js +12 -5
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +25 -20
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +13 -2
- package/dist/dev/elements/sonic-element.js +63 -11
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.d.ts +1 -0
- package/dist/dev/elements/split.js +14 -13
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +2 -2
- package/dist/dev/elements/switch.js +38 -47
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/toggle.js +32 -49
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +11 -5
- package/dist/dev/elements/value-element.js +72 -94
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +22 -22
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +8 -5
- package/dist/dev/elements/wave-element.js +30 -20
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +20 -22
- package/dist/dev/elements/waveform.js +42 -241
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +5 -1
- package/dist/dev/elements/wavestrip.js +10 -5
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.js +81 -119
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +5 -3
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/canvas-surface.d.ts +1 -0
- package/dist/dev/lib/canvas-surface.js +25 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/clock.d.ts +5 -0
- package/dist/dev/lib/clock.js +24 -0
- package/dist/dev/lib/clock.js.map +1 -0
- package/dist/dev/lib/crossfade.d.ts +1 -0
- package/dist/dev/lib/crossfade.js +1 -0
- package/dist/dev/lib/crossfade.js.map +1 -1
- package/dist/dev/lib/envelope-curve.d.ts +1 -0
- package/dist/dev/lib/envelope-curve.js +1 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -1
- package/dist/dev/lib/mirror-children.js.map +1 -1
- package/dist/dev/lib/owned-control.js.map +1 -1
- package/dist/dev/lib/peaks-request.d.ts +5 -0
- package/dist/dev/lib/peaks-request.js +45 -0
- package/dist/dev/lib/peaks-request.js.map +1 -0
- package/dist/dev/lib/positions.d.ts +7 -0
- package/dist/dev/lib/positions.js +59 -0
- package/dist/dev/lib/positions.js.map +1 -0
- package/dist/dev/lib/scrub.d.ts +8 -0
- package/dist/dev/lib/scrub.js +13 -0
- package/dist/dev/lib/scrub.js.map +1 -0
- package/dist/dev/lib/spoken-duration.js +31 -0
- package/dist/dev/lib/spoken-duration.js.map +1 -0
- package/dist/dev/lib/time-regions.d.ts +1 -0
- package/dist/dev/lib/time-regions.js.map +1 -1
- package/dist/dev/lib/units.d.ts +8 -0
- package/dist/dev/lib/value-model.js +63 -0
- package/dist/dev/lib/value-model.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +2 -1
- package/dist/dev/lib/waveform-buckets.js +7 -1
- package/dist/dev/lib/waveform-buckets.js.map +1 -1
- package/dist/dev/lib/waveform-scene.d.ts +11 -0
- package/dist/dev/lib/waveform-scene.js +109 -0
- package/dist/dev/lib/waveform-scene.js.map +1 -0
- package/dist/dev/lib/zoom-gesture.js +97 -1
- package/dist/dev/lib/zoom-gesture.js.map +1 -1
- package/dist/skins/amber.css +1 -0
- package/dist/skins/ivory.css +1 -0
- package/dist/skins/lime.css +1 -0
- package/dist/skins/slate.css +1 -0
- package/dist/styles/button.css +45 -11
- package/dist/styles/dial.css +10 -9
- package/dist/styles/envelope.css +10 -4
- package/dist/styles/led.css +5 -1
- package/dist/styles/material/arc.css +3 -2
- package/dist/styles/material/bracket.css +2 -2
- package/dist/styles/material/cap.css +33 -12
- package/dist/styles/material/core.css +4 -0
- package/dist/styles/material/glass.css +1 -1
- package/dist/styles/material/groove.css +4 -3
- package/dist/styles/material/keycap.css +3 -2
- package/dist/styles/material/lens.css +1 -1
- package/dist/styles/material/pane.css +5 -4
- package/dist/styles/material/readout.css +6 -6
- package/dist/styles/material/well.css +2 -2
- package/dist/styles/meter.css +2 -7
- package/dist/styles/number.css +5 -1
- package/dist/styles/panel.css +4 -3
- package/dist/styles/ring.css +1 -1
- package/dist/styles/screen.css +5 -1
- package/dist/styles/segmented.css +17 -4
- package/dist/styles/slider.css +9 -3
- package/dist/styles/spectrum.css +16 -3
- package/dist/styles/switch.css +35 -10
- package/dist/styles/toggle.css +14 -7
- package/dist/styles/waveform.css +17 -5
- package/dist/styles/wavestrip.css +11 -3
- package/dist/styles/xy.css +9 -5
- package/package.json +1 -6
- package/dist/default/dat.js +0 -51
- package/dist/default/lib/asked-value.js +0 -21
- package/dist/default/lib/fill-watch.js +0 -29
- package/dist/dev/dat.d.ts +0 -19
- package/dist/dev/dat.js +0 -52
- package/dist/dev/dat.js.map +0 -1
- package/dist/dev/lib/asked-value.js +0 -22
- package/dist/dev/lib/asked-value.js.map +0 -1
- package/dist/dev/lib/fill-watch.js +0 -30
- package/dist/dev/lib/fill-watch.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { requireChild, template } from "../lib/render.js";
|
|
2
2
|
import { bindDrag } from "../lib/pointer-drag.js";
|
|
3
|
-
import {
|
|
3
|
+
import { soleStep } from "../lib/positions.js";
|
|
4
|
+
import { isUnder } from "./radio-group.js";
|
|
4
5
|
import { SonicPositionGroupElement } from "./position-group.js";
|
|
5
6
|
|
|
6
7
|
//#region src/elements/switch.ts
|
|
@@ -23,9 +24,9 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
23
24
|
group = renderSwitch();
|
|
24
25
|
#bat = requireChild(this.group, ".sonic-switch-bat", HTMLSpanElement);
|
|
25
26
|
#isClickSwallowed = false;
|
|
26
|
-
claimPress(
|
|
27
|
+
claimPress(index, event) {
|
|
27
28
|
if (this.#isOnBat(event)) return true;
|
|
28
|
-
const target = this.pressTarget(
|
|
29
|
+
const target = this.pressTarget(index);
|
|
29
30
|
if (this.isOptionDisabled(target) || !this.isMomentary(target)) return false;
|
|
30
31
|
this.group.setPointerCapture(event.pointerId);
|
|
31
32
|
this.hold(target, event.pointerId);
|
|
@@ -34,7 +35,6 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
34
35
|
connect(signal) {
|
|
35
36
|
const group = this.group;
|
|
36
37
|
super.connect(signal);
|
|
37
|
-
this.checkStyles(group, "switch.css", ["cap"]);
|
|
38
38
|
this.bare.addEventListener("click", (event) => {
|
|
39
39
|
const isSwallowed = this.#isClickSwallowed && event instanceof PointerEvent && event.pointerType !== "";
|
|
40
40
|
this.#isClickSwallowed = false;
|
|
@@ -45,20 +45,14 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
45
45
|
}, { signal });
|
|
46
46
|
this.#bindBat(group, signal);
|
|
47
47
|
}
|
|
48
|
-
releaseTarget(
|
|
49
|
-
const target = this.pressTarget(
|
|
48
|
+
releaseTarget(index) {
|
|
49
|
+
const target = this.pressTarget(index);
|
|
50
50
|
return this.isMomentary(target) || this.isOptionDisabled(target) ? void 0 : target;
|
|
51
51
|
}
|
|
52
52
|
render() {
|
|
53
53
|
if (!this.isBound()) return;
|
|
54
54
|
super.render();
|
|
55
|
-
|
|
56
|
-
if (options.length > 0) {
|
|
57
|
-
this.#renderThrow(this.checkedIndex(options));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const onSide = this.orientation === "horizontal" ? 1 : -1;
|
|
61
|
-
this.group.style.setProperty("--_sonic-switch-at", String(this.checked ? onSide : -onSide));
|
|
55
|
+
this.#renderThrow(this.checkedIndex());
|
|
62
56
|
}
|
|
63
57
|
renderOption() {
|
|
64
58
|
return renderPosition();
|
|
@@ -66,7 +60,7 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
66
60
|
#bindBat(group, signal) {
|
|
67
61
|
bindDrag(group, {
|
|
68
62
|
grab: (event) => {
|
|
69
|
-
if (!(
|
|
63
|
+
if (!(this.pressedIndex(event.target) >= 0) || this.isDisabled() || !this.#isOnBat(event)) return;
|
|
70
64
|
return {
|
|
71
65
|
hasMoved: false,
|
|
72
66
|
last: this.along(event),
|
|
@@ -87,51 +81,41 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
87
81
|
}
|
|
88
82
|
}, signal);
|
|
89
83
|
}
|
|
90
|
-
#hasSprungFrom(
|
|
91
|
-
if (!
|
|
84
|
+
#hasSprungFrom(index) {
|
|
85
|
+
if (!this.isMomentary(index)) return false;
|
|
92
86
|
this.releaseHold();
|
|
93
|
-
return this.
|
|
87
|
+
return !this.isChecked(index);
|
|
94
88
|
}
|
|
95
89
|
#isOnBat(event) {
|
|
96
90
|
return isUnder(this.#bat, event.clientX, event.clientY);
|
|
97
91
|
}
|
|
98
92
|
#pressBat(event) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const box = this.#bat.getBoundingClientRect();
|
|
105
|
-
const [start, size] = this.orientation === "horizontal" ? [box.left, box.width] : [box.top, box.height];
|
|
106
|
-
const offset = (this.along(event) - start) / size - .5;
|
|
107
|
-
const index = this.checkedIndex(options);
|
|
108
|
-
const side = Math.abs(offset) < pressCenterZone ? 1 : Math.sign(offset);
|
|
109
|
-
const toward = this.stepFrom(index, side);
|
|
110
|
-
const option = toward && toward !== options[index] ? toward : this.stepFrom(index, -side);
|
|
111
|
-
if (!option) return;
|
|
112
|
-
this.chooseByClick(option);
|
|
113
|
-
this.#refocus(options);
|
|
114
|
-
}
|
|
115
|
-
#refocus(options) {
|
|
116
|
-
if (this.group.matches(":focus-within")) options[this.checkedIndex(options)]?.focus();
|
|
93
|
+
const from = this.checkedIndex();
|
|
94
|
+
const target = soleStep(this.positions(), from) ?? this.#sideStep(from, event);
|
|
95
|
+
if (target === void 0) return;
|
|
96
|
+
this.chooseByClick(target);
|
|
97
|
+
this.refocusAt(this.checkedIndex());
|
|
117
98
|
}
|
|
118
99
|
#renderThrow(index) {
|
|
119
|
-
const count = this.
|
|
100
|
+
const count = this.positions().length;
|
|
120
101
|
const at = index < 0 || count < 2 ? 0 : 2 * index / (count - 1) - 1;
|
|
121
102
|
this.group.style.setProperty("--_sonic-switch-at", String(at));
|
|
122
103
|
}
|
|
104
|
+
#sideStep(from, event) {
|
|
105
|
+
const box = this.#bat.getBoundingClientRect();
|
|
106
|
+
const [start, size] = this.orientation === "horizontal" ? [box.left, box.width] : [box.top, box.height];
|
|
107
|
+
const offset = (this.along(event) - start) / size - .5;
|
|
108
|
+
const side = Math.abs(offset) < pressCenterZone ? 1 : Math.sign(offset);
|
|
109
|
+
const toward = this.stepFrom(from, side);
|
|
110
|
+
return toward !== void 0 && toward !== from ? toward : this.stepFrom(from, -side);
|
|
111
|
+
}
|
|
123
112
|
#throwBy(direction, pointerId) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const option = this.stepFrom(index, direction);
|
|
131
|
-
if (!option || option === options[index] || this.#hasSprungFrom(options[index])) return;
|
|
132
|
-
if (this.isMomentary(option)) this.hold(option, pointerId);
|
|
133
|
-
else this.select(option);
|
|
134
|
-
this.#refocus(options);
|
|
113
|
+
const from = this.checkedIndex();
|
|
114
|
+
const target = this.stepFrom(from, direction);
|
|
115
|
+
if (target === void 0 || target === from || this.#hasSprungFrom(from)) return;
|
|
116
|
+
if (this.isMomentary(target)) this.hold(target, pointerId);
|
|
117
|
+
else this.select(target);
|
|
118
|
+
this.refocusAt(this.checkedIndex());
|
|
135
119
|
}
|
|
136
120
|
};
|
|
137
121
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { placeChildren, requireChild, template } from "../lib/render.js";
|
|
2
2
|
import { bindDrag } from "../lib/pointer-drag.js";
|
|
3
3
|
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
4
|
-
import {
|
|
4
|
+
import { enabledEnds } from "../lib/positions.js";
|
|
5
|
+
import { isUnder } from "./radio-group.js";
|
|
5
6
|
import { SonicPositionGroupElement } from "./position-group.js";
|
|
6
7
|
import { positionAt, travelFrom } from "../lib/toggle-travel.js";
|
|
7
8
|
|
|
@@ -31,11 +32,6 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
31
32
|
}
|
|
32
33
|
connect(signal) {
|
|
33
34
|
super.connect(signal);
|
|
34
|
-
this.checkStyles(this.group, "toggle.css", [
|
|
35
|
-
"cap",
|
|
36
|
-
"well",
|
|
37
|
-
"keycap"
|
|
38
|
-
]);
|
|
39
35
|
this.bare.addEventListener("click", (event) => {
|
|
40
36
|
if (event instanceof PointerEvent && event.pointerType !== "") return;
|
|
41
37
|
this.setChecked(!this.checked);
|
|
@@ -52,7 +48,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
52
48
|
}
|
|
53
49
|
render() {
|
|
54
50
|
if (!this.isBound()) return;
|
|
55
|
-
this.group.style.setProperty("--_sonic-toggle-count", String(this
|
|
51
|
+
this.group.style.setProperty("--_sonic-toggle-count", String(this.positions().length));
|
|
56
52
|
this.#placeCap(this.#draggedTo ?? this.#restingAt());
|
|
57
53
|
super.render();
|
|
58
54
|
for (const [index, option] of this.options().entries()) option.style.setProperty("--_sonic-toggle-index", String(index));
|
|
@@ -77,7 +73,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
77
73
|
this.#slideTo(positionAt(drag.travel, pointer), event.pointerId);
|
|
78
74
|
},
|
|
79
75
|
release: (drag) => {
|
|
80
|
-
delete
|
|
76
|
+
delete drag.marked.dataset.sonicPressed;
|
|
81
77
|
this.#draggedTo = void 0;
|
|
82
78
|
this.toggleState("dragging", false);
|
|
83
79
|
this.render();
|
|
@@ -85,25 +81,21 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
85
81
|
}, signal);
|
|
86
82
|
}
|
|
87
83
|
#checkLegends() {}
|
|
88
|
-
#enabledEnds() {
|
|
89
|
-
if (this.isBare()) return [0, 1];
|
|
90
|
-
const options = this.options();
|
|
91
|
-
const isEnabled = (option) => !this.isOptionDisabled(option);
|
|
92
|
-
return [options.findIndex(isEnabled), options.findLastIndex(isEnabled)];
|
|
93
|
-
}
|
|
94
84
|
#fixedLegend() {
|
|
95
85
|
return [...this.children].find((child) => child !== this.group && !super.isMirrored(child));
|
|
96
86
|
}
|
|
97
87
|
#grab(event) {
|
|
98
|
-
const position = this.
|
|
99
|
-
if (this.isDisabled() ||
|
|
88
|
+
const position = this.pressedIndex(event.target);
|
|
89
|
+
if (this.isDisabled() || position < 0) return void 0;
|
|
100
90
|
const isOnCap = isUnder(this.#cap, event.clientX, event.clientY);
|
|
101
91
|
const travel = this.#travel(event, isOnCap);
|
|
102
|
-
if (!travel || this
|
|
103
|
-
(position ?? this.bare
|
|
92
|
+
if (!travel || !isOnCap && this.isOptionDisabled(position)) return void 0;
|
|
93
|
+
const marked = this.options()[position] ?? this.bare;
|
|
94
|
+
marked.toggleAttribute("data-sonic-pressed", true);
|
|
104
95
|
return {
|
|
105
96
|
hasMoved: false,
|
|
106
97
|
isHolding: this.#holdFrom(position, event.pointerId),
|
|
98
|
+
marked,
|
|
107
99
|
position,
|
|
108
100
|
start: this.along(event),
|
|
109
101
|
thresholdPx: dragThresholdPx(event.pointerType),
|
|
@@ -111,57 +103,36 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
111
103
|
};
|
|
112
104
|
}
|
|
113
105
|
#holdFrom(position, pointerId) {
|
|
114
|
-
const target =
|
|
115
|
-
if (!
|
|
106
|
+
const target = this.pressTarget(position);
|
|
107
|
+
if (!this.isMomentary(target) || this.isOptionDisabled(target)) return false;
|
|
116
108
|
this.hold(target, pointerId);
|
|
117
109
|
return true;
|
|
118
110
|
}
|
|
119
|
-
#isBlocked(position, isOnCap) {
|
|
120
|
-
return !isOnCap && position !== void 0 && this.isOptionDisabled(position);
|
|
121
|
-
}
|
|
122
|
-
#onAt() {
|
|
123
|
-
return this.orientation === "vertical" ? 0 : 1;
|
|
124
|
-
}
|
|
125
111
|
#placeCap(at) {
|
|
126
112
|
this.group.style.setProperty("--_sonic-toggle-at", String(at));
|
|
127
113
|
}
|
|
128
114
|
#press(position) {
|
|
129
|
-
if (!position) {
|
|
130
|
-
this.setChecked(!this.checked);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
115
|
const target = this.pressTarget(position);
|
|
134
116
|
if (this.isOptionDisabled(target)) return;
|
|
135
117
|
this.select(target);
|
|
136
|
-
this
|
|
137
|
-
}
|
|
138
|
-
#refocus(position) {
|
|
139
|
-
if (this.group.matches(":focus-within")) position.focus();
|
|
118
|
+
this.refocusAt(target);
|
|
140
119
|
}
|
|
141
120
|
#restingAt() {
|
|
142
|
-
|
|
143
|
-
return Math.max(0, this.checkedIndex(this.options()));
|
|
121
|
+
return Math.max(0, this.checkedIndex());
|
|
144
122
|
}
|
|
145
123
|
#slideTo(at, pointerId) {
|
|
146
|
-
const crossed =
|
|
124
|
+
const crossed = Math.round(at);
|
|
147
125
|
this.#draggedTo = at;
|
|
148
126
|
this.#placeCap(at);
|
|
149
|
-
if (this.
|
|
150
|
-
this.setChecked(Math.round(at) === this.#onAt());
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (!crossed || optionValue(crossed) === this.value) return;
|
|
127
|
+
if (this.isChecked(crossed)) return;
|
|
154
128
|
this.releaseHold();
|
|
155
|
-
if (this.isOptionDisabled(crossed) ||
|
|
129
|
+
if (this.isOptionDisabled(crossed) || this.isChecked(crossed)) return;
|
|
156
130
|
if (this.isMomentary(crossed)) this.hold(crossed, pointerId);
|
|
157
131
|
else this.select(crossed);
|
|
158
|
-
this
|
|
159
|
-
}
|
|
160
|
-
#stopCount() {
|
|
161
|
-
return this.options().length || 2;
|
|
132
|
+
this.refocusAt(crossed);
|
|
162
133
|
}
|
|
163
134
|
#travel(event, isOnCap) {
|
|
164
|
-
const [first, last] =
|
|
135
|
+
const [first, last] = enabledEnds(this.positions());
|
|
165
136
|
if (first < 0) return void 0;
|
|
166
137
|
const box = this.#well.getBoundingClientRect();
|
|
167
138
|
const [start, length, thickness] = this.orientation === "vertical" ? [
|
|
@@ -175,7 +146,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
175
146
|
];
|
|
176
147
|
return travelFrom({
|
|
177
148
|
at: this.#restingAt(),
|
|
178
|
-
count: this
|
|
149
|
+
count: this.positions().length,
|
|
179
150
|
first,
|
|
180
151
|
isOnCap,
|
|
181
152
|
last,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toNumber, trimFloat } from "../lib/math.js";
|
|
2
2
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
3
|
import { SonicFormElement } from "./form-element.js";
|
|
4
4
|
import { Readout } from "./readout.js";
|
|
5
5
|
import { readPxProperty } from "../lib/read-px-property.js";
|
|
6
6
|
import { parseNumberList } from "../lib/number-list.js";
|
|
7
|
-
import { valueMapping } from "../lib/value-mapping.js";
|
|
8
7
|
import { ValueGestures } from "./value-gestures.js";
|
|
9
|
-
import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
|
|
10
8
|
import { bindHoverPreview } from "../lib/hover-preview.js";
|
|
9
|
+
import { createValueModel } from "../lib/value-model.js";
|
|
11
10
|
|
|
12
11
|
//#region src/elements/value-element.ts
|
|
13
12
|
const fallbackTravelPx = 160;
|
|
@@ -56,20 +55,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
56
55
|
input: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),
|
|
57
56
|
isDisabled: () => element.isDisabled(),
|
|
58
57
|
isHeld: () => element.#isHeld(),
|
|
59
|
-
|
|
60
|
-
element.#land = resolve;
|
|
61
|
-
},
|
|
62
|
-
limit: (bounds) => {
|
|
63
|
-
element.#limit = bounds;
|
|
64
|
-
},
|
|
65
|
-
mapping: () => element.mapping(),
|
|
66
|
-
value: () => element.#cell.value,
|
|
67
|
-
watch: (listener) => {
|
|
68
|
-
element.#watchers.add(listener);
|
|
69
|
-
return () => {
|
|
70
|
-
element.#watchers.delete(listener);
|
|
71
|
-
};
|
|
72
|
-
}
|
|
58
|
+
model: element.#model
|
|
73
59
|
});
|
|
74
60
|
}
|
|
75
61
|
get cancelling() {
|
|
@@ -177,6 +163,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
177
163
|
get pointerType() {
|
|
178
164
|
return this.#gestures?.pointerType();
|
|
179
165
|
}
|
|
166
|
+
/** The values a stepped control stops at, spaced evenly along the travel whatever their spacing */
|
|
180
167
|
get positions() {
|
|
181
168
|
return parseNumberList(this.getAttribute("positions"));
|
|
182
169
|
}
|
|
@@ -192,6 +179,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
192
179
|
get revealed() {
|
|
193
180
|
return this.hasState("revealed");
|
|
194
181
|
}
|
|
182
|
+
/** Rounds the value assistive technology hears, in value units; the value itself is untouched */
|
|
195
183
|
get spokenStep() {
|
|
196
184
|
return this.optionalNumberAttribute("spoken-step");
|
|
197
185
|
}
|
|
@@ -211,7 +199,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
211
199
|
this.reflect("taper", curve);
|
|
212
200
|
}
|
|
213
201
|
get value() {
|
|
214
|
-
return this.#
|
|
202
|
+
return this.#model.value;
|
|
215
203
|
}
|
|
216
204
|
set value(next) {
|
|
217
205
|
if (next === void 0 || next === null) {
|
|
@@ -220,17 +208,17 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
220
208
|
}
|
|
221
209
|
const value = toNumber(next);
|
|
222
210
|
if (this.#isHeld()) {
|
|
223
|
-
this
|
|
211
|
+
this.#writeHeld(value);
|
|
224
212
|
return;
|
|
225
213
|
}
|
|
226
|
-
if (
|
|
214
|
+
if (this.#model.write(value)) this.render();
|
|
227
215
|
}
|
|
228
216
|
get valueText() {
|
|
229
|
-
return this.#textFor(this.#
|
|
217
|
+
return this.#textFor(this.#model.value);
|
|
230
218
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
219
|
+
notation = {
|
|
220
|
+
format: String,
|
|
221
|
+
parse: Number.parseFloat
|
|
234
222
|
};
|
|
235
223
|
#formatEntry;
|
|
236
224
|
#formatSpokenValue;
|
|
@@ -239,25 +227,16 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
239
227
|
#heldAt;
|
|
240
228
|
#hover;
|
|
241
229
|
#instance = String(instanceCount += 1);
|
|
242
|
-
#
|
|
243
|
-
#limit;
|
|
244
|
-
#mapping;
|
|
230
|
+
#model = createValueModel(() => this.#spec());
|
|
245
231
|
#parseValue;
|
|
246
|
-
#positions;
|
|
247
232
|
#readout;
|
|
248
|
-
#watchers = /* @__PURE__ */ new Set();
|
|
249
233
|
attributeChangedCallback(name) {
|
|
250
|
-
this.#
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (this.#isHeld()) {
|
|
254
|
-
this.heldWrite(this.numberAttribute("value", this.min));
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
this.#cell.asked = void 0;
|
|
234
|
+
if (name === "value" && this.#isHeld()) {
|
|
235
|
+
this.#writeHeld(this.numberAttribute("value", this.min));
|
|
236
|
+
return;
|
|
258
237
|
}
|
|
259
238
|
if (name === "disabled" && this.isDisabled()) this.#gestures?.end();
|
|
260
|
-
|
|
239
|
+
this.#model.respec(this.numberAttribute("value", this.min), { forgetAsk: name === "value" });
|
|
261
240
|
if (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();
|
|
262
241
|
this.render();
|
|
263
242
|
}
|
|
@@ -287,26 +266,23 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
287
266
|
this.forwardNaming(target, isNamed);
|
|
288
267
|
},
|
|
289
268
|
hasEntry: () => this.entry !== "none",
|
|
290
|
-
holdChanged: () => {
|
|
291
|
-
this.#renderHold();
|
|
292
|
-
},
|
|
293
269
|
input: (next) => this.input(next),
|
|
294
270
|
isDisabled: () => this.isDisabled(),
|
|
295
271
|
keyStep: () => this.keyStep,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
mapping: () => this.mapping(),
|
|
299
|
-
parse: (text) => (this.#parseValue ?? Number.parseFloat)(text),
|
|
272
|
+
model: this.#model,
|
|
273
|
+
parse: (text) => (this.#parseValue ?? this.notation.parse)(text),
|
|
300
274
|
render: () => {
|
|
301
275
|
this.render();
|
|
302
276
|
},
|
|
277
|
+
scrubChanged: () => {
|
|
278
|
+
this.#renderHold();
|
|
279
|
+
},
|
|
303
280
|
scrubsKeyRepeat: () => this.scrubsKeyRepeat(),
|
|
304
281
|
springTarget: () => this.springTarget(),
|
|
305
282
|
tapTarget: () => this.tapTarget(),
|
|
306
283
|
toggleState: (state, isOn) => {
|
|
307
284
|
this.toggleState(state, isOn);
|
|
308
|
-
}
|
|
309
|
-
value: () => this.#cell.value
|
|
285
|
+
}
|
|
310
286
|
}, control);
|
|
311
287
|
this.#gestures.bind(signal, grab);
|
|
312
288
|
control.addEventListener("pointerup", (event) => {
|
|
@@ -322,7 +298,7 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
322
298
|
dismiss: () => {
|
|
323
299
|
this.render();
|
|
324
300
|
},
|
|
325
|
-
isTaken: () => this.#isEditing() || this
|
|
301
|
+
isTaken: () => this.#isEditing() || this.#isRevealed(),
|
|
326
302
|
show: () => {
|
|
327
303
|
if (!this.readout) return;
|
|
328
304
|
this.renderReadout();
|
|
@@ -335,60 +311,32 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
335
311
|
controlRole() {
|
|
336
312
|
return "slider";
|
|
337
313
|
}
|
|
338
|
-
heldFrom() {
|
|
339
|
-
return this.#gestures?.heldFrom();
|
|
340
|
-
}
|
|
341
|
-
heldWrite(next) {
|
|
342
|
-
this.#heldAt = next;
|
|
343
|
-
}
|
|
344
|
-
holdChanged() {}
|
|
345
314
|
input(next) {
|
|
346
|
-
const previous = this.#
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
if (this.#cell.value === previous) return false;
|
|
315
|
+
const previous = this.#model.value;
|
|
316
|
+
if (this.#model.input(next)) this.render();
|
|
317
|
+
if (this.#model.value === previous) return false;
|
|
350
318
|
this.dispatchEvent(new Event("input", {
|
|
351
319
|
bubbles: true,
|
|
352
320
|
composed: true
|
|
353
321
|
}));
|
|
354
322
|
return true;
|
|
355
323
|
}
|
|
356
|
-
isRevealed() {
|
|
357
|
-
return this.#gestures?.isRevealed() === true;
|
|
358
|
-
}
|
|
359
324
|
isWrapping() {
|
|
360
325
|
return false;
|
|
361
326
|
}
|
|
362
327
|
mapping() {
|
|
363
|
-
|
|
364
|
-
const { detent, midpoint } = this;
|
|
365
|
-
this.#mapping = valueMapping({
|
|
366
|
-
...detent === void 0 ? {} : { detent },
|
|
367
|
-
...this.#positions ? { positions: this.#positions } : {},
|
|
368
|
-
max: this.max,
|
|
369
|
-
...midpoint === void 0 ? {} : { midpoint },
|
|
370
|
-
isNotched: this.notched,
|
|
371
|
-
isWrapping: this.isWrapping(),
|
|
372
|
-
min: this.min,
|
|
373
|
-
step: this.step,
|
|
374
|
-
taper: this.taper
|
|
375
|
-
});
|
|
376
|
-
return this.#mapping;
|
|
328
|
+
return this.#model.mapping();
|
|
377
329
|
}
|
|
378
330
|
originValue() {
|
|
379
331
|
return this.origin ?? this.mapping().bounds[0];
|
|
380
332
|
}
|
|
381
|
-
playback() {
|
|
382
|
-
const from = this.heldFrom();
|
|
383
|
-
return from === void 0 ? this.#cell.value : this.#heldAt ?? from;
|
|
384
|
-
}
|
|
385
333
|
proportionsChanged() {}
|
|
386
334
|
readoutValue() {
|
|
387
|
-
return this.#shown().value ?? this.#
|
|
335
|
+
return this.#shown().value ?? this.#model.value;
|
|
388
336
|
}
|
|
389
337
|
render() {
|
|
390
338
|
if (this.isBound()) this.#renderControl();
|
|
391
|
-
|
|
339
|
+
this.#model.notify();
|
|
392
340
|
}
|
|
393
341
|
renderReadout() {
|
|
394
342
|
const readout = this.#readout;
|
|
@@ -404,18 +352,27 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
404
352
|
restoreState(state) {
|
|
405
353
|
this.value = Number(state);
|
|
406
354
|
}
|
|
355
|
+
scrubChanged() {}
|
|
407
356
|
scrubsKeyRepeat() {
|
|
408
357
|
return false;
|
|
409
358
|
}
|
|
359
|
+
scrubState() {
|
|
360
|
+
const from = this.#gestures?.scrubFrom();
|
|
361
|
+
return {
|
|
362
|
+
from,
|
|
363
|
+
isRevealed: this.#isRevealed(),
|
|
364
|
+
played: from === void 0 ? this.#model.value : this.#heldAt ?? from
|
|
365
|
+
};
|
|
366
|
+
}
|
|
410
367
|
springTarget() {}
|
|
411
368
|
tapTarget() {}
|
|
412
369
|
travelPx(control, property) {
|
|
413
370
|
return Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));
|
|
414
371
|
}
|
|
415
372
|
#entryText() {
|
|
416
|
-
const value = this.#
|
|
373
|
+
const value = this.#model.value;
|
|
417
374
|
if (this.#formatEntry) return this.#formatEntry(value);
|
|
418
|
-
if (!this.#parseValue) return
|
|
375
|
+
if (!this.#parseValue) return this.notation.format(value);
|
|
419
376
|
return this.valueText;
|
|
420
377
|
}
|
|
421
378
|
#isEditing() {
|
|
@@ -424,8 +381,11 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
424
381
|
#isHeld() {
|
|
425
382
|
return this.#gestures?.isHeld() === true;
|
|
426
383
|
}
|
|
384
|
+
#isRevealed() {
|
|
385
|
+
return this.#gestures?.isRevealed() === true;
|
|
386
|
+
}
|
|
427
387
|
#renderAria(control, orientation) {
|
|
428
|
-
this.writeFormValue(String(this.#
|
|
388
|
+
this.writeFormValue(String(this.#model.value), String(this.#model.value));
|
|
429
389
|
if (this.#isEditing()) {
|
|
430
390
|
for (const name of roleAttributes) control.removeAttribute(name);
|
|
431
391
|
this.forwardNaming(control, false);
|
|
@@ -438,15 +398,15 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
438
398
|
writeAttribute(control, "aria-valuemax", String(high));
|
|
439
399
|
writeAttribute(control, "aria-valuenow", String(this.#spokenNow()));
|
|
440
400
|
if (orientation) writeAttribute(control, "aria-orientation", orientation);
|
|
441
|
-
writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#spokenNow()));
|
|
401
|
+
writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue ?? this.notation.speak)?.(this.#spokenNow()));
|
|
442
402
|
this.forwardNaming(control, true);
|
|
443
403
|
this.#renderDisabled(control);
|
|
444
404
|
this.renderReadout();
|
|
445
405
|
}
|
|
446
406
|
#renderControl() {
|
|
447
|
-
if (this.#isEditing() || this
|
|
407
|
+
if (this.#isEditing() || this.#isRevealed()) this.#hover?.clear();
|
|
448
408
|
this.draw();
|
|
449
|
-
this.toggleState("at-origin", !this.isWrapping() && this.#
|
|
409
|
+
this.toggleState("at-origin", !this.isWrapping() && this.#model.value === this.mapping().snap(this.originValue()));
|
|
450
410
|
this.#renderAria(this.focusTarget(), this.controlOrientation());
|
|
451
411
|
}
|
|
452
412
|
#renderDisabled(control) {
|
|
@@ -456,29 +416,47 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
456
416
|
writeAttribute(control, "tabindex", isDisabled ? void 0 : stop);
|
|
457
417
|
}
|
|
458
418
|
#renderHold() {
|
|
459
|
-
if (this
|
|
419
|
+
if (this.#gestures?.scrubFrom() === void 0) this.#heldAt = void 0;
|
|
460
420
|
this.#hover?.restore();
|
|
461
421
|
this.renderReadout();
|
|
462
|
-
this.
|
|
422
|
+
this.scrubChanged();
|
|
463
423
|
}
|
|
464
424
|
#shown() {
|
|
465
425
|
if (this.#isEditing()) return {
|
|
466
426
|
isOpen: true,
|
|
467
427
|
value: void 0
|
|
468
428
|
};
|
|
469
|
-
const isRevealed = this
|
|
429
|
+
const isRevealed = this.#isRevealed();
|
|
470
430
|
const preview = isRevealed ? void 0 : this.#hover?.value();
|
|
471
431
|
return {
|
|
472
432
|
isOpen: (isRevealed || preview !== void 0) && this.readout,
|
|
473
|
-
value: preview ?? this.#
|
|
433
|
+
value: preview ?? this.#model.value
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
#spec() {
|
|
437
|
+
const { detent, midpoint, positions } = this;
|
|
438
|
+
return {
|
|
439
|
+
...detent === void 0 ? {} : { detent },
|
|
440
|
+
...positions ? { positions } : {},
|
|
441
|
+
max: this.max,
|
|
442
|
+
...midpoint === void 0 ? {} : { midpoint },
|
|
443
|
+
isNotched: this.notched,
|
|
444
|
+
isWrapping: this.isWrapping(),
|
|
445
|
+
min: this.min,
|
|
446
|
+
step: this.step,
|
|
447
|
+
taper: this.taper
|
|
474
448
|
};
|
|
475
449
|
}
|
|
476
450
|
#spokenNow() {
|
|
477
451
|
const step = this.spokenStep;
|
|
478
|
-
return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#
|
|
452
|
+
return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#model.value / step) * step) : this.#model.value;
|
|
479
453
|
}
|
|
480
454
|
#textFor(value) {
|
|
481
|
-
return this.#formatValue
|
|
455
|
+
return (this.#formatValue ?? this.notation.format)(value);
|
|
456
|
+
}
|
|
457
|
+
#writeHeld(next) {
|
|
458
|
+
this.#heldAt = next;
|
|
459
|
+
this.scrubChanged();
|
|
482
460
|
}
|
|
483
461
|
};
|
|
484
462
|
|