@xsynaptic/sonic-ui 0.4.0 → 0.6.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 +3 -20
- package/dist/default/dat.js +51 -0
- package/dist/default/define/button.js +7 -0
- package/dist/default/define/envelope.js +7 -0
- package/dist/default/define/split.js +7 -0
- package/dist/default/define/switch.js +7 -0
- package/dist/default/define/waveform.js +7 -0
- package/dist/default/define/wavestrip.js +7 -0
- package/dist/default/define/xy.js +7 -0
- package/dist/default/define.js +7 -1
- package/dist/default/elements/button.js +165 -0
- package/dist/default/elements/dial.js +16 -22
- package/dist/default/elements/envelope.js +383 -0
- package/dist/default/elements/meter.js +155 -63
- package/dist/default/elements/number.js +22 -9
- package/dist/default/elements/radio-group.js +183 -0
- package/dist/default/elements/readout-claim.js +81 -0
- package/dist/default/elements/readout.js +35 -0
- package/dist/default/elements/segmented.js +9 -159
- package/dist/default/elements/slider.js +42 -51
- package/dist/default/elements/sonic-element.js +69 -31
- package/dist/default/elements/split.js +249 -0
- package/dist/default/elements/switch.js +273 -0
- package/dist/default/elements/value-element.js +694 -0
- package/dist/default/elements/{range-entry.js → value-entry.js} +3 -3
- package/dist/default/elements/wave-element.js +80 -0
- package/dist/default/elements/waveform.js +328 -0
- package/dist/default/elements/wavestrip.js +185 -0
- package/dist/default/elements/xy.js +487 -0
- package/dist/default/index.js +10 -2
- package/dist/default/lib/adsr-shape.js +113 -0
- package/dist/default/lib/ballistics.js +55 -0
- package/dist/default/lib/canvas-surface.js +203 -0
- package/dist/default/lib/copy-node.js +32 -2
- package/dist/default/lib/crossfade.js +22 -0
- package/dist/default/lib/distribute.js +215 -0
- package/dist/default/lib/drag-step.js +99 -0
- package/dist/default/lib/envelope-curve.js +9 -0
- package/dist/default/lib/field.js +94 -0
- package/dist/default/lib/focus-by-pointer.js +28 -0
- package/dist/default/lib/frame-timeline.js +63 -0
- package/dist/default/lib/hold.js +38 -0
- package/dist/default/lib/marker-band.js +80 -0
- package/dist/default/lib/marker-rider.js +120 -0
- package/dist/default/lib/math.js +17 -3
- package/dist/default/lib/modifier-press.js +11 -0
- package/dist/default/lib/number-list.js +6 -6
- package/dist/default/lib/pointer-drag.js +46 -0
- package/dist/default/lib/render.js +20 -5
- package/dist/default/lib/resample-peaks.js +46 -0
- package/dist/default/lib/strip-scene.js +48 -0
- package/dist/default/lib/taper.js +23 -17
- package/dist/default/lib/time-regions.js +8 -0
- package/dist/default/lib/tracking-clock.js +35 -0
- package/dist/default/lib/value-mapping.js +115 -0
- package/dist/default/lib/waveform-buckets.js +41 -0
- package/dist/default/lib/write-attribute.js +1 -0
- package/dist/dev/dat.d.ts +19 -0
- package/dist/dev/dat.js +52 -0
- package/dist/dev/dat.js.map +1 -0
- package/dist/dev/define/button.d.ts +2 -0
- package/dist/dev/define/button.js +8 -0
- package/dist/dev/define/button.js.map +1 -0
- package/dist/dev/define/dial.js.map +1 -1
- package/dist/dev/define/envelope.d.ts +2 -0
- package/dist/dev/define/envelope.js +8 -0
- package/dist/dev/define/envelope.js.map +1 -0
- package/dist/dev/define/meter.js.map +1 -1
- package/dist/dev/define/number.js.map +1 -1
- package/dist/dev/define/segmented.js.map +1 -1
- package/dist/dev/define/slider.js.map +1 -1
- package/dist/dev/define/split.d.ts +2 -0
- package/dist/dev/define/split.js +8 -0
- package/dist/dev/define/split.js.map +1 -0
- package/dist/dev/define/switch.d.ts +2 -0
- package/dist/dev/define/switch.js +8 -0
- package/dist/dev/define/switch.js.map +1 -0
- package/dist/dev/define/waveform.d.ts +2 -0
- package/dist/dev/define/waveform.js +8 -0
- package/dist/dev/define/waveform.js.map +1 -0
- package/dist/dev/define/wavestrip.d.ts +2 -0
- package/dist/dev/define/wavestrip.js +8 -0
- package/dist/dev/define/wavestrip.js.map +1 -0
- package/dist/dev/define/xy.d.ts +2 -0
- package/dist/dev/define/xy.js +8 -0
- package/dist/dev/define/xy.js.map +1 -0
- package/dist/dev/define.d.ts +15 -3
- package/dist/dev/define.js +7 -1
- package/dist/dev/elements/{key.d.ts → button.d.ts} +8 -6
- package/dist/dev/elements/button.js +166 -0
- package/dist/dev/elements/button.js.map +1 -0
- package/dist/dev/elements/dial.d.ts +6 -4
- package/dist/dev/elements/dial.js +16 -22
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.d.ts +36 -0
- package/dist/dev/elements/envelope.js +388 -0
- package/dist/dev/elements/envelope.js.map +1 -0
- package/dist/dev/elements/form-element.d.ts +1 -1
- package/dist/dev/elements/form-element.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +11 -6
- package/dist/dev/elements/meter.js +155 -63
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +7 -3
- package/dist/dev/elements/number.js +22 -9
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +25 -0
- package/dist/dev/elements/radio-group.js +184 -0
- package/dist/dev/elements/radio-group.js.map +1 -0
- package/dist/dev/elements/readout-claim.js +82 -0
- package/dist/dev/elements/readout-claim.js.map +1 -0
- package/dist/dev/elements/readout.js +36 -0
- package/dist/dev/elements/readout.js.map +1 -0
- package/dist/dev/elements/segmented.d.ts +4 -10
- package/dist/dev/elements/segmented.js +9 -159
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +6 -6
- package/dist/dev/elements/slider.js +42 -51
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +6 -3
- package/dist/dev/elements/sonic-element.js +119 -40
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.d.ts +21 -0
- package/dist/dev/elements/split.js +258 -0
- package/dist/dev/elements/split.js.map +1 -0
- package/dist/dev/elements/switch.d.ts +35 -0
- package/dist/dev/elements/switch.js +274 -0
- package/dist/dev/elements/switch.js.map +1 -0
- package/dist/dev/elements/value-element.d.ts +84 -0
- package/dist/dev/elements/value-element.js +695 -0
- package/dist/dev/elements/value-element.js.map +1 -0
- package/dist/dev/elements/{range-entry.js → value-entry.js} +4 -4
- package/dist/dev/elements/value-entry.js.map +1 -0
- package/dist/dev/elements/wave-element.d.ts +37 -0
- package/dist/dev/elements/wave-element.js +81 -0
- package/dist/dev/elements/wave-element.js.map +1 -0
- package/dist/dev/elements/waveform.d.ts +55 -0
- package/dist/dev/elements/waveform.js +329 -0
- package/dist/dev/elements/waveform.js.map +1 -0
- package/dist/dev/elements/wavestrip.d.ts +51 -0
- package/dist/dev/elements/wavestrip.js +186 -0
- package/dist/dev/elements/wavestrip.js.map +1 -0
- package/dist/dev/elements/xy.d.ts +62 -0
- package/dist/dev/elements/xy.js +488 -0
- package/dist/dev/elements/xy.js.map +1 -0
- package/dist/dev/index.d.ts +11 -2
- package/dist/dev/index.js +10 -2
- package/dist/dev/lib/adsr-shape.js +114 -0
- package/dist/dev/lib/adsr-shape.js.map +1 -0
- package/dist/dev/lib/ballistics.js +56 -0
- package/dist/dev/lib/ballistics.js.map +1 -0
- package/dist/dev/lib/canvas-surface.d.ts +26 -0
- package/dist/dev/lib/canvas-surface.js +204 -0
- package/dist/dev/lib/canvas-surface.js.map +1 -0
- package/dist/dev/lib/copy-node.js +32 -2
- package/dist/dev/lib/copy-node.js.map +1 -1
- package/dist/dev/lib/crossfade.d.ts +12 -0
- package/dist/dev/lib/crossfade.js +23 -0
- package/dist/dev/lib/crossfade.js.map +1 -0
- package/dist/dev/lib/distribute.d.ts +4 -0
- package/dist/dev/lib/distribute.js +216 -0
- package/dist/dev/lib/distribute.js.map +1 -0
- package/dist/dev/lib/drag-step.js +100 -0
- package/dist/dev/lib/drag-step.js.map +1 -0
- package/dist/dev/lib/envelope-curve.d.ts +4 -0
- package/dist/dev/lib/envelope-curve.js +10 -0
- package/dist/dev/lib/envelope-curve.js.map +1 -0
- package/dist/dev/lib/field.d.ts +4 -0
- package/dist/dev/lib/field.js +95 -0
- package/dist/dev/lib/field.js.map +1 -0
- package/dist/dev/lib/focus-by-pointer.js +29 -0
- package/dist/dev/lib/focus-by-pointer.js.map +1 -0
- package/dist/dev/lib/frame-timeline.js +64 -0
- package/dist/dev/lib/frame-timeline.js.map +1 -0
- package/dist/dev/lib/hold.js +39 -0
- package/dist/dev/lib/hold.js.map +1 -0
- package/dist/dev/lib/marker-band.js +81 -0
- package/dist/dev/lib/marker-band.js.map +1 -0
- package/dist/dev/lib/marker-rider.js +121 -0
- package/dist/dev/lib/marker-rider.js.map +1 -0
- package/dist/dev/lib/math.js +17 -3
- package/dist/dev/lib/math.js.map +1 -1
- package/dist/dev/lib/modifier-press.js +12 -0
- package/dist/dev/lib/modifier-press.js.map +1 -0
- package/dist/dev/lib/number-list.js +6 -6
- package/dist/dev/lib/number-list.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +47 -0
- package/dist/dev/lib/pointer-drag.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -1
- package/dist/dev/lib/render.js +20 -5
- package/dist/dev/lib/render.js.map +1 -1
- package/dist/dev/lib/resample-peaks.js +47 -0
- package/dist/dev/lib/resample-peaks.js.map +1 -0
- package/dist/dev/lib/strip-scene.js +49 -0
- package/dist/dev/lib/strip-scene.js.map +1 -0
- package/dist/dev/lib/taper.js +23 -17
- package/dist/dev/lib/taper.js.map +1 -1
- package/dist/dev/lib/time-regions.d.ts +4 -0
- package/dist/dev/lib/time-regions.js +9 -0
- package/dist/dev/lib/time-regions.js.map +1 -0
- package/dist/dev/lib/tracking-clock.js +36 -0
- package/dist/dev/lib/tracking-clock.js.map +1 -0
- package/dist/dev/lib/value-mapping.d.ts +23 -0
- package/dist/dev/lib/value-mapping.js +116 -0
- package/dist/dev/lib/value-mapping.js.map +1 -0
- package/dist/dev/lib/waveform-buckets.d.ts +9 -0
- package/dist/dev/lib/waveform-buckets.js +42 -0
- package/dist/dev/lib/waveform-buckets.js.map +1 -0
- package/dist/dev/lib/write-attribute.js +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -1
- package/dist/skins/amber.css +8 -0
- package/dist/skins/flat.css +25 -0
- package/dist/skins/ivory.css +2 -2
- package/dist/skins/lime.css +2 -0
- package/dist/skins/slate.css +2 -0
- package/dist/styles/button.css +240 -0
- package/dist/styles/controls.css +8 -1
- package/dist/styles/dial.css +110 -56
- package/dist/styles/envelope.css +204 -0
- package/dist/styles/led.css +39 -8
- package/dist/styles/material.css +343 -100
- package/dist/styles/meter.css +41 -52
- package/dist/styles/number.css +5 -1
- package/dist/styles/panel.css +61 -0
- package/dist/styles/ring.css +74 -0
- package/dist/styles/segmented.css +46 -51
- package/dist/styles/slider.css +177 -40
- package/dist/styles/switch.css +386 -0
- package/dist/styles/waveform.css +196 -0
- package/dist/styles/wavestrip.css +232 -0
- package/dist/styles/xy.css +241 -0
- package/package.json +15 -6
- package/dist/default/define/key.js +0 -7
- package/dist/default/elements/key.js +0 -169
- package/dist/default/elements/range-element.js +0 -546
- package/dist/default/lib/detent.js +0 -17
- package/dist/dev/define/key.d.ts +0 -2
- package/dist/dev/define/key.js +0 -8
- package/dist/dev/define/key.js.map +0 -1
- package/dist/dev/elements/key.js +0 -170
- package/dist/dev/elements/key.js.map +0 -1
- package/dist/dev/elements/range-element.d.ts +0 -64
- package/dist/dev/elements/range-element.js +0 -547
- package/dist/dev/elements/range-element.js.map +0 -1
- package/dist/dev/elements/range-entry.js.map +0 -1
- package/dist/dev/lib/detent.js +0 -18
- package/dist/dev/lib/detent.js.map +0 -1
- package/dist/styles/key.css +0 -242
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { requireChild, template } from "../lib/render.js";
|
|
2
|
-
import {
|
|
3
|
-
import { SonicRangeElement } from "./range-element.js";
|
|
2
|
+
import { SonicValueElement } from "./value-element.js";
|
|
4
3
|
|
|
5
4
|
//#region src/elements/number.ts
|
|
6
5
|
const renderNumber = template(`
|
|
@@ -16,29 +15,43 @@ const renderNumber = template(`
|
|
|
16
15
|
/>
|
|
17
16
|
</div>
|
|
18
17
|
`, HTMLDivElement);
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
var SonicNumber = class extends SonicValueElement {
|
|
19
|
+
get press() {
|
|
20
|
+
return this.getAttribute("press") === "step" ? "step" : "none";
|
|
21
|
+
}
|
|
22
|
+
set press(gesture) {
|
|
23
|
+
this.reflect("press", gesture);
|
|
24
|
+
}
|
|
21
25
|
#number = renderNumber();
|
|
22
26
|
#digits = requireChild(this.#number, ".sonic-number-value", HTMLSpanElement);
|
|
27
|
+
connectedCallback() {
|
|
28
|
+
this.upgradeProperties("press");
|
|
29
|
+
super.connectedCallback();
|
|
30
|
+
}
|
|
23
31
|
connect(signal) {
|
|
24
32
|
const number = this.#number;
|
|
25
|
-
this.
|
|
33
|
+
this.keepControl(number, signal);
|
|
26
34
|
this.render();
|
|
27
35
|
this.checkStyles(number, "number.css");
|
|
28
36
|
this.bindGestures(number, signal, () => ({
|
|
29
37
|
position: (event) => -event.clientY,
|
|
30
|
-
travelPx:
|
|
38
|
+
travelPx: this.travelPx(number, "--_sonic-number-travel")
|
|
31
39
|
}));
|
|
32
40
|
}
|
|
33
41
|
controlRole() {
|
|
34
42
|
return "spinbutton";
|
|
35
43
|
}
|
|
44
|
+
draw() {
|
|
45
|
+
this.#digits.textContent = this.valueText;
|
|
46
|
+
}
|
|
36
47
|
focusTarget() {
|
|
37
48
|
return this.#number;
|
|
38
49
|
}
|
|
39
|
-
|
|
40
|
-
this
|
|
41
|
-
this.
|
|
50
|
+
tapTarget() {
|
|
51
|
+
if (this.press !== "step") return void 0;
|
|
52
|
+
const mapping = this.mapping();
|
|
53
|
+
const next = mapping.keyTarget("ArrowUp", this.value);
|
|
54
|
+
return next === void 0 || next === this.value ? mapping.bounds[0] : next;
|
|
42
55
|
}
|
|
43
56
|
};
|
|
44
57
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { placeChildren } from "../lib/render.js";
|
|
2
|
+
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
|
+
import { SonicFormElement } from "./form-element.js";
|
|
4
|
+
import { copyNode } from "../lib/copy-node.js";
|
|
5
|
+
import { bindDrag } from "../lib/pointer-drag.js";
|
|
6
|
+
|
|
7
|
+
//#region src/elements/radio-group.ts
|
|
8
|
+
const keySteps = /* @__PURE__ */ new Map([
|
|
9
|
+
["ArrowDown", 1],
|
|
10
|
+
["ArrowLeft", -1],
|
|
11
|
+
["ArrowRight", 1],
|
|
12
|
+
["ArrowUp", -1]
|
|
13
|
+
]);
|
|
14
|
+
function isInside(box, x, y) {
|
|
15
|
+
return x >= box.left && x < box.right && y >= box.top && y < box.bottom;
|
|
16
|
+
}
|
|
17
|
+
function isUnder(option, x, y) {
|
|
18
|
+
return isInside(option.getBoundingClientRect(), x, y);
|
|
19
|
+
}
|
|
20
|
+
function optionValue(option) {
|
|
21
|
+
return option.querySelector("[data-sonic-value]")?.dataset.sonicValue;
|
|
22
|
+
}
|
|
23
|
+
var SonicRadioGroupElement = class extends SonicFormElement {
|
|
24
|
+
static observedAttributes = [...SonicFormElement.observedAttributes, "value"];
|
|
25
|
+
get value() {
|
|
26
|
+
return this.#value;
|
|
27
|
+
}
|
|
28
|
+
set value(next) {
|
|
29
|
+
if (next === this.#value) return;
|
|
30
|
+
this.#value = next;
|
|
31
|
+
this.render();
|
|
32
|
+
}
|
|
33
|
+
#value = "";
|
|
34
|
+
attributeChangedCallback(name) {
|
|
35
|
+
if (name === "value") this.#value = this.getAttribute("value") ?? "";
|
|
36
|
+
this.render();
|
|
37
|
+
}
|
|
38
|
+
chooseByClick(option) {
|
|
39
|
+
this.select(option);
|
|
40
|
+
}
|
|
41
|
+
chooseByKey(option, _event) {
|
|
42
|
+
this.select(option);
|
|
43
|
+
option.focus();
|
|
44
|
+
}
|
|
45
|
+
claimPress(_option, _event) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
connect(signal) {
|
|
49
|
+
const group = this.group;
|
|
50
|
+
this.upgradeProperties("value");
|
|
51
|
+
this.mirrorChildren({
|
|
52
|
+
control: group,
|
|
53
|
+
copy: copyNode,
|
|
54
|
+
isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
|
|
55
|
+
place: (copies) => {
|
|
56
|
+
this.#copyOptions(copies);
|
|
57
|
+
}
|
|
58
|
+
}, signal);
|
|
59
|
+
group.addEventListener("click", (event) => {
|
|
60
|
+
if (event instanceof PointerEvent && event.pointerType !== "") return;
|
|
61
|
+
const option = this.optionOf(event.target);
|
|
62
|
+
if (option) this.chooseByClick(option);
|
|
63
|
+
}, { signal });
|
|
64
|
+
this.#bindPress(group, signal);
|
|
65
|
+
group.addEventListener("keydown", (event) => {
|
|
66
|
+
if (event.defaultPrevented) return;
|
|
67
|
+
const option = this.optionOf(event.target);
|
|
68
|
+
const next = option && this.#keyTarget(option, event.key);
|
|
69
|
+
if (!next) return;
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
this.chooseByKey(next, event);
|
|
72
|
+
}, { signal });
|
|
73
|
+
}
|
|
74
|
+
focusTarget() {
|
|
75
|
+
return this.options().find((option) => option.tabIndex === 0);
|
|
76
|
+
}
|
|
77
|
+
isWrapping() {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
optionOf(target) {
|
|
81
|
+
if (!(target instanceof Element)) return void 0;
|
|
82
|
+
const option = target.closest("[role=\"radio\"]");
|
|
83
|
+
return option instanceof HTMLButtonElement && option.parentElement === this.group ? option : void 0;
|
|
84
|
+
}
|
|
85
|
+
options() {
|
|
86
|
+
return [...this.group.children].filter((child) => child instanceof HTMLButtonElement && child.getAttribute("role") === "radio");
|
|
87
|
+
}
|
|
88
|
+
releaseTarget(option) {
|
|
89
|
+
return option;
|
|
90
|
+
}
|
|
91
|
+
render() {
|
|
92
|
+
if (!this.isBound()) return;
|
|
93
|
+
const options = this.options();
|
|
94
|
+
const checked = options.find((option) => optionValue(option) === this.#value);
|
|
95
|
+
const focusable = checked ?? options[0];
|
|
96
|
+
const isDisabled = this.isDisabled();
|
|
97
|
+
for (const option of options) {
|
|
98
|
+
writeAttribute(option, "disabled", isDisabled ? "" : void 0);
|
|
99
|
+
writeAttribute(option, "aria-checked", String(option === checked));
|
|
100
|
+
writeAttribute(option, "tabindex", option === focusable ? "0" : "-1");
|
|
101
|
+
}
|
|
102
|
+
this.forwardNaming(this.group, true);
|
|
103
|
+
this.writeFormValue(checked ? this.#value : null, this.#value);
|
|
104
|
+
}
|
|
105
|
+
restoreState(state) {
|
|
106
|
+
this.value = state;
|
|
107
|
+
}
|
|
108
|
+
select(option) {
|
|
109
|
+
const next = optionValue(option);
|
|
110
|
+
if (next === void 0 || next === this.#value) return;
|
|
111
|
+
this.value = next;
|
|
112
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
113
|
+
}
|
|
114
|
+
#bindPress(group, signal) {
|
|
115
|
+
bindDrag(group, {
|
|
116
|
+
grab: (event) => {
|
|
117
|
+
const option = this.optionOf(event.target);
|
|
118
|
+
if (!option || this.isDisabled() || this.claimPress(option, event)) return;
|
|
119
|
+
const boxes = this.#optionBoxes();
|
|
120
|
+
this.#markPressed(option);
|
|
121
|
+
return {
|
|
122
|
+
boxes,
|
|
123
|
+
option
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
lift: (press) => {
|
|
127
|
+
if (!press.option || this.isDisabled()) return;
|
|
128
|
+
const option = this.releaseTarget(press.option);
|
|
129
|
+
if (!option) return;
|
|
130
|
+
this.select(option);
|
|
131
|
+
if (group.matches(":focus-within")) option.focus();
|
|
132
|
+
},
|
|
133
|
+
move: (press, event) => {
|
|
134
|
+
press.option = press.boxes.find(([, box]) => isInside(box, event.clientX, event.clientY))?.[0];
|
|
135
|
+
this.#markPressed(press.option);
|
|
136
|
+
},
|
|
137
|
+
release: () => {
|
|
138
|
+
this.#markPressed(void 0);
|
|
139
|
+
}
|
|
140
|
+
}, signal);
|
|
141
|
+
}
|
|
142
|
+
#copyOptions(copies) {
|
|
143
|
+
const group = this.group;
|
|
144
|
+
const options = this.options();
|
|
145
|
+
const focused = this.optionOf(group.querySelector(":scope > :focus"));
|
|
146
|
+
const focusedValue = focused && optionValue(focused);
|
|
147
|
+
for (const [index, copy] of copies.entries()) {
|
|
148
|
+
let option = options[index];
|
|
149
|
+
if (!option) {
|
|
150
|
+
option = this.renderOption();
|
|
151
|
+
group.append(option);
|
|
152
|
+
}
|
|
153
|
+
if (option.firstElementChild) placeChildren(option.firstElementChild, [copy]);
|
|
154
|
+
}
|
|
155
|
+
for (const option of options.slice(copies.length)) option.remove();
|
|
156
|
+
this.render();
|
|
157
|
+
if (focused) this.#refocus(focused, focusedValue);
|
|
158
|
+
}
|
|
159
|
+
#keyTarget(option, key) {
|
|
160
|
+
const options = this.options();
|
|
161
|
+
if (key === "Home") return options[0];
|
|
162
|
+
if (key === "End") return options.at(-1);
|
|
163
|
+
const step = keySteps.get(key);
|
|
164
|
+
if (step === void 0) return void 0;
|
|
165
|
+
const index = options.indexOf(option) + step;
|
|
166
|
+
if (this.isWrapping()) return options[(index + options.length) % options.length];
|
|
167
|
+
return options[Math.min(Math.max(index, 0), options.length - 1)];
|
|
168
|
+
}
|
|
169
|
+
#markPressed(pressed) {
|
|
170
|
+
for (const option of this.options()) option.toggleAttribute("data-sonic-pressed", option === pressed);
|
|
171
|
+
}
|
|
172
|
+
#optionBoxes() {
|
|
173
|
+
return this.options().map((option) => [option, option.getBoundingClientRect()]);
|
|
174
|
+
}
|
|
175
|
+
#refocus(focused, value) {
|
|
176
|
+
const options = this.options();
|
|
177
|
+
const next = options.find((option) => optionValue(option) === value) ?? options.find((option) => option.tabIndex === 0);
|
|
178
|
+
if (next !== focused) next?.focus();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { SonicRadioGroupElement, isUnder, optionValue };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region src/elements/readout-claim.ts
|
|
2
|
+
const keyRevealMs = 1e3;
|
|
3
|
+
const revealMs = 250;
|
|
4
|
+
var ReadoutClaim = class {
|
|
5
|
+
get isRevealed() {
|
|
6
|
+
return this.#isDragging || this.#keyTimer !== void 0;
|
|
7
|
+
}
|
|
8
|
+
#holdTimer;
|
|
9
|
+
#hover;
|
|
10
|
+
#isDismissed = false;
|
|
11
|
+
#isDragging = false;
|
|
12
|
+
#isEditing = false;
|
|
13
|
+
#keyTimer;
|
|
14
|
+
#onLapse;
|
|
15
|
+
constructor(onLapse) {
|
|
16
|
+
this.#onLapse = onLapse;
|
|
17
|
+
}
|
|
18
|
+
conceal(by) {
|
|
19
|
+
if (by === "drag") {
|
|
20
|
+
const wasDragging = this.#isDragging;
|
|
21
|
+
clearTimeout(this.#holdTimer);
|
|
22
|
+
this.#isDragging = false;
|
|
23
|
+
return wasDragging;
|
|
24
|
+
}
|
|
25
|
+
clearTimeout(this.#keyTimer);
|
|
26
|
+
const wasTimed = this.#keyTimer !== void 0;
|
|
27
|
+
this.#keyTimer = void 0;
|
|
28
|
+
return wasTimed;
|
|
29
|
+
}
|
|
30
|
+
dismiss() {
|
|
31
|
+
const wasHovered = this.#hover !== void 0;
|
|
32
|
+
this.#hover = void 0;
|
|
33
|
+
this.#isDismissed = wasHovered || this.#isDismissed;
|
|
34
|
+
return wasHovered;
|
|
35
|
+
}
|
|
36
|
+
edit(isOpen) {
|
|
37
|
+
this.#isEditing = isOpen;
|
|
38
|
+
if (isOpen) this.#hover = void 0;
|
|
39
|
+
}
|
|
40
|
+
hover(value) {
|
|
41
|
+
if (value === void 0) this.#isDismissed = false;
|
|
42
|
+
if (this.#isDismissed || this.#isEditing || this.isRevealed) return;
|
|
43
|
+
this.#hover = value;
|
|
44
|
+
}
|
|
45
|
+
press(isDown) {
|
|
46
|
+
this.conceal("drag");
|
|
47
|
+
if (!isDown) {
|
|
48
|
+
this.#onLapse();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.#holdTimer = setTimeout(() => {
|
|
52
|
+
this.reveal("drag");
|
|
53
|
+
this.#onLapse();
|
|
54
|
+
}, 250);
|
|
55
|
+
}
|
|
56
|
+
reveal(by) {
|
|
57
|
+
this.#hover = void 0;
|
|
58
|
+
if (by === "drag") {
|
|
59
|
+
this.#isDragging = true;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
clearTimeout(this.#keyTimer);
|
|
63
|
+
this.#keyTimer = setTimeout(() => {
|
|
64
|
+
this.#keyTimer = void 0;
|
|
65
|
+
this.#onLapse();
|
|
66
|
+
}, keyRevealMs);
|
|
67
|
+
}
|
|
68
|
+
shown(current, isReadout) {
|
|
69
|
+
if (this.#isEditing) return {
|
|
70
|
+
isOpen: true,
|
|
71
|
+
value: void 0
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
isOpen: (this.isRevealed || this.#hover !== void 0) && isReadout,
|
|
75
|
+
value: this.#hover ?? current
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { ReadoutClaim };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { requireChild } from "../lib/render.js";
|
|
2
|
+
|
|
3
|
+
//#region src/elements/readout.ts
|
|
4
|
+
const canPopover = "togglePopover" in HTMLElement.prototype;
|
|
5
|
+
let readoutCount = 0;
|
|
6
|
+
var Readout = class {
|
|
7
|
+
#anchor;
|
|
8
|
+
#bubble;
|
|
9
|
+
#name = `--sonic-readout-${String(readoutCount += 1)}`;
|
|
10
|
+
#text;
|
|
11
|
+
constructor(bubble) {
|
|
12
|
+
this.#bubble = bubble;
|
|
13
|
+
this.#text = requireChild(bubble, "span", HTMLElement);
|
|
14
|
+
bubble.style.setProperty("position-anchor", this.#name);
|
|
15
|
+
}
|
|
16
|
+
close() {
|
|
17
|
+
this.#toggle(false);
|
|
18
|
+
}
|
|
19
|
+
show({ anchor, isOpen, text }) {
|
|
20
|
+
if (anchor !== this.#anchor) {
|
|
21
|
+
this.#anchor?.style.removeProperty("anchor-name");
|
|
22
|
+
anchor.style.setProperty("anchor-name", this.#name);
|
|
23
|
+
this.#anchor = anchor;
|
|
24
|
+
}
|
|
25
|
+
this.#text.hidden = text === void 0;
|
|
26
|
+
if (isOpen && text !== void 0 && this.#text.textContent !== text) this.#text.textContent = text;
|
|
27
|
+
this.#toggle(isOpen);
|
|
28
|
+
}
|
|
29
|
+
#toggle(isOpen) {
|
|
30
|
+
if (canPopover && this.#bubble.isConnected) this.#bubble.togglePopover(isOpen);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { Readout };
|
|
@@ -1,171 +1,21 @@
|
|
|
1
1
|
import { template } from "../lib/render.js";
|
|
2
|
-
import {
|
|
3
|
-
import { copyNode } from "../lib/copy-node.js";
|
|
2
|
+
import { SonicRadioGroupElement } from "./radio-group.js";
|
|
4
3
|
|
|
5
4
|
//#region src/elements/segmented.ts
|
|
6
5
|
const renderSegmented = template(`<div class="sonic-segmented" role="radiogroup"></div>`, HTMLDivElement);
|
|
7
|
-
const
|
|
8
|
-
<button class="sonic-segmented-
|
|
6
|
+
const renderOption = template(`
|
|
7
|
+
<button class="sonic-segmented-option" role="radio" type="button">
|
|
9
8
|
<span class="sonic-segmented-cap"></span>
|
|
10
9
|
</button>
|
|
11
10
|
`, HTMLButtonElement);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
["ArrowLeft", -1],
|
|
15
|
-
["ArrowRight", 1],
|
|
16
|
-
["ArrowUp", -1]
|
|
17
|
-
]);
|
|
18
|
-
function isUnder(segment, x, y) {
|
|
19
|
-
const box = segment.getBoundingClientRect();
|
|
20
|
-
return x >= box.left && x < box.right && y >= box.top && y < box.bottom;
|
|
21
|
-
}
|
|
22
|
-
function segmentValue(segment) {
|
|
23
|
-
return segment.querySelector("[data-sonic-value]")?.dataset.sonicValue;
|
|
24
|
-
}
|
|
25
|
-
var SonicSegmented = class extends SonicFormElement {
|
|
26
|
-
static observedAttributes = [...SonicFormElement.observedAttributes, "value"];
|
|
27
|
-
get value() {
|
|
28
|
-
return this.#value;
|
|
29
|
-
}
|
|
30
|
-
set value(next) {
|
|
31
|
-
if (next === this.#value) return;
|
|
32
|
-
this.#value = next;
|
|
33
|
-
this.render();
|
|
34
|
-
}
|
|
35
|
-
#press;
|
|
36
|
-
#segmented = renderSegmented();
|
|
37
|
-
#value = "";
|
|
38
|
-
attributeChangedCallback(name) {
|
|
39
|
-
if (name === "value") this.#value = this.getAttribute("value") ?? "";
|
|
40
|
-
this.render();
|
|
41
|
-
}
|
|
11
|
+
var SonicSegmented = class extends SonicRadioGroupElement {
|
|
12
|
+
group = renderSegmented();
|
|
42
13
|
connect(signal) {
|
|
43
|
-
|
|
44
|
-
this.
|
|
45
|
-
this.mirrorChildren({
|
|
46
|
-
control: segmented,
|
|
47
|
-
copy: (options) => {
|
|
48
|
-
this.#copyOptions(options);
|
|
49
|
-
},
|
|
50
|
-
isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]")
|
|
51
|
-
}, signal);
|
|
52
|
-
this.checkStyles(segmented, "segmented.css");
|
|
53
|
-
segmented.addEventListener("click", (event) => {
|
|
54
|
-
if (event instanceof PointerEvent && event.pointerType !== "") return;
|
|
55
|
-
const segment = this.#segmentOf(event.target);
|
|
56
|
-
if (segment) this.#select(segment);
|
|
57
|
-
}, { signal });
|
|
58
|
-
this.#bindPress(segmented, signal);
|
|
59
|
-
segmented.addEventListener("keydown", (event) => {
|
|
60
|
-
if (event.defaultPrevented) return;
|
|
61
|
-
const segment = this.#segmentOf(event.target);
|
|
62
|
-
const next = segment && this.#keyTarget(segment, event.key);
|
|
63
|
-
if (!next) return;
|
|
64
|
-
event.preventDefault();
|
|
65
|
-
this.#select(next);
|
|
66
|
-
next.focus();
|
|
67
|
-
}, { signal });
|
|
68
|
-
}
|
|
69
|
-
focusTarget() {
|
|
70
|
-
return this.#segments().find((segment) => segment.tabIndex === 0);
|
|
71
|
-
}
|
|
72
|
-
render() {
|
|
73
|
-
const segmented = this.#segmented;
|
|
74
|
-
const segments = this.#segments();
|
|
75
|
-
const checked = segments.find((segment) => segmentValue(segment) === this.#value);
|
|
76
|
-
const focusable = checked ?? segments[0];
|
|
77
|
-
for (const segment of segments) {
|
|
78
|
-
segment.disabled = this.isDisabled();
|
|
79
|
-
segment.setAttribute("aria-checked", String(segment === checked));
|
|
80
|
-
segment.tabIndex = segment === focusable ? 0 : -1;
|
|
81
|
-
}
|
|
82
|
-
this.forwardNaming(segmented, true);
|
|
83
|
-
this.writeFormValue(checked ? this.#value : null, this.#value);
|
|
84
|
-
}
|
|
85
|
-
restoreState(state) {
|
|
86
|
-
this.value = state;
|
|
87
|
-
}
|
|
88
|
-
#bindPress(segmented, signal) {
|
|
89
|
-
const isOwn = (event) => event.pointerId === this.#press?.pointerId;
|
|
90
|
-
const release = (event) => {
|
|
91
|
-
if (isOwn(event)) this.#hold(void 0);
|
|
92
|
-
};
|
|
93
|
-
segmented.addEventListener("pointerdown", (event) => {
|
|
94
|
-
const segment = this.#segmentOf(event.target);
|
|
95
|
-
if (!segment || event.button !== 0 || this.isDisabled()) return;
|
|
96
|
-
segmented.setPointerCapture(event.pointerId);
|
|
97
|
-
this.#hold({
|
|
98
|
-
pointerId: event.pointerId,
|
|
99
|
-
segment
|
|
100
|
-
});
|
|
101
|
-
}, { signal });
|
|
102
|
-
segmented.addEventListener("pointermove", (event) => {
|
|
103
|
-
if (!isOwn(event)) return;
|
|
104
|
-
this.#hold({
|
|
105
|
-
pointerId: event.pointerId,
|
|
106
|
-
segment: this.#segments().find((segment) => isUnder(segment, event.clientX, event.clientY))
|
|
107
|
-
});
|
|
108
|
-
}, { signal });
|
|
109
|
-
segmented.addEventListener("pointerup", (event) => {
|
|
110
|
-
const segment = isOwn(event) ? this.#press?.segment : void 0;
|
|
111
|
-
release(event);
|
|
112
|
-
if (!segment || this.isDisabled()) return;
|
|
113
|
-
this.#select(segment);
|
|
114
|
-
if (segmented.matches(":focus-within")) segment.focus();
|
|
115
|
-
}, { signal });
|
|
116
|
-
segmented.addEventListener("pointercancel", release, { signal });
|
|
117
|
-
segmented.addEventListener("lostpointercapture", release, { signal });
|
|
118
|
-
signal.addEventListener("abort", () => {
|
|
119
|
-
this.#hold(void 0);
|
|
120
|
-
}, { once: true });
|
|
121
|
-
}
|
|
122
|
-
#copyOptions(options) {
|
|
123
|
-
const segmented = this.#segmented;
|
|
124
|
-
const segments = this.#segments();
|
|
125
|
-
const focused = this.#segmentOf(segmented.querySelector(":scope > :focus"));
|
|
126
|
-
const focusedValue = focused && segmentValue(focused);
|
|
127
|
-
for (const [index, option] of options.entries()) {
|
|
128
|
-
let segment = segments[index];
|
|
129
|
-
if (!segment) {
|
|
130
|
-
segment = renderSegment();
|
|
131
|
-
segmented.append(segment);
|
|
132
|
-
}
|
|
133
|
-
segment.firstElementChild?.replaceChildren(copyNode(option));
|
|
134
|
-
}
|
|
135
|
-
for (const segment of segments.slice(options.length)) segment.remove();
|
|
136
|
-
this.render();
|
|
137
|
-
if (focused) this.#refocus(focused, focusedValue);
|
|
138
|
-
}
|
|
139
|
-
#hold(press) {
|
|
140
|
-
this.#press = press;
|
|
141
|
-
for (const segment of this.#segments()) segment.toggleAttribute("data-sonic-pressed", segment === press?.segment);
|
|
142
|
-
}
|
|
143
|
-
#keyTarget(segment, key) {
|
|
144
|
-
const segments = this.#segments();
|
|
145
|
-
if (key === "Home") return segments[0];
|
|
146
|
-
if (key === "End") return segments.at(-1);
|
|
147
|
-
const step = keySteps.get(key);
|
|
148
|
-
if (step === void 0) return void 0;
|
|
149
|
-
return segments[(segments.indexOf(segment) + step + segments.length) % segments.length];
|
|
150
|
-
}
|
|
151
|
-
#refocus(focused, value) {
|
|
152
|
-
const segments = this.#segments();
|
|
153
|
-
const next = segments.find((segment) => segmentValue(segment) === value) ?? segments.find((segment) => segment.tabIndex === 0);
|
|
154
|
-
if (next !== focused) next?.focus();
|
|
155
|
-
}
|
|
156
|
-
#segmentOf(target) {
|
|
157
|
-
if (!(target instanceof Element)) return void 0;
|
|
158
|
-
const segment = target.closest(".sonic-segmented-segment");
|
|
159
|
-
return segment instanceof HTMLButtonElement && segment.parentElement === this.#segmented ? segment : void 0;
|
|
160
|
-
}
|
|
161
|
-
#segments() {
|
|
162
|
-
return [...this.#segmented.children].filter((child) => child instanceof HTMLButtonElement);
|
|
14
|
+
super.connect(signal);
|
|
15
|
+
this.checkStyles(this.group, "segmented.css");
|
|
163
16
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (next === void 0 || next === this.#value) return;
|
|
167
|
-
this.value = next;
|
|
168
|
-
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
17
|
+
renderOption() {
|
|
18
|
+
return renderOption();
|
|
169
19
|
}
|
|
170
20
|
};
|
|
171
21
|
|