@xsynaptic/sonic-ui 0.26.0 → 0.28.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.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/dist/default/define/ticker.js +7 -0
- package/dist/default/define.js +1 -0
- package/dist/default/elements/{display-element.js → analyser-element.js} +10 -12
- package/dist/default/elements/button.js +12 -9
- package/dist/default/elements/dial.js +9 -13
- package/dist/default/elements/envelope.js +26 -23
- package/dist/default/elements/field-gesture.js +49 -8
- package/dist/default/elements/meter.js +6 -8
- package/dist/default/elements/number.js +5 -14
- package/dist/default/elements/position-group.js +4 -4
- package/dist/default/elements/radio-group.js +6 -9
- package/dist/default/elements/segmented.js +7 -4
- package/dist/default/elements/slider.js +12 -13
- package/dist/default/elements/sonic-element.js +7 -5
- package/dist/default/elements/spectrum.js +3 -3
- package/dist/default/elements/switch.js +11 -6
- package/dist/default/elements/ticker.js +313 -0
- package/dist/default/elements/toggle.js +14 -8
- package/dist/default/elements/value-element.js +11 -3
- package/dist/default/elements/wave-element.js +11 -12
- package/dist/default/elements/waveform.js +1 -4
- package/dist/default/elements/wavestrip.js +3 -6
- package/dist/default/elements/xy.js +34 -47
- package/dist/default/index.js +2 -1
- package/dist/default/lib/cancel-zone.js +12 -0
- package/dist/default/lib/canvas-surface.js +13 -1
- package/dist/default/lib/distribute.js +2 -3
- package/dist/default/lib/mirror-children.js +1 -1
- package/dist/default/lib/pointer-drag.js +8 -2
- package/dist/default/lib/render.js +2 -2
- package/dist/default/lib/ticker-leg.js +54 -0
- package/dist/dev/define/ticker.d.ts +2 -0
- package/dist/dev/define/ticker.js +8 -0
- package/dist/dev/define/ticker.js.map +1 -0
- package/dist/dev/define.d.ts +3 -1
- package/dist/dev/define.js +1 -0
- package/dist/dev/elements/{display-element.d.ts → analyser-element.d.ts} +3 -3
- package/dist/dev/elements/{display-element.js → analyser-element.js} +11 -14
- package/dist/dev/elements/analyser-element.js.map +1 -0
- package/dist/dev/elements/button.d.ts +4 -1
- package/dist/dev/elements/button.js +12 -14
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +4 -2
- package/dist/dev/elements/dial.js +9 -17
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.d.ts +5 -1
- package/dist/dev/elements/envelope.js +26 -27
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/field-gesture.js +49 -8
- package/dist/dev/elements/field-gesture.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +2 -1
- package/dist/dev/elements/meter.js +6 -9
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +2 -2
- package/dist/dev/elements/number.js +5 -15
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/position-group.js +4 -4
- package/dist/dev/elements/position-group.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +1 -2
- package/dist/dev/elements/radio-group.js +6 -9
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/segmented.d.ts +4 -2
- package/dist/dev/elements/segmented.js +7 -9
- package/dist/dev/elements/segmented.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +4 -2
- package/dist/dev/elements/slider.js +12 -17
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +6 -7
- package/dist/dev/elements/sonic-element.js +14 -83
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/spectrum.d.ts +2 -2
- package/dist/dev/elements/spectrum.js +3 -3
- package/dist/dev/elements/spectrum.js.map +1 -1
- package/dist/dev/elements/switch.d.ts +4 -1
- package/dist/dev/elements/switch.js +11 -10
- package/dist/dev/elements/switch.js.map +1 -1
- package/dist/dev/elements/ticker.d.ts +25 -0
- package/dist/dev/elements/ticker.js +314 -0
- package/dist/dev/elements/ticker.js.map +1 -0
- package/dist/dev/elements/toggle.d.ts +4 -1
- package/dist/dev/elements/toggle.js +14 -13
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +4 -3
- package/dist/dev/elements/value-element.js +11 -3
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +2 -2
- package/dist/dev/elements/wave-element.js +11 -16
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +0 -1
- package/dist/dev/elements/waveform.js +1 -4
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.js +3 -6
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.d.ts +4 -1
- package/dist/dev/elements/xy.js +34 -51
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +4 -1
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/cancel-zone.js +13 -0
- package/dist/dev/lib/cancel-zone.js.map +1 -0
- package/dist/dev/lib/canvas-surface.js +13 -1
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/check-styles.d.ts +8 -0
- package/dist/dev/lib/check-styles.js +95 -0
- package/dist/dev/lib/check-styles.js.map +1 -0
- package/dist/dev/lib/distribute.js +2 -3
- package/dist/dev/lib/distribute.js.map +1 -1
- package/dist/dev/lib/mirror-children.js +9 -7
- package/dist/dev/lib/mirror-children.js.map +1 -1
- package/dist/dev/lib/pointer-drag.js +8 -2
- package/dist/dev/lib/pointer-drag.js.map +1 -1
- package/dist/dev/lib/render.js +2 -2
- package/dist/dev/lib/render.js.map +1 -1
- package/dist/dev/lib/ticker-leg.js +55 -0
- package/dist/dev/lib/ticker-leg.js.map +1 -0
- package/dist/styles/controls.css +1 -0
- package/dist/styles/dial.css +28 -5
- package/dist/styles/material/cap.css +16 -5
- package/dist/styles/material/core.css +1 -0
- package/dist/styles/slider.css +2 -1
- package/dist/styles/switch.css +3 -1
- package/dist/styles/ticker.css +115 -0
- package/dist/styles/toggle.css +7 -5
- package/package.json +2 -1
- package/dist/dev/elements/display-element.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
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 { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ScrubState } from '#lib/scrub.ts';\nimport type { ValueMapping, ValueSpec } from '#lib/value-mapping.ts';\nimport type { ValueModel } from '#lib/value-model.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { isUnanchored, Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.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 HTMLElementEventMap {\n\t\t'sonic-hover': Event;\n\t\t'sonic-reveal': Event;\n\t}\n}\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tchanged?: () => void;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nexport interface ValueNotation {\n\tformat: (value: number) => string;\n\tparse: (text: string) => number;\n\tspeak?: (value: number) => string;\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\nexport interface ValueLink {\n\tchange: () => void;\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tmodel: ValueModel;\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\tchange: () => {\n\t\t\t\telement.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t},\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\tmodel: element.#model,\n\t\t});\n\t}\n\n\tget cancelling(): boolean {\n\t\treturn this.hasState('cancelling');\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 dragging(): boolean {\n\t\treturn this.hasState('dragging');\n\t}\n\n\tget entry(): 'none' | undefined {\n\t\treturn this.getAttribute('entry') === 'none' ? 'none' : undefined;\n\t}\n\n\tset entry(gesture: 'none' | undefined) {\n\t\tthis.reflect('entry', 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\t/** The values a stepped control stops at, spaced evenly along the travel whatever their spacing */\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 revealed(): boolean {\n\t\treturn this.hasState('revealed');\n\t}\n\n\t/** Rounds the value assistive technology hears, in value units; the value itself is untouched */\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.#model.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.#writeHeld(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#model.write(value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#model.value);\n\t}\n\n\t// `Number.parseFloat`, as `Number('')` is 0; an emptied field should leave the value alone\n\tprotected readonly notation: ValueNotation = { format: String, parse: Number.parseFloat };\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// A subclass adding an input to `isWrapping()` has to observe it\n\treadonly #model = createValueModel(() => this.#spec());\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\t#said:\n\t\t| undefined\n\t\t| { format: ((value: number) => string) | undefined; text: string | undefined; value: number };\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value' && this.#isHeld()) {\n\t\t\tthis.#writeHeld(this.numberAttribute('value', this.min));\n\t\t\treturn;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tthis.#model.respec(this.numberAttribute('value', this.min), { forgetAsk: name === 'value' });\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'entry',\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 abandonHold(): void {\n\t\tthis.#gestures?.abandon();\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\thasEntry: () => !isUnanchored && this.entry !== 'none',\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\tmodel: this.#model,\n\t\t\t\tparse: (text) => (this.#parseValue ?? this.notation.parse)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubChanged: () => {\n\t\t\t\t\tthis.#renderHold();\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},\n\t\t\tcontrol,\n\t\t\tthis.focusTarget(),\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tthis.#hover?.lift(event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, changed, 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\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 input(next: number): boolean {\n\t\tconst previous = this.#model.value;\n\n\t\tif (this.#model.input(next)) this.render();\n\t\tif (this.#model.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 isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\treturn this.#model.mapping();\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\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.#model.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tthis.#model.notify();\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 scrubChanged(): void {\n\t\t// Only a subclass draws a scrub\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected scrubState(): ScrubState {\n\t\tconst from = this.#gestures?.scrubFrom();\n\n\t\treturn {\n\t\t\tfrom,\n\t\t\tisRevealed: this.#isRevealed(),\n\t\t\tplayed: from === undefined ? this.#model.value : (this.#heldAt ?? from),\n\t\t};\n\t}\n\n\tprotected spokenText(value: number): string | undefined {\n\t\treturn (this.#formatSpokenValue ?? this.#formatValue ?? this.notation.speak)?.(value);\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.#model.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// The notation's own parser may not read a consumer's `formatValue`\n\t\tif (!this.#parseValue) return this.notation.format(value);\n\n\t\tconst text = this.valueText;\n\n\t\tif (__DEV__ && !Number.isFinite(this.#parseValue(text))) {\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> opens its entry with \"${text}\", which parseValue cannot read; set formatEntry`,\n\t\t\t);\n\t\t}\n\n\t\treturn text;\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#isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#model.value), String(this.#model.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(control, 'aria-valuetext', this.#spokenValueText());\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.#model.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.#gestures?.scrubFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.#hover?.restore();\n\t\tthis.renderReadout();\n\t\tthis.scrubChanged();\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.#model.value,\n\t\t};\n\t}\n\n\t#spec(): ValueSpec {\n\t\tconst { detent, midpoint, positions } = this;\n\n\t\treturn {\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(positions ? { 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\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.#model.value / step) * step)\n\t\t\t: this.#model.value;\n\t}\n\n\t#spokenValueText(): string | undefined {\n\t\tconst format = this.#formatSpokenValue ?? this.#formatValue ?? this.notation.speak;\n\t\tconst value = this.#spokenNow();\n\t\tconst said = this.#said;\n\t\tif (said && said.format === format && said.value === value) return said.text;\n\n\t\tconst text = format?.(value);\n\n\t\tthis.#said = { format, text, value };\n\n\t\treturn text;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn (this.#formatValue ?? this.notation.format)(value);\n\t}\n\n\t#writeHeld(next: number): void {\n\t\tthis.#heldAt = next;\n\t\tthis.scrubChanged();\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 { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ScrubState } from '#lib/scrub.ts';\nimport type { ValueMapping, ValueSpec } from '#lib/value-mapping.ts';\nimport type { ValueModel } from '#lib/value-model.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { isUnanchored, Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.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 HTMLElementEventMap {\n\t\t'sonic-hover': Event;\n\t\t'sonic-reveal': Event;\n\t}\n}\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tchanged?: () => void;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nexport interface ValueNotation {\n\tformat: (value: number) => string;\n\tparse: (text: string) => number;\n\tspeak?: (value: number) => string;\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\nexport interface ValueLink {\n\tchange: () => void;\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tmodel: ValueModel;\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\tchange: () => {\n\t\t\t\telement.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t},\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\tmodel: element.#model,\n\t\t});\n\t}\n\n\tget cancelling(): boolean {\n\t\treturn this.hasState('cancelling');\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 dragging(): boolean {\n\t\treturn this.hasState('dragging');\n\t}\n\n\tget entry(): 'none' | undefined {\n\t\treturn this.getAttribute('entry') === 'none' ? 'none' : undefined;\n\t}\n\n\tset entry(gesture: 'none' | undefined) {\n\t\tthis.reflect('entry', 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\t/** The values a stepped control stops at, spaced evenly along the travel whatever their spacing */\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 revealed(): boolean {\n\t\treturn this.hasState('revealed');\n\t}\n\n\t/** Rounds the value assistive technology hears, in value units; the value itself is untouched */\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.#model.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.#writeHeld(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#model.write(value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#model.value);\n\t}\n\n\tprotected abstract override readonly control: HTMLElement;\n\n\t// `Number.parseFloat`, as `Number('')` is 0; an emptied field should leave the value alone\n\tprotected readonly notation: ValueNotation = { format: String, parse: Number.parseFloat };\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// A subclass adding an input to `isWrapping()` has to observe it\n\treadonly #model = createValueModel(() => this.#spec());\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\t#said:\n\t\t| undefined\n\t\t| { format: ((value: number) => string) | undefined; text: string | undefined; value: number };\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value' && this.#isHeld()) {\n\t\t\tthis.#writeHeld(this.numberAttribute('value', this.min));\n\t\t\treturn;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tthis.#model.respec(this.numberAttribute('value', this.min), { forgetAsk: name === 'value' });\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'entry',\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 abandonHold(): void {\n\t\tthis.#gestures?.abandon();\n\t}\n\n\tprotected bindGestures(\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => undefined | ValueAxis,\n\t): void {\n\t\tconst control = this.control;\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\thasEntry: () => !isUnanchored && this.entry !== 'none',\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\tmodel: this.#model,\n\t\t\t\tparse: (text) => (this.#parseValue ?? this.notation.parse)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubChanged: () => {\n\t\t\t\t\tthis.#renderHold();\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},\n\t\t\tcontrol,\n\t\t\tthis.focusTarget(),\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tthis.#hover?.lift(event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, changed, 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\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 override focusTarget(): HTMLElement {\n\t\treturn this.control;\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#model.value;\n\n\t\tif (this.#model.input(next)) this.render();\n\t\tif (this.#model.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 isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\treturn this.#model.mapping();\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\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.#model.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tthis.#model.notify();\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 scrubChanged(): void {\n\t\t// Only a subclass draws a scrub\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected scrubState(): ScrubState {\n\t\tconst from = this.#gestures?.scrubFrom();\n\n\t\treturn {\n\t\t\tfrom,\n\t\t\tisRevealed: this.#isRevealed(),\n\t\t\tplayed: from === undefined ? this.#model.value : (this.#heldAt ?? from),\n\t\t};\n\t}\n\n\tprotected spokenText(value: number): string | undefined {\n\t\treturn (this.#formatSpokenValue ?? this.#formatValue ?? this.notation.speak)?.(value);\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 upwardAxis(travelProperty: `--_sonic-${string}`): ValueAxis {\n\t\tconst styles = getComputedStyle(this.control);\n\n\t\treturn {\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: Math.max(1, readPxProperty(styles, travelProperty, fallbackTravelPx)),\n\t\t};\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#model.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// The notation's own parser may not read a consumer's `formatValue`\n\t\tif (!this.#parseValue) return this.notation.format(value);\n\n\t\tconst text = this.valueText;\n\n\t\tif (__DEV__ && !Number.isFinite(this.#parseValue(text))) {\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> opens its entry with \"${text}\", which parseValue cannot read; set formatEntry`,\n\t\t\t);\n\t\t}\n\n\t\treturn text;\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#isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#model.value), String(this.#model.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(control, 'aria-valuetext', this.#spokenValueText());\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.#model.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.#gestures?.scrubFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.#hover?.restore();\n\t\tthis.renderReadout();\n\t\tthis.scrubChanged();\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.#model.value,\n\t\t};\n\t}\n\n\t#spec(): ValueSpec {\n\t\tconst { detent, midpoint, positions } = this;\n\n\t\treturn {\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(positions ? { 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\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.#model.value / step) * step)\n\t\t\t: this.#model.value;\n\t}\n\n\t#spokenValueText(): string | undefined {\n\t\tconst format = this.#formatSpokenValue ?? this.#formatValue ?? this.notation.speak;\n\t\tconst value = this.#spokenNow();\n\t\tconst said = this.#said;\n\t\tif (said && said.format === format && said.value === value) return said.text;\n\n\t\tconst text = format?.(value);\n\n\t\tthis.#said = { format, text, value };\n\n\t\treturn text;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn (this.#formatValue ?? this.notation.format)(value);\n\t}\n\n\t#writeHeld(next: number): void {\n\t\tthis.#heldAt = next;\n\t\tthis.scrubChanged();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BoxProbe } from "../lib/check-styles.js";
|
|
1
2
|
import { SonicValueElement, ValueNotation } from "./value-element.js";
|
|
2
3
|
import { Seconds } from "../lib/units.js";
|
|
3
4
|
import { Surface, SurfaceFrame } from "../lib/canvas-surface.js";
|
|
@@ -26,9 +27,9 @@ export declare abstract class SonicWaveElement<Colour extends string, Length ext
|
|
|
26
27
|
set fill(isFilling: boolean);
|
|
27
28
|
get markers(): Array<WaveMarker>;
|
|
28
29
|
set markers(list: Iterable<WaveMarker> | undefined);
|
|
30
|
+
protected readonly boxProbe: BoxProbe;
|
|
29
31
|
protected abstract readonly canvas: HTMLCanvasElement;
|
|
30
32
|
protected abstract readonly colours: Record<Colour, `--_sonic-${string}`>;
|
|
31
|
-
protected abstract readonly control: HTMLElement;
|
|
32
33
|
protected abstract readonly lengths: Record<Length, `--_sonic-${string}`>;
|
|
33
34
|
protected readonly notation: ValueNotation;
|
|
34
35
|
protected abstract readonly numbers: Record<Numeric, `--_sonic-${string}`>;
|
|
@@ -53,7 +54,6 @@ export declare abstract class SonicWaveElement<Colour extends string, Length ext
|
|
|
53
54
|
protected abstract renderMarkers(): void;
|
|
54
55
|
protected scrubChanged(): void;
|
|
55
56
|
protected showCurrentMarker(seconds: number): void;
|
|
56
|
-
protected stateTarget(): HTMLElement;
|
|
57
57
|
protected surface(): Surface | undefined;
|
|
58
58
|
}
|
|
59
59
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SonicValueElement } from "./value-element.js";
|
|
2
|
-
import {
|
|
2
|
+
import { bindControlSurface } from "../lib/canvas-surface.js";
|
|
3
3
|
import { formatClock, parseClock } from "../lib/clock.js";
|
|
4
4
|
import { spokenDuration } from "../lib/spoken-duration.js";
|
|
5
5
|
|
|
@@ -32,6 +32,10 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
32
32
|
this.renderMarkers();
|
|
33
33
|
this.render();
|
|
34
34
|
}
|
|
35
|
+
boxProbe = {
|
|
36
|
+
property: "margin-bottom",
|
|
37
|
+
selector: "[popover]"
|
|
38
|
+
};
|
|
35
39
|
notation = {
|
|
36
40
|
format: formatClock,
|
|
37
41
|
parse: parseClock,
|
|
@@ -65,15 +69,13 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
65
69
|
}
|
|
66
70
|
connect(signal) {
|
|
67
71
|
const control = this.control;
|
|
68
|
-
this.keepControl(
|
|
72
|
+
this.keepControl(signal);
|
|
69
73
|
this.renderEmpty();
|
|
70
|
-
this.#surface =
|
|
74
|
+
this.#surface = bindControlSurface({
|
|
71
75
|
canvas: this.canvas,
|
|
72
76
|
colours: this.colours,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sizeProperty: this.sizeProperty
|
|
76
|
-
},
|
|
77
|
+
control,
|
|
78
|
+
fill: this.fill,
|
|
77
79
|
lengths: this.lengths,
|
|
78
80
|
numbers: this.numbers,
|
|
79
81
|
paint: (context, frame) => {
|
|
@@ -82,14 +84,10 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
82
84
|
resize: () => {
|
|
83
85
|
this.renderMarkers();
|
|
84
86
|
},
|
|
85
|
-
signal
|
|
87
|
+
signal,
|
|
88
|
+
sizeProperty: this.sizeProperty
|
|
86
89
|
});
|
|
87
90
|
this.render();
|
|
88
|
-
this.checkStyles(control, this.sheet, {
|
|
89
|
-
property: "margin-bottom",
|
|
90
|
-
selector: "[popover]"
|
|
91
|
-
});
|
|
92
|
-
this.#surface.setFill(this.fill);
|
|
93
91
|
}
|
|
94
92
|
markerList() {
|
|
95
93
|
return this.#markers;
|
|
@@ -117,9 +115,6 @@ var SonicWaveElement = class extends SonicValueElement {
|
|
|
117
115
|
this.#current = current;
|
|
118
116
|
this.dispatchEvent(new Event("sonic-marker", { bubbles: true }));
|
|
119
117
|
}
|
|
120
|
-
stateTarget() {
|
|
121
|
-
return this.control;
|
|
122
|
-
}
|
|
123
118
|
surface() {
|
|
124
119
|
return this.#surface;
|
|
125
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wave-element.js","names":[],"sources":["../../../src/elements/wave-element.ts"],"sourcesContent":["import type { ValueNotation } from '#elements/value-element.ts';\nimport type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Point } from '#lib/marker-band.ts';\nimport type { Seconds } from '#lib/units.ts';\n\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"wave-element.js","names":[],"sources":["../../../src/elements/wave-element.ts"],"sourcesContent":["import type { ValueNotation } from '#elements/value-element.ts';\nimport type { Surface, SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { BoxProbe } from '#lib/check-styles.ts';\nimport type { Point } from '#lib/marker-band.ts';\nimport type { Seconds } from '#lib/units.ts';\n\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { bindControlSurface } from '#lib/canvas-surface.ts';\nimport { formatClock, parseClock } from '#lib/clock.ts';\nimport { spokenDuration } from '#lib/spoken-duration.ts';\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-marker': Event;\n\t}\n}\n\nexport interface WaveMarker {\n\t// Open to a consumer's own keys, which `renderLabel` draws from\n\t[key: string]: unknown;\n\tdimmed?: boolean;\n\t/** Makes the marker a region */\n\tend?: Seconds;\n\t/** Lowercase letters, digits and hyphens; colours the marker through `--sonic-marker-<kind>` */\n\tkind?: string;\n\tlabel?: string;\n\tstart: Seconds;\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\tNumeric 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 override readonly boxProbe: BoxProbe = {\n\t\tproperty: 'margin-bottom',\n\t\tselector: '[popover]',\n\t};\n\n\tprotected abstract readonly canvas: HTMLCanvasElement;\n\n\tprotected abstract readonly colours: Record<Colour, `--_sonic-${string}`>;\n\n\tprotected abstract readonly lengths: Record<Length, `--_sonic-${string}`>;\n\n\tprotected override readonly notation: ValueNotation = {\n\t\tformat: formatClock,\n\t\tparse: parseClock,\n\t\tspeak: (seconds) => spokenDuration(seconds, this.closest<HTMLElement>('[lang]')?.lang),\n\t};\n\n\tprotected abstract readonly numbers: Record<Numeric, `--_sonic-${string}`>;\n\n\tprotected abstract override 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\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'fill') this.#surface?.setFill(this.fill);\n\t}\n\n\tabstract clientXOf(value: Seconds): number;\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('fill', 'markers');\n\t\tsuper.connectedCallback();\n\t}\n\n\t/** Copies; none outside the canvas */\n\tmarkersFromPoint(clientX: number, clientY: number): Array<WaveMarker> {\n\t\tconst box = this.canvas.getBoundingClientRect();\n\t\tconst isInside =\n\t\t\tclientX >= box.left && clientX <= box.right && clientY >= box.top && clientY <= box.bottom;\n\t\tif (!isInside) return [];\n\n\t\treturn this.markersInReach({ x: clientX - box.left, y: clientY - box.top }, box.width).flatMap(\n\t\t\t(index) => {\n\t\t\t\tconst marker = this.#markers[index];\n\n\t\t\t\treturn marker ? [{ ...marker }] : [];\n\t\t\t},\n\t\t);\n\t}\n\n\trepaint(): void {\n\t\tthis.#surface?.rebuild();\n\t}\n\n\t/** The time at a viewport x, clamped to the bounds and not snapped to the step */\n\tabstract valueFromPoint(clientX: number, clientY: number): Seconds;\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.keepControl(signal);\n\t\tthis.renderEmpty();\n\t\tthis.#surface = bindControlSurface({\n\t\t\tcanvas: this.canvas,\n\t\t\tcolours: this.colours,\n\t\t\tcontrol,\n\t\t\tfill: this.fill,\n\t\t\tlengths: this.lengths,\n\t\t\tnumbers: this.numbers,\n\t\t\tpaint: (context, frame) => {\n\t\t\t\tthis.paint(context, frame);\n\t\t\t},\n\t\t\tresize: () => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\tsignal,\n\t\t\tsizeProperty: this.sizeProperty,\n\t\t});\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 markersInReach(point: Point, widthPx: number): Array<number>;\n\n\tprotected abstract paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\tframe: SurfaceFrame<Colour, Length, Numeric>,\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 override scrubChanged(): void {\n\t\tthis.render();\n\t}\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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"}
|
|
@@ -82,7 +82,6 @@ export declare class SonicWaveform extends SonicWaveElement<Colour, never, Numer
|
|
|
82
82
|
valueFromPoint(clientX: number, _clientY: number): Seconds;
|
|
83
83
|
protected connect(signal: AbortSignal): void;
|
|
84
84
|
protected draw(): void;
|
|
85
|
-
protected focusTarget(): HTMLElement;
|
|
86
85
|
protected isEmpty(): boolean;
|
|
87
86
|
protected markersInReach({ x }: Point, widthPx: number): Array<number>;
|
|
88
87
|
protected paint(context: CanvasRenderingContext2D, { frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour, never, Numeric>): void;
|
|
@@ -231,7 +231,7 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
233
|
this.#zoomGesture = bindZoom(this.control, zoomTarget, signal);
|
|
234
|
-
this.bindGestures(
|
|
234
|
+
this.bindGestures(signal, () => this.#grab());
|
|
235
235
|
bindZoomKeys(this.control, zoomTarget, signal);
|
|
236
236
|
if (!("fonts" in document)) return;
|
|
237
237
|
document.fonts.addEventListener("loadingdone", () => {
|
|
@@ -243,9 +243,6 @@ var SonicWaveform = class extends SonicWaveElement {
|
|
|
243
243
|
surface?.requestFrame();
|
|
244
244
|
if (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());
|
|
245
245
|
}
|
|
246
|
-
focusTarget() {
|
|
247
|
-
return this.control;
|
|
248
|
-
}
|
|
249
246
|
isEmpty() {
|
|
250
247
|
return !this.#peaks || this.#peaks.samples.length === 0;
|
|
251
248
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waveform.js","names":[],"sources":["../../../src/elements/waveform.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { WaveMarker } from '#elements/wave-element.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Drawn } from '#lib/frame-timeline.ts';\nimport type { Point } from '#lib/marker-band.ts';\nimport type { PeaksRequest } from '#lib/peaks-request.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { Milliseconds, PixelsPerSecond, Seconds } from '#lib/units.ts';\nimport type { BandChannels, WaveformBands } from '#lib/waveform-bands.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\nimport type { WaveformTint } from '#lib/waveform-scene.ts';\nimport type { ZoomGesture } from '#lib/zoom-gesture.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { frameTimeline } from '#lib/frame-timeline.ts';\nimport { isKind, writeKind } from '#lib/marker-band.ts';\nimport { createLabelRider } from '#lib/marker-rider.ts';\nimport { clamp, roundTo } from '#lib/math.ts';\nimport { createPeaksRequest } from '#lib/peaks-request.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { bandTable } from '#lib/waveform-bands.ts';\nimport {\n\tbandChannels,\n\tpaintWaveform,\n\twaveformColours,\n\twaveformNumbers,\n} from '#lib/waveform-scene.ts';\nimport { bindZoom, bindZoomKeys } from '#lib/zoom-gesture.ts';\n\nexport type { PeaksRequest } from '#lib/peaks-request.ts';\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-zoom': Event;\n\t}\n\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-waveform': SonicWaveform;\n\t}\n}\n\ntype Colour = keyof typeof waveformColours;\n\ntype Numeric = keyof typeof waveformNumbers;\n\nexport type LabelRender = (marker: WaveMarker, element: HTMLElement) => void;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\nconst lineReachPx = 4;\n\nconst renderWaveform = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-waveform\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-waveform-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-waveform-scrim\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-ghost\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-playhead\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-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-waveform-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 placeLine(line: HTMLElement, at: number | undefined): void {\n\tconst isHidden = at === undefined || at < 0 || at > 1;\n\n\tif (line.hidden !== isHidden) line.hidden = isHidden;\n\tif (isHidden) return;\n\n\tconst translate = `${String(at * 100)}cqi`;\n\n\tif (line.style.getPropertyValue('translate') !== translate) {\n\t\tline.style.setProperty('translate', translate);\n\t}\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour, never, Numeric> {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicWaveElement.observedAttributes,\n\t\t'pending-delay',\n\t\t'playing',\n\t\t'playback-rate',\n\t\t'reduced-motion',\n\t\t'zoom',\n\t\t'zoom-max',\n\t\t'zoom-min',\n\t\t'zoomable',\n\t];\n\n\tget bands(): undefined | WaveformBands {\n\t\treturn this.#bands;\n\t}\n\n\tset bands(bands: undefined | WaveformBands) {\n\t\tthis.#bands = bands;\n\t\tthis.#bandTable = bandTable(bands);\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\tget peaks(): undefined | WaveformPeaks {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: undefined | WaveformPeaks) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** Spans being fetched; drawn as placeholders until cleared */\n\tget pending(): Array<[Seconds, Seconds]> {\n\t\treturn this.#pending.map(([start, end]) => [start, end]);\n\t}\n\n\tset pending(regions: TimeRegions | undefined) {\n\t\tconst pending = regions ? readRegions(regions) : [];\n\t\tif (String(pending) === String(this.#pending)) return;\n\n\t\tconst listedMs = this.#pendingListedMs;\n\t\tconst nowMs = performance.now();\n\n\t\tthis.#pending = pending;\n\t\tthis.#pendingListedMs = new Map(\n\t\t\tthis.#pending.map((region) => {\n\t\t\t\tconst key = region.join(':');\n\n\t\t\t\treturn [key, listedMs.get(key) ?? nowMs];\n\t\t\t}),\n\t\t);\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** How long a span stays listed in `pending` before its placeholder draws */\n\tget pendingDelay(): Milliseconds {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: Milliseconds | undefined) {\n\t\tthis.reflect('pending-delay', value);\n\t}\n\n\tget playbackRate(): number {\n\t\tconst playbackRate = this.numberAttribute('playback-rate', 1);\n\n\t\treturn playbackRate > 0 ? playbackRate : 1;\n\t}\n\n\tset playbackRate(value: number | undefined) {\n\t\tthis.reflect('playback-rate', value);\n\t}\n\n\tget playing(): boolean {\n\t\treturn this.hasAttribute('playing');\n\t}\n\n\tset playing(isPlaying: boolean) {\n\t\tthis.reflect('playing', isPlaying);\n\t}\n\n\t/** Read every frame for the playback time, so the waveform scrolls smoother than writes to `value` arrive */\n\tget readTime(): (() => Seconds | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => Seconds | undefined) | undefined) {\n\t\tthis.#readTime = read;\n\t\tthis.render();\n\t}\n\n\tget reducedMotion(): 'page' | 'scroll' {\n\t\treturn this.getAttribute('reduced-motion') === 'scroll' ? 'scroll' : 'page';\n\t}\n\n\tset reducedMotion(motion: 'page' | 'scroll' | undefined) {\n\t\tthis.reflect('reduced-motion', motion);\n\t}\n\n\tget renderLabel(): LabelRender | undefined {\n\t\treturn this.#renderLabel;\n\t}\n\n\tset renderLabel(render: LabelRender | undefined) {\n\t\tthis.#renderLabel = render;\n\t\tif (this.isBound()) this.renderMarkers();\n\t}\n\n\tget requestPeaks(): PeaksRequest | undefined {\n\t\treturn this.#requestPeaks;\n\t}\n\n\tset requestPeaks(request: PeaksRequest | undefined) {\n\t\tthis.#requestPeaks = request;\n\t\tthis.#peaksRequest.reset();\n\t}\n\n\tget zoom(): PixelsPerSecond {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom', value);\n\t}\n\n\tget zoomable(): boolean {\n\t\treturn this.hasAttribute('zoomable');\n\t}\n\n\tset zoomable(isZoomable: boolean) {\n\t\tthis.reflect('zoomable', isZoomable);\n\t}\n\n\tget zoomMax(): PixelsPerSecond {\n\t\tconst zoomMax = this.numberAttribute('zoom-max', defaultZoomMax);\n\n\t\treturn Math.max(this.zoomMin, zoomMax > 0 ? zoomMax : defaultZoomMax);\n\t}\n\n\tset zoomMax(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): PixelsPerSecond {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-min', value);\n\t}\n\n\tprotected readonly control = renderWaveform();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-waveform-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = waveformColours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly numbers = waveformNumbers;\n\n\tprotected readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\t#bandColours: Array<BandChannels | undefined> = [];\n\n\t#bands: undefined | WaveformBands;\n\n\t#bandTable: ReturnType<typeof bandTable>;\n\n\treadonly #clock = createTrackingClock();\n\n\t#drawn: (Drawn & { zoom: PixelsPerSecond }) | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\treadonly #peaksRequest = createPeaksRequest(() => {\n\t\tthis.surface()?.invalidate();\n\t});\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | undefined;\n\n\t#renderLabel: LabelRender | undefined;\n\n\t#requestPeaks: PeaksRequest | undefined;\n\n\treadonly #rider = createLabelRider(this.control, {\n\t\tclassName: 'sonic-waveform-label',\n\t\tcolourProperty: '--_sonic-waveform-marker',\n\t\tfadeProperties: ['--_sonic-waveform-label-fade-start', '--_sonic-waveform-label-fade-end'],\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\t#strip: CanvasRenderingContext2D | undefined;\n\n\t#zoomGesture: undefined | ZoomGesture;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#zoomGesture?.sync();\n\t}\n\n\t/** Viewport x of a time as last drawn; not clamped to the window */\n\tclientXOf(value: Seconds): number {\n\t\tconst { left, width } = this.canvas.getBoundingClientRect();\n\n\t\treturn left + this.#drawnX(value, width);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'bands',\n\t\t\t'peaks',\n\t\t\t'pending',\n\t\t\t'pendingDelay',\n\t\t\t'playing',\n\t\t\t'playbackRate',\n\t\t\t'readTime',\n\t\t\t'reducedMotion',\n\t\t\t'renderLabel',\n\t\t\t'requestPeaks',\n\t\t\t'zoom',\n\t\t\t'zoomable',\n\t\t\t'zoomMax',\n\t\t\t'zoomMin',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\t/** The time at a viewport x as last drawn */\n\tvalueFromPoint(clientX: number, _clientY: number): Seconds {\n\t\tconst { left, width } = this.canvas.getBoundingClientRect();\n\t\tconst { startSeconds, zoom } = this.#drawnWindow(width);\n\n\t\treturn clamp(startSeconds + (clientX - left) / zoom, ...this.mapping().bounds);\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\n\t\tconst zoomTarget = {\n\t\t\tclaim: () => {\n\t\t\t\tthis.abandonHold();\n\t\t\t},\n\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\tisZoomable: () => this.zoomable,\n\t\t\tzoom: () => this.zoom,\n\t\t\tzoomTo: (target: number) => {\n\t\t\t\tthis.#zoomTo(target);\n\t\t\t},\n\t\t};\n\n\t\tthis.#zoomGesture = bindZoom(this.control, zoomTarget, signal);\n\t\tthis.bindGestures(this.control, signal, () => this.#grab());\n\t\t// After the value keys, which stand down once a key is taken\n\t\tbindZoomKeys(this.control, zoomTarget, signal);\n\t\tif (!('fonts' in document)) return;\n\n\t\tdocument.fonts.addEventListener(\n\t\t\t'loadingdone',\n\t\t\t() => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tsurface?.requestFrame();\n\t\t// No frame follows the position out of view, or before the first one paints\n\t\tif (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.control;\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.samples.length === 0;\n\t}\n\n\t// The canvas paints a region as its start line alone\n\tprotected markersInReach({ x }: Point, widthPx: number): Array<number> {\n\t\treturn this.markerList()\n\t\t\t.map(({ start }, index) => ({\n\t\t\t\tdistance: Math.abs(this.#drawnX(start, widthPx) - x),\n\t\t\t\tindex,\n\t\t\t}))\n\t\t\t.filter(({ distance }) => distance <= lineReachPx)\n\t\t\t.toSorted((first, second) => first.distance - second.distance)\n\t\t\t.map(({ index }) => index);\n\t}\n\n\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour, never, Numeric>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst isPaged = look.isReducedMotion && this.reducedMotion === 'page';\n\t\tconst pending = this.#duePending();\n\t\tconst range: [number, number] = [this.min, Math.max(this.min, this.max)];\n\t\tconst timeline = frameTimeline({\n\t\t\tclockSeconds: this.#clock.read(frameMs, {\n\t\t\t\tisPlaying: this.playing,\n\t\t\t\tplaybackRate: this.playbackRate,\n\t\t\t\tseconds: this.#sourceSeconds(),\n\t\t\t}),\n\t\t\tframeMs,\n\t\t\t...(held ? { held } : {}),\n\t\t\tisPaged,\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange,\n\t\t\tsize,\n\t\t\tzoom: this.zoom,\n\t\t});\n\t\tconst { seconds, view } = timeline;\n\n\t\tthis.#drawn = { seconds, startSeconds: view.startSeconds, zoom: this.zoom };\n\t\tplaceLine(this.#playhead, timeline.playheadAt);\n\t\tplaceLine(this.#ghost, timeline.ghostAt);\n\t\tthis.showCurrentMarker(seconds);\n\t\tthis.toggleState('pending', view.pending.length > 0);\n\t\tthis.#rider.place({\n\t\t\tisPaged,\n\t\t\tplayheadSeconds: seconds,\n\t\t\tstartSeconds: view.startSeconds,\n\t\t\twidthPx: view.width / view.dpr,\n\t\t\twindowSeconds: view.width / view.pixelsPerSecond,\n\t\t});\n\t\tif (isDirty || timeline.paintKey !== this.#paintedKey) {\n\t\t\tthis.#paintedKey = timeline.paintKey;\n\t\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\t\t\tpaintWaveform(\n\t\t\t\tcontext,\n\t\t\t\t{ ...view, colours: look.colours, numbers: look.numbers },\n\t\t\t\t{\n\t\t\t\t\tbands: this.#tint(isRebuilt),\n\t\t\t\t\tkindColours: this.#kindColours,\n\t\t\t\t\tmarkers: this.markerList().map((marker) => ({\n\t\t\t\t\t\t...marker,\n\t\t\t\t\t\tstart: clamp(marker.start, ...range),\n\t\t\t\t\t})),\n\t\t\t\t\tpeaks: this.#peaks,\n\t\t\t\t\trange,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.#peaksRequest.ask(this.#requestPeaks, timeline.wanted);\n\t\t}\n\t\tif (timeline.isMoving) this.surface()?.requestFrame();\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tconst bounds = this.mapping().bounds;\n\n\t\tconst render = this.#renderLabel;\n\n\t\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap((marker) =>\n\t\t\t\tmarker.label === undefined || marker.label === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tisDimmed: marker.dimmed === true,\n\t\t\t\t\t\t\t\t...(marker.kind === undefined ? {} : { kind: marker.kind }),\n\t\t\t\t\t\t\t\tstart: clamp(marker.start, ...bounds),\n\t\t\t\t\t\t\t\ttext: marker.label,\n\t\t\t\t\t\t\t\t...(render\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\twrite: (element: HTMLElement) => {\n\t\t\t\t\t\t\t\t\t\t\t\trender(marker, element);\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t),\n\t\t);\n\t\tthis.surface()?.rebuild();\n\t}\n\n\tprotected override scrubChanged(): void {\n\t\tconst isScrubbing = this.scrubState().from !== undefined;\n\n\t\tthis.#grabbed = isScrubbing ? (this.#grabbed ?? this.#lastDrawn()) : undefined;\n\t\tsuper.scrubChanged();\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\t// Before the first frame, the window a centerd playhead would draw\n\t#drawnWindow(widthPx: number): { startSeconds: Seconds; zoom: PixelsPerSecond } {\n\t\tconst zoom = this.#drawn?.zoom ?? this.zoom;\n\n\t\treturn { startSeconds: this.#drawn?.startSeconds ?? this.value - widthPx / zoom / 2, zoom };\n\t}\n\n\t#drawnX(value: Seconds, widthPx: number): number {\n\t\tconst { startSeconds, zoom } = this.#drawnWindow(widthPx);\n\n\t\treturn (clamp(value, ...this.mapping().bounds) - startSeconds) * zoom;\n\t}\n\n\t#duePending(): Array<[number, number]> {\n\t\tconst due = dueRegions(this.#pending, this.#pendingListedMs, [\n\t\t\tperformance.now(),\n\t\t\tthis.pendingDelay,\n\t\t]);\n\n\t\tif (due.length < this.#pending.length) this.surface()?.requestFrame();\n\n\t\treturn due;\n\t}\n\n\t#grab(): undefined | ValueAxis {\n\t\tif (this.#zoomGesture?.isPinching() === true) return undefined;\n\n\t\tconst drawn = this.#lastDrawn();\n\n\t\tthis.#grabbed = drawn;\n\n\t\tconst mapping = this.mapping();\n\t\tconst [low, high] = mapping.bounds;\n\n\t\treturn {\n\t\t\tfromProportion: mapping.proportionOf(drawn.seconds),\n\t\t\tisKeptOnCancel: true,\n\t\t\tposition: (event) => -event.clientX,\n\t\t\ttravelPx: Math.max(1, (high - low) * this.zoom),\n\t\t};\n\t}\n\n\t#held(): undefined | { grabbed: Drawn; seconds: number } {\n\t\tconst grabbed = this.#grabbed;\n\t\tconst { from } = this.scrubState();\n\t\tif (!grabbed || from === undefined) return undefined;\n\n\t\treturn { grabbed, seconds: this.value === from ? grabbed.seconds : this.value };\n\t}\n\n\t#lastDrawn(): Drawn {\n\t\treturn this.#drawn ?? { seconds: this.value, startSeconds: this.value };\n\t}\n\n\t#readBandColours(\n\t\tstrip: CanvasRenderingContext2D,\n\t\tbandCount: number,\n\t): Array<BandChannels | undefined> {\n\t\tconst probes = Array.from({ length: bandCount }, (_probe, band) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.style.setProperty(\n\t\t\t\t'color',\n\t\t\t\t`var(--sonic-waveform-band-${String(band + 1)}, transparent)`,\n\t\t\t);\n\n\t\t\treturn probe;\n\t\t});\n\n\t\treturn this.#readProbes(probes).map((colour) => bandChannels(strip, colour));\n\t}\n\n\t#readKindColours(): Map<string, string> {\n\t\tconst kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];\n\t\tconst probes = kinds.map((kind) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.className = 'sonic-waveform-label';\n\t\t\twriteKind(probe, kind, '--_sonic-waveform-marker');\n\n\t\t\treturn probe;\n\t\t});\n\n\t\tconst read = this.#readProbes(probes);\n\n\t\treturn new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t#readProbes(probes: Array<HTMLElement>): Array<string> {\n\t\tthis.control.append(...probes);\n\n\t\tconst read = probes.map((probe) => getComputedStyle(probe).color);\n\n\t\tfor (const probe of probes) probe.remove();\n\n\t\treturn read;\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.scrubState().played;\n\t}\n\n\t#tint(isRebuilt: boolean): undefined | WaveformTint {\n\t\tconst table = this.#bandTable;\n\t\tif (!table) return undefined;\n\n\t\tconst strip =\n\t\t\tthis.#strip ??\n\t\t\tdocument.createElement('canvas').getContext('2d', { willReadFrequently: true }) ??\n\t\t\tundefined;\n\t\tif (!strip) return undefined;\n\n\t\tthis.#strip = strip;\n\n\t\tif (isRebuilt || this.#bandColours.length !== table.bandCount) {\n\t\t\tstrip.canvas.height = 1;\n\t\t\tthis.#bandColours = this.#readBandColours(strip, table.bandCount);\n\t\t}\n\n\t\treturn { colours: this.#bandColours, strip, table };\n\t}\n\n\t#zoomTo(target: number): void {\n\t\tconst next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);\n\t\tif (next === this.zoom) return;\n\n\t\tthis.zoom = next;\n\t\tthis.dispatchEvent(new Event('sonic-zoom', { bubbles: true }));\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"waveform.js","names":[],"sources":["../../../src/elements/waveform.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { WaveMarker } from '#elements/wave-element.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Drawn } from '#lib/frame-timeline.ts';\nimport type { Point } from '#lib/marker-band.ts';\nimport type { PeaksRequest } from '#lib/peaks-request.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { Milliseconds, PixelsPerSecond, Seconds } from '#lib/units.ts';\nimport type { BandChannels, WaveformBands } from '#lib/waveform-bands.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\nimport type { WaveformTint } from '#lib/waveform-scene.ts';\nimport type { ZoomGesture } from '#lib/zoom-gesture.ts';\n\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { frameTimeline } from '#lib/frame-timeline.ts';\nimport { isKind, writeKind } from '#lib/marker-band.ts';\nimport { createLabelRider } from '#lib/marker-rider.ts';\nimport { clamp, roundTo } from '#lib/math.ts';\nimport { createPeaksRequest } from '#lib/peaks-request.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { bandTable } from '#lib/waveform-bands.ts';\nimport {\n\tbandChannels,\n\tpaintWaveform,\n\twaveformColours,\n\twaveformNumbers,\n} from '#lib/waveform-scene.ts';\nimport { bindZoom, bindZoomKeys } from '#lib/zoom-gesture.ts';\n\nexport type { PeaksRequest } from '#lib/peaks-request.ts';\n\ndeclare global {\n\tinterface HTMLElementEventMap {\n\t\t'sonic-zoom': Event;\n\t}\n\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-waveform': SonicWaveform;\n\t}\n}\n\ntype Colour = keyof typeof waveformColours;\n\ntype Numeric = keyof typeof waveformNumbers;\n\nexport type LabelRender = (marker: WaveMarker, element: HTMLElement) => void;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\nconst lineReachPx = 4;\n\nconst renderWaveform = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-waveform\" role=\"slider\" tabindex=\"0\">\n\t\t\t<canvas class=\"sonic-waveform-canvas\" aria-hidden=\"true\"></canvas>\n\t\t\t<div class=\"sonic-waveform-scrim\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-ghost\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-playhead\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-label\" aria-hidden=\"true\" hidden></div>\n\t\t\t<div class=\"sonic-waveform-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-waveform-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 placeLine(line: HTMLElement, at: number | undefined): void {\n\tconst isHidden = at === undefined || at < 0 || at > 1;\n\n\tif (line.hidden !== isHidden) line.hidden = isHidden;\n\tif (isHidden) return;\n\n\tconst translate = `${String(at * 100)}cqi`;\n\n\tif (line.style.getPropertyValue('translate') !== translate) {\n\t\tline.style.setProperty('translate', translate);\n\t}\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour, never, Numeric> {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicWaveElement.observedAttributes,\n\t\t'pending-delay',\n\t\t'playing',\n\t\t'playback-rate',\n\t\t'reduced-motion',\n\t\t'zoom',\n\t\t'zoom-max',\n\t\t'zoom-min',\n\t\t'zoomable',\n\t];\n\n\tget bands(): undefined | WaveformBands {\n\t\treturn this.#bands;\n\t}\n\n\tset bands(bands: undefined | WaveformBands) {\n\t\tthis.#bands = bands;\n\t\tthis.#bandTable = bandTable(bands);\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\tget peaks(): undefined | WaveformPeaks {\n\t\treturn this.#peaks;\n\t}\n\n\tset peaks(peaks: undefined | WaveformPeaks) {\n\t\tthis.#peaks = peaks;\n\t\tthis.renderEmpty();\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** Spans being fetched; drawn as placeholders until cleared */\n\tget pending(): Array<[Seconds, Seconds]> {\n\t\treturn this.#pending.map(([start, end]) => [start, end]);\n\t}\n\n\tset pending(regions: TimeRegions | undefined) {\n\t\tconst pending = regions ? readRegions(regions) : [];\n\t\tif (String(pending) === String(this.#pending)) return;\n\n\t\tconst listedMs = this.#pendingListedMs;\n\t\tconst nowMs = performance.now();\n\n\t\tthis.#pending = pending;\n\t\tthis.#pendingListedMs = new Map(\n\t\t\tthis.#pending.map((region) => {\n\t\t\t\tconst key = region.join(':');\n\n\t\t\t\treturn [key, listedMs.get(key) ?? nowMs];\n\t\t\t}),\n\t\t);\n\t\tthis.#peaksRequest.changed();\n\t}\n\n\t/** How long a span stays listed in `pending` before its placeholder draws */\n\tget pendingDelay(): Milliseconds {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: Milliseconds | undefined) {\n\t\tthis.reflect('pending-delay', value);\n\t}\n\n\tget playbackRate(): number {\n\t\tconst playbackRate = this.numberAttribute('playback-rate', 1);\n\n\t\treturn playbackRate > 0 ? playbackRate : 1;\n\t}\n\n\tset playbackRate(value: number | undefined) {\n\t\tthis.reflect('playback-rate', value);\n\t}\n\n\tget playing(): boolean {\n\t\treturn this.hasAttribute('playing');\n\t}\n\n\tset playing(isPlaying: boolean) {\n\t\tthis.reflect('playing', isPlaying);\n\t}\n\n\t/** Read every frame for the playback time, so the waveform scrolls smoother than writes to `value` arrive */\n\tget readTime(): (() => Seconds | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => Seconds | undefined) | undefined) {\n\t\tthis.#readTime = read;\n\t\tthis.render();\n\t}\n\n\tget reducedMotion(): 'page' | 'scroll' {\n\t\treturn this.getAttribute('reduced-motion') === 'scroll' ? 'scroll' : 'page';\n\t}\n\n\tset reducedMotion(motion: 'page' | 'scroll' | undefined) {\n\t\tthis.reflect('reduced-motion', motion);\n\t}\n\n\tget renderLabel(): LabelRender | undefined {\n\t\treturn this.#renderLabel;\n\t}\n\n\tset renderLabel(render: LabelRender | undefined) {\n\t\tthis.#renderLabel = render;\n\t\tif (this.isBound()) this.renderMarkers();\n\t}\n\n\tget requestPeaks(): PeaksRequest | undefined {\n\t\treturn this.#requestPeaks;\n\t}\n\n\tset requestPeaks(request: PeaksRequest | undefined) {\n\t\tthis.#requestPeaks = request;\n\t\tthis.#peaksRequest.reset();\n\t}\n\n\tget zoom(): PixelsPerSecond {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom', value);\n\t}\n\n\tget zoomable(): boolean {\n\t\treturn this.hasAttribute('zoomable');\n\t}\n\n\tset zoomable(isZoomable: boolean) {\n\t\tthis.reflect('zoomable', isZoomable);\n\t}\n\n\tget zoomMax(): PixelsPerSecond {\n\t\tconst zoomMax = this.numberAttribute('zoom-max', defaultZoomMax);\n\n\t\treturn Math.max(this.zoomMin, zoomMax > 0 ? zoomMax : defaultZoomMax);\n\t}\n\n\tset zoomMax(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): PixelsPerSecond {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: PixelsPerSecond | undefined) {\n\t\tthis.reflect('zoom-min', value);\n\t}\n\n\tprotected override readonly control = renderWaveform();\n\n\tprotected readonly canvas = requireChild(\n\t\tthis.control,\n\t\t'.sonic-waveform-canvas',\n\t\tHTMLCanvasElement,\n\t);\n\n\tprotected readonly colours = waveformColours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly numbers = waveformNumbers;\n\n\tprotected override readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\t#bandColours: Array<BandChannels | undefined> = [];\n\n\t#bands: undefined | WaveformBands;\n\n\t#bandTable: ReturnType<typeof bandTable>;\n\n\treadonly #clock = createTrackingClock();\n\n\t#drawn: (Drawn & { zoom: PixelsPerSecond }) | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\treadonly #peaksRequest = createPeaksRequest(() => {\n\t\tthis.surface()?.invalidate();\n\t});\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | undefined;\n\n\t#renderLabel: LabelRender | undefined;\n\n\t#requestPeaks: PeaksRequest | undefined;\n\n\treadonly #rider = createLabelRider(this.control, {\n\t\tclassName: 'sonic-waveform-label',\n\t\tcolourProperty: '--_sonic-waveform-marker',\n\t\tfadeProperties: ['--_sonic-waveform-label-fade-start', '--_sonic-waveform-label-fade-end'],\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\t#strip: CanvasRenderingContext2D | undefined;\n\n\t#zoomGesture: undefined | ZoomGesture;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#zoomGesture?.sync();\n\t}\n\n\t/** Viewport x of a time as last drawn; not clamped to the window */\n\tclientXOf(value: Seconds): number {\n\t\tconst { left, width } = this.canvas.getBoundingClientRect();\n\n\t\treturn left + this.#drawnX(value, width);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'bands',\n\t\t\t'peaks',\n\t\t\t'pending',\n\t\t\t'pendingDelay',\n\t\t\t'playing',\n\t\t\t'playbackRate',\n\t\t\t'readTime',\n\t\t\t'reducedMotion',\n\t\t\t'renderLabel',\n\t\t\t'requestPeaks',\n\t\t\t'zoom',\n\t\t\t'zoomable',\n\t\t\t'zoomMax',\n\t\t\t'zoomMin',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\t/** The time at a viewport x as last drawn */\n\tvalueFromPoint(clientX: number, _clientY: number): Seconds {\n\t\tconst { left, width } = this.canvas.getBoundingClientRect();\n\t\tconst { startSeconds, zoom } = this.#drawnWindow(width);\n\n\t\treturn clamp(startSeconds + (clientX - left) / zoom, ...this.mapping().bounds);\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\n\t\tconst zoomTarget = {\n\t\t\tclaim: () => {\n\t\t\t\tthis.abandonHold();\n\t\t\t},\n\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\tisZoomable: () => this.zoomable,\n\t\t\tzoom: () => this.zoom,\n\t\t\tzoomTo: (target: number) => {\n\t\t\t\tthis.#zoomTo(target);\n\t\t\t},\n\t\t};\n\n\t\tthis.#zoomGesture = bindZoom(this.control, zoomTarget, signal);\n\t\tthis.bindGestures(signal, () => this.#grab());\n\t\t// After the value keys, which stand down once a key is taken\n\t\tbindZoomKeys(this.control, zoomTarget, signal);\n\t\tif (!('fonts' in document)) return;\n\n\t\tdocument.fonts.addEventListener(\n\t\t\t'loadingdone',\n\t\t\t() => {\n\t\t\t\tthis.renderMarkers();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected draw(): void {\n\t\tconst surface = this.surface();\n\n\t\tsurface?.requestFrame();\n\t\t// No frame follows the position out of view, or before the first one paints\n\t\tif (surface?.isVisible === false || !this.#drawn) this.showCurrentMarker(this.#sourceSeconds());\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.samples.length === 0;\n\t}\n\n\t// The canvas paints a region as its start line alone\n\tprotected markersInReach({ x }: Point, widthPx: number): Array<number> {\n\t\treturn this.markerList()\n\t\t\t.map(({ start }, index) => ({\n\t\t\t\tdistance: Math.abs(this.#drawnX(start, widthPx) - x),\n\t\t\t\tindex,\n\t\t\t}))\n\t\t\t.filter(({ distance }) => distance <= lineReachPx)\n\t\t\t.toSorted((first, second) => first.distance - second.distance)\n\t\t\t.map(({ index }) => index);\n\t}\n\n\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour, never, Numeric>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst isPaged = look.isReducedMotion && this.reducedMotion === 'page';\n\t\tconst pending = this.#duePending();\n\t\tconst range: [number, number] = [this.min, Math.max(this.min, this.max)];\n\t\tconst timeline = frameTimeline({\n\t\t\tclockSeconds: this.#clock.read(frameMs, {\n\t\t\t\tisPlaying: this.playing,\n\t\t\t\tplaybackRate: this.playbackRate,\n\t\t\t\tseconds: this.#sourceSeconds(),\n\t\t\t}),\n\t\t\tframeMs,\n\t\t\t...(held ? { held } : {}),\n\t\t\tisPaged,\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange,\n\t\t\tsize,\n\t\t\tzoom: this.zoom,\n\t\t});\n\t\tconst { seconds, view } = timeline;\n\n\t\tthis.#drawn = { seconds, startSeconds: view.startSeconds, zoom: this.zoom };\n\t\tplaceLine(this.#playhead, timeline.playheadAt);\n\t\tplaceLine(this.#ghost, timeline.ghostAt);\n\t\tthis.showCurrentMarker(seconds);\n\t\tthis.toggleState('pending', view.pending.length > 0);\n\t\tthis.#rider.place({\n\t\t\tisPaged,\n\t\t\tplayheadSeconds: seconds,\n\t\t\tstartSeconds: view.startSeconds,\n\t\t\twidthPx: view.width / view.dpr,\n\t\t\twindowSeconds: view.width / view.pixelsPerSecond,\n\t\t});\n\t\tif (isDirty || timeline.paintKey !== this.#paintedKey) {\n\t\t\tthis.#paintedKey = timeline.paintKey;\n\t\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\t\t\tpaintWaveform(\n\t\t\t\tcontext,\n\t\t\t\t{ ...view, colours: look.colours, numbers: look.numbers },\n\t\t\t\t{\n\t\t\t\t\tbands: this.#tint(isRebuilt),\n\t\t\t\t\tkindColours: this.#kindColours,\n\t\t\t\t\tmarkers: this.markerList().map((marker) => ({\n\t\t\t\t\t\t...marker,\n\t\t\t\t\t\tstart: clamp(marker.start, ...range),\n\t\t\t\t\t})),\n\t\t\t\t\tpeaks: this.#peaks,\n\t\t\t\t\trange,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.#peaksRequest.ask(this.#requestPeaks, timeline.wanted);\n\t\t}\n\t\tif (timeline.isMoving) this.surface()?.requestFrame();\n\t}\n\n\tprotected override renderMarkers(): void {\n\t\tconst bounds = this.mapping().bounds;\n\n\t\tconst render = this.#renderLabel;\n\n\t\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap((marker) =>\n\t\t\t\tmarker.label === undefined || marker.label === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tisDimmed: marker.dimmed === true,\n\t\t\t\t\t\t\t\t...(marker.kind === undefined ? {} : { kind: marker.kind }),\n\t\t\t\t\t\t\t\tstart: clamp(marker.start, ...bounds),\n\t\t\t\t\t\t\t\ttext: marker.label,\n\t\t\t\t\t\t\t\t...(render\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\twrite: (element: HTMLElement) => {\n\t\t\t\t\t\t\t\t\t\t\t\trender(marker, element);\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t),\n\t\t);\n\t\tthis.surface()?.rebuild();\n\t}\n\n\tprotected override scrubChanged(): void {\n\t\tconst isScrubbing = this.scrubState().from !== undefined;\n\n\t\tthis.#grabbed = isScrubbing ? (this.#grabbed ?? this.#lastDrawn()) : undefined;\n\t\tsuper.scrubChanged();\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\t// Before the first frame, the window a centerd playhead would draw\n\t#drawnWindow(widthPx: number): { startSeconds: Seconds; zoom: PixelsPerSecond } {\n\t\tconst zoom = this.#drawn?.zoom ?? this.zoom;\n\n\t\treturn { startSeconds: this.#drawn?.startSeconds ?? this.value - widthPx / zoom / 2, zoom };\n\t}\n\n\t#drawnX(value: Seconds, widthPx: number): number {\n\t\tconst { startSeconds, zoom } = this.#drawnWindow(widthPx);\n\n\t\treturn (clamp(value, ...this.mapping().bounds) - startSeconds) * zoom;\n\t}\n\n\t#duePending(): Array<[number, number]> {\n\t\tconst due = dueRegions(this.#pending, this.#pendingListedMs, [\n\t\t\tperformance.now(),\n\t\t\tthis.pendingDelay,\n\t\t]);\n\n\t\tif (due.length < this.#pending.length) this.surface()?.requestFrame();\n\n\t\treturn due;\n\t}\n\n\t#grab(): undefined | ValueAxis {\n\t\tif (this.#zoomGesture?.isPinching() === true) return undefined;\n\n\t\tconst drawn = this.#lastDrawn();\n\n\t\tthis.#grabbed = drawn;\n\n\t\tconst mapping = this.mapping();\n\t\tconst [low, high] = mapping.bounds;\n\n\t\treturn {\n\t\t\tfromProportion: mapping.proportionOf(drawn.seconds),\n\t\t\tisKeptOnCancel: true,\n\t\t\tposition: (event) => -event.clientX,\n\t\t\ttravelPx: Math.max(1, (high - low) * this.zoom),\n\t\t};\n\t}\n\n\t#held(): undefined | { grabbed: Drawn; seconds: number } {\n\t\tconst grabbed = this.#grabbed;\n\t\tconst { from } = this.scrubState();\n\t\tif (!grabbed || from === undefined) return undefined;\n\n\t\treturn { grabbed, seconds: this.value === from ? grabbed.seconds : this.value };\n\t}\n\n\t#lastDrawn(): Drawn {\n\t\treturn this.#drawn ?? { seconds: this.value, startSeconds: this.value };\n\t}\n\n\t#readBandColours(\n\t\tstrip: CanvasRenderingContext2D,\n\t\tbandCount: number,\n\t): Array<BandChannels | undefined> {\n\t\tconst probes = Array.from({ length: bandCount }, (_probe, band) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.style.setProperty(\n\t\t\t\t'color',\n\t\t\t\t`var(--sonic-waveform-band-${String(band + 1)}, transparent)`,\n\t\t\t);\n\n\t\t\treturn probe;\n\t\t});\n\n\t\treturn this.#readProbes(probes).map((colour) => bandChannels(strip, colour));\n\t}\n\n\t#readKindColours(): Map<string, string> {\n\t\tconst kinds = [...new Set(this.markers.map(({ kind }) => kind).filter((kind) => isKind(kind)))];\n\t\tconst probes = kinds.map((kind) => {\n\t\t\tconst probe = document.createElement('div');\n\n\t\t\tprobe.className = 'sonic-waveform-label';\n\t\t\twriteKind(probe, kind, '--_sonic-waveform-marker');\n\n\t\t\treturn probe;\n\t\t});\n\n\t\tconst read = this.#readProbes(probes);\n\n\t\treturn new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t#readProbes(probes: Array<HTMLElement>): Array<string> {\n\t\tthis.control.append(...probes);\n\n\t\tconst read = probes.map((probe) => getComputedStyle(probe).color);\n\n\t\tfor (const probe of probes) probe.remove();\n\n\t\treturn read;\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.scrubState().played;\n\t}\n\n\t#tint(isRebuilt: boolean): undefined | WaveformTint {\n\t\tconst table = this.#bandTable;\n\t\tif (!table) return undefined;\n\n\t\tconst strip =\n\t\t\tthis.#strip ??\n\t\t\tdocument.createElement('canvas').getContext('2d', { willReadFrequently: true }) ??\n\t\t\tundefined;\n\t\tif (!strip) return undefined;\n\n\t\tthis.#strip = strip;\n\n\t\tif (isRebuilt || this.#bandColours.length !== table.bandCount) {\n\t\t\tstrip.canvas.height = 1;\n\t\t\tthis.#bandColours = this.#readBandColours(strip, table.bandCount);\n\t\t}\n\n\t\treturn { colours: this.#bandColours, strip, table };\n\t}\n\n\t#zoomTo(target: number): void {\n\t\tconst next = roundTo(clamp(target, this.zoomMin, this.zoomMax), 2);\n\t\tif (next === this.zoom) return;\n\n\t\tthis.zoom = next;\n\t\tthis.dispatchEvent(new Event('sonic-zoom', { bubbles: true }));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { clamp, clampProportion } from "../lib/math.js";
|
|
2
2
|
import { requireChild, template } from "../lib/render.js";
|
|
3
|
-
import { readPxProperty } from "../lib/read-px-property.js";
|
|
4
3
|
import { scrubRegions } from "../lib/scrub.js";
|
|
5
4
|
import { sortedRegions } from "../lib/time-regions.js";
|
|
5
|
+
import { outsideCancelZone } from "../lib/cancel-zone.js";
|
|
6
6
|
import { SonicWaveElement } from "./wave-element.js";
|
|
7
7
|
import { createMarkerBand } from "../lib/marker-band.js";
|
|
8
8
|
import { RegionLayer } from "./region-layer.js";
|
|
@@ -22,7 +22,6 @@ const lengths = {
|
|
|
22
22
|
pitch: "--_sonic-wavestrip-bar-pitch",
|
|
23
23
|
radiusRatio: "--_sonic-wavestrip-bar-radius-ratio"
|
|
24
24
|
};
|
|
25
|
-
const cancelZonePx = 48;
|
|
26
25
|
const renderWavestrip = template(`
|
|
27
26
|
<div class="sonic-wavestrip">
|
|
28
27
|
<canvas class="sonic-wavestrip-canvas" role="slider" tabindex="0"></canvas>
|
|
@@ -157,7 +156,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
157
156
|
},
|
|
158
157
|
valueAt: (event) => this.#markerStartAt(event, this.#axis()) ?? this.mapping().snap(this.valueFromPoint(event.clientX, event.clientY))
|
|
159
158
|
});
|
|
160
|
-
this.bindGestures(
|
|
159
|
+
this.bindGestures(signal, (event) => this.#grab(event));
|
|
161
160
|
}
|
|
162
161
|
draw() {
|
|
163
162
|
const surface = this.surface();
|
|
@@ -256,9 +255,7 @@ var SonicWavestrip = class extends SonicWaveElement {
|
|
|
256
255
|
}
|
|
257
256
|
#outside() {
|
|
258
257
|
if (!this.cancellable) return void 0;
|
|
259
|
-
|
|
260
|
-
const zone = readPxProperty(getComputedStyle(this.control), "--_sonic-cancel-zone", cancelZonePx);
|
|
261
|
-
return (event) => event.clientY < box.top - zone || event.clientY > box.bottom + zone;
|
|
258
|
+
return outsideCancelZone(this.control, "--_sonic-cancel-zone");
|
|
262
259
|
}
|
|
263
260
|
#placeReadout() {
|
|
264
261
|
const at = this.mapping().proportionOf(this.readoutValue());
|
|
@@ -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 { WaveMarker } from '#elements/wave-element.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Point } from '#lib/marker-band.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 { RegionLayer } from '#elements/region-layer.ts';\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { createMarkerBand } from '#lib/marker-band.ts';\nimport { clamp, 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\nexport type MarkerRender = (marker: WaveMarker, element: HTMLElement) => void;\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\">\n\t\t\t<canvas class=\"sonic-wavestrip-canvas\" role=\"slider\" tabindex=\"0\"></canvas>\n\t\t\t<div class=\"sonic-wavestrip-markers\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-wavestrip-regions\"></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/** Words a region's `aria-valuetext`; unset, a region speaks its start as the strip speaks a time */\n\tget formatSpokenRegion(): ((start: Seconds, end: Seconds) => string) | undefined {\n\t\treturn this.#formatSpokenRegion;\n\t}\n\n\tset formatSpokenRegion(format: ((start: Seconds, end: Seconds) => string) | undefined) {\n\t\tthis.#formatSpokenRegion = format;\n\t\tthis.#regions?.render();\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\t/** A time a release will seek to, drawn as a scrub's edge; a hold or a hover of the strip's own is drawn instead */\n\tget preview(): Seconds | undefined {\n\t\treturn this.#preview;\n\t}\n\n\tset preview(seconds: null | Seconds | undefined) {\n\t\tthis.#preview = Number.isFinite(seconds) ? (seconds ?? undefined) : undefined;\n\t\tthis.render();\n\t}\n\n\t/** Runs once per point marker on each `markers` or `renderMarker` write; the element lasts until the next */\n\tget renderMarker(): MarkerRender | undefined {\n\t\treturn this.#renderMarker;\n\t}\n\n\tset renderMarker(render: MarkerRender | undefined) {\n\t\tthis.#renderMarker = render;\n\t\tthis.#drawMarker =\n\t\t\trender &&\n\t\t\t((marker, element) => {\n\t\t\t\trender({ ...marker }, element);\n\t\t\t});\n\t\tif (this.isBound()) this.renderMarkers();\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 numbers = {};\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\t#drawMarker: MarkerRender | undefined;\n\n\t#formatSpokenRegion: ((start: Seconds, end: Seconds) => string) | undefined;\n\n\treadonly #markerBand = createMarkerBand<WaveMarker>(\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#preview: Seconds | undefined;\n\n\t#regions: RegionLayer | undefined;\n\n\t#renderMarker: MarkerRender | 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(\n\t\t\t'buffered',\n\t\t\t'cancellable',\n\t\t\t'formatSpokenRegion',\n\t\t\t'peaks',\n\t\t\t'preview',\n\t\t\t'renderMarker',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tvalueFromPoint(clientX: number, _clientY: number): Seconds {\n\t\tconst { startPx, travelPx } = this.#axis();\n\t\tconst mapping = this.mapping();\n\n\t\treturn clamp(\n\t\t\tmapping.valueAt(clampProportion((clientX - startPx) / travelPx)),\n\t\t\t...mapping.bounds,\n\t\t);\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tconst { control } = this;\n\n\t\tthis.#regions = new RegionLayer(\n\t\t\t{\n\t\t\t\taxis: () => this.#axis(),\n\t\t\t\tcontrol,\n\t\t\t\telement: this,\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tlayer: requireChild(control, '.sonic-wavestrip-regions', HTMLDivElement),\n\t\t\t\tmapping: () => this.mapping(),\n\t\t\t\toutside: () => this.#outside(),\n\t\t\t\tseek: (event) => {\n\t\t\t\t\tif (this.input(this.valueFromPoint(event.clientX, event.clientY))) {\n\t\t\t\t\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tspeak: ({ end, start }) =>\n\t\t\t\t\tthis.#formatSpokenRegion?.(start, end) ??\n\t\t\t\t\tthis.spokenText(start) ??\n\t\t\t\t\tthis.notation.format(start),\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tsuper.connect(signal);\n\t\tthis.bindHover(control, signal, {\n\t\t\tchanged: () => {\n\t\t\t\tif (this.#preview !== undefined) this.render();\n\t\t\t},\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout();\n\t\t\t},\n\t\t\tvalueAt: (event) =>\n\t\t\t\tthis.#markerStartAt(event, this.#axis()) ??\n\t\t\t\tthis.mapping().snap(this.valueFromPoint(event.clientX, event.clientY)),\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.#regions?.follow();\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 override focusTarget(): HTMLElement {\n\t\treturn this.canvas;\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.length === 0;\n\t}\n\n\t/** The first point marker is the one a press there snaps to */\n\tprotected markersInReach(point: Point, widthPx: number): Array<number> {\n\t\tconst regions = this.markerProportions().flatMap(([from, to], index) => {\n\t\t\tconst isRegion = this.markerList()[index]?.end !== undefined;\n\n\t\t\treturn isRegion && point.x >= from * widthPx && point.x <= to * widthPx ? [index] : [];\n\t\t});\n\n\t\treturn [...this.#markerBand.inReach(point, widthPx), ...regions];\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(), this.#drawMarker);\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\tprotected override usesChild(child: Node): boolean {\n\t\treturn child instanceof Element && child.localName === 'sonic-region';\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): undefined | ValueAxis {\n\t\tconst axis = this.#axis();\n\t\tconst marker = this.#markerStartAt(event, axis);\n\t\tconst regions = this.#regions;\n\n\t\tif (regions?.isHeld() === true) {\n\t\t\tregions.abandon();\n\t\t\treturn undefined;\n\t\t}\n\t\tif (marker === undefined && regions?.take(event) === true) return undefined;\n\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(marker ?? this.valueFromPoint(event.clientX, event.clientY));\n\n\t\treturn outside ? { ...grabbed, outside } : grabbed;\n\t}\n\n\t#markerStartAt(\n\t\t{ clientX, clientY }: PointerEvent,\n\t\t{ startPx, topPx, travelPx }: { startPx: number; topPx: number; travelPx: number },\n\t): number | undefined {\n\t\tconst [index] = this.#markerBand.inReach(\n\t\t\t{ x: clientX - startPx, y: 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 state = this.scrubState();\n\t\tconst isOwn = state.from !== undefined || this.hoverValue !== undefined;\n\t\tconst preview = isOwn || this.#preview === undefined ? undefined : proportionOf(this.#preview);\n\t\tconst { played, scrub = preview } = scrubRegions(state, 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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"}
|
|
1
|
+
{"version":3,"file":"wavestrip.js","names":[],"sources":["../../../src/elements/wavestrip.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { WaveMarker } from '#elements/wave-element.ts';\nimport type { SurfaceFrame } from '#lib/canvas-surface.ts';\nimport type { Point } from '#lib/marker-band.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 { RegionLayer } from '#elements/region-layer.ts';\nimport { SonicWaveElement } from '#elements/wave-element.ts';\nimport { outsideCancelZone } from '#lib/cancel-zone.ts';\nimport { createMarkerBand } from '#lib/marker-band.ts';\nimport { clamp, clampProportion } from '#lib/math.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\nexport type MarkerRender = (marker: WaveMarker, element: HTMLElement) => void;\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 renderWavestrip = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-wavestrip\">\n\t\t\t<canvas class=\"sonic-wavestrip-canvas\" role=\"slider\" tabindex=\"0\"></canvas>\n\t\t\t<div class=\"sonic-wavestrip-markers\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-wavestrip-regions\"></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/** Words a region's `aria-valuetext`; unset, a region speaks its start as the strip speaks a time */\n\tget formatSpokenRegion(): ((start: Seconds, end: Seconds) => string) | undefined {\n\t\treturn this.#formatSpokenRegion;\n\t}\n\n\tset formatSpokenRegion(format: ((start: Seconds, end: Seconds) => string) | undefined) {\n\t\tthis.#formatSpokenRegion = format;\n\t\tthis.#regions?.render();\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\t/** A time a release will seek to, drawn as a scrub's edge; a hold or a hover of the strip's own is drawn instead */\n\tget preview(): Seconds | undefined {\n\t\treturn this.#preview;\n\t}\n\n\tset preview(seconds: null | Seconds | undefined) {\n\t\tthis.#preview = Number.isFinite(seconds) ? (seconds ?? undefined) : undefined;\n\t\tthis.render();\n\t}\n\n\t/** Runs once per point marker on each `markers` or `renderMarker` write; the element lasts until the next */\n\tget renderMarker(): MarkerRender | undefined {\n\t\treturn this.#renderMarker;\n\t}\n\n\tset renderMarker(render: MarkerRender | undefined) {\n\t\tthis.#renderMarker = render;\n\t\tthis.#drawMarker =\n\t\t\trender &&\n\t\t\t((marker, element) => {\n\t\t\t\trender({ ...marker }, element);\n\t\t\t});\n\t\tif (this.isBound()) this.renderMarkers();\n\t}\n\n\tprotected override 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 numbers = {};\n\n\tprotected override 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\t#drawMarker: MarkerRender | undefined;\n\n\t#formatSpokenRegion: ((start: Seconds, end: Seconds) => string) | undefined;\n\n\treadonly #markerBand = createMarkerBand<WaveMarker>(\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#preview: Seconds | undefined;\n\n\t#regions: RegionLayer | undefined;\n\n\t#renderMarker: MarkerRender | 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(\n\t\t\t'buffered',\n\t\t\t'cancellable',\n\t\t\t'formatSpokenRegion',\n\t\t\t'peaks',\n\t\t\t'preview',\n\t\t\t'renderMarker',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tvalueFromPoint(clientX: number, _clientY: number): Seconds {\n\t\tconst { startPx, travelPx } = this.#axis();\n\t\tconst mapping = this.mapping();\n\n\t\treturn clamp(\n\t\t\tmapping.valueAt(clampProportion((clientX - startPx) / travelPx)),\n\t\t\t...mapping.bounds,\n\t\t);\n\t}\n\n\tprotected override connect(signal: AbortSignal): void {\n\t\tconst { control } = this;\n\n\t\tthis.#regions = new RegionLayer(\n\t\t\t{\n\t\t\t\taxis: () => this.#axis(),\n\t\t\t\tcontrol,\n\t\t\t\telement: this,\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tlayer: requireChild(control, '.sonic-wavestrip-regions', HTMLDivElement),\n\t\t\t\tmapping: () => this.mapping(),\n\t\t\t\toutside: () => this.#outside(),\n\t\t\t\tseek: (event) => {\n\t\t\t\t\tif (this.input(this.valueFromPoint(event.clientX, event.clientY))) {\n\t\t\t\t\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tspeak: ({ end, start }) =>\n\t\t\t\t\tthis.#formatSpokenRegion?.(start, end) ??\n\t\t\t\t\tthis.spokenText(start) ??\n\t\t\t\t\tthis.notation.format(start),\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tsuper.connect(signal);\n\t\tthis.bindHover(control, signal, {\n\t\t\tchanged: () => {\n\t\t\t\tif (this.#preview !== undefined) this.render();\n\t\t\t},\n\t\t\tplace: () => {\n\t\t\t\tthis.#placeReadout();\n\t\t\t},\n\t\t\tvalueAt: (event) =>\n\t\t\t\tthis.#markerStartAt(event, this.#axis()) ??\n\t\t\t\tthis.mapping().snap(this.valueFromPoint(event.clientX, event.clientY)),\n\t\t});\n\t\tthis.bindGestures(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.#regions?.follow();\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 override focusTarget(): HTMLElement {\n\t\treturn this.canvas;\n\t}\n\n\tprotected isEmpty(): boolean {\n\t\treturn !this.#peaks || this.#peaks.length === 0;\n\t}\n\n\t/** The first point marker is the one a press there snaps to */\n\tprotected markersInReach(point: Point, widthPx: number): Array<number> {\n\t\tconst regions = this.markerProportions().flatMap(([from, to], index) => {\n\t\t\tconst isRegion = this.markerList()[index]?.end !== undefined;\n\n\t\t\treturn isRegion && point.x >= from * widthPx && point.x <= to * widthPx ? [index] : [];\n\t\t});\n\n\t\treturn [...this.#markerBand.inReach(point, widthPx), ...regions];\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(), this.#drawMarker);\n\t}\n\n\tprotected override scrubsKeyRepeat(): boolean {\n\t\treturn true;\n\t}\n\n\tprotected override usesChild(child: Node): boolean {\n\t\treturn child instanceof Element && child.localName === 'sonic-region';\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): undefined | ValueAxis {\n\t\tconst axis = this.#axis();\n\t\tconst marker = this.#markerStartAt(event, axis);\n\t\tconst regions = this.#regions;\n\n\t\tif (regions?.isHeld() === true) {\n\t\t\tregions.abandon();\n\t\t\treturn undefined;\n\t\t}\n\t\tif (marker === undefined && regions?.take(event) === true) return undefined;\n\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(marker ?? this.valueFromPoint(event.clientX, event.clientY));\n\n\t\treturn outside ? { ...grabbed, outside } : grabbed;\n\t}\n\n\t#markerStartAt(\n\t\t{ clientX, clientY }: PointerEvent,\n\t\t{ startPx, topPx, travelPx }: { startPx: number; topPx: number; travelPx: number },\n\t): number | undefined {\n\t\tconst [index] = this.#markerBand.inReach(\n\t\t\t{ x: clientX - startPx, y: 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\treturn outsideCancelZone(this.control, '--_sonic-cancel-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 state = this.scrubState();\n\t\tconst isOwn = state.from !== undefined || this.hoverValue !== undefined;\n\t\tconst preview = isOwn || this.#preview === undefined ? undefined : proportionOf(this.#preview);\n\t\tconst { played, scrub = preview } = scrubRegions(state, 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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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BoxProbe } from "../lib/check-styles.js";
|
|
1
2
|
import { SonicFormElement } from "./form-element.js";
|
|
2
3
|
import { ValueSpec } from "../lib/value-mapping.js";
|
|
3
4
|
import { FieldAxis } from "../lib/field.js";
|
|
@@ -55,6 +56,9 @@ export declare class SonicXy extends SonicFormElement {
|
|
|
55
56
|
set yStep(value: number | undefined);
|
|
56
57
|
get yTaper(): ValueSpec['taper'];
|
|
57
58
|
set yTaper(curve: undefined | ValueSpec['taper']);
|
|
59
|
+
protected readonly boxProbe: BoxProbe;
|
|
60
|
+
protected readonly control: HTMLDivElement;
|
|
61
|
+
protected readonly sheet = "xy.css";
|
|
58
62
|
attributeChangedCallback(name: string): void;
|
|
59
63
|
connectedCallback(): void;
|
|
60
64
|
formResetCallback(): void;
|
|
@@ -62,7 +66,6 @@ export declare class SonicXy extends SonicFormElement {
|
|
|
62
66
|
protected focusTarget(): HTMLElement;
|
|
63
67
|
protected render(): void;
|
|
64
68
|
protected restoreState(state: string): void;
|
|
65
|
-
protected stateTarget(): HTMLElement;
|
|
66
69
|
}
|
|
67
70
|
//#endregion
|
|
68
71
|
//# sourceMappingURL=xy.d.ts.map
|