@xsynaptic/sonic-ui 0.17.0 → 0.18.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 (125) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +112 -19
  3. package/dist/default/dat.js +1 -0
  4. package/dist/default/elements/button.js +2 -0
  5. package/dist/default/elements/dial.js +2 -0
  6. package/dist/default/elements/display-element.js +9 -7
  7. package/dist/default/elements/envelope.js +31 -72
  8. package/dist/default/elements/field-gesture.js +81 -0
  9. package/dist/default/elements/meter.js +4 -0
  10. package/dist/default/elements/position-group.js +42 -26
  11. package/dist/default/elements/radio-group.js +59 -56
  12. package/dist/default/elements/scale.js +3 -3
  13. package/dist/default/elements/slider.js +11 -13
  14. package/dist/default/elements/split.js +14 -13
  15. package/dist/default/elements/switch.js +31 -46
  16. package/dist/default/elements/toggle.js +20 -44
  17. package/dist/default/elements/value-element.js +65 -91
  18. package/dist/default/elements/value-gestures.js +22 -22
  19. package/dist/default/elements/wave-element.js +9 -12
  20. package/dist/default/elements/waveform.js +42 -241
  21. package/dist/default/elements/wavestrip.js +10 -5
  22. package/dist/default/elements/xy.js +67 -112
  23. package/dist/default/lib/canvas-surface.js +25 -0
  24. package/dist/default/lib/crossfade.js +1 -0
  25. package/dist/default/lib/envelope-curve.js +1 -0
  26. package/dist/default/lib/peaks-request.js +44 -0
  27. package/dist/default/lib/positions.js +58 -0
  28. package/dist/default/lib/scrub.js +12 -0
  29. package/dist/default/lib/value-model.js +62 -0
  30. package/dist/default/lib/waveform-scene.js +108 -0
  31. package/dist/default/lib/zoom-gesture.js +97 -1
  32. package/dist/dev/dat.d.ts +1 -0
  33. package/dist/dev/dat.js +1 -0
  34. package/dist/dev/dat.js.map +1 -1
  35. package/dist/dev/elements/button.d.ts +2 -0
  36. package/dist/dev/elements/button.js +2 -0
  37. package/dist/dev/elements/button.js.map +1 -1
  38. package/dist/dev/elements/dial.d.ts +2 -0
  39. package/dist/dev/elements/dial.js +2 -0
  40. package/dist/dev/elements/dial.js.map +1 -1
  41. package/dist/dev/elements/display-element.d.ts +5 -3
  42. package/dist/dev/elements/display-element.js +9 -7
  43. package/dist/dev/elements/display-element.js.map +1 -1
  44. package/dist/dev/elements/envelope.js +31 -72
  45. package/dist/dev/elements/envelope.js.map +1 -1
  46. package/dist/dev/elements/field-gesture.js +82 -0
  47. package/dist/dev/elements/field-gesture.js.map +1 -0
  48. package/dist/dev/elements/meter.d.ts +10 -5
  49. package/dist/dev/elements/meter.js +4 -0
  50. package/dist/dev/elements/meter.js.map +1 -1
  51. package/dist/dev/elements/position-group.d.ts +12 -6
  52. package/dist/dev/elements/position-group.js +42 -26
  53. package/dist/dev/elements/position-group.js.map +1 -1
  54. package/dist/dev/elements/radio-group.d.ts +11 -7
  55. package/dist/dev/elements/radio-group.js +59 -56
  56. package/dist/dev/elements/radio-group.js.map +1 -1
  57. package/dist/dev/elements/scale.js +3 -3
  58. package/dist/dev/elements/scale.js.map +1 -1
  59. package/dist/dev/elements/slider.d.ts +4 -2
  60. package/dist/dev/elements/slider.js +11 -13
  61. package/dist/dev/elements/slider.js.map +1 -1
  62. package/dist/dev/elements/split.d.ts +1 -0
  63. package/dist/dev/elements/split.js +14 -13
  64. package/dist/dev/elements/split.js.map +1 -1
  65. package/dist/dev/elements/switch.d.ts +2 -2
  66. package/dist/dev/elements/switch.js +31 -46
  67. package/dist/dev/elements/switch.js.map +1 -1
  68. package/dist/dev/elements/toggle.js +20 -44
  69. package/dist/dev/elements/toggle.js.map +1 -1
  70. package/dist/dev/elements/value-element.d.ts +5 -5
  71. package/dist/dev/elements/value-element.js +65 -91
  72. package/dist/dev/elements/value-element.js.map +1 -1
  73. package/dist/dev/elements/value-gestures.js +22 -22
  74. package/dist/dev/elements/value-gestures.js.map +1 -1
  75. package/dist/dev/elements/wave-element.d.ts +6 -4
  76. package/dist/dev/elements/wave-element.js +9 -12
  77. package/dist/dev/elements/wave-element.js.map +1 -1
  78. package/dist/dev/elements/waveform.d.ts +19 -21
  79. package/dist/dev/elements/waveform.js +42 -241
  80. package/dist/dev/elements/waveform.js.map +1 -1
  81. package/dist/dev/elements/wavestrip.d.ts +5 -1
  82. package/dist/dev/elements/wavestrip.js +10 -5
  83. package/dist/dev/elements/wavestrip.js.map +1 -1
  84. package/dist/dev/elements/xy.js +67 -112
  85. package/dist/dev/elements/xy.js.map +1 -1
  86. package/dist/dev/index.d.ts +2 -1
  87. package/dist/dev/lib/canvas-surface.d.ts +1 -0
  88. package/dist/dev/lib/canvas-surface.js +25 -0
  89. package/dist/dev/lib/canvas-surface.js.map +1 -1
  90. package/dist/dev/lib/crossfade.d.ts +1 -0
  91. package/dist/dev/lib/crossfade.js +1 -0
  92. package/dist/dev/lib/crossfade.js.map +1 -1
  93. package/dist/dev/lib/envelope-curve.d.ts +1 -0
  94. package/dist/dev/lib/envelope-curve.js +1 -0
  95. package/dist/dev/lib/envelope-curve.js.map +1 -1
  96. package/dist/dev/lib/peaks-request.d.ts +5 -0
  97. package/dist/dev/lib/peaks-request.js +45 -0
  98. package/dist/dev/lib/peaks-request.js.map +1 -0
  99. package/dist/dev/lib/positions.d.ts +7 -0
  100. package/dist/dev/lib/positions.js +59 -0
  101. package/dist/dev/lib/positions.js.map +1 -0
  102. package/dist/dev/lib/scrub.d.ts +8 -0
  103. package/dist/dev/lib/scrub.js +13 -0
  104. package/dist/dev/lib/scrub.js.map +1 -0
  105. package/dist/dev/lib/time-regions.d.ts +1 -0
  106. package/dist/dev/lib/time-regions.js.map +1 -1
  107. package/dist/dev/lib/units.d.ts +8 -0
  108. package/dist/dev/lib/value-model.js +63 -0
  109. package/dist/dev/lib/value-model.js.map +1 -0
  110. package/dist/dev/lib/waveform-buckets.d.ts +1 -0
  111. package/dist/dev/lib/waveform-buckets.js.map +1 -1
  112. package/dist/dev/lib/waveform-scene.d.ts +11 -0
  113. package/dist/dev/lib/waveform-scene.js +109 -0
  114. package/dist/dev/lib/waveform-scene.js.map +1 -0
  115. package/dist/dev/lib/zoom-gesture.js +97 -1
  116. package/dist/dev/lib/zoom-gesture.js.map +1 -1
  117. package/dist/styles/button.css +16 -6
  118. package/dist/styles/waveform.css +2 -1
  119. package/package.json +1 -1
  120. package/dist/default/lib/asked-value.js +0 -21
  121. package/dist/default/lib/fill-watch.js +0 -29
  122. package/dist/dev/lib/asked-value.js +0 -22
  123. package/dist/dev/lib/asked-value.js.map +0 -1
  124. package/dist/dev/lib/fill-watch.js +0 -30
  125. package/dist/dev/lib/fill-watch.js.map +0 -1
@@ -1,13 +1,12 @@
1
- import { clamp, toNumber, trimFloat } from "../lib/math.js";
1
+ import { toNumber, trimFloat } from "../lib/math.js";
2
2
  import { writeAttribute } from "../lib/write-attribute.js";
3
3
  import { SonicFormElement } from "./form-element.js";
4
4
  import { Readout } from "./readout.js";
5
5
  import { readPxProperty } from "../lib/read-px-property.js";
6
6
  import { parseNumberList } from "../lib/number-list.js";
7
- import { valueMapping } from "../lib/value-mapping.js";
8
7
  import { ValueGestures } from "./value-gestures.js";
9
- import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
10
8
  import { bindHoverPreview } from "../lib/hover-preview.js";
9
+ import { createValueModel } from "../lib/value-model.js";
11
10
 
12
11
  //#region src/elements/value-element.ts
13
12
  const fallbackTravelPx = 160;
@@ -56,20 +55,7 @@ var SonicValueElement = class extends SonicFormElement {
56
55
  input: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),
57
56
  isDisabled: () => element.isDisabled(),
58
57
  isHeld: () => element.#isHeld(),
59
- land: (resolve) => {
60
- element.#land = resolve;
61
- },
62
- limit: (bounds) => {
63
- element.#limit = bounds;
64
- },
65
- mapping: () => element.mapping(),
66
- value: () => element.#cell.value,
67
- watch: (listener) => {
68
- element.#watchers.add(listener);
69
- return () => {
70
- element.#watchers.delete(listener);
71
- };
72
- }
58
+ model: element.#model
73
59
  });
74
60
  }
75
61
  get cancelling() {
@@ -177,6 +163,7 @@ var SonicValueElement = class extends SonicFormElement {
177
163
  get pointerType() {
178
164
  return this.#gestures?.pointerType();
179
165
  }
166
+ /** The values a stepped control stops at, spaced evenly along the travel whatever their spacing */
180
167
  get positions() {
181
168
  return parseNumberList(this.getAttribute("positions"));
182
169
  }
@@ -192,6 +179,7 @@ var SonicValueElement = class extends SonicFormElement {
192
179
  get revealed() {
193
180
  return this.hasState("revealed");
194
181
  }
182
+ /** Rounds the value assistive technology hears, in value units; the value itself is untouched */
195
183
  get spokenStep() {
196
184
  return this.optionalNumberAttribute("spoken-step");
197
185
  }
@@ -211,7 +199,7 @@ var SonicValueElement = class extends SonicFormElement {
211
199
  this.reflect("taper", curve);
212
200
  }
213
201
  get value() {
214
- return this.#cell.value;
202
+ return this.#model.value;
215
203
  }
216
204
  set value(next) {
217
205
  if (next === void 0 || next === null) {
@@ -220,18 +208,14 @@ var SonicValueElement = class extends SonicFormElement {
220
208
  }
221
209
  const value = toNumber(next);
222
210
  if (this.#isHeld()) {
223
- this.heldWrite(value);
211
+ this.#writeHeld(value);
224
212
  return;
225
213
  }
226
- if (setAsked(this.#cell, this.mapping(), value)) this.render();
214
+ if (this.#model.write(value)) this.render();
227
215
  }
228
216
  get valueText() {
229
- return this.#textFor(this.#cell.value);
217
+ return this.#textFor(this.#model.value);
230
218
  }
231
- #cell = {
232
- asked: void 0,
233
- value: 0
234
- };
235
219
  #formatEntry;
236
220
  #formatSpokenValue;
237
221
  #formatValue;
@@ -239,25 +223,16 @@ var SonicValueElement = class extends SonicFormElement {
239
223
  #heldAt;
240
224
  #hover;
241
225
  #instance = String(instanceCount += 1);
242
- #land;
243
- #limit;
244
- #mapping;
226
+ #model = createValueModel(() => this.#spec());
245
227
  #parseValue;
246
- #positions;
247
228
  #readout;
248
- #watchers = /* @__PURE__ */ new Set();
249
229
  attributeChangedCallback(name) {
250
- this.#mapping = void 0;
251
- this.#positions = parseNumberList(this.getAttribute("positions"));
252
- if (name === "value") {
253
- if (this.#isHeld()) {
254
- this.heldWrite(this.numberAttribute("value", this.min));
255
- return;
256
- }
257
- this.#cell.asked = void 0;
230
+ if (name === "value" && this.#isHeld()) {
231
+ this.#writeHeld(this.numberAttribute("value", this.min));
232
+ return;
258
233
  }
259
234
  if (name === "disabled" && this.isDisabled()) this.#gestures?.end();
260
- resnap(this.#cell, this.mapping(), this.numberAttribute("value", this.min));
235
+ this.#model.respec(this.numberAttribute("value", this.min), { forgetAsk: name === "value" });
261
236
  if (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();
262
237
  this.render();
263
238
  }
@@ -287,26 +262,23 @@ var SonicValueElement = class extends SonicFormElement {
287
262
  this.forwardNaming(target, isNamed);
288
263
  },
289
264
  hasEntry: () => this.entry !== "none",
290
- holdChanged: () => {
291
- this.#renderHold();
292
- },
293
265
  input: (next) => this.input(next),
294
266
  isDisabled: () => this.isDisabled(),
295
267
  keyStep: () => this.keyStep,
296
- land: (target, direction) => this.#land?.(target, direction) ?? target,
297
- limit: () => this.#limit,
298
- mapping: () => this.mapping(),
268
+ model: this.#model,
299
269
  parse: (text) => (this.#parseValue ?? Number.parseFloat)(text),
300
270
  render: () => {
301
271
  this.render();
302
272
  },
273
+ scrubChanged: () => {
274
+ this.#renderHold();
275
+ },
303
276
  scrubsKeyRepeat: () => this.scrubsKeyRepeat(),
304
277
  springTarget: () => this.springTarget(),
305
278
  tapTarget: () => this.tapTarget(),
306
279
  toggleState: (state, isOn) => {
307
280
  this.toggleState(state, isOn);
308
- },
309
- value: () => this.#cell.value
281
+ }
310
282
  }, control);
311
283
  this.#gestures.bind(signal, grab);
312
284
  control.addEventListener("pointerup", (event) => {
@@ -322,7 +294,7 @@ var SonicValueElement = class extends SonicFormElement {
322
294
  dismiss: () => {
323
295
  this.render();
324
296
  },
325
- isTaken: () => this.#isEditing() || this.isRevealed(),
297
+ isTaken: () => this.#isEditing() || this.#isRevealed(),
326
298
  show: () => {
327
299
  if (!this.readout) return;
328
300
  this.renderReadout();
@@ -335,60 +307,32 @@ var SonicValueElement = class extends SonicFormElement {
335
307
  controlRole() {
336
308
  return "slider";
337
309
  }
338
- heldFrom() {
339
- return this.#gestures?.heldFrom();
340
- }
341
- heldWrite(next) {
342
- this.#heldAt = next;
343
- }
344
- holdChanged() {}
345
310
  input(next) {
346
- const previous = this.#cell.value;
347
- const target = this.#limit ? clamp(next, ...this.#limit) : next;
348
- if (moveTo(this.#cell, this.mapping(), target)) this.render();
349
- if (this.#cell.value === previous) return false;
311
+ const previous = this.#model.value;
312
+ if (this.#model.input(next)) this.render();
313
+ if (this.#model.value === previous) return false;
350
314
  this.dispatchEvent(new Event("input", {
351
315
  bubbles: true,
352
316
  composed: true
353
317
  }));
354
318
  return true;
355
319
  }
356
- isRevealed() {
357
- return this.#gestures?.isRevealed() === true;
358
- }
359
320
  isWrapping() {
360
321
  return false;
361
322
  }
362
323
  mapping() {
363
- if (this.#mapping) return this.#mapping;
364
- const { detent, midpoint } = this;
365
- this.#mapping = valueMapping({
366
- ...detent === void 0 ? {} : { detent },
367
- ...this.#positions ? { positions: this.#positions } : {},
368
- max: this.max,
369
- ...midpoint === void 0 ? {} : { midpoint },
370
- isNotched: this.notched,
371
- isWrapping: this.isWrapping(),
372
- min: this.min,
373
- step: this.step,
374
- taper: this.taper
375
- });
376
- return this.#mapping;
324
+ return this.#model.mapping();
377
325
  }
378
326
  originValue() {
379
327
  return this.origin ?? this.mapping().bounds[0];
380
328
  }
381
- playback() {
382
- const from = this.heldFrom();
383
- return from === void 0 ? this.#cell.value : this.#heldAt ?? from;
384
- }
385
329
  proportionsChanged() {}
386
330
  readoutValue() {
387
- return this.#shown().value ?? this.#cell.value;
331
+ return this.#shown().value ?? this.#model.value;
388
332
  }
389
333
  render() {
390
334
  if (this.isBound()) this.#renderControl();
391
- for (const listener of this.#watchers) listener();
335
+ this.#model.notify();
392
336
  }
393
337
  renderReadout() {
394
338
  const readout = this.#readout;
@@ -404,16 +348,25 @@ var SonicValueElement = class extends SonicFormElement {
404
348
  restoreState(state) {
405
349
  this.value = Number(state);
406
350
  }
351
+ scrubChanged() {}
407
352
  scrubsKeyRepeat() {
408
353
  return false;
409
354
  }
355
+ scrubState() {
356
+ const from = this.#gestures?.scrubFrom();
357
+ return {
358
+ from,
359
+ isRevealed: this.#isRevealed(),
360
+ played: from === void 0 ? this.#model.value : this.#heldAt ?? from
361
+ };
362
+ }
410
363
  springTarget() {}
411
364
  tapTarget() {}
412
365
  travelPx(control, property) {
413
366
  return Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));
414
367
  }
415
368
  #entryText() {
416
- const value = this.#cell.value;
369
+ const value = this.#model.value;
417
370
  if (this.#formatEntry) return this.#formatEntry(value);
418
371
  if (!this.#parseValue) return String(value);
419
372
  const text = this.valueText;
@@ -426,8 +379,11 @@ var SonicValueElement = class extends SonicFormElement {
426
379
  #isHeld() {
427
380
  return this.#gestures?.isHeld() === true;
428
381
  }
382
+ #isRevealed() {
383
+ return this.#gestures?.isRevealed() === true;
384
+ }
429
385
  #renderAria(control, orientation) {
430
- this.writeFormValue(String(this.#cell.value), String(this.#cell.value));
386
+ this.writeFormValue(String(this.#model.value), String(this.#model.value));
431
387
  if (this.#isEditing()) {
432
388
  for (const name of roleAttributes) control.removeAttribute(name);
433
389
  this.forwardNaming(control, false);
@@ -446,9 +402,9 @@ var SonicValueElement = class extends SonicFormElement {
446
402
  this.renderReadout();
447
403
  }
448
404
  #renderControl() {
449
- if (this.#isEditing() || this.isRevealed()) this.#hover?.clear();
405
+ if (this.#isEditing() || this.#isRevealed()) this.#hover?.clear();
450
406
  this.draw();
451
- this.toggleState("at-origin", !this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()));
407
+ this.toggleState("at-origin", !this.isWrapping() && this.#model.value === this.mapping().snap(this.originValue()));
452
408
  this.#renderAria(this.focusTarget(), this.controlOrientation());
453
409
  }
454
410
  #renderDisabled(control) {
@@ -458,30 +414,48 @@ var SonicValueElement = class extends SonicFormElement {
458
414
  writeAttribute(control, "tabindex", isDisabled ? void 0 : stop);
459
415
  }
460
416
  #renderHold() {
461
- if (this.heldFrom() === void 0) this.#heldAt = void 0;
417
+ if (this.#gestures?.scrubFrom() === void 0) this.#heldAt = void 0;
462
418
  this.#hover?.restore();
463
419
  this.renderReadout();
464
- this.holdChanged();
420
+ this.scrubChanged();
465
421
  }
466
422
  #shown() {
467
423
  if (this.#isEditing()) return {
468
424
  isOpen: true,
469
425
  value: void 0
470
426
  };
471
- const isRevealed = this.isRevealed();
427
+ const isRevealed = this.#isRevealed();
472
428
  const preview = isRevealed ? void 0 : this.#hover?.value();
473
429
  return {
474
430
  isOpen: (isRevealed || preview !== void 0) && this.readout,
475
- value: preview ?? this.#cell.value
431
+ value: preview ?? this.#model.value
432
+ };
433
+ }
434
+ #spec() {
435
+ const { detent, midpoint, positions } = this;
436
+ return {
437
+ ...detent === void 0 ? {} : { detent },
438
+ ...positions ? { positions } : {},
439
+ max: this.max,
440
+ ...midpoint === void 0 ? {} : { midpoint },
441
+ isNotched: this.notched,
442
+ isWrapping: this.isWrapping(),
443
+ min: this.min,
444
+ step: this.step,
445
+ taper: this.taper
476
446
  };
477
447
  }
478
448
  #spokenNow() {
479
449
  const step = this.spokenStep;
480
- return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#cell.value / step) * step) : this.#cell.value;
450
+ return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#model.value / step) * step) : this.#model.value;
481
451
  }
482
452
  #textFor(value) {
483
453
  return this.#formatValue?.(value) ?? String(value);
484
454
  }
455
+ #writeHeld(next) {
456
+ this.#heldAt = next;
457
+ this.scrubChanged();
458
+ }
485
459
  };
486
460
 
487
461
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { AskedValue } from '#lib/asked-value.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { moveTo, resnap, setAsked } from '#lib/asked-value.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { clamp, toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// eslint-disable-next-line unicorn/consistent-boolean-name -- the name bundlers and other kits use\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-hover': Event;\n\t\t'sonic-reveal': Event;\n\t}\n}\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\nconst roleAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\ntype ValueLanding = (target: number, direction: -1 | 0 | 1) => number;\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tland: (resolve: undefined | ValueLanding) => void;\n\tlimit: (bounds: [number, number] | undefined) => void;\n\tmapping: () => ValueMapping;\n\tvalue: () => number;\n\twatch: (listener: () => void) => () => void;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'detent',\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tland: (resolve) => {\n\t\t\t\telement.#land = resolve;\n\t\t\t},\n\t\t\tlimit: (bounds) => {\n\t\t\t\telement.#limit = bounds;\n\t\t\t},\n\t\t\tmapping: () => element.mapping(),\n\t\t\tvalue: () => element.#cell.value,\n\t\t\twatch: (listener) => {\n\t\t\t\telement.#watchers.add(listener);\n\n\t\t\t\treturn () => {\n\t\t\t\t\telement.#watchers.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t});\n\t}\n\n\tget cancelling(): boolean {\n\t\treturn this.hasState('cancelling');\n\t}\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget detent(): number | undefined {\n\t\treturn this.optionalNumberAttribute('detent');\n\t}\n\n\tset detent(value: number | undefined) {\n\t\tthis.reflect('detent', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\tget doublePress(): 'entry' | 'none' | 'reset' {\n\t\tconst gesture = this.getAttribute('double-press');\n\n\t\treturn gesture === 'none' || gesture === 'reset' ? gesture : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'none' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget dragging(): boolean {\n\t\treturn this.hasState('dragging');\n\t}\n\n\tget entry(): 'none' | undefined {\n\t\treturn this.getAttribute('entry') === 'none' ? 'none' : undefined;\n\t}\n\n\tset entry(gesture: 'none' | undefined) {\n\t\tthis.reflect('entry', gesture);\n\t}\n\n\tget formatEntry(): ((value: number) => string) | undefined {\n\t\treturn this.#formatEntry;\n\t}\n\n\tset formatEntry(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatEntry = format;\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget hoverValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget pointerType(): string | undefined {\n\t\treturn this.#gestures?.pointerType();\n\t}\n\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget revealed(): boolean {\n\t\treturn this.hasState('revealed');\n\t}\n\n\tget spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#cell.value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.heldWrite(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (setAsked(this.#cell, this.mapping(), value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#cell.value);\n\t}\n\n\treadonly #cell: AskedValue = { asked: undefined, value: 0 };\n\n\t#formatEntry: ((value: number) => string) | undefined;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#gestures: undefined | ValueGestures;\n\n\t#heldAt: number | undefined;\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t#land: undefined | ValueLanding;\n\n\t#limit: [number, number] | undefined;\n\n\t// Every attribute it reads is observed; a subclass adding an input to `isWrapping()` has to observe it\n\t#mapping: undefined | ValueMapping;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#positions: Array<number> | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\treadonly #watchers = new Set<() => void>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapping = undefined;\n\t\tthis.#positions = parseNumberList(this.getAttribute('positions'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#isHeld()) {\n\t\t\t\tthis.heldWrite(this.numberAttribute('value', this.min));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#cell.asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tresnap(this.#cell, this.mapping(), this.numberAttribute('value', this.min));\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'entry',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatEntry',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected abandonHold(): void {\n\t\tthis.#gestures?.abandon();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => undefined | ValueAxis,\n\t): void {\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\t\tthis.#gestures = new ValueGestures(\n\t\t\t{\n\t\t\t\tdefault: () => this.default,\n\t\t\t\tdetent: () => this.detent,\n\t\t\t\tdispatch: (type) => {\n\t\t\t\t\tthis.dispatchEvent(new Event(type, { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdoublePress: () => this.doublePress,\n\t\t\t\tentryId: `sonic-entry-${this.#instance}`,\n\t\t\t\tentryText: () => this.#entryText(),\n\t\t\t\tforwardNaming: (target, isNamed) => {\n\t\t\t\t\tthis.forwardNaming(target, isNamed);\n\t\t\t\t},\n\t\t\t\thasEntry: () => this.entry !== 'none',\n\t\t\t\tholdChanged: () => {\n\t\t\t\t\tthis.#renderHold();\n\t\t\t\t},\n\t\t\t\tinput: (next) => this.input(next),\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tkeyStep: () => this.keyStep,\n\t\t\t\tland: (target, direction) => this.#land?.(target, direction) ?? target,\n\t\t\t\tlimit: () => this.#limit,\n\t\t\t\tmapping: () => this.mapping(),\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tparse: (text) => (this.#parseValue ?? Number.parseFloat)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubsKeyRepeat: () => this.scrubsKeyRepeat(),\n\t\t\t\tspringTarget: () => this.springTarget(),\n\t\t\t\ttapTarget: () => this.tapTarget(),\n\t\t\t\ttoggleState: (state, isOn) => {\n\t\t\t\t\tthis.toggleState(state, isOn);\n\t\t\t\t},\n\t\t\t\tvalue: () => this.#cell.value,\n\t\t\t},\n\t\t\tcontrol,\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tthis.#hover?.lift(event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, place, valueAt }: HoverBinding,\n\t): void {\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcanShow,\n\t\t\t\tchanged: () => {\n\t\t\t\t\tthis.dispatchEvent(new Event('sonic-hover', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdismiss: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tisTaken: () => this.#isEditing() || this.isRevealed(),\n\t\t\t\tshow: () => {\n\t\t\t\t\tif (!this.readout) return;\n\n\t\t\t\t\tthis.renderReadout();\n\t\t\t\t\tplace();\n\t\t\t\t},\n\t\t\t\tvalueAt,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected heldFrom(): number | undefined {\n\t\treturn this.#gestures?.heldFrom();\n\t}\n\n\tprotected heldWrite(next: number): void {\n\t\tthis.#heldAt = next;\n\t}\n\n\tprotected holdChanged(): void {\n\t\t// Only a subclass draws a hold\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#cell.value;\n\n\t\tconst target = this.#limit ? clamp(next, ...this.#limit) : next;\n\n\t\tif (moveTo(this.#cell, this.mapping(), target)) this.render();\n\t\tif (this.#cell.value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\tif (this.#mapping) return this.#mapping;\n\n\t\tconst { detent, midpoint } = this;\n\n\t\tthis.#mapping = valueMapping({\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(this.#positions ? { positions: this.#positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t});\n\n\t\treturn this.#mapping;\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected playback(): number {\n\t\tconst from = this.heldFrom();\n\n\t\treturn from === undefined ? this.#cell.value : (this.#heldAt ?? from);\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#cell.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tfor (const listener of this.#watchers) listener();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#cell.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\tif (!this.#parseValue) return String(value);\n\n\t\tconst text = this.valueText;\n\n\t\tif (__DEV__ && !Number.isFinite(this.#parseValue(text))) {\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> opens its entry with \"${text}\", which parseValue cannot read; set formatEntry`,\n\t\t\t);\n\t\t}\n\n\t\treturn text;\n\t}\n\n\t#isEditing(): boolean {\n\t\treturn this.#gestures?.isEditing() === true;\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#gestures?.isHeld() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#cell.value), String(this.#cell.value));\n\t\tif (this.#isEditing()) {\n\t\t\tfor (const name of roleAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tconst [low, high] = this.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#spokenNow()),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\t// A reveal or typed entry takes the readout from a hover\n\t\tif (this.#isEditing() || this.isRevealed()) this.#hover?.clear();\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.heldFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.#hover?.restore();\n\t\tthis.renderReadout();\n\t\tthis.holdChanged();\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.isRevealed();\n\t\tconst preview = isRevealed ? undefined : this.#hover?.value();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#cell.value,\n\t\t};\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#cell.value / step) * step)\n\t\t\t: this.#cell.value;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ScrubState } from '#lib/scrub.ts';\nimport type { ValueMapping, ValueSpec } from '#lib/value-mapping.ts';\nimport type { ValueModel } from '#lib/value-model.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { createValueModel } from '#lib/value-model.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// eslint-disable-next-line unicorn/consistent-boolean-name -- the name bundlers and other kits use\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-hover': Event;\n\t\t'sonic-reveal': Event;\n\t}\n}\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\nconst roleAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tmodel: ValueModel;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'detent',\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tmodel: element.#model,\n\t\t});\n\t}\n\n\tget cancelling(): boolean {\n\t\treturn this.hasState('cancelling');\n\t}\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget detent(): number | undefined {\n\t\treturn this.optionalNumberAttribute('detent');\n\t}\n\n\tset detent(value: number | undefined) {\n\t\tthis.reflect('detent', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\tget doublePress(): 'entry' | 'none' | 'reset' {\n\t\tconst gesture = this.getAttribute('double-press');\n\n\t\treturn gesture === 'none' || gesture === 'reset' ? gesture : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'none' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget dragging(): boolean {\n\t\treturn this.hasState('dragging');\n\t}\n\n\tget entry(): 'none' | undefined {\n\t\treturn this.getAttribute('entry') === 'none' ? 'none' : undefined;\n\t}\n\n\tset entry(gesture: 'none' | undefined) {\n\t\tthis.reflect('entry', gesture);\n\t}\n\n\tget formatEntry(): ((value: number) => string) | undefined {\n\t\treturn this.#formatEntry;\n\t}\n\n\tset formatEntry(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatEntry = format;\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget hoverValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget pointerType(): string | undefined {\n\t\treturn this.#gestures?.pointerType();\n\t}\n\n\t/** The values a stepped control stops at, spaced evenly along the travel whatever their spacing */\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget revealed(): boolean {\n\t\treturn this.hasState('revealed');\n\t}\n\n\t/** Rounds the value assistive technology hears, in value units; the value itself is untouched */\n\tget spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#model.value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.#writeHeld(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#model.write(value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#model.value);\n\t}\n\n\t#formatEntry: ((value: number) => string) | undefined;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#gestures: undefined | ValueGestures;\n\n\t#heldAt: number | undefined;\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t// A subclass adding an input to `isWrapping()` has to observe it\n\treadonly #model = createValueModel(() => this.#spec());\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value' && this.#isHeld()) {\n\t\t\tthis.#writeHeld(this.numberAttribute('value', this.min));\n\t\t\treturn;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tthis.#model.respec(this.numberAttribute('value', this.min), { forgetAsk: name === 'value' });\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'entry',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatEntry',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected abandonHold(): void {\n\t\tthis.#gestures?.abandon();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => undefined | ValueAxis,\n\t): void {\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\t\tthis.#gestures = new ValueGestures(\n\t\t\t{\n\t\t\t\tdefault: () => this.default,\n\t\t\t\tdetent: () => this.detent,\n\t\t\t\tdispatch: (type) => {\n\t\t\t\t\tthis.dispatchEvent(new Event(type, { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdoublePress: () => this.doublePress,\n\t\t\t\tentryId: `sonic-entry-${this.#instance}`,\n\t\t\t\tentryText: () => this.#entryText(),\n\t\t\t\tforwardNaming: (target, isNamed) => {\n\t\t\t\t\tthis.forwardNaming(target, isNamed);\n\t\t\t\t},\n\t\t\t\thasEntry: () => this.entry !== 'none',\n\t\t\t\tinput: (next) => this.input(next),\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tkeyStep: () => this.keyStep,\n\t\t\t\tmodel: this.#model,\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tparse: (text) => (this.#parseValue ?? Number.parseFloat)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubChanged: () => {\n\t\t\t\t\tthis.#renderHold();\n\t\t\t\t},\n\t\t\t\tscrubsKeyRepeat: () => this.scrubsKeyRepeat(),\n\t\t\t\tspringTarget: () => this.springTarget(),\n\t\t\t\ttapTarget: () => this.tapTarget(),\n\t\t\t\ttoggleState: (state, isOn) => {\n\t\t\t\t\tthis.toggleState(state, isOn);\n\t\t\t\t},\n\t\t\t},\n\t\t\tcontrol,\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tthis.#hover?.lift(event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, place, valueAt }: HoverBinding,\n\t): void {\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcanShow,\n\t\t\t\tchanged: () => {\n\t\t\t\t\tthis.dispatchEvent(new Event('sonic-hover', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdismiss: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tisTaken: () => this.#isEditing() || this.#isRevealed(),\n\t\t\t\tshow: () => {\n\t\t\t\t\tif (!this.readout) return;\n\n\t\t\t\t\tthis.renderReadout();\n\t\t\t\t\tplace();\n\t\t\t\t},\n\t\t\t\tvalueAt,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#model.value;\n\n\t\tif (this.#model.input(next)) this.render();\n\t\tif (this.#model.value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\treturn this.#model.mapping();\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#model.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tthis.#model.notify();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubChanged(): void {\n\t\t// Only a subclass draws a scrub\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected scrubState(): ScrubState {\n\t\tconst from = this.#gestures?.scrubFrom();\n\n\t\treturn {\n\t\t\tfrom,\n\t\t\tisRevealed: this.#isRevealed(),\n\t\t\tplayed: from === undefined ? this.#model.value : (this.#heldAt ?? from),\n\t\t};\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#model.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\tif (!this.#parseValue) return String(value);\n\n\t\tconst text = this.valueText;\n\n\t\tif (__DEV__ && !Number.isFinite(this.#parseValue(text))) {\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> opens its entry with \"${text}\", which parseValue cannot read; set formatEntry`,\n\t\t\t);\n\t\t}\n\n\t\treturn text;\n\t}\n\n\t#isEditing(): boolean {\n\t\treturn this.#gestures?.isEditing() === true;\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#gestures?.isHeld() === true;\n\t}\n\n\t#isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#model.value), String(this.#model.value));\n\t\tif (this.#isEditing()) {\n\t\t\tfor (const name of roleAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tconst [low, high] = this.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#spokenNow()),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\t// A reveal or typed entry takes the readout from a hover\n\t\tif (this.#isEditing() || this.#isRevealed()) this.#hover?.clear();\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#model.value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.#gestures?.scrubFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.#hover?.restore();\n\t\tthis.renderReadout();\n\t\tthis.scrubChanged();\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.#isRevealed();\n\t\tconst preview = isRevealed ? undefined : this.#hover?.value();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#model.value,\n\t\t};\n\t}\n\n\t#spec(): ValueSpec {\n\t\tconst { detent, midpoint, positions } = this;\n\n\t\treturn {\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(positions ? { positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t};\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#model.value / step) * step)\n\t\t\t: this.#model.value;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n\n\t#writeHeld(next: number): void {\n\t\tthis.#heldAt = next;\n\t\tthis.scrubChanged();\n\t}\n}\n"],"mappings":";;;;;;;;;;;AAgCA,MAAM,mBAAmB;AAEzB,IAAI,gBAAgB;AAEpB,MAAM,uCAAuB,IAAI,IAAI;CAAC;CAAO;CAAY;CAAO;CAAW;CAAa;AAAO,CAAC;AAEhG,MAAM,iBAAiB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AAUA,IAAI;AAEJ,SAAgB,UAAU,SAAuC;CAChE,OAAO,KAAK,OAAO;AACpB;AAEA,IAAsB,oBAAtB,cAAgD,iBAAiB;CAChE,OAAyB,qBAAqB;EAC7C,GAAG,iBAAiB;EACpB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD;CAEA;EACC,QAAQ,aAAa;GACpB,QAAQ,MAAM,UAAU,WAAW,WAAW,CAAC,QAAQ,QAAQ,MAAM,QAAQ,MAAM,IAAI;GACvF,kBAAkB,QAAQ,WAAW;GACrC,cAAc,QAAQ,QAAQ;GAC9B,OAAO,QAAQ;EAChB;CACD;CAEA,IAAI,aAAsB;EACzB,OAAO,KAAK,SAAS,YAAY;CAClC;CAEA,IAAI,UAA8B;EACjC,OAAO,KAAK,wBAAwB,SAAS;CAC9C;CAEA,IAAI,QAAQ,OAA2B;EACtC,KAAK,QAAQ,WAAW,KAAK;CAC9B;CAEA,IAAI,SAA6B;EAChC,OAAO,KAAK,wBAAwB,QAAQ;CAC7C;CAEA,IAAI,OAAO,OAA2B;EACrC,KAAK,QAAQ,UAAU,KAAK;CAC7B;CAEA,IAAI,SAAkB;EACrB,OAAO,KAAK,aAAa,QAAQ;CAClC;CAEA,IAAI,OAAO,UAAmB;EAC7B,KAAK,QAAQ,UAAU,QAAQ;CAChC;CAEA,IAAI,cAA0C;EAC7C,MAAM,UAAU,KAAK,aAAa,cAAc;EAEhD,OAAO,YAAY,UAAU,YAAY,UAAU,UAAU;CAC9D;CAEA,IAAI,YAAY,SAAiD;EAChE,KAAK,QAAQ,gBAAgB,OAAO;CACrC;CAEA,IAAI,WAAoB;EACvB,OAAO,KAAK,SAAS,UAAU;CAChC;CAEA,IAAI,QAA4B;EAC/B,OAAO,KAAK,aAAa,OAAO,MAAM,SAAS,SAAS;CACzD;CAEA,IAAI,MAAM,SAA6B;EACtC,KAAK,QAAQ,SAAS,OAAO;CAC9B;CAEA,IAAI,cAAuD;EAC1D,OAAO,KAAK;CACb;CAEA,IAAI,YAAY,QAAiD;EAChE,KAAK,eAAe;CACrB;CAEA,IAAI,oBAA6D;EAChE,OAAO,KAAK;CACb;CAEA,IAAI,kBAAkB,QAAiD;EACtE,KAAK,qBAAqB;EAC1B,KAAK,OAAO;CACb;CAEA,IAAI,cAAuD;EAC1D,OAAO,KAAK;CACb;CAEA,IAAI,YAAY,QAAiD;EAChE,KAAK,eAAe;EACpB,KAAK,OAAO;CACb;CAEA,IAAI,aAAiC;EACpC,OAAO,KAAK,QAAQ,MAAM;CAC3B;CAEA,IAAI,UAA8B;EACjC,OAAO,KAAK,wBAAwB,UAAU;CAC/C;CAEA,IAAI,QAAQ,OAA2B;EACtC,KAAK,QAAQ,YAAY,KAAK;CAC/B;CAEA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,GAAG;CACvC;CAEA,IAAI,IAAI,OAA2B;EAClC,KAAK,QAAQ,OAAO,KAAK;CAC1B;CAEA,IAAI,WAA+B;EAClC,OAAO,KAAK,wBAAwB,UAAU;CAC/C;CAEA,IAAI,SAAS,OAA2B;EACvC,KAAK,QAAQ,YAAY,KAAK;CAC/B;CAEA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,CAAC;CACrC;CAEA,IAAI,IAAI,OAA2B;EAClC,KAAK,QAAQ,OAAO,KAAK;CAC1B;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;CAEA,IAAI,SAA6B;EAChC,OAAO,KAAK,wBAAwB,QAAQ;CAC7C;CAEA,IAAI,OAAO,OAA2B;EACrC,KAAK,QAAQ,UAAU,KAAK;CAC7B;CAEA,IAAI,aAAqD;EACxD,OAAO,KAAK;CACb;CAEA,IAAI,WAAW,OAA+C;EAC7D,KAAK,cAAc;CACpB;CAEA,IAAI,cAAkC;EACrC,OAAO,KAAK,WAAW,YAAY;CACpC;;CAGA,IAAI,YAAuC;EAC1C,OAAO,gBAAgB,KAAK,aAAa,WAAW,CAAC;CACtD;CAEA,IAAI,UAAU,WAAsC;EACnD,KAAK,QAAQ,aAAa,WAAW,KAAK,GAAG,CAAC;CAC/C;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,SAAkB;EAC7B,KAAK,QAAQ,WAAW,OAAO;CAChC;CAEA,IAAI,WAAoB;EACvB,OAAO,KAAK,SAAS,UAAU;CAChC;;CAGA,IAAI,aAAiC;EACpC,OAAO,KAAK,wBAAwB,aAAa;CAClD;CAEA,IAAI,WAAW,OAA2B;EACzC,KAAK,QAAQ,eAAe,KAAK;CAClC;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK,gBAAgB,QAAQ,CAAC;CACtC;CAEA,IAAI,KAAK,OAA2B;EACnC,KAAK,QAAQ,QAAQ,KAAK;CAC3B;CAEA,IAAI,QAA0B;EAC7B,OAAO,KAAK,aAAa,OAAO,MAAM,QAAQ,QAAQ;CACvD;CAEA,IAAI,MAAM,OAAqC;EAC9C,KAAK,QAAQ,SAAS,KAAK;CAC5B;CAEA,IAAI,QAAgB;EACnB,OAAO,KAAK,OAAO;CACpB;CAEA,IAAI,MAAM,MAAiC;EAE1C,IAAI,SAAS,UAAa,SAAS,MAAM;GACxC,KAAK,kBAAkB;GACvB;EACD;EAEA,MAAM,QAAQ,SAAS,IAAI;EAE3B,IAAI,KAAK,QAAQ,GAAG;GACnB,KAAK,WAAW,KAAK;GACrB;EACD;EAEA,IAAI,KAAK,OAAO,MAAM,KAAK,GAAG,KAAK,OAAO;CAC3C;CAEA,IAAI,YAAoB;EACvB,OAAO,KAAK,SAAS,KAAK,OAAO,KAAK;CACvC;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA,AAAS,YAAY,OAAQ,iBAAiB,CAAE;CAGhD,AAAS,SAAS,uBAAuB,KAAK,MAAM,CAAC;CAErD;CAEA;CAEA,yBAAyB,MAAoB;EAC5C,IAAI,SAAS,WAAW,KAAK,QAAQ,GAAG;GACvC,KAAK,WAAW,KAAK,gBAAgB,SAAS,KAAK,GAAG,CAAC;GACvD;EACD;EACA,IAAI,SAAS,cAAc,KAAK,WAAW,GAAG,KAAK,WAAW,IAAI;EAElE,KAAK,OAAO,OAAO,KAAK,gBAAgB,SAAS,KAAK,GAAG,GAAG,EAAE,WAAW,SAAS,QAAQ,CAAC;EAC3F,IAAI,qBAAqB,IAAI,IAAI,KAAK,KAAK,QAAQ,GAAG,KAAK,mBAAmB;EAC9E,KAAK,OAAO;CACb;CAEA,AAAS,oBAA0B;EAClC,KAAK,kBACJ,WACA,UACA,eACA,SACA,OACA,YACA,OACA,WACA,UACA,WACA,cACA,QACA,SACA,aACA,UACA,eACA,eACA,qBACA,WACA,cACA,OACD;EACA,MAAM,kBAAkB;CACzB;CAEA,AAAU,cAAoB;EAC7B,KAAK,WAAW,QAAQ;CACzB;CAEA,AAAU,aACT,SACA,QACA,MACO;EACP,MAAM,SAAS,QAAQ,cAA2B,WAAW;EAE7D,IAAI,QAAQ,KAAK,WAAW;GAAE,QAAQ;GAAS,QAAQ,IAAI,QAAQ,MAAM;EAAE;EAC3E,KAAK,YAAY,IAAI,cACpB;GACC,eAAe,KAAK;GACpB,cAAc,KAAK;GACnB,WAAW,SAAS;IACnB,KAAK,cAAc,IAAI,MAAM,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD;GACA,mBAAmB,KAAK;GACxB,SAAS,eAAe,KAAK;GAC7B,iBAAiB,KAAK,WAAW;GACjC,gBAAgB,QAAQ,YAAY;IACnC,KAAK,cAAc,QAAQ,OAAO;GACnC;GACA,gBAAgB,KAAK,UAAU;GAC/B,QAAQ,SAAS,KAAK,MAAM,IAAI;GAChC,kBAAkB,KAAK,WAAW;GAClC,eAAe,KAAK;GACpB,OAAO,KAAK;GAEZ,QAAQ,UAAU,KAAK,eAAe,OAAO,WAAU,CAAE,IAAI;GAC7D,cAAc;IACb,KAAK,OAAO;GACb;GACA,oBAAoB;IACnB,KAAK,YAAY;GAClB;GACA,uBAAuB,KAAK,gBAAgB;GAC5C,oBAAoB,KAAK,aAAa;GACtC,iBAAiB,KAAK,UAAU;GAChC,cAAc,OAAO,SAAS;IAC7B,KAAK,YAAY,OAAO,IAAI;GAC7B;EACD,GACA,OACD;EACA,KAAK,UAAU,KAAK,QAAQ,IAAI;EAChC,QAAQ,iBACP,cACC,UAAU;GACV,KAAK,QAAQ,KAAK,KAAK;EACxB,GACA,EAAE,OAAO,CACV;CACD;CAEA,AAAU,UACT,SACA,QACA,EAAE,gBAAgB,MAAM,OAAO,WACxB;EACP,KAAK,SAAS,iBACb,SACA;GACC;GACA,eAAe;IACd,KAAK,cAAc,IAAI,MAAM,eAAe,EAAE,SAAS,KAAK,CAAC,CAAC;GAC/D;GACA,eAAe;IACd,KAAK,OAAO;GACb;GACA,eAAe,KAAK,WAAW,KAAK,KAAK,YAAY;GACrD,YAAY;IACX,IAAI,CAAC,KAAK,SAAS;IAEnB,KAAK,cAAc;IACnB,MAAM;GACP;GACA;EACD,GACA,MACD;CACD;CAEA,AAAU,qBAA4D,CAEtE;CAEA,AAAU,cAAuC;EAChD,OAAO;CACR;CAMA,AAAU,MAAM,MAAuB;EACtC,MAAM,WAAW,KAAK,OAAO;EAE7B,IAAI,KAAK,OAAO,MAAM,IAAI,GAAG,KAAK,OAAO;EACzC,IAAI,KAAK,OAAO,UAAU,UAAU,OAAO;EAE3C,KAAK,cAAc,IAAI,MAAM,SAAS;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;EAExE,OAAO;CACR;CAEA,AAAU,aAAsB;EAC/B,OAAO;CACR;CAEA,AAAU,UAAwB;EACjC,OAAO,KAAK,OAAO,QAAQ;CAC5B;CAEA,AAAU,cAAsB;EAC/B,OAAO,KAAK,UAAU,KAAK,QAAQ,CAAC,CAAC,OAAO;CAC7C;CAEA,AAAU,qBAA2B,CAErC;CAEA,AAAU,eAAuB;EAChC,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,KAAK,OAAO;CAC3C;CAEA,AAAU,SAAe;EACxB,IAAI,KAAK,QAAQ,GAAG,KAAK,eAAe;EACxC,KAAK,OAAO,OAAO;CACpB;CAEA,AAAU,gBAAsB;EAC/B,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,SAAS;EAEd,MAAM,EAAE,QAAQ,UAAU,KAAK,OAAO;EACtC,MAAM,OAAO,UAAU,SAAY,SAAY,KAAK,SAAS,KAAK;EAElE,QAAQ,OAAO,KAAK;GAAE,QAAQ,QAAQ;GAAQ,QAAQ,UAAU,SAAS;GAAI;EAAK,CAAC;CACpF;CAEA,AAAU,aAAa,OAAqB;EAC3C,KAAK,QAAQ,OAAO,KAAK;CAC1B;CAEA,AAAU,eAAqB,CAE/B;CAEA,AAAU,kBAA2B;EACpC,OAAO;CACR;CAEA,AAAU,aAAyB;EAClC,MAAM,OAAO,KAAK,WAAW,UAAU;EAEvC,OAAO;GACN;GACA,YAAY,KAAK,YAAY;GAC7B,QAAQ,SAAS,SAAY,KAAK,OAAO,QAAS,KAAK,WAAW;EACnE;CACD;CAEA,AAAU,eAAmC,CAE7C;CAEA,AAAU,YAAgC,CAE1C;CAEA,AAAU,SAAS,SAAsB,UAAwC;EAChF,OAAO,KAAK,IAAI,GAAG,eAAe,iBAAiB,OAAO,GAAG,UAAU,gBAAgB,CAAC;CACzF;CAEA,aAAqB;EACpB,MAAM,QAAQ,KAAK,OAAO;EAE1B,IAAI,KAAK,cAAc,OAAO,KAAK,aAAa,KAAK;EAGrD,IAAI,CAAC,KAAK,aAAa,OAAO,OAAO,KAAK;EAE1C,MAAM,OAAO,KAAK;EAElB,IAAe,CAAC,OAAO,SAAS,KAAK,YAAY,IAAI,CAAC,GACrD,QAAQ,KACP,IAAI,KAAK,UAAU,0BAA0B,KAAK,iDACnD;EAGD,OAAO;CACR;CAEA,aAAsB;EACrB,OAAO,KAAK,WAAW,UAAU,MAAM;CACxC;CAEA,UAAmB;EAClB,OAAO,KAAK,WAAW,OAAO,MAAM;CACrC;CAEA,cAAuB;EACtB,OAAO,KAAK,WAAW,WAAW,MAAM;CACzC;CAEA,YAAY,SAAsB,aAA+C;EAChF,KAAK,eAAe,OAAO,KAAK,OAAO,KAAK,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;EACxE,IAAI,KAAK,WAAW,GAAG;GACtB,KAAK,MAAM,QAAQ,gBAAgB,QAAQ,gBAAgB,IAAI;GAC/D,KAAK,cAAc,SAAS,KAAK;GACjC,KAAK,cAAc;GACnB;EACD;EAEA,MAAM,CAAC,KAAK,QAAQ,KAAK,QAAQ,CAAC,CAAC;EAEnC,eAAe,SAAS,QAAQ,KAAK,YAAY,CAAC;EAClD,eAAe,SAAS,iBAAiB,OAAO,GAAG,CAAC;EACpD,eAAe,SAAS,iBAAiB,OAAO,IAAI,CAAC;EACrD,eAAe,SAAS,iBAAiB,OAAO,KAAK,WAAW,CAAC,CAAC;EAClE,IAAI,aAAa,eAAe,SAAS,oBAAoB,WAAW;EACxE,eACC,SACA,mBACC,KAAK,sBAAsB,KAAK,aAAY,GAAI,KAAK,WAAW,CAAC,CACnE;EACA,KAAK,cAAc,SAAS,IAAI;EAChC,KAAK,gBAAgB,OAAO;EAC5B,KAAK,cAAc;CACpB;CAEA,iBAAuB;EAEtB,IAAI,KAAK,WAAW,KAAK,KAAK,YAAY,GAAG,KAAK,QAAQ,MAAM;EAChE,KAAK,KAAK;EACV,KAAK,YACJ,aACA,CAAC,KAAK,WAAW,KAAK,KAAK,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC,CACnF;EACA,KAAK,YAAY,KAAK,YAAY,GAAG,KAAK,mBAAmB,CAAC;CAC/D;CAEA,gBAAgB,SAA4B;EAC3C,MAAM,aAAa,KAAK,WAAW;EAEnC,eAAe,SAAS,iBAAiB,aAAa,SAAS,MAAS;EAExE,MAAM,OAAO,KAAK,aAAa,UAAU,MAAM,OAAO,OAAO;EAE7D,eAAe,SAAS,YAAY,aAAa,SAAY,IAAI;CAClE;CAEA,cAAoB;EACnB,IAAI,KAAK,WAAW,UAAU,MAAM,QAAW,KAAK,UAAU;EAC9D,KAAK,QAAQ,QAAQ;EACrB,KAAK,cAAc;EACnB,KAAK,aAAa;CACnB;CAEA,SAAyD;EACxD,IAAI,KAAK,WAAW,GAAG,OAAO;GAAE,QAAQ;GAAM,OAAO;EAAU;EAE/D,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,UAAU,aAAa,SAAY,KAAK,QAAQ,MAAM;EAE5D,OAAO;GACN,SAAS,cAAc,YAAY,WAAc,KAAK;GACtD,OAAO,WAAW,KAAK,OAAO;EAC/B;CACD;CAEA,QAAmB;EAClB,MAAM,EAAE,QAAQ,UAAU,cAAc;EAExC,OAAO;GACN,GAAI,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;GACzC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;GACjC,KAAK,KAAK;GACV,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,SAAS;GAC7C,WAAW,KAAK;GAChB,YAAY,KAAK,WAAW;GAC5B,KAAK,KAAK;GACV,MAAM,KAAK;GACX,OAAO,KAAK;EACb;CACD;CAEA,aAAqB;EACpB,MAAM,OAAO,KAAK;EAElB,OAAO,SAAS,UAAa,OAAO,IACjC,UAAU,KAAK,MAAM,KAAK,OAAO,QAAQ,IAAI,IAAI,IAAI,IACrD,KAAK,OAAO;CAChB;CAEA,SAAS,OAAuB;EAC/B,OAAO,KAAK,eAAe,KAAK,KAAK,OAAO,KAAK;CAClD;CAEA,WAAW,MAAoB;EAC9B,KAAK,UAAU;EACf,KAAK,aAAa;CACnB;AACD"}
@@ -14,7 +14,7 @@ const detentZonePx = 8;
14
14
  var ValueGestures = class {
15
15
  #claim = new ReadoutClaim(() => {
16
16
  this.#showRevealed();
17
- this.#host.holdChanged();
17
+ this.#host.scrubChanged();
18
18
  });
19
19
  #control;
20
20
  #entry;
@@ -53,7 +53,7 @@ var ValueGestures = class {
53
53
  this.#pointerType = void 0;
54
54
  return;
55
55
  }
56
- const fromValue = host.value();
56
+ const fromValue = host.model.value;
57
57
  host.toggleState("dragging", true);
58
58
  const axis = grab(event);
59
59
  if (!axis) {
@@ -103,9 +103,6 @@ var ValueGestures = class {
103
103
  this.#pointerDrag?.end();
104
104
  this.#endKeyScrub();
105
105
  }
106
- heldFrom() {
107
- return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
108
- }
109
106
  isEditing() {
110
107
  return this.#entry?.isOpen === true;
111
108
  }
@@ -118,6 +115,9 @@ var ValueGestures = class {
118
115
  pointerType() {
119
116
  return this.#pointerType;
120
117
  }
118
+ scrubFrom() {
119
+ return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
120
+ }
121
121
  #bindEntry(signal) {
122
122
  const control = this.#control;
123
123
  const host = this.#host;
@@ -156,13 +156,13 @@ var ValueGestures = class {
156
156
  this.#claim.reveal("keys");
157
157
  this.#showRevealed();
158
158
  this.#keyTo(event, next);
159
- host.holdChanged();
159
+ host.scrubChanged();
160
160
  }, { signal });
161
161
  control.addEventListener("keyup", (event) => {
162
162
  const isMeta = event.key === "Meta";
163
163
  if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
164
164
  if (isMeta && this.#dragging()) return;
165
- if (isMeta || host.mapping().keyTarget(event.key, host.value()) !== void 0) this.#springBack();
165
+ if (isMeta || host.model.mapping().keyTarget(event.key, host.model.value) !== void 0) this.#springBack();
166
166
  }, { signal });
167
167
  control.addEventListener("blur", () => {
168
168
  this.#endKeyScrub();
@@ -172,13 +172,13 @@ var ValueGestures = class {
172
172
  }
173
173
  #commit(next, direction = 0) {
174
174
  const host = this.#host;
175
- const landed = Number.isFinite(next) ? host.land(next, direction) : next;
175
+ const landed = Number.isFinite(next) ? host.model.land(next, direction) : next;
176
176
  if (host.input(landed)) host.dispatch("change");
177
177
  }
178
178
  #concealKeyReveal() {
179
179
  if (!this.#claim.conceal("keys")) return;
180
180
  this.#showRevealed();
181
- this.#host.holdChanged();
181
+ this.#host.scrubChanged();
182
182
  }
183
183
  #crossCancelZone(drag, value) {
184
184
  const host = this.#host;
@@ -196,7 +196,7 @@ var ValueGestures = class {
196
196
  const host = this.#host;
197
197
  const isOutside = drag.outside?.(event) === true;
198
198
  const wasEngaged = drag.state.isEngaged;
199
- const mapping = host.mapping();
199
+ const mapping = host.model.mapping();
200
200
  const { state, value } = stepDrag(mapping, drag.state, {
201
201
  isFine: event.shiftKey,
202
202
  isOutside,
@@ -213,13 +213,13 @@ var ValueGestures = class {
213
213
  const scrub = this.#keyScrub;
214
214
  if (!scrub) return;
215
215
  this.#keyScrub = void 0;
216
- host.holdChanged();
217
- if (host.value() !== scrub.fromValue) host.dispatch("change");
216
+ host.scrubChanged();
217
+ if (host.model.value !== scrub.fromValue) host.dispatch("change");
218
218
  }
219
219
  #keyTarget(key) {
220
220
  const host = this.#host;
221
221
  if (resetKeys.has(key)) return host.default();
222
- return host.mapping().keyTarget(key, host.value(), host.keyStep());
222
+ return host.model.mapping().keyTarget(key, host.model.value, host.keyStep());
223
223
  }
224
224
  #keyTo(event, next) {
225
225
  const host = this.#host;
@@ -228,7 +228,7 @@ var ValueGestures = class {
228
228
  return;
229
229
  }
230
230
  this.#endKeyScrub();
231
- this.#commit(next, next > host.value() ? 1 : -1);
231
+ this.#commit(next, next > host.model.value ? 1 : -1);
232
232
  }
233
233
  #lift(drag, event, entry) {
234
234
  const host = this.#host;
@@ -248,16 +248,16 @@ var ValueGestures = class {
248
248
  clearTimeout(drag.revealTimer);
249
249
  this.#claim.conceal("drag");
250
250
  host.toggleState("cancelling", false);
251
- host.holdChanged();
251
+ host.scrubChanged();
252
252
  this.#showRevealed();
253
- if (host.value() !== drag.fromValue) host.dispatch("change");
253
+ if (host.model.value !== drag.fromValue) host.dispatch("change");
254
254
  this.#springBack();
255
255
  this.#pointerType = void 0;
256
256
  }
257
257
  #renderDragReveal(drag) {
258
258
  if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
259
259
  else this.#claim.conceal("drag");
260
- this.#host.holdChanged();
260
+ this.#host.scrubChanged();
261
261
  this.#showRevealed();
262
262
  }
263
263
  #reset() {
@@ -273,7 +273,7 @@ var ValueGestures = class {
273
273
  if (this.#keyScrub?.key !== key) {
274
274
  this.#endKeyScrub();
275
275
  this.#keyScrub = {
276
- fromValue: host.value(),
276
+ fromValue: host.model.value,
277
277
  key
278
278
  };
279
279
  }
@@ -290,14 +290,14 @@ var ValueGestures = class {
290
290
  const host = this.#host;
291
291
  const target = host.springTarget();
292
292
  if (target === void 0) return;
293
- if (host.mapping().snap(target) !== host.value()) host.toggleState("springing", true);
293
+ if (host.model.mapping().snap(target) !== host.model.value) host.toggleState("springing", true);
294
294
  this.#commit(target);
295
295
  }
296
296
  #startDrag(axis, event) {
297
297
  const host = this.#host;
298
- const mapping = host.mapping();
298
+ const mapping = host.model.mapping();
299
299
  const detent = host.detent();
300
- const value = host.value();
300
+ const value = host.model.value;
301
301
  const start = {
302
302
  from: value,
303
303
  position: axis.position(event),
@@ -316,7 +316,7 @@ var ValueGestures = class {
316
316
  });
317
317
  }
318
318
  #withinLimit(mapping, state) {
319
- const limit = this.#host.limit();
319
+ const limit = this.#host.model.limit();
320
320
  if (!limit || mapping.isWrapping) return state;
321
321
  const [low, high] = limit;
322
322
  return {