@xsynaptic/sonic-ui 0.8.0 → 0.10.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 (74) hide show
  1. package/README.md +0 -2
  2. package/dist/default/elements/dial.js +12 -3
  3. package/dist/default/elements/display-element.js +14 -2
  4. package/dist/default/elements/envelope.js +2 -2
  5. package/dist/default/elements/modulation.js +45 -0
  6. package/dist/default/elements/readout-claim.js +8 -3
  7. package/dist/default/elements/slider.js +16 -18
  8. package/dist/default/elements/spectrum.js +1 -0
  9. package/dist/default/elements/value-element.js +108 -330
  10. package/dist/default/elements/value-gestures.js +301 -0
  11. package/dist/default/elements/wave-element.js +5 -26
  12. package/dist/default/elements/waveform.js +6 -2
  13. package/dist/default/elements/wavestrip.js +5 -16
  14. package/dist/default/elements/xy.js +9 -20
  15. package/dist/default/lib/asked-value.js +21 -0
  16. package/dist/default/lib/canvas-surface.js +3 -0
  17. package/dist/default/lib/fill-watch.js +29 -0
  18. package/dist/default/lib/hover-preview.js +8 -3
  19. package/dist/default/lib/marker-rider.js +6 -3
  20. package/dist/default/lib/read-ms-property.js +10 -0
  21. package/dist/dev/elements/dial.js +12 -3
  22. package/dist/dev/elements/dial.js.map +1 -1
  23. package/dist/dev/elements/display-element.d.ts +4 -1
  24. package/dist/dev/elements/display-element.js +14 -2
  25. package/dist/dev/elements/display-element.js.map +1 -1
  26. package/dist/dev/elements/envelope.js +2 -2
  27. package/dist/dev/elements/envelope.js.map +1 -1
  28. package/dist/dev/elements/modulation.js +46 -0
  29. package/dist/dev/elements/modulation.js.map +1 -0
  30. package/dist/dev/elements/readout-claim.js +8 -3
  31. package/dist/dev/elements/readout-claim.js.map +1 -1
  32. package/dist/dev/elements/slider.d.ts +0 -1
  33. package/dist/dev/elements/slider.js +16 -18
  34. package/dist/dev/elements/slider.js.map +1 -1
  35. package/dist/dev/elements/spectrum.d.ts +1 -0
  36. package/dist/dev/elements/spectrum.js +1 -0
  37. package/dist/dev/elements/spectrum.js.map +1 -1
  38. package/dist/dev/elements/value-element.d.ts +11 -13
  39. package/dist/dev/elements/value-element.js +108 -330
  40. package/dist/dev/elements/value-element.js.map +1 -1
  41. package/dist/dev/elements/value-gestures.d.ts +10 -0
  42. package/dist/dev/elements/value-gestures.js +302 -0
  43. package/dist/dev/elements/value-gestures.js.map +1 -0
  44. package/dist/dev/elements/wave-element.js +5 -26
  45. package/dist/dev/elements/wave-element.js.map +1 -1
  46. package/dist/dev/elements/waveform.js +6 -2
  47. package/dist/dev/elements/waveform.js.map +1 -1
  48. package/dist/dev/elements/wavestrip.d.ts +0 -1
  49. package/dist/dev/elements/wavestrip.js +5 -16
  50. package/dist/dev/elements/wavestrip.js.map +1 -1
  51. package/dist/dev/elements/xy.js +9 -20
  52. package/dist/dev/elements/xy.js.map +1 -1
  53. package/dist/dev/lib/asked-value.js +22 -0
  54. package/dist/dev/lib/asked-value.js.map +1 -0
  55. package/dist/dev/lib/canvas-surface.d.ts +1 -0
  56. package/dist/dev/lib/canvas-surface.js +3 -0
  57. package/dist/dev/lib/canvas-surface.js.map +1 -1
  58. package/dist/dev/lib/fill-watch.js +30 -0
  59. package/dist/dev/lib/fill-watch.js.map +1 -0
  60. package/dist/dev/lib/hover-preview.js +8 -3
  61. package/dist/dev/lib/hover-preview.js.map +1 -1
  62. package/dist/dev/lib/marker-rider.js +6 -3
  63. package/dist/dev/lib/marker-rider.js.map +1 -1
  64. package/dist/dev/lib/read-ms-property.js +11 -0
  65. package/dist/dev/lib/read-ms-property.js.map +1 -0
  66. package/dist/styles/envelope.css +1 -1
  67. package/dist/styles/led.css +1 -1
  68. package/dist/styles/material.css +42 -8
  69. package/dist/styles/spectrum.css +5 -0
  70. package/dist/styles/switch.css +42 -33
  71. package/dist/styles/waveform.css +31 -2
  72. package/dist/styles/wavestrip.css +13 -7
  73. package/dist/styles/xy.css +4 -4
  74. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { requireChild, template } from "../lib/render.js";
2
+ import { createModulation } from "./modulation.js";
2
3
  import { SonicValueElement } from "./value-element.js";
3
4
  import { bindScale } from "./scale.js";
4
5
 
@@ -43,12 +44,15 @@ var SonicDial = class extends SonicValueElement {
43
44
  this.reflect("modulation", amount);
44
45
  }
45
46
  get modulationValue() {
46
- return this.readModulationValue();
47
+ return this.#modulation.value();
47
48
  }
48
49
  set modulationValue(value) {
49
- this.writeModulationValue(this.#dial, "dial", value);
50
+ this.#modulation.write(value, this.isBound() ? this.mapping() : void 0);
50
51
  }
51
52
  #dial = renderDial();
53
+ #modulation = createModulation(this.#dial, "dial", (isModulated) => {
54
+ this.toggleState("modulated", isModulated);
55
+ });
52
56
  connectedCallback() {
53
57
  this.upgradeProperties("endless", "modulationValue", "modulation");
54
58
  super.connectedCallback();
@@ -67,7 +71,12 @@ var SonicDial = class extends SonicValueElement {
67
71
  }));
68
72
  }
69
73
  draw() {
70
- this.writeProportions(this.#dial, "dial", this.endless ? 0 : this.modulation);
74
+ this.#modulation.draw({
75
+ mapping: this.mapping(),
76
+ modulation: this.endless ? 0 : this.modulation,
77
+ origin: this.originValue(),
78
+ value: this.value
79
+ });
71
80
  }
72
81
  focusTarget() {
73
82
  return this.#dial;
@@ -1 +1 @@
1
- {"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#dial, 'dial', value);\n\t}\n\n\treadonly #dial = renderDial();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(dial, 'dial.css');\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.writeProportions(this.#dial, 'dial', this.endless ? 0 : this.modulation);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n}\n"],"mappings":";;;;;AAUA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;CAGA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;CAGA,IAAI,kBAAsC;EACzC,OAAO,KAAK,oBAAoB;CACjC;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,qBAAqB,KAAK,OAAO,QAAQ,KAAK;CACpD;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EACZ,KAAK,YAAY,MAAM,UAAU;EACjC,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,iBAAiB,KAAK,OAAO,QAAQ,KAAK,UAAU,IAAI,KAAK,UAAU;CAC7E;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;AACD"}
1
+ {"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-dial-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\treadonly #dial = renderDial();\n\n\treadonly #modulation = createModulation(this.#dial, 'dial', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(dial, 'dial.css');\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.endless ? 0 : this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n}\n"],"mappings":";;;;;;AAWA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;CAGA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;CAGA,IAAI,kBAAsC;EACzC,OAAO,KAAK,YAAY,MAAM;CAC/B;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAS;CAC1E;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,cAAc,iBAAiB,KAAK,OAAO,SAAS,gBAAgB;EAC5E,KAAK,YAAY,aAAa,WAAW;CAC1C,CAAC;CAED,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EACZ,KAAK,YAAY,MAAM,UAAU;EACjC,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,YAAY,KAAK;GACrB,SAAS,KAAK,QAAQ;GACtB,YAAY,KAAK,UAAU,IAAI,KAAK;GACpC,QAAQ,KAAK,YAAY;GACzB,OAAO,KAAK;EACb,CAAC;CACF;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;AACD"}
@@ -6,6 +6,8 @@ export declare abstract class SonicDisplayElement<Colour extends string, Numeric
6
6
  static readonly observedAttributes: string[];
7
7
  get analyser(): AnalyserNode | undefined;
8
8
  set analyser(node: AnalyserNode | null | undefined);
9
+ get fill(): boolean;
10
+ set fill(isFilling: boolean);
9
11
  get max(): number;
10
12
  set max(decibels: number | undefined);
11
13
  get min(): number;
@@ -15,7 +17,8 @@ export declare abstract class SonicDisplayElement<Colour extends string, Numeric
15
17
  protected abstract readonly control: HTMLElement;
16
18
  protected abstract readonly numbers: Record<Numeric, `--_sonic-${string}`>;
17
19
  protected abstract readonly sheet: string;
18
- attributeChangedCallback(): void;
20
+ protected abstract readonly sizeProperty: `--_sonic-${string}`;
21
+ attributeChangedCallback(name: string): void;
19
22
  connectedCallback(): void;
20
23
  push(frame: Float32Array): void;
21
24
  repaint(): void;
@@ -1,10 +1,12 @@
1
1
  import { SonicElement } from "./sonic-element.js";
2
2
  import { bindSurface } from "../lib/canvas-surface.js";
3
+ import { bindFill } from "../lib/fill-watch.js";
3
4
 
4
5
  //#region src/elements/display-element.ts
5
6
  var SonicDisplayElement = class extends SonicElement {
6
7
  static observedAttributes = [
7
8
  ...SonicElement.observedAttributes,
9
+ "fill",
8
10
  "max",
9
11
  "min"
10
12
  ];
@@ -15,6 +17,12 @@ var SonicDisplayElement = class extends SonicElement {
15
17
  this.#analyser = node ?? void 0;
16
18
  this.#surface?.requestFrame();
17
19
  }
20
+ get fill() {
21
+ return this.hasAttribute("fill");
22
+ }
23
+ set fill(isFilling) {
24
+ this.reflect("fill", isFilling);
25
+ }
18
26
  get max() {
19
27
  return this.numberAttribute("max", 0);
20
28
  }
@@ -30,11 +38,13 @@ var SonicDisplayElement = class extends SonicElement {
30
38
  #analyser;
31
39
  #buffer = /* @__PURE__ */ new Float32Array(0);
32
40
  #surface;
33
- attributeChangedCallback() {
41
+ #syncFill;
42
+ attributeChangedCallback(name) {
43
+ if (name === "fill") this.#syncFill?.(this.fill);
34
44
  this.#surface?.invalidate();
35
45
  }
36
46
  connectedCallback() {
37
- this.upgradeProperties("analyser", "max", "min");
47
+ this.upgradeProperties("analyser", "fill", "max", "min");
38
48
  super.connectedCallback();
39
49
  }
40
50
  push(frame) {
@@ -57,6 +67,8 @@ var SonicDisplayElement = class extends SonicElement {
57
67
  signal
58
68
  });
59
69
  this.checkStyles(control, this.sheet);
70
+ this.#syncFill = bindFill(control, this.sizeProperty, signal);
71
+ this.#syncFill(this.fill);
60
72
  }
61
73
  surface() {
62
74
  return this.#surface;
@@ -1 +1 @@
1
- {"version":3,"file":"display-element.js","names":[],"sources":["../../../src/elements/display-element.ts"],"sourcesContent":["import type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { bindSurface } from '#lib/canvas-surface.ts';\n\nexport abstract class SonicDisplayElement<\n\tColour extends string,\n\tNumeric extends string = never,\n> extends SonicElement {\n\tstatic override readonly observedAttributes = [...SonicElement.observedAttributes, 'max', 'min'];\n\n\tget analyser(): AnalyserNode | undefined {\n\t\treturn this.#analyser;\n\t}\n\n\tset analyser(node: AnalyserNode | null | undefined) {\n\t\tthis.#analyser = node ?? undefined;\n\t\tthis.#surface?.requestFrame();\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 0);\n\t}\n\n\tset max(decibels: number | undefined) {\n\t\tthis.reflect('max', decibels);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', -90);\n\t}\n\n\tset min(decibels: number | undefined) {\n\t\tthis.reflect('min', decibels);\n\t}\n\n\tprotected abstract readonly canvas: HTMLCanvasElement;\n\n\tprotected abstract readonly colours: Record<Colour, `--_sonic-${string}`>;\n\n\tprotected abstract readonly control: HTMLElement;\n\n\tprotected abstract readonly numbers: Record<Numeric, `--_sonic-${string}`>;\n\n\tprotected abstract readonly sheet: string;\n\n\t#analyser: AnalyserNode | undefined;\n\n\t#buffer = new Float32Array(0);\n\n\t#surface: Surface | undefined;\n\n\tattributeChangedCallback(): void {\n\t\tthis.#surface?.invalidate();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('analyser', 'max', 'min');\n\t\tsuper.connectedCallback();\n\t}\n\n\tpush(frame: Float32Array): void {\n\t\tthis.receive(frame);\n\t\tthis.#surface?.requestFrame();\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- the wave controls keep their own copy of the surface binding\n\trepaint(): void {\n\t\tthis.#surface?.rebuild();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.keepControl(control, signal);\n\t\tthis.#surface = bindSurface({\n\t\t\tcanvas: this.canvas,\n\t\t\tcolours: this.colours,\n\t\t\tnumbers: this.numbers,\n\t\t\tpaint: (context, frame) => {\n\t\t\t\tthis.#paint(context, frame);\n\t\t\t},\n\t\t\tsignal,\n\t\t});\n\t\tthis.checkStyles(control, this.sheet);\n\t}\n\n\tprotected abstract paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\tframe: SurfaceFrame<Colour, never, Numeric>,\n\t): void;\n\n\tprotected abstract pull(analyser: AnalyserNode, buffer: Float32Array<ArrayBuffer>): void;\n\n\tprotected abstract receive(frame: Float32Array): void;\n\n\tprotected surface(): Surface | undefined {\n\t\treturn this.#surface;\n\t}\n\n\t// The node never signals that sound came back, so every paint pulls\n\t#paint(context: CanvasRenderingContext2D, frame: SurfaceFrame<Colour, never, Numeric>): void {\n\t\tconst analyser = this.isDisabled() ? undefined : this.#analyser;\n\n\t\tif (analyser) {\n\t\t\tif (this.#buffer.length !== analyser.frequencyBinCount) {\n\t\t\t\tthis.#buffer = new Float32Array(analyser.frequencyBinCount);\n\t\t\t}\n\t\t\tthis.pull(analyser, this.#buffer);\n\t\t\tthis.receive(this.#buffer);\n\t\t}\n\t\tthis.paint(context, frame);\n\t\tif (analyser) this.#surface?.requestFrame();\n\t}\n}\n"],"mappings":";;;;AAKA,IAAsB,sBAAtB,cAGU,aAAa;CACtB,OAAyB,qBAAqB;EAAC,GAAG,aAAa;EAAoB;EAAO;CAAK;CAE/F,IAAI,WAAqC;EACxC,OAAO,KAAK;CACb;CAEA,IAAI,SAAS,MAAuC;EACnD,KAAK,YAAY,QAAQ;EACzB,KAAK,UAAU,aAAa;CAC7B;CAGA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,CAAC;CACrC;CAEA,IAAI,IAAI,UAA8B;EACrC,KAAK,QAAQ,OAAO,QAAQ;CAC7B;CAEA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,GAAG;CACvC;CAEA,IAAI,IAAI,UAA8B;EACrC,KAAK,QAAQ,OAAO,QAAQ;CAC7B;CAYA;CAEA,0BAAU,IAAI,aAAa,CAAC;CAE5B;CAEA,2BAAiC;EAChC,KAAK,UAAU,WAAW;CAC3B;CAEA,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,YAAY,OAAO,KAAK;EAC/C,MAAM,kBAAkB;CACzB;CAEA,KAAK,OAA2B;EAC/B,KAAK,QAAQ,KAAK;EAClB,KAAK,UAAU,aAAa;CAC7B;CAGA,UAAgB;EACf,KAAK,UAAU,QAAQ;CACxB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,UAAU,KAAK;EAErB,KAAK,YAAY,SAAS,MAAM;EAChC,KAAK,WAAW,YAAY;GAC3B,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,SAAS,KAAK;GACd,QAAQ,SAAS,UAAU;IAC1B,KAAK,OAAO,SAAS,KAAK;GAC3B;GACA;EACD,CAAC;EACD,KAAK,YAAY,SAAS,KAAK,KAAK;CACrC;CAWA,AAAU,UAA+B;EACxC,OAAO,KAAK;CACb;CAGA,OAAO,SAAmC,OAAmD;EAC5F,MAAM,WAAW,KAAK,WAAW,IAAI,SAAY,KAAK;EAEtD,IAAI,UAAU;GACb,IAAI,KAAK,QAAQ,WAAW,SAAS,mBACpC,KAAK,UAAU,IAAI,aAAa,SAAS,iBAAiB;GAE3D,KAAK,KAAK,UAAU,KAAK,OAAO;GAChC,KAAK,QAAQ,KAAK,OAAO;EAC1B;EACA,KAAK,MAAM,SAAS,KAAK;EACzB,IAAI,UAAU,KAAK,UAAU,aAAa;CAC3C;AACD"}
1
+ {"version":3,"file":"display-element.js","names":[],"sources":["../../../src/elements/display-element.ts"],"sourcesContent":["import type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { bindSurface } from '#lib/canvas-surface.ts';\nimport { bindFill } from '#lib/fill-watch.ts';\n\nexport abstract class SonicDisplayElement<\n\tColour extends string,\n\tNumeric extends string = never,\n> extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t'fill',\n\t\t'max',\n\t\t'min',\n\t];\n\n\tget analyser(): AnalyserNode | undefined {\n\t\treturn this.#analyser;\n\t}\n\n\tset analyser(node: AnalyserNode | null | undefined) {\n\t\tthis.#analyser = node ?? undefined;\n\t\tthis.#surface?.requestFrame();\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget fill(): boolean {\n\t\treturn this.hasAttribute('fill');\n\t}\n\n\tset fill(isFilling: boolean) {\n\t\tthis.reflect('fill', isFilling);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 0);\n\t}\n\n\tset max(decibels: number | undefined) {\n\t\tthis.reflect('max', decibels);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', -90);\n\t}\n\n\tset min(decibels: number | undefined) {\n\t\tthis.reflect('min', decibels);\n\t}\n\n\tprotected abstract readonly canvas: HTMLCanvasElement;\n\n\tprotected abstract readonly colours: Record<Colour, `--_sonic-${string}`>;\n\n\tprotected abstract readonly control: HTMLElement;\n\n\tprotected abstract readonly numbers: Record<Numeric, `--_sonic-${string}`>;\n\n\tprotected abstract readonly sheet: string;\n\n\tprotected abstract readonly sizeProperty: `--_sonic-${string}`;\n\n\t#analyser: AnalyserNode | undefined;\n\n\t#buffer = new Float32Array(0);\n\n\t#surface: Surface | undefined;\n\n\t#syncFill: ((isFilling: boolean) => void) | undefined;\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'fill') this.#syncFill?.(this.fill);\n\t\tthis.#surface?.invalidate();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('analyser', 'fill', 'max', 'min');\n\t\tsuper.connectedCallback();\n\t}\n\n\tpush(frame: Float32Array): void {\n\t\tthis.receive(frame);\n\t\tthis.#surface?.requestFrame();\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- the wave controls keep their own copy of the surface binding\n\trepaint(): void {\n\t\tthis.#surface?.rebuild();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.keepControl(control, signal);\n\t\tthis.#surface = bindSurface({\n\t\t\tcanvas: this.canvas,\n\t\t\tcolours: this.colours,\n\t\t\tnumbers: this.numbers,\n\t\t\tpaint: (context, frame) => {\n\t\t\t\tthis.#paint(context, frame);\n\t\t\t},\n\t\t\tsignal,\n\t\t});\n\t\tthis.checkStyles(control, this.sheet);\n\t\tthis.#syncFill = bindFill(control, this.sizeProperty, signal);\n\t\tthis.#syncFill(this.fill);\n\t}\n\n\tprotected abstract paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\tframe: SurfaceFrame<Colour, never, Numeric>,\n\t): void;\n\n\tprotected abstract pull(analyser: AnalyserNode, buffer: Float32Array<ArrayBuffer>): void;\n\n\tprotected abstract receive(frame: Float32Array): void;\n\n\tprotected surface(): Surface | undefined {\n\t\treturn this.#surface;\n\t}\n\n\t// The node never signals that sound came back, so every paint pulls\n\t#paint(context: CanvasRenderingContext2D, frame: SurfaceFrame<Colour, never, Numeric>): void {\n\t\tconst analyser = this.isDisabled() ? undefined : this.#analyser;\n\n\t\tif (analyser) {\n\t\t\tif (this.#buffer.length !== analyser.frequencyBinCount) {\n\t\t\t\tthis.#buffer = new Float32Array(analyser.frequencyBinCount);\n\t\t\t}\n\t\t\tthis.pull(analyser, this.#buffer);\n\t\t\tthis.receive(this.#buffer);\n\t\t}\n\t\tthis.paint(context, frame);\n\t\tif (analyser) this.#surface?.requestFrame();\n\t}\n}\n"],"mappings":";;;;;AAMA,IAAsB,sBAAtB,cAGU,aAAa;CACtB,OAAyB,qBAAqB;EAC7C,GAAG,aAAa;EAChB;EACA;EACA;CACD;CAEA,IAAI,WAAqC;EACxC,OAAO,KAAK;CACb;CAEA,IAAI,SAAS,MAAuC;EACnD,KAAK,YAAY,QAAQ;EACzB,KAAK,UAAU,aAAa;CAC7B;CAGA,IAAI,OAAgB;EACnB,OAAO,KAAK,aAAa,MAAM;CAChC;CAEA,IAAI,KAAK,WAAoB;EAC5B,KAAK,QAAQ,QAAQ,SAAS;CAC/B;CAGA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,CAAC;CACrC;CAEA,IAAI,IAAI,UAA8B;EACrC,KAAK,QAAQ,OAAO,QAAQ;CAC7B;CAEA,IAAI,MAAc;EACjB,OAAO,KAAK,gBAAgB,OAAO,GAAG;CACvC;CAEA,IAAI,IAAI,UAA8B;EACrC,KAAK,QAAQ,OAAO,QAAQ;CAC7B;CAcA;CAEA,0BAAU,IAAI,aAAa,CAAC;CAE5B;CAEA;CAEA,yBAAyB,MAAoB;EAC5C,IAAI,SAAS,QAAQ,KAAK,YAAY,KAAK,IAAI;EAC/C,KAAK,UAAU,WAAW;CAC3B;CAEA,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,YAAY,QAAQ,OAAO,KAAK;EACvD,MAAM,kBAAkB;CACzB;CAEA,KAAK,OAA2B;EAC/B,KAAK,QAAQ,KAAK;EAClB,KAAK,UAAU,aAAa;CAC7B;CAGA,UAAgB;EACf,KAAK,UAAU,QAAQ;CACxB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,UAAU,KAAK;EAErB,KAAK,YAAY,SAAS,MAAM;EAChC,KAAK,WAAW,YAAY;GAC3B,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,SAAS,KAAK;GACd,QAAQ,SAAS,UAAU;IAC1B,KAAK,OAAO,SAAS,KAAK;GAC3B;GACA;EACD,CAAC;EACD,KAAK,YAAY,SAAS,KAAK,KAAK;EACpC,KAAK,YAAY,SAAS,SAAS,KAAK,cAAc,MAAM;EAC5D,KAAK,UAAU,KAAK,IAAI;CACzB;CAWA,AAAU,UAA+B;EACxC,OAAO,KAAK;CACb;CAGA,OAAO,SAAmC,OAAmD;EAC5F,MAAM,WAAW,KAAK,WAAW,IAAI,SAAY,KAAK;EAEtD,IAAI,UAAU;GACb,IAAI,KAAK,QAAQ,WAAW,SAAS,mBACpC,KAAK,UAAU,IAAI,aAAa,SAAS,iBAAiB;GAE3D,KAAK,KAAK,UAAU,KAAK,OAAO;GAChC,KAAK,QAAQ,KAAK,OAAO;EAC1B;EACA,KAAK,MAAM,SAAS,KAAK;EACzB,IAAI,UAAU,KAAK,UAAU,aAAa;CAC3C;AACD"}
@@ -2,8 +2,8 @@ import { writeAttribute } from "../lib/write-attribute.js";
2
2
  import { SonicElement } from "./sonic-element.js";
3
3
  import { requireChild, template } from "../lib/render.js";
4
4
  import { bindDrag } from "../lib/pointer-drag.js";
5
- import { ReadoutClaim } from "./readout-claim.js";
6
5
  import { Readout } from "./readout.js";
6
+ import { ReadoutClaim, revealDelay } from "./readout-claim.js";
7
7
  import { dragThresholdPx } from "../lib/drag-step.js";
8
8
  import { SonicValueElement, linkValue } from "./value-element.js";
9
9
  import { adsrPath, adsrShape, adsrStages, timeStages } from "../lib/adsr-shape.js";
@@ -269,7 +269,7 @@ var SonicEnvelope = class extends SonicElement {
269
269
  const pressed = this.#pressed(event);
270
270
  if (!pressed || pressed.drives.length === 0) return void 0;
271
271
  const { drives, isFlipped, part } = pressed;
272
- this.#claim.press(true);
272
+ this.#claim.press(true, revealDelay(this.#envelope));
273
273
  this.#held = part;
274
274
  this.#drawBracket();
275
275
  const box = this.#graph.getBoundingClientRect();
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldDragState, FieldMappings, FieldPoint } from '#lib/field.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\n\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { dragThresholdPx } from '#lib/drag-step.ts';\nimport { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from '#lib/field.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.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 HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleDrag {\n\tdrives: Array<Drive & { from: number }>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n\tstate: FieldDragState;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction flip<Point extends FieldPoint>(point: Point, isFlipped: boolean): Point {\n\treturn isFlipped ? { ...point, y: -point.y } : point;\n}\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-bracket\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #bracket = requireChild(this.#envelope, '.sonic-envelope-bracket', HTMLDivElement);\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\t#pointerDrag: PointerDrag<HandleDrag> | undefined;\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\t#signal: AbortSignal | undefined;\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.#signal) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#pointerDrag?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.#signal = signal;\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tthis.checkStyles(envelope, 'envelope.css');\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#signal = undefined;\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bind(): void {\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#pointerDrag = bindDrag(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tfor (const { binding, from } of drag.drives) binding.link.input(from);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#moveDrag(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#claim.press(false);\n\t\t\t\t\tfor (const { binding, from } of drag.drives) {\n\t\t\t\t\t\tif (binding.link.value() === from) continue;\n\n\t\t\t\t\t\tbinding.element.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawBracket();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawBracket(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#bracket.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#bracket,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleDrag | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tconst { drives, isFlipped, part } = pressed;\n\n\t\tthis.#claim.press(true);\n\t\tthis.#held = part;\n\t\tthis.#drawBracket();\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst start = (axis: FieldAxis) => {\n\t\t\tconst link = drives.find((drive) => drive.axis === axis)?.binding.link;\n\t\t\tif (!link) return;\n\n\t\t\tconst mapping = link.mapping();\n\t\t\tconst from = link.value();\n\n\t\t\treturn { from, mapping, proportion: mapping.proportionOf(from), travelPx: travel[axis] };\n\t\t};\n\n\t\treturn {\n\t\t\tdrives: drives.map((drive) => ({ ...drive, from: drive.binding.link.value() })),\n\t\t\tisFlipped,\n\t\t\tpart,\n\t\t\tstate: startFieldDrag({\n\t\t\t\tposition: flip(pointerPosition(event), isFlipped),\n\t\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\t\tx: start('x'),\n\t\t\t\ty: start('y'),\n\t\t\t}),\n\t\t};\n\t}\n\n\t#moveDrag(drag: HandleDrag, event: PointerEvent): void {\n\t\tconst mappings: FieldMappings = {};\n\n\t\tfor (const { axis, binding } of drag.drives) mappings[axis] = binding.link.mapping();\n\n\t\tconst step = stepFieldDrag(mappings, drag.state, flip(pointerMove(event), drag.isFlipped));\n\n\t\tdrag.state = step.state;\n\t\tif (step.state.isEngaged) this.#claim.reveal('drag');\n\t\tthis.#isDriving = true;\n\t\tfor (const { axis, binding } of drag.drives) {\n\t\t\tconst next = step[axis];\n\n\t\t\tif (next !== undefined) binding.link.input(next);\n\t\t}\n\t\tthis.#isDriving = false;\n\t\tthis.#draw();\n\t}\n\n\t#pressed(\n\t\tevent: PointerEvent,\n\t): (Pick<HandleDrag, 'isFlipped' | 'part'> & { drives: Array<Drive> }) | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst link = this.#bindings.get(stage)?.link;\n\n\t\t\tproportions[stage] = link?.mapping().proportionOf(link.value());\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.value();\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.#signal) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#pointerDrag?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldDragState, FieldMappings, FieldPoint } from '#lib/field.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\n\nimport { ReadoutClaim, revealDelay } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { dragThresholdPx } from '#lib/drag-step.ts';\nimport { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from '#lib/field.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.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 HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleDrag {\n\tdrives: Array<Drive & { from: number }>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n\tstate: FieldDragState;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction flip<Point extends FieldPoint>(point: Point, isFlipped: boolean): Point {\n\treturn isFlipped ? { ...point, y: -point.y } : point;\n}\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-bracket\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #bracket = requireChild(this.#envelope, '.sonic-envelope-bracket', HTMLDivElement);\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\t#pointerDrag: PointerDrag<HandleDrag> | undefined;\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\t#signal: AbortSignal | undefined;\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.#signal) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#pointerDrag?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.#signal = signal;\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tthis.checkStyles(envelope, 'envelope.css');\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#signal = undefined;\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bind(): void {\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#pointerDrag = bindDrag(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tfor (const { binding, from } of drag.drives) binding.link.input(from);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#moveDrag(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#claim.press(false);\n\t\t\t\t\tfor (const { binding, from } of drag.drives) {\n\t\t\t\t\t\tif (binding.link.value() === from) continue;\n\n\t\t\t\t\t\tbinding.element.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawBracket();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawBracket(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#bracket.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#bracket,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleDrag | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tconst { drives, isFlipped, part } = pressed;\n\n\t\tthis.#claim.press(true, revealDelay(this.#envelope));\n\t\tthis.#held = part;\n\t\tthis.#drawBracket();\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst start = (axis: FieldAxis) => {\n\t\t\tconst link = drives.find((drive) => drive.axis === axis)?.binding.link;\n\t\t\tif (!link) return;\n\n\t\t\tconst mapping = link.mapping();\n\t\t\tconst from = link.value();\n\n\t\t\treturn { from, mapping, proportion: mapping.proportionOf(from), travelPx: travel[axis] };\n\t\t};\n\n\t\treturn {\n\t\t\tdrives: drives.map((drive) => ({ ...drive, from: drive.binding.link.value() })),\n\t\t\tisFlipped,\n\t\t\tpart,\n\t\t\tstate: startFieldDrag({\n\t\t\t\tposition: flip(pointerPosition(event), isFlipped),\n\t\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\t\tx: start('x'),\n\t\t\t\ty: start('y'),\n\t\t\t}),\n\t\t};\n\t}\n\n\t#moveDrag(drag: HandleDrag, event: PointerEvent): void {\n\t\tconst mappings: FieldMappings = {};\n\n\t\tfor (const { axis, binding } of drag.drives) mappings[axis] = binding.link.mapping();\n\n\t\tconst step = stepFieldDrag(mappings, drag.state, flip(pointerMove(event), drag.isFlipped));\n\n\t\tdrag.state = step.state;\n\t\tif (step.state.isEngaged) this.#claim.reveal('drag');\n\t\tthis.#isDriving = true;\n\t\tfor (const { axis, binding } of drag.drives) {\n\t\t\tconst next = step[axis];\n\n\t\t\tif (next !== undefined) binding.link.input(next);\n\t\t}\n\t\tthis.#isDriving = false;\n\t\tthis.#draw();\n\t}\n\n\t#pressed(\n\t\tevent: PointerEvent,\n\t): (Pick<HandleDrag, 'isFlipped' | 'part'> & { drives: Array<Drive> }) | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst link = this.#bindings.get(stage)?.link;\n\n\t\t\tproportions[stage] = link?.mapping().proportionOf(link.value());\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.value();\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.#signal) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#pointerDrag?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { toNumber } from "../lib/math.js";
2
+
3
+ //#region src/elements/modulation.ts
4
+ function createModulation(control, prefix, toggle) {
5
+ const { style } = control;
6
+ let isModulated = false;
7
+ let modulationValue;
8
+ const writeProportion = (mapping) => {
9
+ const shown = mapping.isWrapping ? void 0 : modulationValue;
10
+ const isShown = shown !== void 0;
11
+ if (isShown !== isModulated) {
12
+ isModulated = isShown;
13
+ toggle(isShown);
14
+ }
15
+ if (shown === void 0) {
16
+ style.removeProperty(`--_sonic-${prefix}-modulation-value`);
17
+ return;
18
+ }
19
+ style.setProperty(`--_sonic-${prefix}-modulation-value`, String(mapping.proportionOf(shown)));
20
+ };
21
+ return {
22
+ draw: ({ mapping, modulation, origin, value }) => {
23
+ const at = mapping.proportionOf(value);
24
+ const reach = mapping.proportionOf(value + modulation);
25
+ const { positionCount } = mapping;
26
+ style.setProperty(`--_sonic-${prefix}-value`, String(at));
27
+ style.setProperty(`--_sonic-${prefix}-origin`, String(mapping.proportionOf(origin)));
28
+ style.setProperty(`--_sonic-${prefix}-modulation-from`, String(Math.min(at, reach)));
29
+ style.setProperty(`--_sonic-${prefix}-modulation-to`, String(Math.max(at, reach)));
30
+ if (positionCount === void 0) style.removeProperty(`--_sonic-${prefix}-position-count`);
31
+ else style.setProperty(`--_sonic-${prefix}-position-count`, String(positionCount));
32
+ writeProportion(mapping);
33
+ },
34
+ value: () => modulationValue,
35
+ write: (next, mapping) => {
36
+ const value = next === void 0 || next === null ? void 0 : toNumber(next);
37
+ if (value !== void 0 && !Number.isFinite(value)) return;
38
+ modulationValue = value;
39
+ if (mapping) writeProportion(mapping);
40
+ }
41
+ };
42
+ }
43
+
44
+ //#endregion
45
+ export { createModulation };
46
+ //# sourceMappingURL=modulation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modulation.js","names":[],"sources":["../../../src/elements/modulation.ts"],"sourcesContent":["import type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { toNumber } from '#lib/math.ts';\n\ninterface Modulation {\n\tdraw: (drawn: ModulationDraw) => void;\n\tvalue: () => number | undefined;\n\twrite: (next: null | number | undefined, mapping: undefined | ValueMapping) => void;\n}\n\ninterface ModulationDraw {\n\tmapping: ValueMapping;\n\tmodulation: number;\n\torigin: number;\n\tvalue: number;\n}\n\nexport function createModulation(\n\tcontrol: HTMLElement,\n\tprefix: 'dial' | 'slider',\n\ttoggle: (isModulated: boolean) => void,\n): Modulation {\n\tconst { style } = control;\n\tlet isModulated = false;\n\tlet modulationValue: number | undefined;\n\n\tconst writeProportion = (mapping: ValueMapping): void => {\n\t\tconst shown = mapping.isWrapping ? undefined : modulationValue;\n\t\tconst isShown = shown !== undefined;\n\n\t\tif (isShown !== isModulated) {\n\t\t\tisModulated = isShown;\n\t\t\ttoggle(isShown);\n\t\t}\n\t\tif (shown === undefined) {\n\t\t\tstyle.removeProperty(`--_sonic-${prefix}-modulation-value`);\n\t\t\treturn;\n\t\t}\n\n\t\tstyle.setProperty(`--_sonic-${prefix}-modulation-value`, String(mapping.proportionOf(shown)));\n\t};\n\n\treturn {\n\t\tdraw: ({ mapping, modulation, origin, value }) => {\n\t\t\tconst at = mapping.proportionOf(value);\n\t\t\tconst reach = mapping.proportionOf(value + modulation);\n\t\t\tconst { positionCount } = mapping;\n\n\t\t\tstyle.setProperty(`--_sonic-${prefix}-value`, String(at));\n\t\t\tstyle.setProperty(`--_sonic-${prefix}-origin`, String(mapping.proportionOf(origin)));\n\t\t\tstyle.setProperty(`--_sonic-${prefix}-modulation-from`, String(Math.min(at, reach)));\n\t\t\tstyle.setProperty(`--_sonic-${prefix}-modulation-to`, String(Math.max(at, reach)));\n\t\t\tif (positionCount === undefined) style.removeProperty(`--_sonic-${prefix}-position-count`);\n\t\t\telse style.setProperty(`--_sonic-${prefix}-position-count`, String(positionCount));\n\t\t\twriteProportion(mapping);\n\t\t},\n\t\tvalue: () => modulationValue,\n\t\t// No mapping before the control is drawn; the value is held until then\n\t\twrite: (next, mapping) => {\n\t\t\tconst value = next === undefined || next === null ? undefined : toNumber(next);\n\t\t\tif (value !== undefined && !Number.isFinite(value)) return;\n\n\t\t\tmodulationValue = value;\n\t\t\tif (mapping) writeProportion(mapping);\n\t\t},\n\t};\n}\n"],"mappings":";;;AAiBA,SAAgB,iBACf,SACA,QACA,QACa;CACb,MAAM,EAAE,UAAU;CAClB,IAAI,cAAc;CAClB,IAAI;CAEJ,MAAM,mBAAmB,YAAgC;EACxD,MAAM,QAAQ,QAAQ,aAAa,SAAY;EAC/C,MAAM,UAAU,UAAU;EAE1B,IAAI,YAAY,aAAa;GAC5B,cAAc;GACd,OAAO,OAAO;EACf;EACA,IAAI,UAAU,QAAW;GACxB,MAAM,eAAe,YAAY,OAAO,kBAAkB;GAC1D;EACD;EAEA,MAAM,YAAY,YAAY,OAAO,oBAAoB,OAAO,QAAQ,aAAa,KAAK,CAAC,CAAC;CAC7F;CAEA,OAAO;EACN,OAAO,EAAE,SAAS,YAAY,QAAQ,YAAY;GACjD,MAAM,KAAK,QAAQ,aAAa,KAAK;GACrC,MAAM,QAAQ,QAAQ,aAAa,QAAQ,UAAU;GACrD,MAAM,EAAE,kBAAkB;GAE1B,MAAM,YAAY,YAAY,OAAO,SAAS,OAAO,EAAE,CAAC;GACxD,MAAM,YAAY,YAAY,OAAO,UAAU,OAAO,QAAQ,aAAa,MAAM,CAAC,CAAC;GACnF,MAAM,YAAY,YAAY,OAAO,mBAAmB,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC;GACnF,MAAM,YAAY,YAAY,OAAO,iBAAiB,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC;GACjF,IAAI,kBAAkB,QAAW,MAAM,eAAe,YAAY,OAAO,gBAAgB;QACpF,MAAM,YAAY,YAAY,OAAO,kBAAkB,OAAO,aAAa,CAAC;GACjF,gBAAgB,OAAO;EACxB;EACA,aAAa;EAEb,QAAQ,MAAM,YAAY;GACzB,MAAM,QAAQ,SAAS,UAAa,SAAS,OAAO,SAAY,SAAS,IAAI;GAC7E,IAAI,UAAU,UAAa,CAAC,OAAO,SAAS,KAAK,GAAG;GAEpD,kBAAkB;GAClB,IAAI,SAAS,gBAAgB,OAAO;EACrC;CACD;AACD"}
@@ -1,6 +1,11 @@
1
+ import { readMsProperty } from "../lib/read-ms-property.js";
2
+
1
3
  //#region src/elements/readout-claim.ts
2
4
  const keyRevealMs = 1e3;
3
5
  const revealMs = 250;
6
+ function revealDelay(control) {
7
+ return Math.max(0, readMsProperty(getComputedStyle(control), "--_sonic-reveal-delay", revealMs));
8
+ }
4
9
  var ReadoutClaim = class {
5
10
  get isRevealed() {
6
11
  return this.#isDragging || this.#keyTimer !== void 0;
@@ -24,7 +29,7 @@ var ReadoutClaim = class {
24
29
  this.#keyTimer = void 0;
25
30
  return wasTimed;
26
31
  }
27
- press(isDown) {
32
+ press(isDown, delayMs = revealMs) {
28
33
  this.conceal("drag");
29
34
  if (!isDown) {
30
35
  this.#onLapse();
@@ -33,7 +38,7 @@ var ReadoutClaim = class {
33
38
  this.#holdTimer = setTimeout(() => {
34
39
  this.reveal("drag");
35
40
  this.#onLapse();
36
- }, 250);
41
+ }, delayMs);
37
42
  }
38
43
  reveal(by) {
39
44
  if (by === "drag") {
@@ -49,5 +54,5 @@ var ReadoutClaim = class {
49
54
  };
50
55
 
51
56
  //#endregion
52
- export { ReadoutClaim };
57
+ export { ReadoutClaim, revealDelay };
53
58
  //# sourceMappingURL=readout-claim.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"readout-claim.js","names":[],"sources":["../../../src/elements/readout-claim.ts"],"sourcesContent":["const keyRevealMs = 1000;\n\nexport const revealMs = 250;\n\nexport class ReadoutClaim {\n\tget isRevealed(): boolean {\n\t\treturn this.#isDragging || this.#keyTimer !== undefined;\n\t}\n\n\t#holdTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#isDragging = false;\n\n\t#keyTimer: ReturnType<typeof setTimeout> | undefined;\n\n\treadonly #onLapse: () => void;\n\n\tconstructor(onLapse: () => void) {\n\t\tthis.#onLapse = onLapse;\n\t}\n\n\tconceal(by: 'drag' | 'keys'): boolean {\n\t\tif (by === 'drag') {\n\t\t\tconst wasDragging = this.#isDragging;\n\n\t\t\tclearTimeout(this.#holdTimer);\n\t\t\tthis.#isDragging = false;\n\n\t\t\treturn wasDragging;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\n\t\tconst wasTimed = this.#keyTimer !== undefined;\n\n\t\tthis.#keyTimer = undefined;\n\n\t\treturn wasTimed;\n\t}\n\n\tpress(isDown: boolean): void {\n\t\tthis.conceal('drag');\n\t\tif (!isDown) {\n\t\t\tthis.#onLapse();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#holdTimer = setTimeout(() => {\n\t\t\tthis.reveal('drag');\n\t\t\tthis.#onLapse();\n\t\t}, revealMs);\n\t}\n\n\treveal(by: 'drag' | 'keys'): void {\n\t\tif (by === 'drag') {\n\t\t\tthis.#isDragging = true;\n\t\t\treturn;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\t\tthis.#keyTimer = setTimeout(() => {\n\t\t\tthis.#keyTimer = undefined;\n\t\t\tthis.#onLapse();\n\t\t}, keyRevealMs);\n\t}\n}\n"],"mappings":";AAAA,MAAM,cAAc;AAEpB,MAAa,WAAW;AAExB,IAAa,eAAb,MAA0B;CACzB,IAAI,aAAsB;EACzB,OAAO,KAAK,eAAe,KAAK,cAAc;CAC/C;CAEA;CAEA,cAAc;CAEd;CAEA,AAAS;CAET,YAAY,SAAqB;EAChC,KAAK,WAAW;CACjB;CAEA,QAAQ,IAA8B;EACrC,IAAI,OAAO,QAAQ;GAClB,MAAM,cAAc,KAAK;GAEzB,aAAa,KAAK,UAAU;GAC5B,KAAK,cAAc;GAEnB,OAAO;EACR;EAEA,aAAa,KAAK,SAAS;EAE3B,MAAM,WAAW,KAAK,cAAc;EAEpC,KAAK,YAAY;EAEjB,OAAO;CACR;CAEA,MAAM,QAAuB;EAC5B,KAAK,QAAQ,MAAM;EACnB,IAAI,CAAC,QAAQ;GACZ,KAAK,SAAS;GACd;EACD;EAEA,KAAK,aAAa,iBAAiB;GAClC,KAAK,OAAO,MAAM;GAClB,KAAK,SAAS;EACf,MAAW;CACZ;CAEA,OAAO,IAA2B;EACjC,IAAI,OAAO,QAAQ;GAClB,KAAK,cAAc;GACnB;EACD;EAEA,aAAa,KAAK,SAAS;EAC3B,KAAK,YAAY,iBAAiB;GACjC,KAAK,YAAY;GACjB,KAAK,SAAS;EACf,GAAG,WAAW;CACf;AACD"}
1
+ {"version":3,"file":"readout-claim.js","names":[],"sources":["../../../src/elements/readout-claim.ts"],"sourcesContent":["import { readMsProperty } from '#lib/read-ms-property.ts';\n\nconst keyRevealMs = 1000;\n\nconst revealMs = 250;\n\nexport function revealDelay(control: Element): number {\n\treturn Math.max(0, readMsProperty(getComputedStyle(control), '--_sonic-reveal-delay', revealMs));\n}\n\nexport class ReadoutClaim {\n\tget isRevealed(): boolean {\n\t\treturn this.#isDragging || this.#keyTimer !== undefined;\n\t}\n\n\t#holdTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#isDragging = false;\n\n\t#keyTimer: ReturnType<typeof setTimeout> | undefined;\n\n\treadonly #onLapse: () => void;\n\n\tconstructor(onLapse: () => void) {\n\t\tthis.#onLapse = onLapse;\n\t}\n\n\tconceal(by: 'drag' | 'keys'): boolean {\n\t\tif (by === 'drag') {\n\t\t\tconst wasDragging = this.#isDragging;\n\n\t\t\tclearTimeout(this.#holdTimer);\n\t\t\tthis.#isDragging = false;\n\n\t\t\treturn wasDragging;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\n\t\tconst wasTimed = this.#keyTimer !== undefined;\n\n\t\tthis.#keyTimer = undefined;\n\n\t\treturn wasTimed;\n\t}\n\n\tpress(isDown: boolean, delayMs = revealMs): void {\n\t\tthis.conceal('drag');\n\t\tif (!isDown) {\n\t\t\tthis.#onLapse();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#holdTimer = setTimeout(() => {\n\t\t\tthis.reveal('drag');\n\t\t\tthis.#onLapse();\n\t\t}, delayMs);\n\t}\n\n\treveal(by: 'drag' | 'keys'): void {\n\t\tif (by === 'drag') {\n\t\t\tthis.#isDragging = true;\n\t\t\treturn;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\t\tthis.#keyTimer = setTimeout(() => {\n\t\t\tthis.#keyTimer = undefined;\n\t\t\tthis.#onLapse();\n\t\t}, keyRevealMs);\n\t}\n}\n"],"mappings":";;;AAEA,MAAM,cAAc;AAEpB,MAAM,WAAW;AAEjB,SAAgB,YAAY,SAA0B;CACrD,OAAO,KAAK,IAAI,GAAG,eAAe,iBAAiB,OAAO,GAAG,yBAAyB,QAAQ,CAAC;AAChG;AAEA,IAAa,eAAb,MAA0B;CACzB,IAAI,aAAsB;EACzB,OAAO,KAAK,eAAe,KAAK,cAAc;CAC/C;CAEA;CAEA,cAAc;CAEd;CAEA,AAAS;CAET,YAAY,SAAqB;EAChC,KAAK,WAAW;CACjB;CAEA,QAAQ,IAA8B;EACrC,IAAI,OAAO,QAAQ;GAClB,MAAM,cAAc,KAAK;GAEzB,aAAa,KAAK,UAAU;GAC5B,KAAK,cAAc;GAEnB,OAAO;EACR;EAEA,aAAa,KAAK,SAAS;EAE3B,MAAM,WAAW,KAAK,cAAc;EAEpC,KAAK,YAAY;EAEjB,OAAO;CACR;CAEA,MAAM,QAAiB,UAAU,UAAgB;EAChD,KAAK,QAAQ,MAAM;EACnB,IAAI,CAAC,QAAQ;GACZ,KAAK,SAAS;GACd;EACD;EAEA,KAAK,aAAa,iBAAiB;GAClC,KAAK,OAAO,MAAM;GAClB,KAAK,SAAS;EACf,GAAG,OAAO;CACX;CAEA,OAAO,IAA2B;EACjC,IAAI,OAAO,QAAQ;GAClB,KAAK,cAAc;GACnB;EACD;EAEA,aAAa,KAAK,SAAS;EAC3B,KAAK,YAAY,iBAAiB;GACjC,KAAK,YAAY;GACjB,KAAK,SAAS;EACf,GAAG,WAAW;CACf;AACD"}
@@ -30,7 +30,6 @@ export declare class SonicSlider extends SonicValueElement {
30
30
  protected focusTarget(): HTMLElement;
31
31
  protected heldWrite(next: number): void;
32
32
  protected holdChanged(): void;
33
- protected previewValue(): number | undefined;
34
33
  protected scrubsKeyRepeat(): boolean;
35
34
  protected springTarget(): number | undefined;
36
35
  }
@@ -1,8 +1,8 @@
1
1
  import { clampProportion } from "../lib/math.js";
2
2
  import { requireChild, template } from "../lib/render.js";
3
+ import { createModulation } from "./modulation.js";
3
4
  import { SonicValueElement } from "./value-element.js";
4
5
  import { bindScale } from "./scale.js";
5
- import { bindHoverPreview } from "../lib/hover-preview.js";
6
6
  import { sortedRegions } from "../lib/time-regions.js";
7
7
 
8
8
  //#region src/elements/slider.ts
@@ -59,10 +59,10 @@ var SonicSlider = class extends SonicValueElement {
59
59
  this.reflect("modulation", amount);
60
60
  }
61
61
  get modulationValue() {
62
- return this.readModulationValue();
62
+ return this.#modulation.value();
63
63
  }
64
64
  set modulationValue(value) {
65
- this.writeModulationValue(this.#slider, "slider", value);
65
+ this.#modulation.write(value, this.isBound() ? this.mapping() : void 0);
66
66
  }
67
67
  get orientation() {
68
68
  return this.getAttribute("orientation") === "vertical" ? "vertical" : "horizontal";
@@ -83,8 +83,10 @@ var SonicSlider = class extends SonicValueElement {
83
83
  this.reflect("spring", isSpring);
84
84
  }
85
85
  #buffered = [];
86
- #hover;
87
86
  #slider = renderSlider();
87
+ #modulation = createModulation(this.#slider, "slider", (isModulated) => {
88
+ this.toggleState("modulated", isModulated);
89
+ });
88
90
  connectedCallback() {
89
91
  this.upgradeProperties("buffered", "groovePress", "modulationValue", "modulation", "orientation", "scrub", "spring");
90
92
  super.connectedCallback();
@@ -99,14 +101,9 @@ var SonicSlider = class extends SonicValueElement {
99
101
  slider.addEventListener("transitionend", (event) => {
100
102
  if (event.propertyName === "--_sonic-slider-value") this.toggleState("springing", false);
101
103
  }, { signal });
102
- this.#hover = bindHoverPreview(slider, {
103
- canShow: () => this.scrub && this.readout && this.orientation === "horizontal",
104
- dismiss: () => {
105
- this.render();
106
- },
107
- isTaken: () => this.isEditing() || this.isRevealed(),
108
- show: () => {
109
- this.renderReadout();
104
+ this.bindHover(slider, signal, {
105
+ canShow: () => this.scrub && this.orientation === "horizontal",
106
+ place: () => {
110
107
  this.#placeReadout(slider);
111
108
  },
112
109
  valueAt: (event) => {
@@ -115,7 +112,7 @@ var SonicSlider = class extends SonicValueElement {
115
112
  const mapping = this.mapping();
116
113
  return mapping.snap(mapping.valueAt(at));
117
114
  }
118
- }, signal);
115
+ });
119
116
  this.render();
120
117
  this.checkStyles(slider, "slider.css");
121
118
  this.bindGestures(slider, signal, (event) => {
@@ -130,8 +127,12 @@ var SonicSlider = class extends SonicValueElement {
130
127
  return this.orientation;
131
128
  }
132
129
  draw() {
133
- if (this.isEditing() || this.isRevealed()) this.#hover?.clear();
134
- this.writeProportions(this.#slider, "slider", this.modulation);
130
+ this.#modulation.draw({
131
+ mapping: this.mapping(),
132
+ modulation: this.modulation,
133
+ origin: this.originValue(),
134
+ value: this.value
135
+ });
135
136
  this.#renderBuffered(this.#slider);
136
137
  this.#renderScrub(this.#slider);
137
138
  this.#placeReadout(this.#slider);
@@ -146,9 +147,6 @@ var SonicSlider = class extends SonicValueElement {
146
147
  holdChanged() {
147
148
  if (this.scrub) this.render();
148
149
  }
149
- previewValue() {
150
- return this.#hover?.value();
151
- }
152
150
  scrubsKeyRepeat() {
153
151
  return this.scrub;
154
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-element.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { clampProportion } from '#lib/math.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-slider-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'scrub',\n\t\t'spring',\n\t];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget buffered(): Array<[number, number]> {\n\t\treturn this.#buffered.map(([start, end]) => [start, end]);\n\t}\n\n\tset buffered(regions: TimeRegions | undefined) {\n\t\tthis.#buffered = sortedRegions(regions);\n\t\tthis.render();\n\t}\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#slider, 'slider', value);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget scrub(): boolean {\n\t\treturn this.hasAttribute('scrub');\n\t}\n\n\tset scrub(isScrubbing: boolean) {\n\t\tthis.reflect('scrub', isScrubbing);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\t#buffered: Array<[number, number]> = [];\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #slider = renderSlider();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'buffered',\n\t\t\t'groovePress',\n\t\t\t'modulationValue',\n\t\t\t'modulation',\n\t\t\t'orientation',\n\t\t\t'scrub',\n\t\t\t'spring',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: slider, marks: requireChild(slider, '.sonic-slider-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tslider,\n\t\t\t{\n\t\t\t\tcanShow: () => this.scrub && this.readout && this.orientation === 'horizontal',\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\tthis.renderReadout();\n\t\t\t\t\tthis.#placeReadout(slider);\n\t\t\t\t},\n\t\t\t\tvalueAt: (event) => {\n\t\t\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\t\t\tconst at = clampProportion((axis.position(event) - axis.startPx) / axis.travelPx);\n\t\t\t\t\tconst mapping = this.mapping();\n\n\t\t\t\t\treturn mapping.snap(mapping.valueAt(at));\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tif (this.isEditing() || this.isRevealed()) this.#hover?.clear();\n\t\tthis.writeProportions(this.#slider, 'slider', this.modulation);\n\t\tthis.#renderBuffered(this.#slider);\n\t\tthis.#renderScrub(this.#slider);\n\t\tthis.#placeReadout(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override heldWrite(next: number): void {\n\t\tsuper.heldWrite(next);\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override holdChanged(): void {\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override previewValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn this.scrub;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#placeReadout(slider: HTMLElement): void {\n\t\tif (!this.scrub) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-readout-value');\n\t\t\treturn;\n\t\t}\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-readout-value',\n\t\t\tString(this.mapping().proportionOf(this.readoutValue())),\n\t\t);\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.#buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n\n\t#renderScrub(slider: HTMLElement): void {\n\t\tconst { style } = slider;\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst isHeld = this.scrub && this.heldFrom() !== undefined;\n\t\tconst played = proportionOf(this.playback());\n\t\tconst scrubbed = proportionOf(this.value);\n\n\t\tif (isHeld) style.setProperty('--_sonic-slider-played', String(played));\n\t\telse style.removeProperty('--_sonic-slider-played');\n\n\t\tif (played === scrubbed || !isHeld || !this.isRevealed()) {\n\t\t\tstyle.removeProperty('--_sonic-slider-scrub-region');\n\t\t\treturn;\n\t\t}\n\n\t\tconst from = grooveStop(Math.min(played, scrubbed));\n\t\tconst to = grooveStop(Math.max(played, scrubbed));\n\n\t\tstyle.setProperty(\n\t\t\t'--_sonic-slider-scrub-region',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), transparent ${from}, var(--_sonic-slider-scrub) ${from} ${to}, transparent ${to})`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { clampProportion } from '#lib/math.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-slider-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'scrub',\n\t\t'spring',\n\t];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget buffered(): Array<[number, number]> {\n\t\treturn this.#buffered.map(([start, end]) => [start, end]);\n\t}\n\n\tset buffered(regions: TimeRegions | undefined) {\n\t\tthis.#buffered = sortedRegions(regions);\n\t\tthis.render();\n\t}\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget scrub(): boolean {\n\t\treturn this.hasAttribute('scrub');\n\t}\n\n\tset scrub(isScrubbing: boolean) {\n\t\tthis.reflect('scrub', isScrubbing);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #slider = renderSlider();\n\n\treadonly #modulation = createModulation(this.#slider, 'slider', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'buffered',\n\t\t\t'groovePress',\n\t\t\t'modulationValue',\n\t\t\t'modulation',\n\t\t\t'orientation',\n\t\t\t'scrub',\n\t\t\t'spring',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: slider, marks: requireChild(slider, '.sonic-slider-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.bindHover(slider, signal, {\n\t\t\tcanShow: () => this.scrub && this.orientation === 'horizontal',\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout(slider);\n\t\t\t},\n\t\t\tvalueAt: (event) => {\n\t\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\t\tconst at = clampProportion((axis.position(event) - axis.startPx) / axis.travelPx);\n\t\t\t\tconst mapping = this.mapping();\n\n\t\t\t\treturn mapping.snap(mapping.valueAt(at));\n\t\t\t},\n\t\t});\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t\tthis.#renderBuffered(this.#slider);\n\t\tthis.#renderScrub(this.#slider);\n\t\tthis.#placeReadout(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override heldWrite(next: number): void {\n\t\tsuper.heldWrite(next);\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override holdChanged(): void {\n\t\tif (this.scrub) this.render();\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn this.scrub;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#placeReadout(slider: HTMLElement): void {\n\t\tif (!this.scrub) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-readout-value');\n\t\t\treturn;\n\t\t}\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-readout-value',\n\t\t\tString(this.mapping().proportionOf(this.readoutValue())),\n\t\t);\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.#buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n\n\t#renderScrub(slider: HTMLElement): void {\n\t\tconst { style } = slider;\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst isHeld = this.scrub && this.heldFrom() !== undefined;\n\t\tconst played = proportionOf(this.playback());\n\t\tconst scrubbed = proportionOf(this.value);\n\n\t\tif (isHeld) style.setProperty('--_sonic-slider-played', String(played));\n\t\telse style.removeProperty('--_sonic-slider-played');\n\n\t\tif (played === scrubbed || !isHeld || !this.isRevealed()) {\n\t\t\tstyle.removeProperty('--_sonic-slider-scrub-region');\n\t\t\treturn;\n\t\t}\n\n\t\tconst from = grooveStop(Math.min(played, scrubbed));\n\t\tconst to = grooveStop(Math.max(played, scrubbed));\n\n\t\tstyle.setProperty(\n\t\t\t'--_sonic-slider-scrub-region',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), transparent ${from}, var(--_sonic-slider-scrub) ${from} ${to}, transparent ${to})`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -49,6 +49,7 @@ export declare class SonicSpectrum extends SonicDisplayElement<Colour, Numeric>
49
49
  readonly hotFrom: "--_sonic-spectrum-hot-from";
50
50
  };
51
51
  protected readonly sheet = "spectrum.css";
52
+ protected readonly sizeProperty = "--_sonic-spectrum-size";
52
53
  connectedCallback(): void;
53
54
  resetPeak(): void;
54
55
  protected paint(context: CanvasRenderingContext2D, { frameMs, isDirty, look, size }: SurfaceFrame<Colour, never, Numeric>): void;
@@ -119,6 +119,7 @@ var SonicSpectrum = class extends SonicDisplayElement {
119
119
  colours = colours;
120
120
  numbers = numbers;
121
121
  sheet = "spectrum.css";
122
+ sizeProperty = "--_sonic-spectrum-size";
122
123
  #columns;
123
124
  #isSettled = true;
124
125
  #lastFrameMs;