@xsynaptic/sonic-ui 0.15.0 → 0.16.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 +10 -0
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/styles/button.css +65 -57
- package/dist/styles/material/cap.css +26 -2
- package/dist/styles/ring.css +2 -0
- package/dist/styles/segmented.css +11 -2
- package/dist/styles/switch.css +2 -2
- package/dist/styles/toggle.css +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @xsynaptic/sonic-ui
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- - Button: a text legend widens the button to fit, never under `--sonic-button-aspect-ratio`. `--sonic-button-font-ratio` (0.34) and `--sonic-button-padding-ratio` (0.25) size it, replacing a font size set on the host, and it takes an icon's engraving and lit glow.
|
|
8
|
+
- Segmented: `--sonic-segmented-padding-ratio` (0.25) sets an option's inline padding.
|
|
9
|
+
- - Button and segmented: `--sonic-button-latched-scale` and `--sonic-segmented-latched-scale` scale a cap that stays down; unset, the press scale. A press still dips.
|
|
10
|
+
- Focus: `--sonic-focus-glow` scales the glow, 0 to 1; `--sonic-focus-outline` and `--sonic-focus-outline-offset` draw an outline on keyboard focus.
|
|
11
|
+
- Disabled: `--sonic-disabled-opacity` fades a disabled control.
|
|
12
|
+
|
|
3
13
|
## 0.15.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
|
@@ -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 { SurfaceFrame, SurfaceLook } from '#lib/canvas-surface.ts';\nimport type { Drawn, View } from '#lib/frame-timeline.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.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 { capturePointer } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { waveformBuckets } from '#lib/waveform-buckets.ts';\nimport { pinchFactor, wheelFactor, zoomKeyFactor } from '#lib/zoom-gesture.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 colours;\n\nexport type PeaksRequest = (\n\tfromSeconds: number,\n\ttoSeconds: number,\n) => Iterable<Promise<unknown>> | Promise<unknown> | undefined;\n\ninterface Pinch {\n\tpointerIds: [number, number];\n\tstartSpanPx: number;\n\tstartZoom: number;\n}\n\ninterface Scene extends View {\n\tcolours: SurfaceLook<Colour>['colours'];\n}\n\nconst colours = {\n\tends: '--_sonic-waveform-ends',\n\tgrid: '--_sonic-waveform-grid',\n\tmarker: '--_sonic-waveform-marker',\n\tplaceholder: '--_sonic-waveform-placeholder',\n\twave: '--_sonic-waveform-wave',\n\twaveEdge: '--_sonic-waveform-wave-edge',\n} as const;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\n\nconst zoomKeys = new Map([\n\t['+', 1],\n\t['-', -1],\n\t['=', 1],\n]);\n\nconst amplitudeMargin = 0.94;\n\nconst hatchPitchPx = 8;\nconst placeholderWavelengthPx = 48;\nconst placeholderAmplitude = 0.2;\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\nfunction paintHatch(\n\tcontext: CanvasRenderingContext2D,\n\tscene: Scene,\n\t[from, to]: [number, number],\n): void {\n\tif (to <= from) return;\n\n\tconst { dpr, height } = scene;\n\tconst pitch = Math.max(2, Math.round(hatchPitchPx * dpr));\n\tconst offset = scene.startSeconds * scene.pixelsPerSecond;\n\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, height);\n\tcontext.clip();\n\tcontext.beginPath();\n\tfor (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {\n\t\tcontext.moveTo(x, height);\n\t\tcontext.lineTo(x + height, 0);\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.ends;\n\tcontext.stroke();\n\tcontext.restore();\n}\n\nfunction paintPlaceholders(context: CanvasRenderingContext2D, scene: Scene): void {\n\tif (scene.pending.length === 0) return;\n\n\tconst { dpr, height, pixelsPerSecond, startSeconds } = scene;\n\tconst wavelength = placeholderWavelengthPx * dpr;\n\tconst center = height / 2;\n\tconst origin = startSeconds * pixelsPerSecond;\n\tconst waveY = (x: number): number =>\n\t\tcenter -\n\t\tMath.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) *\n\t\t\tcenter *\n\t\t\tplaceholderAmplitude;\n\n\tcontext.beginPath();\n\tfor (const [from, to] of scene.pending) {\n\t\tconst opening = (from - startSeconds) * pixelsPerSecond;\n\t\tconst closing = (to - startSeconds) * pixelsPerSecond;\n\n\t\tcontext.moveTo(opening, waveY(opening));\n\t\tfor (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));\n\t\tcontext.lineTo(closing, waveY(closing));\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.placeholder;\n\tcontext.stroke();\n}\n\nfunction waveFill(context: CanvasRenderingContext2D, scene: Scene): CanvasGradient {\n\tconst { wave, waveEdge } = scene.colours;\n\tconst gradient = context.createLinearGradient(0, 0, 0, scene.height);\n\n\tgradient.addColorStop(0, waveEdge);\n\tgradient.addColorStop(0.5, wave);\n\tgradient.addColorStop(1, waveEdge);\n\n\treturn gradient;\n}\n\nfunction paintWave(context: CanvasRenderingContext2D, scene: Scene, peaks: WaveformPeaks): void {\n\tconst buckets = waveformBuckets(peaks, scene);\n\tconst [first] = buckets;\n\tif (!first) return;\n\n\tconst center = scene.height / 2;\n\tconst amplitudePx = center * amplitudeMargin;\n\n\tcontext.fillStyle = waveFill(context, scene);\n\tcontext.beginPath();\n\tcontext.moveTo(first.x, center);\n\tfor (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);\n\tfor (const bucket of buckets.toReversed())\n\t\tcontext.lineTo(bucket.x, center - bucket.low * amplitudePx);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour> {\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 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.#repaint();\n\t}\n\n\tget pending(): Array<[number, number]> {\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.#repaint();\n\t}\n\n\tget pendingDelay(): number {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: number | 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\tget readTime(): (() => number | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => number | 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 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.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tget zoom(): number {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: number | 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(): number {\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: number | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): number {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: number | 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 = colours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\treadonly #asked = new WeakSet<Promise<unknown>>();\n\n\t#askedKey: string | undefined;\n\n\treadonly #clock = createTrackingClock();\n\n\t#connection: AbortSignal | undefined;\n\n\t#drawn: Drawn | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#isAsking = false;\n\n\t#isPinchSpent = false;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\t#pinch: Pinch | undefined;\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | 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\tfadeProperty: '--_sonic-waveform-label-fade',\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\treadonly #touches = new Map<number, number>();\n\n\t#wheel: AbortController | undefined;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#bindWheel();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\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'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\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\t\tthis.#connection = signal;\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#connection = undefined;\n\t\t\t\tthis.#bindWheel();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t\tthis.#bindWheel();\n\t\tthis.#bindPinch(signal);\n\t\tthis.bindGestures(this.control, signal, () => this.#grab());\n\t\tthis.#bindZoomKeys(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\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst pending = this.#duePending();\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: look.isReducedMotion && this.reducedMotion === 'page',\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange: [this.min, Math.max(this.min, this.max)],\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 };\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\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\tthis.#paintScene(context, { ...view, colours: look.colours }, isRebuilt);\n\t\t\tif (timeline.wanted) this.#ask(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\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap(({ dimmed, kind, label, start }) =>\n\t\t\t\tlabel === undefined || 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: dimmed === true,\n\t\t\t\t\t\t\t\t...(kind === undefined ? {} : { kind }),\n\t\t\t\t\t\t\t\tstart: clamp(start, ...bounds),\n\t\t\t\t\t\t\t\ttext: label,\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\t#ask(wanted: [number, number]): void {\n\t\tconst key = wanted.join(':');\n\t\tif (key === this.#askedKey) return;\n\n\t\tlet asked: ReturnType<PeaksRequest>;\n\n\t\tthis.#askedKey = key;\n\t\tthis.#isAsking = true;\n\t\ttry {\n\t\t\tasked = this.#requestPeaks?.(...wanted);\n\t\t} finally {\n\t\t\tthis.#isAsking = false;\n\t\t}\n\n\t\tconst promises = asked instanceof Promise ? [asked] : (asked ?? []);\n\n\t\tfor (const promise of promises) {\n\t\t\tif (this.#asked.has(promise)) continue;\n\n\t\t\tthis.#asked.add(promise);\n\t\t\tvoid this.#repaintAfter(promise);\n\t\t}\n\t}\n\n\t#bindPinch(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.#touches.clear();\n\t\tthis.#pinch = undefined;\n\t\tthis.#isPinchSpent = false;\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerType !== 'touch' || !this.zoomable || this.isDisabled()) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\t\t\t\tif (this.#pinch || this.#isPinchSpent) return;\n\n\t\t\t\tconst pointerIds = this.#touchPair();\n\t\t\t\tif (!pointerIds) return;\n\n\t\t\t\tthis.abandonHold();\n\t\t\t\tcapturePointer(control, event.pointerId);\n\t\t\t\tthis.#pinch = { pointerIds, startSpanPx: this.#spanPx(pointerIds), startZoom: this.zoom };\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tif (!this.#touches.has(event.pointerId)) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\n\t\t\t\tconst pinch = this.#pinch;\n\t\t\t\tif (!pinch?.pointerIds.includes(event.pointerId)) return;\n\n\t\t\t\tthis.#zoomTo(\n\t\t\t\t\tpinch.startZoom * pinchFactor(pinch.startSpanPx, this.#spanPx(pinch.pointerIds)),\n\t\t\t\t);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tfor (const type of ['pointerup', 'pointercancel'] as const) {\n\t\t\tcontrol.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (!this.#touches.delete(event.pointerId)) return;\n\n\t\t\t\t\tif (this.#pinch?.pointerIds.includes(event.pointerId)) {\n\t\t\t\t\t\tthis.#pinch = undefined;\n\t\t\t\t\t\tthis.#isPinchSpent = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (this.#touches.size === 0) this.#isPinchSpent = false;\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindWheel(): void {\n\t\tthis.#wheel?.abort();\n\t\tthis.#wheel = undefined;\n\t\tif (!this.zoomable || !this.#connection) return;\n\n\t\tthis.#wheel = new AbortController();\n\t\t// Chromium cannot cancel a wheel heard only on the `display: contents` host\n\t\tthis.control.addEventListener(\n\t\t\t'wheel',\n\t\t\t(event) => {\n\t\t\t\tif ((!event.ctrlKey && !event.metaKey) || this.isDisabled()) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * wheelFactor(event.deltaY, event.deltaMode));\n\t\t\t},\n\t\t\t{ passive: false, signal: this.#wheel.signal },\n\t\t);\n\n\t\tlet startZoom = this.zoom;\n\n\t\t// Safari sends a trackpad pinch as gesture events, never as a ctrl wheel; on iOS they shadow a touch pinch\n\t\tfor (const type of ['gesturestart', 'gesturechange']) {\n\t\t\tthis.control.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (this.isDisabled() || this.#touches.size > 0) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (type === 'gesturestart') startZoom = this.zoom;\n\t\t\t\t\telse if ('scale' in event && typeof event.scale === 'number') {\n\t\t\t\t\t\tthis.#zoomTo(startZoom * event.scale);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ signal: this.#wheel.signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindZoomKeys(signal: AbortSignal): void {\n\t\tthis.control.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tconst direction = this.#zoomKey(event);\n\t\t\t\tif (direction === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * zoomKeyFactor ** direction);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\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.#pinch || this.#isPinchSpent) return undefined;\n\n\t\tconst drawn = this.#drawn ?? { seconds: this.value, startSeconds: this.value };\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.heldFrom();\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#paintMarkers(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\n\t\tconst line = Math.max(1, Math.round(scene.dpr));\n\n\t\tfor (const { dimmed, kind, start } of this.markers) {\n\t\t\tconst x = (start - scene.startSeconds) * scene.pixelsPerSecond;\n\n\t\t\tcontext.fillStyle =\n\t\t\t\t(isKind(kind) ? this.#kindColours.get(kind) : undefined) ?? scene.colours.marker;\n\t\t\tcontext.globalAlpha = dimmed === true ? 0.5 : 1;\n\t\t\tcontext.fillRect(x - line / 2, 0, line, scene.height);\n\t\t}\n\t\tcontext.globalAlpha = 1;\n\t}\n\n\t#paintScene(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tconst { dpr, height, pixelsPerSecond, startSeconds, width } = scene;\n\t\tconst x = (seconds: number): number => (seconds - startSeconds) * pixelsPerSecond;\n\t\tconst { ends, grid } = scene.colours;\n\t\tconst line = Math.max(1, Math.round(dpr));\n\t\tconst [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];\n\t\tconst [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];\n\n\t\tcontext.clearRect(0, 0, width, height);\n\t\tpaintHatch(context, scene, [0, from]);\n\t\tpaintHatch(context, scene, [to, width]);\n\t\tcontext.fillStyle = grid;\n\t\tif (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);\n\t\tpaintPlaceholders(context, scene);\n\t\tif (this.#peaks) paintWave(context, scene, this.#peaks);\n\t\t// Scrolling lines sit at fractional x; rounding them judders\n\t\tcontext.fillStyle = ends;\n\t\tfor (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);\n\t\tthis.#paintMarkers(context, scene, isRebuilt);\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\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 new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t// A write from inside the consumer's call repaints without asking for the same window again\n\t#repaint(): void {\n\t\tif (!this.#isAsking) this.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tasync #repaintAfter(asked: Promise<unknown>): Promise<void> {\n\t\tawait Promise.allSettled([asked]);\n\t\tthis.#repaint();\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.playback();\n\t}\n\n\t#spanPx([first, second]: [number, number]): number {\n\t\treturn (this.#touches.get(second) ?? 0) - (this.#touches.get(first) ?? 0);\n\t}\n\n\t#touchPair(): [number, number] | undefined {\n\t\tconst [first, second, third] = this.#touches.keys();\n\t\tif (first === undefined || second === undefined || third !== undefined) return undefined;\n\n\t\treturn [first, second];\n\t}\n\n\t#zoomKey(event: KeyboardEvent): number | undefined {\n\t\tif (!this.zoomable || this.isDisabled() || event.target !== this.control) return undefined;\n\t\tif (event.ctrlKey || event.metaKey || event.altKey) return undefined;\n\n\t\treturn zoomKeys.get(event.key);\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 { SurfaceFrame, SurfaceLook } from '#lib/canvas-surface.ts';\nimport type { Drawn, View } from '#lib/frame-timeline.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.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 { capturePointer } from '#lib/pointer-drag.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { dueRegions, readRegions } from '#lib/time-regions.ts';\nimport { createTrackingClock } from '#lib/tracking-clock.ts';\nimport { waveformBuckets } from '#lib/waveform-buckets.ts';\nimport { pinchFactor, wheelFactor, zoomKeyFactor } from '#lib/zoom-gesture.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 colours;\n\nexport type PeaksRequest = (\n\tfromSeconds: number,\n\ttoSeconds: number,\n) => Iterable<Promise<unknown>> | Promise<unknown> | undefined;\n\ninterface Pinch {\n\tpointerIds: [number, number];\n\tstartSpanPx: number;\n\tstartZoom: number;\n}\n\ninterface Scene extends View {\n\tcolours: SurfaceLook<Colour>['colours'];\n}\n\nconst colours = {\n\tends: '--_sonic-waveform-ends',\n\tgrid: '--_sonic-waveform-grid',\n\tmarker: '--_sonic-waveform-marker',\n\tplaceholder: '--_sonic-waveform-placeholder',\n\twave: '--_sonic-waveform-wave',\n\twaveEdge: '--_sonic-waveform-wave-edge',\n} as const;\n\nconst defaultZoom = 70;\nconst defaultZoomMin = 20;\nconst defaultZoomMax = 280;\n\nconst zoomKeys = new Map([\n\t['+', 1],\n\t['-', -1],\n\t['=', 1],\n]);\n\nconst amplitudeMargin = 0.94;\n\nconst hatchPitchPx = 8;\nconst placeholderWavelengthPx = 48;\nconst placeholderAmplitude = 0.2;\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\nfunction paintHatch(\n\tcontext: CanvasRenderingContext2D,\n\tscene: Scene,\n\t[from, to]: [number, number],\n): void {\n\tif (to <= from) return;\n\n\tconst { dpr, height } = scene;\n\tconst pitch = Math.max(2, Math.round(hatchPitchPx * dpr));\n\tconst offset = scene.startSeconds * scene.pixelsPerSecond;\n\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, height);\n\tcontext.clip();\n\tcontext.beginPath();\n\tfor (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {\n\t\tcontext.moveTo(x, height);\n\t\tcontext.lineTo(x + height, 0);\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.ends;\n\tcontext.stroke();\n\tcontext.restore();\n}\n\nfunction paintPlaceholders(context: CanvasRenderingContext2D, scene: Scene): void {\n\tif (scene.pending.length === 0) return;\n\n\tconst { dpr, height, pixelsPerSecond, startSeconds } = scene;\n\tconst wavelength = placeholderWavelengthPx * dpr;\n\tconst center = height / 2;\n\tconst origin = startSeconds * pixelsPerSecond;\n\tconst waveY = (x: number): number =>\n\t\tcenter -\n\t\tMath.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) *\n\t\t\tcenter *\n\t\t\tplaceholderAmplitude;\n\n\tcontext.beginPath();\n\tfor (const [from, to] of scene.pending) {\n\t\tconst opening = (from - startSeconds) * pixelsPerSecond;\n\t\tconst closing = (to - startSeconds) * pixelsPerSecond;\n\n\t\tcontext.moveTo(opening, waveY(opening));\n\t\tfor (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));\n\t\tcontext.lineTo(closing, waveY(closing));\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.placeholder;\n\tcontext.stroke();\n}\n\nfunction waveFill(context: CanvasRenderingContext2D, scene: Scene): CanvasGradient {\n\tconst { wave, waveEdge } = scene.colours;\n\tconst gradient = context.createLinearGradient(0, 0, 0, scene.height);\n\n\tgradient.addColorStop(0, waveEdge);\n\tgradient.addColorStop(0.5, wave);\n\tgradient.addColorStop(1, waveEdge);\n\n\treturn gradient;\n}\n\nfunction paintWave(context: CanvasRenderingContext2D, scene: Scene, peaks: WaveformPeaks): void {\n\tconst buckets = waveformBuckets(peaks, scene);\n\tconst [first] = buckets;\n\tif (!first) return;\n\n\tconst center = scene.height / 2;\n\tconst amplitudePx = center * amplitudeMargin;\n\n\tcontext.fillStyle = waveFill(context, scene);\n\tcontext.beginPath();\n\tcontext.moveTo(first.x, center);\n\tfor (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);\n\tfor (const bucket of buckets.toReversed())\n\t\tcontext.lineTo(bucket.x, center - bucket.low * amplitudePx);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\nexport class SonicWaveform extends SonicWaveElement<Colour> {\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 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.#repaint();\n\t}\n\n\tget pending(): Array<[number, number]> {\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.#repaint();\n\t}\n\n\tget pendingDelay(): number {\n\t\treturn Math.max(0, this.numberAttribute('pending-delay', 0));\n\t}\n\n\tset pendingDelay(value: number | 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\tget readTime(): (() => number | undefined) | undefined {\n\t\treturn this.#readTime;\n\t}\n\n\tset readTime(read: (() => number | 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 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.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tget zoom(): number {\n\t\tconst zoom = this.numberAttribute('zoom', defaultZoom);\n\n\t\treturn zoom > 0 ? zoom : defaultZoom;\n\t}\n\n\tset zoom(value: number | 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(): number {\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: number | undefined) {\n\t\tthis.reflect('zoom-max', value);\n\t}\n\n\tget zoomMin(): number {\n\t\tconst zoomMin = this.numberAttribute('zoom-min', defaultZoomMin);\n\n\t\treturn zoomMin > 0 ? zoomMin : defaultZoomMin;\n\t}\n\n\tset zoomMin(value: number | 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 = colours;\n\n\tprotected readonly lengths = {};\n\n\tprotected readonly sheet = 'waveform.css';\n\n\tprotected readonly sizeProperty = '--_sonic-waveform-size';\n\n\treadonly #asked = new WeakSet<Promise<unknown>>();\n\n\t#askedKey: string | undefined;\n\n\treadonly #clock = createTrackingClock();\n\n\t#connection: AbortSignal | undefined;\n\n\t#drawn: Drawn | undefined;\n\n\treadonly #ghost = requireChild(this.control, '.sonic-waveform-ghost', HTMLDivElement);\n\n\t#grabbed: Drawn | undefined;\n\n\t#isAsking = false;\n\n\t#isPinchSpent = false;\n\n\t#kindColours = new Map<string, string>();\n\n\t#paintedKey = '';\n\n\t#peaks: undefined | WaveformPeaks;\n\n\t#pending: Array<[number, number]> = [];\n\n\t#pendingListedMs = new Map<string, number>();\n\n\t#pinch: Pinch | undefined;\n\n\treadonly #playhead = requireChild(this.control, '.sonic-waveform-playhead', HTMLDivElement);\n\n\t#readTime: (() => number | undefined) | 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\tfadeProperty: '--_sonic-waveform-label-fade',\n\t\tinsetProperty: '--_sonic-waveform-label-inset',\n\t\tparkProperty: '--_sonic-waveform-label-park',\n\t});\n\n\treadonly #touches = new Map<number, number>();\n\n\t#wheel: AbortController | undefined;\n\n\toverride attributeChangedCallback(name: string): void {\n\t\tsuper.attributeChangedCallback(name);\n\t\tif (name === 'zoomable') this.#bindWheel();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\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'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\tprotected override connect(signal: AbortSignal): void {\n\t\tsuper.connect(signal);\n\t\tthis.#connection = signal;\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#connection = undefined;\n\t\t\t\tthis.#bindWheel();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t\tthis.#bindWheel();\n\t\tthis.#bindPinch(signal);\n\t\tthis.bindGestures(this.control, signal, () => this.#grab());\n\t\tthis.#bindZoomKeys(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\tprotected paint(\n\t\tcontext: CanvasRenderingContext2D,\n\t\t{ frameMs, isDirty, isRebuilt, look, size }: SurfaceFrame<Colour>,\n\t): void {\n\t\tconst held = this.#held();\n\t\tconst pending = this.#duePending();\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: look.isReducedMotion && this.reducedMotion === 'page',\n\t\t\tisPlaying: this.playing,\n\t\t\tisStill: look.isReducedMotion,\n\t\t\tpending,\n\t\t\trange: [this.min, Math.max(this.min, this.max)],\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 };\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\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\tthis.#paintScene(context, { ...view, colours: look.colours }, isRebuilt);\n\t\t\tif (timeline.wanted) this.#ask(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\tthis.#rider.measure(\n\t\t\tthis.markers.flatMap(({ dimmed, kind, label, start }) =>\n\t\t\t\tlabel === undefined || 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: dimmed === true,\n\t\t\t\t\t\t\t\t...(kind === undefined ? {} : { kind }),\n\t\t\t\t\t\t\t\tstart: clamp(start, ...bounds),\n\t\t\t\t\t\t\t\ttext: label,\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\t#ask(wanted: [number, number]): void {\n\t\tconst key = wanted.join(':');\n\t\tif (key === this.#askedKey) return;\n\n\t\tlet asked: ReturnType<PeaksRequest>;\n\n\t\tthis.#askedKey = key;\n\t\tthis.#isAsking = true;\n\t\ttry {\n\t\t\tasked = this.#requestPeaks?.(...wanted);\n\t\t} finally {\n\t\t\tthis.#isAsking = false;\n\t\t}\n\n\t\tconst promises = asked instanceof Promise ? [asked] : (asked ?? []);\n\n\t\tfor (const promise of promises) {\n\t\t\tif (this.#asked.has(promise)) continue;\n\n\t\t\tthis.#asked.add(promise);\n\t\t\tvoid this.#repaintAfter(promise);\n\t\t}\n\t}\n\n\t#bindPinch(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tthis.#touches.clear();\n\t\tthis.#pinch = undefined;\n\t\tthis.#isPinchSpent = false;\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerType !== 'touch' || !this.zoomable || this.isDisabled()) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\t\t\t\tif (this.#pinch || this.#isPinchSpent) return;\n\n\t\t\t\tconst pointerIds = this.#touchPair();\n\t\t\t\tif (!pointerIds) return;\n\n\t\t\t\tthis.abandonHold();\n\t\t\t\tcapturePointer(control, event.pointerId);\n\t\t\t\tthis.#pinch = { pointerIds, startSpanPx: this.#spanPx(pointerIds), startZoom: this.zoom };\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tif (!this.#touches.has(event.pointerId)) return;\n\n\t\t\t\tthis.#touches.set(event.pointerId, event.clientX);\n\n\t\t\t\tconst pinch = this.#pinch;\n\t\t\t\tif (!pinch?.pointerIds.includes(event.pointerId)) return;\n\n\t\t\t\tthis.#zoomTo(\n\t\t\t\t\tpinch.startZoom * pinchFactor(pinch.startSpanPx, this.#spanPx(pinch.pointerIds)),\n\t\t\t\t);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tfor (const type of ['pointerup', 'pointercancel'] as const) {\n\t\t\tcontrol.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (!this.#touches.delete(event.pointerId)) return;\n\n\t\t\t\t\tif (this.#pinch?.pointerIds.includes(event.pointerId)) {\n\t\t\t\t\t\tthis.#pinch = undefined;\n\t\t\t\t\t\tthis.#isPinchSpent = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (this.#touches.size === 0) this.#isPinchSpent = false;\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindWheel(): void {\n\t\tthis.#wheel?.abort();\n\t\tthis.#wheel = undefined;\n\t\tif (!this.zoomable || !this.#connection) return;\n\n\t\tthis.#wheel = new AbortController();\n\t\t// Chromium cannot cancel a wheel heard only on the `display: contents` host\n\t\tthis.control.addEventListener(\n\t\t\t'wheel',\n\t\t\t(event) => {\n\t\t\t\tif ((!event.ctrlKey && !event.metaKey) || this.isDisabled()) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * wheelFactor(event.deltaY, event.deltaMode));\n\t\t\t},\n\t\t\t{ passive: false, signal: this.#wheel.signal },\n\t\t);\n\n\t\tlet startZoom = this.zoom;\n\n\t\t// Safari sends a trackpad pinch as gesture events, never as a ctrl wheel\n\t\tfor (const type of ['gesturestart', 'gesturechange']) {\n\t\t\tthis.control.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (this.isDisabled() || this.#touches.size > 0) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (type === 'gesturestart') startZoom = this.zoom;\n\t\t\t\t\telse if ('scale' in event && typeof event.scale === 'number') {\n\t\t\t\t\t\tthis.#zoomTo(startZoom * event.scale);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ signal: this.#wheel.signal },\n\t\t\t);\n\t\t}\n\t}\n\n\t#bindZoomKeys(signal: AbortSignal): void {\n\t\tthis.control.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tconst direction = this.#zoomKey(event);\n\t\t\t\tif (direction === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#zoomTo(this.zoom * zoomKeyFactor ** direction);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\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.#pinch || this.#isPinchSpent) return undefined;\n\n\t\tconst drawn = this.#drawn ?? { seconds: this.value, startSeconds: this.value };\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.heldFrom();\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#paintMarkers(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tif (isRebuilt) this.#kindColours = this.#readKindColours();\n\n\t\tconst line = Math.max(1, Math.round(scene.dpr));\n\n\t\tfor (const { dimmed, kind, start } of this.markers) {\n\t\t\tconst x = (start - scene.startSeconds) * scene.pixelsPerSecond;\n\n\t\t\tcontext.fillStyle =\n\t\t\t\t(isKind(kind) ? this.#kindColours.get(kind) : undefined) ?? scene.colours.marker;\n\t\t\tcontext.globalAlpha = dimmed === true ? 0.5 : 1;\n\t\t\tcontext.fillRect(x - line / 2, 0, line, scene.height);\n\t\t}\n\t\tcontext.globalAlpha = 1;\n\t}\n\n\t#paintScene(context: CanvasRenderingContext2D, scene: Scene, isRebuilt: boolean): void {\n\t\tconst { dpr, height, pixelsPerSecond, startSeconds, width } = scene;\n\t\tconst x = (seconds: number): number => (seconds - startSeconds) * pixelsPerSecond;\n\t\tconst { ends, grid } = scene.colours;\n\t\tconst line = Math.max(1, Math.round(dpr));\n\t\tconst [opening, closing] = [x(this.min), x(Math.max(this.min, this.max))];\n\t\tconst [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];\n\n\t\tcontext.clearRect(0, 0, width, height);\n\t\tpaintHatch(context, scene, [0, from]);\n\t\tpaintHatch(context, scene, [to, width]);\n\t\tcontext.fillStyle = grid;\n\t\tif (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);\n\t\tpaintPlaceholders(context, scene);\n\t\tif (this.#peaks) paintWave(context, scene, this.#peaks);\n\t\t// Scrolling lines sit at fractional x; rounding them judders\n\t\tcontext.fillStyle = ends;\n\t\tfor (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);\n\t\tthis.#paintMarkers(context, scene, isRebuilt);\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\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 new Map(kinds.map((kind, index) => [kind, read[index] ?? '']));\n\t}\n\n\t// A write from inside the consumer's call repaints without asking for the same window again\n\t#repaint(): void {\n\t\tif (!this.#isAsking) this.#askedKey = undefined;\n\t\tthis.surface()?.invalidate();\n\t}\n\n\tasync #repaintAfter(asked: Promise<unknown>): Promise<void> {\n\t\tawait Promise.allSettled([asked]);\n\t\tthis.#repaint();\n\t}\n\n\t#sourceSeconds(): number {\n\t\treturn this.#readTime?.() ?? this.playback();\n\t}\n\n\t#spanPx([first, second]: [number, number]): number {\n\t\treturn (this.#touches.get(second) ?? 0) - (this.#touches.get(first) ?? 0);\n\t}\n\n\t#touchPair(): [number, number] | undefined {\n\t\tconst [first, second, third] = this.#touches.keys();\n\t\tif (first === undefined || second === undefined || third !== undefined) return undefined;\n\n\t\treturn [first, second];\n\t}\n\n\t#zoomKey(event: KeyboardEvent): number | undefined {\n\t\tif (!this.zoomable || this.isDisabled() || event.target !== this.control) return undefined;\n\t\tif (event.ctrlKey || event.metaKey || event.altKey) return undefined;\n\n\t\treturn zoomKeys.get(event.key);\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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"}
|
package/dist/styles/button.css
CHANGED
|
@@ -31,6 +31,13 @@
|
|
|
31
31
|
overflow: hidden;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
sonic-button:not(:defined, [hidden]):has(> :not(svg, .sonic-led)) {
|
|
35
|
+
inline-size: max-content;
|
|
36
|
+
min-inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-aspect-ratio));
|
|
37
|
+
padding-inline: calc(var(--_sonic-button-size) * var(--sonic-button-padding-ratio, 0.25));
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
.sonic-button {
|
|
35
42
|
--_sonic-button-gap-ratio: var(--sonic-button-gap-ratio, 0);
|
|
36
43
|
--_sonic-button-radius-ratio: var(--sonic-button-radius-ratio, 0.2);
|
|
@@ -39,8 +46,14 @@
|
|
|
39
46
|
--_sonic-button-icon-ratio: var(--sonic-button-icon-ratio, 0.5);
|
|
40
47
|
--_sonic-button-led-ratio: var(--sonic-button-led-ratio, 0.2);
|
|
41
48
|
--_sonic-button-led-icon-ratio: var(--sonic-button-led-icon-ratio, 0.35);
|
|
49
|
+
--_sonic-button-font-ratio: var(--sonic-button-font-ratio, 0.34);
|
|
50
|
+
--_sonic-button-padding-ratio: var(--sonic-button-padding-ratio, 0.25);
|
|
42
51
|
--_sonic-button-depth-ratio: var(--sonic-button-depth-ratio, calc(0.03 * var(--_sonic-relief)));
|
|
43
52
|
--_sonic-button-press-scale: var(--sonic-button-press-scale, 0.985);
|
|
53
|
+
--_sonic-button-latched-scale: var(
|
|
54
|
+
--sonic-button-latched-scale,
|
|
55
|
+
var(--_sonic-button-press-scale)
|
|
56
|
+
);
|
|
44
57
|
--_sonic-unit: calc(var(--_sonic-button-size) / 32);
|
|
45
58
|
--_sonic-button-corner: calc(var(--_sonic-button-size) * var(--_sonic-button-radius-ratio));
|
|
46
59
|
--_sonic-button-gap: calc(var(--_sonic-button-size) * var(--_sonic-button-gap-ratio));
|
|
@@ -61,6 +74,12 @@
|
|
|
61
74
|
transition: color 120ms ease-out;
|
|
62
75
|
}
|
|
63
76
|
|
|
77
|
+
/* An `auto` width stretches in a column flex or grid parent */
|
|
78
|
+
.sonic-button:where(:has(> .sonic-button-cap > :not(svg, .sonic-led))) {
|
|
79
|
+
inline-size: max-content;
|
|
80
|
+
min-inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-aspect-ratio));
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
:where([momentary]) > .sonic-button {
|
|
65
84
|
touch-action: none;
|
|
66
85
|
}
|
|
@@ -86,25 +105,17 @@
|
|
|
86
105
|
transition-delay: var(--sonic-button-busy-delay, 0s);
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
90
108
|
.sonic-button:is(
|
|
91
109
|
[aria-pressed='true'],
|
|
92
110
|
[aria-expanded='true'],
|
|
93
111
|
:where([momentary]:state(pressed)) > *
|
|
94
|
-
)
|
|
95
|
-
.sonic-button:is(:active, [data-sonic-pressed]):where(:not(:disabled, [aria-disabled='true'])) {
|
|
112
|
+
) {
|
|
96
113
|
--_sonic-keycap-shift: calc(var(--_sonic-keycap-depth) / 2);
|
|
97
|
-
--_sonic-keycap-scale: var(--_sonic-button-
|
|
114
|
+
--_sonic-keycap-scale: var(--_sonic-button-latched-scale);
|
|
98
115
|
--_sonic-keycap-drop: var(--_sonic-press-drop);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
102
|
-
.sonic-button:is(
|
|
103
|
-
[aria-pressed='true'],
|
|
104
|
-
[aria-expanded='true'],
|
|
105
|
-
:where([momentary]:state(pressed)) > *
|
|
106
|
-
) {
|
|
107
116
|
--_sonic-keycap-glow: var(--_sonic-keycap-lit-glow);
|
|
117
|
+
--_sonic-blank-edge-lit: currentcolor;
|
|
118
|
+
--_sonic-button-legend-glow: var(--_sonic-legend-glow);
|
|
108
119
|
|
|
109
120
|
color: var(--_sonic-lit);
|
|
110
121
|
background: var(
|
|
@@ -123,6 +134,28 @@
|
|
|
123
134
|
)
|
|
124
135
|
)
|
|
125
136
|
);
|
|
137
|
+
|
|
138
|
+
& .sonic-button-cap > [data-sonic-when='released'] {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@media (forced-colors: active) {
|
|
143
|
+
--_sonic-skirt: var(--_sonic-lit);
|
|
144
|
+
--_sonic-face: var(--_sonic-lit);
|
|
145
|
+
--_sonic-ink: HighlightText;
|
|
146
|
+
|
|
147
|
+
&:is(:disabled, [aria-disabled='true']) {
|
|
148
|
+
--_sonic-ink: ButtonFace;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.sonic-button:is(:active, [data-sonic-pressed], :where([momentary]:state(pressed)) > *):where(
|
|
154
|
+
:not(:disabled, [aria-disabled='true'])
|
|
155
|
+
) {
|
|
156
|
+
--_sonic-keycap-shift: calc(var(--_sonic-keycap-depth) / 2);
|
|
157
|
+
--_sonic-keycap-scale: var(--_sonic-button-press-scale);
|
|
158
|
+
--_sonic-keycap-drop: var(--_sonic-press-drop);
|
|
126
159
|
}
|
|
127
160
|
|
|
128
161
|
.sonic-button:is(:disabled, [aria-disabled='true']) {
|
|
@@ -178,13 +211,9 @@
|
|
|
178
211
|
);
|
|
179
212
|
--_sonic-blank-edge: var(
|
|
180
213
|
--_sonic-blank-edge-off,
|
|
181
|
-
color-mix(in oklab, currentcolor 35%, transparent)
|
|
214
|
+
var(--_sonic-blank-edge-lit, color-mix(in oklab, currentcolor 35%, transparent))
|
|
182
215
|
);
|
|
183
216
|
|
|
184
|
-
&:is([aria-pressed='true'], [aria-expanded='true'], :where([momentary]:state(pressed)) > *) {
|
|
185
|
-
--_sonic-blank-edge: var(--_sonic-blank-edge-off, currentcolor);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
217
|
@media (forced-colors: active) {
|
|
189
218
|
--_sonic-blank-edge: transparent;
|
|
190
219
|
}
|
|
@@ -209,10 +238,20 @@
|
|
|
209
238
|
);
|
|
210
239
|
}
|
|
211
240
|
|
|
241
|
+
& > :not(.sonic-led) {
|
|
242
|
+
filter: var(--_sonic-engraving) var(--_sonic-button-legend-glow,);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
& > :not(svg, .sonic-led) {
|
|
246
|
+
padding-inline: calc(var(--_sonic-button-size) * var(--_sonic-button-padding-ratio));
|
|
247
|
+
font-size: calc(var(--_sonic-button-size) * var(--_sonic-button-font-ratio));
|
|
248
|
+
line-height: 1;
|
|
249
|
+
white-space: nowrap;
|
|
250
|
+
}
|
|
251
|
+
|
|
212
252
|
& > svg {
|
|
213
253
|
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-icon-ratio));
|
|
214
254
|
block-size: calc(var(--_sonic-button-size) * var(--_sonic-button-icon-ratio));
|
|
215
|
-
filter: var(--_sonic-engraving);
|
|
216
255
|
mask-image: linear-gradient(
|
|
217
256
|
var(--_sonic-light-fall),
|
|
218
257
|
#000 15%,
|
|
@@ -222,30 +261,18 @@
|
|
|
222
261
|
}
|
|
223
262
|
}
|
|
224
263
|
|
|
264
|
+
.sonic-button-cap:has(> :not(svg, .sonic-led)) {
|
|
265
|
+
position: relative;
|
|
266
|
+
inset: auto;
|
|
267
|
+
block-size: calc(var(--_sonic-button-size) - 2 * var(--_sonic-button-gap));
|
|
268
|
+
margin: var(--_sonic-button-gap);
|
|
269
|
+
}
|
|
270
|
+
|
|
225
271
|
.sonic-button-cap:has(> .sonic-led) > svg {
|
|
226
272
|
inline-size: calc(var(--_sonic-button-size) * var(--_sonic-button-led-icon-ratio));
|
|
227
273
|
block-size: calc(var(--_sonic-button-size) * var(--_sonic-button-led-icon-ratio));
|
|
228
274
|
}
|
|
229
275
|
|
|
230
|
-
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
231
|
-
.sonic-button:is(
|
|
232
|
-
[aria-pressed='true'],
|
|
233
|
-
[aria-expanded='true'],
|
|
234
|
-
:where([momentary]:state(pressed)) > *
|
|
235
|
-
)
|
|
236
|
-
.sonic-button-cap
|
|
237
|
-
> svg {
|
|
238
|
-
filter: var(--_sonic-engraving) var(--_sonic-legend-glow);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
242
|
-
.sonic-button:is(
|
|
243
|
-
[aria-pressed='true'],
|
|
244
|
-
[aria-expanded='true'],
|
|
245
|
-
:where([momentary]:state(pressed)) > *
|
|
246
|
-
)
|
|
247
|
-
.sonic-button-cap
|
|
248
|
-
> [data-sonic-when='released'],
|
|
249
276
|
.sonic-button:not(
|
|
250
277
|
[aria-pressed='true'],
|
|
251
278
|
[aria-expanded='true'],
|
|
@@ -274,28 +301,9 @@
|
|
|
274
301
|
}
|
|
275
302
|
|
|
276
303
|
@media (forced-colors: active) {
|
|
277
|
-
.sonic-button-cap >
|
|
304
|
+
.sonic-button-cap > :not(.sonic-led) {
|
|
278
305
|
filter: none;
|
|
279
306
|
mask: none;
|
|
280
307
|
}
|
|
281
|
-
|
|
282
|
-
/* fallow-ignore-next-line code-duplication -- one pressed state across the cap's parts */
|
|
283
|
-
.sonic-button:is(
|
|
284
|
-
[aria-pressed='true'],
|
|
285
|
-
[aria-expanded='true'],
|
|
286
|
-
:where([momentary]:state(pressed)) > *
|
|
287
|
-
) {
|
|
288
|
-
--_sonic-skirt: var(--_sonic-lit);
|
|
289
|
-
--_sonic-face: var(--_sonic-lit);
|
|
290
|
-
--_sonic-ink: HighlightText;
|
|
291
|
-
|
|
292
|
-
&:is(:disabled, [aria-disabled='true']) {
|
|
293
|
-
--_sonic-ink: ButtonFace;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
& .sonic-button-cap > svg {
|
|
297
|
-
filter: none;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
308
|
}
|
|
301
309
|
}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
--_sonic-cap: var(--sonic-cap, transparent);
|
|
15
15
|
--_sonic-cap-hover: var(--sonic-cap-hover, transparent);
|
|
16
16
|
--_sonic-focus: var(--sonic-focus, oklch(90% 0.23 128deg));
|
|
17
|
+
--_sonic-focus-strength: var(--sonic-focus-glow, 1);
|
|
18
|
+
--_sonic-disabled-opacity: var(--sonic-disabled-opacity, 1);
|
|
17
19
|
--_sonic-ink: var(--sonic-ink, currentcolor);
|
|
18
20
|
--_sonic-etch: color-mix(in oklab, var(--_sonic-ink) 30%, transparent);
|
|
19
21
|
--_sonic-ink-disabled: var(--sonic-ink-disabled, var(--_sonic-etch));
|
|
@@ -53,7 +55,7 @@
|
|
|
53
55
|
--_sonic-drop-shade: rgb(0 0 0 / calc(0.65 * var(--_sonic-relief)));
|
|
54
56
|
--_sonic-focus-shadow: 0 0 transparent;
|
|
55
57
|
--_sonic-focus-glow: 0 0 max(4px, 3 * var(--_sonic-unit)) max(1px, 0.5 * var(--_sonic-unit))
|
|
56
|
-
color-mix(in oklab, var(--_sonic-focus) 60
|
|
58
|
+
color-mix(in oklab, var(--_sonic-focus) calc(60% * var(--_sonic-focus-strength)), transparent);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
:is(.sonic-number, .sonic-waveform, .sonic-wavestrip, .sonic-xy) {
|
|
@@ -77,6 +79,26 @@
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
@media not (forced-colors: active) {
|
|
83
|
+
:is(
|
|
84
|
+
:disabled
|
|
85
|
+
> :is(
|
|
86
|
+
.sonic-dial,
|
|
87
|
+
.sonic-number,
|
|
88
|
+
.sonic-slider,
|
|
89
|
+
.sonic-waveform,
|
|
90
|
+
.sonic-wavestrip,
|
|
91
|
+
.sonic-xy
|
|
92
|
+
),
|
|
93
|
+
.sonic-switch:not(:has(> .sonic-switch-position:not(:disabled))),
|
|
94
|
+
.sonic-button:is(:disabled, [aria-disabled='true']),
|
|
95
|
+
.sonic-toggle:not(:has(> .sonic-toggle-position:not(:disabled))),
|
|
96
|
+
.sonic-segmented-option:disabled
|
|
97
|
+
) {
|
|
98
|
+
opacity: var(--_sonic-disabled-opacity);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
@media (hover: hover) {
|
|
81
103
|
:is(.sonic-dial, .sonic-button, .sonic-segmented-option, .sonic-slider):not(
|
|
82
104
|
:disabled,
|
|
@@ -97,9 +119,11 @@
|
|
|
97
119
|
.sonic-xy-axis
|
|
98
120
|
):focus-visible {
|
|
99
121
|
outline: 2px solid transparent;
|
|
100
|
-
outline-offset: -2px;
|
|
122
|
+
outline-offset: var(--sonic-focus-outline-offset, -2px);
|
|
101
123
|
|
|
102
124
|
&:not([data-sonic-pointer-focus]) {
|
|
125
|
+
outline: var(--sonic-focus-outline, 2px solid transparent);
|
|
126
|
+
|
|
103
127
|
--_sonic-cap: var(--_sonic-cap-hover);
|
|
104
128
|
--_sonic-focus-shadow: var(--_sonic-focus-glow);
|
|
105
129
|
}
|
package/dist/styles/ring.css
CHANGED
|
@@ -33,7 +33,12 @@
|
|
|
33
33
|
calc(0.03 * var(--_sonic-relief))
|
|
34
34
|
);
|
|
35
35
|
--_sonic-segmented-press-scale: var(--sonic-segmented-press-scale, 0.985);
|
|
36
|
+
--_sonic-segmented-latched-scale: var(
|
|
37
|
+
--sonic-segmented-latched-scale,
|
|
38
|
+
var(--_sonic-segmented-press-scale)
|
|
39
|
+
);
|
|
36
40
|
--_sonic-segmented-font-ratio: var(--sonic-segmented-font-ratio, 0.34);
|
|
41
|
+
--_sonic-segmented-padding-ratio: var(--sonic-segmented-padding-ratio, 0.25);
|
|
37
42
|
--_sonic-segmented-icon-ratio: var(--sonic-segmented-icon-ratio, 0.5);
|
|
38
43
|
--_sonic-unit: calc(var(--_sonic-segmented-size) / 32);
|
|
39
44
|
--_sonic-segmented-corner: calc(
|
|
@@ -83,10 +88,14 @@
|
|
|
83
88
|
.sonic-segmented-option:where([data-sonic-pressed]):not(:disabled),
|
|
84
89
|
.sonic-segmented-option[aria-checked='true'] {
|
|
85
90
|
--_sonic-keycap-shift: calc(var(--_sonic-keycap-depth) / 2);
|
|
86
|
-
--_sonic-keycap-scale: var(--_sonic-segmented-
|
|
91
|
+
--_sonic-keycap-scale: var(--_sonic-segmented-latched-scale);
|
|
87
92
|
--_sonic-keycap-drop: var(--_sonic-press-drop);
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
.sonic-segmented-option:where([data-sonic-pressed]):not(:disabled) {
|
|
96
|
+
--_sonic-keycap-scale: var(--_sonic-segmented-press-scale);
|
|
97
|
+
}
|
|
98
|
+
|
|
90
99
|
.sonic-segmented-option[aria-checked='true'] {
|
|
91
100
|
--_sonic-keycap-glow: var(--_sonic-keycap-lit-glow);
|
|
92
101
|
|
|
@@ -143,7 +152,7 @@
|
|
|
143
152
|
);
|
|
144
153
|
|
|
145
154
|
position: relative;
|
|
146
|
-
padding-inline: calc(var(--_sonic-segmented-size) *
|
|
155
|
+
padding-inline: calc(var(--_sonic-segmented-size) * var(--_sonic-segmented-padding-ratio));
|
|
147
156
|
|
|
148
157
|
& > * {
|
|
149
158
|
filter: var(--_sonic-engraving);
|
package/dist/styles/switch.css
CHANGED
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
&:focus-visible {
|
|
101
|
-
outline: 2px solid transparent;
|
|
102
|
-
outline-offset: -2px;
|
|
101
|
+
outline: var(--sonic-focus-outline, 2px solid transparent);
|
|
102
|
+
outline-offset: var(--sonic-focus-outline-offset, -2px);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
&:has([data-sonic-momentary]) {
|
package/dist/styles/toggle.css
CHANGED
|
@@ -252,8 +252,8 @@
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
.sonic-toggle-position:focus-visible {
|
|
255
|
-
outline: 2px solid transparent;
|
|
256
|
-
outline-offset: -2px;
|
|
255
|
+
outline: var(--sonic-focus-outline, 2px solid transparent);
|
|
256
|
+
outline-offset: var(--sonic-focus-outline-offset, -2px);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
.sonic-toggle-position:disabled {
|