@xsynaptic/sonic-ui 0.6.1 → 0.9.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 (105) hide show
  1. package/README.md +2 -0
  2. package/dist/default/define/spectrum.js +7 -0
  3. package/dist/default/define.js +1 -0
  4. package/dist/default/elements/button.js +30 -2
  5. package/dist/default/elements/dial.js +12 -3
  6. package/dist/default/elements/display-element.js +77 -0
  7. package/dist/default/elements/envelope.js +3 -3
  8. package/dist/default/elements/modulation.js +45 -0
  9. package/dist/default/elements/readout-claim.js +8 -3
  10. package/dist/default/elements/slider.js +68 -4
  11. package/dist/default/elements/spectrum.js +230 -0
  12. package/dist/default/elements/value-element.js +114 -317
  13. package/dist/default/elements/value-gestures.js +301 -0
  14. package/dist/default/elements/wave-element.js +59 -9
  15. package/dist/default/elements/waveform.js +61 -12
  16. package/dist/default/elements/wavestrip.js +25 -49
  17. package/dist/default/elements/xy.js +10 -21
  18. package/dist/default/index.js +2 -1
  19. package/dist/default/lib/asked-value.js +21 -0
  20. package/dist/default/lib/canvas-surface.js +10 -4
  21. package/dist/default/lib/frame-timeline.js +2 -2
  22. package/dist/default/lib/hover-preview.js +46 -0
  23. package/dist/default/lib/marker-rider.js +12 -6
  24. package/dist/default/lib/pointer-drag.js +7 -2
  25. package/dist/default/lib/read-ms-property.js +10 -0
  26. package/dist/default/lib/spectrum-columns.js +74 -0
  27. package/dist/default/lib/spectrum-levels.js +55 -0
  28. package/dist/default/lib/strip-scene.js +12 -1
  29. package/dist/default/lib/time-regions.js +5 -1
  30. package/dist/dev/define/spectrum.d.ts +2 -0
  31. package/dist/dev/define/spectrum.js +8 -0
  32. package/dist/dev/define/spectrum.js.map +1 -0
  33. package/dist/dev/define.d.ts +3 -1
  34. package/dist/dev/define.js +1 -0
  35. package/dist/dev/elements/button.d.ts +2 -0
  36. package/dist/dev/elements/button.js +30 -2
  37. package/dist/dev/elements/button.js.map +1 -1
  38. package/dist/dev/elements/dial.js +12 -3
  39. package/dist/dev/elements/dial.js.map +1 -1
  40. package/dist/dev/elements/display-element.d.ts +29 -0
  41. package/dist/dev/elements/display-element.js +78 -0
  42. package/dist/dev/elements/display-element.js.map +1 -0
  43. package/dist/dev/elements/envelope.js +3 -3
  44. package/dist/dev/elements/envelope.js.map +1 -1
  45. package/dist/dev/elements/modulation.js +46 -0
  46. package/dist/dev/elements/modulation.js.map +1 -0
  47. package/dist/dev/elements/readout-claim.js +8 -3
  48. package/dist/dev/elements/readout-claim.js.map +1 -1
  49. package/dist/dev/elements/slider.d.ts +5 -0
  50. package/dist/dev/elements/slider.js +68 -4
  51. package/dist/dev/elements/slider.js.map +1 -1
  52. package/dist/dev/elements/spectrum.d.ts +59 -0
  53. package/dist/dev/elements/spectrum.js +231 -0
  54. package/dist/dev/elements/spectrum.js.map +1 -0
  55. package/dist/dev/elements/value-element.d.ts +13 -14
  56. package/dist/dev/elements/value-element.js +114 -317
  57. package/dist/dev/elements/value-element.js.map +1 -1
  58. package/dist/dev/elements/value-gestures.d.ts +10 -0
  59. package/dist/dev/elements/value-gestures.js +302 -0
  60. package/dist/dev/elements/value-gestures.js.map +1 -0
  61. package/dist/dev/elements/wave-element.d.ts +8 -2
  62. package/dist/dev/elements/wave-element.js +59 -9
  63. package/dist/dev/elements/wave-element.js.map +1 -1
  64. package/dist/dev/elements/waveform.d.ts +12 -4
  65. package/dist/dev/elements/waveform.js +61 -12
  66. package/dist/dev/elements/waveform.js.map +1 -1
  67. package/dist/dev/elements/wavestrip.d.ts +3 -1
  68. package/dist/dev/elements/wavestrip.js +25 -49
  69. package/dist/dev/elements/wavestrip.js.map +1 -1
  70. package/dist/dev/elements/xy.js +10 -21
  71. package/dist/dev/elements/xy.js.map +1 -1
  72. package/dist/dev/index.d.ts +4 -1
  73. package/dist/dev/index.js +2 -1
  74. package/dist/dev/lib/asked-value.js +22 -0
  75. package/dist/dev/lib/asked-value.js.map +1 -0
  76. package/dist/dev/lib/canvas-surface.d.ts +5 -3
  77. package/dist/dev/lib/canvas-surface.js +10 -4
  78. package/dist/dev/lib/canvas-surface.js.map +1 -1
  79. package/dist/dev/lib/frame-timeline.js +2 -2
  80. package/dist/dev/lib/frame-timeline.js.map +1 -1
  81. package/dist/dev/lib/hover-preview.js +47 -0
  82. package/dist/dev/lib/hover-preview.js.map +1 -0
  83. package/dist/dev/lib/marker-rider.js +12 -6
  84. package/dist/dev/lib/marker-rider.js.map +1 -1
  85. package/dist/dev/lib/pointer-drag.js +7 -2
  86. package/dist/dev/lib/pointer-drag.js.map +1 -1
  87. package/dist/dev/lib/read-ms-property.js +11 -0
  88. package/dist/dev/lib/read-ms-property.js.map +1 -0
  89. package/dist/dev/lib/spectrum-columns.js +75 -0
  90. package/dist/dev/lib/spectrum-columns.js.map +1 -0
  91. package/dist/dev/lib/spectrum-levels.js +56 -0
  92. package/dist/dev/lib/spectrum-levels.js.map +1 -0
  93. package/dist/dev/lib/strip-scene.js +12 -1
  94. package/dist/dev/lib/strip-scene.js.map +1 -1
  95. package/dist/dev/lib/time-regions.js +5 -1
  96. package/dist/dev/lib/time-regions.js.map +1 -1
  97. package/dist/styles/button.css +7 -1
  98. package/dist/styles/controls.css +1 -0
  99. package/dist/styles/material.css +36 -3
  100. package/dist/styles/meter.css +22 -18
  101. package/dist/styles/slider.css +42 -5
  102. package/dist/styles/spectrum.css +105 -0
  103. package/dist/styles/waveform.css +48 -16
  104. package/dist/styles/wavestrip.css +26 -5
  105. package/package.json +2 -1
@@ -1,20 +1,15 @@
1
- import { clamp, toNumber } from "../lib/math.js";
1
+ import { clamp, toNumber, trimFloat } from "../lib/math.js";
2
2
  import { writeAttribute } from "../lib/write-attribute.js";
3
3
  import { SonicFormElement } from "./form-element.js";
4
- import { requireChild } from "../lib/render.js";
5
- import { ReadoutClaim } from "./readout-claim.js";
6
4
  import { Readout } from "./readout.js";
7
- import { ValueEntry } from "./value-entry.js";
8
- import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
9
- import { focusByPointer } from "../lib/focus-by-pointer.js";
10
- import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
11
- import { parseNumberList } from "../lib/number-list.js";
12
- import { bindDrag } from "../lib/pointer-drag.js";
13
5
  import { readPxProperty } from "../lib/read-px-property.js";
14
- import { resetKeys, valueMapping } from "../lib/value-mapping.js";
6
+ import { parseNumberList } from "../lib/number-list.js";
7
+ import { valueMapping } from "../lib/value-mapping.js";
8
+ import { ValueGestures } from "./value-gestures.js";
9
+ import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
10
+ import { bindHoverPreview } from "../lib/hover-preview.js";
15
11
 
16
12
  //#region src/elements/value-element.ts
17
- const detentZonePx = 8;
18
13
  const fallbackTravelPx = 160;
19
14
  let instanceCount = 0;
20
15
  const proportionAttributes = /* @__PURE__ */ new Set([
@@ -49,6 +44,7 @@ var SonicValueElement = class extends SonicFormElement {
49
44
  "notched",
50
45
  "origin",
51
46
  "readout",
47
+ "spoken-step",
52
48
  "step",
53
49
  "tabindex",
54
50
  "taper",
@@ -67,7 +63,7 @@ var SonicValueElement = class extends SonicFormElement {
67
63
  element.#limit = bounds;
68
64
  },
69
65
  mapping: () => element.mapping(),
70
- value: () => element.#value,
66
+ value: () => element.#cell.value,
71
67
  watch: (listener) => {
72
68
  element.#watchers.add(listener);
73
69
  return () => {
@@ -95,7 +91,8 @@ var SonicValueElement = class extends SonicFormElement {
95
91
  this.reflect("dimmed", isDimmed);
96
92
  }
97
93
  get doublePress() {
98
- return this.getAttribute("double-press") === "reset" ? "reset" : "entry";
94
+ const gesture = this.getAttribute("double-press");
95
+ return gesture === "none" || gesture === "reset" ? gesture : "entry";
99
96
  }
100
97
  set doublePress(gesture) {
101
98
  this.reflect("double-press", gesture);
@@ -156,6 +153,9 @@ var SonicValueElement = class extends SonicFormElement {
156
153
  set parseValue(parse) {
157
154
  this.#parseValue = parse;
158
155
  }
156
+ get pointerType() {
157
+ return this.#gestures?.pointerType();
158
+ }
159
159
  get positions() {
160
160
  return parseNumberList(this.getAttribute("positions"));
161
161
  }
@@ -168,6 +168,12 @@ var SonicValueElement = class extends SonicFormElement {
168
168
  set readout(isShown) {
169
169
  this.reflect("readout", isShown);
170
170
  }
171
+ get spokenStep() {
172
+ return this.optionalNumberAttribute("spoken-step");
173
+ }
174
+ set spokenStep(value) {
175
+ this.reflect("spoken-step", value);
176
+ }
171
177
  get step() {
172
178
  return this.numberAttribute("step", 1);
173
179
  }
@@ -181,7 +187,7 @@ var SonicValueElement = class extends SonicFormElement {
181
187
  this.reflect("taper", curve);
182
188
  }
183
189
  get value() {
184
- return this.#value;
190
+ return this.#cell.value;
185
191
  }
186
192
  set value(next) {
187
193
  if (next === void 0 || next === null) {
@@ -193,31 +199,27 @@ var SonicValueElement = class extends SonicFormElement {
193
199
  this.heldWrite(value);
194
200
  return;
195
201
  }
196
- this.#write(value);
197
- if (Number.isFinite(value)) this.#asked = value;
202
+ if (setAsked(this.#cell, this.mapping(), value)) this.render();
198
203
  }
199
204
  get valueText() {
200
- return this.#textFor(this.#value);
205
+ return this.#textFor(this.#cell.value);
201
206
  }
202
- #asked;
203
- #claim = new ReadoutClaim(() => {
204
- this.#renderHold();
205
- });
206
- #entry;
207
+ #cell = {
208
+ asked: void 0,
209
+ value: 0
210
+ };
207
211
  #formatSpokenValue;
208
212
  #formatValue;
213
+ #gestures;
214
+ #heldAt;
215
+ #hover;
209
216
  #instance = String(instanceCount += 1);
210
- #isModulated = false;
211
- #keyScrub;
212
217
  #land;
213
218
  #limit;
214
219
  #mapping;
215
- #modulationValue;
216
220
  #parseValue;
217
- #pointerDrag;
218
221
  #positions;
219
222
  #readout;
220
- #value = 0;
221
223
  #watchers = /* @__PURE__ */ new Set();
222
224
  attributeChangedCallback(name) {
223
225
  this.#mapping = void 0;
@@ -227,102 +229,96 @@ var SonicValueElement = class extends SonicFormElement {
227
229
  this.heldWrite(this.numberAttribute("value", this.min));
228
230
  return;
229
231
  }
230
- this.#asked = void 0;
232
+ this.#cell.asked = void 0;
231
233
  }
232
- if (name === "disabled" && this.isDisabled()) this.#endHolds();
233
- this.#value = this.mapping().snap(this.#asked ?? this.numberAttribute("value", this.min));
234
+ if (name === "disabled" && this.isDisabled()) this.#gestures?.end();
235
+ resnap(this.#cell, this.mapping(), this.numberAttribute("value", this.min));
234
236
  if (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();
235
237
  this.render();
236
238
  }
237
239
  connectedCallback() {
238
- this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
240
+ this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "spokenStep", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
239
241
  super.connectedCallback();
240
242
  }
241
243
  bindGestures(control, signal, grab) {
242
- const entry = this.#bindEntry(control, signal);
243
- this.#pointerDrag = bindDrag(control, {
244
- cancel: (drag) => {
245
- if (drag.isKeptOnCancel !== true) this.input(drag.fromValue);
244
+ const bubble = control.querySelector("[popover]");
245
+ if (bubble) this.#readout = {
246
+ anchor: control,
247
+ bubble: new Readout(bubble)
248
+ };
249
+ this.#gestures = new ValueGestures({
250
+ default: () => this.default,
251
+ detent: () => this.detent,
252
+ dispatch: (type) => {
253
+ this.dispatchEvent(new Event(type, { bubbles: true }));
246
254
  },
247
- grab: (event) => {
248
- if (isMenuPress(event) || this.isDisabled() || entry.isOpen) return;
249
- this.toggleState("springing", false);
250
- if (event.pointerType === "touch") event.preventDefault();
251
- focusByPointer(control);
252
- if (isResetPress(event)) {
253
- this.#reset();
254
- return;
255
- }
256
- const fromValue = this.#value;
257
- const axis = grab(event);
258
- if (!axis) return;
259
- const drag = {
260
- ...axis,
261
- fromValue,
262
- isOutside: false,
263
- isRevealed: false,
264
- revealTimer: void 0,
265
- state: this.#startDrag(control, axis, event)
266
- };
267
- drag.revealTimer = setTimeout(() => {
268
- this.#reveal(drag);
269
- }, 250);
270
- return drag;
255
+ doublePress: () => this.doublePress,
256
+ entryId: `sonic-entry-${this.#instance}`,
257
+ entryText: () => this.#parseValue ? this.valueText : String(this.#cell.value),
258
+ forwardNaming: (target, isNamed) => {
259
+ this.forwardNaming(target, isNamed);
271
260
  },
272
- lift: (drag, event) => {
273
- const tap = drag.state.isEngaged ? void 0 : this.tapTarget();
274
- if (tap !== void 0) {
275
- entry.forgetPress();
276
- this.#commit(tap);
277
- return;
278
- }
279
- if (!entry.press(event) || this.springTarget() !== void 0) return;
280
- if (this.doublePress === "reset") this.#reset();
281
- else entry.open();
261
+ holdChanged: () => {
262
+ this.#renderHold();
282
263
  },
283
- move: (drag, event) => {
284
- this.#dragTo(drag, event);
264
+ input: (next) => this.input(next),
265
+ isDisabled: () => this.isDisabled(),
266
+ keyStep: () => this.keyStep,
267
+ land: (target, direction) => this.#land?.(target, direction) ?? target,
268
+ limit: () => this.#limit,
269
+ mapping: () => this.mapping(),
270
+ parse: (text) => (this.#parseValue ?? Number.parseFloat)(text),
271
+ render: () => {
272
+ this.render();
285
273
  },
286
- release: (drag) => {
287
- this.#releaseDrag(drag);
274
+ scrubsKeyRepeat: () => this.scrubsKeyRepeat(),
275
+ springTarget: () => this.springTarget(),
276
+ tapTarget: () => this.tapTarget(),
277
+ toggleState: (state, isOn) => {
278
+ this.toggleState(state, isOn);
288
279
  },
289
- toggle: (isDragging) => {
290
- this.toggleState("dragging", isDragging);
291
- }
280
+ value: () => this.#cell.value
281
+ }, control);
282
+ this.#gestures.bind(signal, grab);
283
+ }
284
+ bindHover(control, signal, { canShow, place, valueAt }) {
285
+ this.#hover = bindHoverPreview(control, {
286
+ canShow,
287
+ dismiss: () => {
288
+ this.render();
289
+ },
290
+ isTaken: () => this.#isEditing() || this.isRevealed(),
291
+ show: () => {
292
+ this.renderReadout();
293
+ place();
294
+ },
295
+ valueAt
292
296
  }, signal);
293
- control.addEventListener("pointercancel", () => {
294
- entry.forgetPress();
295
- }, { signal });
296
- this.#bindKeys(control, entry, signal);
297
- signal.addEventListener("abort", () => {
298
- this.#endKeyScrub();
299
- this.#concealKeyReveal();
300
- }, { once: true });
301
297
  }
302
298
  controlOrientation() {}
303
299
  controlRole() {
304
300
  return "slider";
305
301
  }
306
302
  heldFrom() {
307
- return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
303
+ return this.#gestures?.heldFrom();
304
+ }
305
+ heldWrite(next) {
306
+ this.#heldAt = next;
308
307
  }
309
- heldWrite(_next) {}
310
308
  holdChanged() {}
311
309
  input(next) {
312
- const previous = this.#value;
313
- this.#write(this.#limit ? clamp(next, ...this.#limit) : next);
314
- if (this.#value === previous) return false;
310
+ const previous = this.#cell.value;
311
+ const target = this.#limit ? clamp(next, ...this.#limit) : next;
312
+ if (moveTo(this.#cell, this.mapping(), target)) this.render();
313
+ if (this.#cell.value === previous) return false;
315
314
  this.dispatchEvent(new Event("input", {
316
315
  bubbles: true,
317
316
  composed: true
318
317
  }));
319
318
  return true;
320
319
  }
321
- isEditing() {
322
- return this.#entry?.isOpen === true;
323
- }
324
320
  isRevealed() {
325
- return this.#claim.isRevealed;
321
+ return this.#gestures?.isRevealed() === true;
326
322
  }
327
323
  isWrapping() {
328
324
  return false;
@@ -346,13 +342,13 @@ var SonicValueElement = class extends SonicFormElement {
346
342
  originValue() {
347
343
  return this.origin ?? this.mapping().bounds[0];
348
344
  }
349
- previewValue() {}
350
- proportionsChanged() {}
351
- readModulationValue() {
352
- return this.#modulationValue;
345
+ playback() {
346
+ const from = this.heldFrom();
347
+ return from === void 0 ? this.#cell.value : this.#heldAt ?? from;
353
348
  }
349
+ proportionsChanged() {}
354
350
  readoutValue() {
355
- return this.#shown().value ?? this.#value;
351
+ return this.#shown().value ?? this.#cell.value;
356
352
  }
357
353
  render() {
358
354
  if (this.isBound()) this.#renderControl();
@@ -362,10 +358,11 @@ var SonicValueElement = class extends SonicFormElement {
362
358
  const readout = this.#readout;
363
359
  if (!readout) return;
364
360
  const { isOpen, value } = this.#shown();
361
+ const text = value === void 0 ? void 0 : this.#textFor(value);
365
362
  readout.bubble.show({
366
363
  anchor: readout.anchor,
367
- isOpen,
368
- text: value === void 0 ? void 0 : this.#textFor(value)
364
+ isOpen: isOpen && text !== "",
365
+ text
369
366
  });
370
367
  }
371
368
  restoreState(state) {
@@ -379,142 +376,15 @@ var SonicValueElement = class extends SonicFormElement {
379
376
  travelPx(control, property) {
380
377
  return Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));
381
378
  }
382
- writeModulationValue(control, prefix, next) {
383
- const value = next === void 0 || next === null ? void 0 : toNumber(next);
384
- if (value !== void 0 && !Number.isFinite(value)) return;
385
- this.#modulationValue = value;
386
- if (this.isBound()) this.#writeModulationProportion(control, prefix);
387
- }
388
- writeProportions(control, prefix, modulation) {
389
- const { style } = control;
390
- const mapping = this.mapping();
391
- const value = mapping.proportionOf(this.#value);
392
- const reach = mapping.proportionOf(this.#value + modulation);
393
- const { positionCount } = mapping;
394
- style.setProperty(`--_sonic-${prefix}-value`, String(value));
395
- style.setProperty(`--_sonic-${prefix}-origin`, String(mapping.proportionOf(this.originValue())));
396
- style.setProperty(`--_sonic-${prefix}-modulation-from`, String(Math.min(value, reach)));
397
- style.setProperty(`--_sonic-${prefix}-modulation-to`, String(Math.max(value, reach)));
398
- if (positionCount === void 0) style.removeProperty(`--_sonic-${prefix}-position-count`);
399
- else style.setProperty(`--_sonic-${prefix}-position-count`, String(positionCount));
400
- this.#writeModulationProportion(control, prefix);
401
- }
402
- #bindEntry(control, signal) {
403
- const input = requireChild(control, "input", HTMLInputElement);
404
- const bubble = control.querySelector("[popover]");
405
- input.id = `sonic-entry-${this.#instance}`;
406
- if (bubble) this.#readout = {
407
- anchor: control,
408
- bubble: new Readout(bubble)
409
- };
410
- const entry = new ValueEntry(input, {
411
- commit: (text) => {
412
- this.#commit((this.#parseValue ?? Number.parseFloat)(text));
413
- },
414
- text: () => this.#parseValue ? this.valueText : String(this.#value),
415
- toggle: (isOpen) => {
416
- if (isOpen) this.forwardNaming(input, true);
417
- this.toggleState("editing", isOpen);
418
- this.render();
419
- }
420
- });
421
- this.#entry = entry;
422
- entry.bind(control, signal);
423
- return entry;
424
- }
425
- #bindKeys(control, entry, signal) {
426
- control.addEventListener("keydown", (event) => {
427
- if (this.isDisabled() || event.defaultPrevented || event.target !== control) return;
428
- this.toggleState("springing", false);
429
- if (event.key === "Enter") {
430
- if (this.springTarget() !== void 0) return;
431
- event.preventDefault();
432
- entry.open();
433
- return;
434
- }
435
- const next = this.#keyTarget(event.key);
436
- if (next === void 0) return;
437
- event.preventDefault();
438
- this.#keyTo(event, next);
439
- this.#claim.reveal("keys");
440
- this.#renderHold();
441
- }, { signal });
442
- control.addEventListener("keyup", (event) => {
443
- const isMeta = event.key === "Meta";
444
- if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
445
- if (isMeta && this.#dragging()) return;
446
- if (isMeta || this.mapping().keyTarget(event.key, this.#value) !== void 0) this.#springBack();
447
- }, { signal });
448
- control.addEventListener("blur", () => {
449
- this.#endKeyScrub();
450
- this.#concealKeyReveal();
451
- this.#springBack();
452
- }, { signal });
453
- }
454
- #commit(next, direction = 0) {
455
- const landed = Number.isFinite(next) ? this.#land?.(next, direction) ?? next : next;
456
- if (this.input(landed)) this.dispatchEvent(new Event("change", { bubbles: true }));
457
- }
458
- #concealKeyReveal() {
459
- if (this.#claim.conceal("keys")) this.#renderHold();
460
- }
461
- #dragging() {
462
- return this.#pointerDrag?.current();
463
- }
464
- #dragTo(drag, event) {
465
- const isOutside = drag.outside?.(event) === true;
466
- const wasEngaged = drag.state.isEngaged;
467
- const mapping = this.mapping();
468
- const { state, value } = stepDrag(mapping, drag.state, {
469
- isFine: event.shiftKey,
470
- isOutside,
471
- position: drag.position(event)
472
- });
473
- drag.state = this.#withinLimit(mapping, state);
474
- if (value === void 0) return;
475
- if (!wasEngaged) this.#reveal(drag);
476
- this.input(isOutside ? drag.fromValue : value);
477
- if (isOutside === drag.isOutside) return;
478
- drag.isOutside = isOutside;
479
- this.#renderDragReveal(drag);
480
- }
481
- #endHolds() {
482
- this.#entry?.close(false);
483
- this.#pointerDrag?.end();
484
- this.#endKeyScrub();
485
- }
486
- #endKeyScrub() {
487
- const scrub = this.#keyScrub;
488
- if (!scrub) return;
489
- this.#keyScrub = void 0;
490
- this.#renderHold();
491
- if (this.#value !== scrub.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
379
+ #isEditing() {
380
+ return this.#gestures?.isEditing() === true;
492
381
  }
493
382
  #isHeld() {
494
- return this.#dragging() !== void 0 || this.#keyScrub !== void 0;
495
- }
496
- #keyTarget(key) {
497
- if (resetKeys.has(key)) return this.default;
498
- return this.mapping().keyTarget(key, this.#value, this.keyStep);
499
- }
500
- #keyTo(event, next) {
501
- if (event.repeat && this.scrubsKeyRepeat()) {
502
- this.#scrubKey(event.key, next);
503
- return;
504
- }
505
- this.#endKeyScrub();
506
- this.#commit(next, next > this.#value ? 1 : -1);
507
- }
508
- #releaseDrag(drag) {
509
- clearTimeout(drag.revealTimer);
510
- this.#claim.conceal("drag");
511
- this.#renderHold();
512
- if (this.#value !== drag.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
513
- this.#springBack();
383
+ return this.#gestures?.isHeld() === true;
514
384
  }
515
385
  #renderAria(control, orientation) {
516
- this.writeFormValue(String(this.#value), String(this.#value));
517
- if (this.#entry?.isOpen) {
386
+ this.writeFormValue(String(this.#cell.value), String(this.#cell.value));
387
+ if (this.#isEditing()) {
518
388
  for (const name of roleAttributes) control.removeAttribute(name);
519
389
  this.forwardNaming(control, false);
520
390
  this.renderReadout();
@@ -524,16 +394,17 @@ var SonicValueElement = class extends SonicFormElement {
524
394
  writeAttribute(control, "role", this.controlRole());
525
395
  writeAttribute(control, "aria-valuemin", String(low));
526
396
  writeAttribute(control, "aria-valuemax", String(high));
527
- writeAttribute(control, "aria-valuenow", String(this.#value));
397
+ writeAttribute(control, "aria-valuenow", String(this.#spokenNow()));
528
398
  if (orientation) writeAttribute(control, "aria-orientation", orientation);
529
- writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#value));
399
+ writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#cell.value));
530
400
  this.forwardNaming(control, true);
531
401
  this.#renderDisabled(control);
532
402
  this.renderReadout();
533
403
  }
534
404
  #renderControl() {
405
+ if (this.#isEditing() || this.isRevealed()) this.#hover?.clear();
535
406
  this.draw();
536
- this.toggleState("at-origin", !this.isWrapping() && this.#value === this.mapping().snap(this.originValue()));
407
+ this.toggleState("at-origin", !this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()));
537
408
  this.#renderAria(this.focusTarget(), this.controlOrientation());
538
409
  }
539
410
  #renderDisabled(control) {
@@ -542,104 +413,30 @@ var SonicValueElement = class extends SonicFormElement {
542
413
  const stop = this.getAttribute("tabindex") === "-1" ? "-1" : "0";
543
414
  writeAttribute(control, "tabindex", isDisabled ? void 0 : stop);
544
415
  }
545
- #renderDragReveal(drag) {
546
- if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
547
- else this.#claim.conceal("drag");
548
- this.#renderHold();
549
- }
550
416
  #renderHold() {
417
+ if (this.heldFrom() === void 0) this.#heldAt = void 0;
551
418
  this.renderReadout();
552
419
  this.holdChanged();
553
420
  }
554
- #reset() {
555
- this.#commit(this.default ?? NaN);
556
- }
557
- #reveal(drag) {
558
- if (drag.isRevealed || this.#dragging() !== drag) return;
559
- drag.isRevealed = true;
560
- this.#renderDragReveal(drag);
561
- }
562
- #scrubKey(key, next) {
563
- if (this.#keyScrub?.key !== key) {
564
- this.#endKeyScrub();
565
- this.#keyScrub = {
566
- fromValue: this.#value,
567
- key
568
- };
569
- }
570
- this.input(next);
571
- }
572
421
  #shown() {
573
- if (this.isEditing()) return {
422
+ if (this.#isEditing()) return {
574
423
  isOpen: true,
575
424
  value: void 0
576
425
  };
577
- const isRevealed = this.#claim.isRevealed;
578
- const preview = isRevealed ? void 0 : this.previewValue();
426
+ const isRevealed = this.isRevealed();
427
+ const preview = isRevealed ? void 0 : this.#hover?.value();
579
428
  return {
580
429
  isOpen: (isRevealed || preview !== void 0) && this.readout,
581
- value: preview ?? this.#value
430
+ value: preview ?? this.#cell.value
582
431
  };
583
432
  }
584
- #springBack() {
585
- const target = this.springTarget();
586
- if (target === void 0) return;
587
- if (this.mapping().snap(target) !== this.#value) this.toggleState("springing", true);
588
- this.#commit(target);
589
- }
590
- #startDrag(control, axis, event) {
591
- const mapping = this.mapping();
592
- const detent = this.detent;
593
- const start = {
594
- from: this.#value,
595
- position: axis.position(event),
596
- proportion: axis.fromProportion ?? mapping.proportionOf(this.#value),
597
- thresholdPx: dragThresholdPx(event.pointerType),
598
- travelPx: axis.travelPx
599
- };
600
- if (detent === void 0) return startDrag(mapping, start);
601
- const zonePx = readPxProperty(getComputedStyle(control), "--_sonic-detent-zone", detentZonePx);
602
- return startDrag(mapping, {
603
- ...start,
604
- detent: {
605
- value: detent,
606
- zone: zonePx / axis.travelPx
607
- }
608
- });
433
+ #spokenNow() {
434
+ const step = this.spokenStep;
435
+ return step !== void 0 && step > 0 ? trimFloat(Math.round(this.#cell.value / step) * step) : this.#cell.value;
609
436
  }
610
437
  #textFor(value) {
611
438
  return this.#formatValue?.(value) ?? String(value);
612
439
  }
613
- #withinLimit(mapping, state) {
614
- const limit = this.#limit;
615
- if (!limit || mapping.isWrapping) return state;
616
- const [low, high] = limit;
617
- return {
618
- ...state,
619
- rawProportion: clamp(state.rawProportion, mapping.proportionOf(low), mapping.proportionOf(high))
620
- };
621
- }
622
- #write(next) {
623
- if (!Number.isFinite(next)) return;
624
- const clamped = this.mapping().snap(next);
625
- this.#asked = clamped;
626
- if (clamped === this.#value) return;
627
- this.#value = clamped;
628
- this.render();
629
- }
630
- #writeModulationProportion(control, prefix) {
631
- const modulationValue = this.isWrapping() ? void 0 : this.#modulationValue;
632
- const isModulated = modulationValue !== void 0;
633
- if (isModulated !== this.#isModulated) {
634
- this.#isModulated = isModulated;
635
- this.toggleState("modulated", isModulated);
636
- }
637
- if (modulationValue === void 0) {
638
- control.style.removeProperty(`--_sonic-${prefix}-modulation-value`);
639
- return;
640
- }
641
- control.style.setProperty(`--_sonic-${prefix}-modulation-value`, String(this.mapping().proportionOf(modulationValue)));
642
- }
643
440
  };
644
441
 
645
442
  //#endregion