@xsynaptic/sonic-ui 0.6.0 → 0.6.1

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 (59) hide show
  1. package/dist/default/elements/button.js +3 -2
  2. package/dist/default/elements/dial.js +5 -1
  3. package/dist/default/elements/envelope.js +14 -2
  4. package/dist/default/elements/meter.js +1 -1
  5. package/dist/default/elements/radio-group.js +3 -2
  6. package/dist/default/elements/readout-claim.js +0 -29
  7. package/dist/default/elements/scale.js +43 -0
  8. package/dist/default/elements/slider.js +16 -3
  9. package/dist/default/elements/sonic-element.js +2 -67
  10. package/dist/default/elements/switch.js +1 -1
  11. package/dist/default/elements/value-element.js +35 -83
  12. package/dist/default/elements/wavestrip.js +42 -4
  13. package/dist/default/elements/xy.js +2 -1
  14. package/dist/default/lib/mirror-children.js +64 -0
  15. package/dist/default/lib/owned-control.js +10 -0
  16. package/dist/default/lib/time-regions.js +4 -1
  17. package/dist/dev/elements/button.js +3 -2
  18. package/dist/dev/elements/button.js.map +1 -1
  19. package/dist/dev/elements/dial.js +5 -1
  20. package/dist/dev/elements/dial.js.map +1 -1
  21. package/dist/dev/elements/envelope.js +14 -2
  22. package/dist/dev/elements/envelope.js.map +1 -1
  23. package/dist/dev/elements/meter.js +1 -1
  24. package/dist/dev/elements/radio-group.js +3 -2
  25. package/dist/dev/elements/radio-group.js.map +1 -1
  26. package/dist/dev/elements/readout-claim.js +0 -29
  27. package/dist/dev/elements/readout-claim.js.map +1 -1
  28. package/dist/dev/elements/scale.js +44 -0
  29. package/dist/dev/elements/scale.js.map +1 -0
  30. package/dist/dev/elements/slider.d.ts +3 -0
  31. package/dist/dev/elements/slider.js +16 -3
  32. package/dist/dev/elements/slider.js.map +1 -1
  33. package/dist/dev/elements/sonic-element.d.ts +0 -8
  34. package/dist/dev/elements/sonic-element.js +3 -90
  35. package/dist/dev/elements/sonic-element.js.map +1 -1
  36. package/dist/dev/elements/switch.js +1 -1
  37. package/dist/dev/elements/value-element.d.ts +3 -5
  38. package/dist/dev/elements/value-element.js +35 -83
  39. package/dist/dev/elements/value-element.js.map +1 -1
  40. package/dist/dev/elements/wavestrip.d.ts +4 -0
  41. package/dist/dev/elements/wavestrip.js +42 -4
  42. package/dist/dev/elements/wavestrip.js.map +1 -1
  43. package/dist/dev/elements/xy.js +2 -1
  44. package/dist/dev/elements/xy.js.map +1 -1
  45. package/dist/dev/lib/mirror-children.js +80 -0
  46. package/dist/dev/lib/mirror-children.js.map +1 -0
  47. package/dist/dev/lib/owned-control.js +23 -0
  48. package/dist/dev/lib/owned-control.js.map +1 -0
  49. package/dist/dev/lib/time-regions.js +4 -1
  50. package/dist/dev/lib/time-regions.js.map +1 -1
  51. package/dist/skins/flat.css +2 -1
  52. package/dist/styles/button.css +21 -0
  53. package/dist/styles/envelope.css +30 -30
  54. package/dist/styles/led.css +18 -2
  55. package/dist/styles/material.css +72 -26
  56. package/dist/styles/panel.css +2 -1
  57. package/dist/styles/switch.css +10 -7
  58. package/dist/styles/xy.css +14 -30
  59. package/package.json +1 -1
@@ -1,8 +1,9 @@
1
- import { placeChildren, requireChild, template } from "../lib/render.js";
2
1
  import { writeAttribute } from "../lib/write-attribute.js";
3
2
  import { SonicFormElement } from "./form-element.js";
4
3
  import { copyNode } from "../lib/copy-node.js";
5
4
  import { bindHold } from "../lib/hold.js";
5
+ import { placeChildren, requireChild, template } from "../lib/render.js";
6
+ import { mirrorChildren } from "../lib/mirror-children.js";
6
7
 
7
8
  //#region src/elements/button.ts
8
9
  const renderButton = template(`
@@ -88,7 +89,7 @@ var SonicButton = class extends SonicFormElement {
88
89
  const button = this.#button;
89
90
  const cap = requireChild(button, ".sonic-button-cap", HTMLSpanElement);
90
91
  this.upgradeProperties("armed", "busy", "defaultPressed", "momentary", "softDisabled", "latching", "pressed", "value");
91
- this.mirrorChildren({
92
+ mirrorChildren(this, {
92
93
  control: button,
93
94
  copy: copyNode,
94
95
  place: (copies) => {
@@ -1,5 +1,6 @@
1
1
  import { requireChild, template } from "../lib/render.js";
2
2
  import { SonicValueElement } from "./value-element.js";
3
+ import { bindScale } from "./scale.js";
3
4
 
4
5
  //#region src/elements/dial.ts
5
6
  const renderDial = template(`
@@ -54,7 +55,10 @@ var SonicDial = class extends SonicValueElement {
54
55
  }
55
56
  connect(signal) {
56
57
  const dial = this.#dial;
57
- this.bindScale(dial, requireChild(dial, ".sonic-dial-scale", HTMLDivElement), signal);
58
+ bindScale(this, {
59
+ control: dial,
60
+ marks: requireChild(dial, ".sonic-dial-scale", HTMLDivElement)
61
+ }, signal);
58
62
  this.render();
59
63
  this.checkStyles(dial, "dial.css");
60
64
  this.bindGestures(dial, signal, () => ({
@@ -1,6 +1,6 @@
1
- import { requireChild, template } from "../lib/render.js";
2
1
  import { writeAttribute } from "../lib/write-attribute.js";
3
2
  import { SonicElement } from "./sonic-element.js";
3
+ import { requireChild, template } from "../lib/render.js";
4
4
  import { ReadoutClaim } from "./readout-claim.js";
5
5
  import { Readout } from "./readout.js";
6
6
  import { dragThresholdPx } from "../lib/drag-step.js";
@@ -44,6 +44,7 @@ function stageParts(envelope, hook, stages) {
44
44
  }
45
45
  const renderEnvelope = template(`
46
46
  <div class="sonic-envelope" aria-hidden="true">
47
+ <div class="sonic-envelope-bracket"></div>
47
48
  <svg class="sonic-envelope-graph" preserveAspectRatio="none" viewBox="0 0 1 1">
48
49
  <path class="sonic-envelope-fill" />
49
50
  <rect class="sonic-envelope-fill sonic-envelope-floor" y="1" />
@@ -127,15 +128,17 @@ var SonicEnvelope = class extends SonicElement {
127
128
  this.reflect("sustain", id);
128
129
  }
129
130
  #bindings = /* @__PURE__ */ new Map();
131
+ #envelope = renderEnvelope();
132
+ #bracket = requireChild(this.#envelope, ".sonic-envelope-bracket", HTMLDivElement);
130
133
  #claim = new ReadoutClaim(() => {
131
134
  this.#renderReadout();
132
135
  });
133
- #envelope = renderEnvelope();
134
136
  #curveHandles = stageParts(this.#envelope, "sonic-envelope-curve", curveStages);
135
137
  #fill = requireChild(this.#envelope, ".sonic-envelope-fill", SVGElement);
136
138
  #floor = requireChild(this.#envelope, ".sonic-envelope-floor", SVGElement);
137
139
  #graph = requireChild(this.#envelope, ".sonic-envelope-graph", SVGElement);
138
140
  #handles = stageParts(this.#envelope, "sonic-envelope-handle", timeStages);
141
+ #held;
139
142
  #isDriving = false;
140
143
  #line = requireChild(this.#envelope, ".sonic-envelope-line", SVGElement);
141
144
  #pointerDrag;
@@ -232,8 +235,15 @@ var SonicEnvelope = class extends SonicElement {
232
235
  at: shape.curveHandles.find((point) => point.stage === stage),
233
236
  drives: this.#curveDrives(stage)
234
237
  });
238
+ this.#drawBracket();
235
239
  this.#renderReadout();
236
240
  }
241
+ #drawBracket() {
242
+ const part = this.#held;
243
+ if (!part) return;
244
+ for (const name of ["--_sonic-envelope-x", "--_sonic-envelope-y"]) this.#bracket.style.setProperty(name, part.style.getPropertyValue(name));
245
+ writeAttribute(this.#bracket, "data-sonic-part", part.classList.contains("sonic-envelope-curve") ? "curve" : void 0);
246
+ }
237
247
  #drawPoint(part, point) {
238
248
  const { at, drives } = point;
239
249
  writeAttribute(part, "hidden", at ? void 0 : "");
@@ -260,6 +270,8 @@ var SonicEnvelope = class extends SonicElement {
260
270
  if (!pressed || pressed.drives.length === 0) return void 0;
261
271
  const { drives, isFlipped, part } = pressed;
262
272
  this.#claim.press(true);
273
+ this.#held = part;
274
+ this.#drawBracket();
263
275
  const box = this.#graph.getBoundingClientRect();
264
276
  const travel = {
265
277
  x: Math.max(1, box.width * this.#shape.share),
@@ -1,6 +1,6 @@
1
1
  import { toNumber } from "../lib/math.js";
2
- import { template } from "../lib/render.js";
3
2
  import { SonicElement } from "./sonic-element.js";
3
+ import { template } from "../lib/render.js";
4
4
  import { parseNumberList } from "../lib/number-list.js";
5
5
  import { linearTaper } from "../lib/taper.js";
6
6
  import { ballisticsRates, fall, rise, stepNeedle } from "../lib/ballistics.js";
@@ -1,7 +1,8 @@
1
- import { placeChildren } from "../lib/render.js";
2
1
  import { writeAttribute } from "../lib/write-attribute.js";
3
2
  import { SonicFormElement } from "./form-element.js";
4
3
  import { copyNode } from "../lib/copy-node.js";
4
+ import { placeChildren } from "../lib/render.js";
5
+ import { mirrorChildren } from "../lib/mirror-children.js";
5
6
  import { bindDrag } from "../lib/pointer-drag.js";
6
7
 
7
8
  //#region src/elements/radio-group.ts
@@ -48,7 +49,7 @@ var SonicRadioGroupElement = class extends SonicFormElement {
48
49
  connect(signal) {
49
50
  const group = this.group;
50
51
  this.upgradeProperties("value");
51
- this.mirrorChildren({
52
+ mirrorChildren(this, {
52
53
  control: group,
53
54
  copy: copyNode,
54
55
  isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
@@ -6,10 +6,7 @@ var ReadoutClaim = class {
6
6
  return this.#isDragging || this.#keyTimer !== void 0;
7
7
  }
8
8
  #holdTimer;
9
- #hover;
10
- #isDismissed = false;
11
9
  #isDragging = false;
12
- #isEditing = false;
13
10
  #keyTimer;
14
11
  #onLapse;
15
12
  constructor(onLapse) {
@@ -27,21 +24,6 @@ var ReadoutClaim = class {
27
24
  this.#keyTimer = void 0;
28
25
  return wasTimed;
29
26
  }
30
- dismiss() {
31
- const wasHovered = this.#hover !== void 0;
32
- this.#hover = void 0;
33
- this.#isDismissed = wasHovered || this.#isDismissed;
34
- return wasHovered;
35
- }
36
- edit(isOpen) {
37
- this.#isEditing = isOpen;
38
- if (isOpen) this.#hover = void 0;
39
- }
40
- hover(value) {
41
- if (value === void 0) this.#isDismissed = false;
42
- if (this.#isDismissed || this.#isEditing || this.isRevealed) return;
43
- this.#hover = value;
44
- }
45
27
  press(isDown) {
46
28
  this.conceal("drag");
47
29
  if (!isDown) {
@@ -54,7 +36,6 @@ var ReadoutClaim = class {
54
36
  }, 250);
55
37
  }
56
38
  reveal(by) {
57
- this.#hover = void 0;
58
39
  if (by === "drag") {
59
40
  this.#isDragging = true;
60
41
  return;
@@ -65,16 +46,6 @@ var ReadoutClaim = class {
65
46
  this.#onLapse();
66
47
  }, keyRevealMs);
67
48
  }
68
- shown(current, isReadout) {
69
- if (this.#isEditing) return {
70
- isOpen: true,
71
- value: void 0
72
- };
73
- return {
74
- isOpen: (this.isRevealed || this.#hover !== void 0) && isReadout,
75
- value: this.#hover ?? current
76
- };
77
- }
78
49
  };
79
50
 
80
51
  //#endregion
@@ -0,0 +1,43 @@
1
+ import { copyNode } from "../lib/copy-node.js";
2
+ import { placeChildren } from "../lib/render.js";
3
+ import { mirrorChildren } from "../lib/mirror-children.js";
4
+ import { linkValue } from "./value-element.js";
5
+
6
+ //#region src/elements/scale.ts
7
+ function scaleValue(mark) {
8
+ const text = mark.dataset.sonicValue?.trim();
9
+ return text ? Number(text) : NaN;
10
+ }
11
+ function scaleMark(original) {
12
+ const mark = copyNode(original);
13
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return void 0;
14
+ if (!Number.isFinite(scaleValue(mark))) return void 0;
15
+ const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
16
+ mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
17
+ return mark;
18
+ }
19
+ function bindScale(element, { control, marks }, signal) {
20
+ const { mapping, watch } = linkValue(element);
21
+ const render = () => {
22
+ const { proportionOf } = mapping();
23
+ for (const mark of marks.children) {
24
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
25
+ const at = String(proportionOf(scaleValue(mark)));
26
+ if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
27
+ }
28
+ };
29
+ mirrorChildren(element, {
30
+ control,
31
+ copy: scaleMark,
32
+ isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
33
+ isPassed: (child) => child instanceof Element && child.matches(".sonic-led"),
34
+ place: (copies) => {
35
+ placeChildren(marks, copies);
36
+ render();
37
+ }
38
+ }, signal);
39
+ signal.addEventListener("abort", watch(render), { once: true });
40
+ }
41
+
42
+ //#endregion
43
+ export { bindScale };
@@ -1,5 +1,7 @@
1
1
  import { requireChild, template } from "../lib/render.js";
2
2
  import { SonicValueElement } from "./value-element.js";
3
+ import { bindScale } from "./scale.js";
4
+ import { sortedRegions } from "../lib/time-regions.js";
3
5
 
4
6
  //#region src/elements/slider.ts
5
7
  function grooveStop(proportion) {
@@ -34,6 +36,13 @@ var SonicSlider = class extends SonicValueElement {
34
36
  "orientation",
35
37
  "spring"
36
38
  ];
39
+ get buffered() {
40
+ return this.#buffered.map(([start, end]) => [start, end]);
41
+ }
42
+ set buffered(regions) {
43
+ this.#buffered = sortedRegions(regions);
44
+ this.render();
45
+ }
37
46
  get groovePress() {
38
47
  return this.getAttribute("groove-press") === "none" ? "none" : "jump";
39
48
  }
@@ -64,15 +73,19 @@ var SonicSlider = class extends SonicValueElement {
64
73
  set spring(isSpring) {
65
74
  this.reflect("spring", isSpring);
66
75
  }
76
+ #buffered = [];
67
77
  #slider = renderSlider();
68
78
  connectedCallback() {
69
- this.upgradeProperties("groovePress", "modulationValue", "modulation", "orientation", "spring");
79
+ this.upgradeProperties("buffered", "groovePress", "modulationValue", "modulation", "orientation", "spring");
70
80
  super.connectedCallback();
71
81
  }
72
82
  connect(signal) {
73
83
  const slider = this.#slider;
74
84
  const cap = requireChild(slider, ".sonic-slider-cap", HTMLDivElement);
75
- this.bindScale(slider, requireChild(slider, ".sonic-slider-scale", HTMLDivElement), signal);
85
+ bindScale(this, {
86
+ control: slider,
87
+ marks: requireChild(slider, ".sonic-slider-scale", HTMLDivElement)
88
+ }, signal);
76
89
  slider.addEventListener("transitionend", (event) => {
77
90
  if (event.propertyName === "--_sonic-slider-value") this.toggleState("springing", false);
78
91
  }, { signal });
@@ -113,7 +126,7 @@ var SonicSlider = class extends SonicValueElement {
113
126
  };
114
127
  }
115
128
  #renderBuffered(slider) {
116
- const buffered = this.buffered;
129
+ const buffered = this.#buffered;
117
130
  if (buffered.length === 0) {
118
131
  slider.style.removeProperty("--_sonic-slider-buffered-regions");
119
132
  return;
@@ -1,5 +1,5 @@
1
1
  import { toNumber } from "../lib/math.js";
2
- import { attachSlots } from "../lib/render.js";
2
+ import { appendOnce } from "../lib/owned-control.js";
3
3
  import { writeAttribute } from "../lib/write-attribute.js";
4
4
 
5
5
  //#region src/elements/sonic-element.ts
@@ -23,7 +23,6 @@ var SonicElement = class extends HTMLElement {
23
23
  }
24
24
  internals = "attachInternals" in HTMLElement.prototype ? this.attachInternals() : void 0;
25
25
  #connection;
26
- #slots;
27
26
  blur() {
28
27
  this.#focused()?.blur();
29
28
  }
@@ -77,8 +76,7 @@ var SonicElement = class extends HTMLElement {
77
76
  }
78
77
  keepControl(control, signal) {
79
78
  const keep = () => {
80
- this.#appendOnce(control);
81
- this.#checkChildren([...this.childNodes].filter((child) => child !== control));
79
+ appendOnce(this, control);
82
80
  };
83
81
  const observer = new MutationObserver(keep);
84
82
  keep();
@@ -87,50 +85,6 @@ var SonicElement = class extends HTMLElement {
87
85
  observer.disconnect();
88
86
  }, { once: true });
89
87
  }
90
- mirrorChildren({ control, copy, isCopied = () => true, isPassed = () => false, place }, signal) {
91
- const slots = this.#slots ?? attachSlots(this);
92
- let copies = /* @__PURE__ */ new Map();
93
- this.#slots = slots;
94
- const mirror = (touched) => {
95
- this.#appendOnce(control);
96
- const children = [...this.childNodes].filter((child) => child !== control);
97
- const slotted = children.filter((child) => child instanceof Element || child instanceof Text);
98
- const kept = copies;
99
- const originals = children.filter((child) => isCopied(child));
100
- this.#checkMirrored(originals);
101
- this.#checkChildren(children.filter((child) => !isCopied(child) && !isPassed(child)));
102
- copies = new Map(originals.map((original) => [original, touched && !touched.has(original) && kept.has(original) ? kept.get(original) : copy(original)]));
103
- place([...copies.values()].filter((made) => made !== void 0));
104
- slots.shown.assign(...slotted.filter((child) => !isCopied(child)), control);
105
- slots.kept.assign(...slotted.filter((child) => isCopied(child)));
106
- };
107
- const watch = () => {
108
- observer.disconnect();
109
- observer.observe(this, { childList: true });
110
- for (const child of this.childNodes) {
111
- if (child === control) continue;
112
- observer.observe(child, {
113
- attributes: true,
114
- characterData: true,
115
- childList: true,
116
- subtree: true
117
- });
118
- }
119
- };
120
- const observer = new MutationObserver((records) => {
121
- const changes = records.filter((record) => this.#isCopiedChange(record, control, isCopied));
122
- if (changes.length === 0) return;
123
- if (changes.some((record) => record.target === this)) {
124
- mirror();
125
- watch();
126
- } else mirror(new Set(changes.map((record) => this.#childHolding(record.target))));
127
- });
128
- mirror();
129
- watch();
130
- signal.addEventListener("abort", () => {
131
- observer.disconnect();
132
- }, { once: true });
133
- }
134
88
  namingLabels() {
135
89
  return [];
136
90
  }
@@ -161,30 +115,11 @@ var SonicElement = class extends HTMLElement {
161
115
  this[name] = value;
162
116
  }
163
117
  }
164
- #appendOnce(control) {
165
- const [hook] = control.classList;
166
- const stale = [...this.children].filter((child) => child !== control && hook !== void 0 && child.classList.contains(hook));
167
- for (const child of stale) child.remove();
168
- if (control.parentNode !== this) this.append(control);
169
- }
170
- #checkChildren(undocumented) {}
171
- #checkMirrored(originals) {}
172
- #childHolding(node) {
173
- let current = node;
174
- while (current && current.parentNode !== this) current = current.parentNode;
175
- return current ?? void 0;
176
- }
177
118
  #focused() {
178
119
  const root = this.getRootNode();
179
120
  const active = root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;
180
121
  return active instanceof HTMLElement && this.contains(active) ? active : void 0;
181
122
  }
182
- #isCopiedChange(record, control, isCopied) {
183
- if (record.target === this) return record.removedNodes.length > 0 || [...record.addedNodes].some((node) => node !== control);
184
- const child = this.#childHolding(record.target);
185
- if (child === void 0) return false;
186
- return isCopied(child) || record.type === "attributes" && record.target === child;
187
- }
188
123
  };
189
124
 
190
125
  //#endregion
@@ -1,7 +1,7 @@
1
1
  import { clamp } from "../lib/math.js";
2
- import { requireChild, template } from "../lib/render.js";
3
2
  import { writeAttribute } from "../lib/write-attribute.js";
4
3
  import { bindHold } from "../lib/hold.js";
4
+ import { requireChild, template } from "../lib/render.js";
5
5
  import { bindDrag } from "../lib/pointer-drag.js";
6
6
  import { SonicRadioGroupElement, isUnder, optionValue } from "./radio-group.js";
7
7
 
@@ -1,8 +1,7 @@
1
1
  import { clamp, toNumber } from "../lib/math.js";
2
- import { placeChildren, requireChild } from "../lib/render.js";
3
2
  import { writeAttribute } from "../lib/write-attribute.js";
4
3
  import { SonicFormElement } from "./form-element.js";
5
- import { copyNode } from "../lib/copy-node.js";
4
+ import { requireChild } from "../lib/render.js";
6
5
  import { ReadoutClaim } from "./readout-claim.js";
7
6
  import { Readout } from "./readout.js";
8
7
  import { ValueEntry } from "./value-entry.js";
@@ -12,7 +11,6 @@ import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
12
11
  import { parseNumberList } from "../lib/number-list.js";
13
12
  import { bindDrag } from "../lib/pointer-drag.js";
14
13
  import { readPxProperty } from "../lib/read-px-property.js";
15
- import { readRegions } from "../lib/time-regions.js";
16
14
  import { resetKeys, valueMapping } from "../lib/value-mapping.js";
17
15
 
18
16
  //#region src/elements/value-element.ts
@@ -37,18 +35,6 @@ const roleAttributes = [
37
35
  "role",
38
36
  "tabindex"
39
37
  ];
40
- function scaleValue(mark) {
41
- const text = mark.dataset.sonicValue?.trim();
42
- return text ? Number(text) : NaN;
43
- }
44
- function scaleMark(original) {
45
- const mark = copyNode(original);
46
- if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return void 0;
47
- if (!Number.isFinite(scaleValue(mark))) return void 0;
48
- const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
49
- mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
50
- return mark;
51
- }
52
38
  let link;
53
39
  function linkValue(element) {
54
40
  return link(element);
@@ -64,6 +50,7 @@ var SonicValueElement = class extends SonicFormElement {
64
50
  "origin",
65
51
  "readout",
66
52
  "step",
53
+ "tabindex",
67
54
  "taper",
68
55
  "value",
69
56
  "positions"
@@ -89,13 +76,6 @@ var SonicValueElement = class extends SonicFormElement {
89
76
  }
90
77
  });
91
78
  }
92
- get buffered() {
93
- return this.#buffered.map(([start, end]) => [start, end]);
94
- }
95
- set buffered(regions) {
96
- this.#buffered = regions ? readRegions(regions).toSorted((first, second) => first[0] - second[0]) : [];
97
- this.render();
98
- }
99
79
  get default() {
100
80
  return this.optionalNumberAttribute("default");
101
81
  }
@@ -220,12 +200,10 @@ var SonicValueElement = class extends SonicFormElement {
220
200
  return this.#textFor(this.#value);
221
201
  }
222
202
  #asked;
223
- #buffered = [];
224
203
  #claim = new ReadoutClaim(() => {
225
204
  this.#renderHold();
226
205
  });
227
206
  #entry;
228
- #escapeWatch;
229
207
  #formatSpokenValue;
230
208
  #formatValue;
231
209
  #instance = String(instanceCount += 1);
@@ -239,7 +217,6 @@ var SonicValueElement = class extends SonicFormElement {
239
217
  #pointerDrag;
240
218
  #positions;
241
219
  #readout;
242
- #scaleMarks;
243
220
  #value = 0;
244
221
  #watchers = /* @__PURE__ */ new Set();
245
222
  attributeChangedCallback(name) {
@@ -258,7 +235,7 @@ var SonicValueElement = class extends SonicFormElement {
258
235
  this.render();
259
236
  }
260
237
  connectedCallback() {
261
- this.upgradeProperties("buffered", "default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
238
+ this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "positions", "detent", "formatValue", "formatSpokenValue", "keyStep", "parseValue", "value");
262
239
  super.connectedCallback();
263
240
  }
264
241
  bindGestures(control, signal, grab) {
@@ -322,19 +299,6 @@ var SonicValueElement = class extends SonicFormElement {
322
299
  this.#concealKeyReveal();
323
300
  }, { once: true });
324
301
  }
325
- bindScale(control, marks, signal) {
326
- this.#scaleMarks = marks;
327
- this.mirrorChildren({
328
- control,
329
- copy: scaleMark,
330
- isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
331
- isPassed: (child) => child instanceof Element && child.matches(".sonic-led"),
332
- place: (copies) => {
333
- placeChildren(marks, copies);
334
- this.#renderScale();
335
- }
336
- }, signal);
337
- }
338
302
  controlOrientation() {}
339
303
  controlRole() {
340
304
  return "slider";
@@ -344,11 +308,6 @@ var SonicValueElement = class extends SonicFormElement {
344
308
  }
345
309
  heldWrite(_next) {}
346
310
  holdChanged() {}
347
- hoverReadout(value) {
348
- this.#claim.hover(value);
349
- this.#renderReadout();
350
- this.#watchEscape(value !== void 0);
351
- }
352
311
  input(next) {
353
312
  const previous = this.#value;
354
313
  this.#write(this.#limit ? clamp(next, ...this.#limit) : next);
@@ -359,6 +318,9 @@ var SonicValueElement = class extends SonicFormElement {
359
318
  }));
360
319
  return true;
361
320
  }
321
+ isEditing() {
322
+ return this.#entry?.isOpen === true;
323
+ }
362
324
  isRevealed() {
363
325
  return this.#claim.isRevealed;
364
326
  }
@@ -384,17 +346,28 @@ var SonicValueElement = class extends SonicFormElement {
384
346
  originValue() {
385
347
  return this.origin ?? this.mapping().bounds[0];
386
348
  }
349
+ previewValue() {}
387
350
  proportionsChanged() {}
388
351
  readModulationValue() {
389
352
  return this.#modulationValue;
390
353
  }
391
354
  readoutValue() {
392
- return this.#claim.shown(this.#value, this.readout).value ?? this.#value;
355
+ return this.#shown().value ?? this.#value;
393
356
  }
394
357
  render() {
395
358
  if (this.isBound()) this.#renderControl();
396
359
  for (const listener of this.#watchers) listener();
397
360
  }
361
+ renderReadout() {
362
+ const readout = this.#readout;
363
+ if (!readout) return;
364
+ const { isOpen, value } = this.#shown();
365
+ readout.bubble.show({
366
+ anchor: readout.anchor,
367
+ isOpen,
368
+ text: value === void 0 ? void 0 : this.#textFor(value)
369
+ });
370
+ }
398
371
  restoreState(state) {
399
372
  this.value = Number(state);
400
373
  }
@@ -440,7 +413,6 @@ var SonicValueElement = class extends SonicFormElement {
440
413
  },
441
414
  text: () => this.#parseValue ? this.valueText : String(this.#value),
442
415
  toggle: (isOpen) => {
443
- this.#claim.edit(isOpen);
444
416
  if (isOpen) this.forwardNaming(input, true);
445
417
  this.toggleState("editing", isOpen);
446
418
  this.render();
@@ -545,7 +517,7 @@ var SonicValueElement = class extends SonicFormElement {
545
517
  if (this.#entry?.isOpen) {
546
518
  for (const name of roleAttributes) control.removeAttribute(name);
547
519
  this.forwardNaming(control, false);
548
- this.#renderReadout();
520
+ this.renderReadout();
549
521
  return;
550
522
  }
551
523
  const [low, high] = this.mapping().bounds;
@@ -557,18 +529,18 @@ var SonicValueElement = class extends SonicFormElement {
557
529
  writeAttribute(control, "aria-valuetext", (this.#formatSpokenValue ?? this.#formatValue)?.(this.#value));
558
530
  this.forwardNaming(control, true);
559
531
  this.#renderDisabled(control);
560
- this.#renderReadout();
532
+ this.renderReadout();
561
533
  }
562
534
  #renderControl() {
563
535
  this.draw();
564
- this.#renderScale();
565
536
  this.toggleState("at-origin", !this.isWrapping() && this.#value === this.mapping().snap(this.originValue()));
566
537
  this.#renderAria(this.focusTarget(), this.controlOrientation());
567
538
  }
568
539
  #renderDisabled(control) {
569
540
  const isDisabled = this.isDisabled();
570
541
  writeAttribute(control, "aria-disabled", isDisabled ? "true" : void 0);
571
- writeAttribute(control, "tabindex", isDisabled ? void 0 : "0");
542
+ const stop = this.getAttribute("tabindex") === "-1" ? "-1" : "0";
543
+ writeAttribute(control, "tabindex", isDisabled ? void 0 : stop);
572
544
  }
573
545
  #renderDragReveal(drag) {
574
546
  if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
@@ -576,28 +548,9 @@ var SonicValueElement = class extends SonicFormElement {
576
548
  this.#renderHold();
577
549
  }
578
550
  #renderHold() {
579
- this.#renderReadout();
551
+ this.renderReadout();
580
552
  this.holdChanged();
581
553
  }
582
- #renderReadout() {
583
- const readout = this.#readout;
584
- if (!readout) return;
585
- const { isOpen, value } = this.#claim.shown(this.#value, this.readout);
586
- readout.bubble.show({
587
- anchor: readout.anchor,
588
- isOpen,
589
- text: value === void 0 ? void 0 : this.#textFor(value)
590
- });
591
- }
592
- #renderScale() {
593
- const marks = this.#scaleMarks?.children ?? [];
594
- const mapping = this.mapping();
595
- for (const mark of marks) {
596
- if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
597
- const at = String(mapping.proportionOf(scaleValue(mark)));
598
- if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
599
- }
600
- }
601
554
  #reset() {
602
555
  this.#commit(this.default ?? NaN);
603
556
  }
@@ -616,6 +569,18 @@ var SonicValueElement = class extends SonicFormElement {
616
569
  }
617
570
  this.input(next);
618
571
  }
572
+ #shown() {
573
+ if (this.isEditing()) return {
574
+ isOpen: true,
575
+ value: void 0
576
+ };
577
+ const isRevealed = this.#claim.isRevealed;
578
+ const preview = isRevealed ? void 0 : this.previewValue();
579
+ return {
580
+ isOpen: (isRevealed || preview !== void 0) && this.readout,
581
+ value: preview ?? this.#value
582
+ };
583
+ }
619
584
  #springBack() {
620
585
  const target = this.springTarget();
621
586
  if (target === void 0) return;
@@ -645,19 +610,6 @@ var SonicValueElement = class extends SonicFormElement {
645
610
  #textFor(value) {
646
611
  return this.#formatValue?.(value) ?? String(value);
647
612
  }
648
- #watchEscape(isHovered) {
649
- if (!isHovered) {
650
- this.#escapeWatch?.abort();
651
- this.#escapeWatch = void 0;
652
- return;
653
- }
654
- if (this.#escapeWatch) return;
655
- const watch = new AbortController();
656
- this.#escapeWatch = watch;
657
- this.ownerDocument.addEventListener("keydown", (event) => {
658
- if (event.key === "Escape" && this.#claim.dismiss()) this.#renderHold();
659
- }, { signal: watch.signal });
660
- }
661
613
  #withinLimit(mapping, state) {
662
614
  const limit = this.#limit;
663
615
  if (!limit || mapping.isWrapping) return state;