@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 +1 @@
1
- {"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { DragState } from '#lib/drag-step.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { ReadoutClaim, revealMs } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueEntry } from '#elements/value-entry.ts';\nimport { dragThresholdPx, startDrag, stepDrag } from '#lib/drag-step.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clamp, toNumber, trimFloat } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { resetKeys, valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\nexport interface ValueAxis {\n\tfromProportion?: number;\n\tisKeptOnCancel?: boolean;\n\toutside?: (event: PointerEvent) => boolean;\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface ValueDrag extends ValueAxis {\n\tfromValue: number;\n\tisOutside: boolean;\n\tisRevealed: boolean;\n\tpointerType: string;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstate: DragState;\n}\n\ninterface KeyScrub {\n\tfromValue: number;\n\tkey: string;\n}\n\nconst detentZonePx = 8;\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\nconst roleAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\ntype ValueLanding = (target: number, direction: -1 | 0 | 1) => number;\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tland: (resolve: undefined | ValueLanding) => void;\n\tlimit: (bounds: [number, number] | undefined) => void;\n\tmapping: () => ValueMapping;\n\tvalue: () => number;\n\twatch: (listener: () => void) => () => void;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'detent',\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tland: (resolve) => {\n\t\t\t\telement.#land = resolve;\n\t\t\t},\n\t\t\tlimit: (bounds) => {\n\t\t\t\telement.#limit = bounds;\n\t\t\t},\n\t\t\tmapping: () => element.mapping(),\n\t\t\tvalue: () => element.#value,\n\t\t\twatch: (listener) => {\n\t\t\t\telement.#watchers.add(listener);\n\n\t\t\t\treturn () => {\n\t\t\t\t\telement.#watchers.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t});\n\t}\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget detent(): number | undefined {\n\t\treturn this.optionalNumberAttribute('detent');\n\t}\n\n\tset detent(value: number | undefined) {\n\t\tthis.reflect('detent', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\tget doublePress(): 'entry' | 'reset' {\n\t\treturn this.getAttribute('double-press') === 'reset' ? 'reset' : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget pointerType(): string | undefined {\n\t\treturn this.#dragging()?.pointerType;\n\t}\n\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.heldWrite(value);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#write(value);\n\t\tif (Number.isFinite(value)) this.#asked = value;\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#value);\n\t}\n\n\t#asked: number | undefined;\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderHold();\n\t});\n\n\t#entry: undefined | ValueEntry;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#heldAt: number | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t#isModulated = false;\n\n\t#isRevealed = false;\n\n\t#keyScrub: KeyScrub | undefined;\n\n\t#land: undefined | ValueLanding;\n\n\t#limit: [number, number] | undefined;\n\n\t// Every attribute it reads is observed; a subclass adding an input to `isWrapping()` has to observe it\n\t#mapping: undefined | ValueMapping;\n\n\t#modulationValue: number | undefined;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#pointerDrag: PointerDrag<ValueDrag> | undefined;\n\n\t#positions: Array<number> | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\t#value = 0;\n\n\treadonly #watchers = new Set<() => void>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapping = undefined;\n\t\tthis.#positions = parseNumberList(this.getAttribute('positions'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#isHeld()) {\n\t\t\t\tthis.heldWrite(this.numberAttribute('value', this.min));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#endHolds();\n\n\t\tthis.#value = this.mapping().snap(this.#asked ?? this.numberAttribute('value', this.min));\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => undefined | ValueAxis,\n\t): void {\n\t\tconst entry = this.#bindEntry(control, signal);\n\n\t\tthis.#pointerDrag = bindDrag<ValueDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tif (drag.isKeptOnCancel !== true) this.input(drag.fromValue);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (isMenuPress(event) || this.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t\tthis.toggleState('springing', false);\n\t\t\t\t\t// A click listener above makes WebKit send a tap's compatibility mousedown, which blurs the entry a double tap just opened\n\t\t\t\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\t\tfocusByPointer(control);\n\t\t\t\t\tif (isResetPress(event)) {\n\t\t\t\t\t\tthis.#reset();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromValue = this.#value;\n\t\t\t\t\tconst axis = grab(event);\n\t\t\t\t\tif (!axis) return;\n\n\t\t\t\t\tconst drag: ValueDrag = {\n\t\t\t\t\t\t...axis,\n\t\t\t\t\t\tfromValue,\n\t\t\t\t\t\tisOutside: false,\n\t\t\t\t\t\tisRevealed: false,\n\t\t\t\t\t\tpointerType: event.pointerType,\n\t\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\t\tstate: this.#startDrag(control, axis, event),\n\t\t\t\t\t};\n\n\t\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t\t}, revealMs);\n\n\t\t\t\t\treturn drag;\n\t\t\t\t},\n\t\t\t\tlift: (drag, event) => {\n\t\t\t\t\tconst tap = drag.state.isEngaged ? undefined : this.tapTarget();\n\n\t\t\t\t\tif (tap !== undefined) {\n\t\t\t\t\t\tentry.forgetPress();\n\t\t\t\t\t\tthis.#commit(tap);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!entry.press(event) || this.springTarget() !== undefined) return;\n\n\t\t\t\t\tif (this.doublePress === 'reset') this.#reset();\n\t\t\t\t\telse entry.open();\n\t\t\t\t},\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#dragTo(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#releaseDrag(drag);\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\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t() => {\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(control, entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected heldFrom(): number | undefined {\n\t\treturn this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;\n\t}\n\n\tprotected heldWrite(next: number): void {\n\t\tthis.#heldAt = next;\n\t}\n\n\tprotected holdChanged(): void {\n\t\t// Only a subclass draws a hold\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#value;\n\n\t\tthis.#write(this.#limit ? clamp(next, ...this.#limit) : next);\n\t\tif (this.#value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected isEditing(): boolean {\n\t\treturn this.#entry?.isOpen === true;\n\t}\n\n\tprotected isRevealed(): boolean {\n\t\treturn this.#claim.isRevealed;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\tif (this.#mapping) return this.#mapping;\n\n\t\tconst { detent, midpoint } = this;\n\n\t\tthis.#mapping = valueMapping({\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(this.#positions ? { positions: this.#positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t});\n\n\t\treturn this.#mapping;\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected playback(): number {\n\t\tconst from = this.heldFrom();\n\n\t\treturn from === undefined ? this.#value : (this.#heldAt ?? from);\n\t}\n\n\tprotected previewValue(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readModulationValue(): number | undefined {\n\t\treturn this.#modulationValue;\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tfor (const listener of this.#watchers) listener();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\tprotected writeModulationValue(\n\t\tcontrol: HTMLElement,\n\t\tprefix: 'dial' | 'slider',\n\t\tnext: null | number | undefined,\n\t): void {\n\t\tconst value = next === undefined || next === null ? undefined : toNumber(next);\n\t\tif (value !== undefined && !Number.isFinite(value)) return;\n\n\t\tthis.#modulationValue = value;\n\t\tif (this.isBound()) this.#writeModulationProportion(control, prefix);\n\t}\n\n\tprotected writeProportions(\n\t\tcontrol: HTMLElement,\n\t\tprefix: 'dial' | 'slider',\n\t\tmodulation: number,\n\t): void {\n\t\tconst { style } = control;\n\t\tconst mapping = this.mapping();\n\t\tconst value = mapping.proportionOf(this.#value);\n\t\tconst reach = mapping.proportionOf(this.#value + modulation);\n\t\tconst { positionCount } = mapping;\n\n\t\tstyle.setProperty(`--_sonic-${prefix}-value`, String(value));\n\t\tstyle.setProperty(\n\t\t\t`--_sonic-${prefix}-origin`,\n\t\t\tString(mapping.proportionOf(this.originValue())),\n\t\t);\n\t\tstyle.setProperty(`--_sonic-${prefix}-modulation-from`, String(Math.min(value, reach)));\n\t\tstyle.setProperty(`--_sonic-${prefix}-modulation-to`, String(Math.max(value, reach)));\n\t\tif (positionCount === undefined) style.removeProperty(`--_sonic-${prefix}-position-count`);\n\t\telse style.setProperty(`--_sonic-${prefix}-position-count`, String(positionCount));\n\t\tthis.#writeModulationProportion(control, prefix);\n\t}\n\n\t#bindEntry(control: HTMLElement, signal: AbortSignal): ValueEntry {\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\t// Chrome's issues panel flags a form field with no id or name; a `name` would submit it\n\t\tinput.id = `sonic-entry-${this.#instance}`;\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\n\t\tconst entry = new ValueEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tthis.#commit((this.#parseValue ?? Number.parseFloat)(text));\n\t\t\t},\n\t\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\t\ttext: () => (this.#parseValue ? this.valueText : String(this.#value)),\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) this.forwardNaming(input, true);\n\t\t\t\tthis.toggleState('editing', isOpen);\n\t\t\t\tthis.render();\n\t\t\t},\n\t\t});\n\n\t\tthis.#entry = entry;\n\t\tentry.bind(control, signal);\n\n\t\treturn entry;\n\t}\n\n\t#bindKeys(control: HTMLElement, entry: ValueEntry, signal: AbortSignal): void {\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\tthis.toggleState('springing', false);\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tif (this.springTarget() !== undefined) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tentry.open();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst next = this.#keyTarget(event.key);\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#keyTo(event, next);\n\t\t\t\tthis.#claim.reveal('keys');\n\t\t\t\tthis.#renderHold();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'keyup',\n\t\t\t(event) => {\n\t\t\t\t// macOS sends no `keyup` for a key let go while Cmd is down\n\t\t\t\tconst isMeta = event.key === 'Meta';\n\n\t\t\t\tif (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();\n\t\t\t\tif (isMeta && this.#dragging()) return;\n\t\t\t\tif (isMeta || this.mapping().keyTarget(event.key, this.#value) !== undefined) {\n\t\t\t\t\tthis.#springBack();\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t\tthis.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#commit(next: number, direction: -1 | 0 | 1 = 0): void {\n\t\tconst landed = Number.isFinite(next) ? (this.#land?.(next, direction) ?? next) : next;\n\n\t\tif (this.input(landed)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#concealKeyReveal(): void {\n\t\tif (this.#claim.conceal('keys')) this.#renderHold();\n\t}\n\n\t#dragging(): undefined | ValueDrag {\n\t\treturn this.#pointerDrag?.current();\n\t}\n\n\t#dragTo(drag: ValueDrag, event: PointerEvent): void {\n\t\tconst isOutside = drag.outside?.(event) === true;\n\t\tconst wasEngaged = drag.state.isEngaged;\n\t\tconst mapping = this.mapping();\n\t\tconst { state, value } = stepDrag(mapping, drag.state, {\n\t\t\tisFine: event.shiftKey,\n\t\t\tisOutside,\n\t\t\tposition: drag.position(event),\n\t\t});\n\n\t\tdrag.state = this.#withinLimit(mapping, state);\n\t\tif (value === undefined) return;\n\n\t\tif (!wasEngaged) this.#reveal(drag);\n\n\t\tconst isCrossing = isOutside !== drag.isOutside;\n\n\t\tif (isCrossing) {\n\t\t\tdrag.isOutside = isOutside;\n\t\t\tthis.toggleState('cancelling', isOutside);\n\t\t}\n\t\tthis.input(isOutside ? drag.fromValue : value);\n\t\tif (isCrossing) this.#renderDragReveal(drag);\n\t}\n\n\t#endHolds(): void {\n\t\tthis.#entry?.close(false);\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#endKeyScrub();\n\t}\n\n\t#endKeyScrub(): void {\n\t\tconst scrub = this.#keyScrub;\n\t\tif (!scrub) return;\n\n\t\tthis.#keyScrub = undefined;\n\t\tthis.#renderHold();\n\t\tif (this.#value !== scrub.fromValue) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#dragging() !== undefined || this.#keyScrub !== undefined;\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tif (resetKeys.has(key)) return this.default;\n\n\t\treturn this.mapping().keyTarget(key, this.#value, this.keyStep);\n\t}\n\n\t#keyTo(event: KeyboardEvent, next: number): void {\n\t\tif (event.repeat && this.scrubsKeyRepeat()) {\n\t\t\tthis.#scrubKey(event.key, next);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#endKeyScrub();\n\t\tthis.#commit(next, next > this.#value ? 1 : -1);\n\t}\n\n\t#releaseDrag(drag: ValueDrag): void {\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#claim.conceal('drag');\n\t\tthis.toggleState('cancelling', false);\n\t\tthis.#renderHold();\n\t\tthis.#showRevealed(false);\n\t\tif (this.#value !== drag.fromValue) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t\tthis.#springBack();\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#value), String(this.#value));\n\t\tif (this.#entry?.isOpen) {\n\t\t\tfor (const name of roleAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tconst [low, high] = this.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#value),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderDragReveal(drag: ValueDrag): void {\n\t\tconst isRevealed = drag.isRevealed && !drag.isOutside;\n\n\t\tif (isRevealed) this.#claim.reveal('drag');\n\t\telse this.#claim.conceal('drag');\n\t\tthis.#renderHold();\n\t\tthis.#showRevealed(isRevealed);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.heldFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.renderReadout();\n\t\tthis.holdChanged();\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.default ?? NaN);\n\t}\n\n\t#reveal(drag: ValueDrag): void {\n\t\tif (drag.isRevealed || this.#dragging() !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderDragReveal(drag);\n\t}\n\n\t#scrubKey(key: string, next: number): void {\n\t\tif (this.#keyScrub?.key !== key) {\n\t\t\tthis.#endKeyScrub();\n\t\t\tthis.#keyScrub = { fromValue: this.#value, key };\n\t\t}\n\t\tthis.input(next);\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.#claim.isRevealed;\n\t\tconst preview = isRevealed ? undefined : this.previewValue();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#value,\n\t\t};\n\t}\n\n\t#showRevealed(isRevealed: boolean): void {\n\t\tif (isRevealed === this.#isRevealed) return;\n\n\t\tthis.#isRevealed = isRevealed;\n\t\tthis.toggleState('revealed', isRevealed);\n\t\tthis.dispatchEvent(new Event('sonic-reveal', { bubbles: true }));\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#value / step) * step)\n\t\t\t: this.#value;\n\t}\n\n\t#springBack(): void {\n\t\tconst target = this.springTarget();\n\t\tif (target === undefined) return;\n\n\t\t// Set before the commit so a `change` listener reading styles sees the glide\n\t\tif (this.mapping().snap(target) !== this.#value) this.toggleState('springing', true);\n\t\tthis.#commit(target);\n\t}\n\n\t#startDrag(control: HTMLElement, axis: ValueAxis, event: PointerEvent): DragState {\n\t\tconst mapping = this.mapping();\n\t\tconst detent = this.detent;\n\t\tconst start = {\n\t\t\tfrom: this.#value,\n\t\t\tposition: axis.position(event),\n\t\t\tproportion: axis.fromProportion ?? mapping.proportionOf(this.#value),\n\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\t\tif (detent === undefined) return startDrag(mapping, start);\n\n\t\tconst zonePx = readPxProperty(getComputedStyle(control), '--_sonic-detent-zone', detentZonePx);\n\n\t\treturn startDrag(mapping, {\n\t\t\t...start,\n\t\t\tdetent: { value: detent, zone: zonePx / axis.travelPx },\n\t\t});\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n\n\t#withinLimit(mapping: ValueMapping, state: DragState): DragState {\n\t\tconst limit = this.#limit;\n\t\tif (!limit || mapping.isWrapping) return state;\n\n\t\tconst [low, high] = limit;\n\n\t\treturn {\n\t\t\t...state,\n\t\t\trawProportion: clamp(\n\t\t\t\tstate.rawProportion,\n\t\t\t\tmapping.proportionOf(low),\n\t\t\t\tmapping.proportionOf(high),\n\t\t\t),\n\t\t};\n\t}\n\n\t#write(next: number): void {\n\t\tif (!Number.isFinite(next)) return;\n\n\t\tconst clamped = this.mapping().snap(next);\n\n\t\tthis.#asked = clamped;\n\t\tif (clamped === this.#value) return;\n\n\t\tthis.#value = clamped;\n\t\tthis.render();\n\t}\n\n\t#writeModulationProportion(control: HTMLElement, prefix: 'dial' | 'slider'): void {\n\t\tconst modulationValue = this.isWrapping() ? undefined : this.#modulationValue;\n\t\tconst isModulated = modulationValue !== undefined;\n\n\t\tif (isModulated !== this.#isModulated) {\n\t\t\tthis.#isModulated = isModulated;\n\t\t\tthis.toggleState('modulated', isModulated);\n\t\t}\n\t\tif (modulationValue === undefined) {\n\t\t\tcontrol.style.removeProperty(`--_sonic-${prefix}-modulation-value`);\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.style.setProperty(\n\t\t\t`--_sonic-${prefix}-modulation-value`,\n\t\t\tString(this.mapping().proportionOf(modulationValue)),\n\t\t);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { AskedValue } from '#lib/asked-value.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { moveTo, resnap, setAsked } from '#lib/asked-value.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { clamp, toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\nconst roleAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\ntype ValueLanding = (target: number, direction: -1 | 0 | 1) => number;\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tland: (resolve: undefined | ValueLanding) => void;\n\tlimit: (bounds: [number, number] | undefined) => void;\n\tmapping: () => ValueMapping;\n\tvalue: () => number;\n\twatch: (listener: () => void) => () => void;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'detent',\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tland: (resolve) => {\n\t\t\t\telement.#land = resolve;\n\t\t\t},\n\t\t\tlimit: (bounds) => {\n\t\t\t\telement.#limit = bounds;\n\t\t\t},\n\t\t\tmapping: () => element.mapping(),\n\t\t\tvalue: () => element.#cell.value,\n\t\t\twatch: (listener) => {\n\t\t\t\telement.#watchers.add(listener);\n\n\t\t\t\treturn () => {\n\t\t\t\t\telement.#watchers.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t});\n\t}\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget detent(): number | undefined {\n\t\treturn this.optionalNumberAttribute('detent');\n\t}\n\n\tset detent(value: number | undefined) {\n\t\tthis.reflect('detent', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\tget doublePress(): 'entry' | 'none' | 'reset' {\n\t\tconst gesture = this.getAttribute('double-press');\n\n\t\treturn gesture === 'none' || gesture === 'reset' ? gesture : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'none' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatEntry(): ((value: number) => string) | undefined {\n\t\treturn this.#formatEntry;\n\t}\n\n\tset formatEntry(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatEntry = format;\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget hoverValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget pointerType(): string | undefined {\n\t\treturn this.#gestures?.pointerType();\n\t}\n\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#cell.value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.heldWrite(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (setAsked(this.#cell, this.mapping(), value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#cell.value);\n\t}\n\n\treadonly #cell: AskedValue = { asked: undefined, value: 0 };\n\n\t#formatEntry: ((value: number) => string) | undefined;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#gestures: undefined | ValueGestures;\n\n\t#heldAt: number | undefined;\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t#land: undefined | ValueLanding;\n\n\t#limit: [number, number] | undefined;\n\n\t// Every attribute it reads is observed; a subclass adding an input to `isWrapping()` has to observe it\n\t#mapping: undefined | ValueMapping;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#positions: Array<number> | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\treadonly #watchers = new Set<() => void>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapping = undefined;\n\t\tthis.#positions = parseNumberList(this.getAttribute('positions'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#isHeld()) {\n\t\t\t\tthis.heldWrite(this.numberAttribute('value', this.min));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#cell.asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tresnap(this.#cell, this.mapping(), this.numberAttribute('value', this.min));\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatEntry',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => undefined | ValueAxis,\n\t): void {\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\t\tthis.#gestures = new ValueGestures(\n\t\t\t{\n\t\t\t\tdefault: () => this.default,\n\t\t\t\tdetent: () => this.detent,\n\t\t\t\tdispatch: (type) => {\n\t\t\t\t\tthis.dispatchEvent(new Event(type, { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdoublePress: () => this.doublePress,\n\t\t\t\tentryId: `sonic-entry-${this.#instance}`,\n\t\t\t\tentryText: () => this.#entryText(),\n\t\t\t\tforwardNaming: (target, isNamed) => {\n\t\t\t\t\tthis.forwardNaming(target, isNamed);\n\t\t\t\t},\n\t\t\t\tholdChanged: () => {\n\t\t\t\t\tthis.#renderHold();\n\t\t\t\t},\n\t\t\t\tinput: (next) => this.input(next),\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tkeyStep: () => this.keyStep,\n\t\t\t\tland: (target, direction) => this.#land?.(target, direction) ?? target,\n\t\t\t\tlimit: () => this.#limit,\n\t\t\t\tmapping: () => this.mapping(),\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tparse: (text) => (this.#parseValue ?? Number.parseFloat)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubsKeyRepeat: () => this.scrubsKeyRepeat(),\n\t\t\t\tspringTarget: () => this.springTarget(),\n\t\t\t\ttapTarget: () => this.tapTarget(),\n\t\t\t\ttoggleState: (state, isOn) => {\n\t\t\t\t\tthis.toggleState(state, isOn);\n\t\t\t\t},\n\t\t\t\tvalue: () => this.#cell.value,\n\t\t\t},\n\t\t\tcontrol,\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, place, valueAt }: HoverBinding,\n\t): void {\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcanShow,\n\t\t\t\tchanged: () => {\n\t\t\t\t\tthis.dispatchEvent(new Event('sonic-hover', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdismiss: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tisTaken: () => this.#isEditing() || this.isRevealed(),\n\t\t\t\tshow: () => {\n\t\t\t\t\tif (!this.readout) return;\n\n\t\t\t\t\tthis.renderReadout();\n\t\t\t\t\tplace();\n\t\t\t\t},\n\t\t\t\tvalueAt,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected heldFrom(): number | undefined {\n\t\treturn this.#gestures?.heldFrom();\n\t}\n\n\tprotected heldWrite(next: number): void {\n\t\tthis.#heldAt = next;\n\t}\n\n\tprotected holdChanged(): void {\n\t\t// Only a subclass draws a hold\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#cell.value;\n\n\t\tconst target = this.#limit ? clamp(next, ...this.#limit) : next;\n\n\t\tif (moveTo(this.#cell, this.mapping(), target)) this.render();\n\t\tif (this.#cell.value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\tif (this.#mapping) return this.#mapping;\n\n\t\tconst { detent, midpoint } = this;\n\n\t\tthis.#mapping = valueMapping({\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(this.#positions ? { positions: this.#positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t});\n\n\t\treturn this.#mapping;\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected playback(): number {\n\t\tconst from = this.heldFrom();\n\n\t\treturn from === undefined ? this.#cell.value : (this.#heldAt ?? from);\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#cell.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tfor (const listener of this.#watchers) listener();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#cell.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\treturn this.#parseValue ? this.valueText : String(value);\n\t}\n\n\t#isEditing(): boolean {\n\t\treturn this.#gestures?.isEditing() === true;\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#gestures?.isHeld() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#cell.value), String(this.#cell.value));\n\t\tif (this.#isEditing()) {\n\t\t\tfor (const name of roleAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tconst [low, high] = this.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#cell.value),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\t// A reveal or typed entry takes the readout from a hover\n\t\tif (this.#isEditing() || this.isRevealed()) this.#hover?.clear();\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.heldFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.renderReadout();\n\t\tthis.holdChanged();\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.isRevealed();\n\t\tconst preview = isRevealed ? undefined : this.#hover?.value();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#cell.value,\n\t\t};\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#cell.value / step) * step)\n\t\t\t: this.#cell.value;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ //#region src/elements/value-gestures.d.ts
2
+ export interface ValueAxis {
3
+ fromProportion?: number;
4
+ isKeptOnCancel?: boolean;
5
+ outside?: (event: PointerEvent) => boolean;
6
+ position: (event: PointerEvent) => number;
7
+ travelPx: number;
8
+ }
9
+ //#endregion
10
+ //# sourceMappingURL=value-gestures.d.ts.map
@@ -0,0 +1,302 @@
1
+ import { clamp } from "../lib/math.js";
2
+ import { requireChild } from "../lib/render.js";
3
+ import { bindDrag } from "../lib/pointer-drag.js";
4
+ import { ReadoutClaim, revealDelay } from "./readout-claim.js";
5
+ import { ValueEntry } from "./value-entry.js";
6
+ import { dragThresholdPx, startDrag, stepDrag } from "../lib/drag-step.js";
7
+ import { focusByPointer } from "../lib/focus-by-pointer.js";
8
+ import { isMenuPress, isResetPress } from "../lib/modifier-press.js";
9
+ import { readPxProperty } from "../lib/read-px-property.js";
10
+ import { resetKeys } from "../lib/value-mapping.js";
11
+
12
+ //#region src/elements/value-gestures.ts
13
+ const detentZonePx = 8;
14
+ var ValueGestures = class {
15
+ #claim = new ReadoutClaim(() => {
16
+ this.#host.holdChanged();
17
+ });
18
+ #control;
19
+ #entry;
20
+ #host;
21
+ #isRevealed = false;
22
+ #keyScrub;
23
+ #pointerDrag;
24
+ constructor(host, control) {
25
+ this.#host = host;
26
+ this.#control = control;
27
+ }
28
+ bind(signal, grab) {
29
+ const control = this.#control;
30
+ const host = this.#host;
31
+ const entry = this.#bindEntry(signal);
32
+ this.#pointerDrag = bindDrag(control, {
33
+ cancel: (drag) => {
34
+ if (drag.isKeptOnCancel !== true) host.input(drag.fromValue);
35
+ },
36
+ grab: (event) => {
37
+ if (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;
38
+ host.toggleState("springing", false);
39
+ if (event.pointerType === "touch") event.preventDefault();
40
+ focusByPointer(control);
41
+ if (isResetPress(event)) {
42
+ this.#reset();
43
+ return;
44
+ }
45
+ const fromValue = host.value();
46
+ const axis = grab(event);
47
+ if (!axis) return;
48
+ const drag = {
49
+ ...axis,
50
+ fromValue,
51
+ isOutside: false,
52
+ isRevealed: false,
53
+ pointerType: event.pointerType,
54
+ revealTimer: void 0,
55
+ state: this.#startDrag(axis, event)
56
+ };
57
+ drag.revealTimer = setTimeout(() => {
58
+ this.#reveal(drag);
59
+ }, revealDelay(control));
60
+ return drag;
61
+ },
62
+ lift: (drag, event) => {
63
+ const tap = drag.state.isEngaged ? void 0 : host.tapTarget();
64
+ if (tap !== void 0) {
65
+ entry.forgetPress();
66
+ this.#commit(tap);
67
+ return;
68
+ }
69
+ if (!entry.press(event) || host.springTarget() !== void 0) return;
70
+ const gesture = host.doublePress();
71
+ if (gesture === "reset") this.#reset();
72
+ else if (gesture === "entry") entry.open();
73
+ },
74
+ move: (drag, event) => {
75
+ this.#dragTo(drag, event);
76
+ },
77
+ release: (drag) => {
78
+ this.#releaseDrag(drag);
79
+ },
80
+ toggle: (isDragging) => {
81
+ host.toggleState("dragging", isDragging);
82
+ }
83
+ }, signal);
84
+ control.addEventListener("pointercancel", () => {
85
+ entry.forgetPress();
86
+ }, { signal });
87
+ this.#bindKeys(entry, signal);
88
+ signal.addEventListener("abort", () => {
89
+ this.#endKeyScrub();
90
+ this.#concealKeyReveal();
91
+ }, { once: true });
92
+ }
93
+ end() {
94
+ this.#entry?.close(false);
95
+ this.#pointerDrag?.end();
96
+ this.#endKeyScrub();
97
+ }
98
+ heldFrom() {
99
+ return this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;
100
+ }
101
+ isEditing() {
102
+ return this.#entry?.isOpen === true;
103
+ }
104
+ isHeld() {
105
+ return this.#dragging() !== void 0 || this.#keyScrub !== void 0;
106
+ }
107
+ isRevealed() {
108
+ return this.#claim.isRevealed;
109
+ }
110
+ pointerType() {
111
+ return this.#dragging()?.pointerType;
112
+ }
113
+ #bindEntry(signal) {
114
+ const control = this.#control;
115
+ const host = this.#host;
116
+ const input = requireChild(control, "input", HTMLInputElement);
117
+ input.id = host.entryId;
118
+ const entry = new ValueEntry(input, {
119
+ commit: (text) => {
120
+ this.#commit(host.parse(text));
121
+ },
122
+ text: host.entryText,
123
+ toggle: (isOpen) => {
124
+ if (isOpen) host.forwardNaming(input, true);
125
+ host.toggleState("editing", isOpen);
126
+ host.render();
127
+ }
128
+ });
129
+ this.#entry = entry;
130
+ entry.bind(control, signal);
131
+ return entry;
132
+ }
133
+ #bindKeys(entry, signal) {
134
+ const control = this.#control;
135
+ const host = this.#host;
136
+ control.addEventListener("keydown", (event) => {
137
+ if (host.isDisabled() || event.defaultPrevented || event.target !== control) return;
138
+ host.toggleState("springing", false);
139
+ if (event.key === "Enter") {
140
+ if (host.springTarget() !== void 0) return;
141
+ event.preventDefault();
142
+ entry.open();
143
+ return;
144
+ }
145
+ const next = this.#keyTarget(event.key);
146
+ if (next === void 0) return;
147
+ event.preventDefault();
148
+ this.#keyTo(event, next);
149
+ this.#claim.reveal("keys");
150
+ host.holdChanged();
151
+ }, { signal });
152
+ control.addEventListener("keyup", (event) => {
153
+ const isMeta = event.key === "Meta";
154
+ if (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();
155
+ if (isMeta && this.#dragging()) return;
156
+ if (isMeta || host.mapping().keyTarget(event.key, host.value()) !== void 0) this.#springBack();
157
+ }, { signal });
158
+ control.addEventListener("blur", () => {
159
+ this.#endKeyScrub();
160
+ this.#concealKeyReveal();
161
+ this.#springBack();
162
+ }, { signal });
163
+ }
164
+ #commit(next, direction = 0) {
165
+ const host = this.#host;
166
+ const landed = Number.isFinite(next) ? host.land(next, direction) : next;
167
+ if (host.input(landed)) host.dispatch("change");
168
+ }
169
+ #concealKeyReveal() {
170
+ if (this.#claim.conceal("keys")) this.#host.holdChanged();
171
+ }
172
+ #dragging() {
173
+ return this.#pointerDrag?.current();
174
+ }
175
+ #dragTo(drag, event) {
176
+ const host = this.#host;
177
+ const isOutside = drag.outside?.(event) === true;
178
+ const wasEngaged = drag.state.isEngaged;
179
+ const mapping = host.mapping();
180
+ const { state, value } = stepDrag(mapping, drag.state, {
181
+ isFine: event.shiftKey,
182
+ isOutside,
183
+ position: drag.position(event)
184
+ });
185
+ drag.state = this.#withinLimit(mapping, state);
186
+ if (value === void 0) return;
187
+ if (!wasEngaged) this.#reveal(drag);
188
+ const isCrossing = isOutside !== drag.isOutside;
189
+ if (isCrossing) {
190
+ drag.isOutside = isOutside;
191
+ host.toggleState("cancelling", isOutside);
192
+ }
193
+ host.input(isOutside ? drag.fromValue : value);
194
+ if (isCrossing) this.#renderDragReveal(drag);
195
+ }
196
+ #endKeyScrub() {
197
+ const host = this.#host;
198
+ const scrub = this.#keyScrub;
199
+ if (!scrub) return;
200
+ this.#keyScrub = void 0;
201
+ host.holdChanged();
202
+ if (host.value() !== scrub.fromValue) host.dispatch("change");
203
+ }
204
+ #keyTarget(key) {
205
+ const host = this.#host;
206
+ if (resetKeys.has(key)) return host.default();
207
+ return host.mapping().keyTarget(key, host.value(), host.keyStep());
208
+ }
209
+ #keyTo(event, next) {
210
+ const host = this.#host;
211
+ if (event.repeat && host.scrubsKeyRepeat()) {
212
+ this.#scrubKey(event.key, next);
213
+ return;
214
+ }
215
+ this.#endKeyScrub();
216
+ this.#commit(next, next > host.value() ? 1 : -1);
217
+ }
218
+ #releaseDrag(drag) {
219
+ const host = this.#host;
220
+ clearTimeout(drag.revealTimer);
221
+ this.#claim.conceal("drag");
222
+ host.toggleState("cancelling", false);
223
+ host.holdChanged();
224
+ this.#showRevealed(false);
225
+ if (host.value() !== drag.fromValue) host.dispatch("change");
226
+ this.#springBack();
227
+ }
228
+ #renderDragReveal(drag) {
229
+ const isRevealed = drag.isRevealed && !drag.isOutside;
230
+ if (isRevealed) this.#claim.reveal("drag");
231
+ else this.#claim.conceal("drag");
232
+ this.#host.holdChanged();
233
+ this.#showRevealed(isRevealed);
234
+ }
235
+ #reset() {
236
+ this.#commit(this.#host.default() ?? NaN);
237
+ }
238
+ #reveal(drag) {
239
+ if (drag.isRevealed || this.#dragging() !== drag) return;
240
+ drag.isRevealed = true;
241
+ this.#renderDragReveal(drag);
242
+ }
243
+ #scrubKey(key, next) {
244
+ const host = this.#host;
245
+ if (this.#keyScrub?.key !== key) {
246
+ this.#endKeyScrub();
247
+ this.#keyScrub = {
248
+ fromValue: host.value(),
249
+ key
250
+ };
251
+ }
252
+ host.input(next);
253
+ }
254
+ #showRevealed(isRevealed) {
255
+ if (isRevealed === this.#isRevealed) return;
256
+ this.#isRevealed = isRevealed;
257
+ this.#host.toggleState("revealed", isRevealed);
258
+ this.#host.dispatch("sonic-reveal");
259
+ }
260
+ #springBack() {
261
+ const host = this.#host;
262
+ const target = host.springTarget();
263
+ if (target === void 0) return;
264
+ if (host.mapping().snap(target) !== host.value()) host.toggleState("springing", true);
265
+ this.#commit(target);
266
+ }
267
+ #startDrag(axis, event) {
268
+ const host = this.#host;
269
+ const mapping = host.mapping();
270
+ const detent = host.detent();
271
+ const value = host.value();
272
+ const start = {
273
+ from: value,
274
+ position: axis.position(event),
275
+ proportion: axis.fromProportion ?? mapping.proportionOf(value),
276
+ thresholdPx: dragThresholdPx(event.pointerType),
277
+ travelPx: axis.travelPx
278
+ };
279
+ if (detent === void 0) return startDrag(mapping, start);
280
+ const zonePx = readPxProperty(getComputedStyle(this.#control), "--_sonic-detent-zone", detentZonePx);
281
+ return startDrag(mapping, {
282
+ ...start,
283
+ detent: {
284
+ value: detent,
285
+ zone: zonePx / axis.travelPx
286
+ }
287
+ });
288
+ }
289
+ #withinLimit(mapping, state) {
290
+ const limit = this.#host.limit();
291
+ if (!limit || mapping.isWrapping) return state;
292
+ const [low, high] = limit;
293
+ return {
294
+ ...state,
295
+ rawProportion: clamp(state.rawProportion, mapping.proportionOf(low), mapping.proportionOf(high))
296
+ };
297
+ }
298
+ };
299
+
300
+ //#endregion
301
+ export { ValueGestures };
302
+ //# sourceMappingURL=value-gestures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"value-gestures.js","names":[],"sources":["../../../src/elements/value-gestures.ts"],"sourcesContent":["import type { DragState } from '#lib/drag-step.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { ReadoutClaim, revealDelay } from '#elements/readout-claim.ts';\nimport { ValueEntry } from '#elements/value-entry.ts';\nimport { dragThresholdPx, startDrag, stepDrag } from '#lib/drag-step.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clamp } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { resetKeys } from '#lib/value-mapping.ts';\n\nexport interface ValueAxis {\n\tfromProportion?: number;\n\tisKeptOnCancel?: boolean;\n\toutside?: (event: PointerEvent) => boolean;\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface GestureHost {\n\tdefault: () => number | undefined;\n\tdetent: () => number | undefined;\n\tdispatch: (type: 'change' | 'sonic-reveal') => void;\n\tdoublePress: () => 'entry' | 'none' | 'reset';\n\tentryId: string;\n\tentryText: () => string;\n\tforwardNaming: (target: Element, isNamed: boolean) => void;\n\tholdChanged: () => void;\n\tinput: (next: number) => boolean;\n\tisDisabled: () => boolean;\n\tkeyStep: () => number | undefined;\n\tland: (target: number, direction: -1 | 0 | 1) => number;\n\tlimit: () => [number, number] | undefined;\n\tmapping: () => ValueMapping;\n\tparse: (text: string) => number;\n\trender: () => void;\n\tscrubsKeyRepeat: () => boolean;\n\tspringTarget: () => number | undefined;\n\ttapTarget: () => number | undefined;\n\ttoggleState: (state: string, isOn: boolean) => void;\n\tvalue: () => number;\n}\n\ninterface KeyScrub {\n\tfromValue: number;\n\tkey: string;\n}\n\ninterface ValueDrag extends ValueAxis {\n\tfromValue: number;\n\tisOutside: boolean;\n\tisRevealed: boolean;\n\tpointerType: string;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstate: DragState;\n}\n\nconst detentZonePx = 8;\n\nexport class ValueGestures {\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#host.holdChanged();\n\t});\n\n\treadonly #control: HTMLElement;\n\n\t#entry: undefined | ValueEntry;\n\n\treadonly #host: GestureHost;\n\n\t#isRevealed = false;\n\n\t#keyScrub: KeyScrub | undefined;\n\n\t#pointerDrag: PointerDrag<ValueDrag> | undefined;\n\n\tconstructor(host: GestureHost, control: HTMLElement) {\n\t\tthis.#host = host;\n\t\tthis.#control = control;\n\t}\n\n\tbind(signal: AbortSignal, grab: (event: PointerEvent) => undefined | ValueAxis): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst entry = this.#bindEntry(signal);\n\n\t\tthis.#pointerDrag = bindDrag<ValueDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tif (drag.isKeptOnCancel !== true) host.input(drag.fromValue);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t\thost.toggleState('springing', false);\n\t\t\t\t\t// A click listener above makes WebKit send a tap's compatibility mousedown, which blurs the entry a double tap just opened\n\t\t\t\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\t\tfocusByPointer(control);\n\t\t\t\t\tif (isResetPress(event)) {\n\t\t\t\t\t\tthis.#reset();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromValue = host.value();\n\t\t\t\t\tconst axis = grab(event);\n\t\t\t\t\tif (!axis) return;\n\n\t\t\t\t\tconst drag: ValueDrag = {\n\t\t\t\t\t\t...axis,\n\t\t\t\t\t\tfromValue,\n\t\t\t\t\t\tisOutside: false,\n\t\t\t\t\t\tisRevealed: false,\n\t\t\t\t\t\tpointerType: event.pointerType,\n\t\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\t\tstate: this.#startDrag(axis, event),\n\t\t\t\t\t};\n\n\t\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t\t}, revealDelay(control));\n\n\t\t\t\t\treturn drag;\n\t\t\t\t},\n\t\t\t\tlift: (drag, event) => {\n\t\t\t\t\tconst tap = drag.state.isEngaged ? undefined : host.tapTarget();\n\n\t\t\t\t\tif (tap !== undefined) {\n\t\t\t\t\t\tentry.forgetPress();\n\t\t\t\t\t\tthis.#commit(tap);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!entry.press(event) || host.springTarget() !== undefined) return;\n\n\t\t\t\t\tconst gesture = host.doublePress();\n\n\t\t\t\t\tif (gesture === 'reset') this.#reset();\n\t\t\t\t\telse if (gesture === 'entry') entry.open();\n\t\t\t\t},\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#dragTo(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#releaseDrag(drag);\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\thost.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t() => {\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tend(): void {\n\t\tthis.#entry?.close(false);\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#endKeyScrub();\n\t}\n\n\theldFrom(): number | undefined {\n\t\treturn this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;\n\t}\n\n\tisEditing(): boolean {\n\t\treturn this.#entry?.isOpen === true;\n\t}\n\n\tisHeld(): boolean {\n\t\treturn this.#dragging() !== undefined || this.#keyScrub !== undefined;\n\t}\n\n\tisRevealed(): boolean {\n\t\treturn this.#claim.isRevealed;\n\t}\n\n\tpointerType(): string | undefined {\n\t\treturn this.#dragging()?.pointerType;\n\t}\n\n\t#bindEntry(signal: AbortSignal): ValueEntry {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\n\n\t\t// Chrome's issues panel flags a form field with no id or name; a `name` would submit it\n\t\tinput.id = host.entryId;\n\n\t\tconst entry = new ValueEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\tthis.#commit(host.parse(text));\n\t\t\t},\n\t\t\ttext: host.entryText,\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) host.forwardNaming(input, true);\n\t\t\t\thost.toggleState('editing', isOpen);\n\t\t\t\thost.render();\n\t\t\t},\n\t\t});\n\n\t\tthis.#entry = entry;\n\t\tentry.bind(control, signal);\n\n\t\treturn entry;\n\t}\n\n\t#bindKeys(entry: ValueEntry, signal: AbortSignal): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (host.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\thost.toggleState('springing', false);\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tif (host.springTarget() !== undefined) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tentry.open();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst next = this.#keyTarget(event.key);\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#keyTo(event, next);\n\t\t\t\tthis.#claim.reveal('keys');\n\t\t\t\thost.holdChanged();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'keyup',\n\t\t\t(event) => {\n\t\t\t\t// macOS sends no `keyup` for a key let go while Cmd is down\n\t\t\t\tconst isMeta = event.key === 'Meta';\n\n\t\t\t\tif (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();\n\t\t\t\tif (isMeta && this.#dragging()) return;\n\t\t\t\tif (isMeta || host.mapping().keyTarget(event.key, host.value()) !== undefined) {\n\t\t\t\t\tthis.#springBack();\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t\tthis.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#commit(next: number, direction: -1 | 0 | 1 = 0): void {\n\t\tconst host = this.#host;\n\t\tconst landed = Number.isFinite(next) ? host.land(next, direction) : next;\n\n\t\tif (host.input(landed)) host.dispatch('change');\n\t}\n\n\t#concealKeyReveal(): void {\n\t\tif (this.#claim.conceal('keys')) this.#host.holdChanged();\n\t}\n\n\t#dragging(): undefined | ValueDrag {\n\t\treturn this.#pointerDrag?.current();\n\t}\n\n\t#dragTo(drag: ValueDrag, event: PointerEvent): void {\n\t\tconst host = this.#host;\n\t\tconst isOutside = drag.outside?.(event) === true;\n\t\tconst wasEngaged = drag.state.isEngaged;\n\t\tconst mapping = host.mapping();\n\t\tconst { state, value } = stepDrag(mapping, drag.state, {\n\t\t\tisFine: event.shiftKey,\n\t\t\tisOutside,\n\t\t\tposition: drag.position(event),\n\t\t});\n\n\t\tdrag.state = this.#withinLimit(mapping, state);\n\t\tif (value === undefined) return;\n\n\t\tif (!wasEngaged) this.#reveal(drag);\n\n\t\tconst isCrossing = isOutside !== drag.isOutside;\n\n\t\tif (isCrossing) {\n\t\t\tdrag.isOutside = isOutside;\n\t\t\thost.toggleState('cancelling', isOutside);\n\t\t}\n\t\thost.input(isOutside ? drag.fromValue : value);\n\t\tif (isCrossing) this.#renderDragReveal(drag);\n\t}\n\n\t#endKeyScrub(): void {\n\t\tconst host = this.#host;\n\t\tconst scrub = this.#keyScrub;\n\t\tif (!scrub) return;\n\n\t\tthis.#keyScrub = undefined;\n\t\thost.holdChanged();\n\t\tif (host.value() !== scrub.fromValue) host.dispatch('change');\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tconst host = this.#host;\n\n\t\tif (resetKeys.has(key)) return host.default();\n\n\t\treturn host.mapping().keyTarget(key, host.value(), host.keyStep());\n\t}\n\n\t#keyTo(event: KeyboardEvent, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (event.repeat && host.scrubsKeyRepeat()) {\n\t\t\tthis.#scrubKey(event.key, next);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#endKeyScrub();\n\t\tthis.#commit(next, next > host.value() ? 1 : -1);\n\t}\n\n\t#releaseDrag(drag: ValueDrag): void {\n\t\tconst host = this.#host;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#claim.conceal('drag');\n\t\thost.toggleState('cancelling', false);\n\t\thost.holdChanged();\n\t\tthis.#showRevealed(false);\n\t\tif (host.value() !== drag.fromValue) host.dispatch('change');\n\t\tthis.#springBack();\n\t}\n\n\t#renderDragReveal(drag: ValueDrag): void {\n\t\tconst isRevealed = drag.isRevealed && !drag.isOutside;\n\n\t\tif (isRevealed) this.#claim.reveal('drag');\n\t\telse this.#claim.conceal('drag');\n\t\tthis.#host.holdChanged();\n\t\tthis.#showRevealed(isRevealed);\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.#host.default() ?? NaN);\n\t}\n\n\t#reveal(drag: ValueDrag): void {\n\t\tif (drag.isRevealed || this.#dragging() !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderDragReveal(drag);\n\t}\n\n\t#scrubKey(key: string, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (this.#keyScrub?.key !== key) {\n\t\t\tthis.#endKeyScrub();\n\t\t\tthis.#keyScrub = { fromValue: host.value(), key };\n\t\t}\n\t\thost.input(next);\n\t}\n\n\t#showRevealed(isRevealed: boolean): void {\n\t\tif (isRevealed === this.#isRevealed) return;\n\n\t\tthis.#isRevealed = isRevealed;\n\t\tthis.#host.toggleState('revealed', isRevealed);\n\t\tthis.#host.dispatch('sonic-reveal');\n\t}\n\n\t#springBack(): void {\n\t\tconst host = this.#host;\n\t\tconst target = host.springTarget();\n\t\tif (target === undefined) return;\n\n\t\t// Set before the commit so a `change` listener reading styles sees the glide\n\t\tif (host.mapping().snap(target) !== host.value()) host.toggleState('springing', true);\n\t\tthis.#commit(target);\n\t}\n\n\t#startDrag(axis: ValueAxis, event: PointerEvent): DragState {\n\t\tconst host = this.#host;\n\t\tconst mapping = host.mapping();\n\t\tconst detent = host.detent();\n\t\tconst value = host.value();\n\t\tconst start = {\n\t\t\tfrom: value,\n\t\t\tposition: axis.position(event),\n\t\t\tproportion: axis.fromProportion ?? mapping.proportionOf(value),\n\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\t\tif (detent === undefined) return startDrag(mapping, start);\n\n\t\tconst zonePx = readPxProperty(\n\t\t\tgetComputedStyle(this.#control),\n\t\t\t'--_sonic-detent-zone',\n\t\t\tdetentZonePx,\n\t\t);\n\n\t\treturn startDrag(mapping, {\n\t\t\t...start,\n\t\t\tdetent: { value: detent, zone: zonePx / axis.travelPx },\n\t\t});\n\t}\n\n\t#withinLimit(mapping: ValueMapping, state: DragState): DragState {\n\t\tconst limit = this.#host.limit();\n\t\tif (!limit || mapping.isWrapping) return state;\n\n\t\tconst [low, high] = limit;\n\n\t\treturn {\n\t\t\t...state,\n\t\t\trawProportion: clamp(\n\t\t\t\tstate.rawProportion,\n\t\t\t\tmapping.proportionOf(low),\n\t\t\t\tmapping.proportionOf(high),\n\t\t\t),\n\t\t};\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { SonicValueElement } from "./value-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/wave-element.ts
5
6
  function markerKey(marker) {
@@ -31,12 +32,12 @@ var SonicWaveElement = class extends SonicValueElement {
31
32
  this.render();
32
33
  }
33
34
  #current;
34
- #fillWatch;
35
35
  #markers = [];
36
36
  #surface;
37
+ #syncFill;
37
38
  attributeChangedCallback(name) {
38
39
  super.attributeChangedCallback(name);
39
- if (name === "fill") this.#watchFill();
40
+ if (name === "fill") this.#syncFill?.(this.fill);
40
41
  }
41
42
  connectedCallback() {
42
43
  this.upgradeProperties("fill", "markers");
@@ -63,11 +64,8 @@ var SonicWaveElement = class extends SonicValueElement {
63
64
  });
64
65
  this.render();
65
66
  this.checkStyles(control, this.sheet);
66
- this.#watchFill();
67
- signal.addEventListener("abort", () => {
68
- this.#fillWatch?.disconnect();
69
- this.#fillWatch = void 0;
70
- }, { once: true });
67
+ this.#syncFill = bindFill(control, this.sizeProperty, signal);
68
+ this.#syncFill(this.fill);
71
69
  }
72
70
  focusTarget() {
73
71
  return this.control;
@@ -105,25 +103,6 @@ var SonicWaveElement = class extends SonicValueElement {
105
103
  surface() {
106
104
  return this.#surface;
107
105
  }
108
- #watchFill() {
109
- const control = this.control;
110
- const isFilling = this.fill && this.isBound();
111
- if (isFilling === (this.#fillWatch !== void 0)) return;
112
- this.#fillWatch?.disconnect();
113
- this.#fillWatch = void 0;
114
- if (!isFilling) {
115
- control.style.removeProperty(this.sizeProperty);
116
- return;
117
- }
118
- this.#fillWatch = new ResizeObserver((entries) => {
119
- const box = entries.at(-1)?.borderBoxSize[0];
120
- if (!box) return;
121
- requestAnimationFrame(() => {
122
- if (this.#fillWatch) control.style.setProperty(this.sizeProperty, `${String(box.blockSize)}px`);
123
- });
124
- });
125
- this.#fillWatch.observe(control);
126
- }
127
106
  };
128
107
 
129
108
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"wave-element.js","names":[],"sources":["../../../src/elements/wave-element.ts"],"sourcesContent":["import type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\n\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { bindSurface } from '#lib/canvas-surface.ts';\n\nexport interface WaveMarker {\n\tdimmed?: boolean;\n\tend?: number;\n\tkind?: string;\n\tlabel?: string;\n\tstart: number;\n}\n\nfunction markerKey(marker: undefined | WaveMarker): string {\n\treturn marker ? [marker.start, marker.end, marker.kind, marker.label].join('|') : '';\n}\n\nexport abstract class SonicWaveElement<\n\tColour extends string,\n\tLength extends string = never,\n> extends SonicValueElement {\n\tstatic override readonly observedAttributes = [...SonicValueElement.observedAttributes, 'fill'];\n\n\tget currentMarker(): undefined | WaveMarker {\n\t\treturn this.#current ? { ...this.#current } : undefined;\n\t}\n\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\tget markers(): Array<WaveMarker> {\n\t\treturn this.#markers.map((marker) => ({ ...marker }));\n\t}\n\n\tset markers(list: Iterable<WaveMarker> | undefined) {\n\t\tthis.#markers = [...(list ?? [])]\n\t\t\t.filter((marker) => Number.isFinite(marker.start))\n\t\t\t.map((marker) => ({ ...marker }))\n\t\t\t.toSorted((first, second) => first.start - second.start);\n\t\tif (!this.isBound()) return;\n\n\t\tthis.renderMarkers();\n\t\tthis.render();\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 lengths: Record<Length, `--_sonic-${string}`>;\n\n\tprotected abstract readonly sheet: string;\n\n\tprotected abstract readonly sizeProperty: `--_sonic-${string}`;\n\n\t#current: undefined | WaveMarker;\n\n\t#fillWatch: ResizeObserver | undefined;\n\n\t#markers: Array<WaveMarker> = [];\n\n\t#surface: Surface | undefined;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'fill') this.#watchFill();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('fill', 'markers');\n\t\tsuper.connectedCallback();\n\t}\n\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.renderEmpty();\n\t\tthis.#surface = bindSurface({\n\t\t\tcanvas: this.canvas,\n\t\t\tcolours: this.colours,\n\t\t\tlengths: this.lengths,\n\t\t\tpaint: (context, frame) => {\n\t\t\t\tthis.paint(context, frame);\n\t\t\t},\n\t\t\tresize: () => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\tsignal,\n\t\t});\n\t\tthis.render();\n\t\tthis.checkStyles(control, this.sheet);\n\t\tthis.#watchFill();\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#fillWatch?.disconnect();\n\t\t\t\tthis.#fillWatch = undefined;\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.control;\n\t}\n\n\tprotected override heldWrite(next: number): void {\n\t\tsuper.heldWrite(next);\n\t\tthis.render();\n\t}\n\n\tprotected override holdChanged(): void {\n\t\tthis.render();\n\t}\n\n\tprotected abstract isEmpty(): boolean;\n\n\tprotected markerList(): ReadonlyArray<WaveMarker> {\n\t\treturn this.#markers;\n\t}\n\n\tprotected markerProportions(): Array<[number, number]> {\n\t\tconst mapping = this.mapping();\n\n\t\treturn this.#markers.map(({ end, start }) => {\n\t\t\tconst at = mapping.proportionOf(start);\n\t\t\tconst to = end === undefined ? at : mapping.proportionOf(end);\n\n\t\t\treturn [Math.min(at, to), Math.max(at, to)];\n\t\t});\n\t}\n\n\tprotected abstract paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\tframe: SurfaceFrame<Colour, Length>,\n\t): void;\n\n\tprotected override proportionsChanged(): void {\n\t\tthis.renderMarkers();\n\t}\n\n\tprotected renderEmpty(): void {\n\t\tthis.toggleState('empty', this.isEmpty());\n\t}\n\n\tprotected abstract renderMarkers(): void;\n\n\tprotected showCurrentMarker(seconds: number): void {\n\t\tconst current = this.#markers.findLast((marker) => marker.start <= seconds);\n\t\tif (markerKey(current) === markerKey(this.#current)) return;\n\n\t\tthis.#current = current;\n\t\tthis.dispatchEvent(new Event('sonic-marker', { bubbles: true }));\n\t}\n\n\tprotected surface(): Surface | undefined {\n\t\treturn this.#surface;\n\t}\n\n\t#watchFill(): void {\n\t\tconst control = this.control;\n\t\tconst isFilling = this.fill && this.isBound();\n\t\tif (isFilling === (this.#fillWatch !== undefined)) return;\n\n\t\tthis.#fillWatch?.disconnect();\n\t\tthis.#fillWatch = undefined;\n\t\tif (!isFilling) {\n\t\t\tcontrol.style.removeProperty(this.sizeProperty);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#fillWatch = new ResizeObserver((entries) => {\n\t\t\tconst box = entries.at(-1)?.borderBoxSize[0];\n\t\t\tif (!box) return;\n\n\t\t\t// Written a frame on, or the canvas resizes inside this delivery and WebKit reports a loop\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (this.#fillWatch) {\n\t\t\t\t\tcontrol.style.setProperty(this.sizeProperty, `${String(box.blockSize)}px`);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tthis.#fillWatch.observe(control);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"wave-element.js","names":[],"sources":["../../../src/elements/wave-element.ts"],"sourcesContent":["import type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\n\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { bindSurface } from '#lib/canvas-surface.ts';\nimport { bindFill } from '#lib/fill-watch.ts';\n\nexport interface WaveMarker {\n\tdimmed?: boolean;\n\tend?: number;\n\tkind?: string;\n\tlabel?: string;\n\tstart: number;\n}\n\nfunction markerKey(marker: undefined | WaveMarker): string {\n\treturn marker ? [marker.start, marker.end, marker.kind, marker.label].join('|') : '';\n}\n\nexport abstract class SonicWaveElement<\n\tColour extends string,\n\tLength extends string = never,\n> extends SonicValueElement {\n\tstatic override readonly observedAttributes = [...SonicValueElement.observedAttributes, 'fill'];\n\n\tget currentMarker(): undefined | WaveMarker {\n\t\treturn this.#current ? { ...this.#current } : undefined;\n\t}\n\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\tget markers(): Array<WaveMarker> {\n\t\treturn this.#markers.map((marker) => ({ ...marker }));\n\t}\n\n\tset markers(list: Iterable<WaveMarker> | undefined) {\n\t\tthis.#markers = [...(list ?? [])]\n\t\t\t.filter((marker) => Number.isFinite(marker.start))\n\t\t\t.map((marker) => ({ ...marker }))\n\t\t\t.toSorted((first, second) => first.start - second.start);\n\t\tif (!this.isBound()) return;\n\n\t\tthis.renderMarkers();\n\t\tthis.render();\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 lengths: Record<Length, `--_sonic-${string}`>;\n\n\tprotected abstract readonly sheet: string;\n\n\tprotected abstract readonly sizeProperty: `--_sonic-${string}`;\n\n\t#current: undefined | WaveMarker;\n\n\t#markers: Array<WaveMarker> = [];\n\n\t#surface: Surface | undefined;\n\n\t#syncFill: ((isFilling: boolean) => void) | undefined;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'fill') this.#syncFill?.(this.fill);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('fill', 'markers');\n\t\tsuper.connectedCallback();\n\t}\n\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.renderEmpty();\n\t\tthis.#surface = bindSurface({\n\t\t\tcanvas: this.canvas,\n\t\t\tcolours: this.colours,\n\t\t\tlengths: this.lengths,\n\t\t\tpaint: (context, frame) => {\n\t\t\t\tthis.paint(context, frame);\n\t\t\t},\n\t\t\tresize: () => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\tsignal,\n\t\t});\n\t\tthis.render();\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 override focusTarget(): HTMLElement {\n\t\treturn this.control;\n\t}\n\n\tprotected override heldWrite(next: number): void {\n\t\tsuper.heldWrite(next);\n\t\tthis.render();\n\t}\n\n\tprotected override holdChanged(): void {\n\t\tthis.render();\n\t}\n\n\tprotected abstract isEmpty(): boolean;\n\n\tprotected markerList(): ReadonlyArray<WaveMarker> {\n\t\treturn this.#markers;\n\t}\n\n\tprotected markerProportions(): Array<[number, number]> {\n\t\tconst mapping = this.mapping();\n\n\t\treturn this.#markers.map(({ end, start }) => {\n\t\t\tconst at = mapping.proportionOf(start);\n\t\t\tconst to = end === undefined ? at : mapping.proportionOf(end);\n\n\t\t\treturn [Math.min(at, to), Math.max(at, to)];\n\t\t});\n\t}\n\n\tprotected abstract paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\tframe: SurfaceFrame<Colour, Length>,\n\t): void;\n\n\tprotected override proportionsChanged(): void {\n\t\tthis.renderMarkers();\n\t}\n\n\tprotected renderEmpty(): void {\n\t\tthis.toggleState('empty', this.isEmpty());\n\t}\n\n\tprotected abstract renderMarkers(): void;\n\n\tprotected showCurrentMarker(seconds: number): void {\n\t\tconst current = this.#markers.findLast((marker) => marker.start <= seconds);\n\t\tif (markerKey(current) === markerKey(this.#current)) return;\n\n\t\tthis.#current = current;\n\t\tthis.dispatchEvent(new Event('sonic-marker', { bubbles: true }));\n\t}\n\n\tprotected surface(): Surface | undefined {\n\t\treturn this.#surface;\n\t}\n}\n"],"mappings":"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"}