@xsynaptic/sonic-ui 0.26.0 → 0.28.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 +20 -0
- package/README.md +1 -0
- package/dist/default/define/ticker.js +7 -0
- package/dist/default/define.js +1 -0
- package/dist/default/elements/{display-element.js → analyser-element.js} +10 -12
- package/dist/default/elements/button.js +12 -9
- package/dist/default/elements/dial.js +9 -13
- package/dist/default/elements/envelope.js +26 -23
- package/dist/default/elements/field-gesture.js +49 -8
- package/dist/default/elements/meter.js +6 -8
- package/dist/default/elements/number.js +5 -14
- package/dist/default/elements/position-group.js +4 -4
- package/dist/default/elements/radio-group.js +6 -9
- package/dist/default/elements/segmented.js +7 -4
- package/dist/default/elements/slider.js +12 -13
- package/dist/default/elements/sonic-element.js +7 -5
- package/dist/default/elements/spectrum.js +3 -3
- package/dist/default/elements/switch.js +11 -6
- package/dist/default/elements/ticker.js +313 -0
- package/dist/default/elements/toggle.js +14 -8
- package/dist/default/elements/value-element.js +11 -3
- package/dist/default/elements/wave-element.js +11 -12
- package/dist/default/elements/waveform.js +1 -4
- package/dist/default/elements/wavestrip.js +3 -6
- package/dist/default/elements/xy.js +34 -47
- package/dist/default/index.js +2 -1
- package/dist/default/lib/cancel-zone.js +12 -0
- package/dist/default/lib/canvas-surface.js +13 -1
- package/dist/default/lib/distribute.js +2 -3
- package/dist/default/lib/mirror-children.js +1 -1
- package/dist/default/lib/pointer-drag.js +8 -2
- package/dist/default/lib/render.js +2 -2
- package/dist/default/lib/ticker-leg.js +54 -0
- package/dist/dev/define/ticker.d.ts +2 -0
- package/dist/dev/define/ticker.js +8 -0
- package/dist/dev/define/ticker.js.map +1 -0
- package/dist/dev/define.d.ts +3 -1
- package/dist/dev/define.js +1 -0
- package/dist/dev/elements/{display-element.d.ts → analyser-element.d.ts} +3 -3
- package/dist/dev/elements/{display-element.js → analyser-element.js} +11 -14
- package/dist/dev/elements/analyser-element.js.map +1 -0
- package/dist/dev/elements/button.d.ts +4 -1
- package/dist/dev/elements/button.js +12 -14
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +4 -2
- package/dist/dev/elements/dial.js +9 -17
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.d.ts +5 -1
- package/dist/dev/elements/envelope.js +26 -27
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +49 -8
- package/dist/dev/elements/field-gesture.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +2 -1
- package/dist/dev/elements/meter.js +6 -9
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +2 -2
- package/dist/dev/elements/number.js +5 -15
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/position-group.js +4 -4
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +1 -2
- package/dist/dev/elements/radio-group.js +6 -9
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/segmented.d.ts +4 -2
- package/dist/dev/elements/segmented.js +7 -9
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +12 -17
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +6 -7
- package/dist/dev/elements/sonic-element.js +14 -83
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/spectrum.d.ts +2 -2
- package/dist/dev/elements/spectrum.js +3 -3
- package/dist/dev/elements/spectrum.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +4 -1
- package/dist/dev/elements/switch.js +11 -10
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/ticker.d.ts +25 -0
- package/dist/dev/elements/ticker.js +314 -0
- package/dist/dev/elements/ticker.js.map +1 -0
- package/dist/dev/elements/toggle.d.ts +4 -1
- package/dist/dev/elements/toggle.js +14 -13
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +4 -3
- package/dist/dev/elements/value-element.js +11 -3
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +2 -2
- package/dist/dev/elements/wave-element.js +11 -16
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +0 -1
- package/dist/dev/elements/waveform.js +1 -4
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.js +3 -6
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.d.ts +4 -1
- package/dist/dev/elements/xy.js +34 -51
- 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/cancel-zone.js +13 -0
- package/dist/dev/lib/cancel-zone.js.map +1 -0
- package/dist/dev/lib/canvas-surface.js +13 -1
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/check-styles.d.ts +8 -0
- package/dist/dev/lib/check-styles.js +95 -0
- package/dist/dev/lib/check-styles.js.map +1 -0
- package/dist/dev/lib/distribute.js +2 -3
- package/dist/dev/lib/distribute.js.map +1 -1
- package/dist/dev/lib/mirror-children.js +9 -7
- package/dist/dev/lib/mirror-children.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +8 -2
- package/dist/dev/lib/pointer-drag.js.map +1 -1
- package/dist/dev/lib/render.js +2 -2
- package/dist/dev/lib/render.js.map +1 -1
- package/dist/dev/lib/ticker-leg.js +55 -0
- package/dist/dev/lib/ticker-leg.js.map +1 -0
- package/dist/styles/controls.css +1 -0
- package/dist/styles/dial.css +28 -5
- package/dist/styles/material/cap.css +16 -5
- package/dist/styles/material/core.css +1 -0
- package/dist/styles/slider.css +2 -1
- package/dist/styles/switch.css +3 -1
- package/dist/styles/ticker.css +115 -0
- package/dist/styles/toggle.css +7 -5
- package/package.json +2 -1
- package/dist/dev/elements/display-element.js.map +0 -1
|
@@ -86,9 +86,14 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
86
86
|
set spring(isSpring) {
|
|
87
87
|
this.reflect("spring", isSpring);
|
|
88
88
|
}
|
|
89
|
+
boxProbe = {
|
|
90
|
+
property: "margin-bottom",
|
|
91
|
+
selector: "[popover]"
|
|
92
|
+
};
|
|
93
|
+
control = renderSlider();
|
|
94
|
+
sheet = "slider.css";
|
|
89
95
|
#buffered = [];
|
|
90
|
-
#
|
|
91
|
-
#modulation = createModulation(this.#slider, "slider", (isModulated) => {
|
|
96
|
+
#modulation = createModulation(this.control, "slider", (isModulated) => {
|
|
92
97
|
this.toggleState("modulated", isModulated);
|
|
93
98
|
});
|
|
94
99
|
connectedCallback() {
|
|
@@ -96,7 +101,7 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
96
101
|
super.connectedCallback();
|
|
97
102
|
}
|
|
98
103
|
connect(signal) {
|
|
99
|
-
const slider = this
|
|
104
|
+
const slider = this.control;
|
|
100
105
|
const cap = requireChild(slider, ".sonic-slider-cap", HTMLDivElement);
|
|
101
106
|
bindScale(this, {
|
|
102
107
|
control: slider,
|
|
@@ -118,7 +123,7 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
118
123
|
}
|
|
119
124
|
});
|
|
120
125
|
this.render();
|
|
121
|
-
this.bindGestures(
|
|
126
|
+
this.bindGestures(signal, (event) => {
|
|
122
127
|
const axis = this.#axis(slider, cap);
|
|
123
128
|
if (event.target instanceof Node && cap.contains(event.target)) return axis;
|
|
124
129
|
if (this.groovePress === "none") return;
|
|
@@ -136,12 +141,9 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
136
141
|
origin: this.originValue(),
|
|
137
142
|
value: this.value
|
|
138
143
|
});
|
|
139
|
-
this.#renderBuffered(this
|
|
140
|
-
this.#renderScrub(this
|
|
141
|
-
this.#placeReadout(this
|
|
142
|
-
}
|
|
143
|
-
focusTarget() {
|
|
144
|
-
return this.#slider;
|
|
144
|
+
this.#renderBuffered(this.control);
|
|
145
|
+
this.#renderScrub(this.control);
|
|
146
|
+
this.#placeReadout(this.control);
|
|
145
147
|
}
|
|
146
148
|
scrubChanged() {
|
|
147
149
|
if (this.scrub) this.render();
|
|
@@ -152,9 +154,6 @@ var SonicSlider = class extends SonicValueElement {
|
|
|
152
154
|
springTarget() {
|
|
153
155
|
return this.spring ? this.originValue() : void 0;
|
|
154
156
|
}
|
|
155
|
-
stateTarget() {
|
|
156
|
-
return this.#slider;
|
|
157
|
-
}
|
|
158
157
|
#axis(slider, cap) {
|
|
159
158
|
const box = slider.getBoundingClientRect();
|
|
160
159
|
const capBox = cap.getBoundingClientRect();
|
|
@@ -48,6 +48,7 @@ var SonicElement = class SonicElement extends HTMLElement {
|
|
|
48
48
|
this.#connection = void 0;
|
|
49
49
|
throw error;
|
|
50
50
|
}
|
|
51
|
+
const { boxProbe: box, control, sheet, sheetToken: token } = this;
|
|
51
52
|
}
|
|
52
53
|
connectedMoveCallback() {}
|
|
53
54
|
disconnectedCallback() {
|
|
@@ -67,7 +68,6 @@ var SonicElement = class SonicElement extends HTMLElement {
|
|
|
67
68
|
}
|
|
68
69
|
if (!this.isDisabled() && !this.#focused()) target.focus(options);
|
|
69
70
|
}
|
|
70
|
-
checkStyles(control, sheet, box) {}
|
|
71
71
|
focusTarget() {}
|
|
72
72
|
forwardNaming(target, isNamed) {
|
|
73
73
|
const labels = isNamed ? this.namingLabels() : [];
|
|
@@ -87,7 +87,9 @@ var SonicElement = class SonicElement extends HTMLElement {
|
|
|
87
87
|
isDisabled() {
|
|
88
88
|
return this.disabled || this.matches(":disabled");
|
|
89
89
|
}
|
|
90
|
-
keepControl(
|
|
90
|
+
keepControl(signal) {
|
|
91
|
+
const control = this.control;
|
|
92
|
+
if (!control) return;
|
|
91
93
|
const keep = () => {
|
|
92
94
|
appendOnce(this, control);
|
|
93
95
|
};
|
|
@@ -116,7 +118,9 @@ var SonicElement = class SonicElement extends HTMLElement {
|
|
|
116
118
|
}
|
|
117
119
|
this.setAttribute(name, value === true ? "" : value.toString());
|
|
118
120
|
}
|
|
119
|
-
stateTarget() {
|
|
121
|
+
stateTarget() {
|
|
122
|
+
return this.control;
|
|
123
|
+
}
|
|
120
124
|
toggleState(state, isOn) {
|
|
121
125
|
if (isOn) this.#states.add(state);
|
|
122
126
|
else this.#states.delete(state);
|
|
@@ -133,8 +137,6 @@ var SonicElement = class SonicElement extends HTMLElement {
|
|
|
133
137
|
usesChild(_child) {
|
|
134
138
|
return false;
|
|
135
139
|
}
|
|
136
|
-
#checkBox(control, checked, box) {}
|
|
137
|
-
#checkHost(checked) {}
|
|
138
140
|
#focused() {
|
|
139
141
|
const root = this.getRootNode();
|
|
140
142
|
const active = root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { requireChild, template } from "../lib/render.js";
|
|
2
|
-
import {
|
|
2
|
+
import { SonicAnalyserElement } from "./analyser-element.js";
|
|
3
3
|
import { columnEdges, columnLevels, gridColumns, gridRows, levelRows, loudestBin, zoneRows } from "../lib/spectrum-columns.js";
|
|
4
4
|
import { areSettled, createLevels, receiveLevels, stepLevels } from "../lib/spectrum-levels.js";
|
|
5
5
|
|
|
@@ -71,9 +71,9 @@ function paintHolds(context, { look, rowOf }, { bars, holds }) {
|
|
|
71
71
|
context.fillStyle = look.colours.lit;
|
|
72
72
|
context.fill();
|
|
73
73
|
}
|
|
74
|
-
var SonicSpectrum = class extends
|
|
74
|
+
var SonicSpectrum = class extends SonicAnalyserElement {
|
|
75
75
|
static observedAttributes = [
|
|
76
|
-
...
|
|
76
|
+
...SonicAnalyserElement.observedAttributes,
|
|
77
77
|
"frequency-max",
|
|
78
78
|
"frequency-min",
|
|
79
79
|
"grid",
|
|
@@ -20,20 +20,25 @@ const renderBare = template(`<button class="sonic-switch-position" role="switch"
|
|
|
20
20
|
const pressCenterZone = .08;
|
|
21
21
|
var SonicSwitch = class extends SonicPositionGroupElement {
|
|
22
22
|
bare = renderBare();
|
|
23
|
+
boxProbe = {
|
|
24
|
+
property: "padding-top",
|
|
25
|
+
selector: ".sonic-switch-label"
|
|
26
|
+
};
|
|
27
|
+
control = renderSwitch();
|
|
23
28
|
defaultOrientation = "vertical";
|
|
24
|
-
|
|
25
|
-
#bat = requireChild(this.
|
|
29
|
+
sheet = "switch.css";
|
|
30
|
+
#bat = requireChild(this.control, ".sonic-switch-bat", HTMLSpanElement);
|
|
26
31
|
#isClickSwallowed = false;
|
|
27
32
|
claimPress(index, event) {
|
|
28
33
|
if (this.#isOnBat(event)) return true;
|
|
29
34
|
const target = this.pressTarget(index);
|
|
30
35
|
if (this.isOptionDisabled(target) || !this.isMomentary(target)) return false;
|
|
31
|
-
capturePointer(this.
|
|
36
|
+
capturePointer(this.control, event.pointerId);
|
|
32
37
|
this.hold(target, event.pointerId);
|
|
33
38
|
return true;
|
|
34
39
|
}
|
|
35
40
|
connect(signal) {
|
|
36
|
-
const group = this.
|
|
41
|
+
const group = this.control;
|
|
37
42
|
super.connect(signal);
|
|
38
43
|
this.bare.addEventListener("click", (event) => {
|
|
39
44
|
const isSwallowed = this.#isClickSwallowed && event instanceof PointerEvent && event.pointerType !== "";
|
|
@@ -70,7 +75,7 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
70
75
|
lift: (press, event) => {
|
|
71
76
|
this.#isClickSwallowed = true;
|
|
72
77
|
if (press.hasMoved || this.isDisabled()) return;
|
|
73
|
-
if (!isUnder(this.
|
|
78
|
+
if (!isUnder(this.control, event.clientX, event.clientY)) return;
|
|
74
79
|
this.#pressBat(event);
|
|
75
80
|
},
|
|
76
81
|
move: (press, event) => {
|
|
@@ -101,7 +106,7 @@ var SonicSwitch = class extends SonicPositionGroupElement {
|
|
|
101
106
|
#renderThrow(index) {
|
|
102
107
|
const count = this.positions().length;
|
|
103
108
|
const at = index < 0 || count < 2 ? 0 : 2 * index / (count - 1) - 1;
|
|
104
|
-
this.
|
|
109
|
+
this.control.style.setProperty("--_sonic-switch-at", String(at));
|
|
105
110
|
}
|
|
106
111
|
#sideStep(from, event) {
|
|
107
112
|
const box = this.#bat.getBoundingClientRect();
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { clamp } from "../lib/math.js";
|
|
2
|
+
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
|
+
import { SonicElement } from "./sonic-element.js";
|
|
4
|
+
import { copyNode } from "../lib/copy-node.js";
|
|
5
|
+
import { placeChildren, requireChild, template } from "../lib/render.js";
|
|
6
|
+
import { mirrorChildren } from "../lib/mirror-children.js";
|
|
7
|
+
import { bindDrag, capturePointer } from "../lib/pointer-drag.js";
|
|
8
|
+
import { readMsProperty } from "../lib/read-ms-property.js";
|
|
9
|
+
import { dragThresholdPx } from "../lib/drag-step.js";
|
|
10
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
11
|
+
import { outsideCancelZone } from "../lib/cancel-zone.js";
|
|
12
|
+
import { clippedEnds, fadeWidth, legDeparture, legFades, legPosition, tickerLeg } from "../lib/ticker-leg.js";
|
|
13
|
+
|
|
14
|
+
//#region src/elements/ticker.ts
|
|
15
|
+
const renderTicker = template(`<span class="sonic-ticker"><span class="sonic-ticker-tape"></span></span>`, HTMLSpanElement);
|
|
16
|
+
const fadePx = 12;
|
|
17
|
+
const holdMs = 2e3;
|
|
18
|
+
const ratePxPerSecond = 40;
|
|
19
|
+
var SonicTicker = class extends SonicElement {
|
|
20
|
+
static observedAttributes = ["paused"];
|
|
21
|
+
get cancellable() {
|
|
22
|
+
return this.hasAttribute("cancellable");
|
|
23
|
+
}
|
|
24
|
+
set cancellable(isCancellable) {
|
|
25
|
+
this.reflect("cancellable", isCancellable);
|
|
26
|
+
}
|
|
27
|
+
get paused() {
|
|
28
|
+
return this.hasAttribute("paused");
|
|
29
|
+
}
|
|
30
|
+
set paused(isPaused) {
|
|
31
|
+
this.reflect("paused", isPaused);
|
|
32
|
+
}
|
|
33
|
+
get text() {
|
|
34
|
+
return this.#originals().map((original) => original.textContent).join("");
|
|
35
|
+
}
|
|
36
|
+
set text(text) {
|
|
37
|
+
for (const original of this.#originals()) original.remove();
|
|
38
|
+
if (text !== void 0 && text !== "") this.prepend(text);
|
|
39
|
+
}
|
|
40
|
+
control = renderTicker();
|
|
41
|
+
sheet = "ticker.css";
|
|
42
|
+
sheetToken = "--_sonic-ticker-fade";
|
|
43
|
+
#fade = fadePx;
|
|
44
|
+
#heading = 1;
|
|
45
|
+
#isClickSwallowed = false;
|
|
46
|
+
#isFocusInside = false;
|
|
47
|
+
#isHovered = false;
|
|
48
|
+
#isOffScreen = false;
|
|
49
|
+
#isPressed = false;
|
|
50
|
+
#mark;
|
|
51
|
+
#measured = "";
|
|
52
|
+
#position = 0;
|
|
53
|
+
#reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
|
|
54
|
+
#running;
|
|
55
|
+
#travel = {
|
|
56
|
+
distance: 0,
|
|
57
|
+
hold: holdMs,
|
|
58
|
+
rate: ratePxPerSecond
|
|
59
|
+
};
|
|
60
|
+
attributeChangedCallback() {
|
|
61
|
+
this.render();
|
|
62
|
+
}
|
|
63
|
+
connect(signal) {
|
|
64
|
+
const control = this.control;
|
|
65
|
+
const tape = requireChild(control, ".sonic-ticker-tape", HTMLSpanElement);
|
|
66
|
+
const measure = () => {
|
|
67
|
+
this.#measure(tape);
|
|
68
|
+
};
|
|
69
|
+
const observer = new ResizeObserver(measure);
|
|
70
|
+
const drag = this.#bindDrag(tape, signal);
|
|
71
|
+
this.upgradeProperties("cancellable", "paused", "text");
|
|
72
|
+
mirrorChildren(this, {
|
|
73
|
+
canCopyLinks: true,
|
|
74
|
+
control,
|
|
75
|
+
copy: copyNode,
|
|
76
|
+
place: (copies) => {
|
|
77
|
+
placeChildren(tape, copies);
|
|
78
|
+
drag.end();
|
|
79
|
+
this.#measure(tape, {
|
|
80
|
+
from: 0,
|
|
81
|
+
heading: 1
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}, signal);
|
|
85
|
+
this.render();
|
|
86
|
+
observer.observe(control);
|
|
87
|
+
observer.observe(tape);
|
|
88
|
+
if ("fonts" in document) document.fonts.addEventListener("loadingdone", measure, { signal });
|
|
89
|
+
this.#bindFocus(tape, signal);
|
|
90
|
+
this.#bindPauses(signal);
|
|
91
|
+
this.#reducedMotion.addEventListener("change", () => {
|
|
92
|
+
this.#run(tape);
|
|
93
|
+
}, { signal });
|
|
94
|
+
signal.addEventListener("abort", () => {
|
|
95
|
+
observer.disconnect();
|
|
96
|
+
this.#stop();
|
|
97
|
+
}, { once: true });
|
|
98
|
+
}
|
|
99
|
+
render() {
|
|
100
|
+
if (!this.isBound()) return;
|
|
101
|
+
this.toggleState("paused", this.paused);
|
|
102
|
+
this.#syncPause();
|
|
103
|
+
}
|
|
104
|
+
#bindDrag(tape, signal) {
|
|
105
|
+
const control = this.control;
|
|
106
|
+
control.addEventListener("click", (event) => {
|
|
107
|
+
if (!this.#isClickSwallowed) return;
|
|
108
|
+
this.#isClickSwallowed = false;
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
event.stopPropagation();
|
|
111
|
+
}, {
|
|
112
|
+
capture: true,
|
|
113
|
+
signal
|
|
114
|
+
});
|
|
115
|
+
this.getRootNode().addEventListener("dragstart", (event) => {
|
|
116
|
+
if (this.#isPressed) event.preventDefault();
|
|
117
|
+
}, { signal });
|
|
118
|
+
return bindDrag(control, {
|
|
119
|
+
grab: (event) => {
|
|
120
|
+
if (this.#travel.distance <= 0) return;
|
|
121
|
+
return {
|
|
122
|
+
from: 0,
|
|
123
|
+
isDragging: false,
|
|
124
|
+
outside: void 0,
|
|
125
|
+
thresholdPx: dragThresholdPx(event.pointerType),
|
|
126
|
+
x: event.clientX
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
isCaptureLate: true,
|
|
130
|
+
move: (held, event) => {
|
|
131
|
+
if (!held.isDragging) {
|
|
132
|
+
if (Math.abs(event.clientX - held.x) < held.thresholdPx) return;
|
|
133
|
+
const { from, heading } = this.#where();
|
|
134
|
+
held.isDragging = true;
|
|
135
|
+
held.from = from;
|
|
136
|
+
held.outside = this.cancellable ? outsideCancelZone(control, "--_sonic-ticker-cancel-zone") : void 0;
|
|
137
|
+
held.x = event.clientX;
|
|
138
|
+
capturePointer(control, event.pointerId);
|
|
139
|
+
this.toggleState("dragging", true);
|
|
140
|
+
this.#stop();
|
|
141
|
+
this.#heading = heading;
|
|
142
|
+
}
|
|
143
|
+
const isOutside = held.outside?.(event) === true;
|
|
144
|
+
this.toggleState("cancelling", isOutside);
|
|
145
|
+
this.#place(tape, isOutside ? held.from : held.from - (event.clientX - held.x));
|
|
146
|
+
},
|
|
147
|
+
release: (held) => {
|
|
148
|
+
if (!held.isDragging) return;
|
|
149
|
+
this.#isClickSwallowed = true;
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
this.#isClickSwallowed = false;
|
|
152
|
+
}, 0);
|
|
153
|
+
this.toggleState("dragging", false);
|
|
154
|
+
this.toggleState("cancelling", false);
|
|
155
|
+
this.#run(tape);
|
|
156
|
+
},
|
|
157
|
+
toggle: (isPressed) => {
|
|
158
|
+
this.#isPressed = isPressed;
|
|
159
|
+
this.#syncPause();
|
|
160
|
+
}
|
|
161
|
+
}, signal);
|
|
162
|
+
}
|
|
163
|
+
#bindFocus(tape, signal) {
|
|
164
|
+
const control = this.control;
|
|
165
|
+
control.addEventListener("focusout", (event) => {
|
|
166
|
+
if (event.relatedTarget instanceof Node && control.contains(event.relatedTarget)) return;
|
|
167
|
+
control.scrollLeft = 0;
|
|
168
|
+
this.#isFocusInside = false;
|
|
169
|
+
this.#run(tape);
|
|
170
|
+
}, { signal });
|
|
171
|
+
control.addEventListener("focusin", (event) => {
|
|
172
|
+
this.#isFocusInside = true;
|
|
173
|
+
this.#run(tape);
|
|
174
|
+
if (event.target instanceof Element) event.target.scrollIntoView({ block: "nearest" });
|
|
175
|
+
}, { signal });
|
|
176
|
+
}
|
|
177
|
+
#bindPauses(signal) {
|
|
178
|
+
const control = this.control;
|
|
179
|
+
for (const type of ["pointerenter", "pointerleave"]) control.addEventListener(type, (event) => {
|
|
180
|
+
if (event.pointerType !== "mouse") return;
|
|
181
|
+
this.#isHovered = type === "pointerenter";
|
|
182
|
+
this.#syncPause();
|
|
183
|
+
}, { signal });
|
|
184
|
+
for (const type of ["focusin", "focusout"]) this.getRootNode().addEventListener(type, () => {
|
|
185
|
+
this.#syncPause();
|
|
186
|
+
}, { signal });
|
|
187
|
+
const view = new IntersectionObserver((entries) => {
|
|
188
|
+
this.#isOffScreen = entries.at(-1)?.isIntersecting === false;
|
|
189
|
+
this.#syncPause();
|
|
190
|
+
});
|
|
191
|
+
view.observe(control);
|
|
192
|
+
signal.addEventListener("abort", () => {
|
|
193
|
+
view.disconnect();
|
|
194
|
+
}, { once: true });
|
|
195
|
+
}
|
|
196
|
+
#isPaused() {
|
|
197
|
+
return this.paused || this.#isHovered || this.#isPressed || this.#isOffScreen || this.control.closest(":focus-visible") !== null;
|
|
198
|
+
}
|
|
199
|
+
#measure(tape, departure) {
|
|
200
|
+
const control = this.control;
|
|
201
|
+
const styles = getComputedStyle(control);
|
|
202
|
+
const box = control.clientWidth - readPxProperty(styles, "padding-left", 0) - readPxProperty(styles, "padding-right", 0);
|
|
203
|
+
const travel = {
|
|
204
|
+
distance: Math.max(0, Math.round(tape.offsetWidth - box)),
|
|
205
|
+
hold: readMsProperty(styles, "--_sonic-ticker-hold", holdMs),
|
|
206
|
+
rate: readPxProperty(styles, "--_sonic-ticker-rate", ratePxPerSecond)
|
|
207
|
+
};
|
|
208
|
+
const fade = readPxProperty(styles, "--_sonic-ticker-fade", fadePx);
|
|
209
|
+
const measured = [...Object.values(travel), fade].join(" ");
|
|
210
|
+
if (!departure && measured === this.#measured) return;
|
|
211
|
+
const from = departure ?? this.#where();
|
|
212
|
+
this.#fade = fade;
|
|
213
|
+
this.#measured = measured;
|
|
214
|
+
this.#travel = travel;
|
|
215
|
+
this.toggleState("overflowing", travel.distance > 0);
|
|
216
|
+
this.#run(tape, from);
|
|
217
|
+
}
|
|
218
|
+
#originals() {
|
|
219
|
+
return [...this.childNodes].filter((child) => child !== this.control);
|
|
220
|
+
}
|
|
221
|
+
#place(tape, position) {
|
|
222
|
+
const { distance } = this.#travel;
|
|
223
|
+
this.#position = clamp(position, 0, distance);
|
|
224
|
+
tape.style.translate = this.#position > 0 ? `${String(-this.#position)}px` : "";
|
|
225
|
+
writeAttribute(this.control, "data-sonic-clipped", clippedEnds(this.#position, distance));
|
|
226
|
+
this.#writeFades(this.#position);
|
|
227
|
+
}
|
|
228
|
+
#run(tape, departure = this.#where()) {
|
|
229
|
+
const leg = !this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState("dragging") ? tickerLeg(this.#travel, departure) : void 0;
|
|
230
|
+
this.#stop();
|
|
231
|
+
this.#heading = departure.heading;
|
|
232
|
+
if (leg) this.#start(tape, leg);
|
|
233
|
+
else this.#place(tape, this.#isFocusInside ? 0 : departure.from);
|
|
234
|
+
}
|
|
235
|
+
#start(tape, leg) {
|
|
236
|
+
const animation = tape.animate({ translate: [`${String(-leg.from)}px`, `${String(-leg.to)}px`] }, {
|
|
237
|
+
delay: leg.hold,
|
|
238
|
+
duration: leg.duration,
|
|
239
|
+
fill: "both"
|
|
240
|
+
});
|
|
241
|
+
animation.addEventListener("finish", () => {
|
|
242
|
+
this.#run(tape, {
|
|
243
|
+
from: leg.to,
|
|
244
|
+
heading: leg.heading
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
this.#running = {
|
|
248
|
+
animation,
|
|
249
|
+
fades: [],
|
|
250
|
+
leg
|
|
251
|
+
};
|
|
252
|
+
tape.style.translate = "";
|
|
253
|
+
writeAttribute(this.control, "data-sonic-clipped", clippedEnds(leg.from, this.#travel.distance));
|
|
254
|
+
this.#syncPause();
|
|
255
|
+
}
|
|
256
|
+
#startFades(leg, elapsed) {
|
|
257
|
+
const fades = legFades(leg, this.#travel.distance, this.#fade);
|
|
258
|
+
return ["start", "end"].flatMap((end) => {
|
|
259
|
+
const { delay, duration, widths } = fades[end];
|
|
260
|
+
if (duration <= 0 || delay + duration <= elapsed) return [];
|
|
261
|
+
return this.control.animate({ [`--_sonic-ticker-fade-${end}`]: widths.map((width) => `${String(width)}px`) }, {
|
|
262
|
+
delay: delay - elapsed,
|
|
263
|
+
duration,
|
|
264
|
+
fill: "both"
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
#stop() {
|
|
269
|
+
const running = this.#running;
|
|
270
|
+
clearTimeout(this.#mark);
|
|
271
|
+
if (!running) return;
|
|
272
|
+
for (const fade of running.fades) fade.cancel();
|
|
273
|
+
running.animation.cancel();
|
|
274
|
+
this.#running = void 0;
|
|
275
|
+
}
|
|
276
|
+
#syncPause() {
|
|
277
|
+
const running = this.#running;
|
|
278
|
+
if (!running) return;
|
|
279
|
+
const { animation, leg } = running;
|
|
280
|
+
const elapsed = Number(animation.currentTime);
|
|
281
|
+
clearTimeout(this.#mark);
|
|
282
|
+
for (const fade of running.fades) fade.cancel();
|
|
283
|
+
running.fades = [];
|
|
284
|
+
this.#writeFades(legPosition(leg, elapsed));
|
|
285
|
+
if (elapsed >= leg.hold) writeAttribute(this.control, "data-sonic-clipped", "start end");
|
|
286
|
+
if (this.#isPaused()) {
|
|
287
|
+
animation.pause();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (animation.playState === "paused") animation.play();
|
|
291
|
+
running.fades = this.#startFades(leg, elapsed);
|
|
292
|
+
if (elapsed >= leg.hold) return;
|
|
293
|
+
this.#mark = setTimeout(() => {
|
|
294
|
+
writeAttribute(this.control, "data-sonic-clipped", "start end");
|
|
295
|
+
}, leg.hold - elapsed);
|
|
296
|
+
}
|
|
297
|
+
#where() {
|
|
298
|
+
const running = this.#running;
|
|
299
|
+
if (!running) return {
|
|
300
|
+
from: this.#position,
|
|
301
|
+
heading: this.#heading
|
|
302
|
+
};
|
|
303
|
+
return legDeparture(running.leg, Number(running.animation.currentTime));
|
|
304
|
+
}
|
|
305
|
+
#writeFades(position) {
|
|
306
|
+
const { style } = this.control;
|
|
307
|
+
style.setProperty("--_sonic-ticker-fade-start", `${String(fadeWidth(position, this.#fade))}px`);
|
|
308
|
+
style.setProperty("--_sonic-ticker-fade-end", `${String(fadeWidth(this.#travel.distance - position, this.#fade))}px`);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
export { SonicTicker };
|
|
@@ -22,12 +22,18 @@ const renderBare = template(`<button class="sonic-toggle-position" role="switch"
|
|
|
22
22
|
const pressTolerance = 2;
|
|
23
23
|
var SonicToggle = class extends SonicPositionGroupElement {
|
|
24
24
|
bare = renderBare();
|
|
25
|
+
boxProbe = {
|
|
26
|
+
property: "margin-top",
|
|
27
|
+
ratio: "--_sonic-toggle-gap-ratio",
|
|
28
|
+
selector: ".sonic-toggle-cap"
|
|
29
|
+
};
|
|
30
|
+
control = renderToggle();
|
|
25
31
|
defaultOrientation = "horizontal";
|
|
26
|
-
|
|
27
|
-
#cap = requireChild(this.
|
|
32
|
+
sheet = "toggle.css";
|
|
33
|
+
#cap = requireChild(this.control, ".sonic-toggle-cap", HTMLSpanElement);
|
|
28
34
|
#draggedTo;
|
|
29
35
|
#hasWarnedOfLegends = false;
|
|
30
|
-
#well = requireChild(this.
|
|
36
|
+
#well = requireChild(this.control, ".sonic-toggle-well", HTMLSpanElement);
|
|
31
37
|
claimPress() {
|
|
32
38
|
return true;
|
|
33
39
|
}
|
|
@@ -49,7 +55,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
49
55
|
}
|
|
50
56
|
render() {
|
|
51
57
|
if (!this.isBound()) return;
|
|
52
|
-
this.
|
|
58
|
+
this.control.style.setProperty("--_sonic-toggle-count", String(this.positions().length));
|
|
53
59
|
this.#placeCap(this.#draggedTo ?? this.#restingAt());
|
|
54
60
|
super.render();
|
|
55
61
|
for (const [index, option] of this.options().entries()) option.style.setProperty("--_sonic-toggle-index", String(index));
|
|
@@ -58,11 +64,11 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
58
64
|
return renderPosition();
|
|
59
65
|
}
|
|
60
66
|
#bindCap(signal) {
|
|
61
|
-
bindDrag(this.
|
|
67
|
+
bindDrag(this.control, {
|
|
62
68
|
grab: (event) => this.#grab(event),
|
|
63
69
|
lift: (drag, event) => {
|
|
64
70
|
if (!drag.isPress || drag.isHolding || this.isDisabled()) return;
|
|
65
|
-
if (!isUnder(this.
|
|
71
|
+
if (!isUnder(this.control, event.clientX, event.clientY)) return;
|
|
66
72
|
this.#press(drag.position);
|
|
67
73
|
},
|
|
68
74
|
move: (drag, event) => {
|
|
@@ -85,7 +91,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
85
91
|
}
|
|
86
92
|
#checkLegends() {}
|
|
87
93
|
#fixedLegend() {
|
|
88
|
-
return [...this.children].find((child) => child !== this.
|
|
94
|
+
return [...this.children].find((child) => child !== this.control && !super.isMirrored(child));
|
|
89
95
|
}
|
|
90
96
|
#grab(event) {
|
|
91
97
|
const position = this.pressedIndex(event.target);
|
|
@@ -113,7 +119,7 @@ var SonicToggle = class extends SonicPositionGroupElement {
|
|
|
113
119
|
return true;
|
|
114
120
|
}
|
|
115
121
|
#placeCap(at) {
|
|
116
|
-
this.
|
|
122
|
+
this.control.style.setProperty("--_sonic-toggle-at", String(at));
|
|
117
123
|
}
|
|
118
124
|
#press(position) {
|
|
119
125
|
const target = this.pressTarget(position);
|
|
@@ -251,7 +251,8 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
251
251
|
abandonHold() {
|
|
252
252
|
this.#gestures?.abandon();
|
|
253
253
|
}
|
|
254
|
-
bindGestures(
|
|
254
|
+
bindGestures(signal, grab) {
|
|
255
|
+
const control = this.control;
|
|
255
256
|
const bubble = control.querySelector("[popover]");
|
|
256
257
|
if (bubble) this.#readout = {
|
|
257
258
|
anchor: control,
|
|
@@ -316,6 +317,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
316
317
|
controlRole() {
|
|
317
318
|
return "slider";
|
|
318
319
|
}
|
|
320
|
+
focusTarget() {
|
|
321
|
+
return this.control;
|
|
322
|
+
}
|
|
319
323
|
input(next) {
|
|
320
324
|
const previous = this.#model.value;
|
|
321
325
|
if (this.#model.input(next)) this.render();
|
|
@@ -374,8 +378,12 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
374
378
|
}
|
|
375
379
|
springTarget() {}
|
|
376
380
|
tapTarget() {}
|
|
377
|
-
|
|
378
|
-
|
|
381
|
+
upwardAxis(travelProperty) {
|
|
382
|
+
const styles = getComputedStyle(this.control);
|
|
383
|
+
return {
|
|
384
|
+
position: (event) => -event.clientY,
|
|
385
|
+
travelPx: Math.max(1, readPxProperty(styles, travelProperty, fallbackTravelPx))
|
|
386
|
+
};
|
|
379
387
|
}
|
|
380
388
|
#entryText() {
|
|
381
389
|
const value = this.#model.value;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SonicValueElement } from "./value-element.js";
|
|
2
|
-
import {
|
|
2
|
+
import { bindControlSurface } from "../lib/canvas-surface.js";
|
|
3
3
|
import { formatClock, parseClock } from "../lib/clock.js";
|
|
4
4
|
import { spokenDuration } from "../lib/spoken-duration.js";
|
|
5
5
|
|
|
@@ -32,6 +32,10 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
32
32
|
this.renderMarkers();
|
|
33
33
|
this.render();
|
|
34
34
|
}
|
|
35
|
+
boxProbe = {
|
|
36
|
+
property: "margin-bottom",
|
|
37
|
+
selector: "[popover]"
|
|
38
|
+
};
|
|
35
39
|
notation = {
|
|
36
40
|
format: formatClock,
|
|
37
41
|
parse: parseClock,
|
|
@@ -65,15 +69,13 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
65
69
|
}
|
|
66
70
|
connect(signal) {
|
|
67
71
|
const control = this.control;
|
|
68
|
-
this.keepControl(
|
|
72
|
+
this.keepControl(signal);
|
|
69
73
|
this.renderEmpty();
|
|
70
|
-
this.#surface =
|
|
74
|
+
this.#surface = bindControlSurface({
|
|
71
75
|
canvas: this.canvas,
|
|
72
76
|
colours: this.colours,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sizeProperty: this.sizeProperty
|
|
76
|
-
},
|
|
77
|
+
control,
|
|
78
|
+
fill: this.fill,
|
|
77
79
|
lengths: this.lengths,
|
|
78
80
|
numbers: this.numbers,
|
|
79
81
|
paint: (context, frame) => {
|
|
@@ -82,10 +84,10 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
82
84
|
resize: () => {
|
|
83
85
|
this.renderMarkers();
|
|
84
86
|
},
|
|
85
|
-
signal
|
|
87
|
+
signal,
|
|
88
|
+
sizeProperty: this.sizeProperty
|
|
86
89
|
});
|
|
87
90
|
this.render();
|
|
88
|
-
this.#surface.setFill(this.fill);
|
|
89
91
|
}
|
|
90
92
|
markerList() {
|
|
91
93
|
return this.#markers;
|
|
@@ -113,9 +115,6 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
113
115
|
this.#current = current;
|
|
114
116
|
this.dispatchEvent(new Event("sonic-marker", { bubbles: true }));
|
|
115
117
|
}
|
|
116
|
-
stateTarget() {
|
|
117
|
-
return this.control;
|
|
118
|
-
}
|
|
119
118
|
surface() {
|
|
120
119
|
return this.#surface;
|
|
121
120
|
}
|
|
@@ -231,7 +231,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
233
|
this.#zoomGesture = bindZoom(this.control, zoomTarget, signal);
|
|
234
|
-
this.bindGestures(
|
|
234
|
+
this.bindGestures(signal, () => this.#grab());
|
|
235
235
|
bindZoomKeys(this.control, zoomTarget, signal);
|
|
236
236
|
if (!("fonts" in document)) return;
|
|
237
237
|
document.fonts.addEventListener("loadingdone", () => {
|
|
@@ -243,9 +243,6 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
243
243
|
surface?.requestFrame();
|
|
244
244
|
if (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());
|
|
245
245
|
}
|
|
246
|
-
focusTarget() {
|
|
247
|
-
return this.control;
|
|
248
|
-
}
|
|
249
246
|
isEmpty() {
|
|
250
247
|
return !this.#peaks || this.#peaks.samples.length === 0;
|
|
251
248
|
}
|