@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
@@ -0,0 +1,694 @@
1
+ import { clamp, toNumber } from "../lib/math.js";
2
+ import { placeChildren, requireChild } from "../lib/render.js";
3
+ import { writeAttribute } from "../lib/write-attribute.js";
4
+ import { SonicFormElement } from "./form-element.js";
5
+ import { copyNode } from "../lib/copy-node.js";
6
+ import { ReadoutClaim } from "./readout-claim.js";
7
+ import { Readout } from "./readout.js";
8
+ import { ValueEntry } from "./value-entry.js";
9
+ import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
10
+ import { focusByPointer } from "../lib/focus-by-pointer.js";
11
+ import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
12
+ import { parseNumberList } from "../lib/number-list.js";
13
+ import { bindDrag } from "../lib/pointer-drag.js";
14
+ import { readPxProperty } from "../lib/read-px-property.js";
15
+ import { readRegions } from "../lib/time-regions.js";
16
+ import { resetKeys, valueMapping } from "../lib/value-mapping.js";
17
+
18
+ //#region src/elements/value-element.ts
19
+ const detentZonePx = 8;
20
+ const fallbackTravelPx = 160;
21
+ let instanceCount = 0;
22
+ const proportionAttributes = /* @__PURE__ */ new Set([
23
+ "max",
24
+ "midpoint",
25
+ "min",
26
+ "notched",
27
+ "positions",
28
+ "taper"
29
+ ]);
30
+ const roleAttributes = [
31
+ "aria-disabled",
32
+ "aria-orientation",
33
+ "aria-valuemax",
34
+ "aria-valuemin",
35
+ "aria-valuenow",
36
+ "aria-valuetext",
37
+ "role",
38
+ "tabindex"
39
+ ];
40
+ function scaleValue(mark) {
41
+ const text = mark.dataset.sonicValue?.trim();
42
+ return text ? Number(text) : NaN;
43
+ }
44
+ function scaleMark(original) {
45
+ const mark = copyNode(original);
46
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return void 0;
47
+ if (!Number.isFinite(scaleValue(mark))) return void 0;
48
+ const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
49
+ mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
50
+ return mark;
51
+ }
52
+ let link;
53
+ function linkValue(element) {
54
+ return link(element);
55
+ }
56
+ var SonicValueElement = class extends SonicFormElement {
57
+ static observedAttributes = [
58
+ ...SonicFormElement.observedAttributes,
59
+ "detent",
60
+ "max",
61
+ "midpoint",
62
+ "min",
63
+ "notched",
64
+ "origin",
65
+ "readout",
66
+ "step",
67
+ "taper",
68
+ "value",
69
+ "positions"
70
+ ];
71
+ static {
72
+ link = (element) => ({
73
+ input: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),
74
+ isDisabled: () => element.isDisabled(),
75
+ isHeld: () => element.#isHeld(),
76
+ land: (resolve) => {
77
+ element.#land = resolve;
78
+ },
79
+ limit: (bounds) => {
80
+ element.#limit = bounds;
81
+ },
82
+ mapping: () => element.mapping(),
83
+ value: () => element.#value,
84
+ watch: (listener) => {
85
+ element.#watchers.add(listener);
86
+ return () => {
87
+ element.#watchers.delete(listener);
88
+ };
89
+ }
90
+ });
91
+ }
92
+ get buffered() {
93
+ return this.#buffered.map(([start, end]) => [start, end]);
94
+ }
95
+ set buffered(regions) {
96
+ this.#buffered = regions ? readRegions(regions).toSorted((first, second) => first[0] - second[0]) : [];
97
+ this.render();
98
+ }
99
+ get default() {
100
+ return this.optionalNumberAttribute("default");
101
+ }
102
+ set default(value) {
103
+ this.reflect("default", value);
104
+ }
105
+ get detent() {
106
+ return this.optionalNumberAttribute("detent");
107
+ }
108
+ set detent(value) {
109
+ this.reflect("detent", value);
110
+ }
111
+ get dimmed() {
112
+ return this.hasAttribute("dimmed");
113
+ }
114
+ set dimmed(isDimmed) {
115
+ this.reflect("dimmed", isDimmed);
116
+ }
117
+ get doublePress() {
118
+ return this.getAttribute("double-press") === "reset" ? "reset" : "entry";
119
+ }
120
+ set doublePress(gesture) {
121
+ this.reflect("double-press", gesture);
122
+ }
123
+ get formatSpokenValue() {
124
+ return this.#formatSpokenValue;
125
+ }
126
+ set formatSpokenValue(format) {
127
+ this.#formatSpokenValue = format;
128
+ this.render();
129
+ }
130
+ get formatValue() {
131
+ return this.#formatValue;
132
+ }
133
+ set formatValue(format) {
134
+ this.#formatValue = format;
135
+ this.render();
136
+ }
137
+ get keyStep() {
138
+ return this.optionalNumberAttribute("key-step");
139
+ }
140
+ set keyStep(value) {
141
+ this.reflect("key-step", value);
142
+ }
143
+ get max() {
144
+ return this.numberAttribute("max", 100);
145
+ }
146
+ set max(value) {
147
+ this.reflect("max", value);
148
+ }
149
+ get midpoint() {
150
+ return this.optionalNumberAttribute("midpoint");
151
+ }
152
+ set midpoint(value) {
153
+ this.reflect("midpoint", value);
154
+ }
155
+ get min() {
156
+ return this.numberAttribute("min", 0);
157
+ }
158
+ set min(value) {
159
+ this.reflect("min", value);
160
+ }
161
+ get notched() {
162
+ return this.hasAttribute("notched");
163
+ }
164
+ set notched(isNotched) {
165
+ this.reflect("notched", isNotched);
166
+ }
167
+ get origin() {
168
+ return this.optionalNumberAttribute("origin");
169
+ }
170
+ set origin(value) {
171
+ this.reflect("origin", value);
172
+ }
173
+ get parseValue() {
174
+ return this.#parseValue;
175
+ }
176
+ set parseValue(parse) {
177
+ this.#parseValue = parse;
178
+ }
179
+ get positions() {
180
+ return parseNumberList(this.getAttribute("positions"));
181
+ }
182
+ set positions(positions) {
183
+ this.reflect("positions", positions?.join(" "));
184
+ }
185
+ get readout() {
186
+ return this.hasAttribute("readout");
187
+ }
188
+ set readout(isShown) {
189
+ this.reflect("readout", isShown);
190
+ }
191
+ get step() {
192
+ return this.numberAttribute("step", 1);
193
+ }
194
+ set step(value) {
195
+ this.reflect("step", value);
196
+ }
197
+ get taper() {
198
+ return this.getAttribute("taper") === "log" ? "log" : "linear";
199
+ }
200
+ set taper(curve) {
201
+ this.reflect("taper", curve);
202
+ }
203
+ get value() {
204
+ return this.#value;
205
+ }
206
+ set value(next) {
207
+ if (next === void 0 || next === null) {
208
+ this.formResetCallback();
209
+ return;
210
+ }
211
+ const value = toNumber(next);
212
+ if (this.#isHeld()) {
213
+ this.heldWrite(value);
214
+ return;
215
+ }
216
+ this.#write(value);
217
+ if (Number.isFinite(value)) this.#asked = value;
218
+ }
219
+ get valueText() {
220
+ return this.#textFor(this.#value);
221
+ }
222
+ #asked;
223
+ #buffered = [];
224
+ #claim = new ReadoutClaim(() => {
225
+ this.#renderHold();
226
+ });
227
+ #entry;
228
+ #escapeWatch;
229
+ #formatSpokenValue;
230
+ #formatValue;
231
+ #instance = String(instanceCount += 1);
232
+ #isModulated = false;
233
+ #keyScrub;
234
+ #land;
235
+ #limit;
236
+ #mapping;
237
+ #modulationValue;
238
+ #parseValue;
239
+ #pointerDrag;
240
+ #positions;
241
+ #readout;
242
+ #scaleMarks;
243
+ #value = 0;
244
+ #watchers = /* @__PURE__ */ new Set();
245
+ attributeChangedCallback(name) {
246
+ this.#mapping = void 0;
247
+ this.#positions = parseNumberList(this.getAttribute("positions"));
248
+ if (name === "value") {
249
+ if (this.#isHeld()) {
250
+ this.heldWrite(this.numberAttribute("value", this.min));
251
+ return;
252
+ }
253
+ this.#asked = void 0;
254
+ }
255
+ if (name === "disabled" && this.isDisabled()) this.#endHolds();
256
+ this.#value = this.mapping().snap(this.#asked ?? this.numberAttribute("value", this.min));
257
+ if (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();
258
+ this.render();
259
+ }
260
+ connectedCallback() {
261
+ this.upgradeProperties("buffered", "default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
262
+ super.connectedCallback();
263
+ }
264
+ bindGestures(control, signal, grab) {
265
+ const entry = this.#bindEntry(control, signal);
266
+ this.#pointerDrag = bindDrag(control, {
267
+ cancel: (drag) => {
268
+ if (drag.isKeptOnCancel !== true) this.input(drag.fromValue);
269
+ },
270
+ grab: (event) => {
271
+ if (isMenuPress(event) || this.isDisabled() || entry.isOpen) return;
272
+ this.toggleState("springing", false);
273
+ if (event.pointerType === "touch") event.preventDefault();
274
+ focusByPointer(control);
275
+ if (isResetPress(event)) {
276
+ this.#reset();
277
+ return;
278
+ }
279
+ const fromValue = this.#value;
280
+ const axis = grab(event);
281
+ if (!axis) return;
282
+ const drag = {
283
+ ...axis,
284
+ fromValue,
285
+ isOutside: false,
286
+ isRevealed: false,
287
+ revealTimer: void 0,
288
+ state: this.#startDrag(control, axis, event)
289
+ };
290
+ drag.revealTimer = setTimeout(() => {
291
+ this.#reveal(drag);
292
+ }, 250);
293
+ return drag;
294
+ },
295
+ lift: (drag, event) => {
296
+ const tap = drag.state.isEngaged ? void 0 : this.tapTarget();
297
+ if (tap !== void 0) {
298
+ entry.forgetPress();
299
+ this.#commit(tap);
300
+ return;
301
+ }
302
+ if (!entry.press(event) || this.springTarget() !== void 0) return;
303
+ if (this.doublePress === "reset") this.#reset();
304
+ else entry.open();
305
+ },
306
+ move: (drag, event) => {
307
+ this.#dragTo(drag, event);
308
+ },
309
+ release: (drag) => {
310
+ this.#releaseDrag(drag);
311
+ },
312
+ toggle: (isDragging) => {
313
+ this.toggleState("dragging", isDragging);
314
+ }
315
+ }, signal);
316
+ control.addEventListener("pointercancel", () => {
317
+ entry.forgetPress();
318
+ }, { signal });
319
+ this.#bindKeys(control, entry, signal);
320
+ signal.addEventListener("abort", () => {
321
+ this.#endKeyScrub();
322
+ this.#concealKeyReveal();
323
+ }, { once: true });
324
+ }
325
+ bindScale(control, marks, signal) {
326
+ this.#scaleMarks = marks;
327
+ this.mirrorChildren({
328
+ control,
329
+ copy: scaleMark,
330
+ isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
331
+ isPassed: (child) => child instanceof Element && child.matches(".sonic-led"),
332
+ place: (copies) => {
333
+ placeChildren(marks, copies);
334
+ this.#renderScale();
335
+ }
336
+ }, signal);
337
+ }
338
+ controlOrientation() {}
339
+ controlRole() {
340
+ return "slider";
341
+ }
342
+ heldFrom() {
343
+ return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
344
+ }
345
+ heldWrite(_next) {}
346
+ holdChanged() {}
347
+ hoverReadout(value) {
348
+ this.#claim.hover(value);
349
+ this.#renderReadout();
350
+ this.#watchEscape(value !== void 0);
351
+ }
352
+ input(next) {
353
+ const previous = this.#value;
354
+ this.#write(this.#limit ? clamp(next, ...this.#limit) : next);
355
+ if (this.#value === previous) return false;
356
+ this.dispatchEvent(new Event("input", {
357
+ bubbles: true,
358
+ composed: true
359
+ }));
360
+ return true;
361
+ }
362
+ isRevealed() {
363
+ return this.#claim.isRevealed;
364
+ }
365
+ isWrapping() {
366
+ return false;
367
+ }
368
+ mapping() {
369
+ if (this.#mapping) return this.#mapping;
370
+ const { detent, midpoint } = this;
371
+ this.#mapping = valueMapping({
372
+ ...detent === void 0 ? {} : { detent },
373
+ ...this.#positions ? { positions: this.#positions } : {},
374
+ max: this.max,
375
+ ...midpoint === void 0 ? {} : { midpoint },
376
+ isNotched: this.notched,
377
+ isWrapping: this.isWrapping(),
378
+ min: this.min,
379
+ step: this.step,
380
+ taper: this.taper
381
+ });
382
+ return this.#mapping;
383
+ }
384
+ originValue() {
385
+ return this.origin ?? this.mapping().bounds[0];
386
+ }
387
+ proportionsChanged() {}
388
+ readModulationValue() {
389
+ return this.#modulationValue;
390
+ }
391
+ readoutValue() {
392
+ return this.#claim.shown(this.#value, this.readout).value ?? this.#value;
393
+ }
394
+ render() {
395
+ if (this.isBound()) this.#renderControl();
396
+ for (const listener of this.#watchers) listener();
397
+ }
398
+ restoreState(state) {
399
+ this.value = Number(state);
400
+ }
401
+ scrubsKeyRepeat() {
402
+ return false;
403
+ }
404
+ springTarget() {}
405
+ tapTarget() {}
406
+ travelPx(control, property) {
407
+ return Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));
408
+ }
409
+ writeModulationValue(control, prefix, next) {
410
+ const value = next === void 0 || next === null ? void 0 : toNumber(next);
411
+ if (value !== void 0 && !Number.isFinite(value)) return;
412
+ this.#modulationValue = value;
413
+ if (this.isBound()) this.#writeModulationProportion(control, prefix);
414
+ }
415
+ writeProportions(control, prefix, modulation) {
416
+ const { style } = control;
417
+ const mapping = this.mapping();
418
+ const value = mapping.proportionOf(this.#value);
419
+ const reach = mapping.proportionOf(this.#value + modulation);
420
+ const { positionCount } = mapping;
421
+ style.setProperty(`--_sonic-${prefix}-value`, String(value));
422
+ style.setProperty(`--_sonic-${prefix}-origin`, String(mapping.proportionOf(this.originValue())));
423
+ style.setProperty(`--_sonic-${prefix}-modulation-from`, String(Math.min(value, reach)));
424
+ style.setProperty(`--_sonic-${prefix}-modulation-to`, String(Math.max(value, reach)));
425
+ if (positionCount === void 0) style.removeProperty(`--_sonic-${prefix}-position-count`);
426
+ else style.setProperty(`--_sonic-${prefix}-position-count`, String(positionCount));
427
+ this.#writeModulationProportion(control, prefix);
428
+ }
429
+ #bindEntry(control, signal) {
430
+ const input = requireChild(control, "input", HTMLInputElement);
431
+ const bubble = control.querySelector("[popover]");
432
+ input.id = `sonic-entry-${this.#instance}`;
433
+ if (bubble) this.#readout = {
434
+ anchor: control,
435
+ bubble: new Readout(bubble)
436
+ };
437
+ const entry = new ValueEntry(input, {
438
+ commit: (text) => {
439
+ this.#commit((this.#parseValue ?? Number.parseFloat)(text));
440
+ },
441
+ text: () => this.#parseValue ? this.valueText : String(this.#value),
442
+ toggle: (isOpen) => {
443
+ this.#claim.edit(isOpen);
444
+ if (isOpen) this.forwardNaming(input, true);
445
+ this.toggleState("editing", isOpen);
446
+ this.render();
447
+ }
448
+ });
449
+ this.#entry = entry;
450
+ entry.bind(control, signal);
451
+ return entry;
452
+ }
453
+ #bindKeys(control, entry, signal) {
454
+ control.addEventListener("keydown", (event) => {
455
+ if (this.isDisabled() || event.defaultPrevented || event.target !== control) return;
456
+ this.toggleState("springing", false);
457
+ if (event.key === "Enter") {
458
+ if (this.springTarget() !== void 0) return;
459
+ event.preventDefault();
460
+ entry.open();
461
+ return;
462
+ }
463
+ const next = this.#keyTarget(event.key);
464
+ if (next === void 0) return;
465
+ event.preventDefault();
466
+ this.#keyTo(event, next);
467
+ this.#claim.reveal("keys");
468
+ this.#renderHold();
469
+ }, { signal });
470
+ control.addEventListener("keyup", (event) => {
471
+ const isMeta = event.key === "Meta";
472
+ if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
473
+ if (isMeta && this.#dragging()) return;
474
+ if (isMeta || this.mapping().keyTarget(event.key, this.#value) !== void 0) this.#springBack();
475
+ }, { signal });
476
+ control.addEventListener("blur", () => {
477
+ this.#endKeyScrub();
478
+ this.#concealKeyReveal();
479
+ this.#springBack();
480
+ }, { signal });
481
+ }
482
+ #commit(next, direction = 0) {
483
+ const landed = Number.isFinite(next) ? this.#land?.(next, direction) ?? next : next;
484
+ if (this.input(landed)) this.dispatchEvent(new Event("change", { bubbles: true }));
485
+ }
486
+ #concealKeyReveal() {
487
+ if (this.#claim.conceal("keys")) this.#renderHold();
488
+ }
489
+ #dragging() {
490
+ return this.#pointerDrag?.current();
491
+ }
492
+ #dragTo(drag, event) {
493
+ const isOutside = drag.outside?.(event) === true;
494
+ const wasEngaged = drag.state.isEngaged;
495
+ const mapping = this.mapping();
496
+ const { state, value } = stepDrag(mapping, drag.state, {
497
+ isFine: event.shiftKey,
498
+ isOutside,
499
+ position: drag.position(event)
500
+ });
501
+ drag.state = this.#withinLimit(mapping, state);
502
+ if (value === void 0) return;
503
+ if (!wasEngaged) this.#reveal(drag);
504
+ this.input(isOutside ? drag.fromValue : value);
505
+ if (isOutside === drag.isOutside) return;
506
+ drag.isOutside = isOutside;
507
+ this.#renderDragReveal(drag);
508
+ }
509
+ #endHolds() {
510
+ this.#entry?.close(false);
511
+ this.#pointerDrag?.end();
512
+ this.#endKeyScrub();
513
+ }
514
+ #endKeyScrub() {
515
+ const scrub = this.#keyScrub;
516
+ if (!scrub) return;
517
+ this.#keyScrub = void 0;
518
+ this.#renderHold();
519
+ if (this.#value !== scrub.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
520
+ }
521
+ #isHeld() {
522
+ return this.#dragging() !== void 0 || this.#keyScrub !== void 0;
523
+ }
524
+ #keyTarget(key) {
525
+ if (resetKeys.has(key)) return this.default;
526
+ return this.mapping().keyTarget(key, this.#value, this.keyStep);
527
+ }
528
+ #keyTo(event, next) {
529
+ if (event.repeat && this.scrubsKeyRepeat()) {
530
+ this.#scrubKey(event.key, next);
531
+ return;
532
+ }
533
+ this.#endKeyScrub();
534
+ this.#commit(next, next > this.#value ? 1 : -1);
535
+ }
536
+ #releaseDrag(drag) {
537
+ clearTimeout(drag.revealTimer);
538
+ this.#claim.conceal("drag");
539
+ this.#renderHold();
540
+ if (this.#value !== drag.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
541
+ this.#springBack();
542
+ }
543
+ #renderAria(control, orientation) {
544
+ this.writeFormValue(String(this.#value), String(this.#value));
545
+ if (this.#entry?.isOpen) {
546
+ for (const name of roleAttributes) control.removeAttribute(name);
547
+ this.forwardNaming(control, false);
548
+ this.#renderReadout();
549
+ return;
550
+ }
551
+ const [low, high] = this.mapping().bounds;
552
+ writeAttribute(control, "role", this.controlRole());
553
+ writeAttribute(control, "aria-valuemin", String(low));
554
+ writeAttribute(control, "aria-valuemax", String(high));
555
+ writeAttribute(control, "aria-valuenow", String(this.#value));
556
+ if (orientation) writeAttribute(control, "aria-orientation", orientation);
557
+ writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#value));
558
+ this.forwardNaming(control, true);
559
+ this.#renderDisabled(control);
560
+ this.#renderReadout();
561
+ }
562
+ #renderControl() {
563
+ this.draw();
564
+ this.#renderScale();
565
+ this.toggleState("at-origin", !this.isWrapping() && this.#value === this.mapping().snap(this.originValue()));
566
+ this.#renderAria(this.focusTarget(), this.controlOrientation());
567
+ }
568
+ #renderDisabled(control) {
569
+ const isDisabled = this.isDisabled();
570
+ writeAttribute(control, "aria-disabled", isDisabled ? "true" : void 0);
571
+ writeAttribute(control, "tabindex", isDisabled ? void 0 : "0");
572
+ }
573
+ #renderDragReveal(drag) {
574
+ if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
575
+ else this.#claim.conceal("drag");
576
+ this.#renderHold();
577
+ }
578
+ #renderHold() {
579
+ this.#renderReadout();
580
+ this.holdChanged();
581
+ }
582
+ #renderReadout() {
583
+ const readout = this.#readout;
584
+ if (!readout) return;
585
+ const { isOpen, value } = this.#claim.shown(this.#value, this.readout);
586
+ readout.bubble.show({
587
+ anchor: readout.anchor,
588
+ isOpen,
589
+ text: value === void 0 ? void 0 : this.#textFor(value)
590
+ });
591
+ }
592
+ #renderScale() {
593
+ const marks = this.#scaleMarks?.children ?? [];
594
+ const mapping = this.mapping();
595
+ for (const mark of marks) {
596
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
597
+ const at = String(mapping.proportionOf(scaleValue(mark)));
598
+ if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
599
+ }
600
+ }
601
+ #reset() {
602
+ this.#commit(this.default ?? NaN);
603
+ }
604
+ #reveal(drag) {
605
+ if (drag.isRevealed || this.#dragging() !== drag) return;
606
+ drag.isRevealed = true;
607
+ this.#renderDragReveal(drag);
608
+ }
609
+ #scrubKey(key, next) {
610
+ if (this.#keyScrub?.key !== key) {
611
+ this.#endKeyScrub();
612
+ this.#keyScrub = {
613
+ fromValue: this.#value,
614
+ key
615
+ };
616
+ }
617
+ this.input(next);
618
+ }
619
+ #springBack() {
620
+ const target = this.springTarget();
621
+ if (target === void 0) return;
622
+ if (this.mapping().snap(target) !== this.#value) this.toggleState("springing", true);
623
+ this.#commit(target);
624
+ }
625
+ #startDrag(control, axis, event) {
626
+ const mapping = this.mapping();
627
+ const detent = this.detent;
628
+ const start = {
629
+ from: this.#value,
630
+ position: axis.position(event),
631
+ proportion: axis.fromProportion ?? mapping.proportionOf(this.#value),
632
+ thresholdPx: dragThresholdPx(event.pointerType),
633
+ travelPx: axis.travelPx
634
+ };
635
+ if (detent === void 0) return startDrag(mapping, start);
636
+ const zonePx = readPxProperty(getComputedStyle(control), "--_sonic-detent-zone", detentZonePx);
637
+ return startDrag(mapping, {
638
+ ...start,
639
+ detent: {
640
+ value: detent,
641
+ zone: zonePx / axis.travelPx
642
+ }
643
+ });
644
+ }
645
+ #textFor(value) {
646
+ return this.#formatValue?.(value) ?? String(value);
647
+ }
648
+ #watchEscape(isHovered) {
649
+ if (!isHovered) {
650
+ this.#escapeWatch?.abort();
651
+ this.#escapeWatch = void 0;
652
+ return;
653
+ }
654
+ if (this.#escapeWatch) return;
655
+ const watch = new AbortController();
656
+ this.#escapeWatch = watch;
657
+ this.ownerDocument.addEventListener("keydown", (event) => {
658
+ if (event.key === "Escape" && this.#claim.dismiss()) this.#renderHold();
659
+ }, { signal: watch.signal });
660
+ }
661
+ #withinLimit(mapping, state) {
662
+ const limit = this.#limit;
663
+ if (!limit || mapping.isWrapping) return state;
664
+ const [low, high] = limit;
665
+ return {
666
+ ...state,
667
+ rawProportion: clamp(state.rawProportion, mapping.proportionOf(low), mapping.proportionOf(high))
668
+ };
669
+ }
670
+ #write(next) {
671
+ if (!Number.isFinite(next)) return;
672
+ const clamped = this.mapping().snap(next);
673
+ this.#asked = clamped;
674
+ if (clamped === this.#value) return;
675
+ this.#value = clamped;
676
+ this.render();
677
+ }
678
+ #writeModulationProportion(control, prefix) {
679
+ const modulationValue = this.isWrapping() ? void 0 : this.#modulationValue;
680
+ const isModulated = modulationValue !== void 0;
681
+ if (isModulated !== this.#isModulated) {
682
+ this.#isModulated = isModulated;
683
+ this.toggleState("modulated", isModulated);
684
+ }
685
+ if (modulationValue === void 0) {
686
+ control.style.removeProperty(`--_sonic-${prefix}-modulation-value`);
687
+ return;
688
+ }
689
+ control.style.setProperty(`--_sonic-${prefix}-modulation-value`, String(this.mapping().proportionOf(modulationValue)));
690
+ }
691
+ };
692
+
693
+ //#endregion
694
+ export { SonicValueElement, linkValue };
@@ -1,11 +1,11 @@
1
- //#region src/elements/range-entry.ts
1
+ //#region src/elements/value-entry.ts
2
2
  const doublePressMs = 500;
3
3
  const doublePressPx = 4;
4
4
  function isDoublePress(previous, next) {
5
5
  if (!previous) return false;
6
6
  return next.time - previous.time < doublePressMs && Math.hypot(next.x - previous.x, next.y - previous.y) < doublePressPx;
7
7
  }
8
- var RangeEntry = class {
8
+ var ValueEntry = class {
9
9
  get isOpen() {
10
10
  return this.#isOpen;
11
11
  }
@@ -71,4 +71,4 @@ var RangeEntry = class {
71
71
  };
72
72
 
73
73
  //#endregion
74
- export { RangeEntry };
74
+ export { ValueEntry };