@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.
Files changed (247) hide show
  1. package/README.md +3 -20
  2. package/dist/default/dat.js +51 -0
  3. package/dist/default/define/button.js +7 -0
  4. package/dist/default/define/envelope.js +7 -0
  5. package/dist/default/define/split.js +7 -0
  6. package/dist/default/define/switch.js +7 -0
  7. package/dist/default/define/waveform.js +7 -0
  8. package/dist/default/define/wavestrip.js +7 -0
  9. package/dist/default/define/xy.js +7 -0
  10. package/dist/default/define.js +7 -1
  11. package/dist/default/elements/button.js +165 -0
  12. package/dist/default/elements/dial.js +16 -22
  13. package/dist/default/elements/envelope.js +383 -0
  14. package/dist/default/elements/meter.js +155 -63
  15. package/dist/default/elements/number.js +22 -9
  16. package/dist/default/elements/radio-group.js +183 -0
  17. package/dist/default/elements/readout-claim.js +81 -0
  18. package/dist/default/elements/readout.js +35 -0
  19. package/dist/default/elements/segmented.js +9 -159
  20. package/dist/default/elements/slider.js +42 -51
  21. package/dist/default/elements/sonic-element.js +69 -31
  22. package/dist/default/elements/split.js +249 -0
  23. package/dist/default/elements/switch.js +273 -0
  24. package/dist/default/elements/value-element.js +694 -0
  25. package/dist/default/elements/{range-entry.js → value-entry.js} +3 -3
  26. package/dist/default/elements/wave-element.js +80 -0
  27. package/dist/default/elements/waveform.js +328 -0
  28. package/dist/default/elements/wavestrip.js +185 -0
  29. package/dist/default/elements/xy.js +487 -0
  30. package/dist/default/index.js +10 -2
  31. package/dist/default/lib/adsr-shape.js +113 -0
  32. package/dist/default/lib/ballistics.js +55 -0
  33. package/dist/default/lib/canvas-surface.js +203 -0
  34. package/dist/default/lib/copy-node.js +32 -2
  35. package/dist/default/lib/crossfade.js +22 -0
  36. package/dist/default/lib/distribute.js +215 -0
  37. package/dist/default/lib/drag-step.js +99 -0
  38. package/dist/default/lib/envelope-curve.js +9 -0
  39. package/dist/default/lib/field.js +94 -0
  40. package/dist/default/lib/focus-by-pointer.js +28 -0
  41. package/dist/default/lib/frame-timeline.js +63 -0
  42. package/dist/default/lib/hold.js +38 -0
  43. package/dist/default/lib/marker-band.js +80 -0
  44. package/dist/default/lib/marker-rider.js +120 -0
  45. package/dist/default/lib/math.js +17 -3
  46. package/dist/default/lib/modifier-press.js +11 -0
  47. package/dist/default/lib/number-list.js +6 -6
  48. package/dist/default/lib/pointer-drag.js +46 -0
  49. package/dist/default/lib/render.js +20 -5
  50. package/dist/default/lib/resample-peaks.js +46 -0
  51. package/dist/default/lib/strip-scene.js +48 -0
  52. package/dist/default/lib/taper.js +23 -17
  53. package/dist/default/lib/time-regions.js +8 -0
  54. package/dist/default/lib/tracking-clock.js +35 -0
  55. package/dist/default/lib/value-mapping.js +115 -0
  56. package/dist/default/lib/waveform-buckets.js +41 -0
  57. package/dist/default/lib/write-attribute.js +1 -0
  58. package/dist/dev/dat.d.ts +19 -0
  59. package/dist/dev/dat.js +52 -0
  60. package/dist/dev/dat.js.map +1 -0
  61. package/dist/dev/define/button.d.ts +2 -0
  62. package/dist/dev/define/button.js +8 -0
  63. package/dist/dev/define/button.js.map +1 -0
  64. package/dist/dev/define/dial.js.map +1 -1
  65. package/dist/dev/define/envelope.d.ts +2 -0
  66. package/dist/dev/define/envelope.js +8 -0
  67. package/dist/dev/define/envelope.js.map +1 -0
  68. package/dist/dev/define/meter.js.map +1 -1
  69. package/dist/dev/define/number.js.map +1 -1
  70. package/dist/dev/define/segmented.js.map +1 -1
  71. package/dist/dev/define/slider.js.map +1 -1
  72. package/dist/dev/define/split.d.ts +2 -0
  73. package/dist/dev/define/split.js +8 -0
  74. package/dist/dev/define/split.js.map +1 -0
  75. package/dist/dev/define/switch.d.ts +2 -0
  76. package/dist/dev/define/switch.js +8 -0
  77. package/dist/dev/define/switch.js.map +1 -0
  78. package/dist/dev/define/waveform.d.ts +2 -0
  79. package/dist/dev/define/waveform.js +8 -0
  80. package/dist/dev/define/waveform.js.map +1 -0
  81. package/dist/dev/define/wavestrip.d.ts +2 -0
  82. package/dist/dev/define/wavestrip.js +8 -0
  83. package/dist/dev/define/wavestrip.js.map +1 -0
  84. package/dist/dev/define/xy.d.ts +2 -0
  85. package/dist/dev/define/xy.js +8 -0
  86. package/dist/dev/define/xy.js.map +1 -0
  87. package/dist/dev/define.d.ts +15 -3
  88. package/dist/dev/define.js +7 -1
  89. package/dist/dev/elements/{key.d.ts → button.d.ts} +8 -6
  90. package/dist/dev/elements/button.js +166 -0
  91. package/dist/dev/elements/button.js.map +1 -0
  92. package/dist/dev/elements/dial.d.ts +6 -4
  93. package/dist/dev/elements/dial.js +16 -22
  94. package/dist/dev/elements/dial.js.map +1 -1
  95. package/dist/dev/elements/envelope.d.ts +36 -0
  96. package/dist/dev/elements/envelope.js +388 -0
  97. package/dist/dev/elements/envelope.js.map +1 -0
  98. package/dist/dev/elements/form-element.d.ts +1 -1
  99. package/dist/dev/elements/form-element.js.map +1 -1
  100. package/dist/dev/elements/meter.d.ts +11 -6
  101. package/dist/dev/elements/meter.js +155 -63
  102. package/dist/dev/elements/meter.js.map +1 -1
  103. package/dist/dev/elements/number.d.ts +7 -3
  104. package/dist/dev/elements/number.js +22 -9
  105. package/dist/dev/elements/number.js.map +1 -1
  106. package/dist/dev/elements/radio-group.d.ts +25 -0
  107. package/dist/dev/elements/radio-group.js +184 -0
  108. package/dist/dev/elements/radio-group.js.map +1 -0
  109. package/dist/dev/elements/readout-claim.js +82 -0
  110. package/dist/dev/elements/readout-claim.js.map +1 -0
  111. package/dist/dev/elements/readout.js +36 -0
  112. package/dist/dev/elements/readout.js.map +1 -0
  113. package/dist/dev/elements/segmented.d.ts +4 -10
  114. package/dist/dev/elements/segmented.js +9 -159
  115. package/dist/dev/elements/segmented.js.map +1 -1
  116. package/dist/dev/elements/slider.d.ts +6 -6
  117. package/dist/dev/elements/slider.js +42 -51
  118. package/dist/dev/elements/slider.js.map +1 -1
  119. package/dist/dev/elements/sonic-element.d.ts +6 -3
  120. package/dist/dev/elements/sonic-element.js +119 -40
  121. package/dist/dev/elements/sonic-element.js.map +1 -1
  122. package/dist/dev/elements/split.d.ts +21 -0
  123. package/dist/dev/elements/split.js +258 -0
  124. package/dist/dev/elements/split.js.map +1 -0
  125. package/dist/dev/elements/switch.d.ts +35 -0
  126. package/dist/dev/elements/switch.js +274 -0
  127. package/dist/dev/elements/switch.js.map +1 -0
  128. package/dist/dev/elements/value-element.d.ts +84 -0
  129. package/dist/dev/elements/value-element.js +695 -0
  130. package/dist/dev/elements/value-element.js.map +1 -0
  131. package/dist/dev/elements/{range-entry.js → value-entry.js} +4 -4
  132. package/dist/dev/elements/value-entry.js.map +1 -0
  133. package/dist/dev/elements/wave-element.d.ts +37 -0
  134. package/dist/dev/elements/wave-element.js +81 -0
  135. package/dist/dev/elements/wave-element.js.map +1 -0
  136. package/dist/dev/elements/waveform.d.ts +55 -0
  137. package/dist/dev/elements/waveform.js +329 -0
  138. package/dist/dev/elements/waveform.js.map +1 -0
  139. package/dist/dev/elements/wavestrip.d.ts +51 -0
  140. package/dist/dev/elements/wavestrip.js +186 -0
  141. package/dist/dev/elements/wavestrip.js.map +1 -0
  142. package/dist/dev/elements/xy.d.ts +62 -0
  143. package/dist/dev/elements/xy.js +488 -0
  144. package/dist/dev/elements/xy.js.map +1 -0
  145. package/dist/dev/index.d.ts +11 -2
  146. package/dist/dev/index.js +10 -2
  147. package/dist/dev/lib/adsr-shape.js +114 -0
  148. package/dist/dev/lib/adsr-shape.js.map +1 -0
  149. package/dist/dev/lib/ballistics.js +56 -0
  150. package/dist/dev/lib/ballistics.js.map +1 -0
  151. package/dist/dev/lib/canvas-surface.d.ts +26 -0
  152. package/dist/dev/lib/canvas-surface.js +204 -0
  153. package/dist/dev/lib/canvas-surface.js.map +1 -0
  154. package/dist/dev/lib/copy-node.js +32 -2
  155. package/dist/dev/lib/copy-node.js.map +1 -1
  156. package/dist/dev/lib/crossfade.d.ts +12 -0
  157. package/dist/dev/lib/crossfade.js +23 -0
  158. package/dist/dev/lib/crossfade.js.map +1 -0
  159. package/dist/dev/lib/distribute.d.ts +4 -0
  160. package/dist/dev/lib/distribute.js +216 -0
  161. package/dist/dev/lib/distribute.js.map +1 -0
  162. package/dist/dev/lib/drag-step.js +100 -0
  163. package/dist/dev/lib/drag-step.js.map +1 -0
  164. package/dist/dev/lib/envelope-curve.d.ts +4 -0
  165. package/dist/dev/lib/envelope-curve.js +10 -0
  166. package/dist/dev/lib/envelope-curve.js.map +1 -0
  167. package/dist/dev/lib/field.d.ts +4 -0
  168. package/dist/dev/lib/field.js +95 -0
  169. package/dist/dev/lib/field.js.map +1 -0
  170. package/dist/dev/lib/focus-by-pointer.js +29 -0
  171. package/dist/dev/lib/focus-by-pointer.js.map +1 -0
  172. package/dist/dev/lib/frame-timeline.js +64 -0
  173. package/dist/dev/lib/frame-timeline.js.map +1 -0
  174. package/dist/dev/lib/hold.js +39 -0
  175. package/dist/dev/lib/hold.js.map +1 -0
  176. package/dist/dev/lib/marker-band.js +81 -0
  177. package/dist/dev/lib/marker-band.js.map +1 -0
  178. package/dist/dev/lib/marker-rider.js +121 -0
  179. package/dist/dev/lib/marker-rider.js.map +1 -0
  180. package/dist/dev/lib/math.js +17 -3
  181. package/dist/dev/lib/math.js.map +1 -1
  182. package/dist/dev/lib/modifier-press.js +12 -0
  183. package/dist/dev/lib/modifier-press.js.map +1 -0
  184. package/dist/dev/lib/number-list.js +6 -6
  185. package/dist/dev/lib/number-list.js.map +1 -1
  186. package/dist/dev/lib/pointer-drag.js +47 -0
  187. package/dist/dev/lib/pointer-drag.js.map +1 -0
  188. package/dist/dev/lib/read-px-property.js.map +1 -1
  189. package/dist/dev/lib/render.js +20 -5
  190. package/dist/dev/lib/render.js.map +1 -1
  191. package/dist/dev/lib/resample-peaks.js +47 -0
  192. package/dist/dev/lib/resample-peaks.js.map +1 -0
  193. package/dist/dev/lib/strip-scene.js +49 -0
  194. package/dist/dev/lib/strip-scene.js.map +1 -0
  195. package/dist/dev/lib/taper.js +23 -17
  196. package/dist/dev/lib/taper.js.map +1 -1
  197. package/dist/dev/lib/time-regions.d.ts +4 -0
  198. package/dist/dev/lib/time-regions.js +9 -0
  199. package/dist/dev/lib/time-regions.js.map +1 -0
  200. package/dist/dev/lib/tracking-clock.js +36 -0
  201. package/dist/dev/lib/tracking-clock.js.map +1 -0
  202. package/dist/dev/lib/value-mapping.d.ts +23 -0
  203. package/dist/dev/lib/value-mapping.js +116 -0
  204. package/dist/dev/lib/value-mapping.js.map +1 -0
  205. package/dist/dev/lib/waveform-buckets.d.ts +9 -0
  206. package/dist/dev/lib/waveform-buckets.js +42 -0
  207. package/dist/dev/lib/waveform-buckets.js.map +1 -0
  208. package/dist/dev/lib/write-attribute.js +1 -0
  209. package/dist/dev/lib/write-attribute.js.map +1 -1
  210. package/dist/skins/amber.css +8 -0
  211. package/dist/skins/flat.css +25 -0
  212. package/dist/skins/ivory.css +2 -2
  213. package/dist/skins/lime.css +2 -0
  214. package/dist/skins/slate.css +2 -0
  215. package/dist/styles/button.css +240 -0
  216. package/dist/styles/controls.css +8 -1
  217. package/dist/styles/dial.css +110 -56
  218. package/dist/styles/envelope.css +204 -0
  219. package/dist/styles/led.css +39 -8
  220. package/dist/styles/material.css +343 -100
  221. package/dist/styles/meter.css +41 -52
  222. package/dist/styles/number.css +5 -1
  223. package/dist/styles/panel.css +61 -0
  224. package/dist/styles/ring.css +74 -0
  225. package/dist/styles/segmented.css +46 -51
  226. package/dist/styles/slider.css +177 -40
  227. package/dist/styles/switch.css +386 -0
  228. package/dist/styles/waveform.css +196 -0
  229. package/dist/styles/wavestrip.css +232 -0
  230. package/dist/styles/xy.css +241 -0
  231. package/package.json +15 -6
  232. package/dist/default/define/key.js +0 -7
  233. package/dist/default/elements/key.js +0 -169
  234. package/dist/default/elements/range-element.js +0 -546
  235. package/dist/default/lib/detent.js +0 -17
  236. package/dist/dev/define/key.d.ts +0 -2
  237. package/dist/dev/define/key.js +0 -8
  238. package/dist/dev/define/key.js.map +0 -1
  239. package/dist/dev/elements/key.js +0 -170
  240. package/dist/dev/elements/key.js.map +0 -1
  241. package/dist/dev/elements/range-element.d.ts +0 -64
  242. package/dist/dev/elements/range-element.js +0 -547
  243. package/dist/dev/elements/range-element.js.map +0 -1
  244. package/dist/dev/elements/range-entry.js.map +0 -1
  245. package/dist/dev/lib/detent.js +0 -18
  246. package/dist/dev/lib/detent.js.map +0 -1
  247. package/dist/styles/key.css +0 -242
@@ -1,169 +0,0 @@
1
- import { requireChild, template } 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
-
6
- //#region src/elements/key.ts
7
- const renderKey = template(`
8
- <button class="sonic-key" type="button"><span class="sonic-key-cap"></span></button>
9
- `, HTMLButtonElement);
10
- const holdKeys = /* @__PURE__ */ new Set([" ", "Enter"]);
11
- var SonicKey = class extends SonicFormElement {
12
- static observedAttributes = [
13
- ...SonicFormElement.observedAttributes,
14
- "busy",
15
- "momentary",
16
- "pressed",
17
- "soft-disabled",
18
- "toggle",
19
- "value"
20
- ];
21
- get busy() {
22
- return this.hasAttribute("busy");
23
- }
24
- set busy(isBusy) {
25
- this.reflect("busy", isBusy);
26
- }
27
- get defaultPressed() {
28
- return this.hasAttribute("pressed");
29
- }
30
- set defaultPressed(isPressed) {
31
- this.reflect("pressed", isPressed);
32
- }
33
- get momentary() {
34
- return this.hasAttribute("momentary");
35
- }
36
- set momentary(isMomentary) {
37
- this.reflect("momentary", isMomentary);
38
- }
39
- get pressed() {
40
- return this.#isDirty ? this.#pressed : this.defaultPressed;
41
- }
42
- set pressed(isPressed) {
43
- this.#isDirty = true;
44
- this.#pressed = isPressed === true;
45
- this.render();
46
- }
47
- get softDisabled() {
48
- return this.hasAttribute("soft-disabled");
49
- }
50
- set softDisabled(isSoftDisabled) {
51
- this.reflect("soft-disabled", isSoftDisabled);
52
- }
53
- get toggle() {
54
- return this.hasAttribute("toggle");
55
- }
56
- set toggle(isToggle) {
57
- this.reflect("toggle", isToggle);
58
- }
59
- get value() {
60
- return this.getAttribute("value") ?? "on";
61
- }
62
- set value(value) {
63
- this.reflect("value", value);
64
- }
65
- #holder;
66
- #isDirty = false;
67
- #key = renderKey();
68
- #pressed = false;
69
- attributeChangedCallback(name) {
70
- if (name === "pressed") this.#isDirty = false;
71
- if (this.isDisabled() || this.#isSoftDisabled()) this.#release();
72
- this.render();
73
- }
74
- formResetCallback() {
75
- this.attributeChangedCallback("pressed");
76
- }
77
- activate() {
78
- this.#key.click();
79
- }
80
- connect(signal) {
81
- const key = this.#key;
82
- const cap = requireChild(key, ".sonic-key-cap", HTMLSpanElement);
83
- this.upgradeProperties("busy", "defaultPressed", "momentary", "softDisabled", "toggle", "pressed", "value");
84
- this.mirrorChildren({
85
- control: key,
86
- copy: (originals) => {
87
- cap.replaceChildren(...originals.map((original) => copyNode(original)));
88
- }
89
- }, signal);
90
- this.render();
91
- this.checkStyles(key, "key.css");
92
- signal.addEventListener("abort", () => {
93
- this.#release();
94
- }, { once: true });
95
- key.addEventListener("click", (event) => {
96
- if (this.#isSoftDisabled()) {
97
- event.preventDefault();
98
- event.stopImmediatePropagation();
99
- return;
100
- }
101
- if (!this.toggle) return;
102
- this.pressed = !this.pressed;
103
- this.dispatchEvent(new Event("change", { bubbles: true }));
104
- }, { signal });
105
- this.#bindMomentary(key, signal);
106
- }
107
- focusTarget() {
108
- return this.#key;
109
- }
110
- render() {
111
- const key = this.#key;
112
- key.disabled = this.isDisabled();
113
- writeAttribute(key, "aria-disabled", this.#isSoftDisabled() ? "true" : void 0);
114
- writeAttribute(key, "aria-busy", this.busy ? "true" : void 0);
115
- key.removeAttribute("aria-pressed");
116
- if (this.toggle) key.setAttribute("aria-pressed", String(this.pressed));
117
- this.toggleState("pressed", this.pressed);
118
- this.forwardNaming(key, true);
119
- const submitted = this.toggle && this.pressed ? this.value : null;
120
- this.writeFormValue(submitted, String(this.pressed));
121
- }
122
- restoreState(state) {
123
- if (this.toggle) this.pressed = state === "true";
124
- }
125
- #bindMomentary(key, signal) {
126
- const releasePointer = (event) => {
127
- if (event.pointerId === this.#holder) this.#release();
128
- };
129
- key.addEventListener("pointerdown", (event) => {
130
- if (event.button !== 0 || !this.#isMomentary()) return;
131
- key.setPointerCapture(event.pointerId);
132
- this.#hold(event.pointerId);
133
- }, { signal });
134
- key.addEventListener("lostpointercapture", releasePointer, { signal });
135
- key.addEventListener("pointercancel", releasePointer, { signal });
136
- key.addEventListener("pointerup", releasePointer, { signal });
137
- key.addEventListener("keydown", (event) => {
138
- if (event.repeat || !holdKeys.has(event.key) || !this.#isMomentary()) return;
139
- this.#hold(event.key);
140
- }, { signal });
141
- key.addEventListener("keyup", (event) => {
142
- if (event.key === this.#holder) this.#release();
143
- }, { signal });
144
- key.addEventListener("blur", () => {
145
- if (typeof this.#holder === "string") this.#release();
146
- }, { signal });
147
- }
148
- #hold(holder) {
149
- if (this.isDisabled() || this.#isSoftDisabled() || this.#holder !== void 0) return;
150
- this.#holder = holder;
151
- this.pressed = true;
152
- this.dispatchEvent(new Event("change", { bubbles: true }));
153
- }
154
- #isMomentary() {
155
- return this.momentary && !this.toggle;
156
- }
157
- #isSoftDisabled() {
158
- return this.softDisabled && !this.isDisabled();
159
- }
160
- #release() {
161
- if (this.#holder === void 0) return;
162
- this.#holder = void 0;
163
- this.pressed = false;
164
- this.dispatchEvent(new Event("change", { bubbles: true }));
165
- }
166
- };
167
-
168
- //#endregion
169
- export { SonicKey };
@@ -1,546 +0,0 @@
1
- import { requireChild } from "../lib/render.js";
2
- import { writeAttribute } from "../lib/write-attribute.js";
3
- import { SonicFormElement } from "./form-element.js";
4
- import { RangeEntry } from "./range-entry.js";
5
- import { copyNode } from "../lib/copy-node.js";
6
- import { passDetent } from "../lib/detent.js";
7
- import { clamp, clampUnit, trimFloat, wrapUnit } from "../lib/math.js";
8
- import { nearestEntry, parseNumberList } from "../lib/number-list.js";
9
- import { readPxProperty } from "../lib/read-px-property.js";
10
- import { linearTaper, listTaper, logTaper, skewTaper } from "../lib/taper.js";
11
-
12
- //#region src/elements/range-element.ts
13
- const detentZonePx = 8;
14
- const fineScale = .1;
15
- const keyRevealMs = 1e3;
16
- const pageSteps = 10;
17
- const revealMs = 250;
18
- const revealPx = 4;
19
- const canPopover = "togglePopover" in HTMLElement.prototype;
20
- let instanceCount = 0;
21
- const resetKeys = /* @__PURE__ */ new Set(["Backspace", "Delete"]);
22
- const endKeys = /* @__PURE__ */ new Set(["End", "Home"]);
23
- const keySteps = /* @__PURE__ */ new Map([
24
- ["ArrowDown", -1],
25
- ["ArrowLeft", -1],
26
- ["ArrowRight", 1],
27
- ["ArrowUp", 1],
28
- ["PageDown", -10],
29
- ["PageUp", pageSteps]
30
- ]);
31
- const roleAttributes = [
32
- "aria-disabled",
33
- "aria-orientation",
34
- "aria-valuemax",
35
- "aria-valuemin",
36
- "aria-valuenow",
37
- "aria-valuetext",
38
- "role",
39
- "tabindex"
40
- ];
41
- function scaleValue(mark) {
42
- const text = mark.dataset.sonicValue?.trim();
43
- return text ? Number(text) : NaN;
44
- }
45
- function scaleMark(original) {
46
- const mark = copyNode(original);
47
- if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return [];
48
- if (!Number.isFinite(scaleValue(mark))) return [];
49
- const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
50
- mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
51
- return [mark];
52
- }
53
- var SonicRangeElement = class extends SonicFormElement {
54
- static observedAttributes = [
55
- ...SonicFormElement.observedAttributes,
56
- "detent",
57
- "max",
58
- "midpoint",
59
- "min",
60
- "notched",
61
- "origin",
62
- "readout",
63
- "step",
64
- "taper",
65
- "value",
66
- "values"
67
- ];
68
- get default() {
69
- return this.optionalNumberAttribute("default");
70
- }
71
- set default(value) {
72
- this.reflect("default", value);
73
- }
74
- get detent() {
75
- return this.optionalNumberAttribute("detent");
76
- }
77
- set detent(value) {
78
- this.reflect("detent", value);
79
- }
80
- get dimmed() {
81
- return this.hasAttribute("dimmed");
82
- }
83
- set dimmed(isDimmed) {
84
- this.reflect("dimmed", isDimmed);
85
- }
86
- get doublePress() {
87
- return this.getAttribute("double-press") === "reset" ? "reset" : "type";
88
- }
89
- set doublePress(gesture) {
90
- this.reflect("double-press", gesture);
91
- }
92
- get formatValue() {
93
- return this.#formatValue;
94
- }
95
- set formatValue(format) {
96
- this.#formatValue = format;
97
- this.render();
98
- }
99
- get max() {
100
- return this.numberAttribute("max", 100);
101
- }
102
- set max(value) {
103
- this.reflect("max", value);
104
- }
105
- get midpoint() {
106
- return this.optionalNumberAttribute("midpoint");
107
- }
108
- set midpoint(value) {
109
- this.reflect("midpoint", value);
110
- }
111
- get min() {
112
- return this.numberAttribute("min", 0);
113
- }
114
- set min(value) {
115
- this.reflect("min", value);
116
- }
117
- get notched() {
118
- return this.hasAttribute("notched");
119
- }
120
- set notched(isNotched) {
121
- this.reflect("notched", isNotched);
122
- }
123
- get origin() {
124
- return this.optionalNumberAttribute("origin");
125
- }
126
- set origin(value) {
127
- this.reflect("origin", value);
128
- }
129
- get parseValue() {
130
- return this.#parseValue;
131
- }
132
- set parseValue(parse) {
133
- this.#parseValue = parse;
134
- }
135
- get readout() {
136
- return this.hasAttribute("readout");
137
- }
138
- set readout(isShown) {
139
- this.reflect("readout", isShown);
140
- }
141
- get step() {
142
- return this.numberAttribute("step", 1);
143
- }
144
- set step(value) {
145
- this.reflect("step", value);
146
- }
147
- get taper() {
148
- return this.getAttribute("taper") === "log" ? "log" : "linear";
149
- }
150
- set taper(curve) {
151
- this.reflect("taper", curve);
152
- }
153
- get value() {
154
- return this.#value;
155
- }
156
- set value(next) {
157
- if (this.#drag) return;
158
- this.#write(next);
159
- }
160
- get values() {
161
- return parseNumberList(this.getAttribute("values"));
162
- }
163
- set values(entries) {
164
- this.reflect("values", entries?.join(" "));
165
- }
166
- #drag;
167
- #entries;
168
- #entry;
169
- #formatValue;
170
- #instance = String(instanceCount += 1);
171
- #isDirty = false;
172
- #keyRevealTimer;
173
- #parseValue;
174
- #readout;
175
- #scale;
176
- #value = 0;
177
- attributeChangedCallback(name) {
178
- this.#entries = parseNumberList(this.getAttribute("values"));
179
- if (name === "value") {
180
- if (this.#drag) return;
181
- this.#isDirty = false;
182
- }
183
- if (name === "disabled" && this.isDisabled()) {
184
- this.#entry?.close(false);
185
- this.#endDrag();
186
- }
187
- this.#value = this.#clamp(this.#isDirty ? this.#value : this.numberAttribute("value", this.min));
188
- this.render();
189
- }
190
- connectedCallback() {
191
- this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "values", "detent", "formatValue", "parseValue", "value");
192
- super.connectedCallback();
193
- }
194
- bindGestures(control, signal, grab) {
195
- const entry = this.#bindEntry(control, signal);
196
- control.addEventListener("pointerdown", (event) => {
197
- if (event.button !== 0 || this.isDisabled() || entry.isOpen) return;
198
- if (event.pointerType === "touch") event.preventDefault();
199
- control.focus();
200
- if (event.metaKey || event.ctrlKey) {
201
- this.#reset();
202
- return;
203
- }
204
- const fromValue = this.#value;
205
- const axis = grab(event);
206
- if (!axis) return;
207
- const startPosition = axis.position(event);
208
- const drag = {
209
- detent: this.#detentHold(control, axis.travelPx),
210
- fromValue,
211
- isRevealed: false,
212
- lastPosition: startPosition,
213
- pointerId: event.pointerId,
214
- position: axis.position,
215
- rawFraction: this.fraction(this.#value),
216
- revealTimer: void 0,
217
- startPosition,
218
- travelPx: axis.travelPx
219
- };
220
- drag.revealTimer = setTimeout(() => {
221
- this.#reveal(drag);
222
- }, revealMs);
223
- this.#drag = drag;
224
- control.setPointerCapture(event.pointerId);
225
- this.toggleState("dragging", true);
226
- }, { signal });
227
- control.addEventListener("pointermove", (event) => {
228
- const drag = this.#drag;
229
- if (event.pointerId !== drag?.pointerId) return;
230
- const position = drag.position(event);
231
- const scale = event.shiftKey ? fineScale : 1;
232
- const next = this.#move(drag, (position - drag.lastPosition) / drag.travelPx * scale);
233
- drag.lastPosition = position;
234
- if (Math.abs(position - drag.startPosition) >= revealPx) this.#reveal(drag);
235
- this.input(next);
236
- }, { signal });
237
- const endDrag = (event) => {
238
- if (event.pointerId === this.#drag?.pointerId) this.#endDrag();
239
- };
240
- control.addEventListener("pointercancel", (event) => {
241
- endDrag(event);
242
- entry.forgetPress();
243
- }, { signal });
244
- control.addEventListener("lostpointercapture", endDrag, { signal });
245
- control.addEventListener("pointerup", (event) => {
246
- if (event.pointerId !== this.#drag?.pointerId) return;
247
- this.#endDrag();
248
- if (!entry.press(event) || this.springTarget() !== void 0) return;
249
- if (this.doublePress === "reset") this.#reset();
250
- else entry.open();
251
- }, { signal });
252
- this.#bindKeys(control, entry, signal);
253
- signal.addEventListener("abort", () => {
254
- this.#endDrag();
255
- this.#hideForKeys();
256
- }, { once: true });
257
- }
258
- bindScale(control, scale, signal) {
259
- this.#scale = scale;
260
- this.mirrorChildren({
261
- control,
262
- copy: (originals) => {
263
- scale.replaceChildren(...originals.flatMap((original) => scaleMark(original)));
264
- this.renderScale();
265
- },
266
- isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]")
267
- }, signal);
268
- }
269
- clampRange(next) {
270
- const min = this.min;
271
- if (!this.wraps()) return clamp(next, min, this.#max());
272
- const range = this.range();
273
- if (range <= 0) return min;
274
- const offset = trimFloat(((next - min) % range + range) % range);
275
- return offset >= range ? min : trimFloat(min + offset);
276
- }
277
- controlRole() {
278
- return "slider";
279
- }
280
- fraction(value) {
281
- return (this.#taper() ?? linearTaper(this.min, this.#max())).position(value);
282
- }
283
- input(next) {
284
- const previous = this.#value;
285
- this.#write(next);
286
- if (this.#value === previous) return false;
287
- this.dispatchEvent(new Event("input", {
288
- bubbles: true,
289
- composed: true
290
- }));
291
- return true;
292
- }
293
- modulationFractions(amount) {
294
- const value = this.fraction(this.#value);
295
- const modulated = this.fraction(this.#value + amount);
296
- return [Math.min(value, modulated), Math.max(value, modulated)];
297
- }
298
- originFraction() {
299
- return this.fraction(this.restValue());
300
- }
301
- positions() {
302
- const positions = this.#entries?.length ?? Math.round(this.range() / this.step) + (this.wraps() ? 0 : 1);
303
- if (!this.notched || !Number.isFinite(positions) || positions < 2) return;
304
- return positions;
305
- }
306
- range() {
307
- return this.#max() - this.min;
308
- }
309
- renderAria(control, orientation) {
310
- this.writeFormValue(String(this.#value), String(this.#value));
311
- if (this.#entry?.isOpen) {
312
- for (const name of roleAttributes) control.removeAttribute(name);
313
- this.forwardNaming(control, false);
314
- this.#renderReadout();
315
- return;
316
- }
317
- const [low, high] = this.#bounds();
318
- control.setAttribute("role", this.controlRole());
319
- control.setAttribute("aria-valuemin", String(low));
320
- control.setAttribute("aria-valuemax", String(high));
321
- control.setAttribute("aria-valuenow", String(this.#value));
322
- if (orientation) control.setAttribute("aria-orientation", orientation);
323
- writeAttribute(control, "aria-valuetext", this.#formatValue?.(this.#value));
324
- this.forwardNaming(control, true);
325
- this.#renderDisabled(control);
326
- this.#renderReadout();
327
- }
328
- renderScale() {
329
- const marks = this.#scale?.children ?? [];
330
- for (const mark of marks) {
331
- if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
332
- const at = String(this.fraction(this.clampRange(scaleValue(mark))));
333
- if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
334
- }
335
- }
336
- restoreState(state) {
337
- this.value = Number(state);
338
- }
339
- restValue() {
340
- return this.origin ?? this.#bounds()[0];
341
- }
342
- springTarget() {}
343
- valueAt(fraction) {
344
- return (this.#taper() ?? linearTaper(this.min, this.#max())).value(fraction);
345
- }
346
- valueText() {
347
- return this.#formatValue?.(this.#value) ?? String(this.#value);
348
- }
349
- wraps() {
350
- return false;
351
- }
352
- #bindEntry(control, signal) {
353
- const input = requireChild(control, "input", HTMLInputElement);
354
- const bubble = control.querySelector("[popover]");
355
- input.id = `sonic-entry-${this.#instance}`;
356
- if (bubble) {
357
- const anchor = `--sonic-readout-${this.#instance}`;
358
- this.#readout = {
359
- bubble,
360
- text: requireChild(bubble, "span", HTMLElement)
361
- };
362
- control.style.setProperty("anchor-name", anchor);
363
- bubble.style.setProperty("position-anchor", anchor);
364
- }
365
- const entry = new RangeEntry(input, {
366
- commit: (text) => {
367
- this.#commit((this.#parseValue ?? Number.parseFloat)(text));
368
- },
369
- text: () => this.#parseValue ? this.valueText() : String(this.#value),
370
- toggle: (isOpen) => {
371
- if (isOpen) this.forwardNaming(input, true);
372
- this.toggleState("editing", isOpen);
373
- this.render();
374
- }
375
- });
376
- this.#entry = entry;
377
- entry.bind(control, signal);
378
- return entry;
379
- }
380
- #bindKeys(control, entry, signal) {
381
- control.addEventListener("keydown", (event) => {
382
- if (this.isDisabled() || event.defaultPrevented || event.target !== control) return;
383
- if (event.key === "Enter") {
384
- if (this.springTarget() !== void 0) return;
385
- event.preventDefault();
386
- entry.open();
387
- return;
388
- }
389
- const next = this.#keyTarget(event.key);
390
- if (next === void 0) return;
391
- event.preventDefault();
392
- this.#commit(next);
393
- this.#revealForKeys();
394
- }, { signal });
395
- control.addEventListener("keyup", (event) => {
396
- if (keySteps.has(event.key) || endKeys.has(event.key)) this.#springBack();
397
- }, { signal });
398
- control.addEventListener("blur", () => {
399
- this.#hideForKeys();
400
- this.#springBack();
401
- }, { signal });
402
- }
403
- #bounds() {
404
- const entries = this.#entries;
405
- return [entries?.[0] ?? this.min, entries?.at(-1) ?? this.#max()];
406
- }
407
- #clamp(next) {
408
- const entries = this.#entries;
409
- if (entries) return nearestEntry(entries, next);
410
- const min = this.min;
411
- const step = this.step;
412
- const stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;
413
- return this.clampRange(trimFloat(stepped));
414
- }
415
- #commit(next) {
416
- if (this.input(next)) this.dispatchEvent(new Event("change", { bubbles: true }));
417
- }
418
- #detentHold(control, travelPx) {
419
- const detent = this.detent;
420
- if (detent === void 0) return void 0;
421
- const value = this.#clamp(detent);
422
- const zonePx = readPxProperty(getComputedStyle(control), "--_sonic-detent-zone", detentZonePx);
423
- return {
424
- place: this.fraction(value),
425
- slack: value === this.#value ? 0 : void 0,
426
- value,
427
- zone: zonePx / travelPx
428
- };
429
- }
430
- #end(step) {
431
- if (!this.wraps()) return this.#bounds()[1];
432
- return this.min + (Math.ceil(this.range() / step) - 1) * step;
433
- }
434
- #endDrag() {
435
- const drag = this.#drag;
436
- if (!drag) return;
437
- clearTimeout(drag.revealTimer);
438
- this.#drag = void 0;
439
- this.toggleState("dragging", false);
440
- this.#renderReadout();
441
- if (this.#value !== drag.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
442
- this.#springBack();
443
- }
444
- #hideForKeys() {
445
- clearTimeout(this.#keyRevealTimer);
446
- if (this.#keyRevealTimer === void 0) return;
447
- this.#keyRevealTimer = void 0;
448
- this.#renderReadout();
449
- }
450
- #isValueShown() {
451
- return (this.#drag?.isRevealed === true || this.#keyRevealTimer !== void 0) && this.readout;
452
- }
453
- #keyTarget(key) {
454
- if (key === "Home") return this.#bounds()[0];
455
- const step = this.step > 0 ? this.step : this.range() / 100;
456
- if (key === "End") return this.#end(step);
457
- if (resetKeys.has(key)) return this.default;
458
- const steps = keySteps.get(key);
459
- if (steps === void 0) return void 0;
460
- return this.#stopAtDetent(this.#stepBy(steps, step));
461
- }
462
- #max() {
463
- return Math.max(this.min, this.max);
464
- }
465
- #move(drag, delta) {
466
- const { detent } = drag;
467
- const to = drag.rawFraction + delta;
468
- const next = detent ? passDetent(detent, drag.rawFraction, to) : to;
469
- if (detent && next === void 0) {
470
- drag.rawFraction = detent.place;
471
- return detent.value;
472
- }
473
- const place = next ?? to;
474
- drag.rawFraction = this.wraps() ? wrapUnit(place) : clampUnit(place);
475
- return this.valueAt(drag.rawFraction);
476
- }
477
- #renderDisabled(control) {
478
- if (this.isDisabled()) {
479
- control.setAttribute("aria-disabled", "true");
480
- control.removeAttribute("tabindex");
481
- return;
482
- }
483
- control.removeAttribute("aria-disabled");
484
- control.setAttribute("tabindex", "0");
485
- }
486
- #renderReadout() {
487
- const readout = this.#readout;
488
- if (!readout || !canPopover || !this.isConnected) return;
489
- const isEditing = this.#entry?.isOpen === true;
490
- readout.text.hidden = isEditing;
491
- if (!isEditing) readout.text.textContent = this.valueText();
492
- readout.bubble.togglePopover(isEditing || this.#isValueShown());
493
- }
494
- #reset() {
495
- this.#commit(this.default ?? NaN);
496
- }
497
- #reveal(drag) {
498
- if (drag.isRevealed || this.#drag !== drag) return;
499
- drag.isRevealed = true;
500
- this.#renderReadout();
501
- }
502
- #revealForKeys() {
503
- clearTimeout(this.#keyRevealTimer);
504
- this.#keyRevealTimer = setTimeout(() => {
505
- this.#hideForKeys();
506
- }, keyRevealMs);
507
- this.#renderReadout();
508
- }
509
- #springBack() {
510
- const target = this.springTarget();
511
- if (target !== void 0) this.#commit(target);
512
- }
513
- #stepBy(steps, step) {
514
- const entries = this.#entries;
515
- if (entries) return entries[clamp(entries.indexOf(this.#value) + steps, 0, entries.length - 1)] ?? this.#value;
516
- const taper = this.#taper();
517
- if (!taper) return this.#value + steps * step;
518
- const next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));
519
- return next === this.#value ? this.#value + Math.sign(steps) * step : next;
520
- }
521
- #stopAtDetent(next) {
522
- const detent = this.detent;
523
- if (detent === void 0) return next;
524
- const value = this.#clamp(detent);
525
- return (this.#value - value) * (next - value) < 0 ? value : next;
526
- }
527
- #taper() {
528
- const entries = this.#entries;
529
- if (entries) return listTaper(entries);
530
- if (this.notched || this.wraps()) return void 0;
531
- const min = this.min;
532
- const max = this.#max();
533
- return (this.taper === "log" ? logTaper(min, max) : void 0) ?? skewTaper(min, max, this.midpoint ?? NaN);
534
- }
535
- #write(next) {
536
- if (!Number.isFinite(next)) return;
537
- this.#isDirty = true;
538
- const clamped = this.#clamp(next);
539
- if (clamped === this.#value) return;
540
- this.#value = clamped;
541
- this.render();
542
- }
543
- };
544
-
545
- //#endregion
546
- export { SonicRangeElement };