@xsynaptic/sonic-ui 0.17.0 → 0.19.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 (178) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +122 -19
  3. package/dist/default/elements/button.js +2 -5
  4. package/dist/default/elements/dial.js +2 -7
  5. package/dist/default/elements/display-element.js +9 -12
  6. package/dist/default/elements/envelope.js +31 -78
  7. package/dist/default/elements/field-gesture.js +81 -0
  8. package/dist/default/elements/meter.js +4 -1
  9. package/dist/default/elements/number.js +0 -7
  10. package/dist/default/elements/position-group.js +42 -26
  11. package/dist/default/elements/radio-group.js +59 -56
  12. package/dist/default/elements/scale.js +3 -3
  13. package/dist/default/elements/segmented.js +0 -5
  14. package/dist/default/elements/slider.js +11 -20
  15. package/dist/default/elements/sonic-element.js +3 -1
  16. package/dist/default/elements/split.js +14 -13
  17. package/dist/default/elements/switch.js +31 -47
  18. package/dist/default/elements/toggle.js +20 -49
  19. package/dist/default/elements/value-element.js +72 -94
  20. package/dist/default/elements/value-gestures.js +22 -22
  21. package/dist/default/elements/wave-element.js +16 -20
  22. package/dist/default/elements/waveform.js +42 -241
  23. package/dist/default/elements/wavestrip.js +10 -5
  24. package/dist/default/elements/xy.js +67 -119
  25. package/dist/default/index.js +2 -1
  26. package/dist/default/lib/canvas-surface.js +25 -0
  27. package/dist/default/lib/clock.js +23 -0
  28. package/dist/default/lib/crossfade.js +1 -0
  29. package/dist/default/lib/envelope-curve.js +1 -0
  30. package/dist/default/lib/peaks-request.js +44 -0
  31. package/dist/default/lib/positions.js +58 -0
  32. package/dist/default/lib/scrub.js +12 -0
  33. package/dist/default/lib/spoken-duration.js +30 -0
  34. package/dist/default/lib/value-model.js +62 -0
  35. package/dist/default/lib/waveform-buckets.js +7 -1
  36. package/dist/default/lib/waveform-scene.js +108 -0
  37. package/dist/default/lib/zoom-gesture.js +97 -1
  38. package/dist/dev/elements/button.d.ts +2 -0
  39. package/dist/dev/elements/button.js +14 -5
  40. package/dist/dev/elements/button.js.map +1 -1
  41. package/dist/dev/elements/dial.d.ts +2 -0
  42. package/dist/dev/elements/dial.js +16 -7
  43. package/dist/dev/elements/dial.js.map +1 -1
  44. package/dist/dev/elements/display-element.d.ts +5 -3
  45. package/dist/dev/elements/display-element.js +11 -9
  46. package/dist/dev/elements/display-element.js.map +1 -1
  47. package/dist/dev/elements/envelope.js +48 -78
  48. package/dist/dev/elements/envelope.js.map +1 -1
  49. package/dist/dev/elements/field-gesture.js +82 -0
  50. package/dist/dev/elements/field-gesture.js.map +1 -0
  51. package/dist/dev/elements/meter.d.ts +10 -5
  52. package/dist/dev/elements/meter.js +5 -1
  53. package/dist/dev/elements/meter.js.map +1 -1
  54. package/dist/dev/elements/number.js +13 -7
  55. package/dist/dev/elements/number.js.map +1 -1
  56. package/dist/dev/elements/position-group.d.ts +12 -6
  57. package/dist/dev/elements/position-group.js +42 -26
  58. package/dist/dev/elements/position-group.js.map +1 -1
  59. package/dist/dev/elements/radio-group.d.ts +11 -7
  60. package/dist/dev/elements/radio-group.js +59 -56
  61. package/dist/dev/elements/radio-group.js.map +1 -1
  62. package/dist/dev/elements/scale.js +3 -3
  63. package/dist/dev/elements/scale.js.map +1 -1
  64. package/dist/dev/elements/segmented.js +12 -5
  65. package/dist/dev/elements/segmented.js.map +1 -1
  66. package/dist/dev/elements/slider.d.ts +4 -2
  67. package/dist/dev/elements/slider.js +25 -20
  68. package/dist/dev/elements/slider.js.map +1 -1
  69. package/dist/dev/elements/sonic-element.d.ts +13 -2
  70. package/dist/dev/elements/sonic-element.js +63 -11
  71. package/dist/dev/elements/sonic-element.js.map +1 -1
  72. package/dist/dev/elements/split.d.ts +1 -0
  73. package/dist/dev/elements/split.js +14 -13
  74. package/dist/dev/elements/split.js.map +1 -1
  75. package/dist/dev/elements/switch.d.ts +2 -2
  76. package/dist/dev/elements/switch.js +38 -47
  77. package/dist/dev/elements/switch.js.map +1 -1
  78. package/dist/dev/elements/toggle.js +32 -49
  79. package/dist/dev/elements/toggle.js.map +1 -1
  80. package/dist/dev/elements/value-element.d.ts +11 -5
  81. package/dist/dev/elements/value-element.js +72 -94
  82. package/dist/dev/elements/value-element.js.map +1 -1
  83. package/dist/dev/elements/value-gestures.js +22 -22
  84. package/dist/dev/elements/value-gestures.js.map +1 -1
  85. package/dist/dev/elements/wave-element.d.ts +8 -5
  86. package/dist/dev/elements/wave-element.js +30 -20
  87. package/dist/dev/elements/wave-element.js.map +1 -1
  88. package/dist/dev/elements/waveform.d.ts +20 -22
  89. package/dist/dev/elements/waveform.js +42 -241
  90. package/dist/dev/elements/waveform.js.map +1 -1
  91. package/dist/dev/elements/wavestrip.d.ts +5 -1
  92. package/dist/dev/elements/wavestrip.js +10 -5
  93. package/dist/dev/elements/wavestrip.js.map +1 -1
  94. package/dist/dev/elements/xy.js +81 -119
  95. package/dist/dev/elements/xy.js.map +1 -1
  96. package/dist/dev/index.d.ts +5 -3
  97. package/dist/dev/index.js +2 -1
  98. package/dist/dev/lib/canvas-surface.d.ts +1 -0
  99. package/dist/dev/lib/canvas-surface.js +25 -0
  100. package/dist/dev/lib/canvas-surface.js.map +1 -1
  101. package/dist/dev/lib/clock.d.ts +5 -0
  102. package/dist/dev/lib/clock.js +24 -0
  103. package/dist/dev/lib/clock.js.map +1 -0
  104. package/dist/dev/lib/crossfade.d.ts +1 -0
  105. package/dist/dev/lib/crossfade.js +1 -0
  106. package/dist/dev/lib/crossfade.js.map +1 -1
  107. package/dist/dev/lib/envelope-curve.d.ts +1 -0
  108. package/dist/dev/lib/envelope-curve.js +1 -0
  109. package/dist/dev/lib/envelope-curve.js.map +1 -1
  110. package/dist/dev/lib/mirror-children.js.map +1 -1
  111. package/dist/dev/lib/owned-control.js.map +1 -1
  112. package/dist/dev/lib/peaks-request.d.ts +5 -0
  113. package/dist/dev/lib/peaks-request.js +45 -0
  114. package/dist/dev/lib/peaks-request.js.map +1 -0
  115. package/dist/dev/lib/positions.d.ts +7 -0
  116. package/dist/dev/lib/positions.js +59 -0
  117. package/dist/dev/lib/positions.js.map +1 -0
  118. package/dist/dev/lib/scrub.d.ts +8 -0
  119. package/dist/dev/lib/scrub.js +13 -0
  120. package/dist/dev/lib/scrub.js.map +1 -0
  121. package/dist/dev/lib/spoken-duration.js +31 -0
  122. package/dist/dev/lib/spoken-duration.js.map +1 -0
  123. package/dist/dev/lib/time-regions.d.ts +1 -0
  124. package/dist/dev/lib/time-regions.js.map +1 -1
  125. package/dist/dev/lib/units.d.ts +8 -0
  126. package/dist/dev/lib/value-model.js +63 -0
  127. package/dist/dev/lib/value-model.js.map +1 -0
  128. package/dist/dev/lib/waveform-buckets.d.ts +2 -1
  129. package/dist/dev/lib/waveform-buckets.js +7 -1
  130. package/dist/dev/lib/waveform-buckets.js.map +1 -1
  131. package/dist/dev/lib/waveform-scene.d.ts +11 -0
  132. package/dist/dev/lib/waveform-scene.js +109 -0
  133. package/dist/dev/lib/waveform-scene.js.map +1 -0
  134. package/dist/dev/lib/zoom-gesture.js +97 -1
  135. package/dist/dev/lib/zoom-gesture.js.map +1 -1
  136. package/dist/skins/amber.css +1 -0
  137. package/dist/skins/ivory.css +1 -0
  138. package/dist/skins/lime.css +1 -0
  139. package/dist/skins/slate.css +1 -0
  140. package/dist/styles/button.css +45 -11
  141. package/dist/styles/dial.css +10 -9
  142. package/dist/styles/envelope.css +10 -4
  143. package/dist/styles/led.css +5 -1
  144. package/dist/styles/material/arc.css +3 -2
  145. package/dist/styles/material/bracket.css +2 -2
  146. package/dist/styles/material/cap.css +33 -12
  147. package/dist/styles/material/core.css +4 -0
  148. package/dist/styles/material/glass.css +1 -1
  149. package/dist/styles/material/groove.css +4 -3
  150. package/dist/styles/material/keycap.css +3 -2
  151. package/dist/styles/material/lens.css +1 -1
  152. package/dist/styles/material/pane.css +5 -4
  153. package/dist/styles/material/readout.css +6 -6
  154. package/dist/styles/material/well.css +2 -2
  155. package/dist/styles/meter.css +2 -7
  156. package/dist/styles/number.css +5 -1
  157. package/dist/styles/panel.css +4 -3
  158. package/dist/styles/ring.css +1 -1
  159. package/dist/styles/screen.css +5 -1
  160. package/dist/styles/segmented.css +17 -4
  161. package/dist/styles/slider.css +9 -3
  162. package/dist/styles/spectrum.css +16 -3
  163. package/dist/styles/switch.css +35 -10
  164. package/dist/styles/toggle.css +14 -7
  165. package/dist/styles/waveform.css +17 -5
  166. package/dist/styles/wavestrip.css +11 -3
  167. package/dist/styles/xy.css +9 -5
  168. package/package.json +1 -6
  169. package/dist/default/dat.js +0 -51
  170. package/dist/default/lib/asked-value.js +0 -21
  171. package/dist/default/lib/fill-watch.js +0 -29
  172. package/dist/dev/dat.d.ts +0 -19
  173. package/dist/dev/dat.js +0 -52
  174. package/dist/dev/dat.js.map +0 -1
  175. package/dist/dev/lib/asked-value.js +0 -22
  176. package/dist/dev/lib/asked-value.js.map +0 -1
  177. package/dist/dev/lib/fill-watch.js +0 -30
  178. package/dist/dev/lib/fill-watch.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"wavestrip.js","names":[],"sources":["../../../src/elements/wavestrip.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { StripRegions } from '#lib/strip-scene.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { createMarkerBand } from '#lib/marker-band.ts';\nimport { clampProportion } from '#lib/math.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { stripBars, stripGroove, stripRegions } from '#lib/strip-scene.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-wavestrip': SonicWavestrip;\n\t}\n}\n\ntype Colour = keyof typeof colours;\n\ntype Length = keyof typeof lengths;\n\nconst colours = {\n\tbuffered: '--_sonic-wavestrip-buffered',\n\tplayed: '--_sonic-wavestrip-played',\n\tscrub: '--_sonic-wavestrip-scrub',\n\twave: '--_sonic-wavestrip-wave',\n} as const;\n\nconst lengths = {\n\tgapRatio: '--_sonic-wavestrip-bar-gap-ratio',\n\tgrooveRatio: '--_sonic-wavestrip-groove-ratio',\n\tgrooveSize: '--_sonic-wavestrip-groove-size',\n\tpitch: '--_sonic-wavestrip-bar-pitch',\n\tradiusRatio: '--_sonic-wavestrip-bar-radius-ratio',\n} as const;\n\nconst cancelZonePx = 48;\n\nconst renderWavestrip = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-wavestrip\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-wavestrip-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-wavestrip-markers\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-wavestrip-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-wavestrip-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\nfunction fillRegion(\n\tcontext: CanvasRenderingContext2D,\n\tpath: Path2D,\n\t[from, to, style]: [number, number, string],\n): void {\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, context.canvas.height);\n\tcontext.clip();\n\tcontext.fillStyle = style;\n\tcontext.fill(path);\n\tcontext.restore();\n}\n\nexport class SonicWavestrip extends SonicWaveElement<Colour, Length> {\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 cancellable(): boolean {\n\t\treturn this.hasAttribute('cancellable');\n\t}\n\n\tset cancellable(isCancellable: boolean) {\n\t\tthis.reflect('cancellable', isCancellable);\n\t}\n\n\tget peaks(): ArrayLike<number> | undefined {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: ArrayLike<number> | undefined) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tprotected readonly control = renderWavestrip();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-wavestrip-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = colours;\n\n\tprotected readonly lengths = lengths;\n\n\tprotected readonly sheet = 'wavestrip.css';\n\n\tprotected readonly sizeProperty = '--_sonic-wavestrip-size';\n\n\t#bars: Path2D | undefined;\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #markerBand = createMarkerBand(\n\t\trequireChild(this.control, '.sonic-wavestrip-markers', HTMLDivElement),\n\t);\n\n\t#paintedKey = '';\n\n\t#peaks: ArrayLike<number> | undefined;\n\n\tclientXOf(value: number): number {\n\t\tconst { startPx, travelPx } = this.#axis();\n\n\t\treturn startPx + this.mapping().proportionOf(value) * travelPx;\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('buffered', 'cancellable', 'peaks');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tconst { control } = this;\n\n\t\tsuper.connect(signal);\n\t\tthis.bindHover(control, signal, {\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout();\n\t\t\t},\n\t\t\tvalueAt: (event) => {\n\t\t\t\tconst axis = this.#axis();\n\t\t\t\tconst at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);\n\t\t\t\tconst mapping = this.mapping();\n\n\t\t\t\treturn this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));\n\t\t\t},\n\t\t});\n\t\tthis.bindGestures(control, signal, (event) => this.#grab(event));\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tthis.#placeReadout();\n\t\tthis.showCurrentMarker(this.playback());\n\t\tif (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) {\n\t\t\tsurface.requestFrame();\n\t\t}\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.length === 0;\n\t}\n\n\tprotected paint(context: CanvasRenderingContext2D, frame: SurfaceFrame<Colour, Length>): void {\n\t\tconst { look, size } = frame;\n\t\tconst strip = this.#stripRegions(size.width);\n\t\tconst bars = this.#barsFor(frame);\n\n\t\tthis.#paintedKey = strip.key;\n\t\tcontext.clearRect(0, 0, size.width, size.height);\n\t\tif (!bars) return;\n\n\t\tcontext.fillStyle = look.colours.wave;\n\t\tcontext.fill(bars);\n\t\tfor (const { from, kind, to } of strip.regions) {\n\t\t\tfillRegion(context, bars, [from, to, look.colours[kind]]);\n\t\t}\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tthis.#markerBand.render(this.markerList(), this.markerProportions());\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\t#axis(): ValueAxis & { startPx: number; topPx: number } {\n\t\tconst box = this.canvas.getBoundingClientRect();\n\n\t\treturn {\n\t\t\tposition: (event: PointerEvent) => event.clientX,\n\t\t\tstartPx: box.left,\n\t\t\ttopPx: box.top,\n\t\t\ttravelPx: Math.max(1, box.width),\n\t\t};\n\t}\n\n\t#barsFor({ isDirty, look, size }: SurfaceFrame<Colour, Length>): Path2D | undefined {\n\t\tif (!isDirty && this.#bars) return this.#bars;\n\n\t\tconst peaks = this.#peaks;\n\t\tconst groove = stripGroove(size, look.lengths);\n\t\tconst drawn = groove ? [groove] : [];\n\t\tconst bars = peaks && peaks.length > 0 ? stripBars(peaks, size, look.lengths) : drawn;\n\n\t\tthis.#bars = undefined;\n\t\tif (bars.length === 0) return undefined;\n\n\t\tconst path = new Path2D();\n\n\t\tfor (const { height, radius, width, x, y } of bars) {\n\t\t\tpath.roundRect(x, y, width, height, radius);\n\t\t}\n\t\tthis.#bars = path;\n\n\t\treturn path;\n\t}\n\n\t#grab(event: PointerEvent): ValueAxis {\n\t\tconst axis = this.#axis();\n\t\tconst at = (event.clientX - axis.startPx) / axis.travelPx;\n\t\tconst outside = this.#outside();\n\t\tconst grabbed = {\n\t\t\tfromProportion: clampProportion(at),\n\t\t\tposition: axis.position,\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\n\t\tthis.input(this.#markerAt(event, axis) ?? this.mapping().valueAt(at));\n\n\t\treturn outside ? { ...grabbed, outside } : grabbed;\n\t}\n\n\t#markerAt(\n\t\tevent: PointerEvent,\n\t\t{ startPx, topPx, travelPx }: { startPx: number; topPx: number; travelPx: number },\n\t): number | undefined {\n\t\tconst index = this.#markerBand.at(\n\t\t\t{ x: event.clientX - startPx, y: event.clientY - topPx },\n\t\t\ttravelPx,\n\t\t);\n\n\t\treturn index === undefined ? undefined : this.markerList()[index]?.start;\n\t}\n\n\t#outside(): ((event: PointerEvent) => boolean) | undefined {\n\t\tif (!this.cancellable) return undefined;\n\n\t\tconst box = this.control.getBoundingClientRect();\n\t\tconst zone = readPxProperty(\n\t\t\tgetComputedStyle(this.control),\n\t\t\t'--_sonic-cancel-zone',\n\t\t\tcancelZonePx,\n\t\t);\n\n\t\treturn (event) => event.clientY < box.top - zone || event.clientY > box.bottom + zone;\n\t}\n\n\t#placeReadout(): void {\n\t\tconst at = this.mapping().proportionOf(this.readoutValue());\n\n\t\tthis.control.style.setProperty('--_sonic-wavestrip-readout-at', String(at));\n\t}\n\n\t#stripRegions(width: number): StripRegions {\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst isScrubbing = this.heldFrom() !== undefined && this.isRevealed();\n\n\t\treturn stripRegions(\n\t\t\t{\n\t\t\t\tbuffered: this.#buffered.map(([start, end]) => [proportionOf(start), proportionOf(end)]),\n\t\t\t\tplayed: proportionOf(this.playback()),\n\t\t\t\t...(isScrubbing ? { scrub: proportionOf(this.value) } : {}),\n\t\t\t},\n\t\t\twidth,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"wavestrip.js","names":[],"sources":["../../../src/elements/wavestrip.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { StripRegions } from '#lib/strip-scene.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { Seconds } from '#lib/units.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { createMarkerBand } from '#lib/marker-band.ts';\nimport { clampProportion } from '#lib/math.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { scrubRegions } from '#lib/scrub.ts';\nimport { stripBars, stripGroove, stripRegions } from '#lib/strip-scene.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-wavestrip': SonicWavestrip;\n\t}\n}\n\ntype Colour = keyof typeof colours;\n\ntype Length = keyof typeof lengths;\n\nconst colours = {\n\tbuffered: '--_sonic-wavestrip-buffered',\n\tplayed: '--_sonic-wavestrip-played',\n\tscrub: '--_sonic-wavestrip-scrub',\n\twave: '--_sonic-wavestrip-wave',\n} as const;\n\nconst lengths = {\n\tgapRatio: '--_sonic-wavestrip-bar-gap-ratio',\n\tgrooveRatio: '--_sonic-wavestrip-groove-ratio',\n\tgrooveSize: '--_sonic-wavestrip-groove-size',\n\tpitch: '--_sonic-wavestrip-bar-pitch',\n\tradiusRatio: '--_sonic-wavestrip-bar-radius-ratio',\n} as const;\n\nconst cancelZonePx = 48;\n\nconst renderWavestrip = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-wavestrip\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-wavestrip-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-wavestrip-markers\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-wavestrip-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-wavestrip-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\nfunction fillRegion(\n\tcontext: CanvasRenderingContext2D,\n\tpath: Path2D,\n\t[from, to, style]: [number, number, string],\n): void {\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, context.canvas.height);\n\tcontext.clip();\n\tcontext.fillStyle = style;\n\tcontext.fill(path);\n\tcontext.restore();\n}\n\nexport class SonicWavestrip extends SonicWaveElement<Colour, Length> {\n\t/** Loaded spans, in seconds */\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 cancellable(): boolean {\n\t\treturn this.hasAttribute('cancellable');\n\t}\n\n\tset cancellable(isCancellable: boolean) {\n\t\tthis.reflect('cancellable', isCancellable);\n\t}\n\n\t/** One amplitude from 0 to 1 per slice of the whole track, any length; resampled to the bars and never normalised */\n\tget peaks(): ArrayLike<number> | undefined {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: ArrayLike<number> | undefined) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tprotected readonly control = renderWavestrip();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-wavestrip-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = colours;\n\n\tprotected readonly lengths = lengths;\n\n\tprotected readonly sheet = 'wavestrip.css';\n\n\tprotected readonly sizeProperty = '--_sonic-wavestrip-size';\n\n\t#bars: Path2D | undefined;\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #markerBand = createMarkerBand(\n\t\trequireChild(this.control, '.sonic-wavestrip-markers', HTMLDivElement),\n\t);\n\n\t#paintedKey = '';\n\n\t#peaks: ArrayLike<number> | undefined;\n\n\t/** Viewport x of a time, clamped to the strip */\n\tclientXOf(value: Seconds): number {\n\t\tconst { startPx, travelPx } = this.#axis();\n\n\t\treturn startPx + this.mapping().proportionOf(value) * travelPx;\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('buffered', 'cancellable', 'peaks');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tconst { control } = this;\n\n\t\tsuper.connect(signal);\n\t\tthis.bindHover(control, signal, {\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout();\n\t\t\t},\n\t\t\tvalueAt: (event) => {\n\t\t\t\tconst axis = this.#axis();\n\t\t\t\tconst at = clampProportion((event.clientX - axis.startPx) / axis.travelPx);\n\t\t\t\tconst mapping = this.mapping();\n\n\t\t\t\treturn this.#markerAt(event, axis) ?? mapping.snap(mapping.valueAt(at));\n\t\t\t},\n\t\t});\n\t\tthis.bindGestures(control, signal, (event) => this.#grab(event));\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tthis.#placeReadout();\n\t\tthis.showCurrentMarker(this.scrubState().played);\n\t\tif (surface && this.#stripRegions(surface.size.width).key !== this.#paintedKey) {\n\t\t\tsurface.requestFrame();\n\t\t}\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.length === 0;\n\t}\n\n\tprotected paint(context: CanvasRenderingContext2D, frame: SurfaceFrame<Colour, Length>): void {\n\t\tconst { look, size } = frame;\n\t\tconst strip = this.#stripRegions(size.width);\n\t\tconst bars = this.#barsFor(frame);\n\n\t\tthis.#paintedKey = strip.key;\n\t\tcontext.clearRect(0, 0, size.width, size.height);\n\t\tif (!bars) return;\n\n\t\tcontext.fillStyle = look.colours.wave;\n\t\tcontext.fill(bars);\n\t\tfor (const { from, kind, to } of strip.regions) {\n\t\t\tfillRegion(context, bars, [from, to, look.colours[kind]]);\n\t\t}\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tthis.#markerBand.render(this.markerList(), this.markerProportions());\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\t#axis(): ValueAxis & { startPx: number; topPx: number } {\n\t\tconst box = this.canvas.getBoundingClientRect();\n\n\t\treturn {\n\t\t\tposition: (event: PointerEvent) => event.clientX,\n\t\t\tstartPx: box.left,\n\t\t\ttopPx: box.top,\n\t\t\ttravelPx: Math.max(1, box.width),\n\t\t};\n\t}\n\n\t#barsFor({ isDirty, look, size }: SurfaceFrame<Colour, Length>): Path2D | undefined {\n\t\tif (!isDirty && this.#bars) return this.#bars;\n\n\t\tconst peaks = this.#peaks;\n\t\tconst groove = stripGroove(size, look.lengths);\n\t\tconst drawn = groove ? [groove] : [];\n\t\tconst bars = peaks && peaks.length > 0 ? stripBars(peaks, size, look.lengths) : drawn;\n\n\t\tthis.#bars = undefined;\n\t\tif (bars.length === 0) return undefined;\n\n\t\tconst path = new Path2D();\n\n\t\tfor (const { height, radius, width, x, y } of bars) {\n\t\t\tpath.roundRect(x, y, width, height, radius);\n\t\t}\n\t\tthis.#bars = path;\n\n\t\treturn path;\n\t}\n\n\t#grab(event: PointerEvent): ValueAxis {\n\t\tconst axis = this.#axis();\n\t\tconst at = (event.clientX - axis.startPx) / axis.travelPx;\n\t\tconst outside = this.#outside();\n\t\tconst grabbed = {\n\t\t\tfromProportion: clampProportion(at),\n\t\t\tposition: axis.position,\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\n\t\tthis.input(this.#markerAt(event, axis) ?? this.mapping().valueAt(at));\n\n\t\treturn outside ? { ...grabbed, outside } : grabbed;\n\t}\n\n\t#markerAt(\n\t\tevent: PointerEvent,\n\t\t{ startPx, topPx, travelPx }: { startPx: number; topPx: number; travelPx: number },\n\t): number | undefined {\n\t\tconst index = this.#markerBand.at(\n\t\t\t{ x: event.clientX - startPx, y: event.clientY - topPx },\n\t\t\ttravelPx,\n\t\t);\n\n\t\treturn index === undefined ? undefined : this.markerList()[index]?.start;\n\t}\n\n\t#outside(): ((event: PointerEvent) => boolean) | undefined {\n\t\tif (!this.cancellable) return undefined;\n\n\t\tconst box = this.control.getBoundingClientRect();\n\t\tconst zone = readPxProperty(\n\t\t\tgetComputedStyle(this.control),\n\t\t\t'--_sonic-cancel-zone',\n\t\t\tcancelZonePx,\n\t\t);\n\n\t\treturn (event) => event.clientY < box.top - zone || event.clientY > box.bottom + zone;\n\t}\n\n\t#placeReadout(): void {\n\t\tconst at = this.mapping().proportionOf(this.readoutValue());\n\n\t\tthis.control.style.setProperty('--_sonic-wavestrip-readout-at', String(at));\n\t}\n\n\t#stripRegions(width: number): StripRegions {\n\t\tconst mapping = this.mapping();\n\t\tconst { proportionOf } = mapping;\n\t\tconst { played, scrub } = scrubRegions(this.scrubState(), this.value, mapping);\n\n\t\treturn stripRegions(\n\t\t\t{\n\t\t\t\tbuffered: this.#buffered.map(([start, end]) => [proportionOf(start), proportionOf(end)]),\n\t\t\t\tplayed,\n\t\t\t\t...(scrub === undefined ? {} : { scrub }),\n\t\t\t},\n\t\t\twidth,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -2,15 +2,14 @@ import { clampProportion, toNumber } from "../lib/math.js";
2
2
  import { writeAttribute } from "../lib/write-attribute.js";
3
3
  import { SonicFormElement } from "./form-element.js";
4
4
  import { requireChild, template } from "../lib/render.js";
5
- import { bindDrag } from "../lib/pointer-drag.js";
6
5
  import { Readout } from "./readout.js";
7
- import { ReadoutClaim, revealDelay } from "./readout-claim.js";
8
- import { dragThresholdPx } from "../lib/drag-step.js";
6
+ import { ReadoutClaim } from "./readout-claim.js";
9
7
  import { focusByPointer } from "../lib/focus-by-pointer.js";
10
8
  import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
11
- import { resetKeys, valueMapping } from "../lib/value-mapping.js";
12
- import { moveTo, resnap, setAsked } from "../lib/asked-value.js";
13
- import { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from "../lib/field.js";
9
+ import { resetKeys } from "../lib/value-mapping.js";
10
+ import { createValueModel } from "../lib/value-model.js";
11
+ import { pointerPosition } from "../lib/field.js";
12
+ import { bindFieldGesture } from "./field-gesture.js";
14
13
 
15
14
  //#region src/elements/xy.ts
16
15
  const axes = ["x", "y"];
@@ -75,7 +74,7 @@ var SonicXy = class extends SonicFormElement {
75
74
  this.render();
76
75
  }
77
76
  get pointerType() {
78
- return this.#pointerType;
77
+ return this.#gesture?.pointerType();
79
78
  }
80
79
  get readout() {
81
80
  return this.hasAttribute("readout");
@@ -84,7 +83,7 @@ var SonicXy = class extends SonicFormElement {
84
83
  this.reflect("readout", isEnabled);
85
84
  }
86
85
  get x() {
87
- return this.#values.x.value;
86
+ return this.#models.x.value;
88
87
  }
89
88
  set x(next) {
90
89
  this.#write("x", next);
@@ -138,7 +137,7 @@ var SonicXy = class extends SonicFormElement {
138
137
  this.reflect("x-taper", curve);
139
138
  }
140
139
  get y() {
141
- return this.#values.y.value;
140
+ return this.#models.y.value;
142
141
  }
143
142
  set y(next) {
144
143
  this.#write("y", next);
@@ -198,53 +197,47 @@ var SonicXy = class extends SonicFormElement {
198
197
  #xy = renderXy();
199
198
  #field = requireChild(this.#xy, ".sonic-xy-field", HTMLDivElement);
200
199
  #formatValue;
201
- #mapped;
200
+ #gesture;
201
+ #models = {
202
+ x: createValueModel(() => this.#spec("x")),
203
+ y: createValueModel(() => this.#spec("y"))
204
+ };
202
205
  #parts = {
203
206
  x: requireChild(this.#xy, "[data-sonic-axis=\"x\"]", HTMLDivElement),
204
207
  y: requireChild(this.#xy, "[data-sonic-axis=\"y\"]", HTMLDivElement)
205
208
  };
206
- #pointerDrag;
207
- #pointerType;
208
209
  #puck = requireChild(this.#xy, ".sonic-xy-puck", HTMLDivElement);
209
210
  #readout = new Readout(requireChild(this.#xy, ".sonic-xy-readout", HTMLDivElement));
210
- #values = {
211
- x: {
212
- asked: void 0,
213
- value: 0
214
- },
215
- y: {
216
- asked: void 0,
217
- value: 0
218
- }
219
- };
220
211
  attributeChangedCallback(name) {
221
- this.#mapped = void 0;
222
- if (name === "x" || name === "y") {
223
- if (this.#pointerDrag?.current()) return;
224
- this.#values[name].asked = void 0;
225
- }
226
- if (name === "disabled" && this.isDisabled()) this.#pointerDrag?.end();
227
- this.#refresh();
212
+ if ((name === "x" || name === "y") && this.#gesture?.current()) return;
213
+ if (name === "disabled" && this.isDisabled()) this.#gesture?.end();
214
+ this.#respec(name);
228
215
  }
229
216
  connectedCallback() {
230
217
  this.upgradeProperties("readout", "xDefault", "xLabel", "xMax", "xMidpoint", "xMin", "xOrigin", "xStep", "xTaper", "yDefault", "yLabel", "yMax", "yMidpoint", "yMin", "yOrigin", "yStep", "yTaper", "formatValue", "x", "y");
231
218
  super.connectedCallback();
232
219
  }
233
220
  formResetCallback() {
234
- for (const axis of axes) this.#values[axis].asked = void 0;
235
- this.#refresh();
221
+ this.#respec(...axes);
236
222
  }
237
223
  connect(signal) {
238
224
  const xy = this.#xy;
239
225
  this.keepControl(xy, signal);
240
226
  this.render();
241
- this.checkStyles(xy, "xy.css", [
242
- "cap",
243
- "glass",
244
- "pane",
245
- "well",
246
- "readout"
247
- ]);
227
+ this.checkStyles(xy, "xy.css", {
228
+ box: {
229
+ property: "margin-bottom",
230
+ selector: "[popover]"
231
+ },
232
+ material: [
233
+ "cap",
234
+ "glass",
235
+ "pane",
236
+ "well",
237
+ "readout",
238
+ ["bracket", ".sonic-xy-bracket"]
239
+ ]
240
+ });
248
241
  this.#bindPointer(xy, signal);
249
242
  this.#bindKeys(xy, signal);
250
243
  }
@@ -254,11 +247,11 @@ var SonicXy = class extends SonicFormElement {
254
247
  render() {
255
248
  if (!this.isBound()) return;
256
249
  const { style } = this.#xy;
257
- const mappings = this.#mappings();
258
250
  for (const axis of axes) {
259
- const mapping = mappings[axis];
251
+ const model = this.#models[axis];
252
+ const mapping = model.mapping();
260
253
  const origin = this.optionalNumberAttribute(`${axis}-origin`) ?? mapping.bounds[0];
261
- style.setProperty(`--_sonic-xy-${axis}`, String(mapping.proportionOf(this.#values[axis].value)));
254
+ style.setProperty(`--_sonic-xy-${axis}`, String(mapping.proportionOf(model.value)));
262
255
  style.setProperty(`--_sonic-xy-${axis}-origin`, String(mapping.proportionOf(origin)));
263
256
  }
264
257
  this.#renderAria();
@@ -271,7 +264,7 @@ var SonicXy = class extends SonicFormElement {
271
264
  this.y = y;
272
265
  }
273
266
  #axisText(axis) {
274
- const { value } = this.#values[axis];
267
+ const { value } = this.#models[axis];
275
268
  return `${this.getAttribute(`${axis}-label`) ?? axis.toUpperCase()} ${this.#formatValue?.(value, axis) ?? String(value)}`;
276
269
  }
277
270
  #bindKeys(xy, signal) {
@@ -297,32 +290,22 @@ var SonicXy = class extends SonicFormElement {
297
290
  xy.addEventListener("mousedown", (event) => {
298
291
  event.preventDefault();
299
292
  }, { signal });
300
- this.#pointerDrag = bindDrag(xy, {
301
- cancel: (drag) => {
302
- this.#input(drag.from);
303
- },
293
+ this.#gesture = bindFieldGesture(xy, {
294
+ claim: this.#claim,
304
295
  grab: (event) => {
305
296
  if (this.isDisabled() || isMenuPress(event)) return;
306
297
  focusByPointer(this.focusTarget());
307
- this.#pointerType = event.pointerType;
308
298
  if (!isResetPress(event)) {
309
299
  this.toggleState("dragging", true);
310
300
  return this.#grab(event);
311
301
  }
312
302
  this.#reset();
313
- this.#pointerType = void 0;
314
303
  },
315
- move: (drag, event) => {
316
- const step = stepFieldDrag(this.#mappings(), drag.state, pointerMove(event));
317
- drag.state = step.state;
318
- if (step.state.isEngaged) this.#claim.reveal("drag");
319
- if (!this.#input(step)) this.#renderReadout();
304
+ input: (_hold, next) => {
305
+ if (!this.#input(next)) this.#renderReadout();
320
306
  },
321
- release: (drag) => {
322
- const hasMoved = this.x !== drag.from.x || this.y !== drag.from.y;
323
- this.#claim.press(false);
324
- if (hasMoved) this.dispatchEvent(new Event("change", { bubbles: true }));
325
- this.#pointerType = void 0;
307
+ release: (_hold, moved) => {
308
+ if (moved.length > 0) this.dispatchEvent(new Event("change", { bubbles: true }));
326
309
  },
327
310
  toggle: (isDragging) => {
328
311
  this.toggleState("dragging", isDragging);
@@ -333,13 +316,13 @@ var SonicXy = class extends SonicFormElement {
333
316
  if (this.#input(next)) this.dispatchEvent(new Event("change", { bubbles: true }));
334
317
  }
335
318
  #grab(event) {
336
- const from = {
337
- x: this.x,
338
- y: this.y
339
- };
340
319
  const field = this.#field.getBoundingClientRect();
341
320
  const puck = this.#puck.getBoundingClientRect();
342
- const mappings = this.#mappings();
321
+ const { x, y } = this.#models;
322
+ const from = {
323
+ x: x.value,
324
+ y: y.value
325
+ };
343
326
  const travel = {
344
327
  x: Math.max(1, field.width - puck.width),
345
328
  y: Math.max(1, field.height - puck.height)
@@ -351,32 +334,26 @@ var SonicXy = class extends SonicFormElement {
351
334
  };
352
335
  const isOnPuck = event.target instanceof Node && this.#puck.contains(event.target);
353
336
  if (!isOnPuck) this.#input({
354
- x: mappings.x.valueAt(pressed.x),
355
- y: mappings.y.valueAt(pressed.y)
337
+ x: x.mapping().valueAt(pressed.x),
338
+ y: y.mapping().valueAt(pressed.y)
356
339
  });
357
- this.#claim.press(true, revealDelay(this.#xy));
358
- const start = (axis) => ({
359
- from: this.#values[axis].value,
360
- mapping: mappings[axis],
361
- proportion: isOnPuck ? mappings[axis].proportionOf(this.#values[axis].value) : clampProportion(pressed[axis]),
340
+ const hold = (axis) => ({
341
+ from: from[axis],
342
+ model: this.#models[axis],
343
+ ...isOnPuck ? {} : { proportion: clampProportion(pressed[axis]) },
362
344
  travelPx: travel[axis]
363
345
  });
364
- return {
365
- from,
366
- state: startFieldDrag({
367
- position,
368
- thresholdPx: dragThresholdPx(event.pointerType),
369
- x: start("x"),
370
- y: start("y")
371
- })
372
- };
346
+ return { axes: {
347
+ x: hold("x"),
348
+ y: hold("y")
349
+ } };
373
350
  }
374
351
  #hasDefault() {
375
352
  return this.xDefault !== void 0 || this.yDefault !== void 0;
376
353
  }
377
354
  #input(next) {
378
355
  let hasMoved = false;
379
- for (const axis of axes) if (moveTo(this.#values[axis], this.#mappings()[axis], next[axis] ?? NaN)) hasMoved = true;
356
+ for (const axis of axes) if (this.#models[axis].input(next[axis] ?? NaN)) hasMoved = true;
380
357
  if (!hasMoved) return false;
381
358
  this.render();
382
359
  this.dispatchEvent(new Event("input", {
@@ -386,9 +363,8 @@ var SonicXy = class extends SonicFormElement {
386
363
  return true;
387
364
  }
388
365
  #keyTo(axis, key) {
389
- const mapping = this.#mappings()[axis];
390
- const next = mapping.keyTarget(key, this.#values[axis].value);
391
- const hasMoved = moveTo(this.#values[axis], mapping, next ?? NaN);
366
+ const model = this.#models[axis];
367
+ const hasMoved = model.input(model.mapping().keyTarget(key, model.value) ?? NaN);
392
368
  this.#currentAxis = axis;
393
369
  this.render();
394
370
  this.#parts[axis].focus();
@@ -399,46 +375,18 @@ var SonicXy = class extends SonicFormElement {
399
375
  }));
400
376
  this.dispatchEvent(new Event("change", { bubbles: true }));
401
377
  }
402
- #mapping(axis) {
403
- const midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);
404
- return valueMapping({
405
- max: this.numberAttribute(`${axis}-max`, 100),
406
- ...midpoint === void 0 ? {} : { midpoint },
407
- isNotched: false,
408
- isWrapping: false,
409
- min: this.numberAttribute(`${axis}-min`, 0),
410
- step: this.numberAttribute(`${axis}-step`, 1),
411
- taper: this.#taper(axis)
412
- });
413
- }
414
- #mappings() {
415
- if (this.#mapped) return this.#mapped;
416
- this.#mapped = {
417
- x: this.#mapping("x"),
418
- y: this.#mapping("y")
419
- };
420
- return this.#mapped;
421
- }
422
- #refresh() {
423
- const mappings = this.#mappings();
424
- for (const axis of axes) {
425
- const mapping = mappings[axis];
426
- resnap(this.#values[axis], mapping, this.numberAttribute(axis, mapping.bounds[0]));
427
- }
428
- this.render();
429
- }
430
378
  #renderAria() {
431
- const mappings = this.#mappings();
432
379
  const isDisabled = this.isDisabled();
433
380
  for (const axis of axes) {
434
381
  const part = this.#parts[axis];
435
- const [low, high] = mappings[axis].bounds;
382
+ const model = this.#models[axis];
383
+ const [low, high] = model.mapping().bounds;
436
384
  const isCurrent = axis === this.#currentAxis;
437
385
  const stop = isCurrent ? "0" : "-1";
438
386
  writeAttribute(part, "aria-orientation", orientations[axis]);
439
387
  writeAttribute(part, "aria-valuemin", String(low));
440
388
  writeAttribute(part, "aria-valuemax", String(high));
441
- writeAttribute(part, "aria-valuenow", String(this.#values[axis].value));
389
+ writeAttribute(part, "aria-valuenow", String(model.value));
442
390
  writeAttribute(part, "aria-valuetext", this.#valueText(axis));
443
391
  this.forwardNaming(part, true);
444
392
  writeAttribute(part, "aria-hidden", isCurrent ? void 0 : "true");
@@ -471,6 +419,22 @@ var SonicXy = class extends SonicFormElement {
471
419
  y: this.yDefault
472
420
  });
473
421
  }
422
+ #respec(...forgotten) {
423
+ for (const axis of axes) this.#models[axis].respec(this.numberAttribute(axis, this.numberAttribute(`${axis}-min`, 0)), { forgetAsk: forgotten.includes(axis) });
424
+ this.render();
425
+ }
426
+ #spec(axis) {
427
+ const midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);
428
+ return {
429
+ max: this.numberAttribute(`${axis}-max`, 100),
430
+ ...midpoint === void 0 ? {} : { midpoint },
431
+ isNotched: false,
432
+ isWrapping: false,
433
+ min: this.numberAttribute(`${axis}-min`, 0),
434
+ step: this.numberAttribute(`${axis}-step`, 1),
435
+ taper: this.#taper(axis)
436
+ };
437
+ }
474
438
  #taper(axis) {
475
439
  return this.getAttribute(`${axis}-taper`) === "log" ? "log" : "linear";
476
440
  }
@@ -478,14 +442,12 @@ var SonicXy = class extends SonicFormElement {
478
442
  return `${this.#axisText(axis)}, ${this.#axisText(axis === "x" ? "y" : "x")}`;
479
443
  }
480
444
  #write(axis, next) {
481
- if (this.#pointerDrag?.current()) return;
482
- const axisValue = this.#values[axis];
445
+ if (this.#gesture?.current()) return;
483
446
  if (next === void 0 || next === null) {
484
- axisValue.asked = void 0;
485
- this.#refresh();
447
+ this.#respec(axis);
486
448
  return;
487
449
  }
488
- if (setAsked(axisValue, this.#mappings()[axis], toNumber(next))) this.render();
450
+ if (this.#models[axis].write(toNumber(next))) this.render();
489
451
  }
490
452
  };
491
453
 
@@ -1 +1 @@
1
- {"version":3,"file":"xy.js","names":[],"sources":["../../../src/elements/xy.ts"],"sourcesContent":["import type { AskedValue } from '#lib/asked-value.ts';\nimport type { FieldAxis, FieldDragState, FieldPoint } from '#lib/field.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\nimport type { ValueMapping, ValueSpec } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { ReadoutClaim, revealDelay } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { moveTo, resnap, setAsked } from '#lib/asked-value.ts';\nimport { dragThresholdPx } from '#lib/drag-step.ts';\nimport { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag } from '#lib/field.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clampProportion, toNumber } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { resetKeys, valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-xy': SonicXy;\n\t}\n}\n\ninterface XyDrag {\n\tfrom: FieldPoint;\n\tstate: FieldDragState;\n}\n\ninterface XyInput {\n\tx: number | undefined;\n\ty: number | undefined;\n}\n\nconst axes = ['x', 'y'] as const;\n\nconst keyMoves = new Map<string, [FieldAxis, string]>([\n\t['ArrowDown', ['y', 'ArrowDown']],\n\t['ArrowLeft', ['x', 'ArrowLeft']],\n\t['ArrowRight', ['x', 'ArrowRight']],\n\t['ArrowUp', ['y', 'ArrowUp']],\n\t['End', ['x', 'PageUp']],\n\t['Home', ['x', 'PageDown']],\n\t['PageDown', ['y', 'PageDown']],\n\t['PageUp', ['y', 'PageUp']],\n]);\n\nconst orientations = { x: 'horizontal', y: 'vertical' } as const;\n\nconst renderXy = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-xy\">\n\t\t\t<div class=\"sonic-xy-bracket\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-xy-field\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-xy-puck\">\n\t\t\t\t<div class=\"sonic-xy-axis\" data-sonic-axis=\"x\" role=\"slider\" tabindex=\"0\"></div>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"sonic-xy-axis\"\n\t\t\t\t\tdata-sonic-axis=\"y\"\n\t\t\t\t\trole=\"slider\"\n\t\t\t\t\ttabindex=\"-1\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t></div>\n\t\t\t</div>\n\t\t\t<div class=\"sonic-xy-readout\" aria-hidden=\"true\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicXy extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'name',\n\t\t'readout',\n\t\t'x',\n\t\t'x-label',\n\t\t'x-max',\n\t\t'x-midpoint',\n\t\t'x-min',\n\t\t'x-origin',\n\t\t'x-step',\n\t\t'x-taper',\n\t\t'y',\n\t\t'y-label',\n\t\t'y-max',\n\t\t'y-midpoint',\n\t\t'y-min',\n\t\t'y-origin',\n\t\t'y-step',\n\t\t'y-taper',\n\t];\n\n\tget formatValue(): ((value: number, axis: FieldAxis) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number, axis: FieldAxis) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget pointerType(): string | undefined {\n\t\treturn this.#pointerType;\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isEnabled: boolean) {\n\t\tthis.reflect('readout', isEnabled);\n\t}\n\n\tget x(): number {\n\t\treturn this.#values.x.value;\n\t}\n\n\tset x(next: null | number | undefined) {\n\t\tthis.#write('x', next);\n\t}\n\n\tget xDefault(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-default');\n\t}\n\n\tset xDefault(value: number | undefined) {\n\t\tthis.reflect('x-default', value);\n\t}\n\n\tget xLabel(): string | undefined {\n\t\treturn this.getAttribute('x-label') ?? undefined;\n\t}\n\n\tset xLabel(label: string | undefined) {\n\t\tthis.reflect('x-label', label);\n\t}\n\n\tget xMax(): number {\n\t\treturn this.numberAttribute('x-max', 100);\n\t}\n\n\tset xMax(value: number | undefined) {\n\t\tthis.reflect('x-max', value);\n\t}\n\n\tget xMidpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-midpoint');\n\t}\n\n\tset xMidpoint(value: number | undefined) {\n\t\tthis.reflect('x-midpoint', value);\n\t}\n\n\tget xMin(): number {\n\t\treturn this.numberAttribute('x-min', 0);\n\t}\n\n\tset xMin(value: number | undefined) {\n\t\tthis.reflect('x-min', value);\n\t}\n\n\tget xOrigin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-origin');\n\t}\n\n\tset xOrigin(value: number | undefined) {\n\t\tthis.reflect('x-origin', value);\n\t}\n\n\tget xStep(): number {\n\t\treturn this.numberAttribute('x-step', 1);\n\t}\n\n\tset xStep(value: number | undefined) {\n\t\tthis.reflect('x-step', value);\n\t}\n\n\tget xTaper(): ValueSpec['taper'] {\n\t\treturn this.#taper('x');\n\t}\n\n\tset xTaper(curve: undefined | ValueSpec['taper']) {\n\t\tthis.reflect('x-taper', curve);\n\t}\n\n\tget y(): number {\n\t\treturn this.#values.y.value;\n\t}\n\n\tset y(next: null | number | undefined) {\n\t\tthis.#write('y', next);\n\t}\n\n\tget yDefault(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-default');\n\t}\n\n\tset yDefault(value: number | undefined) {\n\t\tthis.reflect('y-default', value);\n\t}\n\n\tget yLabel(): string | undefined {\n\t\treturn this.getAttribute('y-label') ?? undefined;\n\t}\n\n\tset yLabel(label: string | undefined) {\n\t\tthis.reflect('y-label', label);\n\t}\n\n\tget yMax(): number {\n\t\treturn this.numberAttribute('y-max', 100);\n\t}\n\n\tset yMax(value: number | undefined) {\n\t\tthis.reflect('y-max', value);\n\t}\n\n\tget yMidpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-midpoint');\n\t}\n\n\tset yMidpoint(value: number | undefined) {\n\t\tthis.reflect('y-midpoint', value);\n\t}\n\n\tget yMin(): number {\n\t\treturn this.numberAttribute('y-min', 0);\n\t}\n\n\tset yMin(value: number | undefined) {\n\t\tthis.reflect('y-min', value);\n\t}\n\n\tget yOrigin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-origin');\n\t}\n\n\tset yOrigin(value: number | undefined) {\n\t\tthis.reflect('y-origin', value);\n\t}\n\n\tget yStep(): number {\n\t\treturn this.numberAttribute('y-step', 1);\n\t}\n\n\tset yStep(value: number | undefined) {\n\t\tthis.reflect('y-step', value);\n\t}\n\n\tget yTaper(): ValueSpec['taper'] {\n\t\treturn this.#taper('y');\n\t}\n\n\tset yTaper(curve: undefined | ValueSpec['taper']) {\n\t\tthis.reflect('y-taper', curve);\n\t}\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\t#currentAxis: FieldAxis = 'x';\n\n\treadonly #xy = renderXy();\n\n\treadonly #field = requireChild(this.#xy, '.sonic-xy-field', HTMLDivElement);\n\n\t#formatValue: ((value: number, axis: FieldAxis) => string) | undefined;\n\n\t#mapped: Record<FieldAxis, ValueMapping> | undefined;\n\n\treadonly #parts: Record<FieldAxis, HTMLDivElement> = {\n\t\tx: requireChild(this.#xy, '[data-sonic-axis=\"x\"]', HTMLDivElement),\n\t\ty: requireChild(this.#xy, '[data-sonic-axis=\"y\"]', HTMLDivElement),\n\t};\n\n\t#pointerDrag: PointerDrag<XyDrag> | undefined;\n\n\t#pointerType: string | undefined;\n\n\treadonly #puck = requireChild(this.#xy, '.sonic-xy-puck', HTMLDivElement);\n\n\treadonly #readout = new Readout(requireChild(this.#xy, '.sonic-xy-readout', HTMLDivElement));\n\n\treadonly #values: Record<FieldAxis, AskedValue> = {\n\t\tx: { asked: undefined, value: 0 },\n\t\ty: { asked: undefined, value: 0 },\n\t};\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapped = undefined;\n\t\tif (name === 'x' || name === 'y') {\n\t\t\tif (this.#pointerDrag?.current()) return;\n\n\t\t\tthis.#values[name].asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#pointerDrag?.end();\n\n\t\tthis.#refresh();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'readout',\n\t\t\t'xDefault',\n\t\t\t'xLabel',\n\t\t\t'xMax',\n\t\t\t'xMidpoint',\n\t\t\t'xMin',\n\t\t\t'xOrigin',\n\t\t\t'xStep',\n\t\t\t'xTaper',\n\t\t\t'yDefault',\n\t\t\t'yLabel',\n\t\t\t'yMax',\n\t\t\t'yMidpoint',\n\t\t\t'yMin',\n\t\t\t'yOrigin',\n\t\t\t'yStep',\n\t\t\t'yTaper',\n\t\t\t'formatValue',\n\t\t\t'x',\n\t\t\t'y',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\toverride formResetCallback(): void {\n\t\tfor (const axis of axes) this.#values[axis].asked = undefined;\n\n\t\tthis.#refresh();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst xy = this.#xy;\n\n\t\tthis.keepControl(xy, signal);\n\t\tthis.render();\n\t\tthis.checkStyles(xy, 'xy.css', ['cap', 'glass', 'pane', 'well', 'readout']);\n\t\tthis.#bindPointer(xy, signal);\n\t\tthis.#bindKeys(xy, signal);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#parts[this.#currentAxis];\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst { style } = this.#xy;\n\t\tconst mappings = this.#mappings();\n\n\t\tfor (const axis of axes) {\n\t\t\tconst mapping = mappings[axis];\n\t\t\tconst origin = this.optionalNumberAttribute(`${axis}-origin`) ?? mapping.bounds[0];\n\n\t\t\tstyle.setProperty(\n\t\t\t\t`--_sonic-xy-${axis}`,\n\t\t\t\tString(mapping.proportionOf(this.#values[axis].value)),\n\t\t\t);\n\t\t\tstyle.setProperty(`--_sonic-xy-${axis}-origin`, String(mapping.proportionOf(origin)));\n\t\t}\n\t\tthis.#renderAria();\n\t\tthis.#renderForm();\n\t\tthis.#renderReadout();\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tconst [x = NaN, y = NaN] = state.split(',').map(Number);\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\t#axisText(axis: FieldAxis): string {\n\t\tconst { value } = this.#values[axis];\n\t\tconst label = this.getAttribute(`${axis}-label`) ?? axis.toUpperCase();\n\n\t\treturn `${label} ${this.#formatValue?.(value, axis) ?? String(value)}`;\n\t}\n\n\t#bindKeys(xy: HTMLElement, signal: AbortSignal): void {\n\t\txy.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.isDisabled() || event.defaultPrevented) return;\n\n\t\t\t\tconst move = keyMoves.get(event.key);\n\t\t\t\tif (!move && !(resetKeys.has(event.key) && this.#hasDefault())) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (move) this.#keyTo(...move);\n\t\t\t\telse this.#reset();\n\t\t\t\tthis.#claim.reveal('keys');\n\t\t\t\tthis.#renderReadout();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\txy.addEventListener(\n\t\t\t'focusout',\n\t\t\t(event) => {\n\t\t\t\t// A key on the other axis moves focus between the two parts\n\t\t\t\tif (event.relatedTarget instanceof Node && xy.contains(event.relatedTarget)) return;\n\t\t\t\tif (this.#claim.conceal('keys')) this.#renderReadout();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#claim.conceal('keys');\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bindPointer(xy: HTMLElement, signal: AbortSignal): void {\n\t\t// A press on the glass would take focus off the axis part and select the page's text\n\t\txy.addEventListener(\n\t\t\t'mousedown',\n\t\t\t(event) => {\n\t\t\t\tevent.preventDefault();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#pointerDrag = bindDrag(\n\t\t\txy,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tthis.#input(drag.from);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (this.isDisabled() || isMenuPress(event)) return;\n\n\t\t\t\t\tfocusByPointer(this.focusTarget());\n\t\t\t\t\tthis.#pointerType = event.pointerType;\n\t\t\t\t\tif (!isResetPress(event)) {\n\t\t\t\t\t\tthis.toggleState('dragging', true);\n\n\t\t\t\t\t\treturn this.#grab(event);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.#reset();\n\t\t\t\t\tthis.#pointerType = undefined;\n\n\t\t\t\t\treturn;\n\t\t\t\t},\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tconst step = stepFieldDrag(this.#mappings(), drag.state, pointerMove(event));\n\n\t\t\t\t\tdrag.state = step.state;\n\t\t\t\t\tif (step.state.isEngaged) this.#claim.reveal('drag');\n\t\t\t\t\tif (!this.#input(step)) this.#renderReadout();\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tconst hasMoved = this.x !== drag.from.x || this.y !== drag.from.y;\n\n\t\t\t\t\tthis.#claim.press(false);\n\t\t\t\t\tif (hasMoved) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\tthis.#pointerType = undefined;\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#commit(next: XyInput): void {\n\t\tif (this.#input(next)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#grab(event: PointerEvent): XyDrag {\n\t\tconst from = { x: this.x, y: this.y };\n\t\tconst field = this.#field.getBoundingClientRect();\n\t\tconst puck = this.#puck.getBoundingClientRect();\n\t\tconst mappings = this.#mappings();\n\t\tconst travel = {\n\t\t\tx: Math.max(1, field.width - puck.width),\n\t\t\ty: Math.max(1, field.height - puck.height),\n\t\t};\n\t\tconst position = pointerPosition(event);\n\t\tconst pressed = {\n\t\t\tx: (position.x - field.left - puck.width / 2) / travel.x,\n\t\t\ty: (position.y + field.bottom - puck.height / 2) / travel.y,\n\t\t};\n\t\tconst isOnPuck = event.target instanceof Node && this.#puck.contains(event.target);\n\n\t\tif (!isOnPuck)\n\t\t\tthis.#input({ x: mappings.x.valueAt(pressed.x), y: mappings.y.valueAt(pressed.y) });\n\t\tthis.#claim.press(true, revealDelay(this.#xy));\n\n\t\tconst start = (axis: FieldAxis) => ({\n\t\t\tfrom: this.#values[axis].value,\n\t\t\tmapping: mappings[axis],\n\t\t\tproportion: isOnPuck\n\t\t\t\t? mappings[axis].proportionOf(this.#values[axis].value)\n\t\t\t\t: clampProportion(pressed[axis]),\n\t\t\ttravelPx: travel[axis],\n\t\t});\n\n\t\treturn {\n\t\t\tfrom,\n\t\t\tstate: startFieldDrag({\n\t\t\t\tposition,\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#hasDefault(): boolean {\n\t\treturn this.xDefault !== undefined || this.yDefault !== undefined;\n\t}\n\n\t#input(next: XyInput): boolean {\n\t\tlet hasMoved = false;\n\n\t\tfor (const axis of axes) {\n\t\t\tif (moveTo(this.#values[axis], this.#mappings()[axis], next[axis] ?? NaN)) hasMoved = true;\n\t\t}\n\t\tif (!hasMoved) return false;\n\n\t\tthis.render();\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\t#keyTo(axis: FieldAxis, key: string): void {\n\t\tconst mapping = this.#mappings()[axis];\n\t\tconst next = mapping.keyTarget(key, this.#values[axis].value);\n\t\tconst hasMoved = moveTo(this.#values[axis], mapping, next ?? NaN);\n\n\t\tthis.#currentAxis = axis;\n\t\tthis.render();\n\t\tthis.#parts[axis].focus();\n\t\tif (!hasMoved) return;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#mapping(axis: FieldAxis): ValueMapping {\n\t\tconst midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);\n\n\t\treturn valueMapping({\n\t\t\tmax: this.numberAttribute(`${axis}-max`, 100),\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: false,\n\t\t\tisWrapping: false,\n\t\t\tmin: this.numberAttribute(`${axis}-min`, 0),\n\t\t\tstep: this.numberAttribute(`${axis}-step`, 1),\n\t\t\ttaper: this.#taper(axis),\n\t\t});\n\t}\n\n\t#mappings(): Record<FieldAxis, ValueMapping> {\n\t\tif (this.#mapped) return this.#mapped;\n\n\t\tthis.#mapped = { x: this.#mapping('x'), y: this.#mapping('y') };\n\n\t\treturn this.#mapped;\n\t}\n\n\t#refresh(): void {\n\t\tconst mappings = this.#mappings();\n\n\t\tfor (const axis of axes) {\n\t\t\tconst mapping = mappings[axis];\n\n\t\t\tresnap(this.#values[axis], mapping, this.numberAttribute(axis, mapping.bounds[0]));\n\t\t}\n\t\tthis.render();\n\t}\n\n\t#renderAria(): void {\n\t\tconst mappings = this.#mappings();\n\t\tconst isDisabled = this.isDisabled();\n\n\t\tfor (const axis of axes) {\n\t\t\tconst part = this.#parts[axis];\n\t\t\tconst [low, high] = mappings[axis].bounds;\n\t\t\tconst isCurrent = axis === this.#currentAxis;\n\t\t\tconst stop = isCurrent ? '0' : '-1';\n\n\t\t\twriteAttribute(part, 'aria-orientation', orientations[axis]);\n\t\t\twriteAttribute(part, 'aria-valuemin', String(low));\n\t\t\twriteAttribute(part, 'aria-valuemax', String(high));\n\t\t\twriteAttribute(part, 'aria-valuenow', String(this.#values[axis].value));\n\t\t\twriteAttribute(part, 'aria-valuetext', this.#valueText(axis));\n\t\t\tthis.forwardNaming(part, true);\n\t\t\twriteAttribute(part, 'aria-hidden', isCurrent ? undefined : 'true');\n\t\t\twriteAttribute(part, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t\twriteAttribute(part, 'tabindex', isDisabled ? undefined : stop);\n\t\t}\n\t}\n\n\t#renderForm(): void {\n\t\tconst { name, x, y } = this;\n\t\tconst state = `${String(x)},${String(y)}`;\n\n\t\tif (name === '') {\n\t\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\t\tthis.writeFormValue(null, state);\n\t\t\treturn;\n\t\t}\n\n\t\tconst fields = new FormData();\n\n\t\tfields.set(`${name}.x`, String(x));\n\t\tfields.set(`${name}.y`, String(y));\n\t\tthis.writeFormValue(fields, state);\n\t}\n\n\t#renderReadout(): void {\n\t\tthis.#readout.show({\n\t\t\tanchor: this.#puck,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: this.#valueText('x'),\n\t\t});\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit({ x: this.xDefault, y: this.yDefault });\n\t}\n\n\t#taper(axis: FieldAxis): ValueSpec['taper'] {\n\t\treturn this.getAttribute(`${axis}-taper`) === 'log' ? 'log' : 'linear';\n\t}\n\n\t#valueText(axis: FieldAxis): string {\n\t\treturn `${this.#axisText(axis)}, ${this.#axisText(axis === 'x' ? 'y' : 'x')}`;\n\t}\n\n\t#write(axis: FieldAxis, next: null | number | undefined): void {\n\t\tif (this.#pointerDrag?.current()) return;\n\n\t\tconst axisValue = this.#values[axis];\n\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\taxisValue.asked = undefined;\n\t\t\tthis.#refresh();\n\t\t\treturn;\n\t\t}\n\n\t\tif (setAsked(axisValue, this.#mappings()[axis], toNumber(next))) this.render();\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"xy.js","names":[],"sources":["../../../src/elements/xy.ts"],"sourcesContent":["import type { FieldGesture, FieldHold, FieldInput } from '#elements/field-gesture.ts';\nimport type { FieldAxis } from '#lib/field.ts';\nimport type { ValueSpec } from '#lib/value-mapping.ts';\nimport type { ValueModel } from '#lib/value-model.ts';\n\nimport { bindFieldGesture } from '#elements/field-gesture.ts';\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { pointerPosition } from '#lib/field.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clampProportion, toNumber } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { resetKeys } from '#lib/value-mapping.ts';\nimport { createValueModel } from '#lib/value-model.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-xy': SonicXy;\n\t}\n}\n\nconst axes = ['x', 'y'] as const;\n\nconst keyMoves = new Map<string, [FieldAxis, string]>([\n\t['ArrowDown', ['y', 'ArrowDown']],\n\t['ArrowLeft', ['x', 'ArrowLeft']],\n\t['ArrowRight', ['x', 'ArrowRight']],\n\t['ArrowUp', ['y', 'ArrowUp']],\n\t['End', ['x', 'PageUp']],\n\t['Home', ['x', 'PageDown']],\n\t['PageDown', ['y', 'PageDown']],\n\t['PageUp', ['y', 'PageUp']],\n]);\n\nconst orientations = { x: 'horizontal', y: 'vertical' } as const;\n\nconst renderXy = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-xy\">\n\t\t\t<div class=\"sonic-xy-bracket\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-xy-field\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-xy-puck\">\n\t\t\t\t<div class=\"sonic-xy-axis\" data-sonic-axis=\"x\" role=\"slider\" tabindex=\"0\"></div>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"sonic-xy-axis\"\n\t\t\t\t\tdata-sonic-axis=\"y\"\n\t\t\t\t\trole=\"slider\"\n\t\t\t\t\ttabindex=\"-1\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t></div>\n\t\t\t</div>\n\t\t\t<div class=\"sonic-xy-readout\" aria-hidden=\"true\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicXy extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'name',\n\t\t'readout',\n\t\t'x',\n\t\t'x-label',\n\t\t'x-max',\n\t\t'x-midpoint',\n\t\t'x-min',\n\t\t'x-origin',\n\t\t'x-step',\n\t\t'x-taper',\n\t\t'y',\n\t\t'y-label',\n\t\t'y-max',\n\t\t'y-midpoint',\n\t\t'y-min',\n\t\t'y-origin',\n\t\t'y-step',\n\t\t'y-taper',\n\t];\n\n\tget formatValue(): ((value: number, axis: FieldAxis) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number, axis: FieldAxis) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget pointerType(): string | undefined {\n\t\treturn this.#gesture?.pointerType();\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isEnabled: boolean) {\n\t\tthis.reflect('readout', isEnabled);\n\t}\n\n\tget x(): number {\n\t\treturn this.#models.x.value;\n\t}\n\n\tset x(next: null | number | undefined) {\n\t\tthis.#write('x', next);\n\t}\n\n\tget xDefault(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-default');\n\t}\n\n\tset xDefault(value: number | undefined) {\n\t\tthis.reflect('x-default', value);\n\t}\n\n\tget xLabel(): string | undefined {\n\t\treturn this.getAttribute('x-label') ?? undefined;\n\t}\n\n\tset xLabel(label: string | undefined) {\n\t\tthis.reflect('x-label', label);\n\t}\n\n\tget xMax(): number {\n\t\treturn this.numberAttribute('x-max', 100);\n\t}\n\n\tset xMax(value: number | undefined) {\n\t\tthis.reflect('x-max', value);\n\t}\n\n\tget xMidpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-midpoint');\n\t}\n\n\tset xMidpoint(value: number | undefined) {\n\t\tthis.reflect('x-midpoint', value);\n\t}\n\n\tget xMin(): number {\n\t\treturn this.numberAttribute('x-min', 0);\n\t}\n\n\tset xMin(value: number | undefined) {\n\t\tthis.reflect('x-min', value);\n\t}\n\n\tget xOrigin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('x-origin');\n\t}\n\n\tset xOrigin(value: number | undefined) {\n\t\tthis.reflect('x-origin', value);\n\t}\n\n\tget xStep(): number {\n\t\treturn this.numberAttribute('x-step', 1);\n\t}\n\n\tset xStep(value: number | undefined) {\n\t\tthis.reflect('x-step', value);\n\t}\n\n\tget xTaper(): ValueSpec['taper'] {\n\t\treturn this.#taper('x');\n\t}\n\n\tset xTaper(curve: undefined | ValueSpec['taper']) {\n\t\tthis.reflect('x-taper', curve);\n\t}\n\n\tget y(): number {\n\t\treturn this.#models.y.value;\n\t}\n\n\tset y(next: null | number | undefined) {\n\t\tthis.#write('y', next);\n\t}\n\n\tget yDefault(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-default');\n\t}\n\n\tset yDefault(value: number | undefined) {\n\t\tthis.reflect('y-default', value);\n\t}\n\n\tget yLabel(): string | undefined {\n\t\treturn this.getAttribute('y-label') ?? undefined;\n\t}\n\n\tset yLabel(label: string | undefined) {\n\t\tthis.reflect('y-label', label);\n\t}\n\n\tget yMax(): number {\n\t\treturn this.numberAttribute('y-max', 100);\n\t}\n\n\tset yMax(value: number | undefined) {\n\t\tthis.reflect('y-max', value);\n\t}\n\n\tget yMidpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-midpoint');\n\t}\n\n\tset yMidpoint(value: number | undefined) {\n\t\tthis.reflect('y-midpoint', value);\n\t}\n\n\tget yMin(): number {\n\t\treturn this.numberAttribute('y-min', 0);\n\t}\n\n\tset yMin(value: number | undefined) {\n\t\tthis.reflect('y-min', value);\n\t}\n\n\tget yOrigin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('y-origin');\n\t}\n\n\tset yOrigin(value: number | undefined) {\n\t\tthis.reflect('y-origin', value);\n\t}\n\n\tget yStep(): number {\n\t\treturn this.numberAttribute('y-step', 1);\n\t}\n\n\tset yStep(value: number | undefined) {\n\t\tthis.reflect('y-step', value);\n\t}\n\n\tget yTaper(): ValueSpec['taper'] {\n\t\treturn this.#taper('y');\n\t}\n\n\tset yTaper(curve: undefined | ValueSpec['taper']) {\n\t\tthis.reflect('y-taper', curve);\n\t}\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\t#currentAxis: FieldAxis = 'x';\n\n\treadonly #xy = renderXy();\n\n\treadonly #field = requireChild(this.#xy, '.sonic-xy-field', HTMLDivElement);\n\n\t#formatValue: ((value: number, axis: FieldAxis) => string) | undefined;\n\n\t#gesture: FieldGesture<FieldHold> | undefined;\n\n\treadonly #models: Record<FieldAxis, ValueModel> = {\n\t\tx: createValueModel(() => this.#spec('x')),\n\t\ty: createValueModel(() => this.#spec('y')),\n\t};\n\n\treadonly #parts: Record<FieldAxis, HTMLDivElement> = {\n\t\tx: requireChild(this.#xy, '[data-sonic-axis=\"x\"]', HTMLDivElement),\n\t\ty: requireChild(this.#xy, '[data-sonic-axis=\"y\"]', HTMLDivElement),\n\t};\n\n\treadonly #puck = requireChild(this.#xy, '.sonic-xy-puck', HTMLDivElement);\n\n\treadonly #readout = new Readout(requireChild(this.#xy, '.sonic-xy-readout', HTMLDivElement));\n\n\tattributeChangedCallback(name: string): void {\n\t\tif ((name === 'x' || name === 'y') && this.#gesture?.current()) return;\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gesture?.end();\n\n\t\tthis.#respec(name);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'readout',\n\t\t\t'xDefault',\n\t\t\t'xLabel',\n\t\t\t'xMax',\n\t\t\t'xMidpoint',\n\t\t\t'xMin',\n\t\t\t'xOrigin',\n\t\t\t'xStep',\n\t\t\t'xTaper',\n\t\t\t'yDefault',\n\t\t\t'yLabel',\n\t\t\t'yMax',\n\t\t\t'yMidpoint',\n\t\t\t'yMin',\n\t\t\t'yOrigin',\n\t\t\t'yStep',\n\t\t\t'yTaper',\n\t\t\t'formatValue',\n\t\t\t'x',\n\t\t\t'y',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\toverride formResetCallback(): void {\n\t\tthis.#respec(...axes);\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst xy = this.#xy;\n\n\t\tthis.keepControl(xy, signal);\n\t\tthis.render();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(xy, 'xy.css', {\n\t\t\t\tbox: { property: 'margin-bottom', selector: '[popover]' },\n\t\t\t\tmaterial: ['cap', 'glass', 'pane', 'well', 'readout', ['bracket', '.sonic-xy-bracket']],\n\t\t\t});\n\t\tthis.#bindPointer(xy, signal);\n\t\tthis.#bindKeys(xy, signal);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#parts[this.#currentAxis];\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst { style } = this.#xy;\n\n\t\tfor (const axis of axes) {\n\t\t\tconst model = this.#models[axis];\n\t\t\tconst mapping = model.mapping();\n\t\t\tconst origin = this.optionalNumberAttribute(`${axis}-origin`) ?? mapping.bounds[0];\n\n\t\t\tstyle.setProperty(`--_sonic-xy-${axis}`, String(mapping.proportionOf(model.value)));\n\t\t\tstyle.setProperty(`--_sonic-xy-${axis}-origin`, String(mapping.proportionOf(origin)));\n\t\t}\n\t\tthis.#renderAria();\n\t\tthis.#renderForm();\n\t\tthis.#renderReadout();\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tconst [x = NaN, y = NaN] = state.split(',').map(Number);\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\t#axisText(axis: FieldAxis): string {\n\t\tconst { value } = this.#models[axis];\n\t\tconst label = this.getAttribute(`${axis}-label`) ?? axis.toUpperCase();\n\n\t\treturn `${label} ${this.#formatValue?.(value, axis) ?? String(value)}`;\n\t}\n\n\t#bindKeys(xy: HTMLElement, signal: AbortSignal): void {\n\t\txy.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.isDisabled() || event.defaultPrevented) return;\n\n\t\t\t\tconst move = keyMoves.get(event.key);\n\t\t\t\tif (!move && !(resetKeys.has(event.key) && this.#hasDefault())) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (move) this.#keyTo(...move);\n\t\t\t\telse this.#reset();\n\t\t\t\tthis.#claim.reveal('keys');\n\t\t\t\tthis.#renderReadout();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\txy.addEventListener(\n\t\t\t'focusout',\n\t\t\t(event) => {\n\t\t\t\t// A key on the other axis moves focus between the two parts\n\t\t\t\tif (event.relatedTarget instanceof Node && xy.contains(event.relatedTarget)) return;\n\t\t\t\tif (this.#claim.conceal('keys')) this.#renderReadout();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#claim.conceal('keys');\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bindPointer(xy: HTMLElement, signal: AbortSignal): void {\n\t\t// A press on the glass would take focus off the axis part and select the page's text\n\t\txy.addEventListener(\n\t\t\t'mousedown',\n\t\t\t(event) => {\n\t\t\t\tevent.preventDefault();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#gesture = bindFieldGesture(\n\t\t\txy,\n\t\t\t{\n\t\t\t\tclaim: this.#claim,\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (this.isDisabled() || isMenuPress(event)) return;\n\n\t\t\t\t\tfocusByPointer(this.focusTarget());\n\t\t\t\t\tif (!isResetPress(event)) {\n\t\t\t\t\t\tthis.toggleState('dragging', true);\n\n\t\t\t\t\t\treturn this.#grab(event);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.#reset();\n\n\t\t\t\t\treturn;\n\t\t\t\t},\n\t\t\t\tinput: (_hold, next) => {\n\t\t\t\t\tif (!this.#input(next)) this.#renderReadout();\n\t\t\t\t},\n\t\t\t\trelease: (_hold, moved) => {\n\t\t\t\t\tif (moved.length > 0) this.dispatchEvent(new Event('change', { bubbles: true }));\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#commit(next: FieldInput): void {\n\t\tif (this.#input(next)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#grab(event: PointerEvent): FieldHold {\n\t\tconst field = this.#field.getBoundingClientRect();\n\t\tconst puck = this.#puck.getBoundingClientRect();\n\t\tconst { x, y } = this.#models;\n\t\tconst from = { x: x.value, y: y.value };\n\t\tconst travel = {\n\t\t\tx: Math.max(1, field.width - puck.width),\n\t\t\ty: Math.max(1, field.height - puck.height),\n\t\t};\n\t\tconst position = pointerPosition(event);\n\t\tconst pressed = {\n\t\t\tx: (position.x - field.left - puck.width / 2) / travel.x,\n\t\t\ty: (position.y + field.bottom - puck.height / 2) / travel.y,\n\t\t};\n\t\tconst isOnPuck = event.target instanceof Node && this.#puck.contains(event.target);\n\n\t\tif (!isOnPuck) {\n\t\t\tthis.#input({ x: x.mapping().valueAt(pressed.x), y: y.mapping().valueAt(pressed.y) });\n\t\t}\n\n\t\tconst hold = (axis: FieldAxis) => ({\n\t\t\tfrom: from[axis],\n\t\t\tmodel: this.#models[axis],\n\t\t\t...(isOnPuck ? {} : { proportion: clampProportion(pressed[axis]) }),\n\t\t\ttravelPx: travel[axis],\n\t\t});\n\n\t\treturn { axes: { x: hold('x'), y: hold('y') } };\n\t}\n\n\t#hasDefault(): boolean {\n\t\treturn this.xDefault !== undefined || this.yDefault !== undefined;\n\t}\n\n\t#input(next: FieldInput): boolean {\n\t\tlet hasMoved = false;\n\n\t\tfor (const axis of axes) {\n\t\t\tif (this.#models[axis].input(next[axis] ?? NaN)) hasMoved = true;\n\t\t}\n\t\tif (!hasMoved) return false;\n\n\t\tthis.render();\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\t#keyTo(axis: FieldAxis, key: string): void {\n\t\tconst model = this.#models[axis];\n\t\tconst hasMoved = model.input(model.mapping().keyTarget(key, model.value) ?? NaN);\n\n\t\tthis.#currentAxis = axis;\n\t\tthis.render();\n\t\tthis.#parts[axis].focus();\n\t\tif (!hasMoved) return;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#renderAria(): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\tfor (const axis of axes) {\n\t\t\tconst part = this.#parts[axis];\n\t\t\tconst model = this.#models[axis];\n\t\t\tconst [low, high] = model.mapping().bounds;\n\t\t\tconst isCurrent = axis === this.#currentAxis;\n\t\t\tconst stop = isCurrent ? '0' : '-1';\n\n\t\t\twriteAttribute(part, 'aria-orientation', orientations[axis]);\n\t\t\twriteAttribute(part, 'aria-valuemin', String(low));\n\t\t\twriteAttribute(part, 'aria-valuemax', String(high));\n\t\t\twriteAttribute(part, 'aria-valuenow', String(model.value));\n\t\t\twriteAttribute(part, 'aria-valuetext', this.#valueText(axis));\n\t\t\tthis.forwardNaming(part, true);\n\t\t\twriteAttribute(part, 'aria-hidden', isCurrent ? undefined : 'true');\n\t\t\twriteAttribute(part, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t\twriteAttribute(part, 'tabindex', isDisabled ? undefined : stop);\n\t\t}\n\t}\n\n\t#renderForm(): void {\n\t\tconst { name, x, y } = this;\n\t\tconst state = `${String(x)},${String(y)}`;\n\n\t\tif (name === '') {\n\t\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\t\tthis.writeFormValue(null, state);\n\t\t\treturn;\n\t\t}\n\n\t\tconst fields = new FormData();\n\n\t\tfields.set(`${name}.x`, String(x));\n\t\tfields.set(`${name}.y`, String(y));\n\t\tthis.writeFormValue(fields, state);\n\t}\n\n\t#renderReadout(): void {\n\t\tthis.#readout.show({\n\t\t\tanchor: this.#puck,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: this.#valueText('x'),\n\t\t});\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit({ x: this.xDefault, y: this.yDefault });\n\t}\n\n\t#respec(...forgotten: Array<string>): void {\n\t\tfor (const axis of axes) {\n\t\t\tthis.#models[axis].respec(\n\t\t\t\tthis.numberAttribute(axis, this.numberAttribute(`${axis}-min`, 0)),\n\t\t\t\t{ forgetAsk: forgotten.includes(axis) },\n\t\t\t);\n\t\t}\n\t\tthis.render();\n\t}\n\n\t#spec(axis: FieldAxis): ValueSpec {\n\t\tconst midpoint = this.optionalNumberAttribute(`${axis}-midpoint`);\n\n\t\treturn {\n\t\t\tmax: this.numberAttribute(`${axis}-max`, 100),\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: false,\n\t\t\tisWrapping: false,\n\t\t\tmin: this.numberAttribute(`${axis}-min`, 0),\n\t\t\tstep: this.numberAttribute(`${axis}-step`, 1),\n\t\t\ttaper: this.#taper(axis),\n\t\t};\n\t}\n\n\t#taper(axis: FieldAxis): ValueSpec['taper'] {\n\t\treturn this.getAttribute(`${axis}-taper`) === 'log' ? 'log' : 'linear';\n\t}\n\n\t#valueText(axis: FieldAxis): string {\n\t\treturn `${this.#axisText(axis)}, ${this.#axisText(axis === 'x' ? 'y' : 'x')}`;\n\t}\n\n\t#write(axis: FieldAxis, next: null | number | undefined): void {\n\t\tif (this.#gesture?.current()) return;\n\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.#respec(axis);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#models[axis].write(toNumber(next))) this.render();\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { WaveformPeaks } from "./lib/waveform-buckets.js";
2
1
  import { SonicButton } from "./elements/button.js";
3
2
  import { SonicDial } from "./elements/dial.js";
4
3
  import { SonicEnvelope } from "./elements/envelope.js";
@@ -12,10 +11,13 @@ import { SonicSplit } from "./elements/split.js";
12
11
  import { SonicSwitch } from "./elements/switch.js";
13
12
  import { SonicToggle } from "./elements/toggle.js";
14
13
  import { WaveMarker } from "./elements/wave-element.js";
15
- import { LabelRender, PeaksRequest, SonicWaveform } from "./elements/waveform.js";
14
+ import { PeaksRequest } from "./lib/peaks-request.js";
15
+ import { WaveformPeaks } from "./lib/waveform-buckets.js";
16
+ import { LabelRender, SonicWaveform } from "./elements/waveform.js";
16
17
  import { SonicWavestrip } from "./elements/wavestrip.js";
17
18
  import { SonicXy } from "./elements/xy.js";
19
+ import { formatClock, parseClock } from "./lib/clock.js";
18
20
  import { crossfadeGains } from "./lib/crossfade.js";
19
21
  import { envelopeCurve } from "./lib/envelope-curve.js";
20
22
  import { formatPercent, parsePercent } from "./lib/percent.js";
21
- export { type LabelRender, type PeaksRequest, SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, type TimeRegions, type WaveMarker, type WaveformPeaks, crossfadeGains, envelopeCurve, formatPercent, parsePercent };
23
+ export { type LabelRender, type PeaksRequest, SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, type TimeRegions, type WaveMarker, type WaveformPeaks, crossfadeGains, envelopeCurve, formatClock, formatPercent, parseClock, parsePercent };
package/dist/dev/index.js CHANGED
@@ -10,10 +10,11 @@ import { SonicSpectrum } from "./elements/spectrum.js";
10
10
  import { SonicSplit } from "./elements/split.js";
11
11
  import { SonicSwitch } from "./elements/switch.js";
12
12
  import { SonicToggle } from "./elements/toggle.js";
13
+ import { formatClock, parseClock } from "./lib/clock.js";
13
14
  import { SonicWaveform } from "./elements/waveform.js";
14
15
  import { SonicWavestrip } from "./elements/wavestrip.js";
15
16
  import { SonicXy } from "./elements/xy.js";
16
17
  import { crossfadeGains } from "./lib/crossfade.js";
17
18
  import { formatPercent, parsePercent } from "./lib/percent.js";
18
19
 
19
- export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatPercent, parsePercent };
20
+ export { SonicButton, SonicDial, SonicEnvelope, SonicMeter, SonicNumber, SonicSegmented, SonicSlider, SonicSpectrum, SonicSplit, SonicSwitch, SonicToggle, SonicWaveform, SonicWavestrip, SonicXy, crossfadeGains, envelopeCurve, formatClock, formatPercent, parseClock, parsePercent };
@@ -22,6 +22,7 @@ export interface Surface {
22
22
  readonly isVisible: boolean;
23
23
  rebuild: () => void;
24
24
  requestFrame: () => void;
25
+ setFill: (isFilling: boolean) => void;
25
26
  readonly size: SurfaceSize;
26
27
  }
27
28
  //#endregion